Skip to main content

Resource Configuration

Broods uses a code-first configuration model. You define agents, workspaces, sandboxes, skills, tools, cron jobs, and channels as typed TypeScript resources inside a broods/ folder. The CLI compiles these into a manifest, syncs them to the cloud, and generates typed runtime references.

Project Layout

my-project/
broods/
index.ts # Resource definitions
_generated/ # Auto-generated by broods dev/deploy
api.ts # Typed agent/resource references
ids.ts # Deployed IDs
greeting-skill/ # Skill bundle (optional)
SKILL.md
tools/ # Custom tool sources (optional)
my-tool.ts
.env.local # Local secrets (never commit)

env — Deferred Secrets

Never bake secrets into your resource files. Use env.NAME to create a deferred reference that resolves on the server at runtime:

import { defineAgent, env } from "broods";

export const myAgent = defineAgent({
name: "my-agent",
config: {
provider: {
openai: { apiKey: env.OPENAI_API_KEY },
},
},
});

Both forms are equivalent:

env.OPENAI_API_KEY // property access
env("OPENAI_API_KEY") // call form

Set the value with the CLI:

broods env set OPENAI_API_KEY

Or let broods dev auto-push it from your local .env.local.

env is not process.env. Using process.env.OPENAI_API_KEY would bake your local value into the deployed config. Always use env.NAME for anything that should stay server-side.

Agents

defineAgent is the core resource. It holds model, provider, tools, channels, workspaces, skills, subagents, and hooks.

import { defineAgent, defineSandbox, defineWorkspace, env } from "broods";

export const myAgent = defineAgent({
name: "my-agent",
description: "A general-purpose assistant", // visible to parent agents
config: {
provider: {
google: { apiKey: env.GOOGLE_API_KEY },
},
model: {
provider: "google",
modelId: "gemini-3-flash",
temperature: 0.7,
},
agent: {
system: "You are a helpful assistant.",
maxTurn: 10,
},
publicAccess: true, // enable public SSE/WebSocket endpoint
},
});

Supported Providers

ProviderConfig keyRequired fields
GooglegoogleapiKey
OpenAIopenaiapiKey
Bedrockbedrockregion, apiKey
GatewaygatewayapiKey
MiniMaxminimaxapiKey

Reasoning / Thinking Tokens

Use standard Vercel AI SDK providerOptions in config.model:

model: {
provider: "openai",
modelId: "o3",
providerOptions: {
openai: {
reasoningEffort: "high",
reasoningSummary: "auto",
},
},
},
ProviderThinking config
OpenAIproviderOptions.openai.reasoningEffort
AnthropicproviderOptions.anthropic.thinking
GoogleproviderOptions.google.thinkingConfig
MiniMaxproviderOptions.anthropic.thinking (Anthropic-compatible)

Structured Output

model: {
provider: "google",
modelId: "gemini-3-flash",
output: {
type: "object",
name: "analysis",
description: "Structured analysis result",
schema: {
type: "object",
properties: {
summary: { type: "string" },
confidence: { type: "number" },
},
required: ["summary", "confidence"],
},
},
},

Tools

Enable built-in and custom tools:

config: {
tools: {
tavilySearch: {
enabled: true,
apiKey: env.TAVILY_API_KEY,
searchDepth: "advanced",
},
googleSearch: { enabled: true },
handoffs: { enabled: true },
},
},

See External Tools for uploading custom tools.

Channels

Attach one or more channels to an agent:

import { defineTelegramChannel } from "broods";

export const telegram = defineTelegramChannel({
botToken: env.TELEGRAM_BOT_TOKEN,
webhookSecret: env.TELEGRAM_WEBHOOK_SECRET,
allowedChatIds: [123456789],
streaming: { mode: "progress" },
});

export const myAgent = defineAgent({
name: "my-agent",
config: {
channels: [telegram],
},
});

Each channel type has its own constructor. See Channels.

Workspaces

Reference workspace resources or pass per-workspace sandbox overrides:

import { defineWorkspace } from "broods";

export const notes = defineWorkspace({
name: "notes",
config: {
storage: { provider: "s3" },
harness: { enabled: true },
},
});

export const myAgent = defineAgent({
name: "my-agent",
config: {
sandbox: mySandbox,
workspaces: [
notes, // inherit agent sandbox
{ workspace: notes, sandbox: null }, // read-only
{ workspace: teamWorkspace, sandbox: k8sSandbox }, // per-workspace override
],
},
});

Subagents

export const research = defineAgent({
name: "research",
config: { /* ... */ },
});

export const myAgent = defineAgent({
name: "my-agent",
config: {
subagent: {
enabled: true,
allowed: [research],
context: "inherited",
mode: "persistent",
},
},
});

