Skip to content

Artifacts

Artifacts are typed, versioned outputs produced by an agent run — files, reports, code, structured data, plans, reasoning traces — that should persist beyond the message text.

ts
import {
  createTextArtifact,
  createMarkdownArtifact,
  createDataArtifact,
  createPlanArtifact,
  createReasoningArtifact,
  InMemoryArtifactStorage,
} from 'personaforge/artifacts';

Artifact types

ts
type ArtifactType =
  | 'file'
  | 'image'
  | 'audio'
  | 'video'
  | 'code'
  | 'data'
  | 'document'
  | 'markdown'
  | 'json'
  | 'reasoning'
  | 'plan'
  | 'report';

Create artifacts

ts
import {
  createTextArtifact,
  createMarkdownArtifact,
  createDataArtifact,
  createPlanArtifact,
  createReasoningArtifact,
} from 'personaforge/artifacts';

// Plain text / code file
const code = createTextArtifact({
  name: 'auth-handler.ts',
  content: `export function verifyToken(token: string) { ... }`,
  type: 'code',
  mimeType: 'text/typescript',
  tags: ['auth', 'typescript'],
  createdBy: 'code-agent',
});

// Markdown report
const report = createMarkdownArtifact({
  name: 'Q4-report.md',
  content: '## Q4 Summary\n\nRevenue up 12% YoY...',
  tags: ['report', 'q4'],
});

// Structured data
const data = createDataArtifact({
  name: 'search-results',
  content: { query: 'LLM benchmarks', results: [...] },
  type: 'json',
});

// Agent reasoning trace
const trace = createReasoningArtifact({
  steps: [
    { title: 'Analyse', action: 'Read the requirements', result: '...', confidence: 0.9 },
  ],
  conclusion: 'Use a queue-based approach.',
  model: 'gpt-4o',
});

// Execution plan
const plan = createPlanArtifact({
  goal: 'Migrate database to PostgreSQL',
  tasks: [
    { id: '1', name: 'Backup current DB', priority: 0 },
    { id: '2', name: 'Provision RDS', priority: 1, dependencies: ['1'] },
  ],
});

ArtifactMetadata fields

All artifacts share these fields:

ts
interface ArtifactMetadata {
  id: string;             // auto-generated UUID
  name: string;           // human-readable name
  type: ArtifactType;
  mimeType?: string;
  sizeBytes?: number;
  createdAt: Date;
  updatedAt: Date;
  version: number;        // starts at 1
  tags?: string[];
  metadata?: Record<string, unknown>;  // custom key-value pairs
  createdBy?: string;     // agent name
  sessionId?: string;
}

Store artifacts

ts
import { InMemoryArtifactStorage } from 'personaforge/artifacts';

const storage = new InMemoryArtifactStorage({
  maxSizeBytes: 100 * 1024 * 1024,  // 100 MB per artifact (default)
  versioning: true,                 // keep a full version history (default: true)
  // basePath?, ttlMs?, metrics? — see ArtifactStorageConfig
});

// Save (creates version 1; id/createdAt/version are generated)
const stored = await storage.save(report);

// Retrieve
const retrieved = await storage.get(stored.id);

// List by type
const allReports = await storage.list({
  type: 'markdown',
  tags: ['q4'],
  createdBy: 'report-agent',
});

// Search
const results = await storage.search('Q4 revenue');

// Delete
await storage.delete(stored.id);

Emit artifacts from agent hooks

Attach artifact creation to the afterRun hook to capture every run's output:

ts
import { createAgent } from 'personaforge';
import { InMemoryArtifactStorage, createMarkdownArtifact } from 'personaforge/artifacts';

const artifactStorage = new InMemoryArtifactStorage();

const agent = createAgent({
  name: 'report-agent',
  instructions: 'Generate detailed reports when asked.',
  model: 'gpt-4o',
  apiKey: process.env.OPENAI_API_KEY!,
  hooks: {
    afterRun: async (result) => {
      // Persist agent text output as a markdown artifact
      const artifact = createMarkdownArtifact({
        name: `report-${result.runId}.md`,
        content: result.text,
        createdBy: 'report-agent',
        metadata: { runId: result.runId, tokens: result.usage?.totalTokens },
      });
      await artifactStorage.save(artifact);
      return result;
    },
  },
});

ArtifactStorage interface

Implement this to persist artifacts to S3, GCS, or any external store:

ts
interface ArtifactStorage {
  save<T>(artifact: Omit<Artifact<T>, 'id' | 'createdAt' | 'updatedAt' | 'version'>): Promise<Artifact<T>>;
  get<T>(id: string): Promise<Artifact<T> | null>;
  getVersion<T>(id: string, version: number): Promise<Artifact<T> | null>;
  listVersions(id: string): Promise<ArtifactMetadata[]>;
  update<T>(id: string, updates: Partial<Omit<Artifact<T>, 'id' | 'createdAt' | 'version'>>): Promise<Artifact<T>>;
  delete(id: string): Promise<boolean>;
  list(filters?: {
    type?: ArtifactType;
    tags?: string[];
    createdBy?: string;
    sessionId?: string;
    limit?: number;
    offset?: number;
  }): Promise<ArtifactMetadata[]>;
  search(query: string, limit?: number): Promise<ArtifactMetadata[]>;
}

Versioning

Storage keeps a full version history (toggle with versioning in ArtifactStorageConfig). update() creates a new version; getVersion() and listVersions() read the history:

ts
const stored = await storage.save(report);   // version 1

// update() records a new version (2, 3, …)
const v2 = await storage.update(stored.id, {
  content: '## Q4 Summary (revised)\n\nRevenue up 14% YoY...',
});

const history = await storage.listVersions(stored.id);   // ArtifactMetadata[]
const original = await storage.getVersion(stored.id, 1);

Media artifacts

Images, audio, and video are first-class artifact types. Build them with the media helpers, or manage them through MediaManager against any ArtifactStorage.

ts
import {
  MediaManager,
  createImageFromUrl,
  createImageFromBase64,
  createAudioFromUrl,
  createVideoFromUrl,
  InMemoryArtifactStorage,
} from 'personaforge/artifacts';
import type { ImageArtifact, AudioArtifact, VideoArtifact } from 'personaforge/artifacts';

// Build media artifacts directly (each returns an artifact ready for storage.save()):
const image = createImageFromUrl('hero.png', 'https://cdn.example.com/hero.png', {
  width: 1024, height: 768, prompt: 'a mountain at sunrise', model: 'dall-e-3',
});
const inline = createImageFromBase64('chart.png', base64Data, 'image/png');
const speech = createAudioFromUrl('greeting.mp3', 'https://cdn.example.com/greeting.mp3', {
  durationSeconds: 3.2, voiceId: 'alloy', transcript: 'Hello there.',
});
const clip = createVideoFromUrl('demo.mp4', 'https://cdn.example.com/demo.mp4', {
  durationSeconds: 30, width: 1920, height: 1080, fps: 30,
});

// …or use MediaManager to save + retrieve in one call:
const media = new MediaManager(new InMemoryArtifactStorage());
const savedImage: ImageArtifact = await media.saveImage('hero.png', 'https://cdn.example.com/hero.png', { width: 1024, height: 768 });
const savedAudio: AudioArtifact = await media.saveAudio('greeting.mp3', 'https://cdn.example.com/greeting.mp3');
const savedVideo: VideoArtifact = await media.saveVideo('demo.mp4', 'https://cdn.example.com/demo.mp4');

Where to go next

  • Storage — key-value storage for lighter-weight state.
  • HooksafterRun where artifacts are typically created.
  • Production — audit stores for compliance.

Released under the MIT License.