@opengeni/contracts 0.39.0 → 0.44.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.
Files changed (130) hide show
  1. package/dist/atlassian.d.ts +278 -0
  2. package/dist/atlassian.js +55 -0
  3. package/dist/atlassian.js.map +1 -0
  4. package/dist/chunk-52SGB2QO.js +688 -0
  5. package/dist/chunk-52SGB2QO.js.map +1 -0
  6. package/dist/{chunk-7JZMI22I.js → chunk-5UDNP7AO.js} +34 -2
  7. package/dist/chunk-5UDNP7AO.js.map +1 -0
  8. package/dist/chunk-BVGIYW7D.js +11 -0
  9. package/dist/chunk-BVGIYW7D.js.map +1 -0
  10. package/dist/chunk-CADSX6UN.js +1145 -0
  11. package/dist/chunk-CADSX6UN.js.map +1 -0
  12. package/dist/chunk-D2HKF2EF.js +1460 -0
  13. package/dist/chunk-D2HKF2EF.js.map +1 -0
  14. package/dist/chunk-FJBG5E4O.js +194 -0
  15. package/dist/chunk-FJBG5E4O.js.map +1 -0
  16. package/dist/chunk-H5BVCZKF.js +382 -0
  17. package/dist/chunk-H5BVCZKF.js.map +1 -0
  18. package/dist/chunk-LSB7GRLG.js +779 -0
  19. package/dist/chunk-LSB7GRLG.js.map +1 -0
  20. package/dist/chunk-MYBZRDTY.js +340 -0
  21. package/dist/chunk-MYBZRDTY.js.map +1 -0
  22. package/dist/chunk-RIY7GYQS.js +1204 -0
  23. package/dist/chunk-RIY7GYQS.js.map +1 -0
  24. package/dist/chunk-TZ6YEYN4.js +386 -0
  25. package/dist/chunk-TZ6YEYN4.js.map +1 -0
  26. package/dist/chunk-VA22KRHS.js +15177 -0
  27. package/dist/chunk-VA22KRHS.js.map +1 -0
  28. package/dist/chunk-Y3MLSLB4.js +392 -0
  29. package/dist/chunk-Y3MLSLB4.js.map +1 -0
  30. package/dist/chunk-YRKRZ6OF.js +134 -0
  31. package/dist/chunk-YRKRZ6OF.js.map +1 -0
  32. package/dist/company-profile.d.ts +629 -0
  33. package/dist/document-artifact-commands.d.ts +282 -0
  34. package/dist/document-artifact-commands.js +54 -0
  35. package/dist/document-artifact-commands.js.map +1 -0
  36. package/dist/document-artifact-query.d.ts +2 -0
  37. package/dist/document-artifact-query.js +54 -0
  38. package/dist/document-artifact-query.js.map +1 -0
  39. package/dist/editable-artifact-binary.d.ts +56 -0
  40. package/dist/editable-artifact-causal-frontier.d.ts +12 -0
  41. package/dist/editable-artifact-causal-frontier.js +18 -0
  42. package/dist/editable-artifact-causal-frontier.js.map +1 -0
  43. package/dist/editable-artifact-codec-registry.d.ts +48 -0
  44. package/dist/editable-artifact-codec-registry.js +20 -0
  45. package/dist/editable-artifact-codec-registry.js.map +1 -0
  46. package/dist/editable-artifact-committed-transaction.d.ts +35 -0
  47. package/dist/editable-artifact-committed-transaction.js +19 -0
  48. package/dist/editable-artifact-committed-transaction.js.map +1 -0
  49. package/dist/editable-artifact-live.d.ts +276 -0
  50. package/dist/editable-artifact-live.js +907 -0
  51. package/dist/editable-artifact-live.js.map +1 -0
  52. package/dist/editable-artifact-serialized-commit.d.ts +53 -0
  53. package/dist/editable-artifact-serialized-commit.js +26 -0
  54. package/dist/editable-artifact-serialized-commit.js.map +1 -0
  55. package/dist/editable-artifact-versions.d.ts +6 -0
  56. package/dist/editable-artifact-versions.js +11 -0
  57. package/dist/editable-artifact-versions.js.map +1 -0
  58. package/dist/editable-artifacts.d.ts +46 -0
  59. package/dist/editable-artifacts.js +236 -0
  60. package/dist/editable-artifacts.js.map +1 -0
  61. package/dist/event-preview.d.ts +7 -7
  62. package/dist/google-drive.d.ts +114 -2
  63. package/dist/google-drive.js +70 -14
  64. package/dist/google-drive.js.map +1 -1
  65. package/dist/image-generation.d.ts +70 -0
  66. package/dist/index.d.ts +5180 -329
  67. package/dist/index.js +1173 -40
  68. package/dist/interaction.d.ts +6895 -0
  69. package/dist/mcp-receipts.d.ts +96 -0
  70. package/dist/memory-slack-delivery.d.ts +397 -0
  71. package/dist/presentation-artifact-commands.d.ts +349 -0
  72. package/dist/presentation-artifact-commands.js +66 -0
  73. package/dist/presentation-artifact-commands.js.map +1 -0
  74. package/dist/presentation-artifact-query.d.ts +2 -0
  75. package/dist/presentation-artifact-query.js +66 -0
  76. package/dist/presentation-artifact-query.js.map +1 -0
  77. package/dist/retained-output.d.ts +83 -6
  78. package/dist/sandbox-snapshots.d.ts +9 -0
  79. package/dist/scoped-knowledge.d.ts +14 -13
  80. package/dist/slack-bot-scopes.d.ts +4 -0
  81. package/dist/slack-bot-scopes.js +3 -1
  82. package/dist/spreadsheet-artifact-commands.d.ts +101 -0
  83. package/dist/spreadsheet-artifact-commands.js +27 -0
  84. package/dist/spreadsheet-artifact-commands.js.map +1 -0
  85. package/dist/spreadsheet-artifact-date.d.ts +4 -0
  86. package/dist/spreadsheet-artifact-query.d.ts +120 -0
  87. package/dist/spreadsheet-artifact-query.js +48 -0
  88. package/dist/spreadsheet-artifact-query.js.map +1 -0
  89. package/dist/tool-catalog.d.ts +576 -0
  90. package/dist/video-generation.d.ts +532 -0
  91. package/dist/video-generation.js +51 -0
  92. package/dist/video-generation.js.map +1 -0
  93. package/dist/workspace-learning-policy.d.ts +208 -0
  94. package/package.json +61 -1
  95. package/src/atlassian.ts +160 -0
  96. package/src/company-profile.ts +306 -0
  97. package/src/document-artifact-commands.ts +1709 -0
  98. package/src/document-artifact-query.ts +2 -0
  99. package/src/editable-artifact-binary.ts +396 -0
  100. package/src/editable-artifact-causal-frontier.ts +154 -0
  101. package/src/editable-artifact-codec-registry.ts +261 -0
  102. package/src/editable-artifact-committed-transaction.ts +478 -0
  103. package/src/editable-artifact-live.ts +1379 -0
  104. package/src/editable-artifact-serialized-commit.ts +494 -0
  105. package/src/editable-artifact-versions.ts +8 -0
  106. package/src/editable-artifacts.ts +473 -0
  107. package/src/event-preview.ts +6 -6
  108. package/src/google-drive.ts +68 -15
  109. package/src/image-generation.ts +106 -0
  110. package/src/index.ts +2535 -282
  111. package/src/interaction.ts +2292 -0
  112. package/src/mcp-receipts.ts +281 -0
  113. package/src/memory-slack-delivery.ts +183 -0
  114. package/src/presentation-artifact-commands.ts +2035 -0
  115. package/src/presentation-artifact-query.ts +2 -0
  116. package/src/retained-output.ts +248 -8
  117. package/src/sandbox-snapshots.ts +28 -1
  118. package/src/scoped-knowledge.ts +18 -4
  119. package/src/slack-bot-scopes.ts +34 -1
  120. package/src/spreadsheet-artifact-commands.ts +999 -0
  121. package/src/spreadsheet-artifact-date.ts +21 -0
  122. package/src/spreadsheet-artifact-query.ts +1411 -0
  123. package/src/tool-catalog.ts +334 -0
  124. package/src/video-generation.ts +394 -0
  125. package/src/workspace-learning-policy.ts +233 -0
  126. package/dist/chunk-7JZMI22I.js.map +0 -1
  127. package/dist/chunk-PCTCJEAF.js +0 -10799
  128. package/dist/chunk-PCTCJEAF.js.map +0 -1
  129. package/dist/secret-redaction.d.ts +0 -40
  130. package/src/secret-redaction.ts +0 -364
