From eef62cc4f28f6152641e04d57d6a796ac672e3c8 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 10 May 2026 15:54:29 +0900 Subject: [PATCH] fix(delegate-task): clarify sync inactivity timeout --- .../delegate-task/sync-poll-timeout.test.ts | 8 ++++---- .../delegate-task/sync-session-poller.test.ts | 4 ++-- src/tools/delegate-task/sync-session-poller.ts | 6 ++++-- src/tools/delegate-task/timing.test.ts | 4 ++-- .../delegate-task/tool-description.test.ts | 18 ++++++++++++++++++ src/tools/delegate-task/tool-description.ts | 1 + 6 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 src/tools/delegate-task/tool-description.test.ts diff --git a/src/tools/delegate-task/sync-poll-timeout.test.ts b/src/tools/delegate-task/sync-poll-timeout.test.ts index a56194441..13784dfa6 100644 --- a/src/tools/delegate-task/sync-poll-timeout.test.ts +++ b/src/tools/delegate-task/sync-poll-timeout.test.ts @@ -75,7 +75,7 @@ describe("syncPollTimeoutMs threading", () => { taskId: undefined, }, 120_000) - expect(result).toBe("Poll timeout reached after 120000ms for session ses_custom") + expect(result).toBe("Poll inactivity timeout reached after 120000ms without active OpenCode status for session ses_custom") expect(abortCount).toBe(1) }) }) @@ -141,7 +141,7 @@ describe("syncPollTimeoutMs threading", () => { taskId: undefined, }) - expect(result).toBe(`Poll timeout reached after ${MAX_POLL_TIME_MS}ms for session ses_default`) + expect(result).toBe(`Poll inactivity timeout reached after ${MAX_POLL_TIME_MS}ms without active OpenCode status for session ses_default`) }) }) @@ -159,7 +159,7 @@ describe("syncPollTimeoutMs threading", () => { taskId: undefined, }) - expect(result).toBe("Poll timeout reached after 120000ms for session ses_legacy") + expect(result).toBe("Poll inactivity timeout reached after 120000ms without active OpenCode status for session ses_legacy") }) }) }) @@ -177,7 +177,7 @@ describe("syncPollTimeoutMs threading", () => { taskId: undefined, }, 10) - expect(result).toBe("Poll timeout reached after 50ms for session ses_guard") + expect(result).toBe("Poll inactivity timeout reached after 50ms without active OpenCode status for session ses_guard") }) }) }) diff --git a/src/tools/delegate-task/sync-session-poller.test.ts b/src/tools/delegate-task/sync-session-poller.test.ts index 004fa0cb8..b2155a085 100644 --- a/src/tools/delegate-task/sync-session-poller.test.ts +++ b/src/tools/delegate-task/sync-session-poller.test.ts @@ -100,7 +100,7 @@ describe("pollSyncSession", () => { }, 50) // then: times out (ignores stale error) - expect(result).toContain("Poll timeout reached") + expect(result).toContain("Poll inactivity timeout reached") }) test("detects completion when assistant message has terminal finish reason", async () => { @@ -459,7 +459,7 @@ describe("pollSyncSession", () => { }, 0) // then: returns timeout error - expect(result).toBe("Poll timeout reached after 50ms for session ses_timeout") + expect(result).toBe("Poll inactivity timeout reached after 50ms without active OpenCode status for session ses_timeout") expect(abortCount).toBe(1) }) }) diff --git a/src/tools/delegate-task/sync-session-poller.ts b/src/tools/delegate-task/sync-session-poller.ts index 9d69e4157..1c093b3a9 100644 --- a/src/tools/delegate-task/sync-session-poller.ts +++ b/src/tools/delegate-task/sync-session-poller.ts @@ -214,9 +214,11 @@ export async function pollSyncSession( } if (timedOut) { - log("[task] Poll timeout reached", { sessionID: input.sessionID, pollCount }) + log("[task] Poll inactivity timeout reached", { sessionID: input.sessionID, pollCount }) abortSyncSession(client, input.sessionID, "poll_timeout") } - return timedOut ? `Poll timeout reached after ${maxPollTimeMs}ms for session ${input.sessionID}` : null + return timedOut + ? `Poll inactivity timeout reached after ${maxPollTimeMs}ms without active OpenCode status for session ${input.sessionID}` + : null } diff --git a/src/tools/delegate-task/timing.test.ts b/src/tools/delegate-task/timing.test.ts index a4ca252ba..64cc2280e 100644 --- a/src/tools/delegate-task/timing.test.ts +++ b/src/tools/delegate-task/timing.test.ts @@ -3,7 +3,7 @@ const { describe, expect, test } = require("bun:test") import { __resetTimingConfig, __setTimingConfig, getDefaultSyncPollTimeoutMs, getTimingConfig } from "./timing" describe("timing sync poll timeout defaults", () => { - test("default sync timeout is 30 minutes", () => { + test("default sync inactivity timeout is 30 minutes", () => { // #given __resetTimingConfig() @@ -14,7 +14,7 @@ describe("timing sync poll timeout defaults", () => { expect(timeout).toBe(30 * 60 * 1000) }) - test("default sync timeout accessor follows MAX_POLL_TIME_MS config", () => { + test("default sync inactivity timeout accessor follows MAX_POLL_TIME_MS config", () => { // #given __resetTimingConfig() diff --git a/src/tools/delegate-task/tool-description.test.ts b/src/tools/delegate-task/tool-description.test.ts new file mode 100644 index 000000000..9b2c15088 --- /dev/null +++ b/src/tools/delegate-task/tool-description.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, test } from "bun:test" + +import { createDelegateTaskPresentation } from "./tool-description" + +describe("createDelegateTaskPresentation", () => { + test("#given sync task usage #when description is rendered #then timeout is described as inactivity based", () => { + //#given + const presentation = createDelegateTaskPresentation({}) + + //#when + const description = presentation.description + + //#then + expect(description).toContain("30-minute inactivity window") + expect(description).toContain("busy/retry/running") + expect(description).toContain("not a total wall-clock limit") + }) +}) diff --git a/src/tools/delegate-task/tool-description.ts b/src/tools/delegate-task/tool-description.ts index 0b2717a82..5ae10ff1b 100644 --- a/src/tools/delegate-task/tool-description.ts +++ b/src/tools/delegate-task/tool-description.ts @@ -67,6 +67,7 @@ export function createDelegateTaskPresentation(options: DelegateTaskToolOptions) ${categoryList} - subagent_type: Use specific agent directly (explore, librarian, oracle, metis, momus) - run_in_background: REQUIRED. true=async (returns task_id), false=sync (waits). Use background=true ONLY for parallel exploration with 5+ independent queries. + Sync waits use a 30-minute inactivity window: OpenCode busy/retry/running status resets the window, so this is not a total wall-clock limit. - task_id: Existing task to continue (from previous task output). Continues the same subagent session with FULL CONTEXT PRESERVED. - command: The command that triggered this task (optional, for slash command tracking).