@jossuealcala/madre 0.7.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/CHANGELOG.md +773 -1
  2. package/README.md +92 -28
  3. package/bin/madre.mjs +30 -10
  4. package/docs/REFERENCE.md +157 -81
  5. package/docs/SDK.md +23 -19
  6. package/package.json +1 -1
  7. package/public/app.js +11207 -4196
  8. package/public/assets/brand/Geist-latin-ext.woff2 +0 -0
  9. package/public/assets/brand/Geist-latin.woff2 +0 -0
  10. package/public/assets/brand/GeistMono-latin-ext.woff2 +0 -0
  11. package/public/assets/brand/GeistMono-latin.woff2 +0 -0
  12. package/public/assets/brand/JetBrainsMono-latin-ext.woff2 +0 -0
  13. package/public/assets/brand/JetBrainsMono-latin.woff2 +0 -0
  14. package/public/assets/brand/OFL-Geist.txt +93 -0
  15. package/public/assets/brand/OFL-GeistMono.txt +93 -0
  16. package/public/assets/brand/OFL-JetBrainsMono.txt +93 -0
  17. package/public/assets/brand/OFL-Sora.txt +93 -0
  18. package/public/assets/brand/Sora-latin-ext.woff2 +0 -0
  19. package/public/assets/brand/Sora-latin.woff2 +0 -0
  20. package/public/assets/brand/madre-app-180.png +0 -0
  21. package/public/assets/brand/madre-app-192.png +0 -0
  22. package/public/assets/brand/madre-app-512.png +0 -0
  23. package/public/assets/brand/madre-app.svg +1 -0
  24. package/public/assets/brand/madre-favicon.svg +1 -0
  25. package/public/assets/news/1.0/alerts.webp +0 -0
  26. package/public/assets/news/1.0/bar.webp +0 -0
  27. package/public/assets/news/1.0/finder.webp +0 -0
  28. package/public/assets/news/1.0/github.webp +0 -0
  29. package/public/assets/news/1.0/intents.webp +0 -0
  30. package/public/assets/news/1.0/looks.webp +0 -0
  31. package/public/assets/news/1.0/modules.webp +0 -0
  32. package/public/assets/news/1.0/mother.webp +0 -0
  33. package/public/assets/news/1.0/nostromo.webp +0 -0
  34. package/public/assets/news/1.0/permissions.webp +0 -0
  35. package/public/assets/news/1.0/room.webp +0 -0
  36. package/public/assets/news/1.0/settings.webp +0 -0
  37. package/public/assets/wallpapers/biznaga.webp +0 -0
  38. package/public/assets/wallpapers/canela.webp +0 -0
  39. package/public/assets/wallpapers/jarrito.webp +0 -0
  40. package/public/assets/wallpapers/magna.webp +0 -0
  41. package/public/assets/wallpapers/oceano.webp +0 -0
  42. package/public/assets/wallpapers/paraiso.webp +0 -0
  43. package/public/assets/wallpapers/quila.webp +0 -0
  44. package/public/assets/wallpapers/tradicion.webp +0 -0
  45. package/public/assets/wallpapers/tropical.webp +0 -0
  46. package/public/assets/wallpapers/urbania.webp +0 -0
  47. package/public/assets/wallpapers/ventura.webp +0 -0
  48. package/public/assets/wallpapers/xico.webp +0 -0
  49. package/public/brands.js +28 -2
  50. package/public/editor.js +744 -0
  51. package/public/es.js +2784 -1026
  52. package/public/i18n.js +1 -1
  53. package/public/index.html +201 -175
  54. package/public/inquiry.js +11 -11
  55. package/public/loader.js +79 -0
  56. package/public/macroverso.js +1497 -0
  57. package/public/manifest.webmanifest +14 -0
  58. package/public/nostromo-engine.js +2222 -0
  59. package/public/resay.js +24 -0
  60. package/public/review-ask.js +85 -0
  61. package/public/salas-mac.css +787 -0
  62. package/public/salas-mac.html +154 -0
  63. package/public/salas-mac.js +3264 -0
  64. package/public/salas.css +85 -0
  65. package/public/salas.html +47 -0
  66. package/public/salas.js +200 -0
  67. package/public/styles.css +6207 -425
  68. package/public/touch.js +83 -0
  69. package/public/tour-chapters.js +342 -0
  70. package/public/tour.js +510 -0
  71. package/public/troubleshooting.js +159 -140
  72. package/src/adapters/codex.mjs +46 -6
  73. package/src/adapters/gemini.mjs +30 -10
  74. package/src/adapters/madre.mjs +110 -18
  75. package/src/adapters/process.mjs +42 -2
  76. package/src/auth-probe.mjs +2 -2
  77. package/src/bridge/devices.mjs +202 -0
  78. package/src/bridge/handoff.mjs +45 -0
  79. package/src/bridge/host.mjs +82 -0
  80. package/src/bridge/network.mjs +133 -0
  81. package/src/bridge/policy.mjs +89 -0
  82. package/src/bridge/qr.mjs +287 -0
  83. package/src/bridge/server.mjs +269 -0
  84. package/src/capabilities.mjs +5 -9
  85. package/src/chats.mjs +59 -22
  86. package/src/checkpoint.mjs +137 -35
  87. package/src/choices.mjs +18 -3
  88. package/src/cli-args.mjs +12 -1
  89. package/src/commands.mjs +15 -15
  90. package/src/config.mjs +73 -9
  91. package/src/conversation-context.mjs +5 -2
  92. package/src/dataset.mjs +3 -1
  93. package/src/directives.mjs +0 -5
  94. package/src/enlaces.mjs +451 -0
  95. package/src/event-store.mjs +55 -10
  96. package/src/exam.mjs +5 -5
  97. package/src/extensions.mjs +0 -2
  98. package/src/file-ops.mjs +23 -0
  99. package/src/files.mjs +52 -2
  100. package/src/github.mjs +407 -0
  101. package/src/json-file.mjs +63 -0
  102. package/src/lease.mjs +2 -2
  103. package/src/lobby-work.mjs +103 -0
  104. package/src/looks.mjs +55 -0
  105. package/src/maturity.mjs +3 -3
  106. package/src/mcp/github-server.mjs +164 -0
  107. package/src/mcp/image-server.mjs +1 -1
  108. package/src/mcp/memory-server.mjs +11 -3
  109. package/src/memory.mjs +38 -12
  110. package/src/models.mjs +89 -5
  111. package/src/modules/ahp.mjs +4 -4
  112. package/src/modules/ash.mjs +1 -1
  113. package/src/modules/bishop-plan.mjs +207 -0
  114. package/src/modules/bishop.mjs +645 -0
  115. package/src/modules/choices.mjs +2 -2
  116. package/src/modules/correo.mjs +1 -1
  117. package/src/modules/enlaces.mjs +255 -0
  118. package/src/modules/git-pulse.mjs +2 -2
  119. package/src/modules/github.mjs +322 -0
  120. package/src/modules/image-studio.mjs +3 -3
  121. package/src/modules/index.mjs +18 -4
  122. package/src/modules/intents-calibration.mjs +338 -0
  123. package/src/modules/intents-desk.mjs +481 -0
  124. package/src/modules/intents-observe.mjs +266 -0
  125. package/src/modules/intents-policy.mjs +414 -0
  126. package/src/modules/intents-try.mjs +91 -0
  127. package/src/modules/intents.mjs +145 -0
  128. package/src/modules/ollama.mjs +4 -4
  129. package/src/modules/playwright.mjs +121 -11
  130. package/src/modules/ripley.mjs +1 -1
  131. package/src/modules/runs.mjs +2 -2
  132. package/src/modules/sdk.mjs +10 -4
  133. package/src/ollama.mjs +6 -1
  134. package/src/outbound.mjs +19 -17
  135. package/src/override.mjs +100 -0
  136. package/src/ports.mjs +23 -1
  137. package/src/prices.mjs +72 -0
  138. package/src/privacy.mjs +30 -11
  139. package/src/quota-sources.mjs +0 -8
  140. package/src/request-guard.mjs +1 -1
  141. package/src/review-ask.mjs +111 -0
  142. package/src/room/archivist.mjs +55 -37
  143. package/src/room/budget.mjs +17 -5
  144. package/src/room/control.mjs +70 -7
  145. package/src/room/economy.mjs +35 -7
  146. package/src/room/escalation.mjs +2 -0
  147. package/src/room/guard.mjs +26 -2
  148. package/src/room/prompt.mjs +22 -15
  149. package/src/room/runs.mjs +84 -21
  150. package/src/room/vectors.mjs +19 -4
  151. package/src/room.mjs +351 -65
  152. package/src/router.mjs +3 -1
  153. package/src/runs.mjs +23 -5
  154. package/src/salas.mjs +313 -0
  155. package/src/sentinel-errors.mjs +1 -1
  156. package/src/server-sentinel.mjs +124 -0
  157. package/src/server.mjs +1526 -163
  158. package/src/setup.mjs +1 -0
  159. package/src/tour.mjs +159 -0
  160. package/src/updates.mjs +48 -8
  161. package/src/usage-sentinel.mjs +1 -1
  162. package/src/verdict.mjs +7 -7
  163. package/src/whats-new.mjs +160 -0
