Downcity
Quickstart

Agent SDK

Use Downcity as a local embeddable Agent or as a remote Agent client

Agent SDK

The full Agent SDK documentation now lives inside the dedicated Agent docs domain.

Start here:


The @downcity/agent package now exposes two primary SDK entries:

  • Agent: local embeddable agent runtime
  • RemoteAgent: remote HTTP client for an already-running agent

Install

pnpm add @downcity/agent

Local Agent

Use new Agent(...) when your app runs in the same process and can provide tools, services, and models directly.

import { Agent } from "@downcity/agent";
import { createOpenAI } from "@ai-sdk/openai";

const openai = createOpenAI({
  apiKey: process.env.OPENAI_API_KEY!,
});

const agent = new Agent({
  id: "vibecape-official",
  path: "/path/to/agent-project",
  tools: {},
});

const session = await agent.session();
await session.set({
  model: openai.responses("gpt-5"),
});

const turn = await session.prompt({
  query: "Rename the local file to match the new convention",
});
const result = await turn.finished;

console.log(result.text);

Inject ChatService

If your client needs to start chat channels directly inside @downcity/agent, pass an explicit ChatService instance:

import { Agent, ChatService } from "@downcity/agent";

const agent = new Agent({
  id: "vibecape-official",
  path: "/path/to/agent-project",
  tools: {},
  services: [
    new ChatService({
      telegram: {
        botToken: process.env.TELEGRAM_BOT_TOKEN!,
      },
    }),
  ],
});

services accepts already-instantiated service objects.
agent.start({ http: { ... }, rpc: true }) automatically starts those services before exposing the endpoints.

Observe a session

const unsubscribe = session.subscribe((event) => {
  if (event.type === "text-delta") {
    process.stdout.write(event.text);
  }
});

const turn = await session.prompt({
  query: "Continue the previous task",
});
await turn.finished;
unsubscribe();

Current session events include:

  • text-delta
  • reasoning-delta
  • tool-call
  • tool-result
  • error

Fork a session

const branched = await session.fork();
const fromMessage = await session.fork("message-id");

If messageId is provided, the new session copies history up to and including that message.

Start HTTP / RPC

const started = await agent.start({
  http: {
    host: "127.0.0.1",
    port: 15314,
  },
  rpc: true,
});

console.log(started.http?.baseUrl);

Remote Agent

Use RemoteAgent when another process has already started agent.start({ http: { ... } }).

import { RemoteAgent } from "@downcity/agent";

const agent = new RemoteAgent({
  baseUrl: "http://127.0.0.1:15314",
});

const session = await agent.session();

const turn = await session.prompt({
  query: "Summarize the repository structure",
});
const result = await turn.finished;

console.log(result.text);

Remote observation uses the same API:

const unsubscribe = session.subscribe((event) => {
  if (event.type === "text-delta") {
    process.stdout.write(event.text);
  }
});

const turn = await session.prompt({ query: "Continue" });
await turn.finished;
unsubscribe();

Session storage

SDK sessions are stored under:

<projectRoot>/.downcity/agents/<agentId>/sessions/<sessionId>/messages/messages.jsonl

This keeps sessions from different agents isolated even when they share the same project directory.