Fake Agent
FakeAgent is an in-process AG-UI AbstractAgent for frontend work when the backend is not ready yet.
It emits a canned stream:
RUN_STARTED- optional
REASONING_MESSAGE_* TEXT_MESSAGE_START- one
TEXT_MESSAGE_CONTENTevent per token TEXT_MESSAGE_ENDRUN_FINISHED
It is for demos, story-like development, and tests. It is not a production transport.
Use the provider
For Angular apps, use provideFakeAgent().
import { ApplicationConfig } from '@angular/core';
import { provideFakeAgent } from '@threadplane/ag-ui';
export const appConfig: ApplicationConfig = {
providers: [
provideFakeAgent({
tokens: ['Hello', ' from', ' a', ' fake', ' agent.'],
delayMs: 50,
}),
],
};Your component stays the same as the real backend version:
import { Component } from '@angular/core';
import { ChatComponent } from '@threadplane/chat';
import { injectAgent } from '@threadplane/ag-ui';
@Component({
standalone: true,
imports: [ChatComponent],
template: `<chat [agent]="agent" />`,
})
export class DemoChat {
protected readonly agent = injectAgent();
}Switching to a real backend is a provider change:
- provideFakeAgent({ tokens: ['Offline', ' response.'] })
+ provideAgent({ url: '/api/agent' })Add reasoning
Pass reasoningTokens when you need to exercise reasoning UI.
provideFakeAgent({
reasoningTokens: ['Reading policy. ', 'Checking account status.'],
tokens: ['The account is eligible.'],
delayMs: 40,
});Reasoning events are emitted before text events and use the same message id, so the reducer stores reasoning and final content on one assistant message.
Use the class directly
Use FakeAgent directly when a test needs an AG-UI source rather than Angular DI.
import { FakeAgent, toAgent } from '@threadplane/ag-ui';
const source = new FakeAgent({
tokens: ['One', ' two', ' three.'],
delayMs: 1,
});
const agent = toAgent(source);That gives you the same Agent contract as provideFakeAgent().
Configuration
| Option | Type | Default | Notes |
|---|---|---|---|
tokens | string[] | A short canned greeting | Emitted as text deltas in order. |
reasoningTokens | string[] | [] | Emitted before text deltas. |
delayMs | number | 60 | Delay between events after the initial start delay. |
script | FakeAgentScript | [] | Raw AG-UI event branches that replace the canned reply, each wrapped in RUN_STARTED / RUN_FINISHED. Accepted by the constructor and by provideFakeAgent(). |
Scripting exact events
Set script when the canned text reply is not enough — tool calls, shared state, custom events, and interrupts all reach the UI through it, with no backend and no direct construction:
import { EventType, type BaseEvent } from '@ag-ui/client';
import { provideFakeAgent } from '@threadplane/ag-ui';
providers: [
provideFakeAgent({
delayMs: 0,
script: [
{
when: 'initial',
events: [
{
type: EventType.CUSTOM,
name: 'on_interrupt',
value: { kind: 'approval', amount: 42 },
} as BaseEvent,
],
},
],
}),
]when: 'initial' matches a turn whose history carries no tool result. { toolMessageFor: 'tool-1' } matches the follow-up turn whose history carries a tool result for tool-1, which is what resolving a client tool produces — so a two-branch script plays a tool call and then the reply that follows it. The first matching branch wins; when none matches, the canned token reply streams instead.
What it does not do
provideFakeAgent() does not call a model, execute tools, or persist history. It streams what you give it: a canned token reply by default, or the exact events in script.
It is deliberately small. Use it to keep UI work moving, not to validate backend behavior.
For backend integration, test against your real AG-UI endpoint and the event map in Event Mapping.