@absolutejs/artifacts 0.0.1 → 0.0.2

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 CHANGED
@@ -14,11 +14,15 @@ routes, authorization, UI, or hosting.
14
14
 
15
15
  - Structured artifact-kind schemas and runtime validation
16
16
  - Draft, published, and archived lifecycle states
17
- - Optimistic revisions that prevent lost edits
18
- - Storage, renderer, and publisher interfaces
19
- - An in-memory store for development and tests
17
+ - Immutable revision history, restoration, and optimistic updates
18
+ - Structured content plus opaque references to generated or source files
19
+ - Artifact, asset, renderer, and publisher storage interfaces
20
+ - In-memory artifact and asset stores for development and tests
20
21
  - Owner-bound lifecycle tools structurally compatible with AI tool maps
21
22
  - Provenance fields for model, tool, trace, and source entities
23
+ - Standard file-backed kinds for documents, presentations, spreadsheets,
24
+ datasets, code, images, audio, video, email, archives, and generic files
25
+ - An optional bridge to `@absolutejs/rag` ingestion
22
26
 
23
27
  Your application retains authorization, durable persistence, public tokens,
24
28
  URLs, notifications, analytics, submissions, and product-specific rendering.
@@ -29,6 +33,7 @@ URLs, notifications, analytics, submissions, and product-specific rendering.
29
33
  import { Type } from "@sinclair/typebox";
30
34
  import {
31
35
  createArtifactService,
36
+ createMemoryArtifactAssetStore,
32
37
  createMemoryArtifactStore,
33
38
  defineArtifactRegistry,
34
39
  } from "@absolutejs/artifacts";
@@ -51,6 +56,7 @@ const registry = defineArtifactRegistry({
51
56
  });
52
57
 
53
58
  const artifacts = createArtifactService({
59
+ assetStore: createMemoryArtifactAssetStore(),
54
60
  registry,
55
61
  store: createMemoryArtifactStore(),
56
62
  });
@@ -67,6 +73,70 @@ const page = await artifacts.create("owner-123", {
67
73
  });
