execweave 0.8.18__tar.gz → 0.8.20__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.
- {execweave-0.8.18 → execweave-0.8.20}/PKG-INFO +1 -1
- {execweave-0.8.18 → execweave-0.8.20}/pyproject.toml +2 -2
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/__init__.py +1 -1
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/_dashboard_shell_base.py +5 -0
- execweave-0.8.20/src/execweave/viewer_edge_decross.py +117 -0
- execweave-0.8.20/src/execweave/viewer_execution_flow.py +433 -0
- execweave-0.8.20/src/execweave/viewer_execution_flow_safety.py +174 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_layout_geometry.py +75 -1
- {execweave-0.8.18 → execweave-0.8.20}/.gitignore +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/LICENSE +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/README.md +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/__main__.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/_conversation_records_core.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/_http_proxy_base.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/_http_proxy_bounded.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/_http_proxy_stage.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/agent_bootstrap.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/agent_topology.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/agent_trace.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/agy_preview_sanitize.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/analysis.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/anthropic.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/anthropic_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/anthropic_full_fidelity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/antigravity_adapter.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/antigravity_adapter_base.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/antigravity_full_fidelity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/antigravity_full_fidelity_base.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/antigravity_full_fidelity_collaboration_base.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/antigravity_hook_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/antigravity_record.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/antigravity_subagent_linkage.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/antigravity_trace_capability.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/auto_specialized.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/backends.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/benchmark.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/claude_adapter.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/claude_child_transcript.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/claude_delegation.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/claude_full_fidelity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/claude_hook_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/claude_hook_contract.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/claude_model_observer.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/claude_record.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_adapter.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_conversation.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_full_fidelity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_hook_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_hook_entry.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_hook_lifecycle.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_message_diagnostics.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_message_transport_diagnostics.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_record.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_rollout_structures.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_rollout_trace.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/codex_rollout_trace_base.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/collector.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/command.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/content_evidence.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/content_store.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_archive.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_message_identity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_preview.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_preview_antigravity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_preview_claude.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_preview_codex.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_preview_common.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_preview_generic.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_preview_lines.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_preview_transcript.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_records.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_records_antigravity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_records_codex.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_records_common.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/conversation_records_ollama.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/correlation.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/cursor_adapter.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/cursor_delegation.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/cursor_delegation_base.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/cursor_full_fidelity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/cursor_hook_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/cursor_hook_contract.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/cursor_record.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/dashboard_shell.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/entry.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/evidence_availability.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/evidence_grade.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/fidelity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/filesystem.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/focus.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/graph.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/graph_ops.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/http_proxy.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/http_proxy_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/inference_gateway.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/inference_gateway_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/inference_gateway_full_fidelity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/inference_identity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/inference_identity_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/integrity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/integrity_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/litellm_callback.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/litellm_callback_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live_core.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live_view.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live_view_extra_style.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live_view_markup.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live_view_process_layout.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live_view_readability.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live_view_script_a.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live_view_script_b.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live_view_script_c.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live_view_script_d.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/live_view_style.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/model_runtime.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/model_runtime_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/model_runtime_full_fidelity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/openai_compatible.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/openai_compatible_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/openai_compatible_full_fidelity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/opencode_adapter.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/opencode_event_contract.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/opencode_full_fidelity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/opencode_hook_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/opencode_plugin_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/opencode_record.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/opencode_task_linkage.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/overhead_benchmark.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/provider_capability.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/provider_lifecycle.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/provider_record.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/rule_pack.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/rule_pack_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/runtime_endpoint_availability.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/scalability_benchmark.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/schema.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/scope.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/semantic.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/sink.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/strace_backend.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/stream_assembly.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/theme.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/top.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/top_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/validate.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/vendor/dagre.min.js +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/view_cli.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_agent_panel.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_agent_panel_antigravity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_agent_panel_claude.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_agent_panel_codex.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_agent_panel_cursor.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_agent_panel_default.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_agent_panel_ollama.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_agent_panel_opencode.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_agent_panel_registry.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_content_inspector.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_dashboard_clean.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_dashboard_focus.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_dashboard_hardening.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_dashboard_hardening_v2.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_dashboard_pr70.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_external_endpoints.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_gif.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_layout_v2.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_layout_v2_detached.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_limits.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_live_layout.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_projection.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_projection_base.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_projection_bridges.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_semantic_projection.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/viewer_tool_mirror_identity.py +0 -0
- {execweave-0.8.18 → execweave-0.8.20}/src/execweave/workflow.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: execweave
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.20
|
|
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.
|
|
8
|
-
version = "0.8.
|
|
7
|
+
# ExecWeave v0.8.20 release metadata
|
|
8
|
+
version = "0.8.20"
|
|
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"
|
|
@@ -8,6 +8,9 @@ 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_edge_decross import inject_edge_decross
|
|
12
|
+
from .viewer_execution_flow import inject_execution_flow
|
|
13
|
+
from .viewer_execution_flow_safety import harden_execution_flow_projection
|
|
11
14
|
from .viewer_layout_v2 import inject_layout_v2
|
|
12
15
|
from .viewer_layout_v2_detached import inject_layout_v2_detached_cluster_policy
|
|
13
16
|
from .viewer_limits import resolve_viewer_limits, viewer_limits_bootstrap
|
|
@@ -76,6 +79,8 @@ def _build_dashboard_html() -> str:
|
|
|
76
79
|
html = inject_pr70_dashboard_repairs(html)
|
|
77
80
|
html = inject_layout_v2(html)
|
|
78
81
|
html = inject_layout_v2_detached_cluster_policy(html)
|
|
82
|
+
html = inject_edge_decross(html)
|
|
83
|
+
html = harden_execution_flow_projection(inject_execution_flow(html))
|
|
79
84
|
return _align_agent_panel_topology(inject_agent_panel(_guard_compact_live_snapshot(html)))
|
|
80
85
|
|
|
81
86
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
_STARTUP_SEAM = "applyTheme(initialTheme());applyTransform();poll();"
|
|
5
|
+
|
|
6
|
+
EDGE_DECROSS_SCRIPT = r"""
|
|
7
|
+
(function(){
|
|
8
|
+
if(typeof execweaveRoute!=='function'||typeof execweaveGeometry==='undefined')return;
|
|
9
|
+
const baseRoute=execweaveRoute;
|
|
10
|
+
const relation=edge=>String(edge?.relation||'').toUpperCase();
|
|
11
|
+
const nodeType=id=>String(typeof nodeById!=='undefined'?nodeById.get(id)?.type||'':'').toLowerCase();
|
|
12
|
+
const isNetworkEdge=edge=>relation(edge)==='CONNECTED_TO'||
|
|
13
|
+
nodeType(edge?.source)==='network_endpoint'||nodeType(edge?.target)==='network_endpoint';
|
|
14
|
+
const routePoints=edge=>{
|
|
15
|
+
const route=baseRoute(edge);
|
|
16
|
+
const points=route?.d?execweaveGeometry.polyline(route.d):null;
|
|
17
|
+
return{route,points};
|
|
18
|
+
};
|
|
19
|
+
const segments=points=>{
|
|
20
|
+
const result=[];
|
|
21
|
+
for(let i=1;i<(points?.length||0);i++)result.push({a:points[i-1],b:points[i]});
|
|
22
|
+
return result;
|
|
23
|
+
};
|
|
24
|
+
const crossesAny=(points,protectedSegments)=>{
|
|
25
|
+
for(let i=1;i<(points?.length||0);i++)for(const segment of protectedSegments)
|
|
26
|
+
if(execweaveGeometry.crosses(points[i-1],points[i],segment.a,segment.b))return true;
|
|
27
|
+
return false;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
execweaveRoute=function(edge){
|
|
31
|
+
const {route,points}=routePoints(edge);
|
|
32
|
+
if(!route||!points||points.length<2||isNetworkEdge(edge)||route.kind==='bundle'||route.kind==='lifecycle-return')return route;
|
|
33
|
+
if(typeof edgeById==='undefined'||typeof positions==='undefined'||!execweaveTopology)return route;
|
|
34
|
+
|
|
35
|
+
const protectedSegments=[];
|
|
36
|
+
for(const peer of edgeById.values()){
|
|
37
|
+
if(edgeId(peer)===edgeId(edge)||!isNetworkEdge(peer))continue;
|
|
38
|
+
const peerPoints=routePoints(peer).points;
|
|
39
|
+
if(peerPoints?.length>=2)protectedSegments.push(...segments(peerPoints));
|
|
40
|
+
}
|
|
41
|
+
if(!protectedSegments.length||!crossesAny(points,protectedSegments))return route;
|
|
42
|
+
|
|
43
|
+
const boxes=[...positions]
|
|
44
|
+
.filter(([id])=>id!==edge.source&&id!==edge.target&&nodeById.has(id))
|
|
45
|
+
.map(([id,p])=>({
|
|
46
|
+
id,x:p.x,y:p.y,
|
|
47
|
+
w:execweaveTopology.width?.get(id)||execweaveWidthOf(id),
|
|
48
|
+
h:execweaveTopology.height?.get(id)||execweaveHeightOf(id),
|
|
49
|
+
}));
|
|
50
|
+
const detour=execweaveGeometry.avoidCrossings(points,boxes,protectedSegments,8);
|
|
51
|
+
if(!detour||detour.crossingsAvoided<=0||detour.points.length<2)return route;
|
|
52
|
+
const d=detour.points.map((point,index)=>`${index?'L':'M'} ${point.x} ${point.y}`).join(' ');
|
|
53
|
+
const label=detour.points[Math.floor(detour.points.length/2)];
|
|
54
|
+
return{
|
|
55
|
+
...route,d,labelX:label.x,labelY:label.y-8,
|
|
56
|
+
geometryKind:'edge-decrossover',
|
|
57
|
+
edgeCrossingDetours:detour.detours,
|
|
58
|
+
protectedNetworkCrossingsAvoided:detour.crossingsAvoided,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if(typeof updateEdgeElement==='function'){
|
|
63
|
+
const updateBase=updateEdgeElement;
|
|
64
|
+
updateEdgeElement=function(edge){
|
|
65
|
+
// The existing PR70 wrapper already computes label placement and then clears
|
|
66
|
+
// it from rendered node boxes. Do not overwrite that cleared label position.
|
|
67
|
+
updateBase(edge);
|
|
68
|
+
const route=execweaveRoute(edge);
|
|
69
|
+
if(route?.geometryKind!=='edge-decrossover'||!route.d)return;
|
|
70
|
+
const els=edgeElements.get(edgeId(edge));if(!els)return;
|
|
71
|
+
// The base renderer normally sees the dynamically replaced execweaveRoute.
|
|
72
|
+
// Reapply only the path as a final authority guard; leave the label untouched.
|
|
73
|
+
els.visible.setAttribute('d',route.d);els.hit.setAttribute('d',route.d);
|
|
74
|
+
els.visible.dataset.geometryKind=route.geometryKind;
|
|
75
|
+
els.hit.dataset.geometryKind=route.geometryKind;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const renderedMetrics=()=>{
|
|
80
|
+
if(typeof positions==='undefined'||typeof edgeById==='undefined'||typeof nodeById==='undefined')return null;
|
|
81
|
+
const nodes=[...positions]
|
|
82
|
+
.filter(([id])=>nodeById.has(id))
|
|
83
|
+
.map(([id,p])=>({
|
|
84
|
+
id,x:p.x,y:p.y,
|
|
85
|
+
w:execweaveTopology?.width?.get(id)||execweaveWidthOf(id),
|
|
86
|
+
h:execweaveTopology?.height?.get(id)||execweaveHeightOf(id),
|
|
87
|
+
}));
|
|
88
|
+
const edges=[...edgeById.values()]
|
|
89
|
+
.filter(edge=>positions.has(edge.source)&&positions.has(edge.target))
|
|
90
|
+
.map(edge=>({...edge,d:execweaveRoute(edge).d}));
|
|
91
|
+
return execweaveGeometry.measure(nodes,edges);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// PR70's optimizer deliberately keeps its own pre-decrossover route family, but
|
|
95
|
+
// public metrics are a rendered-SVG diagnostic contract. Report the final paths
|
|
96
|
+
// users actually see so metric parity cannot drift after this local routing pass.
|
|
97
|
+
if(window.__execweavePr70&&typeof window.__execweavePr70.metrics==='function'){
|
|
98
|
+
const metricsBase=window.__execweavePr70.metrics.bind(window.__execweavePr70);
|
|
99
|
+
window.__execweavePr70.metrics=()=>renderedMetrics()||metricsBase();
|
|
100
|
+
window.__execweavePr70.crossingCount=()=>window.__execweavePr70.metrics().EDGE_CROSSINGS;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
window.__execweaveEdgeDecross={
|
|
104
|
+
version:2,
|
|
105
|
+
routeFor:id=>{const edge=typeof edgeById!=='undefined'?edgeById.get(id):null;return edge?execweaveRoute(edge):null},
|
|
106
|
+
metrics:renderedMetrics,
|
|
107
|
+
};
|
|
108
|
+
})();
|
|
109
|
+
""".strip()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def inject_edge_decross(html: str) -> str:
|
|
113
|
+
"""Prefer local route detours over moving nodes across network evidence edges."""
|
|
114
|
+
|
|
115
|
+
if html.count(_STARTUP_SEAM) != 1:
|
|
116
|
+
raise RuntimeError("edge-decrossover startup seam changed")
|
|
117
|
+
return html.replace(_STARTUP_SEAM, EDGE_DECROSS_SCRIPT + "\n" + _STARTUP_SEAM, 1)
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
_STARTUP_SEAM = "applyTheme(initialTheme());applyTransform();poll();"
|
|
4
|
+
|
|
5
|
+
EXECUTION_FLOW_SCRIPT = r"""
|
|
6
|
+
(function(){
|
|
7
|
+
const MODEL_RELATIONS=new Set([
|
|
8
|
+
'USED_MODEL','INVOKED_MODEL','INVOKES_MODEL','INFERRED','SERVED_BY_MODEL',
|
|
9
|
+
'SWITCHED_MODEL','MODEL_INVOCATION_COMPLETED'
|
|
10
|
+
]);
|
|
11
|
+
const DIRECT_ACTION_RELATIONS=new Map([
|
|
12
|
+
['SPAWNED_AGENT','spawn_agent'],
|
|
13
|
+
['SPAWNED_SUBAGENT','spawn_agent'],
|
|
14
|
+
['ASSIGNED_AGENT_TASK','assign_agent_task'],
|
|
15
|
+
['REQUESTED_SUBTASK','assign_agent_task'],
|
|
16
|
+
['SENT_AGENT_MESSAGE','send_input'],
|
|
17
|
+
['CLOSED_AGENT','close_agent'],
|
|
18
|
+
]);
|
|
19
|
+
const ACTION_RELATION=new Map([
|
|
20
|
+
['spawn_agent','SPAWNED_AGENT'],
|
|
21
|
+
['assign_agent_task','ASSIGNED_AGENT_TASK'],
|
|
22
|
+
['send_input','SENT_AGENT_MESSAGE'],
|
|
23
|
+
['wait_agent','WAITED_FOR'],
|
|
24
|
+
['resume_agent','RESUMED_AGENT'],
|
|
25
|
+
['close_agent','CLOSED_AGENT'],
|
|
26
|
+
]);
|
|
27
|
+
const ACTION_ALIASES=new Map([
|
|
28
|
+
['spawn','spawn_agent'],['spawnagent','spawn_agent'],['spawn_agent','spawn_agent'],
|
|
29
|
+
['spawn_subagent','spawn_agent'],['create_agent','spawn_agent'],['create_subagent','spawn_agent'],
|
|
30
|
+
['send','send_input'],['sendinput','send_input'],['send_input','send_input'],
|
|
31
|
+
['sendmessage','send_input'],['send_message','send_input'],['agent_message','send_input'],
|
|
32
|
+
['wait','wait_agent'],['waitagent','wait_agent'],['wait_agent','wait_agent'],
|
|
33
|
+
['wait_for_agent','wait_agent'],['wait_for_agents','wait_agent'],
|
|
34
|
+
['resume','resume_agent'],['resumeagent','resume_agent'],['resume_agent','resume_agent'],
|
|
35
|
+
['close','close_agent'],['closeagent','close_agent'],['close_agent','close_agent'],
|
|
36
|
+
['assign_agent_task','assign_agent_task'],['assign_task','assign_agent_task'],
|
|
37
|
+
['request_subtask','assign_agent_task'],['subtask','assign_agent_task'],
|
|
38
|
+
]);
|
|
39
|
+
const attrsOf=node=>node&&typeof node.attributes==='object'&&node.attributes?node.attributes:{};
|
|
40
|
+
const relation=edge=>String(edge?.relation||'').toUpperCase();
|
|
41
|
+
const actionName=value=>{
|
|
42
|
+
const text=String(value||'').trim().toLowerCase().replace(/[\s-]+/g,'_');
|
|
43
|
+
return ACTION_ALIASES.get(text)||null;
|
|
44
|
+
};
|
|
45
|
+
const moment=item=>{
|
|
46
|
+
if(Number.isInteger(item?.first_sequence))return{kind:0,value:item.first_sequence};
|
|
47
|
+
const attrs=attrsOf(item);
|
|
48
|
+
if(Number.isInteger(attrs.first_sequence))return{kind:0,value:attrs.first_sequence};
|
|
49
|
+
const stamp=item?.first_seen||attrs.first_seen||attrs.timestamp;
|
|
50
|
+
if(stamp)return{kind:1,value:String(stamp)};
|
|
51
|
+
return{kind:2,value:String(item?.id||'')};
|
|
52
|
+
};
|
|
53
|
+
const momentLE=(left,right)=>{
|
|
54
|
+
if(!left||!right)return false;
|
|
55
|
+
if(left.kind!==right.kind)return left.kind<right.kind;
|
|
56
|
+
return left.value<=right.value;
|
|
57
|
+
};
|
|
58
|
+
const provider=node=>String(attrsOf(node).provider||'unknown').toLowerCase();
|
|
59
|
+
|
|
60
|
+
function execweaveExecutionFlowProjection(display,raw){
|
|
61
|
+
if(!display||!Array.isArray(display.nodes)||!Array.isArray(display.edges))return display;
|
|
62
|
+
const rawNodes=Array.isArray(raw?.nodes)?raw.nodes:[],rawEdges=Array.isArray(raw?.edges)?raw.edges:[];
|
|
63
|
+
const rawById=new Map(rawNodes.filter(node=>node?.id).map(node=>[node.id,node]));
|
|
64
|
+
const agents=new Map(rawNodes.filter(node=>node?.type==='agent'&&node.id).map(node=>[node.id,node]));
|
|
65
|
+
if(!agents.size)return display;
|
|
66
|
+
const models=new Map(rawNodes.filter(node=>node?.type==='model'&&node.id).map(node=>[node.id,node]));
|
|
67
|
+
|
|
68
|
+
const incoming=new Map(),outgoing=new Map();
|
|
69
|
+
for(const edge of rawEdges){
|
|
70
|
+
if(!edge)continue;
|
|
71
|
+
if(!incoming.has(edge.target))incoming.set(edge.target,[]);
|
|
72
|
+
if(!outgoing.has(edge.source))outgoing.set(edge.source,[]);
|
|
73
|
+
incoming.get(edge.target).push(edge);outgoing.get(edge.source).push(edge);
|
|
74
|
+
}
|
|
75
|
+
const aliasToAgent=new Map();
|
|
76
|
+
for(const [id,node] of agents){
|
|
77
|
+
const attrs=attrsOf(node);
|
|
78
|
+
const aliases=[id,node.name,attrs.agent_id,attrs.thread_id,attrs.agent_path,attrs.child_agent_path,attrs.nickname];
|
|
79
|
+
for(const value of aliases)if(value!==undefined&&value!==null&&String(value).trim())aliasToAgent.set(String(value),id);
|
|
80
|
+
}
|
|
81
|
+
const resolveAgent=value=>{
|
|
82
|
+
if(value===undefined||value===null)return null;
|
|
83
|
+
const text=String(value);
|
|
84
|
+
if(agents.has(text))return text;
|
|
85
|
+
return aliasToAgent.get(text)||null;
|
|
86
|
+
};
|
|
87
|
+
const ownerForNode=node=>{
|
|
88
|
+
if(!node?.id)return null;
|
|
89
|
+
for(const edge of incoming.get(node.id)||[])if(agents.has(edge.source))return edge.source;
|
|
90
|
+
for(const edge of incoming.get(node.id)||[]){
|
|
91
|
+
const parent=rawById.get(edge.source);
|
|
92
|
+
if(parent?.type!=='agent_turn')continue;
|
|
93
|
+
for(const grand of incoming.get(parent.id)||[])if(agents.has(grand.source))return grand.source;
|
|
94
|
+
}
|
|
95
|
+
const attrs=attrsOf(node);
|
|
96
|
+
for(const key of ['owner_agent_id','agent_id','source_agent_id','thread_id','agent_path']){
|
|
97
|
+
const found=resolveAgent(attrs[key]);if(found)return found;
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
};
|
|
101
|
+
const agentForAnchor=id=>{
|
|
102
|
+
if(agents.has(id))return id;
|
|
103
|
+
const node=rawById.get(id);
|
|
104
|
+
if(!node)return null;
|
|
105
|
+
return ownerForNode(node);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const modelNameIndex=new Map();
|
|
109
|
+
for(const [id,node] of models){
|
|
110
|
+
const attrs=attrsOf(node);
|
|
111
|
+
for(const value of [id,node.name,attrs.model,attrs.model_name,attrs.native_name]){
|
|
112
|
+
if(value!==undefined&&value!==null&&String(value).trim())modelNameIndex.set(String(value).toLowerCase(),id);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const modelHintFor=node=>{
|
|
116
|
+
const attrs=attrsOf(node);
|
|
117
|
+
for(const key of ['model','model_name','codex_model','default_model','provider_model']){
|
|
118
|
+
const value=attrs[key];
|
|
119
|
+
if(value!==undefined&&value!==null&&String(value).trim())return String(value);
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
};
|
|
123
|
+
const modelIdFromHint=hint=>{
|
|
124
|
+
if(!hint)return null;
|
|
125
|
+
const lower=String(hint).toLowerCase();
|
|
126
|
+
if(modelNameIndex.has(lower))return modelNameIndex.get(lower);
|
|
127
|
+
for(const [key,id] of modelNameIndex)if(key.endsWith(lower)||lower.endsWith(key))return id;
|
|
128
|
+
return null;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const modelEventsByAgent=new Map();
|
|
132
|
+
const addModelEvent=(owner,modelId,item)=>{
|
|
133
|
+
if(!owner||!modelId||!agents.has(owner)||!models.has(modelId))return;
|
|
134
|
+
if(!modelEventsByAgent.has(owner))modelEventsByAgent.set(owner,[]);
|
|
135
|
+
modelEventsByAgent.get(owner).push({modelId,moment:moment(item||{}),edge:item});
|
|
136
|
+
};
|
|
137
|
+
for(const edge of rawEdges){
|
|
138
|
+
if(!agents.has(edge?.source)||!models.has(edge?.target)||!MODEL_RELATIONS.has(relation(edge)))continue;
|
|
139
|
+
addModelEvent(edge.source,edge.target,edge);
|
|
140
|
+
}
|
|
141
|
+
// Hooks and rollout formats often carry the active model on an agent-owned
|
|
142
|
+
// tool/turn/interaction object even when no direct agent -> model edge exists.
|
|
143
|
+
for(const node of rawNodes){
|
|
144
|
+
const owner=ownerForNode(node);
|
|
145
|
+
if(!owner)continue;
|
|
146
|
+
const modelId=modelIdFromHint(modelHintFor(node));
|
|
147
|
+
if(modelId)addModelEvent(owner,modelId,node);
|
|
148
|
+
}
|
|
149
|
+
for(const list of modelEventsByAgent.values())list.sort((a,b)=>{
|
|
150
|
+
if(a.moment.kind!==b.moment.kind)return a.moment.kind-b.moment.kind;
|
|
151
|
+
return a.moment.value<b.moment.value?-1:a.moment.value>b.moment.value?1:0;
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const resolveModel=(owner,item)=>{
|
|
155
|
+
const hinted=modelIdFromHint(modelHintFor(item));
|
|
156
|
+
if(hinted)return hinted;
|
|
157
|
+
const list=modelEventsByAgent.get(owner)||[];
|
|
158
|
+
if(!list.length)return null;
|
|
159
|
+
const at=moment(item);
|
|
160
|
+
let chosen=null;
|
|
161
|
+
for(const event of list)if(momentLE(event.moment,at))chosen=event.modelId;
|
|
162
|
+
if(chosen)return chosen;
|
|
163
|
+
const unique=[...new Set(list.map(event=>event.modelId))];
|
|
164
|
+
return unique.length===1?unique[0]:null;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const occurrences=[];
|
|
168
|
+
const pushOccurrence=(kind,owner,targets,item,rawNodeIds=[],rawEdgeIds=[])=>{
|
|
169
|
+
if(!kind||!owner||!agents.has(owner))return;
|
|
170
|
+
occurrences.push({
|
|
171
|
+
kind,owner,targets:new Set((targets||[]).filter(id=>agents.has(id)&&id!==owner)),
|
|
172
|
+
item:item||{},modelId:resolveModel(owner,item||{}),
|
|
173
|
+
rawNodeIds:[...rawNodeIds],rawEdgeIds:[...rawEdgeIds],
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
for(const node of rawNodes){
|
|
178
|
+
if(node?.type!=='agent_interaction'||!node.id)continue;
|
|
179
|
+
const kind=actionName(attrsOf(node).kind||node.name);
|
|
180
|
+
if(!kind)continue;
|
|
181
|
+
const sources=(incoming.get(node.id)||[]).filter(edge=>relation(edge)==='STARTED_AGENT_INTERACTION');
|
|
182
|
+
const targets=(outgoing.get(node.id)||[]).filter(edge=>relation(edge)==='TARGETED_BY_AGENT_INTERACTION');
|
|
183
|
+
const owner=sources.map(edge=>agentForAnchor(edge.source)).find(Boolean);
|
|
184
|
+
const peerIds=targets.map(edge=>agentForAnchor(edge.target)).filter(Boolean);
|
|
185
|
+
if(owner)pushOccurrence(kind,owner,peerIds,node,[node.id],[...sources,...targets].map(edge=>edge.id).filter(Boolean));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
for(const node of rawNodes){
|
|
189
|
+
if(node?.type!=='agent_message'||!node.id)continue;
|
|
190
|
+
const sent=(incoming.get(node.id)||[]).filter(edge=>relation(edge)==='SENT_AGENT_MESSAGE');
|
|
191
|
+
const delivered=(outgoing.get(node.id)||[]).filter(edge=>relation(edge)==='DELIVERED_AGENT_MESSAGE');
|
|
192
|
+
const owner=sent.map(edge=>agentForAnchor(edge.source)).find(Boolean);
|
|
193
|
+
const peerIds=delivered.map(edge=>agentForAnchor(edge.target)).filter(Boolean);
|
|
194
|
+
if(owner)pushOccurrence('send_input',owner,peerIds,node,[node.id],[...sent,...delivered].map(edge=>edge.id).filter(Boolean));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
for(const edge of rawEdges){
|
|
198
|
+
const kind=DIRECT_ACTION_RELATIONS.get(relation(edge));
|
|
199
|
+
if(!kind)continue;
|
|
200
|
+
const owner=agentForAnchor(edge.source),target=agentForAnchor(edge.target);
|
|
201
|
+
if(owner&&target&&owner!==target)pushOccurrence(kind,owner,[target],edge,[],[edge.id].filter(Boolean));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
for(const call of rawNodes){
|
|
205
|
+
if(!['tool_call','tool_call_observation'].includes(String(call?.type||''))||!call.id)continue;
|
|
206
|
+
let tool=null;
|
|
207
|
+
for(const edge of outgoing.get(call.id)||[]){
|
|
208
|
+
if(relation(edge)!=='USES_TOOL')continue;
|
|
209
|
+
const candidate=rawById.get(edge.target);
|
|
210
|
+
if(candidate?.type==='tool'){tool=candidate;break}
|
|
211
|
+
}
|
|
212
|
+
const kind=actionName(attrsOf(call).tool_name||call.name||attrsOf(tool).native_name||tool?.name);
|
|
213
|
+
if(!kind)continue;
|
|
214
|
+
const owner=ownerForNode(call);
|
|
215
|
+
if(!owner)continue;
|
|
216
|
+
const attrs=attrsOf(call),targets=[];
|
|
217
|
+
for(const key of ['target_agent_id','recipient_agent_id','recipient','agent_id']){
|
|
218
|
+
const found=resolveAgent(attrs[key]);if(found&&found!==owner)targets.push(found);
|
|
219
|
+
}
|
|
220
|
+
for(const key of ['target_agent_ids','recipient_agent_ids','recipients','agent_ids']){
|
|
221
|
+
const values=attrs[key];
|
|
222
|
+
if(Array.isArray(values))for(const value of values){const found=resolveAgent(value);if(found&&found!==owner)targets.push(found)}
|
|
223
|
+
}
|
|
224
|
+
pushOccurrence(kind,owner,targets,call,[call.id],[]);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if(!occurrences.length&&![...modelEventsByAgent.values()].some(list=>list.length))return display;
|
|
228
|
+
|
|
229
|
+
const groups=new Map();
|
|
230
|
+
for(const occurrence of occurrences){
|
|
231
|
+
const key=`${occurrence.owner}\u0000${occurrence.kind}\u0000${occurrence.modelId||''}`;
|
|
232
|
+
let group=groups.get(key);
|
|
233
|
+
if(!group){
|
|
234
|
+
group={owner:occurrence.owner,kind:occurrence.kind,modelId:occurrence.modelId,targets:new Set(),rawNodeIds:new Set(),rawEdgeIds:new Set(),occurrences:[]};
|
|
235
|
+
groups.set(key,group);
|
|
236
|
+
}
|
|
237
|
+
for(const id of occurrence.targets)group.targets.add(id);
|
|
238
|
+
for(const id of occurrence.rawNodeIds)group.rawNodeIds.add(id);
|
|
239
|
+
for(const id of occurrence.rawEdgeIds)group.rawEdgeIds.add(id);
|
|
240
|
+
group.occurrences.push(occurrence);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const roots=[...agents.values()].filter(node=>{
|
|
244
|
+
const attrs=attrsOf(node);
|
|
245
|
+
return attrs.viewer_root===true||attrs.agent_role==='root'||attrs.agent_path==='/root'||attrs.root_agent_path==='/root'||node.name==='/root';
|
|
246
|
+
}).map(node=>node.id);
|
|
247
|
+
const spawnParents=new Map();
|
|
248
|
+
for(const group of groups.values()){
|
|
249
|
+
if(group.kind!=='spawn_agent')continue;
|
|
250
|
+
for(const child of group.targets)if(!spawnParents.has(child))spawnParents.set(child,group.owner);
|
|
251
|
+
}
|
|
252
|
+
if(!roots.length){
|
|
253
|
+
const candidates=[...agents.keys()].filter(id=>!spawnParents.has(id));
|
|
254
|
+
if(candidates.length)roots.push(...candidates.sort());
|
|
255
|
+
}
|
|
256
|
+
const agentRank=new Map(roots.map(id=>[id,0]));
|
|
257
|
+
for(let pass=0;pass<agents.size+2;pass++){
|
|
258
|
+
let changed=false;
|
|
259
|
+
for(const group of groups.values()){
|
|
260
|
+
if(group.kind!=='spawn_agent'||!agentRank.has(group.owner))continue;
|
|
261
|
+
const modelOffset=group.modelId?1:0,actionRank=agentRank.get(group.owner)+modelOffset+1;
|
|
262
|
+
for(const child of group.targets){
|
|
263
|
+
const wanted=actionRank+1,current=agentRank.get(child);
|
|
264
|
+
if(current===undefined||wanted<current){agentRank.set(child,wanted);changed=true}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if(!changed)break;
|
|
268
|
+
}
|
|
269
|
+
for(const id of agents.keys())if(!agentRank.has(id))agentRank.set(id,0);
|
|
270
|
+
|
|
271
|
+
const addedNodes=[],addedEdges=[],contextIds=new Map(),contextifiedModels=new Set();
|
|
272
|
+
const contextId=(owner,modelId)=>`${owner}\u0000${modelId}`;
|
|
273
|
+
const ensureContext=(owner,modelId)=>{
|
|
274
|
+
if(!modelId||!models.has(modelId))return null;
|
|
275
|
+
const key=contextId(owner,modelId);
|
|
276
|
+
if(contextIds.has(key))return contextIds.get(key);
|
|
277
|
+
const model=models.get(modelId),id=`viewer:model-context:${encodeURIComponent(owner)}:${encodeURIComponent(modelId)}`;
|
|
278
|
+
const rank=(agentRank.get(owner)||0)+1;
|
|
279
|
+
const node={
|
|
280
|
+
id,type:'model',name:model.name||attrsOf(model).model||modelId,
|
|
281
|
+
attributes:{...attrsOf(model),viewer_only:true,viewer_model_context:true,owner_agent_id:owner,model_resource_id:modelId,viewer_flow_rank:rank,provider:attrsOf(model).provider||provider(agents.get(owner))}
|
|
282
|
+
};
|
|
283
|
+
addedNodes.push(node);
|
|
284
|
+
addedEdges.push({
|
|
285
|
+
id:`viewer:${owner}--MODEL_CONTEXT-->${id}`,source:owner,target:id,relation:'MODEL_CONTEXT',
|
|
286
|
+
count:1,viewer_only:true,causal:false,inferred:false,
|
|
287
|
+
attributes:{viewer_only:true,projection:'execution_flow',model_resource_id:modelId}
|
|
288
|
+
});
|
|
289
|
+
contextIds.set(key,id);contextifiedModels.add(modelId);return id;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
for(const [owner,list] of modelEventsByAgent){
|
|
293
|
+
for(const modelId of new Set(list.map(item=>item.modelId)))ensureContext(owner,modelId);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const consumedRawNodes=new Set(),consumedRawEdges=new Set(),actionToolIds=new Set();
|
|
297
|
+
for(const group of groups.values()){
|
|
298
|
+
const context=group.modelId?ensureContext(group.owner,group.modelId):null;
|
|
299
|
+
const source=context||group.owner;
|
|
300
|
+
const baseRank=agentRank.get(group.owner)||0;
|
|
301
|
+
const actionRank=baseRank+(context?2:1);
|
|
302
|
+
const id=`viewer:orchestration:${encodeURIComponent(group.owner)}:${encodeURIComponent(group.modelId||'unresolved')}:${group.kind}`;
|
|
303
|
+
addedNodes.push({
|
|
304
|
+
id,type:'tool',name:group.kind,
|
|
305
|
+
attributes:{
|
|
306
|
+
viewer_only:true,viewer_orchestration_action:true,orchestration_kind:group.kind,
|
|
307
|
+
owner_agent_id:group.owner,model_context_id:context,model_resource_id:group.modelId,
|
|
308
|
+
viewer_flow_rank:actionRank,provider:provider(agents.get(group.owner)),
|
|
309
|
+
evidence_node_ids:[...group.rawNodeIds].sort(),evidence_edge_ids:[...group.rawEdgeIds].sort(),
|
|
310
|
+
viewer_occurrence_count:group.occurrences.length,
|
|
311
|
+
viewer_model_context_unresolved:!context||undefined,
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
addedEdges.push({
|
|
315
|
+
id:`viewer:${source}--PERFORMED_ORCHESTRATION-->${id}`,source,target:id,
|
|
316
|
+
relation:'PERFORMED_ORCHESTRATION',count:group.occurrences.length,viewer_only:true,causal:false,inferred:false,
|
|
317
|
+
attributes:{viewer_only:true,projection:'execution_flow',orchestration_kind:group.kind,evidence_node_ids:[...group.rawNodeIds],evidence_edge_ids:[...group.rawEdgeIds]}
|
|
318
|
+
});
|
|
319
|
+
const targetRelation=ACTION_RELATION.get(group.kind)||'TARGETED_AGENT';
|
|
320
|
+
for(const target of [...group.targets].sort()){
|
|
321
|
+
addedEdges.push({
|
|
322
|
+
id:`viewer:${id}--${targetRelation}-->${target}`,source:id,target,relation:targetRelation,
|
|
323
|
+
count:1,viewer_only:true,causal:false,inferred:false,
|
|
324
|
+
attributes:{viewer_only:true,projection:'execution_flow',evidence_node_ids:[...group.rawNodeIds],evidence_edge_ids:[...group.rawEdgeIds]}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
for(const rawId of group.rawNodeIds)consumedRawNodes.add(rawId);
|
|
328
|
+
for(const rawId of group.rawEdgeIds)consumedRawEdges.add(rawId);
|
|
329
|
+
for(const rawId of group.rawNodeIds){
|
|
330
|
+
const rawNode=rawById.get(rawId);
|
|
331
|
+
if(!['tool_call','tool_call_observation'].includes(String(rawNode?.type||'')))continue;
|
|
332
|
+
for(const edge of outgoing.get(rawId)||[]){
|
|
333
|
+
if(relation(edge)==='USES_TOOL')actionToolIds.add(edge.target);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const flowNodes=display.nodes.map(node=>{
|
|
339
|
+
if(node?.type!=='agent'||!agentRank.has(node.id))return node;
|
|
340
|
+
return{...node,attributes:{...attrsOf(node),viewer_flow_rank:agentRank.get(node.id)}};
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
const modelRelation=edge=>MODEL_RELATIONS.has(relation(edge))||relation(edge)==='MODEL_CONTEXT';
|
|
344
|
+
const orchestrationRelation=edge=>DIRECT_ACTION_RELATIONS.has(relation(edge));
|
|
345
|
+
let nodes=flowNodes.filter(node=>!consumedRawNodes.has(node.id)&&!actionToolIds.has(node.id));
|
|
346
|
+
let edges=display.edges.filter(edge=>{
|
|
347
|
+
if(consumedRawEdges.has(edge.id))return false;
|
|
348
|
+
if(orchestrationRelation(edge)&&agents.has(edge.source)&&agents.has(edge.target))return false;
|
|
349
|
+
if(actionToolIds.has(edge.source)||actionToolIds.has(edge.target))return false;
|
|
350
|
+
if(contextifiedModels.has(edge.target)&&agents.has(edge.source)&&modelRelation(edge))return false;
|
|
351
|
+
return true;
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
for(const modelId of contextifiedModels){
|
|
355
|
+
const remaining=edges.filter(edge=>edge.source===modelId||edge.target===modelId);
|
|
356
|
+
if(!remaining.length)nodes=nodes.filter(node=>node.id!==modelId);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const known=new Set(nodes.map(node=>node.id));
|
|
360
|
+
for(const node of addedNodes)if(!known.has(node.id)){known.add(node.id);nodes.push(node)}
|
|
361
|
+
const edgeIds=new Set(edges.map(edge=>edge.id));
|
|
362
|
+
for(const edge of addedEdges)if(!edgeIds.has(edge.id)){edgeIds.add(edge.id);edges.push(edge)}
|
|
363
|
+
|
|
364
|
+
return{
|
|
365
|
+
...display,nodes,edges,node_count:nodes.length,edge_count:edges.length,
|
|
366
|
+
dashboard_projection:{
|
|
367
|
+
...(display.dashboard_projection||{}),
|
|
368
|
+
execution_flow_projection:true,
|
|
369
|
+
model_context_node_count:addedNodes.filter(node=>attrsOf(node).viewer_model_context).length,
|
|
370
|
+
orchestration_action_node_count:addedNodes.filter(node=>attrsOf(node).viewer_orchestration_action).length,
|
|
371
|
+
unique_agent_node_count:nodes.filter(node=>node.type==='agent').length,
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if(typeof execweaveDashboardGraph==='function'){
|
|
377
|
+
const execweaveDashboardGraphBeforeExecutionFlow=execweaveDashboardGraph;
|
|
378
|
+
execweaveDashboardGraph=function(data){
|
|
379
|
+
return execweaveExecutionFlowProjection(execweaveDashboardGraphBeforeExecutionFlow(data),data);
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function execweaveApplyExecutionFlowColumns(topo){
|
|
384
|
+
if(!topo?.spec||typeof nodeById==='undefined')return topo;
|
|
385
|
+
const ranked=[];
|
|
386
|
+
for(const [id,spec] of topo.spec){
|
|
387
|
+
const node=nodeById.get(id),rank=Number(attrsOf(node).viewer_flow_rank);
|
|
388
|
+
if(!node||!Number.isFinite(rank))continue;
|
|
389
|
+
ranked.push({id,spec,rank});
|
|
390
|
+
}
|
|
391
|
+
if(ranked.length<2)return topo;
|
|
392
|
+
const widthByRank=new Map();
|
|
393
|
+
for(const item of ranked)widthByRank.set(item.rank,Math.max(widthByRank.get(item.rank)||160,typeof execweaveWidthOf==='function'?execweaveWidthOf(item.id):160));
|
|
394
|
+
const ranks=[...widthByRank.keys()].sort((a,b)=>a-b);
|
|
395
|
+
const minRank=ranks[0],rootItems=ranked.filter(item=>item.rank===minRank);
|
|
396
|
+
let x=Math.min(...rootItems.map(item=>item.spec.x));
|
|
397
|
+
if(!Number.isFinite(x))x=0;
|
|
398
|
+
const xByRank=new Map();
|
|
399
|
+
for(const rank of ranks){
|
|
400
|
+
xByRank.set(rank,x);
|
|
401
|
+
x+=(widthByRank.get(rank)||160)+72;
|
|
402
|
+
}
|
|
403
|
+
for(const item of ranked){
|
|
404
|
+
item.spec.x=xByRank.get(item.rank);
|
|
405
|
+
item.spec.flowRank=item.rank;
|
|
406
|
+
}
|
|
407
|
+
if(typeof execweaveRecomputePorts==='function')execweaveRecomputePorts(topo);
|
|
408
|
+
return topo;
|
|
409
|
+
}
|
|
410
|
+
window.execweaveApplyExecutionFlowColumns=execweaveApplyExecutionFlowColumns;
|
|
411
|
+
|
|
412
|
+
if(typeof execweaveBuildTopology==='function'){
|
|
413
|
+
const execweaveBuildTopologyBeforeExecutionFlow=execweaveBuildTopology;
|
|
414
|
+
execweaveBuildTopology=function(){
|
|
415
|
+
return execweaveApplyExecutionFlowColumns(execweaveBuildTopologyBeforeExecutionFlow());
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
window.__execweaveExecutionFlow={
|
|
420
|
+
version:1,
|
|
421
|
+
project:execweaveExecutionFlowProjection,
|
|
422
|
+
applyColumns:execweaveApplyExecutionFlowColumns,
|
|
423
|
+
};
|
|
424
|
+
})();
|
|
425
|
+
"""
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def inject_execution_flow(html: str) -> str:
|
|
429
|
+
"""Add provider-neutral model/orchestration/agent presentation without changing evidence."""
|
|
430
|
+
|
|
431
|
+
if html.count(_STARTUP_SEAM) != 1:
|
|
432
|
+
raise RuntimeError("execution-flow startup seam changed")
|
|
433
|
+
return html.replace(_STARTUP_SEAM, EXECUTION_FLOW_SCRIPT + "\n" + _STARTUP_SEAM, 1)
|