primocode 11.1.0 → 11.3.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/README.md +16 -2
- package/bin/primocode-rpc.js +100 -0
- package/lib/agentes-pessoais.js +120 -0
- package/lib/app-server/cli.js +64 -0
- package/lib/app-server/enderecos.js +43 -0
- package/lib/app-server/index.js +3 -0
- package/lib/app-server/pareamento.js +61 -0
- package/lib/app-server/protocolo.js +86 -0
- package/lib/app-server/servidor.js +805 -0
- package/lib/app-server/websocket.js +87 -0
- package/lib/avisos.js +65 -0
- package/lib/cli/sessao.js +202 -17
- package/lib/cli-nova.js +237 -0
- package/lib/config/config.js +22 -2
- package/lib/diff.js +12 -2
- package/lib/motor/aprovacao.js +114 -0
- package/lib/motor/eventos.js +77 -0
- package/lib/motor/index.js +8 -0
- package/lib/motor/tela-terminal.js +64 -0
- package/lib/motor/transcricao.js +57 -0
- package/lib/motor/turno.js +43 -0
- package/lib/motor/voz.js +65 -0
- package/lib/piloto.js +39 -0
- package/lib/plugin/entregaveis/nucleo.js +2 -2
- package/lib/plugin/estudio/criar.js +113 -0
- package/lib/plugin/estudio/gosto.js +3 -0
- package/lib/plugin/estudio/historico.js +60 -0
- package/lib/plugin/estudio/index.js +14 -1
- package/lib/plugin/estudio/kits/tela.html +3 -1
- package/lib/plugin/estudio/miniatura.js +56 -0
- package/lib/plugin/video/auto.js +1 -1
- package/lib/plugin/video/catalogo.js +20 -0
- package/lib/plugin/video/mutar.js +28 -0
- package/lib/plugin/video/ops-fx.js +6 -6
- package/lib/plugin/video/ops.js +2 -13
- package/lib/plugin/video/pedido.js +1 -1
- package/lib/plugin/video/zoom.js +1 -1
- package/lib/projetos-meta.js +107 -0
- package/lib/qr.js +209 -0
- package/lib/session/runner/act.js +127 -68
- package/lib/skill/catalogo.js +3 -1
- package/lib/tool/tools.js +7 -0
- package/lib/tui/entrada.js +143 -18
- package/lib/tui/tela.js +312 -49
- package/lib/tui/ui.js +78 -4
- package/package.json +10 -2
- package/voz/primo_voz/transcrever.py +12 -0
|
@@ -40,6 +40,7 @@ const tempos = require('../../tempos');
|
|
|
40
40
|
const preLeitura = require('../../pre-leitura');
|
|
41
41
|
const travado = require('../../travado');
|
|
42
42
|
const auditoria = require('../../auditoria');
|
|
43
|
+
const eventosMotor = require('../../motor/eventos');
|
|
43
44
|
const chamadaTorta = require('../../chamada-torta');
|
|
44
45
|
const idioma = require('../../idioma');
|
|
45
46
|
const entregaveis = require('../../plugin/entregaveis');
|
|
@@ -103,10 +104,14 @@ function trabalhando() { return TRABALHANDO; }
|
|
|
103
104
|
/** O que ficou na caixa sem ser lido — o terminal roda como pedido novo. */
|
|
104
105
|
function sobrasDaCaixa() { return CAIXA.splice(0); }
|
|
105
106
|
/* Esvazia a caixa dentro da conversa. Devolve quantos recados entraram. */
|
|
106
|
-
function entregarRecados(messages, profundidade) {
|
|
107
|
+
function entregarRecados(messages, profundidade, emitir) {
|
|
107
108
|
if (!CAIXA.length) return 0;
|
|
108
109
|
const recados = CAIXA.splice(0);
|
|
109
|
-
for (const r of recados)
|
|
110
|
+
for (const r of recados) {
|
|
111
|
+
const texto = `recado recebido — entrando na tarefa: ${r}`;
|
|
112
|
+
if (emitir) emitir(eventosMotor.montar('pensamento', { profundidade, texto }));
|
|
113
|
+
else fala.pensamento(texto, profundidade);
|
|
114
|
+
}
|
|
110
115
|
messages.push({
|
|
111
116
|
role: 'user',
|
|
112
117
|
content: (recados.length === 1 ? 'MENSAGEM NOVA do usuário, enviada enquanto você trabalhava:\n'
|
|
@@ -241,6 +246,17 @@ function ident(profundidade) {
|
|
|
241
246
|
* duas versões do texto, que o `limparParaOModelo` apaga antes de a conversa
|
|
242
247
|
* seguir. Sem essa limpeza, cada edição mandaria o arquivo inteiro de volta
|
|
243
248
|
* ao modelo — que acabou de escrevê-lo e não aprende nada relendo. */
|
|
249
|
+
/* +N −M de uma escrita ou edição: o diff de verdade (linhas que entraram e saíram). Arquivo
|
|
250
|
+
novo conta todas as linhas como entradas. Sem as duas versões nem a contagem, null. */
|
|
251
|
+
function contarMudanca(result) {
|
|
252
|
+
if (!result) return null;
|
|
253
|
+
if (typeof result._antes === 'string' && typeof result._depois === 'string') {
|
|
254
|
+
try { return diff.contar(diff.operacoes(result._antes, result._depois)); } catch { return null; }
|
|
255
|
+
}
|
|
256
|
+
if (result.created && Number.isFinite(result.linhas)) return { mais: result.linhas, menos: 0 };
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
|
|
244
260
|
function mostrarDiff(result, profundidade) {
|
|
245
261
|
if (!result || typeof result._antes !== 'string' || typeof result._depois !== 'string') return;
|
|
246
262
|
const bloco = diff.desenhar(result._antes, result._depois, {
|
|
@@ -258,6 +274,14 @@ function limparParaOModelo(result) {
|
|
|
258
274
|
return limpo;
|
|
259
275
|
}
|
|
260
276
|
|
|
277
|
+
/* Os eventos do subagente, do diretor e do pipeline vão para o mesmo emissor
|
|
278
|
+
do pedido principal (vem no ctx). Sem ele, uma tela de terminal nova. */
|
|
279
|
+
function emitirDe(ctx) {
|
|
280
|
+
if (!ctx.emitir) ctx.emitir = require('../../motor/tela-terminal.js').criar();
|
|
281
|
+
return ctx.emitir;
|
|
282
|
+
}
|
|
283
|
+
const telaDe = (ctx, vista, dados) => emitirDe(ctx)(eventosMotor.montar(vista, { profundidade: ctx.profundidade, ...dados }));
|
|
284
|
+
|
|
261
285
|
function logResult(kind, result, profundidade) {
|
|
262
286
|
const p = fala.resultado(profundidade);
|
|
263
287
|
if (!result) return;
|
|
@@ -274,17 +298,24 @@ function logResult(kind, result, profundidade) {
|
|
|
274
298
|
}
|
|
275
299
|
|
|
276
300
|
switch (kind) {
|
|
277
|
-
case 'write_file':
|
|
278
|
-
|
|
301
|
+
case 'write_file': {
|
|
302
|
+
const k = contarMudanca(result);
|
|
303
|
+
if (k) require('../../tui/ui').registrarArquivo(result.path, k.mais, k.menos);
|
|
304
|
+
console.log(p + c.muted(`${result.created ? 'criado' : 'atualizado'} · ${tools.fmtBytes(result.bytes)}`)
|
|
305
|
+
+ (k ? c.muted(' · ') + c.ok(`+${k.mais}`) + ' ' + c.err(`−${k.menos}`) : ''));
|
|
279
306
|
// Reescrever arquivo que já existia apaga o que estava lá. O
|
|
280
307
|
// número de bytes não conta isso; o bloco embaixo conta.
|
|
281
308
|
mostrarDiff(result, profundidade);
|
|
282
309
|
return;
|
|
310
|
+
}
|
|
283
311
|
case 'edit_file':
|
|
284
312
|
case 'multi_edit': {
|
|
285
313
|
const delta = result.linhasDepois - result.linhasAntes;
|
|
286
314
|
const sinal = delta > 0 ? c.ok(`+${delta}`) : delta < 0 ? c.err(`${delta}`) : c.muted('±0');
|
|
287
|
-
|
|
315
|
+
const k = contarMudanca(result);
|
|
316
|
+
if (k) require('../../tui/ui').registrarArquivo(result.path, k.mais, k.menos);
|
|
317
|
+
console.log(p + c.muted(`${result.substituicoes} troca${result.substituicoes > 1 ? 's' : ''} · `) + sinal + c.muted(' linhas')
|
|
318
|
+
+ (k ? c.muted(' · ') + c.ok(`+${k.mais}`) + ' ' + c.err(`−${k.menos}`) : ''));
|
|
288
319
|
mostrarDiff(result, profundidade);
|
|
289
320
|
return;
|
|
290
321
|
}
|
|
@@ -329,6 +360,7 @@ function logResult(kind, result, profundidade) {
|
|
|
329
360
|
return;
|
|
330
361
|
}
|
|
331
362
|
case 'write_todos': {
|
|
363
|
+
require('../../tui/ui').estadoSessao.todos = result.todos || [];
|
|
332
364
|
const marca = { completed: c.ok('✔'), in_progress: c.brand('▶'), pending: c.dim('○') };
|
|
333
365
|
for (const t of result.todos || []) {
|
|
334
366
|
const texto = t.status === 'completed' ? c.dim(t.content) : c.white(t.content);
|
|
@@ -920,7 +952,28 @@ async function runActInterno(userPrompt, opts) {
|
|
|
920
952
|
: () => require('../../browser.js').aquecerRevisor(),
|
|
921
953
|
/* Quem é este agente no livro de ações (auditoria.js). */
|
|
922
954
|
rotulo = subagent ? 'subagente' : 'principal',
|
|
955
|
+
/* Para onde vão os eventos do turno (lib/motor/eventos.js). O motor
|
|
956
|
+
não escreve na tela: quem desenha assina. Sem ninguém de fora, nasce
|
|
957
|
+
uma tela de terminal (lib/motor/tela-terminal.js) — a saída de sempre. */
|
|
958
|
+
emitir: emitirDeFora = null,
|
|
923
959
|
} = opts || {};
|
|
960
|
+
const emitir = emitirDeFora || require('../../motor/tela-terminal.js').criar();
|
|
961
|
+
const ev = (vista, dados) => emitir(eventosMotor.montar(vista, { profundidade, ...dados }));
|
|
962
|
+
/* Spinner por evento: a tela decide se gira (terminal) ou se mostra "trabalhando" (app). */
|
|
963
|
+
const girar = (rotulo) => {
|
|
964
|
+
const id = eventosMotor.novoId();
|
|
965
|
+
ev('spinner-inicia', { id, rotulo });
|
|
966
|
+
return {
|
|
967
|
+
set detalhe(v) { ev('spinner-detalhe', { id, detalhe: v }); },
|
|
968
|
+
stop() { ev('spinner-para', { id }); },
|
|
969
|
+
};
|
|
970
|
+
};
|
|
971
|
+
/* O texto que corre na tela conforme chega: mesma interface do fala.fluxo(). */
|
|
972
|
+
const abrirFluxo = (texto) => {
|
|
973
|
+
const id = eventosMotor.novoId();
|
|
974
|
+
ev('fluxo-inicia', { id, texto });
|
|
975
|
+
return { escrever: (t) => ev('fluxo-escreve', { id, texto: t }), fechar: () => ev('fluxo-fecha', { id }) };
|
|
976
|
+
};
|
|
924
977
|
|
|
925
978
|
// A pasta de projeto só nasce quando o agente for mesmo MEXER EM ARQUIVO.
|
|
926
979
|
// Antes ela nascia na primeira tool qualquer — pedir "abre o Chrome" criava
|
|
@@ -968,7 +1021,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
968
1021
|
const t = Date.now();
|
|
969
1022
|
// Com spinner: eram segundos de tela parada entre o último arquivo e o
|
|
970
1023
|
// "pronto", e parecia que tinha travado ali.
|
|
971
|
-
const sp =
|
|
1024
|
+
const sp = girar('abrindo a página escondido para conferir');
|
|
972
1025
|
try {
|
|
973
1026
|
medida = await medirPagina(pastaAtual);
|
|
974
1027
|
} catch { medida = null; } finally { sp.stop(); }
|
|
@@ -977,7 +1030,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
977
1030
|
if (!medida || !medida.testou) return null;
|
|
978
1031
|
avisoDaPagina = medida.limpo ? ''
|
|
979
1032
|
: String(medida.relato || '').split('\n').filter((l) => /·|BRANCO/.test(l)).slice(0, 3).join(' ');
|
|
980
|
-
if (medida.limpo) {
|
|
1033
|
+
if (medida.limpo) { ev('pensamento', { texto: 'a página abriu sem erro' }); return null; }
|
|
981
1034
|
return conferencias >= 2 ? null : medida.relato;
|
|
982
1035
|
};
|
|
983
1036
|
/* ── RODAR OS TESTES ANTES DE DIZER "PRONTO" (lib/conferir.js) ──────
|
|
@@ -1000,7 +1053,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1000
1053
|
const achados = K.procurar([...arquivosDaTarefa], { raiz: pastaAtual });
|
|
1001
1054
|
avisoDaChave = K.relato(achados);
|
|
1002
1055
|
if (!avisoDaChave) return null;
|
|
1003
|
-
|
|
1056
|
+
ev('linha', { texto: ` ⚠ ${avisoDaChave}`, nivel: 'aviso' });
|
|
1004
1057
|
return conferiuChaves >= 2 ? null : `${avisoDaChave} Tire a chave do código: leia de um .env (process.env) ou do servidor, e ponha o .env no .gitignore.`;
|
|
1005
1058
|
};
|
|
1006
1059
|
const conferirTestes = async () => {
|
|
@@ -1022,14 +1075,14 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1022
1075
|
if (r.ok) {
|
|
1023
1076
|
avisoDosTestes = '';
|
|
1024
1077
|
if (erroQueHavia) notaDoConserto = `O que estava errado: os testes falhavam (${erroQueHavia}); depois do conserto, passaram.`;
|
|
1025
|
-
|
|
1078
|
+
ev('linha', { texto: ` ✓ ${ex.titulo} (${como.comando})`, nivel: 'ok' });
|
|
1026
1079
|
try { checkpoint.marcarFuncionando(pastaAtual, `${como.comando}, pelo agente`); } catch { /* sem ponto pra marcar */ }
|
|
1027
1080
|
return null;
|
|
1028
1081
|
}
|
|
1029
1082
|
avisoDosTestes = `${ex.titulo} ${ex.linhas.slice(0, 2).join(' ')}`.trim();
|
|
1030
1083
|
// a frase que explica (a do Error:, se houver), não a do stack: é a que a pessoa entende
|
|
1031
1084
|
if (!erroQueHavia) erroQueHavia = String(ex.linhas.find((l) => /error|erro|assert|fail|falh/i.test(l)) || ex.linhas[0] || '').trim().slice(0, 140);
|
|
1032
|
-
|
|
1085
|
+
ev('linha', { texto: ` ✗ ${ex.titulo} (${como.comando})`, nivel: 'aviso' });
|
|
1033
1086
|
return rodadasDeTeste >= 2 ? null : `Rodei \`${como.comando}\` e falhou:\n${r.saida}`;
|
|
1034
1087
|
};
|
|
1035
1088
|
/* O ARQUIVO QUE O PEDIDO MANDA GRAVAR (lib/plugin/entregaveis.js): "grave o
|
|
@@ -1098,7 +1151,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1098
1151
|
if (decisao !== 'recusada' && decisao !== 'negada') require('../../plugin/voz-ponte').ferramenta(tc.name, tc.arguments);
|
|
1099
1152
|
const id = auditoria.antes({ agente: rotulo, ferramenta: tc.name, args: tc.arguments, decisao, motivo });
|
|
1100
1153
|
const falhou = auditoria.problema();
|
|
1101
|
-
if (falhou)
|
|
1154
|
+
if (falhou) ev('linha', { texto: `⚠ ${falhou}`, nivel: 'aviso' });
|
|
1102
1155
|
return id;
|
|
1103
1156
|
};
|
|
1104
1157
|
// O que read_file/list_dir devolveram, pela chave da época (repeticao.js).
|
|
@@ -1339,10 +1392,10 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1339
1392
|
}
|
|
1340
1393
|
// Recado digitado durante o passo anterior entra AGORA, antes da ida
|
|
1341
1394
|
// ao modelo — o resultado das ferramentas já está na conversa.
|
|
1342
|
-
if (!subagent) entregarRecados(messages, profundidade);
|
|
1395
|
+
if (!subagent) entregarRecados(messages, profundidade, emitir);
|
|
1343
1396
|
// Tarefa em segundo plano que acabou desde a última volta (lib/fundo.js): o modelo fica sabendo.
|
|
1344
1397
|
if (!subagent) for (const av of require('../../fundo').pegarAvisos()) messages.push({ role: 'user', content: av.texto });
|
|
1345
|
-
const sp =
|
|
1398
|
+
const sp = girar(subagent ? 'subagente trabalhando' : PENSANDO);
|
|
1346
1399
|
|
|
1347
1400
|
const toolCalls = [];
|
|
1348
1401
|
let assistantText = '';
|
|
@@ -1375,7 +1428,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1375
1428
|
resumir: ({ resumo, mensagens }, todos) => comp.resumirComFatos({ resumo, mensagens },
|
|
1376
1429
|
(p) => motor.requestResumo(server, { ...p, token }).then((x) => x && x.resumo), todos),
|
|
1377
1430
|
});
|
|
1378
|
-
if (r.ok)
|
|
1431
|
+
if (r.ok) ev('linha', { texto: ` ⎿ conversa passou de 80% da janela: ${r.resumidas} mensagens viraram resumo (${r.antes} → ${r.depois} tokens)`, nivel: 'dim' });
|
|
1379
1432
|
}
|
|
1380
1433
|
} catch { /* resumo é economia, não requisito */ }
|
|
1381
1434
|
}
|
|
@@ -1396,8 +1449,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1396
1449
|
if (!subagent && !toolCalls.length) {
|
|
1397
1450
|
if (!fluxo && assistantText.length > fala.LIMIAR_PENSAMENTO) {
|
|
1398
1451
|
sp.stop();
|
|
1399
|
-
fluxo =
|
|
1400
|
-
fluxo.escrever(assistantText);
|
|
1452
|
+
fluxo = abrirFluxo(assistantText);
|
|
1401
1453
|
} else if (fluxo) fluxo.escrever(evt.delta);
|
|
1402
1454
|
}
|
|
1403
1455
|
}
|
|
@@ -1429,14 +1481,14 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1429
1481
|
}
|
|
1430
1482
|
}
|
|
1431
1483
|
if (subagent) return { assistantMessage: { role: 'assistant', content: `Subagente falhou: ${fala.frase(e)}` } };
|
|
1432
|
-
|
|
1484
|
+
ev('problema', { erro: e });
|
|
1433
1485
|
throw e;
|
|
1434
1486
|
}
|
|
1435
1487
|
sp.stop();
|
|
1436
1488
|
emVoo = null;
|
|
1437
1489
|
if (!subagent && !avisouReserva && modeloDaVolta && modeloDaVolta.reserva) {
|
|
1438
1490
|
avisouReserva = true;
|
|
1439
|
-
|
|
1491
|
+
ev('linha', { texto: ` ⎿ ${linhaDaReserva(modeloDaVolta.modelo)}`, nivel: 'dim' });
|
|
1440
1492
|
}
|
|
1441
1493
|
traco(`volta ${voltas} voltou em ${Math.round((Date.now() - tRodada) / 1000)}s · ${toolCalls.map((t) => t.name).join(', ') || 'texto'}${streamError ? ' · erro: ' + String(streamError).slice(0, 160) : ''}`);
|
|
1442
1494
|
tempos.somar(subagent ? 'rodada do subagente' : 'rodada do modelo', Date.now() - tRodada);
|
|
@@ -1519,7 +1571,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1519
1571
|
if (fluxo) fluxo.fechar();
|
|
1520
1572
|
const amigavel = fala.frase(streamError);
|
|
1521
1573
|
if (subagent) return { assistantMessage: { role: 'assistant', content: `Subagente falhou: ${amigavel}` }, concluiu: false };
|
|
1522
|
-
|
|
1574
|
+
ev('problema', { erro: streamError });
|
|
1523
1575
|
// concluiu: false — o plano de passos não pode tratar um erro de
|
|
1524
1576
|
// provedor como resultado e passá-lo adiante como "fato".
|
|
1525
1577
|
return { assistantMessage: { role: 'assistant', content: amigavel }, concluiu: false };
|
|
@@ -1561,7 +1613,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1561
1613
|
if (pendentes) cobrouPlano = true; // este empurrão já cobrou o plano aberto
|
|
1562
1614
|
if (!subagent) {
|
|
1563
1615
|
if (fluxo) fluxo.fechar();
|
|
1564
|
-
else
|
|
1616
|
+
else ev('pensamento', { texto: finalText });
|
|
1565
1617
|
}
|
|
1566
1618
|
messages.push({ role: 'assistant', content: finalText });
|
|
1567
1619
|
messages.push({
|
|
@@ -1582,7 +1634,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1582
1634
|
try { const K = require('../../plugin/kits.js'); if (K.continuaVideo(userPrompt)) falta = K.batidasFaltando(pastaAtual); } catch { /* sem kits */ }
|
|
1583
1635
|
if (falta.length) {
|
|
1584
1636
|
empurroesDoKit++;
|
|
1585
|
-
if (fluxo) fluxo.fechar(); else
|
|
1637
|
+
if (fluxo) fluxo.fechar(); else ev('pensamento', { texto: finalText });
|
|
1586
1638
|
messages.push({ role: 'assistant', content: finalText });
|
|
1587
1639
|
messages.push({ role: 'user', content: `O vídeo ainda não está montado: faltam as batidas ${falta.join(', ')} em motion/plano.json. Não explique — mande agora a batida ${falta[0]}: run_shell "primocode kit batida ${falta[0]} --camadas '[…]'" (o formulário: "primocode kit batida ${falta[0]}"), e siga até o cartão (primocode kit plano).` });
|
|
1588
1640
|
finalText = '';
|
|
@@ -1645,7 +1697,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1645
1697
|
}
|
|
1646
1698
|
if (!subagent) {
|
|
1647
1699
|
if (fluxo) fluxo.fechar();
|
|
1648
|
-
else
|
|
1700
|
+
else ev('resposta', { texto: finalText });
|
|
1649
1701
|
}
|
|
1650
1702
|
/* Ia terminar, mas chegou recado no meio: a tarefa não acaba com
|
|
1651
1703
|
a pessoa esperando resposta. A volta seguinte entrega o recado. */
|
|
@@ -1661,7 +1713,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1661
1713
|
// O que o modelo disse ANTES de agir é pensamento, e sai como tal.
|
|
1662
1714
|
if (!subagent) {
|
|
1663
1715
|
if (fluxo) fluxo.fechar();
|
|
1664
|
-
else if (assistantText.trim())
|
|
1716
|
+
else if (assistantText.trim()) ev('pensamento', { texto: assistantText });
|
|
1665
1717
|
}
|
|
1666
1718
|
|
|
1667
1719
|
// Registra o turno do assistente COM os tool_calls. A API exige que
|
|
@@ -1694,14 +1746,14 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1694
1746
|
/* O plano decide quantos subagentes rodam JUNTOS (lib/config/limites.js; grátis e
|
|
1695
1747
|
Primo: um de cada vez, o resto continua em fila). Nunca recusa: só diz o porquê. */
|
|
1696
1748
|
const par = spawns.length > 1 ? await limites.paralelo() : null;
|
|
1697
|
-
if (par) { const av = par.aviso(spawns.length); if (av && !avisouParalelo) { avisouParalelo = true;
|
|
1749
|
+
if (par) { const av = par.aviso(spawns.length); if (av && !avisouParalelo) { avisouParalelo = true; ev('linha', { texto: ` ${av}` }); } }
|
|
1698
1750
|
if (spawns.length > 1 && par.max > 1 && !PARAR) {
|
|
1699
1751
|
const juntos = spawns.slice(0, par.max);
|
|
1700
|
-
|
|
1752
|
+
ev('paralelo', { quantos: juntos.length });
|
|
1701
1753
|
const ids = juntos.map((tc) => anotar(tc, 'permitida'));
|
|
1702
1754
|
const rs = await Promise.all(juntos.map((tc) =>
|
|
1703
1755
|
runSubagent(tc.arguments || {}, {
|
|
1704
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes,
|
|
1756
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes, emitir,
|
|
1705
1757
|
}).catch((e) => `Subagente falhou: ${e && e.message}`)));
|
|
1706
1758
|
juntos.forEach((tc, i) => { resultadosSpawn.set(tc.id, rs[i]); auditoria.depois(ids[i], { ok: true }); });
|
|
1707
1759
|
}
|
|
@@ -1824,7 +1876,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1824
1876
|
usuário (o bin). Sem a resposta dele, não troca. */
|
|
1825
1877
|
if (tc.name === 'mudar_pasta') {
|
|
1826
1878
|
algoRodou = true;
|
|
1827
|
-
|
|
1879
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
1828
1880
|
const idA = anotar(tc, 'permitida');
|
|
1829
1881
|
const r = await require('../../outra-pasta.js').pedirTroca(tc.arguments || {}, {
|
|
1830
1882
|
atual: pastaAtual, subagent, trocar: mudarPasta,
|
|
@@ -1834,7 +1886,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1834
1886
|
criarPasta = null;
|
|
1835
1887
|
temPasta = true;
|
|
1836
1888
|
}
|
|
1837
|
-
|
|
1889
|
+
ev('resultado', { nome: tc.name, resultado: r });
|
|
1838
1890
|
auditoria.depois(idA, r);
|
|
1839
1891
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
1840
1892
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(r) });
|
|
@@ -1847,7 +1899,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1847
1899
|
const r = resultadosSpawn.has(tc.id)
|
|
1848
1900
|
? resultadosSpawn.get(tc.id)
|
|
1849
1901
|
: await runSubagent(tc.arguments || {}, {
|
|
1850
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes,
|
|
1902
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes, emitir,
|
|
1851
1903
|
});
|
|
1852
1904
|
if (idA) auditoria.depois(idA, { ok: true });
|
|
1853
1905
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
@@ -1859,12 +1911,12 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1859
1911
|
nova, e quem sabe abrir conversa é este arquivo. */
|
|
1860
1912
|
if (tc.name === 'planejar_producao') {
|
|
1861
1913
|
algoRodou = true;
|
|
1862
|
-
|
|
1914
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
1863
1915
|
const idA = anotar(tc, 'permitida');
|
|
1864
1916
|
const r = await rodarDiretor(tc.arguments || {}, {
|
|
1865
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes,
|
|
1917
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes, emitir,
|
|
1866
1918
|
});
|
|
1867
|
-
|
|
1919
|
+
ev('resultado', { nome: tc.name, resultado: r });
|
|
1868
1920
|
auditoria.depois(idA, r);
|
|
1869
1921
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(r) });
|
|
1870
1922
|
continue;
|
|
@@ -1879,12 +1931,12 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1879
1931
|
if (tc.name === 'construir_app') {
|
|
1880
1932
|
algoRodou = true;
|
|
1881
1933
|
garantirPasta();
|
|
1882
|
-
|
|
1934
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
1883
1935
|
const idA = anotar(tc, 'permitida');
|
|
1884
1936
|
const r = await rodarPipeline(tc.arguments || {}, {
|
|
1885
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes, livro, autorizar, controle: portaoControle,
|
|
1937
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes, emitir, livro, autorizar, controle: portaoControle,
|
|
1886
1938
|
});
|
|
1887
|
-
|
|
1939
|
+
ev('resultado', { nome: tc.name, resultado: r });
|
|
1888
1940
|
auditoria.depois(idA, r);
|
|
1889
1941
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
1890
1942
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(r) });
|
|
@@ -1957,8 +2009,8 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1957
2009
|
igual = !!e && voltas - e.volta <= 1 && require('fs').readFileSync(abs, 'utf8') === e.texto;
|
|
1958
2010
|
} catch { igual = false; }
|
|
1959
2011
|
if (igual) {
|
|
1960
|
-
|
|
1961
|
-
|
|
2012
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
2013
|
+
ev('resultado-linha', { texto: 'igual ao que ele escreveu — não precisou ler' });
|
|
1962
2014
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify({
|
|
1963
2015
|
ok: true, igualAoQueVoceEscreveu: true,
|
|
1964
2016
|
aviso: 'Este arquivo está EXATAMENTE como você o escreveu nesta tarefa — o conteúdo é o da sua '
|
|
@@ -1990,8 +2042,8 @@ async function runActInterno(userPrompt, opts) {
|
|
|
1990
2042
|
const porta = await permissao.pedir(portaoControle, tc.name, tc.arguments, autorizar);
|
|
1991
2043
|
if (!porta.ok) {
|
|
1992
2044
|
anotar(tc, 'recusada', porta.error);
|
|
1993
|
-
|
|
1994
|
-
|
|
2045
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
2046
|
+
ev('resultado-linha', { texto: 'não autorizado', nivel: 'aviso' });
|
|
1995
2047
|
registro.anotar(livro, tc.name, tc.arguments, { ok: false, error: porta.error });
|
|
1996
2048
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify({ ok: false, error: porta.error }) });
|
|
1997
2049
|
continue;
|
|
@@ -2008,8 +2060,8 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2008
2060
|
});
|
|
2009
2061
|
if (!risco.ok) {
|
|
2010
2062
|
anotar(tc, 'recusada', risco.motivo || risco.error);
|
|
2011
|
-
|
|
2012
|
-
|
|
2063
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
2064
|
+
ev('resultado-linha', { texto: `não autorizado — ${risco.motivo}`, nivel: 'aviso' });
|
|
2013
2065
|
registro.anotar(livro, tc.name, tc.arguments, { ok: false, error: risco.error });
|
|
2014
2066
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify({ ok: false, error: risco.error }) });
|
|
2015
2067
|
continue;
|
|
@@ -2019,8 +2071,8 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2019
2071
|
const gancho = hooks.rodar('PreToolUse', { nome: tc.name, args: tc.arguments, projectDir: pastaAtual });
|
|
2020
2072
|
if (gancho.bloqueado) {
|
|
2021
2073
|
anotar(tc, 'recusada', gancho.motivo);
|
|
2022
|
-
|
|
2023
|
-
|
|
2074
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
2075
|
+
ev('resultado-linha', { texto: `bloqueado por hook — ${gancho.motivo.slice(0, 80)}`, nivel: 'aviso' });
|
|
2024
2076
|
registro.anotar(livro, tc.name, tc.arguments, { ok: false, error: gancho.motivo });
|
|
2025
2077
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify({ ok: false, error: `Bloqueado por hook: ${gancho.motivo}` }) });
|
|
2026
2078
|
continue;
|
|
@@ -2031,13 +2083,13 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2031
2083
|
if (['run_shell', 'run_in_new_terminal', 'run_background'].includes(tc.name) && perigo.apagaBancoNoComando((tc.arguments || {}).command)) {
|
|
2032
2084
|
try {
|
|
2033
2085
|
const copia = require('../../backup-banco.js').fazer(pastaAtual);
|
|
2034
|
-
if (copia)
|
|
2086
|
+
if (copia) ev('linha', { texto: ` ⎿ fiz uma cópia do banco antes (${copia.arquivos.join(', ')}) · /undo devolve`, nivel: 'ok' });
|
|
2035
2087
|
} catch { /* sem cópia o comando segue: ela é rede de segurança, não condição */ }
|
|
2036
2088
|
}
|
|
2037
2089
|
|
|
2038
2090
|
algoRodou = true;
|
|
2039
2091
|
rodouTool = true;
|
|
2040
|
-
|
|
2092
|
+
ev('acao', { nome: tc.name, args: tc.arguments });
|
|
2041
2093
|
/* Antes de mexer no arquivo, guarda o original — é o que o /desfazer
|
|
2042
2094
|
devolve. Caminho inválido não é problema nosso: a tool recusa. */
|
|
2043
2095
|
let antesDaEdicao = null, arquivoDaEdicao = null; // pro alarme do arquivo que encolheu (lib/session/encolheu.js)
|
|
@@ -2052,7 +2104,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2052
2104
|
catch (e) {
|
|
2053
2105
|
// Caminho inválido a tool recusa sozinha. O resto (disco cheio, sem permissão)
|
|
2054
2106
|
// quer dizer que o /desfazer não vai devolver este arquivo: a pessoa tem que saber.
|
|
2055
|
-
if (tc.arguments && tc.arguments.path)
|
|
2107
|
+
if (tc.arguments && tc.arguments.path) ev('linha', { texto: ` ⚠ não guardei o original de ${tc.arguments.path}: o /undo não vai voltar este arquivo (${e.message})`, nivel: 'aviso' });
|
|
2056
2108
|
}
|
|
2057
2109
|
}
|
|
2058
2110
|
/* O servidor, o token e o cliente da API vão junto: a legenda do
|
|
@@ -2061,7 +2113,9 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2061
2113
|
traco(`executando ${tc.name}`);
|
|
2062
2114
|
const idAcao = idsAdiantados.get(tc.id) || anotar(tc, 'permitida');
|
|
2063
2115
|
const result = adiantadas.has(tc.id) ? adiantadas.get(tc.id)
|
|
2064
|
-
: await tools.execute(tc.name, tc.arguments, { projectDir: pastaAtual, server, token, api, escolher: subagent ? undefined : escolher
|
|
2116
|
+
: await tools.execute(tc.name, tc.arguments, { projectDir: pastaAtual, server, token, api, escolher: subagent ? undefined : escolher,
|
|
2117
|
+
// estudio_criar conta o andamento: a tela desenha a barra do item `estudio`.
|
|
2118
|
+
progresso: tc.name === 'estudio_criar' ? (pct, etapa) => ev('estudio-progresso', { pct, etapa }) : undefined });
|
|
2065
2119
|
auditoria.depois(idAcao, result);
|
|
2066
2120
|
if (sinais && result && result.ok && /^(write_file|edit_file|multi_edit)$/.test(tc.name)) sinais.diagSujo = !!result.diagnosticos; // diagnóstico depois da edição
|
|
2067
2121
|
if (antesDaEdicao != null && result && result.ok !== false) {
|
|
@@ -2069,19 +2123,21 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2069
2123
|
try { depois = require('fs').readFileSync(arquivoDaEdicao, 'utf8'); } catch { depois = null; }
|
|
2070
2124
|
const encolheu = require('../encolheu.js').julgar(antesDaEdicao, depois);
|
|
2071
2125
|
if (encolheu) {
|
|
2072
|
-
|
|
2126
|
+
ev('linha', { texto: ` ⚠ ${tc.arguments.path}: ${encolheu.texto}. Se sumiu código sem querer, /undo volta.`, nivel: 'aviso' });
|
|
2073
2127
|
result.aviso_encolheu = `ATENÇÃO: ${tc.arguments.path} ${encolheu.texto}. Confira se você não apagou código sem querer; se apagou, refaça a edição sem cortar nada.`;
|
|
2074
2128
|
}
|
|
2075
2129
|
}
|
|
2076
|
-
for (const aviso of hooks.rodar('PostToolUse', { nome: tc.name, args: tc.arguments, resultado: result, projectDir: pastaAtual }).avisos)
|
|
2130
|
+
for (const aviso of hooks.rodar('PostToolUse', { nome: tc.name, args: tc.arguments, resultado: result, projectDir: pastaAtual }).avisos) ev('linha', { texto: ` ⎿ ${aviso}`, nivel: 'dim' });
|
|
2077
2131
|
traco(`${tc.name} devolveu ok=${result && result.ok}`);
|
|
2078
2132
|
if (/^estudio_|^planejar_producao$/.test(tc.name) && result && result.ok !== false) {
|
|
2079
2133
|
const id = result.id || (result.projeto && result.projeto.id) || (tc.arguments && tc.arguments.id);
|
|
2080
2134
|
if (typeof id === 'string' && id) estudioDaTarefa = id;
|
|
2081
2135
|
}
|
|
2082
|
-
|
|
2136
|
+
ev('resultado', { nome: tc.name, resultado: result });
|
|
2137
|
+
// Render é lento e roda solto: ao fim, o sino avisa (vira notice/created no app).
|
|
2138
|
+
if (tc.name === 'estudio_criar' && result && result.ok !== false && result.arquivo) ev('aviso', { texto: `${result.resumo || 'Pronto'} ${require('path').basename(result.arquivo)}` });
|
|
2083
2139
|
if (/^estudio_render/.test(tc.name) && result && result.conferencia && !subagent) {
|
|
2084
|
-
for (const l of require('../../plugin/estudio/conferencia.js').linhasPraPessoa(result.conferencia))
|
|
2140
|
+
for (const l of require('../../plugin/estudio/conferencia.js').linhasPraPessoa(result.conferencia)) ev('linha', { texto: ` ⎿ ${l}`, nivel: 'aviso' });
|
|
2085
2141
|
}
|
|
2086
2142
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
2087
2143
|
if (RELEITURA.has(tc.name) && result && result.ok !== false) {
|
|
@@ -2202,7 +2258,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2202
2258
|
|
|
2203
2259
|
if (finished) {
|
|
2204
2260
|
finalText = finished;
|
|
2205
|
-
if (!subagent)
|
|
2261
|
+
if (!subagent) ev('resposta', { texto: finished });
|
|
2206
2262
|
// Recado chegou enquanto ele fechava: entrega e segue.
|
|
2207
2263
|
if (!subagent && CAIXA.length && !PARAR) { finalText = ''; continue; }
|
|
2208
2264
|
break;
|
|
@@ -2220,14 +2276,14 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2220
2276
|
let passagemDaParada = null;
|
|
2221
2277
|
if (paradoPorTravar && !finalText) {
|
|
2222
2278
|
finalText = paradoPorTravar;
|
|
2223
|
-
if (!subagent)
|
|
2279
|
+
if (!subagent) ev('aviso', { texto: finalText });
|
|
2224
2280
|
// Parou sem terminar: a memória leva o estado, não só "parei" (lib/passagem.js).
|
|
2225
2281
|
passagemDaParada = require('../../passagem').montar({ pedido: userPrompt, motivo: finalText, registro: livro, falhasPendentes });
|
|
2226
2282
|
}
|
|
2227
2283
|
|
|
2228
2284
|
if (PARAR && !finalText) {
|
|
2229
2285
|
finalText = 'Parado por você (Esc Esc). O que já foi feito, ficou feito.';
|
|
2230
|
-
if (!subagent)
|
|
2286
|
+
if (!subagent) ev('aviso', { texto: finalText });
|
|
2231
2287
|
}
|
|
2232
2288
|
|
|
2233
2289
|
// Nunca terminar em silêncio. Quando o modelo devolvia resposta vazia sem
|
|
@@ -2236,7 +2292,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2236
2292
|
// depois de a primeira tentativa de seletor falhar.
|
|
2237
2293
|
if (!finalText) {
|
|
2238
2294
|
finalText = motivoDaParada({ voltas, voltasEmFalso, falhasPendentes, travou: rodadasParadas >= MAX_RODADAS_PARADAS, teto });
|
|
2239
|
-
if (!subagent)
|
|
2295
|
+
if (!subagent) ev('aviso', { texto: finalText });
|
|
2240
2296
|
passagemDaParada = require('../../passagem').montar({ pedido: userPrompt, motivo: finalText, registro: livro, falhasPendentes });
|
|
2241
2297
|
}
|
|
2242
2298
|
|
|
@@ -2250,7 +2306,7 @@ async function runActInterno(userPrompt, opts) {
|
|
|
2250
2306
|
// arquivo de verdade — a memória era descartada nesse caso, sempre.
|
|
2251
2307
|
// A raiz de trabalho não é projeto: um memory.md solto em ~/primocode
|
|
2252
2308
|
// era o que um pedido de vídeo (que trabalha na raiz) deixava para trás.
|
|
2253
|
-
if (!subagent && temPasta && require('path').resolve(pastaAtual) !== require('path').resolve(cfg.ROOT_DIR)) {
|
|
2309
|
+
if (!subagent && temPasta && pastaAtual && require('path').resolve(pastaAtual) !== require('path').resolve(cfg.ROOT_DIR)) {
|
|
2254
2310
|
cfg.appendProjectMemory(pastaAtual, passagemDaParada || `**Pedido:** ${userPrompt.slice(0, 300)}\n**Resultado:** ${finalText.slice(0, 500)}`);
|
|
2255
2311
|
}
|
|
2256
2312
|
|
|
@@ -2299,11 +2355,13 @@ async function runSubagent(args, ctx) {
|
|
|
2299
2355
|
// O ofício: o modelo diz quem faz (`especialista`), ou a tarefa sugere. O
|
|
2300
2356
|
// briefing vai na frente da tarefa, na mesma mensagem — ver especialistas.js.
|
|
2301
2357
|
// Novo catálogo (agentes/): "id" ou "id1,id2" (até 3 olhares na mesma tarefa); o que não existe cai no legado.
|
|
2302
|
-
|
|
2303
|
-
const
|
|
2358
|
+
// `agente`: um agente da pessoa (~/.primocode/agentes, lib/agentes-pessoais.js) ganha do catálogo e do legado.
|
|
2359
|
+
const meu = args.agente ? require('../../agentes-pessoais').ler(args.agente) : null;
|
|
2360
|
+
const novos = meu ? [] : require('../../agentes').refinar(args.especialista, args.prompt); // o catálogo confere o chute do modelo
|
|
2361
|
+
const esp = meu ? require('../../agentes-pessoais').comoEspecialista(meu) : novos.length ? null : especialistas.escolher(args.especialista, args.prompt);
|
|
2304
2362
|
const agentes = require('../../agentes');
|
|
2305
|
-
const rot = novos.length ? agentes.rotulo(novos) : especialistas.rotulo(esp);
|
|
2306
|
-
|
|
2363
|
+
const rot = meu ? meu.nome : novos.length ? agentes.rotulo(novos) : especialistas.rotulo(esp);
|
|
2364
|
+
telaDe(ctx, 'agente', { rotulo: rot, descricao });
|
|
2307
2365
|
|
|
2308
2366
|
// modelo_sugerido do briefing vale quando o placar ainda não tem dado desse tipo de tarefa.
|
|
2309
2367
|
let modeloDoAgente = model;
|
|
@@ -2322,6 +2380,7 @@ async function runSubagent(args, ctx) {
|
|
|
2322
2380
|
subagent: true,
|
|
2323
2381
|
motor,
|
|
2324
2382
|
permissoes,
|
|
2383
|
+
emitir: emitirDe(ctx),
|
|
2325
2384
|
autorizar: ctx.autorizar,
|
|
2326
2385
|
controle: ctx.controle, // o mesmo objeto: a resposta vale para todos
|
|
2327
2386
|
rotulo: `subagente: ${String(descricao).slice(0, 60)}`,
|
|
@@ -2334,7 +2393,7 @@ async function runSubagent(args, ctx) {
|
|
|
2334
2393
|
if (ctx.livro && r.registro) registro.juntar(ctx.livro, r.registro);
|
|
2335
2394
|
|
|
2336
2395
|
const resumo = r.assistantMessage.content || 'sem resultado';
|
|
2337
|
-
|
|
2396
|
+
telaDe(ctx, 'agente-voltou', { texto: resumo });
|
|
2338
2397
|
return entregaDoAgente(rot, resumo, r.registro, { pasta: projectDir });
|
|
2339
2398
|
}
|
|
2340
2399
|
|
|
@@ -2417,10 +2476,10 @@ async function rodarDiretor(args, ctx) {
|
|
|
2417
2476
|
const diretor = require('../../plugin/estudio/diretor.js');
|
|
2418
2477
|
const correr = async (prompt, rotulo) => {
|
|
2419
2478
|
if (PARAR) return '';
|
|
2420
|
-
|
|
2479
|
+
telaDe(ctx, 'agente', { rotulo: 'Diretor', descricao: rotulo });
|
|
2421
2480
|
const r = await runAct(prompt, {
|
|
2422
2481
|
server, model, token, projectDir, projectName,
|
|
2423
|
-
history: [], profundidade: profundidade + 1, subagent: true, motor, permissoes,
|
|
2482
|
+
history: [], profundidade: profundidade + 1, subagent: true, motor, permissoes, emitir: emitirDe(ctx),
|
|
2424
2483
|
});
|
|
2425
2484
|
return (r && r.assistantMessage && r.assistantMessage.content) || '';
|
|
2426
2485
|
};
|
|
@@ -2431,7 +2490,7 @@ async function rodarDiretor(args, ctx) {
|
|
|
2431
2490
|
formato: args.formato === 'apresentacao' ? 'apresentacao' : 'video',
|
|
2432
2491
|
contexto: args.contexto || '',
|
|
2433
2492
|
correr,
|
|
2434
|
-
mostrar: (t) =>
|
|
2493
|
+
mostrar: (t) => telaDe(ctx, 'agente', { rotulo: 'Diretor', descricao: t }),
|
|
2435
2494
|
});
|
|
2436
2495
|
if (!r.ok) return r;
|
|
2437
2496
|
|
|
@@ -2473,10 +2532,10 @@ async function rodarPipeline(args, ctx) {
|
|
|
2473
2532
|
|
|
2474
2533
|
const correr = async (prompt, rotulo) => {
|
|
2475
2534
|
if (PARAR) return '';
|
|
2476
|
-
|
|
2535
|
+
telaDe(ctx, 'agente', { rotulo: 'Agente', descricao: rotulo });
|
|
2477
2536
|
const r = await runAct(prompt, {
|
|
2478
2537
|
server, model, token, projectDir: pasta, projectName,
|
|
2479
|
-
history: [], profundidade: profundidade + 1, subagent: true, motor, permissoes,
|
|
2538
|
+
history: [], profundidade: profundidade + 1, subagent: true, motor, permissoes, emitir: emitirDe(ctx),
|
|
2480
2539
|
});
|
|
2481
2540
|
return (r && r.assistantMessage && r.assistantMessage.content) || '';
|
|
2482
2541
|
};
|
|
@@ -2486,7 +2545,7 @@ async function rodarPipeline(args, ctx) {
|
|
|
2486
2545
|
pasta,
|
|
2487
2546
|
rodadas: args.rodadas,
|
|
2488
2547
|
correr,
|
|
2489
|
-
mostrar: (linha) =>
|
|
2548
|
+
mostrar: (linha) => telaDe(ctx, 'resultado-linha', { texto: linha }),
|
|
2490
2549
|
abrir: tools.openInBrowser,
|
|
2491
2550
|
});
|
|
2492
2551
|
}
|
|
@@ -2518,5 +2577,5 @@ function novoPortaoDeProva() {
|
|
|
2518
2577
|
};
|
|
2519
2578
|
}
|
|
2520
2579
|
|
|
2521
|
-
module.exports = { summarizeForModel, mesmoComando, EDICAO_DE_VIDEO_FALADO, novoPortaoDeProva, runAct, erroDaCorrente, linhaDaReserva, videoForaDeEdicao, entregaDoAgente, lembreteDeParalelo, buildMemoryContext, leituraPeloShell, historicoEnxuto, interromper, interrompido, mandarDuranteATarefa, trabalhando, sobrasDaCaixa, entregarRecados, compactar, motivoDaParada, pareceParadaNoMeio, ehTetoDeToken, ehTransitorio, esperaDoErro,
|
|
2580
|
+
module.exports = { summarizeForModel, mesmoComando, EDICAO_DE_VIDEO_FALADO, novoPortaoDeProva, runAct, logResult, interrompido: () => PARAR, erroDaCorrente, linhaDaReserva, videoForaDeEdicao, entregaDoAgente, lembreteDeParalelo, buildMemoryContext, leituraPeloShell, historicoEnxuto, interromper, interrompido, mandarDuranteATarefa, trabalhando, sobrasDaCaixa, entregarRecados, compactar, motivoDaParada, pareceParadaNoMeio, ehTetoDeToken, ehTransitorio, esperaDoErro,
|
|
2522
2581
|
anotacaoDoQueJaFoi, resumoDoQueJaFoi, encolherParaContinuar, recadoDoPlanoAberto, MAX_ITERATIONS };
|
package/lib/skill/catalogo.js
CHANGED
|
@@ -82,6 +82,8 @@ const GRUPOS = [
|
|
|
82
82
|
nome: 'Estúdio',
|
|
83
83
|
resumo: 'vídeo, motion, slides e arte — a IA faz, e no fim abre o resultado pronto',
|
|
84
84
|
itens: [
|
|
85
|
+
{ tool: 'estudio_criar', escreve: true, faz: 'a porta simples: cria slides, vídeo, motion, anúncio ou abertura do pedido ao arquivo, e abre o resultado',
|
|
86
|
+
args: 'tipo (slides|video|motion|anuncio|abertura). briefing (uma frase). duracao (s, opcional). formato (9:16|16:9|1:1, opcional). Para controle fino use as outras estudio_*.' },
|
|
85
87
|
{ tool: 'estudio_projeto', escreve: true, faz: 'abre, acha e lista os projetos do estúdio — cada peça é uma pasta que não some',
|
|
86
88
|
args: 'acao: criar | abrir | procurar | listar | anotar | editar | assinatura. '
|
|
87
89
|
+ 'assinatura: quando o usuário diz o que achou de um vídeo ("adorei a virada", "texto demais"), grave com texto (e o id) — o próximo vídeo parte disso; sem texto, lê as faixas e as notas. '
|
|
@@ -282,7 +284,7 @@ const GRUPOS = [
|
|
|
282
284
|
+ 'Para só LER lá não precisa trocar: read_file/list_dir com o caminho absoluto, glob/grep com padrão absoluto.' },
|
|
283
285
|
// Estas duas o loop do agente atende (lib/session/runner/act.js), não o tools.execute.
|
|
284
286
|
{ tool: 'spawn_agent', escreve: false, faz: 'chama um subagente especialista para uma parte da tarefa', noLoop: true,
|
|
285
|
-
args: 'description: 3 a 5 palavras, aparece na tela do usuário. prompt: instrução completa e autossuficiente — o subagente não vê esta conversa. especialista (opcional): id EXATO que agentes_buscar devolveu — não chute o nome (o mais específico vence: css-layout, pagamentos, kubernetes, regex…, não frontend/backend/devops) ou dois separados por vírgula — cada um chega com o briefing da profissão (lib/skill/especialistas.js); sem ele, a tarefa sugere.' },
|
|
287
|
+
args: 'description: 3 a 5 palavras, aparece na tela do usuário. prompt: instrução completa e autossuficiente — o subagente não vê esta conversa. especialista (opcional): id EXATO que agentes_buscar devolveu — não chute o nome (o mais específico vence: css-layout, pagamentos, kubernetes, regex…, não frontend/backend/devops) ou dois separados por vírgula — cada um chega com o briefing da profissão (lib/skill/especialistas.js); sem ele, a tarefa sugere. agente (opcional): nome de um agente da pessoa (@nome); ganha do especialista.' },
|
|
286
288
|
{ tool: 'construir_app', escreve: true, noLoop: true,
|
|
287
289
|
faz: 'constrói um app ou site inteiro com quatro papéis: engenheiro de prompt → protótipo → MVP → revisor que testa escondido, em laço até passar',
|
|
288
290
|
args: 'pedido: o que o usuário quer, com as palavras dele. pasta (opcional): onde construir — sem ela, uma pasta nova com o nome do pedido. rodadas (opcional, 1 a 4, padrão 2): quantas voltas de correção. '
|
package/lib/tool/tools.js
CHANGED
|
@@ -688,6 +688,7 @@ async function despachar(name, args, ctx) {
|
|
|
688
688
|
ok: true, created,
|
|
689
689
|
path: path.relative(projectDir, filePath).replace(/\\/g, '/'),
|
|
690
690
|
bytes: content.length,
|
|
691
|
+
linhas: content.split('\n').length,
|
|
691
692
|
...(created ? {} : { _antes: antes, _depois: content }),
|
|
692
693
|
};
|
|
693
694
|
}
|
|
@@ -943,6 +944,12 @@ async function despachar(name, args, ctx) {
|
|
|
943
944
|
return estudio.ferramentaMidia(args);
|
|
944
945
|
case 'estudio_ae':
|
|
945
946
|
return estudio.ferramentaAE(args);
|
|
947
|
+
case 'estudio_criar':
|
|
948
|
+
// A porta simples: abre o projeto e chama plano, motion e render por dentro. ctx.progresso alimenta a tela.
|
|
949
|
+
return estudio.ferramentaCriar(args, {
|
|
950
|
+
...ctx, projectDir,
|
|
951
|
+
abrir: (arquivo) => { try { openInBrowser(arquivo); } catch {} },
|
|
952
|
+
});
|
|
946
953
|
case 'estudio_motion':
|
|
947
954
|
return estudio.ferramentaMotion(args);
|
|
948
955
|
case 'estudio_editar':
|