Skip to content

feat(protocol): declare env write grant kinds and take a write_object on data/get - #149

Open
earakely-scale wants to merge 8 commits into
edgararakelyan/parts-write-grantfrom
edgararakelyan/env-write-object
Open

earakely-scale wants to merge 8 commits into
edgararakelyan/parts-write-grantfrom
edgararakelyan/env-write-object

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #147: review and merge that first, then this retargets to main.

Summary

An env service can now be handed one caller-named object to upload its data/get export to, and tell callers which write grants it takes. The data/get operation itself is unchanged.

  • The write_object parameter.
    • A @get_data handler may take an optional write_object: WriteObject.
    • _invoke_get validates it only for a handler that takes it, naming the failing fields and never their values, as for write_namespace.
    • The handler uploads with transfers.upload() (parallel parts for an http-put-parts grant) and answers uploaded_object_part(uploaded, name=..., mime_type=...), read back with uploaded_object(part). The name must be relative.
  • The declaration, urn:agentenv:transfers/v1. A handler that takes write_object declares the write grant kinds it uploads through as {"write": [...]}, derived from its signature: http-put and http-put-parts, after http-post-policy when it also takes write_namespace. A handler that takes only write_namespace keeps the card it had, data-objects/v1 alone, so every existing card (the computer drivers' included) is unchanged.
  • client.write_kinds(card) reads the declaration. A card without it counts as accepting http-post-policy alone if it advertises data-objects/v1, and nothing otherwise. A malformed declaration counts as the kinds it names by string, or none. A caller sends only a grant whose kind the card lists.
  • client.get_data(..., write_object=) sends the grant, beside write_namespace and paths. _invoke_get checks paths first, as before, so a handler without them refuses the call before any grant is used.
  • No data-objects/v2. The kind implies the parameter, and data-objects/v1 stays as it is for existing callers.

Wire freeze. test_the_object_write_wire_shape_is_frozen pins the field sets of HttpPartsPutGrant, HttpPutGrant, WriteObject and Uploaded. Receivers refuse unknown fields, so a field added later must be negotiated rather than sent to everyone. A receiver can advertise it in its transfers/v1 entry, whose unknown params readers ignore, or it can be a new grant kind.

Labelled bump-version: this is the release env services pin to start receiving parts grants. No issuer sends write_object yet; that lands in core separately.

Tests

test_data_objects.py covers:

  • the card kinds for write_object-only and combined handlers, and write_namespace-only cards unchanged;
  • write_kinds for declared, data-objects-only, bare and malformed cards;
  • paths and write_object handed together, and paths refused before an object grant is used;
  • the handler receiving the grant or None;
  • an invalid grant refused without echoing its signature;
  • the client sending it, alone and with the other params;
  • an end-to-end export through the real handler, parts uploader, client and reader;
  • the reply surviving the wire, non-object parts reading as None, and relative names.

Integration (local tier, server_env_local_test.py). The items test server now takes write_object: it uploads its state as a zip bundle, and data/add loads such a bundle back. The new test does the following:

  1. Deploy it in a local container.
  2. Hand it a 40-URL parts grant on the local TLS grant server.
  3. Check the server used only the URLs its bundle's size takes, more than PARTS_IN_FLIGHT of them.
  4. Check each part holds its range, and the parts joined make the bundle.

It also checks that the card declares http-put-parts. gateway_test.py's exact-set check of the items card now includes transfers/v1, and the README example names its bundle github.zip. Out-of-tree mutants are all killed: declaration dropped, write_object ignored, write_kinds precedence, reply marker ignored, name unvalidated, client dropping the grant, and an error echoing the grant.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no actionable defects were found.

Summary

Adds write_object support to data/get, declares accepted grant kinds, and provides helpers for uploaded-object replies.

  • Environment exports can now be uploaded to a caller-named object.

Diagram

sequenceDiagram
    participant C as Caller
    participant E as Environment
    participant S as Object store
    C->>E: Read card
    E-->>C: Accepted write grant kinds
    C->>E: data/get with write_object
    E->>E: Check grant
    E->>S: Upload object or parts
    E-->>C: FilePart with Uploaded marker
    C->>C: Read marker with uploaded_object
Loading

Reviews (5) · Last reviewed commit: "fix(protocol): write_kinds reads a decla..." · Reviewed by Greptile

… on data/get

A data/get handler may take a write_object (a WriteObject the caller names)
and answer uploaded_object_part(uploaded, name=..., mime_type=...), read back
with uploaded_object(part). Each grant parameter a handler takes is declared
under urn:agentenv:transfers/v1 as the write grant kinds it uploads through:
write_namespace adds http-post-policy, write_object adds http-put and
http-put-parts. client.write_kinds(card) reads the declaration, taking a card
without it to accept http-post-policy alone when it advertises data-objects/v1.
client.get_data sends write_object.

A test freezes the object-write wire shape: receivers refuse fields they do
not know, so a new capability must be a new grant kind.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale
earakely-scale requested a review from a team as a code owner October 9, 2026 20:14
Comment thread packages/agentenv-protocol/tests/test_data_objects.py
…objects default

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
earakely-scale and others added 6 commits October 9, 2026 15:23
…rts through the local grant server

The items test server takes write_object: handed an object, data/get uploads its state as a zip
bundle through it, and data/add loads such a bundle back. The new local-tier test hands the
deployed server a 40-URL parts grant on the local grant server, then checks three things: the
server used only the URLs its bundle's size takes, more than PARTS_IN_FLIGHT of them; each part
is its range; and the parts put together are the bundle. The gateway test's exact-set check of the
items card now includes transfers/v1, and the README example names its bundle github.zip.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The new parts test is the first in the run to start this process's grant server, inside its own state
root. A later test's containers trust that test's root's CA, so the server must start afresh, as the
prompt-agent tests' fixtures already do. Without this, the file-parts test failed after it in CI.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nv-write-object

# Conflicts:
#	packages/agentenv-protocol/README.md
#	packages/agentenv-protocol/src/agentenv_protocol/__init__.py
#	packages/agentenv-protocol/src/agentenv_protocol/agent_env_environment.py
#	packages/agentenv-protocol/src/agentenv_protocol/client.py
#	packages/agentenv-protocol/src/agentenv_protocol/types.py
#	tst/data/agentenv_mcp/server.py
#	tst/integration/env/envs/server_env_local_test.py
…te_object

A handler that takes only write_namespace keeps the card it had: a card without the declaration already reads as
taking the namespace grant alone, so every existing card, the computer drivers' included, stays as it was and the
conformance probe meets no new extension. write_kinds reads only the names a declaration lists, so a malformed card
takes no kind instead of raising, and data/get's paths and both grants are pinned working together.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… object as no kinds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@earakely-scale

Copy link
Copy Markdown
Collaborator Author

Merged the updated #147 (and so main) in. The conflicts were unions with main's data/get paths, SKILL_EXTENSION_URI and ToolContext; get_data takes write_namespace, write_object and paths, and paths is still checked first.

One behaviour change, in the follow-up commits:

  • transfers/v1 is now declared only by a handler that takes write_object. Declaring it for every write_namespace handler gave main's computer-driver cards an extension the conformance probe reports as unverified. Now every existing card is unchanged, and write_kinds already reads a data-objects-only card as http-post-policy.
  • write_kinds reads a malformed declaration as no kinds instead of raising.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant