@threadplane/ag-ui
v0.0.49
Published
AG-UI protocol adapter for @threadplane/chat — works with any AG-UI-compatible backend.
Maintainers
Readme
@threadplane/ag-ui
Adapter that wraps an AG-UI AbstractAgent into the runtime-neutral Agent contract from @threadplane/chat. Works with any AG-UI-compatible backend.
Part of Threadplane.
Talking to LangGraph Platform directly? See
@threadplane/langgraph— same API shape, LangGraph SDK underneath.
What it does
- Bridges any AG-UI-compatible backend into the Threadplane chat surface via
toAgent(). - Supports: LangGraph, CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, CopilotKit runtime.
- Exposes messages, status, tool calls, and raw AG-UI state as Angular Signals, plus
submit()/stop()/regenerate()actions — coverage depends on what the AG-UI backend emits. - Ships
FakeAgentandprovideFakeAgenttest doubles for unit testing without a live backend.
Install
npm install @threadplane/ag-ui @threadplane/chat @ag-ui/clientPeer dependencies: @threadplane/chat: *, @angular/core: ^20.0.0 || ^21.0.0, @ag-ui/client: *, rxjs: ~7.8.0
Quick start
Register the agent in your ApplicationConfig, then inject it into a component and bind it to <chat>.
// app.config.ts
import { provideAgent } from '@threadplane/ag-ui';
export const appConfig: ApplicationConfig = {
providers: [provideAgent({ url: 'https://your.agent.endpoint' })],
};// app.component.ts
import { Component } from '@angular/core';
import { ChatComponent } from '@threadplane/chat';
import { injectAgent } from '@threadplane/ag-ui';
@Component({
imports: [ChatComponent],
template: `<chat [agent]="agent" />`,
})
export class AppComponent {
protected readonly agent = injectAgent();
}Both @threadplane/langgraph and @threadplane/ag-ui expose provideAgent/injectAgent with the same shape — consumer code is identical regardless of which adapter is wired in.
Capabilities
toAgent() translates AG-UI events into Angular Signals on the runtime-neutral Agent contract:
| Signal | Description |
|---|---|
| messages() | Chat message history |
| status() | 'idle' \| 'running' \| 'error' |
| isLoading() | True while a run is active |
| toolCalls() | In-progress and completed tool calls |
| error() | Last run error, if any |
| state() | Raw AG-UI state snapshot |
Which capabilities populate depends on the events the AG-UI backend emits. submit(), stop(), and regenerate() are supported.
Interrupts (human-in-the-loop)
agent.interrupt() is a Signal<AgentInterrupt | undefined> populated from AG-UI CUSTOM events with name: 'on_interrupt'. The reducer JSON-parses string-serialized value payloads automatically (e.g. ag-ui-langgraph ships interrupts via dump_json_safe), so consumers see the structured object directly.
Resume with agent.submit({ resume }) — this calls runAgent({ forwardedProps: { command: { resume } } }), and the server reads forwarded_props.command.resume (the ag-ui-langgraph convention).
Pair with <chat-approval-card> from @threadplane/chat for the approve/reject/edit UX:
import { Component } from '@angular/core';
import { ChatComponent, ChatApprovalCardComponent } from '@threadplane/chat';
import { injectAgent } from '@threadplane/ag-ui';
@Component({
imports: [ChatComponent, ChatApprovalCardComponent],
template: `
<chat [agent]="agent" />
<chat-approval-card
[agent]="agent"
matchKind="refund_approval"
(action)="onAction($event)" />
`,
})
export class App {
protected readonly agent = injectAgent();
onAction(a: 'approve' | 'cancel') {
void this.agent.submit({ resume: { approved: a === 'approve' } });
}
}See cockpit/ag-ui/interrupts for a complete working example, and the LangGraph interrupts guide for the broader HITL contract — the same Agent.interrupt / submit({ resume }) API works across both adapters.
Citations
bridgeCitationsState(thread, messages) populates Message.citations from AG-UI state. Citations live under the citations key of the agent state, keyed by message ID (state.citations[messageId]).
Example state shape:
{
"state": {
"citations": {
"msg-123": [
{
"id": "src1",
"index": 1,
"title": "Example Source",
"url": "https://example.com",
"snippet": "Relevant excerpt from the source..."
}
]
}
}
}Each citation supports id, index, title, url, snippet, and custom extra fields. The message ID key matches the corresponding message in the chat history.
Testing
// Fake backend — streams canned tokens, no server:
import { provideFakeAgent } from '@threadplane/ag-ui';
providers: [provideFakeAgent({ tokens: ['Hello', ' world'] })];For component/unit tests, use the neutral writable-signal mock mockAgent()
from @threadplane/chat — the ag-ui agent is the neutral Agent contract,
so there is no adapter-specific mock. See
Choosing an adapter → Testing.
Reliability
@threadplane/ag-ui shares the same runtime-neutral Agent contract as @threadplane/langgraph, making it interchangeable at the <chat [agent]> binding. The library follows a patch-only 0.0.x release policy. The CI job "Library — lint / test / build" runs lint, test, and build on every pull request.
Documentation
License
MIT. See LICENSE.
