Skip to content

@uservane/copilotkit

Frontend UserVane capture for CopilotKit.

CopilotKit is a frontend framework. Agent execution and Langfuse tracing live on the customer’s backend (LangGraph, CrewAI, Mastra, or any AG-UI agent) over AG-UI. This package is frontend-only: it captures feedback at deterministic turn resolution and links by CopilotKit threadId. It does not ship a Langfuse trace bridge (unlike @uservane/openai-agents). The backend owns tracing; UserVane posts a session-level uservane.satisfaction score keyed by threadId once the respondent answers.

  1. <UserVaneCopilotFeedback> watches the chat loading edge, binds a server-minted token (sessionId = threadId), and calls resolveTask once per genuine turn. Renders InlineFeedback adjacent to your chat layout (you place the component; no in-transcript slot required).
  2. Server mint (/server) re-exports mintFeedbackToken from @uservane/agent-core/server. Mint with sessionId = threadId.
  3. Re-exports UserVaneProvider, InlineFeedback, useUserVane from @uservane/agent-react for a single import surface.
Terminal window
npm install @uservane/copilotkit @copilotkit/react-core
# peer: react >= 18
# pin CopilotKit >= 1.63 (threadId propagation bug in older versions, #2624)
// app/api/feedback-token/route.ts (server only)
import { mintFeedbackToken } from "@uservane/copilotkit/server";
const threadId = body.threadId; // same value as <CopilotKit threadId={...}>
const { tokens } = await mintFeedbackToken({
secretKey: process.env.USERVANE_SECRET_KEY!, // uv_sk_... never to the client
respondentId: userId,
sessionId: threadId,
surveyId: "surv_post_task",
});
// thread tokens[surveyId] + sessionId to the client

Never import @uservane/copilotkit/server from client code.

import { CopilotKit } from "@copilotkit/react-core";
import { CopilotChat } from "@copilotkit/react-ui";
import {
UserVaneProvider,
UserVaneCopilotFeedback,
} from "@uservane/copilotkit";
const threadId = "/* your stable chat thread id */";
export function SupportChat({ showToken }: { showToken: string }) {
return (
<CopilotKit runtimeUrl="/api/copilotkit" threadId={threadId}>
<UserVaneProvider apiKey="uv_pk_live_..." surveySlug="post-task">
<CopilotChat />
{/* Place adjacent to the chat, not inside the transcript */}
<UserVaneCopilotFeedback
surveyId="surv_post_task"
showToken={showToken}
outcome="done"
/>
</UserVaneProvider>
</CopilotKit>
);
}
  • Signal: isLoading true to false (generation complete). The resolution signal owns the sample, never a model-triggered tool (useCopilotAction / useHumanInTheLoop render paths are out of scope for capture).
  • Once per turn: deduped by the latest assistant message id.
  • Suppressed when the turn ended via stopGeneration (abort) or a HITL interrupt (agent waiting on a human, not a finished answer).
  • sessionId = threadId: mint, bind, submit, and pending session scores share this key. Absent threadId means unbound capture (flagged).

No secret key (uv_sk_) is reachable from the client entry.

Closing the Langfuse loop (backend owns traces)

Section titled “Closing the Langfuse loop (backend owns traces)”

UserVane does not trace the CopilotKit agent. Your backend agent framework must set Langfuse sessionId = threadId on its own traces so the uservane.satisfaction session score lands on the same session. Then run @uservane/langfuse-push (or your own pending-scores consumer).

  • Peer: @copilotkit/react-core >= 1.63
  • Older versions had a threadId-not-forwarded-to-backend bug (#2624). Pin and verify >= 1.63.
  • Model-triggered feedback tools (useCopilotAction / useHumanInTheLoop capture paths).
  • In-transcript v2 UI slots as a package-import contract for capture. Not required: place UserVaneCopilotFeedback next to the chat.
  • A Langfuse trace bridge for CopilotKit (backend owns tracing).
  • Observation-level auto-read (deferred).

TypeDoc client · TypeDoc server · Agent quickstart