68
74
  ```
69
75
 
76
+ Every successful create or lifecycle mutation appends an immutable snapshot.
77
+ Restoring history creates a new private draft instead of rewriting or
78
+ republishing an old revision:
79
+
80
+ ```ts
81
+ const history = await artifacts.listRevisions("owner-123", page.id);
82
+ const restored = await artifacts.restore("owner-123", page.id, 1);
83
+ ```
84
+
85
+ ## File-backed artifact kinds
86
+
87
+ Use the bundled definitions directly or compose them with application-specific
88
+ kinds:
89
+
90
+ ```ts
91
+ import {
92
+ defineArtifactRegistry,
93
+ standardArtifactDefinitions,
94
+ } from "@absolutejs/artifacts";
95
+
96
+ const registry = defineArtifactRegistry({
97
+ ...standardArtifactDefinitions,
98
+ page: myPageDefinition,
99
+ });
100
+ ```
101
+
102
+ File bytes stay in host storage. Artifact records retain opaque references with
103
+ name, media type, size, checksum, role, and storage URI. The URI is not treated
104
+ as a public URL and the package reads it only through the configured asset
105
+ store. Detaching a file does not delete its bytes because older immutable
106
+ revisions may still reference it.
107
+
108
+ ```ts
109
+ const report = await artifacts.create("owner-123", {
110
+ content: { summary: "Quarterly results" },
111
+ createdBy: "agent",
112
+ kind: "document",
113
+ title: "Q3 report",
114
+ });
115
+
116
+ await artifacts.attach("owner-123", report.id, {
117
+ data: pdfBytes,
118
+ mediaType: "application/pdf",
119
+ name: "q3-report.pdf",
120
+ role: "primary",
121
+ });
122
+ ```
123
+
124
+ ## RAG ingestion
125
+
126
+ The optional `@absolutejs/artifacts/rag` entry point resolves one current or
127
+ historical artifact record into the upload contract already accepted by
128
+ `@absolutejs/rag`. Structured content is included as JSON and every attached
129
+ file is included without exposing its storage URI:
130
+
131
+ ```ts
132
+ import { artifactToRAGUploads } from "@absolutejs/artifacts/rag";
133
+ import { buildRAGUpsertInputFromUploads } from "@absolutejs/rag";
134
+
135
+ const revision = await artifacts.getRevision("owner-123", report.id, 2);
136
+ const uploads = await artifactToRAGUploads(revision, assetStore);
137
+ const upsert = await buildRAGUpsertInputFromUploads({ uploads });
138
+ ```
139
+
70
140
  ## Compose publishing and rendering
71
141
 
72
142
  Publishing is an adapter because public access is a host policy:
package/dist/index.js CHANGED
@@ -31,6 +31,76 @@ var defineArtifactRegistry = (definitions) => ({
31
31
  return content;
32
32
  }
33
33
  });
34
+ // src/standardKinds.ts
35
+ import { Type } from "@sinclair/typebox";
36
+ var FileArtifactContentSchema = Type.Object({
37
+ description: Type.Optional(Type.String()),
38
+ instructions: Type.Optional(Type.String()),
39
+ summary: Type.Optional(Type.String())
40
+ });
41
+ var capabilities = [
42
+ "attach",
43
+ "archive",
44
+ "edit",
45
+ "export",
46
+ "preview",
47
+ "refine"
48
+ ];
49
+ var fileKind = (label, acceptedMediaTypes, maxCount = 1) => ({
50
+ assets: { acceptedMediaTypes, maxCount },
51
+ capabilities: [...capabilities],
52
+ content: FileArtifactContentSchema,
53
+ label,
54
+ schemaVersion: 1
55
+ });
56
+ var standardArtifactDefinitions = {
57
+ archive: fileKind("Archive", [
58
+ "application/gzip",
59
+ "application/vnd.rar",
60
+ "application/x-7z-compressed",
61
+ "application/x-bzip2",
62
+ "application/x-tar",
63
+ "application/zip"
64
+ ], 20),
65
+ audio: fileKind("Audio", ["audio/*"]),
66
+ code: fileKind("Code", ["application/json", "application/xml", "text/*"], 100),
67
+ dataset: fileKind("Dataset", [
68
+ "application/json",
69
+ "application/x-ndjson",
70
+ "application/xml",
71
+ "text/csv",
72
+ "text/tab-separated-values",
73
+ "text/plain",
74
+ "text/yaml"
75
+ ], 20),
76
+ document: fileKind("Document", [
77
+ "application/epub+zip",
78
+ "application/msword",
79
+ "application/pdf",
80
+ "application/rtf",
81
+ "application/vnd.oasis.opendocument.text",
82
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
83
+ "text/*"
84
+ ], 20),
85
+ email: fileKind("Email", ["application/mbox", "application/vnd.ms-outlook", "message/*", "text/*"], 100),
86
+ file: fileKind("File", ["*/*"], 100),
87
+ image: fileKind("Image", ["image/*"], 20),
88
+ presentation: fileKind("Presentation", [
89
+ "application/pdf",
90
+ "application/vnd.ms-powerpoint",
91
+ "application/vnd.oasis.opendocument.presentation",
92
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation"
93
+ ], 20),
94
+ spreadsheet: fileKind("Spreadsheet", [
95
+ "application/vnd.ms-excel",
96
+ "application/vnd.oasis.opendocument.spreadsheet",
97
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
98
+ "text/csv",
99
+ "text/tab-separated-values"
100
+ ], 20),
101
+ video: fileKind("Video", ["video/*"])
102
+ };
103
+ var STANDARD_ARTIFACT_KIND_NAMES = Object.keys(standardArtifactDefinitions);
34
104
  // src/renderers.ts
35
105
  var createArtifactRendererRegistry = (initial = []) => {
36
106
  const key = (kind, format) => `${kind}:${format}`;
@@ -55,9 +125,45 @@ var requireCapability = (artifact, capability) => {
55
125
  throw new ArtifactError("unsupported_capability", `${artifact.kind} artifacts do not support ${capability}`);
56
126
  }
57
127
  };
128
+ var mediaTypeMatches = (accepted, actual) => {
129
+ if (accepted === "*/*" || accepted === actual)
130
+ return true;
131
+ if (!accepted.endsWith("/*"))
132
+ return false;
133
+ return actual.startsWith(accepted.slice(0, -1));
134
+ };
58
135
  var createArtifactService = (options) => {
59
136
  const now = () => (options.clock ?? (() => new Date))().toISOString();
60
137
  const idFactory = options.idFactory ?? (() => crypto.randomUUID());
138
+ const validateAssets = (kind, assets) => {
139
+ const policy = options.registry.definitions[kind]?.assets;
140
+ if (!policy) {
141
+ if (assets.length > 0) {
142
+ throw new ArtifactError("invalid_content", `${kind} artifacts do not accept file assets`);
143
+ }
144
+ return assets;
145
+ }
146
+ if (policy.maxCount !== undefined && assets.length > policy.maxCount) {
147
+ throw new ArtifactError("invalid_content", `${kind} artifacts accept at most ${policy.maxCount} file assets`);
148
+ }
149
+ const rejected = assets.find((asset) => policy.acceptedMediaTypes?.length && !policy.acceptedMediaTypes.some((accepted) => mediaTypeMatches(accepted, asset.mediaType)));
150
+ if (rejected) {
151
+ throw new ArtifactError("invalid_content", `${rejected.mediaType} is not accepted by ${kind} artifacts`);
152
+ }
153
+ return assets;
154
+ };
155
+ const validateNewAsset = (kind, input, currentCount) => {
156
+ const policy = options.registry.definitions[kind]?.assets;
157
+ if (!policy) {
158
+ throw new ArtifactError("invalid_content", `${kind} artifacts do not accept file assets`);
159
+ }
160
+ if (policy.maxCount !== undefined && currentCount >= policy.maxCount) {
161
+ throw new ArtifactError("invalid_content", `${kind} artifacts accept at most ${policy.maxCount} file assets`);
162
+ }
163
+ if (policy.acceptedMediaTypes?.length && !policy.acceptedMediaTypes.some((accepted) => mediaTypeMatches(accepted, input.mediaType))) {
164
+ throw new ArtifactError("invalid_content", `${input.mediaType} is not accepted by ${kind} artifacts`);
165
+ }
166
+ };
61
167
  const get = async (ownerId, artifactId) => {
62
168
  const artifact = await options.store.get(ownerId, artifactId);
63
169
  if (!artifact) {
@@ -83,6 +189,28 @@ var createArtifactService = (options) => {
83
189
  updatedAt: now()
84
190
  }, current.revision);
85
191
  },
192
+ attach: async (ownerId, artifactId, input, expectedRevision) => {
193
+ const current = await get(ownerId, artifactId);
194
+ requireCapability(current, "attach");
195
+ validateNewAsset(current.kind, input, current.assets.length);
196
+ if (!options.assetStore) {
197
+ throw new ArtifactError("asset_store_unavailable", "No artifact asset store is configured");
198
+ }
199
+ const reference = await options.assetStore.write(input, {
200
+ artifact: current,
201
+ idempotencyKey: `artifact:${current.id}:asset:${current.revision + 1}`
202
+ });
203
+ const assets = validateAssets(current.kind, [
204
+ ...current.assets.filter((asset) => asset.id !== reference.id),
205
+ reference
206
+ ]);
207
+ return saveRevision({
208
+ ...current,
209
+ assets,
210
+ revision: current.revision + 1,
211
+ updatedAt: now()
212
+ }, expectedRevision ?? current.revision);
213
+ },
86
214
  create: async (ownerId, input) => {
87
215
  const definition = options.registry.definitions[input.kind];
88
216
  if (!definition) {
@@ -91,6 +219,7 @@ var createArtifactService = (options) => {
91
219
  const content = options.registry.parse(input.kind, input.content);
92
220
  const timestamp = now();
93
221
  const artifact = {
222
+ assets: validateAssets(input.kind, input.assets ?? []),
94
223
  capabilities: definition.capabilities ?? ["archive", "edit", "preview"],
95
224
  content,
96
225
  createdAt: timestamp,
@@ -109,8 +238,30 @@ var createArtifactService = (options) => {
109
238
  await options.store.create(artifact);
110
239
  return artifact;
111
240
  },
241
+ detach: async (ownerId, artifactId, assetId, expectedRevision) => {
242
+ const current = await get(ownerId, artifactId);
243
+ requireCapability(current, "attach");
244
+ const assets = current.assets.filter((asset) => asset.id !== assetId);
245
+ if (assets.length === current.assets.length) {
246
+ throw new ArtifactError("not_found", "Artifact asset not found");
247
+ }
248
+ return saveRevision({
249
+ ...current,
250
+ assets,
251
+ revision: current.revision + 1,
252
+ updatedAt: now()
253
+ }, expectedRevision ?? current.revision);
254
+ },
112
255
  get,
256
+ getRevision: async (ownerId, artifactId, revision) => {
257
+ const snapshot = await options.store.getRevision(ownerId, artifactId, revision);
258
+ if (!snapshot) {
259
+ throw new ArtifactError("not_found", "Artifact revision not found");
260
+ }
261
+ return snapshot;
262
+ },
113
263
  list: (ownerId, query) => options.store.list(ownerId, query),
264
+ listRevisions: (ownerId, artifactId) => options.store.listRevisions(ownerId, artifactId),
114
265
  publish: async (ownerId, artifactId) => {
115
266
  const current = await get(ownerId, artifactId);
116
267
  requireCapability(current, "publish");
@@ -151,6 +302,40 @@ var createArtifactService = (options) => {
151
302
  updatedAt: now()
152
303
  }, current.revision);
153
304
  },
305
+ readAsset: async (ownerId, artifactId, assetId) => {
306
+ const artifact = await get(ownerId, artifactId);
307
+ const asset = artifact.assets.find((candidate) => candidate.id === assetId);
308
+ if (!asset) {
309
+ throw new ArtifactError("not_found", "Artifact asset not found");
310
+ }
311
+ if (!options.assetStore) {
312
+ throw new ArtifactError("asset_store_unavailable", "No artifact asset store is configured");
313
+ }
314
+ return {
315
+ asset,
316
+ data: await options.assetStore.read(asset, { artifact })
317
+ };
318
+ },
319
+ restore: async (ownerId, artifactId, revision, expectedRevision) => {
320
+ const current = await get(ownerId, artifactId);
321
+ requireCapability(current, "edit");
322
+ const snapshot = await options.store.getRevision(ownerId, artifactId, revision);
323
+ if (!snapshot) {
324
+ throw new ArtifactError("not_found", "Artifact revision not found");
325
+ }
326
+ const timestamp = now();
327
+ return saveRevision({
328
+ ...current,
329
+ assets: validateAssets(current.kind, snapshot.assets),
330
+ content: options.registry.parse(current.kind, snapshot.content),
331
+ metadata: snapshot.metadata,
332
+ publication: undefined,
333
+ revision: current.revision + 1,
334
+ status: "draft",
335
+ title: snapshot.title,
336
+ updatedAt: timestamp
337
+ }, expectedRevision ?? current.revision);
338
+ },
154
339
  update: async (ownerId, artifactId, input) => {
155
340
  const current = await get(ownerId, artifactId);
156
341
  requireCapability(current, "edit");
@@ -158,6 +343,7 @@ var createArtifactService = (options) => {
158
343
  const content = input.content === undefined ? current.content : options.registry.parse(current.kind, input.content);
159
344
  return saveRevision({
160
345
  ...current,
346
+ assets: input.assets === undefined ? current.assets : validateAssets(current.kind, input.assets),
161
347
  content,
162
348
  metadata: input.metadata ?? current.metadata,
163
349
  revision: current.revision + 1,
@@ -168,32 +354,89 @@ var createArtifactService = (options) => {
168
354
  };
169
355
  };
170
356
  // src/store.ts
357
+ import { createHash } from "crypto";
171
358
  var clone = (value) => structuredClone(value);
359
+ var createMemoryArtifactAssetStore = () => {
360
+ const bytes = new Map;
361
+ const references = new Map;
362
+ const idempotency = new Map;
363
+ return {
364
+ read: async (reference) => {
365
+ const data = bytes.get(reference.id);
366
+ if (!data)
367
+ throw new Error(`Artifact asset not found: ${reference.id}`);
368
+ return clone(data);
369
+ },
370
+ write: async (input, context) => {
371
+ const existingId = idempotency.get(context.idempotencyKey);
372
+ if (existingId)
373
+ return clone(references.get(existingId));
374
+ const id = crypto.randomUUID();
375
+ const reference = {
376
+ checksum: {
377
+ algorithm: "sha256",
378
+ value: createHash("sha256").update(input.data).digest("hex")
379
+ },
380
+ id,
381
+ mediaType: input.mediaType,
382
+ metadata: input.metadata,
383
+ name: input.name,
384
+ role: input.role ?? "attachment",
385
+ size: input.data.byteLength,
386
+ uri: `memory://${id}`
387
+ };
388
+ bytes.set(id, clone(input.data));
389
+ references.set(id, clone(reference));
390
+ idempotency.set(context.idempotencyKey, id);
391
+ return reference;
392
+ }
393
+ };
394
+ };
172
395
  var createMemoryArtifactStore = (initial = []) => {
173
396
  const records = new Map(initial.map((record) => [record.id, clone(record)]));
397
+ const revisions = new Map;
398
+ for (const record of initial)
399
+ revisions.set(record.id, [clone(record)]);
174
400
  return {
175
401
  create: async (record) => {
176
402
  if (records.has(record.id))
177
403
  throw new Error(`Duplicate artifact id: ${record.id}`);
178
404
  records.set(record.id, clone(record));
405
+ revisions.set(record.id, [clone(record)]);
179
406
  },
180
407
  get: async (ownerId, artifactId) => {
181
408
  const record = records.get(artifactId);
182
409
  return record?.ownerId === ownerId ? clone(record) : null;
183
410
  },
411
+ getRevision: async (ownerId, artifactId, revision) => {
412
+ const current = records.get(artifactId);
413
+ if (current?.ownerId !== ownerId)
414
+ return null;
415
+ return clone(revisions.get(artifactId)?.find((candidate) => candidate.revision === revision) ?? null);
416
+ },
184
417
  list: async (ownerId, query = {}) => [...records.values()].filter((record) => record.ownerId === ownerId && (!query.kind || record.kind === query.kind) && (!query.status || record.status === query.status)).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, query.limit ?? Number.POSITIVE_INFINITY).map(clone),
418
+ listRevisions: async (ownerId, artifactId) => {
419
+ const current = records.get(artifactId);
420
+ if (current?.ownerId !== ownerId)
421
+ return [];
422
+ return (revisions.get(artifactId) ?? []).toSorted((left, right) => right.revision - left.revision).map(clone);
423
+ },
185
424
  save: async (record, expectedRevision) => {
186
425
  const current = records.get(record.id);
187
426
  if (!current || current.ownerId !== record.ownerId || current.revision !== expectedRevision) {
188
427
  return false;
189
428
  }
190
429
  records.set(record.id, clone(record));
430
+ revisions.set(record.id, [
431
+ ...revisions.get(record.id) ?? [],
432
+ clone(record)
433
+ ]);
191
434
  return true;
192
435
  }
193
436
  };
