primocode 9.8.0-beta.0 → 9.8.0-beta.10
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 +36 -8
- package/app/ansi.js +55 -0
- package/app/app.css +482 -281
- package/app/app.js +888 -315
- package/app/index.html +176 -101
- package/app/primo-marca.js +58 -1
- package/bin/primocode.js +770 -118
- package/comandos/explicar.md +14 -0
- package/comandos/publicar.md +10 -0
- package/comandos/revisar.md +13 -0
- package/lib/act.js +118 -18
- package/lib/api.js +21 -0
- package/lib/app.js +317 -74
- package/lib/browser.js +79 -3
- package/lib/checkpoint-git.js +218 -0
- package/lib/checkpoint.js +249 -0
- package/lib/claude-engine.js +630 -0
- package/lib/codex-engine.js +207 -0
- package/lib/comandos-extra.js +481 -0
- package/lib/comandos.js +104 -0
- package/lib/config.js +21 -4
- package/lib/fala.js +95 -1
- package/lib/local/cerebro.json +2116 -0
- package/lib/local/entrada.js +332 -0
- package/lib/local/motor-cli.js +90 -0
- package/lib/local/motor.js +264 -0
- package/lib/local/provedor.js +169 -0
- package/lib/mencoes.js +98 -0
- package/lib/offline.js +168 -0
- package/lib/painel.js +291 -0
- package/lib/perigo.js +256 -0
- package/lib/pre-leitura.js +118 -0
- package/lib/repeticao.js +4 -2
- package/lib/skills.js +6 -1
- package/lib/tempos.js +62 -0
- package/lib/ui.js +171 -17
- package/package.json +3 -2
- package/skills/apresentacao-pptx/SKILL.md +52 -0
- package/skills/cartaz-de-arte/LICENSE.txt +202 -0
- package/skills/cartaz-de-arte/SKILL.md +47 -0
- package/skills/criar-skill/LICENSE.txt +202 -0
- package/skills/criar-skill/SKILL.md +49 -0
- package/skills/depurar-de-verdade/LICENSE.txt +21 -0
- package/skills/depurar-de-verdade/SKILL.md +14 -5
- package/skills/design-de-verdade/LICENSE.txt +177 -0
- package/skills/design-de-verdade/SKILL.md +42 -0
- package/skills/documento-word/SKILL.md +57 -0
- package/skills/interface-revisada/SKILL.md +47 -0
- package/skills/me-ensina/LICENSE.txt +21 -0
- package/skills/me-ensina/SKILL.md +27 -0
- package/skills/me-entrevista/LICENSE.txt +21 -0
- package/skills/me-entrevista/SKILL.md +27 -0
- package/skills/motion-de-primeira/SKILL.md +1 -1
- package/skills/pdf-bonito/SKILL.md +48 -0
- package/skills/planilha-excel/SKILL.md +47 -0
- package/skills/react-sem-dor/SKILL.md +48 -0
- package/skills/revisao-antes-de-entregar/LICENSE.txt +21 -0
- package/skills/revisao-antes-de-entregar/SKILL.md +6 -1
- package/skills/site-com-motion/SKILL.md +39 -0
- package/skills/temas-prontos/LICENSE.txt +202 -0
- package/skills/temas-prontos/SKILL.md +50 -0
- package/skills/testar-o-site/LICENSE.txt +202 -0
- package/skills/testar-o-site/SKILL.md +46 -0
- package/skills/video-no-remotion/SKILL.md +48 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
descricao: explica este projeto (ou um arquivo) para quem não programa
|
|
3
|
+
---
|
|
4
|
+
Me explique como se eu nunca tivesse programado: $ARGUMENTOS
|
|
5
|
+
|
|
6
|
+
Se eu não disse o que explicar, explique o projeto inteiro desta pasta. Leia os arquivos antes de falar, não chute.
|
|
7
|
+
|
|
8
|
+
Responda nesta ordem, em frases curtas:
|
|
9
|
+
1. o que isto faz, em uma frase;
|
|
10
|
+
2. as partes principais e o papel de cada uma (use comparações do dia a dia);
|
|
11
|
+
3. como eu rodo e vejo funcionando;
|
|
12
|
+
4. onde eu mexo para mudar o que é mais provável que eu queira mudar.
|
|
13
|
+
|
|
14
|
+
Sem jargão. Se uma palavra técnica for inevitável, explique ela na hora.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
descricao: guia passo a passo para colocar o projeto no ar, com link público
|
|
3
|
+
---
|
|
4
|
+
Quero colocar este projeto no ar, com um link que eu possa mandar para as pessoas. $ARGUMENTOS
|
|
5
|
+
|
|
6
|
+
Primeiro olhe o projeto e me diga, em uma frase, que tipo de coisa ele é (site estático, app com servidor, etc.). Depois recomende UM caminho gratuito que sirva para ele (por exemplo GitHub Pages, Vercel, Netlify ou Railway) e diga por que esse.
|
|
7
|
+
|
|
8
|
+
Me guie um passo por vez, esperando eu confirmar antes do próximo. Faça você o que der para fazer pelo terminal; o que só eu posso fazer (criar conta, clicar num botão no site) me diga exatamente onde clicar.
|
|
9
|
+
|
|
10
|
+
Nunca coloque senha, chave ou arquivo .env no que for publicado. Se achar um, pare e me avise.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
descricao: revisa o projeto (ou o que mudou) atrás de bugs, e explica sem jargão
|
|
3
|
+
---
|
|
4
|
+
Revise este projeto procurando bugs e coisas que vão quebrar. Se for um repositório git com mudanças ainda não salvas, comece pelo que mudou (git status e git diff); se não, olhe os arquivos principais.
|
|
5
|
+
|
|
6
|
+
Foco, se eu disser: $ARGUMENTOS
|
|
7
|
+
|
|
8
|
+
Para cada problema que achar, diga em linguagem simples, como para quem não programa:
|
|
9
|
+
1. o que está errado e onde (arquivo e linha);
|
|
10
|
+
2. o que acontece com quem usa, se ninguém consertar;
|
|
11
|
+
3. como consertar.
|
|
12
|
+
|
|
13
|
+
Ordene do mais grave para o menos grave. Não conserte nada ainda: só mostre a lista e pergunte quais eu quero que você arrume.
|
package/lib/act.js
CHANGED
|
@@ -21,7 +21,11 @@ const repeticao = require('./repeticao');
|
|
|
21
21
|
const diff = require('./diff');
|
|
22
22
|
const registro = require('./registro');
|
|
23
23
|
const permissao = require('./permissao');
|
|
24
|
+
const perigo = require('./perigo');
|
|
24
25
|
const skills = require('./skills');
|
|
26
|
+
const checkpoint = require('./checkpoint');
|
|
27
|
+
const tempos = require('./tempos');
|
|
28
|
+
const preLeitura = require('./pre-leitura');
|
|
25
29
|
|
|
26
30
|
// 40, não 15: com o freio de repetição (repeticao.js) e o de rodada parada,
|
|
27
31
|
// volta a mais não é volta em círculo — é trabalho. Quinze acabavam no meio
|
|
@@ -33,6 +37,8 @@ const MAX_EMPURROES = 3;
|
|
|
33
37
|
// Voltas seguidas sem executar nada antes de desistir.
|
|
34
38
|
const MAX_RODADAS_PARADAS = 3;
|
|
35
39
|
const MAX_PROFUNDIDADE = 2; // principal + 1 nível de subagente
|
|
40
|
+
// As leituras que, repetidas sem mudança no meio, devolvem o guardado.
|
|
41
|
+
const RELEITURA = new Set(['read_file', 'list_dir']);
|
|
36
42
|
|
|
37
43
|
// ── Interrupção (Esc Esc) ─────────────────────────────────────────────────
|
|
38
44
|
// O usuário aperta Esc duas vezes e o agente PARA — entre uma rodada e outra
|
|
@@ -132,6 +138,9 @@ function limparParaOModelo(result) {
|
|
|
132
138
|
|
|
133
139
|
function logResult(kind, result, profundidade) {
|
|
134
140
|
const p = fala.resultado(profundidade);
|
|
141
|
+
// A linha da janela (lib/app.js) fecha aqui: o mesmo resultado que o
|
|
142
|
+
// terminal desenha embaixo, resumido. Ver o canal em lib/fala.js.
|
|
143
|
+
fala.avisar('resultado', fala.resumoDoResultado(kind, result));
|
|
135
144
|
if (!result) return;
|
|
136
145
|
// Uma tool que falhou é informação para o AGENTE, que vai consertar na
|
|
137
146
|
// volta seguinte — não é o erro do usuário. Âmbar, uma linha, sem o
|
|
@@ -380,6 +389,7 @@ async function pausar(ms, subagent) {
|
|
|
380
389
|
const sp = subagent ? null : new Spinner(PENSANDO).start();
|
|
381
390
|
await new Promise((r) => setTimeout(r, ms));
|
|
382
391
|
if (sp) sp.stop();
|
|
392
|
+
tempos.somar('espera de cota/rede', ms);
|
|
383
393
|
}
|
|
384
394
|
|
|
385
395
|
/**
|
|
@@ -449,10 +459,10 @@ function motivoDaParada({ voltas, voltasEmFalso, falhasPendentes, travou }) {
|
|
|
449
459
|
const PULAR_NO_MAPA = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.nuxt', 'out',
|
|
450
460
|
'.cache', 'coverage', '__pycache__', '.venv', 'venv', '.turbo', '.vercel', '.idea', '.vscode']);
|
|
451
461
|
|
|
452
|
-
function
|
|
462
|
+
function arquivosDoProjeto(dir, limite = 80) {
|
|
453
463
|
const path = require('path');
|
|
454
464
|
const fs = require('fs');
|
|
455
|
-
if (!dir || path.resolve(dir) === path.resolve(cfg.ROOT_DIR)) return
|
|
465
|
+
if (!dir || path.resolve(dir) === path.resolve(cfg.ROOT_DIR)) return [];
|
|
456
466
|
const achados = [];
|
|
457
467
|
const andar = (atual, fundo) => {
|
|
458
468
|
if (fundo > 4 || achados.length >= limite) return;
|
|
@@ -468,19 +478,33 @@ function mapaDoProjeto(dir, limite = 80) {
|
|
|
468
478
|
}
|
|
469
479
|
};
|
|
470
480
|
andar(dir, 0);
|
|
481
|
+
return achados;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function mapaDoProjeto(dir, limite = 80, achados = arquivosDoProjeto(dir, limite)) {
|
|
471
485
|
if (!achados.length) return '';
|
|
472
486
|
const mais = achados.length >= limite ? `\n… (mais arquivos — use glob para achar)` : '';
|
|
473
487
|
return `Arquivos que JÁ existem neste projeto (edite-os em vez de recriar):\n${achados.join('\n')}${mais}`;
|
|
474
488
|
}
|
|
475
489
|
|
|
476
|
-
|
|
490
|
+
/* `pedido` é opcional: com ele, o arquivo citado no pedido é o primeiro a
|
|
491
|
+
entrar já lido (ver lib/pre-leitura.js). */
|
|
492
|
+
function buildMemoryContext(projectDir, projectName, pedido = '') {
|
|
477
493
|
const parts = [];
|
|
494
|
+
// As instruções vêm primeiro: são regra de quem manda no projeto, e a
|
|
495
|
+
// memória é o que o agente anotou. Entram aqui (e não no bin) para valer
|
|
496
|
+
// igual no terminal, no modo voz e em cada passo de um plano.
|
|
497
|
+
const instrucoes = require('./mencoes.js').instrucoesDoProjeto(projectDir);
|
|
498
|
+
if (instrucoes) parts.push(instrucoes);
|
|
478
499
|
const geral = cfg.readGeneralMemory();
|
|
479
500
|
if (geral.trim()) parts.push(`[memory-geral.md]\n${geral}`);
|
|
480
501
|
const projeto = cfg.readProjectMemory(projectDir);
|
|
481
502
|
if (projeto.trim()) parts.push(`[${projectName || 'projeto'}/memory.md]\n${projeto}`);
|
|
482
|
-
const
|
|
483
|
-
|
|
503
|
+
const arquivos = arquivosDoProjeto(projectDir);
|
|
504
|
+
const mapa = mapaDoProjeto(projectDir, 80, arquivos);
|
|
505
|
+
if (mapa) parts.push(`[${projectName || 'projeto'}/arquivos]\n${mapa}\n\n${preLeitura.JUNTAR_LEITURAS}`);
|
|
506
|
+
const lidos = preLeitura.bloco(preLeitura.escolher(projectDir, arquivos, pedido));
|
|
507
|
+
if (lidos) parts.push(lidos);
|
|
484
508
|
return parts.join('\n\n');
|
|
485
509
|
}
|
|
486
510
|
|
|
@@ -493,6 +517,10 @@ async function runAct(userPrompt, opts) {
|
|
|
493
517
|
quem sabe lembrar é a SESSÃO — um subagente não pode começar do
|
|
494
518
|
zero e pedir de novo o que já foi autorizado. */
|
|
495
519
|
autorizar = null, controle = null,
|
|
520
|
+
/* O modo do /permissoes (auto · perguntar · livre). Vem da config pela
|
|
521
|
+
CLI e desce para os subagentes pelo ctx: eles recusam o perigoso,
|
|
522
|
+
mas só sabem que é "livre" se alguém disser. */
|
|
523
|
+
permissoes = 'auto',
|
|
496
524
|
/* O cliente HTTP, injetável. NÃO é mais um comutador de motor — o
|
|
497
525
|
PrimoCode tem um motor só, o do servidor, e não há provedor para o
|
|
498
526
|
usuário escolher. Continua um parâmetro porque é por aqui que o
|
|
@@ -509,6 +537,11 @@ async function runAct(userPrompt, opts) {
|
|
|
509
537
|
/* Quem abre a página para conferir antes do "pronto". Injetável pelo
|
|
510
538
|
mesmo motivo do `motor`: o teste mede a regra sem abrir um Chrome. */
|
|
511
539
|
medirPagina = (dir) => require('./pipeline.js').medirNoNavegador(dir),
|
|
540
|
+
/* Quem LIGA o navegador escondido antes da hora. Injetável pelo mesmo
|
|
541
|
+
motivo do `medirPagina` — e só vale junto com o padrão dele: um
|
|
542
|
+
teste que troca o medidor não pode subir um Chrome de verdade. */
|
|
543
|
+
aquecerPagina = opts && opts.medirPagina ? null
|
|
544
|
+
: () => require('./browser.js').aquecerRevisor(),
|
|
512
545
|
} = opts || {};
|
|
513
546
|
|
|
514
547
|
// A pasta de projeto só nasce quando o agente for mesmo MEXER EM ARQUIVO.
|
|
@@ -539,6 +572,7 @@ async function runAct(userPrompt, opts) {
|
|
|
539
572
|
let mexeuNaPagina = false; // mexeu desde a última conferência?
|
|
540
573
|
let conferencias = 0; // até duas: entregar, e conferir o conserto
|
|
541
574
|
let avisoDaPagina = ''; // o que ainda estava quebrado na última
|
|
575
|
+
let msConferindo = 0; // para o PRIMOCODE_TEMPOS separar as fases
|
|
542
576
|
/* Abre a página e devolve o relato quando é para VOLTAR (achou problema e
|
|
543
577
|
ainda cabe uma volta); null quando é para entregar. O que ficar
|
|
544
578
|
quebrado na última conferência fica em `avisoDaPagina`, e a entrega sai
|
|
@@ -549,10 +583,13 @@ async function runAct(userPrompt, opts) {
|
|
|
549
583
|
conferencias++;
|
|
550
584
|
mexeuNaPagina = false;
|
|
551
585
|
let medida = null;
|
|
586
|
+
const t = Date.now();
|
|
552
587
|
try {
|
|
553
588
|
fala.pensamento('abrindo a página para conferir antes de entregar', profundidade);
|
|
554
589
|
medida = await medirPagina(pastaAtual);
|
|
555
590
|
} catch { medida = null; }
|
|
591
|
+
msConferindo += Date.now() - t;
|
|
592
|
+
tempos.somar('conferir página', Date.now() - t);
|
|
556
593
|
if (!medida || !medida.testou) return null;
|
|
557
594
|
avisoDaPagina = medida.limpo ? ''
|
|
558
595
|
: String(medida.relato || '').split('\n').filter((l) => /·|BRANCO/.test(l)).slice(0, 3).join(' ');
|
|
@@ -563,6 +600,8 @@ async function runAct(userPrompt, opts) {
|
|
|
563
600
|
? `${texto}\n\nAtenção: abri a página e ela ainda tem problema — ${avisoDaPagina.trim()}` : texto);
|
|
564
601
|
// Quem já foi chamado, e quantas vezes. Ver lib/repeticao.js.
|
|
565
602
|
const jaChamado = repeticao.novo();
|
|
603
|
+
// O que read_file/list_dir devolveram, pela chave da época (repeticao.js).
|
|
604
|
+
const leiturasGuardadas = new Map();
|
|
566
605
|
/* O livro deste pedido: arquivos, comandos, links, testes e falhas. Sai
|
|
567
606
|
no fim como balanço (ver lib/registro.js). O subagente tem o seu, que
|
|
568
607
|
não se mistura com o do principal — mas o que ele fez também precisa
|
|
@@ -612,7 +651,7 @@ async function runAct(userPrompt, opts) {
|
|
|
612
651
|
|
|
613
652
|
const messages = [];
|
|
614
653
|
if (!subagent) {
|
|
615
|
-
const memory = buildMemoryContext(projectDir, projectName);
|
|
654
|
+
const memory = buildMemoryContext(projectDir, projectName, userPrompt);
|
|
616
655
|
if (memory) messages.push({ role: 'system', content: `Memória persistente:\n\n${memory}` });
|
|
617
656
|
for (const m of (history || []).slice(-20)) {
|
|
618
657
|
if (m && m.role && m.content) messages.push({ role: m.role, content: m.content });
|
|
@@ -724,6 +763,7 @@ async function runAct(userPrompt, opts) {
|
|
|
724
763
|
let fluxo = null;
|
|
725
764
|
|
|
726
765
|
emVoo = new AbortController();
|
|
766
|
+
const tRodada = Date.now();
|
|
727
767
|
try {
|
|
728
768
|
await motor.requestAct(server, { messages: compactar(messages), model, effort, token, projectDir: pastaAtual, projectName, subagent, sinal: emVoo.signal }, (evt) => {
|
|
729
769
|
if (evt.erro) { streamError = evt.erro; return; }
|
|
@@ -770,6 +810,7 @@ async function runAct(userPrompt, opts) {
|
|
|
770
810
|
}
|
|
771
811
|
sp.stop();
|
|
772
812
|
emVoo = null;
|
|
813
|
+
tempos.somar(subagent ? 'rodada do subagente' : 'rodada do modelo', Date.now() - tRodada);
|
|
773
814
|
if (PARAR) { if (fluxo) fluxo.fechar(); break; }
|
|
774
815
|
|
|
775
816
|
// Chamada de função malformada: o modelo errou a SINTAXE da chamada,
|
|
@@ -946,6 +987,8 @@ async function runAct(userPrompt, opts) {
|
|
|
946
987
|
|
|
947
988
|
let finished = null;
|
|
948
989
|
let algoRodou = false;
|
|
990
|
+
const tTools = Date.now();
|
|
991
|
+
const conferindoAntes = msConferindo;
|
|
949
992
|
|
|
950
993
|
// ORQUESTRAÇÃO: subagentes da MESMA rodada rodam juntos. O modelo que
|
|
951
994
|
// pede "3 agentes, um por arquivo" esperava paralelismo e recebia uma
|
|
@@ -962,7 +1005,7 @@ async function runAct(userPrompt, opts) {
|
|
|
962
1005
|
fala.paralelo(juntos.length, profundidade);
|
|
963
1006
|
const rs = await Promise.all(juntos.map((tc) =>
|
|
964
1007
|
runSubagent(tc.arguments || {}, {
|
|
965
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor,
|
|
1008
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes,
|
|
966
1009
|
}).catch((e) => `Subagente falhou: ${e && e.message}`)));
|
|
967
1010
|
juntos.forEach((tc, i) => resultadosSpawn.set(tc.id, rs[i]));
|
|
968
1011
|
}
|
|
@@ -1005,7 +1048,7 @@ async function runAct(userPrompt, opts) {
|
|
|
1005
1048
|
const r = resultadosSpawn.has(tc.id)
|
|
1006
1049
|
? resultadosSpawn.get(tc.id)
|
|
1007
1050
|
: await runSubagent(tc.arguments || {}, {
|
|
1008
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor,
|
|
1051
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes,
|
|
1009
1052
|
});
|
|
1010
1053
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
1011
1054
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: r });
|
|
@@ -1018,7 +1061,7 @@ async function runAct(userPrompt, opts) {
|
|
|
1018
1061
|
algoRodou = true;
|
|
1019
1062
|
fala.acao(tc.name, tc.arguments, profundidade);
|
|
1020
1063
|
const r = await rodarDiretor(tc.arguments || {}, {
|
|
1021
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor,
|
|
1064
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes,
|
|
1022
1065
|
});
|
|
1023
1066
|
logResult(tc.name, r, profundidade);
|
|
1024
1067
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(r) });
|
|
@@ -1036,7 +1079,7 @@ async function runAct(userPrompt, opts) {
|
|
|
1036
1079
|
garantirPasta();
|
|
1037
1080
|
fala.acao(tc.name, tc.arguments, profundidade);
|
|
1038
1081
|
const r = await rodarPipeline(tc.arguments || {}, {
|
|
1039
|
-
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, livro, autorizar, controle: portaoControle,
|
|
1082
|
+
server, model, token, projectDir: pastaAtual, projectName, profundidade, motor, permissoes, livro, autorizar, controle: portaoControle,
|
|
1040
1083
|
});
|
|
1041
1084
|
logResult(tc.name, r, profundidade);
|
|
1042
1085
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
@@ -1053,7 +1096,18 @@ async function runAct(userPrompt, opts) {
|
|
|
1053
1096
|
const eco = repeticao.ver(jaChamado, tc.name, tc.arguments);
|
|
1054
1097
|
if (eco.repetida) {
|
|
1055
1098
|
voltasEmFalso++;
|
|
1056
|
-
|
|
1099
|
+
/* RELER SEM MUDANÇA devolve o que já foi lido, uma vez.
|
|
1100
|
+
O aviso seco ("o resultado está na conversa acima") mentia
|
|
1101
|
+
depois do `compactar`: a leitura velha já tinha virado 300
|
|
1102
|
+
caracteres, e o modelo gastava OUTRA ida ao servidor para
|
|
1103
|
+
achar o arquivo por outro caminho (um `cat`, um grep). Na
|
|
1104
|
+
segunda vez vai o conteúdo guardado — ler de novo sem nada
|
|
1105
|
+
mudar dá o mesmo texto, então não há o que executar. Da
|
|
1106
|
+
terceira em diante é laço, e o freio segue como era. */
|
|
1107
|
+
const guardado = eco.vezes === 2 && leiturasGuardadas.get(eco.chave);
|
|
1108
|
+
messages.push({ role: 'tool', tool_call_id: tc.id, content: guardado
|
|
1109
|
+
? `(sem mudança desde a última leitura — é o mesmo conteúdo; não precisa ler de novo)\n${guardado}`
|
|
1110
|
+
: eco.aviso });
|
|
1057
1111
|
continue;
|
|
1058
1112
|
}
|
|
1059
1113
|
|
|
@@ -1080,6 +1134,7 @@ async function runAct(userPrompt, opts) {
|
|
|
1080
1134
|
const porta = await permissao.pedir(portaoControle, tc.name, tc.arguments, autorizar);
|
|
1081
1135
|
if (!porta.ok) {
|
|
1082
1136
|
fala.acao(tc.name, tc.arguments, profundidade);
|
|
1137
|
+
fala.avisar('resultado', { nome: tc.name, ok: false, resumo: 'não autorizado' });
|
|
1083
1138
|
console.log(fala.resultado(profundidade) + c.warn('não autorizado'));
|
|
1084
1139
|
registro.anotar(livro, tc.name, tc.arguments, { ok: false, error: porta.error });
|
|
1085
1140
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify({ ok: false, error: porta.error }) });
|
|
@@ -1087,12 +1142,40 @@ async function runAct(userPrompt, opts) {
|
|
|
1087
1142
|
}
|
|
1088
1143
|
}
|
|
1089
1144
|
|
|
1145
|
+
/* O PORTÃO do que não tem volta (lib/perigo.js). No mesmo lugar do
|
|
1146
|
+
portão do desktop e pelo mesmo motivo: antes, não depois. A
|
|
1147
|
+
resposta mora no mesmo objeto da sessão — quem lembra é ela. */
|
|
1148
|
+
if (!portaoControle.perigo) portaoControle.perigo = perigo.novo();
|
|
1149
|
+
const risco = await perigo.portao(portaoControle.perigo, {
|
|
1150
|
+
modo: permissoes, nome: tc.name, args: tc.arguments,
|
|
1151
|
+
projectDir: pastaAtual, subagent, perguntar: autorizar,
|
|
1152
|
+
});
|
|
1153
|
+
if (!risco.ok) {
|
|
1154
|
+
fala.acao(tc.name, tc.arguments, profundidade);
|
|
1155
|
+
fala.avisar('resultado', { nome: tc.name, ok: false, resumo: `não autorizado — ${risco.motivo}` });
|
|
1156
|
+
console.log(fala.resultado(profundidade) + c.warn(`não autorizado — ${risco.motivo}`));
|
|
1157
|
+
registro.anotar(livro, tc.name, tc.arguments, { ok: false, error: risco.error });
|
|
1158
|
+
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify({ ok: false, error: risco.error }) });
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1090
1162
|
algoRodou = true;
|
|
1091
1163
|
rodouTool = true;
|
|
1092
1164
|
fala.acao(tc.name, tc.arguments, profundidade);
|
|
1093
|
-
|
|
1165
|
+
/* Antes de mexer no arquivo, guarda o original — é o que o /desfazer
|
|
1166
|
+
devolve. Caminho inválido não é problema nosso: a tool recusa. */
|
|
1167
|
+
if (checkpoint.MUDAM_ARQUIVO.has(tc.name)) {
|
|
1168
|
+
try { checkpoint.antesDeMudar(tools.resolveInProject(tc.arguments && tc.arguments.path, pastaAtual), pastaAtual); } catch {}
|
|
1169
|
+
}
|
|
1170
|
+
/* O servidor, o token e o cliente da API vão junto: a legenda do
|
|
1171
|
+
estudio_video transcreve no servidor, e sem eles a ferramenta
|
|
1172
|
+
respondia "a transcrição precisa do servidor". */
|
|
1173
|
+
const result = await tools.execute(tc.name, tc.arguments, { projectDir: pastaAtual, server, token, api });
|
|
1094
1174
|
logResult(tc.name, result, profundidade);
|
|
1095
1175
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
1176
|
+
if (RELEITURA.has(tc.name) && result && result.ok !== false) {
|
|
1177
|
+
leiturasGuardadas.set(eco.chave, summarizeForModel(result));
|
|
1178
|
+
}
|
|
1096
1179
|
if (tc.name === 'write_todos' && result && Array.isArray(result.todos)) todosDaTarefa = result.todos;
|
|
1097
1180
|
// Além de narrar, ANOTA: no fim do pedido sai o balanço do que
|
|
1098
1181
|
// nasceu, mudou, rodou e falhou. Narrar sozinho não basta — numa
|
|
@@ -1101,6 +1184,19 @@ async function runAct(userPrompt, opts) {
|
|
|
1101
1184
|
if (result && result.ok !== false && catalogo.criaProjeto(tc.name, tc.arguments)
|
|
1102
1185
|
&& /\.(html?|css|m?jsx?|tsx?|vue|svelte)$/i.test(String((tc.arguments && tc.arguments.path) || ''))) {
|
|
1103
1186
|
mexeuNaPagina = true;
|
|
1187
|
+
/* O NAVEGADOR ESCONDIDO SOBE AGORA, e não no `finish`.
|
|
1188
|
+
A conferência só roda quando o agente diz "pronto", e até
|
|
1189
|
+
lá ainda há pelo menos uma rodada do modelo. Subir o Chrome
|
|
1190
|
+
ali, em paralelo com ela, tira do "pronto" o tempo de ligar
|
|
1191
|
+
o navegador e de fazer a PRIMEIRA navegação dele, que é a
|
|
1192
|
+
parte cara (ver `aquecerRevisor` em lib/browser.js). Medido
|
|
1193
|
+
em 24/09: a conferência num Chrome recém-aberto levava de
|
|
1194
|
+
2,1 a 4,6s; aquecido antes, 1,24s — que é o assentar de
|
|
1195
|
+
1,2s, e nada mais. Sem `await` e sem erro: se não subir, a
|
|
1196
|
+
conferência tenta de novo e diz o que houve. */
|
|
1197
|
+
if (aquecerPagina && !subagent && conferencias < 2) {
|
|
1198
|
+
try { Promise.resolve(aquecerPagina()).catch(() => {}); } catch { /* segue */ }
|
|
1199
|
+
}
|
|
1104
1200
|
}
|
|
1105
1201
|
|
|
1106
1202
|
// Uma escrita bem-sucedida limpa a dívida — mas só a do MESMO
|
|
@@ -1142,6 +1238,9 @@ async function runAct(userPrompt, opts) {
|
|
|
1142
1238
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: summarizeForModel(result) });
|
|
1143
1239
|
}
|
|
1144
1240
|
|
|
1241
|
+
// A conferência da página tem linha própria: aqui fica só o resto.
|
|
1242
|
+
tempos.somar('ferramentas', Date.now() - tTools - (msConferindo - conferindoAntes));
|
|
1243
|
+
|
|
1145
1244
|
// Uma volta inteira sem executar nada é o modelo preso. Insistir custa
|
|
1146
1245
|
// um pedido ao servidor por volta, e as 15 voltas saíam caras para
|
|
1147
1246
|
// terminar no mesmo lugar — melhor parar e devolver a vez ao usuário.
|
|
@@ -1232,7 +1331,7 @@ async function runAct(userPrompt, opts) {
|
|
|
1232
1331
|
}
|
|
1233
1332
|
|
|
1234
1333
|
async function runSubagent(args, ctx) {
|
|
1235
|
-
const { server, model, token, projectDir, projectName, profundidade, motor } = ctx;
|
|
1334
|
+
const { server, model, token, projectDir, projectName, profundidade, motor, permissoes } = ctx;
|
|
1236
1335
|
const descricao = args.description || 'sub-tarefa';
|
|
1237
1336
|
|
|
1238
1337
|
if (profundidade + 1 >= MAX_PROFUNDIDADE) {
|
|
@@ -1251,6 +1350,7 @@ async function runSubagent(args, ctx) {
|
|
|
1251
1350
|
profundidade: profundidade + 1,
|
|
1252
1351
|
subagent: true,
|
|
1253
1352
|
motor,
|
|
1353
|
+
permissoes,
|
|
1254
1354
|
autorizar: ctx.autorizar,
|
|
1255
1355
|
controle: ctx.controle, // o mesmo objeto: a resposta vale para todos
|
|
1256
1356
|
});
|
|
@@ -1294,7 +1394,7 @@ async function runSubagent(args, ctx) {
|
|
|
1294
1394
|
* inventar — e inventar é onde ele erra.
|
|
1295
1395
|
*/
|
|
1296
1396
|
async function rodarDiretor(args, ctx) {
|
|
1297
|
-
const { server, model, token, projectDir, projectName, profundidade, motor } = ctx;
|
|
1397
|
+
const { server, model, token, projectDir, projectName, profundidade, motor, permissoes } = ctx;
|
|
1298
1398
|
if (profundidade > 0) {
|
|
1299
1399
|
return { ok: false, error: 'Recusado: o diretor não roda dentro de um subagente. '
|
|
1300
1400
|
+ 'Produza com o plano que você já recebeu.' };
|
|
@@ -1305,7 +1405,7 @@ async function rodarDiretor(args, ctx) {
|
|
|
1305
1405
|
fala.agente('Diretor', rotulo, profundidade);
|
|
1306
1406
|
const r = await runAct(prompt, {
|
|
1307
1407
|
server, model, token, projectDir, projectName,
|
|
1308
|
-
history: [], profundidade: profundidade + 1, subagent: true, motor,
|
|
1408
|
+
history: [], profundidade: profundidade + 1, subagent: true, motor, permissoes,
|
|
1309
1409
|
});
|
|
1310
1410
|
return (r && r.assistantMessage && r.assistantMessage.content) || '';
|
|
1311
1411
|
};
|
|
@@ -1338,7 +1438,7 @@ async function rodarDiretor(args, ctx) {
|
|
|
1338
1438
|
}
|
|
1339
1439
|
|
|
1340
1440
|
async function rodarPipeline(args, ctx) {
|
|
1341
|
-
const { server, model, token, projectDir, projectName, profundidade, motor } = ctx;
|
|
1441
|
+
const { server, model, token, projectDir, projectName, profundidade, motor, permissoes } = ctx;
|
|
1342
1442
|
if (profundidade > 0) {
|
|
1343
1443
|
return { ok: false, error: 'Recusado: o pipeline não roda dentro de um subagente — '
|
|
1344
1444
|
+ 'os papéis dele já são subagentes. Faça a parte que te cabe.' };
|
|
@@ -1361,7 +1461,7 @@ async function rodarPipeline(args, ctx) {
|
|
|
1361
1461
|
fala.agente('Agente', rotulo, profundidade);
|
|
1362
1462
|
const r = await runAct(prompt, {
|
|
1363
1463
|
server, model, token, projectDir: pasta, projectName,
|
|
1364
|
-
history: [], profundidade: profundidade + 1, subagent: true, motor,
|
|
1464
|
+
history: [], profundidade: profundidade + 1, subagent: true, motor, permissoes,
|
|
1365
1465
|
});
|
|
1366
1466
|
return (r && r.assistantMessage && r.assistantMessage.content) || '';
|
|
1367
1467
|
};
|
|
@@ -1376,5 +1476,5 @@ async function rodarPipeline(args, ctx) {
|
|
|
1376
1476
|
});
|
|
1377
1477
|
}
|
|
1378
1478
|
|
|
1379
|
-
module.exports = { runAct, interromper, compactar, motivoDaParada, pareceParadaNoMeio, ehTetoDeToken, ehTransitorio, esperaDoErro,
|
|
1479
|
+
module.exports = { runAct, buildMemoryContext, interromper, compactar, motivoDaParada, pareceParadaNoMeio, ehTetoDeToken, ehTransitorio, esperaDoErro,
|
|
1380
1480
|
anotacaoDoQueJaFoi, encolherParaContinuar, MAX_ITERATIONS };
|
package/lib/api.js
CHANGED
|
@@ -201,14 +201,31 @@ function requestStream(serverUrl, endpoint, payload, onEvent, token, sinal) {
|
|
|
201
201
|
});
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
/* Com um provedor configurado (/api), estas quatro rotas rodam AQUI,
|
|
205
|
+
com a chave da pessoa, em vez de ir ao servidor — ver lib/local/motor.js.
|
|
206
|
+
O resto (login, transcrição, conectores) continua no servidor. */
|
|
207
|
+
function local() {
|
|
208
|
+
try {
|
|
209
|
+
const atual = require('./local/provedor.js').atual();
|
|
210
|
+
if (!atual) return null;
|
|
211
|
+
// Claude Code e Codex: a ferramenta de fora é o cérebro.
|
|
212
|
+
if (atual.cli) return require('./local/motor-cli.js').criar(atual.id);
|
|
213
|
+
return require('./local/motor.js');
|
|
214
|
+
} catch { return null; }
|
|
215
|
+
}
|
|
216
|
+
|
|
204
217
|
// Agente autônomo. `payload` carrega a conversa completa (messages).
|
|
205
218
|
function requestAct(serverUrl, payload, onEvent) {
|
|
219
|
+
const m = local();
|
|
220
|
+
if (m) return m.requestAct(serverUrl, payload, onEvent);
|
|
206
221
|
const { token, sinal, ...body } = payload;
|
|
207
222
|
return requestStream(serverUrl, '/api/act/stream', body, onEvent, token, sinal);
|
|
208
223
|
}
|
|
209
224
|
|
|
210
225
|
// Chat puro.
|
|
211
226
|
function requestChat(serverUrl, payload, onEvent) {
|
|
227
|
+
const m = local();
|
|
228
|
+
if (m) return m.requestChat(serverUrl, payload, onEvent);
|
|
212
229
|
const { token, sinal, ...body } = payload;
|
|
213
230
|
return requestStream(serverUrl, '/api/chat/stream', body, onEvent, token, sinal);
|
|
214
231
|
}
|
|
@@ -216,6 +233,8 @@ function requestChat(serverUrl, payload, onEvent) {
|
|
|
216
233
|
// Resumo incremental da conversa. Vai e volta pequeno: manda o resumo de
|
|
217
234
|
// antes + o que é novo, recebe o resumo atualizado.
|
|
218
235
|
function requestResumo(serverUrl, payload) {
|
|
236
|
+
const m = local();
|
|
237
|
+
if (m) return m.requestResumo(serverUrl, payload);
|
|
219
238
|
const { token, ...body } = payload;
|
|
220
239
|
return request(serverUrl, '/api/resumir', body, token);
|
|
221
240
|
}
|
|
@@ -236,6 +255,8 @@ function requestRefino(serverUrl, payload) {
|
|
|
236
255
|
|
|
237
256
|
// O plano: a tarefa grande dividida em passos, para uma chamada por pedaço.
|
|
238
257
|
function requestPlano(serverUrl, payload) {
|
|
258
|
+
const m = local();
|
|
259
|
+
if (m) return m.requestPlano(serverUrl, payload);
|
|
239
260
|
const { token, ...body } = payload;
|
|
240
261
|
return request(serverUrl, '/api/plano', body, token);
|
|
241
262
|
}
|