n8n-nodes-tembory 1.1.24 → 1.1.25

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';
@@ -2355,13 +2405,22 @@ const compactConversationTimelineForSideChannel = (conversation = {}, maxItems =
2355
2405
  return pruneByLimit(chronological, maxItems).map((message, index) => {
2356
2406
  const role = normalizeConversationRoleForSideChannel(message.role || message.type);
2357
2407
  const content = String(message.content || message.text || message.message || '');
2408
+ const at = message.at || message.timestamp || message.created_at || message.createdAt;
2409
+ const preview = role === 'system' ? '[system context hidden]' : truncate(content, 180);
2358
2410
  return cleanContextValue({
2359
2411
  index,
2360
2412
  role,
2361
2413
  speaker: role,
2362
- at: message.at || message.timestamp || message.created_at || message.createdAt,
2414
+ at,
2363
2415
  chars: content.length,
2364
- content: truncate(content, 700),
2416
+ preview,
2417
+ content: preview,
2418
+ message: {
2419
+ role,
2420
+ at,
2421
+ content: role === 'system' ? '[system context hidden]' : truncate(content, 2000),
2422
+ truncated: content.length > 2000,
2423
+ },
2365
2424
  });
2366
2425
  });
2367
2426
  };
@@ -2427,6 +2486,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2427
2486
  summary.payloadFormat = parsed.payloadFormat;
2428
2487
  summary.options = cleanContextValue(parsed.options || {});
2429
2488
  summary.intent = parsed.observations?.inferred_intent?.label || parsed.workingMemory?.last_user_intent || parsed.decisionState?.current_intent || undefined;
2489
+ const parsedTurnBrief = parsed.turnBrief || parsed.turn_brief || undefined;
2430
2490
  summary.currentUserMessage = conversation.current_user_message ? truncate(conversation.current_user_message, 180) : undefined;
2431
2491
  summary.recentMessages = Array.isArray(conversation.conversation_history_chronological) ? conversation.conversation_history_chronological.length : undefined;
2432
2492
  summary.conversationTimeline = compactConversationTimelineForSideChannel(conversation, 12);
@@ -2442,6 +2502,16 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2442
2502
  at: tools.last_successful_tool.at,
2443
2503
  }
2444
2504
  : (toolItems.length ? { name: toolItems[toolItems.length - 1].name, at: toolItems[toolItems.length - 1].at } : undefined);
2505
+ summary.turnBrief = typeof parsedTurnBrief === 'string'
2506
+ ? cleanContextValue({
2507
+ brief: parsedTurnBrief,
2508
+ intent: summary.intent,
2509
+ currentUserMessage: summary.currentUserMessage,
2510
+ messages: summary.recentMessages,
2511
+ toolCount: summary.toolCount,
2512
+ lastTool: summary.lastTool,
2513
+ })
2514
+ : parsedTurnBrief;
2445
2515
  summary.counts = cleanContextValue({
2446
2516
  conversationMessages: Array.isArray(conversation.conversation_history_chronological) ? conversation.conversation_history_chronological.length : undefined,
2447
2517
  userMessages: Array.isArray(conversation.all_user_messages_chronological) ? conversation.all_user_messages_chronological.length : undefined,
@@ -2463,6 +2533,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2463
2533
  memoryCompression: Boolean(parsed.memoryCompression),
2464
2534
  operationalState: Boolean(parsed.operationalState),
2465
2535
  actionLedger: Array.isArray(parsed.actionLedger),
2536
+ turnBrief: Boolean(parsed.turnBrief || parsed.turn_brief),
2466
2537
  memoryAudit: Boolean(memoryAudit && Object.keys(memoryAudit).length),
2467
2538
  entityTimeline: Array.isArray(parsed.entityTimeline),
2468
2539
  vectorMemories: Array.isArray(parsed.vectorMemories),
@@ -2582,6 +2653,12 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2582
2653
  value: currentTurnFocus,
2583
2654
  });
2584
2655
  }
2656
+ const turnBrief = buildTurnBriefForAgent({ query, recentMessages, toolHistory, operationalState, workingMemory, decisionState, diagnostics });
2657
+ sections.push({
2658
+ section: 'turn_brief',
2659
+ title: 'Turn brief',
2660
+ value: turnBrief,
2661
+ });
2585
2662
  const actionDirective = buildActionDirective({ workingMemory, operationalState });
2586
2663
  if (actionDirective) {
2587
2664
  sections.push({
@@ -2679,6 +2756,10 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2679
2756
  const directive = sectionValue('action_directive');
2680
2757
  const inferredIntent = deriveUserIntentObservation({ query, workingMemory, decisionState, recentMessages });
2681
2758
  const memoryAudit = sectionValue('memory_audit');
2759
+ const compactSummary = cleanContextValue({
2760
+ vector_facts: vectorFacts,
2761
+ slm: hasToolLedger ? undefined : slmSummary,
2762
+ });
2682
2763
  const minimalState = cleanContextValue({
2683
2764
  next_expected_action: directive ? undefined : workingMemory.next_expected_action,
2684
2765
  context_quality_score: diagnostics?.contextHealth?.quality_score || diagnostics?.quality_score,
@@ -2697,13 +2778,11 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2697
2778
  next_expected_action: directive.next_expected_action,
2698
2779
  instruction: directive.instruction,
2699
2780
  }) : undefined,
2781
+ turnBrief: compactTurnBriefForAgent(sectionValue('turn_brief')),
2700
2782
  observations: {
2701
2783
  inferred_intent: inferredIntent,
2702
2784
  },
2703
- summary: {
2704
- vector_facts: vectorFacts,
2705
- slm: hasToolLedger ? undefined : slmSummary,
2706
- },
2785
+ summary: Object.keys(compactSummary).length ? compactSummary : undefined,
2707
2786
  state: minimalState,
2708
2787
  workingMemory: sectionValue('working_memory'),
2709
2788
  decisionState: sectionValue('decision_state'),
@@ -4340,6 +4419,7 @@ exports.__private = {
4340
4419
  buildContextMessages,
4341
4420
  inferToolGuard,
4342
4421
  inferUserIntent,
4422
+ buildTurnBriefForAgent,
4343
4423
  deriveUserIntentObservation,
4344
4424
  deriveWorkingMemory,
4345
4425
  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.25",
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",