n8n-nodes-tembory 1.0.13 → 1.0.15

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,23 @@
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.15`.
6
+
7
+ ## 1.0.15
8
+
9
+ - Reorganiza a interface do node em `Configuração Tembory`, com grupos para Essenciais, Mais Inteligência, Controles Avançados de Busca, Resumo e Estado Persistente, e Diagnóstico e Auditoria.
10
+ - Mantém a coleção legada `Avançado` para compatibilidade com workflows já existentes.
11
+ - Define `Produção Balanceada` como preset padrão para novas configurações.
12
+ - Prioriza IDs estáveis de thread/conversa/contato/cliente antes de `sessionId` na expressão padrão do `ID da Thread`.
13
+ - Adiciona teste para validar a mesclagem dos grupos da nova UI com os presets operacionais.
14
+
15
+ ## 1.0.14
16
+
17
+ - Adiciona Active Summary persistente por namespace/thread para continuar conversas depois de minutos, horas ou dias.
18
+ - Separa cache tecnico curto do resumo SLM da memoria ativa persistente.
19
+ - Aumenta os defaults do resumo SLM nos presets de producao para preservar mais inteligencia antes de comprimir para o agente.
20
+ - Adiciona diagnosticos para saber se o contexto veio de SLM novo, active summary ou cache tecnico.
21
+ - Expõe controles avancados de Active Summary e cache tecnico no node.
6
22
 
7
23
  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
24
 
@@ -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
  }
@@ -881,7 +883,7 @@ const userKeyFrom = (threadId, adv, project = '') => {
881
883
  return namespace ? `${namespace}::${base}` : base;
882
884
  };
883
885
  const applyOperationalPreset = (advanced = {}) => {
884
- const preset = String(advanced.operationPreset || 'custom');
886
+ const preset = String(advanced.operationPreset || 'productionBalanced');
885
887
  const presets = {
886
888
  diagnostic: {
887
889
  summarySource: 'auto',
@@ -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',
@@ -1033,6 +1053,30 @@ const applyOperationalPreset = (advanced = {}) => {
1033
1053
  };
1034
1054
  return { ...(presets[preset === 'lab' ? 'diagnostic' : preset] || {}), ...advanced };
1035
1055
  };
1056
+ const flattenAdvancedGroups = (groups = {}) => {
1057
+ const flattened = {};
1058
+ const groupNames = ['essentials', 'intelligence', 'retrievalControls', 'summaryState', 'diagnosticsAudit'];
1059
+ for (const groupName of groupNames) {
1060
+ const value = groups === null || groups === void 0 ? void 0 : groups[groupName];
1061
+ const groupValue = Array.isArray(value) ? value[0] : value;
1062
+ if (groupValue && typeof groupValue === 'object')
1063
+ Object.assign(flattened, groupValue);
1064
+ }
1065
+ return flattened;
1066
+ };
1067
+ const readAdvancedOptions = (ctx, itemIndex) => {
1068
+ let legacy = {};
1069
+ let grouped = {};
1070
+ try {
1071
+ legacy = ctx.getNodeParameter('advanced', itemIndex, {}) || {};
1072
+ }
1073
+ catch { }
1074
+ try {
1075
+ grouped = flattenAdvancedGroups(ctx.getNodeParameter('advancedGroups', itemIndex, {}) || {});
1076
+ }
1077
+ catch { }
1078
+ return { ...legacy, ...grouped };
1079
+ };
1036
1080
  const pruneByLimit = (items, limit) => items.slice(Math.max(0, items.length - Math.max(1, Number(limit) || 1)));
1037
1081
  const dedupeRecentMessages = (items) => {
1038
1082
  const seen = new Set();
@@ -1582,7 +1626,34 @@ const cleanModelSummaryText = (value, max = 900) => {
1582
1626
  .trim();
1583
1627
  return truncate(text, max);
1584
1628
  };
1585
- const buildConnectedModelSummaryInput = ({ query, profileFacts, workingMemory, decisionState, memoryCompression, operationalState, toolHistory, recentMessages, vectorMemories, highlights, adv }) => {
1629
+ const activeSummaryIsFresh = (entry, adv = {}) => {
1630
+ if (!entry || !entry.summary)
1631
+ return false;
1632
+ const retentionDays = Number(adv.activeSummaryRetentionDays ?? 30);
1633
+ if (retentionDays <= 0)
1634
+ return true;
1635
+ return Date.now() - Number(entry.updatedAt || entry.at || 0) < retentionDays * 86400000;
1636
+ };
1637
+ const readActiveSummary = (store, key, adv = {}) => {
1638
+ if (adv.includeActiveSummary === false)
1639
+ return '';
1640
+ const entry = store.activeSummary?.[key];
1641
+ if (!activeSummaryIsFresh(entry, adv))
1642
+ return '';
1643
+ return truncate(String(entry.summary || ''), Number(adv.activeSummaryMaxChars || 1600));
1644
+ };
1645
+ const writeActiveSummary = (store, key, summary, adv = {}) => {
1646
+ if (!store || !key || adv.persistActiveSummary === false || !summary)
1647
+ return false;
1648
+ store.activeSummary = store.activeSummary || {};
1649
+ store.activeSummary[key] = {
1650
+ summary: truncate(String(summary), Number(adv.activeSummaryMaxChars || 1600)),
1651
+ updatedAt: Date.now(),
1652
+ source: 'slm',
1653
+ };
1654
+ return true;
1655
+ };
1656
+ const buildConnectedModelSummaryInput = ({ query, activeSummary, profileFacts, workingMemory, decisionState, memoryCompression, operationalState, toolHistory, recentMessages, vectorMemories, highlights, adv }) => {
1586
1657
  const source = String(adv.summarySource || 'auto');
1587
1658
  if (source === 'off' || source === 'disabled' || adv.includeConnectedModelSummary === false)
1588
1659
  return '';
@@ -1590,6 +1661,7 @@ const buildConnectedModelSummaryInput = ({ query, profileFacts, workingMemory, d
1590
1661
  const includeActive = source === 'auto' || source === 'activeContext' || source === 'active';
1591
1662
  const payload = cleanContextValue({
1592
1663
  query: String(query || ''),
1664
+ existing_active_summary: activeSummary ? truncate(activeSummary, Number(adv.activeSummaryMaxChars || 1600)) : undefined,
1593
1665
  active_context: includeActive ? {
1594
1666
  profile_facts: renderProfileFacts(profileFacts),
1595
1667
  working_memory: compactWorkingMemoryForAgent(workingMemory || {}),
@@ -1618,10 +1690,10 @@ const invokeConnectedModelSummary = async (connectedLanguageModel, summaryInput,
1618
1690
  const response = await connectedLanguageModel.invoke([
1619
1691
  toBaseMessage({
1620
1692
  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}`,
