@opengeni/sdk 1.0.1 → 2.3.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +117 -2
  2. package/dist/artifacts.js +5 -5
  3. package/dist/automations-client.d.ts +17 -0
  4. package/dist/automations.d.ts +3 -0
  5. package/dist/automations.js +78 -0
  6. package/dist/automations.js.map +1 -0
  7. package/dist/{chunk-YGH5P47G.js → chunk-6IPZY27S.js} +744 -57
  8. package/dist/chunk-6IPZY27S.js.map +1 -0
  9. package/dist/{chunk-TYZ4JL4J.js → chunk-AGUKZAXR.js} +2 -2
  10. package/dist/{chunk-A5DC5WEM.js → chunk-C2H7HBNP.js} +22 -2
  11. package/dist/chunk-C2H7HBNP.js.map +1 -0
  12. package/dist/{chunk-GU6JF75T.js → chunk-C4DJPZRU.js} +1 -1
  13. package/dist/{chunk-GU6JF75T.js.map → chunk-C4DJPZRU.js.map} +1 -1
  14. package/dist/{chunk-ST5DDKJP.js → chunk-GW3EJ2GP.js} +52 -1
  15. package/dist/chunk-GW3EJ2GP.js.map +1 -0
  16. package/dist/{chunk-4DV37UUA.js → chunk-TW5MNHUX.js} +16 -2
  17. package/dist/chunk-TW5MNHUX.js.map +1 -0
  18. package/dist/{chunk-VWE2QIVO.js → chunk-XNG7YGR3.js} +23 -6
  19. package/dist/chunk-XNG7YGR3.js.map +1 -0
  20. package/dist/client.d.ts +159 -26
  21. package/dist/codex-realtime-controller.d.ts +1 -0
  22. package/dist/codex-realtime-controller.js +2 -2
  23. package/dist/company-brain.d.ts +32 -0
  24. package/dist/company-profile.d.ts +1 -1
  25. package/dist/core.js +4 -4
  26. package/dist/editable-artifacts/browser-session.d.ts +7 -3
  27. package/dist/editable-artifacts/controller.d.ts +4 -1
  28. package/dist/editable-artifacts/errors.d.ts +1 -1
  29. package/dist/editable-artifacts/http-live-transport.d.ts +6 -2
  30. package/dist/editable-artifacts/index.d.ts +2 -0
  31. package/dist/editable-artifacts/open-failure.d.ts +9 -0
  32. package/dist/editable-artifacts/types.d.ts +4 -3
  33. package/dist/editable-artifacts/worker/kernel-adapter.d.ts +1 -1
  34. package/dist/editable-artifacts-worker.js +44 -17
  35. package/dist/editable-artifacts-worker.js.map +1 -1
  36. package/dist/editable-artifacts.js +222 -119
  37. package/dist/editable-artifacts.js.map +1 -1
  38. package/dist/errors.d.ts +1 -1
  39. package/dist/index.d.ts +7 -4
  40. package/dist/index.js +61 -12
  41. package/dist/index.js.map +1 -1
  42. package/dist/interaction.d.ts +23 -0
  43. package/dist/interaction.js +4 -1
  44. package/dist/organization-private-session-settings.d.ts +6 -0
  45. package/dist/organization-private-session-settings.js +22 -0
  46. package/dist/organization-private-session-settings.js.map +1 -0
  47. package/dist/pr-review-client.d.ts +15 -0
  48. package/dist/pr-review.d.ts +4 -0
  49. package/dist/pr-review.js +56 -0
  50. package/dist/pr-review.js.map +1 -0
  51. package/dist/preference-registry.d.ts +8 -0
  52. package/dist/realtime.js +2 -2
  53. package/dist/retained-artifacts.d.ts +5 -1
  54. package/dist/types.d.ts +1023 -13
  55. package/dist/workspace-instruction-policies.d.ts +22 -0
  56. package/dist/workspace-learning.d.ts +124 -0
  57. package/package.json +14 -2
  58. package/src/automations-client.ts +118 -0
  59. package/src/automations.ts +15 -0
  60. package/src/client.ts +1049 -71
  61. package/src/codex-realtime-controller.ts +21 -0
  62. package/src/company-brain.ts +99 -0
  63. package/src/company-profile.ts +4 -1
  64. package/src/editable-artifacts/browser-session.ts +36 -22
  65. package/src/editable-artifacts/controller.ts +66 -45
  66. package/src/editable-artifacts/errors.ts +2 -0
  67. package/src/editable-artifacts/http-live-transport.ts +62 -37
  68. package/src/editable-artifacts/index.ts +6 -0
  69. package/src/editable-artifacts/open-failure.ts +79 -0
  70. package/src/editable-artifacts/storage.ts +31 -40
  71. package/src/editable-artifacts/types.ts +4 -3
  72. package/src/editable-artifacts/worker/kernel-adapter.ts +23 -7
  73. package/src/editable-artifacts/worker/runtime.ts +26 -13
  74. package/src/editable-artifacts/worker/wire-codec.ts +23 -5
  75. package/src/errors.ts +1 -1
  76. package/src/index.ts +134 -0
  77. package/src/interaction.ts +95 -0
  78. package/src/organization-private-session-settings.ts +33 -0
  79. package/src/pr-review-client.ts +84 -0
  80. package/src/pr-review.ts +15 -0
  81. package/src/preference-registry.ts +9 -0
  82. package/src/retained-artifacts.ts +101 -0
  83. package/src/stream.ts +13 -2
  84. package/src/types.ts +1174 -12
  85. package/src/workspace-instruction-policies.ts +24 -0
  86. package/src/workspace-learning.ts +138 -0
  87. package/dist/chunk-4DV37UUA.js.map +0 -1
  88. package/dist/chunk-A5DC5WEM.js.map +0 -1
  89. package/dist/chunk-ST5DDKJP.js.map +0 -1
  90. package/dist/chunk-VWE2QIVO.js.map +0 -1
  91. package/dist/chunk-YGH5P47G.js.map +0 -1
  92. /package/dist/{chunk-TYZ4JL4J.js.map → chunk-AGUKZAXR.js.map} +0 -0
