@downcity/agent
Agent, AgentSessions, Session, and execution runtime.
@downcity/agent provides Agent, AgentSessions, Session, and the execution runtime. @downcity/city provides RemoteAgent and
City. Agents do not own Plugins. City owns unique Plugin instances, configuration projection, and lifecycle;
Plugin author contracts live in @downcity/city/plugin. Workspace resources come from
@downcity/city.
const city = new City({ workspaces: [workspace] });
const agent = new Agent({ id: "assistant" });
city.agents.add(agent);
const session = await agent.sessions.create({ workspace });
const turn = await session.prompt({ query: "Hello" });
const result = await turn.finished;Provide Plugin instances through CityOptions.plugins or city.plugins.add() before use.
city.agents.add() only registers an Agent; every City Plugin becomes available automatically.
Read file changes for each turn
A Session bound to a Workspace persists file edits successfully committed by the built-in
structured write and edit tools in the final Assistant Message. The diff comes directly from
each atomic file-tool mutation instead of scanning the worktree before and after a turn, so changes
made by shell commands, scripts, external editors, or other Sessions are excluded. Git is not
required.
The data-session-turn-file-diff data part contains per-file status, line counts, and unified diff.
Use the exported parser to read it safely:
import { read_session_turn_file_diff_data } from "@downcity/agent/session";
const page = await session.messages();
for (const message of page.items) {
if (message.type !== "assistant") continue;
for (const part of message.parts) {
if (part.type !== "data") continue;
const file_diff = read_session_turn_file_diff_data(part);
if (file_diff) console.log(file_diff.files, file_diff.additions, file_diff.deletions);
}
}Group and GroupSession
Group is a collaborative subject with its own model, members, dispatch strategy, and
GroupSession collection. A GroupSession persists shared messages and dispatch state under
groups/<group_id>/sessions/<group_session_id>/.
After the first user message is persisted, the GroupSession uses Group.model to generate a
short title asynchronously. The title is canonical metadata stored in meta.json; it is separate
from preview_text, which remains the latest-message preview. Later messages do not replace the
title. Subscribe to { type: "title", title } for live title changes, or call
group_session.rename(title) to persist a manual title and prevent background generation from
overwriting it.
const group_session = await group.sessions.create({ workspace });
group_session.subscribe((event) => {
if (event.type === "title") console.log(event.title);
});
await group_session.prompt({ query: "Review the login flow" });
await group_session.rename("Login flow review");
const sessions = await group.sessions.list();
console.log(sessions[0].title, sessions[0].preview_text);