agentenv-framework-protocol 0.1.506__tar.gz → 0.1.508__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 (178) hide show
  1. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/PKG-INFO +47 -1
  2. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/README.md +44 -0
  3. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/pyproject.toml +5 -1
  4. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/__init__.py +4 -2
  5. agentenv_framework_protocol-0.1.508/src/agentenv_protocol/computer/_desktop.py +35 -0
  6. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/_macos.py +3 -6
  7. agentenv_framework_protocol-0.1.508/src/agentenv_protocol/computer/_windows.py +316 -0
  8. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/_x11.py +7 -28
  9. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website/__init__.py +6 -2
  10. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website/environment.py +69 -11
  11. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website/readiness.py +3 -0
  12. agentenv_framework_protocol-0.1.508/tests/test_computer_windows_driver.py +267 -0
  13. agentenv_framework_protocol-0.1.508/tests/test_website_live.py +101 -0
  14. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/.gitignore +0 -0
  15. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/LICENSE +0 -0
  16. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/NOTICE +0 -0
  17. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/THIRD_PARTY_NOTICES.md +0 -0
  18. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/__init__.py +0 -0
  19. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/advanced_agent.py +0 -0
  20. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/basic_agent.py +0 -0
  21. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/custom_extensions_agent.py +0 -0
  22. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/multimodal_agent.py +0 -0
  23. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/reference_model.py +0 -0
  24. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/streaming_agent.py +0 -0
  25. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/.gitignore +0 -0
  26. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/Dockerfile +0 -0
  27. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/Dockerfile.dockerignore +0 -0
  28. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/README.md +0 -0
  29. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/data/seed.json +0 -0
  30. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/env.toml +0 -0
  31. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/site.py +0 -0
  32. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/web/index.html +0 -0
  33. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/web/package-lock.json +0 -0
  34. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/web/package.json +0 -0
  35. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/web/src/main.js +0 -0
  36. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/web/src/style.css +0 -0
  37. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/examples/website/notes/web/vite.config.js +0 -0
  38. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/__init__.py +0 -0
  39. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/a2a_agent/__init__.py +0 -0
  40. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/a2a_agent/_trajectory_log.py +0 -0
  41. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
  42. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/a2a_agent/extensions.py +0 -0
  43. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
  44. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/a2a_agent/peer_agents.py +0 -0
  45. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/a2a_agent/proxy.py +0 -0
  46. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
  47. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/a2a_agent/staging.py +0 -0
  48. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
  49. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
  50. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/agent_env_environment.py +0 -0
  51. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/browser_cdp.py +0 -0
  52. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/client.py +0 -0
  53. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/_declare.py +0 -0
  54. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/_job.py +0 -0
  55. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/_linux.py +0 -0
  56. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/_load.py +0 -0
  57. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/_reaping.py +0 -0
  58. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/_session_posix.py +0 -0
  59. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/_session_windows.py +0 -0
  60. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/_with_fds.py +0 -0
  61. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/app.py +0 -0
  62. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/app_data.py +0 -0
  63. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/app_runtime.py +0 -0
  64. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/apps.py +0 -0
  65. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/card.py +0 -0
  66. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/checks.py +0 -0
  67. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/conformance/__init__.py +0 -0
  68. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/conformance/__main__.py +0 -0
  69. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/conformance/checks.py +0 -0
  70. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/conformance/client.py +0 -0
  71. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/conformance/desk.py +0 -0
  72. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/conformance/fixture.py +0 -0
  73. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/conformance/mp4.py +0 -0
  74. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/conformance/page_fixture.py +0 -0
  75. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/conformance/report.py +0 -0
  76. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/conformance/wayland_fixture.py +0 -0
  77. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/dataplane.py +0 -0
  78. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/driver.py +0 -0
  79. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/errors.py +0 -0
  80. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/extensions.py +0 -0
  81. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/frames.py +0 -0
  82. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/keys.py +0 -0
  83. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/launch.py +0 -0
  84. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/manifest.py +0 -0
  85. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/operations.py +0 -0
  86. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/platform.py +0 -0
  87. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/prefix.py +0 -0
  88. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/queue.py +0 -0
  89. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/recording.py +0 -0
  90. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/results.py +0 -0
  91. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/run_install_step.py +0 -0
  92. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/runtime.py +0 -0
  93. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/seed.py +0 -0
  94. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/serve.py +0 -0
  95. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/serve_app.py +0 -0
  96. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/served.py +0 -0
  97. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/session.py +0 -0
  98. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/settings.py +0 -0
  99. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/skill.py +0 -0
  100. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/spaces.py +0 -0
  101. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/svc.py +0 -0
  102. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/computer/tools.py +0 -0
  103. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/errors.py +0 -0
  104. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/manifest.py +0 -0
  105. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/preflight.py +0 -0
  106. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/tool_access.py +0 -0
  107. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/tool_client.py +0 -0
  108. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/tool_context.py +0 -0
  109. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/transfers.py +0 -0
  110. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/types.py +0 -0
  111. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website/clock.py +0 -0
  112. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website/conformance.py +0 -0
  113. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website/errors.py +0 -0
  114. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website/routes.py +0 -0
  115. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website/session.py +0 -0
  116. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website_browser/__init__.py +0 -0
  117. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website_browser/control.py +0 -0
  118. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website_browser/data.py +0 -0
  119. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website_browser/devtools.py +0 -0
  120. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website_browser/environment.py +0 -0
  121. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/src/agentenv_protocol/website_browser/shapes.py +0 -0
  122. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/computer_fake.py +0 -0
  123. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/probe_fake.py +0 -0
  124. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_a2a_agent.py +0 -0
  125. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_a2a_agent_examples.py +0 -0
  126. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_a2a_cdp_config.py +0 -0
  127. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_a2a_object_transfers.py +0 -0
  128. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_a2a_peer_agents.py +0 -0
  129. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_a2a_proxy.py +0 -0
  130. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_a2a_staging.py +0 -0
  131. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_a2a_trajectory_log.py +0 -0
  132. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_agent_env_environment.py +0 -0
  133. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_browser_cdp.py +0 -0
  134. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_browser_data.py +0 -0
  135. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_browser_state.py +0 -0
  136. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_client.py +0 -0
  137. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_app_data.py +0 -0
  138. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_app_server.py +0 -0
  139. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_conformance.py +0 -0
  140. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_conformance_checks.py +0 -0
  141. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_conformance_page.py +0 -0
  142. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_conformance_recording.py +0 -0
  143. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_dataplane.py +0 -0
  144. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_declare.py +0 -0
  145. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_driver.py +0 -0
  146. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_extensions.py +0 -0
  147. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_frames.py +0 -0
  148. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_macos.py +0 -0
  149. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_page.py +0 -0
  150. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_queue.py +0 -0
  151. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_recording.py +0 -0
  152. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_seed.py +0 -0
  153. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_serve.py +0 -0
  154. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_served.py +0 -0
  155. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_session_windows.py +0 -0
  156. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_spaces.py +0 -0
  157. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_svc_forward.py +0 -0
  158. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_computer_tool_prefix.py +0 -0
  159. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_control_token.py +0 -0
  160. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_data_objects.py +0 -0
  161. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_errors.py +0 -0
  162. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_extension_methods.py +0 -0
  163. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_intake.py +0 -0
  164. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_manifest.py +0 -0
  165. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_preflight.py +0 -0
  166. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_tool_access.py +0 -0
  167. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_tool_annotations.py +0 -0
  168. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_tool_client.py +0 -0
  169. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_tool_context.py +0 -0
  170. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_tool_context_fastmcp.py +0 -0
  171. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_tool_context_injection.py +0 -0
  172. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_website_api.py +0 -0
  173. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_website_browser.py +0 -0
  174. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_website_conformance.py +0 -0
  175. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_website_environment.py +0 -0
  176. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_website_mcp.py +0 -0
  177. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/tests/test_website_notes_example.py +0 -0
  178. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.508}/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.506
3
+ Version: 0.1.508
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
@@ -58,6 +58,8 @@ Requires-Dist: websockets>=13; extra == 'dev'
58
58
  Provides-Extra: macos
59
59
  Requires-Dist: pyobjc-framework-quartz>=11; (sys_platform == 'darwin') and extra == 'macos'
60
60
  Requires-Dist: pyobjc-framework-screencapturekit>=11; (sys_platform == 'darwin') and extra == 'macos'
61
+ Provides-Extra: windows
62
+ Requires-Dist: mss<11,>=9; extra == 'windows'
61
63
  Provides-Extra: x11
62
64
  Requires-Dist: mss<11,>=9; extra == 'x11'
63
65
  Requires-Dist: python-xlib<0.34,>=0.33; extra == 'x11'
@@ -555,6 +557,50 @@ called with the caller's role in `ToolContext`. The site's `/mcp` is one of the
555
557
  browsing route (`/website/<name>/`) never reaches. `python -m agentenv_protocol.website.conformance` checks a declared
556
558
  endpoint (`mcp`): it answers `initialize` and lists the tools the card does.
557
559
 
560
+ ## Live websites
561
+
562
+ A live site is a real one, such as github.com, that a task puts in a multi env beside synthetic sites. Its server serves
563
+ none of its pages: the real site does, and the computer reaches it over its own network. The server holds what agentenv
564
+ knows of the site: its card, a data plane that seeds and reads the task's account through the real site's API, `ready`
565
+ saying the account works, and `session`, the account's cookies and local storage, which `browser_sign_in` loads into the
566
+ browser.
567
+
568
+ ```python
569
+ from pydantic import BaseModel, SecretStr
570
+ from agentenv_protocol.website import WebsiteEnvironment, ready_check, session_state, website
571
+
572
+
573
+ @website(domains=["github.com"], live=True, allow_hosts=["*.githubassets.com", "api.github.com"])
574
+ class GitHub(WebsiteEnvironment):
575
+ class Settings(BaseModel):
576
+ token: SecretStr # in env.toml as a secret:KEY or env:NAME reference
577
+
578
+ @ready_check
579
+ def account_works(self) -> bool:
580
+ ... # the API answers for self.settings.token
581
+
582
+ @session_state
583
+ def account(self, user: str | None) -> dict:
584
+ ... # {"cookies": [...], "local_storage": [...]} that sign a browser in
585
+ ```
586
+
587
+ - **Its names:** `domains` are the real site's, where start tabs may open and `browser_sign_in` sets the session's
588
+ cookies. `allow_hosts` are the other hosts its pages reach, such as its assets and API. A computer whose network is an
589
+ allowlist may reach both; one that allows every host is left as it is. A name a synthetic site of the run also claims
590
+ is that site's.
591
+ - **Its card:** `website/v1` says `live` (`WEBSITE_LIVE_PARAM`) and `allow_hosts` (`WEBSITE_ALLOW_HOSTS_PARAM`) beside
592
+ `domains`, and names no routes. The server serves no `/api/session` page route. `@website` refuses a live site
593
+ with no domains or a static folder, and `allow_hosts` on a site that isn't live.
594
+ - **Its session:** the method marked `@session_state` answers `session {user}`. A live site must mark one, since only
595
+ the real site's cookies sign a browser in to it. Each cookie carries no domain and a `max_age`, as a synthetic
596
+ site's does.
597
+ - **Settings:** any site may declare `Settings`. A deploy hands them to its server in `WEBSITE_SETTINGS_ENV`, a JSON
598
+ object, and the site reads them as `self.settings`, unless it has a member of that name of its own. A `SecretStr`
599
+ field's value in env.toml is a `secret:` or `env:` reference, which agentenv resolves only as it writes the server's
600
+ container, and every record shows it as withheld.
601
+ - **The run:** a run that deploys a live site is marked live. The deploy record says `live`, and the run's metadata lists
602
+ its live sites with `reproducible: false`, because the real site may change between runs.
603
+
558
604
  ## Computer drivers
559
605
 
560
606
  A computer driver is what runs inside a computer an agent uses through screenshots and input. Write
@@ -490,6 +490,50 @@ called with the caller's role in `ToolContext`. The site's `/mcp` is one of the
490
490
  browsing route (`/website/<name>/`) never reaches. `python -m agentenv_protocol.website.conformance` checks a declared
491
491
  endpoint (`mcp`): it answers `initialize` and lists the tools the card does.
492
492
 
493
+ ## Live websites
494
+
495
+ A live site is a real one, such as github.com, that a task puts in a multi env beside synthetic sites. Its server serves
496
+ none of its pages: the real site does, and the computer reaches it over its own network. The server holds what agentenv
497
+ knows of the site: its card, a data plane that seeds and reads the task's account through the real site's API, `ready`
498
+ saying the account works, and `session`, the account's cookies and local storage, which `browser_sign_in` loads into the
499
+ browser.
500
+
501
+ ```python
502
+ from pydantic import BaseModel, SecretStr
503
+ from agentenv_protocol.website import WebsiteEnvironment, ready_check, session_state, website
504
+
505
+
506
+ @website(domains=["github.com"], live=True, allow_hosts=["*.githubassets.com", "api.github.com"])
507
+ class GitHub(WebsiteEnvironment):
508
+ class Settings(BaseModel):
509
+ token: SecretStr # in env.toml as a secret:KEY or env:NAME reference
510
+
511
+ @ready_check
512
+ def account_works(self) -> bool:
513
+ ... # the API answers for self.settings.token
514
+
515
+ @session_state
516
+ def account(self, user: str | None) -> dict:
517
+ ... # {"cookies": [...], "local_storage": [...]} that sign a browser in
518
+ ```
519
+
520
+ - **Its names:** `domains` are the real site's, where start tabs may open and `browser_sign_in` sets the session's
521
+ cookies. `allow_hosts` are the other hosts its pages reach, such as its assets and API. A computer whose network is an
522
+ allowlist may reach both; one that allows every host is left as it is. A name a synthetic site of the run also claims
523
+ is that site's.
524
+ - **Its card:** `website/v1` says `live` (`WEBSITE_LIVE_PARAM`) and `allow_hosts` (`WEBSITE_ALLOW_HOSTS_PARAM`) beside
525
+ `domains`, and names no routes. The server serves no `/api/session` page route. `@website` refuses a live site
526
+ with no domains or a static folder, and `allow_hosts` on a site that isn't live.
527
+ - **Its session:** the method marked `@session_state` answers `session {user}`. A live site must mark one, since only
528
+ the real site's cookies sign a browser in to it. Each cookie carries no domain and a `max_age`, as a synthetic
529
+ site's does.
530
+ - **Settings:** any site may declare `Settings`. A deploy hands them to its server in `WEBSITE_SETTINGS_ENV`, a JSON
531
+ object, and the site reads them as `self.settings`, unless it has a member of that name of its own. A `SecretStr`
532
+ field's value in env.toml is a `secret:` or `env:` reference, which agentenv resolves only as it writes the server's
533
+ container, and every record shows it as withheld.
534
+ - **The run:** a run that deploys a live site is marked live. The deploy record says `live`, and the run's metadata lists
535
+ its live sites with `reproducible: false`, because the real site may change between runs.
536
+
493
537
  ## Computer drivers
494
538
 
495
539
  A computer driver is what runs inside a computer an agent uses through screenshots and input. Write
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentenv-framework-protocol"
3
- version = "0.1.506"
3
+ version = "0.1.508"
4
4
  description = "Open data-plane protocol and server SDK for agent environments."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -61,6 +61,10 @@ x11 = [
61
61
  "mss>=9,<11",
62
62
  "python-xlib>=0.33,<0.34",
63
63
  ]
64
+ # A Windows driver on WindowsDriver: mss captures the desktop and SendInput, through ctypes, sends its input.
65
+ windows = [
66
+ "mss>=9,<11",
67
+ ]
64
68
  dev = [
65
69
  "a2a-sdk[http-server]>=0.3.26,<0.4",
66
70
  "python-multipart>=0.0.13",
@@ -100,11 +100,12 @@ if TYPE_CHECKING:
100
100
  )
101
101
  from .driver import DEFAULT_IDENTITY, ComputerDriverEnvironment, DriverDeclaration, computer_driver
102
102
  from ._macos import MacOSDriver
103
+ from ._windows import WindowsDriver
103
104
  from ._x11 import X11Driver
104
105
  from .recording import RecordedFrame, RecordedSegment, RecordHints, Recording, frames_in_thread
105
106
 
106
107
  # The names whose modules need the computer extra (mcp, av and Pillow), by module; MacOSDriver's, the macos extra too,
