@jossuealcala/madre 0.5.2 → 0.7.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/CHANGELOG.md +265 -1
- package/README.md +33 -1
- package/docs/SDK.md +173 -6
- package/docs/sdk/connector-module.mjs +186 -0
- package/package.json +6 -2
- package/public/abduction.js +384 -0
- package/public/app.js +3450 -540
- package/public/assets/abduction-cat-blink.png +0 -0
- package/public/assets/abduction-cat-control-left.png +0 -0
- package/public/assets/abduction-cat-control-right.png +0 -0
- package/public/assets/abduction-cat.png +0 -0
- package/public/es.js +354 -54
- package/public/index.html +65 -19
- package/public/routing.js +74 -0
- package/public/styles.css +788 -77
- package/src/adapters/claude.mjs +6 -2
- package/src/adapters/gemini.mjs +26 -5
- package/src/adapters/madre.mjs +5 -0
- package/src/choices.mjs +64 -0
- package/src/dataset.mjs +11 -6
- package/src/distiller.mjs +12 -1
- package/src/file-ops.mjs +45 -0
- package/src/lease.mjs +37 -2
- package/src/mcp/smtp-server.mjs +189 -0
- package/src/memory-dashboard.mjs +69 -0
- package/src/memory.mjs +222 -18
- package/src/modules/ahp.mjs +1 -1
- package/src/modules/ash-policy.mjs +67 -0
- package/src/modules/ash.mjs +1 -1
- package/src/modules/choices.mjs +34 -0
- package/src/modules/correo.mjs +107 -0
- package/src/modules/index.mjs +48 -4
- package/src/modules/playwright.mjs +5 -1
- package/src/modules/ripley.mjs +13 -1
- package/src/modules/sdk.mjs +44 -9
- package/src/outbound.mjs +37 -3
- package/src/ports.mjs +91 -0
- package/src/request-guard.mjs +26 -0
- package/src/room/archivist.mjs +2 -1
- package/src/room/economy.mjs +83 -1
- package/src/room/prompt.mjs +14 -2
- package/src/room.mjs +150 -18
- package/src/sentinel-errors.mjs +5 -1
- package/src/server.mjs +188 -8
- package/src/smtp.mjs +122 -0
- package/src/vault.mjs +100 -0
- package/src/whats-new.mjs +140 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,10 +4,274 @@ Todas las versiones publicadas de `@jossuealcala/madre`. Fechas en ISO.
|
|
|
4
4
|
|
|
5
5
|
Una versión se cierra cuando está en npm: hasta entonces su sección se llama **Sin publicar** y puede crecer. Cada versión publicada tiene exactamente una etiqueta `vX.Y.Z`, una release en GitHub y una sección aquí; el parche puede llegar a dos dígitos (`0.2.10`) antes de subir el menor. Ver `docs/ROADMAP.md` para el criterio de qué sube cada número.
|
|
6
6
|
|
|
7
|
-
## 0.
|
|
7
|
+
## 0.7.1 · Sin publicar
|
|
8
|
+
|
|
9
|
+
## 0.7.0 · 2026-10-03
|
|
10
|
+
|
|
11
|
+
Esta versión es sobre la memoria de la sala: ahora la ves, la recorres y te dice cómo está. NOSTROMO pasa a tres dimensiones y cada memoria es una estrella que cuenta su propia historia; MEMORIA se vuelve un tablero; y quien llega por primera vez encuentra un recorrido sobre la sala misma. También trae ideas listas para crear tus propios módulos, un botón para detener una sola respuesta y una protección nueva frente a páginas web ajenas. Gracias por usar MADRE y por cada reporte que la hace mejor.
|
|
12
|
+
|
|
13
|
+
### Después de actualizar, MADRE te cuenta qué hay de nuevo
|
|
14
|
+
|
|
15
|
+
- La primera vez que abres la sala tras una actualización aparece una hoja con lo principal de la versión: cada novedad dicha en una frase, **dónde está**, y un enlace a todos los cambios en madre.run. Se cierra con **Continuar** y no vuelve a salir para esa versión.
|
|
16
|
+
- Se recuerda por persona y no por pestaña: si abres la sala en otro puerto, ya la viste. Una instalación nueva no la ve nunca; para eso está el recorrido de bienvenida, que espera a que cierres la hoja si los dos tocan el mismo día.
|
|
17
|
+
- No sale nada a la red: el enlace es la misma página de cambios que ya usaba el canal de releases.
|
|
18
|
+
- Cada versión menor trae la suya, escrita a mano en los dos idiomas de la sala. Una versión menor no se puede cerrar sin ella.
|
|
19
|
+
- Para volver a verla: **ACERCA DE ESTA VERSIÓN**, junto al número en el canal de releases de MU/TH/UR.
|
|
20
|
+
|
|
21
|
+
### El recorrido de bienvenida se hace sobre la sala
|
|
22
|
+
|
|
23
|
+
- La primera vez que alguien entra, el recorrido ya no es texto en una ventana: recorre **la sala misma**. Cuatro paradas, cada una ilumina la parte de la pantalla de la que habla y deja el resto en penumbra, con una tarjeta al lado que la explica en dos o tres frases.
|
|
24
|
+
- **Tu tripulación, una sola caja** (la fila de agentes y el campo de texto) · **Los modos** (el selector de modo del mensaje), los cinco en orden y cada uno en su color · **MU/TH/UR, la consola** (conexiones, memoria, NOSTROMO, diagnóstico) · **MÓDULOS** (poderes opcionales).
|
|
25
|
+
- Y la primera vez que se presiona el **navegador**, los **archivos del proyecto** o las **conversaciones**, el panel se abre como siempre y una tarjeta junto al botón dice para qué sirve. Una vez cada uno.
|
|
26
|
+
- Se vuelve a abrir desde **?** en MU/TH/UR. Si un área no está a la vista, la tarjeta la explica en el centro.
|
|
27
|
+
|
|
28
|
+
### NOSTROMO en tres dimensiones
|
|
29
|
+
|
|
30
|
+
- El archivo es un cerebro de verdad: el perfil de antes visto de lado, y de frente dos hemisferios separados por la cisura. **Arrastra para girarlo**; **Shift + arrastra** para mover la cámara; **RECENTRAR** vuelve a la vista de lado.
|
|
31
|
+
- Lo cercano se ve más grande, lo que queda detrás de MOTHER se dibuja detrás de ella y no se puede tocar a través de ella, y de dos memorias encimadas gana la de enfrente.
|
|
32
|
+
- Las memorias se reparten parejo por toda la superficie, cada clase en una franja propia de adelante hacia atrás, como lóbulos.
|
|
33
|
+
|
|
34
|
+
### Cada memoria es una estrella enana, y su superficie dice cómo está
|
|
35
|
+
|
|
36
|
+
- Una enana tiene luz propia, así que no hay lado oscuro: brilla más en el centro y se apaga hacia el borde —más en azul que en rojo, así que el borde es más cálido—, su superficie hierve en gránulos, y la rodea un resplandor que nace en el borde.
|
|
37
|
+
- **El brillo** sigue diciendo cuánto la usa la sala y lo que está recibiendo ahora.
|
|
38
|
+
- **La superficie** dice en qué estado está: limpia y con fáculas brillantes si la confirmaste; con las manchas propias de su clase si nadie la ha juzgado; muy manchada si resultó falsa; la peor de todas si salió de circulación. Una memoria que la sala nunca ha usado se está **enfriando**: su granulación casi no se mueve, da menos luz y su centro deja de ser blanco.
|
|
39
|
+
- Cada clase tiene su carácter: la amarilla es un sol con gránulos y manchas, la blanca es lisa e intensísima.
|
|
40
|
+
|
|
41
|
+
### MOTHER es magma
|
|
42
|
+
|
|
43
|
+
- El centro es una costra de roca casi negra partida en placas, con grietas al rojo blanco entre ellas y pozas donde la lava se abrió paso. La costra gira y la lava fluye por debajo a otro ritmo, así que la superficie se mueve en lugar de estar pintada.
|
|
44
|
+
- De vez en cuando algo estalla dentro: un destello, un núcleo blanco y una onda de fuego que corre por la costra. Más seguido cuando MOTHER está alarmada; nunca con «reducir movimiento».
|
|
45
|
+
|
|
46
|
+
### Elegir una memoria la trae al frente
|
|
47
|
+
|
|
48
|
+
- Al hacer clic en una memoria, el archivo gira hasta ponerla de frente, la cámara la centra en el espacio que deja libre la tarjeta y todo lo demás se oscurece. Quedan encendidos ella, sus enlaces y una **mira verde**: corchetes que se cierran sobre el blanco, un anillo que gira y la marca **BLANCO FIJADO**.
|
|
49
|
+
- Al pasar el cursor, la mira solo apunta. Al cerrar la tarjeta, la escena vuelve y el archivo se queda girado donde lo dejaste.
|
|
50
|
+
|
|
51
|
+
### Olvidar es una implosión
|
|
52
|
+
|
|
53
|
+
- Una memoria o una aberración olvidada no se apaga: el universo se la traga. Se hincha una última vez, se abre un horizonte con la luz curvándose en su borde, lo que la formaba cae en espiral, desaparece con un destello y una onda que se pierde en la oscuridad.
|
|
54
|
+
- Sus enlaces, y el cable que la unía a MOTHER, se deshacen en polvo desde su extremo hacia afuera, y el polvo se dispersa y se apaga.
|
|
55
|
+
|
|
56
|
+
### MEMORIA es un tablero
|
|
57
|
+
|
|
58
|
+
- Cifras del archivo arriba —intercambios, memorias, turnos que recordaron, en espera, madurez— con lo que pasó esta semana contra la anterior.
|
|
59
|
+
- La actividad de los últimos 30 días día por día, lo que hace al archivo (las seis lecturas de madurez), de qué está hecho por clase, archivista y zona, cuánto llega por búsqueda o por asociación, y las tres pruebas con su barra hasta donde pasan.
|
|
60
|
+
- Debajo, en pliegues propios, **ARCHIVISTA Y RECALL** y **DATASET Y ENTRENAMIENTO**. El tablero se lee sin la designación del proyecto: son conteos, y ningún texto de una memoria sale por ahí.
|
|
61
|
+
|
|
62
|
+
### Una página de otro sitio ya no puede meter un módulo en la sala
|
|
63
|
+
|
|
64
|
+
- La sala escucha en `127.0.0.1`, y eso deja fuera a las otras computadoras pero no a las otras páginas: cualquier sitio abierto en tu navegador podía mandar una petición a `127.0.0.1:4317`, y un módulo subido es código que MADRE corre con tus permisos. Bastaba con visitar la página equivocada mientras la sala estaba abierta.
|
|
65
|
+
- Ahora, antes de cualquier ruta, la sala revisa dos cosas. Que la llamen por el nombre de esta computadora (`127.0.0.1` o `localhost`): una página que apunta su propio dominio a `127.0.0.1` sigue mandando su dominio y se queda en la puerta, también para leer. Y que lo que cambia algo venga de la propia sala: el navegador dice quién pregunta, y una página ajena recibe un 403.
|
|
66
|
+
- La terminal, el `curl` de un agente y el propio CLI de MADRE no mandan origen y siguen entrando igual.
|
|
67
|
+
|
|
68
|
+
### AHP+ se instala en 1.4.2
|
|
69
|
+
|
|
70
|
+
- El módulo AHP+ instala ahora `@jossuealcala/ahp-plus@1.4.2`. La 1.4.1 confundía con una llave nombres de archivo como `work-detail-desktop-scrolled.png` y bloqueaba `ahp project verify` sin que hubiera ningún secreto.
|
|
71
|
+
- Un proyecto que ya tiene la 1.4.1 ve el aviso en su tarjeta de MODULES; se actualiza con `npm install -D --save-exact @jossuealcala/ahp-plus@1.4.2` dentro del proyecto.
|
|
72
|
+
|
|
73
|
+
### Una bodega para las llaves de los conectores
|
|
74
|
+
|
|
75
|
+
- Hasta ahora MADRE no guardaba ninguna credencial: la llave de un agente se escribe donde esa CLI la busca y MADRE no conserva copia. **Un conector no tiene CLI a la que dársela**, así que si MADRE no la guarda, nadie lo hace.
|
|
76
|
+
- Ahora la guarda, y con términos escritos: un archivo por módulo en `~/.pulse/credentials/`, permisos de solo tu usuario dentro de un directorio de solo tu usuario, **aceptada únicamente desde esta computadora**. Nunca en la config, ni en el ledger, ni en un log, ni en lo que lee un agente. El ledger anota que se guardó una y con qué nombre y cuántos caracteres — jamás el valor.
|
|
77
|
+
- En la ficha del módulo hay un piso nuevo, **LLAVES**: un campo por secreto, lo que hay guardado (nombre y largo) y `OLVIDARLA`. El campo nunca se rellena de vuelta.
|
|
78
|
+
- Un módulo solo puede pedir **sus** secretos: su id va cerrado dentro del SDK, así que pedir el de otro no es algo que se pueda escribir.
|
|
79
|
+
|
|
80
|
+
### El SDK, cerrado y con qué copiar
|
|
81
|
+
|
|
82
|
+
- **El contrato completo son 36 campos**, todos documentados, y la guía no puede quedarse atrás del código: si alguna vez se separan, lo detectamos antes de publicar. Antes había once campos que el SDK aceptaba y la guía no mencionaba.
|
|
83
|
+
- **Un ejemplo de conector que funciona tal cual** (`docs/sdk/connector-module.mjs`): manda un Telegram por tu propio bot. Enseña la forma que no se adivina — **un solo archivo que es la ficha cuando MADRE lo importa y el servidor MCP cuando MADRE lo ejecuta**.
|
|
84
|
+
- Tres campos declarados que cualquier conector hereda: `secrets` (MADRE dibuja el campo), `reaches` (el registro de salidas responde por ti) y `sends` (la sala retira esas herramientas por debajo de `#4`). Nada de eso se programa: se declara.
|
|
85
|
+
- **«¿Se puede conectar con X?»** se contesta ahora dentro de la guía, en tres peldaños. El límite no es MADRE: es si ese servicio te deja conseguir tu propia llave en minutos y revocarla sola. Casi todo lo útil cae del lado fácil — GitHub, Linear, Notion, Jira, Slack, Telegram, Stripe, Shopify, Odoo, cualquier base de datos. Donde hay muro es donde el proveedor exige revisar tu app: Gmail en lectura, Meta, banca.
|
|
86
|
+
- Y un arreglo que venía roto sin que se notara: `reaches` nunca llegaba al objeto del módulo, así que la declaración de destinos de un conector **no se registraba en ninguna parte**.
|
|
87
|
+
|
|
88
|
+
### Pedir un módulo es más fácil que encontrarlo
|
|
89
|
+
|
|
90
|
+
- `/modules`, `/modulo` o cualquier cosa que empiece por el nombre del comando **ahora encuentra `/module`**. Antes el menú se cerraba y no mostraba nada, que se lee como «no existe» en vez de «casi».
|
|
91
|
+
- Y una vez ahí, el menú ofrece **ocho ideas para editar**, dos de cada forma que puede tomar un módulo: un comando, un conector, un conector que manda y una ficha propia. Cada idea lleva su ícono y una etiqueta del color de su forma. `/module` solo no hace nada hasta que dices qué debe hacer, y esa era la forma más común de que todo se quedara en nada.
|
|
92
|
+
- La tarjeta **DESARROLLA PARA MADRE** dice ahora qué es un módulo, que puedes simplemente pedirlo, y que conectar con algo de fuera casi siempre se puede. Trae un botón **PÍDELE UNO** que te deja en la caja de texto con `/module` escrito.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### En modo claro volvían a verse los acentos
|
|
96
|
+
|
|
97
|
+
- El texto entre mensajes —el relevo, lo que destiló el archivista— valía **2.92:1** contra la página, por debajo del suelo de 3.0 que pide una interfaz, cuando en oscuro ese mismo papel vale 3.62:1. Y la burbuja del agente valía **1.10:1**: no se leía tenue, se leía como página. Ahora 3.87:1 y 1.22:1, que es la proporción del modo oscuro y no un gris nuevo.
|
|
98
|
+
- **Las esferas de los agentes pedían otra cosa.** Un tinte del color funciona sobre una página casi negra porque el color es la única luz del círculo; sobre una casi blanca el mismo tinte se camina de vuelta a la página. Los peores eran los agentes **más brillantes** —1.06:1 y 1.03:1—, y subir el porcentaje no los arregla: mezclar un amarillo brillante con blanco sigue siendo blanco. En claro el disco conserva su tinte y gana borde y glifo con el color llevado **hacia abajo**. Un color, dos direcciones.
|
|
99
|
+
|
|
100
|
+
### El botón de permiso es un botón, y la frase es una frase
|
|
101
|
+
|
|
102
|
+
- Cuando un agente pedía subir de peldaño, el recuadro punteado se dibujaba alrededor de **todo**: un marco y un fondo detrás de una frase que no se puede pulsar. El ojo leía un panel y tenía que buscar el botón dentro.
|
|
103
|
+
- Ahora solo lo que se puede pulsar lo parece, y lo parece de verdad: relleno en vez de contorno.
|
|
104
|
+
|
|
105
|
+
### MEMORIA sale de CONEXIONES
|
|
106
|
+
|
|
107
|
+
- Era un pliegue dentro de CONEXIONES, que es donde vas a conectar y firmar un agente — otro mandado completamente distinto. Qué guarda el archivo, quién lo destila y qué tan bien está contestando es un tema propio.
|
|
108
|
+
- Ahora tiene su botón en la barra de MU/TH/UR, junto a NOSTROMO, y el panel entero en lugar de un pliegue dentro del panel de otro. Los dos destinos se excluyen: uno a la vez.
|
|
109
|
+
- El **canal de releases** vive solo en la pantalla principal de MU/TH/UR, no encima de CONEXIONES ni de MEMORIA.
|
|
110
|
+
|
|
111
|
+
### ABRIR TODO deja de ser un destino
|
|
112
|
+
|
|
113
|
+
- Vivía en la barra, entre CONEXIONES y NOSTROMO, que son sitios a los que se va. Eso no es un sitio: es una acción sobre el lienzo de abajo.
|
|
114
|
+
- Baja a donde actúa: un texto tenue a la derecha, con la misma voz que el «▸ ABRIR» de cada pliegue, sobre la columna que mueve. Sin marco, para no competir con los botones de la barra.
|
|
115
|
+
- Mueve solo los pliegues de la pantalla que estás viendo.
|
|
116
|
+
|
|
117
|
+
### NOSTROMO toma forma de cerebro
|
|
118
|
+
|
|
119
|
+
- El archivo deja de ser una bola. Es un cerebro visto de lado: lóbulo frontal al frente, base plana, cerebelo abajo atrás, temporal abajo adelante.
|
|
120
|
+
- La forma se mantiene mientras el archivo se mueve: las memorias se acomodan sobre el contorno del cerebro en lugar de volver a formar una bola.
|
|
121
|
+
- Y ahora además gira en tres dimensiones: ver «NOSTROMO en tres dimensiones», arriba.
|
|
122
|
+
|
|
123
|
+
### Se puede apadrinar el proyecto
|
|
124
|
+
|
|
125
|
+
- Botón **Sponsor** en el repositorio, `npm fund` lo lista, y una línea al final del README. GitHub no se lleva comisión.
|
|
126
|
+
- En la sala, una palabra en la esquina junto a la versión: `MADRE 0.7.0 · © 2026 Jossue Alcalá · APOYAR`. No interrumpe nunca, no aparece en el hilo y no sabe si hiciste clic.
|
|
127
|
+
- **Es un enlace, no una petición de red.** La sala no le pide nada a nadie por esto, así que no hay dirección que declarar en `outbound.mjs` ni nada que aparezca en el registro de salidas. El día que esto quisiera *traer* algo —un contador de patrocinadores, una insignia— dejaría de ser un enlace y tendría que declararse como cualquier otra dirección, con su interruptor.
|
|
128
|
+
- MADRE es gratis y seguirá siéndolo. Nada del producto cambia según lo que hagas con ese enlace.
|
|
129
|
+
|
|
130
|
+
### Detener una respuesta sin detener la sala
|
|
131
|
+
|
|
132
|
+
- En la fila del turno en vuelo, junto a los segundos, un botón **DETENER**. Mata el proceso de ese agente y nada más: la sala sigue, los demás agentes siguen, y los pasos de otros planes siguen. PARAR TODO continúa siendo lo que era, el freno maestro para cuando las cosas se desbocaron.
|
|
133
|
+
- Hasta ahora, un agente colgado o que arrancó por el camino equivocado costaba parar la sala entera o esperar al límite de tiempo.
|
|
134
|
+
- La parada queda en el ledger como `turn.stopped`, y el turno termina igual que cualquier fallo porque el proceso de verdad se mató — pero **dice que se pidió**. Por eso el centinela ya no la archiva: un freno que tú accionaste no es una condición que nadie tenga que diagnosticar.
|
|
135
|
+
|
|
136
|
+
### El typebox crece con lo que escribes
|
|
137
|
+
|
|
138
|
+
- La caja crecía hasta tres renglones y ahí se quedaba, que es un límite de caja de chat y esto también es donde se escribe un brief largo. Ahora crece hasta catorce y a partir de ahí se desplaza sola.
|
|
139
|
+
- No crece sin fin a propósito: el hilo de arriba es contra lo que se suele escribir, y no debe desaparecer.
|
|
140
|
+
- El salto de línea es **⌥ Enter** — Option en macOS, Alt en Linux y Windows: la misma tecla física y la misma señal en el evento, así que es una sola regla y no una por plataforma. Enter manda, con o sin Mayús. El campo lo dice al pasar el cursor, porque un atajo que cambia bajo los dedos tiene que decirlo en alguna parte.
|
|
141
|
+
|
|
142
|
+
### MU/TH/UR se lee por bloques, no por renglones
|
|
143
|
+
|
|
144
|
+
- Cada sección de la consola lleva ahora su propio icono de 8 bits en vez de la misma barra verde para todas. Una barra decía «aquí empieza un título» y nada más; un dibujo dice **cuál** sección es antes de leer las palabras. Sin recuadro alrededor: un marco sería una segunda caja diciendo lo que el dibujo ya dice.
|
|
145
|
+
- Y se va la línea que había encima de cada bloque: una raya y un icono son dos maneras de anunciar lo mismo, y con una basta. Los títulos suben de tamaño y de brillo, así que la jerarquía la marca el bloque y no el renglón.
|
|
146
|
+
- Lo mismo en CONEXIONES, que es la misma consola por dentro.
|
|
147
|
+
|
|
148
|
+
### La economía deja de estar escrita en clave
|
|
149
|
+
|
|
150
|
+
- `CH` no se explicaba en ninguna parte de esa pantalla, y en español llegó a verse como «CAR POR TOKEN». Ahora se dice **caracteres**, con todas sus letras, y la unidad va una vez en la tarjeta en lugar de estampada en diez filas.
|
|
151
|
+
- `TURNS WEIGHED` se había traducido como «TURNOS PESADOS», que en español significa otra cosa. Son **turnos con factura**.
|
|
152
|
+
- El número grande ya dice de qué son: **tokens que esta sala nunca pagó**. Antes eran quince millones de nada.
|
|
153
|
+
- Cada lectura es una tarjeta con su borde y su icono, y las seis cifras de arriba son seis tarjetas. Lo que estaba saturado era que todo pesaba igual y nada decía dónde terminaba una lectura.
|
|
154
|
+
|
|
155
|
+
### Arreglos
|
|
156
|
+
|
|
157
|
+
- **Los avisos entre mensajes se descentraban.** El título de cada aviso —«claro», «agotada»— conservaba el punto que lo unía al resto de la frase cuando iba en el mismo renglón, y sobre un renglón propio ese punto colgaba del final y lo sacaba del centro. Y cuando un aviso empezaba con palabras sueltas, la primera palabra en negritas se tomaba por título: «local · @madre · qwen» salía en tres renglones y la memoria olvidada partía su frase en tres.
|
|
158
|
+
- **La clase de una memoria olvidada salía en inglés** —«ABERRATION»— en una sala en español, y la frase («se le olvidó un…») no concordaba con clases femeninas. Ahora dice «memoria · olvidada por la humana · ABERRACIÓN».
|
|
159
|
+
|
|
160
|
+
- **Mover el ratón sobre NOSTROMO mientras abría lanzaba un error.** El mapa recibe el ratón desde que aparece y su cámara se crea un instante después; hasta entonces, apuntar, hacer zoom o RECENTRAR buscaban una cámara que aún no existía. Ahora esperan a que el mapa arranque.
|
|
161
|
+
|
|
162
|
+
- **#12 · Gemini intentaba delegar en un subagente y el turno moría a los 180 s.** La política no le permite `invoke_agent`, y hace bien: un subagente contesta fuera del arriendo, así que nada de lo que escribiera quedaría comprobado, mostrado ni reversible. Pero el CLI no trata la negativa como una respuesta: espera, y el humano espera tres minutos para recibir un error. Ahora se le dice de antemano que haga el trabajo él mismo, y si aun así lo intenta, la sala corta el turno en cuanto lo ve y explica por qué. **Solo ante ese bloqueo**, no ante cualquiera: una escritura denegada deja al CLI trabajando y acaba contestando, y cortar ahí mataría turnos que iban a terminar.
|
|
163
|
+
- **Detener una respuesta esperaba a que de verdad terminara.** Abortar solo pide; el turno todavía tiene que matar su proceso y registrar cómo acabó. Devolver antes dejaba al que llama compitiendo con una escritura que aún no había ocurrido — el freno maestro ya esperaba así.
|
|
164
|
+
|
|
165
|
+
- **#11 · Gemini decía «archivos creados» y no escribía nada.** La regla que le da permiso de escritura se construía con la ruta del proyecto **tal como se la pasaron a MADRE**, y el CLI resuelve la ruta antes de escribir. En macOS `/tmp` es un enlace a `/private/tmp`, así que un proyecto abierto como `/tmp/…` pedía escribir en `/private/tmp/…`, la regla no casaba, y un CLI sin cabeza no puede detenerse a preguntar: la escritura moría sin una palabra y el modelo daba el archivo por creado. Ahora el arriendo lleva las dos formas de su ruta y las dos entran en la regla. **También en la de denegación**, o permitir la segunda ortografía habría dejado `.git` y `.pulse` alcanzables por el otro nombre. Afectaba a Gemini y a Claude; Codex usa el directorio de trabajo y OpenCode la ruta relativa, y por eso funcionaban.
|
|
166
|
+
- **#11 · Y si una escritura se pierde por cualquier otra causa, la sala lo dice.** Cuando un turno podía escribir, no escribió nada y la respuesta nombra archivos, se comprueban contra el disco y se avisa de los que no están. No se lee la prosa buscando una afirmación —eso sería adivinar en dos idiomas y avisar en falso— sino las rutas que nombra; lo que existe no se menciona.
|
|
167
|
+
|
|
168
|
+
- **#16 · RIPLEY decía «nada contesta en esta computadora» cuando sí había servidores.** Por dos causas distintas y ninguna era esa. Con RIPLEY apagado, `/api/ports` contestaba `412` con su explicación y el cliente leía `?.ports ?? []` por encima del error: ahora dice lo que la respuesta dice y ofrece abrir MÓDULOS. Y un servidor atado a todas las interfaces —lo que hacen `node` y `python -m http.server` por defecto— se descartaba sin decir nada: ahora aparece al final de la lista, apagado, con su razón al pasar el cursor. En esta misma máquina eran **tres** servidores invisibles.
|
|
169
|
+
- **#16 · El separador colgando.** Un servidor sin `Content-Type` dejaba la fila como `:5173 node · 200 · `.
|
|
170
|
+
- **#15 · La pregunta que `@madre` reenvía al crew empezaba con «:».** El bucle que quita «pregúntale al crew» solo se llevaba los dos puntos cuando detrás venía otra frase de enlace, así que el «:» viajaba al anuncio, a los cuatro agentes y al turno de cierre.
|
|
171
|
+
- **#14 · «NO PUEDE GENERATE IMAGES» en la sala en español.** El servidor mandaba la etiqueta ya armada donde el cliente esperaba la clave, así que no había nada que traducir. Ahora viaja la clave; la etiqueta se queda solo donde se arma una frase en inglés.
|
|
172
|
+
- **#14 · La línea del plan estaba escrita con literales en inglés.** «@MADRE PUTS @CODEX#1 TO WORK, THEN CLOSES» y «FINISHED · 5 STEPS». Los agentes son fichas y no caben dentro de una cadena, así que la frase se traduce entera alrededor de un marcador y las fichas vuelven a donde estaba el marcador.
|
|
173
|
+
|
|
174
|
+
- En tema oscuro, la versión, el enlace de apoyo y el aviso bajo el campo eran ilegibles: el gris más tenue de la paleta con media opacidad encima, sobre un fondo casi negro, no era discreto sino invisible. La firma y el enlace llevan ahora el verde de la sala —que tiene un valor propio en cada fondo, así que un color sirve para los dos temas— y el aviso sube a un gris que se lee. El aviso se queda fuera del verde a propósito: es una advertencia sobre permisos, no parte de la voz de la sala.
|
|
175
|
+
|
|
176
|
+
- La tarjeta **POR AGENTE** de la economía no se había visto nunca: `table.append(…).lastChild` sobre un `append` que devuelve `undefined` lanzaba en cada render y se llevaba por delante el resto del panel, sin decir nada. Está desde que se escribió el panel.
|
|
177
|
+
|
|
178
|
+
### Tu pulgar mueve lo que la sala recordó de esa respuesta
|
|
179
|
+
|
|
180
|
+
- **El archivista ya sabe qué tiene standing y qué se echó.** Hasta ahora recibía las doce notas más recientes bajo un único encabezado que decía «ya recordado, no lo repitas» — fueran lo que fueran. En el archivo de esta sala, tres de esas doce estaban fuera de circulación, y dos eran **aberraciones**: afirmaciones que la sala ya había establecido como falsas, ofrecidas como si fueran conocimiento.
|
|
181
|
+
- Ahora recibe dos listas que dicen cosas opuestas: lo vigente, encabezado por lo que **tú confirmaste**, y lo descartado, con su propio encabezado — «la sala las sacó de circulación, no vuelvas a escribirlas». Deja de reaprender lo que ya echaste.
|
|
182
|
+
- Cuesta unos 205 tokens más por destilación, medidos. Un descarte viaja recortado porque está ahí para reconocerlo, no para aprenderlo, y la lista desaparece entera cuando no hay nada que descartar: un encabezado sin filas debajo es prompt que se paga y del que nadie aprende.
|
|
183
|
+
|
|
184
|
+
- Los pulgares de cada burbuja existían desde hace versiones y llegaban a un solo sitio: el exportador de dataset. Ahora también llegan al archivo. Lo que la sala destiló de una respuesta que marcaste mal deja de viajar a los turnos; lo que marcaste bien pasa a estar confirmado.
|
|
185
|
+
- **No se reescribe una sola palabra de lo que se recordó.** Lo que cambia es dónde queda parado, no lo que dice: el texto es lo que el archivista oyó y la zona es lo que tú dijiste. Por eso una reacción sirve aunque llegue días después de que el lote se destiló.
|
|
186
|
+
- Cada memoria vive ahora en una de cuatro zonas de la NOSTROMO. Dos viajan a los turnos y dos son archivo que la sala conserva y nunca sirve. Las dos reglas que ya hacían esto a medias —lo falso no viaja, lo refutado tampoco— eran cinco copias sueltas y ahora son una.
|
|
187
|
+
- Un pulgar mueve solo lo que cita **esa** respuesta, nunca su lote: en una sala real un lote abarca cientos de secuencias, y un pulgar no puede hablar por intercambios que no miraste. En el archivo de esta sala, un tercio de las respuestas de agente dejaron memoria y son las que un pulgar alcanza.
|
|
188
|
+
- El archivo manda sobre el pulgar: una nota que la sala estableció falsa sigue fuera de circulación por mucho que te gustara la respuesta de la que salió. «Esto es falso» y «esto me sirvió» no son la misma afirmación.
|
|
189
|
+
- Los pulgares que ya habías pulsado antes de que esto existiera cuentan: el pliegue es sobre el ledger entero y corre en cada arranque.
|
|
190
|
+
- Cuatro maneras de juzgar una respuesta, no dos. Al pulgar arriba se le suma **así lo quiero**, y al pulgar abajo **nunca más**. Los dos nuevos no se muestran hasta que pulsas el que escalan: en reposo la burbuja no lleva más adorno que antes, y la palabra más fuerte aparece justo cuando significa algo.
|
|
191
|
+
- «Así lo quiero» confirma la nota y cuenta como un sí para el corpus; «nunca más» la saca de circulación y deja el par fuera del entrenamiento. Una nota destilada de varias respuestas se queda con **el peor** veredicto de todas: si uno de los intercambios de los que salió estaba mal, la nota es sospechosa por buenos que fueran los demás.
|
|
192
|
+
- Y se ve: cada memoria dice su zona en NOSTROMO, y la que no viaja se dibuja apagada en el mapa. Una señal que gobierna el recall sin que puedas mirarla es justo lo que esta sala no hace. Las zonas no estrenan paleta: el color de una estrella es su clase, y lo que la zona cambia es si está encendida.
|
|
193
|
+
|
|
194
|
+
## 0.6.0 · 2026-10-01
|
|
195
|
+
|
|
196
|
+
### Ash conserva una sola decisión de principio a fin
|
|
197
|
+
|
|
198
|
+
- El historial ya no puede encender ni apagar Ash al abrir la sala o cambiar de conversación: `/api/state` manda y solo un `extension.toggled` vivo cambia el compositor. La vista previa usa además la selección del mensaje, no solo el interruptor global.
|
|
199
|
+
- Cada turno conserva la decisión tomada al pulsar Enviar antes del primer `await`: `requested` y `active` quedan separados, `turn.cost.ash` confirma si el bloque llegó al prompt y un fallo posterior conserva el coste y limpia su forma pendiente. Los pasos y el cierre de un plan mantienen el mismo Ash y el mismo modo.
|
|
200
|
+
- La prosa compacta se adapta a longitud explícita, código y estructura pedida. Una comprobación local marca respuestas vacías, límites excedidos, bloques sin cerrar, planes con texto posterior y deriva de literales pedidos como exactos. No cambia modelo, contexto, herramientas ni repite automáticamente un turno con permisos.
|
|
201
|
+
- La ficha pública no promete un porcentaje fijo: muestra entrada, salida y caché por turno, y dice que el ahorro depende del agente, la tarea y la caché. El corpus reproducible de `scripts/bench-ash.mjs` solo verifica el contrato; no se presenta como medición de ahorro.
|
|
202
|
+
|
|
203
|
+
### La economía deja de ser una promesa y pasa a ser una lectura
|
|
204
|
+
|
|
205
|
+
- El panel ECONOMÍA, a pantalla completa desde la ficha de Ash: lo que nunca se cobró, lo que Ash le hace a una respuesta, a dónde va cada prompt y una tabla por agente. Abre con seis cifras del total —turnos, entrada, salida, no cobrados, caché y caracteres por token— porque seis números de seis naturalezas distintas no tienen forma que dibujar.
|
|
206
|
+
- Cada mensaje dice en su pie cuántos tokens **no** se cobraron: lo que el CLI leyó de su propia caché (medido) más el briefing que la sala nunca envió (estimado). Las dos mitades nunca se suman sin decir cuál es cuál.
|
|
207
|
+
- Ash muestra primero lo que **cuesta** —306 caracteres por turno, medidos del bloque— y después lo que podría ahorrar. Y cuando hay cinco turnos de cada lado, la lectura dice si la diferencia supera la variación propia de ese agente o si todavía no hay nada que leer. Una lectura que solo pudiera darle la razón al interruptor sería publicidad.
|
|
208
|
+
- Los turnos que la sala contestó sola, con `@madre` y sin factura, se cuentan aparte. No se les pone precio: nadie midió lo que habrían costado en otro lado.
|
|
209
|
+
|
|
210
|
+
### Lo que la sala sabe, puesto donde se decide
|
|
211
|
+
|
|
212
|
+
- Arriba del hilo, las decisiones vigentes del proyecto. Una decisión refutada no aparece: el archivo la conserva, pero una franja que dice «decidido» no puede cargar algo que la sala ya estableció como falso.
|
|
213
|
+
- Bajo el compositor, con el campo vacío, las preguntas que el archivista registró como abiertas y nadie contestó, las más arrastradas primero. Se van en cuanto escribes.
|
|
214
|
+
- Una pregunta sobre lo que la sala ya dijo ofrece `@madre` antes de gastar: contesta desde el mismo archivo, en esta computadora y sin tokens. Ofrece; tú eliges.
|
|
215
|
+
- Nada de esto entra en un prompt. Son lecturas del archivo que se te muestran a ti.
|
|
216
|
+
|
|
217
|
+
### Choices · un módulo para devolverte una decisión
|
|
218
|
+
|
|
219
|
+
- Un agente que tiene que elegir entre tres o más opciones reales puede devolvértelas como botones, con un canal propio e inerte: `pulse-ask`. Pulsar uno escribe la opción en el compositor y para ahí; nada corre ni se gasta hasta que tú mandas.
|
|
220
|
+
- No toca el canal de delegación. Ese parser arranca turnos con permisos reales, y enseñarle una segunda gramática sería poner un parser que puede gastar dinero en el camino de uno que no.
|
|
221
|
+
- Tiene interruptor porque no es gratis: la instrucción cuesta unos 135 tokens por turno mientras está encendido, y aparece como bloque propio en la economía. Una capacidad cuyo costo no se puede ver es una capacidad contra la que nadie puede decidir.
|
|
222
|
+
|
|
223
|
+
### La escalera, al lado del agente que pide subir
|
|
224
|
+
|
|
225
|
+
- Cuando una respuesta dice que el turno necesita más permiso del que tuvo, la sala lo ofrece ahí mismo: `#2` arma el compositor y `#3` o `#4` abren la misma anulación de siempre, donde la designación del proyecto se escribe a mano. Ofrece el camino; no concede nada ni acorta la ceremonia.
|
|
226
|
+
- Hasta ahora la sala solo sabía pedir elevación en un caso, un paso de plan que quiere `#2`. Un agente en turno directo que necesitaba `#3` únicamente podía decirlo en una frase, y leerla, salir del mensaje y encontrar el selector era trabajo del humano.
|
|
227
|
+
- El detector es estrecho a propósito: un `#3` suelto no basta, hace falta una palabra de necesidad a su lado, y el modo tiene que estar por encima del que el turno tuvo. Un agente que explica la escalera menciona todos los peldaños y eso no es una petición. No cuesta nada en ningún prompt.
|
|
228
|
+
|
|
229
|
+
### RIPLEY abre lo que ya está escuchando en esta computadora
|
|
230
|
+
|
|
231
|
+
- Un navegador anclado al lado del chat, con pestañas, que abre los servidores que contestan HTTP aquí: los lee de `lsof`/`netstat` y solo acepta direcciones de loopback. La sala no ejecuta nada para encontrarlos.
|
|
232
|
+
- Sin barra de direcciones. Un sitio que manda `X-Frame-Options` o `frame-ancestors` no se deja enmarcar, y ninguna página puede saltárselo: es la defensa estándar contra el clickjacking y RIPLEY ni puede ni debe anularla. Cuando uno se niega, el panel lo dice y ofrece abrirlo en pestaña o copiar la dirección; no abre ventanas por su cuenta.
|
|
233
|
+
- La comprobación de marcado, el permiso y el ajuste siguen vivos debajo, porque el obstáculo es el medio y no el diseño: una MADRE de escritorio con su propio motor sí puede, y ese día la barra vuelve.
|
|
234
|
+
- El visor de archivos volvió a ser solo eso. Su botón de servidores desapareció: esa lista vive en el navegador.
|
|
235
|
+
|
|
236
|
+
### El finder opera sobre los archivos, con las mismas vallas
|
|
237
|
+
|
|
238
|
+
- Clic derecho sobre cualquier fila: copiar, mover, renombrar, archivo nuevo, carpeta nueva. Lo que se toma se ve en una tira bajo el buscador; el nombre se escribe ahí mismo, en el panel, sin sacar al humano de donde estaba mirando.
|
|
239
|
+
- Esto no es un agente bajo un modo: es el humano en su proyecto. Pero las vallas siguen. El proyecto es el mundo —los dos extremos se resuelven dentro o la operación no ocurre, comprobado después de seguir enlaces simbólicos— y las zonas que la sala protege de los agentes se protegen también de un resbalón: `.git`, `.pulse`, `.madre`, un `.env` o los ajustes locales piden la designación del proyecto, la misma palabra que pide CONTROL.
|
|
240
|
+
- Cuentan los dos extremos. Mover algo inofensivo **encima** de una ruta protegida es exactamente como se pierde una ruta protegida.
|
|
241
|
+
- Y un buscador por nombre, con el mismo puntaje que usa el menú `!` del compositor.
|
|
242
|
+
|
|
243
|
+
### La barra, el pie y la firma
|
|
244
|
+
|
|
245
|
+
- La barra superior se compacta por consultas de contenedor, no de ventana: con el navegador abierto la columna del chat mide mucho menos que la pantalla, y una media query nunca se enteraba. Los botones pierden la etiqueta y conservan el icono en el orden en que dejan de caber.
|
|
246
|
+
- Las preguntas abiertas van en un riel horizontal con la etiqueta centrada encima. Antes envolvían a un segundo renglón y empujaban el compositor hacia abajo.
|
|
247
|
+
- El aviso de modo pasó de cuatro renglones a dos, y en la esquina inferior derecha quedó la versión y el copyright. La versión viene de `/api/state`, no escrita a mano donde se olvide en el siguiente release.
|
|
248
|
+
|
|
249
|
+
### El gato ya no es un dibujo: es una pequeña escena
|
|
250
|
+
|
|
251
|
+
- Escribe `madre` sola en la caja, sin arroba ni nada más, y envía. Una nave hiperrealista entra en la sala con su pasajero verde dentro; no responde un agente.
|
|
252
|
+
- Cuatro fotogramas registrados mantienen el casco, el domo y el haz en su sitio mientras el michi parpadea, mira el tablero y juega con los controles. El resultado se siente animado sin los saltos de una colección de imágenes distintas.
|
|
253
|
+
- Las luces del casco persiguen, el emisor respira y el reflejo del domo cambia con la escena. La nave conserva completos sus bordes, antenas y motor durante toda la aparición.
|
|
254
|
+
- Sigue siendo un secreto local: no se enruta, no entra al ledger, no cuesta tokens y ningún agente se entera. Al terminar, el compositor vuelve exactamente como estaba.
|
|
255
|
+
|
|
256
|
+
### Arreglos
|
|
257
|
+
|
|
258
|
+
- Los chips de memoria eran ilegibles en tema claro: el tipo se pintaba con colores elegidos para el mapa oscuro de NOSTROMO, y «HECHO» en un azul casi blanco desaparecía sobre el fondo. Ahora el tipo es un punto con anillo, el texto lleva los colores del texto y la fila perdió la opacidad que la apagaba.
|
|
259
|
+
- La paleta de `/` dejaba sin descripción a cualquier comando con sintaxis larga: la columna del nombre crecía sin límite y la explicación se truncaba hasta desaparecer. La sintaxis bajó a su propio renglón.
|
|
260
|
+
- Un renderizador que declina ya no mete un nulo en la columna de mensajes.
|
|
261
|
+
- El primer replay pintaba chips de memoria antes de que se evaluaran las constantes que necesitan: la página podía morir en una zona muerta temporal antes de dibujar nada. Los colores, las palabras de cada tipo y el estimador de caracteres por token viven ahora con los demás formateadores, arriba.
|
|
262
|
+
- PLAYWRIGHT abortaba hasta esta misma sala con `ERR_BLOCKED_BY_CLIENT`. Pasaba `--blocked-origins '*'` además de la lista blanca, y Playwright casa la ruta añadida más recientemente primero: el bloqueo general ganaba sobre la lista. La lista blanca ya era una puerta cerrada.
|
|
263
|
+
- El `+N` de las memorias usadas podía quedarse solo en un renglón al envolverse la fila; ahora viaja junto al último chip.
|
|
264
|
+
|
|
265
|
+
## 0.5.2 · 2026-09-28
|
|
8
266
|
|
|
9
267
|
Tres reportes de la 0.5.1, y la razón por la que ninguno lo atrapó una prueba. Arreglos: nada cambia modos, módulos, agentes ni a dónde sale nada.
|
|
10
268
|
|
|
269
|
+
### Cerrar una versión abre la siguiente
|
|
270
|
+
|
|
271
|
+
- Dos veces seguidas una versión salió y los arreglos que llegaron minutos después quedaron escritos **dentro de una sección ya publicada**. Eso es reescribir historia a mano, y hay que moverla de sitio antes de poder cerrar nada — porque un número en npm tiene que significar un código y solo uno: la sala decide si hay algo nuevo comparando versiones, y reescribir una dejaría sin señal, para siempre, a quien ya la tuviera.
|
|
272
|
+
- Ahora `scripts/release.mjs` **abre la siguiente sección en el mismo commit que cierra esta**. Lo que venga después tiene dónde escribirse y nadie tiene que acordarse. Abre un parche por defecto; si lo que sigue resulta ser un menor, cambiar el encabezado es todo el trabajo.
|
|
273
|
+
- Y ya no deja cerrar una versión cuya sección esté vacía. Una nota de versión es cómo alguien decide si la toma; una vacía es peor que no publicar.
|
|
274
|
+
|
|
11
275
|
### Los tres reportes de 0.5.1
|
|
12
276
|
|
|
13
277
|
- **Ningún comando de módulo funcionaba al abrir la sala** (#8). `/git`, `/ahp`, `/pendientes`: todos contestaban «comando desconocido» con el módulo encendido, porque la única llamada que le pide al servidor la lista se había caído **dentro** del bloque que cierra una instalación en vivo. Hasta que ocurriera una instalación, el compositor no conocía ni un comando de módulo. Ahora se pide al cargar, y también cuando un módulo entra, sale o cambia de interruptor — que es cuando cambia lo que el compositor acepta.
|
package/README.md
CHANGED
|
@@ -54,6 +54,8 @@ Preguntas *«¿por qué elegimos Postgres?»* tres semanas después y la sala co
|
|
|
54
54
|
|
|
55
55
|
Con [Ollama](https://ollama.com) corriendo, esa memoria además **habla**: `@madre` es un quinto agente, local, gratis, que responde desde todo el archivo con citas y no escribe nada.
|
|
56
56
|
|
|
57
|
+
Y la puedes **ver**. En NOSTROMO el archivo es un cerebro en tres dimensiones que giras con el ratón: cada memoria es una estrella cuyo brillo dice cuánto la usa la sala y cuya superficie cuenta si la confirmaste, si nadie la ha revisado o si se está enfriando. En MEMORIA, un tablero te dice qué tan bien está contestando el archivo y qué conviene hacer después.
|
|
58
|
+
|
|
57
59
|
## Leen por defecto. Escriben cuando tú lo dices.
|
|
58
60
|
|
|
59
61
|
Cada mensaje sale con un modo de permiso, y tu modo es el techo de todo lo que ese mensaje arranque.
|
|
@@ -75,13 +77,39 @@ Subir a `#3` o `#4` es una ceremonia deliberada: MADRE te pide el nombre de la c
|
|
|
75
77
|
|
|
76
78
|
<br>
|
|
77
79
|
|
|
80
|
+
## Hazla tuya
|
|
81
|
+
|
|
82
|
+
MADRE trae ocho módulos. El noveno lo escribes tú — o se lo pides a un agente.
|
|
83
|
+
|
|
84
|
+
**Un módulo es un archivo.** Sin build, sin dependencias, sin registrarse en ningún lado. Lo copias a una carpeta, pulsas RELOAD y aparece con su interruptor, sus ajustes y su ficha. Con uno puedes añadir un comando `/loquesea` que la sala entiende, darle a los agentes una herramienta que antes no tenían, cambiar cómo se comporta la sala, o conectarla con algo que ya usas.
|
|
85
|
+
|
|
86
|
+
**O no lo escribes tú.** En la caja de texto:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
/module un comando que me diga qué cambió en el repo esta semana
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
El agente lee el SDK, escribe el archivo, y MADRE te enseña una tarjeta para revisarlo antes de instalarlo. Nunca se instala solo, y ningún agente puede escribir en la carpeta de módulos.
|
|
93
|
+
|
|
94
|
+
**Conectarla con un servicio de fuera** es lo que más se pide, y la respuesta corta es *casi siempre sí*: el límite no es MADRE, es cómo ese servicio deja que entregues tu propia llave. Vale la pena cuando la consigues en menos de cinco minutos sin ser desarrollador y la puedes revocar sola — GitHub, Linear, Notion, Jira, Slack, Telegram, Stripe, Shopify, Odoo, cualquier base de datos. Donde hay muro es donde el proveedor exige revisar tu app antes de dejarte pasar: Gmail en lectura, Meta, banca.
|
|
95
|
+
|
|
96
|
+
Tu módulo declara qué llave necesita y MADRE dibuja el campo; declara a dónde llega y el registro de salidas responde por él; declara qué herramientas suyas **mandan** algo, y la sala se las retira a los agentes salvo en el peldaño `#4`. Nada de eso lo programas: lo declaras.
|
|
97
|
+
|
|
98
|
+
**[Escribir un módulo →](docs/SDK.md)** · con dos ejemplos completos que funcionan tal cual: [el mínimo](docs/sdk/hello-module.mjs) y [un conector](docs/sdk/connector-module.mjs).
|
|
99
|
+
|
|
100
|
+
<br>
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
<br>
|
|
105
|
+
|
|
78
106
|
## Lo que MADRE no es
|
|
79
107
|
|
|
80
108
|
Esto importa más que cualquier función.
|
|
81
109
|
|
|
82
110
|
**No hay nube.** MADRE no tiene servidor, ni cuenta, ni backend. Corre en `127.0.0.1` y se muere cuando cierras la terminal.
|
|
83
111
|
|
|
84
|
-
**
|
|
112
|
+
**Tus llaves, donde hacen falta y en ningún otro lado.** Cuando das la llave de un agente, MADRE la escribe en el archivo donde ese CLI la busca, cerrada a tu usuario, y no conserva copia. Un conector no tiene CLI al que dársela, así que esa —y solo esa— la guarda MADRE, en su propia bodega de tu máquina, con permisos de solo tu usuario, aceptada únicamente desde esta computadora. Nunca en su config, ni en su registro, ni en sus logs, ni en lo que lee un agente: la ficha solo muestra que hay una guardada y cuántos caracteres tiene.
|
|
85
113
|
|
|
86
114
|
**Nada sale por su cuenta.** Lo que un agente lee viaja a *su* proveedor, con tu cuenta y tus límites — como si lo hubieras corrido en tu terminal, porque eso es exactamente lo que MADRE hace. Los dos únicos envíos propios están bajo tu interruptor: una consulta diaria a npm por si hay versión nueva, y los reportes de fallos, apagados por defecto.
|
|
87
115
|
|
|
@@ -132,6 +160,10 @@ Una versión se cierra cuando está en npm. El detalle de cada una en [CHANGELOG
|
|
|
132
160
|
Problemas: desde MU/TH/UR (`✎ FEEDBACK`) o en [issues](https://github.com/jossuealcacao-exe/madre/issues).
|
|
133
161
|
Seguridad: [SECURITY.md](SECURITY.md) · Contribuir: [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
134
162
|
|
|
163
|
+
MADRE es gratis y seguirá siéndolo. Si te ahorra tiempo y quieres que siga creciendo,
|
|
164
|
+
puedes [apadrinarla](https://github.com/sponsors/jossuealcacao-exe). Nada del producto
|
|
165
|
+
cambia según lo que hagas con ese enlace.
|
|
166
|
+
|
|
135
167
|
Apache-2.0 · [Jossué Alcalá](https://jossuealcala.com/en/)
|
|
136
168
|
|
|
137
169
|
<br>
|
package/docs/SDK.md
CHANGED
|
@@ -13,7 +13,17 @@ Ningún agente puede escribir ahí: `.madre/` es zona prohibida en CONTROL y AIR
|
|
|
13
13
|
|
|
14
14
|
## El archivo
|
|
15
15
|
|
|
16
|
-
El `export default` es un objeto plano. MADRE lo envuelve con `defineModule`.
|
|
16
|
+
El `export default` es un objeto plano. MADRE lo envuelve con `defineModule`. Hay dos ejemplos
|
|
17
|
+
completos, y los dos funcionan tal cual:
|
|
18
|
+
|
|
19
|
+
| | qué enseña |
|
|
20
|
+
|---|---|
|
|
21
|
+
| [`hello-module.mjs`](sdk/hello-module.mjs) | lo mínimo: interruptor, un ajuste, un comando `/hello` |
|
|
22
|
+
| [`connector-module.mjs`](sdk/connector-module.mjs) | un conector de verdad: llave en la bodega, destino declarado, y **un solo archivo que es la ficha al importarse y el servidor MCP al ejecutarse** |
|
|
23
|
+
|
|
24
|
+
Empieza por el primero. Si tu módulo habla con algo de fuera de esta computadora, copia el
|
|
25
|
+
segundo: esa forma no se adivina, y el modo obvio de escribirla falla **en silencio** (ver
|
|
26
|
+
«Un archivo que también es servidor», abajo).
|
|
17
27
|
|
|
18
28
|
```js
|
|
19
29
|
export default {
|
|
@@ -26,7 +36,9 @@ export default {
|
|
|
26
36
|
};
|
|
27
37
|
```
|
|
28
38
|
|
|
29
|
-
**Tu módulo no importa
|
|
39
|
+
**Tu módulo no importa paquetes.** Ni a MADRE, ni al SDK, ni nada de npm. Antes de instalarlo, MADRE copia tu archivo a una carpeta temporal aparte y lo carga ahí —así revisa qué es sin que corra donde vive— y en esa carpeta no hay `node_modules`: un `import` por nombre de paquete no resuelve y la instalación falla.
|
|
40
|
+
|
|
41
|
+
Los **builtins de Node sí**, siempre: `node:fs`, `node:url`, `node:tls`, `node:child_process`. Con eso alcanza para hablar cualquier protocolo. Lo que no hay es nada que instalar.
|
|
30
42
|
|
|
31
43
|
Si necesitas `defineModule` —para calcular algo antes de definir el módulo, por ejemplo— **MADRE te lo entrega**: exporta una función y lo recibes como argumento.
|
|
32
44
|
|
|
@@ -42,6 +54,11 @@ export default ({ defineModule }) => defineModule({
|
|
|
42
54
|
|
|
43
55
|
## Lo que un módulo puede declarar
|
|
44
56
|
|
|
57
|
+
Esta tabla es **el contrato completo**: 36 campos, ni uno más. Cualquier otro que escribas MADRE
|
|
58
|
+
te lo dice al instalar («no entendí estos campos») en vez de ignorarlo en silencio. Una prueba de
|
|
59
|
+
la suite compara esta tabla contra el código, así que si alguna vez se separan, es un fallo.
|
|
60
|
+
|
|
61
|
+
|
|
45
62
|
| Campo | Qué es |
|
|
46
63
|
|---|---|
|
|
47
64
|
| `id`, `name`, `vendor`, `summary`, `creates`, `requires` | Su ficha en MODULES. `summary` es una frase: qué hace. `creates` y `requires` son líneas cortas, una idea cada una |
|
|
@@ -55,12 +72,160 @@ export default ({ defineModule }) => defineModule({
|
|
|
55
72
|
| `toggle(ctx, payload)` | Sustituye el interruptor por defecto; `confirm: 'texto'` pide confirmación antes de encender |
|
|
56
73
|
| `onToggle(ctx, enabled)` | Reacciona al interruptor |
|
|
57
74
|
| `slash` | Comandos `/nombre` que corren en el servidor con tu `ctx` y devuelven `{ ok, title, text }`. La sala los muestra como tarjeta y los agentes los leen |
|
|
58
|
-
| `toolsForTurn(ctx, turn)` | Servidores MCP para el turno de un agente: `[{ name, command, args, env, tools, brief }]`. MADRE los adjunta a la CLI en su corrida aislada y describe `brief` al agente |
|
|
75
|
+
| `toolsForTurn(ctx, turn)` | Servidores MCP para el turno de un agente: `[{ name, command, args, env, tools, sends, brief }]`. MADRE los adjunta a la CLI en su corrida aislada y describe `brief` al agente |
|
|
76
|
+
| `toolsForTurn` → `sends` | Cuáles de tus herramientas **mandan algo fuera**. El núcleo las retira por debajo de `#4` AIRLOCK: ni se le cuenta al modelo que existen. Ver abajo |
|
|
77
|
+
| `reaches` | A dónde llega tu módulo, en las mismas cuatro respuestas que da MADRE de las suyas. Ver abajo |
|
|
78
|
+
| `secrets` | Las llaves que necesitas, declaradas en vez de dibujadas: `[{ name, label, note, where }]`. MADRE pinta el campo, la guarda en su bodega bajo tu id y te la devuelve por `ctx.vault`. Ver abajo |
|
|
59
79
|
| `routes` | Rutas HTTP propias: `{ method, path, handler(ctx, { payload, params, url }) }` → `{ status, body }` |
|
|
60
80
|
| `onEvent(ctx, event)` | Cada evento del ledger |
|
|
61
81
|
| `controls` | Los ajustes de tu módulo, declarados en vez de dibujados: `[{ key, label, type: 'select' \| 'switch' \| 'text', options, note, invert }]`. MADRE los pinta en la ficha y los guarda en tu bloque de `config.json` |
|
|
62
82
|
| `onSettings(ctx, settings)` | Te avisa cuando la humana cambió uno de tus `controls`, por si algo vivo tiene que enterarse |
|
|
63
83
|
| `conditions` | Entradas para el catálogo de MU/TH/UR, con remedio por plataforma |
|
|
84
|
+
| `kind` | `'builtin'` (por defecto) cambia cómo se comporta MADRE y vive en `config.json`; `'installer'` escribe en el proyecto con un comando que la humana confirma antes de que corra |
|
|
85
|
+
| `configKey` | Tu bloque en `config.json`. Por defecto es tu `id` en camelCase; decláralo solo si quieres otro |
|
|
86
|
+
| `card` | Qué tarjeta dibuja MADRE. Un módulo tuyo deja el valor por defecto: las tarjetas con nombre son las que MADRE trae pintadas para los suyos |
|
|
87
|
+
| `models` | Los modelos entre los que elige tu módulo, si envuelve algo que los tiene. Salen en la ficha |
|
|
88
|
+
| `package` | El paquete de npm que instala lo que tu módulo maneja, si lo hay |
|
|
89
|
+
| `confirm` | Un texto. Si está, encender el interruptor pide confirmación y no se enciende hasta que la humana la da |
|
|
90
|
+
| `toggledEvent`, `toggledBody` | Lo que se añade al evento `extension.toggled` y al aviso que ve la sala cuando tu módulo se enciende o se apaga |
|
|
91
|
+
| `preflight(ctx)`, `detect(ctx)`, `installCommand(ctx)` | **Herencia**, de cuando un módulo solo podía ser un instalador. Siguen funcionando; para algo nuevo, devuelve `preflight` desde `status(ctx)` como arriba |
|
|
92
|
+
| `describe(ctx)` | Lo arma MADRE con todo lo anterior. **No lo declares**: está en la lista porque el objeto lo lleva, no porque tengas que escribirlo |
|
|
93
|
+
|
|
94
|
+
### ¿Se puede conectar con *X*?
|
|
95
|
+
|
|
96
|
+
No depende de MADRE. Un módulo puede hacer lo que pueda hacer un proceso de Node en tu
|
|
97
|
+
computadora: hablar TLS, levantar un servidor, guardar una llave. **Depende de cómo ese servicio
|
|
98
|
+
deja que un humano entregue su propia credencial**, y eso cae en tres peldaños:
|
|
99
|
+
|
|
100
|
+
| | qué pide el servicio | ¿funciona al instalarlo? |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| **A** | una llave estática en minutos: token personal, API key, contraseña de aplicación. Sin registrar app, sin revisión | **sí** |
|
|
103
|
+
| **B** | que cada usuario registre su propia app (OAuth sin permisos restringidos) | sí, pero el alta deja de ser «pega esto» — y tendrías que escribir OAuth |
|
|
104
|
+
| **C** | revisión, evaluación de seguridad o editor verificado | **no**: cada usuario tendría que ser su propio desarrollador |
|
|
105
|
+
|
|
106
|
+
En **A** está casi todo lo útil: GitHub, GitLab, Linear, Notion (integración interna), Jira,
|
|
107
|
+
Slack, Discord, Telegram, Stripe, Shopify, Cloudflare, Odoo, cualquier base de datos, y cualquier
|
|
108
|
+
API que acepte un `Authorization: Bearer`. En **B**: Google Calendar y Drive, Microsoft Graph,
|
|
109
|
+
Mercado Libre. En **C**: Gmail en lectura, todo Meta, banca.
|
|
110
|
+
|
|
111
|
+
**La regla**, si no quieres leer la tabla: vale la pena cuando la credencial se consigue en menos
|
|
112
|
+
de cinco minutos sin ser desarrollador, se revoca sola, y lo que tu módulo trae o saca cabe en una
|
|
113
|
+
línea de la ficha. El catálogo largo y por qué, en
|
|
114
|
+
[§6 de `CONECTORES.md`](https://github.com/jossuealcacao-exe/madre/blob/main/docs/CONECTORES.md#6--qu%C3%A9-alcanza-un-m%C3%B3dulo-y-de-qui%C3%A9n-es-el-muro), en el repositorio.
|
|
115
|
+
|
|
116
|
+
### Un archivo que también es servidor
|
|
117
|
+
|
|
118
|
+
`toolsForTurn` devuelve `args` que apuntan a un archivo, y tú solo tienes uno. No hacen falta dos:
|
|
119
|
+
**el tuyo es la ficha cuando MADRE lo importa y el servidor MCP cuando MADRE lo ejecuta.** Sabe
|
|
120
|
+
cuál de las dos cosas es al final del archivo.
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
import { realpath } from 'node:fs/promises';
|
|
124
|
+
import { fileURLToPath } from 'node:url';
|
|
125
|
+
const SELF = fileURLToPath(import.meta.url);
|
|
126
|
+
|
|
127
|
+
// …export default { …, toolsForTurn: () => [{ command: process.execPath, args: [SELF], … }] }
|
|
128
|
+
|
|
129
|
+
const real = async (p) => (p ? realpath(p).catch(() => p) : null);
|
|
130
|
+
if ((await real(process.argv[1])) === (await real(SELF))) serve();
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Resuelve las dos rutas antes de compararlas.** `process.argv[1] === SELF` parece lo mismo y casi
|
|
134
|
+
siempre lo es: cuando MADRE lo lanza, le pasa el mismo `SELF` que ya resolvió el cargador de Node.
|
|
135
|
+
Pero en cuanto **corres el archivo tú a mano para probarlo** —`node ~/.pulse/modules/tuyo.mjs`, o
|
|
136
|
+
cualquier ruta que pase por `/tmp`, que en macOS es un enlace a `/private/tmp`— `argv[1]` conserva
|
|
137
|
+
lo que tecleaste y `SELF` no. La comparación da falso, `serve()` nunca corre, el proceso arranca,
|
|
138
|
+
no contesta nada y **sale con éxito**. No imprime un error porque, sobre el papel, no hubo ninguno.
|
|
139
|
+
Dos líneas te ahorran esa tarde.
|
|
140
|
+
|
|
141
|
+
### Si tu módulo habla con un servicio de fuera
|
|
142
|
+
|
|
143
|
+
Declara a dónde llega. `src/outbound.mjs` no es un registro de salidas: es un contrato, y
|
|
144
|
+
parchea `fetch` global para que ningún módulo pueda evitarlo. Un conector que no declara su
|
|
145
|
+
destino sale en el registro como «fue a una dirección que nada aquí declara» — verdad, e
|
|
146
|
+
inútil para quien intenta distinguir tu módulo de una fuga.
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
reaches: [{
|
|
150
|
+
id: 'gmail',
|
|
151
|
+
host: 'gmail.googleapis.com',
|
|
152
|
+
to: 'Google · Gmail, con tu cuenta',
|
|
153
|
+
what: 'El asunto y el cuerpo del correo que el agente redactó.',
|
|
154
|
+
when: 'cuando un agente usa la herramienta de envío',
|
|
155
|
+
where: 'MODULES → CORREO',
|
|
156
|
+
}]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Las cuatro respuestas son las mismas que da MADRE de las suyas, y por la misma razón: quien
|
|
160
|
+
lee el registro merece saber qué viaja, cuándo, a dónde y dónde se apaga. Tu declaración se
|
|
161
|
+
guarda **aparte** de las del núcleo y marcada con tu `id`, porque quién prometió qué es parte
|
|
162
|
+
de la promesa. Se reconstruye en cada carga: desinstalar tu módulo se lleva su destino.
|
|
163
|
+
|
|
164
|
+
Si apuntas tu módulo a otro servidor en tiempo de ejecución, vuelve a declarar con el **mismo
|
|
165
|
+
`id`**: así se reemplaza la entrada en vez de dejar al registro respondiendo por una dirección
|
|
166
|
+
que ya no usas.
|
|
167
|
+
|
|
168
|
+
### Si tu módulo necesita una llave
|
|
169
|
+
|
|
170
|
+
Decláralas y no las pidas tú. MADRE dibuja un campo de contraseña en el piso `LLAVES` de tu
|
|
171
|
+
ficha, acepta el valor **solo desde esta computadora** (`127.0.0.1`), lo guarda en
|
|
172
|
+
`~/.pulse/credentials/<tu-id>.json` con permisos `0600` dentro de un directorio `0700`, y anota
|
|
173
|
+
en el ledger que se guardó una y **con qué nombre y qué longitud, jamás el valor**.
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
secrets: [{
|
|
177
|
+
name: 'app-password',
|
|
178
|
+
label: 'APP PASSWORD',
|
|
179
|
+
note: 'No es la contraseña de tu cuenta. Una de aplicación, que puedes revocar sola.',
|
|
180
|
+
where: 'https://myaccount.google.com/apppasswords', // enlace a dónde sacarla
|
|
181
|
+
}]
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Y la lees por `ctx.vault`, que viene acotado a tu módulo:
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
await ctx.vault.get('app-password') // el valor, solo para ti
|
|
188
|
+
await ctx.vault.held() // [{ module, name, bytes }] — nombres y tamaños, nunca valores
|
|
189
|
+
await ctx.vault.keep(name, value) // guardar
|
|
190
|
+
await ctx.vault.forget(name) // olvidar; sin nombre, todas las tuyas
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Tu id va cerrado dentro del SDK, así que pedir el secreto de otro módulo no es algo que puedas
|
|
194
|
+
*escribir*. La ficha solo muestra que hay una guardada y cuántos caracteres tiene.
|
|
195
|
+
|
|
196
|
+
**El valor va al entorno del proceso que tú levantas, y a ningún otro sitio.** No al prompt, no
|
|
197
|
+
al `brief`, no a un resultado de herramienta, no al ledger.
|
|
198
|
+
|
|
199
|
+
### Si tu módulo manda algo
|
|
200
|
+
|
|
201
|
+
Leer se deshace: en el peor caso trajiste algo que no servía. **Mandar no.** Un correo, un
|
|
202
|
+
mensaje, un registro en el sistema de alguien más: eso sale con el nombre de quien abrió la sala
|
|
203
|
+
y no vuelve.
|
|
204
|
+
|
|
205
|
+
Por eso el envío vive en `#4` AIRLOCK, donde la regla ya obliga al agente a decir en una línea
|
|
206
|
+
qué sale y a dónde antes de que salga. **Lo impone el núcleo, no tu buena voluntad:**
|
|
207
|
+
|
|
208
|
+
```js
|
|
209
|
+
tools: ['buscar_correo', 'mandar_correo'],
|
|
210
|
+
sends: ['mandar_correo'], // por debajo de #4 esta se retira de la lista
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Si *todas* las herramientas de un servidor mandan, el servidor entero no viaja por debajo de
|
|
214
|
+
`#4`. Si alguna no manda, viaja el servidor con la lista recortada y un `brief` que dice por qué
|
|
215
|
+
viene corto. Y lo que decides comprobar, compruébalo **en tu servidor y no en el `brief`**: un
|
|
216
|
+
prompt es una sugerencia.
|
|
217
|
+
|
|
218
|
+
**¿Se puede conectar con *X*?** La respuesta no depende de MADRE sino de cómo ese servicio deja
|
|
219
|
+
que un humano entregue su propia llave, y está mapeada en tres peldaños —con catálogo— en
|
|
220
|
+
[§6 de `CONECTORES.md`](https://github.com/jossuealcacao-exe/madre/blob/main/docs/CONECTORES.md#6--qu%C3%A9-alcanza-un-m%C3%B3dulo-y-de-qui%C3%A9n-es-el-muro), en el repositorio.
|
|
221
|
+
Léela antes de escribir nada: ahorra descubrir a la mitad que el servicio exige una revisión
|
|
222
|
+
anual de seguridad para dejarte pasar.
|
|
223
|
+
|
|
224
|
+
El diseño entero de los conectores está en
|
|
225
|
+
[`CONECTORES.md`](https://github.com/jossuealcacao-exe/madre/blob/main/docs/CONECTORES.md), en el repositorio. Un conector completo para leer de principio
|
|
226
|
+
a fin —CORREO, que manda correo por SMTP sin una sola dependencia— está en
|
|
227
|
+
`src/modules/correo.mjs` y se lee en diez minutos. Todavía no aparece en MÓDULOS: lo estamos
|
|
228
|
+
terminando, y mientras tanto solo se activa a propósito con `PULSE_LABS=correo`.
|
|
64
229
|
|
|
65
230
|
## Publicarlo
|
|
66
231
|
|
|
@@ -70,7 +235,7 @@ Y en la cabecera de MODULES hay **+ ADD A MODULE** para instalar el `.mjs` de al
|
|
|
70
235
|
|
|
71
236
|
## La ficha
|
|
72
237
|
|
|
73
|
-
Todas las fichas de MODULES tienen los mismos pisos, en el mismo orden. No dibujas una tarjeta: declaras, y MADRE la arma. Es lo que hace que
|
|
238
|
+
Todas las fichas de MODULES tienen los mismos pisos, en el mismo orden. No dibujas una tarjeta: declaras, y MADRE la arma. Es lo que hace que nueve módulos —y el tuyo— se lean igual.
|
|
74
239
|
|
|
75
240
|
| Piso | Qué muestra | De dónde sale |
|
|
76
241
|
|---|---|---|
|
|
@@ -78,7 +243,8 @@ Todas las fichas de MODULES tienen los mismos pisos, en el mismo orden. No dibuj
|
|
|
78
243
|
| 2 · Qué hace | una frase, no tres | `summary` |
|
|
79
244
|
| 3 · Qué toca | lo que escribe y lo que necesita, plegado, con su número al lado | `creates`, `requires` (y `commands`, en su propio pliegue) |
|
|
80
245
|
| 4 · Ajustes | lo tuyo: selectores, interruptores, campos | `controls`, y lo que tu módulo lea de sí mismo |
|
|
81
|
-
| 5 ·
|
|
246
|
+
| 5 · Llaves | un campo por secreto, lo que hay guardado (nombre y largo, nunca el valor) y `OLVIDARLA` | `secrets` |
|
|
247
|
+
| 6 · El interruptor | install, enable o disable. Solo, y siempre abajo | `toggle` / `installCommand` |
|
|
82
248
|
|
|
83
249
|
El estado es una palabra y un punto —`ON` u `OFF`—, nunca un botón: las acciones se presionan abajo. La principal queda al final; un módulo `DEV` también ofrece `DESINSTALAR`, mientras que uno integrado solo puede apagarse. Un módulo apagado se atenúa entero menos esa fila.
|
|
84
250
|
|
|
@@ -91,7 +257,8 @@ ctx = {
|
|
|
91
257
|
env, agents, room, // agentes detectados; la sala (room.capabilities(), room.record(...))
|
|
92
258
|
readConfig(), updateConfig(patch),
|
|
93
259
|
record(type, payload), // escribe un evento en el ledger; tipo "algo.algo", nunca message.* ni agent.*
|
|
94
|
-
services: { imageKey, ollama, … } // lo que el servidor ofrece
|
|
260
|
+
services: { imageKey, ollama, … }, // lo que el servidor ofrece
|
|
261
|
+
vault: { get, held, keep, forget } // tus secretos, y solo los tuyos
|
|
95
262
|
}
|
|
96
263
|
```
|
|
97
264
|
|