diff --git a/ui/src/app/workflow/[workflowId]/run/[runId]/page.tsx b/ui/src/app/workflow/[workflowId]/run/[runId]/page.tsx
index 0ba6278e..e5ce4596 100644
--- a/ui/src/app/workflow/[workflowId]/run/[runId]/page.tsx
+++ b/ui/src/app/workflow/[workflowId]/run/[runId]/page.tsx
@@ -19,7 +19,10 @@ import posthog from 'posthog-js';
import { useEffect, useRef, useState } from 'react';
import WorkflowLayout from '@/app/workflow/WorkflowLayout';
-import { getWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGet } from '@/client/sdk.gen';
+import {
+ getWorkflowApiV1WorkflowFetchWorkflowIdGet,
+ getWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGet,
+} from '@/client/sdk.gen';
import { MediaPreviewButton, MediaPreviewDialog } from '@/components/MediaPreviewDialog';
import { OnboardingTooltip } from '@/components/onboarding/OnboardingTooltip';
import { Button } from '@/components/ui/button';
@@ -88,6 +91,35 @@ function MetricCard({ label, value }: { label: string; value: string }) {
);
}
+function CopyDebugIdButton({ label, value }: { label: string; value: string }) {
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = async () => {
+ await navigator.clipboard.writeText(value);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ };
+
+ return (
+
+ );
+}
+
function buildWaveformPeaks(audioBuffer: AudioBuffer) {
const channel = audioBuffer.getChannelData(0);
const samplesPerBar = Math.max(1, Math.floor(channel.length / WAVEFORM_BAR_COUNT));
@@ -569,6 +601,7 @@ export default function WorkflowRunPage() {
const [isLoading, setIsLoading] = useState(true);
const auth = useAuth();
const [workflowRun, setWorkflowRun] = useState(null);
+ const [workflowName, setWorkflowName] = useState(null);
const { hasSeenTooltip, markTooltipSeen } = useOnboarding();
const customizeButtonRef = useRef(null);
@@ -586,37 +619,52 @@ export default function WorkflowRunPage() {
if (!auth.isAuthenticated || auth.loading) return;
setIsLoading(true);
- const workflowId = params.workflowId;
- const runId = params.runId;
- const response = await getWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGet({
- path: {
- workflow_id: Number(workflowId),
- run_id: Number(runId),
- },
- });
- setIsLoading(false);
- const runData = {
- mode: response.data?.mode ?? '',
- is_completed: response.data?.is_completed ?? false,
- transcript_url: response.data?.transcript_url ?? null,
- recording_url: response.data?.recording_url ?? null,
- user_recording_url: response.data?.user_recording_url ?? null,
- bot_recording_url: response.data?.bot_recording_url ?? null,
- cost_info: response.data?.cost_info ?? null,
- initial_context: response.data?.initial_context as Record | null ?? null,
- gathered_context: response.data?.gathered_context as Record | null ?? null,
- logs: response.data?.logs as WorkflowRunLogs | null ?? null,
- annotations: response.data?.annotations as Record | null ?? null,
- };
- setWorkflowRun(runData);
- posthog.capture(PostHogEvent.WORKFLOW_RUN_DETAILS_VIEWED, {
- workflow_id: Number(workflowId),
- run_id: Number(runId),
- is_completed: runData.is_completed,
- has_recording: !!runData.recording_url,
- has_split_recordings: !!runData.user_recording_url && !!runData.bot_recording_url,
- has_transcript: !!runData.transcript_url,
- });
+ setWorkflowName(null);
+ const workflowId = Number(params.workflowId);
+ const runId = Number(params.runId);
+
+ try {
+ const [runResponse, workflowResponse] = await Promise.all([
+ getWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGet({
+ path: {
+ workflow_id: workflowId,
+ run_id: runId,
+ },
+ }),
+ getWorkflowApiV1WorkflowFetchWorkflowIdGet({
+ path: {
+ workflow_id: workflowId,
+ },
+ }),
+ ]);
+
+ setWorkflowName(workflowResponse.data?.name ?? null);
+ const runData = {
+ mode: runResponse.data?.mode ?? '',
+ is_completed: runResponse.data?.is_completed ?? false,
+ transcript_url: runResponse.data?.transcript_url ?? null,
+ recording_url: runResponse.data?.recording_url ?? null,
+ user_recording_url: runResponse.data?.user_recording_url ?? null,
+ bot_recording_url: runResponse.data?.bot_recording_url ?? null,
+ cost_info: runResponse.data?.cost_info ?? null,
+ initial_context: runResponse.data?.initial_context as Record | null ?? null,
+ gathered_context: runResponse.data?.gathered_context as Record | null ?? null,
+ logs: runResponse.data?.logs as WorkflowRunLogs | null ?? null,
+ annotations: runResponse.data?.annotations as Record | null ?? null,
+ };
+ setWorkflowRun(runData);
+ posthog.capture(PostHogEvent.WORKFLOW_RUN_DETAILS_VIEWED, {
+ workflow_id: workflowId,
+ workflow_name: workflowResponse.data?.name ?? null,
+ run_id: runId,
+ is_completed: runData.is_completed,
+ has_recording: !!runData.recording_url,
+ has_split_recordings: !!runData.user_recording_url && !!runData.bot_recording_url,
+ has_transcript: !!runData.transcript_url,
+ });
+ } finally {
+ setIsLoading(false);
+ }
};
fetchWorkflowRun();
}, [params.workflowId, params.runId, auth]);
@@ -627,6 +675,8 @@ export default function WorkflowRunPage() {
const userSplitRecordingUrl = workflowRun?.user_recording_url ?? null;
const botSplitRecordingUrl = workflowRun?.bot_recording_url ?? null;
const hasSplitTracks = Boolean(userSplitRecordingUrl && botSplitRecordingUrl);
+ const workflowId = String(params.workflowId);
+ const runId = String(params.runId);
if (isLoading) {
returnValue = (
@@ -656,22 +706,43 @@ export default function WorkflowRunPage() {
-
-
-
- {isTextChatRun ? 'Text Chat Session' : 'Agent Run Completed'}
-
-
- {isTextChatRun ? (
-
- ) : (
-
- )}
+
+
+ {workflowName && (
+
+
+
+
+
+
+ Agent
+
+
+ {workflowName}
+
+
+
+ )}
+
+
+
+
+
+
+ {isTextChatRun ? 'Text Chat Session' : 'Agent Run Completed'}
+
+
+ {isTextChatRun ? (
+
+ ) : (
+
+ )}
+
-