chocolatito-code 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp.json.ejemplo +19 -0
- package/COMPUTER-USE.md +484 -0
- package/LICENSE +21 -0
- package/README.md +186 -0
- package/dist/agent/context.d.ts +9 -0
- package/dist/agent/context.js +123 -0
- package/dist/agent/fileTracker.d.ts +16 -0
- package/dist/agent/fileTracker.js +53 -0
- package/dist/agent/loop.d.ts +25 -0
- package/dist/agent/loop.js +524 -0
- package/dist/agent/mentions.d.ts +11 -0
- package/dist/agent/mentions.js +43 -0
- package/dist/agent/retry.d.ts +17 -0
- package/dist/agent/retry.js +57 -0
- package/dist/agent/subagent.d.ts +2 -0
- package/dist/agent/subagent.js +116 -0
- package/dist/agent/syntaxValidator.d.ts +5 -0
- package/dist/agent/syntaxValidator.js +71 -0
- package/dist/agent/tracker.d.ts +19 -0
- package/dist/agent/tracker.js +38 -0
- package/dist/agent/undoManager.d.ts +37 -0
- package/dist/agent/undoManager.js +95 -0
- package/dist/config/constants.d.ts +77 -0
- package/dist/config/constants.js +146 -0
- package/dist/config/engine.d.ts +49 -0
- package/dist/config/engine.js +86 -0
- package/dist/config/env.d.ts +13 -0
- package/dist/config/env.js +103 -0
- package/dist/config/license.d.ts +39 -0
- package/dist/config/license.js +306 -0
- package/dist/config/permissions.d.ts +38 -0
- package/dist/config/permissions.js +239 -0
- package/dist/config/quota.d.ts +58 -0
- package/dist/config/quota.js +156 -0
- package/dist/hooks/manager.d.ts +60 -0
- package/dist/hooks/manager.js +193 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +470 -0
- package/dist/mcp/client.d.ts +55 -0
- package/dist/mcp/client.js +282 -0
- package/dist/mcp/manager.d.ts +26 -0
- package/dist/mcp/manager.js +167 -0
- package/dist/memory/manager.d.ts +9 -0
- package/dist/memory/manager.js +59 -0
- package/dist/prompts/systemPrompt.d.ts +5 -0
- package/dist/prompts/systemPrompt.js +177 -0
- package/dist/sessions/manager.d.ts +21 -0
- package/dist/sessions/manager.js +54 -0
- package/dist/skills/manager.d.ts +14 -0
- package/dist/skills/manager.js +86 -0
- package/dist/tools/browserCdp.d.ts +26 -0
- package/dist/tools/browserCdp.js +610 -0
- package/dist/tools/browserExtension.d.ts +58 -0
- package/dist/tools/browserExtension.js +479 -0
- package/dist/tools/browserLive.d.ts +12 -0
- package/dist/tools/browserLive.js +265 -0
- package/dist/tools/changeDir.d.ts +5 -0
- package/dist/tools/changeDir.js +63 -0
- package/dist/tools/computerUse.d.ts +58 -0
- package/dist/tools/computerUse.js +567 -0
- package/dist/tools/createImage.d.ts +1 -0
- package/dist/tools/createImage.js +34 -0
- package/dist/tools/definitions.d.ts +8 -0
- package/dist/tools/definitions.js +401 -0
- package/dist/tools/deleteFile.d.ts +1 -0
- package/dist/tools/deleteFile.js +17 -0
- package/dist/tools/editFile.d.ts +5 -0
- package/dist/tools/editFile.js +88 -0
- package/dist/tools/findFiles.d.ts +1 -0
- package/dist/tools/findFiles.js +33 -0
- package/dist/tools/getSystemInfo.d.ts +1 -0
- package/dist/tools/getSystemInfo.js +23 -0
- package/dist/tools/gitTools.d.ts +3 -0
- package/dist/tools/gitTools.js +47 -0
- package/dist/tools/grepSearch.d.ts +1 -0
- package/dist/tools/grepSearch.js +37 -0
- package/dist/tools/listDir.d.ts +1 -0
- package/dist/tools/listDir.js +33 -0
- package/dist/tools/moveCopyFile.d.ts +1 -0
- package/dist/tools/moveCopyFile.js +21 -0
- package/dist/tools/runCommand.d.ts +1 -0
- package/dist/tools/runCommand.js +61 -0
- package/dist/tools/runner.d.ts +5 -0
- package/dist/tools/runner.js +177 -0
- package/dist/tools/safety.d.ts +17 -0
- package/dist/tools/safety.js +33 -0
- package/dist/tools/todoTool.d.ts +8 -0
- package/dist/tools/todoTool.js +27 -0
- package/dist/tools/toolDefsComputer.d.ts +13 -0
- package/dist/tools/toolDefsComputer.js +205 -0
- package/dist/tools/truncator.d.ts +11 -0
- package/dist/tools/truncator.js +24 -0
- package/dist/tools/viewFile.d.ts +1 -0
- package/dist/tools/viewFile.js +45 -0
- package/dist/tools/visionBridge.d.ts +37 -0
- package/dist/tools/visionBridge.js +107 -0
- package/dist/tools/webFetch.d.ts +1 -0
- package/dist/tools/webFetch.js +25 -0
- package/dist/tools/webSearch.d.ts +1 -0
- package/dist/tools/webSearch.js +35 -0
- package/dist/tools/win/hostClient.d.ts +31 -0
- package/dist/tools/win/hostClient.js +158 -0
- package/dist/tools/win/hostScript.d.ts +1 -0
- package/dist/tools/win/hostScript.js +925 -0
- package/dist/tools/writeFile.d.ts +1 -0
- package/dist/tools/writeFile.js +13 -0
- package/dist/ui/banner.d.ts +10 -0
- package/dist/ui/banner.js +165 -0
- package/dist/ui/diffViewer.d.ts +1 -0
- package/dist/ui/diffViewer.js +60 -0
- package/dist/ui/interrupt.d.ts +17 -0
- package/dist/ui/interrupt.js +156 -0
- package/dist/ui/markdownStream.d.ts +23 -0
- package/dist/ui/markdownStream.js +0 -0
- package/dist/ui/modes.d.ts +28 -0
- package/dist/ui/modes.js +57 -0
- package/dist/ui/permissionPrompt.d.ts +35 -0
- package/dist/ui/permissionPrompt.js +271 -0
- package/dist/ui/prompt.d.ts +7 -0
- package/dist/ui/prompt.js +466 -0
- package/dist/ui/reasoningStream.d.ts +6 -0
- package/dist/ui/reasoningStream.js +38 -0
- package/dist/ui/renderer.d.ts +10 -0
- package/dist/ui/renderer.js +142 -0
- package/dist/ui/spinner.d.ts +24 -0
- package/dist/ui/spinner.js +111 -0
- package/dist/ui/theme.d.ts +36 -0
- package/dist/ui/theme.js +37 -0
- package/dist/ui/typeAhead.d.ts +31 -0
- package/dist/ui/typeAhead.js +87 -0
- package/extension/README.md +74 -0
- package/extension/background.js +575 -0
- package/extension/content.js +446 -0
- package/extension/manifest.json +36 -0
- package/extension/popup.html +22 -0
- package/extension/popup.js +12 -0
- package/package.json +66 -0
- package/skills/complex-refactor/SKILL.md +13 -0
- package/skills/deep-debugger/SKILL.md +14 -0
- package/skills/software-architect/SKILL.md +20 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comentario": "Renombra este archivo a .mcp.json para activarlo. Se conecta al arrancar.",
|
|
3
|
+
"mcpServers": {
|
|
4
|
+
"filesystem": {
|
|
5
|
+
"command": "npx",
|
|
6
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
|
|
7
|
+
},
|
|
8
|
+
"github": {
|
|
9
|
+
"command": "npx",
|
|
10
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
11
|
+
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" }
|
|
12
|
+
},
|
|
13
|
+
"servidor-remoto": {
|
|
14
|
+
"type": "http",
|
|
15
|
+
"url": "https://ejemplo.com/mcp",
|
|
16
|
+
"headers": { "Authorization": "Bearer ${MI_TOKEN}" }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
package/COMPUTER-USE.md
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
# Computer Use en Chocolatito Code
|
|
2
|
+
|
|
3
|
+
Documento de diseño del control de ordenador y navegador. Explica **por qué fallaba**,
|
|
4
|
+
**qué dicen las implementaciones oficiales**, y **cómo está construido ahora**.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Diagnóstico: por qué abría lo que no era y se quedaba trabado
|
|
9
|
+
|
|
10
|
+
Los cuatro fallos eran independientes y se acumulaban. Cualquiera de ellos por sí solo
|
|
11
|
+
bastaba para que el agente hiciera clic en el sitio equivocado.
|
|
12
|
+
|
|
13
|
+
### 1.1 El agente estaba ciego (causa principal)
|
|
14
|
+
|
|
15
|
+
`computer_use(screenshot)` guardaba un PNG y devolvía al modelo un texto como:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
✔ Captura de pantalla tomada (312.4 KB) -> "screenshot_latest.png" [Ventana: "Chrome"].
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
El motor (`deepseek-chat`) es de texto: nunca vio la imagen. Recibía una confirmación de
|
|
22
|
+
que existía un archivo, y nada más.
|
|
23
|
+
|
|
24
|
+
Como el modelo no podía ver, el system prompt le daba coordenadas fijas escritas a mano:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
* Paso 4: haz clic en "New Project" con (action: "click", x: 180, y: 140)
|
|
28
|
+
* Paso 6: haz clic en la barra de prompt con (action: "click", x: 960, y: 920)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Esas coordenadas eran válidas, como mucho, para una resolución concreta, con la ventana
|
|
32
|
+
maximizada, sin barra de marcadores y con esa versión exacta de la interfaz de Flow.
|
|
33
|
+
En cualquier otra situación el clic caía en un sitio arbitrario. **Esto es lo que hacía
|
|
34
|
+
que "siempre abra lo que no es".**
|
|
35
|
+
|
|
36
|
+
El propio `screenshot` reforzaba el error inventando una guía falsa:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
if (winTitle.includes("flow") || winTitle.includes("chrome")) {
|
|
40
|
+
flowGuide = `\n📍 [Guía de Interfaz Detectada]:
|
|
41
|
+
- Botón "New Project": (X: 180, Y: 140)...`;
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
No detectaba nada: imprimía constantes fijas siempre que el título contuviera "chrome".
|
|
46
|
+
|
|
47
|
+
### 1.2 Todas las coordenadas estaban desviadas un 25%
|
|
48
|
+
|
|
49
|
+
Verificado en esta máquina:
|
|
50
|
+
|
|
51
|
+
| Medición | Valor |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `Screen.PrimaryScreen.Bounds` (proceso sin DPI awareness) | **2048 × 1152** |
|
|
54
|
+
| `GetSystemMetrics` con DPI awareness | **2560 × 1440** |
|
|
55
|
+
| Escala de Windows | 125% |
|
|
56
|
+
|
|
57
|
+
El proceso de PowerShell no declaraba DPI awareness, así que Windows le mentía sobre el
|
|
58
|
+
tamaño de la pantalla. La captura se tomaba en el espacio virtualizado de 2048×1152
|
|
59
|
+
mientras que las coordenadas de UI Automation y del escritorio real son de 2560×1440.
|
|
60
|
+
|
|
61
|
+
Resultado: **un desfase sistemático de 1,25×**. Un clic destinado a (2000, 1100) aterrizaba
|
|
62
|
+
en (1600, 880). Cuanto más a la derecha y más abajo estuviera el objetivo, mayor el error.
|
|
63
|
+
|
|
64
|
+
### 1.3 Cada acción tardaba ~1 segundo
|
|
65
|
+
|
|
66
|
+
Cada clic lanzaba un `powershell.exe` nuevo que compilaba tipos C# con `Add-Type` desde
|
|
67
|
+
cero. Medido: 700–1500 ms por acción, sin contar el trabajo real. Una secuencia de 15
|
|
68
|
+
pasos gastaba unos 20 segundos solo en arrancar procesos. De ahí la sensación de que
|
|
69
|
+
"se queda trabado".
|
|
70
|
+
|
|
71
|
+
### 1.4 Peleaba con el usuario por la pantalla
|
|
72
|
+
|
|
73
|
+
Todo pasaba por el primer plano: `SetForegroundWindow`, `SetCursorPos`, `SendKeys`.
|
|
74
|
+
Además cada clic dibujaba un formulario `TopMost` de Windows Forms como puntero láser,
|
|
75
|
+
que consumía 250 ms y podía robar la activación de la ventana justo antes de pulsar.
|
|
76
|
+
|
|
77
|
+
Y `SendKeys` era frágil por partida doble. El escape estaba mal construido:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const escaped = text.replace(/[{}+^%~()\[\\]]/g, "{$&}");
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Esa clase de caracteres no es la que se pretendía, y `SendKeys` además pierde caracteres
|
|
84
|
+
en textos largos y no maneja bien acentos ni emoji.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 2. Referencias oficiales consultadas
|
|
89
|
+
|
|
90
|
+
### 2.1 Anthropic — Computer Use Tool
|
|
91
|
+
|
|
92
|
+
`platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool`
|
|
93
|
+
|
|
94
|
+
Lo que se ha adoptado de la especificación:
|
|
95
|
+
|
|
96
|
+
- **El ciclo es observar → actuar → verificar.** La guía oficial insiste en cerrar cada
|
|
97
|
+
lote de acciones con una captura para que el modelo compruebe el resultado, y en
|
|
98
|
+
pedirle explícitamente que evalúe si el paso salió bien antes de continuar.
|
|
99
|
+
- **Escalado de imagen con factor guardado.** Se captura a resolución nativa, se reduce
|
|
100
|
+
para el envío, y al ejecutar se vuelven a escalar las coordenadas al espacio físico.
|
|
101
|
+
El límite clásico de lado largo es 1568 px; es el que se usa aquí.
|
|
102
|
+
- **Resoluciones recomendadas** de 1024×768 a 1366×768; evitar por encima de 1920×1080.
|
|
103
|
+
- **Diagnóstico de precisión de clic:** un desfase constante indica desajuste de escala
|
|
104
|
+
entre la captura y la pantalla. Es exactamente el fallo 1.2 de este proyecto.
|
|
105
|
+
- **Nomenclatura de teclas:** `Return`, `Escape`, `ctrl+s`, `alt+Tab`.
|
|
106
|
+
- La implementación de referencia (`anthropic-quickstarts/computer-use-demo`,
|
|
107
|
+
`tools/computer.py`) escribe en trozos con retardo por carácter y espera ~2 s tras
|
|
108
|
+
cada acción para que la interfaz se asiente antes de capturar.
|
|
109
|
+
|
|
110
|
+
### 2.2 Microsoft — Playwright MCP
|
|
111
|
+
|
|
112
|
+
`github.com/microsoft/playwright-mcp`
|
|
113
|
+
|
|
114
|
+
De aquí sale la decisión de diseño más importante:
|
|
115
|
+
|
|
116
|
+
> Opera sobre datos estructurados en vez de píxeles. `browser_snapshot` devuelve el árbol
|
|
117
|
+
> de accesibilidad y cada elemento lleva una referencia; se actúa sobre esa referencia.
|
|
118
|
+
> El modo por coordenadas (`browser_mouse_click_xy`) es opcional (`--caps=vision`) y se
|
|
119
|
+
> reserva para lienzos.
|
|
120
|
+
|
|
121
|
+
Es decir: **el enfoque por defecto de la herramienta oficial no es hacer clic en píxeles,
|
|
122
|
+
sino en elementos identificados**. Eso elimina de raíz la ambigüedad que hacía que se
|
|
123
|
+
pulsara el botón equivocado. Este proyecto aplica la misma idea en dos frentes: el árbol
|
|
124
|
+
de UI Automation para el escritorio y el DOM para las páginas web.
|
|
125
|
+
|
|
126
|
+
### 2.3 Chrome DevTools Protocol
|
|
127
|
+
|
|
128
|
+
`Input.dispatchMouseEvent` entrega el evento directamente a la pestaña. No mueve el ratón
|
|
129
|
+
del sistema y no exige que la ventana esté delante. Es la base técnica del modo en
|
|
130
|
+
segundo plano para navegador.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 3. Arquitectura actual
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
┌──────────────────────────────┐
|
|
138
|
+
│ bucle del agente │
|
|
139
|
+
└──────┬───────────────┬───────┘
|
|
140
|
+
│ │
|
|
141
|
+
computer_use browser
|
|
142
|
+
(escritorio) (páginas web)
|
|
143
|
+
│ │
|
|
144
|
+
┌─────────┴────────┐ │
|
|
145
|
+
│ host persistente │ │ puppeteer.connect
|
|
146
|
+
│ PowerShell + C# │ │ (CDP, puerto 9222)
|
|
147
|
+
│ · DPI-aware │ │
|
|
148
|
+
│ · UI Automation │ ▼
|
|
149
|
+
│ · SendInput │ Chrome real
|
|
150
|
+
│ · PrintWindow │ (perfil con sesión)
|
|
151
|
+
└──────────────────┘
|
|
152
|
+
│
|
|
153
|
+
visionBridge → deepseek-v4-flash-vision-exp
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 3.1 Host persistente de Windows
|
|
157
|
+
|
|
158
|
+
`src/tools/win/hostScript.ts` + `src/tools/win/hostClient.ts`
|
|
159
|
+
|
|
160
|
+
Un único `powershell.exe` por sesión que compila los tipos nativos al arrancar y después
|
|
161
|
+
atiende comandos JSON por stdin, respondiendo `CHOCO#<id>#<json>`.
|
|
162
|
+
|
|
163
|
+
**Medido:** arranque 606 ms, y después **12–246 ms por acción** (frente a 700–1500 ms antes).
|
|
164
|
+
|
|
165
|
+
Lo primero que hace, antes de medir nada:
|
|
166
|
+
|
|
167
|
+
```csharp
|
|
168
|
+
SetProcessDpiAwarenessContext(new IntPtr(-4)); // PER_MONITOR_AWARE_V2
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Con eso hay un único sistema de coordenadas en píxeles físicos y desaparece el desfase
|
|
172
|
+
del 25%.
|
|
173
|
+
|
|
174
|
+
### 3.2 Árbol de accesibilidad: el camino por defecto
|
|
175
|
+
|
|
176
|
+
`ui_snapshot` recorre en anchura el árbol de UI Automation de una ventana y devuelve sus
|
|
177
|
+
controles reales:
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
SNAPSHOT DE "*.env.local: Bloc de notas" (ventana 1800x941 en 280,280)
|
|
181
|
+
|
|
182
|
+
[1] Document "Editor de texto" = "NEXT_PUBLIC_SUPABASE_URL=https://..." @(1180, 773)
|
|
183
|
+
[11] MenuItem "Archivo" @(335, 352)
|
|
184
|
+
[12] MenuItem "Editar" @(422, 352)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Después `ui_click(ref: 11)` activa ese elemento mediante `InvokePattern`, que **no mueve
|
|
188
|
+
el ratón, no roba el foco y no depende de ninguna coordenada**. Si no hay `InvokePattern`
|
|
189
|
+
se prueban `SelectionItem`, `Toggle` y `ExpandCollapse` en ese orden.
|
|
190
|
+
|
|
191
|
+
Ventaja añadida: al ser texto, funciona con un motor sin visión y cuesta muy pocos tokens.
|
|
192
|
+
|
|
193
|
+
### 3.3 Captura de una ventana tapada
|
|
194
|
+
|
|
195
|
+
`PrintWindow(hwnd, hdc, PW_RENDERFULLCONTENT)` pide a la ventana que se redibuje en
|
|
196
|
+
nuestro contexto gráfico. Funciona con la ventana detrás de otras o sin foco. Si el
|
|
197
|
+
resultado sale casi negro (le pasa a Chrome y a apps Electron por composición en GPU),
|
|
198
|
+
se recurre automáticamente a copiar esa región de la pantalla.
|
|
199
|
+
|
|
200
|
+
Verificado: captura completa y legible de Chrome sin traerlo al frente ni una sola vez.
|
|
201
|
+
|
|
202
|
+
### 3.4 Entrada en segundo plano
|
|
203
|
+
|
|
204
|
+
| Modo | Mecanismo | Mueve el ratón | Roba el foco |
|
|
205
|
+
|---|---|---|---|
|
|
206
|
+
| `ui_click` / `ui_type` | Patrones de UI Automation | No | No |
|
|
207
|
+
| `mode: "background"` | `PostMessage` a la ventana | No | No |
|
|
208
|
+
| `mode: "foreground"` | `SendInput` absoluto | Sí | Sí |
|
|
209
|
+
|
|
210
|
+
`SendInput` sustituye a `SendKeys`: con `KEYEVENTF_UNICODE` escribe cualquier carácter
|
|
211
|
+
—acentos, eñes, emoji— sin escapes y sin perder caracteres.
|
|
212
|
+
|
|
213
|
+
### 3.5 Navegador por CDP
|
|
214
|
+
|
|
215
|
+
`src/tools/browserCdp.ts`
|
|
216
|
+
|
|
217
|
+
`browser(snapshot)` recorre el DOM de todos los frames **y atraviesa shadow roots**
|
|
218
|
+
(imprescindible en apps modernas de Google), marca cada elemento con `data-choco-ref` y
|
|
219
|
+
devuelve una lista con su nombre accesible:
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
PAGINA: "DuckDuckGo - Protección. Privacidad. Tranquilidad."
|
|
223
|
+
|
|
224
|
+
[1] textbox "Buscar con DuckDuckGo"
|
|
225
|
+
[3] button "Buscar"
|
|
226
|
+
[12] button "Búsqueda Privada" [hay que hacer scroll]
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
`browser(type, ref: 1, text: "...", submit: true)` escribe y envía por CDP. Verificado de
|
|
230
|
+
extremo a extremo: la búsqueda se ejecutó y el snapshot siguiente confirmó la página de
|
|
231
|
+
resultados, con Chrome en segundo plano en todo momento.
|
|
232
|
+
|
|
233
|
+
**Sesión de Google — límite importante.** Desde **Chrome 136** Google bloquea
|
|
234
|
+
`--remote-debugging-port` cuando se usa el directorio de perfil por defecto. Es una
|
|
235
|
+
medida de seguridad deliberada, no un fallo de configuración. Consecuencia práctica:
|
|
236
|
+
|
|
237
|
+
> **`browser` NO puede engancharse al Chrome del usuario ni a sus cuentas ya iniciadas.**
|
|
238
|
+
|
|
239
|
+
Por eso hay dos caminos, y elegir bien es lo que decide si la tarea funciona:
|
|
240
|
+
|
|
241
|
+
| Situación | Herramienta |
|
|
242
|
+
|---|---|
|
|
243
|
+
| Hace falta la sesión del usuario (Flow, su Gmail, su panel) | `computer_use` sobre su Chrome |
|
|
244
|
+
| Web pública, cuenta del propio agente, `localhost`, scraping | `browser` (CDP) |
|
|
245
|
+
|
|
246
|
+
`browser` usa un perfil propio en `~/.chocolatito/chrome-profile`. Si necesita una cuenta,
|
|
247
|
+
**la inicia el usuario a mano una vez**. El agente nunca pide ni escribe credenciales.
|
|
248
|
+
|
|
249
|
+
La opción `useSystemProfile: true` sigue existiendo pero solo sirve en Chrome anterior
|
|
250
|
+
a 136; en versiones actuales la propia Chrome rechaza el puerto.
|
|
251
|
+
|
|
252
|
+
Se lanza con `--disable-background-timer-throttling`,
|
|
253
|
+
`--disable-backgrounding-occluded-windows` y `--disable-renderer-backgrounding`: sin
|
|
254
|
+
esas banderas Chrome congela temporizadores y renderizado cuando la ventana no está
|
|
255
|
+
delante, que es justo como se va a usar.
|
|
256
|
+
|
|
257
|
+
### 3.7 Conducir el Chrome del usuario (con su sesión)
|
|
258
|
+
|
|
259
|
+
Este es el camino para todo lo que dependa de una cuenta ya iniciada. Verificado de
|
|
260
|
+
extremo a extremo contra Google Flow.
|
|
261
|
+
|
|
262
|
+
**UI Automation sí expone el contenido de las páginas de Chrome.** No solo la barra de
|
|
263
|
+
direcciones y las pestañas: también botones, enlaces y menús del propio sitio, con su
|
|
264
|
+
texto real y coordenadas físicas correctas.
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
computer_use(open_app, appName: "https://labs.google/fx/tools/flow")
|
|
268
|
+
→ abre en el navegador por defecto, con el perfil y la sesión del usuario
|
|
269
|
+
|
|
270
|
+
computer_use(ui_snapshot, window: "Flow")
|
|
271
|
+
→ [34] Button "add_2 Proyecto nuevo" @(1280, 1231)
|
|
272
|
+
[8] Button "Tomas Diego" ← confirma que la sesión está iniciada
|
|
273
|
+
|
|
274
|
+
computer_use(ui_click, ref: 34)
|
|
275
|
+
→ Activado (via invoke, sin mover el ratón)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### Escribir en una web: por qué `ValuePattern` no vale
|
|
279
|
+
|
|
280
|
+
Es la trampa más importante de todo el sistema, y costó descubrirla en vivo.
|
|
281
|
+
|
|
282
|
+
`ValuePattern.SetValue` escribe en el DOM, y el texto **se ve** en pantalla. Pero React
|
|
283
|
+
—y cualquier framework con estado controlado— no recibe los eventos de teclado, así que
|
|
284
|
+
su estado interno sigue vacío. Resultado observado en Flow: el prompt aparecía escrito
|
|
285
|
+
en el campo, y al pulsar Crear la aplicación respondía **"Se debe proporcionar una
|
|
286
|
+
instrucción"**. El botón de enviar seguía marcado como deshabilitado.
|
|
287
|
+
|
|
288
|
+
Por eso `ui_type` ahora detecta si la ventana es un navegador y, en ese caso, hace clic
|
|
289
|
+
real sobre el elemento y teclea de verdad, verificando después leyendo el valor del
|
|
290
|
+
control. Solo en aplicaciones nativas de escritorio usa `ValuePattern`.
|
|
291
|
+
|
|
292
|
+
Aviso relacionado: **no limpies un campo web con `ctrl+a` + `Delete` antes de escribir.**
|
|
293
|
+
Al vaciarse, React vuelve a montar el composer, el foco se pierde y las pulsaciones
|
|
294
|
+
siguientes caen sobre la página, donde pueden disparar atajos de teclado. Observado: se
|
|
295
|
+
activó solo el modo "Agente" de Flow. Haz clic y escribe directamente.
|
|
296
|
+
|
|
297
|
+
#### Búsqueda tolerante
|
|
298
|
+
|
|
299
|
+
Dos correcciones sobre `find_element`, ambas surgidas de fallos reales:
|
|
300
|
+
|
|
301
|
+
- **Consulta en lenguaje natural.** El filtro del árbol es coincidencia de subcadena, así
|
|
302
|
+
que `"el botón Imagen del menú inferior"` no encontraba nada y caía a la visión, que
|
|
303
|
+
devolvió (872, 950) cuando el control estaba en (1352, 894). Ahora se prueba la frase
|
|
304
|
+
completa y después palabra por palabra, descartando palabras vacías.
|
|
305
|
+
- **Tildes.** Buscar `"Imagenes"` no encontraba `"Imágenes"`. Toda comparación pasa ahora
|
|
306
|
+
por una normalización que quita diacríticos.
|
|
307
|
+
|
|
308
|
+
La lección general: **la visión estima coordenadas mal.** Sirve para *entender* una
|
|
309
|
+
pantalla, no para apuntar. Para apuntar, el árbol de accesibilidad.
|
|
310
|
+
|
|
311
|
+
### 3.6 Puente de visión
|
|
312
|
+
|
|
313
|
+
`src/tools/visionBridge.ts`
|
|
314
|
+
|
|
315
|
+
La cuenta tiene `deepseek-v4-flash-vision-exp`, multimodal. La captura se le manda y
|
|
316
|
+
vuelve como texto que el motor principal sí entiende:
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
LO QUE SE VE:
|
|
320
|
+
Aplicación: DuckDuckGo. Pantalla: página de inicio en español.
|
|
321
|
+
El campo de búsqueda está vacío, con el placeholder "Busca de manera privada".
|
|
322
|
+
Elementos accionables: Campo de texto..., Botón "Buscar", Botón "Duck.ai"...
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Coste medido: ~500 tokens de entrada por captura de 1568 px de lado largo.
|
|
326
|
+
|
|
327
|
+
Detalle importante: es un modelo que razona antes de responder. Si se agota el
|
|
328
|
+
presupuesto de tokens razonando, `content` llega vacío. Por eso hay margen amplio
|
|
329
|
+
(1600 tokens) y, si aun así llega vacío, se aprovecha `reasoning_content`.
|
|
330
|
+
|
|
331
|
+
`find_element` combina ambos mundos: primero busca en el árbol de accesibilidad (exacto y
|
|
332
|
+
gratis) y solo si no encuentra nada recurre a la visión, validando que el punto devuelto
|
|
333
|
+
caiga dentro de la imagen antes de darlo por bueno.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## 4. Cómo se usa
|
|
338
|
+
|
|
339
|
+
### Página web (Google Flow, paneles, formularios)
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
browser(attach)
|
|
343
|
+
browser(tabs) → ver qué hay abierto
|
|
344
|
+
browser(select_tab, match: "flow")
|
|
345
|
+
browser(snapshot) → [7] button "New project"
|
|
346
|
+
browser(click, ref: 7)
|
|
347
|
+
browser(wait_for, text: "Prompt")
|
|
348
|
+
browser(snapshot, filter: "prompt") → [3] textbox "Escribe tu idea"
|
|
349
|
+
browser(type, ref: 3, text: "...", submit: true)
|
|
350
|
+
browser(snapshot) → verificar
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Aplicación de escritorio
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
computer_use(list_windows)
|
|
357
|
+
computer_use(ui_snapshot, window: "Blender") → [14] Button "Render"
|
|
358
|
+
computer_use(ui_click, ref: 14)
|
|
359
|
+
computer_use(ui_snapshot, window: "Blender") → verificar
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Cuando no hay árbol accesible (lienzos, vídeo, juegos)
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
computer_use(screenshot, window: "Flow", question: "¿Terminó de generar el vídeo?")
|
|
366
|
+
computer_use(find_element, window: "Flow", query: "botón de descarga")
|
|
367
|
+
computer_use(click, window: "Flow", coordinate: [x, y]) ← solo con las coords que dio find_element
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## 5. Reglas de seguridad incorporadas
|
|
373
|
+
|
|
374
|
+
- **`ui_type` nunca arrasa un documento.** `ValuePattern.SetValue` sustituye *todo* el
|
|
375
|
+
contenido de un control. En un campo de una línea es lo deseable; en un editor de texto
|
|
376
|
+
completo borra el documento entero. Los controles de tipo `Document` se escriben
|
|
377
|
+
enfocando y tecleando (inserta en el cursor) salvo que se pase `replace: true`
|
|
378
|
+
explícitamente.
|
|
379
|
+
- **`open_app` espera a que la ventana exista** antes de devolver el control. Devolver
|
|
380
|
+
antes era lo que hacía que la acción siguiente cayera en la ventana anterior.
|
|
381
|
+
- **`focus_window` verifica el resultado real** con `GetForegroundWindow` en vez de fiarse
|
|
382
|
+
del valor de retorno de `SetForegroundWindow`, que miente cuando Windows bloquea el
|
|
383
|
+
cambio de primer plano.
|
|
384
|
+
- **`browser(close)` no cierra el Chrome del usuario**: si la conexión era a un Chrome
|
|
385
|
+
existente, solo se desconecta.
|
|
386
|
+
- **La visión valida sus propias coordenadas**: un punto fuera de los límites de la imagen
|
|
387
|
+
se trata como alucinación, no como acierto.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## 6. Antes y después
|
|
392
|
+
|
|
393
|
+
| | Antes | Ahora |
|
|
394
|
+
|---|---|---|
|
|
395
|
+
| Coordenadas | Fijas en el prompt, desviadas 25% por DPI | Del árbol de accesibilidad o del DOM, en píxeles físicos |
|
|
396
|
+
| Qué ve el modelo | "Captura guardada, 312 KB" | Lista de controles con texto exacto, o descripción de la imagen |
|
|
397
|
+
| Latencia por acción | 700–1500 ms | 12–246 ms |
|
|
398
|
+
| Pantalla del usuario | Ocupada, con overlay que se pegaba | Libre: se actúa en segundo plano |
|
|
399
|
+
| Escritura | `SendKeys` con escapes rotos | `SendInput` Unicode / `ValuePattern` / CDP |
|
|
400
|
+
| Sesión de Google | Sandbox aislado sin cookies | Perfil persistente con sesión real |
|
|
401
|
+
| Verificación | Ninguna | Snapshot obligatorio después de cada acción |
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## 7. Archivos
|
|
406
|
+
|
|
407
|
+
| Archivo | Contenido |
|
|
408
|
+
|---|---|
|
|
409
|
+
| `src/tools/win/hostScript.ts` | Host PowerShell + C#: DPI, ventanas, UIA, captura, entrada |
|
|
410
|
+
| `src/tools/win/hostClient.ts` | Gestión del proceso persistente y protocolo JSON |
|
|
411
|
+
| `src/tools/computerUse.ts` | Herramienta de escritorio |
|
|
412
|
+
| `src/tools/browserCdp.ts` | Herramienta de navegador por CDP |
|
|
413
|
+
| `src/tools/visionBridge.ts` | Traducción de imagen a texto |
|
|
414
|
+
| `src/tools/toolDefsComputer.ts` | Esquemas de ambas herramientas |
|
|
415
|
+
| `src/prompts/systemPrompt.ts` | Bloque 5: reglas de observar → actuar → verificar |
|
|
416
|
+
|
|
417
|
+
`src/tools/browserLive.ts` queda como código muerto: el nombre `browser_live` sigue
|
|
418
|
+
aceptándose por compatibilidad, pero se redirige a `browserCdp`. Se puede borrar.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## 8. La extensión de Chrome: trabajo real en segundo plano
|
|
423
|
+
|
|
424
|
+
Las dos vías anteriores fallan justo donde importa:
|
|
425
|
+
|
|
426
|
+
| Vía | ¿Usa tus sesiones? | ¿Segundo plano real? |
|
|
427
|
+
|---|---|---|
|
|
428
|
+
| CDP con puerto de depuración (`browser`) | No — Chrome 136+ lo bloquea en el perfil por defecto | Sí, pero en un perfil vacío |
|
|
429
|
+
| UI Automation + ratón y teclado (`computer_use`) | Sí | **No** — para escribir hay que traer la ventana al frente |
|
|
430
|
+
| **Extensión (`chrome`)** | **Sí** | **Sí** |
|
|
431
|
+
|
|
432
|
+
`computer_use` sirve para leer y pulsar en segundo plano, pero escribir en una web exige
|
|
433
|
+
foco real. Si el usuario está jugando, eso le saca del juego. La extensión resuelve el
|
|
434
|
+
problema por construcción: corre *dentro* del navegador, así que no necesita foco ni
|
|
435
|
+
ratón, y hereda la sesión porque **es** el Chrome del usuario.
|
|
436
|
+
|
|
437
|
+
### Cómo se ve mientras trabaja
|
|
438
|
+
|
|
439
|
+
- La pestaña entra en un **grupo naranja "Chocolatito Code"** en la barra de pestañas.
|
|
440
|
+
- En la página aparece un **aviso flotante** abajo a la derecha, en shadow DOM para que
|
|
441
|
+
ni la página lo rompa ni él ensucie la página, con lo que está haciendo en cada
|
|
442
|
+
momento.
|
|
443
|
+
- `chrome(done)` deshace el grupo y retira el aviso.
|
|
444
|
+
|
|
445
|
+
### Verificado
|
|
446
|
+
|
|
447
|
+
Abrió el proyecto de Flow en una pestaña de fondo, con la sesión del usuario, y leyó su
|
|
448
|
+
contenido. La ventana en primer plano fue la misma antes y después:
|
|
449
|
+
|
|
450
|
+
```
|
|
451
|
+
ANTES: Ventana activa: "Extensiones - Google Chrome"
|
|
452
|
+
→ chrome(open, url) + chrome(snapshot) sobre el proyecto de Flow
|
|
453
|
+
DESPUES: Ventana activa: "Extensiones - Google Chrome"
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Trampas encontradas al construirla
|
|
457
|
+
|
|
458
|
+
- **Chrome 137 eliminó `--load-extension`.** Hay que cargarla por la UI. Y si se pasa
|
|
459
|
+
`--disable-extensions-except`, Chrome desactiva *todas* las extensiones, incluida la
|
|
460
|
+
que se acaba de cargar: eso costó varios intentos de depuración.
|
|
461
|
+
- **Un `.click()` desde JS no abre diálogos de archivo.** Chrome exige un gesto de
|
|
462
|
+
usuario de confianza; hay que usar entrada CDP o un clic real.
|
|
463
|
+
- **Los diálogos nativos no se rellenan con `ValuePattern`.** Ponen el texto pero el
|
|
464
|
+
diálogo no lo resuelve como ruta. Hay que enfocar y teclear de verdad.
|
|
465
|
+
- **El título del diálogo cambia entre versiones de Chrome** ("Seleccionar el directorio
|
|
466
|
+
de extensión." frente a "Selecciona el directorio de la extensión."). Nunca dependas
|
|
467
|
+
de un título literal: usa `list_windows` y quédate con el `hwnd`.
|
|
468
|
+
|
|
469
|
+
### Regla de seguridad que salió de un accidente real
|
|
470
|
+
|
|
471
|
+
Buscar la ventana `"Google Chrome"` coincide con **todas** las ventanas de Chrome. El
|
|
472
|
+
desempate era por área, así que eligió la principal del usuario y un `alt+F4` la cerró
|
|
473
|
+
con todas sus pestañas.
|
|
474
|
+
|
|
475
|
+
Ahora `focus_window` **se niega a actuar si la consulta es ambigua**:
|
|
476
|
+
|
|
477
|
+
```
|
|
478
|
+
Error en computer_use (focus_window): "Google Chrome" coincide con 2 ventanas
|
|
479
|
+
(CSP - 1er Ciclo 26-2 - Presentación - Google Chrome | Nueva pestaña - Google Chrome).
|
|
480
|
+
Es ambiguo y no voy a elegir por ti: usa list_windows y pasa un titulo mas concreto.
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Elegir a ciegas entre candidatos empatados no es una comodidad: es cómo se acaba
|
|
484
|
+
mandando una pulsación destructiva a la ventana equivocada.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chocolatito
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|