SDK · @stackbone/sdk

The whole agent stack, in one SDK.

Build a workspace of agents and durable workflows. One typed client wires in your database, RAG, storage, models, secrets and human approval — the same code on your laptop and in the cloud.

  • 13 surfaces
  • durable workflows
  • zero keys to manage
workflows/refund.workflow.ts
import { requestApproval } from '@stackbone/sdk/workflow' export async function refundWorkflow(input) {  'use workflow'  const decision = await requestApproval({    token: `refund-${input.orderId}`,    topic: 'refund', timeout: '24h', fallback: 'reject',  })  if (decision.status !== 'approved') return { refunded: false }   return await performRefund(input.orderId)   // only runs once a human approves}
stackbone dev · workspace ready
01 · Workspace

A project is one folder. Drop an agent in agents/, a workflow in workflows/stackbone dev discovers both by convention. No registry to hand-edit.

my-workspace/
my-workspace/  agents/    support/            # one agent per folder    billing/    lead-qualifier/  workflows/    onboarding.workflow.ts   # one durable workflow / file    reconcile.workflow.ts    refund.workflow.ts  package.json
Agent
A model, a system prompt and its own tools. One folder, one deployable agent.
Workflow
Deterministic steps you author, durable per step. Calls agents, gates on humans.
  • stackbone initscaffold a new workspace
  • stackbone add agent <name>add an agent
  • stackbone add workflow <name>add a durable workflow
  • stackbone devrun the whole workspace locally
02 · Agents

An agent is a system prompt, a model and the typed client. Point it at any model on the gateway, write its instructions, give it tools — and it can read your database, search RAG and hand off to other agents.

agents/support/
agents/support/  agent/    instructions.md     # the system prompt    tools/      search_kb.ts     # a tool, gets the client  package.json         # name + model
instructions.md
You are the support agent for an online store. Answer questions about orders and write short,friendly replies. When you need an order status,call the search_kb tool — never invent one.
tools/search_kb.tshas the client
export default defineTool({  description: 'Search the product knowledge base.',  inputSchema: z.object({ query: z.string() }),  async execute({ query }) {    const res = await stackbone.rag.retrieve({ namespace: 'kb', text: query })    return res.data ?? []  },})

Every tool reaches the same ambient stackbone client — database, RAG, storage, secrets and more.

03 · Workflows

Mark a function "use workflow" and each "use step" becomes a durable checkpoint — kill the runtime mid-run and it resumes from the last completed step, not the top.

workflows/onboarding.workflow.ts
import { stackbone } from '@stackbone/sdk' export async function onboardingWorkflow(input) {  'use workflow'  const signup = await validate(input)        // deterministic step  const copy   = await draftWelcome(signup) // a single LLM call  const tips   = await askSupport(signup)   // hand off to an agent  return persist(signup, { ...copy, tips })} async function askSupport(signup) {  'use step'  const session = stackbone.agent('support').session()  const res = await session.send({ message: `Onboarding tips for ${signup.plan}?` })  return (await res.result()).data}

The contract is a sibling inputSchema / outputSchema — no wrapper, no second type to drift.

  • requestApproval({ token, topic, payload, timeout })pause the run for a human decision
  • stackbone.workflows.start(name, input)kick off another workflow, fire-and-forget
  • stackbone.workflows.startAndWait(name, input)run another workflow as a durable sub-routine
  • stackbone.workflows.schedule(name, input, cron)run a workflow on a cron schedule
  • ingestDocuments({ collection, storageKey })ingest files into RAG from a step

Composition, approval, scheduling and RAG ingest all import from @stackbone/sdk/workflow.

04 · One client

One import, every surface. The same stackbone handle from any tool

  1. Retrieval

    Ingest documents and retrieve by meaning — pgvector, auto-embedded, ranked by similarity.

    const res = await stackbone.rag.retrieve({  namespace: 'kb',  text: question,  model: 'openai/text-embedding-3-small',})res.data  // RetrieveHit[], ranked by similarity

    → Result<RetrieveHit[]>
    auto-embeds the query; the model must match ingest

  2. Agent hand-off

    Hand a turn to any other agent in the workspace, by name. Sessions are durable and resumable.

    const session = stackbone.agent('support').session()const res = await session.send({  message: 'Where is order #2231?',})const answer = (await res.result()).data

    → a durable, resumable session turn
    workflows orchestrate; agents converse

  3. Database

    The agent's own Postgres, as native Drizzle. Your schema, your migrations, full SQL.

    await stackbone.database  .select()  .from(orders)  .where(eq(orders.id, id))// native Drizzle — your schema, your SQL

    → Drizzle rows (throws on misuse)
    the one surface that is not a Result — it throws like Drizzle

  4. Models

    Every model behind the managed gateway, plus image generation. No provider keys to wire.

    const res = await stackbone.ai.chat.completions.create({  model: 'anthropic/claude-haiku-4.5',  messages,})// also stackbone.ai.images.generate(...)

    → Result<…>
    one managed OpenRouter gateway, every model

  5. Object storage

    S3-compatible storage on R2 or MinIO — upload, download and signed URLs.

    const res = await stackbone.storage.upload(key, body)if (res.error) return resres.data  // { key, etag? }

    → Result<{ key; etag? }>

  6. Human approval

    Open a human-in-the-loop gate and resume the moment a person decides.

    await stackbone.approval.request({  topic: 'refund',  payload: { orderId, amount },  onDecide,})

    → Result<ApprovalRequest>
    or requestApproval(...) straight from a workflow

  7. Secrets

    Injected at runtime by the control plane. Read them; never paste them.

    const res = await stackbone.secrets.get('STRIPE_KEY')res.data  // string — injected, never pasted

    → Result<string>

  8. Config

    Per-agent configuration, typed and resolved from the control plane.

    const res = await stackbone.config.get('greeting')res.data  // typed per-agent config value

    → Result<…>

  9. Observability

    OpenTelemetry spans for every call, exported out of the box.

    // OpenTelemetry spans for every call,// exported out of the box.stackbone.observability

    → OpenTelemetry spans

05 · Local = cloud

The same ambient stackbone client runs on your laptop and in the cloud. You never paste a key.

import { stackbone } from '@stackbone/sdk'   // no keys, no config await stackbone.secrets.get('STRIPE_KEY')   // injected, never pastedawait stackbone.ai.chat.completions.create(…)
context:stackbone dev → emulatorlocal
context:provisioned install → cloudlive

the credentials change. the code doesn't.

Write the agents. The SDK is already wired.

The platform for agent developers
Write the agent.
Skip the plumbing.

© 2026 · STACKBONE BUILT WITH ❤️ FROM CANADA AND SPAIN