Terminal Sessions
Any user can open their own coding CLI already wired to your product - your capabilities on its MCP surface, your prompt in its context - from their own terminal.
A terminal session turns your product into a place a user can work from their own CLI. On a machine whose runner is paired with a terminal-capable CLI connected (agentrunner connect claude-code), one command opens their own Claude Code or Codex with your product already wired in: your capabilities on the CLI's MCP surface, your composed instructions in the model's context, and a working folder. No key to paste, nothing to configure.
agentrunner terminal --url https://your-app.com/apiThis ships with the runner and needs config.runner.enabled and nothing else: the route the session is composed from (POST /runner/terminal-spec) mounts with the rest of the /runner/* transport.
What your backend composes
Your app contributes only what it alone knows; the daemon owns the machine and everything on it.
| Your backend supplies | The daemon supplies |
|---|---|
| The tool manifest - your capabilities, at the same surface your in-app assistant runs at | The loopback MCP server the tools are served over (127.0.0.1, its own path token) |
| The instructions - your composed system prompt (AI prompts) | The CLI (the user's own, on their own subscription) and its argv |
| A session-scoped wire token | The folder the session runs in, and the local MCP servers it gets |
The CLI's tool calls come back to POST /runner/tool-call with the session id as their run id and resolve exactly like a chat turn's: scoped to that user and their active organization, under the advertised capability set, and audited. A session runs at the chat capability surface, so a capability you exclude from chat (via surfaces) is excluded here too.
Name capabilities and integrations like identifiers. The daemon joins the manifest's names into one comma-separated allowlist the CLI reads as permission rules, so a capability named list_users,Bash would pre-approve unprompted shell execution on your user's machine. The daemon refuses such a session outright, so what a name like that really does is silently break every terminal session your users open. See Capabilities and AI integrations.
What your users get on their side
Two things belong to the user, and your app can neither set nor read them.
Their own MCP servers
A user can wire their tools into the session beside yours - their issue tracker, their notes, their database:
agentrunner mcp add linear --url https://your-app.com/api --http https://mcp.linear.app/mcp
agentrunner mcp add notes --url https://your-app.com/api --command npx --arg -y --arg @acme/notes-mcp --env NOTES_TOKEN=...
agentrunner mcp listThese are local: your backend can never add, read, or list one, and any MCP server it tries to push into a session is dropped. Credentials passed with --env are encrypted at rest on the user's machine and handed to the CLI through its environment, never on a command line.
Their own folders
A session runs in the product's confined work folder by default. To work on their own project, a user grants that folder once, at their machine:
agentrunner terminal --url https://your-app.com/api --cwd ~/code/my-projectAny folder the user names is honored - their typing is the consent. Your app can never name one or ask for one: a session reports no path back, and the composed spec cannot carry one. Every session is recorded in the user's local audit log with the folder it ran in.
A terminal session is not confined, and is not meant to be. It is the user's own CLI on their own machine with their own terminal attached and approval prompts bypassed, so it can reach anything they can. A dispatched run is the opposite and is where the guarantee lives: driven headlessly, it can touch no file at all.
In the desktop app
If your product ships the desktop app, its Terminal side-panel tab is this same idea in an embedded terminal: the user's own CLI in a real pty, with a folder picker instead of a --cwd flag. It runs on the app's own agent runtime, not on this daemon - a desktop user installs nothing and pairs nothing to open one. See Terminal and side panel.
Version skew
Daemon and backend move together - there is no compatibility window, so a mismatch fails loudly rather than degrading, and updating is the fix rather than waiting.
- A daemon older than your backend has no
terminalcommand and answers with its usage banner;agentrunner updateis the fix. - A backend older than the daemon answers
404at/runner/terminal-spec, which the daemon names exactly; deploy your current backend.
Install & Pairing
What your users do - the containerized or binary AgentRunner install, one-time enrollment, signing their coding CLIs in from the app - plus the deployment requirements and the daemon's local trust guarantees.
Always-on runners
Run the runner on a VPS so automated runs never wait, and what happens to dispatched runs, automations, and chat turns while a device is offline.