by-framework 0.2.2.dev13__tar.gz → 0.2.2.dev14__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 (324) hide show
  1. by_framework-0.2.2.dev14/CHANGELOG.md +30 -0
  2. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/PKG-INFO +1 -1
  3. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/architecture/KEY_FILES.md +68 -4
  4. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/pyproject.toml +1 -1
  5. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/client/client.py +6 -0
  6. by_framework-0.2.2.dev14/src/by_framework/worker/_resume_metadata.py +65 -0
  7. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/processor.py +89 -22
  8. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/runner.py +29 -1
  9. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/worker.py +43 -0
  10. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/client/test_client.py +29 -0
  11. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/integration/test_nested_chain.py +148 -0
  12. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_gateway_worker.py +182 -0
  13. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_processor.py +220 -0
  14. by_framework-0.2.2.dev14/tests/worker/test_resume_metadata.py +69 -0
  15. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_runner.py +52 -0
  16. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/uv.lock +1 -1
  17. by_framework-0.2.2.dev13/CHANGELOG.md +0 -16
  18. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.codex/hooks.json +0 -0
  19. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.dockerignore +0 -0
  20. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  21. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  22. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  23. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.github/RELEASING.md +0 -0
  24. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.github/workflows/ci.yml +0 -0
  25. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.github/workflows/deploy-smoke-test.yml +0 -0
  26. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.github/workflows/publish.yml +0 -0
  27. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.github/workflows/stale.yml +0 -0
  28. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.github/workflows/verify.yml +0 -0
  29. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.gitignore +0 -0
  30. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/.pre-commit-config.yaml +0 -0
  31. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/AGENTS.md +0 -0
  32. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/CLAUDE.md +0 -0
  33. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/CODE_OF_CONDUCT.md +0 -0
  34. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/CONTRIBUTING.md +0 -0
  35. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/LICENSE +0 -0
  36. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/Makefile +0 -0
  37. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/README.md +0 -0
  38. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/README_zh.md +0 -0
  39. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/SECURITY.md +0 -0
  40. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/assets/img/architecture_en.png +0 -0
  41. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/assets/img/architecture_zh.png +0 -0
  42. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/autoformat.sh +0 -0
  43. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/deploy/Dockerfile +0 -0
  44. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/deploy/docker-compose.yml +0 -0
  45. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/deploy/entrypoint.sh +0 -0
  46. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/deploy/kubernetes/worker-deployment.yaml +0 -0
  47. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/README.md +0 -0
  48. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/architecture/GUARD_AUTHORING.md +0 -0
  49. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/architecture/production-deployment.md +0 -0
  50. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/architecture/redis-cluster-mode.md +0 -0
  51. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/architecture/suspend-resume-liveness.md +0 -0
  52. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/architecture/worker-readiness-endpoint.md +0 -0
  53. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/by-framework-trace-change-usage.html +0 -0
  54. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/by-framework-trace-complete-guide.html +0 -0
  55. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/plans/2026-05-15-worker-task-state-stats.md +0 -0
  56. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/plans/2026-06-10-trace-metrics-correlation.md +0 -0
  57. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/docs/worker_management.html +0 -0
  58. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/examples/__init__.py +0 -0
  59. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/examples/echo_worker.py +0 -0
  60. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/examples/send_and_verify.py +0 -0
  61. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-adk/README.md +0 -0
  62. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-adk/pyproject.toml +0 -0
  63. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-adk/src/by_framework_adk/__init__.py +0 -0
  64. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-adk/src/by_framework_adk/_utils.py +0 -0
  65. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-adk/src/by_framework_adk/adapter.py +0 -0
  66. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-adk/src/by_framework_adk/worker.py +0 -0
  67. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-adk/tests/test_adapter.py +0 -0
  68. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-adk/tests/test_worker.py +0 -0
  69. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/README.md +0 -0
  70. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/examples/01_basic_chat.py +0 -0
  71. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/examples/02_tools_and_ask_user.py +0 -0
  72. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/examples/03_agent_delegation.py +0 -0
  73. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/examples/04_task_group.py +0 -0
  74. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/examples/05_real_model_react.py +0 -0
  75. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/examples/README.md +0 -0
  76. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/examples/_infra.py +0 -0
  77. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/pyproject.toml +0 -0
  78. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/src/by_framework_agent/__init__.py +0 -0
  79. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/src/by_framework_agent/litellm_client.py +0 -0
  80. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/src/by_framework_agent/loop.py +0 -0
  81. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/src/by_framework_agent/model_client.py +0 -0
  82. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/src/by_framework_agent/testing.py +0 -0
  83. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/src/by_framework_agent/tool_spec.py +0 -0
  84. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/src/by_framework_agent/worker.py +0 -0
  85. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/tests/conftest.py +0 -0
  86. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/tests/test_agent_as_tool.py +0 -0
  87. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/tests/test_ask_user_suspend_resume.py +0 -0
  88. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/tests/test_cancel_cleanup.py +0 -0
  89. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/tests/test_native_agent_worker.py +0 -0
  90. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/tests/test_task_group_batching.py +0 -0
  91. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-agent/tests/test_tool_calling_loop.py +0 -0
  92. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/Dockerfile +0 -0
  93. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/README.md +0 -0
  94. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/frontend/index.html +0 -0
  95. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/frontend/package-lock.json +0 -0
  96. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/frontend/package.json +0 -0
  97. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/frontend/src/main.jsx +0 -0
  98. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/frontend/src/styles.css +0 -0
  99. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/frontend/vite.config.js +0 -0
  100. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/pyproject.toml +0 -0
  101. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/src/by_framework_dashboard/__init__.py +0 -0
  102. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/src/by_framework_dashboard/adapters.py +0 -0
  103. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/src/by_framework_dashboard/dashboard.py +0 -0
  104. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/src/by_framework_dashboard/static/__init__.py +0 -0
  105. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/src/by_framework_dashboard/static/app.js +0 -0
  106. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/src/by_framework_dashboard/static/index.html +0 -0
  107. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/src/by_framework_dashboard/static/styles.css +0 -0
  108. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-dashboard/tests/test_dashboard_server.py +0 -0
  109. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-history-byclaw/README.md +0 -0
  110. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-history-byclaw/pyproject.toml +0 -0
  111. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-history-byclaw/src/by_framework_history_byclaw/__init__.py +0 -0
  112. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-history-byclaw/src/by_framework_history_byclaw/byclaw_history.py +0 -0
  113. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-history-byclaw/tests/test_byclaw_history.py +0 -0
  114. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-history-postgres/README.md +0 -0
  115. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-history-postgres/pyproject.toml +0 -0
  116. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-history-postgres/src/by_framework_history_postgres/__init__.py +0 -0
  117. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-history-postgres/src/by_framework_history_postgres/postgres.py +0 -0
  118. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-history-postgres/tests/test_postgres_history_storage.py +0 -0
  119. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-langgraph/README.md +0 -0
  120. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-langgraph/pyproject.toml +0 -0
  121. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-langgraph/src/by_framework_langgraph/__init__.py +0 -0
  122. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-langgraph/src/by_framework_langgraph/_utils.py +0 -0
  123. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-langgraph/src/by_framework_langgraph/adapter.py +0 -0
  124. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-langgraph/src/by_framework_langgraph/tools.py +0 -0
  125. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-langgraph/src/by_framework_langgraph/worker.py +0 -0
  126. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-langgraph/tests/test_adapter.py +0 -0
  127. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-langgraph/tests/test_tools.py +0 -0
  128. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-langgraph/tests/test_utils.py +0 -0
  129. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-langfuse/README.md +0 -0
  130. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-langfuse/pyproject.toml +0 -0
  131. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-langfuse/src/by_framework_trace_langfuse/__init__.py +0 -0
  132. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-langfuse/src/by_framework_trace_langfuse/langfuse.py +0 -0
  133. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-langfuse/tests/test_langfuse_import.py +0 -0
  134. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-phoenix/README.md +0 -0
  135. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-phoenix/pyproject.toml +0 -0
  136. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-phoenix/src/by_framework_trace_phoenix/__init__.py +0 -0
  137. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-phoenix/src/by_framework_trace_phoenix/phoenix.py +0 -0
  138. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-phoenix/tests/test_phoenix_import.py +0 -0
  139. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-phoenix/tests/test_phoenix_plugin.py +0 -0
  140. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-query/pyproject.toml +0 -0
  141. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-query/src/by_framework_trace_query/__init__.py +0 -0
  142. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-query/src/by_framework_trace_query/client.py +0 -0
  143. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-query/src/by_framework_trace_query/merger.py +0 -0
  144. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-query/src/by_framework_trace_query/models.py +0 -0
  145. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-query/src/by_framework_trace_query/redis_source.py +0 -0
  146. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/libs/by-framework-trace-query/tests/test_trace_query_sdk.py +0 -0
  147. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/pylintrc +0 -0
  148. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/scripts/.map.conf +0 -0
  149. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/scripts/check-doc-discipline.sh +0 -0
  150. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/scripts/check-entry-freshness.sh +0 -0
  151. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/scripts/check-map-territory.sh +0 -0
  152. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/scripts/map-stop-hook.sh +0 -0
  153. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/scripts/python_quality.sh +0 -0
  154. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/scripts/test-growth-ladder.sh +0 -0
  155. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/scripts/verify.sh +0 -0
  156. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/__init__.py +0 -0
  157. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/__main__.py +0 -0
  158. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/admin/__init__.py +0 -0
  159. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/admin/cli.py +0 -0
  160. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/admin/worker_manager.py +0 -0
  161. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/client/__init__.py +0 -0
  162. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/client/byai_client.py +0 -0
  163. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/common/__init__.py +0 -0
  164. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/common/config.py +0 -0
  165. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/common/constants.py +0 -0
  166. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/common/emitter.py +0 -0
  167. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/common/exceptions.py +0 -0
  168. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/common/logger.py +0 -0
  169. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/common/redis_client.py +0 -0
  170. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/__init__.py +0 -0
  171. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/availability.py +0 -0
  172. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/delivery_gate.py +0 -0
  173. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/discovery.py +0 -0
  174. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/extensions/__init__.py +0 -0
  175. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/extensions/agent_config.py +0 -0
  176. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/extensions/agent_config_audit.py +0 -0
  177. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/extensions/plugin.py +0 -0
  178. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/extensions/registry.py +0 -0
  179. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/extensions/trace_provider.py +0 -0
  180. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/__init__.py +0 -0
  181. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/action_type.py +0 -0
  182. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/agent_state.py +0 -0
  183. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/byai_codec.py +0 -0
  184. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/byai_command.py +0 -0
  185. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/byai_types.py +0 -0
  186. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/commands.py +0 -0
  187. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/content_codec.py +0 -0
  188. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/content_type.py +0 -0
  189. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/data_message.py +0 -0
  190. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/data_shapes.py +0 -0
  191. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/event_type.py +0 -0
  192. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/events.py +0 -0
  193. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/message.py +0 -0
  194. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/message_header.py +0 -0
  195. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/responses.py +0 -0
  196. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/protocol/results.py +0 -0
  197. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/registry.py +0 -0
  198. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/__init__.py +0 -0
  199. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/agent_config_manager.py +0 -0
  200. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/agent_runtime_state.py +0 -0
  201. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/file_manager.py +0 -0
  202. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/file_paths.py +0 -0
  203. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/file_permissions.py +0 -0
  204. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/filestore/__init__.py +0 -0
  205. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/filestore/base.py +0 -0
  206. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/filestore/local.py +0 -0
  207. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/history/__init__.py +0 -0
  208. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/history/base.py +0 -0
  209. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/history/history_manager.py +0 -0
  210. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/history/in_memory.py +0 -0
  211. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/runtime/session_manager.py +0 -0
  212. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/wait_gate.py +0 -0
  213. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/wait_index.py +0 -0
  214. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/wait_reply.py +0 -0
  215. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/wait_sweeper.py +0 -0
  216. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/wakeup_controller.py +0 -0
  217. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/core/workspace.py +0 -0
  218. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/errors/__init__.py +0 -0
  219. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/errors/base.py +0 -0
  220. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/errors/common.py +0 -0
  221. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/errors/execution.py +0 -0
  222. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/errors/http.py +0 -0
  223. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/errors/protocol.py +0 -0
  224. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/errors/registry.py +0 -0
  225. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/metrics/__init__.py +0 -0
  226. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/metrics/catalog.py +0 -0
  227. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/metrics/collector.py +0 -0
  228. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/metrics/read_client.py +0 -0
  229. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/metrics/snapshot.py +0 -0
  230. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/trace/__init__.py +0 -0
  231. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/trace/external_trace.py +0 -0
  232. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/trace/span_recorder.py +0 -0
  233. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/trace/trace_schema.py +0 -0
  234. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/trace/trace_writer.py +0 -0
  235. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/util/__init__.py +0 -0
  236. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/util/discovery_http_client.py +0 -0
  237. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/util/generate_message_id.py +0 -0
  238. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/util/http_client.py +0 -0
  239. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/__init__.py +0 -0
  240. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/_control_handling.py +0 -0
  241. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/_execution_tracking.py +0 -0
  242. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/_message_processing.py +0 -0
  243. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/app.py +0 -0
  244. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/byai_context.py +0 -0
  245. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/byai_worker.py +0 -0
  246. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/context.py +0 -0
  247. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/health_server.py +0 -0
  248. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/heartbeat.py +0 -0
  249. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/sandbox/__init__.py +0 -0
  250. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/src/by_framework/worker/sandbox/hook_sandbox.py +0 -0
  251. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/admin/__init__.py +0 -0
  252. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/admin/test_cli.py +0 -0
  253. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/client/__init__.py +0 -0
  254. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/common/__init__.py +0 -0
  255. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/common/test_config.py +0 -0
  256. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/common/test_constants.py +0 -0
  257. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/common/test_exceptions.py +0 -0
  258. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/common/test_logger.py +0 -0
  259. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/common/test_redis_client.py +0 -0
  260. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/conftest.py +0 -0
  261. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/__init__.py +0 -0
  262. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/protocol/__init__.py +0 -0
  263. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/protocol/test_byai_codec.py +0 -0
  264. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/protocol/test_command_wire.py +0 -0
  265. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/protocol/test_protocol.py +0 -0
  266. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/protocol/test_results.py +0 -0
  267. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/runtime/history/__init__.py +0 -0
  268. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/runtime/history/test_history_persistence.py +0 -0
  269. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/runtime/test_file_access_context.py +0 -0
  270. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/runtime/test_file_manager_default_storage.py +0 -0
  271. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/runtime/test_file_paths.py +0 -0
  272. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/runtime/test_file_permissions.py +0 -0
  273. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/runtime/test_filestore_local_collection.py +0 -0
  274. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/runtime/test_filestore_local_mutation.py +0 -0
  275. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/runtime/test_filestore_local_read.py +0 -0
  276. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/runtime/test_filestore_local_search.py +0 -0
  277. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/test_agent_config_audit.py +0 -0
  278. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/test_availability.py +0 -0
  279. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/test_discovery.py +0 -0
  280. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/test_registry.py +0 -0
  281. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/test_wait_gate.py +0 -0
  282. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/test_wait_index.py +0 -0
  283. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/test_wait_sweeper.py +0 -0
  284. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/core/test_worker_manager.py +0 -0
  285. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/integration/__init__.py +0 -0
  286. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/integration/test_ask_user_flow.py +0 -0
  287. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/integration/test_callback_flow.py +0 -0
  288. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/integration/test_logger_integration.py +0 -0
  289. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/integration/test_orphan_recovery.py +0 -0
  290. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/integration/test_scatter_gather.py +0 -0
  291. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/metrics/test_catalog.py +0 -0
  292. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/metrics/test_collector.py +0 -0
  293. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/metrics/test_metrics.py +0 -0
  294. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/metrics/test_read_client.py +0 -0
  295. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/metrics/test_snapshot.py +0 -0
  296. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/plugin/__init__.py +0 -0
  297. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/plugin/test_langfuse_plugin.py +0 -0
  298. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/plugin/test_plugin_discovery.py +0 -0
  299. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/plugin/test_plugin_improvements.py +0 -0
  300. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/plugin/test_plugin_registry.py +0 -0
  301. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/plugin/test_plugin_system.py +0 -0
  302. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/test_main.py +0 -0
  303. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/trace/test_external_trace.py +0 -0
  304. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/trace/test_logger_correlation.py +0 -0
  305. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/trace/test_otel_exporter.py +0 -0
  306. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/trace/test_span_recorder.py +0 -0
  307. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/trace/test_trace_writer.py +0 -0
  308. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/util/test_discovery_http_client.py +0 -0
  309. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/util/test_discovery_http_client_download.py +0 -0
  310. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/util/test_discovery_http_client_upload.py +0 -0
  311. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/util/test_http_client.py +0 -0
  312. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/__init__.py +0 -0
  313. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_admin_control_handling.py +0 -0
  314. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_app.py +0 -0
  315. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_byai_worker.py +0 -0
  316. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_context.py +0 -0
  317. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_control_handling.py +0 -0
  318. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_emitter.py +0 -0
  319. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_fetch_messages.py +0 -0
  320. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_health_server.py +0 -0
  321. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_heartbeat.py +0 -0
  322. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_message_processing.py +0 -0
  323. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_sandbox.py +0 -0
  324. {by_framework-0.2.2.dev13 → by_framework-0.2.2.dev14}/tests/worker/test_workspace.py +0 -0
