Skip to content
Sign up
Get started

Headless mode

Run Letta Code non-interactively for scripting and automation

Headless mode allows you to run Letta Code non-interactively, making it easy to integrate into scripts, CI/CD pipelines, or compose with other UNIX tools.

Use the -p flag to pass a prompt directly:

Run a one-off prompt
letta -p "Look around this repo and write a README.md documenting it"

You can also pipe input to Letta Code:

Pipe input
echo "Explain this error" | letta -p

Letta Code supports three output formats in headless mode:

Returns the agent’s response as plain text:

Terminal window
letta -p "What files are in this directory?"

Returns a structured JSON response with metadata:

Terminal window
letta -p "List all TypeScript files" --output-format json
Example output
{
"type": "result",
"result": "Found 15 TypeScript files...",
"agent_id": "agent-abc123",
"usage": {
"prompt_tokens": 1250,
"completion_tokens": 89
}
}

Returns line-delimited JSON events for real-time streaming. This is useful for preventing timeouts and getting incremental progress:

Terminal window
letta -p "Explain this codebase" --output-format stream-json

Each line is a JSON event:

Example stream output
{"type":"system","subtype":"init","agent_id":"agent-...","session_id":"agent-...","model":"claude-sonnet-4-5","tools":[...]}
{"type":"message","message_type":"reasoning_message","reasoning":"The user is asking...","otid":"...","seq_id":1}
{"type":"message","message_type":"assistant_message","content":"Here's an overview...","otid":"...","seq_id":5}
{"type":"message","message_type":"stop_reason","stop_reason":"end_turn"}
{"type":"message","message_type":"usage_statistics","prompt_tokens":294,"completion_tokens":97}
{"type":"result","subtype":"success","result":"Here's an overview...","agent_id":"...","session_id":"...","uuid":"..."}

Messages are streamed at the token level - each chunk has the same otid (output turn ID) and incrementing seq_id.

For programmatic control, use --input-format stream-json to enable bidirectional JSON communication over stdin/stdout. This allows external programs to send messages and receive responses in a structured format.

Start bidirectional mode
letta -p --input-format stream-json --output-format stream-json

Send JSON messages to stdin (one per line):

User message
{"type": "user", "message": {"role": "user", "content": "What files are here?"}}
Initialize control request
{"type": "control_request", "request_id": "init_1", "request": {"subtype": "initialize"}}
Interrupt control request
{"type": "control_request", "request_id": "int_1", "request": {"subtype": "interrupt"}}

The CLI emits JSON messages to stdout:

Init event (emitted at session start)
{"type": "system", "subtype": "init", "agent_id": "agent-xxx", "session_id": "agent-xxx", "model": "...", "tools": [...]}
Control response
{"type": "control_response", "response": {"subtype": "success", "request_id": "init_1", "response": {...}}}
Streaming messages
{"type": "message", "message_type": "assistant_message", "content": "Hello!", "session_id": "...", "uuid": "..."}
Result (emitted after each turn)
{"type": "result", "subtype": "success", "result": "Hello!", "session_id": "...", "agent_id": "..."}

The process stays alive until stdin closes, allowing multi-turn conversations:

Multi-turn example
(
echo '{"type": "user", "message": {"role": "user", "content": "Remember: secret is BANANA"}}'
sleep 5
echo '{"type": "user", "message": {"role": "user", "content": "What was the secret?"}}'
) | letta -p --input-format stream-json --output-format stream-json

Add --include-partial-messages to receive token-level streaming events:

Terminal window
letta -p --input-format stream-json --output-format stream-json --include-partial-messages

This wraps each chunk in a stream_event:

{"type": "stream_event", "event": {"message_type": "assistant_message", "content": "Hel"}, "session_id": "...", "uuid": "..."}

By default, headless mode auto-resumes the last agent used in the current directory (just like interactive mode). This means your agent retains memory across headless runs.

