@exulu/backend 0.3.13 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -102,6 +102,47 @@ async function postgresClient() {
102
102
  }
103
103
 
104
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
+ };
105
146
  var usersSchema = {
106
147
  name: {
107
148
  plural: "users",
@@ -159,14 +200,13 @@ var usersSchema = {
159
200
  name: "last_used",
160
201
  type: "date"
161
202
  },
203
+ {
204
+ name: "anthropic_token",
205
+ type: "text"
206
+ },
162
207
  {
163
208
  name: "role",
164
- type: "reference",
165
- references: {
166
- table: "roles",
167
- field: "id",
168
- onDelete: "CASCADE"
169
- }
209
+ type: "uuid"
170
210
  }
171
211
  ]
172
212
  };
@@ -212,10 +252,6 @@ var statisticsSchema = {
212
252
  {
213
253
  name: "total",
214
254
  type: "number"
215
- },
216
- {
217
- name: "timeseries",
218
- type: "json"
219
255
  }
220
256
  ]
221
257
  };
@@ -271,11 +307,11 @@ var evalResultsSchema = {
271
307
  },
272
308
  {
273
309
  name: "agent_id",
274
- type: "text"
310
+ type: "uuid"
275
311
  },
276
312
  {
277
313
  name: "workflow_id",
278
- type: "text"
314
+ type: "uuid"
279
315
  },
280
316
  {
281
317
  name: "eval_type",
@@ -291,50 +327,6 @@ var evalResultsSchema = {
291
327
  }
292
328
  ]
293
329
  };
