@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.js CHANGED
@@ -78,11 +78,6 @@ async function postgresClient() {
78
78
  if (!db["exulu"]) {
79
79
  try {
80
80
  console.log("[EXULU] Initializing exulu database.");
81
- console.log(process.env.POSTGRES_DB_HOST);
82
- console.log(process.env.POSTGRES_DB_PORT);
83
- console.log(process.env.POSTGRES_DB_USER);
84
- console.log(process.env.POSTGRES_DB_PASSWORD);
85
- console.log(process.env.POSTGRES_DB_SSL);
86
81
  const knex = Knex({
87
82
  client: "pg",
88
83
  connection: {
@@ -107,6 +102,47 @@ async function postgresClient() {
107
102
  }
108
103
 
109
104
  // src/postgres/core-schema.ts
105
+ var agentMessagesSchema = {
106
+ name: {
107
+ plural: "agent_messages",
108
+ singular: "agent_message"
109
+ },
110
+ fields: [
111
+ {
112
+ name: "content",
113
+ type: "text"
114
+ },
115
+ {
116
+ name: "title",
117
+ type: "text"
118
+ },
119
+ {
120
+ name: "session",
121
+ type: "text"
122
+ }
123
+ ]
124
+ };
125
+ var agentSessionsSchema = {
126
+ name: {
127
+ plural: "agent_sessions",
128
+ singular: "agent_session"
129
+ },
130
+ fields: [
131
+ {
132
+ name: "agent",
133
+ type: "uuid"
134
+ },
135
+ {
136
+ name: "user",
137
+ // next auth stores users with id type SERIAL, so we need to use number
138
+ type: "number"
139
+ },
140
+ {
141
+ name: "title",
142
+ type: "text"
143
+ }
144
+ ]
145
+ };
110
146
  var usersSchema = {
111
147
  name: {
112
148
  plural: "users",
@@ -164,14 +200,13 @@ var usersSchema = {
164
200
  name: "last_used",
165
201
  type: "date"
166
202
  },
203
+ {
204
+ name: "anthropic_token",
205
+ type: "text"
206
+ },
167
207
  {
168
208
  name: "role",
169
- type: "reference",
170
- references: {
171
- table: "roles",
172
- field: "id",
173
- onDelete: "CASCADE"
174
- }
209
+ type: "uuid"
175
210
  }
176
211
  ]
177
212
  };
@@ -217,10 +252,6 @@ var statisticsSchema = {
217
252
  {
218
253
  name: "total",
219
254
  type: "number"
220
- },
221
- {
222
- name: "timeseries",
223
- type: "json"
224
255
  }
225
256
  ]
226
257
  };
@@ -276,11 +307,11 @@ var evalResultsSchema = {
276
307
  },
277
308
  {
278
309
  name: "agent_id",
279
- type: "text"
310
+ type: "uuid"
280
311
  },
281
312
  {
282
313
  name: "workflow_id",
283
- type: "text"
314
+ type: "uuid"
284
315
  },
285
316
  {
286
317
  name: "eval_type",
@@ -296,50 +327,6 @@ var evalResultsSchema = {
296
327
  }
297
328
  ]
298
329
  };
299
- var threadsSchema = {
300
- name: {
301
- plural: "threads",
302
- singular: "thread"
303
- },
304
- fields: [
305
- {
306
- name: "resourceId",
307
- type: "text"
308
- },
309
- {
310
- name: "title",
311
- type: "text"
312
- },
313
- {
314
- name: "metadata",
315
- type: "text"
316
- }
317
- ]
318
- };
319
- var messagesSchema = {
320
- name: {
321
- plural: "messages",
322
- singular: "message"
323
- },
324
- fields: [
325
- {
326
- name: "thread_id",
327
- type: "text"
328
- },
329
- {
330
- name: "content",
331
- type: "text"
332
- },
333
- {
334
- name: "role",
335
- type: "text"
336
- },
337
- {
338
- name: "type",
339
- type: "text"
340
- }
341
- ]
342
- };
343
330
  var jobsSchema = {
344
331
  name: {
345
332
  plural: "jobs",
@@ -372,19 +359,20 @@ var jobsSchema = {
372
359
  },
373
360
  {
374
361
  name: "agent",
375
- type: "text"
362
+ type: "uuid"
376
363
  },
377
364
  {
378
365
  name: "workflow",
379
- type: "text"
366
+ type: "uuid"
380
367
  },
381
368
  {
382
369
  name: "user",
383
- type: "text"
370
+ // next auth stores users with id type SERIAL, so we need to use number
371
+ type: "number"
384
372
  },
385
373
  {
386
374
  name: "item",
387
- type: "text"
375
+ type: "uuid"
388
376
  },
389
377
  {
390
378
  name: "steps",
@@ -478,7 +466,7 @@ var mapType = (t, type, name, defaultValue) => {
478
466
  return;
479
467
  }
480
468
  if (type === "date") {
481
- t.date(name);
469
+ t.timestamp(name);
482
470
  return;
483
471
  }
484
472
  if (type === "uuid") {
@@ -548,23 +536,44 @@ var generateApiKey = async (name, email) => {
548
536
 
549
537
  // src/postgres/init-db.ts
550
538
  var up = async function(knex) {
539
+ if (!await knex.schema.hasTable("agent_sessions")) {
540
+ await knex.schema.createTable("agent_sessions", (table) => {
541
+ table.uuid("id").primary().defaultTo(knex.fn.uuid());
542
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
543
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
544
+ for (const field of agentSessionsSchema.fields) {
545
+ const { type, name, default: defaultValue } = field;
546
+ if (!type || !name) {
547
+ continue;
548
+ }
549
+ mapType(table, type, sanitizeName(name), defaultValue);
550
+ }
551
+ });
552
+ }
553
+ if (!await knex.schema.hasTable("agent_messages")) {
554
+ await knex.schema.createTable("agent_messages", (table) => {
555
+ table.uuid("id").primary().defaultTo(knex.fn.uuid());
556
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
557
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
558
+ for (const field of agentMessagesSchema.fields) {
559
+ const { type, name, default: defaultValue } = field;
560
+ if (!type || !name) {
561
+ continue;
562
+ }
563
+ mapType(table, type, sanitizeName(name), defaultValue);
564
+ }
565
+ });
566
+ }
551
567
  if (!await knex.schema.hasTable("roles")) {
552
568
  await knex.schema.createTable("roles", (table) => {
553
569
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
554
- table.date("createdAt").defaultTo(knex.fn.now());
555
- table.date("updatedAt").defaultTo(knex.fn.now());
570
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
571
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
556
572
  for (const field of rolesSchema.fields) {
557
- const { type, name, references, default: defaultValue } = field;
573
+ const { type, name, default: defaultValue } = field;
558
574
  if (!type || !name) {
559
575
  continue;
560
576
  }
561
- if (type === "reference") {
562
- if (!references) {
563
- throw new Error("Field with type reference must have a reference definition.");
564
- }
565
- table.uuid(name).references(references.field).inTable(references.table);
566
- return;
567
- }
568
577
  mapType(table, type, sanitizeName(name), defaultValue);
569
578
  }
570
579
  });
@@ -572,20 +581,13 @@ var up = async function(knex) {
572
581
  if (!await knex.schema.hasTable("eval_results")) {
573
582
  await knex.schema.createTable("eval_results", (table) => {
574
583
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
575
- table.date("createdAt").defaultTo(knex.fn.now());
576
- table.date("updatedAt").defaultTo(knex.fn.now());
584
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
585
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
577
586
  for (const field of evalResultsSchema.fields) {
578
- const { type, name, references, default: defaultValue } = field;
587
+ const { type, name, default: defaultValue } = field;
579
588
  if (!type || !name) {
580
589
  continue;
581
590
  }
582
- if (type === "reference") {
583
- if (!references) {
584
- throw new Error("Field with type reference must have a reference definition.");
585
- }
586
- table.uuid(name).references(references.field).inTable(references.table);
587
- return;
588
- }
589
591
  mapType(table, type, sanitizeName(name), defaultValue);
590
592
  }
591
593
  });
@@ -593,62 +595,41 @@ var up = async function(knex) {
593
595
  if (!await knex.schema.hasTable("statistics")) {
594
596
  await knex.schema.createTable("statistics", (table) => {
595
597
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
596
- table.date("createdAt").defaultTo(knex.fn.now());
597
- table.date("updatedAt").defaultTo(knex.fn.now());
598
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
599
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
598
600
  for (const field of statisticsSchema.fields) {
599
- const { type, name, references, default: defaultValue } = field;
601
+ const { type, name, default: defaultValue } = field;
600
602
  if (!type || !name) {
601
603
  continue;
602
604
  }
603
- if (type === "reference") {
604
- if (!references) {
605
- throw new Error("Field with type reference must have a reference definition.");
606
- }
607
- table.uuid(name).references(references.field).inTable(references.table);
608
- return;
609
- }
610
605
  mapType(table, type, sanitizeName(name), defaultValue);
611
606
  }
612
607
  });
613
608
  }
614
609
  if (!await knex.schema.hasTable("jobs")) {
615
610
  await knex.schema.createTable("jobs", (table) => {
616
- table.increments("id").primary();
617
- table.date("createdAt").defaultTo(knex.fn.now());
618
- table.date("updatedAt").defaultTo(knex.fn.now());
611
+ table.uuid("id").primary().defaultTo(knex.fn.uuid());
612
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
613
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
619
614
  for (const field of jobsSchema.fields) {
620
- const { type, name, references, default: defaultValue } = field;
615
+ const { type, name, default: defaultValue } = field;
621
616
  if (!type || !name) {
622
617
  continue;
623
618
  }
624
- if (type === "reference") {
625
- if (!references) {
626
- throw new Error("Field with type reference must have a reference definition.");
627
- }
628
- table.uuid(name).references(references.field).inTable(references.table);
629
- return;
630
- }
631
619
  mapType(table, type, sanitizeName(name), defaultValue);
632
620
  }
633
621
  });
634
622
  }
635
623
  if (!await knex.schema.hasTable("agents")) {
636
624
  await knex.schema.createTable("agents", (table) => {
637
- table.increments("id").primary();
638
- table.date("createdAt").defaultTo(knex.fn.now());
639
- table.date("updatedAt").defaultTo(knex.fn.now());
625
+ table.uuid("id").primary().defaultTo(knex.fn.uuid());
626
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
627
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
640
628
  for (const field of agentsSchema.fields) {
641
- const { type, name, references, default: defaultValue } = field;
629
+ const { type, name, default: defaultValue } = field;
642
630
  if (!type || !name) {
643
631
  continue;
644
632
  }
645
- if (type === "reference") {
646
- if (!references) {
647
- throw new Error("Field with type reference must have a reference definition.");
648
- }
649
- table.uuid(name).references(references.field).inTable(references.table);
650
- return;
651
- }
652
633
  mapType(table, type, sanitizeName(name), defaultValue);
653
634
  }
654
635
  });
@@ -664,8 +645,8 @@ var up = async function(knex) {
664
645
  if (!await knex.schema.hasTable("users")) {
665
646
  await knex.schema.createTable("users", (table) => {
666
647
  table.increments("id").primary();
667
- table.date("createdAt").defaultTo(knex.fn.now());
668
- table.date("updatedAt").defaultTo(knex.fn.now());
648
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
649
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
669
650
  table.string("name", 255);
670
651
  table.string("password", 255);
671
652
  table.string("email", 255);
@@ -673,20 +654,13 @@ var up = async function(knex) {
673
654
  table.text("image");
674
655
  for (const field of usersSchema.fields) {
675
656
  console.log("[EXULU] field", field);
676
- const { type, name, references, default: defaultValue } = field;
657
+ const { type, name, default: defaultValue } = field;
677
658
  if (name === "id" || name === "name" || name === "email" || name === "emailVerified" || name === "image") {
678
659
  continue;
679
660
  }
680
661
  if (!type || !name) {
681
662
  continue;
682
663
  }
683
- if (type === "reference") {
684
- if (!references) {
685
- throw new Error("Field with type reference must have a reference definition.");
686
- }
687
- table.uuid(name).references(references.field).inTable(references.table);
688
- return;
689
- }
690
664
  mapType(table, type, sanitizeName(name), defaultValue);
691
665
  }
692
666
  });
@@ -707,14 +681,6 @@ var up = async function(knex) {
707
681
  table.text("token_type");
708
682
  });
709
683
  }
710
- if (!await knex.schema.hasTable("sessions")) {
711
- await knex.schema.createTable("sessions", (table) => {
712
- table.increments("id").primary();
713
- table.integer("userId").notNullable();
714
- table.timestamp("expires", { useTz: true }).notNullable();
715
- table.string("sessionToken", 255).notNullable();
716
- });
717
- }
718
684
  };
719
685
  var execute = async () => {
720
686
  console.log("[EXULU] Initializing database.");
@@ -965,6 +931,35 @@ var ExuluEvalUtils = {
965
931
  };
966
932
 
967
933
  // src/registry/classes.ts
934
+ function sanitizeToolName(name) {
935
+ if (typeof name !== "string") return "";
936
+ let sanitized = name.replace(/[^a-zA-Z0-9_-]+/g, "_");
937
+ sanitized = sanitized.replace(/^_+|_+$/g, "");
938
+ if (sanitized.length > 128) {
939
+ sanitized = sanitized.substring(0, 128);
940
+ }
941
+ return sanitized;
942
+ }
943
+ var convertToolsArrayToObject = (tools) => {
944
+ if (!tools) return {};
945
+ const sanitizedTools = tools ? tools.map((tool2) => ({
946
+ ...tool2,
947
+ name: sanitizeToolName(tool2.name)
948
+ })) : [];
949
+ const askForConfirmation = {
950
+ description: "Ask the user for confirmation.",
951
+ parameters: z.object({
952
+ message: z.string().describe("The message to ask for confirmation.")
953
+ })
954
+ };
955
+ return {
956
+ ...sanitizedTools?.reduce(
957
+ (prev, cur) => ({ ...prev, [cur.name]: cur.tool }),
958
+ {}
959
+ ),
960
+ askForConfirmation
961
+ };
962
+ };
968
963
  function generateSlug(name) {
969
964
  const normalized = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
970
965
  const lowercase = normalized.toLowerCase();
@@ -991,54 +986,119 @@ var ExuluAgent = class {
991
986
  name;
992
987
  description = "";
993
988
  slug = "";
989
+ type;
994
990
  streaming = false;
995
991
  rateLimit;
996
992
  config;
997
993
  // private memory: Memory | undefined; // TODO do own implementation
998
- tools;
999
994
  evals;
1000
995
  model;
1001
996
  capabilities;
1002
- constructor({ id, name, description, config, rateLimit, capabilities, tools, evals }) {
997
+ constructor({ id, name, description, config, rateLimit, capabilities, type, evals }) {
1003
998
  this.id = id;
1004
999
  this.name = name;
1005
1000
  this.evals = evals;
1006
1001
  this.description = description;
1007
1002
  this.rateLimit = rateLimit;
1008
- this.tools = tools;
1009
1003
  this.config = config;
1010
- this.capabilities = capabilities;
1004
+ this.type = type;
1005
+ this.capabilities = capabilities || {
1006
+ images: [],
1007
+ files: [],
1008
+ audio: [],
1009
+ video: []
1010
+ };
1011
1011
  this.slug = `/agents/${generateSlug(this.name)}/run`;
1012
- this.model = this.config.model;
1012
+ this.model = this.config?.model;
1013
1013
  }
1014
- generate = async ({ prompt, stream }) => {
1014
+ // Exports the agent as a tool that can be used by another agent
1015
+ // todo test this
1016
+ tool = () => {
1017
+ return new ExuluTool({
1018
+ id: this.id,
1019
+ name: `${this.name} agent`,
1020
+ type: "agent",
1021
+ inputSchema: z.object({
1022
+ prompt: z.string()
1023
+ }),
1024
+ description: `A function that calls an AI agent named: ${this.name}. The agent does the following: ${this.description}.`,
1025
+ execute: async ({ prompt }) => {
1026
+ return await this.generateSync({
1027
+ prompt,
1028
+ statistics: {
1029
+ label: "",
1030
+ trigger: "tool"
1031
+ }
1032
+ });
1033
+ }
1034
+ });
1035
+ };
1036
+ generateSync = async ({ messages, prompt, tools, statistics }) => {
1015
1037
  if (!this.model) {
1016
1038
  throw new Error("Model is required for streaming.");
1017
1039
  }
1018
- if (this.config.outputSchema) {
1019
- if (stream) {
1020
- }
1021
- const { object } = await generateObject({
1022
- model: this.model,
1023
- schema: this.config.outputSchema,
1024
- prompt
1025
- });
1026
- return object;
1040
+ if (!this.config) {
1041
+ throw new Error("Config is required for generating.");
1027
1042
  }
1028
- if (stream) {
1029
- const result = streamText({
1030
- model: this.model,
1031
- prompt
1032
- });
1033
- const text2 = await result.text;
1034
- return text2;
1043
+ if (prompt && messages) {
1044
+ throw new Error("Prompt and messages cannot be provided at the same time.");
1035
1045
  }
1036
1046
  const { text } = await generateText({
1037
1047
  model: this.model,
1038
- prompt
1048
+ 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.",
1049
+ messages,
1050
+ prompt,
1051
+ maxRetries: 2,
1052
+ tools: convertToolsArrayToObject(tools),
1053
+ maxSteps: 5
1039
1054
  });
1055
+ if (statistics) {
1056
+ await updateStatistic({
1057
+ name: "count",
1058
+ label: statistics.label,
1059
+ type: STATISTICS_TYPE_ENUM.AGENT_RUN,
1060
+ trigger: statistics.trigger,
1061
+ count: 1
1062
+ });
1063
+ }
1040
1064
  return text;
1041
1065
  };
1066
+ generateStream = ({ messages, prompt, tools, statistics }) => {
1067
+ if (!this.model) {
1068
+ throw new Error("Model is required for streaming.");
1069
+ }
1070
+ if (!this.config) {
1071
+ throw new Error("Config is required for generating.");
1072
+ }
1073
+ if (prompt && messages) {
1074
+ throw new Error("Prompt and messages cannot be provided at the same time.");
1075
+ }
1076
+ return streamText({
1077
+ model: this.model,
1078
+ messages,
1079
+ prompt,
1080
+ 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.",
1081
+ maxRetries: 2,
1082
+ tools: convertToolsArrayToObject(tools),
1083
+ maxSteps: 5,
1084
+ onError: (error) => console.error("[EXULU] chat stream error.", error),
1085
+ onFinish: async ({ response, usage }) => {
1086
+ console.info(
1087
+ "[EXULU] chat stream finished.",
1088
+ usage
1089
+ );
1090
+ if (statistics) {
1091
+ await updateStatistic({
1092
+ name: "count",
1093
+ label: statistics.label,
1094
+ type: STATISTICS_TYPE_ENUM.AGENT_RUN,
1095
+ trigger: statistics.trigger,
1096
+ count: 1
1097
+ });
1098
+ }
1099
+ }
1100
+ });
1101
+ };
1042
1102
  };
1043
1103
  var ExuluEmbedder = class {
1044
1104
  id;
@@ -1061,6 +1121,13 @@ var ExuluEmbedder = class {
1061
1121
  }
1062
1122
  async generateFromQuery(query, statistics) {
1063
1123
  if (statistics) {
1124
+ await updateStatistic({
1125
+ name: "count",
1126
+ label: statistics.label,
1127
+ type: STATISTICS_TYPE_ENUM.EMBEDDER_GENERATE,
1128
+ trigger: statistics.trigger,
1129
+ count: 1
1130
+ });
1064
1131
  }
1065
1132
  return await this.generateEmbeddings({
1066
1133
  item: {
@@ -1074,16 +1141,22 @@ var ExuluEmbedder = class {
1074
1141
  }
1075
1142
  async generateFromDocument(input, statistics) {
1076
1143
  if (statistics) {
1144
+ await updateStatistic({
1145
+ name: "count",
1146
+ label: statistics.label,
1147
+ type: STATISTICS_TYPE_ENUM.EMBEDDER_GENERATE,
1148
+ trigger: statistics.trigger,
1149
+ count: 1
1150
+ });
1077
1151
  }
1078
1152
  if (!this.chunker) {
1079
1153
  throw new Error("Chunker not found for embedder " + this.name);
1080
1154
  }
1081
- console.log("generating chunks");
1082
1155
  if (!input.id) {
1083
1156
  throw new Error("Item id is required for generating embeddings.");
1084
1157
  }
1085
1158
  const output = await this.chunker(input, this.maxChunkSize);
1086
- console.log("generating embeddings");
1159
+ console.log("[EXULU] Generating embeddings.");
1087
1160
  return await this.generateEmbeddings(output);
1088
1161
  }
1089
1162
  };
@@ -1263,9 +1336,8 @@ var ExuluEval = class {
1263
1336
  if (!data.prompt) {
1264
1337
  throw new Error("Prompt is required for running an agent.");
1265
1338
  }
1266
- const result = await runner.agent.generate({
1267
- prompt: data.prompt,
1268
- stream: false
1339
+ const result = await runner.agent.generateSync({
1340
+ prompt: data.prompt
1269
1341
  });
1270
1342
  data.result = result;
1271
1343
  }
@@ -1360,18 +1432,18 @@ var ExuluTool = class {
1360
1432
  id;
1361
1433
  name;
1362
1434
  description;
1363
- parameters;
1435
+ inputSchema;
1364
1436
  type;
1365
1437
  tool;
1366
- constructor({ id, name, description, parameters, type, execute: execute2 }) {
1438
+ constructor({ id, name, description, inputSchema, type, execute: execute2 }) {
1367
1439
  this.id = id;
1368
1440
  this.name = name;
1369
1441
  this.description = description;
1370
- this.parameters = parameters;
1442
+ this.inputSchema = inputSchema;
1371
1443
  this.type = type;
1372
1444
  this.tool = tool({
1373
1445
  description,
1374
- parameters,
1446
+ parameters: inputSchema || z.object({}),
1375
1447
  execute: execute2
1376
1448
  });
1377
1449
  }
@@ -1427,10 +1499,9 @@ var ExuluContext = class {
1427
1499
  }
1428
1500
  const { db: db2 } = await postgresClient();
1429
1501
  Object.keys(item).forEach((key) => {
1430
- if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert") {
1502
+ if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert" || key === "archived") {
1431
1503
  return;
1432
1504
  }
1433
- console.log("this.fields", this.fields);
1434
1505
  const field = this.fields.find((field2) => field2.name === key);
1435
1506
  if (!field) {
1436
1507
  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");
@@ -1475,6 +1546,9 @@ var ExuluContext = class {
1475
1546
  chunk_index: chunk.index,
1476
1547
  embedding: pgvector2.toSql(chunk.vector)
1477
1548
  })));
1549
+ await db2.from(this.getTableName()).where({ id }).update({
1550
+ embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
1551
+ }).returning("id");
1478
1552
  }
1479
1553
  return {
1480
1554
  id: result[0].id,
@@ -1504,10 +1578,9 @@ var ExuluContext = class {
1504
1578
  }
1505
1579
  }
1506
1580
  Object.keys(item).forEach((key) => {
1507
- if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert") {
1581
+ if (key === "name" || key === "description" || key === "external_id" || key === "tags" || key === "source" || key === "textLength" || key === "upsert" || key === "archived") {
1508
1582
  return;
1509
1583
  }
1510
- console.log("this.fields", this.fields);
1511
1584
  const field = this.fields.find((field2) => field2.name === key);
1512
1585
  if (!field) {
1513
1586
  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");
@@ -1549,13 +1622,16 @@ var ExuluContext = class {
1549
1622
  if (!exists) {
1550
1623
  await this.createChunksTable();
1551
1624
  }
1552
- console.log("inserting chunks");
1625
+ console.log("[EXULU] Inserting chunks.");
1553
1626
  await db2.from(this.getChunksTableName()).insert(chunks.map((chunk) => ({
1554
1627
  source,
1555
1628
  content: chunk.content,
1556
1629
  chunk_index: chunk.index,
1557
1630
  embedding: pgvector2.toSql(chunk.vector)
1558
1631
  })));
1632
+ await db2.from(this.getTableName()).where({ id: result[0].id }).update({
1633
+ embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
1634
+ }).returning("id");
1559
1635
  }
1560
1636
  return {
1561
1637
  id: result[0].id,
@@ -1565,6 +1641,8 @@ var ExuluContext = class {
1565
1641
  getItems = async ({
1566
1642
  statistics,
1567
1643
  limit,
1644
+ sort,
1645
+ order,
1568
1646
  page,
1569
1647
  name,
1570
1648
  archived,
@@ -1585,6 +1663,9 @@ var ExuluContext = class {
1585
1663
  const columns = await db2(mainTable).columnInfo();
1586
1664
  const totalQuery = db2.count("* as count").from(mainTable).first();
1587
1665
  const itemsQuery = db2.select(Object.keys(columns).map((column) => mainTable + "." + column)).from(mainTable).offset(offset).limit(limit);
1666
+ if (sort) {
1667
+ itemsQuery.orderBy(sort, order === "desc" ? "desc" : "asc");
1668
+ }
1588
1669
  if (typeof name === "string") {
1589
1670
  itemsQuery.whereILike("name", `%${name}%`);
1590
1671
  totalQuery.whereILike("name", `%${name}%`);
@@ -1628,7 +1709,7 @@ var ExuluContext = class {
1628
1709
  }
1629
1710
  itemsQuery.limit(limit * 5);
1630
1711
  if (statistics) {
1631
- updateStatistic({
1712
+ await updateStatistic({
1632
1713
  name: "count",
1633
1714
  label: statistics.label,
1634
1715
  type: STATISTICS_TYPE_ENUM.CONTEXT_RETRIEVE,
@@ -1761,6 +1842,7 @@ var ExuluContext = class {
1761
1842
  table.text("external_id");
1762
1843
  table.integer("textLength");
1763
1844
  table.text("source");
1845
+ table.timestamp("embeddings_updated_at");
1764
1846
  for (const field of this.fields) {
1765
1847
  const { type, name } = field;
1766
1848
  if (!type || !name) {
@@ -1790,15 +1872,15 @@ var ExuluContext = class {
1790
1872
  id: this.id,
1791
1873
  name: `${this.name} context`,
1792
1874
  type: "context",
1793
- parameters: z.object({
1875
+ inputSchema: z.object({
1794
1876
  query: z.string()
1795
1877
  }),
1796
1878
  description: `Gets information from the context called: ${this.name}. The context description is: ${this.description}.`,
1797
- execute: async ({ context }) => {
1879
+ execute: async ({ query }) => {
1798
1880
  return await this.getItems({
1799
1881
  page: 1,
1800
1882
  limit: 10,
1801
- query: context.query,
1883
+ query,
1802
1884
  statistics: {
1803
1885
  label: this.name,
1804
1886
  trigger: "agent"
@@ -1847,7 +1929,32 @@ var ExuluSource = class {
1847
1929
  }
1848
1930
  };
1849
1931
  var updateStatistic = async (statistic) => {
1850
- return;
1932
+ const currentDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1933
+ const { db: db2 } = await postgresClient();
1934
+ const existing = await db2.from("statistics").where({
1935
+ name: statistic.name,
1936
+ label: statistic.label,
1937
+ type: statistic.type,
1938
+ createdAt: currentDate
1939
+ }).first();
1940
+ if (!existing) {
1941
+ await db2.from("statistics").insert({
1942
+ name: statistic.name,
1943
+ label: statistic.label,
1944
+ type: statistic.type,
1945
+ total: statistic.count ?? 1,
1946
+ createdAt: currentDate
1947
+ });
1948
+ } else {
1949
+ await db2.from("statistics").update({
1950
+ total: db2.raw("total + ?", [statistic.count ?? 1])
1951
+ }).where({
1952
+ name: statistic.name,
1953
+ label: statistic.label,
1954
+ type: statistic.type,
1955
+ createdAt: currentDate
1956
+ });
1957
+ }
1851
1958
  };
1852
1959
 
1853
1960
  // src/registry/index.ts
@@ -1920,7 +2027,6 @@ var authentication = async ({
1920
2027
  internalkey,
1921
2028
  db: db2
1922
2029
  }) => {
1923
- console.log("[EXULU] apikey", apikey);
1924
2030
  if (internalkey) {
1925
2031
  if (!process.env.INTERNAL_SECRET) {
1926
2032
  return {
@@ -1948,7 +2054,7 @@ var authentication = async ({
1948
2054
  }
1949
2055
  if (authtoken) {
1950
2056
  try {
1951
- console.log("authtoken", authtoken);
2057
+ console.log("[EXULU] authtoken", authtoken);
1952
2058
  if (!authtoken?.email) {
1953
2059
  return {
1954
2060
  error: true,
@@ -1957,7 +2063,6 @@ var authentication = async ({
1957
2063
  };
1958
2064
  }
1959
2065
  const user = await db2.from("users").select("*").where("email", authtoken?.email).first();
1960
- console.log("user", user);
1961
2066
  if (!user) {
1962
2067
  return {
1963
2068
  error: true,
@@ -2006,15 +2111,11 @@ var authentication = async ({
2006
2111
  code: 401
2007
2112
  };
2008
2113
  }
2009
- console.log("[EXULU] request_key_name", request_key_name);
2010
- console.log("[EXULU] request_key_compare_value", request_key_compare_value);
2011
2114
  const filtered = users.filter(({ apikey: apikey2, id }) => apikey2.includes(request_key_name));
2012
2115
  for (const user of filtered) {
2013
2116
  const user_key_last_slash_index = user.apikey.lastIndexOf("/");
2014
2117
  const user_key_compare_value = user.apikey.substring(0, user_key_last_slash_index);
2015
- console.log("[EXULU] user_key_compare_value", user_key_compare_value);
2016
2118
  const isMatch = await bcrypt2.compare(request_key_compare_value, user_key_compare_value);
2017
- console.log("[EXULU] isMatch", isMatch);
2018
2119
  if (isMatch) {
2019
2120
  await db2.from("users").where({ id: user.id }).update({
2020
2121
  last_used: /* @__PURE__ */ new Date()
@@ -2047,9 +2148,7 @@ var requestValidators = {
2047
2148
  const { db: db2 } = await postgresClient();
2048
2149
  let authtoken = null;
2049
2150
  if (typeof apikey !== "string") {
2050
- const secret = process.env.NEXTAUTH_SECRET;
2051
- authtoken = await getToken(req.headers["authorization"] ?? "");
2052
- console.log("[EXULU] authtoken", authtoken);
2151
+ authtoken = await getToken((req.headers["authorization"] || req.headers["x-api-key"]) ?? "");
2053
2152
  }
2054
2153
  return await authentication({
2055
2154
  authtoken,
@@ -2151,7 +2250,6 @@ var requestValidators = {
2151
2250
  };
2152
2251
  },
2153
2252
  agents: (req) => {
2154
- console.log("[EXULU] validating request body and headers.", req.body);
2155
2253
  const contentType = req.headers["content-type"] || "";
2156
2254
  if (!contentType.includes("application/json")) {
2157
2255
  return {
@@ -2243,6 +2341,42 @@ import "reflect-metadata";
2243
2341
  // src/registry/utils/graphql.ts
2244
2342
  import { makeExecutableSchema } from "@graphql-tools/schema";
2245
2343
  import GraphQLJSON from "graphql-type-json";
2344
+ import { GraphQLScalarType, Kind } from "graphql";
2345
+ import CryptoJS from "crypto-js";
2346
+ var GraphQLDate = new GraphQLScalarType({
2347
+ name: "Date",
2348
+ description: "Date custom scalar type",
2349
+ serialize(value) {
2350
+ if (value instanceof Date) {
2351
+ return value.toISOString();
2352
+ }
2353
+ if (typeof value === "number") {
2354
+ return new Date(value).toISOString();
2355
+ }
2356
+ if (typeof value === "string") {
2357
+ return new Date(value).toISOString();
2358
+ }
2359
+ return value;
2360
+ },
2361
+ parseValue(value) {
2362
+ if (typeof value === "string") {
2363
+ return new Date(value);
2364
+ }
2365
+ if (typeof value === "number") {
2366
+ return new Date(value);
2367
+ }
2368
+ return value;
2369
+ },
2370
+ parseLiteral(ast) {
2371
+ if (ast.kind === Kind.STRING) {
2372
+ return new Date(ast.value);
2373
+ }
2374
+ if (ast.kind === Kind.INT) {
2375
+ return new Date(parseInt(ast.value, 10));
2376
+ }
2377
+ return null;
2378
+ }
2379
+ });
2246
2380
  var map = (field) => {
2247
2381
  let type;
2248
2382
  switch (field.type) {
@@ -2262,7 +2396,7 @@ var map = (field) => {
2262
2396
  type = "JSON";
2263
2397
  break;
2264
2398
  case "date":
2265
- type = "String";
2399
+ type = "Date";
2266
2400
  break;
2267
2401
  default:
2268
2402
  type = "String";
@@ -2280,8 +2414,8 @@ function createTypeDefs(table) {
2280
2414
  type ${table.name.singular} {
2281
2415
  ${fields.join("\n")}
2282
2416
  id: ID!
2283
- createdAt: String!
2284
- updatedAt: String!
2417
+ createdAt: Date!
2418
+ updatedAt: Date!
2285
2419
  }
2286
2420
  `;
2287
2421
  const inputDef = `
@@ -2307,6 +2441,11 @@ input FilterOperatorString {
2307
2441
  contains: String
2308
2442
  }
2309
2443
 
2444
+ input FilterOperatorDate {
2445
+ lte: Date
2446
+ gte: Date
2447
+ }
2448
+
2310
2449
  input FilterOperatorFloat {
2311
2450
  eq: Float
2312
2451
  ne: Float
@@ -2359,17 +2498,19 @@ function createMutations(table) {
2359
2498
  [`${tableNamePlural}CreateOne`]: async (_, args, context, info) => {
2360
2499
  const { db: db2 } = context;
2361
2500
  const requestedFields = getRequestedFields(info);
2501
+ let { input } = args;
2502
+ input = encryptSensitiveFields(input);
2362
2503
  const results = await db2(tableNamePlural).insert({
2363
- ...args.input,
2504
+ ...input,
2364
2505
  createdAt: /* @__PURE__ */ new Date(),
2365
2506
  updatedAt: /* @__PURE__ */ new Date()
2366
2507
  }).returning(requestedFields);
2367
- console.log("requestedFields", requestedFields);
2368
2508
  return results[0];
2369
2509
  },
2370
2510
  [`${tableNamePlural}UpdateOne`]: async (_, args, context, info) => {
2371
2511
  const { db: db2 } = context;
2372
- const { where, input } = args;
2512
+ let { where, input } = args;
2513
+ input = encryptSensitiveFields(input);
2373
2514
  await db2(tableNamePlural).where(where).update({
2374
2515
  ...input,
2375
2516
  updatedAt: /* @__PURE__ */ new Date()
@@ -2379,7 +2520,8 @@ function createMutations(table) {
2379
2520
  return result;
2380
2521
  },
2381
2522
  [`${tableNamePlural}UpdateOneById`]: async (_, args, context, info) => {
2382
- const { id, input } = args;
2523
+ let { id, input } = args;
2524
+ input = encryptSensitiveFields(input);
2383
2525
  const { db: db2 } = context;
2384
2526
  await db2(tableNamePlural).where({ id }).update({
2385
2527
  ...input,
@@ -2457,7 +2599,6 @@ function createQueries(table) {
2457
2599
  [`${tableNamePlural}Pagination`]: async (_, args, context, info) => {
2458
2600
  const { limit = 10, page = 0, filters = [], sort } = args;
2459
2601
  const { db: db2 } = context;
2460
- console.log("page", page);
2461
2602
  let baseQuery = db2(tableNamePlural);
2462
2603
  baseQuery = applyFilters(baseQuery, filters);
2463
2604
  const [{ count }] = await baseQuery.clone().count("* as count");
@@ -2473,8 +2614,6 @@ function createQueries(table) {
2473
2614
  dataQuery = dataQuery.offset((page - 1) * limit);
2474
2615
  }
2475
2616
  const items = await dataQuery.select(requestedFields).limit(limit);
2476
- console.log("items", items);
2477
- console.log("query", dataQuery.toQuery());
2478
2617
  return {
2479
2618
  pageInfo: {
2480
2619
  pageCount,
@@ -2485,12 +2624,44 @@ function createQueries(table) {
2485
2624
  },
2486
2625
  items
2487
2626
  };
2488
- }
2627
+ },
2628
+ // Add jobStatistics query for jobs table
2629
+ ...tableNamePlural === "jobs" ? {
2630
+ jobStatistics: async (_, args, context, info) => {
2631
+ const { user, agent, from, to } = args;
2632
+ const { db: db2 } = context;
2633
+ let query = db2("jobs");
2634
+ if (user) {
2635
+ query = query.where("user", user);
2636
+ }
2637
+ if (agent) {
2638
+ query = query.where("agent", agent);
2639
+ }
2640
+ if (from) {
2641
+ query = query.where("createdAt", ">=", from);
2642
+ }
2643
+ if (to) {
2644
+ query = query.where("createdAt", "<=", to);
2645
+ }
2646
+ const completedQuery = query.clone().where("status", "completed");
2647
+ const [{ completedCount }] = await completedQuery.count("* as completedCount");
2648
+ const failedQuery = query.clone().where("status", "failed");
2649
+ const [{ failedCount }] = await failedQuery.count("* as failedCount");
2650
+ const durationQuery = query.clone().where("status", "completed").whereNotNull("duration").select(db2.raw('AVG("duration") as averageDuration'));
2651
+ const [{ averageDuration }] = await durationQuery;
2652
+ return {
2653
+ completedCount: Number(completedCount),
2654
+ failedCount: Number(failedCount),
2655
+ averageDuration: averageDuration ? Number(averageDuration) : 0
2656
+ };
2657
+ }
2658
+ } : {}
2489
2659
  };
2490
2660
  }
2491
2661
  function createSDL(tables) {
2492
2662
  let typeDefs = `
2493
2663
  scalar JSON
2664
+ scalar Date
2494
2665
 
2495
2666
  type Query {
2496
2667
  `;
@@ -2498,7 +2669,7 @@ function createSDL(tables) {
2498
2669
  type Mutation {
2499
2670
  `;
2500
2671
  let modelDefs = "";
2501
- const resolvers = { JSON: GraphQLJSON, Query: {}, Mutation: {} };
2672
+ const resolvers = { JSON: GraphQLJSON, Date: GraphQLDate, Query: {}, Mutation: {} };
2502
2673
  for (const table of tables) {
2503
2674
  const tableNamePlural = table.name.plural.toLowerCase();
2504
2675
  const tableNameSingular = table.name.singular.toLowerCase();
@@ -2507,6 +2678,7 @@ function createSDL(tables) {
2507
2678
  ${tableNameSingular}ById(id: ID!): ${tableNameSingular}
2508
2679
  ${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
2509
2680
  ${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
2681
+ ${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
2510
2682
  `;
2511
2683
  mutationDefs += `
2512
2684
  ${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!): ${tableNameSingular}
@@ -2531,6 +2703,15 @@ type PageInfo {
2531
2703
  hasNextPage: Boolean!
2532
2704
  }
2533
2705
  `;
2706
+ if (tableNamePlural === "jobs") {
2707
+ modelDefs += `
2708
+ type JobStatistics {
2709
+ completedCount: Int!
2710
+ failedCount: Int!
2711
+ averageDuration: Float!
2712
+ }
2713
+ `;
2714
+ }
2534
2715
  Object.assign(resolvers.Query, createQueries(table));
2535
2716
  Object.assign(resolvers.Mutation, createMutations(table));
2536
2717
  }
@@ -2567,6 +2748,15 @@ type PageInfo {
2567
2748
  console.log("\n");
2568
2749
  return schema;
2569
2750
  }
2751
+ var sensitiveFields = ["anthropic_token"];
2752
+ var encryptSensitiveFields = (input) => {
2753
+ sensitiveFields.forEach((field) => {
2754
+ if (input[field]) {
2755
+ input[field] = CryptoJS.AES.encrypt(input[field], process.env.NEXTAUTH_SECRET).toString();
2756
+ }
2757
+ });
2758
+ return input;
2759
+ };
2570
2760
 
2571
2761
  // src/registry/routes.ts
2572
2762
  import { expressMiddleware } from "@as-integrations/express5";
@@ -2965,6 +3155,34 @@ var createUppyRoutes = async (app) => {
2965
3155
  // src/registry/routes.ts
2966
3156
  import { InMemoryLRUCache } from "@apollo/utils.keyvaluecache";
2967
3157
  import bodyParser from "body-parser";
3158
+ import CryptoJS2 from "crypto-js";
3159
+
3160
+ // src/registry/utils/claude-messages.ts
3161
+ var CLAUDE_MESSAGES = {
3162
+ authentication_error: `
3163
+ \x1B[41m -- Authentication error please check your IMP token and try again. --
3164
+ \x1B[0m`,
3165
+ missing_body: `
3166
+ \x1B[41m -- Missing body Anthropic response. --
3167
+ \x1B[0m`,
3168
+ missing_nextauth_secret: `
3169
+ \x1B[41m -- Missing NEXTAUTH_SECRET in environment variables on the server. --
3170
+ \x1B[0m`,
3171
+ not_enabled: `
3172
+ \x1B[31m
3173
+ \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
3174
+ \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
3175
+ \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
3176
+ \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
3177
+ \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
3178
+ \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
3179
+ Intelligence Management Platform
3180
+ \x1B[0m
3181
+ \x1B[41m -- Your account has not been enabled to use Claude Code, please contact your admin or enable Claude Code in the user settings. --
3182
+ \x1B[0m`
3183
+ };
3184
+
3185
+ // src/registry/routes.ts
2968
3186
  var REQUEST_SIZE_LIMIT = "50mb";
2969
3187
  var global_queues = {
2970
3188
  logs_cleaner: "logs-cleaner"
@@ -3001,7 +3219,7 @@ var createRecurringJobs = async () => {
3001
3219
  console.table(recurringJobSchedulersLogs);
3002
3220
  return queue;
3003
3221
  };
3004
- var createExpressRoutes = async (app, agents, embedders, tools, workflows, contexts) => {
3222
+ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
3005
3223
  const routeLogs = [];
3006
3224
  var corsOptions = {
3007
3225
  origin: "*",
@@ -3021,6 +3239,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3021
3239
  \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
3022
3240
  \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
3023
3241
  \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
3242
+ Intelligence Management Platform
3024
3243
 
3025
3244
  `);
3026
3245
  console.log("Agents:");
@@ -3069,7 +3288,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3069
3288
  } else {
3070
3289
  console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
3071
3290
  }
3072
- const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, threadsSchema, messagesSchema]);
3291
+ const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, agentSessionsSchema, agentMessagesSchema]);
3073
3292
  console.log("[EXULU] graphql server");
3074
3293
  const server = new ApolloServer({
3075
3294
  cache: new InMemoryLRUCache(),
@@ -3097,10 +3316,52 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3097
3316
  }
3098
3317
  })
3099
3318
  );
3100
- app.get(`/agents`, async (req, res) => {
3319
+ app.get(`/providers`, async (req, res) => {
3320
+ const authenticationResult = await requestValidators.authenticate(req);
3321
+ if (!authenticationResult.user?.id) {
3322
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3323
+ return;
3324
+ }
3101
3325
  res.status(200).json(agents);
3102
3326
  });
3327
+ app.get(`/agents`, async (req, res) => {
3328
+ const authenticationResult = await requestValidators.authenticate(req);
3329
+ if (!authenticationResult.user?.id) {
3330
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3331
+ return;
3332
+ }
3333
+ const { db: db2 } = await postgresClient();
3334
+ const agentsFromDb = await db2.from("agents").select("*");
3335
+ res.status(200).json(agentsFromDb.map((agent) => {
3336
+ const backend = agents.find((a) => a.id === agent.backend);
3337
+ if (!backend) {
3338
+ return null;
3339
+ }
3340
+ return {
3341
+ name: agent.name,
3342
+ id: agent.id,
3343
+ description: agent.description,
3344
+ provider: backend?.model?.provider,
3345
+ model: backend?.model?.modelId,
3346
+ active: agent.active,
3347
+ public: agent.public,
3348
+ type: agent.type,
3349
+ slug: backend?.slug,
3350
+ rateLimit: backend?.rateLimit,
3351
+ streaming: backend?.streaming,
3352
+ capabilities: backend?.capabilities,
3353
+ // todo add contexts
3354
+ availableTools: tools,
3355
+ enabledTools: agent.tools
3356
+ };
3357
+ }).filter(Boolean));
3358
+ });
3103
3359
  app.get(`/agents/:id`, async (req, res) => {
3360
+ const authenticationResult = await requestValidators.authenticate(req);
3361
+ if (!authenticationResult.user?.id) {
3362
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3363
+ return;
3364
+ }
3104
3365
  const { db: db2 } = await postgresClient();
3105
3366
  const id = req.params.id;
3106
3367
  if (!id) {
@@ -3123,6 +3384,8 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3123
3384
  name: agent.name,
3124
3385
  id: agent.id,
3125
3386
  description: agent.description,
3387
+ provider: backend?.model?.provider,
3388
+ model: backend?.model?.modelId,
3126
3389
  active: agent.active,
3127
3390
  public: agent.public,
3128
3391
  type: agent.type,
@@ -3131,7 +3394,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3131
3394
  streaming: backend?.streaming,
3132
3395
  capabilities: backend?.capabilities,
3133
3396
  // todo add contexts
3134
- availableTools: backend?.tools,
3397
+ availableTools: tools,
3135
3398
  enabledTools: agent.tools
3136
3399
  }
3137
3400
  });
@@ -3142,8 +3405,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3142
3405
  name: tool2.name,
3143
3406
  description: tool2.description,
3144
3407
  type: tool2.type || "tool",
3145
- inputSchema: tool2.inputSchema ? zerialize(tool2.inputSchema) : null,
3146
- outputSchema: tool2.outputSchema ? zerialize(tool2.outputSchema) : null
3408
+ inputSchema: tool2.inputSchema ? zerialize(tool2.inputSchema) : null
3147
3409
  })));
3148
3410
  });
3149
3411
  app.get("/tools/:id", async (req, res) => {
@@ -3261,6 +3523,14 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3261
3523
  });
3262
3524
  return;
3263
3525
  }
3526
+ const itemsTableExists = await context.tableExists();
3527
+ if (!itemsTableExists) {
3528
+ await context.createItemsTable();
3529
+ }
3530
+ const chunksTableExists = await db2.schema.hasTable(context.getChunksTableName());
3531
+ if (!chunksTableExists) {
3532
+ await context.createChunksTable();
3533
+ }
3264
3534
  const item = await db2.from(context.getTableName()).where({ id: req.params.id }).select("*").first();
3265
3535
  if (!item) {
3266
3536
  res.status(404).json({
@@ -3382,6 +3652,20 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3382
3652
  }
3383
3653
  let limit = req.query.limit ? parseInt(req.query.limit) : 10;
3384
3654
  let page = req.query.page ? parseInt(req.query.page) : 1;
3655
+ let sort = req.query.sort ? req.query.sort : "created_at";
3656
+ let order = req.query.order ? req.query.order : "desc";
3657
+ if (sort && !["created_at", "embeddings_updated_at"].includes(sort)) {
3658
+ res.status(400).json({
3659
+ message: "Invalid sort field, must be one of: createdAt, embeddings_updated_at"
3660
+ });
3661
+ return;
3662
+ }
3663
+ if (order && !["desc", "asc"].includes(order)) {
3664
+ res.status(400).json({
3665
+ message: "Invalid order, must be one of: desc, asc"
3666
+ });
3667
+ return;
3668
+ }
3385
3669
  const authenticationResult = await requestValidators.authenticate(req);
3386
3670
  if (!authenticationResult.user?.id) {
3387
3671
  res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
@@ -3405,6 +3689,8 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3405
3689
  return;
3406
3690
  }
3407
3691
  const result = await context.getItems({
3692
+ sort,
3693
+ order,
3408
3694
  page,
3409
3695
  limit,
3410
3696
  archived: req.query.archived === "true",
@@ -3484,6 +3770,18 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3484
3770
  message: "Embedding deleted."
3485
3771
  });
3486
3772
  });
3773
+ app.get("/ping", async (req, res) => {
3774
+ const authenticationResult = await requestValidators.authenticate(req);
3775
+ if (!authenticationResult.user?.id) {
3776
+ res.status(200).json({
3777
+ authenticated: false
3778
+ });
3779
+ return;
3780
+ }
3781
+ res.status(200).json({
3782
+ authenticated: true
3783
+ });
3784
+ });
3487
3785
  console.log("[EXULU] statistics timeseries");
3488
3786
  app.post("/statistics/timeseries", async (req, res) => {
3489
3787
  const authenticationResult = await requestValidators.authenticate(req);
@@ -3574,7 +3872,6 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3574
3872
  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) => ({
3575
3873
  jobs: rows
3576
3874
  }));
3577
- console.log({ response });
3578
3875
  let jobs = [];
3579
3876
  if (response[0]) {
3580
3877
  jobs = response[0].jobs.map((job) => ({
@@ -3624,6 +3921,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3624
3921
  slug: "/contexts/" + context.id,
3625
3922
  active: context.active,
3626
3923
  fields: context.fields,
3924
+ configuration: context.configuration,
3627
3925
  sources: context.sources.get().map((source) => ({
3628
3926
  id: source.id,
3629
3927
  name: source.name,
@@ -3671,13 +3969,11 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3671
3969
  });
3672
3970
  console.log("[EXULU] contexts get list");
3673
3971
  app.get(`/contexts`, async (req, res) => {
3674
- console.log("contexts!!");
3675
3972
  const authenticationResult = await requestValidators.authenticate(req);
3676
3973
  if (!authenticationResult.user?.id) {
3677
3974
  res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3678
3975
  return;
3679
3976
  }
3680
- console.log("contexts", contexts?.length);
3681
3977
  res.status(200).json(contexts.map((context) => ({
3682
3978
  id: context.id,
3683
3979
  name: context.name,
@@ -3820,47 +4116,28 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3820
4116
  res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3821
4117
  return;
3822
4118
  }
4119
+ console.log("[EXULU] agent tools", agentInstance.tools);
4120
+ const enabledTools = agentInstance.tools.map((tool2) => tools.find(({ id }) => id === tool2)).filter(Boolean);
4121
+ console.log("[EXULU] enabled tools", enabledTools);
3823
4122
  if (!!stream) {
3824
- const chatClient = await agent.chat(agentInstance.id);
3825
- if (!chatClient) {
3826
- res.status(500).json({
3827
- message: "Agent instantiation not successful."
3828
- });
3829
- return;
3830
- }
3831
- const { textStream } = await chatClient.stream(req.body.messages, {
3832
- threadId: `${req.body.threadId}`,
3833
- // conversation id
3834
- resourceId: `${req.body.resourceId}`,
3835
- // user id
3836
- ...agent.outputSchema && { output: agent.outputSchema },
3837
- maxRetries: 2,
3838
- // todo make part of ExuluAgent class
3839
- maxSteps: 5,
3840
- // todo make part of ExuluAgent class
3841
- onError: (error) => console.error("[EXULU] chat stream error.", error),
3842
- onFinish: ({ response, usage }) => console.info(
3843
- "[EXULU] chat stream finished.",
3844
- usage
3845
- )
4123
+ const result = agent.generateStream({
4124
+ messages: req.body.messages,
4125
+ tools: enabledTools,
4126
+ statistics: {
4127
+ label: agent.name,
4128
+ trigger: "agent"
4129
+ }
3846
4130
  });
3847
- for await (const delta of textStream) {
3848
- res.write(`data: ${delta}
3849
-
3850
- `);
3851
- }
3852
- res.end();
4131
+ result.pipeDataStreamToResponse(res);
3853
4132
  return;
3854
4133
  } else {
3855
- const response = await agent.chat.generate(req.body.messages, {
3856
- resourceId: `${authenticationResult.user.id}`,
3857
- output: agent.outputSchema,
3858
- threadId: `${req.body.threadId}`,
3859
- // conversation id
3860
- maxRetries: 2,
3861
- // todo make part of ExuluAgent class
3862
- maxSteps: 5
3863
- // todo make part of ExuluAgent class
4134
+ const response = await agent.generateSync({
4135
+ messages: req.body.messages,
4136
+ tools: enabledTools.map(),
4137
+ statistics: {
4138
+ label: agent.name,
4139
+ trigger: "agent"
4140
+ }
3864
4141
  });
3865
4142
  res.status(200).json(response);
3866
4143
  return;
@@ -3935,7 +4212,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3935
4212
  console.log("Routes:");
3936
4213
  console.table(routeLogs);
3937
4214
  const TARGET_API = "https://api.anthropic.com";
3938
- app.use("/gateway/anthropic", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
4215
+ app.use("/gateway/anthropic/:id", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
3939
4216
  const path3 = req.url;
3940
4217
  const url = `${TARGET_API}${path3}`;
3941
4218
  console.log("[PROXY] Manual proxy to:", url);
@@ -3944,60 +4221,102 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3944
4221
  console.log("[PROXY] Request body length:", req.body ? req.body.length : 0);
3945
4222
  console.log("[PROXY] Request model name:", req.body.model);
3946
4223
  console.log("[PROXY] Request stream:", req.body.stream);
3947
- console.log("[PROXY] API key:", req.headers["x-api-key"]);
3948
4224
  console.log("[PROXY] Request messages:", req.body.messages?.length);
3949
4225
  try {
3950
- const headers = {
3951
- "x-api-key": process.env.ANTHROPIC_API_KEY,
3952
- "anthropic-version": "2023-06-01",
3953
- "content-type": req.headers["content-type"] || "application/json"
3954
- };
3955
- if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
3956
- if (req.headers["user-agent"]) headers["user-agent"] = req.headers["user-agent"];
3957
4226
  console.log("[PROXY] Request body tools array length:", req.body.tools?.length);
3958
4227
  if (!req.body.tools) {
3959
4228
  req.body.tools = [];
3960
4229
  }
3961
- if (req.headers["x-api-key"] === "PLACEHOLDER") {
3962
- res.write(JSON.stringify({
3963
- "type": "content_block_delta",
3964
- "index": 0,
3965
- "delta": {
3966
- "type": "text_delta",
3967
- "text": "Hello, world!"
3968
- }
3969
- }));
3970
- res.end();
4230
+ const authenticationResult = await requestValidators.authenticate(req);
4231
+ if (!authenticationResult.user?.id) {
4232
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
4233
+ return;
4234
+ }
4235
+ console.log("[EXULU] authentication result", authenticationResult);
4236
+ const { db: db2 } = await postgresClient();
4237
+ const agent = await db2.from("agents").where({
4238
+ id: req.params.id
4239
+ }).first();
4240
+ if (!agent) {
4241
+ const arrayBuffer = createCustomAnthropicStreamingMessage(`
4242
+ \x1B[41m -- Agent ${req.params.id} not found or you do not have access to it. --
4243
+ \x1B[0m`);
4244
+ res.setHeader("Content-Type", "application/json");
4245
+ res.end(Buffer.from(arrayBuffer));
4246
+ return;
4247
+ }
4248
+ console.log("[EXULU] agent", agent?.name);
4249
+ if (!process.env.NEXTAUTH_SECRET) {
4250
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_nextauth_secret);
4251
+ res.setHeader("Content-Type", "application/json");
4252
+ res.end(Buffer.from(arrayBuffer));
4253
+ return;
4254
+ }
4255
+ if (!authenticationResult.user?.anthropic_token) {
4256
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.not_enabled);
4257
+ res.setHeader("Content-Type", "application/json");
4258
+ res.end(Buffer.from(arrayBuffer));
3971
4259
  return;
3972
4260
  }
4261
+ const bytes = CryptoJS2.AES.decrypt(authenticationResult.user?.anthropic_token, process.env.NEXTAUTH_SECRET);
4262
+ const anthropicApiKey = bytes.toString(CryptoJS2.enc.Utf8);
4263
+ const headers = {
4264
+ "x-api-key": anthropicApiKey,
4265
+ "anthropic-version": "2023-06-01",
4266
+ "content-type": req.headers["content-type"] || "application/json"
4267
+ };
4268
+ if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
4269
+ if (req.headers["user-agent"]) headers["user-agent"] = req.headers["user-agent"];
4270
+ console.log("[EXULU] anthropic api key", anthropicApiKey);
3973
4271
  const response = await fetch(url, {
3974
4272
  method: req.method,
3975
4273
  headers,
3976
4274
  body: req.method !== "GET" ? JSON.stringify(req.body) : void 0
3977
4275
  });
3978
- console.log("[PROXY] Response status:", response.status);
4276
+ console.log("[PROXY] Response:", response);
4277
+ console.log("[PROXY] Response:", response.body);
4278
+ await updateStatistic({
4279
+ name: "count",
4280
+ label: "Claude Code",
4281
+ type: STATISTICS_TYPE_ENUM.AGENT_RUN,
4282
+ trigger: "claude-code",
4283
+ count: 1
4284
+ });
3979
4285
  response.headers.forEach((value, key) => {
3980
4286
  res.setHeader(key, value);
3981
4287
  });
3982
4288
  res.status(response.status);
3983
- if (response.headers.get("content-type")?.includes("text/event-stream")) {
4289
+ const isStreaming = response.headers.get("content-type")?.includes("text/event-stream");
4290
+ if (isStreaming && !response?.body) {
4291
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_body);
4292
+ res.setHeader("Content-Type", "application/json");
4293
+ res.end(Buffer.from(arrayBuffer));
4294
+ return;
4295
+ }
4296
+ if (isStreaming) {
3984
4297
  const reader = response.body.getReader();
3985
4298
  const decoder = new TextDecoder();
3986
4299
  while (true) {
3987
4300
  const { done, value } = await reader.read();
3988
4301
  if (done) break;
3989
4302
  const chunk = decoder.decode(value, { stream: true });
4303
+ console.log("[PROXY] Chunk:", chunk);
3990
4304
  res.write(chunk);
3991
4305
  }
3992
4306
  res.end();
3993
- } else {
3994
- const data = await response.arrayBuffer();
3995
- res.end(Buffer.from(data));
4307
+ return;
3996
4308
  }
4309
+ const data = await response.arrayBuffer();
4310
+ console.log("[PROXY] Data:", data);
4311
+ res.end(Buffer.from(data));
3997
4312
  } catch (error) {
3998
4313
  console.error("[PROXY] Manual proxy error:", error);
3999
4314
  if (!res.headersSent) {
4000
- res.status(500).json({ error: error.message });
4315
+ if (error?.message === "Invalid token") {
4316
+ res.status(500).json({ error: "Authentication error, please check your IMP token and try again." });
4317
+ } else {
4318
+ res.status(500).json({ error: error.message });
4319
+ }
4001
4320
  }
4002
4321
  }
4003
4322
  });
@@ -4045,6 +4364,20 @@ var getPresignedFileUrl = async (key) => {
4045
4364
  console.log(`[EXULU] presigned url for file with key: ${key}, generated: ${json.url}`);
4046
4365
  return json.url;
4047
4366
  };
4367
+ var createCustomAnthropicStreamingMessage = (message) => {
4368
+ const responseData = {
4369
+ type: "message",
4370
+ content: [
4371
+ {
4372
+ type: "text",
4373
+ text: message
4374
+ }
4375
+ ]
4376
+ };
4377
+ const jsonString = JSON.stringify(responseData);
4378
+ const arrayBuffer = new TextEncoder().encode(jsonString).buffer;
4379
+ return arrayBuffer;
4380
+ };
4048
4381
 
4049
4382
  // src/registry/workers.ts
4050
4383
  import IORedis from "ioredis";
@@ -4097,7 +4430,7 @@ import * as fs2 from "fs";
4097
4430
  import path2 from "path";
4098
4431
  var defaultLogsDir = path2.join(process.cwd(), "logs");
4099
4432
  var redisConnection;
4100
- var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) => {
4433
+ var createWorkers = async (queues2, contexts, workflows, _logsDir) => {
4101
4434
  if (!redisServer.host || !redisServer.port) {
4102
4435
  console.error("[EXULU] you are trying to start workers, but no redis server is configured in the environment.");
4103
4436
  throw new Error("No redis server configured in the environment, so cannot start workers.");
@@ -4128,7 +4461,7 @@ var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) =>
4128
4461
  if (!bullmqJob.data.embedder) {
4129
4462
  throw new Error(`No embedder set for embedder job.`);
4130
4463
  }
4131
- const embedder = embedders.find((embedder2) => embedder2.id === bullmqJob.data.embedder);
4464
+ const embedder = contexts.find((context2) => context2.embedder?.id === bullmqJob.data.embedder);
4132
4465
  if (!embedder) {
4133
4466
  throw new Error(`Embedder ${bullmqJob.data.embedder} not found in the registry.`);
4134
4467
  }
@@ -4168,6 +4501,9 @@ var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) =>
4168
4501
  duration,
4169
4502
  result: JSON.stringify(result)
4170
4503
  });
4504
+ await db2.from((void 0).getTableName()).where({ id: result[0].id }).update({
4505
+ embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
4506
+ }).returning("id");
4171
4507
  return result;
4172
4508
  }
4173
4509
  if (bullmqJob.data.type === "workflow") {
@@ -4265,7 +4601,7 @@ var ExuluMCP = class {
4265
4601
  express;
4266
4602
  constructor() {
4267
4603
  }
4268
- create = async ({ express: express3, contexts, embedders, agents, workflows, config, tools }) => {
4604
+ create = async ({ express: express3, contexts, agents, workflows, config, tools }) => {
4269
4605
  this.express = express3;
4270
4606
  if (!this.server) {
4271
4607
  console.log("[EXULU] Creating MCP server.");
@@ -4342,8 +4678,6 @@ ${code}`
4342
4678
  throw new Error("MCP server not initialized.");
4343
4679
  }
4344
4680
  const sessionId = req.headers[SESSION_ID_HEADER];
4345
- console.log("sessionId!!", sessionId);
4346
- console.log("req.headers!!", req.headers);
4347
4681
  let transport;
4348
4682
  if (sessionId && this.transports[sessionId]) {
4349
4683
  transport = this.transports[sessionId];
@@ -4373,10 +4707,9 @@ ${code}`
4373
4707
  await transport.handleRequest(req, res, req.body);
4374
4708
  });
4375
4709
  const handleSessionRequest = async (req, res) => {
4376
- console.log("handleSessionRequest", req.body);
4377
4710
  const sessionId = req.headers[SESSION_ID_HEADER];
4378
4711
  if (!sessionId || !this.transports[sessionId]) {
4379
- console.log("Invalid or missing session ID");
4712
+ console.log("[EXULU] MCP request invalid or missing session ID");
4380
4713
  res.status(400).send("Invalid or missing session ID");
4381
4714
  return;
4382
4715
  }
@@ -4399,28 +4732,210 @@ ${code}`
4399
4732
 
4400
4733
  // src/registry/index.ts
4401
4734
  import express2 from "express";
4735
+
4736
+ // src/templates/agents/claude-code.ts
4737
+ var agentId = "0832-5178-1145-2194";
4738
+ var claudeCodeAgent = new ExuluAgent({
4739
+ id: `${agentId}-claude-code-agent`,
4740
+ name: `Claude Code Agent`,
4741
+ description: `Claude Code agent, enabling the creation of multiple Claude Code Agent instances with different configurations (rate limits, functions, etc).`,
4742
+ type: "custom"
4743
+ });
4744
+
4745
+ // src/templates/agents/claude-opus-4.ts
4746
+ import { anthropic } from "@ai-sdk/anthropic";
4747
+ import { z as z3 } from "zod";
4748
+ var agentId2 = "5434-5678-9143-2590";
4749
+ var defaultAgent = new ExuluAgent({
4750
+ id: `${agentId2}-default-claude-4-opus-agent`,
4751
+ name: `Default Claude 4 Opus Agent`,
4752
+ description: `Basic agent without any defined tools, that can support MCP's.`,
4753
+ type: "agent",
4754
+ capabilities: {
4755
+ tools: false,
4756
+ images: [],
4757
+ files: [],
4758
+ audio: [],
4759
+ video: []
4760
+ },
4761
+ evals: [],
4762
+ config: {
4763
+ name: `Default agent`,
4764
+ instructions: "You are a helpful assistant.",
4765
+ model: anthropic("claude-4-opus-20250514"),
4766
+ // todo add a field of type string that adds a dropdown list from which the user can select the model
4767
+ // todo for each model, check which provider is used, and require the admin to add one or multiple
4768
+ // API keys for the provider (which we can then auto-rotate).
4769
+ // todo also add custom fields for rate limiting, so the admin can set custom rate limits for the agent
4770
+ // and allow him/her to decide if the rate limit is per user or per agent.
4771
+ // todo finally allow switching on or off immutable audit logs on the agent. Which then enables OTEL
4772
+ // and stores the logs into the pre-defined storage.
4773
+ custom: z3.object({
4774
+ apiKey: z3.string()
4775
+ })
4776
+ }
4777
+ });
4778
+
4779
+ // src/templates/tools/browserbase.ts
4780
+ import { z as z4 } from "zod";
4781
+ import { Stagehand } from "@browserbasehq/stagehand";
4782
+ import { Browserbase } from "@browserbasehq/sdk";
4783
+ var PROJECT_ID = "811444dd-6e6d-40b5-bd90-541c93e44be6";
4784
+ process.env.BROWSERBASE_PROJECT_ID = PROJECT_ID;
4785
+ var BB_API_KEY = "bb_live_LwMwNgZB5cIEKcBwMuAugrgNkFM";
4786
+ async function createContext() {
4787
+ const bb = new Browserbase({ apiKey: BB_API_KEY });
4788
+ const context = await bb.contexts.create({
4789
+ projectId: PROJECT_ID
4790
+ });
4791
+ return context;
4792
+ }
4793
+ async function createAuthSession(contextId) {
4794
+ const bb = new Browserbase({ apiKey: BB_API_KEY });
4795
+ const session = await bb.sessions.create({
4796
+ projectId: PROJECT_ID,
4797
+ browserSettings: {
4798
+ context: {
4799
+ id: contextId,
4800
+ persist: true
4801
+ }
4802
+ }
4803
+ /* proxies: [{ // not included in the free tier
4804
+ type: "browserbase",
4805
+ geolocation: {
4806
+ city: CITY,
4807
+ country: COUNTRY
4808
+ }
4809
+ }] */
4810
+ });
4811
+ const liveViewLinks = await bb.sessions.debug(session.id);
4812
+ const liveViewLink = liveViewLinks.debuggerFullscreenUrl;
4813
+ console.log(`\u{1F50D} Live View Link: ${liveViewLink}`);
4814
+ console.log("Session URL: https://browserbase.com/sessions/" + session.id);
4815
+ return {
4816
+ url: liveViewLink,
4817
+ id: session.id
4818
+ };
4819
+ }
4820
+ var createSession = new ExuluTool({
4821
+ id: `1234-5178-9423-4267`,
4822
+ type: "function",
4823
+ name: "Create a browserbase session.",
4824
+ description: `
4825
+ Creates a browserbase session and returns the live view url as well as
4826
+ the session id as a JSON object. A browserbase session is a headless browser
4827
+ that can be used to to visit websites and perform actions.
4828
+ `,
4829
+ execute: async () => {
4830
+ const { id } = await createContext();
4831
+ return await createAuthSession(id);
4832
+ }
4833
+ });
4834
+ var askChatgpt = new ExuluTool({
4835
+ id: `1234-5178-9423-4268`,
4836
+ type: "function",
4837
+ name: "ChatGPT browserbase operation.",
4838
+ inputSchema: z4.object({
4839
+ session: z4.string().describe("The session id of the browserbase session."),
4840
+ question: z4.string().describe("The question to ask ChatGPT.")
4841
+ }),
4842
+ description: `Uses an existing, authenticated browserbase session to visit ChatGPT and perform actions such as asking questions.`,
4843
+ execute: async ({ session, question }) => {
4844
+ const stagehand = new Stagehand({
4845
+ // With npx create-browser-app, this config is found
4846
+ // in a separate stagehand.config.ts file
4847
+ env: "BROWSERBASE",
4848
+ // set to "LOCAL" for local development
4849
+ apiKey: BB_API_KEY,
4850
+ // todo make this a config variable the admin can set in the UI
4851
+ modelName: "openai/gpt-4.1-mini",
4852
+ // todo change to claude || optionally make configurable?
4853
+ browserbaseSessionID: session,
4854
+ modelClientOptions: {
4855
+ apiKey: process.env.OPENAI_API_KEY
4856
+ // todo make this a config variable the admin can set in the UI
4857
+ }
4858
+ });
4859
+ await stagehand.init();
4860
+ const page = stagehand.page;
4861
+ await page.goto("https://chatgpt.com");
4862
+ await page.act(`Type in '${question}' into the search bar`);
4863
+ const { answer } = await page.extract({
4864
+ instruction: "The answer to the question generated by ChatGPT.",
4865
+ schema: z4.object({
4866
+ answer: z4.string()
4867
+ })
4868
+ });
4869
+ console.log(answer);
4870
+ await stagehand.close();
4871
+ return {
4872
+ answer
4873
+ };
4874
+ }
4875
+ });
4876
+
4877
+ // src/templates/tools/jira.ts
4878
+ import { z as z5 } from "zod";
4879
+ var getTicket = new ExuluTool({
4880
+ id: `1414-5179-1423-1269`,
4881
+ name: "JIRA ticket retrieval.",
4882
+ type: "function",
4883
+ inputSchema: z5.object({
4884
+ ticketId: z5.string().describe("The id of the ticket to retrieve.")
4885
+ }),
4886
+ description: `Retrieves a ticket from Jira.`,
4887
+ execute: async ({ session, question }) => {
4888
+ return {
4889
+ name: "BYD-1234",
4890
+ id: "12345678",
4891
+ status: "Open",
4892
+ description: "This is a test ticket",
4893
+ assignee: "John Doe",
4894
+ createdAt: "2021-01-01",
4895
+ updatedAt: "2021-01-01",
4896
+ dueDate: "2021-01-01",
4897
+ priority: "High"
4898
+ };
4899
+ }
4900
+ });
4901
+
4902
+ // src/registry/index.ts
4402
4903
  var ExuluApp = class {
4403
4904
  _agents = [];
4404
4905
  _workflows = [];
4405
4906
  _config;
4406
- _embedders = [];
4407
4907
  _queues = [];
4408
4908
  _contexts = {};
4409
4909
  _tools = [];
4410
4910
  _expressApp = null;
4411
4911
  constructor() {
4412
4912
  }
4413
- // Factory function so we can async initialize the
4414
- // MCP server if needed.
4415
- create = async ({ contexts, embedders, agents, workflows, config, tools }) => {
4416
- this._embedders = embedders ?? [];
4913
+ // Factory function so we can async
4914
+ // initialize the MCP server if needed.
4915
+ create = async ({ contexts, agents, workflows, config, tools }) => {
4417
4916
  this._workflows = workflows ?? [];
4418
4917
  this._contexts = contexts ?? {};
4419
- this._agents = agents ?? [];
4918
+ this._agents = [
4919
+ claudeCodeAgent,
4920
+ defaultAgent,
4921
+ ...agents ?? []
4922
+ ];
4420
4923
  this._config = config;
4421
- this._tools = tools ?? [];
4924
+ this._tools = [
4925
+ ...tools ?? [],
4926
+ // Add contexts as tools
4927
+ ...Object.values(contexts || {}).map((context) => context.tool()),
4928
+ // Add agents as tools
4929
+ ...(agents || []).map((agent) => agent.tool()),
4930
+ ...[
4931
+ createSession,
4932
+ askChatgpt,
4933
+ getTicket
4934
+ ]
4935
+ ];
4936
+ const contextsArray = Object.values(contexts || {});
4422
4937
  const queues2 = [
4423
- ...embedders?.length ? embedders.map((agent) => agent.queue?.name || null) : [],
4938
+ ...contextsArray?.length ? contextsArray.map((context) => context.embedder.queue?.name || null) : [],
4424
4939
  ...workflows?.length ? workflows.map((workflow) => workflow.queue?.name || null) : []
4425
4940
  ];
4426
4941
  this._queues = [...new Set(queues2.filter((o) => !!o))];
@@ -4437,9 +4952,6 @@ var ExuluApp = class {
4437
4952
  }
4438
4953
  return this._expressApp;
4439
4954
  }
4440
- embedder(id) {
4441
- return this._embedders.find((x) => x.id === id);
4442
- }
4443
4955
  tool(id) {
4444
4956
  return this._tools.find((x) => x.id === id);
4445
4957
  }
@@ -4455,9 +4967,6 @@ var ExuluApp = class {
4455
4967
  workflow(id) {
4456
4968
  return this._workflows.find((x) => x.id === id);
4457
4969
  }
4458
- get embedders() {
4459
- return this._embedders;
4460
- }
4461
4970
  get contexts() {
4462
4971
  return Object.values(this._contexts ?? {});
4463
4972
  }
@@ -4473,7 +4982,6 @@ var ExuluApp = class {
4473
4982
  return await createWorkers(
4474
4983
  this._queues,
4475
4984
  Object.values(this._contexts ?? {}),
4476
- this._embedders,
4477
4985
  this._workflows,
4478
4986
  this._config?.workers?.logsDir
4479
4987
  );
@@ -4490,7 +4998,6 @@ var ExuluApp = class {
4490
4998
  await createExpressRoutes(
4491
4999
  app,
4492
5000
  this._agents,
4493
- this._embedders,
4494
5001
  this._tools,
4495
5002
  this._workflows,
4496
5003
  Object.values(this._contexts ?? {})
@@ -4500,7 +5007,6 @@ var ExuluApp = class {
4500
5007
  await mcp.create({
4501
5008
  express: app,
4502
5009
  contexts: this._contexts,
4503
- embedders: this._embedders,
4504
5010
  agents: this._agents,
4505
5011
  workflows: this._workflows,
4506
5012
  config: this._config,
@@ -4829,15 +5335,12 @@ var ExuluTokenizer = class {
4829
5335
  console.log("[EXULU] Loading tokenizer.", modelName);
4830
5336
  const model = await load(registry[models[modelName]]);
4831
5337
  console.log("[EXULU] Loaded tokenizer.", modelName, performance.now() - time);
4832
- console.log("[EXULU] Model.", model.bpe_ranks);
4833
- console.log("[EXULU] Model.", model.special_tokens);
4834
- console.log("[EXULU] Model.", model.pat_str);
4835
5338
  const encoder = new Tiktoken(
4836
5339
  model.bpe_ranks,
4837
5340
  model.special_tokens,
4838
5341
  model.pat_str
4839
5342
  );
4840
- console.log("[EXULU] Encoder.", encoder);
5343
+ console.log("[EXULU] Set encoder.");
4841
5344
  this.encoder = encoder;
4842
5345
  return encoder;
4843
5346
  }
@@ -4860,9 +5363,9 @@ var ExuluTokenizer = class {
4860
5363
  throw new Error("Tokenizer not initialized");
4861
5364
  }
4862
5365
  const time = performance.now();
4863
- console.log("[EXULU] Encoding text.", text);
5366
+ console.log("[EXULU] Encoding text length: " + (text?.length || 0));
4864
5367
  const tokens = this.encoder.encode(text);
4865
- console.log("[EXULU] Encoded text.", text, performance.now() - time);
5368
+ console.log("[EXULU] Finished encoding text.", performance.now() - time);
4866
5369
  return tokens;
4867
5370
  }
4868
5371
  async countTokensBatch(texts) {
@@ -4876,7 +5379,6 @@ var ExuluTokenizer = class {
4876
5379
  if (!this.encoder) {
4877
5380
  throw new Error("Tokenizer not initialized");
4878
5381
  }
4879
- console.log("[EXULU] Counting tokens.", text);
4880
5382
  const tokens = this.encoder.encode(text);
4881
5383
  const count = tokens.length;
4882
5384
  console.log("[EXULU] Token count.", count);
@@ -5046,8 +5548,6 @@ var RecursiveChunker = class _RecursiveChunker extends BaseChunker {
5046
5548
  *
5047
5549
  * @example <caption>Accessing properties and methods</caption>
5048
5550
  * const chunker = await RecursiveChunker.create();
5049
- * console.log(chunker.chunkSize); // 512
5050
- * console.log(chunker.rules); // RecursiveRules instance
5051
5551
  * const chunks = await chunker.chunk("Some text"); // Use as object method
5052
5552
  *
5053
5553
  * @note
@@ -5266,8 +5766,6 @@ var RecursiveChunker = class _RecursiveChunker extends BaseChunker {
5266
5766
  if (!text) {
5267
5767
  return [];
5268
5768
  }
5269
- console.log("[EXULU] Rule.", this.rules.length);
5270
- console.log("[EXULU] Level.", level);
5271
5769
  if (level >= this.rules.length) {
5272
5770
  const tokenCount = await this._estimateTokenCount(text);
5273
5771
  return [
@@ -5331,7 +5829,6 @@ var RecursiveChunker = class _RecursiveChunker extends BaseChunker {
5331
5829
  * @returns {Promise<RecursiveChunk[]>} A promise that resolves to an array of RecursiveChunk objects
5332
5830
  */
5333
5831
  async chunk(text) {
5334
- console.log("[EXULU] Chunking text.", text);
5335
5832
  const result = await this._recursiveChunk(text, 0, 0);
5336
5833
  await this.tokenizer.free();
5337
5834
  return result;
@@ -5717,200 +6214,6 @@ var SentenceChunker = class _SentenceChunker extends BaseChunker {
5717
6214
  }
5718
6215
  };
5719
6216
 
5720
- // src/cli/index.tsx
5721
- import { useState as useState2 } from "react";
5722
- import { Box as Box2, Text as Text4, render as render2 } from "ink";
5723
- import { UnorderedList as UnorderedList3 } from "@inkjs/ui";
5724
- import patchConsole from "patch-console";
5725
-
5726
- // src/cli/components/nav.tsx
5727
- import { Select } from "@inkjs/ui";
5728
- import { useApp } from "ink";
5729
- import { jsx } from "react/jsx-runtime";
5730
- var nav = [
5731
- {
5732
- label: "Agents",
5733
- value: "agents"
5734
- },
5735
- {
5736
- label: "Start Claude Code",
5737
- value: "claude-code"
5738
- },
5739
- {
5740
- label: "Exit",
5741
- value: "exit"
5742
- }
5743
- ];
5744
- var Nav = ({ setView }) => {
5745
- const { exit } = useApp();
5746
- return /* @__PURE__ */ jsx(Select, { options: nav, onChange: (value) => {
5747
- if (value === "exit") {
5748
- exit();
5749
- }
5750
- setView(value);
5751
- } });
5752
- };
5753
- var nav_default = Nav;
5754
-
5755
- // src/cli/components/agent-selector.tsx
5756
- import { Text as Text2 } from "ink";
5757
- import { Select as Select2 } from "@inkjs/ui";
5758
- import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
5759
- var AgentSelector = ({ exulu, setAgent, setEvaluations }) => {
5760
- const agents = exulu.agents.map((agent) => ({
5761
- label: agent.name,
5762
- value: agent.id
5763
- }));
5764
- return /* @__PURE__ */ jsxs(Fragment, { children: [
5765
- /* @__PURE__ */ jsx2(Text2, { children: "Please select an agent:" }),
5766
- /* @__PURE__ */ jsx2(Select2, { options: agents, onChange: (value) => {
5767
- console.log("selected agent", value);
5768
- const agent = exulu.agent(value);
5769
- if (!agent) {
5770
- console.error("Agent not found", value);
5771
- return;
5772
- }
5773
- setAgent(agent);
5774
- if (agent) {
5775
- setEvaluations(agent.evals || []);
5776
- }
5777
- } })
5778
- ] });
5779
- };
5780
- var agent_selector_default = AgentSelector;
5781
-
5782
- // src/cli/components/eval-selector.tsx
5783
- import { Select as Select3 } from "@inkjs/ui";
5784
- import { jsx as jsx3 } from "react/jsx-runtime";
5785
- var EvalSelector = ({ evaluations, setEvaluation }) => {
5786
- return /* @__PURE__ */ jsx3(Select3, { options: evaluations.map((evaluation) => ({
5787
- label: evaluation.runner.name,
5788
- value: evaluation.runner.name
5789
- })), onChange: (value) => {
5790
- console.log("selected eval", value);
5791
- const evaluation = evaluations?.find((evaluation2) => evaluation2.runner.name === value);
5792
- if (evaluation) {
5793
- setEvaluation(evaluation);
5794
- }
5795
- } });
5796
- };
5797
- var eval_selector_default = EvalSelector;
5798
-
5799
- // src/cli/components/eval-actions.tsx
5800
- import { useState } from "react";
5801
- import { ProgressBar as ProgressBar2, Select as Select4, UnorderedList as UnorderedList2 } from "@inkjs/ui";
5802
- import { Text as Text3 } from "ink";
5803
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
5804
- var EvalActions = ({ agent, evaluation, setEvaluation }) => {
5805
- const [progress, setProgress] = useState(0);
5806
- const [results, setResults] = useState([]);
5807
- const [running, setRunning] = useState();
5808
- const run = async (evaluation2) => {
5809
- setRunning({
5810
- label: evaluation2.runner.name
5811
- });
5812
- const testCases = evaluation2.runner.testcases;
5813
- const total = testCases.length;
5814
- if (!testCases) {
5815
- throw new Error("No test cases found");
5816
- }
5817
- let i = 0;
5818
- for (const testCase of testCases) {
5819
- i++;
5820
- const result = await evaluation2.runner.run({
5821
- data: testCase,
5822
- runner: {
5823
- agent
5824
- }
5825
- });
5826
- setProgress(Math.round(i / total * 100));
5827
- setResults([...results, {
5828
- name: evaluation2.runner.name,
5829
- prompt: testCase.prompt?.slice(0, 100) + "...",
5830
- score: result.score,
5831
- comment: result.comment
5832
- }]);
5833
- }
5834
- setRunning(void 0);
5835
- };
5836
- if (progress === 100) {
5837
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
5838
- /* @__PURE__ */ jsx4(Text3, { children: "Evaluations completed." }),
5839
- /* @__PURE__ */ jsx4(UnorderedList2, { children: results.map((result) => /* @__PURE__ */ jsx4(UnorderedList2.Item, { children: /* @__PURE__ */ jsxs2(Text3, { children: [
5840
- result.name,
5841
- ": ",
5842
- result.score,
5843
- " - ",
5844
- result.comment
5845
- ] }) })) })
5846
- ] });
5847
- }
5848
- if (running) {
5849
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
5850
- /* @__PURE__ */ jsxs2(Text3, { children: [
5851
- "Running ",
5852
- running.label,
5853
- "..."
5854
- ] }),
5855
- /* @__PURE__ */ jsx4(ProgressBar2, { value: progress })
5856
- ] });
5857
- }
5858
- return /* @__PURE__ */ jsx4(Select4, { options: [{
5859
- label: "Run evaluation",
5860
- value: "run"
5861
- }, {
5862
- label: "Go back",
5863
- value: "back"
5864
- }], onChange: (value) => {
5865
- if (value === "back") {
5866
- setEvaluation(void 0);
5867
- }
5868
- if (value === "run") {
5869
- run(evaluation);
5870
- }
5871
- } });
5872
- };
5873
- var eval_actions_default = EvalActions;
5874
-
5875
- // src/cli/index.tsx
5876
- import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
5877
- var Main = ({ exulu }) => {
5878
- patchConsole((stream, data) => {
5879
- setLogs([...logs, data]);
5880
- });
5881
- const [logs, setLogs] = useState2([]);
5882
- const [view, setView] = useState2();
5883
- const [agent, setAgent] = useState2();
5884
- const [evaluations, setEvaluations] = useState2([]);
5885
- const [evaluation, setEvaluation] = useState2();
5886
- return /* @__PURE__ */ jsxs3(Box2, { borderStyle: "round", borderColor: "cyan", padding: 1, flexDirection: "column", width: "70%", children: [
5887
- /* @__PURE__ */ jsx5(Text4, { children: "Logs:" }),
5888
- /* @__PURE__ */ jsx5(UnorderedList3, { children: logs.map((log, index) => /* @__PURE__ */ jsx5(UnorderedList3.Item, { children: /* @__PURE__ */ jsx5(Text4, { children: log }) })) }),
5889
- !view && /* @__PURE__ */ jsx5(nav_default, { setView }),
5890
- view === "agents" && !agent && /* @__PURE__ */ jsx5(agent_selector_default, { exulu, setAgent, setEvaluations }),
5891
- view === "agents" && agent && !evaluation && /* @__PURE__ */ jsxs3(Fragment3, { children: [
5892
- /* @__PURE__ */ jsxs3(Text4, { children: [
5893
- 'Selected agent "',
5894
- agent.name,
5895
- '". Please select an evaluation:'
5896
- ] }),
5897
- /* @__PURE__ */ jsx5(eval_selector_default, { evaluations, setEvaluation })
5898
- ] }),
5899
- view === "agents" && agent && evaluation && /* @__PURE__ */ jsxs3(Fragment3, { children: [
5900
- /* @__PURE__ */ jsxs3(Text4, { children: [
5901
- "Selected evaluation: ",
5902
- evaluation.runner.name
5903
- ] }),
5904
- /* @__PURE__ */ jsx5(eval_actions_default, { agent, evaluation, setEvaluation })
5905
- ] })
5906
- ] });
5907
- };
5908
- var cli_default = {
5909
- run: (exulu) => {
5910
- render2(/* @__PURE__ */ jsx5(Main, { exulu }));
5911
- }
5912
- };
5913
-
5914
6217
  // src/index.ts
5915
6218
  var ExuluJobs = {
5916
6219
  redis: redisClient,
@@ -5940,7 +6243,6 @@ export {
5940
6243
  ExuluApp,
5941
6244
  authentication as ExuluAuthentication,
5942
6245
  ExuluChunkers,
5943
- cli_default as ExuluCli,
5944
6246
  ExuluContext,
5945
6247
  ExuluDatabase,
5946
6248
  ExuluEmbedder,