@vib3code/sdk 2.0.3-canary.91a95f3 → 2.0.3-canary.ef8d292

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 (74) hide show
  1. package/DOCS/MASTER_PLAN_2026-01-31.md +2 -2
  2. package/DOCS/SYSTEM_INVENTORY.md +2 -2
  3. package/DOCS/WEBGPU_STATUS.md +119 -38
  4. package/DOCS/archive/WEBGPU_STATUS_2026-02-15_STALE.md +38 -0
  5. package/DOCS/dev-tracks/DEV_TRACK_SESSION_2026-02-13.md +13 -0
  6. package/DOCS/dev-tracks/DEV_TRACK_SESSION_2026-02-15.md +142 -0
  7. package/DOCS/dev-tracks/DEV_TRACK_SESSION_2026-02-16.md +108 -0
  8. package/docs/webgpu-live.html +1 -1
  9. package/package.json +10 -1
  10. package/src/agent/index.js +1 -3
  11. package/src/agent/mcp/MCPServer.js +347 -52
  12. package/src/agent/mcp/index.js +1 -1
  13. package/src/agent/mcp/tools.js +87 -0
  14. package/src/cli/index.js +374 -44
  15. package/src/core/VIB3Engine.js +55 -3
  16. package/src/core/index.js +18 -0
  17. package/src/core/renderers/FacetedRendererAdapter.js +10 -9
  18. package/src/core/renderers/HolographicRendererAdapter.js +11 -7
  19. package/src/core/renderers/QuantumRendererAdapter.js +11 -7
  20. package/src/creative/index.js +11 -0
  21. package/src/export/index.js +11 -1
  22. package/src/faceted/FacetedSystem.js +27 -10
  23. package/src/games/glyph-war/GlyphWarVisualizer.js +641 -0
  24. package/src/holograms/HolographicVisualizer.js +58 -89
  25. package/src/holograms/RealHolographicSystem.js +126 -31
  26. package/src/math/Mat4x4.js +70 -13
  27. package/src/math/Rotor4D.js +100 -39
  28. package/src/math/index.js +7 -7
  29. package/src/quantum/QuantumVisualizer.js +24 -20
  30. package/src/reactivity/index.js +3 -5
  31. package/src/render/LayerPresetManager.js +372 -0
  32. package/src/render/LayerReactivityBridge.js +344 -0
  33. package/src/render/LayerRelationshipGraph.js +610 -0
  34. package/src/render/MultiCanvasBridge.js +148 -25
  35. package/src/render/ShaderLoader.js +38 -0
  36. package/src/render/ShaderProgram.js +4 -4
  37. package/src/render/UnifiedRenderBridge.js +1 -1
  38. package/src/render/backends/WebGPUBackend.js +8 -4
  39. package/src/render/index.js +27 -2
  40. package/src/scene/index.js +4 -4
  41. package/src/shaders/common/geometry24.glsl +65 -0
  42. package/src/shaders/common/geometry24.wgsl +54 -0
  43. package/src/shaders/common/rotation4d.glsl +4 -4
  44. package/src/shaders/common/rotation4d.wgsl +2 -2
  45. package/src/shaders/common/uniforms.wgsl +15 -8
  46. package/src/shaders/faceted/faceted.frag.wgsl +19 -6
  47. package/src/shaders/holographic/holographic.frag.wgsl +7 -5
  48. package/src/shaders/quantum/quantum.frag.wgsl +7 -5
  49. package/src/testing/ParallelTestFramework.js +2 -2
  50. package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +2 -2
  51. package/src/viewer/GalleryUI.js +17 -0
  52. package/src/viewer/ViewerPortal.js +2 -2
  53. package/tools/shader-sync-verify.js +6 -4
  54. package/types/adaptive-sdk.d.ts +204 -5
  55. package/types/agent/cli.d.ts +78 -0
  56. package/types/agent/index.d.ts +18 -0
  57. package/types/agent/mcp.d.ts +87 -0
  58. package/types/agent/telemetry.d.ts +190 -0
  59. package/types/core/VIB3Engine.d.ts +26 -0
  60. package/types/core/index.d.ts +261 -0
  61. package/types/creative/AestheticMapper.d.ts +72 -0
  62. package/types/creative/ChoreographyPlayer.d.ts +96 -0
  63. package/types/creative/index.d.ts +17 -0
  64. package/types/export/index.d.ts +243 -0
  65. package/types/geometry/index.d.ts +164 -0
  66. package/types/math/index.d.ts +214 -0
  67. package/types/render/LayerPresetManager.d.ts +78 -0
  68. package/types/render/LayerReactivityBridge.d.ts +85 -0
  69. package/types/render/LayerRelationshipGraph.d.ts +174 -0
  70. package/types/render/index.d.ts +3 -0
  71. package/types/scene/index.d.ts +204 -0
  72. package/types/systems/index.d.ts +244 -0
  73. package/types/variations/index.d.ts +62 -0
  74. 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 = {}) {
@@ -204,6 +237,22 @@ export class MCPServer {
204
237
  case 'control_timeline':
205
238
  result = this.controlTimeline(args);
206
239
  break;
240
+ // Layer relationship tools (Phase 8)
241
+ case 'set_layer_profile':
242
+ result = this.setLayerProfile(args);
243
+ break;
244
+ case 'set_layer_relationship':
245
+ result = this.setLayerRelationship(args);
246
+ break;
247
+ case 'set_layer_keystone':
248
+ result = this.setLayerKeystone(args);
249
+ break;
250
+ case 'get_layer_config':
251
+ result = this.getLayerConfig();
252
+ break;
253
+ case 'tune_layer_relationship':
254
+ result = this.tuneLayerRelationship(args);
255
+ break;
207
256
  default:
208
257
  throw new Error(`Unknown tool: ${toolName}`);
209
258
  }
@@ -467,32 +516,68 @@ export class MCPServer {
467
516
 
468
517
  telemetry.recordEvent(EventType.GALLERY_SAVE, { slot });
469
518
 
519
+ // Persist actual engine state if available
520
+ if (this.engine) {
521
+ const state = this.engine.exportState();
522
+ this._gallerySlots.set(slot, {
523
+ name: name || `Variation ${slot}`,
524
+ saved_at: new Date().toISOString(),
525
+ state
526
+ });
527
+ }
528
+
470
529
  return {
471
530
  slot,
472
531
  name: name || `Variation ${slot}`,
473
532
  saved_at: new Date().toISOString(),
533
+ persisted: !!this.engine,
534
+ gallery_size: this._gallerySlots.size,
474
535
  suggested_next_actions: ['load_from_gallery', 'randomize_parameters']
475
536
  };
476
537
  }
477
538
 
478
539
  /**
479
- * Load from gallery
540
+ * Load from gallery — restores previously saved state
480
541
  */
481
542
  loadFromGallery(args) {
482
543
  const { slot } = args;
483
544
 
484
- if (this.engine) {
485
- // Apply variation
486
- const params = this.engine.parameters?.generateVariationParameters?.(slot) || {};
487
- this.engine.setParameters(params);
545
+ telemetry.recordEvent(EventType.GALLERY_LOAD, { slot });
546
+
547
+ const saved = this._gallerySlots.get(slot);
548
+ if (saved && this.engine) {
549
+ // Restore saved state
550
+ this.engine.importState(saved.state);
551
+ return {
552
+ slot,
553
+ name: saved.name,
554
+ saved_at: saved.saved_at,
555
+ loaded_at: new Date().toISOString(),
556
+ restored: true,
557
+ ...this.getState()
558
+ };
488
559
  }
489
560
 
490
- telemetry.recordEvent(EventType.GALLERY_LOAD, { slot });
561
+ if (!saved) {
562
+ // No saved state — fall back to random variation
563
+ if (this.engine) {
564
+ const params = this.engine.parameters?.generateVariationParameters?.(slot) || {};
565
+ this.engine.setParameters(params);
566
+ }
567
+ return {
568
+ slot,
569
+ loaded_at: new Date().toISOString(),
570
+ restored: false,
571
+ note: 'No saved state in this slot — generated random variation',
572
+ ...this.getState()
573
+ };
574
+ }
491
575
 
492
576
  return {
493
577
  slot,
494
578
  loaded_at: new Date().toISOString(),
495
- ...this.getState()
579
+ restored: false,
580
+ note: 'Engine not initialized — cannot apply state'
496
581
  };
497
582
  }
498
583
 
@@ -1200,8 +1285,17 @@ export class MCPServer {
1200
1285
  (sum, step) => sum + step.duration + step.delay, 0
1201
1286
  );
1202
1287
 
1288
+ // Execute live if engine available
1289
+ let executing = false;
1290
+ const animator = this._getTransitionAnimator();
1291
+ if (animator) {
1292
+ const seqId = animator.sequence(normalizedSequence);
1293
+ executing = !!seqId;
1294
+ }
1295
+
1203
1296
  return {
1204
1297
  transition_id: transitionId,
1298
+ executing,
1205
1299
  step_count: normalizedSequence.length,
1206
1300
  total_duration_ms: totalDuration,
1207
1301
  steps: normalizedSequence.map((step, i) => ({
@@ -1211,7 +1305,7 @@ export class MCPServer {
1211
1305
  easing: step.easing,
1212
1306
  delay: step.delay
1213
1307
  })),
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)});`,
1308
+ 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
1309
  suggested_next_actions: ['describe_visual_state', 'create_timeline', 'save_to_gallery']
1216
1310
  };
1217
1311
  }
@@ -1220,55 +1314,41 @@ export class MCPServer {
1220
1314
  * Apply a named color preset
1221
1315
  */
1222
1316
  applyColorPreset(args) {
1223
- const { preset } = args;
1317
+ const { preset, transition = true, duration = 800 } = args;
1224
1318
 
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
- };
1319
+ const colorSystem = this._getColorPresets();
1320
+
1321
+ if (colorSystem) {
1322
+ // Use real ColorPresetsSystem full preset library with transitions
1323
+ const config = colorSystem.getPreset(preset);
1324
+ if (!config) {
1325
+ const allPresets = colorSystem.getPresets().map(p => p.name);
1326
+ return {
1327
+ error: {
1328
+ type: 'ValidationError',
1329
+ code: 'INVALID_COLOR_PRESET',
1330
+ message: `Unknown color preset: ${preset}`,
1331
+ valid_options: allPresets
1332
+ }
1333
+ };
1334
+ }
1335
+
1336
+ colorSystem.applyPreset(preset, transition, duration);
1250
1337
 
1251
- const presetData = COLOR_PRESETS[preset];
1252
- if (!presetData) {
1253
1338
  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
- }
1339
+ preset,
1340
+ applied: { hue: config.hue, saturation: config.saturation, intensity: config.intensity },
1341
+ transition: transition ? { enabled: true, duration } : { enabled: false },
1342
+ full_config: config,
1343
+ suggested_next_actions: ['set_post_processing', 'describe_visual_state', 'set_visual_parameters']
1260
1344
  };
1261
1345
  }
1262
1346
 
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
-
1347
+ // Fallback: no engine, return preset metadata for artifact mode
1269
1348
  return {
1270
1349
  preset,
1271
- applied: presetData,
1350
+ applied: null,
1351
+ load_code: `const colors = new ColorPresetsSystem((n, v) => engine.setParameter(n, v));\ncolors.applyPreset('${preset}', ${transition}, ${duration});`,
1272
1352
  suggested_next_actions: ['set_post_processing', 'describe_visual_state', 'set_visual_parameters']
1273
1353
  };
1274
1354
  }
@@ -1279,14 +1359,50 @@ export class MCPServer {
1279
1359
  setPostProcessing(args) {
1280
1360
  const { effects, chain_preset, clear_first = true } = args;
1281
1361
 
1362
+ // Try to execute live in browser context
1363
+ let executing = false;
1364
+ if (typeof document !== 'undefined') {
1365
+ try {
1366
+ const target = document.getElementById('viz-container')
1367
+ || document.querySelector('.vib3-container')
1368
+ || document.querySelector('canvas')?.parentElement;
1369
+
1370
+ if (target) {
1371
+ // Lazy-init pipeline, importing dynamically to avoid Node.js issues
1372
+ if (!this._postPipeline) {
1373
+ // PostProcessingPipeline imported statically would fail in Node;
1374
+ // it's already a known browser-only module, so guard at runtime
1375
+ const { PostProcessingPipeline: PPP } = { PostProcessingPipeline: globalThis.PostProcessingPipeline };
1376
+ if (PPP) {
1377
+ this._postPipeline = new PPP(target);
1378
+ }
1379
+ }
1380
+
1381
+ if (this._postPipeline) {
1382
+ if (clear_first) this._postPipeline.clearChain?.();
1383
+ if (chain_preset) {
1384
+ this._postPipeline.loadPresetChain(chain_preset);
1385
+ } else if (effects) {
1386
+ for (const e of effects) {
1387
+ this._postPipeline.addEffect(e.name, { intensity: e.intensity || 0.5, ...e });
1388
+ }
1389
+ }
1390
+ this._postPipeline.apply();
1391
+ executing = true;
1392
+ }
1393
+ }
1394
+ } catch { /* fall through to code generation */ }
1395
+ }
1396
+
1282
1397
  return {
1283
1398
  applied: true,
1399
+ executing,
1284
1400
  effects: effects || [],
1285
1401
  chain_preset: chain_preset || null,
1286
1402
  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}');`,
1403
+ load_code: executing ? null : (effects ?
1404
+ `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();` :
1405
+ `const pipeline = new PostProcessingPipeline(document.getElementById('viz-container'));\npipeline.loadPresetChain('${chain_preset}');\npipeline.apply();`),
1290
1406
  suggested_next_actions: ['describe_visual_state', 'apply_color_preset', 'create_choreography']
1291
1407
  };
