From c740ed8acab252c3aea3f7ffb89d1be8a9620c93 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 12 May 2026 18:14:17 +0900 Subject: [PATCH] fix(delegate-task): route sync prompts by directory --- .../delegate-task/sync-prompt-route.test.ts | 92 +++++++++++++++++++ src/tools/delegate-task/sync-prompt-sender.ts | 7 +- src/tools/delegate-task/sync-task.ts | 1 + 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/tools/delegate-task/sync-prompt-route.test.ts diff --git a/src/tools/delegate-task/sync-prompt-route.test.ts b/src/tools/delegate-task/sync-prompt-route.test.ts new file mode 100644 index 000000000..2ada42772 --- /dev/null +++ b/src/tools/delegate-task/sync-prompt-route.test.ts @@ -0,0 +1,92 @@ +import { describe, expect, mock, test } from "bun:test" + +import { unsafeTestValue } from "../../../test-support/unsafe-test-value" +import type { OpencodeClient } from "./types" +import { sendSyncPrompt } from "./sync-prompt-sender" +import { + promptSyncWithModelSuggestionRetry, + promptWithModelSuggestionRetry, +} from "../../shared/model-suggestion-retry" + +type PromptRetryClient = Parameters[0] +type PromptRetryArgs = Parameters[1] +type PromptSyncRetryClient = Parameters[0] +type PromptSyncRetryArgs = Parameters[1] + +describe("sendSyncPrompt session routing", () => { + test("#given a sync child session directory #when sending the prompt #then promptAsync uses that OpenCode directory route", async () => { + // given + const promptCalls: PromptRetryArgs[] = [] + const promptWithRetry = mock(async (_client: PromptRetryClient, input: PromptRetryArgs) => { + promptCalls.push(input) + }) + + // when + await sendSyncPrompt( + unsafeTestValue({ session: {} }), + { + sessionID: "ses_child", + agentToUse: "sisyphus-junior", + args: { + description: "test task", + prompt: "test prompt", + run_in_background: false, + load_skills: [], + }, + systemContent: undefined, + categoryModel: undefined, + directory: "/parent/project", + toastManager: null, + taskId: undefined, + }, + { + promptWithModelSuggestionRetry: promptWithRetry, + promptSyncWithModelSuggestionRetry: mock(async () => {}), + }, + ) + + // then + expect(promptCalls).toHaveLength(1) + expect(promptCalls[0]?.query).toEqual({ directory: "/parent/project" }) + }) + + test("#given oracle falls back to promptSync #when async prompt returns unexpected EOF #then the sync retry keeps the same directory route", async () => { + // given + const promptSyncCalls: PromptSyncRetryArgs[] = [] + const promptWithRetry = mock(async () => { + throw new Error("JSON Parse error: Unexpected EOF") + }) + const promptSyncWithRetry = mock(async (_client: PromptSyncRetryClient, input: PromptSyncRetryArgs) => { + promptSyncCalls.push(input) + }) + + // when + const result = await sendSyncPrompt( + unsafeTestValue({ session: {} }), + { + sessionID: "ses_child", + agentToUse: "oracle", + args: { + description: "test task", + prompt: "test prompt", + run_in_background: false, + load_skills: [], + }, + systemContent: undefined, + categoryModel: undefined, + directory: "/parent/project", + toastManager: null, + taskId: undefined, + }, + { + promptWithModelSuggestionRetry: promptWithRetry, + promptSyncWithModelSuggestionRetry: promptSyncWithRetry, + }, + ) + + // then + expect(result).toBeNull() + expect(promptSyncCalls).toHaveLength(1) + expect(promptSyncCalls[0]?.query).toEqual({ directory: "/parent/project" }) + }) +}) diff --git a/src/tools/delegate-task/sync-prompt-sender.ts b/src/tools/delegate-task/sync-prompt-sender.ts index 1f8ad22a5..bfdac5fba 100644 --- a/src/tools/delegate-task/sync-prompt-sender.ts +++ b/src/tools/delegate-task/sync-prompt-sender.ts @@ -6,6 +6,7 @@ import { promptSyncWithModelSuggestionRetry, promptWithModelSuggestionRetry, } from "../../shared/model-suggestion-retry" +import { routePromptRetry, routePromptSyncRetry } from "../../shared/session-route" import { formatDetailedError } from "./error-formatting" import { getAgentToolRestrictions } from "../../shared/agent-tool-restrictions" import { stripInvisibleAgentCharacters } from "../../shared/agent-display-names" @@ -59,6 +60,7 @@ export async function sendSyncPrompt( args: DelegateTaskArgs systemContent: string | undefined categoryModel: DelegatedModelConfig | undefined + directory: string toastManager: { removeTask: (id: string) => void } | null | undefined taskId: string | undefined sisyphusAgentConfig?: SisyphusAgentConfig @@ -99,11 +101,12 @@ export async function sendSyncPrompt( } try { - await deps.promptWithModelSuggestionRetry(client, promptArgs) + const routedPromptArgs = routePromptRetry(promptArgs, input.directory) + await deps.promptWithModelSuggestionRetry(client, routedPromptArgs) } catch (promptError) { if (isOracleAgent(input.agentToUse) && isUnexpectedEofError(promptError)) { try { - await deps.promptSyncWithModelSuggestionRetry(client, promptArgs) + await deps.promptSyncWithModelSuggestionRetry(client, routePromptSyncRetry(promptArgs, input.directory)) return null } catch (oracleRetryError) { promptError = oracleRetryError diff --git a/src/tools/delegate-task/sync-task.ts b/src/tools/delegate-task/sync-task.ts index dcc4b31e8..8978d933a 100644 --- a/src/tools/delegate-task/sync-task.ts +++ b/src/tools/delegate-task/sync-task.ts @@ -177,6 +177,7 @@ export async function executeSyncTask( agentToUse, args, systemContent, + directory: createSessionResult.parentDirectory, toastManager, taskId, sisyphusAgentConfig: executorCtx.sisyphusAgentConfig,