agentenv-framework-protocol 0.1.396__tar.gz → 0.1.398__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 (104) hide show
  1. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/PKG-INFO +44 -1
  2. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/README.md +43 -0
  3. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/pyproject.toml +1 -1
  4. agentenv_framework_protocol-0.1.398/src/agentenv_protocol/computer/__init__.py +112 -0
  5. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/_declare.py +26 -19
  6. agentenv_framework_protocol-0.1.398/src/agentenv_protocol/computer/app.py +178 -0
  7. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/driver.py +25 -0
  8. agentenv_framework_protocol-0.1.398/tests/test_computer_declare.py +222 -0
  9. agentenv_framework_protocol-0.1.396/src/agentenv_protocol/computer/__init__.py +0 -72
  10. agentenv_framework_protocol-0.1.396/tests/test_computer_declare.py +0 -116
  11. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/.gitignore +0 -0
  12. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/LICENSE +0 -0
  13. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/NOTICE +0 -0
  14. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/THIRD_PARTY_NOTICES.md +0 -0
  15. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/examples/__init__.py +0 -0
  16. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/examples/advanced_agent.py +0 -0
  17. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/examples/basic_agent.py +0 -0
  18. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/examples/custom_extensions_agent.py +0 -0
  19. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/examples/multimodal_agent.py +0 -0
  20. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/examples/reference_model.py +0 -0
  21. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/examples/streaming_agent.py +0 -0
  22. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/__init__.py +0 -0
  23. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/a2a_agent/__init__.py +0 -0
  24. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/a2a_agent/_trajectory_log.py +0 -0
  25. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
  26. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/a2a_agent/extensions.py +0 -0
  27. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
  28. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/a2a_agent/peer_agents.py +0 -0
  29. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/a2a_agent/proxy.py +0 -0
  30. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
  31. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/a2a_agent/staging.py +0 -0
  32. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
  33. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
  34. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/agent_env_environment.py +0 -0
  35. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/client.py +0 -0
  36. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/card.py +0 -0
  37. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/checks.py +0 -0
  38. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/conformance/__init__.py +0 -0
  39. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/conformance/__main__.py +0 -0
  40. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/conformance/checks.py +0 -0
  41. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/conformance/client.py +0 -0
  42. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/conformance/fixture.py +0 -0
  43. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/conformance/mp4.py +0 -0
  44. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/conformance/report.py +0 -0
  45. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/dataplane.py +0 -0
  46. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/errors.py +0 -0
  47. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/extensions.py +0 -0
  48. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/frames.py +0 -0
  49. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/keys.py +0 -0
  50. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/linux.py +0 -0
  51. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/manifest.py +0 -0
  52. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/operations.py +0 -0
  53. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/platform.py +0 -0
  54. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/queue.py +0 -0
  55. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/recording.py +0 -0
  56. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/results.py +0 -0
  57. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/runtime.py +0 -0
  58. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/serve.py +0 -0
  59. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/settings.py +0 -0
  60. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/skill.py +0 -0
  61. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/spaces.py +0 -0
  62. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/computer/tools.py +0 -0
  63. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/errors.py +0 -0
  64. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/manifest.py +0 -0
  65. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/preflight.py +0 -0
  66. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/tool_access.py +0 -0
  67. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/tool_client.py +0 -0
  68. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/tool_context.py +0 -0
  69. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/transfers.py +0 -0
  70. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/src/agentenv_protocol/types.py +0 -0
  71. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/computer_fake.py +0 -0
  72. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/probe_fake.py +0 -0
  73. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_a2a_agent.py +0 -0
  74. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_a2a_agent_examples.py +0 -0
  75. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_a2a_object_transfers.py +0 -0
  76. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_a2a_peer_agents.py +0 -0
  77. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_a2a_proxy.py +0 -0
  78. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_a2a_staging.py +0 -0
  79. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_a2a_trajectory_log.py +0 -0
  80. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_agent_env_environment.py +0 -0
  81. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_client.py +0 -0
  82. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_computer_conformance.py +0 -0
  83. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_computer_conformance_checks.py +0 -0
  84. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_computer_conformance_recording.py +0 -0
  85. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_computer_dataplane.py +0 -0
  86. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_computer_driver.py +0 -0
  87. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_computer_extensions.py +0 -0
  88. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_computer_frames.py +0 -0
  89. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_computer_queue.py +0 -0
  90. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_computer_recording.py +0 -0
  91. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_computer_serve.py +0 -0
  92. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_computer_spaces.py +0 -0
  93. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_data_objects.py +0 -0
  94. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_errors.py +0 -0
  95. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_extension_methods.py +0 -0
  96. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_intake.py +0 -0
  97. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_manifest.py +0 -0
  98. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_preflight.py +0 -0
  99. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_tool_access.py +0 -0
  100. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_tool_annotations.py +0 -0
  101. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_tool_client.py +0 -0
  102. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_tool_context.py +0 -0
  103. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/tests/test_tool_context_fastmcp.py +0 -0
  104. {agentenv_framework_protocol-0.1.396 → agentenv_framework_protocol-0.1.398}/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.396