@@ -11,6 +11,7 @@ import {
11
11
  EDITABLE_ARTIFACT_COMMAND_MAX_BYTES,
12
12
  EDITABLE_ARTIFACT_INTENT_MAX_BYTES,
13
13
  EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES,
14
+ currentEditableArtifactCompatibility,
14
15
  hashEditableArtifactMutationIntentBytes,
15
16
  } from "@opengeni/contracts/editable-artifacts";
16
17
  import { MAX_COMMITTED_TRANSACTION_BYTES } from "@opengeni/contracts/editable-artifact-committed-transaction";
@@ -34,7 +35,7 @@ import type {
34
35
  } from "./types";
35
36
 
36
37
  const LIVE_PATH = "/v1/editable-artifacts/live";
37
- const LIVE_SUBPROTOCOL = "opengeni-artifact-v1";
38
+ const LIVE_SUBPROTOCOL = "opengeni-artifact-v2";
38
39
  const DEFAULT_MAX_SERVER_FRAME_BYTES = 8 * 1024 * 1024 + 64 * 1024;
39
40
  const DEFAULT_MAX_SNAPSHOT_BYTES = EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES;
40
41
  const DEFAULT_HANDSHAKE_TIMEOUT_MS = 20_000;
@@ -81,9 +82,13 @@ export type EditableArtifactWebSocketLike = {
81
82
  export type CreateEditableArtifactHttpLiveTransportOptions = Readonly<{
82
83
  baseUrl: string | URL;
83
84
  workspaceId: string;
84
- protocolVersion: number;
85
+ modality: EditableArtifactModality;
86
+ liveProtocolVersion: number;
85
87
  kernelVersion: string;
86
88
  modelSchemaVersion: number;
89
+ snapshotVersion: number;
90
+ commandProtocolVersion: number;
91
+ committedTransactionProtocolVersion: number;
87
92
  apiKey?: string;
88
93
  headers?: Readonly<Record<string, string>> | (() => Readonly<Record<string, string>>);
89
94
  credentials?: RequestCredentials;
@@ -111,9 +116,13 @@ class HttpLiveTransport implements EditableArtifactSyncTransport {
111
116
  private readonly baseUrl: URL;
112
117
  private readonly socketUrl: URL;
113
118
  private readonly workspaceId: string;
114
- private readonly protocolVersion: number;
119
+ private readonly modality: EditableArtifactModality;
120
+ private readonly liveProtocolVersion: number;
115
121
  private readonly kernelVersion: string;
116
122
  private readonly modelSchemaVersion: number;
123
+ private readonly snapshotVersion: number;
124
+ private readonly commandProtocolVersion: number;
125
+ private readonly committedTransactionProtocolVersion: number;
117
126
  private readonly apiKey: string | undefined;
118
127
  private readonly headerSource: CreateEditableArtifactHttpLiveTransportOptions["headers"];
119
128
  private readonly credentials: RequestCredentials;
@@ -132,9 +141,34 @@ class HttpLiveTransport implements EditableArtifactSyncTransport {
132
141
  const insecure = options.allowInsecureDevelopmentTransport ?? false;
133
142
  requireTransportSecurity(this.baseUrl, this.socketUrl, insecure);
134
143
  this.workspaceId = boundedString(options.workspaceId, "workspaceId", 256);
135
- this.protocolVersion = positiveU16(options.protocolVersion, "protocolVersion");
144
+ const current = currentEditableArtifactCompatibility(options.modality);
145
+ this.modality = options.modality;
146
+ this.liveProtocolVersion = exactVersion(
147
+ options.liveProtocolVersion,
148
+ current.liveProtocolVersion,
149
+ "liveProtocolVersion",
150
+ );
136
151
  this.kernelVersion = boundedString(options.kernelVersion, "kernelVersion", 512);
137
- this.modelSchemaVersion = positiveU16(options.modelSchemaVersion, "modelSchemaVersion");
152
+ this.modelSchemaVersion = exactVersion(
153
+ options.modelSchemaVersion,
154
+ current.modelSchemaVersion,
155
+ "modelSchemaVersion",
156
+ );
157
+ this.snapshotVersion = exactVersion(
158
+ options.snapshotVersion,
159
+ current.snapshotVersion,
160
+ "snapshotVersion",
161
+ );
162
+ this.commandProtocolVersion = exactVersion(
163
+ options.commandProtocolVersion,
164
+ current.commandProtocolVersion,
165
+ "commandProtocolVersion",
166
+ );
167
+ this.committedTransactionProtocolVersion = exactVersion(
168
+ options.committedTransactionProtocolVersion,
169
+ current.committedTransactionProtocolVersion,
170
+ "committedTransactionProtocolVersion",
171
+ );
138
172
  this.apiKey = options.apiKey;
139
173
  if (this.apiKey !== undefined) boundedString(this.apiKey, "apiKey", 16 * 1024);
140
174
  this.headerSource = options.headers;
@@ -187,9 +221,13 @@ class HttpLiveTransport implements EditableArtifactSyncTransport {
187
221
  },
188
222
  body: JSON.stringify({
189
223
  replicaId: input.replicaId,
190
- protocolVersion: this.protocolVersion,
224
+ modality: this.modality,
225
+ liveProtocolVersion: this.liveProtocolVersion,
191
226
  kernelVersion: this.kernelVersion,
192
227
  modelSchemaVersion: this.modelSchemaVersion,
228
+ snapshotVersion: this.snapshotVersion,
229
+ commandProtocolVersion: this.commandProtocolVersion,
230
+ committedTransactionProtocolVersion: this.committedTransactionProtocolVersion,
193
231
  }),
194
232
  signal: input.signal,
195
233
  });
@@ -211,7 +249,7 @@ class HttpLiveTransport implements EditableArtifactSyncTransport {
211
249
  );
212
250
  }
213
251
  const value = await readBoundedJson(response, MAX_TICKET_RESPONSE_BYTES);
214
- return validateTicket(value, input.artifactId, input.replicaId, this.protocolVersion);
252
+ return validateTicket(value, input.artifactId, input.replicaId, this.liveProtocolVersion);
215
253
  }
