@resolveio/server-lib 20.14.27 → 20.14.29

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.
@@ -18,5 +18,25 @@ export type AiAssistantMongoReadInput = {
18
18
  readonly?: boolean;
19
19
  };
20
20
  };
21
+ export type AiAssistantMongoAggregateInput = {
22
+ database?: string;
23
+ collection?: string;
24
+ query?: Record<string, any>;
25
+ pipeline?: Array<Record<string, any>>;
26
+ options?: {
27
+ allowDiskUse?: boolean;
28
+ maxTimeMS?: number;
29
+ limit?: number;
30
+ };
31
+ permissionView?: string;
32
+ id_client?: string;
33
+ mongo?: {
34
+ database?: string;
35
+ databases?: string[];
36
+ access?: string;
37
+ readonly?: boolean;
38
+ };
39
+ };
21
40
  export declare function loadAiTerminalMethods(methodManager: any): void;
22
41
  export declare function executeAiAssistantMongoRead(payload: AiAssistantMongoReadInput, context: any): Promise<any>;
42
+ export declare function executeAiAssistantMongoAggregate(payload: AiAssistantMongoAggregateInput, context: any): Promise<any>;
@@ -14,6 +14,17 @@ var __extends = (this && this.__extends) || (function () {
14
14
  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
15
  };
16
16
  })();
17
+ var __assign = (this && this.__assign) || function () {
18
+ __assign = Object.assign || function(t) {
19
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
20
+ s = arguments[i];
21
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
22
+ t[p] = s[p];
23
+ }
24
+ return t;
25
+ };
26
+ return __assign.apply(this, arguments);
27
+ };
17
28
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
29
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
30
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -50,17 +61,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
50
61
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
51
62
  }
52
63
  };
53
- var __values = (this && this.__values) || function(o) {
54
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
55
- if (m) return m.call(o);
56
- if (o && typeof o.length === "number") return {
57
- next: function () {
58
- if (o && i >= o.length) o = void 0;
59
- return { value: o && o[i++], done: !o };
60
- }
61
- };
62
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
63
- };
64
64
  var __read = (this && this.__read) || function (o, n) {
65
65
  var m = typeof Symbol === "function" && o[Symbol.iterator];
66
66
  if (!m) return o;
@@ -77,9 +77,30 @@ var __read = (this && this.__read) || function (o, n) {
77
77
  }
78
78
  return ar;
79
79
  };
80
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
81
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
82
+ if (ar || !(i in from)) {
83
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
84
+ ar[i] = from[i];
85
+ }
86
+ }
87
+ return to.concat(ar || Array.prototype.slice.call(from));
88
+ };
89
+ var __values = (this && this.__values) || function(o) {
90
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
91
+ if (m) return m.call(o);
92
+ if (o && typeof o.length === "number") return {
93
+ next: function () {
94
+ if (o && i >= o.length) o = void 0;
95
+ return { value: o && o[i++], done: !o };
96
+ }
97
+ };
98
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
99
+ };
80
100
  Object.defineProperty(exports, "__esModule", { value: true });
81
101
  exports.loadAiTerminalMethods = loadAiTerminalMethods;
82
102
  exports.executeAiAssistantMongoRead = executeAiAssistantMongoRead;
103
+ exports.executeAiAssistantMongoAggregate = executeAiAssistantMongoAggregate;
83
104
  var fs_1 = require("fs");
84
105
  var events_1 = require("events");
85
106
  var os = require("os");
@@ -141,11 +162,11 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
141
162
  '- Prefer high-level explanations and point to routes instead of code. Only mention file paths if explicitly requested.',
142
163
  '- When asked where to do something, give the exact screen name and the in-app route as a standalone path starting with "/" so it can be clicked.',
143
164
  '- When asked "why is this happening," respond with: cause, trigger, data source(s), and expected vs actual outcome.',
144
- '- For troubleshooting, ask 2-3 targeted questions first, then give a short decision tree (If X, do Y; If not, do Z).',
165
+ '- For troubleshooting, ask clarifying questions only when needed to proceed; otherwise answer directly, then give a short decision tree (If X, do Y; If not, do Z).',
145
166
  '- Provide checklists for common tasks, highlighting required fields and common pitfalls.',
146
167
  '- If asked "where is this set," give the screen/workflow name and navigation steps to reach it.',
