@exulu/backend 1.38.0 → 1.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -219,11 +219,17 @@ async function postgresClient() {
219
219
  pool: {
220
220
  min: 2,
221
221
  max: 20,
222
+ // Increased from 20 to handle more concurrent operations
222
223
  acquireTimeoutMillis: 3e4,
223
224
  createTimeoutMillis: 3e4,
224
225
  idleTimeoutMillis: 3e4,
225
226
  reapIntervalMillis: 1e3,
226
- createRetryIntervalMillis: 200
227
+ createRetryIntervalMillis: 200,
228
+ // Log pool events to help debug connection issues
229
+ afterCreate: (conn, done) => {
230
+ console.log("[EXULU] New database connection created");
231
+ done(null, conn);
232
+ }
227
233
  }
228
234
  });
229
235
  try {
@@ -504,6 +510,8 @@ var authentication = async ({
504
510
  type: "api",
505
511
  id: 192837465,
506
512
  email: "internal@exulu.com",
513
+ firstname: "API",
514
+ lastname: "User",
507
515
  role: {
508
516
  id: "internal",
509
517
  name: "Internal",
@@ -1337,10 +1345,10 @@ var rbacSchema = {
1337
1345
  name: "user_id",
1338
1346
  type: "number"
1339
1347
  },
1340
- {
1341
- name: "project_id",
1342
- type: "uuid"
1343
- },
1348
+ /* {
1349
+ name: "project_id",
1350
+ type: "uuid"
1351
+ }, */
1344
1352
  {
1345
1353
  name: "rights",
1346
1354
  type: "text",
@@ -2250,7 +2258,7 @@ function createMutations(table, agents, contexts, tools, config) {
2250
2258
  });
2251
2259
  return {
2252
2260
  // Filter result to only include requested fields
2253
- item: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result: results[0], user: context.user }),
2261
+ item: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result: results[0], user: context.user }),
2254
2262
  job
2255
2263
  };
2256
2264
  },
@@ -2298,7 +2306,7 @@ function createMutations(table, agents, contexts, tools, config) {
2298
2306
  }
2299
2307
  const { job } = await postprocessUpdate({ table, requestedFields, agents, contexts, tools, result, user: context.user.id, role: context.user.role?.id, config });
2300
2308
  return {
2301
- item: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
2309
+ item: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
2302
2310
  job
2303
2311
  };
2304
2312
  },
@@ -2339,7 +2347,7 @@ function createMutations(table, agents, contexts, tools, config) {
2339
2347
  const result = await db3.from(tableNamePlural).select(Object.keys(columns)).where({ id }).first();
2340
2348
  const { job } = await postprocessUpdate({ table, requestedFields, agents, contexts, tools, result, user: context.user.id, role: context.user.role?.id, config });
2341
2349
  return {
2342
- item: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
2350
+ item: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id }),
2343
2351
  job
2344
2352
  };
2345
2353
  },
@@ -2371,7 +2379,7 @@ function createMutations(table, agents, contexts, tools, config) {
2371
2379
  }).del();
2372
2380
  }
2373
2381
  await postprocessDeletion({ table, requestedFields, agents, contexts, tools, result });
2374
- return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id });
2382
+ return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id });
2375
2383
  },
2376
2384
  [`${tableNamePlural}RemoveOne`]: async (_, args, context, info) => {
2377
2385
  const { where } = args;
@@ -2395,7 +2403,7 @@ function createMutations(table, agents, contexts, tools, config) {
2395
2403
  }
2396
2404
  await db3(tableNamePlural).where(where).del();
2397
2405
  await postprocessDeletion({ table, requestedFields, agents, contexts, tools, result });
2398
- return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user.id });
2406
+ return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user.id });
2399
2407
  }
2400
2408
  };
