stackbone.config

Typed reads of dynamic per-agent configuration. Operators tweak values in the dashboard (feature flags, thresholds, JSON snippets); your code reads them at runtime through the ambient stackbone client, and the value propagates without republishing.

Mental model

stackbone.config is the read side of the agent's non-secret operational config. It is agent-local: the operator edits values in the dashboard, Stackbone stores them in your agent's own database, and stackbone.config reads them straight out of that database. Values can be any JSON-serialisable shape.

You read config wherever you need it — from inside an agent tool, or from a durable workflow 'use step'. There is no createClient() and no per-invoke handle to wire up: import the ambient client and call it.

import { stackbone } from '@stackbone/sdk';

const result = await stackbone.config.get('greeting');

A config read is a direct query against the agent's own database — no control-plane round-trip, nothing to negotiate. The facade does not cache, so every call re-reads the database and dashboard edits show up on the next read. Because the read goes through the agent database, your agent must have its Postgres connection configured (the runtime injects STACKBONE_POSTGRES_URL for you).

Read a single key

Read one key from inside a tool or a workflow step. get returns a Result envelope — check .error before touching .data.

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

const needsApproval = tool(
  async ({ amountCents }: { amountCents: number }) => {
    const result = await stackbone.config.get('refund_threshold_cents');
    if (result.error) throw new Error(result.error.code);

    const threshold = result.data; // number, once typed (see below)
    return JSON.stringify({ needsApproval: amountCents > threshold });
  },
  {
    name: 'needs_approval',
    description: 'Decide whether a refund needs human approval.',
    schema: z.object({ amountCents: z.number() }),
  },
);

The same call works unchanged inside a durable workflow step:

async function loadPolicy() {
  'use step';
  const result = await stackbone.config.get('refund_threshold_cents');
  if (result.error) throw new Error(result.error.code);
  return result.data;
}

get rejects empty keys with config_invalid_request. A key the agent does not have set surfaces config_not_found.

Read many at once

getMany takes a list of keys and returns whatever the agent DB actually has:

import { stackbone } from '@stackbone/sdk';

const result = await stackbone.config.getMany(['greeting', 'retries', 'features']);
if (result.error) throw new Error(result.error.code);

console.log(result.data.greeting); // string | undefined

Keys absent from the agent's config come back as omissions in the returned PartialgetMany never fails just because one key is unset. Access fields with ?. and provide defaults at the call site.

Read the whole config

getAll returns the agent's entire config payload as one object. When no config is set yet, it resolves to an empty object ({}), never an error:

const result = await stackbone.config.getAll();
if (result.error) throw new Error(result.error.code);

const config = result.data; // {} when nothing is set

Typed config

By default, config reads are loose: keys are string and values are unknown. To get autocompletion and compile-time key checking, declare the shape once in a config.schema.ts at your project root, exporting a Zod schema named configSchema:

config.schema.ts
import { z } from '@stackbone/sdk';

export const configSchema = z.object({
  greeting: z.string(),
  retries: z.number().int(),
  refund_threshold_cents: z.number().int(),
  features: z.object({ betaInbox: z.boolean() }),
});

That one file drives three things at once: the form the operator sees in the dashboard, the validation applied when they save, and your types. Generate the types with:

stackbone config types

This emits .stackbone/config.d.ts in your project. (stackbone dev regenerates it automatically on every config.schema.ts change, so you usually never run config types by hand.) The generated file augments the SDK's ConfigRegistry interface:

// .stackbone/config.d.ts — generated, do not edit
declare module '@stackbone/sdk' {
  interface ConfigRegistry extends StackboneAgentConfig {}
}

Once that file exists, stackbone.config.get('greeting') is typed string, get('refund_threshold_cents') is typed number, and get('unknownKey') is a compile error. getMany and getAll become strictly keyed against the schema too.

Add .stackbone/ to your .gitignore — it is regenerated locally and does not need to be committed.

No config.schema.ts? Reads stay loose — values come back as unknown. You can pass a per-call type as the fallback, e.g. (await stackbone.config.get('greeting')).data as string, but the schema-driven path is the recommended one.

Setting config

The values your code reads come from a single versioned config document. Operators edit it in the dashboard, but you can also drive it from the CLI:

Command What it does
stackbone config get Print the active config document (value, version, author).
stackbone config set --file cfg.json Save a new version from a JSON object (or pipe it on stdin).
stackbone config versions List recent versions, newest first.
stackbone config rollback --version N --yes Roll the active config back to a prior version.

Every set appends a new version; nothing is edited in place, so you can always roll back. The agent reads the active version on its next config read.

Errors

Config reads hit the agent database, so failures are database-shaped rather than HTTP-shaped.

Code When
config_invalid_request Empty key, or an empty keys array.
config_not_found get(key) and the agent has no value for that key.
config_unavailable The read against the agent database failed.
database_not_configured The agent has no Postgres connection configured (STACKBONE_POSTGRES_URL unset).

Where to go next

  • stackbone.secrets — same ambient pattern, but for encrypted credentials.
  • stackbone.approval — pair config thresholds with a human-approval gate so a value change can flip an action from automatic to "needs approval" without republishing.
  • Agents & sessions and Workflows — the two places your config reads actually run.

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

© 2026 · STACKBONEBUILT WITH ❤️ FROM CANADA AND SPAIN