n8n-nodes-tembory 1.0.13 → 1.0.14

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,7 +2,15 @@
2
2
 
3
3
  Node de memoria operacional da Tembory para agentes de IA no n8n.
4
4
 
5
- Versao atual: `1.0.13`.
5
+ Versao atual: `1.0.14`.
6
+
7
+ ## 1.0.14
8
+
9
+ - Adiciona Active Summary persistente por namespace/thread para continuar conversas depois de minutos, horas ou dias.
10
+ - Separa cache tecnico curto do resumo SLM da memoria ativa persistente.
11
+ - Aumenta os defaults do resumo SLM nos presets de producao para preservar mais inteligencia antes de comprimir para o agente.
12
+ - Adiciona diagnosticos para saber se o contexto veio de SLM novo, active summary ou cache tecnico.
13
+ - Expõe controles avancados de Active Summary e cache tecnico no node.
6
14
 
7
15
  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
16
 
@@ -862,14 +862,16 @@ const getMemoryStore = (ctx) => {
862
862
  data.tembory.workingMemory = data.tembory.workingMemory || {};
863
863
  data.tembory.decisionState = data.tembory.decisionState || {};
864
864
  data.tembory.memoryCompression = data.tembory.memoryCompression || {};
865
+ data.tembory.activeSummary = data.tembory.activeSummary || {};
865
866
  data.tembory.connectedModelSummaryCache = data.tembory.connectedModelSummaryCache || {};
866
867
  return data.tembory;
867
868
  }
868
869
  catch {
869
- global.__temboryMemory = global.__temboryMemory || { toolHistory: {}, recentMessages: {}, profileFacts: {}, workingMemory: {}, decisionState: {}, memoryCompression: {}, connectedModelSummaryCache: {} };
870
+ global.__temboryMemory = global.__temboryMemory || { toolHistory: {}, recentMessages: {}, profileFacts: {}, workingMemory: {}, decisionState: {}, memoryCompression: {}, activeSummary: {}, connectedModelSummaryCache: {} };
870
871
  global.__temboryMemory.workingMemory = global.__temboryMemory.workingMemory || {};
871
872
  global.__temboryMemory.decisionState = global.__temboryMemory.decisionState || {};
872
873
  global.__temboryMemory.memoryCompression = global.__temboryMemory.memoryCompression || {};
874
+ global.__temboryMemory.activeSummary = global.__temboryMemory.activeSummary || {};
873
875
  global.__temboryMemory.connectedModelSummaryCache = global.__temboryMemory.connectedModelSummaryCache || {};
874
876
  return global.__temboryMemory;
875
877
  }