194
437
  };
195
438
  // src/tools.ts
196
- import { Type } from "@sinclair/typebox";
439
+ import { Type as Type2 } from "@sinclair/typebox";
197
440
  var record = (input) => input && typeof input === "object" && !Array.isArray(input) ? input : {};
198
441
  var stringValue = (input, key) => typeof input[key] === "string" ? input[key] : undefined;
199
442
  var createArtifactTools = (options) => ({
@@ -214,10 +457,10 @@ var createArtifactTools = (options) => ({
214
457
  });
215
458
  return JSON.stringify(artifact);
216
459
  },
217
- input: Type.Object({
218
- content: Type.Unknown(),
219
- kind: Type.String({ minLength: 1 }),
220
- title: Type.String({ minLength: 1 })
460
+ input: Type2.Object({
461
+ content: Type2.Unknown(),
462
+ kind: Type2.String({ minLength: 1 }),
463
+ title: Type2.String({ minLength: 1 })
221
464
  })
222
465
  },
223
466
  artifact_get: {
@@ -229,7 +472,7 @@ var createArtifactTools = (options) => ({
229
472
  return "Provide artifactId.";
230
473
  return JSON.stringify(await options.service.get(options.ownerId, artifactId));
231
474
  },
232
- input: Type.Object({ artifactId: Type.String({ minLength: 1 }) })
475
+ input: Type2.Object({ artifactId: Type2.String({ minLength: 1 }) })
233
476
  },
234
477
  artifact_list: {
235
478
  annotations: { readOnlyHint: true },
@@ -242,11 +485,22 @@ var createArtifactTools = (options) => ({
242
485
  status: ARTIFACT_STATUSES.find((candidate) => candidate === status)
243
486
  }));
244
487
  },
245
- input: Type.Object({
246
- kind: Type.Optional(Type.String()),
247
- status: Type.Optional(Type.Union(ARTIFACT_STATUSES.map((status) => Type.Literal(status))))
488
+ input: Type2.Object({
489
+ kind: Type2.Optional(Type2.String()),
490
+ status: Type2.Optional(Type2.Union(ARTIFACT_STATUSES.map((status) => Type2.Literal(status))))
248
491
  })
249
492
  },
493
+ artifact_history: {
494
+ annotations: { readOnlyHint: true },
495
+ description: "List the immutable revisions of one owned artifact.",
496
+ handler: async (raw) => {
497
+ const artifactId = stringValue(record(raw), "artifactId");
498
+ if (!artifactId)
499
+ return "Provide artifactId.";
500
+ return JSON.stringify(await options.service.listRevisions(options.ownerId, artifactId));
501
+ },
502
+ input: Type2.Object({ artifactId: Type2.String({ minLength: 1 }) })
503
+ },
250
504
  artifact_publish: {
251
505
  description: "Publish or unpublish an artifact. Hosts should expose this tool only when the user explicitly controls public access.",
252
506
  handler: async (raw) => {
@@ -258,9 +512,25 @@ var createArtifactTools = (options) => ({
258
512
  const artifact = input.published ? await options.service.publish(options.ownerId, artifactId) : await options.service.unpublish(options.ownerId, artifactId);
259
513
  return JSON.stringify(artifact);
260
514
  },
261
- input: Type.Object({
262
- artifactId: Type.String({ minLength: 1 }),
263
- published: Type.Boolean()
515
+ input: Type2.Object({
516
+ artifactId: Type2.String({ minLength: 1 }),
517
+ published: Type2.Boolean()
518
+ })
519
+ },
520
+ artifact_restore: {
521
+ description: "Restore an immutable artifact revision as a new private draft revision.",
522
+ handler: async (raw) => {
523
+ const input = record(raw);
524
+ const artifactId = stringValue(input, "artifactId");
525
+ if (!artifactId || typeof input.revision !== "number") {
526
+ return "Provide artifactId and revision.";
527
+ }
528
+ return JSON.stringify(await options.service.restore(options.ownerId, artifactId, input.revision, typeof input.expectedRevision === "number" ? input.expectedRevision : undefined));
529
+ },
530
+ input: Type2.Object({
531
+ artifactId: Type2.String({ minLength: 1 }),
532
+ expectedRevision: Type2.Optional(Type2.Integer({ minimum: 1 })),
533
+ revision: Type2.Integer({ minimum: 1 })
264
534
  })
265
535
  },
266
536
  artifact_update: {
@@ -277,20 +547,23 @@ var createArtifactTools = (options) => ({
277
547
  });
278
548
  return JSON.stringify(artifact);
279
549
  },
280
- input: Type.Object({
281
- artifactId: Type.String({ minLength: 1 }),
282
- content: Type.Optional(Type.Unknown()),
283
- expectedRevision: Type.Optional(Type.Integer({ minimum: 1 })),
284
- title: Type.Optional(Type.String({ minLength: 1 }))
550
+ input: Type2.Object({
551
+ artifactId: Type2.String({ minLength: 1 }),
552
+ content: Type2.Optional(Type2.Unknown()),
553
+ expectedRevision: Type2.Optional(Type2.Integer({ minimum: 1 })),
554
+ title: Type2.Optional(Type2.String({ minLength: 1 }))
285
555
  })
286
556
  }
287
557
  });
288
558
  export {
559
+ standardArtifactDefinitions,
289
560
  defineArtifactRegistry,
290
561
  createMemoryArtifactStore,
562
+ createMemoryArtifactAssetStore,
291
563
  createArtifactTools,
292
564
  createArtifactService,
293
565
  createArtifactRendererRegistry,
566
+ STANDARD_ARTIFACT_KIND_NAMES,
294
567
  ArtifactError,
295
568
  ARTIFACT_STATUSES
296
569
  };
package/dist/manifest.js CHANGED
@@ -5954,6 +5954,24 @@ var manifest = defineManifest()({
5954
5954
  kind: Type2.Optional(Type2.String({ minLength: 1 })),
5955
5955
  ownerId: Type2.String({ minLength: 1 })
5956
5956
  })
5957
+ }),
5958
+ artifact_history: tool.runtime({
5959
+ annotations: { readOnlyHint: true },
5960
+ description: "List immutable revisions of one artifact owned by a user.",
5961
+ handler: async ({ artifactId, ownerId }, service) => JSON.stringify(await service.listRevisions(ownerId, artifactId)),
5962
+ input: Type2.Object({
5963
+ artifactId: Type2.String({ minLength: 1 }),
5964
+ ownerId: Type2.String({ minLength: 1 })
5965
+ })
5966
+ }),
5967
+ artifact_restore: tool.runtime({
5968
+ description: "Restore an old artifact revision as a new private draft.",
5969
+ handler: async ({ artifactId, ownerId, revision }, service) => JSON.stringify(await service.restore(ownerId, artifactId, revision)),
5970
+ input: Type2.Object({
5971
+ artifactId: Type2.String({ minLength: 1 }),
5972
+ ownerId: Type2.String({ minLength: 1 }),
5973
+ revision: Type2.Integer({ minimum: 1 })
5974
+ })
5957
5975
  })
5958
5976
  },
5959
5977
  wiring: [
@@ -95,6 +95,56 @@
95
95
  }
96
96
  },
97
97
  "kind": "runtime"
98
+ },
99
+ "artifact_history": {
100
+ "annotations": {
101
+ "readOnlyHint": true
102
+ },
103
+ "description": "List immutable revisions of one artifact owned by a user.",
104
+ "input": {
105
+ "type": "object",
106
+ "required": [
107
+ "artifactId",
108
+ "ownerId"
109
+ ],
110
+ "properties": {
111
+ "artifactId": {
112
+ "minLength": 1,
113
+ "type": "string"
114
+ },
115
+ "ownerId": {
116
+ "minLength": 1,
117
+ "type": "string"
118
+ }
119
+ }
120
+ },
121
+ "kind": "runtime"
122
+ },
123
+ "artifact_restore": {
124
+ "description": "Restore an old artifact revision as a new private draft.",
125
+ "input": {
126
+ "type": "object",
127
+ "required": [
128
+ "artifactId",
129
+ "ownerId",
130
+ "revision"
131
+ ],
132
+ "properties": {
133
+ "artifactId": {
134
+ "minLength": 1,
135
+ "type": "string"
136
+ },
137
+ "ownerId": {
138
+ "minLength": 1,
139
+ "type": "string"
140
+ },
141
+ "revision": {
142
+ "minimum": 1,
143
+ "type": "integer"
144
+ }
145
+ }
146
+ },
147
+ "kind": "runtime"
98
148
  }
99
149
  }
100
150
  }
package/dist/rag.js ADDED
@@ -0,0 +1,44 @@
1
+ // @bun
2
+ // src/rag.ts
3
+ import { Buffer } from "buffer";
4
+ var artifactMetadata = (artifact) => ({
5
+ artifactId: artifact.id,
6
+ artifactKind: artifact.kind,
7
+ artifactRevision: artifact.revision,
8
+ artifactStatus: artifact.status,
9
+ ...artifact.metadata
10
+ });
11
+ var artifactToRAGUploads = async (artifact, reader, options = {}) => {
12
+ const metadata = artifactMetadata(artifact);
13
+ const uploads = await Promise.all(artifact.assets.map(async (asset) => ({
14
+ content: Buffer.from(await reader.read(asset, { artifact })).toString("base64"),
15
+ contentType: asset.mediaType,
16
+ encoding: "base64",
17
+ metadata: {
18
+ ...metadata,
19
+ artifactAssetId: asset.id,
20
+ artifactAssetRole: asset.role,
21
+ ...asset.metadata
22
+ },
23
+ name: asset.name,
24
+ source: `artifact:${artifact.id}:revision:${artifact.revision}:asset:${asset.id}`,
25
+ title: artifact.title
26
+ })));
27
+ if (options.includeStructuredContent === false)
28
+ return uploads;
29
+ return [
30
+ {
31
+ content: JSON.stringify(artifact.content),
32
+ contentType: "application/json",
33
+ encoding: "utf8",
34
+ metadata: { ...metadata, artifactStructuredContent: true },
35
+ name: `${artifact.kind}-${artifact.id}-r${artifact.revision}.json`,
36
+ source: `artifact:${artifact.id}:revision:${artifact.revision}:content`,
37
+ title: artifact.title
38
+ },
39
+ ...uploads
40
+ ];
41
+ };
42
+ export {
43
+ artifactToRAGUploads
44
+ };
@@ -7,8 +7,9 @@
7
7
  * retains authorization, persistence, URLs, UI, and delivery policy.
8
8
  */
9
9
  export { defineArtifactRegistry, type ArtifactContent, type ArtifactKindDefinition, type ArtifactKindDefinitions, type ArtifactRegistry, } from "./registry";
10
+ export { STANDARD_ARTIFACT_KIND_NAMES, standardArtifactDefinitions, } from "./standardKinds";
10
11
  export { createArtifactRendererRegistry, type ArtifactRenderer, type ArtifactRendererRegistry, type ArtifactRenderResult, } from "./renderers";
11
12
  export { createArtifactService, type ArtifactPublisher, type ArtifactService, type ArtifactServiceOptions, } from "./service";
12
- export { createMemoryArtifactStore, type ArtifactStore } from "./store";
13
+ export { createMemoryArtifactStore, createMemoryArtifactAssetStore, type ArtifactAssetStore, type ArtifactStore, } from "./store";
13
14
  export { createArtifactTools, type ArtifactToolDefinition, type ArtifactToolMap, type ArtifactToolOptions, } from "./tools";
14
- export { ARTIFACT_STATUSES, ArtifactError, type ArtifactCapability, type ArtifactCreateInput, type ArtifactErrorCode, type ArtifactListQuery, type ArtifactProvenance, type ArtifactPublication, type ArtifactRecord, type ArtifactStatus, type ArtifactUpdateInput, } from "./types";
15
+ export { ARTIFACT_STATUSES, ArtifactError, type ArtifactAssetReference, type ArtifactAssetRole, type ArtifactAssetWriteInput, type ArtifactCapability, type ArtifactCreateInput, type ArtifactErrorCode, type ArtifactListQuery, type ArtifactProvenance, type ArtifactPublication, type ArtifactRecord, type ArtifactRevision, type ArtifactStatus, type ArtifactUpdateInput, } from "./types";
@@ -1,9 +1,18 @@
1
1
  export declare const manifest: import("@absolutejs/manifest").PackageManifest<Record<string, never>, {
2
2
  archive: (ownerId: string, artifactId: string) => Promise<import("./types").ArtifactRecord>;
3
+ attach: (ownerId: string, artifactId: string, input: import("./types").ArtifactAssetWriteInput, expectedRevision?: number) => Promise<import("./types").ArtifactRecord>;
3
4
  create: (ownerId: string, input: import("./types").ArtifactCreateInput) => Promise<import("./types").ArtifactRecord>;
5
+ detach: (ownerId: string, artifactId: string, assetId: string, expectedRevision?: number) => Promise<import("./types").ArtifactRecord>;
4
6
  get: (ownerId: string, artifactId: string) => Promise<import("./types").ArtifactRecord>;
7
+ getRevision: (ownerId: string, artifactId: string, revision: number) => Promise<Readonly<import("./types").ArtifactRecord<unknown>>>;
5
8
  list: (ownerId: string, query?: import("./types").ArtifactListQuery) => Promise<import("./types").ArtifactRecord[]>;
9
+ listRevisions: (ownerId: string, artifactId: string) => Promise<Readonly<import("./types").ArtifactRecord<unknown>>[]>;
6
10
  publish: (ownerId: string, artifactId: string) => Promise<import("./types").ArtifactRecord>;
7
11
  unpublish: (ownerId: string, artifactId: string) => Promise<import("./types").ArtifactRecord>;
12
+ readAsset: (ownerId: string, artifactId: string, assetId: string) => Promise<{
13
+ asset: import("./types").ArtifactAssetReference;
14
+ data: Uint8Array<ArrayBufferLike>;
15
+ }>;
16
+ restore: (ownerId: string, artifactId: string, revision: number, expectedRevision?: number) => Promise<import("./types").ArtifactRecord>;
8
17
  update: (ownerId: string, artifactId: string, input: import("./types").ArtifactUpdateInput) => Promise<import("./types").ArtifactRecord>;
9
18
  }>;
