@adminforth/agent 2.0.8 → 2.0.9

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,10 +1,6 @@
1
1
  import type { ChatSurfaceAdapter, ChatSurfaceEventSink, ChatSurfaceIncomingMessage, IAdminForth } from "adminforth";
2
- import type { ZodType } from "zod";
3
2
  import type { HandleSpeechTurnInput, HandleTurnInput, RunAndPersistAgentResponseInput, RunAndPersistAgentResponseResult } from "../agentTurnService.js";
4
3
  import type { PluginOptions } from "../types.js";
5
- export type EndpointResponse = {
6
- setStatus: (code: number, message: string) => void;
7
- };
8
4
  export type SessionTurn = {
9
5
  prompt: string;
10
6
  response: string;
@@ -12,7 +8,6 @@ export type SessionTurn = {
12
8
  export type AgentEndpointsContext = {
13
9
  adminforth: IAdminForth;
14
10
  options: PluginOptions;
15
- parseBody<T>(schema: ZodType<T>, body: unknown, response: EndpointResponse): T | null;
16
11
  handleTurn(input: HandleTurnInput): Promise<RunAndPersistAgentResponseResult>;
17
12
  handleSpeechTurn(input: HandleSpeechTurnInput): Promise<RunAndPersistAgentResponseResult | null>;
18
13
  runAndPersistAgentResponse(input: RunAndPersistAgentResponseInput): Promise<RunAndPersistAgentResponseResult>;
@@ -21,6 +16,6 @@ export type AgentEndpointsContext = {
21
16
  createSystemTurn(sessionId: string, systemMessage: string): Promise<string>;
22
17
  handleChatSurfaceMessage(adapter: ChatSurfaceAdapter, incoming: ChatSurfaceIncomingMessage, sink: ChatSurfaceEventSink): Promise<void>;
23
18
  };
24
- export type CoreEndpointsContext = Pick<AgentEndpointsContext, "options" | "parseBody" | "handleTurn" | "handleSpeechTurn">;
25
- export type SessionEndpointsContext = Pick<AgentEndpointsContext, "adminforth" | "options" | "parseBody" | "getSessionTurns" | "createNewTurn" | "createSystemTurn">;
19
+ export type CoreEndpointsContext = Pick<AgentEndpointsContext, "options" | "handleTurn" | "handleSpeechTurn">;
20
+ export type SessionEndpointsContext = Pick<AgentEndpointsContext, "adminforth" | "options" | "getSessionTurns" | "createNewTurn" | "createSystemTurn">;
26
21
  export type ChatSurfaceEndpointsContext = Pick<AgentEndpointsContext, "adminforth" | "options" | "handleChatSurfaceMessage">;
@@ -9,18 +9,51 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { z } from "zod";
11
11
  import { createSseEventEmitter } from "../surfaces/web-sse/createSseEventEmitter.js";
12
+ const currentPageContextSchema = z.object({
13
+ path: z.string(),
14
+ fullPath: z.string(),
15
+ title: z.string(),
16
+ url: z.string(),
17
+ }).strict();
12
18
  const agentResponseBodySchema = z.object({
13
19
  message: z.string(),
14
20
  sessionId: z.string(),
15
21
  mode: z.string().nullish(),
16
22
  timeZone: z.string().optional(),
17
- currentPage: z.custom().optional(),
23
+ currentPage: currentPageContextSchema.optional(),
18
24
  }).strict();
19
25
  const agentApprovalBodySchema = z.object({
20
26
  sessionId: z.string(),
21
27
  decision: z.enum(["approve", "reject"]),
22
28
  }).strict();
23
- const agentSpeechResponseBodySchema = agentResponseBodySchema.omit({ message: true });
29
+ // Sent as multipart/form-data (via multer), so every field arrives as a plain string on the wire.
30
+ // `currentPage` is JSON.stringify'd by the client into a form field and must be parsed back into
31
+ // an object. Zod -> JSON-Schema conversion cannot represent `.transform()` at all (it throws), so
32
+ // `agentSpeechResponseShapeSchema` (plain strings, no transform) is what's passed as request_schema
33
+ // for the AJV pre-check, while `agentSpeechResponseBodySchema` (with the transform) is used via an
34
+ // inline safeParse in the handler to actually parse `currentPage` into an object.
35
+ const agentSpeechResponseShapeSchema = z.object({
36
+ sessionId: z.string(),
37
+ mode: z.string().nullish(),
38
+ timeZone: z.string().optional(),
39
+ currentPage: z.string().optional(),
40
+ }).strict();
41
+ const agentSpeechResponseBodySchema = agentSpeechResponseShapeSchema.extend({
42
+ currentPage: z.string().optional().transform((value, ctx) => {
43
+ if (value === undefined)
44
+ return undefined;
45
+ try {
46
+ return currentPageContextSchema.parse(JSON.parse(value));
47
+ }
48
+ catch (err) {
49
+ ctx.addIssue({
50
+ code: z.ZodIssueCode.custom,
51
+ message: `currentPage must be a JSON-encoded object matching CurrentPageContext: ${err instanceof Error ? err.message : String(err)}`,
52
+ });
53
+ return z.NEVER;
54
+ }
55
+ }),
56
+ });
24
57
  export function setupCoreEndpoints(ctx, server) {
25
58
  server.endpoint({
26
59
  method: 'POST',
@@ -43,11 +76,10 @@ export function setupCoreEndpoints(ctx, server) {
43
76
  server.endpoint({
44
77
  method: 'POST',
45
78
  path: `/agent/response`,
79
+ request_schema: agentResponseBodySchema,
46
80
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, _raw_express_res, abortSignal }) {
47
81
  var _b;
48
- const data = ctx.parseBody(agentResponseBodySchema, body, response);
49
- if (!data)
50
- return;
82
+ const data = body;
51
83
  const emit = createSseEventEmitter(_raw_express_res, {
52
84
  vercelAiUiMessageStream: true,
53
85
  closeActiveBlockOnToolStart: true,
@@ -70,10 +102,9 @@ export function setupCoreEndpoints(ctx, server) {
70
102
  server.endpoint({
71
103
  method: 'POST',
72
104
  path: `/agent/approval`,
105
+ request_schema: agentApprovalBodySchema,
73
106
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, _raw_express_res, abortSignal }) {
74
- const data = ctx.parseBody(agentApprovalBodySchema, body, response);
75
- if (!data)
76
- return;
107
+ const data = body;
77
108
  const emit = createSseEventEmitter(_raw_express_res, {
78
109
  vercelAiUiMessageStream: true,
79
110
  closeActiveBlockOnToolStart: true,
@@ -95,6 +126,7 @@ export function setupCoreEndpoints(ctx, server) {
95
126
  method: 'POST',
96
127
  path: `/agent/speech-response`,
97
128
  target: 'upload',
129
+ request_schema: agentSpeechResponseShapeSchema,
98
130
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, _raw_express_req, _raw_express_res, abortSignal }) {
99
131
  var _b;
100
132
  const req = _raw_express_req;
@@ -103,9 +135,12 @@ export function setupCoreEndpoints(ctx, server) {
103
135
  response.setStatus(400, "Audio adapter is not configured for AdminForth Agent");
104
136
  return { error: "Audio adapter is not configured for AdminForth Agent" };
105
137
  }
106
- const data = ctx.parseBody(agentSpeechResponseBodySchema, body, response);
107
- if (!data)
108
- return;
138
+ const parsed = agentSpeechResponseBodySchema.safeParse(body);
139
+ if (!parsed.success) {
140
+ response.setStatus(400, 'Request body validation failed');
141
+ return { error: 'Request body validation failed', details: parsed.error.issues };
142
+ }
143
+ const data = parsed.data;
109
144
  if (!req.file) {
110
145
  response.setStatus(400, "Audio file is required");
111
146
  return { error: "Audio file is required" };
@@ -28,11 +28,10 @@ export function setupSessionEndpoints(ctx, server) {
28
28
  server.endpoint({
29
29
  method: 'POST',
30
30
  path: `/agent/get-sessions`,
31
+ request_schema: getSessionsBodySchema,
31
32
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
32
33
  var _b;
33
- const data = ctx.parseBody(getSessionsBodySchema, body, response);
34
- if (!data)
35
- return;
34
+ const data = body;
36
35
  const userId = adminUser.pk;
37
36
  const limit = (_b = data.limit) !== null && _b !== void 0 ? _b : 20;
38
37
  const sessions = yield ctx.adminforth.resource(ctx.options.sessionResource.resourceId).list([Filters.EQ(ctx.options.sessionResource.askerIdField, userId)], limit, undefined, [Sorts.DESC(ctx.options.sessionResource.createdAtField)]);
@@ -48,10 +47,9 @@ export function setupSessionEndpoints(ctx, server) {
48
47
  server.endpoint({
49
48
  method: 'POST',
50
49
  path: `/agent/get-session-info`,
50
+ request_schema: sessionIdBodySchema,
51
51
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
52
- const data = ctx.parseBody(sessionIdBodySchema, body, response);
53
- if (!data)
54
- return;
52
+ const data = body;
55
53
  const userId = adminUser.pk;
56
54
  const sessionId = data.sessionId;
57
55
  const session = yield ctx.adminforth.resource(ctx.options.sessionResource.resourceId).get([Filters.EQ(ctx.options.sessionResource.idField, sessionId)]);
@@ -101,10 +99,9 @@ export function setupSessionEndpoints(ctx, server) {
101
99
  server.endpoint({
102
100
  method: 'POST',
103
101
  path: `/agent/create-session`,
102
+ request_schema: createSessionBodySchema,
104
103
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
105
- const data = ctx.parseBody(createSessionBodySchema, body, response);
106
- if (!data)
107
- return;
104
+ const data = body;
108
105
  const triggerMessage = data.triggerMessage;
109
106
  const userId = adminUser.pk;
110
107
  const title = (triggerMessage === null || triggerMessage === void 0 ? void 0 : triggerMessage.slice(0, 40)) || "New Session";
@@ -125,10 +122,9 @@ export function setupSessionEndpoints(ctx, server) {
125
122
  server.endpoint({
126
123
  method: 'POST',
127
124
  path: `/agent/delete-session`,
125
+ request_schema: sessionIdBodySchema,
128
126
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
129
- const data = ctx.parseBody(sessionIdBodySchema, body, response);
130
- if (!data)
131
- return;
127
+ const data = body;
132
128
  const sessionId = data.sessionId;
133
129
  const userId = adminUser.pk;
134
130
  const session = yield ctx.adminforth.resource(ctx.options.sessionResource.resourceId).get([Filters.EQ(ctx.options.sessionResource.idField, sessionId)]);
@@ -153,10 +149,9 @@ export function setupSessionEndpoints(ctx, server) {
153
149
  server.endpoint({
154
150
  method: 'POST',
155
151
  path: `/agent/add-system-message-to-turns`,
152
+ request_schema: addSystemMessageBodySchema,
156
153
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
157
- const data = ctx.parseBody(addSystemMessageBodySchema, body, response);
158
- if (!data)
159
- return;
154
+ const data = body;
160
155
  const session = yield ctx.adminforth.resource(ctx.options.sessionResource.resourceId).get([Filters.EQ(ctx.options.sessionResource.idField, data.sessionId)]);
161
156
  if (!session) {
162
157
  return {
package/dist/index.d.ts CHANGED
@@ -12,7 +12,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
12
12
  private agentTurnService;
13
13
  private speechTurnService;
14
14
  private chatSurfaceService;
15
- private parseBody;
16
15
  private getCheckpointer;
17
16
  private getInternalAgentResourceIds;
18
17
  constructor(options: PluginOptions);
package/dist/index.js CHANGED
@@ -28,14 +28,6 @@ import { TurnPersistenceService } from "./agent/turn/TurnPersistenceService.js";
28
28
  import { TurnPromptBuilder } from "./agent/turn/TurnPromptBuilder.js";
29
29
  import { TurnStreamConsumer } from "./agent/turn/TurnStreamConsumer.js";
30
30
  export default class AdminForthAgentPlugin extends AdminForthPlugin {
31
- parseBody(schema, body, response) {
32
- const parsed = schema.safeParse(body !== null && body !== void 0 ? body : {});
33
- if (!parsed.success) {
34
- response.setStatus(422, parsed.error.message);
35
- return null;
36
- }
37
- return parsed.data;
38
- }
39
31
  getCheckpointer() {
40
32
  if (this.checkpointer)
41
33
  return this.checkpointer;
@@ -147,7 +139,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
147
139
  const endpointContext = {
148
140
  adminforth: this.adminforth,
149
141
  options: this.options,
150
- parseBody: this.parseBody.bind(this),
151
142
  handleTurn: this.agentTurnService.handleTurn.bind(this.agentTurnService),
152
143
  handleSpeechTurn: this.speechTurnService.handle.bind(this.speechTurnService),
153
144
  runAndPersistAgentResponse: this.agentTurnService.runAndPersistAgentResponse.bind(this.agentTurnService),
@@ -4,7 +4,6 @@ import type {
4
4
  ChatSurfaceIncomingMessage,
5
5
  IAdminForth,
6
6
  } from "adminforth";
7
- import type { ZodType } from "zod";
8
7
  import type {
9
8
  HandleSpeechTurnInput,
10
9
  HandleTurnInput,
@@ -13,10 +12,6 @@ import type {
13
12
  } from "../agentTurnService.js";
14
13
  import type { PluginOptions } from "../types.js";
15
14
 
16
- export type EndpointResponse = {
17
- setStatus: (code: number, message: string) => void;
18
- };
19
-
20
15
  export type SessionTurn = {
21
16
  prompt: string;
22
17
  response: string;
@@ -25,7 +20,6 @@ export type SessionTurn = {
25
20
  export type AgentEndpointsContext = {
26
21
  adminforth: IAdminForth;
27
22
  options: PluginOptions;
28
- parseBody<T>(schema: ZodType<T>, body: unknown, response: EndpointResponse): T | null;
29
23
  handleTurn(input: HandleTurnInput): Promise<RunAndPersistAgentResponseResult>;
30
24
  handleSpeechTurn(input: HandleSpeechTurnInput): Promise<RunAndPersistAgentResponseResult | null>;
31
25
  runAndPersistAgentResponse(input: RunAndPersistAgentResponseInput): Promise<RunAndPersistAgentResponseResult>;
@@ -41,12 +35,12 @@ export type AgentEndpointsContext = {
41
35
 
42
36
  export type CoreEndpointsContext = Pick<
43
37
  AgentEndpointsContext,
44
- "options" | "parseBody" | "handleTurn" | "handleSpeechTurn"
38
+ "options" | "handleTurn" | "handleSpeechTurn"
45
39
  >;
46
40
 
47
41
  export type SessionEndpointsContext = Pick<
48
42
  AgentEndpointsContext,
49
- "adminforth" | "options" | "parseBody" | "getSessionTurns" | "createNewTurn"
43
+ "adminforth" | "options" | "getSessionTurns" | "createNewTurn"
50
44
  | "createSystemTurn"
51
45
  >;
52
46
 
package/endpoints/core.ts CHANGED
@@ -12,12 +12,19 @@ type MulterFile = {
12
12
 
13
13
  type ExpressMulterRequest = { file?: MulterFile };
14
14
 
15
+ const currentPageContextSchema = z.object({
16
+ path: z.string(),
17
+ fullPath: z.string(),
18
+ title: z.string(),
19
+ url: z.string(),
20
+ }).strict() satisfies z.ZodType<CurrentPageContext>;
21
+
15
22
  const agentResponseBodySchema = z.object({
16
23
  message: z.string(),
17
24
  sessionId: z.string(),
18
25
  mode: z.string().nullish(),
19
26
  timeZone: z.string().optional(),
20
- currentPage: z.custom<CurrentPageContext>().optional(),
27
+ currentPage: currentPageContextSchema.optional(),
21
28
  }).strict();
22
29
 
23
30
  const agentApprovalBodySchema = z.object({
@@ -25,7 +32,33 @@ const agentApprovalBodySchema = z.object({
25
32
  decision: z.enum(["approve", "reject"]),
26
33
  }).strict();
27
34
 
28
- const agentSpeechResponseBodySchema = agentResponseBodySchema.omit({ message: true });
35
+ // Sent as multipart/form-data (via multer), so every field arrives as a plain string on the wire.
36
+ // `currentPage` is JSON.stringify'd by the client into a form field and must be parsed back into
37
+ // an object. Zod -> JSON-Schema conversion cannot represent `.transform()` at all (it throws), so
38
+ // `agentSpeechResponseShapeSchema` (plain strings, no transform) is what's passed as request_schema
39
+ // for the AJV pre-check, while `agentSpeechResponseBodySchema` (with the transform) is used via an
40
+ // inline safeParse in the handler to actually parse `currentPage` into an object.
41
+ const agentSpeechResponseShapeSchema = z.object({
42
+ sessionId: z.string(),
43
+ mode: z.string().nullish(),
44
+ timeZone: z.string().optional(),
45
+ currentPage: z.string().optional(),
46
+ }).strict();
47
+
48
+ const agentSpeechResponseBodySchema = agentSpeechResponseShapeSchema.extend({
49
+ currentPage: z.string().optional().transform((value, ctx) => {
50
+ if (value === undefined) return undefined;
51
+ try {
52
+ return currentPageContextSchema.parse(JSON.parse(value));
53
+ } catch (err) {
54
+ ctx.addIssue({
55
+ code: z.ZodIssueCode.custom,
56
+ message: `currentPage must be a JSON-encoded object matching CurrentPageContext: ${err instanceof Error ? err.message : String(err)}`,
57
+ });
58
+ return z.NEVER;
59
+ }
60
+ }),
61
+ });
29
62
 
30
63
  export function setupCoreEndpoints(ctx: CoreEndpointsContext, server: IHttpServer) {
31
64
  server.endpoint({
@@ -52,9 +85,9 @@ export function setupCoreEndpoints(ctx: CoreEndpointsContext, server: IHttpServe
52
85
  server.endpoint({
53
86
  method: 'POST',
54
87
  path: `/agent/response`,
88
+ request_schema: agentResponseBodySchema,
55
89
  handler: async ({ body, adminUser, response, _raw_express_res, abortSignal }) => {
56
- const data = ctx.parseBody(agentResponseBodySchema, body, response);
57
- if (!data) return;
90
+ const data = body as z.infer<typeof agentResponseBodySchema>;
58
91
  const emit = createSseEventEmitter(_raw_express_res, {
59
92
  vercelAiUiMessageStream: true,
60
93
  closeActiveBlockOnToolStart: true,
@@ -79,9 +112,9 @@ export function setupCoreEndpoints(ctx: CoreEndpointsContext, server: IHttpServe
79
112
  server.endpoint({
80
113
  method: 'POST',
81
114
  path: `/agent/approval`,
115
+ request_schema: agentApprovalBodySchema,
82
116
  handler: async ({ body, adminUser, response, _raw_express_res, abortSignal }) => {
83
- const data = ctx.parseBody(agentApprovalBodySchema, body, response);
84
- if (!data) return;
117
+ const data = body as z.infer<typeof agentApprovalBodySchema>;
85
118
  const emit = createSseEventEmitter(_raw_express_res, {
86
119
  vercelAiUiMessageStream: true,
87
120
  closeActiveBlockOnToolStart: true,
@@ -105,6 +138,7 @@ export function setupCoreEndpoints(ctx: CoreEndpointsContext, server: IHttpServe
105
138
  method: 'POST',
106
139
  path: `/agent/speech-response`,
107
140
  target: 'upload',
141
+ request_schema: agentSpeechResponseShapeSchema,
108
142
  handler: async ({ body, adminUser, response, _raw_express_req, _raw_express_res, abortSignal }) => {
109
143
  const req = _raw_express_req as ExpressMulterRequest;
110
144
  const audioAdapter = ctx.options.audioAdapter;
@@ -112,8 +146,12 @@ export function setupCoreEndpoints(ctx: CoreEndpointsContext, server: IHttpServe
112
146
  response.setStatus(400, "Audio adapter is not configured for AdminForth Agent");
113
147
  return { error: "Audio adapter is not configured for AdminForth Agent" };
114
148
  }
115
- const data = ctx.parseBody(agentSpeechResponseBodySchema, body, response);
116
- if (!data) return;
149
+ const parsed = agentSpeechResponseBodySchema.safeParse(body);
150
+ if (!parsed.success) {
151
+ response.setStatus(400, 'Request body validation failed');
152
+ return { error: 'Request body validation failed', details: parsed.error.issues };
153
+ }
154
+ const data = parsed.data;
117
155
  if (!req.file) {
118
156
  response.setStatus(400, "Audio file is required");
119
157
  return { error: "Audio file is required" };
@@ -26,9 +26,9 @@ export function setupSessionEndpoints(ctx: SessionEndpointsContext, server: IHtt
26
26
  server.endpoint({
27
27
  method: 'POST',
28
28
  path: `/agent/get-sessions`,
29
+ request_schema: getSessionsBodySchema,
29
30
  handler: async ({body, adminUser, response }) => {
30
- const data = ctx.parseBody(getSessionsBodySchema, body, response);
31
- if (!data) return;
31
+ const data = body as z.infer<typeof getSessionsBodySchema>;
32
32
  const userId = adminUser!.pk;
33
33
  const limit = data.limit ?? 20;
34
34
  const sessions = await ctx.adminforth.resource(ctx.options.sessionResource.resourceId).list(
@@ -47,9 +47,9 @@ export function setupSessionEndpoints(ctx: SessionEndpointsContext, server: IHtt
47
47
  server.endpoint({
48
48
  method: 'POST',
49
49
  path: `/agent/get-session-info`,
50
+ request_schema: sessionIdBodySchema,
50
51
  handler: async ({body, adminUser, response }) => {
51
- const data = ctx.parseBody(sessionIdBodySchema, body, response);
52
- if (!data) return;
52
+ const data = body as z.infer<typeof sessionIdBodySchema>;
53
53
  const userId = adminUser!.pk;
54
54
  const sessionId = data.sessionId;
55
55
  const session = await ctx.adminforth.resource(ctx.options.sessionResource.resourceId).get(
@@ -102,9 +102,9 @@ export function setupSessionEndpoints(ctx: SessionEndpointsContext, server: IHtt
102
102
  server.endpoint({
103
103
  method: 'POST',
104
104
  path: `/agent/create-session`,
105
+ request_schema: createSessionBodySchema,
105
106
  handler: async ({body, adminUser, response }) => {
106
- const data = ctx.parseBody(createSessionBodySchema, body, response);
107
- if (!data) return;
107
+ const data = body as z.infer<typeof createSessionBodySchema>;
108
108
  const triggerMessage = data.triggerMessage;
109
109
  const userId = adminUser!.pk;
110
110
  const title = triggerMessage?.slice(0, 40) || "New Session";
@@ -126,9 +126,9 @@ export function setupSessionEndpoints(ctx: SessionEndpointsContext, server: IHtt
126
126
  server.endpoint({
127
127
  method: 'POST',
128
128
  path: `/agent/delete-session`,
129
+ request_schema: sessionIdBodySchema,
129
130
  handler: async ({body, adminUser, response }) => {
130
- const data = ctx.parseBody(sessionIdBodySchema, body, response);
131
- if (!data) return;
131
+ const data = body as z.infer<typeof sessionIdBodySchema>;
132
132
  const sessionId = data.sessionId;
133
133
  const userId = adminUser!.pk;
134
134
  const session = await ctx.adminforth.resource(ctx.options.sessionResource.resourceId).get(
@@ -158,9 +158,9 @@ export function setupSessionEndpoints(ctx: SessionEndpointsContext, server: IHtt
158
158
  server.endpoint({
159
159
  method: 'POST',
160
160
  path: `/agent/add-system-message-to-turns`,
161
+ request_schema: addSystemMessageBodySchema,
161
162
  handler: async ({body, adminUser, response }) => {
162
- const data = ctx.parseBody(addSystemMessageBodySchema, body, response);
163
- if (!data) return;
163
+ const data = body as z.infer<typeof addSystemMessageBodySchema>;
164
164
  const session = await ctx.adminforth.resource(ctx.options.sessionResource.resourceId).get(
165
165
  [Filters.EQ(ctx.options.sessionResource.idField, data.sessionId)]
166
166
  );
package/index.ts CHANGED
@@ -8,7 +8,6 @@ import { AdminForthPlugin } from "adminforth";
8
8
 
9
9
  import type { PluginOptions } from './types.js';
10
10
  import { MemorySaver, type BaseCheckpointSaver } from "@langchain/langgraph";
11
- import { z } from "zod";
12
11
  import { AdminForthCheckpointSaver } from "./agent/checkpointer.js";
13
12
  import { appendCustomSystemPrompt, buildAgentSystemPrompt, DEFAULT_AGENT_SYSTEM_PROMPT} from "./agent/systemPrompt.js";
14
13
  import { setupCoreEndpoints } from "./endpoints/core.js";
@@ -41,18 +40,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
41
40
  private agentTurnService: AgentTurnService;
42
41
  private speechTurnService: SpeechTurnService;
43
42
  private chatSurfaceService: ChatSurfaceService;
44
- private parseBody<T>(
45
- schema: z.ZodType<T>,
46
- body: unknown,
47
- response: { setStatus: (code: number, message: string) => void },
48
- ): T | null {
49
- const parsed = schema.safeParse(body ?? {});
50
- if (!parsed.success) {
51
- response.setStatus(422, parsed.error.message);
52
- return null;
53
- }
54
- return parsed.data;
55
- }
56
43
  private getCheckpointer() {
57
44
  if (this.checkpointer) return this.checkpointer;
58
45
 
@@ -189,7 +176,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
189
176
  const endpointContext = {
190
177
  adminforth: this.adminforth,
191
178
  options: this.options,
192
- parseBody: this.parseBody.bind(this),
193
179
  handleTurn: this.agentTurnService.handleTurn.bind(this.agentTurnService),
194
180
  handleSpeechTurn: this.speechTurnService.handle.bind(this.speechTurnService),
195
181
  runAndPersistAgentResponse: this.agentTurnService.runAndPersistAgentResponse.bind(this.agentTurnService),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/agent",
3
- "version": "2.0.8",
3
+ "version": "2.0.9",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -25,7 +25,7 @@
25
25
  "description": "AI agent plugin for AdminForth with tool-based workflows and persistent chat sessions",
26
26
  "devDependencies": {
27
27
  "@types/node": "latest",
28
- "adminforth": "^3.7.1",
28
+ "adminforth": "^3.8.2",
29
29
  "semantic-release": "^24.2.1",
30
30
  "semantic-release-slack-bot": "^4.0.2",
31
31
  "typescript": "^5.7.3"
@@ -67,6 +67,6 @@
67
67
  }
68
68
  ],
69
69
  "peerDependencies": {
70
- "adminforth": "^3.7.1"
70
+ "adminforth": "^3.8.2"
71
71
  }
72
72
  }