terminal-smart-cli 0.97.30 → 0.97.31

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
@@ -1079,6 +1079,19 @@ async function pollRun(token, runId, sp) {
1079
1079
  }
1080
1080
  }
1081
1081
 
1082
+ // A run é o resumo; o motivo técnico da falha fica na etapa. Centralizar a
1083
+ // leitura evita que o usuário receba apenas "falhou" e tenha de investigar
1084
+ // logs ou a interface web para saber o que fazer em seguida.
1085
+ function runFailureMessage(run) {
1086
+ const failed = (run && Array.isArray(run.steps) ? run.steps : []).find(s => s && s.status === 'failed');
1087
+ const raw = String((failed && (failed.error_message || failed.result_summary)) || (run && run.error) || '').trim();
1088
+ const code = String((failed && failed.error_code) || '').trim();
1089
+ if (code === 'execution_unavailable' || /n[aã]o executo comandos diretamente|n[aã]o tenho acesso.*(windows|computador|m[aá]quina|sistema)|execute.*powershell/i.test(raw)) {
1090
+ return 'Esta orquestração roda na nuvem e não acessa arquivos do seu computador. Para criar, editar ou inspecionar algo local, use: ts agente "sua tarefa".';
1091
+ }
1092
+ return raw || 'Nenhuma ação foi concluída. Consulte as etapas da execução para ver o motivo.';
1093
+ }
1094
+
1082
1095
  // Fluxo completo de orquestração (usado pelo `ts run` e pelo /run do modo conversa).
1083
1096
  // askFn permite aprovar com o readline do REPL sem conflito de stdin; nunca chama process.exit.
1084
1097
  async function runFlow(goal, { askFn = ui.ask, json = false, yes = false } = {}) {
@@ -1112,7 +1125,7 @@ async function runFlow(goal, { askFn = ui.ask, json = false, yes = false } = {})
1112
1125
  const result = run.final_result || run.result || '';
1113
1126
  if (result) { console.log(''); console.log(ui.md(String(result)).split('\n').map(l => ' ' + l).join('\n')); console.log(''); }
1114
1127
  } else if (run.status === 'failed') {
1115
- console.error(ui.errLine(C.bold(T.run_failed) + (run.error ? C.dim(' · ' + String(run.error).slice(0, 140)) : '')));
1128
+ console.error(ui.errLine(C.bold(T.run_failed) + C.dim(' · ' + runFailureMessage(run).slice(0, 320))));
1116
1129
  } else {
1117
1130
  console.log(ui.infoLine(T.run_paused(runLbl(run.status))));
1118
1131
  }
