execweave 0.8.19__tar.gz → 0.8.21__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.19 → execweave-0.8.21}/PKG-INFO +1 -1
- {execweave-0.8.19 → execweave-0.8.21}/pyproject.toml +2 -2
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/__init__.py +1 -1
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/_dashboard_shell_base.py +6 -1
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/antigravity_full_fidelity_collaboration_base.py +7 -1
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/cursor_delegation_base.py +5 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/opencode_task_linkage.py +10 -1
- execweave-0.8.21/src/execweave/viewer_edge_decross.py +117 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_execution_flow.py +222 -45
- execweave-0.8.21/src/execweave/viewer_execution_flow_provider_normalization.py +105 -0
- execweave-0.8.21/src/execweave/viewer_execution_flow_safety.py +364 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_layout_geometry.py +75 -1
- execweave-0.8.19/src/execweave/viewer_execution_flow_safety.py +0 -160
- {execweave-0.8.19 → execweave-0.8.21}/.gitignore +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/LICENSE +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/README.md +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/__main__.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/_conversation_records_core.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/_http_proxy_base.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/_http_proxy_bounded.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/_http_proxy_stage.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/agent_bootstrap.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/agent_topology.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/agent_trace.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/agy_preview_sanitize.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/analysis.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/anthropic.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/anthropic_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/anthropic_full_fidelity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/antigravity_adapter.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/antigravity_adapter_base.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/antigravity_full_fidelity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/antigravity_full_fidelity_base.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/antigravity_hook_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/antigravity_record.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/antigravity_subagent_linkage.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/antigravity_trace_capability.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/auto_specialized.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/backends.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/benchmark.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/claude_adapter.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/claude_child_transcript.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/claude_delegation.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/claude_full_fidelity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/claude_hook_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/claude_hook_contract.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/claude_model_observer.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/claude_record.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_adapter.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_conversation.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_full_fidelity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_hook_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_hook_entry.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_hook_lifecycle.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_message_diagnostics.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_message_transport_diagnostics.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_record.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_rollout_structures.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_rollout_trace.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/codex_rollout_trace_base.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/collector.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/command.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/content_evidence.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/content_store.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_archive.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_message_identity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_preview.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_preview_antigravity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_preview_claude.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_preview_codex.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_preview_common.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_preview_generic.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_preview_lines.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_preview_transcript.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_records.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_records_antigravity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_records_codex.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_records_common.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/conversation_records_ollama.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/correlation.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/cursor_adapter.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/cursor_delegation.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/cursor_full_fidelity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/cursor_hook_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/cursor_hook_contract.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/cursor_record.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/dashboard_shell.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/entry.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/evidence_availability.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/evidence_grade.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/fidelity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/filesystem.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/focus.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/graph.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/graph_ops.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/http_proxy.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/http_proxy_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/inference_gateway.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/inference_gateway_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/inference_gateway_full_fidelity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/inference_identity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/inference_identity_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/integrity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/integrity_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/litellm_callback.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/litellm_callback_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live_core.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live_view.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live_view_extra_style.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live_view_markup.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live_view_process_layout.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live_view_readability.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live_view_script_a.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live_view_script_b.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live_view_script_c.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live_view_script_d.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/live_view_style.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/model_runtime.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/model_runtime_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/model_runtime_full_fidelity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/openai_compatible.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/openai_compatible_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/openai_compatible_full_fidelity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/opencode_adapter.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/opencode_event_contract.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/opencode_full_fidelity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/opencode_hook_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/opencode_plugin_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/opencode_record.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/overhead_benchmark.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/provider_capability.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/provider_lifecycle.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/provider_record.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/rule_pack.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/rule_pack_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/runtime_endpoint_availability.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/scalability_benchmark.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/schema.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/scope.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/semantic.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/sink.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/strace_backend.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/stream_assembly.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/theme.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/top.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/top_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/validate.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/vendor/dagre.min.js +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/view_cli.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_agent_panel.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_agent_panel_antigravity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_agent_panel_claude.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_agent_panel_codex.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_agent_panel_cursor.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_agent_panel_default.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_agent_panel_ollama.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_agent_panel_opencode.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_agent_panel_registry.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_content_inspector.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_dashboard_clean.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_dashboard_focus.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_dashboard_hardening.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_dashboard_hardening_v2.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_dashboard_pr70.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_external_endpoints.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_gif.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_layout_v2.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_layout_v2_detached.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_limits.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_live_layout.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_projection.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_projection_base.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_projection_bridges.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_semantic_projection.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/src/execweave/viewer_tool_mirror_identity.py +0 -0
- {execweave-0.8.19 → execweave-0.8.21}/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.21
|
|
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.21 release metadata
|
|
8
|
+
version = "0.8.21"
|
|
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,7 +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
|
|
11
12
|
from .viewer_execution_flow import inject_execution_flow
|
|
13
|
+
from .viewer_execution_flow_provider_normalization import normalize_provider_execution_flow
|
|
12
14
|
from .viewer_execution_flow_safety import harden_execution_flow_projection
|
|
13
15
|
from .viewer_layout_v2 import inject_layout_v2
|
|
14
16
|
from .viewer_layout_v2_detached import inject_layout_v2_detached_cluster_policy
|
|
@@ -78,7 +80,10 @@ def _build_dashboard_html() -> str:
|
|
|
78
80
|
html = inject_pr70_dashboard_repairs(html)
|
|
79
81
|
html = inject_layout_v2(html)
|
|
80
82
|
html = inject_layout_v2_detached_cluster_policy(html)
|
|
81
|
-
html =
|
|
83
|
+
html = inject_edge_decross(html)
|
|
84
|
+
html = normalize_provider_execution_flow(
|
|
85
|
+
harden_execution_flow_projection(inject_execution_flow(html))
|
|
86
|
+
)
|
|
82
87
|
return _align_agent_panel_topology(inject_agent_panel(_guard_compact_live_snapshot(html)))
|
|
83
88
|
|
|
84
89
|
|
{execweave-0.8.19 → execweave-0.8.21}/src/execweave/antigravity_full_fidelity_collaboration_base.py
RENAMED
|
@@ -164,7 +164,13 @@ def _invoke_subagent_events(
|
|
|
164
164
|
relation="REQUESTED_SUBTASK",
|
|
165
165
|
source=parent,
|
|
166
166
|
target=subtask,
|
|
167
|
-
attributes={
|
|
167
|
+
attributes={
|
|
168
|
+
"child_identity_exposed": False,
|
|
169
|
+
# Exact requester -> provider conversation/step/subagent-index spec.
|
|
170
|
+
# Validated ASSIGNED_AGENT_TASK still required before inventing a child.
|
|
171
|
+
"identity_exact": True,
|
|
172
|
+
"identity_method": "antigravity_provider_conversation_step_subagent_index",
|
|
173
|
+
},
|
|
168
174
|
)
|
|
169
175
|
)
|
|
170
176
|
if type_name:
|
|
@@ -134,6 +134,11 @@ def cursor_delegation_events(
|
|
|
134
134
|
subtask = _subtask(payload, child)
|
|
135
135
|
visibility = agent_trace_visibility("cursor")
|
|
136
136
|
exact = {
|
|
137
|
+
# Persist the provider's exact subagent id through GraphEdge.to_dict(), not
|
|
138
|
+
# only in event-specific booleans, so the provider-neutral viewer can make
|
|
139
|
+
# the same fail-closed delegation decision from a materialized graph.
|
|
140
|
+
"identity_exact": True,
|
|
141
|
+
"identity_method": "cursor_provider_subagent_id",
|
|
137
142
|
"provider_subagent_id_exact": True,
|
|
138
143
|
"exact_child_agent_linkage": True,
|
|
139
144
|
**visibility,
|
|
@@ -172,6 +172,15 @@ def opencode_task_session_events(
|
|
|
172
172
|
if isinstance(background, bool):
|
|
173
173
|
attributes["background_requested"] = background
|
|
174
174
|
|
|
175
|
+
# GraphEdge deliberately persists identity_exact / identity_method while the
|
|
176
|
+
# provider-specific booleans above remain event-level evidence. Scope those
|
|
177
|
+
# generic identity claims to the task -> child assignment itself; task content
|
|
178
|
+
# edges should not inherit an unrelated identity method.
|
|
179
|
+
assignment_attributes = {
|
|
180
|
+
**attributes,
|
|
181
|
+
"identity_exact": True,
|
|
182
|
+
"identity_method": "opencode_task_tool_parent_and_child_session_ids",
|
|
183
|
+
}
|
|
175
184
|
child = _agent(child_session, agent_name=agent_name, parent_session_id=parent_session)
|
|
176
185
|
events: list[dict[str, Any]] = [
|
|
177
186
|
{
|
|
@@ -180,7 +189,7 @@ def opencode_task_session_events(
|
|
|
180
189
|
"relation": "ASSIGNED_AGENT_TASK",
|
|
181
190
|
"source": _tool_call(current_session, call_id),
|
|
182
191
|
"target": child,
|
|
183
|
-
"attributes":
|
|
192
|
+
"attributes": assignment_attributes,
|
|
184
193
|
}
|
|
185
194
|
]
|
|
186
195
|
|
|
@@ -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)
|
|
@@ -43,17 +43,33 @@ EXECUTION_FLOW_SCRIPT = r"""
|
|
|
43
43
|
return ACTION_ALIASES.get(text)||null;
|
|
44
44
|
};
|
|
45
45
|
const moment=item=>{
|
|
46
|
-
if(Number.isInteger(item?.first_sequence))return{kind:0,value:item.first_sequence};
|
|
47
46
|
const attrs=attrsOf(item);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return{
|
|
47
|
+
const sequence=Number.isInteger(item?.first_sequence)?item.first_sequence
|
|
48
|
+
:(Number.isInteger(attrs.first_sequence)?attrs.first_sequence:null);
|
|
49
|
+
const stamp=item?.first_seen||attrs.first_seen||attrs.timestamp||null;
|
|
50
|
+
return{
|
|
51
|
+
sequence:sequence,
|
|
52
|
+
stamp:stamp?String(stamp):null,
|
|
53
|
+
id:String(item?.id||''),
|
|
54
|
+
};
|
|
52
55
|
};
|
|
53
|
-
const
|
|
56
|
+
const momentComparable=(left,right)=>{
|
|
54
57
|
if(!left||!right)return false;
|
|
55
|
-
if(left.
|
|
56
|
-
|
|
58
|
+
if(left.sequence!==null&&right.sequence!==null)return true;
|
|
59
|
+
if(left.stamp&&right.stamp)return true;
|
|
60
|
+
return false;
|
|
61
|
+
};
|
|
62
|
+
const momentLE=(left,right)=>{
|
|
63
|
+
// Only compare when both ends share a sequence or a timestamp. Mixing kinds
|
|
64
|
+
// (or comparing id fallbacks) falsely orders early actions against future switches.
|
|
65
|
+
if(!momentComparable(left,right))return false;
|
|
66
|
+
if(left.sequence!==null&&right.sequence!==null)return left.sequence<=right.sequence;
|
|
67
|
+
return String(left.stamp)<=String(right.stamp);
|
|
68
|
+
};
|
|
69
|
+
const momentEqual=(left,right)=>{
|
|
70
|
+
if(!momentComparable(left,right))return false;
|
|
71
|
+
if(left.sequence!==null&&right.sequence!==null)return left.sequence===right.sequence;
|
|
72
|
+
return String(left.stamp)===String(right.stamp);
|
|
57
73
|
};
|
|
58
74
|
const provider=node=>String(attrsOf(node).provider||'unknown').toLowerCase();
|
|
59
75
|
|
|
@@ -73,30 +89,58 @@ EXECUTION_FLOW_SCRIPT = r"""
|
|
|
73
89
|
incoming.get(edge.target).push(edge);outgoing.get(edge.source).push(edge);
|
|
74
90
|
}
|
|
75
91
|
const aliasToAgent=new Map();
|
|
92
|
+
const rememberAlias=(alias,id)=>{
|
|
93
|
+
if(alias===undefined||alias===null)return;
|
|
94
|
+
const text=String(alias).trim();
|
|
95
|
+
if(!text)return;
|
|
96
|
+
let bucket=aliasToAgent.get(text);
|
|
97
|
+
if(!bucket){bucket=new Set();aliasToAgent.set(text,bucket)}
|
|
98
|
+
bucket.add(id);
|
|
99
|
+
};
|
|
76
100
|
for(const [id,node] of agents){
|
|
77
101
|
const attrs=attrsOf(node);
|
|
78
|
-
|
|
79
|
-
for(const value of
|
|
102
|
+
rememberAlias(id,id);
|
|
103
|
+
for(const value of [node.name,attrs.agent_id,attrs.thread_id,attrs.agent_path,attrs.child_agent_path,attrs.nickname]){
|
|
104
|
+
rememberAlias(value,id);
|
|
105
|
+
}
|
|
80
106
|
}
|
|
81
107
|
const resolveAgent=value=>{
|
|
82
108
|
if(value===undefined||value===null)return null;
|
|
83
109
|
const text=String(value);
|
|
110
|
+
// Exact canonical id always wins over alias lookup.
|
|
84
111
|
if(agents.has(text))return text;
|
|
85
|
-
|
|
112
|
+
const bucket=aliasToAgent.get(text);
|
|
113
|
+
if(!bucket||!bucket.size)return null;
|
|
114
|
+
if(bucket.size===1)return [...bucket][0];
|
|
115
|
+
// Non-unique alias: abstain rather than last-writer-wins.
|
|
116
|
+
return null;
|
|
117
|
+
};
|
|
118
|
+
const uniqueOwner=candidates=>{
|
|
119
|
+
const ids=[...new Set((candidates||[]).filter(Boolean))];
|
|
120
|
+
return ids.length===1?ids[0]:null;
|
|
86
121
|
};
|
|
87
122
|
const ownerForNode=node=>{
|
|
88
123
|
if(!node?.id)return null;
|
|
89
|
-
|
|
124
|
+
const direct=[];
|
|
125
|
+
for(const edge of incoming.get(node.id)||[])if(agents.has(edge.source))direct.push(edge.source);
|
|
126
|
+
const directOwner=uniqueOwner(direct);
|
|
127
|
+
if(directOwner)return directOwner;
|
|
128
|
+
if(direct.length)return null; // ambiguous direct owners
|
|
129
|
+
const viaTurn=[];
|
|
90
130
|
for(const edge of incoming.get(node.id)||[]){
|
|
91
131
|
const parent=rawById.get(edge.source);
|
|
92
132
|
if(parent?.type!=='agent_turn')continue;
|
|
93
|
-
for(const grand of incoming.get(parent.id)||[])if(agents.has(grand.source))
|
|
133
|
+
for(const grand of incoming.get(parent.id)||[])if(agents.has(grand.source))viaTurn.push(grand.source);
|
|
94
134
|
}
|
|
135
|
+
const turnOwner=uniqueOwner(viaTurn);
|
|
136
|
+
if(turnOwner)return turnOwner;
|
|
137
|
+
if(viaTurn.length)return null;
|
|
95
138
|
const attrs=attrsOf(node);
|
|
139
|
+
const attrOwners=[];
|
|
96
140
|
for(const key of ['owner_agent_id','agent_id','source_agent_id','thread_id','agent_path']){
|
|
97
|
-
const found=resolveAgent(attrs[key]);if(found)
|
|
141
|
+
const found=resolveAgent(attrs[key]);if(found)attrOwners.push(found);
|
|
98
142
|
}
|
|
99
|
-
return
|
|
143
|
+
return uniqueOwner(attrOwners);
|
|
100
144
|
};
|
|
101
145
|
const agentForAnchor=id=>{
|
|
102
146
|
if(agents.has(id))return id;
|
|
@@ -106,13 +150,23 @@ EXECUTION_FLOW_SCRIPT = r"""
|
|
|
106
150
|
};
|
|
107
151
|
|
|
108
152
|
const modelNameIndex=new Map();
|
|
153
|
+
const rememberModelAlias=(alias,id)=>{
|
|
154
|
+
if(alias===undefined||alias===null)return;
|
|
155
|
+
const text=String(alias).trim().toLowerCase();
|
|
156
|
+
if(!text)return;
|
|
157
|
+
let bucket=modelNameIndex.get(text);
|
|
158
|
+
if(!bucket){bucket=new Set();modelNameIndex.set(text,bucket)}
|
|
159
|
+
bucket.add(id);
|
|
160
|
+
};
|
|
109
161
|
for(const [id,node] of models){
|
|
110
162
|
const attrs=attrsOf(node);
|
|
111
|
-
|
|
112
|
-
|
|
163
|
+
rememberModelAlias(id,id);
|
|
164
|
+
for(const value of [node.name,attrs.model,attrs.model_name,attrs.native_name]){
|
|
165
|
+
rememberModelAlias(value,id);
|
|
113
166
|
}
|
|
114
167
|
}
|
|
115
168
|
const modelHintFor=node=>{
|
|
169
|
+
if(!node)return null;
|
|
116
170
|
const attrs=attrsOf(node);
|
|
117
171
|
for(const key of ['model','model_name','codex_model','default_model','provider_model']){
|
|
118
172
|
const value=attrs[key];
|
|
@@ -123,8 +177,13 @@ EXECUTION_FLOW_SCRIPT = r"""
|
|
|
123
177
|
const modelIdFromHint=hint=>{
|
|
124
178
|
if(!hint)return null;
|
|
125
179
|
const lower=String(hint).toLowerCase();
|
|
126
|
-
|
|
127
|
-
|
|
180
|
+
// Exact canonical id first.
|
|
181
|
+
if(models.has(hint))return hint;
|
|
182
|
+
if(models.has(lower))return lower;
|
|
183
|
+
const bucket=modelNameIndex.get(lower);
|
|
184
|
+
if(!bucket||!bucket.size)return null;
|
|
185
|
+
if(bucket.size===1)return [...bucket][0];
|
|
186
|
+
// Ambiguous model name across resources: abstain (no suffix fuzzy match).
|
|
128
187
|
return null;
|
|
129
188
|
};
|
|
130
189
|
|
|
@@ -147,20 +206,43 @@ EXECUTION_FLOW_SCRIPT = r"""
|
|
|
147
206
|
if(modelId)addModelEvent(owner,modelId,node);
|
|
148
207
|
}
|
|
149
208
|
for(const list of modelEventsByAgent.values())list.sort((a,b)=>{
|
|
150
|
-
|
|
151
|
-
|
|
209
|
+
// Prefer sequence when both have it; otherwise timestamp; never cross-rank kinds.
|
|
210
|
+
if(a.moment.sequence!==null&&b.moment.sequence!==null){
|
|
211
|
+
return a.moment.sequence-b.moment.sequence;
|
|
212
|
+
}
|
|
213
|
+
if(a.moment.stamp&&b.moment.stamp){
|
|
214
|
+
return String(a.moment.stamp)<String(b.moment.stamp)?-1:String(a.moment.stamp)>String(b.moment.stamp)?1:0;
|
|
215
|
+
}
|
|
216
|
+
if(a.moment.sequence!==null&&b.moment.sequence===null)return -1;
|
|
217
|
+
if(a.moment.sequence===null&&b.moment.sequence!==null)return 1;
|
|
218
|
+
return String(a.moment.id).localeCompare(String(b.moment.id));
|
|
152
219
|
});
|
|
153
220
|
|
|
154
221
|
const resolveModel=(owner,item)=>{
|
|
155
|
-
|
|
222
|
+
// Item hint is only for the owner/actor side. Callers that resolve a child
|
|
223
|
+
// must pass null as item so an actor tool-call model cannot pollute lookup.
|
|
224
|
+
const hinted=item?modelIdFromHint(modelHintFor(item)):null;
|
|
156
225
|
if(hinted)return hinted;
|
|
157
226
|
const list=modelEventsByAgent.get(owner)||[];
|
|
158
227
|
if(!list.length)return null;
|
|
159
|
-
const at=moment(item);
|
|
160
|
-
|
|
161
|
-
for(const event of list)
|
|
162
|
-
|
|
163
|
-
|
|
228
|
+
const at=moment(item||{});
|
|
229
|
+
const eligible=[];
|
|
230
|
+
for(const event of list){
|
|
231
|
+
if(!momentComparable(event.moment,at))continue;
|
|
232
|
+
if(momentLE(event.moment,at))eligible.push(event);
|
|
233
|
+
}
|
|
234
|
+
if(!eligible.length){
|
|
235
|
+
const unique=[...new Set(list.map(event=>event.modelId))];
|
|
236
|
+
return unique.length===1?unique[0]:null;
|
|
237
|
+
}
|
|
238
|
+
// Prefer the latest comparable event; if several tie at the same moment with
|
|
239
|
+
// conflicting models, abstain instead of taking array order.
|
|
240
|
+
let bestMoment=eligible[0].moment;
|
|
241
|
+
for(const event of eligible){
|
|
242
|
+
if(momentLE(bestMoment,event.moment)&&!momentEqual(bestMoment,event.moment))bestMoment=event.moment;
|
|
243
|
+
}
|
|
244
|
+
const tied=eligible.filter(event=>momentEqual(event.moment,bestMoment));
|
|
245
|
+
const unique=[...new Set(tied.map(event=>event.modelId))];
|
|
164
246
|
return unique.length===1?unique[0]:null;
|
|
165
247
|
};
|
|
166
248
|
|
|
@@ -180,18 +262,36 @@ EXECUTION_FLOW_SCRIPT = r"""
|
|
|
180
262
|
if(!kind)continue;
|
|
181
263
|
const sources=(incoming.get(node.id)||[]).filter(edge=>relation(edge)==='STARTED_AGENT_INTERACTION');
|
|
182
264
|
const targets=(outgoing.get(node.id)||[]).filter(edge=>relation(edge)==='TARGETED_BY_AGENT_INTERACTION');
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
if(owner)
|
|
265
|
+
const ownerCandidates=sources.map(edge=>agentForAnchor(edge.source));
|
|
266
|
+
const owner=uniqueOwner(ownerCandidates);
|
|
267
|
+
if(!owner)continue;
|
|
268
|
+
// Unknown endpoints keep the occurrence target-less rather than inventing uniqueness.
|
|
269
|
+
const peerIds=[];
|
|
270
|
+
let peersComplete=true;
|
|
271
|
+
for(const edge of targets){
|
|
272
|
+
const peer=agentForAnchor(edge.target);
|
|
273
|
+
if(!peer){peersComplete=false;continue}
|
|
274
|
+
peerIds.push(peer);
|
|
275
|
+
}
|
|
276
|
+
if(targets.length&&!peersComplete&&!peerIds.length)continue;
|
|
277
|
+
pushOccurrence(kind,owner,peerIds,node,[node.id],[...sources,...targets].map(edge=>edge.id).filter(Boolean));
|
|
186
278
|
}
|
|
187
279
|
|
|
188
280
|
for(const node of rawNodes){
|
|
189
281
|
if(node?.type!=='agent_message'||!node.id)continue;
|
|
190
282
|
const sent=(incoming.get(node.id)||[]).filter(edge=>relation(edge)==='SENT_AGENT_MESSAGE');
|
|
191
283
|
const delivered=(outgoing.get(node.id)||[]).filter(edge=>relation(edge)==='DELIVERED_AGENT_MESSAGE');
|
|
192
|
-
const owner=sent.map(edge=>agentForAnchor(edge.source))
|
|
193
|
-
|
|
194
|
-
|
|
284
|
+
const owner=uniqueOwner(sent.map(edge=>agentForAnchor(edge.source)));
|
|
285
|
+
if(!owner)continue;
|
|
286
|
+
const peerIds=[];
|
|
287
|
+
let peersComplete=true;
|
|
288
|
+
for(const edge of delivered){
|
|
289
|
+
const peer=agentForAnchor(edge.target);
|
|
290
|
+
if(!peer){peersComplete=false;continue}
|
|
291
|
+
peerIds.push(peer);
|
|
292
|
+
}
|
|
293
|
+
if(delivered.length&&!peersComplete&&!peerIds.length)continue;
|
|
294
|
+
pushOccurrence('send_input',owner,peerIds,node,[node.id],[...sent,...delivered].map(edge=>edge.id).filter(Boolean));
|
|
195
295
|
}
|
|
196
296
|
|
|
197
297
|
for(const edge of rawEdges){
|
|
@@ -293,13 +393,70 @@ EXECUTION_FLOW_SCRIPT = r"""
|
|
|
293
393
|
for(const modelId of new Set(list.map(item=>item.modelId)))ensureContext(owner,modelId);
|
|
294
394
|
}
|
|
295
395
|
|
|
296
|
-
const consumedRawNodes=new Set(),consumedRawEdges=new Set(),
|
|
396
|
+
const consumedRawNodes=new Set(),consumedRawEdges=new Set(),consumedCallIds=new Set();
|
|
397
|
+
const contentRefFromNode=(contentNode,edge,direction)=>{
|
|
398
|
+
const attrs=attrsOf(contentNode);
|
|
399
|
+
const viewer=attrs.viewer_content&&typeof attrs.viewer_content==='object'?attrs.viewer_content:{};
|
|
400
|
+
return{
|
|
401
|
+
content_node_id:contentNode.id,
|
|
402
|
+
raw_node_id:contentNode.id,
|
|
403
|
+
raw_edge_id:edge?.id||null,
|
|
404
|
+
relation:edge?.relation||null,
|
|
405
|
+
direction:direction||null,
|
|
406
|
+
sha256:viewer.sha256||attrs.sha256||attrs.content_sha256||null,
|
|
407
|
+
path:viewer.safe_relative_path||attrs.path||attrs.content_path||null,
|
|
408
|
+
size_bytes:viewer.size_bytes??attrs.size_bytes??attrs.content_size_bytes??null,
|
|
409
|
+
media_type:viewer.media_type||attrs.media_type||attrs.content_media_type||null,
|
|
410
|
+
content_kind:viewer.content_kind||attrs.content_kind||contentNode.name||null,
|
|
411
|
+
};
|
|
412
|
+
};
|
|
297
413
|
for(const group of groups.values()){
|
|
298
414
|
const context=group.modelId?ensureContext(group.owner,group.modelId):null;
|
|
299
415
|
const source=context||group.owner;
|
|
300
416
|
const baseRank=agentRank.get(group.owner)||0;
|
|
301
417
|
const actionRank=baseRank+(context?2:1);
|
|
302
418
|
const id=`viewer:orchestration:${encodeURIComponent(group.owner)}:${encodeURIComponent(group.modelId||'unresolved')}:${group.kind}`;
|
|
419
|
+
const contentRefs=[];
|
|
420
|
+
const toolCallOccurrences=[];
|
|
421
|
+
for(const rawId of group.rawNodeIds){
|
|
422
|
+
const rawNode=rawById.get(rawId);
|
|
423
|
+
if(!rawNode)continue;
|
|
424
|
+
if(String(rawNode.type||'')==='observed_content'){
|
|
425
|
+
contentRefs.push(contentRefFromNode(rawNode,null,'node'));
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
for(const edge of [...(outgoing.get(rawId)||[]),...(incoming.get(rawId)||[])]){
|
|
429
|
+
const peerId=edge.source===rawId?edge.target:edge.source;
|
|
430
|
+
const peer=rawById.get(peerId);
|
|
431
|
+
if(peer?.type!=='observed_content')continue;
|
|
432
|
+
const direction=edge.source===rawId?'outgoing':'incoming';
|
|
433
|
+
const ref=contentRefFromNode(peer,edge,direction);
|
|
434
|
+
if(!contentRefs.some(item=>item.content_node_id===ref.content_node_id&&item.raw_edge_id===ref.raw_edge_id)){
|
|
435
|
+
contentRefs.push(ref);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if(['tool_call','tool_call_observation'].includes(String(rawNode?.type||''))){
|
|
439
|
+
consumedCallIds.add(rawId);
|
|
440
|
+
const attrs=attrsOf(rawNode);
|
|
441
|
+
let toolId=null;
|
|
442
|
+
for(const edge of outgoing.get(rawId)||[]){
|
|
443
|
+
if(relation(edge)==='USES_TOOL'||relation(edge)==='RESOLVED_TOOL'){toolId=edge.target;break}
|
|
444
|
+
}
|
|
445
|
+
toolCallOccurrences.push({
|
|
446
|
+
invocation_id:rawId,
|
|
447
|
+
owner_id:group.owner,
|
|
448
|
+
tool_id:toolId,
|
|
449
|
+
call_ids:[rawId],
|
|
450
|
+
first_sequence:Number.isInteger(rawNode.first_sequence)?rawNode.first_sequence:null,
|
|
451
|
+
last_sequence:Number.isInteger(rawNode.last_sequence)?rawNode.last_sequence:null,
|
|
452
|
+
first_seen:rawNode.first_seen||null,
|
|
453
|
+
last_seen:rawNode.last_seen||null,
|
|
454
|
+
input:attrs.arguments??attrs.args??attrs.tool_input??attrs.input??attrs.parameters??attrs.request??null,
|
|
455
|
+
output:attrs.output??attrs.result??attrs.tool_output??attrs.response??null,
|
|
456
|
+
content_references:contentRefs.filter(ref=>ref.raw_edge_id&&[...(outgoing.get(rawId)||[]),...(incoming.get(rawId)||[])].some(edge=>edge.id===ref.raw_edge_id)),
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
303
460
|
addedNodes.push({
|
|
304
461
|
id,type:'tool',name:group.kind,
|
|
305
462
|
attributes:{
|
|
@@ -309,6 +466,8 @@ EXECUTION_FLOW_SCRIPT = r"""
|
|
|
309
466
|
evidence_node_ids:[...group.rawNodeIds].sort(),evidence_edge_ids:[...group.rawEdgeIds].sort(),
|
|
310
467
|
viewer_occurrence_count:group.occurrences.length,
|
|
311
468
|
viewer_model_context_unresolved:!context||undefined,
|
|
469
|
+
viewer_content_references:contentRefs,
|
|
470
|
+
viewer_tool_call_occurrences:toolCallOccurrences,
|
|
312
471
|
}
|
|
313
472
|
});
|
|
314
473
|
addedEdges.push({
|
|
@@ -326,13 +485,23 @@ EXECUTION_FLOW_SCRIPT = r"""
|
|
|
326
485
|
}
|
|
327
486
|
for(const rawId of group.rawNodeIds)consumedRawNodes.add(rawId);
|
|
328
487
|
for(const rawId of group.rawEdgeIds)consumedRawEdges.add(rawId);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Shared tool definitions / model resources stay unless every remaining USES_TOOL
|
|
491
|
+
// into them came from consumed calls. Consuming one owner's call must not wipe the
|
|
492
|
+
// shared definition for other owners or unresolved calls.
|
|
493
|
+
const hideToolDefs=new Set();
|
|
494
|
+
for(const [toolId,node] of [...rawById].filter(([,n])=>n?.type==='tool')){
|
|
495
|
+
const uses=(incoming.get(toolId)||[]).filter(edge=>['USES_TOOL','RESOLVED_TOOL'].includes(relation(edge)));
|
|
496
|
+
if(!uses.length)continue;
|
|
497
|
+
// Keep shared tool definitions when any live use remains: another owner's
|
|
498
|
+
// unconsumed call, an agent-direct USES_TOOL, or unresolved evidence.
|
|
499
|
+
const allConsumedCalls=uses.every(edge=>{
|
|
500
|
+
const src=rawById.get(edge.source);
|
|
501
|
+
const isCall=['tool_call','tool_call_observation'].includes(String(src?.type||''));
|
|
502
|
+
return isCall&&consumedCallIds.has(edge.source);
|
|
503
|
+
});
|
|
504
|
+
if(allConsumedCalls)hideToolDefs.add(toolId);
|
|
336
505
|
}
|
|
337
506
|
|
|
338
507
|
const flowNodes=display.nodes.map(node=>{
|
|
@@ -342,17 +511,25 @@ EXECUTION_FLOW_SCRIPT = r"""
|
|
|
342
511
|
|
|
343
512
|
const modelRelation=edge=>MODEL_RELATIONS.has(relation(edge))||relation(edge)==='MODEL_CONTEXT';
|
|
344
513
|
const orchestrationRelation=edge=>DIRECT_ACTION_RELATIONS.has(relation(edge));
|
|
345
|
-
let nodes=flowNodes.filter(node=>!consumedRawNodes.has(node.id)&&!
|
|
514
|
+
let nodes=flowNodes.filter(node=>!consumedRawNodes.has(node.id)&&!hideToolDefs.has(node.id));
|
|
346
515
|
let edges=display.edges.filter(edge=>{
|
|
347
516
|
if(consumedRawEdges.has(edge.id))return false;
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
if(
|
|
517
|
+
// Keep unconsumed inferred / provider orchestration evidence. Only hide edges
|
|
518
|
+
// that were actually folded into an occurrence (tracked via consumedRawEdges).
|
|
519
|
+
if(orchestrationRelation(edge)&&agents.has(edge.source)&&agents.has(edge.target)){
|
|
520
|
+
return !consumedRawEdges.has(edge.id);
|
|
521
|
+
}
|
|
522
|
+
if(hideToolDefs.has(edge.source)||hideToolDefs.has(edge.target))return false;
|
|
523
|
+
// Only supersede an owner→model edge when THAT owner already has a viewer
|
|
524
|
+
// model-context for the same model. Other owners' shared-model usage stays.
|
|
525
|
+
if(modelRelation(edge)&&agents.has(edge.source)&&contextIds.has(contextId(edge.source,edge.target)))return false;
|
|
351
526
|
return true;
|
|
352
527
|
});
|
|
353
528
|
|
|
354
529
|
for(const modelId of contextifiedModels){
|
|
355
530
|
const remaining=edges.filter(edge=>edge.source===modelId||edge.target===modelId);
|
|
531
|
+
// Keep the raw model resource whenever any non-superseded usage remains
|
|
532
|
+
// (another owner, unresolved call, or non-agent attachment).
|
|
356
533
|
if(!remaining.length)nodes=nodes.filter(node=>node.id!==modelId);
|
|
357
534
|
}
|
|
358
535
|
|