@adminforth/agent 1.43.29 → 1.44.1

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.
Files changed (39) hide show
  1. package/agentEvents.ts +66 -0
  2. package/agentResponseEvents.ts +1 -206
  3. package/build.log +2 -2
  4. package/custom/conversation_area/ProcessingTimeline.vue +23 -2
  5. package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +2 -2
  6. package/custom/types.ts +5 -4
  7. package/dist/agent/checkpointer.d.ts +29 -0
  8. package/dist/agent/languageDetect.d.ts +10 -0
  9. package/dist/agent/middleware/apiBasedTools.d.ts +3 -0
  10. package/dist/agent/middleware/openAiResponsesContinuation.d.ts +1 -0
  11. package/dist/agent/middleware/sequenceDebug.d.ts +46 -0
  12. package/dist/agent/simpleAgent.d.ts +61 -0
  13. package/dist/agent/skills/registry.d.ts +13 -0
  14. package/dist/agent/systemPrompt.d.ts +11 -0
  15. package/dist/agent/toolCallEvents.d.ts +27 -0
  16. package/dist/agent/tools/apiTool.d.ts +6 -0
  17. package/dist/agent/tools/fetchSkill.d.ts +8 -0
  18. package/dist/agent/tools/fetchToolSchema.d.ts +9 -0
  19. package/dist/agent/tools/getUserLocation.d.ts +8 -0
  20. package/dist/agent/tools/index.d.ts +4 -0
  21. package/dist/agentEvents.d.ts +56 -0
  22. package/dist/agentEvents.js +1 -0
  23. package/dist/agentResponseEvents.d.ts +1 -0
  24. package/dist/agentResponseEvents.js +1 -144
  25. package/dist/apiBasedTools.d.ts +29 -0
  26. package/dist/custom/conversation_area/ProcessingTimeline.vue +23 -2
  27. package/dist/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +2 -2
  28. package/dist/custom/types.ts +5 -4
  29. package/dist/index.d.ts +58 -0
  30. package/dist/index.js +280 -48
  31. package/dist/sanitizeSpeechText.d.ts +1 -0
  32. package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +14 -0
  33. package/dist/surfaces/web-sse/createSseEventEmitter.js +211 -0
  34. package/dist/types.d.ts +94 -0
  35. package/index.ts +315 -46
  36. package/package.json +2 -2
  37. package/surfaces/web-sse/createSseEventEmitter.ts +278 -0
  38. package/tsconfig.json +1 -0
  39. package/types.ts +6 -0
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import { AdminForthCheckpointSaver } from "./agent/checkpointer.js";
24
24
  import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
25
25
  import { detectUserLanguage } from "./agent/languageDetect.js";
26
26
  import { prepareApiBasedTools as buildApiBasedTools } from './apiBasedTools.js';
27
- import { createAgentEventStream } from "./agentResponseEvents.js";
27
+ import { createSseEventEmitter } from "./surfaces/web-sse/createSseEventEmitter.js";
28
28
  import { appendCustomSystemPrompt, buildAgentSystemPrompt, buildAgentTurnSystemPrompt, DEFAULT_AGENT_SYSTEM_PROMPT } from "./agent/systemPrompt.js";
29
29
  import { sanitizeSpeechText } from "./sanitizeSpeechText.js";
30
30
  const agentResponseBodySchema = z.object({
@@ -48,6 +48,8 @@ const sessionIdBodySchema = z.object({
48
48
  const createSessionBodySchema = z.object({
49
49
  triggerMessage: z.string().optional(),
50
50
  }).strict();
51
+ const VEGA_LITE_FENCE_START = "```vega-lite";
52
+ const COMPLETE_VEGA_LITE_BLOCK_RE = /```vega-lite[\s\S]*?```/;
51
53
  function isAbortError(error) {
52
54
  return (error instanceof DOMException && error.name === "AbortError") || (typeof error === "object" &&
53
55
  error !== null &&
@@ -104,6 +106,25 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
104
106
  }));
105
107
  });
106
108
  }
109
+ getChatSurfaceSessionId(incoming) {
110
+ return `${incoming.surface}:${incoming.externalConversationId}`;
111
+ }
112
+ getOrCreateChatSurfaceSession(incoming, adminUser) {
113
+ return __awaiter(this, void 0, void 0, function* () {
114
+ const sessionId = this.getChatSurfaceSessionId(incoming);
115
+ const sessionResource = this.adminforth.resource(this.options.sessionResource.resourceId);
116
+ const session = yield sessionResource.get([Filters.EQ(this.options.sessionResource.idField, sessionId)]);
117
+ if (session) {
118
+ return sessionId;
119
+ }
120
+ yield sessionResource.create({
121
+ [this.options.sessionResource.idField]: sessionId,
122
+ [this.options.sessionResource.titleField]: incoming.prompt.slice(0, 40) || "New Session",
123
+ [this.options.sessionResource.askerIdField]: adminUser.pk,
124
+ });
125
+ return sessionId;
126
+ });
127
+ }
107
128
  getCheckpointer() {
108
129
  if (this.checkpointer)
109
130
  return this.checkpointer;
@@ -170,8 +191,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
170
191
  });
171
192
  }
172
193
  validateConfigAfterDiscover(adminforth, resourceConfig) {
173
- var _a;
194
+ var _a, _b;
174
195
  (_a = this.options.audioAdapter) === null || _a === void 0 ? void 0 : _a.validate();
196
+ for (const chatSurfaceAdapter of (_b = this.options.chatSurfaceAdapters) !== null && _b !== void 0 ? _b : []) {
197
+ chatSurfaceAdapter.validate();
198
+ }
175
199
  this.agentSystemPromptPromise = buildAgentSystemPrompt(adminforth, this.getInternalAgentResourceIds())
176
200
  .then((systemPrompt) => appendCustomSystemPrompt(systemPrompt, this.options.systemPrompt));
177
201
  }
