execweave 0.8.13__tar.gz → 0.8.15__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.13 → execweave-0.8.15}/.github/workflows/provider-capability-stage-integrity.yml +9 -0
- {execweave-0.8.13 → execweave-0.8.15}/PKG-INFO +1 -1
- {execweave-0.8.13 → execweave-0.8.15}/pyproject.toml +2 -2
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/__init__.py +1 -1
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/collector.py +120 -15
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live_view_style.py +2 -1
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_dashboard_clean.py +37 -8
- execweave-0.8.15/tests/test_d003_fast_reparent.py +134 -0
- execweave-0.8.15/tests/test_dashboard_file_activity_filter.py +30 -0
- execweave-0.8.15/tests/test_pypi_0813_cleanup.py +382 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_threat_model.py +2 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_v069_dashboard_release.py +3 -3
- {execweave-0.8.13 → execweave-0.8.15}/.github/workflows/ci.yml +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/.github/workflows/cleanup-workflow-history.yml +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/.github/workflows/i18n.yml +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/.github/workflows/package-benchmark.yml +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/.github/workflows/package-smoke.yml +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/.github/workflows/provider-dashboard-contract.yml +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/.github/workflows/publish.yml +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/.github/workflows/scalability.yml +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/.github/workflows/viewer-agent-isolation.yml +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/.github/workflows/wheel-smoke.yml +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/.github/workflows/windows-launcher.yml +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/.gitignore +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/LICENSE +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/README.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/README.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/README.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/README.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/README.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/README.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/README.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/README.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/benchmarks/phase1_overhead.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/acceptance_hardening_20260906.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/audit_eight_children.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/audit_python_journey.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/audit_real_clicks.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/current_state_reaudit.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/execweave_full_audit.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/gate_status.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/gate_status_history_through_g3.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/github_pr_inventory.json +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/handoff_checkpoint_20260906.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/next_agent_handoff.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/diagnostics/pr_regression_map.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/antigravity-hooks.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/assets/codex.gif +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/benchmarks/README.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/benchmarks/v0.6.0-github-actions.json +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/benchmarks/v0.6.0-github-actions.svg +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/claude-code-hooks.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/claude-code-hooks.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/claude-code-hooks.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/claude-code-hooks.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/claude-code-hooks.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/claude-code-hooks.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/claude-code-hooks.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/claude-code-hooks.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/codex-hooks.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/codex-hooks.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/codex-hooks.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/codex-hooks.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/codex-hooks.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/codex-hooks.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/codex-hooks.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/codex-hooks.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/cursor-hooks.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/cursor-hooks.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/cursor-hooks.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/cursor-hooks.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/cursor-hooks.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/cursor-hooks.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/cursor-hooks.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/cursor-hooks.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/evidence-grades.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/evidence-grades.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/evidence-grades.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/evidence-grades.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/evidence-grades.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/evidence-grades.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/evidence-grades.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/evidence-grades.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/inference-gateway.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/inference-gateway.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/inference-gateway.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/inference-gateway.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/inference-gateway.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/inference-gateway.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/inference-gateway.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/inference-gateway.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/internal/provider-capability-matrix.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/live-graph.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/live-graph.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/live-graph.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/live-graph.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/live-graph.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/live-graph.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/live-graph.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/live-graph.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/model-runtime.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/model-runtime.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/model-runtime.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/model-runtime.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/model-runtime.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/model-runtime.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/model-runtime.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/model-runtime.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/opencode-plugin.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/opencode-plugin.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/opencode-plugin.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/opencode-plugin.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/opencode-plugin.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/opencode-plugin.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/opencode-plugin.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/opencode-plugin.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-1-runtime-collection.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-1-runtime-collection.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-1-runtime-collection.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-1-runtime-collection.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-1-runtime-collection.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-1-runtime-collection.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-1-runtime-collection.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-1-runtime-collection.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-2-execution-graph.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-2-execution-graph.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-2-execution-graph.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-2-execution-graph.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-2-execution-graph.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-2-execution-graph.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-2-execution-graph.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/phase-2-execution-graph.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/remaining-provider-audit-v0.8.7.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/rule-packs.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/rule-packs.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/rule-packs.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/rule-packs.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/rule-packs.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/rule-packs.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/rule-packs.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/rule-packs.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/run-integrity.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/run-integrity.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/run-integrity.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/run-integrity.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/run-integrity.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/run-integrity.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/run-integrity.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/run-integrity.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/runtime-threat-model.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/runtime-threat-model.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/runtime-threat-model.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/runtime-threat-model.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/runtime-threat-model.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/runtime-threat-model.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/runtime-threat-model.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/runtime-threat-model.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/security-analysis.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/security-analysis.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/security-analysis.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/security-analysis.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/security-analysis.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/security-analysis.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/security-analysis.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/security-analysis.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/semantic-telemetry.de.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/semantic-telemetry.fr.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/semantic-telemetry.ja.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/semantic-telemetry.ko.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/semantic-telemetry.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/semantic-telemetry.ru.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/semantic-telemetry.zh-CN.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/semantic-telemetry.zh-TW.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/v0.6.5-hardening-decisions.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/docs/v0.8.3-graph-ergonomics.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/_check_release_stage_integrity_impl.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/_dashboard_acceptance_impl.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/_ollama_interactive_acceptance_impl.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/_ollama_visible_acceptance_impl.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/_python_native_acceptance_impl.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/acceptance/__init__.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/acceptance/browser_diagnostics.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/acceptance/contracts.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/acceptance/g5_runner.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/acceptance/g6_runner.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/acceptance/interrupt_guard.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/acceptance/processes.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/acceptance/reporting.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/acceptance/terminal_output.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/annotate_pytest_failures.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/audit_i18n_parity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_claude_hook.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_claude_record_graph.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_codex_hook.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_conversation_records.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_correlation_graph.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_inference_gateway_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_model_runtime_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_openai_compatible_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_release_stage_integrity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_semantic_graph.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_viewer_js.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_wheel_install.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/check_windows_launchers.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/dashboard_acceptance.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/emit_claude_hook_smoke.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/emit_codex_hook_smoke.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/emit_cursor_hook_smoke.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/emit_opencode_plugin_smoke.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/make_semantic_smoke.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/measure_graph_metrics.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/ollama_interactive_acceptance.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/ollama_visible_acceptance.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/owned_cleanup_acceptance.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/probe_provider_capability.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/python_native_acceptance.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/scripts/sync_i18n_nav.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/__main__.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/_conversation_records_core.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/_dashboard_shell_base.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/_http_proxy_base.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/_http_proxy_bounded.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/_http_proxy_stage.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/agent_bootstrap.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/agent_topology.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/agent_trace.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/agy_preview_sanitize.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/analysis.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/anthropic.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/anthropic_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/anthropic_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/antigravity_adapter.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/antigravity_adapter_base.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/antigravity_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/antigravity_full_fidelity_base.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/antigravity_full_fidelity_collaboration_base.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/antigravity_hook_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/antigravity_record.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/antigravity_subagent_linkage.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/antigravity_trace_capability.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/auto_specialized.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/backends.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/benchmark.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/claude_adapter.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/claude_child_transcript.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/claude_delegation.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/claude_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/claude_hook_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/claude_hook_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/claude_model_observer.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/claude_record.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_adapter.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_conversation.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_hook_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_hook_entry.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_hook_lifecycle.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_message_diagnostics.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_message_transport_diagnostics.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_record.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_rollout_structures.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_rollout_trace.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/codex_rollout_trace_base.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/command.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/content_evidence.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/content_store.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_archive.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_message_identity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_preview.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_preview_antigravity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_preview_claude.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_preview_codex.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_preview_common.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_preview_generic.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_preview_lines.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_preview_transcript.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_records.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_records_antigravity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_records_codex.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_records_common.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/conversation_records_ollama.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/correlation.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/cursor_adapter.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/cursor_delegation.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/cursor_delegation_base.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/cursor_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/cursor_hook_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/cursor_hook_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/cursor_record.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/dashboard_shell.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/entry.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/evidence_availability.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/evidence_grade.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/filesystem.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/focus.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/graph.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/graph_ops.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/http_proxy.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/http_proxy_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/inference_gateway.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/inference_gateway_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/inference_gateway_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/inference_identity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/inference_identity_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/integrity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/integrity_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/litellm_callback.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/litellm_callback_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live_core.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live_view.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live_view_extra_style.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live_view_markup.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live_view_process_layout.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live_view_readability.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live_view_script_a.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live_view_script_b.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live_view_script_c.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/live_view_script_d.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/model_runtime.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/model_runtime_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/model_runtime_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/openai_compatible.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/openai_compatible_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/openai_compatible_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/opencode_adapter.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/opencode_event_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/opencode_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/opencode_hook_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/opencode_plugin_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/opencode_record.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/opencode_task_linkage.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/overhead_benchmark.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/provider_capability.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/provider_lifecycle.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/provider_record.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/rule_pack.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/rule_pack_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/scalability_benchmark.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/schema.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/scope.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/semantic.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/sink.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/strace_backend.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/stream_assembly.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/theme.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/top.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/top_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/validate.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/vendor/dagre.min.js +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/view_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_agent_panel.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_agent_panel_antigravity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_agent_panel_claude.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_agent_panel_codex.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_agent_panel_cursor.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_agent_panel_default.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_agent_panel_ollama.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_agent_panel_opencode.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_agent_panel_registry.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_content_inspector.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_dashboard_focus.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_dashboard_hardening.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_dashboard_hardening_v2.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_external_endpoints.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_limits.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_live_layout.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_projection.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_projection_base.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/viewer_semantic_projection.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/src/execweave/workflow.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/dashboard_readability_fixture.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/fixtures/codex_multi_agent/PROVENANCE.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/fixtures/codex_multi_agent/hook-payloads.json +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/fixtures/codex_multi_agent/rollout-main.jsonl +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/fixtures/hooks/README.md +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/fixtures/hooks/claude.json +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/fixtures/hooks/codex.json +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/fixtures/hooks/cursor.json +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/fixtures/hooks/manifest.json +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/fixtures/hooks/opencode.json +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/multi_agent_run_fixture.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_acceptance_browser_diagnostics_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_acceptance_conpty_selection.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_acceptance_interrupt_guard.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_acceptance_owned_processes.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_acceptance_required_capabilities.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_acceptance_terminal_output.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_acceptance_visible_pipe.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_agent_bootstrap.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_agent_message_viewer.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_agent_node_labels.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_agent_said_panel.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_agent_topology_evidence.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_agent_trace_lifecycle.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_agent_trace_viewer_visibility.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_agy_windows_wire_acceptance.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_analysis.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_anthropic.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_agent_collaboration.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_child_panel_policy.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_child_transcript_tools.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_cursor_launch.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_field_run_regressions.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_history_replay.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_identity_topology.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_invocation_archive.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_lifecycle.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_linkage_projection.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_multi_conversation_isolation.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_official_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_role_path_fallback.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_root_child_history_v2.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_subagent_transcript_linkage.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_antigravity_trace_capability_cli.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_auto_specialized.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_auto_specialized_ollama_two_phase.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_backends.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_claude_adapter.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_claude_child_transcript_tools.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_claude_delegation.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_claude_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_claude_hook_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_claude_hook_metadata.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_claude_hook_viewer.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_claude_model_observer.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_claude_record.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_cleanup_finalizer_identity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_adapter.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_archive_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_child_panel_policy.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_conversation_isolation.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_hook_global_fail_open.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_hook_lifecycle.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_hook_partial_capture.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_interrupt_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_message_diagnostics.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_message_transport_diagnostics.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_nickname_path_fallback.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_plain_routed_agent_message.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_real_multi_agent_conversation.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_record.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_reducer_command.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_rollout_structures.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_rollout_trace.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_codex_routing_native_identity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_collector.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_command.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_condense.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_conversation_access.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_conversation_agent_focus.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_conversation_agent_isolation.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_conversation_identity_merge.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_conversation_message_observation_dedupe.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_conversation_provider_parity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_conversation_root_authority.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_correlation.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_correlation_canonical_path.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_correlation_checker.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_correlation_process_exe.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_cross_provider_agent_trace.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_cursor_adapter.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_cursor_delegation.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_cursor_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_cursor_hook_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_cursor_hook_fail_open.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_cursor_ollama_session_lifecycle.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_cursor_record.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_cursor_task_linkage.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dagre_semantic_pipeline.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_acceptance_relative_output_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_acceptance_reporting.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_camera_scheduler_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_favicon.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_gif_export_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_information_architecture.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_initial_fit_readability_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_node_drag_camera_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_readability_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_round_fold_state_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_simplification.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_dashboard_ux_and_hook_projection.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_delegation_viewer.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_evidence_grade.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_execution_viewer.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_external_endpoints_all_providers.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_filesystem_resilience.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_focus.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_fold_budget_setting.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_g5_hardening_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_g6_owned_parity_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_graph.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_graph_arrange_constraints_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_graph_clear_focus_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_graph_edge_routing_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_graph_lane_separation_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_graph_node_sizing_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_graph_ops.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_hook_fixture_corpus.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_http_proxy.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_http_proxy_request_phase.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_http_proxy_response_memory.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_http_proxy_streaming.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_i18n_readme_policy.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_inference_endpoint_identity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_inference_gateway.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_inference_gateway_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_inference_identity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_integrity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_litellm_callback.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_live.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_live_auth.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_live_dashboard.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_live_dashboard_js.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_live_delta.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_live_logs_export.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_live_theme.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_lmstudio_auto_specialized.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_local_runtime_live_capture.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_model_runtime.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_model_runtime_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_ollama_interactive_acceptance.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_ollama_interactive_terminal_semantics.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_ollama_panel_policy.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_ollama_serve_runtime_integration.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_ollama_visible_acceptance.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_openai_compatible.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_opencode_adapter.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_opencode_event_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_opencode_event_lifecycle.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_opencode_full_fidelity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_opencode_identity_unification.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_opencode_plugin.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_opencode_record.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_opencode_task_session_linkage.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_overhead_benchmark.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_per_agent_conversation_default.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_pr25_premerge_hardening_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_pr25_premerge_hardening_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_pro6_audit_regressions.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_process_tree_layout.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_provider_capability.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_provider_conversation_evidence.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_provider_conversation_history_integrity.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_provider_dashboard_contract.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_provider_lifecycle.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_provider_neutral_viewer_semantics.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_pypi_0812_regressions.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_python_native_acceptance.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_relay_prompt_live_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_release_stage_integrity_version_guard.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_rule_pack.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_schema.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_scope.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_semantic.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_shared_injected_context_scope.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_single_agent_barycenter.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_strace_backend.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_stream_assembly.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_tool_traffic_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_top_detached.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_v063_features.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_v064_live_auto_integrations.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_v064_live_evidence.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_v079_review_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_v079_review_regressions.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_v083_release_gate_p2.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_validate.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_version.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_viewer.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_viewer_agent_isolation_e2e.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_viewer_child_authority.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_viewer_content_inspector.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_viewer_limits.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_viewer_projection.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_visual_ux_readability.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_windows_codex_agy_display_repair.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tests/test_workflow.py +0 -0
- {execweave-0.8.13 → execweave-0.8.15}/tools/verify_metrics_tool.py +0 -0
{execweave-0.8.13 → execweave-0.8.15}/.github/workflows/provider-capability-stage-integrity.yml
RENAMED
|
@@ -78,6 +78,15 @@ jobs:
|
|
|
78
78
|
&& -n "$model_runtime_identity_changed" ]]; then
|
|
79
79
|
args+=(--allow-test-change "tests/test_model_runtime.py=AUD-002 only: the historical llama.cpp request ID assertion now includes the sanitized endpoint scope; the test node ID and runtime privacy contract are unchanged")
|
|
80
80
|
fi
|
|
81
|
+
threat_model_changed=""
|
|
82
|
+
if git diff --name-only "$BASELINE_REF...HEAD" \
|
|
83
|
+
| grep -qx 'tests/test_threat_model.py'; then
|
|
84
|
+
threat_model_changed=yes
|
|
85
|
+
fi
|
|
86
|
+
if [[ "$HEAD_REF" == "fix/0815-threat-model-pid-collision" \
|
|
87
|
+
&& -n "$threat_model_changed" ]]; then
|
|
88
|
+
args+=(--allow-test-change "tests/test_threat_model.py=0.8.15 RC follow-up makes the existing outliving-child assertion hermetic against ambient host PID collisions; the historical test node ID and product expectation are unchanged")
|
|
89
|
+
fi
|
|
81
90
|
if [[ "$HEAD_REF" == "fix/provider-isolation" ]]; then
|
|
82
91
|
while IFS= read -r path; do
|
|
83
92
|
case "$path" in
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: execweave
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.15
|
|
4
4
|
Summary: Turn AI-agent runtime behavior into a local interactive execution graph.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Irish-kw/ExecWeave
|
|
6
6
|
Project-URL: Repository, https://github.com/Irish-kw/ExecWeave
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execweave"
|
|
7
|
-
# ExecWeave v0.8.
|
|
8
|
-
version = "0.8.
|
|
7
|
+
# ExecWeave v0.8.15 release metadata
|
|
8
|
+
version = "0.8.15"
|
|
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,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import ctypes
|
|
3
4
|
import os
|
|
4
5
|
import subprocess
|
|
6
|
+
import sys
|
|
5
7
|
import time
|
|
6
8
|
from dataclasses import dataclass
|
|
7
9
|
from pathlib import Path
|
|
@@ -22,6 +24,10 @@ from .schema import Entity, RuntimeEvent
|
|
|
22
24
|
from .sink import JsonlSink
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
_PR_SET_CHILD_SUBREAPER = 36
|
|
28
|
+
_PR_GET_CHILD_SUBREAPER = 37
|
|
29
|
+
|
|
30
|
+
|
|
25
31
|
@dataclass(frozen=True)
|
|
26
32
|
class ProcessSnapshot:
|
|
27
33
|
pid: int
|
|
@@ -123,11 +129,13 @@ class RuntimeCollector:
|
|
|
123
129
|
self.collect_filesystem = collect_filesystem
|
|
124
130
|
self.collect_network = collect_network
|
|
125
131
|
self._seen_processes: dict[int, ProcessSnapshot] = {}
|
|
132
|
+
self._discovered_processes: dict[int, ProcessSnapshot] = {}
|
|
126
133
|
self._seen_connections: set[tuple[str, str | None, str | None, str]] = set()
|
|
127
134
|
self._network_sample_attempts = 0
|
|
128
135
|
self._network_sample_successes = 0
|
|
129
136
|
self._network_sample_errors = 0
|
|
130
137
|
self._network_access_denied = 0
|
|
138
|
+
self._collector_children_baseline: dict[int, float] | None = None
|
|
131
139
|
|
|
132
140
|
def _filesystem_excluded_roots(self) -> list[Path]:
|
|
133
141
|
"""Return ExecWeave-owned paths that must never become workload evidence.
|
|
@@ -195,10 +203,12 @@ class RuntimeCollector:
|
|
|
195
203
|
)
|
|
196
204
|
|
|
197
205
|
process: subprocess.Popen[bytes] | None = None
|
|
206
|
+
owned: list[psutil.Process] = []
|
|
198
207
|
return_code = 1
|
|
199
208
|
interrupted = False
|
|
200
209
|
collector_error_type: str | None = None
|
|
201
210
|
workload_terminated_due_to_collector_error = False
|
|
211
|
+
subreaper_previous: bool | None = None
|
|
202
212
|
post_command_probe = prepare_post_command_specialized_probe(command)
|
|
203
213
|
try:
|
|
204
214
|
if watcher is not None:
|
|
@@ -209,6 +219,12 @@ class RuntimeCollector:
|
|
|
209
219
|
command,
|
|
210
220
|
server_relay=True,
|
|
211
221
|
) as launch_environment:
|
|
222
|
+
subreaper_previous = self._enable_linux_child_subreaper()
|
|
223
|
+
self._collector_children_baseline = (
|
|
224
|
+
self._direct_child_lifetimes()
|
|
225
|
+
if subreaper_previous is not None
|
|
226
|
+
else None
|
|
227
|
+
)
|
|
212
228
|
process = subprocess.Popen(
|
|
213
229
|
launch_command,
|
|
214
230
|
cwd=str(self.watch_root),
|
|
@@ -230,8 +246,6 @@ class RuntimeCollector:
|
|
|
230
246
|
except KeyboardInterrupt:
|
|
231
247
|
interrupted = True
|
|
232
248
|
return_code = 130
|
|
233
|
-
if process is not None:
|
|
234
|
-
self._terminate_process_tree(process)
|
|
235
249
|
|
|
236
250
|
if not interrupted:
|
|
237
251
|
run_post_command_specialized_probe(
|
|
@@ -241,16 +255,26 @@ class RuntimeCollector:
|
|
|
241
255
|
return return_code
|
|
242
256
|
except BaseException as exc:
|
|
243
257
|
collector_error_type = type(exc).__name__
|
|
244
|
-
if process is not None and self._owned_live_processes(process):
|
|
245
|
-
self._terminate_process_tree(process)
|
|
246
|
-
workload_terminated_due_to_collector_error = True
|
|
247
258
|
raise
|
|
248
259
|
finally:
|
|
260
|
+
# A workload exception is a nonzero child exit, not an exception in
|
|
261
|
+
# this collector. Finalize observed descendants on every exit path,
|
|
262
|
+
# including successful launchers that leave reparented children.
|
|
263
|
+
if process is not None:
|
|
264
|
+
owned = self._owned_live_processes(process)
|
|
265
|
+
workload_terminated_due_to_collector_error = (
|
|
266
|
+
collector_error_type is not None and bool(owned)
|
|
267
|
+
)
|
|
268
|
+
self._terminate_process_tree(process, owned=owned)
|
|
269
|
+
self._restore_linux_child_subreaper(subreaper_previous)
|
|
249
270
|
if watcher is not None:
|
|
250
271
|
watcher.stop()
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
272
|
+
# A failed termination must not become a false clean result merely
|
|
273
|
+
# because the selected child has since reparented out of the tree.
|
|
274
|
+
workload_alive_after_cleanup = any(
|
|
275
|
+
self._process_is_live(candidate) for candidate in owned
|
|
276
|
+
) or bool(process is not None and self._owned_live_processes(process))
|
|
277
|
+
self._collector_children_baseline = None
|
|
254
278
|
self.sink.emit(
|
|
255
279
|
RuntimeEvent.create(
|
|
256
280
|
session_id=self.session_id,
|
|
@@ -278,6 +302,57 @@ class RuntimeCollector:
|
|
|
278
302
|
)
|
|
279
303
|
)
|
|
280
304
|
|
|
305
|
+
@staticmethod
|
|
306
|
+
def _linux_child_subreaper_state() -> bool | None:
|
|
307
|
+
if not sys.platform.startswith("linux"):
|
|
308
|
+
return None
|
|
309
|
+
try:
|
|
310
|
+
libc = ctypes.CDLL(None, use_errno=True)
|
|
311
|
+
state = ctypes.c_int()
|
|
312
|
+
if libc.prctl(_PR_GET_CHILD_SUBREAPER, ctypes.byref(state), 0, 0, 0) != 0:
|
|
313
|
+
return None
|
|
314
|
+
return bool(state.value)
|
|
315
|
+
except (AttributeError, OSError):
|
|
316
|
+
return None
|
|
317
|
+
|
|
318
|
+
@staticmethod
|
|
319
|
+
def _set_linux_child_subreaper(enabled: bool) -> bool:
|
|
320
|
+
if not sys.platform.startswith("linux"):
|
|
321
|
+
return False
|
|
322
|
+
try:
|
|
323
|
+
libc = ctypes.CDLL(None, use_errno=True)
|
|
324
|
+
return libc.prctl(_PR_SET_CHILD_SUBREAPER, int(enabled), 0, 0, 0) == 0
|
|
325
|
+
except (AttributeError, OSError):
|
|
326
|
+
return False
|
|
327
|
+
|
|
328
|
+
@classmethod
|
|
329
|
+
def _enable_linux_child_subreaper(cls) -> bool | None:
|
|
330
|
+
previous = cls._linux_child_subreaper_state()
|
|
331
|
+
if previous is None:
|
|
332
|
+
return None
|
|
333
|
+
if not previous and not cls._set_linux_child_subreaper(True):
|
|
334
|
+
return None
|
|
335
|
+
return previous
|
|
336
|
+
|
|
337
|
+
@classmethod
|
|
338
|
+
def _restore_linux_child_subreaper(cls, previous: bool | None) -> None:
|
|
339
|
+
if previous is False:
|
|
340
|
+
cls._set_linux_child_subreaper(False)
|
|
341
|
+
|
|
342
|
+
@staticmethod
|
|
343
|
+
def _direct_child_lifetimes() -> dict[int, float]:
|
|
344
|
+
try:
|
|
345
|
+
children = psutil.Process(os.getpid()).children(recursive=False)
|
|
346
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
347
|
+
return {}
|
|
348
|
+
lifetimes: dict[int, float] = {}
|
|
349
|
+
for child in children:
|
|
350
|
+
try:
|
|
351
|
+
lifetimes[child.pid] = child.create_time()
|
|
352
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
353
|
+
continue
|
|
354
|
+
return lifetimes
|
|
355
|
+
|
|
281
356
|
@staticmethod
|
|
282
357
|
def _process_is_live(proc: psutil.Process) -> bool:
|
|
283
358
|
try:
|
|
@@ -293,12 +368,13 @@ class RuntimeCollector:
|
|
|
293
368
|
def _process_for_snapshot(self, snapshot: ProcessSnapshot) -> psutil.Process | None:
|
|
294
369
|
try:
|
|
295
370
|
process = psutil.Process(snapshot.pid)
|
|
371
|
+
# Cleanup needs identity, not readable exe/name/cmdline metadata.
|
|
372
|
+
# Never treat a reused PID as the process we originally observed.
|
|
373
|
+
if process.create_time() != snapshot.create_time:
|
|
374
|
+
return None
|
|
375
|
+
return process
|
|
296
376
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
297
377
|
return None
|
|
298
|
-
current = _safe_process_snapshot(process)
|
|
299
|
-
if current is None or not self._same_process_lifetime(snapshot, current):
|
|
300
|
-
return None
|
|
301
|
-
return process
|
|
302
378
|
|
|
303
379
|
def _owned_live_processes(
|
|
304
380
|
self,
|
|
@@ -331,15 +407,39 @@ class RuntimeCollector:
|
|
|
331
407
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
332
408
|
pass
|
|
333
409
|
|
|
334
|
-
|
|
410
|
+
baseline = self._collector_children_baseline
|
|
411
|
+
if baseline is not None:
|
|
412
|
+
try:
|
|
413
|
+
collector_children = psutil.Process(os.getpid()).children(recursive=False)
|
|
414
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
415
|
+
collector_children = []
|
|
416
|
+
for candidate in collector_children:
|
|
417
|
+
try:
|
|
418
|
+
create_time = candidate.create_time()
|
|
419
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
420
|
+
continue
|
|
421
|
+
if baseline.get(candidate.pid) == create_time:
|
|
422
|
+
continue
|
|
423
|
+
add_with_descendants(candidate)
|
|
424
|
+
|
|
425
|
+
snapshots = {**self._seen_processes, **self._discovered_processes}
|
|
426
|
+
for snapshot in snapshots.values():
|
|
335
427
|
tracked = self._process_for_snapshot(snapshot)
|
|
336
428
|
if tracked is not None:
|
|
337
429
|
add_with_descendants(tracked)
|
|
338
430
|
|
|
339
431
|
return list(owned.values())
|
|
340
432
|
|
|
341
|
-
def _terminate_process_tree(
|
|
342
|
-
|
|
433
|
+
def _terminate_process_tree(
|
|
434
|
+
self,
|
|
435
|
+
process: subprocess.Popen[bytes],
|
|
436
|
+
*,
|
|
437
|
+
owned: list[psutil.Process] | None = None,
|
|
438
|
+
) -> None:
|
|
439
|
+
# A caller's selection must survive reparenting between discovery and
|
|
440
|
+
# termination. Re-querying here can lose children we just proved owned.
|
|
441
|
+
if owned is None:
|
|
442
|
+
owned = self._owned_live_processes(process)
|
|
343
443
|
# Terminate descendants/tracked orphans before the launcher/root when it is
|
|
344
444
|
# still alive. Holding psutil handles also keeps cleanup robust if a parent
|
|
345
445
|
# exits while the loop is running.
|
|
@@ -392,6 +492,11 @@ class RuntimeCollector:
|
|
|
392
492
|
current[snapshot.pid] = snapshot
|
|
393
493
|
process_objects[snapshot.pid] = proc
|
|
394
494
|
|
|
495
|
+
# Preserve the complete discovered tree before any event/network callback
|
|
496
|
+
# can raise. _seen_processes only contains successfully registered starts;
|
|
497
|
+
# a root callback can fail after its children have already reparented.
|
|
498
|
+
# Retain only the latest sample here, not an unbounded lifetime history.
|
|
499
|
+
self._discovered_processes = current
|
|
395
500
|
for snapshot in current.values():
|
|
396
501
|
previous = self._seen_processes.get(snapshot.pid)
|
|
397
502
|
if previous is None or not self._same_process_lifetime(previous, snapshot):
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
LIVE_STYLE = r""":root{color-scheme:dark;--bg:#0a0f16;--panel:#101722;--panel2:#151f2c;--panel3:#0d141e;--text:#edf2f7;--muted:#8fa0b5;--border:#263446;--border-strong:#34485e;--edge:#6f8298;--causal:#65c79b;--noncausal:#e5aa61;--inferred:#b18cff;--selected:#75b8ff;--latest:#77d7ff;--danger:#ff7b86;--node-agent:#1e4b7a;--node-process:#1d4e5f;--node-file:#1f5438;--node-network:#643d1f;--node-tool:#4f2f75;--node-model:#662952;--node-other:#334255;--shadow:0 14px 40px rgba(0,0,0,.18)}
|
|
4
4
|
:root[data-theme="light"]{color-scheme:light;--bg:#eef3f8;--panel:#ffffff;--panel2:#f6f9fc;--panel3:#f9fbfd;--text:#172033;--muted:#66758a;--border:#d5dee8;--border-strong:#b9c7d7;--edge:#738195;--causal:#16855a;--noncausal:#b56a16;--inferred:#7b4dd8;--selected:#2563eb;--latest:#0f8fc5;--danger:#c2414f;--node-agent:#2d6391;--node-process:#2b697b;--node-file:#2c6e49;--node-network:#865328;--node-tool:#673d8e;--node-model:#7f3465;--node-other:#495c72;--shadow:0 14px 35px rgba(30,41,59,.08)}
|
|
5
5
|
*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden;background:var(--bg);color:var(--text);font:14px/1.45 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
|
6
|
-
button,input{font:inherit}button{color:inherit}button:focus-visible,input:focus-visible{outline:2px solid var(--selected);outline-offset:2px}
|
|
6
|
+
button,input,select{font:inherit}button{color:inherit}button:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--selected);outline-offset:2px}
|
|
7
7
|
#app{display:grid;grid-template-columns:minmax(0,1fr) 340px;grid-template-rows:64px minmax(0,1fr) 230px;width:100%;height:100%;gap:0}
|
|
8
8
|
header{grid-column:1/3;display:flex;align-items:center;gap:12px;padding:0 16px;background:color-mix(in srgb,var(--panel) 95%,transparent);border-bottom:1px solid var(--border);box-shadow:0 1px 0 rgba(255,255,255,.02);z-index:10;min-width:0}
|
|
9
9
|
.brand{display:flex;align-items:center;gap:10px;min-width:max-content}.brand-mark{width:28px;height:28px;border-radius:8px;display:grid;place-items:center;background:linear-gradient(145deg,var(--panel2),var(--panel));border:1px solid var(--border-strong);font-weight:800;letter-spacing:-.06em}.brand-copy{display:flex;flex-direction:column;line-height:1.1}.brand-copy strong{font-size:14px}.brand-copy span{font-size:10px;color:var(--muted);letter-spacing:.08em;text-transform:uppercase;margin-top:3px}
|
|
@@ -14,6 +14,7 @@ header{grid-column:1/3;display:flex;align-items:center;gap:12px;padding:0 16px;b
|
|
|
14
14
|
.icon-btn{height:34px;min-width:34px;padding:0 10px;border:1px solid var(--border);border-radius:8px;background:var(--panel2);cursor:pointer;transition:border-color .15s ease,background .15s ease}.icon-btn:hover{border-color:var(--border-strong);background:color-mix(in srgb,var(--panel2) 76%,var(--selected) 8%)}
|
|
15
15
|
#graph-panel{grid-column:1;grid-row:2;min-width:0;min-height:0;display:grid;grid-template-rows:46px minmax(0,1fr);border-right:1px solid var(--border);background:var(--panel3)}
|
|
16
16
|
.panel-bar{display:flex;align-items:center;gap:10px;padding:0 12px;border-bottom:1px solid var(--border);background:var(--panel);min-width:0}.panel-title{font-size:12px;font-weight:750;text-transform:uppercase;letter-spacing:.08em}.panel-subtitle{color:var(--muted);font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.panel-spacer{flex:1}
|
|
17
|
+
.graph-file-filter{display:flex;align-items:center;gap:6px;color:var(--muted);font-size:11px;white-space:nowrap}.graph-file-filter select{height:30px;padding:0 8px;border:1px solid var(--border);border-radius:8px;background:var(--panel2);color:var(--text);cursor:pointer}.graph-file-filter select:hover{border-color:var(--border-strong)}
|
|
17
18
|
.segmented{display:flex;align-items:center;padding:2px;border:1px solid var(--border);border-radius:9px;background:var(--panel2);gap:2px}.segmented button{height:28px;padding:0 10px;border:0;border-radius:7px;background:transparent;color:var(--muted);cursor:pointer;font-size:12px}.segmented button:hover{color:var(--text)}.segmented button.active{background:var(--panel);color:var(--text);box-shadow:inset 0 0 0 1px var(--border-strong)}
|
|
18
19
|
.graph-actions{display:flex;align-items:center;gap:6px}.graph-actions button{height:30px;min-width:30px;padding:0 9px;border:1px solid var(--border);border-radius:8px;background:var(--panel2);cursor:pointer;color:var(--muted)}.graph-actions button:hover{color:var(--text);border-color:var(--border-strong)}
|
|
19
20
|
#wrap{position:relative;overflow:hidden;min-width:0;min-height:0;background-image:radial-gradient(circle at 1px 1px,color-mix(in srgb,var(--border) 52%,transparent) 1px,transparent 0);background-size:22px 22px}svg{width:100%;height:100%;display:block;cursor:grab;user-select:none;touch-action:none}svg.panning{cursor:grabbing}
|
|
@@ -99,6 +99,24 @@ function execweaveDashboardGraph(data){
|
|
|
99
99
|
if(!outgoing.has(edge.source))outgoing.set(edge.source,[]);
|
|
100
100
|
incoming.get(edge.target).push(edge);outgoing.get(edge.source).push(edge);
|
|
101
101
|
}
|
|
102
|
+
const fileDisplayMode=String(globalThis.__execweaveFileDisplayMode||'changed');
|
|
103
|
+
const fileTypes=new Set(['file','directory']);
|
|
104
|
+
const fileMutationEvents=new Set(['filesystem.created','filesystem.modified','filesystem.moved','filesystem.deleted']);
|
|
105
|
+
const fileMutationRelation=relation=>/(^|_)(WROTE|WRITE|WRITES|CREATE|CREATED|UPDATE|UPDATED|EDIT|EDITED|DELETE|DELETED|MOVE|MOVED|RENAME|RENAMED|PATCH|REPLACE|REPLACED|SAVE|SAVED|MODIFY|MODIFIED)($|_)/.test(String(relation||'').toUpperCase());
|
|
106
|
+
const fileReadRelation=relation=>/(^|_)(READ|OPEN|SCAN|LIST|STAT|INSPECT|OBSERVE|OBSERVED)($|_)/.test(String(relation||'').toUpperCase());
|
|
107
|
+
const fileActivity=node=>{
|
|
108
|
+
if(!fileTypes.has(String(node?.type||'')))return'other';
|
|
109
|
+
const eventTypes=Array.isArray(node?.event_types)?node.event_types:[];
|
|
110
|
+
const related=[...(incoming.get(node.id)||[]),...(outgoing.get(node.id)||[])];
|
|
111
|
+
const relatedEventTypes=related.flatMap(edge=>Array.isArray(edge?.event_types)?edge.event_types:[]);
|
|
112
|
+
const allEventTypes=[...eventTypes,...relatedEventTypes].map(String);
|
|
113
|
+
if(allEventTypes.some(value=>fileMutationEvents.has(value)))return'changed';
|
|
114
|
+
if(related.some(edge=>fileMutationRelation(edge?.relation)))return'changed';
|
|
115
|
+
const observedEvents=allEventTypes.some(value=>value.startsWith('filesystem.'));
|
|
116
|
+
if(observedEvents||related.some(edge=>fileReadRelation(edge?.relation)))return'observed';
|
|
117
|
+
return'unknown';
|
|
118
|
+
};
|
|
119
|
+
const fileVisible=node=>{const activity=fileActivity(node);if(activity==='other')return true;if(fileDisplayMode==='hide')return false;if(fileDisplayMode==='all')return true;return activity!=='observed'};
|
|
102
120
|
const provider=node=>String(node?.attributes?.provider||'unknown').toLowerCase();
|
|
103
121
|
const toolName=node=>String(node?.attributes?.tool_name||node?.attributes?.native_name||node?.name||'tool');
|
|
104
122
|
const toolKey=node=>`${provider(node)}\u0000${toolName(node).trim().toLowerCase()}`;
|
|
@@ -205,7 +223,10 @@ function execweaveDashboardGraph(data){
|
|
|
205
223
|
};
|
|
206
224
|
for(const group of groups.values())group.occurrences.sort(occurrenceOrder);
|
|
207
225
|
for(const list of toolOccurrences.values())list.sort(occurrenceOrder);
|
|
208
|
-
|
|
226
|
+
const candidateNodes=allNodes.filter(node=>node&&node.id&&!hiddenIds.has(node.id)).filter(node=>node.type!=='tool'||toolCanonicalId.get(node.id)===node.id);
|
|
227
|
+
const hiddenFileObservationCount=candidateNodes.filter(node=>fileTypes.has(String(node?.type||''))&&!fileVisible(node)).length;
|
|
228
|
+
let visibleNodes=candidateNodes.filter(node=>fileVisible(node)).map(node=>{
|
|
229
|
+
if(fileTypes.has(String(node?.type||'')))return{...node,attributes:{...attrsOf(node),viewer_file_activity:fileActivity(node)}};
|
|
209
230
|
if(node.type!=='tool')return node;
|
|
210
231
|
const occurrences=toolOccurrences.get(node.id)||[],members=toolMembersByRepresentative.get(node.id)||[node];
|
|
211
232
|
if(!occurrences.length&&members.length===1)return node;
|
|
@@ -214,7 +235,8 @@ function execweaveDashboardGraph(data){
|
|
|
214
235
|
});
|
|
215
236
|
|
|
216
237
|
const canonicalTypes=new Set(['process','file']);
|
|
217
|
-
const foldableTypes=new Set(['process','file','network_endpoint']);
|
|
238
|
+
const foldableTypes=new Set(['process','file','directory','network_endpoint']);
|
|
239
|
+
const foldType=node=>{const type=String(node?.type||'');return type==='file'||type==='directory'?'filesystem':type};
|
|
218
240
|
const declaredBudget=Number(globalThis.__execweaveFoldBudget);
|
|
219
241
|
const FOLD_BUDGET=Number.isFinite(declaredBudget)&&declaredBudget>=1?Math.floor(declaredBudget):12;
|
|
220
242
|
const canonicalGroups=new Map();
|
|
@@ -246,19 +268,21 @@ function execweaveDashboardGraph(data){
|
|
|
246
268
|
|
|
247
269
|
let foldedNodeCount=0;
|
|
248
270
|
const byType=new Map();
|
|
249
|
-
for(const node of nodes){const
|
|
271
|
+
for(const node of nodes){const rawType=String(node?.type||'');if(!foldableTypes.has(rawType))continue;const type=foldType(node);if(!byType.has(type))byType.set(type,[]);byType.get(type).push(node)}
|
|
250
272
|
const folds=[];
|
|
251
273
|
for(const[type,members]of byType){
|
|
252
274
|
if(members.length<=FOLD_BUDGET)continue;
|
|
253
275
|
const recency=node=>String(node?.last_seen||node?.first_seen||'');
|
|
254
276
|
const ordered=[...members].sort((a,b)=>recency(b).localeCompare(recency(a)));
|
|
255
277
|
const older=ordered.slice(FOLD_BUDGET);if(!older.length)continue;
|
|
256
|
-
const
|
|
278
|
+
const removeIds=new Set(older.map(node=>node.id));
|
|
257
279
|
const foldId=`viewer:folded:${type}`;
|
|
258
280
|
for(const node of older)canonicalId.set(node.id,foldId);
|
|
259
281
|
foldedNodeCount+=older.length;
|
|
260
|
-
|
|
261
|
-
|
|
282
|
+
const foldedType=type==='filesystem'?'file_cluster':type,foldedName=type==='filesystem'?`${older.length} earlier files / directories`:`${older.length} earlier ${type.replace(/_/g,' ')}${older.length===1?'':'s'}`;
|
|
283
|
+
const activities=[...new Set(older.map(node=>node?.attributes?.viewer_file_activity).filter(Boolean))];
|
|
284
|
+
folds.push({id:foldId,type:foldedType,name:foldedName,first_seen:older.reduce((value,node)=>earlier(value,node.first_seen),null),last_seen:older.reduce((value,node)=>later(value,node.last_seen),null),attributes:{viewer_folded:true,viewer_folded_type:type,viewer_folded_count:older.length,viewer_file_activity:type==='filesystem'?(activities.length===1?activities[0]:'mixed'):undefined,viewer_folded_members:older.map(node=>({id:node.id,type:node.type,name:node.name||node.id,activity:node?.attributes?.viewer_file_activity||null,first_seen:node.first_seen||null,last_seen:node.last_seen||null}))}});
|
|
285
|
+
for(let index=nodes.length-1;index>=0;index--)if(removeIds.has(nodes[index].id))nodes.splice(index,1);
|
|
262
286
|
}
|
|
263
287
|
nodes.push(...folds);
|
|
264
288
|
|
|
@@ -280,7 +304,7 @@ function execweaveDashboardGraph(data){
|
|
|
280
304
|
const incident=new Set();for(const edge of edges){incident.add(edge.source);incident.add(edge.target)}
|
|
281
305
|
visibleNodes=nodes.filter(node=>node.type!=='tool'||incident.has(node.id));
|
|
282
306
|
nodeIds=new Set(visibleNodes.map(node=>node.id));edges=edges.filter(edge=>nodeIds.has(edge.source)&&nodeIds.has(edge.target));
|
|
283
|
-
return{...data,nodes:visibleNodes,edges,node_count:visibleNodes.length,edge_count:edges.length,dashboard_projection:{hidden_detail_node_count:hiddenDetailIds.size,hidden_internal_staging_node_count:internalStagingIds.size,canonicalized_process_occurrence_count:canonicalizedProcessOccurrenceCount,canonicalized_tool_entity_count:[...toolMembersByRepresentative.values()].reduce((sum,members)=>sum+Math.max(0,members.length-1),0),folded_node_count:foldedNodeCount,collapsed_tool_call_count:invocationMap.size,raw_tool_call_detail_count:allNodes.filter(node=>['tool_call','tool_call_observation'].includes(String(node?.type||''))).length}};
|
|
307
|
+
return{...data,nodes:visibleNodes,edges,node_count:visibleNodes.length,edge_count:edges.length,dashboard_projection:{hidden_detail_node_count:hiddenDetailIds.size,hidden_internal_staging_node_count:internalStagingIds.size,canonicalized_process_occurrence_count:canonicalizedProcessOccurrenceCount,canonicalized_tool_entity_count:[...toolMembersByRepresentative.values()].reduce((sum,members)=>sum+Math.max(0,members.length-1),0),folded_node_count:foldedNodeCount,collapsed_tool_call_count:invocationMap.size,raw_tool_call_detail_count:allNodes.filter(node=>['tool_call','tool_call_observation'].includes(String(node?.type||''))).length,file_display_mode:fileDisplayMode,hidden_file_observation_node_count:hiddenFileObservationCount}};
|
|
284
308
|
}
|
|
285
309
|
""".strip()
|
|
286
310
|
|
|
@@ -310,7 +334,7 @@ _LIVE_SET_SNAPSHOT_CLEAN = "function setSnapshot(data){const signature=`${data.n
|
|
|
310
334
|
_LIVE_APPLY_DELTA_CLEAN = "function applyDelta(update){if(update.live_payload_compact){updateStats(update);enterProtectiveMode(update);return}const rawNodes=new Map((graph.nodes||[]).map(n=>[n.id,n])),rawEdges=new Map((graph.edges||[]).map(e=>[edgeId(e),e]));for(const node of [...(update.nodes_added||[]),...(update.nodes_updated||[])])if(node?.id)rawNodes.set(node.id,node);for(const edge of [...(update.edges_added||[]),...(update.edges_updated||[])])rawEdges.set(edgeId(edge),edge);graph={...graph,event_count:update.event_count,node_count:update.node_count,edge_count:update.edge_count,nodes:[...rawNodes.values()],edges:[...rawEdges.values()]};const display=execweaveDashboardGraph(graph);updateStats({...update,node_count:display.node_count,edge_count:display.edge_count});if(!withinRenderBudget(display)){enterProtectiveMode(display);return}if(protectedMode)leaveProtectiveMode();nodeById=new Map((display.nodes||[]).map(n=>[n.id,n]));edgeById=new Map((display.edges||[]).map(e=>[edgeId(e),e]));rebuildAdjacency();renderSnapshot();seedActivities();const sortedEdges=[...edgeById.values()].sort((a,b)=>String(a.last_seen||'').localeCompare(String(b.last_seen||''))),sortedNodes=[...nodeById.values()].sort((a,b)=>String(a.last_seen||'').localeCompare(String(b.last_seen||''))),lastEdge=sortedEdges.length?sortedEdges[sortedEdges.length-1]:null,lastNode=sortedNodes.length?sortedNodes[sortedNodes.length-1]:null;markLatest(lastEdge?.target||lastNode?.id||null,lastEdge?edgeId(lastEdge):null);scheduleCamera(false);if(!hasFitted&&positions.size){fit(false);hasFitted=true}}"
|
|
311
335
|
|
|
312
336
|
_LIVE_CORE_EXPORT = "window.__execweaveCore={getActivities:()=>activities.slice(),getGraph:()=>graph,getPositions:()=>new Map(positions),selectEdge,selectNode,focusNode,markLatest,setCameraMode};"
|
|
313
|
-
_LIVE_CORE_EXPORT_CLEAN = "window.__execweaveCore={getActivities:()=>activities.slice(),getGraph:()=>graph,getDisplayGraph:()=>({...graph,nodes:[...nodeById.values()],edges:[...edgeById.values()],node_count:nodeById.size,edge_count:edgeById.size}),getPositions:()=>new Map(positions),selectEdge,selectNode,focusNode,markLatest,setCameraMode};"
|
|
337
|
+
_LIVE_CORE_EXPORT_CLEAN = "const execweaveFileGraphFilter=document.getElementById('file-graph-filter');globalThis.__execweaveFileDisplayMode=execweaveFileGraphFilter?.value||'changed';if(execweaveFileGraphFilter)execweaveFileGraphFilter.addEventListener('change',()=>{globalThis.__execweaveFileDisplayMode=execweaveFileGraphFilter.value||'changed';setSnapshot(graph)});window.__execweaveCore={getActivities:()=>activities.slice(),getGraph:()=>graph,getDisplayGraph:()=>({...graph,nodes:[...nodeById.values()],edges:[...edgeById.values()],node_count:nodeById.size,edge_count:edgeById.size}),getPositions:()=>new Map(positions),selectEdge,selectNode,focusNode,markLatest,setCameraMode};"
|
|
314
338
|
|
|
315
339
|
_LIVE_NOOP_STATS = "else if(data.kind==='noop'){liveSequence=Number(data.sequence)||liveSequence;updateStats(data);}"
|
|
316
340
|
_LIVE_NOOP_STATS_CLEAN = "else if(data.kind==='noop'){liveSequence=Number(data.sequence)||liveSequence;updateStats(protectedMode?data:{...data,node_count:nodeById.size,edge_count:edgeById.size});}"
|
|
@@ -338,6 +362,11 @@ def inject_standalone_dashboard_clean(html: str) -> str:
|
|
|
338
362
|
|
|
339
363
|
def inject_live_dashboard_clean(html: str) -> str:
|
|
340
364
|
"""Simplify only the browser canvas; the live JSON protocol remains unchanged."""
|
|
365
|
+
control_seam = '<div class="panel-spacer"></div>\n <div class="segmented" aria-label="Camera mode">'
|
|
366
|
+
control = '<div class="panel-spacer"></div>\n <label class="graph-file-filter" title="Choose which file and directory nodes appear in the graph"><span>Files</span><select id="file-graph-filter"><option value="changed" selected>Changed only</option><option value="all">All observed</option><option value="hide">Hidden</option></select></label>\n <div class="segmented" aria-label="Camera mode">'
|
|
367
|
+
if html.count(control_seam) != 1:
|
|
368
|
+
raise RuntimeError("file graph filter control seam changed")
|
|
369
|
+
html = html.replace(control_seam, control, 1)
|
|
341
370
|
marker = "function renderSnapshot(){"
|
|
342
371
|
if "function execweaveDashboardGraph(data){" not in html and marker in html:
|
|
343
372
|
html = html.replace(marker, _DASHBOARD_JS + "\n" + marker, 1)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import psutil
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from execweave.collector import RuntimeCollector
|
|
13
|
+
from execweave.sink import JsonlSink
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
_SLEEP = "import time; time.sleep(60)"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _live_pid(pid: int) -> bool:
|
|
20
|
+
try:
|
|
21
|
+
process = psutil.Process(pid)
|
|
22
|
+
return process.is_running() and process.status() != psutil.STATUS_ZOMBIE
|
|
23
|
+
except (psutil.NoSuchProcess, psutil.ZombieProcess):
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _wait_for(predicate, *, timeout: float = 5.0) -> None:
|
|
28
|
+
deadline = time.monotonic() + timeout
|
|
29
|
+
while time.monotonic() < deadline:
|
|
30
|
+
if predicate():
|
|
31
|
+
return
|
|
32
|
+
time.sleep(0.01)
|
|
33
|
+
raise AssertionError("fast-reparent fixture did not reach its handshake")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _collector(tmp_path: Path) -> RuntimeCollector:
|
|
37
|
+
return RuntimeCollector(
|
|
38
|
+
session_id="d003-fast-reparent",
|
|
39
|
+
sink=JsonlSink(tmp_path / "events.jsonl"),
|
|
40
|
+
watch_root=tmp_path,
|
|
41
|
+
poll_interval=0.02,
|
|
42
|
+
collect_filesystem=False,
|
|
43
|
+
collect_network=False,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _detaching_command(pid_file: Path) -> list[str]:
|
|
48
|
+
code = (
|
|
49
|
+
"import subprocess,sys; "
|
|
50
|
+
"child=subprocess.Popen([sys.executable,'-c','import time; time.sleep(60)'], "
|
|
51
|
+
"start_new_session=True); "
|
|
52
|
+
"open(sys.argv[1],'w',encoding='utf-8').write(str(child.pid))"
|
|
53
|
+
)
|
|
54
|
+
return [sys.executable, "-c", code, str(pid_file)]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _miss_short_lived_tree(
|
|
58
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
59
|
+
collector: RuntimeCollector,
|
|
60
|
+
pid_file: Path,
|
|
61
|
+
) -> None:
|
|
62
|
+
def miss_tree(root: psutil.Process) -> None:
|
|
63
|
+
_wait_for(pid_file.exists)
|
|
64
|
+
# Deliberately simulate the polling blind spot from issue #9: the launcher
|
|
65
|
+
# exits after spawning a detached child before a descendant sample lands.
|
|
66
|
+
time.sleep(0.05)
|
|
67
|
+
|
|
68
|
+
monkeypatch.setattr(collector, "_sample_process_tree", miss_tree)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _non_linux_guard() -> bool:
|
|
72
|
+
if sys.platform.startswith("linux"):
|
|
73
|
+
return False
|
|
74
|
+
# Fast detached-child recovery in this regression is a Linux subreaper path.
|
|
75
|
+
# On other platforms the implementation must remain disabled rather than
|
|
76
|
+
# silently attempting Linux prctl semantics.
|
|
77
|
+
assert RuntimeCollector._linux_child_subreaper_state() is None
|
|
78
|
+
assert RuntimeCollector._set_linux_child_subreaper(True) is False
|
|
79
|
+
return True
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_run_cleans_detached_child_even_when_polling_never_observed_it(
|
|
83
|
+
tmp_path: Path,
|
|
84
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
85
|
+
) -> None:
|
|
86
|
+
if _non_linux_guard():
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
collector = _collector(tmp_path)
|
|
90
|
+
pid_file = tmp_path / "detached.pid"
|
|
91
|
+
sentinel = subprocess.Popen([sys.executable, "-c", _SLEEP])
|
|
92
|
+
try:
|
|
93
|
+
_miss_short_lived_tree(monkeypatch, collector, pid_file)
|
|
94
|
+
assert collector.run(_detaching_command(pid_file)) == 0
|
|
95
|
+
child_pid = int(pid_file.read_text(encoding="utf-8"))
|
|
96
|
+
_wait_for(lambda: not _live_pid(child_pid))
|
|
97
|
+
assert _live_pid(sentinel.pid), "cleanup killed a pre-existing unrelated child"
|
|
98
|
+
|
|
99
|
+
finished = json.loads((tmp_path / "events.jsonl").read_text().splitlines()[-1])
|
|
100
|
+
assert finished["event_type"] == "session.finished"
|
|
101
|
+
assert finished["attributes"]["workload_alive_after_cleanup"] is False
|
|
102
|
+
finally:
|
|
103
|
+
if _live_pid(sentinel.pid):
|
|
104
|
+
sentinel.kill()
|
|
105
|
+
sentinel.wait(timeout=5)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_fast_detached_survivor_is_reported_if_cleanup_does_not_terminate_it(
|
|
109
|
+
tmp_path: Path,
|
|
110
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
111
|
+
) -> None:
|
|
112
|
+
if _non_linux_guard():
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
collector = _collector(tmp_path)
|
|
116
|
+
pid_file = tmp_path / "detached.pid"
|
|
117
|
+
_miss_short_lived_tree(monkeypatch, collector, pid_file)
|
|
118
|
+
monkeypatch.setattr(collector, "_terminate_process_tree", lambda process, *, owned=None: None)
|
|
119
|
+
|
|
120
|
+
child_pid: int | None = None
|
|
121
|
+
try:
|
|
122
|
+
assert collector.run(_detaching_command(pid_file)) == 0
|
|
123
|
+
child_pid = int(pid_file.read_text(encoding="utf-8"))
|
|
124
|
+
assert _live_pid(child_pid)
|
|
125
|
+
finished = json.loads((tmp_path / "events.jsonl").read_text().splitlines()[-1])
|
|
126
|
+
assert finished["attributes"]["workload_alive_after_cleanup"] is True
|
|
127
|
+
finally:
|
|
128
|
+
if child_pid is not None and _live_pid(child_pid):
|
|
129
|
+
process = psutil.Process(child_pid)
|
|
130
|
+
process.kill()
|
|
131
|
+
try:
|
|
132
|
+
process.wait(timeout=5)
|
|
133
|
+
except psutil.TimeoutExpired:
|
|
134
|
+
pass
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from execweave.dashboard_shell import DASHBOARD_HTML
|
|
4
|
+
from execweave.viewer_dashboard_clean import _DASHBOARD_JS
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_dashboard_exposes_file_activity_filter_with_changed_default() -> None:
|
|
8
|
+
assert 'id="file-graph-filter"' in DASHBOARD_HTML
|
|
9
|
+
assert '<option value="changed" selected>Changed only</option>' in DASHBOARD_HTML
|
|
10
|
+
assert '<option value="all">All observed</option>' in DASHBOARD_HTML
|
|
11
|
+
assert '<option value="hide">Hidden</option>' in DASHBOARD_HTML
|
|
12
|
+
assert "__execweaveFileDisplayMode" in DASHBOARD_HTML
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_dashboard_file_projection_distinguishes_changes_from_scans() -> None:
|
|
16
|
+
assert "fileMutationEvents=new Set(['filesystem.created','filesystem.modified','filesystem.moved','filesystem.deleted'])" in _DASHBOARD_JS
|
|
17
|
+
assert "fileDisplayMode==='hide'" in _DASHBOARD_JS
|
|
18
|
+
assert "fileDisplayMode==='all'" in _DASHBOARD_JS
|
|
19
|
+
assert "relatedEventTypes=related.flatMap" in _DASHBOARD_JS
|
|
20
|
+
assert "allEventTypes=[...eventTypes,...relatedEventTypes]" in _DASHBOARD_JS
|
|
21
|
+
assert "return activity!=='observed'" in _DASHBOARD_JS
|
|
22
|
+
assert "return'unknown'" in _DASHBOARD_JS
|
|
23
|
+
assert "hidden_file_observation_node_count" in _DASHBOARD_JS
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_file_and_directory_nodes_share_one_fold_group() -> None:
|
|
27
|
+
assert "type==='file'||type==='directory'?'filesystem':type" in _DASHBOARD_JS
|
|
28
|
+
assert "viewer:folded:${type}" in _DASHBOARD_JS
|
|
29
|
+
assert "earlier files / directories" in _DASHBOARD_JS
|
|
30
|
+
assert "foldedType=type==='filesystem'?'file_cluster':type" in _DASHBOARD_JS
|