n8n-nodes-tembory 1.1.23 → 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.
@@ -143,6 +143,48 @@ const asSearchQuery = (value) => {
143
143
  return String(value);
144
144
  return pickText(value, ['query', 'input', 'chatInput', 'text', 'message', 'consolidated_text', 'lastUserMessage']);
145
145
  };
146
+ const currentInputJsonFromContext = (ctx, itemIndex = 0) => {
147
+ try {
148
+ const inputData = typeof (ctx === null || ctx === void 0 ? void 0 : ctx.getInputData) === 'function' ? ctx.getInputData() : [];
149
+ const item = (Array.isArray(inputData) && (inputData[itemIndex] || inputData[0])) || {};
150
+ return item && typeof item.json === 'object' && item.json ? item.json : {};
151
+ }
152
+ catch {
153
+ return {};
154
+ }
155
+ };
156
+ const resolveCurrentTurnQuery = (ctx, itemIndex = 0, inputValues = {}, queryParam = '') => {
157
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
158
+ const inputJson = currentInputJsonFromContext(ctx, itemIndex);
159
+ const candidates = [
160
+ queryParam,
161
+ inputValues.query,
162
+ inputValues.input,
163
+ inputValues.chatInput,
164
+ inputValues.text,
165
+ inputValues.question,
166
+ inputValues.message,
167
+ inputJson.query,
168
+ inputJson.lastUserMessage,
169
+ inputJson.chatInput,
170
+ inputJson.input,
171
+ inputJson.text,
172
+ inputJson.message,
173
+ (_a = inputJson.body) === null || _a === void 0 ? void 0 : _a.consolidated_text,
174
+ (_b = inputJson.body) === null || _b === void 0 ? void 0 : _b.chatInput,
175
+ (_c = inputJson.body) === null || _c === void 0 ? void 0 : _c.message,
176
+ (_d = inputJson.body) === null || _d === void 0 ? void 0 : _d.text,
177
+ (_e = inputJson.body) === null || _e === void 0 ? void 0 : _e.input,
178
+ (_h = (_g = (_f = inputJson.body) === null || _f === void 0 ? void 0 : _f.messages) === null || _g === void 0 ? void 0 : _g[0]) === null || _h === void 0 ? void 0 : _h.text,
179
+ (_k = (_j = inputJson.body) === null || _j === void 0 ? void 0 : _j.messages) === null || _k === void 0 ? void 0 : _k[0],
180
+ ];
181
+ for (const candidate of candidates) {
182
+ const query = asSearchQuery(candidate).trim();
183
+ if (query)
184
+ return stripThreadTestPrefix(query);
185
+ }
186
+ return '';
187
+ };
146
188
  const memoryText = (memory) => {
147
189
  if (!memory)
148
190
  return '';
@@ -1224,6 +1266,8 @@ const mergeRemoteThreadState = (store, key, state) => {
1224
1266
  store.decisionState[key] = { ...(store.decisionState[key] || {}), ...state.decisionState };
1225
1267
  if (state.memoryCompression && typeof state.memoryCompression === 'object')
1226
1268
  store.memoryCompression[key] = state.memoryCompression;
1269
+ if (state.captureState && typeof state.captureState === 'object')
1270
+ store.captureState[key] = { ...(store.captureState[key] || {}), ...state.captureState };
1227
1271
  if (typeof state.activeSummary === 'string' && state.activeSummary)
1228
1272
  store.activeSummary[key] = state.activeSummary;
1229
1273
  };
@@ -1679,6 +1723,56 @@ const buildCurrentTurnFocus = ({ query = '', recentMessages = [], operationalSta
1679
1723
  });
1680
1724
  return focus;
1681
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
+ };
1682
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.';
1683
1777
  const intentConfidence = (intent = '') => {
1684
1778
  if (['conversation_recall', 'operational_status_question'].includes(intent))
@@ -1745,7 +1839,7 @@ const inferUserIntent = (query = '', recentMessages = []) => {
1745
1839
  return 'selection_or_slot';
1746
1840
  if (hasCommitIntent(text))
1747
1841
  return 'commit_or_continue';
1748
- 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))
1749
1843
  return 'tool_action_candidate';
1750
1844
  if (/\b(meu nome|email|telefone|empresa|prefiro|preferencia)\b/.test(text))
1751
1845
  return 'profile_update';
