Summary
Two bugs manifest exclusively in ACP/Zed mode (not in the interactive CLI), causing the task tool to crash and the todo_pause tool to fail to stop the agent continuation loop.
Bug 1: this.isRunning is not a function when the task tool is invoked
Error:
{"type":"tool_response","callId":"call_CAQhiZPfNrzC3aT0QHXZt0ag","toolName":"task","result":{"error":"this.isRunning is not a function. (In 'this.isRunning()', 'this.isRunning' is undefined)"}}
Root cause: In packages/agents/src/core/subagentExecution.ts, the initInteractiveScheduler() function creates a facade object that copies the scheduler's schedule method without binding it:
return {
scheduler: {
schedule: scheduler.schedule, // ← receiver lost!
awaitCompletedCalls: channel.awaitCompletedCalls,
},
...
};
When scheduler.schedule() is later called through the facade, JavaScript sets this to the facade object, not the original CoreToolScheduler. The facade has no isRunning() method, so CoreToolScheduler.schedule() crashes when it calls this.isRunning().
Why only in ACP/Zed: The interactive CLI registers its own schedulerFactoryProvider via interactiveToolScheduler.ts, which correctly uses a closure ((req, sig) => instance.schedule(req, sig)). ACP/Zed does not register a scheduler factory, so the fallback path through initInteractiveScheduler() is taken — exposing the unbound method.
Fix: Replace schedule: scheduler.schedule with schedule: (req, sig) => scheduler.schedule(req, sig) in subagentExecution.ts.
Bug 2: todo_pause does not stop the continuation loop
Symptom: When the model calls todo_pause, the agent continues looping instead of stopping. In the CLI this works because the React UI layer has a separate continuation gate (useTodoContinuation.ts) that checks todoPaused. ACP/Zed has no such outer gate.
Root cause: AgenticLoop.buildNextMessage() unconditionally returns continueLoop: true for any successful tool — including todo_pause:
// AgenticLoop.ts — buildNextMessage()
const responseParts = buildToolResponses(agentTools);
if (responseParts.length === 0) {
return { continueLoop: false, nextMessage: [] };
}
return { continueLoop: true, nextMessage: responseParts }; // ← always continues
There is no check for todo_pause being among the completed tools. The MessageStreamOrchestrator's todoPauseSeen logic is correct but it only observes events from Turn.run(), which emits ToolCallRequest (not ToolCallResponse) for model tool blocks. The actual tool responses are produced later by the AgenticLoop scheduler, which never checks for todo_pause.
Reproduction evidence: Running scripts/test-acp-zed-bugs.mjs confirms the todo_pause scenario times out (the loop never completes), while a normal prompt completes successfully.
Fix: In AgenticLoop.buildNextMessage(), detect a successful todo_pause among the completed tools and return continueLoop: false. Also eagerly record the tool history (via recordCompletedToolHistory from loopHelpers.ts) since there won't be a next model turn to carry the response.
Reproduction
A cross-platform test script has been added at scripts/test-acp-zed-bugs.mjs:
node scripts/test-acp-zed-bugs.mjs --profile <profile-name>
It spawns the llxprt-code process in --experimental-acp mode, performs the ACP handshake, and sends prompts designed to trigger both bugs. Works on Windows, macOS, and Linux.
Impact
- The
task tool is completely unusable in Zed/ACP mode
- The
todo_pause tool (used for loop-breaking) is non-functional in Zed/ACP mode, causing infinite loops and wasted tokens
- These are core agent loop features that should work identically across all frontends
Summary
Two bugs manifest exclusively in ACP/Zed mode (not in the interactive CLI), causing the
tasktool to crash and thetodo_pausetool to fail to stop the agent continuation loop.Bug 1:
this.isRunning is not a functionwhen thetasktool is invokedError:
{"type":"tool_response","callId":"call_CAQhiZPfNrzC3aT0QHXZt0ag","toolName":"task","result":{"error":"this.isRunning is not a function. (In 'this.isRunning()', 'this.isRunning' is undefined)"}}Root cause: In
packages/agents/src/core/subagentExecution.ts, theinitInteractiveScheduler()function creates a facade object that copies the scheduler'sschedulemethod without binding it:When
scheduler.schedule()is later called through the facade, JavaScript setsthisto the facade object, not the originalCoreToolScheduler. The facade has noisRunning()method, soCoreToolScheduler.schedule()crashes when it callsthis.isRunning().Why only in ACP/Zed: The interactive CLI registers its own
schedulerFactoryProviderviainteractiveToolScheduler.ts, which correctly uses a closure ((req, sig) => instance.schedule(req, sig)). ACP/Zed does not register a scheduler factory, so the fallback path throughinitInteractiveScheduler()is taken — exposing the unbound method.Fix: Replace
schedule: scheduler.schedulewithschedule: (req, sig) => scheduler.schedule(req, sig)insubagentExecution.ts.Bug 2:
todo_pausedoes not stop the continuation loopSymptom: When the model calls
todo_pause, the agent continues looping instead of stopping. In the CLI this works because the React UI layer has a separate continuation gate (useTodoContinuation.ts) that checkstodoPaused. ACP/Zed has no such outer gate.Root cause:
AgenticLoop.buildNextMessage()unconditionally returnscontinueLoop: truefor any successful tool — includingtodo_pause:There is no check for
todo_pausebeing among the completed tools. TheMessageStreamOrchestrator'stodoPauseSeenlogic is correct but it only observes events fromTurn.run(), which emitsToolCallRequest(notToolCallResponse) for model tool blocks. The actual tool responses are produced later by theAgenticLoopscheduler, which never checks fortodo_pause.Reproduction evidence: Running
scripts/test-acp-zed-bugs.mjsconfirms the todo_pause scenario times out (the loop never completes), while a normal prompt completes successfully.Fix: In
AgenticLoop.buildNextMessage(), detect a successfultodo_pauseamong the completed tools and returncontinueLoop: false. Also eagerly record the tool history (viarecordCompletedToolHistoryfromloopHelpers.ts) since there won't be a next model turn to carry the response.Reproduction
A cross-platform test script has been added at
scripts/test-acp-zed-bugs.mjs:It spawns the llxprt-code process in
--experimental-acpmode, performs the ACP handshake, and sends prompts designed to trigger both bugs. Works on Windows, macOS, and Linux.Impact
tasktool is completely unusable in Zed/ACP modetodo_pausetool (used for loop-breaking) is non-functional in Zed/ACP mode, causing infinite loops and wasted tokens