@adminforth/agent 1.44.2 → 1.45.1

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 (38) hide show
  1. package/agentTurnService.ts +526 -0
  2. package/build.log +3 -2
  3. package/chatSurfaceService.ts +189 -0
  4. package/custom/ChatSurfaceSettings.vue +125 -0
  5. package/custom/incremark_code_renderers/incremarkRenderer.ts +5 -5
  6. package/dist/agentTurnService.d.ts +70 -0
  7. package/dist/agentTurnService.js +453 -0
  8. package/dist/chatSurfaceService.d.ts +29 -0
  9. package/dist/chatSurfaceService.js +142 -0
  10. package/dist/custom/ChatSurfaceSettings.vue +125 -0
  11. package/dist/custom/incremark_code_renderers/incremarkRenderer.ts +5 -5
  12. package/dist/endpoints/chatSurfaces.d.ts +3 -0
  13. package/dist/endpoints/chatSurfaces.js +91 -0
  14. package/dist/endpoints/context.d.ts +30 -0
  15. package/dist/endpoints/context.js +1 -0
  16. package/dist/endpoints/core.d.ts +3 -0
  17. package/dist/endpoints/core.js +106 -0
  18. package/dist/endpoints/sessions.d.ts +3 -0
  19. package/dist/endpoints/sessions.js +177 -0
  20. package/dist/errors.d.ts +2 -0
  21. package/dist/errors.js +9 -0
  22. package/dist/index.d.ts +5 -42
  23. package/dist/index.js +50 -808
  24. package/dist/sessionStore.d.ts +19 -0
  25. package/dist/sessionStore.js +83 -0
  26. package/dist/types.d.ts +4 -0
  27. package/endpoints/chatSurfaces.ts +93 -0
  28. package/endpoints/context.ts +66 -0
  29. package/endpoints/core.ts +113 -0
  30. package/endpoints/sessions.ts +183 -0
  31. package/errors.ts +10 -0
  32. package/index.ts +60 -907
  33. package/package.json +2 -2
  34. package/sessionStore.ts +94 -0
  35. package/types.ts +5 -0
  36. package/agentResponseEvents.ts +0 -1
  37. package/dist/agentResponseEvents.d.ts +0 -1
  38. package/dist/agentResponseEvents.js +0 -1