216
254
 
217
255
  async openLive(input: {
@@ -246,6 +284,7 @@ class HttpLiveTransport implements EditableArtifactSyncTransport {
246
284
  input.resume.modality === "spreadsheet"
247
285
  ? {
248
286
  ...commonResume,
287
+ modality: "spreadsheet",
249
288
  localCausalFrontier: cloneFrontier(input.resume.causalFrontier),
250
289
  }
251
290
  : {
@@ -458,7 +497,7 @@ class HttpLiveConnection implements EditableArtifactLiveConnection {
458
497
  }
459
498
  const commonBootstrap = {
460
499
  artifactId: this.ticket.artifactId,
461
- protocolVersion: this.ticket.protocolVersion,
500
+ liveProtocolVersion: this.ticket.protocolVersion,
462
501
  headSequence: boundarySequence,
463
502
  headStateHash: boundaryStateHash,
464
503
  kernelVersion: snapshot?.kernelVersion ?? this.kernelVersion,
@@ -549,12 +588,6 @@ class HttpLiveConnection implements EditableArtifactLiveConnection {
549
588
  if (pending.artifactId !== this.ticket.artifactId) {
550
589
  throw new TypeError("pending transaction belongs to another artifact");
551
590
  }
552
- if (pending.protocolVersion !== this.ticket.protocolVersion) {
553
- throw new EditableArtifactTransportError("pending protocol does not match live stream", {
554
- code: "unsupported",
555
- retryable: false,
556
- });
557
- }
558
591
  if (
559
592
  pending.commandBytes.byteLength >
560
593
  Math.min(open.maxCommandBytes, EDITABLE_ARTIFACT_COMMAND_MAX_BYTES)
@@ -581,7 +614,7 @@ class HttpLiveConnection implements EditableArtifactLiveConnection {
581
614
  }
582
615
  const frame = encodeEditableArtifactLiveMutationWireFrame({
583
616
  type: "mutation",
584
- protocolVersion: pending.protocolVersion,
617
+ protocolVersion: this.ticket.protocolVersion,
585
618
  artifactId: pending.artifactId,
586
619
  streamEpoch: open.streamEpoch,
587
620
  requestHash: pending.requestHash,
@@ -756,15 +789,9 @@ class HttpLiveConnection implements EditableArtifactLiveConnection {
756
789
  if (frame.transaction.artifactId !== this.ticket.artifactId) {
757
790
  throw new TypeError("live transaction belongs to another artifact");
758
791
  }
759
- if (transactionModality(frame.transaction) !== this.ticket.modality) {
792
+ if (frame.transaction.modality !== this.ticket.modality) {
760
793
  throw new TypeError("live transaction modality mismatch");
761
794
  }
762
- if (
763
- (frame.transaction.modality === undefined ||
764
- frame.transaction.modality === "spreadsheet") &&
765
- frame.transaction.protocolVersion !== this.ticket.protocolVersion
766
- )
767
- throw new TypeError("live transaction protocol mismatch");
768
795
  if (
769
796
  frame.transaction.committedTransactionBytes.byteLength >
770
797
  this.limits.maxCommittedTransactionBytes
@@ -849,7 +876,7 @@ class HttpLiveConnection implements EditableArtifactLiveConnection {
849
876
  if (frame.protocolVersion !== this.ticket.protocolVersion) {
850
877
  throw new TypeError("live snapshot protocol mismatch");
851
878
  }
852
- if (serverModality(frame.modality) !== this.ticket.modality) {
879
+ if (frame.modality !== this.ticket.modality) {
853
880
  throw new TypeError("live snapshot modality mismatch");
854
881
  }
855
882
  if (this.snapshotAssembly === null) {
@@ -895,7 +922,8 @@ class HttpLiveConnection implements EditableArtifactLiveConnection {
895
922
  ...commonSnapshot,
896
923
  modality: "spreadsheet" as const,
897
924
  causalFrontier: cloneFrontier(frame.causalFrontier),
898
- protocolVersion: frame.protocolVersion,
925
+ operationProtocolVersion: frame.operationProtocolVersion,
926
+ snapshotVersion: frame.snapshotVersion,
899
927
  })
900
928
  : Object.freeze({
901
929
  ...commonSnapshot,
@@ -968,7 +996,7 @@ class HttpLiveConnection implements EditableArtifactLiveConnection {
968
996
  if (frame.protocolVersion !== this.ticket.protocolVersion) {
969
997
  throw new TypeError("live server frame protocol mismatch");
970
998
  }
971
- if (frame.type === "open" && serverModality(frame.modality) !== this.ticket.modality) {
999
+ if (frame.type === "open" && frame.modality !== this.ticket.modality) {
972
1000
  throw new TypeError("live open modality does not match its ticket");
973
1001
  }
974
1002
  if (this.openFrame && frame.streamEpoch !== this.openFrame.streamEpoch) {
@@ -1113,16 +1141,6 @@ function cloneCommitted(
1113
1141
  };
1114
1142
  }
1115
1143
 
1116
- function transactionModality(
1117
- transaction: EditableArtifactCommittedTransaction | ContractCommittedTransaction,
1118
- ): EditableArtifactModality {
1119
- return transaction.modality ?? "spreadsheet";
1120
- }
1121
-
1122
- function serverModality(value: EditableArtifactModality | undefined): EditableArtifactModality {
1123
- return value ?? "spreadsheet";
1124
- }
1125
-
1126
1144
  function serializedBoundaryRevision(
1127
1145
  snapshot: EditableArtifactSnapshot | null,
1128
1146
  resume:
@@ -1148,8 +1166,7 @@ function snapshotChunkAuthorityEqual(
1148
1166
  frame: Extract<EditableArtifactLiveServerFrame, { type: "snapshot" }>,
1149
1167
  metadata: SnapshotAssembly["metadata"],
1150
1168
  ): boolean {
1151
- const modality = serverModality(frame.modality);
1152
- if (modality !== serverModality(metadata.modality)) return false;
1169
+ if (frame.modality !== metadata.modality) return false;
1153
1170
  if ("causalFrontier" in frame) {
1154
1171
  if (!("causalFrontier" in metadata)) return false;
1155
1172
  return frontiersEqual(frame.causalFrontier, metadata.causalFrontier);
@@ -1414,6 +1431,14 @@ function positiveU16(value: unknown, label: string): number {
1414
1431
  return number;
1415
1432
  }
1416
1433
 
1434
+ function exactVersion(value: unknown, expected: number, label: string): number {
1435
+ const actual = positiveU16(value, label);
1436
+ if (actual !== expected) {
1437
+ throw new TypeError(`${label} ${actual} is incompatible with current version ${expected}`);
1438
+ }
1439
+ return actual;
1440
+ }
1441
+
1417
1442
  function positiveBounded(value: unknown, maximum: number, label: string): number {
1418
1443
  if (!Number.isSafeInteger(value) || (value as number) <= 0 || (value as number) > maximum) {
1419
1444
  throw new TypeError(`${label} is invalid`);
@@ -16,6 +16,12 @@ export type {
16
16
  } from "./browser-session";
17
17
  export { EditableArtifactSyncError, EditableArtifactTransportError } from "./errors";
18
18
  export type { EditableArtifactSyncErrorCode } from "./errors";
19
+ export { classifyEditableArtifactOpenFailure } from "./open-failure";
20
+ export type {
21
+ EditableArtifactOpenFailureCategory,
22
+ EditableArtifactOpenFailureEvent,
23
+ EditableArtifactOpenFailureReporter,
24
+ } from "./open-failure";
19
25
  export { createEditableArtifactHttpLiveTransport } from "./http-live-transport";
20
26
  export type {
21
27
  CreateEditableArtifactHttpLiveTransportOptions,
@@ -0,0 +1,79 @@
1
+ export type EditableArtifactOpenFailureCategory =
2
+ | "unsupported_old_format"
3
+ | "byte_corruption"
4
+ | "authored_causal_mismatch"
5
+ | "runtime_package_mismatch"
6
+ | "projection_parity_ci_failure";
7
+
8
+ /** Content-free, bounded telemetry safe to emit for workbook-open failures. */
9
+ export type EditableArtifactOpenFailureEvent = Readonly<{
10
+ category: EditableArtifactOpenFailureCategory;
11
+ code: string;
12
+ }>;
13
+
14
+ export type EditableArtifactOpenFailureReporter = (event: EditableArtifactOpenFailureEvent) => void;
15
+
16
+ const BYTE_CORRUPTION_CODES = new Set([
17
+ "byte_corruption",
18
+ "artifact_bad_magic",
19
+ "artifact_checksum_mismatch",
20
+ "artifact_invalid_collaboration_snapshot",
21
+ "artifact_invalid_snapshot",
22
+ "artifact_invalid_tag",
23
+ "artifact_invalid_utf8",
24
+ "artifact_non_canonical",
25
+ "artifact_trailing_bytes",
26
+ "artifact_truncated",
27
+ ]);
28
+ const AUTHORED_CAUSAL_CODES = new Set([
29
+ "artifact_state_mismatch",
30
+ "authored_causal_mismatch",
31
+ "committed_metadata_mismatch",
32
+ "invalid_frontier",
33
+ "kernel_diverged",
34
+ ]);
35
+ const RUNTIME_PACKAGE_CODES = new Set([
36
+ "artifact_runtime_incompatible",
37
+ "artifact_runtime_integrity",
38
+ "artifact_runtime_manifest_invalid",
39
+ "artifact_runtime_unavailable",
40
+ "artifact_runtime_unsupported_target",
41
+ "incompatible_wasm_build",
42
+ "runtime_identity_mismatch",
43
+ ]);
44
+
45
+ /** Maps only stable error codes; messages and workbook/formula content are ignored. */
46
+ export function classifyEditableArtifactOpenFailure(
47
+ error: unknown,
48
+ ): EditableArtifactOpenFailureEvent | null {
49
+ const code = normalizedErrorCode(error);
50
+ if (code === null) return null;
51
+ let category: EditableArtifactOpenFailureCategory | null = null;
52
+ if (code === "unsupported_protocol" || code === "artifact_unsupported_version") {
53
+ category = "unsupported_old_format";
54
+ } else if (BYTE_CORRUPTION_CODES.has(code)) {
55
+ category = "byte_corruption";
56
+ } else if (AUTHORED_CAUSAL_CODES.has(code)) {
57
+ category = "authored_causal_mismatch";
58
+ } else if (RUNTIME_PACKAGE_CODES.has(code)) {
59
+ category = "runtime_package_mismatch";
60
+ } else if (code === "artifact_runtime_projection_parity") {
61
+ category = "projection_parity_ci_failure";
62
+ }
63
+ return category === null ? null : Object.freeze({ category, code });
64
+ }
65
+
66
+ function normalizedErrorCode(error: unknown): string | null {
67
+ if (typeof error !== "object" || error === null) return null;
68
+ let descriptor: PropertyDescriptor | undefined;
69
+ try {
70
+ descriptor = Object.getOwnPropertyDescriptor(error, "code");
71
+ } catch {
72
+ return null;
73
+ }
74
+ if (!descriptor || !("value" in descriptor)) return null;
75
+ const value = descriptor.value;
76
+ if (typeof value !== "string") return null;
77
+ const code = value.toLowerCase();
78
+ return /^[a-z][a-z0-9_]{0,127}$/u.test(code) ? code : null;
79
+ }
@@ -135,7 +135,7 @@ export class MemoryEditableArtifactStorage implements EditableArtifactStoragePor
135
135
  stateHash: replica.head.stateHash,
136
136
  modality: replica.head.modality,
137
137
  ...(replica.head.snapshot.modality === "spreadsheet"
138
- ? { protocolVersion: replica.head.snapshot.protocolVersion }
138
+ ? { operationProtocolVersion: replica.head.snapshot.operationProtocolVersion }
139
139
  : {}),
140
140
  },
141
141
  input,
@@ -227,10 +227,9 @@ type MemoryStoredReplica = {
227
227
 
228
228
  type IndexedDbStoredReplicaHead = Omit<StoredReplicaHead, "snapshot"> & {
229
229
  namespace: string;
230
- /** Absent only on legacy spreadsheet records. */
231
- modality?: EditableArtifactModality;
230
+ modality: EditableArtifactModality;
232
231
  /** Spreadsheet-only; absent for serialized modalities. */
233
- protocolVersion?: number;
232
+ operationProtocolVersion?: number;
234
233
  tailCount: number;
235
234
  tailBytes: number;
236
235
  };
@@ -240,10 +239,10 @@ type IndexedDbCommittedTransaction = EditableArtifactCommittedTransaction & {
240
239
  };
241
240
  type IndexedDbPendingTransaction = EditableArtifactPendingTransaction & {
242
241
  namespace: string;
243
- /** V3 IndexedDB key-path alias; always exactly clientTransactionId. */
242
+ /** IndexedDB key-path alias; always exactly clientTransactionId. */
244
243
  transactionId: string;
245
244
  };
246
- const DATABASE_VERSION = 3;
245
+ const DATABASE_VERSION = 4;
247
246
  const REPLICA_HEAD_STORE = "replicas";
248
247
  const SNAPSHOT_STORE = "snapshots";
249
248
  const COMMITTED_STORE = "committedTransactions";
@@ -352,7 +351,7 @@ export class IndexedDbEditableArtifactStorage implements EditableArtifactStorage
352
351
  assertStoredHeadAccounting(head, tail.length, retainedTailBytes);
353
352
  const {
354
353
  namespace: _headNamespace,
355
- protocolVersion: retainedProtocolVersion,
354
+ operationProtocolVersion: retainedOperationProtocolVersion,
356
355
  tailCount: _tailCount,
357
356
  tailBytes: _tailBytes,
358
357
  ...storedHead
@@ -364,13 +363,12 @@ export class IndexedDbEditableArtifactStorage implements EditableArtifactStorage
364
363
  normalizeStoredCommitted(value, ownedScope.modality),
365
364
  )
366
365
  .sort(compareCommittedTransactions);
367
- const headModality = head.modality ?? "spreadsheet";
368
- if (headModality !== ownedScope.modality) {
366
+ if (head.modality !== ownedScope.modality) {
369
367
  throw new TypeError("retained head modality does not match its storage scope");
370
368
  }
371
369
  if (
372
370
  storedSnapshot.modality === "spreadsheet" &&
373
- retainedProtocolVersion !== storedSnapshot.protocolVersion
371
+ retainedOperationProtocolVersion !== storedSnapshot.operationProtocolVersion
374
372
  )
375
373
  throw new TypeError("retained head protocol does not match its snapshot");
376
374
  const replica: EditableArtifactStoredReplica = {
@@ -643,7 +641,11 @@ function assertStoredReplica(replica: EditableArtifactStoredReplica): void {
643
641
  assertSha256(replica.snapshot.digest, "snapshot.digest");
644
642
  if (replica.snapshot.modality === "spreadsheet") {
645
643
  assertFrontier(replica.snapshot.causalFrontier, "snapshot.causalFrontier");
646
- assertPositiveInteger(replica.snapshot.protocolVersion, "snapshot.protocolVersion");
644
+ assertPositiveInteger(
645
+ replica.snapshot.operationProtocolVersion,
646
+ "snapshot.operationProtocolVersion",
647
+ );
648
+ assertPositiveInteger(replica.snapshot.snapshotVersion, "snapshot.snapshotVersion");
647
649
  } else {
648
650
  assertSequence(replica.snapshot.nativeRevision, "snapshot.nativeRevision");
649
651
  }
@@ -676,7 +678,7 @@ function assertStoredReplica(replica: EditableArtifactStoredReplica): void {
676
678
  if (
677
679
  transaction.modality === "spreadsheet" &&
678
680
  replica.snapshot.modality === "spreadsheet" &&
679
- transaction.protocolVersion !== replica.snapshot.protocolVersion
681
+ transaction.operationProtocolVersion !== replica.snapshot.operationProtocolVersion
680
682
  )
681
683
  throw new TypeError("stored committed tail protocol does not match its snapshot");
682
684
  if (transaction.modality !== "spreadsheet") {
@@ -805,11 +807,11 @@ function assertCommittedTransaction(
805
807
  }
806
808
  if (transaction.modality === "spreadsheet") {
807
809
  assertFrontier(transaction.causalFrontier, "causalFrontier");
808
- assertPositiveInteger(transaction.protocolVersion, "protocolVersion");
810
+ assertPositiveInteger(transaction.operationProtocolVersion, "operationProtocolVersion");
809
811
  const summary = decodeCommittedTransactionSummary(transaction.committedTransactionBytes);
810
812
  if (
811
813
  summary.transactionId !== transaction.transactionId ||
812
- summary.operationProtocolVersion !== transaction.protocolVersion ||
814
+ summary.operationProtocolVersion !== transaction.operationProtocolVersion ||
813
815
  summary.priorStateHash !== transaction.priorStateHash ||
814
816
  summary.stateHash !== transaction.stateHash ||
815
817
  !frontiersEqual(summary.resultingCausalFrontier, transaction.causalFrontier)
@@ -907,8 +909,8 @@ function assertPendingTransaction(transaction: EditableArtifactPendingTransactio
907
909
 
908
910
  function assertAppendCanApply(
909
911
  head: Pick<StoredReplicaHead, "cursor" | "stateHash"> & {
910
- modality?: EditableArtifactModality;
911
- protocolVersion?: number;
912
+ modality: EditableArtifactModality;
913
+ operationProtocolVersion?: number;
912
914
  },
913
915
  input: EditableArtifactAppendCommittedInput,
914
916
  ): void {
@@ -917,15 +919,14 @@ function assertAppendCanApply(
917
919
  `artifact ${input.artifactId} retained head no longer matches append expectation`,
918
920
  );
919
921
  }
920
- const headModality = head.modality ?? "spreadsheet";
921
- if (input.transaction.modality !== headModality) {
922
+ if (input.transaction.modality !== head.modality) {
922
923
  throw new EditableArtifactStorageConflictError(
923
924
  `artifact ${input.artifactId} append modality does not match retained snapshot`,
924
925
  );
925
926
  }
926
927
  if (
927
928
  input.transaction.modality === "spreadsheet" &&
928
- input.transaction.protocolVersion !== head.protocolVersion
929
+ input.transaction.operationProtocolVersion !== head.operationProtocolVersion
929
930
  ) {
930
931
  throw new EditableArtifactStorageConflictError(
931
932
  `artifact ${input.artifactId} append protocol does not match retained snapshot`,
@@ -936,7 +937,7 @@ function assertAppendCanApply(
936
937
  function assertReplacementCanApply(
937
938
  head:
938
939
  | (Pick<StoredReplicaHead, "cursor" | "stateHash"> & {
939
- protocolVersion?: number;
940
+ operationProtocolVersion?: number;
940
941
  })
941
942
  | undefined,
942
943
  expectedHead: EditableArtifactExpectedStoredHead,
@@ -1028,7 +1029,7 @@ function committedTransactionsEqual(
1028
1029
  left.priorStateHash === right.priorStateHash &&
1029
1030
  left.stateHash === right.stateHash &&
1030
1031
  (left.modality === "spreadsheet" && right.modality === "spreadsheet"
1031
- ? left.protocolVersion === right.protocolVersion &&
1032
+ ? left.operationProtocolVersion === right.operationProtocolVersion &&
1032
1033
  frontiersEqual(left.causalFrontier, right.causalFrontier)
1033
1034
  : left.modality !== "spreadsheet" &&
1034
1035
  right.modality !== "spreadsheet" &&
@@ -1107,9 +1108,7 @@ function normalizeStoredSnapshot(
1107
1108
  snapshot: Omit<IndexedDbStoredSnapshot, "namespace">,
1108
1109
  modality: EditableArtifactModality,
1109
1110
  ): EditableArtifactSnapshot {
1110
- const retainedModality =
1111
- (snapshot as { modality?: EditableArtifactModality }).modality ?? "spreadsheet";
1112
- if (retainedModality !== modality) {
1111
+ if (snapshot.modality !== modality) {
1113
1112
  throw new TypeError("retained snapshot modality does not match its storage scope");
1114
1113
  }
1115
1114
  return { ...snapshot, modality } as EditableArtifactSnapshot;
@@ -1119,9 +1118,7 @@ function normalizeStoredCommitted(
1119
1118
  transaction: Omit<IndexedDbCommittedTransaction, "namespace">,
1120
1119
  modality: EditableArtifactModality,
1121
1120
  ): EditableArtifactCommittedTransaction {
1122
- const retainedModality =
1123
- (transaction as { modality?: EditableArtifactModality }).modality ?? "spreadsheet";
1124
- if (retainedModality !== modality) {
1121
+ if (transaction.modality !== modality) {
1125
1122
  throw new TypeError("retained transaction modality does not match its storage scope");
1126
1123
  }
1127
1124
  return { ...transaction, modality } as EditableArtifactCommittedTransaction;
@@ -1131,9 +1128,7 @@ function normalizeStoredPending(
1131
1128
  transaction: Omit<IndexedDbPendingTransaction, "namespace" | "transactionId">,
1132
1129
  modality: EditableArtifactModality,
1133
1130
  ): EditableArtifactPendingTransaction {
1134
- const retainedModality =
1135
- (transaction as { modality?: EditableArtifactModality }).modality ?? "spreadsheet";
1136
- if (retainedModality !== modality) {
1131
+ if (transaction.modality !== modality) {
1137
1132
  throw new TypeError("retained pending modality does not match its storage scope");
1138
1133
  }
1139
1134
  return { ...transaction, modality } as EditableArtifactPendingTransaction;
@@ -1269,18 +1264,12 @@ function openDatabase(factory: IDBFactory, databaseName: string): Promise<IDBDat
1269
1264
  };
1270
1265
  request.onupgradeneeded = (event) => {
1271
1266
  const database = request.result;
1272
- if (event.oldVersion < 2) {
1273
- // V1 records had no principal partition. They cannot be attributed
1274
- // safely, so discard this rebuildable cache instead of guessing scope.
1267
+ if (event.oldVersion < DATABASE_VERSION) {
1268
+ // All local artifact state is reconstructible. A format hard cut drops
1269
+ // confirmed state and pending commands instead of interpreting old bytes.
1275
1270
  for (const name of [REPLICA_HEAD_STORE, SNAPSHOT_STORE, PENDING_STORE, COMMITTED_STORE]) {
1276
1271
  if (database.objectStoreNames.contains(name)) database.deleteObjectStore(name);
1277
1272
  }
1278
- } else if (event.oldVersion < 3) {
1279
- // V2 heads did not retain constant-time journal accounting. Confirmed
1280
- // state is reconstructible, so rebuild it while preserving the pending WAL.
1281
- for (const name of [REPLICA_HEAD_STORE, SNAPSHOT_STORE, COMMITTED_STORE]) {
1282
- if (database.objectStoreNames.contains(name)) database.deleteObjectStore(name);
1283
- }
1284
1273
  }
1285
1274
  if (!database.objectStoreNames.contains(REPLICA_HEAD_STORE)) {
1286
1275
  database.createObjectStore(REPLICA_HEAD_STORE, {
@@ -1373,7 +1362,9 @@ function replaceIndexedDbReplica(
1373
1362
  updatedAt: replica.updatedAt,
1374
1363
  modality: replica.modality,
1375
1364
  ...(replica.snapshot.modality === "spreadsheet"
1376
- ? { protocolVersion: replica.snapshot.protocolVersion }
1365
+ ? {
1366
+ operationProtocolVersion: replica.snapshot.operationProtocolVersion,
1367
+ }
1377
1368
  : {}),
1378
1369
  tailCount: replica.tail.length,
1379
1370
  tailBytes: committedTailBytes(replica.tail),
@@ -39,7 +39,8 @@ type EditableArtifactSnapshotCommon = {
39
39
  export type EditableArtifactSpreadsheetSnapshot = EditableArtifactSnapshotCommon & {
40
40
  modality: "spreadsheet";
41
41
  causalFrontier: EditableArtifactCausalFrontier;
42
- protocolVersion: number;
42
+ operationProtocolVersion: number;
43
+ snapshotVersion: number;
43
44
  };
44
45
 
45
46
  export type EditableArtifactSerializedSnapshot = EditableArtifactSnapshotCommon & {
@@ -67,7 +68,7 @@ export type EditableArtifactSpreadsheetCommittedTransaction =
67
68
  EditableArtifactCommittedTransactionCommon & {
68
69
  modality: "spreadsheet";
69
70
  causalFrontier: EditableArtifactCausalFrontier;
70
- protocolVersion: number;
71
+ operationProtocolVersion: number;
71
72
  /** Exact whole canonical OGACO committed-transaction envelope. */
72
73
  committedTransactionBytes: Uint8Array;
73
74
  };
@@ -236,7 +237,7 @@ export type EditableArtifactStoredReplica = {
236
237
 
237
238
  type EditableArtifactBootstrapCommon = {
238
239
  artifactId: EditableArtifactId;
239
- protocolVersion: number;
240
+ liveProtocolVersion: number;
240
241
  headSequence: number;
241
242
  headStateHash: string;
242
243
  kernelVersion: string;
@@ -4,15 +4,24 @@ import {
4
4
  EDITABLE_ARTIFACT_INTENT_VERSION,
5
5
  EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES,
6
6
  SPREADSHEET_ARTIFACT_COMMAND_VERSION,
7
+ SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION,
7
8
  } from "@opengeni/contracts/editable-artifacts";
8
- import { MAX_COMMITTED_TRANSACTION_BYTES } from "@opengeni/contracts/editable-artifact-committed-transaction";
9
+ import {
10
+ SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION,
11
+ SPREADSHEET_KERNEL_COMMAND_VERSION,
12
+ SPREADSHEET_KERNEL_SNAPSHOT_VERSION,
13
+ } from "@opengeni/contracts/editable-artifact-versions";
14
+ import {
15
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
16
+ MAX_COMMITTED_TRANSACTION_BYTES,
17
+ } from "@opengeni/contracts/editable-artifact-committed-transaction";
9
18
  import type { ArtifactWorkerInitializeInput } from "./wire-codec";
10
19
  import type { EditableArtifactModality } from "../types";
11
20
  import { ArtifactWorkerProtocolError } from "./rpc-protocol";
12
21
 
13
22
  export type ArtifactWorkerKernelSession = {
14
23
  readonly modality: EditableArtifactModality;
15
- /** Replays one exact whole canonical OGACO001 transaction. */
24
+ /** Replays one exact whole canonical OGACO002 transaction. */
16
25
  applyCommitted: (committedTransactionBytes: Uint8Array) => void;
17
26
  /** Authors and applies one exact OGATX001 intent over the current frontier. */
18
27
  applyPending: (intentBytes: Uint8Array) => void;
@@ -129,6 +138,7 @@ type WasmCapabilities = Readonly<{
129
138
  editableArtifactIntentVersion: number;
130
139
  kernelSnapshotVersion: number;
131
140
  spreadsheetCommandVersion: number;
141
+ spreadsheetModelSchemaVersion: number;
132
142
  maxCellsPerBatch: number;
133
143
  maxCommandBytes: number;
134
144
  maxCommands: number;
@@ -579,6 +589,7 @@ function decodeCapabilities(bytes: Uint8Array): WasmCapabilities {
579
589
  "safeRust",
580
590
  "sessionForks",
581
591
  "spreadsheetCommandVersion",
592
+ "spreadsheetModelSchemaVersion",
582
593
  "statefulSessions",
583
594
  "textLayout",
584
595
  "textLayoutFontBundleVersion",
@@ -637,6 +648,10 @@ function decodeCapabilities(bytes: Uint8Array): WasmCapabilities {
637
648
  value.spreadsheetCommandVersion,
638
649
  "spreadsheetCommandVersion",
639
650
  ),
651
+ spreadsheetModelSchemaVersion: positiveInteger(
652
+ value.spreadsheetModelSchemaVersion,
653
+ "spreadsheetModelSchemaVersion",
654
+ ),
640
655
  maxCellsPerBatch: positiveInteger(value.maxCellsPerBatch, "maxCellsPerBatch"),
641
656
  maxCommands: positiveInteger(value.maxCommands, "maxCommands"),
642
657
  maxSnapshotBytes: positiveInteger(value.maxSnapshotBytes, "maxSnapshotBytes"),
@@ -765,16 +780,17 @@ function decodeCapabilities(bytes: Uint8Array): WasmCapabilities {
765
780
  normalized.canonicalStateHash !== "sha256:canonical-snapshot" ||
766
781
  normalized.editableArtifactIntentVersion !== EDITABLE_ARTIFACT_INTENT_VERSION ||
767
782
  normalized.spreadsheetCommandVersion !== SPREADSHEET_ARTIFACT_COMMAND_VERSION ||
768
- normalized.collaborationSnapshotVersion !== 1 ||
769
- normalized.commandSchemaVersion !== 1 ||
770
- normalized.committedTransactionVersion !== 1 ||
783
+ normalized.spreadsheetModelSchemaVersion !== SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION ||
784
+ normalized.collaborationSnapshotVersion !== SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION ||
785
+ normalized.commandSchemaVersion !== SPREADSHEET_KERNEL_COMMAND_VERSION ||
786
+ normalized.committedTransactionVersion !== COMMITTED_TRANSACTION_PROTOCOL_VERSION ||
771
787
  normalized.documentCommandVersion !== 1 ||
772
788
  normalized.documentQueryResponseVersion !== 1 ||
773
789
  normalized.documentQueryVersion !== 1 ||
774
790
  normalized.documentReceiptVersion !== 1 ||
775
791
  normalized.documentSnapshotVersion !== 1 ||
776
792
  normalized.documentStatefulSessions !== normalized.document ||
777
- normalized.kernelSnapshotVersion !== 1 ||
793
+ normalized.kernelSnapshotVersion !== SPREADSHEET_KERNEL_SNAPSHOT_VERSION ||
778
794
  normalized.maxCellsPerBatch !== WASM_MAX_CELLS_PER_BATCH ||
779
795
  normalized.maxCommandBytes !== WASM_MAX_COMMAND_BYTES ||
780
796
  normalized.maxSpreadsheetCommandBytes !== EDITABLE_ARTIFACT_COMMAND_MAX_BYTES ||
@@ -868,7 +884,7 @@ function capabilityProfile(modality: EditableArtifactModality, capabilities: Was
868
884
  );
869
885
  }
870
886
  return {
871
- modelSchemaVersion: capabilities.collaborationSnapshotVersion,
887
+ modelSchemaVersion: capabilities.spreadsheetModelSchemaVersion,
872
888
  commandVersion: capabilities.spreadsheetCommandVersion,
873
889
  maximumSnapshotBytes: capabilities.maxSnapshotBytes,
874
890
  maximumCommandBytes: capabilities.maxSpreadsheetCommandBytes,