Built-ins

Shell Built-In

Built-in shell tools, sandbox modes, and approval flow

Shell Built-In

Shell is no longer a plugin. It is a built-in capability provided by @downcity/shell and mounted on an Agent with new Shell().

import { Agent } from "@downcity/agent";
import { Shell } from "@downcity/shell";

const agent = new Agent({
  id: "repo-helper",
  path: "/path/to/project",
  shell: new Shell(),
});

Tools

Mounting shell: new Shell() adds these model tools:

  • shell_exec
  • shell_start
  • shell_status
  • shell_read
  • shell_write
  • shell_wait
  • shell_close

Sandbox Modes

Shell tools run in the Safe Sandbox by default.

Use sandbox: "unrestricted" only when a command needs host-level access, such as brew install, npm install -g, pip install --user, gh auth login, or service initialization outside the project sandbox.

shell_exec({
  cmd: "brew install ffmpeg",
  sandbox: "unrestricted",
  reason: "Homebrew writes to the host-level package directory.",
});

Unrestricted execution always requires user approval. Missing reason is rejected before execution.

Approval API

Clients approve shell requests through the Agent SDK:

const pending = await agent.approvals();

await agent.approve({ approval_id: pending[0].approval_id });
await agent.deny({ approval_id: pending[0].approval_id });

RemoteAgent exposes the same API over the Downcity Agent HTTP gateway.

Session Approval Mode

Shell approval mode is scoped to the current session:

  • ask: default mode. Every unrestricted request enters the approval queue.
  • always-allow: automatically approves shell approvals in the current session.
const modes = agent.approval_modes();

agent.set_approval_mode({
  session_id: "consoleui",
  mode: "always-allow",
});

const current = agent.approval_mode({ session_id: "consoleui" });

always-allow does not change the sandbox mode. It only records new unrestricted requests as approved instead of creating pending approvals. Existing pending approvals are not auto-approved.

Semantics

  • Shell is owned by @downcity/shell, not @downcity/plugins.
  • Agent only mounts the Shell tools and forwards session events.
  • Safe Sandbox commands can read and write the project and use the configured sandbox directory.
  • Unrestricted commands execute outside the Safe Sandbox only after per-request approval.
  • always-allow only affects new approval requests in the current session, not other sessions.
  • Every write to an unrestricted shell_start session also requires approval through shell_write.
  • agent.dispose() disposes Shell sessions.