n8n-nodes-tembory 1.0.11 → 1.0.12

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/README.md CHANGED
@@ -2,12 +2,14 @@
2
2
 
3
3
  Node de memoria operacional da Tembory para agentes de IA no n8n.
4
4
 
5
- Versao atual: `1.0.11`.
5
+ Versao atual: `1.0.12`.
6
6
 
7
7
  O Tembory entrega contexto rico para o AI Agent sem depender apenas do historico textual da conversa. Ele combina memoria semantica, working memory, decision state, fatos estaveis do lead, historico de tools, estado operacional, action ledger, timeline de entidades, compressao de memoria, grafo, mensagens recentes e diagnosticos.
8
8
 
9
9
  Nos presets de producao, o contexto e organizado como um pacote acionavel: secoes vazias sao removidas, evidencias de tools ficam canonicas em `tool_history`/`operational_state`, e blocos como `decision_state`, `working_memory` e `memory_compression` sao compactados sem perder IDs, proximas acoes, slots, reservas, confirmacoes e bloqueios de repeticao.
10
10
 
11
+ Quando um modelo barato esta conectado ao Tembory, os presets de producao usam esse SLM para gerar um resumo ativo do contexto. Em modo `auto`, ele organiza tanto o estado atual (`working_memory`, `tool_history`, `operational_state`, `decision_state`) quanto memorias vetoriais recuperadas. Assim o primeiro turno ja pode receber highlights uteis mesmo antes de haver resultados do banco vetorial.
12
+
11
13
  ## Smoke tecnico
12
14
 
13
15
  Antes de publicar uma versao, rode:
@@ -882,6 +882,8 @@ const applyOperationalPreset = (advanced = {}) => {
882
882
  const preset = String(advanced.operationPreset || 'custom');
883
883
  const presets = {
884
884
  diagnostic: {
885
+ summarySource: 'auto',
886
+ includeConnectedModelSummary: true,
885
887
  includeContextHeader: true,
886
888
  includeSummary: true,
887
889
  includeScores: true,
@@ -906,6 +908,8 @@ const applyOperationalPreset = (advanced = {}) => {
906
908
  recentMessagesLastN: 8,
907
909
  },
908
910
  productionBalanced: {
911
+ summarySource: 'auto',
912
+ includeConnectedModelSummary: true,
909
913
  compactStateSections: true,
910
914
  includeContextHeader: true,
911
915
  includeSummary: true,
@@ -931,8 +935,12 @@ const applyOperationalPreset = (advanced = {}) => {
931
935
  recentMessagesLastN: 6,
932
936
  vectorMemoryMaxChars: 360,
933
937
  contextMaxChars: 10000,
938
+ connectedModelSummaryMaxChars: 900,
939
+ connectedModelSummaryInputMaxChars: 3200,
934
940
  },
935
941
  productionCheap: {
942
+ summarySource: 'activeContext',
943
+ includeConnectedModelSummary: true,
936
944
  compactStateSections: true,
937
945
  includeContextHeader: true,
938
946
  includeSummary: false,
@@ -958,8 +966,12 @@ const applyOperationalPreset = (advanced = {}) => {
958
966
  recentMessagesLastN: 4,
959
967
  vectorMemoryMaxChars: 260,
960
968
  contextMaxChars: 7000,
969
+ connectedModelSummaryMaxChars: 700,
970
+ connectedModelSummaryInputMaxChars: 2400,
961
971
  },
962
972
  productionNano: {
973
+ summarySource: 'auto',
974
+ includeConnectedModelSummary: true,
963
975
  compactForAgent: true,
964
976
  includeContextHeader: true,
965
977
  includeSummary: true,
@@ -986,8 +998,12 @@ const applyOperationalPreset = (advanced = {}) => {
986
998
  recentMessagesLastN: 2,
987
999
  vectorMemoryMaxChars: 220,
988
1000
  contextMaxChars: 6000,
1001
+ connectedModelSummaryMaxChars: 700,
1002
+ connectedModelSummaryInputMaxChars: 2200,
989
1003
  },
990
1004
  audit: {
1005
+ summarySource: 'auto',
1006
+ includeConnectedModelSummary: true,
991
1007
  includeContextHeader: true,
992
1008
  includeSummary: true,
993
1009
  includeScores: true,
@@ -1524,6 +1540,87 @@ const compactVectorMemoriesForAgent = (vectorMemories = [], toolHistory = [], ma
1524
1540
  .filter((text) => !hasStructuredTools || !/^\[tool_events_extracted\]/i.test(text))
1525
1541
  .slice(0, maxItems);
1526
1542
  };
1543
+ const modelResponseText = (response) => {
1544
+ const content = response === null || response === void 0 ? void 0 : response.content;
1545
+ if (typeof content === 'string')
1546
+ return content;
1547
+ if (Array.isArray(content)) {
1548
+ return content.map((item) => {
1549
+ if (typeof item === 'string')
1550
+ return item;
1551
+ if (item && typeof item === 'object' && typeof item.text === 'string')
1552
+ return item.text;
1553
+ if (item && typeof item === 'object' && typeof item.content === 'string')
1554
+ return item.content;
1555
+ return '';
1556
+ }).filter(Boolean).join('\n');
1557
+ }
1558
+ if (typeof (response === null || response === void 0 ? void 0 : response.text) === 'string')
1559
+ return response.text;
1560
+ return response === undefined || response === null ? '' : String(response);
1561
+ };
1562
+ const cleanModelSummaryText = (value, max = 900) => {
1563
+ let text = modelResponseText(value).trim();
1564
+ if (!text)
1565
+ return '';
1566
+ try {
1567
+ const parsed = JSON.parse(text);
1568
+ text = modelResponseText(parsed) || (Array.isArray(parsed) ? parsed.map(modelResponseText).filter(Boolean).join('\n') : '');
1569
+ if (!text && parsed && typeof parsed === 'object') {
1570
+ const bullets = parsed.highlights || parsed.current_summary || parsed.summary || parsed.facts;
1571
+ if (Array.isArray(bullets))
1572
+ text = bullets.map((item) => `- ${typeof item === 'string' ? item : safeStringify(item)}`).join('\n');
1573
+ }
1574
+ }
1575
+ catch { }
1576
+ text = text
1577
+ .replace(/^\s*\[\s*\{\s*"type"\s*:\s*"text"\s*,\s*"text"\s*:\s*"/, '')
1578
+ .replace(/"\s*,\s*"annotations"\s*:\s*\[\s*\]\s*\}\s*\]\s*$/, '')
1579
+ .replace(/\\n/g, '\n')
1580
+ .trim();
1581
+ return truncate(text, max);
1582
+ };
1583
+ const buildConnectedModelSummaryInput = ({ query, profileFacts, workingMemory, decisionState, memoryCompression, operationalState, toolHistory, recentMessages, vectorMemories, highlights, adv }) => {
1584
+ const source = String(adv.summarySource || 'auto');
1585
+ if (source === 'off' || source === 'disabled' || adv.includeConnectedModelSummary === false)
1586
+ return '';
1587
+ const includeVectors = source === 'auto' || source === 'vectorOnly' || source === 'vectors';
1588
+ const includeActive = source === 'auto' || source === 'activeContext' || source === 'active';
1589
+ const payload = cleanContextValue({
1590
+ query: String(query || ''),
1591
+ active_context: includeActive ? {
1592
+ profile_facts: renderProfileFacts(profileFacts),
1593
+ working_memory: compactWorkingMemoryForAgent(workingMemory || {}),
1594
+ decision_state: compactDecisionStateForAgent(decisionState || {}),
1595
+ operational_state: compactOperationalStateForAgent(operationalState || {}),
1596
+ memory_compression: compactMemoryCompressionForAgent(memoryCompression || {}),
1597
+ tool_history: compactToolHistoryForAgent(toolHistory || [], adv.summaryToolHistoryLastN || adv.toolHistoryLastN || 6, adv.includeToolResults !== false),
1598
+ recent_messages: pruneByLimit(recentMessages || [], adv.summaryRecentMessagesLastN || 4).map((message) => cleanContextValue({
1599
+ role: message.role,
1600
+ content: truncate(message.content, 240),
1601
+ at: message.at,
1602
+ })),
1603
+ highlights: (highlights || []).slice(0, adv.summaryHighlightsMaxItems || 4),
1604
+ } : undefined,
1605
+ vector_memories: includeVectors ? compactVectorMemoriesForAgent(vectorMemories || [], toolHistory || [], adv.summaryMaxFacts || 4) : undefined,
1606
+ });
1607
+ const hasActiveSignal = includeActive && Boolean((payload.active_context && Object.keys(payload.active_context).length) || payload.query);
1608
+ const hasVectorSignal = includeVectors && Array.isArray(payload.vector_memories) && payload.vector_memories.length > 0;
1609
+ if (!hasActiveSignal && !hasVectorSignal)
1610
+ return '';
1611
+ return truncate(safeStringify(payload), Number(adv.connectedModelSummaryInputMaxChars || 3000));
1612
+ };
1613
+ const invokeConnectedModelSummary = async (connectedLanguageModel, summaryInput, adv = {}) => {
1614
+ if (!connectedLanguageModel || typeof connectedLanguageModel.invoke !== 'function' || !summaryInput)
1615
+ return '';
1616
+ const response = await connectedLanguageModel.invoke([
1617
+ toBaseMessage({
1618
+ role: 'user',
1619
+ content: `Organize the Tembory memory context for the next agent turn. Return concise Portuguese bullets only. Preserve IDs, dates, tool names, next action, do-not-repeat instructions, and contradictions. Do not invent facts.\n\nContext:\n${summaryInput}`,
1620
+ }),
1621
+ ]);
1622
+ return cleanModelSummaryText(response, Number(adv.connectedModelSummaryMaxChars || 900));
1623
+ };
1527
1624
  const contextSizeOfMessages = (messages = []) => {
1528
1625
  const perMessage = (messages || []).map((message, index) => {
1529
1626
  const content = String(message.content || '');
@@ -1601,7 +1698,7 @@ const wrapTemboryMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1601
1698
  return target[prop];
1602
1699
  },
1603
1700
  });
1604
- const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, workingMemory, decisionState, memoryCompression, operationalState, actionLedger, entityTimeline, vectorMemories, recentMessages, toolHistory, highlights, graph, diagnostics, adv }) => {
1701
+ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, workingMemory, decisionState, memoryCompression, operationalState, actionLedger, entityTimeline, vectorMemories, recentMessages, toolHistory, highlights, graph, diagnostics, connectedModelSummary, adv }) => {
1605
1702
  const includeHeader = adv.includeContextHeader !== false;
1606
1703
  const includeSummary = adv.includeSummary !== false;
1607
1704
  const includeScores = adv.includeScores !== false;
@@ -1622,6 +1719,9 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1622
1719
  const summary = compactVectorMemoriesForAgent(vectorMemories, toolHistory, Number(adv.summaryMaxFacts || 3));
1623
1720
  sections.push({ section: 'summary', title: 'Summary', value: summary.length ? summary : null, why_null: summary.length ? undefined : 'no non-tool vector memories to summarize' });
1624
1721
  }
1722
+ if (connectedModelSummary && adv.includeConnectedModelSummary !== false) {
1723
+ sections.push({ section: 'connected_model_summary', title: 'SLM summary', value: connectedModelSummary });
1724
+ }
1625
1725
  sections.push({
1626
1726
  section: 'working_memory',
1627
1727
  title: 'Working memory',
@@ -1687,6 +1787,9 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1687
1787
  : vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 360)).filter(Boolean);
1688
1788
  sections.push({ section: 'summary', title: 'Summary', value: summary.length ? summary : null, why_null: summary.length ? undefined : 'no vector memories to summarize' });
1689
1789
  }
1790
+ if (connectedModelSummary && adv.includeConnectedModelSummary !== false) {
1791
+ sections.push({ section: 'connected_model_summary', title: 'SLM summary', value: connectedModelSummary });
1792
+ }
1690
1793
  sections.push({
1691
1794
  section: 'working_memory',
1692
1795
  title: 'Working memory',
@@ -1995,6 +2098,22 @@ class Mem0Memory {
1995
2098
  { displayName: 'Organizar Seções de Produção', name: 'compactStateSections', type: 'boolean', default: false, description: 'Renderiza blocos de produção com campos canônicos, remove seções vazias e evita duplicar evidências já presentes em tool_history/operational_state.' },
1996
2099
  { displayName: 'Máximo de Caracteres do Contexto', name: 'contextMaxChars', type: 'number', default: 6000, description: 'Limite final aplicado ao contexto entregue ao agente em modos estruturados de produção.' },
1997
2100
  { displayName: 'Incluir Resumo', name: 'includeSummary', type: 'boolean', default: true },
2101
+ { displayName: 'Incluir Resumo do SLM', name: 'includeConnectedModelSummary', type: 'boolean', default: true, description: 'Usa o modelo barato conectado ao Tembory para organizar contexto ativo e/ou memórias vetoriais em bullets curtos.' },
2102
+ {
2103
+ displayName: 'Fonte do Resumo do SLM',
2104
+ name: 'summarySource',
2105
+ type: 'options',
2106
+ options: [
2107
+ { name: 'Automático', value: 'auto' },
2108
+ { name: 'Contexto Ativo', value: 'activeContext' },
2109
+ { name: 'Somente Vetores', value: 'vectorOnly' },
2110
+ { name: 'Desligado', value: 'off' },
2111
+ ],
2112
+ default: 'auto',
2113
+ description: 'Define se o SLM resume working memory/tool state, memórias vetoriais, ambos ou nada.',
2114
+ },
2115
+ { displayName: 'Máximo de Caracteres de Entrada do SLM', name: 'connectedModelSummaryInputMaxChars', type: 'number', default: 3000 },
2116
+ { displayName: 'Máximo de Caracteres do Resumo do SLM', name: 'connectedModelSummaryMaxChars', type: 'number', default: 900 },
1998
2117
  { displayName: 'Máximo de Fatos no Resumo', name: 'summaryMaxFacts', type: 'number', default: 4 },
1999
2118
  { displayName: 'Incluir Scores', name: 'includeScores', type: 'boolean', default: true },
2000
2119
  { displayName: 'Incluir Diagnóstico', name: 'includeDiagnostics', type: 'boolean', default: false },
@@ -2623,34 +2742,6 @@ class Mem0Memory {
2623
2742
  }
2624
2743
  catch { }
2625
2744
  }
2626
- let connectedModelSummary = '';
2627
- if (connectedLanguageModel && typeof connectedLanguageModel.invoke === 'function' && vectorMemories.length && adv.includeSummary !== false) {
2628
- try {
2629
- const facts = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 500)).filter(Boolean).join('\n') || '(no vector memories found)';
2630
- if (facts) {
2631
- const response = await connectedLanguageModel.invoke([
2632
- toBaseMessage({
2633
- role: 'user',
2634
- content: `Summarize available Tembory context as concise factual bullets. If there are no memories, return exactly "(empty)".\n\nQuery: ${String(query || '')}\n\nMemories:\n${truncate(facts, 3000)}`,
2635
- }),
2636
- ]);
2637
- connectedModelSummary = truncate(response?.content || response?.text || String(response || ''), 1200);
2638
- connectedAi.languageModelSummary = true;
2639
- }
2640
- }
2641
- catch (error) {
2642
- connectedAi.errors.push(`languageModel.invoke: ${error.message || String(error)}`);
2643
- }
2644
- }
2645
- const diagnostics = {
2646
- vectorMemories: vectorMemories.length,
2647
- recentMessages: recentMessages.length,
2648
- toolHistory: toolHistory.length,
2649
- graph: graph.length,
2650
- project: project || null,
2651
- memoryNamespace: key,
2652
- connectedAi,
2653
- };
2654
2745
  const includeToolResults = adv.includeToolResults !== false;
2655
2746
  const operationalState = deriveOperationalState(toolHistory, renderProfileFacts(profileFacts), recentMessages, includeToolResults);
2656
2747
  const actionLedger = deriveActionLedger(toolHistory, adv.actionLedgerMaxItems || adv.toolHistoryLastN || 20, includeToolResults);
@@ -2677,6 +2768,38 @@ class Mem0Memory {
2677
2768
  vectorMemories,
2678
2769
  maxItems: adv.compressionMaxItems || 6,
2679
2770
  });
2771
+ let connectedModelSummary = '';
2772
+ if (connectedLanguageModel && typeof connectedLanguageModel.invoke === 'function' && adv.includeSummary !== false && adv.includeConnectedModelSummary !== false) {
2773
+ try {
2774
+ const summaryInput = buildConnectedModelSummaryInput({
2775
+ query,
2776
+ profileFacts,
2777
+ workingMemory,
2778
+ decisionState,
2779
+ memoryCompression,
2780
+ operationalState,
2781
+ toolHistory,
2782
+ recentMessages,
2783
+ vectorMemories,
2784
+ highlights,
2785
+ adv,
2786
+ });
2787
+ connectedModelSummary = await invokeConnectedModelSummary(connectedLanguageModel, summaryInput, adv);
2788
+ connectedAi.languageModelSummary = Boolean(connectedModelSummary);
2789
+ }
2790
+ catch (error) {
2791
+ connectedAi.errors.push(`languageModel.invoke: ${error.message || String(error)}`);
2792
+ }
2793
+ }
2794
+ const diagnostics = {
2795
+ vectorMemories: vectorMemories.length,
2796
+ recentMessages: recentMessages.length,
2797
+ toolHistory: toolHistory.length,
2798
+ graph: graph.length,
2799
+ project: project || null,
2800
+ memoryNamespace: key,
2801
+ connectedAi,
2802
+ };
2680
2803
  const contextHealth = deriveContextHealth({
2681
2804
  userId: key,
2682
2805
  project,
@@ -2714,6 +2837,7 @@ class Mem0Memory {
2714
2837
  highlights,
2715
2838
  graph,
2716
2839
  diagnostics,
2840
+ connectedModelSummary,
2717
2841
  adv,
2718
2842
  });
2719
2843
  diagnostics.contextSize = contextSizeOfMessages(payload);
@@ -2949,4 +3073,7 @@ exports.__private = {
2949
3073
  compactToolResult,
2950
3074
  compactToolHistoryForAgent,
2951
3075
  compactOperationalStateForAgent,
3076
+ buildConnectedModelSummaryInput,
3077
+ cleanModelSummaryText,
3078
+ invokeConnectedModelSummary,
2952
3079
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
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",