@resolveio/server-lib 22.0.7 → 22.0.9

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.
@@ -366,12 +366,65 @@ var AI_ASSISTANT_TERM_SYNONYMS = [
366
366
  expansions: ['invoice', 'invoices', 'paid invoices', 'invoice payments', 'billing', 'sales']
367
367
  }
368
368
  ];
369
+ var AI_ASSISTANT_REPORT_BUILDER_EXPERT_PLAYBOOK = [
370
+ 'Report Builder Bridge Expert Playbook (execute in order for every data request):',
371
+ '1) Determine if data is required.',
372
+ '- If user asks for counts, lists, totals, trends, rankings, or "recent/last", run a report builder directive before answering.',
373
+ '',
374
+ 'Report style mapping (always apply):',
375
+ '- Dated report: any breakdown over time (per day/week/month/quarter/year, trends, time-series comparisons).',
376
+ '- Grouped report: any nested headers, grouped buckets, grouped totals, or multi-level grouping dimensions.',
377
+ '- List report: simple flat records without grouped buckets or time-series grouping.',
378
+ '',
379
+ '2) Resolve the target dataset safely.',
380
+ '- Map user wording to internal collection names using routes, collection hints, field hints, and synonym expansion.',
381
+ '- Prefer report-* collections when permissionView is under /report.',
382
+ '- Never use *.versions unless user explicitly requests bug-history/version investigation.',
383
+ '- Never invent collection names or fields.',
384
+ '',
385
+ '3) Enforce permissions and scope in the directive.',
386
+ '- Always include permissionView.',
387
+ '- Assume non-super-admin unless explicitly told otherwise.',
388
+ '- Invoice-like collections require invoice view access.',
389
+ '- Customer portal users must stay in their own customer scope.',
390
+ '',
391
+ '4) Choose the right bridge tool.',
392
+ '- Use REPORT_BUILDER_READ for record lists, snapshots, detail lookups, and simple totals/counts.',
393
+ '- Use REPORT_BUILDER_AGG for grouped summaries, trends over time, rankings, and breakdowns.',
394
+ '',
395
+ '5) Build a minimal, high-signal query.',
396
+ '- Keep projection tight and limit rows (default 20 unless user asks for more).',
397
+ '- Add sort for deterministic output.',
398
+ '- Always sort by popular keys when available: name, company, item (then title/label/customer as fallbacks), ascending unless the user asks for a different order.',
399
+ '- If no popular key exists, use a stable fallback sort (createdAt/date_created then _id).',
400
+ '- Build filters with explicit boolean logic: use $and for required constraints and $or for alternate text/name matches.',
401
+ '- Use custom/calculated fields when the user asks for derived metrics (ratios, rates, margins, computed totals).',
402
+ '- Use collection joins/lookups when selected fields live across related collections.',
403
+ '- If multiple arrays are selected, link them by shared identifiers (_id or id_<x>) so rows are aligned and not duplicated by cartesian expansion.',
404
+ '- For relative dates, include an upper bound <= $$NOW unless user explicitly asks for future ranges.',
405
+ '- For invoice revenue, prefer paid_total fallback grand_total and paid/closed states when applicable.',
406
+ '',
407
+ '6) Reliability fallback sequence when result is empty.',
408
+ '- Re-check collection and date fields (date_created/createdAt/date_completed/date_paid variants).',
409
+ '- Run tiny probe (limit 1-3) to validate shape before concluding "no data".',
410
+ '- For name filters, verify date-only first; then apply tokenized regex and id/name lookup fallback.',
411
+ '',
412
+ '7) Output behavior after bridge response.',
413
+ '- Summarize the answer first in plain language.',
414
+ '- Then provide a markdown table with clean columns.',
415
+ '- Do not dump raw JSON.',
416
+ '- Do not claim certainty when data could not be fetched.',
417
+ '',
418
+ '8) Directive formatting requirement.',
419
+ '- End with exactly one REPORT_BUILDER_READ or REPORT_BUILDER_AGG line when data fetch is needed.',
420
+ '- Do not include extra directive lines or mixed directive types in one response.'
421
+ ].join('\n');
369
422
  var AI_ASSISTANT_SYSTEM_PROMPT = [
370
423
  'You are the ResolveIO in-app AI assistant running with read-only access to the codebase.',
371
424
  'Core rules:',
372
425
  '- Never share code or file contents. All code is proprietary.',
373
426
  '- Do not modify files, run destructive commands, or access databases directly.',
374
- '- Read-only Mongo access is allowed only via the MONGO_READ/MONGO_AGG directives (see below).',
427
+ '- Read-only data access is allowed only via the REPORT_BUILDER_READ/REPORT_BUILDER_AGG directives (see below).',
375
428
  '- Do not access secrets, credentials, or user data.',
376
429
  '- If the user has a customer portal scope (other.id_customer), only discuss that customer\'s data and what is visible in their customer portal. Never reference other customers or internal/admin-only data. If asked for anything outside the portal, say it isn\'t available.',
377
430
  '- Do not assist with hacking, bypassing security, or abuse.',
@@ -382,14 +435,14 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
382
435
  '- If permissionView starts with /report/, prefer the report-* collection when both report and base collections exist.',
383
436
  '- Map user wording to internal collections/fields yourself. Do not ask for property names unless required to run a query.',
384
437
  '- Use term hints from context (synonym expansions) when mapping user language to collections.',
385
- '- Do not guess or invent collections/fields. If unsure, verify in the codebase or run a small Mongo read (limit 1-5) to learn the shape.',
386
- '- Prefer running a small Mongo read over asking multiple questions.',
387
- '- For any data request (counts, lists, breakdowns, recent/last items), you MUST run a MONGO_READ or MONGO_AGG before answering.',
438
+ '- Do not guess or invent collections/fields. If unsure, verify in the codebase or run a small REPORT_BUILDER_READ probe (limit 1-5) to learn the shape.',
439
+ '- Prefer running a small REPORT_BUILDER_READ probe over asking multiple questions.',
440
+ '- For any data request (counts, lists, breakdowns, recent/last items), you MUST run a REPORT_BUILDER_READ or REPORT_BUILDER_AGG before answering.',
388
441
  '- Ask at most one clarifying question only when required to run a query or resolve missing details.',
389
442
  '- If a field starts with id_ and refers to another collection, treat it as a foreign key and look up the related record when needed.',
390
443
  '- When resolving id_* fields, prefer lookup definitions from collection schemas/report-builder lookup tables to choose the target collection and name fields.',
391
444
  '- When the user provides a customer, well, or chemical name, use case-insensitive regex matching to find it. Assume the name exists and try to match it before asking questions.',
392
- '- Use the codebase context to choose correct collections/fields/workflows and use MONGO_READ/MONGO_AGG to answer with real data when needed.',
445
+ '- Use the codebase context to choose correct collections/fields/workflows and use REPORT_BUILDER_READ/REPORT_BUILDER_AGG to answer with real data when needed.',
393
446
  '- Process (fast path): Queue -> Planning -> Grabbing Data -> Drafting response. Use regex/keyword matching to identify collections/models, draft a minimal query, run the tool, then format a table. This should be fast; avoid extra narration.',
394
447
  '- Assume a relevant collection exists; if verification reads return zero data, report no data found instead of interrogating the user.',
395
448
  '- Never claim "no data exists" unless you resolved a collection and executed a legitimate query (with fallback date fields when needed) that returned zero rows.',
@@ -429,11 +482,11 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
429
482
  '- SUPPORT_TICKET_CREATE: <one-line summary>',
430
483
  '- Only include that line when the user clearly wants a ticket created. Do not claim a ticket is created unless you include that line.',
431
484
  '- Do not end responses with tentative phrasing like "I could" or "I’m going to." Complete the request or state what is required to proceed.',
432
- 'Mongo directives:',
485
+ 'Report builder bridge directives:',
433
486
  '- If you need database data to answer, end your response with a single line exactly in this format:',
434
- '- MONGO_READ: {"collection":"<name>","query":{...},"options":{"projection":{...},"sort":{...},"limit":20},"permissionView":"</route>"}',
487
+ '- REPORT_BUILDER_READ: {"collection":"<name>","query":{...},"options":{"projection":{...},"sort":{...},"limit":20},"permissionView":"</route>"}',
435
488
  '- If you need grouped/aggregated data (totals by user, rankings, trends), end your response with a single line exactly in this format:',
436
- '- MONGO_AGG: {"collection":"<name>","pipeline":[...],"options":{"allowDiskUse":true,"limit":20},"permissionView":"</route>"}',
489
+ '- REPORT_BUILDER_AGG: {"collection":"<name>","pipeline":[...],"options":{"allowDiskUse":true,"limit":20},"permissionView":"</route>"}',
437
490
  '- For invoice data, set permissionView to an invoice route (ex: /invoice/list or /report/invoice).',
438
491
  '- For revenue/sales/billing questions, use invoices and sum paid_total (fallback to grand_total) with date_paid and Paid/Closed status when available.',
439
492
  '- For relative date ranges (last/past/recent), include an upper bound <= $$NOW unless the user specifies a future end date.',
@@ -441,16 +494,18 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
441
494
  '- Assume you are not a super admin unless explicitly told otherwise.',
442
495
  '- Only request data when the user has permission for that module; invoice data requires invoice view access.',
443
496
  '- If the user lacks permission, answer without data and explain how to view it in the app or request access.',
444
- '- For simple counts or time-range totals, use MONGO_READ (includeTotal). For breakdowns, rankings, or sums grouped by a field, use MONGO_AGG.',
497
+ '- For simple counts or time-range totals, use REPORT_BUILDER_READ (includeTotal). For breakdowns, rankings, or sums grouped by a field, use REPORT_BUILDER_AGG.',
445
498
  '- For performance, keep pipelines minimal: avoid $push arrays or large fields unless the user explicitly asks for them.',
446
499
  '- For completion metrics (ex: "completed per day"), filter by completed status when applicable and use completion date fields; if completion dates are missing, fall back to createdAt/date_created and note the fallback.',
447
- '- Before issuing MONGO_READ or MONGO_AGG, verify the collection name and key fields by checking collection/model definitions in the current app (look for "collectionName:" and date fields like date_created/date_completed/createdAt). Do not invent collection names.',
500
+ '- Before issuing REPORT_BUILDER_READ or REPORT_BUILDER_AGG, verify the collection name and key fields by checking collection/model definitions in the current app (look for "collectionName:" and date fields like date_created/date_completed/createdAt). Do not invent collection names.',
448
501
  '- For creation-date questions when both date_created and createdAt exist, match both with $or so results are not missed.',
449
502
  '- When grouping by fields that can be arrays (drivers, deliveries, routes, chemicals), $unwind first and group by both id and name when available.',
450
- '- Use MONGO_READ/MONGO_AGG only to produce summaries/snapshots/health checks (not raw dumps) when permitted.',
503
+ '- Use REPORT_BUILDER_READ/REPORT_BUILDER_AGG only to produce summaries/snapshots/health checks (not raw dumps) when permitted.',
451
504
  '- If the user explicitly asks for IDs, set options.includeIds: true.',
452
505
  '- If a data question returns zero results, verify the collection/date field with a tiny read (limit 1-5) or a date field fallback before concluding there is no data.',
453
- '- Keep responses concise and use the configured reasoning effort level (default low).'
506
+ '- Keep responses concise and use the configured reasoning effort level (default low).',
507
+ '',
508
+ AI_ASSISTANT_REPORT_BUILDER_EXPERT_PLAYBOOK
454
509
  ].join('\n');