1693
+ 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
1694
  }),
1623
1695
  ]);
1624
- return cleanModelSummaryText(response, Number(adv.connectedModelSummaryMaxChars || 700));
1696
+ return cleanModelSummaryText(response, Number(adv.connectedModelSummaryMaxChars || 1200));
1625
1697
  };
1626
1698
  const contextSizeOfMessages = (messages = []) => {
1627
1699
  const perMessage = (messages || []).map((message, index) => {
@@ -1653,6 +1725,7 @@ const wrapTemboryMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1653
1725
  context: response.temboryContext,
1654
1726
  contextText: response.temboryContextText,
1655
1727
  summary: response.temborySummary,
1728
+ activeSummary: response.temboryActiveSummary,
1656
1729
  connectedModelSummary: response.temboryConnectedModelSummary,
1657
1730
  workingMemory: response.temboryWorkingMemory,
1658
1731
  decisionState: response.temboryDecisionState,
@@ -1700,7 +1773,7 @@ const wrapTemboryMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1700
1773
  return target[prop];
1701
1774
  },
1702
1775
  });
1703
- const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, workingMemory, decisionState, memoryCompression, operationalState, actionLedger, entityTimeline, vectorMemories, recentMessages, toolHistory, highlights, graph, diagnostics, connectedModelSummary, adv }) => {
1776
+ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, workingMemory, decisionState, memoryCompression, operationalState, actionLedger, entityTimeline, vectorMemories, recentMessages, toolHistory, highlights, graph, diagnostics, activeSummary, connectedModelSummary, adv }) => {
1704
1777
  const includeHeader = adv.includeContextHeader !== false;
1705
1778
  const includeSummary = adv.includeSummary !== false;
1706
1779
  const includeScores = adv.includeScores !== false;
@@ -1724,6 +1797,9 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1724
1797
  if (connectedModelSummary && adv.includeConnectedModelSummary !== false) {
1725
1798
  sections.push({ section: 'connected_model_summary', title: 'SLM summary', value: connectedModelSummary });
1726
1799
  }
1800
+ else if (activeSummary && adv.includeActiveSummary !== false) {
1801
+ sections.push({ section: 'active_summary', title: 'Active summary', value: activeSummary });
1802
+ }
1727
1803
  sections.push({
1728
1804
  section: 'working_memory',
1729
1805
  title: 'Working memory',
@@ -1792,6 +1868,9 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1792
1868
  if (connectedModelSummary && adv.includeConnectedModelSummary !== false) {
1793
1869
  sections.push({ section: 'connected_model_summary', title: 'SLM summary', value: connectedModelSummary });
1794
1870
  }
1871
+ else if (activeSummary && adv.includeActiveSummary !== false) {
1872
+ sections.push({ section: 'active_summary', title: 'Active summary', value: activeSummary });
1873
+ }
1795
1874
  sections.push({
1796
1875
  section: 'working_memory',
1797
1876
  title: 'Working memory',
@@ -2003,7 +2082,7 @@ class Mem0Memory {
2003
2082
  displayName: 'ID da Thread',
2004
2083
  name: 'threadId',
2005
2084
  type: 'string',
2006
- default: '={{ $json.threadId || $json.sessionId || $json.body?.messages?.[0]?.contactId || $json.body?.contactId || $json.contactId || $json.from || $json.sender || $executionId }}',
2085
+ default: '={{ $json.threadId || $json.body?.threadId || $json.body?.conversationId || $json.conversationId || $json.body?.messages?.[0]?.contactId || $json.body?.contactId || $json.contactId || $json.customerId || $json.body?.customerId || $json.from || $json.sender || $json.sessionId || $executionId }}',
2007
2086
  description: 'Identificador estável desta conversa/thread. Usado como user_id se nenhum User ID explícito for informado.',
2008
2087
  },
2009
2088
  {
@@ -2060,11 +2139,186 @@ class Mem0Memory {
2060
2139
  description: 'Como o contexto recuperado será entregue ao agente antes da LLM.',
2061
2140
  },
2062
2141
  {
2063
- displayName: 'Avançado',
2142
+ displayName: 'Configuração Tembory',
2143
+ name: 'advancedGroups',
2144
+ type: 'fixedCollection',
2145
+ placeholder: 'Adicionar Grupo',
2146
+ default: {
2147
+ essentials: {
2148
+ operationPreset: 'productionBalanced',
2149
+ includeContextHeader: true,
2150
+ compactStateSections: true,
2151
+ contextMaxChars: 10000,
2152
+ includeToolHistory: true,
2153
+ includeToolResults: true,
2154
+ includeWorkingMemory: true,
2155
+ includeDecisionState: true,
2156
+ includeOperationalState: true,
2157
+ includeMemoryCompression: true,
2158
+ },
2159
+ intelligence: {
2160
+ includeProfileFacts: true,
2161
+ includeRecentMessages: true,
2162
+ recentMessagesLastN: 6,
2163
+ includeRecentHighlights: true,
2164
+ recentHighlightsMaxItems: 6,
2165
+ includeRelations: true,
2166
+ maxRelations: 10,
2167
+ includeActionLedger: true,
2168
+ includeEntityTimeline: true,
2169
+ },
2170
+ retrievalControls: {
2171
+ topK: 6,
2172
+ rerank: true,
2173
+ lastN: 8,
2174
+ alpha: 0.65,
2175
+ halfLifeHours: 48,
2176
+ maxReturn: 12,
2177
+ mmr: true,
2178
+ mmrLambda: 0.5,
2179
+ },
2180
+ summaryState: {
2181
+ includeSummary: true,
2182
+ includeConnectedModelSummary: true,
2183
+ summarySource: 'auto',
2184
+ connectedModelSummaryInputMaxChars: 4200,
2185
+ connectedModelSummaryMaxChars: 1200,
2186
+ includeActiveSummary: true,
2187
+ persistActiveSummary: true,
2188
+ activeSummaryMaxChars: 1800,
2189
+ activeSummaryRetentionDays: 30,
2190
+ enableTransientSummaryCache: true,
2191
+ transientSummaryCacheTTLSeconds: 300,
2192
+ transientSummaryCacheMaxItems: 50,
2193
+ summaryMaxFacts: 4,
2194
+ },
2195
+ diagnosticsAudit: {
2196
+ includeScores: false,
2197
+ includeDiagnostics: false,
2198
+ persistToolFactsToMem0: false,
2199
+ includeToolHistorySemanticFallback: false,
2200
+ toolHistoryLastN: 10,
2201
+ toolHistoryTTLSeconds: 3600,
2202
+ },
2203
+ },
2204
+ typeOptions: {
2205
+ multipleValues: false,
2206
+ },
2207
+ description: 'Grupos recomendados para producao. Todos os recursos continuam disponiveis, mas separados por finalidade.',
2208
+ options: [
2209
+ {
2210
+ name: 'essentials',
2211
+ displayName: 'Essenciais',
2212
+ values: [
2213
+ {
2214
+ displayName: 'Preset Operacional',
2215
+ name: 'operationPreset',
2216
+ type: 'options',
2217
+ options: [
2218
+ { name: 'Custom', value: 'custom' },
2219
+ { name: 'Diagnóstico Completo', value: 'diagnostic' },
2220
+ { name: 'Produção Balanceada', value: 'productionBalanced' },
2221
+ { name: 'Produção Econômica', value: 'productionCheap' },
2222
+ { name: 'Produção Nano/SLM', value: 'productionNano' },
2223
+ { name: 'Auditoria', value: 'audit' },
2224
+ ],
2225
+ default: 'productionBalanced',
2226
+ description: 'Preenche defaults seguros para contexto, historico de tools e memoria ativa.',
2227
+ },
2228
+ { displayName: 'Incluir Cabeçalho de Contexto', name: 'includeContextHeader', type: 'boolean', default: true },
2229
+ { displayName: 'Organizar Seções de Produção', name: 'compactStateSections', type: 'boolean', default: true },
2230
+ { displayName: 'Máximo de Caracteres do Contexto', name: 'contextMaxChars', type: 'number', default: 10000 },
2231
+ { displayName: 'Incluir Tool History', name: 'includeToolHistory', type: 'boolean', default: true },
2232
+ { displayName: 'Incluir Resultado Das Tools', name: 'includeToolResults', type: 'boolean', default: true },
2233
+ { displayName: 'Incluir Working Memory', name: 'includeWorkingMemory', type: 'boolean', default: true },
2234
+ { displayName: 'Incluir Decision State', name: 'includeDecisionState', type: 'boolean', default: true },
2235
+ { displayName: 'Incluir Estado Operacional', name: 'includeOperationalState', type: 'boolean', default: true },
2236
+ { displayName: 'Incluir Compressão de Memória', name: 'includeMemoryCompression', type: 'boolean', default: true },
2237
+ ],
2238
+ },
2239
+ {
2240
+ name: 'intelligence',
2241
+ displayName: 'Mais Inteligência',
2242
+ values: [
2243
+ { displayName: 'Incluir Profile Facts', name: 'includeProfileFacts', type: 'boolean', default: true },
2244
+ { displayName: 'Incluir Mensagens Recentes', name: 'includeRecentMessages', type: 'boolean', default: true },
2245
+ { displayName: 'Últimas N Mensagens', name: 'recentMessagesLastN', type: 'number', default: 6 },
2246
+ { displayName: 'Incluir Highlights Recentes', name: 'includeRecentHighlights', type: 'boolean', default: true },
2247
+ { displayName: 'Máximo de Highlights', name: 'recentHighlightsMaxItems', type: 'number', default: 6 },
2248
+ { displayName: 'Incluir Grafo', name: 'includeRelations', type: 'boolean', default: true },
2249
+ { displayName: 'Máximo de Relações', name: 'maxRelations', type: 'number', default: 10 },
2250
+ { displayName: 'Incluir Action Ledger', name: 'includeActionLedger', type: 'boolean', default: true },
2251
+ { displayName: 'Incluir Entity Timeline', name: 'includeEntityTimeline', type: 'boolean', default: true },
2252
+ ],
2253
+ },
2254
+ {
2255
+ name: 'retrievalControls',
2256
+ displayName: 'Controles Avançados de Busca',
2257
+ values: [
2258
+ { displayName: 'Top K', name: 'topK', type: 'number', typeOptions: { minValue: 1 }, default: 6 },
2259
+ { displayName: 'Rerank', name: 'rerank', type: 'boolean', default: true },
2260
+ { displayName: 'Fields (lista separada por vírgula)', name: 'fields', type: 'string', default: '' },
2261
+ { displayName: 'Filters (JSON)', name: 'filters', type: 'json', default: '{}' },
2262
+ { displayName: 'Last N (recentes)', name: 'lastN', type: 'number', default: 8 },
2263
+ { displayName: 'Alpha (peso semântico)', name: 'alpha', type: 'number', typeOptions: { minValue: 0, maxValue: 1, numberPrecision: 2 }, default: 0.65 },
2264
+ { displayName: 'Half-life (horas)', name: 'halfLifeHours', type: 'number', typeOptions: { minValue: 1 }, default: 48 },
2265
+ { displayName: 'Máximo a retornar', name: 'maxReturn', type: 'number', typeOptions: { minValue: 1 }, default: 12 },
2266
+ { displayName: 'MMR (diversidade)', name: 'mmr', type: 'boolean', default: true },
2267
+ { displayName: 'MMR Lambda', name: 'mmrLambda', type: 'number', typeOptions: { minValue: 0, maxValue: 1, numberPrecision: 2 }, default: 0.5 },
2268
+ { displayName: 'Compactar Contexto Para Agente', name: 'compactForAgent', type: 'boolean', default: false },
2269
+ ],
2270
+ },
2271
+ {
2272
+ name: 'summaryState',
2273
+ displayName: 'Resumo e Estado Persistente',
2274
+ values: [
2275
+ { displayName: 'Incluir Resumo', name: 'includeSummary', type: 'boolean', default: true },
2276
+ { displayName: 'Incluir Resumo do SLM', name: 'includeConnectedModelSummary', type: 'boolean', default: true },
2277
+ {
2278
+ displayName: 'Fonte do Resumo do SLM',
2279
+ name: 'summarySource',
2280
+ type: 'options',
2281
+ options: [
2282
+ { name: 'Automático', value: 'auto' },
2283
+ { name: 'Contexto Ativo', value: 'activeContext' },
2284
+ { name: 'Somente Vetores', value: 'vectorOnly' },
2285
+ { name: 'Desligado', value: 'off' },
2286
+ ],
2287
+ default: 'auto',
2288
+ },
2289
+ { displayName: 'Máximo de Caracteres de Entrada do SLM', name: 'connectedModelSummaryInputMaxChars', type: 'number', default: 4200 },
2290
+ { displayName: 'Máximo de Caracteres do Resumo do SLM', name: 'connectedModelSummaryMaxChars', type: 'number', default: 1200 },
2291
+ { displayName: 'Incluir Active Summary', name: 'includeActiveSummary', type: 'boolean', default: true },
2292
+ { displayName: 'Persistir Active Summary', name: 'persistActiveSummary', type: 'boolean', default: true },
2293
+ { displayName: 'Máximo de Caracteres do Active Summary', name: 'activeSummaryMaxChars', type: 'number', default: 1800 },
2294
+ { displayName: 'Retenção do Active Summary (Dias)', name: 'activeSummaryRetentionDays', type: 'number', default: 30 },
2295
+ { displayName: 'Ativar Cache Técnico do Resumo SLM', name: 'enableTransientSummaryCache', type: 'boolean', default: true },
2296
+ { displayName: 'TTL do Cache Técnico SLM (Segundos)', name: 'transientSummaryCacheTTLSeconds', type: 'number', default: 300 },
2297
+ { displayName: 'Máximo de Itens no Cache Técnico SLM', name: 'transientSummaryCacheMaxItems', type: 'number', default: 50 },
2298
+ { displayName: 'Máximo de Fatos no Resumo', name: 'summaryMaxFacts', type: 'number', default: 4 },
2299
+ ],
2300
+ },
2301
+ {
2302
+ name: 'diagnosticsAudit',
2303
+ displayName: 'Diagnóstico e Auditoria',
2304
+ values: [
2305
+ { displayName: 'Incluir Scores', name: 'includeScores', type: 'boolean', default: false },
2306
+ { displayName: 'Incluir Diagnóstico', name: 'includeDiagnostics', type: 'boolean', default: false },
2307
+ { displayName: 'Últimas N Tools', name: 'toolHistoryLastN', type: 'number', default: 10 },
2308
+ { displayName: 'TTL Das Tools (Segundos)', name: 'toolHistoryTTLSeconds', type: 'number', default: 3600 },
2309
+ { displayName: 'Persistir Tool Facts no Backend', name: 'persistToolFactsToMem0', type: 'boolean', default: false },
2310
+ { displayName: 'Fallback Semântico Para Tools', name: 'includeToolHistorySemanticFallback', type: 'boolean', default: false },
2311
+ ],
2312
+ },
2313
+ ],
2314
+ },
2315
+ {
2316
+ displayName: 'Avançado Legado (Compatibilidade)',
2064
2317
  name: 'advanced',
2065
2318
  type: 'collection',
2066
2319
  placeholder: 'Opções',
2067
2320
  default: {},
2321
+ description: 'Campos legados preservados para workflows existentes. Para novas configuracoes, use Configuração Tembory.',
2068
2322
  options: [
2069
2323
  { displayName: 'User ID', name: 'userId', type: 'string', default: '' },
2070
2324
  {
@@ -2079,7 +2333,7 @@ class Mem0Memory {
2079
2333
  { name: 'Produção Nano/SLM', value: 'productionNano' },
2080
2334
  { name: 'Auditoria', value: 'audit' },
2081
2335
  ],
2082
- default: 'custom',
2336
+ default: 'productionBalanced',
2083
2337
  description: 'Preenche defaults seguros para contexto, diagnostico, historico de tools e mensagens recentes. Valores definidos manualmente continuam tendo prioridade.',
2084
2338
  },
2085
2339
  { displayName: 'Agent ID', name: 'agentId', type: 'string', default: '' },
@@ -2115,7 +2369,14 @@ class Mem0Memory {
2115
2369
  description: 'Define se o SLM resume working memory/tool state, memórias vetoriais, ambos ou nada.',
2116
2370
  },
2117
2371
  { 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 },
2372
+ { displayName: 'Máximo de Caracteres do Resumo do SLM', name: 'connectedModelSummaryMaxChars', type: 'number', default: 1200 },
2373
+ { displayName: 'Incluir Active Summary', name: 'includeActiveSummary', type: 'boolean', default: true, description: 'Carrega o resumo ativo persistente da thread/sessão quando disponível.' },
2374
+ { 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.' },
2375
+ { displayName: 'Máximo de Caracteres do Active Summary', name: 'activeSummaryMaxChars', type: 'number', default: 1600 },
2376
+ { 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.' },
2377
+ { 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.' },
2378
+ { displayName: 'TTL do Cache Técnico SLM (Segundos)', name: 'transientSummaryCacheTTLSeconds', type: 'number', default: 300 },
2379
+ { displayName: 'Máximo de Itens no Cache Técnico SLM', name: 'transientSummaryCacheMaxItems', type: 'number', default: 50 },
2119
2380
  { displayName: 'Máximo de Fatos no Resumo', name: 'summaryMaxFacts', type: 'number', default: 4 },
2120
2381
  { displayName: 'Incluir Scores', name: 'includeScores', type: 'boolean', default: true },
2121
2382
  { displayName: 'Incluir Diagnóstico', name: 'includeDiagnostics', type: 'boolean', default: false },
@@ -2270,7 +2531,7 @@ class Mem0Memory {
2270
2531
  async saveContextForItem(itemIndex, inputValues = {}, outputValues = {}) {
2271
2532
  const threadId = this.getNodeParameter('threadId', itemIndex);
2272
2533
  const project = this.getNodeParameter('project', itemIndex, '');
2273
- const adv = applyOperationalPreset(this.getNodeParameter('advanced', itemIndex, {}) || {});
2534
+ const adv = applyOperationalPreset(readAdvancedOptions(this, itemIndex));
2274
2535
  const store = getMemoryStore(this);
2275
2536
  const key = userKeyFrom(threadId, adv, project);
2276
2537
  const input = pickText(inputValues, ['input', 'chatInput', 'text', 'query', 'question']);
@@ -2474,7 +2735,7 @@ class Mem0Memory {
2474
2735
  let payloadFormat = this.getNodeParameter('payloadFormat', itemIndex, 'structured');
2475
2736
  const threadId = this.getNodeParameter('threadId', itemIndex);
2476
2737
  const project = this.getNodeParameter('project', itemIndex, '');
2477
- const adv = applyOperationalPreset(this.getNodeParameter('advanced', itemIndex, {}) || {});
2738
+ const adv = applyOperationalPreset(readAdvancedOptions(this, itemIndex));
2478
2739
  payloadFormat = adv.payloadFormat || payloadFormat;
2479
2740
  const store = getMemoryStore(this);
2480
2741
  const key = userKeyFrom(threadId, adv, project);
@@ -2770,11 +3031,20 @@ class Mem0Memory {
2770
3031
  vectorMemories,
2771
3032
  maxItems: adv.compressionMaxItems || 6,
2772
3033
  });
3034
+ const loadedActiveSummary = readActiveSummary(store, key, adv);
2773
3035
  let connectedModelSummary = '';
3036
+ const summaryDiagnostics = {
3037
+ source: 'none',
3038
+ transientCacheHit: false,
3039
+ activeSummaryLoaded: Boolean(loadedActiveSummary),
3040
+ activeSummaryUpdated: false,
3041
+ summaryChars: 0,
3042
+ };
2774
3043
  if (connectedLanguageModel && typeof connectedLanguageModel.invoke === 'function' && adv.includeSummary !== false && adv.includeConnectedModelSummary !== false) {
2775
3044
  try {
2776
3045
  const summaryInput = buildConnectedModelSummaryInput({
2777
3046
  query,
3047
+ activeSummary: loadedActiveSummary,
2778
3048
  profileFacts,
2779
3049
  workingMemory,
2780
3050
  decisionState,
@@ -2790,21 +3060,26 @@ class Mem0Memory {
2790
3060
  key,
2791
3061
  source: adv.summarySource || 'auto',
2792
3062
  input: summaryInput,
2793
- max: adv.connectedModelSummaryMaxChars || 700,
3063
+ max: adv.connectedModelSummaryMaxChars || 1200,
2794
3064
  }) : '';
2795
3065
  const cached = cacheKey ? store.connectedModelSummaryCache[cacheKey] : null;
2796
- if (cached && cached.summary && Date.now() - Number(cached.at || 0) < Number(adv.connectedModelSummaryCacheTTLSeconds || 60) * 1000) {
3066
+ const transientCacheEnabled = adv.enableTransientSummaryCache !== false;
3067
+ const transientTtlSeconds = Number(adv.transientSummaryCacheTTLSeconds || adv.connectedModelSummaryCacheTTLSeconds || 300);
3068
+ if (transientCacheEnabled && cached && cached.summary && Date.now() - Number(cached.at || 0) < transientTtlSeconds * 1000) {
2797
3069
  connectedModelSummary = cached.summary;
2798
3070
  connectedAi.languageModelSummaryCached = true;
3071
+ summaryDiagnostics.source = 'transient_cache';
3072
+ summaryDiagnostics.transientCacheHit = true;
2799
3073
  }
2800
3074
  else {
2801
3075
  connectedModelSummary = await invokeConnectedModelSummary(connectedLanguageModel, summaryInput, adv);
2802
3076
  if (cacheKey && connectedModelSummary) {
2803
3077
  store.connectedModelSummaryCache[cacheKey] = { summary: connectedModelSummary, at: Date.now() };
2804
3078
  const keys = Object.keys(store.connectedModelSummaryCache);
2805
- for (const oldKey of keys.slice(0, Math.max(0, keys.length - Number(adv.connectedModelSummaryCacheMaxItems || 50))))
3079
+ for (const oldKey of keys.slice(0, Math.max(0, keys.length - Number(adv.transientSummaryCacheMaxItems || adv.connectedModelSummaryCacheMaxItems || 50))))
2806
3080
  delete store.connectedModelSummaryCache[oldKey];
2807
3081
  }
3082
+ summaryDiagnostics.source = connectedModelSummary ? 'fresh_slm' : 'none';
2808
3083
  }
2809
3084
  connectedAi.languageModelSummary = Boolean(connectedModelSummary);
2810
3085
  }
@@ -2812,6 +3087,16 @@ class Mem0Memory {
2812
3087
  connectedAi.errors.push(`languageModel.invoke: ${error.message || String(error)}`);
2813
3088
  }
2814
3089
  }
3090
+ if (!connectedModelSummary && loadedActiveSummary) {
3091
+ connectedModelSummary = loadedActiveSummary;
3092
+ summaryDiagnostics.source = 'active_summary';
3093
+ }
3094
+ if (connectedModelSummary) {
3095
+ summaryDiagnostics.summaryChars = String(connectedModelSummary).length;
3096
+ if (summaryDiagnostics.source === 'fresh_slm') {
3097
+ summaryDiagnostics.activeSummaryUpdated = writeActiveSummary(store, key, connectedModelSummary, adv);
3098
+ }
3099
+ }
2815
3100
  const diagnostics = {
2816
3101
  vectorMemories: vectorMemories.length,
2817
3102
  recentMessages: recentMessages.length,
@@ -2820,6 +3105,7 @@ class Mem0Memory {
2820
3105
  project: project || null,
2821
3106
  memoryNamespace: key,
2822
3107
  connectedAi,
3108
+ activeSummary: summaryDiagnostics,
2823
3109
  };
2824
3110
  const contextHealth = deriveContextHealth({
2825
3111
  userId: key,
@@ -2858,6 +3144,7 @@ class Mem0Memory {
2858
3144
  highlights,
2859
3145
  graph,
2860
3146
  diagnostics,
3147
+ activeSummary: loadedActiveSummary,
2861
3148
  connectedModelSummary,
2862
3149
  adv,
2863
3150
  });
@@ -2904,6 +3191,9 @@ class Mem0Memory {
2904
3191
  includeOperationalState: adv.includeOperationalState !== false,
2905
3192
  includeActionLedger: adv.includeActionLedger !== false,
2906
3193
  includeEntityTimeline: adv.includeEntityTimeline !== false,
3194
+ includeActiveSummary: adv.includeActiveSummary !== false,
3195
+ persistActiveSummary: adv.persistActiveSummary !== false,
3196
+ enableTransientSummaryCache: adv.enableTransientSummaryCache !== false,
2907
3197
  compactForAgent: Boolean(adv.compactForAgent),
2908
3198
  includeWorkingMemory: adv.includeWorkingMemory !== false,
2909
3199
  includeDecisionState: adv.includeDecisionState !== false,
@@ -2912,6 +3202,7 @@ class Mem0Memory {
2912
3202
  },
2913
3203
  context: contextText,
2914
3204
  summary,
3205
+ activeSummary: loadedActiveSummary,
2915
3206
  connectedModelSummary,
2916
3207
  contextHealth,
2917
3208
  contextQualityScore: contextHealth.quality_score,
@@ -2959,6 +3250,7 @@ class Mem0Memory {
2959
3250
  temboryContext: audit,
2960
3251
  temboryContextText: contextText,
2961
3252
  temborySummary: summary,
3253
+ temboryActiveSummary: loadedActiveSummary,
2962
3254
  temboryConnectedModelSummary: connectedModelSummary,
2963
3255
  temboryContextHealth: contextHealth,
2964
3256
  temboryContextQualityScore: contextHealth.quality_score,
@@ -2987,7 +3279,7 @@ class Mem0Memory {
2987
3279
  const retrievalMode = this.getNodeParameter('retrievalMode', i);
2988
3280
  const threadId = this.getNodeParameter('threadId', i);
2989
3281
  const project = this.getNodeParameter('project', i, '');
2990
- const adv = applyOperationalPreset(this.getNodeParameter('advanced', i, {}) || {});
3282
+ const adv = applyOperationalPreset(readAdvancedOptions(this, i));
2991
3283
  const key = userKeyFrom(threadId, adv, project);
2992
3284
  const query = asSearchQuery(this.getNodeParameter('query', i, ''));
2993
3285
  const connectedEmbedding = await getConnectedEmbedding(this, i);
@@ -3069,6 +3361,7 @@ exports.__private = {
3069
3361
  encodeRecentMessage,
3070
3362
  userKeyFrom,
3071
3363
  applyOperationalPreset,
3364
+ flattenAdvancedGroups,
3072
3365
  asSearchQuery,
3073
3366
  canonicalToolInput,
3074
3367
  buildContextMessages,
@@ -3094,6 +3387,9 @@ exports.__private = {
3094
3387
  compactToolResult,
3095
3388
  compactToolHistoryForAgent,
3096
3389
  compactOperationalStateForAgent,
3390
+ activeSummaryIsFresh,
3391
+ readActiveSummary,
3392
+ writeActiveSummary,
3097
3393
  buildConnectedModelSummaryInput,
3098
3394
  cleanModelSummaryText,
3099
3395
  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.15",
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",