Architecture
The GECX Chat SDK is organized into three layers: runtime, transport, and UI adapter. The runtime manages session state and message normalization. The transport handles the wire protocol between your app and a backend. The UI adapter (currently React; the core is framework-neutral) connects runtime state to your components. Each layer is independent -- you can swap transports without touching UI code, or build a custom UI without changing how sessions work.
Layer 1: Runtime
The runtime is the core of the SDK. Two objects do most of the work:
createChatClient-- factory that accepts config (auth, transport, tools, storage) and returns aChatClient. The client creates and manages sessions.ChatSession-- owns the lifecycle of a single conversation. Manages a state machine, normalizes transport events into typed message parts, and exposes a reactive store.
State machine
Every session progresses through a well-defined set of states:
idle -> authenticating -> connecting -> ready -> submitted -> streaming -> ready
| |
+-> uploading -> ready +-> waiting_for_tool -> streaming
+-> ended / expired +-> error -> recovering -> ready
The machine prevents invalid transitions (you cannot send while streaming) and drives UI indicators like loading spinners and error states. Observe transitions via typed events or store snapshots.
Message normalization
Transport events arrive in different shapes depending on the backend. The normalizer converts them into a uniform ChatMessage structure where each message contains typed parts. The discriminated union spans several categories:
| Category | Part types |
|---|---|
| Text | text, text-delta, markdown |
| Rich content | citation, suggestion-chips, product-carousel, order-summary, custom |
| Tools | tool-call, tool-result |
| Lifecycle | file, agent-transfer, diagnostic, end-session, error |
| Voice and multimodal | audio-input, audio-output, transcript, audio-cue, vision |
| Memory | memory-approval, memory-recall-result |
| Signals | sentiment-signal, intent-signal |
| Compute | computer-use-surface |
| Generative UI | a2ui-surface |
Every part carries a stable id so your renderer can reconcile updates efficiently. See Messages and Parts for per-type details and the Message Parts reference for full TypeScript interfaces.
Optional subsystems
The runtime ships several optional subsystems. None of them install unless the host opts in:
| Subsystem | Triggered by | See |
|---|---|---|
MemoryStore | ChatClientConfig.memory | Memory |
VoiceSession | ChatClientConfig.voice (lazy getter on chat.voice) | Voice |
SignalRunner / SignalEscalator | ChatClientConfig.signals | Signals |
agentGraph | createAgentGraphTransport() wrapping the host transport | Agent Graphs |
PermissionManager | Always present; behaviour depends on configured PermissionProvider | Permissions |
ProductAnalyticsCollector + dashboards | ChatClientConfig.analytics; widgets under gecx-chat/dashboards | Analytics, Hosted Dashboard |
gecx eval runner | The gecx eval <dir> CLI plus optional baseline file | Evaluation |
Layer 2: Transport
The transport layer abstracts how messages travel between the SDK and your backend. All transports implement the ChatTransport interface, so the runtime doesn't care which one you use.
Transport tiers
| Transport | Pattern | Use case |
|---|---|---|
| Mock | Simulated streaming | Local dev, tests, demos. No backend needed. |
| HTTP | Request-response | Simple integrations. One response per request. |
| SSE / Proxy | Server-streaming | Production default. Your proxy server streams events via Server-Sent Events. |
| WebSocket | Bidirectional | Real-time use cases needing push from the server. |
Switching transports is a config change:
import { createChatClient } from 'gecx-chat';
import { createProxyTransport } from 'gecx-chat';
const client = createChatClient({
transport: createProxyTransport({ url: '/api/chat' }),
});
Recovery policy
When a transport disconnects mid-turn, the SDK applies a recovery policy: exponential backoff, configurable retry limits, and automatic reconnection. The runtime tracks disconnected and recovering states so your UI can show appropriate feedback.
Layer 3: UI Adapter
The core runtime is framework-neutral. The optional React adapter (gecx-chat/react) provides:
ChatProvider-- React context that holds a sharedChatClient.useChatSession-- hook that returns everything you need:messages,status,send(),inputstate,error, and more.useChatClient-- access the underlying client for multi-session or advanced use cases.MessagePart-- component that renders any part type with sensible defaults; override per type via the renderer registry.
Minimal example:
import { useChatSession, MessagePart } from 'gecx-chat/react';
function Chat() {
const { messages, send, input, status } = useChatSession();
return (
<div>
{messages.map((msg) => (
<div key={msg.id}>
{msg.parts.map((part) => <MessagePart key={part.id} part={part} />)}
</div>
))}
<form onSubmit={(e) => { e.preventDefault(); send(input.value); input.clear(); }}>
<input value={input.value} onChange={(e) => input.set(e.target.value)} />
<button disabled={status !== 'ready'}>Send</button>
</form>
</div>
);
}
Using Vue, Svelte, or vanilla JS? Use createChatClient directly and subscribe to the store for state updates.
Mock-first development
The SDK defaults to mock transport and mock auth. No credentials, no backend, no setup:
import { createChatClient } from 'gecx-chat';
// Zero-config: mock transport + mock auth are the defaults
const client = createChatClient();
const session = client.createSession();
await session.start();
await session.send('Hello');
// session.messages now contains a realistic streamed response
Mock transport simulates realistic streaming with configurable latency, supports custom scenarios triggered by message content, and handles tool calls. Use it for prototyping, unit tests, and demos.
How it fits together
+──────────────────────────────────────────────────────+
| Your App |
| |
| useChatSession() / custom UI |
+──────────────┬───────────────────────────────────────+
|
+──────────────v───────────────────────────────────────+
| SDK |
| |
| ChatClient ──> ChatSession ──> Transport |
| | | | |
| config state machine mock / HTTP / |
| tools normalizer SSE / WebSocket |
| auth store |
+──────────────────────────┬───────────────────────────+
|
+──────────────────────────v───────────────────────────+
| Backend |
| (Customer proxy / Google CCAI / Mock) |
+──────────────────────────────────────────────────────+
Auth providers
The SDK ships four auth providers, swappable via config: mockAuth (default, fake tokens for dev), tokenEndpointAuth (exchanges credentials with your endpoint), customAuth (bring your own logic), and googleTokenBrokerAuth (Google's token broker for production).
What's next
- Quickstart -- get a chat running in 5 minutes
- React Quickstart -- React-specific setup
- Customer Proxy Contract -- how to build the SSE proxy
- Mock Protocol -- mock transport scenarios and customization
- Error Knowledge Base -- every error code and how to fix it
docs/concepts/architecture.md