Skip to content
Cavemandocs
MIT

Define an agent

agent(), context, output, filesystem, and subagents as one frozen definition.

An agent is one frozen AgentDefinition. agent() is the constructor. Everything downstream, sandbox, Context IR, build, receipts, sees that object. There is no second hidden definition.

typescript
import { agent, auto } from "@caveman-ai/agent";

export default agent({
id: "support",
instructions: "Answer from policy. Never invent policy.",
model: auto(),
});

id must match ^[a-z0-9][a-z0-9_-]{0,95}$. Duplicate tool names fail at construction. Tool names starting with cave_ are reserved and also fail at construction.

Default sandbox is required. Default reasoning is low. Options: off, minimal, low, medium, high.

Model selection#

model is one of: auto(), a provider/model string, or a Pi model object.

auto() resolves in this order: CAVE_MODEL, then .caveman/provider.json, then the baseline model for the sole supported credential in the environment.

CredentialBaseline if it is the only one
ANTHROPIC_API_KEYanthropic/claude-haiku-4-5
OPENAI_API_KEYopenai/gpt-5.4-mini
GEMINI_API_KEY or GOOGLE_API_KEYgoogle/gemini-2.5-flash

Zero credentials fail. Several credentials and no CAVE_MODEL fail. A pinned string must use provider/model form. auto() never classifies the task or picks between models on quality.

Context and output#

typescript
import { context, file, output, schema } from "@caveman-ai/agent";

const playbook = context({
id: "support.playbook",
kind: "skill",
source: file("./support.md"),
stability: "build",
safety: "S0",
priority: "required",
});

const answer = output({
maxTokens: 500,
schema: schema.object({ answer: schema.string() }),
});

context() labels a segment. Kind, stability, safety class, priority, recovery, cache region, and privacy class are all explicit. Defaults: safety S0, priority required, recovery none, privacy local_sensitive, cache region frozen_prefix when stability is build and live_zone otherwise.

Build-stable context enters the frozen prefix. Session and turn context stay live. Volatile data in the stable cache zone is rejected. On drift or transform failure the runtime fails open to the original provider-visible bytes.

output({ maxTokens }) is a token budget on the answer. An optional schema constrains the shape.

Filesystem layout#

loadAgentDir(rootDir) lowers a convention directory into an ordinary agent() call.

text
support-bot/
├── instructions.md
├── agent.ts
├── skills/
│ ├── refund-policy.md
│ └── shipping-claims.md
├── tools/
│ └── lookup_order.ts
├── subagents/
└── evals/

agent.ts default-exports an AgentDirConfig: model, optional budget, breakers, and extra context. Instructions come from instructions.md. Each tools/*.ts default-exports a tool(). Each skills/<name>.md contributes a short description to one build-stable skill segment; the body is served later by cave_skill as a live-zone tool result. The model picks from descriptions. There is no embedding ranker.

The loader writes a generated module entry at .caveman/agent-dir-entry.mjs. Directory-loaded runs print a receipt by default, because stdout is yours. run() from a hand-built definition does not, because stdout may be a protocol channel.

Subagents#

typescript
import { agent, auto, subagent } from "@caveman-ai/agent";

const researcher = agent({
id: "researcher",
instructions: "Search, then return sources.",
model: auto(),
});

const root = agent({
id: "support",
instructions: "Delegate research. Answer from policy.",
model: auto(),
tools: [
subagent({
name: "research",
description: "Look up one question.",
agent: researcher,
}),
],
});

Defaults: maxInputChars 32,768, maxCalls 1, maxCostUsd 1, maxContextTokens 128,000. Under a USD-metered parent the child's wallet is carved from the remaining budget and unused remainder returns. A token-metered parent cannot fund a child that declares no maxTokens. A host-mode child is refused under a sandbox: "required" parent.

Optional workspace files#

The core runtime does not search the repository. Products that want AGENTS.md, Agent Skills, Agent Plugins v1, or Vercel OpenPlugin can opt in:

typescript
import { agent } from "@caveman-ai/agent";
import {
applyAgentEnvironment,
loadAgentEnvironment,
} from "@caveman-ai/agent/plugins";

const environment = await loadAgentEnvironment({ cwd: process.cwd() });
const reviewer = applyAgentEnvironment(agent({
id: "reviewer",
instructions: "Review requested changes.",
model: "openai/gpt-5.4",
sandbox: "host",
}), environment);

Metadata enters the stable prefix. Full skill bodies and command markdown enter only after activation. Plugin MCP, hooks, custom agents, and plugin subprocesses are reported and not launched. Ambient secrets are not inherited.

What it will not do#

agent() does not start a process, open a network connection, or look for files. Shared-memory provenance is refused here, at construction, not after a model call. A definition with sandbox: "host" cannot produce a Cave Build lock.