Subagents
Feature Definition
Section titled “Feature Definition”A subagent is a child agent spawned by a parent agent to handle a focused subtask. The parent delegates work, waits for (or ignores) the result, and continues. The key constraints are:
- Scoped tool surface — the subagent should only have access to the tools its task requires. An exploration subagent has no need to write files.
- Bounded iterations — a subagent should have a
max_stepsbudget; it shouldn’t run indefinitely while the parent waits. - Recursion prevention — subagents must not spawn further subagents without explicit permission, or you get infinite agent trees.
- Context isolation — the subagent starts with a fresh context window, seeded by the parent’s prompt. It does not inherit the parent’s full chat history.
The three reference implementations handle this very differently:
- Aider has one mechanism: the architect pattern, which is sequential and synchronous (one coder delegates to another).
- Codex has true async subagent spawning via
AgentControl::spawn_agent(), but no tool filtering. - OpenCode has the most complete system: a
tasktool that creates a childSessionwith inherited permissions and a parent/child link in the database.
Aider Implementation
Section titled “Aider Implementation”Aider has no general subagent mechanism. The closest equivalent is the architect pattern: ArchitectCoder completes its design phase, then spawns an editor coder to execute the changes.
Architect → Editor Delegation
Section titled “Architect → Editor Delegation”File: aider/coders/architect_coder.py:6
class ArchitectCoder(AskCoder): edit_format = "architect" auto_accept_architect = False
def reply_completed(self): content = self.partial_response_content
# If user hasn't pre-approved, ask now if not self.auto_accept_architect: if not self.io.confirm_ask("Edit the files?"): return
# Spawn the editor coder as a "subagent" editor_model = self.main_model.editor_model or self.main_model editor_coder = Coder.create( main_model=editor_model, edit_format=self.main_model.editor_edit_format, suggest_shell_commands=False, map_tokens=0, # no repo map in editor phase from_coder=self, # inherit files and history )
editor_coder.run(with_message=content, preproc=False)
# Propagate cost back to parent self.move_back_cur_messages("I made those edits to the files.") self.total_cost = editor_coder.total_costKey Characteristics
Section titled “Key Characteristics”- Synchronous — the architect blocks while the editor runs. No parallelism.
- Sequential, not hierarchical — the editor is not a “child” in any persistent sense. It runs once and the result is folded back into the architect’s history.
- Context inheritance via
from_coder— file lists, chat history, and cost totals are copied. The repo map is NOT inherited (map_tokens=0). - No recursion guard — Aider doesn’t prevent the editor from spawning another architect, though in practice no built-in editor format does this.
- User approval gate —
auto_accept_architectdefaults toFalse. The user sees the architect’s proposal and explicitly approves before the editor runs.
What Aider Lacks
Section titled “What Aider Lacks”- Parallel subagent execution
- Named subagents with distinct permission sets
- Output streaming from subagent to parent in real-time
- Any mechanism for spawning more than one level of delegation
Codex Implementation
Section titled “Codex Implementation”Codex has proper subagent spawning via AgentControl. A parent agent thread can create new child agent threads, each with their own Config and independent execution lifecycle.
AgentControl::spawn_agent
Section titled “AgentControl::spawn_agent”File: codex-rs/core/src/agent/control.rs:40
pub async fn spawn_agent( &self, config: crate::config::Config, // full config for the child items: Vec<UserInput>, // initial prompt session_source: Option<SessionSource>,) -> CodexResult<ThreadId>Flow:
- Check
Guardsto ensure spawn count is withinagent_max_threadslimit (default: 6). - If limit exceeded, return
CodexErr::AgentLimitReached. - Register new thread in
ThreadManagerState. - Spawn a tokio task running a full
Codexevent loop with the given config. - Return
ThreadId— the caller can subscribe to status updates viasubscribe_status().
AgentStatus Lifecycle
Section titled “AgentStatus Lifecycle”File: codex-rs/core/src/agent/status.rs
pub enum AgentStatus { PendingInit, Running, Completed(String), // final message from agent Errored(String), // error description Shutdown, NotFound,}State transitions:
TurnStarted → RunningTurnComplete → Completed(final_message)Error → Errored(description)TurnAborted → Errored("Interrupted")ShutdownComplete → ShutdownThe parent subscribes to this channel and can poll or await completion:
let mut status_rx = control.subscribe_status(child_thread_id).await?;// Wait until child completeswhile let Some(status) = status_rx.recv().await { match status { AgentStatus::Completed(msg) => { /* use result */ break; } AgentStatus::Errored(e) => { /* handle error */ break; } _ => { /* still running */ } }}Spawn Guards
Section titled “Spawn Guards”File: codex-rs/core/src/agent/control.rs:2
pub(crate) struct AgentControl { manager: Weak<ThreadManagerState>, state: Arc<Guards>, // tracks active thread count}Guards enforces:
MAX_THREAD_SPAWN_DEPTH— prevents spawning beyond a nesting depthagent_max_threads— total concurrent agent cap across the whole session
Test (control.rs:480):
#[tokio::test]async fn spawn_agent_respects_max_threads_limit() { let max_threads = 1usize; let first = control.spawn_agent(config.clone(), input("hello"), None).await.unwrap();
let err = control.spawn_agent(config, input("hello"), None).await.unwrap_err(); assert!(matches!(err, CodexErr::AgentLimitReached { .. }));}Resuming from Rollout
Section titled “Resuming from Rollout”File: codex-rs/core/src/agent/control.rs:71
pub async fn resume_agent_from_rollout( &self, config: crate::config::Config, rollout_path: PathBuf, // path to JSONL session file session_source: SessionSource,) -> CodexResult<ThreadId>This allows spawning a subagent that picks up from a previously-recorded session. The rollout file contains the full event log; the new agent replays it and continues from that state.
What Codex Lacks for Subagents
Section titled “What Codex Lacks for Subagents”- Tool filtering per subagent — child agents get the same tool set as the parent (controlled only by
ApprovalPolicy). - Context isolation — there is no mechanism to give a subagent a pre-seeded context without creating a rollout file first.
- Output routing — subagent events go to the same global event bus as the parent; the caller must filter by
ThreadId.
OpenCode Implementation
Section titled “OpenCode Implementation”OpenCode’s subagent system is the most complete. It’s built on the task tool, which creates a child Session in the database, runs an agent against it, and returns the output to the parent.
The task Tool
Section titled “The task Tool”File: opencode/packages/opencode/src/tool/task.ts
Parameters:
const parameters = z.object({ description: z.string(), // 3-5 word label shown in UI prompt: z.string(), // full task prompt for the subagent subagent_type: z.string(), // agent name — must have mode: "subagent" task_id: z.string().optional(), // resume an existing child session command: z.string().optional(), // name of triggering slash command})Execution Flow
Section titled “Execution Flow”File: opencode/packages/opencode/src/tool/task.ts:45
Step 1 — Permission check:
// Validate caller has permission to invoke this subagent typeconst callerPermissions = ctx.session.permission;if (!PermissionNext.allows(callerPermissions, "task", subagent_type)) { throw new Error(`Permission denied: cannot spawn subagent '${subagent_type}'`);}Step 2 — Session creation or resume:
const session = task_id ? await Session.get(task_id) // resume existing : await Session.create({ parentID: ctx.sessionID, // link to parent title: `${description} (@${subagent_type})`, permission: [ // Prevent subagents from spawning further subagents // (unless the parent explicitly has task permission) ...(hasTaskPermission ? [] : [ { permission: "task", pattern: "*", action: "deny" }, ]), // TodoWrite/TodoRead denied for subagents (no UI) { permission: "todowrite", pattern: "*", action: "deny" }, { permission: "todoread", pattern: "*", action: "deny" }, ], });Step 3 — Execute subagent:
const result = await SessionPrompt.prompt( session.id, prompt, { agentType: subagent_type });Step 4 — Return output:
return { task_id: session.id, content: `<task_result>\n${result.output}\n</task_result>`,}Session Hierarchy
Section titled “Session Hierarchy”File: opencode/packages/opencode/src/session/index.ts:30
export const Session.Info = z.object({ id: Identifier.schema("session"), parentID: Identifier.schema("session").optional(), // null for root sessions title: z.string(), projectID: z.string(), permission: PermissionNext.Ruleset.optional(), // per-session overrides time: z.object({ created: z.number(), updated: z.number(), compacting: z.number().optional(), archived: z.number().optional(), }),})Child sessions are first-class entities in the database. They persist after the task completes, so:
- The parent can re-read the child’s output later via
task_id - Users can inspect child session history in the UI
- The child can be resumed with a follow-up prompt
Permission Inheritance
Section titled “Permission Inheritance”The child session’s permission rules are the union of:
- The subagent’s built-in permission rules (from
AgentDef) - The additional restrictions added by the
tasktool (TodoWrite deny, recursion deny) - Any further restrictions from the parent session’s config
This means a parent running in a restricted session propagates those restrictions to its children. A child cannot exceed the parent’s permissions.
Recursion Prevention
Section titled “Recursion Prevention”By default, subagents spawned by the task tool have the task permission denied. A subagent cannot spawn further subagents unless the parent’s permission config explicitly includes:
permission: task: allow # explicitly permit spawning further subagentsThe built-in general agent has task: allow. The built-in explore agent does not.
Output Format
Section titled “Output Format”The task tool returns a structured string to the parent model:
task_id: ses_01abc123<task_result>[output from the subagent's final response]</task_result>The parent model can reference this output in subsequent reasoning. It can also resume the child by passing task_id in a follow-up task call with a new prompt.
Claude Code Implementation
Section titled “Claude Code Implementation”Reference: Claude Code Subagents docs (fetched 2026-02-21)
Claude Code has the most configurable subagent system, with six built-in agents, custom agent definitions via markdown files, tool filtering, persistent memory, background execution, and git worktree isolation. Subagents are spawned via the Task tool — the same tool used in all other references — but with significantly more configuration surface.
Built-in Subagents
Section titled “Built-in Subagents”| Agent | Model | Tool Access | Purpose |
|---|---|---|---|
| Explore | Haiku | Read-only (Glob, Grep, Read, WebFetch, WebSearch) | Fast codebase search and analysis |
| Plan | Inherits | Read-only | Research and architectural planning |
| general-purpose | Inherits | All except Task, ExitPlanMode, Edit, Write, NotebookEdit | Complex multi-step tasks |
| Bash | Inherits | Terminal only | Running commands in separate context |
| statusline-setup | Sonnet | Limited (Read, Edit) | Status line configuration |
| Claude Code Guide | Haiku | Limited (Glob, Grep, Read, WebFetch, WebSearch) | Questions about Claude Code itself |
Custom Subagent Configuration
Section titled “Custom Subagent Configuration”Custom subagents are markdown files with YAML frontmatter (system prompt in the body). Stored at four scope levels with precedence:
| Location | Scope | Priority |
|---|---|---|
--agents CLI flag | Current session | 1 (highest) |
.claude/agents/ | Project | 2 |
~/.claude/agents/ | User | 3 |
Plugin agents/ | Plugin-scoped | 4 (lowest) |
Frontmatter Fields
Section titled “Frontmatter Fields”| Field | Type | Purpose |
|---|---|---|
name | string | Unique identifier (lowercase, hyphens, max 64 chars) |
description | string | When Claude should delegate to this agent (critical for auto-delegation) |
tools | list | Allowlist of available tools. Task(agent_type) restricts which agents can be spawned |
disallowedTools | list | Denylist removed from inherited/specified tools |
model | string | sonnet, opus, haiku, or inherit |
permissionMode | string | default, acceptEdits, dontAsk, bypassPermissions, plan |
maxTurns | int | Maximum agentic turns before stop |
skills | list | Skills preloaded into context at launch (full content, not just metadata) |
mcpServers | list | MCP servers available to this subagent |
hooks | object | Lifecycle hooks scoped to subagent’s execution |
memory | string | Persistent memory scope: user, project, or local |
background | bool | true = always run as background task |
isolation | string | worktree = isolated git worktree copy |
Critical Architectural Properties
Section titled “Critical Architectural Properties”-
No nesting: Subagents absolutely cannot spawn other subagents. This is an unconditional prohibition — no depth limit, no config to override it.
Task(agent_type)intoolsonly applies when an agent runs as the main thread viaclaude --agent. -
Context isolation: Subagents get a fresh context with system prompt + specified skills + CLAUDE.md. They do not inherit parent conversation history. The parent must include all relevant context in the
promptfield. -
Permission inheritance: Subagents inherit the parent’s permission context but can override mode via
permissionMode. Exception:bypassPermissionsfrom the parent takes absolute precedence and cannot be overridden by the child. -
Output returns to parent: Subagent results are summarized back to the main conversation. The Task tool returns a single message — not the full subagent transcript.
-
Persistent memory: Agents with a
memoryfield get a cross-session directory:- User scope:
~/.claude/agent-memory/<name>/ - Project scope:
.claude/agent-memory/<name>/ - Local scope:
.claude/agent-memory-local/<name>/The first 200 lines ofMEMORY.mdin this directory are injected into the system prompt at startup. Agents can write toMEMORY.mdto persist learnings across sessions.
- User scope:
-
Background subagents: Agents with
background: truerun concurrently. Permissions are pre-approved upfront — anything not pre-approved is auto-denied. MCP tools are unavailable in background mode. -
Git worktree isolation:
isolation: worktreecreates a temporary git worktree, giving the subagent an isolated copy of the repository. The worktree is cleaned up if no changes are made; if changes exist, the worktree path and branch are returned. -
Transcript persistence: Subagent transcripts are stored as
agent-{agentId}.jsonl. They survive main conversation compaction and are cleaned up aftercleanupPeriodDays(default 30). -
Auto-compaction: Subagents auto-compact at ~95% context capacity (configurable via
CLAUDE_AUTOCOMPACT_PCT_OVERRIDE).
Tool Filtering
Section titled “Tool Filtering”Claude Code’s tool filtering is more granular than either Codex or OpenCode:
toolsallowlist: Only the listed tools are available. SupportsTask(agent_type)to restrict which agents can be spawned when the agent runs as main thread.disallowedToolsdenylist: Listed tools are removed from whatever set the agent would otherwise have.- Combined: the effective tool set is
tools - disallowedTools.
To disable a specific built-in subagent, add Task(subagent-name) to deny rules in settings or --disallowedTools "Task(Explore)".
Key Design Differences from Codex and OpenCode
Section titled “Key Design Differences from Codex and OpenCode”| Feature | Codex | OpenCode | Claude Code |
|---|---|---|---|
| Spawn mechanism | AgentControl::spawn_agent() | task tool creating child Session | Task tool with typed agent selection |
| Tool filtering | None (same as parent) | Permission-based deny | tools allowlist + disallowedTools denylist |
| Nesting | Depth-limited (MAX_THREAD_SPAWN_DEPTH) | Permission-based deny | Absolute prohibition (no nesting) |
| Context isolation | Separate Config, no preset context | Fresh Session with parentID | Fresh context + system prompt + preloaded skills |
| Concurrency limit | agent_max_threads (default 6) | Sequential | No explicit limit (parallel supported) |
| Background mode | N/A | N/A | Yes, with pre-approved permissions |
| Persistent memory | Via rollout files | SQLite session persistence | Dedicated memory directory with MEMORY.md |
| Model control | Via Config | Inherits from parent | Per-agent model selection |
| Git isolation | N/A | N/A | isolation: worktree |
| Lifecycle hooks | N/A | N/A | Hooks in agent frontmatter |
Pitfalls & Hard Lessons
Section titled “Pitfalls & Hard Lessons”Sequential Delegation Is Not True Parallelism
Section titled “Sequential Delegation Is Not True Parallelism”Aider’s architect→editor delegation is synchronous. The parent blocks completely while the editor runs. If the editor makes a mistake, the user must restart the whole turn. There is no mechanism to have the architect review the editor’s output and iterate.
Subagent Output Is Unbounded By Default
Section titled “Subagent Output Is Unbounded By Default”The task tool in OpenCode returns the full subagent output to the parent. If the subagent writes 10,000 tokens of analysis, all of it goes into the parent’s context window. This can exhaust the parent’s context budget on the first tool call.
Fix: The OpenCode explore agent limits output via its permission config. For custom agents, always set steps and design the system prompt to produce concise output.
Recursion Guards Are Bypassable
Section titled “Recursion Guards Are Bypassable”Codex’s MAX_THREAD_SPAWN_DEPTH is a compile-time constant. A prompt injection that convinces a subagent to call a tool that itself spawns agents can still trigger multi-level recursion if the depth tracking is not correctly threaded through the agent’s context.
OpenCode’s permission-based approach (task: deny for subagents) is more robust because it’s enforced at the tool dispatch layer, not the agent spawn layer.
Context Isolation Means Re-Explaining the Problem
Section titled “Context Isolation Means Re-Explaining the Problem”A subagent starts with a fresh context. The parent must include all relevant background information in the prompt field. If the parent’s reasoning context is 20,000 tokens, the subagent doesn’t see any of it — only what’s in prompt. Forgetting this leads to subagents that ask questions the parent already knows the answer to.
Thread Limits Create Deadlocks
Section titled “Thread Limits Create Deadlocks”In Codex, if a parent agent spawns 6 subagents (hitting agent_max_threads) and then waits for all of them, but each subagent tries to spawn a child to complete its work, those child spawns will fail with AgentLimitReached. The parent deadlocks waiting for children that can never complete.
Fix: Set agent_max_threads conservatively and reserve headroom for sub-level spawns. Alternatively, use a task queue pattern where children are queued rather than spawned immediately.
Abandoned Child Sessions Accumulate
Section titled “Abandoned Child Sessions Accumulate”In OpenCode, each subagent creates a persisted Session in SQLite. If the parent is cancelled or crashes mid-task, child sessions are left in the database in an incomplete state. There is no garbage collection. Over time, the session DB fills with orphaned partial sessions that confuse users browsing session history.
OpenOxide Blueprint
Section titled “OpenOxide Blueprint”Task Tool Parameters
Section titled “Task Tool Parameters”#[derive(Debug, Deserialize, JsonSchema)]pub struct TaskToolParams { /// 3-5 word description for UI display pub description: String, /// Full task prompt for the subagent pub prompt: String, /// Registered agent name (must have AgentMode::Subagent or All) pub subagent_type: String, /// Resume an existing child session pub task_id: Option<SessionId>,}Execution Model
Section titled “Execution Model”pub async fn execute_task( params: TaskToolParams, parent_session: &Session, registry: &AgentRegistry, db: &Database,) -> Result<TaskOutput> { // 1. Validate subagent type exists and is callable let agent_def = registry.get(¶ms.subagent_type) .ok_or(TaskError::UnknownAgent)?;
if !agent_def.mode.allows_subagent() { return Err(TaskError::NotASubagent); }
// 2. Check parent session permits spawning this agent parent_session.permissions.check("task", ¶ms.subagent_type)?;
// 3. Create or resume child session let child_session = match params.task_id { Some(id) => db.get_session(id).await?, None => db.create_session(SessionInit { parent_id: Some(parent_session.id), agent: params.subagent_type.clone(), // Inherit parent restrictions + add recursion guard permissions: build_child_permissions(parent_session, agent_def), }).await?, };
// 4. Run agent loop in current task (blocking), or spawn to background let output = run_agent_loop(child_session.id, ¶ms.prompt, agent_def).await?;
Ok(TaskOutput { task_id: child_session.id, content: output, })}Recursion Guard
Section titled “Recursion Guard”fn build_child_permissions( parent: &Session, agent_def: &AgentDef,) -> PermissionRuleset { let mut rules = agent_def.permissions.clone();
// Always deny TodoWrite/TodoRead for non-interactive subagents rules.prepend(PermissionRule::deny("todowrite", "*")); rules.prepend(PermissionRule::deny("todoread", "*"));
// Deny task spawning unless agent explicitly allows it if !agent_def.permissions.allows("task", "*") { rules.prepend(PermissionRule::deny("task", "*")); }
// Intersect with parent's permission set (child cannot exceed parent) PermissionRuleset::intersect(rules, parent.permissions.clone())}Output Truncation
Section titled “Output Truncation”The task tool must truncate subagent output before returning it to the parent model. Without truncation, a verbose subagent exhausts the parent’s context budget.
const TASK_OUTPUT_MAX_TOKENS: usize = 8192;
fn truncate_task_output(output: &str, max_tokens: usize) -> String { let token_count = count_tokens(output); if token_count <= max_tokens { return output.to_string(); }
let truncation_note = format!( "\n\n[Output truncated: {} tokens total, showing first {}]", token_count, max_tokens ); let truncated = truncate_to_tokens(output, max_tokens - 50); format!("{}{}", truncated, truncation_note)}Crates
Section titled “Crates”openoxide-agent—TaskToolParams,TaskOutput,execute_task()openoxide-session—Session,SessionInit, parent/child linkingopenoxide-permissions—build_child_permissions(),PermissionRuleset::intersect()tokio— async task execution for background subagents
Tool Filtering (from Claude Code)
Section titled “Tool Filtering (from Claude Code)”Extend TaskToolParams and agent definitions with tool filtering:
pub struct AgentDef { pub name: String, pub description: String, pub system_prompt: String, pub mode: AgentMode, pub permissions: PermissionRuleset, pub model: Option<ModelSpec>, /// Allowlist of available tools. Empty = inherit parent's tools. pub tools: Vec<String>, /// Denylist removed from inherited/specified tools. pub disallowed_tools: Vec<String>, /// Skills preloaded at startup (full content, not metadata-only). pub skills: Vec<String>, /// MCP servers available to this subagent. pub mcp_servers: Vec<String>, /// Lifecycle hooks scoped to this agent's execution. pub hooks: Option<AgentHooks>, /// Persistent memory scope. pub memory: Option<MemoryScope>, /// Always run as background task. pub background: bool, /// Git worktree isolation. pub isolation: Option<IsolationMode>, /// Maximum agentic turns before forced stop. pub max_turns: Option<usize>,}The effective tool set is computed as: (tools OR parent_tools) - disallowed_tools. The Task(agent_type) syntax in tools restricts which agents can be spawned when this agent is the main thread.
Persistent Memory (from Claude Code)
Section titled “Persistent Memory (from Claude Code)”pub enum MemoryScope { /// ~/.openoxide/agent-memory/<name>/ User, /// .openoxide/agent-memory/<name>/ Project, /// .openoxide/agent-memory-local/<name>/ (gitignored) Local,}
pub struct AgentMemory { pub scope: MemoryScope, pub dir: PathBuf, /// First N lines of MEMORY.md injected into system prompt. pub bootstrap_lines: usize, // default: 200}Agents with persistent memory can write to MEMORY.md to persist learnings across sessions. This enables agents that improve over time for project-specific tasks.
Background Subagents (from Claude Code)
Section titled “Background Subagents (from Claude Code)”pub struct BackgroundAgent { /// Permissions are pre-approved upfront. pub pre_approved_permissions: Vec<PermissionGrant>, /// Anything not pre-approved is auto-denied (no user prompt). pub auto_deny_unapproved: bool, /// MCP tools are unavailable in background mode. pub disable_mcp: bool,}Background subagents run concurrently with the main conversation. They are useful for long-running tasks (test suites, large refactors) that don’t need interactive input.
Git Worktree Isolation (from Claude Code)
Section titled “Git Worktree Isolation (from Claude Code)”pub enum IsolationMode { /// Create a temporary git worktree for the subagent. /// Cleaned up automatically if no changes are made. Worktree,}When isolation: worktree is set, the subagent works on an isolated copy of the repository. If it makes changes, the worktree path and branch are returned to the parent.
No-Nesting Policy (from Claude Code)
Section titled “No-Nesting Policy (from Claude Code)”Update the recursion guard to match Claude Code’s absolute prohibition:
fn build_child_permissions( parent: &Session, agent_def: &AgentDef,) -> PermissionRuleset { let mut rules = agent_def.permissions.clone();
// ALWAYS deny task spawning for subagents — no exceptions. // This is simpler and safer than Codex's depth-limited approach. rules.prepend(PermissionRule::deny("task", "*"));
// Always deny TodoWrite/TodoRead for non-interactive subagents rules.prepend(PermissionRule::deny("todowrite", "*")); rules.prepend(PermissionRule::deny("todoread", "*"));
// Intersect with parent's permission set (child cannot exceed parent) PermissionRuleset::intersect(rules, parent.permissions.clone())}Key Design Decisions
Section titled “Key Design Decisions”- Subagent output is always truncated — cap at
TASK_OUTPUT_MAX_TOKENS(configurable). Verbose subagents should not silently consume the parent’s context window. - Child permissions are the intersection of parent and agent — a child can never exceed parent permissions. This is simpler and safer than OpenCode’s additive deny-list approach.
- Child sessions persist — store in SQLite with
parent_idFK. Orphan cleanup runs at startup: sessions older than 7 days with nocompleted_attimestamp are archived. - No nesting, ever — following Claude Code’s absolute prohibition. Subagents cannot spawn other subagents under any configuration. This eliminates the deadlock and depth-explosion risks from Codex’s depth-limited model.
- Tool filtering is dual-mode —
toolsallowlist +disallowed_toolsdenylist provides fine-grained control without requiring users to enumerate every tool (from Claude Code). - Persistent memory — cross-session learning directory with
MEMORY.mdbootstrapping enables agents that improve over time (from Claude Code). - Background mode — concurrent execution with pre-approved permissions for long-running tasks (from Claude Code).
- Git worktree isolation — safe parallel code modifications without affecting the working tree (from Claude Code).
- Lifecycle hooks in agent definition — hooks scoped to the agent’s execution, cleaned up when the agent finishes (from Claude Code).