n8n-nodes-tembory 1.3.1 → 1.3.3

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,20 @@
2
2
 
3
3
  Node de memoria operacional da Tembory para agentes de IA no n8n.
4
4
 
5
- Versao atual: `1.3.1`.
5
+ Versao atual: `1.3.3`.
6
+
7
+ ## 1.3.3
8
+
9
+ - Remove texto generico de trabalho como `continue according to the agent prompt...` quando a memoria nao inferiu uma acao concreta.
10
+ - Para mensagens gerais, a memoria registra conversa e contexto, mas nao inventa `currentGoal`, `currentTask` ou `nextExpectedAction`.
11
+ - Para turnos que podem exigir tool, o debug visual passa a mostrar uma orientacao objetiva: avaliar se precisa de tool e consultar `conversation_frame`, `tool_state`, `tool_history` e `action_ledger` antes de executar algo novo.
12
+
13
+ ## 1.3.2
14
+
15
+ - Remodela o output visual para debug simples no n8n com `sentToAgent`, `slm`, `memory`, `conversation`, `tools`, `io` e `save`.
16
+ - Remove duplicacoes visuais de `saveContext`: `savedMessages`, `conversationTimeline`, `toolNames` e `toolEvents` deixam de aparecer como blocos paralelos no topo.
17
+ - Expõe o status real da SLM no visual: fonte, geração no load atual, cache técnico, active summary carregado/atualizado, chars e preview.
18
+ - Mantem o contexto enviado ao Agent como `tembory.agent_context.v2`; a mudança é no side channel visual e na instrução da SLM para resumir objetivo, próxima ação e evidências operacionais.
6
19
 
7
20
  ## 1.3.1
8
21
 
@@ -2054,7 +2054,7 @@ const turnBriefGuidanceForIntent = (intent = '') => {
2054
2054
  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.';
2055
2055
  if (intent === 'profile_update')
2056
2056
  return 'Likely stable profile data. Save useful facts and continue; call tools only when prompt requires.';
2057
- return 'Continue according to the agent prompt using conversation, tool history and operational state as read-only context.';
2057
+ return 'No specific memory action inferred for this turn; use conversation, tool history and operational state only as read-only context.';
2058
2058
  };
