n8n-nodes-tembory 1.1.20 → 1.1.22

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),
@@ -2326,6 +2346,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2326
2346
  const conversation = parsed.conversation || {};
2327
2347
  const tools = parsed.tools || {};
2328
2348
  const toolItems = Array.isArray(tools.items) ? tools.items : [];
2349
+ const memoryAudit = parsed.memoryAudit || parsed.memory_audit || {};
2329
2350
  const summaryText = parsed.summary?.slm || parsed.summary || parsed.connectedModelSummary || parsed.activeSummary || '';
2330
2351
  summary.userId = parsed.userId;
2331
2352
  summary.project = parsed.project || undefined;
@@ -2357,6 +2378,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2357
2378
  graph: Array.isArray(parsed.graph) ? parsed.graph.length : undefined,
2358
2379
  recentHighlights: Array.isArray(parsed.recentHighlights) ? parsed.recentHighlights.length : undefined,
2359
2380
  });
2381
+ summary.lastSave = memoryAudit.last_save || parsed.state?.last_save || undefined;
2360
2382
  summary.loadedSections = cleanContextValue({
2361
2383
  conversation: Boolean(parsed.conversation),
2362
2384
  summary: Boolean(parsed.summary),
@@ -2367,6 +2389,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
2367
2389
  memoryCompression: Boolean(parsed.memoryCompression),
2368
2390
  operationalState: Boolean(parsed.operationalState),
2369
2391
  actionLedger: Array.isArray(parsed.actionLedger),
2392
+ memoryAudit: Boolean(memoryAudit && Object.keys(memoryAudit).length),
2370
2393
  entityTimeline: Array.isArray(parsed.entityTimeline),
2371
2394
  vectorMemories: Array.isArray(parsed.vectorMemories),
2372
2395
  graph: Array.isArray(parsed.graph),
@@ -2433,70 +2456,18 @@ const wrapTemboryMemory = (memory, ctx, memoryKey, itemIndex = 0) => new Proxy(m
2433
2456
  }
2434
2457
  if (prop === 'saveContext') {
2435
2458
  return async (input = {}, output = {}) => {
2436
- const { index } = ctx.addInputData(n8n_workflow_1.NodeConnectionTypes.AiMemory, [
2437
- [{
2438
- json: {
2439
- action: 'saveContext',
2440
- inputChars: JSON.stringify(input || {}).length,
2441
- outputChars: JSON.stringify(output || {}).length,
2442
- },
2443
- }],
2444
- ]);
2445
2459
  try {
2446
- const response = await target.saveContext(input, output);
2447
- const chatHistory = snapshotJson(await target.chatHistory.getMessages());
2448
- const savedSummary = summarizeSaveContextForSideChannel(input, output, chatHistory);
2449
- ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiMemory, index, [
2450
- [{
2451
- json: {
2452
- action: 'saveContext',
2453
- saved: true,
2454
- inputChars: JSON.stringify(input || {}).length,
2455
- outputChars: JSON.stringify(output || {}).length,
2456
- messages: Array.isArray(chatHistory) ? chatHistory.length : 0,
2457
- savedSummary,
2458
- },
2459
- }],
2460
- ]);
2461
- return response;
2460
+ return await target.saveContext(input, output);
2462
2461
  }
2463
2462
  catch (error) {
2464
- const message = error && error.message ? error.message : String(error);
2465
- const safeInput = stripThreadTestPrefix(pickText(input, ['input', 'chatInput', 'text', 'query', 'question']));
2466
- const safeOutput = cleanAssistantTranscriptText(pickText(output, ['output', 'response', 'text', 'answer']));
2467
- let fallbackSaved = false;
2468
- let fallbackError;
2469
- let chatHistory = [];
2470
- if (safeInput || safeOutput) {
2471
- try {
2472
- await TemboryMemory.prototype.saveContextForItem.call(ctx, itemIndex, safeInput ? { input: safeInput } : {}, safeOutput ? { output: safeOutput } : {});
2473
- fallbackSaved = true;
2474
- try {
2475
- chatHistory = snapshotJson(await target.chatHistory.getMessages());
2476
- }
2477
- catch {
2478
- chatHistory = [];
2479
- }
2480
- }
2481
- catch (fallback) {
2482
- fallbackError = fallback && fallback.message ? fallback.message : String(fallback);
2483
- }
2463
+ try {
2464
+ ctx.logger?.warn('Tembory saveContext failed but was suppressed to keep the n8n Agent turn alive', {
2465
+ message: error && error.message ? error.message : String(error),
2466
+ inputChars: JSON.stringify(input || {}).length,
2467
+ outputChars: JSON.stringify(output || {}).length,
2468
+ });
2484
2469
  }
2485
- ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiMemory, index, [
2486
- [{
2487
- json: {
2488
- action: 'saveContext',
2489
- saved: fallbackSaved,
2490
- fallback: true,
2491
- inputChars: JSON.stringify(input || {}).length,
2492
- outputChars: JSON.stringify(output || {}).length,
2493
- messages: Array.isArray(chatHistory) ? chatHistory.length : 0,
2494
- savedSummary: fallbackSaved ? summarizeSaveContextForSideChannel({ input: safeInput }, { output: safeOutput }, chatHistory) : undefined,
2495
- originalError: truncate(message, 500),
2496
- error: fallbackSaved ? undefined : truncate(fallbackError || message, 500),
2497
- },
2498
- }],
2499
- ]);
2470
+ catch { }
2500
2471
  return undefined;
2501
2472
  }
2502
2473
  };
@@ -2562,6 +2533,18 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2562
2533
  value: adv.includeWorkingMemory === false ? null : compactWorkingMemoryForAgent(workingMemory || {}),
2563
2534
  why_null: adv.includeWorkingMemory === false ? 'working memory disabled' : undefined,
2564
2535
  });
