Configuration¶
OpenPaw uses a two-tier configuration system: global configuration (config.yaml) and per-workspace overrides (agent.yaml).
Configuration is managed by Pydantic models in openpaw/core/config/models/ with loading and merging logic in openpaw/core/config/loader.py.
Global Configuration¶
The global configuration file (config.yaml) defines defaults for all workspaces.
Complete Example¶
# Path to agent workspaces directory
workspaces_path: agent_workspaces
# Logging configuration
logging:
level: INFO # DEBUG, INFO, WARNING, ERROR, CRITICAL
directory: logs # Directory for log files
max_size_mb: 10 # Maximum log file size before rotation
backup_count: 5 # Number of backup files to keep
per_workspace: true # Create separate log files per workspace
# Queue settings (OpenClaw-inspired)
queue:
mode: collect # collect, steer, followup, interrupt
debounce_ms: 1000 # Wait before processing collected messages
cap: 20 # Max queued messages per session
drop_policy: summarize # old, new, summarize
# Lane concurrency limits
lanes:
main_concurrency: 4 # Interactive message processing
subagent_concurrency: 8 # Delegated subagent tasks
cron_concurrency: 2 # Scheduled jobs
# Channel configurations
channels:
telegram:
token: ${TELEGRAM_BOT_TOKEN}
allowed_users: []
allowed_groups: []
# Agent defaults
agent:
# Model format: provider:model_id
model: anthropic:claude-sonnet-4-20250514
max_turns: 50
temperature: 0.7
# Builtin capabilities
builtins:
allow: []
deny: []
brave_search:
enabled: true
config:
count: 5
whisper:
enabled: true
config:
model: whisper-1
elevenlabs:
enabled: true
config:
voice_id: 21m00Tcm4TlvDq8ikWAM
model_id: eleven_turbo_v2_5
browser:
enabled: true
config:
headless: true
allowed_domains: []
blocked_domains: []
timeout_seconds: 30
persist_cookies: false
file_persistence:
enabled: true
config:
max_file_size: 52428800 # 50 MB
clear_data_after_save: false
# email:
# enabled: true
# config:
# provider: gmail
# service_account_file: config/service-account.json
# delegated_user: agent@yourdomain.com
# allowed_recipients:
# - "*@yourdomain.com"
# max_recipients: 10
Configuration Sections¶
Workspaces Path¶
Directory containing agent workspace folders. Can be absolute or relative to config file location.
Logging¶
logging:
level: INFO # Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL
directory: logs # Directory for log files
max_size_mb: 10 # Max log file size before rotation
backup_count: 5 # Number of backup files to keep
per_workspace: true # Separate log file per workspace
level — Logging verbosity. Use DEBUG for troubleshooting, INFO for normal operation.
directory — Where log files are written. Created if it doesn't exist.
max_size_mb — When a log file exceeds this size, it's rotated.
backup_count — Number of rotated log files to keep (e.g., openpaw.log.1, openpaw.log.2).
per_workspace — If true, each workspace gets its own log file (e.g., gilfoyle.log, assistant.log).
Queue Settings¶
queue:
mode: collect # Queue mode
debounce_ms: 1000 # Debounce delay in milliseconds
cap: 20 # Max messages per session
drop_policy: summarize # Policy when cap is reached
mode — How messages are queued and processed:
- collect — Gather messages briefly before processing (default)
- steer — Process immediately, redirect agent if new message arrives during run
- followup — Process sequentially, no redirection
- interrupt — Cancel current processing on new message
debounce_ms — Wait time before processing collected messages (only for collect mode). This batches rapid-fire messages into a single agent invocation.
cap — Maximum queued messages per session. When exceeded, drop_policy applies.
drop_policy — Action when queue cap is reached:
- old — Drop oldest messages
- new — Drop newest messages
- summarize — Compress old messages into a summary
See queue-system.md for detailed behavior and middleware interactions.
Lane Concurrency¶
lanes:
main_concurrency: 4 # User messages
subagent_concurrency: 8 # Delegated tasks
cron_concurrency: 2 # Scheduled jobs
Controls how many concurrent tasks can run per lane. Higher values allow more parallelism but consume more resources.
main_concurrency — Interactive user messages processing
subagent_concurrency — Background sub-agent tasks spawned via spawn_agent
cron_concurrency — Scheduled tasks (static cron YAML + dynamic agent-scheduled jobs)
Channel Configuration¶
Channel configuration is typically done per-workspace in agent.yaml rather than globally. See Channels for full setup guides for each platform.
# Global defaults (config.yaml) — optional
channels:
telegram:
token: ${TELEGRAM_BOT_TOKEN}
allowed_users: []
allowed_groups: []
Agent Defaults¶
model — Default model for all workspaces. Format: provider:model_id.
max_turns — Maximum conversation turns before agent stops (prevents infinite loops).
temperature — Model temperature (0.0-1.0). Lower = more focused, higher = more creative.
Builtins Configuration¶
builtins:
allow: [] # Empty = allow all available
deny:
- group:voice # Deny entire groups
brave_search:
enabled: true
config:
count: 5 # Number of search results
See builtins.md for detailed builtin configuration.
Workspace Configuration¶
Per-workspace configuration (agent.yaml) overrides global defaults for a specific agent.
Location¶
Complete Example¶
name: Gilfoyle
description: Sarcastic systems architect
timezone: America/Denver # IANA timezone identifier (default: UTC)
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: ${ANTHROPIC_API_KEY}
temperature: 0.5
max_turns: 50
channel:
type: telegram
token: ${TELEGRAM_BOT_TOKEN}
allowed_users: [123456789] # Only this user
allowed_groups: []
queue:
mode: steer # Override global queue mode
debounce_ms: 500 # Faster response
builtins:
deny:
- elevenlabs # Disable TTS for this workspace
browser:
enabled: true
config:
allowed_domains:
- "calendly.com"
- "*.google.com"
blocked_domains: []
persist_cookies: true
heartbeat:
enabled: true
interval_minutes: 30
active_hours: "09:00-17:00"
suppress_ok: true
delivery: channel
target_channel: telegram
target_id: 123456789
approval_gates:
enabled: true
timeout_seconds: 120
default_action: deny
tools:
overwrite_file:
require_approval: true
show_args: true
status_reminder:
enabled: true
threshold: 5
max_reminders: 3
cooldown_turns: 1
Workspace-Specific Fields¶
Basic Identity¶
name: Gilfoyle
description: Sarcastic systems architect
timezone: America/Denver # IANA timezone identifier
name — Display name for the agent (optional)
description — Brief description (optional)
timezone — IANA timezone identifier for scheduling, display, and file partitioning. Defaults to UTC if not specified. Examples: America/Denver, Europe/London, Asia/Tokyo.
Timezone Validation: Workspace config validates timezone at load time using Pydantic. Invalid IANA identifiers are rejected with a clear error message.
What uses workspace timezone:
- Heartbeat active hours window (active_hours: "09:00-17:00")
- Cron schedule expressions (APScheduler CronTrigger)
- Agent-created scheduled tasks (schedule_at timestamp parsing)
- File upload date partitions (data/uploads/{YYYY-MM-DD}/)
- /status "tokens today" day boundary
- Display timestamps in conversation archives, task notes, and filesystem listings
What remains UTC (internal storage): - JSONL logs (token_usage.jsonl, heartbeat_log.jsonl) - Session state (sessions.json) - Task internal timestamps (created_at, started_at, completed_at in TASKS.yaml) - Conversation archive JSON sidecar files - LangGraph checkpoint data
Model Configuration¶
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: ${ANTHROPIC_API_KEY}
temperature: 0.5
max_turns: 50
provider — Model provider: anthropic, openai, xai, fireworks, bedrock_converse, or any OpenAI-compatible API via openai with base_url.
model — Model identifier (provider-specific).
api_key — API key for the model provider. Use ${VAR} syntax for environment variables.
temperature — Model temperature (0.0-1.0).
max_turns — Maximum agent turns per run.
Extra kwargs: The model config supports extra="allow" — any additional fields beyond the standard set are passed through to init_chat_model(). This enables OpenAI-compatible APIs via base_url and other provider-specific options.
Channel Configuration¶
Single channel (backward compatible):
Multi-channel (same workspace, multiple platforms):
channels:
- type: telegram
token: ${TELEGRAM_BOT_TOKEN}
allowed_users: [123456789]
- type: discord
token: ${DISCORD_BOT_TOKEN}
allowed_users: [916552354470461470]
mention_required: true
triggers: ["!ask"]
type — Channel type: telegram or discord.
token — Channel-specific bot token.
allowed_users — List of allowed user IDs. Controls who can DM the bot and serves as a fallback for messages from non-allowed groups.
allowed_groups — List of allowed group/guild IDs. When a group is allowed, any user in that group can interact with the bot without being individually listed in allowed_users.
mention_required — Only respond in group channels when @mentioned (default: false).
triggers — List of keyword triggers for group chat activation. Uses OR logic with mention_required.
name — Explicit channel name (required when using two channels of the same type).
context_messages — Number of recent channel messages to fetch as context when the bot is triggered in a group channel (default: 25, set to 0 to disable). Only applies to group/server messages, not DMs.
channel_log — Persistent JSONL logging of all visible channel messages (disabled by default):
channels:
- type: discord
token: ${DISCORD_BOT_TOKEN}
allowed_groups: [111222333]
mention_required: true
context_messages: 25 # On-demand context fetch (default: 25)
channel_log:
enabled: false # Disabled by default; set true to enable
retention_days: 30 # Days before old logs are archived (default: 30)
Logs are written to memory/logs/channel/{server}/{channel}/{YYYY-MM-DD}.jsonl and are readable by the agent via read_file() and grep_files(). DMs are never logged. See Channel History for full details.
See Channels for full setup guides, multi-channel configuration, trigger-based activation, and channel history.
Queue Configuration¶
mode — Override global queue mode for this workspace.
debounce_ms — Override global debounce delay.
Heartbeat Configuration¶
heartbeat:
enabled: true
interval_minutes: 30 # How often to check in
active_hours: "09:00-17:00" # Only run during these hours (optional)
suppress_ok: true # Don't send message if agent responds "HEARTBEAT_OK"
delivery: channel # Where to deliver: channel, agent, or both
target_channel: telegram # Which channel to deliver to
target_id: 123456789 # Channel-agnostic user/chat ID (preferred over chat_id)
enabled — Enable proactive heartbeat check-ins.
interval_minutes — How often to run heartbeats.
active_hours — Only run heartbeats within this window (workspace timezone). Format: "HH:MM-HH:MM". Outside active hours, heartbeats are silently skipped.
suppress_ok — If true and the agent responds with exactly "HEARTBEAT_OK", no message is sent. This prevents noisy "all clear" messages.
output — Where to send heartbeat messages.
See Scheduling for detailed heartbeat behavior including pre-flight skip, task-aware prompts, and the HEARTBEAT_OK protocol.
Approval Gates Configuration¶
approval_gates:
enabled: true
timeout_seconds: 120 # Seconds to wait for user response
default_action: deny # Action on timeout: "deny" or "approve"
tools:
overwrite_file:
require_approval: true
show_args: true # Show tool arguments in approval prompt
delete_task:
require_approval: true
Session TTL¶
# Conversation auto-reset after inactivity (default: 180 minutes, 0 to disable)
session_ttl_minutes: 180
lifecycle:
notify_session_ttl: true # Notify user when session expires
session_ttl_minutes — Auto-resets the conversation (equivalent to /new) after the specified minutes of inactivity. Only applies to group/server channels — DMs are never auto-expired. The check is lazy — it only triggers when a new message arrives. When a session expires, channel context history is not injected into the fresh thread. Set to 0 to disable. Archives are tagged ["ttl_expired"].
lifecycle.notify_session_ttl — When true, the user receives a brief notification when a session expires and is reset.
Status Reminder¶
status_reminder:
enabled: true # Default: true
threshold: 5 # Tool-calling turns before first reminder (1-50)
max_reminders: 3 # Max reminders per agent run (0-20)
cooldown_turns: 1 # Min turns between consecutive reminders (0-10)
enabled — Enable automatic reminders for the agent to use send_message() during long silent tool-calling runs. Only active when the send_message builtin is loaded. Default: true.
threshold — Number of consecutive tool-calling turns without a send_message() call before the first reminder is injected. Range: 1-50.
max_reminders — Maximum number of reminders the agent receives per agent run. Prevents nagging. Set to 0 to disable reminders entirely while keeping detection active. Range: 0-20.
cooldown_turns — Minimum number of turns between consecutive reminders. Prevents back-to-back reminders. Range: 0-10.
Reminders are injected as <framework_instruction> tags into the agent's message stream — they do not create extra checkpoint entries or consume additional API calls.
Status Updates¶
status_updates:
enabled: true # Default: true
agent_start: true # Report when agent run begins
tool_calls_detected: true # Report when agent decides to use tools
tool_start: false # Report before each tool execution
tool_complete: false # Report after each tool execution
subagent_spawned: true # Report when sub-agent is dispatched
min_interval_seconds: 3 # Minimum seconds between auto-updates
edit_in_place: true # Edit single message in place (default: true)
typing_indicator: true # Send typing indicator while agent is processing
reactions: true # Add emoji reactions to the user's original message
use_emojis: true # Prefix status messages with relevant emojis
**enabled** — Enable automatic status updates sent to the user during agent execution. Default: `true`.
**agent_start** — Send `"Starting work..."` on the first run of a user message, and `"Continuing work..."` on subsequent runs (e.g., follow-up or self-continuation). Default: `true`. System events (cron, heartbeat, sub-agent completions) skip the agent start status to avoid mid-task confusion.
**tool_calls_detected** — Send `"Using tools: X, Y..."` when the agent decides to call tools. Default: `true`.
**tool_start** — Send `"Running tool: X..."` before each tool execution. Default: `false` (can be noisy).
**tool_complete** — Send `"Completed: X"` after each tool execution. Default: `false` (can be noisy).
**subagent_spawned** — Send `"Dispatched sub-agent: label"` when `spawn_agent` is called. Default: `true`.
**min_interval_seconds** — Minimum seconds between auto-detected status updates. Prevents spam during rapid tool-call sequences. Default: `3`.
**edit_in_place** — When `true` (default), the first status update sends a new message, and subsequent updates edit the same message in place. This prevents chat clutter. The status message is deleted after the agent run completes. When `false`, each update sends a separate message.
**typing_indicator** — When `true` (default), the framework sends a typing indicator (e.g., "typing..." on Telegram or "Agent is typing..." on Discord) while the agent is actively processing. Cleared when the agent completes or sends a real message.
**reactions** — When `true` (default), the framework adds an emoji reaction to the user's original message to indicate the agent has seen it and is working. The reaction is removed when the agent finishes. Only supported on channels that support emoji reactions (Telegram, Discord).
**use_emojis** — When `true` (default), status messages are prefixed with relevant emoji for visual clarity (e.g., `⚙️` for tool calls, `🚀` for starting work, `🤖` for sub-agent dispatch). Set to `false` for plain text status messages.
Status updates are sent directly to the user channel and do not create extra checkpoint entries or consume additional API calls. Agent-driven `report_progress` tool calls bypass all throttling.
---
### Merging Behavior
Workspace configuration deep-merges over global configuration:
- **Missing fields** inherit from global config
- **Present fields** override global values
- **Nested objects** merge recursively
**Example:**
**Global config.yaml:**
```yaml
agent:
model: anthropic:claude-sonnet-4-20250514
temperature: 0.7
max_turns: 50
Workspace agent.yaml:
Result:
model:
provider: anthropic # From global (parsed from model string)
model: claude-sonnet-4-20250514 # From global
temperature: 0.5 # From workspace
max_turns: 50 # From global
Environment Variables¶
OpenPaw supports environment variable expansion in configuration files using ${VAR_NAME} syntax. This is handled by openpaw/core/config/env_expansion.py.
Example¶
channel:
type: telegram
token: ${TELEGRAM_BOT_TOKEN}
model:
provider: anthropic
api_key: ${ANTHROPIC_API_KEY}
At runtime, OpenPaw expands these from the environment:
Per-Workspace .env Files¶
Each workspace can have its own .env file for workspace-specific secrets:
These are automatically loaded via python-dotenv when the workspace starts.
Best Practices¶
- Never commit secrets — Use environment variables for all API keys and tokens
- Use .env files — Create a
.envfile for local development (add to.gitignore) - Document required variables — List all required environment variables in README
- Provide defaults — Set sensible defaults in
config.yamlwhere possible
Required Variables¶
For basic operation:
- At least one channel bot token:
- TELEGRAM_BOT_TOKEN — Telegram bot token
- DISCORD_BOT_TOKEN — Discord bot token
- At least one model provider credential:
- ANTHROPIC_API_KEY — Claude API access
- OPENAI_API_KEY — OpenAI GPT access
- AWS credentials for Bedrock (see below)
For optional builtins:
- BRAVE_API_KEY — Web search capability
- OPENAI_API_KEY — Whisper audio transcription (also used for GPT models)
- ELEVENLABS_API_KEY — Text-to-speech
- Google service account JSON file — Email integration (see Builtins - Email)
Model Providers¶
Anthropic¶
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: ${ANTHROPIC_API_KEY}
temperature: 0.7
Available models:
- claude-sonnet-4-20250514
- claude-opus-4-20250514
- claude-haiku-4-20250514
OpenAI¶
Available models:
- gpt-4o
- gpt-4o-mini
- gpt-4-turbo
AWS Bedrock¶
OpenPaw supports AWS Bedrock models via the bedrock_converse provider. Available models include Kimi K2 Thinking, Claude, Mistral, Amazon Nova, and others.
Configuration:
model:
provider: bedrock_converse
model: moonshot.kimi-k2-thinking
region: us-east-1 # Optional, defaults to AWS_REGION env var
Available Bedrock Models:
- moonshot.kimi-k2-thinking — Moonshot Kimi K2 (1T MoE, 256K context)
- us.anthropic.claude-haiku-4-5-20251001-v1:0 — Claude Haiku 4.5
- amazon.nova-pro-v1:0 — Amazon Nova Pro
- amazon.nova-lite-v1:0 — Amazon Nova Lite
- mistral.mistral-large-2402-v1:0 — Mistral Large
Note: Newer Bedrock models may require inference profile IDs (prefixed with us. or global.) instead of bare model IDs. Use aws bedrock list-inference-profiles to discover available profiles.
AWS Credentials:
Configure via environment variables or AWS CLI profile:
# Environment variables
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_REGION="us-east-1"
# Or use AWS CLI profile
aws configure
Region Availability (Kimi K2): us-east-1, us-east-2, us-west-2, ap-northeast-1, ap-south-1, sa-east-1
API Key Exclusion: The api_key field is automatically excluded for Bedrock providers (uses AWS credentials instead).
OpenAI-Compatible APIs¶
Any OpenAI-compatible provider can be used by specifying base_url in the workspace model config. Extra kwargs beyond the standard set (provider, model, api_key, temperature, max_turns, timeout_seconds, region) are passed through to init_chat_model().
Example (Moonshot Kimi K2.5):
model:
provider: openai
model: kimi-k2.5
api_key: ${MOONSHOT_API_KEY}
base_url: https://api.moonshot.ai/v1
temperature: 1.0
This enables any provider that implements the OpenAI API interface (Moonshot, Together AI, Groq, etc.).
Configuration Validation¶
OpenPaw validates configuration on startup using Pydantic models. Common errors and solutions:
Missing required field:
Solution: Add the missing field to your config file.Invalid value:
Solution: Check allowed values in this document.Environment variable not set:
Solution: Export the variable or set it in your.env file.
Invalid timezone:
Solution: Use a valid IANA timezone from the tz database.Multiple Configurations¶
You can maintain multiple configuration files for different environments:
# Development
poetry run openpaw -c config.dev.yaml -w my-agent
# Production
poetry run openpaw -c config.prod.yaml -w my-agent
# Testing
poetry run openpaw -c config.test.yaml -w my-agent
This enables environment-specific settings without modifying workspace files.
Example use cases: - Development: verbose logging, low concurrency, test API keys - Production: INFO logging, high concurrency, production API keys - Testing: minimal logging, minimal concurrency, mock channels
Advanced Configuration¶
Custom Model Timeout¶
model:
provider: anthropic
model: claude-sonnet-4-20250514
timeout_seconds: 120 # Override default timeout
timeout_seconds — Maximum time to wait for a model response. Defaults to 60 seconds. Useful for long-running tasks or slow providers.
Queue Modes¶
The four queue modes — collect, steer, interrupt, and followup — control how the system handles messages that arrive while the agent is already working. See Queue System for detailed behavior and examples of each mode.
Builtins Allow/Deny Patterns¶
Allow all, deny specific:
Allow specific, deny all others:
Allow group, deny one member:
Priority: Deny always takes precedence over allow.
Configuration File Locations¶
Global config:
Workspace config:
Workspace .env:
Static cron definitions:
Dynamic cron storage:
Task storage:
Framework-managed state (data/ is write-protected from agents):
agent_workspaces/<workspace-name>/data/
├── conversations.db # AsyncSqliteSaver checkpoint database
├── sessions.json # Session/conversation thread state
├── token_usage.jsonl # Token usage metrics (append-only)
└── subagents.yaml # Sub-agent requests and results
Configuration Tips¶
1. Start with config.example.yaml¶
Copy the example and modify:
This ensures you have all required sections.
2. Use Workspace-Specific Overrides Sparingly¶
Only override what you need:
# Good — only overrides temperature
model:
temperature: 0.5
# Bad — duplicates global config unnecessarily
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: ${ANTHROPIC_API_KEY}
temperature: 0.5 # This is the only thing that changed
max_turns: 50
3. Document Your Environment Variables¶
Create a .env.example file:
# .env.example
TELEGRAM_BOT_TOKEN=
DISCORD_BOT_TOKEN=
ANTHROPIC_API_KEY=
BRAVE_API_KEY=
OPENAI_API_KEY=
ELEVENLABS_API_KEY=
This helps onboarding and documents required credentials.
4. Test Configuration Changes¶
After modifying config, test with verbose logging:
Watch startup logs for validation errors.
5. Use Separate Configs for Environments¶
Don't modify config.yaml for different environments. Instead:
# config.dev.yaml
logging:
level: DEBUG
# config.prod.yaml
logging:
level: INFO
# Run with -c flag
poetry run openpaw -c config.dev.yaml -w my-agent
Troubleshooting¶
Config file not found:
Solution: Ensure the file exists and the path is correct. Use-c flag to specify path.
YAML syntax error:
Solution: Check YAML syntax. Ensure consistent indentation (use spaces, not tabs).Environment variable not expanded:
Solution: Ensure the environment variable is set before running OpenPaw.Workspace not found:
Solution: Ensure workspace directory exists and contains required identity files (agent/AGENT.md, agent/USER.md, agent/SOUL.md, agent/HEARTBEAT.md).
Model provider error:
Solution: Use a supported provider:anthropic, openai, bedrock_converse, or configure OpenAI-compatible API via base_url.
Timezone validation error:
Solution: Use a valid IANA timezone. See list of tz database time zones.