Skip to content

feat(sdk): attachment block ingress, model capabilities, and the delivery chain - #5617

Merged
mmabrouk merged 4 commits into
release/v0.107.0from
wp3-sdk-producer
Aug 1, 2026
Merged

feat(sdk): attachment block ingress, model capabilities, and the delivery chain#5617
mmabrouk merged 4 commits into
release/v0.107.0from
wp3-sdk-producer

Conversation

@mmabrouk

Copy link
Copy Markdown
Member

The runner understands attachment references and model capabilities since WP2, but nothing produces them: the SDK's Vercel adapter has no way to carry a server-issued attachment id, the wire has no model-modality fact, and a failed run reaches the browser as a bare string. This PR is work package 3 of 4 of the Stage 1 plan, the producer side, stacked on #5597.

Before: a file part is always inlined base64; modelCapabilities does not exist; result_from_wire raises a bare RuntimeError discarding everything but the message; delivery outcomes never reach the stream.
After: a file part whose providerMetadata.agenta.attachmentId is a canonical UUID becomes a neutral attachment block (ingress-only: the neutral block carries no URL, so the reverse mapping cannot exist and the docstring says so); size rides the same metadata envelope, because the pinned AI SDK's FileUIPart has no top-level size field and validation would strip it; the resolved connection emits modelCapabilities.inputModalities sourced from the model catalog at the resolver boundary, where a lookup miss omits the field entirely so the runner reads unknown, and bare dated Claude ids fall back to the same sourced fact in the Pi catalog; run failures raise a typed AgentRunFailed with a stable failure_code and sanitized message; attachment_delivery events project onto the Vercel stream as data-attachment-delivery parts in both twins. A new shared golden pins the attachment request shape in both contract suites; the two existing goldens are byte-identical.

The change was adversarially reviewed before this PR opened. The review's headline finding was empirically verified against the pinned AI SDK: the first implementation put the stable error code on the Vercel error frame, and the SDK validates that frame with a zod strictObject that rejects unknown keys, so every error-carrying stream would have aborted with an opaque parse failure instead of showing the sanitized message. The code now travels in a data-agent-error part emitted before the standard two-key error frame. The review also settled the plan's open key-space question (the Pi path resolves for the common case; the dated-Claude miss is closed by the cross-catalog fallback) and exposed one semantics defect at the WP2 seam, fixed on the WP2 branch below: the catalogs only ever enumerate text and image, so the runner's gate treating absence as "unsupported" asserted a false negative for documents; absence now reads as unknown.

