terminal-smart-cli 0.97.8 → 0.97.9
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/lib/agent.js +19 -1
- package/lib/mission-tool-cache.js +55 -0
- package/package.json +1 -1
package/lib/agent.js
CHANGED
|
@@ -13,6 +13,7 @@ const policy = require('./policy');
|
|
|
13
13
|
const checkpoint = require('./checkpoint');
|
|
14
14
|
const skillIndex = require('./skill-index');
|
|
15
15
|
const tools = require('./tools');
|
|
16
|
+
const { MissionToolCache, cacheReference } = require('./mission-tool-cache');
|
|
16
17
|
|
|
17
18
|
// SKILLS INSTALADAS (~/.ts/skills/<slug>/SKILL.md): lê nome+descrição do frontmatter pra
|
|
18
19
|
// oferecer ao agente. O agente LÊ o SKILL.md completo (com ler_arquivo) quando a skill é útil.
|
|
@@ -494,6 +495,7 @@ async function run(task, opts = {}) {
|
|
|
494
495
|
// ação (mesmo comando falhando, ciclo A/B/A/B). WARN = empurra a mudar de abordagem; 2º strike
|
|
495
496
|
// (ou já avisado e ainda em ciclo) = encerra o loop e cai no fechamento honesto garantido.
|
|
496
497
|
const _callCounts = new Map(); const _recentSigs = []; const _warnedSigs = new Set();
|
|
498
|
+
const _missionCache = new MissionToolCache();
|
|
497
499
|
let _loopWarned = false, loopedOut = false;
|
|
498
500
|
const LOOP_WARN = Number(process.env.TS_LOOP_WARN) > 0 ? Number(process.env.TS_LOOP_WARN) : 3;
|
|
499
501
|
const LOOP_BREAK = Number(process.env.TS_LOOP_BREAK) > 0 ? Number(process.env.TS_LOOP_BREAK) : 5;
|
|
@@ -675,6 +677,7 @@ async function run(task, opts = {}) {
|
|
|
675
677
|
const name = (tc.function && tc.function.name) || '';
|
|
676
678
|
let input = {}; try { input = JSON.parse((tc.function && tc.function.arguments) || '{}'); } catch (_) {}
|
|
677
679
|
let result;
|
|
680
|
+
let _cachedContent = '';
|
|
678
681
|
let _ran = false; // true só quando uma ferramenta REALMENTE executou (não gate/bloqueio) → status ✓/✗
|
|
679
682
|
|
|
680
683
|
guardStopped = _guard();
|
|
@@ -971,6 +974,15 @@ async function run(task, opts = {}) {
|
|
|
971
974
|
result = { resumo: sub.resumo };
|
|
972
975
|
steps++; _ran = true;
|
|
973
976
|
}
|
|
977
|
+
if (result === undefined && READONLY.has(name)) {
|
|
978
|
+
const _cached = _missionCache.get(name, input);
|
|
979
|
+
if (_cached) {
|
|
980
|
+
const _visible = messages.some(m => m && m.role === 'tool' && m.tool_call_id === _cached.toolCallId);
|
|
981
|
+
_cachedContent = cacheReference(_cached, name, _visible);
|
|
982
|
+
result = { cached: true, tool: name };
|
|
983
|
+
onStep({ name, detail: 'cache da missão: ' + argsShort(name, input) });
|
|
984
|
+
}
|
|
985
|
+
}
|
|
974
986
|
if (result === undefined) {
|
|
975
987
|
onStep({ name, detail: argsShort(name, input) });
|
|
976
988
|
result = await tools.execute(name, input, { confineDir, baseDir: cwd, token, allowedTools: _allowedToolNames });
|
|
@@ -1023,6 +1035,12 @@ async function run(task, opts = {}) {
|
|
|
1023
1035
|
if (result && (result._backup !== undefined || result._acao !== undefined)) {
|
|
1024
1036
|
result = Object.assign({}, result); delete result._backup; delete result._acao;
|
|
1025
1037
|
}
|
|
1038
|
+
const _cacheClass = core.classifyToolResult(result);
|
|
1039
|
+
if (_cacheClass.ok && READONLY.has(name)) {
|
|
1040
|
+
_missionCache.remember(name, input, { toolCallId: tc.id, content: JSON.stringify(result).slice(0, TOOL_RESULT_CAP) });
|
|
1041
|
+
} else if (_cacheClass.ok && !READONLY.has(name)) {
|
|
1042
|
+
_missionCache.invalidate();
|
|
1043
|
+
}
|
|
1026
1044
|
}
|
|
1027
1045
|
// AVISO SUAVE de convergência: a partir de 2/3 do teto de pesquisa, empurra o modelo a
|
|
1028
1046
|
// concluir (o limite DURO acima corta de vez; este só sinaliza antes, sem bloquear).
|
|
@@ -1042,7 +1060,7 @@ async function run(task, opts = {}) {
|
|
|
1042
1060
|
_logEp('human');
|
|
1043
1061
|
return { text: finalText, steps, credits: charged + _visionCredits, tokens: acc, model: usedModel, actions, cwd, context: lastCtx, needHuman: { motivo: result.motivo, o_que_fazer: result.o_que_fazer } };
|
|
1044
1062
|
}
|
|
1045
|
-
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(result).slice(0, TOOL_RESULT_CAP) });
|
|
1063
|
+
messages.push({ role: 'tool', tool_call_id: tc.id, content: _cachedContent || JSON.stringify(result).slice(0, TOOL_RESULT_CAP) });
|
|
1046
1064
|
if (guardStopped) break;
|
|
1047
1065
|
}
|
|
1048
1066
|
if (loopedOut || guardStopped) break; // watchdog/trava de orçamento cortou → fechamento determinístico
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function stable(value) {
|
|
4
|
+
if (Array.isArray(value)) return value.map(stable);
|
|
5
|
+
if (value && typeof value === 'object') {
|
|
6
|
+
const out = {};
|
|
7
|
+
for (const key of Object.keys(value).sort()) out[key] = stable(value[key]);
|
|
8
|
+
return out;
|
|
9
|
+
}
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function signature(tool, input) {
|
|
14
|
+
return String(tool || '') + '|' + JSON.stringify(stable(input || {}));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class MissionToolCache {
|
|
18
|
+
constructor({ maxEntries = 64 } = {}) {
|
|
19
|
+
this.maxEntries = Math.max(1, maxEntries);
|
|
20
|
+
this.entries = new Map();
|
|
21
|
+
this.hits = 0;
|
|
22
|
+
this.invalidations = 0;
|
|
23
|
+
}
|
|
24
|
+
get(tool, input) {
|
|
25
|
+
const key = signature(tool, input);
|
|
26
|
+
const entry = this.entries.get(key);
|
|
27
|
+
if (!entry) return null;
|
|
28
|
+
this.entries.delete(key); this.entries.set(key, entry);
|
|
29
|
+
this.hits++;
|
|
30
|
+
return entry;
|
|
31
|
+
}
|
|
32
|
+
remember(tool, input, entry) {
|
|
33
|
+
const key = signature(tool, input);
|
|
34
|
+
this.entries.delete(key);
|
|
35
|
+
this.entries.set(key, Object.assign({}, entry));
|
|
36
|
+
while (this.entries.size > this.maxEntries) this.entries.delete(this.entries.keys().next().value);
|
|
37
|
+
}
|
|
38
|
+
invalidate() {
|
|
39
|
+
if (this.entries.size) this.invalidations++;
|
|
40
|
+
this.entries.clear();
|
|
41
|
+
}
|
|
42
|
+
stats() { return { entries: this.entries.size, hits: this.hits, invalidations: this.invalidations }; }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function cacheReference(entry, tool, originalStillVisible) {
|
|
46
|
+
if (!originalStillVisible && entry && entry.content) return entry.content;
|
|
47
|
+
return JSON.stringify({
|
|
48
|
+
cached: true,
|
|
49
|
+
tool,
|
|
50
|
+
message: 'Resultado idêntico já fornecido nesta missão; a fonte não foi consultada novamente. Use o resultado anterior.',
|
|
51
|
+
originalToolCallId: entry && entry.toolCallId,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { MissionToolCache, signature, cacheReference };
|