agentenv-framework-protocol 0.1.474__tar.gz → 0.1.476__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 (163) hide show
  1. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/PKG-INFO +1 -1
  2. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/pyproject.toml +1 -1
  3. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/_linux.py +23 -6
  4. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/conformance/checks.py +63 -17
  5. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/driver.py +3 -3
  6. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/platform.py +6 -3
  7. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_conformance.py +22 -1
  8. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_driver.py +11 -5
  9. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/.gitignore +0 -0
  10. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/LICENSE +0 -0
  11. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/NOTICE +0 -0
  12. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/README.md +0 -0
  13. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/THIRD_PARTY_NOTICES.md +0 -0
  14. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/__init__.py +0 -0
  15. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/advanced_agent.py +0 -0
  16. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/basic_agent.py +0 -0
  17. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/custom_extensions_agent.py +0 -0
  18. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/multimodal_agent.py +0 -0
  19. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/reference_model.py +0 -0
  20. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/streaming_agent.py +0 -0
  21. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/.gitignore +0 -0
  22. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/Dockerfile +0 -0
  23. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/Dockerfile.dockerignore +0 -0
  24. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/README.md +0 -0
  25. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/data/seed.json +0 -0
  26. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/env.toml +0 -0
  27. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/site.py +0 -0
  28. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/web/index.html +0 -0
  29. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/web/package-lock.json +0 -0
  30. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/web/package.json +0 -0
  31. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/web/src/main.js +0 -0
  32. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/web/src/style.css +0 -0
  33. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/examples/website/notes/web/vite.config.js +0 -0
  34. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/__init__.py +0 -0
  35. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/a2a_agent/__init__.py +0 -0
  36. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/a2a_agent/_trajectory_log.py +0 -0
  37. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
  38. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/a2a_agent/extensions.py +0 -0
  39. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
  40. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/a2a_agent/peer_agents.py +0 -0
  41. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/a2a_agent/proxy.py +0 -0
  42. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
  43. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/a2a_agent/staging.py +0 -0
  44. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
  45. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
  46. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/agent_env_environment.py +0 -0
  47. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/browser_cdp.py +0 -0
  48. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/client.py +0 -0
  49. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/__init__.py +0 -0
  50. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/_declare.py +0 -0
  51. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/_load.py +0 -0
  52. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/_macos.py +0 -0
  53. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/_with_fds.py +0 -0
  54. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/_x11.py +0 -0
  55. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/app.py +0 -0
  56. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/app_data.py +0 -0
  57. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/app_runtime.py +0 -0
  58. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/apps.py +0 -0
  59. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/card.py +0 -0
  60. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/checks.py +0 -0
  61. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/conformance/__init__.py +0 -0
  62. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/conformance/__main__.py +0 -0
  63. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/conformance/client.py +0 -0
  64. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/conformance/fixture.py +0 -0
  65. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/conformance/mp4.py +0 -0
  66. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/conformance/page_fixture.py +0 -0
  67. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/conformance/report.py +0 -0
  68. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/dataplane.py +0 -0
  69. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/errors.py +0 -0
  70. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/extensions.py +0 -0
  71. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/frames.py +0 -0
  72. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/keys.py +0 -0
  73. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/launch.py +0 -0
  74. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/manifest.py +0 -0
  75. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/operations.py +0 -0
  76. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/queue.py +0 -0
  77. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/recording.py +0 -0
  78. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/results.py +0 -0
  79. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/run_install_step.py +0 -0
  80. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/runtime.py +0 -0
  81. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/seed.py +0 -0
  82. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/serve.py +0 -0
  83. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/serve_app.py +0 -0
  84. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/session.py +0 -0
  85. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/settings.py +0 -0
  86. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/skill.py +0 -0
  87. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/spaces.py +0 -0
  88. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/svc.py +0 -0
  89. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/computer/tools.py +0 -0
  90. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/errors.py +0 -0
  91. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/manifest.py +0 -0
  92. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/preflight.py +0 -0
  93. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/tool_access.py +0 -0
  94. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/tool_client.py +0 -0
  95. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/tool_context.py +0 -0
  96. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/transfers.py +0 -0
  97. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/types.py +0 -0
  98. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website/__init__.py +0 -0
  99. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website/clock.py +0 -0
  100. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website/conformance.py +0 -0
  101. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website/environment.py +0 -0
  102. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website/errors.py +0 -0
  103. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website/readiness.py +0 -0
  104. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website/routes.py +0 -0
  105. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website/session.py +0 -0
  106. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website_browser/__init__.py +0 -0
  107. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website_browser/control.py +0 -0
  108. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website_browser/data.py +0 -0
  109. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website_browser/devtools.py +0 -0
  110. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website_browser/environment.py +0 -0
  111. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/src/agentenv_protocol/website_browser/shapes.py +0 -0
  112. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/computer_fake.py +0 -0
  113. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/probe_fake.py +0 -0
  114. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_a2a_agent.py +0 -0
  115. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_a2a_agent_examples.py +0 -0
  116. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_a2a_cdp_config.py +0 -0
  117. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_a2a_object_transfers.py +0 -0
  118. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_a2a_peer_agents.py +0 -0
  119. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_a2a_proxy.py +0 -0
  120. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_a2a_staging.py +0 -0
  121. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_a2a_trajectory_log.py +0 -0
  122. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_agent_env_environment.py +0 -0
  123. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_browser_cdp.py +0 -0
  124. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_browser_data.py +0 -0
  125. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_browser_state.py +0 -0
  126. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_client.py +0 -0
  127. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_app_data.py +0 -0
  128. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_app_server.py +0 -0
  129. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_conformance_checks.py +0 -0
  130. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_conformance_page.py +0 -0
  131. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_conformance_recording.py +0 -0
  132. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_dataplane.py +0 -0
  133. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_declare.py +0 -0
  134. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_extensions.py +0 -0
  135. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_frames.py +0 -0
  136. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_macos.py +0 -0
  137. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_page.py +0 -0
  138. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_queue.py +0 -0
  139. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_recording.py +0 -0
  140. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_seed.py +0 -0
  141. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_serve.py +0 -0
  142. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_spaces.py +0 -0
  143. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_computer_svc_forward.py +0 -0
  144. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_control_token.py +0 -0
  145. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_data_objects.py +0 -0
  146. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_errors.py +0 -0
  147. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_extension_methods.py +0 -0
  148. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_intake.py +0 -0
  149. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_manifest.py +0 -0
  150. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_preflight.py +0 -0
  151. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_tool_access.py +0 -0
  152. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_tool_annotations.py +0 -0
  153. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_tool_client.py +0 -0
  154. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_tool_context.py +0 -0
  155. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_tool_context_fastmcp.py +0 -0
  156. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_tool_context_injection.py +0 -0
  157. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_website_api.py +0 -0
  158. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_website_browser.py +0 -0
  159. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_website_conformance.py +0 -0
  160. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_website_environment.py +0 -0
  161. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_website_mcp.py +0 -0
  162. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/tests/test_website_notes_example.py +0 -0
  163. {agentenv_framework_protocol-0.1.474 → agentenv_framework_protocol-0.1.476}/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.474
3
+ Version: 0.1.476
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.474"
3
+ version = "0.1.476"
4
4
  description = "Open data-plane protocol and server SDK for agent environments."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -1,12 +1,15 @@
1
- """The base class's defaults for the OS operations on Linux and macOS, which use POSIX alone. The driver runs as the
2
- session user inside the desktop session, so both act directly: a command inherits the session's environment, a file
3
- lands as that user."""
1
+ """The base class's defaults for the OS operations on Linux, macOS and Windows. The driver runs as the session user
2
+ inside the desktop session, so both act directly: a command inherits the session's environment, a file lands as that
3
+ user. A command runs in a process group of its own, which a timeout kills whole: on Windows, a new process group with no
4
+ console window, killed with its children by taskkill."""
4
5
  from __future__ import annotations
5
6
 
6
7
  import asyncio
7
8
  import contextlib
8
9
  import os
9
10
  import signal
11
+ import subprocess
12
+ import sys
10
13
  import tempfile
11
14
  from pathlib import Path
12
15
 
@@ -22,6 +25,10 @@ POLL_S = 0.02
22
25
  #: How much of a stream is read at a time.
23
26
  READ_BYTES = 65536
24
27
  _draining: set = set()
28
+ WINDOWS = sys.platform == "win32"
29
+ #: How a command starts: in a session, or on Windows, a process group, of its own, with no console window there.
30
+ _SPAWN = ({"creationflags": subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.CREATE_NO_WINDOW} if WINDOWS else
31
+ {"start_new_session": True})
25
32
 
26
33
 
27
34
  async def run_in_session(argv: list[str], cwd: str | None, env: dict[str, str] | None, timeout_s: float, *,
@@ -33,15 +40,14 @@ async def run_in_session(argv: list[str], cwd: str | None, env: dict[str, str] |
33
40
  try:
34
41
  process = await asyncio.create_subprocess_exec(
35
42
  *argv, cwd=workdir, env={**os.environ, **(env or {})}, stdin=asyncio.subprocess.DEVNULL,
36
- stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, start_new_session=True)
43
+ stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, **_SPAWN)
37
44
  except (FileNotFoundError, PermissionError, NotADirectoryError) as e:
38
45
  raise InvalidArgument(f"cannot run {argv[0]!r}: {e.strerror or e}") from None
39
46
  outputs = [_Capped(output_bytes), _Capped(output_bytes)]
40
47
  readers = [asyncio.ensure_future(out.read(stream)) for out, stream in zip(outputs, (process.stdout, process.stderr))]
41
48
  timed_out = not await _exited(process, timeout_s)
42
49
  if timed_out:
43
- with contextlib.suppress(ProcessLookupError):
44
- os.killpg(process.pid, signal.SIGKILL)
50
+ await _killed(process.pid)
45
51
  await _exited(process, DRAIN_S)
46
52
  _, pending = await asyncio.wait(readers, timeout=DRAIN_S)
47
53
  for out in outputs:
@@ -54,6 +60,17 @@ async def run_in_session(argv: list[str], cwd: str | None, env: dict[str, str] |
54
60
  stderr=bytes(stderr.kept), timed_out=timed_out, truncated=stdout.cut or stderr.cut)
55
61
 
56
62
 
63
+ async def _killed(pid: int) -> None:
64
+ """The process group ``pid`` leads killed: on Windows, the process and its children."""
65
+ if WINDOWS:
66
+ killer = await asyncio.create_subprocess_exec("taskkill", "/F", "/T", "/PID", str(pid),
67
+ stdout=asyncio.subprocess.DEVNULL, stderr=asyncio.subprocess.DEVNULL)
68
+ await killer.wait()
69
+ return
70
+ with contextlib.suppress(ProcessLookupError):
71
+ os.killpg(pid, signal.SIGKILL)
72
+
73
+
57
74
  async def _exited(process: asyncio.subprocess.Process, timeout_s: float) -> bool:
58
75
  """Whether the process exits within ``timeout_s``. Read from ``returncode``, which is set when the process exits:
59
76
  ``Process.wait()`` also waits for the pipes to close, which a background child can hold open."""
@@ -72,6 +72,25 @@ READ_DIGEST = ("import hashlib,json,sys;d=open(sys.argv[1],'rb').read();"
72
72
  READ_CHUNK = ("import base64,sys;f=open(sys.argv[1],'rb');f.seek(int(sys.argv[2]));"
73
73
  "print(base64.b64encode(f.read(int(sys.argv[3]))).decode())")
74
74
  CHUNK_BYTES = 512 << 10
75
+ #: The probe's commands on a Windows computer, which has no POSIX shell or tools: Python one-liners on the python3 the
76
+ #: fixture runs on, writing bytes, so output carries no CRLF, and taskkill and the account checks Windows has.
77
+ WIN_ECHO = ("python3", "-c", "import sys;sys.stdout.buffer.write(b'probe\\n');sys.stderr.buffer.write(b'oops\\n');"
78
+ "sys.exit(3)")
79
+ WIN_SLEEP = ("python3", "-c", "import time;time.sleep(30)")
80
+ WIN_NOW = ("python3", "-c", "import time;print(time.time_ns())")
81
+ WIN_CAT = ("python3", "-c", "import sys;sys.stdout.buffer.write(open(sys.argv[1],'rb').read())")
82
+ WIN_TAIL = ("python3", "-c", "import sys;sys.stdout.buffer.write(b''.join(open(sys.argv[1],'rb').readlines()[-5:]))")
83
+ WIN_REMOVE = ("python3", "-c", "import os,shutil,sys;[shutil.rmtree(p,ignore_errors=True) if os.path.isdir(p) else "
84
+ "os.path.exists(p) and os.remove(p) for p in sys.argv[1:]]")
85
+ WIN_RMDIR = ("python3", "-c", "import os,sys;p=sys.argv[1];os.path.isdir(p) and not os.listdir(p) and os.rmdir(p)")
86
+ #: Starts the fixture detached, its output to its log, in a process group of its own with no console window; its pid.
87
+ WIN_START = ("python3", "-c", "import subprocess,sys;log=open(sys.argv[3],'wb');print(subprocess.Popen("
88
+ "[sys.executable,sys.argv[1],sys.argv[2]],stdout=log,stderr=subprocess.STDOUT,stdin=subprocess.DEVNULL,"
89
+ "creationflags=0x08000200).pid)")
90
+ #: A file's owner, its path in the command, since PowerShell takes what follows -Command as more of the command.
91
+ WIN_OWNER = "(Get-Acl -LiteralPath '{}').Owner"
92
+ #: The well-known SID of the local Administrators group, which an unprivileged user's token holds no enabled entry of.
93
+ ADMINISTRATORS = "S-1-5-32-544"
75
94
  #: The checks only ``full`` runs, recorded unverified when the fixture can't be staged.
76
95
  FULL_CHECKS = (("P3", "fixture colours land where the mapping says"), ("P4", "clicks show their effect"),
77
96
  ("P5", "refusals leave the fixture as it was"), ("P6", "typed and pressed keys arrive"),
@@ -98,6 +117,7 @@ class Probe:
98
117
  self.fixture_pid: str | None = None
99
118
  self.page = self.features.get("screen") == "page"
100
119
  self.fixture = "page_fixture.py" if self.page else "fixture.py"
120
+ self.windows = (self.features.get("platform") or {}).get("os") == "windows"
101
121
  self.desk = ""
102
122
  self.tab: str | None = None
103
123
  self.opened: list[str] = []
@@ -188,7 +208,7 @@ class Probe:
188
208
  raise AssertionError(f"{name} {args} failed: {result.content[0].text}")
189
209
 
190
210
  async def state(self) -> dict:
191
- out = await self.driver.run("cat", f"{STAGE}/state/state.json")
211
+ out = await self._cat(f"{STAGE}/state/state.json")
192
212
  return json.loads(out["stdout"])
193
213
 
194
214
  async def until(self, holds: Callable[[Any], bool],
@@ -394,24 +414,26 @@ class Probe:
394
414
  cap = self.driver.params(COMPUTER_EXEC).get("max_output_bytes")
395
415
 
396
416
  async def output() -> str | None:
397
- out = await self.driver.run("sh", "-c", "echo probe; echo oops >&2; exit 3")
417
+ out = await self.driver.run(*(WIN_ECHO if self.windows else ("sh", "-c", "echo probe; echo oops >&2; exit 3")))
398
418
  want = {"exit_code": 3, "stdout": "probe\n", "stderr": "oops\n", "timed_out": False}
399
419
  return None if {k: out.get(k) for k in want} == want else f"it answered {out}"
400
420
 
401
421
  async def timeout() -> str | None:
402
422
  started = time.monotonic()
403
- out = await self.driver.run("sh", "-c", "sleep 30", timeout_s=1)
423
+ out = await self.driver.run(*(WIN_SLEEP if self.windows else ("sh", "-c", "sleep 30")), timeout_s=1)
404
424
  took = time.monotonic() - started
405
425
  return None if out.get("timed_out") and took < 15 else f"after {took:.1f} s it answered {out}"
406
426
 
407
427
  async def once() -> str | None:
408
- request = {"argv": ["sh", "-c", "date +%s%N"], "request_id": f"{PROBE_ROLE}-once-{time.time_ns()}"}
428
+ request = {"argv": list(WIN_NOW if self.windows else ("sh", "-c", "date +%s%N")),
429
+ "request_id": f"{PROBE_ROLE}-once-{time.time_ns()}"}
409
430
  first = await self.driver.invoke(COMPUTER_EXEC, "run", request)
410
431
  again = await self.driver.invoke(COMPUTER_EXEC, "run", request)
411
432
  return None if first == again else f"it ran twice: {first} then {again}"
412
433
 
413
434
  async def capped() -> str | None:
414
- out = await self.driver.run("sh", "-c", f"head -c {cap + 1000} /dev/zero | tr '\\0' a")
435
+ out = await self.driver.run(*(("python3", "-c", f"import sys;sys.stdout.buffer.write(b'a'*{cap + 1000})")
436
+ if self.windows else ("sh", "-c", f"head -c {cap + 1000} /dev/zero | tr '\\0' a")))
415
437
  return None if out.get("truncated") and len(out["stdout"]) == cap else (
416
438
  f"{len(out['stdout'])} bytes came back, truncated {out.get('truncated')}")
417
439
 
@@ -546,28 +568,32 @@ class Probe:
546
568
  added = await self.driver.add_file(f"{STAGE}/{self.fixture}", source)
547
569
  if "error" in added:
548
570
  return f"data/add refused it: {added['error'].get('message')}"
549
- await self.driver.run("sh", "-c", f"rm -rf {STAGE}/state")
550
- started = await self.driver.run(
551
- "sh", "-c", f"python3 {STAGE}/{self.fixture} {STAGE}/state > {STAGE}/fixture.log 2>&1 & echo $!")
571
+ if self.windows:
572
+ await self.driver.run(*WIN_REMOVE, f"{STAGE}/state")
573
+ started = await self.driver.run(*WIN_START, f"{STAGE}/{self.fixture}", f"{STAGE}/state", f"{STAGE}/fixture.log")
574
+ else:
575
+ await self.driver.run("sh", "-c", f"rm -rf {STAGE}/state")
576
+ started = await self.driver.run(
577
+ "sh", "-c", f"python3 {STAGE}/{self.fixture} {STAGE}/state > {STAGE}/fixture.log 2>&1 & echo $!")
552
578
  self.fixture_pid = started["stdout"].strip() or None
553
579
  deadline = time.monotonic() + FIXTURE_START_S
554
580
  while time.monotonic() < deadline:
555
581
  if self.page and self.tab is None and (problem := await self._open_desk()):
556
582
  return problem
557
- layout = await self.driver.run("cat", f"{STAGE}/state/layout.json")
583
+ layout = await self._cat(f"{STAGE}/state/layout.json")
558
584
  if layout.get("exit_code") == 0 and layout["stdout"].strip():
559
585
  self.layout = json.loads(layout["stdout"])
560
586
  return None
561
- state = await self.driver.run("cat", f"{STAGE}/state/state.json")
587
+ state = await self._cat(f"{STAGE}/state/state.json")
562
588
  if state.get("exit_code") == 0 and "error" in (failed := json.loads(state["stdout"] or "{}")):
563
589
  return failed["error"]
564
590
  await asyncio.sleep(0.5)
565
- log = await self.driver.run("tail", "-n", "5", f"{STAGE}/fixture.log")
591
+ log = await self.driver.run(*(WIN_TAIL if self.windows else ("tail", "-n", "5")), f"{STAGE}/fixture.log")
566
592
  return f"no layout after {FIXTURE_START_S:g} s: {log['stdout'].strip() or log['stderr'].strip()}"
567
593
 
568
594
  async def _open_desk(self) -> str | None:
569
595
  """Once the page fixture's server has its port, the desk opened in a new tab, which is the probe's."""
570
- port = await self.driver.run("cat", f"{STAGE}/state/port")
596
+ port = await self._cat(f"{STAGE}/state/port")
571
597
  if port.get("exit_code") != 0 or not port["stdout"].strip():
572
598
  return None
573
599
  self.desk = f"http://127.0.0.1:{port['stdout'].strip()}/"
@@ -588,10 +614,14 @@ class Probe:
588
614
  await self.tool("computer_tabs", {"action": "close", "tab": tab})
589
615
  left.remove(tab)
590
616
  if self.fixture_pid:
591
- await self.driver.run("kill", self.fixture_pid)
592
- await self.driver.run("rm", "-rf", STAGE)
617
+ await self.driver.run(*(("taskkill", "/F", "/T", "/PID") if self.windows else ("kill",)), self.fixture_pid)
618
+ await self.driver.run(*(WIN_REMOVE if self.windows else ("rm", "-rf")), STAGE)
593
619
  if self.upload:
594
- await self.driver.run("rm", "-f", f"{UPLOADS}/{self.upload}")
620
+ await self.driver.run(*(WIN_REMOVE if self.windows else ("rm", "-f")), f"{UPLOADS}/{self.upload}")
621
+
622
+ async def _cat(self, path: str) -> dict:
623
+ """``path``'s bytes as the computer's command answers them."""
624
+ return await self.driver.run(*(WIN_CAT if self.windows else ("cat",)), path)
595
625
 
596
626
  async def _base(self) -> tuple[str, float, object]:
597
627
  """A fresh frame of the fixture: its id, the display's scale, and its image."""
@@ -605,6 +635,21 @@ class Probe:
605
635
  math.floor((y + (h / 2 if dy is None else dy)) * scale))
606
636
 
607
637
  async def _security(self) -> None:
638
+ async def one_account() -> str | None:
639
+ account = (await self.driver.run("whoami"))["stdout"].strip()
640
+ owner = (await self.driver.run("powershell", "-NoProfile", "-Command",
641
+ WIN_OWNER.format(f"{STAGE}/{self.fixture}")))["stdout"].strip()
642
+ groups = (await self.driver.run("whoami", "/groups"))["stdout"]
643
+ if any(ADMINISTRATORS in line and "Enabled group" in line for line in groups.splitlines()):
644
+ return f"commands run as {account}, an administrator"
645
+ return None if owner.lower() == account.lower() else (
646
+ f"data/add wrote as {owner}, commands run as {account}")
647
+
648
+ if self.windows:
649
+ await self.check("P9", "commands and data/add run as one unprivileged user", one_account, COMPUTER_EXEC,
650
+ *DATA_PLANE)
651
+ return
652
+
608
653
  async def one_user() -> str | None:
609
654
  uid = (await self.driver.run("id", "-u"))["stdout"].strip()
610
655
  owner = (await self.driver.run(*OWNER, f"{STAGE}/{self.fixture}"))["stdout"].strip()
@@ -993,8 +1038,9 @@ class Probe:
993
1038
  for recording_id in made:
994
1039
  with contextlib.suppress(Exception):
995
1040
  await self.driver.invoke(COMPUTER_RECORDING, "end", {"recording_id": recording_id})
996
- await self.driver.run("rm", "-rf", f"{RECORDINGS_DIR}/{recording_id}")
997
- await self.driver.run("sh", "-c", f"rmdir {RECORDINGS_DIR} 2>/dev/null || true")
1041
+ await self.driver.run(*(WIN_REMOVE if self.windows else ("rm", "-rf")), f"{RECORDINGS_DIR}/{recording_id}")
1042
+ await self.driver.run(*((*WIN_RMDIR, RECORDINGS_DIR) if self.windows else
1043
+ ("sh", "-c", f"rmdir {RECORDINGS_DIR} 2>/dev/null || true")))
998
1044
 
999
1045
  async def _segments_match(self, wire: dict, fps: int) -> str | None:
1000
1046
  """What is wrong with a recording without a grant, read back from the computer, or None."""
@@ -219,13 +219,13 @@ class ComputerDriverEnvironment(Tools, Extensions, DataPlane, AgentEnvEnvironmen
219
219
  if self._computer is None:
220
220
  raise TypeError(f"{type(self).__qualname__}.__init__ must call super().__init__()")
221
221
 
222
- # The defaults for the OS operations on Linux and macOS, POSIX alike.
222
+ # The defaults for the OS operations on Linux, macOS and Windows.
223
223
 
224
- @default_for("run_in_session", os=["linux", "macos"])
224
+ @default_for("run_in_session", os=["linux", "macos", "windows"])
225
225
  async def _computer_linux_run_in_session(self, argv: list, cwd: str | None, env: dict | None, timeout_s: float):
226
226
  return await _linux.run_in_session(argv, cwd, env, timeout_s, output_bytes=self.settings.exec_output_bytes)
227
227
 
228
- @default_for("write_files", os=["linux", "macos"])
228
+ @default_for("write_files", os=["linux", "macos", "windows"])
229
229
  async def _computer_linux_write_files(self, files: dict) -> None:
230
230
  await asyncio.to_thread(_linux.write_files, files)
231
231
 
@@ -28,14 +28,17 @@ class Platform:
28
28
 
29
29
  def detect(environ: dict | None = None, os_release: Path = Path("/etc/os-release")) -> Platform:
30
30
  """The running platform: the OS, ``/etc/os-release``'s ID and VERSION_ID on Linux and the macOS version on macOS,
31
- the machine, and the display: on Linux from ``XDG_SESSION_TYPE``, ``none`` for a computer with no display, else
32
- ``DISPLAY`` (x11) or ``WAYLAND_DISPLAY`` (wayland); on macOS Aqua."""
31
+ Windows' marketing version (``11`` from build 22000) on Windows, the machine, and the display: on Linux from
32
+ ``XDG_SESSION_TYPE``, ``none`` for a computer with no display, else ``DISPLAY`` (x11) or ``WAYLAND_DISPLAY``
33
+ (wayland); on macOS Aqua; on Windows the Desktop Window Manager (dwm)."""
33
34
  environ = os.environ if environ is None else environ
34
35
  name = _OS.get(sys.platform, sys.platform)
35
36
  release = _os_release(os_release) if name == "linux" else {}
36
37
  if name == "macos":
37
38
  release = {"VERSION_ID": _platform.mac_ver()[0] or None}
38
- display = "aqua" if name == "macos" else None
39
+ if name == "windows":
40
+ release = {"VERSION_ID": "11" if sys.getwindowsversion().build >= 22000 else "10"}
41
+ display = {"macos": "aqua", "windows": "dwm"}.get(name)
39
42
  if name == "linux":
40
43
  session = (environ.get("XDG_SESSION_TYPE") or "").strip().lower()
41
44
  if session in ("x11", "wayland", "none"):
@@ -34,6 +34,7 @@ from agentenv_protocol.computer import ( # noqa: E402
34
34
  type_text,
35
35
  )
36
36
  from agentenv_protocol.computer.conformance import CheckResult, ProbeReport, run_probe # noqa: E402
37
+ from agentenv_protocol.computer import _linux # noqa: E402
37
38
  from agentenv_protocol.computer.conformance import checks # noqa: E402
38
39
  from agentenv_protocol.computer.conformance.client import Driver # noqa: E402
39
40
  from agentenv_protocol import CONTROL_TOKEN_ENV # noqa: E402
@@ -167,7 +168,7 @@ async def test_each_app_a_driver_fronts_that_exports_is_asked_for_its_export_onc
167
168
 
168
169
  @pytest.mark.asyncio
169
170
  async def test_a_driver_without_exec_or_data_add_passes_basic_and_leaves_full_unverified():
170
- report = await _probe(MinimalDriver(FakeScreen(640, 400), platform=Platform(os="windows")))
171
+ report = await _probe(MinimalDriver(FakeScreen(640, 400), platform=Platform(os="freebsd")))
171
172
  assert report.passed, _failed(report)
172
173
  unverified = [c for c in report.checks if c.status == "unverified"]
173
174
  assert {c.check for c in unverified} == {"P3", "P4", "P5", "P6", "P7", "P9"}
@@ -176,6 +177,26 @@ async def test_a_driver_without_exec_or_data_add_passes_basic_and_leaves_full_un
176
177
  assert report.extensions["urn:agentenv:computer/v1"] == "verified"
177
178
 
178
179
 
180
+ @pytest.mark.asyncio
181
+ async def test_a_windows_computer_is_probed_with_the_commands_windows_has(monkeypatch):
182
+ """Its exec checks run Python one-liners rather than a POSIX shell; here they run on this machine's python3."""
183
+ sent = []
184
+ run = _linux.run_in_session
185
+
186
+ async def recorded(argv, *args, **kwargs):
187
+ sent.append(list(argv))
188
+ return await run(argv, *args, **kwargs)
189
+
190
+ monkeypatch.setattr(_linux, "run_in_session", recorded)
191
+ report = await _probe(MinimalDriver(FakeScreen(640, 400), platform=Platform(os="windows", arch="amd64")), "basic")
192
+
193
+ assert report.passed, _failed(report)
194
+ exec_checks = [c for c in report.checks if "urn:agentenv:computer-exec/v1" in c.extensions]
195
+ assert len(exec_checks) >= 4 and all(c.status == "passed" for c in exec_checks)
196
+ assert list(checks.WIN_ECHO) in sent and list(checks.WIN_SLEEP) in sent and list(checks.WIN_NOW) in sent
197
+ assert not [argv for argv in sent if argv[0] == "sh"]
198
+
199
+
179
200
  class NeverReady(FixtureDriver):
180
201
  async def ready_checks(self):
181
202
  return [ReadyCheck("session", False, "no desktop session")]
@@ -10,6 +10,7 @@ pytest.importorskip("PIL")
10
10
  import asyncio # noqa: E402
11
11
  import base64 # noqa: E402
12
12
  import io # noqa: E402
13
+ from types import SimpleNamespace # noqa: E402
13
14
 
14
15
  from computer_fake import LINUX, FakeDriver, FakeScreen, MinimalDriver, http_client, mcp_session # noqa: E402
15
16
  from mcp.server.fastmcp import FastMCP # noqa: E402
@@ -46,7 +47,7 @@ from agentenv_protocol.computer.tools import _modifiers # noqa: E402
46
47
 
47
48
  MACOS = Platform(os="macos", arch="arm64")
48
49
  #: A platform the base class has no OS defaults for.
49
- WINDOWS = Platform(os="windows", arch="amd64")
50
+ FREEBSD = Platform(os="freebsd", arch="amd64")
50
51
 
51
52
 
52
53
  def _required(namespace: dict) -> dict:
@@ -133,7 +134,7 @@ def test_a_driver_method_replaces_the_linux_default():
133
134
  Driver = _define({"run": run_in_session(lambda self, *a: None)})
134
135
  assert declared(Driver)["run_in_session"] == [("run", {})]
135
136
  assert declared(MinimalDriver)["run_in_session"] == [("_computer_linux_run_in_session",
136
- {"os": frozenset({"linux", "macos"})})]
137
+ {"os": frozenset({"linux", "macos", "windows"})})]
137
138
 
138
139
 
139
140
  def test_the_platform_is_detected(tmp_path, monkeypatch):
@@ -148,6 +149,11 @@ def test_the_platform_is_detected(tmp_path, monkeypatch):
148
149
  monkeypatch.setattr("agentenv_protocol.computer.platform.sys.platform", "darwin")
149
150
  monkeypatch.setattr("agentenv_protocol.computer.platform._platform.mac_ver", lambda: ("26.5.2", ("", "", ""), ""))
150
151
  assert detect({"DISPLAY": ":0"}, release) == Platform("macos", None, "26.5.2", "arm64", "aqua")
152
+ monkeypatch.setattr("agentenv_protocol.computer.platform.sys.platform", "win32")
153
+ monkeypatch.setattr("agentenv_protocol.computer.platform._platform.machine", lambda: "AMD64")
154
+ monkeypatch.setattr("agentenv_protocol.computer.platform.sys.getwindowsversion",
155
+ lambda: SimpleNamespace(build=26200), raising=False)
156
+ assert detect({}, release) == Platform("windows", None, "11", "amd64", "dwm")
151
157
 
152
158
 
153
159
  def test_settings_come_from_one_json_value():
@@ -235,7 +241,7 @@ async def test_a_driver_whose_software_ships_a_skill_leads_with_it_and_the_compu
235
241
 
236
242
  @pytest.mark.asyncio
237
243
  async def test_a_minimal_driver_declares_only_what_it_has():
238
- async with http_client(MinimalDriver(platform=WINDOWS)) as http:
244
+ async with http_client(MinimalDriver(platform=FREEBSD)) as http:
239
245
  card = (await http.get("/.well-known/agent-env.json")).json()
240
246
  display_set = await http.post("/agentenv/ext/computer-display/set", json={"width": 1, "height": 1})
241
247
  exec_run = await http.post("/agentenv/ext/computer-exec/run", json={"argv": ["true"], "request_id": "r"})
@@ -462,14 +468,14 @@ def test_a_driver_class_declares_what_it_serves_on_each_platform(monkeypatch):
462
468
  raise AssertionError("declaration builds the card without the subclass's __init__")
463
469
 
464
470
  linux = Declared.declaration(LINUX)
465
- elsewhere = MinimalDriver.declaration(Platform(os="windows"))
471
+ elsewhere = MinimalDriver.declaration(Platform(os="freebsd"))
466
472
  uris = lambda card: {e.uri for e in card.capabilities.extensions} # noqa: E731
467
473
  assert linux.name == "declared" and len(linux.capabilities.tools) == 14
468
474
  assert {"urn:agentenv:computer-pointer/v1", "urn:agentenv:computer-exec/v1"} <= uris(linux)
469
475
  assert elsewhere.name == "MinimalDriver" and len(elsewhere.capabilities.tools) == 7
470
476
  assert "urn:agentenv:computer-exec/v1" not in uris(elsewhere)
471
477
  computer = next(e for e in elsewhere.capabilities.extensions if e.uri == "urn:agentenv:computer/v1")
472
- assert computer.params["platform"] == {"os": "windows"}
478
+ assert computer.params["platform"] == {"os": "freebsd"}
473
479
 
474
480
 
475
481
  def test_a_supports_entry_names_each_platform_it_covers():