2401
2409
  if (table.type === "items") {
@@ -2428,20 +2436,20 @@ function createMutations(table, agents, contexts, tools, config) {
2428
2436
  }
2429
2437
  const { db: db3 } = context;
2430
2438
  let query = db3.from(tableNamePlural).select("*").where({ id: args.item });
2431
- query = applyAccessControl(table, context.user, query);
2439
+ query = applyAccessControl(table, query, context.user);
2432
2440
  const item = await query.first();
2433
2441
  if (!item) {
2434
2442
  throw new Error("Item not found, or your user does not have access to it.");
2435
2443
  }
2436
2444
  const { job, result } = await exists.processField(
2437
2445
  "api",
2438
- context.user.id,
2439
- context.user.role?.id,
2440
2446
  {
2441
2447
  ...item,
2442
2448
  field: args.field
2443
2449
  },
2444
- config
2450
+ config,
2451
+ context.user.id,
2452
+ context.user.role?.id
2445
2453
  );
2446
2454
  return {
2447
2455
  message: job ? "Processing job scheduled." : "Item processed successfully.",
@@ -2488,7 +2496,10 @@ function createMutations(table, agents, contexts, tools, config) {
2488
2496
  };
2489
2497
  }
2490
2498
  console.log("[EXULU] Executing source function directly");
2491
- const result = await source.execute(args.inputs);
2499
+ const result = await source.execute({
2500
+ ...args.inputs,
2501
+ exuluConfig: config
2502
+ });
2492
2503
  let jobs = [];
2493
2504
  let items = [];
2494
2505
  for (const item of result) {
@@ -2612,14 +2623,14 @@ function createMutations(table, agents, contexts, tools, config) {
2612
2623
  }
2613
2624
  return mutations;
2614
2625
  }
2615
- var applyAccessControl = (table, user, query) => {
2626
+ var applyAccessControl = (table, query, user) => {
2616
2627
  const tableNamePlural = table.name.plural.toLowerCase();
2617
- if (table.name.plural !== "agent_sessions" && user.super_admin === true) {
2628
+ if (table.name.plural !== "agent_sessions" && user?.super_admin === true) {
2618
2629
  return query;
2619
2630
  }
2620
- console.log("[EXULU] user.role", user.role);
2631
+ console.log("[EXULU] user.role", user?.role);
2621
2632
  console.log("[EXULU] table.name.plural", table.name.plural);
2622
- if (!user.super_admin && (!user.role || !(table.name.plural === "agents" && (user.role.agents === "read" || user.role.agents === "write")) && !(table.name.plural === "workflow_templates" && (user.role.workflows === "read" || user.role.workflows === "write")) && !(table.name.plural === "variables" && (user.role.variables === "read" || user.role.variables === "write")) && !(table.name.plural === "users" && (user.role.users === "read" || user.role.users === "write")) && !((table.name.plural === "test_cases" || table.name.plural === "eval_sets" || table.name.plural === "eval_runs") && (user.role.evals === "read" || user.role.evals === "write")))) {
2633
+ if (!user?.super_admin && (!user?.role || !(table.name.plural === "agents" && (user.role.agents === "read" || user.role.agents === "write")) && !(table.name.plural === "workflow_templates" && (user.role.workflows === "read" || user.role.workflows === "write")) && !(table.name.plural === "variables" && (user.role.variables === "read" || user.role.variables === "write")) && !(table.name.plural === "users" && (user.role.users === "read" || user.role.users === "write")) && !((table.name.plural === "test_cases" || table.name.plural === "eval_sets" || table.name.plural === "eval_runs") && (user.role.evals === "read" || user.role.evals === "write")))) {
2623
2634
  console.error("==== Access control error: no role found or no access to entity type. ====");
2624
2635
  throw new Error("Access control error: no role found or no access to entity type.");
2625
2636
  }
@@ -2708,7 +2719,7 @@ var removeAgentFields = (requestedFields) => {
2708
2719
  filtered.push("backend");
2709
2720
  return filtered;
2710
2721
  };
2711
- var addAgentFields = async (requestedFields, agents, result, tools, user) => {
2722
+ var addAgentFields = async (args, requestedFields, agents, result, tools, user, contexts) => {
2712
2723
  let backend = agents.find((a) => a.id === result?.backend);
2713
2724
  if (requestedFields.includes("providerName")) {
2714
2725
  result.providerName = backend?.providerName || "";
@@ -2755,6 +2766,17 @@ var addAgentFields = async (requestedFields, agents, result, tools, user) => {
2755
2766
  console.log("[EXULU] hydratedTool", hydratedTool);
2756
2767
  return hydratedTool;
2757
2768
  }));
2769
+ if (args.project) {
2770
+ const projectTool = await createProjectRetrievalTool({
2771
+ projectId: args.project,
2772
+ user,
2773
+ role: user.role?.id,
2774
+ contexts
2775
+ });
2776
+ if (projectTool) {
2777
+ result.tools.unshift(projectTool);
2778
+ }
2779
+ }
2758
2780
  result.tools = result.tools.filter((tool2) => tool2 !== null);
2759
2781
  } else {
2760
2782
  result.tools = [];
@@ -2893,6 +2915,7 @@ var postprocessDeletion = async ({
2893
2915
  return result;
2894
2916
  };
2895
2917
  var finalizeRequestedFields = async ({
2918
+ args,
2896
2919
  table,
2897
2920
  requestedFields,
2898
2921
  agents,
@@ -2909,11 +2932,28 @@ var finalizeRequestedFields = async ({
2909
2932
  }
2910
2933
  if (Array.isArray(result)) {
2911
2934
  result = result.map((item) => {
2912
- return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result: item, user });
2935
+ return finalizeRequestedFields({
2936
+ args,
2937
+ table,
2938
+ requestedFields,
2939
+ agents,
2940
+ contexts,
2941
+ tools,
2942
+ result: item,
2943
+ user
2944
+ });
2913
2945
  });
2914
2946
  } else {
2915
2947
  if (table.name.singular === "agent") {
2916
- result = await addAgentFields(requestedFields, agents, result, tools, user);
2948
+ result = await addAgentFields(
2949
+ args,
2950
+ requestedFields,
2951
+ agents,
2952
+ result,
2953
+ tools,
2954
+ user,
2955
+ contexts
2956
+ );
2917
2957
  if (!requestedFields.includes("backend")) {
2918
2958
  delete result.backend;
2919
2959
  }
@@ -2990,18 +3030,18 @@ function createQueries(table, agents, tools, contexts) {
2990
3030
  const requestedFields = getRequestedFields(info);
2991
3031
  const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
2992
3032
  let query = db3.from(tableNamePlural).select(sanitizedFields).where({ id: args.id });
2993
- query = applyAccessControl(table, context.user, query);
3033
+ query = applyAccessControl(table, query, context.user);
2994
3034
  let result = await query.first();
2995
- return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
3035
+ return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
2996
3036
  },
2997
3037
  [`${tableNameSingular}ByIds`]: async (_, args, context, info) => {
2998
3038
  const { db: db3 } = context;
2999
3039
  const requestedFields = getRequestedFields(info);
3000
3040
  const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
3001
3041
  let query = db3.from(tableNamePlural).select(sanitizedFields).whereIn("id", args.ids);
3002
- query = applyAccessControl(table, context.user, query);
3042
+ query = applyAccessControl(table, query, context.user);
3003
3043
  let result = await query;
3004
- return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
3044
+ return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
3005
3045
  },
3006
3046
  [`${tableNameSingular}One`]: async (_, args, context, info) => {
3007
3047
  const { filters = [], sort } = args;
@@ -3010,10 +3050,10 @@ function createQueries(table, agents, tools, contexts) {
3010
3050
  const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
3011
3051
  let query = db3.from(tableNamePlural).select(sanitizedFields);
3012
3052
  query = applyFilters(query, filters, table);
3013
- query = applyAccessControl(table, context.user, query);
3053
+ query = applyAccessControl(table, query, context.user);
3014
3054
  query = applySorting(query, sort);
3015
3055
  let result = await query.first();
3016
- return finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result, user: context.user });
3056
+ return finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result, user: context.user });
3017
3057
  },
3018
3058
  [`${tableNamePlural}Pagination`]: async (_, args, context, info) => {
3019
3059
  const { limit = 10, page = 0, filters = [], sort } = args;
@@ -3023,7 +3063,7 @@ function createQueries(table, agents, tools, contexts) {
3023
3063
  }
3024
3064
  let countQuery = db3(tableNamePlural);
3025
3065
  countQuery = applyFilters(countQuery, filters, table);
3026
- countQuery = applyAccessControl(table, context.user, countQuery);
3066
+ countQuery = applyAccessControl(table, countQuery, context.user);
3027
3067
  const countResult = await countQuery.count("* as count");
3028
3068
  const itemCount = Number(countResult[0]?.count || 0);
3029
3069
  const pageCount = Math.ceil(itemCount / limit);
@@ -3032,7 +3072,7 @@ function createQueries(table, agents, tools, contexts) {
3032
3072
  const hasNextPage = currentPage < pageCount - 1;
3033
3073
  let dataQuery = db3(tableNamePlural);
3034
3074
  dataQuery = applyFilters(dataQuery, filters, table);
3035
- dataQuery = applyAccessControl(table, context.user, dataQuery);
3075
+ dataQuery = applyAccessControl(table, dataQuery, context.user);
3036
3076
  const requestedFields = getRequestedFields(info);
3037
3077
  dataQuery = applySorting(dataQuery, sort);
3038
3078
  if (page > 1) {
@@ -3048,7 +3088,7 @@ function createQueries(table, agents, tools, contexts) {
3048
3088
  hasPreviousPage,
3049
3089
  hasNextPage
3050
3090
  },
3051
- items: finalizeRequestedFields({ table, requestedFields, agents, contexts, tools, result: items, user: context.user })
3091
+ items: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result: items, user: context.user })
3052
3092
  };
3053
3093
  },
3054
3094
  // Add generic statistics query for all tables
@@ -3057,7 +3097,7 @@ function createQueries(table, agents, tools, contexts) {
3057
3097
  const { db: db3 } = context;
3058
3098
  let query = db3(tableNamePlural);
3059
3099
  query = applyFilters(query, filters, table);
3060
- query = applyAccessControl(table, context.user, query);
3100
+ query = applyAccessControl(table, query, context.user);
3061
3101
  if (groupBy) {
3062
3102
  query = query.select(groupBy).groupBy(groupBy);
3063
3103
  if (tableNamePlural === "tracking") {
@@ -3157,11 +3197,11 @@ var vectorSearch = async ({
3157
3197
  const chunksTable = getChunksTableName(id);
3158
3198
  let countQuery = db3(mainTable);
3159
3199
  countQuery = applyFilters(countQuery, filters, table);
3160
- countQuery = applyAccessControl(table, user, countQuery);
3200
+ countQuery = applyAccessControl(table, countQuery, user);
3161
3201
  const columns = await db3(mainTable).columnInfo();
3162
3202
  let itemsQuery = db3(mainTable).select(Object.keys(columns).map((column) => mainTable + "." + column));
3163
3203
  itemsQuery = applyFilters(itemsQuery, filters, table);
3164
- itemsQuery = applyAccessControl(table, user, itemsQuery);
3204
+ itemsQuery = applyAccessControl(table, itemsQuery, user);
3165
3205
  itemsQuery = applySorting(itemsQuery, sort);
3166
3206
  if (queryRewriter) {
3167
3207
  query = await queryRewriter(query);
@@ -3180,7 +3220,7 @@ var vectorSearch = async ({
3180
3220
  const { chunks } = await embedder.generateFromQuery(context.id, query, {
3181
3221
  label: table.name.singular,
3182
3222
  trigger
3183
- }, user.id, role);
3223
+ }, user?.id, role);
3184
3224
  if (!chunks?.[0]?.vector) {
3185
3225
  throw new Error("No vector generated for query.");
3186
3226
  }
@@ -3570,7 +3610,8 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
3570
3610
  const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
3571
3611
  const processorFields = table.fields.filter((field) => field.processor?.execute);
3572
3612
  typeDefs += `
3573
- ${tableNameSingular}ById(id: ID!): ${tableNameSingular}
3613
+ ${tableNameSingular === "agent" ? `${tableNameSingular}ById(id: ID!, project: ID): ${tableNameSingular}` : `${tableNameSingular}ById(id: ID!): ${tableNameSingular}`}
3614
+
3574
3615
  ${tableNameSingular}ByIds(ids: [ID!]!): [${tableNameSingular}]!
3575
3616
  ${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
3576
3617
  ${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
@@ -4483,16 +4524,27 @@ var addPrefixToKey = (keyPath, config) => {
4483
4524
  }
4484
4525
  return `${prefix}/${keyPath}`;
4485
4526
  };
4486
- var uploadFile = async (user, file, key, config, options = {}) => {
4487
- console.log("[EXULU] Uploading file to S3", key);
4527
+ var uploadFile = async (file, key, config, options = {}, user) => {
4488
4528
  if (!config.fileUploads) {
4489
- throw new Error("File uploads are not configured");
4529
+ throw new Error("File uploads are not configured (in the exported uploadFile function)");
4490
4530
  }
4491
4531
  const client2 = getS3Client(config);
4492
- let folder = `${user}/`;
4532
+ let defaultBucket = config.fileUploads.s3Bucket;
4533
+ let customBucket = false;
4534
+ if (key.includes("[bucket:")) {
4535
+ console.log("[EXULU] key includes [bucket:name]", key);
4536
+ customBucket = key.split("[bucket:")[1]?.split("]")[0] || "";
4537
+ if (!customBucket?.length) {
4538
+ throw new Error("Invalid key, does not contain a bucket name like '[bucket:name]'.");
4539
+ }
4540
+ key = key.split("]")[1] || "";
4541
+ console.log("[EXULU] custom bucket", customBucket);
4542
+ }
4543
+ let folder = user ? `${user}/` : "";
4493
4544
  const fullKey = addPrefixToKey(!key.includes(folder) ? folder + key : key, config);
4545
+ console.log("[EXULU] uploading file to s3 into bucket", customBucket || defaultBucket, "with key", fullKey);
4494
4546
  const command = new import_client_s3.PutObjectCommand({
4495
- Bucket: config.fileUploads.s3Bucket,
4547
+ Bucket: customBucket || defaultBucket,
4496
4548
  Key: fullKey,
4497
4549
  Body: file,
4498
4550
  ContentType: options.contentType,
@@ -4500,6 +4552,10 @@ var uploadFile = async (user, file, key, config, options = {}) => {
4500
4552
  ContentLength: file.byteLength
4501
4553
  });
4502
4554
  await client2.send(command);
4555
+ console.log("[EXULU] file uploaded to s3 into bucket", customBucket || defaultBucket, "with key", fullKey);
4556
+ if (customBucket) {
4557
+ return "[bucket:" + customBucket + "]" + fullKey;
4558
+ }
4503
4559
  return fullKey;
4504
4560
  };
4505
4561
  var createUppyRoutes = async (app, config) => {
@@ -4658,16 +4714,12 @@ var createUppyRoutes = async (app, config) => {
4658
4714
  key = key.split("]")[1] || "";
4659
4715
  console.log("[EXULU] key", key);
4660
4716
  }
4661
- console.log("[EXULU] Getting object metadata from s3", key);
4662
- console.log("[EXULU] bucket", bucket);
4663
- console.log("[EXULU] key", key);
4664
4717
  const client2 = getS3Client(config);
4665
4718
  const command = new import_client_s3.HeadObjectCommand({
4666
4719
  Bucket: bucket,
4667
4720
  Key: key
4668
4721
  });
4669
4722
  const response = await client2.send(command);
4670
- console.log("[EXULU] Object metadata from s3", response);
4671
4723
  res.json(response);
4672
4724
  res.end();
4673
4725
  });
@@ -4982,12 +5034,117 @@ function sanitizeToolName(name) {
4982
5034
  }
4983
5035
  return sanitized;
4984
5036
  }
4985
- var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providerapikey, contexts, user, exuluConfig, sessionID, req) => {
5037
+ var projectsCache = /* @__PURE__ */ new Map();
5038
+ var createProjectRetrievalTool = async ({
5039
+ user,
5040
+ role,
5041
+ contexts,
5042
+ projectId
5043
+ }) => {
5044
+ let project;
5045
+ const cachedProject = projectsCache.get(projectId);
5046
+ const OneMinuteAgo = new Date(Date.now() - 1e3 * 60);
5047
+ if (cachedProject && cachedProject.age > OneMinuteAgo) {
5048
+ project = cachedProject.project;
5049
+ } else {
5050
+ const { db: db3 } = await postgresClient();
5051
+ project = await db3.from("projects").where("id", projectId).first();
5052
+ if (project) {
5053
+ projectsCache.set(projectId, {
5054
+ age: /* @__PURE__ */ new Date(),
5055
+ project
5056
+ });
5057
+ } else {
5058
+ return;
5059
+ }
5060
+ }
5061
+ console.log("[EXULU] Project search tool created for project", project);
5062
+ if (!project.project_items?.length) {
5063
+ return;
5064
+ }
5065
+ const projectRetrievalTool = new ExuluTool2({
5066
+ id: "project_information_retrieval_tool_" + projectId,
5067
+ name: "Project information retrieval tool for project " + project.name,
5068
+ description: "This tool retrieves information about a project from conversations and items that were added to the project " + project.name + ".",
5069
+ inputSchema: import_zod.z.object({
5070
+ query: import_zod.z.string().describe("The query to retrieve information about the project " + project.name + "."),
5071
+ keywords: import_zod.z.array(import_zod.z.string()).describe("The most relevant keywords in the query, such as names of people, companies, products, etc. in the project " + project.name + ".")
5072
+ }),
5073
+ type: "function",
5074
+ category: "project",
5075
+ config: [],
5076
+ execute: async ({ query, keywords }) => {
5077
+ console.log("[EXULU] Project search tool searching for project", project);
5078
+ const items = project.project_items;
5079
+ const set = {};
5080
+ for (const item of items) {
5081
+ const context = item.split("/")[0];
5082
+ if (!context) {
5083
+ throw new Error("The item added to the project does not have a valid gid with the context id as the prefix before the first slash.");
5084
+ }
5085
+ const id = item.split("/")[1];
5086
+ if (set[context]) {
5087
+ set[context].push(id);
5088
+ } else {
5089
+ set[context] = [id];
5090
+ }
5091
+ }
5092
+ console.log("[EXULU] Project search tool searching through contexts", Object.keys(set));
5093
+ const results = await Promise.all(Object.keys(set).map(async (contextName, index) => {
5094
+ const context = contexts.find((context2) => context2.id === contextName);
5095
+ if (!context) {
5096
+ console.error("[EXULU] Context not found for project information retrieval tool.", contextName);
5097
+ return [];
5098
+ }
5099
+ const itemIds = set[contextName];
5100
+ console.log("[EXULU] Project search tool searching through items", itemIds);
5101
+ return await context.search({
5102
+ // todo check if it is more performant to use a concatenation of
5103
+ // the query and keywords, or just the keywords, instead of the
5104
+ // query itself.
5105
+ query,
5106
+ filters: [{
5107
+ id: {
5108
+ in: itemIds
5109
+ }
5110
+ }],
5111
+ user,
5112
+ role,
5113
+ method: "hybridSearch",
5114
+ sort: {
5115
+ field: "updatedAt",
5116
+ direction: "desc"
5117
+ },
5118
+ trigger: "tool",
5119
+ limit: 10,
5120
+ page: 1
5121
+ });
5122
+ }));
5123
+ console.log("[EXULU] Project search tool results", results);
5124
+ return {
5125
+ result: JSON.stringify(results.flat())
5126
+ };
5127
+ }
5128
+ });
5129
+ return projectRetrievalTool;
5130
+ };
5131
+ var convertToolsArrayToObject = async (currentTools, allExuluTools, configs, providerapikey, contexts, user, exuluConfig, sessionID, req, project) => {
4986
5132
  if (!currentTools) return {};
4987
5133
  if (!allExuluTools) return {};
4988
5134
  if (!contexts) {
4989
5135
  contexts = [];
4990
5136
  }
5137
+ if (project) {
5138
+ const projectRetrievalTool = await createProjectRetrievalTool({
5139
+ user,
5140
+ role: user?.role?.id,
5141
+ contexts,
5142
+ projectId: project
5143
+ });
5144
+ if (projectRetrievalTool) {
5145
+ currentTools.push(projectRetrievalTool);
5146
+ }
5147
+ }
4991
5148
  const sanitizedTools = currentTools ? currentTools.map((tool2) => ({
4992
5149
  ...tool2,
4993
5150
  name: sanitizeToolName(tool2.name)
@@ -4996,9 +5153,9 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
4996
5153
  return {
4997
5154
  ...sanitizedTools?.reduce(
4998
5155
  (prev, cur) => {
4999
- let config = configs?.find((config2) => config2.id === cur.id);
5000
- const userDefinedConfigDescription = config?.config.find((config2) => config2.name === "description")?.value;
5001
- const defaultConfigDescription = config?.config.find((config2) => config2.name === "description")?.default;
5156
+ let toolVariableConfig = configs?.find((config) => config.id === cur.id);
5157
+ const userDefinedConfigDescription = toolVariableConfig?.config.find((config) => config.name === "description")?.value;
5158
+ const defaultConfigDescription = toolVariableConfig?.config.find((config) => config.name === "description")?.default;
5002
5159
  const toolDescription = cur.description;
5003
5160
  const description = userDefinedConfigDescription || defaultConfigDescription || toolDescription;
5004
5161
  return {
@@ -5011,8 +5168,8 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
5011
5168
  console.error("[EXULU] Tool execute function is undefined.", cur.tool);
5012
5169
  throw new Error("Tool execute function is undefined.");
5013
5170
  }
5014
- if (config) {
5015
- config = await hydrateVariables(config || []);
5171
+ if (toolVariableConfig) {
5172
+ toolVariableConfig = await hydrateVariables(toolVariableConfig || []);
5016
5173
  }
5017
5174
  let upload = void 0;
5018
5175
  if (exuluConfig?.fileUploads?.s3endpoint && exuluConfig?.fileUploads?.s3key && exuluConfig?.fileUploads?.s3secret && exuluConfig?.fileUploads?.s3Bucket) {
@@ -5065,7 +5222,6 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
5065
5222
  acc[curr.id] = curr;
5066
5223
  return acc;
5067
5224
  }, {});
5068
- console.log("[EXULU] Config", config);
5069
5225
  const response = await cur.tool.execute({
5070
5226
  ...inputs,
5071
5227
  sessionID,
@@ -5080,7 +5236,7 @@ var convertToolsArrayToObject = (currentTools, allExuluTools, configs, providera
5080
5236
  contexts: contextsMap,
5081
5237
  upload,
5082
5238
  exuluConfig,
5083
- config: config ? config.config.reduce((acc, curr) => {
5239
+ toolVariablesConfig: toolVariableConfig ? toolVariableConfig.config.reduce((acc, curr) => {
5084
5240
  acc[curr.name] = curr.value;
5085
5241
  return acc;
5086
5242
  }, {}) : {}
@@ -5114,7 +5270,6 @@ var hydrateVariables = async (tool2) => {
5114
5270
  const variableName = toolConfig.variable;
5115
5271
  const variable = await db3.from("variables").where({ name: variableName }).first();
5116
5272
  if (!variable) {
5117
- console.error("[EXULU] Variable " + variableName + " not found.");
5118
5273
  throw new Error("Variable " + variableName + " not found.");
5119
5274
  }
5120
5275
  let value = variable.value;
@@ -5126,7 +5281,6 @@ var hydrateVariables = async (tool2) => {
5126
5281
  return toolConfig;
5127
5282
  });
5128
5283
  await Promise.all(promises);
5129
- console.log("[EXULU] Variable values retrieved and added to tool config.");
5130
5284
  return tool2;
5131
5285
  };
5132
5286
  function generateSlug(name) {
@@ -5350,7 +5504,21 @@ var ExuluAgent2 = class {
5350
5504
  });
5351
5505
  }
5352
5506
  console.log("[EXULU] Message count for agent: " + this.name, "loaded for generating sync.", messages.length);
5353
- const genericContext = "IMPORTANT: \n\n The current date is " + (/* @__PURE__ */ new Date()).toLocaleDateString() + " and the current time is " + (/* @__PURE__ */ new Date()).toLocaleTimeString() + ". If the user does not explicitly provide the current date, for examle when saying ' this weekend', you should assume they are talking with the current date in mind as a reference.";
5507
+ let project;
5508
+ if (session) {
5509
+ const sessionData = await getSession({ sessionID: session });
5510
+ project = sessionData.project;
5511
+ }
5512
+ const personalizationInformation = exuluConfig?.privacy?.systemPromptPersonalization !== false ? `
5513
+ ${user?.firstname ? `The users first name is "${user.firstname}"` : ""}
5514
+ ${user?.lastname ? `The users last name is "${user.lastname}"` : ""}
5515
+ ${user?.email ? `The users email is "${user.email}"` : ""}
5516
+ ` : "";
5517
+ const genericContext = `IMPORTANT general information:
5518
+ ${personalizationInformation}
5519
+ The current date is "${(/* @__PURE__ */ new Date()).toLocaleDateString()}" and the current time is "${(/* @__PURE__ */ new Date()).toLocaleTimeString()}".
5520
+ If the user does not explicitly provide the current date, for examle when saying ' this weekend', you should assume
5521
+ they are talking with the current date in mind as a reference.`;
5354
5522
  let system = instructions || "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.";
5355
5523
  system += "\n\n" + genericContext;
5356
5524
  if (prompt) {
@@ -5375,7 +5543,7 @@ var ExuluAgent2 = class {
5375
5543
  system,
5376
5544
  prompt,
5377
5545
  maxRetries: 2,
5378
- tools: convertToolsArrayToObject(
5546
+ tools: await convertToolsArrayToObject(
5379
5547
  currentTools,
5380
5548
  allExuluTools,
5381
5549
  toolConfigs,
@@ -5384,7 +5552,8 @@ var ExuluAgent2 = class {
5384
5552
  user,
5385
5553
  exuluConfig,
5386
5554
  session,
5387
- req
5555
+ req,
5556
+ project
5388
5557
  ),
5389
5558
  stopWhen: [(0, import_ai.stepCountIs)(2)]
5390
5559
  });
@@ -5435,7 +5604,7 @@ var ExuluAgent2 = class {
5435
5604
  ignoreIncompleteToolCalls: true
5436
5605
  }),
5437
5606
  maxRetries: 2,
5438
- tools: convertToolsArrayToObject(
5607
+ tools: await convertToolsArrayToObject(
5439
5608
  currentTools,
5440
5609
  allExuluTools,
5441
5610
  toolConfigs,
@@ -5444,7 +5613,8 @@ var ExuluAgent2 = class {
5444
5613
  user,
5445
5614
  exuluConfig,
5446
5615
  session,
5447
- req
5616
+ req,
5617
+ project
5448
5618
  ),
5449
5619
  stopWhen: [(0, import_ai.stepCountIs)(2)]
5450
5620
  });
@@ -5494,7 +5664,6 @@ var ExuluAgent2 = class {
5494
5664
  */
5495
5665
  async processFilePartsInMessages(messages) {
5496
5666
  const processedMessages = await Promise.all(messages.map(async (message) => {
5497
- console.log("[EXULU] Processing file parts in messages: " + JSON.stringify(message, null, 2));
5498
5667
  if (message.role !== "user" || !Array.isArray(message.parts)) {
5499
5668
  return message;
5500
5669
  }
@@ -5505,7 +5674,7 @@ var ExuluAgent2 = class {
5505
5674
  const { mediaType, url, filename } = part;
5506
5675
  const imageTypes = ["image/png", "image/jpeg", "image/jpg", "image/gif", "image/webp"];
5507
5676
  if (imageTypes.includes(mediaType)) {
5508
- console.log(`[EXULU] Converting file part to image part: ${filename}`);
5677
+ console.log(`[EXULU] Converting file part to image part: ${filename} `);
5509
5678
  return {
5510
5679
  type: "image",
5511
5680
  image: url,
@@ -5516,7 +5685,7 @@ var ExuluAgent2 = class {
5516
5685
  try {
5517
5686
  const response = await fetch(url);
5518
5687
  if (!response.ok) {
5519
- console.error(`[EXULU] Failed to fetch file: ${filename}, status: ${response.status}`);
5688
+ console.error(`[EXULU] Failed to fetch file: ${filename}, status: ${response.status} `);
5520
5689
  return {
5521
5690
  type: "text",
5522
5691
  text: `[Error: Could not load file ${filename}]`
@@ -5529,9 +5698,9 @@ var ExuluAgent2 = class {
5529
5698
  });
5530
5699
  return {
5531
5700
  type: "text",
5532
- text: `<file name="${filename}">
5533
- ${extractedText}
5534
- </file>`
5701
+ text: `<file file name = "${filename}" >
5702
+ ${extractedText}
5703
+ </file>`
5535
5704
  };
5536
5705
  } catch (error) {
5537
5706
  console.error(`[EXULU] Error processing file ${filename}:`, error);
@@ -5581,7 +5750,10 @@ ${extractedText}
5581
5750
  });
5582
5751
  let messages = [];
5583
5752
  let previousMessagesContent = previousMessages || [];
5753
+ let project;
5584
5754
  if (session) {
5755
+ const sessionData = await getSession({ sessionID: session });
5756
+ project = sessionData.project;
5585
5757
  console.log("[EXULU] loading previous messages from session: " + session);
5586
5758
  const previousMessages2 = await getAgentMessages({
5587
5759
  session,
@@ -5600,20 +5772,18 @@ ${extractedText}
5600
5772
  messages = messages.filter(
5601
5773
  (message2, index, self) => index === self.findIndex((t) => t.id === message2.id)
5602
5774
  );
5603
- console.log("[EXULU] Processing file parts in messages for OpenAI Responses API compatibility");
5604
5775
  messages = await this.processFilePartsInMessages(messages);
5605
5776
  const genericContext = "IMPORTANT: \n\n The current date is " + (/* @__PURE__ */ new Date()).toLocaleDateString() + " and the current time is " + (/* @__PURE__ */ new Date()).toLocaleTimeString() + ". If the user does not explicitly provide the current date, for examle when saying ' this weekend', you should assume they are talking with the current date in mind as a reference.";
5606
5777
  let system = instructions || "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.";
5607
5778
  system += "\n\n" + genericContext;
5608
- console.log("[EXULU] tools for agent: " + this.name, currentTools?.map((x) => x.name + " (" + x.id + ")"));
5609
- console.log("[EXULU] system", system.slice(0, 100) + "...");
5610
5779
  const result = (0, import_ai.streamText)({
5611
5780
  model,
5612
5781
  // Should be a LanguageModelV1
5613
5782
  messages: (0, import_ai.convertToModelMessages)(messages, {
5614
5783
  ignoreIncompleteToolCalls: true
5615
5784
  }),
5616
- // prepareStep could be used here to set the model for the first step or change other params
5785
+ // PrepareStep could be used here to set the model
5786
+ // for the first step or change other parameters.
5617
5787
  system,
5618
5788
  maxRetries: 2,
5619
5789
  providerOptions: {
@@ -5621,7 +5791,7 @@ ${extractedText}
5621
5791
  reasoningSummary: "auto"
5622
5792
  }
5623
5793
  },
5624
- tools: convertToolsArrayToObject(
5794
+ tools: await convertToolsArrayToObject(
5625
5795
  currentTools,
5626
5796
  allExuluTools,
5627
5797
  toolConfigs,
@@ -5630,7 +5800,8 @@ ${extractedText}
5630
5800
  user,
5631
5801
  exuluConfig,
5632
5802
  session,
5633
- req
5803
+ req,
5804
+ project
5634
5805
  ),
5635
5806
  onError: (error) => {
5636
5807
  console.error("[EXULU] chat stream error.", error);
@@ -5726,12 +5897,10 @@ var ExuluEmbedder = class {
5726
5897
  id
5727
5898
  } = setting;
5728
5899
  let value = "";
5729
- console.log("[EXULU] variable name", variableName);
5730
5900
  const variable = await db3.from("variables").where({ name: variableName }).first();
5731
5901
  if (!variable) {
5732
5902
  throw new Error("Variable not found for embedder setting: " + name + " in context: " + context + " and embedder: " + this.id);
5733
5903
  }
5734
- console.log("[EXULU] variable", variable);
5735
5904
  if (variable.encrypted) {
5736
5905
  if (!process.env.NEXTAUTH_SECRET) {
5737
5906
  throw new Error("NEXTAUTH_SECRET environment variable is not set, cannot decrypt variable: " + name);
@@ -5743,14 +5912,12 @@ var ExuluEmbedder = class {
5743
5912
  throw new Error("Decryption returned empty string - invalid key or corrupted data");
5744
5913
  }
5745
5914
  value = decrypted;
5746
- console.log("[EXULU] successfully decrypted value for", name);
5747
5915
  } catch (error) {
5748
5916
  throw new Error(`Failed to decrypt variable "${name}" for embedder setting in context "${context}": ${error instanceof Error ? error.message : "Unknown error"}. Verify that NEXTAUTH_SECRET matches the key used during encryption.`);
5749
5917
  }
5750
5918
  } else {
5751
5919
  value = variable.value;
5752
5920
  }
5753
- console.log("[EXULU] variable value", value);
5754
5921
  hydrated.push({
5755
5922
  id: id || "",
5756
5923
  name,
@@ -5858,14 +6025,20 @@ var ExuluStorage = class {
5858
6025
  getPresignedUrl = async (key) => {
5859
6026
  return await getPresignedUrl(key, this.config);
5860
6027
  };
5861
- uploadFile = async (user, file, key, type, metadata) => {
5862
- return await uploadFile(user, file, key, this.config, {
5863
- contentType: type,
5864
- metadata: {
5865
- ...metadata,
5866
- type
5867
- }
5868
- });
6028
+ uploadFile = async (file, key, type, user, metadata) => {
6029
+ return await uploadFile(
6030
+ file,
6031
+ key,
6032
+ this.config,
6033
+ {
6034
+ contentType: type,
6035
+ metadata: {
6036
+ ...metadata,
6037
+ type
6038
+ }
6039
+ },
6040
+ user
6041
+ );
5869
6042
  };
5870
6043
  // todo add upload and delete methods
5871
6044
  };
@@ -5885,7 +6058,19 @@ var ExuluContext = class {
5885
6058
  // todo typings
5886
6059
  configuration;
5887
6060
  sources = [];
5888
- constructor({ id, name, description, embedder, active, rateLimit, fields, queryRewriter, resultReranker, configuration, sources }) {
6061
+ constructor({
6062
+ id,
6063
+ name,
6064
+ description,
6065
+ embedder,
6066
+ active,
6067
+ rateLimit,
6068
+ fields,
6069
+ queryRewriter,
6070
+ resultReranker,
6071
+ configuration,
6072
+ sources
6073
+ }) {
5889
6074
  this.id = id;
5890
6075
  this.name = name;
5891
6076
  this.fields = fields || [];
@@ -5902,7 +6087,7 @@ var ExuluContext = class {
5902
6087
  this.queryRewriter = queryRewriter;
5903
6088
  this.resultReranker = resultReranker;
5904
6089
  }
5905
- processField = async (trigger, user, role, item, config) => {
6090
+ processField = async (trigger, item, exuluConfig, user, role) => {
5906
6091
  console.log("[EXULU] processing field", item.field, " in context", this.id);
5907
6092
  console.log("[EXULU] fields", this.fields.map((field2) => field2.name));
5908
6093
  const field = this.fields.find((field2) => {
@@ -5912,7 +6097,7 @@ var ExuluContext = class {
5912
6097
  console.error("[EXULU] field not found or processor not set for field", item.field, " in context", this.id);
5913
6098
  throw new Error("Field not found or processor not set for field " + item.field + " in context " + this.id);
5914
6099
  }
5915
- const exuluStorage = new ExuluStorage({ config });
6100
+ const exuluStorage = new ExuluStorage({ config: exuluConfig });
5916
6101
  const queue = await field.processor.config?.queue;
5917
6102
  if (queue?.queue.name) {
5918
6103
  console.log("[EXULU] processor is in queue mode, scheduling job.");
@@ -5938,6 +6123,7 @@ var ExuluContext = class {
5938
6123
  job: job.id
5939
6124
  };
5940
6125
  }
6126
+ console.log("[EXULU] POS 1 -- EXULU CONTEXT PROCESS FIELD");
5941
6127
  const result = await field.processor.execute({
5942
6128
  item,
5943
6129
  user,
@@ -5950,13 +6136,24 @@ var ExuluContext = class {
5950
6136
  delete: this.deleteItem
5951
6137
  }
5952
6138
  },
5953
- config
6139
+ exuluConfig
5954
6140
  });
5955
6141
  return {
5956
6142
  result,
5957
6143
  job: void 0
5958
6144
  };
5959
6145
  };
6146
+ search = async (options) => {
6147
+ const { db: db3 } = await postgresClient();
6148
+ const result = await vectorSearch({
6149
+ ...options,
6150
+ user: options.user,
6151
+ role: options.role,
6152
+ context: this,
6153
+ db: db3
6154
+ });
6155
+ return result;
6156
+ };
5960
6157
  deleteAll = async () => {
5961
6158
  const { db: db3 } = await postgresClient();
5962
6159
  await db3.from(getTableName(this.id)).delete();
@@ -5966,8 +6163,11 @@ var ExuluContext = class {
5966
6163
  results: []
5967
6164
  };
5968
6165
  };
5969
- executeSource = async (source, inputs) => {
5970
- return await source.execute(inputs);
6166
+ executeSource = async (source, inputs, exuluConfig) => {
6167
+ return await source.execute({
6168
+ ...inputs,
6169
+ exuluConfig
6170
+ });
5971
6171
  };
5972
6172
  tableExists = async () => {
5973
6173
  const { db: db3 } = await postgresClient();
@@ -6023,7 +6223,7 @@ var ExuluContext = class {
6023
6223
  job
6024
6224
  };
6025
6225
  };
6026
- createItem = async (item, config, user, role, upsert) => {
6226
+ createItem = async (item, config, user, role, upsert, generateEmbeddingsOverwrite) => {
6027
6227
  if (upsert && (!item.id && !item.external_id)) {
6028
6228
  throw new Error("Item id or external id is required for upsert.");
6029
6229
  }
@@ -6050,12 +6250,40 @@ var ExuluContext = class {
6050
6250
  throw new Error("Failed to create item.");
6051
6251
  }
6052
6252
  console.log("[EXULU] context configuration", this.configuration);
6053
- if (this.embedder && (this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always")) {
6253
+ let jobs = [];
6254
+ let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "onInsert" || this.configuration.calculateVectors === "always");
6255
+ for (const [key, value] of Object.entries(item)) {
6256
+ console.log("[EXULU] Checking for processors for field", key);
6257
+ const processor = this.fields.find((field) => field.name === key.replace("_s3key", ""))?.processor;
6258
+ console.log("[EXULU] Processor found", processor);
6259
+ if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
6260
+ const {
6261
+ job: processorJob,
6262
+ result: processorResult
6263
+ } = await this.processField(
6264
+ "api",
6265
+ {
6266
+ ...item,
6267
+ id: results[0].id,
6268
+ field: key
6269
+ },
6270
+ config,
6271
+ user,
6272
+ role
6273
+ );
6274
+ if (processorJob) {
6275
+ jobs.push(processorJob);
6276
+ }
6277
+ if (!processorJob && processor.config?.generateEmbeddings) {
6278
+ shouldGenerateEmbeddings = true;
6279
+ }
6280
+ }
6281
+ }
6282
+ if (shouldGenerateEmbeddings) {
6054
6283
  console.log("[EXULU] generating embeddings for item", results[0].id);
6055
- const { job } = await this.embeddings.generate.one({
6284
+ const { job: embeddingsJob } = await this.embeddings.generate.one({
6056
6285
  item: {
6057
6286
  ...item,
6058
- // important we need to full record here with all fields for the embedder
6059
6287
  id: results[0].id
6060
6288
  },
6061
6289
  user,
@@ -6063,17 +6291,16 @@ var ExuluContext = class {
6063
6291
  trigger: "api",
6064
6292
  config
6065
6293
  });
6066
- return {
6067
- item: results[0],
6068
- job
6069
- };
6294
+ if (embeddingsJob) {
6295
+ jobs.push(embeddingsJob);
6296
+ }
6070
6297
  }
6071
6298
  return {
6072
6299
  item: results[0],
6073
- job: void 0
6300
+ job: jobs.length > 0 ? jobs.join(",") : void 0
6074
6301
  };
6075
6302
  };
6076
- updateItem = async (item, config, user, role) => {
6303
+ updateItem = async (item, config, user, role, generateEmbeddingsOverwrite) => {
6077
6304
  const { db: db3 } = await postgresClient();
6078
6305
  if (item.field) {
6079
6306
  delete item.field;
@@ -6097,8 +6324,35 @@ var ExuluContext = class {
6097
6324
  }
6098
6325
  ).returning("id");
6099
6326
  await mutation;
6100
- if (this.embedder && (this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always")) {
6101
- const { job } = await this.embeddings.generate.one({
6327
+ let jobs = [];
6328
+ let shouldGenerateEmbeddings = this.embedder && generateEmbeddingsOverwrite !== false && (generateEmbeddingsOverwrite || this.configuration.calculateVectors === "onUpdate" || this.configuration.calculateVectors === "always");
6329
+ for (const [key, value] of Object.entries(item)) {
6330
+ const processor = this.fields.find((field) => field.name === key.replace("_s3key", ""))?.processor;
6331
+ if (processor && (processor?.config?.trigger === "onInsert" || processor?.config?.trigger === "onUpdate" || processor?.config?.trigger === "always")) {
6332
+ const {
6333
+ job: processorJob,
6334
+ result: processorResult
6335
+ } = await this.processField(
6336
+ "api",
6337
+ {
6338
+ ...item,
6339
+ id: record.id,
6340
+ field: key
6341
+ },
6342
+ config,
6343
+ user,
6344
+ role
6345
+ );
6346
+ if (processorJob) {
6347
+ jobs.push(processorJob);
6348
+ }
6349
+ if (!processorJob && processor.config?.generateEmbeddings) {
6350
+ shouldGenerateEmbeddings = true;
6351
+ }
6352
+ }
6353
+ }
6354
+ if (shouldGenerateEmbeddings) {
6355
+ const { job: embeddingsJob } = await this.embeddings.generate.one({
6102
6356
  item: record,
6103
6357
  // important we need to full record here with all fields for the embedder
6104
6358
  user,
@@ -6106,14 +6360,13 @@ var ExuluContext = class {
6106
6360
  trigger: "api",
6107
6361
  config
6108
6362
  });
6109
- return {
6110
- item: record,
6111
- job
6112
- };
6363
+ if (embeddingsJob) {
6364
+ jobs.push(embeddingsJob);
6365
+ }
6113
6366
  }
6114
6367
  return {
6115
6368
  item: record,
6116
- job: void 0
6369
+ job: jobs.length > 0 ? jobs.join(",") : void 0
6117
6370
  };
6118
6371
  };
6119
6372
  deleteItem = async (item, user, role) => {
@@ -6140,6 +6393,34 @@ var ExuluContext = class {
6140
6393
  job: void 0
6141
6394
  };
6142
6395
  };
6396
+ getItem = async ({ item }) => {
6397
+ const { db: db3 } = await postgresClient();
6398
+ if (!item.id && !item.external_id) {
6399
+ throw new Error("Item id or external id is required to get an item.");
6400
+ }
6401
+ const result = await db3.from(getTableName(this.id)).where({
6402
+ ...item.id ? { id: item.id } : {},
6403
+ ...item.external_id ? { external_id: item.external_id } : {}
6404
+ }).first();
6405
+ if (result) {
6406
+ const chunksCount = await db3.from(getChunksTableName(this.id)).where(
6407
+ { source: result.id }
6408
+ ).count("id");
6409
+ result.chunksCount = Number(chunksCount[0].count) || 0;
6410
+ }
6411
+ return result;
6412
+ };
6413
+ getItems = async ({
6414
+ filters,
6415
+ fields
6416
+ }) => {
6417
+ const { db: db3 } = await postgresClient();
6418
+ let query = db3.from(getTableName(this.id)).select(fields || "*");
6419
+ const tableDefinition = contextToTableDefinition(this);
6420
+ query = applyFilters(query, filters || [], tableDefinition);
6421
+ const items = await query;
6422
+ return items;
6423
+ };
6143
6424
  embeddings = {
6144
6425
  generate: {
6145
6426
  one: async ({
@@ -6156,6 +6437,12 @@ var ExuluContext = class {
6156
6437
  if (!item.id) {
6157
6438
  throw new Error("Item id is required for generating embeddings.");
6158
6439
  }
6440
+ const { db: db3 } = await postgresClient();
6441
+ const record = await db3.from(getTableName(this.id)).where({ id: item.id }).first();
6442
+ if (!record) {
6443
+ throw new Error("Item not found.");
6444
+ }
6445
+ item = record;
6159
6446
  const queue = await this.embedder.queue;
6160
6447
  if (queue?.queue.name) {
6161
6448
  console.log("[EXULU] embedder is in queue mode, scheduling job.");
@@ -6921,7 +7208,7 @@ Mood: friendly and intelligent.
6921
7208
  const user = authenticationResult.user;
6922
7209
  let agentQuery = db3("agents");
6923
7210
  agentQuery.select("*");
6924
- agentQuery = applyAccessControl(agentsSchema2(), authenticationResult.user, agentQuery);
7211
+ agentQuery = applyAccessControl(agentsSchema2(), agentQuery, authenticationResult.user);
6925
7212
  agentQuery.where({ id: req.params.agent });
6926
7213
  const agent = await agentQuery.first();
6927
7214
  if (!agent) {
@@ -6938,7 +7225,7 @@ Mood: friendly and intelligent.
6938
7225
  } else {
6939
7226
  let projectQuery = db3("projects");
6940
7227
  projectQuery.select("*");
6941
- projectQuery = applyAccessControl(projectsSchema2(), authenticationResult.user, projectQuery);
7228
+ projectQuery = applyAccessControl(projectsSchema2(), projectQuery, authenticationResult.user);
6942
7229
  projectQuery.where({ id: req.params.project });
6943
7230
  project = await projectQuery.first();
6944
7231
  if (!project) {
@@ -7280,6 +7567,7 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
7280
7567
  if (!data.role) {
7281
7568
  throw new Error(`Role not set for processor job.`);
7282
7569
  }
7570
+ console.log("[EXULU] POS 2 -- EXULU CONTEXT PROCESS FIELD");
7283
7571
  const result = await field.processor.execute({
7284
7572
  item: data.inputs,
7285
7573
  user: data.user,
@@ -7294,9 +7582,24 @@ var createWorkers = async (agents, queues2, config, contexts, evals, tools, trac
7294
7582
  },
7295
7583
  config
7296
7584
  });
7585
+ let jobs = [];
7586
+ if (field.processor.config?.generateEmbeddings) {
7587
+ const { job: embeddingsJob } = await context.embeddings.generate.one({
7588
+ item: data.inputs,
7589
+ user: data.user,
7590
+ role: data.role,
7591
+ trigger: "api",
7592
+ config
7593
+ });
7594
+ if (embeddingsJob) {
7595
+ jobs.push(embeddingsJob);
7596
+ }
7597
+ }
7297
7598
  return {
7298
7599
  result,
7299
- metadata: {}
7600
+ metadata: {
7601
+ jobs: jobs.length > 0 ? jobs.join(",") : void 0
7602
+ }
7300
7603
  };
7301
7604
  }
7302
7605
  if (data.type === "eval_run") {
@@ -7982,7 +8285,7 @@ var ExuluMCP = class {
7982
8285
  console.log("[EXULU] MCP tool inputs", inputs);
7983
8286
  console.log("[EXULU] MCP tool args", args);
7984
8287
  const configValues = agentInstance.tools;
7985
- const tools = convertToolsArrayToObject(
8288
+ const tools = await convertToolsArrayToObject(
7986
8289
  [tool2],
7987
8290
  allTools,
7988
8291
  configValues,