Skip to content

fix(cli): fail headless subagent permission asks instead of hanging - #11923

Merged
marius-kilocode merged 2 commits into
mainfrom
fix/11903-subagent-permission-hang
Jul 3, 2026
Merged

fix(cli): fail headless subagent permission asks instead of hanging#11923
marius-kilocode merged 2 commits into
mainfrom
fix/11903-subagent-permission-hang

Conversation

@marius-kilocode

@marius-kilocode marius-kilocode commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Sync subagents spawned via the task tool hang forever in headless kilo run when they hit a permission ask rule. Permission.ask blocks on a deferred until a Permission.reply arrives, but the headless run loop only answers permission requests for the root session, so a child-session ask waits for a reply that never comes. Reproduced with permission: { "bash": "ask" } and a prompt that spawns a subagent running a bash command: the run stalls indefinitely after the task starts.

The deny decision is a property of the run context, not the agent definition: interactive clients (TUI, VS Code extension) surface child-session permission prompts on the parent session view and users answer them today, so subagent asks must keep queuing there. Agents with mode: "all" are also valid subagents, so keying off agent.mode === "subagent" would both miss those and not distinguish interactive from headless runs.

Two complementary mechanisms, because kilo run can evaluate permissions in-process (embedded server) or in another process (daemon attach, --attach):

  • Embedded server: plain headless kilo run marks its root session in a Kilo-owned KiloHeadless registry. When a permission request from a descendant of a marked root would queue, Permission.ask fails it immediately with DeniedError. The guard sits at the single enforcement point in Permission.ask, so every ask path is covered (tool asks, MCP tool asks, the doom-loop guard) without threading a flag through callers.
  • Daemon / attach: the registry is process-local and cannot reach a server running elsewhere, so the run loop now also answers tracked task-child permission asks over the wire, the same mechanism --auto already uses. Task children are tracked unconditionally; in plain headless mode their asks are rejected with a visible auto-rejecting notice, and under --dangerously-skip-permissions they are approved, matching what that flag already does for the root session (previously child asks hung there too).

Other behavior:

  • Root-session asks in headless runs keep the existing behavior (the run loop rejects them with a message).
  • --auto behavior is unchanged; it already approves tracked child asks, so headless marking is skipped for it.
  • Interactive clients never mark sessions and never run this loop, so subagent permission prompts remain answerable in the TUI and extension.

Shared upstream files are touched minimally: one marked guard block plus an import in permission/index.ts, and marked additions in cli/cmd/run.ts. The registry and parent resolution live in the new Kilo-owned src/kilocode/permission/headless.ts.

Verified manually with permission: { "bash": "ask" } and a prompt that spawns a bash-running subagent: the run hangs on v7.3.54 until externally killed; with this fix it completes in seconds in embedded mode (server-side deny) and in --attach mode against a separate kilo serve process (wire-level reject with the auto-rejecting notice).

Fixes #11903. Supersedes #11916, which keyed the deny off agent.mode === "subagent" and therefore missed mode: "all" subagents, denied prompts interactive users can answer today, and left the doom-loop ask path uncovered.

Comment thread packages/opencode/src/cli/cmd/run.ts
@kilo-code-bot

kilo-code-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 0
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
packages/opencode/src/cli/cmd/run.ts 706 KiloHeadless.mark/denies share state only within one process; when kilo run attaches to an already-running kilo serve daemon (or --attach), the mark never reaches the process that evaluates permissions, so the subagent hang from #11903 can still occur
Files Reviewed (5 files)
  • .changeset/subagent-permission-hang.md
  • packages/opencode/src/cli/cmd/run.ts - 1 issue
  • packages/opencode/src/kilocode/permission/headless.ts
  • packages/opencode/src/permission/index.ts
  • packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-5-20260630 · Input: 76 · Output: 26K · Cached: 3M

Review guidance: REVIEW.md from base branch main

@marius-kilocode
marius-kilocode disabled auto-merge July 3, 2026 15:53
@marius-kilocode

Copy link
Copy Markdown
Collaborator Author

Remaining known gaps, both acceptable:

Daemon mode inherits the same event-ordering assumption as --auto (metadata before child ask) — not new risk
KiloRunAuto.track only tracks direct children; grandchildren would be missed in daemon mode — moot today since Kilo disables nested subagents, and the registry's parent-walk covers them in embedded mode

@marius-kilocode
marius-kilocode merged commit 771f8c8 into main Jul 3, 2026
26 of 27 checks passed
@marius-kilocode
marius-kilocode deleted the fix/11903-subagent-permission-hang branch July 3, 2026 16:38
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
…rmission-hang

fix(cli): fail headless subagent permission asks instead of hanging
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: subagent permission asks hang indefinitely (sync subagents treated as interactive)

2 participants