stackbone.prompts

stackbone.prompts reads and writes a versioned, key-addressed prompt catalog straight out of the agent's own Postgres (stackbone_platform.prompts / prompt_versions), over the same pool as stackbone.database. There is no control-plane round-trip — the agent owns its data, exactly like stackbone.secrets and stackbone.config. You reach it through the ambient client (import { stackbone } from '@stackbone/sdk'), the same handle you use from any agent tool or durable workflow step.

Each prompt is addressed by a workspace-unique key (summariser, welcome_email, tool_describe_orders) and carries a human-friendly name. Each update(...) appends a new immutable version under that key; the latest version is what get(key) and compile(key, vars) return by default. Pinning a specific version is supported via the { version } option.

Where it fits

A deep agent already has a static, always-on system prompt: the systemPrompt string passed to defineDeepAgent({ ... }) in its index.ts. That is the agent's persona and never changes at runtime, it ships with the build. See Agents for the authoring model.

stackbone.prompts is for the dynamic side: operator-editable, versioned, A/B-routable templates your code fetches and compiles at runtime, no redeploy. Reach for it when a prompt needs to be reviewed, diffed, rolled back, or split-tested without rebuilding the container — e.g. the body of a notification, a summarisation template, or a per-tool instruction your agent renders on the fly.

It is agent-local and peer-free: it reads the agent's own database and pulls in no agent-authoring or workflow dependency, so a workflow-only project can import it without dragging the deep-agent runtime along.

Why it exists

Prompts are the most-edited, least-versioned piece of an agent's behaviour. Without a platform surface, every agent either hardcodes them, embeds JSON files in the project, or invents its own A/B routing. That defeats the "platform feature" promise: prompts must be reviewable, diffable and rollback-able without rebuilding the container.

No capability gate

Like stackbone.secrets and stackbone.config, this surface is agent-local: it reads and writes the agent's own database tables directly, so it is not gated on the contract handshake. The only prerequisite is that the prompts schema has been applied. Those tables are created when your project's migrations run — which happens automatically when you boot your project with stackbone dev (and again when you stackbone publish). A not-yet-migrated agent surfaces prompts_not_configured until then.

API

Every method returns the standard Result<T> envelope ({ data, error: null } | { data: null, error: SdkError }). Import the ambient client and call the accessor — the snippets below show the call site inside an agent tool, but the same calls work verbatim inside a durable workflow "use step".

get(key, options?)

Returns the stored prompt (template + version metadata). The latest version by default; pin one via { version }.

import { tool } from '@langchain/core/tools';
import { stackbone, z } from '@stackbone/sdk';

const fetchSummariserTemplate = tool(
  async () => {
    const { data, error } = await stackbone.prompts.get('summariser');
    if (error) throw error;
    return JSON.stringify({
      template: data.template, // raw template with {{var}} placeholders
      version: data.version, // monotonically increasing per key
    });
  },
  {
    name: 'fetch_summariser_template',
    description: 'Fetch the summariser template.',
    schema: z.object({}),
  },
);

Pinning a version:

const { data } = await stackbone.prompts.get('summariser', { version: 3 });

compile(key, vars, options?)

Fetches the prompt and runs the Mustache-subset compile engine against vars. Returns { output, version } — the rendered string and the version it ran against.

const { data: rendered, error } = await stackbone.prompts.compile('summariser', {
  article: 'Stackbone makes agents shippable...',
});

Pinning a version (e.g. for an A/B split):

await stackbone.prompts.compile('summariser', { article }, { version: 3 });

Feeding a compiled prompt to a model

The point of compiling is to send the rendered string to an LLM. Pair compile(...) with stackbone.ai, which routes the call to a model through the Vercel AI Gateway / OpenRouter:

async function summarise(article: string) {
  'use step';
  const { data, error } = await stackbone.prompts.compile('summariser', { article });
  if (error) throw error;

  const completion = await stackbone.ai.chat.completions.create({
    model: 'anthropic/claude-haiku-4.5',
    messages: [{ role: 'user', content: data.output }],
  });
  return { summary: completion.choices[0]?.message?.content ?? '' };
}

The compiled string is just text — feed it to stackbone.ai, to the Vercel AI SDK (generateText / streamText), or anywhere else you call a model.

list(options?)

Lists the current version of every (non-deleted) prompt, ordered by key and capped by limit (1–100, default 50).

const { data } = await stackbone.prompts.list({ limit: 50 });
for (const prompt of data.items) {
  console.log(prompt.key, '@v' + prompt.version);
}

create(request)

Registers a new prompt at version 1. key and name are required; a duplicate key errors with prompts_already_exists.

await stackbone.prompts.create({
  key: 'welcome_email',
  name: 'Welcome email',
  template: 'Hello {{name}}, welcome to {{product}}.',
});

update(key, options)

Pass a new template to append a new immutable version; name, description and metadata can be updated in place without bumping the version. History is append-only — past versions are kept for audit and rollback. Concurrent updates to the same key are serialized, so two parallel writers can never collide on the next version number.

await stackbone.prompts.update('welcome_email', {
  template: 'Hi {{name}} — welcome to {{product}}.',
});

delete(key, options?)

Soft-deletes the prompt — every version under key stops resolving but the rows stay for audit. ({ version } is reserved for future per-version deletes and is ignored today.) Returns { key, deleted }, where deleted is 1 when a live prompt was removed and 0 when the key was already gone.

The compile engine

compile(...) runs a Mustache-subset. The only template syntax is {{var}} where var matches [a-zA-Z_][a-zA-Z0-9_]*. There are no conditionals, no loops, no helpers, no comments.

Construct Behaviour
{{var}} Substituted with String(vars.var).
{{#if x}}…{{/if}} Treated as a literal — not parsed.
{{!comment}} Treated as a literal.
{{var | filter}} Treated as a literal.
Missing vars.var Errors with code prompts_missing_var.
Extra fields in vars Silently ignored.

This decision is frozen. Any expansion (conditionals, helpers) requires a documented contract change.

Errors

Code Meaning
prompts_invalid_request A required field was missing or empty (key, name, template), or compile() hit a template that failed to compile for a reason other than a missing variable (reserved — unreachable with today's engine).
prompts_not_found The prompt does not exist, was soft-deleted, or the pinned { version } does not exist. meta.key.
prompts_already_exists create() was called with a key that is already registered. meta.key.
prompts_missing_var compile() was called without a value for a {{var}}. meta.name.
prompts_not_configured The prompts schema is not applied — boot your project with stackbone dev to run the migrations.
prompts_unavailable The agent database handle was unavailable or a read/write failed for another reason.
database_not_configured The agent has no Postgres pool (STACKBONE_POSTGRES_URL not injected).

Where to go next

  • stackbone.ai — the model client compile(...) feeds into.
  • stackbone.database — the Postgres that owns the prompts and prompt_versions tables this surface reads and writes.
  • stackbone.secrets and stackbone.config — the other agent-local surfaces that read straight from the agent's own database.
  • Agents — the deep-agent authoring model: the inline systemPrompt as the static system prompt vs dynamic prompts served here.

The platform for agent developers.Build, ship and observe agentsthat survive prod.

© 2026 · STACKBONEBUILT WITH ❤️ FROM CANADA AND SPAIN