Power Actions

Define structured Power Actions with Context, execution identity, and cancellation semantics.

An Action is a Power's explicit business entry point. It consists of a description, optional input schema/command/API mapping, timeout, and execute function.

import { create_action } from "@downcity/city/power";
import { z } from "zod";

const status = create_action({
  description: "Read the current execution scope.",
  input_schema: { zod: z.object({}) },
  timeout_ms: 30_000,
  execute: async ({ context }) => ({
    success: true,
    data: {
      agent_id: context.agent.id,
      workspace_id: context.workspace.id,
      session_id: context.session?.id ?? null,
      turn_id: context.turn?.id ?? null,
      tool_call_id: context.call.tool_call_id ?? null,
    },
  }),
});

Each call receives one read-only input, context. It carries the restricted City, Agent, Workspace, Session, Turn, Config, and Storage handles along with the call identity. context.call.tool_call_id is the model-assigned Tool Call ID (empty for non-tool entries), and context.abort_signal is the cancellation signal for this call.

Actions should pass context.abort_signal to network requests, polling, and long-running work. timeout_ms only triggers cooperative cancellation; underlying operations must observe the signal. The returned PowerActionResult must be JSON-serializable.

An Action may return additional current-Turn content through messages. Text/file/data Parts with role: "agent" are appended to the current Agent Message. Text/context/file Parts with role: "user" are ephemeral input for the next model step and never impersonate a persisted Session Message. The Session domain consistently uses agent; only the model protocol boundary uses assistant.

command maps Commander input to a payload, while api maps an HTTP request. They do not create a second business implementation; both end at the same execute function.

Models call Actions through the Power's own tool, whose name is the Power name. Power code may use context.city.powers.run_action() for another Power registered in City. Application code creates an explicit execution view with city.powers.scope({ agent_id: agent.id, workspace_id: workspace.id }) and calls run_action() on it.