@@ -181,8 +205,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
181
205
  runAgentTurn(input) {
182
206
  return __awaiter(this, void 0, void 0, function* () {
183
207
  var _a, e_1, _b, _c;
184
- var _d, _e, _f, _g, _h, _j;
208
+ var _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
185
209
  let fullResponse = "";
210
+ let bufferedTextDelta = "";
211
+ let isRenderingVegaLite = false;
186
212
  const maxTokens = (_d = this.options.maxTokens) !== null && _d !== void 0 ? _d : 1000;
187
213
  const selectedMode = (_e = this.options.modes.find((mode) => mode.name === input.modeName)) !== null && _e !== void 0 ? _e : this.options.modes[0];
188
214
  const [primaryModelSpec, summaryModelSpec] = yield Promise.all([
@@ -238,14 +264,17 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
238
264
  emitToolCallEvent: (event) => {
239
265
  var _a;
240
266
  input.sequenceDebugCollector.handleToolCallEvent(event);
241
- (_a = input.emitToolCallEvent) === null || _a === void 0 ? void 0 : _a.call(input, event);
267
+ void ((_a = input.emit) === null || _a === void 0 ? void 0 : _a.call(input, {
268
+ type: "tool-call",
269
+ data: event,
270
+ }));
242
271
  },
243
272
  sequenceDebugSink: input.sequenceDebugCollector,
244
273
  });
245
274
  try {
246
- for (var _k = true, _l = __asyncValues(stream), _m; _m = yield _l.next(), _a = _m.done, !_a; _k = true) {
247
- _c = _m.value;
248
- _k = false;
275
+ for (var _p = true, _q = __asyncValues(stream), _r; _r = yield _q.next(), _a = _r.done, !_a; _p = true) {
276
+ _c = _r.value;
277
+ _p = false;
249
278
  const rawChunk = _c;
250
279
  if ((_g = input.abortSignal) === null || _g === void 0 ? void 0 : _g.aborted) {
251
280
  throw new DOMException("This operation was aborted", "AbortError");
@@ -271,21 +300,68 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
271
300
  .map((b) => { var _a; return String((_a = b.text) !== null && _a !== void 0 ? _a : ""); })
272
301
  .join("");
273
302
  if (reasoningDelta) {
274
- (_h = input.emitReasoningDelta) === null || _h === void 0 ? void 0 : _h.call(input, reasoningDelta);
303
+ yield ((_h = input.emit) === null || _h === void 0 ? void 0 : _h.call(input, {
304
+ type: "reasoning-delta",
305
+ delta: reasoningDelta,
306
+ }));
275
307
  }
276
308
  if (textDelta) {
277
309
  fullResponse += textDelta;
278
- (_j = input.emitTextDelta) === null || _j === void 0 ? void 0 : _j.call(input, textDelta);
310
+ bufferedTextDelta += textDelta;
311
+ if (bufferedTextDelta.includes(VEGA_LITE_FENCE_START) &&
312
+ !COMPLETE_VEGA_LITE_BLOCK_RE.test(bufferedTextDelta)) {
313
+ if (!isRenderingVegaLite) {
314
+ isRenderingVegaLite = true;
315
+ yield ((_j = input.emit) === null || _j === void 0 ? void 0 : _j.call(input, {
316
+ type: "rendering",
317
+ phase: "start",
318
+ label: "Rendering...",
319
+ }));
320
+ }
321
+ continue;
322
+ }
323
+ if (isRenderingVegaLite) {
324
+ isRenderingVegaLite = false;
325
+ yield ((_k = input.emit) === null || _k === void 0 ? void 0 : _k.call(input, {
326
+ type: "rendering",
327
+ phase: "end",
328
+ label: "Rendering...",
329
+ }));
330
+ }
331
+ const streamableLength = bufferedTextDelta.includes(VEGA_LITE_FENCE_START)
332
+ ? bufferedTextDelta.length
333
+ : bufferedTextDelta.length - getPartialVegaLiteFenceStartLength(bufferedTextDelta);
334
+ if (!streamableLength) {
335
+ continue;
336
+ }
337
+ yield ((_l = input.emit) === null || _l === void 0 ? void 0 : _l.call(input, {
338
+ type: "text-delta",
339
+ delta: bufferedTextDelta.slice(0, streamableLength),
340
+ }));
341
+ bufferedTextDelta = bufferedTextDelta.slice(streamableLength);
279
342
  }
280
343
  }
281
344
  }
282
345
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
283
346
  finally {
284
347
  try {
285
- if (!_k && !_a && (_b = _l.return)) yield _b.call(_l);
348
+ if (!_p && !_a && (_b = _q.return)) yield _b.call(_q);
286
349
  }
287
350
  finally { if (e_1) throw e_1.error; }
288
351
  }
352
+ if (isRenderingVegaLite) {
353
+ yield ((_m = input.emit) === null || _m === void 0 ? void 0 : _m.call(input, {
354
+ type: "rendering",
355
+ phase: "end",
356
+ label: "Rendering...",
357
+ }));
358
+ }
359
+ if (bufferedTextDelta) {
360
+ yield ((_o = input.emit) === null || _o === void 0 ? void 0 : _o.call(input, {
361
+ type: "text-delta",
362
+ delta: bufferedTextDelta,
363
+ }));
364
+ }
289
365
  return {
290
366
  text: fullResponse,
291
367
  };
@@ -293,7 +369,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
293
369
  }
294
370
  runAndPersistAgentResponse(input) {
295
371
  return __awaiter(this, void 0, void 0, function* () {
296
- var _a, _b;
372
+ var _a;
297
373
  const previousUserMessages = yield this.getPreviousUserMessages(input.sessionId);
298
374
  const turnId = yield this.createNewTurn(input.sessionId, input.prompt);
299
375
  yield this.adminforth.resource(this.options.sessionResource.resourceId).update(input.sessionId, {
@@ -315,9 +391,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
315
391
  abortSignal: input.abortSignal,
316
392
  adminUser: input.adminUser,
317
393
  sequenceDebugCollector,
318
- emitToolCallEvent: input.emitToolCallEvent,
319
- emitReasoningDelta: input.emitReasoningDelta,
320
- emitTextDelta: input.emitTextDelta,
394
+ emit: input.emit,
321
395
  });
322
396
  fullResponse = agentResponse.text;
323
397
  }
@@ -330,7 +404,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
330
404
  failed = true;
331
405
  fullResponse = getErrorMessage(error);
332
406
  logger.error(`${input.failureLogMessage}:\n${fullResponse}`);
333
- (_b = input.emitErrorResponse) === null || _b === void 0 ? void 0 : _b.call(input, fullResponse);
334
407
  }
335
408
  }
336
409
  sequenceDebugCollector.flush();
@@ -349,7 +422,125 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
349
422
  };
350
423
  });
351
424
  }
