@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.
- package/README.md +117 -2
- package/dist/artifacts.js +5 -5
- package/dist/automations-client.d.ts +17 -0
- package/dist/automations.d.ts +3 -0
- package/dist/automations.js +78 -0
- package/dist/automations.js.map +1 -0
- package/dist/{chunk-YGH5P47G.js → chunk-6IPZY27S.js} +744 -57
- package/dist/chunk-6IPZY27S.js.map +1 -0
- package/dist/{chunk-TYZ4JL4J.js → chunk-AGUKZAXR.js} +2 -2
- package/dist/{chunk-A5DC5WEM.js → chunk-C2H7HBNP.js} +22 -2
- package/dist/chunk-C2H7HBNP.js.map +1 -0
- package/dist/{chunk-GU6JF75T.js → chunk-C4DJPZRU.js} +1 -1
- package/dist/{chunk-GU6JF75T.js.map → chunk-C4DJPZRU.js.map} +1 -1
- package/dist/{chunk-ST5DDKJP.js → chunk-GW3EJ2GP.js} +52 -1
- package/dist/chunk-GW3EJ2GP.js.map +1 -0
- package/dist/{chunk-4DV37UUA.js → chunk-TW5MNHUX.js} +16 -2
- package/dist/chunk-TW5MNHUX.js.map +1 -0
- package/dist/{chunk-VWE2QIVO.js → chunk-XNG7YGR3.js} +23 -6
- package/dist/chunk-XNG7YGR3.js.map +1 -0
- package/dist/client.d.ts +159 -26
- package/dist/codex-realtime-controller.d.ts +1 -0
- package/dist/codex-realtime-controller.js +2 -2
- package/dist/company-brain.d.ts +32 -0
- package/dist/company-profile.d.ts +1 -1
- package/dist/core.js +4 -4
- package/dist/editable-artifacts/browser-session.d.ts +7 -3
- package/dist/editable-artifacts/controller.d.ts +4 -1
- package/dist/editable-artifacts/errors.d.ts +1 -1
- package/dist/editable-artifacts/http-live-transport.d.ts +6 -2
- package/dist/editable-artifacts/index.d.ts +2 -0
- package/dist/editable-artifacts/open-failure.d.ts +9 -0
- package/dist/editable-artifacts/types.d.ts +4 -3
- package/dist/editable-artifacts/worker/kernel-adapter.d.ts +1 -1
- package/dist/editable-artifacts-worker.js +44 -17
- package/dist/editable-artifacts-worker.js.map +1 -1
- package/dist/editable-artifacts.js +222 -119
- package/dist/editable-artifacts.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.js +61 -12
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +23 -0
- package/dist/interaction.js +4 -1
- package/dist/organization-private-session-settings.d.ts +6 -0
- package/dist/organization-private-session-settings.js +22 -0
- package/dist/organization-private-session-settings.js.map +1 -0
- package/dist/pr-review-client.d.ts +15 -0
- package/dist/pr-review.d.ts +4 -0
- package/dist/pr-review.js +56 -0
- package/dist/pr-review.js.map +1 -0
- package/dist/preference-registry.d.ts +8 -0
- package/dist/realtime.js +2 -2
- package/dist/retained-artifacts.d.ts +5 -1
- package/dist/types.d.ts +1023 -13
- package/dist/workspace-instruction-policies.d.ts +22 -0
- package/dist/workspace-learning.d.ts +124 -0
- package/package.json +14 -2
- package/src/automations-client.ts +118 -0
- package/src/automations.ts +15 -0
- package/src/client.ts +1049 -71
- package/src/codex-realtime-controller.ts +21 -0
- package/src/company-brain.ts +99 -0
- package/src/company-profile.ts +4 -1
- package/src/editable-artifacts/browser-session.ts +36 -22
- package/src/editable-artifacts/controller.ts +66 -45
- package/src/editable-artifacts/errors.ts +2 -0
- package/src/editable-artifacts/http-live-transport.ts +62 -37
- package/src/editable-artifacts/index.ts +6 -0
- package/src/editable-artifacts/open-failure.ts +79 -0
- package/src/editable-artifacts/storage.ts +31 -40
- package/src/editable-artifacts/types.ts +4 -3
- package/src/editable-artifacts/worker/kernel-adapter.ts +23 -7
- package/src/editable-artifacts/worker/runtime.ts +26 -13
- package/src/editable-artifacts/worker/wire-codec.ts +23 -5
- package/src/errors.ts +1 -1
- package/src/index.ts +134 -0
- package/src/interaction.ts +95 -0
- package/src/organization-private-session-settings.ts +33 -0
- package/src/pr-review-client.ts +84 -0
- package/src/pr-review.ts +15 -0
- package/src/preference-registry.ts +9 -0
- package/src/retained-artifacts.ts +101 -0
- package/src/stream.ts +13 -2
- package/src/types.ts +1174 -12
- package/src/workspace-instruction-policies.ts +24 -0
- package/src/workspace-learning.ts +138 -0
- package/dist/chunk-4DV37UUA.js.map +0 -1
- package/dist/chunk-A5DC5WEM.js.map +0 -1
- package/dist/chunk-ST5DDKJP.js.map +0 -1
- package/dist/chunk-VWE2QIVO.js.map +0 -1
- package/dist/chunk-YGH5P47G.js.map +0 -1
- /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-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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:
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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" &&
|
|
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
|
-
|
|
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
|
-
? {
|
|
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
|
-
|
|
231
|
-
modality?: EditableArtifactModality;
|
|
230
|
+
modality: EditableArtifactModality;
|
|
232
231
|
/** Spreadsheet-only; absent for serialized modalities. */
|
|
233
|
-
|
|
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
|
-
/**
|
|
242
|
+
/** IndexedDB key-path alias; always exactly clientTransactionId. */
|
|
244
243
|
transactionId: string;
|
|
245
244
|
};
|
|
246
|
-
const DATABASE_VERSION =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
911
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 <
|
|
1273
|
-
//
|
|
1274
|
-
//
|
|
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
|
-
? {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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.
|
|
769
|
-
normalized.
|
|
770
|
-
normalized.
|
|
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 !==
|
|
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.
|
|
887
|
+
modelSchemaVersion: capabilities.spreadsheetModelSchemaVersion,
|
|
872
888
|
commandVersion: capabilities.spreadsheetCommandVersion,
|
|
873
889
|
maximumSnapshotBytes: capabilities.maxSnapshotBytes,
|
|
874
890
|
maximumCommandBytes: capabilities.maxSpreadsheetCommandBytes,
|