Artifacts
Artifacts are durable outputs such as reviewed pull requests, reports, or decision records. An artifact kind defines the data it accepts, and the artifacts API tags or updates records by key. Records persist across sessions and can be listed, streamed, or downloaded.
Artifact kinds
Author bot/artifacts.ts with defineArtifacts from
@cursor/bdk/artifacts:
import { z } from "zod";import { defineArtifacts } from "@cursor/bdk/artifacts";export default defineArtifacts({ kinds: { "reviewed-pr": { description: "A pull request this agent reviewed.", schema: z.object({ url: z.string(), verdict: z.string() }), }, report: { description: "A generated report." }, }, agentTool: true,});| Option | Contract |
|---|---|
kinds | Map of accepted kind names to a non-empty description and optional Zod schema |
agentTool | Expose tag_artifact to the model; requires at least one declared kind |
max | Positive retention cap, default 1000. Local and self-hosted servers evict the oldest-updated record; hosted deployments don't evict. |
With declared kinds, tag rejects any other kind. With no registry, it
accepts free-form kind names and defaults an omitted kind to
"artifact". A kind's schema validates data, and the parsed value is
stored, including schema defaults and coercions.
Tag artifacts from host code
Tools, hooks, channel handlers, channel onStart, schedules, and
reminders receive an ArtifactsApi with tag and list.
await ctx.artifacts.tag({ kind: "reviewed-pr", key: prUrl, title: `Reviewed ${prUrl}`, data: { url: prUrl, verdict: "approve" },});| Tag field | Contract |
|---|---|
data | Required JSON data, validated when the kind has a schema |
kind | Declared or free-form kind |
key | Agent-wide upsert key; the same key updates one record across kinds |
title | Optional display title |
contents | String or bytes served by the content route |
contentType | MIME type for contents |
sessionId, turnId | Attribute the artifact to a session or turn |
source | "host" or "model"; defaults to "host" |
Tool, hook, result, and channel-event contexts are session-bound, so
they fill sessionId and the current turnId. Channel routes,
onStart, schedules, and reminders receive an unbound facade; pass
sessionId to attribute an artifact.
Re-tagging a key without contents keeps its file or blob when the
sessionId stays the same. Rebinding the key to another session without
new contents removes the previous payload. tag returns the stored
ArtifactRecord.
| Record field | Contract |
|---|---|
id | Stable ID derived from key, or a generated ID when no key is set |
kind, data | Validated kind and JSON payload |
key, title | Optional upsert key and display title |
content | Optional { size, contentType? } metadata |
sessionId, turnId | Optional session attribution |
source | "host" or "model" |
createdAt, updatedAt | ISO-8601 timestamps |
Expose tag_artifact to the model
With agentTool: true, the tag_artifact server tool materializes from
the kinds registry. Its input accepts the declared kinds and validates
their data with the same schemas as host-side tagging. The kind
descriptions tell the model which output each one represents.
An authored tool named tag_artifact takes precedence over the generated
tool.
List artifacts
list({ kind?, sessionId? }) returns matching records newest-updated
first. HTTP callers can list records and download content through the
artifact routes.
Stream artifact tags
Tagging an artifact with a sessionId emits artifact.tagged on that
session. Its event data contains id, kind, key, title, data,
and source. See Stream events for the
event envelope.