docs(a2a): A2A server guide, architecture, Telegram and Docker notes

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.
This commit is contained in:
EugeneTes
2026-10-07 14:20:19 +00:00
parent f87a8dd8c1
commit d542fcfe42
7 changed files with 291 additions and 8 deletions

View File

@@ -65,6 +65,9 @@ settings use the `GoogleCalendar` section, the scheduler the `Scheduler` section
| `PLAYWRIGHT_ENABLED` / `Mcp:Playwright:Enabled` | `false` | Browser tools through `@playwright/mcp` ([mcp.md](mcp.md)) |
| `PLAYWRIGHT_HEADLESS`, `PLAYWRIGHT_BROWSER`, `PLAYWRIGHT_EXTRA_ARGS`, `PLAYWRIGHT_COMMAND` | `true`, `chromium`, —, `npx` | See [mcp.md](mcp.md#the-playwright-preset) |
| `Mcp:Servers:<name>` | — | Other stdio MCP servers ([mcp.md](mcp.md#configuration)) |
| `A2A__Enabled` / `A2A:Enabled` | `false` | Serve other agents over A2A (see [A2A](#a2a) and [a2a.md](a2a.md)). Opens Kestrel on port 8080 |
| `A2A__PublicUrl` / `A2A:PublicUrl` | — | External base URL for the agent card and push webhooks, e.g. `https://bot.example.com`. Required for push to work; see [docker.md](docker.md) |
| `A2A__*` | see [a2a.md](a2a.md#configuration) | `MaxHops`, `SessionIdleTimeout`, `MaxFileBytes`, `AllowPrivatePushUrls`, `Card:Name`, `Card:Description` |
Sessions are stored as usual under `~/.volcanic/agent/sessions/telegram-<chatId>/`; after a restart each chat resumes
its most recent conversation. Each chat's time zone is kept in `<agent dir>/telegram/chat-settings.json`, so it
@@ -118,6 +121,7 @@ started a chat with the bot, Telegram refuses the message; the bot logs that and
| `/calendar_connect` | Connect this chat's Google Calendar (link and code; the bot confirms when you approve) |
| `/calendar_status` | The connected account and time zone, or the pending code |
| `/calendar_disconnect` | Revoke and delete this chat's calendar token, or cancel a pending connect |
| `/a2a` | This chat's A2A callers and recent tasks (see [A2A](#a2a)) |
Any other `/command` is passed to the session, so extension commands such as `/claude-code-doctor` and
`/claude-code-login` work. `/mcp` lists the MCP servers (see [mcp.md](mcp.md#mcp)).
@@ -284,6 +288,26 @@ chat's account, so nobody sees another chat's calendar through the agent. The bo
sending the link and code and confirms in the chat once access is approved. Setup of the Google Cloud app, token
storage and time zones are in [google-calendar.md](google-calendar.md).
### A2A
With `A2A__Enabled=true` the bot also serves [A2A](a2a.md): other agents can call it over HTTP once given a key.
It opens Kestrel on port 8080 (Coolify needs a domain pointed at that port — see [docker.md](docker.md)) and logs
`A2A: listening on ...`. With it left off (the default) no port is opened at all. If the port is already taken,
the bot logs a critical line and exits with code 3 instead of coming up half-working.
Callers live only in allowed chats' workspaces: each chat's own `.volcanic/a2a/callers.json` and
`.volcanic/a2a/tasks/` (see [a2a.md](a2a.md#files)). A caller's session is isolated from the chat's own
conversation — same workspace, same `AGENTS.md`/`CLAUDE.md`, `PERSONA.md` and `MEMORY.md`, separate history — and
opens with that chat's current time zone and `Telegram:Model`; a later `/timezone` only reaches a caller's session
once it's been idle long enough to be evicted and reopened (`SessionIdleTimeout`, 30 minutes by default).
`notify_owner` delivers as `🤖 A2A · <caller>: <text>` in the owner's chat, through the same channel as everything
else the bot sends there — including during `/impersonate`, when it reaches the admin instead, like any other
output to that chat.
`/a2a` shows this chat's callers (name, mode, and the key's last four characters — only once a key is at least 12
characters long) and its ten most recent tasks with their caller, state and age.
## Security
The agent has the built-in tools, including `bash`, and runs them as the bot's OS user. Only allow-listed user ids