a2a-sdk v1.0.2 replaced the v0 Pydantic discriminated-union types
(Part(root=TextPart(...))/Part(root=FilePart(file=FileWithUri(...))))
with a single protobuf Part struct that has optional `text`, `url`,
`raw`, `data`, `filename`, `media_type` fields. The classes
FilePart, TextPart, FileWithUri don't exist in v1 — import fails:
File "claude_sdk_executor.py", line 592
from a2a.types import FilePart, FileWithUri, Message, Part, Role, TextPart
ImportError: cannot import name 'FilePart' from 'a2a.types'
Production impact: every claude-code workspace (Design Director, UX
Researcher, all coordinators in molecule-core teams) crashes on
result delivery whenever the response includes a /workspace/* file
reference. The A2A delegation loop is broken at the result-delivery
step. Workspaces can receive tasks but can't ship results back.
Fix:
- Drop FilePart/TextPart/FileWithUri imports (don't exist in v1).
- `Part(root=TextPart(text=t))` → `Part(text=t)`.
- `Part(root=FilePart(file=FileWithUri(uri=u, name=n, mimeType=m)))` →
`Part(url=u, filename=n, media_type=m)`.
- `messageId=...` → `message_id=...` (snake_case in protobuf).
- `Role.agent` → `Role.ROLE_AGENT` (v1 enum).
Verified by constructing the exact shape against v1.0.2 in the
running claude-code template image:
Message:
message_id: 03ff9367
role: ROLE_AGENT
parts count: 2
text part: hello
file part: workspace:foo.txt foo.txt text/plain
Refs: molecule-core memory `reference_a2a_sdk_v0_to_v1_migration`
documents the Pydantic→protobuf shift; this is the fifth migration
finding today (after the new_agent_text_message rename in
crewai/openclaw/autogen/gemini-cli).
Test plan:
- [x] `python3 -m py_compile claude_sdk_executor.py` clean.
- [x] Runtime construction smoke verified against the live v1.0.2
a2a-sdk in the claude-code template image.
- [ ] End-to-end: provision a claude-code workspace, send a task
whose response references a /workspace/* file, confirm
result lands without ImportError.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same a2a-sdk 1.x rename already shipped in hermes/executor.py and
workspace/a2a_executor.py: a2a-sdk dropped `new_agent_text_message`
in favor of `new_text_message` (role=Role.agent default preserves
behavior). Three call sites in this file.
Symptom: every claude-code workspace died at create_executor →
ImportError: cannot import name 'new_agent_text_message' from
'a2a.helpers'. Why this slipped past every prior fix:
The boot smoke gate only does `import adapter`. adapter.py imports
ClaudeSDKExecutor lazily INSIDE create_executor() (line 106),
which means claude_sdk_executor.py is never loaded at module
import time. The lazy-load pattern hid the bug from CI.
molecule-ci PR #8 (lint + import-every-app-py smoke) catches this
class going forward — the new smoke loop iterates every /app/*.py
including claude_sdk_executor.py, forcing module-level imports to
resolve.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five `from <runtime_module> import` statements in adapter.py +
claude_sdk_executor.py were never qualified when the template was
extracted to its own repo (#87). They worked when the runtime was
bundled into workspace/ where bare imports resolved against
sibling files; in the template repo they explode at startup with
ModuleNotFoundError as soon as Python reaches the import.
Caught by manual provision after pipeline-3 wire-real E2E. The
plugins import was the first one tripped because it sits in
adapter.setup() — earlier bare imports inside claude_sdk_executor.py
are deferred until the executor is constructed.
Pattern: any `from <X> import Y` where X is a workspace/ module ->
`from molecule_runtime.X import Y`. Fixes:
- adapter.py:97 plugins
- claude_sdk_executor.py executor_helpers, heartbeat, a2a_client, platform_auth
Same class of bug as the runtime's TOP_LEVEL_MODULES drift but
inverted — instead of forgetting to rewrite imports IN the wheel,
the template authors forgot to qualify imports IN the template
code (the build script's rewriter only runs on workspace/ -> wheel).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First half of molecule-core task #87 — move adapter-specific code out
of the universal molecule-runtime package into the template that
actually consumes it.
Adds:
- claude_sdk_executor.py (757 LOC) — copied verbatim from
molecule-core/workspace/claude_sdk_executor.py @ commit 186f25c2.
The adapter at adapter.py:59 already does
`from claude_sdk_executor import ClaudeSDKExecutor` — once this
file lands at /app/, Python's import order picks the local copy
over the same-named module that older molecule-runtime versions
ship under site-packages.
- Dockerfile: COPY claude_sdk_executor.py . alongside adapter.py.
Pure additive at this stage — molecule-runtime still ships the
file too, so any image built from this template just has two copies
on disk (local /app shadows the site-packages one). No behavior
change.
Sequencing (the molecule-core PR follows AFTER this image rebuilds):
1. THIS PR — template gets local copy, image rebuilds with it
(current PR; safe because no removal yet)
2. molecule-core PR — drop workspace/claude_sdk_executor.py, bump
molecule-ai-workspace-runtime PyPI version. Templates that
haven't pulled the new runtime version still work because their
local copy is unchanged.
3. (later) Bump requirements.txt pin in this template once the
new runtime version is on PyPI, so future builds explicitly
install the slimmed runtime.
Why local-copy-first:
- Reverse order (drop from runtime first, then add to template)
creates a window where any template image build pulling the
latest runtime would fail to import claude_sdk_executor.
- This order has zero downtime: every intermediate state is valid.
Validates the capability primitives shipped in molecule-core PRs
#2137-#2144 — once this template image rebuilds and the molecule-
core deletion lands, the claude-code workspace is the FIRST adapter
to live entirely outside molecule-runtime, with native_session +
idle_timeout_override declared via capabilities() (PR #12 here).
Source: molecule-core/workspace/claude_sdk_executor.py @ 186f25c2
(commit hash pinned for traceability of any future divergence).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>