@adminforth/agent 1.43.29 → 1.44.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.
@@ -1,144 +1 @@
1
- import { randomUUID } from "crypto";
2
- export function createAgentEventStream(res, options = {}) {
3
- let isStreamClosed = false;
4
- let activeBlock = null;
5
- res.writeHead(200, Object.assign({ "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "Connection": "keep-alive" }, (options.vercelAiUiMessageStream
6
- ? { "x-vercel-ai-ui-message-stream": "v1" }
7
- : {})));
8
- const stream = {
9
- send(obj) {
10
- if (isStreamClosed || res.writableEnded || res.destroyed) {
11
- return;
12
- }
13
- res.write(`data: ${JSON.stringify(obj)}\n\n`);
14
- },
15
- endActiveBlock() {
16
- if (!activeBlock) {
17
- return;
18
- }
19
- stream.send({
20
- type: `${activeBlock.type}-end`,
21
- id: activeBlock.id,
22
- });
23
- activeBlock = null;
24
- },
25
- startBlock(type) {
26
- if ((activeBlock === null || activeBlock === void 0 ? void 0 : activeBlock.type) === type) {
27
- return activeBlock.id;
28
- }
29
- stream.endActiveBlock();
30
- const id = randomUUID();
31
- activeBlock = { type, id };
32
- stream.send({
33
- type: `${type}-start`,
34
- id,
35
- });
36
- return id;
37
- },
38
- start(messageId) {
39
- stream.send({
40
- type: "start",
41
- messageId,
42
- });
43
- },
44
- textDelta(delta) {
45
- const textId = stream.startBlock("text");
46
- stream.send({
47
- type: "text-delta",
48
- id: textId,
49
- delta,
50
- });
51
- },
52
- reasoningDelta(delta) {
53
- const reasoningId = stream.startBlock("reasoning");
54
- stream.send({
55
- type: "reasoning-delta",
56
- id: reasoningId,
57
- delta,
58
- });
59
- },
60
- toolCall(event) {
61
- if (options.closeActiveBlockOnToolStart && event.phase === "start") {
62
- stream.endActiveBlock();
63
- }
64
- stream.send({
65
- type: "data-tool-call",
66
- data: event,
67
- });
68
- },
69
- transcript(text, language) {
70
- stream.send({
71
- type: "transcript",
72
- data: {
73
- text,
74
- language,
75
- },
76
- });
77
- },
78
- response(text, sessionId, turnId) {
79
- stream.send({
80
- type: "response",
81
- data: {
82
- text,
83
- sessionId,
84
- turnId,
85
- },
86
- });
87
- },
88
- speechResponse(transcript, response, sessionId, turnId) {
89
- stream.send({
90
- type: "speech-response",
91
- data: {
92
- transcript,
93
- response,
94
- sessionId,
95
- turnId,
96
- },
97
- });
98
- },
99
- audioStart(mimeType, format, sampleRate, channelCount, bitsPerSample) {
100
- stream.send({
101
- type: "audio-start",
102
- data: {
103
- mimeType,
104
- format,
105
- sampleRate,
106
- channelCount,
107
- bitsPerSample,
108
- },
109
- });
110
- },
111
- audioDelta(value) {
112
- stream.send({
113
- type: "audio-delta",
114
- data: {
115
- base64: Buffer.from(value).toString("base64"),
116
- },
117
- });
118
- },
119
- audioDone() {
120
- stream.send({
121
- type: "audio-done",
122
- });
123
- },
124
- error(error) {
125
- stream.send({
126
- type: "error",
127
- error,
128
- });
129
- },
130
- end() {
131
- if (isStreamClosed || res.writableEnded || res.destroyed) {
132
- return;
133
- }
134
- stream.endActiveBlock();
135
- stream.send({
136
- type: "finish",
137
- });
138
- res.write("data: [DONE]\n\n");
139
- isStreamClosed = true;
140
- res.end();
141
- },
142
- };
143
- return stream;
144
- }
1
+ export { createSseEventEmitter } from "./surfaces/web-sse/createSseEventEmitter.js";
@@ -0,0 +1,29 @@
1
+ import { type AdminUser, type IAdminForth } from 'adminforth';
2
+ export type ApiBasedToolCallParams = {
3
+ adminUser?: AdminUser;
4
+ adminuser?: AdminUser;
5
+ abortSignal?: AbortSignal;
6
+ inputs?: Record<string, unknown>;
7
+ userTimeZone?: string;
8
+ acceptLanguage?: string;
9
+ };
10
+ export type ApiBasedTool = {
11
+ description?: string;
12
+ input_schema?: unknown;
13
+ output_schema?: unknown;
14
+ call: (params?: ApiBasedToolCallParams) => Promise<string>;
15
+ };
16
+ export declare function formatApiBasedToolCall(params: {
17
+ adminforth: IAdminForth;
18
+ adminUser?: AdminUser;
19
+ inputs?: Record<string, unknown>;
20
+ toolName: string;
21
+ userTimeZone?: string;
22
+ }): Promise<string | undefined>;
23
+ export declare function prepareApiBasedTools(adminforth: IAdminForth, hiddenResourceIds?: Iterable<string>): Record<string, ApiBasedTool>;
24
+ export declare function serializeApiBasedTool(tool: ApiBasedTool | undefined): {
25
+ description: string | undefined;
26
+ input_schema: unknown;
27
+ output_schema: unknown;
28
+ call: string;
29
+ } | null;
@@ -0,0 +1,58 @@
1
+ import type { AdminUser, AdminForthResource, IAdminForth, IHttpServer } from "adminforth";
2
+ import { AdminForthPlugin } from "adminforth";
3
+ import type { PluginOptions } from './types.js';
4
+ import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
5
+ import { type PreviousUserMessage } from "./agent/languageDetect.js";
6
+ import type { AgentEventEmitter } from "./agentEvents.js";
7
+ import type { CurrentPageContext } from "./agent/tools/getUserLocation.js";
8
+ export type { AgentEvent, AgentEventEmitter } from "./agentEvents.js";
9
+ type AgentTurnRunInput = {
10
+ prompt: string;
11
+ sessionId: string;
12
+ turnId: string;
13
+ previousUserMessages: PreviousUserMessage[];
14
+ modeName?: string | null;
15
+ userTimeZone: string;
16
+ currentPage?: CurrentPageContext;
17
+ abortSignal?: AbortSignal;
18
+ adminUser: AdminUser;
19
+ sequenceDebugCollector: ReturnType<typeof createSequenceDebugCollector>;
20
+ emit?: AgentEventEmitter;
21
+ };
22
+ type RunAndPersistAgentResponseInput = Omit<AgentTurnRunInput, "turnId" | "sequenceDebugCollector" | "previousUserMessages"> & {
23
+ failureLogMessage: string;
24
+ abortLogMessage: string;
25
+ };
26
+ type HandleTurnInput = Omit<RunAndPersistAgentResponseInput, "failureLogMessage" | "abortLogMessage"> & {
27
+ emit: AgentEventEmitter;
28
+ failureLogMessage?: string;
29
+ abortLogMessage?: string;
30
+ };
31
+ export default class AdminForthAgentPlugin extends AdminForthPlugin {
32
+ options: PluginOptions;
33
+ agentSystemPromptPromise: Promise<string>;
34
+ private checkpointer;
35
+ private parseBody;
36
+ private createNewTurn;
37
+ private getSessionTurns;
38
+ private getPreviousUserMessages;
39
+ private getChatSurfaceSessionId;
40
+ private getOrCreateChatSurfaceSession;
41
+ private getCheckpointer;
42
+ private getInternalAgentResourceIds;
43
+ constructor(options: PluginOptions);
44
+ modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource): Promise<void>;
45
+ validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource): void;
46
+ instanceUniqueRepresentation(pluginOptions: any): string;
47
+ private runAgentTurn;
48
+ private runAndPersistAgentResponse;
49
+ handleTurn(input: HandleTurnInput): Promise<{
50
+ text: string;
51
+ turnId: any;
52
+ aborted: boolean;
53
+ failed: boolean;
54
+ }>;
55
+ private createChatSurfaceEventEmitter;
56
+ private handleChatSurfaceMessage;
57
+ setupEndpoints(server: IHttpServer): void;
58
+ }
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({
@@ -104,6 +104,25 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
104
104
  }));
105
105
  });
106
106
  }
107
+ getChatSurfaceSessionId(incoming) {
108
+ return `${incoming.surface}:${incoming.externalConversationId}`;
109
+ }
110
+ getOrCreateChatSurfaceSession(incoming, adminUser) {
111
+ return __awaiter(this, void 0, void 0, function* () {
112
+ const sessionId = this.getChatSurfaceSessionId(incoming);
113
+ const sessionResource = this.adminforth.resource(this.options.sessionResource.resourceId);
114
+ const session = yield sessionResource.get([Filters.EQ(this.options.sessionResource.idField, sessionId)]);
115
+ if (session) {
116
+ return sessionId;
117
+ }
118
+ yield sessionResource.create({
119
+ [this.options.sessionResource.idField]: sessionId,
120
+ [this.options.sessionResource.titleField]: incoming.prompt.slice(0, 40) || "New Session",
121
+ [this.options.sessionResource.askerIdField]: adminUser.pk,
122
+ });
123
+ return sessionId;
124
+ });
125
+ }
107
126
  getCheckpointer() {
108
127
  if (this.checkpointer)
109
128
  return this.checkpointer;
@@ -170,8 +189,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
170
189
  });
171
190
  }
172
191
  validateConfigAfterDiscover(adminforth, resourceConfig) {
173
- var _a;
192
+ var _a, _b;
174
193
  (_a = this.options.audioAdapter) === null || _a === void 0 ? void 0 : _a.validate();
194
+ for (const chatSurfaceAdapter of (_b = this.options.chatSurfaceAdapters) !== null && _b !== void 0 ? _b : []) {
195
+ chatSurfaceAdapter.validate();
196
+ }
175
197
  this.agentSystemPromptPromise = buildAgentSystemPrompt(adminforth, this.getInternalAgentResourceIds())
176
198
  .then((systemPrompt) => appendCustomSystemPrompt(systemPrompt, this.options.systemPrompt));
177
199
  }
