xonecode 0.9.0 → 0.11.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 (100) hide show
  1. package/THIRD_PARTY_NOTICES.md +56 -0
  2. package/apps/web/dist/assets/index-BAyybSz-.css +1 -0
  3. package/apps/web/dist/assets/index-BNCMVjWv.js +485 -0
  4. package/apps/web/dist/assets/{php-DjRQ1K-w.js → php-DPbDT8rX.js} +1 -1
  5. package/apps/web/dist/assets/{ruby-BcLyZ3qi.js → ruby-Bfvws9R8.js} +1 -1
  6. package/apps/web/dist/index.html +2 -2
  7. package/apps/web/dist/openui/visor.css +1 -0
  8. package/apps/web/dist/openui/visor.js +9113 -0
  9. package/dist/agent/exportarPdf.js +5 -1
  10. package/dist/agent/exportarPdf.js.map +1 -1
  11. package/dist/agent/grafo/arbolDeProyecto.js +66 -1
  12. package/dist/agent/grafo/arbolDeProyecto.js.map +1 -1
  13. package/dist/agent/grafo/memoriaDeProyecto.js +3 -2
  14. package/dist/agent/grafo/memoriaDeProyecto.js.map +1 -1
  15. package/dist/agent/grafo/navegacionXone.js +8 -3
  16. package/dist/agent/grafo/navegacionXone.js.map +1 -1
  17. package/dist/agent/grafo/proyecto.js +31 -15
  18. package/dist/agent/grafo/proyecto.js.map +1 -1
  19. package/dist/agent/grafo/unirSecciones.js +128 -0
  20. package/dist/agent/grafo/unirSecciones.js.map +1 -0
  21. package/dist/agent/grafo/xoneAgent.js +7 -0
  22. package/dist/agent/grafo/xoneAgent.js.map +1 -1
  23. package/dist/agent/motores/trueforge/capacidades.js +42 -1
  24. package/dist/agent/motores/trueforge/capacidades.js.map +1 -1
  25. package/dist/agent/motores/trueforge/eventosTrueforge.js +52 -7
  26. package/dist/agent/motores/trueforge/eventosTrueforge.js.map +1 -1
  27. package/dist/agent/motores/trueforge/modeloLangchain.js +5 -2
  28. package/dist/agent/motores/trueforge/modeloLangchain.js.map +1 -1
  29. package/dist/agent/motores/trueforge/sesionTrueforge.js +19 -2
  30. package/dist/agent/motores/trueforge/sesionTrueforge.js.map +1 -1
  31. package/dist/agent/motores/trueforge/toolsDeFichero.js +3 -2
  32. package/dist/agent/motores/trueforge/toolsDeFichero.js.map +1 -1
  33. package/dist/agent/motores/trueforge/trueforge.js +1 -1
  34. package/dist/agent/motores/trueforge/trueforge.js.map +1 -1
  35. package/dist/agent/navegacion/indiceEnDisco.js +10 -1
  36. package/dist/agent/navegacion/indiceEnDisco.js.map +1 -1
  37. package/dist/agent/planesEnDisco.js +64 -0
  38. package/dist/agent/planesEnDisco.js.map +1 -0
  39. package/dist/agent/sesiones/modeloDelCambio.js +70 -0
  40. package/dist/agent/sesiones/modeloDelCambio.js.map +1 -0
  41. package/dist/agent/sesiones/sesionGit.js +14 -0
  42. package/dist/agent/sesiones/sesionGit.js.map +1 -1
  43. package/dist/agent/subagentes/agentesEnDisco.js +95 -5
  44. package/dist/agent/subagentes/agentesEnDisco.js.map +1 -1
  45. package/dist/agent/subagentes/escrituraExterna.js +8 -2
  46. package/dist/agent/subagentes/escrituraExterna.js.map +1 -1
  47. package/dist/agent/subagentes/subagenteCodex.js +1 -1
  48. package/dist/agent/subagentes/subagenteCodex.js.map +1 -1
  49. package/dist/agent/subagentes/subagenteExterno.js +1 -1
  50. package/dist/agent/subagentes/subagenteExterno.js.map +1 -1
  51. package/dist/agent/subagentes/subagenteOpencode.js +1 -1
  52. package/dist/agent/subagentes/subagenteOpencode.js.map +1 -1
  53. package/dist/agent/turno/puente.js +17 -1
  54. package/dist/agent/turno/puente.js.map +1 -1
  55. package/dist/agent/turno/resumenDeContexto.js +17 -4
  56. package/dist/agent/turno/resumenDeContexto.js.map +1 -1
  57. package/dist/agent/turno/turnoReal.js +16 -2
  58. package/dist/agent/turno/turnoReal.js.map +1 -1
  59. package/dist/cli/tui/transcript.js +4 -0
  60. package/dist/cli/tui/transcript.js.map +1 -1
  61. package/dist/core/actos.js.map +1 -1
  62. package/dist/core/agentes.js +17 -0
  63. package/dist/core/agentes.js.map +1 -1
  64. package/dist/core/artefactos.js +22 -0
  65. package/dist/core/artefactos.js.map +1 -1
  66. package/dist/core/diffDeColecciones.js +84 -0
  67. package/dist/core/diffDeColecciones.js.map +1 -0
  68. package/dist/core/exportacion.js +6 -1
  69. package/dist/core/exportacion.js.map +1 -1
  70. package/dist/core/fotoDeColecciones.js +48 -0
  71. package/dist/core/fotoDeColecciones.js.map +1 -0
  72. package/dist/core/imagenesDeDocumento.js +88 -0
  73. package/dist/core/imagenesDeDocumento.js.map +1 -0
  74. package/dist/core/memoria.js +24 -0
  75. package/dist/core/memoria.js.map +1 -0
  76. package/dist/core/navegacion.js.map +1 -1
  77. package/dist/core/notify.js +23 -5
  78. package/dist/core/notify.js.map +1 -1
  79. package/dist/core/ports.js.map +1 -1
  80. package/dist/core/serieDeEscrituras.js +13 -0
  81. package/dist/core/serieDeEscrituras.js.map +1 -1
  82. package/dist/core/settings.js +17 -1
  83. package/dist/core/settings.js.map +1 -1
  84. package/dist/core/tareasDelPlan.js +103 -0
  85. package/dist/core/tareasDelPlan.js.map +1 -0
  86. package/dist/core/trazaDeErrores.js +42 -9
  87. package/dist/core/trazaDeErrores.js.map +1 -1
  88. package/dist/core/turno.js +34 -4
  89. package/dist/core/turno.js.map +1 -1
  90. package/dist/web/servidor/arranque.js +197 -1
  91. package/dist/web/servidor/arranque.js.map +1 -1
  92. package/dist/web/servidor/pielWeb.js +63 -4
  93. package/dist/web/servidor/pielWeb.js.map +1 -1
  94. package/dist/web/servidor/transporte.js.map +1 -1
  95. package/dist/web/servidor/visorOpenui.js +63 -0
  96. package/dist/web/servidor/visorOpenui.js.map +1 -0
  97. package/package.json +1 -1
  98. package/skills/openui-builder/SKILL.md +48 -0
  99. package/apps/web/dist/assets/index-CtfT1BjI.css +0 -1
  100. package/apps/web/dist/assets/index-DqcZ7_Rx.js +0 -478
