Repository navigation
tracking: OpenCode v1.18.30 — complete native build from real TypeScript (no stripping, no bundle), startup faster than bun #10107
Description
Activity
- addedenhancementNew capability or improvementNew capability or improvementparityCompatibility gap with Node.js, ECMAScript, or the supported ecosystemCompatibility gap with Node.js, ECMAScript, or the supported ecosystem
on Sep 12, 2026 2026-09-13 first full native build + ladder (perrymaster, linux-x64, perry origin/main 5603d63 + PR #10154's codegen patch, interim alias for #10153,
--define OPENCODE_MODELS_DEV=undefinedfor #10151)Compile: 7,813 modules, 0 JavaScript fallback, COMPILE_RC=0, 1.27 GB binary (~80 min at 6 module jobs × 2 LLVM workers; perry's default 2×2 is far too conservative for this graph).
Oracle = official
opencode-linux-x64v1.18.30 binary, identical cleanHOME/XDG dirs,/usr/bin/time.rung output oracle wall / user / RSS perry wall / user / RSS --versionIDENTICAL 0.45 s / 0.41 s / 193 MB 2.47 s / 2.04 s / 610 MB --helpIDENTICAL 0.32 s / 0.39 s / 205 MB 31.6 s / 31.2 s / 2.39 GB run --helpIDENTICAL 0.32 s 20.1 s / 19.8 s / 1.76 GB models --help,agent --help,mcp --help,providers --help,serve --helpIDENTICAL 0.32 s 5.6–8.8 s / 0.75–0.95 GB completionIDENTICAL 0.32 s 2.1 s / 618 MB modelsoracle lists 7 models perry: Error: Unexpected error / Cannot read properties of undefined (reading 'Service'), rc=1, no log lines even with--print-logs --log-level DEBUGrun --model anthropic/claude-sonnet-4-5 …(bogus key)oracle: > build · claude-sonnet-4-5+Error: API key is invalid.perry: same 'Service'TypeError before any log lineSo every pure-CLI path is byte-identical; the first runtime divergence is one
X.Serviceread on anundefinednamespace in the shared instance bootstrap (effect-cmd.ts→import("@/effect/app-runtime")), which blocks every real command. A probe importing app-runtime's 54 namespace imports is compiling to name the culprit; a debug-symbols build follows for the startup profile.--helpunderPERRY_GC_DIAG=1:arena_live=1,576,988,816, 89 incremental cycles / 93,750 steps,[gc-time] wall_us=30,478,027 step_us=14,691,589 share_permille=527— half of the 30 s is GC over a 1.6 GB live heap that bun never builds (bun peaks at 205 MB RSS). Startup work for #10106 starts from there once symbols exist.2026-09-13 second full build: all three new fixes verified on the real graph
perry = origin/main 5603d63 + PR #10154 (#10152) + PR #10156 (#10153) + PR #10162 (#10160), pinned copy; no alias, no stubs: 7,897 modules native, 0 JS,
COMPILE_RC=0, 1.30 GB. (#10161 for #10151 landed meanwhile via train #10172; this build still used--define OPENCODE_MODELS_DEV=undefinedbecause it started before the landing.)Ladder against the official linux-x64 v1.18.30 binary:
--version,--help,run/models/agent/mcp/providers/serve --help,completionall byte-identical (perry CPU 2.2 s … 27 s vs 0.4 s, unchanged picture, see #10106).Bootstrap progress: the
'Service'crash (#10160) is gone;run,modelsandservenow fail later, still before the firstcreating instancelog line, withCannot read properties of undefined (reading 'size')plus three node-style warnings bun never prints:
(node:PID) Warning: Accessing non-existent property 'ANY' of module exports inside circular dependency ×2 (node:PID) Warning: Accessing non-existent property 'getVercelDataDir' of module exports inside circular dependency(
@vercel/oidcauth-config.js↔token-util.jsis a real CJS cycle; theANYreader is being traced.) A debug-symbols build (PERRY_DEBUG_SYMBOLS=1) is compiling so the'size'site can be taken from a gdb backtrace instead of another 70-minute probe compile.2026-09-13 ladder 4 (compiler = main 5603d63 + #10154 + #10156 + #10162 + PR #10199): the full v1.18.30 graph still compiles natively (7,897 modules, 0 JS fallback, no aliases/stubs); all eight CLI rungs (
--version,--help, every<cmd> --help,completion) are byte-identical to the official bun binary.Next wall (after #10160
'Service'and #10197'size'):models/run/servedie before the first log line withError: Unexpected error / of is not a function. Reduced to #10210: inside a static getter of a class extending a call expression (effect v4Context.Service<…>()(id)),this.of(x)is not found on the call path although the property read finds it, andthisis the holder object rather than the receiver. OpenCode hits it insrc/effect/config-service.ts(static get layer()→tag.of(config)) whileAppRuntimebuildsRuntimeFlags.Runtime wall root cause (2026-09-13 evening): #10222. A symbol-keeping relink + gdb backtrace put the
of is not a functionthrow inpackages/core/src/fs-util.ts: perry lowers classes declared inside a TypeScriptnamespaceincompletely (class not published on the namespace object, static field initializers never run, call-expression heritage never registered).namespace FSUtil { export class Service extends Context.Service<…>()("@opencode/FileSystem") {} }is built byAppRuntimefor every command, somodels,run,serveand the TUI all die there. Three OpenCode files, six classes (core/src/fs-util.ts,core/src/util/effect-flock.ts,core/src/ripgrep/binary.ts). An 8-line repro is on #10222; a codex lane is on the fix (perry-hirlower/module_decl/namespace.rs).#10210 (PR #10213) was real but not this wall; with it and #10199 folded into the pinned compiler every CLI rung stays byte-identical to bun.
Update: with main 0.5.1561 + #10156 + #10199 + #10213 + #10231 the binary is 837 MB (was 1237), all eight CLI rungs stay byte-identical, and the
ofwall is gone. The bootstrap then hit a second namespace gap (exported namespace functions used as values,next is not a function) — fixed as a follow-up commit on #10231. The graph is recompiling with that compiler now; the TUI's next wall is the worker path helper (#10236, lane running); module pruning for startup (#10180) has a lane running.- added a commit that references this issue
on Sep 14, 2026 Runtime wall
next is not a functionroot-caused and fixed: PR #10250. drizzle-orm'sapplyEffectWrappercopies effect's prototype (with[Symbol.iterator]) onto query classes viaObject.assign(Class.prototype, …); perry did not see symbol-keyed properties written to a declared class prototype from instances, soyield* db.run(…)in core's Database layer threw for every command. Found via per-layer bisect → Database → a probe inside packages/core (build OK, first query fails, bun passes) → pure repro. Open fix PRs, all ready + mergeable: #10156, #10193, #10239, #10245, #10250. Full graph recompiling now with main 0.5.1564 + those five.- added 2 commits that reference this issue
on Sep 14, 2026 Status update on the terminal-interface blocker chain. Five compiler defects found and fixed so far, each revealing the next: #10278/PR #10279 (deferred cycle partner), #10280 (Proxy array and request headers), #10178/PR #10282 (esbuild export getters in a CJS cycle), #10281/PR #10283 (the
bunexport condition was ignored), #10286/PR #10288 (import alias claimed by another module's export name).The terminal interface now gets past all five and fails later with
Class extends value is not a constructor", alongside new circular-dependency warnings namingisSuperandfilename` — Babel's CommonJS cycles, reached through the Solid runtime transform. Same family as #10178, different shape.Command line: nine rungs byte-identical to the official binary.
modelsregressed to failing on the current lineage via #10290, which #10283 uncovered rather than caused.TUI wall identified and fixed: #10300.
./tui.shagainst the v8 binary got past the$perryfs/dlopen wall and died with a bareTypeError: on is not a function.PERRY_DISPATCH_DIAG=1named the tower but described the receiver only asobject/pointer; breaking onreport_dispatch_missin gdb and callingjs_console_log_dynamic($xmm0.v2_double[0])printed it:InternalKeyHandler { renderableHandlers: Map(0) {} }with the frame above it:
#3 …_opentui_core_chunk_bun_tkm837n2_js__CliRenderer_constructor #6 …__createCliRenderer #7 perry_closure_opencode_packages_tui_src_app_tsxi.e.
this._keyHandler = new InternalKeyHandler;+this._keyHandler.on("keypress", …)inCliRenderer's constructor.Root cause is general and has nothing to do with opentui: a class with no own constructor whose chain reaches a native instance-stamped base loses that base when constructed from another module. The install rides on
super(); a ctor-less class writes none, so the inlinenewlowering emits it at the call site — but a cross-modulenewcalls the defining module's standalone<class>_constructorsymbol, which never emitted it. For a bareexport class D1 extends EventEmitter {}that symbol is literallyvmovq; vmovq; ret.Affects every base in
NativeInstanceBase:EventEmitter,Map,Set,WeakMap,WeakSet,Event,CustomEvent,AsyncLocalStorage,AsyncResource,DOMException,EventEmitterAsyncResource. In this build it hitsCliRenderer,EditBuffer,TerminalConsoleCache,BaseRenderable,KeyHandler/InternalKeyHandlerandTreeSitterClient— allEventEmittersubclasses declared in one@opentui/corechunk and constructed from another.Fix in
codegen/method.rs(emit the base init in the synthesized standalone ctor) plus a walk relaxation innative_instance_base_in_chain. A 15-cell probe covering all the bases, two-level chains, an ancestor that owns its ctor (no double-init) and seededMap/Setis now byte-identical to bun.Running rung ladder + TUI against a full rebuild now.
Adjacent, filed as part of #10300 but deliberately NOT fixed there:
new Sized(3)on an exportedclass Sized extends Array {}yieldslength 0cross-module (bun: 3).- added a commit that references this issue
on Sep 15, 2026 TUI status after the five fixes landed in a full build (v10): the renderer now constructs and the terminal setup sequences match the oracle — exit status 0 where it was 256, and
createCliRendererno longer throws. Remaining blocker is #10310, which turns out to block therun --model …rung too:- TUI:
tui bootstrap failed { error: '(number).delete is not a function' }—opts.headers.delete("Content-Type")in the generated SDK client, whereopts.headersis the raw handle number. run --model …:Headers constructor: init is not iterable (received 0x4014000000000000)— the same handle, 5, reaching stdlib'sjs_headers_init_from_valuefrom the ext's constructor.
Eight of the nine CLI rungs remain byte-identical to the official binary (
--version,--help,run --help,models --help,agent --help,mcp --help,providers --help,completion); the ninth is the one #10310 broke.- TUI:
modelsis now byte-identical to the official binary (227 B stdout, rc=0 both) — it was blocked on #10290 and the handle-gate fix cleared it.Rung ladder against the v10 build (five fixes: #10300, #10302, #10290, #10303, #10293):
rung result --version,--help,run --help,models --help,agent --help,mcp --help,providers --help,completionIDENTICAL modelsIDENTICAL (newly unblocked) run --model …broken by #10310 TUI renderer constructs; bootstrap blocked by #10310 9 of 10 rungs byte-identical. #10310 is the only thing standing between here and the TUI, and it is also the only thing breaking the tenth rung.
Timings on the Linux build host (warm, single run — not a benchmark host, so treat as shape not measurement):
bun perry RSS bun → perry --version0.33 s 1.83 s 195 MB → 572 MB --help0.31 s 3.23 s 208 MB → 564 MB models0.98 s 28.95 s 469 MB → 3741 MB The
modelsrow is the one worth attacking next for #10106 — 29× and 8× the memory, against 5-10× on the trivial rungs.- added 9 commits that reference this issue
on Sep 15, 2026
Supersedes the pinned-revision tracker #8506 (fc80874, 2026-08-20). Everything below was measured on 2026-09-12 against anomalyco/opencode tag v1.18.30 (commit 3104c14, 2026-09-09, bun 1.3.14) with perry origin/main dc0d876.
Goal
packages/opencode/src/index.tsand its complete dependency graph natively: no feature-removal patch, nobun buildpre-pass, no minified input, no JavaScript engine at runtime.run,serve, the full OpenTUI TUI, providers, MCP, Code Mode, shell tool, image, watcher, plugins (see decision ticket), mDNS and web UI must work.opencode --version: bun = 0.54 s user CPU, 1.0–1.5 s wall, 185 MB RSS on the loaded dev Mac).Reference facts (see
secret-tests/opencode-1.18.30-inventory/README.md).tsx, 9.wasm, 3 Worker entrypoints.new Functionsite was classified (34 sites, 12 packages): none is on the startup or normal-session path; live-but-cold: ajv (MCP outputSchema validators) and fastify+find-my-way+fast-json-stringify (only insideopencode-gitlab-auth's OAuth callback server). Perry's default-on dyn-eval interpreter covers both. No stripping is needed for eval.@parcel/watcher(runtime: Node-API (N-API) host for prebuilt napi addons — tracker #8523/runtime: add native @parcel/watcher compatibility facade #8532), Bun globals (bun-compat: Bun globals + "bun" module shim pack (Bun.stringWidth, Bun.file/write, Bun.stdin.text, Bun.hash) #6560/fix(bun): complete OpenCode compatibility coverage #8537), Worker entries (AOT: compile and register Web Worker entry modules used by full-source OpenCode #8509), asset packaging (feat(compile): package generated asset modules #8534), transpileModule for Code Mode (feat(compat): add native TypeScript transpileModule subset #8535), full-graph compile (fix: compile large TypeScript source graphs natively #8383/fix(compile): support full OpenCode source builds #9133, Windows only so far).Blocker tickets (this set)
@opentui/solid) + solid-js condition swap #10099 — compile-time Solid JSX transform targeting@opentui/solid(+ solid-js export-condition swap)plugin/Bun.pluginas an inert registrar so OpenTUI's runtime-plugin support installs #10100 —bunmodule:plugin()/Bun.pluginno-op registrar (TUI startup calls it)--define, Bun.build semantics) and the no-define fallbacks OpenCode relies on (import.meta.resolve,new URL(..., import.meta.url)) #10101 — build-time defines (--define) and the no-define fallbacks:import.meta.resolve(incl. Bun 2-arg form),new URL(x, import.meta.url)worker pathsimport(path, { with: { type: "toml" } })of a runtime file path must parse TOML like Bun (OpenCode legacy config migration) #10104 — runtime dynamicimport()with{ with: { type: "toml" } }(legacy config migration)opencode --helpcost 31 s #10179 → PR perf(regex): cache compiled programs and speed up test/replace #10193); --version = eager init of 7,897 modules (bun tree-shakes to 4,063) → compile: prune modules reachable only through unused re-exports of side-effect-free packages (Bun's graph: 4,063 modules; perry compiles and initializes 7,897 for the same OpenCode entry) #10180use of undefined value '@…_.str.67.bytes') — @opentui/core chunk-node-q0cwyvm9.js, the one failing module in the OpenCode v1.18.30 graph #10152 — split-unit codegen bug in @opentui/core chunk-node (only failing module) — PR fix(codegen): resolve orphan dispatch bytes in split units #10154 (lane, astra) open; verified on the full graph by the 2026-09-13 perrymaster compileObject.defineProperty(exports, "transformAsync", { get })in @babel/core) binds to a non-existentperry_fn_symbol — the one unresolved link symbol in the OpenCode v1.18.30 graph #10153 — CJS getter re-export (@babel/core transformAsync) binds to a missing perry_fn_ symbol; the only unresolved symbol at link — PR fix(cjs): preserve live getter re-export bindings #10156 (lane, astra) openexport * as X from "./self"is undefined on a dynamic import() namespace (key present, value undefined) — blocks every OpenCode command at the effect-cmd bootstrap #10160 — dynamic import() namespace:export * as X from "./self"value undefined (blocks run/models/serve/TUI bootstrap) — PR fix(codegen): publish self namespace re-exports as live accessors (#10160) #10162 (fix verified on the 2-module repro; full-graph run pending)__exportgetter helper inside a require cycle is not seen as defining the exports — perry prints node's 'Accessing non-existent property … inside circular dependency' warning (@vercel/oidc via @ai-sdk/gateway; semver range↔comparator) where node and bun are silent #10178 — esbuild __export getters in a CJS cycle: perry-only circular-dependency warnings (@vercel/oidc, semver) at every command startns.member(...nonLiteralArray)— a namespace-import member call with a spread argument evaluates to the function instead of calling it; breaks effect's Layer.mergeAll and every OpenCode command #10197 — namespace-member call with spread args is not called (effect Layer.mergeAll → the 'size' bootstrap wall) — PR fix(codegen): route const rest exports through the closure spread call (#10197) #10199 (one-condition fix + regression test; full-graph run pending)Harness recipe (macOS, this repo)
Oracle:
gh release download v1.18.30 -R anomalyco/opencode -p 'opencode-darwin-arm64.zip'(the official bun binary; homebrew ships 1.17.7).Acceptance ladder
--versionprints1.18.30(with defines) /local(without);--helpbyte-identical to the oracle.run "say hi"with a real key round-trips one Anthropic completion;serveanswers/global/health.--versionuser CPU and RSS below the oracle on the quiet Mac mini (see 08).Non-blockers recorded for completeness
@ai-sdk/provider-utilssniffsFunction.prototype.toString.call(fetch)for "internal/deps/undici" — falls back toglobalThis.fetchunder perry, benign.fn.constructor.name === 'AsyncFunction'(GitLab login path only) — needs node-shaped function names (Function .name and Function.prototype.toString leak compiler internals (Made$4,__anon_class_8,[native code]) #9413 landed).typescript@5.8.2(9 MiB, Code Mode),prettier(generate cmd),@babel/*(only because of the runtime Solid plugin — becomes dead once 01/02 land), threeentitiesversions with generated decode tables, js-yaml 3 + esprima.Landing log