> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aion.to/llms.txt
> Use this file to discover all available pages before exploring further.

# Message Mapping

> How Aion maps inbound messages into Google ADK and maps Google ADK output back into A2A.

This page explains message mapping at a higher level than
[ADK Message Mapping](/sdk/python/frameworks/adk/message-mapping). The goal is to show what an ADK
agent receives, how Aion interprets the response on the way back out, and how messaging
distributions keep the reply anchored to the original context.

## Overview

The default Google ADK message-mapping flow is:

1. A distribution or client sends an A2A request into Aion Server.
2. Aion exposes the request through `ctx.aion_runtime_context.inbox`.
3. Your agent yields events that may populate the SDK response buffer or
   provide an explicit A2A outbox.
4. Aion resolves those events into a final A2A `Message` or `Task`.
5. The caller or distribution delivers the response into the original context.

```mermaid theme={null}
sequenceDiagram
participant Caller as Distribution or Client
participant Server as Aion Server
participant ADK as Google ADK Agent
participant Target as Original Context

Caller->>Server: SendMessage or SendStreamingMessage
Server->>ADK: Populate ctx.aion_runtime_context
ADK-->>Server: Yield buffered content, final events, or a2a_outbox
Server-->>Caller: Final A2A Message or Task
Caller-->>Target: Deliver reply in same DM, thread, or conversation
```

## What Your Agent Receives

The main integration surface is `ctx.aion_runtime_context.inbox`:

```python theme={null}
from aion.adk.authoring.invocation import AionInvocationContext


async def _run_async_impl(self, ctx: AionInvocationContext):
    inbox = ctx.aion_runtime_context.inbox
    task = inbox.task
    message = inbox.message
```

This keeps the framework-facing API simple while still exposing the full transport envelope when a
distribution needs to preserve channel, thread, or provider-specific context.

## Default Response Precedence

Google ADK should follow the same general precedence as the other framework
adapters:

1. SDK-managed response buffer
   This is authoritative when populated. It includes helper-emitted reply
   content, final non-partial message content, and partial stream output that
   is intended to become the durable reply for the current turn.
2. `a2a_outbox`
   Use this when you want full control over the outbound A2A payload.
3. Framework-native fallback
   If neither the SDK buffer nor `a2a_outbox` is used, Aion falls back to the
   current turn's partial stream accumulation first and then to the final
   non-partial agent-authored event content.

### Explicit A2A Outbox

```python theme={null}
from a2a.types import Message, Part, Role
from aion.shared.types import A2AOutbox
from google.adk.events import Event, EventActions

from aion.adk.authoring.invocation import AionInvocationContext


async def _run_async_impl(self, ctx: AionInvocationContext):
    yield Event(
        author=self.name,
        actions=EventActions(
            state_delta={
                "a2a_outbox": A2AOutbox(
                    message=Message(
                        role=Role.ROLE_AGENT,
                        parts=[Part(text="Done!")],
                    )
                )
            }
        ),
    )
```

### Framework-Native Fallback

```python theme={null}
from google.adk.events import Event

from aion.adk.authoring.invocation import AionInvocationContext


async def _run_async_impl(self, ctx: AionInvocationContext):
    yield Event(
        author=self.name,
        content={"parts": [{"text": "Here is the answer."}]},
    )
```

## What Happens in a Distribution Flow

For messaging distributions, the default routing target should be preserved automatically:

* DM in, DM out
* reply in, reply out
* shared conversation in, same shared conversation out

That routing information lives in the inbound distribution payload and event parts. Your agent does
not need to re-specify it unless it wants to override the default behavior.

## Example: Slack Mention

```python theme={null}
from google.adk.events import Event

from aion.adk.authoring.invocation import Thread, AionInvocationContext


async def _run_async_impl(self, ctx: AionInvocationContext):
    thread = Thread.from_context(ctx.aion_runtime_context)
    text = thread.message.text if thread.message else ""
    yield Event(
        author=self.name,
        content={"parts": [{"text": f"Slack mention received: {text}"}]},
    )
```

Default behavior:

1. Slack distribution receives a configured mention event.
2. Aion exposes that request through `ctx.aion_runtime_context`.
3. The agent emits a normal text response.
4. Slack distribution replies in the same conversation or thread.

## Example: Telegram DM

```python theme={null}
from google.adk.events import Event

from aion.adk.authoring.invocation import Thread, AionInvocationContext


async def _run_async_impl(self, ctx: AionInvocationContext):
    thread = Thread.from_context(ctx.aion_runtime_context)
    text = thread.message.text if thread.message else ""
    yield Event(
        author=self.name,
        content={"parts": [{"text": f"Telegram DM received: {text}"}]},
    )
```

Because the inbound request carries the Telegram chat context, the distribution can return the
final reply to that same DM automatically.

## When to Reach for `a2a_outbox`

Use `a2a_outbox` when:

* you need structured parts rather than plain text
* you want to emit a provider-neutral card payload
* you need to override the default outbound target
* you want to return a task patch instead of a single message

If you only want a normal text reply, a straightforward final ADK event should be enough.

## Next Step

For the lower-level adapter rules, see
[ADK Message Mapping](/sdk/python/frameworks/adk/message-mapping).
