@opengeni/sdk 0.52.1 → 1.0.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 (300) hide show
  1. package/README.md +1078 -33
  2. package/dist/accounts.d.ts +153 -0
  3. package/dist/accounts.js +307 -0
  4. package/dist/accounts.js.map +1 -0
  5. package/dist/adapters/express.d.ts +31 -0
  6. package/dist/adapters/express.js +87 -0
  7. package/dist/adapters/express.js.map +1 -0
  8. package/dist/adapters/hono.d.ts +16 -0
  9. package/dist/adapters/hono.js +8 -0
  10. package/dist/adapters/hono.js.map +1 -0
  11. package/dist/adapters/next.d.ts +29 -0
  12. package/dist/adapters/next.js +22 -0
  13. package/dist/adapters/next.js.map +1 -0
  14. package/dist/allowance-refusal.d.ts +2 -0
  15. package/dist/allowance-refusal.js +50 -0
  16. package/dist/allowance-refusal.js.map +1 -0
  17. package/dist/artifact-catalog.d.ts +31 -0
  18. package/dist/artifact-client.d.ts +23 -11
  19. package/dist/artifacts.d.ts +1 -1
  20. package/dist/artifacts.js +7 -5
  21. package/dist/automations-client.d.ts +17 -0
  22. package/dist/automations.d.ts +3 -0
  23. package/dist/automations.js +84 -0
  24. package/dist/automations.js.map +1 -0
  25. package/dist/browser.d.ts +11 -0
  26. package/dist/browser.js +47 -0
  27. package/dist/browser.js.map +1 -0
  28. package/dist/chat/fold.d.ts +75 -0
  29. package/dist/chat/handler.d.ts +41 -0
  30. package/dist/chat/http.d.ts +67 -0
  31. package/dist/chat/ids.d.ts +34 -0
  32. package/dist/chat/index.d.ts +9 -0
  33. package/dist/chat/index.js +1694 -0
  34. package/dist/chat/index.js.map +1 -0
  35. package/dist/chat/openai.d.ts +30 -0
  36. package/dist/chat/opengeni.d.ts +128 -0
  37. package/dist/chat/types.d.ts +207 -0
  38. package/dist/chat/vercel.d.ts +49 -0
  39. package/dist/chats.d.ts +8 -0
  40. package/dist/{chunk-KIHGPM7H.js → chunk-2UXZUCEZ.js} +309 -46
  41. package/dist/chunk-2UXZUCEZ.js.map +1 -0
  42. package/dist/chunk-4SMEOV3P.js +246 -0
  43. package/dist/chunk-4SMEOV3P.js.map +1 -0
  44. package/dist/{chunk-FHI4DFIG.js → chunk-AH5D5Q5V.js} +31 -35
  45. package/dist/chunk-AH5D5Q5V.js.map +1 -0
  46. package/dist/chunk-APXVSSSY.js +301 -0
  47. package/dist/chunk-APXVSSSY.js.map +1 -0
  48. package/dist/chunk-DUVIYER4.js +639 -0
  49. package/dist/chunk-DUVIYER4.js.map +1 -0
  50. package/dist/{chunk-ILQZR5N6.js → chunk-FG3OO5LM.js} +3484 -750
  51. package/dist/chunk-FG3OO5LM.js.map +1 -0
  52. package/dist/chunk-GZYZT22T.js +260 -0
  53. package/dist/chunk-GZYZT22T.js.map +1 -0
  54. package/dist/chunk-HMGADJGZ.js +150 -0
  55. package/dist/chunk-HMGADJGZ.js.map +1 -0
  56. package/dist/{chunk-DXDL7EEW.js → chunk-KWADQ227.js} +198 -114
  57. package/dist/chunk-KWADQ227.js.map +1 -0
  58. package/dist/chunk-LY2K6ELJ.js +126 -0
  59. package/dist/chunk-LY2K6ELJ.js.map +1 -0
  60. package/dist/chunk-MOTJYIYL.js +340 -0
  61. package/dist/chunk-MOTJYIYL.js.map +1 -0
  62. package/dist/{chunk-MZWTWOX6.js → chunk-NC57YMUO.js} +447 -3
  63. package/dist/chunk-NC57YMUO.js.map +1 -0
  64. package/dist/chunk-RWKB6YZG.js +1270 -0
  65. package/dist/chunk-RWKB6YZG.js.map +1 -0
  66. package/dist/chunk-U6CV2SXO.js +195 -0
  67. package/dist/chunk-U6CV2SXO.js.map +1 -0
  68. package/dist/chunk-UM4ZXXJR.js +9 -0
  69. package/dist/chunk-UM4ZXXJR.js.map +1 -0
  70. package/dist/chunk-V6GIY62B.js +229 -0
  71. package/dist/chunk-V6GIY62B.js.map +1 -0
  72. package/dist/chunk-WU6GQG25.js +268 -0
  73. package/dist/chunk-WU6GQG25.js.map +1 -0
  74. package/dist/{chunk-VWE2QIVO.js → chunk-XNG7YGR3.js} +23 -6
  75. package/dist/chunk-XNG7YGR3.js.map +1 -0
  76. package/dist/{chunk-FRJFNDIR.js → chunk-YIKA2QT4.js} +37 -2
  77. package/dist/chunk-YIKA2QT4.js.map +1 -0
  78. package/dist/chunk-ZKPY6YAJ.js +135 -0
  79. package/dist/chunk-ZKPY6YAJ.js.map +1 -0
  80. package/dist/client.d.ts +843 -107
  81. package/dist/codex-realtime-controller.d.ts +6 -0
  82. package/dist/codex-realtime-controller.js +2 -2
  83. package/dist/codex-realtime-v3.d.ts +13 -2
  84. package/dist/company-brain.d.ts +22 -0
  85. package/dist/company-profile.d.ts +14 -1
  86. package/dist/core.d.ts +9 -3
  87. package/dist/core.js +28 -9
  88. package/dist/deprecation.d.ts +47 -0
  89. package/dist/desktop.d.ts +8 -0
  90. package/dist/document-authority-client.d.ts +51 -0
  91. package/dist/document-authority.d.ts +2 -0
  92. package/dist/document-authority.js +12 -0
  93. package/dist/document-authority.js.map +1 -0
  94. package/dist/editable-artifacts/browser-session.d.ts +7 -3
  95. package/dist/editable-artifacts/controller.d.ts +4 -1
  96. package/dist/editable-artifacts/errors.d.ts +1 -1
  97. package/dist/editable-artifacts/http-live-transport.d.ts +6 -2
  98. package/dist/editable-artifacts/index.d.ts +2 -0
  99. package/dist/editable-artifacts/open-failure.d.ts +9 -0
  100. package/dist/editable-artifacts/types.d.ts +6 -3
  101. package/dist/editable-artifacts/worker/kernel-adapter.d.ts +1 -1
  102. package/dist/editable-artifacts-worker.js +44 -17
  103. package/dist/editable-artifacts-worker.js.map +1 -1
  104. package/dist/editable-artifacts.js +243 -124
  105. package/dist/editable-artifacts.js.map +1 -1
  106. package/dist/embedding-client.d.ts +129 -0
  107. package/dist/errors.d.ts +36 -1
  108. package/dist/feedback.d.ts +23 -0
  109. package/dist/gateway-realtime-transport.d.ts +1 -0
  110. package/dist/gateway-realtime-transport.js +5 -3
  111. package/dist/github-repositories.d.ts +10 -0
  112. package/dist/github-repositories.js +36 -0
  113. package/dist/github-repositories.js.map +1 -0
  114. package/dist/index.d.ts +61 -12
  115. package/dist/index.js +277 -126
  116. package/dist/index.js.map +1 -1
  117. package/dist/interaction-revision-stream.d.ts +11 -0
  118. package/dist/interaction.d.ts +687 -5
  119. package/dist/interaction.js +32 -1
  120. package/dist/knowledge.d.ts +7 -0
  121. package/dist/knowledge.js +20 -0
  122. package/dist/knowledge.js.map +1 -0
  123. package/dist/message-links.d.ts +68 -0
  124. package/dist/model-context.d.ts +68 -0
  125. package/dist/model-display.d.ts +2 -0
  126. package/dist/model-display.js +15 -0
  127. package/dist/model-display.js.map +1 -0
  128. package/dist/model-picker-order.d.ts +1 -0
  129. package/dist/model-picker-order.js +10 -0
  130. package/dist/model-picker-order.js.map +1 -0
  131. package/dist/organization-integration-policy.d.ts +26 -0
  132. package/dist/organization-integration-policy.js +26 -0
  133. package/dist/organization-integration-policy.js.map +1 -0
  134. package/dist/organization-private-session-settings.d.ts +6 -0
  135. package/dist/organization-private-session-settings.js +22 -0
  136. package/dist/organization-private-session-settings.js.map +1 -0
  137. package/dist/organization-user-setup.d.ts +8 -0
  138. package/dist/organization-user-setup.js +16 -0
  139. package/dist/organization-user-setup.js.map +1 -0
  140. package/dist/pr-review-client.d.ts +16 -0
  141. package/dist/pr-review.d.ts +4 -0
  142. package/dist/pr-review.js +62 -0
  143. package/dist/pr-review.js.map +1 -0
  144. package/dist/preference-registry.d.ts +9 -1
  145. package/dist/proxy.d.ts +7 -5
  146. package/dist/realtime.d.ts +6 -4
  147. package/dist/realtime.js +10 -6
  148. package/dist/realtime.js.map +1 -1
  149. package/dist/retained-artifacts.d.ts +5 -1
  150. package/dist/session-history-import.d.ts +22 -0
  151. package/dist/session-history-import.js +36 -0
  152. package/dist/session-history-import.js.map +1 -0
  153. package/dist/session-list-entries.d.ts +1 -0
  154. package/dist/session-list-entries.js +7 -0
  155. package/dist/session-list-entries.js.map +1 -0
  156. package/dist/session-message-search.d.ts +53 -0
  157. package/dist/session-proxy-client.d.ts +24 -0
  158. package/dist/session-proxy.d.ts +245 -0
  159. package/dist/session-proxy.js +24 -0
  160. package/dist/session-proxy.js.map +1 -0
  161. package/dist/session-titles.d.ts +2 -0
  162. package/dist/site-browser-entry.d.ts +1 -0
  163. package/dist/site-browser-runtime.gen.d.ts +1 -0
  164. package/dist/site-document.d.ts +5 -0
  165. package/dist/site-document.js +20 -0
  166. package/dist/site-document.js.map +1 -0
  167. package/dist/site-document.test.d.ts +1 -0
  168. package/dist/site-http.d.ts +16 -0
  169. package/dist/site-tool-bridge.d.ts +44 -0
  170. package/dist/site.d.ts +101 -0
  171. package/dist/site.js +49 -0
  172. package/dist/site.js.map +1 -0
  173. package/dist/skills.d.ts +75 -0
  174. package/dist/stream.d.ts +2 -0
  175. package/dist/tenant-workspaces.d.ts +47 -0
  176. package/dist/tenant-workspaces.js +8 -0
  177. package/dist/tenant-workspaces.js.map +1 -0
  178. package/dist/tool-auth.d.ts +87 -0
  179. package/dist/tool-auth.js +23 -0
  180. package/dist/tool-auth.js.map +1 -0
  181. package/dist/tool-display-metadata.d.ts +3 -0
  182. package/dist/tools-FXBLUWAE.js +12 -0
  183. package/dist/tools-FXBLUWAE.js.map +1 -0
  184. package/dist/tools.d.ts +61 -0
  185. package/dist/types.d.ts +3048 -406
  186. package/dist/usage-allowances.d.ts +36 -0
  187. package/dist/usage-allowances.js +80 -0
  188. package/dist/usage-allowances.js.map +1 -0
  189. package/dist/workspace-artifacts.d.ts +33 -5
  190. package/dist/workspace-instruction-policies.d.ts +24 -2
  191. package/dist/workspace-integrations.d.ts +74 -0
  192. package/dist/workspace-integrations.js +65 -0
  193. package/dist/workspace-integrations.js.map +1 -0
  194. package/dist/workspace-learning.d.ts +124 -0
  195. package/dist/workspace-live-stream.d.ts +14 -0
  196. package/dist/workspace-state.d.ts +8 -71
  197. package/package.json +108 -3
  198. package/src/accounts.ts +443 -0
  199. package/src/adapters/express.ts +136 -0
  200. package/src/adapters/hono.ts +18 -0
  201. package/src/adapters/next.ts +42 -0
  202. package/src/allowance-refusal.ts +5 -0
  203. package/src/artifact-catalog.ts +41 -0
  204. package/src/artifact-client.ts +43 -52
  205. package/src/artifacts.ts +7 -1
  206. package/src/automations-client.ts +124 -0
  207. package/src/automations.ts +15 -0
  208. package/src/browser.ts +54 -0
  209. package/src/chat/fold.ts +431 -0
  210. package/src/chat/handler.ts +240 -0
  211. package/src/chat/http.ts +257 -0
  212. package/src/chat/ids.ts +85 -0
  213. package/src/chat/index.ts +87 -0
  214. package/src/chat/openai.ts +439 -0
  215. package/src/chat/opengeni.ts +722 -0
  216. package/src/chat/types.ts +219 -0
  217. package/src/chat/vercel.ts +204 -0
  218. package/src/chats.ts +16 -0
  219. package/src/client.ts +5162 -792
  220. package/src/codex-realtime-controller.ts +260 -12
  221. package/src/codex-realtime-lifecycle.ts +1 -0
  222. package/src/codex-realtime-v3.ts +162 -31
  223. package/src/codex-realtime.ts +1 -1
  224. package/src/company-brain.ts +59 -0
  225. package/src/company-profile.ts +17 -1
  226. package/src/core.ts +23 -1
  227. package/src/deprecation.ts +149 -0
  228. package/src/desktop.ts +11 -1
  229. package/src/document-authority-client.ts +219 -0
  230. package/src/document-authority.ts +17 -0
  231. package/src/editable-artifacts/browser-session.ts +36 -22
  232. package/src/editable-artifacts/controller.ts +102 -47
  233. package/src/editable-artifacts/errors.ts +2 -0
  234. package/src/editable-artifacts/http-live-transport.ts +62 -37
  235. package/src/editable-artifacts/index.ts +6 -0
  236. package/src/editable-artifacts/open-failure.ts +79 -0
  237. package/src/editable-artifacts/storage.ts +31 -40
  238. package/src/editable-artifacts/types.ts +6 -3
  239. package/src/editable-artifacts/worker/kernel-adapter.ts +23 -7
  240. package/src/editable-artifacts/worker/runtime.ts +26 -13
  241. package/src/editable-artifacts/worker/wire-codec.ts +23 -5
  242. package/src/embedding-client.ts +519 -0
  243. package/src/errors.ts +185 -4
  244. package/src/feedback.ts +20 -0
  245. package/src/gateway-realtime-transport.ts +253 -110
  246. package/src/github-repositories.ts +60 -0
  247. package/src/index.ts +603 -54
  248. package/src/interaction-revision-stream.ts +117 -0
  249. package/src/interaction.ts +1367 -51
  250. package/src/knowledge.ts +72 -0
  251. package/src/message-links.ts +178 -0
  252. package/src/model-context.ts +112 -0
  253. package/src/model-display.ts +10 -0
  254. package/src/model-picker-order.ts +6 -0
  255. package/src/organization-integration-policy.ts +54 -0
  256. package/src/organization-private-session-settings.ts +33 -0
  257. package/src/organization-user-setup.ts +31 -0
  258. package/src/pr-review-client.ts +92 -0
  259. package/src/pr-review.ts +17 -0
  260. package/src/preference-registry.ts +11 -0
  261. package/src/proxy.ts +17 -6
  262. package/src/realtime.ts +15 -4
  263. package/src/retained-artifacts.ts +101 -0
  264. package/src/session-history-import.ts +84 -0
  265. package/src/session-list-entries.ts +1 -0
  266. package/src/session-message-search.ts +47 -0
  267. package/src/session-proxy-client.ts +64 -0
  268. package/src/session-proxy.ts +1473 -0
  269. package/src/session-titles.ts +10 -0
  270. package/src/site-browser-entry.ts +6 -0
  271. package/src/site-browser-runtime.gen.ts +2 -0
  272. package/src/site-document.test.ts +64 -0
  273. package/src/site-document.ts +18 -0
  274. package/src/site-http.ts +162 -0
  275. package/src/site-tool-bridge.ts +146 -0
  276. package/src/site.ts +585 -0
  277. package/src/skills.ts +69 -0
  278. package/src/sse.ts +8 -2
  279. package/src/stream.ts +38 -4
  280. package/src/tenant-workspaces.ts +148 -0
  281. package/src/tool-auth.ts +301 -0
  282. package/src/tool-display-metadata.ts +19 -0
  283. package/src/tools.ts +396 -0
  284. package/src/types.ts +3607 -493
  285. package/src/usage-allowances.ts +189 -0
  286. package/src/workspace-artifacts.ts +40 -6
  287. package/src/workspace-instruction-policies.ts +26 -1
  288. package/src/workspace-integrations.ts +402 -0
  289. package/src/workspace-learning.ts +138 -0
  290. package/src/workspace-live-stream.ts +137 -0
  291. package/src/workspace-state.ts +8 -82
  292. package/dist/chunk-DXDL7EEW.js.map +0 -1
  293. package/dist/chunk-FHI4DFIG.js.map +0 -1
  294. package/dist/chunk-FRJFNDIR.js.map +0 -1
  295. package/dist/chunk-ILQZR5N6.js.map +0 -1
  296. package/dist/chunk-KIHGPM7H.js.map +0 -1
  297. package/dist/chunk-MZWTWOX6.js.map +0 -1
  298. package/dist/chunk-V5F253OG.js +0 -116
  299. package/dist/chunk-V5F253OG.js.map +0 -1
  300. package/dist/chunk-VWE2QIVO.js.map +0 -1
