xonecode 0.20.1 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/apps/web/dist/assets/index-CFoqpKRk.css +1 -0
  2. package/apps/web/dist/assets/index-DgvLb2SM.js +486 -0
  3. package/apps/web/dist/assets/{php-Dk28VW45.js → php-UkjSyds-.js} +1 -1
  4. package/apps/web/dist/assets/{ruby-CPzEcmdp.js → ruby-e801JuzW.js} +1 -1
  5. package/apps/web/dist/index.html +2 -2
  6. package/dist/agent/conectores/servicioDeConectores.js +86 -45
  7. package/dist/agent/conectores/servicioDeConectores.js.map +1 -1
  8. package/dist/agent/config/modelos.js +4 -0
  9. package/dist/agent/config/modelos.js.map +1 -1
  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/proyecto.js +91 -50
  15. package/dist/agent/grafo/proyecto.js.map +1 -1
  16. package/dist/agent/grafo/xoneAgent.js +4 -4
  17. package/dist/agent/grafo/xoneAgent.js.map +1 -1
  18. package/dist/agent/motores/trueforge/capacidades.js +22 -0
  19. package/dist/agent/motores/trueforge/capacidades.js.map +1 -1
  20. package/dist/agent/motores/trueforge/medidaAutomatica.js +16 -3
  21. package/dist/agent/motores/trueforge/medidaAutomatica.js.map +1 -1
  22. package/dist/agent/motores/trueforge/modeloLangchain.js +7 -1
  23. package/dist/agent/motores/trueforge/modeloLangchain.js.map +1 -1
  24. package/dist/agent/motores/trueforge/sesionTrueforge.js +163 -18
  25. package/dist/agent/motores/trueforge/sesionTrueforge.js.map +1 -1
  26. package/dist/agent/motores/trueforge/toolsDeConectores.js +128 -0
  27. package/dist/agent/motores/trueforge/toolsDeConectores.js.map +1 -0
  28. package/dist/agent/motores/trueforge/toolsDeFichero.js +8 -1
  29. package/dist/agent/motores/trueforge/toolsDeFichero.js.map +1 -1
  30. package/dist/agent/motores/trueforge/traerDeStitch.js +142 -0
  31. package/dist/agent/motores/trueforge/traerDeStitch.js.map +1 -0
  32. package/dist/agent/subagentes/agentesEnDisco.js +10 -6
  33. package/dist/agent/subagentes/agentesEnDisco.js.map +1 -1
  34. package/dist/agent/subagentes/consumoExterno.js +15 -8
  35. package/dist/agent/subagentes/consumoExterno.js.map +1 -1
  36. package/dist/agent/subagentes/escrituraExterna.js +69 -2
  37. package/dist/agent/subagentes/escrituraExterna.js.map +1 -1
  38. package/dist/agent/subagentes/subagenteExterno.js +17 -1
  39. package/dist/agent/subagentes/subagenteExterno.js.map +1 -1
  40. package/dist/agent/turno/resumenDeTool.js +2 -0
  41. package/dist/agent/turno/resumenDeTool.js.map +1 -1
  42. package/dist/agent/turno/turnoReal.js +1 -0
  43. package/dist/agent/turno/turnoReal.js.map +1 -1
  44. package/dist/cli/main.js +5 -2
  45. package/dist/cli/main.js.map +1 -1
  46. package/dist/core/actos.js +44 -1
  47. package/dist/core/actos.js.map +1 -1
  48. package/dist/core/adjuntos.js +2 -3
  49. package/dist/core/adjuntos.js.map +1 -1
  50. package/dist/core/agentes.js +26 -2
  51. package/dist/core/agentes.js.map +1 -1
  52. package/dist/core/comandoExterno.js +69 -0
  53. package/dist/core/comandoExterno.js.map +1 -0
  54. package/dist/core/compararCapturas.js +1 -1
  55. package/dist/core/conectores.js +148 -3
  56. package/dist/core/conectores.js.map +1 -1
  57. package/dist/core/motor.js +5 -0
  58. package/dist/core/motor.js.map +1 -1
  59. package/dist/core/ports.js +28 -0
  60. package/dist/core/ports.js.map +1 -1
  61. package/dist/core/stitch.js +114 -0
  62. package/dist/core/stitch.js.map +1 -0
  63. package/dist/core/tareasDelPlan.js +1 -1
  64. package/dist/core/tareasDelPlan.js.map +1 -1
  65. package/dist/web/servidor/arranque.js +47 -9
  66. package/dist/web/servidor/arranque.js.map +1 -1
  67. package/dist/web/servidor/consolaDeTarea.js +4 -1
  68. package/dist/web/servidor/consolaDeTarea.js.map +1 -1
  69. package/dist/web/servidor/consolaWeb.js +10 -2
  70. package/dist/web/servidor/consolaWeb.js.map +1 -1
  71. package/dist/web/servidor/pielWeb.js +16 -5
  72. package/dist/web/servidor/pielWeb.js.map +1 -1
  73. package/dist/web/servidor/transporte.js.map +1 -1
  74. package/package.json +1 -1
  75. package/apps/web/dist/assets/index-BvPUqjUm.js +0 -485
  76. package/apps/web/dist/assets/index-CGEXMJBx.css +0 -1
