Two related robustness gaps in the detached fork subagent (subagent_type: "fork" / /fork). Both stem from the fork running fire-and-forget in the background with no inline UI.
1. Fork has no turn cap (runaway token burn)
createForkSubagent launches the fork's reasoning loop with an empty RunConfig:
packages/core/src/tools/agent/agent.ts → AgentHeadless.create(FORK_AGENT.name, agentConfig, promptConfig, {}, {} as RunConfig, toolConfig, eventEmitter).
{} as RunConfig leaves max_turns unset, and AgentCore only enforces a cap when options.maxTurns is truthy (agent-core.ts). A fork is fire-and-forget — nobody awaits it — so a loop that fails to converge burns tokens silently with no upper bound.
Fix: pass a default turn cap (max_turns: 200, matching claude-code's fork). Define FORK_DEFAULT_MAX_TURNS = 200 in fork-subagent.ts and pass { max_turns: FORK_DEFAULT_MAX_TURNS }.
2. Permission-gated tool calls are silently auto-denied (should bubble)
FORK_AGENT sets approvalMode: 'default' (fork-subagent.ts:36). In the background launch path, shouldBubble = subagentConfig.approvalMode === BUBBLE_APPROVAL_MODE && isInteractive() (agent.ts:2226) — false for a fork — so bgConfig.getShouldAvoidPermissionPrompts = () => true (agent.ts:2234). With no inline UI, a tool call needing confirmation is auto-denied; since the fork is fire-and-forget, no prompt ever reaches the user.
A fork therefore cannot perform any permission-gated action — including the <fork-boilerplate>-mandated "commit your changes before reporting" — whenever the parent is in default or plan mode (the common interactive case). Forks from yolo / auto-edit / auto parents auto-approve and are unaffected.
The bubble → parent-UI bridge (shouldBubble ? cleanupApprovalBridge…, registry.bridgeApprovalEvents, TOOL_WAITING_APPROVAL — agent.ts:2449) already exists for exactly this interactive-but-detached case. claude-code's fork agent uses permissionMode: 'bubble'.
Fix: set FORK_AGENT.approvalMode to 'bubble' (BUBBLE_APPROVAL_MODE). bubble resolves to Default run behavior (tool calls require confirmation) and only flips deny → surface for interactive sessions; non-interactive forks are gated off (isForkSubagentEnabled = config.isInteractive()), so there is no headless regression.
Repro (problem 2)
- Interactive session, approval mode
default.
/fork "make a one-line edit to README and commit it".
- The fork's edit/commit (or any non-allowlisted shell) is auto-denied; the fork reports it couldn't finish, with no permission prompt shown.
Fork subagent introduced in #5155. PR incoming.
Two related robustness gaps in the detached fork subagent (
subagent_type: "fork"//fork). Both stem from the fork running fire-and-forget in the background with no inline UI.1. Fork has no turn cap (runaway token burn)
createForkSubagentlaunches the fork's reasoning loop with an emptyRunConfig:packages/core/src/tools/agent/agent.ts→AgentHeadless.create(FORK_AGENT.name, agentConfig, promptConfig, {}, {} as RunConfig, toolConfig, eventEmitter).{} as RunConfigleavesmax_turnsunset, andAgentCoreonly enforces a cap whenoptions.maxTurnsis truthy (agent-core.ts). A fork is fire-and-forget — nobody awaits it — so a loop that fails to converge burns tokens silently with no upper bound.Fix: pass a default turn cap (
max_turns: 200, matching claude-code's fork). DefineFORK_DEFAULT_MAX_TURNS = 200infork-subagent.tsand pass{ max_turns: FORK_DEFAULT_MAX_TURNS }.2. Permission-gated tool calls are silently auto-denied (should bubble)
FORK_AGENTsetsapprovalMode: 'default'(fork-subagent.ts:36). In the background launch path,shouldBubble = subagentConfig.approvalMode === BUBBLE_APPROVAL_MODE && isInteractive()(agent.ts:2226) — false for a fork — sobgConfig.getShouldAvoidPermissionPrompts = () => true(agent.ts:2234). With no inline UI, a tool call needing confirmation is auto-denied; since the fork is fire-and-forget, no prompt ever reaches the user.A fork therefore cannot perform any permission-gated action — including the
<fork-boilerplate>-mandated "commit your changes before reporting" — whenever the parent is indefaultorplanmode (the common interactive case). Forks fromyolo/auto-edit/autoparents auto-approve and are unaffected.The bubble → parent-UI bridge (
shouldBubble ? cleanupApprovalBridge…,registry.bridgeApprovalEvents,TOOL_WAITING_APPROVAL—agent.ts:2449) already exists for exactly this interactive-but-detached case. claude-code's fork agent usespermissionMode: 'bubble'.Fix: set
FORK_AGENT.approvalModeto'bubble'(BUBBLE_APPROVAL_MODE).bubbleresolves to Default run behavior (tool calls require confirmation) and only flips deny → surface for interactive sessions; non-interactive forks are gated off (isForkSubagentEnabled = config.isInteractive()), so there is no headless regression.Repro (problem 2)
default./fork "make a one-line edit to README and commit it".Fork subagent introduced in #5155. PR incoming.