@resolveio/server-lib 22.0.8 → 22.0.10

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.
@@ -166,6 +166,9 @@ var AI_ASSISTANT_PLANNER_MAX_ROUTES = 200;
166
166
  var AI_ASSISTANT_PLANNER_DEBUG_MAX_CHARS = 2000;
167
167
  var AI_ASSISTANT_LOCALE = 'en-US';
168
168
  var AI_ASSISTANT_CURRENCY_CODE = 'USD';
169
+ var AI_ASSISTANT_USD_CURRENCY_TEXT_PATTERN = /\b(?:USD|US\$)(?:\s| | |[\u00A0\u202F\u2007])*\$?\s*([-+]?[0-9][0-9,]*(?:\.[0-9]+)?)/gi;
170
+ var AI_ASSISTANT_REVENUE_VERIFY_DIFF_WARN_THRESHOLD = 0.15;
171
+ var AI_ASSISTANT_REVENUE_VERIFY_PAID_GRAND_GAP_WARN_THRESHOLD = 0.10;
169
172
  var AI_ASSISTANT_PROGRESS_TICKS = [
170
173
  'Grabbing Data',
171
174
  'Drafting response'
@@ -489,6 +492,7 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
489
492
  '- REPORT_BUILDER_AGG: {"collection":"<name>","pipeline":[...],"options":{"allowDiskUse":true,"limit":20},"permissionView":"</route>"}',
490
493
  '- For invoice data, set permissionView to an invoice route (ex: /invoice/list or /report/invoice).',
491
494
  '- For revenue/sales/billing questions, use invoices and sum paid_total (fallback to grand_total) with date_paid and Paid/Closed status when available.',
495
+ '- 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.',
492
496
  '- For relative date ranges (last/past/recent), include an upper bound <= $$NOW unless the user specifies a future end date.',
493
497
  '- Keep queries minimal, read-only, and avoid user/credential data unless the user is a super admin.',
494
498
  '- Assume you are not a super admin unless explicitly told otherwise.',
@@ -1799,6 +1803,8 @@ function executeAiAssistantCodexRun(payload, context) {
1799
1803
  if (!assistantContent) {
1800
1804
  assistantContent = buildAssistantCodexErrorMessage(null);
1801
1805
  }
1806
+ assistantContent = applyAssistantVerificationNotes(assistantContent, toolResult);
1807
+ assistantContent = normalizeAssistantCurrencyText(assistantContent);
1802
1808
  if (aiWorkerDebug) {
1803
1809
  finishedAt = Date.now();
1804
1810
  console.log(new Date(), '[AI Worker Debug] codex run complete', {
@@ -2398,7 +2404,7 @@ function executeAiAssistantMongoRead(payload, context) {
2398
2404
  }
2399
2405
  function executeAiAssistantMongoAggregate(payload, context) {
2400
2406
  return __awaiter(this, void 0, void 0, function () {
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;
2407
+ 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, verification, sanitizedDocuments, includeIds, displayDocs, idLookupDisplay, display;
2402
2408
  var _o, _p;
2403
2409
  return __generator(this, function (_q) {
2404
2410
  switch (_q.label) {
@@ -2975,7 +2981,15 @@ function executeAiAssistantMongoAggregate(payload, context) {
2975
2981
  case 60:
2976
2982
  fallbackMeta.zeroDiagnostics = diagnostics;
2977
2983
  _q.label = 61;
2978
- case 61:
2984
+ case 61: return [4 /*yield*/, verifyAssistantAggregateReliability({
2985
+ db: db,
2986
+ collection: collection,
2987
+ pipeline: executedPipeline,
2988
+ documents: documents,
2989
+ aggregateOptions: aggregateOptions
2990
+ })];
2991
+ case 62:
2992
+ verification = _q.sent();
2979
2993
  sanitizedDocuments = isSuperAdmin
2980
2994
  ? documents
2981
2995
  : documents.map(function (doc) { return redactSensitiveFields((0, common_1.deepCopy)(doc)); });
@@ -2990,7 +3004,7 @@ function executeAiAssistantMongoAggregate(payload, context) {
2990
3004
  idCustomer: customerId,
2991
3005
  isSuperAdmin: isSuperAdmin
2992
3006
  })];
2993
- case 62:
3007
+ case 63:
2994
3008
  idLookupDisplay = _q.sent();
2995
3009
  if (idLookupDisplay === null || idLookupDisplay === void 0 ? void 0 : idLookupDisplay.docs) {
2996
3010
  displayDocs = idLookupDisplay.docs;
@@ -3004,27 +3018,482 @@ function executeAiAssistantMongoAggregate(payload, context) {
3004
3018
  maxRows: normalizedOptions.limit || sanitizedDocuments.length,
3005
3019
  includeGroupFromId: true
3006
3020
  });
3007
- return [2 /*return*/, __assign({ documents: sanitizedDocuments, display: display }, (isSuperAdmin ? {
3008
- debug: {
3009
- collection: collection,
3010
- collectionRequested: rawCollection,
3011
- collectionResolved: collection,
3012
- collectionMatched: collectionResolution.matched,
3013
- collectionCandidates: collectionResolution.candidates,
3014
- collectionScore: collectionResolution.score,
3015
- bridge: 'report-builder',
3016
- database: dbName,
3017
- query: scopedQuery,
3018
- options: normalizedOptions.aggregateOptions,
3019
- originalPipeline: limitedPipeline,
3020
- executedPipeline: executedPipeline,
3021
- fallbacks: fallbackMeta
3022
- }
3021
+ return [2 /*return*/, __assign(__assign({ documents: sanitizedDocuments, display: display }, (verification ? { verification: verification } : {})), (isSuperAdmin ? {
3022
+ debug: __assign(__assign({ collection: collection, collectionRequested: rawCollection, collectionResolved: collection, collectionMatched: collectionResolution.matched, collectionCandidates: collectionResolution.candidates, collectionScore: collectionResolution.score, bridge: 'report-builder', database: dbName, query: scopedQuery, options: normalizedOptions.aggregateOptions, originalPipeline: limitedPipeline, executedPipeline: executedPipeline }, (verification ? { verification: verification } : {})), { fallbacks: fallbackMeta })
3023
3023
  } : {}))];
3024
3024
  }
3025
3025
  });
3026
3026
  });
3027
3027
  }
3028
+ function verifyAssistantAggregateReliability(params) {
3029
+ return __awaiter(this, void 0, void 0, function () {
3030
+ var collection, collectionBase, monthKey, amountKey, baseMap, groupIndex, groupPaths, dateField, mergedMatch, dateFieldsInMatch, baseMatchNoDate, primaryDateCondition, verifyPipeline, verifyDocs, _a, verifyMap, comparedMonths, paidCloserCount, grandCloserCount, sumPaidDiffPct, sumGrandDiffPct, maxPaidDiffPct, maxGrandDiffPct, sumPaidGrandGapPct, highVarianceMonths, checks, warnings, avgPaidDiffPct, avgGrandDiffPct, avgPaidGrandGapPct, baselineMetric, rollingWindow, normalizedMetric, bounds, fullMonthMatch, fullMonthDocs, fullMonthMap, fullMonthDiff, _b, alternateDateField, alternateDateCondition, bounds, alternateMatch, alternateDocs, alternateMap, alternateDiff, _c;
3031
+ var _d, _e;
3032
+ var _f;
3033
+ return __generator(this, function (_g) {
3034
+ switch (_g.label) {
3035
+ case 0:
3036
+ collection = normalizeOptionalString(params.collection).toLowerCase();
3037
+ collectionBase = collection.startsWith('report-') ? collection.slice('report-'.length) : collection;
3038
+ if (collectionBase !== 'invoices') {
3039
+ return [2 /*return*/, null];
3040
+ }
3041
+ monthKey = resolveAssistantMonthlyKey(params.documents);
3042
+ if (!monthKey) {
3043
+ return [2 /*return*/, null];
3044
+ }
3045
+ amountKey = resolveAssistantNumericAmountKey(params.documents, monthKey);
3046
+ if (!amountKey) {
3047
+ return [2 /*return*/, null];
3048
+ }
3049
+ baseMap = mapAssistantRevenueByMonth(params.documents, monthKey, amountKey);
3050
+ if (!baseMap.size) {
3051
+ return [2 /*return*/, null];
3052
+ }
3053
+ groupIndex = findAggregateGroupIndex(params.pipeline || []);
3054
+ if (groupIndex === -1) {
3055
+ return [2 /*return*/, null];
3056
+ }
3057
+ groupPaths = extractGroupFieldPaths(((_f = params.pipeline[groupIndex]) === null || _f === void 0 ? void 0 : _f.$group) || {});
3058
+ dateField = groupPaths.find(function (field) { return /(date|paid|invoice|created|updated|_at)$/i.test(field); })
3059
+ || groupPaths.find(function (field) { return /(date|paid|invoice|created|updated)/i.test(field); })
3060
+ || 'date_paid';
3061
+ mergedMatch = mergeAssistantPreGroupMatchStages(params.pipeline || [], groupIndex);
3062
+ dateFieldsInMatch = collectMatchFieldsByCondition(mergedMatch, function (_field, condition) { return isDateCondition(condition); });
3063
+ baseMatchNoDate = dateFieldsInMatch.length ? stripMatchFields(mergedMatch, dateFieldsInMatch) : mergedMatch;
3064
+ primaryDateCondition = findMatchConditionForField(mergedMatch, dateField);
3065
+ verifyPipeline = buildAssistantMonthlyRevenueVerificationPipeline(mergedMatch, dateField);
3066
+ verifyDocs = [];
3067
+ _g.label = 1;
3068
+ case 1:
3069
+ _g.trys.push([1, 3, , 4]);
3070
+ return [4 /*yield*/, params.db.collection(params.collection)
3071
+ .aggregate(verifyPipeline, params.aggregateOptions || {})
3072
+ .toArray()];
3073
+ case 2:
3074
+ verifyDocs = _g.sent();
3075
+ return [3 /*break*/, 4];
3076
+ case 3:
3077
+ _a = _g.sent();
3078
+ return [2 /*return*/, {
3079
+ type: 'invoice_revenue_monthly',
3080
+ checks: [
3081
+ { name: 'Cross-check query', status: 'warn', details: 'Verification query failed; result not cross-checked.' }
3082
+ ],
3083
+ warnings: ['Verification query failed, so this total was not independently cross-checked.'],
3084
+ metrics: {
3085
+ baselineField: amountKey,
3086
+ dateField: dateField
3087
+ }
3088
+ }];
3089
+ case 4:
3090
+ verifyMap = mapAssistantVerificationDocsByMonth(verifyDocs);
3091
+ comparedMonths = 0;
3092
+ paidCloserCount = 0;
3093
+ grandCloserCount = 0;
3094
+ sumPaidDiffPct = 0;
3095
+ sumGrandDiffPct = 0;
3096
+ maxPaidDiffPct = 0;
3097
+ maxGrandDiffPct = 0;
3098
+ sumPaidGrandGapPct = 0;
3099
+ highVarianceMonths = [];
3100
+ baseMap.forEach(function (value, month) {
3101
+ var match = verifyMap.get(month);
3102
+ if (!match) {
3103
+ return;
3104
+ }
3105
+ comparedMonths += 1;
3106
+ var paidDiff = Math.abs(value - match.paid);
3107
+ var grandDiff = Math.abs(value - match.grand);
3108
+ var denom = Math.max(Math.abs(value), 1);
3109
+ var paidPct = paidDiff / denom;
3110
+ var grandPct = grandDiff / denom;
3111
+ sumPaidDiffPct += paidPct;
3112
+ sumGrandDiffPct += grandPct;
3113
+ maxPaidDiffPct = Math.max(maxPaidDiffPct, paidPct);
3114
+ maxGrandDiffPct = Math.max(maxGrandDiffPct, grandPct);
3115
+ if (paidDiff < grandDiff) {
3116
+ paidCloserCount += 1;
3117
+ }
3118
+ else if (grandDiff < paidDiff) {
3119
+ grandCloserCount += 1;
3120
+ }
3121
+ var gapPct = Math.abs(match.paid - match.grand) / Math.max(Math.abs(match.grand), 1);
3122
+ sumPaidGrandGapPct += gapPct;
3123
+ if (Math.min(paidPct, grandPct) >= AI_ASSISTANT_REVENUE_VERIFY_DIFF_WARN_THRESHOLD) {
3124
+ highVarianceMonths.push(month);
3125
+ }
3126
+ });
3127
+ checks = [];
3128
+ warnings = [];
3129
+ avgPaidDiffPct = comparedMonths ? (sumPaidDiffPct / comparedMonths) : 0;
3130
+ avgGrandDiffPct = comparedMonths ? (sumGrandDiffPct / comparedMonths) : 0;
3131
+ avgPaidGrandGapPct = comparedMonths ? (sumPaidGrandGapPct / comparedMonths) : 0;
3132
+ baselineMetric = 'unknown';
3133
+ if (comparedMonths) {
3134
+ if (paidCloserCount > grandCloserCount) {
3135
+ baselineMetric = 'paid_total (date_paid)';
3136
+ }
3137
+ else if (grandCloserCount > paidCloserCount) {
3138
+ baselineMetric = 'grand_total (date_paid)';
3139
+ }
3140
+ else {
3141
+ baselineMetric = 'mixed';
3142
+ }
3143
+ }
3144
+ if (!comparedMonths) {
3145
+ checks.push({
3146
+ name: 'Cross-check coverage',
3147
+ status: 'warn',
3148
+ details: 'No overlapping month rows were available for verification.'
3149
+ });
3150
+ warnings.push('Could not cross-check this result against alternate invoice total fields for the same months.');
3151
+ }
3152
+ else {
3153
+ checks.push({
3154
+ name: 'Metric alignment',
3155
+ status: baselineMetric === 'mixed' ? 'warn' : 'pass',
3156
+ details: "Result aligns most with ".concat(baselineMetric, ".")
3157
+ });
3158
+ if (baselineMetric === 'mixed') {
3159
+ warnings.push('Revenue appears to mix definitions across months (paid_total vs grand_total).');
3160
+ }
3161
+ checks.push({
3162
+ name: 'Field variance',
3163
+ status: avgPaidGrandGapPct >= AI_ASSISTANT_REVENUE_VERIFY_PAID_GRAND_GAP_WARN_THRESHOLD ? 'warn' : 'pass',
3164
+ details: "Average paid-vs-grand gap: ".concat((0, common_1.round)(avgPaidGrandGapPct * 100, 2), "%.")
3165
+ });
3166
+ if (avgPaidGrandGapPct >= AI_ASSISTANT_REVENUE_VERIFY_PAID_GRAND_GAP_WARN_THRESHOLD) {
3167
+ warnings.push("paid_total and grand_total differ materially for this period (".concat((0, common_1.round)(avgPaidGrandGapPct * 100, 2), "% average gap)."));
3168
+ }
3169
+ }
3170
+ rollingWindow = detectAssistantRollingMonthWindow(params.pipeline || []);
3171
+ if ((rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.months) && (rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.upperNow)) {
3172
+ checks.push({
3173
+ name: 'Date window shape',
3174
+ status: 'warn',
3175
+ details: "Query uses rolling $$NOW bounds (last ".concat(rollingWindow.months, " months), which can include partial months.")
3176
+ });
3177
+ warnings.push("Date window is rolling (last ".concat(rollingWindow.months, " months to now), so first/current months may be partial."));
3178
+ }
3179
+ else {
3180
+ checks.push({
3181
+ name: 'Date window shape',
3182
+ status: 'pass',
3183
+ details: 'No rolling $$NOW month window pattern detected.'
3184
+ });
3185
+ }
3186
+ normalizedMetric = normalizeAssistantVerificationMetric(baselineMetric);
3187
+ if (!((rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.months) && (rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.upperNow))) return [3 /*break*/, 8];
3188
+ bounds = resolveAssistantRollingWindowBounds(rollingWindow.months);
3189
+ fullMonthMatch = mergeAssistantMatch(baseMatchNoDate, (_d = {},
3190
+ _d[dateField] = {
3191
+ $gte: bounds.startOfWindow,
3192
+ $lt: bounds.startOfCurrentMonth
3193
+ },
3194
+ _d));
3195
+ _g.label = 5;
3196
+ case 5:
3197
+ _g.trys.push([5, 7, , 8]);
3198
+ return [4 /*yield*/, params.db.collection(params.collection)
3199
+ .aggregate(buildAssistantMonthlyRevenueVerificationPipeline(fullMonthMatch, dateField), params.aggregateOptions || {})
3200
+ .toArray()];
3201
+ case 6:
3202
+ fullMonthDocs = _g.sent();
3203
+ fullMonthMap = mapAssistantMetricByMonth(mapAssistantVerificationDocsByMonth(fullMonthDocs), normalizedMetric);
3204
+ fullMonthDiff = compareAssistantMonthlyValueMaps(baseMap, fullMonthMap);
3205
+ checks.push({
3206
+ name: 'Full-month cross-check',
3207
+ status: fullMonthDiff.comparedMonths && fullMonthDiff.avgDiffPct < AI_ASSISTANT_REVENUE_VERIFY_DIFF_WARN_THRESHOLD ? 'pass' : 'warn',
3208
+ details: fullMonthDiff.comparedMonths
3209
+ ? "Compared ".concat(fullMonthDiff.comparedMonths, " month(s); avg diff ").concat((0, common_1.round)(fullMonthDiff.avgDiffPct * 100, 2), "%.")
3210
+ : 'No overlapping months for full-month cross-check.'
3211
+ });
3212
+ if (fullMonthDiff.comparedMonths && fullMonthDiff.avgDiffPct >= AI_ASSISTANT_REVENUE_VERIFY_DIFF_WARN_THRESHOLD) {
3213
+ warnings.push("Rolling-window totals differ from full-month totals by ".concat((0, common_1.round)(fullMonthDiff.avgDiffPct * 100, 2), "% on average."));
3214
+ }
3215
+ if (!fullMonthDiff.comparedMonths) {
3216
+ warnings.push('Could not compare rolling-window results against full-month buckets.');
3217
+ }
3218
+ return [3 /*break*/, 8];
3219
+ case 7:
3220
+ _b = _g.sent();
3221
+ checks.push({
3222
+ name: 'Full-month cross-check',
3223
+ status: 'warn',
3224
+ details: 'Full-month verification query failed.'
3225
+ });
3226
+ warnings.push('Full-month verification query failed.');
3227
+ return [3 /*break*/, 8];
3228
+ case 8:
3229
+ alternateDateField = dateField === 'date_paid'
3230
+ ? 'date_invoice'
3231
+ : dateField === 'date_invoice'
3232
+ ? 'date_paid'
3233
+ : '';
3234
+ if (!alternateDateField) return [3 /*break*/, 12];
3235
+ alternateDateCondition = null;
3236
+ if ((rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.months) && (rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.upperNow)) {
3237
+ bounds = resolveAssistantRollingWindowBounds(rollingWindow.months);
3238
+ alternateDateCondition = {
3239
+ $gte: bounds.rollingStart,
3240
+ $lte: bounds.now
3241
+ };
3242
+ }
3243
+ else if (primaryDateCondition && typeof primaryDateCondition === 'object') {
3244
+ alternateDateCondition = primaryDateCondition;
3245
+ }
3246
+ if (!alternateDateCondition) return [3 /*break*/, 12];
3247
+ alternateMatch = mergeAssistantMatch(baseMatchNoDate, (_e = {}, _e[alternateDateField] = alternateDateCondition, _e));
3248
+ _g.label = 9;
3249
+ case 9:
3250
+ _g.trys.push([9, 11, , 12]);
3251
+ return [4 /*yield*/, params.db.collection(params.collection)
3252
+ .aggregate(buildAssistantMonthlyRevenueVerificationPipeline(alternateMatch, alternateDateField), params.aggregateOptions || {})
3253
+ .toArray()];
3254
+ case 10:
3255
+ alternateDocs = _g.sent();
3256
+ alternateMap = mapAssistantMetricByMonth(mapAssistantVerificationDocsByMonth(alternateDocs), normalizedMetric);
3257
+ alternateDiff = compareAssistantMonthlyValueMaps(baseMap, alternateMap);
3258
+ checks.push({
3259
+ name: "Alternate date field (".concat(alternateDateField, ")"),
3260
+ status: alternateDiff.comparedMonths && alternateDiff.avgDiffPct < AI_ASSISTANT_REVENUE_VERIFY_DIFF_WARN_THRESHOLD ? 'pass' : 'warn',
3261
+ details: alternateDiff.comparedMonths
3262
+ ? "Compared ".concat(alternateDiff.comparedMonths, " month(s); avg diff ").concat((0, common_1.round)(alternateDiff.avgDiffPct * 100, 2), "%.")
3263
+ : 'No overlapping months for alternate date-field cross-check.'
3264
+ });
3265
+ if (alternateDiff.comparedMonths && alternateDiff.avgDiffPct >= AI_ASSISTANT_REVENUE_VERIFY_DIFF_WARN_THRESHOLD) {
3266
+ warnings.push("Switching from ".concat(dateField, " to ").concat(alternateDateField, " changes monthly totals by ").concat((0, common_1.round)(alternateDiff.avgDiffPct * 100, 2), "% on average."));
3267
+ }
3268
+ return [3 /*break*/, 12];
3269
+ case 11:
3270
+ _c = _g.sent();
3271
+ checks.push({
3272
+ name: "Alternate date field (".concat(alternateDateField, ")"),
3273
+ status: 'warn',
3274
+ details: 'Alternate date-field verification query failed.'
3275
+ });
3276
+ return [3 /*break*/, 12];
3277
+ case 12:
3278
+ if (highVarianceMonths.length) {
3279
+ warnings.push("High variance months vs both paid/grand checks: ".concat(highVarianceMonths.join(', '), "."));
3280
+ }
3281
+ return [2 /*return*/, {
3282
+ type: 'invoice_revenue_monthly',
3283
+ checks: checks,
3284
+ warnings: warnings,
3285
+ metrics: {
3286
+ baselineField: amountKey,
3287
+ dateField: dateField,
3288
+ comparedMonths: comparedMonths,
3289
+ avgDiffToPaidPct: (0, common_1.round)(avgPaidDiffPct * 100, 2),
3290
+ avgDiffToGrandPct: (0, common_1.round)(avgGrandDiffPct * 100, 2),
3291
+ maxDiffToPaidPct: (0, common_1.round)(maxPaidDiffPct * 100, 2),
3292
+ maxDiffToGrandPct: (0, common_1.round)(maxGrandDiffPct * 100, 2),
3293
+ avgPaidGrandGapPct: (0, common_1.round)(avgPaidGrandGapPct * 100, 2)
3294
+ }
3295
+ }];
3296
+ }
3297
+ });
3298
+ });
3299
+ }
3300
+ function resolveAssistantMonthlyKey(documents) {
3301
+ var first = Array.isArray(documents) ? documents.find(function (doc) { return doc && typeof doc === 'object'; }) : null;
3302
+ if (!first) {
3303
+ return '';
3304
+ }
3305
+ return Object.keys(first).find(function (key) {
3306
+ var value = first[key];
3307
+ return typeof value === 'string' && /^\d{4}-\d{2}$/.test(value);
3308
+ }) || '';
3309
+ }
3310
+ function resolveAssistantNumericAmountKey(documents, monthKey) {
3311
+ var first = Array.isArray(documents) ? documents.find(function (doc) { return doc && typeof doc === 'object'; }) : null;
3312
+ if (!first) {
3313
+ return '';
3314
+ }
3315
+ var currencyLike = Object.keys(first).filter(function (key) {
3316
+ if (key === monthKey) {
3317
+ return false;
3318
+ }
3319
+ var value = Number(first[key]);
3320
+ if (!Number.isFinite(value)) {
3321
+ return false;
3322
+ }
3323
+ return /(revenue|amount|total|sales|billing|paid|grand)/i.test(key);
3324
+ });
3325
+ if (currencyLike.length) {
3326
+ return currencyLike[0];
3327
+ }
3328
+ return Object.keys(first).find(function (key) {
3329
+ if (key === monthKey) {
3330
+ return false;
3331
+ }
3332
+ return Number.isFinite(Number(first[key]));
3333
+ }) || '';
3334
+ }
3335
+ function mapAssistantRevenueByMonth(documents, monthKey, valueKey) {
3336
+ var mapped = new Map();
3337
+ (Array.isArray(documents) ? documents : []).forEach(function (doc) {
3338
+ var month = normalizeOptionalString(doc === null || doc === void 0 ? void 0 : doc[monthKey]);
3339
+ if (!month || !/^\d{4}-\d{2}$/.test(month)) {
3340
+ return;
3341
+ }
3342
+ var value = Number(doc === null || doc === void 0 ? void 0 : doc[valueKey]);
3343
+ if (!Number.isFinite(value)) {
3344
+ return;
3345
+ }
3346
+ mapped.set(month, value);
3347
+ });
3348
+ return mapped;
3349
+ }
3350
+ function mergeAssistantPreGroupMatchStages(pipeline, groupIndex) {
3351
+ var matches = [];
3352
+ for (var i = 0; i < groupIndex; i += 1) {
3353
+ var stage = pipeline[i];
3354
+ if (stage && typeof stage === 'object' && stage.$match && typeof stage.$match === 'object') {
3355
+ matches.push(stage.$match);
3356
+ }
3357
+ }
3358
+ if (!matches.length) {
3359
+ return {};
3360
+ }
3361
+ if (matches.length === 1) {
3362
+ return matches[0];
3363
+ }
3364
+ return { $and: matches };
3365
+ }
3366
+ function detectAssistantRollingMonthWindow(pipeline) {
3367
+ var raw = JSON.stringify(Array.isArray(pipeline) ? pipeline : []);
3368
+ if (!raw) {
3369
+ return null;
3370
+ }
3371
+ var amountMatch = raw.match(/"\$dateSubtract":\{"startDate":"\$\$NOW","unit":"month","amount":([0-9]+)\}/);
3372
+ if (!amountMatch) {
3373
+ return null;
3374
+ }
3375
+ var months = Number(amountMatch[1]);
3376
+ if (!Number.isFinite(months) || months < 1) {
3377
+ return null;
3378
+ }
3379
+ var nowCount = (raw.match(/\$\$NOW/g) || []).length;
3380
+ return {
3381
+ months: months,
3382
+ upperNow: nowCount >= 2
3383
+ };
3384
+ }
3385
+ function buildAssistantMonthlyRevenueVerificationPipeline(match, dateField) {
3386
+ var pipeline = [];
3387
+ if (match && typeof match === 'object' && Object.keys(match).length) {
3388
+ pipeline.push({ $match: match });
3389
+ }
3390
+ pipeline.push({
3391
+ $group: {
3392
+ _id: {
3393
+ $dateToString: {
3394
+ format: '%Y-%m',
3395
+ date: "$".concat(dateField)
3396
+ }
3397
+ },
3398
+ paid_total_sum: { $sum: { $ifNull: ['$paid_total', 0] } },
3399
+ grand_total_sum: { $sum: { $ifNull: ['$grand_total', 0] } },
3400
+ invoice_count: { $sum: 1 }
3401
+ }
3402
+ }, {
3403
+ $project: {
3404
+ _id: 0,
3405
+ month: '$_id',
3406
+ paid_total_sum: 1,
3407
+ grand_total_sum: 1,
3408
+ invoice_count: 1
3409
+ }
3410
+ }, { $sort: { month: 1 } }, { $limit: 36 });
3411
+ return pipeline;
3412
+ }
3413
+ function mapAssistantVerificationDocsByMonth(docs) {
3414
+ var mapped = new Map();
3415
+ (Array.isArray(docs) ? docs : []).forEach(function (row) {
3416
+ var month = normalizeOptionalString(row === null || row === void 0 ? void 0 : row.month);
3417
+ if (!month || !/^\d{4}-\d{2}$/.test(month)) {
3418
+ return;
3419
+ }
3420
+ var paid = Number(row === null || row === void 0 ? void 0 : row.paid_total_sum);
3421
+ var grand = Number(row === null || row === void 0 ? void 0 : row.grand_total_sum);
3422
+ var count = Number(row === null || row === void 0 ? void 0 : row.invoice_count);
3423
+ mapped.set(month, {
3424
+ paid: Number.isFinite(paid) ? paid : 0,
3425
+ grand: Number.isFinite(grand) ? grand : 0,
3426
+ count: Number.isFinite(count) ? count : 0
3427
+ });
3428
+ });
3429
+ return mapped;
3430
+ }
3431
+ function normalizeAssistantVerificationMetric(value) {
3432
+ var normalized = normalizeOptionalString(value).toLowerCase();
3433
+ if (normalized.startsWith('paid_total')) {
3434
+ return 'paid';
3435
+ }
3436
+ if (normalized.startsWith('grand_total')) {
3437
+ return 'grand';
3438
+ }
3439
+ return 'mixed';
3440
+ }
3441
+ function mapAssistantMetricByMonth(source, metric) {
3442
+ var mapped = new Map();
3443
+ source.forEach(function (value, month) {
3444
+ var next = metric === 'paid'
3445
+ ? value.paid
3446
+ : metric === 'grand'
3447
+ ? value.grand
3448
+ : value.paid || value.grand;
3449
+ mapped.set(month, Number.isFinite(next) ? next : 0);
3450
+ });
3451
+ return mapped;
3452
+ }
3453
+ function compareAssistantMonthlyValueMaps(base, compare) {
3454
+ var comparedMonths = 0;
3455
+ var sumDiffPct = 0;
3456
+ var maxDiffPct = 0;
3457
+ base.forEach(function (baseValue, month) {
3458
+ var compareValue = compare.get(month);
3459
+ if (typeof compareValue !== 'number') {
3460
+ return;
3461
+ }
3462
+ var diffPct = Math.abs(baseValue - compareValue) / Math.max(Math.abs(baseValue), 1);
3463
+ comparedMonths += 1;
3464
+ sumDiffPct += diffPct;
3465
+ maxDiffPct = Math.max(maxDiffPct, diffPct);
3466
+ });
3467
+ return {
3468
+ comparedMonths: comparedMonths,
3469
+ avgDiffPct: comparedMonths ? (sumDiffPct / comparedMonths) : 0,
3470
+ maxDiffPct: maxDiffPct
3471
+ };
3472
+ }
3473
+ function mergeAssistantMatch(base, extra) {
3474
+ var baseObj = base && typeof base === 'object' ? base : {};
3475
+ var extraObj = extra && typeof extra === 'object' ? extra : {};
3476
+ if (!Object.keys(baseObj).length) {
3477
+ return extraObj;
3478
+ }
3479
+ if (!Object.keys(extraObj).length) {
3480
+ return baseObj;
3481
+ }
3482
+ return { $and: [baseObj, extraObj] };
3483
+ }
3484
+ function resolveAssistantRollingWindowBounds(months) {
3485
+ var now = new Date();
3486
+ var rollingStart = new Date(now);
3487
+ rollingStart.setUTCMonth(rollingStart.getUTCMonth() - months);
3488
+ var startOfCurrentMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
3489
+ var startOfWindow = new Date(Date.UTC(startOfCurrentMonth.getUTCFullYear(), startOfCurrentMonth.getUTCMonth() - months, 1));
3490
+ return {
3491
+ now: now,
3492
+ rollingStart: rollingStart,
3493
+ startOfCurrentMonth: startOfCurrentMonth,
3494
+ startOfWindow: startOfWindow
3495
+ };
3496
+ }
3028
3497
  function extractAssistantMongoDirective(content) {
3029
3498
  var lines = String(content || '').split('\n');
3030
3499
  var directiveIndex = -1;
@@ -3121,6 +3590,9 @@ function buildAssistantToolResultPayload(directive, toolResponse) {
3121
3590
  || normalizeOptionalString((_c = toolResponse === null || toolResponse === void 0 ? void 0 : toolResponse.debug) === null || _c === void 0 ? void 0 : _c.collection)
3122
3591
  || requestedCollection
3123
3592
  || '';
3593
+ var verification = (toolResponse === null || toolResponse === void 0 ? void 0 : toolResponse.verification) && typeof toolResponse.verification === 'object'
3594
+ ? toolResponse.verification
3595
+ : undefined;
3124
3596
  var result = {
3125
3597
  type: directive.type === 'aggregate' ? 'mongo_agg' : 'mongo_read',
3126
3598
  input: directivePayload,
@@ -3131,6 +3603,7 @@ function buildAssistantToolResultPayload(directive, toolResponse) {
3131
3603
  rowCount: rowCount,
3132
3604
  columns: trimmedDisplay.columns,
3133
3605
  truncated: trimmedDisplay.truncated,
3606
+ verification: verification,
3134
3607
  debug: (toolResponse === null || toolResponse === void 0 ? void 0 : toolResponse.debug) && typeof toolResponse.debug === 'object' ? toolResponse.debug : undefined
3135
3608
  }
3136
3609
  };
@@ -3160,6 +3633,26 @@ function buildAssistantToolResultPrompt(result) {
3160
3633
  else {
3161
3634
  lines.push('Preview: (no rows)');
3162
3635
  }
3636
+ var verification = result.output.verification;
3637
+ if (verification && typeof verification === 'object') {
3638
+ var checks = Array.isArray(verification.checks) ? verification.checks : [];
3639
+ var warnings = Array.isArray(verification.warnings) ? verification.warnings : [];
3640
+ if (checks.length || warnings.length) {
3641
+ lines.push('Verification:');
3642
+ }
3643
+ checks.slice(0, 4).forEach(function (entry) {
3644
+ var name = normalizeOptionalString(entry === null || entry === void 0 ? void 0 : entry.name) || 'check';
3645
+ var status = normalizeOptionalString(entry === null || entry === void 0 ? void 0 : entry.status).toLowerCase() === 'warn' ? 'warn' : 'pass';
3646
+ var details = normalizeOptionalString(entry === null || entry === void 0 ? void 0 : entry.details);
3647
+ lines.push("- ".concat(name, ": ").concat(status).concat(details ? " (".concat(details, ")") : ''));
3648
+ });
3649
+ warnings.slice(0, 4).forEach(function (warning) {
3650
+ var text = normalizeOptionalString(warning);
3651
+ if (text) {
3652
+ lines.push("- warning: ".concat(text));
3653
+ }
3654
+ });
3655
+ }
3163
3656
  return lines.join('\n');
3164
3657
  }
3165
3658
  function buildAssistantCodexToolFollowupPrompt(message, attachmentText, historyText, contextText, toolResultText) {
@@ -3168,7 +3661,7 @@ function buildAssistantCodexToolFollowupPrompt(message, attachmentText, historyT
3168
3661
  var trimmedHistory = normalizeOptionalString(historyText);
3169
3662
  var historyBlock = trimmedHistory ? "\n\nConversation so far:\n".concat(trimmedHistory) : '';
3170
3663
  var toolBlock = toolResultText ? "\n\nTool Result:\n".concat(toolResultText) : '';
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.';
3664
+ 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. If the Tool Result includes Verification warnings, explicitly include them and call out the metric/date basis used.';
3172
3665
  return "System:\n".concat(AI_ASSISTANT_SYSTEM_PROMPT).concat(contextBlock).concat(historyBlock, "\n\nUser:\n").concat(message).concat(attachmentText || '').concat(toolBlock).concat(instruction).trim();
3173
3666
  }
3174
3667
  function buildAssistantToolFallbackResponse(result) {
@@ -3187,6 +3680,27 @@ function buildAssistantToolFallbackResponse(result) {
3187
3680
  }
3188
3681
  return lines.join('\n').trim();
3189
3682
  }
3683
+ function applyAssistantVerificationNotes(value, toolResult) {
3684
+ var _a, _b, _c, _d;
3685
+ var content = normalizeOptionalString(value);
3686
+ var warnings = Array.isArray((_b = (_a = toolResult === null || toolResult === void 0 ? void 0 : toolResult.output) === null || _a === void 0 ? void 0 : _a.verification) === null || _b === void 0 ? void 0 : _b.warnings)
3687
+ ? (_d = (_c = toolResult === null || toolResult === void 0 ? void 0 : toolResult.output) === null || _c === void 0 ? void 0 : _c.verification) === null || _d === void 0 ? void 0 : _d.warnings.map(function (entry) { return normalizeOptionalString(entry); }).filter(Boolean)
3688
+ : [];
3689
+ if (!warnings.length) {
3690
+ return content || value || '';
3691
+ }
3692
+ var normalizedLower = String(content || '').toLowerCase();
3693
+ var missing = warnings.filter(function (warning) { return !normalizedLower.includes(String(warning).toLowerCase()); });
3694
+ if (!missing.length) {
3695
+ return content || value || '';
3696
+ }
3697
+ var noteLines = ['Verification notes:'];
3698
+ missing.slice(0, 4).forEach(function (warning) { return noteLines.push("- ".concat(warning)); });
3699
+ if (!content) {
3700
+ return noteLines.join('\n');
3701
+ }
3702
+ return "".concat(content, "\n\n").concat(noteLines.join('\n')).trim();
3703
+ }
3190
3704
  function buildAssistantDebugPayload(params) {
3191
3705
  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;
3192
3706
  var notes = [];
@@ -4876,6 +5390,7 @@ function getAssistantCurrencyFormatter() {
4876
5390
  assistantCurrencyFormatter = new Intl.NumberFormat(AI_ASSISTANT_LOCALE, {
4877
5391
  style: 'currency',
4878
5392
  currency: AI_ASSISTANT_CURRENCY_CODE,
5393
+ currencyDisplay: 'narrowSymbol',
4879
5394
  minimumFractionDigits: 2,
4880
5395
  maximumFractionDigits: 2
4881
5396
  });
@@ -4963,6 +5478,12 @@ function formatAssistantDisplayCell(value, column) {
4963
5478
  if (Array.isArray(value)) {
4964
5479
  return value.map(function (item) { return formatAssistantDisplayCell(item, column); }).filter(Boolean).join(', ');
4965
5480
  }
5481
+ if (typeof value === 'string') {
5482
+ var normalizedCurrency = normalizeAssistantCurrencyText(value);
5483
+ if (normalizedCurrency !== value) {
5484
+ return normalizedCurrency;
5485
+ }
5486
+ }
4966
5487
  var columnKey = String(column || '').toLowerCase();
4967
5488
  if (isAssistantDateColumn(columnKey) && isAssistantLikelyDateValue(value)) {
4968
5489
  return formatAssistantDateValue(value);
@@ -4998,6 +5519,13 @@ function formatAssistantDisplayCell(value, column) {
4998
5519
  }
4999
5520
  return String(value);
5000
5521
  }
5522
+ function normalizeAssistantCurrencyText(value) {
5523
+ var raw = String(value || '');
5524
+ if (!raw) {
5525
+ return '';
5526
+ }
5527
+ return raw.replace(AI_ASSISTANT_USD_CURRENCY_TEXT_PATTERN, function (_match, amount) { return "$".concat(amount); });
5528
+ }
5001
5529
  function formatDisplayTableMarkdown(display) {
5002
5530
  if (!display || !Array.isArray(display.columns) || !display.columns.length) {
5003
5531
  return '';
@@ -5019,7 +5547,7 @@ function stripAssistantMarkdownTables(value) {
5019
5547
  if (!raw) {
5020
5548
  return '';
5021
5549
  }
5022
- var tablePattern = /(^|\n)\|[^\n]*\|\n\|[ \t:-|]+\|\n(?:\|[^\n]*\|\n?)*/g;
5550
+ var tablePattern = /(^|\n)\s*\|[^\n]*\|\s*\n\s*\|[ \t:-|]+\|\s*\n(?:\s*\|[^\n]*\|\s*\n?)*/g;
5023
5551
  var cleaned = raw.replace(tablePattern, '\n').trim();
5024
5552
  return cleaned;
5025
5553
  }
@@ -9180,13 +9708,7 @@ function sanitizeAssistantResponse(value) {
9180
9708
  if (!cleaned) {
9181
9709
  return 'I can’t share code, but I can point you to files or explain behavior at a high level.';
9182
9710
  }
9183
- var normalizedCurrency = cleaned
9184
- .replace(/\bUSD(?:\s|&nbsp;|&#160;|[\u00A0\u202F\u2007])*\$?\s*([-+]?[0-9][0-9,]*(?:\.[0-9]+)?)/g, function (_match, amount) {
9185
- return "$".concat(amount);
9186
- })
9187
- .replace(/\bUS\$\s*([-+]?[0-9][0-9,]*(?:\.[0-9]+)?)/g, function (_match, amount) {
9188
- return "$".concat(amount);
9189
- });
9711
+ var normalizedCurrency = normalizeAssistantCurrencyText(cleaned);
9190
9712
  return normalizeAssistantRoutes(normalizedCurrency);
9191
9713
  }
9192
9714
  function evaluateAssistantGuardrails(message) {