Skip to content

ACP Session: extend concurrent tool execution beyond Agent (read/search/fetch, read-only shell) #3462

@zhangxy-zju

Description

@zhangxy-zju

What would you like to be added?

Follow-up to #2516. With the Agent-only concurrency fix in place, the ACP Session path still executes Read / Search / Fetch / read-only shell calls sequentially, even though CoreToolScheduler has been parallelizing these since #2563 via CONCURRENCY_SAFE_KINDS + isShellCommandReadOnly.

Expected: when the model returns, say, 5 read_file calls or a mix of read_file / grep / web_fetch, ACP runs the contiguous safe batch concurrently (matching the React TUI path).

Why is this needed?

Multi-read turns are a very common model pattern (reading 4-5 files before proposing an edit). Under the current ACP path each read blocks the next, multiplying end-to-end latency by the batch size. The React TUI path has already solved this via coreToolScheduler.partitionToolCalls — ACP users are left paying the cost.

Why it wasn't fixed as part of #2516

Agent tools were safe to add with a minimal patch because there's typically ≤ a few Agent calls per turn and no confirmation UI pressure.

Extending to read-only kinds hits a structural blocker: Session.runTool interleaves L1–L5 permission checks, client.requestPermission RPC, and invocation.execute() in a single method. Running five runTool calls concurrently would fire five parallel requestPermission RPCs at the editor simultaneously, which is a bad UX on Zed / VS Code companion.

coreToolScheduler sidesteps this by design: its state machine resolves all permissions first (calls move to scheduled), then attemptExecutionOfScheduledCalls partitions and runs.

Suggested approach

Two-phase refactor inside Session.ts:

  1. Split runTool(signal, promptId, fc) into

    • #prepareToolCall(fc) → PreparedCall | CancelledCall (build → L1–L5 → requestPermission → Pre/PostToolUseHook wiring)
    • #executePreparedCall(prepared) → Part[] (pure invocation.execute() + emit + result shaping)
  2. In runToolCalls:

    • Run #prepareToolCall for each call (permission stays sequential to avoid parallel requestPermission RPCs).
    • Then use the full isConcurrencySafe + partitionToolCalls from coreToolScheduler (ideally exported from @qwen-code/qwen-code-core).
    • Concurrent batches → Promise.all (respecting QWEN_CODE_MAX_TOOL_CONCURRENCY).

Relevant files

  • packages/cli/src/acp-integration/session/Session.tsrunTool (L1125+), runToolCalls (L1091, added in ACP Session: Agent tool calls should run concurrently #2516 fix)
  • packages/core/src/core/coreToolScheduler.ts:355isConcurrencySafe
  • packages/core/src/core/coreToolScheduler.ts:383partitionToolCalls
  • packages/core/src/tools/tools.ts:753CONCURRENCY_SAFE_KINDS

Related

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions