let-them-talk 5.3.0 → 5.4.1

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 (166) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/README.md +346 -592
  3. package/SECURITY.md +3 -3
  4. package/USAGE.md +151 -0
  5. package/agent-contracts.js +447 -0
  6. package/api-agents.js +760 -0
  7. package/autonomy/decision-v2.js +380 -0
  8. package/autonomy/watchdog-policy.js +572 -0
  9. package/cli.js +454 -298
  10. package/conversation-templates/autonomous-feature.json +83 -22
  11. package/conversation-templates/code-review.json +69 -21
  12. package/conversation-templates/debug-squad.json +69 -21
  13. package/conversation-templates/feature-build.json +69 -21
  14. package/conversation-templates/research-write.json +69 -21
  15. package/dashboard.html +3148 -174
  16. package/dashboard.js +864 -786
  17. package/data-dir.js +58 -0
  18. package/docs/architecture/branch-semantics.md +157 -0
  19. package/docs/architecture/canonical-event-schema.md +88 -0
  20. package/docs/architecture/markdown-workspace.md +183 -0
  21. package/docs/architecture/runtime-contract.md +459 -0
  22. package/docs/architecture/runtime-migration-hardening.md +64 -0
  23. package/events/hooks.js +154 -0
  24. package/events/log.js +457 -0
  25. package/events/replay.js +33 -0
  26. package/events/schema.js +432 -0
  27. package/managed-team-integration.js +261 -0
  28. package/office/agents.js +704 -597
  29. package/office/animation.js +1 -1
  30. package/office/assets/arcade-cabinet.js +141 -0
  31. package/office/assets/archway.js +77 -0
  32. package/office/assets/bar-counter.js +91 -0
  33. package/office/assets/bar-stool.js +71 -0
  34. package/office/assets/beanbag.js +64 -0
  35. package/office/assets/bench.js +99 -0
  36. package/office/assets/bollard.js +87 -0
  37. package/office/assets/cactus.js +100 -0
  38. package/office/assets/carpet-tile.js +46 -0
  39. package/office/assets/chair.js +123 -0
  40. package/office/assets/chandelier.js +107 -0
  41. package/office/assets/coffee-machine.js +95 -0
  42. package/office/assets/coffee-table.js +81 -0
  43. package/office/assets/column.js +95 -0
  44. package/office/assets/desk-lamp.js +102 -0
  45. package/office/assets/desk.js +76 -0
  46. package/office/assets/dining-table.js +105 -0
  47. package/office/assets/door.js +70 -0
  48. package/office/assets/dual-monitor.js +72 -0
  49. package/office/assets/fence.js +76 -0
  50. package/office/assets/filing-cabinet.js +111 -0
  51. package/office/assets/floor-lamp.js +69 -0
  52. package/office/assets/floor-tile.js +54 -0
  53. package/office/assets/flower-pot.js +76 -0
  54. package/office/assets/foosball.js +95 -0
  55. package/office/assets/fridge.js +99 -0
  56. package/office/assets/gaming-chair.js +154 -0
  57. package/office/assets/gaming-desk.js +105 -0
  58. package/office/assets/glass-door.js +72 -0
  59. package/office/assets/glass-wall.js +64 -0
  60. package/office/assets/half-wall.js +49 -0
  61. package/office/assets/hanging-plant.js +112 -0
  62. package/office/assets/index.js +151 -0
  63. package/office/assets/indoor-tree.js +90 -0
  64. package/office/assets/l-sofa.js +153 -0
  65. package/office/assets/marble-floor.js +64 -0
  66. package/office/assets/materials.js +40 -0
  67. package/office/assets/meeting-table.js +88 -0
  68. package/office/assets/microwave.js +94 -0
  69. package/office/assets/monitor.js +67 -0
  70. package/office/assets/neon-strip.js +73 -0
  71. package/office/assets/painting.js +84 -0
  72. package/office/assets/palm-tree.js +108 -0
  73. package/office/assets/pc-tower.js +91 -0
  74. package/office/assets/pendant-light.js +67 -0
  75. package/office/assets/ping-pong.js +114 -0
  76. package/office/assets/plant.js +72 -0
  77. package/office/assets/planter-box.js +95 -0
  78. package/office/assets/pool-table.js +94 -0
  79. package/office/assets/printer.js +113 -0
  80. package/office/assets/reception-desk.js +133 -0
  81. package/office/assets/rug.js +78 -0
  82. package/office/assets/sculpture.js +85 -0
  83. package/office/assets/server-rack.js +98 -0
  84. package/office/assets/sink.js +109 -0
  85. package/office/assets/sofa.js +106 -0
  86. package/office/assets/speaker.js +83 -0
  87. package/office/assets/spotlight.js +83 -0
  88. package/office/assets/street-lamp.js +97 -0
  89. package/office/assets/trash-can.js +83 -0
  90. package/office/assets/treadmill.js +126 -0
  91. package/office/assets/trophy.js +89 -0
  92. package/office/assets/tv-screen.js +79 -0
  93. package/office/assets/vase.js +84 -0
  94. package/office/assets/wall-clock.js +84 -0
  95. package/office/assets/wall.js +53 -0
  96. package/office/assets/water-cooler.js +146 -0
  97. package/office/assets/whiteboard.js +115 -0
  98. package/office/assets.js +3 -431
  99. package/office/builder.js +791 -355
  100. package/office/campus-env.js +1012 -1119
  101. package/office/environment.js +2 -0
  102. package/office/gallery.js +997 -0
  103. package/office/index.js +141 -34
  104. package/office/navigation.js +173 -152
  105. package/office/player.js +178 -68
  106. package/office/robot-character.js +272 -0
  107. package/office/spectator-camera.js +33 -10
  108. package/office/state.js +2 -0
  109. package/office/world-save.js +35 -4
  110. package/package.json +57 -3
  111. package/providers/comfyui.js +383 -0
  112. package/providers/dalle.js +79 -0
  113. package/providers/gemini.js +181 -0
  114. package/providers/ollama.js +184 -0
  115. package/providers/replicate.js +115 -0
  116. package/providers/zai.js +183 -0
  117. package/runtime-descriptor.js +270 -0
  118. package/scripts/check-agent-contract-advisory.js +132 -0
  119. package/scripts/check-api-agent-parity.js +277 -0
  120. package/scripts/check-autonomy-v2-decision.js +207 -0
  121. package/scripts/check-autonomy-v2-execution.js +588 -0
  122. package/scripts/check-autonomy-v2-watchdog.js +224 -0
  123. package/scripts/check-branch-fork-snapshot.js +337 -0
  124. package/scripts/check-branch-isolation.js +787 -0
  125. package/scripts/check-branch-semantics.js +139 -0
  126. package/scripts/check-dashboard-control-plane.js +1304 -0
  127. package/scripts/check-docs-onboarding.js +490 -0
  128. package/scripts/check-event-schema.js +276 -0
  129. package/scripts/check-evidence-completion.js +239 -0
  130. package/scripts/check-invariants.js +992 -0
  131. package/scripts/check-lifecycle-hooks.js +525 -0
  132. package/scripts/check-managed-team-integration.js +166 -0
  133. package/scripts/check-markdown-workspace-export.js +548 -0
  134. package/scripts/check-markdown-workspace-safety.js +347 -0
  135. package/scripts/check-markdown-workspace.js +136 -0
  136. package/scripts/check-message-replay.js +429 -0
  137. package/scripts/check-migration-hardening.js +300 -0
  138. package/scripts/check-performance-indexing.js +272 -0
  139. package/scripts/check-provider-capabilities.js +316 -0
  140. package/scripts/check-runtime-contract.js +109 -0
  141. package/scripts/check-session-aware-context.js +172 -0
  142. package/scripts/check-session-lifecycle.js +210 -0
  143. package/scripts/export-markdown-workspace.js +84 -0
  144. package/scripts/fixtures/message-replay/clean.jsonl +2 -0
  145. package/scripts/fixtures/message-replay/corrupt-correction-payload.jsonl +1 -0
  146. package/scripts/fixtures/message-replay/corrupt-jsonl.jsonl +1 -0
  147. package/scripts/fixtures/message-replay/corrupt-payload.jsonl +1 -0
  148. package/scripts/fixtures/message-replay/out-of-order.jsonl +2 -0
  149. package/scripts/migrate-legacy-to-canonical.js +201 -0
  150. package/scripts/run-verification-suite.js +242 -0
  151. package/scripts/sync-packaged-docs.js +69 -0
  152. package/server.js +9577 -7216
  153. package/state/agents.js +161 -0
  154. package/state/canonical.js +3068 -0
  155. package/state/dashboard-queries.js +441 -0
  156. package/state/evidence.js +56 -0
  157. package/state/io.js +69 -0
  158. package/state/markdown-workspace.js +951 -0
  159. package/state/messages.js +669 -0
  160. package/state/sessions.js +683 -0
  161. package/state/tasks-workflows.js +92 -0
  162. package/templates/debate.json +2 -2
  163. package/templates/managed.json +4 -4
  164. package/templates/pair.json +2 -2
  165. package/templates/review.json +2 -2
  166. package/templates/team.json +3 -3
