Contents
Concepts
You deploy an agent (a program). It runs as instances, each with its own state, and each instance holds sessions (conversations) made of turns and steps. A small kernel dispatches the work; model providers and plugins supply the capabilities.
# Serverless Agent State
An agent is a cloud object, not a process you have to keep alive. It can run as a short job, as a resumable session, or as a durable agent whose transcript, plugin state, channel routes, costs, and execution timeline stay addressable while its compute sleeps.
Some agents finish after a single run; others need state to persist across messages, tool calls, approvals, webhooks, disconnects, restarts, and branches. The runtime wakes when there is work and saves progress after every step.
# Agent-friendly Development
agent.yaml is designed to be edited by both people and coding agents. It is textual, diffable, and schema-validatable, and it explicitly states the model provider, runtime plugins, secret references, and limits. Validation errors are specific enough that an agent can fix the spec and retry.
# Kernel
The kernel is the scheduler at the center of PAREL. It only dispatches; it does not implement model APIs, tools, or memory. The top-level model block selects the model provider; runtime capabilities are loaded as plugins.
The kernel runs a turn loop: take the new input, call the model, run the tools the model asked for, and repeat until the model answers without calling a tool, or a limit or a guard plugin stops the turn.
# Model providers
The model block picks the provider. Built in are anthropic, openai, and openai-responses. Any other service that speaks the OpenAI or Anthropic API works through openai-compatible or anthropic-compatible plus a baseUrl. DeepSeek, for example:
model:
provider: openai-compatible
model: deepseek-v4.1-flash
config:
baseUrl: https://api.deepseek.com/v1
apiKey: ${DEEPSEEK_API_KEY}PAREL does not provide model access. The key comes either from the agent config (a ${NAME} reference, as above) or from the keys your workspace saved in Settings → Model providers. The provider bills you directly.
# Plugins
Model providers and runtime plugins are two separate extension points:
| Type | Role | Examples |
|---|---|---|
| Model provider | LLM API adapter | anthropic, openai, openai-responses, openai-compatible, anthropic-compatible |
| Sandbox | Code execution environment | sandbox-e2b |
| Memory | Context management | memory-rolling-summary |
| Budget | Cost and turn limits | budget-cap |
| Security | Command allowlist and secret redaction | security-basic |
| Subagent | Child-agent delegation | subagent |
Runtime plugins declare lifecycle hooks — for example before/after model calls, tool execution, and context building — and the kernel dispatches to them in order.
# Policy
PAREL has no separate "policy engine." Policy is expressed through guard plugins and lifecycle hooks: a hook can continue, skip, block, suspend, or stop a model call or tool call. The first-party budget-cap (cost / turn limits) and security-basic (a command allowlist — anything outside it is blocked — plus secret redaction) are guard plugins, and custom policy uses the same hook mechanism. When a sandbox is declared but no security plugin is, the runtime auto-injects security-basic as a safety net.
# Agents, instances, and sessions
An agent is the program: a name in your workspace and the versions of its agent.yaml. An instance is one long-lived copy of the agent with its own state (plugin data kept in the instance store, such as the sandbox), its own variables (values for ${var:NAME} references), and, optionally, a pinned version. A session is one conversation on an instance: its transcript and the turns that ran in it.
Instances are named by a key you choose (customer-a, user-42) and are created the first time a key is used. Without a key, everything goes to the default instance, main, so a single-user agent never has to think about instances. Sessions on the same instance share its state: a new session starts a new conversation, not a new sandbox. Resetting an instance clears its state and keeps its sessions.
# A session on the instance "customer-a" (created on first use)
parel sessions create support-bot --instance customer-a
# Values for ${var:NAME} references; replaces the instance's whole set
parel instances vars support-bot customer-a --set REGION=eu
parel instances list support-bot
parel instances reset support-bot customer-a # clear its state, keep its sessionsIn the console, each agent has an Instances tab. Details are in the API reference and the CLI reference.
# Versions and deployments
Every parel deploy creates a new version of the agent (v1, v2, …). A version never changes: it holds the agent.yaml as deployed and the exact plugin versions resolved at that moment. One version is live at a time. Rolling back means making an older version live again; it does not create a new version.
Sessions follow the live version at turn boundaries. New sessions start on it, and a running session switches when its current turn ends; a turn never changes version halfway. Each turn records the version it ran on. The exceptions: sessions of an instance pinned to a version run that version; sessions made by fork, branch, or replay keep the version they came from; and an agent with runtime.deploymentTracking: pinned keeps every session on the version it started with.
parel versions list support-bot # * marks the live version parel rollback support-bot --to v3 # make v3 live again # Try v4 on one instance before making it live for everyone parel instances pin support-bot canary --version v4 parel promote support-bot --version v4 parel instances unpin support-bot canary # follow the live version again
The agent's Versions tab in the console shows the same history, and Make live there does what parel promote does. More commands are in the CLI reference.
# Turns and steps
A turn is one exchange: it starts with an input (a message or a channel event) and ends with the agent's final reply. A step is one model call plus the tool calls it asked for. A turn runs steps until the model answers without calling a tool:
Turn 1
Step 1 model call → "Let me check." + tool call: bash("ls /etc | wc -l")
tool runs → "74"
Step 2 model call → "There are 74 entries in /etc."
[turn ends: no more tool calls]A turn also ends when it reaches runtime.maxSteps (200 by default), when a guard plugin stops it, or on an error. Turns in one session run one at a time; a message that arrives during a turn waits for the next one, unless you send it into the running turn (see below). Steps are also what PAREL plans meter.
Progress is saved after every step. If the runtime restarts in the middle of a turn, the turn continues from the last saved step. A tool call that was cut off is not run again: it gets an "outcome unknown" result, and the model decides what to do.
Session status
The status the API reports, and the word the console shows for it:
| API status | Console | Meaning |
|---|---|---|
| running | Running | A turn is running. |
| suspended | Waiting | Paused until an outside event or an approval arrives, or until you resume it from a breakpoint. |
| ready | Open | Idle. Send a message to continue. |
| completed | Completed | Ended: the session was terminated, or it was a sub-agent session that finished its task. |
| error | Failed | The last turn failed. A new message starts a new turn. |
| timeout | Timed out | A turn made no progress for too long and was closed. A new message starts a new turn. |
# Messages during a running turn
By default, a message sent while a turn is running waits and starts the next turn. Send it with injectInFlight: true and it joins the running turn instead: the model sees it at the next step. If no turn is running, it simply starts one. The console chat sends messages this way while the agent is working, and the CLI has parel steer.
POST /sessions/ssn_x1Y2z3A4b5C6/messages
{ "content": "Skip the tests, just fix the build.", "injectInFlight": true }
# The same from the CLI
parel steer --session ssn_x1Y2z3A4b5C6 "Skip the tests, just fix the build."# Slash commands
A message that starts with / followed by a known command name runs that command instead of starting a turn, and it is not added to the conversation. If a turn is running, the command waits for it to end. Plugins provide the commands: memory-rolling-summary adds /compact, which folds older history into the summary right away (optionally with a note on what to keep). The only built-in command is /help, which lists the commands the session has. A mistyped command is sent as a normal message, with a warning in the response. Commands in channel messages are not recognized yet.
$ /compact keep the open TODOs [/compact] Compacted 24 message(s) into the summary; the last 12 stay verbatim. $ /help [/help] Available commands: /help — List the commands available in this session /compact <focus> — Fold older history into the running summary now
# Images and PDFs
Through the API or the session WebSocket, a message can carry images (PNG, JPEG, GIF, WebP) and PDFs next to its text, encoded as base64. Limits: 1 MiB per file, and at most 3 files and 1.25 MiB in total per message. If the model can't read a file type, it gets a short text note in its place.
POST /sessions/ssn_x1Y2z3A4b5C6/messages
{
"content": [
{ "type": "text", "text": "Does the receipt match the invoice?" },
{ "type": "image", "mediaType": "image/jpeg", "data": "/9j/4AAQSkZJRg..." },
{ "type": "file", "mediaType": "application/pdf", "data": "JVBERi0xLjcK..." }
]
}# Turn receipts and idempotency
Sending a message returns its turnId (or the status queued when a turn is already running), and GET /turns/:turnId tells you at any later time whether that turn is still running or how it ended. With an Idempotency-Key header, a retry with the same key never starts a second turn, and GET /sessions/:id/turns?by_idempotency_key=… finds the turn for a key. See the API reference.
# Snapshots, branches, replays, and forks
PAREL saves a snapshot of a session at the end of every turn, and you can take one at any time. From a snapshot you can branch (start a new session from that point, optionally with a different first message) or replay (run it again from that point to reproduce what happened). The original session never changes. Breakpoints pause a session before its next step, model call, or tool call; it shows as Waiting until you resume or cancel.
A fork (POST /sessions/:id/fork) copies a session's conversation so far into a new, independent session, and can run a first message on it. Sessions made by branch, replay, or fork keep the agent version they came from. Everything here works through the API; snapshots, branch, replay, and breakpoints are also on the session page in the console, under Snapshots.
# Channels (beta)
A channel lets outside events start turns, such as a Slack message or a Telegram update. There are two connection types: webhook (the outside service calls a PAREL URL) and managed_ws (PAREL keeps an outgoing WebSocket open, as Slack Socket Mode needs). A connector plugin speaks the service's protocol; published today are @parel/channel-slack-socket (managed_ws) and @parel/channel-telegram (webhook). PAREL keeps the connection up, drops duplicate events, routes each event to a session, and sends the agent's reply back.
Declare channels in the channels block of agent.yaml (deploying creates the connection and binds it to the agent), or create them through the API; the console has no channel screens yet. The binding's routing mode (main / per_subject / per_actor / isolated) decides which session an event lands in, and instance picks the instance. With routing.injectInFlight, new events from the same conversation join a running turn. An always-on managed_ws connection needs the Hobby or Pro plan; webhooks work on every plan.
channels block in the agent.yaml reference.# Configuration
Agents are declared through agent.yaml. Model providers and runtime plugins are configured separately:
version: "1"
agent:
name: research-bot
model:
provider: anthropic
model: claude-sonnet-5
plugins:
- system-static:
prompt: "You are a research assistant."
- sandbox-e2b:
apiKey: ${E2B_API_KEY}