See Sub Agents for full details.

Skills

import { defineSkill } from "broods";

export const supportFlow = defineSkill({
name: "support-flow",
config: { path: "support-flow" },
});

export const myAgent = defineAgent({
name: "my-agent",
config: {
skills: {
enabled: true,
allowed: [supportFlow],
},
},
});

See Skills for bundle format.

Hooks

Configure lifecycle webhooks:

config: {
hooks: {
webhooks: [
{
enabled: true,
url: "https://example.com/webhooks",
secret: env.WEBHOOK_SECRET,
events: ["agent.started", "agent.finished", "agent.failed"],
},
],
},
},

See Lifecycle Webhooks.

Session

Control history pruning and compaction:

config: {
session: {
pruning: { enabled: true },
compaction: {
enabled: true,
maxContextLength: 100_000,
},
},
},

Sandboxes

import { defineSandbox, env } from "broods";

export const lambdaSandbox = defineSandbox({
name: "lambda-sandbox",
config: {
provider: "lambda",
network: { mode: "deny-all" },
permissionMode: "ask",
timeout: 60,
},
});

export const k8sSandbox = defineSandbox({
name: "persistent-k8s",
config: {
provider: "kubernetes",
network: { mode: "allow-all" },
permissionMode: "bypass",
persistent: true,
lifecycle: { idleTimeoutSeconds: 1800 },
options: { mountAwsS3Buckets: true },
},
});

Supported providers: lambda, e2b, daytona, kubernetes, vercel.

See Workspace & Sandbox for the full sandbox model.

Workspaces

import { defineWorkspace } from "broods";

export const notes = defineWorkspace({
name: "notes",
config: {
storage: { provider: "s3" },
harness: { enabled: true },
},
});

Skills

import { defineSkill } from "broods";

export const supportFlow = defineSkill({
name: "support-flow",
config: { path: "support-flow" },
});

The path is relative to broods/ and must contain a SKILL.md file.

Tools

import { defineTool } from "broods";

export const analyze = defineTool({
name: "analyze",
config: {
path: "tools/analyze.ts",
description: "Analyze structured data.",
inputSchema: {
type: "object",
properties: {
data: { type: "array" },
},
required: ["data"],
},
defaultConfig: {},
},
});

The CLI bundles the TypeScript entrypoint into a self-contained ESM module and uploads it. See External Tools for the bundle contract.

Cron Jobs

import { defineCron } from "broods";

export const dailyDigest = defineCron({
name: "daily-digest",
config: {
agent: myAgent,
input: "Summarize today's activity.",
scheduleExpression: "cron(0 9 * * ? *)",
timezone: "Europe/Amsterdam",
},
});

Or use events instead of input for multimodal payloads:

config: {
agent: myAgent,
events: [
{ role: "user", content: [{ type: "text", text: "Check status." }] },
],
scheduleExpression: "rate(1 hour)",
}

Channels

Each channel has a dedicated constructor:

import {
defineTelegramChannel,
defineDiscordChannel,
defineSlackChannel,
defineGitHubChannel,
definePancakeChannel,
defineZaloChannel,
} from "broods";

A channel definition must be attached to exactly one agent. An agent may have multiple channels, but not multiple of the same type.

Project Config

Optionally export a defineBroods config to set project defaults:

import { defineBroods } from "broods";

export default defineBroods({
project: "my-project",
environments: {
dev: "development",
deploy: "production",
},
dashboardUrl: "https://dashboard.broods.app",
});

These values can be overridden by CLI flags (--project, --env) or .env.local.

Validation

The CLI validates resource configs at compile time:

  • Unknown agent config keys are rejected with suggestions (e.g. workspace: → did you mean workspaces:?).
  • Duplicate resources are rejected.
  • Skill bundles must contain SKILL.md.
  • Tool bundles must build successfully as ESM.
  • Workspace storage provider must be s3.
  • Sandbox mounts must support S3 workspace access.

These checks run during broods dev and broods deploy, so you get fast feedback without deploying broken config.

Generated References

After broods dev or broods deploy, the CLI writes _generated/api.ts with typed references:

import { api } from "./broods/_generated/api";

// api.agents.myAgent → AgentReference
// api.channels.myTelegram → ChannelReference
// api.workspaces.notes → string (workspaceId)
// api.sandboxes.lambda → string (sandboxId)

Use these references with BroodsClient for fully typed runtime calls:

import { BroodsClient } from "broods";
import { api } from "./broods/_generated/api";

const client = new BroodsClient();
await client.run(api.agents.myAgent, { input: "Hello!" });

Raw API Fallback

The code-first layer compiles to the same manifest the raw account API accepts. If you ever need to call the REST API directly, the shapes are identical. See the API Reference for the full OpenAPI spec.