Building an agent cockpit
gecx-chat-cockpit is the agent-side dual of gecx-chat. It mirrors
useChatSession and ChatSession from the agent's perspective:
the agent sees the customer transcript live, plus cockpit-only controls
(pick up, hold, resolve, transfer, end, whisper, add note).
The SDK core stays framework-neutral.
gecx-chat-cockpitand the CCaaS adapters are opt-in dependencies —gecx-chatitself imports neither.
Install
pnpm add gecx-chat gecx-chat-cockpit
Minimal example
import { useAgentSession, CockpitSurface } from 'gecx-chat-cockpit/react';
import { createMockCockpitTransport } from 'gecx-chat-cockpit/testing';
const pair = createMockCockpitTransport();
export function AgentDesk() {
const cockpit = useAgentSession({
identity: { agentId: 'a-101', displayName: 'Sam K.', team: 'Tier 1' },
transport: pair.cockpit,
audit: (event) => console.log('audit', event),
});
return (
<CockpitSurface
state={cockpit.state}
onPickUp={cockpit.pickUp}
onHold={cockpit.placeOnHold}
onResume={cockpit.resumeCall}
onComplete={cockpit.completeSession}
onEnd={cockpit.endSession}
onTransfer={cockpit.transferTo}
onSend={cockpit.sendMessage}
onAddNote={cockpit.addInternalNote}
/>
);
}
The CockpitTransport contract
The cockpit talks to the customer-side session through a
CockpitTransport:
interface CockpitTransport {
onAction(handler: (action: HandoffAction) => void): () => void;
onMessage(handler: (message: ChatMessage) => void): () => void;
sendMessage(text: string, options?: { transferType?: TransferType }): Promise<void>;
dispatchAction(action: HandoffAction): Promise<void>;
getSnapshot?(): Promise<{ state: HandoffProtocolState; bundle: TransferContextBundle | null; messages: ChatMessage[] }>;
}
For local development and tests, createMockCockpitTransport() returns
both sides of an in-process pair. For production deployments, your
back-end exposes a WebSocket (or polling REST) endpoint that adapts to
this shape.
Cockpit-only actions
| Method | Effect |
|---|---|
pickUp() | Claim a queued/ringing transfer. First call wins. |
placeOnHold() | Move from connected to on_hold. |
resumeCall() | Move back from on_hold to connected. |
transferTo(opts) | End the current leg, open a new handoff REQUEST for the next agent (default: human_to_human). |
completeSession(reason?) | Mark the handoff resolved — emits COMPLETE protocol action and transitions to terminal completed state. Distinct from endSession. The default cockpit surface renders this as a "Resolve" button. |
endSession(reason?) | Terminate the connected session without resolution semantics. |
sendMessage(text) | Send a customer-visible reply. |
addInternalNote(text) | Append to the cockpit's internal notes (not customer-visible). |
whisper(text) | Send a supervisor-consult side-channel message. |
Audit events
Every cockpit action emits a CockpitAuditEvent. Pipe these into your
governance audit sink so the agent-side actions show up next to the
customer-side handoff transitions:
useAgentSession({
identity: { agentId: 'a-1' },
transport,
audit: (event) => sendToAuditSink(event),
});
Custom surface
<CockpitSurface> is a sensible default, but the surface is just a
React component. Build your own using the cockpit.state snapshot —
all you need is the AgentSessionApi interface returned by
useAgentSession.
See also
- Handoff concept — the underlying protocol model.
- CCaaS integration — for recording the cockpit's pickup with your system of record.
docs/guides/agent-cockpit.md