455
510
  var AI_ASSISTANT_PLANNER_SYSTEM_PROMPT = [
456
511
  'You are ResolveIO Assistant Planner. Your job is to produce a permissions-aware execution plan for the user\'s request.',
@@ -987,6 +1042,42 @@ function loadAiTerminalMethods(methodManager) {
987
1042
  });
988
1043
  }
989
1044
  },
1045
+ aiAssistantReportBuilderRead: {
1046
+ check: new simpl_schema_1.default({
1047
+ payload: {
1048
+ type: Object,
1049
+ blackbox: true
1050
+ }
1051
+ }),
1052
+ function: function (payload) {
1053
+ return __awaiter(this, void 0, void 0, function () {
1054
+ return __generator(this, function (_a) {
1055
+ switch (_a.label) {
1056
+ case 0: return [4 /*yield*/, executeAiAssistantMongoRead(payload, this)];
1057
+ case 1: return [2 /*return*/, _a.sent()];
1058
+ }
1059
+ });
1060
+ });
1061
+ }
1062
+ },
1063
+ aiAssistantReportBuilderAggregate: {
1064
+ check: new simpl_schema_1.default({
1065
+ payload: {
1066
+ type: Object,
1067
+ blackbox: true
1068
+ }
1069
+ }),
1070
+ function: function (payload) {
1071
+ return __awaiter(this, void 0, void 0, function () {
1072
+ return __generator(this, function (_a) {
1073
+ switch (_a.label) {
1074
+ case 0: return [4 /*yield*/, executeAiAssistantMongoAggregate(payload, this)];
1075
+ case 1: return [2 /*return*/, _a.sent()];
1076
+ }
1077
+ });
1078
+ });
1079
+ }
1080
+ },
990
1081
  aiCoderTerminalDeployTest: {
991
1082
  check: new simpl_schema_1.default({
992
1083
  id_conversation: {
@@ -1645,11 +1736,11 @@ function executeAiAssistantCodexRun(payload, context) {
1645
1736
  _x.trys.push([22, 31, , 32]);
1646
1737
  toolStart = Date.now();
1647
1738
  if (!(effectiveDirective.type === 'aggregate')) return [3 /*break*/, 24];
1648
- return [4 /*yield*/, executeAiAssistantMongoAggregate(toolRequest, context)];
1739
+ return [4 /*yield*/, executeAiAssistantReportBuilderAggregate(toolRequest, context)];
1649
1740
  case 23:
1650
1741
  _e = _x.sent();
1651
1742
  return [3 /*break*/, 26];
1652
- case 24: return [4 /*yield*/, executeAiAssistantMongoRead(toolRequest, context)];
1743
+ case 24: return [4 /*yield*/, executeAiAssistantReportBuilderRead(toolRequest, context)];
1653
1744
  case 25:
1654
1745
  _e = _x.sent();
1655
1746
  _x.label = 26;
@@ -1809,9 +1900,29 @@ function executeAiAssistantCodexRun(payload, context) {
1809
1900
  });
1810
1901
  });
1811
1902
  }
1903
+ function executeAiAssistantReportBuilderRead(payload, context) {
1904
+ return __awaiter(this, void 0, void 0, function () {
1905
+ return __generator(this, function (_a) {
1906
+ switch (_a.label) {
1907
+ case 0: return [4 /*yield*/, executeAiAssistantMongoRead(payload, context)];
1908
+ case 1: return [2 /*return*/, _a.sent()];
1909
+ }
1910
+ });
1911
+ });
1912
+ }
1913
+ function executeAiAssistantReportBuilderAggregate(payload, context) {
1914
+ return __awaiter(this, void 0, void 0, function () {
1915
+ return __generator(this, function (_a) {
1916
+ switch (_a.label) {
1917
+ case 0: return [4 /*yield*/, executeAiAssistantMongoAggregate(payload, context)];
1918
+ case 1: return [2 /*return*/, _a.sent()];
1919
+ }
1920
+ });
1921
+ });
1922
+ }
1812
1923
  function executeAiAssistantMongoRead(payload, context) {
1813
1924
  return __awaiter(this, void 0, void 0, function () {
1814
- var input, rawCollection, dbName, db, collectionResolution, collection, schemaFields, _a, user, isSuperAdmin, customerId, fallbackMeta, baseQuery, stripped, userId, normalizedClient, shouldScopeByClient, _b, strippedClient, clientScopedQuery, scopedQuery, normalized, findOptions, documents, executedQuery, probeDocs, dateFallback, fallbackQuery, fallbackDocs, expanded, fallbackDocs, nameFallback, fallbackDocs, _c, chemicalLookup, fallbackDocs, queryFields, _d, aliases, rewrittenQuery, fallbackDocs, _e, idLookup, fallbackDocs, baseCollection, fallbackPayload, fallbackResult, existingFallbacks, nameFields, dateFields, diagnostics, queryNoName, _f, queryNoDate, _g, _h, _j, _k, allCollections, base, alt, altCount, _l, total, sanitizedDocuments, requestedFields, missingFields, _m, projectionAliases, expandedProjection, refreshedDocs, includeIds, fieldAliases, displayDocs, idLookupDisplay, priorityFields, display;
1925
+ var input, rawCollection, dbName, db, collectionResolution, collection, bridgeCollection, schemaFields, _a, user, isSuperAdmin, customerId, fallbackMeta, baseQuery, stripped, userId, normalizedClient, shouldScopeByClient, _b, strippedClient, clientScopedQuery, scopedQuery, normalized, findOptions, documents, executedQuery, probeDocs, dateFallback, fallbackQuery, fallbackDocs, expanded, fallbackDocs, nameFallback, fallbackDocs, _c, chemicalLookup, fallbackDocs, queryFields, _d, aliases, rewrittenQuery, fallbackDocs, _e, idLookup, fallbackDocs, baseCollection, fallbackPayload, fallbackResult, existingFallbacks, nameFields, dateFields, diagnostics, queryNoName, _f, queryNoDate, _g, _h, _j, _k, allCollections, base, alt, altCount, _l, total, sanitizedDocuments, requestedFields, missingFields, _m, projectionAliases, expandedProjection, refreshedDocs, includeIds, fieldAliases, displayDocs, idLookupDisplay, priorityFields, display;
1815
1926
  var _o, _p;
1816
1927
  return __generator(this, function (_q) {
1817
1928
  switch (_q.label) {
@@ -1819,7 +1930,7 @@ function executeAiAssistantMongoRead(payload, context) {
1819
1930
  input = payload || {};
1820
1931
  rawCollection = normalizeOptionalString(input.collection);
1821
1932
  if (!rawCollection) {
1822
- throw new Error('AI assistant mongo read: Collection is required.');
1933
+ throw new Error('AI assistant report builder bridge: Collection is required.');
1823
1934
  }
1824
1935
  dbName = resolveAssistantDatabaseName(input.database, input.mongo);
1825
1936
  db = resolveio_server_app_1.ResolveIOServer.getMongoConnection().db(dbName);
@@ -1827,15 +1938,26 @@ function executeAiAssistantMongoRead(payload, context) {
1827
1938
  case 1:
1828
1939
  collectionResolution = _q.sent();
1829
1940
  collection = collectionResolution.name || rawCollection;
1941
+ bridgeCollection = resolveAssistantReportBuilderBridgeCollection(collection);
1942
+ if (bridgeCollection.fallbackFrom) {
1943
+ collection = bridgeCollection.collection;
1944
+ }
1830
1945
  schemaFields = getCollectionSchemaFieldNames(collection);
1831
1946
  return [4 /*yield*/, ensureAssistantReadAccess(context, input.permissionView, collection)];
1832
1947
  case 2:
1833
1948
  _a = _q.sent(), user = _a.user, isSuperAdmin = _a.isSuperAdmin;
1834
1949
  if (!isSuperAdmin && AI_ASSISTANT_BLOCKED_COLLECTIONS.has(collection)) {
1835
- throw new Error('AI assistant mongo read: Access denied.');
1950
+ throw new Error('AI assistant report builder bridge: Access denied.');
1836
1951
  }
1837
1952
  customerId = normalizeOptionalString((_o = user === null || user === void 0 ? void 0 : user.other) === null || _o === void 0 ? void 0 : _o.id_customer);
1838
1953
  fallbackMeta = {};
1954
+ if (bridgeCollection.fallbackFrom) {
1955
+ fallbackMeta.reportBuilderBridge = {
1956
+ from: bridgeCollection.fallbackFrom,
1957
+ to: collection,
1958
+ used: true
1959
+ };
1960
+ }
1839
1961
  baseQuery = normalizeMongoQuery(input.query);
1840
1962
  if (!isSuperAdmin && customerId) {
1841
1963
  stripped = stripQueryFieldPathsDeepWithMeta(baseQuery, ['id_customer', 'other.id_customer']);
@@ -1849,7 +1971,7 @@ function executeAiAssistantMongoRead(payload, context) {
1849
1971
  if (!isSuperAdmin && (collection === 'users' || collection === 'user-versions')) {
1850
1972
  userId = normalizeOptionalString(user === null || user === void 0 ? void 0 : user._id);
1851
1973
  if (!userId) {
1852
- throw new Error('AI assistant mongo read: Access denied.');
1974
+ throw new Error('AI assistant report builder bridge: Access denied.');
1853
1975
  }
1854
1976
  baseQuery = {
1855
1977
  $and: [baseQuery, { _id: userId }]
@@ -2262,6 +2384,7 @@ function executeAiAssistantMongoRead(payload, context) {
2262
2384
  collectionMatched: collectionResolution.matched,
2263
2385
  collectionCandidates: collectionResolution.candidates,
2264
2386
  collectionScore: collectionResolution.score,
2387
+ bridge: 'report-builder',
2265
2388
  database: dbName,
2266
2389
  query: executedQuery,
2267
2390
  options: normalized.findOptions,
@@ -2275,7 +2398,7 @@ function executeAiAssistantMongoRead(payload, context) {
2275
2398
  }
2276
2399
  function executeAiAssistantMongoAggregate(payload, context) {
2277
2400
  return __awaiter(this, void 0, void 0, function () {
2278
- var input, rawCollection, dbName, db, collectionResolution, collection, schemaFields, _a, user, isSuperAdmin, customerId, fallbackMeta, baseQuery, stripped, userId, normalizedClient, shouldScopeByClient, _b, clientScopedQuery, scopedQuery, normalizedPipeline, sanitizedPipeline, strippedPipeline, pipelineWithScope, normalizedOptions, limitedPipeline, dateField, aggregateOptions, documents, executedPipeline, probeDocs, fallback, fallbackPipeline, fallbackDocs, createdFallback, createdPipeline, createdDocs, expanded, expandedDocs, completionFallback, fallbackPipeline, fallbackDocs, completionExprFallback, fallbackPipeline, fallbackDocs, unwindFallback, shouldUnwind, _c, _d, fallbackPipeline, fallbackDocs, nameFallback, fallbackPipeline, fallbackDocs, _e, _loop_1, i, state_1, matchFields_1, _f, aliases, rewrittenPipeline, fallbackDocs, _loop_2, i, state_2, baseCollection, fallbackPayload, fallbackResult, existingFallbacks, matchStages, diagnostics, combinedMatch, nameFields, dateFields, queryNoName, _g, queryNoDate, _h, _j, _k, _l, allCollections, base, alt, altCount, _m, sanitizedDocuments, includeIds, displayDocs, idLookupDisplay, display;
2401
+ var input, rawCollection, dbName, db, collectionResolution, collection, bridgeCollection, schemaFields, _a, user, isSuperAdmin, customerId, fallbackMeta, baseQuery, stripped, userId, normalizedClient, shouldScopeByClient, _b, clientScopedQuery, scopedQuery, normalizedPipeline, sanitizedPipeline, strippedPipeline, pipelineWithScope, normalizedOptions, limitedPipeline, dateField, aggregateOptions, documents, executedPipeline, probeDocs, fallback, fallbackPipeline, fallbackDocs, createdFallback, createdPipeline, createdDocs, expanded, expandedDocs, completionFallback, fallbackPipeline, fallbackDocs, completionExprFallback, fallbackPipeline, fallbackDocs, unwindFallback, shouldUnwind, _c, _d, fallbackPipeline, fallbackDocs, nameFallback, fallbackPipeline, fallbackDocs, _e, _loop_1, i, state_1, matchFields_1, _f, aliases, rewrittenPipeline, fallbackDocs, _loop_2, i, state_2, baseCollection, fallbackPayload, fallbackResult, existingFallbacks, matchStages, diagnostics, combinedMatch, nameFields, dateFields, queryNoName, _g, queryNoDate, _h, _j, _k, _l, allCollections, base, alt, altCount, _m, sanitizedDocuments, includeIds, displayDocs, idLookupDisplay, display;
2279
2402
  var _o, _p;
2280
2403
  return __generator(this, function (_q) {
2281
2404
  switch (_q.label) {
@@ -2283,7 +2406,7 @@ function executeAiAssistantMongoAggregate(payload, context) {
2283
2406
  input = payload || {};
2284
2407
  rawCollection = normalizeOptionalString(input.collection);
2285
2408
  if (!rawCollection) {
2286
- throw new Error('AI assistant mongo aggregate: Collection is required.');
2409
+ throw new Error('AI assistant report builder bridge: Collection is required.');
2287
2410
  }
2288
2411
  dbName = resolveAssistantDatabaseName(input.database, input.mongo);
2289
2412
  db = resolveio_server_app_1.ResolveIOServer.getMongoConnection().db(dbName);
@@ -2291,15 +2414,26 @@ function executeAiAssistantMongoAggregate(payload, context) {
2291
2414
  case 1:
2292
2415
  collectionResolution = _q.sent();
2293
2416
  collection = collectionResolution.name || rawCollection;
2417
+ bridgeCollection = resolveAssistantReportBuilderBridgeCollection(collection);
2418
+ if (bridgeCollection.fallbackFrom) {
2419
+ collection = bridgeCollection.collection;
2420
+ }
2294
2421
  schemaFields = getCollectionSchemaFieldNames(collection);
2295
2422
  return [4 /*yield*/, ensureAssistantReadAccess(context, input.permissionView, collection)];
2296
2423
  case 2:
2297
2424
  _a = _q.sent(), user = _a.user, isSuperAdmin = _a.isSuperAdmin;
2298
2425
  if (!isSuperAdmin && AI_ASSISTANT_BLOCKED_COLLECTIONS.has(collection)) {
2299
- throw new Error('AI assistant mongo aggregate: Access denied.');
2426
+ throw new Error('AI assistant report builder bridge: Access denied.');
2300
2427
  }
2301
2428
  customerId = normalizeOptionalString((_o = user === null || user === void 0 ? void 0 : user.other) === null || _o === void 0 ? void 0 : _o.id_customer);
2302
2429
  fallbackMeta = {};
2430
+ if (bridgeCollection.fallbackFrom) {
2431
+ fallbackMeta.reportBuilderBridge = {
2432
+ from: bridgeCollection.fallbackFrom,
2433
+ to: collection,
2434
+ used: true
2435
+ };
2436
+ }
2303
2437
  baseQuery = normalizeMongoQuery(input.query);
2304
2438
  if (!isSuperAdmin && customerId) {
2305
2439
  stripped = stripQueryFieldPathsDeepWithMeta(baseQuery, ['id_customer', 'other.id_customer']);
@@ -2313,7 +2447,7 @@ function executeAiAssistantMongoAggregate(payload, context) {
2313
2447
  if (!isSuperAdmin && (collection === 'users' || collection === 'user-versions')) {
2314
2448
  userId = normalizeOptionalString(user === null || user === void 0 ? void 0 : user._id);
2315
2449
  if (!userId) {
2316
- throw new Error('AI assistant mongo aggregate: Access denied.');
2450
+ throw new Error('AI assistant report builder bridge: Access denied.');
2317
2451
  }
2318
2452
  baseQuery = {
2319
2453
  $and: [baseQuery, { _id: userId }]
@@ -2350,7 +2484,7 @@ function executeAiAssistantMongoAggregate(payload, context) {
2350
2484
  limitedPipeline = applyAssistantAggregateLimit(pipelineWithScope, normalizedOptions.limit, normalizedOptions.maxLimit, normalizedOptions.defaultLimit);
2351
2485
  dateField = findAggregateDateField(limitedPipeline);
2352
2486
  if (containsForbiddenMongoOperators(limitedPipeline)) {
2353
- throw new Error('AI assistant mongo aggregate: Pipeline contains restricted operators.');
2487
+ throw new Error('AI assistant report builder bridge: Pipeline contains restricted operators.');
2354
2488
  }
2355
2489
  aggregateOptions = __assign(__assign({}, normalizedOptions.aggregateOptions), { readPreference: AI_ASSISTANT_READ_PREFERENCE });
2356
2490
  return [4 /*yield*/, db.collection(collection)
@@ -2878,6 +3012,7 @@ function executeAiAssistantMongoAggregate(payload, context) {
2878
3012
  collectionMatched: collectionResolution.matched,
2879
3013
  collectionCandidates: collectionResolution.candidates,
2880
3014
  collectionScore: collectionResolution.score,
3015
+ bridge: 'report-builder',
2881
3016
  database: dbName,
2882
3017
  query: scopedQuery,
2883
3018
  options: normalizedOptions.aggregateOptions,
@@ -2899,6 +3034,21 @@ function extractAssistantMongoDirective(content) {
2899
3034
  lines.forEach(function (line, index) {
2900
3035
  var normalized = line.trim().replace(/^[-*]+\s*/, '');
2901
3036
  var upper = normalized.toUpperCase();
3037
+ if (upper.startsWith('REPORT_BUILDER_READ:')) {
3038
+ directiveIndexes.add(index);
3039
+ directiveIndex = index;
3040
+ directiveLine = normalized;
3041
+ directiveType = 'read';
3042
+ return;
3043
+ }
3044
+ if (upper.startsWith('REPORT_BUILDER_AGG:') || upper.startsWith('REPORT_BUILDER_AGGREGATE:')) {
3045
+ directiveIndexes.add(index);
3046
+ directiveIndex = index;
3047
+ directiveLine = normalized;
3048
+ directiveType = 'aggregate';
3049
+ return;
3050
+ }
3051
+ // Backward compatibility for existing assistant responses.
2902
3052
  if (upper.startsWith('MONGO_READ:')) {
2903
3053
  directiveIndexes.add(index);
2904
3054
  directiveIndex = index;
@@ -3018,7 +3168,7 @@ function buildAssistantCodexToolFollowupPrompt(message, attachmentText, historyT
3018
3168
  var trimmedHistory = normalizeOptionalString(historyText);
3019
3169
  var historyBlock = trimmedHistory ? "\n\nConversation so far:\n".concat(trimmedHistory) : '';
3020
3170
  var toolBlock = toolResultText ? "\n\nTool Result:\n".concat(toolResultText) : '';
3021
- var instruction = '\n\nInstruction:\nNow answer the user. Do NOT output another MONGO_* directive. Output plain Markdown. Summarize first, then include a Markdown table.';
3171
+ var instruction = '\n\nInstruction:\nNow answer the user. Do NOT output another REPORT_BUILDER_* directive. Output plain Markdown. Summarize first, then include a Markdown table.';
3022
3172
  return "System:\n".concat(AI_ASSISTANT_SYSTEM_PROMPT).concat(contextBlock).concat(historyBlock, "\n\nUser:\n").concat(message).concat(attachmentText || '').concat(toolBlock).concat(instruction).trim();
3023
3173
  }
3024
3174
  function buildAssistantToolFallbackResponse(result) {
@@ -3038,13 +3188,13 @@ function buildAssistantToolFallbackResponse(result) {
3038
3188
  return lines.join('\n').trim();
3039
3189
  }
3040
3190
  function buildAssistantDebugPayload(params) {
3041
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3;
3191
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4;
3042
3192
  var notes = [];
3043
3193
  if (params.dataQuestion) {
3044
3194
  notes.push('Detected a data request; tool call required.');
3045
3195
  }
3046
3196
  if (params.directiveSource === 'forced') {
3047
- notes.push('Assistant response omitted a MONGO_* directive; ran a directive-only pass.');
3197
+ notes.push('Assistant response omitted a REPORT_BUILDER_* directive; ran a directive-only pass.');
3048
3198
  }
3049
3199
  if ((_a = params.collectionOverride) === null || _a === void 0 ? void 0 : _a.to) {
3050
3200
  var from = params.collectionOverride.from ? "\"".concat(params.collectionOverride.from, "\"") : '(none)';
@@ -3139,19 +3289,24 @@ function buildAssistantDebugPayload(params) {
3139
3289
  var collection = fallbackInfo.chemicalLookup.collection ? " in ".concat(fallbackInfo.chemicalLookup.collection) : '';
3140
3290
  notes.push("Applied chemical lookup".concat(collection, "."));
3141
3291
  }
3142
- if ((_u = fallbackInfo === null || fallbackInfo === void 0 ? void 0 : fallbackInfo.reportFallback) === null || _u === void 0 ? void 0 : _u.used) {
3292
+ if ((_u = fallbackInfo === null || fallbackInfo === void 0 ? void 0 : fallbackInfo.reportBuilderBridge) === null || _u === void 0 ? void 0 : _u.used) {
3293
+ var from = fallbackInfo.reportBuilderBridge.from || 'unknown';
3294
+ var to = fallbackInfo.reportBuilderBridge.to || 'unknown';
3295
+ notes.push("Report builder bridge collection: ".concat(from, " -> ").concat(to, "."));
3296
+ }
3297
+ if ((_v = fallbackInfo === null || fallbackInfo === void 0 ? void 0 : fallbackInfo.reportFallback) === null || _v === void 0 ? void 0 : _v.used) {
3143
3298
  var from = fallbackInfo.reportFallback.from || 'report';
3144
3299
  var to = fallbackInfo.reportFallback.to || 'base';
3145
3300
  notes.push("Report fallback: ".concat(from, " -> ").concat(to, "."));
3146
3301
  }
3147
- if ((_v = fallbackInfo === null || fallbackInfo === void 0 ? void 0 : fallbackInfo.idLookup) === null || _v === void 0 ? void 0 : _v.used) {
3302
+ if ((_w = fallbackInfo === null || fallbackInfo === void 0 ? void 0 : fallbackInfo.idLookup) === null || _w === void 0 ? void 0 : _w.used) {
3148
3303
  var field = fallbackInfo.idLookup.field || 'id';
3149
3304
  var strategy = fallbackInfo.idLookup.strategy || 'lookup';
3150
3305
  var collection = fallbackInfo.idLookup.collection ? " in ".concat(fallbackInfo.idLookup.collection) : '';
3151
3306
  var detail = fallbackInfo.idLookup.nameField ? " via ".concat(fallbackInfo.idLookup.nameField) : '';
3152
3307
  notes.push("Resolved ".concat(field, " by ").concat(strategy).concat(collection).concat(detail, "."));
3153
3308
  }
3154
- if ((_w = fallbackInfo === null || fallbackInfo === void 0 ? void 0 : fallbackInfo.idDisplayLookup) === null || _w === void 0 ? void 0 : _w.used) {
3309
+ if ((_x = fallbackInfo === null || fallbackInfo === void 0 ? void 0 : fallbackInfo.idDisplayLookup) === null || _x === void 0 ? void 0 : _x.used) {
3155
3310
  var lookups = Array.isArray(fallbackInfo.idDisplayLookup.lookups)
3156
3311
  ? fallbackInfo.idDisplayLookup.lookups
3157
3312
  : [];
@@ -3162,7 +3317,7 @@ function buildAssistantDebugPayload(params) {
3162
3317
  notes.push(summary ? "Resolved id lookups for display: ".concat(summary, ".") : 'Resolved id lookups for display.');
3163
3318
  }
3164
3319
  if (params.toolError) {
3165
- var errorMessage = ((_x = params.toolError) === null || _x === void 0 ? void 0 : _x.message) || String(params.toolError || '');
3320
+ var errorMessage = ((_y = params.toolError) === null || _y === void 0 ? void 0 : _y.message) || String(params.toolError || '');
3166
3321
  if (errorMessage) {
3167
3322
  notes.push("Tool error: ".concat(errorMessage));
3168
3323
  }
@@ -3183,13 +3338,13 @@ function buildAssistantDebugPayload(params) {
3183
3338
  collectionResolved: resolvedCollection || undefined,
3184
3339
  collectionMatched: matchedCollection,
3185
3340
  collectionCandidates: candidateCollections.length ? candidateCollections : undefined,
3186
- collectionScore: typeof ((_y = params.toolResponseDebug) === null || _y === void 0 ? void 0 : _y.collectionScore) === 'number'
3341
+ collectionScore: typeof ((_z = params.toolResponseDebug) === null || _z === void 0 ? void 0 : _z.collectionScore) === 'number'
3187
3342
  ? params.toolResponseDebug.collectionScore
3188
3343
  : undefined,
3189
- query: ((_z = params.toolResponseDebug) === null || _z === void 0 ? void 0 : _z.query) || undefined,
3190
- pipeline: ((_0 = params.toolResponseDebug) === null || _0 === void 0 ? void 0 : _0.executedPipeline) || ((_1 = params.toolResponseDebug) === null || _1 === void 0 ? void 0 : _1.originalPipeline) || undefined,
3191
- options: ((_2 = params.toolResponseDebug) === null || _2 === void 0 ? void 0 : _2.options) || undefined,
3192
- fallbacks: ((_3 = params.toolResponseDebug) === null || _3 === void 0 ? void 0 : _3.fallbacks) || undefined,
3344
+ query: ((_0 = params.toolResponseDebug) === null || _0 === void 0 ? void 0 : _0.query) || undefined,
3345
+ pipeline: ((_1 = params.toolResponseDebug) === null || _1 === void 0 ? void 0 : _1.executedPipeline) || ((_2 = params.toolResponseDebug) === null || _2 === void 0 ? void 0 : _2.originalPipeline) || undefined,
3346
+ options: ((_3 = params.toolResponseDebug) === null || _3 === void 0 ? void 0 : _3.options) || undefined,
3347
+ fallbacks: ((_4 = params.toolResponseDebug) === null || _4 === void 0 ? void 0 : _4.fallbacks) || undefined,
3193
3348
  notes: notes
3194
3349
  };
3195
3350
  if (params.trace && typeof params.trace === 'object') {
@@ -3222,6 +3377,9 @@ function buildAssistantToolErrorMessage(error, directive, request) {
3222
3377
  if (normalized.includes('database access denied')) {
3223
3378
  return "Database access is restricted for that request. ".concat(routeLine);
3224
3379
  }
3380
+ if (normalized.includes('report builder bridge') && normalized.includes('not configured')) {
3381
+ return "That dataset is not configured for report builder access yet. ".concat(routeLine);
3382
+ }
3225
3383
  if (normalized.includes('collection is required')) {
3226
3384
  return 'I need a valid collection to read from. Please specify which screen or dataset you want.';
3227
3385
  }
@@ -4916,30 +5074,30 @@ function ensureAssistantReadAccess(context, permissionView, collection) {
4916
5074
  case 0:
4917
5075
  idUser = context === null || context === void 0 ? void 0 : context.id_user;
4918
5076
  if (!idUser) {
4919
- throw new Error('AI assistant mongo read: Unauthorized.');
5077
+ throw new Error('AI assistant report builder bridge: Unauthorized.');
4920
5078
  }
4921
5079
  return [4 /*yield*/, user_collection_1.Users.findById(idUser)];
4922
5080
  case 1:
4923
5081
  user = _b.sent();
4924
5082
  if (!user) {
4925
- throw new Error('AI assistant mongo read: Unauthorized.');
5083
+ throw new Error('AI assistant report builder bridge: Unauthorized.');
4926
5084
  }
4927
5085
  isSuperAdmin = !!((_a = user === null || user === void 0 ? void 0 : user.roles) === null || _a === void 0 ? void 0 : _a.super_admin);
4928
5086
  normalizedPermission = normalizeOptionalString(permissionView);
4929
5087
  if (!isSuperAdmin) {
4930
5088
  if (!normalizedPermission) {
4931
- throw new Error('AI assistant mongo read: Permission scope required.');
5089
+ throw new Error('AI assistant report builder bridge: Permission scope required.');
4932
5090
  }
4933
5091
  if (!userHasViewPermission(user, normalizedPermission)) {
4934
- throw new Error('AI assistant mongo read: Access denied.');
5092
+ throw new Error('AI assistant report builder bridge: Access denied.');
4935
5093
  }
4936
5094
  normalizedCollection = normalizeOptionalString(collection);
4937
5095
  if (normalizedCollection && requiresInvoicePermission(normalizedCollection) && !userHasInvoiceAccess(user)) {
4938
- throw new Error('AI assistant mongo read: Access denied.');
5096
+ throw new Error('AI assistant report builder bridge: Access denied.');
4939
5097
  }
4940
5098
  }
4941
5099
  else if (normalizedPermission && !userHasViewPermission(user, normalizedPermission)) {
4942
- throw new Error('AI assistant mongo read: Access denied.');
5100
+ throw new Error('AI assistant report builder bridge: Access denied.');
4943
5101
  }
4944
5102
  return [2 /*return*/, { user: user, isSuperAdmin: isSuperAdmin }];
4945
5103
  }
@@ -4951,17 +5109,17 @@ function resolveAssistantDatabaseName(database, mongoConfig) {
4951
5109
  var defaultDb = normalizeOptionalString(mongoConfig === null || mongoConfig === void 0 ? void 0 : mongoConfig.database) || ((_a = resolveio_server_app_1.ResolveIOServer.getServerConfig()) === null || _a === void 0 ? void 0 : _a.DATABASE) || '';
4952
5110
  var dbName = normalizeOptionalString(database) || defaultDb;
4953
5111
  if (!dbName) {
4954
- throw new Error('AI assistant mongo read: Database is required.');
5112
+ throw new Error('AI assistant report builder bridge: Database is required.');
4955
5113
  }
4956
5114
  var allowedFromConfig = Array.isArray(mongoConfig === null || mongoConfig === void 0 ? void 0 : mongoConfig.databases)
4957
5115
  ? mongoConfig === null || mongoConfig === void 0 ? void 0 : mongoConfig.databases.map(function (value) { return normalizeOptionalString(value); }).filter(Boolean)
4958
5116
  : [];
4959
5117
  if (allowedFromConfig.length && !allowedFromConfig.includes(dbName)) {
4960
- throw new Error('AI assistant mongo read: Database access denied.');
5118
+ throw new Error('AI assistant report builder bridge: Database access denied.');
4961
5119
  }
4962
5120
  var allowedDatabases = ((_b = resolveio_server_app_1.ResolveIOServer.getMongoManager()) === null || _b === void 0 ? void 0 : _b.getWatchedDatabases()) || [];
4963
5121
  if (allowedDatabases.length && !allowedDatabases.includes(dbName)) {
4964
- throw new Error('AI assistant mongo read: Database access denied.');
5122
+ throw new Error('AI assistant report builder bridge: Database access denied.');
4965
5123
  }
4966
5124
  return dbName;
4967
5125
  }
@@ -6806,7 +6964,7 @@ function replaceAggregateDateField(pipeline, fromField, toField) {
6806
6964
  function normalizeMongoQuery(query) {
6807
6965
  var normalized = query && typeof query === 'object' ? query : {};
6808
6966
  if (containsForbiddenMongoOperators(normalized)) {
6809
- throw new Error('AI assistant mongo read: Query contains restricted operators.');
6967
+ throw new Error('AI assistant report builder bridge: Query contains restricted operators.');
6810
6968
  }
6811
6969
  var rewritten = rewriteEmbeddedMatchObjects(normalized);
6812
6970
  return applyAssistantNameRegexToQuery(rewritten);
@@ -7073,9 +7231,33 @@ function expandAssistantTermSynonyms(text) {
7073
7231
  });
7074
7232
  return { expanded: expanded, matches: matches };
7075
7233
  }
7234
+ function listAssistantReportBuilderCollectionsFromManager() {
7235
+ var _a;
7236
+ var manager = (_a = resolveio_server_app_1.ResolveIOServer.getMongoManager) === null || _a === void 0 ? void 0 : _a.call(resolveio_server_app_1.ResolveIOServer);
7237
+ var collectionModels = manager && typeof manager.collections === 'function'
7238
+ ? manager.collections()
7239
+ : [];
7240
+ if (!Array.isArray(collectionModels) || !collectionModels.length) {
7241
+ return [];
7242
+ }
7243
+ var seen = new Set();
7244
+ var names = [];
7245
+ collectionModels.forEach(function (model) {
7246
+ if (!(model === null || model === void 0 ? void 0 : model.useRB)) {
7247
+ return;
7248
+ }
7249
+ var name = normalizeOptionalString(model === null || model === void 0 ? void 0 : model.collectionName);
7250
+ if (!name || seen.has(name)) {
7251
+ return;
7252
+ }
7253
+ seen.add(name);
7254
+ names.push(name);
7255
+ });
7256
+ return names;
7257
+ }
7076
7258
  function listAssistantCollections(db, dbName) {
7077
7259
  return __awaiter(this, void 0, void 0, function () {
7078
- var cacheKey, cached, now, collections, names, _a;
7260
+ var cacheKey, cached, now, reportBuilderCollections, collections, names, _a;
7079
7261
  return __generator(this, function (_b) {
7080
7262
  switch (_b.label) {
7081
7263
  case 0:
@@ -7085,6 +7267,11 @@ function listAssistantCollections(db, dbName) {
7085
7267
  if (cached && now - cached.updatedAt < AI_ASSISTANT_COLLECTION_CACHE_TTL_MS) {
7086
7268
  return [2 /*return*/, cached.names];
7087
7269
  }
7270
+ reportBuilderCollections = listAssistantReportBuilderCollectionsFromManager().sort(function (a, b) { return a.localeCompare(b); });
7271
+ if (reportBuilderCollections.length) {
7272
+ AI_ASSISTANT_COLLECTION_CACHE.set(cacheKey, { names: reportBuilderCollections, updatedAt: now });
7273
+ return [2 /*return*/, reportBuilderCollections];
7274
+ }
7088
7275
  _b.label = 1;
7089
7276
  case 1:
7090
7277
  _b.trys.push([1, 3, , 4]);
@@ -7491,6 +7678,36 @@ function resolveAssistantCollectionName(db, dbName, requested) {
7491
7678
  });
7492
7679
  });
7493
7680
  }
7681
+ function resolveAssistantReportBuilderBridgeCollection(collection) {
7682
+ var _a;
7683
+ var normalized = normalizeOptionalString(collection);
7684
+ if (!normalized) {
7685
+ throw new Error('AI assistant report builder bridge: Collection is required.');
7686
+ }
7687
+ var manager = (_a = resolveio_server_app_1.ResolveIOServer.getMongoManager) === null || _a === void 0 ? void 0 : _a.call(resolveio_server_app_1.ResolveIOServer);
7688
+ if (!manager || typeof manager.collection !== 'function') {
7689
+ throw new Error('AI assistant report builder bridge: Mongo manager unavailable.');
7690
+ }
7691
+ var directModel = manager.collection(normalized);
7692
+ if (directModel === null || directModel === void 0 ? void 0 : directModel.useRB) {
7693
+ return { collection: normalized };
7694
+ }
7695
+ var base = stripVersionSuffix(normalized.startsWith('report-') ? normalized.slice('report-'.length) : normalized);
7696
+ var reportCandidate = base ? "report-".concat(base) : '';
7697
+ if (reportCandidate && reportCandidate !== normalized) {
7698
+ var reportModel = manager.collection(reportCandidate);
7699
+ if (reportModel === null || reportModel === void 0 ? void 0 : reportModel.useRB) {
7700
+ return { collection: reportCandidate, fallbackFrom: normalized };
7701
+ }
7702
+ }
7703
+ if (base && base !== normalized) {
7704
+ var baseModel = manager.collection(base);
7705
+ if (baseModel === null || baseModel === void 0 ? void 0 : baseModel.useRB) {
7706
+ return { collection: base, fallbackFrom: normalized };
7707
+ }
7708
+ }
7709
+ throw new Error('AI assistant report builder bridge: Collection is not configured for report builder.');
7710
+ }
7494
7711
  function findQueryDateField(query) {
7495
7712
  var e_22, _a, e_23, _b;
7496
7713
  if (!query || typeof query !== 'object') {
@@ -8555,7 +8772,7 @@ function buildAssistantCodexDirectivePrompt(message, attachmentText, historyText
8555
8772
  var contextBlock = trimmedContext ? "\n\nContext:\n".concat(trimmedContext) : '';
8556
8773
  var trimmedHistory = normalizeOptionalString(historyText);
8557
8774
  var historyBlock = trimmedHistory ? "\n\nConversation so far:\n".concat(trimmedHistory) : '';
8558
- var instruction = '\n\nInstruction:\nReturn ONLY a single MONGO_READ or MONGO_AGG directive line. Do not include any other text.';
8775
+ var instruction = '\n\nInstruction:\nReturn ONLY a single REPORT_BUILDER_READ or REPORT_BUILDER_AGG directive line. Do not include any other text.';
8559
8776
  return "System:\n".concat(AI_ASSISTANT_SYSTEM_PROMPT).concat(contextBlock).concat(historyBlock, "\n\nUser:\n").concat(message).concat(attachmentText || '').concat(instruction).trim();
8560
8777
  }
8561
8778
  function buildAssistantCodexPrompt(message, attachmentText, historyText, contextText) {