xonecode 0.2.0 → 0.5.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.
- package/README.md +49 -7
- package/THIRD_PARTY_NOTICES.md +128 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/apps/web/dist/assets/c-BIGW1oBm.js +1 -0
- package/apps/web/dist/assets/cpp-DddhVWhK.js +1 -0
- package/apps/web/dist/assets/csharp-DSvCPggb.js +1 -0
- package/apps/web/dist/assets/css-CLj8gQPS.js +1 -0
- package/apps/web/dist/assets/go-C27-OAKa.js +1 -0
- package/apps/web/dist/assets/html-CfGypltT.js +1 -0
- package/apps/web/dist/assets/index-BB3SamhR.css +1 -0
- package/apps/web/dist/assets/index-CFYAoHdw.js +477 -0
- package/apps/web/dist/assets/ini-BEwlwnbL.js +1 -0
- package/apps/web/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/apps/web/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/apps/web/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/apps/web/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/apps/web/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/apps/web/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/apps/web/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/apps/web/dist/assets/java-CylS5w8V.js +1 -0
- package/apps/web/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/apps/web/dist/assets/kotlin-BdnUsdx6.js +1 -0
- package/apps/web/dist/assets/less-B1dDrJ26.js +1 -0
- package/apps/web/dist/assets/lua-BaeVxFsk.js +1 -0
- package/apps/web/dist/assets/markdown-Cvjx9yec.js +1 -0
- package/apps/web/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- package/apps/web/dist/assets/php-D_2--9WK.js +1 -0
- package/apps/web/dist/assets/python-B6aJPvgy.js +1 -0
- package/apps/web/dist/assets/ruby-DSHSDbk1.js +1 -0
- package/apps/web/dist/assets/rust-B1yitclQ.js +1 -0
- package/apps/web/dist/assets/scss-D5BDwBP9.js +1 -0
- package/apps/web/dist/assets/sql-CRqJ_cUM.js +1 -0
- package/apps/web/dist/assets/swift-C2oV4EkX.js +1 -0
- package/apps/web/dist/assets/toml-vGWfd6FD.js +1 -0
- package/apps/web/dist/assets/xml-sdJ4AIDG.js +1 -0
- package/apps/web/dist/assets/yaml-Buea-lGh.js +1 -0
- package/apps/web/dist/iconos/xonecode.png +0 -0
- package/apps/web/dist/index.html +20 -0
- package/dist/agent/agentesEnDisco.js +539 -0
- package/dist/agent/agentesEnDisco.js.map +1 -0
- package/dist/agent/arbolDeProyecto.js +245 -0
- package/dist/agent/arbolDeProyecto.js.map +1 -0
- package/dist/agent/artefactosEnDisco.js +139 -0
- package/dist/agent/artefactosEnDisco.js.map +1 -0
- package/dist/agent/aumentador.js +175 -0
- package/dist/agent/aumentador.js.map +1 -0
- package/dist/agent/authEnDisco.js +76 -1
- package/dist/agent/authEnDisco.js.map +1 -1
- package/dist/agent/busquedaRegex.js +92 -0
- package/dist/agent/busquedaRegex.js.map +1 -0
- package/dist/agent/catalogoModelos.js +345 -0
- package/dist/agent/catalogoModelos.js.map +1 -0
- package/dist/agent/checkpointer.js +127 -0
- package/dist/agent/checkpointer.js.map +1 -0
- package/dist/agent/cloudstudioClient.js +194 -0
- package/dist/agent/cloudstudioClient.js.map +1 -0
- package/dist/agent/cloudstudioMcp.js +722 -0
- package/dist/agent/cloudstudioMcp.js.map +1 -0
- package/dist/agent/configEnDisco.js +309 -11
- package/dist/agent/configEnDisco.js.map +1 -1
- package/dist/agent/consumoExterno.js +99 -0
- package/dist/agent/consumoExterno.js.map +1 -0
- package/dist/agent/descarga.js +149 -0
- package/dist/agent/descarga.js.map +1 -0
- package/dist/agent/diagnosticoDeTools.js +51 -0
- package/dist/agent/diagnosticoDeTools.js.map +1 -0
- package/dist/agent/dispositivosEnMaquina.js +596 -0
- package/dist/agent/dispositivosEnMaquina.js.map +1 -0
- package/dist/agent/escrituraDeCodex.js +134 -0
- package/dist/agent/escrituraDeCodex.js.map +1 -0
- package/dist/agent/escrituraDeOpencode.js +80 -0
- package/dist/agent/escrituraDeOpencode.js.map +1 -0
- package/dist/agent/escrituraExterna.js +742 -0
- package/dist/agent/escrituraExterna.js.map +1 -0
- package/dist/agent/gemini.js +81 -0
- package/dist/agent/gemini.js.map +1 -0
- package/dist/agent/git.js +83 -0
- package/dist/agent/git.js.map +1 -0
- package/dist/agent/gitSync.js +441 -0
- package/dist/agent/gitSync.js.map +1 -0
- package/dist/agent/hotswap.js +448 -0
- package/dist/agent/hotswap.js.map +1 -0
- package/dist/agent/instalacionEnMaquina.js +252 -0
- package/dist/agent/instalacionEnMaquina.js.map +1 -0
- package/dist/agent/instantanea.js +7 -10
- package/dist/agent/instantanea.js.map +1 -1
- package/dist/agent/interrupts.js +11 -2
- package/dist/agent/interrupts.js.map +1 -1
- package/dist/agent/juezDeTarea.js +345 -0
- package/dist/agent/juezDeTarea.js.map +1 -0
- package/dist/agent/lanzamientoEnMaquina.js +377 -0
- package/dist/agent/lanzamientoEnMaquina.js.map +1 -0
- package/dist/agent/manifiesto.js +59 -0
- package/dist/agent/manifiesto.js.map +1 -0
- package/dist/agent/memoriaDeProyecto.js +60 -0
- package/dist/agent/memoriaDeProyecto.js.map +1 -0
- package/dist/agent/modelos.js +105 -17
- package/dist/agent/modelos.js.map +1 -1
- package/dist/agent/modelosDeMotor.js +204 -0
- package/dist/agent/modelosDeMotor.js.map +1 -0
- package/dist/agent/paqueteDelProyecto.js +181 -0
- package/dist/agent/paqueteDelProyecto.js.map +1 -0
- package/dist/agent/perfiles.js +92 -31
- package/dist/agent/perfiles.js.map +1 -1
- package/dist/agent/persona.js +40 -0
- package/dist/agent/persona.js.map +1 -0
- package/dist/agent/procesosEnMaquina.js +204 -0
- package/dist/agent/procesosEnMaquina.js.map +1 -0
- package/dist/agent/proyecto.js +311 -13
- package/dist/agent/proyecto.js.map +1 -1
- package/dist/agent/puente.js +63 -6
- package/dist/agent/puente.js.map +1 -1
- package/dist/agent/resumenDeContexto.js +30 -0
- package/dist/agent/resumenDeContexto.js.map +1 -0
- package/dist/agent/resumenDeTool.js +64 -18
- package/dist/agent/resumenDeTool.js.map +1 -1
- package/dist/agent/sesionGit.js +481 -0
- package/dist/agent/sesionGit.js.map +1 -0
- package/dist/agent/settingsEnDisco.js +189 -0
- package/dist/agent/settingsEnDisco.js.map +1 -0
- package/dist/agent/subagenteCodex.js +368 -0
- package/dist/agent/subagenteCodex.js.map +1 -0
- package/dist/agent/subagenteExterno.js +423 -0
- package/dist/agent/subagenteExterno.js.map +1 -0
- package/dist/agent/subagenteOpencode.js +389 -0
- package/dist/agent/subagenteOpencode.js.map +1 -0
- package/dist/agent/subida.js +202 -0
- package/dist/agent/subida.js.map +1 -0
- package/dist/agent/tareasEnDisco.js +466 -0
- package/dist/agent/tareasEnDisco.js.map +1 -0
- package/dist/agent/turnoReal.js +663 -50
- package/dist/agent/turnoReal.js.map +1 -1
- package/dist/agent/versionEnDisco.js +48 -0
- package/dist/agent/versionEnDisco.js.map +1 -0
- package/dist/agent/xoneAgent.js +290 -49
- package/dist/agent/xoneAgent.js.map +1 -1
- package/dist/agent/zip.js +52 -0
- package/dist/agent/zip.js.map +1 -0
- package/dist/cli/acuseDeModelo.js +30 -0
- package/dist/cli/acuseDeModelo.js.map +1 -0
- package/dist/cli/aprobar.js +55 -2
- package/dist/cli/aprobar.js.map +1 -1
- package/dist/cli/config.js +9 -13
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/consola.js +750 -19
- package/dist/cli/consola.js.map +1 -1
- package/dist/cli/main.js +740 -81
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/markdown.js +188 -2
- package/dist/cli/markdown.js.map +1 -1
- package/dist/cli/run.js +32 -4
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/stdio.js +46 -19
- package/dist/cli/stdio.js.map +1 -1
- package/dist/cli/tema.js +96 -8
- package/dist/cli/tema.js.map +1 -1
- package/dist/cli/tokens.js +29 -0
- package/dist/cli/tokens.js.map +1 -0
- package/dist/cli/tui/app.js +171 -0
- package/dist/cli/tui/app.js.map +1 -0
- package/dist/cli/tui/aprobarTui.js +80 -0
- package/dist/cli/tui/aprobarTui.js.map +1 -0
- package/dist/cli/tui/barra.js +37 -0
- package/dist/cli/tui/barra.js.map +1 -0
- package/dist/cli/tui/correrTui.js +478 -0
- package/dist/cli/tui/correrTui.js.map +1 -0
- package/dist/cli/tui/entrada.js +166 -0
- package/dist/cli/tui/entrada.js.map +1 -0
- package/dist/cli/tui/filas.js +24 -0
- package/dist/cli/tui/filas.js.map +1 -0
- package/dist/cli/tui/logo.js +49 -0
- package/dist/cli/tui/logo.js.map +1 -0
- package/dist/cli/tui/pielTui.js +14 -0
- package/dist/cli/tui/pielTui.js.map +1 -0
- package/dist/cli/tui/raton.js +126 -0
- package/dist/cli/tui/raton.js.map +1 -0
- package/dist/cli/tui/sidebar.js +86 -0
- package/dist/cli/tui/sidebar.js.map +1 -0
- package/dist/cli/tui/store.js +143 -0
- package/dist/cli/tui/store.js.map +1 -0
- package/dist/cli/tui/tarjeta.js +19 -0
- package/dist/cli/tui/tarjeta.js.map +1 -0
- package/dist/cli/tui/temaInk.js +114 -0
- package/dist/cli/tui/temaInk.js.map +1 -0
- package/dist/cli/tui/transcript.js +254 -0
- package/dist/cli/tui/transcript.js.map +1 -0
- package/dist/cli/wizardInicial.js +240 -0
- package/dist/cli/wizardInicial.js.map +1 -0
- package/dist/core/actos.js +113 -0
- package/dist/core/actos.js.map +1 -0
- package/dist/core/adjuntos.js +97 -0
- package/dist/core/adjuntos.js.map +1 -0
- package/dist/core/agentes.js +227 -0
- package/dist/core/agentes.js.map +1 -0
- package/dist/core/artefactos.js +149 -0
- package/dist/core/artefactos.js.map +1 -0
- package/dist/core/cloudstudio.js +7 -0
- package/dist/core/cloudstudio.js.map +1 -0
- package/dist/core/config.js +212 -5
- package/dist/core/config.js.map +1 -1
- package/dist/core/contextos.js +53 -1
- package/dist/core/contextos.js.map +1 -1
- package/dist/core/descargas.js +106 -0
- package/dist/core/descargas.js.map +1 -0
- package/dist/core/descriptoresDeApp.js +279 -0
- package/dist/core/descriptoresDeApp.js.map +1 -0
- package/dist/core/dispositivos.js +466 -0
- package/dist/core/dispositivos.js.map +1 -0
- package/dist/core/entrega.js +141 -0
- package/dist/core/entrega.js.map +1 -0
- package/dist/core/entrelazar.js +90 -0
- package/dist/core/entrelazar.js.map +1 -0
- package/dist/core/esqueleto.js +10 -0
- package/dist/core/esqueleto.js.map +1 -1
- package/dist/core/modelos.js +235 -3
- package/dist/core/modelos.js.map +1 -1
- package/dist/core/notify.js +27 -9
- package/dist/core/notify.js.map +1 -1
- package/dist/core/planDeSubida.js +127 -0
- package/dist/core/planDeSubida.js.map +1 -0
- package/dist/core/ports.js +189 -1
- package/dist/core/ports.js.map +1 -1
- package/dist/core/puedeLanzarse.js +239 -0
- package/dist/core/puedeLanzarse.js.map +1 -0
- package/dist/core/rutaVirtual.js +42 -0
- package/dist/core/rutaVirtual.js.map +1 -0
- package/dist/core/settings.js +246 -0
- package/dist/core/settings.js.map +1 -0
- package/dist/core/tareas.js +230 -0
- package/dist/core/tareas.js.map +1 -0
- package/dist/core/textos.js +36 -0
- package/dist/core/textos.js.map +1 -0
- package/dist/core/turno.js +70 -10
- package/dist/core/turno.js.map +1 -1
- package/dist/core/version.js +31 -0
- package/dist/core/version.js.map +1 -0
- package/dist/vendor/tokenTracking.js +22 -1
- package/dist/vendor/tokenTracking.js.map +1 -1
- package/dist/web/servidor/arranque.js +3964 -0
- package/dist/web/servidor/arranque.js.map +1 -0
- package/dist/web/servidor/consolaDeTarea.js +161 -0
- package/dist/web/servidor/consolaDeTarea.js.map +1 -0
- package/dist/web/servidor/consolaWeb.js +389 -0
- package/dist/web/servidor/consolaWeb.js.map +1 -0
- package/dist/web/servidor/corredorDeTareas.js +1119 -0
- package/dist/web/servidor/corredorDeTareas.js.map +1 -0
- package/dist/web/servidor/pielWeb.js +269 -0
- package/dist/web/servidor/pielWeb.js.map +1 -0
- package/dist/web/servidor/servidor.js +257 -0
- package/dist/web/servidor/servidor.js.map +1 -0
- package/dist/web/servidor/sesiones.js +440 -0
- package/dist/web/servidor/sesiones.js.map +1 -0
- package/dist/web/servidor/transporte.js +169 -0
- package/dist/web/servidor/transporte.js.map +1 -0
- package/dist/web/servidor/vestibulo.js +1276 -0
- package/dist/web/servidor/vestibulo.js.map +1 -0
- package/package.json +30 -8
- package/skills/archify/LICENSE +22 -0
- package/skills/archify/SKILL.md +109 -0
- package/skills/archify/THIRD_PARTY_NOTICES.md +56 -0
- package/skills/archify/assets/template.html +14787 -0
- package/skills/archify/bin/archify.mjs +1990 -0
- package/skills/archify/bin/open-artifact.mjs +86 -0
- package/skills/archify/bin/preview.mjs +648 -0
- package/skills/archify/bin/visual-check.mjs +829 -0
- package/skills/archify/brand-marks/README.md +31 -0
- package/skills/archify/brand-marks/catalog.json +131 -0
- package/skills/archify/delta/architecture-delta.mjs +1214 -0
- package/skills/archify/examples/agent-run.lifecycle.json +71 -0
- package/skills/archify/examples/agent-tool-call.workflow.json +94 -0
- package/skills/archify/examples/async-job-roundtrip.sequence.json +61 -0
- package/skills/archify/examples/brand-aware-delivery.architecture.json +47 -0
- package/skills/archify/examples/cache-miss-request.sequence.json +81 -0
- package/skills/archify/examples/checkout-platform.base.architecture.json +31 -0
- package/skills/archify/examples/checkout-platform.head.architecture.json +31 -0
- package/skills/archify/examples/dataflow-product-analytics.html +14898 -0
- package/skills/archify/examples/deployment-release.lifecycle.json +49 -0
- package/skills/archify/examples/event-stream.dataflow.json +57 -0
- package/skills/archify/examples/incident-response.workflow.json +64 -0
- package/skills/archify/examples/lifecycle-agent-run.html +14847 -0
- package/skills/archify/examples/product-analytics.dataflow.json +76 -0
- package/skills/archify/examples/production-deployment.architecture.json +71 -0
- package/skills/archify/examples/release-delivery.workflow.json +62 -0
- package/skills/archify/examples/sequence-cache-miss-request.html +14913 -0
- package/skills/archify/examples/web-app-rendered.html +14862 -0
- package/skills/archify/examples/web-app.architecture.json +46 -0
- package/skills/archify/examples/workflow-agent-tool-call-rendered.html +14904 -0
- package/skills/archify/migrations/workflow-v2.mjs +279 -0
- package/skills/archify/package.json +17 -0
- package/skills/archify/recipes/scenarios.mjs +391 -0
- package/skills/archify/references/authoring-contract.md +198 -0
- package/skills/archify/references/brand-marks.md +69 -0
- package/skills/archify/references/delivery-contract.md +116 -0
- package/skills/archify/references/viewer-runtime.md +45 -0
- package/skills/archify/renderers/architecture/grid.mjs +62 -0
- package/skills/archify/renderers/architecture/render-architecture.mjs +1078 -0
- package/skills/archify/renderers/dataflow/README.md +104 -0
- package/skills/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- package/skills/archify/renderers/lifecycle/README.md +115 -0
- package/skills/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- package/skills/archify/renderers/sequence/README.md +114 -0
- package/skills/archify/renderers/sequence/render-sequence.mjs +453 -0
- package/skills/archify/renderers/shared/brand-marks.mjs +563 -0
- package/skills/archify/renderers/shared/cli.mjs +218 -0
- package/skills/archify/renderers/shared/desktop-readability.mjs +26 -0
- package/skills/archify/renderers/shared/diagnostics.mjs +127 -0
- package/skills/archify/renderers/shared/engineering-profiles.mjs +157 -0
- package/skills/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
- package/skills/archify/renderers/shared/generated-validators.mjs +13 -0
- package/skills/archify/renderers/shared/geometry.mjs +1423 -0
- package/skills/archify/renderers/shared/i18n.mjs +594 -0
- package/skills/archify/renderers/shared/layout-report.mjs +40 -0
- package/skills/archify/renderers/shared/legend.mjs +217 -0
- package/skills/archify/renderers/shared/output-path.mjs +321 -0
- package/skills/archify/renderers/shared/repository-evidence.mjs +235 -0
- package/skills/archify/renderers/shared/text-fit.mjs +49 -0
- package/skills/archify/renderers/shared/utils.mjs +232 -0
- package/skills/archify/renderers/shared/validator.mjs +86 -0
- package/skills/archify/renderers/workflow/README.md +223 -0
- package/skills/archify/renderers/workflow/render-workflow.mjs +35 -0
- package/skills/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- package/skills/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- package/skills/archify/schemas/README.md +211 -0
- package/skills/archify/schemas/architecture.schema.json +176 -0
- package/skills/archify/schemas/common.schema.json +115 -0
- package/skills/archify/schemas/dataflow.schema.json +243 -0
- package/skills/archify/schemas/lifecycle.schema.json +266 -0
- package/skills/archify/schemas/sequence.schema.json +223 -0
- package/skills/archify/schemas/workflow.schema.json +428 -0
- package/skills/archify/scripts/check-render-output.mjs +835 -0
- package/skills/archify/scripts/check-update.mjs +1667 -0
- package/skills/archify/scripts/render-examples.mjs +26 -0
- package/skills/archify/scripts/update-contract.mjs +182 -0
- package/skills/archify/skill-release.json +10 -0
- package/skills/artifacts-builder/SKILL.md +120 -0
- package/skills/artifacts-builder/reference/diagramas.md +123 -0
- package/skills/artifacts-builder/reference/estilo.md +123 -0
- package/skills/artifacts-builder/reference/exportar-pdf.md +56 -0
- package/skills/artifacts-builder/reference/graficas.md +22 -0
- package/skills/artifacts-builder/scripts/bundle_artifact.py +109 -0
- package/skills/artifacts-builder/scripts/validate_artifact.py +345 -0
- package/skills/xone-hotswap/SKILL.md +185 -0
- package/skills/xone-hotswap/references/comandos.md +706 -0
- package/skills/xone-hotswap/references/conexion-y-despliegue.md +226 -0
package/dist/agent/turnoReal.js
CHANGED
|
@@ -1,15 +1,33 @@
|
|
|
1
|
-
import { readdirSync, statSync, existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { join, sep
|
|
1
|
+
import { readdirSync, lstatSync, statSync, existsSync, readFileSync, realpathSync } from "node:fs";
|
|
2
|
+
import { join, sep } from "node:path";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
|
-
import { HumanMessage } from "@langchain/core/messages";
|
|
4
|
+
import { HumanMessage, ToolMessage } from "@langchain/core/messages";
|
|
5
5
|
import { Command, MemorySaver } from "@langchain/langgraph";
|
|
6
6
|
import { collectPending, MAX_APPROVAL_ROUNDS } from "../vendor/hitl.js";
|
|
7
|
+
import { esRutaDeArtefacto } from "../core/artefactos.js";
|
|
8
|
+
/**
|
|
9
|
+
* Cuántas veces se le devuelven los hallazgos al agente para que los corrija. Dos: tres
|
|
10
|
+
* verificaciones en total. Un modelo que no arregla algo en dos intentos con el error
|
|
11
|
+
* delante no lo arregla al quinto, y cada intento son escrituras que pasan por aprobación
|
|
12
|
+
* humana — o sea, tiempo de una persona. La guarda de «no progresa» (misma huella dos
|
|
13
|
+
* veces) corta antes de llegar aquí cuando el modelo repite el mismo cambio.
|
|
14
|
+
*/
|
|
15
|
+
export const TOPE_REPARACIONES = 2;
|
|
7
16
|
import { aPendiente, ficheroDe, cambioDe, buildResume } from "./interrupts.js";
|
|
17
|
+
import { cargarAgentes } from "./agentesEnDisco.js";
|
|
18
|
+
import { crearSubagenteExterno } from "./subagenteExterno.js";
|
|
19
|
+
import { opcionesDeSubagenteExterno } from "./escrituraExterna.js";
|
|
20
|
+
import { ColaDeEventos, entrelazar } from "../core/entrelazar.js";
|
|
21
|
+
import { sumarConsumo, SIN_CONSUMO } from "./consumoExterno.js";
|
|
22
|
+
import { rutaRealDeVirtual } from "../core/rutaVirtual.js";
|
|
8
23
|
import { correrTurno } from "../core/turno.js";
|
|
24
|
+
import { relative, resolve as resolverRuta } from "node:path";
|
|
9
25
|
import { tomarInstantanea } from "./instantanea.js";
|
|
10
26
|
import { construirAgente } from "./xoneAgent.js";
|
|
27
|
+
import { asegurarMemoriaDeProyecto } from "./memoriaDeProyecto.js";
|
|
11
28
|
import { aEventos } from "./puente.js";
|
|
12
29
|
import { createTokenTracker } from "../vendor/tokenTracking.js";
|
|
30
|
+
import { crearDiagnosticoDeTools } from "./diagnosticoDeTools.js";
|
|
13
31
|
/**
|
|
14
32
|
* Recorre la raíz y devuelve las rutas en el ESPACIO VIRTUAL del backend.
|
|
15
33
|
*
|
|
@@ -17,8 +35,10 @@ import { createTokenTracker } from "../vendor/tokenTracking.js";
|
|
|
17
35
|
* importar de agent; al revés no). El recorrido se hace EN CADA llamada a propósito:
|
|
18
36
|
* la lista no se cachea porque los ficheros cambian durante la sesión.
|
|
19
37
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
38
|
+
/** Hasta dónde baja el completado del Tab. El árbol de la consola web pide más (`arbolDeProyecto.ts`). */
|
|
39
|
+
export const PROFUNDIDAD_DEL_TAB = 4;
|
|
40
|
+
export function ficherosDelProyecto(raiz, prof = 0, tope = PROFUNDIDAD_DEL_TAB) {
|
|
41
|
+
if (prof > tope || !existsSync(raiz))
|
|
22
42
|
return new Set();
|
|
23
43
|
const salida = new Set();
|
|
24
44
|
for (const entrada of readdirSync(raiz)) {
|
|
@@ -26,12 +46,28 @@ export function ficherosDelProyecto(raiz, prof = 0) {
|
|
|
26
46
|
continue;
|
|
27
47
|
const ruta = join(raiz, entrada);
|
|
28
48
|
try {
|
|
29
|
-
|
|
49
|
+
// `lstatSync` y no `statSync`: una CARPETA detrás de un enlace simbólico no se
|
|
50
|
+
// recorre. Medido en la pestaña Ficheros de la consola web: «carpeta-enlazada» →
|
|
51
|
+
// «.xonecode» listaba la carpeta denegada bajo otro nombre, y «dir-fuera» →
|
|
52
|
+
// «/algo/de/fuera» listaba nombres de ficheros que no son del proyecto (un
|
|
53
|
+
// «dir-fuera/id_rsa»). Tampoco se lista el enlace en sí: la carpeta a la que apunta
|
|
54
|
+
// o ya está en el árbol por su nombre real, o está fuera del proyecto y no debe
|
|
55
|
+
// salir — y una hoja que al abrirse dice «no se enseña» es un botón muerto.
|
|
56
|
+
// Un enlace a FICHERO sí se queda como hoja: quién decide si se puede leer es la
|
|
57
|
+
// barrera del lector (`arbolDeProyecto.ts`), que recomprueba sobre el `realpath`.
|
|
58
|
+
// Esta función alimenta a TRES consumidores y el cambio es correcto para los tres:
|
|
59
|
+
// el completado de «@ficheros» del Tab (no completa a carpetas ajenas), el universo
|
|
60
|
+
// que consulta `esVistaAplanada` (una vista aplanada de fuera del proyecto no es
|
|
61
|
+
// asunto nuestro) y el árbol de la consola web.
|
|
62
|
+
const info = lstatSync(ruta);
|
|
63
|
+
if (info.isSymbolicLink() && statSync(ruta).isDirectory())
|
|
64
|
+
continue;
|
|
65
|
+
if (info.isDirectory()) {
|
|
30
66
|
// La recursión devuelve rutas relativas al SUBDIRECTORIO: sin recoserle el
|
|
31
67
|
// nombre, «app/Clientes.xne» saldría como «/Clientes.xne» y el Set dejaría de
|
|
32
68
|
// responder por las vistas aplanadas ANIDADAS (este universo es el que consulta
|
|
33
69
|
// `esVistaAplanada`).
|
|
34
|
-
for (const f of ficherosDelProyecto(ruta, prof + 1))
|
|
70
|
+
for (const f of ficherosDelProyecto(ruta, prof + 1, tope))
|
|
35
71
|
salida.add(`/${entrada}${f}`);
|
|
36
72
|
}
|
|
37
73
|
else {
|
|
@@ -44,6 +80,73 @@ export function ficherosDelProyecto(raiz, prof = 0) {
|
|
|
44
80
|
}
|
|
45
81
|
return salida;
|
|
46
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* El nodo de tools del grafo de `createAgent`, comprobado en el paquete instalado
|
|
85
|
+
* (`langchain/dist/agents/nodes/ToolNode.js`: `TOOLS_NODE_NAME = "tools"`). Hace falta para
|
|
86
|
+
* decirle a `updateState` de qué nodo viene lo que se añade; si el nombre cambiara, el
|
|
87
|
+
* remedio de abajo cae al `updateState` sin nodo, que es peor pero no rompe nada.
|
|
88
|
+
*/
|
|
89
|
+
const NODO_DE_TOOLS = "tools";
|
|
90
|
+
/**
|
|
91
|
+
* Cierra las aprobaciones que se quedaron sin contestar cuando murió el proceso anterior.
|
|
92
|
+
*
|
|
93
|
+
* Solo existe desde que el hilo se guarda en disco (`agent/checkpointer.ts`), y arregla un
|
|
94
|
+
* fallo MEDIDO —y medido dos veces, porque la primera medida estaba mal—. La forma del
|
|
95
|
+
* grafo importa: un grafo de un solo nodo donde `START` va al nodo interrumpido reejecuta
|
|
96
|
+
* ese nodo con el mensaje nuevo y vuelve a preguntar, que es inofensivo. El grafo del
|
|
97
|
+
* agente NO tiene esa forma: `START` va al MODELO, y el nodo parado es el de tools. Así que
|
|
98
|
+
* al reabrir una sesión que se cerró con una aprobación delante, lo que le llega al modelo
|
|
99
|
+
* es `human → ai(tool_calls) → human`: un `AIMessage` con llamadas a tool y ningún
|
|
100
|
+
* `ToolMessage` detrás. Gemini y OpenAI rechazan exactamente eso, así que el primer mensaje
|
|
101
|
+
* tras reabrir se iba en un 400 — y nada lo explicaba.
|
|
102
|
+
*
|
|
103
|
+
* El remedio es una respuesta SINTÉTICA por cada llamada colgada, diciendo la verdad: no se
|
|
104
|
+
* aplicó, porque la sesión se cerró antes de que nadie decidiera. Con eso el historial es
|
|
105
|
+
* válido y además honesto — el modelo se entera de que aquella escritura no llegó a pasar,
|
|
106
|
+
* en vez de dar por hecho que sí. Y es cierto: el `interrupt` pausa ANTES de escribir, o
|
|
107
|
+
* sea que el disco no se tocó.
|
|
108
|
+
*
|
|
109
|
+
* Se hace al ABRIR y no al empezar el turno porque es un arreglo del pasado, no del turno:
|
|
110
|
+
* dentro de una sesión viva las aprobaciones se resuelven por su camino normal. Un fallo
|
|
111
|
+
* aquí no puede impedir abrir la sesión, así que se traga — lo que se pierde entonces es
|
|
112
|
+
* este arreglo, no la conversación.
|
|
113
|
+
*
|
|
114
|
+
* Se exporta para poder probarla sin montar un grafo de verdad, igual que `decisionDeTool`.
|
|
115
|
+
*/
|
|
116
|
+
export async function saldarAprobacionesHuerfanas(agente, hilo) {
|
|
117
|
+
const cfg = { configurable: { thread_id: hilo } };
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
119
|
+
const grafo = agente;
|
|
120
|
+
try {
|
|
121
|
+
const estado = await grafo.getState(cfg);
|
|
122
|
+
const mensajes = estado?.values?.messages ?? [];
|
|
123
|
+
if (mensajes.length === 0)
|
|
124
|
+
return;
|
|
125
|
+
const contestadas = new Set(mensajes.filter((m) => m.getType() === "tool").map((m) => m.tool_call_id));
|
|
126
|
+
const colgadas = mensajes
|
|
127
|
+
.filter((m) => m.getType() === "ai")
|
|
128
|
+
.flatMap((m) => m.tool_calls ?? [])
|
|
129
|
+
.filter((c) => c.id !== undefined && !contestadas.has(c.id));
|
|
130
|
+
if (colgadas.length === 0)
|
|
131
|
+
return;
|
|
132
|
+
const respuestas = colgadas.map((c) => new ToolMessage({
|
|
133
|
+
content: "No se aplicó: la sesión se cerró antes de que nadie decidiera sobre esta escritura, " +
|
|
134
|
+
"así que el fichero no se tocó. Si sigue haciendo falta, vuelve a proponerlo.",
|
|
135
|
+
tool_call_id: c.id,
|
|
136
|
+
name: c.name,
|
|
137
|
+
status: "error",
|
|
138
|
+
}));
|
|
139
|
+
try {
|
|
140
|
+
await grafo.updateState(cfg, { messages: respuestas }, NODO_DE_TOOLS);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
await grafo.updateState(cfg, { messages: respuestas });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// Abrir la sesión manda sobre arreglarla.
|
|
148
|
+
}
|
|
149
|
+
}
|
|
47
150
|
/**
|
|
48
151
|
* Abre una sesión real: agente, checkpointer, hilo y tracker se construyen UNA vez aquí, y
|
|
49
152
|
* viven en el cierre. Todo lo que sobreviva al primer turno tiene que salir de este cierre:
|
|
@@ -51,19 +154,120 @@ export function ficherosDelProyecto(raiz, prof = 0) {
|
|
|
51
154
|
*/
|
|
52
155
|
export async function abrirSesionReal(opciones) {
|
|
53
156
|
const { raiz, entorno } = opciones;
|
|
54
|
-
|
|
157
|
+
// Persiste fuera del checkpointer (que es solo de la sesión), pero no sobrescribe nunca
|
|
158
|
+
// una memoria ya creada por el usuario o por otra sesión.
|
|
159
|
+
asegurarMemoriaDeProyecto(raiz);
|
|
160
|
+
/**
|
|
161
|
+
* El tope de rondas de ESTA sesión, resuelto una vez. Ver `OpcionesDeSesion.topeDeRondas`:
|
|
162
|
+
* gobierna los tres sitios que antes miraban `MAX_APPROVAL_ROUNDS` a pelo, y ninguno de
|
|
163
|
+
* los tres puede quedarse fuera — la predicción de cierre tiene que usar exactamente el
|
|
164
|
+
* mismo número que el corte, o un turno se queda sin `fin` o cierra dos veces.
|
|
165
|
+
*/
|
|
166
|
+
const topeDeRondas = opciones.topeDeRondas ?? MAX_APPROVAL_ROUNDS;
|
|
167
|
+
/**
|
|
168
|
+
* Quién autoriza las escrituras de un agente EXTERNO en esta sesión. Se resuelve una vez
|
|
169
|
+
* porque `pedirAprobacion` no cambia dentro de una sesión; la composición vive extraída
|
|
170
|
+
* (`escrituraExterna.ts#politicaExternaDeSesion`) y con test propio.
|
|
171
|
+
*/
|
|
172
|
+
/**
|
|
173
|
+
* El buzón por el que la actividad de un agente EXTERNO entra al flujo de eventos.
|
|
174
|
+
*
|
|
175
|
+
* Vive en el cierre de la SESIÓN y no del turno porque el puerto se construye con el
|
|
176
|
+
* agente, que también es de la sesión. No hay riesgo de mezclar turnos: un agente externo
|
|
177
|
+
* solo corre dentro de uno, y lo que quedara encolado al agotarse un flujo se dice antes
|
|
178
|
+
* de cerrarlo (`entrelazar`).
|
|
179
|
+
*/
|
|
180
|
+
const eventosExternos = new ColaDeEventos();
|
|
181
|
+
/**
|
|
182
|
+
* Lo que lleva consumido ESTA sesión, en dos cuentas que no se mezclan.
|
|
183
|
+
*
|
|
184
|
+
* No se mezclan porque no son el mismo dinero: los del grafo van contra la clave de API
|
|
185
|
+
* del usuario y los del hijo externo contra su suscripción del producto. Sumar TOKENS es
|
|
186
|
+
* legítimo —un token es un token— y sumar el COSTE sería la cifra que miente, así que
|
|
187
|
+
* aquí solo se cuentan tokens y las dos cuentas viajan separadas para que quien las pinte
|
|
188
|
+
* pueda decir la verdad sin tener que elegir una.
|
|
189
|
+
*/
|
|
190
|
+
let consumoExterno = SIN_CONSUMO;
|
|
191
|
+
const oyentesDeConsumo = [];
|
|
192
|
+
const avisarDeConsumo = () => {
|
|
193
|
+
for (const o of oyentesDeConsumo) {
|
|
194
|
+
// Envuelto: un oyente que reviente no puede llevarse por delante un turno. Es la
|
|
195
|
+
// misma razón que el `try` alrededor de `alCambiar` del corredor de tareas, que sin
|
|
196
|
+
// él paraba el corredor entero.
|
|
197
|
+
try {
|
|
198
|
+
o();
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// Un contador que no se pinta no es motivo para tumbar nada.
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
const checkpointer = opciones.checkpointer ?? new MemorySaver();
|
|
55
206
|
const tracker = createTokenTracker();
|
|
207
|
+
const diagnostico = crearDiagnosticoDeTools(raiz);
|
|
56
208
|
let modelos = opciones.modelos;
|
|
57
|
-
let hilo = `xonecode-${randomUUID()}`;
|
|
209
|
+
let hilo = opciones.hilo ?? `xonecode-${randomUUID()}`;
|
|
210
|
+
let cancelarEnCurso;
|
|
211
|
+
let cerrada = false;
|
|
212
|
+
/**
|
|
213
|
+
* Los artefactos escritos en la pasada en curso. Los apunta el backend (es el único que
|
|
214
|
+
* sabe que la escritura ocurrió) y los VACÍA quien los anuncia, para que un artefacto se
|
|
215
|
+
* diga una vez y no en cada pasada de una reparación.
|
|
216
|
+
*/
|
|
217
|
+
const artefactosDeLaPasada = [];
|
|
218
|
+
const carpetaDeArtefactos = opciones.artefactos ?? join(raiz, ".xonecode", "artefactos");
|
|
58
219
|
const construir = async () => construirAgente({
|
|
59
220
|
raiz,
|
|
60
221
|
ficheros: ficherosDelProyecto(raiz),
|
|
222
|
+
// Se releen en CADA construcción del agente y no una vez al abrir la sesión: el
|
|
223
|
+
// usuario puede tocar un `.md` —o guardarlo desde Ajustes— con la consola abierta, y
|
|
224
|
+
// una lista congelada al arrancar le haría creer que su cambio no se aplicó.
|
|
225
|
+
agentes: cargarAgentes(raiz).agentes,
|
|
226
|
+
// Cada llamada al modelo mueve el tracker, y hay que DECIRLO: sin esto el único aviso
|
|
227
|
+
// lo daba el consumo de un agente externo, así que una sesión normal subía sus tokens
|
|
228
|
+
// en silencio y el contador de la web no aparecía nunca.
|
|
229
|
+
alContarTokens: avisarDeConsumo,
|
|
230
|
+
/**
|
|
231
|
+
* El adaptador real. Su import del SDK es dinámico, así que traerlo aquí no carga
|
|
232
|
+
* nada hasta que un agente externo esté dado de alta Y disponible.
|
|
233
|
+
*
|
|
234
|
+
* **La política de escritura sale del MISMO `pedirAprobacion` del HITL del grafo**, y
|
|
235
|
+
* eso es la mitad del diseño: sus implementaciones ya son las dos que hacen falta —la
|
|
236
|
+
* interactiva de las tres pieles, que enseña el diff y espera con plazo naciendo
|
|
237
|
+
* rechazada, y la autónoma de una tarea de fondo, que concede porque la autorización
|
|
238
|
+
* fue crear la tarea, lo ANUNCIA con los nombres y lo apunta en `Tarea.autorizadas`—.
|
|
239
|
+
* Un segundo hueco de política habría sido un segundo sitio donde el fail-closed
|
|
240
|
+
* puede dejar de estarlo. Sin `pedirAprobacion` no hay política, y sin política el
|
|
241
|
+
* hijo no escribe.
|
|
242
|
+
*/
|
|
243
|
+
subagenteExterno: crearSubagenteExterno(opcionesDeSubagenteExterno({
|
|
244
|
+
...(opciones.pedirAprobacion === undefined
|
|
245
|
+
? {}
|
|
246
|
+
: { pedirAprobacion: opciones.pedirAprobacion }),
|
|
247
|
+
// Una FUNCIÓN y no la lista: el hijo escribe durante el turno, así que una foto
|
|
248
|
+
// congelada al abrir la sesión no vería el `.xne` que se acaba de crear — y su
|
|
249
|
+
// `.xml` aplanado dejaría de reconocerse como tal.
|
|
250
|
+
ficherosDelProyecto: () => ficherosDelProyecto(raiz),
|
|
251
|
+
eventos: eventosExternos,
|
|
252
|
+
alConsumir: (c) => {
|
|
253
|
+
consumoExterno = sumarConsumo(consumoExterno, c);
|
|
254
|
+
avisarDeConsumo();
|
|
255
|
+
},
|
|
256
|
+
})),
|
|
61
257
|
modelos,
|
|
62
258
|
skills: opciones.skills,
|
|
63
259
|
checkpointer: checkpointer,
|
|
64
260
|
tracker,
|
|
261
|
+
diagnostico,
|
|
262
|
+
artefactos: {
|
|
263
|
+
carpeta: carpetaDeArtefactos,
|
|
264
|
+
alEscribir: (a) => artefactosDeLaPasada.push(a),
|
|
265
|
+
},
|
|
266
|
+
// Ausente es «no hay», no una carpeta vacía: ver `OpcionesDeSesion.adjuntos`.
|
|
267
|
+
...(opciones.adjuntos === undefined ? {} : { adjuntos: opciones.adjuntos }),
|
|
65
268
|
});
|
|
66
269
|
let agente = await construir();
|
|
270
|
+
await saldarAprobacionesHuerfanas(agente, hilo);
|
|
67
271
|
/**
|
|
68
272
|
* Los pendientes de aprobación, del ESTADO y no del resultado del stream.
|
|
69
273
|
*
|
|
@@ -72,6 +276,29 @@ export async function abrirSesionReal(opciones) {
|
|
|
72
276
|
* —`write_file` lleva el contenido entero—, pero aprobar a ciegas es peor que no aprobar,
|
|
73
277
|
* así que la ruta sí. Una sola llamada a `getState` para las dos cosas.
|
|
74
278
|
*/
|
|
279
|
+
/**
|
|
280
|
+
* El ANTES del diff de una aprobación, leído del disco con las dos barreras: la lexical de
|
|
281
|
+
* `rutaRealDeVirtual` y la recomprobación sobre el camino REAL. Un fichero que no existe
|
|
282
|
+
* —el caso normal de un fichero nuevo— y uno cuyo destino se sale del proyecto dan lo
|
|
283
|
+
* mismo: cadena vacía, que es el lado conservador. No se distinguen a propósito: por aquí
|
|
284
|
+
* no se informa de nada, y decir «ese enlace apunta fuera» exigiría un canal que este
|
|
285
|
+
* lector no tiene.
|
|
286
|
+
*/
|
|
287
|
+
const leerElAntes = (ruta) => {
|
|
288
|
+
const real = rutaRealDeVirtual(raiz, ruta);
|
|
289
|
+
if (real === undefined)
|
|
290
|
+
return "";
|
|
291
|
+
try {
|
|
292
|
+
const raizReal = realpathSync(raiz);
|
|
293
|
+
const destino = realpathSync(real);
|
|
294
|
+
if (destino !== raizReal && !destino.startsWith(raizReal + sep))
|
|
295
|
+
return "";
|
|
296
|
+
return readFileSync(destino, "utf8");
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
return "";
|
|
300
|
+
}
|
|
301
|
+
};
|
|
75
302
|
const leerPendientes = async () => {
|
|
76
303
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
77
304
|
const crudos = collectPending(await agente.getState({
|
|
@@ -83,17 +310,26 @@ export async function abrirSesionReal(opciones) {
|
|
|
83
310
|
const f = ficheroDe(c);
|
|
84
311
|
if (f)
|
|
85
312
|
ficheros.set(c.id, f);
|
|
86
|
-
// El ANTES es el disco: el interrupt pausa ANTES de escribir.
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
313
|
+
// El ANTES es el disco: el interrupt pausa ANTES de escribir. Y si no está en el
|
|
314
|
+
// disco, el fichero es nuevo y su «antes» es la cadena vacía, no un error.
|
|
315
|
+
//
|
|
316
|
+
// La ruta viene del backend del agente, que va con `virtualMode: true`, así que llega
|
|
317
|
+
// ROOTEADA en el proyecto (`/app.xne`) — y esto decía «es relativa a la raíz» y
|
|
318
|
+
// resolvía con `resolve(raiz, ruta)`, que con una absoluta DESCARTA la base. Medido:
|
|
319
|
+
// con la forma rooteada el ANTES salía siempre vacío, así que un fichero EXISTENTE se
|
|
320
|
+
// enseñaba como nuevo y sus líneas quitadas no se veían — en el único momento en que
|
|
321
|
+
// una persona ve lo que el agente va a escribir antes de que exista. Pasó desapercibido
|
|
322
|
+
// porque el test que lo cubría usaba `"app.xne"` sin barra, que es la otra forma que
|
|
323
|
+
// también puede llegar. `rutaRealDeVirtual` acepta las dos y además contiene el `..`,
|
|
324
|
+
// que por aquí traía al diff el contenido de un fichero de FUERA del proyecto.
|
|
325
|
+
//
|
|
326
|
+
// Y la contención lexical NO basta sola: un enlace simbólico DENTRO del proyecto que
|
|
327
|
+
// apunte fuera la pasa —su sitio sí está dentro— y `readFileSync` lo sigue, así que el
|
|
328
|
+
// contenido de fuera acabaría igual en la pantalla de la aprobación. Lo que falla no es
|
|
329
|
+
// el sitio, es el DESTINO: la misma lección que `arbolDeProyecto.ts` ya pagó, y por eso
|
|
330
|
+
// se recomprueba sobre el camino REAL. `realpath` canonicaliza además las mayúsculas,
|
|
331
|
+
// que en APFS y en NTFS es la otra mitad del mismo agujero.
|
|
332
|
+
const vista = cambioDe(c, (ruta) => leerElAntes(ruta));
|
|
97
333
|
if (vista)
|
|
98
334
|
diffs.set(c.id, vista.lineas);
|
|
99
335
|
}
|
|
@@ -106,6 +342,15 @@ export async function abrirSesionReal(opciones) {
|
|
|
106
342
|
* escribió el turno 1.
|
|
107
343
|
*/
|
|
108
344
|
const turno = async (peticion, piel) => {
|
|
345
|
+
// Un turno sobre una sesión ya cerrada reviviría un hilo que su dueño soltó al cambiar
|
|
346
|
+
// de proyecto. Falla en vez de trabajar en silencio sobre la raíz equivocada.
|
|
347
|
+
if (cerrada)
|
|
348
|
+
throw new Error("la sesión ya está cerrada");
|
|
349
|
+
// El reloj del turno ENTERO. Se le pasa a cada pasada de `correrTurno` como `desde`:
|
|
350
|
+
// solo la última cierra, y su `fin` tiene que contar desde aquí y no desde que empezó
|
|
351
|
+
// ella — si no, un turno largo con aprobación por medio diría solo lo que tardó la
|
|
352
|
+
// reanudación.
|
|
353
|
+
const t0 = Date.now();
|
|
109
354
|
const instantanea = await tomarInstantanea(raiz, entorno.git);
|
|
110
355
|
let payload = { messages: [new HumanMessage(peticion)] };
|
|
111
356
|
let bitacora = null;
|
|
@@ -115,46 +360,395 @@ export async function abrirSesionReal(opciones) {
|
|
|
115
360
|
// con las decisiones — también con rejects, que si no se resumen dejan el interrupt
|
|
116
361
|
// colgado para siempre y el modelo nunca llega a saber que se rechazó.
|
|
117
362
|
let cortadoPorTope = false;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
363
|
+
/** Tandas seguidas aprobadas SOLAS —artefactos, o todo si el proyecto está en
|
|
364
|
+
* «sin aprobación»—. Tope propio: ver dónde se usa. */
|
|
365
|
+
let tandasAutomaticas = 0;
|
|
366
|
+
/** Los ficheros del PROYECTO que este turno aplicó sin preguntar. Solo se llena en un
|
|
367
|
+
* proyecto con «sin aprobación»; los artefactos no cuentan aquí, porque un artefacto
|
|
368
|
+
* no es del proyecto y su constancia es su propio acto. */
|
|
369
|
+
const aplicadasSinPreguntar = [];
|
|
370
|
+
/**
|
|
371
|
+
* El estado del lazo, compartido entre el generador y el bucle de rondas.
|
|
372
|
+
*
|
|
373
|
+
* Lo escribe `conVerificacion` mientras la pasada corre, y lo leen el aviso determinista
|
|
374
|
+
* y la decisión de cierre de ESA misma pasada, más el bucle de fuera para saber si hay
|
|
375
|
+
* que reparar. Es la única forma de que quien cierra el turno sepa algo que solo se
|
|
376
|
+
* conoce al agotarse el flujo: si detrás viene otra ronda, otro intento, o nada.
|
|
377
|
+
*/
|
|
378
|
+
let rondaEscribio = false;
|
|
379
|
+
let motivoSinVerificar;
|
|
380
|
+
/**
|
|
381
|
+
* Cómo acabó el verificador en la pasada, y sus hallazgos.
|
|
382
|
+
*
|
|
383
|
+
* Viven aquí y no en la bitácora porque la bitácora responde a otra pregunta: `corrio`
|
|
384
|
+
* dice si un nodo pasó, no con qué veredicto —y de esto depende que una tarea se dé
|
|
385
|
+
* por terminada—. Se reinician en CADA ronda, como `rondaEscribio`: lo que vale es lo
|
|
386
|
+
* que dijo la última pasada, que es la que dejó el proyecto como está.
|
|
387
|
+
*/
|
|
388
|
+
let veredicto = "no-corrio";
|
|
389
|
+
let hallazgosDelTurno = [];
|
|
390
|
+
/**
|
|
391
|
+
* Cuántos hallazgos quedaron FUERA del reparto. `undefined` mientras el verificador no
|
|
392
|
+
* haya corrido: ausente es «no se midió» y `0` es «no había ningún otro»
|
|
393
|
+
* (`core/entrega.ts#ResultadoDeTurno.preexistentes`). De aquí sale lo que le permite al
|
|
394
|
+
* juez de QA leer `hallazgos` por lo que es: una lista YA filtrada.
|
|
395
|
+
*/
|
|
396
|
+
let preexistentesDelTurno;
|
|
397
|
+
/** Si esta pasada cierra el turno. `false` = detrás viene otra ronda o un intento. */
|
|
398
|
+
let cerrarRonda = true;
|
|
399
|
+
/** Si tras esta pasada hay que lanzar un intento de reparación. */
|
|
400
|
+
let reparar = false;
|
|
401
|
+
/** Intentos de reparación ya lanzados en este turno. */
|
|
402
|
+
let intento = 0;
|
|
403
|
+
/** Los hallazgos del último veredicto rojo, para redactar la petición de reparación. */
|
|
404
|
+
let ultimosHallazgos = [];
|
|
405
|
+
/** La huella del veredicto anterior, para detectar que reparar no avanza. */
|
|
406
|
+
let huellaPrevia;
|
|
407
|
+
/**
|
|
408
|
+
* El lazo de verificación, cosido al FINAL del flujo de eventos y no después del turno.
|
|
409
|
+
*
|
|
410
|
+
* Tiene que ir dentro del flujo porque `correrTurno` cierra el turno en su `finally`
|
|
411
|
+
* —el aviso «no ha corrido», y `piel.fin()`— en cuanto el flujo se agota. Verificar
|
|
412
|
+
* después de eso pintaría el veredicto detrás del fin del turno, y en la web el
|
|
413
|
+
* compositor ya se habría encendido con el turno «terminado».
|
|
414
|
+
*
|
|
415
|
+
* Solo verifica la ronda FINAL: la que termina sin escrituras pendientes. En una ronda
|
|
416
|
+
* con aprobaciones por resolver las escrituras no se han aplicado todavía, así que no
|
|
417
|
+
* hay nada que medir — se medirá en la ronda que las aplique.
|
|
418
|
+
*
|
|
419
|
+
* Y solo si el turno tocó ficheros del PROYECTO. Los de `.xonecode/` no cuentan: ahí
|
|
420
|
+
* escribe el propio harness (la memoria, los resúmenes de contexto) y contarlos haría
|
|
421
|
+
* que un turno de pura conversación pasara por el simulador — y que el aviso de «no se
|
|
422
|
+
* verificó» saltara en un «cuéntame un chiste», que es exactamente lo que la bitácora
|
|
423
|
+
* de turno existe para evitar.
|
|
424
|
+
*/
|
|
425
|
+
async function* conVerificacion(eventos) {
|
|
426
|
+
// Un intento de reparación se anuncia al EMPEZAR su pasada, no al final de la anterior:
|
|
427
|
+
// así el «🔁 reparando» abre el tramo de trabajo que viene, en vez de cerrar el que
|
|
428
|
+
// acaba. Es el orden que `guionizado.ts` ya recorre.
|
|
429
|
+
if (intento > 0)
|
|
430
|
+
yield { tipo: "reparacion", intento, tope: TOPE_REPARACIONES };
|
|
431
|
+
reparar = false;
|
|
432
|
+
yield* eventos;
|
|
433
|
+
// Lo primero al agotarse la pasada: lo que el agente dejó escrito sin preguntar. Va
|
|
434
|
+
// ANTES de la decisión de cierre y antes del veredicto porque es trabajo TERMINADO de
|
|
435
|
+
// esta pasada — anunciarlo después del `fin` lo pintaría fuera del turno, que es el
|
|
436
|
+
// mismo motivo por el que el verificador está cosido aquí dentro.
|
|
437
|
+
for (const artefacto of artefactosDeLaPasada.splice(0))
|
|
438
|
+
yield { tipo: "artefacto", artefacto };
|
|
439
|
+
if ((await leerPendientes()).lista.length > 0) {
|
|
440
|
+
// Quedan escrituras por aprobar. Si el bucle va a seguir —hay quien apruebe y no se
|
|
441
|
+
// ha agotado el tope de rondas— esta pasada NO es el final del turno y no cierra.
|
|
442
|
+
// Son las mismas dos condiciones del `break` del bucle, y tienen que serlo: si
|
|
443
|
+
// divergen, un turno se queda sin `fin` o cierra dos veces.
|
|
444
|
+
cerrarRonda = !(ronda < topeDeRondas && opciones.pedirAprobacion !== undefined);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
cerrarRonda = true;
|
|
448
|
+
const cambios = (await instantanea.cambios()).filter((c) => c.clase !== "borrado" && !c.ruta.startsWith(".xonecode/") && c.ruta !== ".xonecode");
|
|
449
|
+
rondaEscribio = cambios.length > 0;
|
|
450
|
+
if (!rondaEscribio) {
|
|
451
|
+
// No hay nada que verificar, y eso NO es un verde. Se DICE, porque este motivo
|
|
452
|
+
// acaba en la tarjeta de una tarea aparcada y «no se sabe» tiene que distinguirse
|
|
453
|
+
// de «el simulador no está» y de «el turno se cortó antes de llegar».
|
|
454
|
+
motivoSinVerificar = "el turno no escribió ningún fichero del proyecto";
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
if (opciones.verifier === undefined) {
|
|
458
|
+
motivoSinVerificar = "esta ejecución no tiene verificador";
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
yield { tipo: "fase", fase: "verificando" };
|
|
462
|
+
let informe;
|
|
463
|
+
try {
|
|
464
|
+
informe = await opciones.verifier.verificar(raiz);
|
|
140
465
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
466
|
+
catch (e) {
|
|
467
|
+
// Que no esté el binario NO es un fallo del proyecto, y se dice como tal. El aviso
|
|
468
|
+
// determinista del final saldrá igualmente: «no ha corrido» sigue siendo verdad.
|
|
469
|
+
motivoSinVerificar = e instanceof Error ? e.message : String(e);
|
|
470
|
+
yield { tipo: "aviso", texto: `⚠ no se pudo verificar: ${motivoSinVerificar}`, severidad: "aviso" };
|
|
471
|
+
return;
|
|
145
472
|
}
|
|
146
|
-
|
|
147
|
-
|
|
473
|
+
// Los hallazgos se reparten entre los ficheros que ESTE turno tocó y los demás. El
|
|
474
|
+
// simulador mira el proyecto entero —es su API—, y un error que ya estaba en un
|
|
475
|
+
// fichero que el agente no abrió no es del agente. Un hallazgo sin fichero no se
|
|
476
|
+
// puede atribuir: se enseña con los del turno, que es el lado conservador.
|
|
477
|
+
const tocados = new Set(cambios.map((c) => resolverRuta(raiz, c.ruta)));
|
|
478
|
+
const delTurno = informe.hallazgos.filter((h) => h.fichero === undefined || tocados.has(resolverRuta(h.fichero)));
|
|
479
|
+
const preexistentes = informe.hallazgos.length - delTurno.length;
|
|
480
|
+
const errores = delTurno.filter((h) => h.severidad === "error").length;
|
|
481
|
+
const hallazgos = delTurno.map((h) => ({
|
|
482
|
+
code: h.code,
|
|
483
|
+
severidad: h.severidad,
|
|
484
|
+
mensaje: h.mensaje,
|
|
485
|
+
...(h.fichero === undefined ? {} : { fichero: relative(raiz, h.fichero) }),
|
|
486
|
+
...(h.linea === undefined ? {} : { linea: h.linea }),
|
|
487
|
+
}));
|
|
488
|
+
// Lo mismo que va al evento, apuntado para el retorno: NO se recalcula ni se
|
|
489
|
+
// re-parsea de la bitácora, que es cómo dos copias de una cuenta acaban discrepando.
|
|
490
|
+
veredicto = errores === 0 ? "verde" : "rojo";
|
|
491
|
+
hallazgosDelTurno = hallazgos;
|
|
492
|
+
// El reparto entero, no solo su mitad. Aquí sí se apunta el cero: el evento lo omite
|
|
493
|
+
// porque una línea de consola que dice «y 0 más» no dice nada, pero quien recibe
|
|
494
|
+
// `hallazgos` necesita saber que la lista está filtrada — y con cero también.
|
|
495
|
+
preexistentesDelTurno = preexistentes;
|
|
496
|
+
yield {
|
|
497
|
+
tipo: "verificacion",
|
|
498
|
+
verde: errores === 0,
|
|
499
|
+
errores,
|
|
500
|
+
avisos: delTurno.length - errores,
|
|
501
|
+
hallazgos,
|
|
502
|
+
...(preexistentes > 0 ? { preexistentes } : {}),
|
|
503
|
+
};
|
|
504
|
+
if (errores === 0)
|
|
505
|
+
return;
|
|
506
|
+
// Rojo. Tres salidas, y solo una de ellas es «inténtalo otra vez».
|
|
507
|
+
//
|
|
508
|
+
// La huella son los ERRORES, no todos los hallazgos: un aviso que va y viene no dice
|
|
509
|
+
// nada de si el error se está arreglando. Y se compara con la del veredicto anterior
|
|
510
|
+
// y no con «¿bajó el número?»: dos errores distintos en vez de dos iguales también es
|
|
511
|
+
// avance, y un modelo que arregla uno y rompe otro no debe quedarse bloqueado como si
|
|
512
|
+
// no hubiera hecho nada.
|
|
513
|
+
const huella = hallazgos
|
|
514
|
+
.filter((h) => h.severidad === "error")
|
|
515
|
+
.map((h) => `${h.code}|${h.fichero ?? ""}|${h.linea ?? ""}`)
|
|
516
|
+
.sort()
|
|
517
|
+
.join("\n");
|
|
518
|
+
if (huella === huellaPrevia) {
|
|
519
|
+
// Corregir no cambió nada: el mismo error, en el mismo sitio. Seguir sería gastar
|
|
520
|
+
// el tope —y aprobaciones humanas— en repetir el mismo cambio.
|
|
521
|
+
yield {
|
|
522
|
+
tipo: "bloqueado",
|
|
523
|
+
motivo: "no-progreso",
|
|
524
|
+
explicacion: `el intento ${intento} dejó los mismos ${errores} error(es) en los mismos sitios`,
|
|
525
|
+
};
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (intento >= TOPE_REPARACIONES) {
|
|
529
|
+
yield {
|
|
530
|
+
tipo: "bloqueado",
|
|
531
|
+
motivo: "tope-reparaciones",
|
|
532
|
+
explicacion: `tras ${intento} intento(s) siguen ${errores} error(es); se deja como está para que lo mires`,
|
|
533
|
+
};
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
// Se intenta otra vez. Esta pasada NO cierra el turno: el `fin` y los avisos van al
|
|
537
|
+
// final del intento que viene, o del que corte.
|
|
538
|
+
huellaPrevia = huella;
|
|
539
|
+
ultimosHallazgos = hallazgos;
|
|
540
|
+
intento += 1;
|
|
541
|
+
reparar = true;
|
|
542
|
+
cerrarRonda = false;
|
|
148
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* La petición de reparación: los hallazgos tal cual, sin más interpretación.
|
|
546
|
+
*
|
|
547
|
+
* Va como un mensaje de USUARIO en el mismo hilo, no como un prompt de sistema nuevo:
|
|
548
|
+
* es la única forma de que el especialista vea lo que acaba de escribir y el error
|
|
549
|
+
* juntos. Y no se le dice CÓMO arreglarlo —eso lo sabe él o no lo sabe—, se le recuerda
|
|
550
|
+
* lo único que importa aquí: que no se invente nada para que el error desaparezca, que
|
|
551
|
+
* es justo lo que XOne no le va a reprochar y el simulador sí.
|
|
552
|
+
*/
|
|
553
|
+
const peticionDeReparacion = () => [
|
|
554
|
+
`El simulador de XOne ha revisado lo que acabas de escribir y ha encontrado ${ultimosHallazgos.filter((h) => h.severidad === "error").length} error(es):`,
|
|
555
|
+
...ultimosHallazgos.map((h) => `- ${h.severidad === "error" ? "ERROR" : "aviso"} ${h.code}${h.fichero === undefined ? "" : ` en ${h.fichero}${h.linea === undefined ? "" : `:${h.linea}`}`}: ${h.mensaje}`),
|
|
556
|
+
"",
|
|
557
|
+
"Corrige los errores. No inventes atributos, funciones ni propiedades para que",
|
|
558
|
+
"desaparezcan: XOne ignora lo desconocido en silencio y el simulador lo detecta.",
|
|
559
|
+
"Si algún error no sabes cómo corregirlo, dilo en vez de intentar otra cosa.",
|
|
560
|
+
].join("\n");
|
|
561
|
+
// Dos bucles anidados y a propósito: el de dentro son las RONDAS de aprobación de una
|
|
562
|
+
// petición (una pausa termina la ronda, se reanuda con las decisiones); el de fuera son
|
|
563
|
+
// los INTENTOS de reparación, cada uno una petición nueva en el mismo hilo. Quién
|
|
564
|
+
// decide si hay intento es el generador, que es quien ve el veredicto.
|
|
565
|
+
do {
|
|
566
|
+
if (intento > 0)
|
|
567
|
+
payload = { messages: [new HumanMessage(peticionDeReparacion())] };
|
|
568
|
+
ronda = 0;
|
|
569
|
+
while (true) {
|
|
570
|
+
ronda += 1;
|
|
571
|
+
rondaEscribio = false;
|
|
572
|
+
motivoSinVerificar = undefined;
|
|
573
|
+
veredicto = "no-corrio";
|
|
574
|
+
hallazgosDelTurno = [];
|
|
575
|
+
preexistentesDelTurno = undefined;
|
|
576
|
+
cerrarRonda = true;
|
|
577
|
+
const aborto = new AbortController();
|
|
578
|
+
cancelarEnCurso = () => aborto.abort(new Error("turno cancelado por el usuario"));
|
|
579
|
+
try {
|
|
580
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
581
|
+
const stream = await agente.stream(payload, {
|
|
582
|
+
configurable: { thread_id: hilo },
|
|
583
|
+
signal: aborto.signal,
|
|
584
|
+
streamMode: ["updates", "messages"],
|
|
585
|
+
subgraphs: true,
|
|
586
|
+
});
|
|
587
|
+
bitacora = await correrTurno(conVerificacion(
|
|
588
|
+
// El flujo del grafo MÁS lo que el agente externo vaya haciendo en su propio
|
|
589
|
+
// proceso: sin esto, ni una de sus tools cruza y son minutos de pantalla quieta.
|
|
590
|
+
entrelazar(aEventos(stream, async () => (await leerPendientes()).lista, ({ nombre, detalle, parametros }) => diagnostico?.herramienta(nombre, detalle, parametros, tracker)), eventosExternos)), piel, {
|
|
591
|
+
// El aviso solo si el turno ESCRIBIÓ y aun así no se verificó. Antes saltaba en
|
|
592
|
+
// todos los turnos, incluido «cuéntame un chiste» — y un aviso que salta cuando
|
|
593
|
+
// no ha pasado nada enseña a ignorarlo, que es lo contrario de lo que se compra
|
|
594
|
+
// con él. Con el motivo, porque «no ha corrido» sin más manda a adivinar.
|
|
595
|
+
avisos: (b) => [
|
|
596
|
+
...(b.corrio("verify") || !rondaEscribio
|
|
597
|
+
? []
|
|
598
|
+
: [
|
|
599
|
+
`⚠ el verificador no ha corrido en este turno${motivoSinVerificar === undefined ? "" : ` (${motivoSinVerificar})`}`,
|
|
600
|
+
]),
|
|
601
|
+
// Lo que se aplicó sin que nadie lo mirara, CON LOS NOMBRES. La decisión se
|
|
602
|
+
// tomó una vez, quizá hace meses, en `settings.json`; el turno que la ejerce
|
|
603
|
+
// es el único momento en que se puede recordar. Solo si hubo alguna: un aviso
|
|
604
|
+
// que salta cuando no ha pasado nada enseña a ignorarlo.
|
|
605
|
+
...(aplicadasSinPreguntar.length === 0
|
|
606
|
+
? []
|
|
607
|
+
: [
|
|
608
|
+
`⚠ ${aplicadasSinPreguntar.length} escritura(s) aplicadas SIN aprobación: ${aplicadasSinPreguntar.join(", ")}` +
|
|
609
|
+
" — este proyecto está en «sin aprobación» (/aprobacion humana lo deshace)",
|
|
610
|
+
]),
|
|
611
|
+
],
|
|
612
|
+
// Solo la ÚLTIMA pasada cierra el turno. Lo decide el generador al agotar el
|
|
613
|
+
// flujo: si quedan rondas o viene un intento, no hay `fin` todavía.
|
|
614
|
+
cerrar: () => cerrarRonda,
|
|
615
|
+
desde: t0,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
finally {
|
|
619
|
+
cancelarEnCurso = undefined;
|
|
620
|
+
}
|
|
621
|
+
const { lista, ficheros, diffs } = await leerPendientes();
|
|
622
|
+
if (lista.length === 0)
|
|
623
|
+
break;
|
|
624
|
+
/**
|
|
625
|
+
* Los artefactos se aprueban SOLOS, y el resto sigue pidiendo permiso.
|
|
626
|
+
*
|
|
627
|
+
* La aprobación existe para proteger el proyecto del usuario, y un artefacto ya no lo
|
|
628
|
+
* toca: vive en la carpeta de la sesión, no entra en git y no sube a CloudStudio. Que
|
|
629
|
+
* dibujar un diagrama costara tres clics enseñaría a aprobar sin mirar, que es como se
|
|
630
|
+
* rompe la aprobación justo cuando importa. A cambio se ANUNCIA (evento `artefacto`):
|
|
631
|
+
* una escritura que nadie aprueba no puede ser además muda.
|
|
632
|
+
*
|
|
633
|
+
* `esRutaDeArtefacto` es una lista blanca de forma y no un `startsWith`, porque de
|
|
634
|
+
* ella depende que esto no sea un camino para escribir en el proyecto sin permiso.
|
|
635
|
+
*/
|
|
636
|
+
const todoAutomatico = opciones.sinAprobacion?.() === true;
|
|
637
|
+
const automaticas = new Map();
|
|
638
|
+
const humanos = [];
|
|
639
|
+
for (const p of lista) {
|
|
640
|
+
const ruta = ficheros.get(p.id);
|
|
641
|
+
if (esRutaDeArtefacto(ruta)) {
|
|
642
|
+
automaticas.set(p.id, { type: "approve" });
|
|
643
|
+
}
|
|
644
|
+
else if (todoAutomatico) {
|
|
645
|
+
// El proyecto está en «sin aprobación». Se apunta la RUTA, que es lo que después
|
|
646
|
+
// dice el aviso de honestidad: un contador sin nombres es el aviso que enseña a
|
|
647
|
+
// ignorar los avisos, que es justo lo que la bitácora existe para evitar.
|
|
648
|
+
automaticas.set(p.id, { type: "approve" });
|
|
649
|
+
aplicadasSinPreguntar.push(ruta ?? p.descripcion);
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
humanos.push(p);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (humanos.length === 0) {
|
|
656
|
+
// Ronda que no gastó a nadie: no cuenta para el tope de APROBACIÓN. Contarla haría
|
|
657
|
+
// que cinco diagramas cortaran el turno con `cortadoPorTope`, cuyo significado
|
|
658
|
+
// —«quedaron escrituras esperando aprobación»— sería falso. La predicción de cierre
|
|
659
|
+
// de la pasada usó el mismo `ronda`, y sigue siendo correcta: viene otra pasada.
|
|
660
|
+
//
|
|
661
|
+
// Pero tiene su PROPIO tope, y por el mismo motivo que el otro: cada pasada es una
|
|
662
|
+
// llamada al modelo, y un agente que escribe un artefacto por pasada sería un bucle
|
|
663
|
+
// que nadie corta —aquí no hay humano al que preguntar, que es justo lo que frena
|
|
664
|
+
// al otro—. Al agotarse se para y se dice, con `cortadoPorTope` puesto: quedaron
|
|
665
|
+
// escrituras sin aplicar, que es lo que ese código de salida significa.
|
|
666
|
+
tandasAutomaticas += 1;
|
|
667
|
+
if (tandasAutomaticas > topeDeRondas) {
|
|
668
|
+
// El texto dice de QUÉ tandas habla: con «sin aprobación» puesto, por aquí pasan
|
|
669
|
+
// también las escrituras del proyecto, y llamarlas «artefactos» sería mentir en
|
|
670
|
+
// el único mensaje que explica por qué el turno se cortó.
|
|
671
|
+
const que = todoAutomatico ? "escrituras sin aprobación" : "artefactos";
|
|
672
|
+
piel.linea(`\n⚠ tope de ${topeDeRondas} tandas de ${que} agotado en este turno.`);
|
|
673
|
+
piel.linea(` quedaban ${lista.length} sin escribir, y NO se han aplicado.`);
|
|
674
|
+
cortadoPorTope = true;
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
ronda -= 1;
|
|
678
|
+
payload = new Command({ resume: buildResume(automaticas) });
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
// El tope existe porque un modelo que insiste tras cada rechazo convierte esto en un
|
|
682
|
+
// ciclo automático de ~200k tokens por ronda (medido en da04). Sin resumir: nada se
|
|
683
|
+
// aplica, pero el interrupt queda en el estado.
|
|
684
|
+
if (ronda >= topeDeRondas) {
|
|
685
|
+
piel.linea(`\n⚠ tope de ${topeDeRondas} rondas de aprobación agotado.`);
|
|
686
|
+
piel.linea(` quedaban ${humanos.length} sin resolver, y NO se han aplicado.`);
|
|
687
|
+
cortadoPorTope = true;
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
if (!opciones.pedirAprobacion) {
|
|
691
|
+
piel.linea(`\n⏸ ${humanos.length} escritura(s) piden aprobación, y no hay quién apruebe.`);
|
|
692
|
+
piel.linea(" Nada se ha aprobado y nada se ha aplicado.");
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
let decisiones;
|
|
696
|
+
try {
|
|
697
|
+
decisiones = await opciones.pedirAprobacion(humanos, ficheros, diffs);
|
|
698
|
+
}
|
|
699
|
+
catch (e) {
|
|
700
|
+
// La ronda que acaba de pasar no cerró el turno —predijo que habría reanudación— y
|
|
701
|
+
// la aprobación ha reventado en vez de contestar (el «sin humano» de `run.ts`, que
|
|
702
|
+
// corta desde dentro). Sin esto, ese turno se quedaba sin `fin`: sin línea de tiempo
|
|
703
|
+
// en stdio y sin plegar en la web. Los avisos no se pierden: con escrituras
|
|
704
|
+
// pendientes ninguno aplica. Se cierra y se propaga, que sigue siendo un fallo.
|
|
705
|
+
if (!cerrarRonda)
|
|
706
|
+
piel.fin(Date.now() - t0);
|
|
707
|
+
throw e;
|
|
708
|
+
}
|
|
709
|
+
// Las dos mitades vuelven en UN solo resume: un interrupt que no se resume se queda
|
|
710
|
+
// colgado para siempre, así que los artefactos aprobados solos tienen que viajar con
|
|
711
|
+
// las decisiones humanas y no en una reanudación aparte.
|
|
712
|
+
payload = new Command({ resume: buildResume(new Map([...automaticas, ...decisiones])) });
|
|
713
|
+
}
|
|
714
|
+
} while (reparar);
|
|
149
715
|
// El diff contra la foto de ESTE turno. Un turno que no tocó nada TIENE que verse igual.
|
|
150
716
|
const cambios = await instantanea.cambios();
|
|
717
|
+
/**
|
|
718
|
+
* Las escrituras que quedaron esperando aprobación, MEDIDAS y no recordadas.
|
|
719
|
+
*
|
|
720
|
+
* Se le pregunta al estado del grafo una vez, aquí, en vez de apuntarlas en cada
|
|
721
|
+
* `break`: hay CUATRO salidas del bucle y tres de ellas usan variables distintas
|
|
722
|
+
* (`lista.length` en el tope de tandas, `humanos.length` en el de rondas y en el «no
|
|
723
|
+
* hay quién apruebe»), así que una contabilidad repartida es una contabilidad que
|
|
724
|
+
* deriva. Y no se deduce de `cortadoPorTope`: por «no hay quién apruebe» se sale sin
|
|
725
|
+
* ponerlo y también quedan escrituras colgando.
|
|
726
|
+
*/
|
|
727
|
+
const pendientes = (await leerPendientes()).lista.length;
|
|
151
728
|
// `cortadoPorTope` viaja en el retorno y no en la bitácora porque es lo que decide el
|
|
152
729
|
// CÓDIGO DE SALIDA de quien invoca: un turno que se quedó con escrituras sin resolver
|
|
153
|
-
// no es un éxito, y CI no puede leerlo como tal.
|
|
154
|
-
|
|
730
|
+
// no es un éxito, y CI no puede leerlo como tal. Y con él viaja ahora lo que decide si
|
|
731
|
+
// una TAREA se puede dar por terminada (`core/entrega.ts`), que hasta ahora se tiraba.
|
|
732
|
+
return {
|
|
733
|
+
bitacora: bitacora,
|
|
734
|
+
cambios,
|
|
735
|
+
cortadoPorTope,
|
|
736
|
+
verificador: veredicto,
|
|
737
|
+
pendientes,
|
|
738
|
+
// Ausente y vacío: sin hallazgos el campo no se pone, para que quien lo lea no
|
|
739
|
+
// confunda «no hubo» con «no se sabe».
|
|
740
|
+
...(hallazgosDelTurno.length === 0 ? {} : { hallazgos: hallazgosDelTurno }),
|
|
741
|
+
...(preexistentesDelTurno === undefined ? {} : { preexistentes: preexistentesDelTurno }),
|
|
742
|
+
...(motivoSinVerificar === undefined ? {} : { motivoSinVerificar }),
|
|
743
|
+
};
|
|
155
744
|
};
|
|
156
745
|
return {
|
|
157
746
|
turno,
|
|
747
|
+
cancelar: () => cancelarEnCurso?.(),
|
|
748
|
+
cerrar: () => {
|
|
749
|
+
cerrada = true;
|
|
750
|
+
cancelarEnCurso?.();
|
|
751
|
+
},
|
|
158
752
|
/** Para `/modelo`: agente nuevo con los mismos hilo, checkpointer y tracker. */
|
|
159
753
|
async cambiarModelos(nuevos) {
|
|
160
754
|
modelos = nuevos;
|
|
@@ -165,6 +759,25 @@ export async function abrirSesionReal(opciones) {
|
|
|
165
759
|
hilo = id ?? `xonecode-${randomUUID()}`;
|
|
166
760
|
},
|
|
167
761
|
tracker,
|
|
762
|
+
/**
|
|
763
|
+
* Lo consumido por la SESIÓN, en sus dos cuentas. Se lee, no se guarda: el tracker y el
|
|
764
|
+
* acumulador viven en este cierre y son la única fuente.
|
|
765
|
+
*/
|
|
766
|
+
consumo: () => ({
|
|
767
|
+
modelo: { entrada: tracker.input, salida: tracker.output, cache: tracker.cache },
|
|
768
|
+
externo: consumoExterno,
|
|
769
|
+
// La entrada de la ÚLTIMA llamada, que es otra pregunta: ver el campo en su tipo.
|
|
770
|
+
contexto: tracker.contexto,
|
|
771
|
+
}),
|
|
772
|
+
/** Avisa cuando cualquiera de las dos cuentas cambia. Devuelve cómo dejar de escuchar. */
|
|
773
|
+
alCambiarConsumo: (oyente) => {
|
|
774
|
+
oyentesDeConsumo.push(oyente);
|
|
775
|
+
return () => {
|
|
776
|
+
const i = oyentesDeConsumo.indexOf(oyente);
|
|
777
|
+
if (i >= 0)
|
|
778
|
+
oyentesDeConsumo.splice(i, 1);
|
|
779
|
+
};
|
|
780
|
+
},
|
|
168
781
|
get hilo() {
|
|
169
782
|
return hilo;
|
|
170
783
|
},
|