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:

FieldTypeRequiredDescription
requestIdstringYesCorrelates events to the originating SendRequest.
correlationIdstringNoOptional end-to-end correlation ID for distributed tracing.
timestampstringYesISO 8601 timestamp.
sequencenumberNoMonotonic per-response sequence number for resume-mode deduplication.

Event Types

Event TypeInterfaceAdditional FieldsDescription
response.startedResponseStartedEventresponseIdMarks the beginning of an agent response.
text.deltaTextDeltaEventdelta, responseIdStreaming text fragment.
text.completedTextCompletedEventtext, responseIdFinal assembled text for the response.
rich.payloadRichPayloadEventpayloadType, payloadVersion?, data, responseIdRich content payload (product carousel, order summary, A2UI frame, etc.).
tool.callToolCallEventtoolCallId, toolName, input, responseIdAgent requests a client-side tool invocation.
tool.resultToolResultEventtoolCallId, output, error?, status?, idempotencyKey?, duplicateDisposition?, approvalPolicy?, responseIdResult of a tool execution sent back to the agent.
citationCitationEventtitle, url, snippet?, responseIdSource citation.
suggestion.chipsSuggestionChipsEventchips[], responseIdSuggestion chips for the user.
handoff.statusHandoffStatusEventstatus, targetAgent?, reason?, queuePosition?, estimatedWaitTime?, routeDecision?, graphPath?, responseIdLive-agent handoff status update. routeDecision and graphPath populated for bot_to_bot (agent-graph) handoffs.
signal.updateSignalUpdateEventsignal (SentimentSignalPart | IntentSignalPart), responseId?Synthesized by SignalRunner when an adapter emits a sentiment or intent classification. Flows through processTransportEvent exactly like server-emitted events.
diagnosticDiagnosticEventcategory, message, details?, responseIdDiagnostic event from the transport or backend.
errorErrorEventcode, message, retryable?, responseId?Transport or server error.
response.completedResponseCompletedEventresponseIdMarks the end of an agent response.
session.endedSessionEndedEventreason, 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:

MethodSignatureDescription
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:

FieldTypeDefaultDescription
classTransportClass'server-stream''request-response', 'server-stream', or 'bidi'.
reconnectbooleanfalseWhether the transport implements reconnect().
resumebooleanfalseWhether the transport implements resumeStream().
multiplexbooleanfalseWhether the transport multiplexes sessions (reserved).
protocolVersionstring'1'Wire-protocol version identifier.
attachmentsAttachmentAdapterCapabilities?unsupportedDeclares the file upload contract: inline-blob, proxy-upload, presigned-url, or unsupported.

Attachment Upload Contracts

The SDK recognizes one attachment contract per transport class:

ContractUsed ByBehavior
inline-blobMock and future validated live session APIsFile bytes travel inline with the message/session request. The current mock transport simulates this path deterministically.
proxy-uploadcreateProxyTransport and customer proxiesBrowser posts multipart data to the customer proxy. The proxy validates limits, applies audit/redaction policy, and forwards to the upstream upload backend.
presigned-urlCustom customer transportsThe 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.
unsupportedSession API, HTTP, WebSocket unless customizedchat.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.

Source: docs/reference/transport-events.md