n8n-nodes-tembory 1.0.19 → 1.0.21

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.0.19`.
5
+ Versao atual: `1.0.21`.
6
+
7
+ ## 1.0.21
8
+
9
+ - O contexto principal passa a anexar o input atual do agente ao transcript antes de renderizar memoria.
10
+ - `Conversation frame` agora inclui `recent_messages_chronological`, com janela curta usuario/assistente em ordem.
11
+ - Mesmo antes do `saveContext`, o agente recebe a mensagem atual e a mensagem anterior do cliente no contexto estrutural.
12
+
13
+ ## 1.0.20
14
+
15
+ - Torna `Conversation frame` obrigatorio em todo contexto estruturado, antes de estado operacional e historico de tools.
16
+ - Expõe `current_user_message`, `previous_user_message` e ultimas mensagens do cliente como fonte autoritativa de curto prazo.
17
+ - Remove a dependencia de detectar termos como `msg`/`mensagem` para o agente conseguir responder sobre a ultima mensagem do cliente.
6
18
 
7
19
  ## 1.0.19
8
20
 
@@ -1090,6 +1090,16 @@ const dedupeRecentMessages = (items) => {
1090
1090
  }
1091
1091
  return out;
1092
1092
  };
1093
+ const appendCurrentUserMessage = (items = [], query = '') => {
1094
+ const content = String(query || '').trim();
1095
+ if (!content)
1096
+ return items || [];
1097
+ const normalized = normalizeIntentText(content).trim();
1098
+ const lastUser = [...(items || [])].reverse().find((msg) => /^(user|human)$/i.test(String(msg.role || '')));
1099
+ if (lastUser && normalizeIntentText(lastUser.content).trim() === normalized)
1100
+ return items || [];
1101
+ return (items || []).concat([{ role: 'user', content: truncate(content, 2000), at: nowIso(), source: 'current_input' }]);
1102
+ };
1093
1103
  const dedupeToolHistory = (items) => {
1094
1104
  const seen = new Set();
1095
1105
  const out = [];
@@ -1167,6 +1177,27 @@ const previousUserMessageForQuery = (query = '', recentMessages = []) => {
1167
1177
  return users[1] || null;
1168
1178
  return users[0] || null;
1169
1179
  };
1180
+ const buildConversationFrame = ({ query = '', recentMessages = [], workingMemory = {} }) => {
1181
+ const normalizedQuery = normalizeIntentText(query).trim();
1182
+ const users = recentUserMessages(recentMessages);
1183
+ const currentUser = users.find((msg) => normalizeIntentText(msg.content).trim() === normalizedQuery) || (query ? { role: 'user', content: query, at: nowIso() } : null);
1184
+ const previousUser = previousUserMessageForQuery(query, recentMessages);
1185
+ const chronological = pruneByLimit(recentMessages || [], 10).map((msg) => ({
1186
+ role: msg.role,
1187
+ content: truncate(msg.content, 500),
1188
+ at: msg.at,
1189
+ }));
1190
+ return cleanContextValue({
1191
+ current_user_message: currentUser ? truncate(currentUser.content, 500) : truncate(query, 500),
1192
+ previous_user_message: previousUser ? truncate(previousUser.content, 500) : (workingMemory === null || workingMemory === void 0 ? void 0 : workingMemory.last_user_message) || null,
1193
+ recent_messages_chronological: chronological,
1194
+ recent_user_messages: users
1195
+ .filter((msg) => normalizeIntentText(msg.content).trim() !== normalizedQuery)
1196
+ .slice(0, 5)
1197
+ .map((msg) => ({ role: msg.role, content: truncate(msg.content, 500), at: msg.at })),
1198
+ instruction: 'This is the authoritative short-term conversation frame. If the user asks about the last/current/previous client message, answer from previous_user_message/recent_user_messages before using vector memories, summaries, operational state, or tool history.',
1199
+ });
1200
+ };
1170
1201
  const buildCurrentTurnFocus = ({ query = '', recentMessages = [], operationalState = {}, workingMemory = {} }) => {
1171
1202
  const recall = isConversationRecallQuery(query);
1172
1203
  const agendaStatus = isAgendaStatusQuery(query);
@@ -1850,10 +1881,15 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1850
1881
  section: 'context_header',
1851
1882
  title: 'Tembory context',
1852
1883
  value: compactForAgent || compactStateSections
1853
- ? '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.'
1854
- : '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.',
1884
+ ? 'Read-only memory. Conversation frame is authoritative for current/previous client messages. 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.'
1885
+ : 'Use this context as read-only memory. Prefer it over guessing. Do not mention internal section names to the user. The Conversation frame is authoritative for current, previous, and recent client messages. 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.',
1855
1886
  });
1856
1887
  }
1888
+ sections.push({
1889
+ section: 'conversation_frame',
1890
+ title: 'Conversation frame',
1891
+ value: buildConversationFrame({ query, recentMessages, workingMemory }),
1892
+ });
1857
1893
  const currentTurnFocus = buildCurrentTurnFocus({ query, recentMessages, operationalState, workingMemory });
1858
1894
  if (currentTurnFocus) {
1859
1895
  sections.push({
@@ -3013,8 +3049,9 @@ class Mem0Memory {
3013
3049
  .flatMap(toolHistoryItemsFromMemory)
3014
3050
  .sort((a, b) => new Date(a.at || 0).getTime() - new Date(b.at || 0).getTime());
3015
3051
  }
3016
- const allRecentMessages = dedupeRecentMessages((store.recentMessages[key] || []).concat(persistedRecentMessages));
3017
- const recentMessages = adv.includeRecentMessages === false ? [] : pruneByLimit(allRecentMessages, adv.recentMessagesLastN || 8);
3052
+ const storedRecentMessages = adv.includeRecentMessages === false ? [] : (store.recentMessages[key] || []).concat(persistedRecentMessages);
3053
+ const allRecentMessages = dedupeRecentMessages(appendCurrentUserMessage(storedRecentMessages, query));
3054
+ const recentMessages = pruneByLimit(allRecentMessages, Math.max(Number(adv.recentMessagesLastN || 8), 8));
3018
3055
  const toolHistoryFromRecentMessages = [];
3019
3056
  if (adv.includeToolHistory !== false) {
3020
3057
  for (const msg of recentMessages)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
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",