Repository navigation
Description
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
Reacted by Kelvin Rosado, digital-spinner, JoshuaNeely, Otávio Ribeiro, mcmikemn, lyxkilo, Arnulf Heimsbakk, Alex Elting, Matti Eiden, Virtuous Contract and 33 more
Metadata
Metadata
Assignees
Labels
No labels
Activity
gf-mse commentedon Jun 14, 2026
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
kevincox commentedon Jun 27, 2026
I would like something similar to this. Right now there are two main options:
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.
fernandoglatz commentedon Jul 19, 2026
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:
Flamefire commentedon Aug 7, 2026
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
kyleburnsdev commentedon Sep 11, 2026
Another +1 to improve the experience for folks running local models who need to limit concurrent requests
feat(opencode): add configurable subagent concurrency limit
Jieiku commentedon Sep 26, 2026
I submitted a pull request that implements this feature: Jieiku@101be29
feat(core): add subagent concurrency limit
gianniacquisto commentedon Oct 16, 2026
I've just added a new PR that ports @Jieiku 's changes over to Opencode V2. #55983
PiRomant commentedon Oct 20, 2026
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.
Slot count is sometimes available from the provider, an opt-in auto-detection mode would also help:
GET /api/v1/models→models[].loaded_instances[].config.parallel("Maximum number of parallel predictions the instance can handle").GET /metricsexposesvllm:num_requests_runningandvllm: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.scrossen commentedon Oct 24, 2026
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.
opencodejr commentedon Oct 29, 2026
Thanks for the request, @Kangaroux, and for including the timing and the
llama-serverlogs, 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:
experimental.subagent_concurrencyand is linked to this issuebalabommablock-cpu commentedon Oct 29, 2026
A short map of where this stands, since #55983 now implements it. I tested that PR twice.
What #55983 does (
experimental.subagent_concurrency: N):What the thread asked for that it doesn't do (yet):
Two small UX things that matter once a call can wait a long time:
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: truewaits 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.
nightlab-agent commentedon Nov 1, 2026
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.
MatthewLeibel commentedon Nov 6, 2026
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.)
Jieiku commentedon Nov 6, 2026
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.
darkmatter2222 commentedon Nov 11, 2026
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—-1or unset for unlimited (today's behavior),0for none,Nfor 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 wayOPENCODE_FLAGSalready 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 existingSubagentJobservice, a slot is taken before the child session is created so a rejected call leaves no orphan session, and at cap0thesubagenttool 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 check34/34, andcheck:generatedexit 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.