@@ -238,7 +260,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
238
260
  emitToolCallEvent: (event) => {
239
261
  var _a;
240
262
  input.sequenceDebugCollector.handleToolCallEvent(event);
241
- (_a = input.emitToolCallEvent) === null || _a === void 0 ? void 0 : _a.call(input, event);
263
+ void ((_a = input.emit) === null || _a === void 0 ? void 0 : _a.call(input, {
264
+ type: "tool-call",
265
+ data: event,
266
+ }));
242
267
  },
243
268
  sequenceDebugSink: input.sequenceDebugCollector,
244
269
  });
@@ -271,11 +296,17 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
271
296
  .map((b) => { var _a; return String((_a = b.text) !== null && _a !== void 0 ? _a : ""); })
272
297
  .join("");
273
298
  if (reasoningDelta) {
274
- (_h = input.emitReasoningDelta) === null || _h === void 0 ? void 0 : _h.call(input, reasoningDelta);
299
+ yield ((_h = input.emit) === null || _h === void 0 ? void 0 : _h.call(input, {
300
+ type: "reasoning-delta",
301
+ delta: reasoningDelta,
302
+ }));
275
303
  }
276
304
  if (textDelta) {
277
305
  fullResponse += textDelta;
278
- (_j = input.emitTextDelta) === null || _j === void 0 ? void 0 : _j.call(input, textDelta);
306
+ yield ((_j = input.emit) === null || _j === void 0 ? void 0 : _j.call(input, {
307
+ type: "text-delta",
308
+ delta: textDelta,
309
+ }));
279
310
  }
280
311
  }
281
312
  }
@@ -293,7 +324,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
293
324
  }
294
325
  runAndPersistAgentResponse(input) {
295
326
  return __awaiter(this, void 0, void 0, function* () {
296
- var _a, _b;
327
+ var _a;
297
328
  const previousUserMessages = yield this.getPreviousUserMessages(input.sessionId);
298
329
  const turnId = yield this.createNewTurn(input.sessionId, input.prompt);
299
330
  yield this.adminforth.resource(this.options.sessionResource.resourceId).update(input.sessionId, {
@@ -315,9 +346,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
315
346
  abortSignal: input.abortSignal,
316
347
  adminUser: input.adminUser,
317
348
  sequenceDebugCollector,
318
- emitToolCallEvent: input.emitToolCallEvent,
319
- emitReasoningDelta: input.emitReasoningDelta,
320
- emitTextDelta: input.emitTextDelta,
349
+ emit: input.emit,
321
350
  });
322
351
  fullResponse = agentResponse.text;
323
352
  }
@@ -330,7 +359,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
330
359
  failed = true;
331
360
  fullResponse = getErrorMessage(error);
332
361
  logger.error(`${input.failureLogMessage}:\n${fullResponse}`);
333
- (_b = input.emitErrorResponse) === null || _b === void 0 ? void 0 : _b.call(input, fullResponse);
334
362
  }
335
363
  }
336
364
  sequenceDebugCollector.flush();
@@ -349,7 +377,125 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
349
377
  };
350
378
  });
351
379
  }
380
+ handleTurn(input) {
381
+ return __awaiter(this, void 0, void 0, function* () {
382
+ var _a, _b;
383
+ yield input.emit({
384
+ type: "turn-started",
385
+ messageId: randomUUID(),
386
+ });
387
+ const agentResponse = yield this.runAndPersistAgentResponse({
388
+ prompt: input.prompt,
389
+ sessionId: input.sessionId,
390
+ modeName: input.modeName,
391
+ userTimeZone: input.userTimeZone,
392
+ currentPage: input.currentPage,
393
+ abortSignal: input.abortSignal,
394
+ adminUser: input.adminUser,
395
+ emit: input.emit,
396
+ failureLogMessage: (_a = input.failureLogMessage) !== null && _a !== void 0 ? _a : "Agent response failed",
397
+ abortLogMessage: (_b = input.abortLogMessage) !== null && _b !== void 0 ? _b : "Agent response aborted",
398
+ });
399
+ if (agentResponse.failed) {
400
+ yield input.emit({
401
+ type: "error",
402
+ error: agentResponse.text,
403
+ });
404
+ }
405
+ else if (!agentResponse.aborted) {
406
+ yield input.emit({
407
+ type: "response",
408
+ text: agentResponse.text,
409
+ sessionId: input.sessionId,
410
+ turnId: agentResponse.turnId,
411
+ });
412
+ }
413
+ yield input.emit({
414
+ type: "finish",
415
+ });
416
+ return agentResponse;
417
+ });
418
+ }
419
+ createChatSurfaceEventEmitter(sink) {
420
+ return (event) => __awaiter(this, void 0, void 0, function* () {
421
+ if (event.type === "text-delta") {
422
+ yield sink.emit({
423
+ type: "text_delta",
424
+ delta: event.delta,
425
+ });
426
+ return;
427
+ }
428
+ if (event.type === "response") {
429
+ yield sink.emit({
430
+ type: "done",
431
+ text: event.text,
432
+ });
433
+ return;
434
+ }
435
+ if (event.type === "error") {
436
+ yield sink.emit({
437
+ type: "error",
438
+ message: event.error,
439
+ });
440
+ }
441
+ });
442
+ }
443
+ handleChatSurfaceMessage(adapter, incoming, sink) {
444
+ return __awaiter(this, void 0, void 0, function* () {
445
+ var _a;
446
+ const adminUser = yield adapter.resolveAdminUser({
447
+ adminforth: this.adminforth,
448
+ incoming,
449
+ });
450
+ if (!adminUser) {
451
+ yield sink.emit({
452
+ type: "error",
453
+ message: "This chat account is not authorized to use AdminForth Agent.",
454
+ });
455
+ return;
456
+ }
457
+ yield this.handleTurn({
458
+ prompt: incoming.prompt,
459
+ sessionId: yield this.getOrCreateChatSurfaceSession(incoming, adminUser),
460
+ modeName: incoming.modeName,
461
+ userTimeZone: (_a = incoming.userTimeZone) !== null && _a !== void 0 ? _a : "UTC",
462
+ adminUser,
463
+ emit: this.createChatSurfaceEventEmitter(sink),
464
+ failureLogMessage: `Agent ${incoming.surface} surface response failed`,
465
+ abortLogMessage: `Agent ${incoming.surface} surface response aborted`,
466
+ });
467
+ });
468
+ }
352
469
  setupEndpoints(server) {
470
+ var _a;
471
+ for (const adapter of (_a = this.options.chatSurfaceAdapters) !== null && _a !== void 0 ? _a : []) {
472
+ server.endpoint({
473
+ method: "POST",
474
+ noAuth: true,
475
+ path: `/agent/surface/${adapter.name}/webhook`,
476
+ handler: (ctx) => __awaiter(this, void 0, void 0, function* () {
477
+ var _a;
478
+ const surfaceContext = {
479
+ body: ctx.body,
480
+ headers: ctx.headers,
481
+ abortSignal: ctx.abortSignal,
482
+ rawRequest: ctx._raw_express_req,
483
+ rawResponse: ctx._raw_express_res,
484
+ };
485
+ const incoming = yield adapter.parseIncomingMessage(surfaceContext);
486
+ if (!incoming)
487
+ return { ok: true };
488
+ const sink = yield adapter.createEventSink(surfaceContext, incoming);
489
+ try {
490
+ yield this.handleChatSurfaceMessage(adapter, incoming, sink);
491
+ }
492
+ finally {
493
+ yield ((_a = sink.close) === null || _a === void 0 ? void 0 : _a.call(sink));
494
+ }
495
+ return { ok: true };
496
+ }),
497
+ });
498
+ }
353
499
  server.endpoint({
354
500
  method: 'POST',
355
501
  path: `/agent/get-placeholder-messages`,
@@ -378,10 +524,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
378
524
  const data = this.parseBody(agentResponseBodySchema, body, response);
379
525
  if (!data)
380
526
  return;
381
- const stream = createAgentEventStream(_raw_express_res, { vercelAiUiMessageStream: true, closeActiveBlockOnToolStart: true });
382
- const messageId = randomUUID();
383
- stream.start(messageId);
384
- yield this.runAndPersistAgentResponse({
527
+ const emit = createSseEventEmitter(_raw_express_res, {
528
+ vercelAiUiMessageStream: true,
529
+ closeActiveBlockOnToolStart: true,
530
+ });
531
+ yield this.handleTurn({
385
532
  prompt: data.message,
386
533
  sessionId: data.sessionId,
387
534
  modeName: data.mode,
@@ -389,14 +536,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
389
536
  currentPage: data.currentPage,
390
537
  abortSignal,
391
538
  adminUser: currentAdminUser,
392
- emitToolCallEvent: stream.toolCall,
393
- emitReasoningDelta: stream.reasoningDelta,
394
- emitTextDelta: stream.textDelta,
395
- emitErrorResponse: stream.textDelta,
539
+ emit,
396
540
  failureLogMessage: "Agent response streaming failed",
397
541
  abortLogMessage: "Agent response streaming aborted by the client",
398
542
  });
399
- stream.end();
400
543
  return null;
401
544
  })
402
545
  });
@@ -420,7 +563,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
420
563
  response.setStatus(400, "Audio file is required");
421
564
  return { error: "Audio file is required" };
422
565
  }