@@ -2,16 +2,24 @@ import {
2
2
  decodeEditableArtifactMutationIntent,
3
3
  EDITABLE_ARTIFACT_COMMAND_MAX_BYTES,
4
4
  EDITABLE_ARTIFACT_INTENT_MAX_BYTES,
5
+ EDITABLE_ARTIFACT_INTENT_PROTOCOL_VERSION,
5
6
  EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES,
6
7
  hashEditableArtifactMutationIntentBytes,
7
8
  } from "@opengeni/contracts/editable-artifacts";
8
9
  import {
10
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
9
11
  decodeCommittedTransactionSummary,
10
12
  MAX_COMMITTED_TRANSACTION_BYTES,
11
13
  } from "@opengeni/contracts/editable-artifact-committed-transaction";
12
14
  import { editableArtifactCodecFor } from "@opengeni/contracts/editable-artifact-codec-registry";
15
+ import { EDITABLE_ARTIFACT_LIVE_WIRE_VERSION } from "@opengeni/contracts/editable-artifact-live";
13
16
  import { decodeEditableArtifactSerializedCommit } from "@opengeni/contracts/editable-artifact-serialized-commit";
17
+ import { SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION } from "@opengeni/contracts/editable-artifact-versions";
14
18
  import { EditableArtifactSyncError } from "./errors";
