agentenv-framework-protocol 0.1.376__tar.gz → 0.1.378__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 (99) hide show
  1. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/PKG-INFO +39 -3
  2. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/README.md +38 -2
  3. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/pyproject.toml +1 -1
  4. agentenv_framework_protocol-0.1.378/src/agentenv_protocol/computer/_declare.py +98 -0
  5. agentenv_framework_protocol-0.1.378/src/agentenv_protocol/computer/conformance/__init__.py +22 -0
  6. agentenv_framework_protocol-0.1.378/src/agentenv_protocol/computer/conformance/__main__.py +42 -0
  7. agentenv_framework_protocol-0.1.378/src/agentenv_protocol/computer/conformance/checks.py +726 -0
  8. agentenv_framework_protocol-0.1.378/src/agentenv_protocol/computer/conformance/client.py +82 -0
  9. agentenv_framework_protocol-0.1.378/src/agentenv_protocol/computer/conformance/fixture.py +208 -0
  10. agentenv_framework_protocol-0.1.378/src/agentenv_protocol/computer/conformance/report.py +43 -0
  11. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/driver.py +13 -2
  12. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/platform.py +16 -0
  13. agentenv_framework_protocol-0.1.378/tests/probe_fake.py +210 -0
  14. agentenv_framework_protocol-0.1.378/tests/test_computer_conformance.py +257 -0
  15. agentenv_framework_protocol-0.1.378/tests/test_computer_conformance_checks.py +283 -0
  16. agentenv_framework_protocol-0.1.378/tests/test_computer_declare.py +116 -0
  17. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_computer_driver.py +30 -1
  18. agentenv_framework_protocol-0.1.376/examples/computer_x11/Dockerfile +0 -19
  19. agentenv_framework_protocol-0.1.376/examples/computer_x11/README.md +0 -60
  20. agentenv_framework_protocol-0.1.376/examples/computer_x11/driver.py +0 -374
  21. agentenv_framework_protocol-0.1.376/examples/computer_x11/fixture.py +0 -173
  22. agentenv_framework_protocol-0.1.376/examples/computer_x11/start.sh +0 -9
  23. agentenv_framework_protocol-0.1.376/tests/test_computer_x11_example.py +0 -290
  24. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/.gitignore +0 -0
  25. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/LICENSE +0 -0
  26. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/NOTICE +0 -0
  27. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/THIRD_PARTY_NOTICES.md +0 -0
  28. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/examples/__init__.py +0 -0
  29. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/examples/advanced_agent.py +0 -0
  30. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/examples/basic_agent.py +0 -0
  31. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/examples/custom_extensions_agent.py +0 -0
  32. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/examples/multimodal_agent.py +0 -0
  33. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/examples/reference_model.py +0 -0
  34. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/examples/streaming_agent.py +0 -0
  35. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/__init__.py +0 -0
  36. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/a2a_agent/__init__.py +0 -0
  37. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/a2a_agent/_trajectory_log.py +0 -0
  38. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
  39. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/a2a_agent/extensions.py +0 -0
  40. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
  41. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/a2a_agent/peer_agents.py +0 -0
  42. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/a2a_agent/proxy.py +0 -0
  43. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
  44. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/a2a_agent/staging.py +0 -0
  45. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
  46. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
  47. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/agent_env_environment.py +0 -0
  48. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/client.py +0 -0
  49. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/__init__.py +0 -0
  50. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/card.py +0 -0
  51. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/checks.py +0 -0
  52. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/dataplane.py +0 -0
  53. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/errors.py +0 -0
  54. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/extensions.py +0 -0
  55. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/frames.py +0 -0
  56. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/keys.py +0 -0
  57. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/linux.py +0 -0
  58. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/operations.py +0 -0
  59. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/queue.py +0 -0
  60. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/results.py +0 -0
  61. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/runtime.py +0 -0
  62. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/settings.py +0 -0
  63. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/spaces.py +0 -0
  64. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/computer/tools.py +0 -0
  65. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/errors.py +0 -0
  66. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/manifest.py +0 -0
  67. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/preflight.py +0 -0
  68. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/tool_access.py +0 -0
  69. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/tool_client.py +0 -0
  70. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/tool_context.py +0 -0
  71. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/transfers.py +0 -0
  72. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/src/agentenv_protocol/types.py +0 -0
  73. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/computer_fake.py +0 -0
  74. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_a2a_agent.py +0 -0
  75. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_a2a_agent_examples.py +0 -0
  76. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_a2a_object_transfers.py +0 -0
  77. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_a2a_peer_agents.py +0 -0
  78. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_a2a_proxy.py +0 -0
  79. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_a2a_staging.py +0 -0
  80. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_a2a_trajectory_log.py +0 -0
  81. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_agent_env_environment.py +0 -0
  82. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_client.py +0 -0
  83. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_computer_dataplane.py +0 -0
  84. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_computer_extensions.py +0 -0
  85. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_computer_frames.py +0 -0
  86. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_computer_queue.py +0 -0
  87. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_computer_spaces.py +0 -0
  88. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_data_objects.py +0 -0
  89. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_errors.py +0 -0
  90. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_extension_methods.py +0 -0
  91. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_intake.py +0 -0
  92. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_manifest.py +0 -0
  93. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_preflight.py +0 -0
  94. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_tool_access.py +0 -0
  95. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_tool_annotations.py +0 -0
  96. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_tool_client.py +0 -0
  97. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_tool_context.py +0 -0
  98. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_tool_context_fastmcp.py +0 -0
  99. {agentenv_framework_protocol-0.1.376 → agentenv_framework_protocol-0.1.378}/tests/test_tool_context_injection.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentenv-framework-protocol
3
- Version: 0.1.376
3
+ Version: 0.1.378
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
@@ -370,8 +370,8 @@ one as a `ComputerDriverEnvironment` subclass from `agentenv_protocol.computer`,
370
370
  `computer` extra (`pip install 'agentenv-framework-protocol[computer]'`: mcp, uvicorn and Pillow).
371
371
  The driver marks the methods that capture the screen and send input; the base class serves the
372
372
  computer contract from them, so the tools, results and errors can't drift from it. A complete
373
- driver for an X11 desktop, with an image to run it in, is in
374
- [`examples/computer_x11/`](https://github.com/scaleapi/agentenv-framework/tree/main/packages/agentenv-protocol/examples/computer_x11).
373
+ driver for an X11 desktop is agent-env's reference driver, in its
374
+ [`drivers` bundle](https://github.com/scaleapi/agentenv-framework/tree/main/src/agent_env/drivers).
375
375
 
376
376
  ```python
377
377
  from agentenv_protocol.computer import (
@@ -473,6 +473,42 @@ and `exec_timeout_s`. A driver reads them as `self.settings`, for example its ta
473
473
  Every caller is `default` until agents carry an identity of their own; `caller_identity(context)`
474
474
  names the caller whose latest frame and others' input a call uses.
475
475
 
476
+ `MyDriver.declaration(platform)` returns the card the class serves on a `Platform`, built without
477
+ running the driver, its operations or its dependencies.
478
+
479
+ ### Checking a driver: the conformance probe
480
+
481
+ The probe checks a running driver against the contract through its card, tools and extensions. It
482
+ reports each check, and gives each extension the card declares a verdict: verified, unverified or
483
+ failed.
484
+
485
+ ```bash
486
+ python -m agentenv_protocol.computer.conformance http://localhost:18765 [--level basic|full] [--json report.json]
487
+ ```
488
+
489
+ ```python
490
+ from agentenv_protocol.computer.conformance import run_probe
491
+
492
+ report = await run_probe("http://localhost:18765", "full") # a ProbeReport
493
+ failed = [c for c in report.checks if c.status == "failed"]
494
+ ```
495
+
496
+ The CLI exits 0 when no check failed, 1 when one did and 2 when it can't reach the driver.
497
+
498
+ | Level | Checks | Needs on the computer |
499
+ |---|---|---|
500
+ | `basic` | P0 ready and a screenshot; P1 the card and tool schemas; P2 native frames, regions and JPEG; P5 refusals and their codes; X the declared extensions (`computer-display`, `computer-spaces`, `computer-keyboard`, `computer-exec`, `data/get`, per-role tool access, the pointer) | nothing |
501
+ | `full` | `basic`, then P3 colours land where the mapping says; P4 clicks show their effect; P5 refusals change nothing; P6 typed and pressed keys arrive; P7 scrolls and drags arrive; P9 commands and `data/add` run as one unprivileged user; X pointer and keyboard input, `data/add` | Linux, `python3` with Tk, a 1280x800 screen or larger, `computer-exec` and `data/add` |
502
+
503
+ At `full` the probe writes the Fixture Desk, a Tk window (`conformance/fixture.py`), into
504
+ `~/.agentenv-probe` with `data/add` and starts it with `computer-exec`. It aims at the boxes the
505
+ window's widgets report, reads back what reached them, and then removes the fixture. When the
506
+ fixture can't be staged, the `full` checks are unverified rather than failed. The probe leaves out
507
+ the checks for an extension the card doesn't declare. P8 is not applicable to a driver without
508
+ leasing, and unverified for one with it until the probe has lease checks. The probe calls as the
509
+ default role; the tool-access check hides a tool from a role of its own, `agentenv-probe-<random>`,
510
+ and gives it back.
511
+
476
512
  ## A2A agent framework
477
513
 
478
514
  The distribution exposes two unrelated decorators named `extension`:
@@ -319,8 +319,8 @@ one as a `ComputerDriverEnvironment` subclass from `agentenv_protocol.computer`,
319
319
  `computer` extra (`pip install 'agentenv-framework-protocol[computer]'`: mcp, uvicorn and Pillow).
320
320
  The driver marks the methods that capture the screen and send input; the base class serves the
321
321
  computer contract from them, so the tools, results and errors can't drift from it. A complete
322
- driver for an X11 desktop, with an image to run it in, is in
323
- [`examples/computer_x11/`](https://github.com/scaleapi/agentenv-framework/tree/main/packages/agentenv-protocol/examples/computer_x11).
322
+ driver for an X11 desktop is agent-env's reference driver, in its
323
+ [`drivers` bundle](https://github.com/scaleapi/agentenv-framework/tree/main/src/agent_env/drivers).
324
324
 
325
325
  ```python
326
326
  from agentenv_protocol.computer import (
@@ -422,6 +422,42 @@ and `exec_timeout_s`. A driver reads them as `self.settings`, for example its ta
422
422
  Every caller is `default` until agents carry an identity of their own; `caller_identity(context)`
423
423
  names the caller whose latest frame and others' input a call uses.
424
424
 
425
+ `MyDriver.declaration(platform)` returns the card the class serves on a `Platform`, built without
426
+ running the driver, its operations or its dependencies.
427
+
428
+ ### Checking a driver: the conformance probe
429
+
430
+ The probe checks a running driver against the contract through its card, tools and extensions. It
431
+ reports each check, and gives each extension the card declares a verdict: verified, unverified or
432
+ failed.
433
+
434
+ ```bash
435
+ python -m agentenv_protocol.computer.conformance http://localhost:18765 [--level basic|full] [--json report.json]
436
+ ```
437
+
438
+ ```python
439
+ from agentenv_protocol.computer.conformance import run_probe
440
+
441
+ report = await run_probe("http://localhost:18765", "full") # a ProbeReport
442
+ failed = [c for c in report.checks if c.status == "failed"]
443
+ ```
444
+
445
+ The CLI exits 0 when no check failed, 1 when one did and 2 when it can't reach the driver.
446
+
447
+ | Level | Checks | Needs on the computer |
448
+ |---|---|---|
449
+ | `basic` | P0 ready and a screenshot; P1 the card and tool schemas; P2 native frames, regions and JPEG; P5 refusals and their codes; X the declared extensions (`computer-display`, `computer-spaces`, `computer-keyboard`, `computer-exec`, `data/get`, per-role tool access, the pointer) | nothing |
450
+ | `full` | `basic`, then P3 colours land where the mapping says; P4 clicks show their effect; P5 refusals change nothing; P6 typed and pressed keys arrive; P7 scrolls and drags arrive; P9 commands and `data/add` run as one unprivileged user; X pointer and keyboard input, `data/add` | Linux, `python3` with Tk, a 1280x800 screen or larger, `computer-exec` and `data/add` |
451
+
452
+ At `full` the probe writes the Fixture Desk, a Tk window (`conformance/fixture.py`), into
453
+ `~/.agentenv-probe` with `data/add` and starts it with `computer-exec`. It aims at the boxes the
454
+ window's widgets report, reads back what reached them, and then removes the fixture. When the
455
+ fixture can't be staged, the `full` checks are unverified rather than failed. The probe leaves out
456
+ the checks for an extension the card doesn't declare. P8 is not applicable to a driver without
457
+ leasing, and unverified for one with it until the probe has lease checks. The probe calls as the
458
+ default role; the tool-access check hides a tool from a role of its own, `agentenv-probe-<random>`,
459
+ and gives it back.
460
+
425
461
  ## A2A agent framework
426
462
 
427
463
  The distribution exposes two unrelated decorators named `extension`:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentenv-framework-protocol"
3
- version = "0.1.376"
3
+ version = "0.1.378"
4
4
  description = "Open data-plane protocol and server SDK for agent environments."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -0,0 +1,98 @@
1
+ """Read a computer driver class where its own dependencies may not be installed, as a ``computer_driver`` env's put
2
+ does: ``python -m agentenv_protocol.computer._declare <folder> <file> <class>`` imports the file with every module
3
+ not installed here standing in as an inert stub, and prints what the class declares for each platform it supports as
4
+ JSON. Nothing runs beyond defining the module and its classes, and a class that can't be defined even so exits 1
5
+ with why."""
6
+ from __future__ import annotations
7
+
8
+ import contextlib
9
+ import importlib.abc
10
+ import importlib.machinery
11
+ import importlib.util
12
+ import json
13
+ import sys
14
+ import types
15
+ from pathlib import Path
16
+
17
+ from .card import COMPUTER
18
+ from .driver import ComputerDriverEnvironment
19
+ from .platform import supported
20
+
21
+
22
+ class _StubMeta(type):
23
+ def __getattr__(cls, name: str):
24
+ if name.startswith("__"):
25
+ raise AttributeError(name)
26
+ return cls
27
+
28
+ def __call__(cls, *args, **kwargs):
29
+ return cls
30
+
31
+ def __getitem__(cls, key):
32
+ return cls
33
+
34
+
35
+ class _Stub(metaclass=_StubMeta):
36
+ """Whatever a stubbed module holds: itself again on any attribute, call or index, and a class to derive from."""
37
+
38
+
39
+ class _StubModule(types.ModuleType):
40
+ __path__: list = []
41
+
42
+ def __getattr__(self, name: str):
43
+ if name.startswith("__"):
44
+ raise AttributeError(name)
45
+ return _Stub
46
+
47
+
48
+ class _Stubs(importlib.abc.MetaPathFinder, importlib.abc.Loader):
49
+ """Last on ``sys.meta_path``: a module no other finder finds imports as a stub."""
50
+
51
+ def find_spec(self, fullname, path, target=None):
52
+ return importlib.machinery.ModuleSpec(fullname, self, is_package=True)
53
+
54
+ def create_module(self, spec):
55
+ return _StubModule(spec.name)
56
+
57
+ def exec_module(self, module) -> None:
58
+ return None
59
+
60
+
61
+ def declare(folder: Path, file: str, name: str) -> dict:
62
+ """What the class ``name`` in ``folder/file`` declares: its card's name, its ``supports``, and for each platform
63
+ those name, the extensions it provides, its tools and the computer/v1 features. Raises ValueError for a class
64
+ that isn't a driver or declares no platform."""
65
+ sys.path.insert(0, str(folder))
66
+ sys.meta_path.append(_Stubs())
67
+ spec = importlib.util.spec_from_file_location("computer_driver_module", folder / file)
68
+ module = importlib.util.module_from_spec(spec)
69
+ spec.loader.exec_module(module)
70
+ cls = getattr(module, name, None)
71
+ if not (isinstance(cls, type) and issubclass(cls, ComputerDriverEnvironment)):
72
+ raise ValueError(f"{file} has no ComputerDriverEnvironment subclass named {name}")
73
+ platforms = [platform for entry in cls.supports for platform in supported(entry)]
74
+ if not platforms:
75
+ raise ValueError(f"{name} declares no platform: name them with @computer_driver(supports=[...])")
76
+ cards = [(platform, cls.declaration(platform)) for platform in platforms]
77
+ return {"name": cards[0][1].name, "supports": list(cls.supports), "platforms": [
78
+ {"platform": platform.as_dict(), "provides": [e.uri for e in card.capabilities.extensions],
79
+ "tools": [t.name for t in card.capabilities.tools],
80
+ "features": next(e.params for e in card.capabilities.extensions if e.uri == COMPUTER)}
81
+ for platform, card in cards]}
82
+
83
+
84
+ def main(argv: list[str]) -> int:
85
+ """Print the declaration as the only output; what the module prints while it loads goes to stderr."""
86
+ folder, file, name = argv
87
+ try:
88
+ with contextlib.redirect_stdout(sys.stderr):
89
+ declared = declare(Path(folder), file, name)
90
+ except Exception as e:
91
+ print(f"{type(e).__name__}: {e}", file=sys.stderr)
92
+ return 1
93
+ print(json.dumps(declared))
94
+ return 0
95
+
96
+
97
+ if __name__ == "__main__":
98
+ sys.exit(main(sys.argv[1:]))
@@ -0,0 +1,22 @@
1
+ """The conformance probe: checks a running computer driver against the contract through its card, tools and
2
+ extensions, and reports each check and each declared extension. ``basic`` needs nothing on the computer; ``full``
3
+ stages the Linux fixture with data/add and reads it back with computer-exec. Run it as
4
+ ``python -m agentenv_protocol.computer.conformance <driver url> [--level basic|full] [--json report.json]``."""
5
+ from __future__ import annotations
6
+
7
+ from .checks import Probe
8
+ from .client import Driver
9
+ from .report import CheckResult, Level, ProbeReport, Status, Verdict
10
+
11
+ __all__ = ["run_probe", "ProbeReport", "CheckResult", "Level", "Status", "Verdict"]
12
+
13
+
14
+ async def run_probe(url: str, level: Level = "full", *, timeout_s: float = 60, ready_timeout_s: float = 60) -> ProbeReport:
15
+ """Probe the driver serving its card at ``url``. ``ready_timeout_s`` is how long it may take to report ready."""
16
+ driver = await Driver.connect(url, timeout_s)
17
+ async with driver.session() as session:
18
+ probe = Probe(driver, session, ready_timeout_s)
19
+ await probe.basic()
20
+ if level == "full":
21
+ await probe.full()
22
+ return ProbeReport.of(level, driver.name, probe.results, driver.extensions)
@@ -0,0 +1,42 @@
1
+ """``python -m agentenv_protocol.computer.conformance <driver url>``: probe a running driver, print each check, and
2
+ exit 1 when one failed or 2 when the driver couldn't be reached."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ from . import run_probe
11
+
12
+ _MARK = {"passed": "PASS", "failed": "FAIL", "unverified": "----", "not_applicable": "n/a "}
13
+
14
+
15
+ def main(argv: list[str] | None = None) -> int:
16
+ parser = argparse.ArgumentParser(prog="python -m agentenv_protocol.computer.conformance",
17
+ description="Probe a running computer driver against the contract.")
18
+ parser.add_argument("url", help="where the driver serves its card, such as http://localhost:18765")
19
+ parser.add_argument("--level", choices=("basic", "full"), default="full")
20
+ parser.add_argument("--json", type=Path, help="also write the report to this file")
21
+ parser.add_argument("--timeout", type=float, default=60, help="seconds for each call, and to report ready")
22
+ args = parser.parse_args(argv)
23
+ try:
24
+ report = asyncio.run(run_probe(args.url, args.level, timeout_s=args.timeout, ready_timeout_s=args.timeout))
25
+ except Exception as e:
26
+ print(f"couldn't probe {args.url}: {type(e).__name__}: {e}", file=sys.stderr)
27
+ return 2
28
+ for result in report.checks:
29
+ detail = f": {result.detail}" if result.detail else ""
30
+ print(f"{_MARK[result.status]} {result.check} {result.name}{detail}")
31
+ counts = report.counts()
32
+ print(f"{report.driver}: {counts['passed']} passed, {counts['failed']} failed, {counts['unverified']} unverified, "
33
+ f"{counts['not_applicable']} not applicable at {report.level}")
34
+ for uri, verdict in report.extensions.items():
35
+ print(f" {verdict:10} {uri}")
36
+ if args.json:
37
+ args.json.write_text(report.model_dump_json(indent=1))
38
+ return 0 if report.passed else 1
39
+
40
+
41
+ if __name__ == "__main__":
42
+ sys.exit(main())