npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@truefoundry/assistant-ui-runtime

v0.1.37

Published

TrueFoundry Gateway agent runtime adapter for assistant-ui

Readme

@truefoundry/assistant-ui-runtime

A headless React runtime that connects assistant-ui to TrueFoundry agent sessions. Bring your own UI and server — the adapter maps sessions, turns, and streaming events onto assistant-ui's external-store runtime.

Built on top of @assistant-ui/react, so Thread, Composer, ThreadList, and tool UIs work against a familiar contract out of the box.

Checkout the Demo here


Table of contents


Installation

npm install @truefoundry/assistant-ui-runtime @assistant-ui/react
# or
pnpm add @truefoundry/assistant-ui-runtime @assistant-ui/react
# or
yarn add @truefoundry/assistant-ui-runtime @assistant-ui/react

Using the built-in TrueFoundry gateway plugin? Also install the gateway SDK:

npm install truefoundry-gateway-sdk

Peers: React ^18 || ^19, @assistant-ui/react in the host app, and an AgentChatServer implementation (plugin or your own). Bundled deps @assistant-ui/core and @assistant-ui/store are pulled in automatically.


Quick start

The fastest path is a TrueFoundry gateway server + the runtime hook + your Thread UI.

"use client";

import { AssistantRuntimeProvider } from "@assistant-ui/react";
import {
  createTrueFoundryAgentUIServer,
  useTrueFoundryAgentRuntime,
} from "@truefoundry/assistant-ui-runtime";
import { Thread } from "@/components/assistant-ui/thread";

const server = await createTrueFoundryAgentUIServer({
  apiKey: process.env.TFY_API_KEY!,
  cpURL: process.env.TFY_CP_URL!,
  // gatewayURL: process.env.TFY_GATEWAY_URL, // optional
});

export function MyAssistant() {
  const runtime = useTrueFoundryAgentRuntime({
    server,
    agentName: "support-bot",
  });

  return (
    <AssistantRuntimeProvider runtime={runtime}>
      <Thread />
    </AssistantRuntimeProvider>
  );
}

That wires streaming turns, tool approvals, ask-user prompts, MCP auth, and sub-agent nesting through the runtime.

Prefer a drop-in chat UI? Pair with @truefoundry/agent-ui-sdk (AgentChat) instead of a custom Thread.


useTrueFoundryAgentRuntime options

UseTrueFoundryAgentRuntimeOptions extends assistant-ui's ExternalStoreSharedOptions. Adapter-specific fields:

| Option | Type | Required | Description | | ------ | ---- | -------- | ----------- | | server | AgentChatServer | ✅ | Server implementation. The runtime never reads credentials itself. | | agentName | string | ✅* | Saved agent to run. *Or use agent for draft / explicit named mode. | | agent | NamedAgentConfig \| DraftAgentConfig | — | Discriminated agent source. Overrides agentName when set. | | initialSessionId | string | — | Pin an existing session once on mount (uncontrolled). | | threadId | string | — | Controlled active session id; reactive and URL-syncable. | | onThreadIdChange | (threadId: string \| undefined) => void | — | Fires when the active session changes. | | onError | (error: unknown) => void | — | Invoked on stream / load / turn errors. | | adapters | { attachments?, speech?, dictation?, voice?, feedback? } | — | Optional assistant-ui adapters forwarded to the runtime. |

Resume / pin a session

const runtime = useTrueFoundryAgentRuntime({
  server,
  agentName: "support-bot",
  initialSessionId: "ses_abc123",
});

Omit <ThreadList> if you manage session ids yourself — the session-list adapter only powers that UI. Each gateway session corresponds to one assistant-ui thread.


Agent modes

agent / agentName control how the runtime sources the agent.

| Mode | Config | Behavior | | ---- | ------ | -------- | | named (default) | agentName or agent: { mode: "named", agentName } | Runs a saved gateway agent | | draft | agent: { mode: "draft", defaultAgentSpec } | Inline mutable AgentSpec, synced via draft sessions |

// Named
const runtime = useTrueFoundryAgentRuntime({
  server,
  agentName: "support-bot",
});

// Draft
const runtime = useTrueFoundryAgentRuntime({
  server,
  agent: {
    mode: "draft",
    defaultAgentSpec: { model: { name: "gpt-4o" } },
    onAgentSpecChange: (spec) => console.log("spec updated", spec),
  },
});

Attachments

Attachments are opt-in. Wire the built-in adapter for composer file pick / previews and gateway forwarding on send.

