@jossuealcala/madre 0.3.3 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +497 -3
- package/CONTRIBUTING.md +3 -1
- package/README.md +68 -186
- package/SECURITY.md +2 -1
- package/bin/madre.mjs +56 -13
- package/docs/INTERNALS.md +16 -0
- package/docs/REFERENCE.md +249 -0
- package/docs/SDK.md +121 -0
- package/docs/room.png +0 -0
- package/docs/sdk/hello-module.mjs +51 -0
- package/package.json +9 -1
- package/public/app.js +3979 -867
- package/public/es.js +2258 -0
- package/public/i18n.js +66 -0
- package/public/index.html +96 -15
- package/public/inquiry.js +220 -0
- package/public/resay.js +77 -0
- package/public/styles.css +622 -65
- package/public/troubleshooting.js +255 -46
- package/src/adapters/claude.mjs +2 -1
- package/src/adapters/codex.mjs +2 -1
- package/src/adapters/gemini.mjs +6 -5
- package/src/adapters/opencode.mjs +2 -1
- package/src/adapters/process.mjs +79 -20
- package/src/asking.mjs +128 -0
- package/src/auth-probe.mjs +58 -1
- package/src/chats.mjs +193 -0
- package/src/checkpoint.mjs +1 -1
- package/src/cold.mjs +56 -0
- package/src/commands.mjs +6 -0
- package/src/conversation-context.mjs +35 -3
- package/src/credentials.mjs +145 -0
- package/src/dataset.mjs +56 -4
- package/src/distiller.mjs +12 -5
- package/src/event-store.mjs +14 -8
- package/src/exam.mjs +240 -0
- package/src/extensions.mjs +3 -2
- package/src/eyecat-watch.mjs +100 -0
- package/src/eyecat.mjs +169 -0
- package/src/i18n.mjs +47 -0
- package/src/image-studio.mjs +2 -0
- package/src/launch.mjs +61 -0
- package/src/maturity.mjs +94 -0
- package/src/mcp/image-server.mjs +36 -3
- package/src/mcp/memory-server.mjs +1 -1
- package/src/memory.mjs +325 -17
- package/src/modules/ahp.mjs +9 -7
- package/src/modules/ash.mjs +36 -0
- package/src/modules/git-pulse.mjs +5 -3
- package/src/modules/helpers.mjs +31 -0
- package/src/modules/image-studio.mjs +10 -4
- package/src/modules/index.mjs +141 -9
- package/src/modules/ollama.mjs +66 -10
- package/src/modules/playwright.mjs +44 -23
- package/src/modules/ripley.mjs +5 -3
- package/src/modules/sdk.mjs +93 -2
- package/src/modules/updates.mjs +81 -0
- package/src/ollama.mjs +5 -2
- package/src/outbound.mjs +297 -0
- package/src/privacy.mjs +54 -7
- package/src/room/context.mjs +4 -4
- package/src/room/economy.mjs +161 -0
- package/src/room/prompt.mjs +118 -46
- package/src/room.mjs +443 -44
- package/src/runtime-detection.mjs +27 -8
- package/src/sentinel-errors.mjs +19 -1
- package/src/server.mjs +709 -71
- package/src/setup.mjs +1 -1
- package/src/updates.mjs +4 -2
- package/src/usage-sentinel.mjs +13 -8
- package/src/verdict.mjs +74 -0
- package/src/ashcode.mjs +0 -64
- package/src/modules/ashcode.mjs +0 -28
package/CONTRIBUTING.md
CHANGED
|
@@ -22,6 +22,8 @@ node ./bin/madre.mjs start --no-open # the room from source, URL in the termi
|
|
|
22
22
|
|
|
23
23
|
## Writing a module
|
|
24
24
|
|
|
25
|
+
For a module of your own, outside this repository, read `docs/SDK.md`: one `.mjs` file in `~/.pulse/modules/` or `<project>/.madre/modules/`, no build, reloaded from MODULES. What follows is for modules that ship inside MADRE.
|
|
26
|
+
|
|
25
27
|
A module is one file in `src/modules/`, registered in `src/modules/index.mjs`:
|
|
26
28
|
|
|
27
29
|
```js
|
|
@@ -71,7 +73,7 @@ docs/report-collector/ the Worker that turns sentinel reports into issues
|
|
|
71
73
|
|
|
72
74
|
- A version is **closed only when it is on npm**. Until then its changelog section reads *Sin publicar* and keeps growing; no new number is opened while the previous one is unpublished.
|
|
73
75
|
- One published version = one `vX.Y.Z` tag = one GitHub release = one changelog section. Tags and releases are created at publish time, never before.
|
|
74
|
-
- Patch (`0.
|
|
76
|
+
- Patch (`0.x.Y`) for fixes and additions inside existing modules; the patch number may reach two digits. Minor (`0.x`) for a new mode, a new module, a new agent, or a change to what leaves the machine. Major when the room's ledger or memory format stops being readable by the previous version.
|
|
75
77
|
- `scripts/release.mjs` does the closing in one go: checks the tree is clean and CI-green, runs the suite and pack:check, turns *Sin publicar* into the dated section, tags, pushes, creates the release and prints the publish command.
|
|
76
78
|
|
|
77
79
|
Open questions go to issues with the `question` label. Ideas go through `✎ FEEDBACK` in MU/TH/UR or a plain issue. Be kind to the crew.
|
package/README.md
CHANGED
|
@@ -1,259 +1,141 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<a href="https://
|
|
2
|
+
<a href="https://madre.run"><img src="https://raw.githubusercontent.com/jossuealcacao-exe/madre/main/docs/madre-banner.svg" alt="MADRE · MU/TH/UR 6000 · INTERFACE 2037" width="100%"></a>
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<a href="https://www.npmjs.com/package/@jossuealcala/madre"><img alt="npm" src="https://img.shields.io/npm/v/@jossuealcala/madre?style=flat-square&label=npm&color=9bff66&labelColor=050605"></a>
|
|
7
7
|
<a href="https://github.com/jossuealcacao-exe/madre/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/jossuealcacao-exe/madre/ci.yml?style=flat-square&label=CI&color=9bff66&labelColor=050605"></a>
|
|
8
|
-
<img alt="status" src="https://img.shields.io/badge/status-beta-ffb000?style=flat-square&labelColor=050605">
|
|
9
8
|
<img alt="node" src="https://img.shields.io/badge/node-%E2%89%A5%2022.5-9bff66?style=flat-square&labelColor=050605">
|
|
10
|
-
<img alt="dependencies" src="https://img.shields.io/badge/
|
|
11
|
-
<img alt="license" src="https://img.shields.io/badge/
|
|
12
|
-
<img alt="crew" src="https://img.shields.io/badge/crew-Codex%20%C2%B7%20Claude%20%C2%B7%20Gemini%20%C2%B7%20OpenCode%20%C2%B7%20%40madre-9bff66?style=flat-square&labelColor=050605">
|
|
9
|
+
<img alt="dependencies" src="https://img.shields.io/badge/dependencias-0-9bff66?style=flat-square&labelColor=050605">
|
|
10
|
+
<img alt="license" src="https://img.shields.io/badge/licencia-Apache--2.0-9bff66?style=flat-square&labelColor=050605">
|
|
13
11
|
</p>
|
|
14
12
|
|
|
15
13
|
```
|
|
16
|
-
MU/TH/UR 6000 · INTERFACE 2037 · MADRE IS READY
|
|
17
|
-
|
|
18
|
-
ONE LOCAL ROOM · FOUR AI CODING AGENTS · ONE SHARED MEMORY · A FIFTH AGENT THAT IS THE MEMORY ITSELF
|
|
19
|
-
READ-ONLY BY DEFAULT · CONTROL WHEN YOU SAY SO · NOTHING LEAVES THIS MACHINE ON ITS OWN
|
|
14
|
+
MU/TH/UR 6000 · INTERFACE 2037 · MADRE IS READY
|
|
20
15
|
```
|
|
21
16
|
|
|
22
|
-
|
|
17
|
+
<br>
|
|
23
18
|
|
|
24
|
-
|
|
19
|
+
# Los agentes que ya tienes.<br>Una sala. Una memoria.
|
|
25
20
|
|
|
26
|
-
|
|
21
|
+
Codex, Claude Code, Gemini CLI y OpenCode trabajando sobre el mismo proyecto,
|
|
22
|
+
en la misma conversación, recordando lo mismo.
|
|
27
23
|
|
|
28
24
|
```
|
|
29
25
|
npx @jossuealcala/madre start
|
|
30
26
|
```
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
## ARRANQUE · LOS PRIMEROS CINCO MINUTOS
|
|
28
|
+
Eso es todo. No hay cuenta que crear, ni nube que configurar, ni llave que pegar.
|
|
29
|
+
MADRE usa las sesiones que esas CLIs ya tienen en tu máquina.
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
2. La sala abre en `http://127.0.0.1:4317`. Elige un agente en el selector o escribe `@claude …`. El chip junto a `to @agente` dice en qué modo sale el mensaje; `#1 EXCHANGE`, solo lectura, es el predeterminado.
|
|
40
|
-
3. Para conectar o reconectar una IA sin terminal: `MU/TH/UR → ⚙ CONNECTIONS`. Codex y Claude tienen `SIGN IN`; Gemini y OpenCode muestran el comando exacto para su propio prompt. Ninguna credencial pasa por MADRE.
|
|
41
|
-
4. Con Ollama, `MODULES → OLLAMA` ya está encendido: la memoria se embebe localmente y `@madre` aparece en la fila.
|
|
42
|
-
5. `⚙ CONNECTIONS → MEMORY` ajusta quién destila la memoria y cada cuánto. `◉ NOSTROMO` muestra lo que la sala recuerda.
|
|
31
|
+
<p align="center">
|
|
32
|
+
<img src="https://raw.githubusercontent.com/jossuealcacao-exe/madre/main/docs/room.png" alt="La sala de MADRE: Codex y Claude respondiendo en el mismo hilo sobre el mismo proyecto" width="100%">
|
|
33
|
+
</p>
|
|
43
34
|
|
|
44
|
-
|
|
45
|
-
npx @jossuealcala/madre doctor # qué agentes hay, quién tiene sesión, si hay versión nueva
|
|
46
|
-
npx @jossuealcala/madre setup # el asistente de conexión, cuando quieras
|
|
47
|
-
npx @jossuealcala/madre start --no-open --project RUTA
|
|
48
|
-
```
|
|
35
|
+
<p align="center"><sub>Una pregunta. Dos agentes. El segundo leyó al primero.</sub></p>
|
|
49
36
|
|
|
50
|
-
|
|
37
|
+
<br>
|
|
51
38
|
|
|
52
39
|
---
|
|
53
40
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
**La barra.** La marca con su latido, la raíz del proyecto, la tripulación, `LIVE`, las alertas (`STOP ALL`, versión nueva), `MODULES`, `MU/TH/UR`, tema y panel de archivos. Cada esfera de agente lleva un anillo con su consumo: el límite real del proveedor cuando el CLI lo publica, si no, la ventana local de MADRE. Un clic la despliega.
|
|
41
|
+
<br>
|
|
57
42
|
|
|
58
|
-
|
|
43
|
+
## Una conversación, no cuatro terminales
|
|
59
44
|
|
|
60
|
-
**
|
|
45
|
+
Escribes `@codex` y contesta Codex. Escribes `@claude` y contesta Claude, **leyendo lo que Codex acaba de decir**. Pides una mesa redonda y cada uno opina por turno sobre lo mismo.
|
|
61
46
|
|
|
62
|
-
|
|
63
|
-
|---|---|
|
|
64
|
-
| `@codex`, `@claude`, `@gemini`, `@opencode`, `@madre` | Menciona a un agente; `@` abre la lista |
|
|
65
|
-
| `!src/room.mjs:12-20` | Adjunta un archivo del proyecto, con líneas si las das; `!` busca entre los archivos |
|
|
66
|
-
| `#0` a `#3` | Fija el modo de permiso de ese mensaje |
|
|
67
|
-
| `/create …`, `/image …`, `/stopall`, `/git …`, `/ahp …` | Comandos; `/` abre el menú. `/git push` muestra qué saldría; solo `/git push confirm` lo envía |
|
|
68
|
-
| `STOPALL` | Freno maestro |
|
|
69
|
-
| Clip, arrastrar o pegar una imagen | Adjunto para la sala, nunca en el proyecto |
|
|
70
|
-
| `@madre, pregúntale al crew …` | Mesa redonda: un paso por agente en línea, cierre citado |
|
|
47
|
+
Hoy eso son cuatro ventanas, cuatro contextos y tú copiando y pegando entre ellas. MADRE lo vuelve un hilo.
|
|
71
48
|
|
|
72
|
-
|
|
49
|
+
## Una memoria que sobrevive a la sesión
|
|
73
50
|
|
|
74
|
-
|
|
51
|
+
Todo lo que se dice queda indexado. Cuando la conversación pasa de la ventana de contexto, cada agente recibe de vuelta lo que hace falta, citado. Cada diez intercambios, la sala destila lo dicho en notas con tipo: una decisión, un hecho, una preferencia, una pregunta abierta.
|
|
75
52
|
|
|
76
|
-
|
|
53
|
+
Preguntas *«¿por qué elegimos Postgres?»* tres semanas después y la sala contesta, aunque esa decisión la tomara otro agente en otra sesión.
|
|
77
54
|
|
|
78
|
-
|
|
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.
|
|
79
56
|
|
|
80
|
-
##
|
|
57
|
+
## Leen por defecto. Escriben cuando tú lo dices.
|
|
81
58
|
|
|
82
|
-
Cada mensaje sale con un modo
|
|
59
|
+
Cada mensaje sale con un modo de permiso, y tu modo es el techo de todo lo que ese mensaje arranque.
|
|
83
60
|
|
|
84
|
-
|
|
|
61
|
+
| | | |
|
|
85
62
|
|---|---|---|
|
|
86
|
-
| `#
|
|
87
|
-
| `#
|
|
88
|
-
| `#
|
|
89
|
-
| `#
|
|
90
|
-
| `#4` | **AIRLOCK** | Todo lo de CONTROL más ejecutar comandos: pruebas, builds, `git push`, deploys con las CLIs y sesiones que ya hay en la máquina. Los archivos vuelven con `UNDO`; lo que sale de la nave, no. La anulación pide dos llaves: la designación y la palabra `AIRLOCK`. |
|
|
63
|
+
| `#1` | **EXCHANGE** | Leer y coordinar. El predeterminado. |
|
|
64
|
+
| `#2` | **CREATE** | Añadir archivos nuevos. Lo que ya existía se restaura al terminar. |
|
|
65
|
+
| `#3` | **CONTROL** | Editar el proyecto. Checkpoint antes, lista de cambios y `UNDO` después. |
|
|
66
|
+
| `#4` | **AIRLOCK** | Comandos, `git push`, deploys. Pide dos llaves. |
|
|
91
67
|
|
|
92
|
-
|
|
68
|
+
También hay `#0 GHOST`, para lo que no debe quedar en ninguna parte.
|
|
93
69
|
|
|
94
|
-
|
|
70
|
+
Subir a `#3` o `#4` es una ceremonia deliberada: MADRE te pide el nombre de la carpeta del proyecto, escrito por ti. Ningún agente puede concederse un permiso escribiéndolo en su respuesta.
|
|
95
71
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
**Modo por paso.** Un orquestador puede pedir el modo de cada paso: `@codex #2: crea la página`, `@claude #3: arregla el router`. MADRE lo acota al modo de tu mensaje y al `MAX MODE` de ese agente. Con tu mensaje en `#3`, la palabra del orquestador basta; con tu mensaje en `#1`, un paso `#2` pasa por la escalación.
|
|
99
|
-
|
|
100
|
-
**Dos controles por agente.** En `⚙ CONNECTIONS` cada agente tiene `MAX MODE`, hasta dónde puede llegar un mensaje dirigido a él, y `DEFAULT MODE`, dónde empieza: `#1` solo lectura hasta que armes CREATE, o `#2` para que cada turno pueda añadir archivos sin pedirlo. Aparte, dos habilidades: generar imágenes y web.
|
|
72
|
+
<br>
|
|
101
73
|
|
|
102
74
|
---
|
|
103
75
|
|
|
104
|
-
|
|
76
|
+
<br>
|
|
105
77
|
|
|
106
|
-
|
|
107
|
-
|---|---|---|---|---|---|
|
|
108
|
-
| **Codex** | sí | `-i` | sandbox de escritura | sí, nativo, con su cuenta de ChatGPT | `web_search` |
|
|
109
|
-
| **Claude Code** | sí | lectura de la ruta | reglas de permiso por ruta | sí, con **Image Studio** | WebFetch / WebSearch |
|
|
110
|
-
| **Gemini CLI** | sí | `read_file` | política por patrón | sí, con **Image Studio** | `google_web_search` |
|
|
111
|
-
| **OpenCode** | sí | `-f` | permisos `edit` por patrón | sí, con **Image Studio** | webfetch / websearch |
|
|
112
|
-
| **@madre** | la memoria | no | no | no | no |
|
|
78
|
+
## Lo que MADRE no es
|
|
113
79
|
|
|
114
|
-
|
|
80
|
+
Esto importa más que cualquier función.
|
|
115
81
|
|
|
116
|
-
|
|
82
|
+
**No hay nube.** MADRE no tiene servidor, ni cuenta, ni backend. Corre en `127.0.0.1` y se muere cuando cierras la terminal.
|
|
117
83
|
|
|
118
|
-
|
|
84
|
+
**No guarda tus credenciales.** Cuando das una llave, MADRE la escribe en el archivo donde ese CLI la busca, cerrada a tu usuario, y no conserva copia. Nunca en su config, ni en su registro, ni en sus logs.
|
|
119
85
|
|
|
120
|
-
|
|
86
|
+
**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.
|
|
121
87
|
|
|
122
|
-
|
|
88
|
+
**Y te deja leerlo.** Dentro de la sala puedes abrir el documento exacto que MADRE escribe en tu nombre cada turno, el comando exacto con el que lo entrega, y el registro de cada llamada que salió de esta máquina. No es una promesa: es una pantalla.
|
|
123
89
|
|
|
124
|
-
|
|
125
|
-
@gemini: Sintetiza en un párrafo quién es el autor, separando hechos de inferencias.
|
|
126
|
-
@codex: Misma pregunta; señala la afirmación menos sustentada.
|
|
127
|
-
@claude: Compara ambas síntesis y marca dónde divergen.
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
MADRE ejecuta los pasos como turnos normales: cada uno queda en el ledger, pasa por handoff, presupuesto y timeout. El paso dirigido al orquestador es su turno de cierre. Los delegados no delegan, así que todo plan termina. Máximo cuatro pasos más el cierre.
|
|
90
|
+
**Cero dependencias.** Node y nada más.
|
|
131
91
|
|
|
132
|
-
|
|
92
|
+
<br>
|
|
133
93
|
|
|
134
94
|
---
|
|
135
95
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
Todo lo dicho fuera de GHOST queda indexado junto al ledger, en `~/.pulse/rooms/<sala>/memory.sqlite`. Nada de esto requiere un comando: ocurre solo.
|
|
139
|
-
|
|
140
|
-
**Recall.** Cuando la conversación excede la ventana de contexto, cada turno recibe los intercambios anteriores que coinciden con la petición, citados con su número de secuencia, para cualquier agente.
|
|
141
|
-
|
|
142
|
-
**Notas destiladas.** Cada diez intercambios o tras diez minutos de reposo, el archivista lee lo no destilado y guarda hasta cinco notas tipadas: decisión, hecho, preferencia, pregunta abierta. El archivista es el agente más barato permitido; con Ollama es el modelo local y no cuesta nada.
|
|
143
|
-
|
|
144
|
-
**Por significado.** Con Ollama (`nomic-embed-text`) o con una key de Gemini, intercambios y notas se embeben en segundo plano; una pregunta en español encuentra una decisión escrita en inglés.
|
|
145
|
-
|
|
146
|
-
**Herramientas.** Cada turno lleva adjunto el servidor MCP `pulse-memory`: `memory_search`, `memory_recall`, `memory_notes`, `memory_timeline`, `project_state` y `memory_note`, que guarda una nota cuando tú pides explícitamente recordar algo. La burbuja muestra `◉ memory saved`.
|
|
147
|
-
|
|
148
|
-
**NOSTROMO.** El mapa de la memoria, detrás de la designación del proyecto: MADRE es un sol rojo que late, cada memoria un planeta en el color de su tipo, unidos por venas que pulsan. Se navega, se lee y solo se puede borrar, con doble confirmación. Ocho toques al corazón activan CODE000 y sellan el archivo diez minutos.
|
|
149
|
-
|
|
150
|
-
**@madre.** Con Ollama y un modelo de chat, la memoria habla: `@madre` responde desde todo el archivo con citas `[#n]`, dice cuando la sala nunca discutió algo, no escribe ni ejecuta, y convoca al crew si se lo pides. Sus tokens son locales y no cuentan. Cuando existe un modelo entrenado con la sala, lo usa.
|
|
151
|
-
|
|
152
|
-
**Ajustes.** `⚙ CONNECTIONS → MEMORY`: quién destila, quiénes pueden, cada cuánto, dónde se embebe, cuánto contexto puede ocupar el recall. Todo en `~/.pulse/config.json`, sin reiniciar.
|
|
96
|
+
<br>
|
|
153
97
|
|
|
154
|
-
|
|
98
|
+
## Empezar
|
|
155
99
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
## MADRE AI · TU PROPIO MODELO
|
|
100
|
+
Necesitas **Node 22.5** o superior y al menos una de estas CLIs con sesión iniciada: Codex, Claude Code, Gemini CLI u OpenCode.
|
|
159
101
|
|
|
160
|
-
|
|
102
|
+
```
|
|
103
|
+
cd tu-proyecto
|
|
104
|
+
npx @jossuealcala/madre start
|
|
105
|
+
```
|
|
161
106
|
|
|
162
|
-
|
|
107
|
+
La sala abre en `http://127.0.0.1:4317`. **Si no tienes ninguna CLI, ábrela igual**: lo primero que ves es el puente, con un botón para instalar y firmar cada agente ahí mismo. MADRE enseña el comando antes de correrlo y transmite cada línea a la pantalla.
|
|
163
108
|
|
|
164
|
-
|
|
109
|
+
```
|
|
110
|
+
npx @jossuealcala/madre doctor # qué hay instalado, quién tiene sesión
|
|
111
|
+
```
|
|
165
112
|
|
|
166
|
-
|
|
113
|
+
La sala habla **español**. El botón `EN` la pasa a inglés.
|
|
167
114
|
|
|
168
|
-
|
|
115
|
+
<br>
|
|
169
116
|
|
|
170
|
-
|
|
117
|
+
**Cómo funciona cada cosa, con los números exactos:** [docs/REFERENCE.md](docs/REFERENCE.md)
|
|
118
|
+
**Cómo funciona por dentro:** [docs/INTERNALS.md](docs/INTERNALS.md) · **Escribir un módulo:** [docs/SDK.md](docs/SDK.md)
|
|
171
119
|
|
|
172
|
-
|
|
173
|
-
|---|---|
|
|
174
|
-
| `⚙ CONNECTIONS` | Sesión, versión y ruta de cada CLI; `SIGN IN` y `RECHECK`; `MAX MODE`, `DEFAULT MODE` y habilidades; timeouts, presupuesto, delegación, modelo de OpenCode; MEMORY y PRIVACY |
|
|
175
|
-
| `◉ NOSTROMO` | El mapa de la memoria |
|
|
176
|
-
| `SENTINEL` | Fallos que ninguna condición explica y caídas del proceso, con rutas, usuarios, correos y claves eliminados. Cada reporte tiene `REPORT ON GITHUB ↗` para leerlo antes de publicarlo; `AUTO-REPORT`, apagado por defecto, envía los nuevos al colector del proyecto |
|
|
177
|
-
| `RELEASE CHANNEL` | Una consulta a npm al día. Si hay versión nueva, una alerta en la barra y aquí `RESTART WITH x.y.z`: la sala cierra, instala y vuelve en la misma dirección; o el comando para hacerlo tú. Nunca mientras los agentes trabajan |
|
|
178
|
-
| `✎ FEEDBACK` | Un issue en blanco con tu entorno ya escrito |
|
|
120
|
+
<br>
|
|
179
121
|
|
|
180
122
|
---
|
|
181
123
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
`MODULES` en la barra lista los disponibles y su estado. Ninguno escribe en el proyecto salvo la instalación de AHP+, que muestra el comando y pide confirmación.
|
|
124
|
+
<br>
|
|
185
125
|
|
|
186
|
-
|
|
187
|
-
|---|---|
|
|
188
|
-
| **Git Pulse** | `/git status`, `/git log [n]`, `/git diff`, `/git branches`: hechos del repositorio como tarjeta en el hilo, que los agentes también leen. `/git commit "mensaje"` y `/git push confirm`: tu mano sobre el repositorio, con vista previa de lo que saldría |
|
|
189
|
-
| **Image Studio** | `generate_image` para Claude, Gemini y OpenCode, con tu key de Gemini, dentro de CREATE |
|
|
190
|
-
| **RIPLEY** | El visor renderiza HTML, SVG y Markdown en un marco sellado, con recarga automática |
|
|
191
|
-
| **OLLAMA** | Embeddings, archivista y `@madre` en local |
|
|
192
|
-
| **PLAYWRIGHT** | Un navegador headless por turno que solo alcanza esta MADRE: abrir la vista previa de RIPLEY, hacer clic, leer consola, capturas al borrador del turno. Requiere `@playwright/mcp` |
|
|
193
|
-
| **AshCode** (beta) | `$ ash_code`: abrevia mensajes localmente antes de enviarlos y pide respuestas concisas. Puede cambiar el significado; el original siempre queda |
|
|
194
|
-
| **AHP+** | Integración externa opcional: estado verificado del proyecto, checkpoints y handoffs en `.ahp/`; `/ahp status`, `/ahp check`, `/ahp context` |
|
|
126
|
+
## Estado
|
|
195
127
|
|
|
196
|
-
|
|
128
|
+
Beta pública. El núcleo está probado y bajo CI en macOS y Linux con Node 22 y 24; la superficie sigue cambiando. Las decisiones que todavía duelen están escritas, sin adornos, en [*Lo que sale de la máquina*](docs/REFERENCE.md#lo-que-sale-de-la-máquina).
|
|
197
129
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
## LO QUE SALE DE LA MÁQUINA
|
|
130
|
+
Una versión se cierra cuando está en npm. El detalle de cada una en [CHANGELOG.md](CHANGELOG.md); lo que viene, en el [roadmap](https://github.com/jossuealcacao-exe/madre/blob/main/docs/ROADMAP.md).
|
|
201
131
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
- **Dos envíos propios, ambos bajo tu interruptor.** El sentinel, apagado por defecto, envía reportes redactados al colector del proyecto. El canal de liberación, encendido por defecto, pregunta a npm por la última versión: viaja el nombre del paquete, nada más, la misma petición que hace `npx`. `PULSE_UPDATE_CHECK=0` lo apaga.
|
|
205
|
-
- **Escritura.** En `#1` nadie escribe. En `#2` solo se añade: lo que existía se restaura al terminar el turno. En `#3` todo el proyecto salvo las zonas prohibidas, con checkpoint y `UNDO`. En un proyecto sin git, MADRE guarda sus fotografías en un repositorio sombra fuera del proyecto.
|
|
206
|
-
- **Memoria.** Todo lo dicho fuera de GHOST queda en `~/.pulse/rooms/<sala>/` y vuelve a los prompts de todos los agentes de esa sala. GHOST es la salida para lo que no debe recordarse; PRIVACY, para los nombres que nunca deben aparecer.
|
|
132
|
+
Problemas: desde MU/TH/UR (`✎ FEEDBACK`) o en [issues](https://github.com/jossuealcacao-exe/madre/issues).
|
|
133
|
+
Seguridad: [SECURITY.md](SECURITY.md) · Contribuir: [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
207
134
|
|
|
208
|
-
|
|
135
|
+
Apache-2.0 · [Jossué Alcalá](https://jossuealcala.com/en/)
|
|
209
136
|
|
|
210
|
-
|
|
137
|
+
<br>
|
|
211
138
|
|
|
212
|
-
**`~/.pulse/config.json`**, o `PULSE_HOME/config.json`. Lo escribe MU/TH/UR; las variables de entorno mandan al siguiente arranque.
|
|
213
|
-
|
|
214
|
-
```json
|
|
215
|
-
{
|
|
216
|
-
"opencode": { "model": "openai/gpt-5.6-sol" },
|
|
217
|
-
"timeouts": { "default": 300000, "claude": 600000 },
|
|
218
|
-
"room": { "softTokenBudget": 500000, "delegation": true, "maxPlanSteps": 4 },
|
|
219
|
-
"memory": { "archivist": "auto", "every": 10, "idleMinutes": 10, "embedProvider": "auto", "recallShare": 0.3 },
|
|
220
|
-
"privacy": { "terms": [], "marker": "[ENTIDAD-ORG]" },
|
|
221
|
-
"updates": { "check": true },
|
|
222
|
-
"telemetry": { "autoReport": false }
|
|
223
|
-
}
|
|
224
139
|
```
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
| Variable | Predeterminado | Efecto |
|
|
229
|
-
|---|---|---|
|
|
230
|
-
| `PULSE_HOME` | `~/.pulse` | Raíz de las salas y del config |
|
|
231
|
-
| `PULSE_SOFT_TOKEN_BUDGET` | `500000` | Presupuesto local de tokens por agente, ventana rodante de 5 h |
|
|
232
|
-
| `PULSE_CONTEXT_MAX_CHARS` | `16000` | Ventana de transcript inyectada |
|
|
233
|
-
| `PULSE_RECALL_SHARE` | `0.3` | Parte de la ventana para la memoria recordada (`0` la apaga) |
|
|
234
|
-
| `PULSE_DISTILL` · `_EVERY` · `_IDLE_MS` · `_MAX_CHARS` · `_AGENT` · `_MODEL` | `1` · `10` · `600000` · `6000` · — · — | Destilación de notas |
|
|
235
|
-
| `PULSE_EMBED` · `PULSE_EMBED_PROVIDER` | `1` · `auto` | Embeddings; `ollama`, `gemini`, `auto` |
|
|
236
|
-
| `PULSE_OLLAMA` · `_HOST` · `_MODEL` · `_EMBED_MODEL` | `1` · `http://127.0.0.1:11434` · el mejor · el mejor | Ollama |
|
|
237
|
-
| `PULSE_MEMORY_TOOLS` | `1` | Servidor MCP `pulse-memory` en cada turno |
|
|
238
|
-
| `PULSE_PRIVATE_TERMS` · `PULSE_PRIVATE_MARKER` | — · `[ENTIDAD-ORG]` | Términos privados extra y su marcador |
|
|
239
|
-
| `PULSE_UPDATE_CHECK` | `1` | `0` apaga la consulta diaria a npm |
|
|
240
|
-
| `PULSE_REPORT_URL` · `PULSE_AUTO_REPORT` | colector del proyecto · `0` | Sentinel |
|
|
241
|
-
| `PULSE_AGENT_TIMEOUT_MS` · `PULSE_<AGENTE>_TIMEOUT_MS` | `180000` · — | Timeouts |
|
|
242
|
-
| `PULSE_MAX_MESSAGE_CHARS` | `20000` | Tamaño máximo de un mensaje |
|
|
243
|
-
| `PULSE_DELEGATION` · `PULSE_MAX_PLAN_STEPS` | `1` · `4` | Delegación |
|
|
244
|
-
| `PULSE_ESCALATION_MS` | `180000` | Cronómetro de la escalación |
|
|
245
|
-
| `PULSE_OPENCODE_MODEL` | del config | `proveedor/modelo` para OpenCode |
|
|
246
|
-
| `PULSE_CLAUDE_USAGE` · `PULSE_OFFICIAL_QUOTA` | `0` · `1` | Cuota oficial de Claude (lee el llavero); `0` apaga todas las lecturas de proveedor |
|
|
247
|
-
| `PULSE_GEMINI_IDLE_MS` · `PULSE_GEMINI_RETRIES` · `PULSE_GEMINI_FALLBACK_MODEL` | `90000` · `1` · `gemini-2.5-flash` | Gemini bajo carga |
|
|
248
|
-
|
|
249
|
-
Cómo funcionan por dentro el ledger, el stream, la memoria, los adaptadores y la recuperación: [`docs/INTERNALS.md`](https://github.com/jossuealcacao-exe/madre/blob/main/docs/INTERNALS.md).
|
|
250
|
-
|
|
251
|
-
---
|
|
252
|
-
|
|
253
|
-
## ESTADO
|
|
254
|
-
|
|
255
|
-
Beta pública. El núcleo está probado y bajo CI en macOS y Linux con Node 22 y 24; la superficie sigue cambiando y las decisiones que aún duelen están escritas arriba, en *Lo que sale de la máquina*. Una versión se cierra cuando está en npm; el detalle de cada una, en [CHANGELOG.md](https://github.com/jossuealcacao-exe/madre/blob/main/CHANGELOG.md), y lo que viene, en el [roadmap](https://github.com/jossuealcacao-exe/madre/blob/main/docs/ROADMAP.md).
|
|
256
|
-
|
|
257
|
-
Problemas: desde MU/TH/UR (`✎ FEEDBACK` o el sentinel) o en [issues](https://github.com/jossuealcacao-exe/madre/issues). Seguridad: [SECURITY.md](https://github.com/jossuealcacao-exe/madre/blob/main/SECURITY.md). Contribuir: [CONTRIBUTING.md](https://github.com/jossuealcacao-exe/madre/blob/main/CONTRIBUTING.md).
|
|
258
|
-
|
|
259
|
-
Apache-2.0. Ver `LICENSE` y `NOTICE`.
|
|
140
|
+
NOBODY DELETES MOTHER'S MEMORY.
|
|
141
|
+
```
|
package/SECURITY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Security · MU/TH/UR 6000 · PRIORITY ONE
|
|
2
2
|
|
|
3
|
-
MADRE runs on your machine, talks to no cloud of its own and stores no credentials. Its agents are CLIs you installed; they talk to their providers.
|
|
3
|
+
MADRE runs on your machine, talks to no cloud of its own and stores no credentials. Its agents are CLIs you installed; they talk to their providers. What leaves the machine, and what never does, is written out in [`docs/REFERENCE.md`](docs/REFERENCE.md#lo-que-sale-de-la-máquina).
|
|
4
4
|
|
|
5
5
|
## Reporting
|
|
6
6
|
|
|
@@ -14,6 +14,7 @@ Please include the MADRE version (`madre doctor --json`), the platform, which ag
|
|
|
14
14
|
## Known, accepted for the beta
|
|
15
15
|
|
|
16
16
|
- In `#3 CONTROL`, `.env` files, `.pulse/`, `.madre/` and `.claude/settings.local.json` are made read-only for the length of the turn and restored from the checkpoint afterwards; `.git/` stays writable because the CLIs need it and is only restored after the turn. An agent that changes permissions on purpose is caught by that restoration, not prevented.
|
|
17
|
+
- `#4 AIRLOCK` runs commands with the CLIs and sessions already on the machine: tests, builds, `git push`, deploys. Files come back with `UNDO`; **what leaves the machine does not**. Arming it asks for two keys — the project designation and the word `AIRLOCK` — and the agent's `MAX MODE` in CONNECTIONS must already reach it, which is a separate deliberate act. No agent can raise its own ceiling, and a permission an agent writes into its own answer is not a permission. Treat `#4` as handing that agent your shell.
|
|
17
18
|
- Everything said outside `#0 GHOST` is kept in the room's memory and reaches every agent of that room. Use GHOST for what must not be remembered.
|
|
18
19
|
|
|
19
20
|
NOBODY DELETES MOTHER'S MEMORY. EVERYTHING ELSE IS FAIR GAME.
|
package/bin/madre.mjs
CHANGED
|
@@ -3,27 +3,67 @@
|
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
5
5
|
import { readFile, stat } from 'node:fs/promises';
|
|
6
|
-
import { startPulse } from '../src/server.mjs';
|
|
6
|
+
import { openUrl, startPulse } from '../src/server.mjs';
|
|
7
|
+
import { realpath } from 'node:fs/promises';
|
|
7
8
|
import { detectAgents } from '../src/runtime-detection.mjs';
|
|
8
9
|
import { probeAll } from '../src/auth-probe.mjs';
|
|
9
10
|
import { applyConfigToEnv, loadConfig } from '../src/config.mjs';
|
|
10
11
|
import { isOnline, runSetup } from '../src/setup.mjs';
|
|
11
12
|
import { parseArgs } from '../src/cli-args.mjs';
|
|
13
|
+
import { createServer } from 'node:net';
|
|
12
14
|
|
|
13
15
|
const cli = parseArgs(process.argv.slice(2));
|
|
14
16
|
const { command } = cli;
|
|
15
17
|
const option = (name, fallback) => cli.option(name.replace(/^--/, ''), fallback);
|
|
18
|
+
|
|
19
|
+
// Is a MADRE already serving this very project nearby? `start` walks ports, so look along the
|
|
20
|
+
// same short stretch and compare the project each one reports.
|
|
21
|
+
async function roomAlreadyOpen(port, projectRoot, span = 8) {
|
|
22
|
+
const mine = await realpath(projectRoot).catch(() => resolve(projectRoot));
|
|
23
|
+
for (let candidate = port; candidate < port + span; candidate += 1) {
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch(`http://127.0.0.1:${candidate}/api/version`, { signal: AbortSignal.timeout(500) });
|
|
26
|
+
if (!response.ok) continue;
|
|
27
|
+
const info = await response.json();
|
|
28
|
+
const theirs = info?.project ? await realpath(info.project).catch(() => info.project) : null;
|
|
29
|
+
if (theirs && theirs === mine) return { url: `http://127.0.0.1:${candidate}`, version: info.current ?? null };
|
|
30
|
+
} catch {
|
|
31
|
+
// nothing listening there, or not a MADRE: keep looking.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
16
38
|
const has = (name) => cli.has(name.replace(/^--/, ''));
|
|
17
39
|
|
|
40
|
+
// Is somebody already on that port? Asked before anything is started, because opening a room
|
|
41
|
+
// means finding the agents, opening the archive and waking the memory, and doing all of that only
|
|
42
|
+
// to then refuse is a wait nobody needed. The real bind below stays the authority: this only
|
|
43
|
+
// saves the waiting.
|
|
44
|
+
function portTaken(port, host = '127.0.0.1') {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const probe = createServer();
|
|
47
|
+
probe.once('error', (error) => resolve(error.code === 'EADDRINUSE'));
|
|
48
|
+
probe.listen(port, host, () => probe.close(() => resolve(false)));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
18
52
|
// With an explicit --port a busy port is an error the user asked for. Without
|
|
19
53
|
// one, MOTHER walks up to the next free port so a second room just opens.
|
|
20
54
|
async function openRoom(options) {
|
|
21
55
|
const explicit = cli.explicit('port');
|
|
22
56
|
let port = options.port;
|
|
57
|
+
if (explicit && await portTaken(port)) {
|
|
58
|
+
console.error(`\n MOTHER › port ${port} is already in use. Another MADRE may be open there; try --port ${port + 1} or omit --port to pick one automatically.\n`);
|
|
59
|
+
process.exit(2);
|
|
60
|
+
}
|
|
23
61
|
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
24
62
|
try {
|
|
25
63
|
return await startPulse({ ...options, port });
|
|
26
64
|
} catch (error) {
|
|
65
|
+
// The room itself says it is taken, whatever port this attempt used.
|
|
66
|
+
if (error.code === 'ROOM_IN_USE') { console.error(`\n MOTHER › ${error.message}\n`); process.exit(2); }
|
|
27
67
|
if (error.code !== 'EADDRINUSE') throw error;
|
|
28
68
|
if (explicit) {
|
|
29
69
|
console.error(`\n MOTHER › port ${port} is already in use. Another MADRE may be open there; try --port ${port + 1} or omit --port to pick one automatically.\n`);
|
|
@@ -132,7 +172,7 @@ if (command === 'doctor' && (has('--catalog') || has('--conditions'))) {
|
|
|
132
172
|
console.log(` ${'Ollama'.padEnd(10)} ${ollama.disabled ? 'ignored (PULSE_OLLAMA=0)' : ollama.running ? `running${ollama.chatModel ? ` · @madre with ${ollama.chatModel}` : ' · no chat model yet'}${ollama.embedModel ? ` · embeddings ${ollama.embedModel}` : ''}` : 'not running · optional'}`);
|
|
133
173
|
console.log(` ${'MADRE'.padEnd(10)} ${update.current}${update.available ? ` · ${update.latest} available · ${update.command}` : update.latest ? ' · up to date' : update.enabled ? ' · npm not reachable' : ' · update check off (PULSE_UPDATE_CHECK=0)'}`);
|
|
134
174
|
console.log(`\n Project ${result.project}`);
|
|
135
|
-
console.log(result.ok ? '\nReady to start. Known conditions and fixes: `madre doctor --catalog [query]`.\n' : '\nNo agent is online. Run `madre
|
|
175
|
+
console.log(result.ok ? '\nReady to start. Known conditions and fixes: `madre doctor --catalog [query]`.\n' : '\nNo agent is online yet. Run `madre start`: the room opens anyway and walks you through installing and signing one in. Known conditions and fixes: `madre doctor --catalog`.\n');
|
|
136
176
|
}
|
|
137
177
|
process.exitCode = result.ok ? 0 : 1;
|
|
138
178
|
} else if (command === 'setup') {
|
|
@@ -145,22 +185,25 @@ if (command === 'doctor' && (has('--catalog') || has('--conditions'))) {
|
|
|
145
185
|
} else if (command === 'start') {
|
|
146
186
|
const port = Number(option('--port', '4317'));
|
|
147
187
|
const noOpen = has('--no-open');
|
|
148
|
-
// First contact
|
|
149
|
-
// through
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
188
|
+
// First contact happens in the room, not here: MADRE opens even with nobody online and the
|
|
189
|
+
// bridge walks the human through installing and signing in. `madre setup` keeps the terminal
|
|
190
|
+
// wizard for whoever prefers it, and `--setup` asks for it explicitly.
|
|
191
|
+
if (has('--setup')) {
|
|
192
|
+
const { action } = await runSetup({ projectRoot, stateRoot });
|
|
193
|
+
if (action !== 'start') process.exit(0);
|
|
194
|
+
}
|
|
195
|
+
// A room for this project may already be open. Show that one instead of starting a second.
|
|
196
|
+
const running = await roomAlreadyOpen(port, projectRoot);
|
|
197
|
+
if (running) {
|
|
198
|
+
console.log(`\nMADRE is already open for this project\n\n ${running.url}\n Project: ${projectRoot}\n Close that room first if you want a fresh one.\n`);
|
|
199
|
+
if (!noOpen) openUrl(running.url);
|
|
200
|
+
process.exit(0);
|
|
158
201
|
}
|
|
159
202
|
await openRoom({ port, projectRoot, openBrowser: !noOpen });
|
|
160
203
|
} else if (command === 'help') {
|
|
161
204
|
console.log(`MADRE
|
|
162
205
|
|
|
163
|
-
madre start [--project PATH] [--port 4317] [--no-open] [--
|
|
206
|
+
madre start [--project PATH] [--port 4317] [--no-open] [--setup]
|
|
164
207
|
Without --port, a busy 4317 falls through to the next free port.
|
|
165
208
|
madre setup [--project PATH] [--port 4317] [--no-open]
|
|
166
209
|
madre doctor [--project PATH] [--json]
|
package/docs/INTERNALS.md
CHANGED
|
@@ -26,8 +26,24 @@ La ventana de contexto que recibe un agente es de 16 000 caracteres (`PULSE_CONT
|
|
|
26
26
|
- `memories` con FTS5: las notas destiladas, tipadas (`decision`, `fact`, `preference`, `question`), con las secuencias que las sustentan, quién las escribió y su origen (`distilled` o `noted`).
|
|
27
27
|
- `entry_vectors` y `memory_vectors`: embeddings de 768 dimensiones, calculados en segundo plano y nunca en el camino crítico de un turno.
|
|
28
28
|
|
|
29
|
+
**Conversaciones.** Un proyecto tiene una memoria y muchas conversaciones. El archivo, la tripulación, los módulos y la lista de privacidad son del proyecto; una conversación es solo el registro de una línea de trabajo. La primera es el `events.jsonl` que siempre estuvo ahí —no se mueve ningún archivo para añadir esto—, las demás viven en `<sala>/chats/<id>/events.jsonl`, y `<sala>/chats.json` guarda sus nombres y cuál está abierta. La numeración es del proyecto, no de la conversación: al abrir una se arranca desde la secuencia más alta que alcanzó cualquier otra, así que `#1411` significa un intercambio de este proyecto y el índice de memoria, las citas y NOSTROMO siguen valiendo. Hay hueco en un ledger y eso está bien: las secuencias son un orden, no un conteo.
|
|
30
|
+
|
|
31
|
+
Una conversación a la vez maneja a la tripulación —dos hilos editando el mismo árbol de trabajo no es una función, es una forma de perder trabajo—, así que abrir otra se niega mientras haya un turno corriendo. Y una sala por proyecto: `<sala>/open.json` lleva el pid y el puerto del servidor que la tiene, y otro arranque sobre el mismo proyecto se niega con la dirección de la sala abierta. Sin eso, dos servidores escribiendo conversaciones distintas del mismo proyecto repartirían la misma secuencia dos veces y el archivo se quedaría callado con una de ellas.
|
|
32
|
+
|
|
29
33
|
**Recall.** Cuando el transcript excede la ventana, cada turno recibe además, dentro del mismo presupuesto (`PULSE_RECALL_SHARE`, 30 % por defecto), las notas y los intercambios anteriores que coinciden con la petición. La búsqueda léxica pondera cada término por su rareza en la sala: una ruta o un nombre pesan más que una palabra común. Con vectores, se fusiona con la similitud de coseno.
|
|
30
34
|
|
|
35
|
+
**Activación en cascada.** Cada recuerdo que entra a un turno deja un renglón con el lote que compartió, así que la sala sabe qué notas llegan juntas. De ahí sale una asociación —Jaccard sobre los turnos donde cada una fue encontrada por la búsqueda misma— y el recall guarda dos huecos para ella: al traer una nota, trae también la que la acompaña, aunque no compartan una sola palabra. Solo votan los turnos donde la búsqueda las encontró por mérito propio, nunca los que la cascada creó, o la red se cerraría sobre sí misma en unos días. La fuerza es un cociente: una pareja que deja de coincidir se apaga sola. Umbral 0.34, mínimo dos coincidencias, y la búsqueda nunca pierde un hueco a manos de la asociación. `PULSE_RECALL_CASCADE=0` o el interruptor en MU/TH/UR la apagan.
|
|
36
|
+
|
|
37
|
+
**Zonas frías.** Un recuerdo está frío cuando se cumplen tres cosas a la vez: ningún turno lo ha llevado nunca, no comparte tema con ningún otro —así que tampoco se le puede llegar de lado— y el archivo se abrió al menos doce veces desde que se escribió. La tercera es la que importa: nunca-recordado es lo que toda nota es el día que nace; frío es la oportunidad que pasó de largo. Las oportunidades se cuentan en turnos que de verdad entraron al archivo, y solo desde el día en que la sala empezó a llevar el rastro. NOSTROMO los cuenta en su cabecera y los rodea de un anillo tenue cuando se le pide.
|
|
38
|
+
|
|
39
|
+
**Qué preguntar.** La sala escribe las preguntas cuya respuesta le falta, siempre con las palabras que ya tiene y nunca inventando un tema que nadie levantó. Tres pozos: las preguntas que el archivista registró como abiertas y nadie contestó, los recuerdos fríos —preguntar es la alternativa honesta a tirar algo que nunca tuvo su oportunidad— y la clase de nota de la que el archivo anda corto (decisiones, preferencias, hechos; preguntas no, porque un archivo corto de preguntas no se arregla pidiendo más). Se toman por turnos para que un pozo lleno no sea la lista entera, y dos formas de preguntar lo mismo cuentan como una. Nada se envía: la pregunta va al compositor y la humana decide quién responde.
|
|
40
|
+
|
|
41
|
+
**El veredicto.** `GET /api/maturity` junta las seis lecturas, las tres pruebas, las zonas frías y las preguntas pendientes, y devuelve una frase y una instrucción. El orden no lo decide el puntaje: primero lo falso (contradicciones abiertas), luego lo que el archivo no puede contestar —y ahí manda a las preguntas que la sala ya escribió—, luego la lectura más floja citando su propio consejo, y al final la prueba que nadie ha corrido. La consistencia se calcula al leer; la cobertura se refresca sola una vez al día, en segundo plano y solo con embeddings locales.
|
|
42
|
+
|
|
43
|
+
**Las tres pruebas.** La lectura de madurez cuenta de qué está hecho el archivo; no dice si funciona. Eso lo dice una prueba, y una prueba solo vale si puede fallar. `COVERAGE` toma preguntas reales del historial, corre el recall en el punto exacto en que cada una se hizo y lo compara con la respuesta que de verdad se dio. `CONSISTENCY` mira las contradicciones que EYECAT sigue sosteniendo, lo que quedó fuera de circulación y si las aberraciones se archivan más seguido últimamente o menos. `MATCH` vuelve a hacerle al modelo local preguntas que contestó una CLI de frontera y compara. Ninguna se gasta un turno de proveedor; la tercera corre en segundo plano y se puede detener.
|
|
44
|
+
|
|
45
|
+
Las tres se miden **contra un control**: dentro de una sala todo habla de los mismos temas, así que a un embedder cualquier par de textos le parece cercano —la primera corrida de COVERAGE dio treinta de treinta—. Para contar, lo que el archivo entregó tiene que ganarle a lo que habría entregado para otra pregunta, por un margen. Cada resultado dice cuántos casos tuvo, con qué midió (significado o palabras), la barra y la media contra el control.
|
|
46
|
+
|
|
31
47
|
**Destilación.** Cada `PULSE_DISTILL_EVERY` intercambios sin destilar, o tras `PULSE_DISTILL_IDLE_MS` de reposo, el archivista lee un lote acotado por `PULSE_DISTILL_MAX_CHARS`, del más reciente hacia atrás, y escribe hasta cinco notas. El archivista es el más barato permitido en el orden Ollama, Gemini, OpenCode, Codex, Claude; el que falla queda en banca treinta minutos y pasa el siguiente. Una sola llamada por lote, nunca durante un turno. Para los modelos locales se pide JSON estricto.
|
|
32
48
|
|
|
33
49
|
**Embeddings.** `PULSE_EMBED_PROVIDER=auto` usa Ollama si corre con un modelo de embeddings (`nomic-embed-text`), si no la key de Gemini (`gemini-embedding-001`, la misma key que usa el Gemini CLI, incluida la del llavero de macOS). Lotes de hasta 100 textos por llamada. `PULSE_EMBED=0` deja el recall léxico.
|