@@ -0,0 +1,125 @@
1
+ <template>
2
+ <div class="flex flex-col justify-center mr-6 md:mr-12">
3
+ <h2 class="flex items-start justify-start leading-none text-gray-800 dark:text-gray-50 text-3xl font-semibold">
4
+ {{ $t('Chat Surfaces') }}
5
+ </h2>
6
+ <p class="text-sm mt-3">
7
+ {{ $t('Connect external chat accounts to your AdminForth user') }}
8
+ </p>
9
+
10
+ <div class="mt-6 flex flex-wrap gap-4">
11
+ <div
12
+ v-for="surface in surfaces"
13
+ :key="surface.name"
14
+ class="flex flex-col w-full lg:w-72 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-4 shadow-sm"
15
+ >
16
+ <div class="flex items-center justify-between gap-3 mb-4">
17
+ <div class="min-w-0">
18
+ <p class="font-semibold text-gray-900 dark:text-white truncate">
19
+ {{ formatSurfaceName(surface.name) }}
20
+ </p>
21
+ <p class="text-xs text-gray-500 dark:text-gray-400">
22
+ {{ surface.externalUserId || $t('Not connected') }}
23
+ </p>
24
+ </div>
25
+ <span
26
+ class="shrink-0 inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium"
27
+ :class="surface.externalUserId
28
+ ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
29
+ : 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300'"
30
+ >
31
+ {{ surface.externalUserId ? $t('Active') : $t('Inactive') }}
32
+ </span>
33
+ </div>
34
+
35
+ <div class="grid gap-2 mt-auto" :class="surface.externalUserId ? 'grid-cols-2' : 'grid-cols-1'">
36
+ <Button
37
+ class="w-full"
38
+ :disabled="isSurfaceBusy(surface.name)"
39
+ :loader="connectingSurfaceName === surface.name"
40
+ @click="connectSurface(surface.name)"
41
+ >
42
+ {{ surface.externalUserId ? $t('Reconnect') : $t('Connect') }}
43
+ </Button>
44
+ <Button
45
+ v-if="surface.externalUserId"
46
+ class="w-full"
47
+ :disabled="isSurfaceBusy(surface.name)"
48
+ :loader="disconnectingSurfaceName === surface.name"
49
+ @click="disconnectSurface(surface.name)"
50
+ >
51
+ {{ $t('Disconnect') }}
52
+ </Button>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </template>
58
+
59
+ <script setup lang="ts">
60
+ import { onMounted, ref } from 'vue';
61
+ import { Button } from '@/afcl';
62
+ import { callAdminForthApi } from '@/utils';
63
+
64
+ type ChatSurface = {
65
+ name: string;
66
+ externalUserId: string | null;
67
+ };
68
+
69
+ const surfaces = ref<ChatSurface[]>([]);
70
+ const connectingSurfaceName = ref<string | null>(null);
71
+ const disconnectingSurfaceName = ref<string | null>(null);
72
+
73
+ onMounted(loadSurfaces);
74
+
75
+ async function loadSurfaces() {
76
+ const response = await callAdminForthApi({
77
+ method: 'POST',
78
+ path: '/agent/surfaces/connectable',
79
+ body: {},
80
+ });
81
+
82
+ surfaces.value = response.surfaces;
83
+ }
84
+
85
+ async function connectSurface(surfaceName: string) {
86
+ connectingSurfaceName.value = surfaceName;
87
+
88
+ try {
89
+ const response = await callAdminForthApi({
90
+ method: 'POST',
91
+ path: `/agent/surface/${surfaceName}/connect-action`,
92
+ body: {},
93
+ });
94
+
95
+ if (response.action.type === 'url') {
96
+ window.open(response.action.url, '_blank', 'noopener,noreferrer');
97
+ }
98
+ } finally {
99
+ connectingSurfaceName.value = null;
100
+ }
101
+ }
102
+
103
+ async function disconnectSurface(surfaceName: string) {
104
+ disconnectingSurfaceName.value = surfaceName;
105
+
106
+ try {
107
+ await callAdminForthApi({
108
+ method: 'POST',
109
+ path: `/agent/surface/${surfaceName}/disconnect`,
110
+ body: {},
111
+ });
112
+ await loadSurfaces();
113
+ } finally {
114
+ disconnectingSurfaceName.value = null;
115
+ }
116
+ }
117
+
118
+ function isSurfaceBusy(surfaceName: string) {
119
+ return connectingSurfaceName.value === surfaceName || disconnectingSurfaceName.value === surfaceName;
120
+ }
121
+
122
+ function formatSurfaceName(surfaceName: string) {
123
+ return surfaceName.charAt(0).toUpperCase() + surfaceName.slice(1);
124
+ }
125
+ </script>
@@ -625,11 +625,11 @@ function sanitizeClassToken(value: string): string {
625
625
 
626
626
  function escapeHtml(value: string): string {
627
627
  return value
628
- .replaceAll('&', '&amp;')
629
- .replaceAll('<', '&lt;')
630
- .replaceAll('>', '&gt;')
631
- .replaceAll('"', '&quot;')
632
- .replaceAll("'", '&#39;');
628
+ .replace(/&/g, '&amp;')
629
+ .replace(/</g, '&lt;')
630
+ .replace(/>/g, '&gt;')
631
+ .replace(/"/g, '&quot;')
632
+ .replace(/'/g, '&#39;');
633
633
  }
634
634
 
635
635
  function escapeAttribute(value: string): string {
@@ -0,0 +1,3 @@
1
+ import type { IHttpServer } from "adminforth";
2
+ import type { ChatSurfaceEndpointsContext } from "./context.js";
3
+ export declare function setupChatSurfaceEndpoints(ctx: ChatSurfaceEndpointsContext, server: IHttpServer): void;
@@ -0,0 +1,91 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ const DEFAULT_ADMIN_USER_EXTERNAL_USER_ID_FIELD = "externalUserId";
11
+ export function setupChatSurfaceEndpoints(ctx, server) {
12
+ var _a;
13
+ if (ctx.getChatSurfaceConnectActionAdapters().length) {
14
+ server.endpoint({
15
+ method: "POST",
16
+ path: "/agent/surfaces/connectable",
17
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ adminUser }) {
18
+ var _b, _c;
19
+ const externalUserIdField = (_b = ctx.options.chatExternalIdsField) !== null && _b !== void 0 ? _b : DEFAULT_ADMIN_USER_EXTERNAL_USER_ID_FIELD;
20
+ const externalIds = (_c = adminUser.dbUser[externalUserIdField]) !== null && _c !== void 0 ? _c : {};
21
+ return {
22
+ surfaces: ctx.getChatSurfaceConnectActionAdapters().map((adapter) => {
23
+ var _a;
24
+ return ({
25
+ name: adapter.name,
26
+ externalUserId: (_a = externalIds[adapter.name]) !== null && _a !== void 0 ? _a : null,
27
+ });
28
+ }),
29
+ };
30
+ }),
31
+ });
32
+ }
33
+ for (const adapter of (_a = ctx.options.chatSurfaceAdapters) !== null && _a !== void 0 ? _a : []) {
34
+ const connectActionAdapter = adapter;
35
+ if (connectActionAdapter.createConnectAction) {
36
+ server.endpoint({
37
+ method: "POST",
38
+ path: `/agent/surface/${adapter.name}/connect-action`,
39
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ adminUser }) {
40
+ const token = ctx.createChatSurfaceLinkToken(adapter.name, adminUser);
41
+ const action = yield connectActionAdapter.createConnectAction({ token });
42
+ return {
43
+ action,
44
+ };
45
+ }),
46
+ });
47
+ server.endpoint({
48
+ method: "POST",
49
+ path: `/agent/surface/${adapter.name}/disconnect`,
50
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ adminUser }) {
51
+ var _b, _c;
52
+ const externalUserIdField = (_b = ctx.options.chatExternalIdsField) !== null && _b !== void 0 ? _b : DEFAULT_ADMIN_USER_EXTERNAL_USER_ID_FIELD;
53
+ const externalIds = Object.assign({}, ((_c = adminUser.dbUser[externalUserIdField]) !== null && _c !== void 0 ? _c : {}));
54
+ delete externalIds[adapter.name];
55
+ yield ctx.adminforth.resource(ctx.adminforth.config.auth.usersResourceId).update(adminUser.pk, {
56
+ [externalUserIdField]: externalIds,
57
+ });
58
+ return {
59
+ ok: true,
60
+ };
61
+ }),
62
+ });
63
+ }
64
+ server.endpoint({
65
+ method: "POST",
66
+ noAuth: true,
67
+ path: `/agent/surface/${adapter.name}/webhook`,
68
+ handler: (endpointInput) => __awaiter(this, void 0, void 0, function* () {
69
+ var _a;
70
+ const surfaceContext = {
71
+ body: endpointInput.body,
72
+ headers: endpointInput.headers,
73
+ abortSignal: endpointInput.abortSignal,
74
+ rawRequest: endpointInput._raw_express_req,
75
+ rawResponse: endpointInput._raw_express_res,
76
+ };
77
+ const incoming = yield adapter.parseIncomingMessage(surfaceContext);
78
+ if (!incoming)
79
+ return { ok: true };
80
+ const sink = yield adapter.createEventSink(surfaceContext, incoming);
81
+ try {
82
+ yield ctx.handleChatSurfaceMessage(adapter, incoming, sink);
83
+ }
84
+ finally {
85
+ yield ((_a = sink.close) === null || _a === void 0 ? void 0 : _a.call(sink));
86
+ }
87
+ return { ok: true };
88
+ }),
89
+ });
90
+ }
91
+ }
@@ -0,0 +1,30 @@
1
+ import type { AdminUser, ChatSurfaceAdapter, ChatSurfaceEventSink, ChatSurfaceIncomingMessage, IAdminForth } from "adminforth";
2
+ import type { ZodType } from "zod";
3
+ import type { HandleSpeechTurnInput, HandleTurnInput, RunAndPersistAgentResponseInput, RunAndPersistAgentResponseResult } from "../agentTurnService.js";
4
+ import type { ChatSurfaceAdapterWithConnectAction } from "../chatSurfaceService.js";
5
+ import type { PluginOptions } from "../types.js";
6
+ export type { ChatSurfaceAdapterWithConnectAction } from "../chatSurfaceService.js";
7
+ export type EndpointResponse = {
8
+ setStatus: (code: number, message: string) => void;
9
+ };
10
+ export type SessionTurn = {
11
+ prompt: string;
12
+ response: string;
13
+ };
14
+ export type AgentEndpointsContext = {
15
+ adminforth: IAdminForth;
16
+ options: PluginOptions;
17
+ parseBody<T>(schema: ZodType<T>, body: unknown, response: EndpointResponse): T | null;
18
+ handleTurn(input: HandleTurnInput): Promise<RunAndPersistAgentResponseResult>;
19
+ handleSpeechTurn(input: HandleSpeechTurnInput): Promise<RunAndPersistAgentResponseResult | null>;
20
+ runAndPersistAgentResponse(input: RunAndPersistAgentResponseInput): Promise<RunAndPersistAgentResponseResult>;
21
+ getSessionTurns(sessionId: string): Promise<SessionTurn[]>;
22
+ createNewTurn(sessionId: string, prompt: string, response?: string): Promise<string>;
23
+ createSystemTurn(sessionId: string, systemMessage: string): Promise<string>;
24
+ getChatSurfaceConnectActionAdapters(): ChatSurfaceAdapterWithConnectAction[];
25
+ createChatSurfaceLinkToken(surface: string, adminUser: AdminUser): string;
26
+ handleChatSurfaceMessage(adapter: ChatSurfaceAdapter, incoming: ChatSurfaceIncomingMessage, sink: ChatSurfaceEventSink): Promise<void>;
27
+ };
28
+ export type CoreEndpointsContext = Pick<AgentEndpointsContext, "options" | "parseBody" | "handleTurn" | "handleSpeechTurn">;
29
+ export type SessionEndpointsContext = Pick<AgentEndpointsContext, "adminforth" | "options" | "parseBody" | "getSessionTurns" | "createNewTurn" | "createSystemTurn">;
30
+ export type ChatSurfaceEndpointsContext = Pick<AgentEndpointsContext, "adminforth" | "options" | "getChatSurfaceConnectActionAdapters" | "createChatSurfaceLinkToken" | "handleChatSurfaceMessage">;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { IHttpServer } from "adminforth";
2
+ import type { CoreEndpointsContext } from "./context.js";
3
+ export declare function setupCoreEndpoints(ctx: CoreEndpointsContext, server: IHttpServer): void;
@@ -0,0 +1,106 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { z } from "zod";
11
+ import { createSseEventEmitter } from "../surfaces/web-sse/createSseEventEmitter.js";
12
+ const agentResponseBodySchema = z.object({
13
+ message: z.string(),
14
+ sessionId: z.string(),
15
+ mode: z.string().nullish(),
16
+ timeZone: z.string().optional(),
17
+ currentPage: z.custom().optional(),
18
+ }).strict();
19
+ const agentSpeechResponseBodySchema = agentResponseBodySchema.omit({ message: true });
20
+ export function setupCoreEndpoints(ctx, server) {
21
+ server.endpoint({
22
+ method: 'POST',
23
+ path: `/agent/get-placeholder-messages`,
24
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ headers, adminUser }) {
25
+ if (!ctx.options.placeholderMessages) {
26
+ return {
27
+ messages: [],
28
+ };
29
+ }
30
+ const messages = yield ctx.options.placeholderMessages({
31
+ adminUser: adminUser,
32
+ headers,
33
+ });
34
+ return {
35
+ messages,
36
+ };
37
+ })
38
+ });
39
+ server.endpoint({
40
+ method: 'POST',
41
+ path: `/agent/response`,
42
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, _raw_express_res, abortSignal }) {
43
+ var _b;
44
+ const data = ctx.parseBody(agentResponseBodySchema, body, response);
45
+ if (!data)
46
+ return;
47
+ const emit = createSseEventEmitter(_raw_express_res, {
48
+ vercelAiUiMessageStream: true,
49
+ closeActiveBlockOnToolStart: true,
50
+ });
51
+ yield ctx.handleTurn({
52
+ prompt: data.message,
53
+ sessionId: data.sessionId,
54
+ modeName: data.mode,
55
+ userTimeZone: (_b = data.timeZone) !== null && _b !== void 0 ? _b : 'UTC',
56
+ currentPage: data.currentPage,
57
+ abortSignal,
58
+ adminUser: adminUser,
59
+ emit,
60
+ failureLogMessage: "Agent response streaming failed",
61
+ abortLogMessage: "Agent response streaming aborted by the client",
62
+ });
63
+ return null;
64
+ })
65
+ });
66
+ server.endpoint({
67
+ method: 'POST',
68
+ path: `/agent/speech-response`,
69
+ target: 'upload',
70
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, _raw_express_req, _raw_express_res, abortSignal }) {
71
+ var _b;
72
+ const req = _raw_express_req;
73
+ const audioAdapter = ctx.options.audioAdapter;
74
+ if (!audioAdapter) {
75
+ response.setStatus(400, "Audio adapter is not configured for AdminForth Agent");
76
+ return { error: "Audio adapter is not configured for AdminForth Agent" };
77
+ }
78
+ const data = ctx.parseBody(agentSpeechResponseBodySchema, body, response);
79
+ if (!data)
80
+ return;
81
+ if (!req.file) {
82
+ response.setStatus(400, "Audio file is required");
83
+ return { error: "Audio file is required" };
84
+ }
85
+ const emit = createSseEventEmitter(_raw_express_res);
86
+ yield ctx.handleSpeechTurn({
87
+ audioAdapter,
88
+ audio: {
89
+ buffer: req.file.buffer,
90
+ filename: req.file.originalname,
91
+ mimeType: req.file.mimetype,
92
+ },
93
+ sessionId: data.sessionId,
94
+ modeName: data.mode,
95
+ userTimeZone: (_b = data.timeZone) !== null && _b !== void 0 ? _b : 'UTC',
96
+ currentPage: data.currentPage,
97
+ abortSignal,
98
+ adminUser: adminUser,
99
+ emit,
100
+ failureLogMessage: "Agent speech response failed",
101
+ abortLogMessage: "Agent speech response aborted by the client",
102
+ });
103
+ return null;
104
+ })
105
+ });
106
+ }
@@ -0,0 +1,3 @@
1
+ import type { IHttpServer } from "adminforth";
2
+ import type { SessionEndpointsContext } from "./context.js";
3
+ export declare function setupSessionEndpoints(ctx: SessionEndpointsContext, server: IHttpServer): void;
@@ -0,0 +1,177 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Filters, Sorts } from "adminforth";
11
+ import { randomUUID } from "crypto";
12
+ import { z } from "zod";
13
+ import { AGENT_SYSTEM_TURN_PROMPT } from "../sessionStore.js";
14
+ const addSystemMessageBodySchema = z.object({
15
+ sessionId: z.string(),
16
+ systemMessage: z.string(),
17
+ }).strict();
18
+ const getSessionsBodySchema = z.object({
19
+ limit: z.number().int().min(1).max(100).optional(),
20
+ }).strict();
21
+ const sessionIdBodySchema = z.object({
22
+ sessionId: z.string(),
23
+ }).strict();
24
+ const createSessionBodySchema = z.object({
25
+ triggerMessage: z.string().optional(),
26
+ }).strict();
27
+ export function setupSessionEndpoints(ctx, server) {
28
+ server.endpoint({
29
+ method: 'POST',
30
+ path: `/agent/get-sessions`,
31
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
32
+ var _b;
33
+ const data = ctx.parseBody(getSessionsBodySchema, body, response);
34
+ if (!data)
35
+ return;
36
+ const userId = adminUser.pk;
37
+ const limit = (_b = data.limit) !== null && _b !== void 0 ? _b : 20;
38
+ 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)]);
39
+ return {
40
+ sessions: sessions.map((session) => ({
41
+ sessionId: session[ctx.options.sessionResource.idField],
42
+ title: session[ctx.options.sessionResource.titleField],
43
+ timestamp: session[ctx.options.sessionResource.createdAtField],
44
+ })),
45
+ };
46
+ })
47
+ });
48
+ server.endpoint({
49
+ method: 'POST',
50
+ path: `/agent/get-session-info`,
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;
55
+ const userId = adminUser.pk;
56
+ const sessionId = data.sessionId;
57
+ const session = yield ctx.adminforth.resource(ctx.options.sessionResource.resourceId).get([Filters.EQ(ctx.options.sessionResource.idField, sessionId)]);
58
+ if (!session) {
59
+ return {
60
+ error: 'Session not found'
61
+ };
62
+ }
63
+ if (session[ctx.options.sessionResource.askerIdField] !== userId) {
64
+ return {
65
+ error: 'Unauthorized'
66
+ };
67
+ }
68
+ const turns = yield ctx.getSessionTurns(sessionId);
69
+ return {
70
+ session: {
71
+ sessionId,
72
+ title: session[ctx.options.sessionResource.titleField],
73
+ timestamp: session[ctx.options.sessionResource.createdAtField],
74
+ messages: turns.flatMap(turn => {
75
+ const messages = [];
76
+ if (turn.prompt === AGENT_SYSTEM_TURN_PROMPT) {
77
+ messages.push({
78
+ text: turn.response,
79
+ role: 'system',
80
+ });
81
+ return messages;
82
+ }
83
+ if (turn.prompt) {
84
+ messages.push({
85
+ text: turn.prompt,
86
+ role: 'user',
87
+ });
88
+ }
89
+ if (turn.response && turn.response !== "not_finished") {
90
+ messages.push({
91
+ text: turn.response,
92
+ role: 'assistant',
93
+ });
94
+ }
95
+ return messages;
96
+ }),
97
+ },
98
+ };
99
+ })
100
+ });
101
+ server.endpoint({
102
+ method: 'POST',
103
+ path: `/agent/create-session`,
104
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
105
+ const data = ctx.parseBody(createSessionBodySchema, body, response);
106
+ if (!data)
107
+ return;
108
+ const triggerMessage = data.triggerMessage;
109
+ const userId = adminUser.pk;
110
+ const title = (triggerMessage === null || triggerMessage === void 0 ? void 0 : triggerMessage.slice(0, 40)) || "New Session";
111
+ const newSession = {
112
+ [ctx.options.sessionResource.idField]: randomUUID(),
113
+ [ctx.options.sessionResource.titleField]: title,
114
+ [ctx.options.sessionResource.askerIdField]: userId,
115
+ };
116
+ const { createdRecord } = yield ctx.adminforth.resource(ctx.options.sessionResource.resourceId).create(newSession);
117
+ return {
118
+ sessionId: createdRecord[ctx.options.sessionResource.idField],
119
+ title: createdRecord[ctx.options.sessionResource.titleField],
120
+ timestamp: createdRecord[ctx.options.sessionResource.createdAtField],
121
+ messages: []
122
+ };
123
+ })
124
+ });
125
+ server.endpoint({
126
+ method: 'POST',
127
+ path: `/agent/delete-session`,
128
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
129
+ const data = ctx.parseBody(sessionIdBodySchema, body, response);
130
+ if (!data)
131
+ return;
132
+ const sessionId = data.sessionId;
133
+ const userId = adminUser.pk;
134
+ const session = yield ctx.adminforth.resource(ctx.options.sessionResource.resourceId).get([Filters.EQ(ctx.options.sessionResource.idField, sessionId)]);
135
+ if (!session) {
136
+ return {
137
+ error: 'Session not found'
138
+ };
139
+ }
140
+ if (session[ctx.options.sessionResource.askerIdField] !== userId) {
141
+ return {
142
+ error: 'Unauthorized'
143
+ };
144
+ }
145
+ yield ctx.adminforth.resource(ctx.options.sessionResource.resourceId).delete(sessionId);
146
+ const turns = yield ctx.adminforth.resource(ctx.options.turnResource.resourceId).list([Filters.EQ(ctx.options.turnResource.sessionIdField, sessionId)]);
147
+ yield Promise.all(turns.map(turn => ctx.adminforth.resource(ctx.options.turnResource.resourceId).delete(turn[ctx.options.turnResource.idField])));
148
+ return {
149
+ ok: true
150
+ };
151
+ })
152
+ });
153
+ server.endpoint({
154
+ method: 'POST',
155
+ path: `/agent/add-system-message-to-turns`,
156
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
157
+ const data = ctx.parseBody(addSystemMessageBodySchema, body, response);
158
+ if (!data)
159
+ return;
160
+ const session = yield ctx.adminforth.resource(ctx.options.sessionResource.resourceId).get([Filters.EQ(ctx.options.sessionResource.idField, data.sessionId)]);
161
+ if (!session) {
162
+ return {
163
+ error: 'Session not found'
164
+ };
165
+ }
166
+ if (session[ctx.options.sessionResource.askerIdField] !== adminUser.pk) {
167
+ return {
168
+ error: 'Unauthorized'
169
+ };
170
+ }
171
+ yield ctx.createSystemTurn(data.sessionId, data.systemMessage);
172
+ return {
173
+ ok: true
174
+ };
175
+ })
176
+ });
177
+ }
@@ -0,0 +1,2 @@
1
+ export declare function isAbortError(error: unknown): boolean;
2
+ export declare function getErrorMessage(error: unknown): string;
package/dist/errors.js ADDED
@@ -0,0 +1,9 @@
1
+ export function isAbortError(error) {
2
+ return typeof error === "object" &&
3
+ error !== null &&
4
+ "name" in error &&
5
+ (error.name === "AbortError" || error.name === "APIUserAbortError");
6
+ }
7
+ export function getErrorMessage(error) {
8
+ return error instanceof Error ? error.message : String(error);
9
+ }
package/dist/index.d.ts CHANGED
@@ -1,58 +1,21 @@
1
- import type { AdminUser, AdminForthResource, IAdminForth, IHttpServer } from "adminforth";
1
+ import type { AdminForthResource, IAdminForth, IHttpServer } from "adminforth";
2
2
  import { AdminForthPlugin } from "adminforth";