425
+ handleTurn(input) {
426
+ return __awaiter(this, void 0, void 0, function* () {
427
+ var _a, _b;
428
+ yield input.emit({
429
+ type: "turn-started",
430
+ messageId: randomUUID(),
431
+ });
432
+ const agentResponse = yield this.runAndPersistAgentResponse({
433
+ prompt: input.prompt,
434
+ sessionId: input.sessionId,
435
+ modeName: input.modeName,
436
+ userTimeZone: input.userTimeZone,
437
+ currentPage: input.currentPage,
438
+ abortSignal: input.abortSignal,
439
+ adminUser: input.adminUser,
440
+ emit: input.emit,
441
+ failureLogMessage: (_a = input.failureLogMessage) !== null && _a !== void 0 ? _a : "Agent response failed",
442
+ abortLogMessage: (_b = input.abortLogMessage) !== null && _b !== void 0 ? _b : "Agent response aborted",
443
+ });
444
+ if (agentResponse.failed) {
445
+ yield input.emit({
446
+ type: "error",
447
+ error: agentResponse.text,
448
+ });
449
+ }
450
+ else if (!agentResponse.aborted) {
451
+ yield input.emit({
452
+ type: "response",
453
+ text: agentResponse.text,
454
+ sessionId: input.sessionId,
455
+ turnId: agentResponse.turnId,
456
+ });
457
+ }
458
+ yield input.emit({
459
+ type: "finish",
460
+ });
461
+ return agentResponse;
462
+ });
463
+ }
464
+ createChatSurfaceEventEmitter(sink) {
465
+ return (event) => __awaiter(this, void 0, void 0, function* () {
466
+ if (event.type === "text-delta") {
467
+ yield sink.emit({
468
+ type: "text_delta",
469
+ delta: event.delta,
470
+ });
471
+ return;
472
+ }
473
+ if (event.type === "response") {
474
+ yield sink.emit({
475
+ type: "done",
476
+ text: event.text,
477
+ });
478
+ return;
479
+ }
480
+ if (event.type === "error") {
481
+ yield sink.emit({
482
+ type: "error",
483
+ message: event.error,
484
+ });
485
+ }
486
+ });
487
+ }
488
+ handleChatSurfaceMessage(adapter, incoming, sink) {
489
+ return __awaiter(this, void 0, void 0, function* () {
490
+ var _a;
491
+ const adminUser = yield adapter.resolveAdminUser({
492
+ adminforth: this.adminforth,
493
+ incoming,
494
+ });
495
+ if (!adminUser) {
496
+ yield sink.emit({
497
+ type: "error",
498
+ message: "This chat account is not authorized to use AdminForth Agent.",
499
+ });
500
+ return;
501
+ }
502
+ yield this.handleTurn({
503
+ prompt: incoming.prompt,
504
+ sessionId: yield this.getOrCreateChatSurfaceSession(incoming, adminUser),
505
+ modeName: incoming.modeName,
506
+ userTimeZone: (_a = incoming.userTimeZone) !== null && _a !== void 0 ? _a : "UTC",
507
+ adminUser,
508
+ emit: this.createChatSurfaceEventEmitter(sink),
509
+ failureLogMessage: `Agent ${incoming.surface} surface response failed`,
510
+ abortLogMessage: `Agent ${incoming.surface} surface response aborted`,
511
+ });
512
+ });
513
+ }
352
514
  setupEndpoints(server) {
515
+ var _a;
516
+ for (const adapter of (_a = this.options.chatSurfaceAdapters) !== null && _a !== void 0 ? _a : []) {
517
+ server.endpoint({
518
+ method: "POST",
519
+ noAuth: true,
520
+ path: `/agent/surface/${adapter.name}/webhook`,
521
+ handler: (ctx) => __awaiter(this, void 0, void 0, function* () {
522
+ var _a;
523
+ const surfaceContext = {
524
+ body: ctx.body,
525
+ headers: ctx.headers,
526
+ abortSignal: ctx.abortSignal,
527
+ rawRequest: ctx._raw_express_req,
528
+ rawResponse: ctx._raw_express_res,
529
+ };
530
+ const incoming = yield adapter.parseIncomingMessage(surfaceContext);
531
+ if (!incoming)
532
+ return { ok: true };
533
+ const sink = yield adapter.createEventSink(surfaceContext, incoming);
534
+ try {
535
+ yield this.handleChatSurfaceMessage(adapter, incoming, sink);
536
+ }
537
+ finally {
538
+ yield ((_a = sink.close) === null || _a === void 0 ? void 0 : _a.call(sink));
539
+ }
540
+ return { ok: true };
541
+ }),
542
+ });
543
+ }
353
544
  server.endpoint({
354
545
  method: 'POST',
355
546
  path: `/agent/get-placeholder-messages`,
@@ -378,10 +569,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
378
569
  const data = this.parseBody(agentResponseBodySchema, body, response);
379
570
  if (!data)
380
571
  return;
381
- const stream = createAgentEventStream(_raw_express_res, { vercelAiUiMessageStream: true, closeActiveBlockOnToolStart: true });
382
- const messageId = randomUUID();
383
- stream.start(messageId);
384
- yield this.runAndPersistAgentResponse({
572
+ const emit = createSseEventEmitter(_raw_express_res, {
573
+ vercelAiUiMessageStream: true,
574
+ closeActiveBlockOnToolStart: true,
575
+ });
576
+ yield this.handleTurn({
385
577
  prompt: data.message,
386
578
  sessionId: data.sessionId,
387
579
  modeName: data.mode,
@@ -389,14 +581,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
389
581
  currentPage: data.currentPage,
390
582
  abortSignal,
391
583
  adminUser: currentAdminUser,
392
- emitToolCallEvent: stream.toolCall,
393
- emitReasoningDelta: stream.reasoningDelta,
394
- emitTextDelta: stream.textDelta,
395
- emitErrorResponse: stream.textDelta,
584
+ emit,
396
585
  failureLogMessage: "Agent response streaming failed",
397
586
  abortLogMessage: "Agent response streaming aborted by the client",
398
587
  });
399
- stream.end();
400
588
  return null;
401
589
  })
402
590
  });
@@ -420,7 +608,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
420
608
  response.setStatus(400, "Audio file is required");
421
609
  return { error: "Audio file is required" };
422
610
  }
