@exulu/backend 0.3.13 → 1.2.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.
package/dist/index.cjs CHANGED
@@ -36,7 +36,6 @@ __export(index_exports, {
36
36
  ExuluApp: () => ExuluApp,
37
37
  ExuluAuthentication: () => authentication,
38
38
  ExuluChunkers: () => ExuluChunkers,
39
- ExuluCli: () => cli_default,
40
39
  ExuluContext: () => ExuluContext,
41
40
  ExuluDatabase: () => ExuluDatabase,
42
41
  ExuluEmbedder: () => ExuluEmbedder,
@@ -146,6 +145,47 @@ async function postgresClient() {
146
145
  }
147
146
 
148
147
  // src/postgres/core-schema.ts
148
+ var agentMessagesSchema = {
149
+ name: {
150
+ plural: "agent_messages",
151
+ singular: "agent_message"
152
+ },
153
+ fields: [
154
+ {
155
+ name: "content",
156
+ type: "text"
157
+ },
158
+ {
159
+ name: "title",
160
+ type: "text"
161
+ },
162
+ {
163
+ name: "session",
164
+ type: "text"
165
+ }
166
+ ]
167
+ };
168
+ var agentSessionsSchema = {
169
+ name: {
170
+ plural: "agent_sessions",
171
+ singular: "agent_session"
172
+ },
173
+ fields: [
174
+ {
175
+ name: "agent",
176
+ type: "uuid"
177
+ },
178
+ {
179
+ name: "user",
180
+ // next auth stores users with id type SERIAL, so we need to use number
181
+ type: "number"
182
+ },
183
+ {
184
+ name: "title",
185
+ type: "text"
186
+ }
187
+ ]
188
+ };
149
189
  var usersSchema = {
150
190
  name: {
151
191
  plural: "users",
@@ -203,14 +243,13 @@ var usersSchema = {
203
243
  name: "last_used",
204
244
  type: "date"
205
245
  },
246
+ {
247
+ name: "anthropic_token",
248
+ type: "text"
249
+ },
206
250
  {
207
251
  name: "role",
208
- type: "reference",
209
- references: {
210
- table: "roles",
211
- field: "id",
212
- onDelete: "CASCADE"
213
- }
252
+ type: "uuid"
214
253
  }
215
254
  ]
216
255
  };
@@ -256,10 +295,6 @@ var statisticsSchema = {
256
295
  {
257
296
  name: "total",
258
297
  type: "number"
259
- },
260
- {
261
- name: "timeseries",
262
- type: "json"
263
298
  }
264
299
  ]
265
300
  };
@@ -315,11 +350,11 @@ var evalResultsSchema = {
315
350
  },
316
351
  {
317
352
  name: "agent_id",
318
- type: "text"
353
+ type: "uuid"
319
354
  },
320
355
  {
321
356
  name: "workflow_id",
322
- type: "text"
357
+ type: "uuid"
323
358
  },
324
359
  {
325
360
  name: "eval_type",
@@ -335,50 +370,6 @@ var evalResultsSchema = {
335
370
  }
336
371
  ]
337
372
  };
