@adminforth/agent 1.24.14 → 1.26.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.
@@ -4,6 +4,7 @@ import {
4
4
  logger,
5
5
  type AdminUser,
6
6
  type CompletionAdapter,
7
+ type HttpExtra,
7
8
  type IAdminForth,
8
9
  } from "adminforth";
9
10
  import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
@@ -24,6 +25,7 @@ export const contextSchema = z.object({
24
25
  userTimeZone: z.string(),
25
26
  sessionId: z.string(),
26
27
  turnId: z.string(),
28
+ httpExtra: z.custom<Partial<HttpExtra>>().optional(),
27
29
  emitToolCallEvent: z.custom<ToolCallEventSink>(),
28
30
  });
29
31
 
@@ -229,6 +231,7 @@ export async function callAgent(params: {
229
231
  customComponentsDir: string;
230
232
  sessionId: string;
231
233
  turnId: string;
234
+ httpExtra?: Partial<HttpExtra>;
232
235
  userTimeZone: string;
233
236
  emitToolCallEvent: ToolCallEventSink;
234
237
  sequenceDebugSink: SequenceDebugModelCallSink;
@@ -246,6 +249,7 @@ export async function callAgent(params: {
246
249
  customComponentsDir,
247
250
  sessionId,
248
251
  turnId,
252
+ httpExtra,
249
253
  userTimeZone,
250
254
  emitToolCallEvent,
251
255
  sequenceDebugSink,
@@ -289,6 +293,7 @@ export async function callAgent(params: {
289
293
  userTimeZone,
290
294
  sessionId,
291
295
  turnId,
296
+ httpExtra,
292
297
  emitToolCallEvent,
293
298
  },
294
299
  });
@@ -51,6 +51,7 @@ export function createApiTool(toolName: string, apiBasedTool: ApiBasedTool) {
51
51
  const normalizedInput = (input ?? {}) as Record<string, unknown>;
52
52
  return apiBasedTool.call({
53
53
  adminUser: runtime.context.adminUser,
54
+ httpExtra: runtime.context.httpExtra,
54
55
  inputs: normalizedInput,
55
56
  userTimeZone: runtime.context.userTimeZone,
56
57
  });
package/apiBasedTools.ts CHANGED
@@ -1,16 +1,14 @@
1
1
  import {
2
2
  AdminForthDataTypes,
3
+ logger,
3
4
  type AdminUser,
4
5
  type HttpExtra,
5
6
  type IAdminForth,
6
- type IAdminForthHttpResponse,
7
- type IHttpServer,
8
7
  type IRegisteredApiSchema,
9
8
  } from 'adminforth';
10
9
  import dayjs from 'dayjs';
11
10
  import timezone from 'dayjs/plugin/timezone.js';
12
11
  import utc from 'dayjs/plugin/utc.js';
13
- import { PassThrough } from 'stream';
14
12
  import { inspect } from 'util';
15
13
  import YAML from 'yaml';
16
14
 
@@ -22,47 +20,6 @@ type CookieItem = {
22
20
  value: string;
23
21
  };
24
22
 
25
- type CapturedEndpointHandlerInput = {
26
- body: Record<string, unknown>;
27
- adminUser?: AdminUser;
28
- query: Record<string, string>;
29
- headers: Record<string, any>;
30
- cookies: CookieItem[];
31
- response: IAdminForthHttpResponse;
32
- requestUrl: string;
33
- abortSignal: AbortSignal;
34
- _raw_express_req: any;
35
- _raw_express_res: any;
36
- tr: (
37
- msg: string,
38
- category: string,
39
- params: any,
40
- pluralizationNumber?: number,
41
- ) => Promise<string>;
42
- };
43
-
44
- type EndpointWithSchemas = {
45
- method: string;
46
- noAuth?: boolean;
47
- path: string;
48
- description?: string;
49
- request_schema?: unknown;
50
- response_schema?: unknown;
51
- responce_schema?: unknown;
52
- handler: (input: CapturedEndpointHandlerInput) => Promise<any> | any;
53
- };
54
-
55
- type CapturedEndpoint = EndpointWithSchemas & {
56
- normalizedResponseSchema?: unknown;
57
- };
58
-
59
- type ToolHttpResponse = IAdminForthHttpResponse & {
60
- headers: Array<[string, string]>;
61
- jsonPayload?: unknown;
62
- status: number;
63
- message?: string;
64
- };
65
-
66
23
  type ToolOverrideCallParams = Pick<ApiBasedToolCallParams, 'httpExtra' | 'inputs' | 'userTimeZone'>;
67
24
 
68
25
  type ToolOverrideContext = {
@@ -388,9 +345,9 @@ function formatDateTimeColumns(
388
345
  async function applyToolOverride(params: {
389
346
  adminforth: IAdminForth;
390
347
  adminUser?: AdminUser;
391
- capturedEndpointsByToolName: Record<string, CapturedEndpoint>;
392
348
  httpExtra?: Partial<HttpExtra>;
393
349
  inputs?: Record<string, unknown>;
350
+ invokeTool: (toolName: string, params?: ToolOverrideCallParams) => Promise<unknown>;
394
351
  output: unknown;
395
352
  toolName: string;
396
353
  userTimeZone?: string;
@@ -398,9 +355,9 @@ async function applyToolOverride(params: {
398
355
  const {
399
356
  adminforth,
400
357
  adminUser,
401
- capturedEndpointsByToolName,
402
358
  httpExtra,
403
359
  inputs,
360
+ invokeTool,
404
361
  output,
405
362
  toolName,
406
363
  userTimeZone,
@@ -427,19 +384,10 @@ async function applyToolOverride(params: {
427
384
  inputs,
428
385
  userTimeZone,
429
386
  invokeTool: async (nestedToolName, nestedParams = {}) => {
430
- const nestedEndpoint = capturedEndpointsByToolName[nestedToolName];
431
-
432
- if (!nestedEndpoint) {
433
- throw new Error(`Tool ${nestedToolName} is not registered`);
434
- }
435
-
436
387
  const nestedInputs = nestedParams.inputs ?? inputs;
437
388
  const nestedHttpExtra = nestedParams.httpExtra ?? httpExtra;
438
389
  const nestedUserTimeZone = nestedParams.userTimeZone ?? userTimeZone;
439
- const nestedOutput = await callCapturedEndpoint({
440
- adminforth,
441
- endpoint: nestedEndpoint,
442
- adminUser,
390
+ const nestedOutput = await invokeTool(nestedToolName, {
443
391
  inputs: nestedInputs,
444
392
  httpExtra: nestedHttpExtra,
445
393
  userTimeZone: nestedUserTimeZone,
@@ -448,9 +396,9 @@ async function applyToolOverride(params: {
448
396
  return applyToolOverride({
449
397
  adminforth,
450
398
  adminUser,
451
- capturedEndpointsByToolName,
452
399
  httpExtra: nestedHttpExtra,
453
400
  inputs: nestedInputs,
401
+ invokeTool,
454
402
  output: nestedOutput,
455
403
  toolName: nestedToolName,
456
404
  userTimeZone: nestedUserTimeZone,
@@ -487,6 +435,10 @@ function openApiSchemaPathToToolName(path: string, adminforth: IAdminForth) {
487
435
  return endpointPathToToolName(stripAdminApiPrefix(path, adminforth));
488
436
  }
489
437
 
438
+ function formatLogNameList(names: string[]) {
439
+ return names.length ? names.join(', ') : '(none)';
440
+ }
441
+
490
442
  export async function formatApiBasedToolCall(params: {
491
443
  adminforth: IAdminForth;
492
444
  adminUser?: AdminUser;
@@ -523,84 +475,6 @@ function normalizeCookies(
523
475
  return Object.entries(cookies).map(([key, value]) => ({ key, value }));
524
476
  }
525
477
 
526
- function createToolResponse(baseResponse?: IAdminForthHttpResponse): ToolHttpResponse {
527
- return {
528
- headers: [],
529
- status: 200,
530
- message: undefined,
531
- setHeader(name, value) {
532
- this.headers.push([name, value]);
533
- baseResponse?.setHeader(name, value);
534
- },
535
- setStatus(code, message) {
536
- this.status = code;
537
- this.message = message;
538
- baseResponse?.setStatus(code, message);
539
- },
540
- blobStream() {
541
- return baseResponse?.blobStream() ?? new PassThrough();
542
- },
543
- };
544
- }
545
-
546
- function createRawExpressRequest(params: {
547
- adminUser?: AdminUser;
548
- body: Record<string, unknown>;
549
- cookies: CookieItem[];
550
- headers: Record<string, any>;
551
- method: string;
552
- query: Record<string, string>;
553
- requestUrl: string;
554
- }) {
555
- const cookieHeader = params.cookies
556
- .map(({ key, value }) => `${key}=${value}`)
557
- .join('; ');
558
-
559
- return {
560
- adminUser: params.adminUser,
561
- body: params.body,
562
- destroyed: false,
563
- headers: {
564
- ...params.headers,
565
- ...(cookieHeader ? { cookie: cookieHeader } : {}),
566
- },
567
- method: params.method.toUpperCase(),
568
- on: () => undefined,
569
- query: params.query,
570
- url: params.requestUrl,
571
- };
572
- }
573
-
574
- function createRawExpressResponse(response: ToolHttpResponse) {
575
- const rawResponse = {
576
- destroyed: false,
577
- on: () => undefined,
578
- setHeader(name: string, value: string) {
579
- response.setHeader(name, value);
580
- return rawResponse;
581
- },
582
- status(code: number) {
583
- response.status = code;
584
- return rawResponse;
585
- },
586
- send(message: string) {
587
- response.message = message;
588
- return rawResponse;
589
- },
590
- json(payload: unknown) {
591
- response.jsonPayload = payload;
592
- response.message = JSON.stringify(payload);
593
- return rawResponse;
594
- },
595
- write: () => true,
596
- writeHead: () => rawResponse,
597
- writableEnded: false,
598
- end: () => rawResponse,
599
- };
600
-
601
- return rawResponse;
602
- }
603
-
604
478
  function normalizeDateTimeInputsToUtc(
605
479
  body: Record<string, unknown>,
606
480
  adminforth: IAdminForth,
@@ -679,100 +553,192 @@ function normalizeDateTimeInputsToUtc(
679
553
  return normalizeValue(body) as Record<string, unknown>;
680
554
  }
681
555
 
682
- async function callCapturedEndpoint(params: {
683
- adminforth: IAdminForth;
684
- adminUser?: AdminUser;
685
- endpoint: CapturedEndpoint;
556
+ const METHODS_WITHOUT_REQUEST_BODY = new Set(['GET', 'HEAD']);
557
+ const HEADERS_NOT_FORWARDED_TO_API_TOOL = new Set([
558
+ 'connection',
559
+ 'content-length',
560
+ 'host',
561
+ 'keep-alive',
562
+ 'proxy-authenticate',
563
+ 'proxy-authorization',
564
+ 'te',
565
+ 'trailer',
566
+ 'transfer-encoding',
567
+ 'upgrade',
568
+ ]);
569
+
570
+ function getHeaderValue(
571
+ headers: Partial<HttpExtra>['headers'] | undefined,
572
+ headerName: string,
573
+ ) {
574
+ const normalizedHeaderName = headerName.toLowerCase();
575
+ const value = Object.entries(headers ?? {}).find(
576
+ ([name]) => name.toLowerCase() === normalizedHeaderName,
577
+ )?.[1];
578
+
579
+ if (typeof value !== 'string') {
580
+ return undefined;
581
+ }
582
+
583
+ return value.split(',')[0].trim();
584
+ }
585
+
586
+ function isAbsoluteHttpUrl(value: string) {
587
+ try {
588
+ const url = new URL(value);
589
+ return url.protocol === 'http:' || url.protocol === 'https:';
590
+ } catch {
591
+ return false;
592
+ }
593
+ }
594
+
595
+ function getRequestOrigin(httpExtra?: Partial<HttpExtra>) {
596
+ const requestUrl = httpExtra?.requestUrl;
597
+
598
+ if (requestUrl && isAbsoluteHttpUrl(requestUrl)) {
599
+ return new URL(requestUrl).origin;
600
+ }
601
+
602
+ const host = getHeaderValue(httpExtra?.headers, 'x-forwarded-host')
603
+ ?? getHeaderValue(httpExtra?.headers, 'host');
604
+
605
+ if (!host) {
606
+ return undefined;
607
+ }
608
+
609
+ const protocol = getHeaderValue(httpExtra?.headers, 'x-forwarded-proto') ?? 'http';
610
+ return `${protocol}://${host}`;
611
+ }
612
+
613
+ function resolveOpenApiRequestUrl(params: {
686
614
  httpExtra?: Partial<HttpExtra>;
687
- inputs?: Record<string, unknown>;
688
- userTimeZone?: string;
615
+ path: string;
616
+ toolName: string;
689
617
  }) {
690
- const { adminforth, adminUser, endpoint, httpExtra, inputs, userTimeZone } = params;
691
- const response = createToolResponse(httpExtra?.response);
692
- const headers = {
693
- 'content-type': 'application/json',
694
- 'X-TimeZone': userTimeZone,
695
- ...(httpExtra?.headers ?? {}),
696
- };
697
- const body = normalizeDateTimeInputsToUtc(
698
- (inputs ?? httpExtra?.body ?? {}) as Record<string, unknown>,
699
- adminforth,
700
- headers['X-TimeZone'],
701
- );
702
- const query = httpExtra?.query ?? {};
703
- const cookies = normalizeCookies(httpExtra?.cookies);
704
- const requestUrl = httpExtra?.requestUrl ?? `${adminforth.config.baseUrl}/adminapi/v1${endpoint.path}`;
705
- const abortController = new AbortController();
706
- const rawRequest = createRawExpressRequest({
707
- adminUser,
708
- body,
709
- cookies,
710
- headers,
711
- method: endpoint.method,
712
- query,
713
- requestUrl,
714
- });
715
- const rawResponse = createRawExpressResponse(response);
716
- const acceptLanguage = headers['accept-language'];
717
- const tr = (
718
- msg: string,
719
- category: string = 'default',
720
- translationParams: any,
721
- pluralizationNumber?: number,
722
- ) => adminforth.tr(msg, category, acceptLanguage, translationParams, pluralizationNumber);
723
-
724
- const output = await endpoint.handler({
725
- body,
726
- adminUser,
727
- query,
728
- headers,
729
- cookies,
730
- response,
731
- requestUrl,
732
- abortSignal: abortController.signal,
733
- _raw_express_req: rawRequest,
734
- _raw_express_res: rawResponse,
735
- tr,
736
- });
618
+ if (isAbsoluteHttpUrl(params.path)) {
619
+ return params.path;
620
+ }
621
+
622
+ const origin = getRequestOrigin(params.httpExtra);
623
+
624
+ if (!origin) {
625
+ throw new Error(
626
+ `Tool "${params.toolName}" has relative OpenAPI path "${params.path}" but request host header is unavailable.`,
627
+ );
628
+ }
629
+
630
+ return new URL(params.path, origin).toString();
631
+ }
632
+
633
+ function createToolRequestHeaders(
634
+ httpExtra: Partial<HttpExtra> | undefined,
635
+ userTimeZone?: string,
636
+ ) {
637
+ const headers: Record<string, string> = {};
737
638
 
738
- if (output !== undefined && output !== null) {
739
- return output;
639
+ for (const [name, value] of Object.entries(httpExtra?.headers ?? {})) {
640
+ const headerName = name.toLowerCase();
641
+
642
+ if (typeof value === 'string' && !HEADERS_NOT_FORWARDED_TO_API_TOOL.has(headerName)) {
643
+ headers[headerName] = value;
644
+ }
740
645
  }
741
646
 
742
- if (response.jsonPayload !== undefined) {
743
- return response.jsonPayload;
647
+ headers.accept = 'application/json';
648
+ headers['content-type'] = 'application/json';
649
+
650
+ if (userTimeZone) {
651
+ headers['x-timezone'] = userTimeZone;
744
652
  }
745
653
 
746
- if (response.message !== undefined) {
747
- return response.message;
654
+ const cookieHeader = normalizeCookies(httpExtra?.cookies)
655
+ .map(({ key, value }) => `${key}=${value}`)
656
+ .join('; ');
657
+
658
+ if (cookieHeader && !headers.cookie) {
659
+ headers.cookie = cookieHeader;
660
+ }
661
+
662
+ return headers;
663
+ }
664
+
665
+ function appendInputsToQueryString(url: string, inputs: Record<string, unknown>) {
666
+ const nextUrl = new URL(url);
667
+
668
+ for (const [key, value] of Object.entries(inputs)) {
669
+ if (value === undefined) {
670
+ continue;
671
+ }
672
+
673
+ if (Array.isArray(value)) {
674
+ for (const item of value) {
675
+ nextUrl.searchParams.append(
676
+ key,
677
+ typeof item === 'object' && item !== null ? JSON.stringify(item) : String(item),
678
+ );
679
+ }
680
+ continue;
681
+ }
682
+
683
+ nextUrl.searchParams.set(
684
+ key,
685
+ typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value),
686
+ );
687
+ }
688
+
689
+ return nextUrl.toString();
690
+ }
691
+
692
+ async function parseOpenApiToolResponse(response: Response) {
693
+ const responseText = await response.text();
694
+ const payload = responseText && response.headers.get('content-type')?.includes('application/json')
695
+ ? JSON.parse(responseText)
696
+ : responseText;
697
+
698
+ if (response.ok) {
699
+ return responseText ? payload : { status: response.status };
748
700
  }
749
701
 
750
702
  return {
751
- headers: response.headers,
703
+ error: 'HTTP_ERROR',
752
704
  status: response.status,
705
+ statusText: response.statusText,
706
+ response: payload,
753
707
  };
754
708
  }
755
709
 
756
- export function prepareApiBasedTools(adminforth: IAdminForth): Record<string, ApiBasedTool> {
757
- const capturedEndpoints: CapturedEndpoint[] = [];
758
-
759
- const captureServer: IHttpServer = {
760
- setupSpaServer() {},
761
- endpoint: ((options: EndpointWithSchemas) => {
762
- capturedEndpoints.push({
763
- ...options,
764
- response_schema: options.response_schema ?? options.responce_schema,
765
- normalizedResponseSchema: options.response_schema ?? options.responce_schema,
766
- });
767
- }) as IHttpServer['endpoint'],
768
- };
710
+ async function callOpenApiSchema(params: {
711
+ adminforth: IAdminForth;
712
+ httpExtra?: Partial<HttpExtra>;
713
+ inputs?: Record<string, unknown>;
714
+ schema: IRegisteredApiSchema;
715
+ toolName: string;
716
+ userTimeZone?: string;
717
+ }) {
718
+ const { adminforth, httpExtra, inputs, schema, toolName, userTimeZone } = params;
719
+ const method = schema.method.toUpperCase();
720
+ const body = normalizeDateTimeInputsToUtc(
721
+ (inputs ?? httpExtra?.body ?? {}) as Record<string, unknown>,
722
+ adminforth,
723
+ userTimeZone,
724
+ );
725
+ const requestUrl = resolveOpenApiRequestUrl({
726
+ httpExtra,
727
+ path: schema.path,
728
+ toolName,
729
+ });
730
+ const hasRequestBody = !METHODS_WITHOUT_REQUEST_BODY.has(method);
731
+ const response = await fetch(hasRequestBody ? requestUrl : appendInputsToQueryString(requestUrl, body), {
732
+ method,
733
+ headers: createToolRequestHeaders(httpExtra, userTimeZone),
734
+ body: hasRequestBody ? JSON.stringify(body) : undefined,
735
+ });
769
736
 
770
- adminforth.setupEndpoints(captureServer);
737
+ return parseOpenApiToolResponse(response);
738
+ }
771
739
 
740
+ export function prepareApiBasedTools(adminforth: IAdminForth): Record<string, ApiBasedTool> {
772
741
  const apiBasedTools: Record<string, ApiBasedTool> = {};
773
- const capturedEndpointsByToolName = Object.fromEntries(
774
- capturedEndpoints.map((endpoint) => [endpointPathToToolName(endpoint.path), endpoint]),
775
- );
776
742
  const openApiSchemas = adminforth.openApi.registeredSchemas.filter(
777
743
  (schema) => schema.request_schema || schema.response_schema,
778
744
  );
@@ -783,24 +749,43 @@ export function prepareApiBasedTools(adminforth: IAdminForth): Record<string, Ap
783
749
  openApiSchemasByToolName.set(toolName, schema);
784
750
  }
785
751
 
752
+ logger.info(
753
+ `AdminForth Agent OpenAPI APIs: ${formatLogNameList(
754
+ adminforth.openApi.registeredSchemas.map((schema) => openApiSchemaPathToToolName(schema.path, adminforth)),
755
+ )}`,
756
+ );
757
+ logger.info(
758
+ `AdminForth Agent OpenAPI tools connected: ${formatLogNameList([...openApiSchemasByToolName.keys()])}`,
759
+ );
760
+
786
761
  for (const [toolName, schema] of openApiSchemasByToolName.entries()) {
787
- const endpoint = capturedEndpointsByToolName[toolName];
788
762
  apiBasedTools[toolName] = {
789
763
  description: schema.description,
790
764
  input_schema: schema.request_schema,
791
765
  input_schma: schema.request_schema,
792
766
  output_schema: schema.response_schema,
793
767
  call: async ({ adminUser, adminuser, inputs, httpExtra, userTimeZone } = {}) => {
794
- if (!endpoint) {
795
- throw new Error(
796
- `Tool "${toolName}" is defined in OpenAPI but has no callable AdminForth endpoint handler.`,
797
- );
798
- }
799
-
800
- const output = await callCapturedEndpoint({
801
- adminforth,
802
- endpoint,
803
- adminUser: adminUser ?? adminuser,
768
+ const invokeTool = async (
769
+ nextToolName: string,
770
+ nextParams: ToolOverrideCallParams = {},
771
+ ) => {
772
+ const nextSchema = openApiSchemasByToolName.get(nextToolName);
773
+
774
+ if (!nextSchema) {
775
+ throw new Error(`Tool ${nextToolName} is not registered in OpenAPI`);
776
+ }
777
+
778
+ return callOpenApiSchema({
779
+ adminforth,
780
+ schema: nextSchema,
781
+ toolName: nextToolName,
782
+ inputs: nextParams.inputs,
783
+ httpExtra: nextParams.httpExtra,
784
+ userTimeZone: nextParams.userTimeZone,
785
+ });
786
+ };
787
+
788
+ const output = await invokeTool(toolName, {
804
789
  inputs,
805
790
  httpExtra,
806
791
  userTimeZone,
@@ -809,9 +794,9 @@ export function prepareApiBasedTools(adminforth: IAdminForth): Record<string, Ap
809
794
  const processedOutput = await applyToolOverride({
810
795
  adminforth,
811
796
  adminUser: adminUser ?? adminuser,
812
- capturedEndpointsByToolName,
813
797
  httpExtra,
814
798
  inputs,
799
+ invokeTool,
815
800
  output,
816
801
  toolName,
817
802
  userTimeZone,
package/build.log CHANGED
@@ -38,5 +38,5 @@ custom/skills/fetch_data/SKILL.md
38
38
  custom/skills/mutate_data/
39
39
  custom/skills/mutate_data/SKILL.md
40
40
 
41
- sent 202,033 bytes received 566 bytes 405,198.00 bytes/sec
42
- total size is 199,749 speedup is 0.99
41
+ sent 206,204 bytes received 562 bytes 413,532.00 bytes/sec
42
+ total size is 203,890 speedup is 0.99
@@ -93,7 +93,8 @@
93
93
  <div
94
94
  class="relative flex-1 min-h-0 flex flex-col overflow-hidden"
95
95
  >
96
- <ConversationArea
96
+ <ConversationArea
97
+ ref="conversationArea"
97
98
  v-if="agentStore.isChatOpen"
98
99
  :messages="agentStore.chatMessages"
99
100
  />
@@ -201,6 +202,7 @@ const props = defineProps<{
201
202
  const chatSurface = useTemplateRef('chatSurface');
202
203
  const textInput = useTemplateRef('textInput');
203
204
  const modeMenu = useTemplateRef('modeMenu');
205
+ const conversationArea = useTemplateRef('conversationArea');
204
206
  const agentStore = useAgentStore();
205
207
  const agentTransitions = useAgentTransitions();
206
208
  const coreStore = useCoreStore();
@@ -254,7 +256,6 @@ onMounted(async () => {
254
256
  agentStore.setIsTeleportedToBody(isTeleportedToBodyFromLocalStorage || props.meta.stickByDefault);
255
257
  }
256
258
  await agentStore.fetchSessionsList();
257
- agentStore.setFullScreen(true);
258
259
  });
259
260
 
260
261
  function autoResize() {
@@ -286,6 +287,7 @@ async function sendMessage() {
286
287
  isModeMenuOpen.value = false;
287
288
  await agentStore.sendMessage();
288
289
  autoResize();
290
+ conversationArea.value?.handleSendMessage();
289
291
  }
290
292
 
291
293
  </script>
@@ -2,7 +2,7 @@
2
2
  <CustomScrollbar
3
3
  ref="containerRef"
4
4
  class="auto-scroll-container mask-y"
5
- :wrapperStyle = "wrapperStyle"
5
+ :wrapperStyle = "wrapperStyle"
6
6
  :contentStyle = "contentStyle"
7
7
  @scroll="handleScroll"
8
8
  >