# GECX Chat SDK Full Agent Guide Use this guide after scanning `docs/agent/llms.txt`. Each recipe is intentionally self-contained so an agent can build a working slice without reading the entire repository. ## 1. Quick Start: Zero To Chat Goal: render a working mock-first React chat. ```tsx 'use client'; import { createMockTransport, scenarioCatalog } from 'gecx-chat/testing'; import { MessagePart, useChatSession } from 'gecx-chat/react'; export function SupportChat() { const chat = useChatSession({ config: { transport: createMockTransport({ scenarios: scenarioCatalog.list(), activeScenarioId: 'order-status', latencyMs: 40, }), }, }); return (
    {chat.messages.map((message) => (
  1. {message.parts.map((part) => )}
  2. ))}
{ event.preventDefault(); void chat.sendText(chat.input.value); }}> chat.input.set(event.currentTarget.value)} aria-label="Message" /> {chat.isStreaming ? : null}
); } ``` Gotchas: use mock transport first, use `part.id` keys, and call `chat.sendText(chat.input.value)` from a form submit. Verify: `pnpm typecheck && pnpm test`. ## 2. Streaming With Typing Indicators Goal: make streaming state visible without coupling UI to transport internals. Use `chat.isStreaming`, `chat.status`, `chat.stop`, and the `streaming-indicators` recipe: ```bash gecx add streaming-indicators --out . ``` Wire the copied component near the composer. Keep the response list in an `aria-live="polite"` region and avoid layout jumps by reserving height for the indicator. Verify: run a slow scenario with `createMockTransport({ scenarios: scenarioCatalog.list(), activeScenarioId: 'slow-streaming', latencyMs: 120 })`. ## 3. Add A Client Tool With Approval Goal: let the assistant call an app-owned action safely. ```ts import { defineClientTool } from 'gecx-chat'; export const addToCartTool = defineClientTool<{ productId: string; quantity: number }>({ name: 'add_to_cart', description: 'Add a product to the signed-in customer cart', inputSchema: { type: 'object', required: ['productId', 'quantity'], properties: { productId: { type: 'string' }, quantity: { type: 'number', minimum: 1 }, }, }, permissions: { requiresUserApproval: true }, timeoutMs: 10_000, execute: async (input, ctx) => { const response = await fetch('/api/cart/items', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(input), signal: ctx.signal, }); if (!response.ok) return { error: 'cart_update_failed', status: response.status }; return await response.json() as Record; }, }); ``` Gotchas: every tool needs JSON Schema, state-changing tools require approval, and fetches must receive `ctx.signal`. Verify: test success, validation failure, denial, and abort. ## 4. Rich Content Rendering Goal: show structured content like citations, chips, order summaries, and product cards. Prefer typed message parts from the SDK and copy-owned recipes from the registry: ```bash gecx add citation-previews gecx add suggestion-chips gecx add product-carousel ``` Render all known parts through `MessagePart` or a registry. Unknown rich payloads must render inertly as unsupported content, never as executable HTML or script. Verify: run `scenarioCatalog.get('commerce-recommendation')` or the generated commerce template. ## 5. Custom Message Part Renderer Goal: register a brand-specific renderer for a typed payload. ```tsx import { ChatProvider, createRendererRegistry } from 'gecx-chat/react'; import type { ProductCarouselPart } from 'gecx-chat'; const renderers = createRendererRegistry({ 'product-carousel': (part: ProductCarouselPart) => (
{part.products.map((product) => (
{product.title} {product.price ? {product.price} : null}
))}
), }); export function App({ children }: { children: React.ReactNode }) { return {children}; } ``` Gotchas: branch by part type, validate payload shape, and keep text inert. Verify: add a renderer unit test and a browser smoke test if visual quality matters. ## 6. Token Endpoint Setup Goal: keep credentials server-side while the browser receives a scoped chat token. ```ts // app/api/gecx-chat-token/route.ts import { createChatTokenHandler } from 'gecx-chat/server'; const handler = createChatTokenHandler({ allowedOrigins: [process.env.NEXT_PUBLIC_APP_URL ?? 'http://localhost:3000'], tokenTtlMs: 15 * 60_000, }); export async function POST(request: Request) { return handler(request); } ``` Client: ```ts import { tokenEndpointAuth } from 'gecx-chat'; tokenEndpointAuth({ endpoint: '/api/gecx-chat-token' }); ``` Gotchas: never ship service account keys to the browser and never log token values. Verify: POST to the route, check CORS/origin behavior, and inspect a redacted debug bundle. ## 7. Custom Transport Goal: connect the UI to any backend while preserving the SDK state model. ```ts import type { ChatTransport, SendRequest, TransportEvent } from 'gecx-chat'; export function createCustomTransport(endpoint: string): ChatTransport { return { async connect() {}, async send() {}, async *stream(request: SendRequest): AsyncIterable { const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(request), }); if (!response.ok) throw new Error('custom_transport_failed'); yield { type: 'response.started', responseId: crypto.randomUUID(), requestId: crypto.randomUUID(), timestamp: new Date().toISOString() }; yield { type: 'text.delta', delta: await response.text(), responseId: crypto.randomUUID(), requestId: crypto.randomUUID(), timestamp: new Date().toISOString() }; yield { type: 'response.completed', responseId: crypto.randomUUID(), requestId: crypto.randomUUID(), timestamp: new Date().toISOString() }; }, async close() {}, }; } ``` Gotchas: preserve event order, provide stable IDs per response, and map backend errors to SDK error codes. Verify: add a transport contract test with a mocked server. ## 8. Proxy Transport Goal: proxy browser requests through an app server. Use `createProxyTransport({ endpoint: '/api/gecx-chat-proxy' })` in the browser. The server route should authenticate the user, call the upstream model/backend, and stream SDK `TransportEvent` records back to the client. Gotchas: set `Content-Type: text/event-stream`, `Cache-Control: no-store`, and `X-Accel-Buffering: no` for streaming proxies. Verify: run a stream-interruption test and ensure browser code has no secrets. ## 9. File Upload Goal: validate and attach user files before sending. Use `chat.attachFile(file)` from `useChatSession`. Configure transport upload support, file validation, progress events, and error rendering. Checklist: max size, allowed MIME types, progress UI, cancellation, retry, and redacted filenames in diagnostic exports when needed. Verify: test accepted file, rejected type, too-large file, and aborted upload. ## 10. Handoff To Live Agent Goal: model and render the full bot↔human lifecycle, including warm transfers, human-to-human transfers, supervisor consults, and voice handoffs. The handoff platform has three actors: - **Customer side** — `ChatSession.requestTransfer(options)` builds a `TransferContextBundle` (transcript digest, identity, warm context, optional SIP envelope) and drives a `HandoffController` state machine. The session emits `handoff_transition` events alongside the existing `handoff_status_changed`. The customer's transcript carries `AgentTransferPart` parts with the new optional fields: `transferType`, `warmContext`, `voice`, `bundleId`, `estimatedWaitMs`, `failureReason`. - **Cockpit side** — `gecx-chat-cockpit` ships `useAgentSession` and ``. The cockpit sees the customer's transcript + cockpit-only actions: `pickUp`, `placeOnHold`, `resumeCall`, `completeSession`, `transferTo`, `endSession`, `addInternalNote`, `whisper`. Use `createMockCockpitTransport()` for offline dev/tests. - **CCaaS adapters** — `gecx-chat-ccaas` exposes opt-in subpaths: `google-ccai`, `salesforce`, `servicenow`, `zendesk`, `genesys`, `five9`. Each adapter takes a `TransferContextBundle` from `buildTransferContextBundle()` and writes it to the destination system. Adapters do not depend on the core SDK. Transfer types (the `TransferType` union): - `bot_to_human` — default escalation. - `human_to_human` — tier-1 → tier-2 escalation. The cockpit's `transferTo` emits this. - `supervisor_consult` — agent ↔ supervisor side channel; the cockpit's `whisper` action uses this. - `blind` — handoff without a context bundle (legacy). - `warm` — handoff with a populated `WarmTransferContext`. State machine: `none → requested → queued → ringing → connected → on_hold → connected → ended | completed`. Plus terminal `failed` / `cancelled` / `completed`. Conflict resolution: first `PICKUP` wins; subsequent picks return `HANDOFF_ALREADY_CLAIMED`. Idempotency: repeat `REQUEST`s with the same `idempotencyKey` are no-ops. Mock: `scenarioCatalog.get('handoff')` exercises the customer-side flow. Use `createMockCockpitTransport()` to drive the cockpit in tests. Verify: every status renders accessibly, the cockpit can pick up the transfer, the warm context surfaces in the customer info panel, and `handoff_transition` audit events fire on every transition. Run `pnpm e2e:cockpit` for the Playwright subset (tagged `@handoff`). ## 11. Conversation Storage And Resumption Goal: resume conversations without coupling UI to a backend. Use `createMemoryStorage()` for tests and ephemeral demos. Use the web storage adapter for local/session persistence when the browser owns the session cache. Keep server-side persistence behind your app backend. Gotchas: namespace storage per user/account, redact sensitive message content from logs, and handle expired sessions explicitly. Verify: create a session, reload/resume, and assert stale sessions fail with actionable errors. ## 12. Error Handling And Diagnostics Goal: give builders actionable failure states. Use SDK error codes, `chat.error`, trace events, and `createDebugBundle`. Render user-facing errors with next steps and builder-only diagnostics behind a debug surface. Common fixes: token endpoint CORS, stream proxy buffering, unknown tool name, invalid tool input, session state mismatch, and aborted requests. Verify: run `gecx doctor` and the diagnostics scenario. ## 13. Debug Bundle Generation Goal: produce a redacted artifact for support. Use `chat.createDebugBundle()` from React or `createDebugBundle(input)` from core. Confirm tokens, private keys, and privileged credentials are redacted before display or export. Verify: tests should include token-looking values and prove they are replaced. ## 14. Styling And Theming Goal: make generated chat feel native to the product. Keep the first screen as the chat experience. Use product-specific nouns, data, empty states, and controls. Recipes are copy-owned reference components; adapt them to the app’s tokens and layout. Verify: check desktop and mobile, no text overlap, focus states visible, and high-contrast controls. ## 15. Accessibility Goal: meet WCAG AA expectations in custom chat surfaces. Use semantic form controls, `aria-live="polite"` for message updates, visible focus, accessible names for icon buttons, keyboard-operable approval dialogs, and reduced-motion-aware streaming indicators. Recipe: `gecx add accessibility-controls`. Verify: keyboard-only send/stop/approve/deny, screen-reader names, and color contrast. ## 16. Testing Patterns Goal: prove behavior without a live backend. Use `createMockChatClient`, `createMockTransport`, `scenarioCatalog`, tool fakes, and replayed scenarios. Prefer integration tests for chat flows and unit tests for renderer/data helpers. Minimum tests: generated app smoke, tool success, invalid tool input, renderer fallback, debug redaction, and scenario replay. Verify: `pnpm test`. ## 17. Production Hardening Goal: survive real users and unreliable networks. Add retry/backoff where appropriate, error boundaries around chat surfaces, telemetry hooks, rate limiting on server routes, origin checks, upload limits, stream cancellation, and graceful degradation for unsupported parts. Verify: lint for browser secrets, typecheck, tests, build, and browser smoke. ## 18. Multi-Session Management Goal: support inboxes, tabs, and account-specific conversations. Create sessions through `client.createSession`, store session IDs in app state, and scope storage by user/account. Avoid global singleton state for product experiences that need multiple simultaneous chats. Verify: send in two sessions and assert messages do not cross-contaminate. ## 19. Tool Orchestration Goal: support multiple tools without unsafe implicit behavior. Register tools explicitly. Use typed outputs and state-changing approval. Show tool cards for pending, approved, denied, running, succeeded, and failed states. Recipe: `gecx add tool-call-cards`. Verify: chain two tools in a mock scenario and assert each tool’s result is attached to the correct call ID. ## 20. Generative UI Goal: let an agent emit interactive generated UI without executing arbitrary payloads. Use A2UI v0.9 over `rich.payload` with `payloadType: 'a2ui.frame'`. The SDK normalizer validates frames and accumulates them into typed `a2ui-surface` message parts. React hosts opt in by importing `createA2UIRenderer` from `gecx-chat/a2ui/react` and registering explicit catalogs from `@a2ui/react/v0_9`. ```tsx import { basicCatalog } from '@a2ui/react/v0_9'; import { createA2UIRenderer } from 'gecx-chat/a2ui/react'; const renderA2UI = createA2UIRenderer({ catalogs: [basicCatalog], onAction: (action) => { // Route to a tool result, user message, or app-owned endpoint. console.log(action.name); }, }); ``` Pattern: agent emits `createSurface`, `updateComponents`, and `updateDataModel`; the host renders only registered catalogs and routes actions through a policy-gated `onAction` callback. Verify: run the `/generative-ui` showcase route, assert malformed frames stay inert, and cover action confirmation or rejection in tests. For a small worked example inside a real product flow, see the "Build a gift bundle" moment in `apps/applied-ai-retail/` — three frames rendered as a Card+Slider+Button configurator. The host wrapper displays live preference state, and the generated Button action round-trips back through chat as an approval-gated `add_to_cart` turn with `giftBundle` metadata. ## 21. Computer-use (sandboxed agent browsing) Goal: let an agent drive a sandboxed browser on the user's behalf — fetch order status, fill a form, retrieve a download link — without leaking control of the host page or the user's credentials. The `computer_use` server tool is **default off**. Three independent layers of policy: 1. **SDK fast-fail** via `ChatGovernance.assertComputerUseAllowed({ urls })`. 2. **Proxy enforcement** via `createComputerUseHandler({ policy: { allowlist, ... } })`. This is the security boundary. 3. **Vendor allowlist** passed to the provider (e.g. Browserbase's `browserSettings.allowedUrls`). ```ts // 1. SDK: opt in + register the tool import { computerUseTool, createChatClient } from 'gecx-chat'; const client = createChatClient({ governance: { computerUse: { enabled: true, allowlist: ['acme-orders.example.com'], maxDurationMs: 5 * 60_000, maxActionsPerSession: 30, }, }, tools: [computerUseTool({ endpoint: '/chat/tool-call' })], }); // 2. Proxy: mount handler import { createComputerUseHandler } from './computerUse/createComputerUseHandler'; import { BrowserbaseProvider, MockProvider } from 'gecx-chat/server'; const handler = createComputerUseHandler({ provider: process.env.COMPUTER_USE_PROVIDER === 'browserbase' ? new BrowserbaseProvider({ apiKey, projectId, act, screenshot }) : new MockProvider(), policy: { allowlist: ['acme-orders.example.com'], maxDurationMs: 300_000, maxActionsPerSession: 30, highRiskActions: new Set(['submit_form', 'download', 'navigate_external']), }, audit: (event) => yourSiem.send(event), }); // then route POST /chat/tool-call (computer_use) → handler.handleToolCall // GET /chat/computer-use/:id/stream → handler.handleStream // POST /chat/computer-use/:id/control → handler.handleControl // POST /admin/computer-use/kill-switch → handler.setKillSwitch // 3. React surface import { ComputerUseSurface } from 'gecx-chat/react'; yourAuditSink(e)} /> ``` Pattern: every agent step is auditable (`governance.computer_use.*`), every URL is allowlisted, high-risk actions surface a per-action approval modal, and the user can abort at any time. The iframe surface uses `sandbox=""` (no scripts, no same-origin), so a compromised payload cannot escalate into the host page DOM. Verify: run the `/computer-use` showcase route (or the applied-retail one) with `COMPUTER_USE_PROVIDER=mock`. The Playwright e2e suites cover consent, high-risk approval, allowlist refusal, and user abort. Full integration runbook + threat model: `docs/guides/computer-use.md` + `docs/reference/computer-use-threat-model.md`. ## 22. Commerce (DTC Retail) Goal: build cart → checkout → track → return flows with PCI/PII safety, idempotency, and a measurable funnel. Detailed reference: `skills/headless-chat-vibe-coding/references/commerce.md`. This section is the index. The five jobs commerce chat must do: 1. **Recommend** — emit a `product-carousel` part. Install the recipe (`pnpm exec gecx add product-carousel`). Stable product IDs are mandatory React keys. 2. **Mutate the cart** — approval-gated `add_to_cart`, `apply_coupon`, `remove_from_cart`. Always idempotent (use `ctx.idempotencyKey`). 3. **Check out** — approval-gated `checkout`. PCI fields stay server-side. Emit an `order-summary` part on success. 4. **Track + service** — read-only `track_order`. Approval-gated `initiate_return`. Render via the `order-status-card` recipe. 5. **Prove the journey** — funnel events flow through `ProductAnalyticsSink`. Aggregate with the `commerce-funnel` recipe. PCI/PII compliance: ```ts import { commerceGovernance, redactCommercePii, redactCommercePiiDeep } from 'gecx-chat'; const policy = commerceGovernance({ strict: true }); // In every state-changing tool's execute body: const safeInput = policy.redactDeep(input); // before logging or analytics const safeOutput = policy.redactDeep(result); // before display // On approval surfaces: const approvalText = policy.redact(rawApprovalText); // On debug bundles: const bundle = chat.createDebugBundle(); const safeBundle = policy.redactDeep(bundle); // before any post or log ``` Strict mode throws `COMMERCE_PII_LEAK` when a PAN-shaped value passes through. Patterns detected and masked: PAN (Luhn-validated), CVV, SSN, EIN, email, phone, US street address. See `docs/reference/error-codes.md#commerce-pii-leak`. Lint your source for raw PCI literals and unguarded payment-method fields: ```bash pnpm exec gecx validate ``` Recipes (under `recipes/`, install with `gecx add `): - `cart-summary` — typed renderer for a `custom-payload` part with `payloadType: 'cart-summary'`. PCI-safe contract. - `order-status-card` — three-mode renderer for `order-status`, `rma-summary`, and `tracking-timeline`. - `commerce-funnel` — pure-TS aggregator mapping `ProductAnalyticsEvent`s to funnel stages with conversion rates, AOV, and cart abandonment. - `product-carousel` — existing first-party recipe. MCP prompts for common commerce tasks: `add-cart-renderer`, `add-checkout-flow`, `add-returns-flow`, `redact-pci-fields`. Verify: typecheck + test + walkthrough capture for the canonical flow: ```bash pnpm typecheck && pnpm test pnpm exec gecx walkthrough capture --task=05-commerce-concierge pnpm exec gecx walkthrough verify --task=05-commerce-concierge ``` ## 23. Cross-Agent Iteration (Skills, MCP, Lab, Walkthroughs) Goal: develop with Claude Code, Codex, or Antigravity in the same repo, with the same skill, same MCP server, same eval lane, and the same walkthrough artifact format. Skill source of truth lives at `skills/headless-chat-vibe-coding/`. The `skills/build.ts` script syncs it to: - `.claude/skills/headless-chat-vibe-coding/` (Claude Code) - `.agents/skills/headless-chat-vibe-coding/` (Codex, Cursor, Gemini CLI, Junie) - `.agent/skills/headless-chat-vibe-coding/` (Antigravity — singular) Runs automatically on `pnpm install` via the `postinstall` hook. Run manually with `pnpm skills:build`. Validate with `pnpm skills:check`. MCP server (`packages/gecx-chat-mcp-server`) ships: - 15 tools (scaffold, add_tool, add_renderer, add_transport, configure, diagnose, validate, doctor, list_recipes, describe_api, list_parts, preview, preview_stop, capture_walkthrough) - 12 resource templates including `gecx-chat://recipes/{recipeId}`, `gecx-chat://scenarios/{id}`, `gecx-chat://examples/{appName}/{filePath}`, `gecx-chat://walkthroughs/{taskId}` - 14 prompts (10 general + 4 commerce-specific) Install for any agent: ```bash pnpm dlx gecx-chat-mcp-server --install # auto-detect from .claude/, .agents/, .agent/ pnpm dlx gecx-chat-mcp-server --install --all # write all three configs ``` Lab playground (`@gecx/lab`) generates a `/lab` route in scaffolded apps with prompt cards, agent-target picker, verify button, and walkthrough capture: ```bash pnpm exec gecx lab init --template commerce --framework next ``` Walkthrough artifact protocol — the cross-agent "show your work" format: ```bash pnpm exec gecx walkthrough capture --task= [--url=] [--driver=stub|playwright] pnpm exec gecx walkthrough verify --task= ``` Schema: `schemas/walkthrough.schema.json`. JSON manifest + PNG screenshots. Antigravity emits natively; CC and Codex emit via the CLI driving Chrome DevTools MCP or Playwright. The eval harness consumes the same format. Eval lanes: Claude Code (live), Codex (opt-in), Antigravity (automated; fixture-dir or CLI-exec mode). All three measure pass rate and time-to-running-chat (`tToRC_ms`). Run with `pnpm agent-evals --report` for an HTML cross-agent comparison.