@@ -0,0 +1,30 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Fixed
11
+ - A resumed agent can read its own request metadata again. Previously,
12
+ `command.header.metadata` after a suspend (`ask_user`, or a `call_agent`
13
+ hop) held only the metadata of the message that woke the agent up, and
14
+ everything it had originally been dispatched with was gone. It is now the
15
+ original dispatch metadata with the waking message's merged on top (the
16
+ waking message wins on key collisions). Handlers that treated the resumed
17
+ header's metadata as "only what this hop just sent" will now see additional
18
+ keys. Per-hop trace fields (`trace_parent_span_id`,
19
+ `framework_parent_span_id`, `langfuse_parent_observation_id`) still
20
+ describe the current hop and are not restored from the record. Executions
21
+ recorded before this change are unaffected — they degrade to the previous
22
+ behaviour rather than erroring.
23
+
24
+ ### Added
25
+ - Standardized Open Source governance files (CONTRIBUTING, CODE_OF_CONDUCT, SECURITY).
26
+ - GitHub Issue and Pull Request templates.
27
+ - Dependabot configuration for dependency monitoring.
28
+
29
+ ## [0.2.0] - 2026-05-13
30
+ ### Initial release
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: by-framework
3
- Version: 0.2.2.dev13
3
+ Version: 0.2.2.dev14
4
4
  Summary: 分布式 Agent 调度框架
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -260,7 +260,13 @@ Entry anatomy:
260
260
  implementations fall back to minting a fresh execution_id. Root-dispatch
