n8n-nodes-tembory 1.1.21 → 1.1.23

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.
@@ -1396,7 +1396,7 @@ const applyOperationalPreset = (advanced = {}) => {
1396
1396
  includeToolHistorySemanticFallback: false,
1397
1397
  includeProfileFacts: true,
1398
1398
  includeOperationalState: true,
1399
- includeActionLedger: false,
1399
+ includeActionLedger: true,
1400
1400
  includeEntityTimeline: false,
1401
1401
  includeWorkingMemory: true,
1402
1402
  includeDecisionState: true,
@@ -2114,9 +2114,29 @@ const compactActionLedgerForAgent = (ledger = [], maxItems = 6, includeResults =
2114
2114
  status: item.status,
2115
2115
  at: item.at,
2116
2116
  reason: item.reason,
2117
- input: item.input,
2118
- result: includeResults ? compactToolResult(item.result, 180) : undefined,
2117
+ input: truncate(String(item.input || ''), 500) || undefined,
2118
+ result: includeResults ? compactToolResult(item.result, 700) : undefined,
2119
2119
  }));
2120
+ const compactSaveAuditForAgent = (capture = {}) => cleanContextValue({
2121
+ last_save: {
2122
+ saved: capture.last_save_status === 'saved' || capture.last_save_saved === true,
2123
+ status: capture.last_save_status,
2124
+ at: capture.last_save_at,
2125
+ had_input: capture.last_save_had_input,
2126
+ had_output: capture.last_save_had_output,
2127
+ input_chars: capture.last_save_input_chars,
2128
+ output_chars: capture.last_save_output_chars,
2129
+ input_preview: capture.last_save_input_preview,
2130
+ output_preview: capture.last_save_output_preview,
2131
+ tool_calls_captured: capture.last_save_tool_calls_captured,
2132
+ tool_names: capture.last_save_tool_names,
2133
+ capture_sources: capture.last_save_capture_sources,
2134
+ conversation_messages_after_save: capture.last_save_conversation_messages_after_save,
2135
+ tool_history_after_save: capture.last_save_tool_history_after_save,
2136
+ thread_state_saved: capture.last_save_thread_state_saved,
2137
+ backend_memory_persistence: capture.last_save_backend_memory_persistence,
2138
+ },
2139
+ });
2120
2140
  const compactEntityTimelineForAgent = (timeline = [], maxItems = 8) => pruneByLimit(timeline || [], maxItems).map((item) => cleanContextValue({
2121
2141
  entity: item.entity || item.source || item.name,
2122
2142
  event: truncate(item.event || item.fact || item.relation || item.kind || item.value, 220),
@@ -2272,6 +2292,35 @@ const compactMessageForSideChannel = (message = {}) => {
2272
2292
  preview: role === 'system' ? '[system context hidden]' : truncate(content, 280),
2273
2293
  });
2274
2294
  };
2295
+ const normalizeConversationRoleForSideChannel = (role = '') => {
2296
+ const normalized = String(role || '').toLowerCase();
2297
+ if (normalized === 'human' || normalized === 'user')
2298
+ return 'user';
2299
+ if (normalized === 'ai' || normalized === 'assistant' || normalized === 'agent')
2300
+ return 'agent';
2301
+ if (normalized === 'system')
2302
+ return 'system';
2303
+ if (normalized === 'tool')
2304
+ return 'tool';
2305
+ return normalized || 'message';
2306
+ };
2307
+ const compactConversationTimelineForSideChannel = (conversation = {}, maxItems = 12) => {
2308
+ const chronological = Array.isArray(conversation.conversation_history_chronological)
2309
+ ? conversation.conversation_history_chronological
2310
+ : [];
2311
+ return pruneByLimit(chronological, maxItems).map((message, index) => {
2312
+ const role = normalizeConversationRoleForSideChannel(message.role || message.type);
2313
+ const content = String(message.content || message.text || message.message || '');
2314
+ return cleanContextValue({
2315
+ index,
2316
+ role,
2317
+ speaker: role,
2318
+ at: message.at || message.timestamp || message.created_at || message.createdAt,
2319
+ chars: content.length,
2320
+ content: truncate(content, 700),
2321
+ });
2322
+ });
2323
+ };
2275
2324
  const summarizeSaveContextForSideChannel = (input = {}, output = {}, chatHistory = []) => {
2276
2325
  const inputMessage = input?.input || input?.chatInput || input?.query || input?.question || input?.text || input?.message || '';
2277
2326
  const outputMessage = output?.output || output?.response || output?.text || output?.message || output?.answer || '';
@@ -2326,6 +2375,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2326
2375
  const conversation = parsed.conversation || {};
2327
2376
  const tools = parsed.tools || {};
2328
2377
  const toolItems = Array.isArray(tools.items) ? tools.items : [];
2378
+ const memoryAudit = parsed.memoryAudit || parsed.memory_audit || {};
2329
2379
  const summaryText = parsed.summary?.slm || parsed.summary || parsed.connectedModelSummary || parsed.activeSummary || '';
2330
2380
  summary.userId = parsed.userId;
2331
2381
  summary.project = parsed.project || undefined;
@@ -2335,6 +2385,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2335
2385
  summary.intent = parsed.observations?.inferred_intent?.label || parsed.workingMemory?.last_user_intent || parsed.decisionState?.current_intent || undefined;
2336
2386
  summary.currentUserMessage = conversation.current_user_message ? truncate(conversation.current_user_message, 180) : undefined;
2337
2387
  summary.recentMessages = Array.isArray(conversation.conversation_history_chronological) ? conversation.conversation_history_chronological.length : undefined;
2388
+ summary.conversationTimeline = compactConversationTimelineForSideChannel(conversation, 12);
2338
2389
  summary.toolCount = toolItems.length || tools.count || parsed.operationalState?.tool_counts?.total || undefined;
2339
2390
  summary.toolNames = toolItems.map((tool) => tool.name || tool.tool_name).filter(Boolean).slice(0, 20);
2340
2391
  summary.toolEvents = toolItems
@@ -2357,6 +2408,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2357
2408
  graph: Array.isArray(parsed.graph) ? parsed.graph.length : undefined,
2358
2409
  recentHighlights: Array.isArray(parsed.recentHighlights) ? parsed.recentHighlights.length : undefined,
2359
2410
  });
