execweave 0.8.34__tar.gz → 0.8.35__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.34 → execweave-0.8.35}/PKG-INFO +18 -3
- {execweave-0.8.34 → execweave-0.8.35}/README.md +17 -2
- {execweave-0.8.34 → execweave-0.8.35}/pyproject.toml +2 -2
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/__init__.py +1 -1
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/_conversation_records_core.py +3 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/_http_proxy_base.py +26 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/_http_proxy_bounded.py +71 -30
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/_http_proxy_stage.py +3 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/agent_bootstrap.py +74 -11
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/anthropic.py +5 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/anthropic_cli.py +3 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_adapter_base.py +5 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_hook_cli.py +9 -2
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_adapter.py +5 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_hook_cli.py +6 -1
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cli.py +9 -8
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_adapter.py +5 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_hook_cli.py +5 -1
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_hook_entry.py +5 -4
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/collector.py +101 -20
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/content_store.py +16 -10
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/correlation.py +11 -19
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_adapter.py +5 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_hook_cli.py +6 -1
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/entry.py +23 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/fidelity.py +4 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/filesystem.py +82 -2
- execweave-0.8.35/src/execweave/finalization.py +92 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/base.py +7 -5
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/graph.py +10 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/graph_ops.py +24 -10
- execweave-0.8.35/src/execweave/hook_command.py +32 -0
- execweave-0.8.35/src/execweave/hook_delivery.py +32 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/inference_gateway.py +5 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/inference_gateway_cli.py +3 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/integrity.py +25 -23
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/integrity_cli.py +12 -1
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/litellm_callback.py +4 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live.py +8 -8
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_core.py +133 -29
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_extra_style.py +1 -1
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_script_a.py +7 -5
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/model_runtime.py +5 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/model_runtime_cli.py +3 -0
- execweave-0.8.35/src/execweave/observation.py +53 -0
- execweave-0.8.35/src/execweave/observation_demo.py +69 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/openai_compatible.py +5 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/openai_compatible_cli.py +3 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_adapter.py +5 -6
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_hook_cli.py +8 -2
- execweave-0.8.35/src/execweave/privacy.py +84 -0
- execweave-0.8.35/src/execweave/private_io.py +465 -0
- execweave-0.8.35/src/execweave/risk.py +106 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/run_assessment.py +16 -4
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/semantic.py +16 -16
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/session_summary.py +38 -0
- execweave-0.8.35/src/execweave/sink.py +53 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/task_validation.py +5 -5
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/top.py +30 -20
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/top_cli.py +3 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer.py +3 -2
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel.py +1 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_flow_canvas.py +525 -52
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_flow_layout.py +8 -55
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_projection.py +4 -3
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_projection_base.py +3 -2
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_run_assessment.py +5 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_semantic_projection.py +152 -4
- execweave-0.8.35/src/execweave/viewer_session_flow.py +491 -0
- execweave-0.8.34/src/execweave/finalization.py +0 -58
- execweave-0.8.34/src/execweave/sink.py +0 -33
- execweave-0.8.34/src/execweave/viewer_session_flow.py +0 -166
- {execweave-0.8.34 → execweave-0.8.35}/.gitignore +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/LICENSE +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/__main__.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/_dashboard_shell_base.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/acceptance_output.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/agent_topology.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/agent_trace.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/agy_preview_sanitize.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/analysis.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/anthropic_full_fidelity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_adapter.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_full_fidelity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_full_fidelity_base.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_full_fidelity_collaboration_base.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_record.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_subagent_linkage.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_trace_capability.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/auto_specialized.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/backends.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/benchmark.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_child_transcript.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_delegation.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_full_fidelity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_hook_contract.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_model_observer.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_record.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_conversation.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_full_fidelity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_hook_lifecycle.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_message_diagnostics.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_message_transport_diagnostics.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_record.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_rollout_structures.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_rollout_trace.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_rollout_trace_base.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/command.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/content_evidence.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/content_integrity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/controlled_checks.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_archive.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_message_identity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_antigravity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_claude.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_codex.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_common.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_generic.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_lines.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_transcript.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_records.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_records_antigravity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_records_codex.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_records_common.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_records_ollama.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_delegation.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_delegation_base.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_full_fidelity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_hook_contract.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_lifecycle.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_record.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/dashboard_shell.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/delivery_status.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/evidence_availability.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/evidence_grade.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/focus.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/__init__.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/autogen.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/camel.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/camel_channel.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/metagpt.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/registry.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/hook_input.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/http_proxy.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/http_proxy_cli.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/inference_gateway_full_fidelity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/inference_identity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/inference_identity_cli.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/investigation_index.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/litellm_callback_cli.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_markup.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_process_layout.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_readability.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_script_b.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_script_c.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_script_d.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_style.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/model_runtime_full_fidelity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/openai_compatible_full_fidelity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_event_contract.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_full_fidelity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_plugin_cli.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_record.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_task_linkage.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/overhead_benchmark.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/provider_capability.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/provider_lifecycle.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/provider_record.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/redacted_archive.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/rule_pack.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/rule_pack_cli.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/runtime_endpoint_availability.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/scalability_benchmark.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/schema.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/scope.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/strace_backend.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/stream_assembly.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/task_artifacts.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/theme.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/validate.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/vendor/dagre.min.js +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/view_cli.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_antigravity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_claude.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_codex.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_cursor.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_default.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_ollama.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_opencode.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_registry.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_archive_verifier.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_background_files.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_content_browser.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_content_health.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_content_inspector.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_controlled_checks.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_conversation_sync.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_dashboard_clean.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_dashboard_focus.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_dashboard_hardening.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_dashboard_hardening_v2.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_dashboard_pr70.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_delivery_status.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_edge_decross.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_execution_flow.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_execution_flow_provider_normalization.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_execution_flow_safety.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_external_endpoints.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_fold_state.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_framework_flow.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_framework_model_output.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_gif.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_history_browser.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_investigation.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_layout_geometry.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_layout_v2.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_layout_v2_detached.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_limits.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_live_layout.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_message_handoffs.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_projection_bridges.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_recorded_sources.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_run_guide.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_runtime_evidence.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_structure_share.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_task_validation.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_tool_mirror_identity.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_workflow_mode.py +0 -0
- {execweave-0.8.34 → execweave-0.8.35}/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.35
|
|
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
|
|
@@ -61,6 +61,10 @@ Provides-Extra: validation
|
|
|
61
61
|
Requires-Dist: cryptography>=44; extra == 'validation'
|
|
62
62
|
Description-Content-Type: text/markdown
|
|
63
63
|
|
|
64
|
+
<p align="center">
|
|
65
|
+
<img src="docs/assets/shot5_brand.jpg" alt="ExecWeave" width="100%">
|
|
66
|
+
</p>
|
|
67
|
+
|
|
64
68
|
# ExecWeave
|
|
65
69
|
|
|
66
70
|
<!-- i18n-nav:start -->
|
|
@@ -147,7 +151,15 @@ execweave top -- codex
|
|
|
147
151
|
|
|
148
152
|
### Provider integration approval
|
|
149
153
|
|
|
150
|
-
|
|
154
|
+
Since 0.8.35, `live` only checks hook configuration; it never installs or rewrites it. Explicitly run `execweave hooks install antigravity` (or `claude`, `codex`, `cursor`, `opencode`) from your user-owned environment, without sudo. `execweave hooks status antigravity` checks setup without writing. Provider approval may still be required. **Configured does not mean evidence was delivered.** Missing delivery and incomplete observation are recorded independently of process exit.
|
|
155
|
+
|
|
156
|
+
See [observation integrity and the negative example](docs/observation-integrity.md) for the three result axes and their honest limits. These changes ship in 0.8.35; earlier versions do not contain them.
|
|
157
|
+
|
|
158
|
+
**Live privacy and authentication (0.8.35)**
|
|
159
|
+
|
|
160
|
+
`execweave live` and `execweave top` record the complete provider/model content an integration exposes (prompts, responses, tool values) by default, because the run stays on your own machine for you to inspect. Add `--metadata-only` when a run must not store that plaintext; `--capture-content` remains accepted and selects the default explicitly. Invalid capture-policy values fail closed to metadata-only at automatic recorder boundaries.
|
|
161
|
+
|
|
162
|
+
The live dashboard no longer puts its API credential in `?t=...`. The announced URL contains no credential. Browser access uses a one-time pairing code submitted in a POST body and then an `HttpOnly; SameSite=Strict` cookie; non-browser API clients use the private `X-ExecWeave-Token` header.
|
|
151
163
|
|
|
152
164
|
Google Antigravity currently uses the `agy` CLI command. ExecWeave also accepts `antigravity` as a friendly alias.
|
|
153
165
|
|
|
@@ -185,6 +197,8 @@ execweave live --open -- ollama run deepseek-r1:1.5b
|
|
|
185
197
|
|
|
186
198
|
This mode does not start an Ollama server. A reachable upstream server is still required.
|
|
187
199
|
|
|
200
|
+
In both modes the relay forwards every request and, by default, records each inference exchange so the prompts and responses appear in the run and on the dashboard. Add `--metadata-only` (for example, `execweave live --metadata-only --open -- ollama serve`) when the conversation must not be stored.
|
|
201
|
+
|
|
188
202
|
## Dashboard
|
|
189
203
|
|
|
190
204
|
The dashboard is designed to keep large, multi-agent runs inspectable without changing the underlying evidence.
|
|
@@ -348,7 +362,8 @@ Raw observations remain separate from derived semantic and correlation outputs.
|
|
|
348
362
|
- **Failure ownership cleanup:** if the portable collector fails unexpectedly after launching a managed workload, it terminates the workload it owns before recording terminal session state. A filesystem observer that only partially starts is also torn down before the startup error propagates.
|
|
349
363
|
- Linux also provides a `strace` reference backend with stronger syscall-attributed evidence for supported executions.
|
|
350
364
|
- Provider semantics depend on what each integration actually exposes. Missing prompts, hidden reasoning, remote provider internals, and unexposed routing cannot be reconstructed reliably.
|
|
351
|
-
- Full-fidelity provider content may contain credentials, secrets, source code, prompts, tool values, model responses, shell output, and file contents.
|
|
365
|
+
- Full-fidelity provider content may contain credentials, secrets, source code, prompts, tool values, model responses, shell output, and file contents. `live`/`top` store it by default because the run stays on the local machine; the run directory is owner-private, and `--metadata-only` keeps that plaintext out of the run entirely.
|
|
366
|
+
- Run artifacts are written through owner-private primitives. POSIX permissions are established before sensitive bytes are published; the Windows path applies a protected owner DACL. Final Windows guarantees still depend on the release CI matrix and are not inferred from Linux tests.
|
|
352
367
|
- Conversation isolation is an attribution rule, not a redaction boundary. Explicitly routed content may legitimately appear at more than one participant.
|
|
353
368
|
- A local integrity manifest detects changes relative to the manifest; it is not an adversary-resistant trusted logging system if both evidence and manifest remain inside the same writable trust boundary.
|
|
354
369
|
- Review the complete run directory before sharing it.
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/shot5_brand.jpg" alt="ExecWeave" width="100%">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
# ExecWeave
|
|
2
6
|
|
|
3
7
|
<!-- i18n-nav:start -->
|
|
@@ -84,7 +88,15 @@ execweave top -- codex
|
|
|
84
88
|
|
|
85
89
|
### Provider integration approval
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
Since 0.8.35, `live` only checks hook configuration; it never installs or rewrites it. Explicitly run `execweave hooks install antigravity` (or `claude`, `codex`, `cursor`, `opencode`) from your user-owned environment, without sudo. `execweave hooks status antigravity` checks setup without writing. Provider approval may still be required. **Configured does not mean evidence was delivered.** Missing delivery and incomplete observation are recorded independently of process exit.
|
|
92
|
+
|
|
93
|
+
See [observation integrity and the negative example](docs/observation-integrity.md) for the three result axes and their honest limits. These changes ship in 0.8.35; earlier versions do not contain them.
|
|
94
|
+
|
|
95
|
+
**Live privacy and authentication (0.8.35)**
|
|
96
|
+
|
|
97
|
+
`execweave live` and `execweave top` record the complete provider/model content an integration exposes (prompts, responses, tool values) by default, because the run stays on your own machine for you to inspect. Add `--metadata-only` when a run must not store that plaintext; `--capture-content` remains accepted and selects the default explicitly. Invalid capture-policy values fail closed to metadata-only at automatic recorder boundaries.
|
|
98
|
+
|
|
99
|
+
The live dashboard no longer puts its API credential in `?t=...`. The announced URL contains no credential. Browser access uses a one-time pairing code submitted in a POST body and then an `HttpOnly; SameSite=Strict` cookie; non-browser API clients use the private `X-ExecWeave-Token` header.
|
|
88
100
|
|
|
89
101
|
Google Antigravity currently uses the `agy` CLI command. ExecWeave also accepts `antigravity` as a friendly alias.
|
|
90
102
|
|
|
@@ -122,6 +134,8 @@ execweave live --open -- ollama run deepseek-r1:1.5b
|
|
|
122
134
|
|
|
123
135
|
This mode does not start an Ollama server. A reachable upstream server is still required.
|
|
124
136
|
|
|
137
|
+
In both modes the relay forwards every request and, by default, records each inference exchange so the prompts and responses appear in the run and on the dashboard. Add `--metadata-only` (for example, `execweave live --metadata-only --open -- ollama serve`) when the conversation must not be stored.
|
|
138
|
+
|
|
125
139
|
## Dashboard
|
|
126
140
|
|
|
127
141
|
The dashboard is designed to keep large, multi-agent runs inspectable without changing the underlying evidence.
|
|
@@ -285,7 +299,8 @@ Raw observations remain separate from derived semantic and correlation outputs.
|
|
|
285
299
|
- **Failure ownership cleanup:** if the portable collector fails unexpectedly after launching a managed workload, it terminates the workload it owns before recording terminal session state. A filesystem observer that only partially starts is also torn down before the startup error propagates.
|
|
286
300
|
- Linux also provides a `strace` reference backend with stronger syscall-attributed evidence for supported executions.
|
|
287
301
|
- Provider semantics depend on what each integration actually exposes. Missing prompts, hidden reasoning, remote provider internals, and unexposed routing cannot be reconstructed reliably.
|
|
288
|
-
- Full-fidelity provider content may contain credentials, secrets, source code, prompts, tool values, model responses, shell output, and file contents.
|
|
302
|
+
- Full-fidelity provider content may contain credentials, secrets, source code, prompts, tool values, model responses, shell output, and file contents. `live`/`top` store it by default because the run stays on the local machine; the run directory is owner-private, and `--metadata-only` keeps that plaintext out of the run entirely.
|
|
303
|
+
- Run artifacts are written through owner-private primitives. POSIX permissions are established before sensitive bytes are published; the Windows path applies a protected owner DACL. Final Windows guarantees still depend on the release CI matrix and are not inferred from Linux tests.
|
|
289
304
|
- Conversation isolation is an attribution rule, not a redaction boundary. Explicitly routed content may legitimately appear at more than one participant.
|
|
290
305
|
- A local integrity manifest detects changes relative to the manifest; it is not an adversary-resistant trusted logging system if both evidence and manifest remain inside the same writable trust boundary.
|
|
291
306
|
- Review the complete run directory before sharing it.
|
|
@@ -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.35 release metadata
|
|
8
|
+
version = "0.8.35"
|
|
9
9
|
description = "Turn AI-agent runtime behavior into a local interactive execution graph."
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
import re
|
|
5
4
|
from collections import Counter
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
from typing import Any
|
|
8
7
|
from .content_store import _filesystem_path
|
|
8
|
+
from .private_io import write_private_json, write_private_text
|
|
9
9
|
|
|
10
10
|
from .agent_topology import (
|
|
11
11
|
COMPLETENESS_ROUTING_ONLY,
|
|
@@ -897,9 +897,6 @@ def write_conversation_records(
|
|
|
897
897
|
entries = payload["entries"]
|
|
898
898
|
json_path = root / "conversations.json"
|
|
899
899
|
markdown_path = root / "conversations.md"
|
|
900
|
-
json_path.
|
|
901
|
-
|
|
902
|
-
encoding="utf-8",
|
|
903
|
-
)
|
|
904
|
-
markdown_path.write_text(_render_markdown(entries), encoding="utf-8")
|
|
900
|
+
write_private_json(json_path, payload, replace=json_path.exists())
|
|
901
|
+
write_private_text(markdown_path, _render_markdown(entries), replace=markdown_path.exists())
|
|
905
902
|
return json_path, markdown_path
|
|
@@ -4,6 +4,7 @@ import http.client
|
|
|
4
4
|
import ipaddress
|
|
5
5
|
import json
|
|
6
6
|
import sys
|
|
7
|
+
import threading
|
|
7
8
|
import uuid
|
|
8
9
|
from dataclasses import dataclass
|
|
9
10
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
@@ -16,6 +17,7 @@ from .content_store import FullFidelityContentStore
|
|
|
16
17
|
from .model_runtime_full_fidelity import runtime_exchange_to_content_events
|
|
17
18
|
from .openai_compatible import append_openai_compatible_records
|
|
18
19
|
from .openai_compatible_full_fidelity import exchange_to_content_events
|
|
20
|
+
from .privacy import full_content_capture_enabled
|
|
19
21
|
from .stream_assembly import (
|
|
20
22
|
OLLAMA_NDJSON,
|
|
21
23
|
OPENAI_CHAT_DELTA,
|
|
@@ -208,6 +210,8 @@ def record_exchange_fail_open(
|
|
|
208
210
|
request_only: bool = False,
|
|
209
211
|
request_recorded: bool = False,
|
|
210
212
|
) -> bool:
|
|
213
|
+
if not full_content_capture_enabled():
|
|
214
|
+
return False
|
|
211
215
|
try:
|
|
212
216
|
request = _json(request_body)
|
|
213
217
|
if not isinstance(request, dict):
|
|
@@ -386,8 +390,30 @@ class ExecWeaveHTTPProxyServer(ThreadingHTTPServer):
|
|
|
386
390
|
self.proxy_config = config
|
|
387
391
|
self.upstream = _parse_upstream(config.upstream)
|
|
388
392
|
self.recorder = recorder
|
|
393
|
+
# Handler threads are daemonic, so ThreadingMixIn.server_close() does not
|
|
394
|
+
# join them. A response can reach the client before its recording finishes,
|
|
395
|
+
# so closing the server waits for those recordings explicitly instead.
|
|
396
|
+
self._recordings = 0
|
|
397
|
+
self._recordings_done = threading.Condition()
|
|
389
398
|
super().__init__(address, ExecWeaveHTTPProxyHandler)
|
|
390
399
|
|
|
400
|
+
def begin_recording(self) -> None:
|
|
401
|
+
with self._recordings_done:
|
|
402
|
+
self._recordings += 1
|
|
403
|
+
|
|
404
|
+
def end_recording(self) -> None:
|
|
405
|
+
with self._recordings_done:
|
|
406
|
+
self._recordings -= 1
|
|
407
|
+
self._recordings_done.notify_all()
|
|
408
|
+
|
|
409
|
+
def wait_for_recordings(self, timeout: float | None = None) -> bool:
|
|
410
|
+
with self._recordings_done:
|
|
411
|
+
return self._recordings_done.wait_for(lambda: self._recordings == 0, timeout)
|
|
412
|
+
|
|
413
|
+
def server_close(self) -> None:
|
|
414
|
+
super().server_close()
|
|
415
|
+
self.wait_for_recordings()
|
|
416
|
+
|
|
391
417
|
|
|
392
418
|
class ExecWeaveHTTPProxyHandler(BaseHTTPRequestHandler):
|
|
393
419
|
server: ExecWeaveHTTPProxyServer
|
|
@@ -4,7 +4,9 @@ import http.client
|
|
|
4
4
|
import itertools
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
|
+
import socket
|
|
7
8
|
import tempfile
|
|
9
|
+
import threading
|
|
8
10
|
from pathlib import Path
|
|
9
11
|
from typing import Any, Iterator
|
|
10
12
|
|
|
@@ -486,34 +488,73 @@ def relay_default(handler: _base.ExecWeaveHTTPProxyHandler) -> None:
|
|
|
486
488
|
return
|
|
487
489
|
finally:
|
|
488
490
|
connection.close()
|
|
491
|
+
|
|
492
|
+
def record() -> None:
|
|
493
|
+
try:
|
|
494
|
+
if capture is not None:
|
|
495
|
+
capture.finish()
|
|
496
|
+
_record_capture(
|
|
497
|
+
handler.server.proxy_config,
|
|
498
|
+
exchange_id=exchange_id,
|
|
499
|
+
request_body=body,
|
|
500
|
+
response_content_type=response_type,
|
|
501
|
+
method=handler.command,
|
|
502
|
+
request_path=target,
|
|
503
|
+
status=status,
|
|
504
|
+
capture=capture,
|
|
505
|
+
)
|
|
506
|
+
else:
|
|
507
|
+
handler.server.recorder(
|
|
508
|
+
handler.server.proxy_config,
|
|
509
|
+
exchange_id=exchange_id,
|
|
510
|
+
method=handler.command,
|
|
511
|
+
request_path=target,
|
|
512
|
+
request_body=body,
|
|
513
|
+
request_content_type=handler.headers.get("Content-Type"),
|
|
514
|
+
status=status,
|
|
515
|
+
response_body=bytes(response_body),
|
|
516
|
+
response_content_type=response_type,
|
|
517
|
+
request_recorded=request_recorded,
|
|
518
|
+
)
|
|
519
|
+
except Exception as exc:
|
|
520
|
+
print(f"ExecWeave HTTP proxy recorder warning: {exc}", file=_base.sys.stderr)
|
|
521
|
+
finally:
|
|
522
|
+
if capture is not None:
|
|
523
|
+
capture.cleanup()
|
|
524
|
+
|
|
525
|
+
_record_after_response(handler, record)
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
# Recording normally finishes in well under this, so the client still sees the end
|
|
529
|
+
# of the response only once its evidence is on disk. A slow disk, lock or scanner
|
|
530
|
+
# must not hold the user's program, though: past this point the response is ended
|
|
531
|
+
# while recording finishes, and closing the server waits for it.
|
|
532
|
+
_CLIENT_RELEASE_SECONDS = 2.0
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _record_after_response(handler: _base.ExecWeaveHTTPProxyHandler, record) -> None:
|
|
536
|
+
server = handler.server
|
|
537
|
+
server.begin_recording()
|
|
538
|
+
|
|
539
|
+
def run() -> None:
|
|
540
|
+
try:
|
|
541
|
+
record()
|
|
542
|
+
finally:
|
|
543
|
+
server.end_recording()
|
|
544
|
+
|
|
545
|
+
worker = threading.Thread(target=run, name="execweave-proxy-record", daemon=True)
|
|
489
546
|
try:
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
handler.server.proxy_config,
|
|
505
|
-
exchange_id=exchange_id,
|
|
506
|
-
method=handler.command,
|
|
507
|
-
request_path=target,
|
|
508
|
-
request_body=body,
|
|
509
|
-
request_content_type=handler.headers.get("Content-Type"),
|
|
510
|
-
status=status,
|
|
511
|
-
response_body=bytes(response_body),
|
|
512
|
-
response_content_type=response_type,
|
|
513
|
-
request_recorded=request_recorded,
|
|
514
|
-
)
|
|
515
|
-
except Exception as exc:
|
|
516
|
-
print(f"ExecWeave HTTP proxy recorder warning: {exc}", file=_base.sys.stderr)
|
|
517
|
-
finally:
|
|
518
|
-
if capture is not None:
|
|
519
|
-
capture.cleanup()
|
|
547
|
+
worker.start()
|
|
548
|
+
except RuntimeError:
|
|
549
|
+
run()
|
|
550
|
+
return
|
|
551
|
+
worker.join(_CLIENT_RELEASE_SECONDS)
|
|
552
|
+
if worker.is_alive():
|
|
553
|
+
# HTTP/1.0 with Connection: close and no Content-Length, so the client
|
|
554
|
+
# treats the end of the stream as the end of the response.
|
|
555
|
+
try:
|
|
556
|
+
handler.wfile.flush()
|
|
557
|
+
handler.connection.shutdown(socket.SHUT_WR)
|
|
558
|
+
except OSError:
|
|
559
|
+
pass
|
|
560
|
+
worker.join()
|
|
@@ -6,6 +6,7 @@ from . import _http_proxy_base as _base
|
|
|
6
6
|
from . import model_runtime_full_fidelity as _runtime_ff
|
|
7
7
|
from . import openai_compatible_full_fidelity as _openai_ff
|
|
8
8
|
from ._http_proxy_base import * # noqa: F403
|
|
9
|
+
from .privacy import full_content_capture_enabled
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
# OpenAI-compatible request config is request-side evidence too. Without this relation
|
|
@@ -435,6 +436,8 @@ def record_exchange_fail_open(
|
|
|
435
436
|
request_only: bool = False,
|
|
436
437
|
request_recorded: bool = False,
|
|
437
438
|
) -> bool:
|
|
439
|
+
if not full_content_capture_enabled():
|
|
440
|
+
return False
|
|
438
441
|
try:
|
|
439
442
|
if request_only:
|
|
440
443
|
return _record_request_phase(
|
|
@@ -8,6 +8,8 @@ from dataclasses import asdict, dataclass
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any, Mapping
|
|
10
10
|
|
|
11
|
+
from .hook_command import hook_argv, hook_command, hook_markers
|
|
12
|
+
|
|
11
13
|
_AUTO_FLAG = "--auto"
|
|
12
14
|
_CONFIG_RETRIES = 3
|
|
13
15
|
_SUPPORTED_AGENTS = {
|
|
@@ -73,7 +75,7 @@ def _read_bytes(path: Path) -> bytes | None:
|
|
|
73
75
|
def _contains_execweave_command(value: object, marker: str) -> bool:
|
|
74
76
|
if isinstance(value, dict):
|
|
75
77
|
command = value.get("command")
|
|
76
|
-
if isinstance(command, str) and
|
|
78
|
+
if isinstance(command, str) and any(value in command for value in hook_markers(marker)):
|
|
77
79
|
return True
|
|
78
80
|
return any(_contains_execweave_command(child, marker) for child in value.values())
|
|
79
81
|
if isinstance(value, list):
|
|
@@ -81,6 +83,28 @@ def _contains_execweave_command(value: object, marker: str) -> bool:
|
|
|
81
83
|
return False
|
|
82
84
|
|
|
83
85
|
|
|
86
|
+
def _migrate_hook_commands(value: object, marker: str) -> bool:
|
|
87
|
+
"""Upgrade only our old canonical bare entry point, retaining user settings."""
|
|
88
|
+
changed = False
|
|
89
|
+
if isinstance(value, dict):
|
|
90
|
+
command = value.get("command")
|
|
91
|
+
if isinstance(command, str):
|
|
92
|
+
prefix = f"{marker} --auto"
|
|
93
|
+
suffix = command[len(prefix):] if command.startswith(prefix) else None
|
|
94
|
+
valid_suffixes = {"", " --event PreInvocation", " --event PostInvocation",
|
|
95
|
+
" --event PostToolUse", " --event Stop"}
|
|
96
|
+
if suffix in valid_suffixes:
|
|
97
|
+
provider = marker.removeprefix("execweave-").removesuffix("-hook")
|
|
98
|
+
value["command"] = hook_command(provider) + suffix
|
|
99
|
+
changed = True
|
|
100
|
+
for child in value.values():
|
|
101
|
+
changed = _migrate_hook_commands(child, marker) or changed
|
|
102
|
+
elif isinstance(value, list):
|
|
103
|
+
for child in value:
|
|
104
|
+
changed = _migrate_hook_commands(child, marker) or changed
|
|
105
|
+
return changed
|
|
106
|
+
|
|
107
|
+
|
|
84
108
|
def _merge_hook_fragment(
|
|
85
109
|
current: dict[str, Any],
|
|
86
110
|
fragment: dict[str, Any],
|
|
@@ -88,7 +112,7 @@ def _merge_hook_fragment(
|
|
|
88
112
|
marker: str,
|
|
89
113
|
) -> tuple[dict[str, Any], bool]:
|
|
90
114
|
merged = deepcopy(current)
|
|
91
|
-
changed =
|
|
115
|
+
changed = _migrate_hook_commands(merged, marker)
|
|
92
116
|
|
|
93
117
|
for key, value in fragment.items():
|
|
94
118
|
if key == "hooks":
|
|
@@ -135,8 +159,9 @@ def _merge_named_hook_fragment(
|
|
|
135
159
|
) -> tuple[dict[str, Any], bool]:
|
|
136
160
|
"""Merge Antigravity's named-hook ``hooks.json`` schema conservatively."""
|
|
137
161
|
merged = deepcopy(current)
|
|
162
|
+
changed = _migrate_hook_commands(merged, marker)
|
|
138
163
|
if _contains_execweave_command(merged, marker):
|
|
139
|
-
return merged,
|
|
164
|
+
return merged, changed
|
|
140
165
|
for name, definition in fragment.items():
|
|
141
166
|
if name in merged:
|
|
142
167
|
raise FileExistsError(
|
|
@@ -215,9 +240,11 @@ def _write_plugin_file(path: Path, content: str, *, marker: str) -> bool:
|
|
|
215
240
|
existing = original.decode("utf-8")
|
|
216
241
|
except UnicodeDecodeError as exc:
|
|
217
242
|
raise ValueError(f"existing OpenCode plugin is not UTF-8: {path}") from exc
|
|
218
|
-
if existing == content
|
|
243
|
+
if existing == content:
|
|
219
244
|
return False
|
|
220
|
-
|
|
245
|
+
from .opencode_plugin_cli import plugin_text
|
|
246
|
+
if existing != plugin_text(("execweave-opencode-hook", _AUTO_FLAG)):
|
|
247
|
+
raise FileExistsError(f"refusing to replace existing OpenCode plugin: {path}")
|
|
221
248
|
if _write_bytes_optimistic(path, original, encoded):
|
|
222
249
|
return True
|
|
223
250
|
raise RuntimeError(f"plugin path changed concurrently while updating {path}")
|
|
@@ -254,11 +281,11 @@ def _provider_fragment(provider: str) -> tuple[dict[str, Any], str]:
|
|
|
254
281
|
if provider == "claude":
|
|
255
282
|
from .claude_hook_cli import claude_hook_config
|
|
256
283
|
|
|
257
|
-
return claude_hook_config(
|
|
284
|
+
return claude_hook_config(hook_command("claude")), "execweave-claude-hook"
|
|
258
285
|
if provider == "codex":
|
|
259
286
|
from .codex_hook_cli import codex_hook_config
|
|
260
287
|
|
|
261
|
-
return codex_hook_config(
|
|
288
|
+
return codex_hook_config(hook_command("codex")), "execweave-codex-hook"
|
|
262
289
|
if provider == "antigravity":
|
|
263
290
|
from .antigravity_hook_cli import antigravity_hook_config
|
|
264
291
|
|
|
@@ -266,7 +293,7 @@ def _provider_fragment(provider: str) -> tuple[dict[str, Any], str]:
|
|
|
266
293
|
if provider == "cursor":
|
|
267
294
|
from .cursor_hook_cli import cursor_hook_config
|
|
268
295
|
|
|
269
|
-
return cursor_hook_config(
|
|
296
|
+
return cursor_hook_config(hook_command("cursor")), "execweave-cursor-hook"
|
|
270
297
|
raise ValueError(f"provider does not use JSON hooks: {provider}")
|
|
271
298
|
|
|
272
299
|
|
|
@@ -293,7 +320,7 @@ def bootstrap_supported_agent(
|
|
|
293
320
|
if provider == "opencode":
|
|
294
321
|
from .opencode_plugin_cli import plugin_text
|
|
295
322
|
|
|
296
|
-
content = plugin_text(("
|
|
323
|
+
content = plugin_text(tuple(hook_argv("opencode")))
|
|
297
324
|
changed = _write_plugin_file(target, content, marker="execweave-opencode-hook")
|
|
298
325
|
else:
|
|
299
326
|
fragment, marker = _provider_fragment(provider)
|
|
@@ -317,10 +344,10 @@ def bootstrap_supported_agent(
|
|
|
317
344
|
|
|
318
345
|
return AgentBootstrapResult(
|
|
319
346
|
provider=provider,
|
|
320
|
-
status="
|
|
347
|
+
status="configured_unverified",
|
|
321
348
|
path=str(target),
|
|
322
349
|
changed=changed,
|
|
323
|
-
detail="specialized hook/plugin
|
|
350
|
+
detail="specialized hook/plugin configured; delivery unverified",
|
|
324
351
|
)
|
|
325
352
|
except (FileExistsError, OSError, RuntimeError, TypeError, ValueError) as exc:
|
|
326
353
|
return AgentBootstrapResult(
|
|
@@ -330,3 +357,39 @@ def bootstrap_supported_agent(
|
|
|
330
357
|
changed=False,
|
|
331
358
|
detail=_bounded_detail(exc),
|
|
332
359
|
)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def inspect_supported_agent(
|
|
363
|
+
command: list[str], *, home: str | Path | None = None,
|
|
364
|
+
environment: Mapping[str, str] | None = None,
|
|
365
|
+
) -> AgentBootstrapResult:
|
|
366
|
+
"""Read configuration only. Configuration is never proof of hook delivery."""
|
|
367
|
+
provider = supported_agent(command)
|
|
368
|
+
if provider is None:
|
|
369
|
+
return AgentBootstrapResult(provider=None, status="unavailable",
|
|
370
|
+
detail="no specialized lifecycle integration")
|
|
371
|
+
env = os.environ if environment is None else environment
|
|
372
|
+
home_path = Path.home() if home is None else Path(home).expanduser()
|
|
373
|
+
target = _provider_target(provider, home=home_path, environment=env)
|
|
374
|
+
try:
|
|
375
|
+
raw = _read_bytes(target)
|
|
376
|
+
if raw is None:
|
|
377
|
+
return AgentBootstrapResult(provider=provider, status="not_configured", path=str(target),
|
|
378
|
+
detail=f"Run execweave hooks install {provider} explicitly; never use sudo")
|
|
379
|
+
if provider == "opencode":
|
|
380
|
+
present = any(marker in raw.decode("utf-8")
|
|
381
|
+
for marker in hook_markers("execweave-opencode-hook"))
|
|
382
|
+
else:
|
|
383
|
+
config = _load_json_object(raw, path=target)
|
|
384
|
+
present = _contains_execweave_command(config, f"execweave-{provider}-hook")
|
|
385
|
+
if provider == "claude" and config.get("disableAllHooks") is True:
|
|
386
|
+
present = False
|
|
387
|
+
if provider == "antigravity":
|
|
388
|
+
group = config.get("execweave-observability", {})
|
|
389
|
+
present = present and isinstance(group, dict) and group.get("enabled") is True
|
|
390
|
+
return AgentBootstrapResult(provider=provider,
|
|
391
|
+
status="configured_unverified" if present else "not_configured",
|
|
392
|
+
path=str(target), detail="delivery must be verified from recorded evidence")
|
|
393
|
+
except (OSError, ValueError, UnicodeError) as exc:
|
|
394
|
+
return AgentBootstrapResult(provider=provider, status="bootstrap_failed", path=str(target),
|
|
395
|
+
detail=type(exc).__name__)
|
|
@@ -2,11 +2,12 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import hashlib
|
|
4
4
|
import json
|
|
5
|
-
import os
|
|
6
5
|
import time
|
|
7
6
|
from datetime import datetime, timezone
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
from typing import Any
|
|
9
|
+
|
|
10
|
+
from .private_io import append_private_text
|
|
10
11
|
from urllib.parse import urlsplit, urlunsplit
|
|
11
12
|
|
|
12
13
|
|
|
@@ -196,7 +197,8 @@ def response_to_events(
|
|
|
196
197
|
|
|
197
198
|
|
|
198
199
|
def append_anthropic_records(path: str | Path, records: list[dict[str, Any]]) -> Path:
|
|
199
|
-
|
|
200
|
+
raw_output = Path(path).expanduser()
|
|
201
|
+
output = raw_output.parent.resolve() / raw_output.name
|
|
200
202
|
output.parent.mkdir(parents=True, exist_ok=True)
|
|
201
203
|
if not records:
|
|
202
204
|
return output
|
|
@@ -217,10 +219,7 @@ def append_anthropic_records(path: str | Path, records: list[dict[str, Any]]) ->
|
|
|
217
219
|
)
|
|
218
220
|
time.sleep(0.01)
|
|
219
221
|
try:
|
|
220
|
-
|
|
221
|
-
handle.write(blob)
|
|
222
|
-
handle.flush()
|
|
223
|
-
os.fsync(handle.fileno())
|
|
222
|
+
append_private_text(output, blob)
|
|
224
223
|
finally:
|
|
225
224
|
try:
|
|
226
225
|
lock_dir.rmdir()
|
|
@@ -10,6 +10,7 @@ from pathlib import Path
|
|
|
10
10
|
from .anthropic import append_anthropic_records, response_to_events, sanitize_anthropic_endpoint
|
|
11
11
|
from .anthropic_full_fidelity import exchange_to_content_events, response_to_content_events
|
|
12
12
|
from .content_store import FullFidelityContentStore
|
|
13
|
+
from .privacy import full_content_capture_enabled
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def _now() -> str:
|
|
@@ -65,6 +66,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
65
66
|
|
|
66
67
|
|
|
67
68
|
def _append_full_fidelity_fail_open(sidecar: Path, records_factory) -> int:
|
|
69
|
+
if not full_content_capture_enabled():
|
|
70
|
+
return 0
|
|
68
71
|
try:
|
|
69
72
|
records = records_factory(FullFidelityContentStore(sidecar.parent))
|
|
70
73
|
append_anthropic_records(sidecar, records)
|
|
@@ -2,13 +2,14 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import hashlib
|
|
4
4
|
import json
|
|
5
|
-
import os
|
|
6
5
|
import sys
|
|
7
6
|
import time
|
|
8
7
|
from datetime import datetime, timezone
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
from typing import Any
|
|
11
10
|
|
|
11
|
+
from .private_io import append_private_text
|
|
12
|
+
|
|
12
13
|
|
|
13
14
|
def _now() -> str:
|
|
14
15
|
return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
@@ -435,7 +436,8 @@ def antigravity_hook_to_semantic_events(
|
|
|
435
436
|
|
|
436
437
|
|
|
437
438
|
def append_semantic_records(path: str | Path, records: list[dict[str, Any]]) -> Path:
|
|
438
|
-
|
|
439
|
+
raw_output = Path(path).expanduser()
|
|
440
|
+
output = raw_output.parent.resolve() / raw_output.name
|
|
439
441
|
output.parent.mkdir(parents=True, exist_ok=True)
|
|
440
442
|
if not records:
|
|
441
443
|
return output
|
|
@@ -454,10 +456,7 @@ def append_semantic_records(path: str | Path, records: list[dict[str, Any]]) ->
|
|
|
454
456
|
raise TimeoutError(f"timed out waiting for semantic sidecar lock: {lock_dir}")
|
|
455
457
|
time.sleep(0.01)
|
|
456
458
|
try:
|
|
457
|
-
|
|
458
|
-
handle.write(blob)
|
|
459
|
-
handle.flush()
|
|
460
|
-
os.fsync(handle.fileno())
|
|
459
|
+
append_private_text(output, blob)
|
|
461
460
|
finally:
|
|
462
461
|
try:
|
|
463
462
|
lock_dir.rmdir()
|
|
@@ -16,6 +16,9 @@ from .antigravity_adapter import (
|
|
|
16
16
|
from .antigravity_full_fidelity import antigravity_hook_to_content_events
|
|
17
17
|
from .antigravity_trace_capability import antigravity_agent_trace_visibility_event
|
|
18
18
|
from .content_store import FullFidelityContentStore
|
|
19
|
+
from .hook_command import hook_command
|
|
20
|
+
from .private_io import private_artifact_path
|
|
21
|
+
from .privacy import full_content_capture_enabled
|
|
19
22
|
from .conversation_archive import antigravity_conversation_archive_events
|
|
20
23
|
|
|
21
24
|
_CAPTURE_ERRORS = (OSError, RuntimeError, TimeoutError, TypeError, ValueError)
|
|
@@ -42,7 +45,7 @@ def _now() -> str:
|
|
|
42
45
|
def _handler(event: str) -> dict[str, Any]:
|
|
43
46
|
return {
|
|
44
47
|
"type": "command",
|
|
45
|
-
"command": f"
|
|
48
|
+
"command": f"{hook_command('antigravity')} --event {event}",
|
|
46
49
|
"timeout": 30,
|
|
47
50
|
}
|
|
48
51
|
|
|
@@ -126,7 +129,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
126
129
|
if sidecar is None:
|
|
127
130
|
configured = os.environ.get("EXECWEAVE_SEMANTIC_SIDECAR")
|
|
128
131
|
sidecar = Path(configured) if configured else _default_sidecar(payload)
|
|
129
|
-
sidecar =
|
|
132
|
+
sidecar = private_artifact_path(sidecar)
|
|
130
133
|
except _CAPTURE_ERRORS as exc:
|
|
131
134
|
print(f"ExecWeave Antigravity hook warning [setup]: {exc}", file=sys.stderr)
|
|
132
135
|
print(json.dumps(_passive_response(args.event), sort_keys=True))
|
|
@@ -156,6 +159,10 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
156
159
|
print(json.dumps(_passive_response(args.event), sort_keys=True))
|
|
157
160
|
return 1
|
|
158
161
|
|
|
162
|
+
if not full_content_capture_enabled():
|
|
163
|
+
print(json.dumps(_passive_response(args.event), sort_keys=True))
|
|
164
|
+
return 0
|
|
165
|
+
|
|
159
166
|
try:
|
|
160
167
|
store = FullFidelityContentStore(sidecar.parent)
|
|
161
168
|
except _CAPTURE_ERRORS as exc:
|