@exulu/backend 0.3.12 → 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,
@@ -122,11 +121,6 @@ async function postgresClient() {
122
121
  if (!db["exulu"]) {
123
122
  try {
124
123
  console.log("[EXULU] Initializing exulu database.");
125
- console.log(process.env.POSTGRES_DB_HOST);
126
- console.log(process.env.POSTGRES_DB_PORT);
127
- console.log(process.env.POSTGRES_DB_USER);
128
- console.log(process.env.POSTGRES_DB_PASSWORD);
129
- console.log(process.env.POSTGRES_DB_SSL);
130
124
  const knex = (0, import_knex.default)({
131
125
  client: "pg",
132
126
  connection: {
@@ -151,6 +145,47 @@ async function postgresClient() {
151
145
  }
152
146
 
153
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
+ };
154
189
  var usersSchema = {
155
190
  name: {
156
191
  plural: "users",
@@ -208,14 +243,13 @@ var usersSchema = {
208
243
  name: "last_used",
209
244
  type: "date"
210
245
  },
246
+ {
247
+ name: "anthropic_token",
248
+ type: "text"
249
+ },
211
250
  {
212
251
  name: "role",
213
- type: "reference",
214
- references: {
215
- table: "roles",
216
- field: "id",
217
- onDelete: "CASCADE"
218
- }
252
+ type: "uuid"
219
253
  }
220
254
  ]
221
255
  };
@@ -261,10 +295,6 @@ var statisticsSchema = {
261
295
  {
262
296
  name: "total",
263
297
  type: "number"
264
- },
265
- {
266
- name: "timeseries",
267
- type: "json"
268
298
  }
269
299
  ]
270
300
  };
@@ -320,11 +350,11 @@ var evalResultsSchema = {
320
350
  },
321
351
  {
322
352
  name: "agent_id",
323
- type: "text"
353
+ type: "uuid"
324
354
  },
325
355
  {
326
356
  name: "workflow_id",
327
- type: "text"
357
+ type: "uuid"
328
358
  },
329
359
  {
330
360
  name: "eval_type",
@@ -340,50 +370,6 @@ var evalResultsSchema = {
340
370
  }
341
371
  ]
342
372
  };
343
- var threadsSchema = {
344
- name: {
345
- plural: "threads",
346
- singular: "thread"
347
- },
348
- fields: [
349
- {
350
- name: "resourceId",
351
- type: "text"
352
- },
353
- {
354
- name: "title",
355
- type: "text"
356
- },
357
- {
358
- name: "metadata",
359
- type: "text"
360
- }
361
- ]
362
- };
363
- var messagesSchema = {
364
- name: {
365
- plural: "messages",
366
- singular: "message"
367
- },
368
- fields: [
369
- {
370
- name: "thread_id",
371
- type: "text"
372
- },
373
- {
374
- name: "content",
375
- type: "text"
376
- },
377
- {
378
- name: "role",
379
- type: "text"
380
- },
381
- {
382
- name: "type",
383
- type: "text"
384
- }
385
- ]
386
- };
387
373
  var jobsSchema = {
388
374
  name: {
389
375
  plural: "jobs",
@@ -416,19 +402,20 @@ var jobsSchema = {
416
402
  },
417
403
  {
418
404
  name: "agent",
419
- type: "text"
405
+ type: "uuid"
420
406
  },
421
407
  {
422
408
  name: "workflow",
423
- type: "text"
409
+ type: "uuid"
424
410
  },
425
411
  {
426
412
  name: "user",
427
- type: "text"
413
+ // next auth stores users with id type SERIAL, so we need to use number
414
+ type: "number"
428
415
  },
429
416
  {
430
417
  name: "item",
431
- type: "text"
418
+ type: "uuid"
432
419
  },
433
420
  {
434
421
  name: "steps",
@@ -522,7 +509,7 @@ var mapType = (t, type, name, defaultValue) => {
522
509
  return;
523
510
  }
524
511
  if (type === "date") {
525
- t.date(name);
512
+ t.timestamp(name);
526
513
  return;
527
514
  }
528
515
  if (type === "uuid") {
@@ -592,23 +579,44 @@ var generateApiKey = async (name, email) => {
592
579
 
593
580
  // src/postgres/init-db.ts
594
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
+ }
595
610
  if (!await knex.schema.hasTable("roles")) {
596
611
  await knex.schema.createTable("roles", (table) => {
597
612
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
598
- table.date("createdAt").defaultTo(knex.fn.now());
599
- table.date("updatedAt").defaultTo(knex.fn.now());
613
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
614
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
600
615
  for (const field of rolesSchema.fields) {
601
- const { type, name, references, default: defaultValue } = field;
616
+ const { type, name, default: defaultValue } = field;
602
617
  if (!type || !name) {
603
618
  continue;
604
619
  }
605
- if (type === "reference") {
606
- if (!references) {
607
- throw new Error("Field with type reference must have a reference definition.");
608
- }
609
- table.uuid(name).references(references.field).inTable(references.table);
610
- return;
611
- }
612
620
  mapType(table, type, sanitizeName(name), defaultValue);
613
621
  }
614
622
  });
@@ -616,20 +624,13 @@ var up = async function(knex) {
616
624
  if (!await knex.schema.hasTable("eval_results")) {
617
625
  await knex.schema.createTable("eval_results", (table) => {
618
626
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
619
- table.date("createdAt").defaultTo(knex.fn.now());
620
- table.date("updatedAt").defaultTo(knex.fn.now());
627
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
628
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
621
629
  for (const field of evalResultsSchema.fields) {
622
- const { type, name, references, default: defaultValue } = field;
630
+ const { type, name, default: defaultValue } = field;
623
631
  if (!type || !name) {
624
632
  continue;
625
633
  }
626
- if (type === "reference") {
627
- if (!references) {
628
- throw new Error("Field with type reference must have a reference definition.");
629
- }
630
- table.uuid(name).references(references.field).inTable(references.table);
631
- return;
632
- }
633
634
  mapType(table, type, sanitizeName(name), defaultValue);
634
635
  }
635
636
  });
@@ -637,62 +638,41 @@ var up = async function(knex) {
637
638
  if (!await knex.schema.hasTable("statistics")) {
638
639
  await knex.schema.createTable("statistics", (table) => {
639
640
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
640
- table.date("createdAt").defaultTo(knex.fn.now());
641
- table.date("updatedAt").defaultTo(knex.fn.now());
641
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
642
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
642
643
  for (const field of statisticsSchema.fields) {
643
- const { type, name, references, default: defaultValue } = field;
644
+ const { type, name, default: defaultValue } = field;
644
645
  if (!type || !name) {
645
646
  continue;
646
647
  }
647
- if (type === "reference") {
648
- if (!references) {
649
- throw new Error("Field with type reference must have a reference definition.");
650
- }
651
- table.uuid(name).references(references.field).inTable(references.table);
652
- return;
653
- }
654
648
  mapType(table, type, sanitizeName(name), defaultValue);
655
649
  }
656
650
  });
657
651
  }
658
652
  if (!await knex.schema.hasTable("jobs")) {
659
653
  await knex.schema.createTable("jobs", (table) => {
660
- table.increments("id").primary();
661
- table.date("createdAt").defaultTo(knex.fn.now());
662
- 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());
663
657
  for (const field of jobsSchema.fields) {
664
- const { type, name, references, default: defaultValue } = field;
658
+ const { type, name, default: defaultValue } = field;
665
659
  if (!type || !name) {
666
660
  continue;
667
661
  }
668
- if (type === "reference") {
669
- if (!references) {
670
- throw new Error("Field with type reference must have a reference definition.");
671
- }
672
- table.uuid(name).references(references.field).inTable(references.table);
673
- return;
674
- }
675
662
  mapType(table, type, sanitizeName(name), defaultValue);
676
663
  }
677
664
  });
678
665
  }
679
666
  if (!await knex.schema.hasTable("agents")) {
680
667
  await knex.schema.createTable("agents", (table) => {
681
- table.increments("id").primary();
682
- table.date("createdAt").defaultTo(knex.fn.now());
683
- 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());
684
671
  for (const field of agentsSchema.fields) {
685
- const { type, name, references, default: defaultValue } = field;
672
+ const { type, name, default: defaultValue } = field;
686
673
  if (!type || !name) {
687
674
  continue;
688
675
  }
689
- if (type === "reference") {
690
- if (!references) {
691
- throw new Error("Field with type reference must have a reference definition.");
692
- }
693
- table.uuid(name).references(references.field).inTable(references.table);
694
- return;
695
- }
696
676
  mapType(table, type, sanitizeName(name), defaultValue);
697
677
  }
698
678
  });
@@ -708,8 +688,8 @@ var up = async function(knex) {
708
688
  if (!await knex.schema.hasTable("users")) {
709
689
  await knex.schema.createTable("users", (table) => {
710
690
  table.increments("id").primary();
711
- table.date("createdAt").defaultTo(knex.fn.now());
712
- table.date("updatedAt").defaultTo(knex.fn.now());
691
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
692
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
713
693
  table.string("name", 255);
714
694
  table.string("password", 255);
715
695
  table.string("email", 255);
@@ -717,20 +697,13 @@ var up = async function(knex) {
717
697
  table.text("image");
718
698
  for (const field of usersSchema.fields) {
719
699
  console.log("[EXULU] field", field);
720
- const { type, name, references, default: defaultValue } = field;
700
+ const { type, name, default: defaultValue } = field;
721
701
  if (name === "id" || name === "name" || name === "email" || name === "emailVerified" || name === "image") {
722
702
  continue;
723
703
  }
724
704
  if (!type || !name) {
725
705
  continue;
726
706
  }
727
- if (type === "reference") {
728
- if (!references) {
729
- throw new Error("Field with type reference must have a reference definition.");
730
- }
731
- table.uuid(name).references(references.field).inTable(references.table);
732
- return;
733
- }
734
707
  mapType(table, type, sanitizeName(name), defaultValue);
735
708
  }
736
709
  });
@@ -751,14 +724,6 @@ var up = async function(knex) {
751
724
  table.text("token_type");
752
725
  });
753
726
  }
754
- if (!await knex.schema.hasTable("sessions")) {
755
- await knex.schema.createTable("sessions", (table) => {
756
- table.increments("id").primary();
757
- table.integer("userId").notNullable();
758
- table.timestamp("expires", { useTz: true }).notNullable();
759
- table.string("sessionToken", 255).notNullable();
760
- });
761
- }
762
727
  };
763
728
  var execute = async () => {
764
729
  console.log("[EXULU] Initializing database.");
@@ -1009,6 +974,35 @@ var ExuluEvalUtils = {
1009
974
  };
1010
975
 
1011
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
+ };
1012
1006
  function generateSlug(name) {
1013
1007
  const normalized = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
1014
1008
  const lowercase = normalized.toLowerCase();
@@ -1035,54 +1029,119 @@ var ExuluAgent = class {
1035
1029
  name;
1036
1030
  description = "";
1037
1031
  slug = "";
1032
+ type;
1038
1033
  streaming = false;
1039
1034
  rateLimit;
1040
1035
  config;
1041
1036
  // private memory: Memory | undefined; // TODO do own implementation
1042
- tools;
1043
1037
  evals;
1044
1038
  model;
1045
1039
  capabilities;
1046
- constructor({ id, name, description, config, rateLimit, capabilities, tools, evals }) {
1040
+ constructor({ id, name, description, config, rateLimit, capabilities, type, evals }) {
1047
1041
  this.id = id;
1048
1042
  this.name = name;
1049
1043
  this.evals = evals;
1050
1044
  this.description = description;
1051
1045
  this.rateLimit = rateLimit;
1052
- this.tools = tools;
1053
1046
  this.config = config;
1054
- this.capabilities = capabilities;
1047
+ this.type = type;
1048
+ this.capabilities = capabilities || {
1049
+ images: [],
1050
+ files: [],
1051
+ audio: [],
1052
+ video: []
1053
+ };
1055
1054
  this.slug = `/agents/${generateSlug(this.name)}/run`;
1056
- this.model = this.config.model;
1055
+ this.model = this.config?.model;
1057
1056
  }
1058
- 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 }) => {
1059
1080
  if (!this.model) {
1060
1081
  throw new Error("Model is required for streaming.");
1061
1082
  }
1062
- if (this.config.outputSchema) {
1063
- if (stream) {
1064
- }
1065
- const { object } = await (0, import_ai.generateObject)({
1066
- model: this.model,
1067
- schema: this.config.outputSchema,
1068
- prompt
1069
- });
1070
- return object;
1083
+ if (!this.config) {
1084
+ throw new Error("Config is required for generating.");
1071
1085
  }
1072
- if (stream) {
1073
- const result = (0, import_ai.streamText)({
1074
- model: this.model,
1075
- prompt
1076
- });
1077
- const text2 = await result.text;
1078
- return text2;
1086
+ if (prompt && messages) {
1087
+ throw new Error("Prompt and messages cannot be provided at the same time.");
1079
1088
  }
1080
1089
  const { text } = await (0, import_ai.generateText)({
1081
1090
  model: this.model,
1082
- 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
1083
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
+ }
1084
1107
  return text;
1085
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
+ };
1086
1145
  };
1087
1146
  var ExuluEmbedder = class {
1088
1147
  id;
@@ -1105,6 +1164,13 @@ var ExuluEmbedder = class {
1105
1164
  }
1106
1165
  async generateFromQuery(query, statistics) {
1107
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
+ });
1108
1174
  }
1109
1175
  return await this.generateEmbeddings({
1110
1176
  item: {
@@ -1118,16 +1184,22 @@ var ExuluEmbedder = class {
1118
1184
  }
1119
1185
  async generateFromDocument(input, statistics) {
1120
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
+ });
1121
1194
  }
1122
1195
  if (!this.chunker) {
1123
1196
  throw new Error("Chunker not found for embedder " + this.name);
1124
1197
  }
1125
- console.log("generating chunks");
1126
1198
  if (!input.id) {
1127
1199
  throw new Error("Item id is required for generating embeddings.");
1128
1200
  }
1129
1201
  const output = await this.chunker(input, this.maxChunkSize);
1130
- console.log("generating embeddings");
1202
+ console.log("[EXULU] Generating embeddings.");
1131
1203
  return await this.generateEmbeddings(output);
1132
1204
  }
1133
1205
  };
@@ -1307,9 +1379,8 @@ var ExuluEval = class {
1307
1379
  if (!data.prompt) {
1308
1380
  throw new Error("Prompt is required for running an agent.");
1309
1381
  }
1310
- const result = await runner.agent.generate({
1311
- prompt: data.prompt,
1312
- stream: false
1382
+ const result = await runner.agent.generateSync({
1383
+ prompt: data.prompt
1313
1384
  });
1314
1385
  data.result = result;
1315
1386
  }
@@ -1404,18 +1475,18 @@ var ExuluTool = class {
1404
1475
  id;
1405
1476
  name;
1406
1477
  description;
1407
- parameters;
1478
+ inputSchema;
1408
1479
  type;
1409
1480
  tool;
1410
- constructor({ id, name, description, parameters, type, execute: execute2 }) {
1481
+ constructor({ id, name, description, inputSchema, type, execute: execute2 }) {
1411
1482
  this.id = id;
1412
1483
  this.name = name;
1413
1484
  this.description = description;
1414
- this.parameters = parameters;
1485
+ this.inputSchema = inputSchema;
1415
1486
  this.type = type;
1416
1487
  this.tool = (0, import_ai.tool)({
1417
1488
  description,
1418
- parameters,
1489
+ parameters: inputSchema || import_zod2.z.object({}),
1419
1490
  execute: execute2
1420
1491
  });
1421
1492
  }
@@ -1471,10 +1542,9 @@ var ExuluContext = class {
1471
1542
  }
1472
1543
  const { db: db2 } = await postgresClient();
1473
1544
  Object.keys(item).forEach((key) => {
1474
- 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") {
1475
1546
  return;
1476
1547
  }
1477
- console.log("this.fields", this.fields);
1478
1548
  const field = this.fields.find((field2) => field2.name === key);
1479
1549
  if (!field) {
1480
1550
  throw new Error("Trying to uppdate value for field '" + key + "' that does not exist on the context fields definition. Available fields: " + this.fields.map((field2) => sanitizeName(field2.name)).join(", ") + " ,name, description, external_id");
@@ -1519,6 +1589,9 @@ var ExuluContext = class {
1519
1589
  chunk_index: chunk.index,
1520
1590
  embedding: import_knex4.default.toSql(chunk.vector)
1521
1591
  })));
1592
+ await db2.from(this.getTableName()).where({ id }).update({
1593
+ embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
1594
+ }).returning("id");
1522
1595
  }
1523
1596
  return {
1524
1597
  id: result[0].id,
@@ -1548,10 +1621,9 @@ var ExuluContext = class {
1548
1621
  }
1549
1622
  }
1550
1623
  Object.keys(item).forEach((key) => {
1551
- 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") {
1552
1625
  return;
1553
1626
  }
1554
- console.log("this.fields", this.fields);
1555
1627
  const field = this.fields.find((field2) => field2.name === key);
1556
1628
  if (!field) {
1557
1629
  throw new Error("Trying to insert value for field '" + key + "' that does not exist on the context fields definition. Available fields: " + this.fields.map((field2) => sanitizeName(field2.name)).join(", ") + " ,name, description, external_id");
@@ -1593,13 +1665,16 @@ var ExuluContext = class {
1593
1665
  if (!exists) {
1594
1666
  await this.createChunksTable();
1595
1667
  }
1596
- console.log("inserting chunks");
1668
+ console.log("[EXULU] Inserting chunks.");
1597
1669
  await db2.from(this.getChunksTableName()).insert(chunks.map((chunk) => ({
1598
1670
  source,
1599
1671
  content: chunk.content,
1600
1672
  chunk_index: chunk.index,
1601
1673
  embedding: import_knex4.default.toSql(chunk.vector)
1602
1674
  })));
1675
+ await db2.from(this.getTableName()).where({ id: result[0].id }).update({
1676
+ embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
1677
+ }).returning("id");
1603
1678
  }
1604
1679
  return {
1605
1680
  id: result[0].id,
@@ -1609,6 +1684,8 @@ var ExuluContext = class {
1609
1684
  getItems = async ({
1610
1685
  statistics,
1611
1686
  limit,
1687
+ sort,
1688
+ order,
1612
1689
  page,
1613
1690
  name,
1614
1691
  archived,
@@ -1629,6 +1706,9 @@ var ExuluContext = class {
1629
1706
  const columns = await db2(mainTable).columnInfo();
1630
1707
  const totalQuery = db2.count("* as count").from(mainTable).first();
1631
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
+ }
1632
1712
  if (typeof name === "string") {
1633
1713
  itemsQuery.whereILike("name", `%${name}%`);
1634
1714
  totalQuery.whereILike("name", `%${name}%`);
@@ -1672,7 +1752,7 @@ var ExuluContext = class {
1672
1752
  }
1673
1753
  itemsQuery.limit(limit * 5);
1674
1754
  if (statistics) {
1675
- updateStatistic({
1755
+ await updateStatistic({
1676
1756
  name: "count",
1677
1757
  label: statistics.label,
1678
1758
  type: STATISTICS_TYPE_ENUM.CONTEXT_RETRIEVE,
@@ -1805,6 +1885,7 @@ var ExuluContext = class {
1805
1885
  table.text("external_id");
1806
1886
  table.integer("textLength");
1807
1887
  table.text("source");
1888
+ table.timestamp("embeddings_updated_at");
1808
1889
  for (const field of this.fields) {
1809
1890
  const { type, name } = field;
1810
1891
  if (!type || !name) {
@@ -1834,15 +1915,15 @@ var ExuluContext = class {
1834
1915
  id: this.id,
1835
1916
  name: `${this.name} context`,
1836
1917
  type: "context",
1837
- parameters: import_zod2.z.object({
1918
+ inputSchema: import_zod2.z.object({
1838
1919
  query: import_zod2.z.string()
1839
1920
  }),
1840
1921
  description: `Gets information from the context called: ${this.name}. The context description is: ${this.description}.`,
1841
- execute: async ({ context }) => {
1922
+ execute: async ({ query }) => {
1842
1923
  return await this.getItems({
1843
1924
  page: 1,
1844
1925
  limit: 10,
1845
- query: context.query,
1926
+ query,
1846
1927
  statistics: {
1847
1928
  label: this.name,
1848
1929
  trigger: "agent"
@@ -1891,7 +1972,32 @@ var ExuluSource = class {
1891
1972
  }
1892
1973
  };
1893
1974
  var updateStatistic = async (statistic) => {
1894
- 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
+ }
1895
2001
  };
1896
2002
 
1897
2003
  // src/registry/index.ts
@@ -1964,7 +2070,6 @@ var authentication = async ({
1964
2070
  internalkey,
1965
2071
  db: db2
1966
2072
  }) => {
1967
- console.log("[EXULU] apikey", apikey);
1968
2073
  if (internalkey) {
1969
2074
  if (!process.env.INTERNAL_SECRET) {
1970
2075
  return {
@@ -1992,7 +2097,7 @@ var authentication = async ({
1992
2097
  }
1993
2098
  if (authtoken) {
1994
2099
  try {
1995
- console.log("authtoken", authtoken);
2100
+ console.log("[EXULU] authtoken", authtoken);
1996
2101
  if (!authtoken?.email) {
1997
2102
  return {
1998
2103
  error: true,
@@ -2001,7 +2106,6 @@ var authentication = async ({
2001
2106
  };
2002
2107
  }
2003
2108
  const user = await db2.from("users").select("*").where("email", authtoken?.email).first();
2004
- console.log("user", user);
2005
2109
  if (!user) {
2006
2110
  return {
2007
2111
  error: true,
@@ -2050,15 +2154,11 @@ var authentication = async ({
2050
2154
  code: 401
2051
2155
  };
2052
2156
  }
2053
- console.log("[EXULU] request_key_name", request_key_name);
2054
- console.log("[EXULU] request_key_compare_value", request_key_compare_value);
2055
2157
  const filtered = users.filter(({ apikey: apikey2, id }) => apikey2.includes(request_key_name));
2056
2158
  for (const user of filtered) {
2057
2159
  const user_key_last_slash_index = user.apikey.lastIndexOf("/");
2058
2160
  const user_key_compare_value = user.apikey.substring(0, user_key_last_slash_index);
2059
- console.log("[EXULU] user_key_compare_value", user_key_compare_value);
2060
2161
  const isMatch = await import_bcryptjs2.default.compare(request_key_compare_value, user_key_compare_value);
2061
- console.log("[EXULU] isMatch", isMatch);
2062
2162
  if (isMatch) {
2063
2163
  await db2.from("users").where({ id: user.id }).update({
2064
2164
  last_used: /* @__PURE__ */ new Date()
@@ -2091,9 +2191,7 @@ var requestValidators = {
2091
2191
  const { db: db2 } = await postgresClient();
2092
2192
  let authtoken = null;
2093
2193
  if (typeof apikey !== "string") {
2094
- const secret = process.env.NEXTAUTH_SECRET;
2095
- authtoken = await getToken(req.headers["authorization"] ?? "");
2096
- console.log("[EXULU] authtoken", authtoken);
2194
+ authtoken = await getToken((req.headers["authorization"] || req.headers["x-api-key"]) ?? "");
2097
2195
  }
2098
2196
  return await authentication({
2099
2197
  authtoken,
@@ -2195,7 +2293,6 @@ var requestValidators = {
2195
2293
  };
2196
2294
  },
2197
2295
  agents: (req) => {
2198
- console.log("[EXULU] validating request body and headers.", req.body);
2199
2296
  const contentType = req.headers["content-type"] || "";
2200
2297
  if (!contentType.includes("application/json")) {
2201
2298
  return {
@@ -2287,6 +2384,42 @@ var import_reflect_metadata = require("reflect-metadata");
2287
2384
  // src/registry/utils/graphql.ts
2288
2385
  var import_schema = require("@graphql-tools/schema");
2289
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
+ });
2290
2423
  var map = (field) => {
2291
2424
  let type;
2292
2425
  switch (field.type) {
@@ -2306,7 +2439,7 @@ var map = (field) => {
2306
2439
  type = "JSON";
2307
2440
  break;
2308
2441
  case "date":
2309
- type = "String";
2442
+ type = "Date";
2310
2443
  break;
2311
2444
  default:
2312
2445
  type = "String";
@@ -2324,8 +2457,8 @@ function createTypeDefs(table) {
2324
2457
  type ${table.name.singular} {
2325
2458
  ${fields.join("\n")}
2326
2459
  id: ID!
2327
- createdAt: String!
2328
- updatedAt: String!
2460
+ createdAt: Date!
2461
+ updatedAt: Date!
2329
2462
  }
2330
2463
  `;
2331
2464
  const inputDef = `
@@ -2351,6 +2484,11 @@ input FilterOperatorString {
2351
2484
  contains: String
2352
2485
  }
2353
2486
 
2487
+ input FilterOperatorDate {
2488
+ lte: Date
2489
+ gte: Date
2490
+ }
2491
+
2354
2492
  input FilterOperatorFloat {
2355
2493
  eq: Float
2356
2494
  ne: Float
@@ -2403,17 +2541,19 @@ function createMutations(table) {
2403
2541
  [`${tableNamePlural}CreateOne`]: async (_, args, context, info) => {
2404
2542
  const { db: db2 } = context;
2405
2543
  const requestedFields = getRequestedFields(info);
2544
+ let { input } = args;
2545
+ input = encryptSensitiveFields(input);
2406
2546
  const results = await db2(tableNamePlural).insert({
2407
- ...args.input,
2547
+ ...input,
2408
2548
  createdAt: /* @__PURE__ */ new Date(),
2409
2549
  updatedAt: /* @__PURE__ */ new Date()
2410
2550
  }).returning(requestedFields);
2411
- console.log("requestedFields", requestedFields);
2412
2551
  return results[0];
2413
2552
  },
2414
2553
  [`${tableNamePlural}UpdateOne`]: async (_, args, context, info) => {
2415
2554
  const { db: db2 } = context;
2416
- const { where, input } = args;
2555
+ let { where, input } = args;
2556
+ input = encryptSensitiveFields(input);
2417
2557
  await db2(tableNamePlural).where(where).update({
2418
2558
  ...input,
2419
2559
  updatedAt: /* @__PURE__ */ new Date()
@@ -2423,7 +2563,8 @@ function createMutations(table) {
2423
2563
  return result;
2424
2564
  },
2425
2565
  [`${tableNamePlural}UpdateOneById`]: async (_, args, context, info) => {
2426
- const { id, input } = args;
2566
+ let { id, input } = args;
2567
+ input = encryptSensitiveFields(input);
2427
2568
  const { db: db2 } = context;
2428
2569
  await db2(tableNamePlural).where({ id }).update({
2429
2570
  ...input,
@@ -2501,7 +2642,6 @@ function createQueries(table) {
2501
2642
  [`${tableNamePlural}Pagination`]: async (_, args, context, info) => {
2502
2643
  const { limit = 10, page = 0, filters = [], sort } = args;
2503
2644
  const { db: db2 } = context;
2504
- console.log("page", page);
2505
2645
  let baseQuery = db2(tableNamePlural);
2506
2646
  baseQuery = applyFilters(baseQuery, filters);
2507
2647
  const [{ count }] = await baseQuery.clone().count("* as count");
@@ -2517,8 +2657,6 @@ function createQueries(table) {
2517
2657
  dataQuery = dataQuery.offset((page - 1) * limit);
2518
2658
  }
2519
2659
  const items = await dataQuery.select(requestedFields).limit(limit);
2520
- console.log("items", items);
2521
- console.log("query", dataQuery.toQuery());
2522
2660
  return {
2523
2661
  pageInfo: {
2524
2662
  pageCount,
@@ -2529,12 +2667,44 @@ function createQueries(table) {
2529
2667
  },
2530
2668
  items
2531
2669
  };
2532
- }
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
+ } : {}
2533
2702
  };
2534
2703
  }
2535
2704
  function createSDL(tables) {
2536
2705
  let typeDefs = `
2537
2706
  scalar JSON
2707
+ scalar Date
2538
2708
 
2539
2709
  type Query {
2540
2710
  `;
@@ -2542,7 +2712,7 @@ function createSDL(tables) {
2542
2712
  type Mutation {
2543
2713
  `;
2544
2714
  let modelDefs = "";
2545
- const resolvers = { JSON: import_graphql_type_json.default, Query: {}, Mutation: {} };
2715
+ const resolvers = { JSON: import_graphql_type_json.default, Date: GraphQLDate, Query: {}, Mutation: {} };
2546
2716
  for (const table of tables) {
2547
2717
  const tableNamePlural = table.name.plural.toLowerCase();
2548
2718
  const tableNameSingular = table.name.singular.toLowerCase();
@@ -2551,6 +2721,7 @@ function createSDL(tables) {
2551
2721
  ${tableNameSingular}ById(id: ID!): ${tableNameSingular}
2552
2722
  ${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
2553
2723
  ${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
2724
+ ${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
2554
2725
  `;
2555
2726
  mutationDefs += `
2556
2727
  ${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!): ${tableNameSingular}
@@ -2575,6 +2746,15 @@ type PageInfo {
2575
2746
  hasNextPage: Boolean!
2576
2747
  }
2577
2748
  `;
2749
+ if (tableNamePlural === "jobs") {
2750
+ modelDefs += `
2751
+ type JobStatistics {
2752
+ completedCount: Int!
2753
+ failedCount: Int!
2754
+ averageDuration: Float!
2755
+ }
2756
+ `;
2757
+ }
2578
2758
  Object.assign(resolvers.Query, createQueries(table));
2579
2759
  Object.assign(resolvers.Mutation, createMutations(table));
2580
2760
  }
@@ -2611,6 +2791,15 @@ type PageInfo {
2611
2791
  console.log("\n");
2612
2792
  return schema;
2613
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
+ };
2614
2803
 
2615
2804
  // src/registry/routes.ts
2616
2805
  var import_express5 = require("@as-integrations/express5");
@@ -3009,6 +3198,34 @@ var createUppyRoutes = async (app) => {
3009
3198
  // src/registry/routes.ts
3010
3199
  var import_utils2 = require("@apollo/utils.keyvaluecache");
3011
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
3012
3229
  var REQUEST_SIZE_LIMIT = "50mb";
3013
3230
  var global_queues = {
3014
3231
  logs_cleaner: "logs-cleaner"
@@ -3045,7 +3262,7 @@ var createRecurringJobs = async () => {
3045
3262
  console.table(recurringJobSchedulersLogs);
3046
3263
  return queue;
3047
3264
  };
3048
- var createExpressRoutes = async (app, agents, embedders, tools, workflows, contexts) => {
3265
+ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
3049
3266
  const routeLogs = [];
3050
3267
  var corsOptions = {
3051
3268
  origin: "*",
@@ -3065,6 +3282,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3065
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
3066
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
3067
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
3068
3286
 
3069
3287
  `);
3070
3288
  console.log("Agents:");
@@ -3113,7 +3331,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3113
3331
  } else {
3114
3332
  console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
3115
3333
  }
3116
- const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, threadsSchema, messagesSchema]);
3334
+ const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, agentSessionsSchema, agentMessagesSchema]);
3117
3335
  console.log("[EXULU] graphql server");
3118
3336
  const server = new import_server3.ApolloServer({
3119
3337
  cache: new import_utils2.InMemoryLRUCache(),
@@ -3141,10 +3359,52 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3141
3359
  }
3142
3360
  })
3143
3361
  );
3144
- 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
+ }
3145
3368
  res.status(200).json(agents);
3146
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
+ });
3147
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
+ }
3148
3408
  const { db: db2 } = await postgresClient();
3149
3409
  const id = req.params.id;
3150
3410
  if (!id) {
@@ -3167,6 +3427,8 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3167
3427
  name: agent.name,
3168
3428
  id: agent.id,
3169
3429
  description: agent.description,
3430
+ provider: backend?.model?.provider,
3431
+ model: backend?.model?.modelId,
3170
3432
  active: agent.active,
3171
3433
  public: agent.public,
3172
3434
  type: agent.type,
@@ -3175,7 +3437,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3175
3437
  streaming: backend?.streaming,
3176
3438
  capabilities: backend?.capabilities,
3177
3439
  // todo add contexts
3178
- availableTools: backend?.tools,
3440
+ availableTools: tools,
3179
3441
  enabledTools: agent.tools
3180
3442
  }
3181
3443
  });
@@ -3186,8 +3448,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3186
3448
  name: tool2.name,
3187
3449
  description: tool2.description,
3188
3450
  type: tool2.type || "tool",
3189
- inputSchema: tool2.inputSchema ? (0, import_zodex.zerialize)(tool2.inputSchema) : null,
3190
- outputSchema: tool2.outputSchema ? (0, import_zodex.zerialize)(tool2.outputSchema) : null
3451
+ inputSchema: tool2.inputSchema ? (0, import_zodex.zerialize)(tool2.inputSchema) : null
3191
3452
  })));
3192
3453
  });
3193
3454
  app.get("/tools/:id", async (req, res) => {
@@ -3305,6 +3566,14 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3305
3566
  });
3306
3567
  return;
3307
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
+ }
3308
3577
  const item = await db2.from(context.getTableName()).where({ id: req.params.id }).select("*").first();
3309
3578
  if (!item) {
3310
3579
  res.status(404).json({
@@ -3426,6 +3695,20 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3426
3695
  }
3427
3696
  let limit = req.query.limit ? parseInt(req.query.limit) : 10;
3428
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
+ }
3429
3712
  const authenticationResult = await requestValidators.authenticate(req);
3430
3713
  if (!authenticationResult.user?.id) {
3431
3714
  res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
@@ -3449,6 +3732,8 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3449
3732
  return;
3450
3733
  }
3451
3734
  const result = await context.getItems({
3735
+ sort,
3736
+ order,
3452
3737
  page,
3453
3738
  limit,
3454
3739
  archived: req.query.archived === "true",
@@ -3528,6 +3813,18 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3528
3813
  message: "Embedding deleted."
3529
3814
  });
3530
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
+ });
3531
3828
  console.log("[EXULU] statistics timeseries");
3532
3829
  app.post("/statistics/timeseries", async (req, res) => {
3533
3830
  const authenticationResult = await requestValidators.authenticate(req);
@@ -3618,7 +3915,6 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3618
3915
  const response = await db2("jobs").select(db2.raw(`to_char("createdAt", 'YYYY-MM-DD') as date`)).count("* as count").where("type", "embedder").groupByRaw(`to_char("createdAt", 'YYYY-MM-DD')`).then((rows) => ({
3619
3916
  jobs: rows
3620
3917
  }));
3621
- console.log({ response });
3622
3918
  let jobs = [];
3623
3919
  if (response[0]) {
3624
3920
  jobs = response[0].jobs.map((job) => ({
@@ -3668,6 +3964,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3668
3964
  slug: "/contexts/" + context.id,
3669
3965
  active: context.active,
3670
3966
  fields: context.fields,
3967
+ configuration: context.configuration,
3671
3968
  sources: context.sources.get().map((source) => ({
3672
3969
  id: source.id,
3673
3970
  name: source.name,
@@ -3715,13 +4012,11 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3715
4012
  });
3716
4013
  console.log("[EXULU] contexts get list");
3717
4014
  app.get(`/contexts`, async (req, res) => {
3718
- console.log("contexts!!");
3719
4015
  const authenticationResult = await requestValidators.authenticate(req);
3720
4016
  if (!authenticationResult.user?.id) {
3721
4017
  res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3722
4018
  return;
3723
4019
  }
3724
- console.log("contexts", contexts?.length);
3725
4020
  res.status(200).json(contexts.map((context) => ({
3726
4021
  id: context.id,
3727
4022
  name: context.name,
@@ -3864,47 +4159,28 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3864
4159
  res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3865
4160
  return;
3866
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);
3867
4165
  if (!!stream) {
3868
- const chatClient = await agent.chat(agentInstance.id);
3869
- if (!chatClient) {
3870
- res.status(500).json({
3871
- message: "Agent instantiation not successful."
3872
- });
3873
- return;
3874
- }
3875
- const { textStream } = await chatClient.stream(req.body.messages, {
3876
- threadId: `${req.body.threadId}`,
3877
- // conversation id
3878
- resourceId: `${req.body.resourceId}`,
3879
- // user id
3880
- ...agent.outputSchema && { output: agent.outputSchema },
3881
- maxRetries: 2,
3882
- // todo make part of ExuluAgent class
3883
- maxSteps: 5,
3884
- // todo make part of ExuluAgent class
3885
- onError: (error) => console.error("[EXULU] chat stream error.", error),
3886
- onFinish: ({ response, usage }) => console.info(
3887
- "[EXULU] chat stream finished.",
3888
- usage
3889
- )
4166
+ const result = agent.generateStream({
4167
+ messages: req.body.messages,
4168
+ tools: enabledTools,
4169
+ statistics: {
4170
+ label: agent.name,
4171
+ trigger: "agent"
4172
+ }
3890
4173
  });
3891
- for await (const delta of textStream) {
3892
- res.write(`data: ${delta}
3893
-
3894
- `);
3895
- }
3896
- res.end();
4174
+ result.pipeDataStreamToResponse(res);
3897
4175
  return;
3898
4176
  } else {
3899
- const response = await agent.chat.generate(req.body.messages, {
3900
- resourceId: `${authenticationResult.user.id}`,
3901
- output: agent.outputSchema,
3902
- threadId: `${req.body.threadId}`,
3903
- // conversation id
3904
- maxRetries: 2,
3905
- // todo make part of ExuluAgent class
3906
- maxSteps: 5
3907
- // 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
+ }
3908
4184
  });
3909
4185
  res.status(200).json(response);
3910
4186
  return;
@@ -3979,7 +4255,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3979
4255
  console.log("Routes:");
3980
4256
  console.table(routeLogs);
3981
4257
  const TARGET_API = "https://api.anthropic.com";
3982
- 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) => {
3983
4259
  const path3 = req.url;
3984
4260
  const url = `${TARGET_API}${path3}`;
3985
4261
  console.log("[PROXY] Manual proxy to:", url);
@@ -3988,60 +4264,102 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3988
4264
  console.log("[PROXY] Request body length:", req.body ? req.body.length : 0);
3989
4265
  console.log("[PROXY] Request model name:", req.body.model);
3990
4266
  console.log("[PROXY] Request stream:", req.body.stream);
3991
- console.log("[PROXY] API key:", req.headers["x-api-key"]);
3992
4267
  console.log("[PROXY] Request messages:", req.body.messages?.length);
3993
4268
  try {
3994
- const headers = {
3995
- "x-api-key": process.env.ANTHROPIC_API_KEY,
3996
- "anthropic-version": "2023-06-01",
3997
- "content-type": req.headers["content-type"] || "application/json"
3998
- };
3999
- if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
4000
- if (req.headers["user-agent"]) headers["user-agent"] = req.headers["user-agent"];
4001
4269
  console.log("[PROXY] Request body tools array length:", req.body.tools?.length);
4002
4270
  if (!req.body.tools) {
4003
4271
  req.body.tools = [];
4004
4272
  }
4005
- if (req.headers["x-api-key"] === "PLACEHOLDER") {
4006
- res.write(JSON.stringify({
4007
- "type": "content_block_delta",
4008
- "index": 0,
4009
- "delta": {
4010
- "type": "text_delta",
4011
- "text": "Hello, world!"
4012
- }
4013
- }));
4014
- 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));
4296
+ return;
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));
4015
4302
  return;
4016
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);
4017
4314
  const response = await fetch(url, {
4018
4315
  method: req.method,
4019
4316
  headers,
4020
4317
  body: req.method !== "GET" ? JSON.stringify(req.body) : void 0
4021
4318
  });
4022
- 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
+ });
4023
4328
  response.headers.forEach((value, key) => {
4024
4329
  res.setHeader(key, value);
4025
4330
  });
4026
4331
  res.status(response.status);
4027
- 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) {
4028
4340
  const reader = response.body.getReader();
4029
4341
  const decoder = new TextDecoder();
4030
4342
  while (true) {
4031
4343
  const { done, value } = await reader.read();
4032
4344
  if (done) break;
4033
4345
  const chunk = decoder.decode(value, { stream: true });
4346
+ console.log("[PROXY] Chunk:", chunk);
4034
4347
  res.write(chunk);
4035
4348
  }
4036
4349
  res.end();
4037
- } else {
4038
- const data = await response.arrayBuffer();
4039
- res.end(Buffer.from(data));
4350
+ return;
4040
4351
  }
4352
+ const data = await response.arrayBuffer();
4353
+ console.log("[PROXY] Data:", data);
4354
+ res.end(Buffer.from(data));
4041
4355
  } catch (error) {
4042
4356
  console.error("[PROXY] Manual proxy error:", error);
4043
4357
  if (!res.headersSent) {
4044
- 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
+ }
4045
4363
  }
4046
4364
  }
4047
4365
  });
@@ -4089,6 +4407,20 @@ var getPresignedFileUrl = async (key) => {
4089
4407
  console.log(`[EXULU] presigned url for file with key: ${key}, generated: ${json.url}`);
4090
4408
  return json.url;
4091
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
+ };
4092
4424
 
4093
4425
  // src/registry/workers.ts
4094
4426
  var import_ioredis = __toESM(require("ioredis"), 1);
@@ -4141,7 +4473,7 @@ var fs2 = __toESM(require("fs"), 1);
4141
4473
  var import_path = __toESM(require("path"), 1);
4142
4474
  var defaultLogsDir = import_path.default.join(process.cwd(), "logs");
4143
4475
  var redisConnection;
4144
- var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) => {
4476
+ var createWorkers = async (queues2, contexts, workflows, _logsDir) => {
4145
4477
  if (!redisServer.host || !redisServer.port) {
4146
4478
  console.error("[EXULU] you are trying to start workers, but no redis server is configured in the environment.");
4147
4479
  throw new Error("No redis server configured in the environment, so cannot start workers.");
@@ -4172,7 +4504,7 @@ var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) =>
4172
4504
  if (!bullmqJob.data.embedder) {
4173
4505
  throw new Error(`No embedder set for embedder job.`);
4174
4506
  }
4175
- const embedder = embedders.find((embedder2) => embedder2.id === bullmqJob.data.embedder);
4507
+ const embedder = contexts.find((context2) => context2.embedder?.id === bullmqJob.data.embedder);
4176
4508
  if (!embedder) {
4177
4509
  throw new Error(`Embedder ${bullmqJob.data.embedder} not found in the registry.`);
4178
4510
  }
@@ -4212,6 +4544,9 @@ var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) =>
4212
4544
  duration,
4213
4545
  result: JSON.stringify(result)
4214
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");
4215
4550
  return result;
4216
4551
  }
4217
4552
  if (bullmqJob.data.type === "workflow") {
@@ -4309,7 +4644,7 @@ var ExuluMCP = class {
4309
4644
  express;
4310
4645
  constructor() {
4311
4646
  }
4312
- create = async ({ express: express3, contexts, embedders, agents, workflows, config, tools }) => {
4647
+ create = async ({ express: express3, contexts, agents, workflows, config, tools }) => {
4313
4648
  this.express = express3;
4314
4649
  if (!this.server) {
4315
4650
  console.log("[EXULU] Creating MCP server.");
@@ -4386,8 +4721,6 @@ ${code}`
4386
4721
  throw new Error("MCP server not initialized.");
4387
4722
  }
4388
4723
  const sessionId = req.headers[SESSION_ID_HEADER];
4389
- console.log("sessionId!!", sessionId);
4390
- console.log("req.headers!!", req.headers);
4391
4724
  let transport;
4392
4725
  if (sessionId && this.transports[sessionId]) {
4393
4726
  transport = this.transports[sessionId];
@@ -4417,10 +4750,9 @@ ${code}`
4417
4750
  await transport.handleRequest(req, res, req.body);
4418
4751
  });
4419
4752
  const handleSessionRequest = async (req, res) => {
4420
- console.log("handleSessionRequest", req.body);
4421
4753
  const sessionId = req.headers[SESSION_ID_HEADER];
4422
4754
  if (!sessionId || !this.transports[sessionId]) {
4423
- console.log("Invalid or missing session ID");
4755
+ console.log("[EXULU] MCP request invalid or missing session ID");
4424
4756
  res.status(400).send("Invalid or missing session ID");
4425
4757
  return;
4426
4758
  }
@@ -4443,28 +4775,210 @@ ${code}`
4443
4775
 
4444
4776
  // src/registry/index.ts
4445
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
4446
4946
  var ExuluApp = class {
4447
4947
  _agents = [];
4448
4948
  _workflows = [];
4449
4949
  _config;
4450
- _embedders = [];
4451
4950
  _queues = [];
4452
4951
  _contexts = {};
4453
4952
  _tools = [];
4454
4953
  _expressApp = null;
4455
4954
  constructor() {
4456
4955
  }
4457
- // Factory function so we can async initialize the
4458
- // MCP server if needed.
4459
- create = async ({ contexts, embedders, agents, workflows, config, tools }) => {
4460
- 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 }) => {
4461
4959
  this._workflows = workflows ?? [];
4462
4960
  this._contexts = contexts ?? {};
4463
- this._agents = agents ?? [];
4961
+ this._agents = [
4962
+ claudeCodeAgent,
4963
+ defaultAgent,
4964
+ ...agents ?? []
4965
+ ];
4464
4966
  this._config = config;
4465
- 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 || {});
4466
4980
  const queues2 = [
4467
- ...embedders?.length ? embedders.map((agent) => agent.queue?.name || null) : [],
4981
+ ...contextsArray?.length ? contextsArray.map((context) => context.embedder.queue?.name || null) : [],
4468
4982
  ...workflows?.length ? workflows.map((workflow) => workflow.queue?.name || null) : []
4469
4983
  ];
4470
4984
  this._queues = [...new Set(queues2.filter((o) => !!o))];
@@ -4481,9 +4995,6 @@ var ExuluApp = class {
4481
4995
  }
4482
4996
  return this._expressApp;
4483
4997
  }
4484
- embedder(id) {
4485
- return this._embedders.find((x) => x.id === id);
4486
- }
4487
4998
  tool(id) {
4488
4999
  return this._tools.find((x) => x.id === id);
4489
5000
  }
@@ -4499,9 +5010,6 @@ var ExuluApp = class {
4499
5010
  workflow(id) {
4500
5011
  return this._workflows.find((x) => x.id === id);
4501
5012
  }
4502
- get embedders() {
4503
- return this._embedders;
4504
- }
4505
5013
  get contexts() {
4506
5014
  return Object.values(this._contexts ?? {});
4507
5015
  }
@@ -4517,7 +5025,6 @@ var ExuluApp = class {
4517
5025
  return await createWorkers(
4518
5026
  this._queues,
4519
5027
  Object.values(this._contexts ?? {}),
4520
- this._embedders,
4521
5028
  this._workflows,
4522
5029
  this._config?.workers?.logsDir
4523
5030
  );
@@ -4534,7 +5041,6 @@ var ExuluApp = class {
4534
5041
  await createExpressRoutes(
4535
5042
  app,
4536
5043
  this._agents,
4537
- this._embedders,
4538
5044
  this._tools,
4539
5045
  this._workflows,
4540
5046
  Object.values(this._contexts ?? {})
@@ -4544,7 +5050,6 @@ var ExuluApp = class {
4544
5050
  await mcp.create({
4545
5051
  express: app,
4546
5052
  contexts: this._contexts,
4547
- embedders: this._embedders,
4548
5053
  agents: this._agents,
4549
5054
  workflows: this._workflows,
4550
5055
  config: this._config,
@@ -4873,15 +5378,12 @@ var ExuluTokenizer = class {
4873
5378
  console.log("[EXULU] Loading tokenizer.", modelName);
4874
5379
  const model = await (0, import_load.load)(import_registry.default[import_model_to_encoding.default[modelName]]);
4875
5380
  console.log("[EXULU] Loaded tokenizer.", modelName, performance.now() - time);
4876
- console.log("[EXULU] Model.", model.bpe_ranks);
4877
- console.log("[EXULU] Model.", model.special_tokens);
4878
- console.log("[EXULU] Model.", model.pat_str);
4879
5381
  const encoder = new import_lite.Tiktoken(
4880
5382
  model.bpe_ranks,
4881
5383
  model.special_tokens,
4882
5384
  model.pat_str
4883
5385
  );
4884
- console.log("[EXULU] Encoder.", encoder);
5386
+ console.log("[EXULU] Set encoder.");
4885
5387
  this.encoder = encoder;
4886
5388
  return encoder;
4887
5389
  }
@@ -4904,9 +5406,9 @@ var ExuluTokenizer = class {
4904
5406
  throw new Error("Tokenizer not initialized");
4905
5407
  }
4906
5408
  const time = performance.now();
4907
- console.log("[EXULU] Encoding text.", text);
5409
+ console.log("[EXULU] Encoding text length: " + (text?.length || 0));
4908
5410
  const tokens = this.encoder.encode(text);
4909
- console.log("[EXULU] Encoded text.", text, performance.now() - time);
5411
+ console.log("[EXULU] Finished encoding text.", performance.now() - time);
4910
5412
  return tokens;
4911
5413
  }
4912
5414
  async countTokensBatch(texts) {
@@ -4920,7 +5422,6 @@ var ExuluTokenizer = class {
4920
5422
  if (!this.encoder) {
4921
5423
  throw new Error("Tokenizer not initialized");
4922
5424
  }
4923
- console.log("[EXULU] Counting tokens.", text);
4924
5425
  const tokens = this.encoder.encode(text);
4925
5426
  const count = tokens.length;
4926
5427
  console.log("[EXULU] Token count.", count);
@@ -5090,8 +5591,6 @@ var RecursiveChunker = class _RecursiveChunker extends BaseChunker {
5090
5591
  *
5091
5592
  * @example <caption>Accessing properties and methods</caption>
5092
5593
  * const chunker = await RecursiveChunker.create();
5093
- * console.log(chunker.chunkSize); // 512
5094
- * console.log(chunker.rules); // RecursiveRules instance
5095
5594
  * const chunks = await chunker.chunk("Some text"); // Use as object method
5096
5595
  *
5097
5596
  * @note
@@ -5310,8 +5809,6 @@ var RecursiveChunker = class _RecursiveChunker extends BaseChunker {
5310
5809
  if (!text) {
5311
5810
  return [];
5312
5811
  }
5313
- console.log("[EXULU] Rule.", this.rules.length);
5314
- console.log("[EXULU] Level.", level);
5315
5812
  if (level >= this.rules.length) {
5316
5813
  const tokenCount = await this._estimateTokenCount(text);
5317
5814
  return [
@@ -5375,7 +5872,6 @@ var RecursiveChunker = class _RecursiveChunker extends BaseChunker {
5375
5872
  * @returns {Promise<RecursiveChunk[]>} A promise that resolves to an array of RecursiveChunk objects
5376
5873
  */
5377
5874
  async chunk(text) {
5378
- console.log("[EXULU] Chunking text.", text);
5379
5875
  const result = await this._recursiveChunk(text, 0, 0);
5380
5876
  await this.tokenizer.free();
5381
5877
  return result;
@@ -5761,200 +6257,6 @@ var SentenceChunker = class _SentenceChunker extends BaseChunker {
5761
6257
  }
5762
6258
  };
5763
6259
 
5764
- // src/cli/index.tsx
5765
- var import_react2 = require("react");
5766
- var import_ink4 = require("ink");
5767
- var import_ui5 = require("@inkjs/ui");
5768
- var import_patch_console = __toESM(require("patch-console"), 1);
5769
-
5770
- // src/cli/components/nav.tsx
5771
- var import_ui = require("@inkjs/ui");
5772
- var import_ink = require("ink");
5773
- var import_jsx_runtime = require("react/jsx-runtime");
5774
- var nav = [
5775
- {
5776
- label: "Agents",
5777
- value: "agents"
5778
- },
5779
- {
5780
- label: "Start Claude Code",
5781
- value: "claude-code"
5782
- },
5783
- {
5784
- label: "Exit",
5785
- value: "exit"
5786
- }
5787
- ];
5788
- var Nav = ({ setView }) => {
5789
- const { exit } = (0, import_ink.useApp)();
5790
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Select, { options: nav, onChange: (value) => {
5791
- if (value === "exit") {
5792
- exit();
5793
- }
5794
- setView(value);
5795
- } });
5796
- };
5797
- var nav_default = Nav;
5798
-
5799
- // src/cli/components/agent-selector.tsx
5800
- var import_ink2 = require("ink");
5801
- var import_ui2 = require("@inkjs/ui");
5802
- var import_jsx_runtime2 = require("react/jsx-runtime");
5803
- var AgentSelector = ({ exulu, setAgent, setEvaluations }) => {
5804
- const agents = exulu.agents.map((agent) => ({
5805
- label: agent.name,
5806
- value: agent.id
5807
- }));
5808
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
5809
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: "Please select an agent:" }),
5810
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui2.Select, { options: agents, onChange: (value) => {
5811
- console.log("selected agent", value);
5812
- const agent = exulu.agent(value);
5813
- if (!agent) {
5814
- console.error("Agent not found", value);
5815
- return;
5816
- }
5817
- setAgent(agent);
5818
- if (agent) {
5819
- setEvaluations(agent.evals || []);
5820
- }
5821
- } })
5822
- ] });
5823
- };
5824
- var agent_selector_default = AgentSelector;
5825
-
5826
- // src/cli/components/eval-selector.tsx
5827
- var import_ui3 = require("@inkjs/ui");
5828
- var import_jsx_runtime3 = require("react/jsx-runtime");
5829
- var EvalSelector = ({ evaluations, setEvaluation }) => {
5830
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui3.Select, { options: evaluations.map((evaluation) => ({
5831
- label: evaluation.runner.name,
5832
- value: evaluation.runner.name
5833
- })), onChange: (value) => {
5834
- console.log("selected eval", value);
5835
- const evaluation = evaluations?.find((evaluation2) => evaluation2.runner.name === value);
5836
- if (evaluation) {
5837
- setEvaluation(evaluation);
5838
- }
5839
- } });
5840
- };
5841
- var eval_selector_default = EvalSelector;
5842
-
5843
- // src/cli/components/eval-actions.tsx
5844
- var import_react = require("react");
5845
- var import_ui4 = require("@inkjs/ui");
5846
- var import_ink3 = require("ink");
5847
- var import_jsx_runtime4 = require("react/jsx-runtime");
5848
- var EvalActions = ({ agent, evaluation, setEvaluation }) => {
5849
- const [progress, setProgress] = (0, import_react.useState)(0);
5850
- const [results, setResults] = (0, import_react.useState)([]);
5851
- const [running, setRunning] = (0, import_react.useState)();
5852
- const run = async (evaluation2) => {
5853
- setRunning({
5854
- label: evaluation2.runner.name
5855
- });
5856
- const testCases = evaluation2.runner.testcases;
5857
- const total = testCases.length;
5858
- if (!testCases) {
5859
- throw new Error("No test cases found");
5860
- }
5861
- let i = 0;
5862
- for (const testCase of testCases) {
5863
- i++;
5864
- const result = await evaluation2.runner.run({
5865
- data: testCase,
5866
- runner: {
5867
- agent
5868
- }
5869
- });
5870
- setProgress(Math.round(i / total * 100));
5871
- setResults([...results, {
5872
- name: evaluation2.runner.name,
5873
- prompt: testCase.prompt?.slice(0, 100) + "...",
5874
- score: result.score,
5875
- comment: result.comment
5876
- }]);
5877
- }
5878
- setRunning(void 0);
5879
- };
5880
- if (progress === 100) {
5881
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
5882
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink3.Text, { children: "Evaluations completed." }),
5883
- /* @__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: [
5884
- result.name,
5885
- ": ",
5886
- result.score,
5887
- " - ",
5888
- result.comment
5889
- ] }) })) })
5890
- ] });
5891
- }
5892
- if (running) {
5893
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
5894
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink3.Text, { children: [
5895
- "Running ",
5896
- running.label,
5897
- "..."
5898
- ] }),
5899
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui4.ProgressBar, { value: progress })
5900
- ] });
5901
- }
5902
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui4.Select, { options: [{
5903
- label: "Run evaluation",
5904
- value: "run"
5905
- }, {
5906
- label: "Go back",
5907
- value: "back"
5908
- }], onChange: (value) => {
5909
- if (value === "back") {
5910
- setEvaluation(void 0);
5911
- }
5912
- if (value === "run") {
5913
- run(evaluation);
5914
- }
5915
- } });
5916
- };
5917
- var eval_actions_default = EvalActions;
5918
-
5919
- // src/cli/index.tsx
5920
- var import_jsx_runtime5 = require("react/jsx-runtime");
5921
- var Main = ({ exulu }) => {
5922
- (0, import_patch_console.default)((stream, data) => {
5923
- setLogs([...logs, data]);
5924
- });
5925
- const [logs, setLogs] = (0, import_react2.useState)([]);
5926
- const [view, setView] = (0, import_react2.useState)();
5927
- const [agent, setAgent] = (0, import_react2.useState)();
5928
- const [evaluations, setEvaluations] = (0, import_react2.useState)([]);
5929
- const [evaluation, setEvaluation] = (0, import_react2.useState)();
5930
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink4.Box, { borderStyle: "round", borderColor: "cyan", padding: 1, flexDirection: "column", width: "70%", children: [
5931
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink4.Text, { children: "Logs:" }),
5932
- /* @__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 }) })) }),
5933
- !view && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(nav_default, { setView }),
5934
- view === "agents" && !agent && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(agent_selector_default, { exulu, setAgent, setEvaluations }),
5935
- view === "agents" && agent && !evaluation && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
5936
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink4.Text, { children: [
5937
- 'Selected agent "',
5938
- agent.name,
5939
- '". Please select an evaluation:'
5940
- ] }),
5941
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(eval_selector_default, { evaluations, setEvaluation })
5942
- ] }),
5943
- view === "agents" && agent && evaluation && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
5944
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink4.Text, { children: [
5945
- "Selected evaluation: ",
5946
- evaluation.runner.name
5947
- ] }),
5948
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(eval_actions_default, { agent, evaluation, setEvaluation })
5949
- ] })
5950
- ] });
5951
- };
5952
- var cli_default = {
5953
- run: (exulu) => {
5954
- (0, import_ink4.render)(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Main, { exulu }));
5955
- }
5956
- };
5957
-
5958
6260
  // src/index.ts
5959
6261
  var ExuluJobs = {
5960
6262
  redis: redisClient,
@@ -5985,7 +6287,6 @@ var ExuluDatabase = {
5985
6287
  ExuluApp,
5986
6288
  ExuluAuthentication,
5987
6289
  ExuluChunkers,
5988
- ExuluCli,
5989
6290
  ExuluContext,
5990
6291
  ExuluDatabase,
5991
6292
  ExuluEmbedder,