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.
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/PKG-INFO +45 -1
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/README.md +44 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/pyproject.toml +1 -1
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/__init__.py +6 -2
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/environment.py +69 -11
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/readiness.py +3 -0
- agentenv_framework_protocol-0.1.507/tests/test_website_live.py +101 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/.gitignore +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/LICENSE +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/NOTICE +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/THIRD_PARTY_NOTICES.md +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/advanced_agent.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/basic_agent.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/custom_extensions_agent.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/multimodal_agent.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/reference_model.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/streaming_agent.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/.gitignore +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/Dockerfile +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/Dockerfile.dockerignore +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/README.md +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/data/seed.json +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/env.toml +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/site.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/index.html +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/package-lock.json +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/package.json +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/src/main.js +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/src/style.css +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/website/notes/web/vite.config.js +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/_trajectory_log.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/extensions.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/peer_agents.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/proxy.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/staging.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/agent_env_environment.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/browser_cdp.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/client.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_declare.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_job.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_linux.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_load.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_macos.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_reaping.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_session_posix.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_session_windows.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_with_fds.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/_x11.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/app.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/app_data.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/app_runtime.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/apps.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/card.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/checks.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/__main__.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/checks.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/client.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/desk.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/fixture.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/mp4.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/page_fixture.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/report.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/conformance/wayland_fixture.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/dataplane.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/driver.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/errors.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/extensions.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/frames.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/keys.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/launch.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/manifest.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/operations.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/platform.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/prefix.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/queue.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/recording.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/results.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/run_install_step.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/runtime.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/seed.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/serve.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/serve_app.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/served.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/session.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/settings.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/skill.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/spaces.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/svc.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/computer/tools.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/errors.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/manifest.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/preflight.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/tool_access.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/tool_client.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/tool_context.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/transfers.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/types.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/clock.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/conformance.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/errors.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/routes.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website/session.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/control.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/data.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/devtools.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/environment.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/src/agentenv_protocol/website_browser/shapes.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/computer_fake.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/probe_fake.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_agent.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_agent_examples.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_cdp_config.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_object_transfers.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_peer_agents.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_proxy.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_staging.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_trajectory_log.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_agent_env_environment.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_browser_cdp.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_browser_data.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_browser_state.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_client.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_app_data.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_app_server.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_conformance.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_conformance_checks.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_conformance_page.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_conformance_recording.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_dataplane.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_declare.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_driver.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_extensions.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_frames.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_macos.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_page.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_queue.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_recording.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_seed.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_serve.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_served.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_session_windows.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_spaces.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_svc_forward.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_computer_tool_prefix.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_control_token.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_data_objects.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_errors.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_extension_methods.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_intake.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_manifest.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_preflight.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_access.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_annotations.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_client.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_context.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_context_fastmcp.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_tool_context_injection.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_api.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_browser.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_conformance.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_environment.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_mcp.py +0 -0
- {agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_website_notes_example.py +0 -0
- {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.
|
|
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
|
|
@@ -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,
|
|
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,
|
|
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
|
|
75
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/THIRD_PARTY_NOTICES.md
RENAMED
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/examples/basic_agent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/computer_fake.py
RENAMED
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/probe_fake.py
RENAMED
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_agent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_a2a_proxy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_errors.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_intake.py
RENAMED
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_manifest.py
RENAMED
|
File without changes
|
{agentenv_framework_protocol-0.1.506 → agentenv_framework_protocol-0.1.507}/tests/test_preflight.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|