# Request

> The payload of the channel request: the tag byte 2 and the request of the channel.

Canonical: https://protocol.diverge.network/2.3.0/container-proxy-endpoints/agents-begin/client/enqueue/request/
Specification revision: 2.3.0

```text
[2][request JSON …]
```

- **`key`.** A string the client chooses: what a
  [dequeue](/2.3.0/container-proxy-endpoints/agents-begin/client/dequeue/) names to withdraw the message, and every
  other message still waiting under the same key. Two messages may
  share one. The server relays it verbatim and does not read it.
- **`content`.** An array of MCP content blocks, in order, each as
  the `ContentBlock` of the crate's MCP dependency states one: text,
  an image, audio, an embedded resource, or a resource link. What the
  container makes of a block is its own, and a container that cannot
  take one refuses the whole message, and the fate is the error, in
  the container's words. A message with no block is refused too.
- **Malformed.** A channel request whose payload does not decode as
  stated is answered by a channel response finish that no channel
  response precedes, and the server does nothing else for it.

The channel request is defined by `diverge-sdk-rs/src/container_proxy/outside/endpoints/agents/begin/client/channel_request/frame.rs`:

```rust
//! What a client's channel request frame carries for an agent
//! container begin.

use std::error::Error;
use std::fmt;

use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
use crate::shared::containers::secrets::Values;
use crate::shared::containers::{dequeue, enqueue, postgres};

/// What the server asks the proxy for once an agent container has
/// begun.
///
/// A payload leads with one byte saying which, and the rest is that
/// variant's own bytes.
///
/// | tag | asks for |
/// |-----|----------|
/// | `0` | [`Postgres`](Self::Postgres) |
/// | `1` | [`Info`](Self::Info) |
/// | `2` | [`Enqueue`](Self::Enqueue) |
/// | `3` | [`Dequeue`](Self::Dequeue) |
/// | `4` | [`Secrets`](Self::Secrets) |
///
/// The first two are the same in both begin scopes, so a reader of
/// one is a reader of both; what follows is this family's own
/// exchange — no daemon connection among them, since an agent
/// container holds no account and asks the daemon nothing — and
/// then the feed, which both scopes have again under a tag of their
/// own. All of them reach INTO the container: the last hop of what a
/// caller opened on the provider, or of what the caller fed. What
/// comes OUT of the agent — its conversation — is no channel: it
/// rides the begin's own main stream.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame {
    /// The server's half of a database connection. Tag `0`.
    ///
    /// Opened once the server has taken the proxy's half, quoting the
    /// same connection; what comes back is everything the container's
    /// driver wrote. See
    /// [`postgres`](crate::shared::containers::postgres) for the pair.
    Postgres(postgres::request::Postgres),
    /// What the arguments may be. Tag `1`.
    ///
    /// Carries nothing — the variant is bare — and the proxy answers
    /// with the image's DIVERGE.md, parsed — read from the
    /// container's root, the program never asked — or that it has
    /// none. See [`info`](crate::shared::containers::info).
    Info,
    /// A message for the agent. Tag `2`.
    ///
    /// The one way into it: a message with no loop running starts one,
    /// on that message, and a message while one runs joins its queue.
    /// Answered once — by an
    /// [`enqueue::response::Frame`](crate::shared::containers::enqueue::response::Frame)
    /// naming the message's fate, whenever that is known — and then the
    /// finish. What the agent says in reply is the begin's main stream.
    /// See [`enqueue`](crate::shared::containers::enqueue).
    Enqueue(enqueue::request::Request),
    /// Withdraw every message still waiting under a key. Tag `3`.
    ///
    /// Carries the key, as the enqueues gave it. Answered once — by a
    /// [`dequeue::response::Frame`](crate::shared::containers::dequeue::response::Frame)
    /// saying whether anything waited under it — and then the finish.
    /// Each message it withdraws is ALSO answered, on its own enqueue
    /// channel. See [`dequeue`](crate::shared::containers::dequeue).
    Dequeue(dequeue::request::Request),
    /// The secrets' values, fed to the program. Tag `4`.
    ///
    /// Carries the [`Values`] as JSON — every name the program
    /// declared, each to its value — which the proxy hands the
    /// program on `POST /secrets` and holds until that call answers.
    /// Opened by the server once before it sends the run's id and
    /// again at every activation, relaying the caller's own feed.
    /// Answered once — by a
    /// [`feed::response::Frame`](crate::shared::containers::secrets::feed::response::Frame),
    /// `Fed` or the program's refusal — and then the finish; a `Fed`
    /// opens the gate the proxy holds every delivery behind. See
    /// [`secrets`](crate::shared::containers::secrets).
    Secrets(Values),
}

/// Tag for [`Frame::Postgres`].
const POSTGRES: u8 = 0;

/// Tag for [`Frame::Info`].
const INFO: u8 = 1;

/// Tag for [`Frame::Enqueue`].
const ENQUEUE: u8 = 2;

/// Tag for [`Frame::Dequeue`].
const DEQUEUE: u8 = 3;

/// Tag for [`Frame::Secrets`].
const SECRETS: u8 = 4;

impl Encode for Frame {
    /// The ordinary JSON failure, from whichever ask has one.
    type Error = serde_json::Error;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
        match self {
            Frame::Postgres(request) => {
                out.extend_from_slice(&[POSTGRES]);
                // Its error is `Infallible`, and an empty match on one
                // is how you say so: there is no value to handle.
                request.encode(out).map_err(|error| match error {})
            }
            Frame::Info => {
                out.extend_from_slice(&[INFO]);
                Ok(())
            }
            Frame::Enqueue(request) => {
                out.extend_from_slice(&[ENQUEUE]);
                request.encode(out)
            }
            Frame::Dequeue(request) => {
                out.extend_from_slice(&[DEQUEUE]);
                request.encode(out)
            }
            Frame::Secrets(values) => {
                out.extend_from_slice(&[SECRETS]);
                serde_json::to_writer(out, values)
            }
        }
    }
}

impl Decode<'_> for Frame {
    /// Several ways to fail, and the parses among them name which.
    type Error = FrameError;

    fn decode(bytes: &[u8]) -> Result<Self, Self::Error> {
        let (tag, rest) = bytes.split_first().ok_or(FrameError::Empty)?;
        match *tag {
            POSTGRES => postgres::request::Postgres::decode(rest)
                .map(Frame::Postgres)
                .map_err(FrameError::Postgres),
            INFO => Ok(Frame::Info),
            ENQUEUE => enqueue::request::Request::decode(rest)
                .map(Frame::Enqueue)
                .map_err(FrameError::Enqueue),
            DEQUEUE => dequeue::request::Request::decode(rest)
                .map(Frame::Dequeue)
                .map_err(FrameError::Dequeue),
            SECRETS => serde_json::from_slice(rest)
                .map(Frame::Secrets)
                .map_err(FrameError::Secrets),
            tag => Err(FrameError::UnknownTag(tag)),
        }
    }
}

/// An agents begin channel request that could not be read.
#[derive(Debug)]
pub enum FrameError {
    /// No bytes at all, so not even a tag.
    Empty,
    /// A tag that is none of this frame's.
    UnknownTag(u8),
    /// The connection id was not four bytes.
    Postgres(postgres::request::PostgresError),
    /// The enqueued message did not parse as JSON.
    Enqueue(serde_json::Error),
    /// The dequeue's key did not parse as JSON.
    Dequeue(serde_json::Error),
    /// The secrets' values did not parse as JSON. The message is the
    /// parser's, which quotes no value.
    Secrets(serde_json::Error),
}

impl fmt::Display for FrameError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FrameError::Empty => {
                f.write_str("agents begin channel request frame is empty")
            }
            FrameError::UnknownTag(tag) => {
                write!(f, "unknown agents begin channel request tag {tag}")
            }
            FrameError::Postgres(error) => write!(f, "{error}"),
            FrameError::Enqueue(error) => {
                write!(f, "enqueue request did not parse: {error}")
            }
            FrameError::Dequeue(error) => {
                write!(f, "dequeue request did not parse: {error}")
            }
            FrameError::Secrets(error) => {
                write!(f, "secrets feed did not parse: {error}")
            }
        }
    }
}

impl Error for FrameError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            FrameError::Enqueue(error) | FrameError::Dequeue(error) | FrameError::Secrets(error) => Some(error),
            FrameError::Postgres(error) => Some(error),
            FrameError::Empty | FrameError::UnknownTag(_) => None,
        }
    }
}
```