147
168
  '- If asked "what changed," summarize release notes if known; if not available, say so and suggest where to check or offer a support ticket.',
148
- '- Suggest 1-2 related screens or next steps when it helps.',
169
+ '- After answering, optionally suggest 0-1 related next steps only when helpful. Do not ask for "next steps" every time.',
149
170
  '- If access is blocked, name the permission/role needed and how to request it.',
150
171
  '- Avoid vague labels like "Operations app"; use the specific screen/workflow name.',
151
172
  '- Do not mention other client projects or ask which client; stay within the current project context.',
@@ -157,14 +178,21 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
157
178
  '- If the user explicitly asks to create/open/file a support ticket, end your response with a single line exactly in this format:',
158
179
  '- SUPPORT_TICKET_CREATE: <one-line summary>',
159
180
  '- Only include that line when the user clearly wants a ticket created. Do not claim a ticket is created unless you include that line.',
181
+ '- Do not end responses with tentative phrasing like "I could" or "I’m going to." Complete the request or state what is required to proceed.',
182
+ '- Use the codebase context to choose correct collections/fields/workflows and use MONGO_READ/MONGO_AGG to answer with real data when needed.',
183
+ '- For direct questions, answer first. Ask a single follow-up only if required to run a query or resolve missing details.',
160
184
  '- If you need database data to answer, end your response with a single line exactly in this format:',
161
185
  '- MONGO_READ: {"collection":"<name>","query":{...},"options":{"projection":{...},"sort":{...},"limit":20},"permissionView":"</route>"}',
186
+ '- If you need grouped/aggregated data (totals by user, rankings, trends), end your response with a single line exactly in this format:',
187
+ '- MONGO_AGG: {"collection":"<name>","pipeline":[...],"options":{"allowDiskUse":true,"limit":20},"permissionView":"</route>"}',
162
188
  '- For invoice data, set permissionView to an invoice route (ex: /invoice/list or /report/invoice).',
163
189
  '- Keep queries minimal, read-only, and avoid user/credential data unless the user is a super admin.',
164
190
  '- Assume you are not a super admin unless explicitly told otherwise.',
165
191
  '- Only request data when the user has permission for that module; invoice data requires invoice view access.',
166
192
  '- If the user lacks permission, answer without data and explain how to view it in the app or request access.',
167
- '- Use MONGO_READ only to produce summaries/snapshots/health checks (not raw dumps) when permitted.',
193
+ '- For simple counts or time-range totals, use MONGO_READ (includeTotal). For breakdowns, rankings, or sums grouped by a field, use MONGO_AGG.',
194
+ '- Before issuing MONGO_READ or MONGO_AGG, verify the collection name and key fields by checking collection/model definitions in the current app (look for "collectionName:" and date fields like date_created/date_completed/createdAt). Do not invent collection names.',
195
+ '- Use MONGO_READ/MONGO_AGG only to produce summaries/snapshots/health checks (not raw dumps) when permitted.',
168
196
  '- When referencing data, summarize it in bullets and avoid raw JSON or dumps.',
169
197
  '- Keep responses concise and use low reasoning effort.'
170
198
  ].join('\n');
@@ -449,6 +477,24 @@ function loadAiTerminalMethods(methodManager) {
449
477
  });
450
478
  }
451
479
  },
480
+ aiAssistantMongoAggregate: {
481
+ check: new simpl_schema_1.default({
482
+ payload: {
483
+ type: Object,
484
+ blackbox: true
485
+ }
486
+ }),
487
+ function: function (payload) {
488
+ return __awaiter(this, void 0, void 0, function () {
489
+ return __generator(this, function (_a) {
490
+ switch (_a.label) {
491
+ case 0: return [4 /*yield*/, executeAiAssistantMongoAggregate(payload, this)];
492
+ case 1: return [2 /*return*/, _a.sent()];
493
+ }
494
+ });
495
+ });
496
+ }
497
+ },
452
498
  aiCoderTerminalDeployTest: {
453
499
  check: new simpl_schema_1.default({
454
500
  id_conversation: {
@@ -900,6 +946,74 @@ function executeAiAssistantMongoRead(payload, context) {
900
946
  });
901
947
  });
902
948
  }
