@jossuealcala/madre 1.0.0 → 1.0.2

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.
Files changed (174) hide show
  1. package/ADDITIONAL-TERMS.md +93 -0
  2. package/CHANGELOG.en.md +1945 -0
  3. package/CHANGELOG.md +78 -1
  4. package/CONTRIBUTING.md +5 -1
  5. package/LICENSE +661 -202
  6. package/NOTICE +34 -3
  7. package/README.en.md +245 -0
  8. package/README.md +23 -13
  9. package/TRADEMARKS.en.md +50 -0
  10. package/TRADEMARKS.md +82 -0
  11. package/bin/madre.mjs +2 -0
  12. package/docs/INTERNALS.md +8 -2
  13. package/docs/REFERENCE.md +17 -7
  14. package/docs/en/INTERNALS.md +103 -0
  15. package/docs/en/REFERENCE.md +339 -0
  16. package/docs/en/SDK.md +306 -0
  17. package/docs/en/training/README.md +59 -0
  18. package/docs/madre-banner.png +0 -0
  19. package/docs/training/README.md +20 -8
  20. package/docs/training/train.sh +2 -1
  21. package/package.json +14 -5
  22. package/public/abduction.js +2 -0
  23. package/public/app.js +2066 -488
  24. package/public/attribution.js +71 -0
  25. package/public/brands.js +3 -1
  26. package/public/editor.js +2 -0
  27. package/public/es.js +1706 -1226
  28. package/public/i18n.js +25 -1
  29. package/public/index.html +13 -10
  30. package/public/inquiry.js +2 -0
  31. package/public/loader.js +2 -0
  32. package/public/macroverso.js +24 -2
  33. package/public/nostromo-engine.js +2 -0
  34. package/public/resay.js +6 -0
  35. package/public/review-ask.js +2 -0
  36. package/public/routing.js +23 -1
  37. package/public/salas-mac.css +539 -28
  38. package/public/salas-mac.html +30 -22
  39. package/public/salas-mac.js +1498 -144
  40. package/public/salas.css +2 -0
  41. package/public/salas.html +1 -0
  42. package/public/salas.js +4 -2
  43. package/public/styles.css +1123 -20
  44. package/public/syntax.js +2 -0
  45. package/public/touch.js +2 -0
  46. package/public/tour-chapters.js +164 -52
  47. package/public/tour.js +85 -12
  48. package/public/troubleshooting.js +20 -18
  49. package/src/adapters/claude.mjs +2 -0
  50. package/src/adapters/codex.mjs +2 -0
  51. package/src/adapters/gemini.mjs +3 -1
  52. package/src/adapters/madre.mjs +3 -1
  53. package/src/adapters/opencode.mjs +7 -3
  54. package/src/adapters/process.mjs +2 -0
  55. package/src/asking.mjs +2 -0
  56. package/src/auth-probe.mjs +32 -7
  57. package/src/bridge/devices.mjs +2 -0
  58. package/src/bridge/handoff.mjs +2 -0
  59. package/src/bridge/host.mjs +2 -0
  60. package/src/bridge/network.mjs +2 -0
  61. package/src/bridge/policy.mjs +3 -1
  62. package/src/bridge/qr.mjs +2 -0
  63. package/src/bridge/server.mjs +2 -0
  64. package/src/capabilities.mjs +2 -0
  65. package/src/chats.mjs +2 -0
  66. package/src/checkpoint.mjs +2 -0
  67. package/src/choices.mjs +2 -0
  68. package/src/cli-args.mjs +2 -0
  69. package/src/cold.mjs +2 -0
  70. package/src/commands.mjs +2 -0
  71. package/src/config.mjs +2 -0
  72. package/src/conversation-context.mjs +15 -1
  73. package/src/credentials.mjs +112 -3
  74. package/src/dataset.mjs +2 -0
  75. package/src/directives.mjs +8 -6
  76. package/src/distiller.mjs +2 -0
  77. package/src/embeddings.mjs +2 -0
  78. package/src/enlaces.mjs +6 -4
  79. package/src/event-store.mjs +2 -0
  80. package/src/exam.mjs +6 -4
  81. package/src/extensions.mjs +33 -10
  82. package/src/eyecat-watch.mjs +2 -0
  83. package/src/eyecat.mjs +2 -0
  84. package/src/file-ops.mjs +2 -0
  85. package/src/files.mjs +32 -0
  86. package/src/github.mjs +2 -0
  87. package/src/heartbeat.mjs +57 -0
  88. package/src/i18n.mjs +15 -1
  89. package/src/image-studio.mjs +2 -0
  90. package/src/json-file.mjs +2 -0
  91. package/src/launch.mjs +2 -0
  92. package/src/lease.mjs +5 -1
  93. package/src/lobby-work.mjs +2 -0
  94. package/src/local-ai.mjs +50 -0
  95. package/src/looks.mjs +40 -5
  96. package/src/maturity.mjs +3 -1
  97. package/src/mcp/connect.mjs +345 -0
  98. package/src/mcp/github-server.mjs +2 -0
  99. package/src/mcp/image-server.mjs +2 -0
  100. package/src/mcp/memory-server.mjs +2 -0
  101. package/src/mcp/proxy.mjs +153 -0
  102. package/src/mcp/smtp-server.mjs +2 -0
  103. package/src/memory-dashboard.mjs +2 -0
  104. package/src/memory-tools.mjs +2 -0
  105. package/src/memory.mjs +2 -0
  106. package/src/models.mjs +83 -3
  107. package/src/modules/ahp.mjs +2 -0
  108. package/src/modules/ash-policy.mjs +2 -0
  109. package/src/modules/ash.mjs +3 -1
  110. package/src/modules/bishop-plan.mjs +5 -3
  111. package/src/modules/bishop.mjs +13 -11
  112. package/src/modules/choices.mjs +2 -0
  113. package/src/modules/correo.mjs +2 -0
  114. package/src/modules/enlaces.mjs +2 -0
  115. package/src/modules/git-pulse.mjs +2 -0
  116. package/src/modules/github.mjs +2 -0
  117. package/src/modules/helpers.mjs +2 -0
  118. package/src/modules/image-studio.mjs +3 -1
  119. package/src/modules/index.mjs +4 -1
  120. package/src/modules/intents-calibration.mjs +2 -0
  121. package/src/modules/intents-desk.mjs +4 -2
  122. package/src/modules/intents-observe.mjs +2 -0
  123. package/src/modules/intents-policy.mjs +4 -1
  124. package/src/modules/intents-try.mjs +2 -0
  125. package/src/modules/intents.mjs +8 -6
  126. package/src/modules/mcp-connect.mjs +293 -0
  127. package/src/modules/ollama.mjs +17 -3
  128. package/src/modules/playwright.mjs +25 -10
  129. package/src/modules/ripley.mjs +2 -0
  130. package/src/modules/runs.mjs +5 -3
  131. package/src/modules/sdk.mjs +2 -0
  132. package/src/modules/updates.mjs +2 -0
  133. package/src/mother.mjs +2 -0
  134. package/src/ollama.mjs +2 -0
  135. package/src/outbound.mjs +24 -5
  136. package/src/override.mjs +2 -0
  137. package/src/ports.mjs +2 -0
  138. package/src/prices.mjs +2 -0
  139. package/src/privacy.mjs +2 -0
  140. package/src/quota-monitor.mjs +2 -0
  141. package/src/quota-sources.mjs +2 -0
  142. package/src/request-guard.mjs +2 -0
  143. package/src/review-ask.mjs +2 -0
  144. package/src/room/archivist.mjs +2 -0
  145. package/src/room/attachments.mjs +2 -0
  146. package/src/room/budget.mjs +2 -0
  147. package/src/room/context.mjs +2 -0
  148. package/src/room/control.mjs +7 -1
  149. package/src/room/copilot.mjs +76 -0
  150. package/src/room/economy.mjs +2 -0
  151. package/src/room/escalation.mjs +3 -0
  152. package/src/room/ghost.mjs +2 -0
  153. package/src/room/guard.mjs +2 -0
  154. package/src/room/prompt.mjs +13 -4
  155. package/src/room/returns.mjs +82 -0
  156. package/src/room/runs.mjs +4 -0
  157. package/src/room/vectors.mjs +2 -0
  158. package/src/room.mjs +264 -45
  159. package/src/router.mjs +2 -0
  160. package/src/runs.mjs +2 -0
  161. package/src/runtime-detection.mjs +2 -0
  162. package/src/salas.mjs +2 -0
  163. package/src/sentinel-errors.mjs +2 -0
  164. package/src/server-sentinel.mjs +2 -0
  165. package/src/server.mjs +485 -55
  166. package/src/setup.mjs +2 -0
  167. package/src/smtp.mjs +2 -0
  168. package/src/tour.mjs +26 -9
  169. package/src/updates.mjs +2 -0
  170. package/src/usage-sentinel.mjs +2 -0
  171. package/src/vault.mjs +2 -0
  172. package/src/verdict.mjs +3 -1
  173. package/src/whats-new.mjs +12 -10
  174. package/docs/madre-banner.svg +0 -25
package/docs/en/SDK.md ADDED
@@ -0,0 +1,306 @@
1
+ # MADRE SDK · write your own modules
2
+
3
+ A MADRE module is **one file**. No build, no dependencies, no signing up anywhere. You copy it into a folder, reload the modules from «Create your module» in MODULES, and it shows up on the board with its switch. You can write it by hand or ask an AI for it, with this document as context.
4
+
5
+ ## Where it lives
6
+
7
+ | Folder | Scope |
8
+ |---|---|
9
+ | `~/.pulse/modules/*.mjs` | All your rooms |
10
+ | `<proyecto>/.madre/modules/*.mjs` | That project only |
11
+
12
+ No agent can write there: `.madre/` is a forbidden zone in CONTROL and AIRLOCK, and `~/.pulse` is outside every project. A module gets into MADRE only because you put it there.
13
+
14
+ ## The file
15
+
16
+ The `export default` is a plain object. MADRE wraps it with `defineModule`. There are two complete
17
+ examples, and both work as is:
18
+
19
+ | | what it teaches |
20
+ |---|---|
21
+ | [`hello-module.mjs`](../sdk/hello-module.mjs) | the minimum: a switch, one setting, a `/hello` command |
22
+ | [`connector-module.mjs`](../sdk/connector-module.mjs) | a real connector: key in the vault, declared destination, and **a single file that is the module page when imported and the MCP server when executed** |
23
+
24
+ Start with the first one. If your module talks to something outside this computer, copy the
25
+ second: that shape can't be guessed, and the obvious way to write it fails **silently** (see
26
+ "A file that is also a server", below).
27
+
28
+ ```js
29
+ export default {
30
+ id: 'hello', // kebab-case
31
+ name: 'HELLO',
32
+ summary: 'Qué hace, en una frase.',
33
+ settings: { enabled: false, greeting: 'hola' },
34
+ async status(ctx) { return { status: { installed: Boolean(ctx.settings.enabled), detail: ctx.settings.enabled ? 'on' : 'off' } }; },
35
+ slash: [{ name: 'hello', usage: '/hello [name]', summary: 'Saluda.', async execute(ctx, args) { return { ok: true, title: 'HELLO', text: `${ctx.settings.greeting}, ${args[0] ?? 'crew'}` }; } }],
36
+ };
37
+ ```
38
+
39
+ **Your module does not import packages.** Not MADRE, not the SDK, nothing from npm. Before installing it, MADRE copies your file to a separate temporary folder and loads it there —that way it checks what it is without running it where it lives— and that folder has no `node_modules`: an `import` by package name doesn't resolve and the installation fails.
40
+
41
+ **Node builtins do work**, always: `node:fs`, `node:url`, `node:tls`, `node:child_process`. That's enough to speak any protocol. What there isn't is anything to install.
42
+
43
+ If you need `defineModule` —to compute something before defining the module, for example— **MADRE hands it to you**: export a function and you receive it as an argument.
44
+
45
+ ```js
46
+ export default ({ defineModule }) => defineModule({
47
+ id: 'mi-modulo',
48
+ name: 'MI MÓDULO',
49
+ // …lo que hayas calculado arriba
50
+ });
51
+ ```
52
+
53
+ > The modules that ship with MADRE do write `import { defineModule } from './sdk.mjs'`, because they live inside the package. **If you ask an agent to write one for you, it will read those files and copy that line** — and it won't work. Tell it to export a plain object, or the function above.
54
+
55
+ ## What a module can declare
56
+
57
+ This table is **the complete contract**: 36 fields, not one more. Any other field you write, MADRE
58
+ tells you about at install time ("I didn't understand these fields") instead of silently ignoring it. A test in
59
+ the suite compares this table against the code, so if they ever drift apart, it's a failure.
60
+
61
+
62
+ | Field | What it is |
63
+ |---|---|
64
+ | `id`, `name`, `vendor`, `summary`, `creates`, `requires` | Its page in MODULES. `summary` is one sentence: what it does. `creates` and `requires` are short lines, one idea each |
65
+ | `version` | Yours, and it starts at `1.0.0`. If you don't declare it, the page shows the file's date |
66
+ | `tracks` | If your module wraps something from outside, its version isn't yours: it's that thing's. Declare `{ name, npm }` or `{ name, github }` and return the version found in `status(ctx)` as `runs: [{ name, version }]`. MADRE shows that version and looks for a newer one on its own, once a day, with the page's ↻ button to check now |
67
+ | `updatePlan(ctx, { latest })` | How that new version is brought to this computer: `{ command, args, display, note, after }`. MADRE shows `display` and runs nothing until the human has read it; the output lands in the room line by line. With no command, return `{ command: null, note, download }` and the page sends you to download it |
68
+ | `updates` | An https `{ url }` where you publish **your own module**. With that, its page gets a button that fetches the file, verifies it the same way as an installation, and replaces it if it passes. If you installed it from a file, MADRE remembers which one and you don't need to declare anything: edit it and ask for a new copy |
69
+ | `settings` | Default values. They live in `~/.pulse/config.json` under `modules.<idEnCamelCase>`; `enabled` is the switch |
70
+ | `status(ctx)` | What the card shows: `{ status: { installed, detail }, preflight: { ok, problems }, install: { display } }`. `install` is returned from here; it is not a top-level field of the module |
71
+ | `status(ctx)` → `runs` | What your module manages that isn't MADRE: `[{ name, version, target }]`. `version` is what you found on this computer (`null` if it isn't there), `target` is what you would install. If it matches `tracks.name`, that is the page's version |
72
+ | `toggle(ctx, payload)` | Replaces the default switch; `confirm: 'texto'` asks for confirmation before turning on |
73
+ | `onToggle(ctx, enabled)` | Reacts to the switch |
74
+ | `slash` | `/name` commands that run on the server with your `ctx` and return `{ ok, title, text }`. The room shows them as a card and the agents read them |
75
+ | `toolsForTurn(ctx, turn)` | MCP servers for an agent's turn: `[{ name, command, args, env, tools, sends, brief }]`. MADRE attaches them to the CLI in its isolated run and describes `brief` to the agent. `turn` carries the agent, the mode, the lease, the folder, the port, and `turn.turn`: the id of the reply that turn will write. If your server calls back to the room on behalf of the turn, send that id; `ctx.room.runningTurn(id)` tells you whether it is still running and for which agent |
76
+ | `toolsForTurn` → `sends` | Which of your tools **send something outside**. The core withdraws them below `#4` AIRLOCK: the model isn't even told they exist. See below |
77
+ | `reaches` | Where your module reaches, in the same four answers MADRE gives about its own. See below |
78
+ | `secrets` | The keys you need, declared instead of drawn: `[{ name, label, note, where }]`. MADRE draws the field, keeps it in its vault under your id, and gives it back to you through `ctx.vault`. See below |
79
+ | `routes` | Your own HTTP routes: `{ method, path, handler(ctx, { payload, params, url, request, viaPhone }) }` → `{ status, body }`. `viaPhone` is `true` when the request comes from a paired phone: deny it yourself if your route belongs to the Mac |
80
+ | `onEvent(ctx, event)` | Every ledger event of every live conversation in the room, while your module is on. `ctx.room` and `ctx.chat` are that conversation's, and `ctx.record` writes into it. It runs separately: whatever it takes long on or fails at doesn't stop the room. As always, a module doesn't fabricate messages or turns |
81
+ | `controls` | Your module's settings, declared instead of drawn: `[{ key, label, type: 'select' \| 'switch' \| 'text', options, choices, note, invert }]`. MADRE draws them on the page and saves them in your block of `config.json`. In a `select`, `choices: [{ value, label }]` says which phrase the person reads for each value that gets saved (if you give it, `options` comes from there); without it, the person reads the value as is |
82
+ | `onSettings(ctx, settings)` | Tells you when the human changed one of your `controls`, in case something live needs to know |
83
+ | `conditions` | Entries for the MU/TH/UR catalog, with a remedy per platform |
84
+ | `kind` | `'builtin'` (the default) changes how MADRE behaves and lives in `config.json`; `'installer'` writes into the project with a command the human confirms before it runs |
85
+ | `configKey` | Your block in `config.json`. By default it is your `id` in camelCase; declare it only if you want a different one |
86
+ | `card` | Which card MADRE draws. A module of yours leaves the default value: the named cards are the ones MADRE ships drawn for its own |
87
+ | `models` | The models your module chooses between, if it wraps something that has them. They show up on the page |
88
+ | `package` | The npm package that installs what your module manages, if there is one |
89
+ | `confirm` | A text. If it is there, turning on the switch asks for confirmation and doesn't turn on until the human gives it |
90
+ | `toggledEvent`, `toggledBody` | What gets added to the `extension.toggled` event and to the notice the room sees when your module is turned on or off |
91
+ | `preflight(ctx)`, `detect(ctx)`, `installCommand(ctx)` | **Legacy**, from when a module could only be an installer. They still work; for something new, return `preflight` from `status(ctx)` as above |
92
+ | `describe(ctx)` | MADRE builds it from everything above. **Don't declare it**: it is on the list because the object carries it, not because you have to write it |
93
+
94
+ ### Can it connect to *X*?
95
+
96
+ That doesn't depend on MADRE. A module can do whatever a Node process can do on your
97
+ computer: speak TLS, start a server, store a key. **It depends on how that service
98
+ lets a human hand over their own credential**, and that falls into three tiers:
99
+
100
+ | | what the service asks for | does it work on install? |
101
+ |---|---|---|
102
+ | **A** | a static key in minutes: personal token, API key, app password. No app to register, no review | **yes** |
103
+ | **B** | that each user register their own app (OAuth without restricted permissions) | yes, but signing up stops being "paste this" — and you would have to write OAuth |
104
+ | **C** | review, security assessment, or verified publisher | **no**: each user would have to be their own developer |
105
+
106
+ Almost everything useful is in **A**: GitHub, GitLab, Linear, Notion (internal integration), Jira,
107
+ Slack, Discord, Telegram, Stripe, Shopify, Cloudflare, Odoo, any database, and any
108
+ API that accepts an `Authorization: Bearer`. In **B**: Google Calendar and Drive, Microsoft Graph,
109
+ Mercado Libre. In **C**: Gmail for reading, all of Meta, banking.
110
+
111
+ **The rule**, if you don't want to read the table: it's worth it when the credential can be obtained in less
112
+ than five minutes without being a developer, can be revoked on its own, and what your module brings in or sends out fits in one
113
+ line of the page. The long catalog, and why, is in
114
+ [§6 of `CONECTORES.md`](https://github.com/jossuealcacao-exe/madre/blob/main/docs/en/CONNECTORS.md#6--qu%C3%A9-alcanza-un-m%C3%B3dulo-y-de-qui%C3%A9n-es-el-muro), in the repository.
115
+
116
+ ### A file that is also a server
117
+
118
+ `toolsForTurn` returns `args` that point to a file, and you only have one. You don't need two:
119
+ **yours is the module page when MADRE imports it and the MCP server when MADRE executes it.** It knows
120
+ which of the two it is at the end of the file.
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
+ **Resolve both paths before comparing them.** `process.argv[1] === SELF` looks the same and almost
134
+ always is: when MADRE launches it, it passes the same `SELF` that Node's loader already resolved.
135
+ But as soon as **you run the file yourself by hand to test it** —`node ~/.pulse/modules/tuyo.mjs`, or
136
+ any path that goes through `/tmp`, which on macOS is a link to `/private/tmp`— `argv[1]` keeps
137
+ what you typed and `SELF` doesn't. The comparison comes out false, `serve()` never runs, the process starts,
138
+ answers nothing and **exits successfully**. It doesn't print an error because, on paper, there wasn't one.
139
+ Two lines save you that afternoon.
140
+
141
+ ### If your module talks to an outside service
142
+
143
+ Declare where it reaches. `src/outbound.mjs` is not a log of outgoing traffic: it is a contract, and
144
+ it patches global `fetch` so that no module can get around it. A connector that doesn't declare its
145
+ destination shows up in the log as "went to an address nothing here declares" — true, and
146
+ useless to someone trying to tell your module apart from a leak.
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
+ The four answers are the same ones MADRE gives about its own, and for the same reason: whoever
160
+ reads the log deserves to know what travels, when, where to, and where it's turned off. Your declaration is
161
+ stored **separately** from the core's and marked with your `id`, because who promised what is part
162
+ of the promise. It is rebuilt on every load: uninstalling your module takes its destination with it.
163
+
164
+ If you point your module at another server at runtime, declare again with the **same
165
+ `id`**: that way the entry is replaced instead of leaving the log answering for an address
166
+ you no longer use.
167
+
168
+ ### If your module needs a key
169
+
170
+ Declare them and don't ask for them yourself. MADRE draws a password field on the `KEYS` floor of your
171
+ page, accepts the value **only from this computer** (`127.0.0.1`), stores it in
172
+ `~/.pulse/credentials/<tu-id>.json` with `0600` permissions inside a `0700` directory, and notes
173
+ in the ledger that one was saved and **with what name and what length, never the value**.
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
+ And you read it through `ctx.vault`, which comes scoped to your module:
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
+ Your id is sealed inside the SDK, so asking for another module's secret is not something you can
194
+ *write*. The page only shows that one is stored and how many characters it has.
195
+
196
+ **The value goes to the environment of the process you start, and nowhere else.** Not to the prompt, not
197
+ to the `brief`, not to a tool result, not to the ledger.
198
+
199
+ ### If your module sends something
200
+
201
+ Reading can be undone: in the worst case you brought in something that wasn't useful. **Sending can't.** An email, a
202
+ message, a record in someone else's system: that goes out under the name of whoever opened the room
203
+ and doesn't come back.
204
+
205
+ That's why sending lives in `#4` AIRLOCK, where the rule already requires the agent to say in one line
206
+ what goes out and where before it goes out. **The core enforces it, not your good will:**
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
+ If *all* of a server's tools send, the whole server doesn't travel below
214
+ `#4`. If some don't send, the server travels with the list trimmed and a `brief` that says why
215
+ it comes short. And whatever you decide to check, check it **in your server and not in the `brief`**: a
216
+ prompt is a suggestion.
217
+
218
+ **Can it connect to *X*?** The answer doesn't depend on MADRE but on how that service lets
219
+ a human hand over their own key, and it is mapped in three tiers —with a catalog— in
220
+ [§6 of `CONECTORES.md`](https://github.com/jossuealcacao-exe/madre/blob/main/docs/en/CONNECTORS.md#6--qu%C3%A9-alcanza-un-m%C3%B3dulo-y-de-qui%C3%A9n-es-el-muro), in the repository.
221
+ Read it before writing anything: it saves you discovering halfway through that the service requires an annual
222
+ security review to let you in.
223
+
224
+ The whole design of the connectors is in
225
+ [`CONECTORES.md`](https://github.com/jossuealcacao-exe/madre/blob/main/docs/en/CONNECTORS.md), in the repository. A complete connector to read from beginning
226
+ to end —CORREO, which sends email over SMTP without a single dependency— is in
227
+ `src/modules/correo.mjs` and takes ten minutes to read. It doesn't appear in MODULES yet: we're
228
+ finishing it, and in the meantime it can only be turned on deliberately with `PULSE_LABS=correo`.
229
+
230
+ ## Publishing it
231
+
232
+ The page of a module of yours has **GET A NEWER FILE**: MADRE fetches the file —from the `url` you declared or from the file you installed it from—, verifies it in a separate copy, and only replaces the installed one if it loads, follows the house rules, and states a different version. Verifying installs nothing.
233
+
234
+ And in the MODULES header there is **+ ADD A MODULE** to install someone else's `.mjs`. It goes through the same door as everything else. Said plainly, because it is what it is: a module runs **inside MADRE, with the permissions of whoever opens it**. MADRE checks that it loads and that it doesn't leave its pen —its own id, routes only under `/api/x/<id>/`, never on top of a MADRE module—; what the code *intends* is something nobody but you can check.
235
+
236
+ ## The module page
237
+
238
+ You don't draw anything: you declare, and MADRE builds your module the same way as its own. In MODULES each module is a card on the board and, when you tap it, a page with the same blocks as SETTINGS. That's why yours reads the same as the ones MADRE ships.
239
+
240
+ **The board card** has an icon, your `name`, a line with your `summary`, and its switch if it is a module that gets turned on. A module of yours carries the «Yours» tag.
241
+
242
+ **The page**, in this order:
243
+
244
+ | Block | What it shows | Where it comes from |
245
+ |---|---|---|
246
+ | What it does | your sentence, the «Active» switch with what your module says about itself, and the version with «Look for a newer one» | `summary`, `status(ctx).status.detail`, `vendor`, `version` or `tracks` |
247
+ | Settings | one row per control: its name on the left, the selector, switch, or field on the right; it saves when you change it | `controls` |
248
+ | Keys | one field per secret, what is stored (name and length, never the value), and the option to forget it | `secrets` |
249
+ | What it touches | each thing it writes or needs, with its label | `creates`, `requires` |
250
+ | Commands | what it adds to the text box | `commands` or `slash` |
251
+ | Yours | get a newer file and remove it | the file you installed, `updates` |
252
+
253
+ A module that installs into the project instead of being turned on keeps its install card, with the command in view before anything runs, inside the «What it does» block. The figures at the top of the page and the dedicated window belong to MADRE's modules that keep a reading (Ash, Suggested effort, Ollama); you don't need to declare anything for them.
254
+
255
+ ## The `ctx`
256
+
257
+ ```
258
+ ctx = {
259
+ projectRoot, stateRoot, // el proyecto y ~/.pulse
260
+ config, settings, // config.json completo y tus ajustes con defaults
261
+ env, agents, room, // agentes detectados; la sala (room.capabilities(), room.record(...))
262
+ readConfig(), updateConfig(patch),
263
+ record(type, payload), // escribe un evento en el ledger; tipo "algo.algo", nunca message.* ni agent.*
264
+ services: { imageKey, ollama, … }, // lo que el servidor ofrece
265
+ vault: { get, held, keep, forget } // tus secretos, y solo los tuyos
266
+ }
267
+ ```
268
+
269
+ `turn`, in `toolsForTurn`: `{ agent, mode, lease, scratchDir, port, roomDir }`. `mode` goes from 0 to 4; in 0 (GHOST) nothing is saved, so decide whether you want to hand over tools there.
270
+
271
+ ## House rules
272
+
273
+ - **Nothing leaves the machine on its own.** If your module talks to a service, say so in `summary` and in `requires`, and do it only when the human asks for it.
274
+ - **Don't store credentials.** Use the sessions that already live on the machine.
275
+ - **Don't write into the project** from a module; that's what the agents' modes are for. A module that installs something into the project is `kind: 'installer'` and shows the command before running it.
276
+ - **Fail softly.** An exception in `toolsForTurn` hands over nothing; in a command, the card states the error. Never break an agent's turn.
277
+ - **MADRE's voice.** Short uppercase labels, notes that say what doesn't leave the machine, `MU/TH/UR › ` when you speak in a notice.
278
+
279
+ ## Let your AI build it, in the room
280
+
281
+ Type `/module <lo que debe hacer>` in the composer. It is the explicit entry point to the guided flow: if `#2 CREATE` isn't active, MADRE asks you to choose it and keeps your request intact. With `#2` active, it keeps the agent you chose and hands it the SDK contract and the example. The agent must write **a single file** named `<id>.module.mjs` in its scratch folder; it cannot install it on its own.
282
+
283
+ MADRE recognizes that name and puts a card in the room: `INSTALL FOR EVERY ROOM` or `INSTALL FOR THIS PROJECT`. Read it, decide, one click. MADRE validates it in a copy, saves it as `<id>.mjs` in the folder you chose, and it appears in MODULES with the «Yours» tag. You can also ask for the same thing in natural language while you're in `#2` or higher; `/module` makes the format explicit and doesn't grant permissions on its own.
284
+
285
+ Agents never write to `~/.pulse/modules` or `.madre/modules`: they propose, you install.
286
+
287
+ A module of yours carries the «Yours» tag and is removed from its page with «Remove» —also from «Create your module», in the list of yours—. MADRE confirms the exact file, deletes that file and its origin record for updates, and withdraws it from the room without restarting it. It doesn't delete files the module created or its saved settings. The modules that ship with MADRE don't show that button: they aren't removed, they're turned off.
288
+
289
+ ## What a module cannot touch
290
+
291
+ A module runs inside MADRE's process with your permissions: install only what you've read. MADRE, for its part, protects its core like this:
292
+
293
+ - Its HTTP routes live under `/api/x/<id>/`; a module with routes outside that doesn't load, so none can impersonate `/api/state`, `/api/messages`, or any route of MADRE's own.
294
+ - It cannot use the `id` of a built-in module or of another one already loaded.
295
+ - `record()` rejects the reserved event types (`message.*`, `agent.*`); it cannot fabricate turns or messages.
296
+ - It receives nobody's credentials; like MADRE, it uses the sessions that already live on the machine.
297
+ - An error in `toolsForTurn` hands over nothing; in a command, the card states the error; at load time, the board warns how many didn't load and «Create your module» says which file and why. None of that stops the room.
298
+
299
+ ## Testing it
300
+
301
+ 1. Copy the file to `~/.pulse/modules/`.
302
+ 2. In the room, MODULES → «Create your module» → «Reload modules». If the file has an error, it tells you right there with the exact line.
303
+ 3. Turn it on with its switch. Type `/tu-comando` in the composer.
304
+ 4. Change the file and reload again: no need to restart the room.
305
+
306
+ With an AI: give it this document and `hello-module.mjs`, describe what you want, and ask it for a single `.mjs` file with `export default { … }`. MADRE does the rest.
@@ -0,0 +1,59 @@
1
+ # MADRE AI · train the project's model
2
+
3
+ Roadmap 2c. The project exports what it remembers as chat pairs; you train a small model with them on your machine; Ollama serves it; `@madre` uses it as soon as it exists. All local.
4
+
5
+ ## 1 · Export
6
+
7
+ From **Settings › What it remembers › Train your own AI › Export** (when it finishes it tells you which folder it used), or in the terminal:
8
+
9
+ ```
10
+ npx @jossuealcala/madre dataset # in the project folder
11
+ ```
12
+
13
+ It writes `~/.pulse/rooms/<project>/dataset/{train.jsonl, valid.jsonl, manifest.json}`. Each line is `{"messages":[{role:"system"},{role:"user"},{role:"assistant"}]}`: the project's real answers, redacted (no paths, names, emails or keys), plus the summarized notes as recall pairs. Messages sent in #0 INCOGNITO are never there.
14
+
15
+ How much you need: with fewer than ~200 pairs the model learns the tone and little more; from ~1000 on it starts to be worth it. Export every so often; the split is deterministic, so lines don't switch sides.
16
+
17
+ ## 2 · Train (Apple Silicon, mlx-lm)
18
+
19
+ ```
20
+ python3 -m venv ~/.madre-train && source ~/.madre-train/bin/activate
21
+ pip install mlx-lm
22
+ cd ~/.pulse/rooms/<project>
23
+ mlx_lm.lora --model Qwen/Qwen2.5-1.5B-Instruct --train --data dataset --iters 600 --batch-size 2 --learning-rate 1e-5 --adapter-path adapters
24
+ mlx_lm.lora --model Qwen/Qwen2.5-1.5B-Instruct --adapter-path adapters --data dataset --test
25
+ ```
26
+
27
+ ### Linux with NVIDIA (experimental)
28
+
29
+ MLX publishes a CUDA package for Linux (CUDA 12, compute capability 7.5 or higher, driver 550.54.14 or higher, glibc 2.35 or newer, Python 3.10 or newer): <https://ml-explore.github.io/mlx/build/html/install.html>. mlx-lm only documents Apple silicon, so test it first:
30
+
31
+ ```
32
+ python3 -m venv ~/.madre-train && source ~/.madre-train/bin/activate
33
+ pip install "mlx[cuda12]" mlx-lm
34
+ python3 -c "import mlx.core as mx; print('CUDA:', mx.cuda.is_available())" # must say True
35
+ ```
36
+
37
+ If it says `True`, the training commands above (from `cd ~/.pulse/rooms/<project>`) and steps 3 and 4 are the same. If not, there's no tested path on Linux yet.
38
+
39
+ [`train.sh`](../../training/train.sh) in `docs/training/` does the same with checks. On 16 GB use the 1.5B; with 32 GB or more, `Qwen/Qwen2.5-3B-Instruct` or `Qwen/Qwen2.5-Coder-7B-Instruct` with `--batch-size 1`. A round of 600 iterations takes 10 to 40 minutes.
40
+
41
+ ## 3 · Serve in Ollama
42
+
43
+ ```
44
+ mlx_lm.fuse --model Qwen/Qwen2.5-1.5B-Instruct --adapter-path adapters --save-path fused
45
+ ```
46
+
47
+ Ollama reads GGUF. For Llama and Mistral `mlx_lm.fuse` accepts `--export-gguf`; for Qwen convert with llama.cpp:
48
+
49
+ ```
50
+ git clone --depth 1 https://github.com/ggml-org/llama.cpp && pip install -r llama.cpp/requirements.txt
51
+ python3 llama.cpp/convert_hf_to_gguf.py fused --outfile madre.gguf --outtype q8_0
52
+ cp <MADRE folder>/docs/training/Modelfile . && ollama create madre-<project> -f Modelfile
53
+ ```
54
+
55
+ The name matters: `madre-<project>` in lowercase, with hyphens. At the next **RECHECK** in Modules › Local AI, `@madre` switches to that model and What it remembers says so. The `Modelfile` comes in `docs/training/` of the MADRE repository and of the npm package.
56
+
57
+ ## 4 · Evaluate before trusting
58
+
59
+ Before promoting a new model, ask `@madre` the ten things the project did decide and the five it never discussed. It should get the first ones right and say "the project never discussed it" for the second ones. If it makes things up, go back to the base model (`ollama rm madre-<project>`) and train with more pairs or fewer iterations.
Binary file
@@ -1,16 +1,16 @@
1
1
  # MADRE AI · entrenar el modelo del proyecto
2
2
 
3
- Roadmap 2c. La sala exporta lo que recuerda como pares de chat; tú entrenas un modelo pequeño con ellos en tu máquina; Ollama lo sirve; `@madre` lo usa en cuanto existe. Todo local.
3
+ Roadmap 2c. El proyecto exporta lo que recuerda como pares de chat; tú entrenas un modelo pequeño con ellos en tu máquina; Ollama lo sirve; `@madre` lo usa en cuanto existe. Todo local.
4
4
 
5
5
  ## 1 · Exportar
6
6
 
7
- Desde MU/TH/UR → `⚙ CONNECTIONS` → `MEMORY` → `EXPORT DATASET`, o en terminal:
7
+ Desde **Ajustes › Lo que recuerda › Entrena tu propia IA › Exportar** (al terminar te dice en qué carpeta quedó), o en terminal:
8
8
 
9
9
  ```
10
- madre dataset # en la carpeta del proyecto
10
+ npx @jossuealcala/madre dataset # en la carpeta del proyecto
11
11
  ```
12
12
 
13
- Escribe `~/.pulse/rooms/<sala>/dataset/{train.jsonl, valid.jsonl, manifest.json}`. Cada línea es `{"messages":[{role:"system"},{role:"user"},{role:"assistant"}]}`: los turnos reales de la sala, redactados (sin rutas, nombres, correos ni claves), más las notas destiladas como pares de recuerdo. GHOST nunca está.
13
+ Escribe `~/.pulse/rooms/<proyecto>/dataset/{train.jsonl, valid.jsonl, manifest.json}`. Cada línea es `{"messages":[{role:"system"},{role:"user"},{role:"assistant"}]}`: las respuestas reales del proyecto, redactados (sin rutas, nombres, correos ni claves), más las notas resumidas como pares de recuerdo. Lo que se envió en #0 INCÓGNITO nunca está.
14
14
 
15
15
  Cuánto hace falta: con menos de ~200 pares el modelo aprende el tono y poco más; a partir de ~1000 empieza a valer. Exporta cada tanto; el split es determinista, así que las líneas no cambian de lado.
16
16
 
@@ -19,11 +19,23 @@ Cuánto hace falta: con menos de ~200 pares el modelo aprende el tono y poco má
19
19
  ```
20
20
  python3 -m venv ~/.madre-train && source ~/.madre-train/bin/activate
21
21
  pip install mlx-lm
22
- cd ~/.pulse/rooms/<sala>
22
+ cd ~/.pulse/rooms/<proyecto>
23
23
  mlx_lm.lora --model Qwen/Qwen2.5-1.5B-Instruct --train --data dataset --iters 600 --batch-size 2 --learning-rate 1e-5 --adapter-path adapters
24
24
  mlx_lm.lora --model Qwen/Qwen2.5-1.5B-Instruct --adapter-path adapters --data dataset --test
25
25
  ```
26
26
 
27
+ ### Linux con NVIDIA (experimental)
28
+
29
+ MLX publica un paquete para CUDA en Linux (CUDA 12, capacidad de cómputo 7.5 o mayor, controlador 550.54.14 o mayor, glibc 2.35 o mayor, Python 3.10 o mayor): <https://ml-explore.github.io/mlx/build/html/install.html>. mlx-lm solo documenta Apple silicon, así que pruébalo primero:
30
+
31
+ ```
32
+ python3 -m venv ~/.madre-train && source ~/.madre-train/bin/activate
33
+ pip install "mlx[cuda12]" mlx-lm
34
+ python3 -c "import mlx.core as mx; print('CUDA:', mx.cuda.is_available())" # debe decir True
35
+ ```
36
+
37
+ Si dice `True`, los comandos de entrenamiento de arriba (desde `cd ~/.pulse/rooms/<proyecto>`) y los pasos 3 y 4 son los mismos. Si no, no hay ruta probada en Linux todavía.
38
+
27
39
  `train.sh` en esta carpeta hace lo mismo con comprobaciones. En 16 GB usa el 1.5B; con 32 GB o más, `Qwen/Qwen2.5-3B-Instruct` o `Qwen/Qwen2.5-Coder-7B-Instruct` con `--batch-size 1`. Una ronda de 600 iteraciones tarda de 10 a 40 minutos.
28
40
 
29
41
  ## 3 · Servir en Ollama
@@ -37,11 +49,11 @@ Ollama lee GGUF. Para Llama y Mistral `mlx_lm.fuse` acepta `--export-gguf`; para
37
49
  ```
38
50
  git clone --depth 1 https://github.com/ggml-org/llama.cpp && pip install -r llama.cpp/requirements.txt
39
51
  python3 llama.cpp/convert_hf_to_gguf.py fused --outfile madre.gguf --outtype q8_0
40
- cp docs/training/Modelfile . && ollama create madre-<proyecto> -f Modelfile
52
+ cp <carpeta de MADRE>/docs/training/Modelfile . && ollama create madre-<proyecto> -f Modelfile
41
53
  ```
42
54
 
43
- El nombre importa: `madre-<proyecto>` en minúsculas, con guiones. Al siguiente `RECHECK` en MODULES → OLLAMA, `@madre` cambia a ese modelo y la tarjeta MEMORY lo dice.
55
+ El nombre importa: `madre-<proyecto>` en minúsculas, con guiones. Al siguiente **REVISAR OTRA VEZ** en Módulos › IA local, `@madre` cambia a ese modelo y Lo que recuerda lo dice. El `Modelfile` viene en `docs/training/` del repositorio de MADRE y del paquete de npm.
44
56
 
45
57
  ## 4 · Evaluar antes de confiar
46
58
 
47
- Antes de promover un modelo nuevo, pregúntale a `@madre` las diez cosas que la sala sí decidió y las cinco que nunca discutió. Debe acertar las primeras y decir "la sala nunca lo discutió" en las segundas. Si inventa, vuelve al modelo base (`ollama rm madre-<proyecto>`) y entrena con más pares o menos iteraciones.
59
+ Antes de promover un modelo nuevo, pregúntale a `@madre` las diez cosas que el proyecto sí decidió y las cinco que nunca discutió. Debe acertar las primeras y decir "el proyecto nunca lo discutió" en las segundas. Si inventa, vuelve al modelo base (`ollama rm madre-<proyecto>`) y entrena con más pares o menos iteraciones.
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bash
2
- # MADRE AI · one LoRA round on this room's dataset with mlx-lm (Apple Silicon).
2
+ # MADRE AI · one LoRA round on this room's dataset with mlx-lm (Apple Silicon; Linux with an
3
+ # NVIDIA card through mlx[cuda12] is experimental: mlx-lm only promises Apple chips).
3
4
  # docs/training/train.sh <room-dir> [base-model] [iters]
4
5
  set -euo pipefail
5
6
  ROOM="${1:?room dir, e.g. ~/.pulse/rooms/<sala>}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jossuealcala/madre",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "MADRE · one local room where the AI coding agents already on your machine (Codex, Claude Code, Gemini CLI, OpenCode) work on your project together, share one memory you can see and explore, and answer to you. Read-only by default, with per-message permission modes and checkpoints you can undo. Nothing leaves your machine on its own.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -32,11 +32,11 @@
32
32
  "url": "https://github.com/jossuealcacao-exe/madre/issues"
33
33
  },
34
34
  "author": {
35
- "name": "Jossué Alcalá",
35
+ "name": "Jossue Alcalá",
36
36
  "url": "https://jossuealcala.com/en/"
37
37
  },
38
38
  "type": "module",
39
- "license": "Apache-2.0",
39
+ "license": "AGPL-3.0-only",
40
40
  "files": [
41
41
  "bin",
42
42
  "src",
@@ -45,7 +45,8 @@
45
45
  "CHANGELOG.md",
46
46
  "LICENSE",
47
47
  "NOTICE",
48
- "docs/madre-banner.svg",
48
+ "TRADEMARKS.md",
49
+ "docs/madre-banner.png",
49
50
  "docs/room.png",
50
51
  "docs/training",
51
52
  "docs/SDK.md",
@@ -53,7 +54,15 @@
53
54
  "docs/INTERNALS.md",
54
55
  "docs/REFERENCE.md",
55
56
  "SECURITY.md",
56
- "CONTRIBUTING.md"
57
+ "CONTRIBUTING.md",
58
+ "README.en.md",
59
+ "CHANGELOG.en.md",
60
+ "TRADEMARKS.en.md",
61
+ "ADDITIONAL-TERMS.md",
62
+ "docs/en/REFERENCE.md",
63
+ "docs/en/SDK.md",
64
+ "docs/en/INTERNALS.md",
65
+ "docs/en/training"
57
66
  ],
58
67
  "bin": {
59
68
  "madre": "./bin/madre.mjs",
@@ -1,3 +1,5 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // MADRE · © 2026 Jossue Alcalá · GNU AGPL v3 with additional terms (Section 7): ADDITIONAL-TERMS.md
1
3
  // ABDUCTION — the one joke in the room, and the only thing here that is not work.
2
4
  //
3
5
  // Type MADRE into the box, alone, and send it. Not @madre: that is the local agent and it has a