294
- var threadsSchema = {
295
- name: {
296
- plural: "threads",
297
- singular: "thread"
298
- },
299
- fields: [
300
- {
301
- name: "resourceId",
302
- type: "text"
303
- },
304
- {
305
- name: "title",
306
- type: "text"
307
- },
308
- {
309
- name: "metadata",
310
- type: "text"
311
- }
312
- ]
313
- };
314
- var messagesSchema = {
315
- name: {
316
- plural: "messages",
317
- singular: "message"
318
- },
319
- fields: [
320
- {
321
- name: "thread_id",
322
- type: "text"
323
- },
324
- {
325
- name: "content",
326
- type: "text"
327
- },
328
- {
329
- name: "role",
330
- type: "text"
331
- },
332
- {
333
- name: "type",
334
- type: "text"
335
- }
336
- ]
337
- };
338
330
  var jobsSchema = {
339
331
  name: {
340
332
  plural: "jobs",
@@ -367,19 +359,20 @@ var jobsSchema = {
367
359
  },
368
360
  {
369
361
  name: "agent",
370
- type: "text"
362
+ type: "uuid"
371
363
  },
372
364
  {
373
365
  name: "workflow",
374
- type: "text"
366
+ type: "uuid"
375
367
  },
376
368
  {
377
369
  name: "user",
378
- type: "text"
370
+ // next auth stores users with id type SERIAL, so we need to use number
371
+ type: "number"
379
372
  },
380
373
  {
381
374
  name: "item",
382
- type: "text"
375
+ type: "uuid"
383
376
  },
384
377
  {
385
378
  name: "steps",
@@ -473,7 +466,7 @@ var mapType = (t, type, name, defaultValue) => {
473
466
  return;
474
467
  }
475
468
  if (type === "date") {
476
- t.date(name);
469
+ t.timestamp(name);
477
470
  return;
478
471
  }
479
472
  if (type === "uuid") {
@@ -543,23 +536,44 @@ var generateApiKey = async (name, email) => {
543
536
 
544
537
  // src/postgres/init-db.ts
545
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
+ }
546
567
  if (!await knex.schema.hasTable("roles")) {
547
568
  await knex.schema.createTable("roles", (table) => {
548
569
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
549
- table.date("createdAt").defaultTo(knex.fn.now());
550
- table.date("updatedAt").defaultTo(knex.fn.now());
570
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
571
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
551
572
  for (const field of rolesSchema.fields) {
552
- const { type, name, references, default: defaultValue } = field;
573
+ const { type, name, default: defaultValue } = field;
553
574
  if (!type || !name) {
554
575
  continue;
555
576
  }
556
- if (type === "reference") {
557
- if (!references) {
558
- throw new Error("Field with type reference must have a reference definition.");
559
- }
560
- table.uuid(name).references(references.field).inTable(references.table);
561
- return;
562
- }
563
577
  mapType(table, type, sanitizeName(name), defaultValue);
564
578
  }
565
579
  });
@@ -567,20 +581,13 @@ var up = async function(knex) {
567
581
  if (!await knex.schema.hasTable("eval_results")) {
568
582
  await knex.schema.createTable("eval_results", (table) => {
569
583
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
570
- table.date("createdAt").defaultTo(knex.fn.now());
571
- table.date("updatedAt").defaultTo(knex.fn.now());
584
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
585
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
572
586
  for (const field of evalResultsSchema.fields) {
573
- const { type, name, references, default: defaultValue } = field;
587
+ const { type, name, default: defaultValue } = field;
574
588
  if (!type || !name) {
575
589
  continue;
576
590
  }
577
- if (type === "reference") {
578
- if (!references) {
579
- throw new Error("Field with type reference must have a reference definition.");
580
- }
581
- table.uuid(name).references(references.field).inTable(references.table);
582
- return;
583
- }
584
591
  mapType(table, type, sanitizeName(name), defaultValue);
585
592
  }
586
593
  });
@@ -588,62 +595,41 @@ var up = async function(knex) {
588
595
  if (!await knex.schema.hasTable("statistics")) {
589
596
  await knex.schema.createTable("statistics", (table) => {
590
597
  table.uuid("id").primary().defaultTo(knex.fn.uuid());
591
- table.date("createdAt").defaultTo(knex.fn.now());
592
- table.date("updatedAt").defaultTo(knex.fn.now());
598
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
599
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
593
600
  for (const field of statisticsSchema.fields) {
594
- const { type, name, references, default: defaultValue } = field;
601
+ const { type, name, default: defaultValue } = field;
595
602
  if (!type || !name) {
596
603
  continue;
597
604
  }
598
- if (type === "reference") {
599
- if (!references) {
600
- throw new Error("Field with type reference must have a reference definition.");
601
- }
602
- table.uuid(name).references(references.field).inTable(references.table);
603
- return;
604
- }
605
605
  mapType(table, type, sanitizeName(name), defaultValue);
606
606
  }
607
607
  });
608
608
  }
609
609
  if (!await knex.schema.hasTable("jobs")) {
610
610
  await knex.schema.createTable("jobs", (table) => {
611
- table.increments("id").primary();
612
- table.date("createdAt").defaultTo(knex.fn.now());
613
- 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());
614
614
  for (const field of jobsSchema.fields) {
615
- const { type, name, references, default: defaultValue } = field;
615
+ const { type, name, default: defaultValue } = field;
616
616
  if (!type || !name) {
617
617
  continue;
618
618
  }
619
- if (type === "reference") {
620
- if (!references) {
621
- throw new Error("Field with type reference must have a reference definition.");
622
- }
623
- table.uuid(name).references(references.field).inTable(references.table);
624
- return;
625
- }
626
619
  mapType(table, type, sanitizeName(name), defaultValue);
627
620
  }
628
621
  });
629
622
  }
630
623
  if (!await knex.schema.hasTable("agents")) {
631
624
  await knex.schema.createTable("agents", (table) => {
632
- table.increments("id").primary();
633
- table.date("createdAt").defaultTo(knex.fn.now());
634
- 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());
635
628
  for (const field of agentsSchema.fields) {
636
- const { type, name, references, default: defaultValue } = field;
629
+ const { type, name, default: defaultValue } = field;
637
630
  if (!type || !name) {
638
631
  continue;
639
632
  }
640
- if (type === "reference") {
641
- if (!references) {
642
- throw new Error("Field with type reference must have a reference definition.");
643
- }
644
- table.uuid(name).references(references.field).inTable(references.table);
645
- return;
646
- }
647
633
  mapType(table, type, sanitizeName(name), defaultValue);
648
634
  }
649
635
  });
@@ -659,8 +645,8 @@ var up = async function(knex) {
659
645
  if (!await knex.schema.hasTable("users")) {
660
646
  await knex.schema.createTable("users", (table) => {
661
647
  table.increments("id").primary();
662
- table.date("createdAt").defaultTo(knex.fn.now());
663
- table.date("updatedAt").defaultTo(knex.fn.now());
648
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
649
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
664
650
  table.string("name", 255);
665
651
  table.string("password", 255);
666
652
  table.string("email", 255);
@@ -668,20 +654,13 @@ var up = async function(knex) {
668
654
  table.text("image");
669
655
  for (const field of usersSchema.fields) {
670
656
  console.log("[EXULU] field", field);
671
- const { type, name, references, default: defaultValue } = field;
657
+ const { type, name, default: defaultValue } = field;
672
658
  if (name === "id" || name === "name" || name === "email" || name === "emailVerified" || name === "image") {
673
659
  continue;
674
660
  }
675
661
  if (!type || !name) {
676
662
  continue;
677
663
  }
678
- if (type === "reference") {
679
- if (!references) {
680
- throw new Error("Field with type reference must have a reference definition.");
681
- }
682
- table.uuid(name).references(references.field).inTable(references.table);
683
- return;
684
- }
685
664
  mapType(table, type, sanitizeName(name), defaultValue);
686
665
  }
687
666
  });
@@ -702,14 +681,6 @@ var up = async function(knex) {
702
681
  table.text("token_type");
703
682
  });
704
683
  }
705
- if (!await knex.schema.hasTable("sessions")) {
706
- await knex.schema.createTable("sessions", (table) => {
707
- table.increments("id").primary();
708
- table.integer("userId").notNullable();
709
- table.timestamp("expires", { useTz: true }).notNullable();
710
- table.string("sessionToken", 255).notNullable();
711
- });
712
- }
713
684
  };
714
685
  var execute = async () => {
715
686
  console.log("[EXULU] Initializing database.");
@@ -960,6 +931,35 @@ var ExuluEvalUtils = {
960
931
  };
961
932
 
962
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
+ };
963
963
  function generateSlug(name) {
964
964
  const normalized = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
965
965
  const lowercase = normalized.toLowerCase();
@@ -986,54 +986,119 @@ var ExuluAgent = class {
986
986
  name;
987
987
  description = "";
988
988
  slug = "";
989
+ type;
989
990
  streaming = false;
990
991
  rateLimit;
991
992
  config;
992
993
  // private memory: Memory | undefined; // TODO do own implementation
993
- tools;
994
994
  evals;
995
995
  model;
996
996
  capabilities;
997
- constructor({ id, name, description, config, rateLimit, capabilities, tools, evals }) {
997
+ constructor({ id, name, description, config, rateLimit, capabilities, type, evals }) {
998
998
  this.id = id;
999
999
  this.name = name;
1000
1000
  this.evals = evals;
1001
1001
  this.description = description;
1002
1002
  this.rateLimit = rateLimit;
1003
- this.tools = tools;
1004
1003
  this.config = config;
1005
- this.capabilities = capabilities;
1004
+ this.type = type;
1005
+ this.capabilities = capabilities || {
1006
+ images: [],
1007
+ files: [],
1008
+ audio: [],
1009
+ video: []
1010
+ };
1006
1011
  this.slug = `/agents/${generateSlug(this.name)}/run`;
1007
- this.model = this.config.model;
1012
+ this.model = this.config?.model;
1008
1013
  }
1009
- 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 }) => {
1010
1037
  if (!this.model) {
1011
1038
  throw new Error("Model is required for streaming.");
1012
1039
  }
1013
- if (this.config.outputSchema) {
1014
- if (stream) {
1015
- }
1016
- const { object } = await generateObject({
1017
- model: this.model,
1018
- schema: this.config.outputSchema,
1019
- prompt
1020
- });
1021
- return object;
1040
+ if (!this.config) {
1041
+ throw new Error("Config is required for generating.");
1022
1042
  }
1023
- if (stream) {
1024
- const result = streamText({
1025
- model: this.model,
1026
- prompt
1027
- });
1028
- const text2 = await result.text;
1029
- return text2;
1043
+ if (prompt && messages) {
1044
+ throw new Error("Prompt and messages cannot be provided at the same time.");
1030
1045
  }
1031
1046
  const { text } = await generateText({
1032
1047
  model: this.model,
1033
- 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
1034
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
+ }
1035
1064
  return text;
1036
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
+ };
1037
1102
  };
1038
1103
  var ExuluEmbedder = class {
1039
1104
  id;
@@ -1056,6 +1121,13 @@ var ExuluEmbedder = class {
1056
1121
  }
1057
1122
  async generateFromQuery(query, statistics) {
1058
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
+ });
1059
1131
  }
1060
1132
  return await this.generateEmbeddings({
1061
1133
  item: {
@@ -1069,6 +1141,13 @@ var ExuluEmbedder = class {
1069
1141
  }
1070
1142
  async generateFromDocument(input, statistics) {
1071
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
+ });
1072
1151
  }
1073
1152
  if (!this.chunker) {
1074
1153
  throw new Error("Chunker not found for embedder " + this.name);
@@ -1257,9 +1336,8 @@ var ExuluEval = class {
1257
1336
  if (!data.prompt) {
1258
1337
  throw new Error("Prompt is required for running an agent.");
1259
1338
  }
1260
- const result = await runner.agent.generate({
1261
- prompt: data.prompt,
1262
- stream: false
1339
+ const result = await runner.agent.generateSync({
1340
+ prompt: data.prompt
1263
1341
  });
1264
1342
  data.result = result;
1265
1343
  }
@@ -1354,18 +1432,18 @@ var ExuluTool = class {
1354
1432
  id;
1355
1433
  name;
1356
1434
  description;
1357
- parameters;
1435
+ inputSchema;
1358
1436
  type;
1359
1437
  tool;
1360
- constructor({ id, name, description, parameters, type, execute: execute2 }) {
1438
+ constructor({ id, name, description, inputSchema, type, execute: execute2 }) {
1361
1439
  this.id = id;
1362
1440
  this.name = name;
1363
1441
  this.description = description;
1364
- this.parameters = parameters;
1442
+ this.inputSchema = inputSchema;
1365
1443
  this.type = type;
1366
1444
  this.tool = tool({
1367
1445
  description,
1368
- parameters,
1446
+ parameters: inputSchema || z.object({}),
1369
1447
  execute: execute2
1370
1448
  });
1371
1449
  }
@@ -1421,7 +1499,7 @@ var ExuluContext = class {
1421
1499
  }
1422
1500
  const { db: db2 } = await postgresClient();
1423
1501
  Object.keys(item).forEach((key) => {
1424
- 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") {
1425
1503
  return;
1426
1504
  }
1427
1505
  const field = this.fields.find((field2) => field2.name === key);
@@ -1468,6 +1546,9 @@ var ExuluContext = class {
1468
1546
  chunk_index: chunk.index,
1469
1547
  embedding: pgvector2.toSql(chunk.vector)
1470
1548
  })));
1549
+ await db2.from(this.getTableName()).where({ id }).update({
1550
+ embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
1551
+ }).returning("id");
1471
1552
  }
1472
1553
  return {
1473
1554
  id: result[0].id,
@@ -1497,7 +1578,7 @@ var ExuluContext = class {
1497
1578
  }
1498
1579
  }
1499
1580
  Object.keys(item).forEach((key) => {
1500
- 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") {
1501
1582
  return;
1502
1583
  }
1503
1584
  const field = this.fields.find((field2) => field2.name === key);
@@ -1548,6 +1629,9 @@ var ExuluContext = class {
1548
1629
  chunk_index: chunk.index,
1549
1630
  embedding: pgvector2.toSql(chunk.vector)
1550
1631
  })));
1632
+ await db2.from(this.getTableName()).where({ id: result[0].id }).update({
1633
+ embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
1634
+ }).returning("id");
1551
1635
  }
1552
1636
  return {
1553
1637
  id: result[0].id,
@@ -1557,6 +1641,8 @@ var ExuluContext = class {
1557
1641
  getItems = async ({
1558
1642
  statistics,
1559
1643
  limit,
1644
+ sort,
1645
+ order,
1560
1646
  page,
1561
1647
  name,
1562
1648
  archived,
@@ -1577,6 +1663,9 @@ var ExuluContext = class {
1577
1663
  const columns = await db2(mainTable).columnInfo();
1578
1664
  const totalQuery = db2.count("* as count").from(mainTable).first();
1579
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
+ }
1580
1669
  if (typeof name === "string") {
1581
1670
  itemsQuery.whereILike("name", `%${name}%`);
1582
1671
  totalQuery.whereILike("name", `%${name}%`);
@@ -1620,7 +1709,7 @@ var ExuluContext = class {
1620
1709
  }
1621
1710
  itemsQuery.limit(limit * 5);
1622
1711
  if (statistics) {
1623
- updateStatistic({
1712
+ await updateStatistic({
1624
1713
  name: "count",
1625
1714
  label: statistics.label,
1626
1715
  type: STATISTICS_TYPE_ENUM.CONTEXT_RETRIEVE,
@@ -1753,6 +1842,7 @@ var ExuluContext = class {
1753
1842
  table.text("external_id");
1754
1843
  table.integer("textLength");
1755
1844
  table.text("source");
1845
+ table.timestamp("embeddings_updated_at");
1756
1846
  for (const field of this.fields) {
1757
1847
  const { type, name } = field;
1758
1848
  if (!type || !name) {
@@ -1782,15 +1872,15 @@ var ExuluContext = class {
1782
1872
  id: this.id,
1783
1873
  name: `${this.name} context`,
1784
1874
  type: "context",
1785
- parameters: z.object({
1875
+ inputSchema: z.object({
1786
1876
  query: z.string()
1787
1877
  }),
1788
1878
  description: `Gets information from the context called: ${this.name}. The context description is: ${this.description}.`,
1789
- execute: async ({ context }) => {
1879
+ execute: async ({ query }) => {
1790
1880
  return await this.getItems({
1791
1881
  page: 1,
1792
1882
  limit: 10,
1793
- query: context.query,
1883
+ query,
1794
1884
  statistics: {
1795
1885
  label: this.name,
1796
1886
  trigger: "agent"
@@ -1839,7 +1929,32 @@ var ExuluSource = class {
1839
1929
  }
1840
1930
  };
1841
1931
  var updateStatistic = async (statistic) => {
1842
- 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
+ }
1843
1958
  };
1844
1959
 
1845
1960
  // src/registry/index.ts
@@ -1939,6 +2054,7 @@ var authentication = async ({
1939
2054
  }
1940
2055
  if (authtoken) {
1941
2056
  try {
2057
+ console.log("[EXULU] authtoken", authtoken);
1942
2058
  if (!authtoken?.email) {
1943
2059
  return {
1944
2060
  error: true,
@@ -2032,8 +2148,7 @@ var requestValidators = {
2032
2148
  const { db: db2 } = await postgresClient();
2033
2149
  let authtoken = null;
2034
2150
  if (typeof apikey !== "string") {
2035
- const secret = process.env.NEXTAUTH_SECRET;
2036
- authtoken = await getToken(req.headers["authorization"] ?? "");
2151
+ authtoken = await getToken((req.headers["authorization"] || req.headers["x-api-key"]) ?? "");
2037
2152
  }
2038
2153
  return await authentication({
2039
2154
  authtoken,
@@ -2226,6 +2341,42 @@ import "reflect-metadata";
2226
2341
  // src/registry/utils/graphql.ts
2227
2342
  import { makeExecutableSchema } from "@graphql-tools/schema";
2228
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
+ });
2229
2380
  var map = (field) => {
2230
2381
  let type;
2231
2382
  switch (field.type) {
@@ -2245,7 +2396,7 @@ var map = (field) => {
2245
2396
  type = "JSON";
2246
2397
  break;
2247
2398
  case "date":
2248
- type = "String";
2399
+ type = "Date";
2249
2400
  break;
2250
2401
  default:
2251
2402
  type = "String";
@@ -2263,8 +2414,8 @@ function createTypeDefs(table) {
2263
2414
  type ${table.name.singular} {
2264
2415
  ${fields.join("\n")}
2265
2416
  id: ID!
2266
- createdAt: String!
2267
- updatedAt: String!
2417
+ createdAt: Date!
2418
+ updatedAt: Date!
2268
2419
  }
2269
2420
  `;
2270
2421
  const inputDef = `
@@ -2290,6 +2441,11 @@ input FilterOperatorString {
2290
2441
  contains: String
2291
2442
  }
2292
2443
 
2444
+ input FilterOperatorDate {
2445
+ lte: Date
2446
+ gte: Date
2447
+ }
2448
+
2293
2449
  input FilterOperatorFloat {
2294
2450
  eq: Float
2295
2451
  ne: Float
@@ -2342,8 +2498,10 @@ function createMutations(table) {
2342
2498
  [`${tableNamePlural}CreateOne`]: async (_, args, context, info) => {
2343
2499
  const { db: db2 } = context;
2344
2500
  const requestedFields = getRequestedFields(info);
2501
+ let { input } = args;
2502
+ input = encryptSensitiveFields(input);
2345
2503
  const results = await db2(tableNamePlural).insert({
2346
- ...args.input,
2504
+ ...input,
2347
2505
  createdAt: /* @__PURE__ */ new Date(),
2348
2506
  updatedAt: /* @__PURE__ */ new Date()
2349
2507
  }).returning(requestedFields);
@@ -2351,7 +2509,8 @@ function createMutations(table) {
2351
2509
  },
2352
2510
  [`${tableNamePlural}UpdateOne`]: async (_, args, context, info) => {
2353
2511
  const { db: db2 } = context;
2354
- const { where, input } = args;
2512
+ let { where, input } = args;
2513
+ input = encryptSensitiveFields(input);
2355
2514
  await db2(tableNamePlural).where(where).update({
2356
2515
  ...input,
2357
2516
  updatedAt: /* @__PURE__ */ new Date()
@@ -2361,7 +2520,8 @@ function createMutations(table) {
2361
2520
  return result;
2362
2521
  },
2363
2522
  [`${tableNamePlural}UpdateOneById`]: async (_, args, context, info) => {
2364
- const { id, input } = args;
2523
+ let { id, input } = args;
2524
+ input = encryptSensitiveFields(input);
2365
2525
  const { db: db2 } = context;
2366
2526
  await db2(tableNamePlural).where({ id }).update({
2367
2527
  ...input,
@@ -2464,12 +2624,44 @@ function createQueries(table) {
2464
2624
  },
2465
2625
  items
2466
2626
  };
2467
- }
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
+ } : {}
2468
2659
  };
2469
2660
  }
2470
2661
  function createSDL(tables) {
2471
2662
  let typeDefs = `
2472
2663
  scalar JSON
2664
+ scalar Date
2473
2665
 
2474
2666
  type Query {
2475
2667
  `;
@@ -2477,7 +2669,7 @@ function createSDL(tables) {
2477
2669
  type Mutation {
2478
2670
  `;
2479
2671
  let modelDefs = "";
2480
- const resolvers = { JSON: GraphQLJSON, Query: {}, Mutation: {} };
2672
+ const resolvers = { JSON: GraphQLJSON, Date: GraphQLDate, Query: {}, Mutation: {} };
2481
2673
  for (const table of tables) {
2482
2674
  const tableNamePlural = table.name.plural.toLowerCase();
2483
2675
  const tableNameSingular = table.name.singular.toLowerCase();
@@ -2486,6 +2678,7 @@ function createSDL(tables) {
2486
2678
  ${tableNameSingular}ById(id: ID!): ${tableNameSingular}
2487
2679
  ${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
2488
2680
  ${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
2681
+ ${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
2489
2682
  `;
2490
2683
  mutationDefs += `
2491
2684
  ${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!): ${tableNameSingular}
@@ -2510,6 +2703,15 @@ type PageInfo {
2510
2703
  hasNextPage: Boolean!
2511
2704
  }
2512
2705
  `;
2706
+ if (tableNamePlural === "jobs") {
2707
+ modelDefs += `
2708
+ type JobStatistics {
2709
+ completedCount: Int!
2710
+ failedCount: Int!
2711
+ averageDuration: Float!
2712
+ }
2713
+ `;
2714
+ }
2513
2715
  Object.assign(resolvers.Query, createQueries(table));
2514
2716
  Object.assign(resolvers.Mutation, createMutations(table));
2515
2717
  }
@@ -2546,6 +2748,15 @@ type PageInfo {
2546
2748
  console.log("\n");
2547
2749
  return schema;
2548
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
+ };
2549
2760
 
2550
2761
  // src/registry/routes.ts
2551
2762
  import { expressMiddleware } from "@as-integrations/express5";
@@ -2944,6 +3155,34 @@ var createUppyRoutes = async (app) => {
2944
3155
  // src/registry/routes.ts
2945
3156
  import { InMemoryLRUCache } from "@apollo/utils.keyvaluecache";
2946
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
2947
3186
  var REQUEST_SIZE_LIMIT = "50mb";
2948
3187
  var global_queues = {
2949
3188
  logs_cleaner: "logs-cleaner"
@@ -2980,7 +3219,7 @@ var createRecurringJobs = async () => {
2980
3219
  console.table(recurringJobSchedulersLogs);
2981
3220
  return queue;
2982
3221
  };
2983
- var createExpressRoutes = async (app, agents, embedders, tools, workflows, contexts) => {
3222
+ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
2984
3223
  const routeLogs = [];
2985
3224
  var corsOptions = {
2986
3225
  origin: "*",
@@ -3000,6 +3239,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3000
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
3001
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
3002
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
3003
3243
 
3004
3244
  `);
3005
3245
  console.log("Agents:");
@@ -3048,7 +3288,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3048
3288
  } else {
3049
3289
  console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
3050
3290
  }
3051
- const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, threadsSchema, messagesSchema]);
3291
+ const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, agentSessionsSchema, agentMessagesSchema]);
3052
3292
  console.log("[EXULU] graphql server");
3053
3293
  const server = new ApolloServer({
3054
3294
  cache: new InMemoryLRUCache(),
@@ -3076,10 +3316,52 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3076
3316
  }
3077
3317
  })
3078
3318
  );
3079
- 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
+ }
3080
3325
  res.status(200).json(agents);
3081
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
+ });
3082
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
+ }
3083
3365
  const { db: db2 } = await postgresClient();
3084
3366
  const id = req.params.id;
3085
3367
  if (!id) {
@@ -3102,6 +3384,8 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3102
3384
  name: agent.name,
3103
3385
  id: agent.id,
3104
3386
  description: agent.description,
3387
+ provider: backend?.model?.provider,
3388
+ model: backend?.model?.modelId,
3105
3389
  active: agent.active,
3106
3390
  public: agent.public,
3107
3391
  type: agent.type,
@@ -3110,7 +3394,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3110
3394
  streaming: backend?.streaming,
3111
3395
  capabilities: backend?.capabilities,
3112
3396
  // todo add contexts
3113
- availableTools: backend?.tools,
3397
+ availableTools: tools,
3114
3398
  enabledTools: agent.tools
3115
3399
  }
3116
3400
  });
@@ -3121,8 +3405,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3121
3405
  name: tool2.name,
3122
3406
  description: tool2.description,
3123
3407
  type: tool2.type || "tool",
3124
- inputSchema: tool2.inputSchema ? zerialize(tool2.inputSchema) : null,
3125
- outputSchema: tool2.outputSchema ? zerialize(tool2.outputSchema) : null
3408
+ inputSchema: tool2.inputSchema ? zerialize(tool2.inputSchema) : null
3126
3409
  })));
3127
3410
  });
3128
3411
  app.get("/tools/:id", async (req, res) => {
@@ -3240,6 +3523,14 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3240
3523
  });
3241
3524
  return;
3242
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
+ }
3243
3534
  const item = await db2.from(context.getTableName()).where({ id: req.params.id }).select("*").first();
3244
3535
  if (!item) {
3245
3536
  res.status(404).json({
@@ -3361,6 +3652,20 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3361
3652
  }
3362
3653
  let limit = req.query.limit ? parseInt(req.query.limit) : 10;
3363
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
+ }
3364
3669
  const authenticationResult = await requestValidators.authenticate(req);
3365
3670
  if (!authenticationResult.user?.id) {
3366
3671
  res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
@@ -3384,6 +3689,8 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3384
3689
  return;
3385
3690
  }
3386
3691
  const result = await context.getItems({
3692
+ sort,
3693
+ order,
3387
3694
  page,
3388
3695
  limit,
3389
3696
  archived: req.query.archived === "true",
@@ -3463,6 +3770,18 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3463
3770
  message: "Embedding deleted."
3464
3771
  });
3465
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
+ });
3466
3785
  console.log("[EXULU] statistics timeseries");
3467
3786
  app.post("/statistics/timeseries", async (req, res) => {
3468
3787
  const authenticationResult = await requestValidators.authenticate(req);
@@ -3602,6 +3921,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3602
3921
  slug: "/contexts/" + context.id,
3603
3922
  active: context.active,
3604
3923
  fields: context.fields,
3924
+ configuration: context.configuration,
3605
3925
  sources: context.sources.get().map((source) => ({
3606
3926
  id: source.id,
3607
3927
  name: source.name,
@@ -3796,47 +4116,28 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3796
4116
  res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
3797
4117
  return;
3798
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);
3799
4122
  if (!!stream) {
3800
- const chatClient = await agent.chat(agentInstance.id);
3801
- if (!chatClient) {
3802
- res.status(500).json({
3803
- message: "Agent instantiation not successful."
3804
- });
3805
- return;
3806
- }
3807
- const { textStream } = await chatClient.stream(req.body.messages, {
3808
- threadId: `${req.body.threadId}`,
3809
- // conversation id
3810
- resourceId: `${req.body.resourceId}`,
3811
- // user id
3812
- ...agent.outputSchema && { output: agent.outputSchema },
3813
- maxRetries: 2,
3814
- // todo make part of ExuluAgent class
3815
- maxSteps: 5,
3816
- // todo make part of ExuluAgent class
3817
- onError: (error) => console.error("[EXULU] chat stream error.", error),
3818
- onFinish: ({ response, usage }) => console.info(
3819
- "[EXULU] chat stream finished.",
3820
- usage
3821
- )
4123
+ const result = agent.generateStream({
4124
+ messages: req.body.messages,
4125
+ tools: enabledTools,
4126
+ statistics: {
4127
+ label: agent.name,
4128
+ trigger: "agent"
4129
+ }
3822
4130
  });
3823
- for await (const delta of textStream) {
3824
- res.write(`data: ${delta}
3825
-
3826
- `);
3827
- }
3828
- res.end();
4131
+ result.pipeDataStreamToResponse(res);
3829
4132
  return;
3830
4133
  } else {
3831
- const response = await agent.chat.generate(req.body.messages, {
3832
- resourceId: `${authenticationResult.user.id}`,
3833
- output: agent.outputSchema,
3834
- threadId: `${req.body.threadId}`,
3835
- // conversation id
3836
- maxRetries: 2,
3837
- // todo make part of ExuluAgent class
3838
- maxSteps: 5
3839
- // 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
+ }
3840
4141
  });
3841
4142
  res.status(200).json(response);
3842
4143
  return;
@@ -3911,7 +4212,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3911
4212
  console.log("Routes:");
3912
4213
  console.table(routeLogs);
3913
4214
  const TARGET_API = "https://api.anthropic.com";
3914
- 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) => {
3915
4216
  const path3 = req.url;
3916
4217
  const url = `${TARGET_API}${path3}`;
3917
4218
  console.log("[PROXY] Manual proxy to:", url);
@@ -3922,57 +4223,100 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3922
4223
  console.log("[PROXY] Request stream:", req.body.stream);
3923
4224
  console.log("[PROXY] Request messages:", req.body.messages?.length);
3924
4225
  try {
3925
- const headers = {
3926
- "x-api-key": process.env.ANTHROPIC_API_KEY,
3927
- "anthropic-version": "2023-06-01",
3928
- "content-type": req.headers["content-type"] || "application/json"
3929
- };
3930
- if (req.headers["accept"]) headers["accept"] = req.headers["accept"];
3931
- if (req.headers["user-agent"]) headers["user-agent"] = req.headers["user-agent"];
3932
4226
  console.log("[PROXY] Request body tools array length:", req.body.tools?.length);
3933
4227
  if (!req.body.tools) {
3934
4228
  req.body.tools = [];
3935
4229
  }
3936
- if (req.headers["x-api-key"] === "PLACEHOLDER") {
3937
- res.write(JSON.stringify({
3938
- "type": "content_block_delta",
3939
- "index": 0,
3940
- "delta": {
3941
- "type": "text_delta",
3942
- "text": "Hello, world!"
3943
- }
3944
- }));
3945
- 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));
3946
4253
  return;
3947
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));
4259
+ return;
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);
3948
4271
  const response = await fetch(url, {
3949
4272
  method: req.method,
3950
4273
  headers,
3951
4274
  body: req.method !== "GET" ? JSON.stringify(req.body) : void 0
3952
4275
  });
3953
- 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
+ });
3954
4285
  response.headers.forEach((value, key) => {
3955
4286
  res.setHeader(key, value);
3956
4287
  });
3957
4288
  res.status(response.status);
3958
- 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) {
3959
4297
  const reader = response.body.getReader();
3960
4298
  const decoder = new TextDecoder();
3961
4299
  while (true) {
3962
4300
  const { done, value } = await reader.read();
3963
4301
  if (done) break;
3964
4302
  const chunk = decoder.decode(value, { stream: true });
4303
+ console.log("[PROXY] Chunk:", chunk);
3965
4304
  res.write(chunk);
3966
4305
  }
3967
4306
  res.end();
3968
- } else {
3969
- const data = await response.arrayBuffer();
3970
- res.end(Buffer.from(data));
4307
+ return;
3971
4308
  }
4309
+ const data = await response.arrayBuffer();
4310
+ console.log("[PROXY] Data:", data);
4311
+ res.end(Buffer.from(data));
3972
4312
  } catch (error) {
3973
4313
  console.error("[PROXY] Manual proxy error:", error);
3974
4314
  if (!res.headersSent) {
3975
- 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
+ }
3976
4320
  }
3977
4321
  }
3978
4322
  });
@@ -4020,6 +4364,20 @@ var getPresignedFileUrl = async (key) => {
4020
4364
  console.log(`[EXULU] presigned url for file with key: ${key}, generated: ${json.url}`);
4021
4365
  return json.url;
4022
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
+ };
4023
4381
 
4024
4382
  // src/registry/workers.ts
4025
4383
  import IORedis from "ioredis";
@@ -4072,7 +4430,7 @@ import * as fs2 from "fs";
4072
4430
  import path2 from "path";
4073
4431
  var defaultLogsDir = path2.join(process.cwd(), "logs");
4074
4432
  var redisConnection;
4075
- var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) => {
4433
+ var createWorkers = async (queues2, contexts, workflows, _logsDir) => {
4076
4434
  if (!redisServer.host || !redisServer.port) {
4077
4435
  console.error("[EXULU] you are trying to start workers, but no redis server is configured in the environment.");
4078
4436
  throw new Error("No redis server configured in the environment, so cannot start workers.");
@@ -4103,7 +4461,7 @@ var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) =>
4103
4461
  if (!bullmqJob.data.embedder) {
4104
4462
  throw new Error(`No embedder set for embedder job.`);
4105
4463
  }
4106
- const embedder = embedders.find((embedder2) => embedder2.id === bullmqJob.data.embedder);
4464
+ const embedder = contexts.find((context2) => context2.embedder?.id === bullmqJob.data.embedder);
4107
4465
  if (!embedder) {
4108
4466
  throw new Error(`Embedder ${bullmqJob.data.embedder} not found in the registry.`);
4109
4467
  }
@@ -4143,6 +4501,9 @@ var createWorkers = async (queues2, contexts, embedders, workflows, _logsDir) =>
4143
4501
  duration,
4144
4502
  result: JSON.stringify(result)
4145
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");
4146
4507
  return result;
4147
4508
  }
4148
4509
  if (bullmqJob.data.type === "workflow") {
@@ -4240,7 +4601,7 @@ var ExuluMCP = class {
4240
4601
  express;
4241
4602
  constructor() {
4242
4603
  }
4243
- create = async ({ express: express3, contexts, embedders, agents, workflows, config, tools }) => {
4604
+ create = async ({ express: express3, contexts, agents, workflows, config, tools }) => {
4244
4605
  this.express = express3;
4245
4606
  if (!this.server) {
4246
4607
  console.log("[EXULU] Creating MCP server.");
@@ -4371,28 +4732,210 @@ ${code}`
4371
4732
 
4372
4733
  // src/registry/index.ts
4373
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
4374
4903
  var ExuluApp = class {
4375
4904
  _agents = [];
4376
4905
  _workflows = [];
4377
4906
  _config;
4378
- _embedders = [];
4379
4907
  _queues = [];
4380
4908
  _contexts = {};
4381
4909
  _tools = [];
4382
4910
  _expressApp = null;
4383
4911
  constructor() {
4384
4912
  }
4385
- // Factory function so we can async initialize the
4386
- // MCP server if needed.
4387
- create = async ({ contexts, embedders, agents, workflows, config, tools }) => {
4388
- 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 }) => {
4389
4916
  this._workflows = workflows ?? [];
4390
4917
  this._contexts = contexts ?? {};
4391
- this._agents = agents ?? [];
4918
+ this._agents = [
4919
+ claudeCodeAgent,
4920
+ defaultAgent,
4921
+ ...agents ?? []
4922
+ ];
4392
4923
  this._config = config;
4393
- 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 || {});
4394
4937
  const queues2 = [
4395
- ...embedders?.length ? embedders.map((agent) => agent.queue?.name || null) : [],
4938
+ ...contextsArray?.length ? contextsArray.map((context) => context.embedder.queue?.name || null) : [],
4396
4939
  ...workflows?.length ? workflows.map((workflow) => workflow.queue?.name || null) : []
4397
4940
  ];
4398
4941
  this._queues = [...new Set(queues2.filter((o) => !!o))];
@@ -4409,9 +4952,6 @@ var ExuluApp = class {
4409
4952
  }
4410
4953
  return this._expressApp;
4411
4954
  }
4412
- embedder(id) {
4413
- return this._embedders.find((x) => x.id === id);
4414
- }
4415
4955
  tool(id) {
4416
4956
  return this._tools.find((x) => x.id === id);
4417
4957
  }
@@ -4427,9 +4967,6 @@ var ExuluApp = class {
4427
4967
  workflow(id) {
4428
4968
  return this._workflows.find((x) => x.id === id);
4429
4969
  }
4430
- get embedders() {
4431
- return this._embedders;
4432
- }
4433
4970
  get contexts() {
4434
4971
  return Object.values(this._contexts ?? {});
4435
4972
  }
@@ -4445,7 +4982,6 @@ var ExuluApp = class {
4445
4982
  return await createWorkers(
4446
4983
  this._queues,
4447
4984
  Object.values(this._contexts ?? {}),
4448
- this._embedders,
4449
4985
  this._workflows,
4450
4986
  this._config?.workers?.logsDir
4451
4987
  );
@@ -4462,7 +4998,6 @@ var ExuluApp = class {
4462
4998
  await createExpressRoutes(
4463
4999
  app,
4464
5000
  this._agents,
4465
- this._embedders,
4466
5001
  this._tools,
4467
5002
  this._workflows,
4468
5003
  Object.values(this._contexts ?? {})
@@ -4472,7 +5007,6 @@ var ExuluApp = class {
4472
5007
  await mcp.create({
4473
5008
  express: app,
4474
5009
  contexts: this._contexts,
4475
- embedders: this._embedders,
4476
5010
  agents: this._agents,
4477
5011
  workflows: this._workflows,
4478
5012
  config: this._config,
@@ -5680,198 +6214,6 @@ var SentenceChunker = class _SentenceChunker extends BaseChunker {
5680
6214
  }
5681
6215
  };
5682
6216
 
5683
- // src/cli/index.tsx
5684
- import { useState as useState2 } from "react";
5685
- import { Box as Box2, Text as Text4, render as render2 } from "ink";
5686
- import { UnorderedList as UnorderedList3 } from "@inkjs/ui";
5687
- import patchConsole from "patch-console";
5688
-
5689
- // src/cli/components/nav.tsx
5690
- import { Select } from "@inkjs/ui";
5691
- import { useApp } from "ink";
5692
- import { jsx } from "react/jsx-runtime";
5693
- var nav = [
5694
- {
5695
- label: "Agents",
5696
- value: "agents"
5697
- },
5698
- {
5699
- label: "Start Claude Code",
5700
- value: "claude-code"
5701
- },
5702
- {
5703
- label: "Exit",
5704
- value: "exit"
5705
- }
5706
- ];
5707
- var Nav = ({ setView }) => {
5708
- const { exit } = useApp();
5709
- return /* @__PURE__ */ jsx(Select, { options: nav, onChange: (value) => {
5710
- if (value === "exit") {
5711
- exit();
5712
- }
5713
- setView(value);
5714
- } });
5715
- };
5716
- var nav_default = Nav;
5717
-
5718
- // src/cli/components/agent-selector.tsx
5719
- import { Text as Text2 } from "ink";
5720
- import { Select as Select2 } from "@inkjs/ui";
5721
- import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
5722
- var AgentSelector = ({ exulu, setAgent, setEvaluations }) => {
5723
- const agents = exulu.agents.map((agent) => ({
5724
- label: agent.name,
5725
- value: agent.id
5726
- }));
5727
- return /* @__PURE__ */ jsxs(Fragment, { children: [
5728
- /* @__PURE__ */ jsx2(Text2, { children: "Please select an agent:" }),
5729
- /* @__PURE__ */ jsx2(Select2, { options: agents, onChange: (value) => {
5730
- const agent = exulu.agent(value);
5731
- if (!agent) {
5732
- console.error("Agent not found", value);
5733
- return;
5734
- }
5735
- setAgent(agent);
5736
- if (agent) {
5737
- setEvaluations(agent.evals || []);
5738
- }
5739
- } })
5740
- ] });
5741
- };
5742
- var agent_selector_default = AgentSelector;
5743
-
5744
- // src/cli/components/eval-selector.tsx
5745
- import { Select as Select3 } from "@inkjs/ui";
5746
- import { jsx as jsx3 } from "react/jsx-runtime";
5747
- var EvalSelector = ({ evaluations, setEvaluation }) => {
5748
- return /* @__PURE__ */ jsx3(Select3, { options: evaluations.map((evaluation) => ({
5749
- label: evaluation.runner.name,
5750
- value: evaluation.runner.name
5751
- })), onChange: (value) => {
5752
- const evaluation = evaluations?.find((evaluation2) => evaluation2.runner.name === value);
5753
- if (evaluation) {
5754
- setEvaluation(evaluation);
5755
- }
5756
- } });
5757
- };
5758
- var eval_selector_default = EvalSelector;
5759
-
5760
- // src/cli/components/eval-actions.tsx
5761
- import { useState } from "react";
5762
- import { ProgressBar as ProgressBar2, Select as Select4, UnorderedList as UnorderedList2 } from "@inkjs/ui";
5763
- import { Text as Text3 } from "ink";
5764
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
5765
- var EvalActions = ({ agent, evaluation, setEvaluation }) => {
5766
- const [progress, setProgress] = useState(0);
5767
- const [results, setResults] = useState([]);
5768
- const [running, setRunning] = useState();
5769
- const run = async (evaluation2) => {
5770
- setRunning({
5771
- label: evaluation2.runner.name
5772
- });
5773
- const testCases = evaluation2.runner.testcases;
5774
- const total = testCases.length;
5775
- if (!testCases) {
5776
- throw new Error("No test cases found");
5777
- }
5778
- let i = 0;
5779
- for (const testCase of testCases) {
5780
- i++;
5781
- const result = await evaluation2.runner.run({
5782
- data: testCase,
5783
- runner: {
5784
- agent
5785
- }
5786
- });
5787
- setProgress(Math.round(i / total * 100));
5788
- setResults([...results, {
5789
- name: evaluation2.runner.name,
5790
- prompt: testCase.prompt?.slice(0, 100) + "...",
5791
- score: result.score,
5792
- comment: result.comment
5793
- }]);
5794
- }
5795
- setRunning(void 0);
5796
- };
5797
- if (progress === 100) {
5798
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
5799
- /* @__PURE__ */ jsx4(Text3, { children: "Evaluations completed." }),
5800
- /* @__PURE__ */ jsx4(UnorderedList2, { children: results.map((result) => /* @__PURE__ */ jsx4(UnorderedList2.Item, { children: /* @__PURE__ */ jsxs2(Text3, { children: [
5801
- result.name,
5802
- ": ",
5803
- result.score,
5804
- " - ",
5805
- result.comment
5806
- ] }) })) })
5807
- ] });
5808
- }
5809
- if (running) {
5810
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
5811
- /* @__PURE__ */ jsxs2(Text3, { children: [
5812
- "Running ",
5813
- running.label,
5814
- "..."
5815
- ] }),
5816
- /* @__PURE__ */ jsx4(ProgressBar2, { value: progress })
5817
- ] });
5818
- }
5819
- return /* @__PURE__ */ jsx4(Select4, { options: [{
5820
- label: "Run evaluation",
5821
- value: "run"
5822
- }, {
5823
- label: "Go back",
5824
- value: "back"
5825
- }], onChange: (value) => {
5826
- if (value === "back") {
5827
- setEvaluation(void 0);
5828
- }
5829
- if (value === "run") {
5830
- run(evaluation);
5831
- }
5832
- } });
5833
- };
5834
- var eval_actions_default = EvalActions;
5835
-
5836
- // src/cli/index.tsx
5837
- import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
5838
- var Main = ({ exulu }) => {
5839
- patchConsole((stream, data) => {
5840
- setLogs([...logs, data]);
5841
- });
5842
- const [logs, setLogs] = useState2([]);
5843
- const [view, setView] = useState2();
5844
- const [agent, setAgent] = useState2();
5845
- const [evaluations, setEvaluations] = useState2([]);
5846
- const [evaluation, setEvaluation] = useState2();
5847
- return /* @__PURE__ */ jsxs3(Box2, { borderStyle: "round", borderColor: "cyan", padding: 1, flexDirection: "column", width: "70%", children: [
5848
- /* @__PURE__ */ jsx5(Text4, { children: "Logs:" }),
5849
- /* @__PURE__ */ jsx5(UnorderedList3, { children: logs.map((log, index) => /* @__PURE__ */ jsx5(UnorderedList3.Item, { children: /* @__PURE__ */ jsx5(Text4, { children: log }) })) }),
5850
- !view && /* @__PURE__ */ jsx5(nav_default, { setView }),
5851
- view === "agents" && !agent && /* @__PURE__ */ jsx5(agent_selector_default, { exulu, setAgent, setEvaluations }),
5852
- view === "agents" && agent && !evaluation && /* @__PURE__ */ jsxs3(Fragment3, { children: [
5853
- /* @__PURE__ */ jsxs3(Text4, { children: [
5854
- 'Selected agent "',
5855
- agent.name,
5856
- '". Please select an evaluation:'
5857
- ] }),
5858
- /* @__PURE__ */ jsx5(eval_selector_default, { evaluations, setEvaluation })
5859
- ] }),
5860
- view === "agents" && agent && evaluation && /* @__PURE__ */ jsxs3(Fragment3, { children: [
5861
- /* @__PURE__ */ jsxs3(Text4, { children: [
5862
- "Selected evaluation: ",
5863
- evaluation.runner.name
5864
- ] }),
5865
- /* @__PURE__ */ jsx5(eval_actions_default, { agent, evaluation, setEvaluation })
5866
- ] })
5867
- ] });
5868
- };
5869
- var cli_default = {
5870
- run: (exulu) => {
5871
- render2(/* @__PURE__ */ jsx5(Main, { exulu }));
5872
- }
5873
- };
5874
-
5875
6217
  // src/index.ts
5876
6218
  var ExuluJobs = {
5877
6219
  redis: redisClient,
@@ -5901,7 +6243,6 @@ export {
5901
6243
  ExuluApp,
5902
6244
  authentication as ExuluAuthentication,
5903
6245
  ExuluChunkers,
5904
- cli_default as ExuluCli,
5905
6246
  ExuluContext,
5906
6247
  ExuluDatabase,
5907
6248
  ExuluEmbedder,