@@ -2311,13 +2405,22 @@ const compactConversationTimelineForSideChannel = (conversation = {}, maxItems =
2311
2405
  return pruneByLimit(chronological, maxItems).map((message, index) => {
2312
2406
  const role = normalizeConversationRoleForSideChannel(message.role || message.type);
2313
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);
2314
2410
  return cleanContextValue({
2315
2411
  index,
2316
2412
  role,
2317
2413
  speaker: role,
2318
- at: message.at || message.timestamp || message.created_at || message.createdAt,
2414
+ at,
2319
2415
  chars: content.length,
2320
- 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
+ },
2321
2424
  });
2322
2425
  });
2323
2426
  };
@@ -2383,6 +2486,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2383
2486
  summary.payloadFormat = parsed.payloadFormat;
2384
2487
  summary.options = cleanContextValue(parsed.options || {});
2385
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;
2386
2490
  summary.currentUserMessage = conversation.current_user_message ? truncate(conversation.current_user_message, 180) : undefined;
2387
2491
  summary.recentMessages = Array.isArray(conversation.conversation_history_chronological) ? conversation.conversation_history_chronological.length : undefined;
2388
2492
  summary.conversationTimeline = compactConversationTimelineForSideChannel(conversation, 12);
@@ -2398,6 +2502,16 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2398
2502
  at: tools.last_successful_tool.at,
2399
2503
  }
2400
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;
2401
2515
  summary.counts = cleanContextValue({
2402
2516
  conversationMessages: Array.isArray(conversation.conversation_history_chronological) ? conversation.conversation_history_chronological.length : undefined,
2403
2517
  userMessages: Array.isArray(conversation.all_user_messages_chronological) ? conversation.all_user_messages_chronological.length : undefined,
@@ -2419,6 +2533,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2419
2533
  memoryCompression: Boolean(parsed.memoryCompression),
2420
2534
  operationalState: Boolean(parsed.operationalState),
2421
2535
  actionLedger: Array.isArray(parsed.actionLedger),
2536
+ turnBrief: Boolean(parsed.turnBrief || parsed.turn_brief),
2422
2537
  memoryAudit: Boolean(memoryAudit && Object.keys(memoryAudit).length),
2423
2538
  entityTimeline: Array.isArray(parsed.entityTimeline),
2424
2539
  vectorMemories: Array.isArray(parsed.vectorMemories),
@@ -2538,6 +2653,12 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2538
2653
  value: currentTurnFocus,
2539
2654
  });
2540
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
+ });
2541
2662
  const actionDirective = buildActionDirective({ workingMemory, operationalState });
2542
2663
  if (actionDirective) {
2543
2664
  sections.push({
@@ -2635,6 +2756,10 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2635
2756
  const directive = sectionValue('action_directive');
2636
2757
  const inferredIntent = deriveUserIntentObservation({ query, workingMemory, decisionState, recentMessages });
2637
2758
  const memoryAudit = sectionValue('memory_audit');
2759
+ const compactSummary = cleanContextValue({
2760
+ vector_facts: vectorFacts,
2761
+ slm: hasToolLedger ? undefined : slmSummary,
2762
+ });
2638
2763
  const minimalState = cleanContextValue({
2639
2764
  next_expected_action: directive ? undefined : workingMemory.next_expected_action,
2640
2765
  context_quality_score: diagnostics?.contextHealth?.quality_score || diagnostics?.quality_score,
@@ -2653,13 +2778,11 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2653
2778
  next_expected_action: directive.next_expected_action,
2654
2779
  instruction: directive.instruction,
2655
2780
  }) : undefined,
2781
+ turnBrief: compactTurnBriefForAgent(sectionValue('turn_brief')),
2656
2782
  observations: {
2657
2783
  inferred_intent: inferredIntent,
2658
2784
  },
2659
- summary: {
2660
- vector_facts: vectorFacts,
2661
- slm: hasToolLedger ? undefined : slmSummary,
2662
- },
2785
+ summary: Object.keys(compactSummary).length ? compactSummary : undefined,
2663
2786
  state: minimalState,
2664
2787
  workingMemory: sectionValue('working_memory'),
2665
2788
  decisionState: sectionValue('decision_state'),
@@ -2932,12 +3055,7 @@ class TemboryMemory {
2932
3055
  name: 'query',
2933
3056
  type: 'string',
2934
3057
  default: '={{ $json.query || $json.lastUserMessage || $json.chatInput || $json.body?.consolidated_text || $json.body?.message || $json.body?.text || "" }}',
2935
- description: 'Consulta em linguagem natural para recuperar memórias relevantes',
2936
- displayOptions: {
2937
- show: {
2938
- retrievalMode: ['semantic', 'semanticV2', 'hybrid'],
2939
- },
2940
- },
3058
+ description: 'Mensagem atual usada para montar o contexto operacional, inferir intenção e recuperar memórias relevantes. Em AI Agent, mantenha a expressão padrão para ler o chatInput do item.',
2941
3059
  },
2942
3060
  {
2943
3061
  displayName: 'Chave de Memória',
@@ -3294,6 +3412,19 @@ class TemboryMemory {
3294
3412
  body.app_id = String(adv.appId);
3295
3413
  if (adv.runId)
3296
3414
  body.run_id = String(adv.runId);
3415
+ const nextCaptureState = cleanContextValue({
3416
+ ...(store.captureState[key] || {}),
3417
+ last_save_status: 'saved',
3418
+ last_save_saved: true,
3419
+ last_save_conversation_messages_after_save: recentForTurn.length,
3420
+ last_save_tool_history_after_save: toolHistoryForTurn.length,
3421
+ last_save_thread_state_saved: true,
3422
+ last_save_backend_memory_persistence: adv.persistBackendMemories === false
3423
+ ? 'disabled'
3424
+ : adv.useVectorMemory === false
3425
+ ? 'thread_state_only'
3426
+ : 'enabled',
3427
+ });
3297
3428
  const threadStateSaved = await saveThreadState(this, key, threadId, project, {
3298
3429
  kind: 'tembory.thread_state.v1',
3299
3430
  threadId,
@@ -3306,20 +3437,12 @@ class TemboryMemory {
3306
3437
  decisionState: decisionStateForTurn,
3307
3438
  memoryCompression: compressionForTurn,
3308
3439
  operationalState: operationalStateForTurn,
3440
+ captureState: nextCaptureState,
3309
3441
  activeSummary: store.activeSummary[key] || '',
3310
3442
  });
3311
3443
  store.captureState[key] = cleanContextValue({
3312
- ...(store.captureState[key] || {}),
3313
- last_save_status: 'saved',
3314
- last_save_saved: true,
3315
- last_save_conversation_messages_after_save: recentForTurn.length,
3316
- last_save_tool_history_after_save: toolHistoryForTurn.length,
3444
+ ...nextCaptureState,
3317
3445
  last_save_thread_state_saved: threadStateSaved,
3318
- last_save_backend_memory_persistence: adv.persistBackendMemories === false
3319
- ? 'disabled'
3320
- : adv.useVectorMemory === false
3321
- ? 'thread_state_only'
3322
- : 'enabled',
3323
3446
  });
3324
3447
  try {
3325
3448
  const globalData = this.getWorkflowStaticData('global');
@@ -3586,7 +3709,7 @@ class TemboryMemory {
3586
3709
  const store = getMemoryStore(this);
3587
3710
  const key = userKeyFrom(threadId, adv, project);
3588
3711
  const queryParam = this.getNodeParameter('query', itemIndex, '');
3589
- const query = stripThreadTestPrefix(asSearchQuery(queryParam) || asSearchQuery(inputValues.query) || asSearchQuery(inputValues.input) || asSearchQuery(inputValues.chatInput));
3712
+ const query = resolveCurrentTurnQuery(this, itemIndex, inputValues, queryParam);
3590
3713
  const remoteThreadState = await loadThreadState(this, key, threadId, project);
3591
3714
  mergeRemoteThreadState(store, key, remoteThreadState);
3592
3715
  let connectedLanguageModel;
@@ -4030,7 +4153,7 @@ class TemboryMemory {
4030
4153
  last_save_conversation_messages_after_save: 0,
4031
4154
  last_save_tool_history_after_save: 0,
4032
4155
  last_save_thread_state_saved: false,
4033
- last_save_backend_memory_persistence: backendPersistenceEnabled ? 'enabled' : 'disabled',
4156
+ last_save_backend_memory_persistence: backendPersistenceEnabled ? (vectorMemoryEnabled ? 'enabled' : 'thread_state_only') : 'disabled',
4034
4157
  },
4035
4158
  connectedAi,
4036
4159
  activeSummary: summaryDiagnostics,
@@ -4288,12 +4411,15 @@ exports.__private = {
4288
4411
  applyOperationalPreset,
4289
4412
  flattenAdvancedGroups,
4290
4413
  asSearchQuery,
4414
+ currentInputJsonFromContext,
4415
+ resolveCurrentTurnQuery,
4291
4416
  safePersistLegacyMemory,
4292
4417
  stripThreadTestPrefix,
4293
4418
  canonicalToolInput,
4294
4419
  buildContextMessages,
4295
4420
  inferToolGuard,
4296
4421
  inferUserIntent,
4422
+ buildTurnBriefForAgent,
4297
4423
  deriveUserIntentObservation,
4298
4424
  deriveWorkingMemory,
4299
4425
  deriveDecisionState,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.1.23",
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",