Page actions

Introduction

@threadplane/ag-ui is protocol-first: it consumes the AG-UI event vocabulary rather than any one runtime's SDK. That makes "any AG-UI backend plugs in" a claim that can be tested instead of asserted.

This section documents what happened when it was tested. On 2026-08-31 the adapter was run against three runtimes that have nothing to do with LangGraph, in two languages, with no adapter changes for messages, tool calls, or state. Each runtime has a standalone Angular example, a real backend, and a committed transcript of its wire traffic.

Note: What this section is not

These pages document runtimes as backends measured against the adapter. They are not a substitute for each vendor's own documentation, and Threadplane does not maintain any of the upstream AG-UI bridges described here.

How it fits

CHAT UI@threadplane/chat<chat> · <chat-message-list> · <chat-input>Agent contract · signals + events$ADAPTER@threadplane/langgraphADAPTER@threadplane/ag-uiLangGraph PlatformCrewAI · Mastra · Agent Fwk · Strands · …
The same Angular surface above the seam — these pages record what three non-LangGraph runtimes did when the backend beneath it changed.

The runtimes

Measured support

RuntimeMessagesTool callsStateInterruptsSubagents
LangGraph (via the AG-UI bridge)YesYesYesYesYes
AWS Strands (Python)YesYesPartialYesYes
Microsoft Agent Framework (Python)YesYesYesYesYes
Mastra (TypeScript)YesYesYesYesYes

Every gap in that table is caused by an upstream integration, not by the AG-UI protocol and not by a defect in @threadplane/ag-ui. The full cause analysis lives in Choosing an adapter.

What is the same everywhere

The Angular side does not change between these three runtimes. Each example binds through the same two calls, provideAgent() and injectAgent(). In your own application the provider takes the endpoint URL directly:

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideAgent } from '@threadplane/ag-ui';
 
export const appConfig: ApplicationConfig = {
  providers: [
    provideAgent({ url: '/agent' }),
  ],
};
import { Component } from '@angular/core';
import { ChatComponent } from '@threadplane/chat';
import { injectAgent } from '@threadplane/ag-ui';
 
@Component({
  selector: 'app-root',
  imports: [ChatComponent],
  template: `<chat [agent]="agent" />`,
})
export class App {
  protected readonly agent = injectAgent();
}

The examples in this repository wrap that same provideAgent() call in a factory, because they resolve their endpoint at runtime rather than hard-coding it, and each one renders a shared-state panel and an approval card around <chat>. The Mastra example also selects interruptTransport: 'mastra-command' so its decisions use the backend's command format even when native outcomes are present.

What changes is the backend, its hosting lane, and the wire conventions it happens to use. The How It Connects page for each runtime records those conventions as they were measured.

What is different everywhere

Three differences turned up repeatedly, and each runtime page returns to them.

Interrupts arrive by two different conventions. AWS Strands and Microsoft Agent Framework signal an interrupt only through the protocol-standard RUN_FINISHED outcome. The LangGraph bridge signals it only through a CUSTOM event named on_interrupt. Mastra emits both. The adapter retains both conventions in one session; native batches take precedence in auto mode regardless of arrival order.

Resume payloads are not portable. The adapter uses the observed batch and configured transport profile to shape submit({ resume }). Mastra requires mastra-command; native batches require a decision for every pending ID. The backend defines the decision payload.

Subagents now stream on every runtime measured here — AWS Strands, Microsoft Agent Framework, and Mastra. Each backend ships a small emitter that translates its native delegation signals into the protocol's SUBAGENT_* events, which @threadplane/ag-ui consumes directly. The per-runtime pages describe the emitter and link the wire capture behind each cell.

Further reading

Looking for something specific?