88a1dab810
---ci--- phase: 7 milestone: v0.2 status: complete requirements: covered: [REQ-2-001, REQ-2-002, REQ-2-003, REQ-2-004, REQ-2-005, REQ-2-006, REQ-2-007, REQ-2-008, REQ-2-009, REQ-2-010, REQ-2-011, REQ-2-012] partial: [] ---/ci--- Milestone v0.2 (ai-tutor-architecture) merged to main. Escalation record (audit remediation, durable): P1 executor delegation failed twice (empty subagent results, zero files created); auto-resolved at full autonomy to inline execution with identical plan fidelity (commit 3271373, reflog-only after phase branch squash-delete).
60 lines
2.2 KiB
TypeScript
60 lines
2.2 KiB
TypeScript
'use client';
|
|
|
|
import { AgentStreamPanel } from './agent-stream-panel';
|
|
import { ShieldCheck } from 'lucide-react';
|
|
|
|
interface IntegritySignal {
|
|
signal_type: string;
|
|
severity: 'low' | 'medium' | 'high';
|
|
note: string;
|
|
}
|
|
|
|
interface ProctorAssessment {
|
|
scenario_id: string;
|
|
signals: IntegritySignal[];
|
|
intervention: string;
|
|
summary: string;
|
|
}
|
|
|
|
const SEVERITY_STYLES: Record<string, string> = {
|
|
low: 'bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-300 dark:border-emerald-800',
|
|
medium: 'bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-900/30 dark:text-amber-300 dark:border-amber-800',
|
|
high: 'bg-red-50 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-300 dark:border-red-800',
|
|
};
|
|
|
|
/**
|
|
* Proctor integrity banner — supportive, coaching-shaped (never punitive).
|
|
*/
|
|
export function ProctorBanner({ scenarioId }: { scenarioId: string }) {
|
|
return (
|
|
<AgentStreamPanel
|
|
title="Proctor — integrity support"
|
|
endpoint="/v1/proctor/signals"
|
|
body={{ scenario_id: scenarioId }}
|
|
emptyHint="Run the Proctor to review this session's integrity signals."
|
|
renderJson={(data) => {
|
|
const assessment = data as unknown as ProctorAssessment;
|
|
return (
|
|
<div className="flex flex-col gap-3">
|
|
<p className="text-xs text-slate-600 dark:text-slate-300">{assessment.summary}</p>
|
|
<div className="flex flex-wrap gap-1.5">
|
|
{assessment.signals.map((s, i) => (
|
|
<span
|
|
key={i}
|
|
className={`inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs ${SEVERITY_STYLES[s.severity] ?? SEVERITY_STYLES.low}`}
|
|
title={s.note}
|
|
>
|
|
<ShieldCheck className="h-3 w-3" />
|
|
{s.signal_type} · {s.severity}
|
|
</span>
|
|
))}
|
|
</div>
|
|
<p className="rounded-md bg-primary-50 px-3 py-2 text-xs text-primary-800 dark:bg-primary-900/30 dark:text-primary-200">
|
|
<strong>Suggested next step:</strong> {assessment.intervention}
|
|
</p>
|
|
</div>
|
|
);
|
|
}}
|
|
/>
|
|
);
|
|
} |