1292
1408
  }
@@ -1665,6 +1781,185 @@ export class MCPServer {
1665
1781
  suggested_next_actions: ['control_timeline', 'describe_visual_state', 'capture_screenshot']
1666
1782
  };
1667
1783
  }
1784
+
1785
+ // ====================================================================
1786
+ // Layer Relationship Tools (Phase 8)
1787
+ // ====================================================================
1788
+
1789
+ /**
1790
+ * Get the holographic system's layer graph (if available).
1791
+ * @private
1792
+ * @returns {import('../../render/LayerRelationshipGraph.js').LayerRelationshipGraph|null}
1793
+ */
1794
+ _getLayerGraph() {
1795
+ if (!this.engine) return null;
1796
+ // Try to access the current system's layer graph
1797
+ const system = this.engine.currentSystem || this.engine._activeSystem;
1798
+ if (system && system.layerGraph) {
1799
+ return system.layerGraph;
1800
+ }
1801
+ if (system && system._layerGraph) {
1802
+ return system._layerGraph;
1803
+ }
1804
+ return null;
1805
+ }
1806
+
1807
+ /**
1808
+ * Load a named layer relationship profile.
1809
+ */
1810
+ setLayerProfile(args) {
1811
+ const { profile } = args;
1812
+ const graph = this._getLayerGraph();
1813
+
1814
+ if (!graph) {
1815
+ return {
1816
+ error: 'Layer relationship graph not available. Switch to holographic system first.',
1817
+ suggested_next_actions: ['switch_system']
1818
+ };
1819
+ }
1820
+
1821
+ graph.loadProfile(profile);
1822
+ telemetry.recordEvent(EventType.PARAMETER_CHANGE, { type: 'layer_profile', profile });
1823
+
1824
+ return {
1825
+ profile,
1826
+ keystone: graph.keystone,
1827
+ active_profile: graph.activeProfile,
1828
+ available_profiles: ['holographic', 'symmetry', 'chord', 'storm', 'legacy'],
1829
+ suggested_next_actions: ['get_layer_config', 'set_layer_relationship', 'tune_layer_relationship']
1830
+ };
1831
+ }
1832
+
1833
+ /**
1834
+ * Set relationship for a specific layer.
1835
+ */
1836
+ setLayerRelationship(args) {
1837
+ const { layer, relationship, config } = args;
1838
+ const graph = this._getLayerGraph();
1839
+
1840
+ if (!graph) {
1841
+ return {
1842
+ error: 'Layer relationship graph not available. Switch to holographic system first.',
1843
+ suggested_next_actions: ['switch_system']
1844
+ };
1845
+ }
1846
+
1847
+ if (config) {
1848
+ graph.setRelationship(layer, { preset: relationship, config });
1849
+ } else {
1850
+ graph.setRelationship(layer, relationship);
1851
+ }
1852
+
1853
+ telemetry.recordEvent(EventType.PARAMETER_CHANGE, {
1854
+ type: 'layer_relationship', layer, relationship
1855
+ });
1856
+
1857
+ return {
1858
+ layer,
1859
+ relationship,
1860
+ config: config || {},
1861
+ keystone: graph.keystone,
1862
+ suggested_next_actions: ['get_layer_config', 'tune_layer_relationship', 'describe_visual_state']
1863
+ };
1864
+ }
1865
+
1866
+ /**
1867
+ * Change the keystone (driver) layer.
1868
+ */
1869
+ setLayerKeystone(args) {
1870
+ const { layer } = 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.setKeystone(layer);
1881
+ telemetry.recordEvent(EventType.PARAMETER_CHANGE, { type: 'layer_keystone', layer });
1882
+
1883
+ return {
1884
+ keystone: layer,
1885
+ note: 'Other layers\' relationships are preserved. Set new relationships for the old keystone if needed.',
1886
+ suggested_next_actions: ['set_layer_relationship', 'get_layer_config']
1887
+ };
1888
+ }
1889
+
1890
+ /**
1891
+ * Get current layer configuration.
1892
+ */
1893
+ getLayerConfig() {
1894
+ const graph = this._getLayerGraph();
1895
+
1896
+ if (!graph) {
1897
+ return {
1898
+ error: 'Layer relationship graph not available. Switch to holographic system first.',
1899
+ suggested_next_actions: ['switch_system']
1900
+ };
1901
+ }
1902
+
1903
+ const config = graph.exportConfig();
1904
+
1905
+ return {
1906
+ keystone: config.keystone,
1907
+ active_profile: config.profile,
1908
+ relationships: config.relationships,
1909
+ shaders: config.shaders,
1910
+ available_profiles: ['holographic', 'symmetry', 'chord', 'storm', 'legacy'],
1911
+ available_presets: ['echo', 'mirror', 'complement', 'harmonic', 'reactive', 'chase'],
1912
+ suggested_next_actions: ['set_layer_profile', 'set_layer_relationship', 'tune_layer_relationship']
1913
+ };
1914
+ }
1915
+
1916
+ /**
1917
+ * Tune a layer's relationship config.
1918
+ */
1919
+ tuneLayerRelationship(args) {
1920
+ const { layer, config: configOverrides } = args;
1921
+ const graph = this._getLayerGraph();
1922
+
1923
+ if (!graph) {
1924
+ return {
1925
+ error: 'Layer relationship graph not available. Switch to holographic system first.',
1926
+ suggested_next_actions: ['switch_system']
1927
+ };
1928
+ }
1929
+
1930
+ const graphConfig = graph.exportConfig();
1931
+ const currentRel = graphConfig.relationships[layer];
1932
+
1933
+ if (!currentRel || !currentRel.preset) {
1934
+ return {
1935
+ error: `Layer "${layer}" has no tunable preset relationship. Set one first with set_layer_relationship.`,
1936
+ suggested_next_actions: ['set_layer_relationship']
1937
+ };
1938
+ }
1939
+
1940
+ const factory = PRESET_REGISTRY[currentRel.preset];
1941
+ if (!factory) {
1942
+ return {
1943
+ error: `Unknown preset "${currentRel.preset}" on layer "${layer}".`,
1944
+ suggested_next_actions: ['set_layer_relationship']
1945
+ };
1946
+ }
1947
+
1948
+ const newConfig = { ...(currentRel.config || {}), ...configOverrides };
1949
+ graph.setRelationship(layer, { preset: currentRel.preset, config: newConfig });
1950
+
1951
+ telemetry.recordEvent(EventType.PARAMETER_CHANGE, {
1952
+ type: 'layer_tune', layer, tuned_keys: Object.keys(configOverrides)
1953
+ });
1954
+
1955
+ return {
1956
+ layer,
1957
+ preset: currentRel.preset,
1958
+ previous_config: currentRel.config,
1959
+ new_config: newConfig,
1960
+ suggested_next_actions: ['get_layer_config', 'describe_visual_state', 'capture_screenshot']
1961
+ };
1962
+ }
1668
1963
  }
