Authoring an agent

An agent is a sandboxed function: a server-side unit whose whole definition lives in one block. Where a server unit runs code you wrote, an agent reads a goal and acts — it reasons, runs tools, reads the results, and loops until it's done. You define what it knows, what it can do, what it may touch, and when it stops — and nothing outside the block is needed.

The five fields

agent :analyst do
  prompt """
  You are a terse data analyst. COMPUTE every number with bash against /work — never
  guess one. When you have the answer, reply in exactly the requested format.
  """

  tools coreutils, web              # capabilities — the kits it may run
  grant fs                          # permissions — the host powers it may use
  limit timeout: 120_000            # guardrail — wall-clock only; turn caps don't exist
  manages "shop"                    # ownership — the `facet app` surface it runs + ships
end

tools/grant/limit are composable Elixir — a value, a list, or a reference to something defined elsewhere. Shorthand: if all you need is a prompt, write the prose straight in the body — agent :x do …instructions… end — and the runtime treats the whole body as the prompt. The four-field form is the canonical one; the shorthand is for a quick brain with default capabilities.

How it's contained

Everything the agent does, it does through bash — and bash is not a host shell. Each command is a wasm kit run in wasmtime against a /work virtual filesystem, so the agent reads and writes only inside /work and runs only the kits in its tools. It can't reach the host filesystem, spawn host processes, or open a raw socket; web access is a brokered, SSRF-gated capability. grant sets the boundary, limit sets the stop — the blast radius of an autonomous agent is its own sandbox. → Sandboxing & the Dock

Running it

agent is a server-side kind — it runs on the Nexus, on the BEAM, where the model and tools live. The runtime drives the loop for you: call the model with bash available → run any command in the sandbox → feed the output back → repeat → stop on a final answer or a limit. To run one from your own code (seeding files, capturing edits), call Nexus.Agent.run/1 with the same fields as the block:

Nexus.Agent.run(
  prompt: "You are a terse data analyst …",
  tools:  [:coreutils, :web],
  grant:  [:fs],
  task:   "How many unique users are in users.csv?",
  seed:   %{"users.csv" => csv}
)
# => {:ok, %{answer: "…", vfs_files: %{…}}}

Declare a reusable agent with the block; define one inline here for a one-off — the field names mean the same thing in both. See it built end to end in A coding agent and fanned out in Agents at scale.

Declared and enforced

The agent's powers aren't just documentation — they're enforced at runtime. tools scopes what its bash may run: a command whose kit isn't in tools is refused. grant gates the host powers — a web command with no web/net grant is refused. limit stops a runaway run at its turn or wall-clock budget. So you review an agent by reading the block — which kits, which host powers — with no hidden prompt, tool registry, or ambient access, and the capability audit checks it just like a sandbox. To add a power, write a toolkit and list it in tools.

Reacting to a run

Tag an agent #event and each run emits an event onto the bus, carrying the agent's other #tags. A hook then matches those events and fires effects — notify, run another agent, or kick off a flow. That's the reactive loop: an agent acts, a hook reacts, a flow sequences — all declared, all in the workbook.