package/public/inquiry.js CHANGED
@@ -30,7 +30,7 @@ export const INQUIRIES = [
30
30
  aliases: ['HELP', '?', 'WHAT CAN YOU TELL ME', 'COMMANDS', 'OPTIONS'],
31
31
  brief: t('what this interface answers'),
32
32
  answer: () => [
33
- t('I ANSWER FROM WHAT IS ALREADY IN THIS ROOM. NOTHING IS ASKED OF THE CREW AND NOTHING LEAVES.'),
33
+ t('I ANSWER FROM WHAT IS ALREADY IN THIS PROJECT. NOTHING IS ASKED OF THE AGENTS AND NOTHING LEAVES.'),
34
34
  '',
35
35
  ...INQUIRIES.filter((one) => one.id !== 'help').map((one) => ` ${(one.takes ? `${one.aliases[0]} <${one.takes}>` : one.aliases[0]).padEnd(22)}${one.brief.toUpperCase()}`),
36
36
  '',
@@ -85,7 +85,7 @@ export const INQUIRIES = [
85
85
  {
86
86
  id: 'egress',
87
87
  aliases: ['EGRESS', 'OUTBOUND', 'WHAT LEFT THIS MACHINE', 'WHAT LEAVES'],
88
- brief: t('every address this room can reach, and whether it is on'),
88
+ brief: t('every address this project can reach, and whether it is on'),
89
89
  answer: ({ outbound }) => (outbound?.destinations?.length
90
90
  ? [
91
91
  ...outbound.destinations.map((one) => ` ${(one.local ? 'LOCAL' : one.on === true ? 'ON' : one.on === false ? 'OFF' : '—').padEnd(6)}${one.to.slice(0, 44).padEnd(46)}${one.calls ? `${plural(one.calls, 'REQUEST')}` : ''}`),
@@ -97,10 +97,10 @@ export const INQUIRIES = [
97
97
  {
98
98
  id: 'window',
99
99
  aliases: ['WINDOW', 'TRANSCRIPT', 'WHAT IS CARRIED'],
100
- brief: t('how much of this room the next turn carries'),
100
+ brief: t('how much of this project the next turn carries'),
101
101
  answer: ({ briefing }) => {
102
102
  const window = briefing?.window;
103
- if (!window || !Number.isInteger(window.from)) return [t('THE WHOLE ROOM STILL FITS. NOTHING IS LEFT BEHIND.')];
103
+ if (!window || !Number.isInteger(window.from)) return [t('THE WHOLE PROJECT STILL FITS. NOTHING IS LEFT BEHIND.')];
104
104
  return [
105
105
  t('THE NEXT TURN CARRIES #{from} TO #{through} · {n} MESSAGES.', { from: window.from, through: window.through, n: window.carried }),
106
106
  window.omitted ? t('{n} OLDER ONES STAY BEHIND. THAT IS WHAT RECALL IS FOR.', { n: window.omitted }) : t('NOTHING IS LEFT BEHIND.'),
@@ -135,13 +135,13 @@ export const INQUIRIES = [
135
135
  id: 'privacy',
136
136
  aliases: ['PRIVACY', 'TERMS', 'WHAT IS PROTECTED'],
137
137
  needs: 'privacy',
138
- brief: t('how many terms this room replaces before anything is said'),
138
+ brief: t('how many terms this project replaces before anything is said'),
139
139
  answer: ({ privacy }) => (privacy?.terms
140
140
  ? [
141
- t('{n} TERMS ARE REPLACED WITH {marker} BEFORE ANYTHING LEAVES THIS ROOM.', { n: privacy.terms, marker: String(privacy.marker ?? '[ENTIDAD-ORG]') }),
141
+ t('{n} TERMS ARE REPLACED WITH {marker} BEFORE ANYTHING LEAVES THIS PROJECT.', { n: privacy.terms, marker: String(privacy.marker ?? '[ENTIDAD-ORG]') }),
142
142
  t('I WILL NOT PRINT THEM. THE WORDS LIVE IN YOUR CONFIG AND THE LEDGER KEEPS THE COUNT, NEVER THE WORD.'),
143
143
  ]
144
- : [t('NO TERM IS BEING PROTECTED IN THIS ROOM. ⚙ CONNECTIONS → PRIVACY IS WHERE THEY GO.')]),
144
+ : [t('No term is being protected in this project. They go in Settings › Privacy.')]),
145
145
  },
146
146
  {
147
147
  id: 'status',
@@ -156,7 +156,7 @@ export const INQUIRIES = [
156
156
  t('NEXT: {what}', { what: String(verdict.next?.text ?? '').toUpperCase() }),
157
157
  verdict.next?.where ? t('WHERE: {where}', { where: verdict.next.where }) : '',
158
158
  ].filter((line) => line !== '')
159
- : [t('NOTHING HAS BEEN MEASURED IN THIS ROOM YET.')]),
159
+ : [t('NOTHING HAS BEEN MEASURED IN THIS PROJECT YET.')]),
160
160
  },
161
161
  {
162
162
  id: 'order937',
@@ -165,11 +165,11 @@ export const INQUIRIES = [
165
165
  answer: () => [
166
166
  t('THERE IS NO ORDER YOU CANNOT READ.'),
167
167
  '',
168
- t('EVERY INSTRUCTION THIS ROOM CARRIES IS IN THE DOCUMENT ABOVE, BLOCK BY BLOCK, IN THE WORDS IT IS SAID IN. NOTHING IS APPENDED AFTER YOU LOOK AWAY AND NOTHING IS KEPT BACK FROM YOU.'),
168
+ t('EVERY INSTRUCTION THIS PROJECT CARRIES IS IN THE DOCUMENT ABOVE, BLOCK BY BLOCK, IN THE WORDS IT IS SAID IN. NOTHING IS APPENDED AFTER YOU LOOK AWAY AND NOTHING IS KEPT BACK FROM YOU.'),
169
169
  t('THAT IS THE WHOLE DIFFERENCE BETWEEN THIS SHIP AND THE OTHER ONE.'),
170
170
  ],
171
171
  },
172
- { id: 'close', aliases: ['CLOSE', 'EXIT', 'QUIT', 'BYE'], brief: t('leave the core'), closes: true, answer: () => [t('INTERFACE CLOSED.')] },
172
+ { id: 'close', aliases: ['CLOSE', 'EXIT', 'QUIT', 'BYE'], brief: t('leave What is sent'), closes: true, answer: () => [t('INTERFACE CLOSED.')] },
173
173
  ];
174
174
 
175
175
  const byAlias = INQUIRIES.flatMap((one) => one.aliases.map((alias) => [alias, one])).sort((a, b) => b[0].length - a[0].length);
@@ -205,7 +205,7 @@ export function answerFor(text, context = {}) {
205
205
  strikes,
206
206
  closes: strikes >= STRIKES,
207
207
  lines: strikes >= STRIKES
208
- ? [REFUSALS[(strikes - 1) % REFUSALS.length], '', t('{n} INQUIRIES I COULD NOT PARSE. INTERFACE CLOSED.', { n: STRIKES }), t('THE CORE IS WHERE YOU LEFT IT. OPEN IT AGAIN WHENEVER YOU LIKE.')]
208
+ ? [REFUSALS[(strikes - 1) % REFUSALS.length], '', t('{n} INQUIRIES I COULD NOT PARSE. INTERFACE CLOSED.', { n: STRIKES }), t('WHAT IS SENT IS WHERE YOU LEFT IT. OPEN IT AGAIN WHENEVER YOU LIKE.')]
209
209
  : [REFUSALS[(strikes - 1) % REFUSALS.length], t('{n} ATTEMPTS LEFT BEFORE THIS INTERFACE CLOSES. HELP LISTS WHAT I ANSWER.', { n: left })],
210
210
  };
211
211
  }
@@ -0,0 +1,79 @@
1
+ // MADRE loading (2026-10-10): the pulse of the logo beating, a thin bar that fills and the share
2
+ // done under it, as a Mac starts. One loader for every wait that takes time — a RELOAD, the room
3
+ // or Home coming back after an update, a project waking, an agent installing — so a wait always
4
+ // looks the same and says how far it is.
5
+ //
6
+ // The share is what is known: steps reached, bytes received, a server that answers. When only the
7
+ // start and the end are known, `creep` moves the bar on its own towards a ceiling it never passes,
8
+ // slower the closer it gets, and the end fills it.
9
+
10
+ export const LOADER_MARK = '<svg viewBox="-2 -1 66 22" aria-hidden="true"><path class="madre-loader-trace" d="M1 10H16L20 10L23.5 2L28 18L32 10L35 10L37 6L39 10H52" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path class="madre-loader-beat" d="M1 10H16L20 10L23.5 2L28 18L32 10L35 10L37 6L39 10H52" pathLength="100" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><circle class="madre-loader-dot" cx="60" cy="10" r="3" fill="currentColor"/></svg>';
11
+
12
+ const clamp = (value) => Math.max(0, Math.min(1, Number(value) || 0));
13
+
14
+ // `small`: in a line, for a card's own wait (a project waking in Home), the words first.
15
+ export function makeLoader({ progress = 0, label = '', small = false } = {}) {
16
+ const node = document.createElement('div');
17
+ node.className = `madre-loader${small ? ' small' : ''}`;
18
+ node.setAttribute('role', 'progressbar');
19
+ node.setAttribute('aria-valuemin', '0');
20
+ node.setAttribute('aria-valuemax', '100');
21
+ const mark = document.createElement('span');
22
+ mark.className = 'madre-loader-mark';
23
+ mark.innerHTML = LOADER_MARK; // trusted: our own SVG string
24
+ const bar = document.createElement('span');
25
+ bar.className = 'madre-loader-bar';
26
+ bar.append(document.createElement('i'));
27
+ const pct = document.createElement('span');
28
+ pct.className = 'madre-loader-pct';
29
+ const said = document.createElement('span');
30
+ said.className = 'madre-loader-said';
31
+ node.append(mark, bar, pct, said);
32
+ setLoader(node, progress, label);
33
+ return node;
34
+ }
35
+
36
+ // The share done (0–1), and optionally what is being waited for. The bar never goes back.
37
+ export function setLoader(node, value, label) {
38
+ if (!node) return;
39
+ const before = Number(node.dataset.p ?? 0);
40
+ const p = Math.max(before, clamp(value));
41
+ node.dataset.p = String(p);
42
+ node.style.setProperty('--p', String(p));
43
+ const shown = Math.round(p * 100);
44
+ const pct = node.querySelector('.madre-loader-pct');
45
+ if (pct) pct.textContent = `${shown} %`;
46
+ node.setAttribute('aria-valuenow', String(shown));
47
+ if (label !== undefined) {
48
+ const said = node.querySelector('.madre-loader-said');
49
+ if (said) said.textContent = label;
50
+ if (label) node.setAttribute('aria-label', label);
51
+ }
52
+ }
53
+
54
+ // For a wait whose middle is unknown: towards `to`, a little less of the way left each tick.
55
+ // Answers a stop.
56
+ export function creep(node, { to = 0.9, every = 160, rate = 0.035 } = {}) {
57
+ const timer = setInterval(() => {
58
+ const p = Number(node?.dataset.p ?? 0);
59
+ if (!node?.isConnected || p >= to) { clearInterval(timer); return; }
60
+ setLoader(node, Math.min(to, p + (to - p) * rate));
61
+ }, every);
62
+ return () => clearInterval(timer);
63
+ }
64
+
65
+ // A curtain over the whole page, in the top layer (a popover): above any window that is open and
66
+ // its blurred backdrop, so nothing ever lies over the loader. Where popovers do not exist it is an
67
+ // ordinary fixed layer.
68
+ export function showCurtain(node) {
69
+ if (typeof node.showPopover === 'function') node.popover = 'manual';
70
+ document.body.append(node);
71
+ try { node.showPopover?.(); } catch { /* already shown, or not a popover: it is on the page */ }
72
+ return node;
73
+ }
74
+
75
+ // Done: the bar fills, holds a moment, and whoever waited goes on.
76
+ export function finishLoader(node, { hold = 260 } = {}) {
77
+ setLoader(node, 1);
78
+ return new Promise((resolve) => setTimeout(resolve, hold));
79
+ }