Local Agent Quickstart
Create a local Agent with a model, Shell, platform Sandbox, and its first Session execution
Local Agent Quickstart
Use this path to embed an Agent directly in a Node.js application. A complete local Agent combines a Workspace, Shell, platform Sandbox, model, and Session.
Install
This example uses macOS:
pnpm add @downcity/agent @downcity/workspace @downcity/sandbox-macos @ai-sdk/openaiOn another platform, replace only the Sandbox package and instance:
| Platform | Package | Adapter |
|---|---|---|
| macOS | @downcity/sandbox-macos | MacOsSeatbeltSandbox |
| Linux | @downcity/sandbox-linux | LinuxBubblewrapSandbox |
| Windows | @downcity/sandbox-windows-mxc | WindowsMxcSandbox |
Complete minimal example
import { Agent } from "@downcity/agent";
import { City } from "@downcity/agent";
import { Workspace } from "@downcity/workspace";
import { createOpenAI } from "@ai-sdk/openai";
import { MacOsSeatbeltSandbox } from "@downcity/sandbox-macos";
import { Shell } from "@downcity/workspace";
const openai = createOpenAI({
apiKey: process.env.OPENAI_API_KEY!,
});
const shell = new Shell({
sandbox: new MacOsSeatbeltSandbox(),
});
const workspace = new Workspace({
id: "project",
path: "/path/to/project",
shell,
});
const city = new City({ workspaces: [workspace] });
const agent = new Agent({
id: "repo-helper",
city,
model: openai.responses("gpt-5"),
});
try {
const session = await agent.sessions.create({ workspace: city.workspaces.get("project")! });
const turn = await session.prompt({
query: "Summarize the current repository structure",
});
const result = await turn.finished;
console.log(result.text);
} finally {
await agent.dispose();
}Six key steps
- Create the Sandbox Adapter for the current platform.
- Create
Shelland inject the Sandbox. - Create
Workspacewith a stable ID, project path, and Shell. - Create
Citywith the Workspace, create the Agent independently, then callcity.agents.add(agent). - Create a Session, start a Turn with
prompt(), and awaitturn.finished. - Call
agent.dispose()infinally.
Shell matters: without it, Workspace still provides file and search Tools, but Agent has no shell_exec or shell_session and cannot run project commands or manage long-lived processes. The SDK does not choose a Sandbox automatically or silently fall back to unrestricted execution when an Adapter is missing.
Objects and lifecycle
City(Workspace) → Agent(city) → Agent Session(Workspace) → Turn
└──────────────→ agent.dispose()- Agent is bound to one City resource container; each Session selects a City Workspace.
- Workspace describes project resources only; AgentWorkspace owns Sessions and runtime state for that Agent in the project.
- Multiple Agents may target the same directory, using distinct agent IDs and isolated Workspace data partitions.
- Plugin lifecycle and ActionSchedule begin starting immediately after
new Agent(...). - Session execution waits for the Agent runtime automatically; callers do not manage a separate startup state.
agent.dispose()releases the Agent's sessions and plugins.city.close()releases shared Workspaces and Shell resources.- HTTP/RPC transports do not belong to Agent; use
@downcity/agentwhen you need network serving.
Model resolution
Local execution resolves the model in this order:
- A Session model set with
await session.set({ model }). - The default
modelpassed to Agent.
If neither exists, the Session cannot execute. A pure SDK host owns model instances directly; in a Downcity project, the CLI host resolves the model from project configuration and the City AIService catalog.