langbot-plugin 0.5.1__tar.gz → 0.5.2__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 (270) hide show
  1. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/PKG-INFO +1 -1
  2. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/pyproject.toml +1 -1
  3. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/langbot_api.py +5 -6
  4. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/run/handler.py +4 -4
  5. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/context.py +29 -4
  6. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/context.py +55 -0
  7. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/helper/marketplace.py +2 -6
  8. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/helper/pkgmgr.py +1 -4
  9. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/connections/stdio.py +3 -10
  10. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/connections/ws.py +3 -7
  11. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/handler.py +22 -23
  12. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/dependency_environment.py +16 -70
  13. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/mgr.py +439 -236
  14. langbot_plugin-0.5.2/src/langbot_plugin/runtime/plugin/restart_coordinator.py +418 -0
  15. langbot_plugin-0.5.2/src/langbot_plugin/version.py +1 -0
  16. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/entities/io/test_protocol.py +89 -5
  17. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/handlers/test_control_handler.py +6 -0
  18. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_installation_manager.py +559 -14
  19. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_restart_coordinator.py +129 -5
  20. langbot_plugin-0.5.1/src/langbot_plugin/runtime/plugin/restart_coordinator.py +0 -293
  21. langbot_plugin-0.5.1/src/langbot_plugin/version.py +0 -1
  22. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.github/workflows/cla.yml +0 -0
  23. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.github/workflows/publish-pypi.yaml +0 -0
  24. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.github/workflows/publish.yml +0 -0
  25. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.github/workflows/test.yml +0 -0
  26. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.gitignore +0 -0
  27. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.python-version +0 -0
  28. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/AGENTS.md +0 -0
  29. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/ARCHITECTURE.md +0 -0
  30. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/CLAUDE.md +0 -0
  31. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/CONTRIBUTING.md +0 -0
  32. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/LICENSE +0 -0
  33. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/README.md +0 -0
  34. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/README_CN.md +0 -0
  35. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/data/.env.example +0 -0
  36. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/Message.md +0 -0
  37. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/PluginPages.md +0 -0
  38. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/communication/apis/lb2rt.md +0 -0
  39. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/communication/runtime_plugin.md +0 -0
  40. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/dependency-management.md +0 -0
  41. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/langbot-plugin-social.png +0 -0
  42. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/scripts/check_action_consistency.py +0 -0
  43. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/scripts/runtime_resource_probe.py +0 -0
  44. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/__init__.py +0 -0
  45. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/__init__.py +0 -0
  46. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/__init__.py +0 -0
  47. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/abstract/__init__.py +0 -0
  48. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/abstract/platform/__init__.py +0 -0
  49. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/abstract/platform/adapter.py +0 -0
  50. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/abstract/platform/event_logger.py +0 -0
  51. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/__init__.py +0 -0
  52. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/base.py +0 -0
  53. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/command/__init__.py +0 -0
  54. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/command/command.py +0 -0
  55. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/common/__init__.py +0 -0
  56. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/common/event_listener.py +0 -0
  57. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/knowledge_engine/__init__.py +0 -0
  58. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/knowledge_engine/engine.py +0 -0
  59. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/manifest.py +0 -0
  60. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/page/__init__.py +0 -0
  61. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/parser/__init__.py +0 -0
  62. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/parser/parser.py +0 -0
  63. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/tool/__init__.py +0 -0
  64. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/tool/tool.py +0 -0
  65. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/plugin.py +0 -0
  66. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/__init__.py +0 -0
  67. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/__init__.py +0 -0
  68. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/command/__init__.py +0 -0
  69. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/command/context.py +0 -0
  70. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/command/errors.py +0 -0
  71. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/pipeline/__init__.py +0 -0
  72. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/pipeline/query.py +0 -0
  73. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/platform/__init__.py +0 -0
  74. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/platform/entities.py +0 -0
  75. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/platform/events.py +0 -0
  76. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/platform/logger.py +0 -0
  77. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/platform/message.py +0 -0
  78. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/provider/__init__.py +0 -0
  79. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/provider/message.py +0 -0
  80. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/provider/prompt.py +0 -0
  81. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/provider/session.py +0 -0
  82. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/rag/__init__.py +0 -0
  83. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/rag/context.py +0 -0
  84. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/rag/enums.py +0 -0
  85. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/rag/errors.py +0 -0
  86. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/rag/models.py +0 -0
  87. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/resource/__init__.py +0 -0
  88. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/resource/tool.py +0 -0
  89. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/context.py +0 -0
  90. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/events.py +0 -0
  91. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/execution.py +0 -0
  92. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/__init__.py +0 -0
  93. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/base.py +0 -0
  94. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/event_context.py +0 -0
  95. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/execute_context.py +0 -0
  96. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/query_based_api.py +0 -0
  97. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/__init__.py +0 -0
  98. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/langbot-page-sdk.js +0 -0
  99. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/.env.example.example +0 -0
  100. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/.github/workflows/release.yml.example +0 -0
  101. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/.gitignore.example +0 -0
  102. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/.vscode/launch.json.example +0 -0
  103. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/README.md.example +0 -0
  104. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/__init__.py +0 -0
  105. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/assets/icon.svg.example +0 -0
  106. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/__init__.py +0 -0
  107. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/commands/__init__.py +0 -0
  108. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/commands/{cmd_name}.py.example +0 -0
  109. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/commands/{cmd_name}.yaml.example +0 -0
  110. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/event_listener/__init__.py +0 -0
  111. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/event_listener/default.py.example +0 -0
  112. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/event_listener/default.yaml.example +0 -0
  113. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/knowledge_engine/__init__.py +0 -0
  114. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/knowledge_engine/{knowledge_engine_name}.py.example +0 -0
  115. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/knowledge_engine/{knowledge_engine_name}.yaml.example +0 -0
  116. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/pages/{page_name}.html.example +0 -0
  117. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/pages/{page_name}.yaml.example +0 -0
  118. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/parser/__init__.py +0 -0
  119. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/parser/{parser_name}.py.example +0 -0
  120. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/parser/{parser_name}.yaml.example +0 -0
  121. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/tools/__init__.py +0 -0
  122. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/tools/{tool_name}.py.example +0 -0
  123. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/tools/{tool_name}.yaml.example +0 -0
  124. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/main.py.example +0 -0
  125. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/manifest.yaml.example +0 -0
  126. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/readme/README_zh_Hans.md.example +0 -0
  127. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/requirements.txt.example +0 -0
  128. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/__init__.py +0 -0
  129. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/actions.py +0 -0
  130. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/backend.py +0 -0
  131. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/client.py +0 -0
  132. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/e2b_backend.py +0 -0
  133. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/errors.py +0 -0
  134. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/models.py +0 -0
  135. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/nsjail_backend.py +0 -0
  136. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/runtime.py +0 -0
  137. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/security.py +0 -0
  138. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/server.py +0 -0
  139. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/skill_store.py +0 -0
  140. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/tenancy.py +0 -0
  141. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/__init__.py +0 -0
  142. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/__main__.py +0 -0
  143. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/__init__.py +0 -0
  144. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/buildplugin.py +0 -0
  145. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/gencomponent.py +0 -0
  146. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/initplugin.py +0 -0
  147. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/login.py +0 -0
  148. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/logout.py +0 -0
  149. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/publish.py +0 -0
  150. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/runplugin.py +0 -0
  151. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/gen/__init__.py +0 -0
  152. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/gen/renderer.py +0 -0
  153. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/i18n.py +0 -0
  154. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/__init__.py +0 -0
  155. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/en_US.py +0 -0
  156. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/es_ES.py +0 -0
  157. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/ja_JP.py +0 -0
  158. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/th_TH.py +0 -0
  159. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/vi_VN.py +0 -0
  160. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/zh_Hans.py +0 -0
  161. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/zh_Hant.py +0 -0
  162. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/run/__init__.py +0 -0
  163. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/run/controller.py +0 -0
  164. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/run/hotreload.py +0 -0
  165. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/utils/__init__.py +0 -0
  166. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/utils/cloudsv.py +0 -0
  167. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/utils/form.py +0 -0
  168. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/utils/page_components.py +0 -0
  169. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/__init__.py +0 -0
  170. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/__init__.py +0 -0
  171. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/actions/__init__.py +0 -0
  172. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/actions/enums.py +0 -0
  173. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/errors.py +0 -0
  174. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/req.py +0 -0
  175. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/resp.py +0 -0
  176. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/marketplace.py +0 -0
  177. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/LICENSE +0 -0
  178. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/README.md +0 -0
  179. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/__init__.py +0 -0
  180. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/app.py +0 -0
  181. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/bounded_executor.py +0 -0
  182. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/event_loop_monitor.py +0 -0
  183. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/helper/__init__.py +0 -0
  184. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/__init__.py +0 -0
  185. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/connection.py +0 -0
  186. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/connections/__init__.py +0 -0
  187. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controller.py +0 -0
  188. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/__init__.py +0 -0
  189. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/stdio/__init__.py +0 -0
  190. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/stdio/client.py +0 -0
  191. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/stdio/server.py +0 -0
  192. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/ws/__init__.py +0 -0
  193. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/ws/client.py +0 -0
  194. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/ws/server.py +0 -0
  195. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/handlers/__init__.py +0 -0
  196. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/handlers/control.py +0 -0
  197. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/handlers/plugin.py +0 -0
  198. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/__init__.py +0 -0
  199. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/artifact.py +0 -0
  200. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/container.py +0 -0
  201. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/logbuffer.py +0 -0
  202. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/worker_launcher.py +0 -0
  203. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/security.py +0 -0
  204. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/settings.py +0 -0
  205. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/__init__.py +0 -0
  206. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/discover/__init__.py +0 -0
  207. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/discover/engine.py +0 -0
  208. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/importutil.py +0 -0
  209. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/log.py +0 -0
  210. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/platform.py +0 -0
  211. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/__init__.py +0 -0
  212. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/definition/components/test_components.py +0 -0
  213. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/definition/test_manifest.py +0 -0
  214. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/builtin/test_command_context.py +0 -0
  215. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/builtin/test_platform_logger.py +0 -0
  216. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/builtin/test_provider_message.py +0 -0
  217. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/builtin/test_rag_models.py +0 -0
  218. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/test_context.py +0 -0
  219. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/test_events.py +0 -0
  220. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/proxies/test_base.py +0 -0
  221. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/proxies/test_langbot_api.py +0 -0
  222. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/proxies/test_query_based_api.py +0 -0
  223. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/__init__.py +0 -0
  224. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_admission.py +0 -0
  225. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_backend.py +0 -0
  226. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_backend_selection.py +0 -0
  227. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_client.py +0 -0
  228. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_e2b_backend.py +0 -0
  229. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_nsjail_backend.py +0 -0
  230. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_runtime.py +0 -0
  231. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_server.py +0 -0
  232. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_skill_store.py +0 -0
  233. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/run/test_controller.py +0 -0
  234. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/run/test_hotreload.py +0 -0
  235. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/run/test_runtime_handler.py +0 -0
  236. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_buildplugin.py +0 -0
  237. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_gencomponent.py +0 -0
  238. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_i18n_form.py +0 -0
  239. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_initplugin.py +0 -0
  240. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_login.py +0 -0
  241. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_logout_publish.py +0 -0
  242. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_page_components.py +0 -0
  243. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_renderer.py +0 -0
  244. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_runplugin.py +0 -0
  245. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/entities/io/test_dependency_errors.py +0 -0
  246. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/helpers/__init__.py +0 -0
  247. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/helpers/protocol.py +0 -0
  248. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/packaging/test_installed_cli_blackbox.py +0 -0
  249. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/helper/test_marketplace.py +0 -0
  250. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/helper/test_pkgmgr.py +0 -0
  251. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/handlers/test_import_contracts.py +0 -0
  252. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/handlers/test_plugin_handler.py +0 -0
  253. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/test_action_consistency.py +0 -0
  254. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/test_connections.py +0 -0
  255. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/test_controllers.py +0 -0
  256. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/test_handler.py +0 -0
  257. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_artifact_store.py +0 -0
  258. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_container.py +0 -0
  259. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_dependency_environment.py +0 -0
  260. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_manager.py +0 -0
  261. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_worker_launcher.py +0 -0
  262. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/test_app.py +0 -0
  263. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/test_bounded_executor.py +0 -0
  264. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/test_event_loop_monitor.py +0 -0
  265. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/test_log.py +0 -0
  266. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/test_message.py +0 -0
  267. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/test_plugin_logbuffer.py +0 -0
  268. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/utils/test_discovery.py +0 -0
  269. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/utils/test_importutil.py +0 -0
  270. {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/utils/test_platform.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: langbot-plugin
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: This package contains the SDK, CLI for building plugins for LangBot, plus the runtime for hosting LangBot plugins
5
5
  Project-URL: Homepage, https://langbot.app
6
6
  Project-URL: Repository, https://github.com/langbot-app/langbot-plugin-sdk
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "langbot-plugin"
3
- version = "0.5.1"
3
+ version = "0.5.2"
4
4
  description = "This package contains the SDK, CLI for building plugins for LangBot, plus the runtime for hosting LangBot plugins"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import asyncio
4
3
  import base64
5
4
  from typing import Any
6
5
 
@@ -178,7 +177,7 @@ class LangBotAPIProxy:
178
177
 
179
178
  async def set_plugin_storage(self, key: str, value: bytes) -> None:
180
179
  """Set a plugin storage value"""
181
- encoded = (await asyncio.to_thread(base64.b64encode, value)).decode("utf-8")
180
+ encoded = base64.b64encode(value).decode("utf-8")
182
181
  await self.plugin_runtime_handler.call_action(
183
182
  PluginToRuntimeAction.SET_PLUGIN_STORAGE,
184
183
  {"key": key, "value_base64": encoded},
@@ -192,7 +191,7 @@ class LangBotAPIProxy:
192
191
  )
193
192
  )["value_base64"]