19
+ import {
20
+ classifyEditableArtifactOpenFailure,
21
+ type EditableArtifactOpenFailureReporter,
22
+ } from "./open-failure";
15
23
  import { EditableArtifactStorageConflictError } from "./storage";
16
24
  import type { EditableArtifactStoragePort, EditableArtifactStorageScope } from "./storage";
17
25
  import type {
@@ -42,7 +50,6 @@ import type {
42
50
  EditableArtifactWorkerKernel,
43
51
  } from "./types";
44
52
 
45
- const DEFAULT_PROTOCOL_VERSION = 1;
46
53
  const DEFAULT_REPLAY_PAGE_SIZE = 256;
47
54
  const DEFAULT_MAX_QUEUE_MESSAGES = 512;
48
55
  const DEFAULT_MAX_QUEUE_BYTES = 8 * 1024 * 1024;
@@ -70,7 +77,7 @@ export type CreateEditableArtifactSyncControllerOptions = {
70
77
  kernelVersion: string;
71
78
  modelSchemaVersion: number;
72
79
  commandVersion: number;
73
- protocolVersion?: number;
80
+ protocolVersion: number;
74
81
  replayPageSize?: number;
75
82
  maxQueuedMessages?: number;
76
83
  maxQueuedBytes?: number;
@@ -87,6 +94,8 @@ export type CreateEditableArtifactSyncControllerOptions = {
87
94
  maxReconnectAttempts?: number;
88
95
  scheduler?: EditableArtifactSyncScheduler;
89
96
  clientTransactionIdFactory?: () => string;
97
+ /** Receives bounded categories/codes only; never workbook or formula content. */
98
+ onOpenFailure?: EditableArtifactOpenFailureReporter;
90
99
  /** Tests/embedded runtimes may inject; defaults to a fresh per-controller UUID. */
91
100
  writerReplicaIdFactory?: () => string;
92
101
  };
@@ -186,6 +195,7 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
186
195
  private readonly maxReconnectAttempts: number;
187
196
  private readonly scheduler: EditableArtifactSyncScheduler;
188
197
  private readonly clientTransactionIdFactory: () => string;
198
+ private readonly onOpenFailure: EditableArtifactOpenFailureReporter | undefined;
189
199
 
190
200
  private readonly abortController = new AbortController();
191
201
  private readonly listeners = new Set<EditableArtifactSyncListener>();
@@ -258,10 +268,10 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
258
268
  );
259
269
  this.modelSchemaVersion = positiveSafeInteger(options.modelSchemaVersion, "modelSchemaVersion");
260
270
  this.commandVersion = positiveSafeInteger(options.commandVersion, "commandVersion");
261
- this.protocolVersion = positiveSafeInteger(
262
- options.protocolVersion ?? DEFAULT_PROTOCOL_VERSION,
263
- "protocolVersion",
264
- );
271
+ this.protocolVersion = positiveSafeInteger(options.protocolVersion, "protocolVersion");
272
+ if (this.protocolVersion !== EDITABLE_ARTIFACT_INTENT_PROTOCOL_VERSION) {
273
+ throw new TypeError("protocolVersion is incompatible with the current intent protocol");
274
+ }
265
275
  this.replayPageSize = positiveSafeInteger(
266
276
  options.replayPageSize ?? DEFAULT_REPLAY_PAGE_SIZE,
267
277
  "replayPageSize",
@@ -324,6 +334,7 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
324
334
  this.scheduler = options.scheduler ?? defaultScheduler;
325
335
  this.clientTransactionIdFactory =
326
336
  options.clientTransactionIdFactory ?? (() => crypto.randomUUID());
337
+ this.onOpenFailure = options.onOpenFailure;
327
338
  }
328
339
 
329
340
  start(): void {
@@ -331,6 +342,7 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
331
342
  this.runTask = this.run().catch((error: unknown) => {
332
343
  if (this.abortController.signal.aborted) return;
333
344
  const failure = asError(error);
345
+ this.reportOpenFailure(failure);
334
346
  if (failure instanceof EditableArtifactSyncError && failure.code === "unsupported_protocol") {
335
347
  this.lastError = failure;
336
348
  this.setState("unsupported");
@@ -366,6 +378,15 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
366
378
  }
367
379
 
368
380
  getView(): EditableArtifactSyncView {
381
+ const blockedPending = this.allBlockedPending();
382
+ const authoringBlockedReason =
383
+ this.modality !== "spreadsheet" && blockedPending.length > 0
384
+ ? "pending_conflict"
385
+ : this.orderedPending().some(
386
+ (transaction) => transaction.replicaId !== this.writerReplicaId,
387
+ )
388
+ ? "prior_writer"
389
+ : undefined;
369
390
  return {
370
391
  artifactId: this.artifactId,
371
392
  modality: this.modality,
@@ -374,7 +395,8 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
374
395
  headSequence: this.headSequence,
375
396
  writable: this.writable,
376
397
  pendingTransactions: this.pending.size,
377
- blockedPending: this.allBlockedPending(),
398
+ blockedPending,
399
+ authoringBlockedReason,
378
400
  queuedMessages: this.liveQueue.length,
379
401
  reconnectAttempt: this.reconnectAttempt,
380
402
  lastError: this.lastError,
@@ -664,7 +686,8 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
664
686
  try {
665
687
  await this.restoreReplica(stored);
666
688
  await this.replacePendingOverlay();
667
- } catch {
689
+ } catch (error) {
690
+ this.reportOpenFailure(error);
668
691
  await this.kernel.reset();
669
692
  this.replica = null;
670
693
  this.retainedTailTransactions = 0;
@@ -680,8 +703,8 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
680
703
  stored.snapshot,
681
704
  this.artifactId,
682
705
  this.modality,
683
- this.protocolVersion,
684
- this.kernelVersion,
706
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
707
+ SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION,
685
708
  this.modelSchemaVersion,
686
709
  this.maxSnapshotBytes,
687
710
  );
@@ -701,12 +724,19 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
701
724
  stored.snapshot.modality === "spreadsheet" ? stored.snapshot.causalFrontier : null;
702
725
  let nativeRevision =
703
726
  stored.snapshot.modality === "spreadsheet" ? null : stored.snapshot.nativeRevision;
727
+ const retainedAcceptances: {
728
+ transaction: EditableArtifactCommittedTransaction;
729
+ pending: Pick<
730
+ EditableArtifactPendingTransaction,
731
+ "clientTransactionId" | "requestHash" | "intentBytes"
732
+ >;
733
+ }[] = [];
704
734
  for (const transaction of stored.tail) {
705
- validateCommittedTransaction(
735
+ const embeddedPending = validateCommittedTransaction(
706
736
  transaction,
707
737
  this.artifactId,
708
738
  this.modality,
709
- this.protocolVersion,
739
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
710
740
  this.maxCommittedTransactionBytes,
711
741
  );
712
742
  if (transaction.startSequence !== cursor + 1) {
@@ -715,6 +745,15 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
715
745
  requireStateHash(transaction.priorStateHash, stateHash, "retained transaction prior state");
716
746
  const applied = await this.kernel.applyRecovered(transaction);
717
747
  requireStateHash(applied.stateHash, transaction.stateHash, "retained transaction");
748
+ if (embeddedPending) {
749
+ const pending = this.pending.get(embeddedPending.clientTransactionId);
750
+ if (
751
+ pending?.requestHash === embeddedPending.requestHash &&
752
+ bytesEqual(pending.intentBytes, embeddedPending.intentBytes)
753
+ ) {
754
+ retainedAcceptances.push({ transaction, pending: embeddedPending });
755
+ }
756
+ }
718
757
  cursor = transaction.endSequence;
719
758
  stateHash = transaction.stateHash;
720
759
  if (transaction.modality === "spreadsheet") {
@@ -750,6 +789,14 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
750
789
  this.retainedTailTransactions = stored.tail.length;
751
790
  this.cursor = cursor;
752
791
  this.headSequence = cursor;
792
+ // Reload may interrupt between retaining a commit and deleting its WAL entry.
793
+ // Only settle exact embedded identities after the entire retained head verifies;
794
+ // otherwise speculative replay mistakes our own committed edit for a conflict.
795
+ // Callers restore the pending overlay after this confirmed projection is ready.
796
+ for (const { transaction, pending } of retainedAcceptances) {
797
+ this.recordAcceptedMappingIdentity(transaction.transactionId, pending);
798
+ await this.settlePending(transaction, true);
799
+ }
753
800
  }
754
801
 
755
802
  private async run(): Promise<void> {
@@ -776,6 +823,7 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
776
823
  delayMs = this.reconnectDelayMs;
777
824
  }
778
825
  const failure = asError(error);
826
+ this.reportOpenFailure(failure);
779
827
  this.lastError = failure;
780
828
  if (failure instanceof EditableArtifactSyncError && failure.requiresSnapshot) {
781
829
  this.requireSnapshot = true;
@@ -956,14 +1004,11 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
956
1004
  if (bootstrap.modality !== this.modality) {
957
1005
  throw invalidBootstrap("bootstrap modality does not match the controller");
958
1006
  }
959
- this.requireProtocol(bootstrap.protocolVersion);
960
- if (
961
- bootstrap.kernelVersion !== this.kernelVersion ||
962
- bootstrap.modelSchemaVersion !== this.modelSchemaVersion
963
- ) {
1007
+ this.requireLiveProtocol(bootstrap.liveProtocolVersion);
1008
+ if (bootstrap.modelSchemaVersion !== this.modelSchemaVersion) {
964
1009
  throw new EditableArtifactSyncError(
965
1010
  "unsupported_protocol",
966
- `artifact kernel/schema ${bootstrap.kernelVersion}/${bootstrap.modelSchemaVersion} is incompatible with ${this.kernelVersion}/${this.modelSchemaVersion}`,
1011
+ `artifact schema ${bootstrap.modelSchemaVersion} is incompatible with ${this.modelSchemaVersion}`,
967
1012
  );
968
1013
  }
969
1014
  requireSha256(bootstrap.headStateHash, "bootstrap.headStateHash");
@@ -1035,8 +1080,8 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
1035
1080
  snapshot,
1036
1081
  this.artifactId,
1037
1082
  this.modality,
1038
- this.protocolVersion,
1039
- this.kernelVersion,
1083
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
1084
+ SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION,
1040
1085
  this.modelSchemaVersion,
1041
1086
  this.maxSnapshotBytes,
1042
1087
  );
@@ -1136,7 +1181,7 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
1136
1181
  transaction,
1137
1182
  this.artifactId,
1138
1183
  this.modality,
1139
- this.protocolVersion,
1184
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
1140
1185
  this.maxCommittedTransactionBytes,
1141
1186
  );
1142
1187
  pageBytes += estimateCommittedTransactionBytes(transaction);
@@ -1167,7 +1212,7 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
1167
1212
  message,
1168
1213
  this.artifactId,
1169
1214
  this.modality,
1170
- this.protocolVersion,
1215
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
1171
1216
  this.maxCommittedTransactionBytes,
1172
1217
  );
1173
1218
  bytes = estimateLiveMessageBytes(message);
@@ -1271,7 +1316,7 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
1271
1316
  transaction,
1272
1317
  this.artifactId,
1273
1318
  this.modality,
1274
- this.protocolVersion,
1319
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
1275
1320
  this.maxCommittedTransactionBytes,
1276
1321
  );
1277
1322
  if (embeddedPending) {
@@ -1568,7 +1613,7 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
1568
1613
  receipt.committed,
1569
1614
  this.artifactId,
1570
1615
  this.modality,
1571
- this.protocolVersion,
1616
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
1572
1617
  this.maxCommittedTransactionBytes,
1573
1618
  );
1574
1619
  this.recordAcceptedMapping(receipt);
@@ -1641,7 +1686,7 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
1641
1686
  "sync ticket is not bound to the requested writer replica",
1642
1687
  );
1643
1688
  }
1644
- this.requireProtocol(ticket.protocolVersion);
1689
+ this.requireLiveProtocol(ticket.protocolVersion);
1645
1690
  boundedNonEmpty(ticket.token, "ticket.token", MAX_TICKET_BYTES);
1646
1691
  const expiresAt = Date.parse(ticket.expiresAt);
1647
1692
  if (!Number.isFinite(expiresAt)) {
@@ -1758,11 +1803,11 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
1758
1803
  );
1759
1804
  }
1760
1805
 
1761
- private requireProtocol(actual: number): void {
1762
- if (actual !== this.protocolVersion) {
1806
+ private requireLiveProtocol(actual: number): void {
1807
+ if (actual !== EDITABLE_ARTIFACT_LIVE_WIRE_VERSION) {
1763
1808
  throw new EditableArtifactSyncError(
1764
1809
  "unsupported_protocol",
1765
- `artifact protocol ${actual} is incompatible with client ${this.protocolVersion}`,
1810
+ `live protocol ${actual} is incompatible with ${EDITABLE_ARTIFACT_LIVE_WIRE_VERSION}`,
1766
1811
  );
1767
1812
  }
1768
1813
  }
@@ -1794,6 +1839,16 @@ class EditableArtifactSyncControllerImpl implements EditableArtifactSyncControll
1794
1839
  this.liveWaiters.clear();
1795
1840
  }
1796
1841
 
1842
+ private reportOpenFailure(error: unknown): void {
1843
+ const event = classifyEditableArtifactOpenFailure(error);
1844
+ if (event === null || this.onOpenFailure === undefined) return;
1845
+ try {
1846
+ this.onOpenFailure(event);
1847
+ } catch {
1848
+ // Observability must never change recovery or availability behavior.
1849
+ }
1850
+ }
1851
+
1797
1852
  private fail(error: Error): void {
1798
1853
  this.lastError = error;
1799
1854
  this.setState("failed");
@@ -1971,7 +2026,7 @@ function validateCommittedTransaction(
1971
2026
  transaction: EditableArtifactCommittedTransaction,
1972
2027
  artifactId: EditableArtifactId,
1973
2028
  modality: EditableArtifactModality,
1974
- protocolVersion: number,
2029
+ operationProtocolVersion: number,
1975
2030
  maxCommittedTransactionBytes: number,
1976
2031
  ): Readonly<{
1977
2032
  clientTransactionId: string;
@@ -2000,10 +2055,10 @@ function validateCommittedTransaction(
2000
2055
  throw invalidSequence("transaction committedTransactionBytes exceeds the configured bound");
2001
2056
  }
2002
2057
  if (transaction.modality === "spreadsheet") {
2003
- if (transaction.protocolVersion !== protocolVersion) {
2058
+ if (transaction.operationProtocolVersion !== operationProtocolVersion) {
2004
2059
  throw new EditableArtifactSyncError(
2005
2060
  "unsupported_protocol",
2006
- `transaction protocol ${transaction.protocolVersion} is incompatible with client ${protocolVersion}`,
2061
+ `transaction operation protocol ${transaction.operationProtocolVersion} is incompatible with ${operationProtocolVersion}`,
2007
2062
  );
2008
2063
  }
2009
2064
  validateFrontier(transaction.causalFrontier, "causalFrontier");
@@ -2015,7 +2070,7 @@ function validateCommittedTransaction(
2015
2070
  }
2016
2071
  if (
2017
2072
  summary.transactionId !== transaction.transactionId ||
2018
- summary.operationProtocolVersion !== transaction.protocolVersion ||
2073
+ summary.operationProtocolVersion !== transaction.operationProtocolVersion ||
2019
2074
  summary.priorStateHash !== transaction.priorStateHash ||
2020
2075
  summary.stateHash !== transaction.stateHash ||
2021
2076
  !frontiersEqual(summary.resultingCausalFrontier, transaction.causalFrontier)
@@ -2061,8 +2116,8 @@ function validateSnapshot(
2061
2116
  snapshot: EditableArtifactStoredReplica["snapshot"],
2062
2117
  artifactId: EditableArtifactId,
2063
2118
  modality: EditableArtifactModality,
2064
- protocolVersion: number,
2065
- kernelVersion: string,
2119
+ operationProtocolVersion: number,
2120
+ snapshotVersion: number,
2066
2121
  modelSchemaVersion: number,
2067
2122
  maxSnapshotBytes: number,
2068
2123
  ): void {
@@ -2076,22 +2131,22 @@ function validateSnapshot(
2076
2131
  positiveSafeInteger(snapshot.modelSchemaVersion, "snapshot.modelSchemaVersion");
2077
2132
  if (snapshot.modality === "spreadsheet") {
2078
2133
  validateFrontier(snapshot.causalFrontier, "snapshot.causalFrontier");
2079
- if (snapshot.protocolVersion !== protocolVersion) {
2134
+ if (
2135
+ snapshot.operationProtocolVersion !== operationProtocolVersion ||
2136
+ snapshot.snapshotVersion !== snapshotVersion
2137
+ ) {
2080
2138
  throw new EditableArtifactSyncError(
2081
2139
  "unsupported_protocol",
2082
- `snapshot protocol ${snapshot.protocolVersion} is incompatible with client ${protocolVersion}`,
2140
+ `snapshot operation/state versions ${snapshot.operationProtocolVersion}/${snapshot.snapshotVersion} are incompatible with ${operationProtocolVersion}/${snapshotVersion}`,
2083
2141
  );
2084
2142
  }
2085
2143
  } else {
2086
2144
  requireNativeRevision(snapshot.nativeRevision, "snapshot.nativeRevision");
2087
2145
  }
2088
- if (
2089
- snapshot.kernelVersion !== kernelVersion ||
2090
- snapshot.modelSchemaVersion !== modelSchemaVersion
2091
- ) {
2146
+ if (snapshot.modelSchemaVersion !== modelSchemaVersion) {
2092
2147
  throw new EditableArtifactSyncError(
2093
2148
  "unsupported_protocol",
2094
- `snapshot kernel/schema ${snapshot.kernelVersion}/${snapshot.modelSchemaVersion} is incompatible with ${kernelVersion}/${modelSchemaVersion}`,
2149
+ `snapshot schema ${snapshot.modelSchemaVersion} is incompatible with ${modelSchemaVersion}`,
2095
2150
  );
2096
2151
  }
2097
2152
  if (!(snapshot.bytes instanceof Uint8Array)) {
@@ -2131,7 +2186,7 @@ function validateLiveMessage(
2131
2186
  message: EditableArtifactLiveMessage,
2132
2187
  artifactId: EditableArtifactId,
2133
2188
  modality: EditableArtifactModality,
2134
- protocolVersion: number,
2189
+ operationProtocolVersion: number,
2135
2190
  maxCommittedTransactionBytes: number,
2136
2191
  ): void {
2137
2192
  if (!message || typeof message !== "object") {
@@ -2142,7 +2197,7 @@ function validateLiveMessage(
2142
2197
  message.transaction,
2143
2198
  artifactId,
2144
2199
  modality,
2145
- protocolVersion,
2200
+ operationProtocolVersion,
2146
2201
  maxCommittedTransactionBytes,
2147
2202
  );
2148
2203
  return;
@@ -2198,7 +2253,7 @@ function estimateCommittedTransactionBytes(
2198
2253
  function requireStateHash(actual: string, expected: string, source: string): void {
2199
2254
  if (actual !== expected) {
2200
2255
  throw new EditableArtifactSyncError(
2201
- "kernel_diverged",
2256
+ "authored_causal_mismatch",
2202
2257
  `${source} produced state hash ${actual}; expected ${expected}`,
2203
2258
  { retryable: true, requiresSnapshot: true },
2204
2259
  );
@@ -2209,7 +2264,7 @@ function requireDigest(actual: string, expected: string, source: string): void {
2209
2264
  requireSha256(actual, `${source} computed digest`);
2210
2265
  if (actual !== expected) {
2211
2266
  throw new EditableArtifactSyncError(
2212
- "kernel_diverged",
2267
+ "byte_corruption",
2213
2268
  `${source} bytes produced digest ${actual}; expected ${expected}`,
2214
2269
  { retryable: true, requiresSnapshot: true },
2215
2270
  );
@@ -2230,7 +2285,7 @@ function requireFrontier(
2230
2285
  )
2231
2286
  ) {
2232
2287
  throw new EditableArtifactSyncError(
2233
- "kernel_diverged",
2288
+ "authored_causal_mismatch",
2234
2289
  `${source} causal frontier does not match the authoritative barrier`,
2235
2290
  { retryable: true, requiresSnapshot: true },
2236
2291
  );
@@ -2416,7 +2471,7 @@ function requireNativeRevisionEqual(actual: number, expected: number, source: st
2416
2471
  requireNativeRevision(expected, `${source} expected native revision`);
2417
2472
  if (actual !== expected) {
2418
2473
  throw new EditableArtifactSyncError(
2419
- "kernel_diverged",
2474
+ "authored_causal_mismatch",
2420
2475
  `${source} native revision ${actual} does not match ${expected}`,
2421
2476
  { retryable: true, requiresSnapshot: true },
2422
2477
  );
@@ -3,6 +3,8 @@ export type EditableArtifactSyncErrorCode =
3
3
  | "invalid_bootstrap"
4
4
  | "invalid_sequence"
5
5
  | "kernel_diverged"
6
+ | "byte_corruption"
7
+ | "authored_causal_mismatch"
6
8
  | "queue_overflow"
7
9
  | "resync_required"
8
10
  | "permission_changed"
@@ -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,