@zhivex-ai/core 0.7.0 → 0.8.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 (77) hide show
  1. package/README.md +5 -0
  2. package/dist/advanced-tool-registry.d.ts +112 -0
  3. package/dist/advanced-tool-registry.d.ts.map +1 -0
  4. package/dist/advanced-tool-registry.js +407 -0
  5. package/dist/advanced-tool-registry.js.map +1 -0
  6. package/dist/agent-evaluation.d.ts +176 -0
  7. package/dist/agent-evaluation.d.ts.map +1 -0
  8. package/dist/agent-evaluation.js +334 -0
  9. package/dist/agent-evaluation.js.map +1 -0
  10. package/dist/agent-store.d.ts.map +1 -1
  11. package/dist/agent-store.js +226 -8
  12. package/dist/agent-store.js.map +1 -1
  13. package/dist/agent-trace.d.ts +127 -0
  14. package/dist/agent-trace.d.ts.map +1 -0
  15. package/dist/agent-trace.js +331 -0
  16. package/dist/agent-trace.js.map +1 -0
  17. package/dist/agent.d.ts +12 -1
  18. package/dist/agent.d.ts.map +1 -1
  19. package/dist/agent.js +469 -36
  20. package/dist/agent.js.map +1 -1
  21. package/dist/api-stability.d.ts +9 -0
  22. package/dist/api-stability.d.ts.map +1 -0
  23. package/dist/api-stability.js +261 -0
  24. package/dist/api-stability.js.map +1 -0
  25. package/dist/artifact.d.ts +165 -0
  26. package/dist/artifact.d.ts.map +1 -0
  27. package/dist/artifact.js +994 -0
  28. package/dist/artifact.js.map +1 -0
  29. package/dist/errors.d.ts +2 -0
  30. package/dist/errors.d.ts.map +1 -1
  31. package/dist/errors.js +2 -0
  32. package/dist/errors.js.map +1 -1
  33. package/dist/generate-text.d.ts.map +1 -1
  34. package/dist/generate-text.js +6 -2
  35. package/dist/generate-text.js.map +1 -1
  36. package/dist/index.d.ts +27 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +14 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/live-agent.d.ts.map +1 -1
  41. package/dist/live-agent.js +7 -1
  42. package/dist/live-agent.js.map +1 -1
  43. package/dist/provider-parity.d.ts +63 -0
  44. package/dist/provider-parity.d.ts.map +1 -0
  45. package/dist/provider-parity.js +175 -0
  46. package/dist/provider-parity.js.map +1 -0
  47. package/dist/runner.d.ts +111 -0
  48. package/dist/runner.d.ts.map +1 -0
  49. package/dist/runner.js +635 -0
  50. package/dist/runner.js.map +1 -0
  51. package/dist/safety-policy.d.ts +65 -0
  52. package/dist/safety-policy.d.ts.map +1 -0
  53. package/dist/safety-policy.js +308 -0
  54. package/dist/safety-policy.js.map +1 -0
  55. package/dist/types.d.ts +119 -3
  56. package/dist/types.d.ts.map +1 -1
  57. package/dist/workflow-artifacts.d.ts +28 -0
  58. package/dist/workflow-artifacts.d.ts.map +1 -0
  59. package/dist/workflow-artifacts.js +86 -0
  60. package/dist/workflow-artifacts.js.map +1 -0
  61. package/dist/workflow-evaluation-diff.d.ts +51 -0
  62. package/dist/workflow-evaluation-diff.d.ts.map +1 -0
  63. package/dist/workflow-evaluation-diff.js +141 -0
  64. package/dist/workflow-evaluation-diff.js.map +1 -0
  65. package/dist/workflow-evaluation.d.ts +94 -0
  66. package/dist/workflow-evaluation.d.ts.map +1 -0
  67. package/dist/workflow-evaluation.js +210 -0
  68. package/dist/workflow-evaluation.js.map +1 -0
  69. package/dist/workflow-state-service.d.ts +67 -0
  70. package/dist/workflow-state-service.d.ts.map +1 -0
  71. package/dist/workflow-state-service.js +498 -0
  72. package/dist/workflow-state-service.js.map +1 -0
  73. package/dist/workflow.d.ts +206 -0
  74. package/dist/workflow.d.ts.map +1 -0
  75. package/dist/workflow.js +727 -0
  76. package/dist/workflow.js.map +1 -0
  77. package/package.json +1 -1
@@ -0,0 +1,994 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { createHash } from "node:crypto";
3
+ import path from "node:path";
4
+ import { ConflictError, ValidationError } from "./errors.js";
5
+ const randomId = (prefix) => `${prefix}_${Math.random().toString(36).slice(2, 10)}`;
6
+ const cloneJson = (value) => JSON.parse(JSON.stringify(value));
7
+ export const ARTIFACT_SCHEMA_VERSION = 1;
8
+ const artifactKey = (input) => `${input.appName}:${input.userId}:${input.sessionId}:${input.id}`;
9
+ const identifierPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
10
+ export const normalizeArtifactRecord = (value) => {
11
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
12
+ throw new ValidationError("ArtifactRecord must be an object.");
13
+ }
14
+ const artifact = value;
15
+ if (artifact.schemaVersion !== undefined && artifact.schemaVersion > ARTIFACT_SCHEMA_VERSION) {
16
+ throw new ValidationError(`Unsupported ArtifactRecord schemaVersion ${artifact.schemaVersion}.`);
17
+ }
18
+ if (typeof artifact.id !== "string" ||
19
+ typeof artifact.appName !== "string" ||
20
+ typeof artifact.userId !== "string" ||
21
+ typeof artifact.sessionId !== "string" ||
22
+ typeof artifact.name !== "string" ||
23
+ typeof artifact.contentType !== "string" ||
24
+ typeof artifact.createdAt !== "number" ||
25
+ typeof artifact.updatedAt !== "number" ||
26
+ !("data" in artifact)) {
27
+ throw new ValidationError("ArtifactRecord is missing required fields.");
28
+ }
29
+ return {
30
+ schemaVersion: ARTIFACT_SCHEMA_VERSION,
31
+ revision: typeof artifact.revision === "number" ? artifact.revision : 1,
32
+ id: artifact.id,
33
+ appName: artifact.appName,
34
+ userId: artifact.userId,
35
+ sessionId: artifact.sessionId,
36
+ workflowRunId: artifact.workflowRunId,
37
+ workflowStepId: artifact.workflowStepId,
38
+ agentRunId: artifact.agentRunId,
39
+ name: artifact.name,
40
+ contentType: artifact.contentType,
41
+ data: cloneJson(artifact.data),
42
+ encoding: artifact.encoding,
43
+ size: artifact.size,
44
+ sha256: artifact.sha256,
45
+ storageMode: artifact.storageMode ?? "json",
46
+ blobPath: artifact.blobPath,
47
+ metadata: artifact.metadata ? cloneJson(artifact.metadata) : undefined,
48
+ createdAt: artifact.createdAt,
49
+ updatedAt: artifact.updatedAt
50
+ };
51
+ };
52
+ export const migrateArtifactRecord = (value, targetVersion = ARTIFACT_SCHEMA_VERSION) => {
53
+ if (targetVersion !== ARTIFACT_SCHEMA_VERSION) {
54
+ throw new ValidationError(`Unsupported ArtifactRecord migration target ${targetVersion}.`);
55
+ }
56
+ return normalizeArtifactRecord(value);
57
+ };
58
+ const cloneArtifact = (artifact) => cloneJson(normalizeArtifactRecord(artifact));
59
+ const bytesFromBinaryInput = (data) => {
60
+ const buffer = typeof data === "string"
61
+ ? Buffer.from(data, "utf8")
62
+ : data instanceof Uint8Array
63
+ ? Buffer.from(data)
64
+ : Buffer.from(data);
65
+ return new Uint8Array(buffer);
66
+ };
67
+ const sha256Digest = (data) => createHash("sha256").update(data).digest("hex");
68
+ const assertExpectedRevision = (current, expectedRevision, resource) => {
69
+ if (expectedRevision !== undefined && (current?.revision ?? 0) !== expectedRevision) {
70
+ throw new ConflictError(`${resource} revision conflict.`);
71
+ }
72
+ };
73
+ const sqliteMutationCount = (result) => {
74
+ if (!result || typeof result !== "object") {
75
+ return undefined;
76
+ }
77
+ const record = result;
78
+ const value = record.changes ?? record.changeset ?? record.rowCount;
79
+ return typeof value === "number" ? value : undefined;
80
+ };
81
+ const validateArtifactMetadata = (input) => {
82
+ if (input.size !== undefined && (!Number.isInteger(input.size) || input.size < 0)) {
83
+ throw new ValidationError('The "size" artifact option must be a non-negative integer.');
84
+ }
85
+ if (input.sha256 !== undefined && !/^[a-f0-9]{64}$/i.test(input.sha256)) {
86
+ throw new ValidationError('The "sha256" artifact option must be a 64-character hexadecimal digest.');
87
+ }
88
+ };
89
+ const validateIdentifier = (value, fieldName) => {
90
+ if (!identifierPattern.test(value)) {
91
+ throw new ValidationError(`The "${fieldName}" option must match the SQL identifier pattern [A-Za-z_][A-Za-z0-9_]*.`);
92
+ }
93
+ return value;
94
+ };
95
+ const getRecordField = (value, candidates) => {
96
+ if (!value || typeof value !== "object") {
97
+ return undefined;
98
+ }
99
+ const record = value;
100
+ for (const candidate of candidates) {
101
+ if (candidate in record) {
102
+ return record[candidate];
103
+ }
104
+ }
105
+ return undefined;
106
+ };
107
+ const parseArtifactJson = (value) => {
108
+ if (!value) {
109
+ return undefined;
110
+ }
111
+ if (typeof value === "string") {
112
+ return normalizeArtifactRecord(JSON.parse(value));
113
+ }
114
+ return normalizeArtifactRecord(value);
115
+ };
116
+ const prepareSqliteStatement = (db, sql) => {
117
+ if (typeof db.prepare === "function") {
118
+ return db.prepare(sql);
119
+ }
120
+ if (typeof db.query === "function") {
121
+ return db.query(sql);
122
+ }
123
+ throw new ValidationError('The "db" option must expose either a "prepare()" or "query()" method.');
124
+ };
125
+ const ensurePostgresTable = (() => {
126
+ const initializedTables = new WeakMap();
127
+ return async (client, tableName, createSql) => {
128
+ let tables = initializedTables.get(client);
129
+ if (!tables) {
130
+ tables = new Map();
131
+ initializedTables.set(client, tables);
132
+ }
133
+ let initialization = tables.get(tableName);
134
+ if (!initialization) {
135
+ initialization = Promise.resolve(client.query(createSql, [])).then(() => undefined);
136
+ tables.set(tableName, initialization);
137
+ }
138
+ await initialization;
139
+ };
140
+ })();
141
+ const createArtifact = (input, existing) => {
142
+ validateArtifactMetadata(input);
143
+ const now = Date.now();
144
+ return {
145
+ schemaVersion: ARTIFACT_SCHEMA_VERSION,
146
+ revision: existing ? existing.revision + 1 : 1,
147
+ id: input.id ?? existing?.id ?? randomId("art"),
148
+ appName: input.appName,
149
+ userId: input.userId,
150
+ sessionId: input.sessionId,
151
+ workflowRunId: input.workflowRunId,
152
+ workflowStepId: input.workflowStepId,
153
+ agentRunId: input.agentRunId,
154
+ name: input.name,
155
+ contentType: input.contentType,
156
+ data: cloneJson(input.data),
157
+ encoding: input.encoding,
158
+ size: input.size,
159
+ sha256: input.sha256,
160
+ storageMode: input.storageMode ?? "json",
161
+ blobPath: input.blobPath,
162
+ metadata: input.metadata ? cloneJson(input.metadata) : undefined,
163
+ createdAt: existing?.createdAt ?? now,
164
+ updatedAt: now
165
+ };
166
+ };
167
+ const matchesListInput = (artifact, input) => artifact.appName === input.appName &&
168
+ artifact.userId === input.userId &&
169
+ artifact.sessionId === input.sessionId &&
170
+ (input.workflowRunId === undefined || artifact.workflowRunId === input.workflowRunId) &&
171
+ (input.workflowStepId === undefined || artifact.workflowStepId === input.workflowStepId) &&
172
+ (input.agentRunId === undefined || artifact.agentRunId === input.agentRunId);
173
+ const fileNameForArtifact = (input) => [input.appName, input.userId, input.sessionId, input.id].map((part) => encodeURIComponent(part)).join("__") + ".json";
174
+ const blobPathForArtifact = (input) => path.join("blobs", [input.appName, input.userId, input.sessionId, input.id].map((part) => encodeURIComponent(part)).join("__") + ".bin");
175
+ const lookupFromArtifact = (artifact) => ({
176
+ appName: artifact.appName,
177
+ userId: artifact.userId,
178
+ sessionId: artifact.sessionId,
179
+ id: artifact.id
180
+ });
181
+ export const createBase64ArtifactData = (input) => {
182
+ const data = typeof input === "object" && "data" in input ? input.data : input;
183
+ const buffer = typeof data === "string"
184
+ ? Buffer.from(data, "utf8")
185
+ : data instanceof Uint8Array
186
+ ? Buffer.from(data)
187
+ : Buffer.from(data);
188
+ return {
189
+ data: buffer.toString("base64"),
190
+ encoding: "base64",
191
+ size: buffer.byteLength
192
+ };
193
+ };
194
+ export const createExternalArtifactReference = (input) => {
195
+ validateArtifactMetadata(input);
196
+ if (!input.uri) {
197
+ throw new ValidationError('The "uri" external artifact reference option is required.');
198
+ }
199
+ return {
200
+ data: null,
201
+ storageMode: "binary",
202
+ size: input.size,
203
+ sha256: input.sha256,
204
+ metadata: {
205
+ ...(input.metadata ?? {}),
206
+ externalBlob: {
207
+ uri: input.uri,
208
+ managedBy: "application"
209
+ }
210
+ }
211
+ };
212
+ };
213
+ export const verifyArtifactRecordIntegrity = (record, data) => {
214
+ const artifact = normalizeArtifactRecord(record);
215
+ const issues = [];
216
+ let bytes = data;
217
+ if (!bytes && artifact.encoding === "base64" && typeof artifact.data === "string") {
218
+ try {
219
+ bytes = new Uint8Array(Buffer.from(artifact.data, "base64"));
220
+ }
221
+ catch {
222
+ issues.push({
223
+ type: "invalid-base64",
224
+ message: `Artifact "${artifact.id}" contains invalid base64 data.`
225
+ });
226
+ }
227
+ }
228
+ if (bytes) {
229
+ if (artifact.size !== undefined && artifact.size !== bytes.byteLength) {
230
+ issues.push({
231
+ type: "size-mismatch",
232
+ message: `Artifact "${artifact.id}" size does not match.`,
233
+ expected: artifact.size,
234
+ actual: bytes.byteLength
235
+ });
236
+ }
237
+ if (artifact.sha256 !== undefined) {
238
+ const actual = sha256Digest(bytes);
239
+ if (artifact.sha256 !== actual) {
240
+ issues.push({
241
+ type: "sha256-mismatch",
242
+ message: `Artifact "${artifact.id}" sha256 does not match.`,
243
+ expected: artifact.sha256,
244
+ actual
245
+ });
246
+ }
247
+ }
248
+ }
249
+ return {
250
+ ok: issues.length === 0,
251
+ artifact,
252
+ issues
253
+ };
254
+ };
255
+ export const verifyArtifactIntegrity = async (service, lookup) => {
256
+ const artifact = await service.loadArtifact(lookup);
257
+ if (!artifact) {
258
+ return {
259
+ ok: false,
260
+ issues: [{
261
+ type: "missing-artifact",
262
+ message: `Artifact "${lookup.id}" was not found.`
263
+ }]
264
+ };
265
+ }
266
+ if (artifact.storageMode === "binary") {
267
+ const binary = await service.loadBinaryArtifact(lookup);
268
+ if (!binary) {
269
+ return {
270
+ ok: false,
271
+ artifact,
272
+ issues: [{
273
+ type: "missing-blob",
274
+ message: `Artifact "${lookup.id}" binary blob was not found.`
275
+ }]
276
+ };
277
+ }
278
+ return verifyArtifactRecordIntegrity(binary.artifact, binary.data);
279
+ }
280
+ return verifyArtifactRecordIntegrity(artifact);
281
+ };
282
+ const listFilesRecursive = async (directory) => {
283
+ try {
284
+ const entries = await fs.readdir(directory, { withFileTypes: true });
285
+ const files = await Promise.all(entries.map(async (entry) => {
286
+ const fullPath = path.join(directory, entry.name);
287
+ if (entry.isDirectory()) {
288
+ return listFilesRecursive(fullPath);
289
+ }
290
+ return [fullPath];
291
+ }));
292
+ return files.flat();
293
+ }
294
+ catch (error) {
295
+ if (error.code === "ENOENT") {
296
+ return [];
297
+ }
298
+ throw error;
299
+ }
300
+ };
301
+ export const inspectFileArtifactStore = async (options) => {
302
+ const artifacts = [];
303
+ const issues = [];
304
+ const referencedBlobPaths = new Set();
305
+ let entries = [];
306
+ try {
307
+ entries = await fs.readdir(options.directory);
308
+ }
309
+ catch (error) {
310
+ if (error.code !== "ENOENT") {
311
+ throw error;
312
+ }
313
+ }
314
+ for (const entry of entries) {
315
+ if (!entry.endsWith(".json")) {
316
+ continue;
317
+ }
318
+ const metadataPath = path.join(options.directory, entry);
319
+ try {
320
+ const artifact = normalizeArtifactRecord(JSON.parse(await fs.readFile(metadataPath, "utf8")));
321
+ artifacts.push(artifact);
322
+ if (artifact.blobPath) {
323
+ referencedBlobPaths.add(path.normalize(artifact.blobPath));
324
+ }
325
+ if (artifact.storageMode === "binary") {
326
+ if (!artifact.blobPath) {
327
+ issues.push({
328
+ type: "missing-blob",
329
+ path: metadataPath,
330
+ artifact,
331
+ message: `Artifact "${artifact.id}" has no blobPath.`
332
+ });
333
+ }
334
+ else {
335
+ const fullBlobPath = path.join(options.directory, artifact.blobPath);
336
+ try {
337
+ await fs.stat(fullBlobPath);
338
+ }
339
+ catch (error) {
340
+ if (error.code === "ENOENT") {
341
+ issues.push({
342
+ type: "missing-blob",
343
+ path: fullBlobPath,
344
+ artifact,
345
+ message: `Artifact "${artifact.id}" references a missing blob.`
346
+ });
347
+ }
348
+ else {
349
+ throw error;
350
+ }
351
+ }
352
+ }
353
+ }
354
+ }
355
+ catch (error) {
356
+ issues.push({
357
+ type: "invalid-metadata",
358
+ path: metadataPath,
359
+ message: error instanceof Error ? error.message : String(error)
360
+ });
361
+ }
362
+ }
363
+ const blobRoot = path.join(options.directory, "blobs");
364
+ for (const blobFile of await listFilesRecursive(blobRoot)) {
365
+ const relative = path.normalize(path.relative(options.directory, blobFile));
366
+ if (!referencedBlobPaths.has(relative)) {
367
+ issues.push({
368
+ type: "orphan-blob",
369
+ path: blobFile,
370
+ message: `Blob "${relative}" is not referenced by artifact metadata.`
371
+ });
372
+ }
373
+ }
374
+ return {
375
+ directory: options.directory,
376
+ artifacts: artifacts.sort((left, right) => left.createdAt - right.createdAt || left.id.localeCompare(right.id)),
377
+ issues
378
+ };
379
+ };
380
+ export const cleanupFileArtifactStore = async (options) => {
381
+ const inspection = await inspectFileArtifactStore(options);
382
+ const deletedBlobPaths = [];
383
+ for (const issue of inspection.issues) {
384
+ if (issue.type !== "orphan-blob") {
385
+ continue;
386
+ }
387
+ if (!options.dryRun) {
388
+ await fs.unlink(issue.path);
389
+ }
390
+ deletedBlobPaths.push(issue.path);
391
+ }
392
+ return {
393
+ ...inspection,
394
+ dryRun: Boolean(options.dryRun),
395
+ deletedBlobPaths
396
+ };
397
+ };
398
+ export const pruneFileArtifactStore = async (options) => {
399
+ const now = options.now ?? Date.now();
400
+ const dryRun = options.dryRun ?? true;
401
+ const inspection = await inspectFileArtifactStore({ directory: options.directory });
402
+ const sorted = inspection.artifacts.sort((left, right) => right.updatedAt - left.updatedAt || artifactKey(left).localeCompare(artifactKey(right)));
403
+ const keepByCount = new Set(options.keepLast === undefined ? [] : sorted.slice(0, Math.max(0, options.keepLast)).map((artifact) => artifactKey(artifact)));
404
+ const shouldDelete = (artifact) => !keepByCount.has(artifactKey(artifact)) &&
405
+ (options.olderThanMs !== undefined ? now - artifact.updatedAt > options.olderThanMs : options.keepLast !== undefined);
406
+ const deleted = sorted.filter(shouldDelete);
407
+ const deletedBlobPaths = deleted.flatMap((artifact) => artifact.blobPath ? [artifact.blobPath] : []);
408
+ if (!dryRun) {
409
+ for (const artifact of deleted) {
410
+ await fs.unlink(path.join(options.directory, fileNameForArtifact(artifact))).catch((error) => {
411
+ if (error.code !== "ENOENT") {
412
+ throw error;
413
+ }
414
+ });
415
+ if (artifact.blobPath) {
416
+ await fs.unlink(path.join(options.directory, artifact.blobPath)).catch((error) => {
417
+ if (error.code !== "ENOENT") {
418
+ throw error;
419
+ }
420
+ });
421
+ }
422
+ }
423
+ }
424
+ return {
425
+ directory: options.directory,
426
+ dryRun,
427
+ deletedArtifactKeys: deleted.map((artifact) => artifactKey(artifact)),
428
+ keptArtifactKeys: sorted.filter((artifact) => !shouldDelete(artifact)).map((artifact) => artifactKey(artifact)),
429
+ deletedBlobPaths
430
+ };
431
+ };
432
+ export const createInMemoryArtifactService = () => {
433
+ const artifacts = new Map();
434
+ const binaryData = new Map();
435
+ return {
436
+ saveArtifact(input) {
437
+ const id = input.id ?? randomId("art");
438
+ const lookup = {
439
+ appName: input.appName,
440
+ userId: input.userId,
441
+ sessionId: input.sessionId,
442
+ id
443
+ };
444
+ const existing = artifacts.get(artifactKey(lookup));
445
+ assertExpectedRevision(existing, input.expectedRevision, "ArtifactRecord");
446
+ const artifact = createArtifact({ ...input, id }, existing);
447
+ artifacts.set(artifactKey(lookup), cloneArtifact(artifact));
448
+ binaryData.delete(artifactKey(lookup));
449
+ return cloneArtifact(artifact);
450
+ },
451
+ saveBinaryArtifact(input) {
452
+ const id = input.id ?? randomId("art");
453
+ const lookup = {
454
+ appName: input.appName,
455
+ userId: input.userId,
456
+ sessionId: input.sessionId,
457
+ id
458
+ };
459
+ const bytes = bytesFromBinaryInput(input.data);
460
+ const sha256 = input.sha256 ?? sha256Digest(bytes);
461
+ const existing = artifacts.get(artifactKey(lookup));
462
+ assertExpectedRevision(existing, input.expectedRevision, "ArtifactRecord");
463
+ const artifact = createArtifact({
464
+ ...input,
465
+ id,
466
+ data: null,
467
+ encoding: "base64",
468
+ size: bytes.byteLength,
469
+ sha256,
470
+ storageMode: "binary"
471
+ }, existing);
472
+ artifacts.set(artifactKey(lookup), cloneArtifact(artifact));
473
+ binaryData.set(artifactKey(lookup), new Uint8Array(bytes));
474
+ return cloneArtifact(artifact);
475
+ },
476
+ loadArtifact(input) {
477
+ const artifact = artifacts.get(artifactKey(input));
478
+ return artifact ? cloneArtifact(artifact) : undefined;
479
+ },
480
+ loadBinaryArtifact(input) {
481
+ const artifact = artifacts.get(artifactKey(input));
482
+ const data = binaryData.get(artifactKey(input));
483
+ if (!artifact || !data) {
484
+ return undefined;
485
+ }
486
+ return {
487
+ artifact: cloneArtifact(artifact),
488
+ data: new Uint8Array(data)
489
+ };
490
+ },
491
+ listArtifacts(input) {
492
+ return [...artifacts.values()]
493
+ .filter((artifact) => matchesListInput(artifact, input))
494
+ .sort((left, right) => left.createdAt - right.createdAt || left.id.localeCompare(right.id))
495
+ .map(cloneArtifact);
496
+ },
497
+ deleteArtifact(input) {
498
+ artifacts.delete(artifactKey(input));
499
+ binaryData.delete(artifactKey(input));
500
+ }
501
+ };
502
+ };
503
+ export const createFileArtifactService = (options) => {
504
+ const filePath = (input) => path.join(options.directory, fileNameForArtifact(input));
505
+ const binaryPath = (input) => path.join(options.directory, blobPathForArtifact(input));
506
+ const load = async (input) => {
507
+ try {
508
+ const content = await fs.readFile(filePath(input), "utf8");
509
+ return normalizeArtifactRecord(JSON.parse(content));
510
+ }
511
+ catch (error) {
512
+ if (error.code === "ENOENT") {
513
+ return undefined;
514
+ }
515
+ throw error;
516
+ }
517
+ };
518
+ const save = async (artifact) => {
519
+ await fs.mkdir(options.directory, { recursive: true });
520
+ await fs.writeFile(filePath(artifact), JSON.stringify(cloneArtifact(artifact), null, 2), "utf8");
521
+ };
522
+ return {
523
+ async saveArtifact(input) {
524
+ const id = input.id ?? randomId("art");
525
+ const lookup = {
526
+ appName: input.appName,
527
+ userId: input.userId,
528
+ sessionId: input.sessionId,
529
+ id
530
+ };
531
+ const existing = await load(lookup);
532
+ assertExpectedRevision(existing, input.expectedRevision, "ArtifactRecord");
533
+ const artifact = createArtifact({ ...input, id }, existing);
534
+ await save(artifact);
535
+ const existingBlob = binaryPath(lookup);
536
+ await fs.unlink(existingBlob).catch((error) => {
537
+ if (error.code !== "ENOENT") {
538
+ throw error;
539
+ }
540
+ });
541
+ return cloneArtifact(artifact);
542
+ },
543
+ async saveBinaryArtifact(input) {
544
+ const id = input.id ?? randomId("art");
545
+ const lookup = {
546
+ appName: input.appName,
547
+ userId: input.userId,
548
+ sessionId: input.sessionId,
549
+ id
550
+ };
551
+ const bytes = bytesFromBinaryInput(input.data);
552
+ const sha256 = input.sha256 ?? sha256Digest(bytes);
553
+ const blobPath = blobPathForArtifact(lookup);
554
+ const existing = await load(lookup);
555
+ assertExpectedRevision(existing, input.expectedRevision, "ArtifactRecord");
556
+ const artifact = createArtifact({
557
+ ...input,
558
+ id,
559
+ data: null,
560
+ encoding: "base64",
561
+ size: bytes.byteLength,
562
+ sha256,
563
+ storageMode: "binary",
564
+ blobPath
565
+ }, existing);
566
+ await fs.mkdir(path.dirname(binaryPath(lookup)), { recursive: true });
567
+ await fs.writeFile(binaryPath(lookup), bytes);
568
+ await save(artifact);
569
+ return cloneArtifact(artifact);
570
+ },
571
+ async loadArtifact(input) {
572
+ return load(input);
573
+ },
574
+ async loadBinaryArtifact(input) {
575
+ const artifact = await load(input);
576
+ if (!artifact || artifact.storageMode !== "binary" || !artifact.blobPath) {
577
+ return undefined;
578
+ }
579
+ try {
580
+ const data = await fs.readFile(path.join(options.directory, artifact.blobPath));
581
+ return {
582
+ artifact,
583
+ data: new Uint8Array(data)
584
+ };
585
+ }
586
+ catch (error) {
587
+ if (error.code === "ENOENT") {
588
+ return undefined;
589
+ }
590
+ throw error;
591
+ }
592
+ },
593
+ async listArtifacts(input) {
594
+ let entries;
595
+ try {
596
+ entries = await fs.readdir(options.directory);
597
+ }
598
+ catch (error) {
599
+ if (error.code === "ENOENT") {
600
+ return [];
601
+ }
602
+ throw error;
603
+ }
604
+ const artifacts = [];
605
+ for (const entry of entries) {
606
+ if (!entry.endsWith(".json")) {
607
+ continue;
608
+ }
609
+ const content = await fs.readFile(path.join(options.directory, entry), "utf8");
610
+ const artifact = normalizeArtifactRecord(JSON.parse(content));
611
+ if (matchesListInput(artifact, input)) {
612
+ artifacts.push(cloneArtifact(artifact));
613
+ }
614
+ }
615
+ return artifacts.sort((left, right) => left.createdAt - right.createdAt || left.id.localeCompare(right.id));
616
+ },
617
+ async deleteArtifact(input) {
618
+ const artifact = await load(input);
619
+ try {
620
+ await fs.unlink(filePath(input));
621
+ }
622
+ catch (error) {
623
+ if (error.code !== "ENOENT") {
624
+ throw error;
625
+ }
626
+ }
627
+ if (artifact?.blobPath) {
628
+ try {
629
+ await fs.unlink(path.join(options.directory, artifact.blobPath));
630
+ }
631
+ catch (error) {
632
+ if (error.code !== "ENOENT") {
633
+ throw error;
634
+ }
635
+ }
636
+ }
637
+ }
638
+ };
639
+ };
640
+ export const createSqliteArtifactService = (options) => {
641
+ const tableName = validateIdentifier(options.tableName ?? "zhivex_artifacts", "tableName");
642
+ options.db.exec(`
643
+ CREATE TABLE IF NOT EXISTS ${tableName} (
644
+ artifact_key TEXT PRIMARY KEY,
645
+ app_name TEXT NOT NULL,
646
+ user_id TEXT NOT NULL,
647
+ session_id TEXT NOT NULL,
648
+ artifact_id TEXT NOT NULL,
649
+ workflow_run_id TEXT,
650
+ workflow_step_id TEXT,
651
+ agent_run_id TEXT,
652
+ artifact_json TEXT NOT NULL,
653
+ created_at_ms INTEGER NOT NULL,
654
+ updated_at_ms INTEGER NOT NULL
655
+ )
656
+ `);
657
+ const loadStatement = prepareSqliteStatement(options.db, `SELECT artifact_json FROM ${tableName} WHERE artifact_key = ?`);
658
+ const listStatement = prepareSqliteStatement(options.db, `SELECT artifact_json FROM ${tableName}
659
+ WHERE app_name = ?
660
+ AND user_id = ?
661
+ AND session_id = ?
662
+ AND (? IS NULL OR workflow_run_id = ?)
663
+ AND (? IS NULL OR workflow_step_id = ?)
664
+ AND (? IS NULL OR agent_run_id = ?)
665
+ ORDER BY created_at_ms ASC, artifact_id ASC`);
666
+ const saveStatement = prepareSqliteStatement(options.db, `
667
+ INSERT INTO ${tableName} (
668
+ artifact_key,
669
+ app_name,
670
+ user_id,
671
+ session_id,
672
+ artifact_id,
673
+ workflow_run_id,
674
+ workflow_step_id,
675
+ agent_run_id,
676
+ artifact_json,
677
+ created_at_ms,
678
+ updated_at_ms
679
+ )
680
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
681
+ ON CONFLICT(artifact_key) DO UPDATE SET
682
+ app_name = excluded.app_name,
683
+ user_id = excluded.user_id,
684
+ session_id = excluded.session_id,
685
+ artifact_id = excluded.artifact_id,
686
+ workflow_run_id = excluded.workflow_run_id,
687
+ workflow_step_id = excluded.workflow_step_id,
688
+ agent_run_id = excluded.agent_run_id,
689
+ artifact_json = excluded.artifact_json,
690
+ updated_at_ms = excluded.updated_at_ms
691
+ `);
692
+ const updateCasStatement = prepareSqliteStatement(options.db, `
693
+ UPDATE ${tableName}
694
+ SET app_name = ?,
695
+ user_id = ?,
696
+ session_id = ?,
697
+ artifact_id = ?,
698
+ workflow_run_id = ?,
699
+ workflow_step_id = ?,
700
+ agent_run_id = ?,
701
+ artifact_json = ?,
702
+ updated_at_ms = ?
703
+ WHERE artifact_key = ?
704
+ AND updated_at_ms = ?
705
+ `);
706
+ const deleteStatement = prepareSqliteStatement(options.db, `DELETE FROM ${tableName} WHERE artifact_key = ?`);
707
+ const load = (input) => {
708
+ const row = loadStatement.get([artifactKey(input)]);
709
+ return parseArtifactJson(getRecordField(row, ["artifact_json", "artifactJson"]));
710
+ };
711
+ const save = (artifact, options) => {
712
+ if (options?.expectedRevision !== undefined && options.existing) {
713
+ const result = updateCasStatement.run([
714
+ artifact.appName,
715
+ artifact.userId,
716
+ artifact.sessionId,
717
+ artifact.id,
718
+ artifact.workflowRunId ?? null,
719
+ artifact.workflowStepId ?? null,
720
+ artifact.agentRunId ?? null,
721
+ JSON.stringify(artifact),
722
+ artifact.updatedAt,
723
+ artifactKey(lookupFromArtifact(artifact)),
724
+ options.existing.updatedAt
725
+ ]);
726
+ if (sqliteMutationCount(result) === 0) {
727
+ throw new ConflictError("ArtifactRecord revision conflict.");
728
+ }
729
+ }
730
+ else {
731
+ saveStatement.run([
732
+ artifactKey(lookupFromArtifact(artifact)),
733
+ artifact.appName,
734
+ artifact.userId,
735
+ artifact.sessionId,
736
+ artifact.id,
737
+ artifact.workflowRunId ?? null,
738
+ artifact.workflowStepId ?? null,
739
+ artifact.agentRunId ?? null,
740
+ JSON.stringify(artifact),
741
+ artifact.createdAt,
742
+ artifact.updatedAt
743
+ ]);
744
+ }
745
+ return cloneArtifact(artifact);
746
+ };
747
+ return {
748
+ saveArtifact(input) {
749
+ const id = input.id ?? randomId("art");
750
+ const existing = load({
751
+ appName: input.appName,
752
+ userId: input.userId,
753
+ sessionId: input.sessionId,
754
+ id
755
+ });
756
+ assertExpectedRevision(existing, input.expectedRevision, "ArtifactRecord");
757
+ return save(createArtifact({ ...input, id }, existing), {
758
+ existing,
759
+ expectedRevision: input.expectedRevision
760
+ });
761
+ },
762
+ saveBinaryArtifact(input) {
763
+ const id = input.id ?? randomId("art");
764
+ const bytes = bytesFromBinaryInput(input.data);
765
+ const existing = load({
766
+ appName: input.appName,
767
+ userId: input.userId,
768
+ sessionId: input.sessionId,
769
+ id
770
+ });
771
+ assertExpectedRevision(existing, input.expectedRevision, "ArtifactRecord");
772
+ return save(createArtifact({
773
+ ...input,
774
+ id,
775
+ data: Buffer.from(bytes).toString("base64"),
776
+ encoding: "base64",
777
+ size: bytes.byteLength,
778
+ sha256: input.sha256 ?? sha256Digest(bytes),
779
+ storageMode: "json"
780
+ }, existing), {
781
+ existing,
782
+ expectedRevision: input.expectedRevision
783
+ });
784
+ },
785
+ loadArtifact(input) {
786
+ return load(input);
787
+ },
788
+ loadBinaryArtifact(input) {
789
+ const artifact = load(input);
790
+ if (!artifact || artifact.encoding !== "base64" || typeof artifact.data !== "string") {
791
+ return undefined;
792
+ }
793
+ return {
794
+ artifact,
795
+ data: new Uint8Array(Buffer.from(artifact.data, "base64"))
796
+ };
797
+ },
798
+ listArtifacts(input) {
799
+ const params = [
800
+ input.appName,
801
+ input.userId,
802
+ input.sessionId,
803
+ input.workflowRunId ?? null,
804
+ input.workflowRunId ?? null,
805
+ input.workflowStepId ?? null,
806
+ input.workflowStepId ?? null,
807
+ input.agentRunId ?? null,
808
+ input.agentRunId ?? null
809
+ ];
810
+ const rows = listStatement.all?.(params) ?? [];
811
+ return rows.flatMap((row) => {
812
+ const artifact = parseArtifactJson(getRecordField(row, ["artifact_json", "artifactJson"]));
813
+ return artifact ? [artifact] : [];
814
+ });
815
+ },
816
+ deleteArtifact(input) {
817
+ deleteStatement.run([artifactKey(input)]);
818
+ }
819
+ };
820
+ };
821
+ export const createPostgresArtifactService = (options) => {
822
+ const tableName = validateIdentifier(options.tableName ?? "zhivex_artifacts", "tableName");
823
+ const createSql = `
824
+ CREATE TABLE IF NOT EXISTS ${tableName} (
825
+ artifact_key TEXT PRIMARY KEY,
826
+ app_name TEXT NOT NULL,
827
+ user_id TEXT NOT NULL,
828
+ session_id TEXT NOT NULL,
829
+ artifact_id TEXT NOT NULL,
830
+ workflow_run_id TEXT,
831
+ workflow_step_id TEXT,
832
+ agent_run_id TEXT,
833
+ artifact_json JSONB NOT NULL,
834
+ created_at_ms BIGINT NOT NULL,
835
+ updated_at_ms BIGINT NOT NULL
836
+ )
837
+ `;
838
+ const load = async (input) => {
839
+ await ensurePostgresTable(options.client, tableName, createSql);
840
+ const result = await options.client.query(`SELECT artifact_json FROM ${tableName} WHERE artifact_key = $1`, [artifactKey(input)]);
841
+ return parseArtifactJson(getRecordField(result.rows[0], ["artifact_json", "artifactJson"]));
842
+ };
843
+ const save = async (artifact, saveOptions) => {
844
+ await ensurePostgresTable(options.client, tableName, createSql);
845
+ if (saveOptions?.expectedRevision !== undefined && saveOptions.existing) {
846
+ const result = await options.client.query(`UPDATE ${tableName}
847
+ SET app_name = $2,
848
+ user_id = $3,
849
+ session_id = $4,
850
+ artifact_id = $5,
851
+ workflow_run_id = $6,
852
+ workflow_step_id = $7,
853
+ agent_run_id = $8,
854
+ artifact_json = $9::jsonb,
855
+ updated_at_ms = $10
856
+ WHERE artifact_key = $1
857
+ AND updated_at_ms = $11
858
+ RETURNING artifact_json`, [
859
+ artifactKey(lookupFromArtifact(artifact)),
860
+ artifact.appName,
861
+ artifact.userId,
862
+ artifact.sessionId,
863
+ artifact.id,
864
+ artifact.workflowRunId ?? null,
865
+ artifact.workflowStepId ?? null,
866
+ artifact.agentRunId ?? null,
867
+ JSON.stringify(artifact),
868
+ artifact.updatedAt,
869
+ saveOptions.existing.updatedAt
870
+ ]);
871
+ if (result.rows.length === 0) {
872
+ throw new ConflictError("ArtifactRecord revision conflict.");
873
+ }
874
+ }
875
+ else {
876
+ await options.client.query(`INSERT INTO ${tableName} (
877
+ artifact_key,
878
+ app_name,
879
+ user_id,
880
+ session_id,
881
+ artifact_id,
882
+ workflow_run_id,
883
+ workflow_step_id,
884
+ agent_run_id,
885
+ artifact_json,
886
+ created_at_ms,
887
+ updated_at_ms
888
+ )
889
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10, $11)
890
+ ON CONFLICT(artifact_key) DO UPDATE SET
891
+ app_name = EXCLUDED.app_name,
892
+ user_id = EXCLUDED.user_id,
893
+ session_id = EXCLUDED.session_id,
894
+ artifact_id = EXCLUDED.artifact_id,
895
+ workflow_run_id = EXCLUDED.workflow_run_id,
896
+ workflow_step_id = EXCLUDED.workflow_step_id,
897
+ agent_run_id = EXCLUDED.agent_run_id,
898
+ artifact_json = EXCLUDED.artifact_json,
899
+ updated_at_ms = EXCLUDED.updated_at_ms`, [
900
+ artifactKey(lookupFromArtifact(artifact)),
901
+ artifact.appName,
902
+ artifact.userId,
903
+ artifact.sessionId,
904
+ artifact.id,
905
+ artifact.workflowRunId ?? null,
906
+ artifact.workflowStepId ?? null,
907
+ artifact.agentRunId ?? null,
908
+ JSON.stringify(artifact),
909
+ artifact.createdAt,
910
+ artifact.updatedAt
911
+ ]);
912
+ }
913
+ return cloneArtifact(artifact);
914
+ };
915
+ return {
916
+ async saveArtifact(input) {
917
+ const id = input.id ?? randomId("art");
918
+ const existing = await load({
919
+ appName: input.appName,
920
+ userId: input.userId,
921
+ sessionId: input.sessionId,
922
+ id
923
+ });
924
+ assertExpectedRevision(existing, input.expectedRevision, "ArtifactRecord");
925
+ return save(createArtifact({ ...input, id }, existing), {
926
+ existing,
927
+ expectedRevision: input.expectedRevision
928
+ });
929
+ },
930
+ async saveBinaryArtifact(input) {
931
+ const id = input.id ?? randomId("art");
932
+ const bytes = bytesFromBinaryInput(input.data);
933
+ const existing = await load({
934
+ appName: input.appName,
935
+ userId: input.userId,
936
+ sessionId: input.sessionId,
937
+ id
938
+ });
939
+ assertExpectedRevision(existing, input.expectedRevision, "ArtifactRecord");
940
+ return save(createArtifact({
941
+ ...input,
942
+ id,
943
+ data: Buffer.from(bytes).toString("base64"),
944
+ encoding: "base64",
945
+ size: bytes.byteLength,
946
+ sha256: input.sha256 ?? sha256Digest(bytes),
947
+ storageMode: "json"
948
+ }, existing), {
949
+ existing,
950
+ expectedRevision: input.expectedRevision
951
+ });
952
+ },
953
+ loadArtifact(input) {
954
+ return load(input);
955
+ },
956
+ async loadBinaryArtifact(input) {
957
+ const artifact = await load(input);
958
+ if (!artifact || artifact.encoding !== "base64" || typeof artifact.data !== "string") {
959
+ return undefined;
960
+ }
961
+ return {
962
+ artifact,
963
+ data: new Uint8Array(Buffer.from(artifact.data, "base64"))
964
+ };
965
+ },
966
+ async listArtifacts(input) {
967
+ await ensurePostgresTable(options.client, tableName, createSql);
968
+ const result = await options.client.query(`SELECT artifact_json FROM ${tableName}
969
+ WHERE app_name = $1
970
+ AND user_id = $2
971
+ AND session_id = $3
972
+ AND ($4::text IS NULL OR workflow_run_id = $4)
973
+ AND ($5::text IS NULL OR workflow_step_id = $5)
974
+ AND ($6::text IS NULL OR agent_run_id = $6)
975
+ ORDER BY created_at_ms ASC, artifact_id ASC`, [
976
+ input.appName,
977
+ input.userId,
978
+ input.sessionId,
979
+ input.workflowRunId ?? null,
980
+ input.workflowStepId ?? null,
981
+ input.agentRunId ?? null
982
+ ]);
983
+ return result.rows.flatMap((row) => {
984
+ const artifact = parseArtifactJson(getRecordField(row, ["artifact_json", "artifactJson"]));
985
+ return artifact ? [artifact] : [];
986
+ });
987
+ },
988
+ async deleteArtifact(input) {
989
+ await ensurePostgresTable(options.client, tableName, createSql);
990
+ await options.client.query(`DELETE FROM ${tableName} WHERE artifact_key = $1`, [artifactKey(input)]);
991
+ }
992
+ };
993
+ };
994
+ //# sourceMappingURL=artifact.js.map