3
3
  import type { PluginOptions } from './types.js';
4
- import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
5
- import { type PreviousUserMessage } from "./agent/languageDetect.js";
6
- import type { AgentEventEmitter } from "./agentEvents.js";
7
- import type { CurrentPageContext } from "./agent/tools/getUserLocation.js";
8
4
  export type { AgentEvent, AgentEventEmitter } from "./agentEvents.js";
9
- type AgentTurnRunInput = {
10
- prompt: string;
11
- sessionId: string;
12
- turnId: string;
13
- previousUserMessages: PreviousUserMessage[];
14
- modeName?: string | null;
15
- userTimeZone: string;
16
- currentPage?: CurrentPageContext;
17
- abortSignal?: AbortSignal;
18
- adminUser: AdminUser;
19
- sequenceDebugCollector: ReturnType<typeof createSequenceDebugCollector>;
20
- emit?: AgentEventEmitter;
21
- };
22
- type RunAndPersistAgentResponseInput = Omit<AgentTurnRunInput, "turnId" | "sequenceDebugCollector" | "previousUserMessages"> & {
23
- failureLogMessage: string;
24
- abortLogMessage: string;
25
- };
26
- type HandleTurnInput = Omit<RunAndPersistAgentResponseInput, "failureLogMessage" | "abortLogMessage"> & {
27
- emit: AgentEventEmitter;
28
- failureLogMessage?: string;
29
- abortLogMessage?: string;
30
- };
31
5
  export default class AdminForthAgentPlugin extends AdminForthPlugin {
32
6
  options: PluginOptions;
33
7
  agentSystemPromptPromise: Promise<string>;
34
8
  private checkpointer;
9
+ private sessionStore;
10
+ private agentTurnService;
11
+ private chatSurfaceService;
12
+ private chatSurfaceSettingsPageRegistered;
35
13
  private parseBody;
36
- private createNewTurn;
37
- private getSessionTurns;
38
- private getPreviousUserMessages;
39
- private getChatSurfaceSessionId;
40
- private getOrCreateChatSurfaceSession;
41
14
  private getCheckpointer;
42
15
  private getInternalAgentResourceIds;
43
16
  constructor(options: PluginOptions);
44
17
  modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource): Promise<void>;
45
18
  validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource): void;
46
19
  instanceUniqueRepresentation(pluginOptions: any): string;
47
- private runAgentTurn;
48
- private runAndPersistAgentResponse;
49
- handleTurn(input: HandleTurnInput): Promise<{
50
- text: string;
51
- turnId: any;
52
- aborted: boolean;
53
- failed: boolean;
54
- }>;
55
- private createChatSurfaceEventEmitter;
56
- private handleChatSurfaceMessage;
57
20
  setupEndpoints(server: IHttpServer): void;
58
21
  }