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 5 commits into
edgararakelyan/parts-write-grantfrom
edgararakelyan/env-write-object
Open

earakely-scale wants to merge 5 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. It declares the write grant kinds a handler uploads through as {"write": [...]}, derived from its signature:
    • write_namespace adds http-post-policy, so these handlers' cards gain this entry next to their existing data-objects/v1;
    • write_object adds http-put and http-put-parts.
  • 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 caller sends only a grant whose kind the card lists.
  • client.get_data(..., write_object=) sends the grant.
  • 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;
  • write_kinds for declared, data-objects-only and bare cards;
  • the handler receiving the grant or None;
  • an invalid grant refused without echoing its signature;
  • the client sending it;
  • 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.

The two existing advertisement tests now expect the transfers entry too.

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 blocking issues remain.

Summary

Adds write_object support to data/get, declares accepted write grant kinds, and adds helpers to read upload results.

  • A data/get handler can upload its export to one caller-named object.
  • Environment cards list the write grant kinds each handler accepts.

Diagram

sequenceDiagram
  participant C as Caller
  participant E as Environment
  participant S as Object store
  C->>E: Read card and accepted write kinds
  C->>E: data/get with write_object
  E->>S: Upload export through grant
  E-->>C: File part with upload result
Loading

Reviews (4) · Last reviewed commit: "test(integration): close the grant serve..." · 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 3 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>

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