Skip to content

Commit 76067f0

Browse files
committed
feat: wire AGUIMock into config, suite, CLI, docs, and CI
Config-loader, suite, and index.ts integration. CLI flags for AG-UI recording. Docs page, homepage cards, sidebar, competitive matrix. AG-UI schema drift CI workflow on PRs and daily.
1 parent f18a5fd commit 76067f0

11 files changed

Lines changed: 520 additions & 14 deletions

File tree

.github/workflows/publish-docker.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ on:
77
pull_request:
88
branches:
99
- main
10-
workflow_dispatch:
1110

1211
env:
1312
REGISTRY: ghcr.io

.github/workflows/test-drift.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,32 @@ name: Drift Tests
22
on:
33
schedule:
44
- cron: "0 6 * * *" # Daily 6am UTC
5+
pull_request:
6+
paths:
7+
- "src/agui-types.ts"
8+
- "src/__tests__/drift/agui-schema.drift.ts"
59
workflow_dispatch: # Manual trigger
610
jobs:
11+
agui-schema-drift:
12+
runs-on: ubuntu-latest
13+
timeout-minutes: 5
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: pnpm/action-setup@v4
17+
- uses: actions/setup-node@v4
18+
with:
19+
node-version: 22
20+
cache: pnpm
21+
- run: pnpm install --frozen-lockfile
22+
23+
- name: Clone ag-ui repo
24+
run: git clone --depth 1 https://github.com/ag-ui-protocol/ag-ui.git ../ag-ui
25+
26+
- name: Run AG-UI schema drift test
27+
run: npx vitest run src/__tests__/drift/agui-schema.drift.ts --config vitest.config.drift.ts
28+
729
drift:
30+
if: github.event_name != 'pull_request'
831
runs-on: ubuntu-latest
932
timeout-minutes: 15
1033
steps:

