by-framework 0.2.2.dev8__tar.gz → 0.2.2.dev10__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 (262) hide show
  1. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/PKG-INFO +1 -1
  2. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/pyproject.toml +1 -1
  3. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/__init__.py +2 -0
  4. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/admin/cli.py +3 -2
  5. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/client/client.py +124 -3
  6. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/common/config.py +10 -1
  7. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/common/constants.py +13 -3
  8. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/common/emitter.py +16 -0
  9. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/__init__.py +2 -0
  10. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/__init__.py +2 -0
  11. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/content_type.py +1 -0
  12. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/responses.py +11 -0
  13. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/registry.py +2 -1
  14. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/app.py +29 -11
  15. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/runner.py +10 -0
  16. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/client/test_client.py +345 -0
  17. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/common/test_config.py +85 -0
  18. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/common/test_constants.py +27 -0
  19. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/common/test_redis_client.py +1 -0
  20. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/test_registry.py +32 -0
  21. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_app.py +69 -0
  22. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_emitter.py +41 -1
  23. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_runner.py +76 -0
  24. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/uv.lock +1 -1
  25. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/.dockerignore +0 -0
  26. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  27. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  28. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  29. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/.github/RELEASING.md +0 -0
  30. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/.github/workflows/ci.yml +0 -0
  31. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/.github/workflows/publish.yml +0 -0
  32. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/.github/workflows/stale.yml +0 -0
  33. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/.gitignore +0 -0
  34. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/.pre-commit-config.yaml +0 -0
  35. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/AGENTS.md +0 -0
  36. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/CHANGELOG.md +0 -0
  37. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/CLAUDE.md +0 -0
  38. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/CODE_OF_CONDUCT.md +0 -0
  39. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/CONTRIBUTING.md +0 -0
  40. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/LICENSE +0 -0
  41. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/Makefile +0 -0
  42. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/README.md +0 -0
  43. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/README_zh.md +0 -0
  44. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/SECURITY.md +0 -0
  45. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/assets/img/architecture_en.png +0 -0
  46. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/assets/img/architecture_zh.png +0 -0
  47. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/autoformat.sh +0 -0
  48. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/docs/README.md +0 -0
  49. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/docs/by-framework-trace-change-usage.html +0 -0
  50. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/docs/by-framework-trace-complete-guide.html +0 -0
  51. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/docs/plans/2026-05-15-worker-task-state-stats.md +0 -0
  52. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/docs/plans/2026-06-10-trace-metrics-correlation.md +0 -0
  53. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/docs/worker_management.html +0 -0
  54. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-adk/README.md +0 -0
  55. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-adk/pyproject.toml +0 -0
  56. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-adk/src/by_framework_adk/__init__.py +0 -0
  57. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-adk/src/by_framework_adk/_utils.py +0 -0
  58. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-adk/src/by_framework_adk/adapter.py +0 -0
  59. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-adk/src/by_framework_adk/worker.py +0 -0
  60. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-adk/tests/test_worker.py +0 -0
  61. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/Dockerfile +0 -0
  62. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/README.md +0 -0
  63. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/index.html +0 -0
  64. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/package-lock.json +0 -0
  65. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/package.json +0 -0
  66. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/src/main.jsx +0 -0
  67. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/src/styles.css +0 -0
  68. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/vite.config.js +0 -0
  69. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/pyproject.toml +0 -0
  70. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/__init__.py +0 -0
  71. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/adapters.py +0 -0
  72. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/dashboard.py +0 -0
  73. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/static/__init__.py +0 -0
  74. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/static/app.js +0 -0
  75. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/static/index.html +0 -0
  76. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/static/styles.css +0 -0
  77. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/tests/test_dashboard_server.py +0 -0
  78. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-history-byclaw/README.md +0 -0
  79. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-history-byclaw/pyproject.toml +0 -0
  80. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-history-byclaw/src/by_framework_history_byclaw/__init__.py +0 -0
  81. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-history-byclaw/src/by_framework_history_byclaw/byclaw_history.py +0 -0
  82. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-history-byclaw/tests/test_byclaw_history.py +0 -0
  83. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-history-postgres/README.md +0 -0
  84. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-history-postgres/pyproject.toml +0 -0
  85. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-history-postgres/src/by_framework_history_postgres/__init__.py +0 -0
  86. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-history-postgres/src/by_framework_history_postgres/postgres.py +0 -0
  87. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-history-postgres/tests/test_postgres_history_storage.py +0 -0
  88. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/README.md +0 -0
  89. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/pyproject.toml +0 -0
  90. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/src/by_framework_langgraph/__init__.py +0 -0
  91. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/src/by_framework_langgraph/_utils.py +0 -0
  92. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/src/by_framework_langgraph/adapter.py +0 -0
  93. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/src/by_framework_langgraph/tools.py +0 -0
  94. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/src/by_framework_langgraph/worker.py +0 -0
  95. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/tests/test_adapter.py +0 -0
  96. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/tests/test_tools.py +0 -0
  97. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/tests/test_utils.py +0 -0
  98. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-langfuse/README.md +0 -0
  99. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-langfuse/pyproject.toml +0 -0
  100. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-langfuse/src/by_framework_trace_langfuse/__init__.py +0 -0
  101. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-langfuse/src/by_framework_trace_langfuse/langfuse.py +0 -0
  102. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-langfuse/tests/test_langfuse_import.py +0 -0
  103. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/README.md +0 -0
  104. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/pyproject.toml +0 -0
  105. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/src/by_framework_trace_phoenix/__init__.py +0 -0
  106. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/src/by_framework_trace_phoenix/phoenix.py +0 -0
  107. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/tests/test_phoenix_import.py +0 -0
  108. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/tests/test_phoenix_plugin.py +0 -0
  109. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/pyproject.toml +0 -0
  110. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/src/by_framework_trace_query/__init__.py +0 -0
  111. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/src/by_framework_trace_query/client.py +0 -0
  112. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/src/by_framework_trace_query/merger.py +0 -0
  113. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/src/by_framework_trace_query/models.py +0 -0
  114. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/src/by_framework_trace_query/redis_source.py +0 -0
  115. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/tests/test_trace_query_sdk.py +0 -0
  116. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/pylintrc +0 -0
  117. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/scripts/python_quality.sh +0 -0
  118. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/__main__.py +0 -0
  119. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/admin/__init__.py +0 -0
  120. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/admin/worker_manager.py +0 -0
  121. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/client/__init__.py +0 -0
  122. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/client/byai_client.py +0 -0
  123. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/common/__init__.py +0 -0
  124. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/common/exceptions.py +0 -0
  125. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/common/logger.py +0 -0
  126. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/common/redis_client.py +0 -0
  127. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/availability.py +0 -0
  128. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/delivery_gate.py +0 -0
  129. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/discovery.py +0 -0
  130. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/__init__.py +0 -0
  131. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/agent_config.py +0 -0
  132. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/agent_config_audit.py +0 -0
  133. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/plugin.py +0 -0
  134. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/registry.py +0 -0
  135. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/trace_provider.py +0 -0
  136. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/action_type.py +0 -0
  137. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/agent_state.py +0 -0
  138. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/byai_codec.py +0 -0
  139. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/byai_command.py +0 -0
  140. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/byai_types.py +0 -0
  141. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/commands.py +0 -0
  142. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/content_codec.py +0 -0
  143. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/data_message.py +0 -0
  144. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/data_shapes.py +0 -0
  145. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/event_type.py +0 -0
  146. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/events.py +0 -0
  147. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/message.py +0 -0
  148. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/message_header.py +0 -0
  149. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/results.py +0 -0
  150. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/__init__.py +0 -0
  151. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/agent_config_manager.py +0 -0
  152. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/agent_runtime_state.py +0 -0
  153. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/file_manager.py +0 -0
  154. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/file_paths.py +0 -0
  155. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/file_permissions.py +0 -0
  156. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/filestore/__init__.py +0 -0
  157. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/filestore/base.py +0 -0
  158. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/filestore/local.py +0 -0
  159. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/history/__init__.py +0 -0
  160. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/history/base.py +0 -0
  161. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/history/history_manager.py +0 -0
  162. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/history/in_memory.py +0 -0
  163. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/session_manager.py +0 -0
  164. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/wakeup_controller.py +0 -0
  165. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/core/workspace.py +0 -0
  166. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/errors/__init__.py +0 -0
  167. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/errors/base.py +0 -0
  168. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/errors/common.py +0 -0
  169. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/errors/execution.py +0 -0
  170. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/errors/http.py +0 -0
  171. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/errors/protocol.py +0 -0
  172. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/errors/registry.py +0 -0
  173. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/metrics/__init__.py +0 -0
  174. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/metrics/catalog.py +0 -0
  175. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/metrics/collector.py +0 -0
  176. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/metrics/read_client.py +0 -0
  177. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/metrics/snapshot.py +0 -0
  178. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/trace/__init__.py +0 -0
  179. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/trace/external_trace.py +0 -0
  180. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/trace/span_recorder.py +0 -0
  181. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/trace/trace_schema.py +0 -0
  182. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/trace/trace_writer.py +0 -0
  183. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/util/__init__.py +0 -0
  184. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/util/discovery_http_client.py +0 -0
  185. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/util/generate_message_id.py +0 -0
  186. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/util/http_client.py +0 -0
  187. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/__init__.py +0 -0
  188. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/_control_handling.py +0 -0
  189. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/_execution_tracking.py +0 -0
  190. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/_message_processing.py +0 -0
  191. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/byai_context.py +0 -0
  192. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/byai_worker.py +0 -0
  193. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/context.py +0 -0
  194. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/heartbeat.py +0 -0
  195. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/processor.py +0 -0
  196. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/sandbox/__init__.py +0 -0
  197. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/sandbox/hook_sandbox.py +0 -0
  198. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/src/by_framework/worker/worker.py +0 -0
  199. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/admin/__init__.py +0 -0
  200. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/admin/test_cli.py +0 -0
  201. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/client/__init__.py +0 -0
  202. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/common/__init__.py +0 -0
  203. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/common/test_exceptions.py +0 -0
  204. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/common/test_logger.py +0 -0
  205. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/conftest.py +0 -0
  206. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/__init__.py +0 -0
  207. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/protocol/__init__.py +0 -0
  208. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/protocol/test_byai_codec.py +0 -0
  209. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/protocol/test_command_wire.py +0 -0
  210. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/protocol/test_protocol.py +0 -0
  211. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/protocol/test_results.py +0 -0
  212. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/runtime/history/__init__.py +0 -0
  213. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/runtime/history/test_history_persistence.py +0 -0
  214. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/runtime/test_file_access_context.py +0 -0
  215. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/runtime/test_file_manager_default_storage.py +0 -0
  216. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/runtime/test_file_paths.py +0 -0
  217. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/runtime/test_file_permissions.py +0 -0
  218. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/runtime/test_filestore_local_collection.py +0 -0
  219. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/runtime/test_filestore_local_mutation.py +0 -0
  220. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/runtime/test_filestore_local_read.py +0 -0
  221. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/runtime/test_filestore_local_search.py +0 -0
  222. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/test_agent_config_audit.py +0 -0
  223. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/test_availability.py +0 -0
  224. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/test_discovery.py +0 -0
  225. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/core/test_worker_manager.py +0 -0
  226. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/integration/__init__.py +0 -0
  227. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/integration/test_ask_user_flow.py +0 -0
  228. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/integration/test_callback_flow.py +0 -0
  229. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/integration/test_logger_integration.py +0 -0
  230. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/integration/test_scatter_gather.py +0 -0
  231. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/metrics/test_catalog.py +0 -0
  232. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/metrics/test_collector.py +0 -0
  233. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/metrics/test_metrics.py +0 -0
  234. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/metrics/test_read_client.py +0 -0
  235. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/metrics/test_snapshot.py +0 -0
  236. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/plugin/__init__.py +0 -0
  237. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/plugin/test_langfuse_plugin.py +0 -0
  238. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/plugin/test_plugin_discovery.py +0 -0
  239. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/plugin/test_plugin_improvements.py +0 -0
  240. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/plugin/test_plugin_registry.py +0 -0
  241. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/plugin/test_plugin_system.py +0 -0
  242. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/trace/test_external_trace.py +0 -0
  243. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/trace/test_logger_correlation.py +0 -0
  244. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/trace/test_otel_exporter.py +0 -0
  245. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/trace/test_span_recorder.py +0 -0
  246. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/trace/test_trace_writer.py +0 -0
  247. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/util/test_discovery_http_client.py +0 -0
  248. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/util/test_discovery_http_client_download.py +0 -0
  249. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/util/test_discovery_http_client_upload.py +0 -0
  250. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/util/test_http_client.py +0 -0
  251. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/__init__.py +0 -0
  252. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_admin_control_handling.py +0 -0
  253. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_byai_worker.py +0 -0
  254. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_context.py +0 -0
  255. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_control_handling.py +0 -0
  256. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_fetch_messages.py +0 -0
  257. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_gateway_worker.py +0 -0
  258. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_heartbeat.py +0 -0
  259. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_message_processing.py +0 -0
  260. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_processor.py +0 -0
  261. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_sandbox.py +0 -0
  262. {by_framework-0.2.2.dev8 → by_framework-0.2.2.dev10}/tests/worker/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: by-framework
3
- Version: 0.2.2.dev8
3
+ Version: 0.2.2.dev10
4
4
  Summary: 分布式 Agent 调度框架
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "by-framework"
3
- version = "0.2.2.dev8"
3
+ version = "0.2.2.dev10"
4
4
  description = "分布式 Agent 调度框架"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -8,6 +8,7 @@ from `GatewayWorker` and running `run_worker`.
8
8
  from .admin import WorkerManager
9
9
  from .client.byai_client import ByaiGatewayClient
10
10
  from .client.client import (
11
+ CancelSessionResponse,
11
12
  CancelTaskResponse,
12
13
  DataStreamEntry,
13
14
  GatewayClient,
@@ -148,6 +149,7 @@ __all__ = [
148
149
  "DataStreamEntry",
149
150
  "SendMessageResponse",
150
151
  "CancelTaskResponse",
152
+ "CancelSessionResponse",
151
153
  "run_worker",
152
154
  "logger",
153
155
  "setup_logging",
@@ -31,8 +31,9 @@ app = typer.Typer(
31
31
  help=(
32
32
  "by-framework cluster admin CLI. For Redis Cluster, omit --redis-url "
33
33
  "and set REDIS_CLUSTER_HOST (comma-separated host:port list; implies "
34
- "cluster mode) and REDIS_KEY_SCHEMA_VERSION=v2. REDIS_MODE=cluster + "
35
- "REDIS_CLUSTER_NODES also still works."
34
+ "both cluster mode and REDIS_KEY_SCHEMA_VERSION=v2 unless set "
35
+ "explicitly). REDIS_MODE=cluster + REDIS_CLUSTER_NODES + "
36
+ "REDIS_KEY_SCHEMA_VERSION=v2 also still works."
36
37
  ),
37
38
  )
38
39
  worker_app = typer.Typer(no_args_is_help=True)
@@ -37,6 +37,7 @@ from by_framework.core.protocol.commands import (
37
37
  from by_framework.core.protocol.data_message import DataMessage
38
38
  from by_framework.core.protocol.message_header import MessageHeader
39
39
  from by_framework.core.protocol.responses import (
40
+ CancelSessionResponse,
40
41
  CancelTaskResponse,
41
42
  ExecutionStatus,
42
43
  SendMessageResponse,
@@ -581,6 +582,99 @@ class GatewayClient:
581
582
  cancelled_count=len(to_cancel),
582
583
  )
583
584
 
585
+ async def cancel_session(
586
+ self,
587
+ session_id: str,
588
+ reason: str = "",
589
+ requested_by: str = "client",
590
+ cancel_mode: str = CancelMode.GRACEFUL,
591
+ ) -> CancelSessionResponse:
592
+ """Cancel every active execution registered under a session.
593
+
594
+ Unlike cancel_task, this does not require a starting message_id:
595
+ it cancels the whole session's active executions in one call.
596
+ """
597
+ if self.registry is None:
598
+ raise ValueError("GatewayClient requires a WorkerRegistry to cancel tasks")
599
+
600
+ all_executions = await self.registry.get_all_session_executions(session_id)
601
+
602
+ if not all_executions:
603
+ return CancelSessionResponse(
604
+ success=False,
605
+ session_id=session_id,
606
+ status=ExecutionStatus.NOT_FOUND,
607
+ timestamp=int(time.time() * 1000),
608
+ cancelled_count=0,
609
+ already_finished_count=0,
610
+ )
611
+
612
+ terminal_states = {"COMPLETED", "FAILED", "CANCELLED"}
613
+ to_cancel = [
614
+ node
615
+ for node in all_executions
616
+ if node.get("status", "") not in terminal_states
617
+ ]
618
+ terminal_nodes = [
619
+ node for node in all_executions if node.get("status", "") in terminal_states
620
+ ]
621
+
622
+ # Flag terminal executions so a still-running descendant can't wake
623
+ # them back up via callback, without changing their status.
624
+ for node in terminal_nodes:
625
+ await self.registry.mark_cancel_requested(
626
+ node.get("execution_id", ""), session_id, reason
627
+ )
628
+
629
+ if not to_cancel:
630
+ return CancelSessionResponse(
631
+ success=False,
632
+ session_id=session_id,
633
+ status=ExecutionStatus.ALREADY_FINISHED,
634
+ timestamp=int(time.time() * 1000),
635
+ cancelled_count=0,
636
+ already_finished_count=len(terminal_nodes),
637
+ )
638
+
639
+ for node in to_cancel:
640
+ node_execution_id = node.get("execution_id", "")
641
+ node_worker_id = node.get("worker_id", "")
642
+ node_message_id = node.get("message_id", "")
643
+
644
+ await self.registry.mark_execution_cancelling(
645
+ node_execution_id, session_id, reason
646
+ )
647
+
648
+ if node_worker_id:
649
+ cancel_command = CancelTaskCommand(
650
+ header=MessageHeader(
651
+ message_id=f"{CANCEL_MESSAGE_ID_PREFIX}{uuid.uuid4().hex[:8]}",
652
+ session_id=session_id,
653
+ trace_id=node.get("trace_id") or uuid.uuid4().hex,
654
+ target_agent_type=node.get("target_agent_type", ""),
655
+ parent_message_id=node_message_id,
656
+ ),
657
+ target_message_id=node_message_id,
658
+ target_execution_id=node_execution_id,
659
+ target_worker_id=node_worker_id,
660
+ reason=reason,
661
+ requested_by=requested_by,
662
+ cancel_mode=cancel_mode,
663
+ )
664
+ await self.redis.xadd(
665
+ RedisKeys.worker_ctrl_stream(node_worker_id),
666
+ cancel_command.to_redis_payload(),
667
+ )
668
+
669
+ return CancelSessionResponse(
670
+ success=True,
671
+ session_id=session_id,
672
+ status=ExecutionStatus.CANCEL_REQUESTED,
673
+ timestamp=int(time.time() * 1000),
674
+ cancelled_count=len(to_cancel),
675
+ already_finished_count=len(terminal_nodes),
676
+ )
677
+
584
678
  async def send_message(
585
679
  self,
586
680
  target_agent_type: str,
@@ -691,7 +785,28 @@ class GatewayClient:
691
785
  content=params["content"],
692
786
  extra_payload=params["extra_payload"],
693
787
  )
694
- execution_id = f"{EXECUTION_ID_PREFIX}{uuid.uuid4().hex[:8]}"
788
+
789
+ # A RESUME reuses the message_id of the original AskAgentCommand so the
790
+ # worker can look the suspended execution back up. Reuse its
791
+ # execution_id too, and skip re-initializing the registry record for
792
+ # it below -- initialize_execution() would otherwise overwrite the
793
+ # message_id -> execution_id mapping and detach this resume from the
794
+ # execution it's meant to continue.
795
+ resumed_execution = None
796
+ if (
797
+ params["action_type"] == ActionType.RESUME.value
798
+ and self.registry
799
+ and hasattr(self.registry, "get_execution_by_message_id")
800
+ ):
801
+ resumed_execution = await self.registry.get_execution_by_message_id(
802
+ message_id, session_id=params["session_id"]
803
+ )
804
+
805
+ execution_id = (
806
+ resumed_execution["execution_id"]
807
+ if resumed_execution
808
+ else f"{EXECUTION_ID_PREFIX}{uuid.uuid4().hex[:8]}"
809
+ )
695
810
 
696
811
  # 3. Resolve route and optionally probe agent type/liveness
697
812
  should_dispatch_control = True
@@ -837,8 +952,14 @@ class GatewayClient:
837
952
  error_code=ExecutionStatus.ERR_AGENT_TYPE_UNAVAILABLE,
838
953
  )
839
954
 
840
- # Initialize execution tracking
841
- if self.registry and hasattr(self.registry, "initialize_execution"):
955
+ # Initialize execution tracking. Skipped for a RESUME that reattached
956
+ # to an existing execution -- that execution is already tracked, and
957
+ # re-initializing it here would overwrite its message_id mapping.
958
+ if (
959
+ not resumed_execution
960
+ and self.registry
961
+ and hasattr(self.registry, "initialize_execution")
962
+ ):
842
963
  try:
843
964
  await self.registry.initialize_execution(
844
965
  {
@@ -9,6 +9,7 @@ from dataclasses import dataclass, field
9
9
  from typing import Literal, Optional
10
10
 
11
11
  from by_framework.common.constants import RedisKeys
12
+ from by_framework.common.logger import logger
12
13
 
13
14
 
14
15
  @dataclass(frozen=True)
@@ -36,6 +37,9 @@ class RedisConfig:
36
37
  to switch to cluster mode, no separate REDIS_MODE=cluster needed.
37
38
  An explicit REDIS_MODE still takes precedence when set, so existing
38
39
  explicit REDIS_MODE=cluster + REDIS_CLUSTER_NODES setups keep working.
40
+
41
+ REDIS_DATABASE replaces REDIS_DB (which still works as a deprecated
42
+ fallback, logging a warning, during the transition period).
39
43
  """
40
44
  password = os.environ.get("REDIS_PASSWORD", "")
41
45
  username = os.environ.get("REDIS_USERNAME") or None
@@ -51,10 +55,15 @@ class RedisConfig:
51
55
  for node in cluster_nodes_str.split(","):
52
56
  node_host, node_port = node.rsplit(":", 1)
53
57
  cluster_nodes.append((node_host, int(node_port)))
58
+ db_str = os.environ.get("REDIS_DATABASE")
59
+ if db_str is None:
60
+ db_str = os.environ.get("REDIS_DB")
61
+ if db_str is not None:
62
+ logger.warning("REDIS_DB is deprecated, use REDIS_DATABASE instead")
54
63
  return cls(
55
64
  host=os.environ.get("REDIS_HOST", "localhost"),
56
65
  port=int(os.environ.get("REDIS_PORT", "6379")),
57
- db=int(os.environ.get("REDIS_DB", "0")),
66
+ db=int(db_str) if db_str is not None else 0,
58
67
  password=password,
59
68
  username=username,
60
69
  max_connections=int(max_connections) if max_connections else None,
@@ -12,10 +12,20 @@ from typing import Optional
12
12
  def get_key_schema_version() -> str:
13
13
  """Return the configured Redis key schema version ("v1" or "v2").
14
14
 
15
- Controlled by REDIS_KEY_SCHEMA_VERSION, defaulting to "v1" (the current
16
- unprefixed key format). Cluster mode requires "v2" (see redis_client.init_redis).
15
+ Controlled by REDIS_KEY_SCHEMA_VERSION, which always wins when set
16
+ explicitly. When it isn't set, REDIS_CLUSTER_HOST being configured
17
+ implies "v2" (cluster mode requires v2 - v1 keys have no hash tags and
18
+ hit CROSSSLOT errors under Cluster; see redis_client.init_redis's
19
+ fail-fast check); otherwise it defaults to "v1" (the legacy unprefixed
20
+ key format). This mirrors RedisConfig.from_env()'s REDIS_MODE/
21
+ REDIS_CLUSTER_HOST precedence, but deliberately does NOT infer "v2"
22
+ from an explicit REDIS_MODE=cluster alone (without REDIS_CLUSTER_HOST)
23
+ - that legacy explicit-mode path still requires
24
+ REDIS_KEY_SCHEMA_VERSION=v2 to be set by hand.
17
25
  """
18
- version = os.environ.get("REDIS_KEY_SCHEMA_VERSION", "v1")
26
+ version = os.environ.get("REDIS_KEY_SCHEMA_VERSION")
27
+ if version is None:
28
+ version = "v2" if os.environ.get("REDIS_CLUSTER_HOST") else "v1"
19
29
  if version not in ("v1", "v2"):
20
30
  raise ValueError(
21
31
  f"Invalid REDIS_KEY_SCHEMA_VERSION: {version!r} (must be 'v1' or 'v2')"
@@ -107,6 +107,22 @@ class DefaultSseLayoutBuilder(DataLayoutBuilder):
107
107
  parent_order_id: Optional[str] = None,
108
108
  ) -> Dict[str, Any]:
109
109
  """Build default BaiYing-compatible user input form payload."""
110
+ try:
111
+ parsed_prompt = json.loads(prompt)
112
+ except (json.JSONDecodeError, TypeError):
113
+ parsed_prompt = None
114
+
115
+ # Structured question prompts are already in the format expected by clients.
116
+ if isinstance(parsed_prompt, dict) and "questions" in parsed_prompt:
117
+ return self.build(
118
+ content=prompt,
119
+ role="assistant",
120
+ content_type=SseReasonMessageType.ask_user_question.value,
121
+ source_agent_type=source_agent_type,
122
+ order_id=order_id,
123
+ parent_order_id=parent_order_id,
124
+ )
125
+
110
126
  input_form = {
111
127
  "formStatus": 0,
112
128
  "pluginMachineFields": [
@@ -26,6 +26,7 @@ from .protocol import (
26
26
  BaiYingMessage,
27
27
  BaiYingMessageRole,
28
28
  BaseCommand,
29
+ CancelSessionResponse,
29
30
  CancelTaskCommand,
30
31
  CancelTaskResponse,
31
32
  DataMessage,
@@ -53,6 +54,7 @@ from .workspace import WorkspaceManager
53
54
  __all__ = [
54
55
  "SendMessageResponse",
55
56
  "CancelTaskResponse",
57
+ "CancelSessionResponse",
56
58
  "BaiYingMessage",
57
59
  "BaiYingMessageRole",
58
60
  "MessageContent",
@@ -65,6 +65,7 @@ from .message import (
65
65
  )
66
66
  from .message_header import MessageHeader
67
67
  from .responses import (
68
+ CancelSessionResponse,
68
69
  CancelTaskResponse,
69
70
  CancelTaskResponseDict,
70
71
  SendMessageResponse,
@@ -81,6 +82,7 @@ from .results import (
81
82
  __all__ = [
82
83
  "SendMessageResponse",
83
84
  "CancelTaskResponse",
85
+ "CancelSessionResponse",
84
86
  "SendMessageResponseDict",
85
87
  "CancelTaskResponseDict",
86
88
  "AgentTaskResult",
@@ -33,6 +33,7 @@ class SseReasonMessageType(str, Enum):
33
33
  think_code_result = "3007" # thinking process code execution result
34
34
  task_finished = "3009" # task finished
35
35
  task_user_input = "3013" # user input
36
+ ask_user_question = "3014" # ask user question
36
37
  task_create_file = "3010" # create file
37
38
  task_title = "3011" # task title
38
39
  agent_card = "2015" # agent card
@@ -92,3 +92,14 @@ class CancelTaskResponse:
92
92
  timestamp: int
93
93
  error: str = ""
94
94
  cancelled_count: int = 0
95
+
96
+
97
+ @dataclass(frozen=True)
98
+ class CancelSessionResponse:
99
+ success: bool
100
+ session_id: str
101
+ status: str
102
+ timestamp: int
103
+ cancelled_count: int = 0
104
+ already_finished_count: int = 0
105
+ error: str = ""
@@ -1011,7 +1011,8 @@ class WorkerRegistry:
1011
1011
  old_execution = dict(current)
1012
1012
  current["status"] = status
1013
1013
  now = int(time.time() * 1000)
1014
- current["finished_at"] = now
1014
+ if is_terminal_state(status):
1015
+ current["finished_at"] = now
1015
1016
  current["updated_at"] = now
1016
1017
  if completion:
1017
1018
  for key, value in completion.items():
@@ -131,6 +131,17 @@ async def _run_worker_async(
131
131
  # parties can configure either mode purely programmatically or purely
132
132
  # via env vars, without the two modes behaving inconsistently.
133
133
  env_config = RedisConfig.from_env()
134
+ if redis_mode is not None:
135
+ effective_mode = redis_mode
136
+ elif redis_cluster_nodes:
137
+ # Mirrors REDIS_CLUSTER_HOST's env-var precedence: passing
138
+ # redis_cluster_nodes alone is enough to imply cluster mode, no
139
+ # separate redis_mode="cluster" required. Without this, passing
140
+ # redis_cluster_nodes alone would silently stay in standalone mode
141
+ # and the nodes list would be dropped on the floor.
142
+ effective_mode = "cluster"
143
+ else:
144
+ effective_mode = env_config.mode
134
145
  effective_config = replace(
135
146
  env_config,
136
147
  host=redis_host if redis_host is not None else env_config.host,
@@ -142,7 +153,7 @@ async def _run_worker_async(
142
153
  username=(
143
154
  redis_username if redis_username is not None else env_config.username
144
155
  ),
145
- mode=redis_mode if redis_mode is not None else env_config.mode,
156
+ mode=effective_mode,
146
157
  cluster_nodes=(
147
158
  redis_cluster_nodes
148
159
  if redis_cluster_nodes is not None
@@ -276,16 +287,23 @@ def run_worker(
276
287
  Redis configuration: every redis_* argument defaults to None, meaning
277
288
  "not specified" - when not passed, the value is read from the
278
289
  corresponding REDIS_* environment variable (REDIS_HOST, REDIS_PORT,
279
- REDIS_DB, REDIS_PASSWORD, REDIS_USERNAME, REDIS_MODE, REDIS_CLUSTER_HOST,
280
- REDIS_CLUSTER_NODES), falling back to "localhost"/6379/standalone if
281
- none is set. Setting REDIS_CLUSTER_HOST (comma-separated "host:port"
282
- list) alone is enough to switch to cluster mode - no separate
283
- REDIS_MODE=cluster required, though an explicit REDIS_MODE still wins if
284
- set. An explicitly-passed argument always takes precedence over its env
285
- var, for every field, in both standalone and cluster mode - so either
286
- mode can be configured purely programmatically (e.g.
287
- ``redis_mode="cluster", redis_cluster_nodes=[("h1", 6379)]``) or purely
288
- via env vars, without the two modes behaving inconsistently.
290
+ REDIS_DATABASE, REDIS_PASSWORD, REDIS_USERNAME, REDIS_MODE,
291
+ REDIS_CLUSTER_HOST, REDIS_CLUSTER_NODES), falling back to
292
+ "localhost"/6379/standalone if none is set. REDIS_DATABASE replaces
293
+ REDIS_DB (still works as a deprecated fallback, logging a warning).
294
+ Setting REDIS_CLUSTER_HOST (comma-separated "host:port" list) alone is
295
+ enough to switch to cluster mode - no separate REDIS_MODE=cluster
296
+ required, though an explicit REDIS_MODE still wins if set; it also
297
+ implies REDIS_KEY_SCHEMA_VERSION=v2 unless that's set explicitly. An
298
+ explicitly-passed argument always takes precedence over its env var,
299
+ for every field, in both standalone and cluster mode. The same
300
+ single-argument convenience carries over to the programmatic path:
301
+ passing just ``redis_cluster_nodes=[("h1", 6379)]`` (no ``redis_mode``)
302
+ is enough to switch to cluster mode, mirroring REDIS_CLUSTER_HOST -
303
+ ``redis_mode`` only needs to be passed to explicitly force a mode
304
+ (e.g. pin a script to standalone regardless of REDIS_CLUSTER_HOST).
305
+ So either mode can be configured purely programmatically or purely via
306
+ env vars, without the two paths behaving inconsistently.
289
307
 
290
308
  Plugin support:
291
309
  - **Auto mode**: System automatically discovers and loads all subclasses
@@ -505,6 +505,16 @@ class WorkerRunner:
505
505
  header.message_id, session_id=header.session_id
506
506
  )
507
507
 
508
+ if isinstance(command, ResumeCommand) and existing_execution is None:
509
+ logger.warning(
510
+ "[%s] ResumeCommand did not resolve to an existing execution "
511
+ "(message_id=%s, session_id=%s); starting a new, disconnected "
512
+ "execution instead of continuing the suspended one.",
513
+ self.worker.worker_id,
514
+ header.message_id,
515
+ header.session_id,
516
+ )
517
+
508
518
  # Skip terminal state replays
509
519
  if (
510
520
  existing_execution