by-framework 0.2.2.dev15__tar.gz → 0.2.2.dev16__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 (325) hide show
  1. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/PKG-INFO +1 -1
  2. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/architecture/KEY_FILES.md +16 -0
  3. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/architecture/worker-readiness-endpoint.md +9 -1
  4. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-langgraph/pyproject.toml +1 -1
  5. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-langfuse/pyproject.toml +1 -1
  6. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/pyproject.toml +1 -1
  7. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/runner.py +25 -2
  8. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_runner.py +208 -0
  9. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/uv.lock +3 -3
  10. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.codex/hooks.json +0 -0
  11. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.dockerignore +0 -0
  12. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  13. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  14. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  15. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.github/RELEASING.md +0 -0
  16. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.github/workflows/ci.yml +0 -0
  17. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.github/workflows/deploy-smoke-test.yml +0 -0
  18. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.github/workflows/publish.yml +0 -0
  19. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.github/workflows/stale.yml +0 -0
  20. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.github/workflows/verify.yml +0 -0
  21. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.gitignore +0 -0
  22. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/.pre-commit-config.yaml +0 -0
  23. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/AGENTS.md +0 -0
  24. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/CHANGELOG.md +0 -0
  25. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/CLAUDE.md +0 -0
  26. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/CODE_OF_CONDUCT.md +0 -0
  27. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/CONTRIBUTING.md +0 -0
  28. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/LICENSE +0 -0
  29. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/Makefile +0 -0
  30. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/README.md +0 -0
  31. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/README_zh.md +0 -0
  32. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/SECURITY.md +0 -0
  33. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/assets/img/architecture_en.png +0 -0
  34. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/assets/img/architecture_zh.png +0 -0
  35. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/autoformat.sh +0 -0
  36. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/deploy/Dockerfile +0 -0
  37. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/deploy/docker-compose.yml +0 -0
  38. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/deploy/entrypoint.sh +0 -0
  39. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/deploy/kubernetes/worker-deployment.yaml +0 -0
  40. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/README.md +0 -0
  41. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/architecture/GUARD_AUTHORING.md +0 -0
  42. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/architecture/langfuse-tracing-memory.md +0 -0
  43. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/architecture/production-deployment.md +0 -0
  44. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/architecture/redis-cluster-mode.md +0 -0
  45. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/architecture/suspend-resume-liveness.md +0 -0
  46. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/by-framework-trace-change-usage.html +0 -0
  47. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/by-framework-trace-complete-guide.html +0 -0
  48. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/plans/2026-05-15-worker-task-state-stats.md +0 -0
  49. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/plans/2026-06-10-trace-metrics-correlation.md +0 -0
  50. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/docs/worker_management.html +0 -0
  51. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/examples/__init__.py +0 -0
  52. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/examples/echo_worker.py +0 -0
  53. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/examples/send_and_verify.py +0 -0
  54. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-adk/README.md +0 -0
  55. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-adk/pyproject.toml +0 -0
  56. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-adk/src/by_framework_adk/__init__.py +0 -0
  57. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-adk/src/by_framework_adk/_utils.py +0 -0
  58. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-adk/src/by_framework_adk/adapter.py +0 -0
  59. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-adk/src/by_framework_adk/worker.py +0 -0
  60. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-adk/tests/test_adapter.py +0 -0
  61. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-adk/tests/test_worker.py +0 -0
  62. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/README.md +0 -0
  63. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/examples/01_basic_chat.py +0 -0
  64. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/examples/02_tools_and_ask_user.py +0 -0
  65. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/examples/03_agent_delegation.py +0 -0
  66. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/examples/04_task_group.py +0 -0
  67. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/examples/05_real_model_react.py +0 -0
  68. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/examples/README.md +0 -0
  69. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/examples/_infra.py +0 -0
  70. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/pyproject.toml +0 -0
  71. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/src/by_framework_agent/__init__.py +0 -0
  72. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/src/by_framework_agent/litellm_client.py +0 -0
  73. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/src/by_framework_agent/loop.py +0 -0
  74. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/src/by_framework_agent/model_client.py +0 -0
  75. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/src/by_framework_agent/testing.py +0 -0
  76. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/src/by_framework_agent/tool_spec.py +0 -0
  77. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/src/by_framework_agent/worker.py +0 -0
  78. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/tests/conftest.py +0 -0
  79. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/tests/test_agent_as_tool.py +0 -0
  80. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/tests/test_ask_user_suspend_resume.py +0 -0
  81. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/tests/test_cancel_cleanup.py +0 -0
  82. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/tests/test_native_agent_worker.py +0 -0
  83. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/tests/test_task_group_batching.py +0 -0
  84. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-agent/tests/test_tool_calling_loop.py +0 -0
  85. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/Dockerfile +0 -0
  86. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/README.md +0 -0
  87. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/frontend/index.html +0 -0
  88. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/frontend/package-lock.json +0 -0
  89. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/frontend/package.json +0 -0
  90. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/frontend/src/main.jsx +0 -0
  91. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/frontend/src/styles.css +0 -0
  92. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/frontend/vite.config.js +0 -0
  93. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/pyproject.toml +0 -0
  94. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/src/by_framework_dashboard/__init__.py +0 -0
  95. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/src/by_framework_dashboard/adapters.py +0 -0
  96. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/src/by_framework_dashboard/dashboard.py +0 -0
  97. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/src/by_framework_dashboard/static/__init__.py +0 -0
  98. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/src/by_framework_dashboard/static/app.js +0 -0
  99. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/src/by_framework_dashboard/static/index.html +0 -0
  100. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/src/by_framework_dashboard/static/styles.css +0 -0
  101. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-dashboard/tests/test_dashboard_server.py +0 -0
  102. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-history-byclaw/README.md +0 -0
  103. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-history-byclaw/pyproject.toml +0 -0
  104. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-history-byclaw/src/by_framework_history_byclaw/__init__.py +0 -0
  105. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-history-byclaw/src/by_framework_history_byclaw/byclaw_history.py +0 -0
  106. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-history-byclaw/tests/test_byclaw_history.py +0 -0
  107. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-history-postgres/README.md +0 -0
  108. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-history-postgres/pyproject.toml +0 -0
  109. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-history-postgres/src/by_framework_history_postgres/__init__.py +0 -0
  110. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-history-postgres/src/by_framework_history_postgres/postgres.py +0 -0
  111. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-history-postgres/tests/test_postgres_history_storage.py +0 -0
  112. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-langgraph/README.md +0 -0
  113. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-langgraph/src/by_framework_langgraph/__init__.py +0 -0
  114. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-langgraph/src/by_framework_langgraph/_utils.py +0 -0
  115. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-langgraph/src/by_framework_langgraph/adapter.py +0 -0
  116. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-langgraph/src/by_framework_langgraph/tools.py +0 -0
  117. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-langgraph/src/by_framework_langgraph/worker.py +0 -0
  118. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-langgraph/tests/test_adapter.py +0 -0
  119. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-langgraph/tests/test_tools.py +0 -0
  120. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-langgraph/tests/test_utils.py +0 -0
  121. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-langfuse/README.md +0 -0
  122. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-langfuse/src/by_framework_trace_langfuse/__init__.py +0 -0
  123. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-langfuse/src/by_framework_trace_langfuse/langfuse.py +0 -0
  124. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-langfuse/tests/test_langfuse_import.py +0 -0
  125. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-phoenix/README.md +0 -0
  126. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-phoenix/pyproject.toml +0 -0
  127. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-phoenix/src/by_framework_trace_phoenix/__init__.py +0 -0
  128. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-phoenix/src/by_framework_trace_phoenix/phoenix.py +0 -0
  129. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-phoenix/tests/test_phoenix_import.py +0 -0
  130. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-phoenix/tests/test_phoenix_plugin.py +0 -0
  131. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-query/pyproject.toml +0 -0
  132. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-query/src/by_framework_trace_query/__init__.py +0 -0
  133. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-query/src/by_framework_trace_query/client.py +0 -0
  134. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-query/src/by_framework_trace_query/merger.py +0 -0
  135. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-query/src/by_framework_trace_query/models.py +0 -0
  136. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-query/src/by_framework_trace_query/redis_source.py +0 -0
  137. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/libs/by-framework-trace-query/tests/test_trace_query_sdk.py +0 -0
  138. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/pylintrc +0 -0
  139. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/scripts/.map.conf +0 -0
  140. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/scripts/check-doc-discipline.sh +0 -0
  141. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/scripts/check-entry-freshness.sh +0 -0
  142. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/scripts/check-map-territory.sh +0 -0
  143. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/scripts/check-metric-catalog.sh +0 -0
  144. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/scripts/map-stop-hook.sh +0 -0
  145. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/scripts/python_quality.sh +0 -0
  146. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/scripts/test-growth-ladder.sh +0 -0
  147. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/scripts/verify.sh +0 -0
  148. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/__init__.py +0 -0
  149. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/__main__.py +0 -0
  150. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/admin/__init__.py +0 -0
  151. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/admin/cli.py +0 -0
  152. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/admin/worker_manager.py +0 -0
  153. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/client/__init__.py +0 -0
  154. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/client/byai_client.py +0 -0
  155. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/client/client.py +0 -0
  156. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/common/__init__.py +0 -0
  157. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/common/config.py +0 -0
  158. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/common/constants.py +0 -0
  159. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/common/emitter.py +0 -0
  160. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/common/exceptions.py +0 -0
  161. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/common/logger.py +0 -0
  162. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/common/redis_client.py +0 -0
  163. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/__init__.py +0 -0
  164. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/availability.py +0 -0
  165. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/delivery_gate.py +0 -0
  166. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/discovery.py +0 -0
  167. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/extensions/__init__.py +0 -0
  168. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/extensions/agent_config.py +0 -0
  169. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/extensions/agent_config_audit.py +0 -0
  170. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/extensions/plugin.py +0 -0
  171. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/extensions/registry.py +0 -0
  172. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/extensions/trace_provider.py +0 -0
  173. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/__init__.py +0 -0
  174. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/action_type.py +0 -0
  175. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/agent_state.py +0 -0
  176. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/byai_codec.py +0 -0
  177. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/byai_command.py +0 -0
  178. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/byai_types.py +0 -0
  179. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/commands.py +0 -0
  180. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/content_codec.py +0 -0
  181. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/content_type.py +0 -0
  182. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/data_message.py +0 -0
  183. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/data_shapes.py +0 -0
  184. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/event_type.py +0 -0
  185. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/events.py +0 -0
  186. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/message.py +0 -0
  187. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/message_header.py +0 -0
  188. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/responses.py +0 -0
  189. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/protocol/results.py +0 -0
  190. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/registry.py +0 -0
  191. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/__init__.py +0 -0
  192. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/agent_config_manager.py +0 -0
  193. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/agent_runtime_state.py +0 -0
  194. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/file_manager.py +0 -0
  195. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/file_paths.py +0 -0
  196. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/file_permissions.py +0 -0
  197. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/filestore/__init__.py +0 -0
  198. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/filestore/base.py +0 -0
  199. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/filestore/local.py +0 -0
  200. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/history/__init__.py +0 -0
  201. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/history/base.py +0 -0
  202. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/history/history_manager.py +0 -0
  203. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/history/in_memory.py +0 -0
  204. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/runtime/session_manager.py +0 -0
  205. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/wait_gate.py +0 -0
  206. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/wait_index.py +0 -0
  207. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/wait_reply.py +0 -0
  208. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/wait_sweeper.py +0 -0
  209. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/wakeup_controller.py +0 -0
  210. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/core/workspace.py +0 -0
  211. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/errors/__init__.py +0 -0
  212. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/errors/base.py +0 -0
  213. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/errors/common.py +0 -0
  214. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/errors/execution.py +0 -0
  215. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/errors/http.py +0 -0
  216. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/errors/protocol.py +0 -0
  217. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/errors/registry.py +0 -0
  218. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/metrics/__init__.py +0 -0
  219. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/metrics/catalog.py +0 -0
  220. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/metrics/collector.py +0 -0
  221. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/metrics/read_client.py +0 -0
  222. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/metrics/snapshot.py +0 -0
  223. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/trace/__init__.py +0 -0
  224. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/trace/external_trace.py +0 -0
  225. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/trace/span_recorder.py +0 -0
  226. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/trace/trace_schema.py +0 -0
  227. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/trace/trace_writer.py +0 -0
  228. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/util/__init__.py +0 -0
  229. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/util/discovery_http_client.py +0 -0
  230. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/util/generate_message_id.py +0 -0
  231. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/util/http_client.py +0 -0
  232. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/__init__.py +0 -0
  233. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/_control_handling.py +0 -0
  234. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/_execution_tracking.py +0 -0
  235. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/_message_processing.py +0 -0
  236. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/_resume_metadata.py +0 -0
  237. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/app.py +0 -0
  238. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/byai_context.py +0 -0
  239. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/byai_worker.py +0 -0
  240. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/context.py +0 -0
  241. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/health_server.py +0 -0
  242. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/heartbeat.py +0 -0
  243. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/processor.py +0 -0
  244. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/sandbox/__init__.py +0 -0
  245. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/sandbox/hook_sandbox.py +0 -0
  246. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/src/by_framework/worker/worker.py +0 -0
  247. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/admin/__init__.py +0 -0
  248. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/admin/test_cli.py +0 -0
  249. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/client/__init__.py +0 -0
  250. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/client/test_client.py +0 -0
  251. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/common/__init__.py +0 -0
  252. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/common/test_config.py +0 -0
  253. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/common/test_constants.py +0 -0
  254. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/common/test_exceptions.py +0 -0
  255. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/common/test_logger.py +0 -0
  256. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/common/test_redis_client.py +0 -0
  257. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/conftest.py +0 -0
  258. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/__init__.py +0 -0
  259. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/protocol/__init__.py +0 -0
  260. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/protocol/test_byai_codec.py +0 -0
  261. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/protocol/test_command_wire.py +0 -0
  262. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/protocol/test_protocol.py +0 -0
  263. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/protocol/test_results.py +0 -0
  264. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/runtime/history/__init__.py +0 -0
  265. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/runtime/history/test_history_persistence.py +0 -0
  266. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/runtime/test_file_access_context.py +0 -0
  267. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/runtime/test_file_manager_default_storage.py +0 -0
  268. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/runtime/test_file_paths.py +0 -0
  269. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/runtime/test_file_permissions.py +0 -0
  270. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/runtime/test_filestore_local_collection.py +0 -0
  271. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/runtime/test_filestore_local_mutation.py +0 -0
  272. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/runtime/test_filestore_local_read.py +0 -0
  273. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/runtime/test_filestore_local_search.py +0 -0
  274. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/test_agent_config_audit.py +0 -0
  275. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/test_availability.py +0 -0
  276. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/test_discovery.py +0 -0
  277. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/test_registry.py +0 -0
  278. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/test_wait_gate.py +0 -0
  279. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/test_wait_index.py +0 -0
  280. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/test_wait_sweeper.py +0 -0
  281. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/core/test_worker_manager.py +0 -0
  282. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/integration/__init__.py +0 -0
  283. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/integration/test_ask_user_flow.py +0 -0
  284. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/integration/test_callback_flow.py +0 -0
  285. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/integration/test_logger_integration.py +0 -0
  286. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/integration/test_nested_chain.py +0 -0
  287. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/integration/test_orphan_recovery.py +0 -0
  288. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/integration/test_scatter_gather.py +0 -0
  289. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/metrics/test_catalog.py +0 -0
  290. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/metrics/test_collector.py +0 -0
  291. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/metrics/test_metrics.py +0 -0
  292. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/metrics/test_read_client.py +0 -0
  293. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/metrics/test_snapshot.py +0 -0
  294. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/plugin/__init__.py +0 -0
  295. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/plugin/test_langfuse_plugin.py +0 -0
  296. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/plugin/test_plugin_discovery.py +0 -0
  297. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/plugin/test_plugin_improvements.py +0 -0
  298. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/plugin/test_plugin_registry.py +0 -0
  299. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/plugin/test_plugin_system.py +0 -0
  300. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/test_main.py +0 -0
  301. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/trace/test_external_trace.py +0 -0
  302. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/trace/test_logger_correlation.py +0 -0
  303. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/trace/test_otel_exporter.py +0 -0
  304. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/trace/test_span_recorder.py +0 -0
  305. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/trace/test_trace_writer.py +0 -0
  306. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/util/test_discovery_http_client.py +0 -0
  307. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/util/test_discovery_http_client_download.py +0 -0
  308. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/util/test_discovery_http_client_upload.py +0 -0
  309. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/util/test_http_client.py +0 -0
  310. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/__init__.py +0 -0
  311. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_admin_control_handling.py +0 -0
  312. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_app.py +0 -0
  313. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_byai_worker.py +0 -0
  314. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_context.py +0 -0
  315. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_control_handling.py +0 -0
  316. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_emitter.py +0 -0
  317. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_fetch_messages.py +0 -0
  318. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_gateway_worker.py +0 -0
  319. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_health_server.py +0 -0
  320. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_heartbeat.py +0 -0
  321. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_message_processing.py +0 -0
  322. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_processor.py +0 -0
  323. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_resume_metadata.py +0 -0
  324. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_sandbox.py +0 -0
  325. {by_framework-0.2.2.dev15 → by_framework-0.2.2.dev16}/tests/worker/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: by-framework
