n8n-nodes-tembory 1.0.47 → 1.0.49

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,
@@ -1623,13 +1626,8 @@ const buildConversationFrame = ({ query = '', recentMessages = [] }) => {
1623
1626
  const frame = cleanContextValue({
1624
1627
  current_user_message: currentUser ? truncate(currentUser.content, 500) : truncate(query, 500),
1625
1628
  conversation_history_chronological: chronological,
1626
- recent_messages_chronological: chronological,
1627
1629
  all_user_messages_chronological: userMessagesChronological,
1628
- recent_user_messages: users
1629
- .filter((msg) => normalizeIntentText(msg.content).trim() !== normalizedQuery)
1630
- .slice(0, 5)
1631
- .map((msg) => ({ role: msg.role, content: truncate(msg.content, 500), at: msg.at })),
1632
- 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.',
1633
1631
  });
1634
1632
  return frame;
1635
1633
  };
@@ -1656,7 +1654,11 @@ const buildCurrentTurnFocus = ({ query = '', recentMessages = [], operationalSta
1656
1654
  all_user_messages_chronological: recall ? userMessagesChronological : undefined,
1657
1655
  tool_status: operationalStatus ? {
1658
1656
  executed_tools: toolState.names || [],
1659
- last_successful_tool: toolState.last_successful_tool || null,
1657
+ last_successful_tool: toolState.last_successful_tool ? {
1658
+ name: toolState.last_successful_tool.name,
1659
+ at: toolState.last_successful_tool.at,
1660
+ status: toolState.last_successful_tool.status || (toolState.last_successful_tool.ok === false ? 'failed' : 'ok'),
1661
+ } : null,
1660
1662
  failed_by_name: toolState.failed_by_name || {},
1661
1663
  } : undefined,
1662
1664
  instruction: recall
@@ -1916,7 +1918,26 @@ const contextMemoryText = (memory, max = 700) => {
1916
1918
  return truncate(text, max);
1917
1919
  };
1918
1920
  const approxTokenCount = (text) => Math.ceil(String(text || '').length / 4);
1919
- 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'];
1921
+ const noisyToolFields = new Set(['requested_from', 'thread', 'thread_id', 'run_id', 'dedupe_key', 'args_hash', 'result_hash', 'source']);
1922
+ 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'];
1923
+ const stripNoisyToolFields = (value, depth = 0) => {
1924
+ if (depth > 8)
1925
+ return undefined;
1926
+ if (Array.isArray(value))
1927
+ return value.map((item) => stripNoisyToolFields(item, depth + 1)).filter((item) => item !== undefined);
1928
+ if (!value || typeof value !== 'object')
1929
+ return value;
1930
+ const out = {};
1931
+ for (const [key, item] of Object.entries(value)) {
1932
+ if (noisyToolFields.has(String(key)))
1933
+ continue;
1934
+ const cleaned = stripNoisyToolFields(item, depth + 1);
1935
+ if (cleaned === undefined)
1936
+ continue;
1937
+ out[key] = cleaned;
1938
+ }
1939
+ return out;
1940
+ };
1920
1941
  const pickImportantFields = (value) => {
1921
1942
  if (value === null || value === undefined)
1922
1943
  return {};
@@ -1929,7 +1950,7 @@ const pickImportantFields = (value) => {
1929
1950
  return;
1930
1951
  }
1931
1952
  for (const key of importantJsonFields) {
1932
- if (item[key] !== undefined && out[key] === undefined)
1953
+ if (!noisyToolFields.has(key) && item[key] !== undefined && out[key] === undefined)
1933
1954
  out[key] = item[key];
1934
1955
  }
1935
1956
  if (item.args)
@@ -1945,12 +1966,15 @@ const compactToolResult = (result, max = 360) => {
1945
1966
  if (!text.trim())
1946
1967
  return undefined;
1947
1968
  try {
1948
- const parsed = JSON.parse(text);
1949
- return truncate(safeStringify(parsed), max);
1969
+ const parsed = stripNoisyToolFields(JSON.parse(text));
1970
+ const picked = pickImportantFields(parsed);
1971
+ return truncate(safeStringify(Object.keys(picked).length ? picked : parsed), max);
1950
1972
  }
1951
1973
  catch { }
1952
1974
  const picked = {};
1953
1975
  for (const key of importantJsonFields) {
1976
+ if (noisyToolFields.has(key))
1977
+ continue;
1954
1978
  const re = new RegExp(`["']?${key}["']?\\s*[:=]\\s*["']?([^"',}\\]]+)`, 'i');
1955
1979
  const match = re.exec(text);
1956
1980
  if (match)
@@ -1961,7 +1985,6 @@ const compactToolResult = (result, max = 360) => {
1961
1985
  return truncate(text, max);
1962
1986
  };
1963
1987
  const compactToolHistoryForAgent = (toolHistory = [], maxItems = 6, includeResults = true) => pruneByLimit(toolHistory || [], maxItems).map((tool) => ({
1964
- id: tool.call_id || tool.id,
1965
1988
  name: tool.name,
1966
1989
  status: tool.ok === false ? 'failed' : 'ok',
1967
1990
  at: tool.at,
@@ -2045,7 +2068,6 @@ const compactMemoryCompressionForAgent = (compression = {}) => ({
2045
2068
  active_memory_count: ((compression.workflow_summary || {}).active_memory_count) || 0,
2046
2069
  });
2047
2070
  const compactActionLedgerForAgent = (ledger = [], maxItems = 6, includeResults = true) => pruneByLimit(ledger || [], maxItems).map((item) => cleanContextValue({
2048
- action_id: item.action_id,
2049
2071
  kind: item.kind,
2050
2072
  tool: item.tool || item.name,
2051
2073
  status: item.status,
@@ -2288,7 +2310,11 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2288
2310
  sections.push({
2289
2311
  section: 'conversation_frame',
2290
2312
  title: 'Conversation frame',
2291
- value: buildConversationFrame({ query, recentMessages, workingMemory }),
2313
+ value: buildConversationFrame({
2314
+ query,
2315
+ recentMessages: compactForAgent ? pruneByLimit(recentMessages || [], Number(adv.agentRecentMessagesLastN || 20)) : recentMessages,
2316
+ workingMemory,
2317
+ }),
2292
2318
  });
2293
2319
  const currentTurnFocus = buildCurrentTurnFocus({ query, recentMessages, operationalState, workingMemory });
2294
2320
  if (currentTurnFocus) {
@@ -2312,32 +2338,20 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2312
2338
  sections.push({ section: 'summary', title: 'Summary', value: summary.length ? summary : null, why_null: summary.length ? undefined : 'no non-tool vector memories to summarize' });
2313
2339
  }
2314
2340
  if (connectedModelSummary && adv.includeConnectedModelSummary !== false) {
2315
- sections.push({ section: 'connected_model_summary', title: 'SLM summary', value: connectedModelSummary });
2341
+ sections.push({ section: 'connected_model_summary', title: 'SLM summary', value: truncate(connectedModelSummary, Number(adv.connectedModelSummaryMaxChars || 900)) });
2316
2342
  }
2317
2343
  else if (activeSummary && adv.includeActiveSummary !== false) {
2318
- sections.push({ section: 'active_summary', title: 'Active summary', value: activeSummary });
2344
+ sections.push({ section: 'active_summary', title: 'Active summary', value: truncate(activeSummary, Number(adv.activeSummaryMaxChars || 900)) });
2319
2345
  }
2320
2346
  sections.push({
2321
2347
  section: 'working_memory',
2322
2348
  title: 'Working memory',
2323
- value: adv.includeWorkingMemory === false ? null : (workingMemory || {}),
2349
+ value: adv.includeWorkingMemory === false ? null : compactWorkingMemoryForAgent(workingMemory || {}),
2324
2350
  why_null: adv.includeWorkingMemory === false ? 'working memory disabled' : undefined,
2325
2351
  });
2326
2352
  sections.push({
2327
- section: 'decision_state',
2328
- title: 'Decision state',
2329
- value: adv.includeDecisionState === false ? null : (decisionState || {}),
2330
- why_null: adv.includeDecisionState === false ? 'decision state disabled' : undefined,
2331
- });
2332
- sections.push({
2333
- section: 'operational_state',
2334
- title: 'Operational state',
2335
- value: adv.includeOperationalState === false ? null : compactOperationalStateForAgent(operationalState || {}),
2336
- why_null: adv.includeOperationalState === false ? 'operational state disabled' : undefined,
2337
- });
2338
- sections.push({
2339
- section: 'tool_history',
2340
- title: 'Tool history',
2353
+ section: 'tool_ledger',
2354
+ title: 'Tool ledger',
2341
2355
  value: {
2342
2356
  enabled: adv.includeToolHistory !== false,
2343
2357
  items: adv.includeToolHistory === false ? [] : compactToolHistoryForAgent(toolHistory, adv.toolHistoryLastN || 6, adv.includeToolResults !== false),
@@ -2350,28 +2364,40 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2350
2364
  why_null: adv.includeProfileFacts === false ? 'profile facts disabled' : undefined,
2351
2365
  });
2352
2366
  sections.push({
2353
- section: 'memory_compression',
2354
- title: 'Memory compression',
2355
- value: adv.includeMemoryCompression === false ? null : compactMemoryCompressionForAgent(memoryCompression || {}),
2356
- why_null: adv.includeMemoryCompression === false ? 'memory compression disabled' : undefined,
2367
+ section: 'next_action',
2368
+ title: 'Next action',
2369
+ value: cleanContextValue({
2370
+ current_intent: (decisionState || {}).current_intent || workingMemory.last_user_intent,
2371
+ latest_tool: ((decisionState || {}).latest_tool || (operationalState || {}).last_tool || undefined),
2372
+ do_not_repeat_tools: ((decisionState || {}).do_not_repeat_tools || []).slice(0, 12),
2373
+ instruction: actionDirective || workingMemory.next_expected_action || 'Continue according to the agent prompt.',
2374
+ }),
2357
2375
  });
2358
- if (adv.includeRecentMessages !== false || currentTurnFocus) {
2359
- sections.push({
2360
- section: 'recent_messages',
2361
- title: 'Recent messages',
2362
- value: pruneByLimit(recentMessages || [], adv.recentMessagesLastN || 4).map((message) => cleanContextValue({
2363
- role: message.role,
2364
- content: truncate(message.content, 260),
2365
- at: message.at,
2366
- })),
2367
- });
2368
- }
2369
2376
  const audit = {
2370
2377
  kind: 'tembory.context.v1',
2371
2378
  meta: { user_id: userId, payloadFormat, query, compact_for_agent: true },
2372
2379
  sections,
2373
2380
  diagnostics,
2374
2381
  };
2382
+ const sectionValue = (name) => {
2383
+ const found = sections.find((section) => section.section === name);
2384
+ return found ? found.value : undefined;
2385
+ };
2386
+ const compactJson = cleanContextValue({
2387
+ kind: 'tembory.agent_context.v1',
2388
+ instruction: includeHeader ? sectionValue('context_header') : undefined,
2389
+ conversation: sectionValue('conversation_frame'),
2390
+ current_turn_focus: sectionValue('current_turn_focus'),
2391
+ action_directive: sectionValue('action_directive'),
2392
+ summary: {
2393
+ vector_facts: sectionValue('summary'),
2394
+ slm: sectionValue('connected_model_summary') || sectionValue('active_summary'),
2395
+ },
2396
+ working_memory: sectionValue('working_memory'),
2397
+ profile: sectionValue('profile_facts'),
2398
+ tools: sectionValue('tool_ledger'),
2399
+ next_action: sectionValue('next_action'),
2400
+ });
2375
2401
  const renderCompactSection = (section) => {
2376
2402
  if (section.value === null || section.value === undefined)
2377
2403
  return `## ${section.title}\n${section.why_null || 'empty'}`;
@@ -2385,7 +2411,7 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2385
2411
  return [{ role: 'system', content: JSON.stringify(audit, null, 2) }];
2386
2412
  if (payloadFormat === 'auditBlocks')
2387
2413
  return sections.map((section) => ({ role: 'system', content: renderCompactSection(section) }));
2388
- return [{ role: 'system', content: truncate(sections.map(renderCompactSection).join('\n\n'), Number(adv.contextMaxChars || 6000)) }];
2414
+ return [{ role: 'system', content: JSON.stringify(compactJson) }];
2389
2415
  }
2390
2416
  if (includeSummary) {
2391
2417
  const summary = compactStateSections
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
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",