and by `diverge-sdk-rs/src/shared/containers/enqueue/request/request.rs`:

```rust
//! A message for the running loop's queue.

use rmcp::model::ContentBlock;
use serde::{Deserialize, Serialize};
use serde_json::Error;

use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};

/// Send a message to the agent.
///
/// With no loop running, the message starts one, and is its input.
/// With one running, the verb is the mechanism's: the message is QUEUED, not injected —
/// the turn in flight always runs to completion, and the agent picks
/// the message up at a seam of its own choosing: folded in beside the
/// next tool results, or opening the next turn when the assistant has
/// already finished. Nothing about this interrupts anything, ever.
///
/// # The content is MCP's
///
/// A list of content blocks — text, an image, audio, an embedded
/// resource, a link to one — in order, each as MCP's own
/// [`ContentBlock`] states it: one vocabulary for what a caller says
/// to an agent, what a tool answers, and what the agent says back.
/// What an agent's image makes of a block is its own. An image that
/// cannot take one refuses the whole message, in its own words, and
/// the fate carries them; nothing is reduced to its text on the
/// caller's behalf. A message with no block is refused too. The
/// [user parts](crate::provider::endpoints::containers::agents::run::server::response::user_parts)
/// that mark this message's delivery carry the same blocks back, one
/// chunk each, verbatim, under this key, at the position it landed.
///
/// # The answer says what became of it
///
/// One frame, then the finish: `delivered` when the agent has taken
/// the message into the conversation, `dequeued` when the caller
/// withdrew it first by a dequeue of its key, and an error when the agent refused the
/// message or no run could start on it. A run ending with the
/// message still waiting does not lose it: the message starts the
/// next run. The two fates carry nothing — the fate is the answer.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Request {
    /// The caller's handle on the message: what a
    /// [`dequeue`](crate::shared::containers::dequeue) names to
    /// withdraw it, and every other message still waiting under the
    /// same key. Not unique — two messages may share one — and not
    /// read: the provider and the proxy compare it, and nothing else.
    pub key: String,
    /// The message's content, in order.
    pub content: Vec<ContentBlock>,
}

/// Its JSON, and nothing in front of it. The tag that says which
/// request this is belongs to whichever frame carries it.
impl Encode for Request {
    /// The ordinary JSON failure.
    type Error = Error;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Error> {
        serde_json::to_writer(out, self)
    }
}

impl Decode<'_> for Request {
    /// The ordinary JSON failure. There is nothing else here to get
    /// wrong — no tag to be unknown, and no empty case, since no bytes
    /// at all is a JSON document that ended too early and is reported
    /// as one.
    type Error = Error;

    fn decode(bytes: &[u8]) -> Result<Self, Error> {
        serde_json::from_slice(bytes)
    }
}
```