3
+ Version: 0.1.398
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
@@ -590,6 +590,49 @@ leasing, and unverified for one with it until the probe has lease checks. The pr
590
590
  default role; the tool-access check hides a tool from a role of its own, `agentenv-probe-<random>`,
591
591
  and gives it back.
592
592
 
593
+ ## Computer apps
594
+
595
+ A computer app is software an agent uses on a computer, beside the driver: a desktop app, a service
596
+ or both. Write one as a `ComputerAppEnvironment` subclass from `agentenv_protocol.computer`, an
597
+ `AgentEnvEnvironment` whose class also says what it is to a computer. It needs only the plain
598
+ package: `agentenv_protocol.computer` imports what a driver needs (mcp, uvicorn, Pillow, PyAV) only
599
+ when a driver's names are used.
600
+
601
+ ```python
602
+ from pydantic import BaseModel, SecretStr
603
+ from agentenv_protocol.computer import ComputerAppEnvironment, Launch, Linux, Service, computer_app
604
+
605
+ @computer_app(supports=[{"os": {"type": "linux", "display": ["x11"]}, "arch": ["amd64", "arm64"]}])
606
+ class MailApp(ComputerAppEnvironment):
607
+ linux = Linux(python="requirements.txt", services=[Service(name="imap", command="venv/bin/python imap.py")])
608
+ launch = Launch(command=["thunderbird"], window={"class": "thunderbird"})
609
+ skill = "skill.md"
610
+
611
+ class Settings(BaseModel):
612
+ signature: str = ""
613
+ password: SecretStr = SecretStr("")
614
+ ```
615
+
616
+ - **`@computer_app`** names the platforms the app supports, as a driver names them, and the
617
+ `permissions` it needs of the computer. Its env.toml names the rest: the app itself
618
+ (`environment_name`) and the `computer` and `driver` it deploys alone on.
619
+ - **`linux = Linux(...)`** is how a Linux computer installs it: `files`, a folder of the app's
620
+ copied under its prefix; `python`, a requirements file there installed into a venv beside them;
621
+ `services`, programs the computer's init runs from the prefix as the session user.
622
+ - **`launch = Launch(...)`** says how it starts in the desktop session, with it (`at="boot"`) or when
623
+ the agent opens it (`"demand"`), and `ready=Ready(...)` when it's ready; `@ready_check` marks a
624
+ method for readiness that needs logic, and `@install_step(scope=...)` one a computer runs as it
625
+ installs the app.
626
+ - **Its skill** is a file of its folder (`skill = "skill.md"`) or an `@agent_skill` method, not both;
627
+ its data hooks are the usual `@add_data`, `@get_data` and `@reset_data`.
628
+ - **`Settings`** is typed; a computer hands each app the values a deploy gives it, as one JSON value
629
+ in `APP_SETTINGS_ENV` (`AGENTENV_APP_SETTINGS`, the apps' values by name), and the base class reads
630
+ its own, by `ENVIRONMENT_NAME`, as `self.settings`. A `SecretStr` field is a secret.
631
+
632
+ `MailApp.declaration()` returns what the class declares, an `AppDeclaration`, which a `computer_app`
633
+ env stores, and `python -m agentenv_protocol.computer._declare <folder> <file> <class>` prints it as
634
+ JSON, or a driver's `DriverDeclaration`, without the class's own dependencies installed.
635
+
593
636
  ## A2A agent framework
594
637
 
595
638
  The distribution exposes two unrelated decorators named `extension`:
@@ -537,6 +537,49 @@ leasing, and unverified for one with it until the probe has lease checks. The pr
537
537
  default role; the tool-access check hides a tool from a role of its own, `agentenv-probe-<random>`,
538
538
  and gives it back.
539
539
 
540
+ ## Computer apps
541
+
542
+ A computer app is software an agent uses on a computer, beside the driver: a desktop app, a service
543
+ or both. Write one as a `ComputerAppEnvironment` subclass from `agentenv_protocol.computer`, an
544
+ `AgentEnvEnvironment` whose class also says what it is to a computer. It needs only the plain
545
+ package: `agentenv_protocol.computer` imports what a driver needs (mcp, uvicorn, Pillow, PyAV) only
546
+ when a driver's names are used.
547
+
548
+ ```python
549
+ from pydantic import BaseModel, SecretStr
550
+ from agentenv_protocol.computer import ComputerAppEnvironment, Launch, Linux, Service, computer_app
551
+
552
+ @computer_app(supports=[{"os": {"type": "linux", "display": ["x11"]}, "arch": ["amd64", "arm64"]}])
553
+ class MailApp(ComputerAppEnvironment):
554
+ linux = Linux(python="requirements.txt", services=[Service(name="imap", command="venv/bin/python imap.py")])
555
+ launch = Launch(command=["thunderbird"], window={"class": "thunderbird"})
556
+ skill = "skill.md"
557
+
558
+ class Settings(BaseModel):
559
+ signature: str = ""
560
+ password: SecretStr = SecretStr("")
561
+ ```
562
+
563
+ - **`@computer_app`** names the platforms the app supports, as a driver names them, and the
564
+ `permissions` it needs of the computer. Its env.toml names the rest: the app itself
565
+ (`environment_name`) and the `computer` and `driver` it deploys alone on.
566
+ - **`linux = Linux(...)`** is how a Linux computer installs it: `files`, a folder of the app's
567
+ copied under its prefix; `python`, a requirements file there installed into a venv beside them;
568
+ `services`, programs the computer's init runs from the prefix as the session user.
569
+ - **`launch = Launch(...)`** says how it starts in the desktop session, with it (`at="boot"`) or when
570
+ the agent opens it (`"demand"`), and `ready=Ready(...)` when it's ready; `@ready_check` marks a
571
+ method for readiness that needs logic, and `@install_step(scope=...)` one a computer runs as it
572
+ installs the app.
573
+ - **Its skill** is a file of its folder (`skill = "skill.md"`) or an `@agent_skill` method, not both;
574
+ its data hooks are the usual `@add_data`, `@get_data` and `@reset_data`.
575
+ - **`Settings`** is typed; a computer hands each app the values a deploy gives it, as one JSON value
576
+ in `APP_SETTINGS_ENV` (`AGENTENV_APP_SETTINGS`, the apps' values by name), and the base class reads
577
+ its own, by `ENVIRONMENT_NAME`, as `self.settings`. A `SecretStr` field is a secret.
578
+
579
+ `MailApp.declaration()` returns what the class declares, an `AppDeclaration`, which a `computer_app`
580
+ env stores, and `python -m agentenv_protocol.computer._declare <folder> <file> <class>` prints it as
581
+ JSON, or a driver's `DriverDeclaration`, without the class's own dependencies installed.
582
+
540
583
  ## A2A agent framework
541
584
 
542
585
  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.396"
3
+ version = "0.1.398"
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,112 @@
1
+ """The computer SDK. A driver subclasses ComputerDriverEnvironment, marks the methods that capture and send input, and
2
+ serve()s it, which needs the ``computer`` extra: ``pip install 'agentenv-framework-protocol[computer]'``. An app
3
+ subclasses ComputerAppEnvironment, which needs only the plain package, so what a driver needs is imported on first
4
+ use."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import importlib
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ from .app import (
12
+ APP_SETTINGS_ENV,
13
+ AppDeclaration,
14
+ ComputerAppEnvironment,
15
+ Launch,
16
+ Linux,
17
+ Ready,
18
+ Service,
19
+ computer_app,
20
+ install_step,
21
+ ready_check,
22
+ )
23
+ from .errors import (
24
+ BackgroundUnavailable,
25
+ ComputerError,
26
+ DeliveryFailed,
27
+ DeviceUnavailable,
28
+ InputBusy,
29
+ Internal,
30
+ InvalidArgument,
31
+ LimitExceeded,
32
+ OutOfBounds,
33
+ PermissionDenied,
34
+ StaleFrame,
35
+ TargetUnavailable,
36
+ Timeout,
37
+ Unsupported,
38
+ )
39
+ from .operations import (
40
+ Display,
41
+ Effect,
42
+ ExecResult,
43
+ Image,
44
+ ReadyCheck,
45
+ capture,
46
+ click,
47
+ display,
48
+ drag,
49
+ key_down,
50
+ key_up,
51
+ pointer_down,
52
+ pointer_move,
53
+ pointer_position,
54
+ pointer_up,
55
+ press,
56
+ record,
57
+ run_in_session,
58
+ scroll,
59
+ set_display,
60
+ type_text,
61
+ write_files,
62
+ )
63
+ from .manifest import ManifestSegment, RecordingManifest
64
+ from .platform import Platform
65
+ from .settings import SETTINGS_ENV, ComputerSettings
66
+
67
+ if TYPE_CHECKING:
68
+ from .card import (
69
+ COMPUTER,
70
+ COMPUTER_DISPLAY,
71
+ COMPUTER_EXEC,
72
+ COMPUTER_KEYBOARD,
73
+ COMPUTER_POINTER,
74
+ COMPUTER_READY,
75
+ COMPUTER_RECORDING,
76
+ COMPUTER_SPACES,
77
+ )
78
+ from .driver import DEFAULT_IDENTITY, ComputerDriverEnvironment, DriverDeclaration, computer_driver
79
+ from .recording import RecordedFrame, RecordedSegment, RecordHints, Recording, frames_in_thread
80
+
81
+ # The names whose modules need the computer extra (mcp, av and Pillow), by module.
82
+ _ON_USE = {
83
+ "driver": ("ComputerDriverEnvironment", "computer_driver", "DEFAULT_IDENTITY", "DriverDeclaration"),
84
+ "card": ("COMPUTER", "COMPUTER_READY", "COMPUTER_DISPLAY", "COMPUTER_SPACES", "COMPUTER_POINTER",
85
+ "COMPUTER_KEYBOARD", "COMPUTER_EXEC", "COMPUTER_RECORDING"),
86
+ "recording": ("Recording", "RecordHints", "RecordedFrame", "RecordedSegment", "frames_in_thread"),
87
+ }
88
+
89
+
90
+ def __getattr__(name: str) -> Any:
91
+ module = next((module for module, names in _ON_USE.items() if name in names), None)
92
+ if module is None:
93
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
94
+ return getattr(importlib.import_module(f"{__name__}.{module}"), name)
95
+
96
+
97
+ __all__ = [
98
+ "ComputerDriverEnvironment", "computer_driver", "DEFAULT_IDENTITY", "DriverDeclaration",
99
+ "ComputerAppEnvironment", "computer_app", "install_step", "ready_check", "Linux", "Service", "Launch", "Ready",
100
+ "AppDeclaration",
101
+ "APP_SETTINGS_ENV",
102
+ "Display", "Image", "Effect", "ExecResult", "ReadyCheck", "Platform", "ComputerSettings", "SETTINGS_ENV",
103
+ "display", "set_display", "capture", "click", "drag", "scroll", "type_text", "press",
104
+ "pointer_move", "pointer_down", "pointer_up", "pointer_position", "key_down", "key_up",
105
+ "run_in_session", "write_files", "record", "Recording", "RecordHints", "RecordedFrame", "RecordedSegment",
106
+ "frames_in_thread", "RecordingManifest", "ManifestSegment",
107
+ "ComputerError", "InvalidArgument", "OutOfBounds", "StaleFrame", "InputBusy", "PermissionDenied", "Unsupported",
108
+ "BackgroundUnavailable", "TargetUnavailable", "DeviceUnavailable", "LimitExceeded", "Timeout", "DeliveryFailed",
109
+ "Internal",
110
+ "COMPUTER", "COMPUTER_READY", "COMPUTER_DISPLAY", "COMPUTER_SPACES", "COMPUTER_POINTER", "COMPUTER_KEYBOARD",
111
+ "COMPUTER_EXEC", "COMPUTER_RECORDING",
112
+ ]
@@ -1,8 +1,8 @@
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."""
1
+ """Read a computer driver or app class where its own dependencies may not be installed, as a ``computer_driver`` or
2
+ ``computer_app`` env's put does: ``python -m agentenv_protocol.computer._declare <folder> <file> <class>`` imports the
3
+ file with every module not installed here standing in as an inert stub, and prints what the class declares as JSON,
4
+ as its base class says. Nothing runs beyond defining the module and its classes, and a class that can't be defined
5
+ even so exits 1 with why."""
6
6
  from __future__ import annotations
7
7
 
8
8
  import contextlib
@@ -14,10 +14,14 @@ import sys
14
14
  import types
15
15
  from pathlib import Path
16
16
 
17
+ from .app import AppDeclaration, ComputerAppEnvironment
17
18
  from .card import COMPUTER
18
- from .driver import ComputerDriverEnvironment
19
+ from .driver import ComputerDriverEnvironment, DriverDeclaration, DriverPlatform
19
20
  from .platform import supported
20
21
 
22
+ #: The env type that stores each kind of declaration, which the printed JSON carries as ``type``.
23
+ _ENV_TYPES = {AppDeclaration: "computer_app", DriverDeclaration: "computer_driver"}
24
+
21
25
 
22
26
  class _StubMeta(type):
23
27
  def __getattr__(cls, name: str):
@@ -58,32 +62,35 @@ class _Stubs(importlib.abc.MetaPathFinder, importlib.abc.Loader):
58
62
  return None
59
63
 
60
64
 
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
+ def declare(folder: Path, file: str, name: str) -> AppDeclaration | DriverDeclaration:
66
+ """What the class ``name`` in ``folder/file`` declares: an app's ``ComputerAppEnvironment.declaration()``; a
67
+ driver's card name, its ``supports``, and for each platform those name, the extensions it provides, its tools and
68
+ the computer/v1 features. Raises ValueError for a class that is neither, or a driver that declares no platform."""
65
69
  sys.path.insert(0, str(folder))
66
70
  sys.meta_path.append(_Stubs())
67
71
  spec = importlib.util.spec_from_file_location("computer_driver_module", folder / file)
68
72
  module = importlib.util.module_from_spec(spec)
69
73
  spec.loader.exec_module(module)
70
74
  cls = getattr(module, name, None)
75
+ if isinstance(cls, type) and issubclass(cls, ComputerAppEnvironment):
76
+ return cls.declaration()
71
77
  if not (isinstance(cls, type) and issubclass(cls, ComputerDriverEnvironment)):
72
- raise ValueError(f"{file} has no ComputerDriverEnvironment subclass named {name}")
78
+ raise ValueError(f"{file} has no ComputerDriverEnvironment or ComputerAppEnvironment subclass named {name}")
73
79
  platforms = [platform for entry in cls.supports for platform in supported(entry)]
74
80
  if not platforms:
75
81
  raise ValueError(f"{name} declares no platform: name them with @computer_driver(supports=[...])")
76
82
  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({k: v for k, v in e.params.items() if k not in ("endpoint", "methods")}
81
- for e in card.capabilities.extensions if e.uri == COMPUTER)}
82
- for platform, card in cards]}
83
+ return DriverDeclaration(name=cards[0][1].name, supports=list(cls.supports), platforms=[
84
+ DriverPlatform(platform=platform, provides=[e.uri for e in card.capabilities.extensions],
85
+ tools=[t.name for t in card.capabilities.tools],
86
+ features=next({k: v for k, v in e.params.items() if k not in ("endpoint", "methods")}
87
+ for e in card.capabilities.extensions if e.uri == COMPUTER))
88
+ for platform, card in cards])
83
89
 
84
90
 
85
91
  def main(argv: list[str]) -> int:
86
- """Print the declaration as the only output; what the module prints while it loads goes to stderr."""
92
+ """Print the declaration as the only output, its env type as ``type``; what the module prints while it loads goes
93
+ to stderr."""
87
94
  folder, file, name = argv
88
95
  try:
89
96
  with contextlib.redirect_stdout(sys.stderr):
@@ -91,7 +98,7 @@ def main(argv: list[str]) -> int:
91
98
  except Exception as e:
92
99
  print(f"{type(e).__name__}: {e}", file=sys.stderr)
93
100
  return 1
94
- print(json.dumps(declared))
101
+ print(json.dumps({"type": _ENV_TYPES[type(declared)], **declared.model_dump(mode="json", exclude_none=True)}))
95
102
  return 0
96
103
 
97
104
 
@@ -0,0 +1,178 @@
1
+ """ComputerAppEnvironment: software on a computer, an AgentEnvEnvironment whose class also declares what it is for a
2
+ computer: the platforms it supports and the permissions it needs (``@computer_app``), how each OS installs it
3
+ (``linux = Linux(...)``), how it launches (``launch``), its install steps and ready checks, its skill, and its typed
4
+ ``Settings``. A ``computer_app`` env's put reads that declaration from the class; its data hooks are the base class's.
5
+ Its name is its env's ``environment_name``."""
6
+ from __future__ import annotations
7
+
8
+ import inspect
9
+ import json
10
+ import os
11
+ from typing import Any, Callable, ClassVar, Literal, Optional
12
+
13
+ from pydantic import BaseModel, ConfigDict, Field
14
+
15
+ from ..agent_env_environment import _EXT_ATTR, _OP_ATTR, AgentEnvEnvironment
16
+ from ..types import METHOD_ADD, METHOD_GET, METHOD_RESET, SKILL_EXTENSION_URI
17
+
18
+ #: The environment variable a computer delivers its apps' settings in: JSON of each app's values by its name.
19
+ APP_SETTINGS_ENV = "AGENTENV_APP_SETTINGS"
20
+ _INSTALL_STEP_ATTR = "_aee_install_step"
21
+ _READY_CHECK_ATTR = "_aee_ready_check"
22
+ _DATA_METHODS = {"reset_data": METHOD_RESET, "add_data": METHOD_ADD, "get_data": METHOD_GET}
23
+
24
+
25
+ class Service(BaseModel):
26
+ """A program the computer's init runs from the app's folder as the session user; a relative program names one
27
+ there."""
28
+
29
+ model_config = ConfigDict(extra="forbid")
30
+
31
+ name: str
32
+ command: str
33
+
34
+
35
+ class Linux(BaseModel):
36
+ """How a Linux computer installs the app: ``files``, a folder of the app's copied under its prefix; ``python``, a
37
+ requirements file in that folder installed into a venv beside them; ``services``, programs its init runs."""
38
+
39
+ model_config = ConfigDict(extra="forbid")
40
+
41
+ files: str = "."
42
+ python: Optional[str] = None
43
+ services: list[Service] = Field(default_factory=list)
44
+
45
+
46
+ class Ready(BaseModel):
47
+ """When the app is ready: its window shown, a port answering or a command exiting 0, within ``timeout_s``."""
48
+
49
+ model_config = ConfigDict(extra="forbid")
50
+
51
+ window: bool = False
52
+ port: Optional[int] = None
53
+ command: Optional[list[str]] = None
54
+ timeout_s: float = 60
55
+
56
+
57
+ class Launch(BaseModel):
58
+ """How the app starts in the desktop session: with it (``boot``) or when the agent opens it (``demand``)."""
59
+
60
+ model_config = ConfigDict(extra="forbid")
61
+
62
+ at: Literal["boot", "demand"] = "boot"
63
+ command: list[str]
64
+ window: Optional[dict[str, str]] = None
65
+ ready: Optional[Ready] = None
66
+
67
+
68
+ class AppSkill(BaseModel):
69
+ """Where an app's skill for agents comes from: a ``file`` of its folder, or the ``@agent_skill`` ``method`` that
70
+ writes it."""
71
+
72
+ model_config = ConfigDict(extra="forbid")
73
+
74
+ file: Optional[str] = None
75
+ method: Optional[str] = None
76
+
77
+
78
+ class InstallStep(BaseModel):
79
+ """An ``@install_step`` method, by name, and whom it runs as."""
80
+
81
+ model_config = ConfigDict(extra="forbid")
82
+
83
+ name: str
84
+ scope: Literal["system", "session"]
85
+
86
+
87
+ class AppDeclaration(BaseModel):
88
+ """What a ComputerAppEnvironment class declares, as a ``computer_app`` env stores it: the platforms it ``supports``,
89
+ as a driver names them; its ``install`` by OS type; ``settings``, the JSON schema of its ``Settings``; and the
90
+ ``data`` methods, ``install_steps`` and ``ready_checks`` it serves, by name."""
91
+
92
+ model_config = ConfigDict(extra="forbid")
93
+
94
+ supports: list[dict[str, Any]]
95
+ permissions: list[str]
96
+ install: dict[str, Linux]
97
+ launch: Optional[Launch] = None
98
+ settings: dict[str, Any]
99
+ skill: Optional[AppSkill] = None
100
+ data: list[str]
101
+ install_steps: list[InstallStep]
102
+ ready_checks: list[str]
103
+
104
+
105
+ def computer_app(*, supports: Optional[list] = None, permissions: Optional[list[str]] = None) -> Callable:
106
+ """Declare what an app is: the platforms it supports, as a driver names them, and the permissions it needs of the
107
+ computer. Its env.toml names it and the computer and driver it deploys alone on."""
108
+ def deco(cls: type) -> type:
109
+ if not (isinstance(cls, type) and issubclass(cls, ComputerAppEnvironment)):
110
+ raise TypeError("@computer_app decorates a ComputerAppEnvironment subclass")
111
+ cls.supports, cls.permissions = list(supports or []), list(permissions or [])
112
+ return cls
113
+ return deco
114
+
115
+
116
+ def install_step(scope: Literal["system", "session"] = "system") -> Callable:
117
+ """Mark a method a computer runs as it installs the app, after its files, in the order the class defines them: as
118
+ root, or for ``session``, as the session user."""
119
+ def deco(fn: Callable) -> Callable:
120
+ setattr(fn, _INSTALL_STEP_ATTR, scope)
121
+ return fn
122
+ return deco
123
+
124
+
125
+ def ready_check(fn: Callable) -> Callable:
126
+ """Mark a method that says whether the app is ready, when ``launch``'s ``ready`` can't."""
127
+ setattr(fn, _READY_CHECK_ATTR, True)
128
+ return fn
129
+
130
+
131
+ class ComputerAppEnvironment(AgentEnvEnvironment):
132
+ """Subclass it, declare it with ``@computer_app``, and set ``linux``, ``launch`` and ``skill`` as class attributes;
133
+ a deploy's settings arrive as ``self.settings``."""
134
+
135
+ supports: ClassVar[list] = []
136
+ permissions: ClassVar[list[str]] = []
137
+ linux: ClassVar[Optional[Linux]] = None
138
+ launch: ClassVar[Optional[Launch]] = None
139
+ #: A file of the app's folder holding its skill for agents, when no ``@agent_skill`` method writes it.
140
+ skill: ClassVar[Optional[str]] = None
141
+
142
+ class Settings(BaseModel):
143
+ """The app's settings: an app declares its own; a ``SecretStr`` field is a secret."""
144
+
145
+ def __init__(self) -> None:
146
+ values = json.loads(os.environ.get(APP_SETTINGS_ENV) or "{}").get(os.environ.get("ENVIRONMENT_NAME"), {})
147
+ self.settings = self.Settings.model_validate(values)
148
+
149
+ @classmethod
150
+ def declaration(cls) -> AppDeclaration:
151
+ """What the class declares. Raises ValueError for a skill declared twice or an install that isn't a
152
+ ``Linux``."""
153
+ if cls.linux is not None and not isinstance(cls.linux, Linux):
154
+ raise ValueError(f"{cls.__name__}.linux is a Linux(...), not {cls.linux!r}")
155
+ methods = _methods(cls)
156
+ skill_methods = [fn.__name__ for fn in methods
157
+ if getattr(getattr(fn, _EXT_ATTR, None), "uri", None) == SKILL_EXTENSION_URI]
158
+ if cls.skill is not None and skill_methods:
159
+ raise ValueError(f"{cls.__name__} declares its skill twice: skill = {cls.skill!r} and @agent_skill "
160
+ f"{skill_methods[0]}")
161
+ return AppDeclaration(
162
+ supports=cls.supports, permissions=cls.permissions,
163
+ install={"linux": cls.linux} if cls.linux is not None else {},
164
+ launch=cls.launch, settings=cls.Settings.model_json_schema(),
165
+ skill=AppSkill(file=cls.skill) if cls.skill is not None else
166
+ AppSkill(method=skill_methods[0]) if skill_methods else None,
167
+ data=sorted({_DATA_METHODS[op] for fn in methods if (op := getattr(fn, _OP_ATTR, None)) in _DATA_METHODS}),
168
+ install_steps=[InstallStep(name=fn.__name__, scope=getattr(fn, _INSTALL_STEP_ATTR)) for fn in methods
169
+ if hasattr(fn, _INSTALL_STEP_ATTR)],
170
+ ready_checks=[fn.__name__ for fn in methods if hasattr(fn, _READY_CHECK_ATTR)],
171
+ )
172
+
173
+
174
+ def _methods(cls: type) -> list[Callable]:
175
+ """The class's methods, each as the class resolves its name, in the order they were first defined."""
176
+ names = dict.fromkeys(name for klass in reversed(cls.__mro__) for name, member in vars(klass).items()
177
+ if inspect.isfunction(member))
178
+ return [method for name in names if inspect.isfunction(method := getattr(cls, name))]
@@ -5,6 +5,8 @@ from __future__ import annotations
5
5
  import asyncio
6
6
  from typing import Any, Callable, ClassVar
7
7
 
8
+ from pydantic import BaseModel, ConfigDict
9
+
8
10
  from ..agent_env_environment import _CARD_CONFIG_ATTR, AgentEnvEnvironment, AgentEnvFastMCPApplication, environment_card
9
11
  from ..tool_context import ToolContext
10
12
  from ..types import EnvironmentCard, EnvironmentExtension, EnvironmentTool
@@ -23,6 +25,29 @@ from .tools import Tools
23
25
  DEFAULT_IDENTITY = "default"
24
26
 
25
27
 
28
+ class DriverPlatform(BaseModel):
29
+ """What a driver serves on one platform its ``supports`` names: the extensions it ``provides``, its ``tools`` and
30
+ its computer/v1 ``features``."""
31
+
32
+ model_config = ConfigDict(extra="forbid")
33
+
34
+ platform: Platform
35
+ provides: list[str]
36
+ tools: list[str]
37
+ features: dict[str, Any]
38
+
39
+
40
+ class DriverDeclaration(BaseModel):
41
+ """What a ComputerDriverEnvironment class declares, as a ``computer_driver`` env stores it: its card's ``name``, the
42
+ platforms it ``supports``, as predicates, and what it serves on each of them."""
43
+
44
+ model_config = ConfigDict(extra="forbid")
45
+
46
+ name: str
47
+ supports: list[dict[str, Any]]
48
+ platforms: list[DriverPlatform]
49
+
50
+
26
51
  class ComputerDriverEnvironment(Tools, Extensions, DataPlane, AgentEnvEnvironment):
27
52
  """Subclass it and mark the methods that capture and send input (see ``operations``); the class is checked when
28
53
  it is defined. Features are class attributes, on the card. A subclass's ``__init__`` calls ``super().__init__()``.