@@ -33,6 +33,7 @@ import { TOPE_DE_LLAMADAS_DEL_CONDUCTOR, TOPE_DE_LLAMADAS_DEL_ESPECIALISTA, UMBR
33
33
  import { crearSubagenteExterno } from "../../subagentes/subagenteExterno.js";
34
34
  import { inventarioDelProyecto, opcionesDeSubagenteExterno } from "../../subagentes/escrituraExterna.js";
35
35
  import { sumarConsumo, SIN_CONSUMO } from "../../subagentes/consumoExterno.js";
36
+ import { sumarPorModelo } from "../../../core/actos.js";
36
37
  import { ColaDeEventos, entrelazar } from "../../../core/entrelazar.js";
37
38
  import { modeloExternoParaTrueforge } from "./modeloExterno.js";
38
39
  import { diferenciasDelContraste, metricasDeTrueforge } from "./metricasTrueforge.js";
@@ -40,9 +41,10 @@ import { cambiosQueSeVerifican, huellaDeErrores, repartirHallazgos, textoDeRepar
40
41
  import { correrTurno } from "../../../core/turno.js";
41
42
  import { createTokenTracker } from "../../../vendor/tokenTracking.js";
42
43
  import { MAX_APPROVAL_ROUNDS } from "../../../vendor/hitl.js";
43
- import { backendDeAgente, entornoDeLaShellDelProyecto } from "../../grafo/proyecto.js";
44
+ import { artefactosNuevos, backendDeAgente, entornoDeLaShellDelProyecto, fotoDeArtefactos, scriptsDeLasSkills } from "../../grafo/proyecto.js";
45
+ import { carpetaDeHotswap } from "../../../core/hotswap.js";
44
46
  import { cargarAgentes } from "../../subagentes/agentesEnDisco.js";
45
- import { fichaDeAgente, promptDeAgente, repartirSkills } from "../../../core/agentes.js";
47
+ import { fichaDeAgente, promptDeAgente, recibeMarcarCriterios, repartirSkills } from "../../../core/agentes.js";
46
48
  import { PERFIL_DEL_ORQUESTADOR, promptOrquestador } from "../../grafo/xoneAgent.js";
47
49
  import { permisosDe, seDetieneEn, TEXTO_HITL } from "../../grafo/perfiles.js";
48
50
  import { cambioDe } from "../../turno/interrupts.js";
@@ -53,7 +55,11 @@ import { modeloParaTrueforge } from "./modeloLangchain.js";
53
55
  import { TOOLS_DE_LECTURA } from "./toolsDeFichero.js";
54
56
  import { topeAgotadoDe, traducirEvento } from "./eventosTrueforge.js";
55
57
  import { anuncioDeSkills } from "./skillsTrueforge.js";
56
- import { capabilitiesDe, capacidadDeFecha, capacidadDeFicheros, capacidadDeAvisoDeVueltas, capacidadDeInstrucciones, capacidadDeNotas, capacidadDePropias, capacidadDeRecortes, capacidadesDelEspecialista, clasesDeTools, toolsDe, } from "./capacidades.js";
58
+ import { capabilitiesDe, capacidadDeFecha, capacidadDeFicheros, capacidadDeAvisoDeVueltas, capacidadDeInstrucciones, capacidadDeNotas, capacidadDeConectores, capacidadDePropias, capacidadDeRecortes, capacidadesDelEspecialista, clasesDeTools, toolsDe, } from "./capacidades.js";
59
+ import { crearConectoresDeSesion, tarjetaDeRemota } from "./toolsDeConectores.js";
60
+ import { CONECTOR_STITCH, crearTraerDeStitch } from "./traerDeStitch.js";
61
+ import { esDeLaMaqueta } from "../../../core/stitch.js";
62
+ import { recibeConectores } from "../../../core/conectores.js";
57
63
  import { buscarMaqueta, medirContraMaqueta, resumenDeMedida, textoDeMedidaAutomatica, ultimaCaptura } from "./medidaAutomatica.js";
58
64
  import { anotarEscritura, capacidadDeInformesDeHijos, escrituraConExito, textoDelInforme } from "./informesDeHijos.js";
59
65
  import { crearDiagnosticoDeTools } from "../../turno/diagnosticoDeTools.js";
@@ -82,6 +88,7 @@ import { crearCompararCapturas } from "../../grafo/compararCapturas.js";
82
88
  import { crearAtributosXone, NOMBRE_ATRIBUTOS_XONE } from "../../grafo/atributosXone.js";
83
89
  import { crearDiferenciaDeCapturas } from "../../grafo/diferenciaDeCapturas.js";
84
90
  import { crearLectorDeReferencias } from "../../grafo/lectorDeReferencias.js";
91
+ import { crearDescribirImagen } from "../../grafo/describirImagen.js";
85
92
  import { crearTraerDeLaMaquina } from "../../grafo/traerDeLaMaquina.js";
86
93
  import { invocarVisualConModelos } from "../../dispositivos/juezVisual.js";
87
94
  import { estilosDeDisco, indiceEnDisco } from "../../navegacion/indiceEnDisco.js";
@@ -112,15 +119,33 @@ const BUCLE_DE_CALIDAD = [
112
119
  * el nombre de la tool y las fichas van escritas aquí, con la MISMA función que deepagents.
113
120
  */
114
121
  /**
115
- * **El idioma, dicho desde código**, al raíz y a cada especialista. Visto en la consola web (MyAllXOne): el texto entre
116
- * herramientas y el razonamiento salían en inglés («Let me call…», «I'll start by reconnoitering…») con la persona
117
- * escribiendo en español. Nuestros prompts están en español pero ninguno lo decía, y encima hay piezas internas en inglés
118
- * que tiran de él: la identidad que añade TrueForge («You are the Agent…»), las descripciones de las tools y la costumbre
119
- * de DeepSeek de razonar en inglés.
122
+ * **El idioma de la SALIDA, dicho desde código**, al raíz y a cada especialista. Visto en la consola web (MyAllXOne):
123
+ * el texto entre herramientas salía en inglés («Let me call…») con la persona escribiendo en español: nuestros prompts
124
+ * están en español pero ninguno lo decía, y hay piezas internas en inglés que tiran de él (la identidad que añade
125
+ * TrueForge, las descripciones de las tools, la costumbre de DeepSeek de razonar en inglés).
126
+ *
127
+ * **Lo que importa es lo que LEE la persona, en SU idioma** (decisión suya): las respuestas, lo que se dice entre
128
+ * herramientas y lo que se entrega. El razonamiento queda libre. Y no es «español» a fuego, es el idioma del usuario: el
129
+ * raíz lo lee de su mensaje; un hijo no lo ve, así que sigue el de su encargo, que el raíz escribe en ese idioma.
120
130
  */
121
- export const IDIOMA_DE_LA_RESPUESTA = "IDIOMA: escribe SIEMPRE en español —lo que dices entre herramientas, tu razonamiento, tus encargos y tus respuestas—, " +
122
- "aunque algunas instrucciones internas y las descripciones de las herramientas estén en inglés. El código, los nombres de " +
131
+ export const IDIOMA_DE_LA_RESPUESTA = "IDIOMA: contesta en el idioma en que te escribe el usuario (el de su último mensaje): tus respuestas y lo que dices " +
132
+ "entre herramientas, que es lo que él lee. Los encargos a tus especialistas, escríbelos en ese mismo idioma. Tu " +
133
+ "razonamiento puede ir en el idioma que quieras. El código, los nombres de ficheros, atributos y funciones, y las citas " +
134
+ "literales se quedan como son.";
135
+ /** Lo mismo para un ESPECIALISTA, que no ve el mensaje del usuario: sigue el idioma de su encargo. */
136
+ export const IDIOMA_DEL_ESPECIALISTA = "IDIOMA: lo que devuelves y lo que entregas (un documento, un texto de la app, un informe) va en el idioma de tu " +
137
+ "encargo, que es el del usuario. Tu razonamiento puede ir en el idioma que quieras. El código, los nombres de " +
123
138
  "ficheros, atributos y funciones, y las citas literales se quedan como son.";
139
+ /**
140
+ * **Al desarrollador no se le cuenta la MAQUINARIA**, solo al raíz, que es quien le habla. Visto en la consola web: al
141
+ * saludar, el raíz explicaba que tenía «la foto del proyecto», que no la iba a «re-escanear», que el especialista lo
142
+ * llevaría «dentro del HANDOFF DE ANÁLISIS» y que usaría `xone_navegacion`. Es cómo trabaja el harness por dentro, no
143
+ * algo que le sirva a quien pide: petición suya. A los hijos no va, porque le hablan al raíz y no a la persona.
144
+ */
145
+ export const SIN_HABLAR_DEL_HARNESS = "CON QUIÉN HABLAS: con un desarrollador de apps XOne, al que le interesa su proyecto y su encargo, no cómo trabajas por " +
146
+ "dentro. No le nombres tus herramientas, a tus especialistas por su nombre interno, los protocolos entre ellos (HANDOFF " +
147
+ "DE ANÁLISIS…), la foto o el contexto que te llega al empezar, ni lo que vas a medir o no: cuéntale lo que encontraste, " +
148
+ "lo que hiciste en su proyecto y lo que necesitas de él. Si pregunta cómo trabajas, entonces sí.";
124
149
  /** Lo que recibe el agente en modo autónomo cuando pregunta sin marcar ninguna opción como recomendada. */
125
150
  export const RESPUESTA_AUTONOMA_SIN_RECOMENDADA = "Modo autónomo: no hay nadie a quien preguntar. Decide tú con lo que dicen el encargo y el plan, sigue, y di en tu respuesta final qué elegiste y por qué.";
126
151
  /** Un comando que saca una captura de pantalla del aparato. */
@@ -469,7 +494,9 @@ export async function abrirSesionTrueforge(opciones) {
469
494
  let capturasDelTurno = [];
470
495
  const anotarArtefacto = (a) => {
471
496
  artefactosPorAnunciar.push(a);
472
- if (a.mime !== undefined && a.mime.startsWith("image/"))
497
+ // La MAQUETA traída (`/artefactos/diseno/`) no es una captura del aparato: el crítico de pantalla
498
+ // la juzgaría como si fuera la app.
499
+ if (a.mime !== undefined && a.mime.startsWith("image/") && !esDeLaMaqueta(a.ruta))
473
500
  capturasDelTurno.push(a);
474
501
  };
475
502
  /** Lo que la persona escribió mientras el agente trabajaba, IXCODE-4: se entrega por
@@ -509,6 +536,17 @@ export async function abrirSesionTrueforge(opciones) {
509
536
  */
510
537
  const eventosExternos = new ColaDeEventos();
511
538
  let consumoExterno = SIN_CONSUMO;
539
+ /**
540
+ * El consumo POR MODELO (`ConsumoPorModelo`): un especialista puede correr en otro modelo que el
541
+ * raíz. Cada hilo apunta al id con que NACIÓ (`modeloDeHilo`); el raíz, al de su papel AHORA
542
+ * (`/modelo` lo cambia). Suma lo mismo que `tracker` y `consumoExterno`, repartido.
543
+ */
544
+ let porModelo = {};
545
+ const modeloDeHilo = new Map();
546
+ const idDelPapel = (papel) => modelos.idDePapel?.(papel) ?? `papel ${papel}`;
547
+ const apuntarModelo = (id, cuenta, c) => {
548
+ porModelo = sumarPorModelo(porModelo, { [id]: { cuenta, ...c } });
549
+ };
512
550
  let apuntarAplicadasSinPreguntar;
513
551
  const externo = (opciones.subagenteExterno ?? crearSubagenteExterno)(opcionesDeSubagenteExterno({
514
552
  ...(opciones.pedirAprobacion === undefined ? {} : { pedirAprobacion: opciones.pedirAprobacion }),
@@ -516,6 +554,13 @@ export async function abrirSesionTrueforge(opciones) {
516
554
  eventos: eventosExternos,
517
555
  alConsumir: (c) => {
518
556
  consumoExterno = sumarConsumo(consumoExterno, c);
557
+ // Claude Code dice su gasto por modelo; los otros dos, solo por motor.
558
+ if (c.porModelo !== undefined && Object.keys(c.porModelo).length > 0) {
559
+ for (const [id, m] of Object.entries(c.porModelo))
560
+ apuntarModelo(`${c.motor}:${id}`, "externo", m);
561
+ }
562
+ else
563
+ apuntarModelo(c.motor, "externo", c);
519
564
  avisar();
520
565
  },
521
566
  modo: {
@@ -582,16 +627,49 @@ export async function abrirSesionTrueforge(opciones) {
582
627
  ];
583
628
  // ¿Existe este atributo en este nodo? A todos, como la navegación: es de lectura y contesta desde la skill del paquete.
584
629
  const atributos = crearAtributosXone();
630
+ // VER una imagen (IXCODE-23), a TODOS, el raíz incluido: es de lectura, y el raíz es quien recibe los adjuntos.
631
+ // `read_file` no abre una imagen y el aviso de adjuntos manda aquí (`describirImagen.ts`).
632
+ const describirImagen = () => crearDescribirImagen({
633
+ invocar: invocarVisualConModelos({ paraPapel: (p) => modelos.paraPapel(p) }),
634
+ leer: lectorDeReferencias,
635
+ carpetas: {
636
+ raiz,
637
+ ...(carpeta === undefined ? {} : { artefactos: carpeta }),
638
+ ...(opciones.adjuntos === undefined ? {} : { adjuntos: opciones.adjuntos }),
639
+ },
640
+ });
641
+ /**
642
+ * Los conectores MCP del proyecto, una vez por sesión: se leen los marcados AHORA y sus tools se
643
+ * piden a la red al primer uso (`toolsDeConectores.ts`). El raíz recibe las de LECTURA; un
644
+ * especialista, las dos clases si `recibeConectores`.
645
+ */
646
+ const delProyecto = opciones.conectores?.delProyecto(raiz) ?? [];
647
+ const deConectores = opciones.conectores === undefined || delProyecto.length === 0 ? undefined : crearConectoresDeSesion(opciones.conectores, delProyecto);
648
+ const conStitch = delProyecto.some((c) => c.id === CONECTOR_STITCH);
649
+ const conectoresDe = (agente) => deConectores === undefined || !recibeConectores(agente)
650
+ ? []
651
+ : [capacidadDeConectores(deConectores, "lectura", backend), capacidadDeConectores(deConectores, "escritura", backend)];
652
+ /**
653
+ * `traer_pantalla_de_stitch` (`traerDeStitch.ts`), solo si la sesión tiene Stitch entre sus
654
+ * conectores y carpeta de artefactos donde dejar la maqueta. La reciben el raíz y quien recibe
655
+ * los conectores: quien pide la pantalla y quien la construye a partir de ella.
656
+ */
657
+ const traerDeStitch = () => opciones.conectores !== undefined && carpeta !== undefined && conStitch
658
+ ? [crearTraerDeStitch({ conectores: opciones.conectores, carpeta, alEscribir: anotarArtefacto })]
659
+ : [];
585
660
  const propiasDelRaiz = [
586
661
  navegacion(),
587
662
  atributos,
663
+ describirImagen(),
588
664
  ...herramientasDeJuicio(),
589
665
  ...(carpeta === undefined ? [] : [crearTraerDeLaMaquina({ carpeta, alEscribir: anotarArtefacto })]),
666
+ ...traerDeStitch(),
590
667
  ];
591
668
  const propiasDe = (agente) => [
592
669
  crearBusquedaRegex(backend),
593
670
  navegacion(),
594
671
  atributos,
672
+ describirImagen(),
595
673
  ...(carpeta !== undefined && (agente.escribeEn ?? []).length > 0
596
674
  ? [crearCopiarArtefacto({ raiz, carpetaDeArtefactos: carpeta, perfil: agente })]
597
675
  : []),
@@ -605,6 +683,7 @@ export async function abrirSesionTrueforge(opciones) {
605
683
  : []),
606
684
  // Buscar un icono cuando faltan los assets (IXCODE-18), con el reparto de deepagents: a quien
607
685
  // escribe el proyecto y solo con el puerto. Es de LECTURA: no entra en `requireApprovalForTools`.
686
+ ...(recibeConectores(agente) ? traerDeStitch() : []),
608
687
  ...(opciones.iconos !== undefined && recibeBuscarIcono(agente)
609
688
  ? [crearBuscarIcono(opciones.iconos)]
610
689
  : []),
@@ -615,8 +694,8 @@ export async function abrirSesionTrueforge(opciones) {
615
694
  ...(carpeta !== undefined && agente.ejecucion === true
616
695
  ? [crearDiferenciaDeCapturas({ leerArtefacto: async (nombre) => readFileSync(join(carpeta, nombre)) })]
617
696
  : []),
618
- // Y marcar en el plan lo comprobado en el aparato, con el reparto de deepagents: a quien ejecuta.
619
- ...(agente.ejecucion === true ? [crearMarcarCriteriosDelPlan({ raiz })] : []),
697
+ // Y marcar en el plan lo comprobado, con el reparto de deepagents (`recibeMarcarCriterios`).
698
+ ...(recibeMarcarCriterios(agente) ? [crearMarcarCriteriosDelPlan({ raiz })] : []),
620
699
  // El BUCLE del desarrollador: quien puede llamar al de pruebas también tiene el crítico y la medida, para
621
700
  // juzgar la pantalla con lo que el otro capture. El veredicto sigue siendo de un modelo aparte, no suyo, y el
622
701
  // juez final del arnés revisa igual (`textoDelBucle`).
@@ -662,10 +741,48 @@ export async function abrirSesionTrueforge(opciones) {
662
741
  hilosExternos.add(params.threadId);
663
742
  externosDelTurno += 1;
664
743
  const motor = agente.motor;
744
+ /**
745
+ * La EJECUCIÓN de un externo: solo Claude Code, solo con `ejecucion: true` en su `.md` y con
746
+ * carpeta de artefactos donde dejar lo que saque (`core/comandoExterno.ts`). Sus scripts, por
747
+ * su nombre; el entorno de NUESTRA shell; y la lectura de ESTAS dos carpetas de la sesión, que
748
+ * es donde sus scripts dejan las capturas. Codex y OpenCode siguen sin shell.
749
+ */
750
+ const ejecucion = agente.ejecucion === true && motor === "claude-code" && carpeta !== undefined
751
+ ? {
752
+ scripts: scriptsDeLasSkills(raiz, agente.skills),
753
+ entorno: entornoDeLaShellDelProyecto(raiz, carpeta),
754
+ lecturas: [carpeta, carpetaDeHotswap(carpeta)],
755
+ }
756
+ : undefined;
757
+ /**
758
+ * Lo que sus comandos dejen en `/artefactos/` se ANUNCIA al volver, con la MISMA foto que nuestra
759
+ * shell (`fotoDeArtefactos`): por nuestra shell se anunciaba y por la suya no, y una captura
760
+ * que no se anuncia no la mide nadie.
761
+ */
762
+ const puertoDelHijo = ejecucion === undefined || carpeta === undefined
763
+ ? externo
764
+ : {
765
+ disponible: (m) => externo.disponible(m),
766
+ correr: async (peticion) => {
767
+ const antes = fotoDeArtefactos(carpeta);
768
+ try {
769
+ return await externo.correr(peticion);
770
+ }
771
+ finally {
772
+ try {
773
+ for (const a of artefactosNuevos(antes, fotoDeArtefactos(carpeta)))
774
+ anotarArtefacto(a);
775
+ }
776
+ catch {
777
+ // Un artefacto que no se pudo anunciar no invalida lo que hizo el hijo.
778
+ }
779
+ }
780
+ },
781
+ };
665
782
  return new AgentThread({
666
783
  definition: {
667
784
  modelClient: modeloExternoParaTrueforge({
668
- puerto: externo,
785
+ puerto: puertoDelHijo,
669
786
  peticion: () => ({
670
787
  motor,
671
788
  cwd: raiz,
@@ -674,6 +791,7 @@ export async function abrirSesionTrueforge(opciones) {
674
791
  ...(agente.modelo === undefined ? {} : { modelo: agente.modelo }),
675
792
  permitirEscritura: !agente.soloLectura,
676
793
  agente: agente.nombre,
794
+ ...(ejecucion === undefined ? {} : { ejecucion }),
677
795
  }),
678
796
  senal: () => aborto?.signal,
679
797
  }),
@@ -738,6 +856,7 @@ export async function abrirSesionTrueforge(opciones) {
738
856
  const piezas = capacidadesDelEspecialista(agente, params.request.name, {
739
857
  backend: backend,
740
858
  propias: propiasDe,
859
+ conectores: conectoresDe,
741
860
  notas: capacidadDeNotasDeLaSesion,
742
861
  puedeLlamar: (a) => conBucleDelDesarrollador && (a.llama?.length ?? 0) > 0,
743
862
  informes: capacidadDeInformes,
@@ -760,9 +879,9 @@ export async function abrirSesionTrueforge(opciones) {
760
879
  ...(nombres.includes(NOMBRE_ATRIBUTOS_XONE) ? [USO_DE_XONE_ATRIBUTOS] : []),
761
880
  ].join("\n");
762
881
  const instrucciones = agente === undefined
763
- ? `${IDIOMA_DE_LA_RESPUESTA}\n\n${nota} Contesta con lo que encuentres y dónde.`
882
+ ? `${IDIOMA_DEL_ESPECIALISTA}\n\n${nota} Contesta con lo que encuentres y dónde.`
764
883
  : [
765
- IDIOMA_DE_LA_RESPUESTA,
884
+ IDIOMA_DEL_ESPECIALISTA,
766
885
  "",
767
886
  promptDeAgente(agente, repartirSkills(agente, disponibles)),
768
887
  "",
@@ -774,6 +893,7 @@ export async function abrirSesionTrueforge(opciones) {
774
893
  .join("\n")
775
894
  .trimEnd();
776
895
  const papel = agente?.soloLectura === true ? "rapido" : "trabajo";
896
+ modeloDeHilo.set(params.threadId, agente?.modelo ?? idDelPapel(papel));
777
897
  const claseDeEsfuerzo = agente === undefined ? undefined : claseDeTrabajo(agente);
778
898
  // La memoria del especialista: su conversación anterior de ESTA sesión, si la hay y se puede usar.
779
899
  const apertura = conMemoriaDeEspecialistas && agente !== undefined ? memoriaDeEspecialistas.abrir(agente.nombre, params.threadId) : undefined;
@@ -862,7 +982,7 @@ export async function abrirSesionTrueforge(opciones) {
862
982
  hijosConMemoria.clear();
863
983
  const definicion = {
864
984
  modelClient: llm,
865
- instruction: [IDIOMA_DE_LA_RESPUESTA, promptOrquestador(especialistas()), notaDeDelegacion(especialistas(), { conIconos: opciones.iconos !== undefined, conComparacion: carpeta !== undefined, conMemoria: conMemoriaDeEspecialistas, conBucle: conBucleDelDesarrollador, conEsperas })].filter((l) => l !== "").join("\n\n"),
985
+ instruction: [IDIOMA_DE_LA_RESPUESTA, SIN_HABLAR_DEL_HARNESS, promptOrquestador(especialistas()), notaDeDelegacion(especialistas(), { conIconos: opciones.iconos !== undefined, conComparacion: carpeta !== undefined, conMemoria: conMemoriaDeEspecialistas, conBucle: conBucleDelDesarrollador, conEsperas })].filter((l) => l !== "").join("\n\n"),
866
986
  // Por TURNO, porque el raíz se rehace desde su foto al final de cada uno (ver `turno`).
867
987
  iterationLimit: LIMITE_DE_LLAMADAS_DEL_RAIZ,
868
988
  };
@@ -876,6 +996,7 @@ export async function abrirSesionTrueforge(opciones) {
876
996
  ...capabilitiesDe([
877
997
  capacidadDeFicheros({ backend, reglas: permisosDe(PERFIL_DEL_ORQUESTADOR), tools: TOOLS_DE_LECTURA, conAprobacion: false }),
878
998
  capacidadDePropias(propiasDelRaiz, backend),
999
+ ...(deConectores === undefined ? [] : [capacidadDeConectores(deConectores, "lectura", backend)]),
879
1000
  capacidadDeRecortes(backend),
880
1001
  capacidadDeFecha(),
881
1002
  capacidadDeNotasDeLaSesion,
@@ -960,7 +1081,7 @@ export async function abrirSesionTrueforge(opciones) {
960
1081
  function medidaTrasProbar(deHilo, hiloPadre, quien) {
961
1082
  if (carpeta === undefined || quien?.ejecucion !== true)
962
1083
  return undefined;
963
- const maqueta = buscarMaqueta(raiz, opciones.adjuntos);
1084
+ const maqueta = buscarMaqueta(raiz, opciones.adjuntos, carpeta);
964
1085
  const captura = ultimaCaptura(carpeta, nacimientoDeHilo.get(deHilo) ?? Date.now());
965
1086
  if (maqueta === undefined || captura === undefined)
966
1087
  return undefined;
@@ -1092,6 +1213,11 @@ export async function abrirSesionTrueforge(opciones) {
1092
1213
  tracker.output += uso.output;
1093
1214
  tracker.cache += uso.cache;
1094
1215
  tracker.calls += 1;
1216
+ apuntarModelo(deHilo === HILO_RAIZ ? idDelPapel("trabajo") : (modeloDeHilo.get(deHilo) ?? idDelPapel("trabajo")), "modelo", {
1217
+ entrada: uso.input,
1218
+ salida: uso.output,
1219
+ cache: uso.cache,
1220
+ });
1095
1221
  // Y solo de una llamada NORMAL: la de la compactación mide lo de ANTES de resumir.
1096
1222
  if (deHilo === HILO_RAIZ && evento.type === "internal.agent.context.append")
1097
1223
  tracker.contexto = uso.input;
@@ -1244,6 +1370,22 @@ export async function abrirSesionTrueforge(opciones) {
1244
1370
  const diffs = new Map();
1245
1371
  const autonomo = opciones.sinAprobacion?.() === true;
1246
1372
  for (const p of pendientes) {
1373
+ // Una tool de un CONECTOR que escribe (generar en Stitch…) va SIEMPRE a una persona, también
1374
+ // en modo autónomo: el modo gobierna las escrituras LOCALES, como con `/sync subir`. Sin
1375
+ // fichero: la tarjeta enseña el conector, la tool y sus argumentos enteros.
1376
+ const remota = deConectores?.remotaDe(p.nombre);
1377
+ if (remota !== undefined) {
1378
+ const tarjeta = tarjetaDeRemota(remota, p.args);
1379
+ humanos.push({
1380
+ id: p.clave,
1381
+ origen: quienEs.get(p.hilo) ?? p.hilo,
1382
+ descripcion: tarjeta.descripcion,
1383
+ decisionesPermitidas: ["approve", "reject"],
1384
+ remota: true,
1385
+ });
1386
+ diffs.set(p.clave, tarjeta.lineas);
1387
+ continue;
1388
+ }
1247
1389
  const ruta = typeof p.args.file_path === "string" ? p.args.file_path : "";
1248
1390
  // Lo que NO es el proyecto —artefactos y planes— y lo que el backend va a rechazar de
1249
1391
  // todas formas no se pregunta: la MISMA función que el HITL de deepagents.
@@ -1634,6 +1776,7 @@ export async function abrirSesionTrueforge(opciones) {
1634
1776
  tracker.calls = 0;
1635
1777
  tracker.contexto = 0;
1636
1778
  consumoExterno = SIN_CONSUMO;
1779
+ porModelo = {};
1637
1780
  avisar();
1638
1781
  },
1639
1782
  cancelar() {
@@ -1666,6 +1809,8 @@ export async function abrirSesionTrueforge(opciones) {
1666
1809
  modelo: { entrada: tracker.input, salida: tracker.output, cache: tracker.cache },
1667
1810
  externo: consumoExterno,
1668
1811
  contexto: tracker.contexto,
1812
+ // Sin nada gastado no hay desglose: vacío no es «no consta», y un objeto vacío lo afirmaría.
1813
+ ...(Object.keys(porModelo).length === 0 ? {} : { porModelo: sumarPorModelo({}, porModelo) }),
1669
1814
  };
1670
1815
  },
1671
1816
  alCambiarConsumo(oyente) {