949
+ function executeAiAssistantMongoAggregate(payload, context) {
950
+ return __awaiter(this, void 0, void 0, function () {
951
+ var input, collection, _a, user, isSuperAdmin, customerId, dbName, db, baseQuery, userId, normalizedClient, shouldScopeByClient, _b, clientScopedQuery, scopedQuery, normalizedPipeline, pipelineWithScope, normalizedOptions, limitedPipeline, documents, sanitizedDocuments;
952
+ var _c;
953
+ return __generator(this, function (_d) {
954
+ switch (_d.label) {
955
+ case 0:
956
+ input = payload || {};
957
+ collection = normalizeOptionalString(input.collection);
958
+ if (!collection) {
959
+ throw new Error('AI assistant mongo aggregate: Collection is required.');
960
+ }
961
+ return [4 /*yield*/, ensureAssistantReadAccess(context, input.permissionView, collection)];
962
+ case 1:
963
+ _a = _d.sent(), user = _a.user, isSuperAdmin = _a.isSuperAdmin;
964
+ if (!isSuperAdmin && AI_ASSISTANT_BLOCKED_COLLECTIONS.has(collection)) {
965
+ throw new Error('AI assistant mongo aggregate: Access denied.');
966
+ }
967
+ customerId = normalizeOptionalString((_c = user === null || user === void 0 ? void 0 : user.other) === null || _c === void 0 ? void 0 : _c.id_customer);
968
+ dbName = resolveAssistantDatabaseName(input.database, input.mongo);
969
+ db = resolveio_server_app_1.ResolveIOServer.getMongoConnection().db(dbName);
970
+ baseQuery = normalizeMongoQuery(input.query);
971
+ if (!isSuperAdmin && (collection === 'users' || collection === 'user-versions')) {
972
+ userId = normalizeOptionalString(user === null || user === void 0 ? void 0 : user._id);
973
+ if (!userId) {
974
+ throw new Error('AI assistant mongo aggregate: Access denied.');
975
+ }
976
+ baseQuery = {
977
+ $and: [baseQuery, { _id: userId }]
978
+ };
979
+ }
980
+ normalizedClient = normalizeOptionalString(input.id_client);
981
+ if (!(!isSuperAdmin && normalizedClient)) return [3 /*break*/, 3];
982
+ return [4 /*yield*/, collectionHasClientIndex(db, dbName, collection)];
983
+ case 2:
984
+ _b = _d.sent();
985
+ return [3 /*break*/, 4];
986
+ case 3:
987
+ _b = false;
988
+ _d.label = 4;
989
+ case 4:
990
+ shouldScopeByClient = _b;
991
+ clientScopedQuery = shouldScopeByClient
992
+ ? applyClientScopeFilter(baseQuery, normalizedClient, isSuperAdmin)
993
+ : baseQuery;
994
+ scopedQuery = applyCustomerScopeFilter(clientScopedQuery, collection, customerId, isSuperAdmin);
995
+ normalizedPipeline = normalizeAssistantAggregatePipeline(input.pipeline);
996
+ pipelineWithScope = buildAssistantAggregatePipeline(scopedQuery, normalizedPipeline);
997
+ normalizedOptions = normalizeAssistantAggregateOptions(input.options);
998
+ limitedPipeline = applyAssistantAggregateLimit(pipelineWithScope, normalizedOptions.limit);
999
+ if (containsForbiddenMongoOperators(limitedPipeline)) {
1000
+ throw new Error('AI assistant mongo aggregate: Pipeline contains restricted operators.');
1001
+ }
1002
+ return [4 /*yield*/, db.collection(collection)
1003
+ .aggregate(limitedPipeline, normalizedOptions.aggregateOptions)
1004
+ .toArray()];
1005
+ case 5:
1006
+ documents = _d.sent();
1007
+ sanitizedDocuments = isSuperAdmin
1008
+ ? documents
1009
+ : documents.map(function (doc) { return redactSensitiveFields((0, common_1.deepCopy)(doc)); });
1010
+ return [2 /*return*/, {
1011
+ documents: sanitizedDocuments
1012
+ }];
1013
+ }
1014
+ });
1015
+ });
1016
+ }
903
1017
  function ensureAssistantReadAccess(context, permissionView, collection) {
904
1018
  return __awaiter(this, void 0, void 0, function () {
905
1019
  var idUser, user, isSuperAdmin, normalizedPermission, normalizedCollection;
@@ -976,6 +1090,76 @@ function normalizeAssistantFindOptions(options) {
976
1090
  includeTotal: normalized.includeTotal === true
977
1091
  };
978
1092
  }
1093
+ function normalizeAssistantAggregatePipeline(pipeline) {
1094
+ if (!Array.isArray(pipeline)) {
1095
+ return [];
1096
+ }
1097
+ return pipeline.filter(function (stage) { return stage && typeof stage === 'object' && !Array.isArray(stage); });
1098
+ }
1099
+ function buildAssistantAggregatePipeline(query, pipeline) {
1100
+ var _a;
1101
+ var scopedPipeline = Array.isArray(pipeline) ? __spreadArray([], __read(pipeline), false) : [];
1102
+ if (!query || !Object.keys(query).length) {
1103
+ return scopedPipeline;
1104
+ }
1105
+ if (scopedPipeline.length && ((_a = scopedPipeline[0]) === null || _a === void 0 ? void 0 : _a.$geoNear) && typeof scopedPipeline[0].$geoNear === 'object') {
1106
+ var geoNearStage = __assign({}, scopedPipeline[0].$geoNear);
1107
+ var existingQuery = geoNearStage.query && typeof geoNearStage.query === 'object' ? geoNearStage.query : {};
1108
+ geoNearStage.query = { $and: [existingQuery, query] };
1109
+ return __spreadArray([{ $geoNear: geoNearStage }], __read(scopedPipeline.slice(1)), false);
1110
+ }
1111
+ return __spreadArray([{ $match: query }], __read(scopedPipeline), false);
1112
+ }
1113
+ function normalizeAssistantAggregateOptions(options) {
1114
+ var normalized = options || {};
1115
+ var allowDiskUse = normalized.allowDiskUse === true ? true : undefined;
1116
+ var maxTimeMS = typeof normalized.maxTimeMS === 'number' && normalized.maxTimeMS > 0
1117
+ ? (0, common_1.round)(normalized.maxTimeMS)
1118
+ : undefined;
1119
+ var limit = typeof normalized.limit === 'number'
1120
+ ? Math.min(Math.max((0, common_1.round)(normalized.limit), 0), AI_ASSISTANT_MONGO_MAX_LIMIT)
1121
+ : undefined;
1122
+ var aggregateOptions = {};
1123
+ if (allowDiskUse !== undefined) {
1124
+ aggregateOptions.allowDiskUse = allowDiskUse;
1125
+ }
1126
+ if (maxTimeMS !== undefined) {
1127
+ aggregateOptions.maxTimeMS = maxTimeMS;
1128
+ }
1129
+ return {
1130
+ aggregateOptions: aggregateOptions,
1131
+ limit: limit
1132
+ };
1133
+ }
1134
+ function findAggregateLimit(pipeline) {
1135
+ for (var i = 0; i < pipeline.length; i += 1) {
1136
+ var stage = pipeline[i];
1137
+ if (!stage || typeof stage !== 'object') {
1138
+ continue;
1139
+ }
1140
+ var limit = stage.$limit;
1141
+ if (typeof limit === 'number' && Number.isFinite(limit)) {
1142
+ return limit;
1143
+ }
1144
+ }
1145
+ return null;
1146
+ }
1147
+ function applyAssistantAggregateLimit(pipeline, limit) {
1148
+ var normalizedPipeline = Array.isArray(pipeline) ? __spreadArray([], __read(pipeline), false) : [];
1149
+ var maxLimit = AI_ASSISTANT_MONGO_MAX_LIMIT;
1150
+ var requestedLimit = typeof limit === 'number' && limit > 0
1151
+ ? Math.min(limit, maxLimit)
1152
+ : AI_ASSISTANT_MONGO_DEFAULT_LIMIT;
1153
+ var existingLimit = findAggregateLimit(normalizedPipeline);
1154
+ if (existingLimit === null) {
1155
+ normalizedPipeline.push({ $limit: requestedLimit });
1156
+ return normalizedPipeline;
1157
+ }
1158
+ if (existingLimit > maxLimit) {
1159
+ normalizedPipeline.push({ $limit: maxLimit });
1160
+ }
1161
+ return normalizedPipeline;
1162
+ }
979
1163
  function normalizeMongoQuery(query) {
980
1164
  var normalized = query && typeof query === 'object' ? query : {};
981
1165
  if (containsForbiddenMongoOperators(normalized)) {