Agent runtime setup

April 22, 2026 · View on GitHub

Lumina's agent is powered by opencode, an open-source coding-agent server embedded into the Electron main process as an in-process HTTP/WS endpoint. This document is everything a new contributor or CI machine needs to run npm run dev successfully.

Why this setup is load-bearing

thirdparty/opencode is gitignored — the full opencode source tree (~147 MB) plus its node_modules (~1.6 GB) never land in Lumina's repo. The Electron main bundle imports a virtual:opencode-server module that electron-vite resolves to thirdparty/opencode/packages/opencode/dist/node/node.js — a 18 MB single file produced by opencode's bun build-node.ts toolchain.

No bundle → npm run dev fails at the main-process build step with a virtual:opencode-server resolver error.

One-time setup per machine

  1. Install bun (required — opencode's build uses Bun.build):

    curl -fsSL https://bun.sh/install | bash
    # macOS alternative:
    # brew install oven-sh/bun/bun
    
  2. Clone opencode into thirdparty/:

    git clone https://github.com/anomalyco/opencode thirdparty/opencode
    
  3. Install opencode's workspace deps (runs a postinstall native-pty rebuild — ~1.6 GB on disk):

    cd thirdparty/opencode && bun install
    
  4. Build the Node bundle (rerun whenever you bump opencode):

    cd ~/Lumina-Note   # back to repo root
    npm run opencode:bundle
    

    Produces thirdparty/opencode/packages/opencode/dist/node/node.js plus three tree-sitter-*.wasm siblings.

After step 4, npm run dev boots normally.

Smoke-testing the bundle

npm run opencode:smoke

Boots the server with a random port + basic auth and hits /global/health. Exits 0 on success. Useful for CI.

Behind the scenes

Build

  • scripts/bundle_opencode.mjs shells out to bun thirdparty/opencode/packages/opencode/script/build-node.ts with OPENCODE_CHANNEL=dev.
  • electron.vite.config.ts has two plugins:
    • lumina:virtual-opencode-server — resolves the virtual id to the built bundle; emits a setup guide as the error message if the bundle is missing.
    • lumina:copy-opencode-assets — after Vite writes out/main, copies the .wasm siblings into out/main/chunks/.

Runtime (Electron main)

  • electron/main/agent-v2/server.ts imports { Log, Server } from "virtual:opencode-server", sets OPENCODE_SERVER_USERNAME/OPENCODE_SERVER_PASSWORD env vars, then Server.listen({ port: 0, hostname: "127.0.0.1", cors: [...] }). Binds an ephemeral port and polls /global/health until ready.
  • window.lumina.opencode.getServerInfo() returns { url, username, password } to the renderer.
  • The renderer calls createOpencodeClient({ baseUrl, headers }) from @opencode-ai/sdk and subscribes to /event SSE for session / message / permission updates.

Native dependency

  • opencode's @lydell/node-pty is aliased at build time to electron/main/vendor/opencode-node-pty.ts, which loads the platform-specific package from opencode's bun workspace via createRequire at runtime. Works for npm run dev; packaging (npm run dist) is not yet wired — see the open TODO in that file.

Upgrading opencode

(cd thirdparty/opencode && git pull && bun install)
npm run opencode:bundle

No changes to Lumina's source are usually needed. If opencode changes its Server.listen() signature or @opencode-ai/sdk exports, update electron/main/agent-v2/virtual-opencode-server.d.ts and the renderer store respectively.

Customization via plugins

The agent can be extended without forking opencode. opencode's plugin system exposes hooks (chat.params, permission.ask, tool, experimental.chat.system.transform, etc.). Lumina-specific behavior (vault context injection, custom tools, approval UX) will live under electron/main/opencode-plugins/ once the plugin flow is wired — see the P5 task in the migration memory.

Troubleshooting

Rollup failed to resolve import "virtual:opencode-server" — run npm run opencode:bundle. If that itself fails, step 3 hasn't completed successfully.

ConnectionRefused or ConnectionClosed during bun install — network / proxy issue, not a code problem. In mainland China you likely need HTTPS_PROXY=http://127.0.0.1:<port> set before bun install.

bun install pulls from cdn.npmmirror.com and URLs look mangled — your repo has a stale package-lock.json or a global ~/.npmrc pointing at a mirror that bun's newer versions can't parse. Use bun install --registry=https://registry.npmjs.org or clear the lockfile.

electron/main/vendor/opencode-node-pty.ts throws at runtimethirdparty/opencode/node_modules/.bun/node_modules/@lydell/node-pty-* is missing. Either step 3 didn't finish or you're running a packaged build (not yet supported, see the TODO).