n8n-nodes-tembory 1.1.41 → 1.1.43

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,19 @@
2
2
 
3
3
  Node de memoria operacional da Tembory para agentes de IA no n8n.
4
4
 
5
- Versao atual: `1.1.41`.
5
+ Versao atual: `1.1.43`.
6
+
7
+ ## 1.1.43
8
+
9
+ - Adiciona `toolCapture` no payload visual de `saveContext`, deixando explicito quando nenhuma execucao de tool chegou para a memoria.
10
+ - Suporta tambem `intermediate_steps` em snake case, alem de `intermediateSteps`.
11
+ - Quando `toolCallsCaptured` for `0`, o payload informa que e necessario o AI Agent entregar `intermediateSteps`, `__temboryToolCalls` ou tool messages para a memoria conseguir salvar a tool.
12
+
13
+ ## 1.1.42
14
+
15
+ - Explicita `conversation` e `conversationTimeline` tambem no payload visual de `saveContext`.
16
+ - Aumenta a timeline visual de conversa do `loadMemoryVariables` para ate 16 mensagens recentes.
17
+ - Parseia resultados de tools de busca vetorial em `outputParsed.documents`, preservando `pageContent`, `metadata`, `id`, `source` e `score` quando existirem.
6
18
 
7
19
  ## 1.1.41
8
20
 
@@ -1210,9 +1210,14 @@ const extractToolCalls = (outputValues = {}) => {
1210
1210
  source: 'tool_object',
1211
1211
  });
1212
1212
  }
