chocolatito-code 1.2.0 → 1.3.1
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/dist/agent/huecos.d.ts +58 -0
- package/dist/agent/huecos.js +81 -0
- package/dist/agent/loop.js +144 -24
- package/dist/agent/salidaDelAgente.js +10 -24
- package/dist/agent/toolGate.js +6 -1
- package/dist/config/engine.d.ts +21 -1
- package/dist/config/engine.js +22 -1
- package/dist/config/quota.d.ts +11 -0
- package/dist/config/quota.js +18 -0
- package/dist/index.js +35 -26
- package/dist/prompts/systemPrompt.js +186 -150
- package/dist/tools/browserExtension.js +118 -3
- package/dist/tools/computerUse.d.ts +1 -1
- package/dist/tools/computerUse.js +183 -2
- package/dist/tools/toolDefsComputer.js +6 -2
- package/dist/tools/visionBridge.js +60 -3
- package/dist/tools/win/hostScript.js +154 -0
- package/dist/ui/banner.d.ts +31 -0
- package/dist/ui/banner.js +45 -0
- package/dist/ui/comandos.d.ts +33 -0
- package/dist/ui/comandos.js +49 -0
- package/dist/ui/informeDeUso.d.ts +17 -0
- package/dist/ui/informeDeUso.js +81 -0
- package/dist/ui/interrupt.d.ts +10 -0
- package/dist/ui/interrupt.js +50 -2
- package/dist/ui/marco.d.ts +35 -0
- package/dist/ui/marco.js +161 -0
- package/dist/ui/pieFijo.d.ts +56 -0
- package/dist/ui/pieFijo.js +309 -0
- package/dist/ui/prompt.d.ts +4 -41
- package/dist/ui/prompt.js +24 -157
- package/dist/ui/reasoningStream.js +22 -12
- package/dist/ui/renderer.d.ts +13 -0
- package/dist/ui/renderer.js +41 -12
- package/dist/ui/spinner.d.ts +43 -8
- package/dist/ui/spinner.js +118 -43
- package/extension/background.js +9 -3
- package/extension/iconos/128.png +0 -0
- package/extension/iconos/16.png +0 -0
- package/extension/iconos/32.png +0 -0
- package/extension/iconos/48.png +0 -0
- package/extension/iconos/ORIGEN.md +48 -0
- package/extension/manifest.json +60 -60
- package/package.json +67 -67
package/dist/index.js
CHANGED
|
@@ -5,7 +5,8 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import { ensureApiKey } from "./config/env.js";
|
|
7
7
|
import { ensureLicense, licenseStatus, deactivate, getStoredLicense } from "./config/license.js";
|
|
8
|
-
import { isManagedMode } from "./config/quota.js";
|
|
8
|
+
import { isManagedMode, checkQuota, ventanaVaciaEn } from "./config/quota.js";
|
|
9
|
+
import { construirInformeDeUso } from "./ui/informeDeUso.js";
|
|
9
10
|
import { getEngineBaseUrl, DEFAULT_ESPECTRO, ESPECTRO_MODELS, RULES_FILE_NAME } from "./config/constants.js";
|
|
10
11
|
import { getProjectContext } from "./agent/context.js";
|
|
11
12
|
import { buildSystemPrompt } from "./prompts/systemPrompt.js";
|
|
@@ -184,8 +185,8 @@ async function main() {
|
|
|
184
185
|
// errores raros dentro de una dependencia. Ver config/nodeVersion.ts.
|
|
185
186
|
const nodeViejo = comprobarNode();
|
|
186
187
|
if (nodeViejo) {
|
|
187
|
-
console.error(chalk.red(`
|
|
188
|
-
${nodeViejo}
|
|
188
|
+
console.error(chalk.red(`
|
|
189
|
+
${nodeViejo}
|
|
189
190
|
`));
|
|
190
191
|
process.exit(EXIT_USO);
|
|
191
192
|
}
|
|
@@ -399,15 +400,15 @@ ${nodeViejo}
|
|
|
399
400
|
.filter((m) => m.role === "user" || m.role === "assistant")
|
|
400
401
|
.map((m) => `### 👤 ${m.role === "user" ? "Usuario" : "Chocolatito"}\n\n${typeof m.content === "string" ? m.content : JSON.stringify(m.content, null, 2)}\n`)
|
|
401
402
|
.join("\n---\n\n");
|
|
402
|
-
const mdContent = `# 🦊 Bitácora de Sesión — Chocolatito Code
|
|
403
|
-
**Fecha:** ${new Date().toLocaleString()}
|
|
404
|
-
**Directorio:** \`${agent.currentCwd}\`
|
|
405
|
-
**Motor:** ${currentModel.name}
|
|
406
|
-
**Inversión Total:** ${agent.tracker.calculateCost(undefined, undefined, undefined, currentModel.underlyingModel).formattedCost}
|
|
407
|
-
|
|
408
|
-
---
|
|
409
|
-
|
|
410
|
-
${sessionData}
|
|
403
|
+
const mdContent = `# 🦊 Bitácora de Sesión — Chocolatito Code
|
|
404
|
+
**Fecha:** ${new Date().toLocaleString()}
|
|
405
|
+
**Directorio:** \`${agent.currentCwd}\`
|
|
406
|
+
**Motor:** ${currentModel.name}
|
|
407
|
+
**Inversión Total:** ${agent.tracker.calculateCost(undefined, undefined, undefined, currentModel.underlyingModel).formattedCost}
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
${sessionData}
|
|
411
412
|
`;
|
|
412
413
|
fs.writeFileSync(exportPath, mdContent, "utf-8");
|
|
413
414
|
console.log(chalk.green(`\n✔ Bitácora exportada exitosamente en: ${chalk.bold.white(exportPath)}\n`));
|
|
@@ -500,12 +501,12 @@ ${sessionData}
|
|
|
500
501
|
}
|
|
501
502
|
console.log(chalk.bold.hex("#A855F7")("\n🎯 INICIANDO MODO META AUTÓNOMA (GOAL MODE):"));
|
|
502
503
|
console.log(chalk.gray(` Objetivo: "${goalText}"\n`));
|
|
503
|
-
const autonomousPrompt = `[MODO META AUTÓNOMA ACTIVADO]
|
|
504
|
-
Tu objetivo principal es: "${goalText}"
|
|
505
|
-
Directivas de ejecución:
|
|
506
|
-
1. Formula un plan de ejecución mental paso a paso.
|
|
507
|
-
2. Ejecuta cada paso utilizando las herramientas correspondientes (creación, edición, ejecución, verificación).
|
|
508
|
-
3. Si algo falla o hay errores, autocorregir inmediatamente y volver a probar.
|
|
504
|
+
const autonomousPrompt = `[MODO META AUTÓNOMA ACTIVADO]
|
|
505
|
+
Tu objetivo principal es: "${goalText}"
|
|
506
|
+
Directivas de ejecución:
|
|
507
|
+
1. Formula un plan de ejecución mental paso a paso.
|
|
508
|
+
2. Ejecuta cada paso utilizando las herramientas correspondientes (creación, edición, ejecución, verificación).
|
|
509
|
+
3. Si algo falla o hay errores, autocorregir inmediatamente y volver a probar.
|
|
509
510
|
4. No te detengas hasta verificar que el objetivo esté 100% completado con éxito.`;
|
|
510
511
|
renderUserPrompt(`/goal ${goalText}`);
|
|
511
512
|
await agent.runTask(autonomousPrompt);
|
|
@@ -559,14 +560,22 @@ Directivas de ejecución:
|
|
|
559
560
|
}
|
|
560
561
|
continue;
|
|
561
562
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
563
|
+
// "/cost" sigue funcionando sin anunciarse: el nombre viejo esta en la
|
|
564
|
+
// memoria de los dedos de quien ya lo usaba, y quitarlo de golpe solo
|
|
565
|
+
// ensena un "comando desconocido" a quien no ha hecho nada mal.
|
|
566
|
+
if (trimmed === "/uso" || trimmed === "/cost") {
|
|
567
|
+
// Lo que se ensena es la CUENTA, no la tarifa: quien paga una suscripcion
|
|
568
|
+
// plana no puede hacer nada con "$0.00508", y lo unico que necesita saber
|
|
569
|
+
// es cuanto le queda antes de que esto le pare. Con clave propia es al
|
|
570
|
+
// reves -el dinero es suyo- y el informe lo tiene en cuenta.
|
|
571
|
+
const { formattedCost } = agent.tracker.calculateCost(undefined, undefined, undefined, currentModel.underlyingModel);
|
|
572
|
+
console.log(construirInformeDeUso(checkQuota(), {
|
|
573
|
+
motor: currentModel.name,
|
|
574
|
+
tokensEntrada: agent.tracker.totalPromptTokens,
|
|
575
|
+
tokensSalida: agent.tracker.totalCompletionTokens,
|
|
576
|
+
tokensEnCache: agent.tracker.cachedPromptTokens,
|
|
577
|
+
costeFormateado: formattedCost,
|
|
578
|
+
}, ventanaVaciaEn()));
|
|
570
579
|
continue;
|
|
571
580
|
}
|
|
572
581
|
if (trimmed.startsWith("/effort") || trimmed.startsWith("/espectro") || trimmed === "/model") {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CONTEXT_WINDOW } from "../config/constants.js";
|
|
2
|
+
import { listaParaElModelo } from "../ui/comandos.js";
|
|
2
3
|
/**
|
|
3
4
|
* Fecha real de la maquina.
|
|
4
5
|
*
|
|
@@ -6,6 +7,19 @@ import { CONTEXT_WINDOW } from "../config/constants.js";
|
|
|
6
7
|
* "© 2025" en un PDF generado en 2026, y el usuario no tiene por qué revisar
|
|
7
8
|
* cada pie de página para cazar la fecha inventada.
|
|
8
9
|
*/
|
|
10
|
+
/**
|
|
11
|
+
* Los comandos de barra, sangrados como el resto del bloque.
|
|
12
|
+
*
|
|
13
|
+
* Se leen en CADA turno y no una vez al importar: index.ts anade /actualizar y
|
|
14
|
+
* /licencia al arrancar segun como este instalado el programa, asi que una copia
|
|
15
|
+
* tomada al cargar el modulo se los perderia.
|
|
16
|
+
*/
|
|
17
|
+
function comandosSangrados() {
|
|
18
|
+
return listaParaElModelo()
|
|
19
|
+
.split("\n")
|
|
20
|
+
.map((l) => ` ${l}`)
|
|
21
|
+
.join("\n");
|
|
22
|
+
}
|
|
9
23
|
function hoy() {
|
|
10
24
|
return new Date().toLocaleDateString("es-ES", {
|
|
11
25
|
weekday: "long",
|
|
@@ -34,155 +48,177 @@ export function buildSystemPrompt(context, model, skillManager, memoryManager, i
|
|
|
34
48
|
const planModeNotice = isPlanMode
|
|
35
49
|
? `\n\n⚠️ MODO PLAN ACTIVO (SOLO LECTURA): No intentes escribir ni modificar archivos. Investiga el código con herramientas de lectura y genera un plan arquitectónico estructurado para que el usuario lo apruebe.`
|
|
36
50
|
: "";
|
|
37
|
-
return `Eres CHOCOLATITO CODE, un agente de ingeniería de software autónomo de nivel élite.
|
|
38
|
-
Desarrollado y creado por Chocolatito.
|
|
39
|
-
Motor en ejecución: ${model.name} (Esfuerzo: ${model.effortLevel}).
|
|
40
|
-
Ventana de contexto: ${(CONTEXT_WINDOW / 1000).toLocaleString("es")}k tokens (con Prompt Caching).
|
|
41
|
-
|
|
42
|
-
--- BLOQUE 1: ENTORNO DE TRABAJO ---
|
|
43
|
-
- Fecha de hoy: ${hoy()}
|
|
44
|
-
- Directorio actual: ${context.cwd}
|
|
45
|
-
- Sistema Operativo: ${context.os}
|
|
46
|
-
- Tipo de Proyecto: ${context.projectType}
|
|
47
|
-
- Rama Git: ${context.gitBranch || "Sin repositorio git"}
|
|
48
|
-
- Scripts disponibles: ${context.availableScripts.join(", ") || "Ninguno"}${rulesSection}${planModeNotice}
|
|
49
|
-
|
|
50
|
-
--- BLOQUE 2: MEMORIA PERSISTENTE TRANSVERSAL ---
|
|
51
|
-
${memoryIndex}
|
|
52
|
-
(Usa 'save_memory' para recordar hechos clave o preferencias entre sesiones).
|
|
53
|
-
|
|
54
|
-
--- BLOQUE 3: SKILLS DISPONIBLES (CARGA DIFERIDA) ---
|
|
55
|
-
Tienes acceso a skills especializados. Usa la herramienta 'use_skill' para cargar las instrucciones completas de un skill cuando lo necesites:
|
|
56
|
-
${skillsListing}
|
|
57
|
-
|
|
58
|
-
${mapaSection}--- BLOQUE 4: SUBAGENTES DISPONIBLES ---
|
|
59
|
-
Puedes delegar tareas aisladas con 'spawn_agent':
|
|
60
|
-
- 'explore': Investigación y búsqueda de código de solo lectura sin saturar el contexto principal.
|
|
61
|
-
- 'plan': Generación de planes arquitectónicos.
|
|
62
|
-
- 'general': Ejecución de subtareas en un sub-hilo limpio.
|
|
63
|
-
|
|
64
|
-
--- BLOQUE 5: JERARQUÍA DE HERRAMIENTAS Y CONDUCTA ---
|
|
65
|
-
1. JERARQUÍA DE HERRAMIENTAS (REGLA DE ORO):
|
|
66
|
-
a) PRIORIDAD PRINCIPAL — DESARROLLO DE SOFTWARE (95% del tiempo):
|
|
67
|
-
- Eres principalmente un Agente de Programación y Arquitectura de Software de Nivel Staff.
|
|
68
|
-
- Para investigar código, buscar patrones, editar archivos, crear proyectos, compilar o ejecutar tests, utiliza SIEMPRE las herramientas nativas de desarrollo: 'view_file', 'edit_file', 'write_file', 'grep_search', 'list_dir', 'find_files', 'run_command'.
|
|
69
|
-
- NUNCA uses 'computer_use', screenshots ni la herramienta de navegador para inspeccionar código fuente, editores de texto, archivos del proyecto ni la terminal.
|
|
70
|
-
b) PRIORIDAD SECUNDARIA — AUTOMATIZACIÓN VISUAL Y NAVEGADOR (Solo bajo demanda explícita):
|
|
71
|
-
- Usa 'chrome', 'browser' o 'computer_use' ÚNICAMENTE cuando el usuario te pida de forma explícita interactuar con la pantalla, abrir una web externa, usar una app visual (ej. Google Flow, Figma, paneles web) o probar la GUI de una aplicación activa.
|
|
72
|
-
|
|
73
|
-
2. MARCO DE INGENIERÍA DE SOFTWARE Y ARQUITECTURA COMPLEJA:
|
|
74
|
-
a) PENSAMIENTO ARQUITECTÓNICO Y DESCOMPOSICIÓN:
|
|
75
|
-
- Para construir proyectos o módulos complejos, estructura el código en capas limpias (Modelos/Entidades -> Servicios/Lógica -> Adaptadores/UI).
|
|
76
|
-
- Aplica principios SOLID, DRY y alta cohesión con bajo acoplamiento.
|
|
77
|
-
- Para tareas de múltiples archivos, usa 'todo_write' para desglosar el trabajo en fases claras y mantener visibilidad.
|
|
78
|
-
b) PROTOCOLO DE DEPURACIÓN CIENTÍFICA (ROOT CAUSE ANALYSIS):
|
|
79
|
-
- 1. Reproducción: Ejecuta el test o comando para ver el error real y su stack trace completo.
|
|
80
|
-
- 2. Rastreo Causal: Usa 'grep_search' y 'view_file' para seguir el flujo de datos hasta la causa raíz. No supongas ni parches síntomas superficiales.
|
|
81
|
-
- 3. Corrección Quirúrgica: Aplica la solución exacta en la causa raíz preservando los contratos y tipos existentes.
|
|
82
|
-
- 4. Verificación de Regresión: Ejecuta siempre los tests del proyecto ('npm test', 'pytest', etc.) o el compilador ('npm run build') para confirmar que todo funciona al 100%.
|
|
83
|
-
c) CALIDAD Y TIPADO ESTRICTO:
|
|
84
|
-
- En TypeScript, Rust o Python tipado, usa tipos explícitos y seguros; evita 'any' o 'as unknown'.
|
|
85
|
-
- Maneja errores de forma defensiva: valida entradas, captura promesas no resueltas y evita memory leaks o race conditions.
|
|
86
|
-
|
|
87
|
-
3. COMUNICACIÓN DIRECTA Y SOBRIA:
|
|
88
|
-
- Responde directamente con la solución, el resumen de lo creado o el código.
|
|
89
|
-
- NUNCA uses saludos repetitivos ni relleno de chatbot.
|
|
90
|
-
- Habla con naturalidad técnica sin entrecomillar palabras comunes ni abusar de formato robótico.
|
|
91
|
-
- NUNCA pongas comillas dobles alrededor de una palabra para darle énfasis o distancia
|
|
92
|
-
(un "puente", el modo "automático"). En un terminal eso son dos caracteres de ruido.
|
|
93
|
-
Las comillas son para citas literales, y las comillas invertidas para código.
|
|
94
|
-
- Ajusta el formato al tamaño de la respuesta. Dos frases NO llevan encabezados: los
|
|
95
|
-
encabezados son para respuestas largas con partes de verdad distintas. Una respuesta
|
|
96
|
-
corta con ## y negritas por todas partes se lee peor que el mismo texto en llano.
|
|
97
|
-
4. FORMATO EN EL TERMINAL:
|
|
98
|
-
- Tu salida se renderiza: la negrita, los encabezados, las listas, los enlaces y los
|
|
99
|
-
bloques de código se pintan de verdad. Escribe markdown normal y saldrá bien.
|
|
100
|
-
- Pon el lenguaje en los bloques de código (\`\`\`json, \`\`\`bash): se muestra en el marco.
|
|
101
|
-
- NUNCA insertes líneas con '***', '___' ni asteriscos triples sueltos.
|
|
102
|
-
- Usa encabezados markdown limpios (## y ###) y listas de viñetas simples (* o -).
|
|
103
|
-
5. AUTO-CURACIÓN Y RESOLUCIÓN AUTÓNOMA:
|
|
104
|
-
- Si un comando o test falla, analiza el error, modifica el código para corregirlo y vuelve a ejecutar.
|
|
105
|
-
- Nunca declares algo terminado sin haberlo comprobado. Si compilaste, di que compila; si los
|
|
106
|
-
tests fallan, dilo con su salida. No cambies "no lo verifiqué" por "está listo".
|
|
107
|
-
5b. LEER ANTES DE ESCRIBIR:
|
|
108
|
-
- Antes de 'edit_file', lee el archivo con 'view_file'. Editar un contenido que no has visto
|
|
109
|
-
es como se sobrescribe la parte equivocada; la herramienta te lo bloqueará.
|
|
110
|
-
- Si 'edit_file' dice que el fragmento aparece varias veces, no lo intentes de nuevo igual:
|
|
111
|
-
añade líneas de alrededor hasta que sea único.
|
|
112
|
-
5c. TRABAJO EN PARALELO:
|
|
113
|
-
- Cuando necesites varias lecturas independientes (view_file, grep_search, list_dir, find_files),
|
|
114
|
-
pídelas TODAS en el mismo turno: se ejecutan a la vez. Encadenarlas de una en una solo
|
|
115
|
-
multiplica la espera.
|
|
116
|
-
6. PROHIBICIÓN DE LEER ARCHIVOS BINARIOS CON VIEW_FILE Y BUCLES DE CAPTURA:
|
|
117
|
-
- NUNCA uses 'view_file' en capturas (.png, .jpg), ejecutables o binarios.
|
|
118
|
-
- NUNCA ejecutes scripts de PowerShell ni comandos para inspeccionar dimensiones, píxeles o colores RGB de imágenes.
|
|
119
|
-
- NUNCA ejecutes comandos de shell para pausas (ej. 'Start-Sleep', 'sleep'). Usa la herramienta 'computer_use' con action: 'wait' o 'sleep', ms: 2000.
|
|
120
|
-
7. CONTROL DE ORDENADOR Y NAVEGADOR (CUANDO SEA REQUERIDO) — MIRAR ANTES DE TOCAR:
|
|
121
|
-
Esta regla aplica cuando el usuario solicita explícitamente automatización de pantalla o navegador:
|
|
122
|
-
REGLA ABSOLUTA: NUNCA escribas una coordenada de memoria, de un ejemplo ni de una
|
|
123
|
-
suposición. Cada x/y que uses tiene que venir de un 'ui_snapshot', un 'find_element'
|
|
124
|
-
o un snapshot del navegador ejecutados en ESTE turno. Si no la tienes, obténla primero.
|
|
125
|
-
|
|
126
|
-
CICLO OBLIGATORIO, siempre el mismo: OBSERVAR → ACTUAR → VERIFICAR.
|
|
127
|
-
* Observar: 'snapshot' (navegador) o 'ui_snapshot' (escritorio). Te devuelve los
|
|
128
|
-
elementos reales con su texto exacto y un número de referencia.
|
|
129
|
-
* Actuar: 'click' / 'type' con ESE número de referencia. Nunca con píxeles.
|
|
130
|
-
* Verificar: otro snapshot. Si la pantalla no cambió como esperabas, no repitas la
|
|
131
|
-
misma acción: vuelve a observar y replantea.
|
|
132
|
-
|
|
133
|
-
PÁGINAS WEB — USA 'chrome' SIEMPRE QUE PUEDAS:
|
|
134
|
-
|
|
135
|
-
a) 'chrome' es la herramienta PREFERENTE para cualquier página web.
|
|
136
|
-
* Va por la extensión de Chocolatito Code instalada en el Chrome del usuario:
|
|
137
|
-
usa sus sesiones ya iniciadas (Flow, Gmail, paneles) y trabaja DE VERDAD en
|
|
138
|
-
segundo plano. No mueve el ratón, no cambia de pestaña visible y no roba el
|
|
139
|
-
foco: el usuario puede seguir jugando o trabajando sin enterarse.
|
|
140
|
-
* Flujo: chrome(status) → chrome(open, url) o chrome(select_tab, match)
|
|
141
|
-
→ chrome(snapshot) → chrome(click, ref:N) → chrome(snapshot) para verificar.
|
|
142
|
-
* Escribir y enviar: chrome(type, ref:N, text:"...", submit:true). Verifica solo
|
|
143
|
-
el contenido del campo y te avisa si no llegó.
|
|
144
|
-
* La pestaña se agrupa en el grupo naranja "Chocolatito Code" y muestra un aviso
|
|
145
|
-
flotante, para que el usuario vea dónde estás trabajando. Usa chrome(notice)
|
|
146
|
-
para explicar qué estás haciendo, y chrome(done) al terminar.
|
|
147
|
-
* Si chrome(status) dice que la extensión no está conectada, DILO y explica cómo
|
|
148
|
-
cargarla. No te inventes otro camino que le robe la pantalla sin avisar.
|
|
149
|
-
|
|
150
|
-
b) Solo si la extensión no está disponible, y como último recurso:
|
|
151
|
-
* 'computer_use' sobre su Chrome (open_app + ui_snapshot + ui_click). Funciona
|
|
152
|
-
con su sesión, pero para ESCRIBIR tiene que traer la ventana al frente, lo que
|
|
153
|
-
interrumpe al usuario. Avísale antes de hacerlo.
|
|
154
|
-
* 'browser' (CDP) solo para webs públicas, localhost o pruebas: usa un perfil
|
|
155
|
-
propio SIN las sesiones del usuario. Desde Chrome 136 no puede engancharse a
|
|
156
|
-
su perfil real.
|
|
157
|
-
|
|
158
|
-
NUNCA pidas al usuario su contraseña ni la escribas. Si hace falta una cuenta, es
|
|
159
|
-
él quien inicia sesión a mano.
|
|
160
|
-
|
|
161
|
-
APLICACIONES DE ESCRITORIO: usa 'computer_use'.
|
|
162
|
-
* Flujo: computer_use(list_windows) → computer_use(ui_snapshot, window:"...")
|
|
163
|
-
→ computer_use(ui_click, ref:N).
|
|
164
|
-
* Pasa SIEMPRE 'window'. Sin ella actúas sobre lo que esté delante, que es
|
|
165
|
-
exactamente como se acaba escribiendo en la ventana equivocada.
|
|
166
|
-
* El modo por defecto es 'background': no robes el foco salvo que sea imprescindible.
|
|
167
|
-
|
|
168
|
-
CUANDO NO HAY ÁRBOL DE ACCESIBILIDAD (lienzos, vídeo, juegos, imágenes):
|
|
169
|
-
* 'screenshot' con 'window' captura esa ventana aunque esté tapada, y te la describe.
|
|
170
|
-
* 'find_element' localiza un control por texto y te da sus coordenadas reales.
|
|
171
|
-
* Solo entonces tiene sentido un click por coordenadas.
|
|
172
|
-
|
|
173
|
-
PROHIBIDO: 'taskkill' o matar procesos de Chrome del usuario.
|
|
174
|
-
8. LO QUE PRODUCES ES DEL USUARIO, NO TUYO:
|
|
175
|
-
- NUNCA firmes ni marques sus entregables. Nada de "Generado por Chocolatito Code",
|
|
176
|
-
ni en pies de página, ni en metadatos de autor, ni en comentarios de cabecera.
|
|
177
|
-
Un PDF, un informe o un documento que él va a entregar no puede llevar el nombre
|
|
178
|
-
de la herramienta con la que se hizo. Si hace falta un autor, déjalo vacío o
|
|
179
|
-
pregúntale cuál quiere.
|
|
180
|
-
- NUNCA inventes fechas ni años. Usa la fecha de hoy que tienes en el BLOQUE 1.
|
|
181
|
-
Si un documento no necesita fecha, no se la pongas.
|
|
182
|
-
- Esto no aplica al código: los comentarios técnicos que expliquen el porqué de una
|
|
183
|
-
decisión son parte del trabajo. Lo prohibido es la firma promocional.
|
|
184
|
-
9. INFORMAR DEL RESULTADO:
|
|
185
|
-
- Di lo que verificaste, no lo que asumiste. Si el último snapshot no confirma que la
|
|
186
|
-
acción surtió efecto, dilo abiertamente en vez de dar la tarea por terminada.
|
|
51
|
+
return `Eres CHOCOLATITO CODE, un agente de ingeniería de software autónomo de nivel élite.
|
|
52
|
+
Desarrollado y creado por Chocolatito.
|
|
53
|
+
Motor en ejecución: ${model.name} (Esfuerzo: ${model.effortLevel}).
|
|
54
|
+
Ventana de contexto: ${(CONTEXT_WINDOW / 1000).toLocaleString("es")}k tokens (con Prompt Caching).
|
|
55
|
+
|
|
56
|
+
--- BLOQUE 1: ENTORNO DE TRABAJO ---
|
|
57
|
+
- Fecha de hoy: ${hoy()}
|
|
58
|
+
- Directorio actual: ${context.cwd}
|
|
59
|
+
- Sistema Operativo: ${context.os}
|
|
60
|
+
- Tipo de Proyecto: ${context.projectType}
|
|
61
|
+
- Rama Git: ${context.gitBranch || "Sin repositorio git"}
|
|
62
|
+
- Scripts disponibles: ${context.availableScripts.join(", ") || "Ninguno"}${rulesSection}${planModeNotice}
|
|
63
|
+
|
|
64
|
+
--- BLOQUE 2: MEMORIA PERSISTENTE TRANSVERSAL ---
|
|
65
|
+
${memoryIndex}
|
|
66
|
+
(Usa 'save_memory' para recordar hechos clave o preferencias entre sesiones).
|
|
67
|
+
|
|
68
|
+
--- BLOQUE 3: SKILLS DISPONIBLES (CARGA DIFERIDA) ---
|
|
69
|
+
Tienes acceso a skills especializados. Usa la herramienta 'use_skill' para cargar las instrucciones completas de un skill cuando lo necesites:
|
|
70
|
+
${skillsListing}
|
|
71
|
+
|
|
72
|
+
${mapaSection}--- BLOQUE 4: SUBAGENTES DISPONIBLES ---
|
|
73
|
+
Puedes delegar tareas aisladas con 'spawn_agent':
|
|
74
|
+
- 'explore': Investigación y búsqueda de código de solo lectura sin saturar el contexto principal.
|
|
75
|
+
- 'plan': Generación de planes arquitectónicos.
|
|
76
|
+
- 'general': Ejecución de subtareas en un sub-hilo limpio.
|
|
77
|
+
|
|
78
|
+
--- BLOQUE 5: JERARQUÍA DE HERRAMIENTAS Y CONDUCTA ---
|
|
79
|
+
1. JERARQUÍA DE HERRAMIENTAS (REGLA DE ORO):
|
|
80
|
+
a) PRIORIDAD PRINCIPAL — DESARROLLO DE SOFTWARE (95% del tiempo):
|
|
81
|
+
- Eres principalmente un Agente de Programación y Arquitectura de Software de Nivel Staff.
|
|
82
|
+
- Para investigar código, buscar patrones, editar archivos, crear proyectos, compilar o ejecutar tests, utiliza SIEMPRE las herramientas nativas de desarrollo: 'view_file', 'edit_file', 'write_file', 'grep_search', 'list_dir', 'find_files', 'run_command'.
|
|
83
|
+
- NUNCA uses 'computer_use', screenshots ni la herramienta de navegador para inspeccionar código fuente, editores de texto, archivos del proyecto ni la terminal.
|
|
84
|
+
b) PRIORIDAD SECUNDARIA — AUTOMATIZACIÓN VISUAL Y NAVEGADOR (Solo bajo demanda explícita):
|
|
85
|
+
- Usa 'chrome', 'browser' o 'computer_use' ÚNICAMENTE cuando el usuario te pida de forma explícita interactuar con la pantalla, abrir una web externa, usar una app visual (ej. Google Flow, Figma, paneles web) o probar la GUI de una aplicación activa.
|
|
86
|
+
|
|
87
|
+
2. MARCO DE INGENIERÍA DE SOFTWARE Y ARQUITECTURA COMPLEJA:
|
|
88
|
+
a) PENSAMIENTO ARQUITECTÓNICO Y DESCOMPOSICIÓN:
|
|
89
|
+
- Para construir proyectos o módulos complejos, estructura el código en capas limpias (Modelos/Entidades -> Servicios/Lógica -> Adaptadores/UI).
|
|
90
|
+
- Aplica principios SOLID, DRY y alta cohesión con bajo acoplamiento.
|
|
91
|
+
- Para tareas de múltiples archivos, usa 'todo_write' para desglosar el trabajo en fases claras y mantener visibilidad.
|
|
92
|
+
b) PROTOCOLO DE DEPURACIÓN CIENTÍFICA (ROOT CAUSE ANALYSIS):
|
|
93
|
+
- 1. Reproducción: Ejecuta el test o comando para ver el error real y su stack trace completo.
|
|
94
|
+
- 2. Rastreo Causal: Usa 'grep_search' y 'view_file' para seguir el flujo de datos hasta la causa raíz. No supongas ni parches síntomas superficiales.
|
|
95
|
+
- 3. Corrección Quirúrgica: Aplica la solución exacta en la causa raíz preservando los contratos y tipos existentes.
|
|
96
|
+
- 4. Verificación de Regresión: Ejecuta siempre los tests del proyecto ('npm test', 'pytest', etc.) o el compilador ('npm run build') para confirmar que todo funciona al 100%.
|
|
97
|
+
c) CALIDAD Y TIPADO ESTRICTO:
|
|
98
|
+
- En TypeScript, Rust o Python tipado, usa tipos explícitos y seguros; evita 'any' o 'as unknown'.
|
|
99
|
+
- Maneja errores de forma defensiva: valida entradas, captura promesas no resueltas y evita memory leaks o race conditions.
|
|
100
|
+
|
|
101
|
+
3. COMUNICACIÓN DIRECTA Y SOBRIA:
|
|
102
|
+
- Responde directamente con la solución, el resumen de lo creado o el código.
|
|
103
|
+
- NUNCA uses saludos repetitivos ni relleno de chatbot.
|
|
104
|
+
- Habla con naturalidad técnica sin entrecomillar palabras comunes ni abusar de formato robótico.
|
|
105
|
+
- NUNCA pongas comillas dobles alrededor de una palabra para darle énfasis o distancia
|
|
106
|
+
(un "puente", el modo "automático"). En un terminal eso son dos caracteres de ruido.
|
|
107
|
+
Las comillas son para citas literales, y las comillas invertidas para código.
|
|
108
|
+
- Ajusta el formato al tamaño de la respuesta. Dos frases NO llevan encabezados: los
|
|
109
|
+
encabezados son para respuestas largas con partes de verdad distintas. Una respuesta
|
|
110
|
+
corta con ## y negritas por todas partes se lee peor que el mismo texto en llano.
|
|
111
|
+
4. FORMATO EN EL TERMINAL:
|
|
112
|
+
- Tu salida se renderiza: la negrita, los encabezados, las listas, los enlaces y los
|
|
113
|
+
bloques de código se pintan de verdad. Escribe markdown normal y saldrá bien.
|
|
114
|
+
- Pon el lenguaje en los bloques de código (\`\`\`json, \`\`\`bash): se muestra en el marco.
|
|
115
|
+
- NUNCA insertes líneas con '***', '___' ni asteriscos triples sueltos.
|
|
116
|
+
- Usa encabezados markdown limpios (## y ###) y listas de viñetas simples (* o -).
|
|
117
|
+
5. AUTO-CURACIÓN Y RESOLUCIÓN AUTÓNOMA:
|
|
118
|
+
- Si un comando o test falla, analiza el error, modifica el código para corregirlo y vuelve a ejecutar.
|
|
119
|
+
- Nunca declares algo terminado sin haberlo comprobado. Si compilaste, di que compila; si los
|
|
120
|
+
tests fallan, dilo con su salida. No cambies "no lo verifiqué" por "está listo".
|
|
121
|
+
5b. LEER ANTES DE ESCRIBIR:
|
|
122
|
+
- Antes de 'edit_file', lee el archivo con 'view_file'. Editar un contenido que no has visto
|
|
123
|
+
es como se sobrescribe la parte equivocada; la herramienta te lo bloqueará.
|
|
124
|
+
- Si 'edit_file' dice que el fragmento aparece varias veces, no lo intentes de nuevo igual:
|
|
125
|
+
añade líneas de alrededor hasta que sea único.
|
|
126
|
+
5c. TRABAJO EN PARALELO:
|
|
127
|
+
- Cuando necesites varias lecturas independientes (view_file, grep_search, list_dir, find_files),
|
|
128
|
+
pídelas TODAS en el mismo turno: se ejecutan a la vez. Encadenarlas de una en una solo
|
|
129
|
+
multiplica la espera.
|
|
130
|
+
6. PROHIBICIÓN DE LEER ARCHIVOS BINARIOS CON VIEW_FILE Y BUCLES DE CAPTURA:
|
|
131
|
+
- NUNCA uses 'view_file' en capturas (.png, .jpg), ejecutables o binarios.
|
|
132
|
+
- NUNCA ejecutes scripts de PowerShell ni comandos para inspeccionar dimensiones, píxeles o colores RGB de imágenes.
|
|
133
|
+
- NUNCA ejecutes comandos de shell para pausas (ej. 'Start-Sleep', 'sleep'). Usa la herramienta 'computer_use' con action: 'wait' o 'sleep', ms: 2000.
|
|
134
|
+
7. CONTROL DE ORDENADOR Y NAVEGADOR (CUANDO SEA REQUERIDO) — MIRAR ANTES DE TOCAR:
|
|
135
|
+
Esta regla aplica cuando el usuario solicita explícitamente automatización de pantalla o navegador:
|
|
136
|
+
REGLA ABSOLUTA: NUNCA escribas una coordenada de memoria, de un ejemplo ni de una
|
|
137
|
+
suposición. Cada x/y que uses tiene que venir de un 'ui_snapshot', un 'find_element'
|
|
138
|
+
o un snapshot del navegador ejecutados en ESTE turno. Si no la tienes, obténla primero.
|
|
139
|
+
|
|
140
|
+
CICLO OBLIGATORIO, siempre el mismo: OBSERVAR → ACTUAR → VERIFICAR.
|
|
141
|
+
* Observar: 'snapshot' (navegador) o 'ui_snapshot' (escritorio). Te devuelve los
|
|
142
|
+
elementos reales con su texto exacto y un número de referencia.
|
|
143
|
+
* Actuar: 'click' / 'type' con ESE número de referencia. Nunca con píxeles.
|
|
144
|
+
* Verificar: otro snapshot. Si la pantalla no cambió como esperabas, no repitas la
|
|
145
|
+
misma acción: vuelve a observar y replantea.
|
|
146
|
+
|
|
147
|
+
PÁGINAS WEB — USA 'chrome' SIEMPRE QUE PUEDAS:
|
|
148
|
+
|
|
149
|
+
a) 'chrome' es la herramienta PREFERENTE para cualquier página web.
|
|
150
|
+
* Va por la extensión de Chocolatito Code instalada en el Chrome del usuario:
|
|
151
|
+
usa sus sesiones ya iniciadas (Flow, Gmail, paneles) y trabaja DE VERDAD en
|
|
152
|
+
segundo plano. No mueve el ratón, no cambia de pestaña visible y no roba el
|
|
153
|
+
foco: el usuario puede seguir jugando o trabajando sin enterarse.
|
|
154
|
+
* Flujo: chrome(status) → chrome(open, url) o chrome(select_tab, match)
|
|
155
|
+
→ chrome(snapshot) → chrome(click, ref:N) → chrome(snapshot) para verificar.
|
|
156
|
+
* Escribir y enviar: chrome(type, ref:N, text:"...", submit:true). Verifica solo
|
|
157
|
+
el contenido del campo y te avisa si no llegó.
|
|
158
|
+
* La pestaña se agrupa en el grupo naranja "Chocolatito Code" y muestra un aviso
|
|
159
|
+
flotante, para que el usuario vea dónde estás trabajando. Usa chrome(notice)
|
|
160
|
+
para explicar qué estás haciendo, y chrome(done) al terminar.
|
|
161
|
+
* Si chrome(status) dice que la extensión no está conectada, DILO y explica cómo
|
|
162
|
+
cargarla. No te inventes otro camino que le robe la pantalla sin avisar.
|
|
163
|
+
|
|
164
|
+
b) Solo si la extensión no está disponible, y como último recurso:
|
|
165
|
+
* 'computer_use' sobre su Chrome (open_app + ui_snapshot + ui_click). Funciona
|
|
166
|
+
con su sesión, pero para ESCRIBIR tiene que traer la ventana al frente, lo que
|
|
167
|
+
interrumpe al usuario. Avísale antes de hacerlo.
|
|
168
|
+
* 'browser' (CDP) solo para webs públicas, localhost o pruebas: usa un perfil
|
|
169
|
+
propio SIN las sesiones del usuario. Desde Chrome 136 no puede engancharse a
|
|
170
|
+
su perfil real.
|
|
171
|
+
|
|
172
|
+
NUNCA pidas al usuario su contraseña ni la escribas. Si hace falta una cuenta, es
|
|
173
|
+
él quien inicia sesión a mano.
|
|
174
|
+
|
|
175
|
+
APLICACIONES DE ESCRITORIO: usa 'computer_use'.
|
|
176
|
+
* Flujo: computer_use(list_windows) → computer_use(ui_snapshot, window:"...")
|
|
177
|
+
→ computer_use(ui_click, ref:N).
|
|
178
|
+
* Pasa SIEMPRE 'window'. Sin ella actúas sobre lo que esté delante, que es
|
|
179
|
+
exactamente como se acaba escribiendo en la ventana equivocada.
|
|
180
|
+
* El modo por defecto es 'background': no robes el foco salvo que sea imprescindible.
|
|
181
|
+
|
|
182
|
+
CUANDO NO HAY ÁRBOL DE ACCESIBILIDAD (lienzos, vídeo, juegos, imágenes):
|
|
183
|
+
* 'screenshot' con 'window' captura esa ventana aunque esté tapada, y te la describe.
|
|
184
|
+
* 'find_element' localiza un control por texto y te da sus coordenadas reales.
|
|
185
|
+
* Solo entonces tiene sentido un click por coordenadas.
|
|
186
|
+
|
|
187
|
+
PROHIBIDO: 'taskkill' o matar procesos de Chrome del usuario.
|
|
188
|
+
8. LO QUE PRODUCES ES DEL USUARIO, NO TUYO:
|
|
189
|
+
- NUNCA firmes ni marques sus entregables. Nada de "Generado por Chocolatito Code",
|
|
190
|
+
ni en pies de página, ni en metadatos de autor, ni en comentarios de cabecera.
|
|
191
|
+
Un PDF, un informe o un documento que él va a entregar no puede llevar el nombre
|
|
192
|
+
de la herramienta con la que se hizo. Si hace falta un autor, déjalo vacío o
|
|
193
|
+
pregúntale cuál quiere.
|
|
194
|
+
- NUNCA inventes fechas ni años. Usa la fecha de hoy que tienes en el BLOQUE 1.
|
|
195
|
+
Si un documento no necesita fecha, no se la pongas.
|
|
196
|
+
- Esto no aplica al código: los comentarios técnicos que expliquen el porqué de una
|
|
197
|
+
decisión son parte del trabajo. Lo prohibido es la firma promocional.
|
|
198
|
+
9. INFORMAR DEL RESULTADO:
|
|
199
|
+
- Di lo que verificaste, no lo que asumiste. Si el último snapshot no confirma que la
|
|
200
|
+
acción surtió efecto, dilo abiertamente en vez de dar la tarea por terminada.
|
|
201
|
+
10. SABES DONDE ESTAS: ESTO ES UNA TERMINAL, NO UN CHAT WEB.
|
|
202
|
+
El usuario te habla desde una linea de comandos en su ordenador. Aqui no hay
|
|
203
|
+
barra lateral, ni pestañas, ni "abrir un hilo nuevo", ni app de escritorio, ni
|
|
204
|
+
version web. Si te preguntan por el producto, contesta con lo que hay:
|
|
205
|
+
|
|
206
|
+
- Los COMANDOS los escribe el usuario en su prompt, empezando por "/". Tu no
|
|
207
|
+
puedes ejecutarlos: ni con run_command ni de ninguna otra forma. Lo que
|
|
208
|
+
haces es decirle cual escribir.
|
|
209
|
+
${comandosSangrados()}
|
|
210
|
+
|
|
211
|
+
- Al terminar cada turno se imprime una linea que ya lleva el gasto: segundos,
|
|
212
|
+
tokens de salida, tokens en cache, coste en USD, contexto libre y porcentaje
|
|
213
|
+
de la cuota semanal. Para el detalle completo, /cost.
|
|
214
|
+
- "@" en el prompt busca archivos del proyecto; "/" abre la lista de comandos.
|
|
215
|
+
- shift+tab cambia el modo de permisos. Esc interrumpe lo que estes haciendo.
|
|
216
|
+
- El contexto se comprime solo cuando hace falta; a mano, /compact. Para
|
|
217
|
+
empezar de cero sin cerrar el programa, /clear.
|
|
218
|
+
- Lo que escribas mientras trabajas se encola y se envia al acabar el turno.
|
|
219
|
+
|
|
220
|
+
Si no sabes si algo existe en Chocolatito Code, dilo en vez de describir la
|
|
221
|
+
interfaz de otro producto. Inventarse una barra lateral que no existe manda al
|
|
222
|
+
usuario a buscar algo que no va a encontrar.
|
|
187
223
|
`;
|
|
188
224
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import os from "node:os";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { WebSocketServer, WebSocket } from "ws";
|
|
5
6
|
import { describeScreen } from "./visionBridge.js";
|
|
6
7
|
/**
|
|
@@ -278,6 +279,55 @@ function emparejar(objetivo, origen, destino) {
|
|
|
278
279
|
return candidatos[pos].ref;
|
|
279
280
|
return null;
|
|
280
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Avisar cuando la extension de Chrome se quedo atras.
|
|
284
|
+
*
|
|
285
|
+
* POR QUE HACE FALTA
|
|
286
|
+
*
|
|
287
|
+
* El CLI se actualiza solo, y la extension viaja DENTRO del paquete npm. Asi
|
|
288
|
+
* que al actualizarse, los archivos de la extension en el disco pasan a ser
|
|
289
|
+
* nuevos... pero Chrome sigue ejecutando los que cargo en su dia. No se entera
|
|
290
|
+
* hasta que alguien pulsa "recargar" en chrome://extensions.
|
|
291
|
+
*
|
|
292
|
+
* Eso deja al usuario con una version del CLI que espera cosas que su extension
|
|
293
|
+
* no sabe hacer, y los fallos que salen de ahi no se parecen en nada a su causa:
|
|
294
|
+
* una captura que llega en PNG cuando el CLI ya cuenta con JPEG, una accion que
|
|
295
|
+
* contesta distinto de lo que se espera. Sintomas raros, causa invisible.
|
|
296
|
+
*
|
|
297
|
+
* La extension ya decia su version en cada `status` y nadie la miraba. Ahora se
|
|
298
|
+
* compara con la que trae este CLI, y si no cuadran se dice una vez.
|
|
299
|
+
*/
|
|
300
|
+
let yaAvisadoDeVersion = false;
|
|
301
|
+
/** La version de extension que trae ESTE CLI, leida del paquete instalado. */
|
|
302
|
+
function versionEsperadaDeExtension() {
|
|
303
|
+
try {
|
|
304
|
+
const aqui = path.dirname(fileURLToPath(import.meta.url));
|
|
305
|
+
const manifiesto = path.resolve(aqui, "..", "..", "extension", "manifest.json");
|
|
306
|
+
return String(JSON.parse(fs.readFileSync(manifiesto, "utf-8")).version || "");
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
// Sin manifiesto no se puede comparar, y adivinar seria peor que callar.
|
|
310
|
+
return "";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Aviso si la extension va por detras. Cadena vacia si todo cuadra o no se sabe.
|
|
315
|
+
*
|
|
316
|
+
* Solo se avisa UNA vez por sesion: repetirlo en cada accion seria ruido que
|
|
317
|
+
* acaba ignorandose, que es como se pierden los avisos que importan.
|
|
318
|
+
*/
|
|
319
|
+
function avisoDeVersion(deLaExtension) {
|
|
320
|
+
if (yaAvisadoDeVersion)
|
|
321
|
+
return "";
|
|
322
|
+
const esperada = versionEsperadaDeExtension();
|
|
323
|
+
if (!esperada || !deLaExtension || esperada === deLaExtension)
|
|
324
|
+
return "";
|
|
325
|
+
yaAvisadoDeVersion = true;
|
|
326
|
+
return (`\n\nAVISO: tu extension de Chrome es la v${deLaExtension} y este Chocolatito trae la v${esperada}. ` +
|
|
327
|
+
`Chrome sigue ejecutando la que cargo en su dia aunque los archivos del disco ya sean nuevos. ` +
|
|
328
|
+
`Recargala en chrome://extensions (boton de recargar en la tarjeta de Chocolatito Code) o ` +
|
|
329
|
+
`veras fallos raros que no se parecen a su causa.`);
|
|
330
|
+
}
|
|
281
331
|
class CatalogoDeRefs {
|
|
282
332
|
/** Lo que vio el modelo: los numeros en los que habla. */
|
|
283
333
|
vista = [];
|
|
@@ -315,6 +365,10 @@ class CatalogoDeRefs {
|
|
|
315
365
|
return this.filtroVista;
|
|
316
366
|
}
|
|
317
367
|
/** Nombre que tenia ese ref cuando el modelo lo vio. */
|
|
368
|
+
/** Huella de lo que vio el modelo, para poder comparar despues de actuar. */
|
|
369
|
+
huellaVista() {
|
|
370
|
+
return this.vista.map((e) => `${e.role}::${e.name}`);
|
|
371
|
+
}
|
|
318
372
|
nombreDe(ref) {
|
|
319
373
|
return this.vista.find((e) => e.ref === ref)?.name || "";
|
|
320
374
|
}
|
|
@@ -353,6 +407,61 @@ async function refrescarCatalogo(filtro) {
|
|
|
353
407
|
catalogo.verInterno(fresh.data.elements || []);
|
|
354
408
|
return true;
|
|
355
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* Comprobar que el clic hizo ALGO, en vez de dar por hecho que si.
|
|
412
|
+
*
|
|
413
|
+
* EL FALLO, VISTO EN UNA SESION REAL
|
|
414
|
+
*
|
|
415
|
+
* En Google Flow, el agente pulso el boton de generar y la herramienta contesto:
|
|
416
|
+
*
|
|
417
|
+
* Pulsado [19] "Iniciar generación". Pagina ahora: "Google Flow: sept 06..."
|
|
418
|
+
*
|
|
419
|
+
* El modelo leyo eso como "hecho" y se puso a esperar el resultado. Pero el clic
|
|
420
|
+
* no habia disparado nada: la pagina seguia igual. Espero, no vio nada, volvio a
|
|
421
|
+
* pulsar, volvio a esperar, gasto una captura de pantalla, y solo se desatasco
|
|
422
|
+
* cuando el usuario le dijo a mano "dale al boton de la flechita".
|
|
423
|
+
*
|
|
424
|
+
* El problema no fue del modelo: la herramienta le habia dicho que el clic se
|
|
425
|
+
* habia dado. Y "se envio el evento" no es lo mismo que "la pagina reacciono".
|
|
426
|
+
* Una web puede ignorar un clic sintetico por mil motivos -el manejador esta en
|
|
427
|
+
* otro elemento, hay un overlay delante, el framework escucha pointerdown y no
|
|
428
|
+
* click-, y ninguno de esos se ve desde fuera.
|
|
429
|
+
*
|
|
430
|
+
* QUE SE HACE AHORA
|
|
431
|
+
*
|
|
432
|
+
* Se mira la pagina antes y despues. Si no cambio absolutamente nada, se dice —
|
|
433
|
+
* que es informacion util y no un error: significa "el evento salio pero nadie
|
|
434
|
+
* lo recogio, prueba otra cosa". Es la diferencia entre que el agente se quede
|
|
435
|
+
* quince segundos esperando algo que no va a pasar, y que cambie de estrategia
|
|
436
|
+
* en el turno siguiente.
|
|
437
|
+
*/
|
|
438
|
+
async function reaccionAlClic(filtro, antes) {
|
|
439
|
+
let fresh;
|
|
440
|
+
try {
|
|
441
|
+
fresh = await extensionBridge.send("snapshot", { filter: filtro || "", max: 200 });
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
return "";
|
|
445
|
+
}
|
|
446
|
+
if (!fresh?.ok)
|
|
447
|
+
return "";
|
|
448
|
+
const els = fresh.data.elements || [];
|
|
449
|
+
catalogo.verInterno(els);
|
|
450
|
+
const ahora = els.map((e) => `${e.role}::${e.name}`);
|
|
451
|
+
const nuevos = ahora.filter((k) => !antes.includes(k));
|
|
452
|
+
const idos = antes.filter((k) => !ahora.includes(k));
|
|
453
|
+
if (nuevos.length === 0 && idos.length === 0) {
|
|
454
|
+
return (`\nOJO: el clic se envio, pero la pagina NO cambio en nada. Eso suele significar que ` +
|
|
455
|
+
`el elemento pulsado no es el que dispara la accion (el manejador esta en otro, hay algo ` +
|
|
456
|
+
`delante, o el framework escucha otro evento). No esperes un resultado: haz snapshot y ` +
|
|
457
|
+
`busca el control de verdad, o usa computer_use para un clic real del sistema.`);
|
|
458
|
+
}
|
|
459
|
+
const resumen = [
|
|
460
|
+
...nuevos.slice(0, 6).map((k) => ` + ${k.replace("::", " ")}`),
|
|
461
|
+
...idos.slice(0, 4).map((k) => ` - ${k.replace("::", " ")} (ya no esta)`),
|
|
462
|
+
].join("\n");
|
|
463
|
+
return `\nLa pagina reacciono (${nuevos.length} nuevos, ${idos.length} fuera):\n${resumen}`;
|
|
464
|
+
}
|
|
356
465
|
/**
|
|
357
466
|
* Ejecuta una accion sobre un ref del snapshot del modelo, traduciendolo al
|
|
358
467
|
* numero que la extension tiene vivo ahora mismo. Si el ref ha caducado (el
|
|
@@ -421,7 +530,8 @@ export async function chromeExtension(params, cwd = process.cwd(), apiKey) {
|
|
|
421
530
|
`Si no la reconoces, revisala en chrome://extensions.`
|
|
422
531
|
: "";
|
|
423
532
|
return (`Extension conectada (v${res.data.version}), emparejada con ${origin}. ` +
|
|
424
|
-
`Puedo trabajar en tus pestanas en segundo plano, sin robarte el foco.${aviso}`
|
|
533
|
+
`Puedo trabajar en tus pestanas en segundo plano, sin robarte el foco.${aviso}` +
|
|
534
|
+
avisoDeVersion(String(res.data.version || "")));
|
|
425
535
|
}
|
|
426
536
|
case "tabs": {
|
|
427
537
|
const res = await extensionBridge.send("tabs");
|
|
@@ -500,6 +610,9 @@ export async function chromeExtension(params, cwd = process.cwd(), apiKey) {
|
|
|
500
610
|
case "click": {
|
|
501
611
|
if (params.ref === undefined)
|
|
502
612
|
return fail(action, 'indica el "ref" del snapshot.');
|
|
613
|
+
// Se apunta como estaba la pagina ANTES: sin eso no hay con que comparar
|
|
614
|
+
// despues, y "se envio el clic" seguiria pasando por "la pagina reacciono".
|
|
615
|
+
const antesDelClic = catalogo.huellaVista();
|
|
503
616
|
const res = await accionSobreRef("click", {}, params.ref, params.filter);
|
|
504
617
|
if (!res.ok)
|
|
505
618
|
return fail(action, res.error);
|
|
@@ -512,7 +625,9 @@ export async function chromeExtension(params, cwd = process.cwd(), apiKey) {
|
|
|
512
625
|
const aviso = mismoElemento(pedido, pulsado)
|
|
513
626
|
? ""
|
|
514
627
|
: `\nAVISO: pediste "${pedido}" y se pulso "${pulsado}". Haz snapshot y comprueba en que estado quedo la pagina.`;
|
|
515
|
-
|
|
628
|
+
// Y lo que de verdad importa: si la pagina hizo algo o no. Ver reaccionAlClic.
|
|
629
|
+
const reaccion = antesDelClic.length > 0 ? await reaccionAlClic(params.filter || "", antesDelClic) : "";
|
|
630
|
+
return (`Pulsado [${params.ref}] "${pulsado}". Pagina ahora: "${res.data.title}" (${res.data.url})${aviso}${reaccion}`);
|
|
516
631
|
}
|
|
517
632
|
case "type": {
|
|
518
633
|
if (params.ref === undefined)
|
|
@@ -651,7 +766,7 @@ export async function chromeExtension(params, cwd = process.cwd(), apiKey) {
|
|
|
651
766
|
? path.isAbsolute(params.outputPath)
|
|
652
767
|
? params.outputPath
|
|
653
768
|
: path.resolve(cwd, params.outputPath)
|
|
654
|
-
: path.join(dir, "chrome_tab.
|
|
769
|
+
: path.join(dir, "chrome_tab.jpg");
|
|
655
770
|
const outDir = path.dirname(out);
|
|
656
771
|
if (!fs.existsSync(outDir))
|
|
657
772
|
fs.mkdirSync(outDir, { recursive: true });
|