Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions backends/opencode/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
Expand Down Expand Up @@ -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<void> {
const shim = resolveHookEntry()
if (!shim) {
Expand Down
4 changes: 3 additions & 1 deletion tests/backends/opencode/retry_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ async function main(): Promise<void> {
// (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.
{
Expand Down