wagent-framework 2.0.0a2__tar.gz → 2.0.0a3__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 (162) hide show
  1. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/PKG-INFO +5 -5
  2. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/README.md +4 -4
  3. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/setup.py +1 -1
  4. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_cli.py +1 -1
  5. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_local_runtime.py +21 -1
  6. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_sessions.py +11 -0
  7. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_tui.py +181 -0
  8. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/__init__.py +9 -7
  9. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/local_runtime.py +10 -1
  10. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/sessions.py +65 -18
  11. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/mcp_client.py +1 -1
  12. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tui.py +235 -6
  13. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/wagent_framework.egg-info/PKG-INFO +5 -5
  14. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/LICENSE +0 -0
  15. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/setup.cfg +0 -0
  16. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test.py +0 -0
  17. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_agent_templates.py +0 -0
  18. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_aop.py +0 -0
  19. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_component_chain.py +0 -0
  20. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_compositions.py +0 -0
  21. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_config.py +0 -0
  22. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_container.py +0 -0
  23. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_deployment.py +0 -0
  24. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_distributed_lock.py +0 -0
  25. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_doctor.py +0 -0
  26. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_evaluation.py +0 -0
  27. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_event_bus.py +0 -0
  28. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_http_provider_templates.py +0 -0
  29. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_kernel_events.py +0 -0
  30. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_kernel_loading.py +0 -0
  31. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_kernel_plugins.py +0 -0
  32. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_kernel_registry.py +0 -0
  33. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_legacy_compat.py +0 -0
  34. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_lifecycle.py +0 -0
  35. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_mcp_client.py +0 -0
  36. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_model_invocation.py +0 -0
  37. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_model_probing.py +0 -0
  38. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_model_routing.py +0 -0
  39. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_model_testing.py +0 -0
  40. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_model_types.py +0 -0
  41. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_observability.py +0 -0
  42. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_openai_compatible_provider.py +0 -0
  43. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_package_integrity.py +0 -0
  44. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_pricing.py +0 -0
  45. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_react_agent.py +0 -0
  46. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_sandbox.py +0 -0
  47. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_sandbox_runtime.py +0 -0
  48. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_scanner.py +0 -0
  49. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_timeout.py +0 -0
  50. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_tool_adapters.py +0 -0
  51. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_tool_loading.py +0 -0
  52. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_tool_runtime.py +0 -0
  53. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_workflow_adapters.py +0 -0
  54. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/tests/test_workflow_engine.py +0 -0
  55. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/__main__.py +0 -0
  56. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/agents/__init__.py +0 -0
  57. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/agents/persistence.py +0 -0
  58. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/agents/react.py +0 -0
  59. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/agents/templates.py +0 -0
  60. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/agents/types.py +0 -0
  61. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/aop/__init__.py +0 -0
  62. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/aop/aspects.py +0 -0
  63. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/aop/joinpoint.py +0 -0
  64. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/aop/pointcut.py +0 -0
  65. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/aop/proxy_factory.py +0 -0
  66. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/cli.py +0 -0
  67. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/compat.py +0 -0
  68. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/compositions.py +0 -0
  69. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/config/__init__.py +0 -0
  70. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/config/dynamic_config.py +0 -0
  71. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/container/__init__.py +0 -0
  72. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/container/bean_factory.py +0 -0
  73. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/container/reflection_cache.py +0 -0
  74. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/core/__init__.py +0 -0
  75. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/core/agent.py +0 -0
  76. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/core/decorators.py +0 -0
  77. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/core/doctor.py +0 -0
  78. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/core/event_bus.py +0 -0
  79. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/deployment/__init__.py +0 -0
  80. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/deployment/fastapi_depends.py +0 -0
  81. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/distributed/__init__.py +0 -0
  82. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/distributed/lock.py +0 -0
  83. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/distributed/lock_pool.py +0 -0
  84. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/evaluation.py +0 -0
  85. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/exceptions/__init__.py +0 -0
  86. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/exceptions/framework_errors.py +0 -0
  87. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/kernel/__init__.py +0 -0
  88. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/kernel/events.py +0 -0
  89. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/kernel/exceptions.py +0 -0
  90. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/kernel/loading.py +0 -0
  91. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/kernel/plugins.py +0 -0
  92. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/kernel/registry.py +0 -0
  93. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/kernel/scope.py +0 -0
  94. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/lifecycle/__init__.py +0 -0
  95. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/lifecycle/graceful_shutdown.py +0 -0
  96. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/lifecycle/manager.py +0 -0
  97. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/lifecycle/order.py +0 -0
  98. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/migration/__init__.py +0 -0
  99. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/migration/migrate_previous.py +0 -0
  100. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/__init__.py +0 -0
  101. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/errors.py +0 -0
  102. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/http_provider.py +0 -0
  103. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/invocation.py +0 -0
  104. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/native_mappings.py +0 -0
  105. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/openai_compatible.py +0 -0
  106. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/pricing.py +0 -0
  107. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/probing.py +0 -0
  108. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/provider.py +0 -0
  109. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/routing.py +0 -0
  110. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/templates.py +0 -0
  111. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/models/types.py +0 -0
  112. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/observability/__init__.py +0 -0
  113. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/observability/health.py +0 -0
  114. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/observability/logging.py +0 -0
  115. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/observability/metrics.py +0 -0
  116. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/observability/tracing.py +0 -0
  117. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/resilience/__init__.py +0 -0
  118. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/resilience/bulkhead.py +0 -0
  119. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/resilience/timeout.py +0 -0
  120. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/sandbox/__init__.py +0 -0
  121. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/sandbox/docker.py +0 -0
  122. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/sandbox/process.py +0 -0
  123. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/sandbox/registry.py +0 -0
  124. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/sandbox/types.py +0 -0
  125. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/sandbox/unsafe_local.py +0 -0
  126. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/scanner/__init__.py +0 -0
  127. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/scanner/cache.py +0 -0
  128. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/scanner/parallel_scanner.py +0 -0
  129. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/security/__init__.py +0 -0
  130. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/security/mcp_auth.py +0 -0
  131. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/skills/__init__.py +0 -0
  132. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/skills/sandbox/__init__.py +0 -0
  133. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/skills/sandbox/nsjail_sandbox.py +0 -0
  134. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/skills/sandbox/wasm_sandbox.py +0 -0
  135. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/skills/signature.py +0 -0
  136. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/skills/skill.py +0 -0
  137. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/testing/__init__.py +0 -0
  138. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/testing/mock_utils.py +0 -0
  139. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/testing/model_provider.py +0 -0
  140. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/__init__.py +0 -0
  141. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/command_adapter.py +0 -0
  142. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/execution.py +0 -0
  143. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/http_adapter.py +0 -0
  144. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/langchain_adapter.py +0 -0
  145. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/loading.py +0 -0
  146. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/mcp_adapter.py +0 -0
  147. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/policies.py +0 -0
  148. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/python.py +0 -0
  149. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/registry.py +0 -0
  150. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/sandbox_adapter.py +0 -0
  151. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/tools/types.py +0 -0
  152. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/workflows/__init__.py +0 -0
  153. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/workflows/adapters.py +0 -0
  154. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/workflows/engine.py +0 -0
  155. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/workflows/persistence.py +0 -0
  156. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/workflows/registry.py +0 -0
  157. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/w_agent/workflows/types.py +0 -0
  158. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/wagent_framework.egg-info/SOURCES.txt +0 -0
  159. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/wagent_framework.egg-info/dependency_links.txt +0 -0
  160. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/wagent_framework.egg-info/entry_points.txt +0 -0
  161. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/wagent_framework.egg-info/requires.txt +0 -0
  162. {wagent_framework-2.0.0a2 → wagent_framework-2.0.0a3}/wagent_framework.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wagent-framework
