@resolveio/server-lib 20.14.4 → 20.14.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/methods/ai-terminal.d.ts +21 -0
- package/methods/ai-terminal.js +678 -51
- package/methods/ai-terminal.js.map +1 -1
- package/methods/report-builder.js +378 -24
- package/methods/report-builder.js.map +1 -1
- package/methods.ts +6 -0
- package/package.json +1 -1
package/methods/ai-terminal.js
CHANGED
|
@@ -48,6 +48,7 @@ var __values = (this && this.__values) || function(o) {
|
|
|
48
48
|
};
|
|
49
49
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
50
|
exports.loadAiTerminalMethods = loadAiTerminalMethods;
|
|
51
|
+
exports.executeAiAssistantMongoRead = executeAiAssistantMongoRead;
|
|
51
52
|
var fs_1 = require("fs");
|
|
52
53
|
var os = require("os");
|
|
53
54
|
var path = require("path");
|
|
@@ -67,21 +68,79 @@ var DEFAULT_MAX_ATTACHMENT_CHARS = 12000;
|
|
|
67
68
|
var DEFAULT_MAX_TOTAL_ATTACHMENT_CHARS = 40000;
|
|
68
69
|
var DEFAULT_CODEX_MODEL = 'gpt-5.2-codex';
|
|
69
70
|
var DEFAULT_CODEX_TIMEOUT_MS = 180000;
|
|
71
|
+
var AI_ASSISTANT_MONGO_DEFAULT_LIMIT = 20;
|
|
72
|
+
var AI_ASSISTANT_MONGO_MAX_LIMIT = 200;
|
|
73
|
+
var AI_ASSISTANT_BLOCKED_COLLECTIONS = new Set([
|
|
74
|
+
'user-groups',
|
|
75
|
+
'logged-in-users',
|
|
76
|
+
'ai-terminal-messages',
|
|
77
|
+
'ai-terminal-conversations',
|
|
78
|
+
'openai-usage-ledger',
|
|
79
|
+
'logs',
|
|
80
|
+
'notifications',
|
|
81
|
+
'email-history'
|
|
82
|
+
]);
|
|
83
|
+
var AI_ASSISTANT_SENSITIVE_FIELDS = [
|
|
84
|
+
'password',
|
|
85
|
+
'salt',
|
|
86
|
+
'hash',
|
|
87
|
+
'secret',
|
|
88
|
+
'token',
|
|
89
|
+
'api_key',
|
|
90
|
+
'email',
|
|
91
|
+
'phonenumber',
|
|
92
|
+
'phone',
|
|
93
|
+
'ssn',
|
|
94
|
+
'address',
|
|
95
|
+
'services',
|
|
96
|
+
'roles'
|
|
97
|
+
];
|
|
70
98
|
var AI_ASSISTANT_SYSTEM_PROMPT = [
|
|
71
99
|
'You are the ResolveIO in-app AI assistant running with read-only access to the codebase.',
|
|
72
|
-
'-
|
|
73
|
-
'- Do not modify files, run destructive commands, or access databases.',
|
|
100
|
+
'- Never share code or file contents. All code is proprietary.',
|
|
101
|
+
'- Do not modify files, run destructive commands, or access databases directly.',
|
|
102
|
+
'- Read-only Mongo access is allowed only via the MONGO_READ directive (see below).',
|
|
74
103
|
'- Do not access secrets, credentials, or user data.',
|
|
75
104
|
'- Do not assist with hacking, bypassing security, or abuse.',
|
|
76
105
|
'- Prefer high-level explanations and point to routes instead of code. Only mention file paths if explicitly requested.',
|
|
77
106
|
'- 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.',
|
|
107
|
+
'- When asked "why is this happening," respond with: cause, trigger, data source(s), and expected vs actual outcome.',
|
|
108
|
+
'- For troubleshooting, ask 2-3 targeted questions first, then give a short decision tree (If X, do Y; If not, do Z).',
|
|
109
|
+
'- Provide checklists for common tasks, highlighting required fields and common pitfalls.',
|
|
110
|
+
'- If asked "where is this set," give the screen/workflow name and navigation steps to reach it.',
|
|
111
|
+
'- If asked "what changed," summarize release notes if known; if not available, say so and suggest where to check or offer a support ticket.',
|
|
112
|
+
'- Suggest 1-2 related screens or next steps when it helps.',
|
|
113
|
+
'- If access is blocked, name the permission/role needed and how to request it.',
|
|
78
114
|
'- Avoid vague labels like "Operations app"; use the specific screen/workflow name.',
|
|
79
115
|
'- Do not mention other client projects or ask which client; stay within the current project context.',
|
|
80
116
|
'- Respond with a single concise message. Do not add labels like "Customer-facing summary" or "Work ticket summary" and do not include estimated hours.',
|
|
117
|
+
'- 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.',
|
|
118
|
+
'- When giving steps, include navigation guidance like "Go to <screen>" and "Click/Select/Enter <field>" so the user can follow it exactly.',
|
|
81
119
|
'- If context scope is provided (ex: current page), prioritize that screen in your answer.',
|
|
82
120
|
'- If a request is out of scope, offer to create a support ticket with a short summary.',
|
|
121
|
+
'- If the user explicitly asks to create/open/file a support ticket, end your response with a single line exactly in this format:',
|
|
122
|
+
'- SUPPORT_TICKET_CREATE: <one-line summary>',
|
|
123
|
+
'- Only include that line when the user clearly wants a ticket created. Do not claim a ticket is created unless you include that line.',
|
|
124
|
+
'- If you need database data to answer, end your response with a single line exactly in this format:',
|
|
125
|
+
'- MONGO_READ: {"collection":"<name>","query":{...},"options":{"projection":{...},"sort":{...},"limit":20},"permissionView":"</route>"}',
|
|
126
|
+
'- For invoice data, set permissionView to an invoice route (ex: /invoice/list or /report/invoice).',
|
|
127
|
+
'- Keep queries minimal, read-only, and avoid user/credential data unless the user is a super admin.',
|
|
128
|
+
'- Assume you are not a super admin unless explicitly told otherwise.',
|
|
129
|
+
'- Only request data when the user has permission for that module; invoice data requires invoice view access.',
|
|
130
|
+
'- If the user lacks permission, answer without data and explain how to view it in the app or request access.',
|
|
131
|
+
'- Use MONGO_READ only to produce summaries/snapshots/health checks (not raw dumps) when permitted.',
|
|
132
|
+
'- When referencing data, summarize it in bullets and avoid raw JSON or dumps.',
|
|
83
133
|
'- Keep responses concise and use low reasoning effort.'
|
|
84
134
|
].join('\n');
|
|
135
|
+
var AI_FORM_PATCH_SYSTEM_PROMPT = [
|
|
136
|
+
'You are the ResolveIO form patch assistant.',
|
|
137
|
+
'- Your job is to map the user request to the provided form fields.',
|
|
138
|
+
'- Only use the allowed fields and their types.',
|
|
139
|
+
'- Return ONLY valid JSON and nothing else.',
|
|
140
|
+
'- If you cannot map the request, return an empty patch with a short note.',
|
|
141
|
+
'- Use ISO 8601 for dates and true/false for booleans.',
|
|
142
|
+
'- Use medium reasoning effort.'
|
|
143
|
+
].join('\n');
|
|
85
144
|
var assistantCodexClient = null;
|
|
86
145
|
function loadAiTerminalMethods(methodManager) {
|
|
87
146
|
methodManager.methods({
|
|
@@ -267,6 +326,24 @@ function loadAiTerminalMethods(methodManager) {
|
|
|
267
326
|
});
|
|
268
327
|
}
|
|
269
328
|
},
|
|
329
|
+
aiFormPatch: {
|
|
330
|
+
check: new simpl_schema_1.default({
|
|
331
|
+
payload: {
|
|
332
|
+
type: Object,
|
|
333
|
+
blackbox: true
|
|
334
|
+
}
|
|
335
|
+
}),
|
|
336
|
+
function: function (payload) {
|
|
337
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
338
|
+
return __generator(this, function (_a) {
|
|
339
|
+
switch (_a.label) {
|
|
340
|
+
case 0: return [4 /*yield*/, executeAiFormPatch(payload, this)];
|
|
341
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
},
|
|
270
347
|
aiCoderTerminalUploadFile: {
|
|
271
348
|
check: new simpl_schema_1.default({
|
|
272
349
|
id_conversation: {
|
|
@@ -315,6 +392,24 @@ function loadAiTerminalMethods(methodManager) {
|
|
|
315
392
|
});
|
|
316
393
|
}
|
|
317
394
|
},
|
|
395
|
+
aiAssistantMongoRead: {
|
|
396
|
+
check: new simpl_schema_1.default({
|
|
397
|
+
payload: {
|
|
398
|
+
type: Object,
|
|
399
|
+
blackbox: true
|
|
400
|
+
}
|
|
401
|
+
}),
|
|
402
|
+
function: function (payload) {
|
|
403
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
404
|
+
return __generator(this, function (_a) {
|
|
405
|
+
switch (_a.label) {
|
|
406
|
+
case 0: return [4 /*yield*/, executeAiAssistantMongoRead(payload, this)];
|
|
407
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
},
|
|
318
413
|
aiCoderTerminalDeployTest: {
|
|
319
414
|
check: new simpl_schema_1.default({
|
|
320
415
|
id_conversation: {
|
|
@@ -483,11 +578,79 @@ function executeAiTerminalRun(payload, context) {
|
|
|
483
578
|
});
|
|
484
579
|
});
|
|
485
580
|
}
|
|
486
|
-
function
|
|
581
|
+
function executeAiFormPatch(payload, context) {
|
|
487
582
|
return __awaiter(this, void 0, void 0, function () {
|
|
488
|
-
var input, message,
|
|
583
|
+
var input, message, allowedFields, guardrail, systemPrompt, userPrompt, messages, openaiSettings, client, response, usage, idClient, parsed;
|
|
584
|
+
var _a;
|
|
489
585
|
return __generator(this, function (_b) {
|
|
490
586
|
switch (_b.label) {
|
|
587
|
+
case 0:
|
|
588
|
+
input = payload || {};
|
|
589
|
+
message = normalizeOptionalString(input.message);
|
|
590
|
+
if (!message) {
|
|
591
|
+
throw new Error('Message is required.');
|
|
592
|
+
}
|
|
593
|
+
if (!(context === null || context === void 0 ? void 0 : context.id_user)) {
|
|
594
|
+
throw new Error('Unauthorized.');
|
|
595
|
+
}
|
|
596
|
+
allowedFields = normalizeAiFormFields(input.allowed_fields || input.fields);
|
|
597
|
+
if (!allowedFields.length) {
|
|
598
|
+
throw new Error('Allowed fields are required.');
|
|
599
|
+
}
|
|
600
|
+
guardrail = evaluateGuardrails(message);
|
|
601
|
+
if (guardrail === null || guardrail === void 0 ? void 0 : guardrail.blocked) {
|
|
602
|
+
return [2 /*return*/, { error: guardrail.response, blocked: true }];
|
|
603
|
+
}
|
|
604
|
+
systemPrompt = buildAiFormSystemPrompt();
|
|
605
|
+
userPrompt = buildAiFormUserPrompt(message, allowedFields, input.patch_format, input.route);
|
|
606
|
+
messages = [
|
|
607
|
+
{ role: 'system', content: systemPrompt },
|
|
608
|
+
{ role: 'user', content: userPrompt }
|
|
609
|
+
];
|
|
610
|
+
openaiSettings = resolveOpenAISettings(input.config || {});
|
|
611
|
+
client = new openai_client_1.OpenAIClient(openaiSettings);
|
|
612
|
+
return [4 /*yield*/, client.chat(messages, { timeoutMs: 60000, responseFormat: 'json_object' })];
|
|
613
|
+
case 1:
|
|
614
|
+
response = _b.sent();
|
|
615
|
+
usage = response.usage || estimateUsage(messages, response.content, response.model || openaiSettings.model);
|
|
616
|
+
idClient = normalizeOptionalString(input.id_client);
|
|
617
|
+
if (!idClient) return [3 /*break*/, 3];
|
|
618
|
+
return [4 /*yield*/, (0, openai_usage_ledger_manager_1.recordOpenAIUsage)({
|
|
619
|
+
id_client: idClient,
|
|
620
|
+
model: response.model || openaiSettings.model || 'unknown',
|
|
621
|
+
input_tokens: usage.inputTokens,
|
|
622
|
+
output_tokens: usage.outputTokens,
|
|
623
|
+
total_tokens: usage.totalTokens,
|
|
624
|
+
category: 'ai-form'
|
|
625
|
+
})];
|
|
626
|
+
case 2:
|
|
627
|
+
_b.sent();
|
|
628
|
+
_b.label = 3;
|
|
629
|
+
case 3:
|
|
630
|
+
parsed = parseJsonObject(response.content);
|
|
631
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
632
|
+
throw new Error('AI form patch response was not valid JSON.');
|
|
633
|
+
}
|
|
634
|
+
return [2 /*return*/, {
|
|
635
|
+
patch: (_a = parsed.patch) !== null && _a !== void 0 ? _a : parsed,
|
|
636
|
+
notes: normalizeOptionalString(parsed.notes) || undefined,
|
|
637
|
+
usage: {
|
|
638
|
+
model: response.model || openaiSettings.model,
|
|
639
|
+
input_tokens: usage.inputTokens,
|
|
640
|
+
output_tokens: usage.outputTokens,
|
|
641
|
+
total_tokens: usage.totalTokens
|
|
642
|
+
}
|
|
643
|
+
}];
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
function executeAiAssistantCodexRun(payload, context) {
|
|
649
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
650
|
+
var input, message, guardrail, conversation_2, now_2, userMsg, assistantMsg, user, isSuperAdmin, hasInvoiceAccess, conversation, now, attachments, attachmentData, historyLimit, history, _a, historyLines, prompt, workspaceRoot, codexClient, runOptions, responseText, assistantContent, userDoc, assistantDoc, insertResult;
|
|
651
|
+
var _b;
|
|
652
|
+
return __generator(this, function (_c) {
|
|
653
|
+
switch (_c.label) {
|
|
491
654
|
case 0:
|
|
492
655
|
input = payload || {};
|
|
493
656
|
message = normalizeOptionalString(input.message);
|
|
@@ -501,7 +664,7 @@ function executeAiAssistantCodexRun(payload, context) {
|
|
|
501
664
|
if (!(guardrail === null || guardrail === void 0 ? void 0 : guardrail.blocked)) return [3 /*break*/, 5];
|
|
502
665
|
return [4 /*yield*/, ensureConversation(input, 'codex')];
|
|
503
666
|
case 1:
|
|
504
|
-
conversation_2 =
|
|
667
|
+
conversation_2 = _c.sent();
|
|
505
668
|
now_2 = new Date();
|
|
506
669
|
userMsg = {
|
|
507
670
|
id_conversation: conversation_2._id,
|
|
@@ -523,36 +686,41 @@ function executeAiAssistantCodexRun(payload, context) {
|
|
|
523
686
|
};
|
|
524
687
|
return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(userMsg)];
|
|
525
688
|
case 2:
|
|
526
|
-
|
|
689
|
+
_c.sent();
|
|
527
690
|
return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(assistantMsg)];
|
|
528
691
|
case 3:
|
|
529
|
-
|
|
692
|
+
_c.sent();
|
|
530
693
|
return [4 /*yield*/, touchConversation(conversation_2._id, now_2)];
|
|
531
694
|
case 4:
|
|
532
|
-
|
|
695
|
+
_c.sent();
|
|
533
696
|
return [2 /*return*/, {
|
|
534
697
|
conversation: conversation_2,
|
|
535
698
|
message: assistantMsg,
|
|
536
699
|
guardrails: { blocked: true, reason: guardrail.reason }
|
|
537
700
|
}];
|
|
538
|
-
case 5: return [4 /*yield*/,
|
|
701
|
+
case 5: return [4 /*yield*/, user_collection_1.Users.findById(context === null || context === void 0 ? void 0 : context.id_user)];
|
|
539
702
|
case 6:
|
|
540
|
-
|
|
703
|
+
user = _c.sent();
|
|
704
|
+
isSuperAdmin = !!((_b = user === null || user === void 0 ? void 0 : user.roles) === null || _b === void 0 ? void 0 : _b.super_admin);
|
|
705
|
+
hasInvoiceAccess = userHasInvoiceAccess(user);
|
|
706
|
+
return [4 /*yield*/, ensureConversation(input, 'codex')];
|
|
707
|
+
case 7:
|
|
708
|
+
conversation = _c.sent();
|
|
541
709
|
now = new Date();
|
|
542
710
|
attachments = Array.isArray(input.attachments) ? input.attachments : [];
|
|
543
711
|
return [4 /*yield*/, readAttachmentContents(attachments)];
|
|
544
|
-
case
|
|
545
|
-
attachmentData =
|
|
712
|
+
case 8:
|
|
713
|
+
attachmentData = _c.sent();
|
|
546
714
|
historyLimit = normalizeHistoryLimit(input.max_history);
|
|
547
|
-
if (!(historyLimit > 0)) return [3 /*break*/,
|
|
715
|
+
if (!(historyLimit > 0)) return [3 /*break*/, 10];
|
|
548
716
|
return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.find({ id_conversation: conversation._id, role: { $in: ['user', 'assistant'] } }, { sort: { createdAt: 1 }, limit: historyLimit * 2 })];
|
|
549
|
-
case 8:
|
|
550
|
-
_a = _b.sent();
|
|
551
|
-
return [3 /*break*/, 10];
|
|
552
717
|
case 9:
|
|
553
|
-
_a =
|
|
554
|
-
|
|
718
|
+
_a = _c.sent();
|
|
719
|
+
return [3 /*break*/, 11];
|
|
555
720
|
case 10:
|
|
721
|
+
_a = [];
|
|
722
|
+
_c.label = 11;
|
|
723
|
+
case 11:
|
|
556
724
|
history = _a;
|
|
557
725
|
historyLines = [];
|
|
558
726
|
history.forEach(function (entry) {
|
|
@@ -562,10 +730,10 @@ function executeAiAssistantCodexRun(payload, context) {
|
|
|
562
730
|
historyLines.push("".concat(role, ": ").concat(content));
|
|
563
731
|
}
|
|
564
732
|
});
|
|
565
|
-
prompt = buildAssistantCodexPrompt(message, attachmentData.promptText, historyLines.join('\n'), buildAssistantContext(input));
|
|
733
|
+
prompt = buildAssistantCodexPrompt(message, attachmentData.promptText, historyLines.join('\n'), buildAssistantContext(input, { isSuperAdmin: isSuperAdmin, hasInvoiceAccess: hasInvoiceAccess }));
|
|
566
734
|
return [4 /*yield*/, resolveAssistantWorkspaceRoot()];
|
|
567
|
-
case
|
|
568
|
-
workspaceRoot =
|
|
735
|
+
case 12:
|
|
736
|
+
workspaceRoot = _c.sent();
|
|
569
737
|
codexClient = getAssistantCodexClient();
|
|
570
738
|
runOptions = {
|
|
571
739
|
timeoutMs: resolveCodexTimeoutMs(),
|
|
@@ -581,8 +749,8 @@ function executeAiAssistantCodexRun(payload, context) {
|
|
|
581
749
|
}
|
|
582
750
|
};
|
|
583
751
|
return [4 /*yield*/, codexClient.run(prompt, runOptions)];
|
|
584
|
-
case
|
|
585
|
-
responseText =
|
|
752
|
+
case 13:
|
|
753
|
+
responseText = _c.sent();
|
|
586
754
|
assistantContent = sanitizeAssistantResponse(responseText);
|
|
587
755
|
userDoc = {
|
|
588
756
|
id_conversation: conversation._id,
|
|
@@ -603,20 +771,20 @@ function executeAiAssistantCodexRun(payload, context) {
|
|
|
603
771
|
updatedAt: now
|
|
604
772
|
};
|
|
605
773
|
return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(userDoc)];
|
|
606
|
-
case 13:
|
|
607
|
-
_b.sent();
|
|
608
|
-
return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(assistantDoc)];
|
|
609
774
|
case 14:
|
|
610
|
-
|
|
611
|
-
return [4 /*yield*/,
|
|
775
|
+
_c.sent();
|
|
776
|
+
return [4 /*yield*/, ai_terminal_message_collection_1.AiTerminalMessages.insertOne(assistantDoc)];
|
|
612
777
|
case 15:
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
return [4 /*yield*/, cleanupAttachments(attachmentData.attachments)];
|
|
778
|
+
insertResult = _c.sent();
|
|
779
|
+
return [4 /*yield*/, touchConversation(conversation._id, now, insertResult._id)];
|
|
616
780
|
case 16:
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
781
|
+
_c.sent();
|
|
782
|
+
if (!(input.delete_files_after_run !== false)) return [3 /*break*/, 18];
|
|
783
|
+
return [4 /*yield*/, cleanupAttachments(attachmentData.attachments)];
|
|
784
|
+
case 17:
|
|
785
|
+
_c.sent();
|
|
786
|
+
_c.label = 18;
|
|
787
|
+
case 18: return [2 /*return*/, {
|
|
620
788
|
conversation: conversation,
|
|
621
789
|
message: assistantDoc
|
|
622
790
|
}];
|
|
@@ -624,6 +792,243 @@ function executeAiAssistantCodexRun(payload, context) {
|
|
|
624
792
|
});
|
|
625
793
|
});
|
|
626
794
|
}
|
|
795
|
+
function executeAiAssistantMongoRead(payload, context) {
|
|
796
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
797
|
+
var input, collection, _a, user, isSuperAdmin, dbName, db, baseQuery, userId, scopedQuery, normalized, documents, total, sanitizedDocuments;
|
|
798
|
+
return __generator(this, function (_b) {
|
|
799
|
+
switch (_b.label) {
|
|
800
|
+
case 0:
|
|
801
|
+
input = payload || {};
|
|
802
|
+
collection = normalizeOptionalString(input.collection);
|
|
803
|
+
if (!collection) {
|
|
804
|
+
throw new Error('AI assistant mongo read: Collection is required.');
|
|
805
|
+
}
|
|
806
|
+
return [4 /*yield*/, ensureAssistantReadAccess(context, input.permissionView, collection)];
|
|
807
|
+
case 1:
|
|
808
|
+
_a = _b.sent(), user = _a.user, isSuperAdmin = _a.isSuperAdmin;
|
|
809
|
+
if (!isSuperAdmin && AI_ASSISTANT_BLOCKED_COLLECTIONS.has(collection)) {
|
|
810
|
+
throw new Error('AI assistant mongo read: Access denied.');
|
|
811
|
+
}
|
|
812
|
+
dbName = resolveAssistantDatabaseName(input.database, input.mongo);
|
|
813
|
+
db = resolveio_server_app_1.ResolveIOServer.getMongoConnection().db(dbName);
|
|
814
|
+
baseQuery = normalizeMongoQuery(input.query);
|
|
815
|
+
if (!isSuperAdmin && (collection === 'users' || collection === 'user-versions')) {
|
|
816
|
+
userId = normalizeOptionalString(user === null || user === void 0 ? void 0 : user._id);
|
|
817
|
+
if (!userId) {
|
|
818
|
+
throw new Error('AI assistant mongo read: Access denied.');
|
|
819
|
+
}
|
|
820
|
+
baseQuery = {
|
|
821
|
+
$and: [baseQuery, { _id: userId }]
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
scopedQuery = applyClientScopeFilter(baseQuery, input.id_client, isSuperAdmin);
|
|
825
|
+
normalized = normalizeAssistantFindOptions(input.options);
|
|
826
|
+
return [4 /*yield*/, db.collection(collection).find(scopedQuery, normalized.findOptions).toArray()];
|
|
827
|
+
case 2:
|
|
828
|
+
documents = _b.sent();
|
|
829
|
+
total = null;
|
|
830
|
+
if (!normalized.includeTotal) return [3 /*break*/, 4];
|
|
831
|
+
return [4 /*yield*/, db.collection(collection).countDocuments(scopedQuery)];
|
|
832
|
+
case 3:
|
|
833
|
+
total = _b.sent();
|
|
834
|
+
_b.label = 4;
|
|
835
|
+
case 4:
|
|
836
|
+
sanitizedDocuments = isSuperAdmin
|
|
837
|
+
? documents
|
|
838
|
+
: documents.map(function (doc) { return redactSensitiveFields((0, common_1.deepCopy)(doc)); });
|
|
839
|
+
return [2 /*return*/, {
|
|
840
|
+
documents: sanitizedDocuments,
|
|
841
|
+
total: total
|
|
842
|
+
}];
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
function ensureAssistantReadAccess(context, permissionView, collection) {
|
|
848
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
849
|
+
var idUser, user, isSuperAdmin, normalizedPermission, normalizedCollection;
|
|
850
|
+
var _a;
|
|
851
|
+
return __generator(this, function (_b) {
|
|
852
|
+
switch (_b.label) {
|
|
853
|
+
case 0:
|
|
854
|
+
idUser = context === null || context === void 0 ? void 0 : context.id_user;
|
|
855
|
+
if (!idUser) {
|
|
856
|
+
throw new Error('AI assistant mongo read: Unauthorized.');
|
|
857
|
+
}
|
|
858
|
+
return [4 /*yield*/, user_collection_1.Users.findById(idUser)];
|
|
859
|
+
case 1:
|
|
860
|
+
user = _b.sent();
|
|
861
|
+
if (!user) {
|
|
862
|
+
throw new Error('AI assistant mongo read: Unauthorized.');
|
|
863
|
+
}
|
|
864
|
+
isSuperAdmin = !!((_a = user === null || user === void 0 ? void 0 : user.roles) === null || _a === void 0 ? void 0 : _a.super_admin);
|
|
865
|
+
normalizedPermission = normalizeOptionalString(permissionView);
|
|
866
|
+
if (!isSuperAdmin) {
|
|
867
|
+
if (!normalizedPermission) {
|
|
868
|
+
throw new Error('AI assistant mongo read: Permission scope required.');
|
|
869
|
+
}
|
|
870
|
+
if (!userHasViewPermission(user, normalizedPermission)) {
|
|
871
|
+
throw new Error('AI assistant mongo read: Access denied.');
|
|
872
|
+
}
|
|
873
|
+
normalizedCollection = normalizeOptionalString(collection);
|
|
874
|
+
if (normalizedCollection && requiresInvoicePermission(normalizedCollection) && !userHasInvoiceAccess(user)) {
|
|
875
|
+
throw new Error('AI assistant mongo read: Access denied.');
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
else if (normalizedPermission && !userHasViewPermission(user, normalizedPermission)) {
|
|
879
|
+
throw new Error('AI assistant mongo read: Access denied.');
|
|
880
|
+
}
|
|
881
|
+
return [2 /*return*/, { user: user, isSuperAdmin: isSuperAdmin }];
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
function resolveAssistantDatabaseName(database, mongoConfig) {
|
|
887
|
+
var _a, _b;
|
|
888
|
+
var defaultDb = normalizeOptionalString(mongoConfig === null || mongoConfig === void 0 ? void 0 : mongoConfig.database) || ((_a = resolveio_server_app_1.ResolveIOServer.getServerConfig()) === null || _a === void 0 ? void 0 : _a.DATABASE) || '';
|
|
889
|
+
var dbName = normalizeOptionalString(database) || defaultDb;
|
|
890
|
+
if (!dbName) {
|
|
891
|
+
throw new Error('AI assistant mongo read: Database is required.');
|
|
892
|
+
}
|
|
893
|
+
var allowedFromConfig = Array.isArray(mongoConfig === null || mongoConfig === void 0 ? void 0 : mongoConfig.databases)
|
|
894
|
+
? mongoConfig === null || mongoConfig === void 0 ? void 0 : mongoConfig.databases.map(function (value) { return normalizeOptionalString(value); }).filter(Boolean)
|
|
895
|
+
: [];
|
|
896
|
+
if (allowedFromConfig.length && !allowedFromConfig.includes(dbName)) {
|
|
897
|
+
throw new Error('AI assistant mongo read: Database access denied.');
|
|
898
|
+
}
|
|
899
|
+
var allowedDatabases = ((_b = resolveio_server_app_1.ResolveIOServer.getMongoManager()) === null || _b === void 0 ? void 0 : _b.getWatchedDatabases()) || [];
|
|
900
|
+
if (allowedDatabases.length && !allowedDatabases.includes(dbName)) {
|
|
901
|
+
throw new Error('AI assistant mongo read: Database access denied.');
|
|
902
|
+
}
|
|
903
|
+
return dbName;
|
|
904
|
+
}
|
|
905
|
+
function normalizeAssistantFindOptions(options) {
|
|
906
|
+
var normalized = options || {};
|
|
907
|
+
var projection = normalized.projection && Object.keys(normalized.projection).length ? normalized.projection : undefined;
|
|
908
|
+
var sort = normalized.sort && Object.keys(normalized.sort).length ? normalized.sort : undefined;
|
|
909
|
+
var limit = typeof normalized.limit === 'number'
|
|
910
|
+
? Math.min(Math.max(normalized.limit, 0), AI_ASSISTANT_MONGO_MAX_LIMIT)
|
|
911
|
+
: AI_ASSISTANT_MONGO_DEFAULT_LIMIT;
|
|
912
|
+
var skip = typeof normalized.skip === 'number' ? Math.max(normalized.skip, 0) : 0;
|
|
913
|
+
return {
|
|
914
|
+
findOptions: {
|
|
915
|
+
projection: projection,
|
|
916
|
+
sort: sort,
|
|
917
|
+
limit: limit,
|
|
918
|
+
skip: skip
|
|
919
|
+
},
|
|
920
|
+
includeTotal: normalized.includeTotal === true
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
function normalizeMongoQuery(query) {
|
|
924
|
+
var normalized = query && typeof query === 'object' ? query : {};
|
|
925
|
+
if (containsForbiddenMongoOperators(normalized)) {
|
|
926
|
+
throw new Error('AI assistant mongo read: Query contains restricted operators.');
|
|
927
|
+
}
|
|
928
|
+
return normalized;
|
|
929
|
+
}
|
|
930
|
+
function containsForbiddenMongoOperators(value) {
|
|
931
|
+
var e_1, _a;
|
|
932
|
+
if (!value || typeof value !== 'object') {
|
|
933
|
+
return false;
|
|
934
|
+
}
|
|
935
|
+
if (Array.isArray(value)) {
|
|
936
|
+
return value.some(function (entry) { return containsForbiddenMongoOperators(entry); });
|
|
937
|
+
}
|
|
938
|
+
try {
|
|
939
|
+
for (var _b = __values(Object.keys(value)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
940
|
+
var key = _c.value;
|
|
941
|
+
var normalized = key.toLowerCase();
|
|
942
|
+
if (normalized === '$where' || normalized === '$function' || normalized === '$accumulator') {
|
|
943
|
+
return true;
|
|
944
|
+
}
|
|
945
|
+
if (containsForbiddenMongoOperators(value[key])) {
|
|
946
|
+
return true;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
951
|
+
finally {
|
|
952
|
+
try {
|
|
953
|
+
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
954
|
+
}
|
|
955
|
+
finally { if (e_1) throw e_1.error; }
|
|
956
|
+
}
|
|
957
|
+
return false;
|
|
958
|
+
}
|
|
959
|
+
function applyClientScopeFilter(query, idClient, isSuperAdmin) {
|
|
960
|
+
if (isSuperAdmin === void 0) { isSuperAdmin = false; }
|
|
961
|
+
if (isSuperAdmin) {
|
|
962
|
+
return query;
|
|
963
|
+
}
|
|
964
|
+
var normalizedClient = normalizeOptionalString(idClient);
|
|
965
|
+
if (!normalizedClient) {
|
|
966
|
+
return query;
|
|
967
|
+
}
|
|
968
|
+
return {
|
|
969
|
+
$and: [query, { id_client: normalizedClient }]
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function userHasViewPermission(user, view) {
|
|
973
|
+
var _a, _b, _c;
|
|
974
|
+
if (!user || !view) {
|
|
975
|
+
return false;
|
|
976
|
+
}
|
|
977
|
+
if ((_a = user.roles) === null || _a === void 0 ? void 0 : _a.super_admin) {
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
980
|
+
var groups = Array.isArray((_b = user.roles) === null || _b === void 0 ? void 0 : _b.groups) ? user.roles.groups : [];
|
|
981
|
+
var miscs = Array.isArray((_c = user.roles) === null || _c === void 0 ? void 0 : _c.miscs) ? user.roles.miscs : [];
|
|
982
|
+
if (groups.some(function (group) { return Array.isArray(group.views) && group.views.some(function (v) { return v.startsWith(view); }); })) {
|
|
983
|
+
return true;
|
|
984
|
+
}
|
|
985
|
+
if (miscs.some(function (v) { return v.startsWith(view); })) {
|
|
986
|
+
return true;
|
|
987
|
+
}
|
|
988
|
+
if (groups.some(function (group) { return group.name === view; })) {
|
|
989
|
+
return true;
|
|
990
|
+
}
|
|
991
|
+
return false;
|
|
992
|
+
}
|
|
993
|
+
function userHasAnyViewPermission(user, views) {
|
|
994
|
+
if (!user || !Array.isArray(views)) {
|
|
995
|
+
return false;
|
|
996
|
+
}
|
|
997
|
+
return views.some(function (view) { return view && userHasViewPermission(user, view); });
|
|
998
|
+
}
|
|
999
|
+
function userHasInvoiceAccess(user) {
|
|
1000
|
+
return userHasAnyViewPermission(user, ['/invoice', '/report/invoice']);
|
|
1001
|
+
}
|
|
1002
|
+
function requiresInvoicePermission(collection) {
|
|
1003
|
+
var normalized = normalizeOptionalString(collection).toLowerCase();
|
|
1004
|
+
if (!normalized) {
|
|
1005
|
+
return false;
|
|
1006
|
+
}
|
|
1007
|
+
return normalized.includes('invoice');
|
|
1008
|
+
}
|
|
1009
|
+
function redactSensitiveFields(value) {
|
|
1010
|
+
if (Array.isArray(value)) {
|
|
1011
|
+
return value.map(function (entry) { return redactSensitiveFields(entry); });
|
|
1012
|
+
}
|
|
1013
|
+
if (!value || typeof value !== 'object') {
|
|
1014
|
+
return value;
|
|
1015
|
+
}
|
|
1016
|
+
var result = {};
|
|
1017
|
+
Object.keys(value).forEach(function (key) {
|
|
1018
|
+
if (shouldRedactField(key)) {
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
result[key] = redactSensitiveFields(value[key]);
|
|
1022
|
+
});
|
|
1023
|
+
return result;
|
|
1024
|
+
}
|
|
1025
|
+
function shouldRedactField(key) {
|
|
1026
|
+
var normalized = String(key || '').trim().toLowerCase();
|
|
1027
|
+
if (!normalized) {
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
return AI_ASSISTANT_SENSITIVE_FIELDS.some(function (field) { return normalized === field || normalized.includes(field); });
|
|
1031
|
+
}
|
|
627
1032
|
function resolveCodexTimeoutMs() {
|
|
628
1033
|
var config = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
|
|
629
1034
|
var raw = normalizeOptionalNumber(config['AI_ASSISTANT_CODEX_TIMEOUT_MS'] || process.env.AI_ASSISTANT_CODEX_TIMEOUT_MS);
|
|
@@ -702,13 +1107,19 @@ function buildAssistantCodexPrompt(message, attachmentText, historyText, context
|
|
|
702
1107
|
var historyBlock = trimmedHistory ? "\n\nConversation so far:\n".concat(trimmedHistory) : '';
|
|
703
1108
|
return "System:\n".concat(AI_ASSISTANT_SYSTEM_PROMPT).concat(contextBlock).concat(historyBlock, "\n\nUser:\n").concat(message).concat(attachmentText || '').trim();
|
|
704
1109
|
}
|
|
705
|
-
function buildAssistantContext(input) {
|
|
706
|
-
var _a, _b;
|
|
1110
|
+
function buildAssistantContext(input, userContext) {
|
|
1111
|
+
var _a, _b, _c, _d, _e, _f;
|
|
707
1112
|
var lines = [];
|
|
708
1113
|
var idApp = normalizeOptionalString(input === null || input === void 0 ? void 0 : input.id_app);
|
|
709
1114
|
if (idApp) {
|
|
710
1115
|
lines.push("Current app id: ".concat(idApp));
|
|
711
1116
|
}
|
|
1117
|
+
if (typeof (userContext === null || userContext === void 0 ? void 0 : userContext.isSuperAdmin) === 'boolean') {
|
|
1118
|
+
lines.push("User is super admin: ".concat(userContext.isSuperAdmin ? 'yes' : 'no'));
|
|
1119
|
+
}
|
|
1120
|
+
if (typeof (userContext === null || userContext === void 0 ? void 0 : userContext.hasInvoiceAccess) === 'boolean') {
|
|
1121
|
+
lines.push("User has invoice access: ".concat(userContext.hasInvoiceAccess ? 'yes' : 'no'));
|
|
1122
|
+
}
|
|
712
1123
|
var contextMode = normalizeOptionalString((_a = input === null || input === void 0 ? void 0 : input.context) === null || _a === void 0 ? void 0 : _a.mode);
|
|
713
1124
|
var contextRoute = normalizeOptionalString((_b = input === null || input === void 0 ? void 0 : input.context) === null || _b === void 0 ? void 0 : _b.route);
|
|
714
1125
|
if (contextMode) {
|
|
@@ -717,8 +1128,136 @@ function buildAssistantContext(input) {
|
|
|
717
1128
|
if (contextRoute) {
|
|
718
1129
|
lines.push("Current page route: ".concat(contextRoute));
|
|
719
1130
|
}
|
|
1131
|
+
var mongoDb = normalizeOptionalString((_c = input === null || input === void 0 ? void 0 : input.mongo) === null || _c === void 0 ? void 0 : _c.database);
|
|
1132
|
+
var mongoDbs = Array.isArray((_d = input === null || input === void 0 ? void 0 : input.mongo) === null || _d === void 0 ? void 0 : _d.databases)
|
|
1133
|
+
? input.mongo.databases.map(function (value) { return normalizeOptionalString(value); }).filter(Boolean)
|
|
1134
|
+
: [];
|
|
1135
|
+
var mongoAccess = normalizeOptionalString((_e = input === null || input === void 0 ? void 0 : input.mongo) === null || _e === void 0 ? void 0 : _e.access)
|
|
1136
|
+
|| (((_f = input === null || input === void 0 ? void 0 : input.mongo) === null || _f === void 0 ? void 0 : _f.readonly) === true ? 'read' : '');
|
|
1137
|
+
if (mongoDb) {
|
|
1138
|
+
lines.push("Mongo database: ".concat(mongoDb));
|
|
1139
|
+
}
|
|
1140
|
+
if (mongoDbs.length) {
|
|
1141
|
+
lines.push("Mongo databases allowed: ".concat(mongoDbs.join(', ')));
|
|
1142
|
+
}
|
|
1143
|
+
if (mongoAccess) {
|
|
1144
|
+
lines.push("Mongo access: ".concat(mongoAccess));
|
|
1145
|
+
}
|
|
720
1146
|
return lines.join('\n');
|
|
721
1147
|
}
|
|
1148
|
+
var cachedClientRouteIndex = null;
|
|
1149
|
+
function normalizeRouteKey(value) {
|
|
1150
|
+
var trimmed = normalizeOptionalString(value);
|
|
1151
|
+
if (!trimmed) {
|
|
1152
|
+
return '';
|
|
1153
|
+
}
|
|
1154
|
+
var withSlash = trimmed.startsWith('/') ? trimmed : "/".concat(trimmed);
|
|
1155
|
+
return withSlash.replace(/\/+$/, '');
|
|
1156
|
+
}
|
|
1157
|
+
function normalizeRouteMatchKey(value) {
|
|
1158
|
+
return normalizeRouteKey(value).toLowerCase();
|
|
1159
|
+
}
|
|
1160
|
+
function buildClientRouteIndex() {
|
|
1161
|
+
var e_2, _a;
|
|
1162
|
+
var _b;
|
|
1163
|
+
var routes = ((_b = resolveio_server_app_1.ResolveIOServer.getClientRoutes) === null || _b === void 0 ? void 0 : _b.call(resolveio_server_app_1.ResolveIOServer)) || [];
|
|
1164
|
+
var set = new Set();
|
|
1165
|
+
var map = new Map();
|
|
1166
|
+
try {
|
|
1167
|
+
for (var routes_1 = __values(routes), routes_1_1 = routes_1.next(); !routes_1_1.done; routes_1_1 = routes_1.next()) {
|
|
1168
|
+
var route = routes_1_1.value;
|
|
1169
|
+
var normalized = normalizeRouteKey(route);
|
|
1170
|
+
if (!normalized) {
|
|
1171
|
+
continue;
|
|
1172
|
+
}
|
|
1173
|
+
var matchKey = normalized.toLowerCase();
|
|
1174
|
+
set.add(matchKey);
|
|
1175
|
+
if (!map.has(matchKey)) {
|
|
1176
|
+
map.set(matchKey, normalized);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
1181
|
+
finally {
|
|
1182
|
+
try {
|
|
1183
|
+
if (routes_1_1 && !routes_1_1.done && (_a = routes_1.return)) _a.call(routes_1);
|
|
1184
|
+
}
|
|
1185
|
+
finally { if (e_2) throw e_2.error; }
|
|
1186
|
+
}
|
|
1187
|
+
return { set: set, map: map, size: routes.length };
|
|
1188
|
+
}
|
|
1189
|
+
function getClientRouteIndex() {
|
|
1190
|
+
var _a;
|
|
1191
|
+
var routes = ((_a = resolveio_server_app_1.ResolveIOServer.getClientRoutes) === null || _a === void 0 ? void 0 : _a.call(resolveio_server_app_1.ResolveIOServer)) || [];
|
|
1192
|
+
if (cachedClientRouteIndex && cachedClientRouteIndex.size === routes.length) {
|
|
1193
|
+
return cachedClientRouteIndex;
|
|
1194
|
+
}
|
|
1195
|
+
cachedClientRouteIndex = buildClientRouteIndex();
|
|
1196
|
+
return cachedClientRouteIndex;
|
|
1197
|
+
}
|
|
1198
|
+
function splitRouteSuffix(value) {
|
|
1199
|
+
var matchIndex = value.search(/[?#]/);
|
|
1200
|
+
if (matchIndex === -1) {
|
|
1201
|
+
return { base: value, suffix: '' };
|
|
1202
|
+
}
|
|
1203
|
+
return { base: value.slice(0, matchIndex), suffix: value.slice(matchIndex) };
|
|
1204
|
+
}
|
|
1205
|
+
function isClientRoute(value, index) {
|
|
1206
|
+
var normalized = normalizeRouteMatchKey(value);
|
|
1207
|
+
if (!normalized) {
|
|
1208
|
+
return false;
|
|
1209
|
+
}
|
|
1210
|
+
if (index.set.has(normalized)) {
|
|
1211
|
+
return true;
|
|
1212
|
+
}
|
|
1213
|
+
var parts = normalized.split('/').filter(Boolean);
|
|
1214
|
+
while (parts.length > 1) {
|
|
1215
|
+
parts.pop();
|
|
1216
|
+
var prefix = "/".concat(parts.join('/'));
|
|
1217
|
+
if (index.set.has(prefix)) {
|
|
1218
|
+
return true;
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
return false;
|
|
1222
|
+
}
|
|
1223
|
+
function getCanonicalRoute(value, index) {
|
|
1224
|
+
var normalized = normalizeRouteMatchKey(value);
|
|
1225
|
+
if (!normalized) {
|
|
1226
|
+
return null;
|
|
1227
|
+
}
|
|
1228
|
+
return index.map.get(normalized) || null;
|
|
1229
|
+
}
|
|
1230
|
+
function swapTwoSegmentRoute(value) {
|
|
1231
|
+
var normalized = normalizeRouteKey(value);
|
|
1232
|
+
if (!normalized) {
|
|
1233
|
+
return null;
|
|
1234
|
+
}
|
|
1235
|
+
var parts = normalized.split('/').filter(Boolean);
|
|
1236
|
+
if (parts.length !== 2) {
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
return "/".concat(parts[1], "/").concat(parts[0]);
|
|
1240
|
+
}
|
|
1241
|
+
function normalizeAssistantRoutes(value) {
|
|
1242
|
+
var index = getClientRouteIndex();
|
|
1243
|
+
if (!index.set.size) {
|
|
1244
|
+
return value;
|
|
1245
|
+
}
|
|
1246
|
+
var routePattern = /\/[a-z0-9][a-z0-9/_-]*(?:\?[a-z0-9=&%._-]+)?(?:#[a-z0-9._-]+)?/gi;
|
|
1247
|
+
return value.replace(routePattern, function (match) {
|
|
1248
|
+
var _a = splitRouteSuffix(match), base = _a.base, suffix = _a.suffix;
|
|
1249
|
+
if (isClientRoute(base, index)) {
|
|
1250
|
+
var canonical_1 = getCanonicalRoute(base, index);
|
|
1251
|
+
return canonical_1 ? "".concat(canonical_1).concat(suffix) : match;
|
|
1252
|
+
}
|
|
1253
|
+
var swapped = swapTwoSegmentRoute(base);
|
|
1254
|
+
if (!swapped || !isClientRoute(swapped, index)) {
|
|
1255
|
+
return match;
|
|
1256
|
+
}
|
|
1257
|
+
var canonical = getCanonicalRoute(swapped, index) || swapped;
|
|
1258
|
+
return "".concat(canonical).concat(suffix);
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
722
1261
|
function sanitizeAssistantResponse(value) {
|
|
723
1262
|
var raw = normalizeOptionalString(value);
|
|
724
1263
|
if (!raw) {
|
|
@@ -753,21 +1292,21 @@ function sanitizeAssistantResponse(value) {
|
|
|
753
1292
|
if (!cleaned) {
|
|
754
1293
|
return 'I can’t share code, but I can point you to files or explain behavior at a high level.';
|
|
755
1294
|
}
|
|
756
|
-
return cleaned;
|
|
1295
|
+
return normalizeAssistantRoutes(cleaned);
|
|
757
1296
|
}
|
|
758
1297
|
function evaluateAssistantGuardrails(message) {
|
|
759
|
-
var
|
|
1298
|
+
var e_3, _a;
|
|
760
1299
|
var normalized = String(message || '').toLowerCase();
|
|
761
1300
|
var patterns = [
|
|
762
1301
|
{
|
|
763
1302
|
pattern: /\b(show|share|paste|provide|dump|output)\b.*\b(code|snippet|file|function|class|script|sql)\b/i,
|
|
764
1303
|
reason: 'Code sharing is restricted.',
|
|
765
|
-
response: 'I can’t share code. I can explain behavior and point you to
|
|
1304
|
+
response: 'I can’t share code or file contents. All code is proprietary. I can explain behavior and point you to screens or routes.'
|
|
766
1305
|
},
|
|
767
1306
|
{
|
|
768
1307
|
pattern: /\b(write|generate|create|implement|fix)\b.*\b(code|script|function|class|endpoint|sql)\b/i,
|
|
769
1308
|
reason: 'Code generation is restricted.',
|
|
770
|
-
response: 'I can’t generate code. I can explain how the feature works and where to look in the
|
|
1309
|
+
response: 'I can’t generate code. All code is proprietary. I can explain how the feature works and where to look in the product.'
|
|
771
1310
|
},
|
|
772
1311
|
{
|
|
773
1312
|
pattern: /\b(credentials?|passwords?|secrets?|tokens?)\b/i,
|
|
@@ -802,12 +1341,12 @@ function evaluateAssistantGuardrails(message) {
|
|
|
802
1341
|
}
|
|
803
1342
|
}
|
|
804
1343
|
}
|
|
805
|
-
catch (
|
|
1344
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
806
1345
|
finally {
|
|
807
1346
|
try {
|
|
808
1347
|
if (patterns_1_1 && !patterns_1_1.done && (_a = patterns_1.return)) _a.call(patterns_1);
|
|
809
1348
|
}
|
|
810
|
-
finally { if (
|
|
1349
|
+
finally { if (e_3) throw e_3.error; }
|
|
811
1350
|
}
|
|
812
1351
|
return null;
|
|
813
1352
|
}
|
|
@@ -827,6 +1366,93 @@ function resolveOpenAISettings(config) {
|
|
|
827
1366
|
retryDelayMs: normalizeOptionalNumber(serverConfig['OPENAI_RETRY_DELAY_MS'] || process.env.OPENAI_RETRY_DELAY_MS)
|
|
828
1367
|
};
|
|
829
1368
|
}
|
|
1369
|
+
function buildAiFormSystemPrompt() {
|
|
1370
|
+
return AI_FORM_PATCH_SYSTEM_PROMPT;
|
|
1371
|
+
}
|
|
1372
|
+
function buildAiFormUserPrompt(message, fields, patchFormat, route) {
|
|
1373
|
+
var lines = [];
|
|
1374
|
+
lines.push("User request: ".concat(message));
|
|
1375
|
+
var normalizedRoute = normalizeOptionalString(route);
|
|
1376
|
+
if (normalizedRoute) {
|
|
1377
|
+
lines.push("Current route: ".concat(normalizedRoute));
|
|
1378
|
+
}
|
|
1379
|
+
if (patchFormat) {
|
|
1380
|
+
lines.push('Patch format:');
|
|
1381
|
+
lines.push(patchFormat);
|
|
1382
|
+
}
|
|
1383
|
+
lines.push('Allowed fields:');
|
|
1384
|
+
fields.forEach(function (field) {
|
|
1385
|
+
var name = field.name || '';
|
|
1386
|
+
if (!name) {
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
var parts = [name];
|
|
1390
|
+
if (field.type) {
|
|
1391
|
+
parts.push("type=".concat(field.type));
|
|
1392
|
+
}
|
|
1393
|
+
if (field.label) {
|
|
1394
|
+
parts.push("label=\"".concat(field.label, "\""));
|
|
1395
|
+
}
|
|
1396
|
+
if (Array.isArray(field.enums) && field.enums.length) {
|
|
1397
|
+
parts.push("enums=".concat(field.enums.join('|')));
|
|
1398
|
+
}
|
|
1399
|
+
lines.push("- ".concat(parts.join(' ')));
|
|
1400
|
+
});
|
|
1401
|
+
lines.push('Return JSON only.');
|
|
1402
|
+
return lines.join('\n');
|
|
1403
|
+
}
|
|
1404
|
+
function normalizeAiFormFields(fields) {
|
|
1405
|
+
if (!Array.isArray(fields)) {
|
|
1406
|
+
return [];
|
|
1407
|
+
}
|
|
1408
|
+
return fields
|
|
1409
|
+
.map(function (field) {
|
|
1410
|
+
if (!field || typeof field !== 'object') {
|
|
1411
|
+
return null;
|
|
1412
|
+
}
|
|
1413
|
+
var name = normalizeOptionalString(field.name) || normalizeOptionalString(field.path);
|
|
1414
|
+
if (!name) {
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1417
|
+
var type = normalizeOptionalString(field.type);
|
|
1418
|
+
var label = normalizeOptionalString(field.label);
|
|
1419
|
+
var enums = Array.isArray(field.enums)
|
|
1420
|
+
? field.enums.map(function (value) { return normalizeOptionalString(value); }).filter(Boolean)
|
|
1421
|
+
: undefined;
|
|
1422
|
+
var normalizedField = { name: name };
|
|
1423
|
+
if (type) {
|
|
1424
|
+
normalizedField.type = type;
|
|
1425
|
+
}
|
|
1426
|
+
if (label) {
|
|
1427
|
+
normalizedField.label = label;
|
|
1428
|
+
}
|
|
1429
|
+
if (enums && enums.length) {
|
|
1430
|
+
normalizedField.enums = enums;
|
|
1431
|
+
}
|
|
1432
|
+
return normalizedField;
|
|
1433
|
+
})
|
|
1434
|
+
.filter(function (field) { return !!field; });
|
|
1435
|
+
}
|
|
1436
|
+
function parseJsonObject(content) {
|
|
1437
|
+
if (!content || typeof content !== 'string') {
|
|
1438
|
+
return null;
|
|
1439
|
+
}
|
|
1440
|
+
try {
|
|
1441
|
+
return JSON.parse(content);
|
|
1442
|
+
}
|
|
1443
|
+
catch (_a) {
|
|
1444
|
+
var match = content.match(/\{[\s\S]*\}/);
|
|
1445
|
+
if (!match) {
|
|
1446
|
+
return null;
|
|
1447
|
+
}
|
|
1448
|
+
try {
|
|
1449
|
+
return JSON.parse(match[0]);
|
|
1450
|
+
}
|
|
1451
|
+
catch (_b) {
|
|
1452
|
+
return null;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
830
1456
|
function resolveUploadLimits() {
|
|
831
1457
|
var config = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
|
|
832
1458
|
var maxFileMb = normalizeOptionalNumber(config['AI_TERMINAL_MAX_FILE_MB'] || process.env.AI_TERMINAL_MAX_FILE_MB) || DEFAULT_MAX_FILE_MB;
|
|
@@ -884,8 +1510,8 @@ function handleCodexUpload(id_conversation, file_name, content_base64, size, con
|
|
|
884
1510
|
}
|
|
885
1511
|
function readAttachmentContents(attachments) {
|
|
886
1512
|
return __awaiter(this, void 0, void 0, function () {
|
|
887
|
-
var limits, totalBytes, totalChars, chunks, cleaned, attachments_1, attachments_1_1, attachment, localPath, safe, stat, ext, name_1, type, readable, content, _a,
|
|
888
|
-
var
|
|
1513
|
+
var limits, totalBytes, totalChars, chunks, cleaned, attachments_1, attachments_1_1, attachment, localPath, safe, stat, ext, name_1, type, readable, content, _a, e_4_1;
|
|
1514
|
+
var e_4, _b;
|
|
889
1515
|
return __generator(this, function (_c) {
|
|
890
1516
|
switch (_c.label) {
|
|
891
1517
|
case 0:
|
|
@@ -964,14 +1590,14 @@ function readAttachmentContents(attachments) {
|
|
|
964
1590
|
return [3 /*break*/, 2];
|
|
965
1591
|
case 10: return [3 /*break*/, 13];
|
|
966
1592
|
case 11:
|
|
967
|
-
|
|
968
|
-
|
|
1593
|
+
e_4_1 = _c.sent();
|
|
1594
|
+
e_4 = { error: e_4_1 };
|
|
969
1595
|
return [3 /*break*/, 13];
|
|
970
1596
|
case 12:
|
|
971
1597
|
try {
|
|
972
1598
|
if (attachments_1_1 && !attachments_1_1.done && (_b = attachments_1.return)) _b.call(attachments_1);
|
|
973
1599
|
}
|
|
974
|
-
finally { if (
|
|
1600
|
+
finally { if (e_4) throw e_4.error; }
|
|
975
1601
|
return [7 /*endfinally*/];
|
|
976
1602
|
case 13: return [2 /*return*/, {
|
|
977
1603
|
promptText: chunks.length ? "\n\nAttachments:\n".concat(chunks.join('\n\n')) : '',
|
|
@@ -1124,10 +1750,11 @@ function estimateUsage(messages, responseText, model) {
|
|
|
1124
1750
|
};
|
|
1125
1751
|
}
|
|
1126
1752
|
function evaluateGuardrails(message) {
|
|
1127
|
-
var
|
|
1753
|
+
var e_5, _a;
|
|
1128
1754
|
var normalized = String(message || '').toLowerCase();
|
|
1129
1755
|
var patterns = [
|
|
1130
1756
|
{ 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.' },
|
|
1757
|
+
{ pattern: /\b(show|share|paste|provide|dump|output)\b.*\b(code|file|contents|snippet)\b/i, reason: 'Code access is restricted.' },
|
|
1131
1758
|
{ pattern: /\b(credentials?|passwords?|secrets?|tokens?)\b/i, reason: 'Credentials and secrets are restricted.' },
|
|
1132
1759
|
{ pattern: /\b(delete|drop)\s+(database|db|schema)\b/i, reason: 'Database operations are restricted.' },
|
|
1133
1760
|
{ pattern: /\b(shell|terminal|ssh|sudo|rm\s+-rf|chmod|chown)\b/i, reason: 'Server operations are restricted.' },
|
|
@@ -1140,17 +1767,17 @@ function evaluateGuardrails(message) {
|
|
|
1140
1767
|
return {
|
|
1141
1768
|
blocked: true,
|
|
1142
1769
|
reason: entry.reason,
|
|
1143
|
-
response: 'I can’t help with that request because it could
|
|
1770
|
+
response: 'I can’t help with that request because it could expose proprietary code or protected information. If you need a change, describe the outcome and I can help within approved workflows.'
|
|
1144
1771
|
};
|
|
1145
1772
|
}
|
|
1146
1773
|
}
|
|
1147
1774
|
}
|
|
1148
|
-
catch (
|
|
1775
|
+
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
1149
1776
|
finally {
|
|
1150
1777
|
try {
|
|
1151
1778
|
if (patterns_2_1 && !patterns_2_1.done && (_a = patterns_2.return)) _a.call(patterns_2);
|
|
1152
1779
|
}
|
|
1153
|
-
finally { if (
|
|
1780
|
+
finally { if (e_5) throw e_5.error; }
|
|
1154
1781
|
}
|
|
1155
1782
|
return null;
|
|
1156
1783
|
}
|