261
261
  trace writes (`_write_trace_root_start/_end`) must only fire when
262
262
  `not parent_message_id` — firing them on every `call_agent` hop would
263
- duplicate trace roots.
263
+ duplicate trace roots. `initialize_execution()`'s payload carries the
264
+ dispatch `metadata`: a root agent suspends (an `ask_user` round is the
265
+ common case) and comes back on a `ResumeCommand` carrying the *callee's*
266
+ metadata, so the record is the only place its own request metadata still
267
+ exists for `worker.py`'s inbound restore to find. `runner.py` records the
268
+ same field independently on first pickup, which is what makes this work
269
+ for dispatchers that don't write it (TS/Java callers, a bare `xadd`).
264
270
 
265
271
  - `src/by_framework/worker/runner.py` — `WorkerRunner`, the consume loop:
266
272
  `XREADGROUP` fetch, command dispatch, resume/suspend bookkeeping, denylist
@@ -290,7 +296,16 @@ Entry anatomy:
290
296
  suspended caller persists as `WAITING_AGENT`/`WAITING_USER` precisely to
291
297
  keep that inference true; reusing QUEUED for any post-pickup state silently
292
298
  makes a resume re-derive its identity from the message header instead of
293
- the record. Every `ResumeCommand` passes the `core/wait_gate.py`
299
+ the record. On a **non-resume** pickup it also writes `header.metadata`
300
+ onto the record (as an `update_execution_status` kwarg, or in the
301
+ `save_execution` fallback payload): `header.metadata` *is* the dispatch
302
+ metadata by definition, so recording it here — rather than trusting
303
+ whoever sent the message to have done it — is what makes
304
+ `worker.py`'s inbound restore work for a client root dispatch and for a
305
+ TS/Java caller, neither of which writes the field. The `ResumeCommand`
306
+ guard is load-bearing: a resume writing its own waking metadata over the
307
+ record would destroy the stored original that the restore exists to
308
+ recover, and nothing else keeps a copy. Every `ResumeCommand` passes the `core/wait_gate.py`
294
309
  idempotency gate *here*, before the execution lookup — and being upstream
