Agents

A Stackbone agent is an in-process deep agent: it runs inside the runtime, with no subprocess and no port of its own. You author it as one file: its name, a model, and the tools it can call. Its instruction is not in the file. You write that in Studio. The runtime builds the agent once at boot and serves it over the standard OpenAI, Anthropic and AG-UI chat APIs. Any chat client or AG-UI frontend can talk to it with a base URL and a key.

Agents build on LangChain's deep-agent framework. Stackbone wraps it with a default model bridge, your org's injected model key, and a durable filesystem backed by your workspace's own storage. You write the agent itself.

The authoring model

  • Authored as one file: an agent is deep-agents/<name>/index.ts, which default-exports defineDeepAgent({ name, model, tools }) from @stackbone/sdk/deep. There is no server to hand-roll and no per-agent manifest.
  • Discovery is by convention. Any folder under deep-agents/ that has an index.ts is an agent. Drop one in and stackbone dev picks it up. The folder name is also the agent's identity: it is the model value a chat client sends to talk to it, and it is the name you pass to defineDeepAgent. The two must match.
  • Its instruction is not in the file: you write the agent's persona in Studio, in the prompt catalogue, and the runtime reads it when it builds the graph. Changing the wording is an edit, not a deploy. An agent whose prompt is still empty runs and answers, it just has no persona yet. See where the instruction lives.
  • Spoken over a standard wire: a request's model field picks which agent answers, on the same POST /openai/v1/chat/completions and POST /anthropic/v1/messages endpoints every agent in the workspace shares. An AG-UI client reaches the same agent at POST /agui/v1/agents/:name, with interrupts, frontend tools and shared state on top. See Agent protocol for the full contract.
  • Tools are plain LangChain tools. Each one is a function wrapped with tool(...) from @langchain/core/tools, given a name, a description, and a Zod schema. The model decides when to call it from those three things.
  • Two tools come ready-made. @stackbone/sdk/deep ships two tool builders you do not have to write. connectorTool({ connector, operation }) turns one operation of a connected provider into a tool (see Integrations), and browserTools() hands the agent a real Chromium (see Browser tools).
  • One ambient client covers the platform. Inside a tool's handler you reach it through the shared stackbone handle (stackbone.config, .storage, .rag, .database, and more). You never construct a client or wire credentials.
  • The same object takes optional extras. Alongside tools you can pass subagents (delegate a job to a specialist, like the built-in browser subagent), interruptOn (pause the run for a human before a named tool runs, see approvals) and editableState (the state keys an AG-UI client is allowed to seed or edit).
// A deep agent is one file: deep-agents/greeter/index.ts.
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
import { defineDeepAgent } from '@stackbone/sdk/deep';
import { stackbone } from '@stackbone/sdk';

const getGreeting = tool(
  async () => {
    const greeting = await stackbone.config.get('greeting');
    return greeting.error ? 'Hello!' : String(greeting.data);
  },
  {
    name: 'get_greeting',
    description: 'Look up the configured greeting.',
    schema: z.object({}),
  },
);

export default defineDeepAgent({
  // Must match the folder name: it is who this agent's prompts belong to.
  name: 'greeter',
  model: 'openai/gpt-4o-mini',
  tools: [getGreeting],
});

Where the instruction lives

The agent's instruction is a prompt in the catalogue, owned by this agent and keyed by its name. You write and edit it in Studio, on the agent's own Prompts panel, and publish the version you want live. The runtime reads the published text when it builds the graph, so a rebuild picks up the new wording with no code change.

Point the agent at a different key, or hand the text values it can render, by declaring them:

export default defineDeepAgent({
  name: 'greeter',
  model: 'openai/gpt-4o-mini',
  instructions: { key: 'front_desk', variables: { tone: 'warm' } },
  tools: [getGreeting],
});

variables fills the {{tone}} placeholders the written text references. A placeholder the text asks for and the declaration does not carry empties the whole instruction, so declare every one the prompt uses.

Subagents work the same way. Each one names itself, and its brief is a prompt under this agent keyed by the subagent's name unless you give it its own instructions.

Files the agent writes

A deep agent has a filesystem of its own. It reads and writes paths like /notes.md, and the runtime keeps them as objects in your workspace's storage instead of on the container's disk. The files survive a restart, and your own code lists them from the fs bucket with stackbone.storage.from('fs').

The scope is the chat session. A file written during a session belongs to that session, so two conversations never read each other's notes. A turn that runs with no session in scope, such as a workflow step calling the agent, writes to a shared area the agent keeps across runs instead.

See stackbone.storage for the surface those objects land in.

Where to go next

  • Getting started: scaffold, write, and run your first agent.
  • Examples: complete agents you can copy.
  • Browser tools: give the agent a real Chromium to navigate, act on and extract from the web.
  • Workflow agents: combine an agent with a durable workflow.
  • Agent protocol: the full HTTP contract, the session key, tool approvals, and the AG-UI surface.
BUILT WITH ❤️ FROM CANADA AND SPAIN