agentenv-framework-protocol 0.1.481__tar.gz → 0.1.483__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 (165) hide show
  1. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/PKG-INFO +1 -1
  2. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/pyproject.toml +1 -1
  3. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/agent_env_environment.py +22 -3
  4. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/__init__.py +2 -2
  5. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/driver.py +28 -8
  6. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/extensions.py +2 -2
  7. agentenv_framework_protocol-0.1.483/src/agentenv_protocol/computer/prefix.py +51 -0
  8. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/runtime.py +1 -1
  9. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/settings.py +17 -0
  10. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/skill.py +6 -3
  11. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/tools.py +7 -2
  12. agentenv_framework_protocol-0.1.483/tests/test_computer_tool_prefix.py +153 -0
  13. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/.gitignore +0 -0
  14. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/LICENSE +0 -0
  15. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/NOTICE +0 -0
  16. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/README.md +0 -0
  17. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/THIRD_PARTY_NOTICES.md +0 -0
  18. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/__init__.py +0 -0
  19. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/advanced_agent.py +0 -0
  20. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/basic_agent.py +0 -0
  21. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/custom_extensions_agent.py +0 -0
  22. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/multimodal_agent.py +0 -0
  23. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/reference_model.py +0 -0
  24. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/streaming_agent.py +0 -0
  25. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/.gitignore +0 -0
  26. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/Dockerfile +0 -0
  27. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/Dockerfile.dockerignore +0 -0
  28. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/README.md +0 -0
  29. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/data/seed.json +0 -0
  30. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/env.toml +0 -0
  31. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/site.py +0 -0
  32. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/web/index.html +0 -0
  33. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/web/package-lock.json +0 -0
  34. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/web/package.json +0 -0
  35. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/web/src/main.js +0 -0
  36. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/web/src/style.css +0 -0
  37. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/examples/website/notes/web/vite.config.js +0 -0
  38. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/__init__.py +0 -0
  39. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/a2a_agent/__init__.py +0 -0
  40. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/a2a_agent/_trajectory_log.py +0 -0
  41. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
  42. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/a2a_agent/extensions.py +0 -0
  43. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
  44. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/a2a_agent/peer_agents.py +0 -0
  45. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/a2a_agent/proxy.py +0 -0
  46. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
  47. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/a2a_agent/staging.py +0 -0
  48. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
  49. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
  50. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/browser_cdp.py +0 -0
  51. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/client.py +0 -0
  52. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/_declare.py +0 -0
  53. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/_linux.py +0 -0
  54. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/_load.py +0 -0
  55. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/_macos.py +0 -0
  56. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/_with_fds.py +0 -0
  57. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/_x11.py +0 -0
  58. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/app.py +0 -0
  59. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/app_data.py +0 -0
  60. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/app_runtime.py +0 -0
  61. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/apps.py +0 -0
  62. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/card.py +0 -0
  63. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/checks.py +0 -0
  64. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/conformance/__init__.py +0 -0
  65. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/conformance/__main__.py +0 -0
  66. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/conformance/checks.py +0 -0
  67. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/conformance/client.py +0 -0
  68. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/conformance/fixture.py +0 -0
  69. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/conformance/mp4.py +0 -0
  70. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/conformance/page_fixture.py +0 -0
  71. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/conformance/report.py +0 -0
  72. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/dataplane.py +0 -0
  73. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/errors.py +0 -0
  74. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/frames.py +0 -0
  75. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/keys.py +0 -0
  76. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/launch.py +0 -0
  77. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/manifest.py +0 -0
  78. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/operations.py +0 -0
  79. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/platform.py +0 -0
  80. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/queue.py +0 -0
  81. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/recording.py +0 -0
  82. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/results.py +0 -0
  83. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/run_install_step.py +0 -0
  84. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/seed.py +0 -0
  85. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/serve.py +0 -0
  86. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/serve_app.py +0 -0
  87. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/session.py +0 -0
  88. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/spaces.py +0 -0
  89. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/computer/svc.py +0 -0
  90. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/errors.py +0 -0
  91. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/manifest.py +0 -0
  92. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/preflight.py +0 -0
  93. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/tool_access.py +0 -0
  94. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/tool_client.py +0 -0
  95. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/tool_context.py +0 -0
  96. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/transfers.py +0 -0
  97. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/types.py +0 -0
  98. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website/__init__.py +0 -0
  99. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website/clock.py +0 -0
  100. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website/conformance.py +0 -0
  101. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website/environment.py +0 -0
  102. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website/errors.py +0 -0
  103. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website/readiness.py +0 -0
  104. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website/routes.py +0 -0
  105. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website/session.py +0 -0
  106. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website_browser/__init__.py +0 -0
  107. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website_browser/control.py +0 -0
  108. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website_browser/data.py +0 -0
  109. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website_browser/devtools.py +0 -0
  110. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website_browser/environment.py +0 -0
  111. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/src/agentenv_protocol/website_browser/shapes.py +0 -0
  112. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/computer_fake.py +0 -0
  113. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/probe_fake.py +0 -0
  114. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_a2a_agent.py +0 -0
  115. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_a2a_agent_examples.py +0 -0
  116. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_a2a_cdp_config.py +0 -0
  117. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_a2a_object_transfers.py +0 -0
  118. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_a2a_peer_agents.py +0 -0
  119. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_a2a_proxy.py +0 -0
  120. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_a2a_staging.py +0 -0
  121. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_a2a_trajectory_log.py +0 -0
  122. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_agent_env_environment.py +0 -0
  123. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_browser_cdp.py +0 -0
  124. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_browser_data.py +0 -0
  125. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_browser_state.py +0 -0
  126. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_client.py +0 -0
  127. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_app_data.py +0 -0
  128. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_app_server.py +0 -0
  129. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_conformance.py +0 -0
  130. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_conformance_checks.py +0 -0
  131. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_conformance_page.py +0 -0
  132. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_conformance_recording.py +0 -0
  133. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_dataplane.py +0 -0
  134. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_declare.py +0 -0
  135. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_driver.py +0 -0
  136. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_extensions.py +0 -0
  137. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_frames.py +0 -0
  138. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_macos.py +0 -0
  139. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_page.py +0 -0
  140. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_queue.py +0 -0
  141. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_recording.py +0 -0
  142. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_seed.py +0 -0
  143. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_serve.py +0 -0
  144. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_spaces.py +0 -0
  145. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_computer_svc_forward.py +0 -0
  146. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_control_token.py +0 -0
  147. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_data_objects.py +0 -0
  148. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_errors.py +0 -0
  149. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_extension_methods.py +0 -0
  150. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_intake.py +0 -0
  151. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_manifest.py +0 -0
  152. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_preflight.py +0 -0
  153. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_tool_access.py +0 -0
  154. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_tool_annotations.py +0 -0
  155. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_tool_client.py +0 -0
  156. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_tool_context.py +0 -0
  157. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_tool_context_fastmcp.py +0 -0
  158. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_tool_context_injection.py +0 -0
  159. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_website_api.py +0 -0
  160. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_website_browser.py +0 -0
  161. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_website_conformance.py +0 -0
  162. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_website_environment.py +0 -0
  163. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_website_mcp.py +0 -0
  164. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_website_notes_example.py +0 -0
  165. {agentenv_framework_protocol-0.1.481 → agentenv_framework_protocol-0.1.483}/tests/test_website_readiness.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentenv-framework-protocol
