execweave 0.8.27__tar.gz → 0.8.29__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 (187) hide show
  1. {execweave-0.8.27 → execweave-0.8.29}/PKG-INFO +10 -2
  2. {execweave-0.8.27 → execweave-0.8.29}/README.md +9 -1
  3. {execweave-0.8.27 → execweave-0.8.29}/pyproject.toml +2 -2
  4. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/__init__.py +1 -1
  5. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/framework_adapters/autogen.py +48 -0
  6. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/framework_adapters/base.py +56 -1
  7. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/framework_adapters/camel.py +35 -5
  8. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/framework_adapters/metagpt.py +20 -2
  9. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live_core.py +16 -6
  10. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/provider_record.py +1 -0
  11. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_agent_panel.py +28 -3
  12. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/workflow.py +60 -3
  13. {execweave-0.8.27 → execweave-0.8.29}/.gitignore +0 -0
  14. {execweave-0.8.27 → execweave-0.8.29}/LICENSE +0 -0
  15. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/__main__.py +0 -0
  16. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/_conversation_records_core.py +0 -0
  17. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/_dashboard_shell_base.py +0 -0
  18. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/_http_proxy_base.py +0 -0
  19. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/_http_proxy_bounded.py +0 -0
  20. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/_http_proxy_stage.py +0 -0
  21. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/agent_bootstrap.py +0 -0
  22. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/agent_topology.py +0 -0
  23. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/agent_trace.py +0 -0
  24. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/agy_preview_sanitize.py +0 -0
  25. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/analysis.py +0 -0
  26. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/anthropic.py +0 -0
  27. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/anthropic_cli.py +0 -0
  28. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/anthropic_full_fidelity.py +0 -0
  29. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/antigravity_adapter.py +0 -0
  30. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/antigravity_adapter_base.py +0 -0
  31. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/antigravity_full_fidelity.py +0 -0
  32. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/antigravity_full_fidelity_base.py +0 -0
  33. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/antigravity_full_fidelity_collaboration_base.py +0 -0
  34. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/antigravity_hook_cli.py +0 -0
  35. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/antigravity_record.py +0 -0
  36. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/antigravity_subagent_linkage.py +0 -0
  37. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/antigravity_trace_capability.py +0 -0
  38. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/auto_specialized.py +0 -0
  39. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/backends.py +0 -0
  40. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/benchmark.py +0 -0
  41. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/claude_adapter.py +0 -0
  42. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/claude_child_transcript.py +0 -0
  43. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/claude_delegation.py +0 -0
  44. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/claude_full_fidelity.py +0 -0
  45. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/claude_hook_cli.py +0 -0
  46. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/claude_hook_contract.py +0 -0
  47. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/claude_model_observer.py +0 -0
  48. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/claude_record.py +0 -0
  49. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/cli.py +0 -0
  50. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_adapter.py +0 -0
  51. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_conversation.py +0 -0
  52. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_full_fidelity.py +0 -0
  53. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_hook_cli.py +0 -0
  54. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_hook_entry.py +0 -0
  55. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_hook_lifecycle.py +0 -0
  56. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_message_diagnostics.py +0 -0
  57. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_message_transport_diagnostics.py +0 -0
  58. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_record.py +0 -0
  59. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_rollout_structures.py +0 -0
  60. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_rollout_trace.py +0 -0
  61. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/codex_rollout_trace_base.py +0 -0
  62. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/collector.py +0 -0
  63. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/command.py +0 -0
  64. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/content_evidence.py +0 -0
  65. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/content_store.py +0 -0
  66. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_archive.py +0 -0
  67. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_message_identity.py +0 -0
  68. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_preview.py +0 -0
  69. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_preview_antigravity.py +0 -0
  70. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_preview_claude.py +0 -0
  71. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_preview_codex.py +0 -0
  72. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_preview_common.py +0 -0
  73. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_preview_generic.py +0 -0
  74. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_preview_lines.py +0 -0
  75. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_preview_transcript.py +0 -0
  76. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_records.py +0 -0
  77. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_records_antigravity.py +0 -0
  78. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_records_codex.py +0 -0
  79. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_records_common.py +0 -0
  80. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/conversation_records_ollama.py +0 -0
  81. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/correlation.py +0 -0
  82. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/cursor_adapter.py +0 -0
  83. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/cursor_delegation.py +0 -0
  84. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/cursor_delegation_base.py +0 -0
  85. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/cursor_full_fidelity.py +0 -0
  86. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/cursor_hook_cli.py +0 -0
  87. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/cursor_hook_contract.py +0 -0
  88. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/cursor_lifecycle.py +0 -0
  89. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/cursor_record.py +0 -0
  90. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/dashboard_shell.py +0 -0
  91. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/entry.py +0 -0
  92. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/evidence_availability.py +0 -0
  93. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/evidence_grade.py +0 -0
  94. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/fidelity.py +0 -0
  95. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/filesystem.py +0 -0
  96. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/focus.py +0 -0
  97. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/framework_adapters/__init__.py +0 -0
  98. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/framework_adapters/registry.py +0 -0
  99. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/graph.py +0 -0
  100. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/graph_ops.py +0 -0
  101. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/http_proxy.py +0 -0
  102. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/http_proxy_cli.py +0 -0
  103. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/inference_gateway.py +0 -0
  104. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/inference_gateway_cli.py +0 -0
  105. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/inference_gateway_full_fidelity.py +0 -0
  106. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/inference_identity.py +0 -0
  107. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/inference_identity_cli.py +0 -0
  108. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/integrity.py +0 -0
  109. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/integrity_cli.py +0 -0
  110. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/litellm_callback.py +0 -0
  111. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/litellm_callback_cli.py +0 -0
  112. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live.py +0 -0
  113. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live_view.py +0 -0
  114. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live_view_extra_style.py +0 -0
  115. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live_view_markup.py +0 -0
  116. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live_view_process_layout.py +0 -0
  117. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live_view_readability.py +0 -0
  118. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live_view_script_a.py +0 -0
  119. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live_view_script_b.py +0 -0
  120. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live_view_script_c.py +0 -0
  121. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live_view_script_d.py +0 -0
  122. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/live_view_style.py +0 -0
  123. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/model_runtime.py +0 -0
  124. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/model_runtime_cli.py +0 -0
  125. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/model_runtime_full_fidelity.py +0 -0
  126. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/openai_compatible.py +0 -0
  127. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/openai_compatible_cli.py +0 -0
  128. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/openai_compatible_full_fidelity.py +0 -0
  129. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/opencode_adapter.py +0 -0
  130. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/opencode_event_contract.py +0 -0
  131. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/opencode_full_fidelity.py +0 -0
  132. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/opencode_hook_cli.py +0 -0
  133. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/opencode_plugin_cli.py +0 -0
  134. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/opencode_record.py +0 -0
  135. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/opencode_task_linkage.py +0 -0
  136. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/overhead_benchmark.py +0 -0
  137. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/provider_capability.py +0 -0
  138. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/provider_lifecycle.py +0 -0
  139. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/rule_pack.py +0 -0
  140. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/rule_pack_cli.py +0 -0
  141. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/runtime_endpoint_availability.py +0 -0
  142. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/scalability_benchmark.py +0 -0
  143. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/schema.py +0 -0
  144. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/scope.py +0 -0
  145. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/semantic.py +0 -0
  146. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/sink.py +0 -0
  147. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/strace_backend.py +0 -0
  148. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/stream_assembly.py +0 -0
  149. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/theme.py +0 -0
  150. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/top.py +0 -0
  151. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/top_cli.py +0 -0
  152. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/validate.py +0 -0
  153. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/vendor/dagre.min.js +0 -0
  154. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/view_cli.py +0 -0
  155. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer.py +0 -0
  156. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_agent_panel_antigravity.py +0 -0
  157. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_agent_panel_claude.py +0 -0
  158. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_agent_panel_codex.py +0 -0
  159. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_agent_panel_cursor.py +0 -0
  160. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_agent_panel_default.py +0 -0
  161. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_agent_panel_ollama.py +0 -0
  162. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_agent_panel_opencode.py +0 -0
  163. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_agent_panel_registry.py +0 -0
  164. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_content_inspector.py +0 -0
  165. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_dashboard_clean.py +0 -0
  166. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_dashboard_focus.py +0 -0
  167. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_dashboard_hardening.py +0 -0
  168. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_dashboard_hardening_v2.py +0 -0
  169. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_dashboard_pr70.py +0 -0
  170. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_edge_decross.py +0 -0
  171. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_execution_flow.py +0 -0
  172. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_execution_flow_provider_normalization.py +0 -0
  173. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_execution_flow_safety.py +0 -0
  174. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_external_endpoints.py +0 -0
  175. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_flow_canvas.py +0 -0
  176. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_flow_layout.py +0 -0
  177. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_gif.py +0 -0
  178. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_layout_geometry.py +0 -0
  179. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_layout_v2.py +0 -0
  180. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_layout_v2_detached.py +0 -0
  181. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_limits.py +0 -0
  182. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_live_layout.py +0 -0
  183. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_projection.py +0 -0
  184. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_projection_base.py +0 -0
  185. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_projection_bridges.py +0 -0
  186. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_semantic_projection.py +0 -0
  187. {execweave-0.8.27 → execweave-0.8.29}/src/execweave/viewer_tool_mirror_identity.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: execweave
