primocode 9.8.0-beta.5 → 9.8.0-beta.7
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 +15 -4
- package/app/ansi.js +55 -0
- package/app/ide.css +345 -0
- package/app/ide.html +183 -0
- package/app/ide.js +639 -0
- package/bin/primocode.js +338 -88
- package/kits/acolhedor/index.html +130 -0
- package/kits/acolhedor/script.js +25 -0
- package/kits/acolhedor/style.css +146 -0
- package/kits/editorial/index.html +115 -0
- package/kits/editorial/script.js +19 -0
- package/kits/editorial/style.css +112 -0
- package/kits/premium/index.html +83 -0
- package/kits/premium/script.js +11 -0
- package/kits/premium/style.css +87 -0
- package/kits/vitrine/index.html +108 -0
- package/kits/vitrine/script.js +20 -0
- package/kits/vitrine/style.css +117 -0
- package/lib/act.js +66 -7
- package/lib/app.js +41 -6
- package/lib/browser.js +79 -3
- package/lib/checkpoint-git.js +218 -0
- package/lib/checkpoint.js +159 -44
- package/lib/comandos-extra.js +481 -0
- package/lib/config.js +5 -2
- package/lib/ide.js +337 -0
- package/lib/kits.js +165 -54
- 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 +2 -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/documento-word/SKILL.md +57 -0
- package/skills/interface-revisada/SKILL.md +47 -0
- 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/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,11 @@
|
|
|
1
|
+
// Premium: um só movimento, na abertura. O arco sobe devagar quando a página
|
|
2
|
+
// carrega, e nada mais se mexe: animação em toda seção tira o ar de calma.
|
|
3
|
+
(function(){
|
|
4
|
+
var d=document;
|
|
5
|
+
d.documentElement.classList.add('js');
|
|
6
|
+
var arco=d.getElementById('arco');
|
|
7
|
+
if(!arco) return;
|
|
8
|
+
// Dois quadros de espera: garante que o navegador pintou o estado inicial
|
|
9
|
+
// antes de ligar a transição (senão ele pula direto para o fim).
|
|
10
|
+
requestAnimationFrame(function(){ requestAnimationFrame(function(){ arco.classList.add('pronto'); }); });
|
|
11
|
+
})();
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
:root{
|
|
2
|
+
/* ── A MARCA ───────────────────────────────────────────────────────
|
|
3
|
+
Para mudar a cara da página inteira, troque SÓ estas linhas:
|
|
4
|
+
--primaria a cor do arco e dos títulos (escura e sóbria)
|
|
5
|
+
--destaque o fio de luz no arco e os detalhes (um metal: latão,
|
|
6
|
+
cobre, prata)
|
|
7
|
+
--apoio as linhas finas e o fundo do depoimento
|
|
8
|
+
--fundo o fundo da página (claro e neutro)
|
|
9
|
+
--fonte-titulo / --fonte-texto as duas fontes (e o link do
|
|
10
|
+
Google Fonts no index.html)
|
|
11
|
+
O resto do arquivo usa estas variáveis: não precisa mexer nele. */
|
|
12
|
+
--primaria:#1f3a34;
|
|
13
|
+
--destaque:#b8955a;
|
|
14
|
+
--apoio:#dcd9d2;
|
|
15
|
+
--fundo:#f4f4f1;
|
|
16
|
+
--fonte-titulo:'Bodoni Moda',Georgia,serif;
|
|
17
|
+
--fonte-texto:'Manrope',system-ui,sans-serif;
|
|
18
|
+
|
|
19
|
+
--tinta:#1c1c1b;
|
|
20
|
+
--suave:#66655f;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Premium: muito espaço vazio e um único elemento marcante (o arco).
|
|
24
|
+
Tudo o mais é fino, calmo e centrado. O luxo aqui é o que NÃO tem. */
|
|
25
|
+
*{margin:0;padding:0;box-sizing:border-box;}
|
|
26
|
+
html{scroll-behavior:smooth;}
|
|
27
|
+
body{font-family:var(--fonte-texto);font-size:1.02rem;line-height:1.75;color:var(--tinta);background:var(--fundo);-webkit-font-smoothing:antialiased;}
|
|
28
|
+
a{color:inherit;}
|
|
29
|
+
:focus-visible{outline:2px solid var(--destaque);outline-offset:4px;}
|
|
30
|
+
h1,h2{font-family:var(--fonte-titulo);font-weight:400;line-height:1.12;color:var(--primaria);letter-spacing:-.01em;}
|
|
31
|
+
.pular{position:absolute;left:-9999px;top:8px;background:var(--tinta);color:var(--fundo);padding:8px 14px;z-index:10;}
|
|
32
|
+
.pular:focus{left:8px;}
|
|
33
|
+
|
|
34
|
+
.topo{display:flex;align-items:center;justify-content:space-between;padding:28px clamp(20px,5vw,64px);}
|
|
35
|
+
.marca{font-family:var(--fonte-titulo);font-size:1.6rem;font-weight:500;text-decoration:none;color:var(--primaria);}
|
|
36
|
+
.menu{display:flex;gap:32px;font-size:.95rem;}
|
|
37
|
+
.menu a{text-decoration:none;color:var(--suave);transition:color .3s;}
|
|
38
|
+
.menu a:hover{color:var(--tinta);}
|
|
39
|
+
|
|
40
|
+
.botao{display:inline-block;padding:14px 32px;border:1px solid var(--primaria);color:var(--primaria);font:500 .98rem var(--fonte-texto);text-decoration:none;letter-spacing:.01em;transition:background .35s,color .35s;}
|
|
41
|
+
.botao:hover{background:var(--primaria);color:var(--fundo);}
|
|
42
|
+
.botao-cheio{background:var(--primaria);color:var(--fundo);}
|
|
43
|
+
.botao-cheio:hover{background:transparent;color:var(--primaria);}
|
|
44
|
+
|
|
45
|
+
/* O arco: uma janela alta, com um fio de metal por dentro e um sol baixo */
|
|
46
|
+
.abertura{display:flex;flex-direction:column;align-items:center;text-align:center;padding:clamp(24px,5vw,56px) 24px clamp(90px,12vw,160px);}
|
|
47
|
+
.arco{position:relative;width:min(380px,70vw);aspect-ratio:3/4.2;border-radius:999px 999px 0 0;background:var(--primaria);overflow:hidden;margin-bottom:clamp(40px,6vw,72px);transform-origin:50% 100%;}
|
|
48
|
+
.arco::before{content:'';position:absolute;inset:18px 18px 0;border:1px solid var(--destaque);border-bottom:0;border-radius:999px 999px 0 0;opacity:.8;}
|
|
49
|
+
.arco-luz{position:absolute;left:50%;bottom:-18%;width:64%;aspect-ratio:1;transform:translateX(-50%);border-radius:50%;background:radial-gradient(circle at 50% 40%,var(--destaque),color-mix(in srgb,var(--destaque) 30%,var(--primaria)) 70%);opacity:.95;}
|
|
50
|
+
.abertura h1{font-size:clamp(2.2rem,4.8vw,4.1rem);max-width:18ch;margin-bottom:22px;}
|
|
51
|
+
.abertura-texto{color:var(--suave);font-size:1.1rem;margin-bottom:40px;}
|
|
52
|
+
|
|
53
|
+
/* Entrada: só o arco sobe, uma vez, ao abrir a página */
|
|
54
|
+
.js .arco{opacity:0;transform:scaleY(.86);}
|
|
55
|
+
.js .arco.pronto{opacity:1;transform:none;transition:opacity 1.4s ease,transform 1.6s cubic-bezier(.2,.7,.1,1);}
|
|
56
|
+
|
|
57
|
+
.manifesto{padding:clamp(80px,12vw,160px) 24px;border-top:1px solid var(--apoio);}
|
|
58
|
+
.manifesto p{max-width:30ch;margin:0 auto;text-align:center;font-family:var(--fonte-titulo);font-weight:400;font-size:clamp(1.5rem,2.7vw,2.2rem);line-height:1.4;color:var(--tinta);}
|
|
59
|
+
|
|
60
|
+
/* O trabalho: três colunas de texto com um fio em cima, sem caixa */
|
|
61
|
+
.trabalho{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(32px,5vw,80px);max-width:1160px;margin:0 auto;padding:clamp(60px,8vw,120px) clamp(24px,5vw,64px);}
|
|
62
|
+
.frente{border-top:1px solid var(--destaque);padding-top:28px;}
|
|
63
|
+
.frente h2{font-size:clamp(1.45rem,2vw,1.8rem);font-weight:400;margin-bottom:12px;}
|
|
64
|
+
.frente p{color:var(--suave);max-width:34ch;}
|
|
65
|
+
|
|
66
|
+
.depoimento{background:var(--apoio);padding:clamp(80px,11vw,150px) 24px;text-align:center;}
|
|
67
|
+
.depoimento p{font-family:var(--fonte-titulo);font-style:italic;font-weight:400;font-size:clamp(1.4rem,2.6vw,2.1rem);line-height:1.3;max-width:28ch;margin:0 auto;color:var(--primaria);}
|
|
68
|
+
.depoimento footer{margin-top:24px;color:var(--suave);font-size:.95rem;}
|
|
69
|
+
|
|
70
|
+
.contato{text-align:center;padding:clamp(90px,12vw,160px) 24px;}
|
|
71
|
+
.contato h2{font-size:clamp(2rem,4vw,3.2rem);margin-bottom:48px;}
|
|
72
|
+
.contato-grade{display:grid;grid-template-columns:repeat(3,auto);justify-content:center;gap:clamp(28px,6vw,96px);margin-bottom:56px;color:var(--suave);}
|
|
73
|
+
.contato-grade a{text-decoration:none;border-bottom:1px solid var(--apoio);transition:border-color .3s;}
|
|
74
|
+
.contato-grade a:hover{border-color:var(--destaque);}
|
|
75
|
+
|
|
76
|
+
.rodape{border-top:1px solid var(--apoio);padding:28px;text-align:center;font-size:.9rem;color:var(--suave);}
|
|
77
|
+
|
|
78
|
+
@media (max-width:760px){
|
|
79
|
+
.trabalho{grid-template-columns:1fr;}
|
|
80
|
+
.contato-grade{grid-template-columns:1fr;gap:20px;}
|
|
81
|
+
.menu{gap:20px;}
|
|
82
|
+
}
|
|
83
|
+
@media (prefers-reduced-motion:reduce){
|
|
84
|
+
html{scroll-behavior:auto;}
|
|
85
|
+
*{transition:none!important;}
|
|
86
|
+
.js .arco{opacity:1;transform:none;}
|
|
87
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang='pt-BR'>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset='UTF-8'>
|
|
5
|
+
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
|
6
|
+
<!-- TROQUE: título e descrição com o nome e a oferta -->
|
|
7
|
+
<title>Sua Marca, a oferta que chama</title>
|
|
8
|
+
<meta name='description' content='Uma frase que diz o que é, quando e onde.'>
|
|
9
|
+
<link rel='preconnect' href='https://fonts.googleapis.com'>
|
|
10
|
+
<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin>
|
|
11
|
+
<!-- TROQUE (se quiser): a fonte. Se trocar aqui, troque --fonte-titulo e --fonte-texto no topo do style.css -->
|
|
12
|
+
<link href='https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,600;12..96,800&display=swap' rel='stylesheet'>
|
|
13
|
+
<link rel='stylesheet' href='style.css'>
|
|
14
|
+
<script src='script.js' defer></script>
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
<a class='pular' href='#conteudo'>Pular para o conteúdo</a>
|
|
18
|
+
|
|
19
|
+
<!-- TROQUE: o nome e o link do pedido (WhatsApp, loja, ingresso) -->
|
|
20
|
+
<header class='topo'>
|
|
21
|
+
<a href='#' class='marca'>Sua Marca</a>
|
|
22
|
+
<a href='https://wa.me/5500000000000' class='botao botao-preto'>Quero o meu</a>
|
|
23
|
+
</header>
|
|
24
|
+
|
|
25
|
+
<main id='conteudo'>
|
|
26
|
+
<!-- TROQUE: o título curto e barulhento (poucas palavras), o selo e a frase de apoio -->
|
|
27
|
+
<section class='palco'>
|
|
28
|
+
<h1>Uma frase curta que dá fome.</h1>
|
|
29
|
+
<p class='selo'><span>Só nesta semana</span></p>
|
|
30
|
+
<div class='palco-base'>
|
|
31
|
+
<p class='palco-texto'>O que é, para quem é, e o motivo para pedir hoje. Duas frases, sem enrolar.</p>
|
|
32
|
+
<a href='https://wa.me/5500000000000' class='botao botao-preto'>Fazer meu pedido</a>
|
|
33
|
+
</div>
|
|
34
|
+
</section>
|
|
35
|
+
|
|
36
|
+
<!-- TROQUE: a data (no texto E no data-alvo, formato 2026-12-31T20:00), o lugar e o preço. Sem data? Apague o <p id='contagem'>. -->
|
|
37
|
+
<section class='quando' aria-label='Quando e onde'>
|
|
38
|
+
<div class='quando-data'>
|
|
39
|
+
<p class='quando-rotulo'>Quando</p>
|
|
40
|
+
<p class='quando-valor'>Sábado, 12 de dezembro</p>
|
|
41
|
+
<p id='contagem' class='contagem' data-alvo='2026-12-12T19:00'></p>
|
|
42
|
+
</div>
|
|
43
|
+
<div class='quando-lugar'>
|
|
44
|
+
<p class='quando-rotulo'>Onde</p>
|
|
45
|
+
<p class='quando-valor'>Rua Exemplo, 100, Sua Cidade</p>
|
|
46
|
+
</div>
|
|
47
|
+
<div class='quando-preco'>
|
|
48
|
+
<p class='quando-rotulo'>A partir de</p>
|
|
49
|
+
<p class='quando-valor'>R$ 00</p>
|
|
50
|
+
</div>
|
|
51
|
+
</section>
|
|
52
|
+
|
|
53
|
+
<!-- TROQUE: os produtos (ou atrações). O primeiro é a estrela e fica grande. Pode ter 4 a 6. -->
|
|
54
|
+
<section id='cardapio' class='mosaico' aria-label='O que tem'>
|
|
55
|
+
<article class='peca peca-estrela'>
|
|
56
|
+
<div class='arte arte-circulos' aria-hidden='true'></div>
|
|
57
|
+
<h2>O campeão da casa</h2>
|
|
58
|
+
<p>Por que todo mundo pede este. Uma frase que dá vontade.</p>
|
|
59
|
+
<p class='etiqueta'>R$ 00</p>
|
|
60
|
+
</article>
|
|
61
|
+
<article class='peca peca-azul'>
|
|
62
|
+
<div class='arte arte-meia' aria-hidden='true'></div>
|
|
63
|
+
<h3>Segundo item</h3>
|
|
64
|
+
<p>Uma frase curta e gostosa.</p>
|
|
65
|
+
<p class='etiqueta'>R$ 00</p>
|
|
66
|
+
</article>
|
|
67
|
+
<article class='peca peca-branca'>
|
|
68
|
+
<div class='arte arte-listras' aria-hidden='true'></div>
|
|
69
|
+
<h3>Terceiro item</h3>
|
|
70
|
+
<p>Uma frase curta e gostosa.</p>
|
|
71
|
+
<p class='etiqueta'>R$ 00</p>
|
|
72
|
+
</article>
|
|
73
|
+
<article class='peca peca-amarela'>
|
|
74
|
+
<div class='arte arte-gota' aria-hidden='true'></div>
|
|
75
|
+
<h3>Quarto item</h3>
|
|
76
|
+
<p>Uma frase curta e gostosa.</p>
|
|
77
|
+
<p class='etiqueta'>R$ 00</p>
|
|
78
|
+
</article>
|
|
79
|
+
<article class='peca peca-preta'>
|
|
80
|
+
<div class='arte arte-estrela' aria-hidden='true'></div>
|
|
81
|
+
<h3>Quinto item</h3>
|
|
82
|
+
<p>Uma frase curta e gostosa.</p>
|
|
83
|
+
<p class='etiqueta'>R$ 00</p>
|
|
84
|
+
</article>
|
|
85
|
+
</section>
|
|
86
|
+
|
|
87
|
+
<!-- TROQUE: como pedir, em três verbos (é uma sequência de verdade) e uma frase -->
|
|
88
|
+
<section class='passos'>
|
|
89
|
+
<p class='passos-verbos'>Escolha. <span>Chame.</span> Aproveite.</p>
|
|
90
|
+
<p class='passos-texto'>Uma frase que explica o caminho: onde pedir, em quanto tempo chega, como pagar.</p>
|
|
91
|
+
</section>
|
|
92
|
+
|
|
93
|
+
<!-- TROQUE: o último empurrão e os contatos -->
|
|
94
|
+
<section id='pedir' class='final'>
|
|
95
|
+
<h2>Bora?</h2>
|
|
96
|
+
<p>O convite final, com a urgência verdadeira (acaba quando?).</p>
|
|
97
|
+
<a href='https://wa.me/5500000000000' class='botao botao-preto'>Chamar no WhatsApp</a>
|
|
98
|
+
<p class='final-redes'><a href='https://instagram.com/'>@suamarca</a> no Instagram</p>
|
|
99
|
+
</section>
|
|
100
|
+
</main>
|
|
101
|
+
|
|
102
|
+
<!-- TROQUE: ano, nome e cidade -->
|
|
103
|
+
<footer class='rodape'>
|
|
104
|
+
<p>© 2026 Sua Marca, Sua Cidade</p>
|
|
105
|
+
<a href='#'>Voltar ao topo</a>
|
|
106
|
+
</footer>
|
|
107
|
+
</body>
|
|
108
|
+
</html>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Vitrine: a pressa de verdade é a data. O script conta quantos dias faltam
|
|
2
|
+
// para o data-alvo e escreve em palavras ("Faltam 12 dias"), em vez de um
|
|
3
|
+
// relógio piscando. Se a data passou ou está vazia, o selo some sozinho.
|
|
4
|
+
(function(){
|
|
5
|
+
var el=document.getElementById('contagem');
|
|
6
|
+
if(!el) return;
|
|
7
|
+
var alvo=new Date(el.getAttribute('data-alvo')||'');
|
|
8
|
+
if(isNaN(alvo)) { el.textContent=''; return; }
|
|
9
|
+
function escrever(){
|
|
10
|
+
var ms=alvo-new Date();
|
|
11
|
+
if(ms<=0){ el.textContent=''; return; }
|
|
12
|
+
var dias=Math.floor(ms/864e5), horas=Math.floor(ms/36e5);
|
|
13
|
+
if(dias>1) el.textContent='Faltam '+dias+' dias';
|
|
14
|
+
else if(dias===1) el.textContent='É amanhã';
|
|
15
|
+
else if(horas>=1) el.textContent='Faltam '+horas+(horas===1?' hora':' horas');
|
|
16
|
+
else el.textContent='É agora';
|
|
17
|
+
}
|
|
18
|
+
escrever();
|
|
19
|
+
setInterval(escrever,6e4);
|
|
20
|
+
})();
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
:root{
|
|
2
|
+
/* ── A MARCA ───────────────────────────────────────────────────────
|
|
3
|
+
Para mudar a cara da página inteira, troque SÓ estas linhas:
|
|
4
|
+
--primaria o bloco de abertura e a peça estrela
|
|
5
|
+
--destaque o selo, as etiquetas de preço e o bloco final
|
|
6
|
+
--apoio a faixa de data e as peças de cor
|
|
7
|
+
--fonte-titulo / --fonte-texto a fonte (e o link do Google
|
|
8
|
+
Fonts no index.html)
|
|
9
|
+
Cores vivas e fortes funcionam melhor aqui. O resto do arquivo usa
|
|
10
|
+
estas variáveis: não precisa mexer nele. */
|
|
11
|
+
--primaria:#ffd23f;
|
|
12
|
+
--destaque:#ff4f8b;
|
|
13
|
+
--apoio:#2d5bff;
|
|
14
|
+
--fonte-titulo:'Bricolage Grotesque',system-ui,sans-serif;
|
|
15
|
+
--fonte-texto:'Bricolage Grotesque',system-ui,sans-serif;
|
|
16
|
+
|
|
17
|
+
--tinta:#141414;
|
|
18
|
+
--branco:#ffffff;
|
|
19
|
+
--lado:clamp(16px,3vw,40px);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Vitrine: blocos de cor chapada, tipo enorme e contorno grosso, como
|
|
23
|
+
cartaz de rua. Nada de sombra suave nem vidro: tudo é plano e forte. */
|
|
24
|
+
*{margin:0;padding:0;box-sizing:border-box;}
|
|
25
|
+
html{scroll-behavior:smooth;}
|
|
26
|
+
body{font-family:var(--fonte-texto);font-size:1.1rem;line-height:1.5;color:var(--tinta);background:var(--branco);-webkit-font-smoothing:antialiased;overflow-x:hidden;}
|
|
27
|
+
a{color:inherit;}
|
|
28
|
+
:focus-visible{outline:4px solid var(--apoio);outline-offset:3px;}
|
|
29
|
+
h1,h2,h3{font-family:var(--fonte-titulo);font-weight:800;line-height:.95;letter-spacing:-.035em;}
|
|
30
|
+
.pular{position:absolute;left:-9999px;top:8px;background:var(--tinta);color:var(--branco);padding:8px 14px;z-index:10;}
|
|
31
|
+
.pular:focus{left:8px;}
|
|
32
|
+
|
|
33
|
+
.topo{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px var(--lado);background:var(--primaria);border-bottom:3px solid var(--tinta);}
|
|
34
|
+
.marca{font-family:var(--fonte-titulo);font-weight:800;font-size:1.6rem;letter-spacing:-.04em;text-decoration:none;}
|
|
35
|
+
|
|
36
|
+
.botao{display:inline-block;padding:14px 26px;border:3px solid var(--tinta);border-radius:14px;background:var(--branco);color:var(--tinta);font:800 1.05rem var(--fonte-titulo);text-decoration:none;box-shadow:5px 5px 0 var(--tinta);transition:transform .15s,box-shadow .15s;}
|
|
37
|
+
.botao:hover{transform:translate(-2px,-2px);box-shadow:7px 7px 0 var(--tinta);}
|
|
38
|
+
.botao:active{transform:translate(3px,3px);box-shadow:2px 2px 0 var(--tinta);}
|
|
39
|
+
.botao-preto{background:var(--tinta);color:var(--branco);box-shadow:5px 5px 0 var(--destaque);}
|
|
40
|
+
.botao-preto:hover{box-shadow:7px 7px 0 var(--destaque);}
|
|
41
|
+
.botao-preto:active{box-shadow:2px 2px 0 var(--destaque);}
|
|
42
|
+
|
|
43
|
+
/* Palco: o título ocupa a largura toda, e o selo gira no canto */
|
|
44
|
+
.palco{position:relative;background:var(--primaria);padding:clamp(40px,6vw,80px) var(--lado) clamp(48px,6vw,80px);border-bottom:3px solid var(--tinta);overflow:hidden;}
|
|
45
|
+
.palco h1{font-size:clamp(3.4rem,12.5vw,13rem);max-width:10ch;position:relative;z-index:1;}
|
|
46
|
+
.selo{position:absolute;right:clamp(16px,6vw,110px);top:clamp(24px,5vw,70px);width:clamp(120px,15vw,210px);aspect-ratio:1;display:grid;place-items:center;text-align:center;border-radius:50%;background:var(--destaque);font-family:var(--fonte-titulo);font-weight:800;font-size:clamp(1rem,1.6vw,1.45rem);line-height:1.05;transform:rotate(-12deg);z-index:2;
|
|
47
|
+
/* a borda serrilhada do selo: um polígono de 24 pontas */
|
|
48
|
+
clip-path:polygon(50% 0%,56.5% 7.4%,65.4% 3.4%,68.8% 12.6%,78.6% 12.4%,78.3% 22.1%,87.6% 25%,84.1% 34.1%,92.1% 39.8%,86.4% 47.6%,92.1% 55.8%,84.1% 61%,87.6% 70%,78.3% 73%,78.6% 82.7%,68.8% 82.4%,65.4% 91.6%,56.5% 87.6%,50% 95%,43.5% 87.6%,34.6% 91.6%,31.2% 82.4%,21.4% 82.7%,21.7% 73%,12.4% 70%,15.9% 61%,7.9% 55.8%,13.6% 47.6%,7.9% 39.8%,15.9% 34.1%,12.4% 25%,21.7% 22.1%,21.4% 12.4%,31.2% 12.6%,34.6% 3.4%,43.5% 7.4%);}
|
|
49
|
+
.selo span{max-width:8ch;}
|
|
50
|
+
.palco-base{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:24px;margin-top:clamp(28px,4vw,48px);}
|
|
51
|
+
.palco-texto{font-size:clamp(1.2rem,1.8vw,1.5rem);font-weight:600;max-width:34ch;}
|
|
52
|
+
|
|
53
|
+
/* Quando e onde: uma faixa de três colunas com filete preto entre elas */
|
|
54
|
+
.quando{display:grid;grid-template-columns:1.3fr 1.3fr 1fr;background:var(--apoio);color:var(--branco);border-bottom:3px solid var(--tinta);}
|
|
55
|
+
.quando>div{padding:28px var(--lado);}
|
|
56
|
+
.quando>div+div{border-left:3px solid var(--tinta);}
|
|
57
|
+
.quando-rotulo{font-weight:600;opacity:.85;}
|
|
58
|
+
.quando-valor{font-family:var(--fonte-titulo);font-weight:800;font-size:clamp(1.5rem,2.6vw,2.3rem);line-height:1.05;letter-spacing:-.02em;margin-top:4px;}
|
|
59
|
+
.contagem{margin-top:10px;display:inline-block;background:var(--primaria);color:var(--tinta);font-weight:800;padding:4px 12px;border-radius:999px;border:2px solid var(--tinta);}
|
|
60
|
+
.contagem:empty{display:none;}
|
|
61
|
+
|
|
62
|
+
/* Mosaico: peças de tamanhos e cores diferentes, e não cards iguais */
|
|
63
|
+
.mosaico{display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:minmax(250px,auto);gap:3px;background:var(--tinta);border-bottom:3px solid var(--tinta);}
|
|
64
|
+
.peca{position:relative;display:flex;flex-direction:column;justify-content:flex-end;gap:8px;padding:28px;overflow:hidden;background:var(--branco);}
|
|
65
|
+
.peca h2,.peca h3,.peca p{position:relative;z-index:1;}
|
|
66
|
+
.peca h3{font-size:clamp(1.6rem,2.4vw,2.2rem);}
|
|
67
|
+
.peca p{max-width:28ch;font-weight:600;}
|
|
68
|
+
.peca-estrela{grid-column:span 2;grid-row:span 2;background:var(--destaque);}
|
|
69
|
+
.peca-estrela h2{font-size:clamp(2.6rem,5.5vw,5.2rem);max-width:9ch;}
|
|
70
|
+
.peca-estrela p{font-size:1.25rem;}
|
|
71
|
+
.peca-azul{background:var(--apoio);color:var(--branco);}
|
|
72
|
+
.peca-amarela{background:var(--primaria);}
|
|
73
|
+
.peca-preta{background:var(--tinta);color:var(--branco);}
|
|
74
|
+
.etiqueta{position:absolute!important;top:22px;right:22px;background:var(--branco);color:var(--tinta);font-family:var(--fonte-titulo);font-weight:800;font-size:1.3rem;padding:6px 14px;border:3px solid var(--tinta);border-radius:10px;transform:rotate(4deg);}
|
|
75
|
+
.peca-estrela .etiqueta{font-size:2rem;padding:8px 18px;transform:rotate(-5deg);}
|
|
76
|
+
|
|
77
|
+
/* As artes: formas geométricas no lugar da foto (o pedido pode não ter foto) */
|
|
78
|
+
.arte{position:absolute;z-index:0;}
|
|
79
|
+
.arte-circulos{right:-8%;top:10%;width:62%;aspect-ratio:1;border-radius:50%;background:radial-gradient(circle,var(--primaria) 0 22%,transparent 22.5% 36%,var(--primaria) 36.5% 50%,transparent 50.5%);}
|
|
80
|
+
.arte-meia{right:-30px;top:-30px;width:170px;aspect-ratio:1;border-radius:50%;background:var(--primaria);}
|
|
81
|
+
.arte-listras{left:0;right:0;top:0;height:46%;background:repeating-linear-gradient(-45deg,var(--destaque) 0 16px,transparent 16px 32px);opacity:.9;}
|
|
82
|
+
.arte-gota{right:24px;top:70px;width:110px;aspect-ratio:1;border-radius:50% 0 50% 50%;background:var(--destaque);transform:rotate(-45deg);}
|
|
83
|
+
.arte-estrela{right:22px;top:64px;width:120px;aspect-ratio:1;background:var(--primaria);clip-path:polygon(50% 0,61% 35%,98% 35%,68% 57%,79% 91%,50% 70%,21% 91%,32% 57%,2% 35%,39% 35%);}
|
|
84
|
+
|
|
85
|
+
/* Passos: três verbos em uma linha enorme */
|
|
86
|
+
.passos{padding:clamp(56px,8vw,110px) var(--lado);border-bottom:3px solid var(--tinta);}
|
|
87
|
+
.passos-verbos{font-family:var(--fonte-titulo);font-weight:800;font-size:clamp(2.6rem,9vw,8.5rem);line-height:.95;letter-spacing:-.04em;}
|
|
88
|
+
.passos-verbos span{color:var(--apoio);}
|
|
89
|
+
.passos-texto{margin-top:24px;font-size:1.3rem;font-weight:600;max-width:40ch;}
|
|
90
|
+
|
|
91
|
+
.final{background:var(--destaque);padding:clamp(56px,8vw,110px) var(--lado);text-align:center;border-bottom:3px solid var(--tinta);}
|
|
92
|
+
.final h2{font-size:clamp(4.5rem,16vw,14rem);}
|
|
93
|
+
.final p{font-size:1.3rem;font-weight:600;margin:16px auto 28px;max-width:34ch;}
|
|
94
|
+
.final .botao-preto{box-shadow:5px 5px 0 var(--primaria);}
|
|
95
|
+
.final-redes{font-size:1.05rem!important;margin-top:28px!important;}
|
|
96
|
+
|
|
97
|
+
.rodape{display:flex;justify-content:space-between;gap:16px;padding:22px var(--lado);background:var(--tinta);color:var(--branco);font-weight:600;}
|
|
98
|
+
|
|
99
|
+
@media (max-width:960px){
|
|
100
|
+
.mosaico{grid-template-columns:1fr 1fr;}
|
|
101
|
+
.peca-estrela{grid-column:span 2;grid-row:span 1;min-height:380px;}
|
|
102
|
+
}
|
|
103
|
+
@media (max-width:640px){
|
|
104
|
+
.quando{grid-template-columns:1fr;}
|
|
105
|
+
.quando>div+div{border-left:0;border-top:3px solid var(--tinta);}
|
|
106
|
+
.mosaico{grid-template-columns:1fr;}
|
|
107
|
+
.peca-estrela,.peca-preta{grid-column:auto;}
|
|
108
|
+
.peca{min-height:230px;}
|
|
109
|
+
.peca-estrela{min-height:400px;padding-top:110px;}
|
|
110
|
+
.selo{position:relative;right:auto;top:auto;width:116px;margin-top:22px;}
|
|
111
|
+
.arte-circulos{width:46%;top:22%;right:-12%;}
|
|
112
|
+
.palco{padding-bottom:40px;}
|
|
113
|
+
.palco-base{flex-direction:column;align-items:flex-start;}
|
|
114
|
+
.rodape{flex-direction:column;}
|
|
115
|
+
.topo .botao{padding:10px 16px;font-size:.95rem;}
|
|
116
|
+
}
|
|
117
|
+
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto;}*{transition:none!important;}}
|
package/lib/act.js
CHANGED
|
@@ -24,6 +24,8 @@ const permissao = require('./permissao');
|
|
|
24
24
|
const perigo = require('./perigo');
|
|
25
25
|
const skills = require('./skills');
|
|
26
26
|
const checkpoint = require('./checkpoint');
|
|
27
|
+
const tempos = require('./tempos');
|
|
28
|
+
const preLeitura = require('./pre-leitura');
|
|
27
29
|
|
|
28
30
|
// 40, não 15: com o freio de repetição (repeticao.js) e o de rodada parada,
|
|
29
31
|
// volta a mais não é volta em círculo — é trabalho. Quinze acabavam no meio
|
|
@@ -35,6 +37,8 @@ const MAX_EMPURROES = 3;
|
|
|
35
37
|
// Voltas seguidas sem executar nada antes de desistir.
|
|
36
38
|
const MAX_RODADAS_PARADAS = 3;
|
|
37
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']);
|
|
38
42
|
|
|
39
43
|
// ── Interrupção (Esc Esc) ─────────────────────────────────────────────────
|
|
40
44
|
// O usuário aperta Esc duas vezes e o agente PARA — entre uma rodada e outra
|
|
@@ -382,6 +386,7 @@ async function pausar(ms, subagent) {
|
|
|
382
386
|
const sp = subagent ? null : new Spinner(PENSANDO).start();
|
|
383
387
|
await new Promise((r) => setTimeout(r, ms));
|
|
384
388
|
if (sp) sp.stop();
|
|
389
|
+
tempos.somar('espera de cota/rede', ms);
|
|
385
390
|
}
|
|
386
391
|
|
|
387
392
|
/**
|
|
@@ -451,10 +456,10 @@ function motivoDaParada({ voltas, voltasEmFalso, falhasPendentes, travou }) {
|
|
|
451
456
|
const PULAR_NO_MAPA = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.nuxt', 'out',
|
|
452
457
|
'.cache', 'coverage', '__pycache__', '.venv', 'venv', '.turbo', '.vercel', '.idea', '.vscode']);
|
|
453
458
|
|
|
454
|
-
function
|
|
459
|
+
function arquivosDoProjeto(dir, limite = 80) {
|
|
455
460
|
const path = require('path');
|
|
456
461
|
const fs = require('fs');
|
|
457
|
-
if (!dir || path.resolve(dir) === path.resolve(cfg.ROOT_DIR)) return
|
|
462
|
+
if (!dir || path.resolve(dir) === path.resolve(cfg.ROOT_DIR)) return [];
|
|
458
463
|
const achados = [];
|
|
459
464
|
const andar = (atual, fundo) => {
|
|
460
465
|
if (fundo > 4 || achados.length >= limite) return;
|
|
@@ -470,12 +475,18 @@ function mapaDoProjeto(dir, limite = 80) {
|
|
|
470
475
|
}
|
|
471
476
|
};
|
|
472
477
|
andar(dir, 0);
|
|
478
|
+
return achados;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function mapaDoProjeto(dir, limite = 80, achados = arquivosDoProjeto(dir, limite)) {
|
|
473
482
|
if (!achados.length) return '';
|
|
474
483
|
const mais = achados.length >= limite ? `\n… (mais arquivos — use glob para achar)` : '';
|
|
475
484
|
return `Arquivos que JÁ existem neste projeto (edite-os em vez de recriar):\n${achados.join('\n')}${mais}`;
|
|
476
485
|
}
|
|
477
486
|
|
|
478
|
-
|
|
487
|
+
/* `pedido` é opcional: com ele, o arquivo citado no pedido é o primeiro a
|
|
488
|
+
entrar já lido (ver lib/pre-leitura.js). */
|
|
489
|
+
function buildMemoryContext(projectDir, projectName, pedido = '') {
|
|
479
490
|
const parts = [];
|
|
480
491
|
// As instruções vêm primeiro: são regra de quem manda no projeto, e a
|
|
481
492
|
// memória é o que o agente anotou. Entram aqui (e não no bin) para valer
|
|
@@ -486,8 +497,11 @@ function buildMemoryContext(projectDir, projectName) {
|
|
|
486
497
|
if (geral.trim()) parts.push(`[memory-geral.md]\n${geral}`);
|
|
487
498
|
const projeto = cfg.readProjectMemory(projectDir);
|
|
488
499
|
if (projeto.trim()) parts.push(`[${projectName || 'projeto'}/memory.md]\n${projeto}`);
|
|
489
|
-
const
|
|
490
|
-
|
|
500
|
+
const arquivos = arquivosDoProjeto(projectDir);
|
|
501
|
+
const mapa = mapaDoProjeto(projectDir, 80, arquivos);
|
|
502
|
+
if (mapa) parts.push(`[${projectName || 'projeto'}/arquivos]\n${mapa}\n\n${preLeitura.JUNTAR_LEITURAS}`);
|
|
503
|
+
const lidos = preLeitura.bloco(preLeitura.escolher(projectDir, arquivos, pedido));
|
|
504
|
+
if (lidos) parts.push(lidos);
|
|
491
505
|
return parts.join('\n\n');
|
|
492
506
|
}
|
|
493
507
|
|
|
@@ -520,6 +534,11 @@ async function runAct(userPrompt, opts) {
|
|
|
520
534
|
/* Quem abre a página para conferir antes do "pronto". Injetável pelo
|
|
521
535
|
mesmo motivo do `motor`: o teste mede a regra sem abrir um Chrome. */
|
|
522
536
|
medirPagina = (dir) => require('./pipeline.js').medirNoNavegador(dir),
|
|
537
|
+
/* Quem LIGA o navegador escondido antes da hora. Injetável pelo mesmo
|
|
538
|
+
motivo do `medirPagina` — e só vale junto com o padrão dele: um
|
|
539
|
+
teste que troca o medidor não pode subir um Chrome de verdade. */
|
|
540
|
+
aquecerPagina = opts && opts.medirPagina ? null
|
|
541
|
+
: () => require('./browser.js').aquecerRevisor(),
|
|
523
542
|
} = opts || {};
|
|
524
543
|
|
|
525
544
|
// A pasta de projeto só nasce quando o agente for mesmo MEXER EM ARQUIVO.
|
|
@@ -550,6 +569,7 @@ async function runAct(userPrompt, opts) {
|
|
|
550
569
|
let mexeuNaPagina = false; // mexeu desde a última conferência?
|
|
551
570
|
let conferencias = 0; // até duas: entregar, e conferir o conserto
|
|
552
571
|
let avisoDaPagina = ''; // o que ainda estava quebrado na última
|
|
572
|
+
let msConferindo = 0; // para o PRIMOCODE_TEMPOS separar as fases
|
|
553
573
|
/* Abre a página e devolve o relato quando é para VOLTAR (achou problema e
|
|
554
574
|
ainda cabe uma volta); null quando é para entregar. O que ficar
|
|
555
575
|
quebrado na última conferência fica em `avisoDaPagina`, e a entrega sai
|
|
@@ -560,10 +580,13 @@ async function runAct(userPrompt, opts) {
|
|
|
560
580
|
conferencias++;
|
|
561
581
|
mexeuNaPagina = false;
|
|
562
582
|
let medida = null;
|
|
583
|
+
const t = Date.now();
|
|
563
584
|
try {
|
|
564
585
|
fala.pensamento('abrindo a página para conferir antes de entregar', profundidade);
|
|
565
586
|
medida = await medirPagina(pastaAtual);
|
|
566
587
|
} catch { medida = null; }
|
|
588
|
+
msConferindo += Date.now() - t;
|
|
589
|
+
tempos.somar('conferir página', Date.now() - t);
|
|
567
590
|
if (!medida || !medida.testou) return null;
|
|
568
591
|
avisoDaPagina = medida.limpo ? ''
|
|
569
592
|
: String(medida.relato || '').split('\n').filter((l) => /·|BRANCO/.test(l)).slice(0, 3).join(' ');
|
|
@@ -574,6 +597,8 @@ async function runAct(userPrompt, opts) {
|
|
|
574
597
|
? `${texto}\n\nAtenção: abri a página e ela ainda tem problema — ${avisoDaPagina.trim()}` : texto);
|
|
575
598
|
// Quem já foi chamado, e quantas vezes. Ver lib/repeticao.js.
|
|
576
599
|
const jaChamado = repeticao.novo();
|
|
600
|
+
// O que read_file/list_dir devolveram, pela chave da época (repeticao.js).
|
|
601
|
+
const leiturasGuardadas = new Map();
|
|
577
602
|
/* O livro deste pedido: arquivos, comandos, links, testes e falhas. Sai
|
|
578
603
|
no fim como balanço (ver lib/registro.js). O subagente tem o seu, que
|
|
579
604
|
não se mistura com o do principal — mas o que ele fez também precisa
|
|
@@ -623,7 +648,7 @@ async function runAct(userPrompt, opts) {
|
|
|
623
648
|
|
|
624
649
|
const messages = [];
|
|
625
650
|
if (!subagent) {
|
|
626
|
-
const memory = buildMemoryContext(projectDir, projectName);
|
|
651
|
+
const memory = buildMemoryContext(projectDir, projectName, userPrompt);
|
|
627
652
|
if (memory) messages.push({ role: 'system', content: `Memória persistente:\n\n${memory}` });
|
|
628
653
|
for (const m of (history || []).slice(-20)) {
|
|
629
654
|
if (m && m.role && m.content) messages.push({ role: m.role, content: m.content });
|
|
@@ -735,6 +760,7 @@ async function runAct(userPrompt, opts) {
|
|
|
735
760
|
let fluxo = null;
|
|
736
761
|
|
|
737
762
|
emVoo = new AbortController();
|
|
763
|
+
const tRodada = Date.now();
|
|
738
764
|
try {
|
|
739
765
|
await motor.requestAct(server, { messages: compactar(messages), model, effort, token, projectDir: pastaAtual, projectName, subagent, sinal: emVoo.signal }, (evt) => {
|
|
740
766
|
if (evt.erro) { streamError = evt.erro; return; }
|
|
@@ -781,6 +807,7 @@ async function runAct(userPrompt, opts) {
|
|
|
781
807
|
}
|
|
782
808
|
sp.stop();
|
|
783
809
|
emVoo = null;
|
|
810
|
+
tempos.somar(subagent ? 'rodada do subagente' : 'rodada do modelo', Date.now() - tRodada);
|
|
784
811
|
if (PARAR) { if (fluxo) fluxo.fechar(); break; }
|
|
785
812
|
|
|
786
813
|
// Chamada de função malformada: o modelo errou a SINTAXE da chamada,
|
|
@@ -957,6 +984,8 @@ async function runAct(userPrompt, opts) {
|
|
|
957
984
|
|
|
958
985
|
let finished = null;
|
|
959
986
|
let algoRodou = false;
|
|
987
|
+
const tTools = Date.now();
|
|
988
|
+
const conferindoAntes = msConferindo;
|
|
960
989
|
|
|
961
990
|
// ORQUESTRAÇÃO: subagentes da MESMA rodada rodam juntos. O modelo que
|
|
962
991
|
// pede "3 agentes, um por arquivo" esperava paralelismo e recebia uma
|
|
@@ -1064,7 +1093,18 @@ async function runAct(userPrompt, opts) {
|
|
|
1064
1093
|
const eco = repeticao.ver(jaChamado, tc.name, tc.arguments);
|
|
1065
1094
|
if (eco.repetida) {
|
|
1066
1095
|
voltasEmFalso++;
|
|
1067
|
-
|
|
1096
|
+
/* RELER SEM MUDANÇA devolve o que já foi lido, uma vez.
|
|
1097
|
+
O aviso seco ("o resultado está na conversa acima") mentia
|
|
1098
|
+
depois do `compactar`: a leitura velha já tinha virado 300
|
|
1099
|
+
caracteres, e o modelo gastava OUTRA ida ao servidor para
|
|
1100
|
+
achar o arquivo por outro caminho (um `cat`, um grep). Na
|
|
1101
|
+
segunda vez vai o conteúdo guardado — ler de novo sem nada
|
|
1102
|
+
mudar dá o mesmo texto, então não há o que executar. Da
|
|
1103
|
+
terceira em diante é laço, e o freio segue como era. */
|
|
1104
|
+
const guardado = eco.vezes === 2 && leiturasGuardadas.get(eco.chave);
|
|
1105
|
+
messages.push({ role: 'tool', tool_call_id: tc.id, content: guardado
|
|
1106
|
+
? `(sem mudança desde a última leitura — é o mesmo conteúdo; não precisa ler de novo)\n${guardado}`
|
|
1107
|
+
: eco.aviso });
|
|
1068
1108
|
continue;
|
|
1069
1109
|
}
|
|
1070
1110
|
|
|
@@ -1125,6 +1165,9 @@ async function runAct(userPrompt, opts) {
|
|
|
1125
1165
|
const result = await tools.execute(tc.name, tc.arguments, { projectDir: pastaAtual });
|
|
1126
1166
|
logResult(tc.name, result, profundidade);
|
|
1127
1167
|
repeticao.executou(jaChamado, tc.name, tc.arguments);
|
|
1168
|
+
if (RELEITURA.has(tc.name) && result && result.ok !== false) {
|
|
1169
|
+
leiturasGuardadas.set(eco.chave, summarizeForModel(result));
|
|
1170
|
+
}
|
|
1128
1171
|
if (tc.name === 'write_todos' && result && Array.isArray(result.todos)) todosDaTarefa = result.todos;
|
|
1129
1172
|
// Além de narrar, ANOTA: no fim do pedido sai o balanço do que
|
|
1130
1173
|
// nasceu, mudou, rodou e falhou. Narrar sozinho não basta — numa
|
|
@@ -1133,6 +1176,19 @@ async function runAct(userPrompt, opts) {
|
|
|
1133
1176
|
if (result && result.ok !== false && catalogo.criaProjeto(tc.name, tc.arguments)
|
|
1134
1177
|
&& /\.(html?|css|m?jsx?|tsx?|vue|svelte)$/i.test(String((tc.arguments && tc.arguments.path) || ''))) {
|
|
1135
1178
|
mexeuNaPagina = true;
|
|
1179
|
+
/* O NAVEGADOR ESCONDIDO SOBE AGORA, e não no `finish`.
|
|
1180
|
+
A conferência só roda quando o agente diz "pronto", e até
|
|
1181
|
+
lá ainda há pelo menos uma rodada do modelo. Subir o Chrome
|
|
1182
|
+
ali, em paralelo com ela, tira do "pronto" o tempo de ligar
|
|
1183
|
+
o navegador e de fazer a PRIMEIRA navegação dele, que é a
|
|
1184
|
+
parte cara (ver `aquecerRevisor` em lib/browser.js). Medido
|
|
1185
|
+
em 24/09: a conferência num Chrome recém-aberto levava de
|
|
1186
|
+
2,1 a 4,6s; aquecido antes, 1,24s — que é o assentar de
|
|
1187
|
+
1,2s, e nada mais. Sem `await` e sem erro: se não subir, a
|
|
1188
|
+
conferência tenta de novo e diz o que houve. */
|
|
1189
|
+
if (aquecerPagina && !subagent && conferencias < 2) {
|
|
1190
|
+
try { Promise.resolve(aquecerPagina()).catch(() => {}); } catch { /* segue */ }
|
|
1191
|
+
}
|
|
1136
1192
|
}
|
|
1137
1193
|
|
|
1138
1194
|
// Uma escrita bem-sucedida limpa a dívida — mas só a do MESMO
|
|
@@ -1174,6 +1230,9 @@ async function runAct(userPrompt, opts) {
|
|
|
1174
1230
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: summarizeForModel(result) });
|
|
1175
1231
|
}
|
|
1176
1232
|
|
|
1233
|
+
// A conferência da página tem linha própria: aqui fica só o resto.
|
|
1234
|
+
tempos.somar('ferramentas', Date.now() - tTools - (msConferindo - conferindoAntes));
|
|
1235
|
+
|
|
1177
1236
|
// Uma volta inteira sem executar nada é o modelo preso. Insistir custa
|
|
1178
1237
|
// um pedido ao servidor por volta, e as 15 voltas saíam caras para
|
|
1179
1238
|
// terminar no mesmo lugar — melhor parar e devolver a vez ao usuário.
|