xonecode 0.19.0 → 0.20.3

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 (79) hide show
  1. package/apps/web/dist/assets/{index-BvPUqjUm.js → index-40sIfNsT.js} +65 -65
  2. package/apps/web/dist/assets/{index-CGEXMJBx.css → index-BYTna8Os.css} +1 -1
  3. package/apps/web/dist/assets/{php-Dk28VW45.js → php-CDg2Quc0.js} +1 -1
  4. package/apps/web/dist/assets/{ruby-CPzEcmdp.js → ruby-DlFaAWQT.js} +1 -1
  5. package/apps/web/dist/index.html +2 -2
  6. package/dist/agent/config/modelos.js +13 -8
  7. package/dist/agent/config/modelos.js.map +1 -1
  8. package/dist/agent/grafo/atributosXone.js +58 -0
  9. package/dist/agent/grafo/atributosXone.js.map +1 -0
  10. package/dist/agent/grafo/describirImagen.js +134 -0
  11. package/dist/agent/grafo/describirImagen.js.map +1 -0
  12. package/dist/agent/grafo/marcarCriteriosDelPlan.js +3 -2
  13. package/dist/agent/grafo/marcarCriteriosDelPlan.js.map +1 -1
  14. package/dist/agent/grafo/xoneAgent.js +7 -6
  15. package/dist/agent/grafo/xoneAgent.js.map +1 -1
  16. package/dist/agent/motores/trueforge/capacidades.js +49 -1
  17. package/dist/agent/motores/trueforge/capacidades.js.map +1 -1
  18. package/dist/agent/motores/trueforge/esperas.js +61 -0
  19. package/dist/agent/motores/trueforge/esperas.js.map +1 -0
  20. package/dist/agent/motores/trueforge/informesDeHijos.js +63 -0
  21. package/dist/agent/motores/trueforge/informesDeHijos.js.map +1 -0
  22. package/dist/agent/motores/trueforge/medidaAutomatica.js +118 -0
  23. package/dist/agent/motores/trueforge/medidaAutomatica.js.map +1 -0
  24. package/dist/agent/motores/trueforge/memoriaDeEspecialistas.js +186 -0
  25. package/dist/agent/motores/trueforge/memoriaDeEspecialistas.js.map +1 -0
  26. package/dist/agent/motores/trueforge/resumenSeguro.js +52 -0
  27. package/dist/agent/motores/trueforge/resumenSeguro.js.map +1 -0
  28. package/dist/agent/motores/trueforge/sesionTrueforge.js +512 -30
  29. package/dist/agent/motores/trueforge/sesionTrueforge.js.map +1 -1
  30. package/dist/agent/motores/trueforge/toolsDeFichero.js +77 -4
  31. package/dist/agent/motores/trueforge/toolsDeFichero.js.map +1 -1
  32. package/dist/agent/subagentes/agentesEnDisco.js +81 -24
  33. package/dist/agent/subagentes/agentesEnDisco.js.map +1 -1
  34. package/dist/agent/turno/diagnosticoDeTools.js +12 -0
  35. package/dist/agent/turno/diagnosticoDeTools.js.map +1 -1
  36. package/dist/agent/turno/informeDeTraza.js +13 -1
  37. package/dist/agent/turno/informeDeTraza.js.map +1 -1
  38. package/dist/agent/turno/resumenDeTool.js +5 -0
  39. package/dist/agent/turno/resumenDeTool.js.map +1 -1
  40. package/dist/core/actos.js.map +1 -1
  41. package/dist/core/adjuntos.js +2 -3
  42. package/dist/core/adjuntos.js.map +1 -1
  43. package/dist/core/agentes.js +20 -1
  44. package/dist/core/agentes.js.map +1 -1
  45. package/dist/core/atributosXone.js +187 -0
  46. package/dist/core/atributosXone.js.map +1 -0
  47. package/dist/core/compararCapturas.js +81 -15
  48. package/dist/core/compararCapturas.js.map +1 -1
  49. package/dist/core/esfuerzo.js +33 -0
  50. package/dist/core/esfuerzo.js.map +1 -1
  51. package/dist/core/iconos.js +6 -1
  52. package/dist/core/iconos.js.map +1 -1
  53. package/dist/core/modoDeEscritura.js +18 -0
  54. package/dist/core/modoDeEscritura.js.map +1 -1
  55. package/dist/core/motor.js +5 -0
  56. package/dist/core/motor.js.map +1 -1
  57. package/dist/core/nucleoXone.js +3 -1
  58. package/dist/core/nucleoXone.js.map +1 -1
  59. package/dist/core/selectorDeCarpeta.js +15 -1
  60. package/dist/core/selectorDeCarpeta.js.map +1 -1
  61. package/dist/core/settings.js +19 -4
  62. package/dist/core/settings.js.map +1 -1
  63. package/dist/core/shellDeAgente.js +39 -0
  64. package/dist/core/shellDeAgente.js.map +1 -1
  65. package/dist/core/tareasDelPlan.js +70 -8
  66. package/dist/core/tareasDelPlan.js.map +1 -1
  67. package/dist/web/servidor/consolaWeb.js +10 -2
  68. package/dist/web/servidor/consolaWeb.js.map +1 -1
  69. package/dist/web/servidor/pielWeb.js +13 -4
  70. package/dist/web/servidor/pielWeb.js.map +1 -1
  71. package/package.json +1 -1
  72. package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +2 -2
  73. package/skills/xone-development/references/datos/appdata.md +5 -3
  74. package/skills/xone-development/references/fundamentos/errores-comunes.md +34 -0
  75. package/skills/xone-hotswap/SKILL.md +2 -0
  76. package/skills/xone-hotswap/scripts/xone-log-android +18 -1
  77. package/skills/xone-plan-builder/SKILL.md +10 -10
  78. package/skills/xone-plan-builder/references/TASKS-FORMAT.md +5 -5
  79. package/skills/xone-spec-builder/SKILL.md +3 -3
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Quién no arranca hasta que otro termine, entre HIJOS que el orquestador lanzó a la vez.
3
+ *
4
+ * El orquestador delega en paralelo cuando le parece independiente, y no siempre lo es: en una pasada real lanzó a
5
+ * `developer-xone` y a `designer-xone` a la vez, el desarrollador escribió el `.xne` antes de que el diseñador
6
+ * terminara y **9 de los 26 recursos del diseñador se quedaron sin usar**. La cola por FICHERO solo evita que dos
7
+ * escriban el mismo; no sabe de dependencias.
8
+ *
9
+ * ## Por qué NO se espera bloqueando (y la primera versión lo hacía)
10
+ *
11
+ * Es un dato del `.md` (`espera: [designer-xone]`). La primera versión paraba al desarrollador ANTES de cada
12
+ * llamada al modelo mientras el diseñador estuviera vivo, y **atascó la pasada**: la librería ejecuta todos los hilos
13
+ * activos juntos y solo devuelve el control cuando TODOS terminan su paso, y una aprobación de escritura del
14
+ * diseñador solo se atiende al devolver el control. El desarrollador, parado dentro de su espera, impedía que
15
+ * volviera, y el diseñador no podía acabar sin aprobación: un bloqueo mutuo, con la CPU a cero y sin ningún error. Los
16
+ * tests con dobles no lo vieron porque el diseñador doble no pedía aprobación.
17
+ *
18
+ * Así que ahora **no bloquea nunca**: si al crear el hijo hay uno vivo —o anunciado por el mismo mensaje del
19
+ * orquestador— de los que declara, el hijo **no arranca** y su llamada se devuelve al instante diciéndolo. El
20
+ * orquestador, que ya tiene el informe del otro cuando lo recibe, vuelve a llamarlo con él en el encargo.
21
+ */
22
+ export function crearEsperas() {
23
+ const vivos = new Map(); // hilo → nombre
24
+ const anunciados = [];
25
+ const terminados = new Set();
26
+ return {
27
+ anunciar(nombre) {
28
+ anunciados.push(nombre);
29
+ },
30
+ nacio(nombre, hilo) {
31
+ const i = anunciados.indexOf(nombre);
32
+ if (i >= 0)
33
+ anunciados.splice(i, 1);
34
+ vivos.set(hilo, nombre);
35
+ },
36
+ murio(hilo) {
37
+ const nombre = vivos.get(hilo);
38
+ if (nombre === undefined)
39
+ return;
40
+ vivos.delete(hilo);
41
+ terminados.add(nombre);
42
+ },
43
+ vivos(nombres, propio) {
44
+ const entre = [...vivos.entries()].filter(([h, n]) => h !== propio && nombres.includes(n)).map(([, n]) => n);
45
+ return [...new Set([...entre, ...anunciados.filter((n) => nombres.includes(n))])];
46
+ },
47
+ terminados(nombres) {
48
+ return nombres.filter((n) => terminados.has(n));
49
+ },
50
+ darPorMuertos() {
51
+ vivos.clear();
52
+ anunciados.length = 0;
53
+ },
54
+ olvidar() {
55
+ vivos.clear();
56
+ anunciados.length = 0;
57
+ terminados.clear();
58
+ },
59
+ };
60
+ }
61
+ //# sourceMappingURL=esperas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"esperas.js","sourceRoot":"","sources":["../../../../src/agent/motores/trueforge/esperas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAmBH,MAAM,UAAU,YAAY;IAC1B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,gBAAgB;IACzD,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,OAAO;QACL,QAAQ,CAAC,MAAM;YACb,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC;QACD,KAAK,CAAC,MAAM,EAAE,IAAI;YAChB,MAAM,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC;gBAAE,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACpC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1B,CAAC;QACD,KAAK,CAAC,IAAI;YACR,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO;YACjC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACnB,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;QACD,KAAK,CAAC,OAAO,EAAE,MAAM;YACnB,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,MAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7G,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpF,CAAC;QACD,UAAU,CAAC,OAAO;YAChB,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,CAAC;QACD,aAAa;YACX,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;QACxB,CAAC;QACD,OAAO;YACL,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;YACtB,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,63 @@
1
+ const lineas = (t) => (typeof t === "string" && t !== "" ? t.replace(/\n$/, "").split("\n").length : 0);
2
+ /** Lo que suma UNA escritura que salió bien, por sus argumentos (los de la tool, que nunca salen de aquí). */
3
+ export function anotarEscritura(cambios, nombre, args) {
4
+ const ruta = typeof args["file_path"] === "string" ? args["file_path"] : undefined;
5
+ if (ruta === undefined || (nombre !== "write_file" && nombre !== "edit_file"))
6
+ return;
7
+ const previo = cambios.get(ruta) ?? { ediciones: 0, nuevo: false, mas: 0, menos: 0 };
8
+ if (nombre === "write_file") {
9
+ cambios.set(ruta, { ediciones: previo.ediciones + 1, nuevo: previo.ediciones === 0 ? true : previo.nuevo, mas: previo.mas + lineas(args["content"]), menos: previo.menos });
10
+ }
11
+ else {
12
+ cambios.set(ruta, { ...previo, ediciones: previo.ediciones + 1, mas: previo.mas + lineas(args["new_string"]), menos: previo.menos + lineas(args["old_string"]) });
13
+ }
14
+ }
15
+ /** ¿Salió bien? El texto de éxito es el de `toolsDeFichero.ts#ejecutar`; un rechazo o un error no cuenta. */
16
+ export function escrituraConExito(contenido) {
17
+ return typeof contenido === "string" && contenido.startsWith("Successfully");
18
+ }
19
+ /**
20
+ * El informe para el padre. `escribe` dice si el hijo podía escribir el proyecto: a uno que no podía no se le
21
+ * atribuye nada, y a uno que podía y no escribió se le dice —un «ya está hecho» sin ninguna escritura es justo lo
22
+ * que el padre tiene que saber—.
23
+ */
24
+ export function textoDelInforme(quien, cambios, escribe, medida) {
25
+ const lineaDeMedida = medida === undefined ? [] : [`Última medida de su pantalla contra la maqueta, hecha por el harness: ${medida}.`];
26
+ if (cambios.size === 0) {
27
+ return escribe ? [`[harness] ${quien} ha terminado su encargo SIN escribir ningún fichero.`, ...lineaDeMedida].join("\n") : undefined;
28
+ }
29
+ const filas = [...cambios.entries()]
30
+ .sort(([a], [b]) => a.localeCompare(b))
31
+ .map(([ruta, c]) => c.nuevo
32
+ ? `- ${ruta}: nuevo${c.ediciones > 1 ? ` y ${c.ediciones - 1} edición(es) después` : ""}, +${c.mas} −${c.menos} líneas`
33
+ : `- ${ruta}: ${c.ediciones} edición(es), +${c.mas} −${c.menos} líneas`);
34
+ return [
35
+ `[harness] Lo que ESCRIBIÓ ${quien} en su encargo, contado por el harness (no por su informe):`,
36
+ ...filas,
37
+ ...lineaDeMedida,
38
+ "Para saber QUÉ ficheros cambió no hace falta releerlos; léelos solo si necesitas su contenido.",
39
+ ].join("\n");
40
+ }
41
+ /**
42
+ * Entrega, antes de la siguiente llamada del hilo, los informes que le esperan (`pendientes` los CONSUME: cada uno
43
+ * sale una vez). Va en el raíz y en quien puede llamar a otros. No añade tools.
44
+ */
45
+ export function capacidadDeInformesDeHijos(pendientes) {
46
+ return {
47
+ nombre: "informes-de-hijos",
48
+ tools: [],
49
+ capability: {
50
+ preLLMProcessors: [
51
+ {
52
+ async *processPreLLM(execution) {
53
+ const textos = pendientes(execution.threadId);
54
+ if (textos.length === 0)
55
+ return;
56
+ yield { type: "internal.agent.context.append", context: [{ role: "user", content: textos.join("\n\n") }], output: [] };
57
+ },
58
+ },
59
+ ],
60
+ },
61
+ };
62
+ }
63
+ //# sourceMappingURL=informesDeHijos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"informesDeHijos.js","sourceRoot":"","sources":["../../../../src/agent/motores/trueforge/informesDeHijos.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,GAAG,CAAC,CAAU,EAAU,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEzH,8GAA8G;AAC9G,MAAM,UAAU,eAAe,CAC7B,OAAqC,EACrC,MAAc,EACd,IAA6B;IAE7B,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnF,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,MAAM,KAAK,YAAY,IAAI,MAAM,KAAK,WAAW,CAAC;QAAE,OAAO;IACtF,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACrF,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAC9K,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC;IACpK,CAAC;AACH,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,iBAAiB,CAAC,SAAkB;IAClD,OAAO,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AAC/E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,OAA6C,EAAE,OAAgB,EAAE,MAAe;IAC7H,MAAM,aAAa,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,yEAAyE,MAAM,GAAG,CAAC,CAAC;IACvI,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,KAAK,uDAAuD,EAAE,GAAG,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxI,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;SACjC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SACtC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CACjB,CAAC,CAAC,KAAK;QACL,CAAC,CAAC,KAAK,IAAI,UAAU,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,SAAS;QACvH,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,SAAS,kBAAkB,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,SAAS,CAC1E,CAAC;IACJ,OAAO;QACL,6BAA6B,KAAK,6DAA6D;QAC/F,GAAG,KAAK;QACR,GAAG,aAAa;QAChB,gGAAgG;KACjG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,UAAsC;IAC/E,OAAO;QACL,MAAM,EAAE,mBAAmB;QAC3B,KAAK,EAAE,EAAE;QACT,UAAU,EAAE;YACV,gBAAgB,EAAE;gBAChB;oBACE,KAAK,CAAC,CAAC,aAAa,CAAC,SAA+B;wBAClD,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;wBAC9C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;4BAAE,OAAO;wBAChC,MAAM,EAAE,IAAI,EAAE,+BAA+B,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;oBACzH,CAAC;iBACF;aACF;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * La MEDIDA de la pantalla, puesta por el harness tras cada prueba en el aparato.
3
+ *
4
+ * Existe por tres pasadas de la misma calculadora: en la mejor (calc9) el desarrollador midió su pantalla con
5
+ * `comparar_capturas` y el crítico diez veces; en la siguiente (calc10), una, y quien acabó juzgando fue el
6
+ * orquestador, con dos rondas de rediseño que no convergieron. El prompt del bucle ya le daba las herramientas y el
7
+ * criterio del 10 %; era texto, y un texto no se cumple solo. Así que cuando vuelve quien EJECUTA con una captura
8
+ * nueva, el harness la mide contra la maqueta —la misma aritmética de `comparar_capturas`, sin modelo— y se la da a
9
+ * quien lo llamó: el desarrollador tiene el número delante sin acordarse de pedirlo, y el orquestador no tiene que
10
+ * volver a medir para saber cómo quedó.
11
+ *
12
+ * Lo cualitativo —texto cortado, formas, colores— sigue siendo del crítico, que cuesta una llamada de visión: aquí
13
+ * solo se le RECUERDA pasarlo si la medida no llega y no lo ha pasado desde su última escritura.
14
+ */
15
+ import { readdirSync, readFileSync, statSync, existsSync } from "node:fs";
16
+ import { join, relative, sep } from "node:path";
17
+ import { compararPantallas, desajusteDeEncaje, informeDeComparacion, TOLERANCIA_DE_ENCAJE } from "../../../core/compararCapturas.js";
18
+ import { decodificarImagen } from "../../dispositivos/decodificarImagen.js";
19
+ /**
20
+ * Rondas VISUALES por encargo: una por captura medida. calc9, calc12 y calc13 sacaron 20-25 capturas cada una, retocando
21
+ * una o dos cosas por despliegue. Con el aviso, la tercera se DICE la última: después se devuelve con lo que falta.
22
+ */
23
+ export const RONDAS_VISUALES = 3;
24
+ /** El criterio de aceptación por omisión del bucle (`textoDelBucle`): por debajo, en vertical Y en horizontal. */
25
+ export const CRITERIO_DE_DISTANCIA = 0.1;
26
+ const ES_IMAGEN = /\.(png|jpe?g)$/i;
27
+ /** La maqueta de la sesión: `/diseno/screen.png` si está, si no la primera imagen de `/diseno/` y después la de los adjuntos. */
28
+ export function buscarMaqueta(raiz, adjuntos) {
29
+ const diseno = join(raiz, "diseno");
30
+ const preferida = join(diseno, "screen.png");
31
+ if (existsSync(preferida))
32
+ return { virtual: "/diseno/screen.png", disco: preferida };
33
+ for (const [carpeta, prefijo] of [
34
+ [diseno, "/diseno/"],
35
+ ...(adjuntos === undefined ? [] : [[adjuntos, "/adjuntos/"]]),
36
+ ]) {
37
+ try {
38
+ const nombre = readdirSync(carpeta).filter((n) => ES_IMAGEN.test(n)).sort()[0];
39
+ if (nombre !== undefined)
40
+ return { virtual: `${prefijo}${nombre}`, disco: join(carpeta, nombre) };
41
+ }
42
+ catch {
43
+ // Sin esa carpeta no hay maqueta ahí.
44
+ }
45
+ }
46
+ return undefined;
47
+ }
48
+ /** La captura MÁS RECIENTE de la carpeta de artefactos escrita desde `desdeMs`, con su nombre relativo (dos niveles). */
49
+ export function ultimaCaptura(carpeta, desdeMs) {
50
+ let mejor;
51
+ const mirar = (dir, nivel) => {
52
+ let entradas;
53
+ try {
54
+ entradas = readdirSync(dir);
55
+ }
56
+ catch {
57
+ return;
58
+ }
59
+ for (const n of entradas) {
60
+ const ruta = join(dir, n);
61
+ let st;
62
+ try {
63
+ st = statSync(ruta);
64
+ }
65
+ catch {
66
+ continue;
67
+ }
68
+ if (st.isDirectory()) {
69
+ if (nivel < 1)
70
+ mirar(ruta, nivel + 1);
71
+ }
72
+ else if (ES_IMAGEN.test(n) && st.mtimeMs >= desdeMs && (mejor === undefined || st.mtimeMs > mejor.t)) {
73
+ mejor = { nombre: relative(carpeta, ruta).split(sep).join("/"), t: st.mtimeMs };
74
+ }
75
+ }
76
+ };
77
+ mirar(carpeta, 0);
78
+ return mejor?.nombre;
79
+ }
80
+ /** Mide una captura contra la maqueta. Lanza si alguna no se puede abrir o decodificar: quien llama lo traga. */
81
+ export function medirContraMaqueta(captura, maqueta) {
82
+ return compararPantallas(decodificarImagen(readFileSync(maqueta)), decodificarImagen(readFileSync(captura)));
83
+ }
84
+ const pct = (x) => `${Math.round(x * 100)} %`;
85
+ /** ¿Cumple el criterio? En las DOS direcciones Y encajando en la pantalla: cada borde del contenido cerca del de la maqueta. */
86
+ export function cumpleElCriterio(c) {
87
+ return (c.distanciaVertical < CRITERIO_DE_DISTANCIA &&
88
+ c.distanciaHorizontal < CRITERIO_DE_DISTANCIA &&
89
+ desajusteDeEncaje(c.encaje) <= TOLERANCIA_DE_ENCAJE);
90
+ }
91
+ /** La cifra corta, para el informe al orquestador. */
92
+ export function resumenDeMedida(c, captura) {
93
+ return `vertical ${pct(c.distanciaVertical)}, horizontal ${pct(c.distanciaHorizontal)}, encaje ${pct(desajusteDeEncaje(c.encaje))} en el peor borde (${cumpleElCriterio(c) ? "cumple" : "NO cumple"} el ${pct(CRITERIO_DE_DISTANCIA)} y el ${pct(TOLERANCIA_DE_ENCAJE)} de encaje; captura /artefactos/${captura})`;
94
+ }
95
+ /** El mensaje para quien llamó a quien ejecuta. `pedirCritica`: no ha pasado el crítico desde su última escritura. */
96
+ export function textoDeMedidaAutomatica(opciones) {
97
+ const { comparacion: c } = opciones;
98
+ const cumple = cumpleElCriterio(c);
99
+ return [
100
+ `[harness] Medida AUTOMÁTICA de la última captura que dejó ${opciones.quien} (/artefactos/${opciones.captura}) contra la maqueta ${opciones.maqueta}:`,
101
+ ...informeDeComparacion(c),
102
+ cumple
103
+ ? `La estructura CUMPLE el criterio (< ${pct(CRITERIO_DE_DISTANCIA)} en vertical y en horizontal, y cada borde a ${pct(TOLERANCIA_DE_ENCAJE)} o menos del de la maqueta). Queda lo que esto no mide: el crítico.`
104
+ : `NO cumple el criterio (< ${pct(CRITERIO_DE_DISTANCIA)} en vertical y en horizontal, y que ENCAJE en la pantalla: cada borde del contenido a ${pct(TOLERANCIA_DE_ENCAJE)} o menos del de la maqueta). No lo des por terminado ni lo devuelvas así: ` +
105
+ "corrige tú, o encarga al diseñador las diferencias CONCRETAS de arriba (qué franja, qué falta o sobra), y vuelve a comprobar.",
106
+ ...(opciones.ronda !== undefined && !cumple
107
+ ? [
108
+ opciones.ronda >= RONDAS_VISUALES
109
+ ? `Es tu ronda visual ${opciones.ronda} de ${RONDAS_VISUALES}: arregla lo que puedas en ESTA y devuelve el trabajo diciendo qué falta, con esta medida. No pidas otra captura para retocar.`
110
+ : `Ronda visual ${opciones.ronda} de ${RONDAS_VISUALES}: junta TODAS las diferencias en un solo cambio antes de pedir la siguiente captura.`,
111
+ ]
112
+ : []),
113
+ ...(opciones.pedirCritica
114
+ ? [`Desde tu última escritura no has pasado xone_critica_visual: pásala con pantalla=/artefactos/${opciones.captura} y referencia=${opciones.maqueta}.`]
115
+ : []),
116
+ ].join("\n");
117
+ }
118
+ //# sourceMappingURL=medidaAutomatica.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"medidaAutomatica.js","sourceRoot":"","sources":["../../../../src/agent/motores/trueforge/medidaAutomatica.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * La memoria de cada ESPECIALISTA dentro de una sesión (opción 3 del análisis de la calculadora).
3
+ *
4
+ * Cada `create_sub_agent` crea un hilo NUEVO y la librería no sabe reanudar uno: el que ya había
5
+ * leído la hoja de estilos y descubierto cómo se lanza la app volvía a leerla y a descubrirlo en la
6
+ * delegación siguiente. Medido en una sesión real de 10 delegaciones: de 362 lecturas y búsquedas,
7
+ * 88 eran del MISMO especialista que ya las había hecho en una delegación anterior —el doble que
8
+ * las de otro agente—, y ese es exactamente el hueco que esto cierra.
9
+ *
10
+ * **Lo que se recuerda es la conversación PROPIA del especialista**, nunca lo que leyeron otros: la
11
+ * siguiente encarnación arranca con su historial entero (`definition.messages`) seguido del encargo
12
+ * nuevo. No se inyecta nada ajeno, y por eso no es «el paquete de contexto» que se descartó.
13
+ *
14
+ * Reglas, y de dónde salen:
15
+ *
16
+ * - **Dos niveles, y por qué** (`TOPE_COMPLETA_TOKENS`, `TOPE_REDUCIDA_TOKENS`). Un hijo no se compacta,
17
+ * por una medida (resumirlo costaba más que reenviarlo), así que su historial solo crece, y al
18
+ * cerrar cada delegación de una sesión real medía: consultor y analista 62-81 mil tokens,
19
+ * desarrollador 96-122 mil, diseñador 124-198 mil, conductor 16-83 mil. Un tope único de 48.000 —la
20
+ * primera versión— olvidaba a casi todos, justo a los que más leen, y la primera prueba real lo
21
+ * delató. Y guardar el historial ENTERO de un diseñador reenvía 200 mil tokens en cada llamada.
22
+ * Así que: si cabe entero, se guarda entero (el especialista conserva lo que leyó); si no, se
23
+ * guarda REDUCIDO —lo que dijo, lo que pidió y qué contestó cada tool, recortado— y sabe QUÉ hizo y
24
+ * descubrió aunque tenga que releer un fichero si lo necesita; y si ni reducido cabe, se queda con la VENTANA de
25
+ * lo más reciente (`ventanaDeHistorial`), no con nada: la primera versión olvidaba todo y borró a cuatro
26
+ * especialistas en una sola pasada.
27
+ * - **Un hilo vivo por especialista**: si ya hay una encarnación corriendo (delegaciones en
28
+ * paralelo), la segunda arranca de cero. Una memoria no se reparte entre dos hilos vivos.
29
+ * - **Saneada**: un hijo cortado o que acaba con una tool call sin respuesta daría un 400 del
30
+ * proveedor; se salda con el mismo texto que el raíz (`saldarColgadas`).
31
+ * - **Solo si acabó BIEN**: un hijo que falló, o al que se cortó con «Detener» (su historial lleva la
32
+ * orden de parar y la heredaría la siguiente encarnación), no deja memoria y borra la que hubiera.
33
+ * - **En memoria y por sesión**: no va a la foto en disco (habría que subir `VERSION_DE_MEMORIA`) y
34
+ * se olvida al abrir otra conversación. **Límite declarado**: reabrir una sesión no la recupera.
35
+ */
36
+ import { saldarColgadas } from "./memoriaTrueforge.js";
37
+ /** Hasta aquí se guarda el historial ENTERO. Provisional: el orden de un conductor o un consultor. */
38
+ export const TOPE_COMPLETA_TOKENS = 48_000;
39
+ /** Hasta aquí se guarda REDUCIDO; por encima se olvida. Provisional. */
40
+ export const TOPE_REDUCIDA_TOKENS = 40_000;
41
+ /** Lo que se conserva de lo que devolvió cada tool en la versión reducida. */
42
+ export const MAX_CARACTERES_DE_RESULTADO = 500;
43
+ /** Tokens por carácter: la misma aproximación que usa el resto del harness donde no hay medida. */
44
+ const CARACTERES_POR_TOKEN = 4;
45
+ /** Lo que se conserva de cada argumento largo de una llamada a tool (el contenido de un `write_file`, el texto de un `edit_file`). */
46
+ export const MAX_CARACTERES_DE_ARGUMENTO = 300;
47
+ /**
48
+ * Los argumentos de una llamada con los valores de texto largos recortados. Un escritor que produce cien mil
49
+ * tokens de salida los lleva ahí (el código entero de cada `edit_file` y `write_file`), y con solo recortar lo que
50
+ * devuelven las tools su historial seguía sin caber: la segunda prueba real lo perdió a 211 mil tokens. La ruta y
51
+ * el resto de campos cortos se conservan, que es lo que dice QUÉ hizo. Si no se entiende el JSON, se deja.
52
+ */
53
+ function recortarArgumentos(argumentos, max) {
54
+ if (typeof argumentos !== "string")
55
+ return argumentos;
56
+ try {
57
+ const valor = JSON.parse(argumentos);
58
+ if (valor === null || typeof valor !== "object" || Array.isArray(valor))
59
+ return argumentos;
60
+ let cambio = false;
61
+ const recortado = Object.fromEntries(Object.entries(valor).map(([k, v]) => {
62
+ if (typeof v === "string" && v.length > max) {
63
+ cambio = true;
64
+ return [k, `${v.slice(0, max)}… [recortado: eran ${String(v.length)} caracteres]`];
65
+ }
66
+ return [k, v];
67
+ }));
68
+ return cambio ? JSON.stringify(recortado) : argumentos;
69
+ }
70
+ catch {
71
+ return argumentos;
72
+ }
73
+ }
74
+ const tokensDe = (mensajes) => Math.ceil(JSON.stringify(mensajes).length / CARACTERES_POR_TOKEN);
75
+ /**
76
+ * El historial sin lo voluminoso: lo que devolvió cada tool se recorta (con la marca de que se recortó, para
77
+ * que el modelo sepa que puede releerlo), los argumentos largos de sus llamadas también, y el razonamiento de
78
+ * las vueltas viejas se quita. Lo que el especialista DIJO y a qué fichero o búsqueda apuntó cada llamada se
79
+ * conservan. Puro.
80
+ */
81
+ export function reducirHistorial(mensajes, maxResultado = MAX_CARACTERES_DE_RESULTADO, maxArgumento = MAX_CARACTERES_DE_ARGUMENTO) {
82
+ return mensajes.map((m) => {
83
+ if (m.role === "tool" && typeof m.content === "string" && m.content.length > maxResultado) {
84
+ return { ...m, content: `${m.content.slice(0, maxResultado)}\n[recortado: eran ${String(m.content.length)} caracteres; vuelve a leerlo si lo necesitas]` };
85
+ }
86
+ if (m.role === "assistant") {
87
+ const { reasoning_content: _r, ...resto } = m;
88
+ void _r;
89
+ if (resto.tool_calls === undefined)
90
+ return resto;
91
+ return {
92
+ ...resto,
93
+ tool_calls: resto.tool_calls.map((t) => t.function === undefined ? t : { ...t, function: { ...t.function, arguments: recortarArgumentos(t.function.arguments, maxArgumento) } }),
94
+ };
95
+ }
96
+ return m;
97
+ });
98
+ }
99
+ /**
100
+ * La VENTANA: lo más reciente que cabe. Cada delegación empieza con un mensaje de usuario (el encargo o el «NUEVO
101
+ * ENCARGO»), así que se descartan delegaciones ENTERAS, las más antiguas primero: una llamada a tool y su respuesta
102
+ * viven dentro de la misma delegación y nunca quedan partidas. Si ni la última cabe sola, se queda con su encargo y
103
+ * lo último que dijo el especialista, que es lo que contó que había hecho.
104
+ *
105
+ * Existe porque en la primera prueba larga «olvidada-por-tope» llegó a borrar la memoria de los cuatro
106
+ * especialistas que más trabajaban, incluido lo reciente: perder lo viejo es aceptable, perder lo que acaba de
107
+ * pasar no. Puro.
108
+ */
109
+ export function ventanaDeHistorial(mensajes, topeTokens) {
110
+ const ms = mensajes;
111
+ const inicios = [];
112
+ ms.forEach((m, i) => {
113
+ if (m.role === "user")
114
+ inicios.push(i);
115
+ });
116
+ if (inicios.length === 0)
117
+ return [...mensajes];
118
+ for (let k = 0; k < inicios.length; k += 1) {
119
+ const cola = ms.slice(inicios[k]);
120
+ if (tokensDe(cola) <= topeTokens)
121
+ return [...cola];
122
+ }
123
+ // Ni la última delegación cabe sola: su encargo y lo último que dijo.
124
+ const ultima = ms.slice(inicios[inicios.length - 1]);
125
+ const dichas = ultima.filter((m) => m.role === "assistant" && typeof m.content === "string" && m.content.trim() !== "");
126
+ const cierre = dichas[dichas.length - 1];
127
+ return cierre === undefined ? [ultima[0]] : [ultima[0], cierre];
128
+ }
129
+ export function crearMemoriaDeEspecialistas(topes = {}) {
130
+ const completa = topes.completa ?? TOPE_COMPLETA_TOKENS;
131
+ const reducida = topes.reducida ?? TOPE_REDUCIDA_TOKENS;
132
+ const guardada = new Map();
133
+ const vivos = new Map(); // hilo → nombre
134
+ const perdidas = new Map(); // por qué no hay memoria de `nombre`
135
+ const hayVivo = (nombre) => [...vivos.values()].includes(nombre);
136
+ return {
137
+ abrir(nombre, hilo) {
138
+ const vivo = hayVivo(nombre);
139
+ vivos.set(hilo, nombre);
140
+ if (vivo)
141
+ return { sin: "hilo-vivo" };
142
+ const previa = guardada.get(nombre);
143
+ if (previa !== undefined)
144
+ return { previa };
145
+ return { sin: perdidas.get(nombre) ?? "primera" };
146
+ },
147
+ cerrar(hilo, { bien, mensajes, tokens }) {
148
+ const nombre = vivos.get(hilo);
149
+ if (nombre === undefined)
150
+ return "primera";
151
+ vivos.delete(hilo);
152
+ if (!bien) {
153
+ guardada.delete(nombre);
154
+ perdidas.set(nombre, "olvidada-por-fallo");
155
+ return "olvidada-por-fallo";
156
+ }
157
+ const saneados = saldarColgadas(mensajes);
158
+ if (tokens <= completa) {
159
+ guardada.set(nombre, { mensajes: saneados, tokens });
160
+ perdidas.delete(nombre);
161
+ return "guardada";
162
+ }
163
+ const recortados = reducirHistorial(saneados);
164
+ const tokensReducidos = tokensDe(recortados);
165
+ if (tokensReducidos <= reducida) {
166
+ guardada.set(nombre, { mensajes: recortados, tokens: tokensReducidos });
167
+ perdidas.delete(nombre);
168
+ return "guardada-reducida";
169
+ }
170
+ // Ni reducido cabe: la ventana con lo más reciente, en vez de olvidarlo todo.
171
+ const ventana = ventanaDeHistorial(recortados, reducida);
172
+ guardada.set(nombre, { mensajes: saldarColgadas(ventana), tokens: tokensDe(ventana) });
173
+ perdidas.delete(nombre);
174
+ return "guardada-ventana";
175
+ },
176
+ darPorMuertos() {
177
+ vivos.clear();
178
+ },
179
+ olvidar() {
180
+ guardada.clear();
181
+ vivos.clear();
182
+ perdidas.clear();
183
+ },
184
+ };
185
+ }
186
+ //# sourceMappingURL=memoriaDeEspecialistas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoriaDeEspecialistas.js","sourceRoot":"","sources":["../../../../src/agent/motores/trueforge/memoriaDeEspecialistas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Un resumen de contexto VACÍO no puede sustituir a la conversación.
3
+ *
4
+ * La compactación de TrueForge (`ContextCompaction.mjs`) pide un resumen al modelo y **sustituye el
5
+ * contexto ENTERO** por su respuesta, sin comprobar que no venga vacía. Con una vacía el raíz se
6
+ * queda sin el encargo: en una sesión real el contexto acabó siendo `[{assistant: ""}, «resumen
7
+ * hecho»]`, y el agente contestó «no tengo ningún encargo» con el encargo a tres mensajes de
8
+ * distancia. Peor, la memoria del hilo quedó así, y reenviar el mensaje no lo arreglaba.
9
+ *
10
+ * Aquí se envuelve el modelo SOLO para esa llamada: una respuesta vacía se reintenta una vez, y si
11
+ * sigue vacía se sustituye por los mensajes de la persona, tal como llegaron al resumen. No mejora
12
+ * un resumen malo; evita que el encargo se pierda.
13
+ *
14
+ * **Límite declarado**: los mensajes se recuperan del texto que la librería le da al modelo
15
+ * (`<n><user>:…`), un formato suyo. Si cambia, el rescate devuelve un aviso sin mensajes en vez de
16
+ * lanzar, y el test de este fichero lo delata.
17
+ */
18
+ const MENSAJE_DE_USUARIO = /<(\d+)><user>:([\s\S]*?)(?=<\d+><(?:user|assistant|tool-response)|$)/g;
19
+ /** Los mensajes de la persona del texto que la compactación le pasa al modelo, en orden. */
20
+ export function mensajesDeLaPersona(candidato) {
21
+ return [...candidato.matchAll(MENSAJE_DE_USUARIO)].map((m) => (m[2] ?? "").trim()).filter((t) => t !== "");
22
+ }
23
+ export function resumenDeRescate(candidato) {
24
+ const mensajes = mensajesDeLaPersona(candidato);
25
+ return [
26
+ "RESUMEN AUTOMÁTICO: el modelo devolvió un resumen vacío, así que se conservan los mensajes de la persona tal como llegaron.",
27
+ "El último es el encargo en curso; no digas que no hay encargo.",
28
+ ...(mensajes.length === 0 ? ["(no se pudieron recuperar los mensajes)"] : mensajes.map((t, i) => `Mensaje ${String(i + 1)} de la persona:\n${t}`)),
29
+ ].join("\n\n");
30
+ }
31
+ const vacio = (r) => typeof r.output.content !== "string" || r.output.content.trim() === "";
32
+ /** El cliente con la compactación protegida. Todo lo demás del cliente pasa igual. */
33
+ export function conResumenSeguro(cliente, avisar) {
34
+ return {
35
+ ...cliente,
36
+ createNonStream: async (cuerpo) => {
37
+ let r = (await cliente.createNonStream(cuerpo));
38
+ if (vacio(r)) {
39
+ avisar?.("compactación: resumen vacío, se reintenta");
40
+ r = (await cliente.createNonStream(cuerpo));
41
+ }
42
+ if (vacio(r)) {
43
+ avisar?.("compactación: resumen vacío otra vez, se conservan los mensajes de la persona");
44
+ const mensajes = cuerpo.messages ?? [];
45
+ const candidato = typeof mensajes[0]?.content === "string" ? mensajes[0].content : "";
46
+ r = { ...r, output: { ...r.output, content: resumenDeRescate(candidato) } };
47
+ }
48
+ return r;
49
+ },
50
+ };
51
+ }
52
+ //# sourceMappingURL=resumenSeguro.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resumenSeguro.js","sourceRoot":"","sources":["../../../../src/agent/motores/trueforge/resumenSeguro.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAYH,MAAM,kBAAkB,GAAG,uEAAuE,CAAC;AAEnG,4FAA4F;AAC5F,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,OAAO,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;AAC7G,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAChD,OAAO;QACL,6HAA6H;QAC7H,gEAAgE;QAChE,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,yCAAyC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,WAAW,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC;KACnJ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACjB,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,CAAqB,EAAW,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAEzH,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAA6B,OAAU,EAAE,MAAgC;IACvG,OAAO;QACL,GAAG,OAAO;QACV,eAAe,EAAE,KAAK,EAAE,MAAa,EAAE,EAAE;YACvC,IAAI,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAkC,CAAC;YACjF,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBACb,MAAM,EAAE,CAAC,2CAA2C,CAAC,CAAC;gBACtD,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAkC,CAAC;YAC/E,CAAC;YACD,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBACb,MAAM,EAAE,CAAC,+EAA+E,CAAC,CAAC;gBAC1F,MAAM,QAAQ,GAAI,MAAsD,CAAC,QAAQ,IAAI,EAAE,CAAC;gBACxF,MAAM,SAAS,GAAG,OAAO,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtF,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;YAC9E,CAAC;YACD,OAAO,CAAU,CAAC;QACpB,CAAC;KACG,CAAC;AACT,CAAC"}