export type Conversation = { id: string; kind: "direct" | "channel"; title?: string; }; export type Thread = { id: string; conversationId: string; title: string; }; export type Message = { id: string; direction: "inbound" | "outbound"; conversation: Conversation; senderId: string; senderName?: string; text: string; timestamp: number; threadId?: string; threadTitle?: string; deleted?: boolean; editedAt?: number; reactions: Array<{ emoji: string; senderId: string }>; }; export type BusEvent = | { cursor: number; kind: "thread-created"; thread: Thread } | { cursor: number; kind: string; message?: Message; emoji?: string }; export type Snapshot = { conversations: Conversation[]; threads: Thread[]; messages: Message[]; events: BusEvent[]; }; export type ReportEnvelope = { report: null | { outputPath: string; markdown: string; generatedAt: string; }; }; export type SeedScenario = { id: string; title: string; surface: string; objective: string; successCriteria: string[]; docsRefs?: string[]; codeRefs?: string[]; }; export type Bootstrap = { baseUrl: string; latestReport: ReportEnvelope["report"]; controlUiUrl: string | null; controlUiEmbeddedUrl: string | null; kickoffTask: string; scenarios: SeedScenario[]; defaults: { conversationKind: "direct" | "channel"; conversationId: string; senderId: string; senderName: string; }; }; export type ScenarioStep = { name: string; status: "pass" | "fail" | "skip"; details?: string; }; export type ScenarioOutcome = { id: string; name: string; status: "pending" | "running" | "pass" | "fail" | "skip"; details?: string; steps?: ScenarioStep[]; startedAt?: string; finishedAt?: string; }; export type ScenarioRun = { kind: "suite" | "self-check"; status: "idle" | "running" | "completed"; startedAt?: string; finishedAt?: string; scenarios: ScenarioOutcome[]; counts: { total: number; pending: number; running: number; passed: number; failed: number; skipped: number; }; }; export type OutcomesEnvelope = { run: ScenarioRun | null; }; export type TabId = "debug" | "scenarios" | "report" | "events"; export type UiState = { bootstrap: Bootstrap | null; snapshot: Snapshot | null; latestReport: ReportEnvelope["report"]; scenarioRun: ScenarioRun | null; selectedConversationId: string | null; selectedThreadId: string | null; selectedScenarioId: string | null; activeTab: TabId; composer: { conversationKind: "direct" | "channel"; conversationId: string; senderId: string; senderName: string; text: string; }; busy: boolean; error: string | null; }; export function formatTime(timestamp: number) { return new Date(timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", }); } function formatIso(iso?: string) { if (!iso) { return "n/a"; } return new Date(iso).toLocaleString([], { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit", }); } function escapeHtml(text: string) { return text .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """); } export function deriveSelectedConversation(state: UiState): string | null { if (state.selectedConversationId) { return state.selectedConversationId; } return state.snapshot?.conversations[0]?.id ?? null; } export function deriveSelectedThread(state: UiState): string | null { return state.selectedThreadId ?? null; } export function filteredMessages(state: UiState) { const messages = state.snapshot?.messages ?? []; return messages.filter((message) => { if (state.selectedConversationId && message.conversation.id !== state.selectedConversationId) { return false; } if (state.selectedThreadId && message.threadId !== state.selectedThreadId) { return false; } return true; }); } function findScenarioOutcome(state: UiState, scenario: SeedScenario) { return ( state.scenarioRun?.scenarios.find((outcome) => outcome.id === scenario.id) ?? state.scenarioRun?.scenarios.find((outcome) => outcome.name === scenario.title) ?? null ); } function renderStatusChip(status: ScenarioOutcome["status"]) { const label = status === "pending" ? "seeded" : status === "pass" ? "pass" : status === "fail" ? "fail" : status; return `${escapeHtml(label)}`; } function renderRefs(refs: string[] | undefined, kind: "docs" | "code") { if (!refs?.length) { return `

No ${kind} refs attached.

`; } return refs.map((ref) => `${escapeHtml(ref)}`).join(""); } function renderScenarioBoard(state: UiState, scenarios: SeedScenario[]) { if (scenarios.length === 0) { return '

No repo-backed scenarios yet.

'; } return scenarios .map((scenario) => { const outcome = findScenarioOutcome(state, scenario); return ` `; }) .join(""); } function renderScenarioInspector(state: UiState, scenarios: SeedScenario[]) { const scenario = scenarios.find((candidate) => candidate.id === state.selectedScenarioId) ?? scenarios[0] ?? null; if (!scenario) { return '

Scenario outcome

No scenario selected.

'; } const outcome = findScenarioOutcome(state, scenario); return `

Scenario inspector

${escapeHtml(scenario.title)}

${renderStatusChip(outcome?.status ?? "pending")}

${escapeHtml(scenario.objective)}

Surface ${escapeHtml(scenario.surface)}
Started ${escapeHtml(formatIso(outcome?.startedAt))}
Finished ${escapeHtml(formatIso(outcome?.finishedAt))}
Run lane ${escapeHtml(state.scenarioRun?.kind ?? "seed only")}

Success criteria

Observed outcome

${ outcome ? `

${escapeHtml(outcome.details ?? "No summary details captured.")}

${ outcome.steps?.length ? outcome.steps .map( (step) => `
${escapeHtml(step.name)} ${renderStatusChip(step.status)}
${ step.details ? `
${escapeHtml(step.details)}
` : `

No extra details.

` }
`, ) .join("") : '

No step-level data captured yet.

' }
` : '

Not executed in the current run yet. Seed plan only.

' }

Docs refs

${renderRefs(scenario.docsRefs, "docs")}

Code refs

${renderRefs(scenario.codeRefs, "code")}
`; } function renderRunPanel(state: UiState) { const run = state.scenarioRun; if (!run) { return `

Run state

No structured scenario run yet. Seed plan loaded; outcomes arrive once a suite or self-check starts.

`; } return `

Live run

${escapeHtml(run.kind === "suite" ? "Scenario suite" : "Self-check")}

${escapeHtml(run.status)}
Total${run.counts.total}
Pass${run.counts.passed}
Fail${run.counts.failed}
Pending${run.counts.pending}

Started ${escapeHtml(formatIso(run.startedAt))} · Finished ${escapeHtml(formatIso(run.finishedAt))}

`; } function renderTabContent(state: UiState, scenarios: SeedScenario[]) { const selectedConversationId = deriveSelectedConversation(state); const selectedThreadId = deriveSelectedThread(state); const messages = filteredMessages({ ...state, selectedConversationId, selectedThreadId, }); const events = (state.snapshot?.events ?? []).slice(-40).toReversed(); const kickoffTask = state.bootstrap?.kickoffTask ?? ""; if (state.activeTab === "scenarios") { return `

Seed plan

Scenario catalog

Click any scenario for full criteria and evidence.
${escapeHtml(kickoffTask || "No kickoff task loaded.")}
${renderScenarioBoard(state, scenarios)}
`; } if (state.activeTab === "report") { return `

Protocol

Latest report

${escapeHtml(state.latestReport?.markdown ?? "Run the suite or self-check to capture a Markdown protocol report.")}
`; } if (state.activeTab === "events") { return `

Wire view

Event stream

Newest first.
${ events.length === 0 ? '

No events yet.

' : events .map((event) => { const tail = "thread" in event ? `${event.thread.conversationId}/${event.thread.id}` : event.message ? `${event.message.senderId}: ${event.message.text}` : ""; return `
${escapeHtml(event.kind)} #${event.cursor} ${escapeHtml(tail)}
`; }) .join("") }
`; } return `

Live lane

Transcript

${escapeHtml(selectedConversationId ?? "No conversation selected")} · ${escapeHtml(selectedThreadId ?? "root thread")}
${ messages.length === 0 ? '

No messages in this slice yet.

' : messages .map( (message) => `
${escapeHtml(message.senderName || message.senderId)} ${message.direction}

${escapeHtml(message.text)}

`, ) .join("") }

Manual probe

Inject inbound

Useful for one-off regression pokes.
`; } export function renderQaLabUi(state: UiState) { const selectedConversationId = deriveSelectedConversation(state); const selectedThreadId = deriveSelectedThread(state); const conversations = state.snapshot?.conversations ?? []; const threads = (state.snapshot?.threads ?? []).filter( (thread) => !selectedConversationId || thread.conversationId === selectedConversationId, ); const scenarios = state.bootstrap?.scenarios ?? []; const hasControlUi = Boolean(state.bootstrap?.controlUiEmbeddedUrl); const dashboardShellClass = hasControlUi ? "dashboard split-dashboard" : "dashboard"; const run = state.scenarioRun; return `
${ hasControlUi ? `

Agent control

Control UI

${ state.bootstrap?.controlUiUrl ? `Open full tab` : "" }
` : "" }

Private QA workspace

QA Lab Debugger

Editorial control-room view for seeded scenarios, live traffic, and protocol evidence.

${hasControlUi ? "Split view linked" : "QA-only view"} Scenarios ${scenarios.length} Conversation ${selectedConversationId ?? "none"} Thread ${selectedThreadId ?? "root"} ${ run ? `${escapeHtml(run.kind)} ${escapeHtml(run.status)} · ${run.counts.passed}/${run.counts.total} pass` : 'No structured run yet' } ${state.latestReport ? `Report ${escapeHtml(state.latestReport.outputPath)}` : 'No report yet'} ${state.error ? `${escapeHtml(state.error)}` : ""}
${renderTabContent(state, scenarios)}
`; }