Skip to content

feat: persistence + resumable runs (provider-agnostic, optional, agent-mode)#785

Open
AlemTuzlak wants to merge 50 commits into
mainfrom
feat/persistence
Open

feat: persistence + resumable runs (provider-agnostic, optional, agent-mode)#785
AlemTuzlak wants to merge 50 commits into
mainfrom
feat/persistence

Conversation

@AlemTuzlak

Copy link
Copy Markdown
Contributor

Builds on #774 (sandboxes). Adds persistence + resumable runs as composable chat() middleware — fully optional, working for both plain model adapters and sandbox-backed harness adapters.

What this adds

withPersistence(...) makes a run durable: server-authoritative thread history, run records, an append-only AG-UI event log, usage, and (agent mode) approvals + artifacts. A run with no persistence middleware is byte-for-byte unchanged.

Resume. Each chunk carries an in-band opaque cursor (a monotonic per-run sequence). A reconnecting client sends runId + cursor; chat({ cursor }) replays the persisted tail and — for harness adapters that re-attach to the still-running in-sandbox process — continues live. The headless client tracks the cursor (resume() / getResumeState() / maybeAutoResume(), autoResume opt-out).

Event model. The persisted log is the AG-UI StreamChunk stream — no parallel event type. Agent activity rides on a typed catalog of well-known CUSTOM events.

Backends (shared SQL core + thin adapters). @tanstack/ai-persistence-sql (one SQL impl behind a minimal SqlDriver), with -sqlite, -postgres, -cloudflare (D1), and BYO -drizzle / -prisma. Raw drivers auto-migrate (opt-out); ORMs own their schema. memoryPersistence() ships in core.

Agent mode. @tanstack/ai-sandbox-persistence bridges a durable SQL SandboxStore + distributed lock into withSandbox. The shared locks capability moves to @tanstack/ai (one token); @tanstack/ai-sandbox re-exports it for back-compat.

Packages

  • New: @tanstack/ai-persistence, -sql, -sqlite, -postgres, -cloudflare, -drizzle, -prisma, @tanstack/ai-sandbox-persistence
  • Changed: @tanstack/ai (cursor/resume/locks/custom-events), @tanstack/ai-sandbox (locks token), @tanstack/ai-client (auto-resume), 4 harness adapters (supportsReattach)

Verification

~1530 tests green (core 1054, ai-sandbox 50, ai-client 384, new packages 42). sherif / knip / docs-links / publint all pass; every package typecheck + lint + build clean. SQLite/Drizzle/Prisma/Cloudflare-D1 are runtime-verified against real node:sqlite; Postgres via a fake-pool plumbing test.

Deferred (documented)

Notable, grounded design choices

  • events/resume contracts live in core — the chat() resume seam consumes them without core depending on the persistence package.
  • Message reconciliation is whole-transcript, server-authoritative: ModelMessage has no id (ids live on the client UIMessage).

🤖 Generated with Claude Code

jherr and others added 30 commits June 12, 2026 07:00
…xample

New @tanstack/ai-claude-code package that runs Claude Code (via
@anthropic-ai/claude-agent-sdk) as a TanStack AI chat backend. Unlike HTTP
provider adapters, this is a harness adapter: Claude Code owns the agent
loop and executes its built-in tools (bash, file edits, search) server-side.

- Stream translator maps Agent SDK messages to AG-UI events; harness tool
  activity arrives as already-resolved TOOL_CALL_*/TOOL_CALL_RESULT pairs
  and runs always finish with stop/length (never tool_calls), so the engine
  never re-executes harness tools. Every started tool call is guaranteed a
  result (synthesized on abort) to keep the engine's pending-call scan safe.
- TanStack toolDefinition() server tools are bridged into the harness as an
  in-process MCP server (raw JSON Schema passthrough, no zod round-trip).
  Client-side/approval tools fail fast — documented v1 limitation.
- Stateful sessions: session id surfaced via a claude-code.session-id CUSTOM
  event; resume via modelOptions.sessionId (+ forkSession).
- Structured output uses the SDK's native outputFormat json_schema.
- settingSources defaults to ['project'] so servers don't inherit user-level
  ~/.claude config from the host machine.
- E2E: excluded from the aimock matrix (subprocess can't carry X-Test-Id
  isolation); covered by 44 unit tests plus a gated live smoke spec
  (CLAUDE_CODE_E2E=1).

Also adds examples/ts-react-coding-agent: a TanStack Start app demoing
session resume, the harness tool timeline, read-only/edit permission modes,
tool bridging, and a sandboxed scratch workspace — with the agent registry
structured so future Codex/Gemini CLI harness adapters can slot in.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ters

Add two new coding-agent harness adapters alongside Claude Code:

- @tanstack/ai-codex drives OpenAI Codex via @openai/codex-sdk with local
  tool execution, resumable sessions (modelOptions.sessionId), structured
  output, and a localhost MCP bridge for TanStack server tools.
- @tanstack/ai-gemini-cli drives `gemini --acp` over the Agent Client
  Protocol with token-level streaming, resumable sessions, a configurable
  permission policy, and headless ACP auth method selection (authMethodId)
  so runs never stall on an interactive auth picker.

Wire both into the ts-react-coding-agent example: the agent dropdown keeps
every harness selectable, and a server function (createServerFn) reports
which agents are actually configured at runtime so the UI can surface a
setup dialog for unconfigured ones. Includes adapter docs and changesets.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add the @tanstack/ai-opencode package, an OpenCode harness adapter that
drives OpenCode (via @opencode-ai/sdk) as a TanStack AI chat backend with
local tool execution, token-level streaming, stateful sessions, and
TanStack tool bridging over a localhost MCP server. Wires the adapter into
the ts-react-coding-agent example, adds the OpenCode adapter docs page, and
anchors the OpenCode.md gitignore entry so it no longer shadows the docs
page on case-insensitive filesystems.

Co-authored-by: Cursor <cursoragent@cursor.com>
…e, withSandbox, workspace, policy

- @tanstack/ai-sandbox: provider-agnostic SandboxHandle/SandboxProvider/SandboxCapabilities contracts
- capability tokens (SandboxCapability + optional SandboxStore/Locks), in-memory store/lock defaults
- defineSandbox lazy controller + ensure state machine (resume->restoreSnapshot->create+bootstrap) with capability-aware degradation
- withSandbox middleware (setup provides handle; onFinish/onError snapshot+destroy)
- defineWorkspace (git/local/none + skills + secrets), provider-agnostic bootstrapWorkspace
- defineSandboxPolicy + evaluateCommand (glob, deny>ask>allow), compound sandbox key (secrets excluded)
- export DefinedChatMiddleware/AnyChatMiddleware from @tanstack/ai for portable middleware authoring
- 22 unit tests (ensure/policy/key/store); types + lint clean

Refs sandbox proposal (Phase A).
…git helper

- @tanstack/ai-sandbox-local-process: SandboxHandle over host fs/child_process (no isolation, dev loop)
- virtual /workspace root mapped to a real host dir with path containment
- exec/spawn (duplex stdin, streamed stdout), localhost port channel, env, fork via dir copy, durable fs resume-by-dir
- core: createExecBackedGit helper (shared by providers without native git); bootstrap clones into the handle's own root
- 10 unit tests (fs/exec/spawn/lifecycle/fork/bootstrap/ensure); types + lint clean
…runner

- @tanstack/ai: TextOptions.capabilities carries the middleware capability context so harness adapters can read provided capabilities (getSandbox(options.capabilities)) from chatStream; populated by the engine
- @tanstack/ai-sandbox: spawnNdjson/toLines — spawn an agent CLI in a sandbox and stream parsed NDJSON stdout (the reusable harness-execution primitive)
- tests: toLines buffering + spawnNdjson parsing (core), real spawn+NDJSON via local-process (11) — 25 core tests; types + lint clean
…ential leakage

