n8n-nodes-tembory 1.0.16 → 1.0.18

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.0.16`.
5
+ Versao atual: `1.0.18`.
6
+
7
+ ## 1.0.18
8
+
9
+ - Corrige perguntas de metaconversa como "qual foi minha ultima mensagem?" para priorizar a mensagem recente exata, mesmo quando existe estado de agenda ativo.
10
+ - Corrige perguntas de status como "eu nao tinha agendado ainda, neh?" para responder se ha reserva/agendamento confirmado ou pendente, em vez de repetir disponibilidade.
11
+ - Inclui um bloco `Current turn focus` no contexto para proteger a intencao atual contra tool state antigo.
12
+ - Adiciona regressao para typos comuns como `agnedamento`/`ganedado` em perguntas de agenda.
13
+
14
+ ## 1.0.17
15
+
16
+ - Oculta `Avançado Legado (Compatibilidade)` da interface normal para evitar duplicidade visual com `Configuração Tembory`.
17
+ - Mantem o campo legado no descriptor e no runtime para workflows antigos que ja salvaram parametros em `advanced`.
18
+ - A UI normal passa a mostrar apenas a configuracao agrupada.
6
19
 
7
20
  ## 1.0.16
8
21
 
@@ -1145,6 +1145,63 @@ const normalizeIntentText = (value = '') => String(value || '')
1145
1145
  .normalize('NFD')
1146
1146
  .replace(/[\u0300-\u036f]/g, '');
1147
1147
  const hasConfirmIntent = (value = '') => /\b(confirm\w*|confim\w*|cnfirm\w*|cofnirm\w*|ocnfi\w*|ocnfia\w*|fechar)\b/.test(normalizeIntentText(value));
1148
+ const isConversationRecallQuery = (value = '') => {
1149
+ const text = normalizeIntentText(value);
1150
+ return /\b(minha|qual|quais|o que|oq|voce|vc|lembra|lembrar|sabe|diga|fala)\b.{0,80}\b(ultima|ultimas|mensagem|mensagens|pergunta|perguntei|falei|disse)\b/.test(text)
1151
+ || /\b(o que|oq)\b.{0,30}\b(eu)\b.{0,30}\b(falei|disse|perguntei|mandei)\b/.test(text)
1152
+ || /\b(nao|nao)\b.{0,20}\b(lembra|sabe)\b.{0,60}\b(mensagem|pergunta|falei|disse|perguntei)\b/.test(text);
1153
+ };
1154
+ const isAgendaStatusQuery = (value = '') => {
1155
+ const text = normalizeIntentText(value);
1156
+ return /\b(ja|ainda|tinha|tenho|foi|esta|ta)\b.{0,60}\b(agend\w*|agned\w*|ganed\w*|marc\w*|reserv\w*|confirm\w*)\b/.test(text)
1157
+ || /\b(agend\w*|agned\w*|ganed\w*|marc\w*|reserv\w*|confirm\w*)\b.{0,60}\b(ja|ainda|feito|feita|confirmado|confirmada)\b/.test(text);
1158
+ };
1159
+ const recentUserMessages = (recentMessages = []) => [...(recentMessages || [])]
1160
+ .filter((msg) => /^(user|human)$/i.test(String(msg.role || '')))
1161
+ .reverse();
1162
+ const previousUserMessageForQuery = (query = '', recentMessages = []) => {
1163
+ const users = recentUserMessages(recentMessages);
1164
+ const normalizedQuery = normalizeIntentText(query).trim();
1165
+ const currentIndex = users.findIndex((msg) => normalizeIntentText(msg.content).trim() === normalizedQuery);
1166
+ if (currentIndex === 0)
1167
+ return users[1] || null;
1168
+ return users[0] || null;
1169
+ };
1170
+ const buildCurrentTurnFocus = ({ query = '', recentMessages = [], operationalState = {}, workingMemory = {} }) => {
1171
+ const recall = isConversationRecallQuery(query);
1172
+ const agendaStatus = isAgendaStatusQuery(query);
1173
+ if (!recall && !agendaStatus)
1174
+ return null;
1175
+ const previousUser = previousUserMessageForQuery(query, recentMessages);
1176
+ const users = recentUserMessages(recentMessages)
1177
+ .filter((msg) => normalizeIntentText(msg.content).trim() !== normalizeIntentText(query).trim())
1178
+ .slice(0, 3)
1179
+ .map((msg) => ({ role: msg.role, content: truncate(msg.content, 500), at: msg.at }));
1180
+ const agenda = (operationalState || {}).agenda_state || {};
1181
+ return cleanContextValue({
1182
+ current_user_request: truncate(query, 500),
1183
+ intent: recall ? 'conversation_recall' : 'agenda_status_question',
1184
+ previous_user_message: previousUser ? truncate(previousUser.content, 500) : (workingMemory === null || workingMemory === void 0 ? void 0 : workingMemory.last_user_message) || null,
1185
+ recent_user_messages: users,
1186
+ agenda_status: agendaStatus ? {
1187
+ reservation_status: agenda.has_confirmation && !agenda.has_pending_pre_reservation
1188
+ ? 'confirmed'
1189
+ : agenda.has_pending_pre_reservation
1190
+ ? 'pending_pre_reservation'
1191
+ : agenda.has_pre_reservation
1192
+ ? 'pre_reserved'
1193
+ : agenda.has_availability
1194
+ ? 'availability_known_not_scheduled'
1195
+ : 'none',
1196
+ has_confirmation: Boolean(agenda.has_confirmation && !agenda.has_pending_pre_reservation),
1197
+ has_pending_pre_reservation: Boolean(agenda.has_pending_pre_reservation),
1198
+ has_availability: Boolean(agenda.has_availability),
1199
+ } : undefined,
1200
+ instruction: recall
1201
+ ? 'Answer the user meta-question from previous_user_message/recent_user_messages. Do not answer with agenda availability unless the user asks for availability.'
1202
+ : 'Answer whether the appointment/reservation is already scheduled using agenda_status. Do not offer availability as the main answer unless there is no schedule/reservation state.',
1203
+ });
1204
+ };
1148
1205
  const inferToolGuard = ({ query, recentMessages, toolHistory, vectorMemories }) => {
1149
1206
  const text = normalizeIntentText(query);
1150
1207
  const names = new Set((toolHistory || []).map((tool) => String(tool.name || '')));
@@ -1174,6 +1231,10 @@ const inferUserIntent = (query = '', recentMessages = []) => {
1174
1231
  const rawText = `${query || ''}\n${latestUser?.content || ''}`;
1175
1232
  const text = normalizeIntentText(rawText);
1176
1233
  const normalizedQuery = normalizeIntentText(query).trim();
1234
+ if (isConversationRecallQuery(query))
1235
+ return 'conversation_recall';
1236
+ if (isAgendaStatusQuery(query))
1237
+ return 'agenda_status_question';
1177
1238
  if (/^(ok|sim|pode|pode sim|isso|isso mesmo|confirmo|confirmar)$/.test(normalizedQuery))
1178
1239
  return 'affirm';
1179
1240
  if (hasConfirmIntent(text))
@@ -1222,6 +1283,10 @@ const deriveNextExpectedAction = (intent, operationalState = {}) => {
1222
1283
  return 'inspect current reservation state before changing or cancelling';
1223
1284
  if (intent === 'profile_update')
1224
1285
  return 'save stable profile facts and continue the conversation';
1286
+ if (intent === 'conversation_recall')
1287
+ return 'answer directly using recent_messages.previous_user_message; do not call agenda tools';
1288
+ if (intent === 'agenda_status_question')
1289
+ return 'answer whether there is a confirmed or pending reservation using operational_state; do not consult availability unless status is unknown';
1225
1290
  return 'answer using retrieved context and avoid unnecessary tool calls';
1226
1291
  };
1227
1292
  const deriveWorkingMemory = ({ query = '', profileFacts = {}, recentMessages = [], toolHistory = [], operationalState = {}, previous = {} }) => {
@@ -1789,6 +1854,14 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1789
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.',
1790
1855
  });
1791
1856
  }
1857
+ const currentTurnFocus = buildCurrentTurnFocus({ query, recentMessages, operationalState, workingMemory });
1858
+ if (currentTurnFocus) {
1859
+ sections.push({
1860
+ section: 'current_turn_focus',
1861
+ title: 'Current turn focus',
1862
+ value: currentTurnFocus,
1863
+ });
1864
+ }
1792
1865
  if (compactForAgent) {
1793
1866
  if (includeSummary) {
1794
1867
  const summary = compactVectorMemoriesForAgent(vectorMemories, toolHistory, Number(adv.summaryMaxFacts || 3));
@@ -1838,6 +1911,17 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
1838
1911
  value: adv.includeMemoryCompression === false ? null : compactMemoryCompressionForAgent(memoryCompression || {}),
1839
1912
  why_null: adv.includeMemoryCompression === false ? 'memory compression disabled' : undefined,
1840
1913
  });
1914
+ if (adv.includeRecentMessages !== false || currentTurnFocus) {
1915
+ sections.push({
1916
+ section: 'recent_messages',
1917
+ title: 'Recent messages',
1918
+ value: pruneByLimit(recentMessages || [], adv.recentMessagesLastN || 4).map((message) => cleanContextValue({
1919
+ role: message.role,
1920
+ content: truncate(message.content, 260),
1921
+ at: message.at,
1922
+ })),
1923
+ });
1924
+ }
1841
1925
  const audit = {
1842
1926
  kind: 'tembory.context.v1',
1843
1927
  meta: { user_id: userId, payloadFormat, query, compact_for_agent: true },
@@ -2261,6 +2345,7 @@ class Mem0Memory {
2261
2345
  type: 'collection',
2262
2346
  placeholder: 'Opções',
2263
2347
  default: {},
2348
+ displayOptions: { hide: { '/payloadFormat': ['structured', 'auditText', 'auditJson', 'auditBlocks', 'singleSystemMessage'] } },
2264
2349
  description: 'Campos legados preservados para workflows existentes. Para novas configuracoes, use Configuração Tembory.',
2265
2350
  options: [
2266
2351
  { displayName: 'User ID', name: 'userId', type: 'string', default: '' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
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",