@resolveio/server-lib 20.13.12 → 20.14.0

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.
Files changed (58) hide show
  1. package/collections/ai-terminal-conversation.collection.d.ts +2 -0
  2. package/collections/ai-terminal-conversation.collection.js +133 -0
  3. package/collections/ai-terminal-conversation.collection.js.map +1 -0
  4. package/collections/ai-terminal-message.collection.d.ts +2 -0
  5. package/collections/ai-terminal-message.collection.js +113 -0
  6. package/collections/ai-terminal-message.collection.js.map +1 -0
  7. package/collections/communication-metric.collection.d.ts +2 -0
  8. package/collections/communication-metric.collection.js +133 -0
  9. package/collections/communication-metric.collection.js.map +1 -0
  10. package/collections/openai-usage-ledger.collection.d.ts +2 -0
  11. package/collections/openai-usage-ledger.collection.js +120 -0
  12. package/collections/openai-usage-ledger.collection.js.map +1 -0
  13. package/managers/communication-metric.manager.d.ts +16 -0
  14. package/managers/communication-metric.manager.js +134 -0
  15. package/managers/communication-metric.manager.js.map +1 -0
  16. package/managers/method.manager.d.ts +2 -0
  17. package/managers/method.manager.js +162 -45
  18. package/managers/method.manager.js.map +1 -1
  19. package/managers/openai-usage-ledger.manager.d.ts +14 -0
  20. package/managers/openai-usage-ledger.manager.js +137 -0
  21. package/managers/openai-usage-ledger.manager.js.map +1 -0
  22. package/managers/subscription.manager.js +2 -0
  23. package/managers/subscription.manager.js.map +1 -1
  24. package/methods/ai-terminal.d.ts +1 -0
  25. package/methods/ai-terminal.js +1200 -0
  26. package/methods/ai-terminal.js.map +1 -0
  27. package/methods/report-builder.js +741 -0
  28. package/methods/report-builder.js.map +1 -1
  29. package/methods.ts +27 -0
  30. package/models/ai-terminal-conversation.model.d.ts +16 -0
  31. package/models/ai-terminal-conversation.model.js +4 -0
  32. package/models/ai-terminal-conversation.model.js.map +1 -0
  33. package/models/ai-terminal-message.model.d.ts +22 -0
  34. package/models/ai-terminal-message.model.js +4 -0
  35. package/models/ai-terminal-message.model.js.map +1 -0
  36. package/models/communication-metric.model.d.ts +20 -0
  37. package/models/communication-metric.model.js +4 -0
  38. package/models/communication-metric.model.js.map +1 -0
  39. package/models/openai-usage-ledger.model.d.ts +14 -0
  40. package/models/openai-usage-ledger.model.js +4 -0
  41. package/models/openai-usage-ledger.model.js.map +1 -0
  42. package/package.json +5 -1
  43. package/public_api.d.ts +13 -0
  44. package/public_api.js +13 -0
  45. package/public_api.js.map +1 -1
  46. package/publications/ai-terminal.d.ts +1 -0
  47. package/publications/ai-terminal.js +58 -0
  48. package/publications/ai-terminal.js.map +1 -0
  49. package/publications.ts +6 -0
  50. package/services/codex-client.d.ts +81 -0
  51. package/services/codex-client.js +991 -0
  52. package/services/codex-client.js.map +1 -0
  53. package/services/openai-client.d.ts +46 -0
  54. package/services/openai-client.js +315 -0
  55. package/services/openai-client.js.map +1 -0
  56. package/util/tokenizer.d.ts +5 -0
  57. package/util/tokenizer.js +35 -0
  58. package/util/tokenizer.js.map +1 -0
@@ -0,0 +1,1200 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __values = (this && this.__values) || function(o) {
39
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
40
+ if (m) return m.call(o);
41
+ if (o && typeof o.length === "number") return {
42
+ next: function () {
43
+ if (o && i >= o.length) o = void 0;
44
+ return { value: o && o[i++], done: !o };
45
+ }
46
+ };
47
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.loadAiTerminalMethods = loadAiTerminalMethods;
51
+ var fs_1 = require("fs");
52
+ var os = require("os");
53
+ var path = require("path");
54
+ var simpl_schema_1 = require("simpl-schema");
55
+ var resolveio_server_app_1 = require("../resolveio-server-app");
56
+ var user_collection_1 = require("../collections/user.collection");
57
+ var ai_terminal_conversation_collection_1 = require("../collections/ai-terminal-conversation.collection");
58
+ var ai_terminal_message_collection_1 = require("../collections/ai-terminal-message.collection");
59
+ var common_1 = require("../util/common");
60
+ var tokenizer_1 = require("../util/tokenizer");
61
+ var openai_client_1 = require("../services/openai-client");
62
+ var codex_client_1 = require("../services/codex-client");
63
+ var openai_usage_ledger_manager_1 = require("../managers/openai-usage-ledger.manager");
64
+ var DEFAULT_MAX_FILE_MB = 50;
65
+ var DEFAULT_MAX_TOTAL_MB = 100;
66
+ var DEFAULT_MAX_ATTACHMENT_CHARS = 12000;
67
+ var DEFAULT_MAX_TOTAL_ATTACHMENT_CHARS = 40000;
68
+ var DEFAULT_CODEX_MODEL = 'gpt-5.2-codex';
69
+ var DEFAULT_CODEX_TIMEOUT_MS = 180000;
70
+ var AI_ASSISTANT_SYSTEM_PROMPT = [
71
+ 'You are the ResolveIO in-app AI assistant running with read-only access to the codebase.',
72
+ '- Do not provide code snippets, full code, or file contents.',
73
+ '- Do not modify files, run destructive commands, or access databases.',
74
+ '- Do not access secrets, credentials, or user data.',
75
+ '- Do not assist with hacking, bypassing security, or abuse.',
76
+ '- Prefer high-level explanations and point to file paths or routes.',
77
+ '- If a request is out of scope, offer to create a support ticket with a short summary.',
78
+ '- Keep responses concise and use low reasoning effort.'
79
+ ].join('\n');
80
+ var assistantCodexClient = null;
81
+ function loadAiTerminalMethods(methodManager) {
82
+ methodManager.methods({
83
+ aiTerminalConversationCreate: {
84
+ check: new simpl_schema_1.default({
85
+ payload: {
86
+ type: Object,
87
+ blackbox: true
88
+ }
89
+ }),
90
+ function: function (payload) {
91
+ return __awaiter(this, void 0, void 0, function () {
92
+ var now, doc, result;
93
+ return __generator(this, function (_a) {
94
+ switch (_a.label) {
95
+ case 0:
96
+ now = new Date();
97
+ doc = {
98
+ id_client: normalizeOptionalString(payload.id_client),
99
+ id_app: normalizeOptionalString(payload.id_app),
100
+ title: normalizeOptionalString(payload.title) || 'New Conversation',
101
+ mode: normalizeConversationMode(payload.mode),
102
+ branch_enabled: payload.branch_enabled === true,
103
+ branch_name: normalizeOptionalString(payload.branch_name),
104
+ status: normalizeConversationStatus(payload.status),
105
+ profile_id: normalizeOptionalString(payload.profile_id),
106
+ metadata: payload.metadata || {},
107
+ last_message_at: undefined,
108
+ last_message_id: undefined,
109
+ createdAt: now,
110
+ updatedAt: now
111
+ };
112
+ return [4 /*yield*/, ai_terminal_conversation_collection_1.AiTerminalConversations.insertOne(doc)];
113
+ case 1:
114
+ result = _a.sent();
115
+ return [2 /*return*/, { id_conversation: result._id }];
116
+ }
117
+ });
118
+ });
119
+ }
120
+ },
121
+ aiTerminalConversationUpdate: {
122
+ check: new simpl_schema_1.default({
123
+ id_conversation: {
124
+ type: String
125
+ },
126
+ patch: {
127
+ type: Object,
128
+ blackbox: true
129
+ }
130
+ }),
131
+ function: function (id_conversation, patch) {
132
+ return __awaiter(this, void 0, void 0, function () {
133
+ var update;
134
+ return __generator(this, function (_a) {
135
+ switch (_a.label) {
136
+ case 0:
137
+ update = {
138
+ updatedAt: new Date()
139
+ };
140
+ if (patch.title !== undefined) {
141
+ update.title = normalizeOptionalString(patch.title);
142
+ }
143
+ if (patch.status !== undefined) {
144
+ update.status = normalizeConversationStatus(patch.status);
145
+ }
146
+ if (patch.branch_enabled !== undefined) {
147
+ update.branch_enabled = !!patch.branch_enabled;
148
+ }
149
+ if (patch.branch_name !== undefined) {
150
+ update.branch_name = normalizeOptionalString(patch.branch_name);
151
+ }
152
+ if (patch.profile_id !== undefined) {
153
+ update.profile_id = normalizeOptionalString(patch.profile_id);
154
+ }
155
+ if (patch.metadata !== undefined) {
156
+ update.metadata = patch.metadata || {};
157
+ }
158
+ return [4 /*yield*/, ai_terminal_conversation_collection_1.AiTerminalConversations.updateOne({ _id: id_conversation }, { $set: update })];
159
+ case 1:
160
+ _a.sent();
161
+ return [2 /*return*/, { id_conversation: id_conversation }];
162
+ }
163
+ });
164
+ });
165
+ }
166
+ },
167
+ aiTerminalConversationDelete: {
168
+ check: new simpl_schema_1.default({
169
+ id_conversation: {
170
+ type: String
171
+ }
172
+ }),
173
+ function: function (id_conversation) {
174
+ return __awaiter(this, void 0, void 0, function () {
175
+ return __generator(this, function (_a) {
176
+ switch (_a.label) {
177
+ case 0: return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.deleteMany({ id_conversation: id_conversation })];
178
+ case 1:
179
+ _a.sent();
180
+ return [4 /*yield*/, ai_terminal_conversation_collection_1.AiTerminalConversations.deleteOne({ _id: id_conversation })];
181
+ case 2:
182
+ _a.sent();
183
+ return [2 /*return*/, { id_conversation: id_conversation }];
184
+ }
185
+ });
186
+ });
187
+ }
188
+ },
189
+ aiTerminalUploadFile: {
190
+ check: new simpl_schema_1.default({
191
+ id_conversation: {
192
+ type: String
193
+ },
194
+ file_name: {
195
+ type: String
196
+ },
197
+ content_base64: {
198
+ type: String
199
+ },
200
+ content_type: {
201
+ type: String,
202
+ optional: true
203
+ },
204
+ size: {
205
+ type: Number
206
+ }
207
+ }),
208
+ function: function (id_conversation, file_name, content_base64, size, content_type) {
209
+ return __awaiter(this, void 0, void 0, function () {
210
+ var limits, safeName, uploadRoot, targetDir, targetPath, dataBuffer, data;
211
+ return __generator(this, function (_a) {
212
+ switch (_a.label) {
213
+ case 0:
214
+ limits = resolveUploadLimits();
215
+ if (size > limits.maxFileBytes) {
216
+ throw new Error("File exceeds ".concat(limits.maxFileMb, "MB limit."));
217
+ }
218
+ safeName = sanitizeFilename(file_name);
219
+ uploadRoot = resolveUploadRoot();
220
+ targetDir = path.join(uploadRoot, id_conversation);
221
+ return [4 /*yield*/, fs_1.promises.mkdir(targetDir, { recursive: true })];
222
+ case 1:
223
+ _a.sent();
224
+ targetPath = path.join(targetDir, safeName);
225
+ dataBuffer = Buffer.from(content_base64, 'base64');
226
+ data = new Uint8Array(dataBuffer);
227
+ if (data.byteLength !== size) {
228
+ if (data.byteLength > limits.maxFileBytes) {
229
+ throw new Error("File exceeds ".concat(limits.maxFileMb, "MB limit."));
230
+ }
231
+ }
232
+ return [4 /*yield*/, fs_1.promises.writeFile(targetPath, data)];
233
+ case 2:
234
+ _a.sent();
235
+ return [2 /*return*/, {
236
+ id_file: (0, common_1.objectIdHexString)(),
237
+ name: safeName,
238
+ size: data.byteLength,
239
+ type: normalizeOptionalString(content_type),
240
+ local_path: targetPath
241
+ }];
242
+ }
243
+ });
244
+ });
245
+ }
246
+ },
247
+ aiTerminalRun: {
248
+ check: new simpl_schema_1.default({
249
+ payload: {
250
+ type: Object,
251
+ blackbox: true
252
+ }
253
+ }),
254
+ function: function (payload) {
255
+ return __awaiter(this, void 0, void 0, function () {
256
+ return __generator(this, function (_a) {
257
+ switch (_a.label) {
258
+ case 0: return [4 /*yield*/, executeAiTerminalRun(payload, this)];
259
+ case 1: return [2 /*return*/, _a.sent()];
260
+ }
261
+ });
262
+ });
263
+ }
264
+ },
265
+ aiCoderTerminalUploadFile: {
266
+ check: new simpl_schema_1.default({
267
+ id_conversation: {
268
+ type: String
269
+ },
270
+ file_name: {
271
+ type: String
272
+ },
273
+ content_base64: {
274
+ type: String
275
+ },
276
+ size: {
277
+ type: Number
278
+ },
279
+ content_type: {
280
+ type: String,
281
+ optional: true
282
+ }
283
+ }),
284
+ function: function (id_conversation, file_name, content_base64, size, content_type) {
285
+ return __awaiter(this, void 0, void 0, function () {
286
+ return __generator(this, function (_a) {
287
+ switch (_a.label) {
288
+ case 0: return [4 /*yield*/, handleCodexUpload(id_conversation, file_name, content_base64, size, content_type)];
289
+ case 1: return [2 /*return*/, _a.sent()];
290
+ }
291
+ });
292
+ });
293
+ }
294
+ },
295
+ aiCoderTerminalRunCodex: {
296
+ check: new simpl_schema_1.default({
297
+ payload: {
298
+ type: Object,
299
+ blackbox: true
300
+ }
301
+ }),
302
+ function: function (payload) {
303
+ return __awaiter(this, void 0, void 0, function () {
304
+ return __generator(this, function (_a) {
305
+ switch (_a.label) {
306
+ case 0: return [4 /*yield*/, executeAiAssistantCodexRun(payload, this)];
307
+ case 1: return [2 /*return*/, _a.sent()];
308
+ }
309
+ });
310
+ });
311
+ }
312
+ },
313
+ aiCoderTerminalDeployTest: {
314
+ check: new simpl_schema_1.default({
315
+ id_conversation: {
316
+ type: String
317
+ }
318
+ }),
319
+ function: function () {
320
+ throw new Error('Test deploys are not supported for AI assistant sessions.');
321
+ }
322
+ }
323
+ });
324
+ }
325
+ function executeAiTerminalRun(payload, context) {
326
+ return __awaiter(this, void 0, void 0, function () {
327
+ var input, message, isSuperAdmin, guardrailsEnabled, guardrail, conversation_1, now_1, userMsg, assistantMsg, conversation, now, attachments, attachmentData, config, systemPrompt, userPromptTemplate, userPrompt, historyLimit, history, _a, messages, openaiSettings, client, response, usage, idClient, userDoc, assistantDoc, insertResult;
328
+ return __generator(this, function (_b) {
329
+ switch (_b.label) {
330
+ case 0:
331
+ input = payload || {};
332
+ message = normalizeOptionalString(input.message);
333
+ if (!message) {
334
+ throw new Error('Message is required.');
335
+ }
336
+ return [4 /*yield*/, resolveIsSuperAdmin(context === null || context === void 0 ? void 0 : context.id_user)];
337
+ case 1:
338
+ isSuperAdmin = _b.sent();
339
+ guardrailsEnabled = input.guardrails !== false && !isSuperAdmin;
340
+ if (!guardrailsEnabled) return [3 /*break*/, 6];
341
+ guardrail = evaluateGuardrails(message);
342
+ if (!(guardrail === null || guardrail === void 0 ? void 0 : guardrail.blocked)) return [3 /*break*/, 6];
343
+ return [4 /*yield*/, ensureConversation(input, 'openai')];
344
+ case 2:
345
+ conversation_1 = _b.sent();
346
+ now_1 = new Date();
347
+ userMsg = {
348
+ id_conversation: conversation_1._id,
349
+ role: 'user',
350
+ content: message,
351
+ createdAt: now_1,
352
+ updatedAt: now_1
353
+ };
354
+ assistantMsg = {
355
+ id_conversation: conversation_1._id,
356
+ role: 'assistant',
357
+ content: guardrail.response,
358
+ metadata: {
359
+ blocked: true,
360
+ reason: guardrail.reason
361
+ },
362
+ createdAt: now_1,
363
+ updatedAt: now_1
364
+ };
365
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(userMsg)];
366
+ case 3:
367
+ _b.sent();
368
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(assistantMsg)];
369
+ case 4:
370
+ _b.sent();
371
+ return [4 /*yield*/, touchConversation(conversation_1._id, now_1)];
372
+ case 5:
373
+ _b.sent();
374
+ return [2 /*return*/, {
375
+ conversation: conversation_1,
376
+ message: assistantMsg,
377
+ guardrails: { blocked: true, reason: guardrail.reason }
378
+ }];
379
+ case 6: return [4 /*yield*/, ensureConversation(input, 'openai')];
380
+ case 7:
381
+ conversation = _b.sent();
382
+ now = new Date();
383
+ attachments = Array.isArray(input.attachments) ? input.attachments : [];
384
+ return [4 /*yield*/, readAttachmentContents(attachments)];
385
+ case 8:
386
+ attachmentData = _b.sent();
387
+ config = sanitizeConfig(input.config || {});
388
+ systemPrompt = normalizeOptionalString(config.system_prompt) || '';
389
+ userPromptTemplate = normalizeOptionalString(config.user_prompt_template);
390
+ userPrompt = buildUserPrompt(userPromptTemplate, message, attachmentData.promptText);
391
+ historyLimit = normalizeHistoryLimit(input.max_history);
392
+ if (!(historyLimit > 0)) return [3 /*break*/, 10];
393
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.find({ id_conversation: conversation._id, role: { $in: ['user', 'assistant'] } }, { sort: { createdAt: 1 }, limit: historyLimit * 2 })];
394
+ case 9:
395
+ _a = _b.sent();
396
+ return [3 /*break*/, 11];
397
+ case 10:
398
+ _a = [];
399
+ _b.label = 11;
400
+ case 11:
401
+ history = _a;
402
+ messages = [];
403
+ if (systemPrompt) {
404
+ messages.push({ role: 'system', content: systemPrompt });
405
+ }
406
+ history.forEach(function (entry) {
407
+ var role = entry.role === 'assistant' ? 'assistant' : 'user';
408
+ var content = normalizeOptionalString(entry.content);
409
+ if (content) {
410
+ messages.push({ role: role, content: content });
411
+ }
412
+ });
413
+ if (userPrompt) {
414
+ messages.push({ role: 'user', content: userPrompt });
415
+ }
416
+ openaiSettings = resolveOpenAISettings(config);
417
+ client = new openai_client_1.OpenAIClient(openaiSettings);
418
+ return [4 /*yield*/, client.chat(messages, { timeoutMs: 60000, responseFormat: config.response_format })];
419
+ case 12:
420
+ response = _b.sent();
421
+ usage = response.usage || estimateUsage(messages, response.content, openaiSettings.model);
422
+ idClient = resolveClientId(conversation, input.id_client);
423
+ if (!idClient) return [3 /*break*/, 14];
424
+ return [4 /*yield*/, (0, openai_usage_ledger_manager_1.recordOpenAIUsage)({
425
+ id_client: idClient,
426
+ model: response.model || openaiSettings.model || 'unknown',
427
+ input_tokens: usage.inputTokens,
428
+ output_tokens: usage.outputTokens,
429
+ total_tokens: usage.totalTokens,
430
+ category: 'ai-terminal',
431
+ id_conversation: conversation._id
432
+ })];
433
+ case 13:
434
+ _b.sent();
435
+ _b.label = 14;
436
+ case 14:
437
+ userDoc = {
438
+ id_conversation: conversation._id,
439
+ role: 'user',
440
+ content: message,
441
+ attachments: attachmentData.attachments,
442
+ createdAt: now,
443
+ updatedAt: now
444
+ };
445
+ assistantDoc = {
446
+ id_conversation: conversation._id,
447
+ role: 'assistant',
448
+ content: response.content,
449
+ usage: {
450
+ model: response.model || openaiSettings.model,
451
+ input_tokens: usage.inputTokens,
452
+ output_tokens: usage.outputTokens,
453
+ total_tokens: usage.totalTokens
454
+ },
455
+ createdAt: now,
456
+ updatedAt: now
457
+ };
458
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(userDoc)];
459
+ case 15:
460
+ _b.sent();
461
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(assistantDoc)];
462
+ case 16:
463
+ insertResult = _b.sent();
464
+ return [4 /*yield*/, touchConversation(conversation._id, now, insertResult._id)];
465
+ case 17:
466
+ _b.sent();
467
+ if (!(input.delete_files_after_run !== false)) return [3 /*break*/, 19];
468
+ return [4 /*yield*/, cleanupAttachments(attachmentData.attachments)];
469
+ case 18:
470
+ _b.sent();
471
+ _b.label = 19;
472
+ case 19: return [2 /*return*/, {
473
+ conversation: conversation,
474
+ message: assistantDoc,
475
+ usage: assistantDoc.usage
476
+ }];
477
+ }
478
+ });
479
+ });
480
+ }
481
+ function executeAiAssistantCodexRun(payload, context) {
482
+ return __awaiter(this, void 0, void 0, function () {
483
+ var input, message, guardrail, conversation_2, now_2, userMsg, assistantMsg, conversation, now, attachments, attachmentData, historyLimit, history, _a, historyLines, prompt, workspaceRoot, codexClient, runOptions, responseText, assistantContent, userDoc, assistantDoc, insertResult;
484
+ return __generator(this, function (_b) {
485
+ switch (_b.label) {
486
+ case 0:
487
+ input = payload || {};
488
+ message = normalizeOptionalString(input.message);
489
+ if (!message) {
490
+ throw new Error('Message is required.');
491
+ }
492
+ if (!(context === null || context === void 0 ? void 0 : context.id_user)) {
493
+ throw new Error('Unauthorized.');
494
+ }
495
+ guardrail = evaluateAssistantGuardrails(message);
496
+ if (!(guardrail === null || guardrail === void 0 ? void 0 : guardrail.blocked)) return [3 /*break*/, 5];
497
+ return [4 /*yield*/, ensureConversation(input, 'codex')];
498
+ case 1:
499
+ conversation_2 = _b.sent();
500
+ now_2 = new Date();
501
+ userMsg = {
502
+ id_conversation: conversation_2._id,
503
+ role: 'user',
504
+ content: message,
505
+ createdAt: now_2,
506
+ updatedAt: now_2
507
+ };
508
+ assistantMsg = {
509
+ id_conversation: conversation_2._id,
510
+ role: 'assistant',
511
+ content: guardrail.response,
512
+ metadata: {
513
+ blocked: true,
514
+ reason: guardrail.reason
515
+ },
516
+ createdAt: now_2,
517
+ updatedAt: now_2
518
+ };
519
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(userMsg)];
520
+ case 2:
521
+ _b.sent();
522
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(assistantMsg)];
523
+ case 3:
524
+ _b.sent();
525
+ return [4 /*yield*/, touchConversation(conversation_2._id, now_2)];
526
+ case 4:
527
+ _b.sent();
528
+ return [2 /*return*/, {
529
+ conversation: conversation_2,
530
+ message: assistantMsg,
531
+ guardrails: { blocked: true, reason: guardrail.reason }
532
+ }];
533
+ case 5: return [4 /*yield*/, ensureConversation(input, 'codex')];
534
+ case 6:
535
+ conversation = _b.sent();
536
+ now = new Date();
537
+ attachments = Array.isArray(input.attachments) ? input.attachments : [];
538
+ return [4 /*yield*/, readAttachmentContents(attachments)];
539
+ case 7:
540
+ attachmentData = _b.sent();
541
+ historyLimit = normalizeHistoryLimit(input.max_history);
542
+ if (!(historyLimit > 0)) return [3 /*break*/, 9];
543
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.find({ id_conversation: conversation._id, role: { $in: ['user', 'assistant'] } }, { sort: { createdAt: 1 }, limit: historyLimit * 2 })];
544
+ case 8:
545
+ _a = _b.sent();
546
+ return [3 /*break*/, 10];
547
+ case 9:
548
+ _a = [];
549
+ _b.label = 10;
550
+ case 10:
551
+ history = _a;
552
+ historyLines = [];
553
+ history.forEach(function (entry) {
554
+ var role = entry.role === 'assistant' ? 'Assistant' : 'User';
555
+ var content = normalizeOptionalString(entry.content);
556
+ if (content) {
557
+ historyLines.push("".concat(role, ": ").concat(content));
558
+ }
559
+ });
560
+ prompt = buildAssistantCodexPrompt(message, attachmentData.promptText, historyLines.join('\n'));
561
+ return [4 /*yield*/, resolveAssistantWorkspaceRoot()];
562
+ case 11:
563
+ workspaceRoot = _b.sent();
564
+ codexClient = getAssistantCodexClient();
565
+ runOptions = {
566
+ timeoutMs: resolveCodexTimeoutMs(),
567
+ threadOptions: {
568
+ workingDirectory: workspaceRoot,
569
+ sandboxMode: 'read-only',
570
+ skipGitRepoCheck: true,
571
+ modelReasoningEffort: 'low',
572
+ networkAccessEnabled: false,
573
+ webSearchMode: 'disabled',
574
+ webSearchEnabled: false,
575
+ approvalPolicy: 'never'
576
+ }
577
+ };
578
+ return [4 /*yield*/, codexClient.run(prompt, runOptions)];
579
+ case 12:
580
+ responseText = _b.sent();
581
+ assistantContent = sanitizeAssistantResponse(responseText);
582
+ userDoc = {
583
+ id_conversation: conversation._id,
584
+ role: 'user',
585
+ content: message,
586
+ attachments: attachmentData.attachments,
587
+ createdAt: now,
588
+ updatedAt: now
589
+ };
590
+ assistantDoc = {
591
+ id_conversation: conversation._id,
592
+ role: 'assistant',
593
+ content: assistantContent,
594
+ metadata: {
595
+ model: resolveCodexModel()
596
+ },
597
+ createdAt: now,
598
+ updatedAt: now
599
+ };
600
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(userDoc)];
601
+ case 13:
602
+ _b.sent();
603
+ return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(assistantDoc)];
604
+ case 14:
605
+ insertResult = _b.sent();
606
+ return [4 /*yield*/, touchConversation(conversation._id, now, insertResult._id)];
607
+ case 15:
608
+ _b.sent();
609
+ if (!(input.delete_files_after_run !== false)) return [3 /*break*/, 17];
610
+ return [4 /*yield*/, cleanupAttachments(attachmentData.attachments)];
611
+ case 16:
612
+ _b.sent();
613
+ _b.label = 17;
614
+ case 17: return [2 /*return*/, {
615
+ conversation: conversation,
616
+ message: assistantDoc
617
+ }];
618
+ }
619
+ });
620
+ });
621
+ }
622
+ function resolveCodexTimeoutMs() {
623
+ var config = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
624
+ var raw = normalizeOptionalNumber(config['AI_ASSISTANT_CODEX_TIMEOUT_MS'] || process.env.AI_ASSISTANT_CODEX_TIMEOUT_MS);
625
+ if (raw && raw > 0) {
626
+ return (0, common_1.round)(raw);
627
+ }
628
+ return DEFAULT_CODEX_TIMEOUT_MS;
629
+ }
630
+ function resolveCodexModel() {
631
+ var config = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
632
+ var raw = normalizeOptionalString(config['AI_ASSISTANT_CODEX_MODEL']
633
+ || process.env.AI_ASSISTANT_CODEX_MODEL
634
+ || config['AI_TERMINAL_CODEX_MODEL']
635
+ || process.env.AI_TERMINAL_CODEX_MODEL
636
+ || config['AI_DASHBOARD_CODEX_MODEL']
637
+ || process.env.AI_DASHBOARD_CODEX_MODEL);
638
+ return raw || DEFAULT_CODEX_MODEL;
639
+ }
640
+ function resolveCodexSettings() {
641
+ var serverConfig = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
642
+ var apiKey = (serverConfig['OPENAI_API_KEY'] || process.env.OPENAI_API_KEY || '').trim();
643
+ if (!apiKey) {
644
+ throw new Error('OpenAI API key missing. Add OPENAI_API_KEY to server config.');
645
+ }
646
+ return {
647
+ apiKey: apiKey,
648
+ baseUrl: (serverConfig['OPENAI_BASE_URL'] || process.env.OPENAI_BASE_URL || '').trim() || undefined,
649
+ model: resolveCodexModel(),
650
+ maxRetries: normalizeOptionalNumber(serverConfig['OPENAI_MAX_RETRIES'] || process.env.OPENAI_MAX_RETRIES),
651
+ retryDelayMs: normalizeOptionalNumber(serverConfig['OPENAI_RETRY_DELAY_MS'] || process.env.OPENAI_RETRY_DELAY_MS)
652
+ };
653
+ }
654
+ function getAssistantCodexClient() {
655
+ if (!assistantCodexClient) {
656
+ assistantCodexClient = new codex_client_1.CodexClient(resolveCodexSettings());
657
+ }
658
+ return assistantCodexClient;
659
+ }
660
+ function resolveAssistantWorkspaceRoot() {
661
+ return __awaiter(this, void 0, void 0, function () {
662
+ var config, configured, candidate, stat, _a;
663
+ return __generator(this, function (_b) {
664
+ switch (_b.label) {
665
+ case 0:
666
+ config = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
667
+ configured = normalizeOptionalString(config['AI_ASSISTANT_WORKSPACE_ROOT']
668
+ || process.env.AI_ASSISTANT_WORKSPACE_ROOT
669
+ || config['CODEX_WORKSPACE_ROOT']
670
+ || process.env.CODEX_WORKSPACE_ROOT);
671
+ candidate = configured || resolveio_server_app_1.ResolveIOServer.getClientDir() || process.cwd();
672
+ stat = null;
673
+ _b.label = 1;
674
+ case 1:
675
+ _b.trys.push([1, 3, , 4]);
676
+ return [4 /*yield*/, fs_1.promises.stat(candidate)];
677
+ case 2:
678
+ stat = _b.sent();
679
+ return [3 /*break*/, 4];
680
+ case 3:
681
+ _a = _b.sent();
682
+ stat = null;
683
+ return [3 /*break*/, 4];
684
+ case 4:
685
+ if (!stat || !stat.isDirectory()) {
686
+ throw new Error('AI assistant workspace root not found.');
687
+ }
688
+ return [2 /*return*/, candidate];
689
+ }
690
+ });
691
+ });
692
+ }
693
+ function buildAssistantCodexPrompt(message, attachmentText, historyText) {
694
+ var trimmedHistory = normalizeOptionalString(historyText);
695
+ var historyBlock = trimmedHistory ? "\n\nConversation so far:\n".concat(trimmedHistory) : '';
696
+ return "System:\n".concat(AI_ASSISTANT_SYSTEM_PROMPT).concat(historyBlock, "\n\nUser:\n").concat(message).concat(attachmentText || '').trim();
697
+ }
698
+ function sanitizeAssistantResponse(value) {
699
+ var raw = normalizeOptionalString(value);
700
+ if (!raw) {
701
+ return 'I could not generate a response. Please try again.';
702
+ }
703
+ var withoutBlocks = raw.replace(/```[\\s\\S]*?```/g, '').replace(/`([^`]+)`/g, '$1').trim();
704
+ if (!withoutBlocks) {
705
+ return 'I can’t share code, but I can point you to files or explain behavior at a high level.';
706
+ }
707
+ return withoutBlocks;
708
+ }
709
+ function evaluateAssistantGuardrails(message) {
710
+ var e_1, _a;
711
+ var normalized = String(message || '').toLowerCase();
712
+ var patterns = [
713
+ {
714
+ pattern: /\b(show|share|paste|provide|dump|output)\b.*\b(code|snippet|file|function|class|script|sql)\b/i,
715
+ reason: 'Code sharing is restricted.',
716
+ response: 'I can’t share code. I can explain behavior and point you to file paths or routes.'
717
+ },
718
+ {
719
+ pattern: /\b(write|generate|create|implement|fix)\b.*\b(code|script|function|class|endpoint|sql)\b/i,
720
+ reason: 'Code generation is restricted.',
721
+ response: 'I can’t generate code. I can explain how the feature works and where to look in the project.'
722
+ },
723
+ {
724
+ pattern: /\b(credentials?|passwords?|secrets?|tokens?)\b/i,
725
+ reason: 'Credentials and secrets are restricted.',
726
+ response: 'I can’t help with credentials or secrets. If you need access, please contact support.'
727
+ },
728
+ {
729
+ pattern: /\b(delete|drop|truncate)\s+(database|db|schema)\b/i,
730
+ reason: 'Database operations are restricted.',
731
+ response: 'I can’t help with database operations. I can explain system behavior or file locations.'
732
+ },
733
+ {
734
+ pattern: /\b(shell|terminal|ssh|sudo|rm\\s+-rf|chmod|chown)\b/i,
735
+ reason: 'System operations are restricted.',
736
+ response: 'I can’t help with system operations. I can explain workflows and where to find related code.'
737
+ },
738
+ {
739
+ pattern: /\b(exploit|bypass|malware|phishing|ransomware|ddos|hack)\b/i,
740
+ reason: 'Security abuse is restricted.',
741
+ response: 'I can’t assist with that request.'
742
+ }
743
+ ];
744
+ try {
745
+ for (var patterns_1 = __values(patterns), patterns_1_1 = patterns_1.next(); !patterns_1_1.done; patterns_1_1 = patterns_1.next()) {
746
+ var entry = patterns_1_1.value;
747
+ if (entry.pattern.test(normalized)) {
748
+ return {
749
+ blocked: true,
750
+ reason: entry.reason,
751
+ response: entry.response
752
+ };
753
+ }
754
+ }
755
+ }
756
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
757
+ finally {
758
+ try {
759
+ if (patterns_1_1 && !patterns_1_1.done && (_a = patterns_1.return)) _a.call(patterns_1);
760
+ }
761
+ finally { if (e_1) throw e_1.error; }
762
+ }
763
+ return null;
764
+ }
765
+ function resolveOpenAISettings(config) {
766
+ var serverConfig = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
767
+ var apiKey = (serverConfig['OPENAI_API_KEY'] || process.env.OPENAI_API_KEY || '').trim();
768
+ if (!apiKey) {
769
+ throw new Error('OpenAI API key missing. Add OPENAI_API_KEY to server config.');
770
+ }
771
+ return {
772
+ apiKey: apiKey,
773
+ baseUrl: (serverConfig['OPENAI_BASE_URL'] || process.env.OPENAI_BASE_URL || '').trim() || undefined,
774
+ model: normalizeOptionalString(config === null || config === void 0 ? void 0 : config.model) || (serverConfig['OPENAI_MODEL'] || process.env.OPENAI_MODEL || 'gpt-5.1').trim(),
775
+ temperature: normalizeOptionalNumber(config === null || config === void 0 ? void 0 : config.temperature),
776
+ maxTokens: normalizeOptionalNumber(config === null || config === void 0 ? void 0 : config.max_tokens),
777
+ maxRetries: normalizeOptionalNumber(serverConfig['OPENAI_MAX_RETRIES'] || process.env.OPENAI_MAX_RETRIES),
778
+ retryDelayMs: normalizeOptionalNumber(serverConfig['OPENAI_RETRY_DELAY_MS'] || process.env.OPENAI_RETRY_DELAY_MS)
779
+ };
780
+ }
781
+ function resolveUploadLimits() {
782
+ var config = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
783
+ var maxFileMb = normalizeOptionalNumber(config['AI_TERMINAL_MAX_FILE_MB'] || process.env.AI_TERMINAL_MAX_FILE_MB) || DEFAULT_MAX_FILE_MB;
784
+ var maxTotalMb = normalizeOptionalNumber(config['AI_TERMINAL_MAX_TOTAL_MB'] || process.env.AI_TERMINAL_MAX_TOTAL_MB) || DEFAULT_MAX_TOTAL_MB;
785
+ return {
786
+ maxFileBytes: (0, common_1.round)(maxFileMb * 1024 * 1024),
787
+ maxFileMb: maxFileMb,
788
+ maxTotalBytes: (0, common_1.round)(maxTotalMb * 1024 * 1024),
789
+ maxTotalMb: maxTotalMb
790
+ };
791
+ }
792
+ function resolveUploadRoot() {
793
+ var config = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
794
+ var configured = normalizeOptionalString(config['AI_TERMINAL_UPLOAD_ROOT'] || process.env.AI_TERMINAL_UPLOAD_ROOT);
795
+ if (configured) {
796
+ return configured;
797
+ }
798
+ return path.join(os.tmpdir(), 'resolveio-ai-terminal-uploads');
799
+ }
800
+ function handleCodexUpload(id_conversation, file_name, content_base64, size, content_type) {
801
+ return __awaiter(this, void 0, void 0, function () {
802
+ var limits, safeName, uploadRoot, targetDir, targetPath, dataBuffer, data;
803
+ return __generator(this, function (_a) {
804
+ switch (_a.label) {
805
+ case 0:
806
+ limits = resolveUploadLimits();
807
+ if (size > limits.maxFileBytes) {
808
+ throw new Error("File exceeds ".concat(limits.maxFileMb, "MB limit."));
809
+ }
810
+ safeName = sanitizeFilename(file_name);
811
+ uploadRoot = resolveUploadRoot();
812
+ targetDir = path.join(uploadRoot, id_conversation);
813
+ return [4 /*yield*/, fs_1.promises.mkdir(targetDir, { recursive: true })];
814
+ case 1:
815
+ _a.sent();
816
+ targetPath = path.join(targetDir, safeName);
817
+ dataBuffer = Buffer.from(content_base64, 'base64');
818
+ data = new Uint8Array(dataBuffer);
819
+ if (data.byteLength > limits.maxFileBytes) {
820
+ throw new Error("File exceeds ".concat(limits.maxFileMb, "MB limit."));
821
+ }
822
+ return [4 /*yield*/, fs_1.promises.writeFile(targetPath, data)];
823
+ case 2:
824
+ _a.sent();
825
+ return [2 /*return*/, {
826
+ id_file: (0, common_1.objectIdHexString)(),
827
+ name: safeName,
828
+ size: data.byteLength,
829
+ type: normalizeOptionalString(content_type),
830
+ local_path: targetPath
831
+ }];
832
+ }
833
+ });
834
+ });
835
+ }
836
+ function readAttachmentContents(attachments) {
837
+ return __awaiter(this, void 0, void 0, function () {
838
+ var limits, totalBytes, totalChars, chunks, cleaned, attachments_1, attachments_1_1, attachment, localPath, safe, stat, ext, name_1, type, readable, content, _a, e_2_1;
839
+ var e_2, _b;
840
+ return __generator(this, function (_c) {
841
+ switch (_c.label) {
842
+ case 0:
843
+ if (!attachments || !attachments.length) {
844
+ return [2 /*return*/, { promptText: '', attachments: [] }];
845
+ }
846
+ limits = resolveAttachmentLimits();
847
+ totalBytes = 0;
848
+ totalChars = 0;
849
+ chunks = [];
850
+ cleaned = [];
851
+ _c.label = 1;
852
+ case 1:
853
+ _c.trys.push([1, 11, 12, 13]);
854
+ attachments_1 = __values(attachments), attachments_1_1 = attachments_1.next();
855
+ _c.label = 2;
856
+ case 2:
857
+ if (!!attachments_1_1.done) return [3 /*break*/, 10];
858
+ attachment = attachments_1_1.value;
859
+ localPath = normalizeOptionalString(attachment.local_path);
860
+ if (!localPath) {
861
+ return [3 /*break*/, 9];
862
+ }
863
+ safe = ensurePathUnderRoot(localPath, resolveUploadRoot());
864
+ if (!safe) {
865
+ return [3 /*break*/, 9];
866
+ }
867
+ _c.label = 3;
868
+ case 3:
869
+ _c.trys.push([3, 8, , 9]);
870
+ return [4 /*yield*/, fs_1.promises.stat(localPath)];
871
+ case 4:
872
+ stat = _c.sent();
873
+ if (!stat.isFile()) {
874
+ return [3 /*break*/, 9];
875
+ }
876
+ totalBytes += stat.size;
877
+ if (totalBytes > limits.maxTotalBytes) {
878
+ return [3 /*break*/, 10];
879
+ }
880
+ ext = path.extname(localPath).toLowerCase();
881
+ name_1 = normalizeOptionalString(attachment.name) || path.basename(localPath);
882
+ type = normalizeOptionalString(attachment.type);
883
+ readable = isReadableAttachment(type, ext);
884
+ content = '';
885
+ if (!readable) return [3 /*break*/, 6];
886
+ return [4 /*yield*/, fs_1.promises.readFile(localPath, 'utf8')];
887
+ case 5:
888
+ content = _c.sent();
889
+ if (content.length > limits.maxAttachmentChars) {
890
+ content = content.slice(0, limits.maxAttachmentChars) + '\n[...truncated]';
891
+ }
892
+ return [3 /*break*/, 7];
893
+ case 6:
894
+ content = "[".concat(name_1, " attached; binary content omitted]");
895
+ _c.label = 7;
896
+ case 7:
897
+ totalChars += content.length;
898
+ if (totalChars > limits.maxTotalAttachmentChars) {
899
+ return [3 /*break*/, 10];
900
+ }
901
+ chunks.push("File: ".concat(name_1, "\n").concat(content));
902
+ cleaned.push({
903
+ id: attachment.id,
904
+ name: name_1,
905
+ type: type,
906
+ size: stat.size,
907
+ local_path: localPath
908
+ });
909
+ return [3 /*break*/, 9];
910
+ case 8:
911
+ _a = _c.sent();
912
+ return [3 /*break*/, 9];
913
+ case 9:
914
+ attachments_1_1 = attachments_1.next();
915
+ return [3 /*break*/, 2];
916
+ case 10: return [3 /*break*/, 13];
917
+ case 11:
918
+ e_2_1 = _c.sent();
919
+ e_2 = { error: e_2_1 };
920
+ return [3 /*break*/, 13];
921
+ case 12:
922
+ try {
923
+ if (attachments_1_1 && !attachments_1_1.done && (_b = attachments_1.return)) _b.call(attachments_1);
924
+ }
925
+ finally { if (e_2) throw e_2.error; }
926
+ return [7 /*endfinally*/];
927
+ case 13: return [2 /*return*/, {
928
+ promptText: chunks.length ? "\n\nAttachments:\n".concat(chunks.join('\n\n')) : '',
929
+ attachments: cleaned
930
+ }];
931
+ }
932
+ });
933
+ });
934
+ }
935
+ function resolveAttachmentLimits() {
936
+ var config = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
937
+ var maxAttachmentChars = normalizeOptionalNumber(config['AI_TERMINAL_MAX_ATTACHMENT_CHARS'] || process.env.AI_TERMINAL_MAX_ATTACHMENT_CHARS)
938
+ || DEFAULT_MAX_ATTACHMENT_CHARS;
939
+ var maxTotalAttachmentChars = normalizeOptionalNumber(config['AI_TERMINAL_MAX_TOTAL_ATTACHMENT_CHARS'] || process.env.AI_TERMINAL_MAX_TOTAL_ATTACHMENT_CHARS)
940
+ || DEFAULT_MAX_TOTAL_ATTACHMENT_CHARS;
941
+ var limits = resolveUploadLimits();
942
+ return {
943
+ maxAttachmentChars: maxAttachmentChars,
944
+ maxTotalAttachmentChars: maxTotalAttachmentChars,
945
+ maxTotalBytes: limits.maxTotalBytes
946
+ };
947
+ }
948
+ function buildUserPrompt(template, inputText, attachmentText) {
949
+ var data = {
950
+ input: inputText || '',
951
+ today: new Date().toISOString().slice(0, 10)
952
+ };
953
+ if (template) {
954
+ return applyTemplate(template, data).trim() + attachmentText;
955
+ }
956
+ return "".concat(inputText || '').concat(attachmentText).trim();
957
+ }
958
+ function applyTemplate(template, data) {
959
+ var output = template;
960
+ Object.keys(data).forEach(function (key) {
961
+ var value = data[key] || '';
962
+ var pattern = new RegExp("{{\\s*".concat(key, "\\s*}}"), 'gi');
963
+ output = output.replace(pattern, value);
964
+ });
965
+ return output;
966
+ }
967
+ function sanitizeConfig(source) {
968
+ return {
969
+ mode: normalizeOptionalString(source === null || source === void 0 ? void 0 : source.mode),
970
+ model: normalizeOptionalString(source === null || source === void 0 ? void 0 : source.model),
971
+ system_prompt: normalizeOptionalString(source === null || source === void 0 ? void 0 : source.system_prompt),
972
+ user_prompt_template: normalizeOptionalString(source === null || source === void 0 ? void 0 : source.user_prompt_template),
973
+ response_format: normalizeOptionalString(source === null || source === void 0 ? void 0 : source.response_format),
974
+ temperature: normalizeOptionalNumber(source === null || source === void 0 ? void 0 : source.temperature),
975
+ max_tokens: normalizeOptionalNumber(source === null || source === void 0 ? void 0 : source.max_tokens)
976
+ };
977
+ }
978
+ function ensureConversation(input, mode) {
979
+ return __awaiter(this, void 0, void 0, function () {
980
+ var idConversation, existing, now, doc, result;
981
+ return __generator(this, function (_a) {
982
+ switch (_a.label) {
983
+ case 0:
984
+ idConversation = normalizeOptionalString(input.id_conversation);
985
+ if (!idConversation) return [3 /*break*/, 2];
986
+ return [4 /*yield*/, ai_terminal_conversation_collection_1.AiTerminalConversations.findById(idConversation)];
987
+ case 1:
988
+ existing = _a.sent();
989
+ if (existing) {
990
+ return [2 /*return*/, existing];
991
+ }
992
+ _a.label = 2;
993
+ case 2:
994
+ now = new Date();
995
+ doc = {
996
+ id_client: normalizeOptionalString(input.id_client),
997
+ id_app: normalizeOptionalString(input.id_app),
998
+ title: 'New Conversation',
999
+ mode: mode,
1000
+ branch_enabled: mode === 'codex',
1001
+ status: normalizeConversationStatus('active'),
1002
+ profile_id: normalizeOptionalString(input.profile_id),
1003
+ metadata: {},
1004
+ createdAt: now,
1005
+ updatedAt: now
1006
+ };
1007
+ return [4 /*yield*/, ai_terminal_conversation_collection_1.AiTerminalConversations.insertOne(doc)];
1008
+ case 3:
1009
+ result = _a.sent();
1010
+ return [2 /*return*/, result];
1011
+ }
1012
+ });
1013
+ });
1014
+ }
1015
+ function touchConversation(idConversation, timestamp, lastMessageId) {
1016
+ return __awaiter(this, void 0, void 0, function () {
1017
+ var update;
1018
+ return __generator(this, function (_a) {
1019
+ switch (_a.label) {
1020
+ case 0:
1021
+ update = {
1022
+ updatedAt: timestamp,
1023
+ last_message_at: timestamp
1024
+ };
1025
+ if (lastMessageId) {
1026
+ update.last_message_id = lastMessageId;
1027
+ }
1028
+ return [4 /*yield*/, ai_terminal_conversation_collection_1.AiTerminalConversations.updateOne({ _id: idConversation }, { $set: update })];
1029
+ case 1:
1030
+ _a.sent();
1031
+ return [2 /*return*/];
1032
+ }
1033
+ });
1034
+ });
1035
+ }
1036
+ function normalizeOptionalString(value) {
1037
+ var raw = typeof value === 'string' ? value.trim() : '';
1038
+ return raw || '';
1039
+ }
1040
+ function normalizeConversationMode(value) {
1041
+ var normalized = normalizeOptionalString(value).toLowerCase();
1042
+ if (normalized === 'codex') {
1043
+ return 'codex';
1044
+ }
1045
+ return 'openai';
1046
+ }
1047
+ function normalizeConversationStatus(value) {
1048
+ var normalized = normalizeOptionalString(value).toLowerCase();
1049
+ if (normalized === 'archived') {
1050
+ return 'archived';
1051
+ }
1052
+ return 'active';
1053
+ }
1054
+ function normalizeOptionalNumber(value) {
1055
+ var parsed = Number(value);
1056
+ return Number.isFinite(parsed) ? parsed : undefined;
1057
+ }
1058
+ function normalizeHistoryLimit(value) {
1059
+ var parsed = Number(value);
1060
+ if (!Number.isFinite(parsed)) {
1061
+ return 12;
1062
+ }
1063
+ return Math.min(Math.max((0, common_1.round)(parsed), 0), 30);
1064
+ }
1065
+ function resolveClientId(conversation, inputClientId) {
1066
+ return normalizeOptionalString(inputClientId) || normalizeOptionalString(conversation === null || conversation === void 0 ? void 0 : conversation.id_client);
1067
+ }
1068
+ function estimateUsage(messages, responseText, model) {
1069
+ var inputTokens = (0, tokenizer_1.countChatTokens)(messages, model);
1070
+ var outputTokens = (0, tokenizer_1.countTokens)(responseText || '', model);
1071
+ return {
1072
+ inputTokens: inputTokens,
1073
+ outputTokens: outputTokens,
1074
+ totalTokens: inputTokens + outputTokens
1075
+ };
1076
+ }
1077
+ function evaluateGuardrails(message) {
1078
+ var e_3, _a;
1079
+ var normalized = String(message || '').toLowerCase();
1080
+ var patterns = [
1081
+ { 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.' },
1082
+ { pattern: /\b(credentials?|passwords?|secrets?|tokens?)\b/i, reason: 'Credentials and secrets are restricted.' },
1083
+ { pattern: /\b(delete|drop)\s+(database|db|schema)\b/i, reason: 'Database operations are restricted.' },
1084
+ { pattern: /\b(shell|terminal|ssh|sudo|rm\s+-rf|chmod|chown)\b/i, reason: 'Server operations are restricted.' },
1085
+ { pattern: /\b(exploit|bypass|malware|phishing|ransomware|ddos)\b/i, reason: 'Security abuse is restricted.' }
1086
+ ];
1087
+ try {
1088
+ for (var patterns_2 = __values(patterns), patterns_2_1 = patterns_2.next(); !patterns_2_1.done; patterns_2_1 = patterns_2.next()) {
1089
+ var entry = patterns_2_1.value;
1090
+ if (entry.pattern.test(normalized)) {
1091
+ return {
1092
+ blocked: true,
1093
+ reason: entry.reason,
1094
+ response: 'I can’t help with that request because it could impact system security or expose protected information. If you need a change, describe the outcome and I can help within approved workflows.'
1095
+ };
1096
+ }
1097
+ }
1098
+ }
1099
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
1100
+ finally {
1101
+ try {
1102
+ if (patterns_2_1 && !patterns_2_1.done && (_a = patterns_2.return)) _a.call(patterns_2);
1103
+ }
1104
+ finally { if (e_3) throw e_3.error; }
1105
+ }
1106
+ return null;
1107
+ }
1108
+ function resolveIsSuperAdmin(id_user) {
1109
+ return __awaiter(this, void 0, void 0, function () {
1110
+ var user, _a;
1111
+ var _b;
1112
+ return __generator(this, function (_c) {
1113
+ switch (_c.label) {
1114
+ case 0:
1115
+ if (!id_user) {
1116
+ return [2 /*return*/, false];
1117
+ }
1118
+ _c.label = 1;
1119
+ case 1:
1120
+ _c.trys.push([1, 3, , 4]);
1121
+ return [4 /*yield*/, user_collection_1.Users.findById(id_user)];
1122
+ case 2:
1123
+ user = _c.sent();
1124
+ return [2 /*return*/, !!((_b = user === null || user === void 0 ? void 0 : user.roles) === null || _b === void 0 ? void 0 : _b.super_admin)];
1125
+ case 3:
1126
+ _a = _c.sent();
1127
+ return [2 /*return*/, false];
1128
+ case 4: return [2 /*return*/];
1129
+ }
1130
+ });
1131
+ });
1132
+ }
1133
+ function sanitizeFilename(value) {
1134
+ var trimmed = (value || '').trim();
1135
+ var base = path.basename(trimmed);
1136
+ return base.replace(/[^\w.\-]/g, '_') || "upload_".concat(Date.now());
1137
+ }
1138
+ function isReadableAttachment(type, ext) {
1139
+ var normalizedType = (type || '').toLowerCase();
1140
+ if (normalizedType.startsWith('text/')) {
1141
+ return true;
1142
+ }
1143
+ if (['application/json', 'application/xml', 'application/csv'].includes(normalizedType)) {
1144
+ return true;
1145
+ }
1146
+ return ['.txt', '.md', '.json', '.csv', '.xml', '.log', '.yml', '.yaml', '.ts', '.tsx', '.js', '.jsx', '.css', '.scss', '.html', '.htm', '.sql'].includes(ext);
1147
+ }
1148
+ function ensurePathUnderRoot(targetPath, root) {
1149
+ var resolvedRoot = path.resolve(root);
1150
+ var resolvedTarget = path.resolve(targetPath);
1151
+ return resolvedTarget.startsWith(resolvedRoot);
1152
+ }
1153
+ function cleanupAttachments(attachments) {
1154
+ return __awaiter(this, void 0, void 0, function () {
1155
+ var uploadRoot, removals;
1156
+ var _this = this;
1157
+ return __generator(this, function (_a) {
1158
+ switch (_a.label) {
1159
+ case 0:
1160
+ if (!attachments || !attachments.length) {
1161
+ return [2 /*return*/];
1162
+ }
1163
+ uploadRoot = resolveUploadRoot();
1164
+ removals = attachments.map(function (attachment) { return __awaiter(_this, void 0, void 0, function () {
1165
+ var localPath, safe, _a;
1166
+ return __generator(this, function (_b) {
1167
+ switch (_b.label) {
1168
+ case 0:
1169
+ localPath = normalizeOptionalString(attachment.local_path);
1170
+ if (!localPath) {
1171
+ return [2 /*return*/];
1172
+ }
1173
+ safe = ensurePathUnderRoot(localPath, uploadRoot);
1174
+ if (!safe) {
1175
+ return [2 /*return*/];
1176
+ }
1177
+ _b.label = 1;
1178
+ case 1:
1179
+ _b.trys.push([1, 3, , 4]);
1180
+ return [4 /*yield*/, fs_1.promises.rm(localPath, { force: true })];
1181
+ case 2:
1182
+ _b.sent();
1183
+ return [3 /*break*/, 4];
1184
+ case 3:
1185
+ _a = _b.sent();
1186
+ return [3 /*break*/, 4];
1187
+ case 4: return [2 /*return*/];
1188
+ }
1189
+ });
1190
+ }); });
1191
+ return [4 /*yield*/, Promise.all(removals)];
1192
+ case 1:
1193
+ _a.sent();
1194
+ return [2 /*return*/];
1195
+ }
1196
+ });
1197
+ });
1198
+ }
1199
+
1200
+ //# sourceMappingURL=ai-terminal.js.map