@resolveio/server-lib 22.0.10 → 22.0.12

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.
@@ -139,7 +139,8 @@ var DEFAULT_MAX_FILE_MB = 50;
139
139
  var DEFAULT_MAX_TOTAL_MB = 100;
140
140
  var DEFAULT_MAX_ATTACHMENT_CHARS = 12000;
141
141
  var DEFAULT_MAX_TOTAL_ATTACHMENT_CHARS = 40000;
142
- var DEFAULT_CODEX_MODEL = 'gpt-5.1-codex-mini';
142
+ var DEFAULT_CODEX_MODEL = 'gpt-5.3-codex';
143
+ var DEFAULT_CODEX_FALLBACK_MODEL = 'gpt-5.2-codex';
143
144
  var DEFAULT_CODEX_TIMEOUT_MS = 180000;
144
145
  var AI_ASSISTANT_MONGO_DEFAULT_LIMIT = 20;
145
146
  var AI_ASSISTANT_MONGO_MAX_LIMIT = 200;
@@ -381,12 +382,13 @@ var AI_ASSISTANT_REPORT_BUILDER_EXPERT_PLAYBOOK = [
381
382
  '',
382
383
  '2) Resolve the target dataset safely.',
383
384
  '- Map user wording to internal collection names using routes, collection hints, field hints, and synonym expansion.',
384
- '- Prefer report-* collections when permissionView is under /report.',
385
+ '- Prefer report-* collections when permissionView is under /report-builder.',
385
386
  '- Never use *.versions unless user explicitly requests bug-history/version investigation.',
386
387
  '- Never invent collection names or fields.',
387
388
  '',
388
389
  '3) Enforce permissions and scope in the directive.',
389
390
  '- Always include permissionView.',
391
+ '- Use /report-builder as the default permissionView for data directives; avoid /report/* routes.',
390
392
  '- Assume non-super-admin unless explicitly told otherwise.',
391
393
  '- Invoice-like collections require invoice view access.',
392
394
  '- Customer portal users must stay in their own customer scope.',
@@ -435,7 +437,7 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
435
437
  '- Step 1 (always): determine the target collections/models/modules/workflows using context, routes, and collection hints. Assume the user is non-technical and will not provide internal names.',
436
438
  '- Never use *.versions collections for normal requests. Only use a .versions collection when explicitly investigating a bug by checking the last ~5 updates.',
437
439
  '- Planning stage: regex/keyword scan the codebase for collectionName/model definitions, methods, publications, and Angular routes/modules to map user wording to internal names.',
438
- '- If permissionView starts with /report/, prefer the report-* collection when both report and base collections exist.',
440
+ '- If permissionView starts with /report-builder, prefer the report-* collection when both report and base collections exist.',
439
441
  '- Map user wording to internal collections/fields yourself. Do not ask for property names unless required to run a query.',
440
442
  '- Use term hints from context (synonym expansions) when mapping user language to collections.',
441
443
  '- 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.',
@@ -490,7 +492,8 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
490
492
  '- REPORT_BUILDER_READ: {"collection":"<name>","query":{...},"options":{"projection":{...},"sort":{...},"limit":20},"permissionView":"</route>"}',
491
493
  '- If you need grouped/aggregated data (totals by user, rankings, trends), end your response with a single line exactly in this format:',
492
494
  '- REPORT_BUILDER_AGG: {"collection":"<name>","pipeline":[...],"options":{"allowDiskUse":true,"limit":20},"permissionView":"</route>"}',
493
- '- For invoice data, set permissionView to an invoice route (ex: /invoice/list or /report/invoice).',
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.',
494
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.',
495
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.',
496
499
  '- For relative date ranges (last/past/recent), include an upper bound <= $$NOW unless the user specifies a future end date.',
@@ -532,7 +535,7 @@ var AI_ASSISTANT_PLANNER_SYSTEM_PROMPT = [
532
535
  ' - Never propose querying blocked/sensitive collections if the user lacks permission.',
533
536
  '',
534
537
  '4) PERMISSION MATCHING:',
535
- ' - Do NOT hardcode invoice access to "/report/invoice".',
538
+ ' - Do NOT hardcode invoice access to "/report-builder/*".',
536
539
  ' - For invoice-related data or navigation, permission is satisfied if ANY user view contains "invoice" case-insensitive.',
537
540
  ' (General rule: for an entity token X, permission is satisfied if any view contains X case-insensitive.)',
538
541
  ' - If permission checks are ambiguous, choose the safest restriction and explain.',
@@ -667,7 +670,7 @@ var AI_FORM_PATCH_SYSTEM_PROMPT = [
667
670
  '- Use ISO 8601 for dates and true/false for booleans.',
668
671
  '- Use medium reasoning effort.'
669
672
  ].join('\n');
670
- var assistantCodexClient = null;
673
+ var assistantCodexClientByConfig = new Map();
671
674
  var assistantCodexRunQueue = [];
672
675
  var assistantCodexRunDraining = false;
673
676
  /* eslint-enable no-unused-vars */
@@ -1320,7 +1323,7 @@ function executeAiFormPatch(payload, context) {
1320
1323
  }
1321
1324
  function executeAiAssistantCodexRun(payload, context) {
1322
1325
  return __awaiter(this, void 0, void 0, function () {
1323
- var input, message, aiWorkerDebug, requestId, guardrail, conversation_2, now_2, userMsg, assistantMsg, user, isSuperAdmin, hasInvoiceAccess, customerId, conversation, now, attachments, attachmentData, historyLimit, history, _a, historyLines, userDoc, initialProgress, assistantDoc, insertResult, assistantMessageId;
1326
+ var input, message, aiWorkerDebug, requestId, codexModel, codexFallbackModels, guardrail, conversation_2, now_2, userMsg, assistantMsg, user, isSuperAdmin, hasInvoiceAccess, customerId, conversation, now, attachments, attachmentData, historyLimit, history, _a, historyLines, recentToolError, userDoc, initialProgress, assistantDoc, insertResult, assistantMessageId;
1324
1327
  var _this = this;
1325
1328
  var _b, _c;
1326
1329
  return __generator(this, function (_d) {
@@ -1336,6 +1339,8 @@ function executeAiAssistantCodexRun(payload, context) {
1336
1339
  }
1337
1340
  aiWorkerDebug = isAiWorkerDebugEnabled();
1338
1341
  requestId = normalizeOptionalString(input.request_id);
1342
+ codexModel = resolveCodexModel(input.config);
1343
+ codexFallbackModels = resolveCodexFallbackModels(input.config, codexModel);
1339
1344
  guardrail = evaluateAssistantGuardrails(message);
1340
1345
  if (!(guardrail === null || guardrail === void 0 ? void 0 : guardrail.blocked)) return [3 /*break*/, 5];
1341
1346
  return [4 /*yield*/, ensureConversation(input, 'codex')];
@@ -1415,6 +1420,9 @@ function executeAiAssistantCodexRun(payload, context) {
1415
1420
  historyLines.push("".concat(role, ": ").concat(content));
1416
1421
  }
1417
1422
  });
1423
+ recentToolError = isAssistantWhyFollowupMessage(message)
1424
+ ? resolveRecentAssistantToolError(history)
1425
+ : '';
1418
1426
  userDoc = {
1419
1427
  id_conversation: conversation._id,
1420
1428
  role: 'user',
@@ -1429,7 +1437,7 @@ function executeAiAssistantCodexRun(payload, context) {
1429
1437
  id_conversation: conversation._id,
1430
1438
  role: 'assistant',
1431
1439
  content: AI_ASSISTANT_PROGRESS_PLACEHOLDER,
1432
- metadata: __assign(__assign({ model: resolveCodexModel() }, (requestId ? { request_id: requestId } : {})), { pending: true, progress: initialProgress }),
1440
+ metadata: __assign(__assign(__assign({ model: codexModel }, (codexFallbackModels.length ? { model_fallbacks: codexFallbackModels } : {})), (requestId ? { request_id: requestId } : {})), { pending: true, progress: initialProgress }),
1433
1441
  createdAt: now,
1434
1442
  updatedAt: now
1435
1443
  };
@@ -1564,16 +1572,22 @@ function executeAiAssistantCodexRun(payload, context) {
1564
1572
  customerId: customerId,
1565
1573
  collectionHints: collectionHints,
1566
1574
  termHints: termHints,
1567
- fieldHints: fieldHints
1575
+ fieldHints: fieldHints,
1576
+ recentToolError: recentToolError
1568
1577
  });
1569
1578
  prompt_1 = buildAssistantCodexPrompt(message, attachmentData.promptText, historyLines.join('\n'), assistantContext);
1570
1579
  return [4 /*yield*/, resolveAssistantWorkspaceRoot()];
1571
1580
  case 6:
1572
1581
  workspaceRoot = _x.sent();
1573
- codexConfig = resolveCodexSettings();
1582
+ codexConfig = resolveCodexSettings({
1583
+ model: codexModel,
1584
+ fallbackModels: codexFallbackModels
1585
+ });
1574
1586
  runOptions = {
1575
1587
  timeoutMs: resolveCodexTimeoutMs(),
1588
+ fallbackModels: codexFallbackModels,
1576
1589
  threadOptions: {
1590
+ model: codexModel,
1577
1591
  workingDirectory: workspaceRoot,
1578
1592
  sandboxMode: 'read-only',
1579
1593
  skipGitRepoCheck: true,
@@ -1804,6 +1818,7 @@ function executeAiAssistantCodexRun(payload, context) {
1804
1818
  assistantContent = buildAssistantCodexErrorMessage(null);
1805
1819
  }
1806
1820
  assistantContent = applyAssistantVerificationNotes(assistantContent, toolResult);
1821
+ assistantContent = applyAssistantDatedReportWindow(assistantContent, toolResult);
1807
1822
  assistantContent = normalizeAssistantCurrencyText(assistantContent);
1808
1823
  if (aiWorkerDebug) {
1809
1824
  finishedAt = Date.now();
@@ -1874,7 +1889,7 @@ function executeAiAssistantCodexRun(payload, context) {
1874
1889
  }
1875
1890
  });
1876
1891
  }
1877
- finalMetadata = __assign(__assign(__assign({ model: resolveCodexModel() }, (requestId ? { request_id: requestId } : {})), (toolResult ? { tool_result: toolResult } : {})), (assistantDebug ? { debug: assistantDebug } : {}));
1892
+ finalMetadata = __assign(__assign(__assign(__assign({ model: codexModel }, (codexFallbackModels.length ? { model_fallbacks: codexFallbackModels } : {})), (requestId ? { request_id: requestId } : {})), (toolResult ? { tool_result: toolResult } : {})), (assistantDebug ? { debug: assistantDebug } : {}));
1878
1893
  finalAssistantDoc = __assign(__assign({}, assistantDoc), { _id: assistantMessageId, content: assistantContent, metadata: finalMetadata, updatedAt: finalNow });
1879
1894
  if (!assistantMessageId) return [3 /*break*/, 39];
1880
1895
  return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.updateOne({ _id: assistantMessageId }, {
@@ -3027,7 +3042,7 @@ function executeAiAssistantMongoAggregate(payload, context) {
3027
3042
  }
3028
3043
  function verifyAssistantAggregateReliability(params) {
3029
3044
  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;
3045
+ 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, rollingWindowBounds, verificationWindow, rollingStart, rollingEnd, fullMonthStart, fullMonthEndExclusive, normalizedMetric, bounds, fullMonthMatch, fullMonthDocs, fullMonthMap, fullMonthDiff, _b, alternateDateField, alternateDateCondition, bounds, alternateMatch, alternateDocs, alternateMap, alternateDiff, _c;
3031
3046
  var _d, _e;
3032
3047
  var _f;
3033
3048
  return __generator(this, function (_g) {
@@ -3168,13 +3183,29 @@ function verifyAssistantAggregateReliability(params) {
3168
3183
  }
3169
3184
  }
3170
3185
  rollingWindow = detectAssistantRollingMonthWindow(params.pipeline || []);
3186
+ rollingWindowBounds = null;
3187
+ verificationWindow = null;
3171
3188
  if ((rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.months) && (rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.upperNow)) {
3189
+ rollingWindowBounds = resolveAssistantRollingWindowBounds(rollingWindow.months);
3190
+ rollingStart = formatAssistantIsoDateOnly(rollingWindowBounds.rollingStart);
3191
+ rollingEnd = formatAssistantIsoDateOnly(rollingWindowBounds.now);
3192
+ fullMonthStart = formatAssistantIsoDateOnly(rollingWindowBounds.startOfWindow);
3193
+ fullMonthEndExclusive = formatAssistantIsoDateOnly(rollingWindowBounds.startOfCurrentMonth);
3194
+ verificationWindow = {
3195
+ type: 'rolling_last_n_months',
3196
+ months: rollingWindow.months,
3197
+ startDate: rollingStart,
3198
+ endDate: rollingEnd,
3199
+ timezone: 'UTC',
3200
+ fullMonthStartDate: fullMonthStart,
3201
+ fullMonthEndDateExclusive: fullMonthEndExclusive
3202
+ };
3172
3203
  checks.push({
3173
3204
  name: 'Date window shape',
3174
3205
  status: 'warn',
3175
- details: "Query uses rolling $$NOW bounds (last ".concat(rollingWindow.months, " months), which can include partial months.")
3206
+ details: "Query uses rolling $$NOW bounds (".concat(rollingStart, " to ").concat(rollingEnd, " UTC; last ").concat(rollingWindow.months, " months), which can include partial months.")
3176
3207
  });
3177
- warnings.push("Date window is rolling (last ".concat(rollingWindow.months, " months to now), so first/current months may be partial."));
3208
+ warnings.push("Date window is rolling (".concat(rollingStart, " to ").concat(rollingEnd, " UTC; last ").concat(rollingWindow.months, " months), so first/current months may be partial."));
3178
3209
  }
3179
3210
  else {
3180
3211
  checks.push({
@@ -3184,8 +3215,8 @@ function verifyAssistantAggregateReliability(params) {
3184
3215
  });
3185
3216
  }
3186
3217
  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);
3218
+ if (!((rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.months) && (rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.upperNow) && rollingWindowBounds)) return [3 /*break*/, 8];
3219
+ bounds = rollingWindowBounds;
3189
3220
  fullMonthMatch = mergeAssistantMatch(baseMatchNoDate, (_d = {},
3190
3221
  _d[dateField] = {
3191
3222
  $gte: bounds.startOfWindow,
@@ -3233,8 +3264,8 @@ function verifyAssistantAggregateReliability(params) {
3233
3264
  : '';
3234
3265
  if (!alternateDateField) return [3 /*break*/, 12];
3235
3266
  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);
3267
+ if ((rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.months) && (rollingWindow === null || rollingWindow === void 0 ? void 0 : rollingWindow.upperNow) && rollingWindowBounds) {
3268
+ bounds = rollingWindowBounds;
3238
3269
  alternateDateCondition = {
3239
3270
  $gte: bounds.rollingStart,
3240
3271
  $lte: bounds.now
@@ -3282,16 +3313,7 @@ function verifyAssistantAggregateReliability(params) {
3282
3313
  type: 'invoice_revenue_monthly',
3283
3314
  checks: checks,
3284
3315
  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
- }
3316
+ metrics: __assign({ baselineField: amountKey, dateField: dateField, comparedMonths: comparedMonths, avgDiffToPaidPct: (0, common_1.round)(avgPaidDiffPct * 100, 2), avgDiffToGrandPct: (0, common_1.round)(avgGrandDiffPct * 100, 2), maxDiffToPaidPct: (0, common_1.round)(maxPaidDiffPct * 100, 2), maxDiffToGrandPct: (0, common_1.round)(maxGrandDiffPct * 100, 2), avgPaidGrandGapPct: (0, common_1.round)(avgPaidGrandGapPct * 100, 2) }, (verificationWindow ? { window: verificationWindow } : {}))
3295
3317
  }];
3296
3318
  }
3297
3319
  });
@@ -3494,6 +3516,12 @@ function resolveAssistantRollingWindowBounds(months) {
3494
3516
  startOfWindow: startOfWindow
3495
3517
  };
3496
3518
  }
3519
+ function formatAssistantIsoDateOnly(value) {
3520
+ if (!(value instanceof Date) || Number.isNaN(value.getTime())) {
3521
+ return '';
3522
+ }
3523
+ return value.toISOString().slice(0, 10);
3524
+ }
3497
3525
  function extractAssistantMongoDirective(content) {
3498
3526
  var lines = String(content || '').split('\n');
3499
3527
  var directiveIndex = -1;
@@ -3548,13 +3576,12 @@ function extractAssistantMongoDirective(content) {
3548
3576
  };
3549
3577
  }
3550
3578
  function buildAssistantToolRequest(directive, payload) {
3551
- var _a;
3552
3579
  var base = directive.payload && typeof directive.payload === 'object' ? directive.payload : {};
3553
3580
  var request = __assign({}, base);
3554
- var route = normalizeOptionalString((_a = payload === null || payload === void 0 ? void 0 : payload.context) === null || _a === void 0 ? void 0 : _a.route);
3555
- if (!request.permissionView && route) {
3556
- request.permissionView = route;
3581
+ if (!request.permissionView) {
3582
+ request.permissionView = '/report-builder';
3557
3583
  }
3584
+ request.permissionView = normalizeAssistantPermissionView(request.permissionView, request.collection);
3558
3585
  if (!request.id_client) {
3559
3586
  var idClient = normalizeOptionalString(payload === null || payload === void 0 ? void 0 : payload.id_client);
3560
3587
  if (idClient) {
@@ -3566,6 +3593,24 @@ function buildAssistantToolRequest(directive, payload) {
3566
3593
  }
3567
3594
  return request;
3568
3595
  }
3596
+ function normalizeAssistantPermissionView(permissionView, collection) {
3597
+ var normalizedPermission = normalizeOptionalString(permissionView);
3598
+ var normalizedCollection = normalizeOptionalString(collection);
3599
+ var loweredPermission = normalizedPermission.toLowerCase();
3600
+ if (!normalizedPermission) {
3601
+ return '/report-builder';
3602
+ }
3603
+ if (loweredPermission === '/report-builder' || loweredPermission.startsWith('/report-builder/')) {
3604
+ return '/report-builder';
3605
+ }
3606
+ if (loweredPermission === '/report' || loweredPermission.startsWith('/report/')) {
3607
+ if (requiresInvoicePermission(normalizedCollection)) {
3608
+ return '/invoice/list';
3609
+ }
3610
+ return '/report-builder';
3611
+ }
3612
+ return normalizedPermission;
3613
+ }
3569
3614
  function buildAssistantToolResultPayload(directive, toolResponse) {
3570
3615
  var _a, _b, _c;
3571
3616
  var directivePayload = directive.payload || {};
@@ -3701,6 +3746,30 @@ function applyAssistantVerificationNotes(value, toolResult) {
3701
3746
  }
3702
3747
  return "".concat(content, "\n\n").concat(noteLines.join('\n')).trim();
3703
3748
  }
3749
+ function applyAssistantDatedReportWindow(value, toolResult) {
3750
+ var _a, _b, _c;
3751
+ var content = normalizeOptionalString(value);
3752
+ var window = (_c = (_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.metrics) === null || _c === void 0 ? void 0 : _c.window;
3753
+ if (!window || typeof window !== 'object') {
3754
+ return content || value || '';
3755
+ }
3756
+ var startDate = normalizeOptionalString(window.startDate);
3757
+ var endDate = normalizeOptionalString(window.endDate);
3758
+ if (!startDate || !endDate) {
3759
+ return content || value || '';
3760
+ }
3761
+ var months = normalizeOptionalNumber(window.months);
3762
+ var monthsText = months && months > 0 ? "; rolling last ".concat((0, common_1.round)(months), " month").concat((0, common_1.round)(months) === 1 ? '' : 's') : '';
3763
+ var line = "Report date range (UTC): ".concat(startDate, " to ").concat(endDate).concat(monthsText, ".");
3764
+ var normalizedLower = String(content || '').toLowerCase();
3765
+ if (normalizedLower.includes(startDate.toLowerCase()) && normalizedLower.includes(endDate.toLowerCase())) {
3766
+ return content || value || '';
3767
+ }
3768
+ if (!content) {
3769
+ return line;
3770
+ }
3771
+ return "".concat(line, "\n\n").concat(content).trim();
3772
+ }
3704
3773
  function buildAssistantDebugPayload(params) {
3705
3774
  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;
3706
3775
  var notes = [];
@@ -3877,7 +3946,7 @@ function buildAssistantToolErrorMessage(error, directive, request) {
3877
3946
  ? "Open ".concat(routeHint, " in the app to view this data or request access.")
3878
3947
  : 'Open the related screen in the app to view this data or request access.';
3879
3948
  if (!routeHint && collection && requiresInvoicePermission(collection)) {
3880
- routeLine = 'Open /invoice/list or /report/invoice to view this data or request access.';
3949
+ routeLine = 'Open /invoice/list or /report-builder/list to view this data or request access.';
3881
3950
  }
3882
3951
  if (normalized.includes('permission scope required')) {
3883
3952
  return "I need a permission scope to access that data. ".concat(routeLine);
@@ -3891,6 +3960,9 @@ function buildAssistantToolErrorMessage(error, directive, request) {
3891
3960
  if (normalized.includes('database access denied')) {
3892
3961
  return "Database access is restricted for that request. ".concat(routeLine);
3893
3962
  }
3963
+ if (normalized.includes('undefined variable') && normalized.includes('now_minus')) {
3964
+ return "The query used an unsupported relative date token. Please retry; the assistant now normalizes relative dates automatically. ".concat(routeLine);
3965
+ }
3894
3966
  if (normalized.includes('report builder bridge') && normalized.includes('not configured')) {
3895
3967
  return "That dataset is not configured for report builder access yet. ".concat(routeLine);
3896
3968
  }
@@ -5547,9 +5619,49 @@ function stripAssistantMarkdownTables(value) {
5547
5619
  if (!raw) {
5548
5620
  return '';
5549
5621
  }
5550
- var tablePattern = /(^|\n)\s*\|[^\n]*\|\s*\n\s*\|[ \t:-|]+\|\s*\n(?:\s*\|[^\n]*\|\s*\n?)*/g;
5551
- var cleaned = raw.replace(tablePattern, '\n').trim();
5552
- return cleaned;
5622
+ var lines = raw.split('\n');
5623
+ var kept = [];
5624
+ for (var index = 0; index < lines.length; index += 1) {
5625
+ var line = lines[index];
5626
+ var nextLine = lines[index + 1];
5627
+ if (isAssistantMarkdownTableHeaderLine(line) && isAssistantMarkdownTableSeparatorLine(nextLine || '')) {
5628
+ index += 2;
5629
+ while (index < lines.length && isAssistantMarkdownTableRowLine(lines[index])) {
5630
+ index += 1;
5631
+ }
5632
+ while (index < lines.length && !normalizeOptionalString(lines[index])) {
5633
+ index += 1;
5634
+ }
5635
+ index -= 1;
5636
+ if (kept.length && normalizeOptionalString(kept[kept.length - 1])) {
5637
+ kept.push('');
5638
+ }
5639
+ continue;
5640
+ }
5641
+ kept.push(line);
5642
+ }
5643
+ return kept.join('\n').replace(/\n{3,}/g, '\n\n').trim();
5644
+ }
5645
+ function isAssistantMarkdownTableHeaderLine(line) {
5646
+ var trimmed = normalizeOptionalString(line);
5647
+ if (!trimmed || !trimmed.includes('|')) {
5648
+ return false;
5649
+ }
5650
+ return /^\|.+\|$/.test(trimmed);
5651
+ }
5652
+ function isAssistantMarkdownTableSeparatorLine(line) {
5653
+ var trimmed = normalizeOptionalString(line);
5654
+ if (!trimmed || !trimmed.includes('|')) {
5655
+ return false;
5656
+ }
5657
+ return /^\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(trimmed);
5658
+ }
5659
+ function isAssistantMarkdownTableRowLine(line) {
5660
+ var trimmed = normalizeOptionalString(line);
5661
+ if (!trimmed || !trimmed.includes('|')) {
5662
+ return false;
5663
+ }
5664
+ return /^\|.*\|$/.test(trimmed);
5553
5665
  }
5554
5666
  function applyAssistantDisplayTableToResponse(value, display) {
5555
5667
  if (!display || !Array.isArray(display.rows) || !display.rows.length) {
@@ -5595,7 +5707,7 @@ function isDisplayObjectLike(value) {
5595
5707
  }
5596
5708
  function ensureAssistantReadAccess(context, permissionView, collection) {
5597
5709
  return __awaiter(this, void 0, void 0, function () {
5598
- var idUser, user, isSuperAdmin, normalizedPermission, normalizedCollection;
5710
+ var idUser, user, isSuperAdmin, normalizedCollection, normalizedPermission, requiresInvoiceAccess, hasInvoiceAccess, hasViewAccess;
5599
5711
  var _a;
5600
5712
  return __generator(this, function (_b) {
5601
5713
  switch (_b.label) {
@@ -5611,20 +5723,21 @@ function ensureAssistantReadAccess(context, permissionView, collection) {
5611
5723
  throw new Error('AI assistant report builder bridge: Unauthorized.');
5612
5724
  }
5613
5725
  isSuperAdmin = !!((_a = user === null || user === void 0 ? void 0 : user.roles) === null || _a === void 0 ? void 0 : _a.super_admin);
5614
- normalizedPermission = normalizeOptionalString(permissionView);
5615
- if (!isSuperAdmin) {
5616
- if (!normalizedPermission) {
5617
- throw new Error('AI assistant report builder bridge: Permission scope required.');
5618
- }
5619
- if (!userHasViewPermission(user, normalizedPermission)) {
5620
- throw new Error('AI assistant report builder bridge: Access denied.');
5621
- }
5622
- normalizedCollection = normalizeOptionalString(collection);
5623
- if (normalizedCollection && requiresInvoicePermission(normalizedCollection) && !userHasInvoiceAccess(user)) {
5624
- throw new Error('AI assistant report builder bridge: Access denied.');
5625
- }
5726
+ if (isSuperAdmin) {
5727
+ return [2 /*return*/, { user: user, isSuperAdmin: isSuperAdmin }];
5626
5728
  }
5627
- else if (normalizedPermission && !userHasViewPermission(user, normalizedPermission)) {
5729
+ normalizedCollection = normalizeOptionalString(collection);
5730
+ normalizedPermission = normalizeAssistantPermissionView(permissionView, normalizedCollection);
5731
+ if (!normalizedPermission) {
5732
+ throw new Error('AI assistant report builder bridge: Permission scope required.');
5733
+ }
5734
+ requiresInvoiceAccess = normalizedCollection ? requiresInvoicePermission(normalizedCollection) : false;
5735
+ hasInvoiceAccess = requiresInvoiceAccess && userHasInvoiceAccess(user);
5736
+ hasViewAccess = userHasViewPermission(user, normalizedPermission);
5737
+ if (!hasViewAccess && !hasInvoiceAccess) {
5738
+ throw new Error('AI assistant report builder bridge: Access denied.');
5739
+ }
5740
+ if (requiresInvoiceAccess && !hasInvoiceAccess) {
5628
5741
  throw new Error('AI assistant report builder bridge: Access denied.');
5629
5742
  }
5630
5743
  return [2 /*return*/, { user: user, isSuperAdmin: isSuperAdmin }];
@@ -5700,6 +5813,90 @@ function sanitizeAssistantProjection(projection) {
5700
5813
  return projection;
5701
5814
  }
5702
5815
  var AGG_MATCH_EXPR_OPERATORS = new Set(['$eq', '$ne', '$gt', '$gte', '$lt', '$lte']);
5816
+ var ASSISTANT_NOW_RELATIVE_PATTERN = /^\$\$NOW_(MINUS|PLUS)_([0-9]+)_(MINUTES?|HOURS?|DAYS?|WEEKS?|MONTHS?|YEARS?)$/i;
5817
+ function normalizeAssistantTimeUnit(raw) {
5818
+ var normalized = String(raw || '').toLowerCase();
5819
+ if (normalized.startsWith('minute')) {
5820
+ return 'minute';
5821
+ }
5822
+ if (normalized.startsWith('hour')) {
5823
+ return 'hour';
5824
+ }
5825
+ if (normalized.startsWith('day')) {
5826
+ return 'day';
5827
+ }
5828
+ if (normalized.startsWith('week')) {
5829
+ return 'week';
5830
+ }
5831
+ if (normalized.startsWith('month')) {
5832
+ return 'month';
5833
+ }
5834
+ return 'year';
5835
+ }
5836
+ function parseAssistantNowRelativeToken(value) {
5837
+ var trimmed = normalizeOptionalString(value);
5838
+ if (!trimmed) {
5839
+ return null;
5840
+ }
5841
+ var match = trimmed.match(ASSISTANT_NOW_RELATIVE_PATTERN);
5842
+ if (!match) {
5843
+ return null;
5844
+ }
5845
+ var direction = String(match[1] || '').toUpperCase() === 'PLUS' ? 'PLUS' : 'MINUS';
5846
+ var amount = Number(match[2]);
5847
+ if (!Number.isFinite(amount) || amount < 0) {
5848
+ return null;
5849
+ }
5850
+ var unit = normalizeAssistantTimeUnit(match[3] || '');
5851
+ return {
5852
+ operator: direction === 'PLUS' ? '$dateAdd' : '$dateSubtract',
5853
+ amount: amount,
5854
+ unit: unit
5855
+ };
5856
+ }
5857
+ function normalizeAssistantNowExprOperand(value) {
5858
+ var _a;
5859
+ if (typeof value !== 'string') {
5860
+ return value;
5861
+ }
5862
+ var trimmed = normalizeOptionalString(value);
5863
+ if (!trimmed) {
5864
+ return value;
5865
+ }
5866
+ if (trimmed === '$$NOW') {
5867
+ return '$$NOW';
5868
+ }
5869
+ var parsed = parseAssistantNowRelativeToken(trimmed);
5870
+ if (!parsed) {
5871
+ return value;
5872
+ }
5873
+ return _a = {},
5874
+ _a[parsed.operator] = {
5875
+ startDate: '$$NOW',
5876
+ unit: parsed.unit,
5877
+ amount: parsed.amount
5878
+ },
5879
+ _a;
5880
+ }
5881
+ function normalizeAssistantNowExprPlaceholdersDeep(value) {
5882
+ if (Array.isArray(value)) {
5883
+ return value.map(function (entry) { return normalizeAssistantNowExprPlaceholdersDeep(entry); });
5884
+ }
5885
+ if (value instanceof Date || value instanceof RegExp || isMongoObjectId(value)) {
5886
+ return value;
5887
+ }
5888
+ if (typeof value === 'string') {
5889
+ return normalizeAssistantNowExprOperand(value);
5890
+ }
5891
+ if (!value || typeof value !== 'object') {
5892
+ return value;
5893
+ }
5894
+ var result = {};
5895
+ Object.keys(value).forEach(function (key) {
5896
+ result[key] = normalizeAssistantNowExprPlaceholdersDeep(value[key]);
5897
+ });
5898
+ return result;
5899
+ }
5703
5900
  function isMatchExpressionOperand(value) {
5704
5901
  if (typeof value === 'string') {
5705
5902
  return value.startsWith('$$');
@@ -5757,7 +5954,7 @@ function rewriteMatchExpressionsToExpr(match) {
5757
5954
  }
5758
5955
  var operand = nextEntry_1[op];
5759
5956
  if (isMatchExpressionOperand(operand)) {
5760
- exprClauses.push((_a = {}, _a[op] = ["$".concat(key), operand], _a));
5957
+ exprClauses.push((_a = {}, _a[op] = ["$".concat(key), normalizeAssistantNowExprOperand(operand)], _a));
5761
5958
  delete nextEntry_1[op];
5762
5959
  moved_1 = true;
5763
5960
  }
@@ -5771,7 +5968,7 @@ function rewriteMatchExpressionsToExpr(match) {
5771
5968
  return;
5772
5969
  }
5773
5970
  if (typeof entry === 'string' && entry.startsWith('$$')) {
5774
- exprClauses.push({ $eq: ["$".concat(key), entry] });
5971
+ exprClauses.push({ $eq: ["$".concat(key), normalizeAssistantNowExprOperand(entry)] });
5775
5972
  return;
5776
5973
  }
5777
5974
  result[key] = entry;
@@ -5848,7 +6045,7 @@ function normalizeAssistantAggregatePipeline(pipeline, collection) {
5848
6045
  var statusNormalized = isInvoiceCollection ? normalizeInvoiceStatusMatch(exprRewritten) : exprRewritten;
5849
6046
  next.$geoNear = __assign(__assign({}, next.$geoNear), { query: applyAssistantNameRegexToQuery(statusNormalized) });
5850
6047
  }
5851
- return next;
6048
+ return normalizeAssistantNowExprPlaceholdersDeep(next);
5852
6049
  });
5853
6050
  }
5854
6051
  function buildAssistantAggregatePipeline(query, pipeline) {
@@ -7495,7 +7692,9 @@ function normalizeMongoQuery(query) {
7495
7692
  throw new Error('AI assistant report builder bridge: Query contains restricted operators.');
7496
7693
  }
7497
7694
  var rewritten = rewriteEmbeddedMatchObjects(normalized);
7498
- return applyAssistantNameRegexToQuery(rewritten);
7695
+ var exprRewritten = rewriteMatchExpressionsToExpr(rewritten);
7696
+ var nowNormalized = normalizeAssistantNowExprPlaceholdersDeep(exprRewritten);
7697
+ return applyAssistantNameRegexToQuery(nowNormalized);
7499
7698
  }
7500
7699
  function shouldApplyAssistantNameRegex(field) {
7501
7700
  var normalized = String(field || '').toLowerCase().trim();
@@ -8019,7 +8218,7 @@ function shouldAllowVersionCollections(message) {
8019
8218
  }
8020
8219
  function resolveReportCollectionName(permissionView, collectionNames, currentCollection) {
8021
8220
  var normalizedView = normalizeOptionalString(permissionView).toLowerCase();
8022
- if (!normalizedView.startsWith('/report/')) {
8221
+ if (!normalizedView.startsWith('/report-builder')) {
8023
8222
  return null;
8024
8223
  }
8025
8224
  var current = stripVersionSuffix(normalizeOptionalString(currentCollection));
@@ -8027,11 +8226,6 @@ function resolveReportCollectionName(permissionView, collectionNames, currentCol
8027
8226
  if (collectionNames.includes(reportCollection)) {
8028
8227
  return reportCollection;
8029
8228
  }
8030
- var routeTail = normalizedView.replace('/report/', '').replace(/\//g, '-');
8031
- var routeCandidate = routeTail ? "report-".concat(routeTail) : '';
8032
- if (routeCandidate && collectionNames.includes(routeCandidate)) {
8033
- return routeCandidate;
8034
- }
8035
8229
  return null;
8036
8230
  }
8037
8231
  function resolveBaseCollectionFromReport(db, dbName, collection) {
@@ -8069,7 +8263,7 @@ function resolveCollectionOverrideWithContext(params) {
8069
8263
  to: reportPreferred,
8070
8264
  fromScore: 0,
8071
8265
  toScore: 0,
8072
- reason: 'report route preference'
8266
+ reason: 'report builder route preference'
8073
8267
  };
8074
8268
  }
8075
8269
  }
@@ -8677,14 +8871,43 @@ function userHasViewPermission(user, view) {
8677
8871
  }
8678
8872
  return false;
8679
8873
  }
8680
- function userHasAnyViewPermission(user, views) {
8681
- if (!user || !Array.isArray(views)) {
8874
+ function collectUserViewPermissions(user) {
8875
+ var _a, _b;
8876
+ if (!user) {
8877
+ return [];
8878
+ }
8879
+ var groups = Array.isArray((_a = user.roles) === null || _a === void 0 ? void 0 : _a.groups) ? user.roles.groups : [];
8880
+ var miscs = Array.isArray((_b = user.roles) === null || _b === void 0 ? void 0 : _b.miscs) ? user.roles.miscs : [];
8881
+ var collected = [];
8882
+ var seen = new Set();
8883
+ var push = function (value) {
8884
+ var normalized = normalizeOptionalString(value);
8885
+ if (!normalized || seen.has(normalized)) {
8886
+ return;
8887
+ }
8888
+ seen.add(normalized);
8889
+ collected.push(normalized);
8890
+ };
8891
+ groups.forEach(function (group) {
8892
+ var views = Array.isArray(group === null || group === void 0 ? void 0 : group.views) ? group.views : [];
8893
+ views.forEach(push);
8894
+ });
8895
+ miscs.forEach(push);
8896
+ return collected;
8897
+ }
8898
+ function userHasViewTokenPermission(user, tokenRegex) {
8899
+ var _a;
8900
+ if (!user || !tokenRegex) {
8682
8901
  return false;
8683
8902
  }
8684
- return views.some(function (view) { return view && userHasViewPermission(user, view); });
8903
+ if ((_a = user.roles) === null || _a === void 0 ? void 0 : _a.super_admin) {
8904
+ return true;
8905
+ }
8906
+ var permissions = collectUserViewPermissions(user);
8907
+ return permissions.some(function (view) { return tokenRegex.test(view); });
8685
8908
  }
8686
8909
  function userHasInvoiceAccess(user) {
8687
- return userHasAnyViewPermission(user, ['/invoice', '/report/invoice']);
8910
+ return userHasViewTokenPermission(user, /invoice/i);
8688
8911
  }
8689
8912
  function requiresInvoicePermission(collection) {
8690
8913
  var normalized = normalizeOptionalString(collection).toLowerCase();
@@ -8736,16 +8959,59 @@ function resolveCodexWorkerThreadEnabled() {
8736
8959
  }
8737
8960
  return process.env.IS_WORKER_INSTANCE !== 'true';
8738
8961
  }
8739
- function resolveCodexModel() {
8740
- var config = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
8741
- var raw = normalizeOptionalString(config['AI_ASSISTANT_CODEX_MODEL']
8962
+ function normalizeCodexModelList(value) {
8963
+ if (Array.isArray(value)) {
8964
+ return value
8965
+ .map(function (entry) { return normalizeOptionalString(entry); })
8966
+ .filter(Boolean);
8967
+ }
8968
+ var raw = normalizeOptionalString(value);
8969
+ if (!raw) {
8970
+ return [];
8971
+ }
8972
+ return raw
8973
+ .split(',')
8974
+ .map(function (entry) { return normalizeOptionalString(entry); })
8975
+ .filter(Boolean);
8976
+ }
8977
+ function resolveCodexModel(config) {
8978
+ var serverConfig = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
8979
+ var raw = normalizeOptionalString((config === null || config === void 0 ? void 0 : config.model)
8980
+ || (config === null || config === void 0 ? void 0 : config.codex_model)
8981
+ || serverConfig['AI_ASSISTANT_CODEX_MODEL']
8742
8982
  || process.env.AI_ASSISTANT_CODEX_MODEL
8743
- || config['AI_TERMINAL_CODEX_MODEL']
8983
+ || serverConfig['AI_TERMINAL_CODEX_MODEL']
8744
8984
  || process.env.AI_TERMINAL_CODEX_MODEL
8745
- || config['AI_DASHBOARD_CODEX_MODEL']
8985
+ || serverConfig['AI_DASHBOARD_CODEX_MODEL']
8746
8986
  || process.env.AI_DASHBOARD_CODEX_MODEL);
8747
8987
  return raw || DEFAULT_CODEX_MODEL;
8748
8988
  }
8989
+ function resolveCodexFallbackModels(config, primaryModel) {
8990
+ var serverConfig = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
8991
+ var primary = normalizeOptionalString(primaryModel || resolveCodexModel(config));
8992
+ var models = [];
8993
+ var push = function (value) {
8994
+ var normalized = normalizeOptionalString(value);
8995
+ if (!normalized || normalized === primary || models.includes(normalized)) {
8996
+ return;
8997
+ }
8998
+ models.push(normalized);
8999
+ };
9000
+ normalizeCodexModelList(config === null || config === void 0 ? void 0 : config.fallback_models).forEach(push);
9001
+ normalizeCodexModelList(config === null || config === void 0 ? void 0 : config.fallbackModels).forEach(push);
9002
+ push(config === null || config === void 0 ? void 0 : config.fallback_model);
9003
+ push(config === null || config === void 0 ? void 0 : config.fallbackModel);
9004
+ normalizeCodexModelList(serverConfig['AI_ASSISTANT_CODEX_FALLBACK_MODELS'] || process.env.AI_ASSISTANT_CODEX_FALLBACK_MODELS).forEach(push);
9005
+ push(serverConfig['AI_ASSISTANT_CODEX_FALLBACK_MODEL'] || process.env.AI_ASSISTANT_CODEX_FALLBACK_MODEL);
9006
+ normalizeCodexModelList(serverConfig['AI_TERMINAL_CODEX_FALLBACK_MODELS'] || process.env.AI_TERMINAL_CODEX_FALLBACK_MODELS).forEach(push);
9007
+ push(serverConfig['AI_TERMINAL_CODEX_FALLBACK_MODEL'] || process.env.AI_TERMINAL_CODEX_FALLBACK_MODEL);
9008
+ normalizeCodexModelList(serverConfig['AI_DASHBOARD_CODEX_FALLBACK_MODELS'] || process.env.AI_DASHBOARD_CODEX_FALLBACK_MODELS).forEach(push);
9009
+ push(serverConfig['AI_DASHBOARD_CODEX_FALLBACK_MODEL'] || process.env.AI_DASHBOARD_CODEX_FALLBACK_MODEL);
9010
+ if (!models.length) {
9011
+ push(DEFAULT_CODEX_FALLBACK_MODEL);
9012
+ }
9013
+ return models;
9014
+ }
8749
9015
  function resolveCodexThoughtLevel() {
8750
9016
  var config = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
8751
9017
  var raw = normalizeOptionalString(config['AI_ASSISTANT_CODEX_THOUGHT_LEVEL']
@@ -8760,25 +9026,38 @@ function resolveCodexThoughtLevel() {
8760
9026
  }
8761
9027
  return 'low';
8762
9028
  }
8763
- function resolveCodexSettings() {
9029
+ function resolveCodexSettings(options) {
8764
9030
  var serverConfig = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
8765
9031
  var apiKey = (serverConfig['OPENAI_API_KEY'] || process.env.OPENAI_API_KEY || '').trim();
8766
9032
  if (!apiKey) {
8767
9033
  throw new Error('OpenAI API key missing. Add OPENAI_API_KEY to server config.');
8768
9034
  }
8769
- return {
8770
- apiKey: apiKey,
8771
- baseUrl: (serverConfig['OPENAI_BASE_URL'] || process.env.OPENAI_BASE_URL || '').trim() || undefined,
8772
- model: resolveCodexModel(),
8773
- maxRetries: normalizeOptionalNumber(serverConfig['OPENAI_MAX_RETRIES'] || process.env.OPENAI_MAX_RETRIES),
8774
- retryDelayMs: normalizeOptionalNumber(serverConfig['OPENAI_RETRY_DELAY_MS'] || process.env.OPENAI_RETRY_DELAY_MS)
8775
- };
9035
+ var model = normalizeOptionalString(options === null || options === void 0 ? void 0 : options.model) || resolveCodexModel();
9036
+ var fallbackModels = resolveCodexFallbackModels({ fallbackModels: options === null || options === void 0 ? void 0 : options.fallbackModels }, model);
9037
+ return __assign(__assign({ apiKey: apiKey, baseUrl: (serverConfig['OPENAI_BASE_URL'] || process.env.OPENAI_BASE_URL || '').trim() || undefined, model: model }, (fallbackModels.length ? { fallbackModel: fallbackModels[0], fallbackModels: fallbackModels } : {})), { maxRetries: normalizeOptionalNumber(serverConfig['OPENAI_MAX_RETRIES'] || process.env.OPENAI_MAX_RETRIES), retryDelayMs: normalizeOptionalNumber(serverConfig['OPENAI_RETRY_DELAY_MS'] || process.env.OPENAI_RETRY_DELAY_MS) });
8776
9038
  }
8777
- function getAssistantCodexClient() {
8778
- if (!assistantCodexClient) {
8779
- assistantCodexClient = new codex_client_1.CodexClient(resolveCodexSettings());
9039
+ function buildAssistantCodexClientCacheKey(config) {
9040
+ var _a, _b;
9041
+ return JSON.stringify({
9042
+ apiKey: normalizeOptionalString(config === null || config === void 0 ? void 0 : config.apiKey),
9043
+ baseUrl: normalizeOptionalString(config === null || config === void 0 ? void 0 : config.baseUrl),
9044
+ model: normalizeOptionalString(config === null || config === void 0 ? void 0 : config.model),
9045
+ fallbackModel: normalizeOptionalString(config === null || config === void 0 ? void 0 : config.fallbackModel),
9046
+ fallbackModels: normalizeCodexModelList(config === null || config === void 0 ? void 0 : config.fallbackModels),
9047
+ maxRetries: (_a = config === null || config === void 0 ? void 0 : config.maxRetries) !== null && _a !== void 0 ? _a : null,
9048
+ retryDelayMs: (_b = config === null || config === void 0 ? void 0 : config.retryDelayMs) !== null && _b !== void 0 ? _b : null
9049
+ });
9050
+ }
9051
+ function getAssistantCodexClient(config) {
9052
+ var resolved = config || resolveCodexSettings();
9053
+ var key = buildAssistantCodexClientCacheKey(resolved);
9054
+ var existing = assistantCodexClientByConfig.get(key);
9055
+ if (existing) {
9056
+ return existing;
8780
9057
  }
8781
- return assistantCodexClient;
9058
+ var client = new codex_client_1.CodexClient(resolved);
9059
+ assistantCodexClientByConfig.set(key, client);
9060
+ return client;
8782
9061
  }
8783
9062
  var CodexWorkerBootstrapError = /** @class */ (function (_super) {
8784
9063
  __extends(CodexWorkerBootstrapError, _super);
@@ -8873,14 +9152,14 @@ function runCodexInWorkerThread(prompt, runOptions, config, streamStatusHandler)
8873
9152
  case 0:
8874
9153
  streamedOptions = applyCodexStreamStatusHandler(runOptions, streamStatusHandler);
8875
9154
  if (!!resolveCodexWorkerThreadEnabled()) return [3 /*break*/, 2];
8876
- codexClient = getAssistantCodexClient();
9155
+ codexClient = getAssistantCodexClient(config);
8877
9156
  return [4 /*yield*/, codexClient.run(prompt, streamedOptions)];
8878
9157
  case 1: return [2 /*return*/, _a.sent()];
8879
9158
  case 2: return [4 /*yield*/, resolveCodexWorkerPath()];
8880
9159
  case 3:
8881
9160
  workerPath = _a.sent();
8882
9161
  if (!!workerPath) return [3 /*break*/, 5];
8883
- codexClient = getAssistantCodexClient();
9162
+ codexClient = getAssistantCodexClient(config);
8884
9163
  return [4 /*yield*/, codexClient.run(prompt, streamedOptions)];
8885
9164
  case 4: return [2 /*return*/, _a.sent()];
8886
9165
  case 5:
@@ -8893,7 +9172,7 @@ function runCodexInWorkerThread(prompt, runOptions, config, streamStatusHandler)
8893
9172
  throw error_4;
8894
9173
  }
8895
9174
  console.error('Codex worker bootstrap failed, falling back to in-process run.', error_4);
8896
- codexClient = getAssistantCodexClient();
9175
+ codexClient = getAssistantCodexClient(config);
8897
9176
  return [4 /*yield*/, codexClient.run(prompt, streamedOptions)];
8898
9177
  case 8: return [2 /*return*/, _a.sent()];
8899
9178
  case 9: return [2 /*return*/];
@@ -9055,7 +9334,8 @@ function sanitizeCodexRunOptions(options) {
9055
9334
  timeoutMs: options.timeoutMs,
9056
9335
  threadOptions: options.threadOptions,
9057
9336
  threadKey: options.threadKey,
9058
- reuseThread: options.reuseThread
9337
+ reuseThread: options.reuseThread,
9338
+ fallbackModels: options.fallbackModels
9059
9339
  };
9060
9340
  }
9061
9341
  function resolveCodexWorkerPath() {
@@ -9438,6 +9718,10 @@ function buildAssistantContext(input, userContext) {
9438
9718
  lines.push(hint);
9439
9719
  });
9440
9720
  }
9721
+ var recentToolError = normalizeOptionalString(userContext === null || userContext === void 0 ? void 0 : userContext.recentToolError);
9722
+ if (recentToolError) {
9723
+ lines.push("Most recent data-query error: ".concat(recentToolError));
9724
+ }
9441
9725
  var mongoDb = normalizeOptionalString((_c = input === null || input === void 0 ? void 0 : input.mongo) === null || _c === void 0 ? void 0 : _c.database);
9442
9726
  var mongoDbs = Array.isArray((_d = input === null || input === void 0 ? void 0 : input.mongo) === null || _d === void 0 ? void 0 : _d.databases)
9443
9727
  ? input.mongo.databases.map(function (value) { return normalizeOptionalString(value); }).filter(Boolean)
@@ -9534,6 +9818,34 @@ function buildAssistantFieldHints(message, collectionNames, options) {
9534
9818
  });
9535
9819
  return hints;
9536
9820
  }
9821
+ function isAssistantWhyFollowupMessage(message) {
9822
+ var normalized = normalizeOptionalString(message).toLowerCase();
9823
+ if (!normalized) {
9824
+ return false;
9825
+ }
9826
+ return /^(so\s+)?why(?:\s+not)?[.!?]*$/.test(normalized)
9827
+ || /^(so\s+)?why\b/.test(normalized);
9828
+ }
9829
+ function resolveRecentAssistantToolError(history) {
9830
+ var _a, _b;
9831
+ var entries = Array.isArray(history) ? history : [];
9832
+ for (var i = entries.length - 1; i >= 0; i -= 1) {
9833
+ var entry = entries[i];
9834
+ if ((entry === null || entry === void 0 ? void 0 : entry.role) !== 'assistant') {
9835
+ continue;
9836
+ }
9837
+ var notes = Array.isArray((_b = (_a = entry === null || entry === void 0 ? void 0 : entry.metadata) === null || _a === void 0 ? void 0 : _a.debug) === null || _b === void 0 ? void 0 : _b.notes)
9838
+ ? entry.metadata.debug.notes
9839
+ : [];
9840
+ var matched = notes
9841
+ .map(function (note) { return normalizeOptionalString(note); })
9842
+ .find(function (note) { return note.toLowerCase().startsWith('tool error:'); });
9843
+ if (matched) {
9844
+ return matched.replace(/^tool error:\s*/i, '').trim();
9845
+ }
9846
+ }
9847
+ return '';
9848
+ }
9537
9849
  var cachedClientRouteIndex = null;
9538
9850
  function normalizeRouteKey(value) {
9539
9851
  var trimmed = normalizeOptionalString(value);