Skip to content

fix(ai-isolate-quickjs): replace asyncify host functions with promise bridging #948

Open
SilvioYMollov wants to merge 10 commits into
TanStack:mainfrom
SilvioYMollov:fix/quickjs-promise-bridge
Open

fix(ai-isolate-quickjs): replace asyncify host functions with promise bridging #948
SilvioYMollov wants to merge 10 commits into
TanStack:mainfrom
SilvioYMollov:fix/quickjs-promise-bridge

Conversation

@SilvioYMollov

@SilvioYMollov SilvioYMollov commented Jul 15, 2026

Copy link
Copy Markdown

Changes

Replaces the Asyncify-based host tool bridge in @tanstack/ai-isolate-quickjs with native QuickJS promises, and makes execution timeouts actually work under the new model.

Problem

Two bugs with the same root cause:

  1. Awaiting a tool binding more than once crashes the process with Assertion failed: p->ref_count == 0. The abort kills the shared asyncify WASM module, so every other context dies too. Root cause is a known upstream asyncify bug: Multiple sequential await calls in evalCodeAsync fail with "Lifetime not alive" justjake/quickjs-emscripten#258.
  2. A tool that never settles hangs execute() forever. The timeout is only a QuickJS interrupt handler, and no host-side timer runs while the guest waits on a host call — so it never fires.

// crashes on the second await
const a = await get({ path: "/a" });
const b = await get({ path: "/b" });

Fix

  • Use the sync QuickJS build instead of newAsyncContext(). Tool bindings are now plain host functions that return a vm.newPromise(); the host resolves it when the tool finishes and pumps executePendingJobs() to resume the guest. The WASM stack never suspends, so the crash can't happen.
  • Race the program promise against a real host-side timer, so stuck executions return TimeoutError instead of hanging. A timeout is terminal for the context: interrupted jobs stay queued in the VM and must never run in a later execution, so outstanding tool calls are cancelled and the VM disposed safely.
  • Surface executePendingJobs() errors instead of swallowing them.
  • The global cross-context queue becomes per-context (it only existed for asyncify), and tool calls in one execution can run concurrently.

Tests

Regression tests for sequential awaits, Promise.all tool calls, TimeoutError on timeout, and context disposal after timeout.

Related

Summary by CodeRabbit

  • New Features
    • Improved QuickJS tool execution with promise-based host bridging and enhanced support for concurrent tool calls.
  • Bug Fixes
    • Refined timeout handling: timeouts now emit a dedicated TimeoutError, cancel pending tool calls, and make the execution context terminal (subsequent executions fail with DisposedError).
    • Improved handling of in-flight tool calls, including proper cleanup and safer disposal during mid-execution timeouts.
  • Documentation
    • Updated migration guidance for the required QuickJS WASM variant and clarified timeout terminality.
  • Tests
    • Added/expanded coverage for timeout normalization, concurrent calls, post-timeout disposal, and resilience after OOM/late settlements.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The QuickJS isolate driver now uses synchronous contexts and promise-based host-tool bridging. Execution tracks shared deadlines and pending cancellations, normalizes timeout interrupts, disposes timed-out contexts, and adds coverage for sequential, concurrent, and terminal timeout behavior.

Changes

QuickJS promise bridge and execution lifecycle

Layer / File(s) Summary
Host tool promise bridge
packages/ai-isolate-quickjs/src/isolate-driver.ts, packages/ai-isolate-quickjs/tests/isolate-driver.test.ts
Synchronous QuickJS contexts inject host tools through promise-backed bindings that return JSON success/error envelopes and support sequential and concurrent calls.
Execution timeout and disposal lifecycle
packages/ai-isolate-quickjs/src/error-normalizer.ts, packages/ai-isolate-quickjs/src/isolate-context.ts, packages/ai-isolate-quickjs/tests/isolate-driver.test.ts
Execution uses explicit promise-job processing, shared deadlines, cancellation grace handling, timeout normalization, and terminal VM disposal; tests cover timeout, disposal, late settlement, and OOM behavior.
Migration notes
.changeset/quickjs-promise-bridge.md
Package notes describe the synchronous WASM variant, promise bridge, concurrent calls, and terminal timeout semantics.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • TanStack/ai#943 — Refactors the same QuickJS bridge and execution timeout paths with corresponding tests.
  • TanStack/ai#946 — Contains closely related synchronous-context and host-to-native-promise bridge changes.

