Agent SDK Architecture
Responsibilities and composition of City, Agent, Workspace, Session, Power, and Shell
Downcity separates the execution subject, project resources, and Power runtime. Agent owns identity,
instruction, model, custom Tools, and Sessions. Workspace owns project resources and Shell. City
owns unique Power instances, lifecycle, and Agent/Group collections.
import { Agent } from "@downcity/agent";
import { City } from "@downcity/city";
import { create_builtin_power_registrations } from "@downcity/powers";
import { Shell, Workspace } from "@downcity/city";
const agent = new Agent({ id, model, instruction });
const workspace = new Workspace({ id: "project", path, shell });
const city = new City({
workspaces: [workspace],
powers: create_builtin_power_registrations(),
});
city.agents.add(agent);
// Session 在创建时选择 Workspace。
const session = await agent.sessions.create({ workspace });Responsibility model
The dependency direction stays explicit:
@downcity/agentdepends only on neutral resource protocols from@downcity/type.@downcity/citydepends on Agent and injects Workspace, Tools, andSessionHooksthrough its internal binding; Agent does not depend back on City.- Hosts choose a Sandbox Provider when constructing each Shell; City does not understand the backend.
- Edge adapters use
@downcity/type/workspacewithout loading Node.js local implementations.
Agent is the subject
An Agent is reusable across projects. It is never configured with one permanent Workspace.
const agent = new Agent({
id: "repo-helper",
model,
instruction: "Maintain the current project.",
tools: { company_search: company_search_tool },
});
city.agents.add(agent);
const frontend = await agent.sessions.create({ workspace: frontend_workspace });
const backend = await agent.sessions.create({ workspace: backend_workspace });Agent does not own Powers. City owns one instance per Power ID and projects every Power into an
Agent/Workspace execution entry. Every Power Action receives the current PowerContext with
directly callable restricted Agent, Workspace, Session, and Turn handles. Powers use one City-level
initialize/dispose lifecycle; Agent/Workspace only defines the current call scope.
Workspace is the resource boundary
Workspace owns:
- A stable Workspace ID and normalized project path.
- Rooted project file access and File/Search tools.
- Workspace environment variables.
- An optional Shell and its Sandbox adapter.
- It never owns Agent or Session runtime storage. City provides the domain-neutral
StorageProvider.
- It never owns Agent or Session runtime storage. City provides the domain-neutral
Workspace does not create SessionStore, understand Agent IDs, register Powers, call models, or create Storage. The constructor has no runtime data-root option.
Shell belongs to Workspace because command execution operates on Workspace resources. The public entry is:
import { Shell, Workspace } from "@downcity/city";There is no separate @downcity/shell package. Platform Sandbox packages remain independently
installable and are injected into Shell.
Session is the execution boundary
agent.sessions.create({ workspace }) combines Agent and Workspace for one Session without merging ownership. Agent, Session, and the City execution projection provide:
- The final Tool set from Workspace tools, Agent custom tools, and Power bridge tools.
AgentSessionsand the Agent domainLocalSessionStore.- Agent/Workspace/Session/Turn-aware PowerContext and an execution lease.
- Logs, scheduled Power Actions, and Shell binding.
Tool name conflicts fail during composition; no source silently overwrites another. The same Agent can hold multiple Sessions with different Workspace resources. Workspace resources may be registered in City or managed by the host outside City.
Private storage
When City receives a local persistent Storage Provider, runtime state is centralized outside the project:
~/.downcity/
└── agents/
└── <agent_id>/
├── sessions/
├── powers/
├── logs/
└── schedule.jsonlCity Storage is a bottom-level file/database capability. Agent code interprets the
agents/<agent_id>/ business scope and creates Session and Power stores inside it. Project
File/Search tools use a different rooted FileSystem, so they cannot inspect or change Session
history, Power state, or runtime data.
Environment and checkpoints
Environment variables describe the project execution environment and therefore belong to Workspace:
<workspace>/.env < WorkspaceOptions.envset_env() and patch_env() update the Workspace snapshot and synchronize Shell. Existing
Sessions commit the new environment at the next Step checkpoint, avoiding context changes halfway
through a model call.
Power registry changes, Session model changes, and compaction follow the same checkpoint rule.
Lifecycle
city.close() stops registered Agents and Sessions, disposes registered Workspace resources,
closes the City Storage Provider, and then closes HTTP/RPC transports. A Workspace managed outside
City remains owned by its host.
Package boundaries
| Package | Owns | Does not own |
|---|---|---|
@downcity/type | Neutral cross-package resource protocols | Concrete resources and lifecycle implementations |
@downcity/agent | Agent, AgentSessions, Session, Store, SessionHooks, model Tool Loop | City composition, Power lifecycle, project resource implementation, platform Sandbox |
@downcity/city | City, Workspace/Shell implementations, Power SDK/runtime, Hook scheduling, RemoteAgent, HTTP/RPC transports, local host data | Agent execution internals, concrete built-in Powers |
| Sandbox packages | Native process isolation for one platform | Workspace, Agent, Session, or Power business behavior |
Continue with Runtime architecture.