n8n-nodes-tembory 1.0.6 → 1.0.8

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.
@@ -928,6 +928,34 @@ const applyOperationalPreset = (advanced = {}) => {
928
928
  toolHistoryLastN: 5,
929
929
  recentMessagesLastN: 4,
930
930
  },
931
+ productionNano: {
932
+ compactForAgent: true,
933
+ includeContextHeader: true,
934
+ includeSummary: true,
935
+ includeScores: false,
936
+ includeDiagnostics: false,
937
+ includeRelations: false,
938
+ includeToolHistory: true,
939
+ includeToolResults: true,
940
+ persistToolFactsToMem0: false,
941
+ includeToolHistorySemanticFallback: false,
942
+ includeProfileFacts: true,
943
+ includeOperationalState: true,
944
+ includeActionLedger: false,
945
+ includeEntityTimeline: false,
946
+ includeWorkingMemory: true,
947
+ includeDecisionState: true,
948
+ includeMemoryCompression: true,
949
+ includeRecentMessages: false,
950
+ includeRecentHighlights: false,
951
+ topK: 4,
952
+ lastN: 4,
953
+ maxReturn: 6,
954
+ toolHistoryLastN: 6,
955
+ recentMessagesLastN: 2,
956
+ vectorMemoryMaxChars: 220,
957
+ contextMaxChars: 6000,
958
+ },
931
959
  audit: {
932
960
  includeContextHeader: true,
933
961
  includeSummary: true,
@@ -976,6 +1004,16 @@ const dedupeToolHistory = (items) => {
976
1004
  const key = toolEventKey(item);
977
1005
  if (seen.has(key))
978
1006
  continue;
1007
+ const atMs = new Date(item.at || 0).getTime();
1008
+ const nearDuplicate = out.some((existing) => {
1009
+ const existingMs = new Date(existing.at || 0).getTime();
1010
+ return Math.abs(atMs - existingMs) < 1000
1011
+ && String(item.name || '') === String(existing.name || '')
1012
+ && canonicalToolInput(item.input) === canonicalToolInput(existing.input)
1013
+ && String(item.result || '') === String(existing.result || '');
1014
+ });
1015
+ if (nearDuplicate)
1016
+ continue;
979
1017
  seen.add(key);
980
1018
  out.push(item);
981
1019
  }
@@ -1211,7 +1249,7 @@ const deriveMemoryCompression = ({ recentMessages = [], toolHistory = [], profil
1211
1249
  const lastMessages = pruneByLimit(recentMessages || [], maxItems).map((msg) => `${msg.role || 'message'}: ${truncate(msg.content || '', 160)}`);
1212
1250
  const lastTools = pruneByLimit(toolHistory || [], maxItems).map((tool) => `${tool.name || 'tool'}:${tool.ok === false ? 'failed' : 'ok'}${tool.at ? `@${tool.at}` : ''}`);
1213
1251
  const profile = renderProfileFacts(profileFacts);
1214
- const activeMemories = (vectorMemories || []).map(enrichContextMemory).filter((item) => item.status === 'active').slice(0, maxItems);
1252
+ const activeMemories = (vectorMemories || []).map((memory) => ({ ...enrichContextMemory(memory), compact_memory: contextMemoryText(memory, 220) })).filter((item) => item.status === 'active').slice(0, maxItems);
1215
1253
  return {
1216
1254
  turn_summary: lastMessages.length ? lastMessages : [],
1217
1255
  session_summary: {
@@ -1223,7 +1261,7 @@ const deriveMemoryCompression = ({ recentMessages = [], toolHistory = [], profil
1223
1261
  workflow_summary: {
1224
1262
  active_memory_count: activeMemories.length,
1225
1263
  top_active_memories: activeMemories.map((item) => ({
1226
- memory: truncate(item.memory, 220),
1264
+ memory: item.compact_memory,
1227
1265
  confidence: item.confidence,
1228
1266
  source: item.source,
1229
1267
  })),
@@ -1298,6 +1336,105 @@ const contextMemoryText = (memory, max = 700) => {
1298
1336
  }
1299
1337
  return truncate(text, max);
1300
1338
  };
1339
+ const approxTokenCount = (text) => Math.ceil(String(text || '').length / 4);
1340
+ const importantJsonFields = ['reservation_id', 'confirmation_id', 'selected_from_message', 'status', 'next_step', 'available_slots', 'timezone', 'error'];
1341
+ const pickImportantFields = (value) => {
1342
+ if (value === null || value === undefined)
1343
+ return {};
1344
+ const out = {};
1345
+ const visit = (item) => {
1346
+ if (!item || typeof item !== 'object')
1347
+ return;
1348
+ if (Array.isArray(item)) {
1349
+ item.slice(0, 3).forEach(visit);
1350
+ return;
1351
+ }
1352
+ for (const key of importantJsonFields) {
1353
+ if (item[key] !== undefined && out[key] === undefined)
1354
+ out[key] = item[key];
1355
+ }
1356
+ if (item.args)
1357
+ visit(item.args);
1358
+ if (item.result)
1359
+ visit(item.result);
1360
+ };
1361
+ visit(value);
1362
+ return out;
1363
+ };
1364
+ const compactToolResult = (result, max = 360) => {
1365
+ const text = String(result || '');
1366
+ if (!text.trim())
1367
+ return undefined;
1368
+ try {
1369
+ const parsed = JSON.parse(text);
1370
+ const picked = pickImportantFields(parsed);
1371
+ if (Object.keys(picked).length)
1372
+ return truncate(safeStringify(picked), max);
1373
+ }
1374
+ catch { }
1375
+ const picked = {};
1376
+ for (const key of importantJsonFields) {
1377
+ const re = new RegExp(`["']?${key}["']?\\s*[:=]\\s*["']?([^"',}\\]]+)`, 'i');
1378
+ const match = re.exec(text);
1379
+ if (match)
1380
+ picked[key] = match[1];
1381
+ }
1382
+ if (Object.keys(picked).length)
1383
+ return truncate(safeStringify(picked), max);
1384
+ return truncate(text, max);
1385
+ };
1386
+ const compactToolHistoryForAgent = (toolHistory = [], maxItems = 6, includeResults = true) => pruneByLimit(toolHistory || [], maxItems).map((tool) => ({
1387
+ name: tool.name,
1388
+ status: tool.ok === false ? 'failed' : 'ok',
1389
+ at: tool.at,
1390
+ input: truncate(String(tool.input || ''), 180) || undefined,
1391
+ result: includeResults ? compactToolResult(tool.result, 360) : undefined,
1392
+ }));
1393
+ const compactOperationalStateForAgent = (state = {}) => {
1394
+ const agenda = state.agenda_state || {};
1395
+ return {
1396
+ last_tool: state.last_tool || null,
1397
+ tool_counts: state.tool_counts || {},
1398
+ agenda_state: {
1399
+ has_availability: Boolean(agenda.has_availability),
1400
+ has_pre_reservation: Boolean(agenda.has_pre_reservation),
1401
+ has_confirmation: Boolean(agenda.has_confirmation),
1402
+ has_pending_pre_reservation: Boolean(agenda.has_pending_pre_reservation),
1403
+ latest_availability: compactToolResult(agenda.latest_availability_result),
1404
+ latest_pre_reservation: compactToolResult(agenda.latest_pre_reservation_result),
1405
+ latest_confirmation: compactToolResult(agenda.latest_confirmation_result),
1406
+ },
1407
+ blocked_without_context: state.blocked_without_context || [],
1408
+ guidance: (state.guidance || []).slice(0, 3),
1409
+ };
1410
+ };
1411
+ const compactMemoryCompressionForAgent = (compression = {}) => ({
1412
+ turn_summary: (compression.turn_summary || []).slice(-3),
1413
+ session_tools: (((compression.session_summary || {}).tools) || []).slice(-6),
1414
+ agenda_state: ((compression.session_summary || {}).agenda_state) ? {
1415
+ has_availability: Boolean(compression.session_summary.agenda_state.has_availability),
1416
+ has_pre_reservation: Boolean(compression.session_summary.agenda_state.has_pre_reservation),
1417
+ has_confirmation: Boolean(compression.session_summary.agenda_state.has_confirmation),
1418
+ has_pending_pre_reservation: Boolean(compression.session_summary.agenda_state.has_pending_pre_reservation),
1419
+ } : undefined,
1420
+ active_memory_count: ((compression.workflow_summary || {}).active_memory_count) || 0,
1421
+ });
1422
+ const compactVectorMemoriesForAgent = (vectorMemories = [], toolHistory = [], maxItems = 3) => {
1423
+ const hasStructuredTools = Array.isArray(toolHistory) && toolHistory.length > 0;
1424
+ return (vectorMemories || [])
1425
+ .map((memory) => contextMemoryText(memory, 220))
1426
+ .filter(Boolean)
1427
+ .filter((text) => !hasStructuredTools || !/^\[tool_events_extracted\]/i.test(text))
1428
+ .slice(0, maxItems);
1429
+ };
1430
+ const contextSizeOfMessages = (messages = []) => {
1431
+ const perMessage = (messages || []).map((message, index) => {
1432
+ const content = String(message.content || '');
1433
+ return { index, role: message.role || 'system', chars: content.length, approx_tokens: approxTokenCount(content) };
1434
+ });
1435
+ const chars = perMessage.reduce((sum, item) => sum + item.chars, 0);
1436
+ return { chars, approx_tokens: approxTokenCount((messages || []).map((m) => m.content || '').join('\n')), messages: perMessage };
1437
+ };
1301
1438
  const wrapElefaiMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1302
1439
  get(target, prop) {
1303
1440
  if (prop === 'loadMemoryVariables') {
@@ -1367,14 +1504,81 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1367
1504
  const includeHeader = adv.includeContextHeader !== false;
1368
1505
  const includeSummary = adv.includeSummary !== false;
1369
1506
  const includeScores = adv.includeScores !== false;
1507
+ const compactForAgent = Boolean(adv.compactForAgent);
1370
1508
  const sections = [];
1371
1509
  if (includeHeader) {
1372
1510
  sections.push({
1373
1511
  section: 'context_header',
1374
1512
  title: 'Tembory context',
1375
- value: 'Use this context as read-only memory. Prefer it over guessing. Do not mention internal section names to the user. Treat next_expected_action as an instruction, not as a suggestion. If it says to call a tool now, call that tool instead of asking the user the same question again. If the user asks to continue, chooses a slot, says ok/sim, reserve, confirm, update, cancel, or performs any downstream action that depends on a prior tool result, first verify the required prior result in tool_history, recent_messages, or vector memories. If the required prior result is absent, do not call the downstream tool; ask for the missing context or call the appropriate prerequisite tool.',
1513
+ value: compactForAgent
1514
+ ? 'Read-only memory. Follow next_expected_action when present. Before calling downstream tools, verify required prior tool context in tool_history or operational_state. Do not repeat tools listed in do_not_repeat_tools.'
1515
+ : 'Use this context as read-only memory. Prefer it over guessing. Do not mention internal section names to the user. Treat next_expected_action as an instruction, not as a suggestion. If it says to call a tool now, call that tool instead of asking the user the same question again. If the user asks to continue, chooses a slot, says ok/sim, reserve, confirm, update, cancel, or performs any downstream action that depends on a prior tool result, first verify the required prior result in tool_history, recent_messages, or vector memories. If the required prior result is absent, do not call the downstream tool; ask for the missing context or call the appropriate prerequisite tool.',
1376
1516
  });
1377
1517
  }
1518
+ if (compactForAgent) {
1519
+ if (includeSummary) {
1520
+ const summary = compactVectorMemoriesForAgent(vectorMemories, toolHistory, Number(adv.summaryMaxFacts || 3));
1521
+ sections.push({ section: 'summary', title: 'Summary', value: summary.length ? summary : null, why_null: summary.length ? undefined : 'no non-tool vector memories to summarize' });
1522
+ }
1523
+ sections.push({
1524
+ section: 'working_memory',
1525
+ title: 'Working memory',
1526
+ value: adv.includeWorkingMemory === false ? null : (workingMemory || {}),
1527
+ why_null: adv.includeWorkingMemory === false ? 'working memory disabled' : undefined,
1528
+ });
1529
+ sections.push({
1530
+ section: 'decision_state',
1531
+ title: 'Decision state',
1532
+ value: adv.includeDecisionState === false ? null : (decisionState || {}),
1533
+ why_null: adv.includeDecisionState === false ? 'decision state disabled' : undefined,
1534
+ });
1535
+ sections.push({
1536
+ section: 'operational_state',
1537
+ title: 'Operational state',
1538
+ value: adv.includeOperationalState === false ? null : compactOperationalStateForAgent(operationalState || {}),
1539
+ why_null: adv.includeOperationalState === false ? 'operational state disabled' : undefined,
1540
+ });
1541
+ sections.push({
1542
+ section: 'tool_history',
1543
+ title: 'Tool history',
1544
+ value: {
1545
+ enabled: adv.includeToolHistory !== false,
1546
+ items: adv.includeToolHistory === false ? [] : compactToolHistoryForAgent(toolHistory, adv.toolHistoryLastN || 6, adv.includeToolResults !== false),
1547
+ },
1548
+ });
1549
+ sections.push({
1550
+ section: 'profile_facts',
1551
+ title: 'Profile facts',
1552
+ value: adv.includeProfileFacts === false ? null : renderProfileFacts(profileFacts),
1553
+ why_null: adv.includeProfileFacts === false ? 'profile facts disabled' : undefined,
1554
+ });
1555
+ sections.push({
1556
+ section: 'memory_compression',
1557
+ title: 'Memory compression',
1558
+ value: adv.includeMemoryCompression === false ? null : compactMemoryCompressionForAgent(memoryCompression || {}),
1559
+ why_null: adv.includeMemoryCompression === false ? 'memory compression disabled' : undefined,
1560
+ });
1561
+ const audit = {
1562
+ kind: 'elefai.brain.context.v1',
1563
+ meta: { user_id: userId, payloadFormat, query, compact_for_agent: true },
1564
+ sections,
1565
+ diagnostics,
1566
+ };
1567
+ const renderCompactSection = (section) => {
1568
+ if (section.value === null || section.value === undefined)
1569
+ return `## ${section.title}\n${section.why_null || 'empty'}`;
1570
+ if (typeof section.value === 'string')
1571
+ return `## ${section.title}\n${section.value}`;
1572
+ if (Array.isArray(section.value))
1573
+ return `## ${section.title}\n${section.value.length ? section.value.map((item) => typeof item === 'string' ? `- ${item}` : `- ${truncate(safeStringify(item), 420)}`).join('\n') : '(empty)'}`;
1574
+ return `## ${section.title}\n${truncate(safeStringify(section.value), 900)}`;
1575
+ };
1576
+ if (payloadFormat === 'auditJson')
1577
+ return [{ role: 'system', content: JSON.stringify(audit, null, 2) }];
1578
+ if (payloadFormat === 'auditBlocks')
1579
+ return sections.map((section) => ({ role: 'system', content: renderCompactSection(section) }));
1580
+ return [{ role: 'system', content: truncate(sections.map(renderCompactSection).join('\n\n'), Number(adv.contextMaxChars || 6000)) }];
1581
+ }
1378
1582
  if (includeSummary) {
1379
1583
  const summary = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 360)).filter(Boolean);
1380
1584
  sections.push({ section: 'summary', title: 'Summary', value: summary.length ? summary : null, why_null: summary.length ? undefined : 'no vector memories to summarize' });
@@ -1644,6 +1848,7 @@ class Mem0Memory {
1644
1848
  { name: 'Diagnóstico Completo', value: 'diagnostic' },
1645
1849
  { name: 'Produção Balanceada', value: 'productionBalanced' },
1646
1850
  { name: 'Produção Econômica', value: 'productionCheap' },
1851
+ { name: 'Produção Nano/SLM', value: 'productionNano' },
1647
1852
  { name: 'Auditoria', value: 'audit' },
1648
1853
  ],
1649
1854
  default: 'custom',
@@ -1663,6 +1868,8 @@ class Mem0Memory {
1663
1868
  { displayName: 'MMR (diversidade)', name: 'mmr', type: 'boolean', default: true, description: 'Aplicar diversidade de resultados (Maximal Marginal Relevance)', displayOptions: { show: { '/retrievalMode': ['hybrid'] } } },
1664
1869
  { displayName: 'MMR Lambda', name: 'mmrLambda', type: 'number', typeOptions: { minValue: 0, maxValue: 1, numberPrecision: 2 }, default: 0.5, description: 'Equilíbrio entre relevância e diversidade no MMR', displayOptions: { show: { '/retrievalMode': ['hybrid'] } } },
1665
1870
  { displayName: 'Incluir Cabeçalho de Contexto', name: 'includeContextHeader', type: 'boolean', default: true },
1871
+ { displayName: 'Compactar Contexto Para Agente', name: 'compactForAgent', type: 'boolean', default: false, description: 'Entrega ao agente apenas o estado acionável: working memory, decisões, estado operacional e histórico de tools compacto. Mantém detalhes completos no audit/debug.' },
1872
+ { displayName: 'Máximo de Caracteres do Contexto', name: 'contextMaxChars', type: 'number', default: 6000, description: 'Limite final aplicado quando Compactar Contexto Para Agente estiver ativo.' },
1666
1873
  { displayName: 'Incluir Resumo', name: 'includeSummary', type: 'boolean', default: true },
1667
1874
  { displayName: 'Máximo de Fatos no Resumo', name: 'summaryMaxFacts', type: 'number', default: 4 },
1668
1875
  { displayName: 'Incluir Scores', name: 'includeScores', type: 'boolean', default: true },
@@ -2352,12 +2559,13 @@ class Mem0Memory {
2352
2559
  diagnostics,
2353
2560
  adv,
2354
2561
  });
2562
+ diagnostics.contextSize = contextSizeOfMessages(payload);
2355
2563
  const normalizedVectorMemories = vectorMemories.map((m) => {
2356
2564
  const scoreMeta = scoreMetaOf(m);
2357
2565
  const rawScore = scoreOf(m);
2358
2566
  const enriched = enrichContextMemory(m);
2359
2567
  return {
2360
- memory: memoryText(m),
2568
+ memory: contextMemoryText(m, Number(adv.vectorMemoryMaxChars || 700)),
2361
2569
  score: adv.includeScores === false ? undefined : (rawScore !== undefined ? rawScore : scoreMeta.hybridScore ?? scoreMeta.recencyScore ?? 'unavailable'),
2362
2570
  semantic_score: adv.includeScores === false ? undefined : scoreMeta.semanticScore,
2363
2571
  recency_score: adv.includeScores === false ? undefined : scoreMeta.recencyScore,
@@ -2372,7 +2580,7 @@ class Mem0Memory {
2372
2580
  raw: adv.includeDiagnostics ? m : undefined,
2373
2581
  };
2374
2582
  });
2375
- const summary = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map(memoryText).filter(Boolean);
2583
+ const summary = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 360)).filter(Boolean);
2376
2584
  const contextText = payload.map((message) => String(message.content || '')).join('\n\n');
2377
2585
  const audit = {
2378
2586
  kind: 'elefai.brain.context.v1',
@@ -2394,6 +2602,7 @@ class Mem0Memory {
2394
2602
  includeOperationalState: adv.includeOperationalState !== false,
2395
2603
  includeActionLedger: adv.includeActionLedger !== false,
2396
2604
  includeEntityTimeline: adv.includeEntityTimeline !== false,
2605
+ compactForAgent: Boolean(adv.compactForAgent),
2397
2606
  includeWorkingMemory: adv.includeWorkingMemory !== false,
2398
2607
  includeDecisionState: adv.includeDecisionState !== false,
2399
2608
  includeMemoryCompression: adv.includeMemoryCompression !== false,
@@ -2577,4 +2786,9 @@ exports.__private = {
2577
2786
  mergeProfileFacts,
2578
2787
  renderProfileFacts,
2579
2788
  isNoisyProfileValue,
2789
+ approxTokenCount,
2790
+ contextSizeOfMessages,
2791
+ compactToolResult,
2792
+ compactToolHistoryForAgent,
2793
+ compactOperationalStateForAgent,
2580
2794
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Tembory node for n8n AI Agents with profile, tools, timeline, graph and semantic memory",
5
5
  "license": "MIT",
6
6
  "homepage": "https://tembory.com",
@@ -24,7 +24,8 @@
24
24
  "lintfix": "tslint --fix -p tsconfig.json -c tslint.json",
25
25
  "test": "node --test test/*.test.js",
26
26
  "smoke:n8n:multiturn": "node scripts/smoke-n8n-multiturn-tools.js",
27
- "prepublishOnly": "npm run build"
27
+ "simulate:agent-context": "node scripts/simulate-agent-context.js",
28
+ "prepublishOnly": "npm test"
28
29
  },
29
30
  "files": [
30
31
  "dist",
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ const Module = require('node:module');
3
+
4
+ const originalLoad = Module._load;
5
+ Module._load = function patchedLoad(request, parent, isMain) {
6
+ if (request === 'n8n-workflow') {
7
+ return {
8
+ NodeConnectionTypes: {
9
+ AiMemory: 'ai_memory',
10
+ AiLanguageModel: 'ai_languageModel',
11
+ AiEmbedding: 'ai_embedding',
12
+ },
13
+ NodeApiError: class NodeApiError extends Error {},
14
+ };
15
+ }
16
+ if (request === '@langchain/core/messages') {
17
+ return {
18
+ HumanMessage: class HumanMessage { constructor(content) { this.content = content; } _getType() { return 'human'; } },
19
+ AIMessage: class AIMessage { constructor(content) { this.content = content; } _getType() { return 'ai'; } },
20
+ SystemMessage: class SystemMessage { constructor(content) { this.content = content; } _getType() { return 'system'; } },
21
+ };
22
+ }
23
+ return originalLoad.call(this, request, parent, isMain);
24
+ };
25
+
26
+ const core = require('../dist/nodes/Mem0/Mem0Memory.node.js').__private;
27
+
28
+ const toolHistory = [
29
+ {
30
+ id: 'tool_ry3twg',
31
+ name: 'agenda_consultar_disponibilidade',
32
+ input: '',
33
+ ok: true,
34
+ at: '2026-05-14T05:34:12.460Z',
35
+ source: 'used_tools_text',
36
+ result: '[{"args":{"tool":"agenda_consultar_disponibilidade","available_slots":"2026-05-12T09:00,2026-05-12T14:00,2026-05-13T10:30","timezone":"America/Sao_Paulo","next_step":"escolher_um_horario"}}]',
37
+ },
38
+ {
39
+ id: 'tool_mu9qd2',
40
+ name: 'agenda_pre_reservar_horario',
41
+ input: '',
42
+ ok: true,
43
+ at: '2026-05-14T05:34:34.017Z',
44
+ source: 'used_tools_text',
45
+ result: '[{"args":{"tool":"agenda_pre_reservar_horario","reservation_id":"RES-ELEFAI-LAB-001","selected_from_message":"dia 13","status":"pre_reserved","next_step":"confirmar_agendamento"}}]',
46
+ },
47
+ ];
48
+
49
+ const vectorMemories = [
50
+ {
51
+ memory: '[Used tools: Tool: agenda_pre_reservar_horario, Input: , Result: [{"args":{"tool":"agenda_pre_reservar_horario","reservation_id":"RES-ELEFAI-LAB-001","selected_from_message":"dia 13","status":"pre_reserved","next_step":"confirmar_agendamento"}}]] Calling agenda_pre_reservar_horario with input: {"id":"call_bfHlltcjI1vCagRkav0fj8Ha"}\nPré-reserva feita para o dia 13.\nSe quiser, posso confirmar agora.',
52
+ created_at: '2026-05-14T05:34:35.373020Z',
53
+ elefaiScore: { semanticScore: 0.71, recencyScore: 0.99, hybridScore: 0.81, source: 'semantic' },
54
+ },
55
+ {
56
+ memory: '[Used tools: Tool: agenda_consultar_disponibilidade, Input: , Result: [{"args":{"tool":"agenda_consultar_disponibilidade","available_slots":"2026-05-12T09:00,2026-05-12T14:00,2026-05-13T10:30","timezone":"America/Sao_Paulo","next_step":"escolher_um_horario"}}]] Temos estes horários vagos:\n- 12/05 às 09:00\n- 12/05 às 14:00\n- 13/05 às 10:30',
57
+ created_at: '2026-05-14T05:34:14.717988Z',
58
+ elefaiScore: { semanticScore: 0.7, recencyScore: 0.99, hybridScore: 0.8, source: 'semantic' },
59
+ },
60
+ { memory: 'Oi! Como posso ajudar?', created_at: '2026-05-14T05:33:47.194630Z' },
61
+ { memory: 'oi', created_at: '2026-05-14T05:33:47.194355Z' },
62
+ ];
63
+
64
+ const operationalState = core.deriveOperationalState(toolHistory, {}, [], true);
65
+ const workingMemory = {
66
+ current_goal: 'call agenda_confirmar_agendamento now; do not ask the same confirmation question again',
67
+ current_task: 'call agenda_confirmar_agendamento now; do not ask the same confirmation question again',
68
+ last_user_intent: 'affirm',
69
+ last_user_message: 'ok',
70
+ next_expected_action: 'call agenda_confirmar_agendamento now; do not ask the same confirmation question again',
71
+ };
72
+ const decisionState = core.deriveDecisionState({ query: 'ok', toolHistory, operationalState, workingMemory });
73
+ const memoryCompression = core.deriveMemoryCompression({ toolHistory, operationalState, vectorMemories });
74
+ const common = {
75
+ payloadFormat: 'structured',
76
+ query: 'ok',
77
+ userId: 'd48292bd4c9f4eccb9120092e3acd073',
78
+ profileFacts: {},
79
+ workingMemory,
80
+ decisionState,
81
+ memoryCompression,
82
+ operationalState,
83
+ actionLedger: core.deriveActionLedger(toolHistory, 20, true),
84
+ entityTimeline: core.deriveEntityTimeline({}, [], [], toolHistory),
85
+ vectorMemories,
86
+ recentMessages: [],
87
+ toolHistory,
88
+ highlights: [
89
+ 'tool agenda_consultar_disponibilidade: [{"args":{"available_slots":"2026-05-12T09:00,2026-05-12T14:00,2026-05-13T10:30"}}]',
90
+ 'tool agenda_pre_reservar_horario: [{"args":{"reservation_id":"RES-ELEFAI-LAB-001","status":"pre_reserved"}}]',
91
+ ],
92
+ graph: [],
93
+ diagnostics: {},
94
+ };
95
+
96
+ const modes = [
97
+ ['productionBalanced', core.applyOperationalPreset({ operationPreset: 'productionBalanced' })],
98
+ ['productionNano', core.applyOperationalPreset({ operationPreset: 'productionNano' })],
99
+ ];
100
+
101
+ for (const [name, adv] of modes) {
102
+ const messages = core.buildContextMessages({ ...common, adv });
103
+ const text = messages.map((message) => message.content).join('\n\n');
104
+ console.log(JSON.stringify({
105
+ mode: name,
106
+ size: core.contextSizeOfMessages(messages),
107
+ has_next_expected_action: /next_expected_action/.test(text),
108
+ has_do_not_repeat_tools: /do_not_repeat_tools/.test(text),
109
+ has_reservation_id: /RES-ELEFAI-LAB-001/.test(text),
110
+ has_available_slots: /available_slots/.test(text),
111
+ has_duplicate_calling_text: /Calling agenda_pre_reservar_horario/.test(text),
112
+ }, null, 2));
113
+ }