n8n-nodes-tembory 1.1.24 → 1.1.26

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.
@@ -1723,6 +1723,56 @@ const buildCurrentTurnFocus = ({ query = '', recentMessages = [], operationalSta
1723
1723
  });
1724
1724
  return focus;
1725
1725
  };
1726
+ const turnBriefGuidanceForIntent = (intent = '') => {
1727
+ if (intent === 'conversation_recall')
1728
+ return 'Answer from the chronological conversation frame. Do not call tools for recall-only requests.';
1729
+ if (intent === 'operational_status_question')
1730
+ return 'Answer from tool_state, tool_history and action_ledger. Do not call tools just to inspect prior calls.';
1731
+ if (['tool_action_candidate', 'selection_or_slot', 'affirm', 'commit_or_continue'].includes(intent))
1732
+ return 'May require a tool. Use prompt policy. Prior tools are evidence only; do not claim new side effects without a current required tool call.';
1733
+ if (intent === 'profile_update')
1734
+ return 'Likely stable profile data. Save useful facts and continue; call tools only when prompt requires.';
1735
+ return 'Continue according to the agent prompt using conversation, tool history and operational state as read-only context.';
1736
+ };
1737
+ const buildTurnBriefForAgent = ({ query = '', recentMessages = [], toolHistory = [], operationalState = {}, workingMemory = {}, decisionState = {}, diagnostics = {} }) => {
1738
+ const currentIntent = (decisionState || {}).current_intent || (workingMemory || {}).last_user_intent || inferUserIntent(query, recentMessages);
1739
+ const toolState = (operationalState || {}).tool_state || {};
1740
+ const lastTool = (toolState.last_successful_tool || (operationalState || {}).last_tool || (Array.isArray(toolHistory) && toolHistory.length ? toolHistory[toolHistory.length - 1] : null));
1741
+ const toolNames = Array.from(new Set([]
1742
+ .concat(toolState.names || [])
1743
+ .concat((Array.isArray(toolHistory) ? toolHistory : []).map((tool) => tool && (tool.name || tool.tool_name || tool.tool)).filter(Boolean))));
1744
+ const userMessages = (recentMessages || []).filter((msg) => /^(user|human)$/i.test(String(msg.role || '')));
1745
+ const lastSave = (diagnostics || {}).captureState || {};
1746
+ return cleanContextValue({
1747
+ current_intent: currentIntent,
1748
+ current_request: truncate(query || ((userMessages[userMessages.length - 1] || {}).content || ''), 300),
1749
+ memory_status: {
1750
+ messages: Array.isArray(recentMessages) ? recentMessages.length : 0,
1751
+ users: userMessages.length,
1752
+ tools: Array.isArray(toolHistory) ? toolHistory.length : 0,
1753
+ last_save_at: lastSave.last_save_at,
1754
+ last_save_tool_calls_captured: lastSave.last_save_tool_calls_captured,
1755
+ },
1756
+ tool_names: toolNames.slice(0, 8),
1757
+ last_tool: lastTool ? {
1758
+ name: lastTool.name || lastTool.tool_name || lastTool.tool,
1759
+ status: lastTool.status || (lastTool.ok === false ? 'failed' : 'ok'),
1760
+ at: lastTool.at || lastTool.timestamp,
1761
+ } : undefined,
1762
+ do_not_repeat_tools: ((decisionState || {}).do_not_repeat_tools || []).slice(0, 12),
1763
+ guidance: turnBriefGuidanceForIntent(currentIntent),
1764
+ });
1765
+ };
1766
+ const compactTurnBriefForAgent = (brief = {}) => {
1767
+ const status = brief.memory_status || {};
1768
+ const parts = [
1769
+ brief.current_intent ? `intent=${brief.current_intent}` : '',
1770
+ brief.current_request ? `request=${truncate(brief.current_request, 80)}` : '',
1771
+ status.tools !== undefined ? `tools=${status.tools}` : '',
1772
+ 'prior tools are evidence; new side effects need current tool',
1773
+ ].filter(Boolean);
1774
+ return parts.join('; ');
1775
+ };
1726
1776
  const SIDE_EFFECT_SAFETY_INSTRUCTION = 'Previous tool calls are evidence only. Never tell the user that a new side-effect action is done, confirmed, booked, created, updated, cancelled, sent, charged, assigned, or executed from memory alone. If the current user turn asks to commit a side-effect and the agent prompt requires a tool, call the appropriate tool in the current turn before saying it is done. Use prior tool outputs only to avoid repeating prerequisite lookup tools and to fill inputs for the required tool call.';
