Skip to content

fix(a2a): send v1 file Part shape; tolerate v1 server-side - #2509

Merged
HongmingWang-Rabbit merged 1 commit into
stagingfrom
fix/a2a-v1-file-part-shape
May 2, 2026
Merged

HongmingWang-Rabbit merged 1 commit into
stagingfrom
fix/a2a-v1-file-part-shape

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Summary

Image-only chats surface Error: message contained no text content. because canvas posts the v0 file-part shape {kind:"file", file:{uri,name,mimeType}} that the workspace runtime's a2a-sdk v1 protobuf parser silently drops. v1 Part has fields [text, raw, url, data, metadata, filename, media_type] — there is no kind, no nested file. With ignore_unknown_fields=True, ParseDict drops both legacy keys, producing a fully-empty Part. No text, no extracted attachments → the executor's "no text content" guard fires.

Three coordinated changes:

  1. canvas/ChatTab.tsx — outbound file parts now use the v1 flat shape {url, filename, mediaType} so the v1 parser populates Part fields instead of dropping them.
  2. workspace/executor_helpers.pyextract_attached_files learns a v1 detection branch (non-empty part.url + filename + media_type) alongside the existing v0 RootModel and flat-file shapes. Defends every runtime mounting the OSS wheel against any client still on the v0 wire.
  3. canvas/message-parser.tsextractFilesFromTask tolerates the v1 shape on incoming agent responses, so file chips render in chat history regardless of which Part shape the runtime emits.

Repro:
```python
from a2a.types import a2a_pb2
from google.protobuf.json_format import ParseDict
v0 = {"kind":"file","file":{"uri":"workspace:/x.png","name":"x.png","mimeType":"image/png"}}
p = ParseDict(v0, a2a_pb2.Part(), ignore_unknown_fields=True)

All fields empty → silently dropped by extract_attached_files (pre-fix).

```

Test plan

  • `workspace/tests/test_executor_helpers.py` — new v1-shape test + empty-Part defense (86 passed)
  • `canvas/.../message-parser.test.ts` — new v1 protobuf flat-shape extraction test + URL-basename fallback (91 passed)
  • `tsc --noEmit` clean across canvas
  • After merge + cascade: send image-only chat in hongming Claude Code Agent workspace; expect agent to receive the image (manifest line in prompt) instead of "no text content" error

Surfaced 2026-05-01 in hongming workspace after the /workspace chown fix unblocked uploads.

Image-only chats surface "Error: message contained no text content"
because canvas posts v0 `{kind:"file", file:{uri,name,mimeType}}` shapes
that the workspace runtime's a2a-sdk v1 protobuf parser silently drops:
v1 `Part` has fields `[text, raw, url, data, metadata, filename,
media_type]` and `ignore_unknown_fields=True` discards `kind`+`file`,
producing a fully-empty Part. With no text and no extracted file
attachments, the executor's "no text content" guard fires.

Three coordinated changes close the gap:

1. canvas/ChatTab.tsx — outbound file parts now carry the v1 flat
   shape `{url, filename, mediaType}` so the v1 protobuf parser
   populates Part fields instead of dropping them.
2. workspace/executor_helpers.py — extract_attached_files learns the
   v1 detection branch (non-empty `part.url` + `filename` +
   `media_type`) alongside the existing v0 RootModel and flat-file
   shapes. Defends every runtime that mounts the OSS wheel against
   the same drop, including any pre-fix client still on the wire.
3. canvas/message-parser.ts — extractFilesFromTask tolerates the v1
   shape on incoming agent responses too, so file chips render in
   chat history regardless of which Part shape the runtime emits.

Test pins:
- workspace/tests/test_executor_helpers.py:
  + v1 protobuf shape extraction
  + empty-Part defense (v0→v1 silent-drop fall-through returns [])
- canvas message-parser test:
  + v1 protobuf flat parts
  + filename fallback to URL basename for v1
@HongmingWang-Rabbit
HongmingWang-Rabbit added this pull request to the merge queue May 2, 2026
Merged via the queue into staging with commit bf83af0 May 2, 2026
22 checks passed
@HongmingWang-Rabbit
HongmingWang-Rabbit deleted the fix/a2a-v1-file-part-shape branch May 2, 2026 08:18
HongmingWang-Rabbit pushed a commit that referenced this pull request Jun 12, 2026
…oard access (core#2509)

Fixes #2509

1. switchOrgUrl now accepts `host` (includes :port) instead of `hostname`,
   so non-443 deployments navigate correctly. (core#2509-1)
2. The /cp/orgs fetch is moved outside the setState updater, eliminating
   the StrictMode double-fetch. A transient error now resets `orgs` to null
   when the menu re-opens, so "No other organizations" no longer caches
   forever. (core#2509-2)
3. The derived apex must contain at least one dot, preventing a 2-label host
   with empty currentSlug from yielding a foreign apex. (core#2509-3)
4. The org switcher block now has an onKeyDown handler (Enter/Space) so
   keyboard users can open it. (core#2509-4)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant