@adminforth/agent 1.43.28 → 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.
Files changed (37) hide show
  1. package/agent/checkpointer.ts +2 -1
  2. package/agent/systemPrompt.ts +2 -1
  3. package/agentEvents.ts +61 -0
  4. package/agentResponseEvents.ts +1 -206
  5. package/apiBasedTools.ts +7 -3
  6. package/dist/agent/checkpointer.d.ts +29 -0
  7. package/dist/agent/languageDetect.d.ts +10 -0
  8. package/dist/agent/middleware/apiBasedTools.d.ts +3 -0
  9. package/dist/agent/middleware/openAiResponsesContinuation.d.ts +1 -0
  10. package/dist/agent/middleware/sequenceDebug.d.ts +46 -0
  11. package/dist/agent/simpleAgent.d.ts +61 -0
  12. package/dist/agent/skills/registry.d.ts +13 -0
  13. package/dist/agent/systemPrompt.d.ts +11 -0
  14. package/dist/agent/systemPrompt.js +3 -1
  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 +52 -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/apiBasedTools.js +5 -3
  27. package/dist/index.d.ts +58 -0
  28. package/dist/index.js +251 -59
  29. package/dist/sanitizeSpeechText.d.ts +1 -0
  30. package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +14 -0
  31. package/dist/surfaces/web-sse/createSseEventEmitter.js +196 -0
  32. package/dist/types.d.ts +94 -0
  33. package/index.ts +279 -59
  34. package/package.json +2 -2
  35. package/surfaces/web-sse/createSseEventEmitter.ts +261 -0
  36. package/tsconfig.json +2 -1
  37. package/types.ts +6 -0
package/index.ts CHANGED
@@ -1,4 +1,12 @@
1
- import type { AdminUser, AdminForthResource, IAdminForth, IHttpServer } from "adminforth";
1
+ import type {
2
+ AdminUser,
3
+ AdminForthResource,
4
+ ChatSurfaceAdapter,
5
+ ChatSurfaceEventSink,
6
+ ChatSurfaceIncomingMessage,
7
+ IAdminForth,
8
+ IHttpServer,
9
+ } from "adminforth";
2
10
 
3
11
  import { AdminForthPlugin, logger, Filters, Sorts } from "adminforth";
4
12
 
@@ -12,12 +20,14 @@ import { AdminForthCheckpointSaver } from "./agent/checkpointer.js";
12
20
  import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
13
21
  import { detectUserLanguage, type PreviousUserMessage } from "./agent/languageDetect.js";
14
22
  import { prepareApiBasedTools as buildApiBasedTools } from './apiBasedTools.js';
15
- import { createAgentEventStream } from "./agentResponseEvents.js";
23
+ import type { AgentEventEmitter } from "./agentEvents.js";
24
+ import { createSseEventEmitter } from "./surfaces/web-sse/createSseEventEmitter.js";
16
25
  import { appendCustomSystemPrompt, buildAgentSystemPrompt, buildAgentTurnSystemPrompt, DEFAULT_AGENT_SYSTEM_PROMPT} from "./agent/systemPrompt.js";
17
- import type { ToolCallEvent } from "./agent/toolCallEvents.js";
18
26
  import type { CurrentPageContext } from "./agent/tools/getUserLocation.js";
19
27
  import { sanitizeSpeechText } from "./sanitizeSpeechText.js";
20
28
 
