From 5d349c5b60ca7ed6838c8bf2bcd5f0264c29d4f8 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Mon, 23 Mar 2026 09:26:46 +0000 Subject: [PATCH] fix: always wait for agent idle before sending input to existing task When an existing task has status 'active', the action previously skipped waitForTaskActive and immediately called sendTaskInput. But the task can be active while the agent is still processing a previous prompt (current_state.state === 'working'). This causes 409/502 errors from the server because the agent isn't ready to accept input. Remove the status guard so waitForTaskActive always runs for existing tasks. The function already handles the active+idle case correctly by returning immediately. --- dist/index.js | 32 ++++++++++++++++++++++++-------- src/action.test.ts | 1 + src/action.ts | 25 +++++++++++++------------ 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/dist/index.js b/dist/index.js index f32a1fb..3d81bf5 100644 --- a/dist/index.js +++ b/dist/index.js @@ -3,25 +3,43 @@ var __getProtoOf = Object.getPrototypeOf; var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; +function __accessProp(key) { + return this[key]; +} +var __toESMCache_node; +var __toESMCache_esm; var __toESM = (mod, isNodeMode, target) => { + var canCache = mod != null && typeof mod === "object"; + if (canCache) { + var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap; + var cached = cache.get(mod); + if (cached) + return cached; + } target = mod != null ? __create(__getProtoOf(mod)) : {}; const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target; for (let key of __getOwnPropNames(mod)) if (!__hasOwnProp.call(to, key)) __defProp(to, key, { - get: () => mod[key], + get: __accessProp.bind(mod, key), enumerable: true }); + if (canCache) + cache.set(mod, to); return to; }; var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports); +var __returnValue = (v) => v; +function __exportSetter(name, newValue) { + this[name] = __returnValue.bind(null, newValue); +} var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true, configurable: true, - set: (newValue) => all[name] = () => newValue + set: __exportSetter.bind(all, name) }); }; @@ -3447,7 +3465,7 @@ var require_constants2 = __commonJS((exports2, module2) => { } })(); var channel; - var structuredClone = globalThis.structuredClone ?? function structuredClone(value, options = undefined) { + var structuredClone = globalThis.structuredClone ?? function structuredClone2(value, options = undefined) { if (arguments.length === 0) { throw new TypeError("missing argument"); } @@ -16372,7 +16390,7 @@ var require_undici = __commonJS((exports2, module2) => { module2.exports.getGlobalDispatcher = getGlobalDispatcher; if (util.nodeMajor > 16 || util.nodeMajor === 16 && util.nodeMinor >= 8) { let fetchImpl = null; - module2.exports.fetch = async function fetch(resource) { + module2.exports.fetch = async function fetch2(resource) { if (!fetchImpl) { fetchImpl = require_fetch().fetch; } @@ -26966,10 +26984,8 @@ class CoderTaskAction { const existingTask = await this.coder.getTask(coderUsername, taskName); if (existingTask) { core.info(`Coder Task: already exists: ${existingTask.name} (id: ${existingTask.id} status: ${existingTask.status})`); - if (existingTask.status !== "active") { - core.info(`Coder Task: waiting for task ${existingTask.name} to become active...`); - await this.coder.waitForTaskActive(coderUsername, existingTask.id, core.debug, 1200000); - } + core.info(`Coder Task: waiting for task ${existingTask.name} to become active and idle...`); + await this.coder.waitForTaskActive(coderUsername, existingTask.id, core.debug, 1200000); core.info("Coder Task: Sending prompt to existing task..."); await this.coder.sendTaskInput(coderUsername, existingTask.id, this.inputs.coderTaskPrompt); core.info("Coder Task: Prompt sent successfully"); diff --git a/src/action.test.ts b/src/action.test.ts index 27b1960..bec3c7c 100644 --- a/src/action.test.ts +++ b/src/action.test.ts @@ -867,6 +867,7 @@ describe("CoderTaskAction", () => { ); coderClient.mockGetTemplateVersionPresets.mockResolvedValue([]); coderClient.mockSendTaskInput.mockResolvedValue(undefined); + coderClient.mockWaitForTaskActive.mockResolvedValue(undefined); octokit.rest.issues.listComments.mockResolvedValue({ data: [], } as ReturnType); diff --git a/src/action.ts b/src/action.ts index 248d065..a953d7f 100644 --- a/src/action.ts +++ b/src/action.ts @@ -173,18 +173,19 @@ export class CoderTaskAction { `Coder Task: already exists: ${existingTask.name} (id: ${existingTask.id} status: ${existingTask.status})`, ); - // Wait for task to become active if it's not already - if (existingTask.status !== "active") { - core.info( - `Coder Task: waiting for task ${existingTask.name} to become active...`, - ); - await this.coder.waitForTaskActive( - coderUsername, - existingTask.id, - core.debug, - 1_200_000, - ); - } + // Wait for task to become active and idle before sending + // input. The agent may be in "working" state even when the + // task status is "active", and sending input in that state + // causes 409/502 errors. + core.info( + `Coder Task: waiting for task ${existingTask.name} to become active and idle...`, + ); + await this.coder.waitForTaskActive( + coderUsername, + existingTask.id, + core.debug, + 1_200_000, + ); core.info("Coder Task: Sending prompt to existing task..."); // Send prompt to existing task using the task ID (UUID)