@@ -0,0 +1,15 @@
1
+ import type { RAGDocumentUploadInput } from "@absolutejs/rag";
2
+ import type { ArtifactAssetReference, ArtifactRecord } from "./types";
3
+ export type ArtifactRAGAssetReader = {
4
+ read(reference: ArtifactAssetReference, context: {
5
+ artifact: ArtifactRecord;
6
+ }): Promise<Uint8Array>;
7
+ };
8
+ export type ArtifactRAGUploadOptions = {
9
+ includeStructuredContent?: boolean;
10
+ };
11
+ /**
12
+ * Resolve an artifact revision into upload inputs accepted by @absolutejs/rag.
13
+ * Storage URIs remain opaque; only the supplied reader is allowed to access bytes.
14
+ */
15
+ export declare const artifactToRAGUploads: (artifact: ArtifactRecord, reader: ArtifactRAGAssetReader, options?: ArtifactRAGUploadOptions) => Promise<RAGDocumentUploadInput[]>;
@@ -1,6 +1,12 @@
1
1
  import type { Static, TSchema } from "@sinclair/typebox";
2
2
  import { type ArtifactCapability } from "./types";
3
+ export type ArtifactAssetPolicy = {
4
+ /** Exact media types or wildcards such as image/* and application/*. */
5
+ acceptedMediaTypes?: string[];
6
+ maxCount?: number;
7
+ };
3
8
  export type ArtifactKindDefinition<TContent extends TSchema = TSchema> = {
9
+ assets?: ArtifactAssetPolicy;
4
10
  capabilities?: ArtifactCapability[];
5
11
  content: TContent;
6
12
  description?: string;
@@ -1,6 +1,6 @@
1
1
  import type { ArtifactKindDefinitions, ArtifactRegistry } from "./registry";
2
- import type { ArtifactStore } from "./store";
3
- import { type ArtifactCreateInput, type ArtifactListQuery, type ArtifactRecord, type ArtifactUpdateInput } from "./types";
2
+ import type { ArtifactAssetStore, ArtifactStore } from "./store";
3
+ import { type ArtifactAssetReference, type ArtifactAssetWriteInput, type ArtifactCreateInput, type ArtifactListQuery, type ArtifactRecord, type ArtifactUpdateInput } from "./types";
4
4
  export type ArtifactPublisher = {
5
5
  publish(artifact: ArtifactRecord, options: {
6
6
  idempotencyKey: string;
@@ -13,6 +13,7 @@ export type ArtifactPublisher = {
13
13
  }): Promise<void>;
14
14
  };
15
15
  export type ArtifactServiceOptions<TDefinitions extends ArtifactKindDefinitions = ArtifactKindDefinitions> = {
16
+ assetStore?: ArtifactAssetStore;
16
17
  clock?: () => Date;
17
18
  idFactory?: () => string;
18
19
  publisher?: ArtifactPublisher;
@@ -22,10 +23,19 @@ export type ArtifactServiceOptions<TDefinitions extends ArtifactKindDefinitions
22
23
  export type ArtifactService = ReturnType<typeof createArtifactService>;
23
24
  export declare const createArtifactService: <TDefinitions extends ArtifactKindDefinitions>(options: ArtifactServiceOptions<TDefinitions>) => {
24
25
  archive: (ownerId: string, artifactId: string) => Promise<ArtifactRecord>;
26
+ attach: (ownerId: string, artifactId: string, input: ArtifactAssetWriteInput, expectedRevision?: number) => Promise<ArtifactRecord>;
25
27
  create: (ownerId: string, input: ArtifactCreateInput) => Promise<ArtifactRecord>;
28
+ detach: (ownerId: string, artifactId: string, assetId: string, expectedRevision?: number) => Promise<ArtifactRecord>;
26
29
  get: (ownerId: string, artifactId: string) => Promise<ArtifactRecord>;
30
+ getRevision: (ownerId: string, artifactId: string, revision: number) => Promise<Readonly<ArtifactRecord<unknown>>>;
27
31
  list: (ownerId: string, query?: ArtifactListQuery) => Promise<ArtifactRecord[]>;
32
+ listRevisions: (ownerId: string, artifactId: string) => Promise<Readonly<ArtifactRecord<unknown>>[]>;
28
33
  publish: (ownerId: string, artifactId: string) => Promise<ArtifactRecord>;
29
34
  unpublish: (ownerId: string, artifactId: string) => Promise<ArtifactRecord>;
35
+ readAsset: (ownerId: string, artifactId: string, assetId: string) => Promise<{
36
+ asset: ArtifactAssetReference;
37
+ data: Uint8Array<ArrayBufferLike>;
38
+ }>;
39
+ restore: (ownerId: string, artifactId: string, revision: number, expectedRevision?: number) => Promise<ArtifactRecord>;
30
40
  update: (ownerId: string, artifactId: string, input: ArtifactUpdateInput) => Promise<ArtifactRecord>;
31
41
  };
@@ -0,0 +1,157 @@
1
+ export declare const standardArtifactDefinitions: {
2
+ archive: {
3
+ assets: {
4
+ acceptedMediaTypes: string[];
5
+ maxCount: number;
6
+ };
7
+ capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
8
+ content: import("@sinclair/typebox").TObject<{
9
+ description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
10
+ instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
11
+ summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
12
+ }>;
13
+ label: string;
14
+ schemaVersion: number;
15
+ };
16
+ audio: {
17
+ assets: {
18
+ acceptedMediaTypes: string[];
19
+ maxCount: number;
20
+ };
21
+ capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
22
+ content: import("@sinclair/typebox").TObject<{
23
+ description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
24
+ instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
25
+ summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
26
+ }>;
27
+ label: string;
28
+ schemaVersion: number;
29
+ };
30
+ code: {
31
+ assets: {
32
+ acceptedMediaTypes: string[];
33
+ maxCount: number;
34
+ };
35
+ capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
36
+ content: import("@sinclair/typebox").TObject<{
37
+ description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
38
+ instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
39
+ summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
40
+ }>;
41
+ label: string;
42
+ schemaVersion: number;
43
+ };
44
+ dataset: {
45
+ assets: {
46
+ acceptedMediaTypes: string[];
47
+ maxCount: number;
48
+ };
49
+ capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
50
+ content: import("@sinclair/typebox").TObject<{
51
+ description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
52
+ instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
53
+ summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
54
+ }>;
55
+ label: string;
56
+ schemaVersion: number;
57
+ };
58
+ document: {
59
+ assets: {
60
+ acceptedMediaTypes: string[];
61
+ maxCount: number;
62
+ };
63
+ capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
64
+ content: import("@sinclair/typebox").TObject<{
65
+ description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
66
+ instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
67
+ summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
68
+ }>;
69
+ label: string;
70
+ schemaVersion: number;
71
+ };
72
+ email: {
73
+ assets: {
74
+ acceptedMediaTypes: string[];
75
+ maxCount: number;
76
+ };
77
+ capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
78
+ content: import("@sinclair/typebox").TObject<{
79
+ description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
80
+ instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
81
+ summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
82
+ }>;
83
+ label: string;
84
+ schemaVersion: number;
85
+ };
86
+ file: {
87
+ assets: {
88
+ acceptedMediaTypes: string[];
89
+ maxCount: number;
90
+ };
91
+ capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
92
+ content: import("@sinclair/typebox").TObject<{
93
+ description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
94
+ instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
95
+ summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
96
+ }>;
97
+ label: string;
98
+ schemaVersion: number;
99
+ };
100
+ image: {
101
+ assets: {
102
+ acceptedMediaTypes: string[];
103
+ maxCount: number;
104
+ };
105
+ capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
106
+ content: import("@sinclair/typebox").TObject<{
107
+ description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
108
+ instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
109
+ summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
110
+ }>;
111
+ label: string;
112
+ schemaVersion: number;
113
+ };
114
+ presentation: {
115
+ assets: {
116
+ acceptedMediaTypes: string[];
117
+ maxCount: number;
118
+ };
119
+ capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
120
+ content: import("@sinclair/typebox").TObject<{
121
+ description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
122
+ instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
123
+ summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
124
+ }>;
125
+ label: string;
126
+ schemaVersion: number;
127
+ };
128
+ spreadsheet: {
129
+ assets: {
130
+ acceptedMediaTypes: string[];
131
+ maxCount: number;
132
+ };
133
+ capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
134
+ content: import("@sinclair/typebox").TObject<{
135
+ description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
136
+ instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
137
+ summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
138
+ }>;
139
+ label: string;
140
+ schemaVersion: number;
141
+ };
142
+ video: {
143
+ assets: {
144
+ acceptedMediaTypes: string[];
145
+ maxCount: number;
146
+ };
147
+ capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
148
+ content: import("@sinclair/typebox").TObject<{
149
+ description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
150
+ instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
151
+ summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
152
+ }>;
153
+ label: string;
154
+ schemaVersion: number;
155
+ };
156
+ };
157
+ export declare const STANDARD_ARTIFACT_KIND_NAMES: Array<keyof typeof standardArtifactDefinitions>;
@@ -1,8 +1,22 @@
1
- import type { ArtifactListQuery, ArtifactRecord } from "./types";
1
+ import type { ArtifactAssetReference, ArtifactAssetWriteInput, ArtifactListQuery, ArtifactRecord, ArtifactRevision } from "./types";
2
+ export type ArtifactAssetStore = {
3
+ read(reference: ArtifactAssetReference, context: {
4
+ artifact: ArtifactRecord;
5
+ }): Promise<Uint8Array>;
6
+ write(input: ArtifactAssetWriteInput, context: {
7
+ artifact: ArtifactRecord;
8
+ idempotencyKey: string;
9
+ }): Promise<ArtifactAssetReference>;
10
+ };
2
11
  export type ArtifactStore = {
12
+ /** Persist the current record and its first immutable revision atomically. */
3
13
  create(record: ArtifactRecord): Promise<void>;
4
14
  get(ownerId: string, artifactId: string): Promise<ArtifactRecord | null>;
15
+ getRevision(ownerId: string, artifactId: string, revision: number): Promise<ArtifactRevision | null>;
5
16
  list(ownerId: string, query?: ArtifactListQuery): Promise<ArtifactRecord[]>;
17
+ listRevisions(ownerId: string, artifactId: string): Promise<ArtifactRevision[]>;
18
+ /** Compare, replace current state, and append its revision atomically. */
6
19
  save(record: ArtifactRecord, expectedRevision: number): Promise<boolean>;
7
20
  };
21
+ export declare const createMemoryArtifactAssetStore: () => ArtifactAssetStore;
8
22
  export declare const createMemoryArtifactStore: (initial?: ArtifactRecord[]) => ArtifactStore;
@@ -1,6 +1,6 @@
1
1
  export declare const ARTIFACT_STATUSES: readonly ["draft", "published", "archived"];
2
2
  export type ArtifactStatus = (typeof ARTIFACT_STATUSES)[number];
3
- export type ArtifactCapability = "archive" | "edit" | "export" | "preview" | "publish" | "refine";
3
+ export type ArtifactCapability = "attach" | "archive" | "edit" | "export" | "preview" | "publish" | "refine";
4
4
  export type ArtifactProvenance = {
5
5
  model?: string;
6
6
  sourceIds?: string[];
@@ -12,7 +12,30 @@ export type ArtifactPublication = {
12
12
  publishedAt: string;
13
13
  url: string;
14
14
  };
15
+ export type ArtifactAssetRole = "attachment" | "primary" | "preview" | "source";
16
+ export type ArtifactAssetReference = {
17
+ checksum?: {
18
+ algorithm: "sha256";
19
+ value: string;
20
+ };
21
+ id: string;
22
+ mediaType: string;
23
+ metadata?: Record<string, unknown>;
24
+ name: string;
25
+ role: ArtifactAssetRole;
26
+ size: number;
27
+ /** Opaque host storage locator. It is not required to be publicly fetchable. */
28
+ uri: string;
29
+ };
30
+ export type ArtifactAssetWriteInput = {
31
+ data: Uint8Array;
32
+ mediaType: string;
33
+ metadata?: Record<string, unknown>;
34
+ name: string;
35
+ role?: ArtifactAssetRole;
36
+ };
15
37
  export type ArtifactRecord<TContent = unknown> = {
38
+ assets: ArtifactAssetReference[];
16
39
  capabilities: ArtifactCapability[];
17
40
  content: TContent;
18
41
  createdAt: string;
@@ -29,12 +52,15 @@ export type ArtifactRecord<TContent = unknown> = {
29
52
  title: string;
30
53
  updatedAt: string;
31
54
  };
55
+ /** An immutable point-in-time copy of an artifact record. */
56
+ export type ArtifactRevision<TContent = unknown> = Readonly<ArtifactRecord<TContent>>;
32
57
  export type ArtifactListQuery = {
33
58
  kind?: string;
34
59
  limit?: number;
35
60
  status?: ArtifactStatus;
36
61
  };
37
62
  export type ArtifactCreateInput = {
63
+ assets?: ArtifactAssetReference[];
38
64
  content: unknown;
39
65
  createdBy: string;
40
66
  kind: string;
@@ -43,12 +69,13 @@ export type ArtifactCreateInput = {
43
69
  title: string;
44
70
  };
45
71
  export type ArtifactUpdateInput = {
72
+ assets?: ArtifactAssetReference[];
46
73
  content?: unknown;
47
74
  expectedRevision?: number;
48
75
  metadata?: Record<string, unknown>;
49
76
  title?: string;
50
77
  };
51
- export type ArtifactErrorCode = "conflict" | "invalid_content" | "not_found" | "publisher_unavailable" | "renderer_unavailable" | "unsupported_capability" | "unknown_kind";
78
+ export type ArtifactErrorCode = "asset_store_unavailable" | "conflict" | "invalid_content" | "not_found" | "publisher_unavailable" | "renderer_unavailable" | "unsupported_capability" | "unknown_kind";
52
79
  export declare class ArtifactError extends Error {
53
80
  readonly code: ArtifactErrorCode;
54
81
  constructor(code: ArtifactErrorCode, message: string);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/artifacts",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Typed, versioned artifacts for AI products — schemas, lifecycle, storage, rendering, publishing, revisions, and agent tools without prescribing a database or host.",
5
5
  "author": "Alex Kahn",
6
6
  "license": "BUSL-1.1",
@@ -14,14 +14,14 @@
14
14
  ],
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": "https://github.com/absolutejs/artifacts.git"
17
+ "url": "git+https://github.com/absolutejs/artifacts.git"
18
18
  },
19
19
  "homepage": "https://github.com/absolutejs/artifacts",
20
20
  "bugs": {
21
21
  "url": "https://github.com/absolutejs/artifacts/issues"
22
22
  },
23
23
  "scripts": {
24
- "build": "rm -rf dist && bun build src/index.ts src/manifest.ts --outdir dist --root ./src --target=bun --external @sinclair/typebox --external @sinclair/typebox/value && tsc --emitDeclarationOnly --project tsconfig.json && absolute-manifest emit",
24
+ "build": "rm -rf dist && bun build src/index.ts src/manifest.ts src/rag.ts --outdir dist --root ./src --target=bun --external @absolutejs/rag --external @sinclair/typebox --external @sinclair/typebox/value && tsc --emitDeclarationOnly --project tsconfig.json && absolute-manifest emit",
25
25
  "format": "prettier --write \"./**/*.{ts,json,md}\"",
26
26
  "release": "bun run format && bun run test && bun run build && bun publish",
27
27
  "test": "bun test",
@@ -44,13 +44,27 @@
44
44
  "import": "./dist/manifest.js",
45
45
  "default": "./dist/manifest.js"
46
46
  },
47
- "./manifest.json": "./dist/manifest.json"
47
+ "./manifest.json": "./dist/manifest.json",
48
+ "./rag": {
49
+ "types": "./dist/src/rag.d.ts",
50
+ "import": "./dist/rag.js",
51
+ "default": "./dist/rag.js"
52
+ }
48
53
  },
49
54
  "dependencies": {
50
55
  "@absolutejs/manifest": "^0.1.0",
51
56
  "@sinclair/typebox": "^0.34.0"
52
57
  },
58
+ "peerDependencies": {
59
+ "@absolutejs/rag": ">=0.0.29"
60
+ },
61
+ "peerDependenciesMeta": {
62
+ "@absolutejs/rag": {
63
+ "optional": true
64
+ }
65
+ },
53
66
  "devDependencies": {
67
+ "@absolutejs/rag": "0.0.29",
54
68
  "@types/bun": "latest",
55
69
  "prettier": "3.5.3",
56
70
  "typescript": "5.8.3"