3
- Version: 0.8.27
3
+ Version: 0.8.29
4
4
  Summary: Turn AI-agent runtime behavior into a local interactive execution graph.
5
5
  Project-URL: Homepage, https://github.com/Irish-kw/ExecWeave
6
6
  Project-URL: Repository, https://github.com/Irish-kw/ExecWeave
@@ -388,5 +388,13 @@ Paper context: Li et al., *CAMEL: Communicative Agents for "Mind" Exploration of
388
388
 
389
389
  ```python
390
390
  from execweave.framework_adapters import AdapterContext, CAMELAdapter
391
- adapter = CAMELAdapter(AdapterContext.from_environment("camel"))
391
+ context = AdapterContext.from_environment("camel", capture_mode="prompt_and_response")
392
+ adapter = CAMELAdapter(context)
393
+ task = adapter.task_created("task-1", content="Inspect the endpoint and report completion.")
392
394
  ```
395
+
396
+ When the application is launched by `execweave record` or `execweave live`,
397
+ the adapter automatically joins that run and the finished Dashboard is built
398
+ from the merged runtime plus framework evidence. Task prompts and routed
399
+ messages remain local content references and are visible from their task and
400
+ agent nodes.
@@ -328,5 +328,13 @@ Paper context: Li et al., *CAMEL: Communicative Agents for "Mind" Exploration of
328
328
 
