langbot-plugin 0.7.14__tar.gz → 0.7.16__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 (398) hide show
  1. langbot_plugin-0.7.16/.github/workflows/box-docker.yml +34 -0
  2. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/ARCHITECTURE.md +4 -1
  3. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/PKG-INFO +1 -1
  4. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/pyproject.toml +1 -1
  5. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/agent_tools/asset_gateway.py +43 -2
  6. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/backend.py +8 -1
  7. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/host_backend.py +22 -16
  8. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/runtime.py +3 -1
  9. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/plugin/mgr.py +7 -0
  10. langbot_plugin-0.7.16/src/langbot_plugin/version.py +1 -0
  11. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/test_agent_tools_mcp_bridge.py +39 -0
  12. langbot_plugin-0.7.16/tests/box/test_artifact_cache_root.py +91 -0
  13. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/test_host_backend.py +80 -0
  14. langbot_plugin-0.7.16/tests/e2e/Dockerfile.box-socket +8 -0
  15. langbot_plugin-0.7.16/tests/e2e/Dockerfile.box-socket.dockerignore +10 -0
  16. langbot_plugin-0.7.16/tests/e2e/test_box_socket_mounts.py +125 -0
  17. langbot_plugin-0.7.16/tests/runtime/plugin/test_event_dispatch_real_worker.py +220 -0
  18. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_manager.py +129 -5
  19. langbot_plugin-0.7.14/src/langbot_plugin/version.py +0 -1
  20. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/.github/workflows/cla.yml +0 -0
  21. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/.github/workflows/publish-pypi.yaml +0 -0
  22. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/.github/workflows/publish.yml +0 -0
  23. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/.github/workflows/test.yml +0 -0
  24. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/.gitignore +0 -0
  25. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/.python-version +0 -0
  26. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/AGENTS.md +0 -0
  27. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/CLAUDE.md +0 -0
  28. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/CONTRIBUTING.md +0 -0
  29. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/LICENSE +0 -0
  30. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/README.md +0 -0
  31. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/README_CN.md +0 -0
  32. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/data/.env.example +0 -0
  33. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/Message.md +0 -0
  34. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/PluginPages.md +0 -0
  35. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/agent-runner-interactions.md +0 -0
  36. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/agent-runner-pluginization/PROTOCOL_V1.md +0 -0
  37. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/agent-runner-pluginization/README.md +0 -0
  38. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/agent-runner-pluginization/SDK_RUNTIME_PLAN.md +0 -0
  39. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/certified-plugins.md +0 -0
  40. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/communication/apis/lb2rt.md +0 -0
  41. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/communication/runtime_plugin.md +0 -0
  42. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/dependency-management.md +0 -0
  43. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/langbot-plugin-social.png +0 -0
  44. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/runner.md +0 -0
  45. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/docs/stateless-components.md +0 -0
  46. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/local_e2e/acceptance.json +0 -0
  47. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/local_e2e/registration_fanin.py +0 -0
  48. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/local_e2e/run_acceptance.py +0 -0
  49. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/mypy.ini +0 -0
  50. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/scripts/check_action_consistency.py +0 -0
  51. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/scripts/runtime_resource_probe.py +0 -0
  52. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/__init__.py +0 -0
  53. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/__init__.py +0 -0
  54. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/agent_tools/__init__.py +0 -0
  55. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/agent_tools/daemon.py +0 -0
  56. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/agent_tools/decorators.py +0 -0
  57. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/agent_tools/external_tools.py +0 -0
  58. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/agent_tools/mcp_access.py +0 -0
  59. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/agent_tools/mcp_bridge.py +0 -0
  60. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/agent_tools/mcp_config.py +0 -0
  61. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/agent_tools/mcp_protocol.py +0 -0
  62. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/agent_tools/mcp_stdio.py +0 -0
  63. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/__init__.py +0 -0
  64. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/abstract/__init__.py +0 -0
  65. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/abstract/platform/__init__.py +0 -0
  66. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/abstract/platform/adapter.py +0 -0
  67. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/abstract/platform/event_logger.py +0 -0
  68. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/__init__.py +0 -0
  69. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/base.py +0 -0
  70. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/command/__init__.py +0 -0
  71. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/command/command.py +0 -0
  72. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/common/__init__.py +0 -0
  73. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/common/event_listener.py +0 -0
  74. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/knowledge_engine/__init__.py +0 -0
  75. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/knowledge_engine/engine.py +0 -0
  76. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/manifest.py +0 -0
  77. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/page/__init__.py +0 -0
  78. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/parser/__init__.py +0 -0
  79. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/parser/parser.py +0 -0
  80. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/runner/__init__.py +0 -0
  81. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/runner/runner.py +0 -0
  82. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/tool/__init__.py +0 -0
  83. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/components/tool/tool.py +0 -0
  84. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/definition/plugin.py +0 -0
  85. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/__init__.py +0 -0
  86. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/__init__.py +0 -0
  87. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/command/__init__.py +0 -0
  88. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/command/context.py +0 -0
  89. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/command/errors.py +0 -0
  90. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/pipeline/__init__.py +0 -0
  91. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/pipeline/query.py +0 -0
  92. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/platform/__init__.py +0 -0
  93. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/platform/entities.py +0 -0
  94. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/platform/errors.py +0 -0
  95. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/platform/events.py +0 -0
  96. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/platform/logger.py +0 -0
  97. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/platform/message.py +0 -0
  98. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/provider/__init__.py +0 -0
  99. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/provider/message.py +0 -0
  100. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/provider/prompt.py +0 -0
  101. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/provider/reasoning.py +0 -0
  102. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/provider/session.py +0 -0
  103. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/rag/__init__.py +0 -0
  104. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/rag/context.py +0 -0
  105. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/rag/enums.py +0 -0
  106. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/rag/errors.py +0 -0
  107. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/rag/models.py +0 -0
  108. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/resource/__init__.py +0 -0
  109. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/resource/tool.py +0 -0
  110. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/__init__.py +0 -0
  111. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/box.py +0 -0
  112. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/capabilities.py +0 -0
  113. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/compat.py +0 -0
  114. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/context.py +0 -0
  115. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/context_access.py +0 -0
  116. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/delivery.py +0 -0
  117. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/errors.py +0 -0
  118. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/event.py +0 -0
  119. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/input.py +0 -0
  120. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/interaction.py +0 -0
  121. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/manifest.py +0 -0
  122. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/page_results.py +0 -0
  123. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/permissions.py +0 -0
  124. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/resources.py +0 -0
  125. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/result.py +0 -0
  126. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/run_ledger.py +0 -0
  127. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/runtime.py +0 -0
  128. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/runtime_registry.py +0 -0
  129. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/state.py +0 -0
  130. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/stats.py +0 -0
  131. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/steering.py +0 -0
  132. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/transcript.py +0 -0
  133. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/builtin/runner/trigger.py +0 -0
  134. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/context.py +0 -0
  135. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/events.py +0 -0
  136. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/entities/execution.py +0 -0
  137. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/__init__.py +0 -0
  138. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/base.py +0 -0
  139. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/event_context.py +0 -0
  140. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/execute_context.py +0 -0
  141. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/invocation.py +0 -0
  142. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/langbot_api.py +0 -0
  143. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/query_based_api.py +0 -0
  144. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/__init__.py +0 -0
  145. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/admin.py +0 -0
  146. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/api.py +0 -0
  147. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/box.py +0 -0
  148. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/common.py +0 -0
  149. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/context.py +0 -0
  150. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/ledger.py +0 -0
  151. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/platform.py +0 -0
  152. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/reply_stream.py +0 -0
  153. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/resources.py +0 -0
  154. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/state.py +0 -0
  155. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/api/proxies/runner/tracing.py +0 -0
  156. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/artifact.py +0 -0
  157. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/__init__.py +0 -0
  158. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/langbot-page-sdk.js +0 -0
  159. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/.env.example.example +0 -0
  160. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/.github/workflows/release.yml.example +0 -0
  161. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/.gitignore.example +0 -0
  162. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/.vscode/launch.json.example +0 -0
  163. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/README.md.example +0 -0
  164. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/__init__.py +0 -0
  165. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/assets/icon.svg.example +0 -0
  166. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/__init__.py +0 -0
  167. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/commands/__init__.py +0 -0
  168. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/commands/{cmd_name}.py.example +0 -0
  169. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/commands/{cmd_name}.yaml.example +0 -0
  170. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/event_listener/__init__.py +0 -0
  171. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/event_listener/default.py.example +0 -0
  172. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/event_listener/default.yaml.example +0 -0
  173. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/knowledge_engine/__init__.py +0 -0
  174. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/knowledge_engine/{knowledge_engine_name}.py.example +0 -0
  175. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/knowledge_engine/{knowledge_engine_name}.yaml.example +0 -0
  176. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/pages/{page_name}.html.example +0 -0
  177. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/pages/{page_name}.yaml.example +0 -0
  178. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/parser/__init__.py +0 -0
  179. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/parser/{parser_name}.py.example +0 -0
  180. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/parser/{parser_name}.yaml.example +0 -0
  181. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/runner/__init__.py +0 -0
  182. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/runner/{runner_name}.py.example +0 -0
  183. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/runner/{runner_name}.yaml.example +0 -0
  184. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/tools/__init__.py +0 -0
  185. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/tools/{tool_name}.py.example +0 -0
  186. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/components/tools/{tool_name}.yaml.example +0 -0
  187. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/main.py.example +0 -0
  188. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/manifest.yaml.example +0 -0
  189. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/readme/README_zh_Hans.md.example +0 -0
  190. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/assets/templates/requirements.txt.example +0 -0
  191. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/__init__.py +0 -0
  192. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/actions.py +0 -0
  193. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/client.py +0 -0
  194. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/e2b_backend.py +0 -0
  195. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/errors.py +0 -0
  196. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/models.py +0 -0
  197. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/nsjail_backend.py +0 -0
  198. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/security.py +0 -0
  199. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/server.py +0 -0
  200. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/box/tenancy.py +0 -0
  201. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/certification.py +0 -0
  202. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/__init__.py +0 -0
  203. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/__main__.py +0 -0
  204. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/commands/__init__.py +0 -0
  205. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/commands/buildplugin.py +0 -0
  206. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/commands/gencomponent.py +0 -0
  207. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/commands/initplugin.py +0 -0
  208. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/commands/login.py +0 -0
  209. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/commands/logout.py +0 -0
  210. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/commands/publish.py +0 -0
  211. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/commands/runplugin.py +0 -0
  212. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/gen/__init__.py +0 -0
  213. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/gen/renderer.py +0 -0
  214. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/i18n.py +0 -0
  215. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/locales/__init__.py +0 -0
  216. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/locales/en_US.py +0 -0
  217. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/locales/es_ES.py +0 -0
  218. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/locales/ja_JP.py +0 -0
  219. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/locales/th_TH.py +0 -0
  220. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/locales/vi_VN.py +0 -0
  221. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/locales/zh_Hans.py +0 -0
  222. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/locales/zh_Hant.py +0 -0
  223. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/run/__init__.py +0 -0
  224. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/run/controller.py +0 -0
  225. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/run/handler.py +0 -0
  226. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/run/hotreload.py +0 -0
  227. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/utils/__init__.py +0 -0
  228. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/utils/cloudsv.py +0 -0
  229. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/utils/form.py +0 -0
  230. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/cli/utils/page_components.py +0 -0
  231. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/entities/__init__.py +0 -0
  232. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/entities/io/__init__.py +0 -0
  233. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/entities/io/actions/__init__.py +0 -0
  234. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/entities/io/actions/enums.py +0 -0
  235. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/entities/io/context.py +0 -0
  236. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/entities/io/errors.py +0 -0
  237. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/entities/io/req.py +0 -0
  238. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/entities/io/resp.py +0 -0
  239. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/entities/marketplace.py +0 -0
  240. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/LICENSE +0 -0
  241. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/README.md +0 -0
  242. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/__init__.py +0 -0
  243. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/app.py +0 -0
  244. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/bounded_executor.py +0 -0
  245. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/context.py +0 -0
  246. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/event_loop_monitor.py +0 -0
  247. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/helper/__init__.py +0 -0
  248. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/helper/marketplace.py +0 -0
  249. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/helper/pkgmgr.py +0 -0
  250. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/__init__.py +0 -0
  251. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/connection.py +0 -0
  252. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/connections/__init__.py +0 -0
  253. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/connections/stdio.py +0 -0
  254. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/connections/ws.py +0 -0
  255. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/controller.py +0 -0
  256. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/controllers/__init__.py +0 -0
  257. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/controllers/stdio/__init__.py +0 -0
  258. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/controllers/stdio/client.py +0 -0
  259. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/controllers/stdio/server.py +0 -0
  260. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/controllers/ws/__init__.py +0 -0
  261. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/controllers/ws/client.py +0 -0
  262. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/controllers/ws/server.py +0 -0
  263. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/handler.py +0 -0
  264. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/handlers/__init__.py +0 -0
  265. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/handlers/control.py +0 -0
  266. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/io/handlers/plugin.py +0 -0
  267. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/ops_metrics.py +0 -0
  268. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/plugin/__init__.py +0 -0
  269. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/plugin/artifact.py +0 -0
  270. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/plugin/container.py +0 -0
  271. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/plugin/dependency_environment.py +0 -0
  272. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/plugin/logbuffer.py +0 -0
  273. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/plugin/restart_coordinator.py +0 -0
  274. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/plugin/runner_service.py +0 -0
  275. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/plugin/worker_launcher.py +0 -0
  276. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/plugin/worker_observability.py +0 -0
  277. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/plugin/worker_watchdog.py +0 -0
  278. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/security.py +0 -0
  279. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/runtime/settings.py +0 -0
  280. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/skill_store.py +0 -0
  281. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/storage.py +0 -0
  282. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/utils/__init__.py +0 -0
  283. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/utils/deadline.py +0 -0
  284. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/utils/discover/__init__.py +0 -0
  285. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/utils/discover/engine.py +0 -0
  286. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/utils/importutil.py +0 -0
  287. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/utils/log.py +0 -0
  288. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/utils/platform.py +0 -0
  289. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/src/langbot_plugin/workspace.py +0 -0
  290. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/__init__.py +0 -0
  291. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/definition/components/test_components.py +0 -0
  292. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/definition/components/test_imports.py +0 -0
  293. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/definition/test_manifest.py +0 -0
  294. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/entities/builtin/runner/test_context_result.py +0 -0
  295. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/entities/builtin/runner/test_history_event_entities.py +0 -0
  296. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/entities/builtin/runner/test_interaction.py +0 -0
  297. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/entities/builtin/runner/test_run_ledger_entities.py +0 -0
  298. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/entities/builtin/test_command_context.py +0 -0
  299. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/entities/builtin/test_platform_logger.py +0 -0
  300. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/entities/builtin/test_provider_message.py +0 -0
  301. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/entities/builtin/test_rag_models.py +0 -0
  302. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/entities/test_context.py +0 -0
  303. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/entities/test_events.py +0 -0
  304. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/proxies/__init__.py +0 -0
  305. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/proxies/test_agent_run_api_proxy.py +0 -0
  306. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/proxies/test_base.py +0 -0
  307. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/proxies/test_box_api.py +0 -0
  308. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/proxies/test_history_event_api_proxy.py +0 -0
  309. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/proxies/test_langbot_api.py +0 -0
  310. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/proxies/test_llm_stream_ownership.py +0 -0
  311. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/proxies/test_query_based_api.py +0 -0
  312. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/proxies/test_reasoning_parameter.py +0 -0
  313. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/test_runner_handlers.py +0 -0
  314. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/test_runner_handlers_matrix.py +0 -0
  315. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/api/test_runner_public_apis.py +0 -0
  316. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/__init__.py +0 -0
  317. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/test_admission.py +0 -0
  318. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/test_backend.py +0 -0
  319. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/test_backend_selection.py +0 -0
  320. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/test_client.py +0 -0
  321. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/test_e2b_backend.py +0 -0
  322. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/test_nsjail_backend.py +0 -0
  323. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/test_runtime.py +0 -0
  324. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/test_server.py +0 -0
  325. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/test_skill_store.py +0 -0
  326. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/box/test_storage_analysis.py +0 -0
  327. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/run/test_controller.py +0 -0
  328. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/run/test_hotreload.py +0 -0
  329. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/run/test_runtime_handler.py +0 -0
  330. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/test_buildplugin.py +0 -0
  331. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/test_gencomponent.py +0 -0
  332. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/test_i18n_form.py +0 -0
  333. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/test_initplugin.py +0 -0
  334. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/test_login.py +0 -0
  335. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/test_logout_publish.py +0 -0
  336. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/test_page_components.py +0 -0
  337. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/test_renderer.py +0 -0
  338. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/test_runner_usages.py +0 -0
  339. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/cli/test_runplugin.py +0 -0
  340. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/e2e/standalone_runtime_eba_probe.py +0 -0
  341. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/e2e/test_run_mount_isolation.py +0 -0
  342. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/entities/io/test_dependency_errors.py +0 -0
  343. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/entities/io/test_protocol.py +0 -0
  344. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/fixtures/eba_event_probe_plugin/.gitignore +0 -0
  345. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/fixtures/eba_event_probe_plugin/assets/icon.svg +0 -0
  346. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/fixtures/eba_event_probe_plugin/components/event_listener/default.py +0 -0
  347. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/fixtures/eba_event_probe_plugin/components/event_listener/default.yaml +0 -0
  348. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/fixtures/eba_event_probe_plugin/main.py +0 -0
  349. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/fixtures/eba_event_probe_plugin/manifest.yaml +0 -0
  350. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/helpers/__init__.py +0 -0
  351. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/helpers/protocol.py +0 -0
  352. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/packaging/test_installed_cli_blackbox.py +0 -0
  353. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/packaging/test_release_metadata.py +0 -0
  354. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/helper/test_marketplace.py +0 -0
  355. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/helper/test_pkgmgr.py +0 -0
  356. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/io/handlers/test_control_handler.py +0 -0
  357. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/io/handlers/test_import_contracts.py +0 -0
  358. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/io/handlers/test_legacy_file_chunk.py +0 -0
  359. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/io/handlers/test_plugin_handler.py +0 -0
  360. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/io/test_action_consistency.py +0 -0
  361. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/io/test_connections.py +0 -0
  362. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/io/test_controllers.py +0 -0
  363. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/io/test_handler.py +0 -0
  364. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/io/test_heartbeat_admission.py +0 -0
  365. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/io/test_stdio_server.py +0 -0
  366. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/io/test_stream_flow_control.py +0 -0
  367. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_artifact_store.py +0 -0
  368. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_container.py +0 -0
  369. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_dependency_environment.py +0 -0
  370. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_exact_dify_real_worker.py +0 -0
  371. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_exact_legacy_sdk_compat.py +0 -0
  372. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_installation_manager.py +0 -0
  373. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_legacy_shadow.py +0 -0
  374. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_mgr_runner.py +0 -0
  375. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_mode_resource_limits.py +0 -0
  376. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_restart_coordinator.py +0 -0
  377. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_shared_worker_subprocess.py +0 -0
  378. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_worker_launcher.py +0 -0
  379. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_worker_plugin_identity.py +0 -0
  380. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/plugin/test_workload_isolation.py +0 -0
  381. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/test_app.py +0 -0
  382. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/test_bounded_executor.py +0 -0
  383. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/test_event_loop_monitor.py +0 -0
  384. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/test_ops_metrics.py +0 -0
  385. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/runtime/test_pull_api_handlers.py +0 -0
  386. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/test_artifact_materialization.py +0 -0
  387. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/test_certification.py +0 -0
  388. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/test_log.py +0 -0
  389. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/test_message.py +0 -0
  390. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/test_plugin_logbuffer.py +0 -0
  391. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/test_skill_store.py +0 -0
  392. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/test_storage.py +0 -0
  393. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/test_worker_observability.py +0 -0
  394. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/test_worker_watchdog.py +0 -0
  395. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/utils/test_deadline.py +0 -0
  396. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/utils/test_discovery.py +0 -0
  397. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/utils/test_importutil.py +0 -0
  398. {langbot_plugin-0.7.14 → langbot_plugin-0.7.16}/tests/utils/test_platform.py +0 -0
@@ -0,0 +1,34 @@
1
+ name: Box Docker Acceptance
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ box-docker:
14
+ name: Real Docker socket mounts
15
+ runs-on: ubuntu-24.04
16
+ timeout-minutes: 15
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ with:
20
+ persist-credentials: false
21
+ - name: Build acceptance image
22
+ run: |
23
+ docker build -f tests/e2e/Dockerfile.box-socket \
24
+ -t langbot-box-socket-test .
25
+ docker pull alpine:3.21
26
+ - name: Verify private tmp and shared artifact cache
27
+ shell: bash
28
+ run: |
29
+ shared_root=$(mktemp -d "${RUNNER_TEMP}/box-socket.XXXXXX")
30
+ docker run --rm --init --name langbot-box-socket-test \
31
+ --volume /var/run/docker.sock:/var/run/docker.sock \
32
+ --volume "$shared_root:$shared_root" \
33
+ --env LANGBOT_TEST_BOX_SHARED_ROOT="$shared_root" \
34
+ langbot-box-socket-test
@@ -458,7 +458,10 @@ values. Ordinary fixed mounts include `content_digest` and `manifest_path` in
458
458
  the session signature; supported digest-addressed packages are excluded from it.
459
459
  Digest-addressed packages use the reserved
460
460
  `/opt/langbot-artifacts/<digest>` namespace. Docker/Podman pre-mount a read-only
461
- cache and atomically populate verified revisions before execution. E2B seals and
461
+ cache and atomically populate verified revisions before execution. Caches live
462
+ under the configured `box.local.host_root`, outside the default writable Workspace,
463
+ and are removed with their session. Containerized Box must share this root at the
464
+ same absolute path as the host daemon. E2B seals and
462
465
  verifies packages before publishing stable digest projections. These packages can
463
466
  be added after a Box is acquired, without replacing the session. nsjail applies
464
467
  all mounts independently per execution. Fixed ordinary host mounts still reject
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: langbot-plugin
3
- Version: 0.7.14
3
+ Version: 0.7.16
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.7.14"
3
+ version = "0.7.16"
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 = [
@@ -37,6 +37,8 @@ LANGBOT_AGENT_GATEWAY_INSTRUCTIONS = (
37
37
  "when reply permission is available."
38
38
  )
39
39
  DEFAULT_RUN_TOKEN_TTL_SECONDS = 3600.0
40
+ MAX_HTTP_MCP_BODY_BYTES = 1024 * 1024
41
+ _HTTP_BODY_TOO_LARGE = object()
40
42
 
41
43
 
42
44
  class _GatewayMCPResolver:
@@ -137,6 +139,12 @@ class AgentAssetGateway:
137
139
  self._thread: threading.Thread | None = None
138
140
  self._registrations: dict[str, AgentAssetGatewayRegistration] = {}
139
141
  self._lock = threading.RLock()
142
+ self._active_handlers = 0
143
+
144
+ @property
145
+ def active_handler_count(self) -> int:
146
+ with self._lock:
147
+ return self._active_handlers
140
148
 
141
149
  @property
142
150
  def endpoint(self) -> str:
@@ -156,6 +164,19 @@ class AgentAssetGateway:
156
164
  gateway = self
157
165
 
158
166
  class Handler(BaseHTTPRequestHandler):
167
+ def setup(self) -> None:
168
+ super().setup()
169
+ self.connection.settimeout(gateway.request_timeout)
170
+ with gateway._lock:
171
+ gateway._active_handlers += 1
172
+
173
+ def finish(self) -> None:
174
+ try:
175
+ super().finish()
176
+ finally:
177
+ with gateway._lock:
178
+ gateway._active_handlers -= 1
179
+
159
180
  def log_message(self, _format: str, *_args: typing.Any) -> None:
160
181
  return
161
182
 
@@ -170,7 +191,13 @@ class AgentAssetGateway:
170
191
  self.send_error(404)
171
192
  return
172
193
 
173
- payload = self._read_json_payload()
194
+ try:
195
+ payload = self._read_json_payload()
196
+ except OSError:
197
+ self.close_connection = True
198
+ return
199
+ if payload is _HTTP_BODY_TOO_LARGE:
200
+ return
174
201
  if isinstance(payload, Exception):
175
202
  self._write_json(
176
203
  400, {"ok": False, "error": f"invalid JSON: {payload}"}
@@ -190,6 +217,12 @@ class AgentAssetGateway:
190
217
  return
191
218
  self._write_json(200, result)
192
219
 
220
+ def handle_one_request(self) -> None:
221
+ try:
222
+ super().handle_one_request()
223
+ except (TimeoutError, OSError):
224
+ self.close_connection = True
225
+
193
226
  def do_DELETE(self) -> None:
194
227
  if self.path not in {"/mcp", "/mcp/http"}:
195
228
  self.send_error(404)
@@ -216,10 +249,18 @@ class AgentAssetGateway:
216
249
  try:
217
250
  length = int(self.headers.get("Content-Length", "0"))
218
251
  except ValueError:
219
- length = 0
252
+ return ValueError("invalid Content-Length")
253
+ if length < 0:
254
+ return ValueError("invalid Content-Length")
255
+ if length > MAX_HTTP_MCP_BODY_BYTES:
256
+ self._write_empty(413)
257
+ self.close_connection = True
258
+ return _HTTP_BODY_TOO_LARGE
220
259
  try:
221
260
  body = self.rfile.read(length).decode("utf-8")
222
261
  return json.loads(body) if body else {}
262
+ except TimeoutError:
263
+ raise
223
264
  except Exception as e:
224
265
  return e
225
266
 
@@ -169,6 +169,7 @@ class CLISandboxBackend(BaseSandboxBackend):
169
169
  self.command = command
170
170
  self.name = backend_name
171
171
  self.cpu_limit_enabled = True
172
+ self.artifact_cache_parent: str | None = None
172
173
  self._artifact_roots: dict[str, str] = {}
173
174
 
174
175
  async def is_available(self) -> bool:
@@ -224,7 +225,13 @@ class CLISandboxBackend(BaseSandboxBackend):
224
225
  )
225
226
  args.extend(["-v", mount_spec])
226
227
 
227
- artifact_root = tempfile.mkdtemp(prefix="langbot-box-artifacts-")
228
+ # Bind sources are resolved by the daemon, not this process. When a
229
+ # shared host root is configured, the cache must live there as well.
230
+ if self.artifact_cache_parent is not None:
231
+ os.makedirs(self.artifact_cache_parent, exist_ok=True)
232
+ artifact_root = tempfile.mkdtemp(
233
+ prefix="langbot-box-artifacts-", dir=self.artifact_cache_parent
234
+ )
228
235
  os.chmod(artifact_root, 0o755)
229
236
  self._artifact_roots[container_name] = artifact_root
230
237
  args.extend(["-v", f"{artifact_root}:{ARTIFACT_MOUNT_ROOT}:ro"])
@@ -329,21 +329,25 @@ class HostProcessBackend(BaseSandboxBackend):
329
329
  stderr=asyncio.subprocess.PIPE,
330
330
  start_new_session=True,
331
331
  )
332
- stdout_task = asyncio.create_task(self._read_stream(process.stdout))
333
- stderr_task = asyncio.create_task(self._read_stream(process.stderr))
332
+ completion = asyncio.gather(
333
+ process.wait(),
334
+ self._read_stream(process.stdout),
335
+ self._read_stream(process.stderr),
336
+ )
334
337
  timed_out = False
335
338
  try:
336
- await asyncio.wait_for(process.wait(), timeout=timeout_sec)
339
+ # Descendants can keep output pipes open after the shell exits.
340
+ # Keep readers alive during cleanup to preserve captured output.
341
+ await asyncio.wait_for(asyncio.shield(completion), timeout=timeout_sec)
337
342
  except asyncio.TimeoutError:
338
343
  timed_out = True
339
344
  await self._terminate_process_group(process)
340
345
  except asyncio.CancelledError:
341
346
  await self._terminate_process_group(process)
342
- await asyncio.gather(stdout_task, stderr_task, return_exceptions=True)
347
+ await completion
343
348
  raise
344
349
 
345
- stdout_bytes, stdout_total = await stdout_task
346
- stderr_bytes, stderr_total = await stderr_task
350
+ _, (stdout_bytes, stdout_total), (stderr_bytes, stderr_total) = await completion
347
351
  return _CommandResult(
348
352
  return_code=process.returncode if process.returncode is not None else -1,
349
353
  stdout=self._clip_captured_bytes(stdout_bytes, stdout_total),
@@ -357,20 +361,22 @@ class HostProcessBackend(BaseSandboxBackend):
357
361
  *,
358
362
  timeout_sec: float = 2.0,
359
363
  ) -> None:
360
- if process.returncode is not None:
361
- return
364
+ """Terminate the isolated group even if its shell has already exited."""
365
+ loop = asyncio.get_running_loop()
366
+ deadline = loop.time() + timeout_sec
362
367
  try:
363
368
  os.killpg(process.pid, signal.SIGTERM)
369
+ # A shell exit does not mean descendants exited or closed their pipes.
370
+ while True:
371
+ os.killpg(process.pid, 0)
372
+ remaining = deadline - loop.time()
373
+ if remaining <= 0:
374
+ break
375
+ await asyncio.sleep(min(0.05, remaining))
376
+ os.killpg(process.pid, signal.SIGKILL)
364
377
  except ProcessLookupError:
365
378
  pass
366
- try:
367
- await asyncio.wait_for(process.wait(), timeout=timeout_sec)
368
- except asyncio.TimeoutError:
369
- try:
370
- os.killpg(process.pid, signal.SIGKILL)
371
- except ProcessLookupError:
372
- pass
373
- await process.wait()
379
+ await process.wait()
374
380
 
375
381
  @staticmethod
376
382
  def _preview(command: str) -> str:
@@ -22,7 +22,7 @@ import pydantic
22
22
  from langbot_plugin.entities.io.context import ActionContext
23
23
  from langbot_plugin.runtime.event_loop_monitor import EventLoopLagMonitor
24
24
 
25
- from .backend import BaseSandboxBackend, DockerBackend
25
+ from .backend import BaseSandboxBackend, CLISandboxBackend, DockerBackend
26
26
  from .host_backend import HostProcessBackend
27
27
  from .nsjail_backend import NsjailBackend
28
28
  from .errors import (
@@ -1220,6 +1220,8 @@ done
1220
1220
  for backend in self.backends:
1221
1221
  if backend is None:
1222
1222
  continue
1223
+ if isinstance(backend, CLISandboxBackend):
1224
+ backend.artifact_cache_parent = self._host_root()
1223
1225
  backend_config = config.get(backend.name, {})
1224
1226
  if backend_config and hasattr(backend, "configure"):
1225
1227
  backend.configure(backend_config)
@@ -41,6 +41,7 @@ from langbot_plugin.runtime import bounded_executor
41
41
  from langbot_plugin.runtime import ops_metrics as ops_metrics_module
42
42
  from langbot_plugin.api.entities.context import EventContext
43
43
  from langbot_plugin.api.definition.components.manifest import ComponentManifest
44
+ from langbot_plugin.api.definition.components.common.event_listener import EventListener
44
45
  from langbot_plugin.api.definition.components.tool.tool import Tool
45
46
  from langbot_plugin.api.definition.components.command.command import Command
46
47
  from langbot_plugin.api.definition.components.knowledge_engine.engine import (
@@ -3194,6 +3195,12 @@ class PluginManager:
3194
3195
  if plugin_id not in include_plugins:
3195
3196
  continue
3196
3197
 
3198
+ if not any(
3199
+ component.manifest.kind == EventListener.__kind__
3200
+ for component in plugin.components
3201
+ ):
3202
+ continue
3203
+
3197
3204
  reply_message_chain_before = _dump_reply_message_chain(event_context)
3198
3205
  trusted_query_id = event_context.query_id
3199
3206
  trusted_query_uuid = event_context.query_uuid
@@ -0,0 +1 @@
1
+ __version__ = "0.7.16"
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import http.client
4
5
  import json
5
6
  import os
6
7
  import time
@@ -765,6 +766,44 @@ def test_asset_gateway_cleans_expired_registrations() -> None:
765
766
  asyncio.run(run_probe())
766
767
 
767
768
 
769
+ def test_asset_gateway_http_rejects_oversized_and_times_out_slow_requests() -> None:
770
+ async def run_probe() -> None:
771
+ gateway = AgentAssetGateway(request_timeout=0.1)
772
+ registration = gateway.register_run(
773
+ FakeRunAPI(), _authorized_ctx(), token="bounded-token"
774
+ )
775
+ try:
776
+ assert gateway._server is not None
777
+ port = gateway._server.server_address[1]
778
+ connection = http.client.HTTPConnection("127.0.0.1", port, timeout=1)
779
+ try:
780
+ connection.putrequest("POST", "/mcp")
781
+ connection.putheader("Content-Length", str(1024 * 1024 + 1))
782
+ connection.endheaders()
783
+ assert connection.getresponse().status == 413
784
+ finally:
785
+ connection.close()
786
+
787
+ connection = http.client.HTTPConnection("127.0.0.1", port, timeout=1)
788
+ try:
789
+ connection.putrequest("POST", "/mcp")
790
+ connection.putheader("Content-Length", "100")
791
+ connection.endheaders()
792
+ connection.send(b"{")
793
+ with pytest.raises(http.client.RemoteDisconnected):
794
+ connection.getresponse()
795
+ finally:
796
+ connection.close()
797
+
798
+ await asyncio.sleep(0.05)
799
+ assert gateway.active_handler_count == 0
800
+ finally:
801
+ registration.stop()
802
+ gateway.stop()
803
+
804
+ asyncio.run(run_probe())
805
+
806
+
768
807
  def test_daemon_config_and_mcp_payload_edges(monkeypatch) -> None:
769
808
  monkeypatch.setenv("LANGBOT_AGENT_RUNTIME_DAEMON_ENABLED", "true")
770
809
  monkeypatch.setenv("LANGBOT_AGENT_RUNTIME_DAEMON_HOST", "0.0.0.0")
@@ -0,0 +1,91 @@
1
+ """Docker/Podman bind sources must use the configured daemon-visible root."""
2
+
3
+ import logging
4
+ from pathlib import Path
5
+ from unittest import mock
6
+
7
+ import pytest
8
+
9
+ from langbot_plugin.box.backend import CLISandboxBackend, _CommandResult
10
+ from langbot_plugin.box.errors import BoxError
11
+ from langbot_plugin.box.models import ARTIFACT_MOUNT_ROOT, BoxSpec
12
+ from langbot_plugin.box.runtime import BoxRuntime
13
+
14
+
15
+ def configured_backend(tmp_path, monkeypatch, name):
16
+ monkeypatch.delenv("LANGBOT_BOX_CONFIG", raising=False)
17
+ logger = logging.getLogger(__name__)
18
+ backend = CLISandboxBackend(logger, name, name)
19
+ runtime = BoxRuntime(logger, backends=[backend])
20
+ # Relative host roots use the runtime's working directory, not the workspace.
21
+ monkeypatch.chdir(tmp_path)
22
+ runtime.init({"local": {"host_root": "shared", "default_workspace": "default"}})
23
+ return backend
24
+
25
+
26
+ @pytest.mark.asyncio
27
+ @pytest.mark.parametrize("name", ["docker", "podman"])
28
+ @pytest.mark.parametrize("mode", ["rw", "ro", "none"])
29
+ async def test_cache_mount_uses_shared_root_without_packages(
30
+ tmp_path, monkeypatch, name, mode
31
+ ):
32
+ backend = configured_backend(tmp_path, monkeypatch, name)
33
+ private_tmp = tmp_path / "container-private-tmp"
34
+ private_tmp.mkdir()
35
+ monkeypatch.setattr("tempfile.tempdir", str(private_tmp))
36
+ workspace = tmp_path / "shared" / "default"
37
+ command = mock.AsyncMock(return_value=_CommandResult(0, "", ""))
38
+ with mock.patch.object(backend, "_run_command", command):
39
+ first = await backend.start_session(
40
+ BoxSpec(session_id="first", host_path=str(workspace), host_path_mode=mode)
41
+ )
42
+ second = await backend.start_session(BoxSpec(session_id="second"))
43
+ roots = [Path(root) for root in backend._artifact_roots.values()]
44
+ assert len(set(roots)) == 2
45
+ assert all(
46
+ root.parent == tmp_path / "shared" and root.is_dir() for root in roots
47
+ )
48
+ assert not list(private_tmp.iterdir())
49
+ assert not list(workspace.iterdir())
50
+ for call, root in zip(command.call_args_list, roots):
51
+ args = call.args[0]
52
+ assert f"{root}:{ARTIFACT_MOUNT_ROOT}:ro" in args
53
+ await backend.stop_session(first)
54
+ assert not roots[0].exists() and roots[1].is_dir()
55
+ await backend.stop_session(second)
56
+ assert not backend._artifact_roots
57
+ assert not any(root.exists() for root in roots)
58
+
59
+
60
+ @pytest.mark.asyncio
61
+ async def test_failed_creation_cleans_shared_cache(tmp_path, monkeypatch):
62
+ backend = configured_backend(tmp_path, monkeypatch, "docker")
63
+ roots = []
64
+
65
+ async def run(args, **kwargs):
66
+ if args[1] == "run":
67
+ roots.extend(Path(root) for root in backend._artifact_roots.values())
68
+ assert roots[0].is_dir()
69
+ raise BoxError("creation failed")
70
+ return _CommandResult(0, "", "")
71
+
72
+ with mock.patch.object(backend, "_run_command", side_effect=run):
73
+ with pytest.raises(BoxError, match="creation failed"):
74
+ await backend.start_session(BoxSpec(session_id="failed"))
75
+ assert roots and not any(root.exists() for root in roots)
76
+ assert not backend._artifact_roots
77
+
78
+
79
+ @pytest.mark.asyncio
80
+ async def test_unusable_shared_root_does_not_retry_in_private_tmp(
81
+ tmp_path, monkeypatch
82
+ ):
83
+ backend = configured_backend(tmp_path, monkeypatch, "docker")
84
+ backend.artifact_cache_parent = str(tmp_path / "file")
85
+ Path(backend.artifact_cache_parent).write_text("not a directory")
86
+ command = mock.AsyncMock()
87
+ with mock.patch.object(backend, "_run_command", command):
88
+ with pytest.raises(FileExistsError):
89
+ await backend.start_session(BoxSpec(session_id="invalid"))
90
+ command.assert_not_called()
91
+ assert not backend._artifact_roots
@@ -3,6 +3,8 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import logging
5
5
  import os
6
+ import shlex
7
+ import signal
6
8
 
7
9
  import pytest
8
10
 
@@ -209,3 +211,81 @@ async def test_host_backend_reports_no_isolation(backend):
209
211
  assert readiness["mount_isolation"] is False
210
212
  assert readiness["network_isolation"] is False
211
213
  assert readiness["cgroup_v2"] is False
214
+
215
+
216
+ @pytest.mark.anyio
217
+ @pytest.mark.parametrize("trigger", ["timeout", "cancel"])
218
+ @pytest.mark.parametrize("topology", ["closed_output", "exited_parent"])
219
+ async def test_host_backend_kills_sigterm_resistant_descendants(
220
+ backend, tmp_path, trigger, topology
221
+ ):
222
+ child_script = (
223
+ "trap '' TERM\n"
224
+ + ("exec >/dev/null 2>&1\n" if topology == "closed_output" else "")
225
+ + 'printf %s "$$" > child.pid\nexec sleep 30'
226
+ )
227
+ command = (
228
+ "printf before-timeout; printf before-timeout-error >&2\n"
229
+ + 'printf %s "$$" > parent.pid\n'
230
+ + f"sh -c {shlex.quote(child_script)} &\n"
231
+ + "while [ ! -s child.pid ]; do sleep 0.01; done\n"
232
+ + ("exit 0" if topology == "exited_parent" else "wait")
233
+ )
234
+ spec = _spec(tmp_path, cmd=command, timeout_sec=1 if trigger == "timeout" else 30)
235
+ session = await backend.start_session(spec)
236
+ task = asyncio.create_task(backend.exec(session, spec))
237
+ parent_pid = None
238
+ try:
239
+ loop = asyncio.get_running_loop()
240
+ deadline = loop.time() + 5
241
+ pid_file = tmp_path / "child.pid"
242
+ while not pid_file.exists() or not pid_file.read_text().strip():
243
+ assert loop.time() < deadline, "child did not become ready"
244
+ await asyncio.sleep(0.01)
245
+ child_pid = int(pid_file.read_text())
246
+ parent_pid = int((tmp_path / "parent.pid").read_text())
247
+ assert os.getpgid(child_pid) == parent_pid
248
+
249
+ # Exercise cancellation only after the parent has already been reaped.
250
+ if topology == "exited_parent":
251
+ while True:
252
+ try:
253
+ os.kill(parent_pid, 0)
254
+ except ProcessLookupError:
255
+ break
256
+ assert loop.time() < deadline, "parent shell did not exit"
257
+ await asyncio.sleep(0.01)
258
+
259
+ if trigger == "cancel":
260
+ task.cancel()
261
+ with pytest.raises(asyncio.CancelledError):
262
+ await asyncio.wait_for(asyncio.shield(task), timeout=6)
263
+ else:
264
+ result = await asyncio.wait_for(asyncio.shield(task), timeout=6)
265
+ assert result.status == BoxExecutionStatus.TIMED_OUT
266
+ assert result.stdout == "before-timeout"
267
+ assert result.stderr == "before-timeout-error"
268
+
269
+ deadline = loop.time() + 1
270
+ while True:
271
+ try:
272
+ os.kill(child_pid, 0)
273
+ except ProcessLookupError:
274
+ break
275
+ assert loop.time() < deadline, "SIGTERM-resistant child survived cleanup"
276
+ await asyncio.sleep(0.01)
277
+ finally:
278
+ # A failing regression must not leave a live descendant in the runner.
279
+ if parent_pid is None:
280
+ pid_file = tmp_path / "parent.pid"
281
+ if pid_file.exists() and pid_file.read_text().strip():
282
+ parent_pid = int(pid_file.read_text())
283
+ if parent_pid is not None:
284
+ try:
285
+ os.killpg(parent_pid, signal.SIGKILL)
286
+ except ProcessLookupError:
287
+ pass
288
+ if not task.done():
289
+ task.cancel()
290
+ await asyncio.gather(task, return_exceptions=True)
291
+ await backend.stop_session(session)
@@ -0,0 +1,8 @@
1
+ FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
2
+ RUN apt-get update && apt-get install -y --no-install-recommends docker.io && rm -rf /var/lib/apt/lists/*
3
+ WORKDIR /sdk
4
+ COPY pyproject.toml README.md ./
5
+ COPY src ./src
6
+ COPY tests/e2e/test_box_socket_mounts.py ./tests/e2e/test_box_socket_mounts.py
7
+ RUN uv sync --dev
8
+ CMD ["uv", "run", "--no-sync", "pytest", "-q", "tests/e2e/test_box_socket_mounts.py"]
@@ -0,0 +1,10 @@
1
+ **
2
+ !pyproject.toml
3
+ !README.md
4
+ !src/
5
+ !src/**
6
+ !tests/
7
+ !tests/e2e/
8
+ !tests/e2e/test_box_socket_mounts.py
9
+ **/__pycache__/
10
+ **/*.pyc
@@ -0,0 +1,125 @@
1
+ """Run inside a container with only Docker socket and same-path host_root binds.
2
+
3
+ Set LANGBOT_TEST_BOX_SHARED_ROOT to that shared root to enable this acceptance
4
+ test. The runtime's private /tmp is deliberately not shared with the daemon.
5
+
6
+ Manual acceptance on a disposable Docker host, from the repository root::
7
+
8
+ docker build -f tests/e2e/Dockerfile.box-socket -t langbot-box-socket-test .
9
+ docker pull alpine:3.21
10
+ shared_root=$(mktemp -d)
11
+ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
12
+ -v "$shared_root:$shared_root" \
13
+ -e LANGBOT_TEST_BOX_SHARED_ROOT="$shared_root" langbot-box-socket-test
14
+ """
15
+
16
+ import asyncio
17
+ import json
18
+ import logging
19
+ import os
20
+ from pathlib import Path
21
+ import tempfile
22
+
23
+ import pytest
24
+
25
+ from langbot_plugin.artifact import build_tree_manifest
26
+ from langbot_plugin.box.backend import DockerBackend
27
+ from langbot_plugin.box.models import ARTIFACT_MOUNT_ROOT, BoxMountSpec, BoxSpec
28
+ from langbot_plugin.box.runtime import BoxRuntime
29
+
30
+ pytestmark = pytest.mark.skipif(
31
+ not os.environ.get("LANGBOT_TEST_BOX_SHARED_ROOT"),
32
+ reason="requires a container with host Docker socket and same-path shared root",
33
+ )
34
+
35
+
36
+ def test_private_tmp_and_shared_root_with_real_docker():
37
+ assert Path("/.dockerenv").exists(), "Run this test inside the Box container"
38
+ root = Path(os.environ["LANGBOT_TEST_BOX_SHARED_ROOT"])
39
+ root.mkdir(parents=True, exist_ok=True)
40
+ with tempfile.TemporaryDirectory(dir=root, prefix="socket-test-") as shared:
41
+ asyncio.run(_exercise(Path(shared)))
42
+
43
+
44
+ async def _exercise(shared):
45
+ logger = logging.getLogger(__name__)
46
+ backend = DockerBackend(logger)
47
+ runtime = BoxRuntime(logger, backends=[backend])
48
+ runtime.init(
49
+ {
50
+ "backend": "docker",
51
+ "local": {"host_root": str(shared)},
52
+ "docker": {"cpu_limit_enabled": False},
53
+ }
54
+ )
55
+ image = os.environ.get("LANGBOT_TEST_BOX_IMAGE", "alpine:3.21")
56
+ empty = BoxSpec(
57
+ session_id="socket-mounts",
58
+ image=image,
59
+ cmd="printf ready",
60
+ network="off",
61
+ host_path=str(shared / "workspace"),
62
+ )
63
+ cache = None
64
+ try:
65
+ # Creating a Box with no packages must still work with a private /tmp.
66
+ result = await runtime.execute(empty)
67
+ assert result.ok and result.stdout == "ready", result.stderr
68
+ session = runtime.get_sessions()[0]
69
+ backend_id = session["backend_session_id"]
70
+ cache = Path(backend._artifact_roots[backend_id])
71
+ assert cache.parent == shared and cache.is_dir()
72
+ inspection = await backend._run_command(
73
+ ["docker", "inspect", backend_id], timeout_sec=10, check=True
74
+ )
75
+ container = json.loads(inspection.stdout)[0]
76
+ assert container["HostConfig"]["ReadonlyRootfs"]
77
+ mounts = container["Mounts"]
78
+ workspace_mount = next(m for m in mounts if m["Destination"] == "/workspace")
79
+ assert workspace_mount["Source"] == empty.host_path and workspace_mount["RW"]
80
+ artifact_mount = next(
81
+ m for m in mounts if m["Destination"] == ARTIFACT_MOUNT_ROOT
82
+ )
83
+ assert artifact_mount["Source"] == str(cache) and not artifact_mount["RW"]
84
+
85
+ # Packages prepared in private /tmp are copied into the shared cache;
86
+ # adding one must preserve the already-created sandbox and its state.
87
+ state = await runtime.execute(
88
+ empty.model_copy(update={"cmd": "printf kept >/tmp/state"})
89
+ )
90
+ assert state.ok, state.stderr
91
+ with tempfile.TemporaryDirectory() as private:
92
+ package = Path(private) / "package"
93
+ package.mkdir()
94
+ (package / "value").write_text("package")
95
+ digest, manifest = build_tree_manifest(
96
+ str(package), max_files=10, max_total_bytes=1024
97
+ )
98
+ manifest_path = Path(private) / "manifest.json"
99
+ manifest_path.write_text(json.dumps(manifest))
100
+ mount = BoxMountSpec(
101
+ host_path=str(package),
102
+ mount_path=f"{ARTIFACT_MOUNT_ROOT}/{digest[7:]}",
103
+ mode="ro",
104
+ content_digest=digest,
105
+ manifest_path=str(manifest_path),
106
+ )
107
+ mounted = empty.model_copy(
108
+ update={
109
+ "extra_mounts": [mount],
110
+ "cmd": f"cat {mount.mount_path}/value /tmp/state",
111
+ }
112
+ )
113
+ result = await runtime.execute(mounted)
114
+ assert result.ok and result.stdout == "packagekept", result.stderr
115
+ assert runtime.get_sessions()[0]["backend_session_id"] == backend_id
116
+ denied = await runtime.execute(
117
+ mounted.model_copy(
118
+ update={"cmd": f"printf changed >{mount.mount_path}/value"}
119
+ )
120
+ )
121
+ assert not denied.ok
122
+ assert (package / "value").read_text() == "package"
123
+ finally:
124
+ await runtime.shutdown()
125
+ assert cache is not None and not cache.exists()