@decido/plugin-dev-console 1.0.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.
@@ -0,0 +1,13 @@
1
+
2
+ Debugger listening on ws://127.0.0.1:62233/8c9e1551-3f82-44ce-842b-b58a12921ab2
3
+ For help, see: https://nodejs.org/en/docs/inspector
4
+ Debugger attached.
5
+
6
+ > @decido/plugin-dev-console@1.0.0 build /Users/julioramirez/dev/active/OnBoardingDecido/plugins/official/dev-console
7
+ > tsc
8
+
9
+ Debugger listening on ws://127.0.0.1:62247/13f2e458-f583-4510-9bed-17f01f07ffc2
10
+ For help, see: https://nodejs.org/en/docs/inspector
11
+ Debugger attached.
12
+ Waiting for the debugger to disconnect...
13
+ Waiting for the debugger to disconnect...
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@decido/plugin-dev-console",
3
+ "version": "1.0.0",
4
+ "main": "src/index.ts",
5
+ "types": "src/index.ts",
6
+ "dependencies": {
7
+ "@xyflow/react": "^12.0.0",
8
+ "framer-motion": "^12.34.3",
9
+ "lucide-react": "^0.370.0",
10
+ "react-shadow": "^20.6.0",
11
+ "recharts": "^3.7.0",
12
+ "zustand": "^4.5.2"
13
+ },
14
+ "devDependencies": {
15
+ "@types/react": "^18.3.1",
16
+ "typescript": "^5.0.0",
17
+ "react": "^18.3.1",
18
+ "react-dom": "^18.3.1"
19
+ },
20
+ "peerDependencies": {
21
+ "@decido/kernel-bridge": "1.0.0",
22
+ "@decido/sdk": "1.0.0",
23
+ "@decido/shell": "1.0.0"
24
+ },
25
+ "license": "UNLICENSED",
26
+ "scripts": {
27
+ "dev": "tsup src/index.ts --format esm --watch",
28
+ "build": "tsup src/index.ts --format esm --dts --minify --clean"
29
+ }
30
+ }
@@ -0,0 +1,120 @@
1
+ import React, { Component, ErrorInfo, ReactNode, useState } from 'react';
2
+ import root from 'react-shadow';
3
+ import { kernel } from '@decido/kernel-bridge';
4
+
5
+ // 1. Error Boundary para atrapar errores de ejecución de la IA
6
+ interface ErrorProps {
7
+ children: ReactNode;
8
+ title: string;
9
+ onCrash?: (error: Error, errorInfo: React.ErrorInfo) => void;
10
+ }
11
+
12
+ class GenerativeErrorBoundary extends Component<ErrorProps, { hasError: boolean, error: any }> {
13
+ constructor(props: any) {
14
+ super(props);
15
+ this.state = { hasError: false, error: null };
16
+ }
17
+
18
+ static getDerivedStateFromError(error: any) {
19
+ return { hasError: true, error };
20
+ }
21
+
22
+ componentDidCatch(error: Error, errorInfo: ErrorInfo) {
23
+ console.error("Generative UI Crash:", error, errorInfo);
24
+
25
+ // Notificamos al motor del Playground que hubo un fallo
26
+ if (this.props.onCrash) {
27
+ this.props.onCrash(error, errorInfo);
28
+ }
29
+ }
30
+
31
+ render() {
32
+ if (this.state.hasError) {
33
+ return (
34
+ <div style={{ padding: '16px', background: 'rgba(100,0,0,0.3)', border: '1px solid rgba(255,0,0,0.5)', borderRadius: '8px', color: '#ffb3b3', fontFamily: 'monospace' }}>
35
+ <h3 style={{ fontWeight: 'bold', display: 'flex', alignItems: 'center', gap: '8px', margin: 0 }}>
36
+ ⚠️ Fallo Crítico en Interfaz Generada
37
+ </h3>
38
+ <p style={{ fontSize: '12px', marginTop: '8px', opacity: 0.8 }}>{this.state.error?.message}</p>
39
+ <button
40
+ onClick={() => this.setState({ hasError: false })}
41
+ style={{ marginTop: '12px', fontSize: '12px', background: 'rgba(255,0,0,0.2)', border: 'none', color: '#fff', cursor: 'pointer', padding: '4px 8px', borderRadius: '4px' }}
42
+ >
43
+ Intentar Reiniciar
44
+ </button>
45
+ </div>
46
+ );
47
+ }
48
+ return this.props.children;
49
+ }
50
+ }
51
+
52
+ // 2. Wrapper con Shadow DOM
53
+ const ShadowDiv = root.div;
54
+
55
+ export const SafeZone = ({ children, title, rawCode, onCrash }: { children: ReactNode, title: string, rawCode?: string, onCrash?: (error: Error, errInfo: any) => void }) => {
56
+ const [isMaterializing, setIsMaterializing] = useState(false);
57
+
58
+ const handleMaterialize = async () => {
59
+ if (!rawCode) return;
60
+ const name = prompt("Nombre del componente (ej: InventoryChart):", title.replace(/[^a-zA-Z0-9]/g, ''));
61
+ if (!name) return;
62
+
63
+ setIsMaterializing(true);
64
+ try {
65
+ const filePath = await kernel.execute('materialize_construct', {
66
+ pluginId: 'madefront-ai',
67
+ name,
68
+ code: rawCode
69
+ });
70
+
71
+ kernel.notify('💎 Materialización Exitosa', `Archivo creado en: ${filePath}`);
72
+ kernel.execute('emit_xp', { label: `Materializado: ${name}`, xp: 250 });
73
+ } catch (e: any) {
74
+ kernel.notify('❌ Error', 'No se pudo escribir en el disco el constructo. ' + e.toString());
75
+ } finally {
76
+ setIsMaterializing(false);
77
+ }
78
+ };
79
+
80
+ return (
81
+ <div className="group relative h-full w-full overflow-hidden">
82
+ {isMaterializing && (
83
+ <div className="absolute inset-0 z-[100] bg-cyan-500/20 animate-pulse border-2 border-cyan-400 pointer-events-none">
84
+ <div className="h-1 bg-cyan-400 w-full absolute top-0 animate-scan"></div>
85
+ </div>
86
+ )}
87
+ {/* Botón Flotante de Guardado (Construct Marketplace) */}
88
+ {rawCode && (
89
+ <button
90
+ onClick={handleMaterialize}
91
+ className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 z-[60] bg-emerald-500 text-black px-3 py-1 rounded-full text-[10px] font-bold shadow-lg transition-all hover:scale-110"
92
+ >
93
+ MATERIALIZAR CONSTRUCTO
94
+ </button>
95
+ )}
96
+
97
+ <GenerativeErrorBoundary title={title} onCrash={onCrash}>
98
+ {/*
99
+ Usamos 'react-shadow' para crear un Shadow Root.
100
+ Esto evita que el CSS del componente generado afecte al resto de Decido-OS
101
+ */}
102
+ <ShadowDiv style={{ height: '100%', width: '100%', display: 'block' }}>
103
+ {/*
104
+ IMPORTANTE: Inyectamos Tailwind en el Shadow DOM.
105
+ Podemos usar el CDN para runtime total.
106
+ */}
107
+ <link rel="stylesheet" href="https://cdn.tailwindcss.com" />
108
+ <style>{`
109
+ :host { display: block; height: 100%; width: 100%; }
110
+ .generative-content { all: initial; font-family: sans-serif; }
111
+ `}</style>
112
+
113
+ <div className="generative-content" style={{ height: '100%', width: '100%' }}>
114
+ {children}
115
+ </div>
116
+ </ShadowDiv>
117
+ </GenerativeErrorBoundary>
118
+ </div>
119
+ );
120
+ };
@@ -0,0 +1,5 @@
1
+ declare module 'react-shadow' {
2
+ export const div: any;
3
+ const root: any;
4
+ export default root;
5
+ }
@@ -0,0 +1,39 @@
1
+ import React, { useMemo } from 'react';
2
+
3
+ export const CodeSnippet = ({ code }: { code: string }) => {
4
+ // Memoized regex-based highlighter to prevent re-renders degradation
5
+ const highlightedCode = useMemo(() => {
6
+ return code.split('\n').map((line, i) => {
7
+ let highlightedLine = line
8
+ .replace(/</g, '&lt;')
9
+ .replace(/>/g, '&gt;')
10
+ .replace(/('.*?'|".*?"|`.*?`)/g, '<span class="text-emerald-400">$1</span>')
11
+ .replace(/(import|from|const|await|async|let|var|function|return|if|else)\b/g, '<span class="text-purple-400">$1</span>')
12
+ .replace(/\b([A-Z][a-zA-Z0-9_]*)\b/g, '<span class="text-cyan-400">$1</span>') // Classes/Interfaces
13
+ .replace(/\b(true|false|null|undefined)\b/g, '<span class="text-orange-400">$1</span>')
14
+ .replace(/(\/\/.*)/g, '<span class="text-zinc-500 italic">$1</span>') // Comments
15
+ .replace(/([{}()[\]])/g, '<span class="text-zinc-400">$1</span>'); // Brackets
16
+
17
+ return (
18
+ <div key={i} className="table-row">
19
+ <span className="table-cell text-right select-none text-zinc-600 pr-4 text-xs opacity-50">{i + 1}</span>
20
+ <span className="table-cell whitespace-pre" dangerouslySetInnerHTML={{ __html: highlightedLine }} />
21
+ </div>
22
+ );
23
+ });
24
+ }, [code]);
25
+
26
+ return (
27
+ <div className="bg-[#0D0D0D] border border-zinc-800 rounded-xl p-4 font-mono text-sm overflow-hidden text-zinc-300 shadow-2xl">
28
+ <div className="flex items-center gap-2 mb-4 border-b border-zinc-800 pb-3">
29
+ <div className="w-2.5 h-2.5 rounded-full bg-red-500/80"></div>
30
+ <div className="w-2.5 h-2.5 rounded-full bg-yellow-500/80"></div>
31
+ <div className="w-2.5 h-2.5 rounded-full bg-green-500/80"></div>
32
+ <span className="ml-2 text-xs text-zinc-500 font-sans">decido-sdk-demo.ts</span>
33
+ </div>
34
+ <div className="table w-full">
35
+ {highlightedCode}
36
+ </div>
37
+ </div>
38
+ );
39
+ };
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { Handle, Position } from '@xyflow/react';
3
+
4
+ export interface OSNodeData {
5
+ label: string;
6
+ status: string;
7
+ icon: React.ReactNode;
8
+ isActive: boolean;
9
+ }
10
+
11
+ export const OSNode: React.FC<{ data: OSNodeData }> = ({ data }) => (
12
+ <div className={`px-4 py-3 rounded-xl bg-black/80 backdrop-blur-md border-2 ${data.isActive ? 'border-cyan-500 shadow-[0_0_20px_rgba(6,182,212,0.4)]' : 'border-zinc-800'} text-white flex flex-col items-center justify-center min-w-[140px] transition-all duration-700`}>
13
+ <Handle type="target" position={Position.Top} className="!bg-zinc-700" />
14
+ <div className={`p-2 rounded-full mb-2 ${data.isActive ? 'bg-cyan-500/20 text-cyan-400' : 'bg-zinc-800 text-zinc-500'}`}>
15
+ {data.icon}
16
+ </div>
17
+ <div className="font-bold text-sm tracking-wide">{data.label}</div>
18
+ <div className={`text-[10px] mt-1 font-mono ${data.isActive ? 'text-cyan-400' : 'text-zinc-600'}`}>
19
+ {data.status}
20
+ </div>
21
+ <Handle type="source" position={Position.Bottom} className="!bg-zinc-700" />
22
+ <Handle type="source" position={Position.Right} id="right" className="!bg-zinc-700" />
23
+ <Handle type="target" position={Position.Left} id="left" className="!bg-zinc-700" />
24
+ </div>
25
+ );
@@ -0,0 +1,120 @@
1
+ import React from 'react';
2
+ import { Cpu, Terminal, Network, BookOpen } from 'lucide-react';
3
+
4
+ export const Sparkles = ({ className }: { className?: string }) => (
5
+ <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
6
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" />
7
+ </svg>
8
+ );
9
+
10
+ export interface StepNodeUpdate {
11
+ id: string;
12
+ isActive: boolean;
13
+ status: string;
14
+ }
15
+
16
+ export interface TutorialStep {
17
+ id: string;
18
+ title: string;
19
+ description: string;
20
+ code: string;
21
+ actionLabel: string;
22
+ nodes: any[]; // Using any to be compatible with ReactFlow Node type
23
+ edges: any[]; // Using any to be compatible with ReactFlow Edge type
24
+ nodeUpdatesOnExecute: StepNodeUpdate[];
25
+ }
26
+
27
+ // --- Tutorial Steps Data ---
28
+ export const TUTORIAL_STEPS: TutorialStep[] = [
29
+ {
30
+ id: 'intro',
31
+ title: 'Inicializando el Kernel Bridge',
32
+ description: 'Decido OS se basa en un Kernel central. Para interactuar con él desde una UI (React/Vue/Svelte), usas el `kernel-bridge`. Inicializarlo es el paso 0 de cualquier app.',
33
+ code: `import { kernel } from '@decido/kernel-bridge';
34
+
35
+ // 1. Iniciar la conexión segura
36
+ await kernel.boot();
37
+
38
+ // 2. Verificar estado del Kernel
39
+ const status = kernel.getStatus();
40
+ console.log("Kernel is:", status); // "ONLINE"
41
+ `,
42
+ actionLabel: 'Ejecutar Boot',
43
+ nodes: [
44
+ { id: 'kernel', type: 'osNode', position: { x: 250, y: 150 }, data: { label: 'Decido Kernel', status: 'OFFLINE', icon: <Cpu className="w-5 h-5" />, isActive: false } }
45
+ ],
46
+ edges: [],
47
+ nodeUpdatesOnExecute: [
48
+ { id: 'kernel', isActive: true, status: 'ONLINE [WS Mux]' }
49
+ ]
50
+ },
51
+ {
52
+ id: 'plugin',
53
+ title: 'Registrando un Plugin UI',
54
+ description: 'Los plugins inyectan Web Components (Widgets) o CORTEX dinámicos dentro del Shell en tiempo de ejecución. Así es como extiendes el sistema.',
55
+ code: `import { usePluginStore } from '@decido/shell';
56
+
57
+ // 1. Construir Manifesto
58
+ const MyPlugin = {
59
+ id: 'stellar-plugin',
60
+ name: 'Stellar Analytics',
61
+ widgets: [{
62
+ id: 'stellar-dashboard',
63
+ component: MyDashboardReactComponent,
64
+ defaultZone: 'main-canvas'
65
+ }]
66
+ };
67
+
68
+ // 2. Registrar en el Store Descentralizado
69
+ usePluginStore.getState().registerPlugin(MyPlugin);
70
+ `,
71
+ actionLabel: 'Inyectar Plugin',
72
+ nodes: [
73
+ { id: 'kernel', type: 'osNode', position: { x: 250, y: 150 }, data: { label: 'Decido Kernel', status: 'ONLINE', icon: <Cpu className="w-5 h-5" />, isActive: true } },
74
+ { id: 'plugin', type: 'osNode', position: { x: 50, y: 300 }, data: { label: 'Stellar Plugin', status: 'UNLOADED', icon: <Terminal className="w-5 h-5" />, isActive: false } },
75
+ { id: 'shell', type: 'osNode', position: { x: 450, y: 300 }, data: { label: 'React Shell', status: 'WAITING', icon: <BookOpen className="w-5 h-5" />, isActive: false } }
76
+ ],
77
+ edges: [
78
+ { id: 'e-kp', source: 'plugin', target: 'kernel', sourceHandle: 'right', targetHandle: 'left', animated: false, style: { stroke: '#52525b', strokeWidth: 2 } },
79
+ { id: 'e-ks', source: 'kernel', target: 'shell', sourceHandle: 'right', targetHandle: 'left', animated: false, style: { stroke: '#52525b', strokeWidth: 2 } }
80
+ ],
81
+ nodeUpdatesOnExecute: [
82
+ { id: 'plugin', isActive: true, status: 'INJECTED' },
83
+ { id: 'shell', isActive: true, status: 'MOUNTED WIDGET' }
84
+ ]
85
+ },
86
+ {
87
+ id: 'agent',
88
+ title: 'Orquestando Swarms / MCP',
89
+ description: 'La magia de Decido OS es su AGI Nativa. Puedes conectar herramientas dinámicamente o escuchar cuando el modelo cognitivo "piensa".',
90
+ code: `import { kernel } from '@decido/kernel-bridge';
91
+
92
+ // Conectar un Servidor MCP (Tooling Externo)
93
+ await kernel.execute('connect_mcp_server', {
94
+ id: 'db-agent',
95
+ name: 'Database Explorer MCP'
96
+ });
97
+
98
+ // Escuchar los "pensamientos" (Cognitive Frames)
99
+ kernel.onEvent((payload) => {
100
+ if (payload.event_type === 'cognitive_frame') {
101
+ ui.toast(\`🤖 Agente pensando: \${payload.data.thought}\`);
102
+ }
103
+ });
104
+ `,
105
+ actionLabel: 'Activar Swarm',
106
+ nodes: [
107
+ { id: 'kernel', type: 'osNode', position: { x: 250, y: 150 }, data: { label: 'Decido Kernel', status: 'ONLINE', icon: <Cpu className="w-5 h-5" />, isActive: true } },
108
+ { id: 'mcp', type: 'osNode', position: { x: 50, y: 50 }, data: { label: 'MCP DB Agent', status: 'DISCONNECTED', icon: <Network className="w-5 h-5" />, isActive: false } },
109
+ { id: 'agent', type: 'osNode', position: { x: 450, y: 50 }, data: { label: 'Cognitive Engine', status: 'IDLE', icon: <Sparkles className="w-5 h-5" />, isActive: false } }
110
+ ],
111
+ edges: [
112
+ { id: 'e-mk', source: 'mcp', target: 'kernel', animated: false, style: { stroke: '#52525b', strokeWidth: 2 } },
113
+ { id: 'e-ak', source: 'kernel', target: 'agent', animated: false, style: { stroke: '#52525b', strokeWidth: 2 } }
114
+ ],
115
+ nodeUpdatesOnExecute: [
116
+ { id: 'mcp', isActive: true, status: 'CONNECTED (stdio)' },
117
+ { id: 'agent', isActive: true, status: 'THINKING...' }
118
+ ]
119
+ }
120
+ ];
@@ -0,0 +1,29 @@
1
+ import * as Recharts from 'recharts';
2
+ import * as FramerMotion from 'framer-motion';
3
+ import * as LucideIcons from 'lucide-react';
4
+
5
+ /**
6
+ * Map of authorized libraries that the OS can inject into the Generative UI.
7
+ */
8
+ export const AVAILABLE_DEPENDENCIES: Record<string, any> = {
9
+ 'recharts': Recharts,
10
+ 'framer-motion': FramerMotion,
11
+ 'lucide': LucideIcons,
12
+ };
13
+
14
+ /**
15
+ * Analyzes the generated code to detect which libraries the AI requests.
16
+ */
17
+ export const detectRequiredDependencies = (code: string): string[] => {
18
+ const deps: string[] = [];
19
+ if (code.includes('LineChart') || code.includes('BarChart') || code.includes('PieChart') || code.includes('AreaChart') || code.includes('ComposedChart')) {
20
+ deps.push('recharts');
21
+ }
22
+ if (code.includes('motion.') || code.includes('AnimatePresence')) {
23
+ deps.push('framer-motion');
24
+ }
25
+ if (code.includes('Lucide') || code.includes('createLucideIcon')) {
26
+ deps.push('lucide');
27
+ }
28
+ return deps;
29
+ };
@@ -0,0 +1,272 @@
1
+ import { createStore } from 'zustand';
2
+ import React from 'react';
3
+ import * as LucideIcons from 'lucide-react';
4
+ import { useShellStore, LayoutNode, SafeLiquidUI } from '@decido/shell';
5
+ import { SafeZone } from '../components/SafeComponentWrapper';
6
+ import { kernel } from '@decido/kernel-bridge';
7
+ import { createSanitizedKernel } from './SecurityProxy';
8
+ import { AVAILABLE_DEPENDENCIES, detectRequiredDependencies } from './DependencyRegistry';
9
+
10
+ export interface PlaygroundState {
11
+ logs: string[];
12
+ isExecuting: boolean;
13
+ }
14
+
15
+ export const playgroundStore = createStore<PlaygroundState>(() => ({
16
+ logs: ['Sistema en línea. Esperando directivas.'],
17
+ isExecuting: false
18
+ }));
19
+
20
+ // Escuchar actualizaciones de datos en vivo y transmutaciones generativas desde Rust/Redis
21
+ kernel.onEvent((payload: any) => {
22
+ if (payload.event_type === 'ui:update_widget_data') {
23
+ const { widgetId, newData } = payload.data;
24
+ useShellStore.getState().setWidgetData(widgetId, newData);
25
+ console.log(`[LiveUpdate] 🔄 Datos actualizados para ${widgetId}`);
26
+ } else if (payload.event_type === 'ui:spawn_dynamic_widget') {
27
+ const title = payload.data?.title || "Misión Completada (Liquid UI)";
28
+ const code = payload.data?.code || payload.data?.content || payload.data || "";
29
+ console.log(`[LiquidUI] 🌊 Materializando interfaz efímera: ${title}`);
30
+ PlaygroundEngine.spawnGenerativeUI(code, title);
31
+ }
32
+ });
33
+
34
+ export const PlaygroundEngine = {
35
+ log(msg: string) {
36
+ playgroundStore.setState((state) => ({
37
+ logs: [...state.logs, `[${new Date().toLocaleTimeString()}] ${msg}`].slice(-10) // Mantener últimos 10 logs
38
+ }));
39
+ },
40
+
41
+ async dispatchMission(objective: string) {
42
+ const { isThinClient } = useShellStore.getState();
43
+
44
+ if (playgroundStore.getState().isExecuting) return;
45
+
46
+ playgroundStore.setState({ isExecuting: true });
47
+ this.log(`🚀 Misión enviada ${isThinClient ? 'remotamente' : 'al enjambre local'}: "${objective}"`);
48
+
49
+ try {
50
+ // Enviamos el mensaje al agente 'dev-master' (que crearemos en Rust)
51
+ await kernel.execute('send_message', {
52
+ from: isThinClient ? "Mobile_User" : "Desktop_User",
53
+ to: "dev-master", // ID del agente en Rust
54
+ intent: "execute_task",
55
+ payload: objective,
56
+ priority: "high"
57
+ });
58
+
59
+ this.log('🛰️ Mensaje propagado por Redis. Esperando respuesta del Agente...');
60
+ } catch (e: any) {
61
+ this.log(`❌ Error de conexión: ${e.message}`);
62
+ } finally {
63
+ playgroundStore.setState({ isExecuting: false });
64
+ }
65
+ },
66
+
67
+ // --- Phase 10: Generative & Self-Healing Engine ---
68
+
69
+ lastAttemptedCode: "",
70
+ lastErrorSignature: "", // Add signature tracker
71
+ currentMission: "",
72
+ retryCount: 0,
73
+ MAX_RETRIES: 2,
74
+
75
+ sanitizeJSX(raw: string): string {
76
+ let clean = raw.trim();
77
+ // Intentar extraer bloque de código con lenguaje especificado (jsx, tsx, etc.)
78
+ const langMatch = clean.match(/```(?:jsx|tsx|javascript|typescript|js|ts)\n([\s\S]*?)\n```/);
79
+ if (langMatch && langMatch[1]) {
80
+ return langMatch[1].trim();
81
+ }
82
+ // Intentar extraer bloque genérico sin lenguaje
83
+ const genericMatch = clean.match(/```\n([\s\S]*?)\n```/);
84
+ if (genericMatch && genericMatch[1]) {
85
+ return genericMatch[1].trim();
86
+ }
87
+ // Si no hay bloques markdown, retornar todo asumiendo que es código puro
88
+ return clean;
89
+ },
90
+
91
+ async spawnGenerativeUI(rawCode: string, missionTitle: string = 'Componente Generado', missionData: any = {}) {
92
+ const code = this.sanitizeJSX(rawCode);
93
+ this.log(`🛡️ Preparando Zona Segura (Shadow DOM) para: ${missionTitle}`);
94
+ this.lastAttemptedCode = code;
95
+ this.currentMission = missionTitle;
96
+
97
+ const widgetId = `gen-live-${Date.now()}`;
98
+
99
+ // Guardar datos iniciales
100
+ useShellStore.getState().setWidgetData(widgetId, missionData);
101
+
102
+ const handleCrash = async (error: Error, errorInfo: any) => {
103
+ this.log(`⚠️ Detectado crash en UI. Iniciando auto-curación...`);
104
+
105
+ if (this.retryCount >= this.MAX_RETRIES) {
106
+ this.log(`❌ Demasiados intentos de reparación. Abortando misión.`);
107
+ kernel.notify('Fallo de Auto-Curación', 'El agente no pudo reparar la interfaz tras varios intentos.');
108
+ return;
109
+ }
110
+
111
+ this.retryCount++;
112
+ this.lastErrorSignature = error.message;
113
+
114
+ // Prepared repair prompt
115
+ const repairPrompt = `
116
+ SISTEMA DE AUTO-CURACIÓN DE DECIDO-OS
117
+ -------------------------------------
118
+ El código React que generaste causó un error de ejecución en tiempo real.
119
+
120
+ ERROR: ${error.message}
121
+ LOCALIZACIÓN: ${JSON.stringify(errorInfo.componentStack?.split('\\n').slice(0, 3))}
122
+
123
+ CÓDIGO FALLIDO:
124
+ \`\`\`jsx
125
+ ${this.lastAttemptedCode}
126
+ \`\`\`
127
+
128
+ TAREA: Corrige el error en tu lógica. Asegúrate de que todas las variables y accesos a mapas/arrays sean seguros (ej. usa optional chaining).
129
+ REGLA CRÍTICA: No uses librerías externas que no sean React ni intentes renderizar objetos como si fueran elementos React.
130
+ Usa únicamente Tailwind inline classes.
131
+ Asegúrate de que el componente exportado dinámicamente se asigne a 'DynamicComponent'.
132
+ Responde devolviendo de nuevo el intento \`ui:spawn_dynamic_widget\` pero con el código completamente solventado. No repitas el mismo error.
133
+ `;
134
+
135
+ await kernel.execute('send_message', {
136
+ from: "System_UI_Sentinel",
137
+ to: "dev-master",
138
+ intent: "repair_ui",
139
+ payload: repairPrompt,
140
+ priority: "high"
141
+ });
142
+
143
+ this.log(`🛰️ Feedback enviado al Orquestador Rust. Re-intentando constructo (Intento ${this.retryCount})...`);
144
+ };
145
+
146
+ try {
147
+ const requiredDeps = detectRequiredDependencies(code);
148
+ this.log(`📦 Dependencias inyectadas: ${requiredDeps.join(', ') || 'Ninguna'}`);
149
+
150
+ const dependencyArgs: Record<string, any> = {
151
+ 'React': React,
152
+ 'kernel': createSanitizedKernel(missionTitle),
153
+ 'Lucide': LucideIcons,
154
+ 'window': undefined,
155
+ 'document': undefined,
156
+ 'localStorage': undefined,
157
+ 'fetch': undefined,
158
+ };
159
+
160
+ requiredDeps.forEach(dep => {
161
+ dependencyArgs[dep.replace('-', '_')] = AVAILABLE_DEPENDENCIES[dep];
162
+ });
163
+
164
+ const argNames = Object.keys(dependencyArgs);
165
+ const argValues = Object.values(dependencyArgs);
166
+
167
+ // 1. DSL Transpilation via Safe AST Engine
168
+ // Bloqueamos el motor generativo para enforzar el AST SafeLiquidUI. No más ejecución libre.
169
+ const RawDynamicComponent: React.FC<any> = (props) => {
170
+ return React.createElement(SafeLiquidUI, {
171
+ uiSchema: code,
172
+ tenantId: 'generative-playground',
173
+ missionData: props.data
174
+ });
175
+ };
176
+
177
+ // 2. Wrap via ErrorBoundary and Security Context with Live Data
178
+ const ProtectedComponent = (props: any) => {
179
+ const liveData = useShellStore((state) => state.widgetDataBus[widgetId]);
180
+
181
+ // EFECTO DE LIMPIEZA: Handshake de Cierre
182
+ React.useEffect(() => {
183
+ return () => {
184
+ console.log(`[Shell] 🛑 Cerrando canal de datos para ${widgetId}`);
185
+ kernel.execute('stop_live_monitor', { widgetId }).catch(e => console.warn(e));
186
+ };
187
+ }, []);
188
+
189
+ return React.createElement(
190
+ SafeZone,
191
+ {
192
+ title: missionTitle,
193
+ rawCode: code,
194
+ onCrash: handleCrash,
195
+ children: React.createElement(RawDynamicComponent, { ...props, data: liveData })
196
+ }
197
+ );
198
+ };
199
+
200
+ // 3. Register Temporary Layout Morphological Widget
201
+ const pluginId = `plugin-${widgetId}`;
202
+
203
+ useShellStore.getState().registerPlugin({
204
+ id: pluginId,
205
+ name: missionTitle,
206
+ version: '1.0.0',
207
+ description: 'Generado vía Auto-Curación',
208
+ widgets: [{
209
+ id: widgetId,
210
+ pluginId: pluginId,
211
+ name: missionTitle,
212
+ capabilities: ['generative:canvas'],
213
+ views: { expanded: ProtectedComponent },
214
+ layoutConfig: { preferredZone: 'main-canvas' },
215
+ component: ProtectedComponent,
216
+ isTransient: true
217
+ }],
218
+ intents: []
219
+ });
220
+
221
+ // 4. Force Structural Layout Transmutation
222
+ const newLayout: LayoutNode = {
223
+ id: `layout-${widgetId}`,
224
+ type: 'split-horizontal',
225
+ children: [
226
+ {
227
+ id: 'side-terminal',
228
+ type: 'widget',
229
+ widgetId: 'forge-terminal',
230
+ size: 30
231
+ },
232
+ {
233
+ id: `main-${widgetId}`,
234
+ type: 'widget',
235
+ widgetId: widgetId,
236
+ size: 70
237
+ }
238
+ ]
239
+ };
240
+
241
+ useShellStore.getState().setLayout(newLayout);
242
+
243
+ kernel.notify('✨ Canvas Actualizado', `Constructo "${missionTitle}" renderizado.`);
244
+ this.log(`✅ UI Montada con éxito en el canvas principal.`);
245
+
246
+ // If we successfully rendered and this was a fix, store the UILesson
247
+ if (this.retryCount > 0 && this.lastErrorSignature) {
248
+ this.log(`🧠 Guardando Lección UI (Memoria Cognitiva) en Qdrant...`);
249
+ kernel.execute('save_lesson', {
250
+ lesson: {
251
+ error_signature: this.lastErrorSignature,
252
+ failed_code: this.lastAttemptedCode,
253
+ fixed_code: code
254
+ }
255
+ }).catch(e => {
256
+ console.warn("Failed to save UILesson:", e);
257
+ });
258
+ }
259
+
260
+ // Reset retries and update current baseline
261
+ this.retryCount = 0;
262
+ this.lastErrorSignature = "";
263
+ this.lastAttemptedCode = code;
264
+
265
+ } catch (error: any) {
266
+ this.log(`❌ Error preventivo de Ensamblaje Estático: ${error.message}`);
267
+
268
+ // Initiate auto-healing even for static syntax errors
269
+ handleCrash(error, { componentStack: "Eval/Transpilation Phase" });
270
+ }
271
+ }
272
+ };