@vib3code/sdk 2.0.3-canary.6f35b4c → 2.0.3-canary.74aebb4

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 (89) hide show
  1. package/DOCS/EXPANSION_DESIGN.md +977 -0
  2. package/DOCS/EXPANSION_DESIGN_ULTRA.md +387 -0
  3. package/DOCS/MASTER_PLAN_2026-01-31.md +2 -2
  4. package/DOCS/OPTIMIZATION_PLAN_MATH.md +118 -0
  5. package/DOCS/SYSTEM_INVENTORY.md +2 -2
  6. package/DOCS/WEBGPU_STATUS.md +119 -38
  7. package/DOCS/archive/WEBGPU_STATUS_2026-02-15_STALE.md +38 -0
  8. package/DOCS/dev-tracks/DEV_TRACK_SESSION_2026-02-15.md +142 -0
  9. package/DOCS/dev-tracks/DEV_TRACK_SESSION_2026-02-16.md +108 -0
  10. package/DOCS/dev-tracks/PERF_UPGRADE_2026-02-16.md +308 -0
  11. package/docs/webgpu-live.html +1 -1
  12. package/package.json +10 -1
  13. package/src/agent/index.js +1 -3
  14. package/src/agent/mcp/MCPServer.js +542 -188
  15. package/src/agent/mcp/index.js +1 -1
  16. package/src/agent/mcp/tools.js +132 -32
  17. package/src/cli/index.js +374 -44
  18. package/src/core/VIB3Engine.js +55 -3
  19. package/src/core/index.js +18 -0
  20. package/src/core/renderers/FacetedRendererAdapter.js +10 -9
  21. package/src/core/renderers/HolographicRendererAdapter.js +11 -7
  22. package/src/core/renderers/QuantumRendererAdapter.js +11 -7
  23. package/src/creative/index.js +11 -0
  24. package/src/experimental/GameLoop.js +72 -0
  25. package/src/experimental/LatticePhysics.js +100 -0
  26. package/src/experimental/LiveDirector.js +143 -0
  27. package/src/experimental/PlayerController4D.js +154 -0
  28. package/src/experimental/VIB3Actor.js +138 -0
  29. package/src/experimental/VIB3Compositor.js +117 -0
  30. package/src/experimental/VIB3Link.js +122 -0
  31. package/src/experimental/VIB3Orchestrator.js +146 -0
  32. package/src/experimental/VIB3Universe.js +109 -0
  33. package/src/experimental/demos/CrystalLabyrinth.js +202 -0
  34. package/src/export/index.js +11 -1
  35. package/src/faceted/FacetedSystem.js +27 -10
  36. package/src/games/glyph-war/GlyphWarVisualizer.js +641 -0
  37. package/src/geometry/generators/Crystal.js +2 -2
  38. package/src/holograms/HolographicVisualizer.js +58 -89
  39. package/src/holograms/RealHolographicSystem.js +126 -31
  40. package/src/math/Mat4x4.js +192 -19
  41. package/src/math/Rotor4D.js +93 -39
  42. package/src/math/Vec4.js +119 -78
  43. package/src/math/index.js +7 -7
  44. package/src/quantum/QuantumVisualizer.js +24 -20
  45. package/src/reactivity/index.js +3 -5
  46. package/src/render/LayerPresetManager.js +372 -0
  47. package/src/render/LayerReactivityBridge.js +344 -0
  48. package/src/render/LayerRelationshipGraph.js +610 -0
  49. package/src/render/MultiCanvasBridge.js +148 -25
  50. package/src/render/ShaderLoader.js +38 -0
  51. package/src/render/ShaderProgram.js +4 -4
  52. package/src/render/UnifiedRenderBridge.js +1 -1
  53. package/src/render/backends/WebGPUBackend.js +8 -4
  54. package/src/render/index.js +27 -2
  55. package/src/scene/index.js +4 -4
  56. package/src/shaders/common/geometry24.glsl +65 -0
  57. package/src/shaders/common/geometry24.wgsl +54 -0
  58. package/src/shaders/common/rotation4d.glsl +4 -4
  59. package/src/shaders/common/rotation4d.wgsl +2 -2
  60. package/src/shaders/common/uniforms.wgsl +15 -8
  61. package/src/shaders/faceted/faceted.frag.wgsl +19 -6
  62. package/src/shaders/holographic/holographic.frag.wgsl +7 -5
  63. package/src/shaders/quantum/quantum.frag.wgsl +7 -5
  64. package/src/testing/ParallelTestFramework.js +2 -2
  65. package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +2 -2
  66. package/src/viewer/GalleryUI.js +17 -0
  67. package/src/viewer/ViewerPortal.js +2 -2
  68. package/tools/shader-sync-verify.js +6 -4
  69. package/types/adaptive-sdk.d.ts +204 -5
  70. package/types/agent/cli.d.ts +78 -0
  71. package/types/agent/index.d.ts +18 -0
  72. package/types/agent/mcp.d.ts +87 -0
  73. package/types/agent/telemetry.d.ts +190 -0
  74. package/types/core/VIB3Engine.d.ts +26 -0
  75. package/types/core/index.d.ts +261 -0
  76. package/types/creative/AestheticMapper.d.ts +72 -0
  77. package/types/creative/ChoreographyPlayer.d.ts +96 -0
  78. package/types/creative/index.d.ts +17 -0
  79. package/types/export/index.d.ts +243 -0
  80. package/types/geometry/index.d.ts +164 -0
  81. package/types/math/index.d.ts +214 -0
  82. package/types/render/LayerPresetManager.d.ts +78 -0
  83. package/types/render/LayerReactivityBridge.d.ts +85 -0
  84. package/types/render/LayerRelationshipGraph.d.ts +174 -0
  85. package/types/render/index.d.ts +3 -0
  86. package/types/scene/index.d.ts +204 -0
  87. package/types/systems/index.d.ts +244 -0
  88. package/types/variations/index.d.ts +62 -0
  89. package/types/viewer/index.d.ts +225 -0