107
- # on first use of the Mac, and X11Driver's, the x11 extra.
108
+ # on first use of the Mac, X11Driver's, the x11 extra, and WindowsDriver's, the windows extra.
108
109
  _ON_USE = {
109
110
  "driver": ("ComputerDriverEnvironment", "computer_driver", "DEFAULT_IDENTITY", "DriverDeclaration"),
110
111
  "card": ("COMPUTER", "COMPUTER_READY", "COMPUTER_DISPLAY", "COMPUTER_SPACES", "COMPUTER_POINTER",
@@ -112,6 +113,7 @@ _ON_USE = {
112
113
  "recording": ("Recording", "RecordHints", "RecordedFrame", "RecordedSegment", "frames_in_thread"),
113
114
  "_macos": ("MacOSDriver",),
114
115
  "_x11": ("X11Driver",),
116
+ "_windows": ("WindowsDriver",),
115
117
  }
116
118
 
117
119
 
@@ -124,7 +126,7 @@ def __getattr__(name: str) -> Any:
124
126
 
125
127
  __all__ = [
126
128
  "ComputerDriverEnvironment", "computer_driver", "DEFAULT_IDENTITY", "DriverDeclaration", "MacOSDriver",
127
- "X11Driver",
129
+ "X11Driver", "WindowsDriver",
128
130
  "ComputerAppEnvironment", "computer_app", "install", "linux", "windows", "install_step", "ready_check", "Linux",
129
131
  "Windows", "Service",
130
132
  "Launch", "Ready", "Packages", "AptSource", "Download", "RunScript",
@@ -0,0 +1,35 @@
1
+ """What the drivers on an OS's own input share: the pacing of clicks and drags, how many presses and points they take,
2
+ a drag's path, and a capture of every monitor through mss, whose class each driver passes from its own import."""
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from typing import Any
7
+
8
+ from .errors import DeviceUnavailable
9
+ from .operations import Image
10
+
11
+ MAX_CLICK_COUNT = 3
12
+ MAX_DRAG_POINTS = 50
13
+ #: The pause between the presses of a double or triple click, in seconds.
14
+ CLICK_GAP_S = 0.04
15
+ #: How far apart a drag's pointer moves are, in pixels.
16
+ DRAG_STEP_PX = 20
17
+
18
+
19
+ def drag_steps(path: list) -> list[tuple[float, float]]:
20
+ """The points a drag moves through after its start, about every DRAG_STEP_PX pixels."""
21
+ steps = []
22
+ for (x0, y0), (x1, y1) in zip(path, path[1:]):
23
+ n = max(2, int(math.hypot(x1 - x0, y1 - y0) / DRAG_STEP_PX))
24
+ steps += [(x0 + (x1 - x0) * k / n, y0 + (y1 - y0) * k / n) for k in range(1, n + 1)]
25
+ return steps
26
+
27
+
28
+ def mss_capture(mss_class: Any) -> Image:
29
+ """Every monitor, as mss's first monitor spans them, in BGRA."""
30
+ try:
31
+ with mss_class() as m:
32
+ shot = m.grab(m.monitors[0])
33
+ return Image(shot.width, shot.height, bytes(shot.bgra), "BGRA")
34
+ except Exception as e:
35
+ raise DeviceUnavailable(f"capture failed: {type(e).__name__}: {e}") from None
@@ -14,6 +14,7 @@ import time
14
14
  from contextlib import contextmanager
15
15
  from typing import Any, Callable, Iterator
16
16
 
17
+ from ._desktop import CLICK_GAP_S, DRAG_STEP_PX, MAX_CLICK_COUNT, MAX_DRAG_POINTS
17
18
  from .driver import ComputerDriverEnvironment
18
19
  from .errors import DeliveryFailed, DeviceUnavailable, Unsupported
19
20
  from .keys import PRINTABLE_KEYS
@@ -59,10 +60,6 @@ MOVED = 5
59
60
  SETTLE_S = 0.2
60
61
  #: How long a click holds its button down, as a hand does: a press released at once is lost now and then.
61
62
  CLICK_HOLD_S = 0.03
62
- #: The pause between the presses of a double or triple click.
63
- CLICK_GAP_S = 0.04
64
- #: How far apart a drag's pointer moves are, in pixels.
65
- DRAG_STEP_PX = 20
66
63
  #: The pause between a scroll's wheel notches, as a wheel turned by hand has: notches posted back to back reach an app
67
64
  #: as fewer.
68
65
  SCROLL_GAP_S = 0.05
@@ -81,8 +78,8 @@ class MacOSDriver(ComputerDriverEnvironment):
81
78
  released."""
82
79
 
83
80
  buttons = ("left", "right", "middle")
84
- max_click_count = 3
85
- max_drag_points = 50
81
+ max_click_count = MAX_CLICK_COUNT
82
+ max_drag_points = MAX_DRAG_POINTS
86
83
  keys = (*MODIFIERS, *NAMED, *PRINTABLE_KEYS)
87
84
 
88
85
  def __init__(self, settings: ComputerSettings | None = None, platform: Platform | None = None) -> None:
@@ -0,0 +1,316 @@
1
+ """computer/v1 on a Windows desktop, for a driver whose software serves tools of its own, as agent-desktop's does: mss
2
+ captures the screen and SendInput sends the input, in the session the driver runs in. The process is aware of each
3
+ monitor's DPI, so a point is a pixel of the capture, which spans every monitor. A recording polls those captures, as
4
+ the base class does for a driver with no stream of its own. A driver subclasses ``WindowsDriver``, declares itself with
5
+ ``computer_driver``, and serves its own tools beside these. Needs the ``windows`` extra: mss."""
6
+ from __future__ import annotations
7
+
8
+ import asyncio
9
+ import contextlib
10
+ import ctypes
11
+ import functools
12
+ import time
13
+
14
+ import mss
15
+
16
+ from ._desktop import CLICK_GAP_S, MAX_CLICK_COUNT, MAX_DRAG_POINTS, drag_steps, mss_capture
17
+ from .driver import ComputerDriverEnvironment
18
+ from .errors import DeliveryFailed, DeviceUnavailable, Unsupported
19
+ from .operations import (Display, Image, ReadyCheck, capture, click, display, drag, key_down, key_up, pointer_down,
20
+ pointer_move, pointer_position, pointer_up, press, scroll, type_text)
21
+ from .platform import Platform
22
+ from .settings import ComputerSettings
23
+
24
+ INPUT_MOUSE, INPUT_KEYBOARD = 0, 1
25
+ MOVE, ABSOLUTE, VIRTUAL_DESK = 0x0001, 0x8000, 0x4000
26
+ WHEEL, HWHEEL, WHEEL_DELTA = 0x0800, 0x1000, 120
27
+ #: Each button's press and release flags, and the X button it is, for the two that are one.
28
+ BUTTONS = {"left": (0x0002, 0x0004, 0), "right": (0x0008, 0x0010, 0), "middle": (0x0020, 0x0040, 0),
29
+ "back": (0x0080, 0x0100, 1), "forward": (0x0080, 0x0100, 2)}
30
+ EXTENDED, KEY_UP, UNICODE = 0x0001, 0x0002, 0x0004
31
+ #: W3C key values and their virtual keys; any other single character is the key its keyboard layout gives it.
32
+ VIRTUAL_KEYS = {
33
+ "Enter": 0x0D, "Tab": 0x09, "Escape": 0x1B, "Backspace": 0x08, "Delete": 0x2E, "ArrowUp": 0x26, "ArrowDown": 0x28,
34
+ "ArrowLeft": 0x25, "ArrowRight": 0x27, "Home": 0x24, "End": 0x23, "PageUp": 0x21, "PageDown": 0x22, "Insert": 0x2D,
35
+ "Control": 0xA2, "Shift": 0xA0, "Alt": 0xA4, "Meta": 0x5B, "Super": 0x5B, "CapsLock": 0x14, "ContextMenu": 0x5D,
36
+ "PrintScreen": 0x2C, "Pause": 0x13, " ": 0x20, **{f"F{n}": 0x6F + n for n in range(1, 25)},
37
+ }
38
+ #: The keys a keyboard sends with the extended flag, without which Windows reads the arrows as the keypad's.
39
+ EXTENDED_KEYS = {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x2C, 0x2D, 0x2E, 0x5B, 0x5D}
40
+ #: The modifiers VkKeyScanW says a character needs, as the bits of its high byte.
41
+ SHIFT_STATE = ((1, 0xA0), (2, 0xA2), (4, 0xA4))
42
+ TYPED = {"\n": 0x0D, "\t": 0x09}
43
+ #: SetProcessDpiAwarenessContext's per-monitor (v2) awareness.
44
+ PER_MONITOR_AWARE_V2 = -4
45
+ #: GetSystemMetrics' indexes of the virtual screen's left, top, width and height.
46
+ VIRTUAL_SCREEN = (76, 77, 78, 79)
47
+ DESKTOP_SWITCHDESKTOP = 0x0100
48
+
49
+
50
+ class _MouseInput(ctypes.Structure):
51
+ _fields_ = [("dx", ctypes.c_long), ("dy", ctypes.c_long), ("mouseData", ctypes.c_ulong),
52
+ ("dwFlags", ctypes.c_ulong), ("time", ctypes.c_ulong), ("dwExtraInfo", ctypes.c_size_t)]
53
+
54
+
55
+ class _KeybdInput(ctypes.Structure):
56
+ _fields_ = [("wVk", ctypes.c_ushort), ("wScan", ctypes.c_ushort), ("dwFlags", ctypes.c_ulong),
57
+ ("time", ctypes.c_ulong), ("dwExtraInfo", ctypes.c_size_t)]
58
+
59
+
60
+ class _HardwareInput(ctypes.Structure):
61
+ _fields_ = [("uMsg", ctypes.c_ulong), ("wParamL", ctypes.c_ushort), ("wParamH", ctypes.c_ushort)]
62
+
63
+
64
+ class _Event(ctypes.Union):
65
+ _fields_ = [("mi", _MouseInput), ("ki", _KeybdInput), ("hi", _HardwareInput)]
66
+
67
+
68
+ class _Input(ctypes.Structure):
69
+ _fields_ = [("type", ctypes.c_ulong), ("u", _Event)]
70
+
71
+
72
+ class _Point(ctypes.Structure):
73
+ _fields_ = [("x", ctypes.c_long), ("y", ctypes.c_long)]
74
+
75
+
76
+ @functools.cache
77
+ def _user32():
78
+ """Windows' user32, typed, with the per-monitor DPI awareness set once so that points and the capture's pixels
79
+ agree."""
80
+ user32 = ctypes.WinDLL("user32", use_last_error=True)
81
+ user32.SendInput.argtypes = (ctypes.c_uint, ctypes.POINTER(_Input), ctypes.c_int)
82
+ user32.SendInput.restype = ctypes.c_uint
83
+ user32.VkKeyScanW.argtypes = (ctypes.c_wchar,)
84
+ user32.VkKeyScanW.restype = ctypes.c_short
85
+ user32.MapVirtualKeyW.argtypes = (ctypes.c_uint, ctypes.c_uint)
86
+ user32.MapVirtualKeyW.restype = ctypes.c_uint
87
+ user32.OpenInputDesktop.argtypes = (ctypes.c_ulong, ctypes.c_bool, ctypes.c_ulong)
88
+ user32.OpenInputDesktop.restype = ctypes.c_void_p
89
+ user32.CloseDesktop.argtypes = (ctypes.c_void_p,)
90
+ user32.GetCursorPos.argtypes = (ctypes.POINTER(_Point),)
91
+ with contextlib.suppress(AttributeError):
92
+ user32.SetProcessDpiAwarenessContext(ctypes.c_void_p(PER_MONITOR_AWARE_V2))
93
+ return user32
94
+
95
+
96
+ class WindowsDriver(ComputerDriverEnvironment):
97
+ """The computer/v1 operations, the pointer and keyboard groups among them, on a Windows desktop."""
98
+
99
+ buttons = ("left", "right", "middle", "back", "forward")
100
+ max_click_count = MAX_CLICK_COUNT
101
+ max_drag_points = MAX_DRAG_POINTS
102
+
103
+ def __init__(self, settings: ComputerSettings | None = None, platform: Platform | None = None) -> None:
104
+ super().__init__(settings, platform)
105
+ #: The virtual keys each key held by @key_down pressed, which @key_up releases.
106
+ self._held: dict[str, list[int]] = {}
107
+
108
+ @display
109
+ async def screen(self) -> Display:
110
+ _, _, width, height = _virtual_screen()
111
+ return Display(width, height, 1.0)
112
+
113
+ @capture
114
+ async def grab(self) -> Image:
115
+ return await asyncio.to_thread(mss_capture, mss.MSS)
116
+
117
+ async def ready_checks(self) -> list[ReadyCheck]:
118
+ user32 = _user32()
119
+ desktop = user32.OpenInputDesktop(0, False, DESKTOP_SWITCHDESKTOP)
120
+ if not desktop:
121
+ return [ReadyCheck("input_desktop", False, f"the input desktop isn't this session's: {_last_error()}")]
122
+ user32.CloseDesktop(desktop)
123
+ return [ReadyCheck("input_desktop", True, "the input desktop is this session's")]
124
+
125
+ @click
126
+ async def click_at(self, x, y, button, count, modifiers) -> None:
127
+ held = self._pressing(modifiers)
128
+
129
+ def act():
130
+ _send([_key(vk, True) for vk in held] + [_move(x, y)])
131
+ for i in range(count):
132
+ _send([_button(button, True), _button(button, False)])
133
+ if i + 1 < count:
134
+ time.sleep(CLICK_GAP_S)
135
+ _send([_key(vk, False) for vk in reversed(held)])
136
+ await asyncio.to_thread(act)
137
+
138
+ @drag
139
+ async def drag_along(self, path, button, duration_ms, modifiers) -> None:
140
+ held = self._pressing(modifiers)
141
+ steps = drag_steps(path)
142
+ pause = duration_ms / 1000 / len(steps)
143
+ finish = [_button(button, False)] + [_key(vk, False) for vk in reversed(held)]
144
+ try:
145
+ await asyncio.to_thread(_send, [_key(vk, True) for vk in held] + [_move(*path[0]), _button(button, True)])
146
+ for px, py in steps:
147
+ await asyncio.to_thread(_send, [_move(px, py)])
148
+ await asyncio.sleep(pause)
149
+ except BaseException:
150
+ with contextlib.suppress(Exception):
151
+ await asyncio.to_thread(_send, finish)
152
+ raise
153
+ await asyncio.to_thread(_send, finish)
154
+
155
+ @scroll
156
+ async def scroll_at(self, x, y, direction, amount, modifiers) -> None:
157
+ held = self._pressing(modifiers)
158
+ await asyncio.to_thread(_send, [_key(vk, True) for vk in held] + [_move(x, y)] + [_wheel(direction)] * amount
159
+ + [_key(vk, False) for vk in reversed(held)])
160
+
161
+ @pointer_move
162
+ async def move(self, x, y) -> None:
163
+ await asyncio.to_thread(_send, [_move(x, y)])
164
+
165
+ @pointer_down
166
+ async def down(self, button) -> None:
167
+ await asyncio.to_thread(_send, [_button(button, True)])
168
+
169
+ @pointer_up
170
+ async def up(self, button) -> None:
171
+ await asyncio.to_thread(_send, [_button(button, False)])
172
+
173
+ @pointer_position
174
+ async def where(self) -> tuple[int, int]:
175
+ point = _Point()
176
+ if not _user32().GetCursorPos(ctypes.byref(point)):
177
+ raise DeviceUnavailable(f"the pointer can't be read: {_last_error()}")
178
+ left, top, _, _ = _virtual_screen()
179
+ return point.x - left, point.y - top
180
+
181
+ @type_text
182
+ async def type_in(self, text) -> None:
183
+ """Each character as its key on the keyboard layout, with the modifiers it needs that no held key holds, so an
184
+ app sees the keys; a character off the layout as itself."""
185
+ hold = self.settings.tap_hold_ms / 1000
186
+ for ch in text:
187
+ vks = [TYPED[ch]] if ch in TYPED else _layout_keys(ch)
188
+ if vks is None:
189
+ downs, ups = _unicode(ch)
190
+ else:
191
+ pressed = [vk for vk in vks if vk not in self._down()]
192
+ downs, ups = [_key(vk, True) for vk in pressed], [_key(vk, False) for vk in reversed(pressed)]
193
+ await asyncio.to_thread(_tap, downs, ups, hold)
194
+ await asyncio.sleep(self.settings.type_delay_ms / 1000)
195
+
196
+ @press
197
+ async def press_keys(self, keys) -> None:
198
+ pressed = self._pressing(keys)
199
+ await asyncio.to_thread(_tap, [_key(vk, True) for vk in pressed], [_key(vk, False) for vk in reversed(pressed)],
200
+ self.settings.tap_hold_ms / 1000)
201
+
202
+ @key_down
203
+ async def keys_down(self, keys) -> None:
204
+ """Press what the keys need that isn't down already; for each key, the virtual keys this pressed, so that
205
+ @key_up releases only those. Every key is looked up before the first press."""
206
+ down = self._down()
207
+ planned = {}
208
+ for key in dict.fromkeys(keys):
209
+ planned[key] = [vk for vk in _virtual_keys(key) if vk not in down]
210
+ down.update(planned[key])
211
+ await asyncio.to_thread(_send, [_key(vk, True) for vks in planned.values() for vk in vks])
212
+ for key, vks in planned.items():
213
+ self._held.setdefault(key, []).extend(vks)
214
+
215
+ @key_up
216
+ async def keys_up(self, keys) -> None:
217
+ """Release what each key's @key_down pressed, nothing for one whose keys were down already, and a key none
218
+ holds as itself; the keys stay held until Windows takes the releases, so a refused call can be made again."""
219
+ released = [vk for key in dict.fromkeys(keys)
220
+ for vk in reversed(self._held[key] if key in self._held else _virtual_keys(key)[-1:])]
221
+ await asyncio.to_thread(_send, [_key(vk, False) for vk in released])
222
+ for key in keys:
223
+ self._held.pop(key, None)
224
+
225
+ def _down(self) -> set[int]:
226
+ return {vk for vks in self._held.values() for vk in vks}
227
+
228
+ def _pressing(self, keys) -> list[int]:
229
+ """The virtual keys that hold ``keys`` down together, in order, each once, but those held keys hold already."""
230
+ down = self._down()
231
+ return [vk for vk in dict.fromkeys(vk for key in keys for vk in _virtual_keys(key)) if vk not in down]
232
+
233
+
234
+ def _send(events: list[_Input]) -> None:
235
+ """``events`` through SendInput, refused when Windows takes fewer than all, as it does when the input desktop isn't
236
+ this session's or a window of higher integrity is in front."""
237
+ if not events:
238
+ return
239
+ sent = _user32().SendInput(len(events), (_Input * len(events))(*events), ctypes.sizeof(_Input))
240
+ if sent != len(events):
241
+ raise DeliveryFailed(f"Windows took {sent} of {len(events)} input events: {_last_error()}")
242
+
243
+
244
+ def _tap(downs: list[_Input], ups: list[_Input], hold_s: float) -> None:
245
+ _send(downs)
246
+ time.sleep(hold_s)
247
+ _send(ups)
248
+
249
+
250
+ def _virtual_screen() -> tuple[int, int, int, int]:
251
+ user32 = _user32()
252
+ return tuple(user32.GetSystemMetrics(i) for i in VIRTUAL_SCREEN)
253
+
254
+
255
+ def _move(x: float, y: float) -> _Input:
256
+ """A move to the pixel (x, y) of the capture, aimed at its centre in the virtual screen's 0 to 65535 coordinates,
257
+ so that Windows' rounding lands on it."""
258
+ _, _, width, height = _virtual_screen()
259
+ dx = min(65535, round((int(x) + 0.5) * 65536 / width))
260
+ dy = min(65535, round((int(y) + 0.5) * 65536 / height))
261
+ return _Input(INPUT_MOUSE, _Event(mi=_MouseInput(dx, dy, 0, MOVE | ABSOLUTE | VIRTUAL_DESK, 0, 0)))
262
+
263
+
264
+ def _button(button: str, down: bool) -> _Input:
265
+ press_flag, release_flag, which = BUTTONS[button]
266
+ return _Input(INPUT_MOUSE, _Event(mi=_MouseInput(0, 0, which, press_flag if down else release_flag, 0, 0)))
267
+
268
+
269
+ def _wheel(direction: str) -> _Input:
270
+ """One notch: up and right turn the wheel forward, down and left back."""
271
+ flag = HWHEEL if direction in ("left", "right") else WHEEL
272
+ notch = WHEEL_DELTA if direction in ("up", "right") else -WHEEL_DELTA
273
+ return _Input(INPUT_MOUSE, _Event(mi=_MouseInput(0, 0, ctypes.c_ulong(notch).value, flag, 0, 0)))
274
+
275
+
276
+ def _key(vk: int, down: bool) -> _Input:
277
+ """A virtual key with its scan code, which apps that read the physical key, as Chromium's ``code`` does, need."""
278
+ flags = (EXTENDED if vk in EXTENDED_KEYS else 0) | (0 if down else KEY_UP)
279
+ return _Input(INPUT_KEYBOARD, _Event(ki=_KeybdInput(vk, _user32().MapVirtualKeyW(vk, 0), flags, 0, 0)))
280
+
281
+
282
+ def _unicode(ch: str) -> tuple[list[_Input], list[_Input]]:
283
+ """A character typed as itself, whatever the keyboard layout: a key for each of its UTF-16 units, two for one
284
+ beyond the BMP."""
285
+ data = ch.encode("utf-16-le")
286
+ units = [int.from_bytes(data[i:i + 2], "little") for i in range(0, len(data), 2)]
287
+ return [_unit(u, 0) for u in units], [_unit(u, KEY_UP) for u in units]
288
+
289
+
290
+ def _unit(unit: int, flags: int) -> _Input:
291
+ return _Input(INPUT_KEYBOARD, _Event(ki=_KeybdInput(0, unit, UNICODE | flags, 0, 0)))
292
+
293
+
294
+ def _virtual_keys(key: str) -> list[int]:
295
+ """The virtual keys that make ``key``: a named key's, or a character's on the keyboard layout with the modifiers
296
+ it needs first."""
297
+ if key in VIRTUAL_KEYS:
298
+ return [VIRTUAL_KEYS[key]]
299
+ if len(key) == 1:
300
+ if (vks := _layout_keys(key)) is None:
301
+ raise Unsupported(f"{key!r} has no key on this keyboard layout; type it as text instead")
302
+ return vks
303
+ raise Unsupported(f"this driver has no Windows virtual key for {key!r}")
304
+
305
+
306
+ def _layout_keys(ch: str) -> list[int] | None:
307
+ """The modifiers and key that type ``ch`` on the keyboard layout, None for a character it has no key for, as one
308
+ beyond the BMP, which VkKeyScanW can't take."""
309
+ scan = _user32().VkKeyScanW(ch) if ord(ch) <= 0xFFFF else -1
310
+ return None if scan == -1 else [vk for bit, vk in SHIFT_STATE if scan >> 8 & bit] + [scan & 0xFF]
311
+
312
+
313
+ def _last_error() -> str:
314
+ return ctypes.WinError(ctypes.get_last_error()).strerror
315
+
316
+
@@ -8,7 +8,6 @@ from __future__ import annotations
8
8
 
9
9
  import asyncio
10
10
  import contextlib
11
- import math
12
11
  import os
13
12
  import threading
14
13
  import time
@@ -23,6 +22,7 @@ from Xlib import display as xdisplay
23
22
  from Xlib.error import ConnectionClosedError, DisplayConnectionError
24
23
  from Xlib.ext import xtest
25
24
 
25
+ from ._desktop import CLICK_GAP_S, MAX_CLICK_COUNT, MAX_DRAG_POINTS, drag_steps, mss_capture
26
26
  from .driver import ComputerDriverEnvironment
27
27
  from .errors import DeliveryFailed, DeviceUnavailable, Unsupported
28
28
  from .operations import (Display, Image, ReadyCheck, capture, click, display, drag, key_down, key_up, pointer_down,
@@ -44,13 +44,9 @@ KEYSYMS = {
44
44
  "PrintScreen": "Print", "Pause": "Pause", " ": "space", **{f"F{n}": f"F{n}" for n in range(1, 25)},
45
45
  }
46
46
  TYPED = {"\n": "Return", "\t": "Tab"}
47
- #: The pause between the presses of a double or triple click, in seconds.
48
- CLICK_GAP_S = 0.04
49
47
  #: How long a button's release may take to reach the app, and how often the driver looks.
50
48
  RELEASE_WAIT_S = 5.0
51
49
  RELEASE_POLL_S = 0.005
52
- #: How far apart a drag's pointer moves are, in pixels.
53
- DRAG_STEP_PX = 20
54
50
  DROPPED = (ConnectionClosedError, DisplayConnectionError, OSError)
55
51
 
56
52
 
@@ -58,8 +54,8 @@ class X11Driver(ComputerDriverEnvironment):
58
54
  """The computer/v1 operations, the pointer and keyboard groups among them, on an X11 desktop."""
59
55
 
60
56
  buttons = ("left", "right", "middle", "back", "forward")
61
- max_click_count = 3
62
- max_drag_points = 50
57
+ max_click_count = MAX_CLICK_COUNT
58
+ max_drag_points = MAX_DRAG_POINTS
63
59
  recording = Recording(max_fps=30)
64
60
 
65
61
  def __init__(self, settings: ComputerSettings | None = None, platform: Platform | None = None) -> None:
@@ -78,7 +74,7 @@ class X11Driver(ComputerDriverEnvironment):
78
74
 
79
75
  @capture
80
76
  async def grab(self) -> Image:
81
- return await asyncio.to_thread(_grab)
77
+ return await asyncio.to_thread(mss_capture, mss.MSS)
82
78
 
83
79
  @record
84
80
  async def stream(self, hints: RecordHints) -> AsyncIterator[RecordedFrame]:
@@ -123,7 +119,7 @@ class X11Driver(ComputerDriverEnvironment):
123
119
  @drag
124
120
  async def drag_along(self, path, button, duration_ms, modifiers) -> None:
125
121
  syms = [_keysym(k) for k in modifiers]
126
- steps = _steps(path)
122
+ steps = drag_steps(path)
127
123
  pause = duration_ms / 1000 / len(steps)
128
124
  held: list[int] = []
129
125
 
@@ -217,7 +213,8 @@ class X11Driver(ComputerDriverEnvironment):
217
213
  async def keys_up(self, keys) -> None:
218
214
  def act(c):
219
215
  for key in keys:
220
- c.release(reversed(self._held.pop(key, None) or c.codes(_keysym(key))[-1:]))
216
+ held = self._held.pop(key, None)
217
+ c.release(reversed(c.codes(_keysym(key))[-1:] if held is None else held))
221
218
  await self._send(act)
222
219
 
223
220
  async def _read(self, fn: Callable[[_Connection], Any]) -> Any:
@@ -368,24 +365,6 @@ class _Connection:
368
365
  return {8 * i + bit for i, byte in enumerate(self.d.query_keymap()) for bit in range(8) if byte >> bit & 1}
369
366
 
370
367
 
371
- def _grab() -> Image:
372
- try:
373
- with mss.MSS() as m:
374
- shot = m.grab(m.monitors[0])
375
- return Image(shot.width, shot.height, bytes(shot.bgra), "BGRA")
376
- except Exception as e:
377
- raise DeviceUnavailable(f"capture failed: {type(e).__name__}: {e}") from None
378
-
379
-
380
- def _steps(path: list) -> list[tuple[float, float]]:
381
- """The points a drag moves through after its start, about every DRAG_STEP_PX pixels."""
382
- steps = []
383
- for (x0, y0), (x1, y1) in zip(path, path[1:]):
384
- n = max(2, int(math.hypot(x1 - x0, y1 - y0) / DRAG_STEP_PX))
385
- steps += [(x0 + (x1 - x0) * k / n, y0 + (y1 - y0) * k / n) for k in range(1, n + 1)]
386
- return steps
387
-
388
-
389
368
  def _keysym(key: str) -> int:
390
369
  if key in KEYSYMS:
391
370
  return XK.string_to_keysym(KEYSYMS[key])