execweave 0.8.28__tar.gz → 0.8.31__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.28 → execweave-0.8.31}/PKG-INFO +10 -2
- {execweave-0.8.28 → execweave-0.8.31}/README.md +9 -1
- {execweave-0.8.28 → execweave-0.8.31}/pyproject.toml +2 -2
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/__init__.py +1 -1
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/framework_adapters/autogen.py +48 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/framework_adapters/base.py +23 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/framework_adapters/camel.py +4 -1
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/framework_adapters/metagpt.py +20 -2
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live_core.py +16 -6
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/provider_record.py +1 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_agent_panel.py +26 -3
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_semantic_projection.py +150 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/workflow.py +60 -3
- {execweave-0.8.28 → execweave-0.8.31}/.gitignore +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/LICENSE +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/__main__.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/_conversation_records_core.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/_dashboard_shell_base.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/_http_proxy_base.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/_http_proxy_bounded.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/_http_proxy_stage.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/agent_bootstrap.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/agent_topology.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/agent_trace.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/agy_preview_sanitize.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/analysis.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/anthropic.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/anthropic_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/anthropic_full_fidelity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/antigravity_adapter.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/antigravity_adapter_base.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/antigravity_full_fidelity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/antigravity_full_fidelity_base.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/antigravity_full_fidelity_collaboration_base.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/antigravity_hook_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/antigravity_record.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/antigravity_subagent_linkage.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/antigravity_trace_capability.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/auto_specialized.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/backends.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/benchmark.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/claude_adapter.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/claude_child_transcript.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/claude_delegation.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/claude_full_fidelity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/claude_hook_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/claude_hook_contract.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/claude_model_observer.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/claude_record.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_adapter.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_conversation.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_full_fidelity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_hook_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_hook_entry.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_hook_lifecycle.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_message_diagnostics.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_message_transport_diagnostics.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_record.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_rollout_structures.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_rollout_trace.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/codex_rollout_trace_base.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/collector.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/command.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/content_evidence.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/content_store.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_archive.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_message_identity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_preview.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_preview_antigravity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_preview_claude.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_preview_codex.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_preview_common.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_preview_generic.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_preview_lines.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_preview_transcript.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_records.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_records_antigravity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_records_codex.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_records_common.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/conversation_records_ollama.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/correlation.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/cursor_adapter.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/cursor_delegation.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/cursor_delegation_base.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/cursor_full_fidelity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/cursor_hook_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/cursor_hook_contract.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/cursor_lifecycle.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/cursor_record.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/dashboard_shell.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/entry.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/evidence_availability.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/evidence_grade.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/fidelity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/filesystem.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/focus.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/framework_adapters/__init__.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/framework_adapters/registry.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/graph.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/graph_ops.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/http_proxy.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/http_proxy_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/inference_gateway.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/inference_gateway_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/inference_gateway_full_fidelity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/inference_identity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/inference_identity_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/integrity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/integrity_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/litellm_callback.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/litellm_callback_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live_view.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live_view_extra_style.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live_view_markup.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live_view_process_layout.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live_view_readability.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live_view_script_a.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live_view_script_b.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live_view_script_c.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live_view_script_d.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/live_view_style.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/model_runtime.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/model_runtime_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/model_runtime_full_fidelity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/openai_compatible.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/openai_compatible_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/openai_compatible_full_fidelity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/opencode_adapter.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/opencode_event_contract.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/opencode_full_fidelity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/opencode_hook_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/opencode_plugin_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/opencode_record.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/opencode_task_linkage.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/overhead_benchmark.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/provider_capability.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/provider_lifecycle.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/rule_pack.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/rule_pack_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/runtime_endpoint_availability.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/scalability_benchmark.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/schema.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/scope.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/semantic.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/sink.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/strace_backend.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/stream_assembly.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/theme.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/top.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/top_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/validate.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/vendor/dagre.min.js +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/view_cli.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_agent_panel_antigravity.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_agent_panel_claude.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_agent_panel_codex.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_agent_panel_cursor.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_agent_panel_default.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_agent_panel_ollama.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_agent_panel_opencode.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_agent_panel_registry.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_content_inspector.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_dashboard_clean.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_dashboard_focus.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_dashboard_hardening.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_dashboard_hardening_v2.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_dashboard_pr70.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_edge_decross.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_execution_flow.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_execution_flow_provider_normalization.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_execution_flow_safety.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_external_endpoints.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_flow_canvas.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_flow_layout.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_gif.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_layout_geometry.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_layout_v2.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_layout_v2_detached.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_limits.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_live_layout.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_projection.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_projection_base.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_projection_bridges.py +0 -0
- {execweave-0.8.28 → execweave-0.8.31}/src/execweave/viewer_tool_mirror_identity.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.31
|
|
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
|
|
@@ -388,5 +388,13 @@ Paper context: Li et al., *CAMEL: Communicative Agents for "Mind" Exploration of
|
|
|
388
388
|
|
|
389
389
|
```python
|
|
390
390
|
from execweave.framework_adapters import AdapterContext, CAMELAdapter
|
|
391
|
-
|
|
391
|
+
context = AdapterContext.from_environment("camel", capture_mode="prompt_and_response")
|
|
392
|
+
adapter = CAMELAdapter(context)
|
|
393
|
+
task = adapter.task_created("task-1", content="Inspect the endpoint and report completion.")
|
|
392
394
|
```
|
|
395
|
+
|
|
396
|
+
When the application is launched by `execweave record` or `execweave live`,
|
|
397
|
+
the adapter automatically joins that run and the finished Dashboard is built
|
|
398
|
+
from the merged runtime plus framework evidence. Task prompts and routed
|
|
399
|
+
messages remain local content references and are visible from their task and
|
|
400
|
+
agent nodes.
|
|
@@ -328,5 +328,13 @@ Paper context: Li et al., *CAMEL: Communicative Agents for "Mind" Exploration of
|
|
|
328
328
|
|
|
329
329
|
```python
|
|
330
330
|
from execweave.framework_adapters import AdapterContext, CAMELAdapter
|
|
331
|
-
|
|
331
|
+
context = AdapterContext.from_environment("camel", capture_mode="prompt_and_response")
|
|
332
|
+
adapter = CAMELAdapter(context)
|
|
333
|
+
task = adapter.task_created("task-1", content="Inspect the endpoint and report completion.")
|
|
332
334
|
```
|
|
335
|
+
|
|
336
|
+
When the application is launched by `execweave record` or `execweave live`,
|
|
337
|
+
the adapter automatically joins that run and the finished Dashboard is built
|
|
338
|
+
from the merged runtime plus framework evidence. Task prompts and routed
|
|
339
|
+
messages remain local content references and are visible from their task and
|
|
340
|
+
agent nodes.
|
|
@@ -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.31 release metadata
|
|
8
|
+
version = "0.8.31"
|
|
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"
|
|
@@ -15,6 +15,7 @@ from .base import (
|
|
|
15
15
|
FrameworkCompatibility,
|
|
16
16
|
MessageRecord,
|
|
17
17
|
ModelCallRecord,
|
|
18
|
+
TaskRecord,
|
|
18
19
|
ToolCallRecord,
|
|
19
20
|
)
|
|
20
21
|
|
|
@@ -26,6 +27,13 @@ class AutoGenAdapter(FrameworkAdapter):
|
|
|
26
27
|
"AGENT_CREATED",
|
|
27
28
|
"AGENT_STARTED",
|
|
28
29
|
"AGENT_STOPPED",
|
|
30
|
+
"TASK_CREATED",
|
|
31
|
+
"TASK_ASSIGNED",
|
|
32
|
+
"TASK_STARTED",
|
|
33
|
+
"TASK_UPDATED",
|
|
34
|
+
"TASK_COMPLETED",
|
|
35
|
+
"TASK_FAILED",
|
|
36
|
+
"TASK_CONTENT_RECORDED",
|
|
29
37
|
"MESSAGE_SENT",
|
|
30
38
|
"MESSAGE_RECEIVED",
|
|
31
39
|
"MODEL_REQUEST",
|
|
@@ -86,6 +94,46 @@ class AutoGenAdapter(FrameworkAdapter):
|
|
|
86
94
|
self.context.record_agent(AgentRecord(agent, role=role, process=process, attributes=attributes))
|
|
87
95
|
return agent
|
|
88
96
|
|
|
97
|
+
def observe_task(
|
|
98
|
+
self,
|
|
99
|
+
task_id: str | int,
|
|
100
|
+
*,
|
|
101
|
+
owner: EntityRef | None = None,
|
|
102
|
+
name: str | None = None,
|
|
103
|
+
content: str | None = None,
|
|
104
|
+
content_kind: str = "task_prompt",
|
|
105
|
+
status: str = "created",
|
|
106
|
+
**attributes: Any,
|
|
107
|
+
) -> EntityRef:
|
|
108
|
+
"""Record an authoritative AutoGen task and its visible task prompt."""
|
|
109
|
+
entity_attributes = dict(attributes)
|
|
110
|
+
if content is not None and self.context.capture_policy.mode in {
|
|
111
|
+
"prompt_only",
|
|
112
|
+
"prompt_and_response",
|
|
113
|
+
}:
|
|
114
|
+
entity_attributes.setdefault("task_prompt", content)
|
|
115
|
+
task = self.task(task_id, name=name, provider="autogen", **entity_attributes)
|
|
116
|
+
event = {
|
|
117
|
+
"created": "TASK_CREATED",
|
|
118
|
+
"started": "TASK_STARTED",
|
|
119
|
+
"updated": "TASK_UPDATED",
|
|
120
|
+
"completed": "TASK_COMPLETED",
|
|
121
|
+
"failed": "TASK_FAILED",
|
|
122
|
+
}.get(status)
|
|
123
|
+
if event is None:
|
|
124
|
+
raise ValueError(f"unsupported AutoGen task status: {status}")
|
|
125
|
+
self.context.record_task(
|
|
126
|
+
TaskRecord(
|
|
127
|
+
task,
|
|
128
|
+
owner=owner,
|
|
129
|
+
content=content,
|
|
130
|
+
content_kind=content_kind,
|
|
131
|
+
attributes=attributes,
|
|
132
|
+
),
|
|
133
|
+
event_type=event,
|
|
134
|
+
)
|
|
135
|
+
return task
|
|
136
|
+
|
|
89
137
|
def observe_message(
|
|
90
138
|
self,
|
|
91
139
|
message_id: str | int,
|
|
@@ -11,6 +11,7 @@ import hashlib
|
|
|
11
11
|
import json
|
|
12
12
|
import os
|
|
13
13
|
import re
|
|
14
|
+
import sys
|
|
14
15
|
import threading
|
|
15
16
|
import time
|
|
16
17
|
from dataclasses import asdict, dataclass, field
|
|
@@ -19,6 +20,8 @@ from pathlib import Path
|
|
|
19
20
|
from typing import Any, Literal, Mapping
|
|
20
21
|
from uuid import uuid4
|
|
21
22
|
|
|
23
|
+
import psutil
|
|
24
|
+
|
|
22
25
|
from ..content_store import ContentReference, FullFidelityContentStore
|
|
23
26
|
from ..schema import SCHEMA_VERSION
|
|
24
27
|
|
|
@@ -137,6 +140,20 @@ class ProcessRef:
|
|
|
137
140
|
return EntityRef("process_reference", f"process-reference:{identity}", self.executable or f"pid {self.pid}", attributes)
|
|
138
141
|
|
|
139
142
|
|
|
143
|
+
def _current_process_ref() -> ProcessRef:
|
|
144
|
+
"""Return the authoritative identity of the adapter's current process."""
|
|
145
|
+
process = psutil.Process(os.getpid())
|
|
146
|
+
try:
|
|
147
|
+
create_time = process.create_time()
|
|
148
|
+
except (psutil.AccessDenied, psutil.NoSuchProcess, psutil.ZombieProcess):
|
|
149
|
+
create_time = None
|
|
150
|
+
try:
|
|
151
|
+
executable = process.exe()
|
|
152
|
+
except (psutil.AccessDenied, psutil.NoSuchProcess, psutil.ZombieProcess):
|
|
153
|
+
executable = sys.executable
|
|
154
|
+
return ProcessRef(os.getpid(), create_time, executable or sys.executable)
|
|
155
|
+
|
|
156
|
+
|
|
140
157
|
@dataclass(frozen=True)
|
|
141
158
|
class AdapterCapabilities:
|
|
142
159
|
events: tuple[str, ...] = ()
|
|
@@ -362,6 +379,12 @@ class AdapterContext:
|
|
|
362
379
|
|
|
363
380
|
@classmethod
|
|
364
381
|
def from_environment(cls, framework: str, *, capture_mode: CaptureMode = "metadata_only", content_root: str | Path | None = None, run_id: str | None = None, session_id: str | None = None, conversation_id: str | None = None, process: ProcessRef | None = None) -> "AdapterContext":
|
|
382
|
+
if (
|
|
383
|
+
process is None
|
|
384
|
+
and os.environ.get("EXECWEAVE_SEMANTIC_SIDECAR")
|
|
385
|
+
and os.environ.get("EXECWEAVE_SESSION_ID")
|
|
386
|
+
):
|
|
387
|
+
process = _current_process_ref()
|
|
365
388
|
return cls(framework=framework, run_id=run_id, session_id=session_id, conversation_id=conversation_id, content_root=content_root, capture_policy=ContentCapturePolicy(capture_mode), process=process)
|
|
366
389
|
|
|
367
390
|
def entity(self, entity_type: str, native_id: str | int | None, *, name: str | None = None, attributes: Mapping[str, Any] | None = None) -> EntityRef:
|
|
@@ -51,7 +51,10 @@ class CAMELAdapter(FrameworkAdapter):
|
|
|
51
51
|
task = existing
|
|
52
52
|
else:
|
|
53
53
|
entity_attributes = dict(attributes)
|
|
54
|
-
if content is not None
|
|
54
|
+
if content is not None and self.context.capture_policy.mode in {
|
|
55
|
+
"prompt_only",
|
|
56
|
+
"prompt_and_response",
|
|
57
|
+
}:
|
|
55
58
|
entity_attributes.setdefault("task_prompt", content)
|
|
56
59
|
task = self.task(native_id, name=name, provider="camel", **entity_attributes)
|
|
57
60
|
self._tasks[str(native_id)] = task
|
|
@@ -30,6 +30,7 @@ class MetaGPTAdapter(FrameworkAdapter):
|
|
|
30
30
|
"TASK_COMPLETED",
|
|
31
31
|
"TASK_FAILED",
|
|
32
32
|
"TASK_UPDATED",
|
|
33
|
+
"TASK_CONTENT_RECORDED",
|
|
33
34
|
"MESSAGE_SENT",
|
|
34
35
|
"MESSAGE_RECEIVED",
|
|
35
36
|
"MODEL_REQUEST",
|
|
@@ -96,10 +97,18 @@ class MetaGPTAdapter(FrameworkAdapter):
|
|
|
96
97
|
*,
|
|
97
98
|
owner: EntityRef | None = None,
|
|
98
99
|
name: str | None = None,
|
|
100
|
+
content: str | None = None,
|
|
101
|
+
content_kind: str = "task_prompt",
|
|
99
102
|
status: str = "created",
|
|
100
103
|
**attributes: Any,
|
|
101
104
|
) -> EntityRef:
|
|
102
|
-
|
|
105
|
+
entity_attributes = dict(attributes)
|
|
106
|
+
if content is not None and self.context.capture_policy.mode in {
|
|
107
|
+
"prompt_only",
|
|
108
|
+
"prompt_and_response",
|
|
109
|
+
}:
|
|
110
|
+
entity_attributes.setdefault("task_prompt", content)
|
|
111
|
+
task = self.task(task_id, name=name, provider="metagpt", **entity_attributes)
|
|
103
112
|
event = {
|
|
104
113
|
"created": "TASK_CREATED",
|
|
105
114
|
"started": "TASK_STARTED",
|
|
@@ -108,7 +117,16 @@ class MetaGPTAdapter(FrameworkAdapter):
|
|
|
108
117
|
}.get(status)
|
|
109
118
|
if event is None:
|
|
110
119
|
raise ValueError(f"unsupported MetaGPT task status: {status}")
|
|
111
|
-
self.context.record_task(
|
|
120
|
+
self.context.record_task(
|
|
121
|
+
TaskRecord(
|
|
122
|
+
task,
|
|
123
|
+
owner=owner,
|
|
124
|
+
content=content,
|
|
125
|
+
content_kind=content_kind,
|
|
126
|
+
attributes=attributes,
|
|
127
|
+
),
|
|
128
|
+
event_type=event,
|
|
129
|
+
)
|
|
112
130
|
return task
|
|
113
131
|
|
|
114
132
|
def observe_message(
|
|
@@ -37,6 +37,8 @@ from .viewer import (
|
|
|
37
37
|
LIVE_DELTA_HISTORY = 256
|
|
38
38
|
LIVE_DELTA_HISTORY_BYTES = 8 * 1024 * 1024
|
|
39
39
|
_SEMANTIC_ENV = "EXECWEAVE_SEMANTIC_SIDECAR"
|
|
40
|
+
_RUN_ID_ENV = "EXECWEAVE_RUN_ID"
|
|
41
|
+
_SESSION_ID_ENV = "EXECWEAVE_SESSION_ID"
|
|
40
42
|
_LIVE_TOKEN_HEADER = "X-ExecWeave-Token"
|
|
41
43
|
|
|
42
44
|
_FINAL_THEME_CSS = """
|
|
@@ -737,16 +739,24 @@ def run_live(
|
|
|
737
739
|
webbrowser.open(authenticated_live_url)
|
|
738
740
|
|
|
739
741
|
return_code = 1
|
|
740
|
-
|
|
741
|
-
|
|
742
|
+
environment_updates = {
|
|
743
|
+
_SEMANTIC_ENV: str(semantic_path),
|
|
744
|
+
_RUN_ID_ENV: session_id,
|
|
745
|
+
_SESSION_ID_ENV: session_id,
|
|
746
|
+
}
|
|
747
|
+
previous_environment = {
|
|
748
|
+
key: os.environ.get(key) for key in environment_updates
|
|
749
|
+
}
|
|
750
|
+
os.environ.update(environment_updates)
|
|
742
751
|
try:
|
|
743
752
|
try:
|
|
744
753
|
return_code = collector.run(command)
|
|
745
754
|
finally:
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
755
|
+
for key, value in previous_environment.items():
|
|
756
|
+
if value is None:
|
|
757
|
+
os.environ.pop(key, None)
|
|
758
|
+
else:
|
|
759
|
+
os.environ[key] = value
|
|
750
760
|
|
|
751
761
|
validation = validate_event_stream(event_path)
|
|
752
762
|
if not validation.valid:
|
|
@@ -418,6 +418,28 @@ function toolOccurrenceSection(node){
|
|
|
418
418
|
});
|
|
419
419
|
return section;
|
|
420
420
|
}
|
|
421
|
+
function execweaveAssignedTaskText(agent){
|
|
422
|
+
if(!agent||String(agent.type||'')!=='agent')return '';
|
|
423
|
+
const projected=attrs(agent),attached=projected.viewer_assigned_task_prompt||projected.viewer_assigned_task_name;
|
|
424
|
+
if(attached)return commandText(attached);
|
|
425
|
+
const graph=rawGraph(),nodes=Array.isArray(graph.nodes)?graph.nodes:[],edges=Array.isArray(graph.edges)?graph.edges:[];
|
|
426
|
+
const nodeMap=new Map(nodes.filter(node=>node&&node.id).map(node=>[String(node.id),node]));
|
|
427
|
+
const exactAssignment=['ASSIGNED','AGENT','TASK'].join('_');
|
|
428
|
+
const direct=edges.filter(edge=>edge&&String(edge.target||'')===String(agent.id||'')&&['ASSIGNED_TO',exactAssignment].includes(String(edge.relation||''))).sort((a,b)=>(Number(a.first_sequence)||0)-(Number(b.first_sequence)||0));
|
|
429
|
+
const owned=edges.filter(edge=>edge&&String(edge.source||'')===String(agent.id||'')&&String(edge.relation||'')==='TASK_CREATED').sort((a,b)=>(Number(a.first_sequence)||0)-(Number(b.first_sequence)||0));
|
|
430
|
+
const started=edges.filter(edge=>edge&&String(edge.source||'')===String(agent.id||'')&&String(edge.relation||'')==='TASK_STARTED').sort((a,b)=>(Number(a.first_sequence)||0)-(Number(b.first_sequence)||0));
|
|
431
|
+
const edge=direct.at(-1)||owned.at(-1)||started.at(-1);if(!edge)return '';
|
|
432
|
+
const taskId=direct.length?edge.source:edge.target,task=nodeMap.get(String(taskId||''));
|
|
433
|
+
if(!task||String(task.type||'')!=='task')return '';
|
|
434
|
+
const a=attrs(task);return commandText(a.task_prompt||a.prompt||a.description||task.name);
|
|
435
|
+
}
|
|
436
|
+
function execweaveFillAssignedTask(round,node,isRoot){
|
|
437
|
+
const task=execweaveAssignedTaskText(node);if(!task)return round;
|
|
438
|
+
const value=round||{cards:[]},cards=Array.isArray(value.cards)?value.cards.map(card=>[card[0],card[1]]):[];
|
|
439
|
+
const label=isRoot?'Prompt':'Task',index=cards.findIndex(card=>String(card[0]||'')===label);
|
|
440
|
+
if(index<0)cards.unshift([label,task]);else if(!commandText(cards[index][1]))cards[index][1]=task;
|
|
441
|
+
return{...value,cards};
|
|
442
|
+
}
|
|
421
443
|
function renderNode(node){
|
|
422
444
|
const rows=nodeCards(node);
|
|
423
445
|
if(!rows.length)return false;
|
|
@@ -436,10 +458,11 @@ function render(node){
|
|
|
436
458
|
selectedNode=node;selectedConversationSignature=conversationSignature(node);detailsEmpty.hidden=true;details.replaceChildren();
|
|
437
459
|
const record=recordFor(node),preview=record?.conversation_preview||{},path=String(preview.agent_path||nodePath(node)||'').trim(),messages=Array.isArray(preview.messages)?preview.messages:[];
|
|
438
460
|
const isRoot=nodeHasRootAuthority(node)||previewUsesRootRenderer(preview);
|
|
439
|
-
const rounds=isRoot?rootRounds(messages,path||'/root'):childRounds(messages,path);
|
|
461
|
+
const rounds=(isRoot?rootRounds(messages,path||'/root'):childRounds(messages,path)).map(round=>execweaveFillAssignedTask(round,node,isRoot));
|
|
440
462
|
const tools=toolCallsFor(String(node.id||''));
|
|
441
|
-
const
|
|
442
|
-
if(
|
|
463
|
+
const frameworkCommunication=attrs(node).conversation_scope==='framework_agent'?uniqueTexts(messages.filter(message=>isObserved(message)&&!isInjected(message)&&String(message?.kind||'')==='agent_message')).join('\n\n'):'';
|
|
464
|
+
const appendTools=()=>{if(frameworkCommunication)details.appendChild(card('Agent communication',frameworkCommunication));if(tools)details.appendChild(card('Tools',tools))};
|
|
465
|
+
if(rounds.length<2){const fallback={cards:isRoot?[['Prompt',''],['Final response','']]:[['Task',''],['Thinking',''],['Response','']]};details.appendChild(roundView(rounds[0]||execweaveFillAssignedTask(fallback,node,isRoot)));appendTools();return true}
|
|
443
466
|
// A subagent borrows the moment and the wording of the unique canonical root round
|
|
444
467
|
// it belongs to. If root identity is ambiguous, the child keeps its own timestamp.
|
|
445
468
|
const runs=isRoot?rounds:runRounds();
|
|
@@ -258,6 +258,151 @@ def orphan_audit(nodes: list[dict[str, Any]], edges: list[dict[str, Any]]) -> di
|
|
|
258
258
|
return {"count": sum(item["count"] for item in by_type), "by_type": by_type}
|
|
259
259
|
|
|
260
260
|
|
|
261
|
+
_FRAMEWORK_TASK_RELATIONS = frozenset(
|
|
262
|
+
{"ASSIGNED_TO", "TASK_CREATED", "TASK_STARTED", "TASK_UPDATED", "TASK_COMPLETED", "TASK_FAILED"}
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _framework_task_edge_score(edge: dict[str, Any]) -> tuple[int, str, str]:
|
|
267
|
+
sequence = edge.get("last_sequence")
|
|
268
|
+
if not isinstance(sequence, int) or isinstance(sequence, bool):
|
|
269
|
+
sequence = edge.get("first_sequence")
|
|
270
|
+
if not isinstance(sequence, int) or isinstance(sequence, bool):
|
|
271
|
+
sequence = -1
|
|
272
|
+
return (sequence, str(edge.get("last_seen") or edge.get("first_seen") or ""), str(edge.get("id") or ""))
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _framework_task_text(task: dict[str, Any]) -> str:
|
|
276
|
+
attributes = _attrs(task)
|
|
277
|
+
for value in (
|
|
278
|
+
attributes.get("task_prompt"),
|
|
279
|
+
attributes.get("prompt"),
|
|
280
|
+
attributes.get("description"),
|
|
281
|
+
task.get("name"),
|
|
282
|
+
):
|
|
283
|
+
if isinstance(value, str) and value.strip():
|
|
284
|
+
return value.strip()
|
|
285
|
+
return ""
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def collapse_framework_tasks(
|
|
289
|
+
nodes: list[dict[str, Any]],
|
|
290
|
+
edges: list[dict[str, Any]],
|
|
291
|
+
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], dict[str, int]]:
|
|
292
|
+
"""Fold framework task presentation into the owning agent, like provider views.
|
|
293
|
+
|
|
294
|
+
Framework adapters retain first-class task entities in the raw graph because task
|
|
295
|
+
lifecycle and prompt evidence must remain auditable. Provider dashboards expose
|
|
296
|
+
that same information on the agent card, however, so the viewer projection hides
|
|
297
|
+
only framework task nodes and copies the latest authoritative task text onto each
|
|
298
|
+
assigned framework agent. Provider graphs are untouched because they do not use
|
|
299
|
+
the framework-agent conversation scope.
|
|
300
|
+
"""
|
|
301
|
+
node_by_id = {
|
|
302
|
+
str(node["id"]): node
|
|
303
|
+
for node in nodes
|
|
304
|
+
if isinstance(node, dict) and isinstance(node.get("id"), str)
|
|
305
|
+
}
|
|
306
|
+
framework_agents = {
|
|
307
|
+
node_id
|
|
308
|
+
for node_id, node in node_by_id.items()
|
|
309
|
+
if node.get("type") == "agent" and _attrs(node).get("conversation_scope") == "framework_agent"
|
|
310
|
+
}
|
|
311
|
+
if not framework_agents:
|
|
312
|
+
return nodes, edges, {
|
|
313
|
+
"framework_task_node_count": 0,
|
|
314
|
+
"framework_task_content_node_count": 0,
|
|
315
|
+
"framework_task_agent_count": 0,
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
task_ids: set[str] = set()
|
|
319
|
+
tasks_by_agent: dict[str, list[tuple[tuple[int, str, str], str]]] = defaultdict(list)
|
|
320
|
+
for edge in edges:
|
|
321
|
+
if not isinstance(edge, dict) or edge.get("relation") not in _FRAMEWORK_TASK_RELATIONS:
|
|
322
|
+
continue
|
|
323
|
+
source = str(edge.get("source") or "")
|
|
324
|
+
target = str(edge.get("target") or "")
|
|
325
|
+
source_node = node_by_id.get(source)
|
|
326
|
+
target_node = node_by_id.get(target)
|
|
327
|
+
task_id = source if source_node and source_node.get("type") == "task" else target if target_node and target_node.get("type") == "task" else None
|
|
328
|
+
if task_id is None:
|
|
329
|
+
continue
|
|
330
|
+
agent_id = source if source in framework_agents else target if target in framework_agents else None
|
|
331
|
+
if agent_id is None:
|
|
332
|
+
continue
|
|
333
|
+
task_ids.add(task_id)
|
|
334
|
+
tasks_by_agent[agent_id].append((_framework_task_edge_score(edge), task_id))
|
|
335
|
+
|
|
336
|
+
if not task_ids:
|
|
337
|
+
return nodes, edges, {
|
|
338
|
+
"framework_task_node_count": 0,
|
|
339
|
+
"framework_task_content_node_count": 0,
|
|
340
|
+
"framework_task_agent_count": 0,
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
projected_nodes: list[dict[str, Any]] = []
|
|
344
|
+
for node in nodes:
|
|
345
|
+
node_id = str(node.get("id") or "")
|
|
346
|
+
if node_id in task_ids:
|
|
347
|
+
continue
|
|
348
|
+
if node.get("type") == "agent" and node_id in framework_agents:
|
|
349
|
+
candidates: dict[str, tuple[int, str, str]] = {}
|
|
350
|
+
for score, task_id in tasks_by_agent.get(node_id, []):
|
|
351
|
+
previous = candidates.get(task_id)
|
|
352
|
+
if previous is None or score > previous:
|
|
353
|
+
candidates[task_id] = score
|
|
354
|
+
ordered_ids = [
|
|
355
|
+
task_id
|
|
356
|
+
for task_id, _ in sorted(
|
|
357
|
+
candidates.items(),
|
|
358
|
+
key=lambda item: (item[1], item[0]),
|
|
359
|
+
reverse=True,
|
|
360
|
+
)
|
|
361
|
+
]
|
|
362
|
+
latest_task = node_by_id.get(ordered_ids[0]) if ordered_ids else None
|
|
363
|
+
attributes = dict(_attrs(node))
|
|
364
|
+
attributes["viewer_assigned_task_ids"] = ordered_ids
|
|
365
|
+
attributes["viewer_assigned_task_count"] = len(ordered_ids)
|
|
366
|
+
if latest_task is not None:
|
|
367
|
+
attributes["viewer_assigned_task_id"] = str(latest_task.get("id") or "")
|
|
368
|
+
attributes["viewer_assigned_task_name"] = str(latest_task.get("name") or "")
|
|
369
|
+
task_text = _framework_task_text(latest_task)
|
|
370
|
+
if task_text:
|
|
371
|
+
attributes["viewer_assigned_task_prompt"] = task_text
|
|
372
|
+
projected_nodes.append({**node, "attributes": attributes})
|
|
373
|
+
continue
|
|
374
|
+
projected_nodes.append(node)
|
|
375
|
+
|
|
376
|
+
hidden_content_ids: set[str] = set()
|
|
377
|
+
for node in nodes:
|
|
378
|
+
node_id = str(node.get("id") or "")
|
|
379
|
+
if node.get("type") != "observed_content" or not node_id:
|
|
380
|
+
continue
|
|
381
|
+
incident = [
|
|
382
|
+
edge
|
|
383
|
+
for edge in edges
|
|
384
|
+
if edge.get("source") == node_id or edge.get("target") == node_id
|
|
385
|
+
]
|
|
386
|
+
if incident and all(
|
|
387
|
+
(edge.get("source") in task_ids or edge.get("target") in task_ids)
|
|
388
|
+
for edge in incident
|
|
389
|
+
):
|
|
390
|
+
hidden_content_ids.add(node_id)
|
|
391
|
+
|
|
392
|
+
hidden_ids = task_ids | hidden_content_ids
|
|
393
|
+
projected_nodes = [node for node in projected_nodes if str(node.get("id") or "") not in hidden_content_ids]
|
|
394
|
+
projected_edges = [
|
|
395
|
+
edge
|
|
396
|
+
for edge in edges
|
|
397
|
+
if edge.get("source") not in hidden_ids and edge.get("target") not in hidden_ids
|
|
398
|
+
]
|
|
399
|
+
return projected_nodes, projected_edges, {
|
|
400
|
+
"framework_task_node_count": len(task_ids),
|
|
401
|
+
"framework_task_content_node_count": len(hidden_content_ids),
|
|
402
|
+
"framework_task_agent_count": len(tasks_by_agent),
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
|
|
261
406
|
def project_provider_neutral_viewer_graph(graph: dict[str, Any]) -> dict[str, Any]:
|
|
262
407
|
"""Provider-neutral display projection; raw graph/evidence remains unchanged."""
|
|
263
408
|
from . import viewer_projection_base as base
|
|
@@ -268,6 +413,7 @@ def project_provider_neutral_viewer_graph(graph: dict[str, Any]) -> dict[str, An
|
|
|
268
413
|
nodes = [deepcopy(n) for n in filtered.get("nodes", []) if isinstance(n, dict)]
|
|
269
414
|
edges = [deepcopy(e) for e in filtered.get("edges", []) if isinstance(e, dict)]
|
|
270
415
|
entries = _entries(graph)
|
|
416
|
+
nodes, edges, framework_tasks = collapse_framework_tasks(nodes, edges)
|
|
271
417
|
nodes, edges, inference = collapse_inference_requests(nodes, edges, entries)
|
|
272
418
|
roots, _ = _roots(nodes, entries)
|
|
273
419
|
nodes, edges, files = collapse_orphan_files(nodes, edges, roots[0] if len(roots) == 1 else None)
|
|
@@ -297,6 +443,7 @@ def project_provider_neutral_viewer_graph(graph: dict[str, Any]) -> dict[str, An
|
|
|
297
443
|
or inference["collapsed_request_count"]
|
|
298
444
|
or local is not None
|
|
299
445
|
or files is not None
|
|
446
|
+
or framework_tasks["framework_task_node_count"]
|
|
300
447
|
)
|
|
301
448
|
if not topology_changed:
|
|
302
449
|
if audit["count"]:
|
|
@@ -313,6 +460,9 @@ def project_provider_neutral_viewer_graph(graph: dict[str, Any]) -> dict[str, An
|
|
|
313
460
|
"orphan_file_node_count": len(files.get("nodes") or []) if files else 0,
|
|
314
461
|
"inference_request_count": inference["collapsed_request_count"], "logical_inference_count": inference["logical_inference_count"],
|
|
315
462
|
"direct_inference_edge_count": inference["direct_inference_edge_count"], "unresolved_inference_requests": inference["unresolved"],
|
|
463
|
+
"framework_task_node_count": framework_tasks["framework_task_node_count"],
|
|
464
|
+
"framework_task_content_node_count": framework_tasks["framework_task_content_node_count"],
|
|
465
|
+
"framework_task_agent_count": framework_tasks["framework_task_agent_count"],
|
|
316
466
|
"internal_hook_node_count": len(hook_nodes), "internal_hook_edge_count": len(hook_edges),
|
|
317
467
|
"projection_topology_changed": True,
|
|
318
468
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from uuid import uuid4
|
|
@@ -7,11 +8,16 @@ from uuid import uuid4
|
|
|
7
8
|
from .backends import BackendName, create_collector, resolve_backend
|
|
8
9
|
from .fidelity import write_fidelity_report
|
|
9
10
|
from .graph import build_execution_graph, write_execution_graph
|
|
11
|
+
from .semantic import merge_semantic_sidecar
|
|
10
12
|
from .sink import JsonlSink
|
|
11
13
|
from .theme import ensure_viewer_theme
|
|
12
14
|
from .validate import validate_event_stream
|
|
13
15
|
from .viewer_projection import strip_internal_hook_execution_graph, write_graph_html
|
|
14
16
|
|
|
17
|
+
_SEMANTIC_ENV = "EXECWEAVE_SEMANTIC_SIDECAR"
|
|
18
|
+
_RUN_ID_ENV = "EXECWEAVE_RUN_ID"
|
|
19
|
+
_SESSION_ID_ENV = "EXECWEAVE_SESSION_ID"
|
|
20
|
+
|
|
15
21
|
|
|
16
22
|
@dataclass(frozen=True)
|
|
17
23
|
class RecordResult:
|
|
@@ -26,6 +32,9 @@ class RecordResult:
|
|
|
26
32
|
event_count: int
|
|
27
33
|
node_count: int
|
|
28
34
|
edge_count: int
|
|
35
|
+
semantic_sidecar: Path | None = None
|
|
36
|
+
materialized_event_stream: Path | None = None
|
|
37
|
+
semantic_event_count: int = 0
|
|
29
38
|
|
|
30
39
|
def to_dict(self) -> dict[str, object]:
|
|
31
40
|
return {
|
|
@@ -40,6 +49,15 @@ class RecordResult:
|
|
|
40
49
|
"event_count": self.event_count,
|
|
41
50
|
"node_count": self.node_count,
|
|
42
51
|
"edge_count": self.edge_count,
|
|
52
|
+
"semantic_sidecar": (
|
|
53
|
+
str(self.semantic_sidecar) if self.semantic_sidecar is not None else None
|
|
54
|
+
),
|
|
55
|
+
"materialized_event_stream": (
|
|
56
|
+
str(self.materialized_event_stream)
|
|
57
|
+
if self.materialized_event_stream is not None
|
|
58
|
+
else str(self.event_stream)
|
|
59
|
+
),
|
|
60
|
+
"semantic_event_count": self.semantic_event_count,
|
|
43
61
|
}
|
|
44
62
|
|
|
45
63
|
|
|
@@ -61,6 +79,7 @@ def record_to_viewer(
|
|
|
61
79
|
collect_network: bool = True,
|
|
62
80
|
keep_raw_trace: bool = False,
|
|
63
81
|
open_browser: bool = False,
|
|
82
|
+
integrate_semantic: bool = True,
|
|
64
83
|
) -> RecordResult:
|
|
65
84
|
"""Record one command and materialize a local graph/viewer after it exits."""
|
|
66
85
|
if not command:
|
|
@@ -79,7 +98,12 @@ def record_to_viewer(
|
|
|
79
98
|
fidelity_path = run_dir / "fidelity.json"
|
|
80
99
|
graph_path = run_dir / "graph.json"
|
|
81
100
|
viewer_path = run_dir / "viewer.html"
|
|
82
|
-
|
|
101
|
+
semantic_path = run_dir / "semantic.jsonl"
|
|
102
|
+
merged_event_path = run_dir / "events.semantic.jsonl"
|
|
103
|
+
artifacts = [event_path, fidelity_path, graph_path, viewer_path]
|
|
104
|
+
if integrate_semantic:
|
|
105
|
+
artifacts.extend([semantic_path, merged_event_path])
|
|
106
|
+
_preflight_artifacts(artifacts)
|
|
83
107
|
|
|
84
108
|
sink = JsonlSink(event_path)
|
|
85
109
|
resolved = resolve_backend(backend)
|
|
@@ -94,14 +118,44 @@ def record_to_viewer(
|
|
|
94
118
|
keep_raw_trace=keep_raw_trace,
|
|
95
119
|
)
|
|
96
120
|
|
|
97
|
-
|
|
121
|
+
environment_updates = {
|
|
122
|
+
_RUN_ID_ENV: session_id,
|
|
123
|
+
_SESSION_ID_ENV: session_id,
|
|
124
|
+
}
|
|
125
|
+
if integrate_semantic:
|
|
126
|
+
environment_updates[_SEMANTIC_ENV] = str(semantic_path)
|
|
127
|
+
previous_environment = {
|
|
128
|
+
key: os.environ.get(key) for key in environment_updates
|
|
129
|
+
}
|
|
130
|
+
os.environ.update(environment_updates)
|
|
131
|
+
try:
|
|
132
|
+
return_code = collector.run(command)
|
|
133
|
+
finally:
|
|
134
|
+
for key, value in previous_environment.items():
|
|
135
|
+
if value is None:
|
|
136
|
+
os.environ.pop(key, None)
|
|
137
|
+
else:
|
|
138
|
+
os.environ[key] = value
|
|
98
139
|
|
|
99
140
|
validation = validate_event_stream(event_path)
|
|
100
141
|
if not validation.valid:
|
|
101
142
|
details = "; ".join(validation.errors)
|
|
102
143
|
raise RuntimeError(f"recorded event stream failed validation: {details}")
|
|
103
144
|
|
|
104
|
-
|
|
145
|
+
materialized_event_path = event_path
|
|
146
|
+
semantic_event_count = 0
|
|
147
|
+
if integrate_semantic and semantic_path.exists() and semantic_path.stat().st_size > 0:
|
|
148
|
+
semantic_merge = merge_semantic_sidecar(
|
|
149
|
+
event_path,
|
|
150
|
+
semantic_path,
|
|
151
|
+
merged_event_path,
|
|
152
|
+
)
|
|
153
|
+
materialized_event_path = merged_event_path
|
|
154
|
+
semantic_event_count = semantic_merge.semantic_event_count
|
|
155
|
+
|
|
156
|
+
execution_graph = strip_internal_hook_execution_graph(
|
|
157
|
+
build_execution_graph(materialized_event_path)
|
|
158
|
+
)
|
|
105
159
|
write_fidelity_report(execution_graph.fidelity, fidelity_path)
|
|
106
160
|
write_execution_graph(execution_graph, graph_path)
|
|
107
161
|
write_graph_html(execution_graph.to_dict(), viewer_path, open_browser=False)
|
|
@@ -123,4 +177,7 @@ def record_to_viewer(
|
|
|
123
177
|
event_count=execution_graph.event_count,
|
|
124
178
|
node_count=len(execution_graph.nodes),
|
|
125
179
|
edge_count=len(execution_graph.edges),
|
|
180
|
+
semantic_sidecar=semantic_path.resolve() if integrate_semantic else None,
|
|
181
|
+
materialized_event_stream=materialized_event_path.resolve(),
|
|
182
|
+
semantic_event_count=semantic_event_count,
|
|
126
183
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|