docs/agui-mock/index.html

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>AG-UI Mock — aimock</title>
7+
<link rel="icon" type="image/svg+xml" href="../favicon.svg" />
8+
<link rel="preconnect" href="https://fonts.googleapis.com" />
9+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10+
<link
11+
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&family=Instrument+Sans:wght@400;500;600;700&display=swap"
12+
rel="stylesheet"
13+
/>
14+
<link rel="stylesheet" href="../style.css" />
15+
</head>
16+
<body>
17+
<nav class="top-nav">
18+
<div class="nav-inner">
19+
<div style="display: flex; align-items: center; gap: 1rem">
20+
<button
21+
class="sidebar-toggle"
22+
onclick="document.querySelector('.sidebar').classList.toggle('open')"
23+
aria-label="Toggle sidebar"
24+
>
25+
&#9776;
26+
</button>
27+
<a href="/" class="nav-brand"> <span class="prompt">$</span> aimock </a>
28+
</div>
29+
<ul class="nav-links">
30+
<li><a href="/">Home</a></li>
31+
<li><a href="/docs" style="color: var(--accent)">Docs</a></li>
32+
<li>
33+
<a href="https://github.com/CopilotKit/aimock" class="gh-link" target="_blank"
34+
><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
35+
<path
36+
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"
37+
/>
38+
</svg>
39+
GitHub</a
40+
>
41+
</li>
42+
</ul>
43+
</div>
44+
</nav>
45+
46+
<div id="section-bar"></div>
47+
48+
<div class="docs-layout">
49+
<aside class="sidebar" id="sidebar"></aside>
50+
51+
<main class="docs-content">
52+
<h1>AGUIMock</h1>
53+
<p class="lead">
54+
Mock the AG-UI (Agent-to-UI) protocol for CopilotKit frontend testing. Point your frontend
55+
at aimock instead of a real agent backend and get deterministic SSE event streams from
56+
fixtures.
57+
</p>
58+
59+
<h2>Quick Start</h2>
60+
<div class="code-block">
61+
<div class="code-block-header">
62+
Standalone mode <span class="lang-tag">typescript</span>
63+
</div>
64+
<pre><code><span class="kw">import</span> { AGUIMock } <span class="kw">from</span> <span class="str">"@copilotkit/aimock"</span>;
65+
66+
<span class="kw">const</span> agui = <span class="kw">new</span> AGUIMock();
67+
agui.<span class="fn">onMessage</span>(<span class="str">"hello"</span>, <span class="str">"Hi! How can I help?"</span>);
68+
agui.<span class="fn">onToolCall</span>(<span class="op">/search/</span>, <span class="str">"web_search"</span>, <span class="str">'{"q":"test"}'</span>, { <span class="prop">result</span>: <span class="str">"[]"</span> });
69+
70+
<span class="kw">const</span> url = <span class="kw">await</span> agui.<span class="fn">start</span>();
71+
<span class="cm">// POST to url with RunAgentInput body</span></code></pre>
72+
</div>
73+
74+
<h2>How It Works</h2>
75+
<ol>
76+
<li>Client sends POST with <code>RunAgentInput</code> JSON body</li>
77+
<li>AGUIMock matches the request against registered fixtures</li>
78+
<li>On match: streams back AG-UI events as SSE</li>
79+
<li>On miss with recording enabled: proxies to upstream, records events</li>
80+
<li>On miss without recording: returns 404</li>
81+
</ol>
82+
83+
<h2>Registration API</h2>
84+
<p>Fluent methods for registering fixture responses:</p>
85+
<table class="endpoint-table">
86+
<thead>
87+
<tr>
88+
<th>Method</th>
89+
<th>Match on</th>
90+
<th>Response</th>
91+
</tr>
92+
</thead>
93+
<tbody>
94+
<tr>
95+
<td><code>onMessage(pattern, text)</code></td>
96+
<td>Last user message</td>
97+
<td>Text response events</td>
98+
</tr>
99+
<tr>
100+
<td><code>onRun(pattern, events)</code></td>
101+
<td>Last user message</td>
102+
<td>Raw event sequence</td>
103+
</tr>
104+
<tr>
105+
<td><code>onToolCall(pattern, name, args, opts?)</code></td>
106+
<td>Last user message</td>
107+
<td>Tool call events</td>
108+
</tr>
109+
<tr>
110+
<td><code>onStateKey(key, snapshot)</code></td>
111+
<td>State key presence</td>
112+
<td>State snapshot</td>
113+
</tr>
114+
<tr>
115+
<td><code>onReasoning(pattern, text)</code></td>
116+
<td>Last user message</td>
117+
<td>Reasoning events</td>
118+
</tr>
119+
<tr>
120+
<td><code>onPredicate(fn, events)</code></td>
121+
<td>Custom function</td>
122+
<td>Raw event sequence</td>
123+
</tr>
124+
</tbody>
125+
</table>
126+
127+
<h2>Event Types</h2>
128+
<p>AG-UI protocol event categories:</p>
129+
<table class="endpoint-table">
130+
<thead>
131+
<tr>
132+
<th>Category</th>
133+
<th>Events</th>
134+
<th>Description</th>
135+
</tr>
136+
</thead>
137+
<tbody>
138+
<tr>
139+
<td>Lifecycle</td>
140+
<td>
141+
<code>RUN_STARTED</code>, <code>RUN_FINISHED</code>, <code>RUN_ERROR</code>,
142+
<code>STEP_STARTED</code>, <code>STEP_FINISHED</code>
143+
</td>
144+
<td>Run management</td>
145+
</tr>
146+
<tr>
147+
<td>Text</td>
148+
<td>
149+
<code>TEXT_MESSAGE_START</code>, <code>TEXT_MESSAGE_CONTENT</code>,
150+
<code>TEXT_MESSAGE_END</code>, <code>TEXT_MESSAGE_CHUNK</code>
151+
</td>
152+
<td>Streaming text</td>
153+
</tr>
154+
<tr>
155+
<td>Tool Calls</td>
156+
<td>
157+
<code>TOOL_CALL_START</code>, <code>TOOL_CALL_ARGS</code>,
158+
<code>TOOL_CALL_END</code>, <code>TOOL_CALL_RESULT</code>
159+
</td>
160+
<td>Tool execution</td>
161+
</tr>
162+
<tr>
163+
<td>State</td>
164+
<td>
165+
<code>STATE_SNAPSHOT</code>, <code>STATE_DELTA</code>,
166+
<code>MESSAGES_SNAPSHOT</code>
167+
</td>
168+
<td>Frontend state sync</td>
169+
</tr>
170+
<tr>
171+
<td>Activity</td>
172+
<td><code>ACTIVITY_SNAPSHOT</code>, <code>ACTIVITY_DELTA</code></td>
173+
<td>Progress indicators</td>
174+
</tr>
175+
<tr>
176+
<td>Reasoning</td>
177+
<td><code>REASONING_START</code>, ..., <code>REASONING_END</code></td>
178+
<td>Chain of thought</td>
179+
</tr>
180+
<tr>
181+
<td>Special</td>
182+
<td><code>RAW</code>, <code>CUSTOM</code></td>
183+
<td>Extensibility</td>
184+
</tr>
185+
</tbody>
186+
</table>
187+
188+
<h2>Event Builders</h2>
189+
<p>Convenience functions for constructing event sequences:</p>
190+
<table class="endpoint-table">
191+
<thead>
192+
<tr>
193+
<th>Builder</th>
194+
<th>Returns</th>
195+
</tr>
196+
</thead>
197+
<tbody>
198+
<tr>
199+
<td><code>buildTextResponse(text)</code></td>
200+
<td>Full text response with lifecycle</td>
201+
</tr>
202+
<tr>
203+
<td><code>buildToolCallResponse(name, args)</code></td>
204+
<td>Tool call with optional result</td>
205+
</tr>
206+
<tr>
207+
<td><code>buildStateUpdate(snapshot)</code></td>
208+
<td>State snapshot</td>
209+
</tr>
210+
<tr>
211+
<td><code>buildStateDelta(patches)</code></td>
212+
<td>JSON Patch incremental update</td>
213+
</tr>
214+
<tr>
215+
<td><code>buildErrorResponse(message)</code></td>
216+
<td>Error termination</td>
217+
</tr>
218+
<tr>
219+
<td><code>buildCompositeResponse(outputs[])</code></td>
220+
<td>Multiple builders merged</td>
221+
</tr>
222+
</tbody>
223+
</table>
224+
225+
<h2>Record &amp; Replay</h2>
226+
<p>
227+
Record mode proxies unmatched requests to an upstream agent, saves the event stream as a
228+
fixture, and replays it on subsequent matches. Proxy-only mode forwards every time without
229+
saving, ideal for demos mixing canned and live scenarios.
230+
</p>
231+
<div class="code-block">
232+
<div class="code-block-header">
233+
Recording setup <span class="lang-tag">typescript</span>
234+
</div>
235+
<pre><code><span class="kw">const</span> agui = <span class="kw">new</span> AGUIMock();
236+
agui.<span class="fn">onMessage</span>(<span class="str">"hello"</span>, <span class="str">"Hi!"</span>); <span class="cm">// known scenario</span>
237+
agui.<span class="fn">enableRecording</span>({
238+
upstream: <span class="str">"http://localhost:8000/agent"</span>,
239+
proxyOnly: <span class="kw">true</span>, <span class="cm">// false to save fixtures</span>
240+
});</code></pre>
241+
</div>
242+
243+
<h2>CLI Usage</h2>
244+
<div class="code-block">
245+
<div class="code-block-header">CLI flags <span class="lang-tag">shell</span></div>
246+
<pre><code>npx aimock --fixtures ./fixtures \
247+
--agui-record \
248+
--agui-upstream http://localhost:8000/agent</code></pre>
249+
</div>
250+
<p>
251+
Flags: <code>--agui-record</code>, <code>--agui-upstream</code>,
252+
<code>--agui-proxy-only</code>
253+
</p>
254+
255+
<h2>JSON Config</h2>
256+
<div class="code-block">
257+
<div class="code-block-header">aimock.json <span class="lang-tag">json</span></div>
258+
<pre><code>{
259+
<span class="prop">"agui"</span>: {
260+
<span class="prop">"path"</span>: <span class="str">"/agui"</span>,
261+
<span class="prop">"fixtures"</span>: [
262+
{ <span class="prop">"match"</span>: { <span class="prop">"message"</span>: <span class="str">"hello"</span> }, <span class="prop">"text"</span>: <span class="str">"Hi!"</span> }
263+
]
264+
}
265+
}</code></pre>
266+
</div>
267+
268+
<h2>Mounting</h2>
269+
<p>
270+
AGUIMock implements <code>Mountable</code> and can be mounted at any path on an LLMock
271+
server via <code>llm.mount("/agui", agui)</code>. See
272+
<a href="/mount">Mount &amp; Composition</a> for details.
273+
</p>
274+
</main>
275+
<aside class="page-toc" id="page-toc"></aside>
276+
</div>
277+
<footer class="docs-footer">
278+
<div class="footer-inner">
279+
<div class="footer-left"><span>$</span> aimock &middot; MIT License</div>
280+
<ul class="footer-links">
281+
<li><a href="https://github.com/CopilotKit/aimock" target="_blank">GitHub</a></li>
282+
<li>
283+
<a href="https://www.npmjs.com/package/@copilotkit/aimock" target="_blank">npm</a>
284+
</li>
285+
</ul>
286+
</div>
287+
</footer>
288+
<script src="../sidebar.js"></script>
289+
<script src="../cli-tabs.js"></script>
290+
</body>
291+
</html>