@@ -77,7 +77,14 @@ import { cambiosDeSesion, fotoDeApertura, olvidarSesion, parcheDeSesion } from "
77
77
  import { commitDeTurno, cambiosPendientes, trabajoSinCommitear } from "../../agent/sesiones/gitSync.js";
78
78
  import { marcarTareaDeSesion, sembrarConsumosPendientes } from "./sesiones.js";
79
79
  import { RUTA_ARTEFACTOS, esRutaDeArtefacto } from "../../core/artefactos.js";
80
- import { arbolDeProyecto, leerFicheroDeProyecto, motivoDeRutaInaceptable, } from "../../agent/grafo/arbolDeProyecto.js";
80
+ import { arbolDeProyecto, leerFicheroDeProyecto, mimeDeImagen, motivoDeRutaInaceptable, } from "../../agent/grafo/arbolDeProyecto.js";
81
+ import { modeloEnDisco } from "../../agent/navegacion/indiceEnDisco.js";
82
+ import { ficherosDelProyecto } from "../../agent/turno/ficherosDelProyecto.js";
83
+ import { fotoDeColecciones } from "../../core/fotoDeColecciones.js";
84
+ import { modeloDelCambio } from "../../agent/sesiones/modeloDelCambio.js";
85
+ import { planesDelProyecto } from "../../agent/planesEnDisco.js";
86
+ import { CSP_DE_OPENUI, documentoDeOpenui, esArtefactoOpenui } from "./visorOpenui.js";
87
+ import { RUTA_IMAGEN_DEL_PROYECTO } from "../../core/imagenesDeDocumento.js";
81
88
  import { leerArtefactoCrudo, leerArtefactoDeSesion, } from "../../agent/grafo/artefactosEnDisco.js";
82
89
  import { CatalogoModelos, baseUrlDeOllama, baseUrlDeOllamaCloud, capacidadesDeOllama, } from "../../agent/config/catalogoModelos.js";
83
90
  import { Modelos } from "../../agent/config/modelos.js";
@@ -163,6 +170,21 @@ export const MS_ENTRE_PROGRESOS = 250;
163
170
  export function raizDelClientePorOmision() {
164
171
  return join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "apps", "web", "dist");
165
172
  }
173
+ /**
174
+ * El visor de OpenUI del build del cliente, o `undefined` si no está. Se lee en cada petición, como
175
+ * el resto del cliente: reconstruirlo se ve recargando, sin reiniciar el servidor.
176
+ */
177
+ export function leerVisorOpenui(raizDelCliente) {
178
+ try {
179
+ return {
180
+ js: readFileSync(join(raizDelCliente, "openui", "visor.js"), "utf8"),
181
+ css: readFileSync(join(raizDelCliente, "openui", "visor.css"), "utf8"),
182
+ };
183
+ }
184
+ catch {
185
+ return undefined;
186
+ }
187
+ }
166
188
  export const FALTA_EL_BUILD = "falta el build del cliente: ejecuta «npm run build:web»";
167
189
  /** Tope del cuerpo de `POST /accion`. Generoso para una prosa larga, finito porque el
168
190
  * cuerpo se acumula en memoria y un cliente roto no puede llenarla. */
@@ -2744,6 +2766,73 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
2744
2766
  emitir({ clase: "arbol", rutas: [], recortado: false, error: "no se pudo listar el proyecto" });
2745
2767
  }
2746
2768
  };
