Pure mapping from Pixir bus Events to ACP session/update notification params, and
from a terminal Conversation.await/2 outcome to an ACP PromptResponse stopReason
(ADR 0009, §§4-5). No IO, no process state.
This is presentation only — the canonical Log is never altered. Streamed
text_deltas become agent_message_chunks; the canonical assistant_message (the
same text) is intentionally dropped here to avoid duplication, and is re-emitted only
by the no-deltas fallback path in Pixir.ACP.Server.
Summary
Functions
ACP tool kind for a Pixir tool registry name (cosmetic — drives only an icon).
Build a one-off agent_message_chunk session/update for a piece of assistant text.
Used by the Server's fallback when a Turn streamed no text_delta (ADR 0009 §4).
Map a RequestPermissionResponse (the {:ok, result} / {:error, _} from
Server.request_permission/2) to the Executor's decision: :allow | {:deny, reason}. A selected outcome whose optionId is an allow option → :allow;
a reject option, a cancelled outcome, or any error/malformed response →
{:deny, reason} (default-deny: anything we can't read as an explicit allow
is a denial).
Build the session/request_permission PARAMS (A.2) from an asker request
%{tool, args, reason, call_id}. Offers exactly two options per decision #7:
allow_once / reject_once (ADR 0006 defers persistent allow-lists). Reuses
title/2/kind/1 so the toolCall reads like the live tool_call update.
Map a canonical History Event to a session/update for session/load REPLAY
(epic A.6) — a fuller mapping than update/2, which intentionally drops
user/assistant (live streaming re-emits deltas, not the canonical message).
On load there are no deltas, so the canonical messages ARE the transcript:
user_message→user_message_chunk, assistant_message→agent_message_chunk,
tool_call/tool_result as in live. Reasoning is omitted (opaque encrypted
rs_ items carry no displayable summary — ADR 0007; the summary text is
ephemeral and never logged). Returns nil for events with no transcript form.
Map a terminal await outcome to an ACP stopReason. cancel_requested? covers the
cancel-vs-terminal race: if session/cancel arrived, resolve "cancelled" even if a
done/error slipped in first. A turn-level error is reported as content, not a
protocol error, so it resolves "end_turn" (ADR 0009 §5).
Translate a Pixir Event into the full session/update params
(%{"sessionId" => acp_sid, "update" => update}), or nil when the event maps to
nothing on the wire.
Types
@type acp_sid() :: String.t()
@type await_outcome() :: :done | :error | :interrupted | :timeout
Functions
ACP tool kind for a Pixir tool registry name (cosmetic — drives only an icon).
Build a one-off agent_message_chunk session/update for a piece of assistant text.
Used by the Server's fallback when a Turn streamed no text_delta (ADR 0009 §4).
Map a RequestPermissionResponse (the {:ok, result} / {:error, _} from
Server.request_permission/2) to the Executor's decision: :allow | {:deny, reason}. A selected outcome whose optionId is an allow option → :allow;
a reject option, a cancelled outcome, or any error/malformed response →
{:deny, reason} (default-deny: anything we can't read as an explicit allow
is a denial).
Build the session/request_permission PARAMS (A.2) from an asker request
%{tool, args, reason, call_id}. Offers exactly two options per decision #7:
allow_once / reject_once (ADR 0006 defers persistent allow-lists). Reuses
title/2/kind/1 so the toolCall reads like the live tool_call update.
@spec replay(Pixir.Event.t(), acp_sid(), keyword()) :: map() | nil
Map a canonical History Event to a session/update for session/load REPLAY
(epic A.6) — a fuller mapping than update/2, which intentionally drops
user/assistant (live streaming re-emits deltas, not the canonical message).
On load there are no deltas, so the canonical messages ARE the transcript:
user_message→user_message_chunk, assistant_message→agent_message_chunk,
tool_call/tool_result as in live. Reasoning is omitted (opaque encrypted
rs_ items carry no displayable summary — ADR 0007; the summary text is
ephemeral and never logged). Returns nil for events with no transcript form.
@spec stop_reason(await_outcome(), boolean()) :: String.t()
Map a terminal await outcome to an ACP stopReason. cancel_requested? covers the
cancel-vs-terminal race: if session/cancel arrived, resolve "cancelled" even if a
done/error slipped in first. A turn-level error is reported as content, not a
protocol error, so it resolves "end_turn" (ADR 0009 §5).
@spec update(Pixir.Event.t(), acp_sid(), keyword()) :: map() | nil
Translate a Pixir Event into the full session/update params
(%{"sessionId" => acp_sid, "update" => update}), or nil when the event maps to
nothing on the wire.