@exulu/backend 1.42.2 → 1.43.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
@@ -38,7 +38,6 @@ __export(index_exports, {
38
38
  ExuluChunkers: () => ExuluChunkers,
39
39
  ExuluContext: () => ExuluContext,
40
40
  ExuluDefaultAgents: () => ExuluDefaultAgents,
41
- ExuluDefaultContexts: () => ExuluDefaultContexts,
42
41
  ExuluEmbedder: () => ExuluEmbedder,
43
42
  ExuluEval: () => ExuluEval2,
44
43
  ExuluJobs: () => ExuluJobs,
@@ -1487,6 +1486,10 @@ var addCoreFields = (schema) => {
1487
1486
  name: "last_processed_at",
1488
1487
  type: "date"
1489
1488
  });
1489
+ schema.fields.push({
1490
+ name: "embeddings_updated_at",
1491
+ type: "date"
1492
+ });
1490
1493
  if (schema.RBAC) {
1491
1494
  if (!schema.fields.some((field) => field.name === "rights_mode")) {
1492
1495
  schema.fields.push({
@@ -1700,7 +1703,8 @@ var checkRecordAccess = async (record, request, user) => {
1700
1703
  const isPublic = record.rights_mode === "public";
1701
1704
  const byUsers = record.rights_mode === "users";
1702
1705
  const byRoles = record.rights_mode === "roles";
1703
- const isCreator = user ? record.created_by === user.id.toString() : false;
1706
+ const createdBy = typeof record.created_by === "string" ? record.created_by : record.created_by?.toString();
1707
+ const isCreator = user ? createdBy === user.id.toString() : false;
1704
1708
  const isAdmin = user ? user.super_admin : false;
1705
1709
  const isApi = user ? user.type === "api" : false;
1706
1710
  let hasAccess = "none";
@@ -1907,6 +1911,7 @@ ${enumValues}
1907
1911
  fields.push(" maxContextLength: Int");
1908
1912
  fields.push(" provider: String");
1909
1913
  fields.push(" authenticationInformation: String");
1914
+ fields.push(" systemInstructions: String");
1910
1915
  fields.push(" slug: String");
1911
1916
  }
1912
1917
  const rbacField = table.RBAC ? " RBAC: RBACData" : "";
@@ -1971,6 +1976,8 @@ input FilterOperatorDate {
1971
1976
  input FilterOperatorFloat {
1972
1977
  eq: Float
1973
1978
  ne: Float
1979
+ lte: Float
1980
+ gte: Float
1974
1981
  in: [Float]
1975
1982
  and: [FilterOperatorFloat]
1976
1983
  or: [FilterOperatorFloat]
@@ -2034,7 +2041,11 @@ var getRequestedFields = (info) => {
2034
2041
  return fields.filter((field) => field !== "pageInfo" && field !== "items" && field !== "RBAC");
2035
2042
  };
2036
2043
  var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRbacRecords) => {
2037
- const { users = [], roles = [], projects = [] } = rbacData;
2044
+ const {
2045
+ users = [],
2046
+ roles = []
2047
+ /* projects = [] */
2048
+ } = rbacData;
2038
2049
  if (!existingRbacRecords) {
2039
2050
  existingRbacRecords = await db3.from("rbac").where({
2040
2051
  entity: entityName,
@@ -2043,25 +2054,19 @@ var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRba
2043
2054
  }
2044
2055
  const newUserRecords = new Set(users.map((u) => `${u.id}:${u.rights}`));
2045
2056
  const newRoleRecords = new Set(roles.map((r) => `${r.id}:${r.rights}`));
2046
- const newProjectRecords = new Set(projects.map((p) => `${p.id}:${p.rights}`));
2047
2057
  const existingUserRecords = new Set(existingRbacRecords.filter((r) => r.access_type === "User").map((r) => `${r.user_id}:${r.rights}`));
2048
2058
  const existingRoleRecords = new Set(existingRbacRecords.filter((r) => r.access_type === "Role").map((r) => `${r.role_id}:${r.rights}`));
2049
2059
  const existingProjectRecords = new Set(existingRbacRecords.filter((r) => r.access_type === "Project").map((r) => `${r.project_id}:${r.rights}`));
2050
2060
  const usersToCreate = users.filter((u) => !existingUserRecords.has(`${u.id}:${u.rights}`));
2051
2061
  const rolesToCreate = roles.filter((r) => !existingRoleRecords.has(`${r.id}:${r.rights}`));
2052
- const projectsToCreate = projects.filter((p) => !existingProjectRecords.has(`${p.id}:${p.rights}`));
2053
2062
  const usersToRemove = existingRbacRecords.filter((r) => r.access_type === "User" && !newUserRecords.has(`${r.user_id}:${r.rights}`));
2054
2063
  const rolesToRemove = existingRbacRecords.filter((r) => r.access_type === "Role" && !newRoleRecords.has(`${r.role_id}:${r.rights}`));
2055
- const projectsToRemove = existingRbacRecords.filter((r) => r.access_type === "Project" && !newProjectRecords.has(`${r.project_id}:${r.rights}`));
2056
2064
  if (usersToRemove.length > 0) {
2057
2065
  await db3.from("rbac").whereIn("id", usersToRemove.map((r) => r.id)).del();
2058
2066
  }
2059
2067
  if (rolesToRemove.length > 0) {
2060
2068
  await db3.from("rbac").whereIn("id", rolesToRemove.map((r) => r.id)).del();
2061
2069
  }
2062
- if (projectsToRemove.length > 0) {
2063
- await db3.from("rbac").whereIn("id", projectsToRemove.map((r) => r.id)).del();
2064
- }
2065
2070
  const recordsToInsert = [];
2066
2071
  usersToCreate.forEach((user) => {
2067
2072
  recordsToInsert.push({
@@ -2085,17 +2090,6 @@ var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRba
2085
2090
  updatedAt: /* @__PURE__ */ new Date()
2086
2091
  });
2087
2092
  });
2088
- projectsToCreate.forEach((project) => {
2089
- recordsToInsert.push({
2090
- entity: entityName,
2091
- access_type: "Project",
2092
- target_resource_id: resourceId,
2093
- project_id: project.id,
2094
- rights: project.rights,
2095
- createdAt: /* @__PURE__ */ new Date(),
2096
- updatedAt: /* @__PURE__ */ new Date()
2097
- });
2098
- });
2099
2093
  if (recordsToInsert.length > 0) {
2100
2094
  await db3.from("rbac").insert(recordsToInsert);
2101
2095
  }
@@ -2109,7 +2103,7 @@ function createMutations(table, agents, contexts, tools, config) {
2109
2103
  if (user.super_admin === true) {
2110
2104
  return true;
2111
2105
  }
2112
- if (!user.super_admin && (!user.role || !(table.name.plural === "agents" && user.role.agents === "write") && !(table.name.plural === "workflow_templates" && user.role.workflows === "write") && !(table.name.plural === "variables" && user.role.variables === "write") && !(table.name.plural === "users" && user.role.users === "write") && !((table.name.plural === "test_cases" || table.name.plural === "eval_sets" || table.name.plural === "eval_runs") && user.role.evals === "write"))) {
2106
+ if (!user.super_admin && (table.name.plural === "agents" || table.name.plural === "workflow_templates" || table.name.plural === "variables" || table.name.plural === "users" || table.name.plural === "test_cases" || table.name.plural === "eval_sets" || table.name.plural === "eval_runs") && (!user.role || !(table.name.plural === "agents" && user.role.agents === "write") && !(table.name.plural === "workflow_templates" && user.role.workflows === "write") && !(table.name.plural === "variables" && user.role.variables === "write") && !(table.name.plural === "users" && user.role.users === "write") && !((table.name.plural === "test_cases" || table.name.plural === "eval_sets" || table.name.plural === "eval_runs") && user.role.evals === "write"))) {
2113
2107
  console.error("Access control error: no role found for current user or no access to entity type.");
2114
2108
  throw new Error("Access control error: no role found for current user or no access to entity type.");
2115
2109
  }
@@ -2163,6 +2157,61 @@ function createMutations(table, agents, contexts, tools, config) {
2163
2157
  }
2164
2158
  };
2165
2159
  const mutations = {
2160
+ [`${tableNamePlural}CopyOneById`]: async (_, args, context, info) => {
2161
+ const { db: db3 } = context;
2162
+ const requestedFields = getRequestedFields(info);
2163
+ let { id } = args;
2164
+ if (!id) {
2165
+ throw new Error("ID is required for copying a record.");
2166
+ }
2167
+ await validateWriteAccess(id, context);
2168
+ const item = await db3.from(tableNamePlural).select("*").where({ id }).first();
2169
+ if (!item) {
2170
+ throw new Error("Record not found");
2171
+ }
2172
+ if (item.rights_mode) {
2173
+ item.rights_mode = "private";
2174
+ }
2175
+ if (item.created_at) {
2176
+ item.created_at = /* @__PURE__ */ new Date();
2177
+ }
2178
+ if (item.createdAt) {
2179
+ item.createdAt = /* @__PURE__ */ new Date();
2180
+ }
2181
+ if (item.updated_at) {
2182
+ item.updated_at = /* @__PURE__ */ new Date();
2183
+ }
2184
+ if (item.updatedAt) {
2185
+ item.updatedAt = /* @__PURE__ */ new Date();
2186
+ }
2187
+ if (item.created_by) {
2188
+ item.created_by = context.user.id;
2189
+ }
2190
+ if (item.createdBy) {
2191
+ item.createdBy = context.user.id;
2192
+ }
2193
+ if (item.name) {
2194
+ item.name = item.name + " (Copy)";
2195
+ }
2196
+ Object.keys(item).forEach((key) => {
2197
+ if (table.fields.find((field) => field.name === key)?.type === "json") {
2198
+ if (typeof item[key] === "object" || Array.isArray(item[key])) {
2199
+ item[key] = JSON.stringify(item[key]);
2200
+ }
2201
+ }
2202
+ });
2203
+ const insert = db3(tableNamePlural).insert({
2204
+ ...item,
2205
+ id: db3.fn.uuid()
2206
+ }).returning("*");
2207
+ const result = await insert;
2208
+ if (!result[0]) {
2209
+ throw new Error("Failed to copy record.");
2210
+ }
2211
+ return {
2212
+ item: finalizeRequestedFields({ args, table, requestedFields, agents, contexts, tools, result: result[0], user: context.user })
2213
+ };
2214
+ },
2166
2215
  [`${tableNamePlural}CreateOne`]: async (_, args, context, info) => {
2167
2216
  const { db: db3 } = context;
2168
2217
  const requestedFields = getRequestedFields(info);
@@ -2420,7 +2469,7 @@ function createMutations(table, agents, contexts, tools, config) {
2420
2469
  }
2421
2470
  const { limit = 10, filters = [], sort } = args;
2422
2471
  const { db: db3 } = context;
2423
- const { items } = await paginationRequest({
2472
+ const { items } = await itemsPaginationRequest({
2424
2473
  db: db3,
2425
2474
  limit,
2426
2475
  page: 0,
@@ -2519,9 +2568,6 @@ function createMutations(table, agents, contexts, tools, config) {
2519
2568
  };
2520
2569
  };
2521
2570
  mutations[`${tableNameSingular}GenerateChunks`] = async (_, args, context, info) => {
2522
- if (!context.user?.super_admin) {
2523
- throw new Error("You are not authorized to generate chunks via API, user must be super admin.");
2524
- }
2525
2571
  const { db: db3 } = await postgresClient();
2526
2572
  const exists = contexts.find((context2) => context2.id === table.id);
2527
2573
  if (!exists) {
@@ -2532,13 +2578,17 @@ function createMutations(table, agents, contexts, tools, config) {
2532
2578
  const columns = await db3(mainTable).columnInfo();
2533
2579
  let query = db3.from(mainTable).select(Object.keys(columns));
2534
2580
  if (!args.where) {
2581
+ if (!context.user?.super_admin) {
2582
+ throw new Error("You are not authorized to generate all chunks via API, user must be super admin.");
2583
+ }
2535
2584
  const {
2536
2585
  jobs: jobs2,
2537
2586
  items: items2
2538
2587
  } = await embeddings.generate.all(
2539
2588
  config,
2540
2589
  context.user.id,
2541
- context.user.role?.id
2590
+ context.user.role?.id,
2591
+ args.limit
2542
2592
  );
2543
2593
  return {
2544
2594
  message: "Chunks generated successfully.",
@@ -2547,6 +2597,9 @@ function createMutations(table, agents, contexts, tools, config) {
2547
2597
  };
2548
2598
  }
2549
2599
  query = applyFilters(query, args.where, table);
2600
+ if (args.limit) {
2601
+ query = query.limit(args.limit);
2602
+ }
2550
2603
  const items = await query;
2551
2604
  if (items.length === 0) {
2552
2605
  throw new Error("No items found to generate chunks for.");
@@ -2571,9 +2624,6 @@ function createMutations(table, agents, contexts, tools, config) {
2571
2624
  };
2572
2625
  };
2573
2626
  mutations[`${tableNameSingular}DeleteChunks`] = async (_, args, context, info) => {
2574
- if (!context.user?.super_admin) {
2575
- throw new Error("You are not authorized to delete chunks via API, user must be super admin.");
2576
- }
2577
2627
  const { db: db3 } = await postgresClient();
2578
2628
  const id = contexts.find((context2) => context2.id === table.id)?.id;
2579
2629
  if (!id) {
@@ -2582,6 +2632,10 @@ function createMutations(table, agents, contexts, tools, config) {
2582
2632
  if (args.where) {
2583
2633
  let query = db3.from(getTableName(id)).select("id");
2584
2634
  query = applyFilters(query, args.where, table);
2635
+ query = applyAccessControl(table, query, context.user);
2636
+ if (args.limit) {
2637
+ query = query.limit(args.limit);
2638
+ }
2585
2639
  const items = await query;
2586
2640
  if (items.length === 0) {
2587
2641
  throw new Error("No items found to delete chunks for.");
@@ -2595,11 +2649,20 @@ function createMutations(table, agents, contexts, tools, config) {
2595
2649
  jobs: []
2596
2650
  };
2597
2651
  } else {
2598
- const count = await db3.from(getChunksTableName(id)).count();
2599
- await db3.from(getChunksTableName(id)).truncate();
2652
+ if (!context.user?.super_admin) {
2653
+ throw new Error("You are not authorized to delete all chunks via API, user must be super admin.");
2654
+ }
2655
+ let count = 0;
2656
+ if (!args.limit) {
2657
+ const result = await db3.from(getChunksTableName(id)).count();
2658
+ count = parseInt(result[0].count);
2659
+ await db3.from(getChunksTableName(id)).truncate();
2660
+ } else {
2661
+ count = await db3.from(getChunksTableName(id)).limit(args.limit).delete();
2662
+ }
2600
2663
  return {
2601
2664
  message: "Chunks deleted successfully.",
2602
- items: parseInt(count[0].count),
2665
+ items: count,
2603
2666
  jobs: []
2604
2667
  };
2605
2668
  }
@@ -2614,8 +2677,8 @@ var applyAccessControl = (table, query, user, field_prefix) => {
2614
2677
  }
2615
2678
  console.log("[EXULU] user.role", user?.role);
2616
2679
  console.log("[EXULU] table.name.plural", table.name.plural);
2617
- if (user && !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")))) {
2618
- console.error("==== Access control error: no role found or no access to entity type. ====");
2680
+ if (user && !user?.super_admin && (table.name.plural === "agents" || table.name.plural === "workflow_templates" || table.name.plural === "variables" || table.name.plural === "users" || table.name.plural === "test_cases" || table.name.plural === "eval_sets" || table.name.plural === "eval_runs") && (!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")))) {
2681
+ console.error("==== Access control error: no role found or no access to entity type. ====", user, table.name.plural);
2619
2682
  throw new Error("Access control error: no role found or no access to entity type.");
2620
2683
  }
2621
2684
  const hasRBAC = table.RBAC === true;
@@ -2658,6 +2721,7 @@ var converOperatorToQuery = (query, fieldName, operators, table, field_prefix) =
2658
2721
  const isJsonField = field?.type === "json";
2659
2722
  const prefix = field_prefix ? field_prefix + "." : "";
2660
2723
  fieldName = prefix + fieldName;
2724
+ console.log("[EXULU] operators", operators);
2661
2725
  if (operators.eq !== void 0) {
2662
2726
  if (isJsonField) {
2663
2727
  query = query.whereRaw(`?? = ?::jsonb`, [fieldName, JSON.stringify(operators.eq)]);
@@ -2689,7 +2753,13 @@ var converOperatorToQuery = (query, fieldName, operators, table, field_prefix) =
2689
2753
  }
2690
2754
  }
2691
2755
  if (operators.lte !== void 0) {
2692
- query = query.where(fieldName, "<=", operators.lte);
2756
+ console.log("[EXULU] operators.lte", operators.lte);
2757
+ console.log("[EXULU] fieldName", fieldName);
2758
+ if (operators.lte === 0 || operators.lte === "0") {
2759
+ query = query.whereNull(fieldName).orWhere(fieldName, "=", 0);
2760
+ } else {
2761
+ query = query.where(fieldName, "<=", operators.lte);
2762
+ }
2693
2763
  }
2694
2764
  if (operators.gte !== void 0) {
2695
2765
  query = query.where(fieldName, ">=", operators.gte);
@@ -2705,7 +2775,8 @@ var backendAgentFields = [
2705
2775
  "capabilities",
2706
2776
  "maxContextLength",
2707
2777
  "provider",
2708
- "authenticationInformation"
2778
+ "authenticationInformation",
2779
+ "systemInstructions"
2709
2780
  ];
2710
2781
  var removeAgentFields = (requestedFields) => {
2711
2782
  const filtered = requestedFields.filter((field) => !backendAgentFields.includes(field));
@@ -2790,6 +2861,9 @@ var addAgentFields = async (args, requestedFields, agents, result, tools, user,
2790
2861
  if (requestedFields.includes("provider")) {
2791
2862
  result.provider = backend?.provider || "";
2792
2863
  }
2864
+ if (requestedFields.includes("systemInstructions")) {
2865
+ result.systemInstructions = backend?.config?.instructions || void 0;
2866
+ }
2793
2867
  if (!requestedFields.includes("backend")) {
2794
2868
  delete result.backend;
2795
2869
  }
@@ -3014,7 +3088,7 @@ var applySorting = (query, sort, field_prefix) => {
3014
3088
  }
3015
3089
  return query;
3016
3090
  };
3017
- var paginationRequest = async ({
3091
+ var itemsPaginationRequest = async ({
3018
3092
  db: db3,
3019
3093
  limit,
3020
3094
  page,
@@ -3044,7 +3118,8 @@ var paginationRequest = async ({
3044
3118
  if (page > 1) {
3045
3119
  dataQuery = dataQuery.offset((page - 1) * limit);
3046
3120
  }
3047
- let items = await dataQuery.select(fields ? fields : "*").limit(limit);
3121
+ dataQuery = dataQuery.select(fields ? fields : "*").limit(limit);
3122
+ let items = await dataQuery;
3048
3123
  return {
3049
3124
  items,
3050
3125
  pageInfo: {
@@ -3095,7 +3170,7 @@ function createQueries(table, agents, tools, contexts) {
3095
3170
  const { limit = 10, page = 0, filters = [], sort } = args;
3096
3171
  const requestedFields = getRequestedFields(info);
3097
3172
  const sanitizedFields = sanitizeRequestedFields(table, requestedFields);
3098
- const { items, pageInfo } = await paginationRequest({
3173
+ const { items, pageInfo } = await itemsPaginationRequest({
3099
3174
  db: db3,
3100
3175
  limit,
3101
3176
  page,
@@ -3170,6 +3245,46 @@ function createQueries(table, agents, tools, contexts) {
3170
3245
  expand: args.expand
3171
3246
  });
3172
3247
  };
3248
+ queries[`${tableNameSingular}ChunkById`] = async (_, args, context, info) => {
3249
+ const exists = contexts.find((ctx) => ctx.id === table.id);
3250
+ if (!exists) {
3251
+ throw new Error("Context " + table.id + " not found in registry.");
3252
+ }
3253
+ const { db: db3 } = context;
3254
+ const chunksTable = getChunksTableName(exists.id);
3255
+ const mainTable = getTableName(exists.id);
3256
+ const chunk = await db3(chunksTable + " as chunks").select([
3257
+ "chunks.id as chunk_id",
3258
+ "chunks.source as chunk_source",
3259
+ "chunks.content as chunk_content",
3260
+ "chunks.chunk_index",
3261
+ "chunks.metadata as chunk_metadata",
3262
+ db3.raw('chunks."createdAt" as chunk_created_at'),
3263
+ db3.raw('chunks."updatedAt" as chunk_updated_at'),
3264
+ "items.id as item_id",
3265
+ "items.name as item_name",
3266
+ "items.external_id as item_external_id",
3267
+ db3.raw('items."updatedAt" as item_updated_at'),
3268
+ db3.raw('items."createdAt" as item_created_at')
3269
+ ]).leftJoin(mainTable + " as items", "chunks.source", "items.id").where("chunks.id", args.id).first();
3270
+ if (!chunk) {
3271
+ return null;
3272
+ }
3273
+ return {
3274
+ chunk_content: chunk.chunk_content,
3275
+ chunk_index: chunk.chunk_index,
3276
+ chunk_id: chunk.chunk_id,
3277
+ chunk_source: chunk.chunk_source,
3278
+ chunk_metadata: chunk.chunk_metadata,
3279
+ chunk_created_at: chunk.chunk_created_at,
3280
+ chunk_updated_at: chunk.chunk_updated_at,
3281
+ item_id: chunk.item_id,
3282
+ item_name: chunk.item_name,
3283
+ item_external_id: chunk.item_external_id,
3284
+ item_updated_at: chunk.item_updated_at,
3285
+ item_created_at: chunk.item_created_at
3286
+ };
3287
+ };
3173
3288
  }
3174
3289
  return queries;
3175
3290
  }
@@ -3302,120 +3417,52 @@ var vectorSearch = async ({
3302
3417
  const fullTextWeight = 2;
3303
3418
  const semanticWeight = 1;
3304
3419
  const rrfK = 50;
3305
- const hybridSQL = `
3306
- WITH full_text AS (
3307
- SELECT
3308
- chunks.id,
3309
- chunks.source,
3310
- row_number() OVER (
3311
- ORDER BY ts_rank(chunks.fts, plainto_tsquery(?, ?)) DESC
3312
- ) AS rank_ix
3313
- FROM ${chunksTable} as chunks
3314
- LEFT JOIN ${mainTable} as items ON items.id = chunks.source
3315
- WHERE chunks.fts @@ plainto_tsquery(?, ?)
3316
- AND ts_rank(chunks.fts, plainto_tsquery(?, ?)) > ?
3317
- AND (items.archived IS FALSE OR items.archived IS NULL)
3318
- ORDER BY rank_ix
3319
- LIMIT LEAST(?, 250) * 2
3320
- ),
3321
- semantic AS (
3322
- SELECT
3323
- chunks.id,
3324
- chunks.source,
3325
- row_number() OVER (
3326
- ORDER BY chunks.embedding <=> ${vectorExpr} ASC
3327
- ) AS rank_ix
3328
- FROM ${chunksTable} as chunks
3329
- LEFT JOIN ${mainTable} as items ON items.id = chunks.source
3330
- WHERE chunks.embedding IS NOT NULL
3331
- AND (1 - (chunks.embedding <=> ${vectorExpr})) >= ?
3332
- AND (items.archived IS FALSE OR items.archived IS NULL)
3333
- ORDER BY rank_ix
3334
- LIMIT LEAST(?, 250) * 2
3335
- )
3336
- SELECT
3337
- items.id as item_id,
3338
- items.name as item_name,
3339
- items.external_id as item_external_id,
3340
- chunks.id AS chunk_id,
3341
- chunks.source,
3342
- chunks.content,
3343
- chunks.chunk_index,
3344
- chunks.metadata,
3345
- chunks."createdAt" as chunk_created_at,
3346
- chunks."updatedAt" as chunk_updated_at,
3347
- items."updatedAt" as item_updated_at,
3348
- items."createdAt" as item_created_at,
3349
- /* Per-signal scores for introspection */
3350
- ts_rank(chunks.fts, plainto_tsquery(?, ?)) AS fts_rank,
3351
- (1 - (chunks.embedding <=> ${vectorExpr})) AS cosine_distance,
3352
-
3353
- /* Hybrid RRF score */
3354
- (
3355
- COALESCE(1.0 / (? + ft.rank_ix), 0.0) * ?
3356
- +
3357
- COALESCE(1.0 / (? + se.rank_ix), 0.0) * ?
3358
- )::float AS hybrid_score
3359
-
3360
- FROM full_text ft
3361
- FULL OUTER JOIN semantic se
3362
- ON ft.id = se.id
3363
- JOIN ${chunksTable} as chunks
3364
- ON COALESCE(ft.id, se.id) = chunks.id
3365
- JOIN ${mainTable} as items
3366
- ON items.id = chunks.source
3367
- WHERE (
3368
- COALESCE(1.0 / (? + ft.rank_ix), 0.0) * ?
3369
- +
3370
- COALESCE(1.0 / (? + se.rank_ix), 0.0) * ?
3371
- ) >= ?
3372
- AND (chunks.fts IS NULL OR ts_rank(chunks.fts, plainto_tsquery(?, ?)) > ?)
3373
- AND (chunks.embedding IS NULL OR (1 - (chunks.embedding <=> ${vectorExpr})) >= ?)
3374
- ORDER BY hybrid_score DESC
3375
- LIMIT LEAST(?, 250)
3376
- OFFSET 0
3377
- `;
3378
- const bindings = [
3379
- // full_text: plainto_tsquery(lang, query) in rank and where
3380
- language,
3381
- query,
3382
- language,
3383
- query,
3384
- language,
3385
- query,
3386
- cutoffs?.tsvector || 0,
3387
- // full_text tsvector cutoff
3388
- matchCount,
3389
- // full_text limit
3390
- cutoffs?.cosineDistance || 0,
3391
- // semantic cosine distance cutoff
3392
- matchCount,
3393
- // semantic limit
3394
- // fts_rank (ts_rank) call
3395
- language,
3396
- query,
3397
- // RRF fusion parameters
3398
- rrfK,
3399
- fullTextWeight,
3400
- rrfK,
3401
- semanticWeight,
3402
- // WHERE clause hybrid_score filter
3403
- rrfK,
3404
- fullTextWeight,
3405
- rrfK,
3406
- semanticWeight,
3407
- cutoffs?.hybrid || 0,
3408
- // Additional cutoff filters in main WHERE clause
3409
- language,
3410
- query,
3411
- cutoffs?.tsvector || 0,
3412
- // tsvector cutoff for results from semantic CTE
3413
- cutoffs?.cosineDistance || 0,
3414
- // cosine distance cutoff for results from full_text CTE
3415
- matchCount
3416
- // final limit
3417
- ];
3418
- resultChunks = await db3.raw(hybridSQL, bindings).then((r) => r.rows ?? r);
3420
+ let fullTextQuery = db3(chunksTable + " as chunks").select([
3421
+ "chunks.id",
3422
+ "chunks.source",
3423
+ db3.raw(`row_number() OVER (ORDER BY ts_rank(chunks.fts, plainto_tsquery(?, ?)) DESC) AS rank_ix`, [language, query])
3424
+ ]).leftJoin(mainTable + " as items", "items.id", "chunks.source").whereRaw(`chunks.fts @@ plainto_tsquery(?, ?)`, [language, query]).whereRaw(`ts_rank(chunks.fts, plainto_tsquery(?, ?)) > ?`, [language, query, cutoffs?.tsvector || 0]).whereRaw(`(items.archived IS FALSE OR items.archived IS NULL)`).limit(Math.min(matchCount * 2, 500));
3425
+ fullTextQuery = applyFilters(fullTextQuery, filters, table, "items");
3426
+ fullTextQuery = applyAccessControl(table, fullTextQuery, user, "items");
3427
+ let semanticQuery = db3(chunksTable + " as chunks").select([
3428
+ "chunks.id",
3429
+ "chunks.source",
3430
+ db3.raw(`row_number() OVER (ORDER BY chunks.embedding <=> ${vectorExpr} ASC) AS rank_ix`)
3431
+ ]).leftJoin(mainTable + " as items", "items.id", "chunks.source").whereNotNull("chunks.embedding").whereRaw(`(1 - (chunks.embedding <=> ${vectorExpr})) >= ?`, [cutoffs?.cosineDistance || 0]).whereRaw(`(items.archived IS FALSE OR items.archived IS NULL)`).limit(Math.min(matchCount * 2, 500));
3432
+ semanticQuery = applyFilters(semanticQuery, filters, table, "items");
3433
+ semanticQuery = applyAccessControl(table, semanticQuery, user, "items");
3434
+ let hybridQuery = db3.with("full_text", fullTextQuery).with("semantic", semanticQuery).select([
3435
+ "items.id as item_id",
3436
+ "items.name as item_name",
3437
+ "items.external_id as item_external_id",
3438
+ "chunks.id as chunk_id",
3439
+ "chunks.source",
3440
+ "chunks.content",
3441
+ "chunks.chunk_index",
3442
+ "chunks.metadata",
3443
+ db3.raw('chunks."createdAt" as chunk_created_at'),
3444
+ db3.raw('chunks."updatedAt" as chunk_updated_at'),
3445
+ db3.raw('items."updatedAt" as item_updated_at'),
3446
+ db3.raw('items."createdAt" as item_created_at'),
3447
+ db3.raw(`ts_rank(chunks.fts, plainto_tsquery(?, ?)) AS fts_rank`, [language, query]),
3448
+ db3.raw(`(1 - (chunks.embedding <=> ${vectorExpr})) AS cosine_distance`),
3449
+ db3.raw(`
3450
+ (
3451
+ COALESCE(1.0 / (? + ft.rank_ix), 0.0) * ?
3452
+ +
3453
+ COALESCE(1.0 / (? + se.rank_ix), 0.0) * ?
3454
+ )::float AS hybrid_score
3455
+ `, [rrfK, fullTextWeight, rrfK, semanticWeight])
3456
+ ]).from("full_text as ft").fullOuterJoin("semantic as se", "ft.id", "se.id").join(chunksTable + " as chunks", function() {
3457
+ this.on(db3.raw("COALESCE(ft.id, se.id)"), "=", "chunks.id");
3458
+ }).join(mainTable + " as items", "items.id", "chunks.source").whereRaw(`
3459
+ (
3460
+ COALESCE(1.0 / (? + ft.rank_ix), 0.0) * ?
3461
+ +
3462
+ COALESCE(1.0 / (? + se.rank_ix), 0.0) * ?
3463
+ ) >= ?
3464
+ `, [rrfK, fullTextWeight, rrfK, semanticWeight, cutoffs?.hybrid || 0]).whereRaw(`(chunks.fts IS NULL OR ts_rank(chunks.fts, plainto_tsquery(?, ?)) > ?)`, [language, query, cutoffs?.tsvector || 0]).whereRaw(`(chunks.embedding IS NULL OR (1 - (chunks.embedding <=> ${vectorExpr})) >= ?)`, [cutoffs?.cosineDistance || 0]).orderByRaw("hybrid_score DESC").limit(Math.min(matchCount, 250));
3465
+ resultChunks = await hybridQuery;
3419
3466
  }
3420
3467
  console.log("[EXULU] Vector search chunk results:", resultChunks?.length);
3421
3468
  let results = resultChunks.map((chunk) => ({
@@ -3651,8 +3698,8 @@ var contextToTableDefinition = (context) => {
3651
3698
  type: "text"
3652
3699
  });
3653
3700
  definition.fields.push({
3654
- name: "embeddings_updated_at",
3655
- type: "date"
3701
+ name: "chunks_count",
3702
+ type: "number"
3656
3703
  });
3657
3704
  definition.fields.push({
3658
3705
  name: "name",
@@ -3755,10 +3802,13 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
3755
3802
  if (table.type === "items") {
3756
3803
  typeDefs += `
3757
3804
  ${tableNamePlural}VectorSearch(query: String!, method: VectorMethodEnum!, filters: [Filter${tableNameSingularUpperCaseFirst}], cutoffs: SearchCutoffs, expand: SearchExpand): ${tableNameSingular}VectorSearchResult
3805
+ ${tableNameSingular}ChunkById(id: ID!): ${tableNameSingular}VectorSearchChunk
3758
3806
  `;
3759
3807
  }
3760
3808
  mutationDefs += `
3761
3809
  ${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!, upsert: Boolean): ${tableNameSingular}MutationPayload
3810
+ ${tableNamePlural}CopyOneById(id: ID!): ${tableNameSingular}MutationPayload
3811
+
3762
3812
  ${tableNamePlural}UpdateOne(where: [Filter${tableNameSingularUpperCaseFirst}], input: ${tableNameSingular}Input!): ${tableNameSingular}MutationPayload
3763
3813
  ${tableNamePlural}UpdateOneById(id: ID!, input: ${tableNameSingular}Input!): ${tableNameSingular}MutationPayload
3764
3814
  ${tableNamePlural}RemoveOneById(id: ID!): ${tableNameSingular}
@@ -3766,9 +3816,9 @@ function createSDL(tables, contexts, agents, tools, config, evals, queues2) {
3766
3816
  `;
3767
3817
  if (table.type === "items") {
3768
3818
  mutationDefs += `
3769
- ${tableNameSingular}GenerateChunks(where: [Filter${tableNameSingularUpperCaseFirst}]): ${tableNameSingular}GenerateChunksReturnPayload
3819
+ ${tableNameSingular}GenerateChunks(where: [Filter${tableNameSingularUpperCaseFirst}], limit: Int): ${tableNameSingular}GenerateChunksReturnPayload
3770
3820
  ${tableNameSingular}ExecuteSource(source: ID!, inputs: JSON!): ${tableNameSingular}ExecuteSourceReturnPayload
3771
- ${tableNameSingular}DeleteChunks(where: [Filter${tableNameSingularUpperCaseFirst}]): ${tableNameSingular}DeleteChunksReturnPayload
3821
+ ${tableNameSingular}DeleteChunks(where: [Filter${tableNameSingularUpperCaseFirst}], limit: Int): ${tableNameSingular}DeleteChunksReturnPayload
3772
3822
  `;
3773
3823
  if (table.processor) {
3774
3824
  mutationDefs += `
@@ -3902,6 +3952,9 @@ type PageInfo {
3902
3952
  typeDefs += `
3903
3953
  contextById(id: ID!): Context
3904
3954
  `;
3955
+ typeDefs += `
3956
+ getUniquePromptTags: [String!]!
3957
+ `;
3905
3958
  mutationDefs += `
3906
3959
  runEval(id: ID!, test_case_ids: [ID!]): RunEvalReturnPayload
3907
3960
  `;
@@ -4359,6 +4412,38 @@ type PageInfo {
4359
4412
  array.push("agents");
4360
4413
  return [...new Set(array)].sort();
4361
4414
  };
4415
+ resolvers.Query["getUniquePromptTags"] = async (_, args, context, info) => {
4416
+ const { db: db3 } = context;
4417
+ const user = context.user;
4418
+ const promptTable = tables.find((t) => t.name.plural === "prompt_library");
4419
+ if (!promptTable) {
4420
+ throw new Error("Prompt library table not found");
4421
+ }
4422
+ let query = db3.from("prompt_library").select("tags");
4423
+ query = applyAccessControl(promptTable, query, user);
4424
+ const results = await query;
4425
+ const allTags = [];
4426
+ for (const row of results) {
4427
+ if (row.tags) {
4428
+ let tags = [];
4429
+ if (typeof row.tags === "string") {
4430
+ try {
4431
+ tags = JSON.parse(row.tags);
4432
+ } catch (e) {
4433
+ tags = [row.tags];
4434
+ }
4435
+ } else if (Array.isArray(row.tags)) {
4436
+ tags = row.tags;
4437
+ }
4438
+ tags.forEach((tag) => {
4439
+ if (tag && typeof tag === "string" && tag.trim()) {
4440
+ allTags.push(tag.trim().toLowerCase());
4441
+ }
4442
+ });
4443
+ }
4444
+ }
4445
+ return [...new Set(allTags)].sort();
4446
+ };
4362
4447
  modelDefs += `
4363
4448
  type ProviderPaginationResult {
4364
4449
  items: [Provider]!
@@ -4730,14 +4815,16 @@ var addBucketPrefixToKey = (key, bucket) => {
4730
4815
  }
4731
4816
  return `${bucket}/${key}`;
4732
4817
  };
4733
- var uploadFile = async (file, fileName, config, options = {}, user, customBucket) => {
4818
+ var uploadFile = async (file, fileName, config, options = {}, user, customBucket, global) => {
4734
4819
  if (!config.fileUploads) {
4735
4820
  throw new Error("File uploads are not configured (in the exported uploadFile function)");
4736
4821
  }
4737
4822
  const client2 = getS3Client(config);
4738
4823
  let defaultBucket = config.fileUploads.s3Bucket;
4739
4824
  let key = fileName;
4740
- key = addUserPrefixToKey(key, user || "api");
4825
+ if (!global) {
4826
+ key = addUserPrefixToKey(key, user || "api");
4827
+ }
4741
4828
  key = addGeneralPrefixToKey(key, config);
4742
4829
  const sanitizedMetadata = sanitizeMetadata(options.metadata);
4743
4830
  const command = new import_client_s3.PutObjectCommand({
@@ -4888,7 +4975,7 @@ var createUppyRoutes = async (app, contexts, config) => {
4888
4975
  return;
4889
4976
  }
4890
4977
  let allowed = false;
4891
- if (user.type === "api" || user.super_admin || key.includes(`user_${user.id}/`)) {
4978
+ if (user.type === "api" || user.super_admin || !key.includes(`user_`) || key.includes(`user_${user.id}/`)) {
4892
4979
  allowed = true;
4893
4980
  }
4894
4981
  if (!allowed) {
@@ -4966,9 +5053,16 @@ var createUppyRoutes = async (app, contexts, config) => {
4966
5053
  return;
4967
5054
  }
4968
5055
  const client2 = getS3Client(config);
5056
+ let prefix = `${config.fileUploads.s3prefix ? config.fileUploads.s3prefix.replace(/\/$/, "") + "/" : ""}`;
5057
+ if (!req.headers.global) {
5058
+ prefix += `user_${authenticationResult.user.id}`;
5059
+ } else {
5060
+ prefix += "global";
5061
+ }
5062
+ console.log("[EXULU] prefix", prefix);
4969
5063
  const command = new import_client_s3.ListObjectsV2Command({
4970
5064
  Bucket: config.fileUploads.s3Bucket,
4971
- Prefix: `${config.fileUploads.s3prefix ? config.fileUploads.s3prefix.replace(/\/$/, "") + "/" : ""}user_${authenticationResult.user.id}`,
5065
+ Prefix: prefix,
4972
5066
  MaxKeys: 9,
4973
5067
  ...req.query.continuationToken && { ContinuationToken: req.query.continuationToken }
4974
5068
  });
@@ -5051,7 +5145,13 @@ var createUppyRoutes = async (app, contexts, config) => {
5051
5145
  const { filename, contentType } = extractFileParameters(req);
5052
5146
  validateFileParameters(filename, contentType);
5053
5147
  const key = generateS3Key2(filename);
5054
- let fullKey = addUserPrefixToKey(key, user.type === "api" ? "api" : user.id);
5148
+ let fullKey = key;
5149
+ console.log("[EXULU] global", req.headers.global);
5150
+ if (!req.headers.global) {
5151
+ fullKey = addUserPrefixToKey(key, user.type === "api" ? "api" : user.id);
5152
+ } else {
5153
+ fullKey = "global/" + key;
5154
+ }
5055
5155
  fullKey = addGeneralPrefixToKey(fullKey, config);
5056
5156
  console.log("[EXULU] signing on server for user", user.id, "with key", fullKey);
5057
5157
  (0, import_s3_request_presigner.getSignedUrl)(
@@ -5107,7 +5207,13 @@ var createUppyRoutes = async (app, contexts, config) => {
5107
5207
  return res.status(400).json({ error: "s3: content type must be a string" });
5108
5208
  }
5109
5209
  const key = `${(0, import_node_crypto.randomUUID)()}-_EXULU_${filename}`;
5110
- let fullKey = addUserPrefixToKey(key, user.type === "api" ? "api" : user.id);
5210
+ let fullKey = key;
5211
+ console.log("[EXULU] global", req.headers.global);
5212
+ if (!req.headers.global) {
5213
+ fullKey = addUserPrefixToKey(key, user.type === "api" ? "api" : user.id);
5214
+ } else {
5215
+ fullKey = "global/" + key;
5216
+ }
5111
5217
  fullKey = addGeneralPrefixToKey(fullKey, config);
5112
5218
  console.log("[EXULU] signing on server for user", user.id, "with key", fullKey);
5113
5219
  const params = {
@@ -5755,6 +5861,31 @@ var ExuluAgent2 = class {
5755
5861
  they are talking with the current date in mind as a reference.`;
5756
5862
  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.";
5757
5863
  system += "\n\n" + genericContext;
5864
+ const includesContextSearchTool = currentTools?.some((tool2) => tool2.name.toLowerCase().includes("context_search") || tool2.id.includes("context_search"));
5865
+ console.log("[EXULU] Current tools: " + currentTools?.map((tool2) => tool2.name));
5866
+ console.log("[EXULU] Includes context search tool: " + includesContextSearchTool);
5867
+ if (includesContextSearchTool) {
5868
+ system += `
5869
+
5870
+
5871
+
5872
+ When you use a context search tool, you will include references to the items
5873
+ retrieved from the context search tool inline in the response using this exact JSON format
5874
+ (all on one line, no line breaks):
5875
+ {item_name: <item_name>, item_id: <item_id>, context: <context_id>, chunk_id: <chunk_id>, chunk_index: <chunk_index>}
5876
+
5877
+ IMPORTANT formatting rules:
5878
+ - Use the exact format shown above, all on ONE line
5879
+ - Do NOT use quotes around field names or values
5880
+ - Use the context ID (e.g., "dx-newlift-newton-knowledge-g3y6r1") from the tool result
5881
+ - Include the file/item name, not the full path
5882
+ - Separate multiple citations with spaces
5883
+
5884
+ Example: {item_name: document.pdf, item_id: abc123, context: my-context-id, chunk_id: chunk_456, chunk_index: 0}
5885
+
5886
+ The citations will be rendered as interactive badges in the UI.
5887
+ `;
5888
+ }
5758
5889
  if (prompt) {
5759
5890
  let result = { object: null, text: "" };
5760
5891
  let inputTokens = 0;
@@ -6015,6 +6146,31 @@ ${extractedText}
6015
6146
  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.";
6016
6147
  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.";
6017
6148
  system += "\n\n" + genericContext;
6149
+ const includesContextSearchTool = currentTools?.some((tool2) => tool2.name.toLowerCase().includes("context_search") || tool2.id.includes("context_search"));
6150
+ console.log("[EXULU] Current tools: " + currentTools?.map((tool2) => tool2.name));
6151
+ console.log("[EXULU] Includes context search tool: " + includesContextSearchTool);
6152
+ if (includesContextSearchTool) {
6153
+ system += `
6154
+
6155
+
6156
+
6157
+ When you use a context search tool, you will include references to the items
6158
+ retrieved from the context search tool inline in the response using this exact JSON format
6159
+ (all on one line, no line breaks):
6160
+ {item_name: <item_name>, item_id: <item_id>, context: <context_id>, chunk_id: <chunk_id>, chunk_index: <chunk_index>}
6161
+
6162
+ IMPORTANT formatting rules:
6163
+ - Use the exact format shown above, all on ONE line
6164
+ - Do NOT use quotes around field names or values
6165
+ - Use the context ID (e.g., "dx-newlift-newton-knowledge-g3y6r1") from the tool result
6166
+ - Include the file/item name, not the full path
6167
+ - Separate multiple citations with spaces
6168
+
6169
+ Example: {item_name: document.pdf, item_id: abc123, context: my-context-id, chunk_id: chunk_456, chunk_index: 0}
6170
+
6171
+ The citations will be rendered as interactive badges in the UI.
6172
+ `;
6173
+ }
6018
6174
  const result = (0, import_ai.streamText)({
6019
6175
  model,
6020
6176
  // Should be a LanguageModelV1
@@ -6558,6 +6714,7 @@ var ExuluContext = class {
6558
6714
  })));
6559
6715
  }
6560
6716
  await db3.from(getTableName(this.id)).where({ id: item.id }).update({
6717
+ chunks_count: chunks?.length || 0,
6561
6718
  embeddings_updated_at: (/* @__PURE__ */ new Date()).toISOString()
6562
6719
  }).returning("id");
6563
6720
  return {
@@ -6827,9 +6984,13 @@ var ExuluContext = class {
6827
6984
  trigger: trigger || "agent"
6828
6985
  }, role, void 0);
6829
6986
  },
6830
- all: async (config, userId, roleId) => {
6987
+ all: async (config, userId, roleId, limit) => {
6831
6988
  const { db: db3 } = await postgresClient();
6832
- const items = await db3.from(getTableName(this.id)).select("*");
6989
+ let query = db3.from(getTableName(this.id)).select("*");
6990
+ if (limit) {
6991
+ query = query.limit(limit);
6992
+ }
6993
+ const items = await query;
6833
6994
  const jobs = [];
6834
6995
  const queue = await this.embedder?.queue;
6835
6996
  if (!queue?.queue.name && items.length > 2e3) {
@@ -6874,7 +7035,7 @@ var ExuluContext = class {
6874
7035
  table.text("rights_mode").defaultTo(this.configuration?.defaultRightsMode ?? "private");
6875
7036
  table.integer("textlength");
6876
7037
  table.text("source");
6877
- table.timestamp("embeddings_updated_at");
7038
+ table.integer("chunks_count").defaultTo(0);
6878
7039
  for (const field of this.fields) {
6879
7040
  let { type, name, unique } = field;
6880
7041
  if (!type || !name) {
@@ -7403,12 +7564,6 @@ Mood: friendly and intelligent.
7403
7564
  return;
7404
7565
  }
7405
7566
  }
7406
- if (user?.type !== "api" && !user?.super_admin && req.body.resourceId !== user?.id) {
7407
- res.status(400).json({
7408
- message: "The provided user id in the resourceId field is not the same as the authenticated user. Only super admins and API users can impersonate other users."
7409
- });
7410
- return;
7411
- }
7412
7567
  console.log("[EXULU] agent tools", agentInstance.tools?.map((x) => x.name + " (" + x.id + ")"));
7413
7568
  const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
7414
7569
  let enabledTools = await getEnabledTools(agentInstance, tools, disabledTools, agents, user);
@@ -9453,45 +9608,6 @@ var createLogger = ({
9453
9608
  };
9454
9609
  var logger_default = createLogger;
9455
9610
 
9456
- // src/templates/contexts/code-standards.ts
9457
- var codeStandardsContext = new ExuluContext({
9458
- id: "code_standards",
9459
- name: "Code Standards",
9460
- description: "Code standards that can be used with the Exulu CLI.",
9461
- configuration: {
9462
- defaultRightsMode: "public"
9463
- },
9464
- fields: [{
9465
- name: "Best practices",
9466
- type: "longText"
9467
- }, {
9468
- name: "Code style",
9469
- type: "longText"
9470
- }, {
9471
- name: "Tech stack",
9472
- type: "longText"
9473
- }],
9474
- active: true
9475
- });
9476
-
9477
- // src/templates/contexts/outputs.ts
9478
- var outputsContext = new ExuluContext({
9479
- id: "outputs_default_context",
9480
- name: "Outputs",
9481
- description: "Outputs from agent sessions that you have saved for re-used later.",
9482
- configuration: {
9483
- defaultRightsMode: "private",
9484
- calculateVectors: "manual"
9485
- },
9486
- fields: [
9487
- {
9488
- name: "content",
9489
- type: "longText"
9490
- }
9491
- ],
9492
- active: true
9493
- });
9494
-
9495
9611
  // src/registry/index.ts
9496
9612
  var import_winston2 = __toESM(require("winston"), 1);
9497
9613
  var import_util = __toESM(require("util"), 1);
@@ -10618,9 +10734,7 @@ var ExuluApp = class {
10618
10734
  ...evals ?? []
10619
10735
  ] : [];
10620
10736
  this._contexts = {
10621
- ...contexts,
10622
- codeStandardsContext,
10623
- outputsContext
10737
+ ...contexts
10624
10738
  };
10625
10739
  this._agents = [
10626
10740
  claudeSonnet4Agent,
@@ -12356,10 +12470,6 @@ var ExuluJobs = {
12356
12470
  validate: validateJob
12357
12471
  }
12358
12472
  };
12359
- var ExuluDefaultContexts = {
12360
- codeStandards: codeStandardsContext,
12361
- outputs: outputsContext
12362
- };
12363
12473
  var ExuluDefaultAgents = {
12364
12474
  anthropic: {
12365
12475
  opus4: claudeOpus4Agent,
@@ -12518,7 +12628,6 @@ var ExuluChunkers = {
12518
12628
  ExuluChunkers,
12519
12629
  ExuluContext,
12520
12630
  ExuluDefaultAgents,
12521
- ExuluDefaultContexts,
12522
12631
  ExuluEmbedder,
12523
12632
  ExuluEval,
12524
12633
  ExuluJobs,