@@ -912,6 +914,12 @@ const applyOperationalPreset = (advanced = {}) => {
912
914
  productionBalanced: {
913
915
  summarySource: 'auto',
914
916
  includeConnectedModelSummary: true,
917
+ includeActiveSummary: true,
918
+ persistActiveSummary: true,
919
+ activeSummaryMaxChars: 1800,
920
+ activeSummaryRetentionDays: 30,
921
+ enableTransientSummaryCache: true,
922
+ transientSummaryCacheTTLSeconds: 300,
915
923
  compactStateSections: true,
916
924
  includeContextHeader: true,
917
925
  includeSummary: true,
@@ -937,12 +945,18 @@ const applyOperationalPreset = (advanced = {}) => {
937
945
  recentMessagesLastN: 6,
938
946
  vectorMemoryMaxChars: 360,
939
947
  contextMaxChars: 10000,
940
- connectedModelSummaryMaxChars: 900,
941
- connectedModelSummaryInputMaxChars: 3200,
948
+ connectedModelSummaryMaxChars: 1200,
949
+ connectedModelSummaryInputMaxChars: 4200,
942
950
  },
943
951
  productionCheap: {
944
952
  summarySource: 'activeContext',
945
953
  includeConnectedModelSummary: true,
954
+ includeActiveSummary: true,
955
+ persistActiveSummary: true,
956
+ activeSummaryMaxChars: 1500,
957
+ activeSummaryRetentionDays: 30,
958
+ enableTransientSummaryCache: true,
959
+ transientSummaryCacheTTLSeconds: 300,
946
960
  compactStateSections: true,
947
961
  includeContextHeader: true,
948
962
  includeSummary: false,
@@ -968,12 +982,18 @@ const applyOperationalPreset = (advanced = {}) => {
968
982
  recentMessagesLastN: 4,
969
983
  vectorMemoryMaxChars: 260,
970
984
  contextMaxChars: 7000,
971
- connectedModelSummaryMaxChars: 700,
972
- connectedModelSummaryInputMaxChars: 2400,
985
+ connectedModelSummaryMaxChars: 1000,
986
+ connectedModelSummaryInputMaxChars: 3600,
973
987
  },
974
988
  productionNano: {
975
989
  summarySource: 'auto',
976
990
  includeConnectedModelSummary: true,
991
+ includeActiveSummary: true,
992
+ persistActiveSummary: true,
993
+ activeSummaryMaxChars: 1600,
994
+ activeSummaryRetentionDays: 30,
995
+ enableTransientSummaryCache: true,
996
+ transientSummaryCacheTTLSeconds: 300,
977
997
  compactForAgent: true,
978
998
  includeContextHeader: true,
979
999
  includeSummary: true,
@@ -999,9 +1019,9 @@ const applyOperationalPreset = (advanced = {}) => {
999
1019
  toolHistoryLastN: 6,
1000
1020
  recentMessagesLastN: 2,
1001
1021
  vectorMemoryMaxChars: 220,
1002
- contextMaxChars: 6000,
1003
- connectedModelSummaryMaxChars: 450,
1004
- connectedModelSummaryInputMaxChars: 1500,
1022
+ contextMaxChars: 7000,
1023
+ connectedModelSummaryMaxChars: 1200,
1024
+ connectedModelSummaryInputMaxChars: 4200,
1005
1025
  },
1006
1026
  audit: {
1007
1027
  summarySource: 'auto',
@@ -1582,7 +1602,34 @@ const cleanModelSummaryText = (value, max = 900) => {
1582
1602
  .trim();
1583
1603
  return truncate(text, max);
1584
1604
  };
1585
- const buildConnectedModelSummaryInput = ({ query, profileFacts, workingMemory, decisionState, memoryCompression, operationalState, toolHistory, recentMessages, vectorMemories, highlights, adv }) => {
1605
+ const activeSummaryIsFresh = (entry, adv = {}) => {
1606
+ if (!entry || !entry.summary)
1607
+ return false;
1608
+ const retentionDays = Number(adv.activeSummaryRetentionDays ?? 30);
1609
+ if (retentionDays <= 0)
1610
+ return true;
1611
+ return Date.now() - Number(entry.updatedAt || entry.at || 0) < retentionDays * 86400000;
1612
+ };
1613
+ const readActiveSummary = (store, key, adv = {}) => {
1614
+ if (adv.includeActiveSummary === false)
1615
+ return '';
1616
+ const entry = store.activeSummary?.[key];
1617
+ if (!activeSummaryIsFresh(entry, adv))
1618
+ return '';
1619
+ return truncate(String(entry.summary || ''), Number(adv.activeSummaryMaxChars || 1600));
1620
+ };
1621
+ const writeActiveSummary = (store, key, summary, adv = {}) => {
1622
+ if (!store || !key || adv.persistActiveSummary === false || !summary)
1623
+ return false;
1624
+ store.activeSummary = store.activeSummary || {};
1625
+ store.activeSummary[key] = {
1626
+ summary: truncate(String(summary), Number(adv.activeSummaryMaxChars || 1600)),
1627
+ updatedAt: Date.now(),
1628
+ source: 'slm',
1629
+ };
1630
+ return true;
1631
+ };
1632
+ const buildConnectedModelSummaryInput = ({ query, activeSummary, profileFacts, workingMemory, decisionState, memoryCompression, operationalState, toolHistory, recentMessages, vectorMemories, highlights, adv }) => {
1586
1633
  const source = String(adv.summarySource || 'auto');
1587
1634
  if (source === 'off' || source === 'disabled' || adv.includeConnectedModelSummary === false)
1588
1635
  return '';
@@ -1590,6 +1637,7 @@ const buildConnectedModelSummaryInput = ({ query, profileFacts, workingMemory, d
1590
1637
  const includeActive = source === 'auto' || source === 'activeContext' || source === 'active';
1591
1638
  const payload = cleanContextValue({
1592
1639
  query: String(query || ''),
1640
+ existing_active_summary: activeSummary ? truncate(activeSummary, Number(adv.activeSummaryMaxChars || 1600)) : undefined,
1593
1641
  active_context: includeActive ? {
1594
1642
  profile_facts: renderProfileFacts(profileFacts),
1595
1643
  working_memory: compactWorkingMemoryForAgent(workingMemory || {}),
@@ -1618,10 +1666,10 @@ const invokeConnectedModelSummary = async (connectedLanguageModel, summaryInput,
1618
1666
  const response = await connectedLanguageModel.invoke([
1619
1667
  toBaseMessage({
1620
1668
  role: 'user',
1621
- content: `Organize the Tembory memory context for the next agent turn. Return only 3 to 6 short Portuguese bullets. No JSON, no markdown table, no field-by-field dump. Preserve important IDs, dates, tool names, next action, do-not-repeat instructions, and contradictions. Do not invent facts.\n\nContext:\n${summaryInput}`,
1669
+ content: `Update the Tembory active summary for the next agent turn. Return only concise Portuguese bullets, no JSON and no markdown table. Preserve IDs, dates, tool names, confirmed decisions, pending actions, constraints, contradictions, and do-not-repeat instructions. Prefer durable useful context over raw logs. Do not invent facts.\n\nContext:\n${summaryInput}`,
1622
1670
  }),
1623
1671
  ]);
1624
- return cleanModelSummaryText(response, Number(adv.connectedModelSummaryMaxChars || 700));
1672
+ return cleanModelSummaryText(response, Number(adv.connectedModelSummaryMaxChars || 1200));
1625
1673
  };
1626
1674
  const contextSizeOfMessages = (messages = []) => {
1627
1675
  const perMessage = (messages || []).map((message, index) => {
@@ -1653,6 +1701,7 @@ const wrapTemboryMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1653
1701
  context: response.temboryContext,
1654
1702
  contextText: response.temboryContextText,
1655
1703
  summary: response.temborySummary,
1704
+ activeSummary: response.temboryActiveSummary,
1656
1705
  connectedModelSummary: response.temboryConnectedModelSummary,
1657
1706
  workingMemory: response.temboryWorkingMemory,
1658
1707
  decisionState: response.temboryDecisionState,
@@ -1700,7 +1749,7 @@ const wrapTemboryMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1700
1749
  return target[prop];
1701
1750
  },
1702
1751
  });
1703
- const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, workingMemory, decisionState, memoryCompression, operationalState, actionLedger, entityTimeline, vectorMemories, recentMessages, toolHistory, highlights, graph, diagnostics, connectedModelSummary, adv }) => {
1752
+ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, workingMemory, decisionState, memoryCompression, operationalState, actionLedger, entityTimeline, vectorMemories, recentMessages, toolHistory, highlights, graph, diagnostics, activeSummary, connectedModelSummary, adv }) => {
1704
1753
  const includeHeader = adv.includeContextHeader !== false;
1705
1754
  const includeSummary = adv.includeSummary !== false;
1706
1755
  const includeScores = adv.includeScores !== false;
@@ -1724,6 +1773,9 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1724
1773
  if (connectedModelSummary && adv.includeConnectedModelSummary !== false) {
1725
1774
  sections.push({ section: 'connected_model_summary', title: 'SLM summary', value: connectedModelSummary });
1726
1775
  }
1776
+ else if (activeSummary && adv.includeActiveSummary !== false) {
1777
+ sections.push({ section: 'active_summary', title: 'Active summary', value: activeSummary });
1778
+ }
1727
1779
  sections.push({
1728
1780
  section: 'working_memory',
1729
1781
  title: 'Working memory',
@@ -1792,6 +1844,9 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1792
1844
  if (connectedModelSummary && adv.includeConnectedModelSummary !== false) {
1793
1845
  sections.push({ section: 'connected_model_summary', title: 'SLM summary', value: connectedModelSummary });
1794
1846
  }
1847
+ else if (activeSummary && adv.includeActiveSummary !== false) {
1848
+ sections.push({ section: 'active_summary', title: 'Active summary', value: activeSummary });
1849
+ }
1795
1850
  sections.push({
1796
1851
  section: 'working_memory',
1797
1852
  title: 'Working memory',
@@ -2115,7 +2170,14 @@ class Mem0Memory {
2115
2170
  description: 'Define se o SLM resume working memory/tool state, memórias vetoriais, ambos ou nada.',
2116
2171
  },
2117
2172
  { displayName: 'Máximo de Caracteres de Entrada do SLM', name: 'connectedModelSummaryInputMaxChars', type: 'number', default: 3000 },
2118
- { displayName: 'Máximo de Caracteres do Resumo do SLM', name: 'connectedModelSummaryMaxChars', type: 'number', default: 900 },
2173
+ { displayName: 'Máximo de Caracteres do Resumo do SLM', name: 'connectedModelSummaryMaxChars', type: 'number', default: 1200 },
2174
+ { displayName: 'Incluir Active Summary', name: 'includeActiveSummary', type: 'boolean', default: true, description: 'Carrega o resumo ativo persistente da thread/sessão quando disponível.' },
2175
+ { displayName: 'Persistir Active Summary', name: 'persistActiveSummary', type: 'boolean', default: true, description: 'Salva o resumo atualizado pelo SLM para continuar a conversa depois de minutos, horas ou dias.' },
2176
+ { displayName: 'Máximo de Caracteres do Active Summary', name: 'activeSummaryMaxChars', type: 'number', default: 1600 },
2177
+ { displayName: 'Retenção do Active Summary (Dias)', name: 'activeSummaryRetentionDays', type: 'number', default: 30, description: 'Use 0 para não expirar pelo node. Não é cache técnico; é memória ativa persistente.' },
2178
+ { displayName: 'Ativar Cache Técnico do Resumo SLM', name: 'enableTransientSummaryCache', type: 'boolean', default: true, description: 'Evita chamadas duplicadas ao SLM para o mesmo pacote de contexto. Não substitui memória persistente.' },
2179
+ { displayName: 'TTL do Cache Técnico SLM (Segundos)', name: 'transientSummaryCacheTTLSeconds', type: 'number', default: 300 },
2180
+ { displayName: 'Máximo de Itens no Cache Técnico SLM', name: 'transientSummaryCacheMaxItems', type: 'number', default: 50 },
2119
2181
  { displayName: 'Máximo de Fatos no Resumo', name: 'summaryMaxFacts', type: 'number', default: 4 },
2120
2182
  { displayName: 'Incluir Scores', name: 'includeScores', type: 'boolean', default: true },
2121
2183
  { displayName: 'Incluir Diagnóstico', name: 'includeDiagnostics', type: 'boolean', default: false },
@@ -2770,11 +2832,20 @@ class Mem0Memory {
2770
2832
  vectorMemories,
2771
2833
  maxItems: adv.compressionMaxItems || 6,
2772
2834
  });
2835
+ const loadedActiveSummary = readActiveSummary(store, key, adv);
2773
2836
  let connectedModelSummary = '';
2837
+ const summaryDiagnostics = {
2838
+ source: 'none',
2839
+ transientCacheHit: false,
2840
+ activeSummaryLoaded: Boolean(loadedActiveSummary),
2841
+ activeSummaryUpdated: false,
2842
+ summaryChars: 0,
2843
+ };
2774
2844
  if (connectedLanguageModel && typeof connectedLanguageModel.invoke === 'function' && adv.includeSummary !== false && adv.includeConnectedModelSummary !== false) {
2775
2845
  try {
2776
2846
  const summaryInput = buildConnectedModelSummaryInput({
2777
2847
  query,
2848
+ activeSummary: loadedActiveSummary,
2778
2849
  profileFacts,
2779
2850
  workingMemory,
2780
2851
  decisionState,
@@ -2790,21 +2861,26 @@ class Mem0Memory {
2790
2861
  key,
2791
2862
  source: adv.summarySource || 'auto',
2792
2863
  input: summaryInput,
2793
- max: adv.connectedModelSummaryMaxChars || 700,
2864
+ max: adv.connectedModelSummaryMaxChars || 1200,
2794
2865
  }) : '';
2795
2866
  const cached = cacheKey ? store.connectedModelSummaryCache[cacheKey] : null;
2796
- if (cached && cached.summary && Date.now() - Number(cached.at || 0) < Number(adv.connectedModelSummaryCacheTTLSeconds || 60) * 1000) {
2867
+ const transientCacheEnabled = adv.enableTransientSummaryCache !== false;
2868
+ const transientTtlSeconds = Number(adv.transientSummaryCacheTTLSeconds || adv.connectedModelSummaryCacheTTLSeconds || 300);
2869
+ if (transientCacheEnabled && cached && cached.summary && Date.now() - Number(cached.at || 0) < transientTtlSeconds * 1000) {
2797
2870
  connectedModelSummary = cached.summary;
2798
2871
  connectedAi.languageModelSummaryCached = true;
2872
+ summaryDiagnostics.source = 'transient_cache';
2873
+ summaryDiagnostics.transientCacheHit = true;
2799
2874
  }
2800
2875
  else {
2801
2876
  connectedModelSummary = await invokeConnectedModelSummary(connectedLanguageModel, summaryInput, adv);
2802
2877
  if (cacheKey && connectedModelSummary) {
2803
2878
  store.connectedModelSummaryCache[cacheKey] = { summary: connectedModelSummary, at: Date.now() };
2804
2879
  const keys = Object.keys(store.connectedModelSummaryCache);
2805
- for (const oldKey of keys.slice(0, Math.max(0, keys.length - Number(adv.connectedModelSummaryCacheMaxItems || 50))))
2880
+ for (const oldKey of keys.slice(0, Math.max(0, keys.length - Number(adv.transientSummaryCacheMaxItems || adv.connectedModelSummaryCacheMaxItems || 50))))
2806
2881
  delete store.connectedModelSummaryCache[oldKey];
2807
2882
  }
2883
+ summaryDiagnostics.source = connectedModelSummary ? 'fresh_slm' : 'none';
2808
2884
  }
2809
2885
  connectedAi.languageModelSummary = Boolean(connectedModelSummary);
2810
2886
  }
@@ -2812,6 +2888,16 @@ class Mem0Memory {
2812
2888
  connectedAi.errors.push(`languageModel.invoke: ${error.message || String(error)}`);
2813
2889
  }
2814
2890
  }
2891
+ if (!connectedModelSummary && loadedActiveSummary) {
2892
+ connectedModelSummary = loadedActiveSummary;
2893
+ summaryDiagnostics.source = 'active_summary';
2894
+ }
2895
+ if (connectedModelSummary) {
2896
+ summaryDiagnostics.summaryChars = String(connectedModelSummary).length;
2897
+ if (summaryDiagnostics.source === 'fresh_slm') {
2898
+ summaryDiagnostics.activeSummaryUpdated = writeActiveSummary(store, key, connectedModelSummary, adv);
2899
+ }
2900
+ }
2815
2901
  const diagnostics = {
2816
2902
  vectorMemories: vectorMemories.length,
2817
2903
  recentMessages: recentMessages.length,
@@ -2820,6 +2906,7 @@ class Mem0Memory {
2820
2906
  project: project || null,
2821
2907
  memoryNamespace: key,
2822
2908
  connectedAi,
2909
+ activeSummary: summaryDiagnostics,
2823
2910
  };
2824
2911
  const contextHealth = deriveContextHealth({
2825
2912
  userId: key,
@@ -2858,6 +2945,7 @@ class Mem0Memory {
2858
2945
  highlights,
2859
2946
  graph,
2860
2947
  diagnostics,
2948
+ activeSummary: loadedActiveSummary,
2861
2949
  connectedModelSummary,
2862
2950
  adv,
2863
2951
  });
@@ -2904,6 +2992,9 @@ class Mem0Memory {
2904
2992
  includeOperationalState: adv.includeOperationalState !== false,
2905
2993
  includeActionLedger: adv.includeActionLedger !== false,
2906
2994
  includeEntityTimeline: adv.includeEntityTimeline !== false,
2995
+ includeActiveSummary: adv.includeActiveSummary !== false,
2996
+ persistActiveSummary: adv.persistActiveSummary !== false,
2997
+ enableTransientSummaryCache: adv.enableTransientSummaryCache !== false,
2907
2998
  compactForAgent: Boolean(adv.compactForAgent),
2908
2999
  includeWorkingMemory: adv.includeWorkingMemory !== false,
2909
3000
  includeDecisionState: adv.includeDecisionState !== false,
@@ -2912,6 +3003,7 @@ class Mem0Memory {
2912
3003
  },
2913
3004
  context: contextText,
2914
3005
  summary,
3006
+ activeSummary: loadedActiveSummary,
2915
3007
  connectedModelSummary,
2916
3008
  contextHealth,
2917
3009
  contextQualityScore: contextHealth.quality_score,
@@ -2959,6 +3051,7 @@ class Mem0Memory {
2959
3051
  temboryContext: audit,
2960
3052
  temboryContextText: contextText,
2961
3053
  temborySummary: summary,
3054
+ temboryActiveSummary: loadedActiveSummary,
2962
3055
  temboryConnectedModelSummary: connectedModelSummary,
2963
3056
  temboryContextHealth: contextHealth,
2964
3057
  temboryContextQualityScore: contextHealth.quality_score,
@@ -3094,6 +3187,9 @@ exports.__private = {
3094
3187
  compactToolResult,
3095
3188
  compactToolHistoryForAgent,
3096
3189
  compactOperationalStateForAgent,
3190
+ activeSummaryIsFresh,
3191
+ readActiveSummary,
3192
+ writeActiveSummary,
3097
3193
  buildConnectedModelSummaryInput,
3098
3194
  cleanModelSummaryText,
3099
3195
  invokeConnectedModelSummary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
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",
@@ -48,7 +48,6 @@
48
48
  "n8n",
49
49
  "tembory",
50
50
  "tembory-memory",
51
- "elephant-brain",
52
51
  "memory",
53
52
  "ai",
54
53
  "llm",