Agents
How OpenWalrus agents work — lifecycle, configuration, execution modes, and stop reasons.
An agent is the core execution unit in OpenWalrus. Each agent wraps an LLM model and can invoke tools. Agents are immutable — conversation history lives in sessions, not in the agent itself.
Agent configuration
Agent configuration has two parts:
Runtime config lives in walrus.toml under [agents.<name>]:
[agents.researcher]
description = "Research specialist"
model = "deepseek-chat"
max_iterations = 32
thinking = true
members = ["walrus"]
skills = ["web-search"]
mcps = ["github"]
[agents.researcher.heartbeat]
interval = 60
prompt = "Check for new tasks"| Field | Description | Default |
|---|---|---|
description | Human-readable description | — |
model | Model name (see providers) | Default from config |
max_iterations | Maximum tool-use rounds | 16 |
thinking | Enable reasoning/thinking mode | false |
heartbeat.interval | Minutes between heartbeat ticks (0 = disabled) | 0 |
heartbeat.prompt | Message sent on each heartbeat tick | — |
members | Agents this agent can delegate to via tasks | [] (no delegation) |
skills | Skill names this agent can access (empty = all) | [] |
mcps | MCP server names this agent can access (empty = all) | [] |
compact_threshold | Token count before auto-compaction | 100000 |
System prompts are Markdown files in ~/.openwalrus/agents/:
<!-- ~/.openwalrus/agents/researcher.md -->
You are a research specialist. Find accurate, well-sourced information.The agent's name comes from the TOML key (e.g., [agents.researcher] → name researcher). A matching .md file in the agents directory provides the system prompt. Agents with a .md file but no TOML entry are registered with default config.
Execution modes
Agents support three execution methods:
Step
Execute a single LLM round — one model call plus any tool dispatches:
let events = agent.step(&tools).await?;Run
Loop until the agent stops (text response or max iterations):
let response = agent.run(&tools).await?;Stream
The canonical execution mode. Returns an async stream of events:
let stream = agent.run_stream(&tools);Agent events
During execution, agents emit events:
| Event | Description |
|---|---|
TextDelta | Incremental text from the model |
ToolCallsStart | Tool calls initiated |
ToolResult | A single tool result returned |
ToolCallsComplete | All tool calls finished |
Done | Agent has stopped |
Stop reasons
An agent stops when one of these conditions is met:
- TextResponse — the model produced text without requesting tools
- MaxIterations — reached the configured limit
- NoAction — the model returned neither text nor tool calls
- Error — an execution error occurred
Delegation scope
When members is set, the agent gains access to delegation tools (delegate, collect). It can only delegate to the named agents. The agent catalog is provided automatically.
Similarly, skills and mcps restrict which skills and MCP servers the agent can discover and use. An empty list means unrestricted access.
At build time, the runtime computes a tool whitelist from these scopes and injects a <scope> block into the system prompt listing available tools.
Heartbeat
Agents with heartbeat.interval > 0 wake up periodically. On each tick, the daemon dispatches any pending work with the heartbeat prompt.
Concurrency
Agents are stored as immutable values in the runtime. Conversation state lives in sessions, each behind Arc<Mutex<Session>>. Multiple agents run concurrently via tokio::spawn, but a single session processes one request at a time.
See event loop for how events are dispatched across agents.