2769
+ /**
2770
+ * El diff semántico de un `.xne` de Revisión. Siempre CONTESTA —la fila abierta espera, y un
2771
+ * silencio la dejaría cargando para siempre—, y la ruta pasa por la MISMA barrera que la del
2772
+ * lector de Ficheros antes de llegar a git.
2773
+ */
2774
+ const atenderModeloDelCambio = async (ruta) => {
2775
+ const abierto = vestibulo.proyectoAbierto();
2776
+ const sesion = abierto?.sesion;
2777
+ const sinNada = (error) => emitir({ clase: "modeloDelCambio", ruta, error });
2778
+ if (abierto === undefined || opciones.modeloDelCambio === undefined)
2779
+ return sinNada("esta ejecución no sabe comparar el modelo");
2780
+ if (sesion === undefined)
2781
+ return sinNada("la sesión todavía no ha empezado");
2782
+ if (motivoDeRutaInaceptable(ruta) !== undefined)
2783
+ return sinNada("esa ruta no se enseña");
2784
+ try {
2785
+ const cambios = await opciones.modeloDelCambio(abierto.raiz, sesion, ruta);
2786
+ if (cambios === undefined)
2787
+ return sinNada("no hay con qué comparar");
2788
+ emitir({ clase: "modeloDelCambio", ruta, cambios });
2789
+ }
2790
+ catch (error) {
2791
+ informar(`no se pudo comparar el modelo (${codigoDe(error)})`);
2792
+ sinNada("no se pudo comparar el modelo");
2793
+ }
2794
+ };
2795
+ /**
2796
+ * Los planes del proyecto abierto. Mismas reglas que el árbol: sin proyecto no se contesta,
2797
+ * sin puerto se contesta con error, y el fallo viaja sin la ruta de la máquina.
2798
+ */
2799
+ const atenderPlanes = async () => {
2800
+ const abierto = vestibulo.proyectoAbierto();
2801
+ if (abierto === undefined)
2802
+ return;
2803
+ if (opciones.planesDelProyecto === undefined) {
2804
+ emitir({ clase: "planes", error: "esta ejecución no puede leer los planes" });
2805
+ return;
2806
+ }
2807
+ try {
2808
+ emitir({ clase: "planes", planes: opciones.planesDelProyecto(abierto.raiz) });
2809
+ }
2810
+ catch (error) {
2811
+ informar(`no se pudieron leer los planes (${codigoDe(error)})`);
2812
+ emitir({ clase: "planes", error: "no se pudieron leer los planes" });
2813
+ }
2814
+ };
2815
+ /**
2816
+ * La foto del modelo XOne del proyecto abierto. Mismas reglas que el árbol: sin proyecto no
2817
+ * se contesta, sin puerto se contesta con error —un «consultando…» eterno es un fallo mudo—,
2818
+ * y el fallo viaja sin la ruta de la máquina.
2819
+ */
2820
+ const atenderColecciones = async () => {
2821
+ const abierto = vestibulo.proyectoAbierto();
2822
+ if (abierto === undefined)
2823
+ return;
2824
+ if (opciones.coleccionesDelProyecto === undefined) {
2825
+ emitir({ clase: "colecciones", error: "esta ejecución no puede leer el modelo del proyecto" });
2826
+ return;
2827
+ }
2828
+ try {
2829
+ emitir({ clase: "colecciones", foto: await opciones.coleccionesDelProyecto(abierto.raiz) });
2830
+ }
2831
+ catch (error) {
2832
+ informar(`no se pudo leer el modelo del proyecto (${codigoDe(error)})`);
2833
+ emitir({ clase: "colecciones", error: "no se pudo leer el modelo del proyecto" });
2834
+ }
2835
+ };
2747
2836
  /**
2748
2837
  * El contenido de una ruta del proyecto abierto. El lector decide si se puede enseñar.
2749
2838
  *
@@ -3341,6 +3430,25 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
3341
3430
  return;
3342
3431
  }
3343
3432
  const descargar = query.get("descargar") !== null;
3433
+ // Un programa de OpenUI se VE dentro de su visor, en un documento autocontenido y sin red
3434
+ // (`visorOpenui.ts`). Descargarlo sigue dando el fichero tal cual.
3435
+ if (!descargar && esArtefactoOpenui(leido.nombre)) {
3436
+ const visor = opciones.visorOpenui?.();
3437
+ if (visor === undefined) {
3438
+ responder(503, "el visor de OpenUI no está construido: ejecuta «npm run build:web»");
3439
+ return;
3440
+ }
3441
+ const documento = Buffer.from(documentoDeOpenui(leido.datos.toString("utf8"), visor, leido.nombre), "utf8");
3442
+ respuesta.writeHead(200, {
3443
+ "Content-Type": "text/html; charset=utf-8",
3444
+ "Content-Length": documento.length,
3445
+ "Content-Security-Policy": CSP_DE_OPENUI,
3446
+ "X-Content-Type-Options": "nosniff",
3447
+ "Cache-Control": "no-store",
3448
+ });
3449
+ respuesta.end(documento);
3450
+ return;
3451
+ }
3344
3452
  // Sin mime conocido se descarga en vez de adivinar. El nombre ya pasó la barrera de
3345
3453
  // segmento llano, así que entre comillas no puede romper la cabecera.
3346
3454
  const inline = !descargar && leido.mime !== undefined;
@@ -3356,6 +3464,67 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
3356
3464
  });
3357
3465
  respuesta.end(leido.datos);
3358
3466
  });
3467
+ /**
3468
+ * `GET /imagen-del-proyecto?ruta=<relativa>` — una IMAGEN del proyecto abierto, para que un `.md`
3469
+ * de la pestaña Ficheros enseñe las suyas.
3470
+ *
3471
+ * Existe porque el visor de markdown solo pinta imágenes `http(s)` absolutas: un enlace relativo o
3472
+ * una `data:` se quedaban en su texto alternativo. La vista del documento reescribe cada enlace a
3473
+ * esta ruta (`arbolDeProyecto.ts#vistaDeMarkdown`), y aquí se sirve con la barrera de siempre:
3474
+ * - **Solo imágenes, por su extensión**, antes de leer nada: esto no es un lector de ficheros por
3475
+ * HTTP, y sin esa criba cualquier ruta que se enseñe se podría pedir por aquí.
3476
+ * - **Leída con `leerFichero`** —las dos barreras de la pestaña Ficheros, sobre el texto y sobre
3477
+ * el camino REAL, y su tope—, no con una copia de ellas.
3478
+ * - `Content-Security-Policy: sandbox`, `nosniff` y `no-store`: un `.svg` abierto a pelo en una
3479
+ * pestaña no ejecuta nada, el navegador no adivina el tipo, y una captura reescrita se ve nueva.
3480
+ */
3481
+ servidor.registrarRuta("GET", RUTA_IMAGEN_DEL_PROYECTO, async (peticion, respuesta) => {
3482
+ const responder = (codigo, texto) => {
3483
+ respuesta.writeHead(codigo, { "Content-Type": "text/plain; charset=utf-8" });
3484
+ respuesta.end(texto);
3485
+ };
3486
+ const ruta = new URLSearchParams((peticion.url ?? "").split("?")[1] ?? "").get("ruta");
3487
+ if (ruta === null || ruta === "") {
3488
+ responder(400, "falta la ruta");
3489
+ return;
3490
+ }
3491
+ if (mimeDeImagen(ruta) === undefined) {
3492
+ responder(403, "eso no es una imagen");
3493
+ return;
3494
+ }
3495
+ const abierto = vestibulo.proyectoAbierto();
3496
+ if (abierto === undefined || opciones.leerFichero === undefined) {
3497
+ responder(404, "no hay imagen");
3498
+ return;
3499
+ }
3500
+ let leido;
3501
+ try {
3502
+ leido = await opciones.leerFichero(abierto.raiz, ruta);
3503
+ }
3504
+ catch (error) {
3505
+ // Como en el resto: al cliente y a `informar` solo el código, nunca el mensaje de Node.
3506
+ informar(`no se pudo servir la imagen «${ruta}» (${codigoDe(error)})`);
3507
+ responder(500, "no se pudo leer");
3508
+ return;
3509
+ }
3510
+ if (leido.error !== undefined) {
3511
+ responder(leido.error === "no existe" ? 404 : 403, leido.error);
3512
+ return;
3513
+ }
3514
+ if (leido.base64 === undefined || leido.mime === undefined) {
3515
+ responder(413, "la imagen es demasiado grande");
3516
+ return;
3517
+ }
3518
+ const datos = Buffer.from(leido.base64, "base64");
3519
+ respuesta.writeHead(200, {
3520
+ "Content-Type": leido.mime,
3521
+ "Content-Length": datos.length,
3522
+ "Content-Security-Policy": "sandbox",
3523
+ "X-Content-Type-Options": "nosniff",
3524
+ "Cache-Control": "no-store",
3525
+ });
3526
+ respuesta.end(datos);
3527
+ });
3359
3528
  /**
3360
3529
  * `POST /adjunto?tarea=<id>&nombre=<fichero>` — los BYTES de un adjunto de tarea.
3361
3530
  *
@@ -3576,6 +3745,27 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
3576
3745
  respuesta.end();
3577
3746
  return;
3578
3747
  }
3748
+ if (typeof mensaje === "object" &&
3749
+ mensaje !== null &&
3750
+ mensaje.clase === "modeloDelCambio" &&
3751
+ typeof mensaje.ruta === "string") {
3752
+ void atenderModeloDelCambio(mensaje.ruta).catch(contar);
3753
+ respuesta.writeHead(204);
3754
+ respuesta.end();
3755
+ return;
3756
+ }
3757
+ if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "planes") {
3758
+ void atenderPlanes().catch(contar);
3759
+ respuesta.writeHead(204);
3760
+ respuesta.end();
3761
+ return;
3762
+ }
3763
+ if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "colecciones") {
3764
+ void atenderColecciones().catch(contar);
3765
+ respuesta.writeHead(204);
3766
+ respuesta.end();
3767
+ return;
3768
+ }
3579
3769
  if (typeof mensaje === "object" &&
3580
3770
  mensaje !== null &&
3581
3771
  mensaje.clase === "sync" &&
@@ -4708,6 +4898,12 @@ export async function arrancarConsolaWeb(opciones) {
4708
4898
  // El proyecto tal como lo ve el agente, para la pestaña Ficheros: mismo filtro, misma
4709
4899
  // barrera de rutas (`agent/grafo/arbolDeProyecto.ts`).
4710
4900
  arbolDelProyecto: async (raiz) => arbolDeProyecto(raiz),
4901
+ // El modelo XOne con el MISMO cargador y el mismo conjunto de ficheros que la tool, para
4902
+ // que la pestaña y el agente no contesten distinto sobre el mismo proyecto.
4903
+ coleccionesDelProyecto: async (raiz) => fotoDeColecciones(await modeloEnDisco(raiz)(ficherosDelProyecto(raiz))),
4904
+ modeloDelCambio,
4905
+ planesDelProyecto,
4906
+ visorOpenui: () => leerVisorOpenui(raizDelCliente),
4711
4907
  leerFichero: leerFicheroDeProyecto,
4712
4908
  leerArtefacto: leerArtefactoDeSesion,
4713
4909
  leerArtefactoCrudo,