package/office/index.js CHANGED
@@ -6,6 +6,8 @@ import { initScene } from './scene.js';
6
6
  import { buildEnvironment, updateTVScreen } from './environment.js';
7
7
  import { updateAgent } from './animation.js';
8
8
  import { syncAgents, processMessages, walkTo, navigateTo, showBubble } from './agents.js';
9
+ import { tickGallery, updateGalleryScreens } from './gallery.js';
10
+ import { updateRobotAnimation } from './robot-character.js';
9
11
  // Side-effect: registers window.officeGetAppearance
10
12
  import './appearance.js';
11
13
  import { spawnPlayer, despawnPlayer, isPlayerMode, updatePlayer, savePlayerAppearance, getPlayerAppearance, getPlayer, invalidateColliders } from './player.js';
@@ -29,6 +31,11 @@ function getCityMods() {
29
31
  function isDriving() { return _cityMods && _cityMods.vehicle && _cityMods.vehicle.isDriving(); }
30
32
  function isConnected() { return false; }
31
33
 
34
+ function scopedOfficeApiUrl(path, options) {
35
+ if (typeof window.scopedApiUrl === 'function') return window.scopedApiUrl(path, null, options);
36
+ return path;
37
+ }
38
+
32
39
  // Expose createCharacter + resolveAppearance for the character designer (Phase 3)
33
40
  export { createCharacter } from './character.js';
34
41
  export { resolveAppearance } from './appearance.js';
@@ -316,7 +323,7 @@ function executeCommand(agentName, action) {
316
323
  showInputOverlay('Send message to ' + agentName + ':', 'Type your message...', function(msg) {
317
324
  if (msg && msg.trim()) {
318
325
  showBubble(agent, 'Message incoming...');
319
- fetch('/api/inject' + (window.activeProject ? '?project=' + encodeURIComponent(window.activeProject) : ''), {
326
+ fetch(scopedOfficeApiUrl('/api/inject'), {
320
327
  method: 'POST',
321
328
  headers: { 'Content-Type': 'application/json', 'X-LTT-Request': '1' },
322
329
  body: JSON.stringify({ to: agentName, content: msg.trim() })
@@ -329,7 +336,7 @@ function executeCommand(agentName, action) {
329
336
  showInputOverlay('New task for ' + agentName + ':', 'Task title...', function(title) {
330
337
  if (title && title.trim()) {
331
338
  showBubble(agent, 'New task assigned!');
332
- fetch('/api/tasks' + (window.activeProject ? '?project=' + encodeURIComponent(window.activeProject) : ''), {
339
+ fetch(scopedOfficeApiUrl('/api/tasks'), {
333
340
  method: 'POST',
334
341
  headers: { 'Content-Type': 'application/json', 'X-LTT-Request': '1' },
335
342
  body: JSON.stringify({ title: title.trim(), assignee: agentName, status: 'pending' })
@@ -350,7 +357,7 @@ function executeCommand(agentName, action) {
350
357
 
351
358
  case 'nudge':
352
359
  showBubble(agent, 'Hey! Wake up!');
353
- fetch('/api/inject' + (window.activeProject ? '?project=' + encodeURIComponent(window.activeProject) : ''), {
360
+ fetch(scopedOfficeApiUrl('/api/inject'), {
354
361
  method: 'POST',
355
362
  headers: { 'Content-Type': 'application/json', 'X-LTT-Request': '1' },
356
363
  body: JSON.stringify({ to: agentName, content: 'Hey ' + agentName + ', the user is waiting for you. Please check for new messages and continue your work.' })
@@ -398,9 +405,15 @@ function animate() {
398
405
  var time = S.clock.getElapsedTime();
399
406
 
400
407
  for (var name in S.agents3d) {
401
- updateAgent(S.agents3d[name], dt, time);
408
+ var ag = S.agents3d[name];
409
+ if (!ag) continue;
410
+ updateAgent(ag, dt, time);
411
+ if (ag.isApiAgent && S.agents3d[name]) updateRobotAnimation(ag, dt, time);
402
412
  }
403
413
 
414
+ // Gallery slideshow tick
415
+ if (S.currentEnv === 'campus') tickGallery(dt);
416
+
404
417
  // Player avatar mode — skip when driving (vehicle takes over)
405
418
  if (isPlayerMode() && S.controls && S.controls.keys && !isDriving()) {
406
419
  updatePlayer(dt, time, S.controls.keys);
@@ -757,6 +770,13 @@ window.office3dStart = function() {
757
770
  syncAgents();
758
771
  processMessages();
759
772
  updateTVScreen(S.clock.getElapsedTime());
773
+ // Fetch media for gallery screens
774
+ if (S.galleryScreens) {
775
+ var pq = window.currentProjectPath ? '?project=' + encodeURIComponent(window.currentProjectPath) : '';
776
+ fetch('/api/media' + pq).then(function(r) { return r.json(); }).then(function(media) {
777
+ if (Array.isArray(media) && media.length > 0) updateGalleryScreens(media);
778
+ }).catch(function() {});
779
+ }
760
780
  }
761
781
  }, 2000);
762
782
  };
@@ -800,9 +820,19 @@ window.office3dSetEnvironment = function(env) {
800
820
  // Load city modules on demand
801
821
  if (env === 'city') getCityMods();
802
822
  if (S.scene) {
803
- // Remove all existing agents so they get recreated with proper desk assignments
823
+ // Remove all existing agents including CSS2D label DOM elements
804
824
  for (var name in S.agents3d) {
805
825
  var agent = S.agents3d[name];
826
+ // Remove CSS2D label DOM elements explicitly
827
+ if (agent.parts.labelDiv && agent.parts.labelDiv.parentElement) agent.parts.labelDiv.remove();
828
+ if (agent.parts.bubbleDiv && agent.parts.bubbleDiv.parentElement) agent.parts.bubbleDiv.remove();
829
+ if (agent.parts.taskDiv && agent.parts.taskDiv.parentElement) agent.parts.taskDiv.remove();
830
+ // Remove all CSS2DObject DOM nodes from the group tree
831
+ agent.parts.group.traverse(function(child) {
832
+ if (child.isCSS2DObject && child.element && child.element.parentElement) {
833
+ child.element.remove();
834
+ }
835
+ });
806
836
  S.scene.remove(agent.parts.group);
807
837
  agent.parts.group.traverse(function(child) {
808
838
  if (child.geometry) child.geometry.dispose();
@@ -813,6 +843,8 @@ window.office3dSetEnvironment = function(env) {
813
843
  });
814
844
  }
815
845
  S.agents3d = {};
846
+ // Also release any claimed gallery seats
847
+ window._gallerySeatsReset = true;
816
848
  S._tvScreen = null;
817
849
  S._roofGroup = null;
818
850
  S._managerDoor = null;
@@ -821,6 +853,7 @@ window.office3dSetEnvironment = function(env) {
821
853
  S._managerOfficePos = null;
822
854
  S._campusDeskPositions = null;
823
855
  S.lastProcessedMsg = 0;
856
+ window._lastProcessedMsg = 0;
824
857
  invalidateColliders();
825
858
  buildEnvironment();
826
859
  // syncAgents will recreate all agents with correct desk assignments
@@ -885,49 +918,123 @@ if (window.activeView === 'office') {
885
918
  window.office3dStart();
886
919
  }
887
920
 
888
- // ===================== INTERACTIVE IFRAME MONITOR (Phase 2) =====================
889
- var activeIframe = null;
921
+ // ===================== INTERACTIVE MONITOR Dashboard + ComfyUI tabs =====================
922
+ var _monitorOverlay = null;
923
+ var _monitorActiveTab = 'dashboard';
890
924
 
891
925
  window.onPlayerSit = function(deskIdx) {
892
- if (activeIframe) return;
893
- var container = document.getElementById('office-3d-container') || document.getElementById('office-area');
926
+ if (_monitorOverlay) return;
927
+
928
+ // Release pointer lock so mouse works in iframe
929
+ if (document.pointerLockElement) document.exitPointerLock();
930
+
931
+ // Use office-area as parent (it has proper positioning)
932
+ var container = document.getElementById('office-area');
933
+ if (!container) container = document.getElementById('office-3d-container');
894
934
  if (!container) return;
895
935
 
896
- // Create iframe overlay positioned over the 3D canvas
936
+ // Overlay
897
937
  var overlay = document.createElement('div');
898
- overlay.id = 'office-iframe-overlay';
899
- overlay.style.cssText = 'position:absolute;top:5%;left:10%;width:80%;height:85%;z-index:200;background:#000;border-radius:8px;box-shadow:0 0 40px rgba(88,166,255,0.3);overflow:hidden;display:flex;flex-direction:column';
938
+ overlay.id = 'office-monitor-overlay';
939
+ overlay.style.cssText = 'position:fixed;top:3%;left:5%;width:90%;height:92%;z-index:99999;background:#0a0c14;border-radius:10px;box-shadow:0 0 60px rgba(88,166,255,0.3),0 0 0 1px #30363d;overflow:hidden;display:flex;flex-direction:column;';
900
940
 
901
- // Header bar (mimics monitor bezel)
941
+ // Header bar with tabs + close
902
942
  var header = document.createElement('div');
903
- header.style.cssText = 'background:#1a1f36;padding:6px 12px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0';
904
- header.innerHTML = '<div style="display:flex;gap:6px"><span style="width:10px;height:10px;border-radius:50%;background:#ff5f57"></span><span style="width:10px;height:10px;border-radius:50%;background:#ffbd2e"></span><span style="width:10px;height:10px;border-radius:50%;background:#28c840"></span></div><span style="color:#8892b0;font-size:11px;font-family:monospace">Let Them Talk Dashboard</span><button id="office-leave-btn" style="background:#ff5f57;color:#fff;border:none;border-radius:4px;padding:3px 12px;font-size:11px;font-weight:bold;cursor:pointer;font-family:monospace">LEAVE</button>';
905
- header.querySelector('#office-leave-btn').addEventListener('click', function() {
906
- if (typeof window.onPlayerStand === 'function') window.onPlayerStand();
907
- // Also trigger player stand-up in player.js
908
- if (typeof window.playerForceStand === 'function') window.playerForceStand();
909
- });
943
+ header.style.cssText = 'background:#141824;padding:0 12px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0;height:36px;border-bottom:1px solid #30363d;';
944
+
945
+ // Tab buttons
946
+ var tabsHtml = '<div style="display:flex;gap:0;">';
947
+ tabsHtml += '<button id="mon-tab-dashboard" style="padding:8px 16px;font-size:11px;font-family:monospace;border:none;cursor:pointer;border-bottom:2px solid #58a6ff;background:transparent;color:#58a6ff;">Dashboard</button>';
948
+ tabsHtml += '<button id="mon-tab-comfyui" style="padding:8px 16px;font-size:11px;font-family:monospace;border:none;cursor:pointer;border-bottom:2px solid transparent;background:transparent;color:#8892b0;">ComfyUI</button>';
949
+ tabsHtml += '</div>';
950
+
951
+ var closeHtml = '<button id="mon-close-btn" style="background:#ff5f57;color:#fff;border:none;border-radius:4px;padding:3px 14px;font-size:11px;font-weight:bold;cursor:pointer;font-family:monospace;">ESC to Leave</button>';
952
+
953
+ header.innerHTML = tabsHtml + closeHtml;
910
954
  overlay.appendChild(header);
911
955
 
912
- // Dashboard iframe
913
- var iframe = document.createElement('iframe');
914
- iframe.src = window.location.origin || 'http://localhost:3000';
915
- iframe.style.cssText = 'flex:1;border:none;width:100%;background:#0d1117';
916
- iframe.allow = 'clipboard-read; clipboard-write';
917
- overlay.appendChild(iframe);
956
+ // Iframe container
957
+ var iframeWrap = document.createElement('div');
958
+ iframeWrap.style.cssText = 'flex:1;position:relative;';
959
+
960
+ // Dashboard iframe. clipboard-write only — the iframe needs to copy agent
961
+ // prompts into the user's clipboard, it never needs to read from it.
962
+ var dashIframe = document.createElement('iframe');
963
+ dashIframe.id = 'mon-iframe-dashboard';
964
+ dashIframe.src = window.location.origin || 'http://localhost:3000';
965
+ dashIframe.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;border:none;background:#0d1117;';
966
+ dashIframe.allow = 'clipboard-write';
967
+ iframeWrap.appendChild(dashIframe);
968
+
969
+ // ComfyUI iframe (hidden initially). Same write-only clipboard scope.
970
+ var comfyIframe = document.createElement('iframe');
971
+ comfyIframe.id = 'mon-iframe-comfyui';
972
+ comfyIframe.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;border:none;background:#1a1a2e;display:none;';
973
+ comfyIframe.allow = 'clipboard-write';
974
+ // Don't load ComfyUI until tab is clicked (saves resources)
975
+ iframeWrap.appendChild(comfyIframe);
976
+
977
+ overlay.appendChild(iframeWrap);
978
+ document.body.appendChild(overlay);
979
+ _monitorOverlay = overlay;
980
+ _monitorActiveTab = 'dashboard';
981
+
982
+ // Tab switching
983
+ function switchTab(tab) {
984
+ _monitorActiveTab = tab;
985
+ var dIframe = document.getElementById('mon-iframe-dashboard');
986
+ var cIframe = document.getElementById('mon-iframe-comfyui');
987
+ var dTab = document.getElementById('mon-tab-dashboard');
988
+ var cTab = document.getElementById('mon-tab-comfyui');
989
+
990
+ if (tab === 'dashboard') {
991
+ if (dIframe) dIframe.style.display = 'block';
992
+ if (cIframe) cIframe.style.display = 'none';
993
+ if (dTab) { dTab.style.color = '#58a6ff'; dTab.style.borderBottomColor = '#58a6ff'; }
994
+ if (cTab) { cTab.style.color = '#8892b0'; cTab.style.borderBottomColor = 'transparent'; }
995
+ } else {
996
+ if (dIframe) dIframe.style.display = 'none';
997
+ if (cIframe) {
998
+ cIframe.style.display = 'block';
999
+ // Lazy-load ComfyUI on first switch. URL is configurable via
1000
+ // window.COMFYUI_URL (set it from the dashboard or page) so users
1001
+ // on non-default ports or remote ComfyUI hosts can override.
1002
+ if (!cIframe.src || cIframe.src === 'about:blank' || cIframe.src === '') {
1003
+ cIframe.src = (typeof window !== 'undefined' && window.COMFYUI_URL) || 'http://127.0.0.1:8188';
1004
+ }
1005
+ }
1006
+ if (dTab) { dTab.style.color = '#8892b0'; dTab.style.borderBottomColor = 'transparent'; }
1007
+ if (cTab) { cTab.style.color = '#ff6b35'; cTab.style.borderBottomColor = '#ff6b35'; }
1008
+ }
1009
+ }
918
1010
 
919
- container.style.position = 'relative';
920
- container.appendChild(overlay);
921
- activeIframe = overlay;
1011
+ overlay.querySelector('#mon-tab-dashboard').addEventListener('click', function() { switchTab('dashboard'); });
1012
+ overlay.querySelector('#mon-tab-comfyui').addEventListener('click', function() { switchTab('comfyui'); });
1013
+
1014
+ // Close button
1015
+ overlay.querySelector('#mon-close-btn').addEventListener('click', function() {
1016
+ if (typeof window.playerForceStand === 'function') window.playerForceStand();
1017
+ window.onPlayerStand();
1018
+ });
1019
+
1020
+ // ESC key closes overlay
1021
+ overlay._escHandler = function(e) {
1022
+ if (e.code === 'Escape' && _monitorOverlay) {
1023
+ if (typeof window.playerForceStand === 'function') window.playerForceStand();
1024
+ window.onPlayerStand();
1025
+ }
1026
+ };
1027
+ document.addEventListener('keydown', overlay._escHandler);
922
1028
 
923
- // Focus iframe for keyboard input
924
- iframe.addEventListener('load', function() { iframe.focus(); });
1029
+ // Focus dashboard iframe
1030
+ dashIframe.addEventListener('load', function() { dashIframe.focus(); });
925
1031
  };
926
1032
 
927
1033
  window.onPlayerStand = function() {
928
- if (activeIframe) {
929
- activeIframe.remove();
930
- activeIframe = null;
1034
+ if (_monitorOverlay) {
1035
+ if (_monitorOverlay._escHandler) document.removeEventListener('keydown', _monitorOverlay._escHandler);
1036
+ _monitorOverlay.remove();
1037
+ _monitorOverlay = null;
931
1038
  }
932
1039
  };
933
1040
 
@@ -5,163 +5,184 @@ import { S } from './state.js';
5
5
  // Agents walk along connected waypoints to avoid walls/objects
6
6
  // ============================================================
7
7
 
8
- // Manager office geometry reference:
9
- // Office group at (12, 5), size 8x7, walls at:
10
- // Front (door): z = 5 - 3.5 = 1.5 (door at center x=12)
11
- // Back: z = 5 + 3.5 = 8.5
12
- // Left: x = 12 - 4 = 8
13
- // Right: x = 12 + 4 = 16
14
- // Glass partition at z = -7 (between workspace and rec)
15
- // Glass partition at x = -8 (between designer and main)
8
+ // Campus geometry reference (90W x 60D):
9
+ // Outer walls: X = ±45, Z = ±30
10
+ // Workspace: 5x4 desk grid at X=[-8,-4,0,4,8], Z=[6,10,14,18]
11
+ // Manager office at (30, 10), size 10x10:
12
+ // Left wall: X = 25, Right wall: X = 35
13
+ // Front wall: Z = 5 (door at center X=30)
14
+ // Back wall: Z = 15
15
+ // Path must approach from south (Z<5) and enter through door
16
+ // Main corridor: Z=0, runs full campus width (-2 to +2)
17
+ // Cross corridors: X=±20, connect north-south (3 units wide)
18
+ // Lobby/Entrance: Z=22-30, front wall gap at center
19
+ // Designer Studio: center (-28, 0), 12x10
20
+ // Bar & Café: center (-28, -18), 12x10
21
+ // Rec Center: center (0, -18), 12x10
22
+ // Gym: center (22, -18), 12x10
23
+ // Staircase: X=35, Z=-14 to -22, connects to mezzanine (Z=-18 to -30)
24
+ // Gallery Wing: inside campus at (-36, 10), 14x12, open east side at X=-29
16
25
 
17
26
  var CAMPUS_WAYPOINTS = [
18
- // === LOBBY / ENTRANCE ===
19
- { id: 'spawn', x: 0, z: 14 },
20
- { id: 'lobby', x: 0, z: 10 },
21
- { id: 'lobby_left', x: -6, z: 10 },
22
- { id: 'lobby_right', x: 6, z: 10 },
23
-
24
- // === MAIN CORRIDOR (runs along z=6, above workspace) ===
25
- { id: 'corr_L', x: -8, z: 7 },
26
- { id: 'corr_CL', x: -3, z: 7 },
27
- { id: 'corr_C', x: 0, z: 7 },
28
- { id: 'corr_CR', x: 3, z: 7 },
29
- { id: 'corr_R', x: 7, z: 7 },
30
-
31
- // === WORKSPACE ZONE (center area, between glass partitions) ===
32
- { id: 'work_N', x: 0, z: 4 }, // north end
33
- { id: 'work_NW', x: -5, z: 4 },
34
- { id: 'work_NE', x: 5, z: 4 },
35
- { id: 'work_W', x: -5, z: 0 },
36
- { id: 'work_C', x: 0, z: 0 },
37
- { id: 'work_E', x: 5, z: 0 },
38
- { id: 'work_SW', x: -5, z: -3 },
39
- { id: 'work_S', x: 0, z: -5 },
40
- { id: 'work_SE', x: 5, z: -3 },
41
-
42
- // === DESIGNER WING (left of glass partition x=-8) ===
43
- { id: 'design_gate', x: -8, z: 3 }, // gap in partition
44
- { id: 'design_N', x: -12, z: 3 },
45
- { id: 'design_C', x: -12.5, z: 0 },
46
- { id: 'design_S', x: -12, z: -3 },
47
-
48
- // === MANAGER OFFICE (right side, enclosed glass room) ===
49
- // Office walls: left x=8, right x=16, front z=1.5, back z=8.5
50
- // Door at front wall center (x=12, z=1.5)
51
- // Path must go AROUND the left-front corner, then to door from outside
52
- { id: 'mgr_hallway', x: 7, z: 3 }, // south of corridor, OUTSIDE left wall (x<8)
53
- { id: 'mgr_corner', x: 7, z: 0 }, // past the front-left corner (x<8, z<1.5)
54
- { id: 'mgr_outside', x: 12, z: 0 }, // in front of door, OUTSIDE front wall (z<1.5)
55
- { id: 'mgr_doorstep', x: 12, z: 1.5 }, // at the door threshold (triggers door open)
56
- { id: 'mgr_entry', x: 12, z: 3 }, // just inside the door
57
- { id: 'mgr_center', x: 12, z: 5 }, // middle of office
58
- { id: 'mgr_desk', x: 12, z: 7 }, // at the desk/chair
59
-
60
- // === BACK ZONE CORRIDOR (runs along z=-7 to z=-8, south of glass partition) ===
61
- { id: 'back_gate', x: 0, z: -6.5 }, // gap in glass partition
62
- { id: 'back_L', x: -8, z: -8 },
63
- { id: 'back_C', x: 0, z: -8 },
64
- { id: 'back_R', x: 8, z: -8 },
65
-
66
- // === BAR (back left) ===
67
- { id: 'bar_entry', x: -10, z: -10 },
68
- { id: 'bar_center', x: -14, z: -12 },
69
-
70
- // === REC CENTER (back center) ===
71
- { id: 'rec_entry', x: 0, z: -10 },
72
- { id: 'rec_center', x: 0, z: -12 },
73
-
74
- // === GYM (back right) ===
75
- { id: 'gym_entry', x: 10, z: -10 },
76
- { id: 'gym_center', x: 14, z: -12 },
77
-
78
- // === MEZZANINE / STAIRS ===
79
- { id: 'stairs_bot', x: 20, z: -5 },
80
- { id: 'stairs_top', x: 20, z: -8 },
81
- { id: 'mezz_C', x: 0, z: -13 },
82
-
83
- // === REST / DRESSING (right wing, for old office compat) ===
84
- { id: 'rest_entry', x: 7.5, z: -5.5 },
85
- { id: 'dress_entry', x: 7.5, z: -1.5 },
27
+ // === LOBBY / ENTRANCE (Z=22-30, front wall gap at center) ===
28
+ { id: 'spawn', x: 0, z: 28 }, // entrance area (between door and reception)
29
+ { id: 'lobby', x: 0, z: 24 }, // inside lobby center
30
+ { id: 'lobby_left', x: -10, z: 24 }, // lobby left side
31
+ { id: 'lobby_right', x: 10, z: 24 }, // lobby right side
32
+
33
+ // === WORKSPACE (5x4 desk grid, X=[-8,-4,0,4,8], Z=[6,10,14,18]) ===
34
+ { id: 'work_N', x: 0, z: 18 }, // northernmost row center
35
+ { id: 'work_NW', x: -8, z: 18 }, // north-west desk
36
+ { id: 'work_NE', x: 8, z: 18 }, // north-east desk
37
+ { id: 'work_W', x: -8, z: 14 }, // west mid desk
38
+ { id: 'work_C', x: 0, z: 14 }, // center desk
39
+ { id: 'work_E', x: 8, z: 14 }, // east mid desk
40
+ { id: 'work_SW', x: -8, z: 10 }, // south-west desk
41
+ { id: 'work_S', x: 0, z: 10 }, // south row center
42
+ { id: 'work_SE', x: 8, z: 10 }, // south-east desk
43
+
44
+ // === MAIN CORRIDOR (Z=0, runs full width) ===
45
+ { id: 'corr_L', x: -20, z: 0 }, // left end (at cross corridor)
46
+ { id: 'corr_CL', x: -8, z: 0 }, // center-left
47
+ { id: 'corr_C', x: 0, z: 0 }, // center
48
+ { id: 'corr_CR', x: 8, z: 0 }, // center-right
49
+ { id: 'corr_R', x: 20, z: 0 }, // right end (at cross corridor)
50
+ { id: 'corr_RR', x: 30, z: 0 }, // far right (toward manager approach)
51
+
52
+ // === CROSS CORRIDORS (X=±20, connect north-south) ===
53
+ { id: 'cross_NL', x: -20, z: 12 }, // north-left cross corridor
54
+ { id: 'cross_SL', x: -20, z: -12 }, // south-left cross corridor
55
+ { id: 'cross_NR', x: 20, z: 12 }, // north-right cross corridor
56
+ { id: 'cross_SR', x: 20, z: -12 }, // south-right cross corridor
57
+
58
+ // === MANAGER OFFICE (center at (30,10), walls: left X=25, right X=35, front Z=5, back Z=15) ===
59
+ // Door at front wall center (X=30, Z=5). Path approaches from south (Z<5).
60
+ { id: 'mgr_hallway', x: 30, z: 3 }, // south of office, in main corridor area
61
+ { id: 'mgr_outside', x: 30, z: 4 }, // directly outside front wall
62
+ { id: 'mgr_doorstep', x: 30, z: 5, triggerDoor: 'open' }, // at door threshold
63
+ { id: 'mgr_entry', x: 30, z: 7 }, // just inside the door
64
+ { id: 'mgr_desk', x: 30, z: 12 }, // at the manager desk
65
+
66
+ // === DESIGNER STUDIO (center (-28, 0), 12x10) ===
67
+ { id: 'design_entry', x: -22, z: 0 }, // entry from cross corridor
68
+ { id: 'design_center', x: -28, z: 0 }, // studio center
69
+
70
+ // === BAR & CAFÉ (center (-28, -18), 12x10) ===
71
+ { id: 'bar_entry', x: -22, z: -14 }, // entry from south cross corridor
72
+ { id: 'bar_center', x: -28, z: -18 }, // bar center
73
+
74
+ // === REC CENTER (center (0, -18), 12x10) ===
75
+ { id: 'rec_entry', x: 0, z: -14 }, // entry from south corridor
76
+ { id: 'rec_center', x: 0, z: -18 }, // rec center
77
+
78
+ // === GYM (center (22, -18), 12x10) ===
79
+ { id: 'gym_entry', x: 14, z: -14 }, // entry from south cross corridor
80
+ { id: 'gym_center', x: 22, z: -18 }, // gym center
81
+
82
+ // === STAIRCASE (X=35, Z=-14 to -22) ===
83
+ { id: 'stairs_bot', x: 35, z: -14 }, // bottom of stairs
84
+ { id: 'stairs_top', x: 35, z: -20 }, // top of stairs (mezzanine level)
85
+
86
+ // === MEZZANINE (Z=-18 to -30) ===
87
+ { id: 'mezz_C', x: 0, z: -24 }, // mezzanine center
88
+
89
+ // === GALLERY WING (inside campus, center at -36, 10, entry from east at X=-29) ===
90
+ { id: 'gallery_entry', x: -28, z: 10 }, // just outside east glass entrance
91
+ { id: 'gallery_center',x: -36, z: 10 }, // gallery center
86
92
  ];
87
93
 
88
94
  var CAMPUS_CONNECTIONS = [
89
- // Lobby connections
90
- ['spawn', 'lobby'],
91
- ['lobby', 'lobby_left'],
92
- ['lobby', 'lobby_right'],
93
- ['lobby', 'corr_C'],
94
- ['lobby_left', 'corr_L'],
95
- ['lobby_right', 'corr_R'],
96
-
97
- // Main corridor (horizontal)
98
- ['corr_L', 'corr_CL'],
99
- ['corr_CL', 'corr_C'],
100
- ['corr_C', 'corr_CR'],
101
- ['corr_CR', 'corr_R'],
102
-
103
- // Corridor → workspace
104
- ['corr_C', 'work_N'],
105
- ['corr_CL', 'work_NW'],
106
- ['corr_CR', 'work_NE'],
107
-
108
- // Workspace grid
109
- ['work_N', 'work_NW'],
110
- ['work_N', 'work_NE'],
111
- ['work_N', 'work_C'],
112
- ['work_NW', 'work_W'],
113
- ['work_NE', 'work_E'],
114
- ['work_W', 'work_C'],
115
- ['work_C', 'work_E'],
116
- ['work_W', 'work_SW'],
117
- ['work_C', 'work_S'],
118
- ['work_E', 'work_SE'],
119
- ['work_SW', 'work_S'],
120
- ['work_S', 'work_SE'],
121
-
122
- // Designer wing (through gap in glass partition)
123
- ['work_NW', 'design_gate'],
124
- ['corr_L', 'design_gate'],
125
- ['design_gate', 'design_N'],
126
- ['design_N', 'design_C'],
127
- ['design_C', 'design_S'],
128
-
129
- // Manager office path goes AROUND the corner then through door
130
- // corr_R(7,7) → mgr_hallway(7,3) mgr_corner(7,0) mgr_outside(12,0) → door → inside
131
- ['corr_R', 'mgr_hallway'], // walk south, outside left wall (x=7 < wall x=8)
132
- ['mgr_hallway', 'mgr_corner'], // walk further south past front-left corner (z=0 < wall z=1.5)
133
- ['mgr_corner', 'mgr_outside'], // walk east to front of door (z=0, safely below front wall z=1.5)
134
- ['mgr_outside', 'mgr_doorstep'], // step to door threshold (triggers open)
135
- ['mgr_doorstep', 'mgr_entry'], // walk through open door into office
136
- ['mgr_entry', 'mgr_center'], // walk deeper inside
137
- ['mgr_center', 'mgr_desk'], // walk to desk
138
-
139
- // Workspace → back zone (through gap in glass partition at z=-7)
140
- ['work_S', 'back_gate'],
141
- ['back_gate', 'back_C'],
142
-
143
- // Back corridor
144
- ['back_L', 'back_C'],
145
- ['back_C', 'back_R'],
146
- ['work_SW', 'back_L'],
147
- ['work_SE', 'back_R'],
148
-
149
- // Back zones
150
- ['back_L', 'bar_entry'],
151
- ['bar_entry', 'bar_center'],
152
- ['back_C', 'rec_entry'],
153
- ['rec_entry', 'rec_center'],
154
- ['back_R', 'gym_entry'],
155
- ['gym_entry', 'gym_center'],
156
-
157
- // Stairs / mezzanine
158
- ['back_R', 'stairs_bot'],
159
- ['stairs_bot', 'stairs_top'],
160
- ['stairs_top', 'mezz_C'],
161
-
162
- // Rest/dressing (legacy)
163
- ['work_SE', 'rest_entry'],
164
- ['work_E', 'dress_entry'],
95
+ // === LOBBY ===
96
+ ['spawn', 'lobby'],
97
+ ['lobby', 'lobby_left'],
98
+ ['lobby', 'lobby_right'],
99
+ ['lobby', 'work_N'], // lobby → northernmost workspace row
100
+ ['lobby_left', 'work_NW'],
101
+ ['lobby_right', 'work_NE'],
102
+
103
+ // === WORKSPACE GRID (rows N→S, Z: 18→10) ===
104
+ // North row (Z=18)
105
+ ['work_N', 'work_NW'],
106
+ ['work_N', 'work_NE'],
107
+ // Mid row (Z=14) — connected to north
108
+ ['work_NW', 'work_W'],
109
+ ['work_N', 'work_C'],
110
+ ['work_NE', 'work_E'],
111
+ // Horizontal mid
112
+ ['work_W', 'work_C'],
113
+ ['work_C', 'work_E'],
114
+ // South row (Z=10) — connected to mid
115
+ ['work_W', 'work_SW'],
116
+ ['work_C', 'work_S'],
117
+ ['work_E', 'work_SE'],
118
+ // Horizontal south
119
+ ['work_SW', 'work_S'],
120
+ ['work_S', 'work_SE'],
121
+
122
+ // === WORKSPACE → MAIN CORRIDOR (Z=0) ===
123
+ ['work_SW', 'corr_CL'], // south-west desk down to corridor
124
+ ['work_S', 'corr_C'], // south center down to corridor
125
+ ['work_SE', 'corr_CR'], // south-east desk down to corridor
126
+
127
+ // === MAIN CORRIDOR (horizontal, Z=0) ===
128
+ ['corr_L', 'corr_CL'],
129
+ ['corr_CL', 'corr_C'],
130
+ ['corr_C', 'corr_CR'],
131
+ ['corr_CR', 'corr_R'],
132
+ ['corr_R', 'corr_RR'], // extend east toward manager side
133
+
134
+ // === CROSS CORRIDORS (X=±20, north-south) ===
135
+ // Left cross corridor (X=-20): lobby-side north main corridor south zone
136
+ ['cross_NL', 'work_NW'], // north end connects to workspace
137
+ ['cross_NL', 'corr_L'], // meets main corridor
138
+ ['corr_L', 'cross_SL'], // south through left cross corridor
139
+ ['cross_SL', 'cross_NL'], // bidirectional shortcut label
140
+ // Right cross corridor (X=20): lobby-side north → main corridor → south zone
141
+ ['cross_NR', 'work_NE'], // north end connects to workspace
142
+ ['cross_NR', 'corr_R'], // meets main corridor
143
+ ['corr_R', 'cross_SR'], // south through right cross corridor
144
+ ['cross_SR', 'cross_NR'], // bidirectional shortcut label
145
+
146
+ // === MANAGER OFFICE ===
147
+ // Approach: corr_RR(30,0) → mgr_hallway(30,3) → mgr_outside(30,4) → door → inside
148
+ ['corr_RR', 'mgr_hallway'], // walk north along X=30 toward office
149
+ ['mgr_hallway', 'mgr_outside'], // step closer to front wall
150
+ ['mgr_outside', 'mgr_doorstep'], // step to door threshold (triggers door open)
151
+ ['mgr_doorstep', 'mgr_entry'], // walk through open door
152
+ ['mgr_entry', 'mgr_desk'], // walk to manager desk
153
+
154
+ // === DESIGNER STUDIO (center -28, 0) — enter from left cross corridor ===
155
+ ['corr_L', 'design_entry'], // branch off main corridor at X=-20 level
156
+ ['design_entry', 'design_center'], // walk into studio
157
+
158
+ // === SOUTH ZONES — accessed via cross corridors reaching south ===
159
+ // Left south: cross_SL(-20,-12) → bar_entry(-22,-14) → bar_center(-28,-18)
160
+ ['cross_SL', 'bar_entry'],
161
+ ['bar_entry', 'bar_center'],
162
+
163
+ // Center south: corr_C(0,0) drops to rec via south cross node
164
+ ['corr_C', 'rec_entry'], // direct south from main corridor center
165
+ ['rec_entry', 'rec_center'],
166
+
167
+ // Right south: cross_SR(20,-12) → gym_entry(14,-14) → gym_center(22,-18)
168
+ ['cross_SR', 'gym_entry'],
169
+ ['gym_entry', 'gym_center'],
170
+
171
+ // Cross-south interconnect (bar ↔ rec ↔ gym at Z≈-14)
172
+ ['bar_entry', 'rec_entry'],
173
+ ['rec_entry', 'gym_entry'],
174
+
175
+ // === STAIRCASE & MEZZANINE ===
176
+ // Stairs bot at (35,-14): reachable from cross_SR and gym side
177
+ ['cross_SR', 'stairs_bot'],
178
+ ['gym_center', 'stairs_bot'],
179
+ ['stairs_bot', 'stairs_top'],
180
+ ['stairs_top', 'mezz_C'],
181
+
182
+ // === GALLERY WING (inside campus, upper-left area) ===
183
+ ['cross_NL', 'gallery_entry'], // north from left cross corridor
184
+ ['design_entry', 'gallery_entry'], // from designer studio area
185
+ ['gallery_entry','gallery_center'], // enter gallery
165
186
  ];
166
187
 
167
188
  // === BUILD GRAPH ===