execweave 0.8.29__tar.gz → 0.8.31__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.29 → execweave-0.8.31}/PKG-INFO +1 -1
  2. {execweave-0.8.29 → execweave-0.8.31}/pyproject.toml +2 -2
  3. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/__init__.py +1 -1
  4. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_agent_panel.py +2 -0
  5. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_semantic_projection.py +150 -0
  6. {execweave-0.8.29 → execweave-0.8.31}/.gitignore +0 -0
  7. {execweave-0.8.29 → execweave-0.8.31}/LICENSE +0 -0
  8. {execweave-0.8.29 → execweave-0.8.31}/README.md +0 -0
  9. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/__main__.py +0 -0
  10. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/_conversation_records_core.py +0 -0
  11. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/_dashboard_shell_base.py +0 -0
  12. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/_http_proxy_base.py +0 -0
  13. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/_http_proxy_bounded.py +0 -0
  14. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/_http_proxy_stage.py +0 -0
  15. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/agent_bootstrap.py +0 -0
  16. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/agent_topology.py +0 -0
  17. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/agent_trace.py +0 -0
  18. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/agy_preview_sanitize.py +0 -0
  19. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/analysis.py +0 -0
  20. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/anthropic.py +0 -0
  21. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/anthropic_cli.py +0 -0
  22. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/anthropic_full_fidelity.py +0 -0
  23. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/antigravity_adapter.py +0 -0
  24. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/antigravity_adapter_base.py +0 -0
  25. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/antigravity_full_fidelity.py +0 -0
  26. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/antigravity_full_fidelity_base.py +0 -0
  27. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/antigravity_full_fidelity_collaboration_base.py +0 -0
  28. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/antigravity_hook_cli.py +0 -0
  29. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/antigravity_record.py +0 -0
  30. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/antigravity_subagent_linkage.py +0 -0
  31. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/antigravity_trace_capability.py +0 -0
  32. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/auto_specialized.py +0 -0
  33. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/backends.py +0 -0
  34. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/benchmark.py +0 -0
  35. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/claude_adapter.py +0 -0
  36. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/claude_child_transcript.py +0 -0
  37. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/claude_delegation.py +0 -0
  38. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/claude_full_fidelity.py +0 -0
  39. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/claude_hook_cli.py +0 -0
  40. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/claude_hook_contract.py +0 -0
  41. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/claude_model_observer.py +0 -0
  42. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/claude_record.py +0 -0
  43. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/cli.py +0 -0
  44. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_adapter.py +0 -0
  45. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_conversation.py +0 -0
  46. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_full_fidelity.py +0 -0
  47. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_hook_cli.py +0 -0
  48. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_hook_entry.py +0 -0
  49. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_hook_lifecycle.py +0 -0
  50. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_message_diagnostics.py +0 -0
  51. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_message_transport_diagnostics.py +0 -0
  52. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_record.py +0 -0
  53. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_rollout_structures.py +0 -0
  54. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_rollout_trace.py +0 -0
  55. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/codex_rollout_trace_base.py +0 -0
  56. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/collector.py +0 -0
  57. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/command.py +0 -0
  58. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/content_evidence.py +0 -0
  59. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/content_store.py +0 -0
  60. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_archive.py +0 -0
  61. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_message_identity.py +0 -0
  62. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_preview.py +0 -0
  63. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_preview_antigravity.py +0 -0
  64. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_preview_claude.py +0 -0
  65. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_preview_codex.py +0 -0
  66. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_preview_common.py +0 -0
  67. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_preview_generic.py +0 -0
  68. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_preview_lines.py +0 -0
  69. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_preview_transcript.py +0 -0
  70. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_records.py +0 -0
  71. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_records_antigravity.py +0 -0
  72. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_records_codex.py +0 -0
  73. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_records_common.py +0 -0
  74. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/conversation_records_ollama.py +0 -0
  75. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/correlation.py +0 -0
  76. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/cursor_adapter.py +0 -0
  77. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/cursor_delegation.py +0 -0
  78. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/cursor_delegation_base.py +0 -0
  79. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/cursor_full_fidelity.py +0 -0
  80. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/cursor_hook_cli.py +0 -0
  81. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/cursor_hook_contract.py +0 -0
  82. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/cursor_lifecycle.py +0 -0
  83. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/cursor_record.py +0 -0
  84. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/dashboard_shell.py +0 -0
  85. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/entry.py +0 -0
  86. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/evidence_availability.py +0 -0
  87. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/evidence_grade.py +0 -0
  88. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/fidelity.py +0 -0
  89. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/filesystem.py +0 -0
  90. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/focus.py +0 -0
  91. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/framework_adapters/__init__.py +0 -0
  92. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/framework_adapters/autogen.py +0 -0
  93. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/framework_adapters/base.py +0 -0
  94. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/framework_adapters/camel.py +0 -0
  95. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/framework_adapters/metagpt.py +0 -0
  96. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/framework_adapters/registry.py +0 -0
  97. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/graph.py +0 -0
  98. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/graph_ops.py +0 -0
  99. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/http_proxy.py +0 -0
  100. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/http_proxy_cli.py +0 -0
  101. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/inference_gateway.py +0 -0
  102. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/inference_gateway_cli.py +0 -0
  103. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/inference_gateway_full_fidelity.py +0 -0
  104. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/inference_identity.py +0 -0
  105. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/inference_identity_cli.py +0 -0
  106. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/integrity.py +0 -0
  107. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/integrity_cli.py +0 -0
  108. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/litellm_callback.py +0 -0
  109. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/litellm_callback_cli.py +0 -0
  110. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live.py +0 -0
  111. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live_core.py +0 -0
  112. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live_view.py +0 -0
  113. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live_view_extra_style.py +0 -0
  114. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live_view_markup.py +0 -0
  115. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live_view_process_layout.py +0 -0
  116. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live_view_readability.py +0 -0
  117. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live_view_script_a.py +0 -0
  118. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live_view_script_b.py +0 -0
  119. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live_view_script_c.py +0 -0
  120. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live_view_script_d.py +0 -0
  121. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/live_view_style.py +0 -0
  122. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/model_runtime.py +0 -0
  123. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/model_runtime_cli.py +0 -0
  124. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/model_runtime_full_fidelity.py +0 -0
  125. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/openai_compatible.py +0 -0
  126. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/openai_compatible_cli.py +0 -0
  127. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/openai_compatible_full_fidelity.py +0 -0
  128. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/opencode_adapter.py +0 -0
  129. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/opencode_event_contract.py +0 -0
  130. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/opencode_full_fidelity.py +0 -0
  131. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/opencode_hook_cli.py +0 -0
  132. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/opencode_plugin_cli.py +0 -0
  133. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/opencode_record.py +0 -0
  134. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/opencode_task_linkage.py +0 -0
  135. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/overhead_benchmark.py +0 -0
  136. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/provider_capability.py +0 -0
  137. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/provider_lifecycle.py +0 -0
  138. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/provider_record.py +0 -0
  139. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/rule_pack.py +0 -0
  140. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/rule_pack_cli.py +0 -0
  141. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/runtime_endpoint_availability.py +0 -0
  142. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/scalability_benchmark.py +0 -0
  143. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/schema.py +0 -0
  144. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/scope.py +0 -0
  145. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/semantic.py +0 -0
  146. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/sink.py +0 -0
  147. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/strace_backend.py +0 -0
  148. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/stream_assembly.py +0 -0
  149. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/theme.py +0 -0
  150. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/top.py +0 -0
  151. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/top_cli.py +0 -0
  152. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/validate.py +0 -0
  153. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/vendor/dagre.min.js +0 -0
  154. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/view_cli.py +0 -0
  155. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer.py +0 -0
  156. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_agent_panel_antigravity.py +0 -0
  157. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_agent_panel_claude.py +0 -0
  158. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_agent_panel_codex.py +0 -0
  159. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_agent_panel_cursor.py +0 -0
  160. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_agent_panel_default.py +0 -0
  161. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_agent_panel_ollama.py +0 -0
  162. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_agent_panel_opencode.py +0 -0
  163. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_agent_panel_registry.py +0 -0
  164. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_content_inspector.py +0 -0
  165. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_dashboard_clean.py +0 -0
  166. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_dashboard_focus.py +0 -0
  167. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_dashboard_hardening.py +0 -0
  168. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_dashboard_hardening_v2.py +0 -0
  169. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_dashboard_pr70.py +0 -0
  170. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_edge_decross.py +0 -0
  171. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_execution_flow.py +0 -0
  172. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_execution_flow_provider_normalization.py +0 -0
  173. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_execution_flow_safety.py +0 -0
  174. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_external_endpoints.py +0 -0
  175. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_flow_canvas.py +0 -0
  176. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_flow_layout.py +0 -0
  177. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_gif.py +0 -0
  178. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_layout_geometry.py +0 -0
  179. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_layout_v2.py +0 -0
  180. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_layout_v2_detached.py +0 -0
  181. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_limits.py +0 -0
  182. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_live_layout.py +0 -0
  183. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_projection.py +0 -0
  184. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_projection_base.py +0 -0
  185. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_projection_bridges.py +0 -0
  186. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/viewer_tool_mirror_identity.py +0 -0
  187. {execweave-0.8.29 → execweave-0.8.31}/src/execweave/workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: execweave
