feat(subagent): opt-in recursion via experimental.subagent_recursion (#65)
* feat(subagent): opt-in recursion via experimental.subagent_recursion
Adds a configuration flag that lets research subagents (explore,
librarian) spawn further subagents via call_omo_agent. Default OFF -
when absent or set to { enabled: false }, behavior is identical to
before. Existing background_task.maxDepth (default 3) and
maxDescendants (default 50) guardrails stay active.
Config shape:
experimental.subagent_recursion = {
enabled?: boolean,
allowed_agents?: string[], // defaults to ['explore', 'librarian']
}
Design choices:
- Only call_omo_agent is ungated. The core OpenCode 'task' tool stays
blocked for subagents even when the flag is on, because 'task'
bypasses the OmO BackgroundManager.assertCanSpawn budget system.
call_omo_agent goes through the budget, so depth and descendant
limits are enforced exactly as they already are for orchestrator
spawns.
- Oracle is NOT in the default allowed_agents list. Oracle is a leaf
consultant by design; enabling recursion for it would invite
runaway consultation chains. Users who want to opt Oracle in can
still do so explicitly via allowed_agents.
- Implementation leaves the existing getAgentToolRestrictions signature
unchanged (15 call sites). A new getAgentToolRestrictionsForSpawn
wraps it with the flag-aware strip. Only the 2 manager.ts spawn
sites migrate to the new function.
Files changed:
- src/config/schema/experimental.ts: adds SubagentRecursionConfigSchema
and experimental.subagent_recursion field.
- src/shared/agent-tool-restrictions.ts: adds isSubagentRecursionAllowed
and getAgentToolRestrictionsForSpawn. Exports automatically via the
shared barrel.
- src/features/background-agent/manager.ts: new subagentRecursionConfig
constructor option; both spawn sites (initial prompt line 585, resume
prompt line 894) call getAgentToolRestrictionsForSpawn instead of
getAgentToolRestrictions.
- src/create-managers.ts: threads pluginConfig.experimental?.subagent_recursion
into the BackgroundManager constructor.
Tests added: src/shared/agent-tool-restrictions.test.ts (23 cases
covering baseline, flag-off, flag-on defaults, case-insensitivity,
invisible-char stripping, explicit allowed_agents override, oracle
leaf-by-default, empty allowed_agents, custom agents).
Verification:
- bun run typecheck: clean
- bun test: 5655 pass, 0 fail (5640 -> 5655)
- lsp_diagnostics on src/: no new errors
Resolves the 6-item plan item 5 (OmO depth-2 opt-in config).
* fix(subagent-recursion): wire config through sync paths + normalize allowed_agents
Addresses reviewer feedback on PR #65:
1. Sync paths now honor the recursion flag (Copilot, codex, cubic)
Before: getAgentToolRestrictionsForSpawn was only called in the
BackgroundManager async/resume paths. Sync spawn (call_omo_agent
synchronous) and sync delegate-task (task run_in_background=false)
still used plain getAgentToolRestrictions, which means the flag
was a no-op for every synchronous subagent invocation. That's the
majority path for research tasks (explore/librarian orchestrated
synchronously).
After: thread SubagentRecursionConfig through:
- createCallOmoAgent -> executeSync -> tools spread
- createDelegateTask options -> ExecutorContext -> sendSyncPrompt
and executeSyncContinuation -> tools spread
All three spawn sites (manager launch, manager resume, call_omo_agent
sync, delegate-task sync, delegate-task resume) now consult the
flag.
2. Normalize allowed_agents entries (Copilot)
New normalizeAgentName() strips invisibles, trims whitespace, and
lowercases - applied to BOTH the incoming agentName AND each entry
in allowed_agents. Config entries like '\u200BExplore ' now match
the runtime 'explore' agent name correctly.
3. Remove hardcoded line number references from comment (Copilot)
The comment previously named manager.ts lines 578 and 887, which
would drift on future edits. Rewritten to describe the spread-order
invariant generically.
Test coverage added:
- Regression test for invisible/whitespace normalization in
allowed_agents entries.
Verification:
- bun run typecheck: clean
- bun test: 5656 pass, 0 fail (5655 -> 5656, +1 test)
- lsp_diagnostics on src/: no new errors V
Vacbo committed
d276ccc90265c1c232f8fa5930d18a1c89240b99
Parent: 4f80aa7
Committed by GitHub <noreply@github.com>
on 4/22/2026, 2:50:23 AM