Skip to content

Copilot API

Status: Draft | Last reviewed: 2026-10-07

How to put a TrueServe API in front of the copilot (agent-assist) pipeline so a custom Salesforce Lightning Web Component — or any host — gets real-time suggestions and chat Q&A during a live conversation, with the underlying AI (Google, AWS, or a TrueServe agent) swappable by config. Read this before building the /copilot/* facade or a Salesforce connector; it maps what already exists to what a follow-up must add.

This is a design/feasibility doc. No /copilot/* routes or connector LWC exist yet — the sections below name the existing building blocks each one reuses.

For live Genesys transcripts, the existing AudioHook connector and conversation SDK already provide the capture/subscription path without this proposed facade. Use Live Genesys transcripts for that implemented contract and its external test-phone limitations.

The existing conversation API now supports host events without the proposed facade: POST /conversations/{id}/ui-action accepts delivery: card | stream | both. SDK sendCopilotEvent uses stream delivery; opted-in Copilot workers process it independently of speech. The Salesforce Agent Assist wrapper exchanges these events over LMS. See Copilot host context and the embed protocol.

The Google Agent Assist contract you are replacing

Google's agent-assist-integrations ships the reference backend for hosted Agent Assist. Its contract has three seams:

  • Browser event bus — a global add/dispatchAgentAssistEvent(name, {detail}, {namespace}) API served by prebuilt web components (gstatic common.js / container.js). Key events: analyze-content-requested, analyze-content-response-received, smart-reply-selected, conversation-summarization-received, search-knowledge-requested/-response-received.
  • UI-Connector backend — Cloud Run Flask service: POST /register exchanges a platform token for a short-lived HS256 JWT, then transparently proxies the Dialogflow v2beta1 REST surface and pushes voice suggestions over Socket.IO (join-conversation + four event types).
  • Dialogflow resources — ConversationProfile / Conversation / Participant / AnalyzeContent, each suggestion carrying an answerRecord you PATCH for feedback.

You do not reimplement any of this. The Salesforce panels that consume Agent Assist (below) discard Google's widget UI and its Dialogflow event vocabulary — they keep only a small provider-agnostic seam. That seam is the target.

The DCC swap seam (target contract)

The two Aura containers to integrate — DCC_RightPanel (voice) and DCC_CoupaChatRightPanel (chat) — live in the devsdo Salesforce org, not in this repo. Retrieve them read-only with sf project retrieve start -m "AuraDefinitionBundle:DCC_RightPanel" -o devsdo.

Each panel embeds Google's connector headless: a hidden div mounts agentAssistContainerModule / googleAgentAssistHeadlessConnector, which subscribes to the Agent Assist event bus and re-emits every event upward as one bubbling agentassistevent carrying { eventType, eventDetail }. Transform LWCs then normalize that into a provider-agnostic seam that three backends — Google (dccGoogleAgentAssistInsights), a raw WebSocket (dccRealTimeInsights), and Amazon Q (dccAmazonQRealTimeInsights, over a Lightning Message Service channel) — already emit identically.

Direction Event detail shape
Backend → panel (insights) conversationinsight { isQa: true, questionData: { question, answer, id, status, isAnswerGenerated, currentTime, isRead, recommendationsList } }
Panel → backend (chat) sendquery { query }
Backend → panel (chat) receiveResponse (via @api) { answer, sources: [{ title, url }], error }

Because Amazon Q already rides this seam without any Dialogflow vocabulary, a TrueServe backend joins the same way: a connector LWC that emits conversationinsight and answers sendquery with receiveResponse. No /register, no Socket.IO, no answerRecord.

TrueServe substrate you build on

The copilot runtime already exists end to end. The API is a facade plus a subscribe/turn broker over it — not new AI.

Capability Where it lives
Structured-output suggestion LLM SuggestionLLMProcessor — per-type JSON schemas (smart_reply, response_suggestion, knowledge_assist, article, form_filling); provider env map covers openai / google_cloud / google_vertex / aws_bedrock
Service-agnostic suggestion frame suggestion_frames.py — SuggestionType enum + SuggestionFrame
Frame → conversation-api ConversationAPIPublisher → Redis entries:{id} (+ reserved entries:{id}-assist shard)
Redis → SSE normalization process_sse_outgoing_entry in http_api.py
First-class copilot agent subtypes agent_assistant, voice_augmentation in service_constants.py
Synchronous turn pattern run_agent_turn() in a2a_server.py

The SSE stream (GET /sse/conversations/{id}) already emits copilot entries. The normalizer collapses SUGGESTED_RESPONSE and SUGGESTED_KNOWLEDGE to type: "knowledge_suggestion" (with suggestion_type smart_reply / article), SUGGESTION to knowledge_suggestion or suggestion (form-filling), and CUSTOM_INSIGHT to its own kind. That is the raw material a connector maps to conversationinsight.

flowchart LR
    subgraph SF["Salesforce (devsdo org)"]
        panel["DCC_RightPanel / DCC_CoupaChatRightPanel"]
        connector["trueServeCopilotConnector (new LWC)"]
        insights["dcc*RealTimeInsights"]
        chat["dccCoupaAgentAssistChat"]
    end
    subgraph CAPI["conversation-api"]
        facade["/copilot/* facade (new)"]
        sse["GET /sse/conversations/:id"]
        turn["run_agent_turn()"]
        redis[("Redis entries:{id} + -assist")]
    end
    subgraph ORCH["ai-orchestrator"]
        proc["SuggestionLLMProcessor"]
        pub["ConversationAPIPublisher"]
        llm{{"stage provider/service"}}
    end
    llm --> proc --> pub --> redis
    redis --> sse
    redis --> turn --> facade
    connector -- subscribe --> sse
    sse -- "suggestion / knowledge_suggestion" --> connector
    connector -- "conversationinsight" --> insights
    chat -- "sendquery" --> connector
    connector -- "POST /copilot/:id/query" --> facade
    facade -- "{answer, sources[]}" --> connector -- receiveResponse --> chat

Proposed copilot API facade

Add a /copilot/* router to conversation-api. Every route reuses existing plumbing; none needs a new AI path.

Route Purpose Reuses
POST /copilot/sessions Open a copilot session against an agent, or bind to an existing conversation create_session; get_conversation_by_external_id
POST /copilot/{id}/analyze Push one transcript turn (foreign-telephony fallback) the POST /conversations/{id}/inject / send_user_message path
POST /copilot/{id}/query Synchronous chat Q&A → { answer, sources[] } a generalized run_agent_turn
POST /copilot/{id}/summary Conversation summary on demand a copilot agent turn
GET /sse/conversations/{id} Live suggestion stream (existing — no new route) process_sse_outgoing_entry

Suggestions flow over the existing SSE suggestion / knowledge_suggestion events; the facade adds no second stream. The synchronous endpoints reuse the Redis-drain turn pattern: create or reuse a session, post the text, drain entries:{id} until the turn goes idle or A2A_TURN_TIMEOUT_SEC elapses, return the collected agent text.

Authenticate a Salesforce caller with either a tsf_ bearer token (agents:manage) presented from Apex via a Named Credential, or the surface-internal HMAC path that surface-app already uses. The Salesforce package uses separate endpoints for these calls: ai-manager mints embed sessions, while GET /conversations/by-external-id/{external_id} targets the public conversation-api host. Pointing the resolver Named Credential at the Agent Studio web host produces a 307 /login response that Apex will not follow. Register the Salesforce origin in ai-manager's allowed-origins for any browser-direct calls (GET /api/allowed-origins/active gates conversation-api CORS).

The connector LWC (swap point)

Build one small LWC — call it trueServeCopilotConnector — as a drop-in replacement for agentAssistContainerModule inside the DCC panels. Both panels already forward their connector's output centrally (handleGoogleAgentAssistEvent fans agentassistevent out to child components), so the connector only has to emit the normalized events the children already understand:

  • On mount, resolve the conversation (see binding modes below) and subscribe to GET /sse/conversations/{id}.
  • Map each suggestion / knowledge_suggestion / custom_insight SSE event to a conversationinsight CustomEvent with the { isQa, questionData } shape from the seam table.
  • Handle sendquery by calling POST /copilot/{id}/query and returning the result to dccCoupaAgentAssistChat via its receiveResponse @api.

Use the existing Agent Studio for Salesforce package as the auth precedent: TrueServeSurfaceController.mintEmbedToken(agentId, conversationId, audienceOrigin, sessionVars) already mints the embed JWT from Apex, and its LWCs already consume TrueServe SSE (final_transcript).

Voice vs chat binding

The chat panel always drives a text session. The voice panel resolves the transcript of a live call, and there are two modes — support both.

  • Resolve an existing conversation (primary). When the call runs on the TrueServe stack (Service Cloud Voice BYOT via connect-scv-translator, or TrueServe telephony), the conversation and its transcript already exist. The LWC resolves it with GET /conversations/by-external-id/{external_id} (this route exists; its code comment names Salesforce LWCs as the consumer and it returns {conversation, surface_config_id, surface_config}), then subscribes to SSE. No transcript is pushed.
  • Push the transcript (fallback). When the call runs on foreign telephony (Genesys, Amazon Connect) and TrueServe provides only the copilot, Salesforce pushes each transcript turn to POST /copilot/{id}/analyze (mirroring Google's analyze-content-requested), and suggestions come back over SSE.

Swapping the underlying AI

This is the payoff. The copilot's LLM is the stage provider / service in agent_versions.config. SERVICE_LOOKUP_V2 already carries AWS/Amazon (Nova Sonic, Bedrock), Google, OpenAI, and Salesforce Agentforce entries. Retargeting the copilot from one backend to another is a config edit resolved by get_service_mapping() — no change to the /copilot/* API, the connector LWC, or the DCC panels. See universal-pipeline-builder.md for how config becomes a running pipeline.

Feasibility verdict and gaps

Ready today:

  • The suggestion pipeline (SuggestionLLMProcessor → SuggestionFrame → publisher) and its SSE events.
  • agent_assistant / voice_augmentation subtypes.
  • GET /conversations/by-external-id for voice binding.
  • run_agent_turn as the synchronous-turn template.
  • The DCC provider-agnostic seam (proven by three existing backends).
  • Config-driven AI swap via SERVICE_LOOKUP_V2.

The follow-up must build:

  • The /copilot/* facade routes in conversation-api.
  • A generalization of run_agent_turn for query / summary (parameterize the throwaway vs. bound conversation).
  • The trueServeCopilotConnector LWC.
  • Registration of the Salesforce origin in allowed-origins.

Watch these:

  • Sync-turn latency. query / summary block on the Redis drain up to A2A_TURN_TIMEOUT_SEC (default 45s). Keep copilot turns short, or return partials.
  • Token handling in the browser. A browser LWC must not hold a tsf_ token. Use an Apex proxy or the embed/HMAC path; the token stays server-side.

See also