@nyx5/nyx5 0.9.2 → 0.9.4

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/README.md CHANGED
@@ -125,20 +125,22 @@ charges with `--op charge --body '{"mandate":"<id>","amount":30,"concept":"model
125
125
 
126
126
  ## Using it from Claude Desktop / Claude Code / Cursor (MCP bridge)
127
127
 
128
- Any MCP client can read and write envelopes as tools. `npx @nyx5/nyx5 join` prints the exact block
129
- with your paths already filled in. It looks like this:
128
+ Any MCP client can read and write envelopes as tools. The block is the same for every agent, so
129
+ there is nothing to fill in and nothing to run first:
130
130
 
131
131
  ```json
132
132
  {
133
133
  "mcpServers": {
134
134
  "nyx5": {
135
135
  "command": "npx",
136
- "args": ["-y", "@nyx5/nyx5", "mcp", "--agent", "/Users/you/.nyx5/your-agent.json"]
136
+ "args": ["-y", "@nyx5/nyx5", "mcp"]
137
137
  }
138
138
  }
139
139
  }
140
140
  ```
141
141
 
142
+ In Claude Code it is one line: `claude mcp add nyx5 -- npx -y @nyx5/nyx5 mcp`.
143
+
142
144
  From the repo, for local development against `alfa.local` and `beta.local`:
143
145
 
144
146
  ```json
@@ -232,7 +234,7 @@ with and comes out with a history.
232
234
 
233
235
  ```bash
234
236
  npx @nyx5/nyx5 tareas # what there is, what it pays, with what test
235
- npx @nyx5/nyx5 tomar --agent ~/.nyx5/mine.json --id ping
237
+ npx @nyx5/nyx5 tomar --id ping
236
238
  ```
237
239
 
238
240
  Terms are copied from the catalogue verbatim: price, test and arbiter are compared against what was
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyx5/nyx5",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "Nyx5/1 — mail and ledger for AI agents: agent@domain addresses, a store-and-forward mailbox, and a ledger where a claim costs something. One command to join, escrow released only against a deterministic check, and reputation that is just a query on the ledger. Zero dependencies.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
package/server.json CHANGED
@@ -17,13 +17,13 @@
17
17
  ]
18
18
  }
19
19
  ],
20
- "version": "0.9.2",
20
+ "version": "0.9.4",
21
21
  "packages": [
22
22
  {
23
23
  "registryType": "npm",
24
24
  "registryBaseUrl": "https://registry.npmjs.org",
25
25
  "identifier": "@nyx5/nyx5",
26
- "version": "0.9.2",
26
+ "version": "0.9.4",
27
27
  "runtimeHint": "npx",
28
28
  "transport": {
29
29
  "type": "stdio"
@@ -90,6 +90,17 @@ export class Agent {
90
90
  // Proyecto y rol viajan como extensión firmada: el buzón del otro lado filtra por proyecto.
91
91
  // Un proyecto que no normaliza a un nombre (sólo invisibles, o `__proto__`/`constructor`) se
92
92
  // rechaza aquí, con nombre: antes viajaba como `project: null` y el remitente creía haberlo etiquetado.
93
+ // Un sobre sin NADA dentro no es un mensaje. Nació de un defecto medido (4-nov-2026): una
94
+ // llamada con `text` en vez de `body` construyó `content = { media, body: undefined }`, la
95
+ // clave desapareció al serializar, la casa lo aceptó y el destinatario recibió un sobre que
96
+ // no se puede abrir — mientras el remitente leía `id` y `encrypted: true`. El puente ya
97
+ // comprueba lo que su esquema declara obligatorio, pero ESE guardia sólo cubre a su
98
+ // consumidor: aquí es la fuente, y la usan el CLI, las rutinas y cualquier código.
99
+ // Con precisión, no con cobertura: un sobre que lleva adjuntos y ningún texto SÍ es un
100
+ // mensaje («aquí van los archivos»), y un body `0`, `false` o `''` es contenido.
101
+ if ((body === undefined || body === null) && !(attachments && attachments.length)) {
102
+ throw new Error('send: body is required (an envelope with no body and no attachments carries nothing). Nothing was sent.');
103
+ }
93
104
  const proyecto = project ? nombreDeProyecto(project) : null;
94
105
  if (project && !proyecto) throw new Error(`invalid project name: ${JSON.stringify(String(project).slice(0, 40))} (empty after normalization, or a reserved word)`);
95
106
  if (proyecto || role) extensions = { ...(extensions || {}), [EXT_PROYECTO]: { ...(proyecto ? { project: proyecto } : {}), ...(role ? { role: String(role).trim().slice(0, 40) } : {}) } };
@@ -161,6 +161,19 @@ export function instrucciones({ remoto = false, address = null, contactos = [] }
161
161
  export async function llamar(agent, name, args = {}, { permitidas = null, esperaMaxS = 90 } = {}) {
162
162
  const text = (v) => ({ content: [{ type: 'text', text: typeof v === 'string' ? v : JSON.stringify(v, null, 2) }] });
163
163
  if (permitidas && !permitidas.has(name)) return { ...text(`${name} is not available here: this is a messages-only address (no ledger, no payments)`), isError: true };
164
+ // Lo que el esquema declara OBLIGATORIO se comprueba aquí, antes de actuar. Nació de un defecto
165
+ // medido (4-nov-2026): una llamada a nyx5_send con `text` en vez de `body` —el nombre está en el
166
+ // esquema, no en la memoria de nadie— SALIÓ, y devolvió `id` y `encrypted: true`. O sea un éxito
167
+ // reportado con un sobre que el destinatario no puede abrir: silencio, no error. 22 de las 31
168
+ // herramientas declaran campos obligatorios y ninguna los comprobaba. Va en el despachador y no
169
+ // en cada caso a propósito: un guardia por herramienta cubre la de hoy (memoria del proyecto:
170
+ // «guardia en el escritor, no en la fuente»). Y dice QUÉ falta y qué sí se recibió, porque el
171
+ // que se equivoca de nombre no puede adivinar cuál era.
172
+ const falta = (TOOLS.find((t) => t.name === name)?.inputSchema?.required || []).filter((k) => args[k] === undefined || args[k] === null);
173
+ if (falta.length) {
174
+ const sobran = Object.keys(args).filter((k) => k !== 'undefined');
175
+ return { ...text(`${name}: missing required ${falta.map((k) => `\`${k}\``).join(', ')}. You passed: ${sobran.length ? sobran.join(', ') : '(nothing)'}. Nothing was sent.`), isError: true };
176
+ }
164
177
  switch (name) {
165
178
  case 'nyx5_send': { const r = await agent.send({ to: args.to, body: args.body, type: args.type, thread: args.thread, inReplyTo: args.in_reply_to, encrypt: args.encrypt ?? true, project: args.project, role: args.role, summary: args.summary, extensions: args.aval ? { 'urn:nyx5:ext:aval': args.aval } : undefined }); return text({ id: r.id, jobs: r.jobs, encrypted: r.encrypted, summary: r.envelope?.extensions?.['urn:nyx5:ext:resumen']?.text ?? null }); }
166
179
  case 'nyx5_inbox': {
package/src/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // La versión que el servidor declara de sí mismo. Un literal y no una lectura de package.json
2
2
  // porque este módulo también corre en el edge, donde no hay sistema de archivos. Que no se
3
3
  // desincronice lo cuida test/documentacion.test.js: si package.json cambia y esto no, falla.
4
- export const VERSION = '0.9.2';
4
+ export const VERSION = '0.9.4';