Summary
new Worker(file) is only compiled into a native worker entry when the compile-time worker-path helper (crates/perry-hir/src/dynamic_import/worker_paths.rs) can evaluate file. It accepts string literals, new URL(x, import.meta.url), path joins, ternaries and calls to synchronous single-return helpers, and rejects everything else: async and generator helpers are not static path helpers, helper body must contain only a single return (no effects, mutation or multiple returns), and Expr::Await falls into unsupported expression (effects, mutation and opaque calls are not evaluated). The runtime then throws worker_threads Worker filename was not statically resolvable at compile time; constructing this Worker is unsupported in the compiled binary.
OpenCode v1.18.30 (tracker #10107) picks its TUI worker like this (packages/opencode/src/cli/cmd/tui.ts), with OPENCODE_WORKER_PATH supplied as a --define string by scripts/build_opencode.ts:
declare const OPENCODE_WORKER_PATH: string
async function target() {
if (typeof OPENCODE_WORKER_PATH !== "undefined") return OPENCODE_WORKER_PATH
const dist = new URL("./cli/tui/worker.js", import.meta.url)
if (await Filesystem.exists(fileURLToPath(dist))) return dist
return new URL("../tui/worker.ts", import.meta.url)
}
…
const file = await target()
const worker = new Worker(file, { env: … })
The full compile warns worker_threads Worker in module cli/cmd/tui.ts: Worker path helper: unsupported expression (effects, mutation and opaque calls are not evaluated) — this Worker will throw if constructed at runtime, and opencode (the TUI, the default command) dies right after startup with the runtime error above. Every other rung is fine.
Ask
Extend the helper so this shape resolves, without evaluating effects:
await <expr> → resolve <expr> (an await does not change which paths are possible).
- A helper (sync or async) whose body is a chain of
if (<cond>) return <e_i>; / const x = <pure expr>; statements ending in return <e_n>; resolves to the union of all return expressions' paths; conditions are not evaluated (an opaque call like await Filesystem.exists(...) inside a condition is fine because the value is still one of the returns). Union semantics are sound for this purpose: extra candidates only cost an extra compiled worker entry.
- With a
--define, typeof OPENCODE_WORKER_PATH !== "undefined" is a constant; folding it is optional once (2) uses union semantics.
- A candidate that resolves to a file that does not exist at compile time (here
./cli/tui/worker.js, the bun-build layout) should be skipped with a warning rather than failing the whole resolution; duplicates (OPENCODE_WORKER_PATH and ../tui/worker.ts name the same file) collapse to one entry.
- Keep the current limits (work budget, string limit, recursion guard) and the current rejections for real effects (assignments, calls whose result is returned from an opaque callee).
Expected: opencode (TUI) constructs its worker natively; perry compile reports the worker entry in its summary instead of the warning.
Summary
new Worker(file)is only compiled into a native worker entry when the compile-time worker-path helper (crates/perry-hir/src/dynamic_import/worker_paths.rs) can evaluatefile. It accepts string literals,new URL(x, import.meta.url), path joins, ternaries and calls to synchronous single-returnhelpers, and rejects everything else:async and generator helpers are not static path helpers,helper body must contain only a single return (no effects, mutation or multiple returns), andExpr::Awaitfalls intounsupported expression (effects, mutation and opaque calls are not evaluated). The runtime then throwsworker_threads Worker filename was not statically resolvable at compile time; constructing this Worker is unsupported in the compiled binary.OpenCode v1.18.30 (tracker #10107) picks its TUI worker like this (
packages/opencode/src/cli/cmd/tui.ts), withOPENCODE_WORKER_PATHsupplied as a--definestring byscripts/build_opencode.ts:The full compile warns
worker_threads Worker in module cli/cmd/tui.ts: Worker path helper: unsupported expression (effects, mutation and opaque calls are not evaluated) — this Worker will throw if constructed at runtime, andopencode(the TUI, the default command) dies right after startup with the runtime error above. Every other rung is fine.Ask
Extend the helper so this shape resolves, without evaluating effects:
await <expr>→ resolve<expr>(an await does not change which paths are possible).if (<cond>) return <e_i>;/const x = <pure expr>;statements ending inreturn <e_n>;resolves to the union of allreturnexpressions' paths; conditions are not evaluated (an opaque call likeawait Filesystem.exists(...)inside a condition is fine because the value is still one of the returns). Union semantics are sound for this purpose: extra candidates only cost an extra compiled worker entry.--define,typeof OPENCODE_WORKER_PATH !== "undefined"is a constant; folding it is optional once (2) uses union semantics../cli/tui/worker.js, the bun-build layout) should be skipped with a warning rather than failing the whole resolution; duplicates (OPENCODE_WORKER_PATHand../tui/worker.tsname the same file) collapse to one entry.Expected:
opencode(TUI) constructs its worker natively;perry compilereports the worker entry in its summary instead of the warning.