From e7eed686f76f708ac0054e38c8f2037ecabff06a Mon Sep 17 00:00:00 2001 From: Janni Turunen Date: Wed, 8 Apr 2026 01:00:49 +0300 Subject: [PATCH 1/2] fix(taskctl): add timeout enforcement and logging for Composer agent (#394) --- packages/opencode/src/tasks/composer.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/tasks/composer.ts b/packages/opencode/src/tasks/composer.ts index 7560df12f9f4..3ffea9c5a7da 100644 --- a/packages/opencode/src/tasks/composer.ts +++ b/packages/opencode/src/tasks/composer.ts @@ -5,6 +5,9 @@ import { MessageV2 } from "../session/message-v2" import { Store } from "./store" import { Validation } from "./validation" import { generateUniqueSlug, slugify } from "./tool" +import { Log } from "../util/log" + +const log = Log.create({ service: "taskctl.composer" }) const ComposerTasksSchema = z.object({ title: z.string().min(1).max(200), @@ -37,6 +40,8 @@ async function defaultSpawnComposerFn( permission: [], }) + const startTime = Date.now() + // prompt() blocks until the agent finishes // Race with timeout — if timeout fires, cancel the session cleanly let timedOut = false @@ -48,12 +53,21 @@ async function defaultSpawnComposerFn( }, timeoutMs), ) + log.info("composer started", { sessionID: session.id, timeoutMs }) + await Promise.race([ SessionPrompt.prompt({ sessionID: session.id, agent: "composer", parts: [{ type: "text", text: prompt }] }), timeoutPromise, ]) - if (timedOut) return undefined + const elapsedMs = Date.now() - startTime + + if (timedOut) { + log.warn("composer timed out", { sessionID: session.id, elapsedMs }) + throw new Error(`Composer timed out after ${timeoutMs / 1000}s. Issue description may be too complex. Consider simplifying.`) + } + + log.info("composer completed", { sessionID: session.id, timedOut }) // Agent finished — read final assistant message directly from DB // MessageV2.stream() is descending order — .find() gets the newest assistant message From b8dfd4d0acd0227d7f50131774099bc707221ca7 Mon Sep 17 00:00:00 2001 From: Janni Turunen Date: Wed, 8 Apr 2026 09:58:53 +0300 Subject: [PATCH 2/2] refactor(taskctl): log elapsedMs on composer success (#394) Address code review feedback - success path should log elapsed time for diagnostic value instead of timedOut (always false on success). --- packages/opencode/src/tasks/composer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/tasks/composer.ts b/packages/opencode/src/tasks/composer.ts index 3ffea9c5a7da..7cd7923cf809 100644 --- a/packages/opencode/src/tasks/composer.ts +++ b/packages/opencode/src/tasks/composer.ts @@ -67,7 +67,7 @@ async function defaultSpawnComposerFn( throw new Error(`Composer timed out after ${timeoutMs / 1000}s. Issue description may be too complex. Consider simplifying.`) } - log.info("composer completed", { sessionID: session.id, timedOut }) + log.info("composer completed", { sessionID: session.id, elapsedMs }) // Agent finished — read final assistant message directly from DB // MessageV2.stream() is descending order — .find() gets the newest assistant message