@@ -9,6 +9,9 @@ import { telemetry, EventType, withTelemetry } from '../telemetry/index.js';
9
9
  import { AestheticMapper } from '../../creative/AestheticMapper.js';
10
10
  import { ChoreographyPlayer } from '../../creative/ChoreographyPlayer.js';
11
11
  import { ParameterTimeline } from '../../creative/ParameterTimeline.js';
12
+ import { ColorPresetsSystem } from '../../creative/ColorPresetsSystem.js';
13
+ import { TransitionAnimator } from '../../creative/TransitionAnimator.js';
14
+ import { PRESET_REGISTRY } from '../../render/LayerRelationshipGraph.js';
12
15
 
13
16
  /**
14
17
  * Generate unique IDs
@@ -41,6 +44,36 @@ export class MCPServer {
41
44
  this.engine = engine;
42
45
  this.sceneId = null;
43
46
  this.initialized = false;
47
+ this._gallerySlots = new Map();
48
+ }
49
+
50
+ /**
51
+ * Get or lazily create ColorPresetsSystem instance.
52
+ * Requires engine for the parameter update callback.
53
+ * @returns {ColorPresetsSystem|null}
54
+ */
55
+ _getColorPresets() {
56
+ if (this._colorPresets) return this._colorPresets;
57
+ if (!this.engine) return null;
58
+ this._colorPresets = new ColorPresetsSystem(
59
+ (name, value) => this.engine.setParameter(name, value)
60
+ );
61
+ return this._colorPresets;
62
+ }
63
+
64
+ /**
65
+ * Get or lazily create TransitionAnimator instance.
66
+ * Requires engine for the parameter update/get callbacks.
67
+ * @returns {TransitionAnimator|null}
68
+ */
69
+ _getTransitionAnimator() {
70
+ if (this._transitionAnimator) return this._transitionAnimator;
71
+ if (!this.engine) return null;
72
+ this._transitionAnimator = new TransitionAnimator(
73
+ (name, value) => this.engine.setParameter(name, value),
74
+ (name) => this.engine.getParameter(name)
75
+ );
76
+ return this._transitionAnimator;
44
77
  }
45
78
 