2411
+ summary.lastSave = memoryAudit.last_save || parsed.state?.last_save || undefined;
2360
2412
  summary.loadedSections = cleanContextValue({
2361
2413
  conversation: Boolean(parsed.conversation),
2362
2414
  summary: Boolean(parsed.summary),
@@ -2367,6 +2419,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2367
2419
  memoryCompression: Boolean(parsed.memoryCompression),
2368
2420
  operationalState: Boolean(parsed.operationalState),
2369
2421
  actionLedger: Array.isArray(parsed.actionLedger),
2422
+ memoryAudit: Boolean(memoryAudit && Object.keys(memoryAudit).length),
2370
2423
  entityTimeline: Array.isArray(parsed.entityTimeline),
2371
2424
  vectorMemories: Array.isArray(parsed.vectorMemories),
2372
2425
  graph: Array.isArray(parsed.graph),
@@ -2510,6 +2563,18 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2510
2563
  value: adv.includeWorkingMemory === false ? null : compactWorkingMemoryForAgent(workingMemory || {}),
2511
2564
  why_null: adv.includeWorkingMemory === false ? 'working memory disabled' : undefined,
2512
2565
  });
2566
+ sections.push({
2567
+ section: 'decision_state',
2568
+ title: 'Decision state',
2569
+ value: adv.includeDecisionState === false ? null : compactDecisionStateForAgent(decisionState || {}),
2570
+ why_null: adv.includeDecisionState === false ? 'decision state disabled' : undefined,
2571
+ });
2572
+ sections.push({
2573
+ section: 'operational_state',
2574
+ title: 'Operational state',
2575
+ value: adv.includeOperationalState === false ? null : compactOperationalStateForAgent(operationalState || {}),
2576
+ why_null: adv.includeOperationalState === false ? 'operational state disabled' : undefined,
2577
+ });
2513
2578
  sections.push({
2514
2579
  section: 'tool_ledger',
2515
2580
  title: 'Tool ledger',
@@ -2518,12 +2583,29 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2518
2583
  items: adv.includeToolHistory === false ? [] : compactToolHistoryForAgent(toolHistory, adv.toolHistoryLastN || 6, adv.includeToolResults !== false),
2519
2584
  },
2520
2585
  });
2586
+ sections.push({
2587
+ section: 'action_ledger',
2588
+ title: 'Action ledger',
2589
+ value: adv.includeActionLedger === false ? null : compactActionLedgerForAgent(actionLedger || [], adv.actionLedgerMaxItems || adv.toolHistoryLastN || 8, adv.includeToolResults !== false),
2590
+ why_null: adv.includeActionLedger === false ? 'action ledger disabled' : undefined,
2591
+ });
2592
+ sections.push({
2593
+ section: 'memory_compression',
2594
+ title: 'Memory compression',
2595
+ value: adv.includeMemoryCompression === false ? null : compactMemoryCompressionForAgent(memoryCompression || {}),
2596
+ why_null: adv.includeMemoryCompression === false ? 'memory compression disabled' : undefined,
2597
+ });
2521
2598
  sections.push({
2522
2599
  section: 'profile_facts',
2523
2600
  title: 'Profile facts',
2524
2601
  value: adv.includeProfileFacts === false ? null : renderProfileFacts(profileFacts),
2525
2602
  why_null: adv.includeProfileFacts === false ? 'profile facts disabled' : undefined,
2526
2603
  });
2604
+ sections.push({
2605
+ section: 'memory_audit',
2606
+ title: 'Memory audit',
2607
+ value: compactSaveAuditForAgent((diagnostics || {}).captureState || {}),
2608
+ });
2527
2609
  sections.push({
2528
2610
  section: 'next_action',
2529
2611
  title: 'Next action',
@@ -2552,8 +2634,11 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2552
2634
  const slmSummary = sectionValue('connected_model_summary') || sectionValue('active_summary');
2553
2635
  const directive = sectionValue('action_directive');
2554
2636
  const inferredIntent = deriveUserIntentObservation({ query, workingMemory, decisionState, recentMessages });
2637
+ const memoryAudit = sectionValue('memory_audit');
2555
2638
  const minimalState = cleanContextValue({
2556
2639
  next_expected_action: directive ? undefined : workingMemory.next_expected_action,
2640
+ context_quality_score: diagnostics?.contextHealth?.quality_score || diagnostics?.quality_score,
2641
+ last_save: memoryAudit?.last_save,
2557
2642
  });
2558
2643
  const compactJson = cleanContextValue({
2559
2644
  kind: 'tembory.agent_context.v1',
@@ -2576,8 +2661,14 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2576
2661
  slm: hasToolLedger ? undefined : slmSummary,
2577
2662
  },
2578
2663
  state: minimalState,
2664
+ workingMemory: sectionValue('working_memory'),
2665
+ decisionState: sectionValue('decision_state'),
2666
+ operationalState: sectionValue('operational_state'),
2667
+ actionLedger: sectionValue('action_ledger'),
2668
+ memoryCompression: sectionValue('memory_compression'),
2579
2669
  profile: sectionValue('profile_facts'),
2580
2670
  tools: compactToolLedger,
2671
+ memoryAudit,
2581
2672
  });
2582
2673
  const renderCompactSection = (section) => {
2583
2674
  if (section.value === null || section.value === undefined)
@@ -2678,6 +2769,11 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2678
2769
  value: adv.includeMemoryCompression === false ? null : (compactStateSections ? compactMemoryCompressionForAgent(memoryCompression || {}) : (memoryCompression || {})),
2679
2770
  why_null: adv.includeMemoryCompression === false ? 'memory compression disabled' : undefined,
2680
2771
  });
2772
+ sections.push({
2773
+ section: 'memory_audit',
2774
+ title: 'Memory audit',
2775
+ value: compactSaveAuditForAgent((diagnostics || {}).captureState || {}),
2776
+ });
2681
2777
  sections.push({
2682
2778
  section: 'graph',
2683
2779
  title: 'Graph',
@@ -3096,10 +3192,17 @@ class TemboryMemory {
3096
3192
  const rawOutput = pickText(outputValues, ['output', 'response', 'text', 'answer']);
3097
3193
  const output = cleanAssistantTranscriptText(rawOutput);
3098
3194
  const toolCalls = extractToolCalls(outputValues);
3195
+ const saveAt = nowIso();
3099
3196
  store.captureState[key] = {
3100
- last_save_at: nowIso(),
3197
+ last_save_status: 'started',
3198
+ last_save_saved: false,
3199
+ last_save_at: saveAt,
3101
3200
  last_save_had_input: Boolean(input),
3102
3201
  last_save_had_output: Boolean(output),
3202
+ last_save_input_chars: input.length,
3203
+ last_save_output_chars: output.length,
3204
+ last_save_input_preview: input ? truncate(input, 500) : undefined,
3205
+ last_save_output_preview: output ? truncate(output, 700) : undefined,
3103
3206
  last_save_tool_calls_captured: toolCalls.length,
3104
3207
  last_save_tool_names: toolCalls.map((tool) => tool.name).filter(Boolean).slice(0, 20),
3105
3208
  last_save_capture_sources: Array.from(new Set(toolCalls.map((tool) => tool.source).filter(Boolean))),
@@ -3191,7 +3294,7 @@ class TemboryMemory {
3191
3294
  body.app_id = String(adv.appId);
3192
3295
  if (adv.runId)
3193
3296
  body.run_id = String(adv.runId);
3194
- await saveThreadState(this, key, threadId, project, {
3297
+ const threadStateSaved = await saveThreadState(this, key, threadId, project, {
3195
3298
  kind: 'tembory.thread_state.v1',
3196
3299
  threadId,
3197
3300
  project: project || undefined,
@@ -3205,6 +3308,24 @@ class TemboryMemory {
3205
3308
  operationalState: operationalStateForTurn,
3206
3309
  activeSummary: store.activeSummary[key] || '',
3207
3310
  });
3311
+ 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,
3317
+ 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
+ });
3324
+ try {
3325
+ const globalData = this.getWorkflowStaticData('global');
3326
+ globalData.__dataChanged = true;
3327
+ }
3328
+ catch { }
3208
3329
  if (adv.persistBackendMemories === false)
3209
3330
  return;
3210
3331
  if (adv.useVectorMemory === false)
@@ -3896,12 +4017,20 @@ class TemboryMemory {
3896
4017
  vectorMemoryEnabled,
3897
4018
  backendPersistenceEnabled,
3898
4019
  captureState: store.captureState[key] || {
4020
+ last_save_status: null,
4021
+ last_save_saved: false,
3899
4022
  last_save_at: null,
3900
4023
  last_save_had_input: false,
3901
4024
  last_save_had_output: false,
4025
+ last_save_input_chars: 0,
4026
+ last_save_output_chars: 0,
3902
4027
  last_save_tool_calls_captured: 0,
3903
4028
  last_save_tool_names: [],
3904
4029
  last_save_capture_sources: [],
4030
+ last_save_conversation_messages_after_save: 0,
4031
+ last_save_tool_history_after_save: 0,
4032
+ last_save_thread_state_saved: false,
4033
+ last_save_backend_memory_persistence: backendPersistenceEnabled ? 'enabled' : 'disabled',
3905
4034
  },
3906
4035
  connectedAi,
3907
4036
  activeSummary: summaryDiagnostics,
@@ -3918,6 +4047,7 @@ class TemboryMemory {
3918
4047
  operationalState,
3919
4048
  diagnostics,
3920
4049
  });
4050
+ diagnostics.contextHealth = contextHealth;
3921
4051
  store.workingMemory[key] = workingMemory;
3922
4052
  store.decisionState[key] = decisionState;
3923
4053
  store.memoryCompression[key] = memoryCompression;
@@ -4187,6 +4317,7 @@ exports.__private = {
4187
4317
  compactVectorMemoriesForAgent,
4188
4318
  isConversationEchoMemory,
4189
4319
  compactOperationalStateForAgent,
4320
+ compactSaveAuditForAgent,
4190
4321
  activeSummaryIsFresh,
4191
4322
  readActiveSummary,
4192
4323
  writeActiveSummary,
@@ -4194,6 +4325,7 @@ exports.__private = {
4194
4325
  cleanModelSummaryText,
4195
4326
  invokeConnectedModelSummary,
4196
4327
  compactMemoryEventPayload,
4328
+ compactConversationTimelineForSideChannel,
4197
4329
  summarizeMemoryMessagesForSideChannel,
4198
4330
  summarizeSaveContextForSideChannel,
4199
4331
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.1.21",
3
+ "version": "1.1.23",
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",