@resolveio/server-lib 22.0.4 → 22.0.5

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.
@@ -159,6 +159,10 @@ var AI_ASSISTANT_COLLECTION_OVERRIDE_MIN_SCORE = 20;
159
159
  var AI_ASSISTANT_ID_LOOKUP_MAX_RESULTS = 20;
160
160
  var AI_ASSISTANT_ID_LOOKUP_CANDIDATE_LIMIT = 4;
161
161
  var AI_ASSISTANT_NAME_MATCH_FALLBACK_MAX_FIELDS = 12;
162
+ var AI_ASSISTANT_CONTEXT_MAX_COLLECTIONS = 3;
163
+ var AI_ASSISTANT_CONTEXT_MAX_FIELDS_PER_COLLECTION = 24;
164
+ var AI_ASSISTANT_LOCALE = 'en-US';
165
+ var AI_ASSISTANT_CURRENCY_CODE = 'USD';
162
166
  var AI_ASSISTANT_PROGRESS_TICKS = [
163
167
  'Grabbing Data',
164
168
  'Drafting response'
@@ -277,6 +281,18 @@ var AI_ASSISTANT_COLLECTION_DOMAIN_TOKENS = new Set([
277
281
  'chemicals',
278
282
  'invoice',
279
283
  'invoices',
284
+ 'revenue',
285
+ 'revenues',
286
+ 'sales',
287
+ 'sale',
288
+ 'billing',
289
+ 'billings',
290
+ 'payment',
291
+ 'payments',
292
+ 'paid',
293
+ 'income',
294
+ 'finance',
295
+ 'financial',
280
296
  'work',
281
297
  'order',
282
298
  'orders',
@@ -317,7 +333,11 @@ var AI_ASSISTANT_FIELD_TOKEN_SYNONYMS = {
317
333
  batch: ['lot'],
318
334
  lot: ['batch'],
319
335
  date: ['created', 'createdat', 'created_at', 'date_created', 'updated', 'updatedat', 'updated_at', 'date_updated'],
320
- total: ['sum', 'amount', 'total_amount', 'total_quantity']
336
+ total: ['sum', 'amount', 'total_amount', 'total_quantity'],
337
+ revenue: ['sales', 'billing', 'paid', 'payment', 'total', 'amount', 'grand', 'subtotal', 'sub_total', 'paid_total', 'grand_total'],
338
+ sales: ['revenue', 'billing', 'paid', 'payment', 'total', 'amount', 'grand', 'subtotal', 'sub_total'],
339
+ billing: ['revenue', 'sales', 'paid', 'payment', 'total', 'amount', 'grand', 'subtotal', 'sub_total'],
340
+ payment: ['paid', 'revenue', 'sales', 'billing', 'total', 'amount']
321
341
  };
322
342
  var AI_ASSISTANT_ID_LOOKUP_HINTS = {
323
343
  customer: ['customers', 'customer-versions'],
@@ -336,6 +356,11 @@ var AI_ASSISTANT_TERM_SYNONYMS = [
336
356
  label: 'blend tickets',
337
357
  pattern: /\bblend(?:ing)?\s+tickets?\b/i,
338
358
  expansions: ['chemical blends', 'chemical-blends', 'report-chemical-blends', 'blend batches']
359
+ },
360
+ {
361
+ label: 'revenue',
362
+ pattern: /\b(revenue|sales|billing)\b/i,
363
+ expansions: ['invoice', 'invoices', 'paid invoices', 'invoice payments', 'billing', 'sales']
339
364
  }
340
365
  ];
341
366
  var AI_ASSISTANT_SYSTEM_PROMPT = [
@@ -372,6 +397,7 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
372
397
  '- Output plain Markdown (NO triple backticks).',
373
398
  '- When you reference database results, summarize first, then include a Markdown table.',
374
399
  '- When presenting record lists or aggregates, produce a Markdown table (pipes).',
400
+ '- Format currency as $1,234.56 (no USD prefix) with two decimals.',
375
401
  '- Never show raw JSON dumps.',
376
402
  '- Do not invent placeholders like "Not available" or "N/A". If a value is missing in tool results, leave the cell blank or omit the column and note it briefly.',
377
403
  '- Do not include `_id` & `__v` & `id_<other collection _id property>` in tables by default.',
@@ -406,6 +432,8 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
406
432
  '- If you need grouped/aggregated data (totals by user, rankings, trends), end your response with a single line exactly in this format:',
407
433
  '- MONGO_AGG: {"collection":"<name>","pipeline":[...],"options":{"allowDiskUse":true,"limit":20},"permissionView":"</route>"}',
408
434
  '- For invoice data, set permissionView to an invoice route (ex: /invoice/list or /report/invoice).',
435
+ '- For revenue/sales/billing questions, use invoices and sum paid_total (fallback to grand_total) with date_paid and Paid/Closed status when available.',
436
+ '- For relative date ranges (last/past/recent), include an upper bound <= $$NOW unless the user specifies a future end date.',
409
437
  '- Keep queries minimal, read-only, and avoid user/credential data unless the user is a super admin.',
410
438
  '- Assume you are not a super admin unless explicitly told otherwise.',
411
439
  '- Only request data when the user has permission for that module; invoice data requires invoice view access.',
@@ -1168,7 +1196,7 @@ function executeAiAssistantCodexRun(payload, context) {
1168
1196
  insertResult = _d.sent();
1169
1197
  assistantMessageId = (insertResult === null || insertResult === void 0 ? void 0 : insertResult._id) || (insertResult === null || insertResult === void 0 ? void 0 : insertResult.insertedId);
1170
1198
  enqueueAssistantCodexRun(function () { return __awaiter(_this, void 0, void 0, function () {
1171
- var runStart, steps, recordStep, progressTracker, streamProgress, assistantContent, toolResult, assistantDebug, directiveSource, dataQuestion, lastDirective, toolResponseDebug, toolError, termHints, collectionHints, collectionTokenization, collectionRanking, collectionSelection, collectionOverride, collectionNames, timingBreakdown, contextRoute, contextMode, hintSeed, termExpansion, hintText, baseTokens, expandedTokens, baseWeights, expandedWeights, dbName, db, _a, assistantContext, prompt_1, workspaceRoot, codexConfig, runOptions, responseText, directiveText, directive, directivePrompt, directiveStart, forcedDirective, _b, initialStart, directivePrompt, forcedStart, forcedDirective, _c, requestedCollection, cleanedResponseText, effectiveDirective, toolRequest, toolStart, toolResponse, _d, toolPayload, followupPrompt, followupStart, followupText, _e, error_2, error_3, finishedAt, finalNow, finishedAt, codexMs, draftingMs, finalMetadata, finalAssistantDoc;
1199
+ var runStart, steps, recordStep, progressTracker, streamProgress, assistantContent, toolResult, assistantDebug, directiveSource, dataQuestion, lastDirective, toolResponseDebug, toolError, termHints, collectionHints, fieldHints, collectionTokenization, collectionRanking, collectionSelection, collectionOverride, collectionNames, timingBreakdown, contextRoute, contextMode, hintSeed, termExpansion, hintText, baseTokens, expandedTokens, baseWeights, expandedWeights, dbName, db, _a, routeHints, rankedCollections, hintCollections, assistantContext, prompt_1, workspaceRoot, codexConfig, runOptions, responseText, directiveText, directive, directivePrompt, directiveStart, forcedDirective, _b, initialStart, directivePrompt, forcedStart, forcedDirective, _c, requestedCollection, cleanedResponseText, effectiveDirective, toolRequest, toolStart, toolResponse, _d, toolPayload, followupPrompt, followupStart, followupText, _e, error_2, error_3, finishedAt, finalNow, finishedAt, codexMs, draftingMs, finalMetadata, finalAssistantDoc;
1172
1200
  var _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
1173
1201
  return __generator(this, function (_u) {
1174
1202
  switch (_u.label) {
@@ -1206,6 +1234,7 @@ function executeAiAssistantCodexRun(payload, context) {
1206
1234
  toolError = null;
1207
1235
  termHints = [];
1208
1236
  collectionHints = [];
1237
+ fieldHints = [];
1209
1238
  collectionTokenization = null;
1210
1239
  collectionRanking = null;
1211
1240
  collectionSelection = null;
@@ -1263,18 +1292,30 @@ function executeAiAssistantCodexRun(payload, context) {
1263
1292
  : null);
1264
1293
  return [3 /*break*/, 5];
1265
1294
  case 5:
1295
+ routeHints = resolveCollectionHintsFromRoute(contextRoute, collectionNames);
1296
+ if (routeHints.length) {
1297
+ collectionHints = mergeAssistantHintValues(routeHints, collectionHints);
1298
+ }
1299
+ rankedCollections = Array.isArray(collectionRanking === null || collectionRanking === void 0 ? void 0 : collectionRanking.ranked)
1300
+ ? collectionRanking === null || collectionRanking === void 0 ? void 0 : collectionRanking.ranked.map(function (entry) { return entry.name; })
1301
+ : [];
1302
+ hintCollections = mergeAssistantHintValues(collectionHints, rankedCollections)
1303
+ .slice(0, AI_ASSISTANT_CONTEXT_MAX_COLLECTIONS);
1304
+ fieldHints = buildAssistantFieldHints(message, hintCollections);
1266
1305
  recordStep('Planning: collection hints', {
1267
1306
  contextRoute: contextRoute || undefined,
1268
1307
  contextMode: contextMode || undefined,
1269
1308
  collectionHints: collectionHints.length ? collectionHints : undefined,
1270
- termHints: termHints.length ? termHints : undefined
1309
+ termHints: termHints.length ? termHints : undefined,
1310
+ fieldHints: fieldHints.length ? fieldHints : undefined
1271
1311
  });
1272
1312
  assistantContext = buildAssistantContext(input, {
1273
1313
  isSuperAdmin: isSuperAdmin,
1274
1314
  hasInvoiceAccess: hasInvoiceAccess,
1275
1315
  customerId: customerId,
1276
1316
  collectionHints: collectionHints,
1277
- termHints: termHints
1317
+ termHints: termHints,
1318
+ fieldHints: fieldHints
1278
1319
  });
1279
1320
  prompt_1 = buildAssistantCodexPrompt(message, attachmentData.promptText, historyLines.join('\n'), assistantContext);
1280
1321
  return [4 /*yield*/, resolveAssistantWorkspaceRoot()];
@@ -1446,6 +1487,7 @@ function executeAiAssistantCodexRun(payload, context) {
1446
1487
  followupText = _u.sent();
1447
1488
  timingBreakdown.followupMs = Date.now() - followupStart;
1448
1489
  assistantContent = sanitizeAssistantResponse(followupText);
1490
+ assistantContent = applyAssistantDisplayTableToResponse(assistantContent, toolPayload.result.output.display);
1449
1491
  return [3 /*break*/, 25];
1450
1492
  case 24:
1451
1493
  _e = _u.sent();
@@ -1513,6 +1555,7 @@ function executeAiAssistantCodexRun(payload, context) {
1513
1555
  contextRoute: contextRoute || undefined,
1514
1556
  termHints: termHints.length ? termHints : undefined,
1515
1557
  collectionHints: collectionHints.length ? collectionHints : undefined,
1558
+ fieldHints: fieldHints.length ? fieldHints : undefined,
1516
1559
  collectionTokenization: collectionTokenization || undefined,
1517
1560
  collectionRanking: collectionRanking || undefined,
1518
1561
  collectionSelection: collectionSelection || undefined,
@@ -4402,6 +4445,136 @@ function trimDisplayTable(display, options) {
4402
4445
  }) : [];
4403
4446
  return __assign(__assign({}, display), { columns: columns, rows: rows, truncated: display.truncated || display.columns.length > maxColumns || rowsSource.length > maxRows });
4404
4447
  }
4448
+ var assistantCurrencyFormatter = null;
4449
+ var assistantNumberFormatter = null;
4450
+ var assistantPercentFormatter = null;
4451
+ function getAssistantCurrencyFormatter() {
4452
+ if (!assistantCurrencyFormatter) {
4453
+ assistantCurrencyFormatter = new Intl.NumberFormat(AI_ASSISTANT_LOCALE, {
4454
+ style: 'currency',
4455
+ currency: AI_ASSISTANT_CURRENCY_CODE,
4456
+ minimumFractionDigits: 2,
4457
+ maximumFractionDigits: 2
4458
+ });
4459
+ }
4460
+ return assistantCurrencyFormatter;
4461
+ }
4462
+ function getAssistantNumberFormatter() {
4463
+ if (!assistantNumberFormatter) {
4464
+ assistantNumberFormatter = new Intl.NumberFormat(AI_ASSISTANT_LOCALE, {
4465
+ minimumFractionDigits: 0,
4466
+ maximumFractionDigits: 2
4467
+ });
4468
+ }
4469
+ return assistantNumberFormatter;
4470
+ }
4471
+ function getAssistantPercentFormatter() {
4472
+ if (!assistantPercentFormatter) {
4473
+ assistantPercentFormatter = new Intl.NumberFormat(AI_ASSISTANT_LOCALE, {
4474
+ style: 'percent',
4475
+ minimumFractionDigits: 0,
4476
+ maximumFractionDigits: 2
4477
+ });
4478
+ }
4479
+ return assistantPercentFormatter;
4480
+ }
4481
+ function resolveAssistantNumericValue(value) {
4482
+ if (typeof value === 'number' && Number.isFinite(value)) {
4483
+ return value;
4484
+ }
4485
+ if (typeof value === 'string') {
4486
+ var cleaned = value.replace(/[^0-9.+-]/g, '');
4487
+ if (!cleaned) {
4488
+ return null;
4489
+ }
4490
+ var parsed = Number(cleaned);
4491
+ return Number.isFinite(parsed) ? parsed : null;
4492
+ }
4493
+ return null;
4494
+ }
4495
+ function isAssistantPercentColumn(column) {
4496
+ return /(percent|pct|percentage|ratio|rate)\b/.test(column);
4497
+ }
4498
+ function isAssistantCurrencyColumn(column) {
4499
+ return /(amount|total|price|cost|balance|fee|revenue|invoice|usd|tax|subtotal|paid|billing|sales)\b/.test(column);
4500
+ }
4501
+ function isAssistantDateColumn(column) {
4502
+ return /(date|time|created|updated|timestamp|at)\b/.test(column);
4503
+ }
4504
+ function isAssistantLikelyDateValue(value) {
4505
+ if (!value) {
4506
+ return false;
4507
+ }
4508
+ if (value instanceof Date) {
4509
+ return true;
4510
+ }
4511
+ if (typeof value === 'string') {
4512
+ if (!/[0-9]/.test(value)) {
4513
+ return false;
4514
+ }
4515
+ var parsed = Date.parse(value);
4516
+ return !Number.isNaN(parsed);
4517
+ }
4518
+ return false;
4519
+ }
4520
+ function formatAssistantDateValue(value) {
4521
+ var dateValue = value instanceof Date ? value : new Date(value);
4522
+ if (Number.isNaN(dateValue.getTime())) {
4523
+ return String(value);
4524
+ }
4525
+ var iso = dateValue.toISOString();
4526
+ var _a = __read(iso.split('T'), 2), datePart = _a[0], timePart = _a[1];
4527
+ if (!timePart) {
4528
+ return datePart || iso;
4529
+ }
4530
+ var trimmed = timePart.replace('Z', '');
4531
+ if (trimmed.startsWith('00:00:00')) {
4532
+ return datePart;
4533
+ }
4534
+ return "".concat(datePart, " ").concat(trimmed.slice(0, 5));
4535
+ }
4536
+ function formatAssistantDisplayCell(value, column) {
4537
+ if (value === null || value === undefined || value === '') {
4538
+ return '';
4539
+ }
4540
+ if (Array.isArray(value)) {
4541
+ return value.map(function (item) { return formatAssistantDisplayCell(item, column); }).filter(Boolean).join(', ');
4542
+ }
4543
+ var columnKey = String(column || '').toLowerCase();
4544
+ if (isAssistantDateColumn(columnKey) && isAssistantLikelyDateValue(value)) {
4545
+ return formatAssistantDateValue(value);
4546
+ }
4547
+ var numericValue = resolveAssistantNumericValue(value);
4548
+ if (numericValue !== null) {
4549
+ if (isAssistantPercentColumn(columnKey)) {
4550
+ var percentValue = numericValue > 1 && numericValue <= 100 ? numericValue / 100 : numericValue;
4551
+ try {
4552
+ return getAssistantPercentFormatter().format(percentValue);
4553
+ }
4554
+ catch (_a) {
4555
+ return "".concat((0, common_1.round)(percentValue * 100), "%");
4556
+ }
4557
+ }
4558
+ if (isAssistantCurrencyColumn(columnKey)) {
4559
+ try {
4560
+ return getAssistantCurrencyFormatter().format(numericValue);
4561
+ }
4562
+ catch (_b) {
4563
+ return "$".concat(numericValue.toFixed(2));
4564
+ }
4565
+ }
4566
+ try {
4567
+ return getAssistantNumberFormatter().format(numericValue);
4568
+ }
4569
+ catch (_c) {
4570
+ return String(numericValue);
4571
+ }
4572
+ }
4573
+ if (isAssistantLikelyDateValue(value) && isAssistantDateColumn(columnKey)) {
4574
+ return formatAssistantDateValue(value);
4575
+ }
4576
+ return String(value);
4577
+ }
4405
4578
  function formatDisplayTableMarkdown(display) {
4406
4579
  if (!display || !Array.isArray(display.columns) || !display.columns.length) {
4407
4580
  return '';
@@ -4409,11 +4582,38 @@ function formatDisplayTableMarkdown(display) {
4409
4582
  var header = "| ".concat(display.columns.join(' | '), " |");
4410
4583
  var separator = "| ".concat(display.columns.map(function () { return '---'; }).join(' | '), " |");
4411
4584
  var rows = (display.rows || []).map(function (row) {
4412
- var cells = display.columns.map(function (column) { return escapeMarkdownCell(row === null || row === void 0 ? void 0 : row[column]); });
4585
+ var cells = display.columns.map(function (column) {
4586
+ var rawValue = row === null || row === void 0 ? void 0 : row[column];
4587
+ var formatted = formatAssistantDisplayCell(rawValue, column);
4588
+ return escapeMarkdownCell(formatted);
4589
+ });
4413
4590
  return "| ".concat(cells.join(' | '), " |");
4414
4591
  });
4415
4592
  return __spreadArray([header, separator], __read(rows), false).join('\n').trim();
4416
4593
  }
4594
+ function stripAssistantMarkdownTables(value) {
4595
+ var raw = normalizeOptionalString(value);
4596
+ if (!raw) {
4597
+ return '';
4598
+ }
4599
+ var tablePattern = /(^|\n)\|[^\n]*\|\n\|[ \t:-|]+\|\n(?:\|[^\n]*\|\n?)*/g;
4600
+ var cleaned = raw.replace(tablePattern, '\n').trim();
4601
+ return cleaned;
4602
+ }
4603
+ function applyAssistantDisplayTableToResponse(value, display) {
4604
+ if (!display || !Array.isArray(display.rows) || !display.rows.length) {
4605
+ return value;
4606
+ }
4607
+ var table = formatDisplayTableMarkdown(display);
4608
+ if (!table) {
4609
+ return value;
4610
+ }
4611
+ var cleaned = stripAssistantMarkdownTables(value);
4612
+ if (!cleaned) {
4613
+ return table;
4614
+ }
4615
+ return "".concat(cleaned.trim(), "\n\n").concat(table).trim();
4616
+ }
4417
4617
  function escapeMarkdownCell(value) {
4418
4618
  var raw = value === null || value === undefined ? '' : String(value);
4419
4619
  return raw.replace(/\|/g, '\\|').replace(/\r?\n/g, ' ').trim();
@@ -6559,6 +6759,59 @@ function resolveCollectionHintsFromTokens(tokens, collectionNames, max) {
6559
6759
  .slice(0, Math.max(max, 0))
6560
6760
  .map(function (entry) { return entry.name; });
6561
6761
  }
6762
+ function mergeAssistantHintValues() {
6763
+ var groups = [];
6764
+ for (var _i = 0; _i < arguments.length; _i++) {
6765
+ groups[_i] = arguments[_i];
6766
+ }
6767
+ var result = [];
6768
+ var seen = new Set();
6769
+ groups.forEach(function (group) {
6770
+ if (!Array.isArray(group)) {
6771
+ return;
6772
+ }
6773
+ group.forEach(function (value) {
6774
+ var normalized = normalizeOptionalString(value);
6775
+ if (!normalized || seen.has(normalized)) {
6776
+ return;
6777
+ }
6778
+ seen.add(normalized);
6779
+ result.push(normalized);
6780
+ });
6781
+ });
6782
+ return result;
6783
+ }
6784
+ function resolveCollectionHintsFromRoute(route, collectionNames) {
6785
+ var normalizedRoute = normalizeOptionalString(route);
6786
+ if (!normalizedRoute || !collectionNames.length) {
6787
+ return [];
6788
+ }
6789
+ var parts = normalizedRoute.toLowerCase().split(/[/?#]+/g).filter(Boolean);
6790
+ var hints = [];
6791
+ parts.forEach(function (part) {
6792
+ var cleaned = part.replace(/[^a-z0-9_-]/g, '');
6793
+ if (!cleaned) {
6794
+ return;
6795
+ }
6796
+ var base = cleaned.replace(/^-+|-+$/g, '');
6797
+ if (!base) {
6798
+ return;
6799
+ }
6800
+ if (collectionNames.includes(base)) {
6801
+ hints.push(base);
6802
+ }
6803
+ if (!base.endsWith('s') && collectionNames.includes("".concat(base, "s"))) {
6804
+ hints.push("".concat(base, "s"));
6805
+ }
6806
+ if (collectionNames.includes("report-".concat(base))) {
6807
+ hints.push("report-".concat(base));
6808
+ }
6809
+ if (!base.endsWith('s') && collectionNames.includes("report-".concat(base, "s"))) {
6810
+ hints.push("report-".concat(base, "s"));
6811
+ }
6812
+ });
6813
+ return mergeAssistantHintValues(hints);
6814
+ }
6562
6815
  function buildCollectionRankingDebugFromTokens(tokens, collectionNames, max) {
6563
6816
  if (max === void 0) { max = 8; }
6564
6817
  var requestedKey = normalizeCollectionKey(tokens.join('-'));
@@ -7682,31 +7935,164 @@ function pathExists(target) {
7682
7935
  });
7683
7936
  });
7684
7937
  }
7685
- function resolveAssistantWorkspaceRoot() {
7938
+ function directoryExists(target) {
7939
+ return __awaiter(this, void 0, void 0, function () {
7940
+ var stat, _a;
7941
+ return __generator(this, function (_b) {
7942
+ switch (_b.label) {
7943
+ case 0:
7944
+ _b.trys.push([0, 2, , 3]);
7945
+ return [4 /*yield*/, fs_1.promises.stat(target)];
7946
+ case 1:
7947
+ stat = _b.sent();
7948
+ return [2 /*return*/, stat.isDirectory()];
7949
+ case 2:
7950
+ _a = _b.sent();
7951
+ return [2 /*return*/, false];
7952
+ case 3: return [2 /*return*/];
7953
+ }
7954
+ });
7955
+ });
7956
+ }
7957
+ function isAssistantPathUnderRoot(target, root) {
7958
+ if (!target || !root) {
7959
+ return false;
7960
+ }
7961
+ var relative = path.relative(root, target);
7962
+ if (!relative) {
7963
+ return true;
7964
+ }
7965
+ return !relative.startsWith('..') && !path.isAbsolute(relative);
7966
+ }
7967
+ function isAssistantProjectRoot(candidate) {
7686
7968
  return __awaiter(this, void 0, void 0, function () {
7687
- var config, configured, candidate, stat, _a;
7969
+ var serverPath, angularPath, _a, hasServer, hasPackage;
7688
7970
  return __generator(this, function (_b) {
7689
7971
  switch (_b.label) {
7972
+ case 0:
7973
+ if (!candidate) {
7974
+ return [2 /*return*/, false];
7975
+ }
7976
+ return [4 /*yield*/, directoryExists(candidate)];
7977
+ case 1:
7978
+ if (!(_b.sent())) {
7979
+ return [2 /*return*/, false];
7980
+ }
7981
+ serverPath = path.join(candidate, 'server');
7982
+ angularPath = path.join(candidate, 'angular');
7983
+ return [4 /*yield*/, directoryExists(serverPath)];
7984
+ case 2:
7985
+ _a = (_b.sent());
7986
+ if (!_a) return [3 /*break*/, 4];
7987
+ return [4 /*yield*/, directoryExists(angularPath)];
7988
+ case 3:
7989
+ _a = (_b.sent());
7990
+ _b.label = 4;
7991
+ case 4:
7992
+ if (_a) {
7993
+ return [2 /*return*/, true];
7994
+ }
7995
+ return [4 /*yield*/, directoryExists(serverPath)];
7996
+ case 5:
7997
+ hasServer = _b.sent();
7998
+ return [4 /*yield*/, pathExists(path.join(candidate, 'package.json'))];
7999
+ case 6:
8000
+ hasPackage = _b.sent();
8001
+ return [2 /*return*/, hasServer && hasPackage];
8002
+ }
8003
+ });
8004
+ });
8005
+ }
8006
+ function resolveAssistantProjectRoot(clientDir) {
8007
+ return __awaiter(this, void 0, void 0, function () {
8008
+ var normalized, resolved, root, parts, i, candidate;
8009
+ return __generator(this, function (_a) {
8010
+ switch (_a.label) {
8011
+ case 0:
8012
+ normalized = normalizeOptionalString(clientDir);
8013
+ if (!normalized) {
8014
+ return [2 /*return*/, ''];
8015
+ }
8016
+ resolved = path.resolve(normalized);
8017
+ root = path.parse(resolved).root || path.sep;
8018
+ parts = resolved.slice(root.length).split(path.sep).filter(Boolean);
8019
+ if (!parts.length) {
8020
+ return [2 /*return*/, ''];
8021
+ }
8022
+ i = parts.length;
8023
+ _a.label = 1;
8024
+ case 1:
8025
+ if (!(i >= 1)) return [3 /*break*/, 4];
8026
+ candidate = path.join.apply(path, __spreadArray([root], __read(parts.slice(0, i)), false));
8027
+ return [4 /*yield*/, isAssistantProjectRoot(candidate)];
8028
+ case 2:
8029
+ if (_a.sent()) {
8030
+ return [2 /*return*/, candidate];
8031
+ }
8032
+ _a.label = 3;
8033
+ case 3:
8034
+ i -= 1;
8035
+ return [3 /*break*/, 1];
8036
+ case 4: return [2 /*return*/, ''];
8037
+ }
8038
+ });
8039
+ });
8040
+ }
8041
+ function resolveAssistantWorkspaceRoot() {
8042
+ return __awaiter(this, void 0, void 0, function () {
8043
+ var config, configured, configuredRoot, clientDir, resolvedClientDir, projectRoot, _a, candidate, stat, _b;
8044
+ return __generator(this, function (_c) {
8045
+ switch (_c.label) {
7690
8046
  case 0:
7691
8047
  config = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
7692
8048
  configured = normalizeOptionalString(config['AI_ASSISTANT_WORKSPACE_ROOT']
7693
8049
  || process.env.AI_ASSISTANT_WORKSPACE_ROOT
7694
8050
  || config['CODEX_WORKSPACE_ROOT']
7695
8051
  || process.env.CODEX_WORKSPACE_ROOT);
7696
- candidate = configured || resolveio_server_app_1.ResolveIOServer.getClientDir() || process.cwd();
7697
- stat = null;
7698
- _b.label = 1;
8052
+ configuredRoot = configured ? path.resolve(configured) : '';
8053
+ clientDir = normalizeOptionalString(resolveio_server_app_1.ResolveIOServer.getClientDir());
8054
+ resolvedClientDir = clientDir ? path.resolve(clientDir) : '';
8055
+ if (!resolvedClientDir) return [3 /*break*/, 2];
8056
+ return [4 /*yield*/, resolveAssistantProjectRoot(resolvedClientDir)];
7699
8057
  case 1:
7700
- _b.trys.push([1, 3, , 4]);
7701
- return [4 /*yield*/, fs_1.promises.stat(candidate)];
8058
+ _a = _c.sent();
8059
+ return [3 /*break*/, 3];
7702
8060
  case 2:
7703
- stat = _b.sent();
7704
- return [3 /*break*/, 4];
8061
+ _a = '';
8062
+ _c.label = 3;
7705
8063
  case 3:
7706
- _a = _b.sent();
8064
+ projectRoot = _a;
8065
+ candidate = configuredRoot || projectRoot || resolvedClientDir || process.cwd();
8066
+ if (configuredRoot) {
8067
+ if (projectRoot && isAssistantPathUnderRoot(projectRoot, configuredRoot)) {
8068
+ candidate = projectRoot;
8069
+ }
8070
+ else if (projectRoot && isAssistantPathUnderRoot(configuredRoot, projectRoot)) {
8071
+ candidate = configuredRoot;
8072
+ }
8073
+ else {
8074
+ candidate = configuredRoot;
8075
+ }
8076
+ }
8077
+ else if (projectRoot) {
8078
+ candidate = projectRoot;
8079
+ }
8080
+ else if (resolvedClientDir) {
8081
+ candidate = resolvedClientDir;
8082
+ }
7707
8083
  stat = null;
7708
- return [3 /*break*/, 4];
8084
+ _c.label = 4;
7709
8085
  case 4:
8086
+ _c.trys.push([4, 6, , 7]);
8087
+ return [4 /*yield*/, fs_1.promises.stat(candidate)];
8088
+ case 5:
8089
+ stat = _c.sent();
8090
+ return [3 /*break*/, 7];
8091
+ case 6:
8092
+ _b = _c.sent();
8093
+ stat = null;
8094
+ return [3 /*break*/, 7];
8095
+ case 7:
7710
8096
  if (!stat || !stat.isDirectory()) {
7711
8097
  throw new Error('AI assistant workspace root not found.');
7712
8098
  }
@@ -7797,6 +8183,14 @@ function buildAssistantContext(input, userContext) {
7797
8183
  if (termHints.length) {
7798
8184
  lines.push("Term hints: ".concat(termHints.join('; ')));
7799
8185
  }
8186
+ var fieldHints = Array.isArray(userContext === null || userContext === void 0 ? void 0 : userContext.fieldHints)
8187
+ ? userContext.fieldHints.filter(Boolean)
8188
+ : [];
8189
+ if (fieldHints.length) {
8190
+ fieldHints.forEach(function (hint) {
8191
+ lines.push(hint);
8192
+ });
8193
+ }
7800
8194
  var mongoDb = normalizeOptionalString((_c = input === null || input === void 0 ? void 0 : input.mongo) === null || _c === void 0 ? void 0 : _c.database);
7801
8195
  var mongoDbs = Array.isArray((_d = input === null || input === void 0 ? void 0 : input.mongo) === null || _d === void 0 ? void 0 : _d.databases)
7802
8196
  ? input.mongo.databases.map(function (value) { return normalizeOptionalString(value); }).filter(Boolean)
@@ -7814,6 +8208,85 @@ function buildAssistantContext(input, userContext) {
7814
8208
  }
7815
8209
  return lines.join('\n');
7816
8210
  }
8211
+ function buildAssistantFieldHints(message, collectionNames, options) {
8212
+ var normalizedMessage = normalizeOptionalString(message);
8213
+ if (!normalizedMessage || !Array.isArray(collectionNames) || !collectionNames.length) {
8214
+ return [];
8215
+ }
8216
+ var maxCollections = typeof (options === null || options === void 0 ? void 0 : options.maxCollections) === 'number'
8217
+ ? Math.max(options.maxCollections, 0)
8218
+ : AI_ASSISTANT_CONTEXT_MAX_COLLECTIONS;
8219
+ var maxFields = typeof (options === null || options === void 0 ? void 0 : options.maxFields) === 'number'
8220
+ ? Math.max(options.maxFields, 0)
8221
+ : AI_ASSISTANT_CONTEXT_MAX_FIELDS_PER_COLLECTION;
8222
+ if (!maxCollections || !maxFields) {
8223
+ return [];
8224
+ }
8225
+ var baseTokens = tokenizeFieldKey(normalizedMessage);
8226
+ var expandedTokens = expandFieldTokens(baseTokens);
8227
+ var tokenSet = new Set(expandedTokens);
8228
+ [
8229
+ 'date',
8230
+ 'status',
8231
+ 'total',
8232
+ 'amount',
8233
+ 'paid',
8234
+ 'balance',
8235
+ 'grand',
8236
+ 'subtotal',
8237
+ 'invoice',
8238
+ 'revenue',
8239
+ 'sales',
8240
+ 'billing',
8241
+ 'payment'
8242
+ ].forEach(function (token) { return tokenSet.add(token); });
8243
+ var hints = [];
8244
+ var candidates = collectionNames.filter(Boolean).slice(0, maxCollections);
8245
+ candidates.forEach(function (collection) {
8246
+ var fields = getCollectionSchemaFieldNames(collection);
8247
+ if (!fields.length) {
8248
+ return;
8249
+ }
8250
+ var scored = fields
8251
+ .filter(function (field) { return field && !shouldRedactField(field); })
8252
+ .map(function (field) {
8253
+ var tokens = tokenizeFieldKey(field);
8254
+ if (!tokens.length) {
8255
+ return null;
8256
+ }
8257
+ var score = tokens.filter(function (token) { return tokenSet.has(token); }).length;
8258
+ var hasDate = tokens.includes('date');
8259
+ var hasStatus = tokens.includes('status');
8260
+ var hasTotal = tokens.includes('total') || tokens.includes('amount') || tokens.includes('paid') || tokens.includes('balance');
8261
+ if (score === 0 && (hasDate || hasStatus || hasTotal)) {
8262
+ score = 1;
8263
+ }
8264
+ if (score <= 0) {
8265
+ return null;
8266
+ }
8267
+ if (isAssistantIdField(field)) {
8268
+ score -= 1;
8269
+ }
8270
+ if (field.includes('.$.') && score < 2) {
8271
+ return null;
8272
+ }
8273
+ return { field: field, score: score };
8274
+ })
8275
+ .filter(function (entry) { return !!entry; })
8276
+ .sort(function (a, b) {
8277
+ if (a.score !== b.score) {
8278
+ return b.score - a.score;
8279
+ }
8280
+ return a.field.localeCompare(b.field);
8281
+ })
8282
+ .slice(0, maxFields)
8283
+ .map(function (entry) { return entry.field; });
8284
+ if (scored.length) {
8285
+ hints.push("Field hints (".concat(collection, "): ").concat(scored.join(', ')));
8286
+ }
8287
+ });
8288
+ return hints;
8289
+ }
7817
8290
  var cachedClientRouteIndex = null;
7818
8291
  function normalizeRouteKey(value) {
7819
8292
  var trimmed = normalizeOptionalString(value);
@@ -7988,7 +8461,11 @@ function sanitizeAssistantResponse(value) {
7988
8461
  if (!cleaned) {
7989
8462
  return 'I can’t share code, but I can point you to files or explain behavior at a high level.';
7990
8463
  }
7991
- var normalizedCurrency = cleaned.replace(/\bUSD\s*([0-9][0-9,]*(?:\.[0-9]+)?)/g, function (_match, amount) {
8464
+ var normalizedCurrency = cleaned
8465
+ .replace(/\bUSD(?:\s|&nbsp;|&#160;|[\u00A0\u202F\u2007])*\$?\s*([-+]?[0-9][0-9,]*(?:\.[0-9]+)?)/g, function (_match, amount) {
8466
+ return "$".concat(amount);
8467
+ })
8468
+ .replace(/\bUS\$\s*([-+]?[0-9][0-9,]*(?:\.[0-9]+)?)/g, function (_match, amount) {
7992
8469
  return "$".concat(amount);
7993
8470
  });
7994
8471
  return normalizeAssistantRoutes(normalizedCurrency);