Security review (PR #774):
- argument injection: insert '--' end-of-options separators before positionals (clone url/target, add paths) and reject url/ref/dir/path values beginning with '-' (flag-smuggling guard)
- secrets in argv: stop embedding the auth token in the clone URL (leaked via ps/logs); use a one-shot credential.helper that reads the token from the child ENV, single-quoted so the outer shell never expands it
- 4 unit tests pinning: token absent from argv + present in env, '--' separators, leading-dash rejection, quote escaping
- @tanstack/ai-sandbox-docker: SandboxHandle over a Docker container
- create/resume-by-id/restoreSnapshot(commit image)/destroy; durable fs across stop/start
- exec + duplex spawn via dockerode exec + stream demux; fs over base64 piping (binary-safe, no tar dep)
- commit-based snapshot + fork; host.docker.internal gateway for host MCP reachability; publishPorts -> ports.connect
- exec-backed git reused from core
- 3 integration tests (gated on a reachable daemon) — verified green against a real daemon: exec, fs+binary round-trip, snapshot, resume, spawn streaming, ensure+bootstrap
- pnpm-workspace: declare dockerode's optional native deps (cpu-features, ssh2) as not-built (JS fallback, local socket)
- claudeCodeText now declares requires:[SandboxCapability] and spawns the claude CLI INSIDE the sandbox via sandbox.process (claude -p --output-format stream-json), reusing translateSdkStream for the stdout NDJSON
- prompt fed via stdin (not argv); session id surfaced as before; emits a file.changed CUSTOM event with the git diff after the run
- permission-mode/allowed/disallowed/add-dir/max-turns/system-prompt mapped to CLI flags; default permission-mode bypassPermissions (sandbox is isolated)
- drop @anthropic-ai/claude-agent-sdk + @modelcontextprotocol/sdk deps; remove the in-process tool bridge (chat()-tools MCP proxy deferred — adapter rejects tools for now); provider-options self-contained
- spawnNdjson gains an  option to feed stdin
- deterministic test via a fake claude CLI in a real local-process sandbox (24 tests); types + lint clean
Runnable demo (examples/sandbox-coding-agent) that runs Claude Code inside a sandbox to fix a bug end-to-end via chat() + withSandbox:
- bootstraps a tiny git repo with a deliberate bug, asks the agent to fix it, streams output + prints the git diff
- Docker provider by default (installs the claude CLI in setup); SANDBOX=local runs on the host process
- README with prerequisites + run instructions for manual e2e verification
…lag mapping; changesets

- SandboxPolicyCapability: withSandbox provides the definition policy (conditionally); harness adapters read it via getOptional
- claude-code maps defineSandboxPolicy (default decision + fileWrite/network caps + tool-name command rules) onto --permission-mode/--allowedTools/--disallowedTools (best-effort; fine-grained command globs await the MCP permission-prompt tool)
- changesets for the sandbox layer + updated claude-code changeset for the in-sandbox behavior
- policy-map unit tests (5)
- docs/sandbox/overview.md: mental model, providers, defineWorkspace/defineSandboxPolicy, lifecycle/resume, events, the runnable example (no as-casts; latest model id)
- docs/config.json: new Sandboxes section (addedAt 2026-06-16)
- packages/ai-sandbox/skills/ai-sandbox: agent skill covering the sandbox APIs + critical rules
- ship skills in the package files
- test:docs green
…n-sandbox agent

- startHostToolBridge: host-side Streamable-HTTP MCP server exposing chat() server tools; the in-sandbox claude calls mcp__tanstack__<tool>, proxied back to the host where execute() runs (closures/DB/secrets). Per-run bearer token; binds for host.docker.internal reachability from Docker
- adapter wires --mcp-config when tools are present, picks localhost vs host.docker.internal by provider, and tears the bridge down after the run; tools no longer rejected
- 3 host-side tests via the MCP SDK client (list/call/error/auth) — verified green without needing claude
- docs + skill updated to describe the tool-proxy
- @tanstack/ai-sandbox-cloudflare: cloudflareSandbox() on @cloudflare/sandbox (edge, inside a Worker)
- uniform SandboxHandle: exec, base64-backed fs, exec-backed git, exposePort preview URLs (previewHostname), setEnvVars; spawn via startProcess+onOutput queue
- ephemeral disk + no GA snapshots -> durableFilesystem/snapshots false (withSandbox re-bootstraps across cold starts); background processes have no stdin (documented; stdin-fed harnesses need local-process/docker)
- compiles against the real @cloudflare/sandbox types; 7 deterministic handle tests against a mock Sandbox (fs round-trip, exec, spawn queue, stdin limitation, port). Runtime verification pending a Workers runtime
- align @cloudflare/workers-types version with the workspace (sherif)
- codexText declares requires:[SandboxCapability]; spawns 'codex exec --experimental-json' inside the sandbox (mirroring @openai/codex-sdk's own CLI invocation), prompt via stdin, JSONL thread events → existing translateThreadEvents
- sandbox mode / approval policy / reasoning effort / add-dir / skip-git-repo-check / config mapped to codex CLI flags; resume via 'resume <id>'
- drop @openai/codex-sdk + @modelcontextprotocol/sdk + the in-process tool bridge; provider-options self-contained; chat()-tools bridging deferred (rejects tools)
- deterministic fake-codex-CLI test in a real local-process sandbox (27 tests); types/lint/knip/sherif clean
- geminiCliText declares requires:[SandboxCapability]; spawns 'gemini --acp' inside the sandbox and drives it over ACP via the sandbox's duplex process IO
- new spawnHandleToAcpTransport adapts a SpawnHandle into the Uint8Array WebStreams ndJsonStream needs; all @agentclientprotocol/sdk protocol handling reused unchanged
- drop local child_process spawn + @modelcontextprotocol/sdk + in-process tool bridge; chat()-tools bridging deferred (rejects tools); structuredOutput throws not-supported
- transport-adapter + requires-sandbox tests (36); types/lint/knip/sherif clean
- opencodeText declares requires:[SandboxCapability]; spawns 'opencode serve' inside the sandbox, waits for readiness, exposes the port, and connects @opencode-ai/sdk's HTTP client via baseUrl (reusing startOpencodeSession's connect path)
- new startOpencodeServerInSandbox helper (readiness detection + port exposure); Docker needs publishPorts:[port]
- drop @modelcontextprotocol/sdk + in-process tool bridge; chat()-tools bridging deferred (rejects tools); structuredOutput throws not-supported; permission governed by the dynamic handler
- server-helper + requires-sandbox tests (36); types/lint/knip/sherif clean
AlemTuzlak and others added 19 commits June 16, 2026 20:56
…4 harness adapters

- move startHostToolBridge + BRIDGED_MCP_SERVER_NAME + hostForSandbox into @tanstack/ai-sandbox core (shared); add @modelcontextprotocol/sdk dep there; tool-bridge test relocated to core
- claude-code: import bridge from core; build --mcp-config from the bridge (drop local bridge + dep)
- codex: bridge tools via --config mcp_servers.<name>.url + bearer_token
- gemini-cli: bridge tools via ACP newSession mcpServers (http + Authorization header)
- opencode: bridge tools via OPENCODE_CONFIG_CONTENT mcp.remote (url + bearer header) at server spawn
- all adapters no longer reject tools; bridged tool names feed the permission handlers; changesets updated
- types/eslint/lib/build/knip/sherif green across all 5 packages
- @tanstack/ai-sandbox: shared resolveApproval (policy + client approvals -> allow/deny/needs-approval), stable approvalId, buildApprovalRequestedEvent (AG-UI CUSTOM 'approval-requested')
- @tanstack/ai: TextOptions.approvals threaded from the engine's initialApprovals so harness adapters resolve ask-policy permission requests against the client's decisions (resume-based loop)
- 11 unit tests for the resolver/keying/event; @tanstack/ai 1033 tests still pass
Wire client-in-the-loop approvals through every in-sandbox harness adapter,
built on the shared approval primitives in @tanstack/ai-sandbox.

- core bridge: optional permission-prompt tool on startHostToolBridge, and
  export PermissionToolResult so adapters can type their resolver.
- claude-code: enforce the sandbox policy via --permission-prompt-tool; an
  `ask` action with no client decision yet emits an approval-requested event
  and denies, so the client approves and re-runs to continue.
- gemini-cli / opencode: resolveInteractivePermission consults policy + client
  approvals, collects approval-requested events, and yields them after the
  stream (coercing nullable ACP tool titles).
- codex: map defineSandboxPolicy onto codex exec`s coarse knobs (sandbox mode,
  approval_policy, network_access). codex exec is non-interactive with no
  per-action host callback, so the resume-based approval flow is not available
  for codex (documented); adds policy-map + tests.
- changesets updated to describe the interactive-approval behavior.
Add provider-agnostic sandbox file-event hooks and a runnable demo that
uses them.

Hooks (@tanstack/ai-sandbox):
- watchWorkspace(handle, { onEvent }) + watchWithHooks(handle, hooks) emit
  typed FileEvents (create/change/delete). A native fs.watch fast-path is
  used when the provider advertises it; otherwise a portable `find -printf`
  mtime snapshot-diff poll runs (no extra deps / image changes). .git and
  node_modules are ignored by default.
- withSandboxFileEvents() middleware surfaces events into the chat() stream
  as CUSTOM `sandbox.file` events, interleaved with the agent's output.
- local-process gains the native fs.watch seam (Node recursive watch on
  Windows/macOS; Linux falls back to the poll).

Example (examples/sandbox-issue-triage):
- Fetches the first open issue on TanStack/ai, clones the repo into a
  sandbox, runs Claude Code inside it to triage the issue and write
  ISSUE-REPORT.md, reads it back via sandbox.fs, and writes a local report
  with the observed file events appended. Two entrypoints: process + docker.

Docs/skill updated; changeset added.
…M-event catalog

- Add optional in-band `cursor` to StreamChunk + `cursor` input on chat().
- Add a *replayAndResume() seam: when a cursor is supplied and a ResumeSource
  capability is provided, replay the persisted event tail; if the run is still
  running and the adapter supports re-attach, continue live. No-op without a
  resume source (a non-persisted run is unchanged).
- Move the generic LockStore + LocksCapability ('locks') into core so it is a
  single shared token across the sandbox and persistence layers.
- Add ResumeSource capability/contract and a typed catalog of well-known CUSTOM
  event names (file.changed, process.*, approval.*, artifact.*, sandbox.*).
…ttach markers

- ai-sandbox now imports LocksCapability/LockStore/InMemoryLockStore from
  @tanstack/ai and re-exports them for back-compat (one global 'locks' token).
- Add a token-identity test asserting the sandbox and core LocksCapability are
  the same object.
- Mark the four harness adapters (claude-code, codex, gemini-cli, opencode) with
  `supportsReattach = true` so the engine continues a still-running in-sandbox
  run live after replaying the persisted tail.
…andbox bridge

- @tanstack/ai-persistence: store contracts, withPersistence middleware,
  memoryPersistence, cursor utilities, approval controller, resume-source
  adapter, history projection. Fully optional; works with and without sandbox.
- @tanstack/ai-persistence-sql: one SQL store impl behind a minimal SqlDriver
  (sqlite|postgres dialect) + versioned migrations + DDL.
- Backends: -sqlite (node:sqlite/better-sqlite3), -postgres (pg), -cloudflare
  (D1, compile-verified), -drizzle and -prisma (BYO).
- @tanstack/ai-sandbox-persistence: durable SQL-backed SandboxStore +
  withPersistenceBridge wiring durable store/locks into withSandbox (agent mode).
- Track the latest in-band cursor per active run; expose getResumeState(),
  resume(), and maybeAutoResume() with an `autoResume` opt-out (default on).
- Pass `cursor` through the connection adapter's RunAgentInput payload so the
  server can replay a run. streamResponse reuses the original runId on resume.
…ip config

- New docs/persistence/overview.md (+ nav entry, addedAt) with server + client +
  agent-mode usage.
- New ai-core/persistence agent skill.
- Changeset covering the feature; knip ignore for the optional pg peer dep.
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0d1485d5-8d89-4020-b56e-ee6bc421ea69

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/persistence

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 and usage tips.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

18 package(s) bumped directly, 30 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-claude-code 0.1.0 → 1.0.0 Changeset
@tanstack/ai-codex 0.1.0 → 1.0.0 Changeset
@tanstack/ai-gemini-cli 0.1.0 → 1.0.0 Changeset
@tanstack/ai-opencode 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-cloudflare 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-drizzle 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-postgres 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-prisma 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-sql 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-sqlite 0.1.0 → 1.0.0 Changeset
@tanstack/ai-sandbox 0.1.0 → 1.0.0 Changeset
@tanstack/ai-sandbox-cloudflare 0.1.0 → 1.0.0 Changeset
@tanstack/ai-sandbox-docker 0.1.0 → 1.0.0 Changeset
@tanstack/ai-sandbox-local-process 0.1.0 → 1.0.0 Changeset
@tanstack/ai-sandbox-persistence 0.1.0 → 1.0.0 Changeset
@tanstack/ai-angular 0.1.2 → 1.0.0 Dependent
@tanstack/ai-anthropic 0.15.3 → 1.0.0 Dependent
@tanstack/ai-code-mode 0.2.7 → 1.0.0 Dependent
@tanstack/ai-code-mode-skills 0.2.7 → 1.0.0 Dependent
@tanstack/ai-elevenlabs 0.2.22 → 1.0.0 Dependent
@tanstack/ai-event-client 0.6.1 → 1.0.0 Dependent
@tanstack/ai-fal 0.8.1 → 1.0.0 Dependent
@tanstack/ai-gemini 0.16.1 → 1.0.0 Dependent
@tanstack/ai-grok 0.11.4 → 1.0.0 Dependent
@tanstack/ai-groq 0.4.4 → 1.0.0 Dependent
@tanstack/ai-isolate-node 0.1.32 → 1.0.0 Dependent
@tanstack/ai-isolate-quickjs 0.1.32 → 1.0.0 Dependent
@tanstack/ai-ollama 0.8.3 → 1.0.0 Dependent
@tanstack/ai-openai 0.14.3 → 1.0.0 Dependent
@tanstack/ai-openrouter 0.13.3 → 1.0.0 Dependent
@tanstack/ai-preact 0.9.7 → 1.0.0 Dependent
@tanstack/ai-react 0.15.7 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.8.8 → 1.0.0 Dependent
@tanstack/ai-solid 0.13.7 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.7.8 → 1.0.0 Dependent
@tanstack/ai-svelte 0.13.7 → 1.0.0 Dependent
@tanstack/ai-vue 0.13.7 → 1.0.0 Dependent
@tanstack/openai-base 0.8.3 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai 0.30.0 → 0.31.0 Changeset
@tanstack/ai-client 0.17.2 → 0.18.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-devtools-core 0.4.10 → 0.4.11 Dependent
@tanstack/ai-isolate-cloudflare 0.2.23 → 0.2.24 Dependent
@tanstack/ai-mcp 0.1.2 → 0.1.3 Dependent
@tanstack/ai-vue-ui 0.2.19 → 0.2.20 Dependent
@tanstack/preact-ai-devtools 0.1.53 → 0.1.54 Dependent
@tanstack/react-ai-devtools 0.2.53 → 0.2.54 Dependent
@tanstack/solid-ai-devtools 0.2.53 → 0.2.54 Dependent

@nx-cloud

nx-cloud Bot commented Jun 18, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 767e1c6

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

☁️ Nx Cloud last updated this comment at 2026-06-18 19:08:11 UTC

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​pg@​8.20.01001007383100
Addedpg@​8.21.0991009989100

View full report

@pkg-pr-new

pkg-pr-new Bot commented Jun 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

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

@tanstack/ai-angular

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

@tanstack/ai-anthropic

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

@tanstack/ai-claude-code

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

@tanstack/ai-client

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

@tanstack/ai-code-mode

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

@tanstack/ai-code-mode-skills

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

@tanstack/ai-codex

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

@tanstack/ai-devtools-core

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

@tanstack/ai-elevenlabs

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

@tanstack/ai-event-client

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

@tanstack/ai-fal

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

@tanstack/ai-gemini

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

@tanstack/ai-gemini-cli

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini-cli@785

@tanstack/ai-grok

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

@tanstack/ai-groq

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

@tanstack/ai-isolate-cloudflare

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

@tanstack/ai-isolate-node

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

@tanstack/ai-isolate-quickjs

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

@tanstack/ai-mcp

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

@tanstack/ai-ollama

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

@tanstack/ai-openai

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

@tanstack/ai-opencode

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

@tanstack/ai-openrouter

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

@tanstack/ai-persistence

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence@785

@tanstack/ai-persistence-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-cloudflare@785

@tanstack/ai-persistence-drizzle

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-drizzle@785

@tanstack/ai-persistence-postgres

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-postgres@785

@tanstack/ai-persistence-prisma

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-prisma@785

@tanstack/ai-persistence-sql

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-sql@785

@tanstack/ai-persistence-sqlite

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-sqlite@785

@tanstack/ai-preact

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

@tanstack/ai-react

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

@tanstack/ai-react-ui

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

@tanstack/ai-sandbox

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

@tanstack/ai-sandbox-cloudflare

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

@tanstack/ai-sandbox-docker

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

@tanstack/ai-sandbox-local-process

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

@tanstack/ai-sandbox-persistence

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-persistence@785

@tanstack/ai-solid

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

@tanstack/ai-solid-ui

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

@tanstack/ai-svelte

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

@tanstack/ai-utils

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

@tanstack/ai-vue

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

@tanstack/ai-vue-ui

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

@tanstack/openai-base

npm i https://pkg.pr.new/TanStack/ai/@tanstack/openai-base@785

@tanstack/preact-ai-devtools

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

@tanstack/react-ai-devtools

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

@tanstack/solid-ai-devtools

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

commit: 767e1c6

@willwillems

Copy link
Copy Markdown

Awesome to see that this is in progress, it was one of the final bits that prevented it from feeling completely batteries included!

AlemTuzlak added a commit that referenced this pull request Jun 26, 2026
* chore(deps): add @mcp-ui/client + ext-apps for MCP Apps support

* feat(ai): add UIResourcePart + UIResourceEvent types

* feat(ai-mcp): capture serverId + _meta.ui.resourceUri at tool discovery

* feat(ai): extend MCPToolSource with optional readResource

* feat(ai-mcp): in-memory McpSessionStore (#785-shaped seam)

* feat(ai-mcp): surface embedded ui:// resources separately from model text

* feat(ai): reconcile ui-resource CUSTOM events into UIResourcePart

* feat(ai-mcp): add public callTool method on MCPClient

* feat(ai): emit ui-resource events from MCP tool results (eager read, fail-soft)

* feat(ai-mcp): createMcpAppCallHandler + ./apps subpath (reconnect default, allowlist)

* fix(ai-mcp): type callTool return as SDK union to unblock declaration build

* feat(ai-client): createMcpAppBridge (framework-agnostic tool/prompt/link routing)

* feat(ai-react): MCPAppResource wrapper for @mcp-ui/client AppRenderer

* feat(ai-preact): MCPAppResource wrapper for @mcp-ui/client AppRenderer

* docs(skills): document MCP Apps support in ai-mcp + tool-calling skills

* docs: MCP Apps guide (server + client, React/Preact)

* fix(ai-client): handle UIResourcePart variant in MessagePart union

* fix(ai-react,ai-preact): narrow onCallTool structuredContent to satisfy CallToolResult

* test(e2e): MCP Apps static render + interactive call + allowlist rejection

* chore: add changeset and format MCP Apps files

* fix: satisfy eslint in MCP Apps source (require-await, arrow signatures)

* chore: align MCP Apps deps + knip ignore for optional @mcp-ui/client peer

* docs: make MCP Apps snippets type-check under kiira (no as-casts)

* fix(ai-client): bridge HTTP-status guard, drop dead onNotify/onIntent, warn on dropped link

* feat(ai): add toolName to UIResourcePart (AppRenderer requires the MCP tool name)

* fix(ai-mcp): pool readResource + call-handler prefix/serverId routing; drop dead extractUiResources

* fix(ai-react,ai-preact): use part.toolName, run .tsx tests, coalesce result, empty-prompt guard

* docs: correct MCP Apps skill/docs to shipped API; gpt-4o -> gpt-5.5 in tool-calling skill

* fix(ai): emit ui-resource only on uri match; reconcile to tool-call owner message

* fix(ai-mcp): drop harmful prefix-strip, pool readResource uri-ownership, sliding session TTL

* fix: openLink throw-safety, meta doc honesty, prop JSDoc parity, stronger test assertions

* docs: correct allowlist/prefix descriptions to match strip-free handler; pin ui:// exclusion test

* fix(ai-mcp): type error detail as string|undefined to satisfy no-unnecessary-condition

* ci: apply automated fixes

* feat(ai-mcp): createMcpAppCallHandler takes clients; MCPClient.getInfo + MCPClients.getServers

* test: adopt clients-based call handler + test-hygiene cleanup (remove casts, extract helpers)

* docs: clients-based createMcpAppCallHandler API; clarify sandbox proxy URL

* fix(ai-mcp): key app registry by serverId + collision throw, store->clients fallback, close e2e client, error causes

* test(ai-mcp): cover registry keying/collision, store fallback, readResource error causes

* fix(ai-mcp): getInfo/getServers retain only serializable TransportConfig (drop single-use Transport instances)

* test(ai-mcp): instance-built clients expose no reconnectable transport descriptor

* docs: multi-server interactive routing requires a per-server prefix

* ci: apply automated fixes

* fix(mcp-apps): reject malformed call args + validate openLink URL scheme

Address PR review feedback:
- createMcpAppCallHandler: reject a non-object args payload (array/primitive/
  null) instead of silently coercing it to {}; absent args still defaults to {}.
- createMcpAppBridge.openLink: only forward http(s)/mailto URLs to the host
  onLink handler; reject javascript:/data:/file:/etc. from untrusted widgets.
- docs(SKILL): point Preact readers at the @tanstack/ai-preact/mcp-apps subpath.

* fix(mcp-apps): address review — observability, fail-soft scope, type dedup

Applies the PR-review findings on the MCP Apps surface:

- processor: warn (not silently drop) a ui-resource event that resolves to
  no target message — a vanished widget is otherwise undebuggable client-side.
- call-handler: add optional onError(err, { phase, req }) so the otherwise
  opaque server handler can report 'call' and 'close' failures; library stays
  console-free.
- tool-calls: move emitCustomEvent out of the read try so an emit-path error
  can't be mislabeled as a read failure.
- pool.readResource: attach ALL per-client errors via AggregateError instead
  of last-error-wins, so the owning server's failure isn't buried.
- session-store: opportunistic expiry sweep on set() to bound growth for
  set-but-never-read threads.
- types: extract shared McpResourceReadResult (kills the hand-copied shape);
  type the processor event as UIResourceEvent['value'] and drop the as-cast;
  narrow isToolCallResponse without a cast; fix orphaned/inaccurate JSDoc and
  add a per-run mutation note on bindReadResource.
- docs: drop redundant updatedAt on the new page; document that unsafe link
  schemes are rejected even with an onLink handler.

Tests: pin the "widget never enters model input" invariant; onLink-throws
fail-soft; tool-result-still-flows on read failure; session-store sweep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: apply automated fixes

* fix(mcp-apps): onError sync-throw safety + test-quality fixes from re-review

Round-2 review of the prior fixes commit:

- call-handler: extract reportError() so onError is invoked inside the promise
  chain — a SYNCHRONOUSLY-throwing hook no longer escapes during argument
  evaluation and can't break the handler's fail-soft result (the previous
  `Promise.resolve(onError(...)).catch()` only absorbed async rejections).
- tests: cover the onError hook (phase 'call', phase 'close', and both sync-
  throw and async-reject safety) — previously untested.
- tests: drop a tautological `not.toContain('ui-resource')` assertion and
  reword the messages.ts invariant comment to claim only the load-bearing
  uri/HTML checks; reword the session-store sweep test to state honestly that
  it guards set() correctness across the sweep, not the (unobservable) memory
  reclamation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: apply automated fixes

* docs(example): add MCP Apps demo (static + interactive) to ts-react-chat

Adds a `/mcp-apps` route demonstrating both kinds of MCP Apps end to end:
- STATIC: an in-process MCP server (`api.mcp-apps-weather-server`) exposing a
  display-only `show_weather_card` tool whose `ui://weather/card` resource
  renders as a self-contained forecast card.
- INTERACTIVE: the official Three.js MCP server (@modelcontextprotocol/
  server-threejs) run on :3001, whose widget calls tools back through the
  bridge.

Wiring:
- `api.mcp-apps-chat` connects both servers via createMCPClient and streams
  `ui-resource` parts (tolerates :3001 being down).
- `api.mcp-apps-call` mounts createMcpAppCallHandler over both for the
  interactive plane.
- The page renders `ui-resource` parts with `MCPAppResource` + a
  `createMcpAppBridge`, seeds `toolInput` from the sibling tool-call part, and
  withholds the bridge for the static widget (display-only). Suggestion pills
  trigger each app.
- Vendors the official sandbox-proxy page and serves it cross-origin on :8765
  (a hard requirement of @mcp-ui/client AppRenderer); `dev` now runs the proxy,
  the Three.js server, and Vite via concurrently.

Verified: page renders with no console errors, the static MCP server route and
the Three.js server both respond, the proxy serves, and the example
type-checks. The live model->tool->widget render requires a provider API key.

* ci: apply automated fixes

* feat(example): add interactive storefront MCP App + solar-system scene

Address feedback on the /mcp-apps demo:
- Add an INTERACTIVE storefront widget (api.mcp-apps-shop-server) that
  demonstrates the full bridge round-trip: clicking "Buy now" in the sandbox
  sends a tools/call over a hand-rolled MCP Apps app-bridge -> AppRenderer ->
  createMcpAppBridge -> POST /api/mcp-apps-call -> createMcpAppCallHandler ->
  buy_product() on the server -> order confirmation rendered back in the widget.
  The widget speaks the app-bridge protocol in plain JS (no build step).
- Wire the shop server into the chat + call routes; gate the bridge on
  non-static widgets (the weather card stays display-only).
- Change the Three.js suggestion to render a solar system instead of a cube.
- Fix the tool-call note: show a check when done instead of a perpetual spinner.
- Make the weather pill name a city so the tool fires deterministically.

Verified live in the browser: static card, interactive buy round-trip
(correct server order ids, no auto-fire), and the 3D solar system all render.

* ci: apply automated fixes

* feat(ai-react,ai-preact): add useMcpAppBridge hook

A React/Preact wrapper over createMcpAppBridge that returns a stable bridge for
a given threadId/callEndpoint while always invoking the latest chat.sendMessage
and onLink (kept in refs) — removing the hand-written useMemo + exhaustive-deps
disable the example previously needed.

- Exported from the main entry of @tanstack/ai-react and @tanstack/ai-preact
  (no @mcp-ui/client needed — it only wraps the ai-client bridge); also
  re-export createMcpAppBridge / McpAppBridge / CreateMcpAppBridgeOptions.
- Unit tests cover stable identity, recreation on threadId change, latest-
  callback invocation (no stale closure), and display-only openLink.
- Use the hook in the ts-react-chat /mcp-apps example.
- Update docs/mcp/apps.md (interactive example + API reference) and the ai-mcp
  SKILL to use the hook; bump the docs updatedAt and the changeset.

* style: format useMcpAppBridge signatures

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Base automatically changed from feat/sandboxes to main June 30, 2026 14:12
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.

3 participants