295
310
  of `GatewayWorker` is what also puts it before Task Group join, whose
296
311
  `HINCRBY completed` a duplicate would push past `total` and aggregate a
@@ -353,6 +368,22 @@ Entry anatomy:
353
368
  transient hop overwrite the caller's own data instead of being layered
354
369
  under `task_result.metadata` like `_enqueue_agent_return()`'s merge
355
370
  already does correctly.
371
+ That is the OUTBOUND direction. The INBOUND one — what this execution's own
372
+ handler reads — is `_restore_inbound_metadata()`, and it is deliberately a
373
+ different rule: the original dispatch metadata from the same snapshot is
374
+ the base and the waking message's metadata is **merged on top** (waking
375
+ message wins collisions), because this agent *is* the addressee of that
376
+ message. Without it, everything a handler was dispatched with disappears
377
+ the moment it first suspends. Two constraints keep the directions from
378
+ contaminating each other: the restore must build a NEW command
379
+ (`prepare_command_for_processing()` returns the *same* object for the base
380
+ worker, so an in-place header mutation would rewrite `raw_command`'s and
381
+ leak the merge into the reply), and `_resolve_reply_command()` must keep
382
+ taking `raw_command`. The shared merge rule, including which framework
383
+ trace keys are excluded from the restored base, lives in
384
+ `_resume_metadata.py` — one copy, because the last time these two files
385
+ each carried their own version of a resume rule the processor was left
386
+ behind for a whole commit.
356
387
  The success path must NOT reply while `context._is_suspended` — a suspended
