agentenv-framework-protocol 0.1.506__tar.gz → 0.1.507__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 (175) hide show
  1. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/PKG-INFO +45 -1
  2. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/README.md +44 -0
  3. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/pyproject.toml +1 -1
  4. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/__init__.py +6 -2
  5. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/environment.py +69 -11
  6. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/readiness.py +3 -0
  7. agentenv_framework_protocol-0.1.507/tests/test_website_live.py +101 -0
  8. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/.gitignore +0 -0
  9. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/LICENSE +0 -0
  10. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/NOTICE +0 -0
  11. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/THIRD_PARTY_NOTICES.md +0 -0
  12. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/__init__.py +0 -0
  13. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/advanced_agent.py +0 -0
  14. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/basic_agent.py +0 -0
  15. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/custom_extensions_agent.py +0 -0
  16. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/multimodal_agent.py +0 -0
  17. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/reference_model.py +0 -0
  18. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/streaming_agent.py +0 -0
  19. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/.gitignore +0 -0
  20. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/Dockerfile +0 -0
  21. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/Dockerfile.dockerignore +0 -0
  22. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/README.md +0 -0
  23. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/data/seed.json +0 -0
  24. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/env.toml +0 -0
  25. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/site.py +0 -0
  26. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/index.html +0 -0
  27. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/package-lock.json +0 -0
  28. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/package.json +0 -0
  29. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/src/main.js +0 -0
  30. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/src/style.css +0 -0
  31. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/vite.config.js +0 -0
  32. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/__init__.py +0 -0
  33. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/__init__.py +0 -0
  34. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/_trajectory_log.py +0 -0
  35. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
  36. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/extensions.py +0 -0
  37. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
  38. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/peer_agents.py +0 -0
  39. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/proxy.py +0 -0
  40. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
  41. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/staging.py +0 -0
  42. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
  43. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
  44. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/agent_env_environment.py +0 -0
  45. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/browser_cdp.py +0 -0
  46. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/client.py +0 -0
  47. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/__init__.py +0 -0
  48. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_declare.py +0 -0
  49. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_job.py +0 -0
  50. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_linux.py +0 -0
  51. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_load.py +0 -0
  52. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_macos.py +0 -0
  53. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_reaping.py +0 -0
  54. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_session_posix.py +0 -0
  55. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_session_windows.py +0 -0
  56. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_with_fds.py +0 -0
  57. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_x11.py +0 -0
  58. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/app.py +0 -0
  59. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/app_data.py +0 -0
  60. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/app_runtime.py +0 -0
  61. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/apps.py +0 -0
  62. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/card.py +0 -0
  63. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/checks.py +0 -0
  64. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/__init__.py +0 -0
  65. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/__main__.py +0 -0
  66. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/checks.py +0 -0
  67. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/client.py +0 -0
  68. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/desk.py +0 -0
  69. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/fixture.py +0 -0
  70. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/mp4.py +0 -0
  71. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/page_fixture.py +0 -0
  72. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/report.py +0 -0
  73. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/wayland_fixture.py +0 -0
  74. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/dataplane.py +0 -0
  75. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/driver.py +0 -0
  76. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/errors.py +0 -0
  77. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/extensions.py +0 -0
  78. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/frames.py +0 -0
  79. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/keys.py +0 -0
  80. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/launch.py +0 -0
  81. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/manifest.py +0 -0
  82. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/operations.py +0 -0
  83. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/platform.py +0 -0
  84. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/prefix.py +0 -0
  85. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/queue.py +0 -0
  86. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/recording.py +0 -0
  87. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/results.py +0 -0
  88. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/run_install_step.py +0 -0
  89. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/runtime.py +0 -0
  90. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/seed.py +0 -0
  91. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/serve.py +0 -0
  92. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/serve_app.py +0 -0
  93. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/served.py +0 -0
  94. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/session.py +0 -0
  95. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/settings.py +0 -0
  96. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/skill.py +0 -0
  97. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/spaces.py +0 -0
  98. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/svc.py +0 -0
  99. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/tools.py +0 -0
  100. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/errors.py +0 -0
  101. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/manifest.py +0 -0
  102. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/preflight.py +0 -0
  103. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/tool_access.py +0 -0
  104. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/tool_client.py +0 -0
  105. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/tool_context.py +0 -0
  106. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/transfers.py +0 -0
  107. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/types.py +0 -0
  108. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/clock.py +0 -0
  109. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/conformance.py +0 -0
  110. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/errors.py +0 -0
  111. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/routes.py +0 -0
  112. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/session.py +0 -0
  113. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/__init__.py +0 -0
  114. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/control.py +0 -0
  115. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/data.py +0 -0
  116. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/devtools.py +0 -0
  117. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/environment.py +0 -0
  118. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/shapes.py +0 -0
  119. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/computer_fake.py +0 -0
  120. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/probe_fake.py +0 -0
  121. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_agent.py +0 -0
  122. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_agent_examples.py +0 -0
  123. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_cdp_config.py +0 -0
  124. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_object_transfers.py +0 -0
  125. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_peer_agents.py +0 -0
  126. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_proxy.py +0 -0
  127. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_staging.py +0 -0
  128. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_trajectory_log.py +0 -0
  129. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_agent_env_environment.py +0 -0
  130. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_browser_cdp.py +0 -0
  131. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_browser_data.py +0 -0
  132. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_browser_state.py +0 -0
  133. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_client.py +0 -0
  134. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_app_data.py +0 -0
  135. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_app_server.py +0 -0
  136. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_conformance.py +0 -0
  137. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_conformance_checks.py +0 -0
  138. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_conformance_page.py +0 -0
  139. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_conformance_recording.py +0 -0
  140. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_dataplane.py +0 -0
  141. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_declare.py +0 -0
  142. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_driver.py +0 -0
  143. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_extensions.py +0 -0
  144. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_frames.py +0 -0
  145. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_macos.py +0 -0
  146. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_page.py +0 -0
  147. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_queue.py +0 -0
  148. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_recording.py +0 -0
  149. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_seed.py +0 -0
  150. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_serve.py +0 -0
  151. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_served.py +0 -0
  152. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_session_windows.py +0 -0
  153. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_spaces.py +0 -0
  154. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_svc_forward.py +0 -0
  155. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_tool_prefix.py +0 -0
  156. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_control_token.py +0 -0
  157. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_data_objects.py +0 -0
  158. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_errors.py +0 -0
  159. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_extension_methods.py +0 -0
  160. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_intake.py +0 -0
  161. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_manifest.py +0 -0
  162. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_preflight.py +0 -0
  163. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_access.py +0 -0
  164. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_annotations.py +0 -0
  165. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_client.py +0 -0
  166. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_context.py +0 -0
  167. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_context_fastmcp.py +0 -0
  168. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_context_injection.py +0 -0
  169. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_api.py +0 -0
  170. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_browser.py +0 -0
  171. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_conformance.py +0 -0
  172. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_environment.py +0 -0
  173. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_mcp.py +0 -0
  174. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_notes_example.py +0 -0
  175. {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/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.507
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
@@ -555,6 +555,50 @@ called with the caller's role in `ToolContext`. The site's `/mcp` is one of the
555
555
  browsing route (`/website/<name>/`) never reaches. `python -m agentenv_protocol.website.conformance` checks a declared
556
556
  endpoint (`mcp`): it answers `initialize` and lists the tools the card does.
557
557
 
558
+ ## Live websites
559
+
560
+ 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
561
+ none of its pages: the real site does, and the computer reaches it over its own network. The server holds what agentenv
562
+ knows of the site: its card, a data plane that seeds and reads the task's account through the real site's API, `ready`
563
+ saying the account works, and `session`, the account's cookies and local storage, which `browser_sign_in` loads into the
564
+ browser.
565
+
566
+ ```python
567
+ from pydantic import BaseModel, SecretStr
568
+ from agentenv_protocol.website import WebsiteEnvironment, ready_check, session_state, website
569
+
570
+
571
+ @website(domains=["github.com"], live=True, allow_hosts=["*.githubassets.com", "api.github.com"])
572
+ class GitHub(WebsiteEnvironment):
573
+ class Settings(BaseModel):
574
+ token: SecretStr # in env.toml as a secret:KEY or env:NAME reference
575
+
576
+ @ready_check
577
+ def account_works(self) -> bool:
578
+ ... # the API answers for self.settings.token
579
+
580
+ @session_state
581
+ def account(self, user: str | None) -> dict:
582
+ ... # {"cookies": [...], "local_storage": [...]} that sign a browser in
583
+ ```
584
+
585
+ - **Its names:** `domains` are the real site's, where start tabs may open and `browser_sign_in` sets the session's
586
+ cookies. `allow_hosts` are the other hosts its pages reach, such as its assets and API. A computer whose network is an
587
+ allowlist may reach both; one that allows every host is left as it is. A name a synthetic site of the run also claims
588
+ is that site's.
589
+ - **Its card:** `website/v1` says `live` (`WEBSITE_LIVE_PARAM`) and `allow_hosts` (`WEBSITE_ALLOW_HOSTS_PARAM`) beside
590
+ `domains`, and names no routes. The server serves no `/api/session` page route. `@website` refuses a live site
591
+ with no domains or a static folder, and `allow_hosts` on a site that isn't live.
592
+ - **Its session:** the method marked `@session_state` answers `session {user}`. A live site must mark one, since only
593
+ the real site's cookies sign a browser in to it. Each cookie carries no domain and a `max_age`, as a synthetic
594
+ site's does.
595
+ - **Settings:** any site may declare `Settings`. A deploy hands them to its server in `WEBSITE_SETTINGS_ENV`, a JSON
596
+ object, and the site reads them as `self.settings`, unless it has a member of that name of its own. A `SecretStr`
597
+ field's value in env.toml is a `secret:` or `env:` reference, which agentenv resolves only as it writes the server's
598
+ container, and every record shows it as withheld.
599
+ - **The run:** a run that deploys a live site is marked live. The deploy record says `live`, and the run's metadata lists
600
+ its live sites with `reproducible: false`, because the real site may change between runs.
601
+
558
602
  ## Computer drivers
559
603
 
560
604
  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.507"
4
4
  description = "Open data-plane protocol and server SDK for agent environments."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -10,10 +10,12 @@ from __future__ import annotations
10
10
  from .clock import CLOCK_EXTENSION_URI, UNIVERSE_NOW_HEADER, ClockUnavailable, UniverseClock
11
11
  from .environment import (
12
12
  SESSION_PATH,
13
+ WEBSITE_SETTINGS_ENV,
13
14
  WebsiteDeclaration,
14
15
  WebsiteEnvironment,
15
16
  host_problem,
16
17
  ready_check,
18
+ session_state,
17
19
  website,
18
20
  )
19
21
  from .errors import ApiError, BadRequest, Conflict, Forbidden, NotFound, Unauthorized
@@ -21,8 +23,10 @@ from .readiness import (
21
23
  DEFAULT_HEALTH_PATH,
22
24
  READY_TIMEOUT_S,
23
25
  SITE_READY_TIMEOUT_S,
26
+ WEBSITE_ALLOW_HOSTS_PARAM,
24
27
  WEBSITE_BROWSE_URL_PARAM,
25
28
  WEBSITE_EXTENSION_URI,
29
+ WEBSITE_LIVE_PARAM,
26
30
  WEBSITE_READY_OP,
27
31
  WebsiteReadiness,
28
32
  env_readiness,
@@ -32,11 +36,11 @@ from .routes import ROLE_KEY_ENV, ROLE_KEY_HEADER, ApiContext, ApiRoute, api, re
32
36
  from .session import SESSION_COOKIE, SESSION_SECRET_ENV, SessionSigner
33
37
 
34
38
  __all__ = [
35
- "WebsiteEnvironment", "WebsiteDeclaration", "website", "api", "ApiContext", "ApiRoute", "ready_check", "host_problem",
39
+ "WebsiteEnvironment", "WebsiteDeclaration", "website", "api", "ApiContext", "ApiRoute", "ready_check", "session_state", "host_problem",
36
40
  "ApiError", "BadRequest", "Unauthorized", "Forbidden", "NotFound", "Conflict",
37
41
  "UniverseClock", "ClockUnavailable", "SessionSigner", "reserved_path",
38
42
  "WEBSITE_EXTENSION_URI", "CLOCK_EXTENSION_URI", "UNIVERSE_NOW_HEADER", "DEFAULT_HEALTH_PATH", "SESSION_PATH",
39
43
  "SESSION_COOKIE", "SESSION_SECRET_ENV", "ROLE_KEY_ENV", "ROLE_KEY_HEADER",
40
- "WEBSITE_READY_OP", "WEBSITE_BROWSE_URL_PARAM", "READY_TIMEOUT_S", "SITE_READY_TIMEOUT_S", "WebsiteReadiness",
44
+ "WEBSITE_READY_OP", "WEBSITE_BROWSE_URL_PARAM", "WEBSITE_LIVE_PARAM", "WEBSITE_ALLOW_HOSTS_PARAM", "WEBSITE_SETTINGS_ENV", "READY_TIMEOUT_S", "SITE_READY_TIMEOUT_S", "WebsiteReadiness",
41
45
  "website_readiness", "env_readiness",
42
46
  ]
@@ -14,11 +14,19 @@ an agent can act on the site through tools beside its pages. A site with none se
14
14
 
15
15
  One route is built in, replaced by a site's own ``@api`` route at its very path and answering before one whose path
16
16
  parameters would also match it: ``/api/session``, which a frontend calls to learn who is signed in, signing the acting
17
- user in when nobody is (GET), or to sign out (DELETE)."""
17
+ user in when nobody is (GET), or to sign out (DELETE).
18
+
19
+ A live site, ``@website(live=True, ...)``, is a real site, such as github.com, that its server serves no pages of: the
20
+ real site does, at its ``domains``, its pages reaching the ``allow_hosts`` too. Its server holds what agentenv knows of
21
+ it: the card, a data plane that seeds and reads its account through the real site's API, ``ready`` saying the account
22
+ works, and ``session``, the account's cookies and local storage, from the method marked ``@session_state``. The site's
23
+ ``Settings``, which a deploy hands it as ``WEBSITE_SETTINGS_ENV`` (its credentials, ``SecretStr`` fields, among them), are
24
+ ``self.settings``, unless the site has a member of that name of its own; any site may declare them."""
18
25
  from __future__ import annotations
19
26
 
20
27
  import asyncio
21
28
  import inspect
29
+ import json
22
30
  import math
23
31
  import os
24
32
  import re
@@ -27,6 +35,7 @@ from pathlib import Path
27
35
  from typing import Any, ClassVar, NamedTuple, Optional
28
36
  from urllib.parse import unquote
29
37
 
38
+ from pydantic import BaseModel
30
39
  from starlette.applications import Starlette
31
40
  from starlette.datastructures import MutableHeaders
32
41
  from starlette.middleware import Middleware
@@ -46,15 +55,19 @@ from ..agent_env_environment import (
46
55
  )
47
56
  from .clock import CLOCK_EXTENSION_URI, UNIVERSE_NOW_HEADER, ClockUnavailable, UniverseClock, clock_patch_script, iso
48
57
  from .errors import ApiError, NotFound
49
- from .readiness import SITE_READY_TIMEOUT_S, WEBSITE_EXTENSION_URI, WEBSITE_READY_OP, WebsiteReadiness
58
+ from .readiness import (SITE_READY_TIMEOUT_S, WEBSITE_ALLOW_HOSTS_PARAM, WEBSITE_EXTENSION_URI, WEBSITE_LIVE_PARAM,
59
+ WEBSITE_READY_OP, WebsiteReadiness)
50
60
  from .routes import (ApiContext, ApiRoute, endpoint, error_response, forwarded_prefix, openapi, openapi_path, request_role,
51
61
  reserved_path, routes_of)
52
62
  from .session import SessionSigner, session_cookie_name
53
63
 
54
64
  SESSION_PATH = "/api/session"
65
+ #: The variable a deploy hands a site its settings in, a JSON object of each by its name.
66
+ WEBSITE_SETTINGS_ENV = "AGENTENV_WEBSITE_SETTINGS"
55
67
  CHECK_TIMEOUT_S = 10.0
56
68
  _WEBSITE_ATTR = "_aee_website"
57
69
  _READY_ATTR = "_aee_website_ready"
70
+ _SESSION_ATTR = "_aee_website_session"
58
71
  _HEAD = re.compile(rb"<head(?:\s[^>]*)?>", re.IGNORECASE)
59
72
  _PREAMBLE = re.compile(rb"\A(?:\s|<!--.*?-->|<!doctype[^>]*>|<html(?:\s[^>]*)?>)*", re.IGNORECASE | re.DOTALL)
60
73
  _HOST = re.compile(r"(\*\.)?[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*")
@@ -62,23 +75,37 @@ _HOST = re.compile(r"(\*\.)?[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\.[A-Za-z0-9]
62
75
 
63
76
  class WebsiteDeclaration(NamedTuple):
64
77
  """What ``@website`` declares: the site's domains, its static folder (relative to the class's file), how long it
65
- may take to be ready after its server starts, and whether its HTML pages read the universe's time."""
78
+ may take to be ready after its server starts, whether its HTML pages read the universe's time, and, for a live
79
+ site, that it is one and the other hosts its pages reach."""
66
80
 
67
81
  domains: tuple[str, ...] = ()
68
82
  static: Optional[str] = None
69
83
  ready_timeout_s: float = SITE_READY_TIMEOUT_S
70
84
  clock_patch: bool = False
85
+ live: bool = False
86
+ allow_hosts: tuple[str, ...] = ()
71
87
 
72
88
 
73
89
  def website(_cls: type | None = None, *, domains: list[str] | tuple[str, ...] = (), static: str | None = None,
74
- ready_timeout_s: float = SITE_READY_TIMEOUT_S, clock_patch: bool = False) -> Any:
75
- """Declare a WebsiteEnvironment's site."""
90
+ ready_timeout_s: float = SITE_READY_TIMEOUT_S, clock_patch: bool = False, live: bool = False,
91
+ allow_hosts: list[str] | tuple[str, ...] = ()) -> Any:
92
+ """Declare a WebsiteEnvironment's site; ``live=True`` a real one, at its ``domains``, whose pages reach the
93
+ ``allow_hosts`` too."""
76
94
  if _cls is not None:
77
95
  raise TypeError("use @website(...) with parentheses")
78
- if isinstance(domains, str):
79
- raise ValueError(f"domains is a list of host names, not the string {domains!r}")
80
- if problems := [problem for host in domains if (problem := host_problem(host))]:
81
- raise ValueError(f"domains: {problems[0]}")
96
+ for field, hosts in (("domains", domains), ("allow_hosts", allow_hosts)):
97
+ if isinstance(hosts, str):
98
+ raise ValueError(f"{field} is a list of host names, not the string {hosts!r}")
99
+ if problems := [problem for host in hosts if (problem := host_problem(host))]:
100
+ raise ValueError(f"{field}: {problems[0]}")
101
+ if not isinstance(live, bool):
102
+ raise ValueError(f"live is True or False, not {live!r}")
103
+ if live and not domains:
104
+ raise ValueError("a live site names the domains the real site is at")
105
+ if live and static is not None:
106
+ raise ValueError("a live site serves no pages, the real site does, so it takes no static folder")
107
+ if allow_hosts and not live:
108
+ raise ValueError("allow_hosts are the other hosts a live site's pages reach; declare live=True")
82
109
  if isinstance(ready_timeout_s, bool) or not isinstance(ready_timeout_s, (int, float)) or not (
83
110
  0 < ready_timeout_s < math.inf):
84
111
  raise ValueError(f"ready_timeout_s is a number of seconds above 0, not {ready_timeout_s!r}")
@@ -86,7 +113,8 @@ def website(_cls: type | None = None, *, domains: list[str] | tuple[str, ...] =
86
113
  def deco(cls: type) -> type:
87
114
  if not (isinstance(cls, type) and issubclass(cls, WebsiteEnvironment)):
88
115
  raise TypeError(f"@website declares a WebsiteEnvironment subclass, not {cls!r}")
89
- setattr(cls, _WEBSITE_ATTR, WebsiteDeclaration(tuple(domains), static, float(ready_timeout_s), clock_patch))
116
+ setattr(cls, _WEBSITE_ATTR, WebsiteDeclaration(tuple(domains), static, float(ready_timeout_s), clock_patch, live,
117
+ tuple(allow_hosts)))
90
118
  return cls
91
119
 
92
120
  return deco
@@ -100,6 +128,14 @@ def host_problem(host: Any) -> str | None:
100
128
  return None
101
129
 
102
130
 
131
+ def session_state(fn: Any) -> Any:
132
+ """Mark the method that answers ``website/v1``'s ``session``: it takes ``user``, the account to sign in as or
133
+ None, and returns ``{cookies, local_storage}``, each cookie with no domain and a ``max_age``. A live site marks
134
+ one, since only the real site's cookies sign a browser in to it; a site that marks none signs its own cookie."""
135
+ setattr(fn, _SESSION_ATTR, True)
136
+ return fn
137
+
138
+
103
139
  def ready_check(fn: Any) -> Any:
104
140
  """Mark a method that says whether the site is usable yet: ``website/v1``'s ``ready`` says ready once every one
105
141
  returns a truthy value. They run at once, each within ``CHECK_TIMEOUT_S``, a plain ``def`` in a worker thread."""
@@ -115,6 +151,10 @@ class WebsiteEnvironment(AgentEnvEnvironment):
115
151
  app: ClassVar[Any] = None
116
152
  asgi: Any = None
117
153
 
154
+ class Settings(BaseModel):
155
+ """The site's settings, which a deploy hands it: a site declares its own; a ``SecretStr`` field is a secret,
156
+ whose env.toml value is a ``secret:`` or ``env:`` reference agentenv resolves only as it starts the server."""
157
+
118
158
  def __init_subclass__(cls, **kwargs: Any) -> None:
119
159
  super().__init_subclass__(**kwargs)
120
160
  routes_of(cls)
@@ -124,6 +164,9 @@ class WebsiteEnvironment(AgentEnvEnvironment):
124
164
  self._signer = SessionSigner(cookie_name=session_cookie_name(self._build_card().name))
125
165
  self._started = time.monotonic()
126
166
  self._checking: dict[str, asyncio.Future] = {}
167
+ settings = type(self).Settings.model_validate(json.loads(os.environ.get(WEBSITE_SETTINGS_ENV) or "{}"))
168
+ if not hasattr(type(self), "settings"): # a site's own member named settings, an @api route say, stays its own
169
+ self.settings = settings
127
170
 
128
171
  @classmethod
129
172
  def declaration(cls) -> WebsiteDeclaration:
@@ -180,7 +223,7 @@ class WebsiteEnvironment(AgentEnvEnvironment):
180
223
  else:
181
224
  asgi = Starlette(middleware=middleware)
182
225
  AgentEnvStarletteApplication(self._build_card(), self).add_routes_to_app(asgi)
183
- if SESSION_PATH not in taken:
226
+ if SESSION_PATH not in taken and not declaration.live:
184
227
  asgi.router.routes.append(Route(SESSION_PATH, self._session, methods=["GET", "DELETE"]))
185
228
  asgi.router.routes.extend(Route(r.path, _unreserved(endpoint(getattr(self, r.fn.__name__), r, self.context)),
186
229
  methods=[r.method]) for r in api_routes)
@@ -209,6 +252,9 @@ class WebsiteEnvironment(AgentEnvEnvironment):
209
252
  def extension_params(self, uri: str) -> dict:
210
253
  if uri == WEBSITE_EXTENSION_URI:
211
254
  declaration = self.declaration()
255
+ if declaration.live:
256
+ return {"domains": list(declaration.domains), WEBSITE_LIVE_PARAM: True,
257
+ WEBSITE_ALLOW_HOSTS_PARAM: list(declaration.allow_hosts)}
212
258
  return {"domains": list(declaration.domains),
213
259
  "routes": [r.advertised() for r in self.routes()], "session_cookie": self.session_cookie,
214
260
  "session_path": SESSION_PATH}
@@ -242,6 +288,18 @@ class WebsiteEnvironment(AgentEnvEnvironment):
242
288
 
243
289
  @extension(WEBSITE_EXTENSION_URI, op="session")
244
290
  async def _website_session(self, user: Optional[str] = None) -> dict:
291
+ marked = [method for method, _marker in _marked_members(self, _SESSION_ATTR)]
292
+ if len(marked) > 1:
293
+ raise TypeError(f"{type(self).__qualname__} marks {len(marked)} methods @session_state; mark one")
294
+ if marked:
295
+ state = await _maybe_await(marked[0](user))
296
+ if not (isinstance(state, dict) and isinstance(state.get("cookies", []), list)
297
+ and isinstance(state.get("local_storage", []), list)):
298
+ raise TypeError(f"@session_state {marked[0].__name__} returns {{cookies, local_storage}}, not {state!r}")
299
+ return {"cookies": state.get("cookies", []), "local_storage": state.get("local_storage", [])}
300
+ if self.declaration().live:
301
+ raise TypeError(f"{type(self).__qualname__} is a live site and marks no @session_state method, so it can't "
302
+ "sign a browser in")
245
303
  return {"cookies": [self._session_signer().cookie(user)], "local_storage": []}
246
304
 
247
305
  async def _not_ready(self) -> list[str]:
@@ -20,6 +20,9 @@ from ..client import find_extension_method, invoke_extension
20
20
  WEBSITE_EXTENSION_URI = "urn:agentenv:website/v1"
21
21
  WEBSITE_READY_OP = "ready"
22
22
  WEBSITE_BROWSE_URL_PARAM = "browse_url"
23
+ #: A live site's ``website/v1`` params: that it is one, and the other hosts its pages reach.
24
+ WEBSITE_LIVE_PARAM = "live"
25
+ WEBSITE_ALLOW_HOSTS_PARAM = "allow_hosts"
23
26
  READY_TIMEOUT_S = 10.0
24
27
  SITE_READY_TIMEOUT_S = 300.0
25
28
  DEFAULT_HEALTH_PATH = "/api/health"
@@ -0,0 +1,101 @@
1
+ """A live site on WebsiteEnvironment: a real site, such as github.com, whose server serves none of its pages, only the
2
+ card, with website/v1's live params, a data plane, ready and the account's session from its ``@session_state`` method;
3
+ and a site's ``Settings``, which a deploy hands it in ``WEBSITE_SETTINGS_ENV``."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import asyncio
8
+ import json
9
+
10
+ import pytest
11
+ from pydantic import BaseModel, SecretStr
12
+ from starlette.testclient import TestClient
13
+
14
+ from agentenv_protocol.website import (
15
+ WEBSITE_ALLOW_HOSTS_PARAM,
16
+ WEBSITE_EXTENSION_URI,
17
+ WEBSITE_LIVE_PARAM,
18
+ WEBSITE_SETTINGS_ENV,
19
+ WebsiteEnvironment,
20
+ session_state,
21
+ website,
22
+ )
23
+
24
+
25
+ @website(domains=["github.com"], live=True, allow_hosts=["*.githubassets.com", "api.github.com"])
26
+ class GitHub(WebsiteEnvironment):
27
+ class Settings(BaseModel):
28
+ token: SecretStr
29
+ org: str = "octo"
30
+
31
+ @session_state
32
+ async def account(self, user: str | None) -> dict:
33
+ value = f"{user}:{self.settings.token.get_secret_value()}"
34
+ return {"cookies": [{"name": "user_session", "value": value, "path": "/", "max_age": 60}], "local_storage": []}
35
+
36
+
37
+ @pytest.fixture
38
+ def github(monkeypatch) -> GitHub:
39
+ monkeypatch.setenv(WEBSITE_SETTINGS_ENV, json.dumps({"token": "t0"}))
40
+ return GitHub()
41
+
42
+
43
+ def _params(client: TestClient) -> dict:
44
+ card = client.get("/.well-known/agent-env.json").json()
45
+ return next(e for e in card["capabilities"]["extensions"] if e["uri"] == WEBSITE_EXTENSION_URI)["params"]
46
+
47
+
48
+ def test_a_live_sites_card_names_the_real_site_and_the_hosts_its_pages_reach_and_it_serves_no_pages(github) -> None:
49
+ client = TestClient(github.create_app())
50
+
51
+ params = _params(client)
52
+
53
+ assert {key: params[key] for key in ("domains", WEBSITE_LIVE_PARAM, WEBSITE_ALLOW_HOSTS_PARAM)} == {
54
+ "domains": ["github.com"], WEBSITE_LIVE_PARAM: True,
55
+ WEBSITE_ALLOW_HOSTS_PARAM: ["*.githubassets.com", "api.github.com"]}
56
+ assert not {"routes", "session_cookie", "session_path"} & params.keys()
57
+ assert params["methods"]["session"]["endpoint"] == "/agentenv/ext/website/session"
58
+ assert (client.get("/").status_code, client.get("/api/session").status_code) == (404, 404)
59
+
60
+
61
+ def test_a_live_sites_session_is_its_accounts_as_its_session_state_method_says(github) -> None:
62
+ client = TestClient(github.create_app())
63
+
64
+ state = client.post("/agentenv/ext/website/session", json={"user": "alex"}).json()
65
+
66
+ assert state == {"cookies": [{"name": "user_session", "value": "alex:t0", "path": "/", "max_age": 60}],
67
+ "local_storage": []}
68
+
69
+
70
+ def test_a_sites_settings_arrive_from_the_deploy_and_a_secret_stays_one(github, monkeypatch) -> None:
71
+ assert (github.settings.org, github.settings.token.get_secret_value()) == ("octo", "t0")
72
+ assert "t0" not in repr(github.settings)
73
+ monkeypatch.delenv(WEBSITE_SETTINGS_ENV)
74
+
75
+ @website(domains=["shop.test"])
76
+ class Shop(WebsiteEnvironment):
77
+ pass
78
+
79
+ assert Shop().settings.model_dump() == {}
80
+
81
+
82
+ def test_a_live_site_marking_no_session_state_method_cant_sign_a_browser_in() -> None:
83
+ @website(domains=["github.com"], live=True)
84
+ class Bare(WebsiteEnvironment):
85
+ pass
86
+
87
+ with pytest.raises(TypeError, match="marks no @session_state method"):
88
+ asyncio.run(Bare()._website_session("alex"))
89
+
90
+
91
+ @pytest.mark.parametrize(("kwargs", "problem"), [
92
+ ({"live": True}, "names the domains the real site is at"),
93
+ ({"domains": ["github.com"], "live": True, "static": "dist/"}, "serves no pages"),
94
+ ({"domains": ["github.com"], "allow_hosts": ["api.github.com"]}, "declare live=True"),
95
+ ({"domains": ["github.com"], "live": True, "allow_hosts": "api.github.com"}, "not the string"),
96
+ ({"domains": ["github.com"], "live": True, "allow_hosts": ["https://api.github.com"]}, "not a host name"),
97
+ ({"domains": ["github.com"], "live": "yes"}, "True or False"),
98
+ ], ids=["no-domains", "static", "allow-hosts-not-live", "allow-hosts-string", "allow-hosts-url", "live-not-bool"])
99
+ def test_website_refuses_a_live_declaration_that_cant_be_one(kwargs, problem) -> None:
100
+ with pytest.raises(ValueError, match=problem):
101
+ website(**kwargs)