Local Agent

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/openai

On another platform, replace only the Sandbox package and instance:

PlatformPackageAdapter
macOS@downcity/sandbox-macosMacOsSeatbeltSandbox
Linux@downcity/sandbox-linuxLinuxBubblewrapSandbox
Windows@downcity/sandbox-windows-mxcWindowsMxcSandbox

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

  1. Create the Sandbox Adapter for the current platform.
  2. Create Shell and inject the Sandbox.
  3. Create Workspace with a stable ID, project path, and Shell.
  4. Create City with the Workspace, create the Agent independently, then call city.agents.add(agent).
  5. Create a Session, start a Turn with prompt(), and await turn.finished.
  6. Call agent.dispose() in finally.

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/agent when you need network serving.

Model resolution

Local execution resolves the model in this order:

  1. A Session model set with await session.set({ model }).
  2. The default model passed 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.

Next steps