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 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminal-smart-cli",
3
- "version": "0.97.8",
3
+ "version": "0.97.9",
4
4
  "description": "Terminal Smart no seu terminal — pergunte, analise logs por pipe e orquestre agentes de IA. Comando: ts",
5
5
  "bin": {
6
6
  "ts": "bin/ts.js"