Composed Agent Capabilities
Official Python Deep Agents documentation uses "harness" vocabulary for a bundle of long-running agent capabilities: planning, a virtual filesystem, permissions, subagents, context management, code execution, human review, skills, memory files, and profiles. BeamWeaver exposes those capabilities as normal agent composition instead of a separate create_deep_agent API or dedicated runtime mode.
Use use BeamWeaver.Agent or BeamWeaver.Agent.build/1, then enable the capabilities you need through agent fields and middleware. The same graph runtime, checkpointers, stores, interrupts, streaming, and middleware pipeline apply to both small agents and long-running composed agents.
BeamWeaver treats every Deep Agents capability as a positive declaration. Adding Middleware.TodoList adds planning. Adding filesystem or Middleware.Filesystem adds file tools. Adding subagents adds the task tool. Omitting a capability removes that behavior; there is no BeamWeaver DeepAgent mode, constructor, or negative capability flag.
Capability Map
| Deep Agents capability | BeamWeaver surface |
|---|---|
| Planning |
BeamWeaver.Agent.Middleware.TodoList and BeamWeaver.Tools.Todo
|
| Virtual filesystem |
filesystem / :filesystem, BeamWeaver.Agent.Middleware.Filesystem, BeamWeaver.Tools.Filesystem
|
| Filesystem permissions |
filesystem_permissions / :filesystem_permissions, BeamWeaver.Filesystem.Permission
|
| Task delegation |
subagents, async_subagents, BeamWeaver.Agent.Middleware.Subagents, BeamWeaver.Agent.Middleware.AsyncSubagents
|
| Context management |
compact_conversation, overflow_recovery, prompt_caching, BeamWeaver.Agent.Middleware.Summarization
|
| Code execution |
Executable filesystem backends, execute, and BeamWeaver.Agent.Middleware.ShellTool
|
| Human review |
interrupt_on and BeamWeaver.Agent.Middleware.HumanInTheLoop
|
| Skills |
skills and BeamWeaver.Agent.Middleware.Skills
|
| Memory files |
memory and BeamWeaver.Agent.Middleware.Memory
|
| Profiles |
BeamWeaver.Agent.CapabilityProfile and BeamWeaver.Agent.CapabilityProfileConfig
|
Build A Composed Agent
Runtime construction:
alias BeamWeaver.Agent
alias BeamWeaver.Agent.Middleware
alias BeamWeaver.Agent.Subagent
alias BeamWeaver.Core.Message
alias BeamWeaver.Filesystem
alias BeamWeaver.Filesystem.Permission
{:ok, agent} =
Agent.build(
name: "engineering_agent",
model: BeamWeaver.Models.init_chat_model!("anthropic:claude-sonnet-4-6"),
tools: [MyApp.Tools.SearchDocs],
filesystem: Filesystem.State.new(),
filesystem_permissions: [
Permission.new(
operations: [:read, :write],
paths: ["/workspace/.env", "/workspace/secrets/**"],
mode: :deny
),
Permission.new(operations: [:read, :write], paths: ["/workspace/**"], mode: :allow)
],
skills: ["/skills"],
memory: ["/AGENTS.md"],
subagents: [
Subagent.Spec.new(
name: "researcher",
description: "Search and summarize project evidence.",
system_prompt: "Return only sourced findings."
)
],
compact_conversation: true,
overflow_recovery: true,
prompt_caching: true,
interrupt_on: %{"edit_file" => true},
middleware: [
{Middleware.TodoList, tool_name: "write_todos"}
]
)
Agent.invoke(agent, %{messages: [Message.user("Investigate the failing tests.")]})
Module-defined agents use the same fields:
defmodule MyApp.Agents.ProjectResearcher do
use BeamWeaver.Agent
name "project_researcher"
description "Search and summarize project evidence."
model BeamWeaver.Models.init_chat_model!("anthropic:claude-sonnet-4-6")
system_prompt "Return only sourced findings."
end
defmodule MyApp.EngineeringAgent do
use BeamWeaver.Agent
alias BeamWeaver.Agent.Middleware
alias BeamWeaver.Filesystem
alias BeamWeaver.Filesystem.Permission
model BeamWeaver.Models.init_chat_model!("anthropic:claude-sonnet-4-6")
tools do
tool MyApp.Tools.SearchDocs
end
filesystem Filesystem.State.new()
filesystem_permissions [
Permission.new(operations: [:read, :write], paths: ["/workspace/.env"], mode: :deny),
Permission.new(operations: [:read, :write], paths: ["/workspace/**"], mode: :allow)
]
skills ["/skills"]
memory ["/AGENTS.md"]
subagents do
subagent MyApp.Agents.ProjectResearcher
end
compact_conversation true
overflow_recovery true
interrupt_on %{"edit_file" => true}
middleware do
use Middleware.TodoList, tool_name: "write_todos"
end
end
Composition Order
BeamWeaver has two ways to compose capabilities:
-
Declarative agent fields such as
filesystem,subagents,memory,compact_conversation, andoverflow_recovery. -
Explicit
middleware [...]entries.
Field-derived capabilities are added only when the corresponding field is set. Explicit middleware entries keep the order you declare and replace a duplicate field-derived middleware of the same type. When trace parity or prompt assembly order matters, declare the exact middleware stack instead of relying on field-derived convenience wiring.
Planning
Add BeamWeaver.Agent.Middleware.TodoList when the model should keep an explicit task list. The middleware contributes a TODO tool, stores the list in agent state, and prevents multiple TODO writes from one model response.
middleware do
use BeamWeaver.Agent.Middleware.TodoList,
state_key: :todos,
tool_name: "write_todos"
end
The default top-level tool name is todo. Set tool_name: "write_todos" when you want the official Deep Agents name. Generated subagents receive TODO planning only when you compose Middleware.TodoList into that child.
Virtual Filesystem
Set filesystem on an agent to add BeamWeaver.Agent.Middleware.Filesystem. It contributes these tools:
| Tool | Description |
|---|---|
ls
| List virtual files and directories. |
read_file
|
Read a file by absolute virtual path with optional offset and limit line pagination.
|
write_file
| Create a new file. |
edit_file
| Perform exact string replacement in a UTF-8 text file. |
glob
| Find files by glob pattern. |
grep
| Search UTF-8 file contents for a literal string. |
execute
|
Run shell commands when the backend implements BeamWeaver.Filesystem.Executable.
|
Filesystem backends are virtual and POSIX-style from the agent's perspective. Current built-in backends include:
| Backend | Use |
|---|---|
BeamWeaver.Filesystem.State
| Thread-scoped files stored in graph state. |
BeamWeaver.Filesystem.Store
|
Files persisted in a BeamWeaver.Memory.Store namespace.
|
BeamWeaver.Filesystem.Local
| Trusted local development or CI root. |
BeamWeaver.Filesystem.LocalShell
| Trusted local root plus shell execution. |
BeamWeaver.Filesystem.Composite
| Route virtual path prefixes to different backends. |
BeamWeaver.Filesystem.Sandbox
|
Adapt a BeamWeaver.Sandbox backend into the filesystem protocol.
|
read_file returns text directly for UTF-8 files. Binary files are stored as base64 BeamWeaver.Filesystem.FileData; filesystem tools return a text notice plus a base64 content block for model consumption, while custom tools can use download_files/2 for raw bytes.
For routing, persistence, local disk, sandbox, and custom filesystem details, see Filesystem .
Filesystem Permissions
Use BeamWeaver.Filesystem.Permission rules to restrict the model-visible filesystem tools:
filesystem_permissions [
Permission.new(operations: [:read, :write], paths: ["/workspace/.env"], mode: :deny),
Permission.new(operations: [:read, :write], paths: ["/workspace/**"], mode: :allow)
]
Rules are evaluated in list order. The first matching rule wins. If no rule matches, access is allowed. Permissions apply to ls, read_file, write_file, edit_file, glob, and grep. They also post-filter listed and searched paths so denied files do not appear in model-visible results.
For focused examples including read-only memory, subagent overrides, and composite filesystem caveats, see Filesystem Permissions .
Task Delegation
Pass subagents to add the task tool. Each task launches an ephemeral subagent with its own message context and returns one final result to the parent:
alias BeamWeaver.Agent.Subagent
defmodule MyApp.Agents.Researcher do
use BeamWeaver.Agent
name "researcher"
description "Collect evidence without changing files."
model BeamWeaver.Models.init_chat_model!("openai:gpt-5.4")
system_prompt "Return concise findings with file paths."
end
defmodule MyApp.Agents.Editor do
use BeamWeaver.Agent
name "editor"
description "Apply small scoped edits."
model BeamWeaver.Models.init_chat_model!("openai:gpt-5.4")
system_prompt "Edit only files mentioned by the task."
end
subagents do
subagent MyApp.Agents.Researcher
subagent MyApp.Agents.Editor
end
Synchronous subagents receive a fresh messages list so their working context does not pollute the parent conversation. Parent messages are not inherited unless the subagent explicitly sets inherit_messages: true, and parent tool protocol messages are filtered before inheritance.
Child modules do not automatically receive TODO, filesystem, skills, summarization, or compaction middleware. Compose those capabilities inside the child module when it needs them. Parent-only options such as capture_output stay on the subagent line in the parent.
Use async_subagents or BeamWeaver.Agent.Subagent.AsyncSpec entries when the work should run on a remote Agent Protocol server:
async_subagents do
async_subagent "remote_research",
description: "Background research worker.",
graph_id: "research_graph",
url: "https://agents.example.com"
end
Async subagents expose start_async_task, check_async_task, update_async_task, cancel_async_task, and list_async_tasks.
For full delegation details, see Subagents and Async Subagents .
Context And Token Management
BeamWeaver combines several context controls:
| Control | BeamWeaver surface |
|---|---|
| Summarize older messages before model calls |
BeamWeaver.Agent.Middleware.Summarization
|
| Give the model a manual compaction tool |
compact_conversation: true or compact_conversation true
|
| Retry context overflow with clipped/offloaded tool results |
overflow_recovery: true or overflow_recovery true
|
| Save oversized tool results to the filesystem |
BeamWeaver.Agent.Middleware.Filesystem
|
| Save oversized user messages to conversation-history files |
BeamWeaver.Agent.Middleware.Filesystem
|
| Enable provider-aware prompt cache controls |
prompt_caching: true or prompt_caching true
|
| Isolate heavy subtasks |
subagents and async_subagents
|
compact_conversation adds a compact_conversation tool that summarizes older messages, writes the full conversation slice to the filesystem, and replaces the old messages with a summary on the next model turn. overflow_recovery is a defensive retry path for provider context-limit errors.
Code Execution
BeamWeaver exposes shell execution through executable filesystem backends and through the prebuilt shell middleware.
Use a sandbox-backed filesystem when execution should be part of the virtual filesystem workspace:
filesystem BeamWeaver.Filesystem.Sandbox.new(
sandbox: BeamWeaver.Sandbox.local(root: "/tmp/my-agent-workspace")
)
For container isolation, use BeamWeaver.Sandbox.Docker and select a hardened runtime for production deployments when available:
filesystem BeamWeaver.Filesystem.Sandbox.new(
sandbox:
BeamWeaver.Sandbox.Docker.new(
image: "docker.io/library/python:3.11-slim",
runtime: "runsc"
)
)
For a narrower command surface, add BeamWeaver.Agent.Middleware.ShellTool with an allow-list policy instead of exposing a general execute tool.
See Sandboxes for lifecycle, file transfer, provider adapter, and security guidance.
Human-In-The-Loop
Set interrupt_on to pause before selected tools execute:
interrupt_on %{
"edit_file" => true,
"send_email" => %{allowed_decisions: [:approve, :edit, :reject]}
}
true allows approve, edit, reject, or respond decisions. A map can restrict the allowed decisions and optionally provide an argument schema or custom description. HITL requires a checkpointer because the run pauses at a graph interrupt and resumes later.
case MyApp.EngineeringAgent.invoke(input,
checkpointer: checkpointer,
config: %{"configurable" => %{"thread_id" => "review-1"}}
) do
{:interrupted, interrupt} ->
MyApp.EngineeringAgent.resume_review(
%{decisions: [%{type: :approve}]},
checkpointer: checkpointer,
config: %{"configurable" => %{"thread_id" => "review-1"}}
)
end
Skills
Set skills to load SKILL.md metadata into the system prompt. Skills use progressive disclosure: BeamWeaver reads frontmatter at startup, lists the available skills, and instructs the model to read the full SKILL.md with read_file only when relevant.
skills ["/skills/base", {"/skills/project", "Project"}]
Configure a model-visible filesystem when the agent should read full skill files or supporting assets during a run.
Each skill must have frontmatter with at least name and description:
---
name: research
description: Find source-backed answers in project files.
allowed-tools: read_file grep
---
Use `grep` first, then read narrow file ranges.
Later skill sources override earlier ones by skill name. Metadata such as license, compatibility, metadata, and allowed-tools is included in the model-visible skill list.
For source precedence, store-backed skill libraries, subagent inheritance, and unsupported interpreter-skill cases, see Skills .
Memory Files
Set memory true to load /AGENTS.md, or pass one or more paths:
memory ["/AGENTS.md", "/project/AGENTS.md"]
Memory files are always loaded into the system prompt through BeamWeaver.Agent.Middleware.Memory. They are useful for durable preferences, project conventions, and coding guidelines. They are different from BeamWeaver.Memory stores, which are application-level long-term memory stores used by tools and runtime code.
The memory middleware strips HTML comments before prompt injection and tells the model to treat memory as reference material, not hidden higher-priority system instructions. Agents can update memory by editing the configured files when the user explicitly asks to remember reusable information.
For store-backed long-term memory files, including user-scoped, agent-scoped, and organization-scoped namespaces, see Memory .
Capability Profiles
BeamWeaver.Agent.CapabilityProfile is the native profile data structure for provider or model-specific capability defaults:
alias BeamWeaver.Agent.CapabilityProfile
:ok =
CapabilityProfile.register_capability_profile(
"anthropic:claude-sonnet-4-6",
CapabilityProfile.new(
system_prompt_suffix: "Use tools carefully and keep notes in files.",
excluded_tools: ["execute"],
tool_description_overrides: %{"grep" => "Search UTF-8 files for exact text."}
)
)
profile = CapabilityProfile.get_capability_profile("anthropic:claude-sonnet-4-6")
CapabilityProfileConfig is the serializable shape for storing or shipping profile configuration. Profiles support base prompt text, prompt suffixes, extra middleware, excluded middleware, excluded tools, tool-description overrides, and general-purpose subagent metadata.
Normal BeamWeaver agent builds do not automatically apply capability profiles as an overlay; direct agent options remain the runtime source of truth. See Profiles for the full split between capability, provider, and model profiles.