1213
- if (Array.isArray(obj.intermediateSteps)) {
1214
- for (let index = 0; index < obj.intermediateSteps.length; index++) {
1215
- const step = obj.intermediateSteps[index];
1213
+ const intermediateSteps = Array.isArray(obj.intermediateSteps)
1214
+ ? obj.intermediateSteps
1215
+ : Array.isArray(obj.intermediate_steps)
1216
+ ? obj.intermediate_steps
1217
+ : undefined;
1218
+ if (Array.isArray(intermediateSteps)) {
1219
+ for (let index = 0; index < intermediateSteps.length; index++) {
1220
+ const step = intermediateSteps[index];
1216
1221
  if (Array.isArray(step) && step.length >= 1) {
1217
1222
  const action = step[0] || {};
1218
1223
  push(action.tool || action.name, action.toolInput || action.input || action.args, step[1], true, {
@@ -2680,9 +2685,44 @@ const extractToolOperationalFacts = (tool = {}) => {
2680
2685
  const facts = mergeOperationalFactObjects(outputFacts, inputFacts);
2681
2686
  return Object.keys(facts || {}).length ? facts : undefined;
2682
2687
  };
2688
+ const compactVectorDocumentForSideChannel = (value) => {
2689
+ const original = value;
2690
+ let parsed = value;
2691
+ if (typeof parsed === 'string')
2692
+ parsed = tryParseJsonValue(parsed) || parsed;
2693
+ if (parsed && typeof parsed === 'object' && typeof parsed.text === 'string') {
2694
+ const textParsed = tryParseJsonValue(parsed.text);
2695
+ if (textParsed && typeof textParsed === 'object')
2696
+ parsed = { ...parsed, ...textParsed };
2697
+ }
2698
+ if (!parsed || typeof parsed !== 'object')
2699
+ return undefined;
2700
+ const pageContent = parsed.pageContent || parsed.content || parsed.document || (original && original.type === 'text' ? parsed.text : undefined);
2701
+ const rawMetadata = parsed.metadata && typeof parsed.metadata === 'object' ? parsed.metadata : undefined;
2702
+ const metadata = rawMetadata ? stripNoisyToolFields(rawMetadata) : undefined;
2703
+ if (!pageContent && !metadata)
2704
+ return undefined;
2705
+ return cleanContextValue({
2706
+ pageContent: pageContent ? truncate(String(pageContent), 1200) : undefined,
2707
+ metadata,
2708
+ id: parsed.id || metadata?.id,
2709
+ source: parsed.source || rawMetadata?.source,
2710
+ score: parsed.score || metadata?.score,
2711
+ });
2712
+ };
2713
+ const compactVectorDocumentsForSideChannel = (value) => {
2714
+ const items = Array.isArray(value) ? value : [value];
2715
+ return items
2716
+ .map((item) => compactVectorDocumentForSideChannel(item))
2717
+ .filter((item) => item && Object.keys(item).length)
2718
+ .slice(0, 8);
2719
+ };
2683
2720
  const compactParsedToolOutputForSideChannel = (parsed) => {
2684
2721
  if (parsed === undefined || parsed === null)
2685
2722
  return undefined;
2723
+ const vectorDocuments = compactVectorDocumentsForSideChannel(parsed);
2724
+ if (vectorDocuments.length)
2725
+ return cleanContextValue({ documents: vectorDocuments, count: vectorDocuments.length });
2686
2726
  if (Array.isArray(parsed))
2687
2727
  return cleanContextValue(parsed.map((item) => compactParsedToolOutputForSideChannel(item)));
2688
2728
  if (typeof parsed !== 'object')
@@ -3081,17 +3121,19 @@ const normalizeConversationRoleForSideChannel = (role = '') => {
3081
3121
  return 'tool';
3082
3122
  return normalized || 'message';
3083
3123
  };
3084
- const compactConversationTimelineForSideChannel = (conversation = {}, maxItems = 4, includeFull = false) => {
3124
+ const compactConversationTimelineForSideChannel = (conversation = {}, maxItems = 16, includeFull = false) => {
3085
3125
  const chronological = Array.isArray(conversation.conversation_history_chronological)
3086
3126
  ? conversation.conversation_history_chronological
3087
3127
  : [];
3088
- return pruneByLimit(chronological, maxItems).map((message, index) => {
3128
+ const visible = pruneByLimit(chronological, maxItems);
3129
+ const offset = Math.max(0, chronological.length - visible.length);
3130
+ return visible.map((message, index) => {
3089
3131
  const role = normalizeConversationRoleForSideChannel(message.role || message.type);
3090
3132
  const content = String(message.content || message.text || message.message || '');
3091
3133
  const at = message.at || message.timestamp || message.created_at || message.createdAt;
3092
3134
  const preview = role === 'system' ? '[system context hidden]' : truncate(content, 180);
3093
3135
  return cleanContextValue({
3094
- index,
3136
+ index: offset + index,
3095
3137
  role,
3096
3138
  speaker: role,
3097
3139
  at,
@@ -3106,6 +3148,80 @@ const compactConversationTimelineForSideChannel = (conversation = {}, maxItems =
3106
3148
  });
3107
3149
  });
3108
3150
  };
3151
+ const conversationEntriesFromMessagesForSideChannel = (messages = []) => (Array.isArray(messages) ? messages : [])
3152
+ .map((message) => {
3153
+ const type = messageTypeOf(message) || String(message.role || 'message').toLowerCase();
3154
+ const role = type === 'human' || type === 'user' ? 'user' : type === 'ai' || type === 'assistant' ? 'agent' : type === 'tool' ? 'tool' : type === 'system' ? 'system' : 'message';
3155
+ const content = messageContentOf(message);
3156
+ return {
3157
+ role,
3158
+ speaker: role,
3159
+ at: message.at || message.timestamp || message.created_at || message.createdAt,
3160
+ content,
3161
+ };
3162
+ })
3163
+ .filter((message) => message.role !== 'system' && message.content);
3164
+ const conversationDigestFromMessagesForSideChannel = (messages = []) => {
3165
+ const entries = conversationEntriesFromMessagesForSideChannel(messages);
3166
+ const lastUser = [...entries].reverse().find((message) => message.role === 'user');
3167
+ const lastAgent = [...entries].reverse().find((message) => message.role === 'agent');
3168
+ return cleanContextValue({
3169
+ messages: entries.length,
3170
+ userMessages: entries.filter((message) => message.role === 'user').length,
3171
+ assistantMessages: entries.filter((message) => message.role === 'agent').length,
3172
+ lastUser: lastUser ? {
3173
+ at: lastUser.at,
3174
+ preview: truncate(lastUser.content, 240),
3175
+ } : undefined,
3176
+ lastAgent: lastAgent ? {
3177
+ at: lastAgent.at,
3178
+ preview: truncate(lastAgent.content, 240),
3179
+ } : undefined,
3180
+ });
3181
+ };
3182
+ const conversationTimelineFromMessagesForSideChannel = (messages = [], maxItems = 16) => {
3183
+ const entries = conversationEntriesFromMessagesForSideChannel(messages);
3184
+ const visible = pruneByLimit(entries, maxItems);
3185
+ const offset = Math.max(0, entries.length - visible.length);
3186
+ return visible.map((message, index) => cleanContextValue({
3187
+ index: offset + index,
3188
+ role: message.role,
3189
+ speaker: message.speaker,
3190
+ at: message.at,
3191
+ chars: message.content.length,
3192
+ preview: truncate(message.content, 180),
3193
+ }));
3194
+ };
3195
+ const hasToolCaptureSignal = (output = {}) => {
3196
+ let hasIntermediateSteps = false;
3197
+ let hasExplicitToolCalls = false;
3198
+ let hasToolObject = false;
3199
+ readDeep(output || {}, (obj) => {
3200
+ if (Array.isArray(obj.intermediateSteps) || Array.isArray(obj.intermediate_steps))
3201
+ hasIntermediateSteps = true;
3202
+ if (Array.isArray(obj.__temboryToolCalls) || Array.isArray(obj.toolCalls) || Array.isArray(obj.tool_calls))
3203
+ hasExplicitToolCalls = true;
3204
+ const name = obj.tool || obj.toolName || obj.name;
3205
+ const hasInput = obj.toolInput !== undefined || obj.input !== undefined || obj.args !== undefined;
3206
+ const hasResult = obj.observation !== undefined || obj.result !== undefined || obj.output !== undefined || obj.error !== undefined;
3207
+ if (name && hasInput && hasResult)
3208
+ hasToolObject = true;
3209
+ });
3210
+ return { hasIntermediateSteps, hasExplicitToolCalls, hasToolObject };
3211
+ };
3212
+ const summarizeToolCaptureForSideChannel = (output = {}, toolCalls = []) => {
3213
+ const signals = hasToolCaptureSignal(output);
3214
+ const sources = Array.from(new Set((toolCalls || []).map((tool) => tool.source).filter(Boolean)));
3215
+ return cleanContextValue({
3216
+ status: toolCalls.length ? 'captured' : 'no_tool_payload_received',
3217
+ captured: toolCalls.length,
3218
+ sources,
3219
+ signals,
3220
+ note: toolCalls.length
3221
+ ? undefined
3222
+ : 'No tool execution data was received by memory in this saveContext call. If a tool ran, enable AI Agent Return Intermediate Steps or pass tool messages to memory.',
3223
+ });
3224
+ };
3109
3225
  const summarizeSaveContextForSideChannel = (input = {}, output = {}, chatHistory = []) => {
3110
3226
  const inputMessage = input?.input || input?.chatInput || input?.query || input?.question || input?.text || input?.message || '';
3111
3227
  const outputMessage = output?.output || output?.response || output?.text || output?.message || output?.answer || '';
@@ -3114,6 +3230,8 @@ const summarizeSaveContextForSideChannel = (input = {}, output = {}, chatHistory
3114
3230
  return keepVisibleToolLogArrays(cleanContextValue({
3115
3231
  visualSchema: VISUAL_SCHEMA_VERSION,
3116
3232
  messagesAfterSave: Array.isArray(chatHistory) ? chatHistory.length : 0,
3233
+ conversation: conversationDigestFromMessagesForSideChannel(chatHistory),
3234
+ conversationTimeline: conversationTimelineFromMessagesForSideChannel(chatHistory, 16),
3117
3235
  savedMessages: Array.isArray(chatHistory)
3118
3236
  ? chatHistory.slice(-6).map((message) => compactMessageForSideChannel(message))
3119
3237
  : undefined,
@@ -3121,6 +3239,7 @@ const summarizeSaveContextForSideChannel = (input = {}, output = {}, chatHistory
3121
3239
  assistantOutput: outputMessage ? truncate(String(outputMessage), 700) : undefined,
3122
3240
  toolCallsCaptured: toolCalls.length,
3123
3241
  toolNames: toolEvents.map((tool) => tool.name).filter(Boolean),
3242
+ toolCapture: summarizeToolCaptureForSideChannel(output, toolCalls),
3124
3243
  toolLog: buildToolLogForSideChannel(toolEvents, toolCalls.length, SIDE_CHANNEL_SAVE_TOOL_EVENT_MAX),
3125
3244
  toolEvents,
3126
3245
  }));
@@ -3320,7 +3439,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
3320
3439
  preview: truncate(String(lastAgent.content || lastAgent.text || lastAgent.message || ''), 240),
3321
3440
  } : undefined,
3322
3441
  });
3323
- summary.conversationTimeline = compactConversationTimelineForSideChannel(conversation, 4);
3442
+ summary.conversationTimeline = compactConversationTimelineForSideChannel(conversation, 16);
3324
3443
  const visibleToolEvents = compactToolEventsForSideChannel(toolItems, SIDE_CHANNEL_TOOL_EVENT_MAX);
3325
3444
  summary.toolCount = toolItems.length || tools.count || parsed.operationalState?.tool_counts?.total || undefined;
3326
3445
  summary.toolNames = toolItems.map((tool) => tool.name || tool.tool_name).filter(Boolean).slice(0, 12);
@@ -5501,6 +5620,7 @@ exports.__private = {
5501
5620
  compactToolResult,
5502
5621
  compactToolAuditForSideChannel,
5503
5622
  buildToolLogForSideChannel,
5623
+ compactParsedToolOutputForSideChannel,
5504
5624
  compactToolHistoryForAgent,
5505
5625
  compactVectorMemoriesForAgent,
5506
5626
  isConversationEchoMemory,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.1.41",
3
+ "version": "1.1.43",
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",