A profile is a reviewed recipe for sessions: where they may work, which tools exist, which models, MCP servers and extension modules they get, what their tools’ environment holds and what must wait for a person. Clients only pick a profile by name and fill in nonsecret values.
01Anatomy
Every field has one effect
Fig. 5.1Every profile runs in its own worker, so each field acts on that process alone.
profiles:
personal:
cwdRoots: [~/src, ~/notes] # required; symlinks resolved
model: anthropic/claude-sonnet-4-5
thinking: medium # off · minimal · low · medium · high · xhigh · max
tools: [read, write, edit, bash] # the default: all four
extensions: [./extensions/personal.mjs]
mcpServers:
files: { command: mcp-files, args: [--stdio] }
remote: { url: https://mcp.example/mcp }
envFiles: [./personal.env]
requireApproval: [bash]
reader:
cwdRoots: [~/src]
tools: [read] # write, edit and bash do not exist here
Each profile appears to AHP clients as an agent named after it. Creating a session asks for a cwd under its roots, a model and a thinking level, with the profile’s defaults filled in. In Nix, the same fields sit under services.pi-daemon.profiles.
02Environment
Credentials reach tools, never clients
Fig. 5.2Clients never supply secrets. Per-session credentials live in files beside each session’s directory, read only in the worker.
sessionEnvFiles is how an application gives each of its agents its own identity and token without sending them over a protocol: with {cwd}/../env.sh, an agent working in ~/work/agents/a7/repo gets the variables in ~/work/agents/a7/env.sh in its bash, ! commands, terminals and MCP servers. A missing file is skipped; a rewritten one applies from its next use.
03Tools
A tool a profile does not list does not exist
Tool
What it does
read
reads files in the session’s environment; images (jpg, png, gif, webp, bmp) are resized to provider limits and attached for the model, as in TUI Pi
write, edit
change files; changes to one file are serialised; a chat’s changes form its changeset
bash
runs in the session’s cwd with its layered environment, output streamed and committed as it runs
ask_user
pi-daemon’s own extension: asks the person a question, free text or a choice, and waits for any client to answer. serve does not register it by itself; a profile adds it with a module that re-exports InputExtension
subagent
pi-daemon’s own extension: delegates a self-contained task to a child conversation owned by the call; added the same way, from SubagentExtension
The first four are Durable’s coding tools, registered per worker from the profile’s tools list. A profile with tools: [read] is a read-only profile: there is no bash to misuse, because none was ever registered.
04Modules
Behaviour as trusted Durable extensions
Fig. 5.3Hooks can steer; an observer only watches. Anything that changes what the agent does must be a hook.
What a module exportsextension: hooks, tools, prompt sections, wrappers and tasks, or a function returning them. observe: a read-only observer of agent events. Its name may not clash with another module, builtin-tools or pi-prompt.
Hot reloadEditing the file reloads it: the next model request uses the new version. Work already running keeps the code it started with.
FailureA module that fails to load stops its profile’s worker from starting. A reload that fails keeps the last good version and reports invalid_extension.
CommandsA module can export commands: /name runs in the session’s worker instead of a model turn, from any client, even while the chat is busy, at most once per request id. It can notify the session’s clients and keep up to 64 KiB of per-session state that survives restarts.
What reload does not doOnly the edited file is re-imported, not modules it imports, and each reload keeps the old version in memory until the worker restarts.
05MCP
The main way capability arrives
Per sessionEach open session connects its profile’s servers itself: stdio servers run in its cwd with its layered environment plus the server’s own env; url servers are reached over streamable HTTP.
Named and foundTools are mcp__<server>__<tool>. Up to 32 MCP tools are all offered to the model; beyond that it gets tool_search, which loads the best matches from its next call on.
RestartedA server that crashes or fails to start is retried with backoff, and at once when one of its tools is called. A call it dies during is an error result. Opening a session waits up to 10 seconds; a slower server adds its tools when it is up.
Visible and controllableAHP clients see each server and its state, the session’s tools, skills, prompt templates and extensions, and can stop and start a server. A stopped server stays stopped until a client starts it, or its worker restarts.
Clients cannot add MCP servers; they come only from the profile.
06TUI Pi parity
The same agent you run in a terminal
Like TUI Pi
How
system prompt
Pi’s preamble (or SYSTEM.md), tool list and rules, APPEND_SYSTEM.md, AGENTS.md and CLAUDE.md, skills and the cwd, read from the profile’s agentDir and the session’s directory as TUI Pi reads them with extensions off and the project trusted
models and logins
the same auth.json and models.json through Pi’s model runtime; a /login in TUI Pi reaches running sessions, and a refreshed OAuth token is written back under Pi’s file lock
settings
settings.json compaction, retry, steeringMode and followUpMode, live: edits apply to the next turn
commands
/compact, /model, /thinking, /fork, /new, prompt templates, /skill: commands and ! shell commands over AHP
Unlike TUI Pi
Why
coding-agent extensions
Durable cannot load them. Their behaviour comes back as Durable modules, MCP servers or, for UI, client-side extensions.
project .pi/settings.json
not read; the profile’s agentDir decides
prompt files edited mid-run
read once per worker per directory, so prompt caching works; restart the worker to pick up edits
Pi packages
skills from configured packages are used only if already installed; the daemon never installs anything
07Approvals
Calls that wait for a person, across restarts
Fig. 5.4The wait and the decision are state of the call’s Durable tool task: a restart neither loses an approval nor runs an unapproved call.
requireResultApproval works the same way after the call: an afterTool hook holds the result until a client confirms it, and a denied result reaches the model as an error. The first decision wins; others are rejected to their sender. Edited tool input is not supported.
08Applications
An application is a profile plus a client
A coordinator such as Cacophony brings its agent behaviour as a profile — its own Durable module for pause gates and lifecycle records, its MCP servers for commands, sessionEnvFiles for each agent’s identity — and drives its agents over AHP with turns, cancels and session lifecycle. Its worker is its own process with its own turn gate, so its fleet can neither crash nor starve your personal sessions.