Transport Events
Transport events are the wire-level protocol between a ChatTransport and the SDK session loop. Every transport (mock, proxy, Session API, WebSocket) emits the same TransportEvent discriminated union. The normalizer in packages/gecx-chat/src/messages/normalize.ts converts these events into stable ChatMessagePart values for UI rendering.
TransportEventBase
All events extend this base:
| Field | Type | Required | Description |
|---|---|---|---|
requestId | string | Yes | Correlates events to the originating SendRequest. |
correlationId | string | No | Optional end-to-end correlation ID for distributed tracing. |
timestamp | string | Yes | ISO 8601 timestamp. |
sequence | number | No | Monotonic per-response sequence number for resume-mode deduplication. |
Event Types
| Event Type | Interface | Additional Fields | Description |
|---|---|---|---|
response.started | ResponseStartedEvent | responseId | Marks the beginning of an agent response. |
text.delta | TextDeltaEvent | delta, responseId | Streaming text fragment. |
text.completed | TextCompletedEvent | text, responseId | Final assembled text for the response. |
rich.payload | RichPayloadEvent | payloadType, payloadVersion?, data, responseId | Rich content payload (product carousel, order summary, A2UI frame, etc.). |
tool.call | ToolCallEvent | toolCallId, toolName, input, responseId | Agent requests a client-side tool invocation. |
tool.result | ToolResultEvent | toolCallId, output, error?, status?, idempotencyKey?, duplicateDisposition?, approvalPolicy?, responseId | Result of a tool execution sent back to the agent. |
citation | CitationEvent | title, url, snippet?, responseId | Source citation. |
suggestion.chips | SuggestionChipsEvent | chips[], responseId | Suggestion chips for the user. |
handoff.status | HandoffStatusEvent | status, targetAgent?, reason?, queuePosition?, estimatedWaitTime?, routeDecision?, graphPath?, responseId | Live-agent handoff status update. routeDecision and graphPath populated for bot_to_bot (agent-graph) handoffs. |
signal.update | SignalUpdateEvent | signal (SentimentSignalPart | IntentSignalPart), responseId? | Synthesized by SignalRunner when an adapter emits a sentiment or intent classification. Flows through processTransportEvent exactly like server-emitted events. |
diagnostic | DiagnosticEvent | category, message, details?, responseId | Diagnostic event from the transport or backend. |
error | ErrorEvent | code, message, retryable?, responseId? | Transport or server error. |
response.completed | ResponseCompletedEvent | responseId | Marks the end of an agent response. |
session.ended | SessionEndedEvent | reason, responseId? | Session was terminated by the server. |
Typical Event Flow
A standard text response:
response.started --> text.delta (x N) --> text.completed --> response.completed
A response with a tool call:
response.started --> text.delta --> tool.call --> [SDK executes tool]
--> tool.result --> text.delta --> response.completed
Server-side business actions use the same tool.call and tool.result wire
events. UI-visible lifecycle updates such as awaiting_approval,
pending_supervisor, async_pending, executing, duplicate, completed,
failed, and timed_out are emitted by the SDK's tool registry while the
transport turn is waiting for the tool result. A tool.result.status can carry a
final status when a custom transport already knows it.
A response with rich content:
response.started --> text.delta --> rich.payload (product-carousel)
--> suggestion.chips --> response.completed
A response with a handoff:
response.started --> handoff.status (requested) --> handoff.status (queued)
--> handoff.status (connected) --> response.completed
ChatTransport Interface
Transports implement this interface:
| Method | Signature | Description |
|---|---|---|
connect | (sessionId: string) => Promise<void> | Establish connection for a session. |
send | (request: SendRequest) => Promise<void> | Fire-and-forget send (request-response transports). |
stream | (request: SendRequest, signal?: AbortSignal) => AsyncIterable<TransportEvent> | Stream events for a send request. |
upload | (file: UploadBody, sessionId: string, signal?: AbortSignal) => AsyncIterable<UploadProgressEvent> | Upload a file (optional). |
negotiate | () => Promise<Partial<SessionCapabilities>> | Server capability negotiation (optional). |
reconnect | (reason: string) => Promise<void> | Re-establish connection after disconnect (optional). |
resumeStream | (cursor: TurnCursor, signal?: AbortSignal) => AsyncIterable<TransportEvent> | Resume a partially-streamed turn (optional). |
close | () => Promise<void> | Tear down the transport. |
TransportCapabilities
Static capability declaration on a transport:
| Field | Type | Default | Description |
|---|---|---|---|
class | TransportClass | 'server-stream' | 'request-response', 'server-stream', or 'bidi'. |
reconnect | boolean | false | Whether the transport implements reconnect(). |
resume | boolean | false | Whether the transport implements resumeStream(). |
multiplex | boolean | false | Whether the transport multiplexes sessions (reserved). |
protocolVersion | string | '1' | Wire-protocol version identifier. |
attachments | AttachmentAdapterCapabilities? | unsupported | Declares the file upload contract: inline-blob, proxy-upload, presigned-url, or unsupported. |
Attachment Upload Contracts
The SDK recognizes one attachment contract per transport class:
| Contract | Used By | Behavior |
|---|---|---|
inline-blob | Mock and future validated live session APIs | File bytes travel inline with the message/session request. The current mock transport simulates this path deterministically. |
proxy-upload | createProxyTransport and customer proxies | Browser posts multipart data to the customer proxy. The proxy validates limits, applies audit/redaction policy, and forwards to the upstream upload backend. |
presigned-url | Custom customer transports | The backend returns a one-time URL and the adapter uploads directly to storage. This SDK version defines the type contract but does not ship a storage backend. |
unsupported | Session API, HTTP, WebSocket unless customized | chat.attachFile(file) fails with UPLOAD_FAILED and remediation instead of fake-uploading bytes. |
UploadProgressEvent may include attachmentId, status, lifecycle, scanStatus, metadata, errorCode, and remediation. Transport implementations must never emit file bytes, base64 payloads, or extracted file text in upload events.
ChatSession also emits an attachment_lifecycle session event for each UploadProgress object produced by chat.attachFile(file) or chat.retryAttachment(attachmentId). React apps can either consume the async iterable directly or subscribe to this event when they need centralized upload instrumentation.
docs/reference/transport-events.md