29
+ export type { AgentEvent, AgentEventEmitter } from "./agentEvents.js";
30
+
21
31
  type MulterFile = {
22
32
  buffer: Buffer;
23
33
  originalname: string;
@@ -37,18 +47,21 @@ type AgentTurnRunInput = {
37
47
  abortSignal?: AbortSignal;
38
48
  adminUser: AdminUser;
39
49
  sequenceDebugCollector: ReturnType<typeof createSequenceDebugCollector>;
40
- emitReasoningDelta?: (delta: string) => void;
41
- emitTextDelta?: (delta: string) => void;
42
- emitToolCallEvent?: (event: ToolCallEvent) => void;
50
+ emit?: AgentEventEmitter;
43
51
  };
44
52
 
45
53
  type RunAndPersistAgentResponseInput =
46
54
  Omit<AgentTurnRunInput, "turnId" | "sequenceDebugCollector" | "previousUserMessages"> & {
47
- emitErrorResponse?: (response: string) => void;
48
55
  failureLogMessage: string;
49
56
  abortLogMessage: string;
50
57
  };
51
58
 
59
+ type HandleTurnInput = Omit<RunAndPersistAgentResponseInput, "failureLogMessage" | "abortLogMessage"> & {
60
+ emit: AgentEventEmitter;
61
+ failureLogMessage?: string;
62
+ abortLogMessage?: string;
63
+ };
64
+
52
65
  const agentResponseBodySchema = z.object({
53
66
  message: z.string(),
54
67
  sessionId: z.string(),
@@ -91,6 +104,14 @@ function getErrorMessage(error: unknown): string {
91
104
  return error instanceof Error ? error.message : String(error);
92
105
  }
93
106
 
107
+ function requireAdminUser(adminUser: AdminUser | undefined): AdminUser {
108
+ if (!adminUser) {
109
+ throw new Error("AdminForth Agent endpoint requires an authenticated admin user");
110
+ }
111
+
112
+ return adminUser;
113
+ }
114
+
94
115
  export default class AdminForthAgentPlugin extends AdminForthPlugin {
95
116
  options: PluginOptions;
96
117
  agentSystemPromptPromise: Promise<string>;
@@ -98,7 +119,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
98
119
  private parseBody<T>(
99
120
  schema: z.ZodType<T>,
100
121
  body: unknown,
101
- response: { setStatus: (code: number, message?: string) => void },
122
+ response: { setStatus: (code: number, message: string) => void },
102
123
  ): T | null {
103
124
  const parsed = schema.safeParse(body ?? {});
104
125
  if (!parsed.success) {
@@ -146,6 +167,33 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
146
167
  }));
147
168
  }
148
169
 
170
+ private getChatSurfaceSessionId(incoming: ChatSurfaceIncomingMessage) {
171
+ return `${incoming.surface}:${incoming.externalConversationId}`;
172
+ }
173
+
174
+ private async getOrCreateChatSurfaceSession(
175
+ incoming: ChatSurfaceIncomingMessage,
176
+ adminUser: AdminUser,
177
+ ) {
178
+ const sessionId = this.getChatSurfaceSessionId(incoming);
179
+ const sessionResource = this.adminforth.resource(this.options.sessionResource.resourceId);
180
+ const session = await sessionResource.get(
181
+ [Filters.EQ(this.options.sessionResource.idField, sessionId)]
182
+ );
183
+
184
+ if (session) {
185
+ return sessionId;
186
+ }
187
+
188
+ await sessionResource.create({
189
+ [this.options.sessionResource.idField]: sessionId,
190
+ [this.options.sessionResource.titleField]: incoming.prompt.slice(0, 40) || "New Session",
191
+ [this.options.sessionResource.askerIdField]: adminUser.pk,
192
+ });
193
+
194
+ return sessionId;
195
+ }
196
+
149
197
  private getCheckpointer() {
150
198
  if (this.checkpointer) return this.checkpointer;
151
199
 
@@ -215,6 +263,9 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
215
263
 
216
264
  validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
217
265
  this.options.audioAdapter?.validate();
266
+ for (const chatSurfaceAdapter of this.options.chatSurfaceAdapters ?? []) {
267
+ chatSurfaceAdapter.validate();
268
+ }
218
269
  this.agentSystemPromptPromise = buildAgentSystemPrompt(
219
270
  adminforth,
220
271
  this.getInternalAgentResourceIds(),
@@ -258,7 +309,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
258
309
  const systemPrompt = buildAgentTurnSystemPrompt({
259
310
  agentSystemPrompt: await this.agentSystemPromptPromise,
260
311
  adminUser: input.adminUser,
261
- usernameField: this.adminforth.config.auth.usernameField,
312
+ usernameField: this.adminforth.config.auth!.usernameField,
262
313
  userLanguage,
263
314
  });
264
315
  const apiBasedTools = buildApiBasedTools(
@@ -279,7 +330,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
279
330
  adminUser: input.adminUser,
280
331
  adminforth: this.adminforth,
281
332
  apiBasedTools,
282
- customComponentsDir: this.adminforth.config.customization.customComponentsDir,
333
+ customComponentsDir: this.adminforth.config.customization.customComponentsDir ?? "custom",
283
334
  sessionId: input.sessionId,
284
335
  turnId: input.turnId,
285
336
  currentPage: input.currentPage,
@@ -287,7 +338,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
287
338
  abortSignal: input.abortSignal,
288
339
  emitToolCallEvent: (event) => {
289
340
  input.sequenceDebugCollector.handleToolCallEvent(event);
290
- input.emitToolCallEvent?.(event);
341
+ void input.emit?.({
342
+ type: "tool-call",
343
+ data: event,
344
+ });
291
345
  },
292
346
  sequenceDebugSink: input.sequenceDebugCollector,
293
347
  });
@@ -324,12 +378,18 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
324
378
  .join("");
325
379
 
326
380
  if (reasoningDelta) {
327
- input.emitReasoningDelta?.(reasoningDelta);
381
+ await input.emit?.({
382
+ type: "reasoning-delta",
383
+ delta: reasoningDelta,
384
+ });
328
385
  }
329
386
 
330
387
  if (textDelta) {
331
388
  fullResponse += textDelta;
332
- input.emitTextDelta?.(textDelta);
389
+ await input.emit?.({
390
+ type: "text-delta",
391
+ delta: textDelta,
392
+ });
333
393
  }
334
394
  }
335
395
 
@@ -361,9 +421,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
361
421
  abortSignal: input.abortSignal,
362
422
  adminUser: input.adminUser,
363
423
  sequenceDebugCollector,
364
- emitToolCallEvent: input.emitToolCallEvent,
365
- emitReasoningDelta: input.emitReasoningDelta,
366
- emitTextDelta: input.emitTextDelta,
424
+ emit: input.emit,
367
425
  });
368
426
  fullResponse = agentResponse.text;
369
427
  } catch (error) {
@@ -374,7 +432,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
374
432
  failed = true;
375
433
  fullResponse = getErrorMessage(error);
376
434
  logger.error(`${input.failureLogMessage}:\n${fullResponse}`);
377
- input.emitErrorResponse?.(fullResponse);
378
435
  }
379
436
  }
380
437
 
@@ -397,11 +454,140 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
397
454
  };
398
455
  }
399
456
 
457
+ async handleTurn(input: HandleTurnInput) {
458
+ await input.emit({
459
+ type: "turn-started",
460
+ messageId: randomUUID(),
461
+ });
462
+
463
+ const agentResponse = await this.runAndPersistAgentResponse({
464
+ prompt: input.prompt,
465
+ sessionId: input.sessionId,
466
+ modeName: input.modeName,
467
+ userTimeZone: input.userTimeZone,
468
+ currentPage: input.currentPage,
469
+ abortSignal: input.abortSignal,
470
+ adminUser: input.adminUser,
471
+ emit: input.emit,
472
+ failureLogMessage: input.failureLogMessage ?? "Agent response failed",
473
+ abortLogMessage: input.abortLogMessage ?? "Agent response aborted",
474
+ });
475
+
476
+ if (agentResponse.failed) {
477
+ await input.emit({
478
+ type: "error",
479
+ error: agentResponse.text,
480
+ });
481
+ } else if (!agentResponse.aborted) {
482
+ await input.emit({
483
+ type: "response",
484
+ text: agentResponse.text,
485
+ sessionId: input.sessionId,
486
+ turnId: agentResponse.turnId,
487
+ });
488
+ }
489
+
490
+ await input.emit({
491
+ type: "finish",
492
+ });
493
+
494
+ return agentResponse;
495
+ }
496
+
497
+ private createChatSurfaceEventEmitter(sink: ChatSurfaceEventSink): AgentEventEmitter {
498
+ return async (event) => {
499
+ if (event.type === "text-delta") {
500
+ await sink.emit({
501
+ type: "text_delta",
502
+ delta: event.delta,
503
+ });
504
+ return;
505
+ }
506
+
507
+ if (event.type === "response") {
508
+ await sink.emit({
509
+ type: "done",
510
+ text: event.text,
511
+ });
512
+ return;
513
+ }
514
+
515
+ if (event.type === "error") {
516
+ await sink.emit({
517
+ type: "error",
518
+ message: event.error,
519
+ });
520
+ }
521
+ };
522
+ }
523
+
524
+ private async handleChatSurfaceMessage(
525
+ adapter: ChatSurfaceAdapter,
526
+ incoming: ChatSurfaceIncomingMessage,
527
+ sink: ChatSurfaceEventSink,
528
+ ) {
529
+ const adminUser = await adapter.resolveAdminUser({
530
+ adminforth: this.adminforth,
531
+ incoming,
532
+ });
533
+
534
+ if (!adminUser) {
535
+ await sink.emit({
536
+ type: "error",
537
+ message: "This chat account is not authorized to use AdminForth Agent.",
538
+ });
539
+ return;
540
+ }
541
+
542
+ await this.handleTurn({
543
+ prompt: incoming.prompt,
544
+ sessionId: await this.getOrCreateChatSurfaceSession(incoming, adminUser),
545
+ modeName: incoming.modeName,
546
+ userTimeZone: incoming.userTimeZone ?? "UTC",
547
+ adminUser,
548
+ emit: this.createChatSurfaceEventEmitter(sink),
549
+ failureLogMessage: `Agent ${incoming.surface} surface response failed`,
550
+ abortLogMessage: `Agent ${incoming.surface} surface response aborted`,
551
+ });
552
+ }
553
+
400
554
  setupEndpoints(server: IHttpServer) {
555
+ for (const adapter of this.options.chatSurfaceAdapters ?? []) {
556
+ server.endpoint({
557
+ method: "POST",
558
+ noAuth: true,
559
+ path: `/agent/surface/${adapter.name}/webhook`,
560
+ handler: async (ctx) => {
561
+ const surfaceContext = {
562
+ body: ctx.body,
563
+ headers: ctx.headers,
564
+ abortSignal: ctx.abortSignal,
565
+ rawRequest: ctx._raw_express_req,
566
+ rawResponse: ctx._raw_express_res,
567
+ };
568
+ const incoming = await adapter.parseIncomingMessage(surfaceContext);
569
+
570
+ if (!incoming) return { ok: true };
571
+
572
+ const sink = await adapter.createEventSink(surfaceContext, incoming);
573
+
574
+ try {
575
+ await this.handleChatSurfaceMessage(adapter, incoming, sink);
576
+ } finally {
577
+ await sink.close?.();
578
+ }
579
+
580
+ return { ok: true };
581
+ },
582
+ });
583
+ }
584
+
401
585
  server.endpoint({
402
586
  method: 'POST',
403
587
  path: `/agent/get-placeholder-messages`,
404
588
  handler: async ({ headers, adminUser }) => {
589
+ const currentAdminUser = requireAdminUser(adminUser);
590
+
405
591
  if (!this.options.placeholderMessages) {
406
592
  return {
407
593
  messages: [],
@@ -409,7 +595,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
409
595
  }
410
596
 
411
597
  const messages = await this.options.placeholderMessages({
412
- adminUser,
598
+ adminUser: currentAdminUser,
413
599
  headers,
414
600
  });
415
601
 
@@ -422,28 +608,26 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
422
608
  method: 'POST',
423
609
  path: `/agent/response`,
424
610
  handler: async ({ body, adminUser, response, _raw_express_res, abortSignal }) => {
611
+ const currentAdminUser = requireAdminUser(adminUser);
425
612
  const data = this.parseBody(agentResponseBodySchema, body, response);
426
613
  if (!data) return;
427
- const stream = createAgentEventStream(_raw_express_res, {vercelAiUiMessageStream: true, closeActiveBlockOnToolStart: true});
428
- const messageId = randomUUID();
614
+ const emit = createSseEventEmitter(_raw_express_res, {
615
+ vercelAiUiMessageStream: true,
616
+ closeActiveBlockOnToolStart: true,
617
+ });
429
618
 
430
- stream.start(messageId);
431
- await this.runAndPersistAgentResponse({
619
+ await this.handleTurn({
432
620
  prompt: data.message,
433
621
  sessionId: data.sessionId,
434
622
  modeName: data.mode,
435
623
  userTimeZone: data.timeZone ?? 'UTC',
436
624
  currentPage: data.currentPage,
437
625
  abortSignal,
438
- adminUser,
439
- emitToolCallEvent: stream.toolCall,
440
- emitReasoningDelta: stream.reasoningDelta,
441
- emitTextDelta: stream.textDelta,
442
- emitErrorResponse: stream.textDelta,
626
+ adminUser: currentAdminUser,
627
+ emit,
443
628
  failureLogMessage: "Agent response streaming failed",
444
629
  abortLogMessage: "Agent response streaming aborted by the client",
445
630
  });
446
- stream.end();
447
631
  return null;
448
632
  }
449
633
  });
@@ -452,19 +636,20 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
452
636
  path: `/agent/speech-response`,
453
637
  target: 'upload',
454
638
  handler: async ({ body, adminUser, response, _raw_express_req, _raw_express_res, abortSignal }) => {
639
+ const currentAdminUser = requireAdminUser(adminUser);
455
640
  const req = _raw_express_req as ExpressMulterRequest;
456
641
  const audioAdapter = this.options.audioAdapter;
457
642
  if (!audioAdapter) {
458
- response.setStatus(400, undefined);
643
+ response.setStatus(400, "Audio adapter is not configured for AdminForth Agent");
459
644
  return { error: "Audio adapter is not configured for AdminForth Agent" };
460
645
  }
461
646
  const data = this.parseBody(agentSpeechResponseBodySchema, body, response);
462
647
  if (!data) return;
463
648
  if (!req.file) {
464
- response.setStatus(400, undefined);
649
+ response.setStatus(400, "Audio file is required");
465
650
  return { error: "Audio file is required" };
466
651
  }
467
- const stream = createAgentEventStream(_raw_express_res);
652
+ const emit = createSseEventEmitter(_raw_express_res);
468
653
 
469
654
  let transcription;
470
655
 
@@ -479,28 +664,38 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
479
664
  } catch (error) {
480
665
  if (abortSignal.aborted || isAbortError(error)) {
481
666
  logger.info("Agent speech transcription aborted by the client");
482
- stream.end();
667
+ await emit({ type: "finish" });
483
668
  return null;
484
669
  }
485
670
 
486
671
  logger.error(`Agent speech transcription failed:\n${getErrorMessage(error)}`);
487
- stream.error("Speech transcription failed. Check server logs for details.");
488
- stream.end();
672
+ await emit({
673
+ type: "error",
674
+ error: "Speech transcription failed. Check server logs for details.",
675
+ });
676
+ await emit({ type: "finish" });
489
677
  return null;
490
678
  }
491
679
 
492
680
  if (abortSignal.aborted) {
493
- stream.end();
681
+ await emit({ type: "finish" });
494
682
  return null;
495
683
  }
496
684
 
497
685
  const prompt = transcription.text;
498
686
  if (!prompt) {
499
- stream.error("Speech transcription is empty");
500
- stream.end();
687
+ await emit({
688
+ type: "error",
689
+ error: "Speech transcription is empty",
690
+ });
691
+ await emit({ type: "finish" });
501
692
  return null;
502
693
  }
503
- stream.transcript(transcription.text, transcription.language);
694
+ await emit({
695
+ type: "transcript",
696
+ text: transcription.text,
697
+ language: transcription.language,
698
+ });
504
699
 
505
700
  const sessionId = data.sessionId as string;
506
701
  const currentPage = data.currentPage;
@@ -511,35 +706,43 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
511
706
  userTimeZone: data.timeZone ?? 'UTC',
512
707
  currentPage,
513
708
  abortSignal,
514
- adminUser,
515
- emitToolCallEvent: stream.toolCall,
709
+ adminUser: currentAdminUser,
710
+ emit: async (event) => {
711
+ if (event.type === "tool-call") {
712
+ await emit(event);
713
+ }
714
+ },
516
715
  failureLogMessage: "Agent speech response failed",
517
716
  abortLogMessage: "Agent speech response aborted by the client",
518
717
  });
519
718
 
520
719
  if (agentResponse.aborted) {
521
- stream.end();
720
+ await emit({ type: "finish" });
522
721
  return null;
523
722
  }
524
723
 
525
724
  if (agentResponse.failed) {
526
- stream.error(agentResponse.text);
527
- stream.end();
725
+ await emit({
726
+ type: "error",
727
+ error: agentResponse.text,
728
+ });
729
+ await emit({ type: "finish" });
528
730
  return null;
529
731
  }
530
732
 
531
733
  try {
532
- stream.speechResponse(
533
- {
734
+ await emit({
735
+ type: "speech-response",
736
+ transcript: {
534
737
  text: transcription.text,
535
738
  language: transcription.language,
536
739
  },
537
- {
740
+ response: {
538
741
  text: agentResponse.text,
539
742
  },
540
743
  sessionId,
541
- agentResponse.turnId,
542
- );
744
+ turnId: agentResponse.turnId,
745
+ });
543
746
  const speech = await audioAdapter.synthesize({
544
747
  text: sanitizeSpeechText(agentResponse.text),
545
748
  stream: true,
@@ -548,7 +751,14 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
548
751
  abortSignal,
549
752
  });
550
753
 
551
- stream.audioStart(speech.mimeType, speech.format, 24000, 1, 16);
754
+ await emit({
755
+ type: "audio-start",
756
+ mimeType: speech.mimeType,
757
+ format: speech.format,
758
+ sampleRate: 24000,
759
+ channelCount: 1,
760
+ bitsPerSample: 16,
761
+ });
552
762
 
553
763
  const reader = speech.audioStream.getReader();
554
764
  const cancelAudioStream = () => {
@@ -574,24 +784,30 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
574
784
  break;
575
785
  }
576
786
 
577
- stream.audioDelta(value);
787
+ await emit({
788
+ type: "audio-delta",
789
+ value,
790
+ });
578
791
  }
579
792
  } finally {
580
793
  abortSignal.removeEventListener("abort", cancelAudioStream);
581
794
  reader.releaseLock();
582
795
  }
583
796
 
584
- stream.audioDone();
585
- stream.end();
797
+ await emit({ type: "audio-done" });
798
+ await emit({ type: "finish" });
586
799
  return null;
587
800
  } catch (error) {
588
801
  if (abortSignal.aborted || isAbortError(error)) {
589
802
  logger.info("Agent speech audio streaming aborted by the client");
590
803
  } else {
591
804
  logger.error(`Agent speech audio streaming failed:\n${error}`);
592
- stream.error(error);
805
+ await emit({
806
+ type: "error",
807
+ error: getErrorMessage(error),
808
+ });
593
809
  }
594
- stream.end();
810
+ await emit({ type: "finish" });
595
811
  return null;
596
812
  }
597
813
  }
@@ -600,9 +816,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
600
816
  method: 'POST',
601
817
  path: `/agent/get-sessions`,
602
818
  handler: async ({body, adminUser, response }) => {
819
+ const currentAdminUser = requireAdminUser(adminUser);
603
820
  const data = this.parseBody(getSessionsBodySchema, body, response);
604
821
  if (!data) return;
605
- const userId = adminUser.pk;
822
+ const userId = currentAdminUser.pk;
606
823
  const limit = data.limit ?? 20;
607
824
  const sessions = await this.adminforth.resource(this.options.sessionResource.resourceId).list(
608
825
  [Filters.EQ(this.options.sessionResource.askerIdField, userId)], limit, undefined, [Sorts.DESC(this.options.sessionResource.createdAtField)]
@@ -620,12 +837,13 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
620
837
  method: 'POST',
621
838
  path: `/agent/get-session-info`,
622
839
  handler: async ({body, adminUser, response }) => {
840
+ const currentAdminUser = requireAdminUser(adminUser);
623
841
  const parsedBody = sessionIdBodySchema.safeParse(body);
624
842
  if (!parsedBody.success) {
625
843
  response.setStatus(422, parsedBody.error.message);
626
844
  return;
627
845
  }
628
- const userId = adminUser.pk;
846
+ const userId = currentAdminUser.pk;
629
847
  const sessionId = parsedBody.data.sessionId;
630
848
  const session = await this.adminforth.resource(this.options.sessionResource.resourceId).get(
631
849
  [Filters.EQ(this.options.sessionResource.idField, sessionId)]
@@ -647,7 +865,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
647
865
  title: session[this.options.sessionResource.titleField],
648
866
  timestamp: session[this.options.sessionResource.createdAtField],
649
867
  messages: turns.flatMap(turn => {
650
- const messages = [];
868
+ const messages: Array<{ text: string; role: 'user' | 'assistant' }> = [];
651
869
  if (turn.prompt) {
652
870
  messages.push({
653
871
  text: turn.prompt,
@@ -670,10 +888,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
670
888
  method: 'POST',
671
889
  path: `/agent/create-session`,
672
890
  handler: async ({body, adminUser, response }) => {
891
+ const currentAdminUser = requireAdminUser(adminUser);
673
892
  const data = this.parseBody(createSessionBodySchema, body, response);
674
893
  if (!data) return;
675
894
  const triggerMessage = data.triggerMessage;
676
- const userId = adminUser.pk;
895
+ const userId = currentAdminUser.pk;
677
896
  const title = triggerMessage?.slice(0, 40) || "New Session";
678
897
  const newSession = {
679
898
  [this.options.sessionResource.idField]: randomUUID(),
@@ -693,10 +912,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
693
912
  method: 'POST',
694
913
  path: `/agent/delete-session`,
695
914
  handler: async ({body, adminUser, response }) => {
915
+ const currentAdminUser = requireAdminUser(adminUser);
696
916
  const data = this.parseBody(sessionIdBodySchema, body, response);
697
917
  if (!data) return;
698
918
  const sessionId = data.sessionId;
699
- const userId = adminUser.pk;
919
+ const userId = currentAdminUser.pk;
700
920
  const session = await this.adminforth.resource(this.options.sessionResource.resourceId).get(
701
921
  [Filters.EQ(this.options.sessionResource.idField, sessionId)]
702
922
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/agent",
3
- "version": "1.43.28",
3
+ "version": "1.44.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -33,7 +33,7 @@
33
33
  "@langchain/core": "^1.1.40",
34
34
  "@langchain/langgraph": "^1.2.8",
35
35
  "@langchain/langgraph-checkpoint": "^1.0.1",
36
- "adminforth": "^2.53.5",
36
+ "adminforth": ">=2.54.0",
37
37
  "dayjs": "^1.11.20",
38
38
  "langchain": "^1.3.3",
39
39
  "multer": "^2.1.1",