@opengeni/core 0.21.10 → 0.28.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 (132) hide show
  1. package/dist/access/index.d.ts +1 -1
  2. package/dist/application/new-session-drafts.d.ts +1 -1
  3. package/dist/application/session-commands.d.ts +2 -2
  4. package/dist/billing/limits.d.ts +1 -1
  5. package/dist/canonical-human-identities.d.ts +12 -0
  6. package/dist/canonical-human-identities.js +23 -0
  7. package/dist/canonical-human-identities.js.map +1 -0
  8. package/dist/chunk-6WKPWE5S.js +2380 -0
  9. package/dist/chunk-6WKPWE5S.js.map +1 -0
  10. package/dist/chunk-IBOEYG6N.js +34 -0
  11. package/dist/chunk-IBOEYG6N.js.map +1 -0
  12. package/dist/chunk-JJU6I5XD.js +2713 -0
  13. package/dist/chunk-JJU6I5XD.js.map +1 -0
  14. package/dist/chunk-NYPMQ7NO.js +2270 -0
  15. package/dist/chunk-NYPMQ7NO.js.map +1 -0
  16. package/dist/dependencies.d.ts +30 -6
  17. package/dist/domain/capabilities.d.ts +28 -3
  18. package/dist/domain/company-profile-durable-learning-adapter.d.ts +39 -0
  19. package/dist/domain/conversation-integrations.d.ts +225 -0
  20. package/dist/domain/durable-learning-slack-publication.d.ts +72 -0
  21. package/dist/domain/editable-artifacts/agent-application.d.ts +192 -0
  22. package/dist/domain/editable-artifacts/durable-export-postgres.d.ts +4 -0
  23. package/dist/domain/editable-artifacts/durable-export.d.ts +212 -0
  24. package/dist/domain/editable-artifacts/errors.d.ts +47 -0
  25. package/dist/domain/editable-artifacts/hash.d.ts +11 -0
  26. package/dist/domain/editable-artifacts/in-memory.d.ts +99 -0
  27. package/dist/domain/editable-artifacts/index.d.ts +14 -0
  28. package/dist/domain/editable-artifacts/ports.d.ts +341 -0
  29. package/dist/domain/editable-artifacts/postgres-adapters.d.ts +13 -0
  30. package/dist/domain/editable-artifacts/service.d.ts +105 -0
  31. package/dist/domain/editable-artifacts/snapshot-compaction.d.ts +44 -0
  32. package/dist/domain/editable-artifacts/snapshot-verifier-genesis.d.ts +50 -0
  33. package/dist/domain/editable-artifacts/snapshot-verifier-materialization.d.ts +21 -0
  34. package/dist/domain/editable-artifacts/snapshot-verifier.d.ts +118 -0
  35. package/dist/domain/editable-artifacts/types.d.ts +410 -0
  36. package/dist/domain/fiken.d.ts +35 -0
  37. package/dist/domain/memory-slack-delivery.d.ts +19 -0
  38. package/dist/domain/packs.d.ts +29 -1
  39. package/dist/domain/personal-connection-delegations.d.ts +9 -1
  40. package/dist/domain/scheduled-tasks.d.ts +2 -2
  41. package/dist/domain/session-tool-policy.d.ts +2 -2
  42. package/dist/domain/sessions.d.ts +24 -8
  43. package/dist/domain/skill-imports.d.ts +32 -0
  44. package/dist/domain/slack-publication-secret-safety.d.ts +8 -0
  45. package/dist/domain/timeline-annotations.d.ts +5 -0
  46. package/dist/domain/video-generation-capabilities.d.ts +10 -0
  47. package/dist/domain/video-generation.d.ts +47 -0
  48. package/dist/editable-artifact-live/application.d.ts +69 -0
  49. package/dist/editable-artifact-live/errors.d.ts +1 -0
  50. package/dist/editable-artifact-live/index.d.ts +8 -0
  51. package/dist/editable-artifact-live/outbox-dispatcher.d.ts +162 -0
  52. package/dist/editable-artifact-live/ports.d.ts +100 -0
  53. package/dist/editable-artifact-live/server.d.ts +71 -0
  54. package/dist/editable-artifact-live/ticket.d.ts +40 -0
  55. package/dist/editable-artifact-live/types.d.ts +213 -0
  56. package/dist/editable-artifact-live/wire.d.ts +34 -0
  57. package/dist/editable-artifact-live.d.ts +2 -0
  58. package/dist/editable-artifact-live.js +58 -0
  59. package/dist/editable-artifact-live.js.map +1 -0
  60. package/dist/editable-artifacts.d.ts +2 -0
  61. package/dist/editable-artifacts.js +179 -0
  62. package/dist/editable-artifacts.js.map +1 -0
  63. package/dist/index.d.ts +37 -26
  64. package/dist/index.js +4145 -959
  65. package/dist/index.js.map +1 -1
  66. package/dist/managed-session.d.ts +6 -2
  67. package/dist/rigs/index.d.ts +1 -1
  68. package/dist/rigs/provider-images.d.ts +31 -0
  69. package/dist/sandbox/fleet.d.ts +8 -5
  70. package/dist/sandbox/routing.d.ts +1 -0
  71. package/dist/session-authorization.d.ts +4 -2
  72. package/dist/transcription.d.ts +5 -0
  73. package/dist/workflow-wake-contract.d.ts +7 -0
  74. package/package.json +22 -10
  75. package/src/access/index.ts +14 -2
  76. package/src/application/new-session-drafts.ts +2 -0
  77. package/src/application/session-commands.ts +188 -107
  78. package/src/canonical-human-identities.ts +27 -0
  79. package/src/dependencies.ts +34 -2
  80. package/src/domain/capabilities.ts +729 -408
  81. package/src/domain/company-profile-durable-learning-adapter.ts +221 -0
  82. package/src/domain/conversation-integrations.ts +1238 -0
  83. package/src/domain/durable-learning-slack-publication.ts +202 -0
  84. package/src/domain/editable-artifacts/agent-application.ts +687 -0
  85. package/src/domain/editable-artifacts/durable-export-postgres.ts +139 -0
  86. package/src/domain/editable-artifacts/durable-export.ts +734 -0
  87. package/src/domain/editable-artifacts/errors.ts +115 -0
  88. package/src/domain/editable-artifacts/hash.ts +131 -0
  89. package/src/domain/editable-artifacts/in-memory.ts +1662 -0
  90. package/src/domain/editable-artifacts/index.ts +14 -0
  91. package/src/domain/editable-artifacts/ports.ts +462 -0
  92. package/src/domain/editable-artifacts/postgres-adapters.ts +49 -0
  93. package/src/domain/editable-artifacts/service.ts +2402 -0
  94. package/src/domain/editable-artifacts/snapshot-compaction.ts +246 -0
  95. package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +235 -0
  96. package/src/domain/editable-artifacts/snapshot-verifier-materialization.ts +74 -0
  97. package/src/domain/editable-artifacts/snapshot-verifier.ts +687 -0
  98. package/src/domain/editable-artifacts/types.ts +835 -0
  99. package/src/domain/fiken.ts +88 -0
  100. package/src/domain/insights.ts +163 -50
  101. package/src/domain/memory-slack-delivery.ts +204 -0
  102. package/src/domain/memory-slack-publication.ts +5 -2
  103. package/src/domain/packs.ts +266 -5
  104. package/src/domain/personal-connection-delegations.ts +97 -6
  105. package/src/domain/scheduled-tasks.ts +171 -29
  106. package/src/domain/session-tool-policy.ts +8 -2
  107. package/src/domain/sessions.ts +395 -148
  108. package/src/domain/skill-imports.ts +350 -0
  109. package/src/domain/slack-publication-secret-safety.ts +42 -0
  110. package/src/domain/timeline-annotations.ts +204 -0
  111. package/src/domain/video-generation-capabilities.ts +104 -0
  112. package/src/domain/video-generation.ts +249 -0
  113. package/src/editable-artifact-live/application.ts +182 -0
  114. package/src/editable-artifact-live/errors.ts +4 -0
  115. package/src/editable-artifact-live/index.ts +8 -0
  116. package/src/editable-artifact-live/outbox-dispatcher.ts +916 -0
  117. package/src/editable-artifact-live/ports.ts +139 -0
  118. package/src/editable-artifact-live/server.ts +1707 -0
  119. package/src/editable-artifact-live/ticket.ts +217 -0
  120. package/src/editable-artifact-live/types.ts +286 -0
  121. package/src/editable-artifact-live/wire.ts +95 -0
  122. package/src/editable-artifact-live.ts +2 -0
  123. package/src/editable-artifacts.ts +2 -0
  124. package/src/index.ts +15 -0
  125. package/src/managed-session.ts +19 -2
  126. package/src/rigs/index.ts +26 -19
  127. package/src/rigs/provider-images.ts +110 -0
  128. package/src/sandbox/fleet.ts +72 -66
  129. package/src/sandbox/routing.ts +6 -3
  130. package/src/session-authorization.ts +187 -52
  131. package/src/transcription.ts +5 -0
  132. package/src/workflow-wake-contract.ts +8 -0