2059
2059
  const buildTurnBriefForAgent = ({ query = '', recentMessages = [], toolHistory = [], operationalState = {}, workingMemory = {}, decisionState = {}, diagnostics = {} }) => {
2060
2060
  const currentIntent = (decisionState || {}).current_intent || (workingMemory || {}).last_user_intent || inferUserIntent(query, recentMessages);
@@ -2182,16 +2182,16 @@ const inferUserIntent = (query = '', recentMessages = []) => {
2182
2182
  };
2183
2183
  const deriveNextExpectedAction = (intent, operationalState = {}) => {
2184
2184
  if (['affirm', 'commit_or_continue', 'tool_action_candidate', 'selection_or_slot'].includes(intent))
2185
- return 'continue according to the agent prompt using conversation_frame, tool_state, tool_history and action_ledger; do not apply domain-specific memory rules';
2185
+ return 'evaluate whether the current turn needs a tool; reuse conversation_frame, tool_state, tool_history and action_ledger before executing anything new';
2186
2186
  if (intent === 'profile_update')
2187
2187
  return 'save stable profile facts and continue the conversation';
2188
2188
  if (intent === 'conversation_recall')
2189
2189
  return 'answer directly using conversation_history_chronological/all_user_messages_chronological; do not call tools for recall-only questions';
2190
2190
  if (intent === 'operational_status_question')
2191
2191
  return 'answer status questions from tool_state, tool_history and action_ledger; do not call tools unless the agent prompt requires it';
2192
- return 'continue according to the agent prompt using retrieved context; call tools when the agent prompt or tool policy requires them';
2192
+ return undefined;
2193
2193
  };
2194
- 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 || ''));
2194
+ const isGenericMemoryNextAction = (value = '') => /answer using retrieved context and avoid unnecessary tool calls|continue according to the agent prompt using retrieved context|continue according to the agent prompt/i.test(String(value || ''));
2195
2195
  const shouldCarryPreviousGoal = (intent = '', previousGoal = '') => {
2196
2196
  if (!previousGoal || isGenericMemoryNextAction(previousGoal))
2197
2197
  return false;
@@ -3231,6 +3231,14 @@ const buildAgentContextV2 = ({ query = '', userId = '', payloadFormat = 'structu
3231
3231
  memory: cleanContextValue({
3232
3232
  slmSummary: connectedModelSummary ? truncate(connectedModelSummary, Number(adv.connectedModelSummaryMaxChars || 900)) : undefined,
3233
3233
  activeSummary: !connectedModelSummary && activeSummary ? truncate(activeSummary, Number(adv.activeSummaryMaxChars || 900)) : undefined,
3234
+ summaryStatus: diagnostics?.activeSummary ? cleanContextValue({
3235
+ source: diagnostics.activeSummary.source,
3236
+ generated: Boolean(connectedModelSummary),
3237
+ activeSummaryLoaded: Boolean(diagnostics.activeSummary.activeSummaryLoaded),
3238
+ activeSummaryUpdated: Boolean(diagnostics.activeSummary.activeSummaryUpdated),
3239
+ transientCacheHit: Boolean(diagnostics.activeSummary.transientCacheHit),
3240
+ chars: diagnostics.activeSummary.summaryChars,
3241
+ }) : undefined,
3234
3242
  referenceFacts,
3235
3243
  compression: adv.includeMemoryCompression === false ? undefined : compactMemoryCompressionForAgent(memoryCompression || {}),
3236
3244
  }),
@@ -3352,13 +3360,13 @@ const invokeConnectedModelSummary = async (connectedLanguageModel, summaryInput,
3352
3360
  const response = await connectedLanguageModel.invoke([
3353
3361
  toBaseMessage({
3354
3362
  role: 'user',
3355
- 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, explicit pending actions, constraints, contradictions, and do-not-repeat instructions. Prefer durable useful context over raw logs. Do not invent facts. Treat inferred gaps or missing details as read-only observations, not action requirements, unless they are explicitly present in tool_state, action_ledger, working_memory.next_expected_action, or the current user message. Do not convert observations, guesses, or helpful suggestions into blocking pending actions. Do-not-repeat means avoiding duplicate prerequisite lookup calls; it never means a current side-effect action is completed without a successful tool call in the current turn.\n\nContext:\n${summaryInput}`,
3363
+ content: `Update the Tembory active summary for the next agent turn. Return only concise Portuguese bullets, no JSON and no markdown table. Include what the agent is doing now, the current objective, the next expected action, useful tool evidence, pending confirmations, durable user facts, constraints, contradictions, and do-not-repeat guidance when present. Preserve IDs, dates, tool names, confirmed decisions, explicit pending actions, constraints, contradictions, and do-not-repeat instructions. Prefer durable useful context over raw logs. Do not invent facts. Treat inferred gaps or missing details as read-only observations, not action requirements, unless they are explicitly present in tool_state, action_ledger, working_memory.next_expected_action, or the current user message. Do not convert observations, guesses, or helpful suggestions into blocking pending actions. Do-not-repeat means avoiding duplicate prerequisite lookup calls; it never means a current side-effect action is completed without a successful tool call in the current turn.\n\nContext:\n${summaryInput}`,
3356
3364
  }),
3357
3365
  ]);
3358
3366
  return cleanModelSummaryText(response, Number(adv.connectedModelSummaryMaxChars || 1200));
3359
3367
  };
3360
- const VISUAL_SCHEMA_VERSION = 'tembory.visual.v1';
3361
- const TOOL_LOG_VISUAL_SCHEMA_VERSION = 'tembory.visual.v1.toolLog';
3368
+ const VISUAL_SCHEMA_VERSION = 'tembory.visual.v2';
3369
+ const TOOL_LOG_VISUAL_SCHEMA_VERSION = 'tembory.visual.v2.toolLog';
3362
3370
  const SIDE_CHANNEL_TOOL_EVENT_MAX = 12;
3363
3371
  const SIDE_CHANNEL_SAVE_TOOL_EVENT_MAX = 20;
3364
3372
  const compactParsedToolInputForSideChannel = (input) => {
@@ -3538,22 +3546,35 @@ const summarizeSaveContextForSideChannel = (input = {}, output = {}, chatHistory
3538
3546
  const outputMessage = output?.output || output?.response || output?.text || output?.message || output?.answer || '';
3539
3547
  const toolCalls = extractToolCalls(output || {});
3540
3548
  const toolEvents = compactToolEventsForSideChannel(toolCalls, SIDE_CHANNEL_SAVE_TOOL_EVENT_MAX);
3541
- return keepVisibleToolLogArrays(cleanContextValue({
3549
+ const conversation = conversationDigestFromMessagesForSideChannel(chatHistory);
3550
+ const timeline = conversationTimelineFromMessagesForSideChannel(chatHistory, 16);
3551
+ return cleanContextValue({
3542
3552
  visualSchema: VISUAL_SCHEMA_VERSION,
3543
- messagesAfterSave: Array.isArray(chatHistory) ? chatHistory.length : 0,
3544
- conversation: conversationDigestFromMessagesForSideChannel(chatHistory),
3545
- conversationTimeline: conversationTimelineFromMessagesForSideChannel(chatHistory, 16),
3546
- savedMessages: Array.isArray(chatHistory)
3547
- ? chatHistory.slice(-6).map((message) => compactMessageForSideChannel(message))
3548
- : undefined,
3549
- userInput: inputMessage ? truncate(String(inputMessage), 500) : undefined,
3550
- assistantOutput: outputMessage ? truncate(String(outputMessage), 700) : undefined,
3551
- toolCallsCaptured: toolCalls.length,
3552
- toolNames: toolEvents.map((tool) => tool.name).filter(Boolean),
3553
- toolCapture: summarizeToolCaptureForSideChannel(output, toolCalls),
3554
- toolLog: buildToolLogForSideChannel(toolEvents, toolCalls.length, SIDE_CHANNEL_SAVE_TOOL_EVENT_MAX),
3555
- toolEvents,
3556
- }));
3553
+ summary: cleanContextValue({
3554
+ action: 'saveContext',
3555
+ messagesAfterSave: Array.isArray(chatHistory) ? chatHistory.length : 0,
3556
+ userInputPreview: inputMessage ? truncate(String(inputMessage), 220) : undefined,
3557
+ assistantOutputPreview: outputMessage ? truncate(String(outputMessage), 260) : undefined,
3558
+ toolCallsCaptured: toolCalls.length,
3559
+ }),
3560
+ conversation: cleanContextValue({
3561
+ ...conversation,
3562
+ timeline,
3563
+ }),
3564
+ io: cleanContextValue({
3565
+ userInput: inputMessage ? truncate(String(inputMessage), 500) : undefined,
3566
+ assistantOutput: outputMessage ? truncate(String(outputMessage), 700) : undefined,
3567
+ }),
3568
+ tools: cleanContextValue({
3569
+ captured: toolCalls.length,
3570
+ capture: summarizeToolCaptureForSideChannel(output, toolCalls),
3571
+ log: buildToolLogForSideChannel(toolEvents, toolCalls.length, SIDE_CHANNEL_SAVE_TOOL_EVENT_MAX),
3572
+ }),
3573
+ save: cleanContextValue({
3574
+ messagesAfterSave: Array.isArray(chatHistory) ? chatHistory.length : 0,
3575
+ status: 'saved_to_memory_contract',
3576
+ }),
3577
+ });
3557
3578
  };
3558
3579
  const compactMemoryEventPayload = (payload = {}) => {
3559
3580
  const compact = { ...(payload || {}) };
@@ -3771,10 +3792,20 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
3771
3792
  toolCallsCaptured: parsed.captureHealth?.toolCallsCapturedLastSave,
3772
3793
  captureSources: parsed.captureHealth?.captureSources,
3773
3794
  } : {}));
3795
+ const summaryStatus = parsed.memory?.summaryStatus || parsed.diagnostics?.activeSummary || {};
3796
+ const slmPreview = parsed.memory?.slmSummary || parsed.memory?.activeSummary || summaryText || '';
3774
3797
  const fullDedupeSummary = parsed.dedupeSummary || parsed.diagnostics?.dedupeSummary || undefined;
3775
3798
  const loadedSections = loadedSectionsForSideChannel(parsed, memoryAudit);
3776
3799
  const agentContextBudget = agentContextBudgetForSideChannel(list, parsed);
3777
3800
  summary.visualSchema = VISUAL_SCHEMA_VERSION;
3801
+ summary.sentToAgent = cleanContextValue({
3802
+ messages: list.length,
3803
+ contextKind: parsed.kind,
3804
+ schemaVersion: parsed.schemaVersion,
3805
+ chars: agentContextBudget.chars,
3806
+ approxTokens: agentContextBudget.approxTokens,
3807
+ largestSections: agentContextBudget.largestSections,
3808
+ });
3778
3809
  summary.userId = parsed.userId;
3779
3810
  summary.project = parsed.project || undefined;
3780
3811
  summary.retrievalMode = parsed.retrievalMode;
@@ -3796,6 +3827,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
3796
3827
  at: lastAgent.at || lastAgent.timestamp || lastAgent.created_at || lastAgent.createdAt,
3797
3828
  preview: truncate(String(lastAgent.content || lastAgent.text || lastAgent.message || ''), 240),
3798
3829
  } : undefined,
3830
+ timeline: compactConversationTimelineForSideChannel(conversation, 16),
3799
3831
  });
3800
3832
  summary.conversationTimeline = compactConversationTimelineForSideChannel(conversation, 16);
3801
3833
  const visibleToolEvents = compactToolEventsForSideChannel(toolItems, SIDE_CHANNEL_TOOL_EVENT_MAX);
@@ -3831,6 +3863,30 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
3831
3863
  });
3832
3864
  summary.loadedSections = loadedSections;
3833
3865
  summary.agentContextBudget = agentContextBudget;
3866
+ summary.slm = cleanContextValue({
3867
+ status: slmPreview ? 'available' : 'empty',
3868
+ source: summaryStatus.source || (parsed.memory?.slmSummary ? 'slm_summary' : parsed.memory?.activeSummary ? 'active_summary' : 'none'),
3869
+ generatedThisLoad: summaryStatus.source === 'fresh_slm',
3870
+ activeSummaryLoaded: summaryStatus.activeSummaryLoaded,
3871
+ activeSummaryUpdated: summaryStatus.activeSummaryUpdated,
3872
+ transientCacheHit: summaryStatus.transientCacheHit,
3873
+ chars: summaryStatus.chars || summaryStatus.summaryChars || (slmPreview ? String(slmPreview).length : 0),
3874
+ preview: slmPreview ? truncate(String(slmPreview), 500) : undefined,
3875
+ });
3876
+ summary.memory = cleanContextValue({
3877
+ conversation: summary.conversation,
3878
+ tools: {
3879
+ count: toolItems.length || summary.toolCount || 0,
3880
+ names: summary.toolNames,
3881
+ last: summary.lastTool,
3882
+ log: summary.toolLog,
3883
+ },
3884
+ compression: parsed.memory?.compression || parsed.memoryCompression,
3885
+ referenceFacts: parsed.memory?.referenceFacts,
3886
+ canonicalFacts: parsed.canonicalFacts,
3887
+ captureHealth: parsed.captureHealth,
3888
+ });
3889
+ summary.tools = summary.memory?.tools;
3834
3890
  summary.lastSave = Object.keys(lastSave).length ? lastSave : undefined;
3835
3891
  summary.dedupe = fullDedupeSummary ? compactDedupeForSideChannel(fullDedupeSummary) : undefined;
3836
3892
  summary.workingMemory = cleanContextValue({
@@ -3875,6 +3931,13 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
3875
3931
  dedupeSummary: fullDedupeSummary,
3876
3932
  conversationTimelineFull: compactConversationTimelineForSideChannel(conversation, 12, true),
3877
3933
  }) : undefined;
3934
+ delete summary.currentUserMessage;
3935
+ delete summary.toolNames;
3936
+ delete summary.toolEvents;
3937
+ delete summary.toolLog;
3938
+ delete summary.lastTool;
3939
+ delete summary.agentContextBudget;
3940
+ delete summary.conversationTimeline;
3878
3941
  return Object.fromEntries(Object.entries(summary).filter(([, value]) => value !== undefined));
3879
3942
  }
3880
3943
  catch {
@@ -4068,7 +4131,7 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
4068
4131
  latest_tool: ((decisionState || {}).latest_tool || (operationalState || {}).last_tool || undefined),
4069
4132
  avoid_repeating_tools_unless_needed: ((decisionState || {}).avoid_repeating_tools_unless_needed || []).slice(0, 12),
4070
4133
  do_not_repeat_tools_legacy: ((decisionState || {}).do_not_repeat_tools || []).slice(0, 12),
4071
- instruction: actionDirective || workingMemory.next_expected_action || 'Continue according to the agent prompt.',
4134
+ instruction: actionDirective || workingMemory.next_expected_action || undefined,
4072
4135
  }),
4073
4136
  });
4074
4137
  const audit = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
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",