background-agents
v0.1.2
Published
A TypeScript SDK for interacting with various AI coding agents (Claude, Codex, OpenCode, Gemini, Pi)
Downloads
89
Maintainers
Readme
Background Agents SDK
A TypeScript SDK for running AI coding agents (Claude, Codex, Gemini, Goose, OpenCode, Pi) in secure Daytona sandboxes. Designed for background execution with polling-based event streaming.
import { Daytona } from "@daytonaio/sdk"
import { createSession } from "background-agents"
const daytona = new Daytona({ apiKey: process.env.DAYTONA_API_KEY })
const sandbox = await daytona.create()
const session = await createSession("claude", {
sandbox,
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY }
})
await session.start("Refactor the auth module")
// Poll for events
while (await session.isRunning()) {
const { events } = await session.getEvents()
for (const event of events) {
if (event.type === "token") process.stdout.write(event.text)
}
await new Promise(r => setTimeout(r, 1000))
}
await sandbox.delete()Features
- Secure sandboxed execution — Agents run in isolated Daytona sandboxes
- Background execution — Start agents, poll for events, survive restarts
- Unified API — One interface for Claude, Codex, Gemini, Goose, OpenCode, and Pi
- Zero-friction setup — Provider CLI auto-installed in sandbox
- Session persistence — Resume conversations across runs and restarts
Provider support
| Provider | Status | Auth |
|----------|--------|------|
| Claude | ✅ | ANTHROPIC_API_KEY or CLAUDE_CODE_CREDENTIALS |
| Codex | ✅ | OPENAI_API_KEY |
| Goose | ✅ | Provider-specific (e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY) |
| OpenCode | ✅ | Provider-specific (e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY) |
| Gemini | ✅ | GEMINI_API_KEY |
| Pi | ✅ | Provider-specific (e.g. ANTHROPIC_API_KEY, OPENAI_API_KEY) |
CLI reference commands
| Provider | CLI Command |
|----------|-------------|
| Claude | claude -p --output-format stream-json --verbose --dangerously-skip-permissions "prompt" |
| Codex | codex exec --json --skip-git-repo-check --yolo "prompt" |
| Goose | goose run --output-format stream-json --text "prompt" |
| OpenCode | opencode run --format json --variant medium "prompt" |
| Gemini | gemini --output-format stream-json --yolo -p "prompt" |
| Pi | pi --mode json -p "prompt" |
Prerequisites
A Daytona API key for secure sandboxed execution.
export DAYTONA_API_KEY=dtn_your_api_keyInstallation
npm install background-agents @daytonaio/sdkQuick start
import { Daytona } from "@daytonaio/sdk"
import { createSession } from "background-agents"
// 1. Create sandbox
const daytona = new Daytona({ apiKey: process.env.DAYTONA_API_KEY })
const sandbox = await daytona.create()
// 2. Create session
const session = await createSession("claude", {
sandbox,
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
model: "sonnet",
systemPrompt: "You are a helpful coding assistant.",
})
// 3. Start a task
await session.start("Create a hello world script")
// 4. Poll for events
while (await session.isRunning()) {
const { events } = await session.getEvents()
for (const event of events) {
if (event.type === "token") process.stdout.write(event.text)
if (event.type === "tool_start") console.log(`\n[Tool: ${event.name}]`)
if (event.type === "end") console.log("\nDone.")
}
await new Promise(r => setTimeout(r, 1000))
}
// 5. Cleanup
await sandbox.delete()Restart-tolerant workflows
The SDK is designed for long-running tasks that may outlive your server process. Persist sandbox.id and session.id, then reattach after restart.
import { Daytona } from "@daytonaio/sdk"
import { createSession, getSession } from "background-agents"
const daytona = new Daytona({ apiKey: process.env.DAYTONA_API_KEY! })
const sandbox = await daytona.create()
// Start a task
const session = await createSession("claude", {
sandbox,
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
model: "sonnet",
})
await session.start("Do a long-running refactor...")
// Persist these IDs, then exit
const sandboxId = sandbox.id
const sessionId = session.id // Save this to reattach later
// --- After restart ---
// Reattach to existing session
const sandbox = await daytona.get(sandboxId)
const session = await getSession(sessionId, { sandbox })
// Continue polling
const { events, running } = await session.getEvents()
for (const event of events) {
if (event.type === "token") process.stdout.write(event.text)
}
// Cancel if needed
await session.cancel()API reference
createSession(provider, options)
Creates a session. The provider CLI is installed automatically.
const session = await createSession("claude", {
sandbox, // Daytona sandbox
env: { ANTHROPIC_API_KEY: "sk-..." }, // Environment variables
model: "sonnet", // Optional: model name
systemPrompt: "You are helpful.", // Optional: system prompt
})session.start(prompt)
Starts a background task. Returns immediately with process info.
const { pid, outputFile } = await session.start("Your task here")session.getEvents()
Polls for new events since last call.
const { events, running } = await session.getEvents()
// events: Event[] - new events since last poll
// running: boolean - true if agent is still runningsession.isRunning()
Returns true while the agent is running.
session.cancel()
Kills the running agent process.
getSession(sessionId, options)
Reattaches to an existing session by ID.
const session = await getSession(
sessionId, // session.id from createSession()
{ sandbox }
)Event types
| Event | Description | Fields |
|-------|-------------|--------|
| session | Session started | id: string |
| token | Streamed text | text: string |
| tool_start | Tool invoked | name: string, input?: unknown |
| tool_delta | Tool streaming | text: string |
| tool_end | Tool finished | output?: string |
| end | Task complete | error?: string |
| agent_crashed | Process crashed | message?: string, output?: string |
type Event =
| { type: "session"; id: string }
| { type: "token"; text: string }
| { type: "tool_start"; name: string; input?: unknown }
| { type: "tool_delta"; text: string }
| { type: "tool_end"; output?: string }
| { type: "end"; error?: string }
| { type: "agent_crashed"; message?: string; output?: string }Model selection
| Provider | Example | Docs |
|----------|---------|------|
| Claude | model: "sonnet" | Claude Code models |
| Codex | model: "gpt-4o" | Codex CLI models |
| Goose | model: "gpt-4o" | Goose providers |
| OpenCode | model: "openai/gpt-4o" | OpenCode models |
| Gemini | model: "gemini-2.0-flash" | Gemini CLI model |
| Pi | model: "sonnet" or model: "openai/gpt-4o" | Pi CLI models |
How it works
- Sandbox — Create a Daytona sandbox for isolated execution
- CLI install — Provider CLI is installed in the sandbox automatically
- Background execution — Agent runs via
nohup, outputs to a log file - Polling — SDK polls the log file for new JSON events
- Completion — A
.donefile signals when the agent finishes - Cleanup — You call
sandbox.delete()when done
┌─────────────┐ ┌──────────────────────────────────────┐
│ Your App │────▶│ Daytona Sandbox │
│ │ │ ┌─────────────┐ ┌─────────────┐ │
│ (polling) │◀────│ │ Log File │◀───│ Agent CLI │ │
│ │ │ └─────────────┘ └─────────────┘ │
└─────────────┘ └──────────────────────────────────────┘Debug mode
Set CODING_AGENTS_DEBUG=1 to enable debug logging:
CODING_AGENTS_DEBUG=1 npx tsx your-script.tsClaude OAuth credentials
Claude can authenticate via ANTHROPIC_API_KEY or CLAUDE_CODE_CREDENTIALS. The latter uses OAuth credentials from a Claude Pro/Max subscription.
First, sign in locally:
claude auth loginThen retrieve your credentials:
| OS | Command |
|----|---------|
| macOS | security find-generic-password -s "Claude Code-credentials" -w |
| Linux | cat ~/.claude/.credentials.json |
| Windows | type %USERPROFILE%\.claude\.credentials.json |
Pass the output as CLAUDE_CODE_CREDENTIALS. The SDK automatically writes it to ~/.claude/.credentials.json in the sandbox.
Development
Build, test, and iterate locally. Start by installing dependencies and running the unit test suite:
npm install
npm run build
npm testFor integration and end-to-end testing, see TESTING.md.
For testing scenarios, you can use the deterministic Eliza agent, which requires no provider API key.
Resources
Sandbox — Daytona Docs · Daytona GitHub
Agents — Claude Code · Codex CLI · Gemini CLI · Goose · OpenCode · Pi
License
MIT
