n8n-nodes-tembory 1.0.4 → 1.0.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.
@@ -630,17 +630,21 @@ const toolHistoryFromMemory = (item) => {
630
630
  source: meta.source || 'metadata',
631
631
  };
632
632
  };
633
- const toolHistoryItemsFromMemory = (item) => {
633
+ const explicitToolHistoryItemsFromMemory = (item) => {
634
634
  const meta = metadataOf(item);
635
635
  const content = meta.content || item.memory || item.text || item.value || item.content || item.data;
636
636
  const marked = parseToolHistoryMarker(content);
637
637
  if (marked)
638
638
  return [marked];
639
- if (meta.kind === 'recent_message') {
640
- const textCalls = extractToolCallsFromText(content, meta.at || item.created_at || item.createdAt || nowIso());
641
- if (textCalls.length)
642
- return textCalls;
643
- }
639
+ const textCalls = extractToolCallsFromText(content, meta.at || item.created_at || item.createdAt || nowIso());
640
+ return textCalls.length ? textCalls : [];
641
+ };
642
+ const toolHistoryItemsFromMemory = (item) => {
643
+ const meta = metadataOf(item);
644
+ const content = meta.content || item.memory || item.text || item.value || item.content || item.data;
645
+ const explicit = explicitToolHistoryItemsFromMemory(item);
646
+ if (explicit.length)
647
+ return explicit;
644
648
  const single = toolHistoryFromMemory(item);
645
649
  return single ? [single] : [];
646
650
  };
@@ -1005,11 +1009,18 @@ const getRecentHighlights = (recentMessages, tools, maxItems = 6) => {
1005
1009
  }
1006
1010
  return pruneByLimit(Array.from(new Set(lines)), maxItems);
1007
1011
  };
1012
+ const normalizeIntentText = (value = '') => String(value || '')
1013
+ .toLowerCase()
1014
+ .normalize('NFD')
1015
+ .replace(/[\u0300-\u036f]/g, '');
1016
+ const hasConfirmIntent = (value = '') => /\b(confirm\w*|confim\w*|cnfirm\w*|cofnirm\w*|ocnfi\w*|ocnfia\w*|fechar)\b/.test(normalizeIntentText(value));
1008
1017
  const inferToolGuard = ({ query, recentMessages, toolHistory, vectorMemories }) => {
1009
- const text = String(query || '').toLowerCase();
1018
+ const text = normalizeIntentText(query);
1010
1019
  const names = new Set((toolHistory || []).map((tool) => String(tool.name || '')));
1011
1020
  const hasRecentContext = (recentMessages || []).some((msg) => /reserva|pré-reserva|pre-reserva|agend|confirm|hor[aá]rio|dispon/i.test(String(msg.content || '')));
1012
1021
  const hasVectorContext = (vectorMemories || []).some((memory) => /reserva|pré-reserva|pre-reserva|agend|confirm|hor[aá]rio|dispon/i.test(memoryText(memory)));
1022
+ const hasAvailabilityTool = names.has('agenda_consultar_disponibilidade');
1023
+ const hasPreReservationTool = names.has('agenda_pre_reservar_horario');
1013
1024
  const hasAnyContext = names.size > 0 || hasRecentContext || hasVectorContext;
1014
1025
  const blocked = [];
1015
1026
  const reasons = [];
@@ -1017,32 +1028,34 @@ const inferToolGuard = ({ query, recentMessages, toolHistory, vectorMemories })
1017
1028
  blocked.push('agenda_pre_reservar_horario', 'agenda_confirmar_agendamento');
1018
1029
  reasons.push('no prior availability or pre-reservation context was found for this session');
1019
1030
  }
