terminal-smart-cli 0.97.66 → 0.97.67
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 +9 -2
- package/lib/core.js +4 -1
- package/lib/eval.js +3 -1
- package/lib/intelligence-core.js +39 -12
- package/lib/mission-tool-cache.js +12 -0
- package/lib/router.js +12 -2
- package/package.json +2 -2
package/lib/agent.js
CHANGED
|
@@ -89,11 +89,18 @@ function missionGuardDecision({ credits = 0, maxCredits = DEFAULT_MISSION_CREDIT
|
|
|
89
89
|
// primeiro e os fallbacks permanecem dentro do contrato do plano.
|
|
90
90
|
function planUpgradeLimit(plan) {
|
|
91
91
|
const id = intelligence.normalizeAgentPlan ? intelligence.normalizeAgentPlan(plan) : String(plan || 'free').toLowerCase();
|
|
92
|
-
|
|
92
|
+
// O Free passou de 0 para 1 degrau. Não é upgrade comercial: a contingência
|
|
93
|
+
// do Free é Luna (0.10/0.60), MAIS BARATA que o principal DeepSeek Pro
|
|
94
|
+
// (0.63/1.26). Com 0 degraus a contingência anunciada no plano ("DeepSeek Pro
|
|
95
|
+
// no chat, com contingência Luna") era cortada pelo slice e nunca existia.
|
|
96
|
+
return id === 'pro' ? 2 : 1;
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
function executorFallbackChain({ selectedModel, forced = false, source = 'cloud', plan = 'free', complex = false } = {}) {
|
|
96
|
-
|
|
100
|
+
// Um id aposentado pode chegar aqui de config antiga, de histórico ou do
|
|
101
|
+
// roteador de outra versão. Converter na entrada é o que impede a cadeia
|
|
102
|
+
// inteira de apontar para um modelo que o gateway não serve mais.
|
|
103
|
+
const first = intelligence.supportedModelId(String(selectedModel || DEFAULT_EXECUTOR).trim());
|
|
97
104
|
if (forced) return [first];
|
|
98
105
|
const role = intelligence.agentRoleContract('executor', plan, { complex });
|
|
99
106
|
// O roteador pode selecionar um executor mais específico que o contrato padrão.
|
package/lib/core.js
CHANGED
|
@@ -95,7 +95,10 @@ const MODEL_WINDOWS = {
|
|
|
95
95
|
'minimax': 200000, 'qwen': 64000, 'gemini': 500000, 'mimo': 128000,
|
|
96
96
|
};
|
|
97
97
|
const CTX_WINDOW_DEFAULT = 100000; // modelo desconhecido → conservador
|
|
98
|
-
|
|
98
|
+
// deepseek-v4-flash venceu o bake-off de código em 2026-08 e era o executor
|
|
99
|
+
// padrão, mas foi aposentado do gateway. O padrão passa a ser o executor do
|
|
100
|
+
// contrato oficial (AGENT_ROLES.executor), que é o mesmo nas três superfícies.
|
|
101
|
+
const DEFAULT_EXECUTOR = 'deepseek-v4-pro';
|
|
99
102
|
// limiar/rabo têm override por env (TS_COMPACT_AT / TS_COMPACT_TAIL) — usado nos testes.
|
|
100
103
|
const COMPACT_AT = Number(process.env.TS_COMPACT_AT) > 0 ? Number(process.env.TS_COMPACT_AT) : 0.65;
|
|
101
104
|
// piso de 6: rabo curto demais apaga a memória de trabalho e o agente relê arquivos em loop
|
package/lib/eval.js
CHANGED
|
@@ -24,7 +24,9 @@ const agent = require('./agent');
|
|
|
24
24
|
const intelligence = require('./intelligence-core');
|
|
25
25
|
|
|
26
26
|
// modelo default do JUIZ: barato e suficiente pra avaliar critério objetivo.
|
|
27
|
-
|
|
27
|
+
// Era deepseek-v4-flash, aposentado. Luna custa 0.10/0.60, tem `review` no
|
|
28
|
+
// catálogo e é mais barata que o Flash aposentado era.
|
|
29
|
+
const DEFAULT_JUDGE = 'gpt-5.6-luna';
|
|
28
30
|
|
|
29
31
|
// ── suíte ────────────────────────────────────────────────────────────────────
|
|
30
32
|
function loadSuite(p) {
|
package/lib/intelligence-core.js
CHANGED
|
@@ -171,14 +171,27 @@ const MODEL_CATALOG = Object.freeze({
|
|
|
171
171
|
});
|
|
172
172
|
|
|
173
173
|
const MODEL_ALIASES = Object.freeze({
|
|
174
|
-
deepseek: 'deepseek-v4-
|
|
175
|
-
'deepseek-v4': 'deepseek-v4-
|
|
174
|
+
deepseek: 'deepseek-v4-pro',
|
|
175
|
+
'deepseek-v4': 'deepseek-v4-pro',
|
|
176
176
|
glm: 'glm-4.6',
|
|
177
177
|
mimo: 'mimo-v2.5',
|
|
178
|
-
'gemini-lite': 'gemini-2.5-flash
|
|
178
|
+
'gemini-lite': 'gemini-2.5-flash',
|
|
179
179
|
'claude-haiku': 'claude-haiku-4-5',
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
+
// Modelos aposentados do gateway do produto. Eles PERMANECEM no catalogo porque
|
|
183
|
+
// historico de consumo antigo ainda precisa ser precificado com o preco que
|
|
184
|
+
// valia na epoca; o que eles nao podem mais e entrar em contrato ativo ou em
|
|
185
|
+
// roteamento. A conversao mora AQUI, em cada superficie, e nao na reescrita que
|
|
186
|
+
// o backend faz no gate do proxy: uma superficie que dependesse daquela correcao
|
|
187
|
+
// ficaria errada em offline, em BYOK e em qualquer caminho que nao passe pelo
|
|
188
|
+
// proxy — foi exatamente o caso do CLI, cuja cadeia de executor do plano Free
|
|
189
|
+
// apontava inteira para os dois ids abaixo.
|
|
190
|
+
const RETIRED_MODELS = Object.freeze({
|
|
191
|
+
'deepseek-v4-flash': 'deepseek-v4-pro',
|
|
192
|
+
'gemini-2.5-flash-lite': 'gemini-2.5-flash',
|
|
193
|
+
});
|
|
194
|
+
|
|
182
195
|
// Equipe oficial do modo Automático: especialização e prova objetiva vêm antes
|
|
183
196
|
// de cascatas longas de modelos. Premium fica fora até validação real.
|
|
184
197
|
const OFFICIAL_AUTOMATIC_TEAM = Object.freeze({
|
|
@@ -194,23 +207,23 @@ const OFFICIAL_AUTOMATIC_TEAM = Object.freeze({
|
|
|
194
207
|
const AGENT_ROLES = Object.freeze({
|
|
195
208
|
classifier: Object.freeze({
|
|
196
209
|
prompt: 'Classifique a intenção e devolva somente a estrutura pedida. Não execute ferramentas nem invente dados.',
|
|
197
|
-
models: Object.freeze({ free: ['
|
|
210
|
+
models: Object.freeze({ free: ['deepseek-v4-pro', 'gpt-5.6-luna'], basic: ['deepseek-v4-pro', 'gpt-4o-mini'], pro: ['deepseek-v4-pro', 'gpt-4o-mini'] }),
|
|
198
211
|
}),
|
|
199
212
|
planner: Object.freeze({
|
|
200
213
|
prompt: 'Decomponha o objetivo em etapas executáveis, dependências, riscos, critérios de aceite e provas. Não execute a tarefa.',
|
|
201
|
-
models: Object.freeze({ free: ['deepseek-v4-
|
|
214
|
+
models: Object.freeze({ free: ['deepseek-v4-pro', 'gpt-5.6-luna'], basic: ['gpt-5.6-luna', 'glm-5.2', 'deepseek-v4-pro'], pro: ['gpt-5.6-luna', 'glm-5.2', 'deepseek-v4-pro'] }),
|
|
202
215
|
}),
|
|
203
216
|
executor: Object.freeze({
|
|
204
217
|
prompt: 'Execute somente a etapa recebida com as ferramentas autorizadas. Não declare sucesso sem resultado verificável.',
|
|
205
|
-
models: Object.freeze({ free: ['deepseek-v4-
|
|
218
|
+
models: Object.freeze({ free: ['deepseek-v4-pro', 'gpt-5.6-luna'], basic: ['deepseek-v4-pro', 'gpt-5.6-luna', 'glm-5.2'], pro: ['deepseek-v4-pro', 'gpt-5.6-luna', 'glm-5.2'] }),
|
|
206
219
|
}),
|
|
207
220
|
inspector: Object.freeze({
|
|
208
221
|
prompt: 'Inspecione em modo somente leitura e compare evidências com os critérios de aceite. Não altere nada e não elogie por cortesia.',
|
|
209
|
-
models: Object.freeze({ free: ['
|
|
222
|
+
models: Object.freeze({ free: ['deepseek-v4-pro', 'gpt-5.6-luna'], basic: ['gpt-5.6-luna', 'glm-5.2'], pro: ['gpt-5.6-luna', 'glm-5.2'] }),
|
|
210
223
|
}),
|
|
211
224
|
corrector: Object.freeze({
|
|
212
225
|
prompt: 'Corrija apenas falhas confirmadas pelo inspetor, preserve o que funciona e repita as provas afetadas. Não amplie o escopo.',
|
|
213
|
-
models: Object.freeze({ free: ['deepseek-v4-
|
|
226
|
+
models: Object.freeze({ free: ['deepseek-v4-pro', 'gpt-5.6-luna'], basic: ['deepseek-v4-pro', 'gpt-5.6-luna', 'glm-5.2'], pro: ['deepseek-v4-pro', 'gpt-5.6-luna', 'glm-5.2'] }),
|
|
214
227
|
}),
|
|
215
228
|
vision: Object.freeze({
|
|
216
229
|
prompt: 'Extraia fatos observáveis da imagem e separe leitura, inferência e incerteza. Não invente texto ilegível.',
|
|
@@ -222,11 +235,11 @@ const AGENT_ROLES = Object.freeze({
|
|
|
222
235
|
}),
|
|
223
236
|
site_builder: Object.freeze({
|
|
224
237
|
prompt: 'Crie ou edite o site completo, responsivo e acessível. Preserve o existente quando editar e valide a URL publicada.',
|
|
225
|
-
models: Object.freeze({ free: ['deepseek-v4-
|
|
238
|
+
models: Object.freeze({ free: ['deepseek-v4-pro', 'gpt-5.6-luna'], basic: ['deepseek-v4-pro', 'gpt-5.6-luna'], pro: ['deepseek-v4-pro', 'gpt-5.6-luna'] }),
|
|
226
239
|
}),
|
|
227
240
|
cloud_box: Object.freeze({
|
|
228
241
|
prompt: 'Opere a box com mudanças mínimas, registre comandos e valide serviço, porta e URL. Nunca exponha segredos.',
|
|
229
|
-
models: Object.freeze({ free: ['deepseek-v4-
|
|
242
|
+
models: Object.freeze({ free: ['deepseek-v4-pro', 'gpt-5.6-luna'], basic: ['deepseek-v4-pro'], pro: ['deepseek-v4-pro', 'gpt-5.6-luna'] }),
|
|
230
243
|
}),
|
|
231
244
|
});
|
|
232
245
|
|
|
@@ -256,6 +269,14 @@ function normalizeModelId(model) {
|
|
|
256
269
|
return upstream ? upstream[0] : transportFree;
|
|
257
270
|
}
|
|
258
271
|
|
|
272
|
+
// Camada de compatibilidade: recebe qualquer id (alias, id do upstream, id
|
|
273
|
+
// aposentado) e devolve um id que o produto ainda serve. Use isto — e nao
|
|
274
|
+
// normalizeModelId — em qualquer ponto que va CHAMAR um modelo.
|
|
275
|
+
function supportedModelId(model) {
|
|
276
|
+
const id = normalizeModelId(model);
|
|
277
|
+
return RETIRED_MODELS[id] || id;
|
|
278
|
+
}
|
|
279
|
+
|
|
259
280
|
function modelInfo(model, now) {
|
|
260
281
|
const id = normalizeModelId(model);
|
|
261
282
|
const info = MODEL_CATALOG[id] || {
|
|
@@ -512,7 +533,12 @@ function routeModel(task, reliability) {
|
|
|
512
533
|
if (t.kind === 'code') required.add('code');
|
|
513
534
|
if (t.kind === 'review') required.add('review');
|
|
514
535
|
if (t.kind === 'vision') required.add('vision');
|
|
515
|
-
|
|
536
|
+
// Aposentado nao volta por roteamento. Sem este filtro o CLI devolvia
|
|
537
|
+
// deepseek-v4-flash como melhor candidato de codigo e dependia do backend
|
|
538
|
+
// reescrever depois — o que nao acontece em BYOK nem offline.
|
|
539
|
+
const candidates = (t.candidates || Object.keys(MODEL_CATALOG)).map(supportedModelId)
|
|
540
|
+
.filter(id => MODEL_CATALOG[id] && id !== 'smart')
|
|
541
|
+
.filter((id, i, list) => list.indexOf(id) === i);
|
|
516
542
|
const rel = reliability || {};
|
|
517
543
|
const plan = String(t.plan || '').toLowerCase();
|
|
518
544
|
const isPaid = plan && plan !== 'free' && plan !== 'gratuito';
|
|
@@ -527,12 +553,13 @@ function routeModel(task, reliability) {
|
|
|
527
553
|
const score = (observed * 0.35) + (m.quality * qualityWeight) + ((1 / (1 + price)) * costWeight) - (missing * 2);
|
|
528
554
|
return { id, score: +score.toFixed(6), missing };
|
|
529
555
|
}).sort((a, b) => b.score - a.score);
|
|
530
|
-
return { model: scored[0] ? scored[0].id :
|
|
556
|
+
return { model: scored[0] ? scored[0].id : 'deepseek-v4-pro', ranked: scored };
|
|
531
557
|
}
|
|
532
558
|
|
|
533
559
|
module.exports = {
|
|
534
560
|
INTELLIGENCE_CONTRACT, PRICE_REVISION, MODEL_CATALOG, MODEL_ALIASES, DEFAULT_PRICE,
|
|
535
561
|
MEMORY_TYPES, MEMORY_TRUST, CONTEXT_SOURCES, OFFICIAL_AUTOMATIC_TEAM, AGENT_ROLES,
|
|
562
|
+
RETIRED_MODELS, supportedModelId,
|
|
536
563
|
normalizeModelId, modelInfo, legacyPriceMap, normalizeUsage, estimateCostUsd, catalogFreshness,
|
|
537
564
|
createContextLedger, createMemoryRecord, scoreMemoryRecord, createHandoff, cleanOperationalText, buildOperationalSummary,
|
|
538
565
|
normalizeAgentPlan, agentRoleContract, routeModel,
|
|
@@ -44,6 +44,18 @@ class MissionToolCache {
|
|
|
44
44
|
stats() { return { entries: this.entries.size, hits: this.hits, invalidations: this.invalidations, charactersSaved: this.charactersSaved, estimatedTokensSaved: Math.ceil(this.charactersSaved / 4) }; }
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
// O terceiro parâmetro é IGNORADO de propósito, e continua na assinatura só para
|
|
48
|
+
// não quebrar os três pontos de chamada (cli/lib/agent.js, app-electron/main.js e
|
|
49
|
+
// backend/server/index.js), que ainda o calculam para a métrica de economia.
|
|
50
|
+
//
|
|
51
|
+
// Ele era `originalStillVisible`, e a decisão de encurtar dependia dele. O cálculo
|
|
52
|
+
// nas três superfícies verificava se existia uma mensagem com o mesmo
|
|
53
|
+
// `tool_call_id` — ou seja, se o ID estava visível, NÃO se o conteúdo ainda estava.
|
|
54
|
+
// Transports de assinatura pessoal reconstroem a conversa preservando o id e
|
|
55
|
+
// descartando o payload: o check aprovava, o modelo recebia "use o resultado
|
|
56
|
+
// anterior", não achava nada, relia o mesmo arquivo, recebia o mesmo ponteiro — e a
|
|
57
|
+
// missão só terminava quando o watchdog a matava. Não há como esta função saber se
|
|
58
|
+
// o payload sobreviveu, porque ela não vê a conversa; então ela não aposta.
|
|
47
59
|
function cacheReference(entry, tool, originalStillVisible) {
|
|
48
60
|
// Sempre devolva o conteúdo ao modelo. Alguns transports de assinatura pessoal
|
|
49
61
|
// reconstroem a conversa e preservam o id da tool call, mas não o payload antigo.
|
package/lib/router.js
CHANGED
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
const { api } = require('./api');
|
|
8
8
|
const keyring = require('./keyring');
|
|
9
9
|
|
|
10
|
+
// Modelo da camada 2 (classificacao de intencao). Era gemini-2.5-flash-lite,
|
|
11
|
+
// aposentado do gateway: a chamada falhava e o catch devolvia "chat" para tudo,
|
|
12
|
+
// o que fazia pedido de acao virar conversa. gemini-2.5-flash e o substituto
|
|
13
|
+
// da mesma familia, tem `classification` no catalogo, e a chamada usa
|
|
14
|
+
// max_completion_tokens: 5 — o custo por classificacao segue irrelevante.
|
|
15
|
+
const INTENT_MODEL = 'gemini-2.5-flash';
|
|
16
|
+
|
|
10
17
|
// Pergunta/conversa: forma interrogativa ou pedido de explicação
|
|
11
18
|
const Q_RE = /^(como|por ?qu[eê]|o ?que|qual|quais|quando|onde|quem|ser[aá] que|devo|posso|vale a pena|existe|tem como|é melhor|what|how|why|which|when|where|explique|me explica|explica|o que significa|diferen[çc]a entre)\b|\?\s*$/i;
|
|
12
19
|
|
|
@@ -67,7 +74,10 @@ async function classify(msg, token) {
|
|
|
67
74
|
method: 'POST', signal: ctrl.signal,
|
|
68
75
|
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + k.key },
|
|
69
76
|
body: JSON.stringify({
|
|
70
|
-
|
|
77
|
+
// Era gemini-2.5-flash-lite, aposentado: o gateway devolvia erro ou o
|
|
78
|
+
// backend reescrevia, e a classificação de intenção caía silenciosamente
|
|
79
|
+
// no catch que devolve "chat" para tudo.
|
|
80
|
+
model: keyring.modeloPara(k, INTENT_MODEL), stream: false, max_completion_tokens: 5,
|
|
71
81
|
messages: [
|
|
72
82
|
{ role: 'system', content: 'Classifique a intenção da mensagem de um usuário de um CLI DevOps. Responda APENAS uma palavra: "chat" (pergunta, conversa, pedido de explicação), "agente" (quer que algo seja FEITO nesta máquina: comandos, arquivos, diagnóstico, instalação) ou "orquestrar" (objetivo GRANDE com vários entregáveis para uma equipe de agentes).' },
|
|
73
83
|
{ role: 'user', content: String(msg).slice(0, 500) },
|
|
@@ -79,7 +89,7 @@ async function classify(msg, token) {
|
|
|
79
89
|
const word = String(j?.choices?.[0]?.message?.content || '').toLowerCase();
|
|
80
90
|
// cobra a classificação (mesmo padrão do web-intent) — best-effort
|
|
81
91
|
const u = j?.usage || {};
|
|
82
|
-
if (u.prompt_tokens && !k.billingAuthoritative) api('/api/credit/charge', { method: 'POST', token, body: { model:
|
|
92
|
+
if (u.prompt_tokens && !k.billingAuthoritative) api('/api/credit/charge', { method: 'POST', token, body: { model: INTENT_MODEL, inTok: u.prompt_tokens || 0, outTok: u.completion_tokens || 0 } }).catch(() => {});
|
|
83
93
|
if (word.includes('agente')) return 'agente';
|
|
84
94
|
if (word.includes('orquestrar')) return 'run';
|
|
85
95
|
return 'chat';
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "terminal-smart-cli",
|
|
3
|
-
"version": "0.97.
|
|
3
|
+
"version": "0.97.67",
|
|
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"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "node
|
|
9
|
+
"test": "node scripts/run-tests.js"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"bin",
|