@adminforth/agent 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.woodpecker/buildRelease.sh +13 -0
  2. package/.woodpecker/buildSlackNotify.sh +46 -0
  3. package/.woodpecker/release.yml +57 -0
  4. package/agent/middleware/apiBasedTools.ts +109 -0
  5. package/agent/middleware/sequenceDebug.ts +302 -0
  6. package/agent/simpleAgent.ts +291 -0
  7. package/agent/skills/registry.ts +135 -0
  8. package/agent/systemPrompt.ts +69 -0
  9. package/agent/toolCallEvents.ts +17 -0
  10. package/agent/tools/apiTool.ts +99 -0
  11. package/agent/tools/fetchSkill.ts +58 -0
  12. package/agent/tools/fetchToolSchema.ts +50 -0
  13. package/agent/tools/index.ts +26 -0
  14. package/apiBasedTools.ts +625 -0
  15. package/build.log +30 -0
  16. package/custom/ChatSurface.vue +184 -0
  17. package/custom/ConversationArea.vue +175 -0
  18. package/custom/Message.vue +206 -0
  19. package/custom/SessionsHistory.vue +93 -0
  20. package/custom/ToolRenderer.vue +131 -0
  21. package/custom/ToolsGroup.vue +67 -0
  22. package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +301 -0
  23. package/custom/incremark_code_renderers/incremarkCodeHighlight.ts +285 -0
  24. package/custom/incremark_code_renderers/incremarkRenderer.ts +653 -0
  25. package/custom/incremark_code_renderers/renderIncremarkMarkdown.ts +118 -0
  26. package/custom/package.json +26 -0
  27. package/custom/pnpm-lock.yaml +1467 -0
  28. package/custom/skills/fetch_data/SKILL.md +15 -0
  29. package/custom/skills/mutate_data/SKILL.md +108 -0
  30. package/custom/tsconfig.json +16 -0
  31. package/custom/types.ts +34 -0
  32. package/custom/useAgentStore.ts +349 -0
  33. package/dist/agent/middleware/apiBasedTools.js +91 -0
  34. package/dist/agent/middleware/sequenceDebug.js +210 -0
  35. package/dist/agent/simpleAgent.js +173 -0
  36. package/dist/agent/skills/registry.js +108 -0
  37. package/dist/agent/systemPrompt.js +64 -0
  38. package/dist/agent/toolCallEvents.js +1 -0
  39. package/dist/agent/tools/apiTool.js +93 -0
  40. package/dist/agent/tools/fetchSkill.js +51 -0
  41. package/dist/agent/tools/fetchToolSchema.js +36 -0
  42. package/dist/agent/tools/index.js +28 -0
  43. package/dist/apiBasedTools.js +412 -0
  44. package/dist/custom/ChatSurface.vue +184 -0
  45. package/dist/custom/ConversationArea.vue +175 -0
  46. package/dist/custom/Message.vue +206 -0
  47. package/dist/custom/SessionsHistory.vue +93 -0
  48. package/dist/custom/ToolRenderer.vue +131 -0
  49. package/dist/custom/ToolsGroup.vue +67 -0
  50. package/dist/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +301 -0
  51. package/dist/custom/incremark_code_renderers/incremarkCodeHighlight.ts +285 -0
  52. package/dist/custom/incremark_code_renderers/incremarkRenderer.ts +653 -0
  53. package/dist/custom/incremark_code_renderers/renderIncremarkMarkdown.ts +118 -0
  54. package/dist/custom/package.json +26 -0
  55. package/dist/custom/pnpm-lock.yaml +1467 -0
  56. package/dist/custom/skills/fetch_data/SKILL.md +15 -0
  57. package/dist/custom/skills/mutate_data/SKILL.md +108 -0
  58. package/dist/custom/tsconfig.json +16 -0
  59. package/dist/custom/types.ts +34 -0
  60. package/dist/custom/useAgentStore.ts +349 -0
  61. package/dist/index.js +415 -0
  62. package/dist/types.js +1 -0
  63. package/index.ts +457 -0
  64. package/package.json +58 -0
  65. package/tsconfig.json +13 -0
  66. package/types.ts +45 -0
