@resolveio/server-lib 22.0.13 → 22.0.14

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.
@@ -373,7 +373,7 @@ var AI_ASSISTANT_TERM_SYNONYMS = [
373
373
  var AI_ASSISTANT_REPORT_BUILDER_EXPERT_PLAYBOOK = [
374
374
  'Report Builder Bridge Expert Playbook (execute in order for every data request):',
375
375
  '1) Determine if data is required.',
376
- '- If user asks for counts, lists, totals, trends, rankings, or "recent/last", run a report builder directive before answering.',
376
+ '- If user asks for counts, lists, totals, trends, rankings, or "recent/last", run a data directive before answering.',
377
377
  '',
378
378
  'Report style mapping (always apply):',
379
379
  '- Dated report: any breakdown over time (per day/week/month/quarter/year, trends, time-series comparisons).',
@@ -388,7 +388,7 @@ var AI_ASSISTANT_REPORT_BUILDER_EXPERT_PLAYBOOK = [
388
388
  '',
389
389
  '3) Enforce permissions and scope in the directive.',
390
390
  '- Always include permissionView.',
391
- '- Use /report-builder as the default permissionView for data directives; avoid /report/* routes.',
391
+ '- Prefer module-specific permissionView routes (for example /invoice/list for invoices). Avoid /report/* routes and only use /report-builder when the user is explicitly asking about report building.',
392
392
  '- Assume non-super-admin unless explicitly told otherwise.',
393
393
  '- Invoice-like collections require invoice view access.',
394
394
  '- Customer portal users must stay in their own customer scope.',
@@ -492,8 +492,8 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
492
492
  '- REPORT_BUILDER_READ: {"collection":"<name>","query":{...},"options":{"projection":{...},"sort":{...},"limit":20},"permissionView":"</route>"}',
493
493
  '- If you need grouped/aggregated data (totals by user, rankings, trends), end your response with a single line exactly in this format:',
494
494
  '- REPORT_BUILDER_AGG: {"collection":"<name>","pipeline":[...],"options":{"allowDiskUse":true,"limit":20},"permissionView":"</route>"}',
495
- '- For invoice data, set permissionView to an invoice-capable route (ex: /invoice/list or /report-builder/list).',
496
- '- Do not use /report/* routes as permissionView for data directives; use /report-builder or a module route.',
495
+ '- For invoice data, set permissionView to an invoice-capable route (ex: /invoice/list).',
496
+ '- Do not use /report/* routes as permissionView for data directives; use a module route that matches the collection.',
497
497
  '- For revenue/sales/billing questions, use invoices and sum paid_total (fallback to grand_total) with date_paid and Paid/Closed status when available.',
498
498
  '- For revenue answers, always state the metric/date basis used (paid_total/date_paid vs grand_total/date_invoice). If tool verification warns about ambiguity or partial months, call that out before totals.',
499
499
  '- For relative date ranges (last/past/recent), include an upper bound <= $$NOW unless the user specifies a future end date.',
@@ -3624,7 +3624,7 @@ function buildAssistantToolRequest(directive, payload) {
3624
3624
  var base = directive.payload && typeof directive.payload === 'object' ? directive.payload : {};
3625
3625
  var request = __assign({}, base);
3626
3626
  if (!request.permissionView) {
3627
- request.permissionView = '/report-builder';
3627
+ request.permissionView = resolveDefaultAssistantPermissionView(request.collection);
3628
3628
  }
3629
3629
  request.permissionView = normalizeAssistantPermissionView(request.permissionView, request.collection);
3630
3630
  if (!request.id_client) {
@@ -3638,21 +3638,53 @@ function buildAssistantToolRequest(directive, payload) {
3638
3638
  }
3639
3639
  return request;
3640
3640
  }
3641
+ function resolveDefaultAssistantPermissionView(collection) {
3642
+ var normalizedCollection = normalizeOptionalString(collection).toLowerCase();
3643
+ if (!normalizedCollection) {
3644
+ return '/report-builder';
3645
+ }
3646
+ var base = stripVersionSuffix(normalizedCollection.startsWith('report-')
3647
+ ? normalizedCollection.slice('report-'.length)
3648
+ : normalizedCollection);
3649
+ if (!base) {
3650
+ return '/report-builder';
3651
+ }
3652
+ if (requiresInvoicePermission(base)) {
3653
+ return '/invoice/list';
3654
+ }
3655
+ if (base.startsWith('sales-tax')) {
3656
+ return '/sales-tax/list';
3657
+ }
3658
+ if (base.startsWith('expense')) {
3659
+ return '/expense/list';
3660
+ }
3661
+ if (base.startsWith('distribution')) {
3662
+ return '/distribution/list';
3663
+ }
3664
+ if (base.startsWith('prospective-client')) {
3665
+ return '/prospective-client/list';
3666
+ }
3667
+ if (base.startsWith('client')) {
3668
+ return '/client/list';
3669
+ }
3670
+ if (base.startsWith('employee')) {
3671
+ return '/employee/list';
3672
+ }
3673
+ return '/report-builder';
3674
+ }
3641
3675
  function normalizeAssistantPermissionView(permissionView, collection) {
3642
3676
  var normalizedPermission = normalizeOptionalString(permissionView);
3643
3677
  var normalizedCollection = normalizeOptionalString(collection);
3644
3678
  var loweredPermission = normalizedPermission.toLowerCase();
3679
+ var fallbackPermission = resolveDefaultAssistantPermissionView(normalizedCollection);
3645
3680
  if (!normalizedPermission) {
3646
- return '/report-builder';
3681
+ return fallbackPermission;
3647
3682
  }
3648
3683
  if (loweredPermission === '/report-builder' || loweredPermission.startsWith('/report-builder/')) {
3649
- return '/report-builder';
3684
+ return fallbackPermission;
3650
3685
  }
3651
3686
  if (loweredPermission === '/report' || loweredPermission.startsWith('/report/')) {
3652
- if (requiresInvoicePermission(normalizedCollection)) {
3653
- return '/invoice/list';
3654
- }
3655
- return '/report-builder';
3687
+ return fallbackPermission;
3656
3688
  }
3657
3689
  return normalizedPermission;
3658
3690
  }
@@ -4033,14 +4065,13 @@ function buildAssistantToolErrorMessage(error, directive, request) {
4033
4065
  var _a, _b;
4034
4066
  var rawMessage = normalizeOptionalString(error === null || error === void 0 ? void 0 : error.message) || 'Unable to access data.';
4035
4067
  var normalized = rawMessage.toLowerCase();
4036
- var routeHint = normalizeOptionalString(request === null || request === void 0 ? void 0 : request.permissionView)
4037
- || normalizeOptionalString((_a = directive.payload) === null || _a === void 0 ? void 0 : _a.permissionView);
4038
- var collection = normalizeOptionalString(request === null || request === void 0 ? void 0 : request.collection) || normalizeOptionalString((_b = directive.payload) === null || _b === void 0 ? void 0 : _b.collection);
4068
+ var collection = normalizeOptionalString(request === null || request === void 0 ? void 0 : request.collection) || normalizeOptionalString((_a = directive.payload) === null || _a === void 0 ? void 0 : _a.collection);
4069
+ var routeHint = resolveAssistantErrorRouteHint(normalizeOptionalString(request === null || request === void 0 ? void 0 : request.permissionView) || normalizeOptionalString((_b = directive.payload) === null || _b === void 0 ? void 0 : _b.permissionView), collection);
4039
4070
  var routeLine = routeHint
4040
4071
  ? "Open ".concat(routeHint, " in the app to view this data or request access.")
4041
- : 'Open the related screen in the app to view this data or request access.';
4072
+ : 'Request access to the related module in the app.';
4042
4073
  if (!routeHint && collection && requiresInvoicePermission(collection)) {
4043
- routeLine = 'Open /invoice/list or /report-builder/list to view this data or request access.';
4074
+ routeLine = 'Open /invoice/list in the app to view this data or request access.';
4044
4075
  }
4045
4076
  if (normalized.includes('permission scope required')) {
4046
4077
  return "I need a permission scope to access that data. ".concat(routeLine);
@@ -4057,14 +4088,30 @@ function buildAssistantToolErrorMessage(error, directive, request) {
4057
4088
  if (normalized.includes('undefined variable') && normalized.includes('now_minus')) {
4058
4089
  return "The query used an unsupported relative date token. Please retry; the assistant now normalizes relative dates automatically. ".concat(routeLine);
4059
4090
  }
4091
+ if (normalized.includes('invalid character for a variable name') && (normalized.includes('now-') || normalized.includes('now+'))) {
4092
+ return "The query used an unsupported relative date token. Please retry; the assistant now normalizes relative dates automatically. ".concat(routeLine);
4093
+ }
4060
4094
  if (normalized.includes('report builder bridge') && normalized.includes('not configured')) {
4061
- return "That dataset is not configured for report builder access yet. ".concat(routeLine);
4095
+ return "That dataset is not configured for assistant data access yet. ".concat(routeLine);
4062
4096
  }
4063
4097
  if (normalized.includes('collection is required')) {
4064
4098
  return 'I need a valid collection to read from. Please specify which screen or dataset you want.';
4065
4099
  }
4066
4100
  return "I couldn't access the requested data. ".concat(routeLine);
4067
4101
  }
4102
+ function resolveAssistantErrorRouteHint(routeHint, collection) {
4103
+ var normalizedHint = normalizeOptionalString(routeHint);
4104
+ if (!normalizedHint) {
4105
+ var fallback = resolveDefaultAssistantPermissionView(collection);
4106
+ return fallback.startsWith('/report-builder') ? '' : fallback;
4107
+ }
4108
+ var loweredHint = normalizedHint.toLowerCase();
4109
+ if (loweredHint.startsWith('/report-builder') || loweredHint === '/report' || loweredHint.startsWith('/report/')) {
4110
+ var fallback = resolveDefaultAssistantPermissionView(collection);
4111
+ return fallback.startsWith('/report-builder') ? '' : fallback;
4112
+ }
4113
+ return normalizedHint;
4114
+ }
4068
4115
  function deriveAssistantStreamStatus(event) {
4069
4116
  var _a;
4070
4117
  if (!event || !event.type) {
@@ -5599,8 +5646,25 @@ function resolveAssistantNumericValue(value) {
5599
5646
  function isAssistantPercentColumn(column) {
5600
5647
  return /(percent|pct|percentage|ratio|rate)\b/.test(column);
5601
5648
  }
5649
+ var ASSISTANT_NON_CURRENCY_COLUMN_PATTERN = /\b(invoice\s*number|count|qty|quantity|index|rank|sequence|seq|id|code|ticket|number)\b/;
5650
+ var ASSISTANT_CURRENCY_HINT_PATTERN = /\b(amount|price|cost|balance|fee|revenue|tax|billing|charge|payment|profit|margin|due)\b/;
5651
+ var ASSISTANT_MONEY_TOTAL_PATTERN = /\b(sub\s*total|subtotal|grand\s*total|paid\s*total)\b/;
5652
+ var ASSISTANT_TOTAL_WITH_MONEY_HINT_PATTERN = /\btotal\b.*\b(amount|revenue|sales|tax|price|cost|balance|paid|due|charge|billing|profit|margin)\b/;
5602
5653
  function isAssistantCurrencyColumn(column) {
5603
- return /(amount|total|price|cost|balance|fee|revenue|invoice|usd|tax|subtotal|paid|billing|sales)\b/.test(column);
5654
+ var normalized = String(column || '').toLowerCase().replace(/[_-]+/g, ' ').trim();
5655
+ if (!normalized) {
5656
+ return false;
5657
+ }
5658
+ var hasMoneyHint = ASSISTANT_CURRENCY_HINT_PATTERN.test(normalized)
5659
+ || ASSISTANT_MONEY_TOTAL_PATTERN.test(normalized)
5660
+ || ASSISTANT_TOTAL_WITH_MONEY_HINT_PATTERN.test(normalized);
5661
+ if (ASSISTANT_NON_CURRENCY_COLUMN_PATTERN.test(normalized) && !hasMoneyHint) {
5662
+ return false;
5663
+ }
5664
+ if (/\btotal\b/.test(normalized) && !hasMoneyHint) {
5665
+ return false;
5666
+ }
5667
+ return hasMoneyHint;
5604
5668
  }
5605
5669
  function isAssistantDateColumn(column) {
5606
5670
  return /(date|time|created|updated|timestamp|at)\b/.test(column);
@@ -5914,6 +5978,7 @@ function sanitizeAssistantProjection(projection) {
5914
5978
  }
5915
5979
  var AGG_MATCH_EXPR_OPERATORS = new Set(['$eq', '$ne', '$gt', '$gte', '$lt', '$lte']);
5916
5980
  var ASSISTANT_NOW_RELATIVE_PATTERN = /^\$\$NOW_(MINUS|PLUS)_([0-9]+)_(MINUTES?|HOURS?|DAYS?|WEEKS?|MONTHS?|YEARS?)$/i;
5981
+ var ASSISTANT_NOW_RELATIVE_COMPACT_PATTERN = /^\$\$NOW([+-])([0-9]+)\s*(MINUTES?|MINS?|MIN|HOURS?|HRS?|HR|DAYS?|WEEKS?|WKS?|WK|MONTHS?|MOS?|MO|M|YEARS?|YRS?|YR|Y)$/i;
5917
5982
  function normalizeAssistantTimeUnit(raw) {
5918
5983
  var normalized = String(raw || '').toLowerCase();
5919
5984
  if (normalized.startsWith('minute')) {
@@ -5933,21 +5998,54 @@ function normalizeAssistantTimeUnit(raw) {
5933
5998
  }
5934
5999
  return 'year';
5935
6000
  }
6001
+ function normalizeAssistantCompactTimeUnit(raw) {
6002
+ var normalized = String(raw || '').toLowerCase();
6003
+ if (normalized === 'm' || normalized === 'mo' || normalized === 'mos' || normalized.startsWith('month')) {
6004
+ return 'month';
6005
+ }
6006
+ if (normalized === 'min' || normalized === 'mins' || normalized.startsWith('minute')) {
6007
+ return 'minute';
6008
+ }
6009
+ if (normalized === 'h' || normalized === 'hr' || normalized === 'hrs' || normalized.startsWith('hour')) {
6010
+ return 'hour';
6011
+ }
6012
+ if (normalized === 'd' || normalized.startsWith('day')) {
6013
+ return 'day';
6014
+ }
6015
+ if (normalized === 'w' || normalized === 'wk' || normalized === 'wks' || normalized.startsWith('week')) {
6016
+ return 'week';
6017
+ }
6018
+ return 'year';
6019
+ }
5936
6020
  function parseAssistantNowRelativeToken(value) {
5937
6021
  var trimmed = normalizeOptionalString(value);
5938
6022
  if (!trimmed) {
5939
6023
  return null;
5940
6024
  }
5941
- var match = trimmed.match(ASSISTANT_NOW_RELATIVE_PATTERN);
5942
- if (!match) {
6025
+ var expandedMatch = trimmed.match(ASSISTANT_NOW_RELATIVE_PATTERN);
6026
+ if (expandedMatch) {
6027
+ var direction_1 = String(expandedMatch[1] || '').toUpperCase() === 'PLUS' ? 'PLUS' : 'MINUS';
6028
+ var amount_1 = Number(expandedMatch[2]);
6029
+ if (!Number.isFinite(amount_1) || amount_1 < 0) {
6030
+ return null;
6031
+ }
6032
+ var unit_1 = normalizeAssistantTimeUnit(expandedMatch[3] || '');
6033
+ return {
6034
+ operator: direction_1 === 'PLUS' ? '$dateAdd' : '$dateSubtract',
6035
+ amount: amount_1,
6036
+ unit: unit_1
6037
+ };
6038
+ }
6039
+ var compactMatch = trimmed.match(ASSISTANT_NOW_RELATIVE_COMPACT_PATTERN);
6040
+ if (!compactMatch) {
5943
6041
  return null;
5944
6042
  }
5945
- var direction = String(match[1] || '').toUpperCase() === 'PLUS' ? 'PLUS' : 'MINUS';
5946
- var amount = Number(match[2]);
6043
+ var direction = compactMatch[1] === '+' ? 'PLUS' : 'MINUS';
6044
+ var amount = Number(compactMatch[2]);
5947
6045
  if (!Number.isFinite(amount) || amount < 0) {
5948
6046
  return null;
5949
6047
  }
5950
- var unit = normalizeAssistantTimeUnit(match[3] || '');
6048
+ var unit = normalizeAssistantCompactTimeUnit(compactMatch[3] || '');
5951
6049
  return {
5952
6050
  operator: direction === 'PLUS' ? '$dateAdd' : '$dateSubtract',
5953
6051
  amount: amount,