46
79
  buildResponse(operation, data, options = {}) {
@@ -140,10 +173,13 @@ export class MCPServer {
140
173
  result = this.getParameterSchema();
141
174
  break;
142
175
  case 'get_sdk_context':
143
- result = this.getSDKContext();
176
+ result = this.getSDKContext(args);
144
177
  break;
145
- case 'verify_knowledge':
146
- result = this.verifyKnowledge(args);
178
+ case 'inspect_layers':
179
+ result = this.inspectLayers(args);
180
+ break;
181
+ case 'set_holographic_layer':
182
+ result = this.setHolographicLayer(args);
147
183
  break;
148
184
  // Reactivity tools (Phase 6.5)
149
185
  case 'set_reactivity_config':
@@ -204,6 +240,22 @@ export class MCPServer {
204
240
  case 'control_timeline':
205
241
  result = this.controlTimeline(args);
206
242
  break;
243
+ // Layer relationship tools (Phase 8)
244
+ case 'set_layer_profile':
245
+ result = this.setLayerProfile(args);
246
+ break;
247
+ case 'set_layer_relationship':
248
+ result = this.setLayerRelationship(args);
249
+ break;
250
+ case 'set_layer_keystone':
251
+ result = this.setLayerKeystone(args);
252
+ break;
253
+ case 'get_layer_config':
254
+ result = this.getLayerConfig();
255
+ break;
256
+ case 'tune_layer_relationship':
257
+ result = this.tuneLayerRelationship(args);
258
+ break;
207
259
  default:
208
260
  throw new Error(`Unknown tool: ${toolName}`);
209
261
  }
@@ -467,32 +519,68 @@ export class MCPServer {
467
519
 
468
520
  telemetry.recordEvent(EventType.GALLERY_SAVE, { slot });
469
521
 
522
+ // Persist actual engine state if available
523
+ if (this.engine) {
524
+ const state = this.engine.exportState();
525
+ this._gallerySlots.set(slot, {
526
+ name: name || `Variation ${slot}`,
527
+ saved_at: new Date().toISOString(),
528
+ state
529
+ });
530
+ }
531
+
470
532
  return {
471
533
  slot,
472
534
  name: name || `Variation ${slot}`,
473
535
  saved_at: new Date().toISOString(),
536
+ persisted: !!this.engine,
537
+ gallery_size: this._gallerySlots.size,
474
538
  suggested_next_actions: ['load_from_gallery', 'randomize_parameters']
475
539
  };
476
540
  }
477
541
 
478
542
  /**
479
- * Load from gallery
543
+ * Load from gallery — restores previously saved state
480
544
  */
481
545
  loadFromGallery(args) {
482
546
  const { slot } = args;
483
547
 
484
- if (this.engine) {
485
- // Apply variation
486
- const params = this.engine.parameters?.generateVariationParameters?.(slot) || {};
487
- this.engine.setParameters(params);
548
+ telemetry.recordEvent(EventType.GALLERY_LOAD, { slot });
549
+
550
+ const saved = this._gallerySlots.get(slot);
551
+ if (saved && this.engine) {
552
+ // Restore saved state
553
+ this.engine.importState(saved.state);
554
+ return {
555
+ slot,
556
+ name: saved.name,
557
+ saved_at: saved.saved_at,
558
+ loaded_at: new Date().toISOString(),
559
+ restored: true,
560
+ ...this.getState()
561
+ };
488
562
  }
489
563
 
490
- telemetry.recordEvent(EventType.GALLERY_LOAD, { slot });
564
+ if (!saved) {
565
+ // No saved state — fall back to random variation
566
+ if (this.engine) {
567
+ const params = this.engine.parameters?.generateVariationParameters?.(slot) || {};
568
+ this.engine.setParameters(params);
569
+ }
570
+ return {
571
+ slot,
572
+ loaded_at: new Date().toISOString(),
573
+ restored: false,
574
+ note: 'No saved state in this slot — generated random variation',
575
+ ...this.getState()
576
+ };
577
+ }
491
578
 
492
579
  return {
493
580
  slot,
494
581
  loaded_at: new Date().toISOString(),
495
- ...this.getState()
582
+ restored: false,
583
+ note: 'Engine not initialized — cannot apply state'
496
584
  };
497
585
  }
498
586
 
@@ -565,162 +653,218 @@ export class MCPServer {
565
653
  /**
566
654
  * Get SDK context for agent onboarding
567
655
  */
568
- getSDKContext() {
569
- return {
570
- sdk_name: 'VIB3+ SDK',
571
- version: '1.9.0',
572
- purpose: 'General-purpose 4D rotation visualization SDK for plugins, extensions, wearables, and agentic use',
573
-
574
- quick_reference: {
575
- active_visualization_systems: 3,
576
- placeholder_systems: 1,
577
- rotation_planes: 6,
578
- base_geometries: 8,
579
- core_warp_types: 3,
580
- total_geometries: 24,
581
- canvas_layers_per_system: 5
582
- },
583
-
584
- systems: {
585
- ACTIVE: [
586
- { name: 'quantum', description: 'Complex quantum lattice visualizations with 24 geometries' },
587
- { name: 'faceted', description: 'Clean 2D geometric patterns with 4D rotation' },
588
- { name: 'holographic', description: '5-layer audio-reactive holographic effects' }
589
- ],
590
- PLACEHOLDER_TBD: [
591
- { name: 'polychora', status: 'TBD', description: '4D polytopes - placeholder, not production ready' }
592
- ]
656
+ getSDKContext(args = {}) {
657
+ const { include_state = true, include_tools = false } = args;
658
+
659
+ const context = {
660
+ sdk: 'VIB3+ 4D Visualization Engine',
661
+ version: '2.0.3',
662
+
663
+ // Capability manifest — what this engine can do
664
+ capabilities: {
665
+ systems: ['quantum', 'faceted', 'holographic'],
666
+ geometries: { count: 24, formula: 'core_type * 8 + base_geometry', base: 8, warps: 3 },
667
+ rotation: { planes: 6, '3D': ['XY', 'XZ', 'YZ'], '4D': ['XW', 'YW', 'ZW'], range: '±6.28 rad' },
668
+ layers: { count: 5, roles: ['background', 'shadow', 'content', 'highlight', 'accent'], addressable: true },
669
+ audio: { bands: ['bass', 'mid', 'high'], modes: ['add', 'multiply', 'replace', 'max', 'min'] },
670
+ input: { sources: ['deviceTilt', 'mousePosition', 'gyroscope', 'gamepad', 'perspective', 'programmatic', 'audio', 'midi'] },
671
+ creative: {
672
+ color_presets: 22,
673
+ easing_functions: 14,
674
+ post_effects: 14,
675
+ aesthetic_keywords: '130+',
676
+ choreography: true,
677
+ timeline_bpm_sync: true
678
+ },
679
+ environment: {
680
+ browser: typeof document !== 'undefined',
681
+ screenshot: typeof document !== 'undefined',
682
+ webgpu: typeof navigator !== 'undefined' && !!navigator.gpu,
683
+ wasm: typeof WebAssembly !== 'undefined'
684
+ }
593
685
  },
594
686
 
595
- geometry_encoding: {
596
- formula: 'geometry_index = core_index * 8 + base_index',
597
- base_geometries: ['tetrahedron', 'hypercube', 'sphere', 'torus', 'klein_bottle', 'fractal', 'wave', 'crystal'],
598
- core_types: ['base (0)', 'hypersphere (1)', 'hypertetrahedron (2)'],
599
- example: 'geometry 10 = hypersphere(sphere) because 1*8+2=10'
687
+ // Parameter ranges — the agent needs these to generate valid values
688
+ parameter_ranges: {
689
+ geometry: { min: 0, max: 23, type: 'integer' },
690
+ hue: { min: 0, max: 360, type: 'integer' },
691
+ saturation: { min: 0, max: 1 },
692
+ intensity: { min: 0, max: 1 },
693
+ speed: { min: 0.1, max: 3 },
694
+ chaos: { min: 0, max: 1 },
695
+ morphFactor: { min: 0, max: 2 },
696
+ gridDensity: { min: 4, max: 100 },
697
+ dimension: { min: 3.0, max: 4.5 },
698
+ rot4dXY: { min: -6.28, max: 6.28 },
699
+ rot4dXW: { min: -6.28, max: 6.28 }
600
700
  },
601
701
 
602
- rotation_planes: {
603
- total: 6,
604
- '3D_space': ['XY', 'XZ', 'YZ'],
605
- '4D_hyperspace': ['XW', 'YW', 'ZW'],
606
- range: '-6.28 to 6.28 radians'
702
+ // Workflow hints — what tool sequences accomplish creative goals
703
+ workflows: {
704
+ quick_design: 'design_from_description describe_visual_state → batch_set_parameters',
705
+ choreography: 'create_choreography play_choreography → describe_visual_state',
706
+ evolve: 'batch_set_parameters describe_visual_state → batch_set_parameters (iterate)',
707
+ layer_control: 'inspect_layers → set_holographic_layer → inspect_layers',
708
+ audio_reactive: 'configure_audio_band → apply_behavior_preset → describe_visual_state'
607
709
  },
608
710
 
609
- canvas_layers: {
610
- count: 5,
611
- names: ['background', 'shadow', 'content', 'highlight', 'accent']
612
- },
711
+ // Geometry quick-reference
712
+ geometry_map: {
713
+ 'sphere': 2, 'hypersphere+sphere': 10, 'hypertetra+sphere': 18,
714
+ 'torus': 3, 'hypersphere+torus': 11, 'hypertetra+torus': 19,
715
+ 'fractal': 5, 'hypersphere+fractal': 13, 'hypertetra+fractal': 21,
716
+ 'crystal': 7, 'wave': 6, 'klein_bottle': 4, 'hypercube': 1, 'tetrahedron': 0
717
+ }
718
+ };
613
719
 
614
- knowledge_quiz: {
615
- IMPORTANT: 'Call verify_knowledge with multiple choice answers (a/b/c/d) to confirm understanding',
616
- questions: [
617
- 'Q1: How many rotation planes? a)3 b)4 c)6 d)8',
618
- 'Q2: Geometry encoding formula? a)base*3+core b)core*8+base c)base+core d)core*base',
619
- 'Q3: Canvas layers per system? a)3 b)4 c)5 d)6',
620
- 'Q4: Which are the 3 ACTIVE systems? a)quantum,faceted,holographic b)quantum,faceted,polychora c)all four d)none',
621
- 'Q5: How many base geometry types? a)6 b)8 c)10 d)24',
622
- 'Q6: Core warp types? a)base,sphere,cube b)base,hypersphere,hypertetrahedron c)2D,3D,4D d)none'
623
- ]
624
- },
720
+ if (include_state) {
721
+ context.current_state = this.getState();
722
+ }
625
723
 
626
- documentation: {
627
- primary: 'DOCS/SYSTEM_INVENTORY.md',
628
- geometry: '24-GEOMETRY-6D-ROTATION-SUMMARY.md',
629
- controls: 'DOCS/CONTROL_REFERENCE.md',
630
- cli: 'DOCS/CLI_ONBOARDING.md'
631
- },
724
+ if (include_tools) {
725
+ context.tool_summary = Object.entries(toolDefinitions).map(([name, def]) => ({
726
+ name,
727
+ description: def.description.split('.')[0] // First sentence only
728
+ }));
729
+ }
632
730
 
633
- suggested_next_actions: ['verify_knowledge', 'create_4d_visualization', 'search_geometries']
634
- };
731
+ return context;
635
732
  }
636
733
 
637
734
  /**
638
- * Verify agent knowledge of SDK (multiple choice)
735
+ * Inspect holographic layer state returns per-layer metadata in JSON format.
736
+ * This replaces the old verify_knowledge quiz with something actually useful.
639
737
  */
640
- verifyKnowledge(answers) {
641
- const correctAnswers = {
642
- q1_rotation_planes: 'c', // 6 rotation planes
643
- q2_geometry_formula: 'b', // core*8+base
644
- q3_canvas_layers: 'c', // 5 layers
645
- q4_active_systems: 'a', // quantum, faceted, holographic (polychora is TBD)
646
- q5_base_geometries: 'b', // 8 base geometries
647
- q6_core_types: 'b' // base, hypersphere, hypertetrahedron
648
- };
738
+ inspectLayers(args = {}) {
739
+ const { layer = 'all' } = args;
649
740
 
650
- const docReferences = {
651
- q1_rotation_planes: {
652
- topic: '6D ROTATION SYSTEM',
653
- doc: 'DOCS/SYSTEM_INVENTORY.md#the-6d-rotation-system',
654
- reason: '6 planes: XY, XZ, YZ (3D) + XW, YW, ZW (4D hyperspace)'
655
- },
656
- q2_geometry_formula: {
657
- topic: 'GEOMETRY ENCODING',
658
- doc: '24-GEOMETRY-6D-ROTATION-SUMMARY.md',
659
- reason: 'geometry = coreIndex * 8 + baseIndex. Example: 10 = 1*8+2 = hypersphere+sphere'
660
- },
661
- q3_canvas_layers: {
662
- topic: 'CANVAS LAYER SYSTEM',
663
- doc: 'DOCS/SYSTEM_INVENTORY.md#the-4-visualization-systems',
664
- reason: '5 layers: background, shadow, content, highlight, accent'
665
- },
666
- q4_active_systems: {
667
- topic: 'ACTIVE VS PLACEHOLDER SYSTEMS',
668
- doc: 'DOCS/SYSTEM_INVENTORY.md',
669
- reason: 'Only 3 ACTIVE: quantum, faceted, holographic. Polychora is TBD/placeholder!'
670
- },
671
- q5_base_geometries: {
672
- topic: 'BASE GEOMETRY TYPES',
673
- doc: '24-GEOMETRY-6D-ROTATION-SUMMARY.md',
674
- reason: '8 base: tetrahedron, hypercube, sphere, torus, klein, fractal, wave, crystal'
675
- },
676
- q6_core_types: {
677
- topic: 'CORE WARP TYPES',
678
- doc: '24-GEOMETRY-6D-ROTATION-SUMMARY.md',
679
- reason: '3 cores: base (no warp), hypersphere, hypertetrahedron'
680
- }
741
+ const systemName = this.engine?.currentSystemName || 'unknown';
742
+ const isHolographic = systemName === 'holographic';
743
+
744
+ if (!isHolographic) {
745
+ return {
746
+ system: systemName,
747
+ note: 'Layer inspection is most detailed for the holographic system (5 independent canvas layers). Switch with switch_system("holographic").',
748
+ layers: [{
749
+ role: 'main',
750
+ system: systemName,
751
+ opacity: 1.0,
752
+ enabled: true,
753
+ description: `Single canvas for ${systemName} system`
754
+ }],
755
+ suggested_next_actions: ['switch_system', 'describe_visual_state']
756
+ };
757
+ }
758
+
759
+ // Build layer metadata from the holographic system
760
+ const system = this.engine?.currentSystem;
761
+ const layerRoles = ['background', 'shadow', 'content', 'highlight', 'accent'];
762
+ const defaultConfigs = {
763
+ background: { densityMult: 0.4, speedMult: 0.2, colorShift: 0, intensity: 0.2, reactivity: 0.5 },
764
+ shadow: { densityMult: 0.8, speedMult: 0.3, colorShift: 180, intensity: 0.4, reactivity: 0.7 },
765
+ content: { densityMult: 1.0, speedMult: 1.0, colorShift: 0, intensity: 1.0, reactivity: 0.9 },
766
+ highlight: { densityMult: 1.5, speedMult: 0.8, colorShift: 60, intensity: 0.6, reactivity: 1.1 },
767
+ accent: { densityMult: 2.5, speedMult: 0.4, colorShift: 300, intensity: 0.3, reactivity: 1.5 }
681
768
  };
682
769
 
683
- const results = {
684
- score: 0,
685
- max_score: 6,
686
- details: [],
687
- REVIEW_REQUIRED: []
770
+ const buildLayerInfo = (role) => {
771
+ const config = defaultConfigs[role] || {};
772
+ const visualizer = system?.visualizers?.find?.(v => v?.role === role);
773
+ const overrides = this._layerOverrides?.get(role) || {};
774
+
775
+ return {
776
+ role,
777
+ enabled: overrides.enabled !== undefined ? overrides.enabled : true,
778
+ opacity: overrides.opacity !== undefined ? overrides.opacity : 1.0,
779
+ blendMode: overrides.blendMode || 'normal',
780
+ densityMult: overrides.densityMult ?? config.densityMult,
781
+ speedMult: overrides.speedMult ?? config.speedMult,
782
+ colorShift: overrides.colorShift ?? config.colorShift,
783
+ intensity: overrides.intensity ?? config.intensity,
784
+ reactivity: overrides.reactivity ?? config.reactivity,
785
+ has_visualizer: !!visualizer
786
+ };
688
787
  };
689
788
 
690
- // Check each answer
691
- for (const [question, correct] of Object.entries(correctAnswers)) {
692
- const given = answers[question]?.toLowerCase?.() || answers[question];
693
- if (given === correct) {
694
- results.score++;
695
- results.details.push({ question, status: '✓ CORRECT' });
696
- } else if (given !== undefined) {
697
- results.details.push({
698
- question,
699
- status: '✗ WRONG',
700
- your_answer: given,
701
- correct_answer: correct
702
- });
703
- results.REVIEW_REQUIRED.push(docReferences[question]);
704
- }
705
- }
789
+ const layers = layer === 'all'
790
+ ? layerRoles.map(buildLayerInfo)
791
+ : [buildLayerInfo(layer)];
792
+
793
+ return {
794
+ system: 'holographic',
795
+ layer_count: layerRoles.length,
796
+ layers,
797
+ suggested_next_actions: ['set_holographic_layer', 'batch_set_parameters', 'describe_visual_state']
798
+ };
799
+ }
706
800
 
707
- results.percentage = Math.round((results.score / results.max_score) * 100);
801
+ /**
802
+ * Set properties on an individual holographic layer.
803
+ * Stores overrides in a layer override map and applies them to the visualizer.
804
+ */
805
+ setHolographicLayer(args) {
806
+ const { layer, opacity, blendMode, enabled, colorShift, densityMult, speedMult, reactivity } = args;
708
807
 
709
- // Build response
710
- if (results.REVIEW_REQUIRED.length > 0) {
711
- results.MESSAGE = `Score: ${results.score}/${results.max_score}. YOU MAY PROCEED but PLEASE review the topics below to avoid errors.`;
712
- results.URGENT = results.REVIEW_REQUIRED.map(ref => ({
713
- TOPIC: ref.topic,
714
- READ: ref.doc,
715
- WHY: ref.reason
716
- }));
717
- } else {
718
- results.MESSAGE = `PERFECT SCORE! You understand the VIB3+ SDK architecture.`;
808
+ const systemName = this.engine?.currentSystemName || 'unknown';
809
+ if (systemName !== 'holographic') {
810
+ return {
811
+ error: {
812
+ type: 'SystemError',
813
+ code: 'NOT_HOLOGRAPHIC',
814
+ message: `set_holographic_layer requires holographic system (current: ${systemName})`,
815
+ suggestion: 'Call switch_system("holographic") first'
816
+ }
817
+ };
719
818
  }
720
819
 
721
- results.suggested_next_actions = ['create_4d_visualization', 'get_state', 'search_geometries'];
820
+ // Initialize layer override storage
821
+ if (!this._layerOverrides) this._layerOverrides = new Map();
822
+ const existing = this._layerOverrides.get(layer) || {};
823
+ const updates = {};
824
+
825
+ if (opacity !== undefined) { existing.opacity = opacity; updates.opacity = opacity; }
826
+ if (blendMode !== undefined) { existing.blendMode = blendMode; updates.blendMode = blendMode; }
827
+ if (enabled !== undefined) { existing.enabled = enabled; updates.enabled = enabled; }
828
+ if (colorShift !== undefined) { existing.colorShift = colorShift; updates.colorShift = colorShift; }
829
+ if (densityMult !== undefined) { existing.densityMult = densityMult; updates.densityMult = densityMult; }
830
+ if (speedMult !== undefined) { existing.speedMult = speedMult; updates.speedMult = speedMult; }
831
+ if (reactivity !== undefined) { existing.reactivity = reactivity; updates.reactivity = reactivity; }
832
+
833
+ this._layerOverrides.set(layer, existing);
834
+
835
+ // Apply to visualizer if available
836
+ const system = this.engine?.currentSystem;
837
+ const visualizer = system?.visualizers?.find?.(v => v?.role === layer);
838
+ if (visualizer) {
839
+ if (opacity !== undefined && visualizer.canvas) {
840
+ visualizer.canvas.style.opacity = String(opacity);
841
+ }
842
+ if (blendMode !== undefined && visualizer.canvas) {
843
+ visualizer.canvas.style.mixBlendMode = blendMode;
844
+ }
845
+ if (enabled !== undefined && visualizer.canvas) {
846
+ visualizer.canvas.style.display = enabled ? '' : 'none';
847
+ }
848
+ if (colorShift !== undefined && visualizer.roleParams) {
849
+ visualizer.roleParams.colorShift = colorShift;
850
+ }
851
+ if (densityMult !== undefined && visualizer.roleParams) {
852
+ visualizer.roleParams.densityMult = densityMult;
853
+ }
854
+ if (speedMult !== undefined && visualizer.roleParams) {
855
+ visualizer.roleParams.speedMult = speedMult;
856
+ }
857
+ if (reactivity !== undefined) {
858
+ visualizer.reactivity = reactivity;
859
+ }
860
+ }
722
861
 
723
- return results;
862
+ return {
863
+ layer,
864
+ applied: updates,
865
+ current_state: this.inspectLayers({ layer }).layers[0],
866
+ suggested_next_actions: ['inspect_layers', 'set_holographic_layer', 'describe_visual_state']
867
+ };
724
868
  }
725
869
 
726
870
  /**
@@ -1200,8 +1344,17 @@ export class MCPServer {
1200
1344
  (sum, step) => sum + step.duration + step.delay, 0
1201
1345
  );
1202
1346
 
1347
+ // Execute live if engine available
1348
+ let executing = false;
1349
+ const animator = this._getTransitionAnimator();
1350
+ if (animator) {
1351
+ const seqId = animator.sequence(normalizedSequence);
1352
+ executing = !!seqId;
1353
+ }
1354
+
1203
1355
  return {
1204
1356
  transition_id: transitionId,
1357
+ executing,
1205
1358
  step_count: normalizedSequence.length,
1206
1359
  total_duration_ms: totalDuration,
1207
1360
  steps: normalizedSequence.map((step, i) => ({
@@ -1211,7 +1364,7 @@ export class MCPServer {
1211
1364
  easing: step.easing,
1212
1365
  delay: step.delay
1213
1366
  })),
1214
- load_code: `const animator = new TransitionAnimator(\n (n, v) => engine.setParameter(n, v),\n (n) => engine.getParameter(n)\n);\nanimator.sequence(${JSON.stringify(normalizedSequence)});`,
1367
+ load_code: executing ? null : `const animator = new TransitionAnimator(\n (n, v) => engine.setParameter(n, v),\n (n) => engine.getParameter(n)\n);\nanimator.sequence(${JSON.stringify(normalizedSequence)});`,
1215
1368
  suggested_next_actions: ['describe_visual_state', 'create_timeline', 'save_to_gallery']
1216
1369
  };
1217
1370
  }
@@ -1220,55 +1373,41 @@ export class MCPServer {
1220
1373
  * Apply a named color preset
1221
1374
  */
1222
1375
  applyColorPreset(args) {
1223
- const { preset } = args;
1376
+ const { preset, transition = true, duration = 800 } = args;
1224
1377
 
1225
- // Color preset hue/saturation mappings (subset — full list in ColorPresetsSystem)
1226
- const COLOR_PRESETS = {
1227
- Ocean: { hue: 200, saturation: 0.8, intensity: 0.6 },
1228
- Lava: { hue: 15, saturation: 0.9, intensity: 0.8 },
1229
- Neon: { hue: 300, saturation: 1.0, intensity: 0.9 },
1230
- Monochrome: { hue: 0, saturation: 0.0, intensity: 0.6 },
1231
- Sunset: { hue: 30, saturation: 0.85, intensity: 0.7 },
1232
- Aurora: { hue: 140, saturation: 0.7, intensity: 0.6 },
1233
- Cyberpunk: { hue: 280, saturation: 0.9, intensity: 0.8 },
1234
- Forest: { hue: 120, saturation: 0.6, intensity: 0.5 },
1235
- Desert: { hue: 40, saturation: 0.5, intensity: 0.7 },
1236
- Galaxy: { hue: 260, saturation: 0.8, intensity: 0.4 },
1237
- Ice: { hue: 190, saturation: 0.5, intensity: 0.8 },
1238
- Fire: { hue: 10, saturation: 1.0, intensity: 0.9 },
1239
- Toxic: { hue: 100, saturation: 0.9, intensity: 0.7 },
1240
- Royal: { hue: 270, saturation: 0.7, intensity: 0.5 },
1241
- Pastel: { hue: 330, saturation: 0.3, intensity: 0.8 },
1242
- Retro: { hue: 50, saturation: 0.7, intensity: 0.6 },
1243
- Midnight: { hue: 240, saturation: 0.6, intensity: 0.3 },
1244
- Tropical: { hue: 160, saturation: 0.8, intensity: 0.7 },
1245
- Ethereal: { hue: 220, saturation: 0.4, intensity: 0.7 },
1246
- Volcanic: { hue: 5, saturation: 0.95, intensity: 0.6 },
1247
- Holographic: { hue: 180, saturation: 0.6, intensity: 0.8 },
1248
- Vaporwave: { hue: 310, saturation: 0.7, intensity: 0.7 }
1249
- };
1378
+ const colorSystem = this._getColorPresets();
1379
+
1380
+ if (colorSystem) {
1381
+ // Use real ColorPresetsSystem full preset library with transitions
1382
+ const config = colorSystem.getPreset(preset);
1383
+ if (!config) {
1384
+ const allPresets = colorSystem.getPresets().map(p => p.name);
1385
+ return {
1386
+ error: {
1387
+ type: 'ValidationError',
1388
+ code: 'INVALID_COLOR_PRESET',
1389
+ message: `Unknown color preset: ${preset}`,
1390
+ valid_options: allPresets
1391
+ }
1392
+ };
1393
+ }
1394
+
1395
+ colorSystem.applyPreset(preset, transition, duration);
1250
1396
 
1251
- const presetData = COLOR_PRESETS[preset];
1252
- if (!presetData) {
1253
1397
  return {
1254
- error: {
1255
- type: 'ValidationError',
1256
- code: 'INVALID_COLOR_PRESET',
1257
- message: `Unknown color preset: ${preset}`,
1258
- valid_options: Object.keys(COLOR_PRESETS)
1259
- }
1398
+ preset,
1399
+ applied: { hue: config.hue, saturation: config.saturation, intensity: config.intensity },
1400
+ transition: transition ? { enabled: true, duration } : { enabled: false },
1401
+ full_config: config,
1402
+ suggested_next_actions: ['set_post_processing', 'describe_visual_state', 'set_visual_parameters']
1260
1403
  };
1261
1404
  }
1262
1405
 
1263
- if (this.engine) {
1264
- this.engine.setParameter('hue', presetData.hue);
1265
- this.engine.setParameter('saturation', presetData.saturation);
1266
- this.engine.setParameter('intensity', presetData.intensity);
1267
- }
1268
-
1406
+ // Fallback: no engine, return preset metadata for artifact mode
1269
1407
  return {
1270
1408
  preset,
1271
- applied: presetData,
1409
+ applied: null,
1410
+ load_code: `const colors = new ColorPresetsSystem((n, v) => engine.setParameter(n, v));\ncolors.applyPreset('${preset}', ${transition}, ${duration});`,
1272
1411
  suggested_next_actions: ['set_post_processing', 'describe_visual_state', 'set_visual_parameters']
1273
1412
  };
1274
1413
  }
@@ -1279,14 +1418,50 @@ export class MCPServer {
1279
1418
  setPostProcessing(args) {
1280
1419
  const { effects, chain_preset, clear_first = true } = args;
1281
1420
 
1421
+ // Try to execute live in browser context
1422
+ let executing = false;
1423
+ if (typeof document !== 'undefined') {
1424
+ try {
1425
+ const target = document.getElementById('viz-container')
1426
+ || document.querySelector('.vib3-container')
1427
+ || document.querySelector('canvas')?.parentElement;
1428
+
1429
+ if (target) {
1430
+ // Lazy-init pipeline, importing dynamically to avoid Node.js issues
1431
+ if (!this._postPipeline) {
1432
+ // PostProcessingPipeline imported statically would fail in Node;
1433
+ // it's already a known browser-only module, so guard at runtime
1434
+ const { PostProcessingPipeline: PPP } = { PostProcessingPipeline: globalThis.PostProcessingPipeline };
1435
+ if (PPP) {
1436
+ this._postPipeline = new PPP(target);
1437
+ }
1438
+ }
1439
+
1440
+ if (this._postPipeline) {
1441
+ if (clear_first) this._postPipeline.clearChain?.();
1442
+ if (chain_preset) {
1443
+ this._postPipeline.loadPresetChain(chain_preset);
1444
+ } else if (effects) {
1445
+ for (const e of effects) {
1446
+ this._postPipeline.addEffect(e.name, { intensity: e.intensity || 0.5, ...e });
1447
+ }
1448
+ }
1449
+ this._postPipeline.apply();
1450
+ executing = true;
1451
+ }
1452
+ }
1453
+ } catch { /* fall through to code generation */ }
1454
+ }
1455
+
1282
1456
  return {
1283
1457
  applied: true,
1458
+ executing,
1284
1459
  effects: effects || [],
1285
1460
  chain_preset: chain_preset || null,
1286
1461
  cleared_previous: clear_first,
1287
- load_code: effects ?
1288
- `const pipeline = new PostProcessingPipeline(gl, canvas);\n${effects.map(e => `pipeline.addEffect('${e.name}', { intensity: ${e.intensity || 0.5} });`).join('\n')}` :
1289
- `pipeline.applyChain('${chain_preset}');`,
1462
+ load_code: executing ? null : (effects ?
1463
+ `const pipeline = new PostProcessingPipeline(document.getElementById('viz-container'));\n${effects.map(e => `pipeline.addEffect('${e.name}', { intensity: ${e.intensity || 0.5} });`).join('\n')}\npipeline.apply();` :
1464
+ `const pipeline = new PostProcessingPipeline(document.getElementById('viz-container'));\npipeline.loadPresetChain('${chain_preset}');\npipeline.apply();`),
1290
1465
  suggested_next_actions: ['describe_visual_state', 'apply_color_preset', 'create_choreography']
1291
1466
  };
1292
1467
  }
@@ -1665,6 +1840,185 @@ export class MCPServer {
1665
1840
  suggested_next_actions: ['control_timeline', 'describe_visual_state', 'capture_screenshot']
1666
1841
  };
1667
1842
  }
1843
+
1844
+ // ====================================================================
1845
+ // Layer Relationship Tools (Phase 8)
1846
+ // ====================================================================
1847
+
1848
+ /**
1849
+ * Get the holographic system's layer graph (if available).
1850
+ * @private
1851
+ * @returns {import('../../render/LayerRelationshipGraph.js').LayerRelationshipGraph|null}
1852
+ */
1853
+ _getLayerGraph() {
1854
+ if (!this.engine) return null;
1855
+ // Try to access the current system's layer graph
1856
+ const system = this.engine.currentSystem || this.engine._activeSystem;
1857
+ if (system && system.layerGraph) {
1858
+ return system.layerGraph;
1859
+ }
1860
+ if (system && system._layerGraph) {
1861
+ return system._layerGraph;
1862
+ }
1863
+ return null;
1864
+ }
1865
+
1866
+ /**
1867
+ * Load a named layer relationship profile.
1868
+ */
1869
+ setLayerProfile(args) {
1870
+ const { profile } = args;
1871
+ const graph = this._getLayerGraph();
1872
+
1873
+ if (!graph) {
1874
+ return {
1875
+ error: 'Layer relationship graph not available. Switch to holographic system first.',
1876
+ suggested_next_actions: ['switch_system']
1877
+ };
1878
+ }
1879
+
1880
+ graph.loadProfile(profile);
1881
+ telemetry.recordEvent(EventType.PARAMETER_CHANGE, { type: 'layer_profile', profile });
1882
+
1883
+ return {
1884
+ profile,
1885
+ keystone: graph.keystone,
1886
+ active_profile: graph.activeProfile,
1887
+ available_profiles: ['holographic', 'symmetry', 'chord', 'storm', 'legacy'],
1888
+ suggested_next_actions: ['get_layer_config', 'set_layer_relationship', 'tune_layer_relationship']
1889
+ };
1890
+ }
1891
+
1892
+ /**
1893
+ * Set relationship for a specific layer.
1894
+ */
1895
+ setLayerRelationship(args) {
1896
+ const { layer, relationship, config } = args;
1897
+ const graph = this._getLayerGraph();
1898
+
1899
+ if (!graph) {
1900
+ return {
1901
+ error: 'Layer relationship graph not available. Switch to holographic system first.',
1902
+ suggested_next_actions: ['switch_system']
1903
+ };
1904
+ }
1905
+
1906
+ if (config) {
1907
+ graph.setRelationship(layer, { preset: relationship, config });
1908
+ } else {
1909
+ graph.setRelationship(layer, relationship);
1910
+ }
1911
+
1912
+ telemetry.recordEvent(EventType.PARAMETER_CHANGE, {
1913
+ type: 'layer_relationship', layer, relationship
1914
+ });
1915
+
1916
+ return {
1917
+ layer,
1918
+ relationship,
1919
+ config: config || {},
1920
+ keystone: graph.keystone,
1921
+ suggested_next_actions: ['get_layer_config', 'tune_layer_relationship', 'describe_visual_state']
1922
+ };
1923
+ }
1924
+
1925
+ /**
1926
+ * Change the keystone (driver) layer.
1927
+ */
1928
+ setLayerKeystone(args) {
1929
+ const { layer } = args;
1930
+ const graph = this._getLayerGraph();
1931
+
1932
+ if (!graph) {
1933
+ return {
1934
+ error: 'Layer relationship graph not available. Switch to holographic system first.',
1935
+ suggested_next_actions: ['switch_system']
1936
+ };
1937
+ }
1938
+
1939
+ graph.setKeystone(layer);
1940
+ telemetry.recordEvent(EventType.PARAMETER_CHANGE, { type: 'layer_keystone', layer });
1941
+
1942
+ return {
1943
+ keystone: layer,
1944
+ note: 'Other layers\' relationships are preserved. Set new relationships for the old keystone if needed.',
1945
+ suggested_next_actions: ['set_layer_relationship', 'get_layer_config']
1946
+ };
1947
+ }
1948
+
1949
+ /**
1950
+ * Get current layer configuration.
1951
+ */
1952
+ getLayerConfig() {
1953
+ const graph = this._getLayerGraph();
1954
+
1955
+ if (!graph) {
1956
+ return {
1957
+ error: 'Layer relationship graph not available. Switch to holographic system first.',
1958
+ suggested_next_actions: ['switch_system']
1959
+ };
1960
+ }
1961
+
1962
+ const config = graph.exportConfig();
1963
+
1964
+ return {
1965
+ keystone: config.keystone,
1966
+ active_profile: config.profile,
1967
+ relationships: config.relationships,
1968
+ shaders: config.shaders,
1969
+ available_profiles: ['holographic', 'symmetry', 'chord', 'storm', 'legacy'],
1970
+ available_presets: ['echo', 'mirror', 'complement', 'harmonic', 'reactive', 'chase'],
1971
+ suggested_next_actions: ['set_layer_profile', 'set_layer_relationship', 'tune_layer_relationship']
1972
+ };
1973
+ }
1974
+
1975
+ /**
1976
+ * Tune a layer's relationship config.
1977
+ */
1978
+ tuneLayerRelationship(args) {
1979
+ const { layer, config: configOverrides } = args;
1980
+ const graph = this._getLayerGraph();
1981
+
1982
+ if (!graph) {
1983
+ return {
1984
+ error: 'Layer relationship graph not available. Switch to holographic system first.',
1985
+ suggested_next_actions: ['switch_system']
1986
+ };
1987
+ }
1988
+
1989
+ const graphConfig = graph.exportConfig();
1990
+ const currentRel = graphConfig.relationships[layer];
1991
+
1992
+ if (!currentRel || !currentRel.preset) {
1993
+ return {
1994
+ error: `Layer "${layer}" has no tunable preset relationship. Set one first with set_layer_relationship.`,
1995
+ suggested_next_actions: ['set_layer_relationship']
1996
+ };
1997
+ }
1998
+
1999
+ const factory = PRESET_REGISTRY[currentRel.preset];
2000
+ if (!factory) {
2001
+ return {
2002
+ error: `Unknown preset "${currentRel.preset}" on layer "${layer}".`,
2003
+ suggested_next_actions: ['set_layer_relationship']
2004
+ };
2005
+ }
2006
+
2007
+ const newConfig = { ...(currentRel.config || {}), ...configOverrides };
2008
+ graph.setRelationship(layer, { preset: currentRel.preset, config: newConfig });
2009
+
2010
+ telemetry.recordEvent(EventType.PARAMETER_CHANGE, {
2011
+ type: 'layer_tune', layer, tuned_keys: Object.keys(configOverrides)
2012
+ });
2013
+
2014
+ return {
2015
+ layer,
2016
+ preset: currentRel.preset,
2017
+ previous_config: currentRel.config,
2018
+ new_config: newConfig,
2019
+ suggested_next_actions: ['get_layer_config', 'describe_visual_state', 'capture_screenshot']
2020
+ };
2021
+ }
1668
2022
  }
1669
2023
 
1670
2024
  // Singleton instance