diff --git a/api/group-management.mdx b/api/group-management.mdx index 0bfaa96..ee39d09 100644 --- a/api/group-management.mdx +++ b/api/group-management.mdx @@ -146,7 +146,7 @@ Each session has two databases in `data/v2-sessions/{agent_group_id}/{session_id | Table | Purpose | |-------|---------| -| `messages_in` | Inbound messages, tasks, system notifications | +| `messages_in` | Inbound messages, tasks, system notifications. A2A inbound rows carry `source_session_id` for reply return-path routing | | `delivered` | Delivery tracking | | `destinations` | Live destination map | | `session_routing` | Default reply routing | @@ -155,7 +155,7 @@ Each session has two databases in `data/v2-sessions/{agent_group_id}/{session_id | Table | Purpose | |-------|---------| -| `messages_out` | Outbound messages | +| `messages_out` | Outbound messages, including `in_reply_to` stamped by the agent's MCP tools for A2A reply correlation | | `processing_ack` | Processing acknowledgments | | `session_state` | Persistent key/value store | | `container_state` | Tool-in-flight tracking | diff --git a/api/message-routing.mdx b/api/message-routing.mdx index 4578590..8934a13 100644 --- a/api/message-routing.mdx +++ b/api/message-routing.mdx @@ -86,11 +86,21 @@ For each session with due outbound messages: 2. Filter already-delivered via `inbound.db`'s `delivered` table 3. Route by `kind`: - `system` — dispatch to registered delivery action handlers - - `channel_type='agent'` — agent-to-agent module + - `channel_type='agent'` — agent-to-agent module (see [return-path routing](#agent-to-agent-return-path-routing)) - Normal — permission check, then channel adapter delivery 4. Mark delivered in `inbound.db` 5. Clean up `outbox/` files (best-effort) +## Agent-to-agent return-path routing + +When an agent emits a reply to another agent group, the target may have multiple active sessions. The router resolves the destination session in three layers, highest fidelity first: + +1. **Direct return-path** — if the outbound carries `in_reply_to`, the router opens the source session's `inbound.db`, reads the triggering row's `source_session_id`, and routes to that session. +2. **Peer-affinity fallback** — when `in_reply_to` is absent or doesn't resolve, the router uses the most recent A2A inbound from the same peer agent group whose `source_session_id` is non-null. +3. **Legacy fallback** — newest active session for the target agent group (pre-migration compatibility). + +The `source_session_id` column on `messages_in` is stamped when an outbound A2A message is converted to the target's inbound row. The container's MCP `send_message` and `send_file` tools thread the current batch's `in_reply_to` onto every outbound row so the host can correlate replies back to the originating session. + ### Delivery actions Modules register handlers via `registerDeliveryAction(action, handler)`: diff --git a/changelog/docs-updates.mdx b/changelog/docs-updates.mdx index 3da553f..6edf58f 100644 --- a/changelog/docs-updates.mdx +++ b/changelog/docs-updates.mdx @@ -5,6 +5,15 @@ icon: "file-pen" rss: true --- + + Documented the agent-to-agent reply routing fix from [qwibitai/nanoclaw#2267](https://github.com/qwibitai/nanoclaw/pull/2267). When a target agent group has multiple active sessions, A2A replies now route back to the session that asked the question via the new `source_session_id` column on `messages_in` and `in_reply_to` threading on `messages_out`. + + ## Updated + - **`api/message-routing.mdx`**: new "Agent-to-agent return-path routing" section covering the three-layer resolution (direct return-path, peer-affinity fallback, legacy newest-active-session fallback) + - **`concepts/architecture.mdx`**: `messages_in` schema note for `source_session_id`; module summary updated to describe origin-aware A2A routing + - **`api/group-management.mdx`**: session-database table notes for the new `source_session_id` and `in_reply_to` fields + + Phase A of the v2 documentation sprint — bringing the pages every new user lands on into alignment with the v2 rewrite. All claims verified directly against upstream source (`src/db/schema.ts`, `src/types.ts`, `src/config.ts`, `container/Dockerfile`, `src/delivery.ts`) rather than upstream `docs/` (which includes a stale `architecture.md` draft and a `db-session.md` that omits the `container_state` table). diff --git a/concepts/architecture.mdx b/concepts/architecture.mdx index bc7f3f8..c11f846 100644 --- a/concepts/architecture.mdx +++ b/concepts/architecture.mdx @@ -117,7 +117,7 @@ The host sweep (`src/host-sweep.ts`) runs every 60 seconds: **Session inbound.db** (host writes, container reads): -- **messages_in** — inbound messages with status, `process_after`, recurrence, `series_id`, and trigger flag +- **messages_in** — inbound messages with status, `process_after`, recurrence, `series_id`, trigger flag, and (for A2A inbound) `source_session_id` for return-path routing - **delivered** — tracks delivery outcomes - **destinations** — live destination map (channels and other agents) - **session_routing** — default reply routing @@ -135,7 +135,7 @@ Modules self-register via barrel imports and provide optional hooks into the rou - **Permissions** — sender resolution, access gating, channel approval, sender approval - **Scheduling** — task creation, pause/resume/cancel/update via delivery actions -- **Agent-to-agent** — cross-agent message routing +- **Agent-to-agent** — cross-agent message routing with origin-aware return paths so replies land in the session that asked the question, not just the target's newest active session - **Approvals** — interactive question cards - **Self-mod** — agent self-modification requests - **Typing** — typing indicator management