import {
  trueFoundryAttachmentAdapter,
  useTrueFoundryAgentRuntime,
} from "@truefoundry/assistant-ui-runtime";

const runtime = useTrueFoundryAgentRuntime({
  server,
  agentName,
  adapters: { attachments: trueFoundryAttachmentAdapter },
});

Runtime extras

Typed escape hatch for adapter-specific state and actions (same pattern as @assistant-ui/react-google-adk). Use selector hooks for thread-level UI; use action hooks / trueFoundryExtras.get(aui) / getTrueFoundryExtras(aui) inside nested sub-agent renderers (PartPrimitive.Messages is readonly and shadows thread.extras — get/use and the convenience hooks walk the parent AUI chain so approvals / ask-user still hit the root runtime).

Approvals, ask-user, MCP auth

import {
  useTrueFoundryApprovals,
  useTrueFoundryToolResponses,
  useTrueFoundryMcpAuth,
} from "@truefoundry/assistant-ui-runtime";

const { pending, respond } = useTrueFoundryApprovals();
const { pending: asks, respond: answer } = useTrueFoundryToolResponses();
const { pending: mcp, resume } = useTrueFoundryMcpAuth();

Batched resume: the gateway requires every pending user.tool_approval and user.tool_response across all threads (root + sub-agents) in a single resume call. The adapter stages decisions locally and only sends when nothing is pending anywhere — partial resumes are rejected.

Hooks reference

| Hook | Returns | Description | | ---- | ------- | ----------- | | useTrueFoundryApprovals() | { pending, respond } | Pending tool approvals + respond | | useTrueFoundryToolResponses() | { pending, respond } | Pending ask-user prompts + respond | | useTrueFoundryMcpAuth() | { pending, resume } | Pending MCP OAuth + resume | | useTrueFoundryRespondToToolApproval() | (r) => void | Respond from any render context | | useTrueFoundryRespondToToolResponse() | (r) => void | Answer ask-user from any render context | | useTrueFoundryResumeMcpAuth() | () => Promise<void> | Resume after MCP OAuth | | useTrueFoundryCancel() | () => Promise<void> | Cancel the active turn | | useTrueFoundryHistoryPagination() | { hasOlderHistory, isLoadingOlderHistory, loadOlderHistory } | Scroll-up older history |

Low-level namespace

import { trueFoundryExtras, getTrueFoundryExtras } from "@truefoundry/assistant-ui-runtime";

// get/use walk Object.create(parent) AUI clients — safe inside nested sub-agent UI.
const extras = trueFoundryExtras.use();
const pending = trueFoundryExtras.use((e) => e.pendingApprovals, []);
const rootExtras = getTrueFoundryExtras(aui); // same walk as trueFoundryExtras.get

Server port (AgentChatServer)

The runtime never holds credentials. It accepts any object implementing AgentChatServer — a flat, stateless port with methods like createSession, listSessions, createTurn, etc.

First-party: use createTrueFoundryAgentUIServer (requires truefoundry-gateway-sdk). Chat-only: createTrueFoundryChatServer.

Your own backend:

import type { AgentChatServer } from "@truefoundry/assistant-ui-runtime";

const server: AgentChatServer = {
  createSession: async (req) => {
    /* … */
  },
  listSessions: async (req) => {
    /* … */
  },
  getSession: async (req) => {
    /* … */
  },
  updateSession: async (req) => {
    /* … */
  },
  createTurn: (req) => {
    /* return AsyncIterable<TurnStreamData> */
  },
  cancelSession: async (req) => {
    /* … */
  },
  listTurns: async (req) => {
    /* … */
  },
  getTurn: async (req) => {
    /* … */
  },
  listEvents: async (req) => {
    /* … */
  },
};

ListResult<T> is { data: T[]; nextPageToken?: string } — flat token-based pagination. Optional methods: deleteSession, listTurnEvents, subscribeToTurn, downloadSandboxFile.

The composed AgentUIServer can also expose an optional AgentMetricsServer under metrics. UI hosts use its getCharts, getMeters, and getChartData methods for per-agent aggregate cards and time-series charts; the runtime itself does not invoke this port.

Hosts can expose an optional PermissionsServer under permissions to return USE grants for agents and MANAGE / DELETE grants for resource ids. Omitting the port means permission-aware consumers make no request and retain their default behavior.

Without subscribeToTurn

subscribeToTurn is what lets the runtime re-attach to a turn that is still running after a refresh. When a server omits it and a session loads with a running turn, the runtime does not throw. It renders the loaded history, reports the thread as running (so your UI shows a pending indicator rather than an endless skeleton), and calls onError with a TurnResumeUnsupportedError. The turn keeps running on the backend — reload the session to pick up the result.