338
- var threadsSchema = {
339
- name: {
340
- plural: "threads",
341
- singular: "thread"
342
- },
343
- fields: [
344
- {
345
- name: "resourceId",
346
- type: "text"
347
- },
348
- {
349
- name: "title",
350
- type: "text"
351
- },
352
- {
353
- name: "metadata",
354
- type: "text"
355
- }
356
- ]
357
- };
358
- var messagesSchema = {
359
- name: {
360
- plural: "messages",
361
- singular: "message"
362
- },
363
- fields: [
364
- {
365
- name: "thread_id",
366
- type: "text"
367
- },
368
- {
369
- name: "content",
370
- type: "text"
371
- },
372
- {
373
- name: "role",
374
- type: "text"
375
- },
376
- {
377
- name: "type",
378
- type: "text"
379
- }
380
- ]
381
- };
382
373
  var jobsSchema = {
383
374
  name: {
384
375
  plural: "jobs",
@@ -411,19 +402,20 @@ var jobsSchema = {
411
402
  },
412
403
  {
413
404
  name: "agent",
414
- type: "text"
405
+ type: "uuid"
415
406
  },
416
407
  {
417
408
  name: "workflow",
418
- type: "text"
409
+ type: "uuid"
419
410
  },
420
411
  {
421
412
  name: "user",
422
- type: "text"
413
+ // next auth stores users with id type SERIAL, so we need to use number
414
+ type: "number"
423
415
  },
424
416
  {
425
417
  name: "item",
426
- type: "text"
418
+ type: "uuid"
427
419
  },
428
420
  {
429
421
  name: "steps",
@@ -517,7 +509,7 @@ var mapType = (t, type, name, defaultValue) => {
517
509
  return;
518
510
  }
519
511
  if (type === "date") {
520
- t.date(name);
512
+ t.timestamp(name);
521
513
  return;
522
514
  }
523
515
  if (type === "uuid") {
@@ -587,23 +579,44 @@ var generateApiKey = async (name, email) => {
587
579
 
588
580
  // src/postgres/init-db.ts
589
581
  var up = async function(knex) {
582
+ if (!await knex.schema.hasTable("agent_sessions")) {
583
+ await knex.schema.createTable("agent_sessions", (table) => {
584
+ table.uuid("id").primary().defaultTo(knex.fn.uuid());
585
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
586
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
587
+ for (const field of agentSessionsSchema.fields) {
588
+ const { type, name, default: defaultValue } = field;
589
+ if (!type || !name) {
590
+ continue;
591
+ }
592
+ mapType(table, type, sanitizeName(name), defaultValue);
593
+ }
594
+ });
595
+ }
596
+ if (!await knex.schema.hasTable("agent_messages")) {
597
+ await knex.schema.createTable("agent_messages", (table) => {
598
+ table.uuid("id").primary().defaultTo(knex.fn.uuid());
599
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
600
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
601
+ for (const field of agentMessagesSchema.fields) {
602
+ const { type, name, default: defaultValue } = field;
603
+ if (!type || !name) {
604
+ continue;
605
+ }
606
+ mapType(table, type, sanitizeName(name), defaultValue);
607
+ }
608
+ });
609
+ }
590
610
  if (!await knex.schema.hasTable("roles")) {
591
611
  await knex.schema.createTable("roles", (table) => {
592
612
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
593
- table.date("createdAt").defaultTo(knex.fn.now());
594
- table.date("updatedAt").defaultTo(knex.fn.now());
613
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
614
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
595
615
  for (const field of rolesSchema.fields) {
596
- const { type, name, references, default: defaultValue } = field;
616
+ const { type, name, default: defaultValue } = field;
597
617
  if (!type || !name) {
598
618
  continue;
599
619
  }
600
- if (type === "reference") {
601
- if (!references) {
602
- throw new Error("Field with type reference must have a reference definition.");
603
- }
604
- table.uuid(name).references(references.field).inTable(references.table);
605
- return;
606
- }
607
620
  mapType(table, type, sanitizeName(name), defaultValue);
608
621
  }
609
622
  });
@@ -611,20 +624,13 @@ var up = async function(knex) {
611
624
  if (!await knex.schema.hasTable("eval_results")) {
612
625
  await knex.schema.createTable("eval_results", (table) => {
613
626
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
614
- table.date("createdAt").defaultTo(knex.fn.now());
615
- table.date("updatedAt").defaultTo(knex.fn.now());
627
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
628
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
616
629
  for (const field of evalResultsSchema.fields) {
617
- const { type, name, references, default: defaultValue } = field;
630
+ const { type, name, default: defaultValue } = field;
618
631
  if (!type || !name) {
619
632
  continue;
620
633
  }
621
- if (type === "reference") {
622
- if (!references) {
623
- throw new Error("Field with type reference must have a reference definition.");
624
- }
625
- table.uuid(name).references(references.field).inTable(references.table);
626
- return;
627
- }
628
634
  mapType(table, type, sanitizeName(name), defaultValue);
629
635
  }
630
636
  });
@@ -632,62 +638,41 @@ var up = async function(knex) {
632
638
  if (!await knex.schema.hasTable("statistics")) {
633
639
  await knex.schema.createTable("statistics", (table) => {
634
640
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
635
- table.date("createdAt").defaultTo(knex.fn.now());
636
- table.date("updatedAt").defaultTo(knex.fn.now());
641
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
642
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
637
643
  for (const field of statisticsSchema.fields) {
638
- const { type, name, references, default: defaultValue } = field;
644
+ const { type, name, default: defaultValue } = field;
639
645
  if (!type || !name) {
640
646
  continue;
641
647
  }
642
- if (type === "reference") {
643
- if (!references) {
644
- throw new Error("Field with type reference must have a reference definition.");
645
- }
646
- table.uuid(name).references(references.field).inTable(references.table);
647
- return;
648
- }
649
648
  mapType(table, type, sanitizeName(name), defaultValue);
650
649
  }
651
650
  });
652
651
  }
653
652
  if (!await knex.schema.hasTable("jobs")) {
654
653
  await knex.schema.createTable("jobs", (table) => {
655
- table.increments("id").primary();
656
- table.date("createdAt").defaultTo(knex.fn.now());
657
- table.date("updatedAt").defaultTo(knex.fn.now());
654
+ table.uuid("id").primary().defaultTo(knex.fn.uuid());
655
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
656
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
658
657
  for (const field of jobsSchema.fields) {
659
- const { type, name, references, default: defaultValue } = field;
658
+ const { type, name, default: defaultValue } = field;
660
659
  if (!type || !name) {
661
660
  continue;
662
661
  }
663
- if (type === "reference") {
664
- if (!references) {
665
- throw new Error("Field with type reference must have a reference definition.");
666
- }
667
- table.uuid(name).references(references.field).inTable(references.table);
668
- return;
669
- }
670
662
  mapType(table, type, sanitizeName(name), defaultValue);
671
663
  }
672
664
  });
673
665
  }
674
666
  if (!await knex.schema.hasTable("agents")) {
675
667
  await knex.schema.createTable("agents", (table) => {
676
- table.increments("id").primary();
677
- table.date("createdAt").defaultTo(knex.fn.now());
678
- table.date("updatedAt").defaultTo(knex.fn.now());
668
+ table.uuid("id").primary().defaultTo(knex.fn.uuid());
669
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
670
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
679
671
  for (const field of agentsSchema.fields) {
680
- const { type, name, references, default: defaultValue } = field;
672
+ const { type, name, default: defaultValue } = field;
681
673
  if (!type || !name) {
682
674
  continue;
683
675
  }
684
- if (type === "reference") {
685
- if (!references) {
686
- throw new Error("Field with type reference must have a reference definition.");
687
- }
688
- table.uuid(name).references(references.field).inTable(references.table);
689
- return;
690
- }
691
676
  mapType(table, type, sanitizeName(name), defaultValue);
692
677
  }
693
678
  });
@@ -703,8 +688,8 @@ var up = async function(knex) {
703
688
  if (!await knex.schema.hasTable("users")) {
704
689
  await knex.schema.createTable("users", (table) => {
705
690
  table.increments("id").primary();
706
- table.date("createdAt").defaultTo(knex.fn.now());
707
- table.date("updatedAt").defaultTo(knex.fn.now());
691
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
692
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
708
693
  table.string("name", 255);
709
694
  table.string("password", 255);
710
695
  table.string("email", 255);
@@ -712,20 +697,13 @@ var up = async function(knex) {
712
697
  table.text("image");
713
698
  for (const field of usersSchema.fields) {
714
699
  console.log("[EXULU] field", field);
715
- const { type, name, references, default: defaultValue } = field;
700
+ const { type, name, default: defaultValue } = field;
716
701
  if (name === "id" || name === "name" || name === "email" || name === "emailVerified" || name === "image") {
717
702
  continue;
718
703
  }
719
704
  if (!type || !name) {
720
705
  continue;
721
706
  }
722
- if (type === "reference") {
723
- if (!references) {
724
- throw new Error("Field with type reference must have a reference definition.");
725
- }
726
- table.uuid(name).references(references.field).inTable(references.table);
727
- return;
728
- }
729
707
  mapType(table, type, sanitizeName(name), defaultValue);
730
708
  }
731
709
  });
@@ -746,14 +724,6 @@ var up = async function(knex) {
746
724
  table.text("token_type");
747
725
  });
748
726
  }
749
- if (!await knex.schema.hasTable("sessions")) {
750
- await knex.schema.createTable("sessions", (table) => {
751
- table.increments("id").primary();
752
- table.integer("userId").notNullable();
753
- table.timestamp("expires", { useTz: true }).notNullable();
754
- table.string("sessionToken", 255).notNullable();
755
- });
756
- }
757
727
  };
758
728
  var execute = async () => {
759
729
  console.log("[EXULU] Initializing database.");
@@ -1004,6 +974,35 @@ var ExuluEvalUtils = {
1004
974
  };
1005
975
 
1006
976
  // src/registry/classes.ts
977
+ function sanitizeToolName(name) {
978
+ if (typeof name !== "string") return "";
979
+ let sanitized = name.replace(/[^a-zA-Z0-9_-]+/g, "_");
980
+ sanitized = sanitized.replace(/^_+|_+$/g, "");
981
+ if (sanitized.length > 128) {
982
+ sanitized = sanitized.substring(0, 128);
983
+ }
984
+ return sanitized;
985
+ }
986
+ var convertToolsArrayToObject = (tools) => {
987
+ if (!tools) return {};
988
+ const sanitizedTools = tools ? tools.map((tool2) => ({
989
+ ...tool2,
990
+ name: sanitizeToolName(tool2.name)
991
+ })) : [];
992
+ const askForConfirmation = {
993
+ description: "Ask the user for confirmation.",
994
+ parameters: import_zod2.z.object({
995
+ message: import_zod2.z.string().describe("The message to ask for confirmation.")
996
+ })
997
+ };
998
+ return {
999
+ ...sanitizedTools?.reduce(
1000
+ (prev, cur) => ({ ...prev, [cur.name]: cur.tool }),
1001
+ {}
1002
+ ),
1003
+ askForConfirmation
1004
+ };
1005
+ };
1007
1006
  function generateSlug(name) {
1008
1007
  const normalized = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
1009
1008
  const lowercase = normalized.toLowerCase();
@@ -1030,54 +1029,119 @@ var ExuluAgent = class {
1030
1029
  name;
1031
1030
  description = "";
1032
1031
  slug = "";
1032
+ type;
1033
1033
  streaming = false;
1034
1034
  rateLimit;
1035
1035
  config;
1036
1036
  // private memory: Memory | undefined; // TODO do own implementation
1037
- tools;
1038
1037
  evals;
1039
1038
  model;
1040
1039
  capabilities;
1041
- constructor({ id, name, description, config, rateLimit, capabilities, tools, evals }) {
1040
+ constructor({ id, name, description, config, rateLimit, capabilities, type, evals }) {
1042
1041
  this.id = id;
1043
1042
  this.name = name;
1044
1043
  this.evals = evals;
1045
1044
  this.description = description;
1046
1045
  this.rateLimit = rateLimit;
1047
- this.tools = tools;
1048
1046
  this.config = config;
1049
- this.capabilities = capabilities;
1047
+ this.type = type;
1048
+ this.capabilities = capabilities || {
1049
+ images: [],
1050
+ files: [],
1051
+ audio: [],
1052
+ video: []
1053
+ };
1050
1054
  this.slug = `/agents/${generateSlug(this.name)}/run`;
1051
- this.model = this.config.model;
1055
+ this.model = this.config?.model;
1052
1056
  }
1053
- generate = async ({ prompt, stream }) => {
1057
+ // Exports the agent as a tool that can be used by another agent
1058
+ // todo test this
1059
+ tool = () => {
1060
+ return new ExuluTool({
1061
+ id: this.id,
1062
+ name: `${this.name} agent`,
1063
+ type: "agent",
1064
+ inputSchema: import_zod2.z.object({
1065
+ prompt: import_zod2.z.string()
1066
+ }),
1067
+ description: `A function that calls an AI agent named: ${this.name}. The agent does the following: ${this.description}.`,
1068
+ execute: async ({ prompt }) => {
1069
+ return await this.generateSync({
1070
+ prompt,
1071
+ statistics: {
1072
+ label: "",
1073
+ trigger: "tool"
1074
+ }
1075
+ });
1076
+ }
1077
+ });
1078
+ };
1079
+ generateSync = async ({ messages, prompt, tools, statistics }) => {
1054
1080
  if (!this.model) {
1055
1081
  throw new Error("Model is required for streaming.");
1056
1082
  }
1057
- if (this.config.outputSchema) {
1058
- if (stream) {
1059
- }
1060
- const { object } = await (0, import_ai.generateObject)({
1061
- model: this.model,
1062
- schema: this.config.outputSchema,
1063
- prompt
1064
- });
1065
- return object;
1083
+ if (!this.config) {
1084
+ throw new Error("Config is required for generating.");
1066
1085
  }
1067
- if (stream) {
1068
- const result = (0, import_ai.streamText)({
1069
- model: this.model,
1070
- prompt
1071
- });
1072
- const text2 = await result.text;
1073
- return text2;
1086
+ if (prompt && messages) {
1087
+ throw new Error("Prompt and messages cannot be provided at the same time.");
1074
1088
  }
1075
1089
  const { text } = await (0, import_ai.generateText)({
1076
1090
  model: this.model,
1077
- prompt
1091
+ system: "You are a helpful assistant. When you use a tool to answer a question do not explicitly comment on the result of the tool call unless the user has explicitly you to do something with the result.",
1092
+ messages,
1093
+ prompt,
1094
+ maxRetries: 2,
1095
+ tools: convertToolsArrayToObject(tools),
1096
+ maxSteps: 5
1078
1097
  });
1098
+ if (statistics) {
1099
+ await updateStatistic({
1100
+ name: "count",
1101
+ label: statistics.label,
1102
+ type: STATISTICS_TYPE_ENUM.AGENT_RUN,
1103
+ trigger: statistics.trigger,
1104
+ count: 1
1105
+ });
1106
+ }
1079
1107
  return text;
1080
1108
  };
1109
+ generateStream = ({ messages, prompt, tools, statistics }) => {
1110
+ if (!this.model) {
1111
+ throw new Error("Model is required for streaming.");
1112
+ }
1113
+ if (!this.config) {
1114
+ throw new Error("Config is required for generating.");
1115
+ }
1116
+ if (prompt && messages) {
1117
+ throw new Error("Prompt and messages cannot be provided at the same time.");
1118
+ }
1119
+ return (0, import_ai.streamText)({
1120
+ model: this.model,
1121
+ messages,
1122
+ prompt,
1123
+ system: "You are a helpful assistant. When you use a tool to answer a question do not explicitly comment on the result of the tool call unless the user has explicitly you to do something with the result.",
1124
+ maxRetries: 2,
1125
+ tools: convertToolsArrayToObject(tools),
1126
+ maxSteps: 5,
1127
+ onError: (error) => console.error("[EXULU] chat stream error.", error),
1128
+ onFinish: async ({ response, usage }) => {
1129
+ console.info(
1130
+ "[EXULU] chat stream finished.",
1131
+ usage
1132
+ );
1133
+ if (statistics) {
1134
+ await updateStatistic({
1135
+ name: "count",
1136
+ label: statistics.label,
1137
+ type: STATISTICS_TYPE_ENUM.AGENT_RUN,
1138
+ trigger: statistics.trigger,
1139
+ count: 1
1140
+ });
1141
+ }
1142
+ }
1143
+ });
1144
+ };
1081
1145
  };
1082
1146
  var ExuluEmbedder = class {
1083
1147
  id;
@@ -1100,6 +1164,13 @@ var ExuluEmbedder = class {
1100
1164
  }
1101
1165
  async generateFromQuery(query, statistics) {
1102
1166
  if (statistics) {
1167
+ await updateStatistic({
1168
+ name: "count",
1169
+ label: statistics.label,
1170
+ type: STATISTICS_TYPE_ENUM.EMBEDDER_GENERATE,
1171
+ trigger: statistics.trigger,
1172
+ count: 1
1173
+ });
1103
1174
  }
1104
1175
  return await this.generateEmbeddings({
1105
1176
  item: {
@@ -1113,6 +1184,13 @@ var ExuluEmbedder = class {
1113
1184
  }
1114
1185
  async generateFromDocument(input, statistics) {
1115
1186
  if (statistics) {
1187
+ await updateStatistic({
1188
+ name: "count",
1189
+ label: statistics.label,
1190
+ type: STATISTICS_TYPE_ENUM.EMBEDDER_GENERATE,
1191
+ trigger: statistics.trigger,
1192
+ count: 1
1193
+ });
1116
1194
  }
1117
1195
  if (!this.chunker) {
1118
1196
  throw new Error("Chunker not found for embedder " + this.name);
@@ -1301,9 +1379,8 @@ var ExuluEval = class {
1301
1379
  if (!data.prompt) {
1302
1380
  throw new Error("Prompt is required for running an agent.");
1303
1381
  }
1304
- const result = await runner.agent.generate({
1305
- prompt: data.prompt,
1306
- stream: false
1382
+ const result = await runner.agent.generateSync({
1383
+ prompt: data.prompt
1307
1384
  });
1308
1385
  data.result = result;
1309
1386
  }
@@ -1398,18 +1475,18 @@ var ExuluTool = class {
1398
1475
  id;
1399
1476
  name;
1400
1477
  description;
1401
- parameters;
1478
+ inputSchema;
1402
1479
  type;
1403
1480
  tool;
1404
- constructor({ id, name, description, parameters, type, execute: execute2 }) {
1481
+ constructor({ id, name, description, inputSchema, type, execute: execute2 }) {
1405
1482
  this.id = id;
1406
1483
  this.name = name;
1407
1484
  this.description = description;
1408
- this.parameters = parameters;
1485
+ this.inputSchema = inputSchema;
1409
1486
  this.type = type;
1410
1487
  this.tool = (0, import_ai.tool)({
1411
1488
  description,
1412
- parameters,
1489
+ parameters: inputSchema || import_zod2.z.object({}),
1413
1490
  execute: execute2
1414
1491
  });
1415
1492
  }
@@ -1465,7 +1542,7 @@ var ExuluContext = class {
1465
1542
  }
1466
1543
  const { db: db2 } = await postgresClient();
1467
1544
  Object.keys(item).forEach((key) => {
1468
- if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert") {
1545
+ if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert" || key === "archived") {
1469
1546
  return;
1470
1547
  }
1471
1548
  const field = this.fields.find((field2) => field2.name === key);
@@ -1512,6 +1589,9 @@ var ExuluContext = class {
1512
1589
  chunk_index: chunk.index,
1513
1590
  embedding: import_knex4.default.toSql(chunk.vector)
1514
1591
  })));
1592
+ await db2.from(this.getTableName()).where({ id }).update({
1593
+ embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
1594
+ }).returning("id");
1515
1595
  }
1516
1596
  return {
1517
1597
  id: result[0].id,
@@ -1541,7 +1621,7 @@ var ExuluContext = class {
1541
1621
  }
1542
1622
  }
1543
1623
  Object.keys(item).forEach((key) => {
1544
- if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert") {
1624
+ if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert" || key === "archived") {
1545
1625
  return;
1546
1626
  }
1547
1627
  const field = this.fields.find((field2) => field2.name === key);
@@ -1592,6 +1672,9 @@ var ExuluContext = class {
1592
1672
  chunk_index: chunk.index,
1593
1673
  embedding: import_knex4.default.toSql(chunk.vector)
1594
1674
  })));
1675
+ await db2.from(this.getTableName()).where({ id: result[0].id }).update({
1676
+ embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
1677
+ }).returning("id");
1595
1678
  }
1596
1679
  return {
1597
1680
  id: result[0].id,
@@ -1601,6 +1684,8 @@ var ExuluContext = class {
1601
1684
  getItems = async ({
1602
1685
  statistics,
1603
1686
  limit,
1687
+ sort,
1688
+ order,
1604
1689
  page,
1605
1690
  name,
1606
1691
  archived,
@@ -1621,6 +1706,9 @@ var ExuluContext = class {
1621
1706
  const columns = await db2(mainTable).columnInfo();
1622
1707
  const totalQuery = db2.count("* as count").from(mainTable).first();
1623
1708
  const itemsQuery = db2.select(Object.keys(columns).map((column) => mainTable + "." + column)).from(mainTable).offset(offset).limit(limit);
1709
+ if (sort) {
1710
+ itemsQuery.orderBy(sort, order === "desc" ? "desc" : "asc");
1711
+ }
1624
1712
  if (typeof name === "string") {
1625
1713
  itemsQuery.whereILike("name", `%${name}%`);
1626
1714
  totalQuery.whereILike("name", `%${name}%`);
@@ -1664,7 +1752,7 @@ var ExuluContext = class {
1664
1752
  }
1665
1753
  itemsQuery.limit(limit * 5);
1666
1754
  if (statistics) {
1667
- updateStatistic({
1755
+ await updateStatistic({
1668
1756
  name: "count",
1669
1757
  label: statistics.label,
1670
1758
  type: STATISTICS_TYPE_ENUM.CONTEXT_RETRIEVE,
@@ -1797,6 +1885,7 @@ var ExuluContext = class {
1797
1885
  table.text("external_id");
1798
1886
  table.integer("textLength");
1799
1887
  table.text("source");
1888
+ table.timestamp("embeddings_updated_at");
1800
1889
  for (const field of this.fields) {
1801
1890
  const { type, name } = field;
1802
1891
  if (!type || !name) {
@@ -1826,15 +1915,15 @@ var ExuluContext = class {
1826
1915
  id: this.id,
1827
1916
  name: `${this.name} context`,
1828
1917
  type: "context",
1829
- parameters: import_zod2.z.object({
1918
+ inputSchema: import_zod2.z.object({
1830
1919
  query: import_zod2.z.string()
1831
1920
  }),
1832
1921
  description: `Gets information from the context called: ${this.name}. The context description is: ${this.description}.`,
1833
- execute: async ({ context }) => {
1922
+ execute: async ({ query }) => {
1834
1923
  return await this.getItems({
1835
1924
  page: 1,
1836
1925
  limit: 10,
1837
- query: context.query,
1926
+ query,
1838
1927
  statistics: {
1839
1928
  label: this.name,
1840
1929
  trigger: "agent"
@@ -1883,7 +1972,32 @@ var ExuluSource = class {
1883
1972
  }
1884
1973
  };
1885
1974
  var updateStatistic = async (statistic) => {
1886
- return;
1975
+ const currentDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1976
+ const { db: db2 } = await postgresClient();
1977
+ const existing = await db2.from("statistics").where({
1978
+ name: statistic.name,
1979
+ label: statistic.label,
1980
+ type: statistic.type,
1981
+ createdAt: currentDate
1982
+ }).first();
1983
+ if (!existing) {
1984
+ await db2.from("statistics").insert({
1985
+ name: statistic.name,
1986
+ label: statistic.label,
1987
+ type: statistic.type,
1988
+ total: statistic.count ?? 1,
1989
+ createdAt: currentDate
1990
+ });
1991
+ } else {
1992
+ await db2.from("statistics").update({
1993
+ total: db2.raw("total + ?", [statistic.count ?? 1])
1994
+ }).where({
1995
+ name: statistic.name,
1996
+ label: statistic.label,
1997
+ type: statistic.type,
1998
+ createdAt: currentDate
1999
+ });
2000
+ }
1887
2001
  };
1888
2002
 
1889
2003
  // src/registry/index.ts
@@ -1983,6 +2097,7 @@ var authentication = async ({
1983
2097
  }
1984
2098
  if (authtoken) {
1985
2099
  try {
2100
+ console.log("[EXULU] authtoken", authtoken);
1986
2101
  if (!authtoken?.email) {
1987
2102
  return {
1988
2103
  error: true,
@@ -2076,8 +2191,7 @@ var requestValidators = {
2076
2191
  const { db: db2 } = await postgresClient();
2077
2192
  let authtoken = null;
2078
2193
  if (typeof apikey !== "string") {
2079
- const secret = process.env.NEXTAUTH_SECRET;
2080
- authtoken = await getToken(req.headers["authorization"] ?? "");
2194
+ authtoken = await getToken((req.headers["authorization"] || req.headers["x-api-key"]) ?? "");
2081
2195
  }
2082
2196
  return await authentication({
2083
2197
  authtoken,
@@ -2270,6 +2384,42 @@ var import_reflect_metadata = require("reflect-metadata");
2270
2384
  // src/registry/utils/graphql.ts
2271
2385
  var import_schema = require("@graphql-tools/schema");
2272
2386
  var import_graphql_type_json = __toESM(require("graphql-type-json"), 1);
2387
+ var import_graphql = require("graphql");
2388
+ var import_crypto_js = __toESM(require("crypto-js"), 1);
2389
+ var GraphQLDate = new import_graphql.GraphQLScalarType({
2390
+ name: "Date",
2391
+ description: "Date custom scalar type",
2392
+ serialize(value) {
2393
+ if (value instanceof Date) {
2394
+ return value.toISOString();
2395
+ }
2396
+ if (typeof value === "number") {
2397
+ return new Date(value).toISOString();
2398
+ }
2399
+ if (typeof value === "string") {
2400
+ return new Date(value).toISOString();
2401
+ }
2402
+ return value;
2403
+ },
2404
+ parseValue(value) {
2405
+ if (typeof value === "string") {
2406
+ return new Date(value);
2407
+ }
2408
+ if (typeof value === "number") {
2409
+ return new Date(value);
2410
+ }
2411
+ return value;
2412
+ },
2413
+ parseLiteral(ast) {
2414
+ if (ast.kind === import_graphql.Kind.STRING) {
2415
+ return new Date(ast.value);
2416
+ }
2417
+ if (ast.kind === import_graphql.Kind.INT) {
2418
+ return new Date(parseInt(ast.value, 10));
2419
+ }
2420
+ return null;
2421
+ }
2422
+ });
2273
2423
  var map = (field) => {
2274
2424
  let type;
2275
2425
  switch (field.type) {
@@ -2289,7 +2439,7 @@ var map = (field) => {
2289
2439
  type = "JSON";
2290
2440
  break;
2291
2441
  case "date":
2292
- type = "String";
2442
+ type = "Date";
2293
2443
  break;
2294
2444
  default:
2295
2445
  type = "String";
@@ -2307,8 +2457,8 @@ function createTypeDefs(table) {
2307
2457
  type ${table.name.singular} {
2308
2458
  ${fields.join("\n")}
2309
2459
  id: ID!
2310
- createdAt: String!
2311
- updatedAt: String!
2460
+ createdAt: Date!
2461
+ updatedAt: Date!
2312
2462
  }
2313
2463
  `;
2314
2464
  const inputDef = `
@@ -2334,6 +2484,11 @@ input FilterOperatorString {
2334
2484
  contains: String
2335
2485
  }
2336
2486
 
2487
+ input FilterOperatorDate {
2488
+ lte: Date
2489
+ gte: Date
2490
+ }
2491
+
2337
2492
  input FilterOperatorFloat {
2338
2493
  eq: Float
2339
2494
  ne: Float
@@ -2386,8 +2541,10 @@ function createMutations(table) {
2386
2541
  [`${tableNamePlural}CreateOne`]: async (_, args, context, info) => {
2387
2542
  const { db: db2 } = context;
2388
2543
  const requestedFields = getRequestedFields(info);
2544
+ let { input } = args;
2545
+ input = encryptSensitiveFields(input);
2389
2546
  const results = await db2(tableNamePlural).insert({
2390
- ...args.input,
2547
+ ...input,
2391
2548
  createdAt: /* @__PURE__ */ new Date(),
2392
2549
  updatedAt: /* @__PURE__ */ new Date()
2393
2550
  }).returning(requestedFields);
@@ -2395,7 +2552,8 @@ function createMutations(table) {
2395
2552
  },
2396
2553
  [`${tableNamePlural}UpdateOne`]: async (_, args, context, info) => {
2397
2554
  const { db: db2 } = context;
2398
- const { where, input } = args;
2555
+ let { where, input } = args;
2556
+ input = encryptSensitiveFields(input);
2399
2557
  await db2(tableNamePlural).where(where).update({
2400
2558
  ...input,
2401
2559
  updatedAt: /* @__PURE__ */ new Date()
@@ -2405,7 +2563,8 @@ function createMutations(table) {
2405
2563
  return result;
2406
2564
  },
2407
2565
  [`${tableNamePlural}UpdateOneById`]: async (_, args, context, info) => {
2408
- const { id, input } = args;
2566
+ let { id, input } = args;
2567
+ input = encryptSensitiveFields(input);
2409
2568
  const { db: db2 } = context;
2410
2569
  await db2(tableNamePlural).where({ id }).update({
2411
2570
  ...input,
@@ -2508,12 +2667,44 @@ function createQueries(table) {
2508
2667
  },
2509
2668
  items
2510
2669
  };
2511
- }
2670
+ },
2671
+ // Add jobStatistics query for jobs table
2672
+ ...tableNamePlural === "jobs" ? {
2673
+ jobStatistics: async (_, args, context, info) => {
2674
+ const { user, agent, from, to } = args;
2675
+ const { db: db2 } = context;
2676
+ let query = db2("jobs");
2677
+ if (user) {
2678
+ query = query.where("user", user);
2679
+ }
2680
+ if (agent) {
2681
+ query = query.where("agent", agent);
2682
+ }
2683
+ if (from) {
2684
+ query = query.where("createdAt", ">=", from);
2685
+ }
2686
+ if (to) {
2687
+ query = query.where("createdAt", "<=", to);
2688
+ }
2689
+ const completedQuery = query.clone().where("status", "completed");
2690
+ const [{ completedCount }] = await completedQuery.count("* as completedCount");
2691
+ const failedQuery = query.clone().where("status", "failed");
2692
+ const [{ failedCount }] = await failedQuery.count("* as failedCount");
2693
+ const durationQuery = query.clone().where("status", "completed").whereNotNull("duration").select(db2.raw('AVG("duration") as averageDuration'));
2694
+ const [{ averageDuration }] = await durationQuery;
2695
+ return {
2696
+ completedCount: Number(completedCount),
2697
+ failedCount: Number(failedCount),
2698
+ averageDuration: averageDuration ? Number(averageDuration) : 0
2699
+ };
2700
+ }
2701
+ } : {}
2512
2702
  };
2513
2703
  }
2514
2704
  function createSDL(tables) {
2515
2705
  let typeDefs = `
2516
2706
  scalar JSON
2707
+ scalar Date
2517
2708
 
2518
2709
  type Query {
2519
2710
  `;
@@ -2521,7 +2712,7 @@ function createSDL(tables) {
2521
2712
  type Mutation {
2522
2713
  `;
2523
2714
  let modelDefs = "";
2524
- const resolvers = { JSON: import_graphql_type_json.default, Query: {}, Mutation: {} };
2715
+ const resolvers = { JSON: import_graphql_type_json.default, Date: GraphQLDate, Query: {}, Mutation: {} };
2525
2716
  for (const table of tables) {
2526
2717
  const tableNamePlural = table.name.plural.toLowerCase();
2527
2718
  const tableNameSingular = table.name.singular.toLowerCase();
@@ -2530,6 +2721,7 @@ function createSDL(tables) {
2530
2721
  ${tableNameSingular}ById(id: ID!): ${tableNameSingular}
2531
2722
  ${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
2532
2723
  ${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
2724
+ ${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
2533
2725
  `;
2534
2726
  mutationDefs += `
2535
2727
  ${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!): ${tableNameSingular}
@@ -2554,6 +2746,15 @@ type PageInfo {
2554
2746
  hasNextPage: Boolean!
2555
2747
  }
2556
2748
  `;
2749
+ if (tableNamePlural === "jobs") {
2750
+ modelDefs += `
2751
+ type JobStatistics {
2752
+ completedCount: Int!
2753
+ failedCount: Int!
2754
+ averageDuration: Float!
2755
+ }
2756
+ `;
2757
+ }
2557
2758
  Object.assign(resolvers.Query, createQueries(table));
2558
2759
  Object.assign(resolvers.Mutation, createMutations(table));
2559
2760
  }
@@ -2590,6 +2791,15 @@ type PageInfo {
2590
2791
  console.log("\n");
2591
2792
  return schema;
2592
2793
  }
2794
+ var sensitiveFields = ["anthropic_token"];
2795
+ var encryptSensitiveFields = (input) => {
2796
+ sensitiveFields.forEach((field) => {
2797
+ if (input[field]) {
2798
+ input[field] = import_crypto_js.default.AES.encrypt(input[field], process.env.NEXTAUTH_SECRET).toString();
2799
+ }
2800
+ });
2801
+ return input;
2802
+ };
2593
2803
 
2594
2804
  // src/registry/routes.ts
2595
2805
  var import_express5 = require("@as-integrations/express5");
@@ -2988,6 +3198,34 @@ var createUppyRoutes = async (app) => {
2988
3198
  // src/registry/routes.ts
2989
3199
  var import_utils2 = require("@apollo/utils.keyvaluecache");
2990
3200
  var import_body_parser = __toESM(require("body-parser"), 1);
3201
+ var import_crypto_js2 = __toESM(require("crypto-js"), 1);
3202
+
3203
+ // src/registry/utils/claude-messages.ts
3204
+ var CLAUDE_MESSAGES = {
3205
+ authentication_error: `
3206
+ \x1B[41m -- Authentication error please check your IMP token and try again. --
3207
+ \x1B[0m`,
3208
+ missing_body: `
3209
+ \x1B[41m -- Missing body Anthropic response. --
3210
+ \x1B[0m`,
3211
+ missing_nextauth_secret: `
3212
+ \x1B[41m -- Missing NEXTAUTH_SECRET in environment variables on the server. --
3213
+ \x1B[0m`,
3214
+ not_enabled: `
3215
+ \x1B[31m
3216
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
3217
+ \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
3218
+ \u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
3219
+ \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
3220
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
3221
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
3222
+ Intelligence Management Platform
3223
+ \x1B[0m
3224
+ \x1B[41m -- Your account has not been enabled to use Claude Code, please contact your admin or enable Claude Code in the user settings. --
3225
+ \x1B[0m`
3226
+ };
3227
+
3228
+ // src/registry/routes.ts
2991
3229
  var REQUEST_SIZE_LIMIT = "50mb";
2992
3230
  var global_queues = {
2993
3231
  logs_cleaner: "logs-cleaner"
@@ -3024,7 +3262,7 @@ var createRecurringJobs = async () => {
3024
3262
  console.table(recurringJobSchedulersLogs);
3025
3263
  return queue;
3026
3264
  };
3027
- var createExpressRoutes = async (app, agents, embedders, tools, workflows, contexts) => {
3265
+ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
3028
3266
  const routeLogs = [];
3029
3267
  var corsOptions = {
3030
3268
  origin: "*",
@@ -3044,6 +3282,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3044
3282
  \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
3045
3283
  \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
3046
3284
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
3285
+ Intelligence Management Platform
3047
3286
 
3048
3287
  `);
3049
3288
  console.log("Agents:");
@@ -3092,7 +3331,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3092
3331
  } else {
3093
3332
  console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
3094
3333
  }
3095
- const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, threadsSchema, messagesSchema]);
3334
+ const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, agentSessionsSchema, agentMessagesSchema]);
3096
3335
  console.log("[EXULU] graphql server");
3097
3336
  const server = new import_server3.ApolloServer({
3098
3337
  cache: new import_utils2.InMemoryLRUCache(),
@@ -3120,10 +3359,52 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3120
3359
  }
3121
3360
  })
3122
3361
  );
3123
- app.get(`/agents`, async (req, res) => {
3362
+ app.get(`/providers`, async (req, res) => {
3363
+ const authenticationResult = await requestValidators.authenticate(req);
3364
+ if (!authenticationResult.user?.id) {
3365
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3366
+ return;
3367
+ }
3124
3368
  res.status(200).json(agents);
3125
3369
  });
3370
+ app.get(`/agents`, async (req, res) => {
3371
+ const authenticationResult = await requestValidators.authenticate(req);
3372
+ if (!authenticationResult.user?.id) {
3373
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3374
+ return;
3375
+ }
3376
+ const { db: db2 } = await postgresClient();
3377
+ const agentsFromDb = await db2.from("agents").select("*");
3378
+ res.status(200).json(agentsFromDb.map((agent) => {
3379
+ const backend = agents.find((a) => a.id === agent.backend);
3380
+ if (!backend) {
3381
+ return null;
3382
+ }
3383
+ return {
3384
+ name: agent.name,
3385
+ id: agent.id,
3386
+ description: agent.description,
3387
+ provider: backend?.model?.provider,
3388
+ model: backend?.model?.modelId,
3389
+ active: agent.active,
3390
+ public: agent.public,
3391
+ type: agent.type,
3392
+ slug: backend?.slug,
3393
+ rateLimit: backend?.rateLimit,
3394
+ streaming: backend?.streaming,
3395
+ capabilities: backend?.capabilities,
3396
+ // todo add contexts
3397
+ availableTools: tools,
3398
+ enabledTools: agent.tools
3399
+ };
3400
+ }).filter(Boolean));
3401
+ });
3126
3402
  app.get(`/agents/:id`, async (req, res) => {
3403
+ const authenticationResult = await requestValidators.authenticate(req);
3404
+ if (!authenticationResult.user?.id) {
3405
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3406
+ return;
3407
+ }
3127
3408
  const { db: db2 } = await postgresClient();
3128
3409
  const id = req.params.id;
3129
3410
  if (!id) {
@@ -3146,6 +3427,8 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3146
3427
  name: agent.name,
3147
3428
  id: agent.id,
3148
3429
  description: agent.description,
3430
+ provider: backend?.model?.provider,
3431
+ model: backend?.model?.modelId,
3149
3432
  active: agent.active,
3150
3433
  public: agent.public,
3151
3434
  type: agent.type,
@@ -3154,7 +3437,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3154
3437
  streaming: backend?.streaming,
3155
3438
  capabilities: backend?.capabilities,
3156
3439
  // todo add contexts
3157
- availableTools: backend?.tools,
3440
+ availableTools: tools,
3158
3441
  enabledTools: agent.tools
3159
3442
  }
3160
3443
  });
@@ -3165,8 +3448,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3165
3448
  name: tool2.name,
3166
3449
  description: tool2.description,
3167
3450
  type: tool2.type || "tool",
3168
- inputSchema: tool2.inputSchema ? (0, import_zodex.zerialize)(tool2.inputSchema) : null,
3169
- outputSchema: tool2.outputSchema ? (0, import_zodex.zerialize)(tool2.outputSchema) : null
3451
+ inputSchema: tool2.inputSchema ? (0, import_zodex.zerialize)(tool2.inputSchema) : null
3170
3452
  })));
3171
3453
  });
3172
3454
  app.get("/tools/:id", async (req, res) => {
@@ -3284,6 +3566,14 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3284
3566
  });
3285
3567
  return;
3286
3568
  }
3569
+ const itemsTableExists = await context.tableExists();
3570
+ if (!itemsTableExists) {
3571
+ await context.createItemsTable();
3572
+ }
3573
+ const chunksTableExists = await db2.schema.hasTable(context.getChunksTableName());
3574
+ if (!chunksTableExists) {
3575
+ await context.createChunksTable();
3576
+ }
3287
3577
  const item = await db2.from(context.getTableName()).where({ id: req.params.id }).select("*").first();
3288
3578
  if (!item) {
3289
3579
  res.status(404).json({
@@ -3405,6 +3695,20 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3405
3695
  }
3406
3696
  let limit = req.query.limit ? parseInt(req.query.limit) : 10;
3407
3697
  let page = req.query.page ? parseInt(req.query.page) : 1;
3698
+ let sort = req.query.sort ? req.query.sort : "created_at";
3699
+ let order = req.query.order ? req.query.order : "desc";
3700
+ if (sort && !["created_at", "embeddings_updated_at"].includes(sort)) {
3701
+ res.status(400).json({
3702
+ message: "Invalid sort field, must be one of: createdAt, embeddings_updated_at"
3703
+ });
3704
+ return;
3705
+ }
3706
+ if (order && !["desc", "asc"].includes(order)) {
3707
+ res.status(400).json({
3708
+ message: "Invalid order, must be one of: desc, asc"
3709
+ });
3710
+ return;
3711
+ }
3408
3712
  const authenticationResult = await requestValidators.authenticate(req);
3409
3713
  if (!authenticationResult.user?.id) {
3410
3714
  res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
@@ -3428,6 +3732,8 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3428
3732
  return;
3429
3733
  }
3430
3734
  const result = await context.getItems({
3735
+ sort,
3736
+ order,
3431
3737
  page,
3432
3738
  limit,
3433
3739
  archived: req.query.archived === "true",
@@ -3507,6 +3813,18 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3507
3813
  message: "Embedding deleted."
3508
3814
  });
3509
3815
  });
3816
+ app.get("/ping", async (req, res) => {
3817
+ const authenticationResult = await requestValidators.authenticate(req);
3818
+ if (!authenticationResult.user?.id) {
3819
+ res.status(200).json({
3820
+ authenticated: false
3821
+ });
3822
+ return;
3823
+ }
3824
+ res.status(200).json({
3825
+ authenticated: true
3826
+ });
3827
+ });
3510
3828
  console.log("[EXULU] statistics timeseries");
3511
3829
  app.post("/statistics/timeseries", async (req, res) => {
3512
3830
  const authenticationResult = await requestValidators.authenticate(req);
@@ -3646,6 +3964,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3646
3964
  slug: "/contexts/" + context.id,
3647
3965
  active: context.active,
3648
3966
  fields: context.fields,
3967
+ configuration: context.configuration,
3649
3968
  sources: context.sources.get().map((source) => ({
3650
3969
  id: source.id,
3651
3970
  name: source.name,
@@ -3840,47 +4159,28 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3840
4159
  res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3841
4160
  return;
3842
4161
  }
4162
+ console.log("[EXULU] agent tools", agentInstance.tools);
4163
+ const enabledTools = agentInstance.tools.map((tool2) => tools.find(({ id }) => id === tool2)).filter(Boolean);
4164
+ console.log("[EXULU] enabled tools", enabledTools);
3843
4165
  if (!!stream) {
3844
- const chatClient = await agent.chat(agentInstance.id);
3845
- if (!chatClient) {
3846
- res.status(500).json({
3847
- message: "Agent instantiation not successful."
3848
- });
3849
- return;
3850
- }
3851
- const { textStream } = await chatClient.stream(req.body.messages, {
3852
- threadId: `${req.body.threadId}`,
3853
- // conversation id
3854
- resourceId: `${req.body.resourceId}`,
3855
- // user id
3856
- ...agent.outputSchema && { output: agent.outputSchema },
3857
- maxRetries: 2,
3858
- // todo make part of ExuluAgent class
3859
- maxSteps: 5,
3860
- // todo make part of ExuluAgent class
3861
- onError: (error) => console.error("[EXULU] chat stream error.", error),
3862
- onFinish: ({ response, usage }) => console.info(
3863
- "[EXULU] chat stream finished.",
3864
- usage
3865
- )
4166
+ const result = agent.generateStream({
4167
+ messages: req.body.messages,
4168
+ tools: enabledTools,
4169
+ statistics: {
4170
+ label: agent.name,
4171
+ trigger: "agent"
4172
+ }
3866
4173
  });
3867
- for await (const delta of textStream) {
3868
- res.write(`data: ${delta}
3869
-
3870
- `);
3871
- }
3872
- res.end();
4174
+ result.pipeDataStreamToResponse(res);
3873
4175
  return;
3874
4176
  } else {
3875
- const response = await agent.chat.generate(req.body.messages, {
3876
- resourceId: `${authenticationResult.user.id}`,
3877
- output: agent.outputSchema,
3878
- threadId: `${req.body.threadId}`,
3879
- // conversation id
3880
- maxRetries: 2,
3881
- // todo make part of ExuluAgent class
3882
- maxSteps: 5
3883
- // todo make part of ExuluAgent class
4177
+ const response = await agent.generateSync({
4178
+ messages: req.body.messages,
4179
+ tools: enabledTools.map(),
4180
+ statistics: {
4181
+ label: agent.name,
4182
+ trigger: "agent"
4183
+ }
3884
4184
  });
3885
4185
  res.status(200).json(response);
3886
4186
  return;
@@ -3955,7 +4255,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3955
4255
  console.log("Routes:");
3956
4256
  console.table(routeLogs);
3957
4257
  const TARGET_API = "https://api.anthropic.com";
3958
- app.use("/gateway/anthropic", import_express3.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
4258
+ app.use("/gateway/anthropic/:id", import_express3.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
3959
4259
  const path3 = req.url;
3960
4260
  const url = `${TARGET_API}${path3}`;
3961
4261
  console.log("[PROXY] Manual proxy to:", url);
@@ -3966,57 +4266,100 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3966
4266
  console.log("[PROXY] Request stream:", req.body.stream);
3967
4267
  console.log("[PROXY] Request messages:", req.body.messages?.length);
3968
4268
  try {
3969
- const headers = {
3970
- "x-api-key": process.env.ANTHROPIC_API_KEY,
3971
- "anthropic-version": "2023-06-01",
3972
- "content-type": req.headers["content-type"] || "application/json"
3973
- };
3974
- if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
3975
- if (req.headers["user-agent"]) headers["user-agent"] = req.headers["user-agent"];
3976
4269
  console.log("[PROXY] Request body tools array length:", req.body.tools?.length);
3977
4270
  if (!req.body.tools) {
3978
4271
  req.body.tools = [];
3979
4272
  }
3980
- if (req.headers["x-api-key"] === "PLACEHOLDER") {
3981
- res.write(JSON.stringify({
3982
- "type": "content_block_delta",
3983
- "index": 0,
3984
- "delta": {
3985
- "type": "text_delta",
3986
- "text": "Hello, world!"
3987
- }
3988
- }));
3989
- res.end();
4273
+ const authenticationResult = await requestValidators.authenticate(req);
4274
+ if (!authenticationResult.user?.id) {
4275
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
4276
+ return;
4277
+ }
4278
+ console.log("[EXULU] authentication result", authenticationResult);
4279
+ const { db: db2 } = await postgresClient();
4280
+ const agent = await db2.from("agents").where({
4281
+ id: req.params.id
4282
+ }).first();
4283
+ if (!agent) {
4284
+ const arrayBuffer = createCustomAnthropicStreamingMessage(`
4285
+ \x1B[41m -- Agent ${req.params.id} not found or you do not have access to it. --
4286
+ \x1B[0m`);
4287
+ res.setHeader("Content-Type", "application/json");
4288
+ res.end(Buffer.from(arrayBuffer));
4289
+ return;
4290
+ }
4291
+ console.log("[EXULU] agent", agent?.name);
4292
+ if (!process.env.NEXTAUTH_SECRET) {
4293
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_nextauth_secret);
4294
+ res.setHeader("Content-Type", "application/json");
4295
+ res.end(Buffer.from(arrayBuffer));
3990
4296
  return;
3991
4297
  }
4298
+ if (!authenticationResult.user?.anthropic_token) {
4299
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.not_enabled);
4300
+ res.setHeader("Content-Type", "application/json");
4301
+ res.end(Buffer.from(arrayBuffer));
4302
+ return;
4303
+ }
4304
+ const bytes = import_crypto_js2.default.AES.decrypt(authenticationResult.user?.anthropic_token, process.env.NEXTAUTH_SECRET);
4305
+ const anthropicApiKey = bytes.toString(import_crypto_js2.default.enc.Utf8);
4306
+ const headers = {
4307
+ "x-api-key": anthropicApiKey,
4308
+ "anthropic-version": "2023-06-01",
4309
+ "content-type": req.headers["content-type"] || "application/json"
4310
+ };
4311
+ if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
4312
+ if (req.headers["user-agent"]) headers["user-agent"] = req.headers["user-agent"];
4313
+ console.log("[EXULU] anthropic api key", anthropicApiKey);
3992
4314
  const response = await fetch(url, {
3993
4315
  method: req.method,
3994
4316
  headers,
3995
4317
  body: req.method !== "GET" ? JSON.stringify(req.body) : void 0
3996
4318
  });
3997
- console.log("[PROXY] Response status:", response.status);
4319
+ console.log("[PROXY] Response:", response);
4320
+ console.log("[PROXY] Response:", response.body);
4321
+ await updateStatistic({
4322
+ name: "count",
4323
+ label: "Claude Code",
4324
+ type: STATISTICS_TYPE_ENUM.AGENT_RUN,
4325
+ trigger: "claude-code",
4326
+ count: 1
4327
+ });
3998
4328
  response.headers.forEach((value, key) => {
3999
4329
  res.setHeader(key, value);
4000
4330
  });
4001
4331
  res.status(response.status);
4002
- if (response.headers.get("content-type")?.includes("text/event-stream")) {
4332
+ const isStreaming = response.headers.get("content-type")?.includes("text/event-stream");
4333
+ if (isStreaming && !response?.body) {
4334
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_body);
4335
+ res.setHeader("Content-Type", "application/json");
4336
+ res.end(Buffer.from(arrayBuffer));
4337
+ return;
4338
+ }
4339
+ if (isStreaming) {
4003
4340
  const reader = response.body.getReader();
4004
4341
  const decoder = new TextDecoder();
4005
4342
  while (true) {
4006
4343
  const { done, value } = await reader.read();
4007
4344
  if (done) break;
4008
4345
  const chunk = decoder.decode(value, { stream: true });
4346
+ console.log("[PROXY] Chunk:", chunk);
4009
4347
  res.write(chunk);
4010
4348
  }
4011
4349
  res.end();
4012
- } else {
4013
- const data = await response.arrayBuffer();
4014
- res.end(Buffer.from(data));
4350
+ return;
4015
4351
  }
4352
+ const data = await response.arrayBuffer();
4353
+ console.log("[PROXY] Data:", data);
4354
+ res.end(Buffer.from(data));
4016
4355
  } catch (error) {
4017
4356
  console.error("[PROXY] Manual proxy error:", error);
4018
4357
  if (!res.headersSent) {
4019
- res.status(500).json({ error: error.message });
4358
+ if (error?.message === "Invalid token") {
4359
+ res.status(500).json({ error: "Authentication error, please check your IMP token and try again." });
4360
+ } else {
4361
+ res.status(500).json({ error: error.message });
4362
+ }
4020
4363
  }
4021
4364
  }
4022
4365
  });
@@ -4064,6 +4407,20 @@ var getPresignedFileUrl = async (key) => {
4064
4407
  console.log(`[EXULU] presigned url for file with key: ${key}, generated: ${json.url}`);
4065
4408
  return json.url;
4066
4409
  };
4410
+ var createCustomAnthropicStreamingMessage = (message) => {
4411
+ const responseData = {
4412
+ type: "message",
4413
+ content: [
4414
+ {
4415
+ type: "text",
4416
+ text: message
4417
+ }
4418
+ ]
4419
+ };
4420
+ const jsonString = JSON.stringify(responseData);
4421
+ const arrayBuffer = new TextEncoder().encode(jsonString).buffer;
4422
+ return arrayBuffer;
4423
+ };
4067
4424
 
4068
4425
  // src/registry/workers.ts
4069
4426
  var import_ioredis = __toESM(require("ioredis"), 1);
@@ -4116,7 +4473,7 @@ var fs2 = __toESM(require("fs"), 1);
4116
4473
  var import_path = __toESM(require("path"), 1);
4117
4474
  var defaultLogsDir = import_path.default.join(process.cwd(), "logs");
4118
4475
  var redisConnection;
4119
- var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) => {
4476
+ var createWorkers = async (queues2, contexts, workflows, _logsDir) => {
4120
4477
  if (!redisServer.host || !redisServer.port) {
4121
4478
  console.error("[EXULU] you are trying to start workers, but no redis server is configured in the environment.");
4122
4479
  throw new Error("No redis server configured in the environment, so cannot start workers.");
@@ -4147,7 +4504,7 @@ var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) =>
4147
4504
  if (!bullmqJob.data.embedder) {
4148
4505
  throw new Error(`No embedder set for embedder job.`);
4149
4506
  }
4150
- const embedder = embedders.find((embedder2) => embedder2.id === bullmqJob.data.embedder);
4507
+ const embedder = contexts.find((context2) => context2.embedder?.id === bullmqJob.data.embedder);
4151
4508
  if (!embedder) {
4152
4509
  throw new Error(`Embedder ${bullmqJob.data.embedder} not found in the registry.`);
4153
4510
  }
@@ -4187,6 +4544,9 @@ var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) =>
4187
4544
  duration,
4188
4545
  result: JSON.stringify(result)
4189
4546
  });
4547
+ await db2.from((void 0).getTableName()).where({ id: result[0].id }).update({
4548
+ embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
4549
+ }).returning("id");
4190
4550
  return result;
4191
4551
  }
4192
4552
  if (bullmqJob.data.type === "workflow") {
@@ -4284,7 +4644,7 @@ var ExuluMCP = class {
4284
4644
  express;
4285
4645
  constructor() {
4286
4646
  }
4287
- create = async ({ express: express3, contexts, embedders, agents, workflows, config, tools }) => {
4647
+ create = async ({ express: express3, contexts, agents, workflows, config, tools }) => {
4288
4648
  this.express = express3;
4289
4649
  if (!this.server) {
4290
4650
  console.log("[EXULU] Creating MCP server.");
@@ -4415,28 +4775,210 @@ ${code}`
4415
4775
 
4416
4776
  // src/registry/index.ts
4417
4777
  var import_express7 = __toESM(require("express"), 1);
4778
+
4779
+ // src/templates/agents/claude-code.ts
4780
+ var agentId = "0832-5178-1145-2194";
4781
+ var claudeCodeAgent = new ExuluAgent({
4782
+ id: `${agentId}-claude-code-agent`,
4783
+ name: `Claude Code Agent`,
4784
+ description: `Claude Code agent, enabling the creation of multiple Claude Code Agent instances with different configurations (rate limits, functions, etc).`,
4785
+ type: "custom"
4786
+ });
4787
+
4788
+ // src/templates/agents/claude-opus-4.ts
4789
+ var import_anthropic = require("@ai-sdk/anthropic");
4790
+ var import_zod4 = require("zod");
4791
+ var agentId2 = "5434-5678-9143-2590";
4792
+ var defaultAgent = new ExuluAgent({
4793
+ id: `${agentId2}-default-claude-4-opus-agent`,
4794
+ name: `Default Claude 4 Opus Agent`,
4795
+ description: `Basic agent without any defined tools, that can support MCP's.`,
4796
+ type: "agent",
4797
+ capabilities: {
4798
+ tools: false,
4799
+ images: [],
4800
+ files: [],
4801
+ audio: [],
4802
+ video: []
4803
+ },
4804
+ evals: [],
4805
+ config: {
4806
+ name: `Default agent`,
4807
+ instructions: "You are a helpful assistant.",
4808
+ model: (0, import_anthropic.anthropic)("claude-4-opus-20250514"),
4809
+ // todo add a field of type string that adds a dropdown list from which the user can select the model
4810
+ // todo for each model, check which provider is used, and require the admin to add one or multiple
4811
+ // API keys for the provider (which we can then auto-rotate).
4812
+ // todo also add custom fields for rate limiting, so the admin can set custom rate limits for the agent
4813
+ // and allow him/her to decide if the rate limit is per user or per agent.
4814
+ // todo finally allow switching on or off immutable audit logs on the agent. Which then enables OTEL
4815
+ // and stores the logs into the pre-defined storage.
4816
+ custom: import_zod4.z.object({
4817
+ apiKey: import_zod4.z.string()
4818
+ })
4819
+ }
4820
+ });
4821
+
4822
+ // src/templates/tools/browserbase.ts
4823
+ var import_zod5 = require("zod");
4824
+ var import_stagehand = require("@browserbasehq/stagehand");
4825
+ var import_sdk = require("@browserbasehq/sdk");
4826
+ var PROJECT_ID = "811444dd-6e6d-40b5-bd90-541c93e44be6";
4827
+ process.env.BROWSERBASE_PROJECT_ID = PROJECT_ID;
4828
+ var BB_API_KEY = "bb_live_LwMwNgZB5cIEKcBwMuAugrgNkFM";
4829
+ async function createContext() {
4830
+ const bb = new import_sdk.Browserbase({ apiKey: BB_API_KEY });
4831
+ const context = await bb.contexts.create({
4832
+ projectId: PROJECT_ID
4833
+ });
4834
+ return context;
4835
+ }
4836
+ async function createAuthSession(contextId) {
4837
+ const bb = new import_sdk.Browserbase({ apiKey: BB_API_KEY });
4838
+ const session = await bb.sessions.create({
4839
+ projectId: PROJECT_ID,
4840
+ browserSettings: {
4841
+ context: {
4842
+ id: contextId,
4843
+ persist: true
4844
+ }
4845
+ }
4846
+ /* proxies: [{ // not included in the free tier
4847
+ type: "browserbase",
4848
+ geolocation: {
4849
+ city: CITY,
4850
+ country: COUNTRY
4851
+ }
4852
+ }] */
4853
+ });
4854
+ const liveViewLinks = await bb.sessions.debug(session.id);
4855
+ const liveViewLink = liveViewLinks.debuggerFullscreenUrl;
4856
+ console.log(`\u{1F50D} Live View Link: ${liveViewLink}`);
4857
+ console.log("Session URL: https://browserbase.com/sessions/" + session.id);
4858
+ return {
4859
+ url: liveViewLink,
4860
+ id: session.id
4861
+ };
4862
+ }
4863
+ var createSession = new ExuluTool({
4864
+ id: `1234-5178-9423-4267`,
4865
+ type: "function",
4866
+ name: "Create a browserbase session.",
4867
+ description: `
4868
+ Creates a browserbase session and returns the live view url as well as
4869
+ the session id as a JSON object. A browserbase session is a headless browser
4870
+ that can be used to to visit websites and perform actions.
4871
+ `,
4872
+ execute: async () => {
4873
+ const { id } = await createContext();
4874
+ return await createAuthSession(id);
4875
+ }
4876
+ });
4877
+ var askChatgpt = new ExuluTool({
4878
+ id: `1234-5178-9423-4268`,
4879
+ type: "function",
4880
+ name: "ChatGPT browserbase operation.",
4881
+ inputSchema: import_zod5.z.object({
4882
+ session: import_zod5.z.string().describe("The session id of the browserbase session."),
4883
+ question: import_zod5.z.string().describe("The question to ask ChatGPT.")
4884
+ }),
4885
+ description: `Uses an existing, authenticated browserbase session to visit ChatGPT and perform actions such as asking questions.`,
4886
+ execute: async ({ session, question }) => {
4887
+ const stagehand = new import_stagehand.Stagehand({
4888
+ // With npx create-browser-app, this config is found
4889
+ // in a separate stagehand.config.ts file
4890
+ env: "BROWSERBASE",
4891
+ // set to "LOCAL" for local development
4892
+ apiKey: BB_API_KEY,
4893
+ // todo make this a config variable the admin can set in the UI
4894
+ modelName: "openai/gpt-4.1-mini",
4895
+ // todo change to claude || optionally make configurable?
4896
+ browserbaseSessionID: session,
4897
+ modelClientOptions: {
4898
+ apiKey: process.env.OPENAI_API_KEY
4899
+ // todo make this a config variable the admin can set in the UI
4900
+ }
4901
+ });
4902
+ await stagehand.init();
4903
+ const page = stagehand.page;
4904
+ await page.goto("https://chatgpt.com");
4905
+ await page.act(`Type in '${question}' into the search bar`);
4906
+ const { answer } = await page.extract({
4907
+ instruction: "The answer to the question generated by ChatGPT.",
4908
+ schema: import_zod5.z.object({
4909
+ answer: import_zod5.z.string()
4910
+ })
4911
+ });
4912
+ console.log(answer);
4913
+ await stagehand.close();
4914
+ return {
4915
+ answer
4916
+ };
4917
+ }
4918
+ });
4919
+
4920
+ // src/templates/tools/jira.ts
4921
+ var import_zod6 = require("zod");
4922
+ var getTicket = new ExuluTool({
4923
+ id: `1414-5179-1423-1269`,
4924
+ name: "JIRA ticket retrieval.",
4925
+ type: "function",
4926
+ inputSchema: import_zod6.z.object({
4927
+ ticketId: import_zod6.z.string().describe("The id of the ticket to retrieve.")
4928
+ }),
4929
+ description: `Retrieves a ticket from Jira.`,
4930
+ execute: async ({ session, question }) => {
4931
+ return {
4932
+ name: "BYD-1234",
4933
+ id: "12345678",
4934
+ status: "Open",
4935
+ description: "This is a test ticket",
4936
+ assignee: "John Doe",
4937
+ createdAt: "2021-01-01",
4938
+ updatedAt: "2021-01-01",
4939
+ dueDate: "2021-01-01",
4940
+ priority: "High"
4941
+ };
4942
+ }
4943
+ });
4944
+
4945
+ // src/registry/index.ts
4418
4946
  var ExuluApp = class {
4419
4947
  _agents = [];
4420
4948
  _workflows = [];
4421
4949
  _config;
4422
- _embedders = [];
4423
4950
  _queues = [];
4424
4951
  _contexts = {};
4425
4952
  _tools = [];
4426
4953
  _expressApp = null;
4427
4954
  constructor() {
4428
4955
  }
4429
- // Factory function so we can async initialize the
4430
- // MCP server if needed.
4431
- create = async ({ contexts, embedders, agents, workflows, config, tools }) => {
4432
- this._embedders = embedders ?? [];
4956
+ // Factory function so we can async
4957
+ // initialize the MCP server if needed.
4958
+ create = async ({ contexts, agents, workflows, config, tools }) => {
4433
4959
  this._workflows = workflows ?? [];
4434
4960
  this._contexts = contexts ?? {};
4435
- this._agents = agents ?? [];
4961
+ this._agents = [
4962
+ claudeCodeAgent,
4963
+ defaultAgent,
4964
+ ...agents ?? []
4965
+ ];
4436
4966
  this._config = config;
4437
- this._tools = tools ?? [];
4967
+ this._tools = [
4968
+ ...tools ?? [],
4969
+ // Add contexts as tools
4970
+ ...Object.values(contexts || {}).map((context) => context.tool()),
4971
+ // Add agents as tools
4972
+ ...(agents || []).map((agent) => agent.tool()),
4973
+ ...[
4974
+ createSession,
4975
+ askChatgpt,
4976
+ getTicket
4977
+ ]
4978
+ ];
4979
+ const contextsArray = Object.values(contexts || {});
4438
4980
  const queues2 = [
4439
- ...embedders?.length ? embedders.map((agent) => agent.queue?.name || null) : [],
4981
+ ...contextsArray?.length ? contextsArray.map((context) => context.embedder.queue?.name || null) : [],
4440
4982
  ...workflows?.length ? workflows.map((workflow) => workflow.queue?.name || null) : []
4441
4983
  ];
4442
4984
  this._queues = [...new Set(queues2.filter((o) => !!o))];
@@ -4453,9 +4995,6 @@ var ExuluApp = class {
4453
4995
  }
4454
4996
  return this._expressApp;
4455
4997
  }
4456
- embedder(id) {
4457
- return this._embedders.find((x) => x.id === id);
4458
- }
4459
4998
  tool(id) {
4460
4999
  return this._tools.find((x) => x.id === id);
4461
5000
  }
@@ -4471,9 +5010,6 @@ var ExuluApp = class {
4471
5010
  workflow(id) {
4472
5011
  return this._workflows.find((x) => x.id === id);
4473
5012
  }
4474
- get embedders() {
4475
- return this._embedders;
4476
- }
4477
5013
  get contexts() {
4478
5014
  return Object.values(this._contexts ?? {});
4479
5015
  }
@@ -4489,7 +5025,6 @@ var ExuluApp = class {
4489
5025
  return await createWorkers(
4490
5026
  this._queues,
4491
5027
  Object.values(this._contexts ?? {}),
4492
- this._embedders,
4493
5028
  this._workflows,
4494
5029
  this._config?.workers?.logsDir
4495
5030
  );
@@ -4506,7 +5041,6 @@ var ExuluApp = class {
4506
5041
  await createExpressRoutes(
4507
5042
  app,
4508
5043
  this._agents,
4509
- this._embedders,
4510
5044
  this._tools,
4511
5045
  this._workflows,
4512
5046
  Object.values(this._contexts ?? {})
@@ -4516,7 +5050,6 @@ var ExuluApp = class {
4516
5050
  await mcp.create({
4517
5051
  express: app,
4518
5052
  contexts: this._contexts,
4519
- embedders: this._embedders,
4520
5053
  agents: this._agents,
4521
5054
  workflows: this._workflows,
4522
5055
  config: this._config,
@@ -5724,198 +6257,6 @@ var SentenceChunker = class _SentenceChunker extends BaseChunker {
5724
6257
  }
5725
6258
  };
5726
6259
 
5727
- // src/cli/index.tsx
5728
- var import_react2 = require("react");
5729
- var import_ink4 = require("ink");
5730
- var import_ui5 = require("@inkjs/ui");
5731
- var import_patch_console = __toESM(require("patch-console"), 1);
5732
-
5733
- // src/cli/components/nav.tsx
5734
- var import_ui = require("@inkjs/ui");
5735
- var import_ink = require("ink");
5736
- var import_jsx_runtime = require("react/jsx-runtime");
5737
- var nav = [
5738
- {
5739
- label: "Agents",
5740
- value: "agents"
5741
- },
5742
- {
5743
- label: "Start Claude Code",
5744
- value: "claude-code"
5745
- },
5746
- {
5747
- label: "Exit",
5748
- value: "exit"
5749
- }
5750
- ];
5751
- var Nav = ({ setView }) => {
5752
- const { exit } = (0, import_ink.useApp)();
5753
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Select, { options: nav, onChange: (value) => {
5754
- if (value === "exit") {
5755
- exit();
5756
- }
5757
- setView(value);
5758
- } });
5759
- };
5760
- var nav_default = Nav;
5761
-
5762
- // src/cli/components/agent-selector.tsx
5763
- var import_ink2 = require("ink");
5764
- var import_ui2 = require("@inkjs/ui");
5765
- var import_jsx_runtime2 = require("react/jsx-runtime");
5766
- var AgentSelector = ({ exulu, setAgent, setEvaluations }) => {
5767
- const agents = exulu.agents.map((agent) => ({
5768
- label: agent.name,
5769
- value: agent.id
5770
- }));
5771
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
5772
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: "Please select an agent:" }),
5773
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui2.Select, { options: agents, onChange: (value) => {
5774
- const agent = exulu.agent(value);
5775
- if (!agent) {
5776
- console.error("Agent not found", value);
5777
- return;
5778
- }
5779
- setAgent(agent);
5780
- if (agent) {
5781
- setEvaluations(agent.evals || []);
5782
- }
5783
- } })
5784
- ] });
5785
- };
5786
- var agent_selector_default = AgentSelector;
5787
-
5788
- // src/cli/components/eval-selector.tsx
5789
- var import_ui3 = require("@inkjs/ui");
5790
- var import_jsx_runtime3 = require("react/jsx-runtime");
5791
- var EvalSelector = ({ evaluations, setEvaluation }) => {
5792
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui3.Select, { options: evaluations.map((evaluation) => ({
5793
- label: evaluation.runner.name,
5794
- value: evaluation.runner.name
5795
- })), onChange: (value) => {
5796
- const evaluation = evaluations?.find((evaluation2) => evaluation2.runner.name === value);
5797
- if (evaluation) {
5798
- setEvaluation(evaluation);
5799
- }
5800
- } });
5801
- };
5802
- var eval_selector_default = EvalSelector;
5803
-
5804
- // src/cli/components/eval-actions.tsx
5805
- var import_react = require("react");
5806
- var import_ui4 = require("@inkjs/ui");
5807
- var import_ink3 = require("ink");
5808
- var import_jsx_runtime4 = require("react/jsx-runtime");
5809
- var EvalActions = ({ agent, evaluation, setEvaluation }) => {
5810
- const [progress, setProgress] = (0, import_react.useState)(0);
5811
- const [results, setResults] = (0, import_react.useState)([]);
5812
- const [running, setRunning] = (0, import_react.useState)();
5813
- const run = async (evaluation2) => {
5814
- setRunning({
5815
- label: evaluation2.runner.name
5816
- });
5817
- const testCases = evaluation2.runner.testcases;
5818
- const total = testCases.length;
5819
- if (!testCases) {
5820
- throw new Error("No test cases found");
5821
- }
5822
- let i = 0;
5823
- for (const testCase of testCases) {
5824
- i++;
5825
- const result = await evaluation2.runner.run({
5826
- data: testCase,
5827
- runner: {
5828
- agent
5829
- }
5830
- });
5831
- setProgress(Math.round(i / total * 100));
5832
- setResults([...results, {
5833
- name: evaluation2.runner.name,
5834
- prompt: testCase.prompt?.slice(0, 100) + "...",
5835
- score: result.score,
5836
- comment: result.comment
5837
- }]);
5838
- }
5839
- setRunning(void 0);
5840
- };
5841
- if (progress === 100) {
5842
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
5843
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink3.Text, { children: "Evaluations completed." }),
5844
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui4.UnorderedList, { children: results.map((result) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui4.UnorderedList.Item, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink3.Text, { children: [
5845
- result.name,
5846
- ": ",
5847
- result.score,
5848
- " - ",
5849
- result.comment
5850
- ] }) })) })
5851
- ] });
5852
- }
5853
- if (running) {
5854
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
5855
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink3.Text, { children: [
5856
- "Running ",
5857
- running.label,
5858
- "..."
5859
- ] }),
5860
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui4.ProgressBar, { value: progress })
5861
- ] });
5862
- }
5863
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui4.Select, { options: [{
5864
- label: "Run evaluation",
5865
- value: "run"
5866
- }, {
5867
- label: "Go back",
5868
- value: "back"
5869
- }], onChange: (value) => {
5870
- if (value === "back") {
5871
- setEvaluation(void 0);
5872
- }
5873
- if (value === "run") {
5874
- run(evaluation);
5875
- }
5876
- } });
5877
- };
5878
- var eval_actions_default = EvalActions;
5879
-
5880
- // src/cli/index.tsx
5881
- var import_jsx_runtime5 = require("react/jsx-runtime");
5882
- var Main = ({ exulu }) => {
5883
- (0, import_patch_console.default)((stream, data) => {
5884
- setLogs([...logs, data]);
5885
- });
5886
- const [logs, setLogs] = (0, import_react2.useState)([]);
5887
- const [view, setView] = (0, import_react2.useState)();
5888
- const [agent, setAgent] = (0, import_react2.useState)();
5889
- const [evaluations, setEvaluations] = (0, import_react2.useState)([]);
5890
- const [evaluation, setEvaluation] = (0, import_react2.useState)();
5891
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink4.Box, { borderStyle: "round", borderColor: "cyan", padding: 1, flexDirection: "column", width: "70%", children: [
5892
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink4.Text, { children: "Logs:" }),
5893
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.UnorderedList, { children: logs.map((log, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.UnorderedList.Item, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink4.Text, { children: log }) })) }),
5894
- !view && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(nav_default, { setView }),
5895
- view === "agents" && !agent && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(agent_selector_default, { exulu, setAgent, setEvaluations }),
5896
- view === "agents" && agent && !evaluation && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
5897
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink4.Text, { children: [
5898
- 'Selected agent "',
5899
- agent.name,
5900
- '". Please select an evaluation:'
5901
- ] }),
5902
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(eval_selector_default, { evaluations, setEvaluation })
5903
- ] }),
5904
- view === "agents" && agent && evaluation && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
5905
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink4.Text, { children: [
5906
- "Selected evaluation: ",
5907
- evaluation.runner.name
5908
- ] }),
5909
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(eval_actions_default, { agent, evaluation, setEvaluation })
5910
- ] })
5911
- ] });
5912
- };
5913
- var cli_default = {
5914
- run: (exulu) => {
5915
- (0, import_ink4.render)(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Main, { exulu }));
5916
- }
5917
- };
5918
-
5919
6260
  // src/index.ts
5920
6261
  var ExuluJobs = {
5921
6262
  redis: redisClient,
@@ -5946,7 +6287,6 @@ var ExuluDatabase = {
5946
6287
  ExuluApp,
5947
6288
  ExuluAuthentication,
5948
6289
  ExuluChunkers,
5949
- ExuluCli,
5950
6290
  ExuluContext,
5951
6291
  ExuluDatabase,
5952
6292
  ExuluEmbedder,