diff --git a/src/group-queue.ts b/src/group-queue.ts index d1f980c..f72f6a0 100644 --- a/src/group-queue.ts +++ b/src/group-queue.ts @@ -17,6 +17,7 @@ const BASE_RETRY_MS = 5000; interface GroupState { active: boolean; idleWaiting: boolean; + isTaskContainer: boolean; pendingMessages: boolean; pendingTasks: QueuedTask[]; process: ChildProcess | null; @@ -39,6 +40,7 @@ export class GroupQueue { state = { active: false, idleWaiting: false, + isTaskContainer: false, pendingMessages: false, pendingTasks: [], process: null, @@ -142,7 +144,8 @@ export class GroupQueue { */ sendMessage(groupJid: string, text: string): boolean { const state = this.getGroup(groupJid); - if (!state.active || !state.groupFolder) return false; + if (!state.active || !state.groupFolder || state.isTaskContainer) return false; + state.idleWaiting = false; // Agent is about to receive work, no longer idle const inputDir = path.join(DATA_DIR, 'ipc', state.groupFolder, 'input'); try { @@ -181,6 +184,7 @@ export class GroupQueue { const state = this.getGroup(groupJid); state.active = true; state.idleWaiting = false; + state.isTaskContainer = false; state.pendingMessages = false; this.activeCount++; @@ -215,6 +219,7 @@ export class GroupQueue { const state = this.getGroup(groupJid); state.active = true; state.idleWaiting = false; + state.isTaskContainer = true; this.activeCount++; logger.debug( @@ -228,6 +233,7 @@ export class GroupQueue { logger.error({ groupJid, taskId: task.id, err }, 'Error running task'); } finally { state.active = false; + state.isTaskContainer = false; state.process = null; state.containerName = null; state.groupFolder = null; diff --git a/src/index.ts b/src/index.ts index d3815db..061740f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -187,7 +187,7 @@ async function processGroupMessages(chatJid: string): Promise { resetIdleTimer(); } - if (!result.result && result.status === 'success') { + if (result.status === 'success') { queue.notifyIdle(chatJid); } diff --git a/src/task-scheduler.ts b/src/task-scheduler.ts index 9910bdd..0579a8c 100644 --- a/src/task-scheduler.ts +++ b/src/task-scheduler.ts @@ -89,16 +89,18 @@ async function runTask( const sessionId = task.context_mode === 'group' ? sessions[task.group_folder] : undefined; - // Idle timer: writes _close sentinel after IDLE_TIMEOUT of no output, - // so the container exits instead of hanging at waitForIpcMessage forever. - let idleTimer: ReturnType | null = null; + // After the task produces a result, close the container promptly. + // Tasks are single-turn — no need to wait IDLE_TIMEOUT (30 min) for the + // query loop to time out. A short delay handles any final MCP calls. + const TASK_CLOSE_DELAY_MS = 10000; + let closeTimer: ReturnType | null = null; - const resetIdleTimer = () => { - if (idleTimer) clearTimeout(idleTimer); - idleTimer = setTimeout(() => { - logger.debug({ taskId: task.id }, 'Scheduled task idle timeout, closing container stdin'); + const scheduleClose = () => { + if (closeTimer) return; // already scheduled + closeTimer = setTimeout(() => { + logger.debug({ taskId: task.id }, 'Closing task container after result'); deps.queue.closeStdin(task.chat_jid); - }, IDLE_TIMEOUT); + }, TASK_CLOSE_DELAY_MS); }; try { @@ -118,10 +120,9 @@ async function runTask( result = streamedOutput.result; // Forward result to user (sendMessage handles formatting) await deps.sendMessage(task.chat_jid, streamedOutput.result); - // Only reset idle timer on actual results, not session-update markers - resetIdleTimer(); + scheduleClose(); } - if (!streamedOutput.result && streamedOutput.status === 'success') { + if (streamedOutput.status === 'success') { deps.queue.notifyIdle(task.chat_jid); } if (streamedOutput.status === 'error') { @@ -130,7 +131,7 @@ async function runTask( }, ); - if (idleTimer) clearTimeout(idleTimer); + if (closeTimer) clearTimeout(closeTimer); if (output.status === 'error') { error = output.error || 'Unknown error'; @@ -144,7 +145,7 @@ async function runTask( 'Task completed', ); } catch (err) { - if (idleTimer) clearTimeout(idleTimer); + if (closeTimer) clearTimeout(closeTimer); error = err instanceof Error ? err.message : String(err); logger.error({ taskId: task.id, error }, 'Task failed'); }