@resolveio/server-lib 20.14.4 → 20.14.6

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.
@@ -95,6 +95,7 @@ var common_1 = require("../util/common");
95
95
  var report_builder_unwinds_1 = require("../util/report-builder-unwinds");
96
96
  var schema_report_builder_1 = require("../util/schema-report-builder");
97
97
  var tokenizer_1 = require("../util/tokenizer");
98
+ var ai_terminal_1 = require("./ai-terminal");
98
99
  var DEFAULT_REPORT_BUILDER_CODEX_MODEL = 'gpt-5.2-codex';
99
100
  var DEFAULT_REPORT_BUILDER_CODEX_TIMEOUT_MS = 60000;
100
101
  var reportBuilderCodexClient = null;
@@ -1663,7 +1664,7 @@ function expandLayoutColumnFilters(filters, selectedFields) {
1663
1664
  }
1664
1665
  function executeReportBuilderAi(payload, context) {
1665
1666
  return __awaiter(this, void 0, void 0, function () {
1666
- var input, prompt, isSuperAdmin, guardrailsEnabled, guardrail, reportType, collectionRoot, collectionJoins, collections, resolvedFields, fieldLimit, trimmedFields, arraySummary, systemPrompt, userPrompt, codexModel, codexClient, codexPrompt, runOptions, responseText, usage, parsed, sanitizeContext, patch, summaryNotes, idClient;
1667
+ var input, prompt, isSuperAdmin, guardrailsEnabled, guardrail, reportType, collectionRoot, collectionJoins, collections, fieldLimit, resolvedFields, usedFallbackFields, derivedFields, trimmedFields, arraySummary, systemPrompt, userPrompt, codexModel, codexClient, runOptions, responseText, usage, parsed, allowMongoRead, mongoPayload, mongoReadResult, error_1, message, mongoContext, usage2, sanitizeContext, patch, summaryNotes, idClient;
1667
1668
  return __generator(this, function (_a) {
1668
1669
  switch (_a.label) {
1669
1670
  case 0:
@@ -1686,9 +1687,15 @@ function executeReportBuilderAi(payload, context) {
1686
1687
  collectionRoot = normalizeOptionalString(input.collection_root);
1687
1688
  collectionJoins = Array.isArray(input.collection_joins) ? input.collection_joins : [];
1688
1689
  collections = sanitizeCollections(input.available_collections || []);
1689
- resolvedFields = sanitizeFields(input.available_fields || []);
1690
1690
  fieldLimit = normalizeFieldLimit(input.field_limit);
1691
- trimmedFields = trimFieldsForPrompt(resolvedFields, fieldLimit);
1691
+ resolvedFields = sanitizeFields(input.available_fields || []);
1692
+ usedFallbackFields = false;
1693
+ if (!resolvedFields.length && collections.length) {
1694
+ derivedFields = deriveFieldsFromCollections(collections);
1695
+ resolvedFields = sanitizeFields(derivedFields);
1696
+ usedFallbackFields = resolvedFields.length > 0;
1697
+ }
1698
+ trimmedFields = trimFieldsForPrompt(resolvedFields, fieldLimit, usedFallbackFields);
1692
1699
  arraySummary = buildArrayFieldSummary(trimmedFields.fields);
1693
1700
  systemPrompt = buildReportBuilderSystemPrompt(reportType);
1694
1701
  userPrompt = buildReportBuilderUserPrompt({
@@ -1710,12 +1717,11 @@ function executeReportBuilderAi(payload, context) {
1710
1717
  });
1711
1718
  codexModel = resolveReportBuilderCodexModel(input.config);
1712
1719
  codexClient = getReportBuilderCodexClient();
1713
- codexPrompt = buildReportBuilderCodexPrompt(systemPrompt, userPrompt);
1714
1720
  runOptions = {
1715
1721
  timeoutMs: resolveReportBuilderCodexTimeoutMs(input.config),
1716
1722
  threadOptions: resolveReportBuilderCodexThreadOptions(input.config, codexModel)
1717
1723
  };
1718
- return [4 /*yield*/, codexClient.run(codexPrompt, runOptions)];
1724
+ return [4 /*yield*/, codexClient.run(buildReportBuilderCodexPrompt(systemPrompt, userPrompt), runOptions)];
1719
1725
  case 2:
1720
1726
  responseText = _a.sent();
1721
1727
  usage = estimateUsage([
@@ -1723,6 +1729,58 @@ function executeReportBuilderAi(payload, context) {
1723
1729
  { role: 'user', content: userPrompt }
1724
1730
  ], responseText, codexModel);
1725
1731
  parsed = safeJsonParse(responseText);
1732
+ allowMongoRead = input.allow_mongo_read === true;
1733
+ if (!(allowMongoRead && parsed && typeof parsed === 'object' && parsed.mongo_read)) return [3 /*break*/, 8];
1734
+ mongoPayload = resolveMongoReadPayload(parsed.mongo_read, input, collections);
1735
+ if (!mongoPayload) return [3 /*break*/, 8];
1736
+ mongoReadResult = null;
1737
+ _a.label = 3;
1738
+ case 3:
1739
+ _a.trys.push([3, 5, , 6]);
1740
+ return [4 /*yield*/, (0, ai_terminal_1.executeAiAssistantMongoRead)(mongoPayload, context)];
1741
+ case 4:
1742
+ mongoReadResult = _a.sent();
1743
+ return [3 /*break*/, 6];
1744
+ case 5:
1745
+ error_1 = _a.sent();
1746
+ message = error_1 instanceof Error ? error_1.message : String(error_1 || 'Mongo read failed.');
1747
+ mongoReadResult = { error: message };
1748
+ return [3 /*break*/, 6];
1749
+ case 6:
1750
+ mongoContext = buildMongoReadContext(mongoPayload, mongoReadResult);
1751
+ userPrompt = buildReportBuilderUserPrompt({
1752
+ prompt: prompt,
1753
+ reportType: reportType,
1754
+ reportName: normalizeOptionalString(input.report_name),
1755
+ collectionRoot: collectionRoot,
1756
+ collectionJoins: collectionJoins,
1757
+ idDateField: normalizeOptionalString(input.id_date_field),
1758
+ dateInterval: normalizeOptionalString(input.date_interval),
1759
+ collections: collections,
1760
+ fields: trimmedFields.fields,
1761
+ fieldMeta: {
1762
+ truncated: trimmedFields.truncated,
1763
+ total: trimmedFields.total,
1764
+ used: trimmedFields.fields.length
1765
+ },
1766
+ arraySummary: arraySummary,
1767
+ mongoReadResult: mongoContext
1768
+ });
1769
+ return [4 /*yield*/, codexClient.run(buildReportBuilderCodexPrompt(systemPrompt, userPrompt), runOptions)];
1770
+ case 7:
1771
+ responseText = _a.sent();
1772
+ usage2 = estimateUsage([
1773
+ { role: 'system', content: systemPrompt },
1774
+ { role: 'user', content: userPrompt }
1775
+ ], responseText, codexModel);
1776
+ usage = {
1777
+ inputTokens: usage.inputTokens + usage2.inputTokens,
1778
+ outputTokens: usage.outputTokens + usage2.outputTokens,
1779
+ totalTokens: usage.totalTokens + usage2.totalTokens
1780
+ };
1781
+ parsed = safeJsonParse(responseText);
1782
+ _a.label = 8;
1783
+ case 8:
1726
1784
  if (!parsed || typeof parsed !== 'object') {
1727
1785
  throw new Error('AI response was not valid JSON.');
1728
1786
  }
@@ -1735,9 +1793,9 @@ function executeReportBuilderAi(payload, context) {
1735
1793
  usedFields: trimmedFields.fields.length
1736
1794
  });
1737
1795
  return [4 /*yield*/, resolveClientId(input.id_client, context === null || context === void 0 ? void 0 : context.id_user)];
1738
- case 3:
1796
+ case 9:
1739
1797
  idClient = _a.sent();
1740
- if (!idClient) return [3 /*break*/, 5];
1798
+ if (!idClient) return [3 /*break*/, 11];
1741
1799
  return [4 /*yield*/, (0, openai_usage_ledger_manager_1.recordOpenAIUsage)({
1742
1800
  id_client: idClient,
1743
1801
  model: codexModel || 'unknown',
@@ -1747,10 +1805,10 @@ function executeReportBuilderAi(payload, context) {
1747
1805
  category: 'report-builder-ai',
1748
1806
  id_request: ''
1749
1807
  })];
1750
- case 4:
1808
+ case 10:
1751
1809
  _a.sent();
1752
- _a.label = 5;
1753
- case 5: return [2 /*return*/, {
1810
+ _a.label = 11;
1811
+ case 11: return [2 /*return*/, {
1754
1812
  patch: patch,
1755
1813
  notes: summaryNotes,
1756
1814
  usage: {
@@ -1783,7 +1841,15 @@ function buildReportBuilderSystemPrompt(reportType) {
1783
1841
  '- notes (string)',
1784
1842
  'Use only provided collections and field_path values (they reflect the current models/collections).',
1785
1843
  'Always set collection_root to the best matching collection from available_collections.',
1844
+ 'When available_fields spans multiple collections, choose collection_root first and then select fields whose collection_name matches collection_root or a join alias.',
1786
1845
  'Prefer exact field_path values from available_fields; never invent field paths.',
1846
+ 'Translate the request into concrete layout_columns, groups_row, sort, totals, filters, and links whenever possible.',
1847
+ 'Every layout column must be complete: include a header plus at least one mapping with a valid field_path (or text mode with non-empty text). Do not return columns without mapped fields.',
1848
+ 'Unless the user explicitly asks for a different order, add sort entries for the primary grouping/identifier fields (groups_row or the first key layout_columns) using ascending order so results are alphabetical.',
1849
+ 'If the request specifies a time grain (daily/weekly/monthly/etc), set date_interval and id_date_field to the best matching date field.',
1850
+ 'If you need sample records to confirm field choices, you may return JSON with {"mongo_read": { "collection": "<collection>", "query": {}, "options": {"limit": 5} }, "notes": "why you need data"} .',
1851
+ 'Only request mongo_read when necessary and only use collections from available_collections.',
1852
+ 'When mongo_read_result is present in the user context, use it to finalize the patch and do not request another read.',
1787
1853
  'Use field_type to guide filters/totals (numeric fields for totals, date fields for date_interval, boolean fields for boolean_value).',
1788
1854
  'If field_summary.truncated is true, only use provided fields and note missing needs in notes.',
1789
1855
  'If you need joined data, add collection_joins with an alias and use the alias in field_path.',
@@ -1811,6 +1877,7 @@ function buildReportBuilderUserPrompt(input) {
1811
1877
  available_fields: input.fields || [],
1812
1878
  field_summary: input.fieldMeta || undefined,
1813
1879
  array_summary: input.arraySummary || [],
1880
+ mongo_read_result: input.mongoReadResult || undefined,
1814
1881
  request: input.prompt
1815
1882
  };
1816
1883
  return JSON.stringify(context);
@@ -1946,13 +2013,224 @@ function sanitizeFields(raw) {
1946
2013
  });
1947
2014
  return out;
1948
2015
  }
1949
- function trimFieldsForPrompt(fields, limit) {
2016
+ function resolveCollectionNameFromList(value, collections) {
2017
+ var e_1, _a;
2018
+ var key = normalizeKey(value);
2019
+ if (!key) {
2020
+ return '';
2021
+ }
2022
+ try {
2023
+ for (var _b = __values(collections || []), _c = _b.next(); !_c.done; _c = _b.next()) {
2024
+ var col = _c.value;
2025
+ if (normalizeKey(col.collection) === key || normalizeKey(col.name) === key) {
2026
+ return col.collection;
2027
+ }
2028
+ }
2029
+ }
2030
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
2031
+ finally {
2032
+ try {
2033
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
2034
+ }
2035
+ finally { if (e_1) throw e_1.error; }
2036
+ }
2037
+ return '';
2038
+ }
2039
+ function resolveMongoReadPayload(raw, input, collections) {
2040
+ if (!raw || typeof raw !== 'object') {
2041
+ return null;
2042
+ }
2043
+ var collection = resolveCollectionNameFromList(raw.collection || raw.collection_name || raw.collectionName, collections);
2044
+ if (!collection) {
2045
+ return null;
2046
+ }
2047
+ var query = raw.query && typeof raw.query === 'object' && !Array.isArray(raw.query) ? raw.query : {};
2048
+ var optionsRaw = raw.options && typeof raw.options === 'object' ? raw.options : {};
2049
+ var limitRaw = normalizeOptionalNumber(optionsRaw.limit);
2050
+ var skipRaw = normalizeOptionalNumber(optionsRaw.skip);
2051
+ var permissionView = normalizeOptionalString(input.permission_view || input.permissionView) || '/report-builder';
2052
+ var request = {
2053
+ collection: collection,
2054
+ database: normalizeOptionalString(raw.database) || undefined,
2055
+ query: query,
2056
+ options: {
2057
+ projection: optionsRaw.projection && typeof optionsRaw.projection === 'object' ? optionsRaw.projection : undefined,
2058
+ sort: optionsRaw.sort && typeof optionsRaw.sort === 'object' ? optionsRaw.sort : undefined,
2059
+ limit: limitRaw ? Math.min(Math.max((0, common_1.round)(limitRaw), 1), 10) : 5,
2060
+ skip: skipRaw ? Math.max((0, common_1.round)(skipRaw), 0) : undefined,
2061
+ includeTotal: optionsRaw.includeTotal === true
2062
+ },
2063
+ permissionView: permissionView || undefined,
2064
+ id_client: normalizeOptionalString(input.id_client) || undefined,
2065
+ mongo: input.mongo
2066
+ };
2067
+ return request;
2068
+ }
2069
+ function buildMongoReadContext(request, response) {
2070
+ var documents = Array.isArray(response === null || response === void 0 ? void 0 : response.documents) ? response.documents : [];
2071
+ var total = typeof (response === null || response === void 0 ? void 0 : response.total) === 'number' ? response.total : null;
2072
+ var sample = summarizeMongoDocuments(documents);
2073
+ return {
2074
+ request: {
2075
+ collection: request.collection,
2076
+ query: request.query || {},
2077
+ options: request.options || {}
2078
+ },
2079
+ result: {
2080
+ total: total,
2081
+ sample: sample,
2082
+ fields: extractMongoSampleFields(sample)
2083
+ }
2084
+ };
2085
+ }
2086
+ function summarizeMongoDocuments(documents) {
2087
+ var maxDocs = 5;
2088
+ return (documents || []).slice(0, maxDocs).map(function (doc) { return pruneMongoValue(doc, 0); });
2089
+ }
2090
+ function pruneMongoValue(value, depth) {
2091
+ var maxDepth = 3;
2092
+ var maxKeys = 30;
2093
+ var maxArray = 8;
2094
+ var maxString = 200;
2095
+ if (depth >= maxDepth) {
2096
+ return '[Truncated]';
2097
+ }
2098
+ if (Array.isArray(value)) {
2099
+ return value.slice(0, maxArray).map(function (item) { return pruneMongoValue(item, depth + 1); });
2100
+ }
2101
+ if (value && typeof value === 'object') {
2102
+ var keys = Object.keys(value);
2103
+ var trimmedKeys = keys.slice(0, maxKeys);
2104
+ var result_1 = {};
2105
+ trimmedKeys.forEach(function (key) {
2106
+ result_1[key] = pruneMongoValue(value[key], depth + 1);
2107
+ });
2108
+ if (keys.length > trimmedKeys.length) {
2109
+ result_1.__truncated__ = true;
2110
+ }
2111
+ return result_1;
2112
+ }
2113
+ if (typeof value === 'string' && value.length > maxString) {
2114
+ return value.slice(0, maxString) + '...';
2115
+ }
2116
+ return value;
2117
+ }
2118
+ function extractMongoSampleFields(samples) {
2119
+ var fields = new Set();
2120
+ (samples || []).forEach(function (sample) {
2121
+ if (!sample || typeof sample !== 'object') {
2122
+ return;
2123
+ }
2124
+ Object.keys(sample).forEach(function (key) { return fields.add(key); });
2125
+ });
2126
+ return Array.from(fields);
2127
+ }
2128
+ function collectTreeLeaves(items, res) {
2129
+ (items || []).forEach(function (item) {
2130
+ var _a;
2131
+ if (item === null || item === void 0 ? void 0 : item.isLeaf) {
2132
+ res.push(item);
2133
+ return;
2134
+ }
2135
+ if ((_a = item === null || item === void 0 ? void 0 : item.children) === null || _a === void 0 ? void 0 : _a.length) {
2136
+ collectTreeLeaves(item.children, res);
2137
+ }
2138
+ });
2139
+ }
2140
+ function deriveFieldsFromCollections(collections) {
2141
+ var fields = [];
2142
+ var seen = new Set();
2143
+ (collections || []).forEach(function (col) {
2144
+ var collectionName = normalizeOptionalString(col === null || col === void 0 ? void 0 : col.collection);
2145
+ if (!collectionName) {
2146
+ return;
2147
+ }
2148
+ var tree;
2149
+ try {
2150
+ tree = buildCollectionTree(collectionName);
2151
+ }
2152
+ catch (error) {
2153
+ var message = error instanceof Error ? error.message : String(error || 'Unknown error');
2154
+ console.warn('RB AI field derivation failed', { collection: collectionName, error: message });
2155
+ return;
2156
+ }
2157
+ var leaves = [];
2158
+ collectTreeLeaves((tree === null || tree === void 0 ? void 0 : tree.children) || [], leaves);
2159
+ leaves.forEach(function (leaf) {
2160
+ var fieldPath = normalizeOptionalString(leaf === null || leaf === void 0 ? void 0 : leaf.fieldPath);
2161
+ if (!fieldPath) {
2162
+ return;
2163
+ }
2164
+ var key = fieldPath.toLowerCase();
2165
+ if (seen.has(key)) {
2166
+ return;
2167
+ }
2168
+ seen.add(key);
2169
+ fields.push({
2170
+ field_path: fieldPath,
2171
+ field_path_name: normalizeOptionalString(leaf === null || leaf === void 0 ? void 0 : leaf.fieldPathName) || fieldPath,
2172
+ field_type: normalizeOptionalString(leaf === null || leaf === void 0 ? void 0 : leaf.fieldType) || 'String',
2173
+ collection_name: normalizeOptionalString((leaf === null || leaf === void 0 ? void 0 : leaf.collection_name) || (leaf === null || leaf === void 0 ? void 0 : leaf.collection)) || collectionName
2174
+ });
2175
+ });
2176
+ });
2177
+ return fields;
2178
+ }
2179
+ function trimFieldsForPrompt(fields, limit, balanceByCollection) {
2180
+ var e_2, _a;
2181
+ if (balanceByCollection === void 0) { balanceByCollection = false; }
1950
2182
  if (!fields || !fields.length) {
1951
2183
  return { fields: [], truncated: false, total: 0 };
1952
2184
  }
1953
2185
  if (fields.length <= limit) {
1954
2186
  return { fields: fields, truncated: false, total: fields.length };
1955
2187
  }
2188
+ if (balanceByCollection) {
2189
+ var buckets_1 = new Map();
2190
+ var order_1 = [];
2191
+ fields.forEach(function (field) {
2192
+ var _a;
2193
+ var key = normalizeOptionalString(field === null || field === void 0 ? void 0 : field.collection_name) || 'unknown';
2194
+ if (!buckets_1.has(key)) {
2195
+ buckets_1.set(key, []);
2196
+ order_1.push(key);
2197
+ }
2198
+ (_a = buckets_1.get(key)) === null || _a === void 0 ? void 0 : _a.push(field);
2199
+ });
2200
+ if (buckets_1.size > 1) {
2201
+ var trackers = order_1.map(function (key) { return ({
2202
+ key: key,
2203
+ list: buckets_1.get(key) || [],
2204
+ index: 0
2205
+ }); });
2206
+ var trimmed_1 = [];
2207
+ var added = true;
2208
+ while (trimmed_1.length < limit && added) {
2209
+ added = false;
2210
+ try {
2211
+ for (var trackers_1 = (e_2 = void 0, __values(trackers)), trackers_1_1 = trackers_1.next(); !trackers_1_1.done; trackers_1_1 = trackers_1.next()) {
2212
+ var tracker = trackers_1_1.value;
2213
+ if (trimmed_1.length >= limit) {
2214
+ break;
2215
+ }
2216
+ if (tracker.index < tracker.list.length) {
2217
+ trimmed_1.push(tracker.list[tracker.index]);
2218
+ tracker.index += 1;
2219
+ added = true;
2220
+ }
2221
+ }
2222
+ }
2223
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
2224
+ finally {
2225
+ try {
2226
+ if (trackers_1_1 && !trackers_1_1.done && (_a = trackers_1.return)) _a.call(trackers_1);
2227
+ }
2228
+ finally { if (e_2) throw e_2.error; }
2229
+ }
2230
+ }
2231
+ return { fields: trimmed_1, truncated: true, total: fields.length };
2232
+ }
2233
+ }
1956
2234
  var trimmed = fields.slice(0, limit);
1957
2235
  return { fields: trimmed, truncated: true, total: fields.length };
1958
2236
  }
@@ -2040,20 +2318,25 @@ function sanitizeReportBuilderPatch(raw, ctx) {
2040
2318
  return;
2041
2319
  }
2042
2320
  var header = normalizeOptionalString(col.header || col.title || col.name);
2043
- var fieldPath = resolveFieldPath(col.field_path || col.fieldPath, ctx);
2321
+ var fieldPath = resolveFieldPath(col.field_path || col.fieldPath || col.field || col.field_name || col.fieldName || col.path, ctx);
2322
+ if (!fieldPath && header) {
2323
+ fieldPath = resolveFieldPath(header, ctx);
2324
+ }
2044
2325
  var mappingsRaw = Array.isArray(col.mappings) ? col.mappings : [];
2045
2326
  var mappings = [];
2046
2327
  mappingsRaw.forEach(function (map) {
2047
- var mappedField = resolveFieldPath((map === null || map === void 0 ? void 0 : map.field_path) || (map === null || map === void 0 ? void 0 : map.fieldPath), ctx);
2328
+ var mappedField = resolveFieldPath((map === null || map === void 0 ? void 0 : map.field_path) || (map === null || map === void 0 ? void 0 : map.fieldPath) || (map === null || map === void 0 ? void 0 : map.field) || (map === null || map === void 0 ? void 0 : map.field_name) || (map === null || map === void 0 ? void 0 : map.fieldName) || (map === null || map === void 0 ? void 0 : map.path) || (map === null || map === void 0 ? void 0 : map.name), ctx);
2048
2329
  var mode = normalizeLayoutMode(map === null || map === void 0 ? void 0 : map.mode);
2049
2330
  var text = normalizeOptionalString(map === null || map === void 0 ? void 0 : map.text);
2050
2331
  var collection = resolveCollectionName(map === null || map === void 0 ? void 0 : map.collection, ctx);
2051
2332
  if (mode === 'text') {
2052
- mappings.push({
2053
- mode: mode,
2054
- text: text,
2055
- collection: collection
2056
- });
2333
+ if (text) {
2334
+ mappings.push({
2335
+ mode: mode,
2336
+ text: text,
2337
+ collection: collection
2338
+ });
2339
+ }
2057
2340
  }
2058
2341
  else if (mappedField) {
2059
2342
  mappings.push({
@@ -2066,10 +2349,33 @@ function sanitizeReportBuilderPatch(raw, ctx) {
2066
2349
  });
2067
2350
  }
2068
2351
  });
2069
- if (!mappings.length && fieldPath) {
2070
- mappings.push({ field_path: fieldPath, mode: normalizeLayoutMode(col.mode) });
2352
+ if (!mappings.length) {
2353
+ if (fieldPath) {
2354
+ mappings.push({ field_path: fieldPath, mode: normalizeLayoutMode(col.mode) });
2355
+ }
2356
+ else {
2357
+ var text = normalizeOptionalString(col.text);
2358
+ var mode = normalizeLayoutMode(col.mode);
2359
+ if ((mode === 'text' || text) && text) {
2360
+ mappings.push({
2361
+ mode: 'text',
2362
+ text: text,
2363
+ collection: resolveCollectionName(col.collection, ctx)
2364
+ });
2365
+ }
2366
+ }
2367
+ }
2368
+ if (!fieldPath) {
2369
+ var firstMapped = mappings.find(function (mapping) { return !!(mapping === null || mapping === void 0 ? void 0 : mapping.field_path); });
2370
+ if (firstMapped === null || firstMapped === void 0 ? void 0 : firstMapped.field_path) {
2371
+ fieldPath = firstMapped.field_path;
2372
+ }
2071
2373
  }
2072
- if (!header && !mappings.length) {
2374
+ if (!header && fieldPath) {
2375
+ var meta = ctx.fieldIndex.get(normalizePathKey(fieldPath));
2376
+ header = normalizeOptionalString(meta === null || meta === void 0 ? void 0 : meta.field_path_name);
2377
+ }
2378
+ if (!mappings.length) {
2073
2379
  return;
2074
2380
  }
2075
2381
  layoutColumns.push({
@@ -2145,6 +2451,12 @@ function sanitizeReportBuilderPatch(raw, ctx) {
2145
2451
  if (sort.length) {
2146
2452
  patch.sort = sort;
2147
2453
  }
2454
+ else {
2455
+ var defaultSortFields = buildDefaultSortFields(groupsRow, layoutColumns);
2456
+ if (defaultSortFields.length) {
2457
+ patch.sort = defaultSortFields.map(function (field_path) { return ({ field_path: field_path, order: 'asc' }); });
2458
+ }
2459
+ }
2148
2460
  var totalsRaw = Array.isArray(raw.totals) ? raw.totals : [];
2149
2461
  var totals = [];
2150
2462
  totalsRaw.forEach(function (total) {
@@ -2186,6 +2498,48 @@ function sanitizeReportBuilderPatch(raw, ctx) {
2186
2498
  }
2187
2499
  return patch;
2188
2500
  }
2501
+ function buildDefaultSortFields(groupsRow, layoutColumns) {
2502
+ var fields = [];
2503
+ var addField = function (fieldPath) {
2504
+ var normalized = normalizeOptionalString(fieldPath);
2505
+ if (normalized && !fields.includes(normalized)) {
2506
+ fields.push(normalized);
2507
+ }
2508
+ };
2509
+ (groupsRow || []).forEach(addField);
2510
+ if (fields.length) {
2511
+ return fields;
2512
+ }
2513
+ (layoutColumns || []).forEach(function (col) {
2514
+ var e_3, _a;
2515
+ if (!col || typeof col !== 'object') {
2516
+ return;
2517
+ }
2518
+ var fieldPath = '';
2519
+ var mappings = Array.isArray(col.mappings) ? col.mappings : [];
2520
+ try {
2521
+ for (var mappings_1 = __values(mappings), mappings_1_1 = mappings_1.next(); !mappings_1_1.done; mappings_1_1 = mappings_1.next()) {
2522
+ var mapping = mappings_1_1.value;
2523
+ if (mapping === null || mapping === void 0 ? void 0 : mapping.field_path) {
2524
+ fieldPath = mapping.field_path;
2525
+ break;
2526
+ }
2527
+ }
2528
+ }
2529
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
2530
+ finally {
2531
+ try {
2532
+ if (mappings_1_1 && !mappings_1_1.done && (_a = mappings_1.return)) _a.call(mappings_1);
2533
+ }
2534
+ finally { if (e_3) throw e_3.error; }
2535
+ }
2536
+ if (!fieldPath && col.field_path) {
2537
+ fieldPath = col.field_path;
2538
+ }
2539
+ addField(fieldPath);
2540
+ });
2541
+ return fields;
2542
+ }
2189
2543
  function buildPatchNotes(patch, meta) {
2190
2544
  var columns = Array.isArray(patch === null || patch === void 0 ? void 0 : patch.layout_columns) ? patch.layout_columns.length : 0;
2191
2545
  var groups = Array.isArray(patch === null || patch === void 0 ? void 0 : patch.groups_row) ? patch.groups_row.length : 0;
@@ -2368,7 +2722,7 @@ function safeJsonParse(value) {
2368
2722
  }
2369
2723
  }
2370
2724
  function evaluateReportBuilderGuardrails(message) {
2371
- var e_1, _a;
2725
+ var e_4, _a;
2372
2726
  var normalized = String(message || '').toLowerCase();
2373
2727
  var patterns = [
2374
2728
  { pattern: /\b(source\s*code|full\s*code|entire\s*code|repo\s*dump|repository|git\s*clone)\b/i, reason: 'Code access is restricted.' },
@@ -2390,12 +2744,12 @@ function evaluateReportBuilderGuardrails(message) {
2390
2744
  }
2391
2745
  }
2392
2746
  }
2393
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
2747
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
2394
2748
  finally {
2395
2749
  try {
2396
2750
  if (patterns_1_1 && !patterns_1_1.done && (_a = patterns_1.return)) _a.call(patterns_1);
2397
2751
  }
2398
- finally { if (e_1) throw e_1.error; }
2752
+ finally { if (e_4) throw e_4.error; }
2399
2753
  }
2400
2754
  return null;
2401
2755
  }