2536
+ sections.push({
2537
+ section: 'decision_state',
2538
+ title: 'Decision state',
2539
+ value: adv.includeDecisionState === false ? null : compactDecisionStateForAgent(decisionState || {}),
2540
+ why_null: adv.includeDecisionState === false ? 'decision state disabled' : undefined,
2541
+ });
2542
+ sections.push({
2543
+ section: 'operational_state',
2544
+ title: 'Operational state',
2545
+ value: adv.includeOperationalState === false ? null : compactOperationalStateForAgent(operationalState || {}),
2546
+ why_null: adv.includeOperationalState === false ? 'operational state disabled' : undefined,
2547
+ });
2565
2548
  sections.push({
2566
2549
  section: 'tool_ledger',
2567
2550
  title: 'Tool ledger',
@@ -2570,12 +2553,29 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2570
2553
  items: adv.includeToolHistory === false ? [] : compactToolHistoryForAgent(toolHistory, adv.toolHistoryLastN || 6, adv.includeToolResults !== false),
2571
2554
  },
2572
2555
  });
2556
+ sections.push({
2557
+ section: 'action_ledger',
2558
+ title: 'Action ledger',
2559
+ value: adv.includeActionLedger === false ? null : compactActionLedgerForAgent(actionLedger || [], adv.actionLedgerMaxItems || adv.toolHistoryLastN || 8, adv.includeToolResults !== false),
2560
+ why_null: adv.includeActionLedger === false ? 'action ledger disabled' : undefined,
2561
+ });
2562
+ sections.push({
2563
+ section: 'memory_compression',
2564
+ title: 'Memory compression',
2565
+ value: adv.includeMemoryCompression === false ? null : compactMemoryCompressionForAgent(memoryCompression || {}),
2566
+ why_null: adv.includeMemoryCompression === false ? 'memory compression disabled' : undefined,
2567
+ });
2573
2568
  sections.push({
2574
2569
  section: 'profile_facts',
2575
2570
  title: 'Profile facts',
2576
2571
  value: adv.includeProfileFacts === false ? null : renderProfileFacts(profileFacts),
2577
2572
  why_null: adv.includeProfileFacts === false ? 'profile facts disabled' : undefined,
2578
2573
  });