package/dist/index.js ADDED
@@ -0,0 +1,415 @@
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
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
11
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
12
+ var m = o[Symbol.asyncIterator], i;
13
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
14
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
15
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
16
+ };
17
+ import { AdminForthPlugin, logger, Filters, Sorts } from "adminforth";
18
+ import { randomUUID } from 'crypto';
19
+ import { HumanMessage, SystemMessage } from "langchain";
20
+ import { createAgentChatModel, callAgent } from "./agent/simpleAgent.js";
21
+ import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
22
+ import { prepareApiBasedTools as buildApiBasedTools, } from './apiBasedTools.js';
23
+ import { buildAgentSystemPrompt, DEFAULT_AGENT_SYSTEM_PROMPT, } from "./agent/systemPrompt.js";
24
+ import { ALWAYS_AVAILABLE_API_TOOL_NAMES } from "./agent/tools/index.js";
25
+ function isAggregateErrorLike(error) {
26
+ return typeof error === "object" && error !== null && Array.isArray(error.errors);
27
+ }
28
+ function formatAgentError(error) {
29
+ var _a, _b;
30
+ if (isAggregateErrorLike(error)) {
31
+ const nestedErrors = error.errors
32
+ .map((nestedError, index) => {
33
+ var _a;
34
+ if (nestedError instanceof Error) {
35
+ return `${index + 1}. ${(_a = nestedError.stack) !== null && _a !== void 0 ? _a : nestedError.message}`;
36
+ }
37
+ return `${index + 1}. ${String(nestedError)}`;
38
+ })
39
+ .join("\n");
40
+ return `${(_a = error.stack) !== null && _a !== void 0 ? _a : error.message}\nNested errors:\n${nestedErrors}`;
41
+ }
42
+ if (error instanceof Error) {
43
+ return (_b = error.stack) !== null && _b !== void 0 ? _b : error.message;
44
+ }
45
+ return String(error);
46
+ }
47
+ function assertRequiredApiTool(apiBasedTools, toolName) {
48
+ if (toolName in apiBasedTools) {
49
+ return;
50
+ }
51
+ const availableToolNames = Object.keys(apiBasedTools).sort().join(", ");
52
+ throw new Error(`Required API tool "${toolName}" is missing from AdminForth Agent tools. Available tools: ${availableToolNames}`);
53
+ }
54
+ export default class AdminForthAgentPlugin extends AdminForthPlugin {
55
+ createNewTurn(sessionId, prompt, response) {
56
+ return __awaiter(this, void 0, void 0, function* () {
57
+ const turnId = randomUUID();
58
+ const turnRecord = {
59
+ [this.options.turnResource.idField]: turnId,
60
+ [this.options.turnResource.sessionIdField]: sessionId,
61
+ [this.options.turnResource.promptField]: prompt,
62
+ [this.options.turnResource.responseField]: response || "not_finished",
63
+ };
64
+ const newTurn = yield this.adminforth.resource(this.options.turnResource.resourceId).create(turnRecord);
65
+ return newTurn.createdRecord[this.options.turnResource.idField];
66
+ });
67
+ }
68
+ updateTurn(turnId, updates) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ yield this.adminforth.resource(this.options.turnResource.resourceId).update(turnId, updates);
71
+ return { ok: true };
72
+ });
73
+ }
74
+ getSessionTurns(sessionId) {
75
+ return __awaiter(this, void 0, void 0, function* () {
76
+ const turns = yield this.adminforth.resource(this.options.turnResource.resourceId).list([Filters.EQ(this.options.turnResource.sessionIdField, sessionId)], undefined, undefined, [Sorts.ASC(this.options.turnResource.createdAtField)]);
77
+ return turns.map(turn => ({
78
+ prompt: turn[this.options.turnResource.promptField],
79
+ response: turn[this.options.turnResource.responseField],
80
+ }));
81
+ });
82
+ }
83
+ constructor(options) {
84
+ super(options, import.meta.url);
85
+ this.apiBasedTools = {};
86
+ this.agentSystemPromptPromise = Promise.resolve(DEFAULT_AGENT_SYSTEM_PROMPT);
87
+ this.options = options;
88
+ this.shouldHaveSingleInstancePerWholeApp = () => false;
89
+ }
90
+ modifyResourceConfig(adminforth, resourceConfig) {
91
+ const _super = Object.create(null, {
92
+ modifyResourceConfig: { get: () => super.modifyResourceConfig }
93
+ });
94
+ return __awaiter(this, void 0, void 0, function* () {
95
+ _super.modifyResourceConfig.call(this, adminforth, resourceConfig);
96
+ if (!this.adminforth.config.customization.globalInjections.header) {
97
+ this.adminforth.config.customization.globalInjections.header = [];
98
+ }
99
+ this.adminforth.config.customization.globalInjections.header.push({
100
+ file: this.componentPath("ChatSurface.vue"),
101
+ meta: {
102
+ pluginInstanceId: this.pluginInstanceId,
103
+ }
104
+ });
105
+ if (!this.pluginOptions.completionAdapter) {
106
+ throw new Error("CompletionAdapter is required for AdminForthAgentPlugin");
107
+ }
108
+ if (!this.pluginOptions.sessionResource) {
109
+ throw new Error("sessionResource is required for AdminForthAgentPlugin");
110
+ }
111
+ });
112
+ }
113
+ validateConfigAfterDiscover(adminforth, resourceConfig) {
114
+ this.apiBasedTools = buildApiBasedTools(adminforth);
115
+ for (const toolName of ALWAYS_AVAILABLE_API_TOOL_NAMES) {
116
+ assertRequiredApiTool(this.apiBasedTools, toolName);
117
+ }
118
+ assertRequiredApiTool(this.apiBasedTools, "update_record");
119
+ this.agentSystemPromptPromise = buildAgentSystemPrompt(adminforth);
120
+ }
121
+ instanceUniqueRepresentation(pluginOptions) {
122
+ return `single`;
123
+ }
124
+ setupEndpoints(server) {
125
+ server.endpoint({
126
+ method: 'POST',
127
+ path: `/agent/response`,
128
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, _raw_express_res }) {
129
+ var _b, e_1, _c, _d;
130
+ var _e, _f, _g;
131
+ const res = _raw_express_res;
132
+ const messageId = randomUUID();
133
+ const prompt = body.message;
134
+ const userTimeZone = (_e = body.timeZone) !== null && _e !== void 0 ? _e : 'UTC';
135
+ const sessionId = body.sessionId || (adminUser === null || adminUser === void 0 ? void 0 : adminUser.pk) || (adminUser === null || adminUser === void 0 ? void 0 : adminUser.username) || 'default';
136
+ const turnId = yield this.createNewTurn(sessionId, prompt);
137
+ let fullResponse = "";
138
+ let isStreamClosed = false;
139
+ const sequenceDebugCollector = createSequenceDebugCollector();
140
+ res.writeHead(200, {
141
+ 'Content-Type': 'text/event-stream',
142
+ 'Cache-Control': 'no-cache',
143
+ 'Connection': 'keep-alive',
144
+ 'x-vercel-ai-ui-message-stream': 'v1',
145
+ });
146
+ const send = (obj) => {
147
+ if (isStreamClosed || res.writableEnded || res.destroyed) {
148
+ return;
149
+ }
150
+ res.write(`data: ${JSON.stringify(obj)}\n\n`);
151
+ };
152
+ const emitToolCallEvent = (event) => {
153
+ sequenceDebugCollector.handleToolCallEvent(event);
154
+ send({
155
+ type: "data-tool-call",
156
+ data: event,
157
+ });
158
+ };
159
+ let activeBlock = null;
160
+ const endActiveBlock = () => {
161
+ if (!activeBlock) {
162
+ return;
163
+ }
164
+ send({
165
+ type: `${activeBlock.type}-end`,
166
+ id: activeBlock.id,
167
+ });
168
+ activeBlock = null;
169
+ };
170
+ const startBlock = (type) => {
171
+ if ((activeBlock === null || activeBlock === void 0 ? void 0 : activeBlock.type) === type) {
172
+ return activeBlock.id;
173
+ }
174
+ endActiveBlock();
175
+ const id = randomUUID();
176
+ activeBlock = { type, id };
177
+ send({
178
+ type: `${type}-start`,
179
+ id,
180
+ });
181
+ return id;
182
+ };
183
+ const endStream = () => {
184
+ if (isStreamClosed || res.writableEnded || res.destroyed) {
185
+ return;
186
+ }
187
+ endActiveBlock();
188
+ send({
189
+ type: 'finish',
190
+ });
191
+ res.write(`data: [DONE]\n\n`);
192
+ isStreamClosed = true;
193
+ res.end();
194
+ };
195
+ try {
196
+ send({
197
+ type: 'start',
198
+ messageId,
199
+ });
200
+ const maxTokens = (_f = this.options.maxTokens) !== null && _f !== void 0 ? _f : 10000;
201
+ const reasoning = (_g = this.options.reasoning) !== null && _g !== void 0 ? _g : 'low';
202
+ const summaryReasoning = 'low';
203
+ const model = createAgentChatModel({
204
+ adapter: this.options.completionAdapter,
205
+ maxTokens,
206
+ reasoning,
207
+ });
208
+ const summaryModel = createAgentChatModel({
209
+ adapter: this.options.completionAdapter,
210
+ maxTokens,
211
+ reasoning: summaryReasoning,
212
+ });
213
+ const systemPrompt = yield this.agentSystemPromptPromise;
214
+ const stream = yield callAgent({
215
+ name: `adminforth-agent-${this.pluginInstanceId}`,
216
+ model,
217
+ summaryModel,
218
+ messages: [
219
+ new SystemMessage(systemPrompt),
220
+ new HumanMessage(prompt),
221
+ ],
222
+ adminUser,
223
+ apiBasedTools: this.apiBasedTools,
224
+ customComponentsDir: this.adminforth.config.customization.customComponentsDir,
225
+ sessionId,
226
+ turnId,
227
+ userTimeZone,
228
+ emitToolCallEvent,
229
+ sequenceDebugSink: sequenceDebugCollector,
230
+ });
231
+ try {
232
+ for (var _h = true, _j = __asyncValues(stream), _k; _k = yield _j.next(), _b = _k.done, !_b; _h = true) {
233
+ _d = _k.value;
234
+ _h = false;
235
+ const rawChunk = _d;
236
+ const [token, metadata] = rawChunk;
237
+ const nodeName = typeof (metadata === null || metadata === void 0 ? void 0 : metadata.langgraph_node) === "string"
238
+ ? metadata.langgraph_node
239
+ : "";
240
+ if (nodeName && !["model", "model_request"].includes(nodeName)) {
241
+ continue;
242
+ }
243
+ const blocks = Array.isArray(token === null || token === void 0 ? void 0 : token.contentBlocks)
244
+ ? token.contentBlocks
245
+ : Array.isArray(token === null || token === void 0 ? void 0 : token.content)
246
+ ? token.content
247
+ : [];
248
+ const reasoningDelta = blocks
249
+ .filter((b) => (b === null || b === void 0 ? void 0 : b.type) === "reasoning")
250
+ .map((b) => { var _a; return String((_a = b.reasoning) !== null && _a !== void 0 ? _a : ""); })
251
+ .join("");
252
+ const textDelta = blocks
253
+ .filter((b) => (b === null || b === void 0 ? void 0 : b.type) === "text")
254
+ .map((b) => { var _a; return String((_a = b.text) !== null && _a !== void 0 ? _a : ""); })
255
+ .join("");
256
+ if (reasoningDelta) {
257
+ const reasoningId = startBlock('reasoning');
258
+ send({
259
+ type: 'reasoning-delta',
260
+ id: reasoningId,
261
+ delta: reasoningDelta,
262
+ });
263
+ }
264
+ if (textDelta) {
265
+ const textId = startBlock('text');
266
+ fullResponse += textDelta;
267
+ send({
268
+ type: 'text-delta',
269
+ id: textId,
270
+ delta: textDelta,
271
+ });
272
+ }
273
+ }
274
+ }
275
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
276
+ finally {
277
+ try {
278
+ if (!_h && !_b && (_c = _j.return)) yield _c.call(_j);
279
+ }
280
+ finally { if (e_1) throw e_1.error; }
281
+ }
282
+ }
283
+ catch (error) {
284
+ logger.error(`Agent response streaming failed:\n${formatAgentError(error)}`);
285
+ sequenceDebugCollector.flush();
286
+ const textId = startBlock('text');
287
+ send({
288
+ type: 'text-delta',
289
+ id: textId,
290
+ delta: 'Agent response failed. Check server logs for details.',
291
+ });
292
+ }
293
+ sequenceDebugCollector.flush();
294
+ const turnUpdates = {
295
+ [this.options.turnResource.responseField]: fullResponse,
296
+ };
297
+ if (this.options.turnResource.debugField) {
298
+ turnUpdates[this.options.turnResource.debugField] = sequenceDebugCollector.getHistory();
299
+ }
300
+ yield this.updateTurn(turnId, turnUpdates);
301
+ endStream();
302
+ return null;
303
+ })
304
+ });
305
+ server.endpoint({
306
+ method: 'POST',
307
+ path: `/agent/get-sessions`,
308
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser }) {
309
+ const userId = adminUser.pk;
310
+ const sessions = yield this.adminforth.resource(this.pluginOptions.sessionResource.resourceId).list([Filters.EQ(this.pluginOptions.sessionResource.askerIdField, userId)], undefined, undefined, [Sorts.DESC(this.pluginOptions.sessionResource.createdAtField)]);
311
+ const sessionsToReturn = [];
312
+ for (const session of sessions) {
313
+ sessionsToReturn.push({
314
+ sessionId: session[this.pluginOptions.sessionResource.idField],
315
+ title: session[this.pluginOptions.sessionResource.titleField],
316
+ timestamp: session[this.pluginOptions.sessionResource.createdAtField],
317
+ });
318
+ }
319
+ return {
320
+ sessions: sessionsToReturn
321
+ };
322
+ })
323
+ });
324
+ server.endpoint({
325
+ method: 'POST',
326
+ path: `/agent/get-session-info`,
327
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser }) {
328
+ const userId = adminUser.pk;
329
+ const sessionId = body.sessionId;
330
+ const session = yield this.adminforth.resource(this.pluginOptions.sessionResource.resourceId).get([Filters.EQ(this.pluginOptions.sessionResource.idField, sessionId)]);
331
+ if (!session) {
332
+ return {
333
+ error: 'Session not found'
334
+ };
335
+ }
336
+ if (session[this.pluginOptions.sessionResource.askerIdField] !== userId) {
337
+ return {
338
+ error: 'Unauthorized'
339
+ };
340
+ }
341
+ const turns = yield this.getSessionTurns(sessionId);
342
+ const messagesToReturn = [];
343
+ for (const turn of turns) {
344
+ messagesToReturn.push({
345
+ text: turn.prompt,
346
+ role: 'user',
347
+ });
348
+ if (turn.response !== "not_finished") {
349
+ messagesToReturn.push({
350
+ text: turn.response,
351
+ role: 'assistant',
352
+ });
353
+ }
354
+ }
355
+ const sessionToReturn = {
356
+ sessionId: session[this.pluginOptions.sessionResource.idField],
357
+ title: session[this.pluginOptions.sessionResource.titleField],
358
+ timestamp: session[this.pluginOptions.sessionResource.createdAtField],
359
+ messages: messagesToReturn
360
+ };
361
+ return {
362
+ session: sessionToReturn
363
+ };
364
+ })
365
+ });
366
+ server.endpoint({
367
+ method: 'POST',
368
+ path: `/agent/create-session`,
369
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser }) {
370
+ const triggerMessage = body.triggerMessage;
371
+ const userId = adminUser.pk;
372
+ const title = triggerMessage ? (triggerMessage.length > 40 ? triggerMessage.slice(0, 40) + '...' : triggerMessage) : 'New Session';
373
+ const newSession = {
374
+ [this.pluginOptions.sessionResource.idField]: randomUUID(),
375
+ [this.pluginOptions.sessionResource.titleField]: title,
376
+ [this.pluginOptions.sessionResource.askerIdField]: userId,
377
+ };
378
+ yield this.adminforth.resource(this.pluginOptions.sessionResource.resourceId).create(newSession);
379
+ return {
380
+ sessionId: newSession[this.pluginOptions.sessionResource.idField],
381
+ title: newSession[this.pluginOptions.sessionResource.titleField],
382
+ timestamp: newSession[this.pluginOptions.sessionResource.createdAtField],
383
+ messages: []
384
+ };
385
+ })
386
+ });
387
+ server.endpoint({
388
+ method: 'POST',
389
+ path: `/agent/delete-session`,
390
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser }) {
391
+ const sessionId = body.sessionId;
392
+ const userId = adminUser.pk;
393
+ const session = yield this.adminforth.resource(this.pluginOptions.sessionResource.resourceId).get([Filters.EQ(this.pluginOptions.sessionResource.idField, sessionId)]);
394
+ if (!session) {
395
+ return {
396
+ error: 'Session not found'
397
+ };
398
+ }
399
+ if (session[this.pluginOptions.sessionResource.askerIdField] !== userId) {
400
+ return {
401
+ error: 'Unauthorized'
402
+ };
403
+ }
404
+ yield this.adminforth.resource(this.pluginOptions.sessionResource.resourceId).delete(sessionId);
405
+ const turns = yield this.adminforth.resource(this.pluginOptions.turnResource.resourceId).list([Filters.EQ(this.pluginOptions.turnResource.sessionIdField, sessionId)]);
406
+ for (const turn of turns) {
407
+ yield this.adminforth.resource(this.pluginOptions.turnResource.resourceId).delete(turn[this.pluginOptions.turnResource.idField]);
408
+ }
409
+ return {
410
+ ok: true
411
+ };
412
+ })
413
+ });
414
+ }
415
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};