329
329
  ```python
330
330
  from execweave.framework_adapters import AdapterContext, CAMELAdapter
331
- adapter = CAMELAdapter(AdapterContext.from_environment("camel"))
331
+ context = AdapterContext.from_environment("camel", capture_mode="prompt_and_response")
332
+ adapter = CAMELAdapter(context)
333
+ task = adapter.task_created("task-1", content="Inspect the endpoint and report completion.")
332
334
  ```
335
+
336
+ When the application is launched by `execweave record` or `execweave live`,
337
+ the adapter automatically joins that run and the finished Dashboard is built
338
+ from the merged runtime plus framework evidence. Task prompts and routed
339
+ messages remain local content references and are visible from their task and
340
+ agent nodes.
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "execweave"
7
- # ExecWeave v0.8.27 release metadata
8
- version = "0.8.27"
7
+ # ExecWeave v0.8.29 release metadata
8
+ version = "0.8.29"
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.27"
3
+ __version__ = "0.8.29"
@@ -15,6 +15,7 @@ from .base import (
15
15
  FrameworkCompatibility,
16
16
  MessageRecord,
17
17
  ModelCallRecord,
18
+ TaskRecord,
18
19
  ToolCallRecord,
19
20
  )
20
21
 
@@ -26,6 +27,13 @@ class AutoGenAdapter(FrameworkAdapter):
26
27
  "AGENT_CREATED",
27
28
  "AGENT_STARTED",
28
29
  "AGENT_STOPPED",
30
+ "TASK_CREATED",
31
+ "TASK_ASSIGNED",
32
+ "TASK_STARTED",
33
+ "TASK_UPDATED",
34
+ "TASK_COMPLETED",
35
+ "TASK_FAILED",
36
+ "TASK_CONTENT_RECORDED",
29
37
  "MESSAGE_SENT",
30
38
  "MESSAGE_RECEIVED",
31
39
  "MODEL_REQUEST",
@@ -86,6 +94,46 @@ class AutoGenAdapter(FrameworkAdapter):
86
94
  self.context.record_agent(AgentRecord(agent, role=role, process=process, attributes=attributes))
87
95
  return agent
88
96
 
97
+ def observe_task(
98
+ self,
99
+ task_id: str | int,
100
+ *,
101
+ owner: EntityRef | None = None,
102
+ name: str | None = None,
103
+ content: str | None = None,
104
+ content_kind: str = "task_prompt",
105
+ status: str = "created",
106
+ **attributes: Any,
107
+ ) -> EntityRef:
108
+ """Record an authoritative AutoGen task and its visible task prompt."""
109
+ entity_attributes = dict(attributes)
110
+ if content is not None and self.context.capture_policy.mode in {
111
+ "prompt_only",
112
+ "prompt_and_response",
113
+ }:
114
+ entity_attributes.setdefault("task_prompt", content)
115
+ task = self.task(task_id, name=name, provider="autogen", **entity_attributes)
116
+ event = {
117
+ "created": "TASK_CREATED",
118
+ "started": "TASK_STARTED",
119
+ "updated": "TASK_UPDATED",
120
+ "completed": "TASK_COMPLETED",
121
+ "failed": "TASK_FAILED",
122
+ }.get(status)
123
+ if event is None:
124
+ raise ValueError(f"unsupported AutoGen task status: {status}")
125
+ self.context.record_task(
126
+ TaskRecord(
127
+ task,
128
+ owner=owner,
129
+ content=content,
130
+ content_kind=content_kind,
131
+ attributes=attributes,
132
+ ),
133
+ event_type=event,
134
+ )
135
+ return task
136
+
89
137
  def observe_message(
90
138
  self,
91
139
  message_id: str | int,
@@ -11,6 +11,7 @@ import hashlib
11
11
  import json
12
12
  import os
13
13
  import re
14
+ import sys
14
15
  import threading
15
16
  import time
16
17
  from dataclasses import asdict, dataclass, field
@@ -19,6 +20,8 @@ from pathlib import Path
19
20
  from typing import Any, Literal, Mapping
20
21
  from uuid import uuid4
21
22
 
23
+ import psutil
24
+
22
25
  from ..content_store import ContentReference, FullFidelityContentStore
23
26
  from ..schema import SCHEMA_VERSION
24
27
 
@@ -33,6 +36,7 @@ CANONICAL_EVENT_TYPES = frozenset(
33
36
  {
34
37
  "AGENT_CREATED", "AGENT_STARTED", "AGENT_STOPPED",
35
38
  "TASK_CREATED", "TASK_ASSIGNED", "TASK_STARTED", "TASK_UPDATED",
39
+ "TASK_CONTENT_RECORDED",
36
40
  "TASK_COMPLETED", "TASK_FAILED",
37
41
  "MESSAGE_SENT", "MESSAGE_RECEIVED", "MESSAGE_UNROUTED",
38
42
  "MODEL_REQUEST", "MODEL_RESPONSE", "MODEL_FAILURE",
@@ -136,6 +140,20 @@ class ProcessRef:
136
140
  return EntityRef("process_reference", f"process-reference:{identity}", self.executable or f"pid {self.pid}", attributes)
137
141
 
138
142
 
143
+ def _current_process_ref() -> ProcessRef:
144
+ """Return the authoritative identity of the adapter's current process."""
145
+ process = psutil.Process(os.getpid())
146
+ try:
147
+ create_time = process.create_time()
148
+ except (psutil.AccessDenied, psutil.NoSuchProcess, psutil.ZombieProcess):
149
+ create_time = None
150
+ try:
151
+ executable = process.exe()
152
+ except (psutil.AccessDenied, psutil.NoSuchProcess, psutil.ZombieProcess):
153
+ executable = sys.executable
154
+ return ProcessRef(os.getpid(), create_time, executable or sys.executable)
155
+
156
+
139
157
  @dataclass(frozen=True)
140
158
  class AdapterCapabilities:
141
159
  events: tuple[str, ...] = ()
@@ -298,6 +316,8 @@ class TaskRecord:
298
316
  task: EntityRef
299
317
  owner: EntityRef | None = None
300
318
  parent_task: EntityRef | None = None
319
+ content: str | None = None
320
+ content_kind: str = "task_prompt"
301
321
  attributes: Mapping[str, Any] = field(default_factory=dict)
302
322
 
303
323
 
@@ -359,6 +379,12 @@ class AdapterContext:
359
379
 
360
380
  @classmethod
361
381
  def from_environment(cls, framework: str, *, capture_mode: CaptureMode = "metadata_only", content_root: str | Path | None = None, run_id: str | None = None, session_id: str | None = None, conversation_id: str | None = None, process: ProcessRef | None = None) -> "AdapterContext":
382
+ if (
383
+ process is None
384
+ and os.environ.get("EXECWEAVE_SEMANTIC_SIDECAR")
385
+ and os.environ.get("EXECWEAVE_SESSION_ID")
386
+ ):
387
+ process = _current_process_ref()
362
388
  return cls(framework=framework, run_id=run_id, session_id=session_id, conversation_id=conversation_id, content_root=content_root, capture_policy=ContentCapturePolicy(capture_mode), process=process)
363
389
 
364
390
  def entity(self, entity_type: str, native_id: str | int | None, *, name: str | None = None, attributes: Mapping[str, Any] | None = None) -> EntityRef:
@@ -391,9 +417,38 @@ class AdapterContext:
391
417
  return record.agent
392
418
 
393
419
  def record_task(self, record: TaskRecord, *, event_type: str = "TASK_CREATED") -> EntityRef:
394
- self.emit(event_type, event_type, source=record.owner, target=record.task, attributes={"parent_task_id": record.parent_task.id if record.parent_task else None, **dict(record.attributes)})
420
+ content_reference = None
421
+ if record.content is not None:
422
+ content_kind = record.content_kind
423
+ if "." not in content_kind:
424
+ content_kind = f"{self.framework}.{content_kind}"
425
+ content_reference = self.content.put_text(record.content, content_kind=content_kind)
426
+ task_attributes = {
427
+ "parent_task_id": record.parent_task.id if record.parent_task else None,
428
+ **self.content.reference_attributes(content_reference),
429
+ **dict(record.attributes),
430
+ }
431
+ self.emit(event_type, event_type, source=record.owner, target=record.task, attributes=task_attributes)
395
432
  if record.parent_task is not None:
396
433
  self.emit("TASK_CREATED", "PARENT_TASK", source=record.parent_task, target=record.task, attributes={"parent_task_id": record.parent_task.id})
434
+ if content_reference is not None:
435
+ content = EntityRef(
436
+ "observed_content",
437
+ f"observed-content:task:sha256:{content_reference.sha256}",
438
+ content_reference.content_kind,
439
+ content_reference.to_dict(),
440
+ )
441
+ self.emit(
442
+ "TASK_CONTENT_RECORDED",
443
+ "HAS_TASK_CONTENT",
444
+ source=record.task,
445
+ target=content,
446
+ attributes={
447
+ "task_id": record.task.id,
448
+ "conversation_scope": "task",
449
+ **self.content.reference_attributes(content_reference),
450
+ },
451
+ )
397
452
  return record.task
398
453
 
399
454
  def record_message(self, record: MessageRecord) -> EntityRef:
@@ -11,7 +11,7 @@ from .base import AdapterCapabilities, AdapterContext, AgentRecord, EntityRef, F
11
11
  class CAMELAdapter(FrameworkAdapter):
12
12
  framework_name = "camel"
13
13
  capabilities = AdapterCapabilities(
14
- events=("AGENT_CREATED", "AGENT_STARTED", "AGENT_STOPPED", "TASK_CREATED", "TASK_ASSIGNED", "TASK_STARTED", "TASK_UPDATED", "TASK_COMPLETED", "TASK_FAILED", "MESSAGE_SENT", "MESSAGE_RECEIVED", "MESSAGE_UNROUTED", "MODEL_REQUEST", "MODEL_RESPONSE", "MODEL_FAILURE"),
14
+ events=("AGENT_CREATED", "AGENT_STARTED", "AGENT_STOPPED", "TASK_CREATED", "TASK_ASSIGNED", "TASK_STARTED", "TASK_UPDATED", "TASK_COMPLETED", "TASK_FAILED", "TASK_CONTENT_RECORDED", "MESSAGE_SENT", "MESSAGE_RECEIVED", "MESSAGE_UNROUTED", "MODEL_REQUEST", "MODEL_RESPONSE", "MODEL_FAILURE"),
15
15
  content_modes=("metadata_only", "content_ref_only", "prompt_only", "prompt_and_response"),
16
16
  authoritative_surfaces=("camel.societies.workforce.WorkforceCallback", "camel.models.BaseModelBackend.run", "camel.models.BaseModelBackend.arun"),
17
17
  known_limitations=("WorkforceCallback LogEvent does not expose the emitting worker or recipient; those log messages are explicitly marked MESSAGE_UNROUTED and correlated to the observed process instead of being assigned to a guessed agent.", "A created worker is not treated as assigned until CAMEL reports a task assignment."),
@@ -21,6 +21,7 @@ class CAMELAdapter(FrameworkAdapter):
21
21
  super().__init__(context)
22
22
  self._agents: dict[str, EntityRef] = {}
23
23
  self._tasks: dict[str, EntityRef] = {}
24
+ self._active_tasks_by_agent: dict[str, EntityRef] = {}
24
25
  self._message_counter = 0
25
26
 
26
27
  @classmethod
@@ -44,18 +45,43 @@ class CAMELAdapter(FrameworkAdapter):
44
45
  def agent_stopped(self, agent: EntityRef, **attributes: Any) -> None:
45
46
  self.emit("AGENT_STOPPED", "AGENT_STOPPED", source=agent, attributes=attributes)
46
47
 
47
- def task_event(self, event_type: str, native_id: str | int, *, name: str | None = None, owner: EntityRef | None = None, parent: EntityRef | None = None, **attributes: Any) -> EntityRef:
48
- task = self.task(native_id, name=name, provider="camel", **attributes)
48
+ def task_event(self, event_type: str, native_id: str | int, *, name: str | None = None, owner: EntityRef | None = None, parent: EntityRef | None = None, content: str | None = None, content_kind: str = "task_prompt", **attributes: Any) -> EntityRef:
49
+ existing = self._tasks.get(str(native_id))
50
+ if existing is not None and name is None and content is None and not attributes:
51
+ task = existing
52
+ else:
53
+ entity_attributes = dict(attributes)
54
+ if content is not None and self.context.capture_policy.mode in {
55
+ "prompt_only",
56
+ "prompt_and_response",
57
+ }:
58
+ entity_attributes.setdefault("task_prompt", content)
59
+ task = self.task(native_id, name=name, provider="camel", **entity_attributes)
49
60
  self._tasks[str(native_id)] = task
50
- self.context.record_task(TaskRecord(task, owner=owner, parent_task=parent, attributes=attributes), event_type=event_type)
61
+ self.context.record_task(
62
+ TaskRecord(
63
+ task,
64
+ owner=owner,
65
+ parent_task=parent,
66
+ content=content,
67
+ content_kind=content_kind,
68
+ attributes=attributes,
69
+ ),
70
+ event_type=event_type,
71
+ )
51
72
  return task
52
73
 
53
74
  def task_created(self, native_id: str | int, **kwargs: Any) -> EntityRef:
54
75
  return self.task_event("TASK_CREATED", native_id, **kwargs)
55
76
 
56
77
  def task_assigned(self, task: EntityRef, agent: EntityRef, **attributes: Any) -> None:
78
+ self._active_tasks_by_agent[agent.id] = task
57
79
  self.emit("TASK_ASSIGNED", "ASSIGNED_TO", source=task, target=agent, attributes=attributes)
58
80
 
81
+ def active_task_for(self, agent: EntityRef | None) -> EntityRef | None:
82
+ """Return the latest provider-observed task assigned to an agent."""
83
+ return self._active_tasks_by_agent.get(agent.id) if agent is not None else None
84
+
59
85
  def task_started(self, task: EntityRef, agent: EntityRef | None = None, **attributes: Any) -> None:
60
86
  self.emit("TASK_STARTED", "TASK_STARTED", source=agent or task, target=task, attributes=attributes)
61
87
 
@@ -170,7 +196,11 @@ class CAMELAdapter(FrameworkAdapter):
170
196
  if name == "log_worker_created":
171
197
  return self.agent_created(_native(payload, "worker_id", "id", "name"), name=_string(_value(payload, "name", "role")), role=_string(_value(payload, "role", "worker_type")))
172
198
  if name == "log_task_created":
173
- return self.task_created(_native(payload, "task_id", "id", "name"), name=_string(_value(payload, "name", "description")))
199
+ task_key = _native(payload, "task_id", "id", "name")
200
+ existing = self._tasks.get(str(task_key))
201
+ if existing is not None:
202
+ return existing
203
+ return self.task_created(task_key, name=_string(_value(payload, "name", "description")))
174
204
  if name == "log_task_assigned":
175
205
  task_key = _native(payload, "task_id", "id", "name")
176
206
  task = self._tasks.get(str(task_key)) or self.task_created(task_key)
@@ -30,6 +30,7 @@ class MetaGPTAdapter(FrameworkAdapter):
30
30
  "TASK_COMPLETED",
31
31
  "TASK_FAILED",
32
32
  "TASK_UPDATED",
33
+ "TASK_CONTENT_RECORDED",
33
34
  "MESSAGE_SENT",
34
35
  "MESSAGE_RECEIVED",
35
36
  "MODEL_REQUEST",
@@ -96,10 +97,18 @@ class MetaGPTAdapter(FrameworkAdapter):
96
97
  *,
97
98
  owner: EntityRef | None = None,
98
99
  name: str | None = None,
100
+ content: str | None = None,
101
+ content_kind: str = "task_prompt",
99
102
  status: str = "created",
100
103
  **attributes: Any,
101
104
  ) -> EntityRef:
102
- task = self.task(task_id, name=name, provider="metagpt", **attributes)
105
+ entity_attributes = dict(attributes)
106
+ if content is not None and self.context.capture_policy.mode in {
107
+ "prompt_only",
108
+ "prompt_and_response",
109
+ }:
110
+ entity_attributes.setdefault("task_prompt", content)
111
+ task = self.task(task_id, name=name, provider="metagpt", **entity_attributes)
103
112
  event = {
104
113
  "created": "TASK_CREATED",
105
114
  "started": "TASK_STARTED",
@@ -108,7 +117,16 @@ class MetaGPTAdapter(FrameworkAdapter):
108
117
  }.get(status)
109
118
  if event is None:
110
119
  raise ValueError(f"unsupported MetaGPT task status: {status}")
111
- self.context.record_task(TaskRecord(task, owner=owner, attributes=attributes), event_type=event)
120
+ self.context.record_task(
121
+ TaskRecord(
122
+ task,
123
+ owner=owner,
124
+ content=content,
125
+ content_kind=content_kind,
126
+ attributes=attributes,
127
+ ),
128
+ event_type=event,
129
+ )
112
130
  return task
113
131
 
114
132
  def observe_message(
@@ -37,6 +37,8 @@ from .viewer import (
37
37
  LIVE_DELTA_HISTORY = 256
38
38
  LIVE_DELTA_HISTORY_BYTES = 8 * 1024 * 1024
39
39
  _SEMANTIC_ENV = "EXECWEAVE_SEMANTIC_SIDECAR"
40
+ _RUN_ID_ENV = "EXECWEAVE_RUN_ID"
41
+ _SESSION_ID_ENV = "EXECWEAVE_SESSION_ID"
40
42
  _LIVE_TOKEN_HEADER = "X-ExecWeave-Token"
41
43
 
42
44
  _FINAL_THEME_CSS = """
@@ -737,16 +739,24 @@ def run_live(
737
739
  webbrowser.open(authenticated_live_url)
738
740
 
739
741
  return_code = 1
740
- previous_semantic_sidecar = os.environ.get(_SEMANTIC_ENV)
741
- os.environ[_SEMANTIC_ENV] = str(semantic_path)
742
+ environment_updates = {
743
+ _SEMANTIC_ENV: str(semantic_path),
744
+ _RUN_ID_ENV: session_id,
745
+ _SESSION_ID_ENV: session_id,
746
+ }
747
+ previous_environment = {
748
+ key: os.environ.get(key) for key in environment_updates
749
+ }
750
+ os.environ.update(environment_updates)
742
751
  try:
743
752
  try:
744
753
  return_code = collector.run(command)
745
754
  finally:
746
- if previous_semantic_sidecar is None:
747
- os.environ.pop(_SEMANTIC_ENV, None)
748
- else:
749
- os.environ[_SEMANTIC_ENV] = previous_semantic_sidecar
755
+ for key, value in previous_environment.items():
756
+ if value is None:
757
+ os.environ.pop(key, None)
758
+ else:
759
+ os.environ[key] = value
750
760
 
751
761
  validation = validate_event_stream(event_path)
752
762
  if not validation.valid:
@@ -183,6 +183,7 @@ def record_provider_to_viewer(
183
183
  collect_network=collect_network,
184
184
  keep_raw_trace=keep_raw_trace,
185
185
  open_browser=False,
186
+ integrate_semantic=False,
186
187
  )
187
188
  finally:
188
189
  _restore_environment(previous_environment)
@@ -372,6 +372,10 @@ function execweaveNodeCardsBase(node){
372
372
  add('Command',a.command);
373
373
  add('Working directory',a.cwd);
374
374
  add('Backend',a.backend);
375
+ }else if(kind==='task'){
376
+ add('Task',a.task_prompt||a.prompt||a.description||node?.name);
377
+ add('Prompt',a.task_prompt||a.prompt||a.description);
378
+ add('Provider',a.provider);
375
379
  }else if(kind==='network_endpoint'){
376
380
  const endpoints=Array.isArray(a.endpoints)?a.endpoints:[];
377
381
  if(endpoints.length){
@@ -414,6 +418,26 @@ function toolOccurrenceSection(node){
414
418
  });
415
419
  return section;
416
420
  }
421
+ function execweaveAssignedTaskText(agent){
422
+ if(!agent||String(agent.type||'')!=='agent')return '';
423
+ const graph=rawGraph(),nodes=Array.isArray(graph.nodes)?graph.nodes:[],edges=Array.isArray(graph.edges)?graph.edges:[];
424
+ const nodeMap=new Map(nodes.filter(node=>node&&node.id).map(node=>[String(node.id),node]));
425
+ const exactAssignment=['ASSIGNED','AGENT','TASK'].join('_');
426
+ const direct=edges.filter(edge=>edge&&String(edge.target||'')===String(agent.id||'')&&['ASSIGNED_TO',exactAssignment].includes(String(edge.relation||''))).sort((a,b)=>(Number(a.first_sequence)||0)-(Number(b.first_sequence)||0));
427
+ const owned=edges.filter(edge=>edge&&String(edge.source||'')===String(agent.id||'')&&String(edge.relation||'')==='TASK_CREATED').sort((a,b)=>(Number(a.first_sequence)||0)-(Number(b.first_sequence)||0));
428
+ const started=edges.filter(edge=>edge&&String(edge.source||'')===String(agent.id||'')&&String(edge.relation||'')==='TASK_STARTED').sort((a,b)=>(Number(a.first_sequence)||0)-(Number(b.first_sequence)||0));
429
+ const edge=direct.at(-1)||owned.at(-1)||started.at(-1);if(!edge)return '';
430
+ const taskId=direct.length?edge.source:edge.target,task=nodeMap.get(String(taskId||''));
431
+ if(!task||String(task.type||'')!=='task')return '';
432
+ const a=attrs(task);return commandText(a.task_prompt||a.prompt||a.description||task.name);
433
+ }
434
+ function execweaveFillAssignedTask(round,node,isRoot){
435
+ const task=execweaveAssignedTaskText(node);if(!task)return round;
436
+ const value=round||{cards:[]},cards=Array.isArray(value.cards)?value.cards.map(card=>[card[0],card[1]]):[];
437
+ const label=isRoot?'Prompt':'Task',index=cards.findIndex(card=>String(card[0]||'')===label);
438
+ if(index<0)cards.unshift([label,task]);else if(!commandText(cards[index][1]))cards[index][1]=task;
439
+ return{...value,cards};
440
+ }
417
441
  function renderNode(node){
418
442
  const rows=nodeCards(node);
419
443
  if(!rows.length)return false;
@@ -432,10 +456,11 @@ function render(node){
432
456
  selectedNode=node;selectedConversationSignature=conversationSignature(node);detailsEmpty.hidden=true;details.replaceChildren();
433
457
  const record=recordFor(node),preview=record?.conversation_preview||{},path=String(preview.agent_path||nodePath(node)||'').trim(),messages=Array.isArray(preview.messages)?preview.messages:[];
434
458
  const isRoot=nodeHasRootAuthority(node)||previewUsesRootRenderer(preview);
435
- const rounds=isRoot?rootRounds(messages,path||'/root'):childRounds(messages,path);
459
+ const rounds=(isRoot?rootRounds(messages,path||'/root'):childRounds(messages,path)).map(round=>execweaveFillAssignedTask(round,node,isRoot));
436
460
  const tools=toolCallsFor(String(node.id||''));
437
- const appendTools=()=>{if(tools)details.appendChild(card('Tools',tools))};
438
- if(rounds.length<2){details.appendChild(roundView(rounds[0]||{cards:isRoot?[['Prompt',''],['Final response','']]:[['Task',''],['Thinking',''],['Response','']]}));appendTools();return true}
461
+ const frameworkCommunication=attrs(node).conversation_scope==='framework_agent'?uniqueTexts(messages.filter(message=>isObserved(message)&&!isInjected(message)&&String(message?.kind||'')==='agent_message')).join('\n\n'):'';
462
+ const appendTools=()=>{if(frameworkCommunication)details.appendChild(card('Agent communication',frameworkCommunication));if(tools)details.appendChild(card('Tools',tools))};
463
+ if(rounds.length<2){const fallback={cards:isRoot?[['Prompt',''],['Final response','']]:[['Task',''],['Thinking',''],['Response','']]};details.appendChild(roundView(rounds[0]||execweaveFillAssignedTask(fallback,node,isRoot)));appendTools();return true}
439
464
  // A subagent borrows the moment and the wording of the unique canonical root round
440
465
  // it belongs to. If root identity is ambiguous, the child keeps its own timestamp.
441
466
  const runs=isRoot?rounds:runRounds();
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import os
3
4
  from dataclasses import dataclass
4
5
  from pathlib import Path
5
6
  from uuid import uuid4
@@ -7,11 +8,16 @@ from uuid import uuid4
7
8
  from .backends import BackendName, create_collector, resolve_backend
8
9
  from .fidelity import write_fidelity_report
9
10
  from .graph import build_execution_graph, write_execution_graph
11
+ from .semantic import merge_semantic_sidecar
10
12
  from .sink import JsonlSink
11
13
  from .theme import ensure_viewer_theme
12
14
  from .validate import validate_event_stream
13
15
  from .viewer_projection import strip_internal_hook_execution_graph, write_graph_html
14
16
 
17
+ _SEMANTIC_ENV = "EXECWEAVE_SEMANTIC_SIDECAR"
18
+ _RUN_ID_ENV = "EXECWEAVE_RUN_ID"
19
+ _SESSION_ID_ENV = "EXECWEAVE_SESSION_ID"
20
+
15
21
 
16
22
  @dataclass(frozen=True)
17
23
  class RecordResult:
@@ -26,6 +32,9 @@ class RecordResult:
26
32
  event_count: int
27
33
  node_count: int
28
34
  edge_count: int
35
+ semantic_sidecar: Path | None = None
36
+ materialized_event_stream: Path | None = None
37
+ semantic_event_count: int = 0
29
38
 
30
39
  def to_dict(self) -> dict[str, object]:
31
40
  return {
@@ -40,6 +49,15 @@ class RecordResult:
40
49
  "event_count": self.event_count,
41
50
  "node_count": self.node_count,
42
51
  "edge_count": self.edge_count,
52
+ "semantic_sidecar": (
53
+ str(self.semantic_sidecar) if self.semantic_sidecar is not None else None
54
+ ),
55
+ "materialized_event_stream": (
56
+ str(self.materialized_event_stream)
57
+ if self.materialized_event_stream is not None
58
+ else str(self.event_stream)
59
+ ),
60
+ "semantic_event_count": self.semantic_event_count,
43
61
  }
44
62
 
45
63
 
@@ -61,6 +79,7 @@ def record_to_viewer(
61
79
  collect_network: bool = True,
62
80
  keep_raw_trace: bool = False,
63
81
  open_browser: bool = False,
82
+ integrate_semantic: bool = True,
64
83
  ) -> RecordResult:
65
84
  """Record one command and materialize a local graph/viewer after it exits."""
66
85
  if not command:
@@ -79,7 +98,12 @@ def record_to_viewer(
79
98
  fidelity_path = run_dir / "fidelity.json"
80
99
  graph_path = run_dir / "graph.json"
81
100
  viewer_path = run_dir / "viewer.html"
82
- _preflight_artifacts([event_path, fidelity_path, graph_path, viewer_path])
101
+ semantic_path = run_dir / "semantic.jsonl"
102
+ merged_event_path = run_dir / "events.semantic.jsonl"
103
+ artifacts = [event_path, fidelity_path, graph_path, viewer_path]
104
+ if integrate_semantic:
105
+ artifacts.extend([semantic_path, merged_event_path])
106
+ _preflight_artifacts(artifacts)
83
107
 
84
108
  sink = JsonlSink(event_path)
85
109
  resolved = resolve_backend(backend)
@@ -94,14 +118,44 @@ def record_to_viewer(
94
118
  keep_raw_trace=keep_raw_trace,
95
119
  )
96
120
 
97
- return_code = collector.run(command)
121
+ environment_updates = {
122
+ _RUN_ID_ENV: session_id,
123
+ _SESSION_ID_ENV: session_id,
124
+ }
125
+ if integrate_semantic:
126
+ environment_updates[_SEMANTIC_ENV] = str(semantic_path)
127
+ previous_environment = {
128
+ key: os.environ.get(key) for key in environment_updates
129
+ }
130
+ os.environ.update(environment_updates)
131
+ try:
132
+ return_code = collector.run(command)
133
+ finally:
134
+ for key, value in previous_environment.items():
135
+ if value is None:
136
+ os.environ.pop(key, None)
137
+ else:
138
+ os.environ[key] = value
98
139
 
99
140
  validation = validate_event_stream(event_path)
100
141
  if not validation.valid:
101
142
  details = "; ".join(validation.errors)
102
143
  raise RuntimeError(f"recorded event stream failed validation: {details}")
103
144
 
104
- execution_graph = strip_internal_hook_execution_graph(build_execution_graph(event_path))
145
+ materialized_event_path = event_path
146
+ semantic_event_count = 0
147
+ if integrate_semantic and semantic_path.exists() and semantic_path.stat().st_size > 0:
148
+ semantic_merge = merge_semantic_sidecar(
149
+ event_path,
150
+ semantic_path,
151
+ merged_event_path,
152
+ )
153
+ materialized_event_path = merged_event_path
154
+ semantic_event_count = semantic_merge.semantic_event_count
155
+
156
+ execution_graph = strip_internal_hook_execution_graph(
157
+ build_execution_graph(materialized_event_path)
158
+ )
105
159
  write_fidelity_report(execution_graph.fidelity, fidelity_path)
106
160
  write_execution_graph(execution_graph, graph_path)
107
161
  write_graph_html(execution_graph.to_dict(), viewer_path, open_browser=False)
@@ -123,4 +177,7 @@ def record_to_viewer(
123
177
  event_count=execution_graph.event_count,
124
178
  node_count=len(execution_graph.nodes),
125
179
  edge_count=len(execution_graph.edges),
180
+ semantic_sidecar=semantic_path.resolve() if integrate_semantic else None,
181
+ materialized_event_stream=materialized_event_path.resolve(),
182
+ semantic_event_count=semantic_event_count,
126
183
  )
File without changes
File without changes