194
193
 
195
- return await asyncio.to_thread(base64.b64decode, resp)
194
+ return base64.b64decode(resp)
196
195
 
197
196
  async def get_plugin_storage_keys(self) -> list[str]:
198
197
  """Get all plugin storage keys"""
@@ -210,7 +209,7 @@ class LangBotAPIProxy:
210
209
 
211
210
  async def set_workspace_storage(self, key: str, value: bytes) -> None:
212
211
  """Set a workspace storage value"""
213
- encoded = (await asyncio.to_thread(base64.b64encode, value)).decode("utf-8")
212
+ encoded = base64.b64encode(value).decode("utf-8")
214
213
  await self.plugin_runtime_handler.call_action(
215
214
  PluginToRuntimeAction.SET_WORKSPACE_STORAGE,
216
215
  {"key": key, "value_base64": encoded},
@@ -224,7 +223,7 @@ class LangBotAPIProxy:
224
223
  )
225
224
  )["value_base64"]
226
225
 
227
- return await asyncio.to_thread(base64.b64decode, resp)
226
+ return base64.b64decode(resp)
228
227
 
229
228
  async def get_workspace_storage_keys(self) -> list[str]:
230
229
  """Get all workspace storage keys"""
@@ -255,7 +254,7 @@ class LangBotAPIProxy:
255
254
  )
256
255
  )["file_base64"]
257
256
 
258
- return await asyncio.to_thread(base64.b64decode, resp)
257
+ return base64.b64decode(resp)
259
258
 
260
259
  async def list_plugins_manifest(self) -> list[str]:
261
260
  """List all plugins"""
@@ -4,7 +4,6 @@ import asyncio
4
4
  import os
5
5
  import mimetypes
6
6
  import typing
7
- import aiofiles
8
7
  import logging
9
8
  from copy import deepcopy
10
9
  from pathlib import Path
@@ -43,12 +42,13 @@ MAX_RUNTIME_UI_FILE_BYTES = 4 * 1024 * 1024
43
42
 
44
43
 
45
44
  async def _read_runtime_ui_file_limited(path: str | Path) -> bytes:
46
- if await asyncio.to_thread(os.path.getsize, path) > MAX_RUNTIME_UI_FILE_BYTES:
45
+ file_path = Path(path)
46
+ if file_path.stat().st_size > MAX_RUNTIME_UI_FILE_BYTES:
47
47
  raise ValueError(
48
48
  f"Plugin UI file exceeds the {MAX_RUNTIME_UI_FILE_BYTES}-byte limit"
49
49
  )
50
- async with aiofiles.open(path, "rb") as file:
51
- content = await file.read(MAX_RUNTIME_UI_FILE_BYTES + 1)
50
+ with file_path.open("rb") as file:
51
+ content = file.read(MAX_RUNTIME_UI_FILE_BYTES + 1)
52
52
  if len(content) > MAX_RUNTIME_UI_FILE_BYTES:
53
53
  raise ValueError(
54
54
  f"Plugin UI file exceeds the {MAX_RUNTIME_UI_FILE_BYTES}-byte limit"
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import json
3
4
  import math
4
5
  import re
5
6
  from typing import Literal
@@ -39,6 +40,7 @@ class PluginWorkerPolicy(pydantic.BaseModel):
39
40
  max_total_cpus: float = 8.0
40
41
  max_total_memory_mb: int = 8192
41
42
  max_installations: int = 10_000
43
+ max_pending_registrations: int = 1024
42
44
  max_concurrent_restarts: int = 1
43
45
  restart_failure_threshold: int = 8
44
46
  restart_failure_window_seconds: float = 30.0
@@ -62,6 +64,7 @@ class PluginWorkerPolicy(pydantic.BaseModel):
62
64
  "max_workers",
63
65
  "max_total_memory_mb",
64
66
  "max_installations",
67
+ "max_pending_registrations",
65
68
  "max_concurrent_restarts",
66
69
  "restart_failure_threshold",
67
70
  mode="before",
@@ -112,6 +115,7 @@ class PluginWorkerPolicy(pydantic.BaseModel):
112
115
  "max_workers",
113
116
  "max_total_memory_mb",
114
117
  "max_installations",
118
+ "max_pending_registrations",
115
119
  "max_concurrent_restarts",
116
120
  "restart_failure_threshold",
117
121
  )
@@ -129,10 +133,6 @@ class PluginWorkerPolicy(pydantic.BaseModel):
129
133
  raise ValueError("max_total_memory_mb cannot be lower than max_memory_mb")
130
134
  if self.max_installations < self.max_workers:
131
135
  raise ValueError("max_installations cannot be lower than max_workers")
132
- if self.max_concurrent_restarts > self.effective_worker_capacity:
133
- raise ValueError(
134
- "max_concurrent_restarts cannot exceed effective worker capacity"
135
- )
136
136
  if self.restart_failure_threshold > self.max_installations:
137
137
  raise ValueError(
138
138
  "restart_failure_threshold cannot exceed max_installations"
@@ -292,6 +292,31 @@ class RuntimeConfig(pydantic.BaseModel):
292
292
 
293
293
  model_config = pydantic.ConfigDict(extra="forbid", frozen=True)
294
294
 
295
+ def model_dump(self, **kwargs):
296
+ include_pending_registration_limit = kwargs.pop(
297
+ "include_pending_registration_limit",
298
+ False,
299
+ )
300
+ payload = super().model_dump(**kwargs)
301
+ if not include_pending_registration_limit and isinstance(
302
+ payload.get("worker_policy"),
303
+ dict,
304
+ ):
305
+ payload["worker_policy"].pop("max_pending_registrations", None)
306
+ return payload
307
+
308
+ def model_dump_json(self, **kwargs) -> str:
309
+ include_pending_registration_limit = kwargs.pop(
310
+ "include_pending_registration_limit",
311
+ False,
312
+ )
313
+ return json.dumps(
314
+ self.model_dump(
315
+ include_pending_registration_limit=include_pending_registration_limit,
316
+ **kwargs,
317
+ )
318
+ )
319
+
295
320
  @pydantic.field_validator("cloud_service_url")
296
321
  @classmethod
297
322
  def validate_cloud_service_url(cls, value: str | None) -> str | None:
@@ -70,6 +70,17 @@ class RuntimeContext:
70
70
  getattr(plugin_manager, "handlers", ()),
71
71
  )
72
72
  event_loop_monitor = self.event_loop_monitor
73
+ installation_state_counts = {
74
+ "running": 0,
75
+ "starting": 0,
76
+ "failed": 0,
77
+ "disabled": 0,
78
+ }
79
+ for runtime in getattr(plugin_manager, "_installations", {}).values():
80
+ state = str(getattr(runtime, "state", "disabled"))
81
+ if state in installation_state_counts:
82
+ installation_state_counts[state] += 1
83
+
73
84
  return {
74
85
  "event_loop": (
75
86
  event_loop_monitor.snapshot() if event_loop_monitor is not None else {}
@@ -84,6 +95,7 @@ class RuntimeContext:
84
95
  getattr(plugin_manager, "_plugin_supervisors", ())
85
96
  ),
86
97
  "installation_runtimes": len(getattr(plugin_manager, "_installations", ())),
98
+ "installation_states": installation_state_counts,
87
99
  "pending_registrations": len(
88
100
  getattr(plugin_manager, "_pending_registrations", ())
89
101
  ),
@@ -287,6 +299,49 @@ class RuntimeContext:
287
299
  self._installation_watermarks[binding.installation_uuid] = binding
288
300
  return binding
289
301
 
302
+ def reconcile_installation_watermarks(
303
+ self,
304
+ authoritative_bindings: tuple[InstallationBinding, ...],
305
+ ) -> None:
306
+ """Drop inactive historical fences absent from an authoritative replay."""
307
+
308
+ for binding in self.inactive_installation_watermark_snapshots(
309
+ authoritative_bindings
310
+ ):
311
+ self.drop_installation_watermark_if_current(binding)
312
+
313
+ def inactive_installation_watermark_snapshots(
314
+ self,
315
+ authoritative_bindings: tuple[InstallationBinding, ...],
316
+ ) -> tuple[InstallationBinding, ...]:
317
+ """Return inactive watermark values eligible for authoritative GC."""
318
+
319
+ authoritative_uuids = {
320
+ binding.installation_uuid for binding in authoritative_bindings
321
+ }
322
+ snapshots: list[InstallationBinding] = []
323
+ for installation_uuid, watermark in list(self._installation_watermarks.items()):
324
+ if installation_uuid in authoritative_uuids:
325
+ continue
326
+ if installation_uuid in self._installation_bindings:
327
+ continue
328
+ snapshots.append(watermark)
329
+ return tuple(snapshots)
330
+
331
+ def drop_installation_watermark_if_current(
332
+ self,
333
+ binding: InstallationBinding,
334
+ ) -> bool:
335
+ """Drop one inactive watermark only if it still matches ``binding``."""
336
+
337
+ current = self._installation_watermarks.get(binding.installation_uuid)
338
+ if current != binding:
339
+ return False
340
+ if binding.installation_uuid in self._installation_bindings:
341
+ return False
342
+ self._installation_watermarks.pop(binding.installation_uuid, None)
343
+ return True
344
+
290
345
  def is_current_installation_binding(
291
346
  self,
292
347
  action_context: InstallationBinding | dict,
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import asyncio
4
3
  import json
5
4
  import time
6
5
 
@@ -44,7 +43,7 @@ async def _request_json(client: httpx.AsyncClient, url: str) -> dict:
44
43
  response,
45
44
  max_bytes=_MAX_MARKETPLACE_JSON_BYTES,
46
45
  )
47
- payload = await asyncio.to_thread(json.loads, body)
46
+ payload = json.loads(body)
48
47
  if not isinstance(payload, dict):
49
48
  raise RuntimeError("Marketplace returned a non-object response")
50
49
  if payload.get("code") != 0:
@@ -61,10 +60,7 @@ async def get_plugin_info(
61
60
  )
62
61
  async with httpx.AsyncClient(timeout=30) as client:
63
62
  payload = await _request_json(client, url)
64
- return await asyncio.to_thread(
65
- entities_marketplace.PluginInfo.model_validate,
66
- payload["data"]["plugin"],
67
- )
63
+ return entities_marketplace.PluginInfo.model_validate(payload["data"]["plugin"])
68
64
 
69
65
 
70
66
  async def download_plugin(
@@ -532,10 +532,7 @@ async def ensure_plugin_environment(plugin_path: str) -> str:
532
532
  if candidate.is_file():
533
533
  return str(candidate.resolve())
534
534
 
535
- await asyncio.to_thread(
536
- venv.EnvBuilder(with_pip=True, system_site_packages=True).create,
537
- root,
538
- )
535
+ venv.EnvBuilder(with_pip=True, system_site_packages=True).create(root)
539
536
  if not candidate.is_file():
540
537
  raise RuntimeError(f"Plugin virtual environment was not created at {root}")
541
538
  return str(candidate.resolve())
@@ -39,7 +39,7 @@ class StdioConnection(connection.Connection):
39
39
  async def send(self, message: str) -> None:
40
40
  """Send message with chunking support for large data."""
41
41
  async with self._send_lock: # 确保同一时间只有一个send操作
42
- message_bytes = await asyncio.to_thread(message.encode, "utf-8")
42
+ message_bytes = message.encode("utf-8")
43
43
  message_size = len(message_bytes)
44
44
  if message_size > MAX_MESSAGE_BYTES:
45
45
  raise ValueError(
@@ -62,11 +62,7 @@ class StdioConnection(connection.Connection):
62
62
  # For large messages, send in chunks
63
63
  try:
64
64
  del message_bytes
65
- chunks = await asyncio.to_thread(
66
- split_utf8_chunks,
67
- message,
68
- self.chunk_size,
69
- )
65
+ chunks = split_utf8_chunks(message, self.chunk_size)
70
66
  # Send start marker for chunked message
71
67
  chunk_header = json.dumps(
72
68
  {"type": "chunk_start", "total_size": message_size}
@@ -216,10 +212,7 @@ class StdioConnection(connection.Connection):
216
212
  "Incomplete runtime chunked message"
217
213
  )
218
214
  # Reconstruct original message
219
- return await asyncio.to_thread(
220
- "".join,
221
- chunks,
222
- )
215
+ return "".join(chunks)
223
216
 
224
217
  # Yield control periodically
225
218
  if len(chunks) % 50 == 0:
@@ -30,7 +30,7 @@ class WebSocketConnection(io_connection.Connection):
30
30
  async def send(self, message: str) -> None:
31
31
  """Send message with chunking support for large data."""
32
32
  async with self._send_lock: # 确保同一时间只有一个send操作
33
- message_bytes = await asyncio.to_thread(message.encode, "utf-8")
33
+ message_bytes = message.encode("utf-8")
34
34
  message_size = len(message_bytes)
35
35
  if message_size > MAX_MESSAGE_BYTES:
36
36
  raise ValueError(
@@ -55,11 +55,7 @@ class WebSocketConnection(io_connection.Connection):
55
55
  # an independent message forces the receiver to guess message
56
56
  # boundaries and permits unbounded cross-message accumulation.
57
57
  del message_bytes
58
- chunks = await asyncio.to_thread(
59
- split_utf8_chunks,
60
- message,
61
- self.chunk_size,
62
- )
58
+ chunks = split_utf8_chunks(message, self.chunk_size)
63
59
  await self.websocket.send(chunks)
64
60
  except WebSocketClosed:
65
61
  raise ConnectionClosedError("Connection closed during send")
@@ -89,7 +85,7 @@ class WebSocketConnection(io_connection.Connection):
89
85
  # recv_streaming yields exactly one WebSocket message. JSON
90
86
  # validation belongs to Handler; never concatenate separate peer
91
87
  # messages while waiting for one that happens to parse.
92
- return await asyncio.to_thread("".join, message_chunks)
88
+ return "".join(message_chunks)
93
89
 
94
90
  except WebSocketClosed:
95
91
  raise ConnectionClosedError("Connection closed")
@@ -19,8 +19,6 @@ import uuid
19
19
  import contextlib
20
20
  import contextvars
21
21
  import re
22
- import aiofiles
23
- import aiofiles.os
24
22
  import logging
25
23
  from langbot_plugin.runtime.io import connection
26
24
  from langbot_plugin.entities.io.req import ActionRequest
@@ -40,6 +38,7 @@ from langbot_plugin.runtime.security import PLUGIN_RUNTIME_PROFILE_ENV
40
38
  from langbot_plugin.runtime.bounded_executor import blocking_work_scope
41
39
 
42
40
  logger = logging.getLogger(__name__)
41
+ _ORIGINAL_ASYNCIO_TO_THREAD = asyncio.to_thread
43
42
 
44
43
  FILE_STORAGE_DIR = "data/temp/lbp"
45
44
  SHARED_WORKER_FILE_STORAGE_DIR = "/tmp/lbp-rpc"
@@ -52,6 +51,12 @@ _SAFE_FILE_KEY_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,254}$")
52
51
  _SAFE_FILE_EXTENSION_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$")
53
52
 
54
53
 
54
+ async def _run_small_protocol_work(fn: Callable[..., Any], *args: Any) -> Any:
55
+ if asyncio.to_thread is _ORIGINAL_ASYNCIO_TO_THREAD:
56
+ return fn(*args)
57
+ return await asyncio.to_thread(fn, *args)
58
+
59
+
55
60
  def _file_storage_path(
56
61
  file_key: str,
57
62
  file_storage_dir: str | os.PathLike[str] = FILE_STORAGE_DIR,
@@ -196,8 +201,8 @@ class Handler(abc.ABC):
196
201
  and resulting_size > self.max_file_bytes
197
202
  ):
198
203
  raise ValueError("File transfer exceeds the configured size limit")
199
- async with aiofiles.open(file_path, mode) as f:
200
- await f.write(chunk_bytes)
204
+ with open(file_path, mode) as f:
205
+ f.write(chunk_bytes)
201
206
  return ActionResponse.success({})
202
207
 
203
208
  def _message_blocking_scope(
@@ -215,7 +220,7 @@ class Handler(abc.ABC):
215
220
  """Parse peer JSON outside the shared event loop with tenant fairness."""
216
221
 
217
222
  with blocking_work_scope(self._message_blocking_scope()):
218
- return await asyncio.to_thread(json.loads, message)
223
+ return await _run_small_protocol_work(json.loads, message)
219
224
 
220
225
  async def _encode_message(
221
226
  self,
@@ -228,10 +233,11 @@ class Handler(abc.ABC):
228
233
  with blocking_work_scope(
229
234
  self._message_blocking_scope(action_context),
230
235
  ):
231
- return await asyncio.to_thread(
232
- lambda: json.dumps(
233
- payload.model_dump() if hasattr(payload, "model_dump") else payload
234
- )
236
+ return await _run_small_protocol_work(
237
+ lambda value: json.dumps(
238
+ value.model_dump() if hasattr(value, "model_dump") else value
239
+ ),
240
+ payload,
235
241
  )
236
242
 
237
243
  async def _validate_message_model(
@@ -242,7 +248,7 @@ class Handler(abc.ABC):
242
248
  """Run potentially deep Pydantic validation outside the event loop."""
243
249
 
244
250
  with blocking_work_scope(self._message_blocking_scope()):
245
- return await asyncio.to_thread(model_type.model_validate, payload)
251
+ return await _run_small_protocol_work(model_type.model_validate, payload)
246
252
 
247
253
  async def _send_message(
248
254
  self,
@@ -272,7 +278,7 @@ class Handler(abc.ABC):
272
278
  return f"{exc.__class__.__name__}: {message}"
273
279
 
274
280
  with blocking_work_scope(self._message_blocking_scope()):
275
- return await asyncio.to_thread(render)
281
+ return await _run_small_protocol_work(render)
276
282
 
277
283
  def set_disconnect_callback(
278
284
  self,
@@ -793,16 +799,13 @@ class Handler(abc.ABC):
793
799
  file_path = _file_storage_path(file_key, self.file_storage_dir)
794
800
  if self.max_file_bytes is not None:
795
801
  try:
796
- file_size = await asyncio.to_thread(os.path.getsize, file_path)
802
+ file_size = os.path.getsize(file_path)
797
803
  except FileNotFoundError:
798
804
  raise
799
805
  if file_size > self.max_file_bytes:
800
806
  raise ValueError("File transfer exceeds the configured size limit")
801
- async with aiofiles.open(
802
- file_path,
803
- "rb",
804
- ) as f:
805
- content = await f.read(
807
+ with open(file_path, "rb") as f:
808
+ content = f.read(
806
809
  self.max_file_bytes + 1 if self.max_file_bytes is not None else -1
807
810
  )
808
811
  if self.max_file_bytes is not None and len(content) > self.max_file_bytes:
@@ -812,9 +815,7 @@ class Handler(abc.ABC):
812
815
  async def delete_local_file(self, file_key: str) -> None:
813
816
  async with self._file_transfer_lock:
814
817
  try:
815
- await aiofiles.os.remove(
816
- _file_storage_path(file_key, self.file_storage_dir)
817
- )
818
+ os.remove(_file_storage_path(file_key, self.file_storage_dir))
818
819
  except FileNotFoundError:
819
820
  pass
820
821
  finally:
@@ -826,9 +827,7 @@ class Handler(abc.ABC):
826
827
  self._owned_transfer_files.clear()
827
828
  for file_key in file_keys:
828
829
  try:
829
- await aiofiles.os.remove(
830
- _file_storage_path(file_key, self.file_storage_dir)
831
- )
830
+ os.remove(_file_storage_path(file_key, self.file_storage_dir))
832
831
  except FileNotFoundError:
833
832
  pass
834
833
  except OSError as exc:
@@ -18,7 +18,6 @@ from packaging.requirements import InvalidRequirement, Requirement
18
18
  from packaging.utils import canonicalize_name
19
19
 
20
20
  from langbot_plugin.runtime.plugin.artifact import PluginArtifact
21
- from langbot_plugin.runtime import bounded_executor
22
21
 
23
22
 
24
23
  _ENVIRONMENT_SCHEMA_VERSION = 1
@@ -79,10 +78,7 @@ class PluginDependencyEnvironmentStore:
79
78
  runtime_fingerprint: str,
80
79
  installer: DependencyInstaller,
81
80
  ) -> PluginDependencyEnvironment:
82
- requirements, requirements_digest = await asyncio.to_thread(
83
- self._read_requirements,
84
- artifact,
85
- )
81
+ requirements, requirements_digest = self._read_requirements(artifact)
86
82
  digest = self._environment_digest(
87
83
  artifact.digest,
88
84
  requirements_digest,
@@ -95,13 +91,13 @@ class PluginDependencyEnvironmentStore:
95
91
  runtime_fingerprint=runtime_fingerprint,
96
92
  )
97
93
 
98
- ready = await asyncio.to_thread(self.get_ready, digest, expected=expected)
94
+ ready = self.get_ready(digest, expected=expected)
99
95
  if ready is not None:
100
96
  return ready
101
97
 
102
98
  lock = self._prepare_locks.setdefault(digest, asyncio.Lock())
103
99
  async with lock:
104
- ready = await asyncio.to_thread(self.get_ready, digest, expected=expected)
100
+ ready = self.get_ready(digest, expected=expected)
105
101
  if ready is not None:
106
102
  return ready
107
103
  return await self._prepare_locked(
@@ -186,17 +182,7 @@ class PluginDependencyEnvironmentStore:
186
182
  raise
187
183
  return staging
188
184
 
189
- staging_task = asyncio.create_task(asyncio.to_thread(create_staging))
190
- try:
191
- staging = await asyncio.shield(staging_task)
192
- except asyncio.CancelledError:
193
- staging = await staging_task
194
- await bounded_executor.run_blocking_cleanup(
195
- shutil.rmtree,
196
- staging.root_path,
197
- True,
198
- )
199
- raise
185
+ staging = create_staging()
200
186
  temporary_root = staging.root_path
201
187
 
202
188
  try:
@@ -204,75 +190,35 @@ class PluginDependencyEnvironmentStore:
204
190
  # Validate structure before reading distribution metadata: a build
205
191
  # backend is untrusted and could otherwise make the Runtime follow
206
192
  # a link outside the staging tree while verifying its output.
207
- await bounded_executor.run_blocking_atomic(
208
- self._validate_tree_entries,
209
- staging.site_packages_path,
210
- )
211
- await bounded_executor.run_blocking_atomic(
212
- self._validate_installed_requirements,
193
+ self._validate_tree_entries(staging.site_packages_path)
194
+ self._validate_installed_requirements(
213
195
  staging.site_packages_path,
214
196
  requirements,
215
197
  )
216
- await bounded_executor.run_blocking_cleanup(
217
- shutil.rmtree,
218
- staging.scratch_path,
219
- )
220
- await bounded_executor.run_blocking_atomic(
221
- self._make_tree_read_only,
222
- staging.site_packages_path,
223
- )
224
- await bounded_executor.run_blocking_atomic(
225
- self._write_marker,
226
- temporary_root / _READY_MARKER,
227
- expected,
228
- )
229
- await bounded_executor.run_blocking_atomic(
230
- temporary_root.chmod,
231
- 0o555,
232
- )
198
+ shutil.rmtree(staging.scratch_path)
199
+ self._make_tree_read_only(staging.site_packages_path)
200
+ self._write_marker(temporary_root / _READY_MARKER, expected)
201
+ temporary_root.chmod(0o555)
233
202
 
234
203
  target_root = self.environments_path / digest
235
204
  try:
236
- await bounded_executor.run_blocking_atomic(
237
- os.rename,
238
- temporary_root,
239
- target_root,
240
- )
205
+ os.rename(temporary_root, target_root)
241
206
  except OSError:
242
207
  # Another Runtime process may have completed the exact same
243
208
  # environment while this process was preparing its staging tree.
244
- ready = await asyncio.to_thread(
245
- self.get_ready,
246
- digest,
247
- expected=expected,
248
- )
209
+ ready = self.get_ready(digest, expected=expected)
249
210
  if ready is None:
250
211
  raise
251
- await bounded_executor.run_blocking_cleanup(
252
- shutil.rmtree,
253
- temporary_root,
254
- True,
255
- )
212
+ shutil.rmtree(temporary_root, ignore_errors=True)
256
213
  return ready
257
- await bounded_executor.run_blocking_atomic(
258
- self._fsync_directory,
259
- self.environments_path,
260
- )
214
+ self._fsync_directory(self.environments_path)
261
215
 
262
- ready = await bounded_executor.run_blocking_atomic(
263
- self.get_ready,
264
- digest,
265
- expected=expected,
266
- )
216
+ ready = self.get_ready(digest, expected=expected)
267
217
  if ready is None: # pragma: no cover - publication invariant
268
218
  raise RuntimeError("Dependency environment publication failed")
269
219
  return ready
270
220
  except BaseException:
271
- await bounded_executor.run_blocking_cleanup(
272
- shutil.rmtree,
273
- temporary_root,
274
- True,
275
- )
221
+ shutil.rmtree(temporary_root, ignore_errors=True)
276
222
  raise
277
223
 
278
224
  @staticmethod