n8n-nodes-tembory 1.1.26 → 1.1.28
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.
|
@@ -153,38 +153,39 @@ const currentInputJsonFromContext = (ctx, itemIndex = 0) => {
|
|
|
153
153
|
return {};
|
|
154
154
|
}
|
|
155
155
|
};
|
|
156
|
-
const
|
|
156
|
+
const resolveCurrentTurnQueryWithSource = (ctx, itemIndex = 0, inputValues = {}, queryParam = '') => {
|
|
157
157
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
158
158
|
const inputJson = currentInputJsonFromContext(ctx, itemIndex);
|
|
159
159
|
const candidates = [
|
|
160
|
-
queryParam,
|
|
161
|
-
inputValues.query,
|
|
162
|
-
inputValues.input,
|
|
163
|
-
inputValues.chatInput,
|
|
164
|
-
inputValues.text,
|
|
165
|
-
inputValues.question,
|
|
166
|
-
inputValues.message,
|
|
167
|
-
inputJson.query,
|
|
168
|
-
inputJson.lastUserMessage,
|
|
169
|
-
inputJson.chatInput,
|
|
170
|
-
inputJson.input,
|
|
171
|
-
inputJson.text,
|
|
172
|
-
inputJson.message,
|
|
173
|
-
(_a = inputJson.body) === null || _a === void 0 ? void 0 : _a.consolidated_text,
|
|
174
|
-
(_b = inputJson.body) === null || _b === void 0 ? void 0 : _b.chatInput,
|
|
175
|
-
(_c = inputJson.body) === null || _c === void 0 ? void 0 : _c.message,
|
|
176
|
-
(_d = inputJson.body) === null || _d === void 0 ? void 0 : _d.text,
|
|
177
|
-
(_e = inputJson.body) === null || _e === void 0 ? void 0 : _e.input,
|
|
178
|
-
(_h = (_g = (_f = inputJson.body) === null || _f === void 0 ? void 0 : _f.messages) === null || _g === void 0 ? void 0 : _g[0]) === null || _h === void 0 ? void 0 : _h.text,
|
|
179
|
-
(_k = (_j = inputJson.body) === null || _j === void 0 ? void 0 : _j.messages) === null || _k === void 0 ? void 0 : _k[0],
|
|
160
|
+
['query_param', queryParam],
|
|
161
|
+
['input_values.query', inputValues.query],
|
|
162
|
+
['input_values.input', inputValues.input],
|
|
163
|
+
['input_values.chatInput', inputValues.chatInput],
|
|
164
|
+
['input_values.text', inputValues.text],
|
|
165
|
+
['input_values.question', inputValues.question],
|
|
166
|
+
['input_values.message', inputValues.message],
|
|
167
|
+
['input_json.query', inputJson.query],
|
|
168
|
+
['input_json.lastUserMessage', inputJson.lastUserMessage],
|
|
169
|
+
['input_json.chatInput', inputJson.chatInput],
|
|
170
|
+
['input_json.input', inputJson.input],
|
|
171
|
+
['input_json.text', inputJson.text],
|
|
172
|
+
['input_json.message', inputJson.message],
|
|
173
|
+
['input_json.body.consolidated_text', (_a = inputJson.body) === null || _a === void 0 ? void 0 : _a.consolidated_text],
|
|
174
|
+
['input_json.body.chatInput', (_b = inputJson.body) === null || _b === void 0 ? void 0 : _b.chatInput],
|
|
175
|
+
['input_json.body.message', (_c = inputJson.body) === null || _c === void 0 ? void 0 : _c.message],
|
|
176
|
+
['input_json.body.text', (_d = inputJson.body) === null || _d === void 0 ? void 0 : _d.text],
|
|
177
|
+
['input_json.body.input', (_e = inputJson.body) === null || _e === void 0 ? void 0 : _e.input],
|
|
178
|
+
['input_json.body.messages.0.text', (_h = (_g = (_f = inputJson.body) === null || _f === void 0 ? void 0 : _f.messages) === null || _g === void 0 ? void 0 : _g[0]) === null || _h === void 0 ? void 0 : _h.text],
|
|
179
|
+
['input_json.body.messages.0', (_k = (_j = inputJson.body) === null || _j === void 0 ? void 0 : _j.messages) === null || _k === void 0 ? void 0 : _k[0]],
|
|
180
180
|
];
|
|
181
|
-
for (const candidate of candidates) {
|
|
181
|
+
for (const [source, candidate] of candidates) {
|
|
182
182
|
const query = asSearchQuery(candidate).trim();
|
|
183
183
|
if (query)
|
|
184
|
-
return stripThreadTestPrefix(query);
|
|
184
|
+
return { query: stripThreadTestPrefix(query), source };
|
|
185
185
|
}
|
|
186
|
-
return '';
|
|
186
|
+
return { query: '', source: 'missing' };
|
|
187
187
|
};
|
|
188
|
+
const resolveCurrentTurnQuery = (ctx, itemIndex = 0, inputValues = {}, queryParam = '') => resolveCurrentTurnQueryWithSource(ctx, itemIndex, inputValues, queryParam).query;
|
|
188
189
|
const memoryText = (memory) => {
|
|
189
190
|
if (!memory)
|
|
190
191
|
return '';
|
|
@@ -1205,16 +1206,18 @@ const getMemoryStore = (ctx) => {
|
|
|
1205
1206
|
data.tembory.activeSummary = data.tembory.activeSummary || {};
|
|
1206
1207
|
data.tembory.connectedModelSummaryCache = data.tembory.connectedModelSummaryCache || {};
|
|
1207
1208
|
data.tembory.captureState = data.tembory.captureState || {};
|
|
1209
|
+
data.tembory.memoryDedupe = data.tembory.memoryDedupe || {};
|
|
1208
1210
|
return data.tembory;
|
|
1209
1211
|
}
|
|
1210
1212
|
catch {
|
|
1211
|
-
global.__temboryMemory = global.__temboryMemory || { toolHistory: {}, recentMessages: {}, profileFacts: {}, workingMemory: {}, decisionState: {}, memoryCompression: {}, activeSummary: {}, connectedModelSummaryCache: {}, captureState: {} };
|
|
1213
|
+
global.__temboryMemory = global.__temboryMemory || { toolHistory: {}, recentMessages: {}, profileFacts: {}, workingMemory: {}, decisionState: {}, memoryCompression: {}, activeSummary: {}, connectedModelSummaryCache: {}, captureState: {}, memoryDedupe: {} };
|
|
1212
1214
|
global.__temboryMemory.workingMemory = global.__temboryMemory.workingMemory || {};
|
|
1213
1215
|
global.__temboryMemory.decisionState = global.__temboryMemory.decisionState || {};
|
|
1214
1216
|
global.__temboryMemory.memoryCompression = global.__temboryMemory.memoryCompression || {};
|
|
1215
1217
|
global.__temboryMemory.activeSummary = global.__temboryMemory.activeSummary || {};
|
|
1216
1218
|
global.__temboryMemory.connectedModelSummaryCache = global.__temboryMemory.connectedModelSummaryCache || {};
|
|
1217
1219
|
global.__temboryMemory.captureState = global.__temboryMemory.captureState || {};
|
|
1220
|
+
global.__temboryMemory.memoryDedupe = global.__temboryMemory.memoryDedupe || {};
|
|
1218
1221
|
return global.__temboryMemory;
|
|
1219
1222
|
}
|
|
1220
1223
|
};
|
|
@@ -1268,6 +1271,8 @@ const mergeRemoteThreadState = (store, key, state) => {
|
|
|
1268
1271
|
store.memoryCompression[key] = state.memoryCompression;
|
|
1269
1272
|
if (state.captureState && typeof state.captureState === 'object')
|
|
1270
1273
|
store.captureState[key] = { ...(store.captureState[key] || {}), ...state.captureState };
|
|
1274
|
+
if (state.memoryDedupe && typeof state.memoryDedupe === 'object')
|
|
1275
|
+
store.memoryDedupe[key] = mergeMemoryDedupeState(store.memoryDedupe[key], state.memoryDedupe);
|
|
1271
1276
|
if (typeof state.activeSummary === 'string' && state.activeSummary)
|
|
1272
1277
|
store.activeSummary[key] = state.activeSummary;
|
|
1273
1278
|
};
|
|
@@ -1632,6 +1637,195 @@ const normalizeIntentText = (value = '') => String(value || '')
|
|
|
1632
1637
|
.toLowerCase()
|
|
1633
1638
|
.normalize('NFD')
|
|
1634
1639
|
.replace(/[\u0300-\u036f]/g, '');
|
|
1640
|
+
const MEMORY_DEDUPE_MAX_KEYS = 600;
|
|
1641
|
+
const LEDGER_MEMORY_KINDS = new Set(['conversation_ledger', 'tool_ledger', 'turn_archive']);
|
|
1642
|
+
const normalizeDedupeText = (value = '') => normalizeIntentText(value).replace(/\s+/g, ' ').trim();
|
|
1643
|
+
const memoryBodyText = (body = {}) => {
|
|
1644
|
+
const messages = Array.isArray(body.messages) ? body.messages : [];
|
|
1645
|
+
if (messages.length)
|
|
1646
|
+
return messages.map((message) => message.content || message.text || message.message || '').filter(Boolean).join('\n');
|
|
1647
|
+
return body.memory || body.text || body.value || body.content || '';
|
|
1648
|
+
};
|
|
1649
|
+
const parseTurnArchiveText = (text = '') => {
|
|
1650
|
+
const raw = String(text || '');
|
|
1651
|
+
const start = raw.indexOf('{');
|
|
1652
|
+
if (start < 0)
|
|
1653
|
+
return null;
|
|
1654
|
+
try {
|
|
1655
|
+
const parsed = JSON.parse(raw.slice(start));
|
|
1656
|
+
return parsed && parsed.marker === 'tembory_turn_archive_v1' ? parsed : null;
|
|
1657
|
+
}
|
|
1658
|
+
catch {
|
|
1659
|
+
return null;
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
const compactToolForDedupe = (tool = {}, index = 0) => ({
|
|
1663
|
+
id: tool.id || tool.callId || tool.call_id || undefined,
|
|
1664
|
+
sequence: tool.sequence || index + 1,
|
|
1665
|
+
name: tool.name || tool.tool || tool.toolName || '',
|
|
1666
|
+
input: canonicalToolInput(tool.input || tool.toolInput || tool.args || ''),
|
|
1667
|
+
ok: tool.ok !== false && tool.status !== 'failed',
|
|
1668
|
+
result: String(tool.result || tool.output || ''),
|
|
1669
|
+
});
|
|
1670
|
+
const canonicalMemoryPayloadForDedupe = (kind = 'memory', body = {}, diagnostics = {}) => {
|
|
1671
|
+
const meta = body.metadata || {};
|
|
1672
|
+
const text = memoryBodyText(body);
|
|
1673
|
+
if (kind === 'recent_message') {
|
|
1674
|
+
const marker = parseRecentMessageMarker(text);
|
|
1675
|
+
return stableStringify({
|
|
1676
|
+
role: meta.role || marker?.role || 'user',
|
|
1677
|
+
content: normalizeDedupeText(meta.content || marker?.content || text),
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
if (kind === 'tool_history') {
|
|
1681
|
+
const marker = parseToolHistoryMarker(text);
|
|
1682
|
+
const tool = marker || {
|
|
1683
|
+
id: meta.id,
|
|
1684
|
+
turnId: meta.turn_id || meta.turnId,
|
|
1685
|
+
sequence: meta.sequence,
|
|
1686
|
+
name: meta.name || diagnostics.tool,
|
|
1687
|
+
input: meta.input,
|
|
1688
|
+
ok: meta.ok,
|
|
1689
|
+
result: meta.result,
|
|
1690
|
+
};
|
|
1691
|
+
return stableStringify(compactToolForDedupe(tool));
|
|
1692
|
+
}
|
|
1693
|
+
if (kind === 'tool_ledger') {
|
|
1694
|
+
return stableStringify(dedupeToolHistory(parseToolLedgerMarker(text)).map(compactToolForDedupe));
|
|
1695
|
+
}
|
|
1696
|
+
if (kind === 'conversation_ledger') {
|
|
1697
|
+
const messages = parseConversationLedgerMarker(text);
|
|
1698
|
+
return stableStringify((messages || []).map((message) => ({
|
|
1699
|
+
role: message.role || 'user',
|
|
1700
|
+
content: normalizeDedupeText(message.content || ''),
|
|
1701
|
+
})));
|
|
1702
|
+
}
|
|
1703
|
+
if (kind === 'turn_archive') {
|
|
1704
|
+
const archive = parseTurnArchiveText(text);
|
|
1705
|
+
if (archive) {
|
|
1706
|
+
return stableStringify({
|
|
1707
|
+
conversation: (archive.conversation || []).map((message) => ({
|
|
1708
|
+
role: message.role || 'user',
|
|
1709
|
+
content: normalizeDedupeText(message.content || ''),
|
|
1710
|
+
})),
|
|
1711
|
+
tools: (archive.tools || []).map(compactToolForDedupe),
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
return normalizeDedupeText(String(text || '')
|
|
1716
|
+
.replace(/"generated_at"\s*:\s*"[^"]+"/g, '"generated_at":"<ts>"')
|
|
1717
|
+
.replace(/"timestamp"\s*:\s*"[^"]+"/g, '"timestamp":"<ts>"'));
|
|
1718
|
+
};
|
|
1719
|
+
const inferMemoryKindFromText = (text = '') => {
|
|
1720
|
+
const raw = String(text || '');
|
|
1721
|
+
if (parseRecentMessageMarker(raw))
|
|
1722
|
+
return 'recent_message';
|
|
1723
|
+
if (parseToolHistoryMarker(raw))
|
|
1724
|
+
return 'tool_history';
|
|
1725
|
+
if (parseToolLedgerMarker(raw).length)
|
|
1726
|
+
return 'tool_ledger';
|
|
1727
|
+
if (parseConversationLedgerMarker(raw).length)
|
|
1728
|
+
return 'conversation_ledger';
|
|
1729
|
+
if (parseTurnArchiveText(raw))
|
|
1730
|
+
return 'turn_archive';
|
|
1731
|
+
return '';
|
|
1732
|
+
};
|
|
1733
|
+
const buildMemoryDedupeCandidate = (body = {}, diagnostics = {}) => {
|
|
1734
|
+
const meta = body.metadata || {};
|
|
1735
|
+
const text = memoryBodyText(body);
|
|
1736
|
+
const kind = String(meta.kind || diagnostics.kind || inferMemoryKindFromText(text) || 'memory');
|
|
1737
|
+
const thread = String(meta.thread_id || diagnostics.thread_id || body.thread_id || '');
|
|
1738
|
+
const project = String(meta.project || diagnostics.project || body.project || '');
|
|
1739
|
+
const slot = [kind, thread, project].filter(Boolean).join(':') || kind;
|
|
1740
|
+
const canonical = canonicalMemoryPayloadForDedupe(kind, body, diagnostics);
|
|
1741
|
+
const key = `${slot}:${stableHash(canonical)}`;
|
|
1742
|
+
return cleanContextValue({
|
|
1743
|
+
key,
|
|
1744
|
+
slot,
|
|
1745
|
+
kind,
|
|
1746
|
+
content_hash: stableHash(canonical),
|
|
1747
|
+
strategy: LEDGER_MEMORY_KINDS.has(kind) ? 'latest-ledger-per-content' : 'exact-canonical-content',
|
|
1748
|
+
});
|
|
1749
|
+
};
|
|
1750
|
+
const mergeMemoryDedupeState = (current = {}, incoming = {}) => {
|
|
1751
|
+
const merged = {
|
|
1752
|
+
seen: { ...((current || {}).seen || {}), ...((incoming || {}).seen || {}) },
|
|
1753
|
+
latestBySlot: { ...((current || {}).latestBySlot || {}), ...((incoming || {}).latestBySlot || {}) },
|
|
1754
|
+
lastSummary: (incoming || {}).lastSummary || (current || {}).lastSummary || undefined,
|
|
1755
|
+
updatedAt: (incoming || {}).updatedAt || (current || {}).updatedAt || undefined,
|
|
1756
|
+
};
|
|
1757
|
+
const entries = Object.entries(merged.seen);
|
|
1758
|
+
if (entries.length > MEMORY_DEDUPE_MAX_KEYS) {
|
|
1759
|
+
entries
|
|
1760
|
+
.sort((a, b) => String(a[1] || '').localeCompare(String(b[1] || '')))
|
|
1761
|
+
.slice(0, entries.length - MEMORY_DEDUPE_MAX_KEYS)
|
|
1762
|
+
.forEach(([key]) => delete merged.seen[key]);
|
|
1763
|
+
}
|
|
1764
|
+
return cleanContextValue(merged);
|
|
1765
|
+
};
|
|
1766
|
+
const ensureMemoryDedupeState = (store, key) => {
|
|
1767
|
+
store.memoryDedupe = store.memoryDedupe || {};
|
|
1768
|
+
store.memoryDedupe[key] = mergeMemoryDedupeState(store.memoryDedupe[key] || {}, {});
|
|
1769
|
+
store.memoryDedupe[key].seen = store.memoryDedupe[key].seen || {};
|
|
1770
|
+
store.memoryDedupe[key].latestBySlot = store.memoryDedupe[key].latestBySlot || {};
|
|
1771
|
+
return store.memoryDedupe[key];
|
|
1772
|
+
};
|
|
1773
|
+
const createDedupeSummary = () => ({
|
|
1774
|
+
enabled: true,
|
|
1775
|
+
write: { candidates: 0, persisted: 0, skipped: 0, byKind: {} },
|
|
1776
|
+
load: {},
|
|
1777
|
+
});
|
|
1778
|
+
const recordDedupeWriteSummary = (summary, candidate, decision) => {
|
|
1779
|
+
if (!summary || !candidate)
|
|
1780
|
+
return;
|
|
1781
|
+
summary.write.candidates += 1;
|
|
1782
|
+
summary.write[decision.persist ? 'persisted' : 'skipped'] += 1;
|
|
1783
|
+
summary.write.byKind[candidate.kind] = summary.write.byKind[candidate.kind] || { candidates: 0, persisted: 0, skipped: 0 };
|
|
1784
|
+
summary.write.byKind[candidate.kind].candidates += 1;
|
|
1785
|
+
summary.write.byKind[candidate.kind][decision.persist ? 'persisted' : 'skipped'] += 1;
|
|
1786
|
+
};
|
|
1787
|
+
const evaluateMemoryDedupeWrite = (state, candidate, at = nowIso()) => {
|
|
1788
|
+
if (!candidate || !candidate.key)
|
|
1789
|
+
return { persist: true, reason: 'no_dedupe_key' };
|
|
1790
|
+
state.seen = state.seen || {};
|
|
1791
|
+
state.latestBySlot = state.latestBySlot || {};
|
|
1792
|
+
if (state.seen[candidate.key])
|
|
1793
|
+
return { persist: false, reason: 'duplicate_memory_key' };
|
|
1794
|
+
state.seen[candidate.key] = at;
|
|
1795
|
+
state.latestBySlot[candidate.slot] = candidate.key;
|
|
1796
|
+
state.updatedAt = at;
|
|
1797
|
+
return { persist: true, reason: 'new_memory_key' };
|
|
1798
|
+
};
|
|
1799
|
+
const dedupeMemoryItemsForLoad = (items = []) => {
|
|
1800
|
+
const list = Array.isArray(items) ? items : [];
|
|
1801
|
+
const seen = new Set();
|
|
1802
|
+
const seenSlots = new Set();
|
|
1803
|
+
const out = [];
|
|
1804
|
+
for (const item of [...list].reverse()) {
|
|
1805
|
+
const candidate = buildMemoryDedupeCandidate({ messages: [{ role: 'system', content: memoryText(item) }], metadata: metadataOf(item) }, {});
|
|
1806
|
+
const slotKey = LEDGER_MEMORY_KINDS.has(candidate.kind) ? candidate.slot : candidate.key;
|
|
1807
|
+
const key = LEDGER_MEMORY_KINDS.has(candidate.kind) ? slotKey : candidate.key;
|
|
1808
|
+
if (LEDGER_MEMORY_KINDS.has(candidate.kind)) {
|
|
1809
|
+
if (seenSlots.has(slotKey))
|
|
1810
|
+
continue;
|
|
1811
|
+
seenSlots.add(slotKey);
|
|
1812
|
+
}
|
|
1813
|
+
else if (seen.has(key)) {
|
|
1814
|
+
continue;
|
|
1815
|
+
}
|
|
1816
|
+
seen.add(key);
|
|
1817
|
+
out.push(item);
|
|
1818
|
+
}
|
|
1819
|
+
out.reverse();
|
|
1820
|
+
return {
|
|
1821
|
+
items: out,
|
|
1822
|
+
summary: cleanContextValue({
|
|
1823
|
+
input: list.length,
|
|
1824
|
+
kept: out.length,
|
|
1825
|
+
removed: Math.max(0, list.length - out.length),
|
|
1826
|
+
}),
|
|
1827
|
+
};
|
|
1828
|
+
};
|
|
1635
1829
|
const hasCommitIntent = (value = '') => /\b(confirm\w*|confim\w*|cnfirm\w*|cofnirm\w*|ocnfi\w*|ocnfia\w*|fechar|aprovar|finalizar|prosseguir|continuar|sim)\b/.test(normalizeIntentText(value));
|
|
1636
1830
|
const isConversationRecallQuery = (value = '') => {
|
|
1637
1831
|
const text = normalizeIntentText(value);
|
|
@@ -2287,6 +2481,7 @@ const compactSaveAuditForAgent = (capture = {}) => cleanContextValue({
|
|
|
2287
2481
|
tool_history_after_save: capture.last_save_tool_history_after_save,
|
|
2288
2482
|
thread_state_saved: capture.last_save_thread_state_saved,
|
|
2289
2483
|
backend_memory_persistence: capture.last_save_backend_memory_persistence,
|
|
2484
|
+
dedupe: capture.last_save_dedupe_summary,
|
|
2290
2485
|
},
|
|
2291
2486
|
});
|
|
2292
2487
|
const compactEntityTimelineForAgent = (timeline = [], maxItems = 8) => pruneByLimit(timeline || [], maxItems).map((item) => cleanContextValue({
|
|
@@ -2431,8 +2626,8 @@ const compactToolAuditForSideChannel = (tool = {}) => cleanContextValue({
|
|
|
2431
2626
|
status: tool.status || (tool.ok === false ? 'failed' : 'ok'),
|
|
2432
2627
|
at: tool.at || tool.timestamp,
|
|
2433
2628
|
timestamp: tool.timestamp || tool.at,
|
|
2434
|
-
input: truncate(String(tool.input || tool.tool_args || tool.normalized_args || ''),
|
|
2435
|
-
output: compactToolResult(tool.result !== undefined ? tool.result : tool.output !== undefined ? tool.output : tool.observation,
|
|
2629
|
+
input: truncate(String(tool.input || tool.tool_args || tool.normalized_args || ''), 300) || undefined,
|
|
2630
|
+
output: compactToolResult(tool.result !== undefined ? tool.result : tool.output !== undefined ? tool.output : tool.observation, 800),
|
|
2436
2631
|
});
|
|
2437
2632
|
const compactMessageForSideChannel = (message = {}) => {
|
|
2438
2633
|
const type = messageTypeOf(message) || String(message.role || 'message').toLowerCase();
|
|
@@ -2456,7 +2651,7 @@ const normalizeConversationRoleForSideChannel = (role = '') => {
|
|
|
2456
2651
|
return 'tool';
|
|
2457
2652
|
return normalized || 'message';
|
|
2458
2653
|
};
|
|
2459
|
-
const compactConversationTimelineForSideChannel = (conversation = {}, maxItems =
|
|
2654
|
+
const compactConversationTimelineForSideChannel = (conversation = {}, maxItems = 4, includeFull = false) => {
|
|
2460
2655
|
const chronological = Array.isArray(conversation.conversation_history_chronological)
|
|
2461
2656
|
? conversation.conversation_history_chronological
|
|
2462
2657
|
: [];
|
|
@@ -2472,13 +2667,12 @@ const compactConversationTimelineForSideChannel = (conversation = {}, maxItems =
|
|
|
2472
2667
|
at,
|
|
2473
2668
|
chars: content.length,
|
|
2474
2669
|
preview,
|
|
2475
|
-
|
|
2476
|
-
message: {
|
|
2670
|
+
message: includeFull ? {
|
|
2477
2671
|
role,
|
|
2478
2672
|
at,
|
|
2479
2673
|
content: role === 'system' ? '[system context hidden]' : truncate(content, 2000),
|
|
2480
2674
|
truncated: content.length > 2000,
|
|
2481
|
-
},
|
|
2675
|
+
} : undefined,
|
|
2482
2676
|
});
|
|
2483
2677
|
});
|
|
2484
2678
|
};
|
|
@@ -2508,11 +2702,11 @@ const compactMemoryEventPayload = (payload = {}) => {
|
|
|
2508
2702
|
compact.toolNames = compact.toolCalls
|
|
2509
2703
|
.map((tool) => (tool && (tool.name || tool.tool_name || tool.tool)) || '')
|
|
2510
2704
|
.filter(Boolean)
|
|
2511
|
-
.slice(0,
|
|
2705
|
+
.slice(0, 12);
|
|
2512
2706
|
compact.toolEvents = compact.toolCalls
|
|
2513
2707
|
.map((tool) => compactToolAuditForSideChannel(tool || {}))
|
|
2514
2708
|
.filter((tool) => tool && Object.keys(tool).length)
|
|
2515
|
-
.slice(0,
|
|
2709
|
+
.slice(0, 8);
|
|
2516
2710
|
delete compact.toolCalls;
|
|
2517
2711
|
}
|
|
2518
2712
|
for (const key of ['input', 'output', 'values', 'chatHistory', 'context', 'contextText', 'diagnostics']) {
|
|
@@ -2522,6 +2716,49 @@ const compactMemoryEventPayload = (payload = {}) => {
|
|
|
2522
2716
|
}
|
|
2523
2717
|
return compact;
|
|
2524
2718
|
};
|
|
2719
|
+
const compactLastSaveForSideChannel = (lastSave = {}) => cleanContextValue({
|
|
2720
|
+
saved: lastSave.saved,
|
|
2721
|
+
status: lastSave.status,
|
|
2722
|
+
at: lastSave.at,
|
|
2723
|
+
inputChars: lastSave.input_chars,
|
|
2724
|
+
outputChars: lastSave.output_chars,
|
|
2725
|
+
toolCallsCaptured: lastSave.tool_calls_captured,
|
|
2726
|
+
toolNames: lastSave.tool_names,
|
|
2727
|
+
conversationMessagesAfterSave: lastSave.conversation_messages_after_save,
|
|
2728
|
+
toolHistoryAfterSave: lastSave.tool_history_after_save,
|
|
2729
|
+
threadStateSaved: lastSave.thread_state_saved,
|
|
2730
|
+
backendPersistence: lastSave.backend_memory_persistence,
|
|
2731
|
+
});
|
|
2732
|
+
const compactDedupeForSideChannel = (dedupe = {}) => {
|
|
2733
|
+
const load = dedupe.load || {};
|
|
2734
|
+
const removedOnLoad = Object.values(load).reduce((sum, item) => sum + Number((item || {}).removed || 0), 0);
|
|
2735
|
+
const write = dedupe.lastSave?.write || dedupe.write || {};
|
|
2736
|
+
return cleanContextValue({
|
|
2737
|
+
enabled: dedupe.enabled,
|
|
2738
|
+
removedOnLoad,
|
|
2739
|
+
skippedWrites: Number(write.skipped || 0),
|
|
2740
|
+
persistedWrites: Number(write.persisted || 0),
|
|
2741
|
+
candidates: Number(write.candidates || 0),
|
|
2742
|
+
});
|
|
2743
|
+
};
|
|
2744
|
+
const loadedSectionsForSideChannel = (parsed = {}, memoryAudit = {}) => cleanContextValue({
|
|
2745
|
+
conversation: Boolean(parsed.conversation),
|
|
2746
|
+
summary: Boolean(parsed.summary),
|
|
2747
|
+
activeSummary: Boolean(parsed.activeSummary),
|
|
2748
|
+
connectedModelSummary: Boolean(parsed.connectedModelSummary),
|
|
2749
|
+
workingMemory: Boolean(parsed.workingMemory),
|
|
2750
|
+
decisionState: Boolean(parsed.decisionState),
|
|
2751
|
+
memoryCompression: Boolean(parsed.memoryCompression),
|
|
2752
|
+
operationalState: Boolean(parsed.operationalState),
|
|
2753
|
+
actionLedger: Array.isArray(parsed.actionLedger),
|
|
2754
|
+
turnBrief: Boolean(parsed.turnBrief || parsed.turn_brief),
|
|
2755
|
+
memoryAudit: Boolean(memoryAudit && Object.keys(memoryAudit).length),
|
|
2756
|
+
entityTimeline: Array.isArray(parsed.entityTimeline),
|
|
2757
|
+
vectorMemories: Array.isArray(parsed.vectorMemories),
|
|
2758
|
+
graph: Array.isArray(parsed.graph),
|
|
2759
|
+
recentHighlights: Array.isArray(parsed.recentHighlights),
|
|
2760
|
+
contextHealth: Boolean(parsed.contextHealth),
|
|
2761
|
+
});
|
|
2525
2762
|
const summarizeMemoryMessagesForSideChannel = (messages = []) => {
|
|
2526
2763
|
const list = Array.isArray(messages) ? messages : [];
|
|
2527
2764
|
const summary = { messages: list.length };
|
|
@@ -2538,20 +2775,45 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
|
|
|
2538
2775
|
const toolItems = Array.isArray(tools.items) ? tools.items : [];
|
|
2539
2776
|
const memoryAudit = parsed.memoryAudit || parsed.memory_audit || {};
|
|
2540
2777
|
const summaryText = parsed.summary?.slm || parsed.summary || parsed.connectedModelSummary || parsed.activeSummary || '';
|
|
2778
|
+
const chronological = Array.isArray(conversation.conversation_history_chronological)
|
|
2779
|
+
? conversation.conversation_history_chronological
|
|
2780
|
+
: [];
|
|
2781
|
+
const allUserMessages = Array.isArray(conversation.all_user_messages_chronological)
|
|
2782
|
+
? conversation.all_user_messages_chronological
|
|
2783
|
+
: chronological.filter((message) => normalizeConversationRoleForSideChannel(message.role || message.type) === 'user');
|
|
2784
|
+
const lastUser = [...chronological].reverse().find((message) => normalizeConversationRoleForSideChannel(message.role || message.type) === 'user');
|
|
2785
|
+
const lastAgent = [...chronological].reverse().find((message) => normalizeConversationRoleForSideChannel(message.role || message.type) === 'agent');
|
|
2786
|
+
const includeDebug = Boolean(parsed.options?.includeDiagnostics || parsed.diagnostics?.includeDiagnostics || parsed.diagnostics?.include_diagnostics);
|
|
2787
|
+
const lastSave = compactLastSaveForSideChannel(memoryAudit.last_save || parsed.state?.last_save || {});
|
|
2788
|
+
const fullDedupeSummary = parsed.dedupeSummary || parsed.diagnostics?.dedupeSummary || undefined;
|
|
2789
|
+
const loadedSections = loadedSectionsForSideChannel(parsed, memoryAudit);
|
|
2541
2790
|
summary.userId = parsed.userId;
|
|
2542
2791
|
summary.project = parsed.project || undefined;
|
|
2543
2792
|
summary.retrievalMode = parsed.retrievalMode;
|
|
2544
2793
|
summary.payloadFormat = parsed.payloadFormat;
|
|
2545
|
-
summary.options = cleanContextValue(parsed.options || {});
|
|
2546
2794
|
summary.intent = parsed.observations?.inferred_intent?.label || parsed.workingMemory?.last_user_intent || parsed.decisionState?.current_intent || undefined;
|
|
2547
2795
|
const parsedTurnBrief = parsed.turnBrief || parsed.turn_brief || undefined;
|
|
2796
|
+
summary.currentTurn = parsed.currentTurn || parsed.current_turn || parsed.diagnostics?.currentTurn || undefined;
|
|
2548
2797
|
summary.currentUserMessage = conversation.current_user_message ? truncate(conversation.current_user_message, 180) : undefined;
|
|
2549
|
-
summary.recentMessages =
|
|
2550
|
-
summary.
|
|
2798
|
+
summary.recentMessages = chronological.length || undefined;
|
|
2799
|
+
summary.conversation = cleanContextValue({
|
|
2800
|
+
messages: chronological.length,
|
|
2801
|
+
userMessages: allUserMessages.length,
|
|
2802
|
+
currentUserMessage: summary.currentUserMessage,
|
|
2803
|
+
lastUser: lastUser ? {
|
|
2804
|
+
at: lastUser.at || lastUser.timestamp || lastUser.created_at || lastUser.createdAt,
|
|
2805
|
+
preview: truncate(String(lastUser.content || lastUser.text || lastUser.message || ''), 240),
|
|
2806
|
+
} : undefined,
|
|
2807
|
+
lastAgent: lastAgent ? {
|
|
2808
|
+
at: lastAgent.at || lastAgent.timestamp || lastAgent.created_at || lastAgent.createdAt,
|
|
2809
|
+
preview: truncate(String(lastAgent.content || lastAgent.text || lastAgent.message || ''), 240),
|
|
2810
|
+
} : undefined,
|
|
2811
|
+
});
|
|
2812
|
+
summary.conversationTimeline = compactConversationTimelineForSideChannel(conversation, 4);
|
|
2551
2813
|
summary.toolCount = toolItems.length || tools.count || parsed.operationalState?.tool_counts?.total || undefined;
|
|
2552
|
-
summary.toolNames = toolItems.map((tool) => tool.name || tool.tool_name).filter(Boolean).slice(0,
|
|
2814
|
+
summary.toolNames = toolItems.map((tool) => tool.name || tool.tool_name).filter(Boolean).slice(0, 12);
|
|
2553
2815
|
summary.toolEvents = toolItems
|
|
2554
|
-
.slice(-
|
|
2816
|
+
.slice(-3)
|
|
2555
2817
|
.map((tool) => compactToolAuditForSideChannel(tool || {}))
|
|
2556
2818
|
.filter((tool) => tool && Object.keys(tool).length);
|
|
2557
2819
|
summary.lastTool = tools.last_successful_tool
|
|
@@ -2571,8 +2833,8 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
|
|
|
2571
2833
|
})
|
|
2572
2834
|
: parsedTurnBrief;
|
|
2573
2835
|
summary.counts = cleanContextValue({
|
|
2574
|
-
conversationMessages:
|
|
2575
|
-
userMessages:
|
|
2836
|
+
conversationMessages: chronological.length || undefined,
|
|
2837
|
+
userMessages: allUserMessages.length || undefined,
|
|
2576
2838
|
toolHistory: toolItems.length,
|
|
2577
2839
|
actionLedger: Array.isArray(parsed.actionLedger) ? parsed.actionLedger.length : undefined,
|
|
2578
2840
|
entityTimeline: Array.isArray(parsed.entityTimeline) ? parsed.entityTimeline.length : undefined,
|
|
@@ -2580,25 +2842,8 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
|
|
|
2580
2842
|
graph: Array.isArray(parsed.graph) ? parsed.graph.length : undefined,
|
|
2581
2843
|
recentHighlights: Array.isArray(parsed.recentHighlights) ? parsed.recentHighlights.length : undefined,
|
|
2582
2844
|
});
|
|
2583
|
-
summary.lastSave =
|
|
2584
|
-
summary.
|
|
2585
|
-
conversation: Boolean(parsed.conversation),
|
|
2586
|
-
summary: Boolean(parsed.summary),
|
|
2587
|
-
activeSummary: Boolean(parsed.activeSummary),
|
|
2588
|
-
connectedModelSummary: Boolean(parsed.connectedModelSummary),
|
|
2589
|
-
workingMemory: Boolean(parsed.workingMemory),
|
|
2590
|
-
decisionState: Boolean(parsed.decisionState),
|
|
2591
|
-
memoryCompression: Boolean(parsed.memoryCompression),
|
|
2592
|
-
operationalState: Boolean(parsed.operationalState),
|
|
2593
|
-
actionLedger: Array.isArray(parsed.actionLedger),
|
|
2594
|
-
turnBrief: Boolean(parsed.turnBrief || parsed.turn_brief),
|
|
2595
|
-
memoryAudit: Boolean(memoryAudit && Object.keys(memoryAudit).length),
|
|
2596
|
-
entityTimeline: Array.isArray(parsed.entityTimeline),
|
|
2597
|
-
vectorMemories: Array.isArray(parsed.vectorMemories),
|
|
2598
|
-
graph: Array.isArray(parsed.graph),
|
|
2599
|
-
recentHighlights: Array.isArray(parsed.recentHighlights),
|
|
2600
|
-
contextHealth: Boolean(parsed.contextHealth),
|
|
2601
|
-
});
|
|
2845
|
+
summary.lastSave = Object.keys(lastSave).length ? lastSave : undefined;
|
|
2846
|
+
summary.dedupe = fullDedupeSummary ? compactDedupeForSideChannel(fullDedupeSummary) : undefined;
|
|
2602
2847
|
summary.workingMemory = cleanContextValue({
|
|
2603
2848
|
currentGoal: parsed.workingMemory?.current_goal,
|
|
2604
2849
|
currentGoalReason: parsed.workingMemory?.current_goal_reason,
|
|
@@ -2619,8 +2864,14 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
|
|
|
2619
2864
|
doNotRepeatTools: parsed.decisionState?.do_not_repeat_tools,
|
|
2620
2865
|
});
|
|
2621
2866
|
summary.quality = parsed.contextHealth?.quality_score || parsed.contextQualityScore || undefined;
|
|
2622
|
-
summary.
|
|
2623
|
-
|
|
2867
|
+
summary.debug = includeDebug ? cleanContextValue({
|
|
2868
|
+
options: parsed.options,
|
|
2869
|
+
loadedSections,
|
|
2870
|
+
contextSize: parsed.diagnostics?.contextSize,
|
|
2871
|
+
summaryChars: typeof summaryText === 'string' ? summaryText.length : safeStringify(summaryText).length,
|
|
2872
|
+
dedupeSummary: fullDedupeSummary,
|
|
2873
|
+
conversationTimelineFull: compactConversationTimelineForSideChannel(conversation, 12, true),
|
|
2874
|
+
}) : undefined;
|
|
2624
2875
|
return Object.fromEntries(Object.entries(summary).filter(([, value]) => value !== undefined));
|
|
2625
2876
|
}
|
|
2626
2877
|
catch {
|
|
@@ -2837,6 +3088,7 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
|
|
|
2837
3088
|
current_user_request: focus.current_user_request,
|
|
2838
3089
|
instruction: focus.instruction,
|
|
2839
3090
|
}),
|
|
3091
|
+
currentTurn: diagnostics?.currentTurn,
|
|
2840
3092
|
action_directive: directive ? cleanContextValue({
|
|
2841
3093
|
required_tool: directive.required_tool,
|
|
2842
3094
|
next_expected_action: directive.next_expected_action,
|
|
@@ -2856,6 +3108,7 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
|
|
|
2856
3108
|
profile: sectionValue('profile_facts'),
|
|
2857
3109
|
tools: compactToolLedger,
|
|
2858
3110
|
memoryAudit,
|
|
3111
|
+
dedupeSummary: diagnostics?.dedupeSummary,
|
|
2859
3112
|
});
|
|
2860
3113
|
const renderCompactSection = (section) => {
|
|
2861
3114
|
if (section.value === null || section.value === undefined)
|
|
@@ -3375,6 +3628,58 @@ class TemboryMemory {
|
|
|
3375
3628
|
const output = cleanAssistantTranscriptText(rawOutput);
|
|
3376
3629
|
const toolCalls = extractToolCalls(outputValues);
|
|
3377
3630
|
const saveAt = nowIso();
|
|
3631
|
+
const dedupeState = ensureMemoryDedupeState(store, key);
|
|
3632
|
+
const dedupeSummary = createDedupeSummary();
|
|
3633
|
+
const persistLegacyMemory = async (legacyBody, diagnostics = {}) => {
|
|
3634
|
+
const candidate = buildMemoryDedupeCandidate(legacyBody, diagnostics);
|
|
3635
|
+
const decision = evaluateMemoryDedupeWrite(dedupeState, candidate, saveAt);
|
|
3636
|
+
recordDedupeWriteSummary(dedupeSummary, candidate, decision);
|
|
3637
|
+
if (!decision.persist)
|
|
3638
|
+
return { skipped: true, reason: decision.reason, dedupeKey: candidate.key };
|
|
3639
|
+
const bodyWithDedupe = {
|
|
3640
|
+
...legacyBody,
|
|
3641
|
+
metadata: cleanContextValue({
|
|
3642
|
+
...(legacyBody.metadata || {}),
|
|
3643
|
+
memory_dedupe_key: candidate.key,
|
|
3644
|
+
memory_dedupe_slot: candidate.slot,
|
|
3645
|
+
memory_dedupe_strategy: candidate.strategy,
|
|
3646
|
+
}),
|
|
3647
|
+
};
|
|
3648
|
+
return await safePersistLegacyMemory(this, bodyWithDedupe, {
|
|
3649
|
+
...diagnostics,
|
|
3650
|
+
memory_dedupe_key: candidate.key,
|
|
3651
|
+
memory_dedupe_slot: candidate.slot,
|
|
3652
|
+
});
|
|
3653
|
+
};
|
|
3654
|
+
const finalizeDedupeSummary = async () => {
|
|
3655
|
+
dedupeState.lastSummary = dedupeSummary;
|
|
3656
|
+
dedupeState.updatedAt = nowIso();
|
|
3657
|
+
store.memoryDedupe[key] = mergeMemoryDedupeState(store.memoryDedupe[key], dedupeState);
|
|
3658
|
+
store.captureState[key] = cleanContextValue({
|
|
3659
|
+
...(store.captureState[key] || {}),
|
|
3660
|
+
last_save_dedupe_summary: dedupeSummary,
|
|
3661
|
+
});
|
|
3662
|
+
const finalThreadStateSaved = await saveThreadState(this, key, threadId, project, {
|
|
3663
|
+
kind: 'tembory.thread_state.v1',
|
|
3664
|
+
threadId,
|
|
3665
|
+
project: project || undefined,
|
|
3666
|
+
updatedAt: nowIso(),
|
|
3667
|
+
recentMessages: recentForTurn,
|
|
3668
|
+
toolHistory: toolHistoryForTurn,
|
|
3669
|
+
profileFacts: store.profileFacts[key] || {},
|
|
3670
|
+
workingMemory: workingMemoryForTurn,
|
|
3671
|
+
decisionState: decisionStateForTurn,
|
|
3672
|
+
memoryCompression: compressionForTurn,
|
|
3673
|
+
operationalState: operationalStateForTurn,
|
|
3674
|
+
captureState: store.captureState[key],
|
|
3675
|
+
memoryDedupe: store.memoryDedupe[key],
|
|
3676
|
+
activeSummary: store.activeSummary[key] || '',
|
|
3677
|
+
});
|
|
3678
|
+
store.captureState[key] = cleanContextValue({
|
|
3679
|
+
...(store.captureState[key] || {}),
|
|
3680
|
+
last_save_thread_state_saved: (store.captureState[key] || {}).last_save_thread_state_saved || finalThreadStateSaved,
|
|
3681
|
+
});
|
|
3682
|
+
};
|
|
3378
3683
|
store.captureState[key] = {
|
|
3379
3684
|
last_save_status: 'started',
|
|
3380
3685
|
last_save_saved: false,
|
|
@@ -3388,6 +3693,7 @@ class TemboryMemory {
|
|
|
3388
3693
|
last_save_tool_calls_captured: toolCalls.length,
|
|
3389
3694
|
last_save_tool_names: toolCalls.map((tool) => tool.name).filter(Boolean).slice(0, 20),
|
|
3390
3695
|
last_save_capture_sources: Array.from(new Set(toolCalls.map((tool) => tool.source).filter(Boolean))),
|
|
3696
|
+
last_save_dedupe_summary: dedupeSummary,
|
|
3391
3697
|
};
|
|
3392
3698
|
const recentForTembory = [];
|
|
3393
3699
|
const profileFromTurn = extractProfileFactsFromText(input, 'user_message', nowIso());
|
|
@@ -3488,6 +3794,7 @@ class TemboryMemory {
|
|
|
3488
3794
|
: adv.useVectorMemory === false
|
|
3489
3795
|
? 'thread_state_only'
|
|
3490
3796
|
: 'enabled',
|
|
3797
|
+
last_save_dedupe_summary: dedupeSummary,
|
|
3491
3798
|
});
|
|
3492
3799
|
const threadStateSaved = await saveThreadState(this, key, threadId, project, {
|
|
3493
3800
|
kind: 'tembory.thread_state.v1',
|
|
@@ -3502,6 +3809,7 @@ class TemboryMemory {
|
|
|
3502
3809
|
memoryCompression: compressionForTurn,
|
|
3503
3810
|
operationalState: operationalStateForTurn,
|
|
3504
3811
|
captureState: nextCaptureState,
|
|
3812
|
+
memoryDedupe: dedupeState,
|
|
3505
3813
|
activeSummary: store.activeSummary[key] || '',
|
|
3506
3814
|
});
|
|
3507
3815
|
store.captureState[key] = cleanContextValue({
|
|
@@ -3513,10 +3821,14 @@ class TemboryMemory {
|
|
|
3513
3821
|
globalData.__dataChanged = true;
|
|
3514
3822
|
}
|
|
3515
3823
|
catch { }
|
|
3516
|
-
if (adv.persistBackendMemories === false)
|
|
3824
|
+
if (adv.persistBackendMemories === false) {
|
|
3825
|
+
await finalizeDedupeSummary();
|
|
3517
3826
|
return;
|
|
3518
|
-
|
|
3827
|
+
}
|
|
3828
|
+
if (adv.useVectorMemory === false) {
|
|
3829
|
+
await finalizeDedupeSummary();
|
|
3519
3830
|
return;
|
|
3831
|
+
}
|
|
3520
3832
|
const connectedEmbedding = await getConnectedEmbedding(this, itemIndex);
|
|
3521
3833
|
if (connectedEmbedding) {
|
|
3522
3834
|
const ids = { user_id: body.user_id, agent_id: body.agent_id, run_id: body.run_id };
|
|
@@ -3529,21 +3841,32 @@ class TemboryMemory {
|
|
|
3529
3841
|
memoryCompression: compressionForTurn,
|
|
3530
3842
|
operationalState: operationalStateForTurn,
|
|
3531
3843
|
});
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3844
|
+
const archiveMetadata = {
|
|
3845
|
+
kind: 'turn_archive',
|
|
3846
|
+
thread_id: threadId,
|
|
3847
|
+
project: project || undefined,
|
|
3848
|
+
source: 'n8n_connected_embedding',
|
|
3849
|
+
generated_at: nowIso(),
|
|
3850
|
+
message_count: recentForTurn.length,
|
|
3851
|
+
tool_count: toolHistoryForTurn.length,
|
|
3852
|
+
latest_tool: toolHistoryForTurn.at(-1)?.name || null,
|
|
3853
|
+
};
|
|
3854
|
+
const archiveCandidate = buildMemoryDedupeCandidate({ messages: [{ role: 'system', content: archiveText }], metadata: archiveMetadata }, { kind: 'turn_archive', user_id: body.user_id });
|
|
3855
|
+
const archiveDecision = evaluateMemoryDedupeWrite(dedupeState, archiveCandidate, saveAt);
|
|
3856
|
+
recordDedupeWriteSummary(dedupeSummary, archiveCandidate, archiveDecision);
|
|
3857
|
+
if (archiveDecision.persist) {
|
|
3858
|
+
await saveClientVectorMemories(this, [
|
|
3859
|
+
await createClientVectorMemory(connectedEmbedding, archiveText, {
|
|
3860
|
+
...archiveMetadata,
|
|
3861
|
+
memory_dedupe_key: archiveCandidate.key,
|
|
3862
|
+
memory_dedupe_slot: archiveCandidate.slot,
|
|
3863
|
+
memory_dedupe_strategy: archiveCandidate.strategy,
|
|
3864
|
+
}, ids),
|
|
3865
|
+
], ids);
|
|
3866
|
+
}
|
|
3544
3867
|
if (adv.includeRecentMessages !== false && recentForTembory.length) {
|
|
3545
3868
|
for (const recent of recentForTembory) {
|
|
3546
|
-
await
|
|
3869
|
+
await persistLegacyMemory({
|
|
3547
3870
|
messages: [{ role: 'system', content: encodeRecentMessage(recent, threadId) }],
|
|
3548
3871
|
infer: false,
|
|
3549
3872
|
user_id: body.user_id,
|
|
@@ -3561,9 +3884,10 @@ class TemboryMemory {
|
|
|
3561
3884
|
}, {
|
|
3562
3885
|
kind: 'recent_message',
|
|
3563
3886
|
user_id: body.user_id,
|
|
3887
|
+
thread_id: threadId,
|
|
3564
3888
|
});
|
|
3565
3889
|
}
|
|
3566
|
-
await
|
|
3890
|
+
await persistLegacyMemory({
|
|
3567
3891
|
messages: [{ role: 'system', content: encodeConversationLedger(recentForTurn, threadId) }],
|
|
3568
3892
|
infer: false,
|
|
3569
3893
|
user_id: body.user_id,
|
|
@@ -3579,11 +3903,12 @@ class TemboryMemory {
|
|
|
3579
3903
|
}, {
|
|
3580
3904
|
kind: 'conversation_ledger',
|
|
3581
3905
|
user_id: body.user_id,
|
|
3906
|
+
thread_id: threadId,
|
|
3582
3907
|
});
|
|
3583
3908
|
}
|
|
3584
3909
|
if (adv.includeToolHistory !== false && toolCalls.length) {
|
|
3585
3910
|
for (const tool of toolCalls) {
|
|
3586
|
-
await
|
|
3911
|
+
await persistLegacyMemory({
|
|
3587
3912
|
messages: [{ role: 'system', content: encodeToolCall(tool, threadId) }],
|
|
3588
3913
|
infer: false,
|
|
3589
3914
|
user_id: body.user_id,
|
|
@@ -3607,9 +3932,10 @@ class TemboryMemory {
|
|
|
3607
3932
|
kind: 'tool_history',
|
|
3608
3933
|
user_id: body.user_id,
|
|
3609
3934
|
tool: tool.name,
|
|
3935
|
+
thread_id: threadId,
|
|
3610
3936
|
});
|
|
3611
3937
|
}
|
|
3612
|
-
await
|
|
3938
|
+
await persistLegacyMemory({
|
|
3613
3939
|
messages: [{ role: 'system', content: encodeToolLedger(toolHistoryForTurn, threadId) }],
|
|
3614
3940
|
infer: false,
|
|
3615
3941
|
user_id: body.user_id,
|
|
@@ -3625,18 +3951,21 @@ class TemboryMemory {
|
|
|
3625
3951
|
}, {
|
|
3626
3952
|
kind: 'tool_ledger',
|
|
3627
3953
|
user_id: body.user_id,
|
|
3954
|
+
thread_id: threadId,
|
|
3628
3955
|
});
|
|
3629
3956
|
}
|
|
3957
|
+
await finalizeDedupeSummary();
|
|
3630
3958
|
return;
|
|
3631
3959
|
}
|
|
3632
3960
|
if (messages.length)
|
|
3633
|
-
await
|
|
3961
|
+
await persistLegacyMemory(body, {
|
|
3634
3962
|
kind: 'conversation_messages',
|
|
3635
3963
|
user_id: body.user_id,
|
|
3964
|
+
thread_id: threadId,
|
|
3636
3965
|
});
|
|
3637
3966
|
if (adv.includeRecentMessages !== false && recentForTembory.length) {
|
|
3638
3967
|
for (const recent of recentForTembory) {
|
|
3639
|
-
await
|
|
3968
|
+
await persistLegacyMemory({
|
|
3640
3969
|
messages: [{ role: 'system', content: encodeRecentMessage(recent, threadId) }],
|
|
3641
3970
|
infer: false,
|
|
3642
3971
|
user_id: body.user_id,
|
|
@@ -3653,9 +3982,10 @@ class TemboryMemory {
|
|
|
3653
3982
|
}, {
|
|
3654
3983
|
kind: 'recent_message',
|
|
3655
3984
|
user_id: body.user_id,
|
|
3985
|
+
thread_id: threadId,
|
|
3656
3986
|
});
|
|
3657
3987
|
}
|
|
3658
|
-
await
|
|
3988
|
+
await persistLegacyMemory({
|
|
3659
3989
|
messages: [{ role: 'system', content: encodeConversationLedger(recentForTurn, threadId) }],
|
|
3660
3990
|
infer: false,
|
|
3661
3991
|
user_id: body.user_id,
|
|
@@ -3671,11 +4001,12 @@ class TemboryMemory {
|
|
|
3671
4001
|
}, {
|
|
3672
4002
|
kind: 'conversation_ledger',
|
|
3673
4003
|
user_id: body.user_id,
|
|
4004
|
+
thread_id: threadId,
|
|
3674
4005
|
});
|
|
3675
4006
|
}
|
|
3676
4007
|
if (adv.includeToolHistory !== false && !adv.persistToolFactsToTembory && toolCalls.length) {
|
|
3677
4008
|
for (const tool of toolCalls) {
|
|
3678
|
-
await
|
|
4009
|
+
await persistLegacyMemory({
|
|
3679
4010
|
messages: [{ role: 'system', content: encodeToolCall(tool, threadId) }],
|
|
3680
4011
|
infer: false,
|
|
3681
4012
|
user_id: body.user_id,
|
|
@@ -3699,9 +4030,10 @@ class TemboryMemory {
|
|
|
3699
4030
|
kind: 'tool_history',
|
|
3700
4031
|
user_id: body.user_id,
|
|
3701
4032
|
tool: tool.name,
|
|
4033
|
+
thread_id: threadId,
|
|
3702
4034
|
});
|
|
3703
4035
|
}
|
|
3704
|
-
await
|
|
4036
|
+
await persistLegacyMemory({
|
|
3705
4037
|
messages: [{ role: 'system', content: encodeToolLedger(toolHistoryForTurn, threadId) }],
|
|
3706
4038
|
infer: false,
|
|
3707
4039
|
user_id: body.user_id,
|
|
@@ -3717,11 +4049,12 @@ class TemboryMemory {
|
|
|
3717
4049
|
}, {
|
|
3718
4050
|
kind: 'tool_ledger',
|
|
3719
4051
|
user_id: body.user_id,
|
|
4052
|
+
thread_id: threadId,
|
|
3720
4053
|
});
|
|
3721
4054
|
}
|
|
3722
4055
|
if (adv.persistToolFactsToTembory && toolCalls.length) {
|
|
3723
4056
|
const facts = toolCalls.map((tool) => `Tool ${tool.name} input=${tool.input}${tool.result ? ` result=${tool.result}` : ''}`).join('\n');
|
|
3724
|
-
await
|
|
4057
|
+
await persistLegacyMemory({
|
|
3725
4058
|
messages: [{ role: 'system', content: `Tool facts (read-only):\n${truncate(facts, 2000)}` }],
|
|
3726
4059
|
infer: false,
|
|
3727
4060
|
user_id: body.user_id,
|
|
@@ -3729,9 +4062,10 @@ class TemboryMemory {
|
|
|
3729
4062
|
}, {
|
|
3730
4063
|
kind: 'tool_facts',
|
|
3731
4064
|
user_id: body.user_id,
|
|
4065
|
+
thread_id: threadId,
|
|
3732
4066
|
});
|
|
3733
4067
|
for (const tool of toolCalls) {
|
|
3734
|
-
await
|
|
4068
|
+
await persistLegacyMemory({
|
|
3735
4069
|
messages: [{ role: 'system', content: encodeToolCall(tool, threadId) }],
|
|
3736
4070
|
infer: false,
|
|
3737
4071
|
user_id: body.user_id,
|
|
@@ -3754,9 +4088,29 @@ class TemboryMemory {
|
|
|
3754
4088
|
kind: 'tool_history',
|
|
3755
4089
|
user_id: body.user_id,
|
|
3756
4090
|
tool: tool.name,
|
|
4091
|
+
thread_id: threadId,
|
|
3757
4092
|
});
|
|
3758
4093
|
}
|
|
4094
|
+
await persistLegacyMemory({
|
|
4095
|
+
messages: [{ role: 'system', content: encodeToolLedger(toolHistoryForTurn, threadId) }],
|
|
4096
|
+
infer: false,
|
|
4097
|
+
user_id: body.user_id,
|
|
4098
|
+
agent_id: body.agent_id,
|
|
4099
|
+
run_id: body.run_id,
|
|
4100
|
+
metadata: {
|
|
4101
|
+
kind: 'tool_ledger',
|
|
4102
|
+
thread_id: threadId,
|
|
4103
|
+
project: project || undefined,
|
|
4104
|
+
source: 'tembory_transcript',
|
|
4105
|
+
generated_at: nowIso(),
|
|
4106
|
+
},
|
|
4107
|
+
}, {
|
|
4108
|
+
kind: 'tool_ledger',
|
|
4109
|
+
user_id: body.user_id,
|
|
4110
|
+
thread_id: threadId,
|
|
4111
|
+
});
|
|
3759
4112
|
}
|
|
4113
|
+
await finalizeDedupeSummary();
|
|
3760
4114
|
}
|
|
3761
4115
|
async loadMemoryVariablesForItem(itemIndex, inputValues = {}) {
|
|
3762
4116
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
@@ -3773,7 +4127,8 @@ class TemboryMemory {
|
|
|
3773
4127
|
const store = getMemoryStore(this);
|
|
3774
4128
|
const key = userKeyFrom(threadId, adv, project);
|
|
3775
4129
|
const queryParam = this.getNodeParameter('query', itemIndex, '');
|
|
3776
|
-
const
|
|
4130
|
+
const currentTurn = resolveCurrentTurnQueryWithSource(this, itemIndex, inputValues, queryParam);
|
|
4131
|
+
const query = currentTurn.query;
|
|
3777
4132
|
const remoteThreadState = await loadThreadState(this, key, threadId, project);
|
|
3778
4133
|
mergeRemoteThreadState(store, key, remoteThreadState);
|
|
3779
4134
|
let connectedLanguageModel;
|
|
@@ -3802,12 +4157,18 @@ class TemboryMemory {
|
|
|
3802
4157
|
retrievalMode,
|
|
3803
4158
|
requestedRetrievalMode,
|
|
3804
4159
|
hasQuery: String(query || '').trim().length > 0,
|
|
4160
|
+
querySource: currentTurn.source,
|
|
3805
4161
|
connectedAi,
|
|
3806
4162
|
});
|
|
3807
4163
|
}
|
|
3808
4164
|
catch { }
|
|
3809
4165
|
let payload;
|
|
3810
4166
|
let vectorMemories = [];
|
|
4167
|
+
const loadDedupeSummary = {
|
|
4168
|
+
enabled: true,
|
|
4169
|
+
load: {},
|
|
4170
|
+
lastSave: store.memoryDedupe[key]?.lastSummary,
|
|
4171
|
+
};
|
|
3811
4172
|
if (vectorMemoryEnabled && (retrievalMode === 'semantic' || retrievalMode === 'semanticV2' || retrievalMode === 'hybrid')) {
|
|
3812
4173
|
const body = { query: String(query || '') };
|
|
3813
4174
|
// IDs
|
|
@@ -4068,6 +4429,12 @@ class TemboryMemory {
|
|
|
4068
4429
|
connectedAi.errors.push(`persistedContext.load: ${error.message || String(error)}`);
|
|
4069
4430
|
}
|
|
4070
4431
|
}
|
|
4432
|
+
const vectorDedupe = dedupeMemoryItemsForLoad(vectorMemories);
|
|
4433
|
+
vectorMemories = vectorDedupe.items;
|
|
4434
|
+
loadDedupeSummary.load.vectorMemories = vectorDedupe.summary;
|
|
4435
|
+
const persistedDedupe = dedupeMemoryItemsForLoad(persistedMemoryItems);
|
|
4436
|
+
persistedMemoryItems = persistedDedupe.items;
|
|
4437
|
+
loadDedupeSummary.load.persistedMemories = persistedDedupe.summary;
|
|
4071
4438
|
if (adv.includeRecentMessages !== false) {
|
|
4072
4439
|
persistedRecentMessages = persistedMemoryItems
|
|
4073
4440
|
.flatMap(recentMessagesFromMemory)
|
|
@@ -4082,7 +4449,13 @@ class TemboryMemory {
|
|
|
4082
4449
|
.sort((a, b) => new Date(a.at || 0).getTime() - new Date(b.at || 0).getTime());
|
|
4083
4450
|
}
|
|
4084
4451
|
const storedRecentMessages = adv.includeRecentMessages === false ? [] : (store.recentMessages[key] || []).concat(persistedRecentMessages, vectorRecentMessages);
|
|
4085
|
-
const
|
|
4452
|
+
const appendedRecentMessages = appendCurrentUserMessage(storedRecentMessages, query);
|
|
4453
|
+
const allRecentMessages = dedupeRecentMessages(appendedRecentMessages);
|
|
4454
|
+
loadDedupeSummary.load.recentMessages = cleanContextValue({
|
|
4455
|
+
input: appendedRecentMessages.length,
|
|
4456
|
+
kept: allRecentMessages.length,
|
|
4457
|
+
removed: Math.max(0, appendedRecentMessages.length - allRecentMessages.length),
|
|
4458
|
+
});
|
|
4086
4459
|
const recentMessages = pruneConversationMessagesPreserveAnchors(allRecentMessages, Math.max(Number(adv.recentMessagesLastN || 8), 8));
|
|
4087
4460
|
const toolHistoryFromRecentMessages = [];
|
|
4088
4461
|
if (adv.includeToolHistory !== false) {
|
|
@@ -4091,7 +4464,13 @@ class TemboryMemory {
|
|
|
4091
4464
|
}
|
|
4092
4465
|
const toolHistoryFromVectorMarkers = adv.includeToolHistory === false ? [] : vectorMemories.flatMap(explicitToolHistoryItemsFromMemory);
|
|
4093
4466
|
const toolHistoryFromVectorMemories = adv.includeToolHistory === false || adv.includeToolHistorySemanticFallback !== true ? [] : vectorMemories.flatMap(toolHistoryItemsFromMemory);
|
|
4094
|
-
const
|
|
4467
|
+
const combinedToolHistory = (store.toolHistory[key] || []).concat(persistedToolHistory, toolHistoryFromRecentMessages, toolHistoryFromVectorMarkers, toolHistoryFromVectorMemories);
|
|
4468
|
+
const toolHistory = adv.includeToolHistory === false ? [] : applyToolHistoryWindow(combinedToolHistory, adv.toolHistoryTTLSeconds, adv.toolHistoryLastN || 15);
|
|
4469
|
+
loadDedupeSummary.load.toolHistory = cleanContextValue({
|
|
4470
|
+
input: adv.includeToolHistory === false ? 0 : combinedToolHistory.length,
|
|
4471
|
+
kept: toolHistory.length,
|
|
4472
|
+
removed: Math.max(0, (adv.includeToolHistory === false ? 0 : combinedToolHistory.length) - toolHistory.length),
|
|
4473
|
+
});
|
|
4095
4474
|
const highlights = adv.includeRecentHighlights === false ? [] : getRecentHighlights(recentMessages, toolHistory, adv.recentHighlightsMaxItems || 6);
|
|
4096
4475
|
const profileFacts = adv.includeProfileFacts === false ? {} : mergeProfileFacts(store.profileFacts[key], profileFactsFromMessages(allRecentMessages), profileFactsFromMemories(vectorMemories));
|
|
4097
4476
|
if (adv.includeProfileFacts !== false && Object.keys(profileFacts).length) {
|
|
@@ -4221,6 +4600,8 @@ class TemboryMemory {
|
|
|
4221
4600
|
},
|
|
4222
4601
|
connectedAi,
|
|
4223
4602
|
activeSummary: summaryDiagnostics,
|
|
4603
|
+
dedupeSummary: cleanContextValue(loadDedupeSummary),
|
|
4604
|
+
currentTurn,
|
|
4224
4605
|
};
|
|
4225
4606
|
const contextHealth = deriveContextHealth({
|
|
4226
4607
|
userId: key,
|
|
@@ -4292,6 +4673,7 @@ class TemboryMemory {
|
|
|
4292
4673
|
userId: key,
|
|
4293
4674
|
project: project || undefined,
|
|
4294
4675
|
query,
|
|
4676
|
+
currentTurn,
|
|
4295
4677
|
retrievalMode,
|
|
4296
4678
|
requestedRetrievalMode: requestedRetrievalMode === retrievalMode ? undefined : requestedRetrievalMode,
|
|
4297
4679
|
payloadFormat,
|
|
@@ -4360,6 +4742,7 @@ class TemboryMemory {
|
|
|
4360
4742
|
})),
|
|
4361
4743
|
},
|
|
4362
4744
|
diagnostics,
|
|
4745
|
+
dedupeSummary: diagnostics.dedupeSummary,
|
|
4363
4746
|
};
|
|
4364
4747
|
return {
|
|
4365
4748
|
response: {
|
|
@@ -4476,6 +4859,7 @@ exports.__private = {
|
|
|
4476
4859
|
flattenAdvancedGroups,
|
|
4477
4860
|
asSearchQuery,
|
|
4478
4861
|
currentInputJsonFromContext,
|
|
4862
|
+
resolveCurrentTurnQueryWithSource,
|
|
4479
4863
|
resolveCurrentTurnQuery,
|
|
4480
4864
|
safePersistLegacyMemory,
|
|
4481
4865
|
stripThreadTestPrefix,
|
|
@@ -4493,6 +4877,10 @@ exports.__private = {
|
|
|
4493
4877
|
deriveOperationalState,
|
|
4494
4878
|
deriveActionLedger,
|
|
4495
4879
|
deriveEntityTimeline,
|
|
4880
|
+
buildMemoryDedupeCandidate,
|
|
4881
|
+
dedupeMemoryItemsForLoad,
|
|
4882
|
+
mergeMemoryDedupeState,
|
|
4883
|
+
evaluateMemoryDedupeWrite,
|
|
4496
4884
|
scoreOf,
|
|
4497
4885
|
scoreMetaOf,
|
|
4498
4886
|
extractProfileFactsFromText,
|
package/package.json
CHANGED