n8n-nodes-tembory 1.0.46 → 1.0.48

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.
@@ -1238,10 +1238,11 @@ const applyOperationalPreset = (advanced = {}) => {
1238
1238
  includeConnectedModelSummary: true,
1239
1239
  includeActiveSummary: true,
1240
1240
  persistActiveSummary: true,
1241
- activeSummaryMaxChars: 1800,
1241
+ activeSummaryMaxChars: 1200,
1242
1242
  activeSummaryRetentionDays: 30,
1243
1243
  enableTransientSummaryCache: true,
1244
1244
  transientSummaryCacheTTLSeconds: 300,
1245
+ compactForAgent: true,
1245
1246
  compactStateSections: true,
1246
1247
  useVectorMemory: false,
1247
1248
  persistBackendMemories: true,
@@ -1271,9 +1272,9 @@ const applyOperationalPreset = (advanced = {}) => {
1271
1272
  toolHistoryLastN: 15,
1272
1273
  recentMessagesLastN: 50,
1273
1274
  vectorMemoryMaxChars: 220,
1274
- contextMaxChars: 9000,
1275
- connectedModelSummaryMaxChars: 1200,
1276
- connectedModelSummaryInputMaxChars: 4200,
1275
+ contextMaxChars: 6000,
1276
+ connectedModelSummaryMaxChars: 900,
1277
+ connectedModelSummaryInputMaxChars: 3600,
1277
1278
  },
1278
1279
  diagnostic: {
1279
1280
  summarySource: 'auto',
@@ -1310,6 +1311,7 @@ const applyOperationalPreset = (advanced = {}) => {
1310
1311
  activeSummaryRetentionDays: 30,
1311
1312
  enableTransientSummaryCache: true,
1312
1313
  transientSummaryCacheTTLSeconds: 300,
1314
+ compactForAgent: true,
1313
1315
  compactStateSections: true,
1314
1316
  includeContextHeader: true,
1315
1317
  includeSummary: true,
@@ -1347,6 +1349,7 @@ const applyOperationalPreset = (advanced = {}) => {
1347
1349
  activeSummaryRetentionDays: 30,
1348
1350
  enableTransientSummaryCache: true,
1349
1351
  transientSummaryCacheTTLSeconds: 300,
1352
+ compactForAgent: true,
1350
1353
  compactStateSections: true,
1351
1354
  includeContextHeader: true,
1352
1355
  includeSummary: false,
@@ -1441,7 +1444,12 @@ const applyOperationalPreset = (advanced = {}) => {
1441
1444
  payloadFormat: 'auditJson',
1442
1445
  },
1443
1446
  };
1444
- return { ...(presets[preset === 'lab' ? 'diagnostic' : preset] || {}), ...advanced };
1447
+ const resolved = { ...(presets[preset === 'lab' ? 'diagnostic' : preset] || {}), ...advanced };
1448
+ if (resolved.useVectorMemory === false) {
1449
+ resolved.includeRelations = false;
1450
+ resolved.includeEntityTimeline = false;
1451
+ }
1452
+ return resolved;
1445
1453
  };
1446
1454
  const flattenAdvancedGroups = (groups = {}) => {
1447
1455
  const flattened = {};
@@ -1618,13 +1626,8 @@ const buildConversationFrame = ({ query = '', recentMessages = [] }) => {
1618
1626
  const frame = cleanContextValue({
1619
1627
  current_user_message: currentUser ? truncate(currentUser.content, 500) : truncate(query, 500),
1620
1628
  conversation_history_chronological: chronological,
1621
- recent_messages_chronological: chronological,
1622
1629
  all_user_messages_chronological: userMessagesChronological,
1623
- recent_user_messages: users
1624
- .filter((msg) => normalizeIntentText(msg.content).trim() !== normalizedQuery)
1625
- .slice(0, 5)
1626
- .map((msg) => ({ role: msg.role, content: truncate(msg.content, 500), at: msg.at })),
1627
- instruction: 'This is the authoritative short-term conversation frame. If the user asks about current/previous/last client messages or what they already said, answer from conversation_history_chronological/all_user_messages_chronological before using vector memories, summaries, operational state, or tool history. For the last message before the current one, use the user message immediately before current_user_message in all_user_messages_chronological.',
1630
+ instruction: 'Authoritative short-term transcript. For recall questions, answer from conversation_history_chronological/all_user_messages_chronological before summaries or tool history.',
1628
1631
  });
1629
1632
  return frame;
1630
1633
  };
@@ -1911,7 +1914,26 @@ const contextMemoryText = (memory, max = 700) => {
1911
1914
  return truncate(text, max);
1912
1915
  };
1913
1916
  const approxTokenCount = (text) => Math.ceil(String(text || '').length / 4);
1914
- const importantJsonFields = ['id', 'status', 'state', 'next_step', 'action', 'intent', 'tool', 'input', 'output', 'result', 'error', 'message', 'reason', 'customer_id', 'user_id', 'lead_id', 'ticket_id', 'charge_id'];
1917
+ const noisyToolFields = new Set(['requested_from', 'thread', 'thread_id', 'run_id', 'dedupe_key', 'args_hash', 'result_hash', 'source']);
1918
+ const importantJsonFields = ['id', 'status', 'state', 'next_step', 'action', 'intent', 'tool', 'input', 'output', 'result', 'error', 'message', 'reason', 'customer_id', 'user_id', 'lead_id', 'ticket_id', 'charge_id', 'reservation_id', 'confirmation_id', 'selected_from_message', 'available_slots', 'timezone', 'name', 'phone', 'segmento', 'platform', 'plataforma', 'lifecycle_stage', 'provider', 'sku', 'product', 'stock', 'queue', 'priority'];
1919
+ const stripNoisyToolFields = (value, depth = 0) => {
1920
+ if (depth > 8)
1921
+ return undefined;
1922
+ if (Array.isArray(value))
1923
+ return value.map((item) => stripNoisyToolFields(item, depth + 1)).filter((item) => item !== undefined);
1924
+ if (!value || typeof value !== 'object')
1925
+ return value;
1926
+ const out = {};
1927
+ for (const [key, item] of Object.entries(value)) {
1928
+ if (noisyToolFields.has(String(key)))
1929
+ continue;
1930
+ const cleaned = stripNoisyToolFields(item, depth + 1);
1931
+ if (cleaned === undefined)
1932
+ continue;
1933
+ out[key] = cleaned;
1934
+ }
1935
+ return out;
1936
+ };
1915
1937
  const pickImportantFields = (value) => {
1916
1938
  if (value === null || value === undefined)
1917
1939
  return {};
@@ -1924,7 +1946,7 @@ const pickImportantFields = (value) => {
1924
1946
  return;
1925
1947
  }
1926
1948
  for (const key of importantJsonFields) {
1927
- if (item[key] !== undefined && out[key] === undefined)
1949
+ if (!noisyToolFields.has(key) && item[key] !== undefined && out[key] === undefined)
1928
1950
  out[key] = item[key];
1929
1951
  }
1930
1952
  if (item.args)
@@ -1940,12 +1962,15 @@ const compactToolResult = (result, max = 360) => {
1940
1962
  if (!text.trim())
1941
1963
  return undefined;
1942
1964
  try {
1943
- const parsed = JSON.parse(text);
1944
- return truncate(safeStringify(parsed), max);
1965
+ const parsed = stripNoisyToolFields(JSON.parse(text));
1966
+ const picked = pickImportantFields(parsed);
1967
+ return truncate(safeStringify(Object.keys(picked).length ? picked : parsed), max);
1945
1968
  }
1946
1969
  catch { }
1947
1970
  const picked = {};
1948
1971
  for (const key of importantJsonFields) {
1972
+ if (noisyToolFields.has(key))
1973
+ continue;
1949
1974
  const re = new RegExp(`["']?${key}["']?\\s*[:=]\\s*["']?([^"',}\\]]+)`, 'i');
1950
1975
  const match = re.exec(text);
1951
1976
  if (match)
@@ -1956,7 +1981,6 @@ const compactToolResult = (result, max = 360) => {
1956
1981
  return truncate(text, max);
1957
1982
  };
1958
1983
  const compactToolHistoryForAgent = (toolHistory = [], maxItems = 6, includeResults = true) => pruneByLimit(toolHistory || [], maxItems).map((tool) => ({
1959
- id: tool.call_id || tool.id,
1960
1984
  name: tool.name,
1961
1985
  status: tool.ok === false ? 'failed' : 'ok',
1962
1986
  at: tool.at,
@@ -2040,7 +2064,6 @@ const compactMemoryCompressionForAgent = (compression = {}) => ({
2040
2064
  active_memory_count: ((compression.workflow_summary || {}).active_memory_count) || 0,
2041
2065
  });
2042
2066
  const compactActionLedgerForAgent = (ledger = [], maxItems = 6, includeResults = true) => pruneByLimit(ledger || [], maxItems).map((item) => cleanContextValue({
2043
- action_id: item.action_id,
2044
2067
  kind: item.kind,
2045
2068
  tool: item.tool || item.name,
2046
2069
  status: item.status,
@@ -2307,32 +2330,20 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2307
2330
  sections.push({ section: 'summary', title: 'Summary', value: summary.length ? summary : null, why_null: summary.length ? undefined : 'no non-tool vector memories to summarize' });
2308
2331
  }
2309
2332
  if (connectedModelSummary && adv.includeConnectedModelSummary !== false) {
2310
- sections.push({ section: 'connected_model_summary', title: 'SLM summary', value: connectedModelSummary });
2333
+ sections.push({ section: 'connected_model_summary', title: 'SLM summary', value: truncate(connectedModelSummary, Number(adv.connectedModelSummaryMaxChars || 900)) });
2311
2334
  }
2312
2335
  else if (activeSummary && adv.includeActiveSummary !== false) {
2313
- sections.push({ section: 'active_summary', title: 'Active summary', value: activeSummary });
2336
+ sections.push({ section: 'active_summary', title: 'Active summary', value: truncate(activeSummary, Number(adv.activeSummaryMaxChars || 900)) });
2314
2337
  }
2315
2338
  sections.push({
2316
2339
  section: 'working_memory',
2317
2340
  title: 'Working memory',
2318
- value: adv.includeWorkingMemory === false ? null : (workingMemory || {}),
2341
+ value: adv.includeWorkingMemory === false ? null : compactWorkingMemoryForAgent(workingMemory || {}),
2319
2342
  why_null: adv.includeWorkingMemory === false ? 'working memory disabled' : undefined,
2320
2343
  });
2321
2344
  sections.push({
2322
- section: 'decision_state',
2323
- title: 'Decision state',
2324
- value: adv.includeDecisionState === false ? null : (decisionState || {}),
2325
- why_null: adv.includeDecisionState === false ? 'decision state disabled' : undefined,
2326
- });
2327
- sections.push({
2328
- section: 'operational_state',
2329
- title: 'Operational state',
2330
- value: adv.includeOperationalState === false ? null : compactOperationalStateForAgent(operationalState || {}),
2331
- why_null: adv.includeOperationalState === false ? 'operational state disabled' : undefined,
2332
- });
2333
- sections.push({
2334
- section: 'tool_history',
2335
- title: 'Tool history',
2345
+ section: 'tool_ledger',
2346
+ title: 'Tool ledger',
2336
2347
  value: {
2337
2348
  enabled: adv.includeToolHistory !== false,
2338
2349
  items: adv.includeToolHistory === false ? [] : compactToolHistoryForAgent(toolHistory, adv.toolHistoryLastN || 6, adv.includeToolResults !== false),
@@ -2345,22 +2356,15 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2345
2356
  why_null: adv.includeProfileFacts === false ? 'profile facts disabled' : undefined,
2346
2357
  });
2347
2358
  sections.push({
2348
- section: 'memory_compression',
2349
- title: 'Memory compression',
2350
- value: adv.includeMemoryCompression === false ? null : compactMemoryCompressionForAgent(memoryCompression || {}),
2351
- why_null: adv.includeMemoryCompression === false ? 'memory compression disabled' : undefined,
2359
+ section: 'next_action',
2360
+ title: 'Next action',
2361
+ value: cleanContextValue({
2362
+ current_intent: (decisionState || {}).current_intent || workingMemory.last_user_intent,
2363
+ latest_tool: ((decisionState || {}).latest_tool || (operationalState || {}).last_tool || undefined),
2364
+ do_not_repeat_tools: ((decisionState || {}).do_not_repeat_tools || []).slice(0, 12),
2365
+ instruction: actionDirective || workingMemory.next_expected_action || 'Continue according to the agent prompt.',
2366
+ }),
2352
2367
  });
2353
- if (adv.includeRecentMessages !== false || currentTurnFocus) {
2354
- sections.push({
2355
- section: 'recent_messages',
2356
- title: 'Recent messages',
2357
- value: pruneByLimit(recentMessages || [], adv.recentMessagesLastN || 4).map((message) => cleanContextValue({
2358
- role: message.role,
2359
- content: truncate(message.content, 260),
2360
- at: message.at,
2361
- })),
2362
- });
2363
- }
2364
2368
  const audit = {
2365
2369
  kind: 'tembory.context.v1',
2366
2370
  meta: { user_id: userId, payloadFormat, query, compact_for_agent: true },
@@ -2380,7 +2384,18 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2380
2384
  return [{ role: 'system', content: JSON.stringify(audit, null, 2) }];
2381
2385
  if (payloadFormat === 'auditBlocks')
2382
2386
  return sections.map((section) => ({ role: 'system', content: renderCompactSection(section) }));
2383
- return [{ role: 'system', content: truncate(sections.map(renderCompactSection).join('\n\n'), Number(adv.contextMaxChars || 6000)) }];
2387
+ const renderableSections = sections.filter((section) => {
2388
+ if (section.section === 'summary' && !section.value)
2389
+ return false;
2390
+ if (section.value === null || section.value === undefined)
2391
+ return false;
2392
+ if (Array.isArray(section.value) && section.value.length === 0)
2393
+ return false;
2394
+ if (typeof section.value === 'object' && !Array.isArray(section.value) && Object.keys(section.value).length === 0)
2395
+ return false;
2396
+ return true;
2397
+ });
2398
+ return [{ role: 'system', content: truncate(renderableSections.map(renderCompactSection).join('\n\n'), Number(adv.contextMaxChars || 6000)) }];
2384
2399
  }
2385
2400
  if (includeSummary) {
2386
2401
  const summary = compactStateSections
@@ -3122,8 +3137,6 @@ class Mem0Memory {
3122
3137
  body.app_id = String(adv.appId);
3123
3138
  if (adv.runId)
3124
3139
  body.run_id = String(adv.runId);
3125
- if (adv.persistBackendMemories === false)
3126
- return;
3127
3140
  await saveThreadState(this, key, threadId, project, {
3128
3141
  kind: 'tembory.thread_state.v1',
3129
3142
  threadId,
@@ -3138,6 +3151,8 @@ class Mem0Memory {
3138
3151
  operationalState: operationalStateForTurn,
3139
3152
  activeSummary: store.activeSummary[key] || '',
3140
3153
  });
3154
+ if (adv.persistBackendMemories === false)
3155
+ return;
3141
3156
  if (adv.useVectorMemory === false)
3142
3157
  return;
3143
3158
  const connectedEmbedding = await getConnectedEmbedding(this, itemIndex);
@@ -3372,7 +3387,7 @@ class Mem0Memory {
3372
3387
  const key = userKeyFrom(threadId, adv, project);
3373
3388
  const queryParam = this.getNodeParameter('query', itemIndex, '');
3374
3389
  const query = stripThreadTestPrefix(asSearchQuery(queryParam) || asSearchQuery(inputValues.query) || asSearchQuery(inputValues.input) || asSearchQuery(inputValues.chatInput));
3375
- const remoteThreadState = backendPersistenceEnabled ? await loadThreadState(this, key, threadId, project) : null;
3390
+ const remoteThreadState = await loadThreadState(this, key, threadId, project);
3376
3391
  mergeRemoteThreadState(store, key, remoteThreadState);
3377
3392
  let connectedLanguageModel;
3378
3393
  let connectedEmbedding;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.0.46",
3
+ "version": "1.0.48",
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",