Skip to content

feat(prompt-agent): a user-sim agent receives readable URLs for store-owned file parts too - #61

Merged
earakely-scale merged 9 commits into
mainfrom
edgararakelyan/one-send-helper
Oct 7, 2026
Merged

earakely-scale merged 9 commits into
mainfrom
edgararakelyan/one-send-helper

Conversation

@earakely-scale

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

Copy link
Copy Markdown
Collaborator

Summary

prompt_agent sends messages to two peers: the target agent, and the user agent that answers it in a multi-turn conversation. Only the first went through readable_parts (#57). The user agent was sent the target agent's reply parts unchanged. So a user-sim agent received a file part naming a store-owned object as the store's raw URL, which it can't read without the store's credentials.

Both sends now go through one helper, send_and_wait, in a2a_agent/object_transfer.py. It sends parts to an A2A peer and polls the task until it ends.

  • A peer agent-env deployed (the target, or a user-sim running in a sandbox) is sent each file part a configured store owns as an HTTPS URL it can read, through readable_parts.
  • A user-sim is made able to read only the files the target was sent in this conversation. A reply is the target's model output, and core must not mint a readable URL for any object it happens to name. prompt_agent collects the file URIs it sends the target and passes them as shareable. Any other store-owned URI in a reply is sent as it is, as before.
  • Any other peer, such as a human-facing endpoint named by user_a2a_url or a registered human peer with no sandbox, reads the store itself. It is sent the parts as they are, so no signed URL is stored on its side.
  • before_send runs once the parts are ready, just before the message goes out. The target's turn is still recorded there, so an object the agent can't be sent still leaves no turn waiting for a reply.

There are no other message sends to agents with file parts in core: the rubrics judge and the trigger executor send text only.

Tests

  • Unit (prompt_agent_file_parts_test.py, readable_parts_test.py):

    • a user-sim is sent a file the target passes on as a grant;
    • a file the target names but was never sent reaches the user-sim as it is, with no grant;
    • a user-sim the store's grants can't reach gets a copy staged on it, staged before the message and cleared after the reply;
    • a user-sim that can be given no readable URL fails the step before it is sent anything;
    • registered and user_a2a_url human peers get the object's own URL;
    • readable_parts(shareable=...) makes only the named objects readable.
  • Integration, new tst/integration/task_step/test_prompt_agent_file_parts_local.py, fast tier, on real local agents:

    • the echo agent now reports what each file part it's sent holds, and answers send-file <uri> with a file part;
    • the solver fetches its prompt file over a grant;
    • the user-sim fetches the file the solver passes back, and can't open one the solver names but was never sent;
    • both kinds of human peer are sent the store's own URL;
    • the conversation records only the store's own URLs.

    Before this change there was no integration test that sent an agent a file part.

  • Mutant checks:

    • main's code fails the user-sim tests;
    • dropping the sandbox check fails the registered-human test;
    • this branch before shareable fails both guard tests.
  • Suite: tst/unit + protocol, 6263 passed. CI's integration tiers pass.

  • Plugin API: no break.

End-to-end and chaos results are in a comment below.

🤖 Generated with Claude Code

RetriggerConfidence Score: 5/5

The PR appears safe to merge; no new issue remains from the changes since the previous review.

Fix All in CursorFindings

  1. P2 Human file reads go untested ▶
Fix with agent prompt
### Issue 1
tst/integration/task_step/test_prompt_agent_file_parts_local.py:undefined-189
The human-peer test now expects the peer to fail when reading the file. The test peer no longer opens `file://` URLs, so this test can pass even if the URL sent to a human peer points to the wrong file. Keep the test agent's safe reader, but check the sent URL against the store object or use a separate trusted reader to check its contents.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

prompt_agent now prepares file parts for both deployed agents in a multi-turn conversation. User-sims get readable links only for files the target received, while human peers keep the store’s own URLs.

  • Deployed agents get readable file links for shared files.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Target[Target reply] --> Check{User peer}
  Check -->|Sandboxed user-sim| Shared{URI sent to target?}
  Shared -->|Yes| Readable[Grant or stage readable file]
  Shared -->|No| Original[Keep original URI]
  Check -->|Human peer| Original
  Readable --> Send[Send and wait]
  Original --> Send
Loading

Reviews (7) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..."

…-owned file parts too

prompt_agent's two sends, to the target agent and to the user agent, now go through one helper,
send_and_wait, which sends and polls an A2A peer. A peer that is an agent agent-env deployed (the
target, or a user-sim) is sent each file part a configured store owns as an HTTPS URL it can read,
through readable_parts; any other peer, such as a human-facing endpoint named by user_a2a_url, reads
the store itself and is sent the parts as they are, so no signed URL is stored there. Before, the
user-sim was sent the target agent's reply parts unchanged.

The target's turn is still recorded only once its parts are ready to send (send_and_wait's
before_send).

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 6, 2026 13:24
Comment thread src/agent_env/task_step/task_steps/prompt_agent.py Outdated
Comment thread src/agent_env/task_step/task_steps/prompt_agent.py Outdated
earakely-scale and others added 4 commits October 6, 2026 06:33
…sent the store's own URLs

deploy_human_agent registers a human peer as a deployed agent without a sandbox. Only a user-sim
running in a sandbox is sent readable URLs; a human peer reads the store itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… staged copy, and one with no readable URL fails before it is sent anything

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…peer reads the store itself