357
388
  execution has no result, and forwarding the value the handler returned in
358
389
  order to unwind both wakes the caller early and consumes the single reply it
@@ -431,8 +462,22 @@ Entry anatomy:
431
462
  to no execution. `_resolve_reply_header()` must read a resumed execution's
432
463
  caller from the execution record, not from the resume header (which names
433
464
  the sub-agent), while excluding `CLIENT_SOURCE_AGENT_TYPE` for the reason
434
- spelled out under `worker.py`; and no callback may be sent while
435
- `context._is_suspended` (with the same terminal-status exception).
465
+ spelled out under `worker.py`; it restores **four** fields from that record
466
+ `source_agent_type`, `parent_message_id`, `task_group_id` and
467
+ `metadata` — the last one as a full replacement, never merged with the
468
+ waking message's own metadata, exactly as `worker.py`'s
469
+ `_resolve_reply_command()` does (restoring only the three routing fields is
470
+ the drift this entry warns about: it shipped that way and leaked the waking
471
+ hop's metadata to the caller); `_restore_inbound_metadata()` mirrors
472
+ `worker.py`'s inbound merge for what the *handler* reads, and must run even
473
+ when `_resolve_reply_header()` returned `None` — a client-dispatched root
474
+ is owed no reply but still has its own metadata to get back, so the two
475
+ must not share a short-circuit. `_load_execution_snapshot()` is the single
476
+ registry read feeding both directions; being fail-soft there is what keeps
477
+ a registry error a degrade rather than a lost reply. The reply is built
478
+ from `raw_command`, never the inbound-restored one. And no callback may be
479
+ sent while `context._is_suspended` (with the same terminal-status
480
+ exception).
436
481
  Being a *second* entry point for replies, it carries the same
437
482
  `core/wait_gate.py` gate as `runner.py` — a gate on one of two doors is not
438
483
  a gate, and an ungated reply here would both wake a resolved caller and
@@ -441,6 +486,25 @@ Entry anatomy:
441
486
  `call_agents`' queued stand-ins on the same terms as `worker.py` — after
442
487
  the handler returns, never when it raised.
443
488
 
489
+ - `src/by_framework/worker/_resume_metadata.py` — the one copy of the
490
+ **inbound** resume-metadata rule, shared by `worker.py` and `processor.py`
491
+ so the two cannot drift again. `merge_resume_metadata()` puts the
492
+ execution's original dispatch metadata underneath the waking message's
493
+ (waking message wins collisions) — the opposite of the outbound
494
+ replacement, because a resumed handler *is* the addressee of the message
495
+ that woke it. `FRAMEWORK_HOP_METADATA_KEYS` (`trace_parent_span_id`,
496
+ `framework_parent_span_id`, `langfuse_parent_observation_id`) are dropped
497
+ from the restored base: the framework injects them per dispatch, so a
498
+ stored copy describes the hop that *created* the execution, and
499
+ `context.py`'s `_resolve_call_langfuse_parent_id()` reads that key straight
500
+ off the current command — restoring it would parent a post-resume call to
501
+ an observation from before the suspend. The filter belongs here, at read
502
+ time, and not at write time: the outbound path deliberately restores the
503
+ stored dict verbatim, trace keys included, to stay identical to the
504
+ non-suspended reply. A missing/absent stored value degrades to the waking
505
+ message's metadata unchanged, which is the pre-restore behaviour, so old
506
+ records and cross-SDK callers need no migration or version gate.
507
+
444
508
  - `src/by_framework/core/registry.py` — `WorkerRegistry`: Redis-backed worker
445
509
  membership/heartbeat/execution-state, admin lifecycle, locking primitives.
446
510
  `mark_execution_finished()` must stamp `finished_at` only
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "by-framework"
3
- version = "0.2.2.dev13"
3
+ version = "0.2.2.dev14"
4
4
  description = "分布式 Agent 调度框架"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -971,6 +971,12 @@ class GatewayClient:
971
971
  "parent_message_id": params["parent_message_id"] or "",
972
972
  "source_agent_type": CLIENT_SOURCE_AGENT_TYPE,
973
973
  "target_agent_type": params["target_agent_type"],
974
+ # What this execution was dispatched with, so a worker
975
+ # can restore it after the execution suspends and
976
+ # resumes (see worker/_resume_metadata.py). Only the
977
+ # non-RESUME branch reaches here, so this never
978
+ # overwrites a suspended execution's original.
979
+ "metadata": dict(metadata or {}),
974
980
  "stream_name": route.stream_name,
975
981
  "status": "QUEUED",
976
982
  "route_policy": route_policy,
@@ -0,0 +1,65 @@
1
+ """The inbound half of a resumed execution's metadata.
2
+
3
+ Two different directions restore metadata on a resume, and they are NOT the
4
+ same rule. Keep them apart:
5
+
6
+ - **Outbound** (``GatewayWorker._resolve_reply_command`` /
7
+ ``GatewayProcessor._resolve_reply_header``): the header a resumed execution
8
+ *sends* to its caller. The stored dispatch metadata **replaces**
9
+ ``header.metadata`` wholesale — the waking hop's data is plumbing the
10
+ original caller never asked for, and ``_enqueue_agent_return``'s
11
+ ``{**header.metadata, **task_result.metadata}`` is the one sanctioned
12
+ channel for a handler to forward part of it.
13
+ - **Inbound** (this module): the header a resumed execution's own handler
14
+ *reads*. Here the waking message's metadata is legitimate payload — an
15
+ ``ask_user`` answer's metadata was sent BY a client TO this agent — so it is
16
+ merged on top of the original dispatch metadata rather than discarded.
17
+
18
+ Without this, everything a handler was originally dispatched with disappears
19
+ the first time it suspends: it comes back seeing only whatever woke it up.
20
+ """
21
+
22
+ from typing import Any, Mapping, Optional
23
+
24
+ # Injected per dispatch by AgentContext._dispatch_single_task, not supplied by
25
+ # business code. A stored copy of them is stale by definition — it describes
26
+ # the hop that dispatched the execution, not the hop resuming it now — so they
27
+ # are dropped from the restored base and always come from the current message.
28
+ # Letting them through would surface a dispatch-time span id where the waking
29
+ # message has none, and AgentContext._resolve_call_langfuse_parent_id() reads
30
+ # exactly that as its last-resort fallback: a call made after a resume would
31
+ # parent to the original dispatch's observation.
32
+ FRAMEWORK_HOP_METADATA_KEYS = frozenset(
33
+ {
34
+ "trace_parent_span_id",
35
+ "framework_parent_span_id",
36
+ "langfuse_parent_observation_id",
37
+ }
38
+ )
39
+
40
+
41
+ def merge_resume_metadata(
42
+ stored: Optional[Mapping[str, Any]],
43
+ incoming: Optional[Mapping[str, Any]],
44
+ ) -> dict[str, Any]:
45
+ """Merge an execution's original dispatch metadata under this hop's.
46
+
47
+ ``stored`` is the ``metadata`` field of the execution record (what the
48
+ execution was originally dispatched with); ``incoming`` is the waking
49
+ message's own metadata. The waking message wins on key collisions: it is
50
+ the newer, more specific hop, and this keeps the property that every key a
51
+ handler can read today stays readable — the restore only ever adds keys.
52
+
53
+ A ``stored`` that is missing (an execution recorded before this field
54
+ existed, or one dispatched by an SDK that doesn't write it) degrades to
55
+ ``incoming`` unchanged, i.e. exactly the pre-restore behaviour.
56
+
57
+ Neither argument is mutated.
58
+ """
59
+ merged = {
60
+ key: value
61
+ for key, value in (stored or {}).items()
62
+ if key not in FRAMEWORK_HOP_METADATA_KEYS
63
+ }
64
+ merged.update(incoming or {})
65
+ return merged
@@ -24,6 +24,7 @@ from by_framework.core.protocol.results import (
24
24
  from by_framework.core.runtime.file_permissions import FilePermissionPolicy
25
25
  from by_framework.core.wait_gate import consume_wait_entry, emit_orphaned_reply
26
26
  from by_framework.core.wait_reply import flush_pending_group_replies
27
+ from by_framework.worker._resume_metadata import merge_resume_metadata
27
28
  from by_framework.worker.context import AgentContext
28
29
 
29
30
  ContextHandler = Callable[
@@ -99,8 +100,18 @@ class GatewayProcessor:
99
100
  )
100
101
  return None
101
102
 
102
- reply_header = await self._resolve_reply_header(command)
103
+ # One lookup feeds both directions: the header this execution replies
104
+ # with, and the header its own handler reads. They are different
105
+ # rules over the same record — see _resolve_reply_header and
106
+ # _restore_inbound_metadata.
107
+ snapshot = (
108
+ await self._load_execution_snapshot(command) if is_agent_return else None
109
+ )
110
+ raw_command = command
111
+ reply_header = self._resolve_reply_header(command, snapshot)
103
112
  has_source_agent = reply_header is not None
113
+ command = self._restore_inbound_metadata(command, is_agent_return, snapshot)
114
+ header = command.header
104
115
 
105
116
  context = AgentContext(
106
117
  session_id=header.session_id,
@@ -163,8 +174,10 @@ class GatewayProcessor:
163
174
  getattr(context, "_is_suspended", False)
164
175
  ) and not is_terminal_state(task_result.status)
165
176
  if has_source_agent and not is_suspended:
177
+ # raw_command, not the inbound-restored one: the reply is the
178
+ # outbound direction and must not inherit the inbound merge.
166
179
  await self._enqueue_callback(
167
- command,
180
+ raw_command,
168
181
  task_result.status,
169
182
  task_result.reply_data,
170
183
  content=task_result.content,
@@ -210,7 +223,7 @@ class GatewayProcessor:
210
223
 
211
224
  if has_source_agent:
212
225
  await self._enqueue_callback(
213
- command,
226
+ raw_command,
214
227
  AgentState.FAILED.value,
215
228
  {"error": str(e)},
216
229
  reply_header=reply_header,
@@ -221,47 +234,101 @@ class GatewayProcessor:
221
234
  )
222
235
  raise
223
236
 
224
- async def _resolve_reply_header(
237
+ async def _load_execution_snapshot(
225
238
  self, command: GatewayCommand
226
- ) -> Optional[MessageHeader]:
227
- """Return the header describing the caller this execution owes a reply
228
- to, or None when nobody is waiting.
239
+ ) -> Optional[dict[str, Any]]:
240
+ """Read the execution record the original dispatch wrote.
229
241
 
230
- Mirrors ``GatewayWorker._resolve_reply_command``: a resume's header
231
- describes the sub-agent that just finished, so the caller has to be
232
- read back from the execution record the original dispatch wrote — and,
233
- for the same reason as there, ``CLIENT_SOURCE_AGENT_TYPE`` is not a
234
- caller. It is what a client writes on a root execution's record, and
235
- nothing consumes its control stream.
242
+ Fetched once per resume and shared by both restore directions.
243
+ Fail-soft: a registry error degrades to "no record", which each caller
244
+ then handles as its own no-op.
236
245
  """
237
246
  header = command.header
238
- if not isinstance(command, ResumeCommand):
239
- return header if header.source_agent_type else None
240
-
241
- execution: Optional[dict[str, Any]] = None
242
247
  try:
243
248
  from by_framework.core.registry import WorkerRegistry
244
249
 
245
- execution = await WorkerRegistry(self.redis).get_execution_by_message_id(
250
+ return await WorkerRegistry(self.redis).get_execution_by_message_id(
246
251
  header.message_id, session_id=header.session_id
247
252
  )
248
253
  except Exception as error: # pylint: disable=broad-exception-caught
249
254
  self.logger.warning(
250
- "[%s] Could not resolve the caller of resumed execution %s: %s",
255
+ "[%s] Could not load the execution record of resumed execution "
256
+ "%s: %s",
251
257
  self.worker_id,
252
258
  header.message_id,
253
259
  error,
254
260
  )
255
261
  return None
256
262
 
257
- caller_agent_type = str((execution or {}).get("source_agent_type", "") or "")
263
+ def _resolve_reply_header(
264
+ self,
265
+ command: GatewayCommand,
266
+ snapshot: Optional[dict[str, Any]],
267
+ ) -> Optional[MessageHeader]:
268
+ """Return the header describing the caller this execution owes a reply
269
+ to, or None when nobody is waiting.
270
+
271
+ Mirrors ``GatewayWorker._resolve_reply_command``: a resume's header
272
+ describes the sub-agent that just finished, so the caller has to be
273
+ read back from the execution record the original dispatch wrote — and,
274
+ for the same reason as there, ``CLIENT_SOURCE_AGENT_TYPE`` is not a
275
+ caller. It is what a client writes on a root execution's record, and
276
+ nothing consumes its control stream.
277
+
278
+ Four fields come from that snapshot, not from the waking message:
279
+ ``source_agent_type``, ``parent_message_id``, ``task_group_id`` and
280
+ ``metadata``. The last one is restored as a full replacement rather
281
+ than a merge, exactly as ``GatewayWorker._resolve_reply_command``
282
+ does it: the waking message (an ``ask_user`` answer, or a sub-call's
283
+ reply) is transient plumbing for that one hop, not something the
284
+ caller ever sent. A snapshot missing the field (an execution recorded
285
+ before it existed) degrades to an empty dict rather than leaking the
286
+ waking message's metadata to the caller.
287
+
288
+ This is the OUTBOUND direction only. What the handler itself reads is
289
+ ``_restore_inbound_metadata``, which merges rather than replaces —
290
+ and which must run even when this returns None, since a
291
+ client-dispatched root has no caller but still has its own metadata
292
+ to get back.
293
+ """
294
+ header = command.header
295
+ if not isinstance(command, ResumeCommand):
296
+ return header if header.source_agent_type else None
297
+
298
+ caller_agent_type = str((snapshot or {}).get("source_agent_type", "") or "")
258
299
  if not caller_agent_type or caller_agent_type == CLIENT_SOURCE_AGENT_TYPE:
259
300
  return None
260
301
  return replace(
261
302
  header,
262
303
  source_agent_type=caller_agent_type,
263
- parent_message_id=str((execution or {}).get("parent_message_id", "") or ""),
264
- task_group_id=str((execution or {}).get("task_group_id", "") or ""),
304
+ parent_message_id=str((snapshot or {}).get("parent_message_id", "") or ""),
305
+ task_group_id=str((snapshot or {}).get("task_group_id", "") or ""),
306
+ metadata=dict((snapshot or {}).get("metadata") or {}),
307
+ )
308
+
309
+ @staticmethod
310
+ def _restore_inbound_metadata(
311
+ command: GatewayCommand,
312
+ is_agent_return: bool,
313
+ snapshot: Optional[dict[str, Any]],
314
+ ) -> GatewayCommand:
315
+ """Give a resumed handler its own dispatch metadata back.
316
+
317
+ Mirrors ``GatewayWorker._restore_inbound_metadata``, including its
318
+ merge-don't-replace rule and its no-mutation rule; see that docstring
319
+ for why the inbound direction differs from the outbound one.
320
+ """
321
+ if not is_agent_return:
322
+ return command
323
+ return replace(
324
+ command,
325
+ header=replace(
326
+ command.header,
327
+ metadata=merge_resume_metadata(
328
+ (snapshot or {}).get("metadata"),
329
+ command.header.metadata,
330
+ ),
331
+ ),
265
332
  )
266
333
 
267
334
  async def _enqueue_callback(
@@ -625,11 +625,33 @@ class WorkerRunner:
625
625
  if registry:
626
626
  if existing_execution:
627
627
  if hasattr(registry, "update_execution_status"):
628
+ status_fields: dict[str, Any] = {
629
+ "worker_id": self.worker.worker_id
630
+ }
631
+ if not isinstance(command, ResumeCommand):
632
+ # The dispatch metadata, recorded by whoever is
633
+ # executing the message rather than by whoever
634
+ # sent it: header.metadata IS the dispatch
635
+ # metadata by definition, so this is correct for
636
+ # every dispatcher — including a client root
637
+ # dispatch and a TS/Java caller, neither of which
638
+ # writes the field (see context.py's
639
+ # initialize_execution, which does, and is kept
640
+ # because it is the only record that exists
641
+ # before a callee is ever picked up).
642
+ # _handle_message reads it back to restore what
643
+ # this execution was originally asked for once it
644
+ # resumes.
645
+ #
646
+ # A ResumeCommand must NOT write it: the waking
647
+ # message's metadata would overwrite the very
648
+ # original this exists to preserve.
649
+ status_fields["metadata"] = dict(header.metadata)
628
650
  await registry.update_execution_status(
629
651
  execution_id,
630
652
  header.session_id,
631
653
  "RUNNING",
632
- worker_id=self.worker.worker_id,
654
+ **status_fields,
633
655
  )
634
656
  elif hasattr(registry, "save_execution"):
635
657
  await registry.save_execution(
@@ -641,6 +663,12 @@ class WorkerRunner:
641
663
  "parent_message_id": header.parent_message_id or "",
642
664
  "worker_id": self.worker.worker_id,
643
665
  "target_agent_type": header.target_agent_type,
666
+ # Same reason as the update branch above. This one
667
+ # is the no-existing-record fallback, so there is
668
+ # no stored original to protect and no ResumeCommand
669
+ # guard to make: whatever woke this is all this
670
+ # execution has ever been told.
671
+ "metadata": dict(header.metadata),
644
672
  "stream_name": stream_name,
645
673
  "redis_message_id": msg_id,
646
674
  "status": "RUNNING",
@@ -59,6 +59,7 @@ from by_framework.trace.span_recorder import TraceSpan, str_to_uint64
59
59
  from by_framework.worker.context import AgentContext, current_agent_context_var
60
60
  from by_framework.worker.heartbeat import WorkerHeartbeat
61
61
 
62
+ from ._resume_metadata import merge_resume_metadata
62
63
  from .sandbox.hook_sandbox import active_workspace
63
64
 
64
65
 
@@ -377,6 +378,47 @@ class GatewayWorker(ABC):
377
378
  ),
378
379
  )
379
380
 
381
+ @staticmethod
382
+ def _restore_inbound_metadata(
383
+ command: GatewayCommand,
384
+ is_agent_return: bool,
385
+ execution: Optional["RunningExecution"],
386
+ ) -> GatewayCommand:
387
+ """Give a resumed handler its own dispatch metadata back.
388
+
389
+ The mirror image of ``_resolve_reply_command``, and deliberately not
390
+ the same rule. That one rebuilds the header this execution *sends*;
391
+ this one rebuilds the header it *reads*. A resumed handler otherwise
392
+ sees only the metadata of whatever woke it up — an ``ask_user``
393
+ answer's, or a sub-call's reply — and everything the execution was
394
+ originally dispatched with is gone from the moment it first suspends.
395
+
396
+ Merged, not replaced (the opposite of the outbound direction): this
397
+ agent IS the addressee of the waking message, so its metadata is real
398
+ payload here rather than someone else's plumbing. Original dispatch
399
+ metadata is the base, the waking message wins on collisions. See
400
+ ``_resume_metadata.py`` for why the framework's per-hop trace keys are
401
+ excluded from the base.
402
+
403
+ Returns a NEW command — never mutates. ``prepare_command_for_processing``
404
+ returns the *same* object for the base worker (only ``ByaiWorker``
405
+ rebuilds it), so mutating the header here would also rewrite
406
+ ``raw_command``'s and leak this merge into the reply that goes out.
407
+ """
408
+ if not is_agent_return:
409
+ return command
410
+ snapshot = getattr(execution, "existing_data", None) or {}
411
+ return replace(
412
+ command,
413
+ header=replace(
414
+ command.header,
415
+ metadata=merge_resume_metadata(
416
+ snapshot.get("metadata"),
417
+ command.header.metadata,
418
+ ),
419
+ ),
420
+ )
421
+
380
422
  @staticmethod
381
423
  def _apply_suspended_status(
382
424
  task_result: AgentTaskResult,
@@ -682,6 +724,7 @@ class GatewayWorker(ABC):
682
724
  is_agent_return = isinstance(raw_command, ResumeCommand)
683
725
  reply_command = self._resolve_reply_command(raw_command, execution)
684
726
  has_source_agent = reply_command is not None
727
+ command = self._restore_inbound_metadata(command, is_agent_return, execution)
685
728
 
686
729
  # Get workspace dir from workspace_manager if available
687
730
  # Note: We don't use hasattr check because it doesn't work well with mocks
@@ -1340,6 +1340,34 @@ async def test_client_send_message_ask_agent_does_not_query_registry_for_resume_
1340
1340
  mock_registry.initialize_execution.assert_awaited_once()
1341
1341
 
1342
1342
 
1343
+ @pytest.mark.asyncio
1344
+ async def test_client_root_dispatch_records_its_metadata_on_the_execution():
1345
+ """A root execution's own dispatch metadata has to be on its record.
1346
+
1347
+ The agent will suspend (an `ask_user` round is the common case) and come
1348
+ back on a ResumeCommand carrying the *callee's* metadata; the only way it
1349
+ can still read what the client originally sent is for the client to have
1350
+ recorded it here.
1351
+ """
1352
+ mock_redis = AsyncMock()
1353
+ mock_registry = AsyncMock()
1354
+
1355
+ client = GatewayClient(redis_client=mock_redis, registry=mock_registry)
1356
+ await client.send_message(
1357
+ target_agent_type="langgraph_agent",
1358
+ session_id="s1",
1359
+ content="hello",
1360
+ action_type="ASK_AGENT",
1361
+ message_id="msg-new",
1362
+ target_worker_id="worker-42",
1363
+ metadata={"tenant": "acme", "req": "r-1"},
1364
+ )
1365
+
1366
+ initialized = mock_registry.initialize_execution.await_args.args[0]
1367
+ assert initialized["metadata"]["tenant"] == "acme"
1368
+ assert initialized["metadata"]["req"] == "r-1"
1369
+
1370
+
1343
1371
  @pytest.mark.asyncio
1344
1372
  async def test_client_send_message_resume_falls_back_when_registry_lacks_lookup():
1345
1373
  """A registry double/implementation without get_execution_by_message_id
@@ -1349,6 +1377,7 @@ async def test_client_send_message_resume_falls_back_when_registry_lacks_lookup(
1349
1377
  mock_redis = AsyncMock()
1350
1378
 
1351
1379
  class RegistryWithoutLookup:
1380
+
1352
1381
  async def initialize_execution(self, execution):
1353
1382
  return None
1354
1383