agentenv-framework-protocol 0.1.403__tar.gz → 0.1.405__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.403 → agentenv_framework_protocol-0.1.405}/PKG-INFO +120 -15
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/README.md +119 -14
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/pyproject.toml +1 -1
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/__init__.py +6 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/agent_env_environment.py +206 -7
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/__init__.py +12 -2
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/_declare.py +2 -1
- agentenv_framework_protocol-0.1.405/src/agentenv_protocol/computer/app.py +482 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/driver.py +8 -3
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/extensions.py +1 -1
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/keys.py +2 -0
- agentenv_framework_protocol-0.1.405/src/agentenv_protocol/computer/run_install_step.py +43 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/skill.py +30 -7
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/probe_fake.py +1 -1
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_agent_env_environment.py +13 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_app_server.py +14 -5
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_dataplane.py +1 -1
- agentenv_framework_protocol-0.1.405/tests/test_computer_declare.py +362 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_driver.py +18 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_extensions.py +4 -4
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_serve.py +57 -2
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_tool_access.py +25 -3
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_tool_annotations.py +155 -1
- agentenv_framework_protocol-0.1.403/src/agentenv_protocol/computer/app.py +0 -240
- agentenv_framework_protocol-0.1.403/tests/test_computer_declare.py +0 -241
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/.gitignore +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/LICENSE +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/NOTICE +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/THIRD_PARTY_NOTICES.md +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/examples/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/examples/advanced_agent.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/examples/basic_agent.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/examples/custom_extensions_agent.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/examples/multimodal_agent.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/examples/reference_model.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/examples/streaming_agent.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/a2a_agent/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/a2a_agent/_trajectory_log.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/a2a_agent/extensions.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/a2a_agent/peer_agents.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/a2a_agent/proxy.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/a2a_agent/staging.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/client.py +0 -0
- /agentenv_framework_protocol-0.1.403/src/agentenv_protocol/computer/linux.py → /agentenv_framework_protocol-0.1.405/src/agentenv_protocol/computer/_linux.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/_load.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/app_runtime.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/apps.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/card.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/checks.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/conformance/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/conformance/__main__.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/conformance/checks.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/conformance/client.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/conformance/fixture.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/conformance/mp4.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/conformance/report.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/dataplane.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/errors.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/frames.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/launch.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/manifest.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/operations.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/platform.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/queue.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/recording.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/results.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/runtime.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/serve.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/serve_app.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/session.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/settings.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/spaces.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/computer/tools.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/errors.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/manifest.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/preflight.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/tool_access.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/tool_client.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/tool_context.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/transfers.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/src/agentenv_protocol/types.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/computer_fake.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_a2a_agent.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_a2a_agent_examples.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_a2a_object_transfers.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_a2a_peer_agents.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_a2a_proxy.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_a2a_staging.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_a2a_trajectory_log.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_client.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_conformance.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_conformance_checks.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_conformance_recording.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_frames.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_queue.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_recording.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_computer_spaces.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_data_objects.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_errors.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_extension_methods.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_intake.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_manifest.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_preflight.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_tool_client.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_tool_context.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/tests/test_tool_context_fastmcp.py +0 -0
- {agentenv_framework_protocol-0.1.403 → agentenv_framework_protocol-0.1.405}/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.
|
|
3
|
+
Version: 0.1.405
|
|
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
|
|
@@ -296,6 +296,69 @@ at the gateway.
|
|
|
296
296
|
|
|
297
297
|
Where the binding lives depends on the app. On mcp's own FastMCP it wraps the tool manager's dispatch; on the standalone `fastmcp` package it joins the app's middleware chain as the outermost layer, so a handler's own middleware runs under it too. An app with neither seam mounts unbound, with a warning, unless a tool declares a `ToolContext` parameter or the handler defines `on_tool_call`: then the mount is refused rather than serving an empty caller.
|
|
298
298
|
|
|
299
|
+
### Tools served on another server's behalf
|
|
300
|
+
|
|
301
|
+
An environment can serve tools another server defines, each with that server's own schema:
|
|
302
|
+
`forwarded_tools()` returns `ForwardedTool(tool, call, annotations, output_schema)` entries, where
|
|
303
|
+
`tool` is an `EnvironmentTool` carrying the other server's `inputSchema`, and `call` takes the
|
|
304
|
+
call's arguments as keywords, untouched, and returns that server's `CallToolResult`, which reaches
|
|
305
|
+
the caller as it is. An `output_schema` is listed as the tool's `outputSchema` and isn't checked
|
|
306
|
+
here, so `call` answers every result, its errors too, in a shape that schema admits. They're listed
|
|
307
|
+
on the card beside the `@tool` methods, chosen by `served_tools` and under tool access like them;
|
|
308
|
+
one named like another tool is refused. They need mcp's FastMCP. The cua-driver driver serves
|
|
309
|
+
cua-driver's own tools this way.
|
|
310
|
+
|
|
311
|
+
### Resources
|
|
312
|
+
|
|
313
|
+
Mark a method that serves MCP resources with `@resource`; an env may mark several, and each read
|
|
314
|
+
calls the method, which returns text or bytes. The URI decides what's served:
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
@environment_card(name="orders")
|
|
318
|
+
class Orders(AgentEnvEnvironment):
|
|
319
|
+
@resource("docs://orders/refund-policy", mime_type="text/markdown")
|
|
320
|
+
def refund_policy(self) -> str:
|
|
321
|
+
"""When a refund is allowed and who approves it."""
|
|
322
|
+
return POLICY.read_text()
|
|
323
|
+
|
|
324
|
+
@resource("orders://{order_id}")
|
|
325
|
+
async def order(self, order_id: str) -> str:
|
|
326
|
+
return json.dumps(await self.db.order(order_id))
|
|
327
|
+
|
|
328
|
+
@resource("skill://upstream/{file}", listed=lambda env: UPSTREAM["resources"])
|
|
329
|
+
async def upstream_file(self, file: str) -> str:
|
|
330
|
+
return await read_upstream(f"skill://upstream/{file}")
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
- A fixed URI is one resource. Its name defaults to the method's name and its description to the
|
|
334
|
+
docstring.
|
|
335
|
+
- A template such as `orders://{order_id}` is an MCP resource template; the method takes its parts.
|
|
336
|
+
- A template with `listed` serves the resources `listed` returns, each as MCP lists one (`uri`,
|
|
337
|
+
`name`, `description`, `mimeType`), and only those. Each is read through the method with its
|
|
338
|
+
URI's parts. That's how an env serves another server's resources from a list: the cua-driver
|
|
339
|
+
driver serves cua-driver's skill files this way.
|
|
340
|
+
|
|
341
|
+
A URI served twice, a listed URI that doesn't match its template, and a method that doesn't take
|
|
342
|
+
exactly the template's parts are refused. Resources need mcp's FastMCP.
|
|
343
|
+
|
|
344
|
+
### Instructions at connect
|
|
345
|
+
|
|
346
|
+
An MCP server may answer `initialize` with `instructions`: how to use it, which a client may put in
|
|
347
|
+
the agent's context, as Claude Code does. Mark the plain method that returns them with
|
|
348
|
+
`@server_instructions`:
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
@environment_card(name="orders")
|
|
352
|
+
class Orders(AgentEnvEnvironment):
|
|
353
|
+
@server_instructions
|
|
354
|
+
def how_to(self) -> str:
|
|
355
|
+
return "Look an order up with get_order before refunding it; refunds can't be undone."
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
They're read once, when the SDK builds the app (`create_app`, `serve`, `create_fastmcp_app`), and
|
|
359
|
+
every session gets the same text. An env has at most one such method. An app you build yourself
|
|
360
|
+
and pass to `mount` keeps its own instructions: set them when you construct it.
|
|
361
|
+
|
|
299
362
|
### Getting part of the data
|
|
300
363
|
|
|
301
364
|
A `@get_data` handler that takes a `paths` parameter returns only the data under the paths a caller
|
|
@@ -318,9 +381,11 @@ def guide(self):
|
|
|
318
381
|
The card then declares `urn:agentenv:skill/v1` (`SKILL_EXTENSION_URI`), whose `get {name}`
|
|
319
382
|
answers `{name, description, skill_md}`, the shape `skill-config/v1` takes, with the frontmatter
|
|
320
383
|
written under the name the caller gives; a name that isn't lowercase letters, digits and single
|
|
321
|
-
hyphens, up to 64, is refused `invalid_argument`.
|
|
322
|
-
|
|
323
|
-
|
|
384
|
+
hyphens, up to 64, is refused `invalid_argument`. A skill with other files beside its SKILL.md
|
|
385
|
+
returns them as a third value, `{relative path: text}`, and `get` answers them as `files`.
|
|
386
|
+
agent-env's `add_skills` step installs it on an agent through `env_ids`, named after the env's
|
|
387
|
+
environment name, a skill with files as a bundle of them; for an env behind a gateway, whose card
|
|
388
|
+
nests its servers' cards, it installs each server's skill under that server's name.
|
|
324
389
|
|
|
325
390
|
### Uploading an export through a grant
|
|
326
391
|
|
|
@@ -482,7 +547,9 @@ driver has them, through the `served_tools`, `served_extensions` and `served_ope
|
|
|
482
547
|
features. The driver serves its skill through `skill/v1` (an `@agent_skill` handler on the base
|
|
483
548
|
class): a SKILL.md written from the computer as it booted (its platform, session user and home,
|
|
484
549
|
the live screen size, what its input takes, its OS's shortcuts) and ending with the driver
|
|
485
|
-
class's `notes`, markdown for what its card can't say.
|
|
550
|
+
class's `notes`, markdown for what its card can't say. A driver whose software ships a skill of
|
|
551
|
+
its own returns it from `bundled_skill()` (its description, body and other files), and the
|
|
552
|
+
computer skill leads with it, the computer's part following.
|
|
486
553
|
- **Framework extensions.** `computer-ready/v1` (`status`: real checks of the display and a
|
|
487
554
|
capture, the driver's own `ready_checks()`, and one per app on the computer, asked of the app's
|
|
488
555
|
own server (see Computer apps); tools refuse `device_unavailable` until they pass once), `computer-display/v1`, `computer-spaces/v1` (the default space, written into the tool
|
|
@@ -604,37 +671,75 @@ server, which needs the `app` extra (below).
|
|
|
604
671
|
|
|
605
672
|
```python
|
|
606
673
|
from pydantic import BaseModel, SecretStr
|
|
607
|
-
from agentenv_protocol.computer import ComputerAppEnvironment, Launch,
|
|
674
|
+
from agentenv_protocol.computer import (ComputerAppEnvironment, Launch, Packages, Service, computer_app,
|
|
675
|
+
install_step, linux)
|
|
676
|
+
|
|
677
|
+
class LinuxSettings(BaseModel):
|
|
678
|
+
font: str = "DejaVu Sans"
|
|
608
679
|
|
|
609
680
|
@computer_app(supports=[{"os": {"type": "linux", "display": ["x11"]}, "arch": ["amd64", "arm64"]}])
|
|
681
|
+
@linux(packages=Packages(apt=["thunderbird"]), python="requirements.txt",
|
|
682
|
+
services=[Service(name="imap", command="venv/bin/python imap.py")],
|
|
683
|
+
launch=Launch(command=["thunderbird"], window={"class": "thunderbird"}), settings=LinuxSettings)
|
|
610
684
|
class MailApp(ComputerAppEnvironment):
|
|
611
|
-
linux = Linux(python="requirements.txt", services=[Service(name="imap", command="venv/bin/python imap.py")])
|
|
612
|
-
launch = Launch(command=["thunderbird"], window={"class": "thunderbird"})
|
|
613
685
|
skill = "skill.md"
|
|
614
686
|
|
|
615
687
|
class Settings(BaseModel):
|
|
616
688
|
signature: str = ""
|
|
617
689
|
password: SecretStr = SecretStr("")
|
|
690
|
+
|
|
691
|
+
class BuildSettings(BaseModel):
|
|
692
|
+
locale: str = "en_US"
|
|
693
|
+
|
|
694
|
+
@install_step(scope="session")
|
|
695
|
+
def profile(self) -> None: ...
|
|
618
696
|
```
|
|
619
697
|
|
|
620
698
|
- **`@computer_app`** names the platforms the app supports, as a driver names them, and the
|
|
621
699
|
`permissions` it needs of the computer. Its env.toml names the rest: the app itself
|
|
622
|
-
(`environment_name`)
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
700
|
+
(`environment_name`), the `computer` and `driver` it deploys alone on, and `build_settings`, its
|
|
701
|
+
build settings' values (`build_settings = { locale = "fr_FR" }`).
|
|
702
|
+
- **`@linux(...)`** is how a Linux computer installs it, `Linux`'s fields: `files`, a folder of the app's
|
|
703
|
+
copied under its prefix, which holds the class's file; `packages`, the OS's, as apt takes them
|
|
704
|
+
(`Packages(apt=["name", "name=version", "name/suite"])`); `python`, a requirements file there,
|
|
705
|
+
installed into the venv every app gets beside them; `sources`, extra apt repositories
|
|
706
|
+
(`AptSource`, with a key file in the folder); `downloads`, files fetched by URL and checked by
|
|
707
|
+
sha256 (`Download`); `run`, scripts of the folder run as root or the session user (`RunScript`);
|
|
708
|
+
`services`, programs the computer's init runs from the prefix as the session user; and `ports`,
|
|
709
|
+
those it listens on. Packages, requirements and downloads are resolved and fetched once, when the
|
|
710
|
+
computer env is put, and every build installs only those stored files, with the network closed.
|
|
711
|
+
Each value is checked as the class is defined: a URL, a sha256, a path that stays in the folder.
|
|
712
|
+
Installs are exact: apt installs each package without its recommends, and the venv gets exactly
|
|
713
|
+
the wheels resolved for it (`pip install --no-deps`). A requirements file lists everything itself,
|
|
714
|
+
since its lock covers that file alone: a line including another file (`-r`, `-c`), an editable
|
|
715
|
+
install (`-e`), a local path or a local folder of wheels is refused at put.
|
|
716
|
+
- **Each OS has its own decorator,** and what differs by OS goes in it: `launch=` always; and
|
|
717
|
+
optionally `skill=`, a file used instead of the class's, and `settings=` and `build_settings=`,
|
|
718
|
+
models whose fields add to the class's `Settings` and `BuildSettings` on that OS. What's on the
|
|
719
|
+
class applies to every OS. `@install(os_type, spec, launch=..., ...)` is the generic form, for an
|
|
720
|
+
OS type a plugin adds, with that OS type's own install model as `spec`; `@linux(...)` is
|
|
721
|
+
`@install("linux", Linux(...), ...)`. On the computer the class reads what's declared for the OS
|
|
722
|
+
it runs on (`platform.detect()`).
|
|
723
|
+
- **`launch=Launch(...)`** says how it starts in the desktop session: with it (`at="boot"`) or when
|
|
628
724
|
the agent opens it (`"demand"`). `ready=Ready(...)` says when it's ready, within `timeout_s` of its
|
|
629
725
|
launch: a window matching `launch.window` by its class, instance or title, a port answering on
|
|
630
726
|
loopback, or a command exiting 0. `@ready_check` marks a method for readiness that needs logic (a
|
|
631
727
|
sync one runs in a thread), and `@install_step(scope=...)` one a computer runs as it installs the
|
|
632
|
-
app
|
|
728
|
+
app, after its run scripts, offline and without the class's `__init__`. Each runs on every OS the
|
|
729
|
+
app installs on, or on those its `os=` names (`@install_step(os="linux")`, `@ready_check(os=[...])`);
|
|
730
|
+
naming one the app declares no install for is refused when the class is read. A step reads
|
|
731
|
+
`self.build_settings`; a deploy's `self.settings` aren't known at build, and reading one raises
|
|
732
|
+
`AttributeError` (`python -m agentenv_protocol.computer.run_install_step <file>:<Class> <method>`).
|
|
633
733
|
- **Its skill** is a file of its folder (`skill = "skill.md"`) or an `@agent_skill` method, not both;
|
|
634
734
|
its data hooks are the usual `@add_data`, `@get_data` and `@reset_data`.
|
|
635
735
|
- **`Settings`** is typed; a computer hands each app the values a deploy gives it, as one JSON value
|
|
636
736
|
in `APP_SETTINGS_ENV` (`AGENTENV_APP_SETTINGS`, the apps' values by name), and the base class reads
|
|
637
737
|
its own, by `ENVIRONMENT_NAME`, as `self.settings`. A `SecretStr` field is a secret.
|
|
738
|
+
- **`BuildSettings`** is typed too: values the app's env fixes in its computer's image, which only
|
|
739
|
+
another image changes, so a deploy can't give one. The composer writes them to
|
|
740
|
+
`.agentenv/build_settings.json` in the app's folder, and the base class reads them, over the
|
|
741
|
+
defaults, as `self.build_settings`, in its install steps and on the computer alike. None is a
|
|
742
|
+
secret, since the image keeps them; `declaration()` refuses a `SecretStr` one.
|
|
638
743
|
|
|
639
744
|
On a computer each app is its own server: `python -m agentenv_protocol.computer.serve_app
|
|
640
745
|
<file>:<Class>`, a program the computer's init runs from the app's folder in the app's venv, which
|
|
@@ -240,6 +240,69 @@ at the gateway.
|
|
|
240
240
|
|
|
241
241
|
Where the binding lives depends on the app. On mcp's own FastMCP it wraps the tool manager's dispatch; on the standalone `fastmcp` package it joins the app's middleware chain as the outermost layer, so a handler's own middleware runs under it too. An app with neither seam mounts unbound, with a warning, unless a tool declares a `ToolContext` parameter or the handler defines `on_tool_call`: then the mount is refused rather than serving an empty caller.
|
|
242
242
|
|
|
243
|
+
### Tools served on another server's behalf
|
|
244
|
+
|
|
245
|
+
An environment can serve tools another server defines, each with that server's own schema:
|
|
246
|
+
`forwarded_tools()` returns `ForwardedTool(tool, call, annotations, output_schema)` entries, where
|
|
247
|
+
`tool` is an `EnvironmentTool` carrying the other server's `inputSchema`, and `call` takes the
|
|
248
|
+
call's arguments as keywords, untouched, and returns that server's `CallToolResult`, which reaches
|
|
249
|
+
the caller as it is. An `output_schema` is listed as the tool's `outputSchema` and isn't checked
|
|
250
|
+
here, so `call` answers every result, its errors too, in a shape that schema admits. They're listed
|
|
251
|
+
on the card beside the `@tool` methods, chosen by `served_tools` and under tool access like them;
|
|
252
|
+
one named like another tool is refused. They need mcp's FastMCP. The cua-driver driver serves
|
|
253
|
+
cua-driver's own tools this way.
|
|
254
|
+
|
|
255
|
+
### Resources
|
|
256
|
+
|
|
257
|
+
Mark a method that serves MCP resources with `@resource`; an env may mark several, and each read
|
|
258
|
+
calls the method, which returns text or bytes. The URI decides what's served:
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
@environment_card(name="orders")
|
|
262
|
+
class Orders(AgentEnvEnvironment):
|
|
263
|
+
@resource("docs://orders/refund-policy", mime_type="text/markdown")
|
|
264
|
+
def refund_policy(self) -> str:
|
|
265
|
+
"""When a refund is allowed and who approves it."""
|
|
266
|
+
return POLICY.read_text()
|
|
267
|
+
|
|
268
|
+
@resource("orders://{order_id}")
|
|
269
|
+
async def order(self, order_id: str) -> str:
|
|
270
|
+
return json.dumps(await self.db.order(order_id))
|
|
271
|
+
|
|
272
|
+
@resource("skill://upstream/{file}", listed=lambda env: UPSTREAM["resources"])
|
|
273
|
+
async def upstream_file(self, file: str) -> str:
|
|
274
|
+
return await read_upstream(f"skill://upstream/{file}")
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
- A fixed URI is one resource. Its name defaults to the method's name and its description to the
|
|
278
|
+
docstring.
|
|
279
|
+
- A template such as `orders://{order_id}` is an MCP resource template; the method takes its parts.
|
|
280
|
+
- A template with `listed` serves the resources `listed` returns, each as MCP lists one (`uri`,
|
|
281
|
+
`name`, `description`, `mimeType`), and only those. Each is read through the method with its
|
|
282
|
+
URI's parts. That's how an env serves another server's resources from a list: the cua-driver
|
|
283
|
+
driver serves cua-driver's skill files this way.
|
|
284
|
+
|
|
285
|
+
A URI served twice, a listed URI that doesn't match its template, and a method that doesn't take
|
|
286
|
+
exactly the template's parts are refused. Resources need mcp's FastMCP.
|
|
287
|
+
|
|
288
|
+
### Instructions at connect
|
|
289
|
+
|
|
290
|
+
An MCP server may answer `initialize` with `instructions`: how to use it, which a client may put in
|
|
291
|
+
the agent's context, as Claude Code does. Mark the plain method that returns them with
|
|
292
|
+
`@server_instructions`:
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
@environment_card(name="orders")
|
|
296
|
+
class Orders(AgentEnvEnvironment):
|
|
297
|
+
@server_instructions
|
|
298
|
+
def how_to(self) -> str:
|
|
299
|
+
return "Look an order up with get_order before refunding it; refunds can't be undone."
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
They're read once, when the SDK builds the app (`create_app`, `serve`, `create_fastmcp_app`), and
|
|
303
|
+
every session gets the same text. An env has at most one such method. An app you build yourself
|
|
304
|
+
and pass to `mount` keeps its own instructions: set them when you construct it.
|
|
305
|
+
|
|
243
306
|
### Getting part of the data
|
|
244
307
|
|
|
245
308
|
A `@get_data` handler that takes a `paths` parameter returns only the data under the paths a caller
|
|
@@ -262,9 +325,11 @@ def guide(self):
|
|
|
262
325
|
The card then declares `urn:agentenv:skill/v1` (`SKILL_EXTENSION_URI`), whose `get {name}`
|
|
263
326
|
answers `{name, description, skill_md}`, the shape `skill-config/v1` takes, with the frontmatter
|
|
264
327
|
written under the name the caller gives; a name that isn't lowercase letters, digits and single
|
|
265
|
-
hyphens, up to 64, is refused `invalid_argument`.
|
|
266
|
-
|
|
267
|
-
|
|
328
|
+
hyphens, up to 64, is refused `invalid_argument`. A skill with other files beside its SKILL.md
|
|
329
|
+
returns them as a third value, `{relative path: text}`, and `get` answers them as `files`.
|
|
330
|
+
agent-env's `add_skills` step installs it on an agent through `env_ids`, named after the env's
|
|
331
|
+
environment name, a skill with files as a bundle of them; for an env behind a gateway, whose card
|
|
332
|
+
nests its servers' cards, it installs each server's skill under that server's name.
|
|
268
333
|
|
|
269
334
|
### Uploading an export through a grant
|
|
270
335
|
|
|
@@ -426,7 +491,9 @@ driver has them, through the `served_tools`, `served_extensions` and `served_ope
|
|
|
426
491
|
features. The driver serves its skill through `skill/v1` (an `@agent_skill` handler on the base
|
|
427
492
|
class): a SKILL.md written from the computer as it booted (its platform, session user and home,
|
|
428
493
|
the live screen size, what its input takes, its OS's shortcuts) and ending with the driver
|
|
429
|
-
class's `notes`, markdown for what its card can't say.
|
|
494
|
+
class's `notes`, markdown for what its card can't say. A driver whose software ships a skill of
|
|
495
|
+
its own returns it from `bundled_skill()` (its description, body and other files), and the
|
|
496
|
+
computer skill leads with it, the computer's part following.
|
|
430
497
|
- **Framework extensions.** `computer-ready/v1` (`status`: real checks of the display and a
|
|
431
498
|
capture, the driver's own `ready_checks()`, and one per app on the computer, asked of the app's
|
|
432
499
|
own server (see Computer apps); tools refuse `device_unavailable` until they pass once), `computer-display/v1`, `computer-spaces/v1` (the default space, written into the tool
|
|
@@ -548,37 +615,75 @@ server, which needs the `app` extra (below).
|
|
|
548
615
|
|
|
549
616
|
```python
|
|
550
617
|
from pydantic import BaseModel, SecretStr
|
|
551
|
-
from agentenv_protocol.computer import ComputerAppEnvironment, Launch,
|
|
618
|
+
from agentenv_protocol.computer import (ComputerAppEnvironment, Launch, Packages, Service, computer_app,
|
|
619
|
+
install_step, linux)
|
|
620
|
+
|
|
621
|
+
class LinuxSettings(BaseModel):
|
|
622
|
+
font: str = "DejaVu Sans"
|
|
552
623
|
|
|
553
624
|
@computer_app(supports=[{"os": {"type": "linux", "display": ["x11"]}, "arch": ["amd64", "arm64"]}])
|
|
625
|
+
@linux(packages=Packages(apt=["thunderbird"]), python="requirements.txt",
|
|
626
|
+
services=[Service(name="imap", command="venv/bin/python imap.py")],
|
|
627
|
+
launch=Launch(command=["thunderbird"], window={"class": "thunderbird"}), settings=LinuxSettings)
|
|
554
628
|
class MailApp(ComputerAppEnvironment):
|
|
555
|
-
linux = Linux(python="requirements.txt", services=[Service(name="imap", command="venv/bin/python imap.py")])
|
|
556
|
-
launch = Launch(command=["thunderbird"], window={"class": "thunderbird"})
|
|
557
629
|
skill = "skill.md"
|
|
558
630
|
|
|
559
631
|
class Settings(BaseModel):
|
|
560
632
|
signature: str = ""
|
|
561
633
|
password: SecretStr = SecretStr("")
|
|
634
|
+
|
|
635
|
+
class BuildSettings(BaseModel):
|
|
636
|
+
locale: str = "en_US"
|
|
637
|
+
|
|
638
|
+
@install_step(scope="session")
|
|
639
|
+
def profile(self) -> None: ...
|
|
562
640
|
```
|
|
563
641
|
|
|
564
642
|
- **`@computer_app`** names the platforms the app supports, as a driver names them, and the
|
|
565
643
|
`permissions` it needs of the computer. Its env.toml names the rest: the app itself
|
|
566
|
-
(`environment_name`)
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
644
|
+
(`environment_name`), the `computer` and `driver` it deploys alone on, and `build_settings`, its
|
|
645
|
+
build settings' values (`build_settings = { locale = "fr_FR" }`).
|
|
646
|
+
- **`@linux(...)`** is how a Linux computer installs it, `Linux`'s fields: `files`, a folder of the app's
|
|
647
|
+
copied under its prefix, which holds the class's file; `packages`, the OS's, as apt takes them
|
|
648
|
+
(`Packages(apt=["name", "name=version", "name/suite"])`); `python`, a requirements file there,
|
|
649
|
+
installed into the venv every app gets beside them; `sources`, extra apt repositories
|
|
650
|
+
(`AptSource`, with a key file in the folder); `downloads`, files fetched by URL and checked by
|
|
651
|
+
sha256 (`Download`); `run`, scripts of the folder run as root or the session user (`RunScript`);
|
|
652
|
+
`services`, programs the computer's init runs from the prefix as the session user; and `ports`,
|
|
653
|
+
those it listens on. Packages, requirements and downloads are resolved and fetched once, when the
|
|
654
|
+
computer env is put, and every build installs only those stored files, with the network closed.
|
|
655
|
+
Each value is checked as the class is defined: a URL, a sha256, a path that stays in the folder.
|
|
656
|
+
Installs are exact: apt installs each package without its recommends, and the venv gets exactly
|
|
657
|
+
the wheels resolved for it (`pip install --no-deps`). A requirements file lists everything itself,
|
|
658
|
+
since its lock covers that file alone: a line including another file (`-r`, `-c`), an editable
|
|
659
|
+
install (`-e`), a local path or a local folder of wheels is refused at put.
|
|
660
|
+
- **Each OS has its own decorator,** and what differs by OS goes in it: `launch=` always; and
|
|
661
|
+
optionally `skill=`, a file used instead of the class's, and `settings=` and `build_settings=`,
|
|
662
|
+
models whose fields add to the class's `Settings` and `BuildSettings` on that OS. What's on the
|
|
663
|
+
class applies to every OS. `@install(os_type, spec, launch=..., ...)` is the generic form, for an
|
|
664
|
+
OS type a plugin adds, with that OS type's own install model as `spec`; `@linux(...)` is
|
|
665
|
+
`@install("linux", Linux(...), ...)`. On the computer the class reads what's declared for the OS
|
|
666
|
+
it runs on (`platform.detect()`).
|
|
667
|
+
- **`launch=Launch(...)`** says how it starts in the desktop session: with it (`at="boot"`) or when
|
|
572
668
|
the agent opens it (`"demand"`). `ready=Ready(...)` says when it's ready, within `timeout_s` of its
|
|
573
669
|
launch: a window matching `launch.window` by its class, instance or title, a port answering on
|
|
574
670
|
loopback, or a command exiting 0. `@ready_check` marks a method for readiness that needs logic (a
|
|
575
671
|
sync one runs in a thread), and `@install_step(scope=...)` one a computer runs as it installs the
|
|
576
|
-
app
|
|
672
|
+
app, after its run scripts, offline and without the class's `__init__`. Each runs on every OS the
|
|
673
|
+
app installs on, or on those its `os=` names (`@install_step(os="linux")`, `@ready_check(os=[...])`);
|
|
674
|
+
naming one the app declares no install for is refused when the class is read. A step reads
|
|
675
|
+
`self.build_settings`; a deploy's `self.settings` aren't known at build, and reading one raises
|
|
676
|
+
`AttributeError` (`python -m agentenv_protocol.computer.run_install_step <file>:<Class> <method>`).
|
|
577
677
|
- **Its skill** is a file of its folder (`skill = "skill.md"`) or an `@agent_skill` method, not both;
|
|
578
678
|
its data hooks are the usual `@add_data`, `@get_data` and `@reset_data`.
|
|
579
679
|
- **`Settings`** is typed; a computer hands each app the values a deploy gives it, as one JSON value
|
|
580
680
|
in `APP_SETTINGS_ENV` (`AGENTENV_APP_SETTINGS`, the apps' values by name), and the base class reads
|
|
581
681
|
its own, by `ENVIRONMENT_NAME`, as `self.settings`. A `SecretStr` field is a secret.
|
|
682
|
+
- **`BuildSettings`** is typed too: values the app's env fixes in its computer's image, which only
|
|
683
|
+
another image changes, so a deploy can't give one. The composer writes them to
|
|
684
|
+
`.agentenv/build_settings.json` in the app's folder, and the base class reads them, over the
|
|
685
|
+
defaults, as `self.build_settings`, in its install steps and on the computer alike. None is a
|
|
686
|
+
secret, since the image keeps them; `declaration()` refuses a `SecretStr` one.
|
|
582
687
|
|
|
583
688
|
On a computer each app is its own server: `python -m agentenv_protocol.computer.serve_app
|
|
584
689
|
<file>:<Class>`, a program the computer's init runs from the app's folder in the app's venv, which
|
|
@@ -11,8 +11,11 @@ from .agent_env_environment import (
|
|
|
11
11
|
AgentEnvEnvironment,
|
|
12
12
|
AgentEnvFastMCPApplication,
|
|
13
13
|
AgentEnvStarletteApplication,
|
|
14
|
+
ForwardedTool,
|
|
14
15
|
add_data,
|
|
15
16
|
agent_skill,
|
|
17
|
+
resource,
|
|
18
|
+
server_instructions,
|
|
16
19
|
create_fastmcp_app,
|
|
17
20
|
environment_card,
|
|
18
21
|
extension,
|
|
@@ -88,12 +91,15 @@ __all__ = [
|
|
|
88
91
|
"AgentEnvEnvironment",
|
|
89
92
|
"AgentEnvFastMCPApplication",
|
|
90
93
|
"AgentEnvStarletteApplication",
|
|
94
|
+
"ForwardedTool",
|
|
91
95
|
"create_fastmcp_app",
|
|
92
96
|
"environment_card",
|
|
93
97
|
"reset_data",
|
|
94
98
|
"add_data",
|
|
95
99
|
"get_data",
|
|
96
100
|
"agent_skill",
|
|
101
|
+
"resource",
|
|
102
|
+
"server_instructions",
|
|
97
103
|
"extension",
|
|
98
104
|
"tool",
|
|
99
105
|
"injecting",
|