package/lib/agent.js CHANGED
@@ -1259,7 +1259,13 @@ async function run(task, opts = {}) {
1259
1259
  async function _callRole(role, system, user, validate = null) {
1260
1260
  const phase = role === 'planner' ? 'planning' : (role === 'inspector' ? 'verification' : 'execution');
1261
1261
  const contract = intelligence.agentRoleContract(role, opts.accountPlan || 'free', { complex: _complexTask });
1262
- const candidates = (model ? [selectedModel] : [...contract.candidates]).slice(0, 1 + planUpgradeLimit(opts.accountPlan || 'free'));
1262
+ const baseCandidates = (model ? [selectedModel] : [...contract.candidates]).slice(0, 1 + planUpgradeLimit(opts.accountPlan || 'free'));
1263
+ // Plano vazio/JSON inválido é uma falha objetiva, mas não autoriza trocar o
1264
+ // planejador pelo executor. A segunda tentativa curta mantém a Luna como
1265
+ // dona do planejamento e atende inclusive ao Free sem upgrade de modelo.
1266
+ const candidates = role === 'planner' && baseCandidates.length === 1
1267
+ ? [baseCandidates[0], baseCandidates[0]]
1268
+ : baseCandidates;
1263
1269
  let last = null;
1264
1270
  let lastFailureReported = false;
1265
1271
  const phaseStartedAt = Date.now();
@@ -1296,8 +1302,12 @@ async function run(task, opts = {}) {
1296
1302
  onStepDone({ name: 'agente_' + role, ok: false, status: 'error', evidence: last.message });
1297
1303
  lastFailureReported = true;
1298
1304
  if (candidateIndex + 1 < candidates.length) {
1299
- _fallbackTrace.push({ role, from: roleModel, to: candidates[candidateIndex + 1], reason: last.message });
1300
- onStep({ name: 'fallback_modelo', detail: `${last.message}: ${roleModel} ${candidates[candidateIndex + 1]}`, retry: true });
1305
+ const next = candidates[candidateIndex + 1];
1306
+ if (next === roleModel) onStep({ name: 'nova_tentativa', detail: `${last.message}: nova tentativa curta com ${roleModel}`, retry: true });
1307
+ else {
1308
+ _fallbackTrace.push({ role, from: roleModel, to: next, reason: last.message });
1309
+ onStep({ name: 'fallback_modelo', detail: `${last.message}: ${roleModel} → ${next}`, retry: true });
1310
+ }
1301
1311
  }
1302
1312
  continue;
1303
1313
  }
@@ -1313,8 +1323,12 @@ async function run(task, opts = {}) {
1313
1323
  lastFailureReported = true;
1314
1324
  if (candidateIndex + 1 < candidates.length) {
1315
1325
  const reason = String(e && e.message || 'falha objetiva').slice(0, 100);
1316
- _fallbackTrace.push({ role, from: roleModel, to: candidates[candidateIndex + 1], reason });
1317
- onStep({ name: 'fallback_modelo', detail: `${reason}: ${roleModel} ${candidates[candidateIndex + 1]}`, retry: true });
1326
+ const next = candidates[candidateIndex + 1];
1327
+ if (next === roleModel) onStep({ name: 'nova_tentativa', detail: `${reason}: nova tentativa curta com ${roleModel}`, retry: true });
1328
+ else {
1329
+ _fallbackTrace.push({ role, from: roleModel, to: next, reason });
1330
+ onStep({ name: 'fallback_modelo', detail: `${reason}: ${roleModel} → ${next}`, retry: true });
1331
+ }
1318
1332
  }
1319
1333
  if (!isTransientModelError(e) && Number(e && e.status) !== 404 && !/model.*(?:not found|unavailable|unsupported)/i.test(String(e && e.message || ''))) break;
1320
1334
  }
@@ -8,7 +8,7 @@
8
8
 
9
9
  const crypto = require('crypto');
10
10
 
11
- const INTELLIGENCE_CONTRACT = 1;
11
+ const INTELLIGENCE_CONTRACT = 2;
12
12
  // Keep this machine-readable. Human/provider notes belong in commit history;
13
13
  // catalogFreshness appends an ISO time before parsing this value.
14
14
  const PRICE_REVISION = '2026-08-11';
@@ -179,9 +179,18 @@ const MODEL_ALIASES = Object.freeze({
179
179
  'claude-haiku': 'claude-haiku-4-5',
180
180
  });
181
181
 
182
+ // Equipe oficial do modo Automático: especialização e prova objetiva vêm antes
183
+ // de cascatas longas de modelos. Premium fica fora até validação real.
184
+ const OFFICIAL_AUTOMATIC_TEAM = Object.freeze({
185
+ planner: Object.freeze({ model: 'gpt-5.6-luna', label: 'Luna', responsibility: 'planejamento' }),
186
+ executor: Object.freeze({ model: 'deepseek-v4-pro', label: 'DeepSeek Pro', responsibility: 'execução' }),
187
+ corrector: Object.freeze({ model: 'deepseek-v4-pro', label: 'DeepSeek Pro', responsibility: 'correção' }),
188
+ multimodal: Object.freeze({ model: 'mimo-v2.5', fallback: 'gemini-2.5-flash', label: 'MiMo', responsibility: 'imagem, arquivo e documento visual' }),
189
+ verifier: Object.freeze({ model: null, label: 'Verificadores determinísticos', responsibility: 'prova objetiva de entrega' }),
190
+ });
191
+
182
192
  // Contrato dos agentes funcionais. Isto não é fine-tuning: cada papel recebe um
183
- // prompt curto, uma cadeia de modelos por plano e continua sujeito à prova das
184
- // ferramentas. O primeiro modelo permitido é o padrão; os seguintes são fallback.
193
+ // prompt curto e continua sujeito à prova das ferramentas.
185
194
  const AGENT_ROLES = Object.freeze({
186
195
  classifier: Object.freeze({
187
196
  prompt: 'Classifique a intenção e devolva somente a estrutura pedida. Não execute ferramentas nem invente dados.',
@@ -189,19 +198,19 @@ const AGENT_ROLES = Object.freeze({
189
198
  }),
190
199
  planner: Object.freeze({
191
200
  prompt: 'Decomponha o objetivo em etapas executáveis, dependências, riscos, critérios de aceite e provas. Não execute a tarefa.',
192
- models: Object.freeze({ free: ['deepseek-v4-flash'], basic: ['gpt-5.6-luna', 'deepseek-v4-pro'], pro: ['gpt-5.6-luna', 'deepseek-v4-pro', 'glm-5.2'] }),
201
+ models: Object.freeze({ free: ['deepseek-v4-flash'], basic: ['gpt-5.6-luna'], pro: ['gpt-5.6-luna'] }),
193
202
  }),
194
203
  executor: Object.freeze({
195
204
  prompt: 'Execute somente a etapa recebida com as ferramentas autorizadas. Não declare sucesso sem resultado verificável.',
196
- models: Object.freeze({ free: ['deepseek-v4-flash'], basic: ['deepseek-v4-pro', 'gpt-5.6-luna'], pro: ['deepseek-v4-pro', 'gpt-5.6-luna'] }),
205
+ models: Object.freeze({ free: ['deepseek-v4-flash'], basic: ['deepseek-v4-pro'], pro: ['deepseek-v4-pro'] }),
197
206
  }),
198
207
  inspector: Object.freeze({
199
208
  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.',
200
- models: Object.freeze({ free: ['gemini-2.5-flash-lite'], basic: ['gpt-5.6-luna', 'gemini-2.5-flash'], pro: ['gpt-5.6-luna', 'gemini-2.5-flash'] }),
209
+ models: Object.freeze({ free: ['gemini-2.5-flash-lite'], basic: ['gpt-5.6-luna', 'glm-5.2'], pro: ['gpt-5.6-luna', 'glm-5.2'] }),
201
210
  }),
202
211
  corrector: Object.freeze({
203
212
  prompt: 'Corrija apenas falhas confirmadas pelo inspetor, preserve o que funciona e repita as provas afetadas. Não amplie o escopo.',
204
- models: Object.freeze({ free: ['deepseek-v4-flash'], basic: ['deepseek-v4-pro', 'gpt-5.6-luna'], pro: ['deepseek-v4-pro', 'gpt-5.6-luna', 'glm-5.2'] }),
213
+ models: Object.freeze({ free: ['deepseek-v4-flash'], basic: ['deepseek-v4-pro'], pro: ['deepseek-v4-pro'] }),
205
214
  }),
206
215
  vision: Object.freeze({
207
216
  prompt: 'Extraia fatos observáveis da imagem e separe leitura, inferência e incerteza. Não invente texto ilegível.',
@@ -209,7 +218,7 @@ const AGENT_ROLES = Object.freeze({
209
218
  }),
210
219
  document: Object.freeze({
211
220
  prompt: 'Produza conteúdo estruturado e use o motor nativo do formato solicitado. Preserve editabilidade e valide o arquivo gerado.',
212
- models: Object.freeze({ free: ['deepseek-v4-flash'], basic: ['deepseek-v4-pro', 'mimo-v2.5', 'gemini-2.5-flash'], pro: ['mimo-v2.5', 'deepseek-v4-pro', 'gpt-5.6-luna'] }),
221
+ models: Object.freeze({ free: ['gemini-2.5-flash'], basic: ['mimo-v2.5', 'gemini-2.5-flash'], pro: ['mimo-v2.5', 'gemini-2.5-flash'] }),
213
222
  }),
214
223
  site_builder: Object.freeze({
215
224
  prompt: 'Crie ou edite o site completo, responsivo e acessível. Preserve o existente quando editar e valide a URL publicada.',
@@ -479,7 +488,7 @@ function agentRoleContract(role, plan, options) {
479
488
  // alteram o projeto. Isto vale inclusive no Free: o limite desse plano e de
480
489
  // tentativas/orcamento, nao uma degradacao silenciosa da qualidade do papel.
481
490
  const complexCore = options && options.complex === true && ['planner', 'executor', 'corrector'].includes(id)
482
- ? (id === 'planner' ? ['gpt-5.6-luna', 'deepseek-v4-pro'] : ['deepseek-v4-pro', 'gpt-5.6-luna'])
491
+ ? (id === 'planner' ? ['gpt-5.6-luna'] : ['deepseek-v4-pro'])
483
492
  : null;
484
493
  const requested = complexCore || spec.models[planId] || spec.models.free;
485
494
  const allowedRaw = complexCore && planId === 'free' ? null : (options && Array.isArray(options.allowedModels) ? options.allowedModels : null);
@@ -524,7 +533,7 @@ function routeModel(task, reliability) {
524
533
 
525
534
  module.exports = {
526
535
  INTELLIGENCE_CONTRACT, PRICE_REVISION, MODEL_CATALOG, MODEL_ALIASES, DEFAULT_PRICE,
527
- MEMORY_TYPES, MEMORY_TRUST, CONTEXT_SOURCES, AGENT_ROLES,
536
+ MEMORY_TYPES, MEMORY_TRUST, CONTEXT_SOURCES, OFFICIAL_AUTOMATIC_TEAM, AGENT_ROLES,
528
537
  normalizeModelId, modelInfo, legacyPriceMap, normalizeUsage, estimateCostUsd, catalogFreshness,
529
538
  createContextLedger, createMemoryRecord, scoreMemoryRecord, createHandoff, cleanOperationalText, buildOperationalSummary,
530
539
  normalizeAgentPlan, agentRoleContract, routeModel,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminal-smart-cli",
3
- "version": "0.97.30",
3
+ "version": "0.97.31",
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"