@adminforth/agent 1.24.14 → 1.25.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
41
+ sent 202,067 bytes received 566 bytes 405,266.00 bytes/sec
42
42
  total size is 199,749 speedup is 0.99
@@ -19,6 +19,7 @@ export const contextSchema = z.object({
19
19
  userTimeZone: z.string(),
20
20
  sessionId: z.string(),
21
21
  turnId: z.string(),
22
+ httpExtra: z.custom().optional(),
22
23
  emitToolCallEvent: z.custom(),
23
24
  });
24
25
  function isLangChainAgentCompletionAdapter(adapter) {
@@ -130,7 +131,7 @@ export function createAgentChatModel(params) {
130
131
  }
131
132
  export function callAgent(params) {
132
133
  return __awaiter(this, void 0, void 0, function* () {
133
- const { name, model, summaryModel, modelMiddleware = [], checkpointer, messages, adminUser, adminforth, apiBasedTools, customComponentsDir, sessionId, turnId, userTimeZone, emitToolCallEvent, sequenceDebugSink, } = params;
134
+ const { name, model, summaryModel, modelMiddleware = [], checkpointer, messages, adminUser, adminforth, apiBasedTools, customComponentsDir, sessionId, turnId, httpExtra, userTimeZone, emitToolCallEvent, sequenceDebugSink, } = params;
134
135
  const tools = yield createAgentTools(customComponentsDir, apiBasedTools);
135
136
  const apiBasedToolsMiddleware = createApiBasedToolsMiddleware(apiBasedTools, adminforth);
136
137
  const sequenceDebugMiddleware = createSequenceDebugMiddleware(sequenceDebugSink);
@@ -164,6 +165,7 @@ export function callAgent(params) {
164
165
  userTimeZone,
165
166
  sessionId,
166
167
  turnId,
168
+ httpExtra,
167
169
  emitToolCallEvent,
168
170
  },
169
171
  });
@@ -49,6 +49,7 @@ export function createApiTool(toolName, apiBasedTool) {
49
49
  const normalizedInput = (input !== null && input !== void 0 ? input : {});
50
50
  return apiBasedTool.call({
51
51
  adminUser: runtime.context.adminUser,
52
+ httpExtra: runtime.context.httpExtra,
52
53
  inputs: normalizedInput,
53
54
  userTimeZone: runtime.context.userTimeZone,
54
55
  });
@@ -7,11 +7,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { AdminForthDataTypes, } from 'adminforth';
10
+ import { AdminForthDataTypes, logger, } from 'adminforth';
11
11
  import dayjs from 'dayjs';
12
12
  import timezone from 'dayjs/plugin/timezone.js';
13
13
  import utc from 'dayjs/plugin/utc.js';
14
- import { PassThrough } from 'stream';
15
14
  import { inspect } from 'util';
16
15
  import YAML from 'yaml';
17
16
  dayjs.extend(utc);
@@ -230,7 +229,7 @@ function formatDateTimeColumns(rows, dateTimeColumnNames, userTimeZone) {
230
229
  function applyToolOverride(params) {
231
230
  return __awaiter(this, void 0, void 0, function* () {
232
231
  var _a;
233
- const { adminforth, adminUser, capturedEndpointsByToolName, httpExtra, inputs, output, toolName, userTimeZone, } = params;
232
+ const { adminforth, adminUser, httpExtra, inputs, invokeTool, output, toolName, userTimeZone, } = params;
234
233
  const sanitizedOutput = sanitizeForYaml(output);
235
234
  const override = TOOL_OVERRIDES[toolName];
236
235
  if (!override) {
@@ -250,17 +249,10 @@ function applyToolOverride(params) {
250
249
  userTimeZone,
251
250
  invokeTool: (nestedToolName_1, ...args_1) => __awaiter(this, [nestedToolName_1, ...args_1], void 0, function* (nestedToolName, nestedParams = {}) {
252
251
  var _a, _b, _c;
253
- const nestedEndpoint = capturedEndpointsByToolName[nestedToolName];
254
- if (!nestedEndpoint) {
255
- throw new Error(`Tool ${nestedToolName} is not registered`);
256
- }
257
252
  const nestedInputs = (_a = nestedParams.inputs) !== null && _a !== void 0 ? _a : inputs;
258
253
  const nestedHttpExtra = (_b = nestedParams.httpExtra) !== null && _b !== void 0 ? _b : httpExtra;
259
254
  const nestedUserTimeZone = (_c = nestedParams.userTimeZone) !== null && _c !== void 0 ? _c : userTimeZone;
260
- const nestedOutput = yield callCapturedEndpoint({
261
- adminforth,
262
- endpoint: nestedEndpoint,
263
- adminUser,
255
+ const nestedOutput = yield invokeTool(nestedToolName, {
264
256
  inputs: nestedInputs,
265
257
  httpExtra: nestedHttpExtra,
266
258
  userTimeZone: nestedUserTimeZone,
@@ -268,9 +260,9 @@ function applyToolOverride(params) {
268
260
  return applyToolOverride({
269
261
  adminforth,
270
262
  adminUser,
271
- capturedEndpointsByToolName,
272
263
  httpExtra: nestedHttpExtra,
273
264
  inputs: nestedInputs,
265
+ invokeTool,
274
266
  output: nestedOutput,
275
267
  toolName: nestedToolName,
276
268
  userTimeZone: nestedUserTimeZone,
@@ -301,6 +293,9 @@ function stripAdminApiPrefix(path, adminforth) {
301
293
  function openApiSchemaPathToToolName(path, adminforth) {
302
294
  return endpointPathToToolName(stripAdminApiPrefix(path, adminforth));
303
295
  }
296
+ function formatLogNameList(names) {
297
+ return names.length ? names.join(', ') : '(none)';
298
+ }
304
299
  export function formatApiBasedToolCall(params) {
305
300
  return __awaiter(this, void 0, void 0, function* () {
306
301
  var _a;
@@ -326,69 +321,6 @@ function normalizeCookies(cookies) {
326
321
  }
327
322
  return Object.entries(cookies).map(([key, value]) => ({ key, value }));
328
323
  }
329
- function createToolResponse(baseResponse) {
330
- return {
331
- headers: [],
332
- status: 200,
333
- message: undefined,
334
- setHeader(name, value) {
335
- this.headers.push([name, value]);
336
- baseResponse === null || baseResponse === void 0 ? void 0 : baseResponse.setHeader(name, value);
337
- },
338
- setStatus(code, message) {
339
- this.status = code;
340
- this.message = message;
341
- baseResponse === null || baseResponse === void 0 ? void 0 : baseResponse.setStatus(code, message);
342
- },
343
- blobStream() {
344
- var _a;
345
- return (_a = baseResponse === null || baseResponse === void 0 ? void 0 : baseResponse.blobStream()) !== null && _a !== void 0 ? _a : new PassThrough();
346
- },
347
- };
348
- }
349
- function createRawExpressRequest(params) {
350
- const cookieHeader = params.cookies
351
- .map(({ key, value }) => `${key}=${value}`)
352
- .join('; ');
353
- return {
354
- adminUser: params.adminUser,
355
- body: params.body,
356
- destroyed: false,
357
- headers: Object.assign(Object.assign({}, params.headers), (cookieHeader ? { cookie: cookieHeader } : {})),
358
- method: params.method.toUpperCase(),
359
- on: () => undefined,
360
- query: params.query,
361
- url: params.requestUrl,
362
- };
363
- }
364
- function createRawExpressResponse(response) {
365
- const rawResponse = {
366
- destroyed: false,
367
- on: () => undefined,
368
- setHeader(name, value) {
369
- response.setHeader(name, value);
370
- return rawResponse;
371
- },
372
- status(code) {
373
- response.status = code;
374
- return rawResponse;
375
- },
376
- send(message) {
377
- response.message = message;
378
- return rawResponse;
379
- },
380
- json(payload) {
381
- response.jsonPayload = payload;
382
- response.message = JSON.stringify(payload);
383
- return rawResponse;
384
- },
385
- write: () => true,
386
- writeHead: () => rawResponse,
387
- writableEnded: false,
388
- end: () => rawResponse,
389
- };
390
- return rawResponse;
391
- }
392
324
  function normalizeDateTimeInputsToUtc(body, adminforth, userTimeZone) {
393
325
  if (!userTimeZone || typeof body.resourceId !== 'string') {
394
326
  return body;
@@ -437,90 +369,168 @@ function normalizeDateTimeInputsToUtc(body, adminforth, userTimeZone) {
437
369
  };
438
370
  return normalizeValue(body);
439
371
  }
440
- function callCapturedEndpoint(params) {
441
- return __awaiter(this, void 0, void 0, function* () {
442
- var _a, _b, _c, _d;
443
- const { adminforth, adminUser, endpoint, httpExtra, inputs, userTimeZone } = params;
444
- const response = createToolResponse(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.response);
445
- const headers = Object.assign({ 'content-type': 'application/json', 'X-TimeZone': userTimeZone }, ((_a = httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.headers) !== null && _a !== void 0 ? _a : {}));
446
- const body = normalizeDateTimeInputsToUtc(((_b = inputs !== null && inputs !== void 0 ? inputs : httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.body) !== null && _b !== void 0 ? _b : {}), adminforth, headers['X-TimeZone']);
447
- const query = (_c = httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.query) !== null && _c !== void 0 ? _c : {};
448
- const cookies = normalizeCookies(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.cookies);
449
- const requestUrl = (_d = httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.requestUrl) !== null && _d !== void 0 ? _d : `${adminforth.config.baseUrl}/adminapi/v1${endpoint.path}`;
450
- const abortController = new AbortController();
451
- const rawRequest = createRawExpressRequest({
452
- adminUser,
453
- body,
454
- cookies,
455
- headers,
456
- method: endpoint.method,
457
- query,
458
- requestUrl,
459
- });
460
- const rawResponse = createRawExpressResponse(response);
461
- const acceptLanguage = headers['accept-language'];
462
- const tr = (msg, category = 'default', translationParams, pluralizationNumber) => adminforth.tr(msg, category, acceptLanguage, translationParams, pluralizationNumber);
463
- const output = yield endpoint.handler({
464
- body,
465
- adminUser,
466
- query,
467
- headers,
468
- cookies,
469
- response,
470
- requestUrl,
471
- abortSignal: abortController.signal,
472
- _raw_express_req: rawRequest,
473
- _raw_express_res: rawResponse,
474
- tr,
475
- });
476
- if (output !== undefined && output !== null) {
477
- return output;
372
+ const METHODS_WITHOUT_REQUEST_BODY = new Set(['GET', 'HEAD']);
373
+ const HEADERS_NOT_FORWARDED_TO_API_TOOL = new Set([
374
+ 'connection',
375
+ 'content-length',
376
+ 'host',
377
+ 'keep-alive',
378
+ 'proxy-authenticate',
379
+ 'proxy-authorization',
380
+ 'te',
381
+ 'trailer',
382
+ 'transfer-encoding',
383
+ 'upgrade',
384
+ ]);
385
+ function getHeaderValue(headers, headerName) {
386
+ var _a;
387
+ const normalizedHeaderName = headerName.toLowerCase();
388
+ const value = (_a = Object.entries(headers !== null && headers !== void 0 ? headers : {}).find(([name]) => name.toLowerCase() === normalizedHeaderName)) === null || _a === void 0 ? void 0 : _a[1];
389
+ if (typeof value !== 'string') {
390
+ return undefined;
391
+ }
392
+ return value.split(',')[0].trim();
393
+ }
394
+ function isAbsoluteHttpUrl(value) {
395
+ try {
396
+ const url = new URL(value);
397
+ return url.protocol === 'http:' || url.protocol === 'https:';
398
+ }
399
+ catch (_a) {
400
+ return false;
401
+ }
402
+ }
403
+ function getRequestOrigin(httpExtra) {
404
+ var _a, _b;
405
+ const requestUrl = httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.requestUrl;
406
+ if (requestUrl && isAbsoluteHttpUrl(requestUrl)) {
407
+ return new URL(requestUrl).origin;
408
+ }
409
+ const host = (_a = getHeaderValue(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.headers, 'x-forwarded-host')) !== null && _a !== void 0 ? _a : getHeaderValue(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.headers, 'host');
410
+ if (!host) {
411
+ return undefined;
412
+ }
413
+ const protocol = (_b = getHeaderValue(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.headers, 'x-forwarded-proto')) !== null && _b !== void 0 ? _b : 'http';
414
+ return `${protocol}://${host}`;
415
+ }
416
+ function resolveOpenApiRequestUrl(params) {
417
+ if (isAbsoluteHttpUrl(params.path)) {
418
+ return params.path;
419
+ }
420
+ const origin = getRequestOrigin(params.httpExtra);
421
+ if (!origin) {
422
+ throw new Error(`Tool "${params.toolName}" has relative OpenAPI path "${params.path}" but request host header is unavailable.`);
423
+ }
424
+ return new URL(params.path, origin).toString();
425
+ }
426
+ function createToolRequestHeaders(httpExtra, userTimeZone) {
427
+ var _a;
428
+ const headers = {};
429
+ for (const [name, value] of Object.entries((_a = httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.headers) !== null && _a !== void 0 ? _a : {})) {
430
+ const headerName = name.toLowerCase();
431
+ if (typeof value === 'string' && !HEADERS_NOT_FORWARDED_TO_API_TOOL.has(headerName)) {
432
+ headers[headerName] = value;
478
433
  }
479
- if (response.jsonPayload !== undefined) {
480
- return response.jsonPayload;
434
+ }
435
+ headers.accept = 'application/json';
436
+ headers['content-type'] = 'application/json';
437
+ if (userTimeZone) {
438
+ headers['x-timezone'] = userTimeZone;
439
+ }
440
+ const cookieHeader = normalizeCookies(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.cookies)
441
+ .map(({ key, value }) => `${key}=${value}`)
442
+ .join('; ');
443
+ if (cookieHeader && !headers.cookie) {
444
+ headers.cookie = cookieHeader;
445
+ }
446
+ return headers;
447
+ }
448
+ function appendInputsToQueryString(url, inputs) {
449
+ const nextUrl = new URL(url);
450
+ for (const [key, value] of Object.entries(inputs)) {
451
+ if (value === undefined) {
452
+ continue;
481
453
  }
482
- if (response.message !== undefined) {
483
- return response.message;
454
+ if (Array.isArray(value)) {
455
+ for (const item of value) {
456
+ nextUrl.searchParams.append(key, typeof item === 'object' && item !== null ? JSON.stringify(item) : String(item));
457
+ }
458
+ continue;
459
+ }
460
+ nextUrl.searchParams.set(key, typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value));
461
+ }
462
+ return nextUrl.toString();
463
+ }
464
+ function parseOpenApiToolResponse(response) {
465
+ return __awaiter(this, void 0, void 0, function* () {
466
+ var _a;
467
+ const responseText = yield response.text();
468
+ const payload = responseText && ((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('application/json'))
469
+ ? JSON.parse(responseText)
470
+ : responseText;
471
+ if (response.ok) {
472
+ return responseText ? payload : { status: response.status };
484
473
  }
485
474
  return {
486
- headers: response.headers,
475
+ error: 'HTTP_ERROR',
487
476
  status: response.status,
477
+ statusText: response.statusText,
478
+ response: payload,
488
479
  };
489
480
  });
490
481
  }
482
+ function callOpenApiSchema(params) {
483
+ return __awaiter(this, void 0, void 0, function* () {
484
+ var _a;
485
+ const { adminforth, httpExtra, inputs, schema, toolName, userTimeZone } = params;
486
+ const method = schema.method.toUpperCase();
487
+ const body = normalizeDateTimeInputsToUtc(((_a = inputs !== null && inputs !== void 0 ? inputs : httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.body) !== null && _a !== void 0 ? _a : {}), adminforth, userTimeZone);
488
+ const requestUrl = resolveOpenApiRequestUrl({
489
+ httpExtra,
490
+ path: schema.path,
491
+ toolName,
492
+ });
493
+ const hasRequestBody = !METHODS_WITHOUT_REQUEST_BODY.has(method);
494
+ const response = yield fetch(hasRequestBody ? requestUrl : appendInputsToQueryString(requestUrl, body), {
495
+ method,
496
+ headers: createToolRequestHeaders(httpExtra, userTimeZone),
497
+ body: hasRequestBody ? JSON.stringify(body) : undefined,
498
+ });
499
+ return parseOpenApiToolResponse(response);
500
+ });
501
+ }
491
502
  export function prepareApiBasedTools(adminforth) {
492
- const capturedEndpoints = [];
493
- const captureServer = {
494
- setupSpaServer() { },
495
- endpoint: ((options) => {
496
- var _a, _b;
497
- capturedEndpoints.push(Object.assign(Object.assign({}, options), { response_schema: (_a = options.response_schema) !== null && _a !== void 0 ? _a : options.responce_schema, normalizedResponseSchema: (_b = options.response_schema) !== null && _b !== void 0 ? _b : options.responce_schema }));
498
- }),
499
- };
500
- adminforth.setupEndpoints(captureServer);
501
503
  const apiBasedTools = {};
502
- const capturedEndpointsByToolName = Object.fromEntries(capturedEndpoints.map((endpoint) => [endpointPathToToolName(endpoint.path), endpoint]));
503
504
  const openApiSchemas = adminforth.openApi.registeredSchemas.filter((schema) => schema.request_schema || schema.response_schema);
504
505
  const openApiSchemasByToolName = new Map();
505
506
  for (const schema of openApiSchemas) {
506
507
  const toolName = openApiSchemaPathToToolName(schema.path, adminforth);
507
508
  openApiSchemasByToolName.set(toolName, schema);
508
509
  }
510
+ logger.info(`AdminForth Agent OpenAPI APIs: ${formatLogNameList(adminforth.openApi.registeredSchemas.map((schema) => openApiSchemaPathToToolName(schema.path, adminforth)))}`);
511
+ logger.info(`AdminForth Agent OpenAPI tools connected: ${formatLogNameList([...openApiSchemasByToolName.keys()])}`);
509
512
  for (const [toolName, schema] of openApiSchemasByToolName.entries()) {
510
- const endpoint = capturedEndpointsByToolName[toolName];
511
513
  apiBasedTools[toolName] = {
512
514
  description: schema.description,
513
515
  input_schema: schema.request_schema,
514
516
  input_schma: schema.request_schema,
515
517
  output_schema: schema.response_schema,
516
518
  call: (...args_1) => __awaiter(this, [...args_1], void 0, function* ({ adminUser, adminuser, inputs, httpExtra, userTimeZone } = {}) {
517
- if (!endpoint) {
518
- throw new Error(`Tool "${toolName}" is defined in OpenAPI but has no callable AdminForth endpoint handler.`);
519
- }
520
- const output = yield callCapturedEndpoint({
521
- adminforth,
522
- endpoint,
523
- adminUser: adminUser !== null && adminUser !== void 0 ? adminUser : adminuser,
519
+ const invokeTool = (nextToolName_1, ...args_2) => __awaiter(this, [nextToolName_1, ...args_2], void 0, function* (nextToolName, nextParams = {}) {
520
+ const nextSchema = openApiSchemasByToolName.get(nextToolName);
521
+ if (!nextSchema) {
522
+ throw new Error(`Tool ${nextToolName} is not registered in OpenAPI`);
523
+ }
524
+ return callOpenApiSchema({
525
+ adminforth,
526
+ schema: nextSchema,
527
+ toolName: nextToolName,
528
+ inputs: nextParams.inputs,
529
+ httpExtra: nextParams.httpExtra,
530
+ userTimeZone: nextParams.userTimeZone,
531
+ });
532
+ });
533
+ const output = yield invokeTool(toolName, {
524
534
  inputs,
525
535
  httpExtra,
526
536
  userTimeZone,
@@ -528,9 +538,9 @@ export function prepareApiBasedTools(adminforth) {
528
538
  const processedOutput = yield applyToolOverride({
529
539
  adminforth,
530
540
  adminUser: adminUser !== null && adminUser !== void 0 ? adminUser : adminuser,
531
- capturedEndpointsByToolName,
532
541
  httpExtra,
533
542
  inputs,
543
+ invokeTool,
534
544
  output,
535
545
  toolName,
536
546
  userTimeZone,
package/dist/index.js CHANGED
@@ -168,11 +168,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
168
168
  });
169
169
  }
170
170
  validateConfigAfterDiscover(adminforth, resourceConfig) {
171
- this.apiBasedTools = buildApiBasedTools(adminforth);
172
- for (const toolName of ALWAYS_AVAILABLE_API_TOOL_NAMES) {
173
- assertRequiredApiTool(this.apiBasedTools, toolName);
174
- }
175
- assertRequiredApiTool(this.apiBasedTools, "update_record");
176
171
  this.agentSystemPromptPromise = buildAgentSystemPrompt(adminforth)
177
172
  .then((systemPrompt) => appendCustomSystemPrompt(systemPrompt, this.options.systemPrompt));
178
173
  }
@@ -208,7 +203,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
208
203
  server.endpoint({
209
204
  method: 'POST',
210
205
  path: `/agent/response`,
211
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, _raw_express_res }) {
206
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, query, headers, cookies, adminUser, response, requestUrl, _raw_express_res }) {
212
207
  var _b, e_1, _c, _d;
213
208
  var _e, _f, _g;
214
209
  const res = _raw_express_res;
@@ -288,6 +283,12 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
288
283
  const selectedMode = (_g = this.options.modes.find((mode) => mode.name === body.mode)) !== null && _g !== void 0 ? _g : this.options.modes[0];
289
284
  const { model, summaryModel, modelMiddleware } = yield this.getModeModels(selectedMode, maxTokens);
290
285
  const systemPrompt = yield this.agentSystemPromptPromise;
286
+ const apiBasedTools = buildApiBasedTools(this.adminforth);
287
+ for (const toolName of ALWAYS_AVAILABLE_API_TOOL_NAMES) {
288
+ assertRequiredApiTool(apiBasedTools, toolName);
289
+ }
290
+ assertRequiredApiTool(apiBasedTools, "update_record");
291
+ this.apiBasedTools = apiBasedTools;
291
292
  const stream = yield callAgent({
292
293
  name: `adminforth-agent-${this.pluginInstanceId}`,
293
294
  model,
@@ -300,10 +301,18 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
300
301
  ],
301
302
  adminUser,
302
303
  adminforth: this.adminforth,
303
- apiBasedTools: this.apiBasedTools,
304
+ apiBasedTools,
304
305
  customComponentsDir: this.adminforth.config.customization.customComponentsDir,
305
306
  sessionId,
306
307
  turnId,
308
+ httpExtra: {
309
+ body,
310
+ query,
311
+ headers,
312
+ cookies,
313
+ requestUrl,
314
+ response,
315
+ },
307
316
  userTimeZone,
308
317
  emitToolCallEvent,
309
318
  sequenceDebugSink: sequenceDebugCollector,
package/index.ts CHANGED
@@ -204,11 +204,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
204
204
  }
205
205
 
206
206
  validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
207
- this.apiBasedTools = buildApiBasedTools(adminforth);
208
- for (const toolName of ALWAYS_AVAILABLE_API_TOOL_NAMES) {
209
- assertRequiredApiTool(this.apiBasedTools, toolName);
210
- }
211
- assertRequiredApiTool(this.apiBasedTools, "update_record");
212
207
  this.agentSystemPromptPromise = buildAgentSystemPrompt(adminforth)
213
208
  .then((systemPrompt) => appendCustomSystemPrompt(systemPrompt, this.options.systemPrompt));
214
209
  }
@@ -248,7 +243,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
248
243
  server.endpoint({
249
244
  method: 'POST',
250
245
  path: `/agent/response`,
251
- handler: async ({ body, adminUser, _raw_express_res }) => {
246
+ handler: async ({ body, query, headers, cookies, adminUser, response, requestUrl, _raw_express_res }) => {
252
247
  const res = _raw_express_res;
253
248
  const messageId = randomUUID();
254
249
  const prompt = body.message;
@@ -346,6 +341,12 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
346
341
  const { model, summaryModel, modelMiddleware } =
347
342
  await this.getModeModels(selectedMode, maxTokens);
348
343
  const systemPrompt = await this.agentSystemPromptPromise;
344
+ const apiBasedTools = buildApiBasedTools(this.adminforth);
345
+ for (const toolName of ALWAYS_AVAILABLE_API_TOOL_NAMES) {
346
+ assertRequiredApiTool(apiBasedTools, toolName);
347
+ }
348
+ assertRequiredApiTool(apiBasedTools, "update_record");
349
+ this.apiBasedTools = apiBasedTools;
349
350
  const stream = await callAgent({
350
351
  name: `adminforth-agent-${this.pluginInstanceId}`,
351
352
  model,
@@ -358,10 +359,18 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
358
359
  ],
359
360
  adminUser,
360
361
  adminforth: this.adminforth,
361
- apiBasedTools: this.apiBasedTools,
362
+ apiBasedTools,
362
363
  customComponentsDir: this.adminforth.config.customization.customComponentsDir,
363
364
  sessionId,
364
365
  turnId,
366
+ httpExtra: {
367
+ body,
368
+ query,
369
+ headers,
370
+ cookies,
371
+ requestUrl,
372
+ response,
373
+ },
365
374
  userTimeZone,
366
375
  emitToolCallEvent,
367
376
  sequenceDebugSink: sequenceDebugCollector,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/agent",
3
- "version": "1.24.14",
3
+ "version": "1.25.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",