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.
Files changed (231) hide show
  1. {execweave-0.8.34 → execweave-0.8.35}/PKG-INFO +18 -3
  2. {execweave-0.8.34 → execweave-0.8.35}/README.md +17 -2
  3. {execweave-0.8.34 → execweave-0.8.35}/pyproject.toml +2 -2
  4. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/__init__.py +1 -1
  5. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/_conversation_records_core.py +3 -6
  6. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/_http_proxy_base.py +26 -0
  7. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/_http_proxy_bounded.py +71 -30
  8. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/_http_proxy_stage.py +3 -0
  9. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/agent_bootstrap.py +74 -11
  10. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/anthropic.py +5 -6
  11. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/anthropic_cli.py +3 -0
  12. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_adapter_base.py +5 -6
  13. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_hook_cli.py +9 -2
  14. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_adapter.py +5 -6
  15. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_hook_cli.py +6 -1
  16. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cli.py +9 -8
  17. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_adapter.py +5 -6
  18. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_hook_cli.py +5 -1
  19. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_hook_entry.py +5 -4
  20. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/collector.py +101 -20
  21. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/content_store.py +16 -10
  22. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/correlation.py +11 -19
  23. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_adapter.py +5 -6
  24. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_hook_cli.py +6 -1
  25. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/entry.py +23 -6
  26. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/fidelity.py +4 -6
  27. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/filesystem.py +82 -2
  28. execweave-0.8.35/src/execweave/finalization.py +92 -0
  29. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/base.py +7 -5
  30. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/graph.py +10 -6
  31. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/graph_ops.py +24 -10
  32. execweave-0.8.35/src/execweave/hook_command.py +32 -0
  33. execweave-0.8.35/src/execweave/hook_delivery.py +32 -0
  34. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/inference_gateway.py +5 -6
  35. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/inference_gateway_cli.py +3 -0
  36. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/integrity.py +25 -23
  37. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/integrity_cli.py +12 -1
  38. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/litellm_callback.py +4 -0
  39. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live.py +8 -8
  40. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_core.py +133 -29
  41. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_extra_style.py +1 -1
  42. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_script_a.py +7 -5
  43. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/model_runtime.py +5 -6
  44. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/model_runtime_cli.py +3 -0
  45. execweave-0.8.35/src/execweave/observation.py +53 -0
  46. execweave-0.8.35/src/execweave/observation_demo.py +69 -0
  47. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/openai_compatible.py +5 -6
  48. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/openai_compatible_cli.py +3 -0
  49. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_adapter.py +5 -6
  50. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_hook_cli.py +8 -2
  51. execweave-0.8.35/src/execweave/privacy.py +84 -0
  52. execweave-0.8.35/src/execweave/private_io.py +465 -0
  53. execweave-0.8.35/src/execweave/risk.py +106 -0
  54. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/run_assessment.py +16 -4
  55. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/semantic.py +16 -16
  56. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/session_summary.py +38 -0
  57. execweave-0.8.35/src/execweave/sink.py +53 -0
  58. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/task_validation.py +5 -5
  59. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/top.py +30 -20
  60. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/top_cli.py +3 -0
  61. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer.py +3 -2
  62. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel.py +1 -0
  63. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_flow_canvas.py +525 -52
  64. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_flow_layout.py +8 -55
  65. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_projection.py +4 -3
  66. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_projection_base.py +3 -2
  67. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_run_assessment.py +5 -0
  68. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_semantic_projection.py +152 -4
  69. execweave-0.8.35/src/execweave/viewer_session_flow.py +491 -0
  70. execweave-0.8.34/src/execweave/finalization.py +0 -58
  71. execweave-0.8.34/src/execweave/sink.py +0 -33
  72. execweave-0.8.34/src/execweave/viewer_session_flow.py +0 -166
  73. {execweave-0.8.34 → execweave-0.8.35}/.gitignore +0 -0
  74. {execweave-0.8.34 → execweave-0.8.35}/LICENSE +0 -0
  75. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/__main__.py +0 -0
  76. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/_dashboard_shell_base.py +0 -0
  77. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/acceptance_output.py +0 -0
  78. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/agent_topology.py +0 -0
  79. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/agent_trace.py +0 -0
  80. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/agy_preview_sanitize.py +0 -0
  81. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/analysis.py +0 -0
  82. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/anthropic_full_fidelity.py +0 -0
  83. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_adapter.py +0 -0
  84. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_full_fidelity.py +0 -0
  85. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_full_fidelity_base.py +0 -0
  86. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_full_fidelity_collaboration_base.py +0 -0
  87. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_record.py +0 -0
  88. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_subagent_linkage.py +0 -0
  89. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/antigravity_trace_capability.py +0 -0
  90. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/auto_specialized.py +0 -0
  91. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/backends.py +0 -0
  92. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/benchmark.py +0 -0
  93. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_child_transcript.py +0 -0
  94. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_delegation.py +0 -0
  95. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_full_fidelity.py +0 -0
  96. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_hook_contract.py +0 -0
  97. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_model_observer.py +0 -0
  98. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/claude_record.py +0 -0
  99. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_conversation.py +0 -0
  100. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_full_fidelity.py +0 -0
  101. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_hook_lifecycle.py +0 -0
  102. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_message_diagnostics.py +0 -0
  103. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_message_transport_diagnostics.py +0 -0
  104. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_record.py +0 -0
  105. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_rollout_structures.py +0 -0
  106. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_rollout_trace.py +0 -0
  107. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/codex_rollout_trace_base.py +0 -0
  108. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/command.py +0 -0
  109. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/content_evidence.py +0 -0
  110. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/content_integrity.py +0 -0
  111. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/controlled_checks.py +0 -0
  112. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_archive.py +0 -0
  113. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_message_identity.py +0 -0
  114. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview.py +0 -0
  115. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_antigravity.py +0 -0
  116. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_claude.py +0 -0
  117. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_codex.py +0 -0
  118. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_common.py +0 -0
  119. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_generic.py +0 -0
  120. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_lines.py +0 -0
  121. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_preview_transcript.py +0 -0
  122. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_records.py +0 -0
  123. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_records_antigravity.py +0 -0
  124. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_records_codex.py +0 -0
  125. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_records_common.py +0 -0
  126. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/conversation_records_ollama.py +0 -0
  127. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_delegation.py +0 -0
  128. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_delegation_base.py +0 -0
  129. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_full_fidelity.py +0 -0
  130. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_hook_contract.py +0 -0
  131. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_lifecycle.py +0 -0
  132. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/cursor_record.py +0 -0
  133. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/dashboard_shell.py +0 -0
  134. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/delivery_status.py +0 -0
  135. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/evidence_availability.py +0 -0
  136. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/evidence_grade.py +0 -0
  137. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/focus.py +0 -0
  138. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/__init__.py +0 -0
  139. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/autogen.py +0 -0
  140. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/camel.py +0 -0
  141. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/camel_channel.py +0 -0
  142. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/metagpt.py +0 -0
  143. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/framework_adapters/registry.py +0 -0
  144. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/hook_input.py +0 -0
  145. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/http_proxy.py +0 -0
  146. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/http_proxy_cli.py +0 -0
  147. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/inference_gateway_full_fidelity.py +0 -0
  148. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/inference_identity.py +0 -0
  149. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/inference_identity_cli.py +0 -0
  150. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/investigation_index.py +0 -0
  151. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/litellm_callback_cli.py +0 -0
  152. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view.py +0 -0
  153. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_markup.py +0 -0
  154. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_process_layout.py +0 -0
  155. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_readability.py +0 -0
  156. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_script_b.py +0 -0
  157. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_script_c.py +0 -0
  158. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_script_d.py +0 -0
  159. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/live_view_style.py +0 -0
  160. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/model_runtime_full_fidelity.py +0 -0
  161. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/openai_compatible_full_fidelity.py +0 -0
  162. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_event_contract.py +0 -0
  163. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_full_fidelity.py +0 -0
  164. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_plugin_cli.py +0 -0
  165. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_record.py +0 -0
  166. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/opencode_task_linkage.py +0 -0
  167. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/overhead_benchmark.py +0 -0
  168. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/provider_capability.py +0 -0
  169. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/provider_lifecycle.py +0 -0
  170. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/provider_record.py +0 -0
  171. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/redacted_archive.py +0 -0
  172. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/rule_pack.py +0 -0
  173. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/rule_pack_cli.py +0 -0
  174. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/runtime_endpoint_availability.py +0 -0
  175. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/scalability_benchmark.py +0 -0
  176. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/schema.py +0 -0
  177. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/scope.py +0 -0
  178. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/strace_backend.py +0 -0
  179. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/stream_assembly.py +0 -0
  180. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/task_artifacts.py +0 -0
  181. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/theme.py +0 -0
  182. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/validate.py +0 -0
  183. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/vendor/dagre.min.js +0 -0
  184. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/view_cli.py +0 -0
  185. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_antigravity.py +0 -0
  186. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_claude.py +0 -0
  187. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_codex.py +0 -0
  188. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_cursor.py +0 -0
  189. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_default.py +0 -0
  190. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_ollama.py +0 -0
  191. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_opencode.py +0 -0
  192. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_agent_panel_registry.py +0 -0
  193. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_archive_verifier.py +0 -0
  194. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_background_files.py +0 -0
  195. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_content_browser.py +0 -0
  196. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_content_health.py +0 -0
  197. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_content_inspector.py +0 -0
  198. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_controlled_checks.py +0 -0
  199. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_conversation_sync.py +0 -0
  200. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_dashboard_clean.py +0 -0
  201. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_dashboard_focus.py +0 -0
  202. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_dashboard_hardening.py +0 -0
  203. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_dashboard_hardening_v2.py +0 -0
  204. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_dashboard_pr70.py +0 -0
  205. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_delivery_status.py +0 -0
  206. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_edge_decross.py +0 -0
  207. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_execution_flow.py +0 -0
  208. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_execution_flow_provider_normalization.py +0 -0
  209. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_execution_flow_safety.py +0 -0
  210. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_external_endpoints.py +0 -0
  211. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_fold_state.py +0 -0
  212. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_framework_flow.py +0 -0
  213. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_framework_model_output.py +0 -0
  214. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_gif.py +0 -0
  215. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_history_browser.py +0 -0
  216. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_investigation.py +0 -0
  217. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_layout_geometry.py +0 -0
  218. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_layout_v2.py +0 -0
  219. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_layout_v2_detached.py +0 -0
  220. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_limits.py +0 -0
  221. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_live_layout.py +0 -0
  222. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_message_handoffs.py +0 -0
  223. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_projection_bridges.py +0 -0
  224. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_recorded_sources.py +0 -0
  225. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_run_guide.py +0 -0
  226. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_runtime_evidence.py +0 -0
  227. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_structure_share.py +0 -0
  228. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_task_validation.py +0 -0
  229. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_tool_mirror_identity.py +0 -0
  230. {execweave-0.8.34 → execweave-0.8.35}/src/execweave/viewer_workflow_mode.py +0 -0
  231. {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.34
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
- Some agents and IDEs ask for permission before enabling a local hook or plugin. Approve the ExecWeave integration if you want provider-level prompt, response, tool, model, and conversation evidence. If you do not approve it, OS-runtime observation can still work, but semantic coverage may be reduced.
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
- Some agents and IDEs ask for permission before enabling a local hook or plugin. Approve the ExecWeave integration if you want provider-level prompt, response, tool, model, and conversation evidence. If you do not approve it, OS-runtime observation can still work, but semantic coverage may be reduced.
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.34 release metadata
8
- version = "0.8.34"
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,3 +1,3 @@
1
1
  """ExecWeave runtime graph package."""
2
2
 
3
- __version__ = "0.8.34"
3
+ __version__ = "0.8.35"
@@ -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.write_text(
901
- json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True) + "\n",
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
- if capture is not None:
491
- capture.finish()
492
- _record_capture(
493
- handler.server.proxy_config,
494
- exchange_id=exchange_id,
495
- request_body=body,
496
- response_content_type=response_type,
497
- method=handler.command,
498
- request_path=target,
499
- status=status,
500
- capture=capture,
501
- )
502
- else:
503
- handler.server.recorder(
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 marker in command:
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 = False
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, False
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 or marker in existing:
243
+ if existing == content:
219
244
  return False
220
- raise FileExistsError(f"refusing to replace existing OpenCode plugin: {path}")
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(f"execweave-claude-hook {_AUTO_FLAG}"), "execweave-claude-hook"
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(f"execweave-codex-hook {_AUTO_FLAG}"), "execweave-codex-hook"
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(f"execweave-cursor-hook {_AUTO_FLAG}"), "execweave-cursor-hook"
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(("execweave-opencode-hook", _AUTO_FLAG))
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="active",
347
+ status="configured_unverified",
321
348
  path=str(target),
322
349
  changed=changed,
323
- detail="specialized hook/plugin bootstrap is configured",
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
- output = Path(path).expanduser().resolve()
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
- with output.open("a", encoding="utf-8", newline="\n") as handle:
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
- output = Path(path).expanduser().resolve()
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
- with output.open("a", encoding="utf-8", newline="\n") as handle:
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"execweave-antigravity-hook --auto --event {event}",
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 = Path(sidecar).expanduser().resolve()
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: