execweave 0.8.24__tar.gz → 0.8.25__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.24 → execweave-0.8.25}/PKG-INFO +1 -1
  2. {execweave-0.8.24 → execweave-0.8.25}/pyproject.toml +2 -2
  3. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/__init__.py +1 -1
  4. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/_conversation_records_core.py +3 -0
  5. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_preview_common.py +25 -0
  6. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_preview_generic.py +15 -0
  7. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_preview_lines.py +19 -2
  8. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/framework_adapters/autogen.py +5 -0
  9. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/framework_adapters/base.py +67 -8
  10. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/framework_adapters/camel.py +13 -3
  11. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/framework_adapters/metagpt.py +57 -12
  12. {execweave-0.8.24 → execweave-0.8.25}/.gitignore +0 -0
  13. {execweave-0.8.24 → execweave-0.8.25}/LICENSE +0 -0
  14. {execweave-0.8.24 → execweave-0.8.25}/README.md +0 -0
  15. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/__main__.py +0 -0
  16. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/_dashboard_shell_base.py +0 -0
  17. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/_http_proxy_base.py +0 -0
  18. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/_http_proxy_bounded.py +0 -0
  19. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/_http_proxy_stage.py +0 -0
  20. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/agent_bootstrap.py +0 -0
  21. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/agent_topology.py +0 -0
  22. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/agent_trace.py +0 -0
  23. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/agy_preview_sanitize.py +0 -0
  24. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/analysis.py +0 -0
  25. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/anthropic.py +0 -0
  26. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/anthropic_cli.py +0 -0
  27. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/anthropic_full_fidelity.py +0 -0
  28. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/antigravity_adapter.py +0 -0
  29. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/antigravity_adapter_base.py +0 -0
  30. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/antigravity_full_fidelity.py +0 -0
  31. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/antigravity_full_fidelity_base.py +0 -0
  32. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/antigravity_full_fidelity_collaboration_base.py +0 -0
  33. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/antigravity_hook_cli.py +0 -0
  34. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/antigravity_record.py +0 -0
  35. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/antigravity_subagent_linkage.py +0 -0
  36. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/antigravity_trace_capability.py +0 -0
  37. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/auto_specialized.py +0 -0
  38. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/backends.py +0 -0
  39. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/benchmark.py +0 -0
  40. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/claude_adapter.py +0 -0
  41. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/claude_child_transcript.py +0 -0
  42. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/claude_delegation.py +0 -0
  43. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/claude_full_fidelity.py +0 -0
  44. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/claude_hook_cli.py +0 -0
  45. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/claude_hook_contract.py +0 -0
  46. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/claude_model_observer.py +0 -0
  47. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/claude_record.py +0 -0
  48. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/cli.py +0 -0
  49. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_adapter.py +0 -0
  50. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_conversation.py +0 -0
  51. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_full_fidelity.py +0 -0
  52. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_hook_cli.py +0 -0
  53. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_hook_entry.py +0 -0
  54. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_hook_lifecycle.py +0 -0
  55. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_message_diagnostics.py +0 -0
  56. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_message_transport_diagnostics.py +0 -0
  57. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_record.py +0 -0
  58. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_rollout_structures.py +0 -0
  59. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_rollout_trace.py +0 -0
  60. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/codex_rollout_trace_base.py +0 -0
  61. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/collector.py +0 -0
  62. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/command.py +0 -0
  63. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/content_evidence.py +0 -0
  64. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/content_store.py +0 -0
  65. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_archive.py +0 -0
  66. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_message_identity.py +0 -0
  67. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_preview.py +0 -0
  68. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_preview_antigravity.py +0 -0
  69. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_preview_claude.py +0 -0
  70. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_preview_codex.py +0 -0
  71. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_preview_transcript.py +0 -0
  72. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_records.py +0 -0
  73. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_records_antigravity.py +0 -0
  74. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_records_codex.py +0 -0
  75. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_records_common.py +0 -0
  76. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/conversation_records_ollama.py +0 -0
  77. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/correlation.py +0 -0
  78. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/cursor_adapter.py +0 -0
  79. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/cursor_delegation.py +0 -0
  80. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/cursor_delegation_base.py +0 -0
  81. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/cursor_full_fidelity.py +0 -0
  82. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/cursor_hook_cli.py +0 -0
  83. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/cursor_hook_contract.py +0 -0
  84. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/cursor_lifecycle.py +0 -0
  85. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/cursor_record.py +0 -0
  86. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/dashboard_shell.py +0 -0
  87. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/entry.py +0 -0
  88. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/evidence_availability.py +0 -0
  89. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/evidence_grade.py +0 -0
  90. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/fidelity.py +0 -0
  91. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/filesystem.py +0 -0
  92. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/focus.py +0 -0
  93. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/framework_adapters/__init__.py +0 -0
  94. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/framework_adapters/registry.py +0 -0
  95. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/graph.py +0 -0
  96. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/graph_ops.py +0 -0
  97. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/http_proxy.py +0 -0
  98. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/http_proxy_cli.py +0 -0
  99. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/inference_gateway.py +0 -0
  100. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/inference_gateway_cli.py +0 -0
  101. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/inference_gateway_full_fidelity.py +0 -0
  102. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/inference_identity.py +0 -0
  103. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/inference_identity_cli.py +0 -0
  104. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/integrity.py +0 -0
  105. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/integrity_cli.py +0 -0
  106. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/litellm_callback.py +0 -0
  107. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/litellm_callback_cli.py +0 -0
  108. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live.py +0 -0
  109. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live_core.py +0 -0
  110. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live_view.py +0 -0
  111. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live_view_extra_style.py +0 -0
  112. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live_view_markup.py +0 -0
  113. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live_view_process_layout.py +0 -0
  114. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live_view_readability.py +0 -0
  115. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live_view_script_a.py +0 -0
  116. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live_view_script_b.py +0 -0
  117. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live_view_script_c.py +0 -0
  118. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live_view_script_d.py +0 -0
  119. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/live_view_style.py +0 -0
  120. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/model_runtime.py +0 -0
  121. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/model_runtime_cli.py +0 -0
  122. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/model_runtime_full_fidelity.py +0 -0
  123. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/openai_compatible.py +0 -0
  124. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/openai_compatible_cli.py +0 -0
  125. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/openai_compatible_full_fidelity.py +0 -0
  126. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/opencode_adapter.py +0 -0
  127. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/opencode_event_contract.py +0 -0
  128. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/opencode_full_fidelity.py +0 -0
  129. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/opencode_hook_cli.py +0 -0
  130. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/opencode_plugin_cli.py +0 -0
  131. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/opencode_record.py +0 -0
  132. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/opencode_task_linkage.py +0 -0
  133. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/overhead_benchmark.py +0 -0
  134. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/provider_capability.py +0 -0
  135. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/provider_lifecycle.py +0 -0
  136. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/provider_record.py +0 -0
  137. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/rule_pack.py +0 -0
  138. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/rule_pack_cli.py +0 -0
  139. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/runtime_endpoint_availability.py +0 -0
  140. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/scalability_benchmark.py +0 -0
  141. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/schema.py +0 -0
  142. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/scope.py +0 -0
  143. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/semantic.py +0 -0
  144. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/sink.py +0 -0
  145. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/strace_backend.py +0 -0
  146. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/stream_assembly.py +0 -0
  147. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/theme.py +0 -0
  148. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/top.py +0 -0
  149. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/top_cli.py +0 -0
  150. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/validate.py +0 -0
  151. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/vendor/dagre.min.js +0 -0
  152. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/view_cli.py +0 -0
  153. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer.py +0 -0
  154. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_agent_panel.py +0 -0
  155. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_agent_panel_antigravity.py +0 -0
  156. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_agent_panel_claude.py +0 -0
  157. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_agent_panel_codex.py +0 -0
  158. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_agent_panel_cursor.py +0 -0
  159. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_agent_panel_default.py +0 -0
  160. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_agent_panel_ollama.py +0 -0
  161. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_agent_panel_opencode.py +0 -0
  162. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_agent_panel_registry.py +0 -0
  163. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_content_inspector.py +0 -0
  164. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_dashboard_clean.py +0 -0
  165. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_dashboard_focus.py +0 -0
  166. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_dashboard_hardening.py +0 -0
  167. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_dashboard_hardening_v2.py +0 -0
  168. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_dashboard_pr70.py +0 -0
  169. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_edge_decross.py +0 -0
  170. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_execution_flow.py +0 -0
  171. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_execution_flow_provider_normalization.py +0 -0
  172. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_execution_flow_safety.py +0 -0
  173. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_external_endpoints.py +0 -0
  174. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_flow_canvas.py +0 -0
  175. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_flow_layout.py +0 -0
  176. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_gif.py +0 -0
  177. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_layout_geometry.py +0 -0
  178. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_layout_v2.py +0 -0
  179. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_layout_v2_detached.py +0 -0
  180. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_limits.py +0 -0
  181. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_live_layout.py +0 -0
  182. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_projection.py +0 -0
  183. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_projection_base.py +0 -0
  184. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_projection_bridges.py +0 -0
  185. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_semantic_projection.py +0 -0
  186. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/viewer_tool_mirror_identity.py +0 -0
  187. {execweave-0.8.24 → execweave-0.8.25}/src/execweave/workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: execweave
3
- Version: 0.8.24
3
+ Version: 0.8.25
4
4
  Summary: Turn AI-agent runtime behavior into a local interactive execution graph.
5
5
  Project-URL: Homepage, https://github.com/Irish-kw/ExecWeave
6
6
  Project-URL: Repository, https://github.com/Irish-kw/ExecWeave
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "execweave"
7
- # ExecWeave v0.8.24 release metadata
8
- version = "0.8.24"
7
+ # ExecWeave v0.8.25 release metadata
8
+ version = "0.8.25"
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.24"
3
+ __version__ = "0.8.25"
@@ -47,6 +47,9 @@ _CONVERSATION_TOKENS = (
47
47
  "request_prompt",
48
48
  "request_input",
49
49
  "model_context_messages",
50
+ "model_request",
51
+ "model_response",
52
+ "model_failure",
50
53
  )
51
54
 
52
55
 
@@ -5,6 +5,9 @@ from typing import Any
5
5
 
6
6
  from .agent_topology import (
7
7
  THREAD_ID_EXECWEAVE_DERIVED,
8
+ THREAD_ID_PROVIDER_NATIVE,
9
+ PATH_PROVIDER_DECLARED,
10
+ TOPOLOGY_OBSERVED,
8
11
  resolve_agent_topology,
9
12
  )
10
13
  from .conversation_preview_generic import ( # noqa: F401
@@ -93,6 +96,28 @@ def _agent_identity(provider: str, source: dict[str, Any] | None) -> dict[str, A
93
96
  source_id_text = source_id if isinstance(source_id, str) else ""
94
97
  provider_label = _provider_label(provider)
95
98
  provider_key = provider.lower() or "provider"
99
+ if attrs.get("conversation_scope") == "framework_agent":
100
+ native_id = str(attrs.get("conversation_agent_id") or source_id_text or "agent")
101
+ agent_path = attrs.get("conversation_agent_path")
102
+ if not isinstance(agent_path, str) or not agent_path.strip():
103
+ agent_path = f"/{provider_key}/{native_id}"
104
+ label = source_name or attrs.get("conversation_agent_name") or native_id
105
+ return {
106
+ "thread_id": f"{provider_key}:agent:{native_id}",
107
+ "thread_id_source": THREAD_ID_PROVIDER_NATIVE,
108
+ "parent_thread_id": None,
109
+ "agent_label": str(label),
110
+ "provider_label": provider_label,
111
+ "agent_nickname": None,
112
+ "agent_path": agent_path,
113
+ "agent_path_source": PATH_PROVIDER_DECLARED,
114
+ "topology_state": TOPOLOGY_OBSERVED,
115
+ "topology_evidence": "framework_agent_boundary",
116
+ "parent_agent_path": None,
117
+ "parent_relation_source": None,
118
+ "provider_native_id": native_id,
119
+ "is_root": False,
120
+ }
96
121
  topology = resolve_agent_topology(source)
97
122
  agent_path = topology.agent_path
98
123
  is_root = topology.is_root
@@ -64,6 +64,12 @@ def _routed_agent_message(
64
64
  return []
65
65
  sender = value.get("sender")
66
66
  recipient = value.get("recipient")
67
+ if recipient is None and isinstance(value.get("recipients"), list):
68
+ recipient = ", ".join(
69
+ str(item).strip()
70
+ for item in value["recipients"]
71
+ if str(item).strip()
72
+ ) or None
67
73
  kind = value.get("kind")
68
74
  phase = value.get("phase")
69
75
  task_name = value.get("task_name")
@@ -101,6 +107,15 @@ def _generic_content_messages(
101
107
  routed = _routed_agent_message(value, timestamp=timestamp, ordinal=ordinal, agent_path=agent_path)
102
108
  if routed:
103
109
  return routed
110
+ if "model_request" in kind or "model_response" in kind or "model_failure" in kind:
111
+ structured = _structured_messages(value, timestamp=timestamp, ordinal=ordinal, agent_path=agent_path)
112
+ if structured:
113
+ return structured
114
+ text = _text_parts(value)
115
+ if text:
116
+ if "model_request" in kind:
117
+ return [_message(timestamp=timestamp, ordinal=ordinal, kind="model_request", sender="context", recipient=agent_path, text=text, phase="request")]
118
+ return [_message(timestamp=timestamp, ordinal=ordinal, kind="assistant_message", sender=agent_path, recipient=None, text=text, phase="response")]
104
119
  text = _text_parts(value)
105
120
  if not text:
106
121
  return []
@@ -24,6 +24,13 @@ def _structured_messages(
24
24
  if isinstance(value, dict):
25
25
  if isinstance(value.get("message"), dict):
26
26
  candidates.append(value["message"])
27
+ if isinstance(value.get("messages"), list):
28
+ for item in value["messages"]:
29
+ if isinstance(item, dict):
30
+ if isinstance(item.get("message"), dict):
31
+ candidates.append(item["message"])
32
+ elif any(key in item for key in ("role", "content", "text")):
33
+ candidates.append(item)
27
34
  elif any(key in value for key in ("role", "content", "text")):
28
35
  candidates.append(value)
29
36
  elif isinstance(value, list):
@@ -41,10 +48,20 @@ def _structured_messages(
41
48
  text = _text_parts(item.get("content")) or _text_parts(item.get("text"))
42
49
  if not text:
43
50
  continue
51
+ sender = item.get("source") or item.get("sender")
52
+ recipient = item.get("recipient") or item.get("target")
53
+ if recipient is None and isinstance(item.get("recipients"), list):
54
+ recipient = ", ".join(
55
+ str(value).strip()
56
+ for value in item["recipients"]
57
+ if str(value).strip()
58
+ ) or None
59
+ sender = sender.strip() if isinstance(sender, str) and sender.strip() else None
60
+ recipient = recipient.strip() if isinstance(recipient, str) and recipient.strip() else None
44
61
  if role in {"user", "human"}:
45
- messages.append(_message(timestamp=timestamp, ordinal=ordinal, kind="user_message", sender="user", recipient=agent_path, text=text))
62
+ messages.append(_message(timestamp=timestamp, ordinal=ordinal, kind="user_message", sender=sender or "user", recipient=recipient or agent_path, text=text))
46
63
  elif role in {"assistant", "model", "agent"}:
47
- messages.append(_message(timestamp=timestamp, ordinal=ordinal, kind="assistant_message", sender=agent_path, recipient=None, text=text, phase="response"))
64
+ messages.append(_message(timestamp=timestamp, ordinal=ordinal, kind="assistant_message", sender=sender or agent_path, recipient=recipient, text=text, phase="response"))
48
65
  return messages
49
66
 
50
67
 
@@ -185,6 +185,11 @@ class AutoGenAdapter(FrameworkAdapter):
185
185
  source = self._agents.get(str(source_name)) or self.observe_agent(
186
186
  str(source_name), name=str(source_name)
187
187
  )
188
+ target_name = getattr(event, "target", None) or getattr(event, "recipient", None)
189
+ if target is None and target_name is not None:
190
+ target = self._agents.get(str(target_name)) or self.observe_agent(
191
+ str(target_name), name=str(target_name)
192
+ )
188
193
  content = getattr(event, "content", None)
189
194
  if event_name == "ThoughtEvent":
190
195
  return self.observe_message(
@@ -92,6 +92,12 @@ def _string(value: Any) -> str | None:
92
92
  return text or None
93
93
 
94
94
 
95
+ def _entity_label(entity: EntityRef | None) -> str | None:
96
+ if entity is None:
97
+ return None
98
+ return entity.name or entity.id
99
+
100
+
95
101
  @dataclass(frozen=True)
96
102
  class EntityRef:
97
103
  type: str
@@ -307,6 +313,7 @@ class MessageRecord:
307
313
  content_kind: str = "message"
308
314
  direction: Literal["sent", "received"] = "sent"
309
315
  attributes: Mapping[str, Any] = field(default_factory=dict)
316
+ content_payload: Any | None = None
310
317
 
311
318
 
312
319
  @dataclass(frozen=True)
@@ -390,28 +397,73 @@ class AdapterContext:
390
397
  return record.task
391
398
 
392
399
  def record_message(self, record: MessageRecord) -> EntityRef:
393
- reference = self.content.put_text(record.content, content_kind=record.content_kind) if record.content is not None else None
394
- attrs = {"message_id": record.message.id, "role": record.role, "task_id": record.task.id if record.task else None, "parent_message_id": record.parent_message.id if record.parent_message else None, **self.content.reference_attributes(reference), **dict(record.attributes)}
400
+ content_kind = f"{self.framework}.agent_message"
401
+ message_payload = record.content_payload if record.content_payload is not None else {
402
+ "message_id": record.message.id,
403
+ "text": record.content,
404
+ "sender": _entity_label(record.source),
405
+ "recipient": _entity_label(record.target),
406
+ "role": record.role,
407
+ "kind": "agent_message",
408
+ "phase": "received" if record.direction == "received" else "sent",
409
+ "content_state": "plaintext",
410
+ }
411
+ reference = self.content.put_json(message_payload, content_kind=content_kind) if record.content is not None else None
412
+ attrs = {"message_id": record.message.id, "role": record.role, "task_id": record.task.id if record.task else None, "parent_message_id": record.parent_message.id if record.parent_message else None, "sender_agent_id": record.source.id if record.source and record.source.type == "agent" else None, "recipient_agent_id": record.target.id if record.target and record.target.type == "agent" else None, **self.content.reference_attributes(reference), **dict(record.attributes)}
395
413
  event_type = "MESSAGE_SENT" if record.direction == "sent" else "MESSAGE_RECEIVED"
396
414
  self.emit(event_type, event_type, source=record.source, target=record.target or record.message, attributes=attrs)
397
415
  if reference is not None:
398
416
  content = EntityRef("observed_content", f"observed-content:message:sha256:{reference.sha256}", reference.content_kind, reference.to_dict())
399
- self.emit("MESSAGE_CONTENT_RECORDED", "HAS_MESSAGE_CONTENT", source=record.message, target=content, attributes={"message_id": record.message.id, **self.content.reference_attributes(reference)})
417
+ participants: list[tuple[EntityRef, str]] = []
418
+ for side, participant in (("sender", record.source), ("recipient", record.target)):
419
+ if participant is None or participant.type != "agent":
420
+ continue
421
+ if any(existing.id == participant.id for existing, _ in participants):
422
+ participants = [(existing, "sender_and_recipient" if existing.id == participant.id else existing_side) for existing, existing_side in participants]
423
+ continue
424
+ participants.append((participant, side))
425
+ if not participants:
426
+ participants = [(record.message, "message")]
427
+ for participant, side in participants:
428
+ self.emit(
429
+ "MESSAGE_CONTENT_RECORDED",
430
+ "HAS_MESSAGE_CONTENT",
431
+ source=participant,
432
+ target=content,
433
+ attributes={
434
+ "message_id": record.message.id,
435
+ "conversation_scope": "agent" if participant.type == "agent" else "message",
436
+ "conversation_side": side,
437
+ "conversation_agent_id": participant.id if participant.type == "agent" else None,
438
+ **self.content.reference_attributes(reference),
439
+ },
440
+ )
400
441
  return record.message
401
442
 
402
443
  def record_model_call(self, record: ModelCallRecord) -> EntityRef:
403
444
  if record.status == "request":
404
- event_type, relation, value, kind = "MODEL_REQUEST", "REQUESTS_MODEL_CALL", record.request, record.content_kind or "model_request"
445
+ event_type, relation, value, kind = "MODEL_REQUEST", "REQUESTS_MODEL_CALL", record.request, f"{self.framework}.model_request"
405
446
  elif record.status == "response":
406
- event_type, relation, value, kind = "MODEL_RESPONSE", "MODEL_CALL_RESPONDS", record.response, record.content_kind or "model_response"
447
+ event_type, relation, value, kind = "MODEL_RESPONSE", "MODEL_CALL_RESPONDS", record.response, f"{self.framework}.model_response"
407
448
  else:
408
- event_type, relation, value, kind = "MODEL_FAILURE", "MODEL_CALL_FAILED", record.response or record.request, record.content_kind or "model_failure"
449
+ event_type, relation, value, kind = "MODEL_FAILURE", "MODEL_CALL_FAILED", record.response or record.request, f"{self.framework}.model_failure"
409
450
  reference = self.content.put_text(value, content_kind=kind) if isinstance(value, str) else self.content.put_json(value, content_kind=kind) if value is not None else None
410
451
  attrs = {"model_call_id": record.model_call.id, "task_id": record.task.id if record.task else None, "requesting_agent_id": record.requesting_agent.id if record.requesting_agent else None, **self.content.reference_attributes(reference), **dict(record.attributes)}
411
452
  self.emit(event_type, relation, source=record.requesting_agent, target=record.model, attributes=attrs)
412
453
  if reference is not None:
413
454
  content = EntityRef("observed_content", f"observed-content:model:sha256:{reference.sha256}", reference.content_kind, reference.to_dict())
414
- self.emit("MODEL_CONTENT_RECORDED", "HAS_MODEL_CONTENT", source=record.model_call, target=content, attributes={"model_call_id": record.model_call.id, **self.content.reference_attributes(reference)})
455
+ self.emit(
456
+ "MODEL_CONTENT_RECORDED",
457
+ "HAS_MODEL_CONTENT",
458
+ source=record.requesting_agent or record.model_call,
459
+ target=content,
460
+ attributes={
461
+ "model_call_id": record.model_call.id,
462
+ "conversation_scope": "agent" if record.requesting_agent is not None else "model_call",
463
+ "conversation_agent_id": record.requesting_agent.id if record.requesting_agent else None,
464
+ **self.content.reference_attributes(reference),
465
+ },
466
+ )
415
467
  return record.model_call
416
468
 
417
469
  def record_tool_call(self, record: ToolCallRecord) -> EntityRef:
@@ -452,7 +504,14 @@ class FrameworkAdapter:
452
504
  return self.context.emit(event_type, relation, **kwargs)
453
505
 
454
506
  def agent(self, native_id: str | int, *, name: str | None = None, **attributes: Any) -> EntityRef:
455
- return self.entity("agent", native_id, name=name, attributes=attributes)
507
+ native = _ID_SAFE.sub("_", str(native_id).strip()) or "agent"
508
+ values = {
509
+ "conversation_scope": "framework_agent",
510
+ "conversation_agent_id": str(native_id),
511
+ "conversation_agent_path": f"/{self.framework_name}/{native}",
512
+ **attributes,
513
+ }
514
+ return self.entity("agent", native_id, name=name, attributes=values)
456
515
 
457
516
  def task(self, native_id: str | int, *, name: str | None = None, **attributes: Any) -> EntityRef:
458
517
  return self.entity("task", native_id, name=name, attributes=attributes)
@@ -14,13 +14,14 @@ class CAMELAdapter(FrameworkAdapter):
14
14
  events=("AGENT_CREATED", "AGENT_STARTED", "AGENT_STOPPED", "TASK_CREATED", "TASK_ASSIGNED", "TASK_STARTED", "TASK_UPDATED", "TASK_COMPLETED", "TASK_FAILED", "MESSAGE_SENT", "MESSAGE_RECEIVED", "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
- known_limitations=("WorkforceCallback does not expose every message boundary; callers must use an authoritative model/message hook.", "A created worker is not treated as assigned until CAMEL reports a task assignment."),
17
+ known_limitations=("WorkforceCallback LogEvent does not expose the emitting worker or recipient; those log messages remain explicitly unrouted unless a stream-chunk or model/message hook supplies the boundary.", "A created worker is not treated as assigned until CAMEL reports a task assignment."),
18
18
  )
19
19
 
20
20
  def __init__(self, context: AdapterContext) -> None:
21
21
  super().__init__(context)
22
22
  self._agents: dict[str, EntityRef] = {}
23
23
  self._tasks: dict[str, EntityRef] = {}
24
+ self._message_counter = 0
24
25
 
25
26
  @classmethod
26
27
  def compatibility(cls) -> FrameworkCompatibility:
@@ -91,19 +92,28 @@ class CAMELAdapter(FrameworkAdapter):
91
92
  payload = _payload(args, kwargs)
92
93
  if name in {"log_message", "log_stream_chunk"}:
93
94
  content = _string(_value(payload, "message", "text"))
94
- message_id = _native(payload, "message_id", "id", "task_id", "worker_id")
95
+ native_message_id = _value(payload, "message_id", "id", "task_id", "worker_id")
96
+ self._message_counter += 1
97
+ message_id = (
98
+ native_message_id
99
+ if isinstance(native_message_id, (str, int)) and str(native_message_id)
100
+ else f"{self.framework_name}-message-{self._message_counter}"
101
+ )
95
102
  worker_id = _value(payload, "worker_id", "agent_id")
96
103
  source = self._agents.get(str(worker_id)) if worker_id is not None else None
104
+ target_id = _value(payload, "target_worker_id", "recipient_worker_id", "target_agent_id", "recipient_agent_id", "receiver_id")
105
+ target = self._agents.get(str(target_id)) if target_id is not None else None
97
106
  return self.message(
98
107
  message_id,
99
108
  source,
100
- None,
109
+ target,
101
110
  content=content,
102
111
  role="stream" if name == "log_stream_chunk" else "log",
103
112
  attributes={
104
113
  "callback_name": name,
105
114
  "event_type": _value(payload, "event_type"),
106
115
  "metadata": _value(payload, "metadata"),
116
+ "message_id_source": "provider" if native_message_id is not None else "execweave_derived",
107
117
  },
108
118
  )
109
119
  if name == "log_task_decomposed":
@@ -121,6 +121,7 @@ class MetaGPTAdapter(FrameworkAdapter):
121
121
  role: str | None = None,
122
122
  task: EntityRef | None = None,
123
123
  received: bool = False,
124
+ content_payload: Any | None = None,
124
125
  **attributes: Any,
125
126
  ) -> EntityRef:
126
127
  message = self.entity("message", message_id, name="MetaGPT message", attributes={"provider": "metagpt"})
@@ -132,6 +133,7 @@ class MetaGPTAdapter(FrameworkAdapter):
132
133
  role=role,
133
134
  task=task,
134
135
  content=content,
136
+ content_payload=content_payload,
135
137
  direction="received" if received else "sent",
136
138
  attributes=attributes,
137
139
  )
@@ -145,19 +147,62 @@ class MetaGPTAdapter(FrameworkAdapter):
145
147
  source_name = getattr(message_object, "sent_from", None)
146
148
  target_name = getattr(message_object, "send_to", None)
147
149
  source = self._roles.get(str(source_name)) if source_name else None
148
- target = self._roles.get(str(target_name)) if isinstance(target_name, str) else None
149
- return self.observe_message(
150
- message_id,
151
- source=source,
152
- target=target,
153
- content=_text(getattr(message_object, "content", None)),
154
- role=_text(getattr(message_object, "role", None)),
155
- task=task,
156
- cause_by=_text(getattr(message_object, "cause_by", None)),
157
- metadata=getattr(message_object, "metadata", None),
158
- message_type=type(message_object).__name__,
150
+ content = _text(getattr(message_object, "content", None))
151
+ role = _text(getattr(message_object, "role", None))
152
+ common_attributes = {
153
+ "cause_by": _text(getattr(message_object, "cause_by", None)),
154
+ "metadata": getattr(message_object, "metadata", None),
155
+ "message_type": type(message_object).__name__,
159
156
  **attributes,
160
- )
157
+ }
158
+ target_names = None
159
+ if isinstance(target_name, (list, tuple, set, frozenset)):
160
+ target_names = list(target_name)
161
+ if isinstance(target_name, (set, frozenset)):
162
+ target_names.sort(key=str)
163
+ targets = [self._roles.get(str(name)) for name in target_names if name]
164
+ targets = [target for target in targets if target is not None]
165
+ else:
166
+ target = self._roles.get(str(target_name)) if isinstance(target_name, str) else None
167
+ targets = [target] if target is not None else []
168
+ shared_content_payload = None
169
+ if target_names is not None and targets:
170
+ recipient_labels = [target.name or target.id for target in targets]
171
+ shared_content_payload = {
172
+ "message_id": message_id,
173
+ "text": content,
174
+ "sender": source.name or source.id if source is not None else None,
175
+ "recipient": recipient_labels[0] if len(recipient_labels) == 1 else None,
176
+ "recipients": recipient_labels,
177
+ "role": role,
178
+ "kind": "agent_message",
179
+ "phase": "sent",
180
+ "content_state": "plaintext",
181
+ }
182
+ if not targets:
183
+ return self.observe_message(
184
+ message_id,
185
+ source=source,
186
+ target=None,
187
+ content=content,
188
+ role=role,
189
+ task=task,
190
+ content_payload=shared_content_payload,
191
+ **common_attributes,
192
+ )
193
+ message = None
194
+ for target in targets:
195
+ message = self.observe_message(
196
+ message_id,
197
+ source=source,
198
+ target=target,
199
+ content=content,
200
+ role=role,
201
+ task=task,
202
+ content_payload=shared_content_payload,
203
+ **common_attributes,
204
+ )
205
+ return message
161
206
 
162
207
  def observe_action(
163
208
  self,
File without changes
File without changes
File without changes