feat(ai-isolate): add native QuickJS Code Mode isolate driver for Bun#750
feat(ai-isolate): add native QuickJS Code Mode isolate driver for Bun#750lithdew wants to merge 2 commits into
Conversation
Adds @tanstack/ai-isolate-quickjs-bun, a Code Mode IsolateDriver that runs QuickJS natively on the Bun runtime via bun:ffi (through quickjs-bun) instead of WebAssembly. It is a drop-in replacement for @tanstack/ai-isolate-quickjs on Bun servers. - Per-context native QuickJS runtime with its own memory/stack/timeout limits; contexts execute independently (the WASM driver serializes all executions through one asyncified module). - Same JSON tool-call protocol, console prefixes, and normalized MemoryLimit/StackOverflowError/DisposedError contract as the other drivers, plus a normalized TimeoutError for deadline expiry. - maxToolCalls (default 1000) and conosle log caps to bound stack and memory growth from untrusted snadbox code. - Requires Bun >= 1.3.14; throws an error on Node.js. Unit tests mirror the WASM/Node suites and run under `bun test`; the Node-side rejection test runs in normal CI. Docs, the ai-code-mode README + skill, and the code-mode example have been updated.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughNew package ChangesBun QuickJS Isolate Driver
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
examples/ts-code-mode-web/src/components/ToolSidebar.tsx (1)
106-111: 💤 Low valueConsider runtime detection for better UX.
The option is marked
available: trueunconditionally, so users on Node.js can select it but will encounter a runtime error when the driver attempts to create a context. While the description warns "requires running the server with Bun," detecting the runtime at page load and conditionally settingavailable: falseon Node would prevent confusing errors.🎨 Optional runtime detection pattern
{ id: 'quickjs-bun', name: 'QuickJS Bun', description: 'Native QuickJS engine (requires running the server with Bun)', - available: true, + available: typeof Bun !== 'undefined', },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/ts-code-mode-web/src/components/ToolSidebar.tsx` around lines 106 - 111, The quickjs-bun item in ToolSidebar.tsx currently hard-codes available: true, which allows Node clients to select it and hit runtime errors; change it to compute availability at load time (e.g., derive a boolean like isBunRuntime from a server-provided endpoint or a prop/initial state and set available: isBunRuntime) so the entry for id 'quickjs-bun' is disabled when the server/runtime is not Bun; update the ToolSidebar component to fetch or accept the runtime indicator (or feature flag) and use that to set the available property for the 'quickjs-bun' item and the UI disabled state.packages/ai-isolate-quickjs-bun/src/isolate-driver.ts (1)
141-142: 💤 Low valueConsider caching the module import alongside the library.
importQuickJSBun()is called on everycreateContext()invocation. While JavaScript runtimes cache dynamic imports, you could align this with thelibraryPromisepattern for consistency and to make the caching explicit.♻️ Suggested refactor
-let libraryPromise: Promise<QuickJS> | undefined +let modulePromise: Promise<QuickJSBunModule> | undefined +let libraryPromise: Promise<QuickJS> | undefined +async function loadQuickJSModule(): Promise<QuickJSBunModule> { + modulePromise ??= importQuickJSBun() + try { + return await modulePromise + } catch (error) { + modulePromise = undefined + throw error + } +} async function loadQuickJSLibrary(): Promise<QuickJS> { - libraryPromise ??= importQuickJSBun().then((mod) => new mod.QuickJS()) + libraryPromise ??= loadQuickJSModule().then((mod) => new mod.QuickJS()) // ... }Then in
createContext:- const quickjs = await importQuickJSBun() + const quickjs = await loadQuickJSModule() const library = await loadQuickJSLibrary()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ai-isolate-quickjs-bun/src/isolate-driver.ts` around lines 141 - 142, importQuickJSBun() is being invoked on every createContext() call; make this explicit and consistent with the existing libraryPromise pattern by caching the dynamic import result. Add a top-level promise (e.g., quickjsModulePromise) that stores importQuickJSBun(), use that cached promise inside createContext() instead of calling importQuickJSBun() directly, and ensure you still await loadQuickJSLibrary() (libraryPromise) as before so both the module and library are only loaded once.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/code-mode/code-mode-isolates.md`:
- Around line 115-120: The markdown link in the QuickJS Bun Driver paragraph
uses backticks around the link text
(`[quickjs-bun](https://github.com/superpowerdotcom/quickjs-bun)`) which
prevents it rendering as a proper clickable link; update the text in the QuickJS
Bun Driver section (the line containing
`[quickjs-bun](https://github.com/superpowerdotcom/quickjs-bun)`) to either use
monospace label with a linked URL like via the
[`quickjs-bun`](https://github.com/superpowerdotcom/quickjs-bun) package or a
normal link via the
[quickjs-bun](https://github.com/superpowerdotcom/quickjs-bun) package so the
link renders correctly.
---
Nitpick comments:
In `@examples/ts-code-mode-web/src/components/ToolSidebar.tsx`:
- Around line 106-111: The quickjs-bun item in ToolSidebar.tsx currently
hard-codes available: true, which allows Node clients to select it and hit
runtime errors; change it to compute availability at load time (e.g., derive a
boolean like isBunRuntime from a server-provided endpoint or a prop/initial
state and set available: isBunRuntime) so the entry for id 'quickjs-bun' is
disabled when the server/runtime is not Bun; update the ToolSidebar component to
fetch or accept the runtime indicator (or feature flag) and use that to set the
available property for the 'quickjs-bun' item and the UI disabled state.
In `@packages/ai-isolate-quickjs-bun/src/isolate-driver.ts`:
- Around line 141-142: importQuickJSBun() is being invoked on every
createContext() call; make this explicit and consistent with the existing
libraryPromise pattern by caching the dynamic import result. Add a top-level
promise (e.g., quickjsModulePromise) that stores importQuickJSBun(), use that
cached promise inside createContext() instead of calling importQuickJSBun()
directly, and ensure you still await loadQuickJSLibrary() (libraryPromise) as
before so both the module and library are only loaded once.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: efa0ea2b-d53c-4c49-ac81-f8007e2d5a1d
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (23)
.changeset/quickjs-bun-isolate-driver.mddocs/code-mode/code-mode-isolates.mddocs/code-mode/code-mode.mddocs/comparison/vercel-ai-sdk.mddocs/config.jsonexamples/ts-code-mode-web/package.jsonexamples/ts-code-mode-web/src/components/ToolSidebar.tsxexamples/ts-code-mode-web/src/lib/create-isolate-driver.tsexamples/ts-code-mode-web/vite.config.tsknip.jsonpackages/ai-code-mode/README.mdpackages/ai-code-mode/skills/ai-code-mode/SKILL.mdpackages/ai-isolate-quickjs-bun/README.mdpackages/ai-isolate-quickjs-bun/benchmarks/compare-with-wasm.tspackages/ai-isolate-quickjs-bun/package.jsonpackages/ai-isolate-quickjs-bun/src/error-normalizer.tspackages/ai-isolate-quickjs-bun/src/index.tspackages/ai-isolate-quickjs-bun/src/isolate-context.tspackages/ai-isolate-quickjs-bun/src/isolate-driver.tspackages/ai-isolate-quickjs-bun/tests/escape-attempts.test.tspackages/ai-isolate-quickjs-bun/tests/isolate-driver.test.tspackages/ai-isolate-quickjs-bun/tsconfig.jsonpackages/ai-isolate-quickjs-bun/vite.config.ts
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
🎯 Changes
Adds a new Code Mode isolate driver,
@tanstack/ai-isolate-quickjs-bun, that runs QuickJS natively on Bun viabun:ffi(throughquickjs-bun) rather than WebAssembly.It implements the existing
IsolateDrivercontract from@tanstack/ai-code-mode, so it's a drop-in replacement for@tanstack/ai-isolate-quickjson Bun servers:Why
On Bun, the existing WASM driver (
quickjs-emscripten) is both slower and, in my testing, unreliable for async host tool calls. Its asyncify bridge crashes the shared WASM module (memory access out of bounds) and hangs once a single execution makes ≥ 4 sequential awaited tool calls (reproduced on Node 22 and Bun 1.3.14).quickjs-bunmaps the QuickJS C API directly throughbun:ffi, giving each context its own native runtime with no asyncify and no shared-VM serialization.Benchmarks
packages/ai-isolate-quickjs-bun/benchmarks/compare-with-wasm.ts, both drivers driven through the publicIsolateDriverinterface (Apple M-series, darwin/arm64; Bun 1.3.14 for the native driver, Node 22 for the WASM driver as its native habitat):return 1 + 1fib(20))return 1 + 1(reused context)¹ The WASM driver's asyncified host tool calls repeatedly crash the shared WASM module and hang subsequent executions (≥ 4 sequential awaited host calls per process), on both Node 22 and Bun 1.3.14. Sync-only workloads are unaffected. WASM wins cold start (one-time WASM instantiate vs TinyCC compile of the QuickJS sources); the native driver wins steady-state per-execution by ~14× on the trivial case.
Notes
The suites are gated with
describe.skipIf(typeof Bun === 'undefined'), mirroring how@tanstack/ai-isolate-nodeskips when its native addon is unavailable — would like to know if it is fine to add aoven-sh/setup-bunjob (pnpm --filter @tanstack/ai-isolate-quickjs-bun test:bun) so that the full test suite is ran in CI.✅ Checklist
pnpm run test:pr.🚀 Release Impact
Summary by CodeRabbit
New Features
Documentation
Examples
Tests & Benchmarks