@dudousxd/nestjs-agent-core 0.34.0 → 0.35.0

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.
@@ -0,0 +1,1025 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/ag-ui/interrupt-id.ts
5
+ var PREFIX = "agora_";
6
+ function encodeInterruptId(address) {
7
+ const compact = [
8
+ address.kind === "approval" ? "a" : "e",
9
+ address.parked,
10
+ address.stream,
11
+ address.toolCallId,
12
+ address.position
13
+ ];
14
+ return PREFIX + Buffer.from(JSON.stringify(compact), "utf8").toString("base64url");
15
+ }
16
+ __name(encodeInterruptId, "encodeInterruptId");
17
+ function decodeInterruptId(id) {
18
+ if (typeof id !== "string" || !id.startsWith(PREFIX)) return null;
19
+ try {
20
+ const parsed = JSON.parse(Buffer.from(id.slice(PREFIX.length), "base64url").toString("utf8"));
21
+ if (!Array.isArray(parsed) || parsed.length !== 5) return null;
22
+ const [kind, parked, stream, toolCallId, position] = parsed;
23
+ if (kind !== "a" && kind !== "e" || typeof parked !== "string" || typeof stream !== "string" || typeof toolCallId !== "string" || typeof position !== "number" || !Number.isSafeInteger(position) || position < 0) {
24
+ return null;
25
+ }
26
+ return {
27
+ kind: kind === "a" ? "approval" : "elicitation",
28
+ parked,
29
+ stream,
30
+ toolCallId,
31
+ position
32
+ };
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+ __name(decodeInterruptId, "decodeInterruptId");
38
+
39
+ // src/ag-ui/types.ts
40
+ var AG_UI_PROTOCOL_VERSION = "1.0";
41
+ var AG_UI_CUSTOM = {
42
+ /** `{ runId, threadId }` — the library's own ids for the run behind this stream (cancel, re-attach). */
43
+ run: "agora.run",
44
+ /** A generative-UI component (`AgentUiComponent`); a repeat `id` replaces. */
45
+ ui: "agora.ui",
46
+ title: "agora.title",
47
+ queue: "agora.queue",
48
+ /** The approval request as the native protocol carries it, for a consumer that renders it live. */
49
+ approvalRequested: "agora.approval-requested",
50
+ approvalSettled: "agora.approval-settled",
51
+ /** The whole question set of an elicitation (`ElicitationRequest`). */
52
+ elicitation: "agora.elicitation",
53
+ /** `{ usage, costUsd, reasoningMs }` of one model step. */
54
+ stepUsage: "agora.step-usage",
55
+ /** `{ message }` — input material this producer could not use and dropped. */
56
+ warning: "agora.warning"
57
+ };
58
+
59
+ // src/ag-ui/encoder.ts
60
+ var APPROVAL_SCHEMA = {
61
+ type: "object",
62
+ properties: {
63
+ approved: {
64
+ type: "boolean"
65
+ },
66
+ reason: {
67
+ type: "string"
68
+ },
69
+ remember: {
70
+ type: "boolean"
71
+ }
72
+ },
73
+ required: [
74
+ "approved"
75
+ ]
76
+ };
77
+ function elicitationSchema(request) {
78
+ const properties = {};
79
+ for (const question of request.questions) {
80
+ const options = question.options?.map((option) => option.value);
81
+ properties[question.id] = {
82
+ type: "array",
83
+ title: question.prompt,
84
+ ...question.description !== void 0 ? {
85
+ description: question.description
86
+ } : {},
87
+ items: options !== void 0 && options.length > 0 && question.allowFreeText !== true ? {
88
+ type: "string",
89
+ enum: options
90
+ } : {
91
+ type: "string"
92
+ },
93
+ ...question.multiple === true ? {} : {
94
+ maxItems: 1
95
+ },
96
+ ...question.defaults !== void 0 ? {
97
+ default: question.defaults
98
+ } : {}
99
+ };
100
+ }
101
+ return {
102
+ type: "object",
103
+ properties: {
104
+ answers: {
105
+ type: "object",
106
+ properties
107
+ }
108
+ },
109
+ required: [
110
+ "answers"
111
+ ]
112
+ };
113
+ }
114
+ __name(elicitationSchema, "elicitationSchema");
115
+ function text(value) {
116
+ if (typeof value === "string") return value;
117
+ const json = JSON.stringify(value);
118
+ return json === void 0 ? "" : json;
119
+ }
120
+ __name(text, "text");
121
+ var AgUiEncoder = class {
122
+ static {
123
+ __name(this, "AgUiEncoder");
124
+ }
125
+ options;
126
+ position = 0;
127
+ started = false;
128
+ closed = false;
129
+ cancelled = false;
130
+ crossed = false;
131
+ messageSeq = 0;
132
+ stepSeq = 0;
133
+ openText;
134
+ openReasoning;
135
+ /** The last text message of the current step: a tool call attaches to it. */
136
+ stepMessage;
137
+ openSteps = [];
138
+ calls = /* @__PURE__ */ new Map();
139
+ pending = /* @__PURE__ */ new Map();
140
+ usage = /* @__PURE__ */ new Map();
141
+ constructor(options) {
142
+ this.options = options;
143
+ }
144
+ /** How many frames of the library stream this encoder has consumed. */
145
+ get consumed() {
146
+ return this.position;
147
+ }
148
+ /** The run is waiting on at least one approval or question set nobody has settled yet. */
149
+ get waiting() {
150
+ return this.pending.size > 0;
151
+ }
152
+ /**
153
+ * Is anything still in flight that is NOT waiting on a person — a tool call announced and neither
154
+ * answered nor parked? While there is, more frames are coming without anyone's help.
155
+ */
156
+ get busy() {
157
+ for (const [id, call] of this.calls) {
158
+ if (!call.answered && !this.pending.has(id)) return true;
159
+ }
160
+ return false;
161
+ }
162
+ /** `RUN_STARTED`, and the library's own ids for the run. Call once, before the first frame. */
163
+ start() {
164
+ if (this.started) return [];
165
+ this.started = true;
166
+ return [
167
+ {
168
+ type: "RUN_STARTED",
169
+ threadId: this.options.threadId,
170
+ runId: this.options.runId,
171
+ protocolVersion: AG_UI_PROTOCOL_VERSION
172
+ },
173
+ {
174
+ type: "CUSTOM",
175
+ name: AG_UI_CUSTOM.run,
176
+ value: {
177
+ runId: this.options.streamRunId,
178
+ threadId: this.options.streamThreadId ?? this.options.threadId
179
+ }
180
+ }
181
+ ];
182
+ }
183
+ /** The AG-UI events the next frame of the library stream stands for (possibly none). */
184
+ encode(frame) {
185
+ const position = this.position;
186
+ this.position += 1;
187
+ if (this.closed) return [];
188
+ const skip = this.options.skip ?? 0;
189
+ if (!this.crossed && position >= skip) this.crossBoundary();
190
+ const events = this.project(frame, position);
191
+ if (position < skip) {
192
+ if (this.position >= skip) this.crossBoundary();
193
+ return [];
194
+ }
195
+ return events;
196
+ }
197
+ /**
198
+ * The replayed frames are behind us: from here on events are written. The AG-UI run that delivered
199
+ * them closed everything it had open before it finished, so this run starts with nothing open, and
200
+ * what the resuming request answered is no longer waiting.
201
+ */
202
+ crossBoundary() {
203
+ this.crossed = true;
204
+ this.openText = void 0;
205
+ this.openReasoning = void 0;
206
+ this.stepMessage = void 0;
207
+ this.openSteps.length = 0;
208
+ for (const call of this.calls.values()) {
209
+ call.started = false;
210
+ call.ended = true;
211
+ }
212
+ for (const id of this.options.answered ?? []) this.pending.delete(id);
213
+ }
214
+ get replaying() {
215
+ return this.position <= (this.options.skip ?? 0);
216
+ }
217
+ project(event, position) {
218
+ switch (event.kind) {
219
+ case "error": {
220
+ this.closed = true;
221
+ const usage = this.usageEntries();
222
+ return [
223
+ {
224
+ type: "RUN_ERROR",
225
+ message: event.message,
226
+ code: event.code,
227
+ ...usage.length > 0 ? {
228
+ usage
229
+ } : {}
230
+ }
231
+ ];
232
+ }
233
+ case "ui":
234
+ return [
235
+ this.custom(AG_UI_CUSTOM.ui, {
236
+ id: event.id ?? `ui:${position}`,
237
+ component: event.component,
238
+ props: event.props,
239
+ ...event.version !== void 0 ? {
240
+ version: event.version
241
+ } : {},
242
+ ...event.toolCallId !== void 0 ? {
243
+ toolCallId: event.toolCallId
244
+ } : {}
245
+ })
246
+ ];
247
+ case "approval-requested": {
248
+ const call = this.calls.get(event.id);
249
+ const parked = event.runId ?? this.options.streamRunId;
250
+ const toolName = event.toolName ?? call?.name ?? "";
251
+ const input = event.input ?? call?.input ?? null;
252
+ this.pending.set(event.id, {
253
+ kind: "approval",
254
+ parked,
255
+ toolCallId: event.id,
256
+ interrupt: {
257
+ reason: "tool_approval",
258
+ message: event.reason ?? (toolName.length > 0 ? `Approve ${toolName}?` : "Approve?"),
259
+ toolCallId: event.id,
260
+ responseSchema: APPROVAL_SCHEMA,
261
+ ...event.expiresAt !== void 0 ? {
262
+ expiresAt: event.expiresAt
263
+ } : {},
264
+ metadata: {
265
+ "agora.toolName": toolName,
266
+ "agora.input": input,
267
+ "agora.approver": event.approver
268
+ }
269
+ }
270
+ });
271
+ return [
272
+ this.custom(AG_UI_CUSTOM.approvalRequested, {
273
+ id: event.id,
274
+ runId: parked,
275
+ toolName,
276
+ input,
277
+ approver: event.approver,
278
+ ...event.expiresAt !== void 0 ? {
279
+ expiresAt: event.expiresAt
280
+ } : {},
281
+ ...event.reason !== void 0 ? {
282
+ reason: event.reason
283
+ } : {}
284
+ })
285
+ ];
286
+ }
287
+ case "elicitation": {
288
+ const parked = event.runId ?? this.options.streamRunId;
289
+ this.pending.set(event.id, {
290
+ kind: "elicitation",
291
+ parked,
292
+ toolCallId: event.id,
293
+ interrupt: {
294
+ reason: "input_required",
295
+ message: event.request.preamble ?? event.request.questions[0]?.prompt ?? "Input required",
296
+ // An intake's question set belongs to no tool call the stream announced.
297
+ ...this.calls.has(event.id) ? {
298
+ toolCallId: event.id
299
+ } : {},
300
+ responseSchema: elicitationSchema(event.request),
301
+ metadata: {
302
+ "agora.request": event.request
303
+ }
304
+ }
305
+ });
306
+ return [
307
+ this.custom(AG_UI_CUSTOM.elicitation, {
308
+ runId: parked,
309
+ request: event.request
310
+ })
311
+ ];
312
+ }
313
+ case "text":
314
+ return event.text.length > 0 ? this.textDelta(event.text) : [];
315
+ case "reasoning":
316
+ return event.text.length > 0 ? this.reasoningDelta(event.text) : [];
317
+ case "step-start": {
318
+ this.stepSeq += 1;
319
+ const stepName = `step-${this.stepSeq}`;
320
+ this.openSteps.push(stepName);
321
+ this.stepMessage = void 0;
322
+ return [
323
+ ...this.closeStreams(),
324
+ {
325
+ type: "STEP_STARTED",
326
+ stepName
327
+ }
328
+ ];
329
+ }
330
+ case "step-finish": {
331
+ const out = this.closeStreams();
332
+ if (!this.replaying) this.addUsage(event.usage, event.model);
333
+ const stepName = this.openSteps.pop();
334
+ if (stepName !== void 0) out.push({
335
+ type: "STEP_FINISHED",
336
+ stepName
337
+ });
338
+ if (event.usage !== void 0 || event.costUsd !== void 0) {
339
+ out.push(this.custom(AG_UI_CUSTOM.stepUsage, {
340
+ ...event.usage !== void 0 ? {
341
+ usage: event.usage
342
+ } : {},
343
+ ...event.costUsd !== void 0 ? {
344
+ costUsd: event.costUsd
345
+ } : {},
346
+ ...event.reasoningMs !== void 0 ? {
347
+ reasoningMs: event.reasoningMs
348
+ } : {}
349
+ }));
350
+ }
351
+ return out;
352
+ }
353
+ case "tool-input-start": {
354
+ if (this.calls.has(event.id)) return [];
355
+ const out = this.closeStreams();
356
+ this.calls.set(event.id, {
357
+ name: event.name,
358
+ started: true,
359
+ ended: false,
360
+ sawDelta: false,
361
+ answered: false
362
+ });
363
+ out.push(this.callStart(event.id, event.name));
364
+ return out;
365
+ }
366
+ case "tool-input-delta": {
367
+ const call = this.calls.get(event.id);
368
+ if (call === void 0 || call.ended || event.delta.length === 0) return [];
369
+ call.sawDelta = true;
370
+ return [
371
+ {
372
+ type: "TOOL_CALL_ARGS",
373
+ toolCallId: event.id,
374
+ delta: event.delta
375
+ }
376
+ ];
377
+ }
378
+ case "tool-input-available": {
379
+ const known = this.calls.get(event.id);
380
+ if (known?.ended === true) return [];
381
+ const out = this.closeStreams();
382
+ const call = known ?? {
383
+ name: event.name,
384
+ started: false,
385
+ ended: false,
386
+ sawDelta: false,
387
+ answered: false
388
+ };
389
+ if (!call.started) {
390
+ call.started = true;
391
+ out.push(this.callStart(event.id, event.name));
392
+ }
393
+ if (!call.sawDelta) {
394
+ out.push({
395
+ type: "TOOL_CALL_ARGS",
396
+ toolCallId: event.id,
397
+ delta: text(event.input ?? {})
398
+ });
399
+ }
400
+ call.ended = true;
401
+ call.input = event.input;
402
+ this.calls.set(event.id, call);
403
+ out.push({
404
+ type: "TOOL_CALL_END",
405
+ toolCallId: event.id
406
+ });
407
+ return out;
408
+ }
409
+ case "tool-output":
410
+ return this.result(event.id, text(event.output));
411
+ case "tool-output-error":
412
+ return this.result(event.id, event.error, {
413
+ "agora.outcome": "error"
414
+ });
415
+ case "tool-output-denied":
416
+ return this.result(event.id, event.reason ?? "denied", {
417
+ "agora.outcome": "denied"
418
+ });
419
+ case "approval-settled":
420
+ this.pending.delete(event.id);
421
+ return [
422
+ this.custom(AG_UI_CUSTOM.approvalSettled, event)
423
+ ];
424
+ case "title":
425
+ return [
426
+ this.custom(AG_UI_CUSTOM.title, {
427
+ title: event.title
428
+ })
429
+ ];
430
+ case "queue":
431
+ return [
432
+ this.custom(AG_UI_CUSTOM.queue, {
433
+ queue: event.queue,
434
+ ...event.started !== void 0 ? {
435
+ started: event.started
436
+ } : {}
437
+ })
438
+ ];
439
+ case "cancelled":
440
+ this.cancelled = true;
441
+ return [];
442
+ default:
443
+ return [];
444
+ }
445
+ }
446
+ /**
447
+ * The events that end the AG-UI run: whatever is still open is closed first, then `RUN_FINISHED`
448
+ * with how it ended — interrupted when the run is waiting on someone, cancelled when it was
449
+ * stopped, success otherwise. Nothing after a `RUN_ERROR`. `ended` says the library run's stream
450
+ * itself ended, rather than this reader having stopped following it.
451
+ */
452
+ finish(ended = false) {
453
+ if (this.closed) return [];
454
+ this.closed = true;
455
+ if (!this.crossed) this.crossBoundary();
456
+ if (ended) this.pending.clear();
457
+ const out = this.start();
458
+ out.push(...this.closeStreams());
459
+ for (const [id, call] of this.calls) {
460
+ if (call.started && !call.ended) {
461
+ call.ended = true;
462
+ out.push({
463
+ type: "TOOL_CALL_END",
464
+ toolCallId: id
465
+ });
466
+ }
467
+ }
468
+ while (this.openSteps.length > 0) {
469
+ out.push({
470
+ type: "STEP_FINISHED",
471
+ stepName: this.openSteps.pop()
472
+ });
473
+ }
474
+ const usage = this.usageEntries();
475
+ const interrupts = this.cancelled ? [] : this.interrupts();
476
+ out.push({
477
+ type: "RUN_FINISHED",
478
+ threadId: this.options.threadId,
479
+ runId: this.options.runId,
480
+ ...this.cancelled ? {
481
+ outcome: {
482
+ type: "cancelled"
483
+ }
484
+ } : interrupts.length > 0 ? {
485
+ outcome: {
486
+ type: "interrupt",
487
+ interrupts
488
+ }
489
+ } : {},
490
+ ...usage.length > 0 ? {
491
+ usage
492
+ } : {}
493
+ });
494
+ return out;
495
+ }
496
+ interrupts() {
497
+ return [
498
+ ...this.pending.values()
499
+ ].map((entry) => ({
500
+ id: encodeInterruptId({
501
+ kind: entry.kind,
502
+ parked: entry.parked,
503
+ stream: this.options.streamRunId,
504
+ toolCallId: entry.toolCallId,
505
+ position: this.position
506
+ }),
507
+ ...entry.interrupt
508
+ }));
509
+ }
510
+ custom(name, value) {
511
+ return {
512
+ type: "CUSTOM",
513
+ name,
514
+ value
515
+ };
516
+ }
517
+ nextMessageId() {
518
+ this.messageSeq += 1;
519
+ return `${this.options.runId}:m${this.messageSeq}`;
520
+ }
521
+ textDelta(delta) {
522
+ const out = [];
523
+ if (this.openReasoning !== void 0) out.push(...this.closeReasoning());
524
+ if (this.openText === void 0) {
525
+ this.openText = this.nextMessageId();
526
+ this.stepMessage = this.openText;
527
+ out.push({
528
+ type: "TEXT_MESSAGE_START",
529
+ messageId: this.openText,
530
+ role: "assistant"
531
+ });
532
+ }
533
+ out.push({
534
+ type: "TEXT_MESSAGE_CONTENT",
535
+ messageId: this.openText,
536
+ delta
537
+ });
538
+ return out;
539
+ }
540
+ reasoningDelta(delta) {
541
+ const out = [];
542
+ if (this.openText !== void 0) out.push(...this.closeText());
543
+ if (this.openReasoning === void 0) {
544
+ this.openReasoning = this.nextMessageId();
545
+ out.push({
546
+ type: "REASONING_START",
547
+ messageId: this.openReasoning
548
+ }, {
549
+ type: "REASONING_MESSAGE_START",
550
+ messageId: this.openReasoning,
551
+ role: "reasoning"
552
+ });
553
+ }
554
+ out.push({
555
+ type: "REASONING_MESSAGE_CONTENT",
556
+ messageId: this.openReasoning,
557
+ delta
558
+ });
559
+ return out;
560
+ }
561
+ closeText() {
562
+ if (this.openText === void 0) return [];
563
+ const messageId = this.openText;
564
+ this.openText = void 0;
565
+ return [
566
+ {
567
+ type: "TEXT_MESSAGE_END",
568
+ messageId
569
+ }
570
+ ];
571
+ }
572
+ closeReasoning() {
573
+ if (this.openReasoning === void 0) return [];
574
+ const messageId = this.openReasoning;
575
+ this.openReasoning = void 0;
576
+ return [
577
+ {
578
+ type: "REASONING_MESSAGE_END",
579
+ messageId
580
+ },
581
+ {
582
+ type: "REASONING_END",
583
+ messageId
584
+ }
585
+ ];
586
+ }
587
+ closeStreams() {
588
+ return [
589
+ ...this.closeReasoning(),
590
+ ...this.closeText()
591
+ ];
592
+ }
593
+ callStart(id, name) {
594
+ return {
595
+ type: "TOOL_CALL_START",
596
+ toolCallId: id,
597
+ toolCallName: name,
598
+ ...this.stepMessage !== void 0 ? {
599
+ parentMessageId: this.stepMessage
600
+ } : {}
601
+ };
602
+ }
603
+ result(id, content, metadata) {
604
+ const call = this.calls.get(id);
605
+ if (call !== void 0) call.answered = true;
606
+ else {
607
+ this.calls.set(id, {
608
+ name: "",
609
+ started: false,
610
+ ended: true,
611
+ sawDelta: false,
612
+ answered: true
613
+ });
614
+ }
615
+ this.pending.delete(id);
616
+ return [
617
+ ...this.closeStreams(),
618
+ {
619
+ type: "TOOL_CALL_RESULT",
620
+ messageId: `${this.options.runId}:tool:${id}`,
621
+ toolCallId: id,
622
+ content,
623
+ role: "tool",
624
+ ...metadata !== void 0 ? {
625
+ metadata
626
+ } : {}
627
+ }
628
+ ];
629
+ }
630
+ addUsage(usage, model) {
631
+ if (usage === void 0) return;
632
+ const name = typeof model === "string" && model.length > 0 ? model : this.options.model;
633
+ const key = name ?? "";
634
+ const entry = this.usage.get(key) ?? (name !== void 0 ? {
635
+ model: name
636
+ } : {});
637
+ entry.inputTokens = (entry.inputTokens ?? 0) + usage.inputTokens;
638
+ entry.outputTokens = (entry.outputTokens ?? 0) + usage.outputTokens;
639
+ entry.totalTokens = entry.inputTokens + entry.outputTokens;
640
+ if (usage.cacheReadTokens !== void 0) {
641
+ entry.cachedInputTokens = (entry.cachedInputTokens ?? 0) + usage.cacheReadTokens;
642
+ }
643
+ if (usage.cacheWriteTokens !== void 0) {
644
+ entry.cacheWriteInputTokens = (entry.cacheWriteInputTokens ?? 0) + usage.cacheWriteTokens;
645
+ }
646
+ if (usage.reasoningTokens !== void 0) {
647
+ entry.reasoningTokens = (entry.reasoningTokens ?? 0) + usage.reasoningTokens;
648
+ }
649
+ this.usage.set(key, entry);
650
+ }
651
+ usageEntries() {
652
+ return [
653
+ ...this.usage.values()
654
+ ];
655
+ }
656
+ };
657
+
658
+ // src/ag-ui/input.ts
659
+ function isRecord(value) {
660
+ return typeof value === "object" && value !== null && !Array.isArray(value);
661
+ }
662
+ __name(isRecord, "isRecord");
663
+ function parseRunInput(body) {
664
+ if (!isRecord(body)) return "the body must be a RunAgentInput object";
665
+ if (typeof body.threadId !== "string" || body.threadId.length === 0) {
666
+ return "threadId must be a non-empty string";
667
+ }
668
+ if (body.threadId.length > 255) return "threadId must be at most 255 characters";
669
+ if (typeof body.runId !== "string" || body.runId.length === 0) {
670
+ return "runId must be a non-empty string";
671
+ }
672
+ if (!Array.isArray(body.messages)) return "messages must be an array";
673
+ for (const message of body.messages) {
674
+ if (!isRecord(message) || typeof message.role !== "string") {
675
+ return "each message must be an object with a role";
676
+ }
677
+ }
678
+ if (body.resume !== void 0) {
679
+ if (!Array.isArray(body.resume)) return "resume must be an array";
680
+ for (const entry of body.resume) {
681
+ if (!isRecord(entry) || typeof entry.interruptId !== "string" || entry.status !== "resolved" && entry.status !== "cancelled") {
682
+ return "each resume entry needs an interruptId and a status of 'resolved' or 'cancelled'";
683
+ }
684
+ }
685
+ }
686
+ if (body.context !== void 0 && !Array.isArray(body.context)) {
687
+ return "context must be an array";
688
+ }
689
+ return body;
690
+ }
691
+ __name(parseRunInput, "parseRunInput");
692
+ var EXTENSIONS = {
693
+ "image/png": "png",
694
+ "image/jpeg": "jpg",
695
+ "image/gif": "gif",
696
+ "image/webp": "webp",
697
+ "application/pdf": "pdf",
698
+ "text/plain": "txt"
699
+ };
700
+ function filenameOf(part, index, contentType) {
701
+ const metadata = part.metadata;
702
+ if (isRecord(metadata)) {
703
+ for (const key of [
704
+ "filename",
705
+ "name",
706
+ "title"
707
+ ]) {
708
+ const value = metadata[key];
709
+ if (typeof value === "string" && value.trim().length > 0) return value.trim().slice(0, 200);
710
+ }
711
+ }
712
+ const extension = EXTENSIONS[contentType];
713
+ return `${part.type}-${index + 1}${extension !== void 0 ? `.${extension}` : ""}`;
714
+ }
715
+ __name(filenameOf, "filenameOf");
716
+ function readUserTurn(messages) {
717
+ let last;
718
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
719
+ if (messages[index]?.role === "user") {
720
+ last = messages[index];
721
+ break;
722
+ }
723
+ }
724
+ if (last === void 0) return null;
725
+ if (typeof last.content === "string") return {
726
+ text: last.content,
727
+ media: [],
728
+ dropped: []
729
+ };
730
+ if (!Array.isArray(last.content)) return {
731
+ text: "",
732
+ media: [],
733
+ dropped: []
734
+ };
735
+ const texts = [];
736
+ const media = [];
737
+ const dropped = [];
738
+ last.content.forEach((part, index) => {
739
+ if (!isRecord(part)) return;
740
+ if (part.type === "text") {
741
+ if (typeof part.text === "string") texts.push(part.text);
742
+ return;
743
+ }
744
+ if (part.type !== "image" && part.type !== "audio" && part.type !== "video" && part.type !== "document") {
745
+ return;
746
+ }
747
+ const source = part.source;
748
+ if (!isRecord(source) || source.type !== "data") {
749
+ const how = isRecord(source) && typeof source.type === "string" ? source.type : "unknown";
750
+ dropped.push(`The ${part.type} part ${index + 1} was not used: only inline data is accepted, not a ${how} source.`);
751
+ return;
752
+ }
753
+ if (typeof source.value !== "string" || typeof source.mimeType !== "string") {
754
+ dropped.push(`The ${part.type} part ${index + 1} was not used: its inline data is malformed.`);
755
+ return;
756
+ }
757
+ const data = Buffer.from(source.value, "base64");
758
+ if (data.length === 0) {
759
+ dropped.push(`The ${part.type} part ${index + 1} was not used: it carries no bytes.`);
760
+ return;
761
+ }
762
+ const contentType = source.mimeType.toLowerCase();
763
+ media.push({
764
+ kind: part.type,
765
+ contentType,
766
+ data,
767
+ filename: filenameOf(part, index, contentType)
768
+ });
769
+ });
770
+ return {
771
+ text: texts.join("\n"),
772
+ media,
773
+ dropped
774
+ };
775
+ }
776
+ __name(readUserTurn, "readUserTurn");
777
+ function readContext(context) {
778
+ if (context === void 0) return void 0;
779
+ const entries = context.filter((entry) => isRecord(entry) && typeof entry.description === "string" && typeof entry.value === "string");
780
+ return entries.length > 0 ? entries : void 0;
781
+ }
782
+ __name(readContext, "readContext");
783
+ function readForwardedProps(forwarded) {
784
+ if (!isRecord(forwarded)) return {};
785
+ const pick = /* @__PURE__ */ __name((key) => {
786
+ const value = forwarded[key];
787
+ return typeof value === "string" && value.length > 0 ? value : void 0;
788
+ }, "pick");
789
+ const agent = pick("agent");
790
+ const model = pick("model");
791
+ const persona = pick("persona");
792
+ return {
793
+ ...agent !== void 0 ? {
794
+ agent
795
+ } : {},
796
+ ...model !== void 0 ? {
797
+ model
798
+ } : {},
799
+ ...persona !== void 0 ? {
800
+ persona
801
+ } : {},
802
+ ...isRecord(forwarded.pageContext) ? {
803
+ pageContext: forwarded.pageContext
804
+ } : {}
805
+ };
806
+ }
807
+ __name(readForwardedProps, "readForwardedProps");
808
+ function planResume(resume) {
809
+ const decisions = [];
810
+ const unrecognised = [];
811
+ for (const entry of resume) {
812
+ const address = decodeInterruptId(entry.interruptId);
813
+ if (address === null) {
814
+ unrecognised.push(entry.interruptId);
815
+ continue;
816
+ }
817
+ decisions.push({
818
+ address,
819
+ entry
820
+ });
821
+ }
822
+ const first = decisions[0];
823
+ if (first === void 0) return {
824
+ decisions,
825
+ unrecognised
826
+ };
827
+ for (const { address } of decisions) {
828
+ if (address.stream !== first.address.stream || address.position !== first.address.position) {
829
+ return "the resume entries answer interrupts of different runs";
830
+ }
831
+ }
832
+ const seen = /* @__PURE__ */ new Set();
833
+ for (const { address } of decisions) {
834
+ if (seen.has(address.toolCallId)) return "an interrupt is answered twice";
835
+ seen.add(address.toolCallId);
836
+ }
837
+ return {
838
+ decisions,
839
+ unrecognised
840
+ };
841
+ }
842
+ __name(planResume, "planResume");
843
+ function readApprovalPayload(payload) {
844
+ if (typeof payload === "boolean") return {
845
+ approved: payload
846
+ };
847
+ if (!isRecord(payload) || typeof payload.approved !== "boolean") return null;
848
+ return {
849
+ approved: payload.approved,
850
+ ...typeof payload.reason === "string" ? {
851
+ reason: payload.reason
852
+ } : {},
853
+ ...typeof payload.remember === "boolean" ? {
854
+ remember: payload.remember
855
+ } : {}
856
+ };
857
+ }
858
+ __name(readApprovalPayload, "readApprovalPayload");
859
+ function readAnswersPayload(payload) {
860
+ const source = isRecord(payload) && isRecord(payload.answers) ? payload.answers : payload;
861
+ if (!isRecord(source)) return null;
862
+ const answers = {};
863
+ for (const [key, value] of Object.entries(source)) {
864
+ if (typeof value === "string") answers[key] = [
865
+ value
866
+ ];
867
+ else if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
868
+ answers[key] = value;
869
+ } else return null;
870
+ }
871
+ return answers;
872
+ }
873
+ __name(readAnswersPayload, "readAnswersPayload");
874
+
875
+ // src/spi/token-stream-sink.ts
876
+ var AgentStreamError = class extends Error {
877
+ static {
878
+ __name(this, "AgentStreamError");
879
+ }
880
+ code;
881
+ constructor(error) {
882
+ super(error.message);
883
+ this.name = "AgentStreamError";
884
+ this.code = error.code;
885
+ }
886
+ };
887
+
888
+ // src/stream-events.ts
889
+ var encoder = new TextEncoder();
890
+ function decodeStreamEvent(line) {
891
+ try {
892
+ const parsed = JSON.parse(line);
893
+ if (typeof parsed === "object" && parsed !== null && typeof parsed.kind === "string") {
894
+ return parsed;
895
+ }
896
+ } catch {
897
+ }
898
+ return null;
899
+ }
900
+ __name(decodeStreamEvent, "decodeStreamEvent");
901
+
902
+ // src/ag-ui/stream.ts
903
+ var DEFAULT_QUIET_MS = 750;
904
+ var GRACE_MS = 50;
905
+ async function* agUiEvents(frames, options) {
906
+ const encoder2 = new AgUiEncoder(options);
907
+ const quietMs = options.quietMs ?? DEFAULT_QUIET_MS;
908
+ yield* encoder2.start();
909
+ yield* options.preamble ?? [];
910
+ const iterator = frames[Symbol.asyncIterator]();
911
+ let upcoming;
912
+ let ended = false;
913
+ try {
914
+ for (; ; ) {
915
+ upcoming ??= iterator.next();
916
+ const caughtUp = encoder2.consumed >= (options.skip ?? 0);
917
+ let next;
918
+ if (caughtUp && encoder2.waiting) {
919
+ next = await raceQuiet(upcoming, encoder2.busy ? quietMs : Math.min(quietMs, GRACE_MS));
920
+ } else {
921
+ next = await upcoming;
922
+ }
923
+ if (next === "quiet") break;
924
+ upcoming = void 0;
925
+ if (next.done === true) {
926
+ ended = true;
927
+ break;
928
+ }
929
+ yield* encoder2.encode(next.value);
930
+ }
931
+ } finally {
932
+ void iterator.return?.();
933
+ }
934
+ yield* encoder2.finish(ended);
935
+ }
936
+ __name(agUiEvents, "agUiEvents");
937
+ function raceQuiet(upcoming, ms) {
938
+ return new Promise((resolve, reject) => {
939
+ const timer = setTimeout(() => resolve("quiet"), ms);
940
+ upcoming.then((result) => {
941
+ clearTimeout(timer);
942
+ resolve(result);
943
+ }, (error) => {
944
+ clearTimeout(timer);
945
+ reject(error);
946
+ });
947
+ });
948
+ }
949
+ __name(raceQuiet, "raceQuiet");
950
+ function agUiSse(event) {
951
+ return `data: ${JSON.stringify(event)}
952
+
953
+ `;
954
+ }
955
+ __name(agUiSse, "agUiSse");
956
+ async function* agUiFramesFromNdjson(chunks) {
957
+ const decoder = new TextDecoder();
958
+ let buffer = "";
959
+ const read = /* @__PURE__ */ __name((line) => {
960
+ const event = decodeStreamEvent(line);
961
+ if (event !== null) return [
962
+ event
963
+ ];
964
+ const at = line.indexOf('{"kind":');
965
+ const tail = at > 0 ? decodeStreamEvent(line.slice(at)) : null;
966
+ if (tail !== null) return [
967
+ {
968
+ kind: "text",
969
+ text: line.slice(0, at)
970
+ },
971
+ tail
972
+ ];
973
+ return [
974
+ {
975
+ kind: "text",
976
+ text: line
977
+ }
978
+ ];
979
+ }, "read");
980
+ try {
981
+ for await (const chunk of chunks) {
982
+ buffer += decoder.decode(chunk, {
983
+ stream: true
984
+ });
985
+ let newline = buffer.indexOf("\n");
986
+ while (newline !== -1) {
987
+ const line = buffer.slice(0, newline);
988
+ buffer = buffer.slice(newline + 1);
989
+ if (line.length > 0) yield* read(line);
990
+ newline = buffer.indexOf("\n");
991
+ }
992
+ }
993
+ buffer += decoder.decode();
994
+ if (buffer.length > 0) yield* read(buffer);
995
+ } catch (error) {
996
+ if (error instanceof AgentStreamError) {
997
+ yield {
998
+ kind: "error",
999
+ code: error.code,
1000
+ message: error.message
1001
+ };
1002
+ return;
1003
+ }
1004
+ throw error;
1005
+ }
1006
+ }
1007
+ __name(agUiFramesFromNdjson, "agUiFramesFromNdjson");
1008
+ export {
1009
+ AG_UI_CUSTOM,
1010
+ AG_UI_PROTOCOL_VERSION,
1011
+ AgUiEncoder,
1012
+ agUiEvents,
1013
+ agUiFramesFromNdjson,
1014
+ agUiSse,
1015
+ decodeInterruptId,
1016
+ encodeInterruptId,
1017
+ parseRunInput,
1018
+ planResume,
1019
+ readAnswersPayload,
1020
+ readApprovalPayload,
1021
+ readContext,
1022
+ readForwardedProps,
1023
+ readUserTurn
1024
+ };
1025
+ //# sourceMappingURL=index.js.map