diff --git a/backends/opencode/index.ts b/backends/opencode/index.ts index 184278c..717358c 100644 --- a/backends/opencode/index.ts +++ b/backends/opencode/index.ts @@ -79,10 +79,10 @@ const MAX_HOOK_ATTEMPTS = 3 // its cached time — a synchronous retry would re-read the same stale value and // fail again (which is exactly why the *next* hook in a burst always succeeds). // A genuine timeout takes ~HOOK_TIMEOUT_MS, far above SPURIOUS_TIMEOUT_MS, so it -// is never retried. `label` is used only for the diagnostic log. Exported so the -// retry behaviour can be exercised by the test harness (it's a Windows libuv -// quirk that can't otherwise be reproduced on CI). -export async function runWithSpuriousRetry( +// is never retried. `label` is used only for the diagnostic log. Exercised by +// retry_test.ts (it's a Windows libuv quirk that can't otherwise be reproduced +// on CI). +async function runWithSpuriousRetry( run: () => void, label = "hook-entry", ): Promise { @@ -116,6 +116,12 @@ export async function runWithSpuriousRetry( } } +// Exposed as a property on a plain object — NOT as a function-valued export — +// so retry_test.ts can still exercise it. opencode's plugin loader treats any +// export whose value is a function as a plugin factory and crashes the server +// at startup (opencode-ai 1.18.30, "plugin config hook failed (N.config)"). +export const retry = { runWithSpuriousRetry } + async function runHook(event: "pre" | "post", payload: object): Promise { const shim = resolveHookEntry() if (!shim) { diff --git a/tests/backends/opencode/retry_test.ts b/tests/backends/opencode/retry_test.ts index 7bbbf3f..7dac264 100644 --- a/tests/backends/opencode/retry_test.ts +++ b/tests/backends/opencode/retry_test.ts @@ -36,7 +36,9 @@ async function main(): Promise { // (the bare `D:\…` path is rejected by the ESM loader as an unsupported scheme). const indexPath = resolve(__dirname, "../../../backends/opencode/index.ts") const mod = await import(pathToFileURL(indexPath).href) - const runWithSpuriousRetry = mod.runWithSpuriousRetry as RunWithSpuriousRetry + // Exposed as `retry.runWithSpuriousRetry` (a property on a const object, not a + // bare function export — see backends/opencode/index.ts). + const runWithSpuriousRetry = mod.retry.runWithSpuriousRetry as RunWithSpuriousRetry // Success on the first attempt → run called exactly once. {