3
- Version: 0.2.2.dev15
3
+ Version: 0.2.2.dev16
4
4
  Summary: 分布式 Agent 调度框架
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -314,6 +314,22 @@ Entry anatomy:
314
314
  best-effort — a background component that fails to construct must never
315
315
  prevent the worker from consuming — and every one of them must be
316
316
  cancelled and awaited in `_shutdown()`, or shutdown hangs on it.
317
+ `_run_once()` acquires a concurrency slot via `_acquire_slot()`, never a
318
+ bare `await self.semaphore.acquire()` — a saturated pool (all
319
+ `max_concurrency` slots held by legitimately long-running tasks) must not
320
+ be indistinguishable from a genuinely stuck consume loop. `_acquire_slot()`
321
+ polls `len(self._running_tasks) >= self.max_concurrency` and calls
322
+ `_mark_consumer_tick()` on every poll while waiting, so `_is_consumer_healthy()`
323
+ (whose own comparison logic is untouched) keeps seeing fresh ticks for as
324
+ long as the loop is merely busy, not stalled. `self._slot_wait_poll_seconds`
325
+ is derived from `_consumer_health_timeout_seconds` (not hardcoded) so the
326
+ two stay proportional under any lease-TTL/stream-block-ms configuration.
327
+ Trade-off this does *not* solve, by design: it cannot distinguish a
328
+ legitimately long task from a deadlocked one that will never return — both
329
+ present as `_running_tasks` stuck at `max_concurrency`; task-level SLA
330
+ enforcement (detecting and cancelling an individual stuck task) is a
331
+ separate concern from consumer-loop liveness and is not implemented here —
332
+ see `CancelTaskCommand` for the existing per-task cancel path.
317
333
 
318
334
  - `src/by_framework/worker/health_server.py` — `WorkerHealthServer`: the
319
335
  `/readyz` readiness HTTP endpoint, on its own daemon thread (mirrors
@@ -216,7 +216,15 @@ regardless.
216
216
  2. `draining` — `SIGTERM`/`SIGINT` received, shutdown in progress
217
217
  3. `evicted` — `admin_lifecycle == "evicted"`
218
218
  4. `suspended` — `admin_lifecycle == "suspended"`
219
- 5. `consumer_stalled` — consume loop stale past `_consumer_health_timeout_seconds`
219
+ 5. `consumer_stalled` — consume loop stale past `_consumer_health_timeout_seconds`.
220
+ "Stale" means the loop itself has stopped making progress (crashed task,
221
+ or an unexpected hang outside slot-waiting) — it deliberately excludes a
222
+ consume loop that is merely waiting for every `max_concurrency` slot to
223
+ free up while legitimately long-running tasks are in flight. A saturated-
224
+ but-alive loop keeps refreshing its liveness signal while it waits (see
225
+ `runner.py`'s `_acquire_slot()` in `docs/architecture/KEY_FILES.md`), so
226
+ it never surfaces here; only a loop that has actually stopped responding
227
+ does.
220
228
  6. `serving` — none of the above; `ready=true`
221
229
 
222
230
  `ready` is `true` if and only if `reason == "serving"`.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "by-framework-langgraph"
3
- version = "0.0.3.dev4"
3
+ version = "0.0.3.dev5"
4
4
  description = "LangGraph integration for by-framework"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "by-framework-trace-langfuse"
3
- version = "0.0.3.dev4"
3
+ version = "0.0.3.dev5"
4
4
  description = "Langfuse trace provider for by-framework"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "by-framework"
3
- version = "0.2.2.dev15"
3
+ version = "0.2.2.dev16"
4
4
  description = "分布式 Agent 调度框架"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -83,6 +83,7 @@ class WorkerRunner:
83
83
  self.worker = worker
84
84
  self.group_name = group_name or self._auto_group_name()
85
85
  self.consumer_name = worker.worker_id
86
+ self.max_concurrency = max_concurrency
86
87
  self.semaphore = asyncio.Semaphore(max_concurrency)
87
88
  self.fetch_count = fetch_count
88
89
  # OTel for worker.execute is emitted via a live wrapping span (see
@@ -121,6 +122,14 @@ class WorkerRunner:
121
122
  float(self.worker.heartbeat_lease_ttl_seconds) * 2.0,
122
123
  stream_block_seconds * 3.0,
123
124
  )
125
+ # Poll interval used by _acquire_slot() while waiting for a saturated
126
+ # semaphore. Derived from _consumer_health_timeout_seconds (not a
127
+ # hardcoded constant) so it stays well under the health-check
128
+ # deadline under any configuration, with a floor to avoid busy-
129
+ # polling if the timeout is configured very small.
130
+ self._slot_wait_poll_seconds = max(
131
+ self._consumer_health_timeout_seconds / 4.0, 1.0
132
+ )
124
133
  # Set as the first step of _shutdown() - see
125
134
  # docs/architecture/worker-readiness-endpoint.md Decision 6.
126
135
  self._draining: bool = False
@@ -386,9 +395,23 @@ class WorkerRunner:
386
395
  finally:
387
396
  await self.redis.xack(stream_name, self.group_name, msg_id)
388
397
 
398
+ async def _acquire_slot(self) -> None:
399
+ """Wait for a concurrency slot, marking the consumer tick on every
400
+ poll so a saturated-but-alive loop isn't judged stalled.
401
+
402
+ Polling on `len(self._running_tasks)` instead of wrapping
403
+ `semaphore.acquire()` in `asyncio.wait_for()` avoids relying on
404
+ Semaphore cancellation semantics for a lock this runner is the only
405
+ waiter on — see design.md's "why not asyncio.wait_for" section.
406
+ """
407
+ while len(self._running_tasks) >= self.max_concurrency:
408
+ self._mark_consumer_tick()
409
+ await asyncio.sleep(self._slot_wait_poll_seconds)
410
+ await self.semaphore.acquire()
411
+
389
412
  async def _run_once(self) -> bool:
390
413
  """Fetch and start processing messages."""
391
- await self.semaphore.acquire()
414
+ await self._acquire_slot()
392
415
 
393
416
  try:
394
417
  messages = await self.fetch_messages(
@@ -401,7 +424,7 @@ class WorkerRunner:
401
424
 
402
425
  for i, (stream_name, msg_id, data_dict) in enumerate(messages):
403
426
  if i > 0:
404
- await self.semaphore.acquire()
427
+ await self._acquire_slot()
405
428
 
406
429
  task = asyncio.create_task(
407
430
  self._process_and_release(stream_name, msg_id, data_dict)
@@ -193,6 +193,81 @@ class StartupOrderStop(RuntimeError):
193
193
  """Sentinel used to stop the infinite runner loop in startup-order tests."""
194
194
 
195
195
 
196
+ class _ContinuousMockRedis(MockRedisRunner):
197
+ """Returns a fresh AskAgentCommand message on every xreadgroup call.
198
+
199
+ Used by the semaphore-saturation tests to simulate a control stream
200
+ that always has more work waiting, so the consume loop is genuinely
201
+ busy (not idle) while a concurrency slot is unavailable.
202
+ """
203
+
204
+ def __init__(self, agent_type: str = "dummy_agent"):
205
+ super().__init__(message_to_return=None)
206
+ self.agent_type = agent_type
207
+ self._counter = 0
208
+
209
+ async def xreadgroup(self, groupname, consumername, streams, count=1, block=0):
210
+ self.called_xreadgroup = True
211
+ self._counter += 1
212
+ msg = AskAgentCommand(
213
+ header=MessageHeader(
214
+ message_id=f"msg-{self._counter}",
215
+ session_id="sess-1",
216
+ trace_id="trace-1",
217
+ target_agent_type=self.agent_type,
218
+ ),
219
+ content="test",
220
+ )
221
+ return [
222
+ [
223
+ RedisKeys.ctrl_stream(self.agent_type).encode(),
224
+ [
225
+ (
226
+ f"{self._counter}-0".encode(),
227
+ {b"data": json.dumps(msg.to_dict()).encode()},
228
+ )
229
+ ],
230
+ ]
231
+ ]
232
+
233
+
234
+ class _SlowWorker(DummyWorker):
235
+ """Every task takes `delay` seconds - stands in for a long-running
236
+ LLM/tool-call agent task that legitimately occupies a concurrency slot."""
237
+
238
+ def __init__(self, delay: float):
239
+ super().__init__()
240
+ self.delay = delay
241
+ self.completed = 0
242
+
243
+ async def _handle_message(self, command, **kwargs):
244
+ await asyncio.sleep(self.delay)
245
+ self.completed += 1
246
+ return AgentTaskResult(status=AgentState.COMPLETED.value)
247
+
248
+
249
+ class _MixedDurationWorker(DummyWorker):
250
+ """The first message processed takes `long_delay`; every subsequent one
251
+ takes `short_delay` - simulates one slot pinned by a long task while
252
+ the rest of the pool cycles normally."""
253
+
254
+ def __init__(self, long_delay: float, short_delay: float):
255
+ super().__init__()
256
+ self.long_delay = long_delay
257
+ self.short_delay = short_delay
258
+ self.completed = 0
259
+ self._long_dispatched = False
260
+ self._lock = asyncio.Lock()
261
+
262
+ async def _handle_message(self, command, **kwargs):
263
+ async with self._lock:
264
+ is_long = not self._long_dispatched
265
+ self._long_dispatched = True
266
+ await asyncio.sleep(self.long_delay if is_long else self.short_delay)
267
+ self.completed += 1
268
+ return AgentTaskResult(status=AgentState.COMPLETED.value)
269
+
270
+
196
271
  class TestWorkerRunner(unittest.IsolatedAsyncioTestCase):
197
272
 
198
273
  async def test_runner_pull_and_dispatch(self):
@@ -1398,6 +1473,139 @@ class TestWorkerRunner(unittest.IsolatedAsyncioTestCase):
1398
1473
  with self.assertRaises(asyncio.CancelledError):
1399
1474
  await runner._consumer_task
1400
1475
 
1476
+ async def test_runner_healthy_during_single_slot_saturation_live(self):
1477
+ """max_concurrency=1: a single task running well past the (shrunk)
1478
+ health timeout, with more messages continuously available, must
1479
+ never flip _is_consumer_healthy() to False - this is the exact
1480
+ scenario from the PRD's Background (real asyncio scheduling, no
1481
+ mocked clock): the consume loop is busy, not stuck."""
1482
+ worker = _SlowWorker(delay=0.3)
1483
+ redis_mock = _ContinuousMockRedis()
1484
+ runner = WorkerRunner(
1485
+ redis_client=redis_mock,
1486
+ worker=worker,
1487
+ group_name="test_group",
1488
+ max_concurrency=1,
1489
+ fetch_count=1,
1490
+ )
1491
+ # Shrink well below the task duration so a stale tick would be
1492
+ # caught, and re-derive the poll interval the same way __init__
1493
+ # does (a smaller floor than production's 1.0s so the test runs
1494
+ # fast, without changing the ratio the design specifies).
1495
+ runner._consumer_health_timeout_seconds = 0.1
1496
+ runner._slot_wait_poll_seconds = max(
1497
+ runner._consumer_health_timeout_seconds / 4.0, 0.02
1498
+ )
1499
+ runner._mark_consumer_tick()
1500
+ runner._consumer_task = asyncio.ensure_future(runner._consume_loop())
1501
+ try:
1502
+ loop = asyncio.get_running_loop()
1503
+ deadline = loop.time() + 1.0
1504
+ saw_unhealthy = False
1505
+ while loop.time() < deadline:
1506
+ if not runner._is_consumer_healthy():
1507
+ saw_unhealthy = True
1508
+ break
1509
+ await asyncio.sleep(0.01)
1510
+ self.assertFalse(
1511
+ saw_unhealthy,
1512
+ "consumer was judged unhealthy while merely saturated, not stuck",
1513
+ )
1514
+ # Sanity: the loop was actually doing work throughout, not
1515
+ # idling because fetch never returned anything.
1516
+ self.assertGreater(worker.completed, 0)
1517
+ finally:
1518
+ runner._consumer_task.cancel()
1519
+ with self.assertRaises(asyncio.CancelledError):
1520
+ await runner._consumer_task
1521
+ await runner.wait_for_tasks()
1522
+
1523
+ async def test_runner_healthy_during_partial_saturation_live(self):
1524
+ """max_concurrency>1: one slot pinned by a long task while the rest
1525
+ of the pool keeps cycling normally must never flip
1526
+ _is_consumer_healthy() to False."""
1527
+ worker = _MixedDurationWorker(long_delay=0.4, short_delay=0.05)
1528
+ redis_mock = _ContinuousMockRedis()
1529
+ runner = WorkerRunner(
1530
+ redis_client=redis_mock,
1531
+ worker=worker,
1532
+ group_name="test_group",
1533
+ max_concurrency=3,
1534
+ fetch_count=1,
1535
+ )
1536
+ runner._consumer_health_timeout_seconds = 0.1
1537
+ runner._slot_wait_poll_seconds = max(
1538
+ runner._consumer_health_timeout_seconds / 4.0, 0.02
1539
+ )
1540
+ runner._mark_consumer_tick()
1541
+ runner._consumer_task = asyncio.ensure_future(runner._consume_loop())
1542
+ try:
1543
+ loop = asyncio.get_running_loop()
1544
+ deadline = loop.time() + 1.0
1545
+ saw_unhealthy = False
1546
+ while loop.time() < deadline:
1547
+ if not runner._is_consumer_healthy():
1548
+ saw_unhealthy = True
1549
+ break
1550
+ await asyncio.sleep(0.01)
1551
+ self.assertFalse(
1552
+ saw_unhealthy,
1553
+ "consumer was judged unhealthy while one slot was merely "
1554
+ "pinned by a long task, not stuck",
1555
+ )
1556
+ # The short-task slots must have cycled multiple times while
1557
+ # the long task was still occupying its own slot.
1558
+ self.assertGreater(worker.completed, 1)
1559
+ finally:
1560
+ runner._consumer_task.cancel()
1561
+ with self.assertRaises(asyncio.CancelledError):
1562
+ await runner._consumer_task
1563
+ await runner.wait_for_tasks()
1564
+
1565
+ async def test_runner_consumer_stalled_detected_despite_free_slot_live(self):
1566
+ """Regression guard: a free concurrency slot must not mask a
1567
+ genuinely stuck consume loop. If fetch/dispatch itself stops
1568
+ making progress - not because every slot is occupied -
1569
+ _is_consumer_healthy() must still flip to False. Uses real
1570
+ asyncio scheduling (wall-clock sleep past the shrunk timeout, no
1571
+ mocked clock) so this fix cannot degrade into "saturated (or
1572
+ merely idle) == always healthy"."""
1573
+ worker = DummyWorker()
1574
+ redis_mock = MockRedisRunner(message_to_return=[])
1575
+ runner = WorkerRunner(
1576
+ redis_client=redis_mock,
1577
+ worker=worker,
1578
+ group_name="test_group",
1579
+ max_concurrency=5,
1580
+ )
1581
+ runner._consumer_health_timeout_seconds = 0.2
1582
+ runner._slot_wait_poll_seconds = 0.05
1583
+ runner._mark_consumer_tick()
1584
+
1585
+ hang_forever = asyncio.Event()
1586
+
1587
+ async def _hanging_fetch(*args, **kwargs):
1588
+ await hang_forever.wait()
1589
+ return []
1590
+
1591
+ # Simulate the consume loop getting stuck somewhere other than the
1592
+ # slot wait (e.g. an unexpected hang inside fetch/dispatch) while a
1593
+ # slot is freely available - _acquire_slot()'s polling branch must
1594
+ # never even be entered here (len(_running_tasks) stays 0, well
1595
+ # under max_concurrency), so it cannot be the thing masking this.
1596
+ runner.fetch_messages = _hanging_fetch
1597
+
1598
+ runner._consumer_task = asyncio.ensure_future(runner._consume_loop())
1599
+ try:
1600
+ await asyncio.sleep(runner._consumer_health_timeout_seconds + 0.3)
1601
+ self.assertFalse(runner._is_consumer_healthy())
1602
+ self.assertEqual(len(runner._running_tasks), 0)
1603
+ finally:
1604
+ hang_forever.set()
1605
+ runner._consumer_task.cancel()
1606
+ with self.assertRaises(asyncio.CancelledError):
1607
+ await runner._consumer_task
1608
+
1401
1609
 
1402
1610
  if __name__ == "__main__":
1403
1611
  unittest.main()
@@ -315,7 +315,7 @@ wheels = [
315
315
 
316
316
  [[package]]
317
317
  name = "by-framework"
318
- version = "0.2.2.dev15"
318
+ version = "0.2.2.dev16"
319
319
  source = { editable = "." }
320
320
  dependencies = [
321
321
  { name = "dill" },
@@ -531,7 +531,7 @@ provides-extras = ["dev"]
531
531
 
532
532
  [[package]]
533
533
  name = "by-framework-langgraph"
534
- version = "0.0.3.dev4"
534
+ version = "0.0.3.dev5"
535
535
  source = { editable = "libs/by-framework-langgraph" }
536
536
  dependencies = [
537
537
  { name = "by-framework" },
@@ -577,7 +577,7 @@ provides-extras = ["dev", "langfuse", "phoenix"]
577
577
 
578
578
  [[package]]
579
579
  name = "by-framework-trace-langfuse"
580
- version = "0.0.3.dev4"
580
+ version = "0.0.3.dev5"
581
581
  source = { editable = "libs/by-framework-trace-langfuse" }
582
582
  dependencies = [
583
583
  { name = "by-framework" },