execweave 0.8.17__tar.gz → 0.8.18__tar.gz

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 (174) hide show
  1. {execweave-0.8.17 → execweave-0.8.18}/PKG-INFO +1 -1
  2. {execweave-0.8.17 → execweave-0.8.18}/pyproject.toml +2 -2
  3. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/__init__.py +1 -1
  4. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/_dashboard_shell_base.py +5 -1
  5. execweave-0.8.18/src/execweave/viewer_layout_v2.py +429 -0
  6. execweave-0.8.18/src/execweave/viewer_layout_v2_detached.py +25 -0
  7. {execweave-0.8.17 → execweave-0.8.18}/.gitignore +0 -0
  8. {execweave-0.8.17 → execweave-0.8.18}/LICENSE +0 -0
  9. {execweave-0.8.17 → execweave-0.8.18}/README.md +0 -0
  10. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/__main__.py +0 -0
  11. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/_conversation_records_core.py +0 -0
  12. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/_http_proxy_base.py +0 -0
  13. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/_http_proxy_bounded.py +0 -0
  14. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/_http_proxy_stage.py +0 -0
  15. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/agent_bootstrap.py +0 -0
  16. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/agent_topology.py +0 -0
  17. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/agent_trace.py +0 -0
  18. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/agy_preview_sanitize.py +0 -0
  19. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/analysis.py +0 -0
  20. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/anthropic.py +0 -0
  21. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/anthropic_cli.py +0 -0
  22. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/anthropic_full_fidelity.py +0 -0
  23. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/antigravity_adapter.py +0 -0
  24. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/antigravity_adapter_base.py +0 -0
  25. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/antigravity_full_fidelity.py +0 -0
  26. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/antigravity_full_fidelity_base.py +0 -0
  27. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/antigravity_full_fidelity_collaboration_base.py +0 -0
  28. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/antigravity_hook_cli.py +0 -0
  29. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/antigravity_record.py +0 -0
  30. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/antigravity_subagent_linkage.py +0 -0
  31. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/antigravity_trace_capability.py +0 -0
  32. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/auto_specialized.py +0 -0
  33. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/backends.py +0 -0
  34. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/benchmark.py +0 -0
  35. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/claude_adapter.py +0 -0
  36. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/claude_child_transcript.py +0 -0
  37. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/claude_delegation.py +0 -0
  38. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/claude_full_fidelity.py +0 -0
  39. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/claude_hook_cli.py +0 -0
  40. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/claude_hook_contract.py +0 -0
  41. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/claude_model_observer.py +0 -0
  42. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/claude_record.py +0 -0
  43. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/cli.py +0 -0
  44. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_adapter.py +0 -0
  45. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_conversation.py +0 -0
  46. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_full_fidelity.py +0 -0
  47. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_hook_cli.py +0 -0
  48. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_hook_entry.py +0 -0
  49. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_hook_lifecycle.py +0 -0
  50. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_message_diagnostics.py +0 -0
  51. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_message_transport_diagnostics.py +0 -0
  52. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_record.py +0 -0
  53. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_rollout_structures.py +0 -0
  54. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_rollout_trace.py +0 -0
  55. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/codex_rollout_trace_base.py +0 -0
  56. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/collector.py +0 -0
  57. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/command.py +0 -0
  58. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/content_evidence.py +0 -0
  59. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/content_store.py +0 -0
  60. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_archive.py +0 -0
  61. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_message_identity.py +0 -0
  62. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_preview.py +0 -0
  63. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_preview_antigravity.py +0 -0
  64. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_preview_claude.py +0 -0
  65. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_preview_codex.py +0 -0
  66. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_preview_common.py +0 -0
  67. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_preview_generic.py +0 -0
  68. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_preview_lines.py +0 -0
  69. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_preview_transcript.py +0 -0
  70. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_records.py +0 -0
  71. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_records_antigravity.py +0 -0
  72. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_records_codex.py +0 -0
  73. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_records_common.py +0 -0
  74. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/conversation_records_ollama.py +0 -0
  75. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/correlation.py +0 -0
  76. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/cursor_adapter.py +0 -0
  77. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/cursor_delegation.py +0 -0
  78. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/cursor_delegation_base.py +0 -0
  79. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/cursor_full_fidelity.py +0 -0
  80. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/cursor_hook_cli.py +0 -0
  81. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/cursor_hook_contract.py +0 -0
  82. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/cursor_record.py +0 -0
  83. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/dashboard_shell.py +0 -0
  84. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/entry.py +0 -0
  85. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/evidence_availability.py +0 -0
  86. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/evidence_grade.py +0 -0
  87. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/fidelity.py +0 -0
  88. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/filesystem.py +0 -0
  89. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/focus.py +0 -0
  90. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/graph.py +0 -0
  91. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/graph_ops.py +0 -0
  92. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/http_proxy.py +0 -0
  93. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/http_proxy_cli.py +0 -0
  94. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/inference_gateway.py +0 -0
  95. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/inference_gateway_cli.py +0 -0
  96. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/inference_gateway_full_fidelity.py +0 -0
  97. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/inference_identity.py +0 -0
  98. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/inference_identity_cli.py +0 -0
  99. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/integrity.py +0 -0
  100. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/integrity_cli.py +0 -0
  101. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/litellm_callback.py +0 -0
  102. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/litellm_callback_cli.py +0 -0
  103. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live.py +0 -0
  104. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live_core.py +0 -0
  105. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live_view.py +0 -0
  106. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live_view_extra_style.py +0 -0
  107. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live_view_markup.py +0 -0
  108. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live_view_process_layout.py +0 -0
  109. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live_view_readability.py +0 -0
  110. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live_view_script_a.py +0 -0
  111. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live_view_script_b.py +0 -0
  112. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live_view_script_c.py +0 -0
  113. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live_view_script_d.py +0 -0
  114. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/live_view_style.py +0 -0
  115. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/model_runtime.py +0 -0
  116. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/model_runtime_cli.py +0 -0
  117. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/model_runtime_full_fidelity.py +0 -0
  118. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/openai_compatible.py +0 -0
  119. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/openai_compatible_cli.py +0 -0
  120. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/openai_compatible_full_fidelity.py +0 -0
  121. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/opencode_adapter.py +0 -0
  122. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/opencode_event_contract.py +0 -0
  123. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/opencode_full_fidelity.py +0 -0
  124. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/opencode_hook_cli.py +0 -0
  125. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/opencode_plugin_cli.py +0 -0
  126. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/opencode_record.py +0 -0
  127. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/opencode_task_linkage.py +0 -0
  128. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/overhead_benchmark.py +0 -0
  129. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/provider_capability.py +0 -0
  130. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/provider_lifecycle.py +0 -0
  131. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/provider_record.py +0 -0
  132. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/rule_pack.py +0 -0
  133. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/rule_pack_cli.py +0 -0
  134. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/runtime_endpoint_availability.py +0 -0
  135. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/scalability_benchmark.py +0 -0
  136. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/schema.py +0 -0
  137. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/scope.py +0 -0
  138. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/semantic.py +0 -0
  139. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/sink.py +0 -0
  140. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/strace_backend.py +0 -0
  141. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/stream_assembly.py +0 -0
  142. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/theme.py +0 -0
  143. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/top.py +0 -0
  144. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/top_cli.py +0 -0
  145. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/validate.py +0 -0
  146. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/vendor/dagre.min.js +0 -0
  147. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/view_cli.py +0 -0
  148. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer.py +0 -0
  149. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_agent_panel.py +0 -0
  150. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_agent_panel_antigravity.py +0 -0
  151. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_agent_panel_claude.py +0 -0
  152. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_agent_panel_codex.py +0 -0
  153. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_agent_panel_cursor.py +0 -0
  154. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_agent_panel_default.py +0 -0
  155. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_agent_panel_ollama.py +0 -0
  156. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_agent_panel_opencode.py +0 -0
  157. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_agent_panel_registry.py +0 -0
  158. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_content_inspector.py +0 -0
  159. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_dashboard_clean.py +0 -0
  160. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_dashboard_focus.py +0 -0
  161. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_dashboard_hardening.py +0 -0
  162. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_dashboard_hardening_v2.py +0 -0
  163. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_dashboard_pr70.py +0 -0
  164. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_external_endpoints.py +0 -0
  165. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_gif.py +0 -0
  166. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_layout_geometry.py +0 -0
  167. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_limits.py +0 -0
  168. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_live_layout.py +0 -0
  169. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_projection.py +0 -0
  170. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_projection_base.py +0 -0
  171. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_projection_bridges.py +0 -0
  172. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_semantic_projection.py +0 -0
  173. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/viewer_tool_mirror_identity.py +0 -0
  174. {execweave-0.8.17 → execweave-0.8.18}/src/execweave/workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: execweave
3
- Version: 0.8.17
3
+ Version: 0.8.18
4
4
  Summary: Turn AI-agent runtime behavior into a local interactive execution graph.
5
5
  Project-URL: Homepage, https://github.com/Irish-kw/ExecWeave
6
6
  Project-URL: Repository, https://github.com/Irish-kw/ExecWeave
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "execweave"
7
- # ExecWeave v0.8.17 release metadata
8
- version = "0.8.17"
7
+ # ExecWeave v0.8.18 release metadata
8
+ version = "0.8.18"
9
9
  description = "Turn AI-agent runtime behavior into a local interactive execution graph."
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.10"
@@ -1,3 +1,3 @@
1
1
  """ExecWeave runtime graph package."""
2
2
 
3
- __version__ = "0.8.17"
3
+ __version__ = "0.8.18"
@@ -8,6 +8,8 @@ from .viewer_agent_panel import inject_agent_panel
8
8
  from .viewer_dashboard_clean import fold_budget_bootstrap, inject_live_dashboard_clean
9
9
  from .viewer_dashboard_focus import inject_live_dashboard_focus
10
10
  from .viewer_dashboard_pr70 import inject_pr70_dashboard_repairs
11
+ from .viewer_layout_v2 import inject_layout_v2
12
+ from .viewer_layout_v2_detached import inject_layout_v2_detached_cluster_policy
11
13
  from .viewer_limits import resolve_viewer_limits, viewer_limits_bootstrap
12
14
  from .viewer_live_layout import inject_live_dashboard_layout
13
15
 
@@ -72,6 +74,8 @@ def _build_dashboard_html() -> str:
72
74
  html = _preserve_semantic_arrange(html)
73
75
  html = _route_ordinary_edges_from_final_positions(html)
74
76
  html = inject_pr70_dashboard_repairs(html)
77
+ html = inject_layout_v2(html)
78
+ html = inject_layout_v2_detached_cluster_policy(html)
75
79
  return _align_agent_panel_topology(inject_agent_panel(_guard_compact_live_snapshot(html)))
76
80
 
77
81
 
@@ -117,4 +121,4 @@ def render_static_dashboard_html(
117
121
  "<body>",
118
122
  '<body>\n<!-- unified dashboard: theme is owned by the visible #theme-toggle control -->',
119
123
  1,
120
- )
124
+ )
@@ -0,0 +1,429 @@
1
+ from __future__ import annotations
2
+
3
+ _STARTUP_SEAM = "applyTheme(initialTheme());applyTransform();poll();"
4
+ _BAND_GAP_SEAM = "const EXECWEAVE_BAND_GAP=170;"
5
+ _PACKING_SEAM = """ const columns=Math.max(1,Math.ceil(Math.sqrt(compBoxes.length)));
6
+ const widest=compBoxes.reduce((width,box)=>Math.max(width,box.w),0);
7
+ const packingWidth=Math.max(spineWidth,columns*widest+(columns-1)*bandGap);"""
8
+ _PACKING_REPLACEMENT = """ const columns=Math.max(1,Math.ceil(Math.sqrt(compBoxes.length)));
9
+ const widest=compBoxes.reduce((width,box)=>Math.max(width,box.w),0);
10
+ const packingWidth=typeof execweaveLayoutV2PackingWidth==='function'
11
+ ?execweaveLayoutV2PackingWidth(compBoxes,spineWidth,bandGap)
12
+ :Math.max(spineWidth,columns*widest+(columns-1)*bandGap);"""
13
+ _SYNC_ORDER_SEAM = """ for(const [id,p] of positions){const s=topo.spec.get(id);if(s){s.x=p.x;s.y=p.y}}
14
+ if(typeof execweaveRecomputePorts==='function')execweaveRecomputePorts(topo);"""
15
+ _SYNC_ORDER_REPLACEMENT = """ for(const [id,p] of positions){const s=topo.spec.get(id);if(s){s.x=p.x;s.y=p.y}}
16
+ if(typeof execweaveLayoutV2SyncFinalOrder==='function')execweaveLayoutV2SyncFinalOrder(topo,positions);
17
+ if(typeof execweaveRecomputePorts==='function')execweaveRecomputePorts(topo);"""
18
+ _BUS_ROUTE_SEAM = """ function route(edge){
19
+ const topo=execweaveTopology,bundle=topo.bundleByEdge.get(edgeId(edge));
20
+ if(bundle&&bundle.size>1){"""
21
+ _BUS_ROUTE_REPLACEMENT = """ function route(edge){
22
+ const topo=execweaveTopology,bundle=topo.bundleByEdge.get(edgeId(edge));
23
+ if(bundle&&bundle.size>1&&typeof execweaveLayoutV2BusRoute==='function'){
24
+ const layoutV2Bus=execweaveLayoutV2BusRoute(edge);
25
+ if(layoutV2Bus)return layoutV2Bus;
26
+ }
27
+ if(bundle&&bundle.size>1){"""
28
+ _BUILD_TOPOLOGY_SEAM = """ const pr70BuildTopologyBase=execweaveBuildTopology;
29
+ execweaveBuildTopology=function(){
30
+ const topo=pr70BuildTopologyBase();
31
+ return optimize(pr70RetargetDagreRoutePoints(topo));
32
+ };"""
33
+ _BUILD_TOPOLOGY_REPLACEMENT = """ const pr70BuildTopologyBase=execweaveBuildTopology;
34
+ execweaveBuildTopology=function(){
35
+ const topo=pr70BuildTopologyBase();
36
+ const optimized=optimize(pr70RetargetDagreRoutePoints(topo));
37
+ return typeof execweaveLayoutV2FinalizeTopology==='function'
38
+ ?execweaveLayoutV2FinalizeTopology(optimized):optimized;
39
+ };"""
40
+ _INPUT_KEY_SEAM = """ const inputKey=JSON.stringify({nodes:[...topo.spec].map(([id,s])=>[id,s.x,s.y,topo.width.get(id),topo.height.get(id)]).sort(),
41
+ edges:[...edgeById.values()].map(e=>[edgeId(e),e.source,e.target,e.relation]).sort()});"""
42
+ _INPUT_KEY_REPLACEMENT = """ const layoutMode=(typeof window!=='undefined'&&window.__execweaveLayoutV2Mode)||'live';
43
+ const inputKey=JSON.stringify({mode:layoutMode,nodes:[...topo.spec].map(([id,s])=>[id,s.x,s.y,topo.width.get(id),topo.height.get(id)]).sort(),
44
+ edges:[...edgeById.values()].map(e=>[edgeId(e),e.source,e.target,e.relation]).sort()});"""
45
+ _BUDGET_SEAM = " const budget=Math.max(24,Math.min(512,Math.floor(24000/Math.max(1,edgeById.size))));"
46
+ _BUDGET_REPLACEMENT = """ const budget=layoutMode==='arrange'
47
+ ?Math.max(96,Math.min(1024,Math.floor(48000/Math.max(1,edgeById.size))))
48
+ :Math.max(24,Math.min(512,Math.floor(24000/Math.max(1,edgeById.size))));"""
49
+ _BETTER_SEAM = """ const crossingCeiling=best.EDGE_CROSSINGS;
50
+ const better=m=>m.NODE_OVERLAPS===0&&m.EDGE_CROSSINGS<=crossingCeiling&&
51
+ (m.EDGE_NODE_INTERSECTIONS<best.EDGE_NODE_INTERSECTIONS||
52
+ m.EDGE_NODE_INTERSECTIONS===best.EDGE_NODE_INTERSECTIONS&&m.EDGE_CROSSINGS<best.EDGE_CROSSINGS);"""
53
+ _BETTER_REPLACEMENT = """ const quality=m=>(m.NODE_DENSITY||0)-.10*(m.ASPECT_ERROR||0)-.04*Math.max(0,(m.P95_EDGE_STRETCH||1)-1);
54
+ const better=m=>{
55
+ if(m.NODE_OVERLAPS!==0||m.EDGE_NODE_INTERSECTIONS>best.EDGE_NODE_INTERSECTIONS)return false;
56
+ if(m.EDGE_CROSSINGS>best.EDGE_CROSSINGS)return false;
57
+ if(m.EDGE_NODE_INTERSECTIONS<best.EDGE_NODE_INTERSECTIONS||m.EDGE_CROSSINGS<best.EDGE_CROSSINGS)return true;
58
+ return quality(m)>quality(best)+1e-9;
59
+ };"""
60
+ _ROUNDS_SEAM = " for(let round=0;round<2&&evaluations<budget;round++){"
61
+ _ROUNDS_REPLACEMENT = " for(let round=0;round<(layoutMode==='arrange'?4:2)&&evaluations<budget;round++){"
62
+ _ARRANGE_SEAM = """function execweaveArrangePositions(){
63
+ execweaveTopology=execweaveBuildTopology();"""
64
+ _ARRANGE_REPLACEMENT = """function execweaveArrangePositions(){
65
+ const execweavePriorLayoutV2Mode=(typeof window!=='undefined'&&window.__execweaveLayoutV2Mode)||'live';
66
+ const execweaveNextLayoutV2Mode=(typeof nodeById!=='undefined'&&nodeById.size>96)?'live':'arrange';
67
+ if(typeof window!=='undefined')window.__execweaveLayoutV2Mode=execweaveNextLayoutV2Mode;
68
+ try{execweaveTopology=execweaveBuildTopology()}
69
+ finally{if(typeof window!=='undefined')window.__execweaveLayoutV2Mode=execweavePriorLayoutV2Mode}"""
70
+
71
+ LAYOUT_V2_SCRIPT = r"""
72
+ (function(){
73
+ window.__execweaveLayoutV2Mode=window.__execweaveLayoutV2Mode||'live';
74
+
75
+ function execweaveLayoutV2SyncFinalOrder(topo=execweaveTopology,placement=positions){
76
+ if(!topo?.spec||!(placement instanceof Map))return new Map();
77
+ const byLane=new Map();
78
+ for(const [id,p] of placement){
79
+ const spec=topo.spec.get(id);
80
+ if(!spec||!Number.isFinite(p?.y))continue;
81
+ const key=String(spec.lane||'other');
82
+ if(!byLane.has(key))byLane.set(key,[]);
83
+ byLane.get(key).push(id);
84
+ }
85
+ const finalOrder=new Map();
86
+ for(const ids of byLane.values()){
87
+ ids.sort((a,b)=>{
88
+ const ap=placement.get(a),bp=placement.get(b);
89
+ return (ap.y-bp.y)||(ap.x-bp.x)||String(a).localeCompare(String(b));
90
+ });
91
+ ids.forEach((id,index)=>{
92
+ const spec=topo.spec.get(id);
93
+ if(spec)spec.finalOrder=index;
94
+ finalOrder.set(id,index);
95
+ });
96
+ }
97
+ topo.finalOrder=finalOrder;
98
+ return finalOrder;
99
+ }
100
+ window.execweaveLayoutV2SyncFinalOrder=execweaveLayoutV2SyncFinalOrder;
101
+
102
+ function execweaveLayoutV2ViewportAspect(){
103
+ try{
104
+ const el=(typeof svg!=='undefined'&&svg)||document.getElementById('svg');
105
+ const box=el&&typeof el.getBoundingClientRect==='function'?el.getBoundingClientRect():null;
106
+ const value=box&&box.width>0&&box.height>0?box.width/box.height:16/9;
107
+ return Math.max(1.2,Math.min(2.4,value));
108
+ }catch(_){return 16/9}
109
+ }
110
+
111
+ function execweaveLayoutV2PackingWidth(boxes,spineWidth,gap){
112
+ if(!Array.isArray(boxes)||!boxes.length)return spineWidth;
113
+ const targetAspect=execweaveLayoutV2ViewportAspect();
114
+ const totalArea=boxes.reduce((sum,box)=>sum+Math.max(1,box.w+gap)*Math.max(1,box.h+gap),0);
115
+ const widest=boxes.reduce((width,box)=>Math.max(width,box.w),0);
116
+ const base=Math.max(spineWidth,widest,Math.sqrt(totalArea*targetAspect));
117
+ const widths=boxes.map(box=>box.w).sort((a,b)=>b-a);
118
+ const breakpoints=[];
119
+ let prefix=0;
120
+ for(let i=0;i<Math.min(widths.length,8);i++){
121
+ prefix+=widths[i];
122
+ breakpoints.push(prefix+i*gap);
123
+ }
124
+ const candidates=[spineWidth,widest,...breakpoints,...[.8,1,1.2,1.45].map(scale=>base*scale)]
125
+ .map(value=>Math.max(widest,value))
126
+ .sort((a,b)=>a-b)
127
+ .filter((value,index,list)=>index===0||Math.abs(value-list[index-1])>1);
128
+ let bestWidth=candidates[0],bestCost=Infinity;
129
+ for(const width of candidates){
130
+ let x=0,y=0,rowHeight=0,maxRight=0;
131
+ for(const box of boxes){
132
+ if(x>0&&x+box.w>width){x=0;y+=rowHeight+gap;rowHeight=0}
133
+ maxRight=Math.max(maxRight,x+box.w);
134
+ rowHeight=Math.max(rowHeight,box.h);
135
+ x+=box.w+gap;
136
+ }
137
+ const height=Math.max(1,y+rowHeight),usedWidth=Math.max(1,maxRight);
138
+ const area=usedWidth*height;
139
+ const aspectError=Math.abs(Math.log((usedWidth/height)/targetAspect));
140
+ const cost=area*(1+.35*aspectError);
141
+ if(cost<bestCost-1e-6||(Math.abs(cost-bestCost)<=1e-6&&width<bestWidth)){bestCost=cost;bestWidth=width}
142
+ }
143
+ return Math.max(spineWidth,bestWidth);
144
+ }
145
+ window.execweaveLayoutV2PackingWidth=execweaveLayoutV2PackingWidth;
146
+
147
+ function execweaveLayoutV2FinalizeTopology(topo){
148
+ if(!topo?.spec||typeof nodeById==='undefined'||typeof edgeById==='undefined'||typeof execweaveComponents!=='function')return topo;
149
+ const nodes=[...nodeById.values()].filter(node=>topo.spec.has(node.id));
150
+ const edges=[...edgeById.values()].filter(edge=>topo.spec.has(edge.source)&&topo.spec.has(edge.target));
151
+ const componentOf=execweaveComponents(nodes,edges);
152
+ if(!componentOf.size)return topo;
153
+ const sizes=new Map();
154
+ for(const value of componentOf.values())sizes.set(value,(sizes.get(value)||0)+1);
155
+ const roots=nodes.filter(typeof execweaveIsRoot==='function'?execweaveIsRoot:()=>false);
156
+ let primary=roots.length?componentOf.get(roots[0].id):undefined;
157
+ if(primary===undefined){
158
+ let best=-1;
159
+ for(const [value,size] of [...sizes.entries()].sort((a,b)=>a[0]-b[0]))if(size>best){best=size;primary=value}
160
+ }
161
+ const agentIds=new Set(nodes.filter(node=>node?.type==='agent').map(node=>node.id));
162
+ const spineComponents=new Set([...componentOf.entries()].filter(([id])=>agentIds.has(id)).map(([,value])=>value));
163
+ if(primary!==undefined)spineComponents.add(primary);
164
+ const secondary=[...sizes.keys()].filter(value=>!spineComponents.has(value)).sort((a,b)=>a-b);
165
+ topo.secondaryPackedIds=new Set();
166
+ if(!secondary.length){
167
+ const placement=new Map([...topo.spec].map(([id,spec])=>[id,{x:spec.x,y:spec.y}]));
168
+ execweaveLayoutV2SyncFinalOrder(topo,placement);
169
+ if(typeof execweaveRecomputePorts==='function')execweaveRecomputePorts(topo);
170
+ return topo;
171
+ }
172
+
173
+ let spineLeft=Infinity,spineRight=-Infinity,spineFloor=-Infinity;
174
+ for(const [id,value] of componentOf){
175
+ if(!spineComponents.has(value))continue;
176
+ const spec=topo.spec.get(id);if(!spec)continue;
177
+ const w=topo.width?.get(id)||execweaveWidthOf(id),h=topo.height?.get(id)||execweaveHeightOf(id);
178
+ spineLeft=Math.min(spineLeft,spec.x);spineRight=Math.max(spineRight,spec.x+w);spineFloor=Math.max(spineFloor,spec.y+h);
179
+ }
180
+ if(!Number.isFinite(spineLeft)){spineLeft=0;spineRight=600;spineFloor=100}
181
+ const gap=64,spineWidth=Math.max(600,spineRight-spineLeft),boxes=[];
182
+
183
+ for(const value of secondary){
184
+ const members=[...componentOf.entries()].filter(([,component])=>component===value).map(([id])=>id).sort();
185
+ // The global lane pass can give two nodes in one detached component hundreds
186
+ // of pixels of unrelated vertical separation. Compact each X column locally
187
+ // before packing components, while preserving the current within-column order.
188
+ const columns=new Map();
189
+ for(const id of members){
190
+ const spec=topo.spec.get(id);if(!spec)continue;
191
+ const key=Number(spec.x).toFixed(3);
192
+ if(!columns.has(key))columns.set(key,[]);
193
+ columns.get(key).push(id);
194
+ }
195
+ for(const ids of columns.values()){
196
+ ids.sort((a,b)=>(topo.spec.get(a).y-topo.spec.get(b).y)||String(a).localeCompare(String(b)));
197
+ let y=0;
198
+ for(const id of ids){
199
+ const spec=topo.spec.get(id),h=topo.height?.get(id)||execweaveHeightOf(id);
200
+ spec.y=y;y+=h+24;
201
+ }
202
+ }
203
+ let minX=Infinity,maxX=-Infinity,minY=Infinity,maxY=-Infinity;
204
+ for(const id of members){
205
+ const spec=topo.spec.get(id);if(!spec)continue;
206
+ const w=topo.width?.get(id)||execweaveWidthOf(id),h=topo.height?.get(id)||execweaveHeightOf(id);
207
+ minX=Math.min(minX,spec.x);maxX=Math.max(maxX,spec.x+w);minY=Math.min(minY,spec.y);maxY=Math.max(maxY,spec.y+h);
208
+ }
209
+ if(Number.isFinite(minX))boxes.push({value,members,minX,maxX,minY,maxY,w:maxX-minX,h:maxY-minY});
210
+ }
211
+
212
+ const packingWidth=execweaveLayoutV2PackingWidth(boxes,spineWidth,gap);
213
+ let cursorX=spineLeft,cursorY=spineFloor+gap,rowHeight=0;
214
+ for(const box of boxes){
215
+ if(cursorX>spineLeft&&cursorX+box.w>spineLeft+packingWidth){cursorX=spineLeft;cursorY+=rowHeight+gap;rowHeight=0}
216
+ const shiftX=cursorX-box.minX,shiftY=cursorY-box.minY;
217
+ for(const id of box.members){
218
+ const spec=topo.spec.get(id);if(spec){spec.x+=shiftX;spec.y+=shiftY;topo.secondaryPackedIds.add(id)}
219
+ }
220
+ cursorX+=box.w+gap;rowHeight=Math.max(rowHeight,box.h);
221
+ }
222
+ const placement=new Map([...topo.spec].map(([id,spec])=>[id,{x:spec.x,y:spec.y}]));
223
+ execweaveLayoutV2SyncFinalOrder(topo,placement);
224
+ if(typeof execweaveRecomputePorts==='function')execweaveRecomputePorts(topo);
225
+ return topo;
226
+ }
227
+ window.execweaveLayoutV2FinalizeTopology=execweaveLayoutV2FinalizeTopology;
228
+
229
+ function execweaveLayoutV2HubInversionRate(){
230
+ if(typeof edgeById==='undefined'||typeof positions==='undefined'||!execweaveTopology?.targetPort)return 0;
231
+ const incoming=new Map();
232
+ for(const edge of edgeById.values()){
233
+ if(!positions.has(edge.source)||!positions.has(edge.target))continue;
234
+ const target=typeof nodeById!=='undefined'?nodeById.get(edge.target):null;
235
+ const lane=target?String(execweaveTopology.spec.get(target.id)?.lane||''):'';
236
+ if(lane!=='tool'&&lane!=='model')continue;
237
+ if(!incoming.has(edge.target))incoming.set(edge.target,[]);
238
+ incoming.get(edge.target).push(edge);
239
+ }
240
+ let worst=0;
241
+ for(const edges of incoming.values()){
242
+ if(edges.length<3)continue;
243
+ const ordered=[...edges].sort((a,b)=>{
244
+ const ap=positions.get(a.source),bp=positions.get(b.source);
245
+ return (ap.y-bp.y)||(ap.x-bp.x)||edgeId(a).localeCompare(edgeId(b));
246
+ });
247
+ const slots=ordered.map(edge=>execweaveTopology.targetPort.get(edgeId(edge))?.index??0);
248
+ let inv=0;
249
+ for(let i=0;i<slots.length;i++)for(let j=0;j<i;j++)if(slots[j]>slots[i])inv++;
250
+ worst=Math.max(worst,inv/Math.max(1,slots.length*(slots.length-1)/2));
251
+ }
252
+ return worst;
253
+ }
254
+
255
+ function execweaveLayoutV2OrderMismatches(){
256
+ if(typeof positions==='undefined'||!execweaveTopology?.spec)return 0;
257
+ const byLane=new Map();
258
+ for(const [id,p] of positions){
259
+ const spec=execweaveTopology.spec.get(id);if(!spec)continue;
260
+ const lane=String(spec.lane||'other');
261
+ if(!byLane.has(lane))byLane.set(lane,[]);
262
+ byLane.get(lane).push([id,p,spec]);
263
+ }
264
+ let mismatches=0;
265
+ for(const rows of byLane.values()){
266
+ rows.sort((a,b)=>(a[1].y-b[1].y)||(a[1].x-b[1].x)||String(a[0]).localeCompare(String(b[0])));
267
+ rows.forEach((row,index)=>{if(Number(row[2].finalOrder)!==index)mismatches++});
268
+ }
269
+ return mismatches;
270
+ }
271
+
272
+ const layoutV2MeasureBase=execweaveGeometry.measure.bind(execweaveGeometry);
273
+ execweaveGeometry.measure=function(nodes,edges){
274
+ const metrics=layoutV2MeasureBase(nodes,edges);
275
+ if(!nodes.length)return{...metrics,CROSSINGS_PER_EDGE:0,CROSSING_SPAN_RATE:0,NONINCIDENT_EDGE_CROSSINGS:0,CROSSING_SPAN_PAIRS:0,NODE_DENSITY:0,ASPECT_ERROR:0,P95_EDGE_STRETCH:0,HUB_PORT_INVERSION_RATE:0,FINAL_ORDER_AUTHORITY_MISMATCHES:0};
276
+ const left=Math.min(...nodes.map(n=>n.x)),right=Math.max(...nodes.map(n=>n.x+n.w));
277
+ const top=Math.min(...nodes.map(n=>n.y)),bottom=Math.max(...nodes.map(n=>n.y+n.h));
278
+ const width=Math.max(1,right-left),height=Math.max(1,bottom-top);
279
+ const nodeArea=nodes.reduce((sum,n)=>sum+Math.max(0,n.w*n.h),0);
280
+ const density=nodeArea/(width*height);
281
+ const targetAspect=execweaveLayoutV2ViewportAspect();
282
+ const aspectError=Math.abs(Math.log((width/height)/targetAspect));
283
+ const routes=edges.map(edge=>{
284
+ const points=execweaveGeometry.sample(edge.d);
285
+ return{edge,points,left:Math.min(...points.map(p=>p.x)),right:Math.max(...points.map(p=>p.x)),
286
+ top:Math.min(...points.map(p=>p.y)),bottom:Math.max(...points.map(p=>p.y))};
287
+ });
288
+ let spanPairs=0,nonincidentCrossings=0;
289
+ for(let i=0;i<routes.length;i++)for(let j=0;j<i;j++){
290
+ const a=routes[i],b=routes[j],ae=a.edge,be=b.edge;
291
+ if(ae.source===be.source||ae.source===be.target||ae.target===be.source||ae.target===be.target)continue;
292
+ if(a.right<b.left||b.right<a.left||a.bottom<b.top||b.bottom<a.top)continue;
293
+ spanPairs++;
294
+ let hit=false;
295
+ for(let ai=1;ai<a.points.length&&!hit;ai++)for(let bi=1;bi<b.points.length&&!hit;bi++)
296
+ hit=execweaveGeometry.crosses(a.points[ai-1],a.points[ai],b.points[bi-1],b.points[bi]);
297
+ if(hit)nonincidentCrossings++;
298
+ }
299
+ const stretch=routes.map(({points})=>{
300
+ let length=0;
301
+ for(let i=1;i<points.length;i++)length+=Math.hypot(points[i].x-points[i-1].x,points[i].y-points[i-1].y);
302
+ const first=points[0],last=points[points.length-1];
303
+ const lower=Math.max(1,Math.hypot(last.x-first.x,last.y-first.y));
304
+ return length/lower;
305
+ }).sort((a,b)=>a-b);
306
+ return{...metrics,
307
+ CROSSINGS_PER_EDGE:metrics.EDGE_CROSSINGS/Math.max(1,edges.length),
308
+ CROSSING_SPAN_RATE:nonincidentCrossings/Math.max(1,spanPairs),
309
+ NONINCIDENT_EDGE_CROSSINGS:nonincidentCrossings,
310
+ CROSSING_SPAN_PAIRS:spanPairs,
311
+ NODE_DENSITY:density,
312
+ ASPECT_ERROR:aspectError,
313
+ P95_EDGE_STRETCH:stretch[Math.max(0,Math.ceil(stretch.length*.95)-1)]||0,
314
+ HUB_PORT_INVERSION_RATE:execweaveLayoutV2HubInversionRate(),
315
+ FINAL_ORDER_AUTHORITY_MISMATCHES:execweaveLayoutV2OrderMismatches(),
316
+ GRAPH_BBOX_WIDTH:width,GRAPH_BBOX_HEIGHT:height};
317
+ };
318
+
319
+ function execweaveLayoutV2BusRoute(edge){
320
+ if(typeof edgeById==='undefined'||typeof positions==='undefined'||!execweaveTopology?.bundleByEdge)return null;
321
+ if(typeof execweaveIsStopped==='function'&&execweaveIsStopped(edge))return null;
322
+ const bundle=execweaveTopology.bundleByEdge.get(edgeId(edge));
323
+ if(!bundle||bundle.size<2)return null;
324
+ const members=[...edgeById.values()].filter(candidate=>{
325
+ const other=execweaveTopology.bundleByEdge.get(edgeId(candidate));
326
+ return other&&other.size>1&&other.key===bundle.key&&positions.has(candidate.source)&&positions.has(candidate.target);
327
+ }).sort((a,b)=>edgeId(a).localeCompare(edgeId(b)));
328
+ if(members.length<2||new Set(members.map(member=>member.target)).size!==1)return null;
329
+ const topo=execweaveTopology;
330
+ const anchors=members.map(member=>{
331
+ const sp=positions.get(member.source),tp=positions.get(member.target);
332
+ const sw=topo.width?.get(member.source)||execweaveWidthOf(member.source);
333
+ const tw=topo.width?.get(member.target)||execweaveWidthOf(member.target);
334
+ const forward=tp.x+tw/2>=sp.x+sw/2;
335
+ const sx=forward?sp.x+sw:sp.x,tx=forward?tp.x:tp.x+tw;
336
+ const sy=execweavePortY(sp,topo.sourcePort.get(edgeId(member)),member.source);
337
+ const ty=execweavePortY(tp,topo.targetPort.get(edgeId(member)),member.target);
338
+ return{member,forward,sx,sy,tx,ty};
339
+ });
340
+ if(new Set(anchors.map(row=>row.forward)).size!==1)return null;
341
+ const lo=Math.max(...anchors.map(row=>Math.min(row.sx,row.tx)));
342
+ const hi=Math.min(...anchors.map(row=>Math.max(row.sx,row.tx)));
343
+ if(!(hi-lo>=24))return null;
344
+ const groups=new Map();
345
+ for(const candidate of edgeById.values()){
346
+ const group=topo.bundleByEdge.get(edgeId(candidate));
347
+ if(!group||group.size<2||groups.has(group.key)||!positions.has(candidate.target))continue;
348
+ groups.set(group.key,{key:group.key,target:candidate.target,y:positions.get(candidate.target).y});
349
+ }
350
+ const orderedGroups=[...groups.values()].sort((a,b)=>a.y-b.y||a.key.localeCompare(b.key));
351
+ const groupIndex=Math.max(0,orderedGroups.findIndex(group=>group.key===bundle.key));
352
+ const fraction=orderedGroups.length<=1?.55:.36+.32*groupIndex/Math.max(1,orderedGroups.length-1);
353
+ const ideal=lo+(hi-lo)*fraction;
354
+ const boxes=[...positions].filter(([id])=>nodeById.has(id)).map(([id,p])=>({id,x:p.x,y:p.y,w:execweaveWidthOf(id),h:execweaveHeightOf(id)}));
355
+ const candidateXs=[ideal,lo+(hi-lo)*.44,lo+(hi-lo)*.56,
356
+ ...boxes.flatMap(box=>[box.x-6,box.x+box.w+6])]
357
+ .filter(x=>x>=lo&&x<=hi)
358
+ .sort((a,b)=>Math.abs(a-ideal)-Math.abs(b-ideal)||a-b);
359
+ const clear=x=>anchors.every(row=>{
360
+ const ownBoxes=boxes.filter(box=>box.id!==row.member.source&&box.id!==row.member.target);
361
+ const p={x,y:row.sy},q={x,y:row.ty};
362
+ return !ownBoxes.some(box=>execweaveGeometry.throughBox({x:row.sx,y:row.sy},p,box)||
363
+ execweaveGeometry.throughBox(p,q,box)||execweaveGeometry.throughBox(q,{x:row.tx,y:row.ty},box));
364
+ });
365
+ const trunkX=candidateXs.find(clear);
366
+ if(!Number.isFinite(trunkX))return null;
367
+ const row=anchors.find(item=>edgeId(item.member)===edgeId(edge));
368
+ if(!row)return null;
369
+ return{d:`M ${row.sx} ${row.sy} H ${trunkX} V ${row.ty} H ${row.tx}`,
370
+ labelX:(trunkX+row.tx)/2,labelY:row.ty-8,kind:'bundle',
371
+ geometryKind:'shared-resource-bus',bundle};
372
+ }
373
+ window.execweaveLayoutV2BusRoute=execweaveLayoutV2BusRoute;
374
+
375
+ window.__execweaveLayoutV2={
376
+ syncFinalOrder:execweaveLayoutV2SyncFinalOrder,
377
+ finalizeTopology:execweaveLayoutV2FinalizeTopology,
378
+ packingWidth:execweaveLayoutV2PackingWidth,
379
+ hubInversionRate:execweaveLayoutV2HubInversionRate,
380
+ orderMismatches:execweaveLayoutV2OrderMismatches,
381
+ busRoute:execweaveLayoutV2BusRoute,
382
+ mode:()=>window.__execweaveLayoutV2Mode,
383
+ };
384
+ })();
385
+ """.strip()
386
+
387
+
388
+ def _replace_once(html: str, old: str, new: str, error: str) -> str:
389
+ if html.count(old) != 1:
390
+ raise RuntimeError(error)
391
+ return html.replace(old, new, 1)
392
+
393
+
394
+ def inject_layout_v2(html: str) -> str:
395
+ """Install Layout V2 authority, shared-resource routing and quality objectives."""
396
+
397
+ html = _replace_once(
398
+ html, _BAND_GAP_SEAM, "const EXECWEAVE_BAND_GAP=64;", "layout-v2 band-gap seam changed"
399
+ )
400
+ html = _replace_once(
401
+ html, _PACKING_SEAM, _PACKING_REPLACEMENT, "layout-v2 packing seam changed"
402
+ )
403
+ html = _replace_once(
404
+ html, _SYNC_ORDER_SEAM, _SYNC_ORDER_REPLACEMENT, "layout-v2 final-order seam changed"
405
+ )
406
+ html = _replace_once(
407
+ html, _BUS_ROUTE_SEAM, _BUS_ROUTE_REPLACEMENT, "layout-v2 bundle route seam changed"
408
+ )
409
+ html = _replace_once(
410
+ html, _BUILD_TOPOLOGY_SEAM, _BUILD_TOPOLOGY_REPLACEMENT, "layout-v2 topology-finalize seam changed"
411
+ )
412
+ html = _replace_once(
413
+ html, _INPUT_KEY_SEAM, _INPUT_KEY_REPLACEMENT, "layout-v2 cache-key seam changed"
414
+ )
415
+ html = _replace_once(
416
+ html, _BUDGET_SEAM, _BUDGET_REPLACEMENT, "layout-v2 optimization budget seam changed"
417
+ )
418
+ html = _replace_once(
419
+ html, _BETTER_SEAM, _BETTER_REPLACEMENT, "layout-v2 objective seam changed"
420
+ )
421
+ html = _replace_once(
422
+ html, _ROUNDS_SEAM, _ROUNDS_REPLACEMENT, "layout-v2 optimization round seam changed"
423
+ )
424
+ html = _replace_once(
425
+ html, _ARRANGE_SEAM, _ARRANGE_REPLACEMENT, "layout-v2 arrange-mode seam changed"
426
+ )
427
+ if html.count(_STARTUP_SEAM) != 1:
428
+ raise RuntimeError("layout-v2 startup seam changed")
429
+ return html.replace(_STARTUP_SEAM, LAYOUT_V2_SCRIPT + "\n" + _STARTUP_SEAM, 1)
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ _COMPONENT_SEAM = """ const nodes=[...nodeById.values()].filter(node=>topo.spec.has(node.id));
4
+ const edges=[...edgeById.values()].filter(edge=>topo.spec.has(edge.source)&&topo.spec.has(edge.target));
5
+ const componentOf=execweaveComponents(nodes,edges);"""
6
+ _COMPONENT_REPLACEMENT = """ const nodes=[...nodeById.values()].filter(node=>topo.spec.has(node.id));
7
+ const edges=[...edgeById.values()].filter(edge=>topo.spec.has(edge.source)&&topo.spec.has(edge.target));
8
+ // The orphan file summary is connected to the root by a viewer-only,
9
+ // non-causal OBSERVED_FILES bridge solely so the inspector can expose the
10
+ // collapsed evidence. That presentation bridge must not promote genuinely
11
+ // detached evidence into the execution spine for packing.
12
+ const detachedClusterIds=new Set(nodes.filter(node=>{
13
+ const attrs=node&&typeof node.attributes==='object'&&node.attributes?node.attributes:{};
14
+ return node.id==='viewer-cluster:orphan-files'||attrs.reason==='orphan_file_directory_nodes';
15
+ }).map(node=>node.id));
16
+ const layoutEdges=edges.filter(edge=>!detachedClusterIds.has(edge.source)&&!detachedClusterIds.has(edge.target));
17
+ const componentOf=execweaveComponents(nodes,layoutEdges);"""
18
+
19
+
20
+ def inject_layout_v2_detached_cluster_policy(html: str) -> str:
21
+ """Keep presentation-only orphan clusters out of execution-spine packing."""
22
+
23
+ if html.count(_COMPONENT_SEAM) != 1:
24
+ raise RuntimeError("layout-v2 detached-cluster component seam changed")
25
+ return html.replace(_COMPONENT_SEAM, _COMPONENT_REPLACEMENT, 1)
File without changes
File without changes
File without changes