diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-25 10:42:53 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-25 10:42:53 +0300 |
| commit | dd146c20086e95d388e22cbca907dbf68364628b (patch) | |
| tree | a7d7be73d3c2448aad53eb14a303dcc0f675c878 | |
| parent | 7e7f545112e918451320a87922ab6b34461ed3c5 (diff) | |
fix(loop-scheduler): defer agent_busy reset after agent_end instead of isIdle polling
The agent_end handler receives ExtensionContext, not the command variant,
so ctx.waitForIdle() was never available (TypeError). The previous fix
replaced it with an isIdle() polling loop, but isIdle() remains false
while any agent_end listener is still executing (finishRun() completes
only after listeners settle). This caused a 30-second busy-wait on every
turn, leaving the UI stuck on 'Working...'.
Defer resetting agentBusy and draining pending jobs via setTimeout(..., 0)
so the work happens in a macrotask after finishRun() has completed and the
agent is genuinely idle.
| -rw-r--r-- | pi/agent/extensions/loop-scheduler/index.ts | 16 |
1 files changed, 5 insertions, 11 deletions
diff --git a/pi/agent/extensions/loop-scheduler/index.ts b/pi/agent/extensions/loop-scheduler/index.ts index cdd30a4..64da563 100644 --- a/pi/agent/extensions/loop-scheduler/index.ts +++ b/pi/agent/extensions/loop-scheduler/index.ts @@ -1209,17 +1209,11 @@ export default function loopSchedulerExtension(pi: ExtensionAPI): void { // draining the queue and our message sits there forever, visible as a // stuck "Follow-up: ..." in pi's UI. We'd also leak agentBusy=true and // block every subsequent pending job because no further agent_end fires. - // Wait for the run to actually finish before draining. - // ctx.waitForIdle() is only available on ExtensionCommandContext, not - // ExtensionContext, so we poll isIdle() instead. - let attempts = 0; - const maxAttempts = 600; // ~30s at 50ms each - while (!ctx.isIdle() && attempts < maxAttempts) { - await new Promise((r) => setTimeout(r, 50)); - attempts++; - } - agentBusy = false; - drainPendingJobs(); + // Defer draining to the next macrotask so finishRun() completes first. + setTimeout(() => { + agentBusy = false; + drainPendingJobs(); + }, 0); }); pi.on("session_shutdown", async (_event, ctx) => { |
