@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.
Files changed (47) hide show
  1. package/CHANGELOG.md +265 -1
  2. package/README.md +33 -1
  3. package/docs/SDK.md +173 -6
  4. package/docs/sdk/connector-module.mjs +186 -0
  5. package/package.json +6 -2
  6. package/public/abduction.js +384 -0
  7. package/public/app.js +3450 -540
  8. package/public/assets/abduction-cat-blink.png +0 -0
  9. package/public/assets/abduction-cat-control-left.png +0 -0
  10. package/public/assets/abduction-cat-control-right.png +0 -0
  11. package/public/assets/abduction-cat.png +0 -0
  12. package/public/es.js +354 -54
  13. package/public/index.html +65 -19
  14. package/public/routing.js +74 -0
  15. package/public/styles.css +788 -77
  16. package/src/adapters/claude.mjs +6 -2
  17. package/src/adapters/gemini.mjs +26 -5
  18. package/src/adapters/madre.mjs +5 -0
  19. package/src/choices.mjs +64 -0
  20. package/src/dataset.mjs +11 -6
  21. package/src/distiller.mjs +12 -1
  22. package/src/file-ops.mjs +45 -0
  23. package/src/lease.mjs +37 -2
  24. package/src/mcp/smtp-server.mjs +189 -0
  25. package/src/memory-dashboard.mjs +69 -0
  26. package/src/memory.mjs +222 -18
  27. package/src/modules/ahp.mjs +1 -1
  28. package/src/modules/ash-policy.mjs +67 -0
  29. package/src/modules/ash.mjs +1 -1
  30. package/src/modules/choices.mjs +34 -0
  31. package/src/modules/correo.mjs +107 -0
  32. package/src/modules/index.mjs +48 -4
  33. package/src/modules/playwright.mjs +5 -1
  34. package/src/modules/ripley.mjs +13 -1
  35. package/src/modules/sdk.mjs +44 -9
  36. package/src/outbound.mjs +37 -3
  37. package/src/ports.mjs +91 -0
  38. package/src/request-guard.mjs +26 -0
  39. package/src/room/archivist.mjs +2 -1
  40. package/src/room/economy.mjs +83 -1
  41. package/src/room/prompt.mjs +14 -2
  42. package/src/room.mjs +150 -18
  43. package/src/sentinel-errors.mjs +5 -1
  44. package/src/server.mjs +188 -8
  45. package/src/smtp.mjs +122 -0
  46. package/src/vault.mjs +100 -0
  47. package/src/whats-new.mjs +140 -0
@@ -0,0 +1,140 @@
1
+ // What a version brings, said to the person who just got it.
2
+ //
3
+ // After an update the room opens once on a sheet of what is new: a few things, each said plainly,
4
+ // each with where to find it, and a way to read every change. Once per version, for everyone who
5
+ // came from an older one; never on a first install, where the tour does that job.
6
+ //
7
+ // Whether it has been seen lives in ~/.pulse/whats-new.json and not in the browser: the page's
8
+ // storage belongs to one port, and the same person opening the room on another port has still
9
+ // seen it. Nothing here touches the network; the link to every change is the release page the
10
+ // room already knows (updates.mjs).
11
+ //
12
+ // The notes are written by hand, version by version, in both languages the room speaks. A minor
13
+ // version cannot be closed without them (scripts/release.mjs); a patch may ship without, and then
14
+ // nothing opens.
15
+
16
+ import { existsSync } from 'node:fs';
17
+ import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
18
+ import { join } from 'node:path';
19
+ import { compareVersions } from './updates.mjs';
20
+
21
+ const FILE = 'whats-new.json';
22
+
23
+ // `icon` names one of the room's 8-bit icons. `where` says where it lives, in the words the
24
+ // screens use. `more` is the one thing nobody is told how to find.
25
+ export const NOTES = {
26
+ '0.7.0': {
27
+ intro: {
28
+ es: 'Esta versión le da forma a la memoria de la sala: ahora la ves en tres dimensiones, la lees en un tablero y cada memoria te dice cómo está.',
29
+ en: 'This version gives the room’s memory a shape: you see it in three dimensions, read it on a dashboard, and every memory tells you how it is doing.',
30
+ },
31
+ items: [
32
+ {
33
+ icon: 'orbit',
34
+ title: { es: 'NOSTROMO en tres dimensiones', en: 'NOSTROMO in three dimensions' },
35
+ body: {
36
+ es: 'El archivo de memorias tiene forma de cerebro y lo giras con el ratón. Al elegir una memoria, la sala la pone de frente, la fija con una mira y atenúa todo lo demás.',
37
+ en: 'The memory archive is shaped like a brain and you turn it with the mouse. Choose a memory and the room brings it to face you, locks a sight on it and dims everything else.',
38
+ },
39
+ where: { es: 'MU/TH/UR › NOSTROMO · arrastra para girar, Shift + arrastra para moverte', en: 'MU/TH/UR › NOSTROMO · drag to turn, Shift + drag to move' },
40
+ },
41
+ {
42
+ icon: 'star',
43
+ title: { es: 'Cada memoria es una estrella', en: 'Every memory is a star' },
44
+ body: {
45
+ es: 'Su brillo dice cuánto la usa la sala; su superficie, si la confirmaste, si nadie la ha juzgado, si resultó falsa o si se está enfriando. En el centro, MOTHER: magma vivo.',
46
+ en: 'Its brightness says how much the room relies on it; its surface, whether you confirmed it, nobody has judged it yet, it turned out false, or it is cooling. At the centre, MOTHER: living magma.',
47
+ },
48
+ where: { es: 'MU/TH/UR › NOSTROMO', en: 'MU/TH/UR › NOSTROMO' },
49
+ },
50
+ {
51
+ icon: 'bars',
52
+ title: { es: 'Un tablero para la memoria', en: 'A dashboard for memory' },
53
+ body: {
54
+ es: 'Lo que el archivo hizo día a día, de qué está hecho, qué tan bien contesta y cuál es el siguiente paso, en una sola pantalla.',
55
+ en: 'What the archive did day by day, what it is made of, how well it answers and what to do next, on a single screen.',
56
+ },
57
+ where: { es: 'MU/TH/UR › MEMORIA', en: 'MU/TH/UR › MEMORY' },
58
+ },
59
+ {
60
+ icon: 'chip',
61
+ title: { es: 'Ideas para tu propio módulo', en: 'Ideas for a module of your own' },
62
+ body: {
63
+ es: 'Escribe /module y elige entre ocho ideas listas para editar: comandos, conectores con tus servicios y fichas propias. Un agente lo escribe y tú lo revisas antes de instalarlo.',
64
+ en: 'Type /module and pick one of eight ideas ready to edit: commands, connectors to your services and cards of your own. An agent writes it and you review it before it is installed.',
65
+ },
66
+ where: { es: 'En la caja de texto · escribe /module', en: 'In the message box · type /module' },
67
+ },
68
+ {
69
+ icon: 'stop',
70
+ title: { es: 'Detén una respuesta, no la sala', en: 'Stop one reply, not the room' },
71
+ body: {
72
+ es: 'Un botón DETENER detiene a un solo agente a mitad de su respuesta; los demás siguen trabajando.',
73
+ en: 'A STOP button halts one agent in the middle of its reply; the others keep working.',
74
+ },
75
+ where: { es: 'En la fila del turno en curso, junto a los segundos', en: 'On the row of the turn in progress, next to the seconds' },
76
+ },
77
+ {
78
+ icon: 'lock',
79
+ title: { es: 'Más protección desde el navegador', en: 'More protection from the browser' },
80
+ body: {
81
+ es: 'Ninguna página web que tengas abierta puede ya instalar un módulo en tu sala ni leer de ella.',
82
+ en: 'No web page you have open can install a module in your room or read from it anymore.',
83
+ },
84
+ where: { es: 'Siempre activo; no tienes que hacer nada', en: 'Always on; there is nothing to set up' },
85
+ },
86
+ ],
87
+ more: {
88
+ icon: 'ufo',
89
+ title: { es: 'Una cosa más…', en: 'One more thing…' },
90
+ body: {
91
+ es: 'Alguien más viaja a bordo. No está en ningún menú ni en ningún manual. Dicen que solo baja cuando llamas a la nave por su nombre, y por nada más.',
92
+ en: 'Someone else travels on board. Not in any menu, not in any manual. They say it only comes down when you call the ship by its name, and nothing else.',
93
+ },
94
+ },
95
+ },
96
+ };
97
+
98
+ export const notesFor = (version) => NOTES[version] ?? null;
99
+
100
+ // Whether this person should see the sheet for this version. Someone who has seen an older one,
101
+ // or who used the room before the sheet existed, sees it once; a first install never does.
102
+ export function shouldShow({ version, seen = null, returning = false }) {
103
+ if (!notesFor(version)) return false;
104
+ if (seen) return compareVersions(version, seen) > 0;
105
+ return Boolean(returning);
106
+ }
107
+
108
+ export async function readSeen(root) {
109
+ try {
110
+ const value = JSON.parse(await readFile(join(root, FILE), 'utf8'))?.seen;
111
+ return typeof value === 'string' ? value : null;
112
+ } catch {
113
+ return null;
114
+ }
115
+ }
116
+
117
+ export async function markSeen(root, version) {
118
+ await mkdir(root, { recursive: true });
119
+ await writeFile(join(root, FILE), `${JSON.stringify({ seen: version }, null, 2)}\n`);
120
+ }
121
+
122
+ // Read at start-up, before this run creates anything: a room that already had rooms used MADRE
123
+ // before. A first install has nothing to catch up on, so it is marked as seen and never shown.
124
+ export async function bootWhatsNew({ root, version }) {
125
+ const seen = await readSeen(root);
126
+ if (seen) return { seen, returning: true };
127
+ let returning = false;
128
+ if (existsSync(join(root, 'rooms'))) {
129
+ try { returning = (await readdir(join(root, 'rooms'))).length > 0; } catch { returning = false; }
130
+ }
131
+ if (!returning) await markSeen(root, version);
132
+ return { seen: returning ? null : version, returning };
133
+ }
134
+
135
+ // `any`: the sheet for this version whether or not it was seen, for when it is asked for.
136
+ export async function whatsNew({ root, version, returning, any = false }) {
137
+ const seen = await readSeen(root);
138
+ const show = shouldShow({ version, seen, returning });
139
+ return { version, show, notes: show || any ? notesFor(version) : null };
140
+ }