3
- Version: 0.1.481
3
+ Version: 0.1.483
4
4
  Summary: Open data-plane protocol and server SDK for agent environments.
5
5
  Project-URL: Homepage, https://www.agentenvframework.com
6
6
  Project-URL: Documentation, https://www.agentenvframework.com/docs
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentenv-framework-protocol"
3
- version = "0.1.481"
3
+ version = "0.1.483"
4
4
  description = "Open data-plane protocol and server SDK for agent environments."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -309,7 +309,8 @@ class AgentEnvApplication(ABC):
309
309
  self._app: Any = None
310
310
  ext_handlers = _served_extensions(handler, _discover_extensions(handler))
311
311
  environment_card = _merge_extensions(environment_card, ext_handlers + _declared_extensions(handler, ext_handlers))
312
- tool_handlers = _served_tools(handler, _with_forwarded(_discover_tools(handler, environment_card.name), handler))
312
+ tool_handlers = _named_tools(handler, _served_tools(
313
+ handler, _with_forwarded(_discover_tools(handler, environment_card.name), handler)))
313
314
  if tool_handlers and not self.supports_tools:
314
315
  raise ValueError(f"@tool methods require a FastMCP-backed application; {type(self).__name__} cannot serve MCP tools")
315
316
  self._resources = _discover_resources(handler)
@@ -1107,11 +1108,15 @@ def _with_parts(method: Callable, parts: dict[str, str]) -> Callable:
1107
1108
 
1108
1109
 
1109
1110
  def _instructions(handler: Any) -> str | None:
1110
- """What the handler's @server_instructions method returns; None without one."""
1111
+ """What the handler's @server_instructions method returns, as its ``served_instructions`` serves it; None without
1112
+ one."""
1111
1113
  marked = [method for method, _marker in _marked_members(handler, _INSTRUCTIONS_ATTR)]
1112
1114
  if len(marked) > 1:
1113
1115
  raise ValueError(f"an environment has one @server_instructions method, not {sorted(m.__name__ for m in marked)}")
1114
- return marked[0]() if marked else None
1116
+ if not marked:
1117
+ return None
1118
+ hook = getattr(handler, "served_instructions", None)
1119
+ return hook(marked[0]()) if hook is not None else marked[0]()
1115
1120
 
1116
1121
 
1117
1122
  def _discover_methods(handler: Any) -> dict:
@@ -1320,6 +1325,20 @@ def _served_tools(handler: Any, tool_handlers: list) -> list:
1320
1325
  return [(descriptor, by_name[descriptor.name]) for descriptor in chosen]
1321
1326
 
1322
1327
 
1328
+ def _named_tools(handler: Any, tool_handlers: list) -> list:
1329
+ """The tools as the handler's ``serve_tools_as`` names and describes them, in their order; as they are without one."""
1330
+ hook = getattr(handler, "serve_tools_as", None)
1331
+ if hook is None:
1332
+ return tool_handlers
1333
+ named = list(hook([descriptor for descriptor, _fn in tool_handlers]))
1334
+ if len(named) != len(tool_handlers):
1335
+ raise ValueError(f"serve_tools_as returned {len(named)} tools for {len(tool_handlers)}")
1336
+ names = [descriptor.name for descriptor in named]
1337
+ if len(set(names)) != len(names):
1338
+ raise ValueError(f"serve_tools_as named two tools alike: {sorted({n for n in names if names.count(n) > 1})}")
1339
+ return [(descriptor, fn) for descriptor, (_old, fn) in zip(named, tool_handlers)]
1340
+
1341
+
1323
1342
  def _merge_tools(card: EnvironmentCard, tool_handlers: list) -> EnvironmentCard:
1324
1343
  """Advertise decorator-declared tools under card.capabilities; card-declared names win."""
1325
1344
  if not tool_handlers:
@@ -82,7 +82,7 @@ from .dataplane import part_bytes
82
82
  from .keys import PRINTABLE_KEYS
83
83
  from .manifest import ManifestSegment, RecordingManifest
84
84
  from .platform import Platform
85
- from .settings import SETTINGS_ENV, ComputerSettings
85
+ from .settings import SETTINGS_ENV, TOOL_PREFIX_ENV, ComputerSettings, tool_prefix_problem
86
86
 
87
87
  if TYPE_CHECKING:
88
88
  from .card import (
@@ -127,7 +127,7 @@ __all__ = [
127
127
  "Launch", "Ready", "Packages", "AptSource", "Download", "RunScript",
128
128
  "AppDeclaration", "AppOs",
129
129
  "APP_SETTINGS_ENV", "part_bytes",
130
- "Display", "Image", "Effect", "ExecResult", "ReadyCheck", "Platform", "ComputerSettings", "SETTINGS_ENV",
130
+ "Display", "Image", "Effect", "ExecResult", "ReadyCheck", "Platform", "ComputerSettings", "SETTINGS_ENV", "TOOL_PREFIX_ENV", "tool_prefix_problem",
131
131
  "Tab", "PageState", "PageDialog", "FileChooser",
132
132
  "PRINTABLE_KEYS",
133
133
  "display", "set_display", "capture", "click", "drag", "scroll", "type_text", "press",
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
 
6
6
  import asyncio
7
7
  import logging
8
+ import os
8
9
  from pathlib import Path
9
10
  from typing import Any, Callable, ClassVar, Literal, Optional
10
11
 
@@ -13,9 +14,9 @@ from starlette.requests import Request
13
14
  from starlette.responses import Response
14
15
  from starlette.websockets import WebSocket
15
16
 
16
- from ..agent_env_environment import (_CARD_CONFIG_ATTR, AgentEnvEnvironment, AgentEnvFastMCPApplication,
17
- AgentEnvError, _error_response, add_websocket_route, control_check,
18
- environment_card)
17
+ from ..agent_env_environment import (_CARD_CONFIG_ATTR, _TOOL_ATTR, AgentEnvEnvironment, AgentEnvFastMCPApplication,
18
+ AgentEnvError, _error_response, _marked_members, add_websocket_route,
19
+ control_check, environment_card)
19
20
  from ..tool_context import ToolContext
20
21
  from ..types import EnvironmentCard, EnvironmentExtension, EnvironmentTool
21
22
  from . import _linux, apps, card, svc
@@ -27,8 +28,9 @@ from .operations import ReadyCheck, check_class, default_for
27
28
  from .platform import Platform, detect
28
29
  from .recording import Recording
29
30
  from .runtime import ComputerRuntime
30
- from .settings import ComputerSettings, load_settings
31
- from .tools import Tools
31
+ from .prefix import ToolPrefix
32
+ from .settings import TOOL_PREFIX_ENV, ComputerSettings, load_settings, tool_prefix_problem
33
+ from .tools import COMPUTER_TOOLS, Tools
32
34
 
33
35
  #: The caller every call comes from until agents carry an identity of their own.
34
36
  DEFAULT_IDENTITY = "default"
@@ -99,9 +101,15 @@ class ComputerDriverEnvironment(Tools, Extensions, DataPlane, AgentEnvEnvironmen
99
101
  check_class(cls)
100
102
  check_features(cls)
101
103
 
102
- def __init__(self, settings: ComputerSettings | None = None, platform: Platform | None = None) -> None:
104
+ def __init__(self, settings: ComputerSettings | None = None, platform: Platform | None = None,
105
+ tool_prefix: str | None = None) -> None:
103
106
  self.settings = settings if settings is not None else load_settings(model=type(self).Settings)
104
107
  self.platform = platform if platform is not None else detect()
108
+ prefix = os.environ.get(TOOL_PREFIX_ENV, "") if tool_prefix is None else tool_prefix
109
+ if problem := tool_prefix_problem(prefix):
110
+ raise ValueError(problem)
111
+ #: The start of every name it serves a tool under: the deploy's ``AGENTENV_TOOL_PREFIX``, none by default.
112
+ self._computer_prefix = ToolPrefix(prefix)
105
113
  self._computer = ComputerRuntime(self)
106
114
  #: The routes each app's card opens to agents, by app, read once it answers.
107
115
  self._computer_agent_routes: dict[str, list[str]] = {}
@@ -113,7 +121,7 @@ class ComputerDriverEnvironment(Tools, Extensions, DataPlane, AgentEnvEnvironmen
113
121
  running its ``__init__``: what a ``computer_driver`` env records for each platform it supports. The card
114
122
  hooks may read the class and the base class's state, not what a subclass's ``__init__`` sets."""
115
123
  driver = cls.__new__(cls)
116
- ComputerDriverEnvironment.__init__(driver, settings=cls.Settings(), platform=platform)
124
+ ComputerDriverEnvironment.__init__(driver, settings=cls.Settings(), platform=platform, tool_prefix="")
117
125
  config = dict(getattr(cls, _CARD_CONFIG_ATTR, None) or {})
118
126
  card = EnvironmentCard(**{**config, "name": config.get("name") or cls.__name__})
119
127
  return AgentEnvFastMCPApplication(card, driver).environment_card
@@ -203,6 +211,17 @@ class ComputerDriverEnvironment(Tools, Extensions, DataPlane, AgentEnvEnvironmen
203
211
  def served_tools(self, tools: list[EnvironmentTool]) -> list[EnvironmentTool]:
204
212
  return card.served_tools(self, tools)
205
213
 
214
+ def serve_tools_as(self, tools: list[EnvironmentTool]) -> list[EnvironmentTool]:
215
+ return self._computer_prefix.tools(tools)
216
+
217
+ def served_instructions(self, text: str) -> str:
218
+ return self._computer_prefix.instructions(text, self._computer_other_tools())
219
+
220
+ def _computer_other_tools(self) -> list[str]:
221
+ """Its driver's own and forwarded tools, whose text stays as written, by their own names."""
222
+ return [marker.name for _member, marker in _marked_members(self, _TOOL_ATTR) if marker.name not in COMPUTER_TOOLS] + [
223
+ forwarded.tool.name for forwarded in self.forwarded_tools()]
224
+
206
225
  def served_extensions(self, extensions: list[EnvironmentExtension]) -> list[EnvironmentExtension]:
207
226
  return card.served_extensions(self, extensions)
208
227
 
@@ -213,7 +232,8 @@ class ComputerDriverEnvironment(Tools, Extensions, DataPlane, AgentEnvEnvironmen
213
232
  return card.extension_params(self, uri)
214
233
 
215
234
  def declared_extensions(self) -> list[EnvironmentExtension]:
216
- return card.declared_extensions(self)
235
+ return [extension.model_copy(update={"description": self._computer_prefix.text(extension.description)})
236
+ for extension in card.declared_extensions(self)]
217
237
 
218
238
  def _computer_require_init(self) -> None:
219
239
  if self._computer is None:
@@ -79,8 +79,8 @@ class Extensions:
79
79
  self.settings.default_norm_scale)
80
80
  self.settings = self.settings.model_copy(update={"default_space": space, "default_norm_scale": scale})
81
81
  for tool in self.mounted_card().capabilities.tools or []:
82
- if tool.name in card.POINT_TOOLS:
83
- await self.update_tool(tool.name, description=card.point_description(self, tool.name))
82
+ if (own := self._computer_prefix.own(tool.name)) in card.POINT_TOOLS:
83
+ await self.update_tool(tool.name, description=self._computer_prefix.text(card.point_description(self, own)))
84
84
  return await self._computer_spaces_get()
85
85
 
86
86
  @extension(COMPUTER_KEYBOARD, op="get", method="GET",
@@ -0,0 +1,51 @@
1
+ """A computer's tool prefix: the names it serves its tools under, so several computers serve theirs side by side on
2
+ one gateway. The SDK's ``computer_*`` names in its text are rewritten to them; its driver's own and forwarded tools
3
+ keep their text as written, and a sentence tells an agent how those are named."""
4
+ from __future__ import annotations
5
+
6
+ import re
7
+ from collections.abc import Iterable
8
+
9
+ from ..types import EnvironmentTool
10
+ from .tools import COMPUTER_TOOLS
11
+
12
+ #: The SDK's tool names, and a ``computer_*`` family of them, each as a whole word.
13
+ _SDK_NAMES = re.compile(r"(?<![\w-])(?:" + "|".join([*map(re.escape, sorted(COMPUTER_TOOLS, key=len, reverse=True)),
14
+ r"computer_(?:[a-z]+_)*\*"]) + r")(?![\w-])")
15
+
16
+
17
+ class ToolPrefix:
18
+ """``prefix``, the start of every name a computer serves a tool under; none leaves every name as it is."""
19
+
20
+ def __init__(self, prefix: str) -> None:
21
+ self.prefix = prefix
22
+
23
+ def served(self, name: str) -> str:
24
+ """The name the tool ``name`` is served under."""
25
+ return f"{self.prefix}{name}"
26
+
27
+ def own(self, served: str) -> str:
28
+ """A served tool's own name."""
29
+ return served.removeprefix(self.prefix)
30
+
31
+ def text(self, text: str | None) -> str | None:
32
+ """``text`` naming the SDK's tools as they are served; any other name stays as written."""
33
+ return _SDK_NAMES.sub(lambda m: self.prefix + m.group(0), text) if self.prefix and text else text
34
+
35
+ def tools(self, tools: list[EnvironmentTool]) -> list[EnvironmentTool]:
36
+ """``tools`` under their served names, the SDK's own with their descriptions' names served too."""
37
+ return [tool.model_copy(update={"name": self.served(tool.name), **(
38
+ {"description": self.text(tool.description)} if tool.name in COMPUTER_TOOLS else {})}) for tool in tools]
39
+
40
+ def rule(self, others: Iterable[str]) -> str | None:
41
+ """The sentence telling an agent how ``others``, the tools whose text stays as written, are served; None
42
+ without a prefix or any of them."""
43
+ example = next(iter(others), None)
44
+ if not self.prefix or example is None:
45
+ return None
46
+ return (f"This computer serves its tools under `{self.prefix}`: a tool this text names without `{self.prefix}` is "
47
+ f"served with it, so `{example}` is `{self.prefix}{example}`. Names inside a tool's arguments stay as written.")
48
+
49
+ def instructions(self, text: str, others: Iterable[str]) -> str:
50
+ """An MCP server's instructions as the computer serves them: after the rule, if there is one, its names served."""
51
+ return "\n\n".join(part for part in (self.rule(others), self.text(text)) if part)
@@ -116,7 +116,7 @@ class ComputerRuntime:
116
116
  async with self.queue.turn(self.driver.settings.queue_wait_s):
117
117
  if operation in DIALOG_BLOCKS and self.has("page") and (dialog := (await self.page_now()).dialog):
118
118
  raise DialogOpen(f'a {dialog.type} dialog is open on the page: "{dialog.message}"; answer it with '
119
- "computer_dialog first")
119
+ f"{self.driver._computer_prefix.served('computer_dialog')} first")
120
120
  if mapped and frame is not None:
121
121
  self.frames.check_current(frame, await self.display_now())
122
122
  if at_pointer:
@@ -3,12 +3,29 @@ from __future__ import annotations
3
3
 
4
4
  import json
5
5
  import os
6
+ import re
6
7
  from typing import Literal
7
8
 
8
9
  from pydantic import BaseModel, ConfigDict, Field, ValidationError
9
10
 
10
11
 
11
12
  SETTINGS_ENV = "AGENTENV_COMPUTER_SETTINGS"
13
+ #: The prefix a deploy gives the names of the computer's tools, so several computers serve them side by side.
14
+ TOOL_PREFIX_ENV = "AGENTENV_TOOL_PREFIX"
15
+ _TOOL_PREFIX = re.compile(r"[A-Za-z0-9_-]*")
16
+ #: The longest a tool prefix may be, its separator included, so a prefixed name stays well within the 64 characters
17
+ #: model APIs take for a tool's name.
18
+ MAX_TOOL_PREFIX = 16
19
+
20
+
21
+ def tool_prefix_problem(prefix: str) -> str | None:
22
+ """What's wrong with ``prefix`` as a tool prefix, or None: only what a tool's name may hold, at most
23
+ ``MAX_TOOL_PREFIX`` characters."""
24
+ if not _TOOL_PREFIX.fullmatch(prefix):
25
+ return f"tool prefix {prefix!r} holds characters a tool's name can't"
26
+ if len(prefix) > MAX_TOOL_PREFIX:
27
+ return f"tool prefix {prefix!r} is {len(prefix)} characters, and a tool prefix is at most {MAX_TOOL_PREFIX}"
28
+ return None
12
29
 
13
30
  Space = Literal["frame", "norm"]
14
31
 
@@ -61,11 +61,14 @@ async def skill(driver: ComputerDriverEnvironment) -> tuple[str, str, dict[str,
61
61
  if notes := driver.notes.strip():
62
62
  sections += ["## Notes", notes]
63
63
  body = "\n\n".join(sections) + "\n"
64
+ prefix = driver._computer_prefix
65
+ lead = f"{rule}\n\n" if (rule := prefix.rule(driver._computer_other_tools())) else ""
64
66
  if bundled is None:
65
- return description, body, {}
67
+ return prefix.text(description), prefix.text(lead + body), {}
66
68
  own_description, own_body, files = bundled
67
- return (f"{own_description} This computer: {system}, {display.width}x{display.height}, and its computer_* tools.",
68
- own_body.rstrip() + "\n\n" + body, files)
69
+ return (prefix.text(f"{own_description} This computer: {system}, {display.width}x{display.height}, and its "
70
+ "computer_* tools."),
71
+ prefix.text(lead + own_body.rstrip() + "\n\n" + body), files)
69
72
 
70
73
 
71
74
  def _input(driver: ComputerDriverEnvironment) -> str:
@@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Annotated, Any, Callable, Literal, Optional
9
9
  from mcp.types import CallToolResult
10
10
  from pydantic import Field
11
11
 
12
- from ..agent_env_environment import tool
12
+ from ..agent_env_environment import _TOOL_ATTR, tool
13
13
  from ..tool_context import ToolContext
14
14
  from .errors import InvalidArgument, Unsupported
15
15
  from .frames import Frame
@@ -201,7 +201,8 @@ class Tools:
201
201
  raise InvalidArgument(f"text is at most {self.max_text_length} characters on this computer, got {len(text)}")
202
202
  control = sorted({f"U+{ord(c):04X}" for c in text if c not in "\n\t" and unicodedata.category(c) == "Cc"})
203
203
  if control:
204
- raise InvalidArgument(f"text has control characters {control}; press keys with computer_key")
204
+ raise InvalidArgument(f"text has control characters {control}; press keys with "
205
+ f"{self._computer_prefix.served('computer_key')}")
205
206
  return await computer.act(ctx, "type_text", (text,), frame=computer.frames.latest(self.caller_identity(ctx)),
206
207
  screenshot=screenshot, summary=f"Typed {len(text)} characters")
207
208
 
@@ -509,3 +510,7 @@ def _check_range(name: str, value: int, low: int, high: int) -> None:
509
510
  def _display(frame: Frame) -> str:
510
511
  d = frame.display
511
512
  return f"{d.width}×{d.height}" + (f" at scale {d.scale:g}" if d.scale != 1 else "")
513
+
514
+
515
+ #: The tools the SDK serves on every computer, by their own names.
516
+ COMPUTER_TOOLS: tuple[str, ...] = tuple(sorted(name for name, member in vars(Tools).items() if hasattr(member, _TOOL_ATTR)))
@@ -0,0 +1,153 @@
1
+ """A computer deployed with a tool prefix serves every tool under it, and names its tools that way wherever it names
2
+ them: the card, tools/list, its skill, its extensions' descriptions and its errors. Without one, nothing changes."""
3
+ from __future__ import annotations
4
+
5
+ import re
6
+
7
+ import pytest
8
+
9
+ pytest.importorskip("mcp.server.fastmcp")
10
+ pytest.importorskip("PIL")
11
+
12
+ from computer_fake import LINUX, FakeDriver, http_client, mcp_session # noqa: E402
13
+ from mcp.types import CallToolResult, TextContent # noqa: E402
14
+
15
+ from agentenv_protocol import EnvironmentTool, ForwardedTool, server_instructions # noqa: E402
16
+ from agentenv_protocol.computer import TOOL_PREFIX_ENV # noqa: E402
17
+
18
+ PREFIX = "desktop_"
19
+ UNPREFIXED = re.compile(r"(?<![\w-])computer_")
20
+
21
+
22
+ async def _names(driver) -> tuple[set, set]:
23
+ async with http_client(driver) as http, mcp_session(driver) as session:
24
+ card = (await http.get("/.well-known/agent-env.json")).json()
25
+ listed = {tool.name for tool in (await session.list_tools()).tools}
26
+ return {tool["name"] for tool in card["capabilities"]["tools"]}, listed
27
+
28
+
29
+ @pytest.mark.asyncio
30
+ async def test_a_deploys_tool_prefix_names_every_tool_on_the_card_and_in_tools_list(monkeypatch):
31
+ own_card, own_listed = await _names(FakeDriver())
32
+ monkeypatch.setenv(TOOL_PREFIX_ENV, PREFIX)
33
+ driver = FakeDriver()
34
+ card, listed = await _names(driver)
35
+ assert card == listed == {PREFIX + name for name in own_card} and own_card == own_listed
36
+ async with mcp_session(driver) as session:
37
+ shot = await session.call_tool(f"{PREFIX}computer_screenshot", {})
38
+ unknown = await session.call_tool("computer_screenshot", {})
39
+ assert not shot.isError and any(part.type == "image" for part in shot.content)
40
+ assert unknown.isError
41
+
42
+
43
+ def test_the_declaration_a_driver_env_stores_names_the_tools_without_the_deploys_prefix(monkeypatch):
44
+ monkeypatch.setenv(TOOL_PREFIX_ENV, PREFIX)
45
+ names = {tool.name for tool in FakeDriver.declaration(LINUX).capabilities.tools}
46
+ assert "computer_screenshot" in names and not any(name.startswith(PREFIX) for name in names)
47
+
48
+
49
+ @pytest.mark.asyncio
50
+ async def test_its_skill_its_extensions_and_its_notes_name_the_tools_as_it_serves_them(monkeypatch):
51
+ monkeypatch.setenv(TOOL_PREFIX_ENV, PREFIX)
52
+ monkeypatch.setattr(FakeDriver, "notes", "Take a computer_screenshot first; desktop_computer_click is already right.")
53
+ async with http_client(FakeDriver()) as http:
54
+ skill = (await http.get("/agentenv/ext/skill/get", params={"name": "desktop"})).json()
55
+ card = (await http.get("/.well-known/agent-env.json")).json()
56
+ text = skill["description"] + skill["skill_md"]
57
+ assert f"`{PREFIX}computer_screenshot`" in text and f"`{PREFIX}computer_*`" in text and f"`{PREFIX}computer_click`" in text
58
+ assert "Take a desktop_computer_screenshot first; desktop_computer_click is already right." in text
59
+ assert not UNPREFIXED.search(text) and "desktop_desktop_" not in text and "serves its tools under" not in text
60
+ descriptions = " ".join(e["description"] for e in card["capabilities"]["extensions"] if e.get("description"))
61
+ assert f"The seven {PREFIX}computer_* tools" in descriptions and not UNPREFIXED.search(descriptions)
62
+ tools = " ".join(t.get("description") or "" for t in card["capabilities"]["tools"])
63
+ assert not UNPREFIXED.search(tools)
64
+
65
+
66
+ @pytest.mark.asyncio
67
+ async def test_an_error_names_the_tool_to_use_instead_as_it_is_served(monkeypatch):
68
+ monkeypatch.setenv(TOOL_PREFIX_ENV, PREFIX)
69
+ async with mcp_session(FakeDriver()) as session:
70
+ refused = await session.call_tool(f"{PREFIX}computer_type", {"text": "a\x07b"})
71
+ assert refused.isError and f"press keys with {PREFIX}computer_key" in refused.content[0].text
72
+
73
+
74
+ @pytest.mark.asyncio
75
+ async def test_tool_access_rules_name_the_tools_as_it_serves_them(monkeypatch):
76
+ monkeypatch.setenv(TOOL_PREFIX_ENV, PREFIX)
77
+ driver = FakeDriver()
78
+ async with http_client(driver) as http, mcp_session(driver) as session:
79
+ disabled = await http.post("/tools/disable", json={"role": "default", "tools": [f"{PREFIX}computer_click"]})
80
+ listed = {tool.name for tool in (await session.list_tools()).tools}
81
+ refused = await session.call_tool(f"{PREFIX}computer_click", {"x": 1, "y": 1})
82
+ assert disabled.status_code == 200
83
+ assert f"{PREFIX}computer_click" not in listed and f"{PREFIX}computer_screenshot" in listed
84
+ assert refused.isError and "is disabled for role 'default'" in refused.content[0].text
85
+
86
+
87
+ @pytest.mark.asyncio
88
+ async def test_setting_the_default_space_rewrites_the_prefixed_point_tools(monkeypatch):
89
+ monkeypatch.setenv(TOOL_PREFIX_ENV, PREFIX)
90
+ driver = FakeDriver()
91
+ async with http_client(driver) as http, mcp_session(driver) as session:
92
+ await http.post("/agentenv/ext/computer-spaces/set", json={"default_space": "norm", "default_norm_scale": 1000})
93
+ after = {t.name: t.description for t in (await session.list_tools()).tools}
94
+ assert "norm space by default: 0 to 1000" in after[f"{PREFIX}computer_click"]
95
+
96
+
97
+ @pytest.mark.parametrize("prefix, problem", [
98
+ ("desk top_", "holds characters a tool's name can't"),
99
+ ("desktop/", "holds characters a tool's name can't"),
100
+ ("décor_", "holds characters a tool's name can't"),
101
+ ("accounting-desk1_", "is 17 characters, and a tool prefix is at most 16"),
102
+ ])
103
+ def test_a_prefix_a_tool_name_cant_hold_or_that_is_too_long_is_refused(monkeypatch, prefix, problem):
104
+ monkeypatch.setenv(TOOL_PREFIX_ENV, prefix)
105
+ with pytest.raises(ValueError, match=problem):
106
+ FakeDriver()
107
+
108
+
109
+ @pytest.mark.asyncio
110
+ async def test_a_prefix_of_sixteen_characters_is_taken(monkeypatch):
111
+ monkeypatch.setenv(TOOL_PREFIX_ENV, "accounting-desk_")
112
+ card, _listed = await _names(FakeDriver())
113
+ assert "accounting-desk_computer_pointer_position" in card
114
+
115
+
116
+ class Forwarding(FakeDriver):
117
+ """A driver forwarding another program's batch tool, whose steps name that program's own actions."""
118
+
119
+ def forwarded_tools(self):
120
+ schema = {"type": "object", "properties": {"tool": {"type": "string", "enum": ["click", "double_click"]}}}
121
+ return [ForwardedTool(EnvironmentTool(name="run_steps", description="Run click or double_click in one call.",
122
+ inputSchema=schema), self.steps)]
123
+
124
+ async def steps(self, tool):
125
+ return CallToolResult(content=[TextContent(type="text", text=f"ran {tool}")])
126
+
127
+ @server_instructions
128
+ def instructions(self):
129
+ return "Act through run_steps, and take a computer_screenshot first."
130
+
131
+
132
+ @pytest.mark.asyncio
133
+ async def test_a_forwarded_tool_keeps_its_own_text_and_the_skill_and_instructions_lead_with_the_prefix_rule(monkeypatch):
134
+ monkeypatch.setenv(TOOL_PREFIX_ENV, PREFIX)
135
+ driver = Forwarding()
136
+ instructions = driver.create_app().instructions
137
+ async with http_client(driver) as http, mcp_session(driver) as session:
138
+ tools = {tool.name: tool for tool in (await session.list_tools()).tools}
139
+ ran = await session.call_tool(f"{PREFIX}run_steps", {"tool": "click"})
140
+ skill = (await http.get("/agentenv/ext/skill/get", params={"name": "desktop"})).json()
141
+ forwarded = tools[f"{PREFIX}run_steps"]
142
+ assert forwarded.description == "Run click or double_click in one call."
143
+ assert forwarded.inputSchema["properties"]["tool"]["enum"] == ["click", "double_click"]
144
+ assert not ran.isError and ran.content[0].text == "ran click"
145
+ rule = (f"This computer serves its tools under `{PREFIX}`: a tool this text names without `{PREFIX}` is served with it, "
146
+ f"so `run_steps` is `{PREFIX}run_steps`. Names inside a tool's arguments stay as written.")
147
+ assert skill["skill_md"].partition("---\n\n")[2].startswith(rule)
148
+ assert instructions == f"{rule}\n\nAct through run_steps, and take a {PREFIX}computer_screenshot first."
149
+
150
+
151
+ def test_without_a_prefix_a_forwarding_drivers_instructions_are_its_own(monkeypatch):
152
+ monkeypatch.delenv(TOOL_PREFIX_ENV, raising=False)
153
+ assert Forwarding().create_app().instructions == "Act through run_steps, and take a computer_screenshot first."