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 runtimeRemoteAgent: remote HTTP client for an already-running agent
Install
pnpm add @downcity/agentLocal 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-deltareasoning-deltatool-calltool-resulterror
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.jsonlThis keeps sessions from different agents isolated even when they share the same project directory.