3
- Version: 2.0.0a2
3
+ Version: 2.0.0a3
4
4
  Summary: Open, composable Python framework for building agents
5
5
  Home-page: https://github.com/LuckyStar2456/W-Agent-FrameWork
6
6
  Author: LuckyStar2456
@@ -73,7 +73,7 @@ Dynamic: summary
73
73
 
74
74
  W-Agent 是一个面向本地开发者的 Python 开源 Agent 开发框架。它的目标不是提供托管平台或固定 Harness,而是提供稳定、可扩展的协议与可自由装配的模块,让开发者能够替换模型、路由、Agent Loop、Workflow、工具、状态、沙箱和界面实现。
75
75
 
76
- 当前稳定发布版本是 `1.5.2`,最新 Alpha 为 `2.0.0a2`。1.x 工程底座继续保留,微内核、模型、工具、单 Agent 与本地 Workflow 基础已经实现,其余下一代能力按路线图分阶段交付。文档使用明确状态标记,避免把规划能力描述为现有能力。
76
+ 当前稳定发布版本是 `1.5.2`,最新 Alpha 为 `2.0.0a2`,仓库主线为 `2.0.0a3` 开发版。1.x 工程底座继续保留,微内核、模型、工具、单 Agent 与本地 Workflow 基础已经实现,其余下一代能力按路线图分阶段交付。文档使用明确状态标记,避免把规划能力描述为现有能力。
77
77
 
78
78
  ## 状态标记
79
79
 