docs/docs/index.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@
8484
.section-pill.pill-gray {
8585
border-left: 3px solid var(--text-dim);
8686
}
87+
.section-pill.pill-cyan {
88+
border-left: 3px solid #22d3ee;
89+
}
8790
.section-pill-icon {
8891
font-size: 0.85rem;
8992
line-height: 1;
@@ -275,6 +278,9 @@
275278
<a href="/a2a-mock" class="section-pill pill-purple">
276279
<span class="section-pill-icon">&#129309;</span> A2A Protocol
277280
</a>
281+
<a href="/agui-mock" class="section-pill pill-cyan">
282+
<span class="section-pill-icon">&#128421;</span> AG-UI
283+
</a>
278284
<a href="/vector-mock" class="section-pill pill-amber">
279285
<span class="section-pill-icon">&#128230;</span> Vector DBs
280286
</a>
@@ -351,6 +357,21 @@ <h2>The Suite</h2>
351357
<a href="/a2a-mock" class="suite-card-cta">Get started <span>&rarr;</span></a>
352358
</div>
353359

360+
<!-- AG-UI Protocol -->
361+
<div class="suite-card border-purple">
362+
<div class="suite-card-header">
363+
<span class="suite-card-icon">&#129502;</span>
364+
<span class="suite-card-title">AG-UI Protocol</span>
365+
</div>
366+
<p class="suite-card-desc">Mock agent-to-UI event streams for frontend testing</p>
367+
<ul class="suite-card-links">
368+
<li><a href="/agui-mock">Event types</a></li>
369+
<li><a href="/agui-mock">Builders</a></li>
370+
<li><a href="/agui-mock">Record &amp; replay</a></li>
371+
</ul>
372+
<a href="/agui-mock" class="suite-card-cta">Get started <span>&rarr;</span></a>
373+
</div>
374+
354375
<!-- Vector Databases -->
355376
<div class="suite-card border-amber">
356377
<div class="suite-card-header">

0 commit comments

Comments
 (0)