| import { useI18n } from '../i18n' |
| import type { OrchestrateResult } from '../api/ai' |
|
|
| type StepRole = 'advisory' | 'authoritative' | 'user' | 'notExecuted' | 'executed' |
|
|
| interface Step { |
| key: string |
| labelKey: |
| | 'trace.context' | 'trace.risk' | 'trace.policy' | 'trace.auth' |
| | 'trace.rail' | 'trace.userAction' | 'trace.paymentCore' |
| role: StepRole |
| summary: string |
| } |
|
|
| function roleBadge(role: StepRole): { key: string; tone: string } { |
| switch (role) { |
| case 'advisory': return { key: 'shadow.advisory', tone: 'tone-info' } |
| case 'authoritative': return { key: 'authoritative.badge', tone: 'tone-ok' } |
| case 'user': return { key: 'trace.userRequired', tone: 'tone-warn' } |
| case 'executed': return { key: 'trace.executed', tone: 'tone-ok' } |
| default: return { key: 'trace.notExecuted', tone: 'tone-bad' } |
| } |
| } |
|
|
| |
| export function AgentTrace({ result }: { result: OrchestrateResult }) { |
| const { t } = useI18n() |
| const executed = result.agent_state === 'initiated' |
| const steps: Step[] = [ |
| { key: 'context', labelKey: 'trace.context', role: 'advisory', |
| summary: result.risk ? '' : t('shadow.noChange') }, |
| { key: 'risk', labelKey: 'trace.risk', role: 'advisory', |
| summary: result.risk ? result.risk.band : 'model_unavailable' }, |
| { key: 'policy', labelKey: 'trace.policy', role: 'authoritative', summary: result.decision }, |
| { key: 'auth', labelKey: 'trace.auth', role: 'advisory', |
| summary: result.auth_recommendation?.recommended_method ?? '' }, |
| { key: 'rail', labelKey: 'trace.rail', role: 'advisory', |
| summary: (result.rails?.ranked ?? []).join(', ') }, |
| { key: 'user', labelKey: 'trace.userAction', role: 'user', |
| summary: result.decision === 'step_up' ? t('trace.userRequired') : '' }, |
| { key: 'core', labelKey: 'trace.paymentCore', |
| role: executed ? 'executed' : 'notExecuted', |
| summary: result.payment?.id ?? t('trace.notExecuted') }, |
| ] |
| return ( |
| <div className="agent-trace"> |
| <h3>{t('trace.title')}</h3> |
| <ol className="trace-list"> |
| {steps.map((s) => { |
| const b = roleBadge(s.role) |
| return ( |
| <li key={s.key} className={`trace-step role-${s.role}`}> |
| <div className="trace-head"> |
| <span className="trace-label">{t(s.labelKey)}</span> |
| <span className={`badge ${b.tone}`}>{t(b.key as never)}</span> |
| </div> |
| {s.summary && <span className="trace-summary">{s.summary}</span>} |
| </li> |
| ) |
| })} |
| </ol> |
| </div> |
| ) |
| } |
|
|