@@ -109,7 +109,7 @@ W-Agent 遵循以下原则:
109
109
  - Skill 加载、签名校验、MCP JWT 认证、Redis 分布式锁。
110
110
  - LangChain 工具适配、FastAPI 示例集成和测试辅助设施。
111
111
 
112
- 以下 Phase 1 能力在当前 `2.0.0a2` 源码中为 `Implemented`:
112
+ 以下 Phase 1 能力在当前 `2.0.0a3` 源码中为 `Implemented`:
113
113
 
114
114
  - `PluginSpec`、装饰器、YAML 引用与 Python entry point 发现。
115
115
  - 统一、版本感知、分作用域的能力注册表与不可变快照。
@@ -175,7 +175,7 @@ pip install "wagent-framework[wasm]"
175
175
  pip install "wagent-framework[tui]"
176
176
  ```
177
177
 
178
- PyPI 1.x 支持 Python 3.9+;`2.0.0a2` 与后续 2.x Alpha 要求 Python 3.11+。
178
+ PyPI 1.x 支持 Python 3.9+;`2.0.0a2`、当前 `2.0.0a3` 源码与后续 2.x Alpha 要求 Python 3.11+。
179
179
 
180
180
  ## 1.x 最小示例
181
181
 
@@ -221,7 +221,7 @@ wagent run "hello" --confirm-model-call --json
221
221
  wagent evaluate cases.json --confirm-model-call --report report.json
222
222
  ```
223
223
 
224
- CLI 和 TUI 只调用公开 Python API。当前 CLI/TUI 基础标记为 `Experimental`:已覆盖初始化、模板列表、安全/主动端点探测、装配管理/离线预览、本地 Session 生命周期、需逐次确认的配置化 Agent 运行与 Token 汇总、Agent Checkpoint 脱敏列表,以及 CLI 的 Catalog 工具选择、独立代码加载确认、权限授予、精确 Call ID 审批恢复和 CLI/TUI 隐私安全本地评测;TUI 工具批准执行、通用插件操作、Workflow Checkpoint 汇总和实时事件查看仍为 `Planned`。
224
+ CLI 和 TUI 只调用公开 Python API。当前 CLI/TUI 基础标记为 `Experimental`:已覆盖初始化、模板列表、安全/主动端点探测、装配管理/离线预览、本地 Session 生命周期、需逐次确认的配置化 Agent 运行与 Token 汇总、Agent Checkpoint 脱敏列表、Catalog 工具选择、独立代码加载确认、权限授予、精确 Call ID 审批恢复、脱敏实时 RunEvent,以及 CLI/TUI 隐私安全本地评测;通用插件操作和 Workflow Checkpoint 汇总仍为 `Planned`。
225
225
 
226
226
  ## 工程装配分享
227
227
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  W-Agent 是一个面向本地开发者的 Python 开源 Agent 开发框架。它的目标不是提供托管平台或固定 Harness,而是提供稳定、可扩展的协议与可自由装配的模块,让开发者能够替换模型、路由、Agent Loop、Workflow、工具、状态、沙箱和界面实现。
6
6
 
7
- 当前稳定发布版本是 `1.5.2`,最新 Alpha 为 `2.0.0a2`。1.x 工程底座继续保留,微内核、模型、工具、单 Agent 与本地 Workflow 基础已经实现,其余下一代能力按路线图分阶段交付。文档使用明确状态标记,避免把规划能力描述为现有能力。
7
+ 当前稳定发布版本是 `1.5.2`,最新 Alpha 为 `2.0.0a2`,仓库主线为 `2.0.0a3` 开发版。1.x 工程底座继续保留,微内核、模型、工具、单 Agent 与本地 Workflow 基础已经实现,其余下一代能力按路线图分阶段交付。文档使用明确状态标记,避免把规划能力描述为现有能力。
8
8
 
9
9
  ## 状态标记
10
10
 
@@ -40,7 +40,7 @@ W-Agent 遵循以下原则:
40
40
  - Skill 加载、签名校验、MCP JWT 认证、Redis 分布式锁。
41
41
  - LangChain 工具适配、FastAPI 示例集成和测试辅助设施。
42
42
 
43
- 以下 Phase 1 能力在当前 `2.0.0a2` 源码中为 `Implemented`:
43
+ 以下 Phase 1 能力在当前 `2.0.0a3` 源码中为 `Implemented`:
44
44
 
45
45
  - `PluginSpec`、装饰器、YAML 引用与 Python entry point 发现。
46
46
  - 统一、版本感知、分作用域的能力注册表与不可变快照。
@@ -106,7 +106,7 @@ pip install "wagent-framework[wasm]"
106
106
  pip install "wagent-framework[tui]"
107
107
  ```
108
108
 
109
- PyPI 1.x 支持 Python 3.9+;`2.0.0a2` 与后续 2.x Alpha 要求 Python 3.11+。
109
+ PyPI 1.x 支持 Python 3.9+;`2.0.0a2`、当前 `2.0.0a3` 源码与后续 2.x Alpha 要求 Python 3.11+。
110
110
 
111
111
  ## 1.x 最小示例
112
112
 
@@ -152,7 +152,7 @@ wagent run "hello" --confirm-model-call --json
152
152
  wagent evaluate cases.json --confirm-model-call --report report.json
153
153
  ```
154
154
 
155
- CLI 和 TUI 只调用公开 Python API。当前 CLI/TUI 基础标记为 `Experimental`:已覆盖初始化、模板列表、安全/主动端点探测、装配管理/离线预览、本地 Session 生命周期、需逐次确认的配置化 Agent 运行与 Token 汇总、Agent Checkpoint 脱敏列表,以及 CLI 的 Catalog 工具选择、独立代码加载确认、权限授予、精确 Call ID 审批恢复和 CLI/TUI 隐私安全本地评测;TUI 工具批准执行、通用插件操作、Workflow Checkpoint 汇总和实时事件查看仍为 `Planned`。
155
+ CLI 和 TUI 只调用公开 Python API。当前 CLI/TUI 基础标记为 `Experimental`:已覆盖初始化、模板列表、安全/主动端点探测、装配管理/离线预览、本地 Session 生命周期、需逐次确认的配置化 Agent 运行与 Token 汇总、Agent Checkpoint 脱敏列表、Catalog 工具选择、独立代码加载确认、权限授予、精确 Call ID 审批恢复、脱敏实时 RunEvent,以及 CLI/TUI 隐私安全本地评测;通用插件操作和 Workflow Checkpoint 汇总仍为 `Planned`。
156
156
 
157
157
  ## 工程装配分享
158
158
 
@@ -8,7 +8,7 @@ with open(os.path.join(here, "README.md"), "r", encoding="utf-8") as f:
8
8
 