@@ -0,0 +1,1379 @@
1
+ import {
2
+ EDITABLE_ARTIFACT_INTENT_MAX_BYTES,
3
+ hashEditableArtifactMutationIntentBytes,
4
+ type EditableArtifactCausalEntry,
5
+ } from "./editable-artifacts";
6
+
7
+ const MAGIC = new TextEncoder().encode("OGALV001");
8
+ const HEADER_BYTES = 20;
9
+ const MAX_METADATA_BYTES = 64 * 1024;
10
+ const MAX_TOKEN_BYTES = 4_096;
11
+ const MAX_IDENTIFIER_BYTES = 512;
12
+ const MAX_CAUSAL_ACTORS = 1_024;
13
+ const textEncoder = new TextEncoder();
14
+ const strictDecoder = new TextDecoder("utf-8", { fatal: true });
15
+
16
+ const CLIENT_OPEN = 1;
17
+ const CLIENT_APPLIED = 2;
18
+ const CLIENT_MUTATION = 3;
19
+
20
+ const SERVER_OPEN = 64;
21
+ const SERVER_SNAPSHOT = 65;
22
+ const SERVER_TRANSACTION = 66;
23
+ const SERVER_BARRIER = 67;
24
+ const SERVER_WATERMARK = 68;
25
+ const SERVER_RESYNC_REQUIRED = 69;
26
+ const SERVER_APPLIED = 70;
27
+ const SERVER_AUTHORIZATION_CHANGED = 71;
28
+ const SERVER_MUTATION_ACCEPTED = 72;
29
+ const SERVER_MUTATION_REJECTED = 73;
30
+ const RESYNC_REASONS = new Set([
31
+ "retention_gap",
32
+ "durable_gap",
33
+ "invalid_ack",
34
+ "slow_client",
35
+ "oversized_frame",
36
+ ]);
37
+ const MUTATION_REJECTION_CODES = new Set([
38
+ "invalid_request",
39
+ "forbidden",
40
+ "conflict",
41
+ "unsupported",
42
+ "unavailable",
43
+ ]);
44
+
45
+ export const EDITABLE_ARTIFACT_LIVE_WIRE_VERSION = 1;
46
+
47
+ export type EditableArtifactId = string;
48
+ export type EditableArtifactRequestHash = string;
49
+ export type EditableArtifactStateHash = string;
50
+ export type EditableArtifactContentHash = string;
51
+ export type EditableArtifactTransactionId = string;
52
+ export type EditableArtifactClientTransactionId = string;
53
+ export type EditableArtifactCausalFrontier = readonly EditableArtifactCausalEntry[];
54
+ export type EditableArtifactLiveModality = "document" | "spreadsheet" | "presentation";
55
+
56
+ export type EditableArtifactLiveErrorCode =
57
+ | "invalid_ticket"
58
+ | "ticket_expired"
59
+ | "ticket_replayed"
60
+ | "protocol_mismatch"
61
+ | "permission_changed"
62
+ | "invalid_frame"
63
+ | "oversized_frame"
64
+ | "stale_epoch"
65
+ | "invalid_ack"
66
+ | "durable_gap"
67
+ | "retention_gap"
68
+ | "slow_client"
69
+ | "closed";
70
+
71
+ /** Shared transport error: core and SDK must classify the same frame identically. */
72
+ export class EditableArtifactLiveError extends Error {
73
+ constructor(
74
+ readonly code: EditableArtifactLiveErrorCode,
75
+ message: string,
76
+ readonly options: Readonly<{
77
+ retryable?: boolean;
78
+ requiresSnapshot?: boolean;
79
+ cause?: unknown;
80
+ }> = {},
81
+ ) {
82
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
83
+ this.name = "EditableArtifactLiveError";
84
+ }
85
+
86
+ get retryable(): boolean {
87
+ return this.options.retryable ?? false;
88
+ }
89
+
90
+ get requiresSnapshot(): boolean {
91
+ return this.options.requiresSnapshot ?? false;
92
+ }
93
+ }
94
+
95
+ type EditableArtifactLiveResumeCommon = Readonly<{
96
+ localCursor: number | null;
97
+ localStateHash: EditableArtifactStateHash | null;
98
+ requireSnapshot: boolean;
99
+ }>;
100
+
101
+ export type EditableArtifactLiveResume =
102
+ | (EditableArtifactLiveResumeCommon &
103
+ Readonly<{
104
+ /** Omitted on OGALV001 for byte-compatible spreadsheet sessions. */
105
+ modality?: "spreadsheet";
106
+ localCausalFrontier: EditableArtifactCausalFrontier;
107
+ }>)
108
+ | (EditableArtifactLiveResumeCommon &
109
+ Readonly<{
110
+ modality: "document" | "presentation";
111
+ localNativeRevision: number | null;
112
+ }>);
113
+
114
+ export type EditableArtifactLiveTicket = Readonly<{
115
+ artifactId: EditableArtifactId;
116
+ modality: EditableArtifactLiveModality;
117
+ replicaId: string;
118
+ token: string;
119
+ expiresAt: string;
120
+ protocolVersion: number;
121
+ }>;
122
+
123
+ type EditableArtifactLiveSnapshotCommon = Readonly<{
124
+ artifactId: EditableArtifactId;
125
+ sequence: number;
126
+ stateHash: EditableArtifactStateHash;
127
+ digest: EditableArtifactContentHash;
128
+ kernelVersion: string;
129
+ modelSchemaVersion: number;
130
+ bytes: Uint8Array;
131
+ }>;
132
+
133
+ export type EditableArtifactLiveSnapshot =
134
+ | (EditableArtifactLiveSnapshotCommon &
135
+ Readonly<{
136
+ modality?: "spreadsheet";
137
+ causalFrontier: EditableArtifactCausalFrontier;
138
+ protocolVersion: number;
139
+ }>)
140
+ | (EditableArtifactLiveSnapshotCommon &
141
+ Readonly<{
142
+ modality: "document" | "presentation";
143
+ nativeRevision: number;
144
+ }>);
145
+
146
+ type EditableArtifactLiveCommittedTransactionCommon = Readonly<{
147
+ artifactId: EditableArtifactId;
148
+ transactionId: EditableArtifactTransactionId;
149
+ requestHash: EditableArtifactRequestHash;
150
+ startSequence: number;
151
+ endSequence: number;
152
+ priorStateHash: EditableArtifactStateHash;
153
+ stateHash: EditableArtifactStateHash;
154
+ committedTransactionBytes: Uint8Array;
155
+ }>;
156
+
157
+ export type EditableArtifactLiveCommittedTransaction =
158
+ | (EditableArtifactLiveCommittedTransactionCommon &
159
+ Readonly<{
160
+ modality?: "spreadsheet";
161
+ causalFrontier: EditableArtifactCausalFrontier;
162
+ protocolVersion: number;
163
+ /** Exact canonical whole OGACO transaction envelope. */
164
+ committedTransactionBytes: Uint8Array;
165
+ }>)
166
+ | (EditableArtifactLiveCommittedTransactionCommon &
167
+ Readonly<{
168
+ modality: "document" | "presentation";
169
+ priorNativeRevision: number;
170
+ nativeRevision: number;
171
+ commitProtocolVersion: number;
172
+ /** Exact canonical whole OGAST transaction envelope. */
173
+ committedTransactionBytes: Uint8Array;
174
+ }>);
175
+
176
+ type EditableArtifactLiveHeadCommon = Readonly<{
177
+ headSequence: number;
178
+ stateHash: EditableArtifactStateHash;
179
+ minimumReplaySequence: number;
180
+ }>;
181
+
182
+ export type EditableArtifactLiveHead =
183
+ | (EditableArtifactLiveHeadCommon &
184
+ Readonly<{
185
+ modality: "spreadsheet";
186
+ causalFrontier: EditableArtifactCausalFrontier;
187
+ }>)
188
+ | (EditableArtifactLiveHeadCommon &
189
+ Readonly<{
190
+ modality: "document" | "presentation";
191
+ nativeRevision: number;
192
+ }>);
193
+
194
+ export type EditableArtifactLiveBootstrap = EditableArtifactLiveHead &
195
+ Readonly<{
196
+ resumeAccepted: boolean;
197
+ resumeSequence: number;
198
+ resumeStateHash: EditableArtifactStateHash;
199
+ snapshot: EditableArtifactLiveSnapshot | null;
200
+ }>;
201
+
202
+ export type EditableArtifactLiveAppliedClientFrame = Readonly<{
203
+ type: "applied";
204
+ protocolVersion: number;
205
+ artifactId: EditableArtifactId;
206
+ streamEpoch: string;
207
+ sequence: number;
208
+ stateHash: EditableArtifactStateHash;
209
+ }>;
210
+
211
+ export type EditableArtifactLiveMutationClientFrame = Readonly<{
212
+ protocolVersion: number;
213
+ artifactId: EditableArtifactId;
214
+ streamEpoch: string;
215
+ requestHash: EditableArtifactRequestHash;
216
+ /** Exact bounded OGATX001 client-intent envelope, never canonical operations. */
217
+ intentBytes: Uint8Array;
218
+ }>;
219
+
220
+ export type EditableArtifactLiveClientFrame = EditableArtifactLiveAppliedClientFrame;
221
+
222
+ export type EditableArtifactLiveCloseReason =
223
+ | "closed"
224
+ | "ticket_expired"
225
+ | "ticket_replayed"
226
+ | "protocol_mismatch"
227
+ | "permission_changed"
228
+ | "invalid_frame"
229
+ | "oversized_frame"
230
+ | "stale_epoch"
231
+ | "invalid_ack"
232
+ | "retention_gap"
233
+ | "durable_gap"
234
+ | "slow_client"
235
+ | "transport_error";
236
+
237
+ export type EditableArtifactLiveClose = Readonly<{
238
+ reason: EditableArtifactLiveCloseReason;
239
+ retryable: boolean;
240
+ requiresSnapshot: boolean;
241
+ }>;
242
+
243
+ export type EditableArtifactLiveMutationReceipt = Readonly<{
244
+ clientTransactionId: EditableArtifactClientTransactionId;
245
+ requestHash: EditableArtifactRequestHash;
246
+ transaction: EditableArtifactLiveCommittedTransaction;
247
+ }>;
248
+
249
+ export type EditableArtifactLiveServerFrame =
250
+ | Readonly<{
251
+ type: "open";
252
+ protocolVersion: number;
253
+ artifactId: EditableArtifactId;
254
+ streamEpoch: string;
255
+ /** Omitted on the wire for spreadsheet compatibility. */
256
+ modality?: EditableArtifactLiveModality;
257
+ writable: boolean;
258
+ headSequence: number;
259
+ minimumReplaySequence: number;
260
+ maxClientFrameBytes: number;
261
+ maxCommandBytes: number;
262
+ maxIntentBytes: number;
263
+ maxCommittedTransactionBytes: number;
264
+ maxSnapshotBytes: number;
265
+ maxInFlightTransactions: number;
266
+ maxInFlightBytes: number;
267
+ }>
268
+ | (Readonly<{
269
+ type: "snapshot";
270
+ protocolVersion: number;
271
+ artifactId: EditableArtifactId;
272
+ streamEpoch: string;
273
+ sequence: number;
274
+ stateHash: EditableArtifactStateHash;
275
+ digest: EditableArtifactContentHash;
276
+ kernelVersion: string;
277
+ modelSchemaVersion: number;
278
+ offset: number;
279
+ totalBytes: number;
280
+ final: boolean;
281
+ bytes: Uint8Array;
282
+ }> &
283
+ (
284
+ | Readonly<{
285
+ /** Omitted on the wire for spreadsheet compatibility. */
286
+ modality?: "spreadsheet";
287
+ causalFrontier: EditableArtifactCausalFrontier;
288
+ }>
289
+ | Readonly<{
290
+ modality: "document" | "presentation";
291
+ nativeRevision: number;
292
+ }>
293
+ ))
294
+ | Readonly<{
295
+ type: "transaction";
296
+ protocolVersion: number;
297
+ artifactId: EditableArtifactId;
298
+ streamEpoch: string;
299
+ transaction: EditableArtifactLiveCommittedTransaction;
300
+ }>
301
+ | Readonly<{
302
+ type: "barrier";
303
+ protocolVersion: number;
304
+ artifactId: EditableArtifactId;
305
+ streamEpoch: string;
306
+ sequence: number;
307
+ stateHash: EditableArtifactStateHash;
308
+ }>
309
+ | Readonly<{
310
+ type: "watermark";
311
+ protocolVersion: number;
312
+ artifactId: EditableArtifactId;
313
+ streamEpoch: string;
314
+ headSequence: number;
315
+ }>
316
+ | Readonly<{
317
+ type: "resyncRequired";
318
+ protocolVersion: number;
319
+ artifactId: EditableArtifactId;
320
+ streamEpoch: string;
321
+ reason: "retention_gap" | "durable_gap" | "invalid_ack" | "slow_client" | "oversized_frame";
322
+ headSequence: number;
323
+ }>
324
+ | Readonly<{
325
+ type: "applied";
326
+ protocolVersion: number;
327
+ artifactId: EditableArtifactId;
328
+ streamEpoch: string;
329
+ sequence: number;
330
+ stateHash: EditableArtifactStateHash;
331
+ }>
332
+ | Readonly<{
333
+ type: "authorizationChanged";
334
+ protocolVersion: number;
335
+ artifactId: EditableArtifactId;
336
+ streamEpoch: string;
337
+ writable: boolean;
338
+ }>
339
+ | Readonly<{
340
+ type: "mutationAccepted";
341
+ protocolVersion: number;
342
+ artifactId: EditableArtifactId;
343
+ streamEpoch: string;
344
+ requestHash: EditableArtifactRequestHash;
345
+ clientTransactionId: EditableArtifactClientTransactionId;
346
+ transactionId: EditableArtifactTransactionId;
347
+ startSequence: number;
348
+ endSequence: number;
349
+ stateHash: EditableArtifactStateHash;
350
+ }>
351
+ | Readonly<{
352
+ type: "mutationRejected";
353
+ protocolVersion: number;
354
+ artifactId: EditableArtifactId;
355
+ streamEpoch: string;
356
+ requestHash: EditableArtifactRequestHash;
357
+ code: "invalid_request" | "forbidden" | "conflict" | "unsupported" | "unavailable";
358
+ retryable: boolean;
359
+ }>;
360
+
361
+ export type EditableArtifactLiveOpenWireFrame = Readonly<{
362
+ type: "open";
363
+ protocolVersion: number;
364
+ artifactId: EditableArtifactId;
365
+ token: string;
366
+ resume: EditableArtifactLiveResume;
367
+ }>;
368
+
369
+ export type EditableArtifactLiveAppliedWireFrame = Readonly<{
370
+ type: "applied";
371
+ protocolVersion: number;
372
+ artifactId: EditableArtifactId;
373
+ streamEpoch: string;
374
+ sequence: number;
375
+ stateHash: EditableArtifactStateHash;
376
+ }>;
377
+
378
+ export type EditableArtifactLiveMutationWireFrame = Readonly<{
379
+ type: "mutation";
380
+ protocolVersion: number;
381
+ artifactId: EditableArtifactId;
382
+ streamEpoch: string;
383
+ requestHash: EditableArtifactRequestHash;
384
+ /** Exact OGATX001 bytes. No client-authored operation/state projection exists. */
385
+ intentBytes: Uint8Array;
386
+ }>;
387
+
388
+ export type EditableArtifactLiveClientWireFrame =
389
+ | EditableArtifactLiveOpenWireFrame
390
+ | EditableArtifactLiveAppliedWireFrame
391
+ | EditableArtifactLiveMutationWireFrame;
392
+
393
+ /**
394
+ * One bounded binary WebSocket envelope. Metadata is strict canonical JSON;
395
+ * snapshots, OGATX intents, and committed OGACO transactions remain opaque
396
+ * binary payloads and are never base64-encoded or reserialized by transport.
397
+ */
398
+ export function decodeEditableArtifactLiveClientWireFrame(
399
+ bytes: Uint8Array,
400
+ ): EditableArtifactLiveClientWireFrame {
401
+ const decoded = decodeEnvelope(bytes);
402
+ if (decoded.kind === CLIENT_OPEN) return decodeOpen(decoded.metadata, decoded.payload);
403
+ if (decoded.kind === CLIENT_APPLIED) return decodeApplied(decoded.metadata, decoded.payload);
404
+ if (decoded.kind === CLIENT_MUTATION) return decodeMutation(decoded.metadata, decoded.payload);
405
+ throw invalidFrame("Unknown editable artifact client frame kind");
406
+ }
407
+
408
+ export function encodeEditableArtifactLiveOpenWireFrame(
409
+ frame: EditableArtifactLiveOpenWireFrame,
410
+ ): Uint8Array {
411
+ const artifactId = editableArtifactId(frame.artifactId);
412
+ const protocolVersion = positiveInteger(frame.protocolVersion, "protocolVersion");
413
+ const token = boundedToken(frame.token);
414
+ const resume = normalizeResume(frame.resume);
415
+ if (resume.modality === "document" || resume.modality === "presentation") {
416
+ return encodeEnvelope(
417
+ CLIENT_OPEN,
418
+ {
419
+ type: "open",
420
+ protocolVersion,
421
+ artifactId,
422
+ token,
423
+ modality: resume.modality,
424
+ localCursor: resume.localCursor,
425
+ localStateHash: resume.localStateHash,
426
+ localNativeRevision: resume.localNativeRevision,
427
+ requireSnapshot: resume.requireSnapshot,
428
+ },
429
+ EMPTY_BYTES,
430
+ );
431
+ }
432
+ if (!("localCausalFrontier" in resume)) {
433
+ throw invalidFrame("spreadsheet resume requires a causal frontier");
434
+ }
435
+ return encodeEnvelope(
436
+ CLIENT_OPEN,
437
+ {
438
+ type: "open",
439
+ protocolVersion,
440
+ artifactId,
441
+ token,
442
+ localCursor: resume.localCursor,
443
+ localStateHash: resume.localStateHash,
444
+ localCausalFrontier: resume.localCausalFrontier,
445
+ requireSnapshot: resume.requireSnapshot,
446
+ },
447
+ EMPTY_BYTES,
448
+ );
449
+ }
450
+
451
+ export function encodeEditableArtifactLiveAppliedWireFrame(
452
+ frame: EditableArtifactLiveAppliedWireFrame,
453
+ ): Uint8Array {
454
+ return encodeEnvelope(
455
+ CLIENT_APPLIED,
456
+ {
457
+ type: "applied",
458
+ protocolVersion: positiveInteger(frame.protocolVersion, "protocolVersion"),
459
+ artifactId: editableArtifactId(frame.artifactId),
460
+ streamEpoch: boundedString(frame.streamEpoch, "streamEpoch", MAX_IDENTIFIER_BYTES),
461
+ sequence: nonnegativeInteger(frame.sequence, "sequence"),
462
+ stateHash: editableArtifactStateHash(frame.stateHash),
463
+ },
464
+ EMPTY_BYTES,
465
+ );
466
+ }
467
+
468
+ export function encodeEditableArtifactLiveMutationWireFrame(
469
+ frame: EditableArtifactLiveMutationWireFrame,
470
+ ): Uint8Array {
471
+ const intentBytes = ownPayload(frame.intentBytes, EDITABLE_ARTIFACT_INTENT_MAX_BYTES, false);
472
+ const requestHash = editableArtifactRequestHash(frame.requestHash);
473
+ if (hashEditableArtifactMutationIntentBytes(intentBytes) !== requestHash) {
474
+ throw invalidFrame("Mutation request hash does not match exact OGATX bytes");
475
+ }
476
+ return encodeEnvelope(
477
+ CLIENT_MUTATION,
478
+ {
479
+ type: "mutation",
480
+ protocolVersion: positiveInteger(frame.protocolVersion, "protocolVersion"),
481
+ artifactId: editableArtifactId(frame.artifactId),
482
+ streamEpoch: boundedString(frame.streamEpoch, "streamEpoch", MAX_IDENTIFIER_BYTES),
483
+ requestHash,
484
+ },
485
+ intentBytes,
486
+ );
487
+ }
488
+
489
+ export function encodeEditableArtifactLiveServerWireFrame(
490
+ frame: EditableArtifactLiveServerFrame,
491
+ ): Uint8Array {
492
+ switch (frame.type) {
493
+ case "open": {
494
+ const { modality, ...metadata } = frame;
495
+ return encodeEnvelope(
496
+ SERVER_OPEN,
497
+ modality === "document" || modality === "presentation"
498
+ ? {
499
+ type: metadata.type,
500
+ protocolVersion: metadata.protocolVersion,
501
+ artifactId: metadata.artifactId,
502
+ streamEpoch: metadata.streamEpoch,
503
+ modality,
504
+ writable: metadata.writable,
505
+ headSequence: metadata.headSequence,
506
+ minimumReplaySequence: metadata.minimumReplaySequence,
507
+ maxClientFrameBytes: metadata.maxClientFrameBytes,
508
+ maxCommandBytes: metadata.maxCommandBytes,
509
+ maxIntentBytes: metadata.maxIntentBytes,
510
+ maxCommittedTransactionBytes: metadata.maxCommittedTransactionBytes,
511
+ maxSnapshotBytes: metadata.maxSnapshotBytes,
512
+ maxInFlightTransactions: metadata.maxInFlightTransactions,
513
+ maxInFlightBytes: metadata.maxInFlightBytes,
514
+ }
515
+ : metadata,
516
+ EMPTY_BYTES,
517
+ );
518
+ }
519
+ case "snapshot": {
520
+ const { bytes, modality, ...metadata } = frame;
521
+ if (
522
+ (modality === "document" || modality === "presentation") &&
523
+ !("nativeRevision" in metadata)
524
+ ) {
525
+ throw invalidFrame("serialized snapshot requires a native revision");
526
+ }
527
+ if (
528
+ modality !== "document" &&
529
+ modality !== "presentation" &&
530
+ !("causalFrontier" in metadata)
531
+ ) {
532
+ throw invalidFrame("spreadsheet snapshot requires a causal frontier");
533
+ }
534
+ return encodeEnvelope(
535
+ SERVER_SNAPSHOT,
536
+ modality === "document" || modality === "presentation"
537
+ ? {
538
+ type: metadata.type,
539
+ protocolVersion: metadata.protocolVersion,
540
+ artifactId: metadata.artifactId,
541
+ streamEpoch: metadata.streamEpoch,
542
+ modality,
543
+ sequence: metadata.sequence,
544
+ stateHash: metadata.stateHash,
545
+ nativeRevision: (metadata as { nativeRevision: number }).nativeRevision,
546
+ digest: metadata.digest,
547
+ kernelVersion: metadata.kernelVersion,
548
+ modelSchemaVersion: metadata.modelSchemaVersion,
549
+ offset: metadata.offset,
550
+ totalBytes: metadata.totalBytes,
551
+ final: metadata.final,
552
+ }
553
+ : {
554
+ type: metadata.type,
555
+ protocolVersion: metadata.protocolVersion,
556
+ artifactId: metadata.artifactId,
557
+ streamEpoch: metadata.streamEpoch,
558
+ sequence: metadata.sequence,
559
+ stateHash: metadata.stateHash,
560
+ causalFrontier: (metadata as { causalFrontier: EditableArtifactCausalFrontier })
561
+ .causalFrontier,
562
+ digest: metadata.digest,
563
+ kernelVersion: metadata.kernelVersion,
564
+ modelSchemaVersion: metadata.modelSchemaVersion,
565
+ offset: metadata.offset,
566
+ totalBytes: metadata.totalBytes,
567
+ final: metadata.final,
568
+ },
569
+ bytes,
570
+ );
571
+ }
572
+ case "transaction": {
573
+ const { committedTransactionBytes, ...transaction } = frame.transaction;
574
+ const serializedTransaction = transaction as Extract<
575
+ EditableArtifactLiveCommittedTransaction,
576
+ { modality: "document" | "presentation" }
577
+ >;
578
+ const transactionMetadata =
579
+ transaction.modality === undefined || transaction.modality === "spreadsheet"
580
+ ? {
581
+ artifactId: transaction.artifactId,
582
+ transactionId: transaction.transactionId,
583
+ requestHash: transaction.requestHash,
584
+ startSequence: transaction.startSequence,
585
+ endSequence: transaction.endSequence,
586
+ priorStateHash: transaction.priorStateHash,
587
+ stateHash: transaction.stateHash,
588
+ causalFrontier: transaction.causalFrontier,
589
+ protocolVersion: transaction.protocolVersion,
590
+ }
591
+ : {
592
+ artifactId: serializedTransaction.artifactId,
593
+ modality: serializedTransaction.modality,
594
+ transactionId: serializedTransaction.transactionId,
595
+ requestHash: serializedTransaction.requestHash,
596
+ startSequence: serializedTransaction.startSequence,
597
+ endSequence: serializedTransaction.endSequence,
598
+ priorStateHash: serializedTransaction.priorStateHash,
599
+ stateHash: serializedTransaction.stateHash,
600
+ priorNativeRevision: serializedTransaction.priorNativeRevision,
601
+ nativeRevision: serializedTransaction.nativeRevision,
602
+ commitProtocolVersion: serializedTransaction.commitProtocolVersion,
603
+ };
604
+ return encodeEnvelope(
605
+ SERVER_TRANSACTION,
606
+ { ...frame, transaction: transactionMetadata },
607
+ committedTransactionBytes,
608
+ );
609
+ }
610
+ case "barrier":
611
+ return encodeEnvelope(SERVER_BARRIER, { ...frame }, EMPTY_BYTES);
612
+ case "watermark":
613
+ return encodeEnvelope(SERVER_WATERMARK, { ...frame }, EMPTY_BYTES);
614
+ case "resyncRequired":
615
+ return encodeEnvelope(SERVER_RESYNC_REQUIRED, { ...frame }, EMPTY_BYTES);
616
+ case "applied":
617
+ return encodeEnvelope(SERVER_APPLIED, { ...frame }, EMPTY_BYTES);
618
+ case "authorizationChanged":
619
+ return encodeEnvelope(SERVER_AUTHORIZATION_CHANGED, { ...frame }, EMPTY_BYTES);
620
+ case "mutationAccepted":
621
+ return encodeEnvelope(SERVER_MUTATION_ACCEPTED, { ...frame }, EMPTY_BYTES);
622
+ case "mutationRejected":
623
+ return encodeEnvelope(SERVER_MUTATION_REJECTED, { ...frame }, EMPTY_BYTES);
624
+ }
625
+ }
626
+
627
+ /** Strict inverse used by browser/desktop SDK transports. */
628
+ export function decodeEditableArtifactLiveServerWireFrame(
629
+ bytes: Uint8Array,
630
+ ): EditableArtifactLiveServerFrame {
631
+ const decoded = decodeEnvelope(bytes);
632
+ const metadata = decoded.metadata;
633
+ switch (decoded.kind) {
634
+ case SERVER_OPEN: {
635
+ requireNoPayload(decoded.payload);
636
+ const modality = serverModality(metadata);
637
+ requireServerKeys(metadata, "open", [
638
+ ...(modality === "spreadsheet" ? [] : ["modality"]),
639
+ "writable",
640
+ "headSequence",
641
+ "minimumReplaySequence",
642
+ "maxClientFrameBytes",
643
+ "maxCommandBytes",
644
+ "maxIntentBytes",
645
+ "maxCommittedTransactionBytes",
646
+ "maxSnapshotBytes",
647
+ "maxInFlightTransactions",
648
+ "maxInFlightBytes",
649
+ ]);
650
+ const common = {
651
+ ...serverIdentity(metadata, "open"),
652
+ writable: requiredBoolean(metadata.writable, "writable"),
653
+ headSequence: nonnegativeInteger(metadata.headSequence, "headSequence"),
654
+ minimumReplaySequence: nonnegativeInteger(
655
+ metadata.minimumReplaySequence,
656
+ "minimumReplaySequence",
657
+ ),
658
+ maxClientFrameBytes: positiveInteger(metadata.maxClientFrameBytes, "maxClientFrameBytes"),
659
+ maxCommandBytes: nonnegativeInteger(metadata.maxCommandBytes, "maxCommandBytes"),
660
+ maxIntentBytes: nonnegativeInteger(metadata.maxIntentBytes, "maxIntentBytes"),
661
+ maxCommittedTransactionBytes: nonnegativeInteger(
662
+ metadata.maxCommittedTransactionBytes,
663
+ "maxCommittedTransactionBytes",
664
+ ),
665
+ maxSnapshotBytes: positiveInteger(metadata.maxSnapshotBytes, "maxSnapshotBytes"),
666
+ maxInFlightTransactions: positiveInteger(
667
+ metadata.maxInFlightTransactions,
668
+ "maxInFlightTransactions",
669
+ ),
670
+ maxInFlightBytes: positiveInteger(metadata.maxInFlightBytes, "maxInFlightBytes"),
671
+ } as const;
672
+ return Object.freeze(modality === "spreadsheet" ? common : { ...common, modality });
673
+ }
674
+ case SERVER_SNAPSHOT: {
675
+ const modality = serverModality(metadata);
676
+ requireServerKeys(metadata, "snapshot", [
677
+ ...(modality === "spreadsheet" ? [] : ["modality"]),
678
+ "sequence",
679
+ "stateHash",
680
+ modality === "spreadsheet" ? "causalFrontier" : "nativeRevision",
681
+ "digest",
682
+ "kernelVersion",
683
+ "modelSchemaVersion",
684
+ "offset",
685
+ "totalBytes",
686
+ "final",
687
+ ]);
688
+ const offset = nonnegativeInteger(metadata.offset, "offset");
689
+ const totalBytes = positiveInteger(metadata.totalBytes, "totalBytes");
690
+ const final = requiredBoolean(metadata.final, "final");
691
+ if (decoded.payload.byteLength === 0 || offset + decoded.payload.byteLength > totalBytes) {
692
+ throw invalidFrame("snapshot chunk bounds are invalid");
693
+ }
694
+ if (final !== (offset + decoded.payload.byteLength === totalBytes)) {
695
+ throw invalidFrame("snapshot final flag does not match its byte boundary");
696
+ }
697
+ const common = {
698
+ ...serverIdentity(metadata, "snapshot"),
699
+ sequence: nonnegativeInteger(metadata.sequence, "sequence"),
700
+ stateHash: editableArtifactStateHash(requiredString(metadata.stateHash, "stateHash")),
701
+ digest: editableArtifactContentHash(requiredString(metadata.digest, "digest")),
702
+ kernelVersion: boundedString(
703
+ requiredString(metadata.kernelVersion, "kernelVersion"),
704
+ "kernelVersion",
705
+ MAX_IDENTIFIER_BYTES,
706
+ ),
707
+ modelSchemaVersion: positiveInteger(metadata.modelSchemaVersion, "modelSchemaVersion"),
708
+ offset,
709
+ totalBytes,
710
+ final,
711
+ bytes: decoded.payload,
712
+ } as const;
713
+ return Object.freeze(
714
+ modality === "spreadsheet"
715
+ ? {
716
+ ...common,
717
+ causalFrontier: requiredFrontier(metadata.causalFrontier),
718
+ }
719
+ : {
720
+ ...common,
721
+ modality,
722
+ nativeRevision: nonnegativeInteger(metadata.nativeRevision, "nativeRevision"),
723
+ },
724
+ );
725
+ }
726
+ case SERVER_TRANSACTION: {
727
+ requireServerKeys(metadata, "transaction", ["transaction"]);
728
+ const transaction = requiredRecord(metadata.transaction, "transaction");
729
+ const modality = transactionModality(transaction);
730
+ requireExactKeys(
731
+ transaction,
732
+ modality === "spreadsheet"
733
+ ? [
734
+ "artifactId",
735
+ "transactionId",
736
+ "requestHash",
737
+ "startSequence",
738
+ "endSequence",
739
+ "priorStateHash",
740
+ "stateHash",
741
+ "causalFrontier",
742
+ "protocolVersion",
743
+ ]
744
+ : [
745
+ "artifactId",
746
+ "modality",
747
+ "transactionId",
748
+ "requestHash",
749
+ "startSequence",
750
+ "endSequence",
751
+ "priorStateHash",
752
+ "stateHash",
753
+ "priorNativeRevision",
754
+ "nativeRevision",
755
+ "commitProtocolVersion",
756
+ ],
757
+ );
758
+ if (decoded.payload.byteLength === 0) {
759
+ throw invalidFrame("transaction payload must not be empty");
760
+ }
761
+ const startSequence = positiveInteger(transaction.startSequence, "startSequence");
762
+ const endSequence = positiveInteger(transaction.endSequence, "endSequence");
763
+ if (endSequence < startSequence)
764
+ throw invalidFrame("transaction sequence interval is inverted");
765
+ const common = {
766
+ ...serverIdentity(metadata, "transaction"),
767
+ transaction: {
768
+ artifactId: editableArtifactId(requiredString(transaction.artifactId, "artifactId")),
769
+ transactionId: editableArtifactTransactionId(
770
+ requiredString(transaction.transactionId, "transactionId"),
771
+ ),
772
+ requestHash: editableArtifactRequestHash(
773
+ requiredString(transaction.requestHash, "requestHash"),
774
+ ),
775
+ startSequence,
776
+ endSequence,
777
+ priorStateHash: editableArtifactStateHash(
778
+ requiredString(transaction.priorStateHash, "priorStateHash"),
779
+ ),
780
+ stateHash: editableArtifactStateHash(requiredString(transaction.stateHash, "stateHash")),
781
+ committedTransactionBytes: decoded.payload,
782
+ },
783
+ } as const;
784
+ return Object.freeze({
785
+ ...common,
786
+ transaction: Object.freeze(
787
+ modality === "spreadsheet"
788
+ ? {
789
+ ...common.transaction,
790
+ causalFrontier: requiredFrontier(transaction.causalFrontier),
791
+ protocolVersion: positiveInteger(transaction.protocolVersion, "protocolVersion"),
792
+ }
793
+ : {
794
+ ...common.transaction,
795
+ modality,
796
+ priorNativeRevision: nonnegativeInteger(
797
+ transaction.priorNativeRevision,
798
+ "priorNativeRevision",
799
+ ),
800
+ nativeRevision: nonnegativeInteger(transaction.nativeRevision, "nativeRevision"),
801
+ commitProtocolVersion: positiveInteger(
802
+ transaction.commitProtocolVersion,
803
+ "commitProtocolVersion",
804
+ ),
805
+ },
806
+ ),
807
+ });
808
+ }
809
+ case SERVER_BARRIER:
810
+ case SERVER_APPLIED: {
811
+ const type = decoded.kind === SERVER_BARRIER ? "barrier" : "applied";
812
+ requireNoPayload(decoded.payload);
813
+ requireServerKeys(metadata, type, ["sequence", "stateHash"]);
814
+ return Object.freeze({
815
+ ...serverIdentity(metadata, type),
816
+ sequence: nonnegativeInteger(metadata.sequence, "sequence"),
817
+ stateHash: editableArtifactStateHash(requiredString(metadata.stateHash, "stateHash")),
818
+ });
819
+ }
820
+ case SERVER_WATERMARK: {
821
+ requireNoPayload(decoded.payload);
822
+ requireServerKeys(metadata, "watermark", ["headSequence"]);
823
+ return Object.freeze({
824
+ ...serverIdentity(metadata, "watermark"),
825
+ headSequence: nonnegativeInteger(metadata.headSequence, "headSequence"),
826
+ });
827
+ }
828
+ case SERVER_RESYNC_REQUIRED: {
829
+ requireNoPayload(decoded.payload);
830
+ requireServerKeys(metadata, "resyncRequired", ["reason", "headSequence"]);
831
+ const reason = requiredString(metadata.reason, "reason");
832
+ if (!RESYNC_REASONS.has(reason)) throw invalidFrame("resync reason is invalid");
833
+ return Object.freeze({
834
+ ...serverIdentity(metadata, "resyncRequired"),
835
+ reason: reason as Extract<
836
+ EditableArtifactLiveServerFrame,
837
+ { type: "resyncRequired" }
838
+ >["reason"],
839
+ headSequence: nonnegativeInteger(metadata.headSequence, "headSequence"),
840
+ });
841
+ }
842
+ case SERVER_AUTHORIZATION_CHANGED: {
843
+ requireNoPayload(decoded.payload);
844
+ requireServerKeys(metadata, "authorizationChanged", ["writable"]);
845
+ return Object.freeze({
846
+ ...serverIdentity(metadata, "authorizationChanged"),
847
+ writable: requiredBoolean(metadata.writable, "writable"),
848
+ });
849
+ }
850
+ case SERVER_MUTATION_ACCEPTED: {
851
+ requireNoPayload(decoded.payload);
852
+ requireServerKeys(metadata, "mutationAccepted", [
853
+ "requestHash",
854
+ "clientTransactionId",
855
+ "transactionId",
856
+ "startSequence",
857
+ "endSequence",
858
+ "stateHash",
859
+ ]);
860
+ const startSequence = positiveInteger(metadata.startSequence, "startSequence");
861
+ const endSequence = positiveInteger(metadata.endSequence, "endSequence");
862
+ if (endSequence < startSequence) throw invalidFrame("accepted sequence interval is inverted");
863
+ return Object.freeze({
864
+ ...serverIdentity(metadata, "mutationAccepted"),
865
+ requestHash: editableArtifactRequestHash(
866
+ requiredString(metadata.requestHash, "requestHash"),
867
+ ),
868
+ clientTransactionId: editableArtifactClientTransactionId(
869
+ requiredString(metadata.clientTransactionId, "clientTransactionId"),
870
+ ),
871
+ transactionId: editableArtifactTransactionId(
872
+ requiredString(metadata.transactionId, "transactionId"),
873
+ ),
874
+ startSequence,
875
+ endSequence,
876
+ stateHash: editableArtifactStateHash(requiredString(metadata.stateHash, "stateHash")),
877
+ });
878
+ }
879
+ case SERVER_MUTATION_REJECTED: {
880
+ requireNoPayload(decoded.payload);
881
+ requireServerKeys(metadata, "mutationRejected", ["requestHash", "code", "retryable"]);
882
+ const code = requiredString(metadata.code, "code");
883
+ if (!MUTATION_REJECTION_CODES.has(code)) {
884
+ throw invalidFrame("mutation rejection code is invalid");
885
+ }
886
+ return Object.freeze({
887
+ ...serverIdentity(metadata, "mutationRejected"),
888
+ requestHash: editableArtifactRequestHash(
889
+ requiredString(metadata.requestHash, "requestHash"),
890
+ ),
891
+ code: code as Extract<
892
+ EditableArtifactLiveServerFrame,
893
+ { type: "mutationRejected" }
894
+ >["code"],
895
+ retryable: requiredBoolean(metadata.retryable, "retryable"),
896
+ });
897
+ }
898
+ default:
899
+ throw invalidFrame("Unknown editable artifact server frame kind");
900
+ }
901
+ }
902
+
903
+ /** Header-level inspection for transport tests and SDK adapters. */
904
+ export function inspectEditableArtifactLiveWireEnvelope(bytes: Uint8Array): Readonly<{
905
+ kind: number;
906
+ metadata: Readonly<Record<string, unknown>>;
907
+ payload: Uint8Array;
908
+ }> {
909
+ return decodeEnvelope(bytes);
910
+ }
911
+
912
+ function decodeOpen(
913
+ metadata: Readonly<Record<string, unknown>>,
914
+ payload: Uint8Array,
915
+ ): EditableArtifactLiveOpenWireFrame {
916
+ requireNoPayload(payload);
917
+ const modality = clientOpenModality(metadata);
918
+ requireExactKeys(
919
+ metadata,
920
+ modality === "spreadsheet"
921
+ ? [
922
+ "type",
923
+ "protocolVersion",
924
+ "artifactId",
925
+ "token",
926
+ "localCursor",
927
+ "localStateHash",
928
+ "localCausalFrontier",
929
+ "requireSnapshot",
930
+ ]
931
+ : [
932
+ "type",
933
+ "protocolVersion",
934
+ "artifactId",
935
+ "token",
936
+ "modality",
937
+ "localCursor",
938
+ "localStateHash",
939
+ "localNativeRevision",
940
+ "requireSnapshot",
941
+ ],
942
+ );
943
+ requireLiteral(metadata.type, "open", "type");
944
+ return Object.freeze({
945
+ type: "open",
946
+ protocolVersion: positiveInteger(metadata.protocolVersion, "protocolVersion"),
947
+ artifactId: editableArtifactId(requiredString(metadata.artifactId, "artifactId")),
948
+ token: boundedToken(requiredString(metadata.token, "token")),
949
+ resume: normalizeResume(
950
+ modality === "spreadsheet"
951
+ ? {
952
+ modality,
953
+ localCursor: nullableNonnegativeInteger(metadata.localCursor, "localCursor"),
954
+ localStateHash:
955
+ metadata.localStateHash === null
956
+ ? null
957
+ : editableArtifactStateHash(
958
+ requiredString(metadata.localStateHash, "localStateHash"),
959
+ ),
960
+ localCausalFrontier: requiredFrontier(metadata.localCausalFrontier),
961
+ requireSnapshot: requiredBoolean(metadata.requireSnapshot, "requireSnapshot"),
962
+ }
963
+ : {
964
+ modality,
965
+ localCursor: nullableNonnegativeInteger(metadata.localCursor, "localCursor"),
966
+ localStateHash:
967
+ metadata.localStateHash === null
968
+ ? null
969
+ : editableArtifactStateHash(
970
+ requiredString(metadata.localStateHash, "localStateHash"),
971
+ ),
972
+ localNativeRevision: nullableNonnegativeInteger(
973
+ metadata.localNativeRevision,
974
+ "localNativeRevision",
975
+ ),
976
+ requireSnapshot: requiredBoolean(metadata.requireSnapshot, "requireSnapshot"),
977
+ },
978
+ ),
979
+ });
980
+ }
981
+
982
+ function decodeApplied(
983
+ metadata: Readonly<Record<string, unknown>>,
984
+ payload: Uint8Array,
985
+ ): EditableArtifactLiveAppliedWireFrame {
986
+ requireNoPayload(payload);
987
+ requireExactKeys(metadata, [
988
+ "type",
989
+ "protocolVersion",
990
+ "artifactId",
991
+ "streamEpoch",
992
+ "sequence",
993
+ "stateHash",
994
+ ]);
995
+ requireLiteral(metadata.type, "applied", "type");
996
+ return Object.freeze({
997
+ type: "applied",
998
+ protocolVersion: positiveInteger(metadata.protocolVersion, "protocolVersion"),
999
+ artifactId: editableArtifactId(requiredString(metadata.artifactId, "artifactId")),
1000
+ streamEpoch: boundedString(
1001
+ requiredString(metadata.streamEpoch, "streamEpoch"),
1002
+ "streamEpoch",
1003
+ MAX_IDENTIFIER_BYTES,
1004
+ ),
1005
+ sequence: nonnegativeInteger(metadata.sequence, "sequence"),
1006
+ stateHash: editableArtifactStateHash(requiredString(metadata.stateHash, "stateHash")),
1007
+ });
1008
+ }
1009
+
1010
+ function decodeMutation(
1011
+ metadata: Readonly<Record<string, unknown>>,
1012
+ payload: Uint8Array,
1013
+ ): EditableArtifactLiveMutationWireFrame {
1014
+ requireExactKeys(metadata, [
1015
+ "type",
1016
+ "protocolVersion",
1017
+ "artifactId",
1018
+ "streamEpoch",
1019
+ "requestHash",
1020
+ ]);
1021
+ requireLiteral(metadata.type, "mutation", "type");
1022
+ const requestHash = editableArtifactRequestHash(
1023
+ requiredString(metadata.requestHash, "requestHash"),
1024
+ );
1025
+ const intentBytes = ownPayload(payload, EDITABLE_ARTIFACT_INTENT_MAX_BYTES, false);
1026
+ try {
1027
+ if (hashEditableArtifactMutationIntentBytes(intentBytes) !== requestHash) {
1028
+ throw new TypeError("request hash mismatch");
1029
+ }
1030
+ } catch (cause) {
1031
+ throw invalidFrame("Mutation request hash does not match exact OGATX bytes", cause);
1032
+ }
1033
+ return Object.freeze({
1034
+ type: "mutation",
1035
+ protocolVersion: positiveInteger(metadata.protocolVersion, "protocolVersion"),
1036
+ artifactId: editableArtifactId(requiredString(metadata.artifactId, "artifactId")),
1037
+ streamEpoch: boundedString(
1038
+ requiredString(metadata.streamEpoch, "streamEpoch"),
1039
+ "streamEpoch",
1040
+ MAX_IDENTIFIER_BYTES,
1041
+ ),
1042
+ requestHash,
1043
+ intentBytes,
1044
+ });
1045
+ }
1046
+
1047
+ function encodeEnvelope(
1048
+ kind: number,
1049
+ metadata: Readonly<Record<string, unknown>>,
1050
+ rawPayload: Uint8Array,
1051
+ ): Uint8Array {
1052
+ if (!Number.isInteger(kind) || kind < 1 || kind > 255) throw new TypeError("wire kind invalid");
1053
+ const metadataBytes = textEncoder.encode(JSON.stringify(metadata));
1054
+ if (metadataBytes.byteLength < 2 || metadataBytes.byteLength > MAX_METADATA_BYTES) {
1055
+ throw new RangeError("Editable artifact wire metadata exceeds bound");
1056
+ }
1057
+ const payload = ownPayload(rawPayload, 128 * 1024 * 1024, true);
1058
+ const total = HEADER_BYTES + metadataBytes.byteLength + payload.byteLength;
1059
+ if (!Number.isSafeInteger(total)) throw new RangeError("Editable artifact wire frame too large");
1060
+ const output = new Uint8Array(total);
1061
+ output.set(MAGIC, 0);
1062
+ const view = new DataView(output.buffer);
1063
+ view.setUint8(8, EDITABLE_ARTIFACT_LIVE_WIRE_VERSION);
1064
+ view.setUint8(9, kind);
1065
+ view.setUint16(10, 0, true);
1066
+ view.setUint32(12, metadataBytes.byteLength, true);
1067
+ view.setUint32(16, payload.byteLength, true);
1068
+ output.set(metadataBytes, HEADER_BYTES);
1069
+ output.set(payload, HEADER_BYTES + metadataBytes.byteLength);
1070
+ return output;
1071
+ }
1072
+
1073
+ function decodeEnvelope(bytes: Uint8Array): Readonly<{
1074
+ kind: number;
1075
+ metadata: Readonly<Record<string, unknown>>;
1076
+ payload: Uint8Array;
1077
+ }> {
1078
+ if (!(bytes instanceof Uint8Array) || bytes.byteLength < HEADER_BYTES) {
1079
+ throw invalidFrame("Editable artifact wire frame is truncated");
1080
+ }
1081
+ for (let index = 0; index < MAGIC.byteLength; index += 1) {
1082
+ if (bytes[index] !== MAGIC[index]) throw invalidFrame("Editable artifact wire magic mismatch");
1083
+ }
1084
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1085
+ if (view.getUint8(8) !== EDITABLE_ARTIFACT_LIVE_WIRE_VERSION) {
1086
+ throw new EditableArtifactLiveError("protocol_mismatch", "Live wire version unsupported");
1087
+ }
1088
+ const kind = view.getUint8(9);
1089
+ if (view.getUint16(10, true) !== 0) throw invalidFrame("Editable artifact wire flags invalid");
1090
+ const metadataLength = view.getUint32(12, true);
1091
+ const payloadLength = view.getUint32(16, true);
1092
+ if (metadataLength < 2 || metadataLength > MAX_METADATA_BYTES) {
1093
+ throw invalidFrame("Editable artifact wire metadata exceeds bound");
1094
+ }
1095
+ const expected = HEADER_BYTES + metadataLength + payloadLength;
1096
+ if (expected !== bytes.byteLength) throw invalidFrame("Editable artifact wire length mismatch");
1097
+ const metadataBytes = bytes.subarray(HEADER_BYTES, HEADER_BYTES + metadataLength);
1098
+ let metadataText: string;
1099
+ let value: unknown;
1100
+ try {
1101
+ metadataText = strictDecoder.decode(metadataBytes);
1102
+ value = JSON.parse(metadataText);
1103
+ } catch (cause) {
1104
+ throw invalidFrame("Editable artifact wire metadata is not UTF-8 JSON", cause);
1105
+ }
1106
+ const metadata = requiredRecord(value, "metadata");
1107
+ // Canonical JSON rejects duplicate keys, whitespace variants, and unstable
1108
+ // property order before any token or identity is acted on.
1109
+ if (JSON.stringify(metadata) !== metadataText) {
1110
+ throw invalidFrame("Editable artifact wire metadata is not canonical JSON");
1111
+ }
1112
+ return Object.freeze({
1113
+ kind,
1114
+ metadata: Object.freeze(metadata),
1115
+ payload: bytes.slice(HEADER_BYTES + metadataLength),
1116
+ });
1117
+ }
1118
+
1119
+ function normalizeResume(resume: EditableArtifactLiveResume): EditableArtifactLiveResume {
1120
+ if (!resume || typeof resume !== "object") throw invalidFrame("resume must be an object");
1121
+ const localCursor = nullableNonnegativeInteger(resume.localCursor, "localCursor");
1122
+ const localStateHash =
1123
+ resume.localStateHash === null ? null : editableArtifactStateHash(resume.localStateHash);
1124
+ const requireSnapshot = requiredBoolean(resume.requireSnapshot, "requireSnapshot");
1125
+ if ((localCursor === null) !== (localStateHash === null)) {
1126
+ throw invalidFrame("resume cursor and state hash must both be null or both be present");
1127
+ }
1128
+ const modality = resume.modality ?? "spreadsheet";
1129
+ if (modality === "spreadsheet") {
1130
+ if (!("localCausalFrontier" in resume)) {
1131
+ throw invalidFrame("spreadsheet resume requires a causal frontier");
1132
+ }
1133
+ return Object.freeze({
1134
+ localCursor,
1135
+ localStateHash,
1136
+ localCausalFrontier: requiredFrontier(resume.localCausalFrontier),
1137
+ requireSnapshot,
1138
+ });
1139
+ }
1140
+ editableArtifactLiveModality(modality);
1141
+ if (!("localNativeRevision" in resume)) {
1142
+ throw invalidFrame("serialized resume requires a native revision");
1143
+ }
1144
+ const localNativeRevision = nullableNonnegativeInteger(
1145
+ resume.localNativeRevision,
1146
+ "localNativeRevision",
1147
+ );
1148
+ if ((localCursor === null) !== (localNativeRevision === null)) {
1149
+ throw invalidFrame(
1150
+ "serialized resume cursor and native revision must both be null or both be present",
1151
+ );
1152
+ }
1153
+ return Object.freeze({
1154
+ modality,
1155
+ localCursor,
1156
+ localStateHash,
1157
+ localNativeRevision,
1158
+ requireSnapshot,
1159
+ });
1160
+ }
1161
+
1162
+ function requiredFrontier(value: unknown): EditableArtifactCausalFrontier {
1163
+ if (!Array.isArray(value) || value.length > MAX_CAUSAL_ACTORS) {
1164
+ throw invalidFrame("causal frontier exceeds bound");
1165
+ }
1166
+ return editableArtifactCausalFrontier(value as EditableArtifactCausalFrontier);
1167
+ }
1168
+
1169
+ function editableArtifactLiveModality(value: unknown): EditableArtifactLiveModality {
1170
+ if (value !== "document" && value !== "spreadsheet" && value !== "presentation") {
1171
+ throw invalidFrame("editable artifact modality is invalid");
1172
+ }
1173
+ return value;
1174
+ }
1175
+
1176
+ function clientOpenModality(
1177
+ metadata: Readonly<Record<string, unknown>>,
1178
+ ): EditableArtifactLiveModality {
1179
+ return metadata.modality === undefined
1180
+ ? "spreadsheet"
1181
+ : editableArtifactLiveModality(metadata.modality);
1182
+ }
1183
+
1184
+ function serverModality(metadata: Readonly<Record<string, unknown>>): EditableArtifactLiveModality {
1185
+ return metadata.modality === undefined
1186
+ ? "spreadsheet"
1187
+ : editableArtifactLiveModality(metadata.modality);
1188
+ }
1189
+
1190
+ function transactionModality(
1191
+ transaction: Readonly<Record<string, unknown>>,
1192
+ ): EditableArtifactLiveModality {
1193
+ return transaction.modality === undefined
1194
+ ? "spreadsheet"
1195
+ : editableArtifactLiveModality(transaction.modality);
1196
+ }
1197
+
1198
+ function requireExactKeys(
1199
+ record: Readonly<Record<string, unknown>>,
1200
+ expected: readonly string[],
1201
+ ): void {
1202
+ const keys = Object.keys(record);
1203
+ if (keys.length !== expected.length || keys.some((key, index) => key !== expected[index])) {
1204
+ throw invalidFrame("Editable artifact wire metadata fields are invalid or out of order");
1205
+ }
1206
+ }
1207
+
1208
+ function requireServerKeys(
1209
+ metadata: Readonly<Record<string, unknown>>,
1210
+ type: string,
1211
+ trailing: readonly string[],
1212
+ ): void {
1213
+ requireExactKeys(metadata, ["type", "protocolVersion", "artifactId", "streamEpoch", ...trailing]);
1214
+ requireLiteral(metadata.type, type, "type");
1215
+ }
1216
+
1217
+ function serverIdentity<Type extends EditableArtifactLiveServerFrame["type"]>(
1218
+ metadata: Readonly<Record<string, unknown>>,
1219
+ type: Type,
1220
+ ): Readonly<{
1221
+ type: Type;
1222
+ protocolVersion: number;
1223
+ artifactId: EditableArtifactId;
1224
+ streamEpoch: string;
1225
+ }> {
1226
+ return Object.freeze({
1227
+ type,
1228
+ protocolVersion: positiveInteger(metadata.protocolVersion, "protocolVersion"),
1229
+ artifactId: editableArtifactId(requiredString(metadata.artifactId, "artifactId")),
1230
+ streamEpoch: boundedString(
1231
+ requiredString(metadata.streamEpoch, "streamEpoch"),
1232
+ "streamEpoch",
1233
+ MAX_IDENTIFIER_BYTES,
1234
+ ),
1235
+ });
1236
+ }
1237
+
1238
+ function requiredRecord(value: unknown, label: string): Record<string, unknown> {
1239
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1240
+ throw invalidFrame(`${label} must be an object`);
1241
+ }
1242
+ const prototype = Object.getPrototypeOf(value);
1243
+ if (prototype !== Object.prototype && prototype !== null) {
1244
+ throw invalidFrame(`${label} must be a plain object`);
1245
+ }
1246
+ return value as Record<string, unknown>;
1247
+ }
1248
+
1249
+ function requiredString(value: unknown, label: string): string {
1250
+ if (typeof value !== "string") throw invalidFrame(`${label} must be a string`);
1251
+ return value;
1252
+ }
1253
+
1254
+ function requiredBoolean(value: unknown, label: string): boolean {
1255
+ if (typeof value !== "boolean") throw invalidFrame(`${label} must be boolean`);
1256
+ return value;
1257
+ }
1258
+
1259
+ function requireLiteral(value: unknown, expected: string, label: string): void {
1260
+ if (value !== expected) throw invalidFrame(`${label} must equal ${expected}`);
1261
+ }
1262
+
1263
+ function positiveInteger(value: unknown, label: string): number {
1264
+ if (!Number.isSafeInteger(value) || (value as number) < 1) {
1265
+ throw invalidFrame(`${label} must be a positive safe integer`);
1266
+ }
1267
+ return value as number;
1268
+ }
1269
+
1270
+ function nonnegativeInteger(value: unknown, label: string): number {
1271
+ if (!Number.isSafeInteger(value) || (value as number) < 0) {
1272
+ throw invalidFrame(`${label} must be a nonnegative safe integer`);
1273
+ }
1274
+ return value as number;
1275
+ }
1276
+
1277
+ function nullableNonnegativeInteger(value: unknown, label: string): number | null {
1278
+ return value === null ? null : nonnegativeInteger(value, label);
1279
+ }
1280
+
1281
+ function boundedString(value: string, label: string, maxBytes: number): string {
1282
+ const bytes = textEncoder.encode(value).byteLength;
1283
+ if (bytes < 1 || bytes > maxBytes || value.trim() !== value) {
1284
+ throw invalidFrame(`${label} is malformed`);
1285
+ }
1286
+ return value;
1287
+ }
1288
+
1289
+ function boundedToken(value: string): string {
1290
+ const token = boundedString(value, "token", MAX_TOKEN_BYTES);
1291
+ if (!/^[A-Za-z0-9._~-]+$/u.test(token)) throw invalidFrame("token is malformed");
1292
+ return token;
1293
+ }
1294
+
1295
+ function editableArtifactId(value: string): EditableArtifactId {
1296
+ return stableId(value, "artifact id");
1297
+ }
1298
+
1299
+ function editableArtifactTransactionId(value: string): EditableArtifactTransactionId {
1300
+ return stableId(value, "server transaction id");
1301
+ }
1302
+
1303
+ function editableArtifactClientTransactionId(value: string): EditableArtifactClientTransactionId {
1304
+ if (!/^[A-Za-z0-9._:-]{1,200}$/u.test(value)) {
1305
+ throw invalidFrame("clientTransactionId is malformed");
1306
+ }
1307
+ return value;
1308
+ }
1309
+
1310
+ function editableArtifactRequestHash(value: string): EditableArtifactRequestHash {
1311
+ return sha256Text(value, "request hash");
1312
+ }
1313
+
1314
+ function editableArtifactStateHash(value: string): EditableArtifactStateHash {
1315
+ return sha256Text(value, "state hash");
1316
+ }
1317
+
1318
+ function editableArtifactContentHash(value: string): EditableArtifactContentHash {
1319
+ return sha256Text(value, "content hash");
1320
+ }
1321
+
1322
+ function stableId(value: string, label: string): string {
1323
+ if (!/^[0-9a-f]{32}$/u.test(value) || /^0{32}$/u.test(value)) {
1324
+ throw invalidFrame(`${label} is malformed`);
1325
+ }
1326
+ return value;
1327
+ }
1328
+
1329
+ function sha256Text(value: string, label: string): string {
1330
+ if (!/^sha256:[0-9a-f]{64}$/u.test(value)) {
1331
+ throw invalidFrame(`${label} is malformed`);
1332
+ }
1333
+ return value;
1334
+ }
1335
+
1336
+ function editableArtifactCausalFrontier(
1337
+ entries: EditableArtifactCausalFrontier,
1338
+ ): EditableArtifactCausalFrontier {
1339
+ const seen = new Set<string>();
1340
+ const normalized = entries.map((entry, index) => {
1341
+ const record = requiredRecord(entry, `causal frontier entry ${index}`);
1342
+ requireExactKeys(record, ["replicaId", "counter"]);
1343
+ const replicaId = requiredString(record.replicaId, "causal replicaId");
1344
+ if (!/^[0-9a-f]{16}$/u.test(replicaId) || /^0{16}$/u.test(replicaId)) {
1345
+ throw invalidFrame("causal replicaId is malformed");
1346
+ }
1347
+ const counter = positiveInteger(record.counter, "causal counter");
1348
+ if (seen.has(replicaId)) throw invalidFrame("duplicate causal replicaId");
1349
+ seen.add(replicaId);
1350
+ return Object.freeze({ replicaId, counter });
1351
+ });
1352
+ normalized.sort((left, right) =>
1353
+ left.replicaId < right.replicaId ? -1 : left.replicaId > right.replicaId ? 1 : 0,
1354
+ );
1355
+ return Object.freeze(normalized);
1356
+ }
1357
+
1358
+ function ownPayload(value: Uint8Array, maxBytes: number, allowEmpty: boolean): Uint8Array {
1359
+ if (
1360
+ !(value instanceof Uint8Array) ||
1361
+ (!allowEmpty && value.byteLength === 0) ||
1362
+ value.byteLength > maxBytes
1363
+ ) {
1364
+ throw invalidFrame("Editable artifact wire payload exceeds bound");
1365
+ }
1366
+ return value.slice();
1367
+ }
1368
+
1369
+ function requireNoPayload(payload: Uint8Array): void {
1370
+ if (payload.byteLength !== 0) throw invalidFrame("Frame kind must not carry a binary payload");
1371
+ }
1372
+
1373
+ function invalidFrame(message: string, cause?: unknown): EditableArtifactLiveError {
1374
+ return new EditableArtifactLiveError("invalid_frame", message, {
1375
+ ...(cause ? { cause } : {}),
1376
+ });
1377
+ }
1378
+
1379
+ const EMPTY_BYTES = new Uint8Array(0);