n8n-nodes-tembory 1.1.38 → 1.1.40
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.
- package/README.md +13 -1
- package/dist/nodes/Tembory/TemboryMemory.node.js +220 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Node de memoria operacional da Tembory para agentes de IA no n8n.
|
|
4
4
|
|
|
5
|
-
Versao atual: `1.1.
|
|
5
|
+
Versao atual: `1.1.40`.
|
|
6
|
+
|
|
7
|
+
## 1.1.40
|
|
8
|
+
|
|
9
|
+
- Extrai fatos operacionais path aware de inputs e outputs de tools, preservando IDs como `serviceId`, `providerId`, `locationId`, `customerId`, `reservationId` e `bookingId`.
|
|
10
|
+
- Injeta esses fatos em `operationalState`, `actionLedger`, `toolLedger` e `action_directive`.
|
|
11
|
+
- Corrige o risco de o Agent usar `serviceId` antigo de memoria vetorial quando o `check_availabilities` mais recente ja retornou o ID correto para o `Booking`.
|
|
12
|
+
|
|
13
|
+
## 1.1.39
|
|
14
|
+
|
|
15
|
+
- Ajusta a semantica de repeticao de tools: `do_not_repeat_tools` fica restrito a turnos de recall, status operacional ou pedido explicito para nao chamar tools.
|
|
16
|
+
- Em turnos normais, o Agent recebe `repeat_tool_policy` e `avoid_repeating_tools_unless_needed`, deixando claro que tools anteriores sao evidencia reutilizavel, nao ferramentas proibidas.
|
|
17
|
+
- Compacta `decisionState.tool_decision_state` para evitar duplicar outputs longos de tools no contexto balanceado.
|
|
6
18
|
|
|
7
19
|
## 1.1.38
|
|
8
20
|
|
|
@@ -489,6 +489,7 @@ const deriveOperationalState = (toolHistory = [], profileFacts = {}, recentMessa
|
|
|
489
489
|
const name = String(tool.name || 'unknown_tool');
|
|
490
490
|
const compactInput = compactToolPayload(safeParseToolPayload(tool.input));
|
|
491
491
|
const compactResult = maybeToolResult(tool, includeResults);
|
|
492
|
+
const facts = extractToolOperationalFacts(tool);
|
|
492
493
|
const reason = truncate(String(tool.reason || tool.decision || tool.why || tool.source || 'recorded tool call'), 260);
|
|
493
494
|
toolCountsByName[name] = (toolCountsByName[name] || 0) + 1;
|
|
494
495
|
latestByName[name] = {
|
|
@@ -496,6 +497,7 @@ const deriveOperationalState = (toolHistory = [], profileFacts = {}, recentMessa
|
|
|
496
497
|
ok: tool.ok !== false,
|
|
497
498
|
at: tool.at || null,
|
|
498
499
|
input: compactInput,
|
|
500
|
+
facts,
|
|
499
501
|
result: compactResult,
|
|
500
502
|
reason,
|
|
501
503
|
source: tool.source || 'unknown',
|
|
@@ -507,6 +509,7 @@ const deriveOperationalState = (toolHistory = [], profileFacts = {}, recentMessa
|
|
|
507
509
|
at: tool.at || null,
|
|
508
510
|
reason,
|
|
509
511
|
input: compactInput,
|
|
512
|
+
facts,
|
|
510
513
|
output: compactResult,
|
|
511
514
|
}));
|
|
512
515
|
if (tool.ok === false)
|
|
@@ -538,6 +541,7 @@ const deriveOperationalState = (toolHistory = [], profileFacts = {}, recentMessa
|
|
|
538
541
|
last_successful_tool: successfulTools.length ? {
|
|
539
542
|
name: successfulTools[successfulTools.length - 1].name,
|
|
540
543
|
at: successfulTools[successfulTools.length - 1].at || null,
|
|
544
|
+
facts: extractToolOperationalFacts(successfulTools[successfulTools.length - 1]),
|
|
541
545
|
result: maybeToolResult(successfulTools[successfulTools.length - 1], includeResults),
|
|
542
546
|
} : null,
|
|
543
547
|
},
|
|
@@ -556,6 +560,7 @@ const deriveActionLedger = (toolHistory = [], maxItems = 20, includeResults = tr
|
|
|
556
560
|
status: tool.ok === false ? 'failed' : 'ok',
|
|
557
561
|
reason: truncate(String(tool.reason || tool.decision || tool.why || tool.source || 'recorded tool call'), 260),
|
|
558
562
|
input: compactToolPayload(safeParseToolPayload(tool.input)),
|
|
563
|
+
facts: extractToolOperationalFacts(tool),
|
|
559
564
|
result: maybeToolResult(tool, includeResults),
|
|
560
565
|
at: tool.at || null,
|
|
561
566
|
source: tool.source || 'unknown',
|
|
@@ -2046,6 +2051,8 @@ const buildTurnBriefForAgent = ({ query = '', recentMessages = [], toolHistory =
|
|
|
2046
2051
|
status: lastTool.status || (lastTool.ok === false ? 'failed' : 'ok'),
|
|
2047
2052
|
at: lastTool.at || lastTool.timestamp,
|
|
2048
2053
|
} : undefined,
|
|
2054
|
+
repeat_tool_policy: (decisionState || {}).repeat_tool_policy,
|
|
2055
|
+
avoid_repeating_tools_unless_needed: ((decisionState || {}).avoid_repeating_tools_unless_needed || []).slice(0, 12),
|
|
2049
2056
|
do_not_repeat_tools: ((decisionState || {}).do_not_repeat_tools || []).slice(0, 12),
|
|
2050
2057
|
guidance: turnBriefGuidanceForIntent(currentIntent),
|
|
2051
2058
|
});
|
|
@@ -2092,16 +2099,25 @@ const buildActionDirective = ({ workingMemory = {}, operationalState = {} }) =>
|
|
|
2092
2099
|
return null;
|
|
2093
2100
|
const toolState = (operationalState || {}).tool_state || {};
|
|
2094
2101
|
const latestForTool = ((toolState.latest_by_name || {})[tool]) || null;
|
|
2102
|
+
const recentToolEvidence = pruneByLimit((toolState.recent_sequence || [])
|
|
2103
|
+
.filter((item) => item && item.status !== 'failed')
|
|
2104
|
+
.map((item) => cleanContextValue({
|
|
2105
|
+
tool: item.tool,
|
|
2106
|
+
at: item.at,
|
|
2107
|
+
facts: item.facts,
|
|
2108
|
+
}))
|
|
2109
|
+
.filter((item) => item && item.facts), 6);
|
|
2095
2110
|
return cleanContextValue({
|
|
2096
2111
|
required_tool: tool,
|
|
2097
2112
|
next_expected_action: next,
|
|
2113
|
+
evidence_from_recent_tools: recentToolEvidence,
|
|
2098
2114
|
tool_state: {
|
|
2099
2115
|
last_tool: (operationalState || {}).last_tool || null,
|
|
2100
2116
|
required_tool_last_result: latestForTool,
|
|
2101
2117
|
counts_by_name: toolState.counts_by_name || {},
|
|
2102
2118
|
failed_by_name: toolState.failed_by_name || {},
|
|
2103
2119
|
},
|
|
2104
|
-
instruction: `
|
|
2120
|
+
instruction: `call ${tool} now if the agent prompt requires it. Use tool evidence for exact IDs. Do not complete side effects from memory.`,
|
|
2105
2121
|
});
|
|
2106
2122
|
};
|
|
2107
2123
|
const inferToolGuard = ({ query, recentMessages, toolHistory, vectorMemories }) => {
|
|
@@ -2151,6 +2167,19 @@ const shouldCarryPreviousGoal = (intent = '', previousGoal = '') => {
|
|
|
2151
2167
|
return false;
|
|
2152
2168
|
return ['general_message', 'profile_update', 'unknown'].includes(intent);
|
|
2153
2169
|
};
|
|
2170
|
+
const toolNameSuggestsSideEffect = (name = '') => /(?:^|[_\-. ])(book|booking|reserv\w*|agend\w*|confirm\w*|cancel\w*|cri\w*|create|update|upsert|set|send|enviar|registr\w*|cadastr\w*|charge|cobran\w*|pagamento|ticket)(?:$|[_\-. ])/i.test(String(name || ''));
|
|
2171
|
+
const deriveToolRepeatPolicy = ({ intent = '', query = '', executedTools = [], toolState = {} }) => {
|
|
2172
|
+
const tools = Array.from(new Set(executedTools.filter(Boolean)));
|
|
2173
|
+
const strictMemoryOnlyTurn = intent === 'conversation_recall' || intent === 'operational_status_question' || hasNoToolRequested(query);
|
|
2174
|
+
const sideEffectToolCandidates = tools.filter(toolNameSuggestsSideEffect);
|
|
2175
|
+
return cleanContextValue({
|
|
2176
|
+
mode: strictMemoryOnlyTurn ? 'answer_from_memory_when_possible' : 'conditional_reuse',
|
|
2177
|
+
avoid_repeating_tools_unless_needed: tools,
|
|
2178
|
+
legacy_do_not_repeat_applies: strictMemoryOnlyTurn,
|
|
2179
|
+
side_effect_tool_candidates: sideEffectToolCandidates,
|
|
2180
|
+
instruction: 'Prior tools are evidence, not forbidden tools. Reuse outputs when sufficient; call tools for fresh data, new scope, or current side effects.',
|
|
2181
|
+
});
|
|
2182
|
+
};
|
|
2154
2183
|
const deriveWorkingMemory = ({ query = '', profileFacts = {}, recentMessages = [], toolHistory = [], operationalState = {}, previous = {} }) => {
|
|
2155
2184
|
const intent = inferUserIntent(query, recentMessages);
|
|
2156
2185
|
const chronological = sortConversationChronological(recentMessages || []);
|
|
@@ -2225,7 +2254,6 @@ const deriveWorkingMemory = ({ query = '', profileFacts = {}, recentMessages = [
|
|
|
2225
2254
|
};
|
|
2226
2255
|
const deriveDecisionState = ({ query = '', toolHistory = [], operationalState = {}, workingMemory = {} }) => {
|
|
2227
2256
|
const decisions = [];
|
|
2228
|
-
const doNotRepeatTools = [];
|
|
2229
2257
|
const intent = workingMemory.last_user_intent || inferUserIntent(query, []);
|
|
2230
2258
|
const now = nowIso();
|
|
2231
2259
|
const toolState = operationalState.tool_state || {};
|
|
@@ -2244,9 +2272,9 @@ const deriveDecisionState = ({ query = '', toolHistory = [], operationalState =
|
|
|
2244
2272
|
tool: extra.tool,
|
|
2245
2273
|
});
|
|
2246
2274
|
};
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2275
|
+
const executedTools = Array.from(new Set(Object.keys(toolState.counts_by_name || {})));
|
|
2276
|
+
const toolRepeatPolicy = deriveToolRepeatPolicy({ intent, query, executedTools, toolState });
|
|
2277
|
+
const doNotRepeatTools = toolRepeatPolicy.legacy_do_not_repeat_applies ? executedTools : [];
|
|
2250
2278
|
if (toolState.last_successful_tool)
|
|
2251
2279
|
pushDecision('last_successful_tool_recorded', `latest successful tool is ${toolState.last_successful_tool.name}`, 'tool_state records successful tool execution', 'tool_orchestration', { confidence: 0.9, tool: toolState.last_successful_tool.name });
|
|
2252
2280
|
for (const [name, count] of Object.entries(toolState.failed_by_name || {}))
|
|
@@ -2254,15 +2282,18 @@ const deriveDecisionState = ({ query = '', toolHistory = [], operationalState =
|
|
|
2254
2282
|
return {
|
|
2255
2283
|
active_decisions: decisions,
|
|
2256
2284
|
do_not_repeat_tools: Array.from(new Set(doNotRepeatTools)),
|
|
2285
|
+
repeat_tool_policy: toolRepeatPolicy,
|
|
2286
|
+
avoid_repeating_tools_unless_needed: toolRepeatPolicy.avoid_repeating_tools_unless_needed,
|
|
2257
2287
|
current_intent: intent,
|
|
2258
2288
|
tool_decision_state: {
|
|
2259
|
-
executed_tools:
|
|
2289
|
+
executed_tools: executedTools,
|
|
2260
2290
|
failed_tools: Object.keys(toolState.failed_by_name || {}),
|
|
2261
2291
|
last_successful_tool: toolState.last_successful_tool || null,
|
|
2262
2292
|
do_not_repeat_tools: Array.from(new Set(doNotRepeatTools)),
|
|
2293
|
+
repeat_tool_policy: toolRepeatPolicy,
|
|
2263
2294
|
evaluated_at: now,
|
|
2264
2295
|
},
|
|
2265
|
-
conflict_policy: 'prefer active decisions
|
|
2296
|
+
conflict_policy: 'prefer newer active decisions; ignore superseded or expired memories unless auditing',
|
|
2266
2297
|
decision_count: decisions.length,
|
|
2267
2298
|
latest_tool: toolHistory && toolHistory.length ? {
|
|
2268
2299
|
name: toolHistory[toolHistory.length - 1].name,
|
|
@@ -2503,6 +2534,152 @@ const normalizeToolResultEnvelope = (value) => {
|
|
|
2503
2534
|
}
|
|
2504
2535
|
return parsed;
|
|
2505
2536
|
};
|
|
2537
|
+
const normalizeFactKey = (key = '') => String(key || '').toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
2538
|
+
const scalarFactValue = (value) => {
|
|
2539
|
+
if (value === undefined || value === null || value === '')
|
|
2540
|
+
return undefined;
|
|
2541
|
+
if (['string', 'number', 'boolean'].includes(typeof value))
|
|
2542
|
+
return value;
|
|
2543
|
+
return undefined;
|
|
2544
|
+
};
|
|
2545
|
+
const pushCandidate = (facts, bucket, value, path) => {
|
|
2546
|
+
const scalar = scalarFactValue(value);
|
|
2547
|
+
if (scalar === undefined)
|
|
2548
|
+
return;
|
|
2549
|
+
facts[bucket] = facts[bucket] || [];
|
|
2550
|
+
if (!facts[bucket].some((item) => String(item.value) === String(scalar) && item.path === path)) {
|
|
2551
|
+
facts[bucket].push({ value: scalar, path });
|
|
2552
|
+
}
|
|
2553
|
+
};
|
|
2554
|
+
const firstCandidateValue = (items = []) => items.length ? items[0].value : undefined;
|
|
2555
|
+
const extractOperationalFactsFromValue = (value, maxCandidates = 8) => {
|
|
2556
|
+
const parsed = normalizeToolResultEnvelope(value);
|
|
2557
|
+
const facts = {
|
|
2558
|
+
serviceIdCandidates: [],
|
|
2559
|
+
providerIdCandidates: [],
|
|
2560
|
+
locationIdCandidates: [],
|
|
2561
|
+
customerIdCandidates: [],
|
|
2562
|
+
reservationIdCandidates: [],
|
|
2563
|
+
bookingIdCandidates: [],
|
|
2564
|
+
confirmationIdCandidates: [],
|
|
2565
|
+
serviceNameCandidates: [],
|
|
2566
|
+
providerNameCandidates: [],
|
|
2567
|
+
statusCandidates: [],
|
|
2568
|
+
timeCandidates: [],
|
|
2569
|
+
dateCandidates: [],
|
|
2570
|
+
};
|
|
2571
|
+
const seen = new Set();
|
|
2572
|
+
let visited = 0;
|
|
2573
|
+
const visit = (item, path = []) => {
|
|
2574
|
+
if (visited > 220 || item === undefined || item === null)
|
|
2575
|
+
return;
|
|
2576
|
+
if (typeof item !== 'object')
|
|
2577
|
+
return;
|
|
2578
|
+
if (seen.has(item))
|
|
2579
|
+
return;
|
|
2580
|
+
seen.add(item);
|
|
2581
|
+
visited += 1;
|
|
2582
|
+
if (Array.isArray(item)) {
|
|
2583
|
+
item.slice(0, 12).forEach((entry, index) => visit(entry, path.concat(String(index))));
|
|
2584
|
+
return;
|
|
2585
|
+
}
|
|
2586
|
+
for (const [rawKey, rawValue] of Object.entries(item)) {
|
|
2587
|
+
const key = normalizeFactKey(rawKey);
|
|
2588
|
+
const nextPath = path.concat(rawKey);
|
|
2589
|
+
const pathText = nextPath.join('.');
|
|
2590
|
+
const parentText = path.join('.').toLowerCase();
|
|
2591
|
+
const parentSuggestsService = /(^|\.)(service|services|servico|servicos|especialidade|especialidades)(\.|$)/i.test(parentText);
|
|
2592
|
+
const parentSuggestsProvider = /(^|\.)(provider|providers|veterinario|veterinarios|vet|vets|professional|profissional|profissionais)(\.|$)/i.test(parentText);
|
|
2593
|
+
const parentSuggestsLocation = /(^|\.)(location|locations|local|locais|unidade|unidades)(\.|$)/i.test(parentText);
|
|
2594
|
+
if (key === 'serviceid' || key === 'servicoid' || (key === 'id' && parentSuggestsService))
|
|
2595
|
+
pushCandidate(facts, 'serviceIdCandidates', rawValue, pathText);
|
|
2596
|
+
if (key === 'providerid' || key === 'veterinarioid' || key === 'vetid' || (key === 'id' && parentSuggestsProvider))
|
|
2597
|
+
pushCandidate(facts, 'providerIdCandidates', rawValue, pathText);
|
|
2598
|
+
if (key === 'locationid' || key === 'localid' || key === 'unidadeid' || (key === 'id' && parentSuggestsLocation))
|
|
2599
|
+
pushCandidate(facts, 'locationIdCandidates', rawValue, pathText);
|
|
2600
|
+
if (key === 'customerid' || key === 'clienteid')
|
|
2601
|
+
pushCandidate(facts, 'customerIdCandidates', rawValue, pathText);
|
|
2602
|
+
if (key === 'reservationid' || key === 'reservaid')
|
|
2603
|
+
pushCandidate(facts, 'reservationIdCandidates', rawValue, pathText);
|
|
2604
|
+
if (key === 'bookingid' || key === 'appointmentid' || key === 'agendamentoid')
|
|
2605
|
+
pushCandidate(facts, 'bookingIdCandidates', rawValue, pathText);
|
|
2606
|
+
if (key === 'confirmationid')
|
|
2607
|
+
pushCandidate(facts, 'confirmationIdCandidates', rawValue, pathText);
|
|
2608
|
+
if (key === 'servicename' || key === 'servico' || key === 'especialidade' || (key === 'name' && parentSuggestsService))
|
|
2609
|
+
pushCandidate(facts, 'serviceNameCandidates', rawValue, pathText);
|
|
2610
|
+
if (key === 'providername' || key === 'veterinarioname' || key === 'nomeveterinario' || (key === 'name' && parentSuggestsProvider))
|
|
2611
|
+
pushCandidate(facts, 'providerNameCandidates', rawValue, pathText);
|
|
2612
|
+
if (key === 'status' || key === 'state')
|
|
2613
|
+
pushCandidate(facts, 'statusCandidates', rawValue, pathText);
|
|
2614
|
+
if (key === 'horario' || key === 'horarios' || key === 'time' || key === 'start' || key === 'end')
|
|
2615
|
+
pushCandidate(facts, 'timeCandidates', rawValue, pathText);
|
|
2616
|
+
if (key === 'date' || key === 'data' || key === 'datadisponivel')
|
|
2617
|
+
pushCandidate(facts, 'dateCandidates', rawValue, pathText);
|
|
2618
|
+
if (rawValue && typeof rawValue === 'object')
|
|
2619
|
+
visit(rawValue, nextPath);
|
|
2620
|
+
}
|
|
2621
|
+
};
|
|
2622
|
+
visit(parsed);
|
|
2623
|
+
const compactCandidates = (items = []) => items.slice(0, maxCandidates);
|
|
2624
|
+
return cleanContextValue({
|
|
2625
|
+
ids: {
|
|
2626
|
+
serviceId: firstCandidateValue(facts.serviceIdCandidates),
|
|
2627
|
+
providerId: firstCandidateValue(facts.providerIdCandidates),
|
|
2628
|
+
locationId: firstCandidateValue(facts.locationIdCandidates),
|
|
2629
|
+
customerId: firstCandidateValue(facts.customerIdCandidates),
|
|
2630
|
+
reservationId: firstCandidateValue(facts.reservationIdCandidates),
|
|
2631
|
+
bookingId: firstCandidateValue(facts.bookingIdCandidates),
|
|
2632
|
+
confirmationId: firstCandidateValue(facts.confirmationIdCandidates),
|
|
2633
|
+
},
|
|
2634
|
+
labels: {
|
|
2635
|
+
serviceName: firstCandidateValue(facts.serviceNameCandidates),
|
|
2636
|
+
providerName: firstCandidateValue(facts.providerNameCandidates),
|
|
2637
|
+
status: firstCandidateValue(facts.statusCandidates),
|
|
2638
|
+
date: firstCandidateValue(facts.dateCandidates),
|
|
2639
|
+
time: firstCandidateValue(facts.timeCandidates),
|
|
2640
|
+
},
|
|
2641
|
+
candidates: {
|
|
2642
|
+
serviceIds: compactCandidates(facts.serviceIdCandidates),
|
|
2643
|
+
providerIds: compactCandidates(facts.providerIdCandidates),
|
|
2644
|
+
locationIds: compactCandidates(facts.locationIdCandidates),
|
|
2645
|
+
},
|
|
2646
|
+
});
|
|
2647
|
+
};
|
|
2648
|
+
const mergeOperationalFactObjects = (primary = {}, secondary = {}) => {
|
|
2649
|
+
const pick = (path) => {
|
|
2650
|
+
const read = (source) => path.reduce((acc, key) => acc && acc[key] !== undefined ? acc[key] : undefined, source);
|
|
2651
|
+
return read(primary) !== undefined ? read(primary) : read(secondary);
|
|
2652
|
+
};
|
|
2653
|
+
return cleanContextValue({
|
|
2654
|
+
ids: {
|
|
2655
|
+
serviceId: pick(['ids', 'serviceId']),
|
|
2656
|
+
providerId: pick(['ids', 'providerId']),
|
|
2657
|
+
locationId: pick(['ids', 'locationId']),
|
|
2658
|
+
customerId: pick(['ids', 'customerId']),
|
|
2659
|
+
reservationId: pick(['ids', 'reservationId']),
|
|
2660
|
+
bookingId: pick(['ids', 'bookingId']),
|
|
2661
|
+
confirmationId: pick(['ids', 'confirmationId']),
|
|
2662
|
+
},
|
|
2663
|
+
labels: {
|
|
2664
|
+
serviceName: pick(['labels', 'serviceName']),
|
|
2665
|
+
providerName: pick(['labels', 'providerName']),
|
|
2666
|
+
status: pick(['labels', 'status']),
|
|
2667
|
+
date: pick(['labels', 'date']),
|
|
2668
|
+
time: pick(['labels', 'time']),
|
|
2669
|
+
},
|
|
2670
|
+
candidates: {
|
|
2671
|
+
serviceIds: ((primary.candidates || {}).serviceIds || (secondary.candidates || {}).serviceIds || []).slice(0, 8),
|
|
2672
|
+
providerIds: ((primary.candidates || {}).providerIds || (secondary.candidates || {}).providerIds || []).slice(0, 8),
|
|
2673
|
+
locationIds: ((primary.candidates || {}).locationIds || (secondary.candidates || {}).locationIds || []).slice(0, 8),
|
|
2674
|
+
},
|
|
2675
|
+
});
|
|
2676
|
+
};
|
|
2677
|
+
const extractToolOperationalFacts = (tool = {}) => {
|
|
2678
|
+
const inputFacts = extractOperationalFactsFromValue(safeParseToolPayload(tool.input));
|
|
2679
|
+
const outputFacts = extractOperationalFactsFromValue(tool.result);
|
|
2680
|
+
const facts = mergeOperationalFactObjects(outputFacts, inputFacts);
|
|
2681
|
+
return Object.keys(facts || {}).length ? facts : undefined;
|
|
2682
|
+
};
|
|
2506
2683
|
const compactParsedToolOutputForSideChannel = (parsed) => {
|
|
2507
2684
|
if (parsed === undefined || parsed === null)
|
|
2508
2685
|
return undefined;
|
|
@@ -2542,6 +2719,7 @@ const compactToolHistoryForAgent = (toolHistory = [], maxItems = 6, includeResul
|
|
|
2542
2719
|
at: tool.at,
|
|
2543
2720
|
reason: truncate(String(tool.reason || tool.decision || tool.why || tool.source || 'recorded tool call'), 140),
|
|
2544
2721
|
input: truncate(String(tool.input || ''), 500) || undefined,
|
|
2722
|
+
facts: extractToolOperationalFacts(tool),
|
|
2545
2723
|
result: includeResults ? compactToolResult(tool.result, 1200) : undefined,
|
|
2546
2724
|
}));
|
|
2547
2725
|
const cleanContextValue = (value) => {
|
|
@@ -2580,6 +2758,26 @@ const compactWorkingMemoryForAgent = (memory = {}) => cleanContextValue({
|
|
|
2580
2758
|
next_expected_action: memory.next_expected_action,
|
|
2581
2759
|
updated_at: memory.updated_at,
|
|
2582
2760
|
});
|
|
2761
|
+
const compactToolDecisionStateForAgent = (toolDecisionState = {}) => {
|
|
2762
|
+
const lastTool = toolDecisionState.last_successful_tool || null;
|
|
2763
|
+
return cleanContextValue({
|
|
2764
|
+
executed_tools: (toolDecisionState.executed_tools || []).slice(0, 12),
|
|
2765
|
+
failed_tools: (toolDecisionState.failed_tools || []).slice(0, 12),
|
|
2766
|
+
last_successful_tool: lastTool ? cleanContextValue({
|
|
2767
|
+
name: lastTool.name || lastTool.tool_name || lastTool.tool,
|
|
2768
|
+
status: lastTool.status || (lastTool.ok === false ? 'failed' : 'ok'),
|
|
2769
|
+
at: lastTool.at || lastTool.timestamp,
|
|
2770
|
+
}) : undefined,
|
|
2771
|
+
do_not_repeat_tools: (toolDecisionState.do_not_repeat_tools || []).slice(0, 12),
|
|
2772
|
+
evaluated_at: toolDecisionState.evaluated_at,
|
|
2773
|
+
});
|
|
2774
|
+
};
|
|
2775
|
+
const compactRepeatToolPolicyForAgent = (policy = {}) => cleanContextValue({
|
|
2776
|
+
mode: policy.mode,
|
|
2777
|
+
legacy_do_not_repeat_applies: policy.legacy_do_not_repeat_applies,
|
|
2778
|
+
side_effect_tool_candidates: (policy.side_effect_tool_candidates || []).slice(0, 12),
|
|
2779
|
+
instruction: policy.instruction,
|
|
2780
|
+
});
|
|
2583
2781
|
const compactDecisionStateForAgent = (state = {}) => cleanContextValue({
|
|
2584
2782
|
current_intent: state.current_intent,
|
|
2585
2783
|
active_decisions: (state.active_decisions || []).slice(-4).map((decision) => cleanContextValue({
|
|
@@ -2592,18 +2790,19 @@ const compactDecisionStateForAgent = (state = {}) => cleanContextValue({
|
|
|
2592
2790
|
at: decision.at || decision.updated_at,
|
|
2593
2791
|
})),
|
|
2594
2792
|
do_not_repeat_tools: state.do_not_repeat_tools,
|
|
2595
|
-
|
|
2793
|
+
repeat_tool_policy: state.repeat_tool_policy ? compactRepeatToolPolicyForAgent(state.repeat_tool_policy) : undefined,
|
|
2794
|
+
avoid_repeating_tools_unless_needed: state.avoid_repeating_tools_unless_needed,
|
|
2795
|
+
tool_decision_state: state.tool_decision_state ? compactToolDecisionStateForAgent(state.tool_decision_state) : undefined,
|
|
2596
2796
|
latest_tool: state.latest_tool ? cleanContextValue({
|
|
2597
2797
|
name: state.latest_tool.name,
|
|
2598
2798
|
status: state.latest_tool.status || (state.latest_tool.ok === false ? 'failed' : 'ok'),
|
|
2599
2799
|
at: state.latest_tool.at,
|
|
2600
2800
|
}) : undefined,
|
|
2601
|
-
conflict_policy: state.conflict_policy,
|
|
2602
2801
|
});
|
|
2603
2802
|
const compactOperationalStateForAgent = (state = {}) => {
|
|
2604
2803
|
const counts = state.tool_counts || {};
|
|
2605
2804
|
const toolState = state.tool_state || {};
|
|
2606
|
-
return {
|
|
2805
|
+
return cleanContextValue({
|
|
2607
2806
|
last_tool: state.last_tool || null,
|
|
2608
2807
|
tool_counts: {
|
|
2609
2808
|
total: counts.total || 0,
|
|
@@ -2617,8 +2816,7 @@ const compactOperationalStateForAgent = (state = {}) => {
|
|
|
2617
2816
|
last_successful_tool: toolState.last_successful_tool || undefined,
|
|
2618
2817
|
} : undefined,
|
|
2619
2818
|
blocked_without_context: state.blocked_without_context || [],
|
|
2620
|
-
|
|
2621
|
-
};
|
|
2819
|
+
});
|
|
2622
2820
|
};
|
|
2623
2821
|
const compactMemoryCompressionForAgent = (compression = {}) => ({
|
|
2624
2822
|
turn_summary: (compression.turn_summary || []).slice(-3),
|
|
@@ -3138,6 +3336,12 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
|
|
|
3138
3336
|
decisionCount: parsed.decisionState?.decision_count,
|
|
3139
3337
|
latestTool: parsed.decisionState?.latest_tool,
|
|
3140
3338
|
doNotRepeatTools: parsed.decisionState?.do_not_repeat_tools,
|
|
3339
|
+
avoidRepeatingToolsUnlessNeeded: parsed.decisionState?.avoid_repeating_tools_unless_needed,
|
|
3340
|
+
repeatToolPolicy: parsed.decisionState?.repeat_tool_policy ? {
|
|
3341
|
+
mode: parsed.decisionState.repeat_tool_policy.mode,
|
|
3342
|
+
legacyDoNotRepeatApplies: parsed.decisionState.repeat_tool_policy.legacy_do_not_repeat_applies,
|
|
3343
|
+
sideEffectToolCandidates: parsed.decisionState.repeat_tool_policy.side_effect_tool_candidates,
|
|
3344
|
+
} : undefined,
|
|
3141
3345
|
});
|
|
3142
3346
|
summary.quality = parsed.contextHealth?.quality_score || parsed.contextQualityScore || undefined;
|
|
3143
3347
|
summary.debug = includeDebug ? cleanContextValue({
|
|
@@ -3338,7 +3542,9 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
|
|
|
3338
3542
|
value: cleanContextValue({
|
|
3339
3543
|
current_intent: (decisionState || {}).current_intent || workingMemory.last_user_intent,
|
|
3340
3544
|
latest_tool: ((decisionState || {}).latest_tool || (operationalState || {}).last_tool || undefined),
|
|
3341
|
-
|
|
3545
|
+
repeat_tool_policy: (decisionState || {}).repeat_tool_policy,
|
|
3546
|
+
avoid_repeating_tools_unless_needed: ((decisionState || {}).avoid_repeating_tools_unless_needed || []).slice(0, 12),
|
|
3547
|
+
do_not_repeat_tools_legacy: ((decisionState || {}).do_not_repeat_tools || []).slice(0, 12),
|
|
3342
3548
|
instruction: actionDirective || workingMemory.next_expected_action || 'Continue according to the agent prompt.',
|
|
3343
3549
|
}),
|
|
3344
3550
|
});
|
|
@@ -3382,7 +3588,7 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
|
|
|
3382
3588
|
statusAnswerMaterial: sectionValue('status_answer_material'),
|
|
3383
3589
|
action_directive: directive ? cleanContextValue({
|
|
3384
3590
|
required_tool: directive.required_tool,
|
|
3385
|
-
|
|
3591
|
+
evidence_from_recent_tools: directive.evidence_from_recent_tools,
|
|
3386
3592
|
instruction: directive.instruction,
|
|
3387
3593
|
}) : undefined,
|
|
3388
3594
|
turnBrief: compactTurnBriefForAgent(sectionValue('turn_brief')),
|
package/package.json
CHANGED