9
9
  setup(
10
10
  name="wagent-framework",
11
- version="2.0.0a2",
11
+ version="2.0.0a3",
12
12
  description="Open, composable Python framework for building agents",
13
13
  long_description=long_description,
14
14
  long_description_content_type="text/markdown",
@@ -25,7 +25,7 @@ def test_cli_version_and_profile_json_are_machine_readable():
25
25
  profiles = runner.invoke(app, ["profile", "list", "--json"])
26
26
 
27
27
  assert version.exit_code == 0
28
- assert "2.0.0a2" in version.stdout
28
+ assert "2.0.0a3" in version.stdout
29
29
  assert profiles.exit_code == 0
30
30
  assert [item["key"] for item in json.loads(profiles.stdout)] == [
31
31
  "customer-support",
@@ -97,6 +97,11 @@ def _templates(captured):
97
97
  @pytest.mark.asyncio
98
98
  async def test_local_runtime_assembles_full_text_run_and_persists_session(tmp_path):
99
99
  captured = {}
100
+ events = []
101
+
102
+ async def record_event(event):
103
+ events.append(event)
104
+
100
105
  runtime = assemble_local_runtime(
101
106
  _config(),
102
107
  tmp_path / ".wagent",
@@ -104,9 +109,20 @@ async def test_local_runtime_assembles_full_text_run_and_persists_session(tmp_pa
104
109
  environ={"TEST_MODEL_KEY": "not-persisted"},
105
110
  )
106
111
 
107
- run = await runtime.run("hi", session_title="Configured run")
112
+ run = await runtime.run(
113
+ "hi",
114
+ session_title="Configured run",
115
+ event_callback=record_event,
116
+ )
108
117
 
109
118
  assert run.result.output == "hello"
119
+ assert [event.type.value for event in events] == [
120
+ "run-started",
121
+ "model-started",
122
+ "model-completed",
123
+ "token-usage",
124
+ "run-completed",
125
+ ]
110
126
  assert run.result.usage == TokenUsage(3, 2)
111
127
  assert run.result.usage_complete is True
112
128
  assert len(run.result.attempts) == 1
@@ -393,17 +409,21 @@ async def test_local_runtime_selects_catalog_tools_and_resumes_approval(tmp_path
393
409
  assert pending.result.pending_tool_call.id == "save-1"
394
410
  assert writes == []
395
411
 
412
+ resumed_events = []
396
413
  completed = await runtime.resume(
397
414
  pending.session.session_id,
398
415
  pending.result.run_id,
399
416
  permissions=frozenset({"notes.write"}),
400
417
  approved_call_ids=frozenset({"save-1"}),
418
+ event_callback=resumed_events.append,
401
419
  )
402
420
 
403
421
  assert completed.result.output == "saved"
404
422
  assert completed.result.stop_reason.value == "completed"
405
423
  assert writes == ["value"]
406
424
  assert completed.result.usage == TokenUsage(7, 2)
425
+ assert resumed_events[0].type.value == "run-resumed"
426
+ assert resumed_events[-1].type.value == "run-completed"
407
427
 
408
428
 
409
429
  def test_local_runtime_tool_selection_requires_budget_and_authorized_catalog(tmp_path):
@@ -132,6 +132,17 @@ async def test_session_resume_updates_existing_run_without_duplicate_input(tmp_p
132
132
  run_id="run-approval",
133
133
  )
134
134
 
135
+ with pytest.raises(SessionError, match="streamed approval resume"):
136
+ await manager.resume_agent(
137
+ loop,
138
+ "approval-1",
139
+ "run-approval",
140
+ tool_context=ToolExecutionContext(
141
+ approved_call_ids=frozenset({"call-1"})
142
+ ),
143
+ event_callback=lambda event: None,
144
+ )
145
+
135
146
  resumed = await manager.resume_agent(
136
147
  loop,
137
148
  "approval-1",
@@ -12,6 +12,8 @@ from w_agent import (
12
12
  ModelDescriptor,
13
13
  ModelMessage,
14
14
  ModelCost,
15
+ RunEvent,
16
+ RunEventType,
15
17
  RunResult,
16
18
  StopReason,
17
19
  TokenUsage,
@@ -109,6 +111,185 @@ async def test_tui_run_requires_explicit_confirmation_before_config_or_network(
109
111
  assert "config" in str(app.query_one("#run-result").content).lower()
110
112
 
111
113
 
114
+ @pytest.mark.asyncio
115
+ async def test_tui_tool_code_requires_separate_import_confirmation(
116
+ tmp_path,
117
+ monkeypatch,
118
+ ):
119
+ loaded = []
120
+ monkeypatch.setattr(
121
+ tui_module,
122
+ "load_tool_entries",
123
+ lambda entries: loaded.append(entries) or {},
124
+ )
125
+ app = WAgentTui(tmp_path)
126
+
127
+ async with app.run_test(size=(140, 65)) as pilot:
128
+ app.query_one(TabbedContent).active = "run"
129
+ await pilot.pause()
130
+ app.query_one("#run-prompt").value = "hello"
131
+ app.query_one("#run-tool-entries").value = "my_tools:bindings"
132
+ app.query_one("#run-confirm").value = "RUN"
133
+ await pilot.click("#run-button")
134
+ await pilot.pause()
135
+
136
+ assert loaded == []
137
+ assert "type LOAD TOOLS" in str(app.query_one("#run-result").content)
138
+
139
+
140
+ @pytest.mark.asyncio
141
+ async def test_tui_projects_live_events_without_prompt_or_arguments(
142
+ tmp_path,
143
+ monkeypatch,
144
+ ):
145
+ class Runtime:
146
+ closed = False
147
+
148
+ async def __aenter__(self):
149
+ return self
150
+
151
+ async def __aexit__(self, *exc_info):
152
+ self.closed = True
153
+
154
+ async def run(self, prompt, **kwargs):
155
+ assert prompt == "SECRET_PROMPT"
156
+ assert kwargs["permissions"] == frozenset({"notes.write"})
157
+ await kwargs["event_callback"](
158
+ RunEvent(
159
+ 1,
160
+ RunEventType.TOOL_REQUESTED,
161
+ "run-live",
162
+ {
163
+ "step": 1,
164
+ "call_id": "call-1",
165
+ "name": "save_note",
166
+ "arguments": {"text": "SECRET_ARGUMENT"},
167
+ "text": "SECRET_OUTPUT",
168
+ },
169
+ session_id="session-live",
170
+ )
171
+ )
172
+ return SimpleNamespace(
173
+ session=SimpleNamespace(session_id="session-live"),
174
+ result=RunResult(
175
+ "run-live",
176
+ StopReason.COMPLETED,
177
+ "done",
178
+ (ModelMessage.text(MessageRole.ASSISTANT, "done"),),
179
+ (),
180
+ steps=1,
181
+ tool_calls=1,
182
+ ),
183
+ )
184
+
185
+ runtime = Runtime()
186
+ loaded = []
187
+ monkeypatch.setattr(tui_module, "load_local_runtime_config", lambda path: object())
188
+ monkeypatch.setattr(
189
+ tui_module,
190
+ "load_tool_entries",
191
+ lambda entries: loaded.append(entries) or {"save_note": object()},
192
+ )
193
+ monkeypatch.setattr(
194
+ tui_module,
195
+ "assemble_local_runtime",
196
+ lambda config, state_root, *, tool_bindings: runtime,
197
+ )
198
+ app = WAgentTui(tmp_path)
199
+
200
+ async with app.run_test(size=(150, 70)) as pilot:
201
+ app.query_one(TabbedContent).active = "run"
202
+ await pilot.pause()
203
+ app.query_one("#run-prompt").value = "SECRET_PROMPT"
204
+ app.query_one("#run-tool-entries").value = "my_tools:bindings"
205
+ app.query_one("#run-permissions").value = "notes.write"
206
+ app.query_one("#run-tool-confirm").value = "LOAD TOOLS"
207
+ app.query_one("#run-confirm").value = "RUN"
208
+ await pilot.click("#run-button")
209
+ await pilot.pause()
210
+
211
+ rendered = str(app.query_one("#run-events").content)
212
+ assert "tool-requested" in rendered
213
+ assert "call_id=call-1" in rendered
214
+ assert "SECRET_PROMPT" not in rendered
215
+ assert "SECRET_ARGUMENT" not in rendered
216
+ assert "SECRET_OUTPUT" not in rendered
217
+ assert loaded == [("my_tools:bindings",)]
218
+ assert runtime.closed is True
219
+
220
+
221
+ @pytest.mark.asyncio
222
+ async def test_tui_resumes_exact_approved_call_with_live_events(
223
+ tmp_path,
224
+ monkeypatch,
225
+ ):
226
+ class Runtime:
227
+ async def __aenter__(self):
228
+ return self
229
+
230
+ async def __aexit__(self, *exc_info):
231
+ pass
232
+
233
+ async def resume(self, session_id, run_id, **kwargs):
234
+ assert session_id == "session-1"
235
+ assert run_id == "run-1"
236
+ assert kwargs["approved_call_ids"] == frozenset({"call-1"})
237
+ assert kwargs["permissions"] == frozenset({"notes.write"})
238
+ await kwargs["event_callback"](
239
+ RunEvent(
240
+ 2,
241
+ RunEventType.RUN_RESUMED,
242
+ "run-1",
243
+ {"checkpoint_status": "resuming", "pending_call_id": "call-1"},
244
+ session_id="session-1",
245
+ )
246
+ )
247
+ return SimpleNamespace(
248
+ session=SimpleNamespace(session_id="session-1"),
249
+ result=RunResult(
250
+ "run-1",
251
+ StopReason.COMPLETED,
252
+ "approved",
253
+ (ModelMessage.text(MessageRole.ASSISTANT, "approved"),),
254
+ (),
255
+ steps=2,
256
+ tool_calls=1,
257
+ ),
258
+ )
259
+
260
+ runtime = Runtime()
261
+ monkeypatch.setattr(tui_module, "load_local_runtime_config", lambda path: object())
262
+ monkeypatch.setattr(tui_module, "load_tool_entries", lambda entries: {})
263
+ monkeypatch.setattr(
264
+ tui_module,
265
+ "assemble_local_runtime",
266
+ lambda config, state_root, *, tool_bindings: runtime,
267
+ )
268
+ app = WAgentTui(tmp_path)
269
+
270
+ async with app.run_test(size=(150, 75)) as pilot:
271
+ app.query_one(TabbedContent).active = "checkpoints"
272
+ await pilot.pause()
273
+ app.query_one("#checkpoint-session").value = "session-1"
274
+ app.query_one("#checkpoint-run").value = "run-1"
275
+ app.query_one("#checkpoint-call").value = "call-1"
276
+ app.query_one("#checkpoint-tool-entries").value = "my_tools:bindings"
277
+ app.query_one("#checkpoint-permissions").value = "notes.write"
278
+ app.query_one("#checkpoint-tool-confirm").value = "LOAD TOOLS"
279
+ app.query_one("#checkpoint-confirm").value = "RESUME"
280
+ await pilot.click("#checkpoint-resume")
281
+ await pilot.pause()
282
+
283
+ assert "Stop: completed" in str(
284
+ app.query_one("#checkpoint-resume-result").content
285
+ )
286
+ events = str(app.query_one("#checkpoint-events").content)
287
+ assert "run-resumed" in events
288
+ assert "pending_call_id=call-1" in events
289
+ assert app.query_one("#checkpoint-confirm").value == ""
290
+ assert app.query_one("#checkpoint-tool-confirm").value == ""
291
+
292
+
112
293
  @pytest.mark.asyncio
113
294
  async def test_tui_active_provider_probe_requires_explicit_confirmation(tmp_path):
114
295
  app = WAgentTui(tmp_path)
@@ -1,6 +1,6 @@
1
1
  """W-Agent 框架包"""
2
2
 
3
- __version__ = "2.0.0a2"
3
+ __version__ = "2.0.0a3"
4
4
  __author__ = "LuckyStar2456"
5
5
  __license__ = "MIT"
6
6
 
@@ -117,10 +117,11 @@ from w_agent.sandbox import (
117
117
  UnsafeLocalSandboxProvider,
118
118
  WorkspaceAccess,
119
119
  )
120
- from w_agent.sessions import (
121
- InMemorySessionStore,
122
- JsonSessionStore,
123
- SessionError,
120
+ from w_agent.sessions import (
121
+ InMemorySessionStore,
122
+ JsonSessionStore,
123
+ RunEventCallback,
124
+ SessionError,
124
125
  SessionManager,
125
126
  SessionMessage,
126
127
  SessionRecord,
@@ -747,8 +748,9 @@ __all__ = [
747
748
  "workflow_resume_tool",
748
749
  "workflow_start_tool",
749
750
  "InMemorySessionStore",
750
- "JsonSessionStore",
751
- "SessionError",
751
+ "JsonSessionStore",
752
+ "RunEventCallback",
753
+ "SessionError",
752
754
  "SessionManager",
753
755
  "SessionMessage",
754
756
  "SessionRecord",
@@ -39,7 +39,12 @@ from w_agent.models import (
39
39
  WeightedRoutingPolicy,
40
40
  builtin_provider_template_registry,
41
41
  )
42
- from w_agent.sessions import JsonSessionStore, SessionManager, SessionRecord
42
+ from w_agent.sessions import (
43
+ JsonSessionStore,
44
+ RunEventCallback,
45
+ SessionManager,
46
+ SessionRecord,
47
+ )
43
48
  from w_agent.tools import ToolBinding, ToolExecutionContext, ToolExecutor, ToolRegistry
44
49
 
45
50
  _COMPATIBLE_TEMPLATES = frozenset({"deepseek", "glm", "qwen", "turbo"})
@@ -260,6 +265,7 @@ class LocalAgentRuntime:
260
265
  permissions: frozenset[str] = frozenset(),
261
266
  approved_call_ids: frozenset[str] = frozenset(),
262
267
  cancellation: CancellationToken | None = None,
268
+ event_callback: RunEventCallback | None = None,
263
269
  ) -> LocalRuntimeRun:
264
270
  self._ensure_open()
265
271
  if not prompt:
@@ -279,6 +285,7 @@ class LocalAgentRuntime:
279
285
  approved_call_ids=approved_call_ids,
280
286
  cancellation=cancellation,
281
287
  ),
288
+ event_callback=event_callback,
282
289
  )
283
290
  return LocalRuntimeRun(await self.sessions.get(session.session_id), result)
284
291
 
@@ -290,6 +297,7 @@ class LocalAgentRuntime:
290
297
  permissions: frozenset[str] = frozenset(),
291
298
  approved_call_ids: frozenset[str],
292
299
  cancellation: CancellationToken | None = None,
300
+ event_callback: RunEventCallback | None = None,
293
301
  ) -> LocalRuntimeRun:
294
302
  """Resume one persisted approval checkpoint with explicit authority."""
295
303
 
@@ -306,6 +314,7 @@ class LocalAgentRuntime:
306
314
  cancellation=cancellation,
307
315
  ),
308
316
  cancellation=cancellation,
317
+ event_callback=event_callback,
309
318
  )
310
319
  return LocalRuntimeRun(await self.sessions.get(session_id), result)
311
320
 
@@ -3,11 +3,12 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
+ import inspect
6
7
  import json
7
8
  import math
8
9
  import os
9
10
  import re
10
- from collections.abc import Iterable
11
+ from collections.abc import Awaitable, Callable, Iterable
11
12
  from dataclasses import dataclass, field, replace
12
13
  from datetime import UTC, datetime
13
14
  from enum import StrEnum
@@ -17,7 +18,14 @@ from types import MappingProxyType
17
18
  from typing import Any, Mapping, Protocol
18
19
  from uuid import uuid4
19
20
 
20
- from w_agent.agents import AgentDefinition, AgentLoop, RunContext, RunResult
21
+ from w_agent.agents import (
22
+ AgentDefinition,
23
+ AgentExecution,
24
+ AgentLoop,
25
+ RunContext,
26
+ RunEvent,
27
+ RunResult,
28
+ )
21
29
  from w_agent.kernel import ScopePath
22
30
  from w_agent.models import (
23
31
  CancellationToken,
@@ -128,6 +136,9 @@ class ResumableAgentLoop(Protocol):
128
136
  ) -> RunResult: ...
129
137
 
130
138
 
139
+ RunEventCallback = Callable[[RunEvent], Awaitable[None] | None]
140
+
141
+
131
142
  class InMemorySessionStore:
132
143
  def __init__(self) -> None:
133
144
  self._sessions: dict[str, SessionRecord] = {}
@@ -276,6 +287,7 @@ class SessionManager:
276
287
  cancellation: CancellationToken | None = None,
277
288
  tool_context: ToolExecutionContext | None = None,
278
289
  metadata: Mapping[str, Any] | None = None,
290
+ event_callback: RunEventCallback | None = None,
279
291
  ) -> RunResult:
280
292
  current = tuple(messages)
281
293
  if not current:
@@ -283,18 +295,22 @@ class SessionManager:
283
295
  session = await self._active(session_id)
284
296
  history = await self.conversation(session_id) if include_history else ()
285
297
  resolved_run_id = run_id or f"run-{uuid4().hex}"
286
- result = await loop.run(
287
- definition,
288
- RunContext(
289
- resolved_run_id,
290
- (*history, *current),
291
- scope=scope or ScopePath.application(),
292
- cancellation=cancellation,
293
- tool_context=tool_context or ToolExecutionContext(),
294
- session_id=session_id,
295
- metadata=metadata or {},
296
- ),
298
+ context = RunContext(
299
+ resolved_run_id,
300
+ (*history, *current),
301
+ scope=scope or ScopePath.application(),
302
+ cancellation=cancellation,
303
+ tool_context=tool_context or ToolExecutionContext(),
304
+ session_id=session_id,
305
+ metadata=metadata or {},
297
306
  )
307
+ if event_callback is None:
308
+ result = await loop.run(definition, context)
309
+ else:
310
+ result = await _consume_execution(
311
+ loop.stream(definition, context),
312
+ event_callback,
313
+ )
298
314
  if result.run_id != resolved_run_id:
299
315
  raise SessionError("agent returned a different run id")
300
316
  await self._record(session, definition, current, result, append_input=True)
@@ -308,15 +324,31 @@ class SessionManager:
308
324
  *,
309
325
  tool_context: ToolExecutionContext,
310
326
  cancellation: CancellationToken | None = None,
327
+ event_callback: RunEventCallback | None = None,
311
328
  ) -> RunResult:
312
329
  session = await self._active(session_id)
313
330
  if not any(item.run_id == run_id for item in session.runs):
314
331
  raise SessionError("run does not belong to session")
315
- result = await loop.resume(
316
- run_id,
317
- tool_context=tool_context,
318
- cancellation=cancellation,
319
- )
332
+ if event_callback is None:
333
+ result = await loop.resume(
334
+ run_id,
335
+ tool_context=tool_context,
336
+ cancellation=cancellation,
337
+ )
338
+ else:
339
+ resume_stream = getattr(loop, "resume_stream", None)
340
+ if resume_stream is None:
341
+ raise SessionError(
342
+ "agent loop does not support streamed approval resume"
343
+ )
344
+ result = await _consume_execution(
345
+ resume_stream(
346
+ run_id,
347
+ tool_context=tool_context,
348
+ cancellation=cancellation,
349
+ ),
350
+ event_callback,
351
+ )
320
352
  if result.run_id != run_id:
321
353
  raise SessionError("resumed agent returned a different run id")
322
354
  await self._record(
@@ -386,6 +418,21 @@ class SessionManager:
386
418
  )
387
419
 
388
420
 
421
+ async def _consume_execution(
422
+ execution: AgentExecution,
423
+ event_callback: RunEventCallback,
424
+ ) -> RunResult:
425
+ """Consume one public event stream while projecting each event to a host."""
426
+
427
+ async for event in execution:
428
+ callback_result = event_callback(event)
429
+ if inspect.isawaitable(callback_result):
430
+ await callback_result
431
+ if execution.result is None:
432
+ raise SessionError("agent event stream completed without a result")
433
+ return execution.result
434
+
435
+
389
436
  def _project_messages(
390
437
  messages: Iterable[ModelMessage],
391
438
  run_id: str,
@@ -63,7 +63,7 @@ class McpInputRequiredError(McpError):
63
63
  @dataclass(frozen=True, slots=True)
64
64
  class McpClientInfo:
65
65
  name: str = "w-agent"
66
- version: str = "2.0.0a2"
66
+ version: str = "2.0.0a3"
67
67
 
68
68
  def __post_init__(self) -> None:
69
69
  if not self.name.strip() or not self.version.strip():