Implicit decisions and their tradeoffs

  • The failure code is SDK-owned (agent_run_failed, runner_error, no_output, or the exception's own failure_code): the deployed runner result carries only error?: string, so there was nothing to mirror. The attribute is failure_code, not code, because the errors module already uses code for an integer HTTP status.
  • attachment_delivery parses through the generic event path rather than a dedicated branch: the generic path preserves the full payload, and a special case would only duplicate it. Regression coverage pins it.
  • The model catalog is no longer decorative and its docstring now says so: its modalities field feeds the runtime delivery gate through the connection resolver, though the catalog itself still gates nothing.
  • This lane sits above fix(sdk): ship Pi's built-in tools in the default agent template (#5590) #5597 and the sandbox-slug branch deliberately: the typed-failure work and the contract-test additions build on those lanes' changes in the same regions, so the lanes were linearized into the train (the sandbox-slug content already merged as [5575] fix(sdk): Make the sandbox-refused error slug provider-neutral #5585 and dissolves on the next rebase).

Forced routes to double-check

  • A catalog miss means workspace-only delivery for that model's uploads. The resolver never guesses; the cost of the honesty rule is that a model absent from both catalogs delivers attachments to the workspace with a notice until the catalog learns it. The cross-catalog fallback closes the known dated-Claude class; anything else missing is a data addition, not a code change.
  • Deploy note: model capabilities join the runner's config fingerprint (WP2), so the first deploy of this package cold-starts warm sessions whose model resolves, once. A runner deploy already does that to every parked session, so there is no second disruption class.

QA

704 Python agent tests and 1,377 runner tests pass; ruff clean; both existing goldens byte-identical (sha256-verified before and after). End-to-end live QA (real references flowing browser to model) runs with WP4 on the dev stack; this package alone is dormant until the front end produces references, which is the reader-first rollout working as designed.

https://claude.ai/code/session_01A1XQVjHPYJgVBHWSNUphtx

@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview Aug 1, 2026 6:00pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for image attachments, including attachment metadata and delivery events.
    • Added model input-modality capabilities to identify models that support images.
    • Added standardized agent failure codes and structured streaming error messages.
    • Improved compatibility with Vercel UI file messages and attachment handling.
  • Bug Fixes

    • Invalid attachment metadata now falls back safely to standard image handling.
    • Unsupported outbound attachments are omitted without disrupting responses.
  • Documentation

    • Documented multimodal workflow behavior, error handling, catalog fallbacks, and future modality constraints.

Walkthrough

The SDK now supports attachment content blocks, model input-modality metadata, structured agent failures, attachment-delivery stream events, and Vercel adapter projections. Python and runner wire-contract tests cover image attachment requests and multimodal result handling.

Changes

Multimodal agent delivery

Layer / File(s) Summary
Runtime and wire contracts
sdks/python/agenta/sdk/agents/{dtos.py,errors.py,wire_models.py,utils/wire.py,__init__.py}
Adds attachment metadata, modelCapabilities, and the public AgentRunFailed exception.
Model capability resolution
sdks/python/agenta/sdk/agents/{model_catalog.py,connections/*,platform/connections.py}, sdks/python/oss/tests/pytest/unit/agents/connections/*
Resolves model input modalities across catalog and connection paths, with normalization and fallback behavior.
Vercel attachment and error projection
sdks/python/agenta/sdk/agents/adapters/vercel/*, sdks/python/oss/tests/pytest/unit/agents/adapters/*, sdks/python/oss/tests/pytest/unit/agents/test_ui_messages.py
Maps valid attachment files, emits attachment-delivery events, and pairs structured error metadata with standard error frames.
Cross-runtime wire validation
sdks/python/oss/tests/pytest/unit/agents/golden/*, sdks/python/oss/tests/pytest/unit/agents/test_wire_*, sdks/python/oss/tests/pytest/unit/agents/test_result_multimodality.py, services/runner/tests/unit/wire-contract.test.ts, docs/design/.../stage-1.md
Validates attachment request serialization, model capabilities, result events, failure handling, and protocol constraints.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant VercelAdapter
  participant AgentStream
  participant ConnectionResolver
  participant ModelCatalog
  Client->>VercelAdapter: submit file part with attachment metadata
  VercelAdapter->>AgentStream: convert file to attachment block
  ConnectionResolver->>ModelCatalog: resolve model input modalities
  ModelCatalog-->>ConnectionResolver: return text and image modalities
  AgentStream-->>VercelAdapter: emit attachment delivery or failure event
  VercelAdapter-->>Client: emit structured data and standard stream frame
Loading

Possibly related PRs

  • Agenta-AI/agenta#4791: Introduced the shared agent runtime and Vercel adapter APIs extended by this change.
  • Agenta-AI/agenta#5439: Documents the multimodality SDK, attachment references, and model input-modality gating implemented here.
  • Agenta-AI/agenta#4936: Shares stream-adapter changes for Vercel event behavior.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 60.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main SDK changes for attachment ingress, model capabilities, and delivery handling.
Description check ✅ Passed The description directly explains the attachment, model capability, typed failure, delivery, testing, and deployment changes.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wp3-sdk-producer

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

resolved_code = AgentRunFailed.failure_code
resolved_text = _as_text(error_text)
yield {
"type": "data-agent-error",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The error code deliberately rides this separate data part, never the error frame itself: the pinned AI SDK validates the error chunk as a zod strictObject and an extra key aborts the whole stream with a parse failure (empirically verified during review). If you ever need more fields on failures, they go here, not on the frame.

return _CLAUDE_CATALOG


def model_input_modalities(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This lookup is the one place the wire learns what a model can see, and its honesty rule is the load-bearing part: a miss returns None so the field is omitted and the runner reads unknown (workspace-only with a notice), never a guess. The dated-Claude fallback reads the same sourced fact from the Pi catalog under anthropic/; that is a second read of recorded data, not an inference.

@mmabrouk

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mmabrouk
mmabrouk marked this pull request as ready for review August 1, 2026 00:28
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. Feature Request New feature or request SDK labels Aug 1, 2026
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Status Destroyed (PR closed)

Updated at 2026-08-01T18:14:58.673Z

Comment thread sdks/python/agenta/sdk/agents/model_catalog.py Outdated
@mmabrouk

mmabrouk commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fd53e2b7-f116-46b7-b150-076a9a3c13c5

📥 Commits

Reviewing files that changed from the base of the PR and between 200c360 and df5321b.

📒 Files selected for processing (26)
  • docs/design/agent-workflows/projects/agent-multi-modality/protocols/stage-1.md
  • sdks/python/agenta/sdk/agents/__init__.py
  • sdks/python/agenta/sdk/agents/adapters/vercel/messages.py
  • sdks/python/agenta/sdk/agents/adapters/vercel/stream.py
  • sdks/python/agenta/sdk/agents/connections/models.py
  • sdks/python/agenta/sdk/agents/connections/resolver.py
  • sdks/python/agenta/sdk/agents/dtos.py
  • sdks/python/agenta/sdk/agents/errors.py
  • sdks/python/agenta/sdk/agents/model_catalog.py
  • sdks/python/agenta/sdk/agents/platform/connections.py
  • sdks/python/agenta/sdk/agents/utils/wire.py
  • sdks/python/agenta/sdk/agents/wire_models.py
  • sdks/python/oss/tests/pytest/unit/agents/adapters/test_vercel_messages_roundtrip.py
  • sdks/python/oss/tests/pytest/unit/agents/adapters/test_vercel_stream_conformance.py
  • sdks/python/oss/tests/pytest/unit/agents/adapters/test_vercel_stream_multimodality.py
  • sdks/python/oss/tests/pytest/unit/agents/connections/test_model_catalog.py
  • sdks/python/oss/tests/pytest/unit/agents/connections/test_models.py
  • sdks/python/oss/tests/pytest/unit/agents/connections/test_resolver.py
  • sdks/python/oss/tests/pytest/unit/agents/golden/run_request.attachment.json
  • sdks/python/oss/tests/pytest/unit/agents/platform/test_connections_http.py
  • sdks/python/oss/tests/pytest/unit/agents/test_dtos_content_blocks.py
  • sdks/python/oss/tests/pytest/unit/agents/test_result_multimodality.py
  • sdks/python/oss/tests/pytest/unit/agents/test_ui_messages.py
  • sdks/python/oss/tests/pytest/unit/agents/test_wire_contract.py
  • sdks/python/oss/tests/pytest/unit/agents/test_wire_models.py
  • services/runner/tests/unit/wire-contract.test.ts

Comment on lines +892 to +897
resolved_text = _as_text(error_text)
yield {
"type": "data-agent-error",
"data": {"code": resolved_code, "errorText": resolved_text},
}
yield {"type": "error", "errorText": resolved_text}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Sanitize runner error text in the shared emission helper.

_error_parts converts error_text with _as_text only. The runner-error paths can therefore send stack frames, filesystem paths, or redaction candidates to both UI-facing error parts. Apply sanitize_runner_error here before emitting either part.

Proposed fix
-    resolved_text = _as_text(error_text)
+    resolved_text = sanitize_runner_error(error_text)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
resolved_text = _as_text(error_text)
yield {
"type": "data-agent-error",
"data": {"code": resolved_code, "errorText": resolved_text},
}
yield {"type": "error", "errorText": resolved_text}
resolved_text = sanitize_runner_error(error_text)
yield {
"type": "data-agent-error",
"data": {"code": resolved_code, "errorText": resolved_text},
}
yield {"type": "error", "errorText": resolved_text}

…very chain

The Vercel adapter maps a file part whose providerMetadata.agenta carries a
canonical-UUID attachmentId (and optionally size, since FileUIPart has no
top-level size field) into the neutral attachment block; the mapping is
ingress-only, because the neutral block carries no URL to rebuild a part from.
The resolved connection puts the model's input modalities on the wire as
modelCapabilities, sourced from the catalog at the resolver boundary; a lookup
miss omits the field so the runner reads unknown, and bare dated Claude ids
fall back to the same sourced fact in the Pi catalog. Run failures raise a
typed AgentRunFailed with a stable failure_code and sanitized message; the
Vercel stream keeps the error frame at exactly {type, errorText} (the pinned
AI SDK validates it strictly) and carries the code in a preceding
data-agent-error part; attachment_delivery events project as
data-attachment-delivery in both stream twins. A new shared golden pins the
attachment request shape in both contract suites; the two existing goldens are
byte-identical.

Claude-Session: https://claude.ai/code/session_01A1XQVjHPYJgVBHWSNUphtx
…arized base

errors.py and the wire-contract suite build on the sandbox-slug rename and the
Pi-builtins changes in the same regions, which is why those two lanes now sit
below this one in the stack.

Claude-Session: https://claude.ai/code/session_01A1XQVjHPYJgVBHWSNUphtx
@mmabrouk
mmabrouk force-pushed the fix/pi-default-builtins branch from 4708ca5 to 356aa03 Compare August 1, 2026 17:58
@mmabrouk
mmabrouk changed the base branch from fix/pi-default-builtins to release/v0.107.0 August 1, 2026 18:14
@mmabrouk
mmabrouk merged commit 2d77c87 into release/v0.107.0 Aug 1, 2026
40 of 41 checks passed
@mmabrouk
mmabrouk deleted the wp3-sdk-producer branch August 1, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature Request New feature or request lgtm This PR has been approved by a maintainer SDK size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant