feat(agents): stage files and download turn artifacts (#4009)
## Summary
Stage selected local files and read an artifact from the exact completed
turn, either in memory or streamed to a local path. The helpers keep
upload IDs available for explicit cleanup and stream downloads to a
caller-chosen path.
Before:
```python
uploaded = client.files.create(file=Path("source.pdf"), purpose="user_data")
inputs = [{"type": "file_id", "file_id": uploaded.id, "path": "/workspace/source.pdf"}]
# Pass inputs when creating the hosted environment, then run the session.
artifact = next(a for a in client.beta.agents.sessions.artifacts.list(result.session_id)
if a.turn_id == result.turn_id and a.path == "/workspace/outputs/report.md")
with client.beta.agents.sessions.artifacts.with_streaming_response.content(
artifact.id, session_id=result.session_id,
) as content:
content.stream_to_file("report.md")
```
After:
```python
prepared = client.beta.agents.environments.files.prepare({
"/workspace/source.pdf": Path("source.pdf"),
})
# Pass prepared.files when creating the hosted environment, then run the session.
artifacts = client.beta.agents.sessions.artifacts.for_result(result)
report_bytes = artifacts.content("/workspace/outputs/report.md").content
# Or stream to an application-owned path:
artifact = artifacts.download("/workspace/outputs/report.md", to=Path("report.md"))
```
`prepare_directory(..., include=[...])` selects a directory snapshot;
`files.upload(...)` uploads and stages one file in an existing
environment. Sync and async helpers live under the beta Agents
namespace. They preflight selected files before uploading, preserve
partial upload ownership on errors, and detect missing or ambiguous
artifacts across all pages.
Local path selection is intended for static application-owned files and
stable directories; it is not a filesystem sandbox for untrusted paths
or hostile local writers.
This PR now targets `main` directly. Reattachment/result recovery is
deferred: a silent attachment cannot reliably distinguish pending work
from an already-completed turn, so these helpers do not depend on
SDK-side recovery heuristics.
### Stack
- https://github.com/openai/openai-python/pull/4004 (merged
prerequisite)
- https://github.com/openai/openai-python/pull/4007 (merged
prerequisite)
- https://github.com/openai/openai-python/pull/4008 (closed/deferred;
not a dependency)
- https://github.com/openai/openai-python/pull/4009 👈 this PR A
Alex Chang committed
4fc2438ea73f8c1a4802b0cd5ab7607fc3cc1d16
Parent: a91d779
Committed by GitHub <noreply@github.com>
on 10/1/2026, 9:53:49 PM