@atomoz/workflows-nodes 0.1.26 → 0.1.28
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/dist/index.cjs +792 -59
- package/dist/index.d.cts +42 -4
- package/dist/index.d.ts +42 -4
- package/dist/index.js +776 -59
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1008,7 +1008,7 @@ async function createLLMFromModel(modelConfig, authToken, streaming = false) {
|
|
|
1008
1008
|
case "gemini":
|
|
1009
1009
|
return new ChatGoogle({
|
|
1010
1010
|
model: "gemini-flash-latest",
|
|
1011
|
-
apiKey: "
|
|
1011
|
+
apiKey: "AIzaSyAWj0Al2sVJ8vqaRN4UY7c6imAg7a3NbEc",
|
|
1012
1012
|
streaming
|
|
1013
1013
|
});
|
|
1014
1014
|
case "openai":
|
|
@@ -1034,6 +1034,62 @@ async function createLLMFromModel(modelConfig, authToken, streaming = false) {
|
|
|
1034
1034
|
}
|
|
1035
1035
|
|
|
1036
1036
|
// src/utils/guardrail-executor.ts
|
|
1037
|
+
function emitGuardrailEvent(context, name, passed, action, details) {
|
|
1038
|
+
if (context?.emitter?.emitThought) {
|
|
1039
|
+
context.emitter.emitThought({
|
|
1040
|
+
content: `guardrail:${passed ? "passed" : "failed"}:${name}`,
|
|
1041
|
+
type: "guardrail",
|
|
1042
|
+
name,
|
|
1043
|
+
passed,
|
|
1044
|
+
action: action || "evaluate",
|
|
1045
|
+
details
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
async function loadChatHistory(context) {
|
|
1050
|
+
const checkpointer = context?.checkpointer;
|
|
1051
|
+
const sessionId = context?.sessionId;
|
|
1052
|
+
if (!checkpointer || !sessionId) {
|
|
1053
|
+
console.log(`[GUARDRAIL] checkpointer=${!!checkpointer}, sessionId=${sessionId}`);
|
|
1054
|
+
return null;
|
|
1055
|
+
}
|
|
1056
|
+
try {
|
|
1057
|
+
console.log(`[GUARDRAIL] Carregando hist\xF3rico da mem\xF3ria (session: ${sessionId})`);
|
|
1058
|
+
if (typeof checkpointer.setup === "function" && !checkpointer.isSetup) {
|
|
1059
|
+
await checkpointer.setup();
|
|
1060
|
+
}
|
|
1061
|
+
let checkpoint = null;
|
|
1062
|
+
if (typeof checkpointer.getTuple === "function") {
|
|
1063
|
+
const tuple = await checkpointer.getTuple({ configurable: { thread_id: sessionId } });
|
|
1064
|
+
checkpoint = tuple?.checkpoint;
|
|
1065
|
+
console.log(`[GUARDRAIL] getTuple result:`, tuple ? "found" : "null");
|
|
1066
|
+
}
|
|
1067
|
+
if (!checkpoint && typeof checkpointer.get === "function") {
|
|
1068
|
+
checkpoint = await checkpointer.get({ configurable: { thread_id: sessionId } });
|
|
1069
|
+
console.log(`[GUARDRAIL] get result:`, checkpoint ? "found" : "null");
|
|
1070
|
+
}
|
|
1071
|
+
if (!checkpoint) {
|
|
1072
|
+
console.log(`[GUARDRAIL] Nenhum checkpoint encontrado`);
|
|
1073
|
+
return null;
|
|
1074
|
+
}
|
|
1075
|
+
console.log(`[GUARDRAIL] Checkpoint keys:`, Object.keys(checkpoint));
|
|
1076
|
+
const messages = checkpoint?.channel_values?.messages || checkpoint?.values?.messages || checkpoint?.messages || [];
|
|
1077
|
+
if (!messages || messages.length === 0) {
|
|
1078
|
+
console.log(`[GUARDRAIL] Nenhuma mensagem no checkpoint`);
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
const historyLines = messages.map((m) => {
|
|
1082
|
+
const role = m._getType?.() === "human" || m.type === "human" ? "Usu\xE1rio" : "Assistente";
|
|
1083
|
+
const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
1084
|
+
return `${role}: ${content.slice(0, 200)}${content.length > 200 ? "..." : ""}`;
|
|
1085
|
+
});
|
|
1086
|
+
console.log(`[GUARDRAIL] Hist\xF3rico carregado: ${messages.length} mensagens`);
|
|
1087
|
+
return historyLines.join("\n");
|
|
1088
|
+
} catch (error) {
|
|
1089
|
+
console.error(`[GUARDRAIL] Erro ao carregar hist\xF3rico:`, error);
|
|
1090
|
+
return null;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1037
1093
|
async function executeGuardrails(guardrails, content, mode = "fail-first", context = {}) {
|
|
1038
1094
|
const list = Array.isArray(guardrails) ? guardrails : [guardrails];
|
|
1039
1095
|
const result = {
|
|
@@ -1084,6 +1140,7 @@ async function executeGuardrails(guardrails, content, mode = "fail-first", conte
|
|
|
1084
1140
|
if (!guardPassed && actionInstruction) {
|
|
1085
1141
|
violationMessage = actionInstruction;
|
|
1086
1142
|
}
|
|
1143
|
+
emitGuardrailEvent(context, guard.name, guardPassed, guard.action, violationMessage || "Rule passed");
|
|
1087
1144
|
} else if (guard.type === "function") {
|
|
1088
1145
|
const { nodeFunction, name, actionInstruction } = guard;
|
|
1089
1146
|
if (typeof nodeFunction === "function") {
|
|
@@ -1116,47 +1173,97 @@ async function executeGuardrails(guardrails, content, mode = "fail-first", conte
|
|
|
1116
1173
|
guardPassed = false;
|
|
1117
1174
|
violationMessage = `Erro na fun\xE7\xE3o guardrail: ${error instanceof Error ? error.message : String(error)}`;
|
|
1118
1175
|
}
|
|
1176
|
+
emitGuardrailEvent(context, name, guardPassed, guard.action, violationMessage || "Function passed");
|
|
1119
1177
|
}
|
|
1120
1178
|
} else if (guard.type === "model") {
|
|
1121
1179
|
const { model, evaluationPrompt, name, actionInstruction } = guard;
|
|
1180
|
+
console.log(`[GUARDRAIL LLM] Iniciando guardrail "${name}"`);
|
|
1181
|
+
console.log(`[GUARDRAIL LLM] Conte\xFAdo a avaliar (${content.length} chars):`, content.slice(0, 200) + (content.length > 200 ? "..." : ""));
|
|
1182
|
+
console.log(`[GUARDRAIL LLM] Action: ${guard.action}, Model dispon\xEDvel: ${!!model}`);
|
|
1183
|
+
const chatHistory = await loadChatHistory(context);
|
|
1184
|
+
if (chatHistory) {
|
|
1185
|
+
console.log(`[GUARDRAIL LLM] Hist\xF3rico inclu\xEDdo no contexto`);
|
|
1186
|
+
}
|
|
1122
1187
|
if (model && typeof model.invoke === "function") {
|
|
1123
1188
|
try {
|
|
1124
1189
|
let prompt = evaluationPrompt;
|
|
1190
|
+
console.log(`[GUARDRAIL LLM] Evaluation prompt base:`, (prompt || "").slice(0, 200));
|
|
1125
1191
|
if (actionInstruction) {
|
|
1126
1192
|
prompt += `
|
|
1127
1193
|
|
|
1128
1194
|
INSTRU\xC7\xC3O ADICIONAL DE A\xC7\xC3O:
|
|
1129
1195
|
${actionInstruction}`;
|
|
1196
|
+
}
|
|
1197
|
+
if (chatHistory) {
|
|
1198
|
+
prompt += `
|
|
1199
|
+
|
|
1200
|
+
HIST\xD3RICO DA CONVERSA:
|
|
1201
|
+
${chatHistory}`;
|
|
1130
1202
|
}
|
|
1131
1203
|
if (prompt.includes("{{content}}")) {
|
|
1132
1204
|
prompt = prompt.replace("{{content}}", content);
|
|
1133
1205
|
} else {
|
|
1134
1206
|
prompt = `${prompt}
|
|
1135
1207
|
|
|
1136
|
-
CONTE\xDADO
|
|
1208
|
+
CONTE\xDADO A AVALIAR (resposta atual):
|
|
1137
1209
|
${content}`;
|
|
1138
1210
|
}
|
|
1211
|
+
console.log(`[GUARDRAIL LLM] Chamando modelo...`);
|
|
1139
1212
|
const response = await model.invoke(prompt);
|
|
1140
1213
|
const resultText = typeof response.content === "string" ? response.content : String(response.content);
|
|
1214
|
+
console.log(`[GUARDRAIL LLM] Resposta do modelo:`, resultText.slice(0, 300));
|
|
1215
|
+
const isUnsafe = resultText.toUpperCase().includes("UNSAFE");
|
|
1216
|
+
const isSafe = resultText.toUpperCase().startsWith("SAFE") || resultText.toUpperCase().includes("SAFE") && !isUnsafe;
|
|
1141
1217
|
if (guard.action === "fix") {
|
|
1142
|
-
if (
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1218
|
+
if (isUnsafe) {
|
|
1219
|
+
console.log(`[GUARDRAIL LLM] Modo FIX - Detectado UNSAFE, chamando modelo para corrigir...`);
|
|
1220
|
+
const fixPrompt = `Voc\xEA \xE9 um corretor de conte\xFAdo. O texto abaixo foi marcado como UNSAFE por violar uma regra.
|
|
1221
|
+
|
|
1222
|
+
REGRA VIOLADA:
|
|
1223
|
+
${evaluationPrompt}
|
|
1224
|
+
|
|
1225
|
+
${actionInstruction ? `INSTRU\xC7\xC3O DE CORRE\xC7\xC3O:
|
|
1226
|
+
${actionInstruction}
|
|
1227
|
+
` : ""}
|
|
1228
|
+
|
|
1229
|
+
CONTE\xDADO ORIGINAL:
|
|
1230
|
+
${content}
|
|
1231
|
+
|
|
1232
|
+
Reescreva o conte\xFAdo corrigindo a viola\xE7\xE3o. Retorne APENAS o texto corrigido, sem explica\xE7\xF5es ou prefixos.`;
|
|
1233
|
+
try {
|
|
1234
|
+
const fixResponse = await model.invoke(fixPrompt);
|
|
1235
|
+
const fixedContent = typeof fixResponse.content === "string" ? fixResponse.content : String(fixResponse.content);
|
|
1236
|
+
console.log(`[GUARDRAIL LLM] Conte\xFAdo corrigido:`, fixedContent.slice(0, 300));
|
|
1237
|
+
result.modifiedContent = fixedContent;
|
|
1238
|
+
violationMessage = `Conte\xFAdo reescrito pelo guardrail "${name}".`;
|
|
1239
|
+
result.violations.push({
|
|
1240
|
+
name: guard.name,
|
|
1241
|
+
message: violationMessage,
|
|
1242
|
+
action: "fix"
|
|
1243
|
+
});
|
|
1244
|
+
emitGuardrailEvent(context, name, false, "fix", `Content fixed: ${fixedContent.slice(0, 100)}...`);
|
|
1245
|
+
} catch (fixError) {
|
|
1246
|
+
console.error(`[GUARDRAIL LLM] Erro ao corrigir conte\xFAdo:`, fixError);
|
|
1247
|
+
}
|
|
1248
|
+
} else {
|
|
1249
|
+
console.log(`[GUARDRAIL LLM] Modo FIX - Conte\xFAdo aprovado (SAFE)`);
|
|
1150
1250
|
}
|
|
1151
1251
|
} else {
|
|
1152
|
-
if (
|
|
1252
|
+
if (isUnsafe) {
|
|
1153
1253
|
guardPassed = false;
|
|
1154
1254
|
violationMessage = actionInstruction || `Modelo de avalia\xE7\xE3o "${name}" detectou viola\xE7\xE3o de seguran\xE7a/pol\xEDtica.`;
|
|
1255
|
+
console.log(`[GUARDRAIL LLM] Resultado: UNSAFE - Bloqueado`);
|
|
1256
|
+
emitGuardrailEvent(context, name, false, guard.action, violationMessage);
|
|
1257
|
+
} else {
|
|
1258
|
+
console.log(`[GUARDRAIL LLM] Resultado: SAFE - Aprovado`);
|
|
1259
|
+
emitGuardrailEvent(context, name, true, "approve", "Content passed evaluation");
|
|
1155
1260
|
}
|
|
1156
1261
|
}
|
|
1157
1262
|
} catch (error) {
|
|
1158
|
-
console.error(`
|
|
1263
|
+
console.error(`[GUARDRAIL LLM] Erro ao executar guardrail "${name}":`, error);
|
|
1159
1264
|
}
|
|
1265
|
+
} else {
|
|
1266
|
+
console.log(`[GUARDRAIL LLM] Model n\xE3o dispon\xEDvel ou n\xE3o \xE9 invoc\xE1vel para guardrail "${name}"`);
|
|
1160
1267
|
}
|
|
1161
1268
|
}
|
|
1162
1269
|
if (!guardPassed) {
|
|
@@ -1220,7 +1327,7 @@ var IaAgentNodeFunction = async (inputs) => {
|
|
|
1220
1327
|
let guardrailViolations = [];
|
|
1221
1328
|
if (message && beforeGuardrails) {
|
|
1222
1329
|
const preparedBefore = await prepareGuardrails(beforeGuardrails);
|
|
1223
|
-
const context = { ...inputs, input: message };
|
|
1330
|
+
const context = { ...inputs, input: message, checkpointer, sessionId };
|
|
1224
1331
|
const beforeResults = await executeGuardrails(preparedBefore, message, guardrailMode, context);
|
|
1225
1332
|
if (beforeResults.blocked) {
|
|
1226
1333
|
console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Blocked by before-agent guardrail:`, beforeResults.message);
|
|
@@ -1422,7 +1529,7 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1422
1529
|
}
|
|
1423
1530
|
if (output && afterGuardrails) {
|
|
1424
1531
|
const preparedAfter = await prepareGuardrails(afterGuardrails);
|
|
1425
|
-
const context = { ...inputs, input: output };
|
|
1532
|
+
const context = { ...inputs, input: output, checkpointer, sessionId };
|
|
1426
1533
|
const afterResults = await executeGuardrails(preparedAfter, output, guardrailMode, context);
|
|
1427
1534
|
if (afterResults.blocked) {
|
|
1428
1535
|
console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Response blocked by after-agent guardrail:`, afterResults.message);
|
|
@@ -2311,8 +2418,271 @@ var WhatsappMessageTriggerNode = {
|
|
|
2311
2418
|
]
|
|
2312
2419
|
};
|
|
2313
2420
|
|
|
2421
|
+
// src/utils/sandbox-executor.ts
|
|
2422
|
+
var ivmModule = null;
|
|
2423
|
+
async function getIVM() {
|
|
2424
|
+
if (ivmModule !== null) return ivmModule;
|
|
2425
|
+
try {
|
|
2426
|
+
const mod = await import("isolated-vm");
|
|
2427
|
+
ivmModule = {
|
|
2428
|
+
Isolate: mod.Isolate || mod.default?.Isolate,
|
|
2429
|
+
ExternalCopy: mod.ExternalCopy || mod.default?.ExternalCopy,
|
|
2430
|
+
Reference: mod.Reference || mod.default?.Reference
|
|
2431
|
+
};
|
|
2432
|
+
if (!ivmModule.Isolate) {
|
|
2433
|
+
console.warn("[Sandbox] isolated-vm Isolate n\xE3o encontrado");
|
|
2434
|
+
return null;
|
|
2435
|
+
}
|
|
2436
|
+
return ivmModule;
|
|
2437
|
+
} catch (e) {
|
|
2438
|
+
console.warn("[Sandbox] isolated-vm n\xE3o dispon\xEDvel, usando fallback inseguro");
|
|
2439
|
+
return null;
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
var DEFAULT_OPTIONS = {
|
|
2443
|
+
timeout: 1e4,
|
|
2444
|
+
memoryLimit: 128,
|
|
2445
|
+
allowFetch: true
|
|
2446
|
+
};
|
|
2447
|
+
async function executeSandboxed(code, sandboxContext, options = {}) {
|
|
2448
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
2449
|
+
const ivm = await getIVM();
|
|
2450
|
+
if (!ivm) {
|
|
2451
|
+
return executeFallback(code, sandboxContext);
|
|
2452
|
+
}
|
|
2453
|
+
let isolate = null;
|
|
2454
|
+
let context = null;
|
|
2455
|
+
try {
|
|
2456
|
+
isolate = new ivm.Isolate({ memoryLimit: opts.memoryLimit });
|
|
2457
|
+
context = await isolate.createContext();
|
|
2458
|
+
const jail = context.global;
|
|
2459
|
+
await jail.set("globalThis", jail.derefInto());
|
|
2460
|
+
await injectContextVariables(jail, sandboxContext, ivm);
|
|
2461
|
+
await jail.set("__logRef", new ivm.Reference(function(...args) {
|
|
2462
|
+
console.log("[Sandbox]", ...args);
|
|
2463
|
+
}));
|
|
2464
|
+
if (opts.allowFetch) {
|
|
2465
|
+
await jail.set("__fetchRef", new ivm.Reference(async function(url, optionsJson) {
|
|
2466
|
+
try {
|
|
2467
|
+
const options2 = optionsJson ? JSON.parse(optionsJson) : {};
|
|
2468
|
+
const res = await fetch(url, {
|
|
2469
|
+
method: options2?.method || "GET",
|
|
2470
|
+
headers: options2?.headers,
|
|
2471
|
+
body: options2?.body
|
|
2472
|
+
});
|
|
2473
|
+
const contentType = res.headers.get("content-type") || "";
|
|
2474
|
+
let data;
|
|
2475
|
+
if (contentType.includes("application/json")) {
|
|
2476
|
+
data = await res.json();
|
|
2477
|
+
} else {
|
|
2478
|
+
data = await res.text();
|
|
2479
|
+
}
|
|
2480
|
+
return JSON.stringify({
|
|
2481
|
+
ok: res.ok,
|
|
2482
|
+
status: res.status,
|
|
2483
|
+
statusText: res.statusText,
|
|
2484
|
+
data
|
|
2485
|
+
});
|
|
2486
|
+
} catch (error) {
|
|
2487
|
+
return JSON.stringify({
|
|
2488
|
+
ok: false,
|
|
2489
|
+
status: 0,
|
|
2490
|
+
statusText: "Network Error",
|
|
2491
|
+
error: error.message
|
|
2492
|
+
});
|
|
2493
|
+
}
|
|
2494
|
+
}));
|
|
2495
|
+
}
|
|
2496
|
+
const bootstrapCode = `
|
|
2497
|
+
// Console
|
|
2498
|
+
const console = {
|
|
2499
|
+
log: (...args) => __logRef.applySync(undefined, args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))),
|
|
2500
|
+
warn: (...args) => __logRef.applySync(undefined, ['[WARN]', ...args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))]),
|
|
2501
|
+
error: (...args) => __logRef.applySync(undefined, ['[ERROR]', ...args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))]),
|
|
2502
|
+
info: (...args) => __logRef.applySync(undefined, args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))),
|
|
2503
|
+
};
|
|
2504
|
+
globalThis.console = console;
|
|
2505
|
+
|
|
2506
|
+
${opts.allowFetch ? `
|
|
2507
|
+
// Fetch
|
|
2508
|
+
globalThis.fetch = async (url, options = {}) => {
|
|
2509
|
+
const optionsJson = JSON.stringify(options);
|
|
2510
|
+
const resultJson = await __fetchRef.apply(undefined, [url, optionsJson], { result: { promise: true } });
|
|
2511
|
+
const result = JSON.parse(resultJson);
|
|
2512
|
+
|
|
2513
|
+
if (!result.ok && result.error) {
|
|
2514
|
+
throw new Error(result.error);
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
return {
|
|
2518
|
+
ok: result.ok,
|
|
2519
|
+
status: result.status,
|
|
2520
|
+
statusText: result.statusText,
|
|
2521
|
+
json: async () => result.data,
|
|
2522
|
+
text: async () => typeof result.data === 'string' ? result.data : JSON.stringify(result.data),
|
|
2523
|
+
};
|
|
2524
|
+
};
|
|
2525
|
+
` : ""}
|
|
2526
|
+
|
|
2527
|
+
// Base64
|
|
2528
|
+
globalThis.atob = (str) => {
|
|
2529
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
2530
|
+
let output = '';
|
|
2531
|
+
str = String(str).replace(/=+$/, '');
|
|
2532
|
+
for (let i = 0; i < str.length; i += 4) {
|
|
2533
|
+
const enc1 = chars.indexOf(str.charAt(i));
|
|
2534
|
+
const enc2 = chars.indexOf(str.charAt(i + 1));
|
|
2535
|
+
const enc3 = chars.indexOf(str.charAt(i + 2));
|
|
2536
|
+
const enc4 = chars.indexOf(str.charAt(i + 3));
|
|
2537
|
+
const chr1 = (enc1 << 2) | (enc2 >> 4);
|
|
2538
|
+
const chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
|
2539
|
+
const chr3 = ((enc3 & 3) << 6) | enc4;
|
|
2540
|
+
output += String.fromCharCode(chr1);
|
|
2541
|
+
if (enc3 !== 64) output += String.fromCharCode(chr2);
|
|
2542
|
+
if (enc4 !== 64) output += String.fromCharCode(chr3);
|
|
2543
|
+
}
|
|
2544
|
+
return output;
|
|
2545
|
+
};
|
|
2546
|
+
|
|
2547
|
+
globalThis.btoa = (str) => {
|
|
2548
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
2549
|
+
str = String(str);
|
|
2550
|
+
let output = '';
|
|
2551
|
+
for (let i = 0; i < str.length; i += 3) {
|
|
2552
|
+
const chr1 = str.charCodeAt(i);
|
|
2553
|
+
const chr2 = str.charCodeAt(i + 1);
|
|
2554
|
+
const chr3 = str.charCodeAt(i + 2);
|
|
2555
|
+
const enc1 = chr1 >> 2;
|
|
2556
|
+
const enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
|
2557
|
+
const enc3 = isNaN(chr2) ? 64 : ((chr2 & 15) << 2) | (chr3 >> 6);
|
|
2558
|
+
const enc4 = isNaN(chr3) ? 64 : chr3 & 63;
|
|
2559
|
+
output += chars.charAt(enc1) + chars.charAt(enc2) + chars.charAt(enc3) + chars.charAt(enc4);
|
|
2560
|
+
}
|
|
2561
|
+
return output;
|
|
2562
|
+
};
|
|
2563
|
+
`;
|
|
2564
|
+
const bootstrapScript = await isolate.compileScript(bootstrapCode);
|
|
2565
|
+
await bootstrapScript.run(context);
|
|
2566
|
+
const wrappedCode = wrapUserCode(code);
|
|
2567
|
+
const finalCode = `
|
|
2568
|
+
(async () => {
|
|
2569
|
+
const __userResult = await (${wrappedCode});
|
|
2570
|
+
// Serializa o resultado para poder transferir
|
|
2571
|
+
if (__userResult === undefined) return '__UNDEFINED__';
|
|
2572
|
+
if (__userResult === null) return 'null';
|
|
2573
|
+
if (typeof __userResult === 'object') {
|
|
2574
|
+
return JSON.stringify({ __type: 'object', value: __userResult });
|
|
2575
|
+
}
|
|
2576
|
+
return JSON.stringify({ __type: typeof __userResult, value: __userResult });
|
|
2577
|
+
})()
|
|
2578
|
+
`;
|
|
2579
|
+
const script = await isolate.compileScript(finalCode);
|
|
2580
|
+
const rawResult = await script.run(context, {
|
|
2581
|
+
timeout: opts.timeout,
|
|
2582
|
+
promise: true
|
|
2583
|
+
});
|
|
2584
|
+
if (rawResult === "__UNDEFINED__") return void 0;
|
|
2585
|
+
if (rawResult === "null") return null;
|
|
2586
|
+
try {
|
|
2587
|
+
const parsed = JSON.parse(rawResult);
|
|
2588
|
+
return parsed.value;
|
|
2589
|
+
} catch {
|
|
2590
|
+
return rawResult;
|
|
2591
|
+
}
|
|
2592
|
+
} catch (error) {
|
|
2593
|
+
if (error.message?.includes("Script execution timed out")) {
|
|
2594
|
+
throw new Error("Tempo de execu\xE7\xE3o excedido (timeout)");
|
|
2595
|
+
}
|
|
2596
|
+
if (error.message?.includes("Isolate was disposed")) {
|
|
2597
|
+
throw new Error("Execu\xE7\xE3o cancelada: limite de mem\xF3ria excedido");
|
|
2598
|
+
}
|
|
2599
|
+
throw new Error(`Erro na execu\xE7\xE3o do c\xF3digo: ${error.message || "Erro desconhecido"}`);
|
|
2600
|
+
} finally {
|
|
2601
|
+
if (context) {
|
|
2602
|
+
try {
|
|
2603
|
+
context.release();
|
|
2604
|
+
} catch {
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
if (isolate) {
|
|
2608
|
+
try {
|
|
2609
|
+
isolate.dispose();
|
|
2610
|
+
} catch {
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
async function injectContextVariables(jail, ctx, ivm) {
|
|
2616
|
+
const safeClone = (value) => {
|
|
2617
|
+
if (value === void 0 || value === null) {
|
|
2618
|
+
return value;
|
|
2619
|
+
}
|
|
2620
|
+
try {
|
|
2621
|
+
if (typeof value !== "object") {
|
|
2622
|
+
return value;
|
|
2623
|
+
}
|
|
2624
|
+
return new ivm.ExternalCopy(value).copyInto();
|
|
2625
|
+
} catch {
|
|
2626
|
+
try {
|
|
2627
|
+
return JSON.parse(JSON.stringify(value));
|
|
2628
|
+
} catch {
|
|
2629
|
+
return void 0;
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
};
|
|
2633
|
+
await jail.set("input", safeClone(ctx.input));
|
|
2634
|
+
await jail.set("context", safeClone(ctx.context ?? {}));
|
|
2635
|
+
await jail.set("$inputs", safeClone(ctx.$inputs ?? {}));
|
|
2636
|
+
await jail.set("$vars", safeClone(ctx.$vars ?? {}));
|
|
2637
|
+
await jail.set("$field", safeClone(ctx.$field ?? {}));
|
|
2638
|
+
await jail.set("request", safeClone(ctx.request));
|
|
2639
|
+
await jail.set("params", safeClone(ctx.params));
|
|
2640
|
+
await jail.set("__placeholders", safeClone(ctx.__placeholders ?? {}));
|
|
2641
|
+
}
|
|
2642
|
+
function wrapUserCode(code) {
|
|
2643
|
+
const trimmed = code.trim();
|
|
2644
|
+
if (trimmed.startsWith("return ")) {
|
|
2645
|
+
return `(async function() { ${code} })()`;
|
|
2646
|
+
}
|
|
2647
|
+
const isSimpleExpression = !trimmed.includes("{") && !trimmed.includes("if ") && !trimmed.includes("for ") && !trimmed.includes("while ") && !trimmed.includes("function ") && !trimmed.includes("const ") && !trimmed.includes("let ") && !trimmed.includes("var ");
|
|
2648
|
+
if (isSimpleExpression) {
|
|
2649
|
+
return `(async function() { return ${code} })()`;
|
|
2650
|
+
}
|
|
2651
|
+
return `(async function() { ${code} })()`;
|
|
2652
|
+
}
|
|
2653
|
+
function executeFallback(code, ctx) {
|
|
2654
|
+
console.warn("[Sandbox] Usando execu\xE7\xE3o INSEGURA (new Function). Instale isolated-vm para seguran\xE7a.");
|
|
2655
|
+
const customFunction = new Function(
|
|
2656
|
+
"input",
|
|
2657
|
+
"context",
|
|
2658
|
+
"request",
|
|
2659
|
+
"params",
|
|
2660
|
+
"$inputs",
|
|
2661
|
+
"$vars",
|
|
2662
|
+
"$field",
|
|
2663
|
+
"__placeholders",
|
|
2664
|
+
code
|
|
2665
|
+
);
|
|
2666
|
+
return customFunction(
|
|
2667
|
+
ctx.input,
|
|
2668
|
+
ctx.context,
|
|
2669
|
+
ctx.request,
|
|
2670
|
+
ctx.params,
|
|
2671
|
+
ctx.$inputs,
|
|
2672
|
+
ctx.$vars,
|
|
2673
|
+
ctx.$field,
|
|
2674
|
+
ctx.__placeholders
|
|
2675
|
+
);
|
|
2676
|
+
}
|
|
2677
|
+
function looksLikeCode(code) {
|
|
2678
|
+
if (typeof code !== "string") return false;
|
|
2679
|
+
const c = code.trim();
|
|
2680
|
+
if (c.length < 3) return false;
|
|
2681
|
+
return /(return\s+|=>|function\s*\(|;|\n|\{|\})/.test(c);
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2314
2684
|
// src/nodes/processors/custom-code.ts
|
|
2315
|
-
var NodeFunction = (params) => {
|
|
2685
|
+
var NodeFunction = async (params) => {
|
|
2316
2686
|
let input = params?.inputValue ?? params?.input;
|
|
2317
2687
|
const context = params && params.fieldValues ? params.fieldValues : params || {};
|
|
2318
2688
|
let customCode = context?.customCode ?? params?.customCode;
|
|
@@ -2325,12 +2695,6 @@ var NodeFunction = (params) => {
|
|
|
2325
2695
|
else if (firstInputField.value !== void 0) input = firstInputField.value;
|
|
2326
2696
|
}
|
|
2327
2697
|
}
|
|
2328
|
-
const looksLikeCode = (code) => {
|
|
2329
|
-
if (typeof code !== "string") return false;
|
|
2330
|
-
const c = code.trim();
|
|
2331
|
-
if (c.length < 3) return false;
|
|
2332
|
-
return /(return\s+|=>|function\s*\(|;|\n|\{|\})/.test(c);
|
|
2333
|
-
};
|
|
2334
2698
|
if (typeof customCode === "string" && !looksLikeCode(customCode)) {
|
|
2335
2699
|
if (input === void 0) input = customCode;
|
|
2336
2700
|
customCode = "";
|
|
@@ -2338,18 +2702,25 @@ var NodeFunction = (params) => {
|
|
|
2338
2702
|
if (!customCode || typeof customCode === "string" && customCode.trim() === "") {
|
|
2339
2703
|
return input;
|
|
2340
2704
|
}
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2705
|
+
const sandboxContext = {
|
|
2706
|
+
input,
|
|
2707
|
+
context,
|
|
2708
|
+
$inputs: params?.results || {},
|
|
2709
|
+
$vars: context && context.variables || params?.variables || void 0,
|
|
2710
|
+
request: params?.request,
|
|
2711
|
+
params,
|
|
2712
|
+
__placeholders: params?.__codePlaceholders ?? context?.__codePlaceholders
|
|
2713
|
+
};
|
|
2714
|
+
const result = await executeSandboxed(customCode, sandboxContext, {
|
|
2715
|
+
timeout: 3e4,
|
|
2716
|
+
// 30 segundos
|
|
2717
|
+
memoryLimit: 128,
|
|
2718
|
+
// 128MB
|
|
2719
|
+
allowFetch: true
|
|
2720
|
+
});
|
|
2721
|
+
return result;
|
|
2351
2722
|
};
|
|
2352
|
-
var CustomNodeFunction = (params) => {
|
|
2723
|
+
var CustomNodeFunction = async (params) => {
|
|
2353
2724
|
const context = params && params.fieldValues ? params.fieldValues : params || {};
|
|
2354
2725
|
const customCode = context?.customCode;
|
|
2355
2726
|
if (!customCode || typeof customCode === "string" && customCode.trim() === "") {
|
|
@@ -2372,29 +2743,36 @@ var CustomNodeFunction = (params) => {
|
|
|
2372
2743
|
}
|
|
2373
2744
|
$field[fieldId] = value;
|
|
2374
2745
|
});
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2746
|
+
const sandboxContext = {
|
|
2747
|
+
$field,
|
|
2748
|
+
context,
|
|
2749
|
+
$inputs: params?.results || {},
|
|
2750
|
+
$vars: context && context.variables || params?.variables || void 0,
|
|
2751
|
+
request: params?.request,
|
|
2752
|
+
params
|
|
2753
|
+
};
|
|
2754
|
+
const result = await executeSandboxed(customCode, sandboxContext, {
|
|
2755
|
+
timeout: 3e4,
|
|
2756
|
+
// 30 segundos
|
|
2757
|
+
memoryLimit: 128,
|
|
2758
|
+
// 128MB
|
|
2759
|
+
allowFetch: true
|
|
2760
|
+
});
|
|
2761
|
+
return result;
|
|
2384
2762
|
};
|
|
2385
2763
|
var CustomCodeNode = {
|
|
2386
2764
|
label: "Custom Code",
|
|
2387
2765
|
type: "CustomCodeNode",
|
|
2388
2766
|
category: "step",
|
|
2389
2767
|
icon: "\u{1F4BB}",
|
|
2390
|
-
description: "Node para executar c\xF3digo JavaScript customizado",
|
|
2768
|
+
description: "Node para executar c\xF3digo JavaScript customizado (sandbox segura)",
|
|
2391
2769
|
fields: [
|
|
2392
2770
|
{
|
|
2393
2771
|
id: "customCode",
|
|
2394
2772
|
label: "C\xF3digo Customizado",
|
|
2395
2773
|
type: "code",
|
|
2396
2774
|
required: false,
|
|
2397
|
-
placeholder: '// Seu c\xF3digo JavaScript aqui\n// Use "input" para acessar o valor de entrada\n// Use "context" para acessar os dados do n\xF3\nreturn input;'
|
|
2775
|
+
placeholder: '// Seu c\xF3digo JavaScript aqui\n// Use "input" para acessar o valor de entrada\n// Use "context" para acessar os dados do n\xF3\n// fetch(), JSON, Math, Date est\xE3o dispon\xEDveis\nreturn input;'
|
|
2398
2776
|
},
|
|
2399
2777
|
{
|
|
2400
2778
|
id: "input",
|
|
@@ -2856,6 +3234,335 @@ var ModelGuardrailNodeFunction = async (inputs) => {
|
|
|
2856
3234
|
};
|
|
2857
3235
|
};
|
|
2858
3236
|
|
|
3237
|
+
// src/nodes/chat/getOrCreateThread/data.ts
|
|
3238
|
+
import { z as z17 } from "zod";
|
|
3239
|
+
var GetOrCreateThreadSchema = z17.object({
|
|
3240
|
+
source: z17.string().describe("Channel source: whatsapp, telegram, web, etc."),
|
|
3241
|
+
identifier: z17.string().describe("User identifier: phone number, chat_id, userId"),
|
|
3242
|
+
timeoutMinutes: z17.number().optional().describe("Inactivity timeout in minutes (default: 1440 = 24h)")
|
|
3243
|
+
});
|
|
3244
|
+
var GetOrCreateThreadNode = {
|
|
3245
|
+
label: "Get or Create Thread",
|
|
3246
|
+
type: "GetOrCreateThreadNode",
|
|
3247
|
+
category: "chat",
|
|
3248
|
+
description: "Generates or retrieves a threadId based on identifier + timeout. Ensures tenant isolation.",
|
|
3249
|
+
icon: "\u{1F9F5}",
|
|
3250
|
+
group: "Chat",
|
|
3251
|
+
tags: {
|
|
3252
|
+
execution: "async",
|
|
3253
|
+
group: "Chat"
|
|
3254
|
+
},
|
|
3255
|
+
fields: [
|
|
3256
|
+
{
|
|
3257
|
+
id: "source",
|
|
3258
|
+
label: "Source",
|
|
3259
|
+
type: "string",
|
|
3260
|
+
required: true,
|
|
3261
|
+
defaultValue: "whatsapp",
|
|
3262
|
+
placeholder: "whatsapp, telegram, web",
|
|
3263
|
+
handle: {
|
|
3264
|
+
type: "input",
|
|
3265
|
+
label: "Source",
|
|
3266
|
+
name: "source",
|
|
3267
|
+
fieldType: "string"
|
|
3268
|
+
}
|
|
3269
|
+
},
|
|
3270
|
+
{
|
|
3271
|
+
id: "identifier",
|
|
3272
|
+
label: "Identifier",
|
|
3273
|
+
type: "string",
|
|
3274
|
+
required: true,
|
|
3275
|
+
placeholder: "+5511999999999",
|
|
3276
|
+
handle: {
|
|
3277
|
+
type: "input",
|
|
3278
|
+
label: "Identifier",
|
|
3279
|
+
name: "identifier",
|
|
3280
|
+
fieldType: "string"
|
|
3281
|
+
}
|
|
3282
|
+
},
|
|
3283
|
+
{
|
|
3284
|
+
id: "timeoutMinutes",
|
|
3285
|
+
label: "Timeout (minutes)",
|
|
3286
|
+
type: "number",
|
|
3287
|
+
required: false,
|
|
3288
|
+
defaultValue: "1440",
|
|
3289
|
+
placeholder: "1440 (24 hours)"
|
|
3290
|
+
},
|
|
3291
|
+
{
|
|
3292
|
+
id: "threadId",
|
|
3293
|
+
label: "Thread ID",
|
|
3294
|
+
type: "string",
|
|
3295
|
+
required: true,
|
|
3296
|
+
typeable: false,
|
|
3297
|
+
handle: {
|
|
3298
|
+
type: "output",
|
|
3299
|
+
label: "Thread ID",
|
|
3300
|
+
name: "threadId",
|
|
3301
|
+
fieldType: "string"
|
|
3302
|
+
}
|
|
3303
|
+
},
|
|
3304
|
+
{
|
|
3305
|
+
id: "isNew",
|
|
3306
|
+
label: "Is New Thread",
|
|
3307
|
+
type: "boolean",
|
|
3308
|
+
required: true,
|
|
3309
|
+
typeable: false,
|
|
3310
|
+
handle: {
|
|
3311
|
+
type: "output",
|
|
3312
|
+
label: "Is New",
|
|
3313
|
+
name: "isNew",
|
|
3314
|
+
fieldType: "boolean"
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
]
|
|
3318
|
+
};
|
|
3319
|
+
|
|
3320
|
+
// src/nodes/chat/getOrCreateThread/function.ts
|
|
3321
|
+
var GetOrCreateThreadNodeFunction = async (inputs) => {
|
|
3322
|
+
const fieldValues = inputs.fieldValues || {};
|
|
3323
|
+
const context = inputs.context || {};
|
|
3324
|
+
const source = fieldValues.source;
|
|
3325
|
+
const identifier = fieldValues.identifier;
|
|
3326
|
+
const timeoutMinutes = Number(fieldValues.timeoutMinutes) || 1440;
|
|
3327
|
+
const companyId = context?.companyId;
|
|
3328
|
+
if (!companyId) {
|
|
3329
|
+
throw new Error("GetOrCreateThread requires companyId in context for tenant isolation");
|
|
3330
|
+
}
|
|
3331
|
+
if (!source || !identifier) {
|
|
3332
|
+
throw new Error("GetOrCreateThread requires source and identifier");
|
|
3333
|
+
}
|
|
3334
|
+
const timeoutMs = timeoutMinutes * 60 * 1e3;
|
|
3335
|
+
const thresholdDate = new Date(Date.now() - timeoutMs);
|
|
3336
|
+
const db = context?.db;
|
|
3337
|
+
if (!db) {
|
|
3338
|
+
throw new Error("GetOrCreateThread requires database connection in context");
|
|
3339
|
+
}
|
|
3340
|
+
try {
|
|
3341
|
+
const existingSessions = await db.query(`
|
|
3342
|
+
SELECT id, session_id, updated_at
|
|
3343
|
+
FROM chat_sessions
|
|
3344
|
+
WHERE company_id = $1
|
|
3345
|
+
AND metadata->>'source' = $2
|
|
3346
|
+
AND metadata->>'identifier' = $3
|
|
3347
|
+
AND updated_at > $4
|
|
3348
|
+
ORDER BY updated_at DESC
|
|
3349
|
+
LIMIT 1
|
|
3350
|
+
`, [companyId, source, identifier, thresholdDate]);
|
|
3351
|
+
if (existingSessions.rows && existingSessions.rows.length > 0) {
|
|
3352
|
+
const session = existingSessions.rows[0];
|
|
3353
|
+
await db.query(`
|
|
3354
|
+
UPDATE chat_sessions
|
|
3355
|
+
SET updated_at = NOW()
|
|
3356
|
+
WHERE id = $1 AND company_id = $2
|
|
3357
|
+
`, [session.id, companyId]);
|
|
3358
|
+
return {
|
|
3359
|
+
threadId: session.session_id,
|
|
3360
|
+
isNew: false
|
|
3361
|
+
};
|
|
3362
|
+
}
|
|
3363
|
+
const newSessionId = crypto.randomUUID();
|
|
3364
|
+
const workflowId = context?.workflowId;
|
|
3365
|
+
await db.query(`
|
|
3366
|
+
INSERT INTO chat_sessions (workflow_id, company_id, chat_id, session_id, metadata)
|
|
3367
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
3368
|
+
`, [
|
|
3369
|
+
workflowId || companyId,
|
|
3370
|
+
// fallback to companyId if no workflowId
|
|
3371
|
+
companyId,
|
|
3372
|
+
`${source}:${identifier}`,
|
|
3373
|
+
// chatId as composite key
|
|
3374
|
+
newSessionId,
|
|
3375
|
+
JSON.stringify({ source, identifier })
|
|
3376
|
+
]);
|
|
3377
|
+
return {
|
|
3378
|
+
threadId: newSessionId,
|
|
3379
|
+
isNew: true
|
|
3380
|
+
};
|
|
3381
|
+
} catch (error) {
|
|
3382
|
+
console.error("[GetOrCreateThread] Error:", error);
|
|
3383
|
+
throw error;
|
|
3384
|
+
}
|
|
3385
|
+
};
|
|
3386
|
+
|
|
3387
|
+
// src/nodes/chat/chatLog/data.ts
|
|
3388
|
+
import { z as z18 } from "zod";
|
|
3389
|
+
var ChatLogSchema = z18.object({
|
|
3390
|
+
threadId: z18.string().describe("Thread ID from GetOrCreateThread"),
|
|
3391
|
+
direction: z18.enum(["inbound", "outbound"]).describe("Message direction"),
|
|
3392
|
+
content: z18.string().describe("Message content"),
|
|
3393
|
+
source: z18.string().optional().describe("Channel source"),
|
|
3394
|
+
identifier: z18.string().optional().describe("User identifier"),
|
|
3395
|
+
metadata: z18.record(z18.string(), z18.any()).optional().describe("Additional metadata")
|
|
3396
|
+
});
|
|
3397
|
+
var ChatLogNode = {
|
|
3398
|
+
label: "Chat Log",
|
|
3399
|
+
type: "ChatLogNode",
|
|
3400
|
+
category: "chat",
|
|
3401
|
+
description: "Saves a message to the chat history. Supports inbound (user) and outbound (assistant) messages.",
|
|
3402
|
+
icon: "\u{1F4AC}",
|
|
3403
|
+
group: "Chat",
|
|
3404
|
+
tags: {
|
|
3405
|
+
execution: "async",
|
|
3406
|
+
group: "Chat"
|
|
3407
|
+
},
|
|
3408
|
+
fields: [
|
|
3409
|
+
{
|
|
3410
|
+
id: "threadId",
|
|
3411
|
+
label: "Thread ID",
|
|
3412
|
+
type: "string",
|
|
3413
|
+
required: true,
|
|
3414
|
+
placeholder: "From GetOrCreateThread node",
|
|
3415
|
+
handle: {
|
|
3416
|
+
type: "input",
|
|
3417
|
+
label: "Thread ID",
|
|
3418
|
+
name: "threadId",
|
|
3419
|
+
fieldType: "string"
|
|
3420
|
+
}
|
|
3421
|
+
},
|
|
3422
|
+
{
|
|
3423
|
+
id: "direction",
|
|
3424
|
+
label: "Direction",
|
|
3425
|
+
type: "select",
|
|
3426
|
+
required: true,
|
|
3427
|
+
defaultValue: "inbound",
|
|
3428
|
+
options: [
|
|
3429
|
+
{ label: "Inbound (User \u2192 System)", value: "inbound" },
|
|
3430
|
+
{ label: "Outbound (System \u2192 User)", value: "outbound" }
|
|
3431
|
+
]
|
|
3432
|
+
},
|
|
3433
|
+
{
|
|
3434
|
+
id: "content",
|
|
3435
|
+
label: "Content",
|
|
3436
|
+
type: "string",
|
|
3437
|
+
required: true,
|
|
3438
|
+
placeholder: "Message content",
|
|
3439
|
+
handle: {
|
|
3440
|
+
type: "input",
|
|
3441
|
+
label: "Content",
|
|
3442
|
+
name: "content",
|
|
3443
|
+
fieldType: "string"
|
|
3444
|
+
}
|
|
3445
|
+
},
|
|
3446
|
+
{
|
|
3447
|
+
id: "source",
|
|
3448
|
+
label: "Source",
|
|
3449
|
+
type: "string",
|
|
3450
|
+
required: false,
|
|
3451
|
+
defaultValue: "whatsapp",
|
|
3452
|
+
placeholder: "whatsapp, telegram, web",
|
|
3453
|
+
handle: {
|
|
3454
|
+
type: "input",
|
|
3455
|
+
label: "Source",
|
|
3456
|
+
name: "source",
|
|
3457
|
+
fieldType: "string"
|
|
3458
|
+
}
|
|
3459
|
+
},
|
|
3460
|
+
{
|
|
3461
|
+
id: "identifier",
|
|
3462
|
+
label: "Identifier",
|
|
3463
|
+
type: "string",
|
|
3464
|
+
required: false,
|
|
3465
|
+
placeholder: "+5511999999999",
|
|
3466
|
+
handle: {
|
|
3467
|
+
type: "input",
|
|
3468
|
+
label: "Identifier",
|
|
3469
|
+
name: "identifier",
|
|
3470
|
+
fieldType: "string"
|
|
3471
|
+
}
|
|
3472
|
+
},
|
|
3473
|
+
{
|
|
3474
|
+
id: "messageId",
|
|
3475
|
+
label: "Message ID",
|
|
3476
|
+
type: "string",
|
|
3477
|
+
required: true,
|
|
3478
|
+
typeable: false,
|
|
3479
|
+
handle: {
|
|
3480
|
+
type: "output",
|
|
3481
|
+
label: "Message ID",
|
|
3482
|
+
name: "messageId",
|
|
3483
|
+
fieldType: "string"
|
|
3484
|
+
}
|
|
3485
|
+
},
|
|
3486
|
+
{
|
|
3487
|
+
id: "success",
|
|
3488
|
+
label: "Success",
|
|
3489
|
+
type: "boolean",
|
|
3490
|
+
required: true,
|
|
3491
|
+
typeable: false,
|
|
3492
|
+
handle: {
|
|
3493
|
+
type: "output",
|
|
3494
|
+
label: "Success",
|
|
3495
|
+
name: "success",
|
|
3496
|
+
fieldType: "boolean"
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
]
|
|
3500
|
+
};
|
|
3501
|
+
|
|
3502
|
+
// src/nodes/chat/chatLog/function.ts
|
|
3503
|
+
var ChatLogNodeFunction = async (inputs) => {
|
|
3504
|
+
const fieldValues = inputs.fieldValues || {};
|
|
3505
|
+
const context = inputs.context || {};
|
|
3506
|
+
const threadId = fieldValues.threadId;
|
|
3507
|
+
const direction = fieldValues.direction;
|
|
3508
|
+
const content = fieldValues.content;
|
|
3509
|
+
const source = fieldValues.source;
|
|
3510
|
+
const identifier = fieldValues.identifier;
|
|
3511
|
+
const companyId = context?.companyId;
|
|
3512
|
+
if (!companyId) {
|
|
3513
|
+
throw new Error("ChatLog requires companyId in context for tenant isolation");
|
|
3514
|
+
}
|
|
3515
|
+
if (!threadId || !content) {
|
|
3516
|
+
throw new Error("ChatLog requires threadId and content");
|
|
3517
|
+
}
|
|
3518
|
+
const role = direction === "inbound" ? "user" : "assistant";
|
|
3519
|
+
const db = context?.db;
|
|
3520
|
+
if (!db) {
|
|
3521
|
+
throw new Error("ChatLog requires database connection in context");
|
|
3522
|
+
}
|
|
3523
|
+
try {
|
|
3524
|
+
const sessionResult = await db.query(`
|
|
3525
|
+
SELECT id FROM chat_sessions
|
|
3526
|
+
WHERE session_id = $1 AND company_id = $2
|
|
3527
|
+
LIMIT 1
|
|
3528
|
+
`, [threadId, companyId]);
|
|
3529
|
+
if (!sessionResult.rows || sessionResult.rows.length === 0) {
|
|
3530
|
+
throw new Error(`Session not found for threadId: ${threadId}`);
|
|
3531
|
+
}
|
|
3532
|
+
const sessionId = sessionResult.rows[0].id;
|
|
3533
|
+
const messageId = crypto.randomUUID();
|
|
3534
|
+
await db.query(`
|
|
3535
|
+
INSERT INTO chat_messages (id, session_id, role, content, metadata)
|
|
3536
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
3537
|
+
`, [
|
|
3538
|
+
messageId,
|
|
3539
|
+
sessionId,
|
|
3540
|
+
role,
|
|
3541
|
+
content,
|
|
3542
|
+
JSON.stringify({
|
|
3543
|
+
direction,
|
|
3544
|
+
source: source || null,
|
|
3545
|
+
identifier: identifier || null
|
|
3546
|
+
})
|
|
3547
|
+
]);
|
|
3548
|
+
await db.query(`
|
|
3549
|
+
UPDATE chat_sessions
|
|
3550
|
+
SET updated_at = NOW()
|
|
3551
|
+
WHERE id = $1 AND company_id = $2
|
|
3552
|
+
`, [sessionId, companyId]);
|
|
3553
|
+
return {
|
|
3554
|
+
messageId,
|
|
3555
|
+
success: true
|
|
3556
|
+
};
|
|
3557
|
+
} catch (error) {
|
|
3558
|
+
console.error("[ChatLog] Error:", error);
|
|
3559
|
+
return {
|
|
3560
|
+
messageId: null,
|
|
3561
|
+
success: false
|
|
3562
|
+
};
|
|
3563
|
+
}
|
|
3564
|
+
};
|
|
3565
|
+
|
|
2859
3566
|
// src/nodes/consts/nodes.ts
|
|
2860
3567
|
var nodes = [
|
|
2861
3568
|
ChatInputNode,
|
|
@@ -2882,7 +3589,9 @@ var nodes = [
|
|
|
2882
3589
|
PromptGuardrailNode,
|
|
2883
3590
|
RuleGuardrailNode,
|
|
2884
3591
|
FunctionGuardrailNode,
|
|
2885
|
-
ModelGuardrailNode
|
|
3592
|
+
ModelGuardrailNode,
|
|
3593
|
+
GetOrCreateThreadNode,
|
|
3594
|
+
ChatLogNode
|
|
2886
3595
|
];
|
|
2887
3596
|
var nodes_default = nodes;
|
|
2888
3597
|
|
|
@@ -3004,7 +3713,9 @@ var nodeFunctions = {
|
|
|
3004
3713
|
PromptGuardrailNode: PromptGuardrailNodeFunction,
|
|
3005
3714
|
RuleGuardrailNode: RuleGuardrailNodeFunction,
|
|
3006
3715
|
FunctionGuardrailNode: FunctionGuardrailNodeFunction,
|
|
3007
|
-
ModelGuardrailNode: ModelGuardrailNodeFunction
|
|
3716
|
+
ModelGuardrailNode: ModelGuardrailNodeFunction,
|
|
3717
|
+
GetOrCreateThreadNode: GetOrCreateThreadNodeFunction,
|
|
3718
|
+
ChatLogNode: ChatLogNodeFunction
|
|
3008
3719
|
};
|
|
3009
3720
|
var node_functions_default = nodeFunctions;
|
|
3010
3721
|
|
|
@@ -3118,12 +3829,12 @@ var HttpPutInputNodeFunction = (params) => {
|
|
|
3118
3829
|
};
|
|
3119
3830
|
|
|
3120
3831
|
// src/nodes/inputs/http/put/schema.ts
|
|
3121
|
-
import { z as
|
|
3122
|
-
var HttpPutInputNodeSchema =
|
|
3832
|
+
import { z as z19 } from "zod";
|
|
3833
|
+
var HttpPutInputNodeSchema = z19.object({
|
|
3123
3834
|
route: RouteSchema,
|
|
3124
|
-
queryParams:
|
|
3125
|
-
headers:
|
|
3126
|
-
body:
|
|
3835
|
+
queryParams: z19.array(QueryParamSchema).optional().describe("Query parameters configuration"),
|
|
3836
|
+
headers: z19.array(HeaderSchema).optional().describe("Headers configuration"),
|
|
3837
|
+
body: z19.array(BodyFieldSchema).optional().describe("Body fields configuration")
|
|
3127
3838
|
});
|
|
3128
3839
|
|
|
3129
3840
|
// src/nodes/inputs/http/delete/data.ts
|
|
@@ -3215,11 +3926,11 @@ var HttpDeleteInputNodeFunction = async (params) => {
|
|
|
3215
3926
|
};
|
|
3216
3927
|
|
|
3217
3928
|
// src/nodes/inputs/http/delete/schema.ts
|
|
3218
|
-
import { z as
|
|
3219
|
-
var HttpDeleteInputNodeSchema =
|
|
3929
|
+
import { z as z20 } from "zod";
|
|
3930
|
+
var HttpDeleteInputNodeSchema = z20.object({
|
|
3220
3931
|
route: RouteSchema,
|
|
3221
|
-
queryParams:
|
|
3222
|
-
headers:
|
|
3932
|
+
queryParams: z20.array(QueryParamSchema).optional().describe("Query parameters configuration"),
|
|
3933
|
+
headers: z20.array(HeaderSchema).optional().describe("Headers configuration")
|
|
3223
3934
|
});
|
|
3224
3935
|
|
|
3225
3936
|
// src/nodes/inputs/http/patch/data.ts
|
|
@@ -3332,12 +4043,12 @@ var HttpPatchInputNodeFunction = (params) => {
|
|
|
3332
4043
|
};
|
|
3333
4044
|
|
|
3334
4045
|
// src/nodes/inputs/http/patch/schema.ts
|
|
3335
|
-
import { z as
|
|
3336
|
-
var HttpPatchInputNodeSchema =
|
|
4046
|
+
import { z as z21 } from "zod";
|
|
4047
|
+
var HttpPatchInputNodeSchema = z21.object({
|
|
3337
4048
|
route: RouteSchema,
|
|
3338
|
-
queryParams:
|
|
3339
|
-
headers:
|
|
3340
|
-
body:
|
|
4049
|
+
queryParams: z21.array(QueryParamSchema).optional().describe("Query parameters configuration"),
|
|
4050
|
+
headers: z21.array(HeaderSchema).optional().describe("Headers configuration"),
|
|
4051
|
+
body: z21.array(BodyFieldSchema).optional().describe("Body fields configuration")
|
|
3341
4052
|
});
|
|
3342
4053
|
|
|
3343
4054
|
// src/nodes/inputs/http/utils.ts
|
|
@@ -3396,7 +4107,13 @@ export {
|
|
|
3396
4107
|
AiToolNodeFunction,
|
|
3397
4108
|
AiToolNodeSchema,
|
|
3398
4109
|
BodyFieldSchema,
|
|
4110
|
+
ChatLogNode,
|
|
4111
|
+
ChatLogNodeFunction,
|
|
4112
|
+
ChatLogSchema,
|
|
3399
4113
|
CustomToolSchema,
|
|
4114
|
+
GetOrCreateThreadNode,
|
|
4115
|
+
GetOrCreateThreadNodeFunction,
|
|
4116
|
+
GetOrCreateThreadSchema,
|
|
3400
4117
|
HTTP_METHODS,
|
|
3401
4118
|
HTTP_NODE_TYPES,
|
|
3402
4119
|
HeaderSchema,
|