1669
1964
 
1670
1965
  // Singleton instance
@@ -6,4 +6,4 @@
6
6
  export { MCPServer, mcpServer } from './MCPServer.js';
7
7
  export { toolDefinitions, getToolList, getToolNames, getTool, validateToolInput } from './tools.js';
8
8
 
9
- export default mcpServer from './MCPServer.js';
9
+ export { mcpServer as default } from './MCPServer.js';
@@ -762,6 +762,93 @@ export const toolDefinitions = {
762
762
  },
763
763
  required: ['timeline_id', 'action']
764
764
  }
765
+ },
766
+
767
+ // Layer Relationship Tools (Phase 8)
768
+ set_layer_profile: {
769
+ name: 'set_layer_profile',
770
+ description: 'Loads a named layer relationship profile that configures how the 5 canvas layers relate to each other. Profiles: holographic (default), symmetry, chord, storm, legacy (original static behavior).',
771
+ inputSchema: {
772
+ type: 'object',
773
+ properties: {
774
+ profile: {
775
+ type: 'string',
776
+ enum: ['holographic', 'symmetry', 'chord', 'storm', 'legacy'],
777
+ description: 'Named profile to load'
778
+ }
779
+ },
780
+ required: ['profile']
781
+ }
782
+ },
783
+
784
+ set_layer_relationship: {
785
+ name: 'set_layer_relationship',
786
+ description: 'Sets the relationship type for a specific layer relative to the keystone. Available relationships: echo (attenuated follower), mirror (inverted rotation/hue), complement (color opposite), harmonic (musical intervals), reactive (amplifies changes), chase (delayed follower).',
787
+ inputSchema: {
788
+ type: 'object',
789
+ properties: {
790
+ layer: {
791
+ type: 'string',
792
+ enum: ['background', 'shadow', 'content', 'highlight', 'accent'],
793
+ description: 'Target layer name'
794
+ },
795
+ relationship: {
796
+ type: 'string',
797
+ enum: ['echo', 'mirror', 'complement', 'harmonic', 'reactive', 'chase'],
798
+ description: 'Relationship preset name'
799
+ },
800
+ config: {
801
+ type: 'object',
802
+ description: 'Optional config overrides for the relationship (e.g., { opacity: 0.5, gain: 3 })'
803
+ }
804
+ },
805
+ required: ['layer', 'relationship']
806
+ }
807
+ },
808
+
809
+ set_layer_keystone: {
810
+ name: 'set_layer_keystone',
811
+ description: 'Changes which layer acts as the keystone (driver) for the layer relationship graph. Other layers derive their parameters from the keystone through relationship functions.',
812
+ inputSchema: {
813
+ type: 'object',
814
+ properties: {
815
+ layer: {
816
+ type: 'string',
817
+ enum: ['background', 'shadow', 'content', 'highlight', 'accent'],
818
+ description: 'Layer to designate as keystone'
819
+ }
820
+ },
821
+ required: ['layer']
822
+ }
823
+ },
824
+
825
+ get_layer_config: {
826
+ name: 'get_layer_config',
827
+ description: 'Returns the current layer relationship configuration including keystone, profile, and per-layer relationships with their config parameters.',
828
+ inputSchema: {
829
+ type: 'object',
830
+ properties: {}
831
+ }
832
+ },
833
+
834
+ tune_layer_relationship: {
835
+ name: 'tune_layer_relationship',
836
+ description: 'Hot-patches a layer relationship config without replacing the full graph. Merges provided config values into the existing relationship (e.g., increase reactive gain, shift harmonic hue angle).',
837
+ inputSchema: {
838
+ type: 'object',
839
+ properties: {
840
+ layer: {
841
+ type: 'string',
842
+ enum: ['background', 'shadow', 'content', 'highlight', 'accent'],
843
+ description: 'Layer to tune'
844
+ },
845
+ config: {
846
+ type: 'object',
847
+ description: 'Config values to merge (e.g., { opacity: 0.6, gain: 3.0, hueAngle: 120 })'
848
+ }
849
+ },
850
+ required: ['layer', 'config']
851
+ }
765
852
  }
766
853
  };
767
854