2574
+ sections.push({
2575
+ section: 'memory_audit',
2576
+ title: 'Memory audit',
2577
+ value: compactSaveAuditForAgent((diagnostics || {}).captureState || {}),
2578
+ });
2579
2579
  sections.push({
2580
2580
  section: 'next_action',
2581
2581
  title: 'Next action',
@@ -2604,8 +2604,11 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2604
2604
  const slmSummary = sectionValue('connected_model_summary') || sectionValue('active_summary');
2605
2605
  const directive = sectionValue('action_directive');
2606
2606
  const inferredIntent = deriveUserIntentObservation({ query, workingMemory, decisionState, recentMessages });
2607
+ const memoryAudit = sectionValue('memory_audit');
2607
2608
  const minimalState = cleanContextValue({
2608
2609
  next_expected_action: directive ? undefined : workingMemory.next_expected_action,
2610
+ context_quality_score: diagnostics?.contextHealth?.quality_score || diagnostics?.quality_score,
2611
+ last_save: memoryAudit?.last_save,
2609
2612
  });
2610
2613
  const compactJson = cleanContextValue({
2611
2614
  kind: 'tembory.agent_context.v1',
@@ -2628,8 +2631,14 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2628
2631
  slm: hasToolLedger ? undefined : slmSummary,
2629
2632
  },
2630
2633
  state: minimalState,
2634
+ workingMemory: sectionValue('working_memory'),
2635
+ decisionState: sectionValue('decision_state'),
2636
+ operationalState: sectionValue('operational_state'),
2637
+ actionLedger: sectionValue('action_ledger'),
2638
+ memoryCompression: sectionValue('memory_compression'),
2631
2639
  profile: sectionValue('profile_facts'),
2632
2640
  tools: compactToolLedger,
2641
+ memoryAudit,
2633
2642
  });
2634
2643
  const renderCompactSection = (section) => {
2635
2644
  if (section.value === null || section.value === undefined)
@@ -2730,6 +2739,11 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
2730
2739
  value: adv.includeMemoryCompression === false ? null : (compactStateSections ? compactMemoryCompressionForAgent(memoryCompression || {}) : (memoryCompression || {})),
2731
2740
  why_null: adv.includeMemoryCompression === false ? 'memory compression disabled' : undefined,
2732
2741
  });