1020
- if ((/confirm|confirma|confirmar/.test(text)) && !names.has('agenda_pre_reservar_horario') && !hasRecentContext) {
1031
+ if (hasConfirmIntent(text) && !hasPreReservationTool) {
1021
1032
  blocked.push('agenda_confirmar_agendamento');
1022
- reasons.push('confirmation requested but no prior pre-reservation was found in tool_history or recent_messages');
1033
+ reasons.push('confirmation requested but no structured agenda_pre_reservar_horario tool result was found; assistant text or vector memories are not enough to confirm');
1023
1034
  }
1024
- if ((/pre.?reserv|pré.?reserv|reservar/.test(text)) && !names.has('agenda_consultar_disponibilidade') && !hasRecentContext) {
1035
+ if ((/pre.?reserv|pre.?reserv|reservar/.test(text)) && !hasAvailabilityTool) {
1025
1036
  blocked.push('agenda_pre_reservar_horario');
1026
- reasons.push('reservation requested but no prior availability result was found in tool_history or recent_messages');
1037
+ reasons.push('reservation requested but no structured agenda_consultar_disponibilidade tool result was found');
1027
1038
  }
1028
1039
  return { blockedTools: Array.from(new Set(blocked)), reasons };
1029
1040
  };
1030
1041
  const inferUserIntent = (query = '', recentMessages = []) => {
1031
1042
  const latestUser = [...(recentMessages || [])].reverse().find((msg) => /^(user|human)$/i.test(String(msg.role || '')));
1032
- const text = `${query || ''}\n${latestUser?.content || ''}`.toLowerCase();
1033
- if (/^(ok|sim|pode|pode sim|isso|isso mesmo|confirmo|confirmar)$/i.test(String(query || '').trim()))
1043
+ const rawText = `${query || ''}\n${latestUser?.content || ''}`;
1044
+ const text = normalizeIntentText(rawText);
1045
+ const normalizedQuery = normalizeIntentText(query).trim();
1046
+ if (/^(ok|sim|pode|pode sim|isso|isso mesmo|confirmo|confirmar)$/.test(normalizedQuery))
1034
1047
  return 'affirm';
1035
- if (/\b(confirm|confirma|confirmar|fechar|pode confirmar)\b/.test(text))
1048
+ if (hasConfirmIntent(text))
1036
1049
  return 'confirm';
1037
1050
  if (containsSelectedSlot(text))
1038
1051
  return 'select_slot';
1039
- if (/\b(pre.?reserv|pré.?reserv|reservar|segurar|marcar)\b/.test(text))
1052
+ if (/\b(pre.?reserv|pre.?reserv|reservar|segurar|marcar)\b/.test(text))
1040
1053
  return 'pre_reserve';
1041
- if (/\b(disponibilidade|hor[aá]rios?|agenda(?:r|mento)?|quando pode|tem vaga|quero agendar|gostaria de agendar|preciso agendar)\b/.test(text))
1054
+ if (/\b(disponibilidade|horarios?|agenda(?:r|mento)?|quando pode|tem vaga|quero agendar|gostaria de agendar|preciso agendar)\b/.test(text))
1042
1055
  return 'check_availability';
1043
1056
  if (/\b(cancel|desmarcar|remarcar|alterar|mudar)\b/.test(text))
1044
1057
  return 'change_or_cancel';
1045
- if (/\b(meu nome|email|telefone|empresa|prefiro|prefer[eê]ncia)\b/.test(text))
1058
+ if (/\b(meu nome|email|telefone|empresa|prefiro|preferencia)\b/.test(text))
1046
1059
  return 'profile_update';
1047
1060
  if (text.trim())
1048
1061
  return 'general_message';
@@ -1060,7 +1073,7 @@ const deriveNextExpectedAction = (intent, operationalState = {}) => {
1060
1073
  if (intent === 'confirm') {
1061
1074
  if (agenda.has_pending_pre_reservation)
1062
1075
  return 'call agenda_confirmar_agendamento now using the latest pre-reservation context; do not ask again';
1063
- return 'ask for or create a pre-reservation before confirming';
1076
+ return 'do not call agenda_confirmar_agendamento; call agenda_pre_reservar_horario first if a selected slot and availability are known, otherwise call agenda_consultar_disponibilidade';
1064
1077
  }
1065
1078
  if (intent === 'select_slot') {
1066
1079
  if (agenda.has_availability)
@@ -1274,6 +1287,17 @@ const deriveContextHealth = ({ userId = '', project = '', vectorMemories = [], r
1274
1287
  generated_at: nowIso(),
1275
1288
  };
1276
1289
  };
1290
+ const contextMemoryText = (memory, max = 700) => {
1291
+ const text = memoryText(memory);
1292
+ const tools = explicitToolHistoryItemsFromMemory(memory);
1293
+ if (tools.length) {
1294
+ const names = tools.map((tool) => `${tool.name || 'tool'}:${tool.ok === false ? 'failed' : 'ok'}`).join(', ');
1295
+ const ids = tools.map((tool) => tool.result || tool.input || '').join(' ');
1296
+ const idMatch = /(reservation_id|confirmation_id|id)["':\s]+([A-Za-z0-9_.:-]+)/i.exec(ids);
1297
+ return truncate(`[tool_events_extracted] ${names}${idMatch ? ` ${idMatch[1]}=${idMatch[2]}` : ''}. See Tool history for structured details.`, max);
1298
+ }
1299
+ return truncate(text, max);
1300
+ };
1277
1301
  const wrapElefaiMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1278
1302
  get(target, prop) {
1279
1303
  if (prop === 'loadMemoryVariables') {
@@ -1352,7 +1376,7 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1352
1376
  });
1353
1377
  }
1354
1378
  if (includeSummary) {
1355
- const summary = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map(memoryText).filter(Boolean);
1379
+ const summary = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 360)).filter(Boolean);
1356
1380
  sections.push({ section: 'summary', title: 'Summary', value: summary.length ? summary : null, why_null: summary.length ? undefined : 'no vector memories to summarize' });
1357
1381
  }
1358
1382
  sections.push({
@@ -1410,7 +1434,7 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1410
1434
  const scoreMeta = scoreMetaOf(m);
1411
1435
  const rawScore = scoreOf(m);
1412
1436
  return {
1413
- memory: memoryText(m),
1437
+ memory: contextMemoryText(m, Number(adv.vectorMemoryMaxChars || 700)),
1414
1438
  score: includeScores ? (rawScore !== undefined ? rawScore : scoreMeta.hybridScore ?? scoreMeta.recencyScore ?? 'unavailable') : undefined,
1415
1439
  semantic_score: includeScores ? scoreMeta.semanticScore : undefined,
1416
1440
  recency_score: includeScores ? scoreMeta.recencyScore : undefined,
@@ -2222,8 +2246,9 @@ class Mem0Memory {
2222
2246
  for (const msg of recentMessages)
2223
2247
  toolHistoryFromRecentMessages.push(...extractToolCallsFromText(msg.content, msg.at));
2224
2248
  }
2249
+ const toolHistoryFromVectorMarkers = adv.includeToolHistory === false ? [] : vectorMemories.flatMap(explicitToolHistoryItemsFromMemory);
2225
2250
  const toolHistoryFromVectorMemories = adv.includeToolHistory === false || adv.includeToolHistorySemanticFallback !== true ? [] : vectorMemories.flatMap(toolHistoryItemsFromMemory);
2226
- const toolHistory = adv.includeToolHistory === false ? [] : applyToolHistoryWindow((store.toolHistory[key] || []).concat(persistedToolHistory, toolHistoryFromRecentMessages, toolHistoryFromVectorMemories), adv.toolHistoryTTLSeconds, adv.toolHistoryLastN || 15);
2251
+ const toolHistory = adv.includeToolHistory === false ? [] : applyToolHistoryWindow((store.toolHistory[key] || []).concat(persistedToolHistory, toolHistoryFromRecentMessages, toolHistoryFromVectorMarkers, toolHistoryFromVectorMemories), adv.toolHistoryTTLSeconds, adv.toolHistoryLastN || 15);
2227
2252
  const highlights = adv.includeRecentHighlights === false ? [] : getRecentHighlights(recentMessages, toolHistory, adv.recentHighlightsMaxItems || 6);
2228
2253
  const profileFacts = adv.includeProfileFacts === false ? {} : mergeProfileFacts(store.profileFacts[key], profileFactsFromMessages(allRecentMessages), profileFactsFromMemories(vectorMemories));
2229
2254
  if (adv.includeProfileFacts !== false && Object.keys(profileFacts).length) {
@@ -2237,7 +2262,7 @@ class Mem0Memory {
2237
2262
  let connectedModelSummary = '';
2238
2263
  if (connectedLanguageModel && typeof connectedLanguageModel.invoke === 'function' && (vectorMemories.length || String(query || '').trim()) && adv.includeSummary !== false) {
2239
2264
  try {
2240
- const facts = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map(memoryText).filter(Boolean).join('\n') || '(no vector memories found)';
2265
+ const facts = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 500)).filter(Boolean).join('\n') || '(no vector memories found)';
2241
2266
  if (facts || String(query || '').trim()) {
2242
2267
  const response = await connectedLanguageModel.invoke([
2243
2268
  toBaseMessage({
@@ -2521,6 +2546,7 @@ exports.__private = {
2521
2546
  extractToolCallsFromText,
2522
2547
  extractToolCalls,
2523
2548
  toolHistoryItemsFromMemory,
2549
+ explicitToolHistoryItemsFromMemory,
2524
2550
  toolHistoryFromMemory,
2525
2551
  recentMessageFromMemory,
2526
2552
  dedupeToolHistory,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
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",