n8n-nodes-tembory 1.0.9 → 1.0.11

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,10 +2,12 @@
2
2
 
3
3
  Node de memoria operacional da Tembory para agentes de IA no n8n.
4
4
 
5
- Versao atual: `1.0.1`.
5
+ Versao atual: `1.0.11`.
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
+ 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
+
9
11
  ## Smoke tecnico
10
12
 
11
13
  Antes de publicar uma versao, rode:
@@ -27,7 +27,7 @@ const snapshotJson = (value) => {
27
27
  }
28
28
  };
29
29
  const truncate = (value, max = MAX_TEXT) => {
30
- const text = typeof value === 'string' ? value : safeStringify(value);
30
+ const text = value === undefined || value === null ? '' : (typeof value === 'string' ? value : safeStringify(value));
31
31
  if (text.length <= max)
32
32
  return text;
33
33
  return `${text.slice(0, max)}\n... [truncated ${text.length - max} chars]`;
@@ -53,7 +53,7 @@ const saveClientVectorMemories = async (ctx, memories, ids = {}) => {
53
53
  const valid = memories.filter((memory) => String(memory === null || memory === void 0 ? void 0 : memory.text || '').trim());
54
54
  if (!valid.length)
55
55
  return null;
56
- return GenericFunctions_1.mem0ApiRequest.call(ctx, 'POST', '/elefai/v1/vector-memories', {
56
+ return GenericFunctions_1.mem0ApiRequest.call(ctx, 'POST', '/tembory/v1/vector-memories', {
57
57
  memories: valid,
58
58
  user_id: ids.user_id,
59
59
  agent_id: ids.agent_id,
@@ -64,7 +64,7 @@ const searchClientVectorMemories = async (ctx, embedding, query, body = {}) => {
64
64
  const text = String(query || '').trim();
65
65
  if (!text)
66
66
  return { results: [] };
67
- return GenericFunctions_1.mem0ApiRequest.call(ctx, 'POST', '/elefai/v1/vector-memories/search', {
67
+ return GenericFunctions_1.mem0ApiRequest.call(ctx, 'POST', '/tembory/v1/vector-memories/search', {
68
68
  ...body,
69
69
  query: text,
70
70
  embedding: await embedQueryCached(embedding, text),
@@ -167,11 +167,11 @@ const scoreOf = (m) => {
167
167
  var _a, _b, _c, _d;
168
168
  return (_d = (_c = (_b = (_a = m === null || m === void 0 ? void 0 : m.score) !== null && _a !== void 0 ? _a : m === null || m === void 0 ? void 0 : m.similarity) !== null && _b !== void 0 ? _b : m === null || m === void 0 ? void 0 : m.relevance) !== null && _c !== void 0 ? _c : m === null || m === void 0 ? void 0 : m.distance) !== null && _d !== void 0 ? _d : undefined;
169
169
  };
170
- const scoreMetaOf = (m) => (m && typeof m === 'object' && (m.elefaiScore || m._elefaiScore)) || {};
171
- const withElefaiScore = (m, elefaiScore) => {
170
+ const scoreMetaOf = (m) => (m && typeof m === 'object' && (m.temboryScore || m._temboryScore)) || {};
171
+ const withTemboryScore = (m, temboryScore) => {
172
172
  if (!m || typeof m !== 'object')
173
173
  return m;
174
- return { ...m, elefaiScore };
174
+ return { ...m, temboryScore };
175
175
  };
176
176
  const normalizeFactValue = (value) => String(value || '').replace(/\*\*/g, '').replace(/\s+/g, ' ').replace(/[.,;:]+$/g, '').trim();
177
177
  const profileSourceRank = (source = '') => {
@@ -504,8 +504,8 @@ const deriveEntityTimeline = (profileFacts = {}, graph = [], recentMessages = []
504
504
  }
505
505
  return pruneByLimit(deduped.sort((a, b) => String(a.at || '').localeCompare(String(b.at || ''))), maxItems || 24);
506
506
  };
507
- const RECENT_MESSAGE_MARKER = '__elefai_recent_message_v1__';
508
- const TOOL_HISTORY_MARKER = '__elefai_tool_history_v1__';
507
+ const RECENT_MESSAGE_MARKER = '__tembory_recent_message_v1__';
508
+ const TOOL_HISTORY_MARKER = '__tembory_tool_history_v1__';
509
509
  const encodeRecentMessage = (recent, threadId) => `${RECENT_MESSAGE_MARKER}${safeStringify({
510
510
  role: recent.role || 'user',
511
511
  content: recent.content || '',
@@ -855,21 +855,21 @@ const extractToolCalls = (outputValues = {}) => {
855
855
  const getMemoryStore = (ctx) => {
856
856
  try {
857
857
  const data = ctx.getWorkflowStaticData('global');
858
- data.elefaiBrain = data.elefaiBrain || {};
859
- data.elefaiBrain.toolHistory = data.elefaiBrain.toolHistory || {};
860
- data.elefaiBrain.recentMessages = data.elefaiBrain.recentMessages || {};
861
- data.elefaiBrain.profileFacts = data.elefaiBrain.profileFacts || {};
862
- data.elefaiBrain.workingMemory = data.elefaiBrain.workingMemory || {};
863
- data.elefaiBrain.decisionState = data.elefaiBrain.decisionState || {};
864
- data.elefaiBrain.memoryCompression = data.elefaiBrain.memoryCompression || {};
865
- return data.elefaiBrain;
858
+ data.tembory = data.tembory || {};
859
+ data.tembory.toolHistory = data.tembory.toolHistory || {};
860
+ data.tembory.recentMessages = data.tembory.recentMessages || {};
861
+ data.tembory.profileFacts = data.tembory.profileFacts || {};
862
+ data.tembory.workingMemory = data.tembory.workingMemory || {};
863
+ data.tembory.decisionState = data.tembory.decisionState || {};
864
+ data.tembory.memoryCompression = data.tembory.memoryCompression || {};
865
+ return data.tembory;
866
866
  }
867
867
  catch {
868
- global.__elefaiBrainMemory = global.__elefaiBrainMemory || { toolHistory: {}, recentMessages: {}, profileFacts: {}, workingMemory: {}, decisionState: {}, memoryCompression: {} };
869
- global.__elefaiBrainMemory.workingMemory = global.__elefaiBrainMemory.workingMemory || {};
870
- global.__elefaiBrainMemory.decisionState = global.__elefaiBrainMemory.decisionState || {};
871
- global.__elefaiBrainMemory.memoryCompression = global.__elefaiBrainMemory.memoryCompression || {};
872
- return global.__elefaiBrainMemory;
868
+ global.__temboryMemory = global.__temboryMemory || { toolHistory: {}, recentMessages: {}, profileFacts: {}, workingMemory: {}, decisionState: {}, memoryCompression: {} };
869
+ global.__temboryMemory.workingMemory = global.__temboryMemory.workingMemory || {};
870
+ global.__temboryMemory.decisionState = global.__temboryMemory.decisionState || {};
871
+ global.__temboryMemory.memoryCompression = global.__temboryMemory.memoryCompression || {};
872
+ return global.__temboryMemory;
873
873
  }
874
874
  };
875
875
  const namespacePart = (value) => String(value || '').trim().replace(/\s+/g, '_').replace(/[:|]+/g, '-');
@@ -906,6 +906,7 @@ const applyOperationalPreset = (advanced = {}) => {
906
906
  recentMessagesLastN: 8,
907
907
  },
908
908
  productionBalanced: {
909
+ compactStateSections: true,
909
910
  includeContextHeader: true,
910
911
  includeSummary: true,
911
912
  includeScores: false,
@@ -928,8 +929,11 @@ const applyOperationalPreset = (advanced = {}) => {
928
929
  lastN: 8,
929
930
  toolHistoryLastN: 10,
930
931
  recentMessagesLastN: 6,
932
+ vectorMemoryMaxChars: 360,
933
+ contextMaxChars: 10000,
931
934
  },
932
935
  productionCheap: {
936
+ compactStateSections: true,
933
937
  includeContextHeader: true,
934
938
  includeSummary: false,
935
939
  includeScores: false,
@@ -952,6 +956,8 @@ const applyOperationalPreset = (advanced = {}) => {
952
956
  lastN: 4,
953
957
  toolHistoryLastN: 5,
954
958
  recentMessagesLastN: 4,
959
+ vectorMemoryMaxChars: 260,
960
+ contextMaxChars: 7000,
955
961
  },
956
962
  productionNano: {
957
963
  compactForAgent: true,
@@ -1328,7 +1334,7 @@ const deriveContextHealth = ({ userId = '', project = '', vectorMemories = [], r
1328
1334
  const missing = Object.entries(checks).filter(([, ok]) => !ok).map(([key]) => key);
1329
1335
  const agenda = operationalState.agenda_state || {};
1330
1336
  return {
1331
- kind: 'elefai.brain.context_health.v1',
1337
+ kind: 'tembory.context_health.v1',
1332
1338
  namespace: userId || null,
1333
1339
  project: project || null,
1334
1340
  quality_score: Math.max(0, Math.min(1000, score)),
@@ -1409,12 +1415,63 @@ const compactToolResult = (result, max = 360) => {
1409
1415
  return truncate(text, max);
1410
1416
  };
1411
1417
  const compactToolHistoryForAgent = (toolHistory = [], maxItems = 6, includeResults = true) => pruneByLimit(toolHistory || [], maxItems).map((tool) => ({
1418
+ id: tool.call_id || tool.id,
1412
1419
  name: tool.name,
1413
1420
  status: tool.ok === false ? 'failed' : 'ok',
1414
1421
  at: tool.at,
1415
1422
  input: truncate(String(tool.input || ''), 180) || undefined,
1416
1423
  result: includeResults ? compactToolResult(tool.result, 360) : undefined,
1417
1424
  }));
1425
+ const cleanContextValue = (value) => {
1426
+ if (Array.isArray(value)) {
1427
+ return value.map(cleanContextValue).filter((item) => item !== undefined && item !== null && !(Array.isArray(item) && item.length === 0) && !(typeof item === 'object' && !Array.isArray(item) && Object.keys(item).length === 0));
1428
+ }
1429
+ if (!value || typeof value !== 'object')
1430
+ return value === '' ? undefined : value;
1431
+ const out = {};
1432
+ for (const [key, item] of Object.entries(value)) {
1433
+ const cleaned = cleanContextValue(item);
1434
+ if (cleaned === undefined || cleaned === null)
1435
+ continue;
1436
+ if (Array.isArray(cleaned) && cleaned.length === 0)
1437
+ continue;
1438
+ if (typeof cleaned === 'object' && !Array.isArray(cleaned) && Object.keys(cleaned).length === 0)
1439
+ continue;
1440
+ out[key] = cleaned;
1441
+ }
1442
+ return out;
1443
+ };
1444
+ const compactWorkingMemoryForAgent = (memory = {}) => cleanContextValue({
1445
+ current_goal: memory.current_goal,
1446
+ current_task: memory.current_task,
1447
+ last_user_intent: memory.last_user_intent,
1448
+ last_user_message: truncate(memory.last_user_message, 220),
1449
+ active_entities: memory.active_entities,
1450
+ open_decisions: memory.open_decisions,
1451
+ last_error: memory.last_error,
1452
+ next_expected_action: memory.next_expected_action,
1453
+ updated_at: memory.updated_at,
1454
+ });
1455
+ const compactDecisionStateForAgent = (state = {}) => cleanContextValue({
1456
+ current_intent: state.current_intent,
1457
+ active_decisions: (state.active_decisions || []).slice(-4).map((decision) => cleanContextValue({
1458
+ id: decision.id,
1459
+ status: decision.status,
1460
+ decision: truncate(decision.decision || decision.summary || decision.reason, 260),
1461
+ tool: decision.tool || decision.next_tool || decision.required_tool,
1462
+ reservation_id: decision.reservation_id,
1463
+ confirmation_id: decision.confirmation_id,
1464
+ at: decision.at || decision.updated_at,
1465
+ })),
1466
+ do_not_repeat_tools: state.do_not_repeat_tools,
1467
+ agenda_decision_state: state.agenda_decision_state,
1468
+ latest_tool: state.latest_tool ? cleanContextValue({
1469
+ name: state.latest_tool.name,
1470
+ status: state.latest_tool.status || (state.latest_tool.ok === false ? 'failed' : 'ok'),
1471
+ at: state.latest_tool.at,
1472
+ }) : undefined,
1473
+ conflict_policy: state.conflict_policy,
1474
+ });
1418
1475
  const compactOperationalStateForAgent = (state = {}) => {
1419
1476
  const agenda = state.agenda_state || {};
1420
1477
  return {
@@ -1444,6 +1501,21 @@ const compactMemoryCompressionForAgent = (compression = {}) => ({
1444
1501
  } : undefined,
1445
1502
  active_memory_count: ((compression.workflow_summary || {}).active_memory_count) || 0,
1446
1503
  });
1504
+ const compactActionLedgerForAgent = (ledger = [], maxItems = 6, includeResults = true) => pruneByLimit(ledger || [], maxItems).map((item) => cleanContextValue({
1505
+ action_id: item.action_id,
1506
+ kind: item.kind,
1507
+ tool: item.tool || item.name,
1508
+ status: item.status,
1509
+ at: item.at,
1510
+ result: includeResults ? compactToolResult(item.result, 260) : undefined,
1511
+ }));
1512
+ const compactEntityTimelineForAgent = (timeline = [], maxItems = 8) => pruneByLimit(timeline || [], maxItems).map((item) => cleanContextValue({
1513
+ entity: item.entity || item.source || item.name,
1514
+ event: truncate(item.event || item.fact || item.relation || item.kind || item.value, 220),
1515
+ target: item.target,
1516
+ at: item.at,
1517
+ source: item.source_type || item.source,
1518
+ }));
1447
1519
  const compactVectorMemoriesForAgent = (vectorMemories = [], toolHistory = [], maxItems = 3) => {
1448
1520
  const hasStructuredTools = Array.isArray(toolHistory) && toolHistory.length > 0;
1449
1521
  return (vectorMemories || [])
@@ -1460,7 +1532,7 @@ const contextSizeOfMessages = (messages = []) => {
1460
1532
  const chars = perMessage.reduce((sum, item) => sum + item.chars, 0);
1461
1533
  return { chars, approx_tokens: approxTokenCount((messages || []).map((m) => m.content || '').join('\n')), messages: perMessage };
1462
1534
  };
1463
- const wrapElefaiMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1535
+ const wrapTemboryMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1464
1536
  get(target, prop) {
1465
1537
  if (prop === 'loadMemoryVariables') {
1466
1538
  return async (values = {}) => {
@@ -1469,7 +1541,7 @@ const wrapElefaiMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1469
1541
  ]);
1470
1542
  try {
1471
1543
  const response = await target.loadMemoryVariables(values);
1472
- const cacheHit = Boolean(response.elefaiBrainDiagnostics && response.elefaiBrainDiagnostics.cacheHit);
1544
+ const cacheHit = Boolean(response.temboryDiagnostics && response.temboryDiagnostics.cacheHit);
1473
1545
  const chatHistory = cacheHit
1474
1546
  ? [{ cached: true, messages: Array.isArray(response[memoryKey] || response.chatHistory) ? (response[memoryKey] || response.chatHistory).length : 0 }]
1475
1547
  : snapshotJson(response[memoryKey] || response.chatHistory || []);
@@ -1479,23 +1551,23 @@ const wrapElefaiMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1479
1551
  action: 'loadMemoryVariables',
1480
1552
  cached: cacheHit || undefined,
1481
1553
  chatHistory,
1482
- context: response.elefaiBrainContext,
1483
- contextText: response.elefaiBrainContextText,
1484
- summary: response.elefaiBrainSummary,
1485
- connectedModelSummary: response.elefaiBrainConnectedModelSummary,
1486
- workingMemory: response.elefaiBrainWorkingMemory,
1487
- decisionState: response.elefaiBrainDecisionState,
1488
- memoryCompression: response.elefaiBrainMemoryCompression,
1489
- profileFacts: response.elefaiBrainProfileFacts,
1490
- operationalState: response.elefaiBrainOperationalState,
1491
- actionLedger: response.elefaiBrainActionLedger,
1492
- entityTimeline: response.elefaiBrainEntityTimeline,
1493
- vectorMemories: response.elefaiBrainVectorMemories,
1494
- graph: response.elefaiBrainGraph,
1495
- recentMessages: response.elefaiBrainRecentMessages,
1496
- recentHighlights: response.elefaiBrainRecentHighlights,
1497
- toolHistory: response.elefaiBrainToolHistory,
1498
- diagnostics: response.elefaiBrainDiagnostics,
1554
+ context: response.temboryContext,
1555
+ contextText: response.temboryContextText,
1556
+ summary: response.temborySummary,
1557
+ connectedModelSummary: response.temboryConnectedModelSummary,
1558
+ workingMemory: response.temboryWorkingMemory,
1559
+ decisionState: response.temboryDecisionState,
1560
+ memoryCompression: response.temboryMemoryCompression,
1561
+ profileFacts: response.temboryProfileFacts,
1562
+ operationalState: response.temboryOperationalState,
1563
+ actionLedger: response.temboryActionLedger,
1564
+ entityTimeline: response.temboryEntityTimeline,
1565
+ vectorMemories: response.temboryVectorMemories,
1566
+ graph: response.temboryGraph,
1567
+ recentMessages: response.temboryRecentMessages,
1568
+ recentHighlights: response.temboryRecentHighlights,
1569
+ toolHistory: response.temboryToolHistory,
1570
+ diagnostics: response.temboryDiagnostics,
1499
1571
  },
1500
1572
  }],
1501
1573
  ]);
@@ -1534,12 +1606,13 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1534
1606
  const includeSummary = adv.includeSummary !== false;
1535
1607
  const includeScores = adv.includeScores !== false;
1536
1608
  const compactForAgent = Boolean(adv.compactForAgent);
1609
+ const compactStateSections = Boolean(adv.compactStateSections);
1537
1610
  const sections = [];
1538
1611
  if (includeHeader) {
1539
1612
  sections.push({
1540
1613
  section: 'context_header',
1541
1614
  title: 'Tembory context',
1542
- value: compactForAgent
1615
+ value: compactForAgent || compactStateSections
1543
1616
  ? '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.'
1544
1617
  : '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.',
1545
1618
  });
@@ -1588,7 +1661,7 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1588
1661
  why_null: adv.includeMemoryCompression === false ? 'memory compression disabled' : undefined,
1589
1662
  });
1590
1663
  const audit = {
1591
- kind: 'elefai.brain.context.v1',
1664
+ kind: 'tembory.context.v1',
1592
1665
  meta: { user_id: userId, payloadFormat, query, compact_for_agent: true },
1593
1666
  sections,
1594
1667
  diagnostics,
@@ -1609,13 +1682,15 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1609
1682
  return [{ role: 'system', content: truncate(sections.map(renderCompactSection).join('\n\n'), Number(adv.contextMaxChars || 6000)) }];
1610
1683
  }
1611
1684
  if (includeSummary) {
1612
- const summary = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 360)).filter(Boolean);
1685
+ const summary = compactStateSections
1686
+ ? compactVectorMemoriesForAgent(vectorMemories, toolHistory, Number(adv.summaryMaxFacts || 4))
1687
+ : vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 360)).filter(Boolean);
1613
1688
  sections.push({ section: 'summary', title: 'Summary', value: summary.length ? summary : null, why_null: summary.length ? undefined : 'no vector memories to summarize' });
1614
1689
  }
1615
1690
  sections.push({
1616
1691
  section: 'working_memory',
1617
1692
  title: 'Working memory',
1618
- value: adv.includeWorkingMemory === false ? null : (workingMemory || {}),
1693
+ value: adv.includeWorkingMemory === false ? null : (compactStateSections ? compactWorkingMemoryForAgent(workingMemory || {}) : (workingMemory || {})),
1619
1694
  why_null: adv.includeWorkingMemory === false ? 'working memory disabled' : undefined,
1620
1695
  });
1621
1696
  sections.push({
@@ -1639,31 +1714,31 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1639
1714
  sections.push({
1640
1715
  section: 'decision_state',
1641
1716
  title: 'Decision state',
1642
- value: adv.includeDecisionState === false ? null : (decisionState || {}),
1717
+ value: adv.includeDecisionState === false ? null : (compactStateSections ? compactDecisionStateForAgent(decisionState || {}) : (decisionState || {})),
1643
1718
  why_null: adv.includeDecisionState === false ? 'decision state disabled' : undefined,
1644
1719
  });
1645
1720
  sections.push({
1646
1721
  section: 'operational_state',
1647
1722
  title: 'Operational state',
1648
- value: adv.includeOperationalState === false ? null : operationalState,
1723
+ value: adv.includeOperationalState === false ? null : (compactStateSections ? compactOperationalStateForAgent(operationalState || {}) : operationalState),
1649
1724
  why_null: adv.includeOperationalState === false ? 'operational state disabled' : undefined,
1650
1725
  });
1651
1726
  sections.push({
1652
1727
  section: 'action_ledger',
1653
1728
  title: 'Action ledger',
1654
- value: adv.includeActionLedger === false ? null : (actionLedger || []),
1729
+ value: adv.includeActionLedger === false ? null : (compactStateSections ? compactActionLedgerForAgent(actionLedger || [], adv.actionLedgerMaxItems || 6, adv.includeToolResults !== false) : (actionLedger || [])),
1655
1730
  why_null: adv.includeActionLedger === false ? 'action ledger disabled' : undefined,
1656
1731
  });
1657
1732
  sections.push({
1658
1733
  section: 'entity_timeline',
1659
1734
  title: 'Entity timeline',
1660
- value: adv.includeEntityTimeline === false ? null : (entityTimeline || []),
1735
+ value: adv.includeEntityTimeline === false ? null : (compactStateSections ? compactEntityTimelineForAgent(entityTimeline || [], adv.entityTimelineMaxItems || 8) : (entityTimeline || [])),
1661
1736
  why_null: adv.includeEntityTimeline === false ? 'entity timeline disabled' : undefined,
1662
1737
  });
1663
1738
  sections.push({
1664
1739
  section: 'vector',
1665
1740
  title: 'Vector memories',
1666
- value: vectorMemories.map((m) => {
1741
+ value: compactStateSections ? compactVectorMemoriesForAgent(vectorMemories, toolHistory, Number(adv.maxReturn || adv.topK || 4)) : vectorMemories.map((m) => {
1667
1742
  const scoreMeta = scoreMetaOf(m);
1668
1743
  const rawScore = scoreOf(m);
1669
1744
  return {
@@ -1681,7 +1756,7 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1681
1756
  sections.push({
1682
1757
  section: 'memory_compression',
1683
1758
  title: 'Memory compression',
1684
- value: adv.includeMemoryCompression === false ? null : (memoryCompression || {}),
1759
+ value: adv.includeMemoryCompression === false ? null : (compactStateSections ? compactMemoryCompressionForAgent(memoryCompression || {}) : (memoryCompression || {})),
1685
1760
  why_null: adv.includeMemoryCompression === false ? 'memory compression disabled' : undefined,
1686
1761
  });
1687
1762
  sections.push({
@@ -1693,7 +1768,11 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1693
1768
  sections.push({
1694
1769
  section: 'recent_messages',
1695
1770
  title: 'Recent messages',
1696
- value: recentMessages || [],
1771
+ value: compactStateSections ? pruneByLimit(recentMessages || [], adv.recentMessagesLastN || 4).map((message) => cleanContextValue({
1772
+ role: message.role,
1773
+ content: truncate(message.content, 260),
1774
+ at: message.at,
1775
+ })) : recentMessages || [],
1697
1776
  });
1698
1777
  sections.push({
1699
1778
  section: 'recent_highlights',
@@ -1704,18 +1783,18 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1704
1783
  section: 'tool_history',
1705
1784
  title: 'Tool history',
1706
1785
  value: {
1707
- enabled: adv.includeToolHistory !== false,
1708
- items: adv.includeToolHistory === false ? [] : toolHistory.map((tool) => ({
1709
- id: tool.id,
1710
- turnId: tool.turnId,
1711
- sequence: tool.sequence,
1786
+ enabled: adv.includeToolHistory !== false,
1787
+ items: adv.includeToolHistory === false ? [] : (compactStateSections ? compactToolHistoryForAgent(toolHistory, adv.toolHistoryLastN || 8, adv.includeToolResults !== false) : toolHistory.map((tool) => ({
1788
+ id: tool.id,
1789
+ turnId: tool.turnId,
1790
+ sequence: tool.sequence,
1712
1791
  name: tool.name,
1713
1792
  input: tool.input,
1714
1793
  ok: tool.ok,
1715
1794
  at: tool.at,
1716
- source: tool.source,
1717
- result: adv.includeToolResults === false ? undefined : tool.result,
1718
- })),
1795
+ source: tool.source,
1796
+ result: adv.includeToolResults === false ? undefined : tool.result,
1797
+ }))),
1719
1798
  },
1720
1799
  });
1721
1800
  if (adv.includeDiagnostics) {
@@ -1726,7 +1805,7 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1726
1805
  });
1727
1806
  }
1728
1807
  const audit = {
1729
- kind: 'elefai.brain.context.v1',
1808
+ kind: 'tembory.context.v1',
1730
1809
  meta: { user_id: userId, payloadFormat, query },
1731
1810
  sections,
1732
1811
  diagnostics,
@@ -1740,17 +1819,32 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1740
1819
  return `## ${section.title}\n${section.value.length ? section.value.map((item) => typeof item === 'string' ? `- ${item}` : `- ${truncate(safeStringify(item), 900)}`).join('\n') : '(empty)'}`;
1741
1820
  return `## ${section.title}\n${truncate(safeStringify(section.value), 1600)}`;
1742
1821
  };
1822
+ const sectionIsEmpty = (section) => {
1823
+ if (!compactStateSections)
1824
+ return false;
1825
+ if (section.section === 'context_header' || section.section === 'tool_guard' || section.section === 'working_memory' || section.section === 'decision_state' || section.section === 'operational_state' || section.section === 'tool_history')
1826
+ return false;
1827
+ if (section.value === null || section.value === undefined)
1828
+ return true;
1829
+ if (Array.isArray(section.value) && section.value.length === 0)
1830
+ return true;
1831
+ if (typeof section.value === 'object' && !Array.isArray(section.value) && Object.keys(section.value).length === 0)
1832
+ return true;
1833
+ return false;
1834
+ };
1835
+ const renderableSections = compactStateSections ? sections.filter((section) => !sectionIsEmpty(section)) : sections;
1743
1836
  if (payloadFormat === 'auditJson') {
1744
1837
  return [{ role: 'system', content: JSON.stringify(audit, null, 2) }];
1745
1838
  }
1746
1839
  if (payloadFormat === 'auditBlocks') {
1747
- return sections.map((section) => ({ role: 'system', content: renderSection(section) }));
1840
+ return renderableSections.map((section) => ({ role: 'system', content: renderSection(section) }));
1748
1841
  }
1749
- const text = sections.map(renderSection).join('\n\n');
1842
+ const text = renderableSections.map(renderSection).join('\n\n');
1843
+ const maxChars = Number(adv.contextMaxChars || 16000);
1750
1844
  if (payloadFormat === 'auditText' || payloadFormat === 'singleSystemMessage' || payloadFormat === 'structured') {
1751
- return [{ role: 'system', content: truncate(text, 16000) }];
1845
+ return [{ role: 'system', content: truncate(text, maxChars) }];
1752
1846
  }
1753
- return [{ role: 'system', content: truncate(text, 16000) }];
1847
+ return [{ role: 'system', content: truncate(text, maxChars) }];
1754
1848
  };
1755
1849
  class Mem0Memory {
1756
1850
  constructor() {
@@ -1898,7 +1992,8 @@ class Mem0Memory {
1898
1992
  { 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'] } } },
1899
1993
  { displayName: 'Incluir Cabeçalho de Contexto', name: 'includeContextHeader', type: 'boolean', default: true },
1900
1994
  { 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.' },
1901
- { displayName: 'Máximo de Caracteres do Contexto', name: 'contextMaxChars', type: 'number', default: 6000, description: 'Limite final aplicado quando Compactar Contexto Para Agente estiver ativo.' },
1995
+ { 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
+ { 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.' },
1902
1997
  { displayName: 'Incluir Resumo', name: 'includeSummary', type: 'boolean', default: true },
1903
1998
  { displayName: 'Máximo de Fatos no Resumo', name: 'summaryMaxFacts', type: 'number', default: 4 },
1904
1999
  { displayName: 'Incluir Scores', name: 'includeScores', type: 'boolean', default: true },
@@ -1968,24 +2063,24 @@ class Mem0Memory {
1968
2063
  if (loadCache.has(cacheKey)) {
1969
2064
  result = snapshotJson(loadCache.get(cacheKey));
1970
2065
  result.response = result.response || {};
1971
- result.response.elefaiBrainDiagnostics = {
1972
- ...(result.response.elefaiBrainDiagnostics || {}),
2066
+ result.response.temboryDiagnostics = {
2067
+ ...(result.response.temboryDiagnostics || {}),
1973
2068
  cacheHit: true,
1974
2069
  cacheScope: 'supplyData.loadMemoryVariables',
1975
2070
  };
1976
- if (result.response.elefaiBrainContext) {
1977
- result.response.elefaiBrainContext = {
1978
- kind: result.response.elefaiBrainContext.kind,
1979
- userId: result.response.elefaiBrainContext.userId,
1980
- project: result.response.elefaiBrainContext.project,
1981
- query: result.response.elefaiBrainContext.query,
1982
- retrievalMode: result.response.elefaiBrainContext.retrievalMode,
1983
- payloadFormat: result.response.elefaiBrainContext.payloadFormat,
1984
- contextQualityScore: result.response.elefaiBrainContext.contextQualityScore,
2071
+ if (result.response.temboryContext) {
2072
+ result.response.temboryContext = {
2073
+ kind: result.response.temboryContext.kind,
2074
+ userId: result.response.temboryContext.userId,
2075
+ project: result.response.temboryContext.project,
2076
+ query: result.response.temboryContext.query,
2077
+ retrievalMode: result.response.temboryContext.retrievalMode,
2078
+ payloadFormat: result.response.temboryContext.payloadFormat,
2079
+ contextQualityScore: result.response.temboryContext.contextQualityScore,
1985
2080
  cacheHit: true,
1986
2081
  };
1987
2082
  }
1988
- delete result.response.elefaiBrainContextText;
2083
+ delete result.response.temboryContextText;
1989
2084
  }
1990
2085
  else {
1991
2086
  result = await Mem0Memory.prototype.loadMemoryVariablesForItem.call(this, itemIndex, inputValues);
@@ -2016,7 +2111,7 @@ class Mem0Memory {
2016
2111
  await Mem0Memory.prototype.saveContextForItem.call(this, itemIndex, inputValues, outputValues);
2017
2112
  },
2018
2113
  };
2019
- return { response: wrapElefaiMemory(memory, this, memoryKey) };
2114
+ return { response: wrapTemboryMemory(memory, this, memoryKey) };
2020
2115
  }
2021
2116
  async saveMessagesForItem(itemIndex, messages = []) {
2022
2117
  const inputValues = {};
@@ -2427,7 +2522,7 @@ class Mem0Memory {
2427
2522
  }
2428
2523
  ranked = selected;
2429
2524
  }
2430
- const finalMemories = ranked.slice(0, maxReturn).map((r) => withElefaiScore(r.m, {
2525
+ const finalMemories = ranked.slice(0, maxReturn).map((r) => withTemboryScore(r.m, {
2431
2526
  semanticScore: r.semanticScore,
2432
2527
  recencyScore: r.recencyScore,
2433
2528
  hybridScore: r.hybrid,
@@ -2437,7 +2532,7 @@ class Mem0Memory {
2437
2532
  payload = finalMemories.map((m) => { var _a, _b; return ({ role: 'system', content: (_b = (_a = m.memory) !== null && _a !== void 0 ? _a : m.text) !== null && _b !== void 0 ? _b : JSON.stringify(m) }); });
2438
2533
  }
2439
2534
  else {
2440
- vectorMemories = semMemories.map((m) => withElefaiScore(m, {
2535
+ vectorMemories = semMemories.map((m) => withTemboryScore(m, {
2441
2536
  semanticScore: scoreOf(m),
2442
2537
  source: 'semantic',
2443
2538
  }));
@@ -2645,7 +2740,7 @@ class Mem0Memory {
2645
2740
  const summary = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 360)).filter(Boolean);
2646
2741
  const contextText = payload.map((message) => String(message.content || '')).join('\n\n');
2647
2742
  const audit = {
2648
- kind: 'elefai.brain.context.v1',
2743
+ kind: 'tembory.context.v1',
2649
2744
  userId: key,
2650
2745
  project: project || undefined,
2651
2746
  query,
@@ -2716,25 +2811,25 @@ class Mem0Memory {
2716
2811
  return {
2717
2812
  response: {
2718
2813
  [memoryKey]: payload,
2719
- elefaiBrainContext: audit,
2720
- elefaiBrainContextText: contextText,
2721
- elefaiBrainSummary: summary,
2722
- elefaiBrainConnectedModelSummary: connectedModelSummary,
2723
- elefaiBrainContextHealth: contextHealth,
2724
- elefaiBrainContextQualityScore: contextHealth.quality_score,
2725
- elefaiBrainWorkingMemory: workingMemory,
2726
- elefaiBrainDecisionState: decisionState,
2727
- elefaiBrainMemoryCompression: memoryCompression,
2728
- elefaiBrainProfileFacts: renderProfileFacts(profileFacts),
2729
- elefaiBrainOperationalState: operationalState,
2730
- elefaiBrainActionLedger: actionLedger,
2731
- elefaiBrainEntityTimeline: entityTimeline,
2732
- elefaiBrainVectorMemories: normalizedVectorMemories,
2733
- elefaiBrainGraph: graph,
2734
- elefaiBrainRecentMessages: recentMessages,
2735
- elefaiBrainRecentHighlights: highlights,
2736
- elefaiBrainToolHistory: audit.toolHistory,
2737
- elefaiBrainDiagnostics: diagnostics,
2814
+ temboryContext: audit,
2815
+ temboryContextText: contextText,
2816
+ temborySummary: summary,
2817
+ temboryConnectedModelSummary: connectedModelSummary,
2818
+ temboryContextHealth: contextHealth,
2819
+ temboryContextQualityScore: contextHealth.quality_score,
2820
+ temboryWorkingMemory: workingMemory,
2821
+ temboryDecisionState: decisionState,
2822
+ temboryMemoryCompression: memoryCompression,
2823
+ temboryProfileFacts: renderProfileFacts(profileFacts),
2824
+ temboryOperationalState: operationalState,
2825
+ temboryActionLedger: actionLedger,
2826
+ temboryEntityTimeline: entityTimeline,
2827
+ temboryVectorMemories: normalizedVectorMemories,
2828
+ temboryGraph: graph,
2829
+ temboryRecentMessages: recentMessages,
2830
+ temboryRecentHighlights: highlights,
2831
+ temboryToolHistory: audit.toolHistory,
2832
+ temboryDiagnostics: diagnostics,
2738
2833
  },
2739
2834
  };
2740
2835
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
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",
@@ -42,20 +42,20 @@ const toolHistory = [
42
42
  ok: true,
43
43
  at: '2026-05-14T05:34:34.017Z',
44
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"}}]',
45
+ result: '[{"args":{"tool":"agenda_pre_reservar_horario","reservation_id":"RES-TEMBORY-LAB-001","selected_from_message":"dia 13","status":"pre_reserved","next_step":"confirmar_agendamento"}}]',
46
46
  },
47
47
  ];
48
48
 
49
49
  const vectorMemories = [
50
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.',
51
+ memory: '[Used tools: Tool: agenda_pre_reservar_horario, Input: , Result: [{"args":{"tool":"agenda_pre_reservar_horario","reservation_id":"RES-TEMBORY-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
52
  created_at: '2026-05-14T05:34:35.373020Z',
53
- elefaiScore: { semanticScore: 0.71, recencyScore: 0.99, hybridScore: 0.81, source: 'semantic' },
53
+ temboryScore: { semanticScore: 0.71, recencyScore: 0.99, hybridScore: 0.81, source: 'semantic' },
54
54
  },
55
55
  {
56
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
57
  created_at: '2026-05-14T05:34:14.717988Z',
58
- elefaiScore: { semanticScore: 0.7, recencyScore: 0.99, hybridScore: 0.8, source: 'semantic' },
58
+ temboryScore: { semanticScore: 0.7, recencyScore: 0.99, hybridScore: 0.8, source: 'semantic' },
59
59
  },
60
60
  { memory: 'Oi! Como posso ajudar?', created_at: '2026-05-14T05:33:47.194630Z' },
61
61
  { memory: 'oi', created_at: '2026-05-14T05:33:47.194355Z' },
@@ -87,7 +87,7 @@ const common = {
87
87
  toolHistory,
88
88
  highlights: [
89
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"}}]',
90
+ 'tool agenda_pre_reservar_horario: [{"args":{"reservation_id":"RES-TEMBORY-LAB-001","status":"pre_reserved"}}]',
91
91
  ],
92
92
  graph: [],
93
93
  diagnostics: {},
@@ -106,7 +106,7 @@ for (const [name, adv] of modes) {
106
106
  size: core.contextSizeOfMessages(messages),
107
107
  has_next_expected_action: /next_expected_action/.test(text),
108
108
  has_do_not_repeat_tools: /do_not_repeat_tools/.test(text),
109
- has_reservation_id: /RES-ELEFAI-LAB-001/.test(text),
109
+ has_reservation_id: /RES-TEMBORY-LAB-001/.test(text),
110
110
  has_available_slots: /available_slots/.test(text),
111
111
  has_duplicate_calling_text: /Calling agenda_pre_reservar_horario/.test(text),
112
112
  }, null, 2));