No integration test sent an agent a file part. The echo agent now reports what each file part it
is sent holds, and answers a `send-file <uri>` line with a file part, so a local run can show:
the solver fetches the file in its prompt and a user-sim the one the solver returns, both over
grants; a human peer, named by user_a2a_url or registered, is sent the store's own URL and reads
it from the store; and the conversation records only the store's own URLs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comment thread src/agent_env/task_step/task_steps/prompt_agent.py Outdated
…n sent the target

A target's reply is model output, and naming any object a store owns in it made core send the
user-sim a URL it could read, and so read the object out through the user-sim's reply. The
user-sim is now made able to read only the files the target was sent in this conversation;
any other object a store owns is sent as it is, as before this branch. readable_parts and
send_and_wait take the objects that may be made readable as `shareable`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale

earakely-scale commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator Author

End-to-end and chaos results (final head 855d29b)

Setup:

  • Real agents on remote sandboxes on two providers (Modal containers, and a VM provider), against a dev deployment whose S3 store issues grants.
  • The solver is the echo test agent. Its prompt carries one store file, and it returns that file plus a second it was only told the URL of.
  • The user-sims are the echo agent and two model-driven CLI wrappers (Claude Code, OpenCode).
  • Large files are checked by sha256. Signed URLs are recorded redacted.
case Modal VM provider
grants: user-sim fetches the shared file; the never-sent file arrives as s3:// pass pass
grants, 3 turns: the user-sim reads the shared file on each of its turns, through a fresh grant each time pass pass
no grants, signed URL pass pass
no grant or URL, staged on the agent pass fail*
staged, 3 turns: a copy is pushed before, and cleared after, each of the 3 sends that carry the file pass –
no URL or staging for the user-sim (on the other provider): the step fails before it is sent anything pass pass
human peer, by user_a2a_url and registered: sent the s3:// URLs as they are pass ×2 pass ×2
256 MB shared file over grants: the solver's and the user-sim's sha256 match pass pass
256 MB over a signed URL pass –
64 MB staged pass –
Claude Code user-sim quotes the shared file's passphrase; the never-sent file's secret appears nowhere pass pass
Claude Code user-sim computes a 128 MB shared file's sha256 with a shell command pass pass
OpenCode user-sim fail† fail†
user-sim killed 8 s after it is sent the files the agent is given up on after 61 s, the conversation ends and the step completes the poll gets a 404 and the step fails‡

* VM-provider staging fails for the solver and the user-sim alike. Inside the VM, the agent's own public hostname presents a self-signed certificate (confirmed with a probe from the container). So the target path from #57 fails the same way. It's a platform issue, tracked separately.

† OpenCode fails its turn when a message carries an s3:// attachment. It rejects that scheme, as it does on main, and the conversation ends cleanly after turn 1. When the solver passed on only the shared file, OpenCode read it and quoted the passphrase on both providers.

‡ This is poll_a2a_task's handling of a 4xx, unchanged by this PR. The VM provider's gateway answers 404 for a dead VM.

Also checked on this head:

Earlier rounds on 8c14a26 (the sharing fix) and e3d556c gave the same results for the cases they covered.

Comment thread tst/data/a2a_agent/agent.py Outdated
Comment thread tst/data/a2a_agent/agent.py Outdated
…le parts; name the test waits

Reading file:// paths let anyone who can reach a deployed echo agent read its files, its
environment included. The human-peer test now checks the peer is sent the store's own URL
without the agent opening it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
_, reply, peer_reply, _ = _conversation(await task.run())

assert _uris(reply) == [urls["report"]]
assert _text(peer_reply).splitlines()[-1].startswith("could not read report.txt over file:")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Human file reads go untested

The human-peer test now expects the peer to fail when reading the file. The test peer no longer opens file:// URLs, so this test can pass even if the URL sent to a human peer points to the wrong file. Keep the test agent's safe reader, but check the sent URL against the store object or use a separate trusted reader to check its contents.

Knowledge Base Used: Storage, objects, documents, and secrets

Prompt To Fix With AI
This is a comment left during a code review.
Path: tst/integration/task_step/test_prompt_agent_file_parts_local.py
Line: 189

Comment:
**Human file reads go untested**

The human-peer test now expects the peer to fail when reading the file. The test peer no longer opens `file://` URLs, so this test can pass even if the URL sent to a human peer points to the wrong file. Keep the test agent's safe reader, but check the sent URL against the store object or use a separate trusted reader to check its contents.

**Knowledge Base Used:** [Storage, objects, documents, and secrets](https://app.greptile.com/scale-ai/-/custom-context/knowledge-base/scaleapi/agentenv-framework/-/docs/storage-and-secrets.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Cursor Fix in Claude Code Fix in Codex

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8f7cf7e: the in-process peer records the file URIs it is sent, and the test asserts they are exactly the store object's own URL.

earakely-scale and others added 2 commits October 6, 2026 15:47
…wn URL, not just a file:// one

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

# Conflicts:
#	src/agent_env/task_step/task_steps/prompt_agent.py
@earakely-scale
earakely-scale merged commit bc8506e into main Oct 7, 2026
13 checks passed
@earakely-scale
earakely-scale deleted the edgararakelyan/one-send-helper branch October 7, 2026 01:23
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