1727
1777
  const intentConfidence = (intent = '') => {
1728
1778
  if (['conversation_recall', 'operational_status_question'].includes(intent))
@@ -1789,7 +1839,7 @@ const inferUserIntent = (query = '', recentMessages = []) => {
1789
1839
  return 'selection_or_slot';
1790
1840
  if (hasCommitIntent(text))
1791
1841
  return 'commit_or_continue';
1792
- if (/\b(buscar|busca|criar|cria|atualizar|atualiza|consultar|consulta|reservar|reserva|agendar|agenda|abrir|abre|cancelar|cancela|enviar|envia|gerar|gera|validar|valida|processar|processa|executar|executa)\b/.test(text))
1842
+ if (/\b(buscar|busca|criar|cria|atualizar|atualiza|consultar|consulta|reservar|reserva|agendar|agenda|abrir|abre|cancelar|cancela|enviar|envia|gerar|gera|validar|valida|processar|processa|executar|executa|registrar|registra|salvar|salva|gravar|grava|cadastrar|cadastra|inserir|insere)\b/.test(text))
1793
1843
  return 'tool_action_candidate';
1794
1844
  if (/\b(meu nome|email|telefone|empresa|prefiro|preferencia)\b/.test(text))
1795
1845
  return 'profile_update';
@@ -1809,21 +1859,73 @@ const deriveNextExpectedAction = (intent, operationalState = {}) => {
1809
1859
  return 'continue according to the agent prompt using retrieved context; call tools when the agent prompt or tool policy requires them';
1810
1860
  };
1811
1861
  const isGenericMemoryNextAction = (value = '') => /answer using retrieved context and avoid unnecessary tool calls|continue according to the agent prompt using retrieved context/i.test(String(value || ''));
1862
+ const shouldCarryPreviousGoal = (intent = '', previousGoal = '') => {
1863
+ if (!previousGoal || isGenericMemoryNextAction(previousGoal))
1864
+ return false;
1865
+ return ['general_message', 'profile_update', 'unknown'].includes(intent);
1866
+ };
1812
1867
  const deriveWorkingMemory = ({ query = '', profileFacts = {}, recentMessages = [], toolHistory = [], operationalState = {}, previous = {} }) => {
1813
1868
  const intent = inferUserIntent(query, recentMessages);
1814
- const lastUser = [...(recentMessages || [])].reverse().find((msg) => /^(user|human)$/i.test(String(msg.role || '')));
1869
+ const chronological = sortConversationChronological(recentMessages || []);
1870
+ const userMessages = chronological.filter((msg) => /^(user|human)$/i.test(String(msg.role || '')));
1871
+ const assistantMessages = chronological.filter((msg) => /^(assistant|ai|agent)$/i.test(String(msg.role || '')));
1872
+ const normalizedQuery = normalizeIntentText(query).trim();
1873
+ const lastUser = [...userMessages].reverse().find((msg) => /^(user|human)$/i.test(String(msg.role || '')));
1874
+ const priorUser = [...userMessages].reverse().find((msg) => normalizeIntentText(msg.content).trim() !== normalizedQuery);
1875
+ const lastAssistant = assistantMessages[assistantMessages.length - 1] || null;
1815
1876
  const activeEntities = [];
1816
1877
  for (const key of ['name', 'company', 'email', 'phone']) {
1817
1878
  if (profileFacts && profileFacts[key])
1818
1879
  activeEntities.push({ type: key, value: profileFacts[key] });
1819
1880
  }
1820
1881
  const lastTool = toolHistory && toolHistory.length ? toolHistory[toolHistory.length - 1] : null;
1882
+ const toolState = (operationalState || {}).tool_state || {};
1821
1883
  const nextExpectedAction = deriveNextExpectedAction(intent, operationalState);
1884
+ const carryPreviousGoal = shouldCarryPreviousGoal(intent, previous.current_goal);
1885
+ const currentGoal = carryPreviousGoal ? previous.current_goal : nextExpectedAction;
1886
+ const toolNames = Array.from(new Set((toolHistory || []).map((tool) => tool.name || tool.tool_name || tool.tool).filter(Boolean)));
1887
+ const currentTurnMayNeedTool = ['affirm', 'commit_or_continue', 'tool_action_candidate', 'selection_or_slot'].includes(intent);
1822
1888
  return {
1823
- current_goal: previous.current_goal && !isGenericMemoryNextAction(previous.current_goal) ? previous.current_goal : nextExpectedAction,
1889
+ current_goal: currentGoal,
1890
+ current_goal_reason: carryPreviousGoal ? 'carried_from_previous_non_generic_goal' : 'derived_from_current_turn_intent',
1824
1891
  current_task: nextExpectedAction,
1825
1892
  last_user_intent: intent,
1826
1893
  last_user_message: lastUser ? truncate(lastUser.content, 500) : truncate(query, 500),
1894
+ conversation_digest: cleanContextValue({
1895
+ messages: chronological.length,
1896
+ user_messages: userMessages.length,
1897
+ assistant_messages: assistantMessages.length,
1898
+ current_user: truncate(query, 220),
1899
+ prior_user: priorUser ? { content: truncate(priorUser.content, 180), at: priorUser.at } : undefined,
1900
+ last_assistant: lastAssistant ? { content: truncate(lastAssistant.content, 180), at: lastAssistant.at } : undefined,
1901
+ }),
1902
+ tool_digest: cleanContextValue({
1903
+ total: Array.isArray(toolHistory) ? toolHistory.length : 0,
1904
+ ok: (operationalState || {}).tool_counts?.ok,
1905
+ failed: (operationalState || {}).tool_counts?.failed,
1906
+ names: toolNames.slice(0, 12),
1907
+ last_successful: toolState.last_successful_tool ? {
1908
+ name: toolState.last_successful_tool.name,
1909
+ at: toolState.last_successful_tool.at,
1910
+ status: toolState.last_successful_tool.status || 'ok',
1911
+ } : undefined,
1912
+ failed_by_name: toolState.failed_by_name,
1913
+ }),
1914
+ turn_flags: cleanContextValue({
1915
+ recall_only: intent === 'conversation_recall',
1916
+ status_question: intent === 'operational_status_question',
1917
+ current_turn_may_need_tool: currentTurnMayNeedTool,
1918
+ has_prior_tools: Array.isArray(toolHistory) && toolHistory.length > 0,
1919
+ has_profile_facts: activeEntities.length > 0,
1920
+ carry_previous_goal: carryPreviousGoal,
1921
+ }),
1922
+ context_sources: cleanContextValue({
1923
+ conversation: chronological.length > 0,
1924
+ tool_history: Array.isArray(toolHistory) && toolHistory.length > 0,
1925
+ operational_state: Boolean((operationalState || {}).tool_state || (operationalState || {}).tool_counts),
1926
+ profile_facts: activeEntities.length > 0,
1927
+ }),
1928
+ agent_guidance: nextExpectedAction,
1827
1929
  active_entities: activeEntities,
1828
1930
  open_decisions: previous.open_decisions || [],
1829
1931
  last_error: lastTool && lastTool.ok === false ? { tool: lastTool.name, at: lastTool.at, result: lastTool.result } : null,
@@ -2098,9 +2200,15 @@ const cleanContextValue = (value) => {
2098
2200
  };
2099
2201
  const compactWorkingMemoryForAgent = (memory = {}) => cleanContextValue({
2100
2202
  current_goal: memory.current_goal,
2203
+ current_goal_reason: memory.current_goal_reason,
2101
2204
  current_task: memory.current_task,
2102
2205
  last_user_intent: memory.last_user_intent,
2103
2206
  last_user_message: truncate(memory.last_user_message, 220),
2207
+ conversation_digest: memory.conversation_digest,
2208
+ tool_digest: memory.tool_digest,
2209
+ turn_flags: memory.turn_flags,
2210
+ context_sources: memory.context_sources,
2211
+ agent_guidance: memory.agent_guidance,
2104
2212
  active_entities: memory.active_entities,
2105
2213
  open_decisions: memory.open_decisions,
2106
2214
  last_error: memory.last_error,
@@ -2355,13 +2463,22 @@ const compactConversationTimelineForSideChannel = (conversation = {}, maxItems =
2355
2463
  return pruneByLimit(chronological, maxItems).map((message, index) => {
2356
2464
  const role = normalizeConversationRoleForSideChannel(message.role || message.type);
2357
2465
  const content = String(message.content || message.text || message.message || '');
2466
+ const at = message.at || message.timestamp || message.created_at || message.createdAt;
2467
+ const preview = role === 'system' ? '[system context hidden]' : truncate(content, 180);
2358
2468
  return cleanContextValue({
2359
2469
  index,
2360
2470
  role,
2361
2471
  speaker: role,
2362
- at: message.at || message.timestamp || message.created_at || message.createdAt,
2472
+ at,
2363
2473
  chars: content.length,
2364
- content: truncate(content, 700),
2474
+ preview,
2475
+ content: preview,
2476
+ message: {
2477
+ role,
2478
+ at,
2479
+ content: role === 'system' ? '[system context hidden]' : truncate(content, 2000),
2480
+ truncated: content.length > 2000,
2481
+ },
2365
2482
  });
2366
2483
  });
2367
2484
  };
@@ -2427,6 +2544,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2427
2544
  summary.payloadFormat = parsed.payloadFormat;
2428
2545
  summary.options = cleanContextValue(parsed.options || {});
2429
2546
  summary.intent = parsed.observations?.inferred_intent?.label || parsed.workingMemory?.last_user_intent || parsed.decisionState?.current_intent || undefined;
2547
+ const parsedTurnBrief = parsed.turnBrief || parsed.turn_brief || undefined;
2430
2548
  summary.currentUserMessage = conversation.current_user_message ? truncate(conversation.current_user_message, 180) : undefined;
2431
2549
  summary.recentMessages = Array.isArray(conversation.conversation_history_chronological) ? conversation.conversation_history_chronological.length : undefined;
2432
2550
  summary.conversationTimeline = compactConversationTimelineForSideChannel(conversation, 12);
@@ -2442,6 +2560,16 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2442
2560
  at: tools.last_successful_tool.at,
2443
2561
  }
2444
2562
  : (toolItems.length ? { name: toolItems[toolItems.length - 1].name, at: toolItems[toolItems.length - 1].at } : undefined);
2563
+ summary.turnBrief = typeof parsedTurnBrief === 'string'
2564
+ ? cleanContextValue({
2565
+ brief: parsedTurnBrief,
2566
+ intent: summary.intent,
2567
+ currentUserMessage: summary.currentUserMessage,
2568
+ messages: summary.recentMessages,
2569
+ toolCount: summary.toolCount,
2570
+ lastTool: summary.lastTool,
2571
+ })
2572
+ : parsedTurnBrief;
2445
2573
  summary.counts = cleanContextValue({
2446
2574
  conversationMessages: Array.isArray(conversation.conversation_history_chronological) ? conversation.conversation_history_chronological.length : undefined,
2447
2575
  userMessages: Array.isArray(conversation.all_user_messages_chronological) ? conversation.all_user_messages_chronological.length : undefined,
@@ -2463,6 +2591,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2463
2591
  memoryCompression: Boolean(parsed.memoryCompression),
2464
2592
  operationalState: Boolean(parsed.operationalState),
2465
2593
  actionLedger: Array.isArray(parsed.actionLedger),
2594
+ turnBrief: Boolean(parsed.turnBrief || parsed.turn_brief),
2466
2595
  memoryAudit: Boolean(memoryAudit && Object.keys(memoryAudit).length),
2467
2596
  entityTimeline: Array.isArray(parsed.entityTimeline),
2468
2597
  vectorMemories: Array.isArray(parsed.vectorMemories),
@@ -2472,10 +2601,16 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2472
2601
  });
2473
2602
  summary.workingMemory = cleanContextValue({
2474
2603
  currentGoal: parsed.workingMemory?.current_goal,
2604
+ currentGoalReason: parsed.workingMemory?.current_goal_reason,
2475
2605
  currentTask: parsed.workingMemory?.current_task,
2476
2606
  nextExpectedAction: parsed.workingMemory?.next_expected_action,
2477
2607
  lastUserIntent: parsed.workingMemory?.last_user_intent,
2478
2608
  lastUserMessage: parsed.workingMemory?.last_user_message ? truncate(parsed.workingMemory.last_user_message, 220) : undefined,
2609
+ conversation: parsed.workingMemory?.conversation_digest,
2610
+ tools: parsed.workingMemory?.tool_digest,
2611
+ turnFlags: parsed.workingMemory?.turn_flags,
2612
+ contextSources: parsed.workingMemory?.context_sources,
2613
+ agentGuidance: parsed.workingMemory?.agent_guidance,
2479
2614
  });
2480
2615
  summary.decisionState = cleanContextValue({
2481
2616
  currentIntent: parsed.decisionState?.current_intent,
@@ -2582,6 +2717,12 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2582
2717
  value: currentTurnFocus,
2583
2718
  });
2584
2719
  }
2720
+ const turnBrief = buildTurnBriefForAgent({ query, recentMessages, toolHistory, operationalState, workingMemory, decisionState, diagnostics });
2721
+ sections.push({
2722
+ section: 'turn_brief',
2723
+ title: 'Turn brief',
2724
+ value: turnBrief,
2725
+ });
2585
2726
  const actionDirective = buildActionDirective({ workingMemory, operationalState });
2586
2727
  if (actionDirective) {
2587
2728
  sections.push({
@@ -2679,6 +2820,10 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2679
2820
  const directive = sectionValue('action_directive');
2680
2821
  const inferredIntent = deriveUserIntentObservation({ query, workingMemory, decisionState, recentMessages });
2681
2822
  const memoryAudit = sectionValue('memory_audit');
2823
+ const compactSummary = cleanContextValue({
2824
+ vector_facts: vectorFacts,
2825
+ slm: hasToolLedger ? undefined : slmSummary,
2826
+ });
2682
2827
  const minimalState = cleanContextValue({
2683
2828
  next_expected_action: directive ? undefined : workingMemory.next_expected_action,
2684
2829
  context_quality_score: diagnostics?.contextHealth?.quality_score || diagnostics?.quality_score,
@@ -2697,13 +2842,11 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2697
2842
  next_expected_action: directive.next_expected_action,
2698
2843
  instruction: directive.instruction,
2699
2844
  }) : undefined,
2845
+ turnBrief: compactTurnBriefForAgent(sectionValue('turn_brief')),
2700
2846
  observations: {
2701
2847
  inferred_intent: inferredIntent,
2702
2848
  },
2703
- summary: {
2704
- vector_facts: vectorFacts,
2705
- slm: hasToolLedger ? undefined : slmSummary,
2706
- },
2849
+ summary: Object.keys(compactSummary).length ? compactSummary : undefined,
2707
2850
  state: minimalState,
2708
2851
  workingMemory: sectionValue('working_memory'),
2709
2852
  decisionState: sectionValue('decision_state'),
@@ -4340,6 +4483,7 @@ exports.__private = {
4340
4483
  buildContextMessages,
4341
4484
  inferToolGuard,
4342
4485
  inferUserIntent,
4486
+ buildTurnBriefForAgent,
4343
4487
  deriveUserIntentObservation,
4344
4488
  deriveWorkingMemory,
4345
4489
  deriveDecisionState,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.1.24",
3
+ "version": "1.1.26",
4
4
  "description": "Tembory node for n8n AI Agents with operational memory, tool history and decision state",
5
5
  "license": "MIT",
6
6
  "homepage": "https://tembory.com",