n8n-nodes-tembory 1.1.25 → 1.1.26

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.
@@ -1859,21 +1859,73 @@ const deriveNextExpectedAction = (intent, operationalState = {}) => {
1859
1859
  return 'continue according to the agent prompt using retrieved context; call tools when the agent prompt or tool policy requires them';
1860
1860
  };
1861
1861
  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 || ''));
1862
+ const shouldCarryPreviousGoal = (intent = '', previousGoal = '') => {
1863
+ if (!previousGoal || isGenericMemoryNextAction(previousGoal))
1864
+ return false;
1865
+ return ['general_message', 'profile_update', 'unknown'].includes(intent);
1866
+ };
1862
1867
  const deriveWorkingMemory = ({ query = '', profileFacts = {}, recentMessages = [], toolHistory = [], operationalState = {}, previous = {} }) => {
1863
1868
  const intent = inferUserIntent(query, recentMessages);
1864
- const lastUser = [...(recentMessages || [])].reverse().find((msg) => /^(user|human)$/i.test(String(msg.role || '')));
1869
+ const chronological = sortConversationChronological(recentMessages || []);
1870
+ const userMessages = chronological.filter((msg) => /^(user|human)$/i.test(String(msg.role || '')));
1871
+ const assistantMessages = chronological.filter((msg) => /^(assistant|ai|agent)$/i.test(String(msg.role || '')));
1872
+ const normalizedQuery = normalizeIntentText(query).trim();
1873
+ const lastUser = [...userMessages].reverse().find((msg) => /^(user|human)$/i.test(String(msg.role || '')));
1874
+ const priorUser = [...userMessages].reverse().find((msg) => normalizeIntentText(msg.content).trim() !== normalizedQuery);
1875
+ const lastAssistant = assistantMessages[assistantMessages.length - 1] || null;
1865
1876
  const activeEntities = [];
1866
1877
  for (const key of ['name', 'company', 'email', 'phone']) {
1867
1878
  if (profileFacts && profileFacts[key])
1868
1879
  activeEntities.push({ type: key, value: profileFacts[key] });
1869
1880
  }
1870
1881
  const lastTool = toolHistory && toolHistory.length ? toolHistory[toolHistory.length - 1] : null;
1882
+ const toolState = (operationalState || {}).tool_state || {};
1871
1883
  const nextExpectedAction = deriveNextExpectedAction(intent, operationalState);
1884
+ const carryPreviousGoal = shouldCarryPreviousGoal(intent, previous.current_goal);
1885
+ const currentGoal = carryPreviousGoal ? previous.current_goal : nextExpectedAction;
1886
+ const toolNames = Array.from(new Set((toolHistory || []).map((tool) => tool.name || tool.tool_name || tool.tool).filter(Boolean)));
1887
+ const currentTurnMayNeedTool = ['affirm', 'commit_or_continue', 'tool_action_candidate', 'selection_or_slot'].includes(intent);
1872
1888
  return {
1873
- current_goal: previous.current_goal && !isGenericMemoryNextAction(previous.current_goal) ? previous.current_goal : nextExpectedAction,
1889
+ current_goal: currentGoal,
1890
+ current_goal_reason: carryPreviousGoal ? 'carried_from_previous_non_generic_goal' : 'derived_from_current_turn_intent',
1874
1891
  current_task: nextExpectedAction,
1875
1892
  last_user_intent: intent,
1876
1893
  last_user_message: lastUser ? truncate(lastUser.content, 500) : truncate(query, 500),
1894
+ conversation_digest: cleanContextValue({
1895
+ messages: chronological.length,
1896
+ user_messages: userMessages.length,
1897
+ assistant_messages: assistantMessages.length,
1898
+ current_user: truncate(query, 220),
1899
+ prior_user: priorUser ? { content: truncate(priorUser.content, 180), at: priorUser.at } : undefined,
1900
+ last_assistant: lastAssistant ? { content: truncate(lastAssistant.content, 180), at: lastAssistant.at } : undefined,
1901
+ }),
1902
+ tool_digest: cleanContextValue({
1903
+ total: Array.isArray(toolHistory) ? toolHistory.length : 0,
1904
+ ok: (operationalState || {}).tool_counts?.ok,
1905
+ failed: (operationalState || {}).tool_counts?.failed,
1906
+ names: toolNames.slice(0, 12),
1907
+ last_successful: toolState.last_successful_tool ? {
1908
+ name: toolState.last_successful_tool.name,
1909
+ at: toolState.last_successful_tool.at,
1910
+ status: toolState.last_successful_tool.status || 'ok',
1911
+ } : undefined,
1912
+ failed_by_name: toolState.failed_by_name,
1913
+ }),
1914
+ turn_flags: cleanContextValue({
1915
+ recall_only: intent === 'conversation_recall',
1916
+ status_question: intent === 'operational_status_question',
1917
+ current_turn_may_need_tool: currentTurnMayNeedTool,
1918
+ has_prior_tools: Array.isArray(toolHistory) && toolHistory.length > 0,
1919
+ has_profile_facts: activeEntities.length > 0,
1920
+ carry_previous_goal: carryPreviousGoal,
1921
+ }),
1922
+ context_sources: cleanContextValue({
1923
+ conversation: chronological.length > 0,
1924
+ tool_history: Array.isArray(toolHistory) && toolHistory.length > 0,
1925
+ operational_state: Boolean((operationalState || {}).tool_state || (operationalState || {}).tool_counts),
1926
+ profile_facts: activeEntities.length > 0,
1927
+ }),
1928
+ agent_guidance: nextExpectedAction,
1877
1929
  active_entities: activeEntities,
1878
1930
  open_decisions: previous.open_decisions || [],
1879
1931
  last_error: lastTool && lastTool.ok === false ? { tool: lastTool.name, at: lastTool.at, result: lastTool.result } : null,
@@ -2148,9 +2200,15 @@ const cleanContextValue = (value) => {
2148
2200
  };
2149
2201
  const compactWorkingMemoryForAgent = (memory = {}) => cleanContextValue({
2150
2202
  current_goal: memory.current_goal,
2203
+ current_goal_reason: memory.current_goal_reason,
2151
2204
  current_task: memory.current_task,
2152
2205
  last_user_intent: memory.last_user_intent,
2153
2206
  last_user_message: truncate(memory.last_user_message, 220),
2207
+ conversation_digest: memory.conversation_digest,
2208
+ tool_digest: memory.tool_digest,
2209
+ turn_flags: memory.turn_flags,
2210
+ context_sources: memory.context_sources,
2211
+ agent_guidance: memory.agent_guidance,
2154
2212
  active_entities: memory.active_entities,
2155
2213
  open_decisions: memory.open_decisions,
2156
2214
  last_error: memory.last_error,
@@ -2543,10 +2601,16 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2543
2601
  });
2544
2602
  summary.workingMemory = cleanContextValue({
2545
2603
  currentGoal: parsed.workingMemory?.current_goal,
2604
+ currentGoalReason: parsed.workingMemory?.current_goal_reason,
2546
2605
  currentTask: parsed.workingMemory?.current_task,
2547
2606
  nextExpectedAction: parsed.workingMemory?.next_expected_action,
2548
2607
  lastUserIntent: parsed.workingMemory?.last_user_intent,
2549
2608
  lastUserMessage: parsed.workingMemory?.last_user_message ? truncate(parsed.workingMemory.last_user_message, 220) : undefined,
2609
+ conversation: parsed.workingMemory?.conversation_digest,
2610
+ tools: parsed.workingMemory?.tool_digest,
2611
+ turnFlags: parsed.workingMemory?.turn_flags,
2612
+ contextSources: parsed.workingMemory?.context_sources,
2613
+ agentGuidance: parsed.workingMemory?.agent_guidance,
2550
2614
  });
2551
2615
  summary.decisionState = cleanContextValue({
2552
2616
  currentIntent: parsed.decisionState?.current_intent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.1.25",
3
+ "version": "1.1.26",
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",