import { TurnResumeUnsupportedError } from "@truefoundry/assistant-ui-runtime";

const runtime = useTrueFoundryAgentRuntime({
  server,
  agentName,
  onError: (error) => {
    if (error instanceof TurnResumeUnsupportedError) {
      showResumeUnavailableDialog();
      return;
    }
    showToast(error);
  },
});

Match on error.name === "TurnResumeUnsupportedError" (exported as TURN_RESUME_UNSUPPORTED_ERROR_NAME) when you cannot import the class.


TrueFoundry agent UI server plugin

createTrueFoundryAgentUIServer builds gateway chat + Control Plane builder lists from { apiKey, cpURL, gatewayURL? }. Same bearer for CP and gateway.

import { createTrueFoundryAgentUIServer } from "@truefoundry/assistant-ui-runtime";
// or
import { createTrueFoundryAgentUIServer } from "@truefoundry/assistant-ui-runtime/plugins/truefoundry-agent-server-adapter";

const server = await createTrueFoundryAgentUIServer({
  apiKey: process.env.TFY_API_KEY!,
  cpURL: process.env.TFY_CP_URL!,
});

Chat-only escape hatch: createTrueFoundryChatServer({ apiKey, baseUrl }).

See the plugin README for gateway URL resolution, builder CP paths, Tfy* types, and host-spec extension.


Exports

| Export | Kind | Purpose | | ------ | ---- | ------- | | useTrueFoundryAgentRuntime | Hook | Root runtime — wires external-store + thread list | | createTrueFoundryAgentUIServer | Function | Full pack: gateway chat + CP builder (also via plugin subpath) | | createTrueFoundryChatServer | Function | Chat-only gateway → AgentChatServer | | trueFoundryAttachmentAdapter | Adapter | Opt-in composer attachments | | trueFoundryExtras | Namespace | Low-level extras access | | TurnResumeUnsupportedError | Class | Reported when a running turn cannot be streamed (no subscribeToTurn) | | useTrueFoundryApprovals / ToolResponses / McpAuth / … | Hooks | Pending state + actions | | AgentChatServer, AgentBuilderServer, CatalogServer, Session, Turn, … | Types | Server ports + DTOs | | TfyAgentSpec, TfySession, isTfyToolInfo, … | Types / guards | Gateway-concrete types from the plugin | | NamedAgentConfig, DraftAgentConfig | Types | Agent source discriminants |


Architecture (source map)

For contributors working inside this package. Source lives in src/; the published entry point is dist/index.js (built by tsup).

| File | Responsibility | | ---- | -------------- | | server/types.ts | AgentChatServer + AgentBuilderServer + optional catalog, sessions, schedules, metrics, and permissions ports; AgentSpec; session/turn/pagination types | | server/events.ts | Concrete turn/stream event types | | draft/ | Draft-mode helpers (mergeAgentSpec, session bridge, draft thread-list, useDraftAgentSpec) | | useTrueFoundryAgentRuntime.ts | Public hook — external-store + thread-list + extras | | useTrueFoundryAgentMessages.ts | Reactive session snapshot: load, stream, cancel, resume | | truefoundryExtras.ts / hooks.ts | Extras namespace + consumer hooks | | convertTurnMessages.ts | Pure projection from snapshot → thread messages | | foldPeerThreads.ts | Nest peer/sub-agent threads under spawning tool calls | | plugins/truefoundry-agent-server-adapter/ | Gateway chat + CP builder → AgentUIServerPort |

Invariants

  • One gateway session ⇄ one assistant-ui thread (session.id = thread remoteId).
  • Root thread id is always "main" (ROOT_THREAD_ID); sub-agents nest under their create_sub_agent tool call.
  • The runtime never holds credentials — only a pre-built AgentChatServer.
  • A paused turn's resume input must include all pending approvals + tool responses across every thread in one batch.
  • Two agent modes: named (agentName) and draft (agent: { mode: "draft", … }).

Local development

pnpm build      # tsup → dist/
pnpm test       # vitest run
pnpm typecheck  # tsc --noEmit

Unsupported assistant-ui features

| Feature | Notes | | ------- | ----- | | Attachment rendering | Forwarded on send; user bubbles show text only today | | Speech / Dictation / Voice | Pass-through only | | Feedback | Pass-through only; not persisted to the gateway | | Thread rename / archive / delete | Thread-list adapter no-ops | | Thread title generation | Returns an empty stream |


License

See LICENSE.