opencode icon indicating copy to clipboard operation
opencode copied to clipboard

refactor: simplify task tool subagent filtering

Open malhashemi opened this issue 2 weeks ago • 3 comments

Summary

Small cleanup to align the task tool's filtering approach with how skill.ts handles it, plus simplification of the userInvokedAgents mechanism.

Self-contained filtering:

  • Move subagent filtering into task.ts itself (using ctx?.agent from #7042)
  • Remove the post-hoc regeneration in prompt.ts

Simplify bypass logic:

  • Replace userInvokedAgents array with bypassAgentCheck boolean
  • Only check the current turn's user message (not all messages in session)
  • Prevents bypass from persisting across the entire session

Test cleanup:

  • Remove redundant filterSubagents tests (already covered by PermissionNext.evaluate tests)

Changes

  • task.ts: Filter during description generation + simplify permission check
  • prompt.ts: Remove regeneration block + use bypassAgentCheck boolean
  • test: Remove redundant tests (~130 lines)

No functional changes to permission behavior - just cleaner code!

malhashemi avatar Jan 07 '26 04:01 malhashemi

The following comment was made by an LLM, it may be inaccurate:

No duplicate PRs found

github-actions[bot] avatar Jan 07 '26 04:01 github-actions[bot]

@rekram1-node Just tidying up, no functionality changes, just moved the filtration logic to task took to be self contained.

malhashemi avatar Jan 07 '26 04:01 malhashemi

Hey! Looked into simplifying userInvokedAgentsbypassAgentCheck.

The tricky part was that the old code looked at ALL user messages in the session, so once you @ invoked any agent, the bypass would persist for the entire session - even for subsequent turns where you didn't @ invoke anything. And with using bypassAgentCheck the primary agent can literally invoke any subagent for the reset of the session once you invoke it once.

Fixed it by only checking the last user message (current turn):

// Before: checks all messages (persists across session)
const userInvokedAgents = msgs
  .filter((m) => m.info.role === "user")
  .flatMap((m) => m.parts.filter((p) => p.type === "agent"))
  .map((p) => p.name)

// After: checks only current turn
const lastUserMsg = msgs.findLast((m) => m.info.role === "user")
const bypassAgentCheck = lastUserMsg?.parts.some((p) => p.type === "agent") ?? false

This way:

  • Turn 1: @plan do X → bypasses ✅
  • Turn 2: "call plan again" (no @) → prompts as expected ✅

Also cleaned up the redundant filterSubagents tests - the PermissionNext.evaluate tests already cover similar logic.

malhashemi avatar Jan 07 '26 09:01 malhashemi

/review

rekram1-node avatar Jan 07 '26 19:01 rekram1-node

lgtm

github-actions[bot] avatar Jan 07 '26 19:01 github-actions[bot]