2742
+ sections.push({
2743
+ section: 'memory_audit',
2744
+ title: 'Memory audit',
2745
+ value: compactSaveAuditForAgent((diagnostics || {}).captureState || {}),
2746
+ });
2733
2747
  sections.push({
2734
2748
  section: 'graph',
2735
2749
  title: 'Graph',
@@ -3148,10 +3162,17 @@ class TemboryMemory {
3148
3162
  const rawOutput = pickText(outputValues, ['output', 'response', 'text', 'answer']);
3149
3163
  const output = cleanAssistantTranscriptText(rawOutput);
3150
3164
  const toolCalls = extractToolCalls(outputValues);
3165
+ const saveAt = nowIso();
3151
3166
  store.captureState[key] = {
3152
- last_save_at: nowIso(),
3167
+ last_save_status: 'started',
3168
+ last_save_saved: false,
3169
+ last_save_at: saveAt,
3153
3170
  last_save_had_input: Boolean(input),
3154
3171
  last_save_had_output: Boolean(output),
3172
+ last_save_input_chars: input.length,
3173
+ last_save_output_chars: output.length,
3174
+ last_save_input_preview: input ? truncate(input, 500) : undefined,
3175
+ last_save_output_preview: output ? truncate(output, 700) : undefined,
3155
3176
  last_save_tool_calls_captured: toolCalls.length,
3156
3177
  last_save_tool_names: toolCalls.map((tool) => tool.name).filter(Boolean).slice(0, 20),
3157
3178
  last_save_capture_sources: Array.from(new Set(toolCalls.map((tool) => tool.source).filter(Boolean))),
@@ -3243,7 +3264,7 @@ class TemboryMemory {
3243
3264
  body.app_id = String(adv.appId);
3244
3265
  if (adv.runId)
3245
3266
  body.run_id = String(adv.runId);
3246
- await saveThreadState(this, key, threadId, project, {
3267
+ const threadStateSaved = await saveThreadState(this, key, threadId, project, {
3247
3268
  kind: 'tembory.thread_state.v1',
3248
3269
  threadId,
3249
3270
  project: project || undefined,
@@ -3257,6 +3278,24 @@ class TemboryMemory {
3257
3278
  operationalState: operationalStateForTurn,
3258
3279
  activeSummary: store.activeSummary[key] || '',
3259
3280
  });
3281
+ store.captureState[key] = cleanContextValue({
3282
+ ...(store.captureState[key] || {}),
3283
+ last_save_status: 'saved',
3284
+ last_save_saved: true,
3285
+ last_save_conversation_messages_after_save: recentForTurn.length,
3286
+ last_save_tool_history_after_save: toolHistoryForTurn.length,
3287
+ last_save_thread_state_saved: threadStateSaved,
3288
+ last_save_backend_memory_persistence: adv.persistBackendMemories === false
3289
+ ? 'disabled'
3290
+ : adv.useVectorMemory === false
3291
+ ? 'thread_state_only'
3292
+ : 'enabled',
3293
+ });
3294
+ try {
3295
+ const globalData = this.getWorkflowStaticData('global');
3296
+ globalData.__dataChanged = true;
3297
+ }
3298
+ catch { }
3260
3299
  if (adv.persistBackendMemories === false)
3261
3300
  return;
3262
3301
  if (adv.useVectorMemory === false)
@@ -3948,12 +3987,20 @@ class TemboryMemory {
3948
3987
  vectorMemoryEnabled,
3949
3988
  backendPersistenceEnabled,
3950
3989
  captureState: store.captureState[key] || {
3990
+ last_save_status: null,
3991
+ last_save_saved: false,
3951
3992
  last_save_at: null,
3952
3993
  last_save_had_input: false,
3953
3994
  last_save_had_output: false,
3995
+ last_save_input_chars: 0,
3996
+ last_save_output_chars: 0,
3954
3997
  last_save_tool_calls_captured: 0,
3955
3998
  last_save_tool_names: [],
3956
3999
  last_save_capture_sources: [],
4000
+ last_save_conversation_messages_after_save: 0,
4001
+ last_save_tool_history_after_save: 0,
4002
+ last_save_thread_state_saved: false,
4003
+ last_save_backend_memory_persistence: backendPersistenceEnabled ? 'enabled' : 'disabled',
3957
4004
  },
3958
4005
  connectedAi,
3959
4006
  activeSummary: summaryDiagnostics,
@@ -3970,6 +4017,7 @@ class TemboryMemory {
3970
4017
  operationalState,
3971
4018
  diagnostics,
3972
4019
  });
4020
+ diagnostics.contextHealth = contextHealth;
3973
4021
  store.workingMemory[key] = workingMemory;
3974
4022
  store.decisionState[key] = decisionState;
3975
4023
  store.memoryCompression[key] = memoryCompression;
@@ -4239,6 +4287,7 @@ exports.__private = {
4239
4287
  compactVectorMemoriesForAgent,
4240
4288
  isConversationEchoMemory,
4241
4289
  compactOperationalStateForAgent,
4290
+ compactSaveAuditForAgent,
4242
4291
  activeSummaryIsFresh,
4243
4292
  readActiveSummary,
4244
4293
  writeActiveSummary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.1.20",
3
+ "version": "1.1.22",
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",