- A2AWatchRegistry.Shared was initialised before DefaultPollDelays (static fields run in source order), so the bot's
registry had null poll delays and every poll watch crashed on its first wait, then sat in the registry doing
nothing until a restart. The delays now come first; a test reads them through Shared.
- TaskNotFound from the remote ends the watch at once with a "(lost)" notice instead of retrying for a day.
- A crashed watch loop restarts by polling after a pause; the third crash in a row ends it with a "(stopped)" notice.
Hour-long remote tasks were never delivered: a subscribe watch held one SSE stream for up to a day, the remote
sent nothing while its agent worked without text, and once the quiet connection died on the way (no FIN/RST) the
watch waited on it with no error and no log line.
- A subscription lasts at most 5 minutes (SubscriptionSlice), then GetTask and a new subscription: a pause, not a
failure. A slice ended mid-read counts as ours even when it surfaces as an I/O error.
- The shared handler connects with TCP keepalive (60 s, every 15 s, 4 probes) and HTTP/2 pings.
- Log lines when a watch starts or is restored, when a slice ends on a running task, when a result is delivered or
kept; the Telegram host's console log carries a UTC timestamp.
- action=status on a settled task ends its watch, so the result isn't delivered a second time.
- A "context busy" refusal of send says nothing was sent, names the watched task in that context and that its
result will arrive by itself; refusals never touch a watch.
The 1.0 SDK ignores unknown part fields, so a file part shaped {kind:file, file:{bytes|uri,
name, mimeType}} arrived as an empty part and vanished. A2AWireCompat rewrites such parts
to {raw|url, filename, mediaType} in JSON and SSE responses before the SDK reads them, and
marks any part it still can't read so the result lists it with its field names.
File parts were only taken from artifacts as inline bytes up to 10 MB; parts in the
status message or the history were dropped silently, URL parts were never downloaded,
and a plain message reply lost its files. Files now go to inbox/a2a/<taskId>/ from
artifacts, status message and recent agent messages (deduplicated), URLs are streamed
to disk (agent key only on the agent's origin, else https without it), the cap is the
new MaxReceivedFileBytes (200 MB), and results list path, bytes and media type or why
a file was not saved. Re-saving a task overwrites, so action=status refetches files.
Add docs/a2a.md (concepts, file formats, endpoints, sessions, task
lifecycle, configuration, security notes, curl examples) describing
what src/Volcanic.AgenticCore.A2A.Server actually does. Add an A2A
project paragraph to architecture.md, an "A2A" section to
telegram-bot.md (/a2a, notify_owner, isolated sessions, port 8080),
A2A_ENABLED/A2A_PUBLIC_URL and the aspnet image to docker.md, and
project-map/test-command updates to CLAUDE.md.
Mark the A2A server design spec implemented and record the deviations
found while building it (A2AHostOptions, synchronous
IA2ACallerDirectory.Find, generic WorkspaceCallerDirectory, per-status
push payload, split file-part error handling, the outbound guarded
handler, the per-caller server/router architecture, the restart grace
period, the owner= log field, the aspnet image, and the actual push
retry schedule). Note the aspnet image in the docker-deployment spec
too, without rewriting its historical decisions.