v1.8.1 — fix recursive subagent loop
Critical bugfix: runaway recursive subagent loop. /yt-extract <url> (default and --full-transcript modes — any path that dispatches a subagent) could spawn an ever-deepening chain of general-purpose subagents (~one new level every ~30 s), burning tokens and never producing output. Each dispatched worker's first action was Skill(yt-extract) — it re-invoked this skill instead of running the Python command it was handed, and Step 1 then dispatched the next worker, ad infinitum.
Root cause
The recursion needed two things, both introduced in v1.6.0:
- v1.6.0 removed
disable-model-invocation: trueto make the skill callable programmatically by other skills. That flag was also the hard structural guard that kept a subagent from reaching/yt-extractthrough the Skill tool. Its removal was compensated only by a tightened description — a soft, model-interpreted guard. But the worker's own task ("Extract all data for this YouTube video…") reads as an explicit extract request, so the soft guard matched exactly the case it was meant to exclude. - The skill dispatched
subagent_type: general-purpose, which inherits all tools — includingSkillandAgent. With the skill now model-invocable, the worker could (and did) re-invoke it.
Fixed
- New restricted worker agent
agents/extract-worker.md—toolsallowlist isBash, Read, Glob, Greponly, so the worker has noSkilland noAgenttool and physically cannot re-invoke the skill or spawn subagents. SKILL.mdStep 1 now dispatchessubagent_type: "yt-extract:extract-worker"instead ofgeneral-purpose. The skill stays model-invocable (programmatic invocation by other skills is preserved).- Both Step 1 subagent prompts gained a "you are a LEAF worker — run only the Bash command, never invoke a skill or dispatch a subagent" guard (defense-in-depth).
- Worker no longer asks the user directly. The subagent prompts used to instruct the worker to call
AskUserQuestionforFOLDER_EXISTSandSCREENSHOTS_ASK_USER, but subagents have noAskUserQuestiontool (it depends on the main-chat UI). The worker now returns these states; the orchestrator (main context) asks the user and re-dispatches with--force/ explicit timestamps. The--transcript-onlypath is unaffected. - New test
tests/test_skill_contract.py— static contract test locking the dispatch target (restricted worker, notgeneral-purpose), the worker tool allowlist (excludesSkill/Agent/Task), and that no worker prompt instructsAskUserQuestion. Closes the previously untested orchestration-layer gap.
Upgrade note
Adds a fourth plugin component type (an internal agent). No user-facing CLI change. To restore the hard block instead — at the cost of programmatic invocability — re-add disable-model-invocation: true to the SKILL.md frontmatter.