@resolveio/server-lib 20.14.45 → 20.14.47

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.
@@ -101,6 +101,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
101
101
  exports.loadAiTerminalMethods = loadAiTerminalMethods;
102
102
  exports.executeAiAssistantMongoRead = executeAiAssistantMongoRead;
103
103
  exports.executeAiAssistantMongoAggregate = executeAiAssistantMongoAggregate;
104
+ exports.extractAssistantMongoDirective = extractAssistantMongoDirective;
105
+ exports.serializeMongoValue = serializeMongoValue;
106
+ exports.flattenForTable = flattenForTable;
107
+ exports.buildDisplayTable = buildDisplayTable;
108
+ exports.formatDisplayTableMarkdown = formatDisplayTableMarkdown;
104
109
  var fs_1 = require("fs");
105
110
  var events_1 = require("events");
106
111
  var os = require("os");
@@ -125,6 +130,35 @@ var DEFAULT_CODEX_MODEL = 'gpt-5.2-codex';
125
130
  var DEFAULT_CODEX_TIMEOUT_MS = 180000;
126
131
  var AI_ASSISTANT_MONGO_DEFAULT_LIMIT = 20;
127
132
  var AI_ASSISTANT_MONGO_MAX_LIMIT = 200;
133
+ var AI_ASSISTANT_TOOL_MAX_STEPS = 1;
134
+ var AI_ASSISTANT_DISPLAY_MAX_COLUMNS = 12;
135
+ var AI_ASSISTANT_DISPLAY_PREVIEW_MAX_ROWS = 20;
136
+ var AI_ASSISTANT_DISPLAY_STRING_LIMIT = 160;
137
+ var AI_ASSISTANT_PROGRESS_PLACEHOLDER = 'Thinking...';
138
+ var AI_ASSISTANT_PROGRESS_TICK_MS = 8000;
139
+ var AI_ASSISTANT_PROGRESS_TICKS = [
140
+ 'Reviewing context',
141
+ 'Checking data',
142
+ 'Drafting response'
143
+ ];
144
+ var AI_ASSISTANT_DISPLAY_PRIORITY_FIELDS = [
145
+ 'name',
146
+ 'title',
147
+ 'status',
148
+ 'type',
149
+ 'stage',
150
+ 'state',
151
+ 'category',
152
+ 'date_created',
153
+ 'createdAt',
154
+ 'updatedAt',
155
+ 'date_updated',
156
+ 'date_completed',
157
+ 'id_customer',
158
+ 'id_client',
159
+ 'id_user',
160
+ 'id_invoice'
161
+ ];
128
162
  var AI_ASSISTANT_DATE_FALLBACKS = {
129
163
  date_created: 'createdAt',
130
164
  createdAt: 'date_created'
@@ -157,12 +191,29 @@ var AI_ASSISTANT_SENSITIVE_FIELDS = [
157
191
  var AI_ASSISTANT_CLIENT_SCOPE_CACHE = new Map();
158
192
  var AI_ASSISTANT_SYSTEM_PROMPT = [
159
193
  'You are the ResolveIO in-app AI assistant running with read-only access to the codebase.',
194
+ 'Core rules:',
160
195
  '- Never share code or file contents. All code is proprietary.',
161
196
  '- Do not modify files, run destructive commands, or access databases directly.',
162
- '- Read-only Mongo access is allowed only via the MONGO_READ directive (see below).',
197
+ '- Read-only Mongo access is allowed only via the MONGO_READ/MONGO_AGG directives (see below).',
163
198
  '- Do not access secrets, credentials, or user data.',
164
199
  '- If the user has a customer portal scope (other.id_customer), only discuss that customer\'s data and what is visible in their customer portal. Never reference other customers or internal/admin-only data. If asked for anything outside the portal, say it isn\'t available.',
165
200
  '- Do not assist with hacking, bypassing security, or abuse.',
201
+ 'Accuracy & tools:',
202
+ '- Do not guess or invent collections/fields. If unsure, verify in the codebase or run a small Mongo read (limit 1-5) to learn the shape.',
203
+ '- Prefer running a small Mongo read over asking multiple questions.',
204
+ '- Ask at most one clarifying question only when required to run a query or resolve missing details.',
205
+ '- Use the codebase context to choose correct collections/fields/workflows and use MONGO_READ/MONGO_AGG to answer with real data when needed.',
206
+ '- For direct questions, answer first. Ask a single follow-up only if required to proceed.',
207
+ 'Data Presentation:',
208
+ '- Output plain Markdown (NO triple backticks).',
209
+ '- When you reference database results, summarize first, then include a Markdown table.',
210
+ '- When presenting record lists or aggregates, produce a Markdown table (pipes).',
211
+ '- Never show raw JSON dumps.',
212
+ '- Do not include `_id` & `__v` & `id_<other collection _id property>` in tables by default.',
213
+ '- If `_id` & `__v` & `id_<other collection _id property>` is needed, show it as `id` and shorten (e.g., first 6-8 chars) unless asked otherwise.',
214
+ '- Rename `createdAt` to `Created At` and `updatedAt` to `Updated At` in tables.',
215
+ 'Response style:',
216
+ '- Keep responses concise: answer first, then 3-8 bullets, then a table when data is involved.',
166
217
  '- Prefer high-level explanations and point to routes instead of code. Only mention file paths if explicitly requested.',
167
218
  '- 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.',
168
219
  '- When asked "why is this happening," respond with: cause, trigger, data source(s), and expected vs actual outcome.',
@@ -174,7 +225,7 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
174
225
  '- If access is blocked, name the permission/role needed and how to request it.',
175
226
  '- Avoid vague labels like "Operations app"; use the specific screen/workflow name.',
176
227
  '- Do not mention other client projects or ask which client; stay within the current project context.',
177
- '- Respond with a single concise message. Do not add labels like "Customer-facing summary" or "Work ticket summary" and do not include estimated hours.',
228
+ '- Do not add labels like "Customer-facing summary" or "Work ticket summary" and do not include estimated hours.',
178
229
  '- Use structured responses by default: short heading, then bullet points. For "how do I" questions, provide a step-by-step list (numbered) with explicit page/screen names and actions.',
179
230
  '- When giving steps, include navigation guidance like "Go to <screen>" and "Click/Select/Enter <field>" so the user can follow it exactly.',
180
231
  '- If context scope is provided (ex: current page), prioritize that screen in your answer.',
@@ -183,8 +234,7 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
183
234
  '- SUPPORT_TICKET_CREATE: <one-line summary>',
184
235
  '- Only include that line when the user clearly wants a ticket created. Do not claim a ticket is created unless you include that line.',
185
236
  '- 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.',
186
- '- Use the codebase context to choose correct collections/fields/workflows and use MONGO_READ/MONGO_AGG to answer with real data when needed.',
187
- '- For direct questions, answer first. Ask a single follow-up only if required to run a query or resolve missing details.',
237
+ 'Mongo directives:',
188
238
  '- If you need database data to answer, end your response with a single line exactly in this format:',
189
239
  '- MONGO_READ: {"collection":"<name>","query":{...},"options":{"projection":{...},"sort":{...},"limit":20},"permissionView":"</route>"}',
190
240
  '- If you need grouped/aggregated data (totals by user, rankings, trends), end your response with a single line exactly in this format:',
@@ -199,7 +249,7 @@ var AI_ASSISTANT_SYSTEM_PROMPT = [
199
249
  '- For creation-date questions when both date_created and createdAt exist, match both with $or so results are not missed.',
200
250
  '- When grouping by fields that can be arrays (drivers, deliveries, routes, chemicals), $unwind first and group by both id and name when available.',
201
251
  '- Use MONGO_READ/MONGO_AGG only to produce summaries/snapshots/health checks (not raw dumps) when permitted.',
202
- '- When referencing data, summarize it in bullets and avoid raw JSON or dumps.',
252
+ '- If the user explicitly asks for IDs, set options.includeIds: true.',
203
253
  '- Keep responses concise and use low reasoning effort.'
204
254
  ].join('\n');
205
255
  var AI_FORM_PATCH_SYSTEM_PROMPT = [
@@ -738,10 +788,10 @@ function executeAiFormPatch(payload, context) {
738
788
  }
739
789
  function executeAiAssistantCodexRun(payload, context) {
740
790
  return __awaiter(this, void 0, void 0, function () {
741
- var input, message, guardrail, conversation_2, now_2, userMsg, assistantMsg, user, isSuperAdmin, hasInvoiceAccess, customerId, conversation, now, attachments, attachmentData, historyLimit, history, _a, historyLines, prompt, workspaceRoot, codexConfig, runOptions, responseText, assistantContent, userDoc, assistantDoc, insertResult;
742
- var _b, _c;
743
- return __generator(this, function (_d) {
744
- switch (_d.label) {
791
+ var input, message, guardrail, conversation_2, now_2, userMsg, assistantMsg, user, isSuperAdmin, hasInvoiceAccess, customerId, conversation, now, attachments, attachmentData, historyLimit, history, _a, historyLines, assistantContext, prompt, workspaceRoot, codexConfig, runOptions, userDoc, initialProgress, assistantDoc, insertResult, assistantMessageId, progressTracker, assistantContent, toolResult, responseText, directive, cleanedResponseText, toolRequest, toolResponse, _b, toolPayload, followupPrompt, followupText, _c, error_1, error_2, finalNow, finalMetadata, finalAssistantDoc;
792
+ var _d, _e;
793
+ return __generator(this, function (_f) {
794
+ switch (_f.label) {
745
795
  case 0:
746
796
  input = payload || {};
747
797
  message = normalizeOptionalString(input.message);
@@ -755,7 +805,7 @@ function executeAiAssistantCodexRun(payload, context) {
755
805
  if (!(guardrail === null || guardrail === void 0 ? void 0 : guardrail.blocked)) return [3 /*break*/, 5];
756
806
  return [4 /*yield*/, ensureConversation(input, 'codex')];
757
807
  case 1:
758
- conversation_2 = _d.sent();
808
+ conversation_2 = _f.sent();
759
809
  now_2 = new Date();
760
810
  userMsg = {
761
811
  id_conversation: conversation_2._id,
@@ -777,13 +827,13 @@ function executeAiAssistantCodexRun(payload, context) {
777
827
  };
778
828
  return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(userMsg)];
779
829
  case 2:
780
- _d.sent();
830
+ _f.sent();
781
831
  return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(assistantMsg)];
782
832
  case 3:
783
- _d.sent();
833
+ _f.sent();
784
834
  return [4 /*yield*/, touchConversation(conversation_2._id, now_2)];
785
835
  case 4:
786
- _d.sent();
836
+ _f.sent();
787
837
  return [2 /*return*/, {
788
838
  conversation: conversation_2,
789
839
  message: assistantMsg,
@@ -791,27 +841,27 @@ function executeAiAssistantCodexRun(payload, context) {
791
841
  }];
792
842
  case 5: return [4 /*yield*/, user_collection_1.Users.findById(context === null || context === void 0 ? void 0 : context.id_user)];
793
843
  case 6:
794
- user = _d.sent();
795
- isSuperAdmin = !!((_b = user === null || user === void 0 ? void 0 : user.roles) === null || _b === void 0 ? void 0 : _b.super_admin);
844
+ user = _f.sent();
845
+ isSuperAdmin = !!((_d = user === null || user === void 0 ? void 0 : user.roles) === null || _d === void 0 ? void 0 : _d.super_admin);
796
846
  hasInvoiceAccess = userHasInvoiceAccess(user);
797
- customerId = normalizeOptionalString((_c = user === null || user === void 0 ? void 0 : user.other) === null || _c === void 0 ? void 0 : _c.id_customer);
847
+ customerId = normalizeOptionalString((_e = user === null || user === void 0 ? void 0 : user.other) === null || _e === void 0 ? void 0 : _e.id_customer);
798
848
  return [4 /*yield*/, ensureConversation(input, 'codex')];
799
849
  case 7:
800
- conversation = _d.sent();
850
+ conversation = _f.sent();
801
851
  now = new Date();
802
852
  attachments = Array.isArray(input.attachments) ? input.attachments : [];
803
853
  return [4 /*yield*/, readAttachmentContents(attachments)];
804
854
  case 8:
805
- attachmentData = _d.sent();
855
+ attachmentData = _f.sent();
806
856
  historyLimit = normalizeHistoryLimit(input.max_history);
807
857
  if (!(historyLimit > 0)) return [3 /*break*/, 10];
808
858
  return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.find({ id_conversation: conversation._id, role: { $in: ['user', 'assistant'] } }, { sort: { createdAt: 1 }, limit: historyLimit * 2 })];
809
859
  case 9:
810
- _a = _d.sent();
860
+ _a = _f.sent();
811
861
  return [3 /*break*/, 11];
812
862
  case 10:
813
863
  _a = [];
814
- _d.label = 11;
864
+ _f.label = 11;
815
865
  case 11:
816
866
  history = _a;
817
867
  historyLines = [];
@@ -822,10 +872,11 @@ function executeAiAssistantCodexRun(payload, context) {
822
872
  historyLines.push("".concat(role, ": ").concat(content));
823
873
  }
824
874
  });
825
- prompt = buildAssistantCodexPrompt(message, attachmentData.promptText, historyLines.join('\n'), buildAssistantContext(input, { isSuperAdmin: isSuperAdmin, hasInvoiceAccess: hasInvoiceAccess, customerId: customerId }));
875
+ assistantContext = buildAssistantContext(input, { isSuperAdmin: isSuperAdmin, hasInvoiceAccess: hasInvoiceAccess, customerId: customerId });
876
+ prompt = buildAssistantCodexPrompt(message, attachmentData.promptText, historyLines.join('\n'), assistantContext);
826
877
  return [4 /*yield*/, resolveAssistantWorkspaceRoot()];
827
878
  case 12:
828
- workspaceRoot = _d.sent();
879
+ workspaceRoot = _f.sent();
829
880
  codexConfig = resolveCodexSettings();
830
881
  runOptions = {
831
882
  timeoutMs: resolveCodexTimeoutMs(),
@@ -840,10 +891,6 @@ function executeAiAssistantCodexRun(payload, context) {
840
891
  approvalPolicy: 'never'
841
892
  }
842
893
  };
843
- return [4 /*yield*/, runCodexInWorkerThread(prompt, runOptions, codexConfig)];
844
- case 13:
845
- responseText = _d.sent();
846
- assistantContent = sanitizeAssistantResponse(responseText);
847
894
  userDoc = {
848
895
  id_conversation: conversation._id,
849
896
  role: 'user',
@@ -852,44 +899,125 @@ function executeAiAssistantCodexRun(payload, context) {
852
899
  createdAt: now,
853
900
  updatedAt: now
854
901
  };
902
+ initialProgress = ['Thinking'];
855
903
  assistantDoc = {
856
904
  id_conversation: conversation._id,
857
905
  role: 'assistant',
858
- content: assistantContent,
906
+ content: AI_ASSISTANT_PROGRESS_PLACEHOLDER,
859
907
  metadata: {
860
- model: resolveCodexModel()
908
+ model: resolveCodexModel(),
909
+ pending: true,
910
+ progress: initialProgress
861
911
  },
862
912
  createdAt: now,
863
913
  updatedAt: now
864
914
  };
865
915
  return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(userDoc)];
866
- case 14:
867
- _d.sent();
916
+ case 13:
917
+ _f.sent();
868
918
  return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(assistantDoc)];
919
+ case 14:
920
+ insertResult = _f.sent();
921
+ assistantMessageId = (insertResult === null || insertResult === void 0 ? void 0 : insertResult._id) || (insertResult === null || insertResult === void 0 ? void 0 : insertResult.insertedId);
922
+ progressTracker = createAssistantProgressTracker(assistantMessageId, initialProgress);
923
+ assistantContent = '';
924
+ toolResult = null;
925
+ _f.label = 15;
869
926
  case 15:
870
- insertResult = _d.sent();
871
- return [4 /*yield*/, touchConversation(conversation._id, now, insertResult._id)];
927
+ _f.trys.push([15, 30, 31, 32]);
928
+ return [4 /*yield*/, runCodexInWorkerThread(prompt, runOptions, codexConfig)];
872
929
  case 16:
873
- _d.sent();
874
- if (!(input.delete_files_after_run !== false)) return [3 /*break*/, 18];
875
- return [4 /*yield*/, cleanupAttachments(attachmentData.attachments)];
930
+ responseText = _f.sent();
931
+ directive = extractAssistantMongoDirective(responseText);
932
+ cleanedResponseText = (directive === null || directive === void 0 ? void 0 : directive.cleaned) || responseText;
933
+ assistantContent = sanitizeAssistantResponse(cleanedResponseText);
934
+ if (!((directive === null || directive === void 0 ? void 0 : directive.payload) && AI_ASSISTANT_TOOL_MAX_STEPS > 0)) return [3 /*break*/, 28];
935
+ toolRequest = buildAssistantToolRequest(directive, input);
936
+ progressTracker.push(directive.type === 'aggregate' ? 'Running Mongo aggregation' : 'Running Mongo read');
937
+ _f.label = 17;
876
938
  case 17:
877
- _d.sent();
878
- _d.label = 18;
879
- case 18: return [2 /*return*/, {
880
- conversation: conversation,
881
- message: assistantDoc
882
- }];
939
+ _f.trys.push([17, 26, , 27]);
940
+ if (!(directive.type === 'aggregate')) return [3 /*break*/, 19];
941
+ return [4 /*yield*/, executeAiAssistantMongoAggregate(toolRequest, context)];
942
+ case 18:
943
+ _b = _f.sent();
944
+ return [3 /*break*/, 21];
945
+ case 19: return [4 /*yield*/, executeAiAssistantMongoRead(toolRequest, context)];
946
+ case 20:
947
+ _b = _f.sent();
948
+ _f.label = 21;
949
+ case 21:
950
+ toolResponse = _b;
951
+ toolPayload = buildAssistantToolResultPayload(directive, toolResponse);
952
+ toolResult = toolPayload.result;
953
+ progressTracker.push('Summarizing results');
954
+ followupPrompt = buildAssistantCodexToolFollowupPrompt(message, attachmentData.promptText, historyLines.join('\n'), assistantContext, toolPayload.prompt);
955
+ _f.label = 22;
956
+ case 22:
957
+ _f.trys.push([22, 24, , 25]);
958
+ return [4 /*yield*/, runCodexInWorkerThread(followupPrompt, runOptions, codexConfig)];
959
+ case 23:
960
+ followupText = _f.sent();
961
+ assistantContent = sanitizeAssistantResponse(followupText);
962
+ return [3 /*break*/, 25];
963
+ case 24:
964
+ _c = _f.sent();
965
+ assistantContent = buildAssistantToolFallbackResponse(toolPayload.result);
966
+ return [3 /*break*/, 25];
967
+ case 25: return [3 /*break*/, 27];
968
+ case 26:
969
+ error_1 = _f.sent();
970
+ assistantContent = buildAssistantToolErrorMessage(error_1, directive, toolRequest);
971
+ return [3 /*break*/, 27];
972
+ case 27: return [3 /*break*/, 29];
973
+ case 28:
974
+ progressTracker.push('Drafting response');
975
+ _f.label = 29;
976
+ case 29: return [3 /*break*/, 32];
977
+ case 30:
978
+ error_2 = _f.sent();
979
+ assistantContent = buildAssistantCodexErrorMessage(error_2);
980
+ return [3 /*break*/, 32];
981
+ case 31:
982
+ progressTracker.stop();
983
+ return [7 /*endfinally*/];
984
+ case 32:
985
+ if (!assistantContent) {
986
+ assistantContent = buildAssistantCodexErrorMessage(null);
987
+ }
988
+ finalNow = new Date();
989
+ finalMetadata = __assign({ model: resolveCodexModel() }, (toolResult ? { tool_result: toolResult } : {}));
990
+ finalAssistantDoc = __assign(__assign({}, assistantDoc), { _id: assistantMessageId, content: assistantContent, metadata: finalMetadata, updatedAt: finalNow });
991
+ if (!assistantMessageId) return [3 /*break*/, 34];
992
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.updateOne({ _id: assistantMessageId }, {
993
+ $set: {
994
+ content: assistantContent,
995
+ metadata: finalMetadata,
996
+ updatedAt: finalNow
997
+ }
998
+ })];
999
+ case 33:
1000
+ _f.sent();
1001
+ _f.label = 34;
1002
+ case 34: return [4 /*yield*/, touchConversation(conversation._id, finalNow, assistantMessageId ? String(assistantMessageId) : undefined)];
1003
+ case 35:
1004
+ _f.sent();
1005
+ if (!(input.delete_files_after_run !== false)) return [3 /*break*/, 37];
1006
+ return [4 /*yield*/, cleanupAttachments(attachmentData.attachments)];
1007
+ case 36:
1008
+ _f.sent();
1009
+ _f.label = 37;
1010
+ case 37: return [2 /*return*/, __assign({ conversation: conversation, message: finalAssistantDoc }, (toolResult ? { tool_result: toolResult } : {}))];
883
1011
  }
884
1012
  });
885
1013
  });
886
1014
  }
887
1015
  function executeAiAssistantMongoRead(payload, context) {
888
1016
  return __awaiter(this, void 0, void 0, function () {
889
- var input, collection, _a, user, isSuperAdmin, customerId, dbName, db, baseQuery, userId, normalizedClient, shouldScopeByClient, _b, clientScopedQuery, scopedQuery, normalized, documents, total, sanitizedDocuments;
890
- var _c;
891
- return __generator(this, function (_d) {
892
- switch (_d.label) {
1017
+ var input, collection, _a, user, isSuperAdmin, customerId, dbName, db, baseQuery, userId, normalizedClient, shouldScopeByClient, _b, clientScopedQuery, scopedQuery, normalized, documents, total, sanitizedDocuments, includeIds, display;
1018
+ var _c, _d;
1019
+ return __generator(this, function (_e) {
1020
+ switch (_e.label) {
893
1021
  case 0:
894
1022
  input = payload || {};
895
1023
  collection = normalizeOptionalString(input.collection);
@@ -898,7 +1026,7 @@ function executeAiAssistantMongoRead(payload, context) {
898
1026
  }
899
1027
  return [4 /*yield*/, ensureAssistantReadAccess(context, input.permissionView, collection)];
900
1028
  case 1:
901
- _a = _d.sent(), user = _a.user, isSuperAdmin = _a.isSuperAdmin;
1029
+ _a = _e.sent(), user = _a.user, isSuperAdmin = _a.isSuperAdmin;
902
1030
  if (!isSuperAdmin && AI_ASSISTANT_BLOCKED_COLLECTIONS.has(collection)) {
903
1031
  throw new Error('AI assistant mongo read: Access denied.');
904
1032
  }
@@ -919,11 +1047,11 @@ function executeAiAssistantMongoRead(payload, context) {
919
1047
  if (!(!isSuperAdmin && normalizedClient)) return [3 /*break*/, 3];
920
1048
  return [4 /*yield*/, collectionHasClientIndex(db, dbName, collection)];
921
1049
  case 2:
922
- _b = _d.sent();
1050
+ _b = _e.sent();
923
1051
  return [3 /*break*/, 4];
924
1052
  case 3:
925
1053
  _b = false;
926
- _d.label = 4;
1054
+ _e.label = 4;
927
1055
  case 4:
928
1056
  shouldScopeByClient = _b;
929
1057
  clientScopedQuery = shouldScopeByClient
@@ -933,18 +1061,27 @@ function executeAiAssistantMongoRead(payload, context) {
933
1061
  normalized = normalizeAssistantFindOptions(input.options);
934
1062
  return [4 /*yield*/, db.collection(collection).find(scopedQuery, normalized.findOptions).toArray()];
935
1063
  case 5:
936
- documents = _d.sent();
1064
+ documents = _e.sent();
937
1065
  total = null;
938
1066
  if (!normalized.includeTotal) return [3 /*break*/, 7];
939
1067
  return [4 /*yield*/, db.collection(collection).countDocuments(scopedQuery)];
940
1068
  case 6:
941
- total = _d.sent();
942
- _d.label = 7;
1069
+ total = _e.sent();
1070
+ _e.label = 7;
943
1071
  case 7:
944
1072
  sanitizedDocuments = isSuperAdmin
945
1073
  ? documents
946
1074
  : documents.map(function (doc) { return redactSensitiveFields((0, common_1.deepCopy)(doc)); });
947
- return [2 /*return*/, __assign({ documents: sanitizedDocuments, total: total }, (isSuperAdmin ? {
1075
+ includeIds = ((_d = input.options) === null || _d === void 0 ? void 0 : _d.includeIds) === true;
1076
+ display = buildDisplayTable(sanitizedDocuments, {
1077
+ includeIds: includeIds,
1078
+ maxColumns: AI_ASSISTANT_DISPLAY_MAX_COLUMNS,
1079
+ maxRows: normalized.findOptions.limit
1080
+ });
1081
+ if (total !== null) {
1082
+ display.total = total;
1083
+ }
1084
+ return [2 /*return*/, __assign({ documents: sanitizedDocuments, total: total, display: display }, (isSuperAdmin ? {
948
1085
  debug: {
949
1086
  collection: collection,
950
1087
  database: dbName,
@@ -959,10 +1096,10 @@ function executeAiAssistantMongoRead(payload, context) {
959
1096
  }
960
1097
  function executeAiAssistantMongoAggregate(payload, context) {
961
1098
  return __awaiter(this, void 0, void 0, function () {
962
- var input, collection, _a, user, isSuperAdmin, customerId, dbName, db, baseQuery, userId, normalizedClient, shouldScopeByClient, _b, clientScopedQuery, scopedQuery, normalizedPipeline, pipelineWithScope, normalizedOptions, limitedPipeline, documents, executedPipeline, fallbackMeta, fallback, fallbackPipeline, fallbackDocs, unwindFallback, fallbackPipeline, fallbackDocs, sanitizedDocuments;
963
- var _c;
964
- return __generator(this, function (_d) {
965
- switch (_d.label) {
1099
+ var input, collection, _a, user, isSuperAdmin, customerId, dbName, db, baseQuery, userId, normalizedClient, shouldScopeByClient, _b, clientScopedQuery, scopedQuery, normalizedPipeline, pipelineWithScope, normalizedOptions, limitedPipeline, documents, executedPipeline, fallbackMeta, fallback, fallbackPipeline, fallbackDocs, unwindFallback, fallbackPipeline, fallbackDocs, sanitizedDocuments, includeIds, display;
1100
+ var _c, _d;
1101
+ return __generator(this, function (_e) {
1102
+ switch (_e.label) {
966
1103
  case 0:
967
1104
  input = payload || {};
968
1105
  collection = normalizeOptionalString(input.collection);
@@ -971,7 +1108,7 @@ function executeAiAssistantMongoAggregate(payload, context) {
971
1108
  }
972
1109
  return [4 /*yield*/, ensureAssistantReadAccess(context, input.permissionView, collection)];
973
1110
  case 1:
974
- _a = _d.sent(), user = _a.user, isSuperAdmin = _a.isSuperAdmin;
1111
+ _a = _e.sent(), user = _a.user, isSuperAdmin = _a.isSuperAdmin;
975
1112
  if (!isSuperAdmin && AI_ASSISTANT_BLOCKED_COLLECTIONS.has(collection)) {
976
1113
  throw new Error('AI assistant mongo aggregate: Access denied.');
977
1114
  }
@@ -992,11 +1129,11 @@ function executeAiAssistantMongoAggregate(payload, context) {
992
1129
  if (!(!isSuperAdmin && normalizedClient)) return [3 /*break*/, 3];
993
1130
  return [4 /*yield*/, collectionHasClientIndex(db, dbName, collection)];
994
1131
  case 2:
995
- _b = _d.sent();
1132
+ _b = _e.sent();
996
1133
  return [3 /*break*/, 4];
997
1134
  case 3:
998
1135
  _b = false;
999
- _d.label = 4;
1136
+ _e.label = 4;
1000
1137
  case 4:
1001
1138
  shouldScopeByClient = _b;
1002
1139
  clientScopedQuery = shouldScopeByClient
@@ -1014,7 +1151,7 @@ function executeAiAssistantMongoAggregate(payload, context) {
1014
1151
  .aggregate(limitedPipeline, normalizedOptions.aggregateOptions)
1015
1152
  .toArray()];
1016
1153
  case 5:
1017
- documents = _d.sent();
1154
+ documents = _e.sent();
1018
1155
  executedPipeline = limitedPipeline;
1019
1156
  fallbackMeta = {};
1020
1157
  if (!!documents.length) return [3 /*break*/, 7];
@@ -1027,13 +1164,13 @@ function executeAiAssistantMongoAggregate(payload, context) {
1027
1164
  .aggregate(fallbackPipeline, normalizedOptions.aggregateOptions)
1028
1165
  .toArray()];
1029
1166
  case 6:
1030
- fallbackDocs = _d.sent();
1167
+ fallbackDocs = _e.sent();
1031
1168
  if (fallbackDocs.length) {
1032
1169
  documents = fallbackDocs;
1033
1170
  executedPipeline = fallbackPipeline;
1034
1171
  fallbackMeta.dateField.used = true;
1035
1172
  }
1036
- _d.label = 7;
1173
+ _e.label = 7;
1037
1174
  case 7:
1038
1175
  if (!(documents.length <= 1)) return [3 /*break*/, 9];
1039
1176
  unwindFallback = resolveAggregateUnwindFallback(executedPipeline);
@@ -1045,18 +1182,24 @@ function executeAiAssistantMongoAggregate(payload, context) {
1045
1182
  .aggregate(fallbackPipeline, normalizedOptions.aggregateOptions)
1046
1183
  .toArray()];
1047
1184
  case 8:
1048
- fallbackDocs = _d.sent();
1185
+ fallbackDocs = _e.sent();
1049
1186
  if (fallbackDocs.length > documents.length) {
1050
1187
  documents = fallbackDocs;
1051
1188
  executedPipeline = fallbackPipeline;
1052
1189
  fallbackMeta.unwind.used = true;
1053
1190
  }
1054
- _d.label = 9;
1191
+ _e.label = 9;
1055
1192
  case 9:
1056
1193
  sanitizedDocuments = isSuperAdmin
1057
1194
  ? documents
1058
1195
  : documents.map(function (doc) { return redactSensitiveFields((0, common_1.deepCopy)(doc)); });
1059
- return [2 /*return*/, __assign({ documents: sanitizedDocuments }, (isSuperAdmin ? {
1196
+ includeIds = ((_d = input.options) === null || _d === void 0 ? void 0 : _d.includeIds) === true;
1197
+ display = buildDisplayTable(sanitizedDocuments, {
1198
+ includeIds: includeIds,
1199
+ maxColumns: AI_ASSISTANT_DISPLAY_MAX_COLUMNS,
1200
+ maxRows: normalizedOptions.limit || sanitizedDocuments.length
1201
+ });
1202
+ return [2 /*return*/, __assign({ documents: sanitizedDocuments, display: display }, (isSuperAdmin ? {
1060
1203
  debug: {
1061
1204
  collection: collection,
1062
1205
  database: dbName,
@@ -1071,6 +1214,607 @@ function executeAiAssistantMongoAggregate(payload, context) {
1071
1214
  });
1072
1215
  });
1073
1216
  }
1217
+ function extractAssistantMongoDirective(content) {
1218
+ var lines = String(content || '').split('\n');
1219
+ var directiveIndex = -1;
1220
+ var directiveLine = '';
1221
+ var directiveType = null;
1222
+ var directiveIndexes = new Set();
1223
+ lines.forEach(function (line, index) {
1224
+ var normalized = line.trim().replace(/^[-*]+\s*/, '');
1225
+ var upper = normalized.toUpperCase();
1226
+ if (upper.startsWith('MONGO_READ:')) {
1227
+ directiveIndexes.add(index);
1228
+ directiveIndex = index;
1229
+ directiveLine = normalized;
1230
+ directiveType = 'read';
1231
+ return;
1232
+ }
1233
+ if (upper.startsWith('MONGO_AGG:') || upper.startsWith('MONGO_AGGREGATE:')) {
1234
+ directiveIndexes.add(index);
1235
+ directiveIndex = index;
1236
+ directiveLine = normalized;
1237
+ directiveType = 'aggregate';
1238
+ }
1239
+ });
1240
+ if (directiveIndex === -1 || !directiveType) {
1241
+ return null;
1242
+ }
1243
+ var colonIndex = directiveLine.indexOf(':');
1244
+ var after = colonIndex >= 0 ? directiveLine.slice(colonIndex + 1).trim() : '';
1245
+ var parsed = after ? parseJsonObject(after) : null;
1246
+ var payload = parsed && typeof parsed === 'object' ? parsed : null;
1247
+ var cleaned = lines.filter(function (_, index) { return !directiveIndexes.has(index); }).join('\n').trim();
1248
+ return {
1249
+ type: directiveType,
1250
+ payload: payload,
1251
+ cleaned: cleaned,
1252
+ rawLine: directiveLine
1253
+ };
1254
+ }
1255
+ function buildAssistantToolRequest(directive, payload) {
1256
+ var _a;
1257
+ var base = directive.payload && typeof directive.payload === 'object' ? directive.payload : {};
1258
+ var request = __assign({}, base);
1259
+ var route = normalizeOptionalString((_a = payload === null || payload === void 0 ? void 0 : payload.context) === null || _a === void 0 ? void 0 : _a.route);
1260
+ if (!request.permissionView && route) {
1261
+ request.permissionView = route;
1262
+ }
1263
+ if (!request.id_client) {
1264
+ var idClient = normalizeOptionalString(payload === null || payload === void 0 ? void 0 : payload.id_client);
1265
+ if (idClient) {
1266
+ request.id_client = idClient;
1267
+ }
1268
+ }
1269
+ if (!request.mongo && (payload === null || payload === void 0 ? void 0 : payload.mongo)) {
1270
+ request.mongo = payload.mongo;
1271
+ }
1272
+ return request;
1273
+ }
1274
+ function buildAssistantToolResultPayload(directive, toolResponse) {
1275
+ var _a;
1276
+ var directivePayload = directive.payload || {};
1277
+ var documents = Array.isArray(toolResponse === null || toolResponse === void 0 ? void 0 : toolResponse.documents) ? toolResponse.documents : [];
1278
+ var includeIds = ((_a = directivePayload === null || directivePayload === void 0 ? void 0 : directivePayload.options) === null || _a === void 0 ? void 0 : _a.includeIds) === true;
1279
+ var display = (toolResponse === null || toolResponse === void 0 ? void 0 : toolResponse.display) && typeof toolResponse.display === 'object'
1280
+ ? toolResponse.display
1281
+ : buildDisplayTable(documents, {
1282
+ includeIds: includeIds,
1283
+ maxColumns: AI_ASSISTANT_DISPLAY_MAX_COLUMNS,
1284
+ maxRows: AI_ASSISTANT_DISPLAY_PREVIEW_MAX_ROWS
1285
+ });
1286
+ var trimmedDisplay = trimDisplayTable(display, {
1287
+ maxColumns: AI_ASSISTANT_DISPLAY_MAX_COLUMNS,
1288
+ maxRows: AI_ASSISTANT_DISPLAY_PREVIEW_MAX_ROWS
1289
+ });
1290
+ var total = typeof (toolResponse === null || toolResponse === void 0 ? void 0 : toolResponse.total) === 'number' ? toolResponse.total : null;
1291
+ var rowCount = documents.length || trimmedDisplay.rowCount;
1292
+ var collection = normalizeOptionalString(directivePayload === null || directivePayload === void 0 ? void 0 : directivePayload.collection) || '';
1293
+ var result = {
1294
+ type: directive.type === 'aggregate' ? 'mongo_agg' : 'mongo_read',
1295
+ input: directivePayload,
1296
+ output: {
1297
+ display: trimmedDisplay,
1298
+ total: total !== null ? total : undefined,
1299
+ collection: collection || undefined,
1300
+ rowCount: rowCount,
1301
+ columns: trimmedDisplay.columns,
1302
+ truncated: trimmedDisplay.truncated
1303
+ }
1304
+ };
1305
+ return {
1306
+ result: result,
1307
+ prompt: buildAssistantToolResultPrompt(result)
1308
+ };
1309
+ }
1310
+ function buildAssistantToolResultPrompt(result) {
1311
+ var _a, _b;
1312
+ var lines = ['Tool Result:'];
1313
+ lines.push("Type: ".concat(result.type));
1314
+ if (result.output.collection) {
1315
+ lines.push("Collection: ".concat(result.output.collection));
1316
+ }
1317
+ lines.push("Row count: ".concat(result.output.rowCount));
1318
+ if (typeof result.output.total === 'number') {
1319
+ lines.push("Total: ".concat(result.output.total));
1320
+ }
1321
+ if (Array.isArray(result.output.columns) && result.output.columns.length) {
1322
+ lines.push("Columns: ".concat(result.output.columns.join(', ')));
1323
+ }
1324
+ if ((_b = (_a = result.output.display) === null || _a === void 0 ? void 0 : _a.rows) === null || _b === void 0 ? void 0 : _b.length) {
1325
+ lines.push('Preview:');
1326
+ lines.push(formatDisplayTableMarkdown(result.output.display));
1327
+ }
1328
+ else {
1329
+ lines.push('Preview: (no rows)');
1330
+ }
1331
+ return lines.join('\n');
1332
+ }
1333
+ function buildAssistantCodexToolFollowupPrompt(message, attachmentText, historyText, contextText, toolResultText) {
1334
+ var trimmedContext = normalizeOptionalString(contextText);
1335
+ var contextBlock = trimmedContext ? "\n\nContext:\n".concat(trimmedContext) : '';
1336
+ var trimmedHistory = normalizeOptionalString(historyText);
1337
+ var historyBlock = trimmedHistory ? "\n\nConversation so far:\n".concat(trimmedHistory) : '';
1338
+ var toolBlock = toolResultText ? "\n\nTool Result:\n".concat(toolResultText) : '';
1339
+ var instruction = '\n\nInstruction:\nNow answer the user. Do NOT output another MONGO_* directive. Output plain Markdown. Summarize first, then include a Markdown table.';
1340
+ return "System:\n".concat(AI_ASSISTANT_SYSTEM_PROMPT).concat(contextBlock).concat(historyBlock, "\n\nUser:\n").concat(message).concat(attachmentText || '').concat(toolBlock).concat(instruction).trim();
1341
+ }
1342
+ function buildAssistantToolFallbackResponse(result) {
1343
+ var _a, _b;
1344
+ var lines = ['Here is a data snapshot based on the tool result:'];
1345
+ if (result.output.collection) {
1346
+ lines.push("- Collection: ".concat(result.output.collection));
1347
+ }
1348
+ lines.push("- Rows returned: ".concat(result.output.rowCount));
1349
+ if (typeof result.output.total === 'number') {
1350
+ lines.push("- Total: ".concat(result.output.total));
1351
+ }
1352
+ if ((_b = (_a = result.output.display) === null || _a === void 0 ? void 0 : _a.rows) === null || _b === void 0 ? void 0 : _b.length) {
1353
+ lines.push('');
1354
+ lines.push(formatDisplayTableMarkdown(result.output.display));
1355
+ }
1356
+ return lines.join('\n').trim();
1357
+ }
1358
+ function buildAssistantToolErrorMessage(error, directive, request) {
1359
+ var _a, _b;
1360
+ var rawMessage = normalizeOptionalString(error === null || error === void 0 ? void 0 : error.message) || 'Unable to access data.';
1361
+ var normalized = rawMessage.toLowerCase();
1362
+ var routeHint = normalizeOptionalString(request === null || request === void 0 ? void 0 : request.permissionView)
1363
+ || normalizeOptionalString((_a = directive.payload) === null || _a === void 0 ? void 0 : _a.permissionView);
1364
+ var collection = normalizeOptionalString(request === null || request === void 0 ? void 0 : request.collection) || normalizeOptionalString((_b = directive.payload) === null || _b === void 0 ? void 0 : _b.collection);
1365
+ var routeLine = routeHint
1366
+ ? "Open ".concat(routeHint, " in the app to view this data or request access.")
1367
+ : 'Open the related screen in the app to view this data or request access.';
1368
+ if (!routeHint && collection && requiresInvoicePermission(collection)) {
1369
+ routeLine = 'Open /invoice/list or /report/invoice to view this data or request access.';
1370
+ }
1371
+ if (normalized.includes('permission scope required')) {
1372
+ return "I need a permission scope to access that data. ".concat(routeLine);
1373
+ }
1374
+ if (normalized.includes('access denied')) {
1375
+ if (collection && requiresInvoicePermission(collection)) {
1376
+ return "Invoice access is required to view that data. ".concat(routeLine);
1377
+ }
1378
+ return "You don't have permission to view that data. ".concat(routeLine);
1379
+ }
1380
+ if (normalized.includes('database access denied')) {
1381
+ return "Database access is restricted for that request. ".concat(routeLine);
1382
+ }
1383
+ if (normalized.includes('collection is required')) {
1384
+ return 'I need a valid collection to read from. Please specify which screen or dataset you want.';
1385
+ }
1386
+ return "I couldn't access the requested data. ".concat(routeLine);
1387
+ }
1388
+ function normalizeAssistantProgress(items) {
1389
+ var seen = new Set();
1390
+ var cleaned = [];
1391
+ items.forEach(function (item) {
1392
+ var trimmed = String(item || '').trim();
1393
+ if (!trimmed) {
1394
+ return;
1395
+ }
1396
+ var key = trimmed.toLowerCase();
1397
+ if (seen.has(key)) {
1398
+ return;
1399
+ }
1400
+ seen.add(key);
1401
+ cleaned.push(trimmed);
1402
+ });
1403
+ return cleaned;
1404
+ }
1405
+ function updateAssistantProgress(messageId, progress) {
1406
+ return __awaiter(this, void 0, void 0, function () {
1407
+ var normalized, _a;
1408
+ return __generator(this, function (_b) {
1409
+ switch (_b.label) {
1410
+ case 0:
1411
+ normalized = normalizeAssistantProgress(progress);
1412
+ if (!messageId || !normalized.length) {
1413
+ return [2 /*return*/];
1414
+ }
1415
+ _b.label = 1;
1416
+ case 1:
1417
+ _b.trys.push([1, 3, , 4]);
1418
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.updateOne({ _id: messageId, 'metadata.pending': true }, {
1419
+ $set: {
1420
+ 'metadata.pending': true,
1421
+ 'metadata.progress': normalized,
1422
+ updatedAt: new Date()
1423
+ }
1424
+ })];
1425
+ case 2:
1426
+ _b.sent();
1427
+ return [3 /*break*/, 4];
1428
+ case 3:
1429
+ _a = _b.sent();
1430
+ return [3 /*break*/, 4];
1431
+ case 4: return [2 /*return*/];
1432
+ }
1433
+ });
1434
+ });
1435
+ }
1436
+ function createAssistantProgressTracker(messageId, initialProgress) {
1437
+ var _this = this;
1438
+ var progress = normalizeAssistantProgress(initialProgress);
1439
+ var stopped = false;
1440
+ var tickIndex = 0;
1441
+ var updateScheduled = false;
1442
+ var timer = null;
1443
+ var queueProgressUpdate = function () {
1444
+ if (stopped || !messageId || updateScheduled) {
1445
+ return;
1446
+ }
1447
+ updateScheduled = true;
1448
+ setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
1449
+ var _a;
1450
+ return __generator(this, function (_b) {
1451
+ switch (_b.label) {
1452
+ case 0:
1453
+ updateScheduled = false;
1454
+ if (stopped) {
1455
+ return [2 /*return*/];
1456
+ }
1457
+ _b.label = 1;
1458
+ case 1:
1459
+ _b.trys.push([1, 3, , 4]);
1460
+ return [4 /*yield*/, updateAssistantProgress(messageId, progress)];
1461
+ case 2:
1462
+ _b.sent();
1463
+ return [3 /*break*/, 4];
1464
+ case 3:
1465
+ _a = _b.sent();
1466
+ return [3 /*break*/, 4];
1467
+ case 4: return [2 /*return*/];
1468
+ }
1469
+ });
1470
+ }); }, 0);
1471
+ };
1472
+ if (messageId) {
1473
+ timer = setInterval(function () {
1474
+ if (stopped) {
1475
+ if (timer) {
1476
+ clearInterval(timer);
1477
+ timer = null;
1478
+ }
1479
+ return;
1480
+ }
1481
+ if (tickIndex >= AI_ASSISTANT_PROGRESS_TICKS.length) {
1482
+ if (timer) {
1483
+ clearInterval(timer);
1484
+ timer = null;
1485
+ }
1486
+ return;
1487
+ }
1488
+ var next = AI_ASSISTANT_PROGRESS_TICKS[tickIndex++];
1489
+ progress = normalizeAssistantProgress(__spreadArray(__spreadArray([], __read(progress), false), [next], false));
1490
+ queueProgressUpdate();
1491
+ }, AI_ASSISTANT_PROGRESS_TICK_MS);
1492
+ }
1493
+ return {
1494
+ push: function (item) {
1495
+ if (stopped || !messageId) {
1496
+ return;
1497
+ }
1498
+ progress = normalizeAssistantProgress(__spreadArray(__spreadArray([], __read(progress), false), [item], false));
1499
+ queueProgressUpdate();
1500
+ },
1501
+ stop: function () {
1502
+ stopped = true;
1503
+ if (timer) {
1504
+ clearInterval(timer);
1505
+ timer = null;
1506
+ }
1507
+ }
1508
+ };
1509
+ }
1510
+ function buildAssistantCodexErrorMessage(error) {
1511
+ var raw = normalizeOptionalString(error === null || error === void 0 ? void 0 : error.message);
1512
+ if (raw && /timed out/i.test(raw)) {
1513
+ return 'That took longer than expected. Please try again or narrow the request.';
1514
+ }
1515
+ return 'I ran into an internal error while preparing that response. Please try again.';
1516
+ }
1517
+ function isAssistantIdField(key) {
1518
+ var normalized = String(key || '').trim().toLowerCase();
1519
+ if (!normalized) {
1520
+ return false;
1521
+ }
1522
+ if (normalized === '_id' || normalized === '__v') {
1523
+ return true;
1524
+ }
1525
+ if (normalized.startsWith('id_')) {
1526
+ return true;
1527
+ }
1528
+ var parts = normalized.split('.');
1529
+ var last = parts[parts.length - 1] || '';
1530
+ return last.startsWith('id_');
1531
+ }
1532
+ function isMongoObjectId(value) {
1533
+ if (!value || typeof value !== 'object') {
1534
+ return false;
1535
+ }
1536
+ if (typeof value.toHexString === 'function') {
1537
+ return true;
1538
+ }
1539
+ return value._bsontype === 'ObjectId';
1540
+ }
1541
+ function isPlainObject(value) {
1542
+ return !!value && typeof value === 'object' && Object.prototype.toString.call(value) === '[object Object]';
1543
+ }
1544
+ function normalizeColumnPriorityKey(column) {
1545
+ var trimmed = String(column || '').trim();
1546
+ if (!trimmed) {
1547
+ return '';
1548
+ }
1549
+ var base = trimmed.includes('.') ? trimmed.split('.').pop() || trimmed : trimmed;
1550
+ return base.toLowerCase();
1551
+ }
1552
+ function formatDisplayColumnName(column) {
1553
+ var trimmed = String(column || '').trim();
1554
+ if (!trimmed) {
1555
+ return '';
1556
+ }
1557
+ if (trimmed === '_id') {
1558
+ return 'id';
1559
+ }
1560
+ if (trimmed === '_group') {
1561
+ return 'Group';
1562
+ }
1563
+ var normalized = trimmed.replace(/[\s_]+/g, '').toLowerCase();
1564
+ if (normalized === 'createdat') {
1565
+ return 'Created At';
1566
+ }
1567
+ if (normalized === 'updatedat') {
1568
+ return 'Updated At';
1569
+ }
1570
+ return trimmed;
1571
+ }
1572
+ function truncateDisplayText(value, maxLength) {
1573
+ if (maxLength === void 0) { maxLength = AI_ASSISTANT_DISPLAY_STRING_LIMIT; }
1574
+ if (!value) {
1575
+ return value;
1576
+ }
1577
+ if (value.length <= maxLength) {
1578
+ return value;
1579
+ }
1580
+ var sliceLength = Math.max(0, maxLength - 3);
1581
+ return "".concat(value.slice(0, sliceLength), "...");
1582
+ }
1583
+ function serializeMongoValue(value, maxLength) {
1584
+ if (maxLength === void 0) { maxLength = AI_ASSISTANT_DISPLAY_STRING_LIMIT; }
1585
+ if (value === null || value === undefined) {
1586
+ return null;
1587
+ }
1588
+ if (typeof value === 'number' || typeof value === 'boolean') {
1589
+ return value;
1590
+ }
1591
+ if (typeof value === 'string') {
1592
+ return truncateDisplayText(value, maxLength);
1593
+ }
1594
+ if (value instanceof Date) {
1595
+ return value.toISOString();
1596
+ }
1597
+ if (isMongoObjectId(value)) {
1598
+ try {
1599
+ var hex = typeof value.toHexString === 'function' ? value.toHexString() : String(value);
1600
+ var shortened = hex.length > 8 ? hex.slice(0, 8) : hex;
1601
+ return truncateDisplayText(shortened, maxLength);
1602
+ }
1603
+ catch (_a) {
1604
+ return truncateDisplayText(String(value), maxLength);
1605
+ }
1606
+ }
1607
+ if (Array.isArray(value)) {
1608
+ var preview = value.slice(0, 3).map(function (entry) { return serializeMongoValue(entry, maxLength); });
1609
+ var compact = preview.map(function (entry) { return (entry === null || entry === undefined) ? '' : String(entry); }).join(', ');
1610
+ var suffix = value.length > 3 ? ', ...' : '';
1611
+ return truncateDisplayText("[".concat(compact).concat(suffix, "]"), maxLength);
1612
+ }
1613
+ if (typeof value === 'object') {
1614
+ var text = '';
1615
+ try {
1616
+ text = JSON.stringify(value);
1617
+ }
1618
+ catch (_b) {
1619
+ text = String(value);
1620
+ }
1621
+ if (!text || text === '[object Object]') {
1622
+ try {
1623
+ text = JSON.stringify(value, null, 2);
1624
+ }
1625
+ catch (_c) {
1626
+ text = String(value);
1627
+ }
1628
+ }
1629
+ return truncateDisplayText(text, maxLength);
1630
+ }
1631
+ return truncateDisplayText(String(value), maxLength);
1632
+ }
1633
+ function flattenForTable(doc) {
1634
+ var result = {};
1635
+ if (!doc || typeof doc !== 'object') {
1636
+ return result;
1637
+ }
1638
+ if (Object.prototype.hasOwnProperty.call(doc, '_id')) {
1639
+ var idValue_1 = doc._id;
1640
+ if (idValue_1 !== null && idValue_1 !== undefined) {
1641
+ if (isPlainObject(idValue_1) && !isMongoObjectId(idValue_1) && !(idValue_1 instanceof Date)) {
1642
+ Object.keys(idValue_1).forEach(function (key) {
1643
+ if (!key || Object.prototype.hasOwnProperty.call(result, key)) {
1644
+ return;
1645
+ }
1646
+ result[key] = idValue_1[key];
1647
+ });
1648
+ }
1649
+ else if (!Object.prototype.hasOwnProperty.call(result, '_group')) {
1650
+ result._group = idValue_1;
1651
+ }
1652
+ }
1653
+ }
1654
+ Object.keys(doc).forEach(function (key) {
1655
+ if (key === '_id') {
1656
+ return;
1657
+ }
1658
+ var value = doc[key];
1659
+ if (value === undefined) {
1660
+ return;
1661
+ }
1662
+ if (isPlainObject(value) && !isMongoObjectId(value) && !(value instanceof Date)) {
1663
+ var nestedKeys = Object.keys(value);
1664
+ if (nestedKeys.length && nestedKeys.length <= 6) {
1665
+ nestedKeys.forEach(function (nestedKey) {
1666
+ if (!nestedKey) {
1667
+ return;
1668
+ }
1669
+ var nestedValue = value[nestedKey];
1670
+ if (nestedValue === undefined) {
1671
+ return;
1672
+ }
1673
+ result["".concat(key, ".").concat(nestedKey)] = nestedValue;
1674
+ });
1675
+ return;
1676
+ }
1677
+ }
1678
+ result[key] = value;
1679
+ });
1680
+ return result;
1681
+ }
1682
+ function buildDisplayTable(docs, options) {
1683
+ var rowsRaw = Array.isArray(docs) ? docs.map(function (doc) { return flattenForTable(doc); }) : [];
1684
+ var stats = new Map();
1685
+ rowsRaw.forEach(function (row) {
1686
+ Object.keys(row).forEach(function (key) {
1687
+ var value = row[key];
1688
+ var entry = stats.get(key) || { nonEmpty: 0, objectLike: 0 };
1689
+ if (!isEmptyDisplayValue(value)) {
1690
+ entry.nonEmpty += 1;
1691
+ }
1692
+ if (isDisplayObjectLike(value)) {
1693
+ entry.objectLike += 1;
1694
+ }
1695
+ stats.set(key, entry);
1696
+ });
1697
+ });
1698
+ var columns = Array.from(stats.keys()).filter(function (key) {
1699
+ var entry = stats.get(key);
1700
+ if (!entry || entry.nonEmpty === 0) {
1701
+ return false;
1702
+ }
1703
+ if (!(options === null || options === void 0 ? void 0 : options.includeIds) && isAssistantIdField(key)) {
1704
+ return false;
1705
+ }
1706
+ if (entry.objectLike / Math.max(entry.nonEmpty, 1) > 0.5) {
1707
+ return false;
1708
+ }
1709
+ return true;
1710
+ });
1711
+ if (!columns.length && rowsRaw.length) {
1712
+ columns = Object.keys(rowsRaw[0] || {}).filter(function (key) { return (options === null || options === void 0 ? void 0 : options.includeIds) || !isAssistantIdField(key); });
1713
+ }
1714
+ var priorityFields = (options === null || options === void 0 ? void 0 : options.priorityFields) || AI_ASSISTANT_DISPLAY_PRIORITY_FIELDS;
1715
+ var priorityMap = new Map(priorityFields.map(function (field, index) { return [field.toLowerCase(), index]; }));
1716
+ columns.sort(function (a, b) {
1717
+ var _a, _b, _c, _d;
1718
+ var aPriority = (_a = priorityMap.get(normalizeColumnPriorityKey(a))) !== null && _a !== void 0 ? _a : 999;
1719
+ var bPriority = (_b = priorityMap.get(normalizeColumnPriorityKey(b))) !== null && _b !== void 0 ? _b : 999;
1720
+ if (aPriority !== bPriority) {
1721
+ return aPriority - bPriority;
1722
+ }
1723
+ var aCount = ((_c = stats.get(a)) === null || _c === void 0 ? void 0 : _c.nonEmpty) || 0;
1724
+ var bCount = ((_d = stats.get(b)) === null || _d === void 0 ? void 0 : _d.nonEmpty) || 0;
1725
+ if (aCount !== bCount) {
1726
+ return bCount - aCount;
1727
+ }
1728
+ return a.localeCompare(b);
1729
+ });
1730
+ var maxColumns = typeof (options === null || options === void 0 ? void 0 : options.maxColumns) === 'number'
1731
+ ? Math.max(options.maxColumns, 0)
1732
+ : AI_ASSISTANT_DISPLAY_MAX_COLUMNS;
1733
+ if (maxColumns && columns.length > maxColumns) {
1734
+ columns = columns.slice(0, maxColumns);
1735
+ }
1736
+ var columnLabels = columns.map(function (column) { return formatDisplayColumnName(column) || column; });
1737
+ var maxRows = typeof (options === null || options === void 0 ? void 0 : options.maxRows) === 'number'
1738
+ ? Math.max(options.maxRows, 0)
1739
+ : rowsRaw.length;
1740
+ var limitedRows = maxRows ? rowsRaw.slice(0, maxRows) : [];
1741
+ var rows = limitedRows.map(function (row) {
1742
+ var next = {};
1743
+ columns.forEach(function (column, index) {
1744
+ var label = columnLabels[index] || column;
1745
+ next[label] = serializeMongoValue(row[column]);
1746
+ });
1747
+ return next;
1748
+ });
1749
+ return {
1750
+ columns: columnLabels,
1751
+ rows: rows,
1752
+ rowCount: rowsRaw.length,
1753
+ truncated: rowsRaw.length > maxRows || columns.length > maxColumns,
1754
+ includeIds: (options === null || options === void 0 ? void 0 : options.includeIds) === true
1755
+ };
1756
+ }
1757
+ function trimDisplayTable(display, options) {
1758
+ if (!display || !Array.isArray(display.columns)) {
1759
+ return display;
1760
+ }
1761
+ var rowsSource = Array.isArray(display.rows) ? display.rows : [];
1762
+ var maxColumns = typeof (options === null || options === void 0 ? void 0 : options.maxColumns) === 'number'
1763
+ ? Math.max(options.maxColumns, 0)
1764
+ : display.columns.length;
1765
+ var maxRows = typeof (options === null || options === void 0 ? void 0 : options.maxRows) === 'number'
1766
+ ? Math.max(options.maxRows, 0)
1767
+ : rowsSource.length;
1768
+ var columns = maxColumns ? display.columns.slice(0, maxColumns) : [];
1769
+ var rows = maxRows ? rowsSource.slice(0, maxRows).map(function (row) {
1770
+ var next = {};
1771
+ columns.forEach(function (column) {
1772
+ next[column] = row === null || row === void 0 ? void 0 : row[column];
1773
+ });
1774
+ return next;
1775
+ }) : [];
1776
+ return __assign(__assign({}, display), { columns: columns, rows: rows, truncated: display.truncated || display.columns.length > maxColumns || rowsSource.length > maxRows });
1777
+ }
1778
+ function formatDisplayTableMarkdown(display) {
1779
+ if (!display || !Array.isArray(display.columns) || !display.columns.length) {
1780
+ return '';
1781
+ }
1782
+ var header = "| ".concat(display.columns.join(' | '), " |");
1783
+ var separator = "| ".concat(display.columns.map(function () { return '---'; }).join(' | '), " |");
1784
+ var rows = (display.rows || []).map(function (row) {
1785
+ var cells = display.columns.map(function (column) { return escapeMarkdownCell(row === null || row === void 0 ? void 0 : row[column]); });
1786
+ return "| ".concat(cells.join(' | '), " |");
1787
+ });
1788
+ return __spreadArray([header, separator], __read(rows), false).join('\n').trim();
1789
+ }
1790
+ function escapeMarkdownCell(value) {
1791
+ var raw = value === null || value === undefined ? '' : String(value);
1792
+ return raw.replace(/\|/g, '\\|').replace(/\r?\n/g, ' ').trim();
1793
+ }
1794
+ function isEmptyDisplayValue(value) {
1795
+ if (value === null || value === undefined) {
1796
+ return true;
1797
+ }
1798
+ if (typeof value === 'string') {
1799
+ return !value.trim();
1800
+ }
1801
+ if (Array.isArray(value)) {
1802
+ return value.length === 0;
1803
+ }
1804
+ return false;
1805
+ }
1806
+ function isDisplayObjectLike(value) {
1807
+ if (value === null || value === undefined) {
1808
+ return false;
1809
+ }
1810
+ if (value instanceof Date) {
1811
+ return false;
1812
+ }
1813
+ if (isMongoObjectId(value)) {
1814
+ return false;
1815
+ }
1816
+ return typeof value === 'object';
1817
+ }
1074
1818
  function ensureAssistantReadAccess(context, permissionView, collection) {
1075
1819
  return __awaiter(this, void 0, void 0, function () {
1076
1820
  var idUser, user, isSuperAdmin, normalizedPermission, normalizedCollection;
@@ -1131,7 +1875,7 @@ function resolveAssistantDatabaseName(database, mongoConfig) {
1131
1875
  }
1132
1876
  function normalizeAssistantFindOptions(options) {
1133
1877
  var normalized = options || {};
1134
- var projection = normalized.projection && Object.keys(normalized.projection).length ? normalized.projection : undefined;
1878
+ var projection = sanitizeAssistantProjection(normalized.projection);
1135
1879
  var sort = normalized.sort && Object.keys(normalized.sort).length ? normalized.sort : undefined;
1136
1880
  var limit = typeof normalized.limit === 'number'
1137
1881
  ? Math.min(Math.max(normalized.limit, 0), AI_ASSISTANT_MONGO_MAX_LIMIT)
@@ -1147,6 +1891,33 @@ function normalizeAssistantFindOptions(options) {
1147
1891
  includeTotal: normalized.includeTotal === true
1148
1892
  };
1149
1893
  }
1894
+ function sanitizeAssistantProjection(projection) {
1895
+ if (!projection || typeof projection !== 'object') {
1896
+ return undefined;
1897
+ }
1898
+ var keys = Object.keys(projection);
1899
+ if (!keys.length) {
1900
+ return undefined;
1901
+ }
1902
+ var hasInclude = false;
1903
+ var hasExclude = false;
1904
+ keys.forEach(function (key) {
1905
+ var value = projection[key];
1906
+ if (value === 0 || value === false) {
1907
+ hasExclude = true;
1908
+ }
1909
+ else if (value === 1 || value === true) {
1910
+ hasInclude = true;
1911
+ }
1912
+ });
1913
+ if (hasInclude && !hasExclude) {
1914
+ var onlyIds = keys.every(function (key) { return isAssistantIdField(key); });
1915
+ if (onlyIds) {
1916
+ return undefined;
1917
+ }
1918
+ }
1919
+ return projection;
1920
+ }
1150
1921
  function normalizeAssistantAggregatePipeline(pipeline) {
1151
1922
  if (!Array.isArray(pipeline)) {
1152
1923
  return [];
@@ -1635,7 +2406,7 @@ var CodexWorkerBootstrapError = /** @class */ (function (_super) {
1635
2406
  }(Error));
1636
2407
  function runCodexInWorkerThread(prompt, runOptions, config) {
1637
2408
  return __awaiter(this, void 0, void 0, function () {
1638
- var codexClient, workerPath, codexClient, error_1, codexClient;
2409
+ var codexClient, workerPath, codexClient, error_3, codexClient;
1639
2410
  return __generator(this, function (_a) {
1640
2411
  switch (_a.label) {
1641
2412
  case 0:
@@ -1655,11 +2426,11 @@ function runCodexInWorkerThread(prompt, runOptions, config) {
1655
2426
  return [4 /*yield*/, runCodexInWorkerThreadInternal(workerPath, prompt, runOptions, config)];
1656
2427
  case 6: return [2 /*return*/, _a.sent()];
1657
2428
  case 7:
1658
- error_1 = _a.sent();
1659
- if (!(error_1 instanceof CodexWorkerBootstrapError)) {
1660
- throw error_1;
2429
+ error_3 = _a.sent();
2430
+ if (!(error_3 instanceof CodexWorkerBootstrapError)) {
2431
+ throw error_3;
1661
2432
  }
1662
- console.error('Codex worker bootstrap failed, falling back to in-process run.', error_1);
2433
+ console.error('Codex worker bootstrap failed, falling back to in-process run.', error_3);
1663
2434
  codexClient = getAssistantCodexClient();
1664
2435
  return [4 /*yield*/, codexClient.run(prompt, runOptions)];
1665
2436
  case 8: return [2 /*return*/, _a.sent()];
@@ -1696,7 +2467,7 @@ function runCodexInWorkerThreadInternal(workerPath, prompt, runOptions, config)
1696
2467
  timeoutMs = ((sanitizedOptions === null || sanitizedOptions === void 0 ? void 0 : sanitizedOptions.timeoutMs) || resolveCodexTimeoutMs()) + 15000;
1697
2468
  timeoutController = new AbortController();
1698
2469
  timeoutPromise = (function () { return __awaiter(_this, void 0, void 0, function () {
1699
- var error_2;
2470
+ var error_4;
1700
2471
  return __generator(this, function (_a) {
1701
2472
  switch (_a.label) {
1702
2473
  case 0:
@@ -1706,11 +2477,11 @@ function runCodexInWorkerThreadInternal(workerPath, prompt, runOptions, config)
1706
2477
  _a.sent();
1707
2478
  return [2 /*return*/, { type: 'timeout' }];
1708
2479
  case 2:
1709
- error_2 = _a.sent();
1710
- if ((error_2 === null || error_2 === void 0 ? void 0 : error_2.name) === 'AbortError') {
2480
+ error_4 = _a.sent();
2481
+ if ((error_4 === null || error_4 === void 0 ? void 0 : error_4.name) === 'AbortError') {
1711
2482
  return [2 /*return*/, { type: 'aborted' }];
1712
2483
  }
1713
- throw error_2;
2484
+ throw error_4;
1714
2485
  case 3: return [2 /*return*/];
1715
2486
  }
1716
2487
  });
@@ -2068,7 +2839,7 @@ function sanitizeAssistantResponse(value) {
2068
2839
  if (!raw) {
2069
2840
  return 'I could not generate a response. Please try again.';
2070
2841
  }
2071
- var withoutBlocks = raw.replace(/```[\\s\\S]*?```/g, '').replace(/`([^`]+)`/g, '$1').trim();
2842
+ var withoutBlocks = raw.replace(/```[\s\S]*?```/g, '').replace(/`([^`]+)`/g, '$1').trim();
2072
2843
  var stripEstimated = function (line) { return line.replace(/\bEstimated (human )?hours:\s*.*$/i, '').trimEnd(); };
2073
2844
  var isTentativeQueryLine = function (line) {
2074
2845
  var normalized = line.trim().replace(/^[-*•]+\s*/, '');