423
- const stream = createAgentEventStream(_raw_express_res);
566
+ const emit = createSseEventEmitter(_raw_express_res);
424
567
  let transcription;
425
568
  try {
426
569
  transcription = yield audioAdapter.transcribe({
@@ -434,25 +577,35 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
434
577
  catch (error) {
435
578
  if (abortSignal.aborted || isAbortError(error)) {
436
579
  logger.info("Agent speech transcription aborted by the client");
437
- stream.end();
580
+ yield emit({ type: "finish" });
438
581
  return null;
439
582
  }
440
583
  logger.error(`Agent speech transcription failed:\n${getErrorMessage(error)}`);
441
- stream.error("Speech transcription failed. Check server logs for details.");
442
- stream.end();
584
+ yield emit({
585
+ type: "error",
586
+ error: "Speech transcription failed. Check server logs for details.",
587
+ });
588
+ yield emit({ type: "finish" });
443
589
  return null;
444
590
  }
445
591
  if (abortSignal.aborted) {
446
- stream.end();
592
+ yield emit({ type: "finish" });
447
593
  return null;
448
594
  }
449
595
  const prompt = transcription.text;
450
596
  if (!prompt) {
451
- stream.error("Speech transcription is empty");
452
- stream.end();
597
+ yield emit({
598
+ type: "error",
599
+ error: "Speech transcription is empty",
600
+ });
601
+ yield emit({ type: "finish" });
453
602
  return null;
454
603
  }
455
- stream.transcript(transcription.text, transcription.language);
604
+ yield emit({
605
+ type: "transcript",
606
+ text: transcription.text,
607
+ language: transcription.language,
608
+ });
456
609
  const sessionId = data.sessionId;
457
610
  const currentPage = data.currentPage;
458
611
  const agentResponse = yield this.runAndPersistAgentResponse({
@@ -463,26 +616,39 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
463
616
  currentPage,
464
617
  abortSignal,
465
618
  adminUser: currentAdminUser,
466
- emitToolCallEvent: stream.toolCall,
619
+ emit: (event) => __awaiter(this, void 0, void 0, function* () {
620
+ if (event.type === "tool-call") {
621
+ yield emit(event);
622
+ }
623
+ }),
467
624
  failureLogMessage: "Agent speech response failed",
468
625
  abortLogMessage: "Agent speech response aborted by the client",
469
626
  });
470
627
  if (agentResponse.aborted) {
471
- stream.end();
628
+ yield emit({ type: "finish" });
472
629
  return null;
473
630
  }
474
631
  if (agentResponse.failed) {
475
- stream.error(agentResponse.text);
476
- stream.end();
632
+ yield emit({
633
+ type: "error",
634
+ error: agentResponse.text,
635
+ });
636
+ yield emit({ type: "finish" });
477
637
  return null;
478
638
  }
479
639
  try {
480
- stream.speechResponse({
481
- text: transcription.text,
482
- language: transcription.language,
483
- }, {
484
- text: agentResponse.text,
485
- }, sessionId, agentResponse.turnId);
640
+ yield emit({
641
+ type: "speech-response",
642
+ transcript: {
643
+ text: transcription.text,
644
+ language: transcription.language,
645
+ },
646
+ response: {
647
+ text: agentResponse.text,
648
+ },
649
+ sessionId,
650
+ turnId: agentResponse.turnId,
651
+ });
486
652
  const speech = yield audioAdapter.synthesize({
487
653
  text: sanitizeSpeechText(agentResponse.text),
488
654
  stream: true,
@@ -490,7 +656,14 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
490
656
  format: "pcm",
491
657
  abortSignal,
492
658
  });
493
- stream.audioStart(speech.mimeType, speech.format, 24000, 1, 16);
659
+ yield emit({
660
+ type: "audio-start",
661
+ mimeType: speech.mimeType,
662
+ format: speech.format,
663
+ sampleRate: 24000,
664
+ channelCount: 1,
665
+ bitsPerSample: 16,
666
+ });
494
667
  const reader = speech.audioStream.getReader();
495
668
  const cancelAudioStream = () => {
496
669
  void reader.cancel().catch(() => undefined);
@@ -509,15 +682,18 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
509
682
  if (abortSignal.aborted) {
510
683
  break;
511
684
  }
512
- stream.audioDelta(value);
685
+ yield emit({
686
+ type: "audio-delta",
687
+ value,
688
+ });
513
689
  }
514
690
  }
515
691
  finally {
516
692
  abortSignal.removeEventListener("abort", cancelAudioStream);
517
693
  reader.releaseLock();
518
694
  }
519
- stream.audioDone();
520
- stream.end();
695
+ yield emit({ type: "audio-done" });
696
+ yield emit({ type: "finish" });
521
697
  return null;
522
698
  }
523
699
  catch (error) {
@@ -526,9 +702,12 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
526
702
  }
527
703
  else {
528
704
  logger.error(`Agent speech audio streaming failed:\n${error}`);
529
- stream.error(getErrorMessage(error));
705
+ yield emit({
706
+ type: "error",
707
+ error: getErrorMessage(error),
708
+ });
530
709
  }
531
- stream.end();
710
+ yield emit({ type: "finish" });
532
711
  return null;
533
712
  }
534
713
  })
@@ -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 {};