423
- const stream = createAgentEventStream(_raw_express_res);
611
+ const emit = createSseEventEmitter(_raw_express_res);
424
612
  let transcription;
425
613
  try {
426
614
  transcription = yield audioAdapter.transcribe({
@@ -434,25 +622,35 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
434
622
  catch (error) {
435
623
  if (abortSignal.aborted || isAbortError(error)) {
436
624
  logger.info("Agent speech transcription aborted by the client");
437
- stream.end();
625
+ yield emit({ type: "finish" });
438
626
  return null;
439
627
  }
440
628
  logger.error(`Agent speech transcription failed:\n${getErrorMessage(error)}`);
441
- stream.error("Speech transcription failed. Check server logs for details.");
442
- stream.end();
629
+ yield emit({
630
+ type: "error",
631
+ error: "Speech transcription failed. Check server logs for details.",
632
+ });
633
+ yield emit({ type: "finish" });
443
634
  return null;
444
635
  }
445
636
  if (abortSignal.aborted) {
446
- stream.end();
637
+ yield emit({ type: "finish" });
447
638
  return null;
448
639
  }
449
640
  const prompt = transcription.text;
450
641
  if (!prompt) {
451
- stream.error("Speech transcription is empty");
452
- stream.end();
642
+ yield emit({
643
+ type: "error",
644
+ error: "Speech transcription is empty",
645
+ });
646
+ yield emit({ type: "finish" });
453
647
  return null;
454
648
  }
455
- stream.transcript(transcription.text, transcription.language);
649
+ yield emit({
650
+ type: "transcript",
651
+ text: transcription.text,
652
+ language: transcription.language,
653
+ });
456
654
  const sessionId = data.sessionId;
457
655
  const currentPage = data.currentPage;
458
656
  const agentResponse = yield this.runAndPersistAgentResponse({
@@ -463,26 +661,39 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
463
661
  currentPage,
464
662
  abortSignal,
465
663
  adminUser: currentAdminUser,
466
- emitToolCallEvent: stream.toolCall,
664
+ emit: (event) => __awaiter(this, void 0, void 0, function* () {
665
+ if (event.type === "tool-call") {
666
+ yield emit(event);
667
+ }
668
+ }),
467
669
  failureLogMessage: "Agent speech response failed",
468
670
  abortLogMessage: "Agent speech response aborted by the client",
469
671
  });
470
672
  if (agentResponse.aborted) {
471
- stream.end();
673
+ yield emit({ type: "finish" });
472
674
  return null;
473
675
  }
474
676
  if (agentResponse.failed) {
475
- stream.error(agentResponse.text);
476
- stream.end();
677
+ yield emit({
678
+ type: "error",
679
+ error: agentResponse.text,
680
+ });
681
+ yield emit({ type: "finish" });
477
682
  return null;
478
683
  }
479
684
  try {
480
- stream.speechResponse({
481
- text: transcription.text,
482
- language: transcription.language,
483
- }, {
484
- text: agentResponse.text,
485
- }, sessionId, agentResponse.turnId);
685
+ yield emit({
686
+ type: "speech-response",
687
+ transcript: {
688
+ text: transcription.text,
689
+ language: transcription.language,
690
+ },
691
+ response: {
692
+ text: agentResponse.text,
693
+ },
694
+ sessionId,
695
+ turnId: agentResponse.turnId,
696
+ });
486
697
  const speech = yield audioAdapter.synthesize({
487
698
  text: sanitizeSpeechText(agentResponse.text),
488
699
  stream: true,
@@ -490,7 +701,14 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
490
701
  format: "pcm",
491
702
  abortSignal,
492
703
  });
493
- stream.audioStart(speech.mimeType, speech.format, 24000, 1, 16);
704
+ yield emit({
705
+ type: "audio-start",
706
+ mimeType: speech.mimeType,
707
+ format: speech.format,
708
+ sampleRate: 24000,
709
+ channelCount: 1,
710
+ bitsPerSample: 16,
711
+ });
494
712
  const reader = speech.audioStream.getReader();
495
713
  const cancelAudioStream = () => {
496
714
  void reader.cancel().catch(() => undefined);
@@ -509,15 +727,18 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
509
727
  if (abortSignal.aborted) {
510
728
  break;
511
729
  }
512
- stream.audioDelta(value);
730
+ yield emit({
731
+ type: "audio-delta",
732
+ value,
733
+ });
513
734
  }
514
735
  }
515
736
  finally {
516
737
  abortSignal.removeEventListener("abort", cancelAudioStream);
517
738
  reader.releaseLock();
518
739
  }
519
- stream.audioDone();
520
- stream.end();
740
+ yield emit({ type: "audio-done" });
741
+ yield emit({ type: "finish" });
521
742
  return null;
522
743
  }
523
744
  catch (error) {
@@ -526,9 +747,12 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
526
747
  }
527
748
  else {
528
749
  logger.error(`Agent speech audio streaming failed:\n${error}`);
529
- stream.error(getErrorMessage(error));
750
+ yield emit({
751
+ type: "error",
752
+ error: getErrorMessage(error),
753
+ });
530
754
  }
531
- stream.end();
755
+ yield emit({ type: "finish" });
532
756
  return null;
533
757
  }
534
758
  })
@@ -674,3 +898,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
674
898
  });
675
899
  }
676
900
  }
901
+ function getPartialVegaLiteFenceStartLength(text) {
902
+ for (let length = Math.min(text.length, VEGA_LITE_FENCE_START.length - 1); length > 0; length -= 1) {
903
+ if (VEGA_LITE_FENCE_START.startsWith(text.slice(-length))) {
904
+ return length;
905
+ }
906
+ }
907
+ return 0;
908
+ }
@@ -0,0 +1 @@
1
+ export declare function sanitizeSpeechText(input: string): string;
@@ -0,0 +1,14 @@
1
+ import type { AgentEventEmitter } from "../../agentEvents.js";
2
+ type AgentEventStreamResponse = {
3
+ writeHead: (statusCode: number, headers: Record<string, string>) => void;
4
+ write: (chunk: string) => unknown;
5
+ end: () => unknown;
6
+ writableEnded: boolean;
7
+ destroyed: boolean;
8
+ };
9
+ type AgentEventStreamOptions = {
10
+ vercelAiUiMessageStream?: boolean;
11
+ closeActiveBlockOnToolStart?: boolean;
12
+ };
13
+ export declare function createSseEventEmitter(res: AgentEventStreamResponse, options?: AgentEventStreamOptions): AgentEventEmitter;
14
+ export {};