@threadplane/ag-ui 0.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/fesm2022/threadplane-ag-ui.mjs +749 -0
- package/fesm2022/threadplane-ag-ui.mjs.map +1 -0
- package/package.json +39 -0
- package/types/threadplane-ag-ui.d.ts +105 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# @threadplane/ag-ui
|
|
2
|
+
|
|
3
|
+
Adapter that wraps an [AG-UI](https://github.com/ag-ui-protocol/ag-ui) `AbstractAgent` into the runtime-neutral `Agent` contract from `@threadplane/chat`. Works with any AG-UI-compatible backend — LangGraph, CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, CopilotKit runtime.
|
|
4
|
+
|
|
5
|
+
Part of [Threadplane](https://github.com/cacheplane/angular-agent-framework). MIT licensed.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @threadplane/ag-ui @threadplane/chat @ag-ui/client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { provideAgUiAgent, AG_UI_AGENT } from '@threadplane/ag-ui';
|
|
17
|
+
import { ChatComponent } from '@threadplane/chat';
|
|
18
|
+
|
|
19
|
+
// app.config.ts
|
|
20
|
+
export const appConfig: ApplicationConfig = {
|
|
21
|
+
providers: [provideAgUiAgent({ url: 'https://your.agent.endpoint' })],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// component
|
|
25
|
+
@Component({
|
|
26
|
+
imports: [ChatComponent],
|
|
27
|
+
template: `<chat [agent]="agent" />`,
|
|
28
|
+
})
|
|
29
|
+
export class App {
|
|
30
|
+
protected readonly agent = inject(AG_UI_AGENT);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Citations
|
|
35
|
+
|
|
36
|
+
The `bridgeCitationsState()` function populates `Message.citations` from AG-UI STATE_DELTA events. Citations are located at JSON Pointer `/citations/{messageId}`.
|
|
37
|
+
|
|
38
|
+
### Example: AG-UI citations state shape
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"state": {
|
|
43
|
+
"citations": {
|
|
44
|
+
"msg-123": [
|
|
45
|
+
{
|
|
46
|
+
"id": "src1",
|
|
47
|
+
"index": 1,
|
|
48
|
+
"title": "Example Source",
|
|
49
|
+
"url": "https://example.com",
|
|
50
|
+
"snippet": "Relevant excerpt from the source..."
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Each citation object in the array supports `id`, `index`, `title`, `url`, `snippet`, and custom `extra` fields. The messageId key matches the corresponding message in the chat history.
|
|
59
|
+
|
|
60
|
+
## Documentation
|
|
61
|
+
|
|
62
|
+
- [Quickstart](https://threadplane.ai/docs/agent/getting-started/quickstart)
|
|
63
|
+
- [AG-UI adapter guide](https://threadplane.ai/docs/chat/guides/writing-an-adapter)
|
|
64
|
+
- [AG-UI protocol](https://github.com/ag-ui-protocol/ag-ui)
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
MIT — free for any use. See [LICENSE](../../LICENSE).
|
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
import { signal, InjectionToken, inject } from '@angular/core';
|
|
2
|
+
import { Subject, Observable } from 'rxjs';
|
|
3
|
+
import { HttpAgent, AbstractAgent, EventType } from '@ag-ui/client';
|
|
4
|
+
|
|
5
|
+
// SPDX-License-Identifier: MIT
|
|
6
|
+
// Minimal RFC-6902 JSON Patch implementation, scoped to the ops the ag-ui
|
|
7
|
+
// reducer actually receives via STATE_DELTA events: add, replace, remove,
|
|
8
|
+
// move, copy, test. Pure ESM, zero deps. Replaces a CommonJS-only third-party
|
|
9
|
+
// dependency that broke ESM-strict consumers (Vitest, Vite test envs).
|
|
10
|
+
/**
|
|
11
|
+
* Apply a sequence of JSON Patch (RFC-6902) operations to `target`. Returns a
|
|
12
|
+
* new document. The input is not mutated.
|
|
13
|
+
*
|
|
14
|
+
* Operations apply in order; if any operation fails (invalid path, failed
|
|
15
|
+
* test, etc.) the whole patch throws — matching `fast-json-patch`'s
|
|
16
|
+
* `validate: false` behavior used by the reducer.
|
|
17
|
+
*/
|
|
18
|
+
function applyPatch(target, ops) {
|
|
19
|
+
let current = target;
|
|
20
|
+
for (const op of ops) {
|
|
21
|
+
current = applyOne(current, op);
|
|
22
|
+
}
|
|
23
|
+
return current;
|
|
24
|
+
}
|
|
25
|
+
function applyOne(doc, op) {
|
|
26
|
+
switch (op.op) {
|
|
27
|
+
case 'add': return setAt(doc, parsePointer(op.path), op.value, /*replaceArrayDash*/ true);
|
|
28
|
+
case 'replace': return setAt(doc, parsePointer(op.path), op.value, /*replaceArrayDash*/ false);
|
|
29
|
+
case 'remove': return removeAt(doc, parsePointer(op.path));
|
|
30
|
+
case 'move': {
|
|
31
|
+
if (op.from == null)
|
|
32
|
+
throw new Error("'move' op requires 'from'");
|
|
33
|
+
const fromTokens = parsePointer(op.from);
|
|
34
|
+
const value = getAt(doc, fromTokens);
|
|
35
|
+
const removed = removeAt(doc, fromTokens);
|
|
36
|
+
return setAt(removed, parsePointer(op.path), value, true);
|
|
37
|
+
}
|
|
38
|
+
case 'copy': {
|
|
39
|
+
if (op.from == null)
|
|
40
|
+
throw new Error("'copy' op requires 'from'");
|
|
41
|
+
const value = getAt(doc, parsePointer(op.from));
|
|
42
|
+
return setAt(doc, parsePointer(op.path), structuredCloneSafe(value), true);
|
|
43
|
+
}
|
|
44
|
+
case 'test': {
|
|
45
|
+
const actual = getAt(doc, parsePointer(op.path));
|
|
46
|
+
if (!deepEqual(actual, op.value)) {
|
|
47
|
+
throw new Error(`'test' op failed at path ${op.path}`);
|
|
48
|
+
}
|
|
49
|
+
return doc;
|
|
50
|
+
}
|
|
51
|
+
default: {
|
|
52
|
+
const o = op;
|
|
53
|
+
throw new Error(`Unsupported JSON Patch op: ${o.op}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Parse an RFC-6901 JSON Pointer string into its tokens.
|
|
59
|
+
* "" → []
|
|
60
|
+
* "/foo/0" → ["foo", "0"]
|
|
61
|
+
* "/a~1b" → ["a/b"] (~1 → /)
|
|
62
|
+
* "/a~0b" → ["a~b"] (~0 → ~)
|
|
63
|
+
*/
|
|
64
|
+
function parsePointer(pointer) {
|
|
65
|
+
if (pointer === '')
|
|
66
|
+
return [];
|
|
67
|
+
if (!pointer.startsWith('/')) {
|
|
68
|
+
throw new Error(`Invalid JSON Pointer: ${pointer}`);
|
|
69
|
+
}
|
|
70
|
+
return pointer
|
|
71
|
+
.slice(1)
|
|
72
|
+
.split('/')
|
|
73
|
+
.map(token => token.replace(/~1/g, '/').replace(/~0/g, '~'));
|
|
74
|
+
}
|
|
75
|
+
function getAt(doc, tokens) {
|
|
76
|
+
let cur = doc;
|
|
77
|
+
for (const token of tokens) {
|
|
78
|
+
cur = stepInto(cur, token);
|
|
79
|
+
}
|
|
80
|
+
return cur;
|
|
81
|
+
}
|
|
82
|
+
function stepInto(node, token) {
|
|
83
|
+
if (Array.isArray(node)) {
|
|
84
|
+
const i = parseArrayIndex(token, node.length);
|
|
85
|
+
return node[i];
|
|
86
|
+
}
|
|
87
|
+
if (node !== null && typeof node === 'object') {
|
|
88
|
+
return node[token];
|
|
89
|
+
}
|
|
90
|
+
throw new Error(`Cannot traverse non-container at token "${token}"`);
|
|
91
|
+
}
|
|
92
|
+
function setAt(doc, tokens, value, allowArrayAppend) {
|
|
93
|
+
if (tokens.length === 0) {
|
|
94
|
+
// Replace root.
|
|
95
|
+
return structuredCloneSafe(value);
|
|
96
|
+
}
|
|
97
|
+
const [head, ...rest] = tokens;
|
|
98
|
+
if (Array.isArray(doc)) {
|
|
99
|
+
const arr = doc.slice();
|
|
100
|
+
const i = head === '-' && allowArrayAppend ? arr.length : parseArrayIndex(head, arr.length + (allowArrayAppend ? 1 : 0));
|
|
101
|
+
if (rest.length === 0) {
|
|
102
|
+
if (allowArrayAppend) {
|
|
103
|
+
// RFC-6902 add: insert at index, shifting elements right
|
|
104
|
+
arr.splice(i, 0, structuredCloneSafe(value));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// replace: overwrite at index
|
|
108
|
+
if (i >= arr.length)
|
|
109
|
+
throw new Error(`Cannot replace beyond array length at "/${tokens.join('/')}"`);
|
|
110
|
+
arr[i] = structuredCloneSafe(value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
if (i >= arr.length)
|
|
115
|
+
throw new Error(`Cannot descend into non-existent array index ${i}`);
|
|
116
|
+
arr[i] = setAt(arr[i], rest, value, allowArrayAppend);
|
|
117
|
+
}
|
|
118
|
+
return arr;
|
|
119
|
+
}
|
|
120
|
+
if (doc === null || typeof doc !== 'object') {
|
|
121
|
+
throw new Error(`Cannot descend into non-container at "/${tokens.join('/')}"`);
|
|
122
|
+
}
|
|
123
|
+
const obj = { ...doc };
|
|
124
|
+
if (rest.length === 0) {
|
|
125
|
+
obj[head] = structuredCloneSafe(value);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
if (!(head in obj)) {
|
|
129
|
+
throw new Error(`Cannot descend into missing path "/${tokens.join('/')}"`);
|
|
130
|
+
}
|
|
131
|
+
obj[head] = setAt(obj[head], rest, value, allowArrayAppend);
|
|
132
|
+
}
|
|
133
|
+
return obj;
|
|
134
|
+
}
|
|
135
|
+
function removeAt(doc, tokens) {
|
|
136
|
+
if (tokens.length === 0) {
|
|
137
|
+
throw new Error('Cannot remove root');
|
|
138
|
+
}
|
|
139
|
+
const [head, ...rest] = tokens;
|
|
140
|
+
if (Array.isArray(doc)) {
|
|
141
|
+
const arr = doc.slice();
|
|
142
|
+
const i = parseArrayIndex(head, arr.length);
|
|
143
|
+
if (i >= arr.length)
|
|
144
|
+
throw new Error(`Cannot remove non-existent array index ${i}`);
|
|
145
|
+
if (rest.length === 0) {
|
|
146
|
+
arr.splice(i, 1);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
arr[i] = removeAt(arr[i], rest);
|
|
150
|
+
}
|
|
151
|
+
return arr;
|
|
152
|
+
}
|
|
153
|
+
if (doc === null || typeof doc !== 'object') {
|
|
154
|
+
throw new Error(`Cannot remove from non-container at token "${head}"`);
|
|
155
|
+
}
|
|
156
|
+
const obj = { ...doc };
|
|
157
|
+
if (rest.length === 0) {
|
|
158
|
+
if (!(head in obj))
|
|
159
|
+
throw new Error(`Cannot remove non-existent key "${head}"`);
|
|
160
|
+
delete obj[head];
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
if (!(head in obj))
|
|
164
|
+
throw new Error(`Cannot descend into missing path "${head}"`);
|
|
165
|
+
obj[head] = removeAt(obj[head], rest);
|
|
166
|
+
}
|
|
167
|
+
return obj;
|
|
168
|
+
}
|
|
169
|
+
function parseArrayIndex(token, lengthBound) {
|
|
170
|
+
if (token === '-') {
|
|
171
|
+
// "-" is the "after-last" sentinel; only valid for `add` (handled by caller)
|
|
172
|
+
throw new Error(`Array end marker "-" not valid in this position`);
|
|
173
|
+
}
|
|
174
|
+
if (!/^(0|[1-9]\d*)$/.test(token)) {
|
|
175
|
+
throw new Error(`Invalid array index: "${token}"`);
|
|
176
|
+
}
|
|
177
|
+
const i = Number.parseInt(token, 10);
|
|
178
|
+
if (i > lengthBound) {
|
|
179
|
+
throw new Error(`Array index ${i} exceeds bound ${lengthBound}`);
|
|
180
|
+
}
|
|
181
|
+
return i;
|
|
182
|
+
}
|
|
183
|
+
function structuredCloneSafe(v) {
|
|
184
|
+
// Cheap deep clone for JSON-like values (no functions, no cycles) — matches
|
|
185
|
+
// the deep-clone the reducer was already doing pre-applyPatch with the prior
|
|
186
|
+
// dependency.
|
|
187
|
+
if (v === null || typeof v !== 'object')
|
|
188
|
+
return v;
|
|
189
|
+
return JSON.parse(JSON.stringify(v));
|
|
190
|
+
}
|
|
191
|
+
function deepEqual(a, b) {
|
|
192
|
+
if (a === b)
|
|
193
|
+
return true;
|
|
194
|
+
if (a === null || b === null)
|
|
195
|
+
return false;
|
|
196
|
+
if (typeof a !== 'object' || typeof b !== 'object')
|
|
197
|
+
return false;
|
|
198
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
199
|
+
return false;
|
|
200
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
201
|
+
if (a.length !== b.length)
|
|
202
|
+
return false;
|
|
203
|
+
for (let i = 0; i < a.length; i++) {
|
|
204
|
+
if (!deepEqual(a[i], b[i]))
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
const ao = a;
|
|
210
|
+
const bo = b;
|
|
211
|
+
const aKeys = Object.keys(ao);
|
|
212
|
+
const bKeys = Object.keys(bo);
|
|
213
|
+
if (aKeys.length !== bKeys.length)
|
|
214
|
+
return false;
|
|
215
|
+
for (const k of aKeys) {
|
|
216
|
+
if (!Object.prototype.hasOwnProperty.call(bo, k))
|
|
217
|
+
return false;
|
|
218
|
+
if (!deepEqual(ao[k], bo[k]))
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function bridgeCitationsState(thread, messages) {
|
|
225
|
+
const citationsByMsg = thread.state?.citations;
|
|
226
|
+
if (!citationsByMsg || typeof citationsByMsg !== 'object')
|
|
227
|
+
return messages;
|
|
228
|
+
const map = citationsByMsg;
|
|
229
|
+
return messages.map(msg => {
|
|
230
|
+
const raw = map[msg.id];
|
|
231
|
+
if (!Array.isArray(raw) || raw.length === 0)
|
|
232
|
+
return msg;
|
|
233
|
+
return { ...msg, citations: raw.map((entry, i) => normalizeCitation(entry, i + 1)) };
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function normalizeCitation(entry, fallbackIndex) {
|
|
237
|
+
if (typeof entry === 'string') {
|
|
238
|
+
return { id: `c${fallbackIndex}`, index: fallbackIndex, url: entry };
|
|
239
|
+
}
|
|
240
|
+
const e = (entry ?? {});
|
|
241
|
+
const str = (key) => typeof e[key] === 'string' ? e[key] : undefined;
|
|
242
|
+
const firstStr = (...keys) => {
|
|
243
|
+
for (const k of keys) {
|
|
244
|
+
const v = str(k);
|
|
245
|
+
if (v !== undefined)
|
|
246
|
+
return v;
|
|
247
|
+
}
|
|
248
|
+
return undefined;
|
|
249
|
+
};
|
|
250
|
+
return {
|
|
251
|
+
id: str('id') ?? str('refId') ?? `c${fallbackIndex}`,
|
|
252
|
+
index: typeof e['index'] === 'number' ? e['index'] : fallbackIndex,
|
|
253
|
+
title: firstStr('title', 'name'),
|
|
254
|
+
url: firstStr('url', 'href', 'source'),
|
|
255
|
+
snippet: firstStr('snippet', 'content', 'excerpt'),
|
|
256
|
+
extra: typeof e['extra'] === 'object' && e['extra'] !== null
|
|
257
|
+
? e['extra']
|
|
258
|
+
: undefined,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Per-message reasoning timing. Populated by REASONING_MESSAGE_START /
|
|
264
|
+
* REASONING_MESSAGE_END handlers. The map lives on the module — same
|
|
265
|
+
* scope as the reducer function. ReducerStore stays free of timing
|
|
266
|
+
* state; consumers read it via `Message.reasoningDurationMs` on
|
|
267
|
+
* messages that completed reasoning.
|
|
268
|
+
*
|
|
269
|
+
* Keyed by messageId. We do not need cross-thread isolation here:
|
|
270
|
+
* AG-UI's source agent recreates the reducer pipeline per session, and
|
|
271
|
+
* messageIds are unique within a session.
|
|
272
|
+
*/
|
|
273
|
+
const reasoningTimingMap = new Map();
|
|
274
|
+
function resolveReasoningDurationMs(messageId) {
|
|
275
|
+
const entry = reasoningTimingMap.get(messageId);
|
|
276
|
+
if (!entry || entry.endedAt === undefined)
|
|
277
|
+
return undefined;
|
|
278
|
+
return entry.endedAt - entry.startedAt;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Pure function: applies a single AG-UI BaseEvent to the store. Caller
|
|
282
|
+
* subscribes to source.agent() and forwards each event here. Designed
|
|
283
|
+
* for testability — no side effects beyond the supplied store.
|
|
284
|
+
*/
|
|
285
|
+
function reduceEvent(event, store) {
|
|
286
|
+
switch (event.type) {
|
|
287
|
+
case 'RUN_STARTED': {
|
|
288
|
+
store.status.set('running');
|
|
289
|
+
store.isLoading.set(true);
|
|
290
|
+
store.error.set(null);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
case 'RUN_FINISHED': {
|
|
294
|
+
store.status.set('idle');
|
|
295
|
+
store.isLoading.set(false);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
case 'RUN_ERROR': {
|
|
299
|
+
store.status.set('error');
|
|
300
|
+
store.isLoading.set(false);
|
|
301
|
+
store.error.set(event.message ?? event);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
case 'TEXT_MESSAGE_START': {
|
|
305
|
+
const id = messageIdFrom(event);
|
|
306
|
+
store.messages.update((prev) => prev.some((m) => m.id === id)
|
|
307
|
+
? prev.map((m) => m.id === id ? { ...m, content: m.content ?? '' } : m)
|
|
308
|
+
: [...prev, { id, role: 'assistant', content: '' }]);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
case 'REASONING_MESSAGE_START': {
|
|
312
|
+
const id = messageIdFrom(event);
|
|
313
|
+
reasoningTimingMap.set(id, { startedAt: Date.now() });
|
|
314
|
+
// Initialize an assistant slot with empty reasoning if it doesn't already exist.
|
|
315
|
+
store.messages.update((prev) => prev.some((m) => m.id === id)
|
|
316
|
+
? prev.map((m) => m.id === id
|
|
317
|
+
? { ...m, reasoning: m.reasoning ?? '' }
|
|
318
|
+
: m)
|
|
319
|
+
: [...prev, { id, role: 'assistant', content: '', reasoning: '' }]);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
case 'REASONING_MESSAGE_CONTENT':
|
|
323
|
+
case 'REASONING_MESSAGE_CHUNK': {
|
|
324
|
+
const id = messageIdFrom(event);
|
|
325
|
+
const delta = event.delta ?? '';
|
|
326
|
+
store.messages.update((prev) => prev.map((m) => m.id === id
|
|
327
|
+
? { ...m, reasoning: (m.reasoning ?? '') + delta }
|
|
328
|
+
: m));
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
case 'REASONING_MESSAGE_END': {
|
|
332
|
+
const id = messageIdFrom(event);
|
|
333
|
+
const entry = reasoningTimingMap.get(id);
|
|
334
|
+
if (entry) {
|
|
335
|
+
entry.endedAt = Date.now();
|
|
336
|
+
reasoningTimingMap.set(id, entry);
|
|
337
|
+
const duration = resolveReasoningDurationMs(id);
|
|
338
|
+
if (duration !== undefined) {
|
|
339
|
+
store.messages.update((prev) => prev.map((m) => m.id === id ? { ...m, reasoningDurationMs: duration } : m));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
case 'TEXT_MESSAGE_CONTENT': {
|
|
345
|
+
const id = messageIdFrom(event);
|
|
346
|
+
const delta = event.delta ?? '';
|
|
347
|
+
store.messages.update((prev) => prev.map((m) => m.id === id ? { ...m, content: m.content + delta } : m));
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
case 'TEXT_MESSAGE_END': {
|
|
351
|
+
// No-op — message is finalized by virtue of TEXT_MESSAGE_CONTENT
|
|
352
|
+
// having been applied. Reserved for future hooks.
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
case 'TOOL_CALL_START': {
|
|
356
|
+
const e = event;
|
|
357
|
+
store.toolCalls.update((prev) => [
|
|
358
|
+
...prev,
|
|
359
|
+
{ id: e.toolCallId, name: e.toolCallName, args: {}, status: 'running' },
|
|
360
|
+
]);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
case 'TOOL_CALL_ARGS': {
|
|
364
|
+
const e = event;
|
|
365
|
+
const args = safeParseArgs(e.delta);
|
|
366
|
+
store.toolCalls.update((prev) => prev.map((t) => t.id === e.toolCallId ? { ...t, args } : t));
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
case 'TOOL_CALL_END': {
|
|
370
|
+
const e = event;
|
|
371
|
+
store.toolCalls.update((prev) => prev.map((t) => t.id === e.toolCallId ? { ...t, status: 'complete' } : t));
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
case 'TOOL_CALL_RESULT': {
|
|
375
|
+
const e = event;
|
|
376
|
+
store.toolCalls.update((prev) => prev.map((t) => t.id === e.toolCallId ? { ...t, result: e.content } : t));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
case 'STATE_SNAPSHOT': {
|
|
380
|
+
const e = event;
|
|
381
|
+
const snapshot = e.snapshot ?? {};
|
|
382
|
+
store.state.set(snapshot);
|
|
383
|
+
store.messages.update(msgs => bridgeCitationsState({ state: snapshot }, msgs));
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
case 'STATE_DELTA': {
|
|
387
|
+
const e = event;
|
|
388
|
+
const next = applyPatch(deepClone(store.state()), e.delta);
|
|
389
|
+
store.state.set(next);
|
|
390
|
+
store.messages.update(msgs => bridgeCitationsState({ state: next }, msgs));
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
case 'MESSAGES_SNAPSHOT': {
|
|
394
|
+
const e = event;
|
|
395
|
+
store.messages.set(e.messages ?? []);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
case 'CUSTOM': {
|
|
399
|
+
const e = event;
|
|
400
|
+
if (e.name === 'state_update' && isRecord(e.value)) {
|
|
401
|
+
store.events$.next({ type: 'state_update', data: e.value });
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
store.events$.next({ type: 'custom', name: e.name, data: e.value });
|
|
405
|
+
}
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
default: {
|
|
409
|
+
// Unknown event types are ignored; AG-UI may add new ones in
|
|
410
|
+
// future protocol versions. We surface them as no-ops rather
|
|
411
|
+
// than throwing, so a partial-version mismatch doesn't crash.
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
function messageIdFrom(event) {
|
|
417
|
+
return event.messageId ?? 'unknown';
|
|
418
|
+
}
|
|
419
|
+
function safeParseArgs(delta) {
|
|
420
|
+
try {
|
|
421
|
+
const parsed = JSON.parse(delta);
|
|
422
|
+
return isRecord(parsed) ? parsed : {};
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
return {};
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function isRecord(v) {
|
|
429
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
430
|
+
}
|
|
431
|
+
function deepClone(v) {
|
|
432
|
+
return JSON.parse(JSON.stringify(v));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// SPDX-License-Identifier: MIT
|
|
436
|
+
function captureAgentRuntimeTelemetry(sink, event, properties) {
|
|
437
|
+
if (!sink)
|
|
438
|
+
return;
|
|
439
|
+
try {
|
|
440
|
+
void Promise.resolve(sink({ event, properties })).catch(() => undefined);
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
// Keep telemetry side effects isolated from adapter control flow.
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function agentRuntimeTelemetryErrorClass(error) {
|
|
447
|
+
if (error instanceof Error)
|
|
448
|
+
return error.name || error.constructor.name || 'Error';
|
|
449
|
+
if (error
|
|
450
|
+
&& typeof error === 'object'
|
|
451
|
+
&& 'name' in error
|
|
452
|
+
&& typeof error.name === 'string'
|
|
453
|
+
&& error.name.length > 0) {
|
|
454
|
+
return error.name;
|
|
455
|
+
}
|
|
456
|
+
return 'UnknownError';
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Wraps an AG-UI AbstractAgent into the runtime-neutral Agent contract.
|
|
460
|
+
*
|
|
461
|
+
* The adapter subscribes to source.subscribe({ onEvent }) and reduces every
|
|
462
|
+
* event into the produced Agent's signals. submit() optimistically appends the
|
|
463
|
+
* user message to both our signals and the source agent's internal message
|
|
464
|
+
* list, then calls source.runAgent(). stop() calls source.abortRun().
|
|
465
|
+
*
|
|
466
|
+
* Subscription cleanup: the returned Agent does NOT manage its own lifetime.
|
|
467
|
+
* Callers using DI should rely on the provider's destroy hook; direct callers
|
|
468
|
+
* of toAgent() should treat the returned object's lifecycle as tied to the
|
|
469
|
+
* agent instance they constructed. The subscriber registered via
|
|
470
|
+
* source.subscribe() will fire for the lifetime of source.
|
|
471
|
+
*/
|
|
472
|
+
function toAgent(source, options = {}) {
|
|
473
|
+
const store = {
|
|
474
|
+
messages: signal([]),
|
|
475
|
+
status: signal('idle'),
|
|
476
|
+
isLoading: signal(false),
|
|
477
|
+
error: signal(null),
|
|
478
|
+
toolCalls: signal([]),
|
|
479
|
+
state: signal({}),
|
|
480
|
+
events$: new Subject(),
|
|
481
|
+
};
|
|
482
|
+
const telemetryProperties = { transport: 'ag-ui', surface: 'to_agent' };
|
|
483
|
+
let activeRun = null;
|
|
484
|
+
captureAgentRuntimeTelemetry(options.telemetry, 'ngaf:runtime_instance_created', telemetryProperties);
|
|
485
|
+
function startRunTelemetry(requestType) {
|
|
486
|
+
const run = { startedAt: Date.now(), errored: false };
|
|
487
|
+
activeRun = run;
|
|
488
|
+
captureAgentRuntimeTelemetry(options.telemetry, 'ngaf:runtime_request_created', {
|
|
489
|
+
...telemetryProperties,
|
|
490
|
+
requestType,
|
|
491
|
+
});
|
|
492
|
+
captureAgentRuntimeTelemetry(options.telemetry, 'ngaf:stream_started', telemetryProperties);
|
|
493
|
+
return run;
|
|
494
|
+
}
|
|
495
|
+
function finishRunTelemetry(run) {
|
|
496
|
+
if (run.errored)
|
|
497
|
+
return;
|
|
498
|
+
captureAgentRuntimeTelemetry(options.telemetry, 'ngaf:stream_ended', {
|
|
499
|
+
...telemetryProperties,
|
|
500
|
+
durationMs: Date.now() - run.startedAt,
|
|
501
|
+
});
|
|
502
|
+
if (activeRun === run)
|
|
503
|
+
activeRun = null;
|
|
504
|
+
}
|
|
505
|
+
function failRunTelemetry(error, run = activeRun) {
|
|
506
|
+
if (!run || run.errored)
|
|
507
|
+
return;
|
|
508
|
+
run.errored = true;
|
|
509
|
+
captureAgentRuntimeTelemetry(options.telemetry, 'ngaf:stream_errored', {
|
|
510
|
+
...telemetryProperties,
|
|
511
|
+
durationMs: Date.now() - run.startedAt,
|
|
512
|
+
errorClass: agentRuntimeTelemetryErrorClass(error),
|
|
513
|
+
});
|
|
514
|
+
if (activeRun === run)
|
|
515
|
+
activeRun = null;
|
|
516
|
+
}
|
|
517
|
+
// Tap all events from the source agent via the AgentSubscriber API.
|
|
518
|
+
// This subscription lives for the lifetime of `source`.
|
|
519
|
+
source.subscribe({
|
|
520
|
+
onEvent({ event }) {
|
|
521
|
+
reduceEvent(event, store);
|
|
522
|
+
},
|
|
523
|
+
onRunFailed({ error }) {
|
|
524
|
+
store.status.set('error');
|
|
525
|
+
store.isLoading.set(false);
|
|
526
|
+
store.error.set(error);
|
|
527
|
+
failRunTelemetry(error);
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
return {
|
|
531
|
+
messages: store.messages,
|
|
532
|
+
status: store.status,
|
|
533
|
+
isLoading: store.isLoading,
|
|
534
|
+
error: store.error,
|
|
535
|
+
toolCalls: store.toolCalls,
|
|
536
|
+
state: store.state,
|
|
537
|
+
events$: store.events$.asObservable(),
|
|
538
|
+
submit: async (input, _opts) => {
|
|
539
|
+
// Optimistic append of user message to our signals and to the source
|
|
540
|
+
// agent's own message list so runAgent() sees the new message.
|
|
541
|
+
const userMsg = buildUserMessage(input);
|
|
542
|
+
if (userMsg) {
|
|
543
|
+
store.messages.update((prev) => [...prev, userMsg]);
|
|
544
|
+
// Sync to AG-UI source so it's included in the next run's input.
|
|
545
|
+
source.addMessage(userMsg);
|
|
546
|
+
}
|
|
547
|
+
const run = startRunTelemetry('submit');
|
|
548
|
+
try {
|
|
549
|
+
await source.runAgent();
|
|
550
|
+
finishRunTelemetry(run);
|
|
551
|
+
}
|
|
552
|
+
catch (err) {
|
|
553
|
+
// If the run was aborted via stop(), abortRun() resolves the promise
|
|
554
|
+
// rather than rejecting — but catch any unexpected errors here.
|
|
555
|
+
store.status.set('error');
|
|
556
|
+
store.isLoading.set(false);
|
|
557
|
+
store.error.set(err);
|
|
558
|
+
failRunTelemetry(err, run);
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
stop: async () => {
|
|
562
|
+
source.abortRun();
|
|
563
|
+
},
|
|
564
|
+
regenerate: async (assistantMessageIndex) => {
|
|
565
|
+
if (store.isLoading()) {
|
|
566
|
+
throw new Error('Cannot regenerate while agent is loading another response');
|
|
567
|
+
}
|
|
568
|
+
const msgs = store.messages();
|
|
569
|
+
const target = msgs[assistantMessageIndex];
|
|
570
|
+
if (!target || target.role !== 'assistant') {
|
|
571
|
+
throw new Error(`Message at index ${assistantMessageIndex} is not an assistant message`);
|
|
572
|
+
}
|
|
573
|
+
// Find the user message immediately preceding the target assistant message.
|
|
574
|
+
const userIdx = msgs
|
|
575
|
+
.slice(0, assistantMessageIndex)
|
|
576
|
+
.map((m, i) => ({ m, i }))
|
|
577
|
+
.reverse()
|
|
578
|
+
.find(({ m }) => m.role === 'user')?.i;
|
|
579
|
+
if (userIdx === undefined) {
|
|
580
|
+
throw new Error('No user message found before the target assistant message');
|
|
581
|
+
}
|
|
582
|
+
// Truncate local message buffer INCLUSIVE of the user message. This
|
|
583
|
+
// preserves the user message in the UI (replace-semantics) while the
|
|
584
|
+
// new assistant response streams in. The trailing user message becomes
|
|
585
|
+
// the active prompt for the next run — we must NOT re-add it.
|
|
586
|
+
const trimmed = msgs.slice(0, userIdx + 1);
|
|
587
|
+
store.messages.set(trimmed);
|
|
588
|
+
// Sync the trimmed list back to the source agent so its internal state
|
|
589
|
+
// matches what we're about to re-run. source.setMessages() replaces the
|
|
590
|
+
// agent's internal message list without appending — the trailing user
|
|
591
|
+
// message in `trimmed` becomes the active prompt for the next run.
|
|
592
|
+
source.setMessages(trimmed);
|
|
593
|
+
const run = startRunTelemetry('regenerate');
|
|
594
|
+
try {
|
|
595
|
+
await source.runAgent();
|
|
596
|
+
finishRunTelemetry(run);
|
|
597
|
+
}
|
|
598
|
+
catch (err) {
|
|
599
|
+
store.status.set('error');
|
|
600
|
+
store.isLoading.set(false);
|
|
601
|
+
store.error.set(err);
|
|
602
|
+
failRunTelemetry(err, run);
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
function buildUserMessage(input) {
|
|
608
|
+
if (input.message === undefined)
|
|
609
|
+
return undefined;
|
|
610
|
+
const content = typeof input.message === 'string'
|
|
611
|
+
? input.message
|
|
612
|
+
: input.message.map((b) => b.type === 'text' ? b.text : JSON.stringify(b)).join('');
|
|
613
|
+
return { id: randomId(), role: 'user', content };
|
|
614
|
+
}
|
|
615
|
+
function randomId() {
|
|
616
|
+
return Math.random().toString(36).slice(2);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// SPDX-License-Identifier: MIT
|
|
620
|
+
const AG_UI_AGENT = new InjectionToken('AG_UI_AGENT');
|
|
621
|
+
/**
|
|
622
|
+
* Provides an Agent instance wired through HttpAgent and toAgent.
|
|
623
|
+
* Constructs an HttpAgent from config and wraps it in the runtime-neutral
|
|
624
|
+
* Agent contract via toAgent(). Returns a provider array suitable for
|
|
625
|
+
* bootstrapApplication or TestBed.configureTestingModule().
|
|
626
|
+
*/
|
|
627
|
+
function provideAgUiAgent(config) {
|
|
628
|
+
return [
|
|
629
|
+
{
|
|
630
|
+
provide: AG_UI_AGENT,
|
|
631
|
+
useFactory: () => {
|
|
632
|
+
const source = new HttpAgent({
|
|
633
|
+
url: config.url,
|
|
634
|
+
...(config.agentId !== undefined ? { agentId: config.agentId } : {}),
|
|
635
|
+
...(config.threadId !== undefined ? { threadId: config.threadId } : {}),
|
|
636
|
+
...(config.headers !== undefined ? { headers: config.headers } : {}),
|
|
637
|
+
});
|
|
638
|
+
return toAgent(source, { telemetry: config.telemetry });
|
|
639
|
+
},
|
|
640
|
+
},
|
|
641
|
+
];
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Injects the AG_UI_AGENT from Angular's dependency injection container.
|
|
645
|
+
* Use this in components or services that have been provided via provideAgUiAgent().
|
|
646
|
+
*/
|
|
647
|
+
function injectAgUiAgent() {
|
|
648
|
+
return inject(AG_UI_AGENT);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// libs/ag-ui/src/lib/testing/fake-agent.ts
|
|
652
|
+
// SPDX-License-Identifier: MIT
|
|
653
|
+
/**
|
|
654
|
+
* In-process AG-UI agent that emits a canned streaming response.
|
|
655
|
+
*
|
|
656
|
+
* Use for offline demos and tests where a real backend isn't available.
|
|
657
|
+
* Echoes a fixed assistant reply token-by-token with realistic timing.
|
|
658
|
+
*
|
|
659
|
+
* NOT for production use.
|
|
660
|
+
*/
|
|
661
|
+
class FakeAgent extends AbstractAgent {
|
|
662
|
+
/**
|
|
663
|
+
* Tokens streamed back as the assistant reply. Override with custom
|
|
664
|
+
* tokens via the constructor for varied demo content.
|
|
665
|
+
*/
|
|
666
|
+
tokens;
|
|
667
|
+
/** Optional reasoning chunks emitted before the text reply. */
|
|
668
|
+
reasoningTokens;
|
|
669
|
+
/** Milliseconds between successive token emissions. */
|
|
670
|
+
delayMs;
|
|
671
|
+
constructor(opts = {}) {
|
|
672
|
+
super();
|
|
673
|
+
this.tokens = opts.tokens ?? [
|
|
674
|
+
'Hello', ' from', ' the', ' fake', ' AG-UI', ' agent.',
|
|
675
|
+
' This', ' is', ' a', ' canned', ' streaming', ' reply.',
|
|
676
|
+
];
|
|
677
|
+
this.reasoningTokens = opts.reasoningTokens ?? [];
|
|
678
|
+
this.delayMs = opts.delayMs ?? 60;
|
|
679
|
+
}
|
|
680
|
+
run(input) {
|
|
681
|
+
const tokens = this.tokens;
|
|
682
|
+
const reasoningTokens = this.reasoningTokens;
|
|
683
|
+
const delayMs = this.delayMs;
|
|
684
|
+
const messageId = `fake-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
685
|
+
const sequence = [
|
|
686
|
+
{ type: EventType.RUN_STARTED, threadId: input.threadId, runId: input.runId },
|
|
687
|
+
];
|
|
688
|
+
if (reasoningTokens.length > 0) {
|
|
689
|
+
sequence.push({ type: EventType.REASONING_MESSAGE_START, messageId, role: 'assistant' });
|
|
690
|
+
for (const delta of reasoningTokens) {
|
|
691
|
+
sequence.push({ type: EventType.REASONING_MESSAGE_CONTENT, messageId, delta });
|
|
692
|
+
}
|
|
693
|
+
sequence.push({ type: EventType.REASONING_MESSAGE_END, messageId });
|
|
694
|
+
}
|
|
695
|
+
sequence.push({ type: EventType.TEXT_MESSAGE_START, messageId, role: 'assistant' });
|
|
696
|
+
for (const delta of tokens) {
|
|
697
|
+
sequence.push({ type: EventType.TEXT_MESSAGE_CONTENT, messageId, delta });
|
|
698
|
+
}
|
|
699
|
+
sequence.push({ type: EventType.TEXT_MESSAGE_END, messageId });
|
|
700
|
+
sequence.push({ type: EventType.RUN_FINISHED, threadId: input.threadId, runId: input.runId });
|
|
701
|
+
return new Observable((observer) => {
|
|
702
|
+
let cancelled = false;
|
|
703
|
+
let timer;
|
|
704
|
+
let i = 0;
|
|
705
|
+
const emitNext = () => {
|
|
706
|
+
if (cancelled)
|
|
707
|
+
return;
|
|
708
|
+
if (i >= sequence.length) {
|
|
709
|
+
observer.complete();
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
observer.next(sequence[i]);
|
|
713
|
+
i++;
|
|
714
|
+
// Steady cadence except a tiny initial delay before RUN_STARTED.
|
|
715
|
+
timer = setTimeout(emitNext, delayMs);
|
|
716
|
+
};
|
|
717
|
+
timer = setTimeout(emitNext, 30);
|
|
718
|
+
return () => {
|
|
719
|
+
cancelled = true;
|
|
720
|
+
if (timer !== undefined)
|
|
721
|
+
clearTimeout(timer);
|
|
722
|
+
};
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Registers an in-process FakeAgent under AG_UI_AGENT.
|
|
729
|
+
*
|
|
730
|
+
* Use for offline demos and development. Drop-in replacement for
|
|
731
|
+
* provideAgUiAgent({ url }) when no real backend is available.
|
|
732
|
+
*/
|
|
733
|
+
function provideFakeAgUiAgent(config = {}) {
|
|
734
|
+
return [
|
|
735
|
+
{
|
|
736
|
+
provide: AG_UI_AGENT,
|
|
737
|
+
useFactory: () => toAgent(new FakeAgent(config)),
|
|
738
|
+
},
|
|
739
|
+
];
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// SPDX-License-Identifier: MIT
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Generated bundle index. Do not edit.
|
|
746
|
+
*/
|
|
747
|
+
|
|
748
|
+
export { AG_UI_AGENT, FakeAgent, bridgeCitationsState, injectAgUiAgent, provideAgUiAgent, provideFakeAgUiAgent, toAgent };
|
|
749
|
+
//# sourceMappingURL=threadplane-ag-ui.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threadplane-ag-ui.mjs","sources":["../../../../libs/ag-ui/src/lib/internal/apply-patch.ts","../../../../libs/ag-ui/src/lib/bridge-citations-state.ts","../../../../libs/ag-ui/src/lib/reducer.ts","../../../../libs/ag-ui/src/lib/to-agent.ts","../../../../libs/ag-ui/src/lib/provide-ag-ui-agent.ts","../../../../libs/ag-ui/src/lib/testing/fake-agent.ts","../../../../libs/ag-ui/src/lib/testing/provide-fake-ag-ui-agent.ts","../../../../libs/ag-ui/src/public-api.ts","../../../../libs/ag-ui/src/threadplane-ag-ui.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\n// Minimal RFC-6902 JSON Patch implementation, scoped to the ops the ag-ui\n// reducer actually receives via STATE_DELTA events: add, replace, remove,\n// move, copy, test. Pure ESM, zero deps. Replaces a CommonJS-only third-party\n// dependency that broke ESM-strict consumers (Vitest, Vite test envs).\n\nexport interface JsonPatchOp {\n readonly op: 'add' | 'replace' | 'remove' | 'move' | 'copy' | 'test';\n readonly path: string;\n readonly value?: unknown;\n readonly from?: string;\n}\n\n/**\n * Apply a sequence of JSON Patch (RFC-6902) operations to `target`. Returns a\n * new document. The input is not mutated.\n *\n * Operations apply in order; if any operation fails (invalid path, failed\n * test, etc.) the whole patch throws — matching `fast-json-patch`'s\n * `validate: false` behavior used by the reducer.\n */\nexport function applyPatch<T>(target: T, ops: readonly JsonPatchOp[]): T {\n let current: unknown = target;\n for (const op of ops) {\n current = applyOne(current, op);\n }\n return current as T;\n}\n\nfunction applyOne(doc: unknown, op: JsonPatchOp): unknown {\n switch (op.op) {\n case 'add': return setAt(doc, parsePointer(op.path), op.value, /*replaceArrayDash*/ true);\n case 'replace': return setAt(doc, parsePointer(op.path), op.value, /*replaceArrayDash*/ false);\n case 'remove': return removeAt(doc, parsePointer(op.path));\n case 'move': {\n if (op.from == null) throw new Error(\"'move' op requires 'from'\");\n const fromTokens = parsePointer(op.from);\n const value = getAt(doc, fromTokens);\n const removed = removeAt(doc, fromTokens);\n return setAt(removed, parsePointer(op.path), value, true);\n }\n case 'copy': {\n if (op.from == null) throw new Error(\"'copy' op requires 'from'\");\n const value = getAt(doc, parsePointer(op.from));\n return setAt(doc, parsePointer(op.path), structuredCloneSafe(value), true);\n }\n case 'test': {\n const actual = getAt(doc, parsePointer(op.path));\n if (!deepEqual(actual, op.value)) {\n throw new Error(`'test' op failed at path ${op.path}`);\n }\n return doc;\n }\n default: {\n const o: { op: string } = op as never;\n throw new Error(`Unsupported JSON Patch op: ${o.op}`);\n }\n }\n}\n\n/**\n * Parse an RFC-6901 JSON Pointer string into its tokens.\n * \"\" → []\n * \"/foo/0\" → [\"foo\", \"0\"]\n * \"/a~1b\" → [\"a/b\"] (~1 → /)\n * \"/a~0b\" → [\"a~b\"] (~0 → ~)\n */\nexport function parsePointer(pointer: string): string[] {\n if (pointer === '') return [];\n if (!pointer.startsWith('/')) {\n throw new Error(`Invalid JSON Pointer: ${pointer}`);\n }\n return pointer\n .slice(1)\n .split('/')\n .map(token => token.replace(/~1/g, '/').replace(/~0/g, '~'));\n}\n\nfunction getAt(doc: unknown, tokens: readonly string[]): unknown {\n let cur: unknown = doc;\n for (const token of tokens) {\n cur = stepInto(cur, token);\n }\n return cur;\n}\n\nfunction stepInto(node: unknown, token: string): unknown {\n if (Array.isArray(node)) {\n const i = parseArrayIndex(token, node.length);\n return node[i];\n }\n if (node !== null && typeof node === 'object') {\n return (node as Record<string, unknown>)[token];\n }\n throw new Error(`Cannot traverse non-container at token \"${token}\"`);\n}\n\nfunction setAt(\n doc: unknown,\n tokens: readonly string[],\n value: unknown,\n allowArrayAppend: boolean,\n): unknown {\n if (tokens.length === 0) {\n // Replace root.\n return structuredCloneSafe(value);\n }\n const [head, ...rest] = tokens;\n if (Array.isArray(doc)) {\n const arr = doc.slice();\n const i = head === '-' && allowArrayAppend ? arr.length : parseArrayIndex(head!, arr.length + (allowArrayAppend ? 1 : 0));\n if (rest.length === 0) {\n if (allowArrayAppend) {\n // RFC-6902 add: insert at index, shifting elements right\n arr.splice(i, 0, structuredCloneSafe(value));\n } else {\n // replace: overwrite at index\n if (i >= arr.length) throw new Error(`Cannot replace beyond array length at \"/${tokens.join('/')}\"`);\n arr[i] = structuredCloneSafe(value);\n }\n } else {\n if (i >= arr.length) throw new Error(`Cannot descend into non-existent array index ${i}`);\n arr[i] = setAt(arr[i], rest, value, allowArrayAppend);\n }\n return arr;\n }\n if (doc === null || typeof doc !== 'object') {\n throw new Error(`Cannot descend into non-container at \"/${tokens.join('/')}\"`);\n }\n const obj = { ...(doc as Record<string, unknown>) };\n if (rest.length === 0) {\n obj[head!] = structuredCloneSafe(value);\n } else {\n if (!(head! in obj)) {\n throw new Error(`Cannot descend into missing path \"/${tokens.join('/')}\"`);\n }\n obj[head!] = setAt(obj[head!], rest, value, allowArrayAppend);\n }\n return obj;\n}\n\nfunction removeAt(doc: unknown, tokens: readonly string[]): unknown {\n if (tokens.length === 0) {\n throw new Error('Cannot remove root');\n }\n const [head, ...rest] = tokens;\n if (Array.isArray(doc)) {\n const arr = doc.slice();\n const i = parseArrayIndex(head!, arr.length);\n if (i >= arr.length) throw new Error(`Cannot remove non-existent array index ${i}`);\n if (rest.length === 0) {\n arr.splice(i, 1);\n } else {\n arr[i] = removeAt(arr[i], rest);\n }\n return arr;\n }\n if (doc === null || typeof doc !== 'object') {\n throw new Error(`Cannot remove from non-container at token \"${head}\"`);\n }\n const obj = { ...(doc as Record<string, unknown>) };\n if (rest.length === 0) {\n if (!(head! in obj)) throw new Error(`Cannot remove non-existent key \"${head}\"`);\n delete obj[head!];\n } else {\n if (!(head! in obj)) throw new Error(`Cannot descend into missing path \"${head}\"`);\n obj[head!] = removeAt(obj[head!], rest);\n }\n return obj;\n}\n\nfunction parseArrayIndex(token: string, lengthBound: number): number {\n if (token === '-') {\n // \"-\" is the \"after-last\" sentinel; only valid for `add` (handled by caller)\n throw new Error(`Array end marker \"-\" not valid in this position`);\n }\n if (!/^(0|[1-9]\\d*)$/.test(token)) {\n throw new Error(`Invalid array index: \"${token}\"`);\n }\n const i = Number.parseInt(token, 10);\n if (i > lengthBound) {\n throw new Error(`Array index ${i} exceeds bound ${lengthBound}`);\n }\n return i;\n}\n\nfunction structuredCloneSafe<T>(v: T): T {\n // Cheap deep clone for JSON-like values (no functions, no cycles) — matches\n // the deep-clone the reducer was already doing pre-applyPatch with the prior\n // dependency.\n if (v === null || typeof v !== 'object') return v;\n return JSON.parse(JSON.stringify(v)) as T;\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n if (typeof a !== 'object' || typeof b !== 'object') return false;\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false;\n }\n return true;\n }\n const ao = a as Record<string, unknown>;\n const bo = b as Record<string, unknown>;\n const aKeys = Object.keys(ao);\n const bKeys = Object.keys(bo);\n if (aKeys.length !== bKeys.length) return false;\n for (const k of aKeys) {\n if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;\n if (!deepEqual(ao[k], bo[k])) return false;\n }\n return true;\n}\n","// libs/ag-ui/src/lib/bridge-citations-state.ts\n// SPDX-License-Identifier: MIT\nimport type { Citation, Message } from '@threadplane/chat';\n\ninterface ThreadStateLike {\n state?: Record<string, unknown>;\n}\n\nexport function bridgeCitationsState(thread: ThreadStateLike, messages: Message[]): Message[] {\n const citationsByMsg = (thread.state as { citations?: unknown })?.citations;\n if (!citationsByMsg || typeof citationsByMsg !== 'object') return messages;\n const map = citationsByMsg as Record<string, unknown>;\n return messages.map(msg => {\n const raw = map[msg.id];\n if (!Array.isArray(raw) || raw.length === 0) return msg;\n return { ...msg, citations: raw.map((entry, i) => normalizeCitation(entry, i + 1)) };\n });\n}\n\nfunction normalizeCitation(entry: unknown, fallbackIndex: number): Citation {\n if (typeof entry === 'string') {\n return { id: `c${fallbackIndex}`, index: fallbackIndex, url: entry };\n }\n const e = (entry ?? {}) as Record<string, unknown>;\n const str = (key: string): string | undefined =>\n typeof e[key] === 'string' ? (e[key] as string) : undefined;\n const firstStr = (...keys: string[]): string | undefined => {\n for (const k of keys) {\n const v = str(k);\n if (v !== undefined) return v;\n }\n return undefined;\n };\n return {\n id: str('id') ?? str('refId') ?? `c${fallbackIndex}`,\n index: typeof e['index'] === 'number' ? (e['index'] as number) : fallbackIndex,\n title: firstStr('title', 'name'),\n url: firstStr('url', 'href', 'source'),\n snippet: firstStr('snippet', 'content', 'excerpt'),\n extra:\n typeof e['extra'] === 'object' && e['extra'] !== null\n ? (e['extra'] as Record<string, unknown>)\n : undefined,\n };\n}\n","// SPDX-License-Identifier: MIT\n// @ag-ui/client@0.0.52 — EventType is a string enum with uppercase values.\n// Discriminator strings (e.g. 'RUN_STARTED') match EventType enum members\n// verbatim; the switch cases below use the string literals directly so this\n// file has no runtime dependency on the EventType enum import.\nimport type { WritableSignal } from '@angular/core';\nimport type { Subject } from 'rxjs';\nimport type {\n Message, AgentStatus, ToolCall, AgentEvent,\n} from '@threadplane/chat';\nimport type { BaseEvent } from '@ag-ui/client';\nimport { applyPatch, type JsonPatchOp } from './internal/apply-patch';\nimport { bridgeCitationsState } from './bridge-citations-state';\n\nexport interface ReducerStore {\n messages: WritableSignal<Message[]>;\n status: WritableSignal<AgentStatus>;\n isLoading: WritableSignal<boolean>;\n error: WritableSignal<unknown>;\n toolCalls: WritableSignal<ToolCall[]>;\n state: WritableSignal<Record<string, unknown>>;\n events$: Subject<AgentEvent>;\n}\n\n/**\n * Per-message reasoning timing. Populated by REASONING_MESSAGE_START /\n * REASONING_MESSAGE_END handlers. The map lives on the module — same\n * scope as the reducer function. ReducerStore stays free of timing\n * state; consumers read it via `Message.reasoningDurationMs` on\n * messages that completed reasoning.\n *\n * Keyed by messageId. We do not need cross-thread isolation here:\n * AG-UI's source agent recreates the reducer pipeline per session, and\n * messageIds are unique within a session.\n */\nconst reasoningTimingMap = new Map<string, { startedAt: number; endedAt?: number }>();\n\nfunction resolveReasoningDurationMs(messageId: string): number | undefined {\n const entry = reasoningTimingMap.get(messageId);\n if (!entry || entry.endedAt === undefined) return undefined;\n return entry.endedAt - entry.startedAt;\n}\n\n/**\n * Pure function: applies a single AG-UI BaseEvent to the store. Caller\n * subscribes to source.agent() and forwards each event here. Designed\n * for testability — no side effects beyond the supplied store.\n */\nexport function reduceEvent(event: BaseEvent, store: ReducerStore): void {\n switch (event.type) {\n case 'RUN_STARTED': {\n store.status.set('running');\n store.isLoading.set(true);\n store.error.set(null);\n return;\n }\n case 'RUN_FINISHED': {\n store.status.set('idle');\n store.isLoading.set(false);\n return;\n }\n case 'RUN_ERROR': {\n store.status.set('error');\n store.isLoading.set(false);\n store.error.set((event as { message?: unknown }).message ?? event);\n return;\n }\n case 'TEXT_MESSAGE_START': {\n const id = messageIdFrom(event);\n store.messages.update((prev) =>\n prev.some((m) => m.id === id)\n ? prev.map((m) => m.id === id ? { ...m, content: m.content ?? '' } : m)\n : [...prev, { id, role: 'assistant', content: '' }],\n );\n return;\n }\n case 'REASONING_MESSAGE_START': {\n const id = messageIdFrom(event);\n reasoningTimingMap.set(id, { startedAt: Date.now() });\n // Initialize an assistant slot with empty reasoning if it doesn't already exist.\n store.messages.update((prev) =>\n prev.some((m) => m.id === id)\n ? prev.map((m) => m.id === id\n ? { ...m, reasoning: m.reasoning ?? '' }\n : m)\n : [...prev, { id, role: 'assistant', content: '', reasoning: '' }],\n );\n return;\n }\n case 'REASONING_MESSAGE_CONTENT':\n case 'REASONING_MESSAGE_CHUNK': {\n const id = messageIdFrom(event);\n const delta = (event as { delta?: string }).delta ?? '';\n store.messages.update((prev) =>\n prev.map((m) => m.id === id\n ? { ...m, reasoning: (m.reasoning ?? '') + delta }\n : m),\n );\n return;\n }\n case 'REASONING_MESSAGE_END': {\n const id = messageIdFrom(event);\n const entry = reasoningTimingMap.get(id);\n if (entry) {\n entry.endedAt = Date.now();\n reasoningTimingMap.set(id, entry);\n const duration = resolveReasoningDurationMs(id);\n if (duration !== undefined) {\n store.messages.update((prev) =>\n prev.map((m) => m.id === id ? { ...m, reasoningDurationMs: duration } : m),\n );\n }\n }\n return;\n }\n case 'TEXT_MESSAGE_CONTENT': {\n const id = messageIdFrom(event);\n const delta = (event as { delta?: string }).delta ?? '';\n store.messages.update((prev) =>\n prev.map((m) => m.id === id ? { ...m, content: m.content + delta } : m),\n );\n return;\n }\n case 'TEXT_MESSAGE_END': {\n // No-op — message is finalized by virtue of TEXT_MESSAGE_CONTENT\n // having been applied. Reserved for future hooks.\n return;\n }\n case 'TOOL_CALL_START': {\n const e = event as unknown as { toolCallId: string; toolCallName: string };\n store.toolCalls.update((prev) => [\n ...prev,\n { id: e.toolCallId, name: e.toolCallName, args: {}, status: 'running' },\n ]);\n return;\n }\n case 'TOOL_CALL_ARGS': {\n const e = event as unknown as { toolCallId: string; delta: string };\n const args = safeParseArgs(e.delta);\n store.toolCalls.update((prev) =>\n prev.map((t) => t.id === e.toolCallId ? { ...t, args } : t),\n );\n return;\n }\n case 'TOOL_CALL_END': {\n const e = event as unknown as { toolCallId: string };\n store.toolCalls.update((prev) =>\n prev.map((t) => t.id === e.toolCallId ? { ...t, status: 'complete' } : t),\n );\n return;\n }\n case 'TOOL_CALL_RESULT': {\n const e = event as unknown as { toolCallId: string; content: unknown };\n store.toolCalls.update((prev) =>\n prev.map((t) => t.id === e.toolCallId ? { ...t, result: e.content } : t),\n );\n return;\n }\n case 'STATE_SNAPSHOT': {\n const e = event as unknown as { snapshot: Record<string, unknown> };\n const snapshot = e.snapshot ?? {};\n store.state.set(snapshot);\n store.messages.update(msgs => bridgeCitationsState({ state: snapshot }, msgs));\n return;\n }\n case 'STATE_DELTA': {\n const e = event as unknown as { delta: JsonPatchOp[] };\n const next = applyPatch(deepClone(store.state()), e.delta);\n store.state.set(next);\n store.messages.update(msgs => bridgeCitationsState({ state: next }, msgs));\n return;\n }\n case 'MESSAGES_SNAPSHOT': {\n const e = event as unknown as { messages: Message[] };\n store.messages.set(e.messages ?? []);\n return;\n }\n case 'CUSTOM': {\n const e = event as unknown as { name: string; value: unknown };\n if (e.name === 'state_update' && isRecord(e.value)) {\n store.events$.next({ type: 'state_update', data: e.value });\n } else {\n store.events$.next({ type: 'custom', name: e.name, data: e.value });\n }\n return;\n }\n default: {\n // Unknown event types are ignored; AG-UI may add new ones in\n // future protocol versions. We surface them as no-ops rather\n // than throwing, so a partial-version mismatch doesn't crash.\n return;\n }\n }\n}\n\nfunction messageIdFrom(event: BaseEvent): string {\n return (event as { messageId?: string }).messageId ?? 'unknown';\n}\n\nfunction safeParseArgs(delta: string): Record<string, unknown> {\n try {\n const parsed = JSON.parse(delta);\n return isRecord(parsed) ? parsed : {};\n } catch {\n return {};\n }\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction deepClone<T>(v: T): T {\n return JSON.parse(JSON.stringify(v));\n}\n","// SPDX-License-Identifier: MIT\nimport { signal } from '@angular/core';\nimport { Subject } from 'rxjs';\nimport type { AbstractAgent } from '@ag-ui/client';\nimport type {\n Agent, Message, AgentStatus, ToolCall, AgentEvent,\n AgentRuntimeTelemetryEvent,\n AgentRuntimeTelemetryProperties,\n AgentRuntimeTelemetrySink,\n AgentSubmitInput, AgentSubmitOptions,\n} from '@threadplane/chat';\nimport { reduceEvent, type ReducerStore } from './reducer';\n\nexport interface ToAgentOptions {\n /** Optional app-owned telemetry sink. No telemetry is emitted unless this is provided. */\n telemetry?: AgentRuntimeTelemetrySink | false;\n}\n\nfunction captureAgentRuntimeTelemetry(\n sink: AgentRuntimeTelemetrySink | false | undefined,\n event: AgentRuntimeTelemetryEvent,\n properties: AgentRuntimeTelemetryProperties,\n): void {\n if (!sink) return;\n try {\n void Promise.resolve(sink({ event, properties })).catch(() => undefined);\n } catch {\n // Keep telemetry side effects isolated from adapter control flow.\n }\n}\n\nfunction agentRuntimeTelemetryErrorClass(error: unknown): string {\n if (error instanceof Error) return error.name || error.constructor.name || 'Error';\n if (\n error\n && typeof error === 'object'\n && 'name' in error\n && typeof error.name === 'string'\n && error.name.length > 0\n ) {\n return error.name;\n }\n return 'UnknownError';\n}\n\n/**\n * Wraps an AG-UI AbstractAgent into the runtime-neutral Agent contract.\n *\n * The adapter subscribes to source.subscribe({ onEvent }) and reduces every\n * event into the produced Agent's signals. submit() optimistically appends the\n * user message to both our signals and the source agent's internal message\n * list, then calls source.runAgent(). stop() calls source.abortRun().\n *\n * Subscription cleanup: the returned Agent does NOT manage its own lifetime.\n * Callers using DI should rely on the provider's destroy hook; direct callers\n * of toAgent() should treat the returned object's lifecycle as tied to the\n * agent instance they constructed. The subscriber registered via\n * source.subscribe() will fire for the lifetime of source.\n */\nexport function toAgent(source: AbstractAgent, options: ToAgentOptions = {}): Agent {\n const store: ReducerStore = {\n messages: signal<Message[]>([]),\n status: signal<AgentStatus>('idle'),\n isLoading: signal<boolean>(false),\n error: signal<unknown>(null),\n toolCalls: signal<ToolCall[]>([]),\n state: signal<Record<string, unknown>>({}),\n events$: new Subject<AgentEvent>(),\n };\n const telemetryProperties = { transport: 'ag-ui' as const, surface: 'to_agent' };\n let activeRun: { startedAt: number; errored: boolean } | null = null;\n\n captureAgentRuntimeTelemetry(\n options.telemetry,\n 'ngaf:runtime_instance_created',\n telemetryProperties,\n );\n\n function startRunTelemetry(requestType: string): { startedAt: number; errored: boolean } {\n const run = { startedAt: Date.now(), errored: false };\n activeRun = run;\n captureAgentRuntimeTelemetry(options.telemetry, 'ngaf:runtime_request_created', {\n ...telemetryProperties,\n requestType,\n });\n captureAgentRuntimeTelemetry(options.telemetry, 'ngaf:stream_started', telemetryProperties);\n return run;\n }\n\n function finishRunTelemetry(run: { startedAt: number; errored: boolean }): void {\n if (run.errored) return;\n captureAgentRuntimeTelemetry(options.telemetry, 'ngaf:stream_ended', {\n ...telemetryProperties,\n durationMs: Date.now() - run.startedAt,\n });\n if (activeRun === run) activeRun = null;\n }\n\n function failRunTelemetry(error: unknown, run = activeRun): void {\n if (!run || run.errored) return;\n run.errored = true;\n captureAgentRuntimeTelemetry(options.telemetry, 'ngaf:stream_errored', {\n ...telemetryProperties,\n durationMs: Date.now() - run.startedAt,\n errorClass: agentRuntimeTelemetryErrorClass(error),\n });\n if (activeRun === run) activeRun = null;\n }\n\n // Tap all events from the source agent via the AgentSubscriber API.\n // This subscription lives for the lifetime of `source`.\n source.subscribe({\n onEvent({ event }) {\n reduceEvent(event, store);\n },\n onRunFailed({ error }) {\n store.status.set('error');\n store.isLoading.set(false);\n store.error.set(error);\n failRunTelemetry(error);\n },\n });\n\n return {\n messages: store.messages,\n status: store.status,\n isLoading: store.isLoading,\n error: store.error,\n toolCalls: store.toolCalls,\n state: store.state,\n events$: store.events$.asObservable(),\n\n submit: async (input: AgentSubmitInput, _opts?: AgentSubmitOptions) => {\n // Optimistic append of user message to our signals and to the source\n // agent's own message list so runAgent() sees the new message.\n const userMsg = buildUserMessage(input);\n if (userMsg) {\n store.messages.update((prev) => [...prev, userMsg]);\n // Sync to AG-UI source so it's included in the next run's input.\n source.addMessage(userMsg as Parameters<typeof source.addMessage>[0]);\n }\n\n const run = startRunTelemetry('submit');\n try {\n await source.runAgent();\n finishRunTelemetry(run);\n } catch (err) {\n // If the run was aborted via stop(), abortRun() resolves the promise\n // rather than rejecting — but catch any unexpected errors here.\n store.status.set('error');\n store.isLoading.set(false);\n store.error.set(err);\n failRunTelemetry(err, run);\n }\n },\n\n stop: async () => {\n source.abortRun();\n },\n\n regenerate: async (assistantMessageIndex: number): Promise<void> => {\n if (store.isLoading()) {\n throw new Error('Cannot regenerate while agent is loading another response');\n }\n const msgs = store.messages();\n const target = msgs[assistantMessageIndex];\n if (!target || target.role !== 'assistant') {\n throw new Error(`Message at index ${assistantMessageIndex} is not an assistant message`);\n }\n\n // Find the user message immediately preceding the target assistant message.\n const userIdx = msgs\n .slice(0, assistantMessageIndex)\n .map((m, i) => ({ m, i }))\n .reverse()\n .find(({ m }) => m.role === 'user')?.i;\n if (userIdx === undefined) {\n throw new Error('No user message found before the target assistant message');\n }\n\n // Truncate local message buffer INCLUSIVE of the user message. This\n // preserves the user message in the UI (replace-semantics) while the\n // new assistant response streams in. The trailing user message becomes\n // the active prompt for the next run — we must NOT re-add it.\n const trimmed = msgs.slice(0, userIdx + 1);\n store.messages.set(trimmed);\n\n // Sync the trimmed list back to the source agent so its internal state\n // matches what we're about to re-run. source.setMessages() replaces the\n // agent's internal message list without appending — the trailing user\n // message in `trimmed` becomes the active prompt for the next run.\n source.setMessages(trimmed as Parameters<typeof source.setMessages>[0]);\n\n const run = startRunTelemetry('regenerate');\n try {\n await source.runAgent();\n finishRunTelemetry(run);\n } catch (err) {\n store.status.set('error');\n store.isLoading.set(false);\n store.error.set(err);\n failRunTelemetry(err, run);\n }\n },\n };\n}\n\nfunction buildUserMessage(input: AgentSubmitInput): Message | undefined {\n if (input.message === undefined) return undefined;\n const content = typeof input.message === 'string'\n ? input.message\n : input.message.map((b) => b.type === 'text' ? b.text : JSON.stringify(b)).join('');\n return { id: randomId(), role: 'user', content };\n}\n\nfunction randomId(): string {\n return Math.random().toString(36).slice(2);\n}\n","// SPDX-License-Identifier: MIT\nimport { InjectionToken, inject, type Provider } from '@angular/core';\nimport { HttpAgent } from '@ag-ui/client';\nimport type { Agent, AgentRuntimeTelemetrySink } from '@threadplane/chat';\nimport { toAgent } from './to-agent';\n\n/**\n * Configuration for the AG-UI agent provider.\n * HttpAgentConfig shape (from @ag-ui/client@0.0.52):\n * - url: string (required) — endpoint for the HTTP agent\n * - agentId: string (optional) — agent identifier\n * - threadId: string (optional) — thread identifier\n * - headers: Record<string, string> (optional) — custom HTTP headers\n */\nexport interface AgUiAgentConfig {\n url: string;\n agentId?: string;\n threadId?: string;\n headers?: Record<string, string>;\n /** Optional app-owned telemetry sink. No telemetry is emitted unless this is provided. */\n telemetry?: AgentRuntimeTelemetrySink | false;\n}\n\nexport const AG_UI_AGENT = new InjectionToken<Agent>('AG_UI_AGENT');\n\n/**\n * Provides an Agent instance wired through HttpAgent and toAgent.\n * Constructs an HttpAgent from config and wraps it in the runtime-neutral\n * Agent contract via toAgent(). Returns a provider array suitable for\n * bootstrapApplication or TestBed.configureTestingModule().\n */\nexport function provideAgUiAgent(config: AgUiAgentConfig): Provider[] {\n return [\n {\n provide: AG_UI_AGENT,\n useFactory: () => {\n const source = new HttpAgent({\n url: config.url,\n ...(config.agentId !== undefined ? { agentId: config.agentId } : {}),\n ...(config.threadId !== undefined ? { threadId: config.threadId } : {}),\n ...(config.headers !== undefined ? { headers: config.headers } : {}),\n });\n return toAgent(source, { telemetry: config.telemetry });\n },\n },\n ];\n}\n\n/**\n * Injects the AG_UI_AGENT from Angular's dependency injection container.\n * Use this in components or services that have been provided via provideAgUiAgent().\n */\nexport function injectAgUiAgent(): Agent {\n return inject(AG_UI_AGENT);\n}\n","// libs/ag-ui/src/lib/testing/fake-agent.ts\n// SPDX-License-Identifier: MIT\nimport {\n AbstractAgent,\n EventType,\n type BaseEvent,\n type RunAgentInput,\n} from '@ag-ui/client';\nimport { Observable } from 'rxjs';\n\n/**\n * In-process AG-UI agent that emits a canned streaming response.\n *\n * Use for offline demos and tests where a real backend isn't available.\n * Echoes a fixed assistant reply token-by-token with realistic timing.\n *\n * NOT for production use.\n */\nexport class FakeAgent extends AbstractAgent {\n /**\n * Tokens streamed back as the assistant reply. Override with custom\n * tokens via the constructor for varied demo content.\n */\n private readonly tokens: string[];\n\n /** Optional reasoning chunks emitted before the text reply. */\n private readonly reasoningTokens: string[];\n\n /** Milliseconds between successive token emissions. */\n private readonly delayMs: number;\n\n constructor(opts: {\n tokens?: string[];\n /** Optional reasoning chunks emitted before the text reply. */\n reasoningTokens?: string[];\n delayMs?: number;\n } = {}) {\n super();\n this.tokens = opts.tokens ?? [\n 'Hello', ' from', ' the', ' fake', ' AG-UI', ' agent.',\n ' This', ' is', ' a', ' canned', ' streaming', ' reply.',\n ];\n this.reasoningTokens = opts.reasoningTokens ?? [];\n this.delayMs = opts.delayMs ?? 60;\n }\n\n run(input: RunAgentInput): Observable<BaseEvent> {\n const tokens = this.tokens;\n const reasoningTokens = this.reasoningTokens;\n const delayMs = this.delayMs;\n const messageId = `fake-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n const sequence: BaseEvent[] = [\n { type: EventType.RUN_STARTED, threadId: input.threadId, runId: input.runId } as BaseEvent,\n ];\n\n if (reasoningTokens.length > 0) {\n sequence.push({ type: EventType.REASONING_MESSAGE_START, messageId, role: 'assistant' } as BaseEvent);\n for (const delta of reasoningTokens) {\n sequence.push({ type: EventType.REASONING_MESSAGE_CONTENT, messageId, delta } as BaseEvent);\n }\n sequence.push({ type: EventType.REASONING_MESSAGE_END, messageId } as BaseEvent);\n }\n\n sequence.push({ type: EventType.TEXT_MESSAGE_START, messageId, role: 'assistant' } as BaseEvent);\n for (const delta of tokens) {\n sequence.push({ type: EventType.TEXT_MESSAGE_CONTENT, messageId, delta } as BaseEvent);\n }\n sequence.push({ type: EventType.TEXT_MESSAGE_END, messageId } as BaseEvent);\n sequence.push({ type: EventType.RUN_FINISHED, threadId: input.threadId, runId: input.runId } as BaseEvent);\n\n return new Observable<BaseEvent>((observer) => {\n let cancelled = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let i = 0;\n\n const emitNext = () => {\n if (cancelled) return;\n if (i >= sequence.length) {\n observer.complete();\n return;\n }\n observer.next(sequence[i]);\n i++;\n // Steady cadence except a tiny initial delay before RUN_STARTED.\n timer = setTimeout(emitNext, delayMs);\n };\n\n timer = setTimeout(emitNext, 30);\n\n return () => {\n cancelled = true;\n if (timer !== undefined) clearTimeout(timer);\n };\n });\n }\n}\n","// libs/ag-ui/src/lib/testing/provide-fake-ag-ui-agent.ts\n// SPDX-License-Identifier: MIT\nimport { type Provider } from '@angular/core';\nimport { AG_UI_AGENT } from '../provide-ag-ui-agent';\nimport { toAgent } from '../to-agent';\nimport { FakeAgent } from './fake-agent';\n\nexport interface FakeAgUiAgentConfig {\n /** Tokens streamed back as the assistant reply. */\n tokens?: string[];\n /** Optional reasoning chunks emitted before the text reply. */\n reasoningTokens?: string[];\n /** Milliseconds between successive token emissions. */\n delayMs?: number;\n}\n\n/**\n * Registers an in-process FakeAgent under AG_UI_AGENT.\n *\n * Use for offline demos and development. Drop-in replacement for\n * provideAgUiAgent({ url }) when no real backend is available.\n */\nexport function provideFakeAgUiAgent(config: FakeAgUiAgentConfig = {}): Provider[] {\n return [\n {\n provide: AG_UI_AGENT,\n useFactory: () => toAgent(new FakeAgent(config)),\n },\n ];\n}\n","// SPDX-License-Identifier: MIT\nexport { toAgent } from './lib/to-agent';\nexport type { ToAgentOptions } from './lib/to-agent';\nexport { provideAgUiAgent, AG_UI_AGENT, injectAgUiAgent } from './lib/provide-ag-ui-agent';\nexport type { AgUiAgentConfig } from './lib/provide-ag-ui-agent';\nexport { FakeAgent } from './lib/testing/fake-agent';\nexport { provideFakeAgUiAgent } from './lib/testing/provide-fake-ag-ui-agent';\nexport type { FakeAgUiAgentConfig } from './lib/testing/provide-fake-ag-ui-agent';\n\n// Citation state bridge — useful for advanced consumers building custom\n// reducers or merging citations from non-standard state paths.\nexport { bridgeCitationsState } from './lib/bridge-citations-state';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@threadplane/ag-ui",
|
|
3
|
+
"version": "0.0.46",
|
|
4
|
+
"peerDependencies": {
|
|
5
|
+
"@threadplane/chat": "*",
|
|
6
|
+
"@angular/core": "^20.0.0 || ^21.0.0",
|
|
7
|
+
"@ag-ui/client": "*",
|
|
8
|
+
"rxjs": "~7.8.0"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/cacheplane/angular-agent-framework.git",
|
|
14
|
+
"directory": "libs/ag-ui"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/cacheplane/angular-agent-framework#readme",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/cacheplane/angular-agent-framework/issues"
|
|
19
|
+
},
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"module": "fesm2022/threadplane-ag-ui.mjs",
|
|
22
|
+
"typings": "types/threadplane-ag-ui.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": {
|
|
25
|
+
"default": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./types/threadplane-ag-ui.d.ts",
|
|
29
|
+
"default": "./fesm2022/threadplane-ag-ui.mjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"tslib": "^2.3.0",
|
|
34
|
+
"@threadplane/telemetry": "*"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"postinstall": "threadplane-telemetry-postinstall || true"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { AbstractAgent, RunAgentInput, BaseEvent } from '@ag-ui/client';
|
|
2
|
+
import { AgentRuntimeTelemetrySink, Agent, Message } from '@threadplane/chat';
|
|
3
|
+
import { InjectionToken, Provider } from '@angular/core';
|
|
4
|
+
import { Observable } from 'rxjs';
|
|
5
|
+
|
|
6
|
+
interface ToAgentOptions {
|
|
7
|
+
/** Optional app-owned telemetry sink. No telemetry is emitted unless this is provided. */
|
|
8
|
+
telemetry?: AgentRuntimeTelemetrySink | false;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Wraps an AG-UI AbstractAgent into the runtime-neutral Agent contract.
|
|
12
|
+
*
|
|
13
|
+
* The adapter subscribes to source.subscribe({ onEvent }) and reduces every
|
|
14
|
+
* event into the produced Agent's signals. submit() optimistically appends the
|
|
15
|
+
* user message to both our signals and the source agent's internal message
|
|
16
|
+
* list, then calls source.runAgent(). stop() calls source.abortRun().
|
|
17
|
+
*
|
|
18
|
+
* Subscription cleanup: the returned Agent does NOT manage its own lifetime.
|
|
19
|
+
* Callers using DI should rely on the provider's destroy hook; direct callers
|
|
20
|
+
* of toAgent() should treat the returned object's lifecycle as tied to the
|
|
21
|
+
* agent instance they constructed. The subscriber registered via
|
|
22
|
+
* source.subscribe() will fire for the lifetime of source.
|
|
23
|
+
*/
|
|
24
|
+
declare function toAgent(source: AbstractAgent, options?: ToAgentOptions): Agent;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Configuration for the AG-UI agent provider.
|
|
28
|
+
* HttpAgentConfig shape (from @ag-ui/client@0.0.52):
|
|
29
|
+
* - url: string (required) — endpoint for the HTTP agent
|
|
30
|
+
* - agentId: string (optional) — agent identifier
|
|
31
|
+
* - threadId: string (optional) — thread identifier
|
|
32
|
+
* - headers: Record<string, string> (optional) — custom HTTP headers
|
|
33
|
+
*/
|
|
34
|
+
interface AgUiAgentConfig {
|
|
35
|
+
url: string;
|
|
36
|
+
agentId?: string;
|
|
37
|
+
threadId?: string;
|
|
38
|
+
headers?: Record<string, string>;
|
|
39
|
+
/** Optional app-owned telemetry sink. No telemetry is emitted unless this is provided. */
|
|
40
|
+
telemetry?: AgentRuntimeTelemetrySink | false;
|
|
41
|
+
}
|
|
42
|
+
declare const AG_UI_AGENT: InjectionToken<Agent>;
|
|
43
|
+
/**
|
|
44
|
+
* Provides an Agent instance wired through HttpAgent and toAgent.
|
|
45
|
+
* Constructs an HttpAgent from config and wraps it in the runtime-neutral
|
|
46
|
+
* Agent contract via toAgent(). Returns a provider array suitable for
|
|
47
|
+
* bootstrapApplication or TestBed.configureTestingModule().
|
|
48
|
+
*/
|
|
49
|
+
declare function provideAgUiAgent(config: AgUiAgentConfig): Provider[];
|
|
50
|
+
/**
|
|
51
|
+
* Injects the AG_UI_AGENT from Angular's dependency injection container.
|
|
52
|
+
* Use this in components or services that have been provided via provideAgUiAgent().
|
|
53
|
+
*/
|
|
54
|
+
declare function injectAgUiAgent(): Agent;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* In-process AG-UI agent that emits a canned streaming response.
|
|
58
|
+
*
|
|
59
|
+
* Use for offline demos and tests where a real backend isn't available.
|
|
60
|
+
* Echoes a fixed assistant reply token-by-token with realistic timing.
|
|
61
|
+
*
|
|
62
|
+
* NOT for production use.
|
|
63
|
+
*/
|
|
64
|
+
declare class FakeAgent extends AbstractAgent {
|
|
65
|
+
/**
|
|
66
|
+
* Tokens streamed back as the assistant reply. Override with custom
|
|
67
|
+
* tokens via the constructor for varied demo content.
|
|
68
|
+
*/
|
|
69
|
+
private readonly tokens;
|
|
70
|
+
/** Optional reasoning chunks emitted before the text reply. */
|
|
71
|
+
private readonly reasoningTokens;
|
|
72
|
+
/** Milliseconds between successive token emissions. */
|
|
73
|
+
private readonly delayMs;
|
|
74
|
+
constructor(opts?: {
|
|
75
|
+
tokens?: string[];
|
|
76
|
+
/** Optional reasoning chunks emitted before the text reply. */
|
|
77
|
+
reasoningTokens?: string[];
|
|
78
|
+
delayMs?: number;
|
|
79
|
+
});
|
|
80
|
+
run(input: RunAgentInput): Observable<BaseEvent>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface FakeAgUiAgentConfig {
|
|
84
|
+
/** Tokens streamed back as the assistant reply. */
|
|
85
|
+
tokens?: string[];
|
|
86
|
+
/** Optional reasoning chunks emitted before the text reply. */
|
|
87
|
+
reasoningTokens?: string[];
|
|
88
|
+
/** Milliseconds between successive token emissions. */
|
|
89
|
+
delayMs?: number;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Registers an in-process FakeAgent under AG_UI_AGENT.
|
|
93
|
+
*
|
|
94
|
+
* Use for offline demos and development. Drop-in replacement for
|
|
95
|
+
* provideAgUiAgent({ url }) when no real backend is available.
|
|
96
|
+
*/
|
|
97
|
+
declare function provideFakeAgUiAgent(config?: FakeAgUiAgentConfig): Provider[];
|
|
98
|
+
|
|
99
|
+
interface ThreadStateLike {
|
|
100
|
+
state?: Record<string, unknown>;
|
|
101
|
+
}
|
|
102
|
+
declare function bridgeCitationsState(thread: ThreadStateLike, messages: Message[]): Message[];
|
|
103
|
+
|
|
104
|
+
export { AG_UI_AGENT, FakeAgent, bridgeCitationsState, injectAgUiAgent, provideAgUiAgent, provideFakeAgUiAgent, toAgent };
|
|
105
|
+
export type { AgUiAgentConfig, FakeAgUiAgentConfig, ToAgentOptions };
|