terminal-smart-cli 0.97.28 → 0.97.30
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 +59 -5
- package/lib/acp.js +40 -18
- package/lib/agent.js +508 -73
- package/lib/core.js +11 -4
- package/lib/eval.js +2 -2
- package/lib/evolution-proposal.js +47 -0
- package/lib/file-lock.js +42 -0
- package/lib/gateways.js +69 -0
- package/lib/intelligence-core.js +102 -29
- package/lib/meta.js +28 -2
- package/lib/owner-audit.js +112 -0
- package/lib/providers.js +4 -1
- package/lib/recovery.js +12 -0
- package/lib/tools.js +145 -25
- package/lib/ui.js +10 -0
- package/package.json +2 -2
package/bin/ts.js
CHANGED
|
@@ -1085,7 +1085,9 @@ async function runFlow(goal, { askFn = ui.ask, json = false, yes = false } = {})
|
|
|
1085
1085
|
const token = needToken();
|
|
1086
1086
|
const sp0 = ui.spinner(runLbl('planning')).start();
|
|
1087
1087
|
let r;
|
|
1088
|
-
|
|
1088
|
+
// `ts run` é a execução autônoma do CLI: começa no modo Confiável para ter
|
|
1089
|
+
// planejamento e uma recuperação objetiva. O chat simples continua Rápido.
|
|
1090
|
+
try { r = await api('/api/ia/orchestrate', { method: 'POST', token, body: { goal, mode: 'reliable' }, timeoutMs: 120000 }); }
|
|
1089
1091
|
catch (e) { sp0.stop(); throw e; }
|
|
1090
1092
|
sp0.stop();
|
|
1091
1093
|
let run = r.run;
|
|
@@ -1433,7 +1435,7 @@ async function agentCmd(words, { askFn, cwd: cwdIn = null, onCwd = null, maxIter
|
|
|
1433
1435
|
// HEADLESS stream-json: fecha com assistant (texto) + result (métricas) e sai.
|
|
1434
1436
|
if (streamJson) {
|
|
1435
1437
|
_emit(core.AgentEvents.assistant(out.text || ''));
|
|
1436
|
-
_emit(core.AgentEvents.result({ steps: out.steps, credits: out.credits, tokens: out.tokens, context: out.context || null, needHuman: out.needHuman || null,
|
|
1438
|
+
_emit(core.AgentEvents.result({ steps: out.steps, credits: out.credits, tokens: out.tokens, context: out.context || null, needHuman: out.needHuman || null, guard: out.guard || null, observability: out.observability || null, duration_ms: Date.now() - t0, worktree: _wt ? { branch: _wt.branch, changed: (_wtInfo && _wtInfo.changed) || 0 } : null }));
|
|
1437
1439
|
return;
|
|
1438
1440
|
}
|
|
1439
1441
|
if (JSON_OUT) { console.log(JSON.stringify({ ok: true, result: out.text, steps: out.steps, credits: out.credits, tokens: out.tokens, context: out.context, needHuman: out.needHuman, guard: out.guard || null })); return; }
|
|
@@ -1531,7 +1533,7 @@ async function acpCmd() {
|
|
|
1531
1533
|
// token pode faltar — o servidor responde o handshake mesmo assim e só recusa no prompt
|
|
1532
1534
|
// (mensagem "rode ts login"). NÃO escreve NADA em stdout aqui (corromperia o JSON-RPC).
|
|
1533
1535
|
const { acpServer } = require('../lib/acp');
|
|
1534
|
-
acpServer({ token: cfg.token || null, lang: cfg.lang || 'pt' });
|
|
1536
|
+
acpServer({ token: cfg.token || null, lang: cfg.lang || 'pt', plan: process.env.TS_ACCOUNT_PLAN || cfg.plan || 'free' });
|
|
1535
1537
|
// fica vivo lendo stdin até o editor fechar (rl 'close' → process.exit).
|
|
1536
1538
|
return new Promise(() => {});
|
|
1537
1539
|
}
|
|
@@ -1546,6 +1548,16 @@ async function evalCmd(words) {
|
|
|
1546
1548
|
const _val = (names) => { const i = process.argv.findIndex(a => names.includes(a)); return i >= 0 ? (process.argv[i + 1] || null) : null; };
|
|
1547
1549
|
const model = _val(['--modelo', '--model']); // executor do agente (bake-off)
|
|
1548
1550
|
const judgeModel = _val(['--juiz', '--judge']) || undefined; // modelo do juiz (default barato)
|
|
1551
|
+
// Evals precisam de orçamento próprio: o laboratório não pode ficar preso
|
|
1552
|
+
// no teto padrão de uma missão só porque um provedor demorou a encerrar.
|
|
1553
|
+
const _evalSteps = Number(_val(['--passos', '--steps']));
|
|
1554
|
+
const _evalSeconds = Number(_val(['--tempo-max-seg', '--max-seconds']));
|
|
1555
|
+
const _evalCredits = Number(_val(['--orcamento-creditos', '--max-creditos']));
|
|
1556
|
+
const evalLimits = {
|
|
1557
|
+
maxIter: Number.isFinite(_evalSteps) && _evalSteps > 0 ? Math.min(60, Math.floor(_evalSteps)) : undefined,
|
|
1558
|
+
maxDurationMs: Number.isFinite(_evalSeconds) && _evalSeconds > 0 ? Math.min(3600, _evalSeconds) * 1000 : undefined,
|
|
1559
|
+
maxCredits: Number.isFinite(_evalCredits) && _evalCredits > 0 ? Math.floor(_evalCredits) : undefined,
|
|
1560
|
+
};
|
|
1549
1561
|
const smoke = FLAGS.has('--smoke');
|
|
1550
1562
|
// caminho da suíte = positional que não seja valor de --modelo/--juiz. Prefere um que
|
|
1551
1563
|
// termine em .json ou exista em disco (robusto quando o valor de --modelo bate com o nome).
|
|
@@ -1585,7 +1597,7 @@ async function evalCmd(words) {
|
|
|
1585
1597
|
let mx;
|
|
1586
1598
|
try {
|
|
1587
1599
|
mx = await evalMod.runMatrix(suite, {
|
|
1588
|
-
token, lang: cfg.lang || 'pt', judgeModel, models,
|
|
1600
|
+
token, lang: cfg.lang || 'pt', judgeModel, models, ...evalLimits,
|
|
1589
1601
|
onModel: ({ model, phase, row }) => { if (JSON_OUT) return; if (phase === 'start') spM.text((en ? 'testing ' : 'testando ') + model + '…'); else if (phase === 'done') { spM.stop(); console.log(' ' + C.cyan('◆') + ' ' + C.bold(model) + C.dim(' ' + row.passed + '/' + row.total + ' · score ' + row.avgScore + ' · ' + row.credits + ' cr') + (row.error ? C.err(' ' + row.error) : '')); spM.start(); } },
|
|
1590
1602
|
onCase: ({ phase, id, pass, score }) => { if (!JSON_OUT && phase === 'done') { spM.stop(); console.log(' ' + (pass ? C.ok('✔') : C.err('✗')) + C.dim(' ' + id + ' ' + score)); spM.start(); } },
|
|
1591
1603
|
});
|
|
@@ -1611,7 +1623,7 @@ async function evalCmd(words) {
|
|
|
1611
1623
|
let res;
|
|
1612
1624
|
try {
|
|
1613
1625
|
res = await evalMod.runSuite(suite, {
|
|
1614
|
-
token, lang: cfg.lang || 'pt', model, judgeModel,
|
|
1626
|
+
token, lang: cfg.lang || 'pt', model, judgeModel, ...evalLimits,
|
|
1615
1627
|
onCase: ({ i, total, id, phase, pass, score }) => {
|
|
1616
1628
|
if (JSON_OUT) return;
|
|
1617
1629
|
if (phase === 'done') {
|
|
@@ -1733,6 +1745,47 @@ async function vpsCmd(words) {
|
|
|
1733
1745
|
} catch (e) { sp && sp.stop && sp.stop(); console.log('\n ' + C.err('✗') + ' ' + e.message + '\n'); }
|
|
1734
1746
|
}
|
|
1735
1747
|
|
|
1748
|
+
// ── ts gateway: aliases OpenSSH com operação forçada ────────────────────────
|
|
1749
|
+
function gatewayCmd(words) {
|
|
1750
|
+
const gateways = require('../lib/gateways');
|
|
1751
|
+
const sub = String(words[0] || 'listar').toLowerCase();
|
|
1752
|
+
const flag = (names) => { const i = rawArgs.findIndex(a => names.includes(a)); return i >= 0 ? rawArgs[i + 1] : null; };
|
|
1753
|
+
// PowerShell separa valores com vírgula em múltiplos argumentos quando eles
|
|
1754
|
+
// não estão entre aspas. Aceitamos as duas formas: status,logs e "status,logs".
|
|
1755
|
+
const flagList = (names) => {
|
|
1756
|
+
const i = rawArgs.findIndex(a => names.includes(a));
|
|
1757
|
+
if (i < 0) return null;
|
|
1758
|
+
const values = [];
|
|
1759
|
+
for (let p = i + 1; p < rawArgs.length && !String(rawArgs[p]).startsWith('-'); p++) values.push(rawArgs[p]);
|
|
1760
|
+
return values.join(',');
|
|
1761
|
+
};
|
|
1762
|
+
if (['listar', 'list', 'ls', 'ver', 'status'].includes(sub)) {
|
|
1763
|
+
const rows = gateways.list();
|
|
1764
|
+
if (!rows.length) { console.log(ui.infoLine('Nenhum gateway configurado. Exemplo: ts gateway adicionar producao --alias meu-gateway --operacoes status,logs,deploy')); return; }
|
|
1765
|
+
console.log('');
|
|
1766
|
+
for (const item of rows) console.log(` ${C.cyan(item.name)} ${C.bold(item.label)}${C.dim(' · alias ' + item.sshAlias + ' · ' + item.operations.join(', '))}`);
|
|
1767
|
+
console.log('');
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
if (['adicionar', 'add', 'set', 'configurar', 'config'].includes(sub)) {
|
|
1771
|
+
const name = words[1];
|
|
1772
|
+
const alias = flag(['--alias', '--ssh-alias']);
|
|
1773
|
+
const operations = flagList(['--operacoes', '--operations']) || 'status';
|
|
1774
|
+
const label = flag(['--nome', '--label']) || name;
|
|
1775
|
+
try {
|
|
1776
|
+
const item = gateways.save(name, { label, sshAlias: alias, operations });
|
|
1777
|
+
console.log(ui.okLine(`Gateway salvo: ${item.label} · alias ${item.sshAlias} · ${item.operations.join(', ')}`));
|
|
1778
|
+
} catch (e) { console.log(ui.errLine(e.message)); }
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
if (['remover', 'remove', 'rm', 'apagar'].includes(sub)) {
|
|
1782
|
+
const removed = gateways.remove(words[1]);
|
|
1783
|
+
console.log(removed ? ui.okLine('Gateway removido.') : ui.infoLine('Gateway não encontrado.'));
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
console.log(ui.infoLine('Uso: ts gateway listar | adicionar <nome> --alias <alias-openssh> --operacoes status,logs,deploy | remover <nome>'));
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1736
1789
|
// ── ts meta: MISSÃO longa (checklist + rodadas até terminar — modo noturno) ──
|
|
1737
1790
|
function _metaChecklistBox(itens) {
|
|
1738
1791
|
return ui.box(itens.map(it =>
|
|
@@ -3195,6 +3248,7 @@ function recallCmd(args) {
|
|
|
3195
3248
|
case 'memoria': case 'memória': case 'memory': return memoriaCmd(POS.slice(1));
|
|
3196
3249
|
case 'recall': case 'lembrei': case 'sessoes': case 'sessões': return recallCmd(POS.slice(1));
|
|
3197
3250
|
case 'vps': case 'servidor': return vpsCmd(POS.slice(1));
|
|
3251
|
+
case 'gateway': case 'gateways': return gatewayCmd(POS.slice(1));
|
|
3198
3252
|
case 'site': case 'sites': case 'pagina': return siteCmd(POS.slice(1));
|
|
3199
3253
|
case 'cloud': case 'nuvem': return cloudCmd(POS.slice(1));
|
|
3200
3254
|
case 'diagnosticar': case 'diagnose': case 'investigar': case 'debug': return diagnosticarCmd(POS.slice(1));
|
package/lib/acp.js
CHANGED
|
@@ -33,7 +33,7 @@ function _kindOf(name) {
|
|
|
33
33
|
return 'other';
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function acpServer({ token, lang = 'pt' }) {
|
|
36
|
+
function acpServer({ token, lang = 'pt', plan = 'free' }) {
|
|
37
37
|
const write = (obj) => { try { process.stdout.write(JSON.stringify(obj) + '\n'); } catch (_) {} };
|
|
38
38
|
const log = (...a) => { try { process.stderr.write('[ts acp] ' + a.join(' ') + '\n'); } catch (_) {} };
|
|
39
39
|
const notify = (method, params) => write({ jsonrpc: '2.0', method, params });
|
|
@@ -96,25 +96,42 @@ function acpServer({ token, lang = 'pt' }) {
|
|
|
96
96
|
sess.cancelled = false;
|
|
97
97
|
sess.running = true;
|
|
98
98
|
let out = null;
|
|
99
|
+
const openSteps = new Map();
|
|
100
|
+
const beginStep = ({ name, detail, blocked, phase, model, retry }) => {
|
|
101
|
+
const tcid = 'tc-' + (++seq);
|
|
102
|
+
const queue = openSteps.get(name) || [];
|
|
103
|
+
queue.push(tcid); openSteps.set(name, queue);
|
|
104
|
+
notify('session/update', { sessionId: sid, update: {
|
|
105
|
+
sessionUpdate: 'tool_call', toolCallId: tcid,
|
|
106
|
+
title: name, rawName: name, detail: detail || '', phase: phase || '', model: model || '',
|
|
107
|
+
kind: _kindOf(name), status: blocked ? 'failed' : 'in_progress',
|
|
108
|
+
} });
|
|
109
|
+
if (blocked || retry || ['compactar_contexto', 'gateway'].includes(String(name))) {
|
|
110
|
+
queue.shift(); if (queue.length) openSteps.set(name, queue); else openSteps.delete(name);
|
|
111
|
+
notify('session/update', { sessionId: sid, update: {
|
|
112
|
+
sessionUpdate: 'tool_call_update', toolCallId: tcid,
|
|
113
|
+
status: blocked ? 'failed' : 'completed', evidence: String(detail || '').slice(0, 800),
|
|
114
|
+
} });
|
|
115
|
+
}
|
|
116
|
+
return tcid;
|
|
117
|
+
};
|
|
118
|
+
const finishStep = ({ name, ok, status, evidence }) => {
|
|
119
|
+
const queue = openSteps.get(name) || [];
|
|
120
|
+
const tcid = queue.shift() || beginStep({ name, detail: '' });
|
|
121
|
+
if (queue.length) openSteps.set(name, queue); else openSteps.delete(name);
|
|
122
|
+
notify('session/update', { sessionId: sid, update: {
|
|
123
|
+
sessionUpdate: 'tool_call_update', toolCallId: tcid,
|
|
124
|
+
status: ok === false || status === 'error' ? 'failed' : 'completed',
|
|
125
|
+
evidence: String(evidence || '').slice(0, 800),
|
|
126
|
+
} });
|
|
127
|
+
};
|
|
99
128
|
try {
|
|
100
129
|
out = await agent.run(text, {
|
|
101
|
-
token, lang, yes: false, cwd: sess.cwd, confineDir: null,
|
|
130
|
+
token, lang, yes: false, cwd: sess.cwd, confineDir: null, accountPlan: plan,
|
|
102
131
|
shouldStop: () => sess.cancelled, // cancelamento cooperativo: para no próximo passo
|
|
103
|
-
onThinking: () => {},
|
|
104
|
-
onStep:
|
|
105
|
-
|
|
106
|
-
// tool_call (início) + tool_call_update (fim) — nosso onStep dispara já
|
|
107
|
-
// com o passo concluído, então mandamos os dois em sequência.
|
|
108
|
-
notify('session/update', { sessionId: sid, update: {
|
|
109
|
-
sessionUpdate: 'tool_call', toolCallId: tcid,
|
|
110
|
-
title: name + (detail ? ' · ' + detail : ''), kind: _kindOf(name),
|
|
111
|
-
status: blocked ? 'failed' : 'in_progress',
|
|
112
|
-
} });
|
|
113
|
-
notify('session/update', { sessionId: sid, update: {
|
|
114
|
-
sessionUpdate: 'tool_call_update', toolCallId: tcid,
|
|
115
|
-
status: blocked ? 'failed' : 'completed',
|
|
116
|
-
} });
|
|
117
|
-
},
|
|
132
|
+
onThinking: (progress) => notify('session/update', { sessionId: sid, update: Object.assign({ sessionUpdate: 'agent_progress' }, progress || {}) }),
|
|
133
|
+
onStep: beginStep,
|
|
134
|
+
onStepDone: finishStep,
|
|
118
135
|
askApprove: async (cmd) => {
|
|
119
136
|
if (sess.cancelled) return false;
|
|
120
137
|
// pede a decisão ao EDITOR (o usuário aprova/recusa na UI do IDE)
|
|
@@ -145,7 +162,12 @@ function acpServer({ token, lang = 'pt' }) {
|
|
|
145
162
|
if (sess.cancelled) return { stopReason: 'cancelled' };
|
|
146
163
|
// texto final do agente → chunk de mensagem
|
|
147
164
|
if (out && out.text) notify('session/update', { sessionId: sid, update: { sessionUpdate: 'agent_message_chunk', content: { type: 'text', text: out.text } } });
|
|
148
|
-
return {
|
|
165
|
+
return {
|
|
166
|
+
stopReason: 'end_turn',
|
|
167
|
+
usage: out ? { inTok: out.tokens && out.tokens.inTok || 0, outTok: out.tokens && out.tokens.outTok || 0, cacheTok: out.tokens && out.tokens.cachedTok || 0, credits: out.credits || 0, model: out.model || '' } : {},
|
|
168
|
+
report: out && out.report || null,
|
|
169
|
+
completion: out && out.completion || null,
|
|
170
|
+
};
|
|
149
171
|
},
|
|
150
172
|
// NOTIFICAÇÃO (sem id): marca cancelamento (o agente para no próximo passo via
|
|
151
173
|
// shouldStop) E destrava qualquer permissão em voo DESTA sessão (→ nega na hora),
|