Sequence Diagram(s)

sequenceDiagram
  participant QuickJS
  participant QuickJSIsolateContext
  participant ToolBinding
  QuickJS->>QuickJSIsolateContext: execute guest code
  QuickJSIsolateContext->>ToolBinding: invoke promise-backed binding
  ToolBinding-->>QuickJSIsolateContext: return result or error envelope
  QuickJSIsolateContext-->>QuickJS: resolve guest promise
  QuickJSIsolateContext->>QuickJSIsolateContext: enforce deadline and dispose on timeout
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: replacing asyncify host bridging with promise-based bridging in ai-isolate-quickjs.
Description check ✅ Passed The description covers the problem, fix, tests, and related issue, but it omits the template's checklist and release impact sections.
Linked Issues check ✅ Passed The changes address #947 by removing asyncify, supporting sequential and concurrent tool calls, enforcing host-side timeouts, and disposing timed-out contexts.
Out of Scope Changes check ✅ Passed The added tests and changeset support the same QuickJS bridge and timeout fix, with no clear unrelated code changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@SilvioYMollov SilvioYMollov changed the title Fix/quickjs promise bridge fix(ai-isolate-quickjs): replace asyncify host functions with promise bridging (quickjs-emscripten#258) Jul 15, 2026
@SilvioYMollov SilvioYMollov changed the title fix(ai-isolate-quickjs): replace asyncify host functions with promise bridging (quickjs-emscripten#258) fix(ai-isolate-quickjs): replace asyncify host functions with promise bridging Jul 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ai-isolate-quickjs/src/isolate-context.ts (1)

276-288: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

dispose() can double-dispose (or wrongly free a leaked VM) after awaiting the queue.

dispose() checks this.disposed once, before awaiting this.execQueue. If an execute() call is in flight and it finishes via releaseAfterTimeout()/releaseVmAfterFatalError() while dispose() is waiting, this.disposed becomes true and this.vm may already be disposed (or intentionally left undisposed because guestSettled was still false — the documented "leak instead of crash" case for an unsettled program promise). Once the await resolves, dispose() unconditionally does this.disposed = true; this.vm.dispose() again:

  • If the VM was already disposed → double-dispose (this library errors on invalid/duplicate handle operations).
  • If the VM was intentionally left un-disposed because the guest program promise never settled → this call disposes it anyway, which is exactly the "freeing a runtime that still holds an unsettled program promise aborts the shared WASM module" failure this file's own comments warn about.

Neither existing timeout test calls dispose() concurrently with an in-flight execute(), so this path is untested.

🐛 Proposed fix
     if (this.executing) {
       await this.execQueue
+      // The in-flight execute() may have already disposed (or intentionally
+      // left undisposed) the VM via releaseAfterTimeout/releaseVmAfterFatalError.
+      if (this.disposed) return
     }
🤖 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/src/isolate-context.ts` around lines 276 - 288,
Recheck this.disposed after awaiting this.execQueue in dispose() and return
without touching the VM if execution cleanup already disposed or intentionally
retained it. Only set this.disposed and call this.vm.dispose() when the context
remains undisposed after the wait, preserving the unsettled-program leak
behavior.
🧹 Nitpick comments (1)
packages/ai-isolate-quickjs/tests/isolate-driver.test.ts (1)

217-237: 📐 Maintainability & Code Quality | 🔵 Trivial

Consider adding a regression test for dispose() racing an in-flight timeout.

These tests cover the sequential case (timeout resolves, then dispose() is called afterward). They don't cover calling context.dispose() while a timing-out execute() is still in-flight — which is exactly the scenario where the dispose() double-release issue (flagged in isolate-context.ts) manifests. Not blocking, but would have caught that bug.

🤖 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/tests/isolate-driver.test.ts` around lines 217 -
237, Add a regression test alongside the existing timeout disposal test that
starts a timing-out context.execute call, invokes context.dispose() before
execution settles, and awaits both promises. Assert disposal resolves
successfully and the in-flight execution completes with the expected timeout or
disposed outcome, confirming no double-release failure.
🤖 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 `@packages/ai-isolate-quickjs/src/error-normalizer.ts`:
- Around line 46-54: Update normalizeError in
packages/ai-isolate-quickjs/src/error-normalizer.ts to detect interrupted
QuickJS errors in the dumped plain-object branch, requiring name InternalError
and an interrupted message before returning TimeoutError. Keep the existing
Error-instance check similarly scoped to InternalError, and update the
jobs.error handling in packages/ai-isolate-quickjs/src/isolate-context.ts only
as needed to preserve releaseAfterTimeout() for these normalized interrupts.

In `@packages/ai-isolate-quickjs/src/isolate-driver.ts`:
- Around line 67-140: Update the settled handler in injectBinding so the
QuickJSDeferredPromise created by vm.newPromise() is disposed after
executePendingJobs() completes, including the jobs.error handling path. Ensure
disposal occurs once per tool call after pending jobs are drained, while
preserving the existing runtime-alive guard and error cleanup.

---

Outside diff comments:
In `@packages/ai-isolate-quickjs/src/isolate-context.ts`:
- Around line 276-288: Recheck this.disposed after awaiting this.execQueue in
dispose() and return without touching the VM if execution cleanup already
disposed or intentionally retained it. Only set this.disposed and call
this.vm.dispose() when the context remains undisposed after the wait, preserving
the unsettled-program leak behavior.

---

Nitpick comments:
In `@packages/ai-isolate-quickjs/tests/isolate-driver.test.ts`:
- Around line 217-237: Add a regression test alongside the existing timeout
disposal test that starts a timing-out context.execute call, invokes
context.dispose() before execution settles, and awaits both promises. Assert
disposal resolves successfully and the in-flight execution completes with the
expected timeout or disposed outcome, confirming no double-release failure.
🪄 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: 12c506fa-80e8-43ab-ac16-5c53f12bbffb

📥 Commits

Reviewing files that changed from the base of the PR and between 5fcaf90 and 6d7c9a1.

📒 Files selected for processing (5)
  • .changeset/quickjs-promise-bridge.md
  • packages/ai-isolate-quickjs/src/error-normalizer.ts
  • packages/ai-isolate-quickjs/src/isolate-context.ts
  • packages/ai-isolate-quickjs/src/isolate-driver.ts
  • packages/ai-isolate-quickjs/tests/isolate-driver.test.ts

Comment on lines +46 to +54
// QuickJS reports a fired interrupt handler as "InternalError: interrupted".
if (lower.includes('interrupted')) {
return {
name: TIMEOUT_ERROR,
message: 'Code execution timed out',
stack: error.stack,
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files packages/ai-isolate-quickjs/src | sed -n '1,200p'
echo '--- error-normalizer.ts ---'
wc -l packages/ai-isolate-quickjs/src/error-normalizer.ts
sed -n '1,220p' packages/ai-isolate-quickjs/src/error-normalizer.ts
echo '--- isolate-context.ts ---'
wc -l packages/ai-isolate-quickjs/src/isolate-context.ts
sed -n '1,280p' packages/ai-isolate-quickjs/src/isolate-context.ts
echo '--- normalizeError usages ---'
rg -n "normalizeError|interrupted|InternalError|TIMEOUT_ERROR|jobs.error|executePendingJobs|pendingJobs" packages/ai-isolate-quickjs/src

Repository: TanStack/ai

Length of output: 14755


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
files = [
    Path('packages/ai-isolate-quickjs/src/error-normalizer.ts'),
    Path('packages/ai-isolate-quickjs/src/isolate-context.ts'),
]
for p in files:
    print(f"===== {p} =====")
    text = p.read_text()
    for needle in ['interrupted', 'InternalError', 'TIMEOUT_ERROR', 'normalizeError', 'jobs.error', 'executePendingJobs']:
        if needle in text:
            print(f"-- contains {needle}")
    print()
PY

Repository: TanStack/ai

Length of output: 512


Handle QuickJS interrupts in the dumped-object path too

normalizeError() only maps "interrupted" to TimeoutError for Error instances. The jobs.error path in packages/ai-isolate-quickjs/src/isolate-context.ts dumps the QuickJS error into a plain object first, so interrupts there still normalize to InternalError and skip releaseAfterTimeout(). Add the same InternalError/interrupted check to the object branch, and keep the Error branch scoped to InternalError to avoid misclassifying unrelated errors.

📍 Affects 2 files
  • packages/ai-isolate-quickjs/src/error-normalizer.ts#L46-L54 (this comment)
  • packages/ai-isolate-quickjs/src/isolate-context.ts#L161-L198
🤖 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/src/error-normalizer.ts` around lines 46 - 54,
Update normalizeError in packages/ai-isolate-quickjs/src/error-normalizer.ts to
detect interrupted QuickJS errors in the dumped plain-object branch, requiring
name InternalError and an interrupted message before returning TimeoutError.
Keep the existing Error-instance check similarly scoped to InternalError, and
update the jobs.error handling in
packages/ai-isolate-quickjs/src/isolate-context.ts only as needed to preserve
releaseAfterTimeout() for these normalized interrupts.

Comment thread packages/ai-isolate-quickjs/src/isolate-driver.ts
@tombeckenham tombeckenham self-assigned this Jul 17, 2026
@tombeckenham
tombeckenham self-requested a review July 17, 2026 00:36

@tombeckenham tombeckenham left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

Strong root-cause fix for #947 / quickjs-emscripten#258. Promise bridging, host-side deadlines, terminal timeouts, and abandoned-tool dispose are the right direction — and the regression suite is substantially better than the competing PR.

Requesting changes for one FreeRuntime hole and a few important follow-ups before merge.

Freshness

  • Behind main by ~6 commits; MERGEABLE, no real conflict with packages/ai-isolate-quickjs (only a version bump on main).
  • Still fully applicable; soft rebase is fine, not blocking.
  • Prefer this PR over #946 as the landing vehicle (more complete lifecycle, migration notes, broader tests). One idea worth porting from #946: cancel pending host deferreds on fatal OOM/stack, not only timeout.

Must fix

  1. Fatal OOM/stack path does not settle tool deferreds before vm.dispose() (inline).
  2. promise.settled early-return can skip deferred dispose (inline).

Should fix

  1. Timeout path should assert shared WASM still healthy (new context after hanging-tool timeout / late host settle) — dispose path already does this.
  2. Stale product docs still describe asyncify + global queue (docs/code-mode/code-mode-isolates.md, package README, code-mode skill).
  3. Early jobs.error after guestSettled = false can leave a reusable dirty context (inline).
  4. guestSettled intro comment is inaccurate for busy-loop interrupt (inline).

Strengths

  • Correct asyncify removal; dual timeout (interrupt + host deadline).
  • Intentional leak when guest cannot settle after timeout.
  • Excellent Promise.race abandon + dispose regression (process-level WASM health).
  • Changeset migration notes (sync WASM + terminal timeouts) match code-mode per-execution context usage.
  • Review follow-ups (dispose race recheck, deferred settle on dispose) look solid.

Process

  • Full TanStack CI has not run on this fork head (only CodeRabbit/Socket) — please ensure workflows are approved/green before merge.
  • After the fatal-path cancel lands, unit tests covering “OOM with in-flight tool → new context still works” would lock the invariant.

@@ -76,10 +158,37 @@ export class QuickJSIsolateContext implements IsolateContext {
this.vm.dispose()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical — FreeRuntime hole on fatal limits

releaseAfterTimeout and dispose() both cancel pendingCancels before vm.dispose() because unsettled QuickJS deferreds abort the shared WASM module (list_empty(&rt->gc_obj_list)). This path does not.

Repro shape:

const p = hangTool({})              // registers deferred + pendingCancel
return 'x'.repeat(8 * 1024 * 1024)  // MemoryLimitError while deferred still live

Existing OOM tests never leave a tool in flight, so this is unguarded.

Suggested fix: best-effort cancel + grace tick (same as timeout/dispose) before free; if the sick heap cannot cancel, catch and prefer intentional leak over aborting the shared module. See also #946’s requestVmDispose(), which already cancels on both timeout and fatal limits.

Test to add: start a never-resolving tool, then OOM (or stack overflow); assert a new context from the same driver still executes successfully.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for pointing this out! Fatal limit cleanup now settles pending tool deferred before disposing the VM. I also added an OOM regression test that verifies a new context still works afterward, using your repro suggestion.

// active execute() the interrupt deadline is 0, so a stray job from an
// abandoned execution is interrupted instead of running unbounded.
void promise.settled.then(() => {
if (!vm.runtime.alive) return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important — early return skips promise.dispose()

If settled runs after the runtime is already dead, this returns before the try/finally that calls promise.dispose(). Paired with the fatal path that frees without cancelling deferreds, host-side deferred lifetimes can outlive FreeRuntime.

Please dispose unconditionally, e.g.:

void promise.settled.then(() => {
  try {
    if (vm.runtime.alive) {
      const jobs = vm.runtime.executePendingJobs()
      // ...
    }
  } finally {
    if (promise.alive) promise.dispose()
  }
})

if (jobs.error) {
const dumped: unknown = this.vm.dump(jobs.error)
jobs.error.dispose()
return await fail(dumped)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important — dirty context reuse after early jobs.error

Here guestSettled is already false and nativePromise is never awaited. fail() only terminal-disposes for TimeoutError / memory / stack.

For any other jobs.error, the context stays reusable while the prior program promise / jobs may still be pending — next execute() can interleave leftover work under a fresh deadline.

Prefer one of: await/cancel the program promise, treat as terminal (dispose), or fully drain/cancel outstanding work before returning a non-terminal error.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by treating an early pending-job error as terminal and cancelling any outstanding work before safely releasing the context.


const releaseVmAfterFatalLimit = () => {
// True until a program promise is in flight; sync failure paths (parse
// errors, interrupted straight-line code) leave no unsettled guest state.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit — comment contradicts busy-loop timeout path

This says interrupted straight-line code leaves no unsettled guest state, but the busy-loop timeout sets guestSettled = false before executePendingJobs() and relies on the intentional VM leak when it stays false (releaseAfterTimeout).

Suggest rewriting along the lines of: starts true; cleared once resolvePromise is armed; stays false if the program promise never settles (e.g. interrupt during a spin); only pre-arm failures leave it true so timeout release can dispose.

expect(second.success).toBe(false)
expect(second.error?.name).toBe('DisposedError')

await expect(context.dispose()).resolves.toBeUndefined()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important — assert process-level WASM health after timeout cancel

This correctly checks context terminality (DisposedError on reuse). The dispose + abandoned-tool test below goes further and proves a new context still works after FreeRuntime-sensitive cleanup.

Please extend this path similarly:

  1. After hanging-tool TimeoutError, createContext + execute on the same driver succeeds.
  2. Optionally: host tool resolves after timeout cancel; second settle must no-op without poisoning the module.

That locks the same invariant the dispose path already guards.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extended the test coverage, it now resolves the host tool after timeout cancellation and verifies that a new context still executes successfully, confirming the late settlement is a no-op and the shared WASM module remains healthy.

@nx-cloud

nx-cloud Bot commented Jul 17, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 3ddf160

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 5s View ↗

☁️ Nx Cloud last updated this comment at 2026-07-17 00:37:24 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jul 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@948

@tanstack/ai-acp

npm i https://pkg.pr.new/@tanstack/ai-acp@948

@tanstack/ai-angular

npm i https://pkg.pr.new/@tanstack/ai-angular@948

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@948

@tanstack/ai-bedrock

npm i https://pkg.pr.new/@tanstack/ai-bedrock@948

@tanstack/ai-claude-code

npm i https://pkg.pr.new/@tanstack/ai-claude-code@948

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@948

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@948

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@948

@tanstack/ai-codex

npm i https://pkg.pr.new/@tanstack/ai-codex@948

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@948

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@948

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@948

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@948

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@948

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@948

@tanstack/ai-grok-build

npm i https://pkg.pr.new/@tanstack/ai-grok-build@948

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@948

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@948

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@948

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@948

@tanstack/ai-mcp

npm i https://pkg.pr.new/@tanstack/ai-mcp@948

@tanstack/ai-mistral

npm i https://pkg.pr.new/@tanstack/ai-mistral@948

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@948

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@948

@tanstack/ai-opencode

npm i https://pkg.pr.new/@tanstack/ai-opencode@948

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@948

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@948

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@948

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@948

@tanstack/ai-sandbox

npm i https://pkg.pr.new/@tanstack/ai-sandbox@948

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-sandbox-cloudflare@948

@tanstack/ai-sandbox-daytona

npm i https://pkg.pr.new/@tanstack/ai-sandbox-daytona@948

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/@tanstack/ai-sandbox-docker@948

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/@tanstack/ai-sandbox-local-process@948

@tanstack/ai-sandbox-sprites

npm i https://pkg.pr.new/@tanstack/ai-sandbox-sprites@948

@tanstack/ai-sandbox-vercel

npm i https://pkg.pr.new/@tanstack/ai-sandbox-vercel@948

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@948

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@948

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@948

@tanstack/ai-utils

npm i https://pkg.pr.new/@tanstack/ai-utils@948

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@948

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@948

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@948

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@948

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@948

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@948

commit: 3ddf160

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants