terminal-smart-cli 0.50.1 → 0.51.0

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/bin/ts.js CHANGED
@@ -1361,6 +1361,8 @@ async function diagnosticarCmd(args) {
1361
1361
  else if (e.type === 'probe') console.log(' ' + C.dim('$ ') + e.probe.slice(0, 90));
1362
1362
  else if (e.type === 'blocked') console.log(' ' + C.err(en ? 'probe blocked (not read-only)' : 'sonda bloqueada (não é só-leitura)'));
1363
1363
  else if (e.type === 'skip') console.log(' ' + C.dim(en ? 'repeated probe skipped' : 'sonda repetida pulada'));
1364
+ else if (e.type === 'verify') console.log(' ' + C.dim('↯ ') + (en ? 'adversarial check (trying to refute it)…' : 'checagem adversarial (tentando refutar)…'));
1365
+ else if (e.type === 'refuted') console.log(' ' + C.err(en ? '✗ own conclusion refuted — back to investigating' : '✗ própria conclusão refutada — volta a investigar'));
1364
1366
  else if (e.type === 'stuck') console.log(' ' + C.dim(en ? 'exhausted hypotheses' : 'hipóteses esgotadas'));
1365
1367
  },
1366
1368
  });
@@ -1369,7 +1371,7 @@ async function diagnosticarCmd(args) {
1369
1371
  if (r.status === 'no_credits') { console.log(ui.errLine(T.no_credits)); return; }
1370
1372
  if (r.status === 'solved') {
1371
1373
  console.log(ui.box([
1372
- C.ok(en ? 'ROOT CAUSE FOUND' : 'CAUSA RAIZ ENCONTRADA') + C.dim(' (' + r.category + ', ' + r.rounds + (en ? ' rounds' : ' rodadas') + ', ' + r.credits + ' cr)'),
1374
+ C.ok(en ? 'ROOT CAUSE FOUND' : 'CAUSA RAIZ ENCONTRADA') + (r.verified ? C.ok(en ? ' ✓verified' : ' ✓verificada') : C.dim(en ? ' (unverified)' : ' (não verificada)')) + C.dim(' (' + r.category + ', ' + r.rounds + (en ? ' rounds' : ' rodadas') + ', ' + r.credits + ' cr)'),
1373
1375
  '',
1374
1376
  C.bold(en ? 'Cause: ' : 'Causa: ') + r.rootCause,
1375
1377
  '',
package/lib/diagnose.js CHANGED
@@ -65,8 +65,34 @@ async function diagnose(symptom, opts = {}) {
65
65
  onEvent({ type: 'think', round, hypothesis: j.hypothesis || '', category: j.category || '', thought: j.thought || '', confidence: j.confidence || 0 });
66
66
 
67
67
  if (j.status === 'solved' && (j.root_cause || j.fix)) {
68
- result = { status: 'solved', rootCause: j.root_cause || j.hypothesis || '', fix: j.fix || '', category: j.category || 'unknown', confidence: j.confidence || 0, evidence, rounds: round };
69
- onEvent({ type: 'solved', rootCause: result.rootCause, fix: result.fix, category: result.category });
68
+ // ── VERIFICAÇÃO ADVERSARIAL: antes de cravar, TENTA REFUTAR a própria conclusão. ──
69
+ // (nasceu de um falso-positivo real: "libtask.so quebrada" quando os símbolos
70
+ // indefinidos eram resolvidos pelo executável gs em runtime — ldd -r engana.)
71
+ const claim = j.root_cause || j.hypothesis || '';
72
+ onEvent({ type: 'verify', round, rootCause: claim });
73
+ const vAsk = lang === 'en'
74
+ ? 'Before finalizing, ADVERSARIALLY refute this. Give ONE read-only probe whose output would DISPROVE the root cause if it is wrong. Classic trap: a symbol/lib/file that looks "missing/broken" but is provided elsewhere (e.g. an undefined symbol in a .so is resolved by the MAIN executable at runtime → check the main binary with nm -C). JSON {"refute_probe":"...","disproves_if":"..."}'
75
+ : 'Antes de finalizar, REFUTE adversarialmente. Dê UMA sonda só-leitura cuja saída DESMENTIRIA a causa raiz se ela estiver errada. Armadilha clássica: um símbolo/lib/arquivo que parece "faltando/quebrado" mas é provido em OUTRO lugar (ex.: símbolo indefinido numa .so é resolvido pelo EXECUTÁVEL principal em runtime → cheque o binário principal com nm -C). JSON {"refute_probe":"...","disproves_if":"..."}';
76
+ let vr = null; try { vr = await agent.llm({ baseUrl: k.baseUrl, key: k.key, model, noTools: true, signalMs: 90000, messages: [{ role: 'system', content: sysPrompt(lang, target) }, { role: 'user', content: (lang === 'en' ? 'SYMPTOM:\n' : 'SINTOMA:\n') + symptom + '\n\n' + (lang === 'en' ? 'EVIDENCE:\n' : 'EVIDÊNCIAS:\n') + evLog + '\n\n' + (lang === 'en' ? 'PROPOSED ROOT CAUSE: ' : 'CAUSA RAIZ PROPOSTA: ') + claim + '\n\n' + vAsk }] }); } catch (_) {}
77
+ if (vr) { inTok += (vr.usage.prompt_tokens || 0); outTok += (vr.usage.completion_tokens || 0); }
78
+ const vj = vr ? extractJson(vr.msg.content) : null;
79
+ const rprobe = vj && String(vj.refute_probe || '').trim();
80
+ if (rprobe && isProbe(rprobe) && !seenProbes.has(rprobe)) {
81
+ seenProbes.add(rprobe);
82
+ onEvent({ type: 'probe', round, hypothesis: '↯ verificação: ' + (j.hypothesis || ''), probe: rprobe, expect: vj.disproves_if || '' });
83
+ let rout; try { const ex = await tools.execute('executar_comando', { comando: wrap(rprobe) }, { baseDir: cwd, token }); rout = ex.erro ? ('ERRO: ' + ex.erro) : ([ex.stdout, ex.stderr].filter(Boolean).join('\n') || ('(sem saída, exit ' + (ex.codigo ?? '?') + ')')); } catch (e) { rout = 'FALHA: ' + (e.message || e); }
84
+ evidence.push({ round, hypothesis: '↯ verificação: ' + (j.hypothesis || ''), probe: rprobe, output: rout });
85
+ let fr = null; try { fr = await agent.llm({ baseUrl: k.baseUrl, key: k.key, model, noTools: true, signalMs: 60000, messages: [{ role: 'system', content: sysPrompt(lang, target) }, { role: 'user', content: (lang === 'en' ? 'Root cause claim: ' : 'Causa raiz alegada: ') + claim + '\n' + (lang === 'en' ? 'Refutation probe output:\n' : 'Saída da sonda de refutação:\n') + String(rout).slice(0, 1600) + '\n\n' + (lang === 'en' ? 'CONFIRMED or REFUTED? JSON {"verdict":"confirmed|refuted","reason":"..."}' : 'CONFIRMADA ou REFUTADA? JSON {"verdict":"confirmed|refuted","reason":"..."}') }] }); } catch (_) {}
86
+ if (fr) { inTok += (fr.usage.prompt_tokens || 0); outTok += (fr.usage.completion_tokens || 0); }
87
+ const fj = fr ? extractJson(fr.msg.content) : null;
88
+ if (fj && /refut/i.test(String(fj.verdict))) {
89
+ onEvent({ type: 'refuted', round, reason: fj.reason || '' });
90
+ evidence.push({ round, hypothesis: '✗ REFUTADA: ' + (j.hypothesis || ''), probe: '(veredito)', output: 'Causa REFUTADA — ' + (fj.reason || '') + ' — seguir investigando.' });
91
+ continue; // a própria conclusão caiu na verificação → volta a investigar
92
+ }
93
+ }
94
+ result = { status: 'solved', rootCause: claim, fix: j.fix || '', category: j.category || 'unknown', confidence: j.confidence || 0, verified: !!rprobe, evidence, rounds: round };
95
+ onEvent({ type: 'solved', rootCause: result.rootCause, fix: result.fix, category: result.category, verified: result.verified });
70
96
  break;
71
97
  }
72
98
  if (j.status === 'stuck' || !j.probe) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminal-smart-cli",
3
- "version": "0.50.1",
3
+ "version": "0.51.0",
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"