---
title: "AEP-0008: Text Answers and Response-Channel Declarations"
sidebarTitle: "AEP-0008: Text answers"
description: "Two small receiver/consumer rules that close one live-verified usability hole in the attention control loop, without changing any schema:"
---

{/* GENERATED by docs/render-site.js from spec/AEP-0008-text-answers-and-response-channels.md; do not edit. Edit the source and re-run `node docs/render-site.js` */}

<Note>

**Draft**: the AEP suite is pre-`v0.1`; each document's status lives in its header table. This page is rendered from the canonical [`spec/AEP-0008-text-answers-and-response-channels.md`](https://github.com/agenteventprotocol/agent-event-protocol/blob/main/spec/AEP-0008-text-answers-and-response-channels.md), which is the normative text.

</Note>

| Field | Value |
|---|---|
| **AEP** | 0008 |
| **Title** | Text Answers and Response-Channel Declarations |
| **Type** | Standards Track — Core |
| **Status** | Accepted (2026-07-16: normative text, fixtures, and prototype landed in one change per GOVERNANCE.md §2) |
| **Sponsor** | AEP maintainers (per GOVERNANCE.md §2) |
| **Created** | 2026-07-16 |
| **Requires** | AEP-0002 (attention payloads, lifecycle), AEP-0004 (control commands, ack semantics) |
| **Supersedes / Superseded-by** | n/a (additive to AEP-0002 §5.3) |

## Abstract

Two small receiver/consumer rules that close one live-verified usability
hole in the attention control loop, without changing any schema:

1. **Text answers against `options`**, a receiver-side mapping rule: a
   `control.attention.respond` whose `answer` carries only `text` against an
   option-bearing request SHOULD be honored when the text names an option,
   and MUST NOT be applied as the permissive choice of a
   `kind: "permission"` request when it does not.
2. **`respond_via` as the authoritative channel declaration**, a
   consumer-side rule: when a request declares how its answer travels,
   consumers SHOULD offer control delivery only if `control` is listed; a
   present value without `control` marks the request informational on the
   bus (its answer arrives by the stated channel, e.g. `oob`).

Both rules are stated once, in AEP-0002 §5.3 (amended in place), so every
adapter and every consumer inherit the same behavior. No field, kind, enum
value, or lifecycle state is added or changed; the change is entirely in
what receivers and consumers do with members that already exist.

## Motivation

Live operation surfaced a hole neither side of the control loop owned:

- Consumers (correctly) render a free-text reply affordance beside option
  buttons on attention cards: free text is load-bearing for deny reasons
  and steer-style guidance, and AEP-0002 §5.3 already gives `answer.text`
  first-class carriage.
- Reference emitters with held permission gates (two of them, on different
  vendor surfaces) validated answers strictly against `answer.option`, so
  an operator who **typed the word `deny` into the reply box was nacked
  `invalid`** by the very gate whose Deny button sat beside the box. Every
  posture of the loop refused honest human input that named the decision.
- The same live sessions showed the inverse hole on *observed* (read-only)
  approval cards: the emitter knew the request could never be answered from
  the bus (the decision belongs to the agent's own UI), but the event did
  not say so, and consumers rendered answer affordances that could only
  ever earn a nack. `respond_via` existed precisely to carry this fact and
  was not being declared or consulted.

One vendor's own terminal prompt already sets the precedent for the safe
direction: it maps recognized option words to their choice and **anything
else to the refusing choice**, never to permission.

## Specification

The normative text lives in AEP-0002 §5.3, amended in place (the same
mechanism AEP-0006 used). The two amendments, reproduced here:

### 1. Text answers against `options` (attention.answered)

> *Text answers against `options` (AEP-0008).* When a
> `control.attention.respond` carries `text` and no `option` answering a
> request that offered `options`, the receiving emitter SHOULD treat text
> that, after trimming, case-insensitively equals an option's `id`, or
> exactly one option's `label`, as that option, recording the mapped choice
> as `answer.option` on `attention.answered` (the matched word is the
> decision itself, not accompanying text).
>
> For `kind: "permission"` requests, unmatched text MUST NOT be applied as the
> permissive choice: the emitter SHOULD either apply its refusing option with
> the text preserved as the stated reason (under `answer.text`'s capture rule)
> or reject the command (`control.rejected{reason:"invalid"}`).
>
> The recorded `answer` always states what was actually applied. Consumers
> MUST NOT depend on text mapping: `option` remains the lossless channel.

### 2. `respond_via` consumer guidance (attention.requested)

> `respond_via?: array(enum(control|oob)) [metadata]`: how a response can be
> delivered; when present it is the emitter's authoritative declaration for THIS
> request: consumers SHOULD offer control delivery only if `control` is listed,
> and a present value without `control` marks the request informational on the
> bus, with its answer arriving by the stated channel, e.g. `oob` for a decision
> made in the agent's own UI; see AEP-0008.

An absent `respond_via` means what it always has: nothing is declared, and
send-and-settle (the ack/nack loop of AEP-0004 §3) remains the honest way
to learn whether a target accepts an answer.

## Rationale

**Why receiver-side, not consumer-side.** A consumer could rewrite
`{text: "deny"}` into `{option: "deny"}` before sending, but option-id
meaning belongs to the adapter that minted the options (only it knows that
`deny` blocks the call while `session` grants a scoped allowlist entry),
and N consumers would each re-implement the mapping, divergently. Stated
once at the receiver, every consumer, present and future, inherits it.

**Why the refusing choice for unmatched text, specifically.** Free text on
a permission card is nearly always a reason or a redirection ("use the
staging registry instead"): human intent that should land as a deny with
the words preserved, not be bounced. Mapping it to the *permissive* choice
is the one indefensible reading: ambiguous input must never grant.

This mirrors the surveyed vendors' own typed-input semantics (unrecognized
input refuses) and one vendor family's documented "tell the agent what to
do differently" path, where free text both denies the call and steers the
retry.

Emitters that prefer to reject unmatched text may still do so (`invalid`
remains legal), but the permissive mapping is forbidden.

**Why `respond_via` needed no new vocabulary.** The member and both enum
values predate this AEP; the gap was behavioral (emitters not declaring,
consumers not consulting). Declaring `["oob"]` on an observed read-only
approval card makes the card self-describing: a vendor-ignorant consumer
can render the decision menu as information (what the agent is asking its
own operator) without inviting an answer the emitter must nack. This is
deliberately *request-scoped* truth: it rides the event itself and cannot
go stale the way observed capability state can.

**Vendor-neutrality criteria (how this change was screened).** Control
affordances originate on vendor surfaces, and the standing risk is
per-vendor semantics leaking into core. Each addition here was screened
against the criteria the project applies to any vendor-facing control
feature, recorded so future adapter work inherits the test:

1. *Schema neutrality*: vendor specifics ride generic shapes as instance
   data; no vendor-named field or enum value enters core schemas.
2. *Consumer-blind renderability*: a consumer that never heard of the
   vendor renders the affordance correctly and completely from the event
   alone; correctness never requires vendor-conditional consumer logic.
3. *Answer round-trip fidelity*: `{option}`/`{text}`/`{values}` carries
   the decision losslessly; the adapter owns what an option id means on
   its vendor surface.
4. *Cross-vendor recurrence*: the capability class recurs across at
   least two surfaced agents before core semantics are minted;
   single-vendor concepts stay in `x.*`.
5. *No degradation by absence*: absence is the default posture; presence
   is purely additive for everyone else.
6. *Sovereignty and honesty*: nothing claims unobserved state or
   overrides the agent's own decision surface; failure modes stay honest.

Both rules pass all six: they use only preexisting generic members (1),
bind receivers/consumers without vendor conditionals (2), record the
applied decision on the stream (3), generalize behavior already shipped
independently on two vendor gate surfaces, with a third vendor's own
terminal semantics agreeing (4), leave rejecting receivers and undeclared
requests exactly as they were (5), and fail toward refusal, never
permission (6).

**Alternatives rejected.** Consumer-side text→option rewriting (breaks 2
and 3 above); suppressing the free-text affordance on option-bearing cards
(free text is load-bearing for reasons/guidance; the fix is honoring it,
not removing it); relay-side rewriting of text answers (AEP-0004 §4.4
byte-identical forwarding is non-negotiable); mapping unmatched text to
the permissive choice (never); making the text-mapping rule a MUST
(receivers legitimately differ on label matching and locale; the safety
half, never permissive, is the only MUST-grade clause).

## Backward Compatibility

Fully additive. Emitters that keep rejecting text-only answers remain
conformant (`invalid` stays legal); the only new obligation is
conditional and vacuous for them (MUST NOT apply unmatched text as the
permissive choice: a rejecting emitter never applies it at all). The
reference implementation's remaining held gates (the Codex-, VS Code-,
Qwen-, OpenCode-, and Kilo-surface loops) implement the SHOULD, and any
emitter that instead keeps rejecting stays conformant.

Consumers that ignore `respond_via` keep today's send-and-settle behavior
and learn from nacks; consumers that honor it render
strictly more truthfully. No schema changes, so no generated-type or
validator changes; existing captured streams remain valid byte-for-byte.

## Prototype and fixtures (GOVERNANCE.md §2)

- **Prototype**: two of the reference implementation's held permission
  gates (the Hermes-surface and Claude-Code-surface adapters) implement the
  text-mapping rule: option words map (any case, trimmed), unmatched text
  applies the refusing option with the text as the stated reason, and the
  answered event records the applied option. The Hermes-surface observed
  approval card declares the vendor's four-way decision menu as `options` data
  with `respond_via: ["oob"]`.

  Both are exercised end-to-end (real shim, real relay) in the reference CI
  smokes; the reference consumer renders declared non-control requests as
  informational.
- **Fixtures**: the Hermes mapping fixture
  (`conformance/fixtures/mappings/hermes.json`) pins the menu-as-data +
  `respond_via: ["oob"]` shape of the observed approval card; both
  conformance runners check it. Held-gate answer semantics are runtime
  state and are smoke-pinned in the reference repository (the AEP-0004
  precedent for runtime control behavior).

## Security considerations

The text-mapping rule tightens the loop's safety posture rather than
loosening it: the only MUST-grade clause forbids ambiguous input from
granting permission, and the mapped-word path is strictly narrower than
what a consumer could already send as `{option}`.

`respond_via` declarations are emitter-asserted like every other payload
fact (AEP-0003 §8.2); a false `["oob"]` declaration can suppress a
consumer's answer affordance but never grants anything, and the send path
a consumer loses is one the emitter would nack anyway. Deployments
enforcing control ACLs (AEP-0004 §4.3) are unaffected: text answers ride
the same `control.attention.respond` command they already gate.

## References

- AEP-0002 §5.3/§7 (attention payloads and lifecycle).
- AEP-0004 §3/§5 (acks, `control.attention.respond`).
- AEP-0006 (the `values` answer member and its gating rule: the pattern this
  AEP's in-place amendment follows).
