Tools and Powers

Configure Agent Tools and provide Powers through City

Tools and Powers both participate in Session execution, but ownership differs: Agent owns custom Tools; City owns unique Power instances, configuration projection, and lifecycle.

Custom Tools

A local Agent directly receives custom Tools:

const agent = new Agent({
  id: "repo-helper",
  tools: {
    my_tool,
  },
});

They are merged with the Tools of the Workspace selected by the Session. Name conflicts throw instead of silently replacing a Tool.

Workspace and Shell

Workspace provides grep, find, read, write, and edit. Configure Shell explicitly when command execution is required:

import { Agent } from "@downcity/agent";
import { MicrosandboxProvider } from "@downcity/sandbox-microsandbox";
import { Shell, Workspace } from "@downcity/city";

const agent = new Agent({ id: "repo-helper" });
const workspace = new Workspace({
  id: "project",
  path: "/path/to/project",
  shell: new Shell({
    sandbox_provider: new MicrosandboxProvider(),
  }),
  runtime_path: "/path/to/downcity-runtime/project",
});

Shell adds shell_exec and shell_session. File and search Tools remain restricted to the Workspace root. Approvals and asynchronous questions belong to a specific Session and use session.interactions() and session.respond(...).

City provides Powers

The Agent constructor does not accept Powers. Once registrations are provided to City, every Agent receives them automatically:

import { Agent } from "@downcity/agent";
import { City } from "@downcity/city";
import { create_builtin_power_registrations } from "@downcity/powers";
import { Workspace } from "@downcity/city";

const workspace = new Workspace({
  id: "project",
  path: "/path/to/project",
});
const agent = new Agent({ id: "repo-helper", model });
const city = new City({
  workspaces: [workspace],
  powers: create_builtin_power_registrations(),
});

city.agents.add(agent);

City automatically exposes every registered Power to every Agent. A running City can update the collection through city.powers.add() and city.powers.remove().

Each Power ID identifies one instance inside a City with a City-level initialize/dispose lifecycle. Agents and Workspaces provide call context; they do not own Power lifecycle.

City-level configuration

The host provides one Config store for each Power. Configuration does not enter Agent or determine instance count:

const city = new City({
  power_host: {
    config: (power_id) => open_power_config_store(power_id),
  },
});

Each Power ID has one configuration inside a City. A Power models its own account collection and selection rules when needed; City does not provide a generic named-configuration layer. After saving Config, the Power refreshes any affected long-lived resources.

Calling Powers

Host code calls Actions and Hooks through an Agent/Workspace execution entry:


const result = await city.powers.scope({ agent_id: agent.id, workspace_id: workspace.id }).run_action({
  power: "memory",
  action: "search",
  payload: { query: "project conventions" },
});

const enriched = await city.powers.scope({ agent_id: agent.id, workspace_id: workspace.id }).pipeline("chat.enrich_message", input);
await city.powers.scope({ agent_id: agent.id, workspace_id: workspace.id }).guard("review.require_checked", enriched);

When a Power declares Actions, the host pushes one AgentTool named after that Power. The model calls it with { action, args }, or omits action to read the action index with each schema. Power system() text is contributed at the session.system_context checkpoint.

Direct communication through PowerContext

Power author APIs come from @downcity/city/power. Action, Hook, System, and Availability receive a PowerContext with restricted city, agent, workspace, session, turn, config, and storage handles.

context.session and context.agent.sessions are directly callable in-process objects; a Power does not need to retain only session_id and query the host again. Use context.workspace.files for Workspace files and context.storage.files for private Power files.

HTTP

A Power may declare structured HTTP routes. City mounts and authenticates them and injects the request-specific PowerContext. Powers do not create independent servers.

See also