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 a ChatClient. 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:

CategoryPart types
Texttext, text-delta, markdown
Rich contentcitation, suggestion-chips, product-carousel, order-summary, custom
Toolstool-call, tool-result
Lifecyclefile, agent-transfer, diagnostic, end-session, error
Voice and multimodalaudio-input, audio-output, transcript, audio-cue, vision
Memorymemory-approval, memory-recall-result
Signalssentiment-signal, intent-signal
Computecomputer-use-surface
Generative UIa2ui-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:

SubsystemTriggered bySee
MemoryStoreChatClientConfig.memoryMemory
VoiceSessionChatClientConfig.voice (lazy getter on chat.voice)Voice
SignalRunner / SignalEscalatorChatClientConfig.signalsSignals
agentGraphcreateAgentGraphTransport() wrapping the host transportAgent Graphs
PermissionManagerAlways present; behaviour depends on configured PermissionProviderPermissions
ProductAnalyticsCollector + dashboardsChatClientConfig.analytics; widgets under gecx-chat/dashboardsAnalytics, Hosted Dashboard
gecx eval runnerThe gecx eval <dir> CLI plus optional baseline fileEvaluation

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

TransportPatternUse case
MockSimulated streamingLocal dev, tests, demos. No backend needed.
HTTPRequest-responseSimple integrations. One response per request.
SSE / ProxyServer-streamingProduction default. Your proxy server streams events via Server-Sent Events.
WebSocketBidirectionalReal-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 shared ChatClient.
  • useChatSession -- hook that returns everything you need: messages, status, send(), input state, 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

Source: docs/concepts/architecture.md