@@ -0,0 +1,687 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import {
4
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
5
+ EDITABLE_ARTIFACT_MODEL_SCHEMA_VERSION,
6
+ } from "@opengeni/contracts/editable-artifact-versions";
7
+ import {
8
+ DOCUMENT_ARTIFACT_COMMAND_VERSION,
9
+ EDITABLE_ARTIFACT_INTENT_VERSION,
10
+ PRESENTATION_ARTIFACT_COMMAND_VERSION,
11
+ SPREADSHEET_ARTIFACT_COMMAND_VERSION,
12
+ decodeEditableArtifactMutationIntent,
13
+ decodeDocumentArtifactQueryResponse,
14
+ decodePresentationArtifactQueryResponse,
15
+ decodeSpreadsheetArtifactKernelProjection,
16
+ encodeDocumentArtifactCommandBatch,
17
+ encodeDocumentArtifactQuery,
18
+ encodePresentationArtifactCommandBatch,
19
+ encodePresentationArtifactQuery,
20
+ encodeSpreadsheetArtifactCommandBatch,
21
+ encodeSpreadsheetArtifactKernelQuery,
22
+ hashEditableArtifactMutationIntent,
23
+ type DocumentArtifactCommand,
24
+ type DocumentArtifactQuery,
25
+ type PresentationArtifactCommand,
26
+ type PresentationArtifactQuery,
27
+ type SpreadsheetArtifactCommand,
28
+ type SpreadsheetArtifactKernelQuery,
29
+ } from "@opengeni/contracts/editable-artifacts";
30
+
31
+ import type {
32
+ EditableArtifactDurableExportService,
33
+ EditableArtifactMaterializationFormat,
34
+ EditableArtifactMaterializationJob,
35
+ } from "./durable-export";
36
+ import {
37
+ EditableArtifactDomainError,
38
+ EditableArtifactIdempotencyConflictError,
39
+ EditableArtifactNotFoundError,
40
+ EditableArtifactStaleBaseError,
41
+ } from "./errors";
42
+ import type { EditableArtifactService } from "./service";
43
+ import {
44
+ editableArtifactClientTransactionId,
45
+ editableArtifactId,
46
+ editableArtifactRequestHash,
47
+ editableArtifactScope,
48
+ editableArtifactStateHash,
49
+ validateEditableArtifactActor,
50
+ type EditableArtifact,
51
+ type EditableArtifactActor,
52
+ type EditableArtifactClientTransactionId,
53
+ type EditableArtifactId,
54
+ type EditableArtifactModality,
55
+ type EditableArtifactOriginalImport,
56
+ type EditableArtifactScope,
57
+ type EditableArtifactReceipt,
58
+ type ImportEditableArtifactRequest,
59
+ type EditableArtifactStateHash,
60
+ } from "./types";
61
+ import type { EditableArtifactKernelState } from "./ports";
62
+
63
+ export type EditableArtifactAgentCommandBatch =
64
+ | Readonly<{ modality: "spreadsheet"; commands: readonly SpreadsheetArtifactCommand[] }>
65
+ | Readonly<{ modality: "document"; commands: readonly DocumentArtifactCommand[] }>
66
+ | Readonly<{ modality: "presentation"; commands: readonly PresentationArtifactCommand[] }>;
67
+
68
+ export type EditableArtifactAgentQuery =
69
+ | Readonly<{ modality: "spreadsheet"; query: SpreadsheetArtifactKernelQuery }>
70
+ | Readonly<{ modality: "document"; query: DocumentArtifactQuery }>
71
+ | Readonly<{ modality: "presentation"; query: PresentationArtifactQuery }>;
72
+
73
+ export type EditableArtifactAgentContext = Readonly<{
74
+ scope: EditableArtifactScope;
75
+ actor: EditableArtifactActor;
76
+ sessionId: string;
77
+ }>;
78
+
79
+ export type EditableArtifactAgentMetadata = Readonly<{
80
+ id: string;
81
+ modality: EditableArtifactModality;
82
+ title: string;
83
+ lifecycle: EditableArtifact["lifecycle"];
84
+ headSequence: number;
85
+ stateHash: string;
86
+ createdAt: string;
87
+ updatedAt: string;
88
+ }>;
89
+
90
+ export type EditableArtifactAgentMutationReceipt = Readonly<{
91
+ artifact: EditableArtifactAgentMetadata;
92
+ transaction: Readonly<{
93
+ id: string;
94
+ clientTransactionId: string;
95
+ sequenceStart: number;
96
+ sequenceEnd: number;
97
+ stateHash: string;
98
+ committedAt: string;
99
+ replayed: boolean;
100
+ }>;
101
+ }>;
102
+
103
+ export type EditableArtifactAgentExportReceipt = Readonly<{
104
+ fileId: string;
105
+ filename: string;
106
+ contentType: string;
107
+ sizeBytes: number;
108
+ sha256: string;
109
+ artifactId: string;
110
+ versionId: string;
111
+ materializationJobId: string;
112
+ sourceHeadSequence: number;
113
+ sourceStateHash: string;
114
+ }>;
115
+
116
+ export type PreparedEditableArtifactImport = Readonly<{
117
+ originalImport: EditableArtifactOriginalImport;
118
+ snapshot: ImportEditableArtifactRequest["snapshot"];
119
+ }>;
120
+
121
+ export type EditableArtifactOfficeImportErrorCode =
122
+ | "invalid_source"
123
+ | "source_changed"
124
+ | "unsupported_content";
125
+
126
+ export class EditableArtifactOfficeImportError extends Error {
127
+ constructor(readonly code: EditableArtifactOfficeImportErrorCode) {
128
+ super(code);
129
+ this.name = "EditableArtifactOfficeImportError";
130
+ }
131
+ }
132
+
133
+ export interface EditableArtifactAgentAssociationPort {
134
+ listArtifactIds(input: {
135
+ scope: EditableArtifactScope;
136
+ sessionId: string;
137
+ limit: number;
138
+ }): Promise<readonly EditableArtifactId[]>;
139
+ touch(input: {
140
+ scope: EditableArtifactScope;
141
+ sessionId: string;
142
+ artifactId: EditableArtifactId;
143
+ }): Promise<void>;
144
+ }
145
+
146
+ export interface EditableArtifactAgentInspectionKernelPort {
147
+ query(input: { state: EditableArtifactKernelState; queryBytes: Uint8Array }): Promise<Uint8Array>;
148
+ }
149
+
150
+ export interface EditableArtifactOfficeImportPort {
151
+ prepare(input: {
152
+ scope: EditableArtifactScope;
153
+ actor: EditableArtifactActor;
154
+ fileId: string;
155
+ modality: EditableArtifactModality;
156
+ signal?: AbortSignal;
157
+ }): Promise<PreparedEditableArtifactImport>;
158
+ }
159
+
160
+ export interface EditableArtifactAgentWorkspaceFilePort {
161
+ ensureMaterializationFile(input: {
162
+ scope: EditableArtifactScope;
163
+ actor: EditableArtifactActor;
164
+ artifact: EditableArtifact;
165
+ versionId: string;
166
+ jobId: string;
167
+ filename: string;
168
+ sourceHeadSequence: number;
169
+ sourceStateHash: string;
170
+ signal?: AbortSignal;
171
+ }): Promise<EditableArtifactAgentExportReceipt>;
172
+ }
173
+
174
+ export type EditableArtifactAgentApplicationDependencies = Readonly<{
175
+ domain: EditableArtifactService;
176
+ exports: EditableArtifactDurableExportService;
177
+ associations: EditableArtifactAgentAssociationPort;
178
+ inspector: EditableArtifactAgentInspectionKernelPort;
179
+ officeImports: EditableArtifactOfficeImportPort;
180
+ workspaceFiles: EditableArtifactAgentWorkspaceFilePort;
181
+ }>;
182
+
183
+ /**
184
+ * Attempt-neutral use cases shared by direct model tools and Codemode. This is
185
+ * not another artifact engine: every mutation terminates at EditableArtifactService.
186
+ */
187
+ export class EditableArtifactAgentApplication {
188
+ constructor(private readonly dependencies: EditableArtifactAgentApplicationDependencies) {}
189
+
190
+ async list(
191
+ input: EditableArtifactAgentContext & Readonly<{ limit?: number }>,
192
+ ): Promise<readonly EditableArtifactAgentMetadata[]> {
193
+ const context = agentContext(input);
194
+ const limit = boundedLimit(input.limit ?? 64);
195
+ const ids = await this.dependencies.associations.listArtifactIds({
196
+ scope: context.scope,
197
+ sessionId: context.sessionId,
198
+ limit,
199
+ });
200
+ const artifacts: EditableArtifactAgentMetadata[] = [];
201
+ for (const artifactId of ids) {
202
+ try {
203
+ const artifact = await this.dependencies.domain.getArtifact({
204
+ scope: context.scope,
205
+ actor: context.actor,
206
+ artifactId,
207
+ });
208
+ artifacts.push(projectArtifact(artifact));
209
+ } catch (error) {
210
+ if (
211
+ error instanceof EditableArtifactNotFoundError ||
212
+ (error instanceof EditableArtifactDomainError && error.code === "forbidden")
213
+ ) {
214
+ continue;
215
+ }
216
+ throw error;
217
+ }
218
+ }
219
+ return Object.freeze(artifacts);
220
+ }
221
+
222
+ async create(
223
+ input: EditableArtifactAgentContext &
224
+ Readonly<{
225
+ idempotencyKey: EditableArtifactClientTransactionId;
226
+ modality: EditableArtifactModality;
227
+ title: string;
228
+ signal?: AbortSignal;
229
+ }>,
230
+ ): Promise<EditableArtifactAgentMetadata> {
231
+ const context = agentContext(input);
232
+ const result = await this.dependencies.domain.createArtifact({
233
+ scope: context.scope,
234
+ actor: context.actor,
235
+ request: {
236
+ idempotencyKey: editableArtifactClientTransactionId(input.idempotencyKey),
237
+ modality: input.modality,
238
+ title: input.title,
239
+ },
240
+ ...(input.signal ? { signal: input.signal } : {}),
241
+ });
242
+ await this.touch(context, result.artifact.id);
243
+ return projectArtifact(result.artifact);
244
+ }
245
+
246
+ async import(
247
+ input: EditableArtifactAgentContext &
248
+ Readonly<{
249
+ idempotencyKey: EditableArtifactClientTransactionId;
250
+ fileId: string;
251
+ modality: EditableArtifactModality;
252
+ title: string;
253
+ signal?: AbortSignal;
254
+ }>,
255
+ ): Promise<EditableArtifactAgentMetadata> {
256
+ const context = agentContext(input);
257
+ const prepared = await this.dependencies.officeImports.prepare({
258
+ scope: context.scope,
259
+ actor: context.actor,
260
+ fileId: input.fileId,
261
+ modality: input.modality,
262
+ ...(input.signal ? { signal: input.signal } : {}),
263
+ });
264
+ const result = await this.dependencies.domain.importArtifact({
265
+ scope: context.scope,
266
+ actor: context.actor,
267
+ request: {
268
+ idempotencyKey: editableArtifactClientTransactionId(input.idempotencyKey),
269
+ modality: input.modality,
270
+ title: input.title,
271
+ originalImport: prepared.originalImport,
272
+ snapshot: prepared.snapshot,
273
+ },
274
+ ...(input.signal ? { signal: input.signal } : {}),
275
+ });
276
+ await this.touch(context, result.artifact.id);
277
+ return projectArtifact(result.artifact);
278
+ }
279
+
280
+ async get(
281
+ input: EditableArtifactAgentContext & Readonly<{ artifactId: EditableArtifactId }>,
282
+ ): Promise<EditableArtifactAgentMetadata> {
283
+ const context = agentContext(input);
284
+ const artifact = await this.dependencies.domain.getArtifact({
285
+ scope: context.scope,
286
+ actor: context.actor,
287
+ artifactId: editableArtifactId(input.artifactId),
288
+ });
289
+ await this.touch(context, artifact.id);
290
+ return projectArtifact(artifact);
291
+ }
292
+
293
+ async inspect(
294
+ input: EditableArtifactAgentContext &
295
+ Readonly<{ artifactId: EditableArtifactId; request: EditableArtifactAgentQuery }>,
296
+ ): Promise<Readonly<{ artifact: EditableArtifactAgentMetadata; projection: unknown }>> {
297
+ const context = agentContext(input);
298
+ const artifactId = editableArtifactId(input.artifactId);
299
+ const state = await this.dependencies.domain.readCurrentKernelState({
300
+ scope: context.scope,
301
+ actor: context.actor,
302
+ artifactId,
303
+ });
304
+ if (state.modality !== input.request.modality)
305
+ throw new TypeError("Artifact modality mismatch");
306
+ const queryBytes = encodeQuery(input.request);
307
+ const responseBytes = await this.dependencies.inspector.query({ state, queryBytes });
308
+ const projection = decodeQueryResponse(input.request.modality, responseBytes);
309
+ await this.touch(context, artifactId);
310
+ return Object.freeze({ artifact: projectArtifact(state.artifact), projection });
311
+ }
312
+
313
+ async apply(
314
+ input: EditableArtifactAgentContext &
315
+ Readonly<{
316
+ artifactId: EditableArtifactId;
317
+ clientTransactionId: EditableArtifactClientTransactionId;
318
+ expectedHeadSequence: number;
319
+ expectedStateHash: string;
320
+ batch: EditableArtifactAgentCommandBatch;
321
+ }>,
322
+ ): Promise<EditableArtifactAgentMutationReceipt> {
323
+ const context = agentContext(input);
324
+ const artifactId = editableArtifactId(input.artifactId);
325
+ const commandBytes = encodeCommands(input.batch);
326
+ const clientTransactionId = editableArtifactClientTransactionId(input.clientTransactionId);
327
+ const expectedHeadSequence = boundedHeadSequence(input.expectedHeadSequence);
328
+ const expectedStateHash = editableArtifactStateHash(input.expectedStateHash);
329
+ const artifact = await this.dependencies.domain.getArtifact({
330
+ scope: context.scope,
331
+ actor: context.actor,
332
+ artifactId,
333
+ });
334
+ if (artifact.modality !== input.batch.modality)
335
+ throw new TypeError("Artifact modality mismatch");
336
+ if (
337
+ artifact.headSequence !== expectedHeadSequence ||
338
+ artifact.stateHash !== expectedStateHash
339
+ ) {
340
+ const existing = await this.dependencies.domain.findTransactionReceipt({
341
+ scope: context.scope,
342
+ actor: context.actor,
343
+ artifactId,
344
+ clientTransactionId,
345
+ });
346
+ if (existing) {
347
+ if (
348
+ !receiptMatchesAgentMutation(
349
+ existing,
350
+ expectedHeadSequence,
351
+ expectedStateHash,
352
+ commandBytes,
353
+ )
354
+ ) {
355
+ throw new EditableArtifactIdempotencyConflictError();
356
+ }
357
+ await this.touch(context, artifactId);
358
+ return Object.freeze({
359
+ artifact: projectArtifact(artifact),
360
+ transaction: projectReceipt(existing, true),
361
+ });
362
+ }
363
+ throw new EditableArtifactStaleBaseError();
364
+ }
365
+ const authored = hashEditableArtifactMutationIntent({
366
+ envelopeVersion: EDITABLE_ARTIFACT_INTENT_VERSION,
367
+ protocolVersion: COMMITTED_TRANSACTION_PROTOCOL_VERSION,
368
+ modelSchemaVersion: EDITABLE_ARTIFACT_MODEL_SCHEMA_VERSION,
369
+ commandProtocolVersion: commandProtocolVersion(input.batch.modality),
370
+ artifactId,
371
+ clientTransactionId,
372
+ replicaId: context.actor.replicaId,
373
+ replicaCounter: 1,
374
+ previousLocalTransactionId: null,
375
+ observedHeadSequence: expectedHeadSequence,
376
+ causalBase: artifact.modality === "spreadsheet" ? artifact.causalFrontier : [],
377
+ selectiveUndoOperationIds: [],
378
+ commandBytes,
379
+ });
380
+ let applied: Awaited<ReturnType<EditableArtifactService["applyTransaction"]>>;
381
+ try {
382
+ applied = await this.dependencies.domain.applyTransaction({
383
+ scope: context.scope,
384
+ actor: context.actor,
385
+ artifactId,
386
+ request: {
387
+ intentBytes: authored.bytes,
388
+ requestHash: editableArtifactRequestHash(authored.requestHash),
389
+ expectedHead: Object.freeze({
390
+ sequence: expectedHeadSequence,
391
+ stateHash: expectedStateHash,
392
+ }),
393
+ },
394
+ });
395
+ } catch (error) {
396
+ if (!(error instanceof EditableArtifactIdempotencyConflictError)) throw error;
397
+ const receipt = await this.dependencies.domain.findTransactionReceipt({
398
+ scope: context.scope,
399
+ actor: context.actor,
400
+ artifactId,
401
+ clientTransactionId,
402
+ });
403
+ if (
404
+ !receipt ||
405
+ !receiptMatchesAgentMutation(receipt, expectedHeadSequence, expectedStateHash, commandBytes)
406
+ ) {
407
+ throw error;
408
+ }
409
+ applied = Object.freeze({ receipt, replayed: true });
410
+ }
411
+ const current = await this.dependencies.domain.getArtifact({
412
+ scope: context.scope,
413
+ actor: context.actor,
414
+ artifactId,
415
+ });
416
+ await this.touch(context, artifactId);
417
+ return Object.freeze({
418
+ artifact: projectArtifact(current),
419
+ transaction: projectReceipt(applied.receipt, applied.replayed),
420
+ });
421
+ }
422
+
423
+ async startExport(
424
+ input: EditableArtifactAgentContext &
425
+ Readonly<{
426
+ artifactId: EditableArtifactId;
427
+ idempotencyKey: string;
428
+ format: EditableArtifactMaterializationFormat;
429
+ options?: Readonly<Record<string, unknown>>;
430
+ signal?: AbortSignal;
431
+ }>,
432
+ ): Promise<
433
+ Readonly<{
434
+ artifact: EditableArtifactAgentMetadata;
435
+ versionId: string;
436
+ jobId: string;
437
+ sourceHeadSequence: number;
438
+ sourceStateHash: string;
439
+ state: EditableArtifactMaterializationJob["state"];
440
+ }>
441
+ > {
442
+ const context = agentContext(input);
443
+ const artifactId = editableArtifactId(input.artifactId);
444
+ const artifact = await this.dependencies.domain.getArtifact({
445
+ scope: context.scope,
446
+ actor: context.actor,
447
+ artifactId,
448
+ });
449
+ const version = await this.dependencies.exports.pinVersion({
450
+ scope: context.scope,
451
+ actor: context.actor,
452
+ artifactId,
453
+ idempotencyKey: exportChildIdempotencyKey(input.idempotencyKey, "version"),
454
+ name: `Export ${artifact.title}`,
455
+ ...(input.signal ? { signal: input.signal } : {}),
456
+ });
457
+ const materialization = await this.dependencies.exports.enqueueMaterialization({
458
+ scope: context.scope,
459
+ actor: context.actor,
460
+ artifactId,
461
+ idempotencyKey: exportChildIdempotencyKey(input.idempotencyKey, "materialization"),
462
+ versionId: version.version.id,
463
+ format: input.format,
464
+ ...(input.options ? { options: input.options } : {}),
465
+ });
466
+ const current = await this.dependencies.domain.getArtifact({
467
+ scope: context.scope,
468
+ actor: context.actor,
469
+ artifactId,
470
+ });
471
+ await this.touch(context, artifactId);
472
+ return Object.freeze({
473
+ artifact: projectArtifact(current),
474
+ versionId: version.version.id,
475
+ jobId: materialization.job.id,
476
+ sourceHeadSequence: materialization.job.targetHeadSequence,
477
+ sourceStateHash: materialization.job.stateHash,
478
+ state: materialization.job.state,
479
+ });
480
+ }
481
+
482
+ async exportStatus(
483
+ input: EditableArtifactAgentContext &
484
+ Readonly<{
485
+ artifactId: EditableArtifactId;
486
+ versionId: string;
487
+ jobId: string;
488
+ signal?: AbortSignal;
489
+ }>,
490
+ ): Promise<
491
+ Readonly<{
492
+ artifact: EditableArtifactAgentMetadata;
493
+ versionId: string;
494
+ jobId: string;
495
+ sourceHeadSequence: number;
496
+ sourceStateHash: string;
497
+ state: EditableArtifactMaterializationJob["state"];
498
+ errorCode: string | null;
499
+ file: EditableArtifactAgentExportReceipt | null;
500
+ }>
501
+ > {
502
+ const context = agentContext(input);
503
+ const artifactId = editableArtifactId(input.artifactId);
504
+ const artifact = await this.dependencies.domain.getArtifact({
505
+ scope: context.scope,
506
+ actor: context.actor,
507
+ artifactId,
508
+ });
509
+ const job = await this.dependencies.exports.getMaterialization({
510
+ scope: context.scope,
511
+ actor: context.actor,
512
+ artifactId,
513
+ jobId: input.jobId,
514
+ });
515
+ if (job.versionId !== input.versionId) throw new TypeError("Export version mismatch");
516
+ const file =
517
+ job.state === "succeeded" && job.result
518
+ ? await this.dependencies.workspaceFiles.ensureMaterializationFile({
519
+ scope: context.scope,
520
+ actor: context.actor,
521
+ artifact,
522
+ versionId: job.versionId,
523
+ jobId: job.id,
524
+ filename: defaultExportFilename(artifact.title, job.format),
525
+ sourceHeadSequence: job.targetHeadSequence,
526
+ sourceStateHash: job.stateHash,
527
+ ...(input.signal ? { signal: input.signal } : {}),
528
+ })
529
+ : null;
530
+ await this.touch(context, artifactId);
531
+ return Object.freeze({
532
+ artifact: projectArtifact(artifact),
533
+ versionId: job.versionId,
534
+ jobId: job.id,
535
+ sourceHeadSequence: job.targetHeadSequence,
536
+ sourceStateHash: job.stateHash,
537
+ state: job.state,
538
+ errorCode: job.errorCode,
539
+ file,
540
+ });
541
+ }
542
+
543
+ private async touch(context: EditableArtifactAgentContext, artifactId: EditableArtifactId) {
544
+ await this.dependencies.associations.touch({
545
+ scope: context.scope,
546
+ sessionId: context.sessionId,
547
+ artifactId,
548
+ });
549
+ }
550
+ }
551
+
552
+ function agentContext(input: EditableArtifactAgentContext): EditableArtifactAgentContext {
553
+ const scope = editableArtifactScope(input.scope);
554
+ validateEditableArtifactActor(input.actor);
555
+ if (input.actor.kind !== "agent" || input.actor.sessionId !== input.sessionId) {
556
+ throw new TypeError("Artifact agent context is not bound to its attempt session");
557
+ }
558
+ return Object.freeze({
559
+ scope,
560
+ actor: Object.freeze({ ...input.actor }) as EditableArtifactActor,
561
+ sessionId: boundedSessionId(input.sessionId),
562
+ });
563
+ }
564
+
565
+ function encodeCommands(batch: EditableArtifactAgentCommandBatch): Uint8Array {
566
+ if (batch.modality === "spreadsheet") {
567
+ return encodeSpreadsheetArtifactCommandBatch({ version: 1, commands: batch.commands });
568
+ }
569
+ if (batch.modality === "document") {
570
+ return encodeDocumentArtifactCommandBatch({ version: 1, commands: batch.commands });
571
+ }
572
+ return encodePresentationArtifactCommandBatch({ version: 1, commands: batch.commands });
573
+ }
574
+
575
+ function encodeQuery(request: EditableArtifactAgentQuery): Uint8Array {
576
+ if (request.modality === "spreadsheet") {
577
+ return encodeSpreadsheetArtifactKernelQuery(request.query);
578
+ }
579
+ if (request.modality === "document") return encodeDocumentArtifactQuery(request.query);
580
+ return encodePresentationArtifactQuery(request.query);
581
+ }
582
+
583
+ function decodeQueryResponse(modality: EditableArtifactModality, bytes: Uint8Array): unknown {
584
+ if (modality === "spreadsheet") return decodeSpreadsheetArtifactKernelProjection(bytes);
585
+ if (modality === "document") return decodeDocumentArtifactQueryResponse(bytes);
586
+ return decodePresentationArtifactQueryResponse(bytes);
587
+ }
588
+
589
+ function commandProtocolVersion(modality: EditableArtifactModality): number {
590
+ if (modality === "spreadsheet") return SPREADSHEET_ARTIFACT_COMMAND_VERSION;
591
+ if (modality === "document") return DOCUMENT_ARTIFACT_COMMAND_VERSION;
592
+ return PRESENTATION_ARTIFACT_COMMAND_VERSION;
593
+ }
594
+
595
+ function projectArtifact(artifact: EditableArtifact): EditableArtifactAgentMetadata {
596
+ return Object.freeze({
597
+ id: artifact.id,
598
+ modality: artifact.modality,
599
+ title: artifact.title,
600
+ lifecycle: artifact.lifecycle,
601
+ headSequence: artifact.headSequence,
602
+ stateHash: artifact.stateHash,
603
+ createdAt: artifact.createdAt,
604
+ updatedAt: artifact.updatedAt,
605
+ });
606
+ }
607
+
608
+ function projectReceipt(
609
+ receipt: EditableArtifactReceipt,
610
+ replayed: boolean,
611
+ ): EditableArtifactAgentMutationReceipt["transaction"] {
612
+ return Object.freeze({
613
+ id: receipt.serverTransactionId,
614
+ clientTransactionId: receipt.clientTransactionId,
615
+ sequenceStart: receipt.sequenceStart,
616
+ sequenceEnd: receipt.sequenceEnd,
617
+ stateHash: receipt.stateHash,
618
+ committedAt: receipt.committedAt,
619
+ replayed,
620
+ });
621
+ }
622
+
623
+ function receiptMatchesAgentMutation(
624
+ receipt: EditableArtifactReceipt,
625
+ expectedHeadSequence: number,
626
+ expectedStateHash: EditableArtifactStateHash,
627
+ commandBytes: Uint8Array,
628
+ ): boolean {
629
+ const intent = decodeEditableArtifactMutationIntent(receipt.intentBytes);
630
+ return (
631
+ intent.observedHeadSequence === expectedHeadSequence &&
632
+ receipt.priorStateHash === expectedStateHash &&
633
+ sameBytes(commandBytes, intent.commandBytes)
634
+ );
635
+ }
636
+
637
+ function sameBytes(left: Uint8Array, right: Uint8Array): boolean {
638
+ if (left.byteLength !== right.byteLength) return false;
639
+ for (let index = 0; index < left.byteLength; index += 1) {
640
+ if (left[index] !== right[index]) return false;
641
+ }
642
+ return true;
643
+ }
644
+
645
+ function boundedSessionId(value: string): string {
646
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(value)) {
647
+ throw new TypeError("Artifact session id is invalid");
648
+ }
649
+ return value;
650
+ }
651
+
652
+ function boundedLimit(value: number): number {
653
+ if (!Number.isSafeInteger(value) || value < 1 || value > 64) {
654
+ throw new TypeError("Artifact list limit must be between 1 and 64");
655
+ }
656
+ return value;
657
+ }
658
+
659
+ function boundedHeadSequence(value: number): number {
660
+ if (!Number.isSafeInteger(value) || value < 0) {
661
+ throw new TypeError("Artifact expected head sequence must be a nonnegative safe integer");
662
+ }
663
+ return value;
664
+ }
665
+
666
+ function defaultExportFilename(title: string, format: string): string {
667
+ const stem = title
668
+ .normalize("NFKC")
669
+ .replace(/[\\/:*?"<>|\u0000-\u001f\u007f]+/gu, "-")
670
+ .replace(/\s+/gu, " ")
671
+ .trim()
672
+ .slice(0, 180);
673
+ return `${stem || "artifact"}.${format}`;
674
+ }
675
+
676
+ function exportChildIdempotencyKey(
677
+ value: string,
678
+ operation: "version" | "materialization",
679
+ ): string {
680
+ const digest = createHash("sha256")
681
+ .update("opengeni:editable-artifact-export:v1\0", "utf8")
682
+ .update(operation, "utf8")
683
+ .update("\0", "utf8")
684
+ .update(value, "utf8")
685
+ .digest("hex");
686
+ return `artifact-export:${operation}:${digest}`;
687
+ }