3
- Version: 0.8.29
3
+ Version: 0.8.31
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.29 release metadata
8
- version = "0.8.29"
7
+ # ExecWeave v0.8.31 release metadata
8
+ version = "0.8.31"
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.29"
3
+ __version__ = "0.8.31"
@@ -420,6 +420,8 @@ function toolOccurrenceSection(node){
420
420
  }
421
421
  function execweaveAssignedTaskText(agent){
422
422
  if(!agent||String(agent.type||'')!=='agent')return '';
423
+ const projected=attrs(agent),attached=projected.viewer_assigned_task_prompt||projected.viewer_assigned_task_name;
424
+ if(attached)return commandText(attached);
423
425
  const graph=rawGraph(),nodes=Array.isArray(graph.nodes)?graph.nodes:[],edges=Array.isArray(graph.edges)?graph.edges:[];
424
426
  const nodeMap=new Map(nodes.filter(node=>node&&node.id).map(node=>[String(node.id),node]));
425
427
  const exactAssignment=['ASSIGNED','AGENT','TASK'].join('_');
@@ -258,6 +258,151 @@ def orphan_audit(nodes: list[dict[str, Any]], edges: list[dict[str, Any]]) -> di
258
258
  return {"count": sum(item["count"] for item in by_type), "by_type": by_type}
259
259
 
260
260
 
261
+ _FRAMEWORK_TASK_RELATIONS = frozenset(
262
+ {"ASSIGNED_TO", "TASK_CREATED", "TASK_STARTED", "TASK_UPDATED", "TASK_COMPLETED", "TASK_FAILED"}
263
+ )
264
+
265
+
266
+ def _framework_task_edge_score(edge: dict[str, Any]) -> tuple[int, str, str]:
267
+ sequence = edge.get("last_sequence")
268
+ if not isinstance(sequence, int) or isinstance(sequence, bool):
269
+ sequence = edge.get("first_sequence")
270
+ if not isinstance(sequence, int) or isinstance(sequence, bool):
271
+ sequence = -1
272
+ return (sequence, str(edge.get("last_seen") or edge.get("first_seen") or ""), str(edge.get("id") or ""))
273
+
274
+
275
+ def _framework_task_text(task: dict[str, Any]) -> str:
276
+ attributes = _attrs(task)
277
+ for value in (
278
+ attributes.get("task_prompt"),
279
+ attributes.get("prompt"),
280
+ attributes.get("description"),
281
+ task.get("name"),
282
+ ):
283
+ if isinstance(value, str) and value.strip():
284
+ return value.strip()
285
+ return ""
286
+
287
+
288
+ def collapse_framework_tasks(
289
+ nodes: list[dict[str, Any]],
290
+ edges: list[dict[str, Any]],
291
+ ) -> tuple[list[dict[str, Any]], list[dict[str, Any]], dict[str, int]]:
292
+ """Fold framework task presentation into the owning agent, like provider views.
293
+
294
+ Framework adapters retain first-class task entities in the raw graph because task
295
+ lifecycle and prompt evidence must remain auditable. Provider dashboards expose
296
+ that same information on the agent card, however, so the viewer projection hides
297
+ only framework task nodes and copies the latest authoritative task text onto each
298
+ assigned framework agent. Provider graphs are untouched because they do not use
299
+ the framework-agent conversation scope.
300
+ """
301
+ node_by_id = {
302
+ str(node["id"]): node
303
+ for node in nodes
304
+ if isinstance(node, dict) and isinstance(node.get("id"), str)
305
+ }
306
+ framework_agents = {
307
+ node_id
308
+ for node_id, node in node_by_id.items()
309
+ if node.get("type") == "agent" and _attrs(node).get("conversation_scope") == "framework_agent"
310
+ }
311
+ if not framework_agents:
312
+ return nodes, edges, {
313
+ "framework_task_node_count": 0,
314
+ "framework_task_content_node_count": 0,
315
+ "framework_task_agent_count": 0,
316
+ }
317
+
318
+ task_ids: set[str] = set()
319
+ tasks_by_agent: dict[str, list[tuple[tuple[int, str, str], str]]] = defaultdict(list)
320
+ for edge in edges:
321
+ if not isinstance(edge, dict) or edge.get("relation") not in _FRAMEWORK_TASK_RELATIONS:
322
+ continue
323
+ source = str(edge.get("source") or "")
324
+ target = str(edge.get("target") or "")
325
+ source_node = node_by_id.get(source)
326
+ target_node = node_by_id.get(target)
327
+ task_id = source if source_node and source_node.get("type") == "task" else target if target_node and target_node.get("type") == "task" else None
328
+ if task_id is None:
329
+ continue
330
+ agent_id = source if source in framework_agents else target if target in framework_agents else None
331
+ if agent_id is None:
332
+ continue
333
+ task_ids.add(task_id)
334
+ tasks_by_agent[agent_id].append((_framework_task_edge_score(edge), task_id))
335
+
336
+ if not task_ids:
337
+ return nodes, edges, {
338
+ "framework_task_node_count": 0,
339
+ "framework_task_content_node_count": 0,
340
+ "framework_task_agent_count": 0,
341
+ }
342
+
343
+ projected_nodes: list[dict[str, Any]] = []
344
+ for node in nodes:
345
+ node_id = str(node.get("id") or "")
346
+ if node_id in task_ids:
347
+ continue
348
+ if node.get("type") == "agent" and node_id in framework_agents:
349
+ candidates: dict[str, tuple[int, str, str]] = {}
350
+ for score, task_id in tasks_by_agent.get(node_id, []):
351
+ previous = candidates.get(task_id)
352
+ if previous is None or score > previous:
353
+ candidates[task_id] = score
354
+ ordered_ids = [
355
+ task_id
356
+ for task_id, _ in sorted(
357
+ candidates.items(),
358
+ key=lambda item: (item[1], item[0]),
359
+ reverse=True,
360
+ )
361
+ ]
362
+ latest_task = node_by_id.get(ordered_ids[0]) if ordered_ids else None
363
+ attributes = dict(_attrs(node))
364
+ attributes["viewer_assigned_task_ids"] = ordered_ids
365
+ attributes["viewer_assigned_task_count"] = len(ordered_ids)
366
+ if latest_task is not None:
367
+ attributes["viewer_assigned_task_id"] = str(latest_task.get("id") or "")
368
+ attributes["viewer_assigned_task_name"] = str(latest_task.get("name") or "")
369
+ task_text = _framework_task_text(latest_task)
370
+ if task_text:
371
+ attributes["viewer_assigned_task_prompt"] = task_text
372
+ projected_nodes.append({**node, "attributes": attributes})
373
+ continue
374
+ projected_nodes.append(node)
375
+
376
+ hidden_content_ids: set[str] = set()
377
+ for node in nodes:
378
+ node_id = str(node.get("id") or "")
379
+ if node.get("type") != "observed_content" or not node_id:
380
+ continue
381
+ incident = [
382
+ edge
383
+ for edge in edges
384
+ if edge.get("source") == node_id or edge.get("target") == node_id
385
+ ]
386
+ if incident and all(
387
+ (edge.get("source") in task_ids or edge.get("target") in task_ids)
388
+ for edge in incident
389
+ ):
390
+ hidden_content_ids.add(node_id)
391
+
392
+ hidden_ids = task_ids | hidden_content_ids
393
+ projected_nodes = [node for node in projected_nodes if str(node.get("id") or "") not in hidden_content_ids]
394
+ projected_edges = [
395
+ edge
396
+ for edge in edges
397
+ if edge.get("source") not in hidden_ids and edge.get("target") not in hidden_ids
398
+ ]
399
+ return projected_nodes, projected_edges, {
400
+ "framework_task_node_count": len(task_ids),
401
+ "framework_task_content_node_count": len(hidden_content_ids),
402
+ "framework_task_agent_count": len(tasks_by_agent),
403
+ }
404
+
405
+
261
406
  def project_provider_neutral_viewer_graph(graph: dict[str, Any]) -> dict[str, Any]:
262
407
  """Provider-neutral display projection; raw graph/evidence remains unchanged."""
263
408
  from . import viewer_projection_base as base
@@ -268,6 +413,7 @@ def project_provider_neutral_viewer_graph(graph: dict[str, Any]) -> dict[str, An
268
413
  nodes = [deepcopy(n) for n in filtered.get("nodes", []) if isinstance(n, dict)]
269
414
  edges = [deepcopy(e) for e in filtered.get("edges", []) if isinstance(e, dict)]
270
415
  entries = _entries(graph)
416
+ nodes, edges, framework_tasks = collapse_framework_tasks(nodes, edges)
271
417
  nodes, edges, inference = collapse_inference_requests(nodes, edges, entries)
272
418
  roots, _ = _roots(nodes, entries)
273
419
  nodes, edges, files = collapse_orphan_files(nodes, edges, roots[0] if len(roots) == 1 else None)
@@ -297,6 +443,7 @@ def project_provider_neutral_viewer_graph(graph: dict[str, Any]) -> dict[str, An
297
443
  or inference["collapsed_request_count"]
298
444
  or local is not None
299
445
  or files is not None
446
+ or framework_tasks["framework_task_node_count"]
300
447
  )
301
448
  if not topology_changed:
302
449
  if audit["count"]:
@@ -313,6 +460,9 @@ def project_provider_neutral_viewer_graph(graph: dict[str, Any]) -> dict[str, An
313
460
  "orphan_file_node_count": len(files.get("nodes") or []) if files else 0,
314
461
  "inference_request_count": inference["collapsed_request_count"], "logical_inference_count": inference["logical_inference_count"],
315
462
  "direct_inference_edge_count": inference["direct_inference_edge_count"], "unresolved_inference_requests": inference["unresolved"],
463
+ "framework_task_node_count": framework_tasks["framework_task_node_count"],
464
+ "framework_task_content_node_count": framework_tasks["framework_task_content_node_count"],
465
+ "framework_task_agent_count": framework_tasks["framework_task_agent_count"],
316
466
  "internal_hook_node_count": len(hook_nodes), "internal_hook_edge_count": len(hook_edges),
317
467
  "projection_topology_changed": True,
318
468
  })
File without changes
File without changes
File without changes