Create a new agent
letta -p "..." --new
Use a specific agent
letta -p "..." --agent <agent-id>

Specify a model for the headless run:

Terminal window
letta -p "..." --model sonnet-4.5
letta -p "..." -m gpt-5-codex
letta -p "..." -m haiku

See Models for the full list of supported model IDs.

Use --yolo to bypass all permission prompts (use with caution):

Terminal window
letta -p "Refactor this file" --yolo

The --tools flag controls which tools are attached to the agent (removing them from the context window entirely):

Only load specific tools
letta -p "Analyze this codebase" --tools "Read,Glob,Grep"
No tools (conversation only)
letta -p "What do you think about this approach?" --tools ""

This is different from --allowedTools/--disallowedTools which control permissions but keep tools in context. See Permissions for more details.

Auto-allow edits only
letta -p "Fix the type errors" --permission-mode acceptEdits
Read-only mode
letta -p "Review this PR" --permission-mode plan

Use -n or --name to resume an agent by name (case-insensitive). Matches pinned agents or recent agents:

Terminal window
letta -p "Continue where we left off" --name myproject

Customize the agent’s system prompt when creating new agents:

Use a preset
letta -p "..." --new --system letta-claude
Use a custom prompt
letta -p "..." --new --system-custom "You are a Python expert who writes clean code."
Extend a preset with additional instructions
letta -p "..." --new --system letta-claude --system-append "Always respond in Spanish."

Available presets:

  • default / letta-claude - Full Letta Code prompt (Claude-optimized)
  • letta-codex - Full Letta Code prompt (Codex-optimized)
  • letta-gemini - Full Letta Code prompt (Gemini-optimized)
  • claude - Basic Claude (no skills/memory instructions)
  • codex - Basic Codex
  • gemini - Basic Gemini

Customize which memory blocks the agent uses:

Specify which preset blocks to include
letta -p "..." --new --init-blocks "persona,project"
Set values for preset blocks
letta -p "..." --new --init-blocks "persona,project" \
--block-value persona="You are a Go expert" \
--block-value project="CLI tool for Docker"
Use completely custom memory blocks (JSON)
letta -p "..." --new --memory-blocks '[
{"label": "context", "value": "API documentation for Acme Corp..."},
{"label": "rules", "value": "Always use TypeScript"}
]'
No optional blocks (only core skills blocks)
letta -p "..." --new --init-blocks ""

Available preset blocks:

  • persona - Agent’s personality and behavior
  • human - Information about the user
  • project - Current project context

Core blocks (always included):

  • skills - Available skills directory
  • loaded_skills - Currently loaded skill instructions

Force a specific toolset instead of auto-detection based on model:

Terminal window
letta -p "..." --toolset codex # Codex-style tools
letta -p "..." --toolset gemini # Gemini-style tools
letta -p "..." --toolset default # Default Letta tools

When creating a new agent with --new, specify which base tools to attach:

Specify which base tools to attach
letta -p "..." --new --base-tools "memory,web_search"

Create an agent from an AgentFile template:

Terminal window
letta -p "..." --from-af ./my-agent.af
Run lint and fix errors
letta -p "Run the linter and fix any errors" --yolo

Use JSON output to parse results programmatically:

Terminal window
result=$(letta -p "What is the main entry point of this project?" --output-format json)
echo $result | jq '.result'

Use --tools to restrict the agent to read-only operations:

Terminal window
letta -p "Review this codebase for potential security issues" --tools "Read,Glob,Grep"

Run Letta Code on a schedule using cron:

Daily code review at 9am
0 9 * * * cd /path/to/project && letta -p "Review recent changes and summarize any issues" --tools "Read,Glob,Grep" --output-format json >> /var/log/letta-review.log 2>&1
Weekly dependency check
0 10 * * 1 cd /path/to/project && letta -p "Check for outdated dependencies and security vulnerabilities" --yolo >> /var/log/letta-deps.log 2>&1