dsh-llm-verifier 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/client.tsx CHANGED
@@ -3,7 +3,7 @@ import type { ModelProviderGroup, SettingsNamespaceView } from '@deepseek-ai/dsh
3
3
  import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
4
4
  import type {} from '@deepseek-ai/dsh-client-ui-conversation/client'
5
5
  import type {} from '@deepseek-ai/dsh-client-ui-slots'
6
- import { Button, IconDataOutline16, IconRefreshOutline16, Input } from '@deepseek-ai/dsh-client-ui-primitives'
6
+ import { Button, IconDataOutline16, IconRefreshOutline16, Input, StateDot, type StateDotState } from '@deepseek-ai/dsh-client-ui-primitives'
7
7
  import { Fragment, useEffect, useMemo, useRef, useState } from 'react'
8
8
  import {
9
9
  zh, en, dictionaries, toolLabels, tFormat, useLanguage, detectLanguage,
@@ -145,7 +145,7 @@ const sectionTitle: React.CSSProperties = { display: 'flex', alignItems: 'center
145
145
  const sectionHeadingStyle: React.CSSProperties = { fontSize: 14, fontWeight: 500, lineHeight: '22px' }
146
146
  const sectionSummaryStyle: React.CSSProperties = { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', minWidth: 0, flex: '1 1 auto' }
147
147
  const badgeStyle: React.CSSProperties = { flex: '0 0 auto', padding: '0 6px', borderRadius: 999, fontSize: 11, lineHeight: '16px', color: 'var(--dsw-alias-label-tertiary)', border: '1px solid var(--dsw-alias-border-l2)' }
148
- const linkButton: React.CSSProperties = { border: 0, background: 'transparent', padding: 0, font: 'inherit', fontSize: 12, lineHeight: '18px', cursor: 'pointer', color: 'var(--dsw-alias-brand-primary, #4f8cff)' }
148
+ const linkButton: React.CSSProperties = { border: 0, background: 'transparent', padding: 0, font: 'inherit', fontSize: 12, lineHeight: '18px', cursor: 'pointer', color: 'var(--dsw-alias-link)' }
149
149
  const row: React.CSSProperties = { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '6px 24px', minHeight: 56, padding: '10px 0', borderBottom: '1px solid var(--dsw-alias-border-l2)' }
150
150
  const labelCell: React.CSSProperties = { flex: '1 1 220px', minWidth: 0 }
151
151
  const controlCell: React.CSSProperties = { flex: '0 1 268px', minWidth: 170, display: 'flex', justifyContent: 'flex-end' }
@@ -153,16 +153,33 @@ const fieldTitle: React.CSSProperties = { fontSize: 14, fontWeight: 400, lineHei
153
153
  const fieldHelp: React.CSSProperties = { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)', marginTop: 2 }
154
154
  const fullLine: React.CSSProperties = { flex: '1 1 100%', margin: '0 0 2px', fontSize: 12, lineHeight: '18px' }
155
155
  const unitStyle: React.CSSProperties = { flex: '0 0 auto', fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' }
156
- const sliderStyle: React.CSSProperties = { width: '100%', margin: 0, accentColor: 'var(--dsw-alias-brand-primary, #4f8cff)' }
156
+ const sliderStyle: React.CSSProperties = { width: '100%', margin: 0, accentColor: 'var(--dsw-alias-state-business-primary)' }
157
157
  const toolbarStyle: React.CSSProperties = { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8, padding: '10px 0', borderTop: '1px solid var(--dsw-alias-border-l2)' }
158
158
  const summaryLineStyle: React.CSSProperties = { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' }
159
- const stickyBar: React.CSSProperties = { position: 'sticky', bottom: 0, zIndex: 5, display: 'flex', flexDirection: 'column', gap: 6, padding: '10px 0 12px', borderTop: '1px solid var(--dsw-alias-border-l2)', background: 'var(--dsw-alias-bg-module, #171925)', boxShadow: '0 -10px 24px rgba(0,0,0,.18)' }
159
+ const stickyBar: React.CSSProperties = { position: 'sticky', bottom: 0, zIndex: 5, display: 'flex', flexDirection: 'column', gap: 6, padding: '10px 0 12px', borderTop: '1px solid var(--dsw-alias-border-l2)', background: 'var(--dsw-alias-bg-module-platform)', boxShadow: '0 -10px 24px var(--dsw-alias-bg-mask-2)' }
160
160
  const statusStyle: React.CSSProperties = { fontSize: 12, lineHeight: '18px', display: 'inline-flex', alignItems: 'center', gap: 8, minWidth: 0, flexWrap: 'wrap' }
161
- const selectStyle: React.CSSProperties = { boxSizing: 'border-box', width: '100%', height: 36, padding: '0 34px 0 12px', borderRadius: 8, color: 'var(--dsw-alias-label-primary)', background: 'var(--dsw-alias-bg-input)', border: '1px solid var(--dsw-alias-border-l2)', font: 'inherit', fontSize: 14, lineHeight: '22px', outline: 'none' }
162
- const toggleStyle = (enabled: boolean): React.CSSProperties => ({ position: 'relative', flex: '0 0 auto', width: 40, height: 22, padding: 0, border: 0, borderRadius: 999, cursor: 'pointer', transition: 'background .15s ease', background: enabled ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-bg-input)' })
163
- const toggleThumbStyle = (enabled: boolean): React.CSSProperties => ({ position: 'absolute', top: 3, left: enabled ? 21 : 3, width: 16, height: 16, borderRadius: '50%', background: 'var(--dsw-static-neutral-00, #fff)', boxShadow: '0 1px 3px rgba(0,0,0,.28)', transition: 'left .15s ease' })
164
- const dashboardCard: React.CSSProperties = { border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.13))', background: 'color-mix(in srgb, var(--dsw-alias-bg-module, #171925) 88%, transparent)', borderRadius: 16, boxShadow: '0 12px 36px rgba(0,0,0,.12)' }
165
- const muted: React.CSSProperties = { color: 'var(--dsw-text-secondary)', fontSize: 12 }
161
+ const selectStyle: React.CSSProperties = { boxSizing: 'border-box', width: '100%', height: 36, padding: '0 34px 0 12px', borderRadius: 8, color: 'var(--dsw-alias-label-primary)', background: 'var(--dsw-specific-input-major)', border: '1px solid var(--dsw-alias-border-l2)', font: 'inherit', fontSize: 14, lineHeight: '22px', outline: 'none' }
162
+ const toggleStyle = (enabled: boolean): React.CSSProperties => ({ position: 'relative', flex: '0 0 auto', width: 40, height: 22, padding: 0, border: 0, borderRadius: 999, cursor: 'pointer', transition: 'background .15s ease', background: enabled ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-specific-input-major)' })
163
+ const toggleThumbStyle = (enabled: boolean): React.CSSProperties => ({ position: 'absolute', top: 3, left: enabled ? 21 : 3, width: 16, height: 16, borderRadius: '50%', background: 'var(--dsw-static-neutral-00)', boxShadow: '0 1px 3px rgba(0,0,0,.28)', transition: 'left .15s ease' })
164
+ const dashboardCard: React.CSSProperties = { border: '1px solid var(--dsw-alias-border-l2)', background: 'var(--dsw-alias-bg-module-platform)', borderRadius: 16, boxShadow: '0 12px 36px var(--dsw-alias-bg-mask-2)' }
165
+ const muted: React.CSSProperties = { color: 'var(--dsw-alias-label-tertiary)', fontSize: 12 }
166
+ /** Chart palette rides the state aliases so bars and line stay legible in both themes. */
167
+ const chartBarColor = 'var(--dsw-alias-state-business-primary)'
168
+ const chartLineColor = 'var(--dsw-alias-state-warn-primary)'
169
+ /** Status chip tones; each one mixes its own state token, so a fill, a ring and its text move together. */
170
+ type StatusTone = 'pass' | 'warn' | 'error' | 'neutral'
171
+ function toneChip(tone: StatusTone): React.CSSProperties {
172
+ const token = tone === 'error' ? 'var(--dsw-alias-state-error-primary)'
173
+ : tone === 'warn' ? 'var(--dsw-alias-state-warn-primary)'
174
+ : tone === 'pass' ? 'var(--dsw-alias-state-success-primary)'
175
+ : 'var(--dsw-alias-state-business-primary)'
176
+ return {
177
+ background: `color-mix(in srgb, ${token} 14%, transparent)`,
178
+ border: `1px solid color-mix(in srgb, ${token} 32%, transparent)`,
179
+ // Amber's 500 step is a fill; its 600 step is the readable text rung.
180
+ color: tone === 'warn' ? 'var(--dsw-alias-state-warn-label)' : token,
181
+ }
182
+ }
166
183
  const toolColors: Record<string, string> = { verifier_route_classify: '#d97706', verifier_compare: '#4f8cff', verifier_select: '#8b6df6', verifier_track: '#2fc5c9', verifier_best_of_n: '#e2569b', verifier_current_session: '#f5a524' }
167
184
 
168
185
  function record(value: unknown): Record<string, unknown> { return typeof value === 'object' && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : {} }
@@ -478,8 +495,8 @@ export function VerifierSettings({ remote }: VerifierSettingsProps) {
478
495
  const first = preset[0]
479
496
  if (!first) return null
480
497
  return <details style={{ margin: '2px 0 6px 4px', fontSize: 12 }}>
481
- <summary style={{ cursor: 'pointer', color: 'var(--dsw-text-secondary)' }}>{tFormat(t['field.criteriaPreset.previewSummary'], { count: preset.length })}</summary>
482
- <pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', maxHeight: 240, overflow: 'auto', background: 'var(--dsw-surface-sunken)', border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))', borderRadius: 8, padding: 10, marginTop: 8 }}>{buildPairwisePrompt(t['field.criteriaPreset.sampleTask'], t['field.criteriaPreset.sampleA'], t['field.criteriaPreset.sampleB'], first, DEFAULT_GROUND_TRUTH_NOTE)}</pre>
498
+ <summary style={{ cursor: 'pointer', color: 'var(--dsw-alias-label-secondary)' }}>{tFormat(t['field.criteriaPreset.previewSummary'], { count: preset.length })}</summary>
499
+ <pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', maxHeight: 240, overflow: 'auto', background: 'var(--dsw-alias-markdown-code-block)', border: '1px solid var(--dsw-alias-border-l1)', borderRadius: 8, padding: 10, marginTop: 8 }}>{buildPairwisePrompt(t['field.criteriaPreset.sampleTask'], t['field.criteriaPreset.sampleA'], t['field.criteriaPreset.sampleB'], first, DEFAULT_GROUND_TRUTH_NOTE)}</pre>
483
500
  </details>
484
501
  })() : null
485
502
  return <Fragment key={String(field.key)}>
@@ -549,7 +566,7 @@ export function VerifierSettings({ remote }: VerifierSettingsProps) {
549
566
  </section>
550
567
  })}
551
568
 
552
- {loaded.failures.length > 0 && <div style={{ padding: '10px 12px', borderRadius: 8, background: 'var(--dsw-alias-state-warn-bg)', color: 'var(--dsw-alias-state-warn-label)', fontSize: 12, lineHeight: '18px' }}>
569
+ {loaded.failures.length > 0 && <div style={{ padding: '10px 12px', borderRadius: 8, background: 'var(--dsw-alias-state-warn-tertiary)', color: 'var(--dsw-alias-state-warn-label)', fontSize: 12, lineHeight: '18px' }}>
553
570
  <div style={{ fontWeight: 500, marginBottom: 3 }}>{t['settings.catalogFailures']}</div>
554
571
  {loaded.failures.map(failure => <div key={failure}>{failure}</div>)}
555
572
  </div>}
@@ -585,11 +602,11 @@ function TrendChart({ daily, days, lang }:{daily:DailyStatistics[];days:number;l
585
602
  const max=Math.max(1,...rows.flatMap(row=>[row.invocations,row.calls]));const x=(index:number)=>pad.l+(rows.length<=1?innerW/2:index*innerW/(rows.length-1));const y=(value:number)=>pad.t+innerH-value/max*innerH
586
603
  const points=rows.map((row,index)=>`${x(index)},${y(row.calls)}`).join(' ');const step=rows.length>16?Math.ceil(rows.length/7):Math.max(1,Math.ceil(rows.length/7));const barWidth=Math.max(3,Math.min(18,innerW/Math.max(rows.length,1)*.55))
587
604
  return <div style={{width:'100%',overflowX:'auto'}}><svg viewBox={`0 0 ${width} ${height}`} style={{display:'block',width:'100%',minWidth:620,height:'auto'}} aria-label={t['chart.ariaLabel']}>
588
- {[0,.25,.5,.75,1].map(ratio=><g key={ratio}><line x1={pad.l} x2={width-pad.r} y1={pad.t+innerH*ratio} y2={pad.t+innerH*ratio} stroke="rgba(148,163,184,.16)"/><text x={pad.l-8} y={pad.t+innerH*ratio+4} textAnchor="end" fontSize="10" fill="var(--dsw-text-secondary)">{Math.round(max*(1-ratio))}</text></g>)}
589
- {rows.map((row,index)=><rect key={row.date} x={x(index)-barWidth/2} y={y(row.invocations)} width={barWidth} height={pad.t+innerH-y(row.invocations)} rx="2" fill="#4f8cff" opacity=".82"/>)}
590
- <polyline points={points} fill="none" stroke="#5ed7e8" strokeWidth="2.4" strokeLinejoin="round" strokeLinecap="round"/>
591
- {rows.map((row,index)=>index%step===0||index===rows.length-1?<text key={row.date} x={x(index)} y={height-14} textAnchor="middle" fontSize="10" fill="var(--dsw-text-secondary)">{Number(row.date.slice(5,7))}/{Number(row.date.slice(8,10))}</text>:null)}
592
- </svg><div style={{display:'flex',justifyContent:'center',gap:18,...muted}}><span><i style={{display:'inline-block',width:8,height:8,borderRadius:2,background:'#4f8cff',marginRight:6}}/>{t['chart.legendToolCalls']}</span><span><i style={{display:'inline-block',width:14,height:2,background:'#5ed7e8',marginRight:6,verticalAlign:'middle'}}/>{t['chart.legendModelCalls']}</span></div></div>
605
+ {[0,.25,.5,.75,1].map(ratio=><g key={ratio}><line x1={pad.l} x2={width-pad.r} y1={pad.t+innerH*ratio} y2={pad.t+innerH*ratio} stroke="var(--dsw-alias-border-l2)"/><text x={pad.l-8} y={pad.t+innerH*ratio+4} textAnchor="end" fontSize="10" fill="var(--dsw-alias-label-secondary)">{Math.round(max*(1-ratio))}</text></g>)}
606
+ {rows.map((row,index)=><rect key={row.date} x={x(index)-barWidth/2} y={y(row.invocations)} width={barWidth} height={pad.t+innerH-y(row.invocations)} rx="2" fill={chartBarColor} opacity=".82"/>)}
607
+ <polyline points={points} fill="none" stroke={chartLineColor} strokeWidth="2.4" strokeLinejoin="round" strokeLinecap="round"/>
608
+ {rows.map((row,index)=>index%step===0||index===rows.length-1?<text key={row.date} x={x(index)} y={height-14} textAnchor="middle" fontSize="10" fill="var(--dsw-alias-label-secondary)">{Number(row.date.slice(5,7))}/{Number(row.date.slice(8,10))}</text>:null)}
609
+ </svg><div style={{display:'flex',justifyContent:'center',gap:18,...muted}}><span><i style={{display:'inline-block',width:8,height:8,borderRadius:2,background:chartBarColor,marginRight:6}}/>{t['chart.legendToolCalls']}</span><span><i style={{display:'inline-block',width:14,height:2,background:chartLineColor,marginRight:6,verticalAlign:'middle'}}/>{t['chart.legendModelCalls']}</span></div></div>
593
610
  }
594
611
 
595
612
  export function StatisticsPage({ sessionId, rpc, isGlobal, blankComposerSeat }: StatisticsPageProps) {
@@ -764,12 +781,12 @@ export function StatisticsPage({ sessionId, rpc, isGlobal, blankComposerSeat }:
764
781
  }, [days, sessionOnly, sessionId, refresh, rpc, t, queryKey])
765
782
 
766
783
  const totals = data?.totals
767
- return <main style={{ height: '100%', overflow: 'auto', boxSizing: 'border-box', padding: '22px clamp(16px, 3vw, 38px) 48px', color: 'var(--dsw-text-primary)', background: 'radial-gradient(circle at 10% 0%, rgba(115,77,255,.09), transparent 32%), radial-gradient(circle at 100% 8%, rgba(47,197,201,.07), transparent 28%)' }}>
784
+ return <main style={{ height: '100%', overflow: 'auto', boxSizing: 'border-box', padding: '22px clamp(16px, 3vw, 38px) 48px', color: 'var(--dsw-alias-label-primary)', background: 'radial-gradient(circle at 10% 0%, rgba(115,77,255,.09), transparent 32%), radial-gradient(circle at 100% 8%, rgba(47,197,201,.07), transparent 28%)' }}>
768
785
  <div style={{ maxWidth: 1180, margin: '0 auto', display: 'flex', flexDirection: 'column', gap: 16 }}>
769
786
  <header style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16, flexWrap: 'wrap' }}>
770
787
  <div>
771
788
  <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
772
- <span style={{ display: 'grid', placeItems: 'center', width: 34, height: 34, borderRadius: 10, background: 'rgba(79,140,255,.14)', color: '#6da0ff' }}>
789
+ <span style={{ display: 'grid', placeItems: 'center', width: 34, height: 34, borderRadius: 10, background: 'color-mix(in srgb, var(--dsw-alias-state-business-primary) 14%, transparent)', color: 'var(--dsw-alias-state-business-primary)' }}>
773
790
  <IconDataOutline16 size={18} />
774
791
  </span>
775
792
  <h2 style={{ margin: 0, fontSize: 23 }}>{isGlobal ? t['global.panelTitle'] : t['stats.pageTitle']}</h2>
@@ -777,24 +794,24 @@ export function StatisticsPage({ sessionId, rpc, isGlobal, blankComposerSeat }:
777
794
  <p style={{ margin: '7px 0 0 44px', ...muted }}>{isGlobal ? t['global.panelIntro'] : tFormat(t['stats.updatedAt'], { time: data ? dateTime(data.generatedAt, lang) : '--' })}</p>
778
795
  </div>
779
796
  <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
780
- <div style={{ display: 'flex', padding: 3, borderRadius: 10, background: 'var(--dsw-surface-sunken)', border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))' }}>
781
- {[7, 30, 90].map(value => <button key={value} aria-pressed={days === value} onClick={() => setDays(value)} style={{ border: 0, borderRadius: 7, padding: '6px 10px', cursor: 'pointer', color: days === value ? '#fff' : 'var(--dsw-text-secondary)', background: days === value ? '#3f68d8' : 'transparent' }}>{tFormat(t['stats.daysUnit'], { days: value })}</button>)}
797
+ <div style={{ display: 'flex', padding: 3, borderRadius: 10, background: 'var(--dsw-alias-bg-multi-select)', border: '1px solid var(--dsw-alias-border-l2)' }}>
798
+ {[7, 30, 90].map(value => <button key={value} aria-pressed={days === value} onClick={() => setDays(value)} style={{ border: 0, borderRadius: 7, padding: '6px 10px', cursor: 'pointer', color: days === value ? 'var(--dsw-alias-state-business-primary)' : 'var(--dsw-alias-label-secondary)', background: days === value ? 'color-mix(in srgb, var(--dsw-alias-state-business-primary) 16%, transparent)' : 'transparent' }}>{tFormat(t['stats.daysUnit'], { days: value })}</button>)}
782
799
  </div>
783
- {Boolean(sessionId) && <button aria-pressed={sessionOnly} onClick={() => setSessionOnly(value => !value)} style={{ border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.15))', borderRadius: 9, padding: '7px 11px', cursor: 'pointer', color: 'var(--dsw-text-primary)', background: sessionOnly ? 'rgba(79,140,255,.18)' : 'var(--dsw-surface-sunken)' }}>{sessionOnly ? t['stats.currentSession'] : t['stats.allSessions']}</button>}
784
- <button type="button" disabled={probe?.busy === true} onClick={() => void runProbe()} style={{ border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.15))', borderRadius: 9, padding: '7px 11px', cursor: 'pointer', color: 'var(--dsw-text-primary)', background: 'var(--dsw-surface-sunken)' }}>{probe?.busy === true ? t['probe.running'] : t['probe.button']}</button>
785
- <button title={t['stats.refresh']} onClick={() => setRefresh(value => value + 1)} style={{ display: 'grid', placeItems: 'center', width: 34, height: 34, borderRadius: 9, border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.15))', color: 'var(--dsw-text-primary)', background: 'var(--dsw-surface-sunken)', cursor: 'pointer' }}><IconRefreshOutline16 size={16} /></button>
800
+ {Boolean(sessionId) && <button aria-pressed={sessionOnly} onClick={() => setSessionOnly(value => !value)} style={{ border: '1px solid var(--dsw-alias-border-l3)', borderRadius: 9, padding: '7px 11px', cursor: 'pointer', color: sessionOnly ? 'var(--dsw-alias-state-business-primary)' : 'var(--dsw-alias-label-primary)', background: sessionOnly ? 'color-mix(in srgb, var(--dsw-alias-state-business-primary) 14%, transparent)' : 'var(--dsw-alias-interactive-bg-hover)' }}>{sessionOnly ? t['stats.currentSession'] : t['stats.allSessions']}</button>}
801
+ <button type="button" disabled={probe?.busy === true} onClick={() => void runProbe()} style={{ border: '1px solid var(--dsw-alias-border-l3)', borderRadius: 9, padding: '7px 11px', cursor: 'pointer', color: 'var(--dsw-alias-label-primary)', background: 'var(--dsw-alias-interactive-bg-hover)' }}>{probe?.busy === true ? t['probe.running'] : t['probe.button']}</button>
802
+ <button title={t['stats.refresh']} onClick={() => setRefresh(value => value + 1)} style={{ display: 'grid', placeItems: 'center', width: 34, height: 34, borderRadius: 9, border: '1px solid var(--dsw-alias-border-l3)', color: 'var(--dsw-alias-label-primary)', background: 'var(--dsw-alias-interactive-bg-hover)', cursor: 'pointer' }}><IconRefreshOutline16 size={16} /></button>
786
803
  </div>
787
804
  </header>
788
- {error && <div style={{ ...dashboardCard, padding: 18, borderColor: 'var(--dsw-danger, #e85858)', color: 'var(--dsw-danger, #e85858)' }}>{error}<div style={{ ...muted, marginTop: 6 }}>{t['stats.hostRestartHint']}</div></div>}
805
+ {error && <div style={{ ...dashboardCard, padding: 18, borderColor: 'var(--dsw-alias-state-error-primary)', color: 'var(--dsw-alias-state-error-primary)' }}>{error}<div style={{ ...muted, marginTop: 6 }}>{t['stats.hostRestartHint']}</div></div>}
789
806
  {probe !== null && <section style={{ ...dashboardCard, padding: '16px 18px' }}>
790
807
  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}><strong>{t['probe.title']}</strong><span style={muted}>{t['probe.note']}</span></div>
791
- {probe.error !== undefined && <div style={{ marginTop: 10, fontSize: 12, color: '#e76565' }}>{probe.error}</div>}
808
+ {probe.error !== undefined && <div style={{ marginTop: 10, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' }}>{probe.error}</div>}
792
809
  {probe.value !== undefined && <>
793
810
  <div style={{ ...muted, marginTop: 8 }}>{tFormat(t['probe.rubric'], { source: probe.value.rubric.source, count: probe.value.rubric.count, file: probe.value.rubric.file ?? '' })}</div>
794
- {probe.value.rubric.error !== undefined && <div style={{ marginTop: 6, fontSize: 12, color: '#e3bd63' }}>{tFormat(t['probe.rubricFallback'], { error: probe.value.rubric.error })}</div>}
811
+ {probe.value.rubric.error !== undefined && <div style={{ marginTop: 6, fontSize: 12, color: 'var(--dsw-alias-state-warn-label)' }}>{tFormat(t['probe.rubricFallback'], { error: probe.value.rubric.error })}</div>}
795
812
  <div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 8 }}>
796
- {probe.value.judges.map(judge => <div key={judge.label + judge.model} style={{ display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center', fontSize: 12, padding: '9px 11px', borderRadius: 9, background: 'var(--dsw-surface-sunken)' }}>
797
- <span style={{ color: judge.ok ? '#77d49b' : '#e76565', fontWeight: 600 }}>{judge.ok ? 'OK' : t['probe.failed']}</span>
813
+ {probe.value.judges.map(judge => <div key={judge.label + judge.model} style={{ display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center', fontSize: 12, padding: '9px 11px', borderRadius: 9, background: 'var(--dsw-alias-interactive-bg-hover)' }}>
814
+ <span style={{ color: judge.ok ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)', fontWeight: 600 }}>{judge.ok ? 'OK' : t['probe.failed']}</span>
798
815
  <strong>{judge.label}</strong>
799
816
  <span style={muted}>{judge.provider}/{judge.model}</span>
800
817
  {judge.ok ? <>
@@ -802,7 +819,7 @@ export function StatisticsPage({ sessionId, rpc, isGlobal, blankComposerSeat }:
802
819
  {judge.scoreA !== undefined && judge.scoreB !== undefined && <span style={muted}>{tFormat(t['probe.scores'], { a: formatPercentage(judge.scoreA), b: formatPercentage(judge.scoreB) })}</span>}
803
820
  <span style={muted}>{tFormat(t['probe.latency'], { ms: String(judge.latencyMs) })}</span>
804
821
  {judge.calls !== undefined && <span style={muted}>{tFormat(t['probe.calls'], { calls: String(judge.calls) })}</span>}
805
- </> : <span style={{ color: '#e76565' }}>{judge.error}</span>}
822
+ </> : <span style={{ color: 'var(--dsw-alias-state-error-primary)' }}>{judge.error}</span>}
806
823
  </div>)}
807
824
  </div>
808
825
  </>}
@@ -815,46 +832,46 @@ export function StatisticsPage({ sessionId, rpc, isGlobal, blankComposerSeat }:
815
832
  <div style={muted}>{tFormat(t['stats.callsSummary'], { invocations: compact(totals?.invocations ?? 0, lang), calls: compact(totals?.calls ?? 0, lang) })}</div>
816
833
  </div>
817
834
  <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: '9px 16px', fontSize: 13 }}>
818
- <span style={muted}>{t['stats.successCalls']}</span><strong>{compact(totals?.successes ?? 0, lang)} <small style={{ color: '#77d49b' }}>▲ {((totals?.successRate ?? 0) * 100).toFixed(1)}%</small></strong>
835
+ <span style={muted}>{t['stats.successCalls']}</span><strong>{compact(totals?.successes ?? 0, lang)} <small style={{ color: 'var(--dsw-alias-state-success-primary)' }}>▲ {((totals?.successRate ?? 0) * 100).toFixed(1)}%</small></strong>
819
836
  <span style={muted}>{t['stats.failedCalls']}</span><strong>{compact(totals?.failures ?? 0, lang)}</strong>
820
837
  <span style={muted}>{t['stats.avgDuration']}</span><strong>{duration(totals?.averageDurationMs ?? 0)}</strong>
821
838
  </div>
822
839
  </section>
823
840
  <section style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(180px,1fr))', gap: 12 }}>
824
- <Metric label={t['metric.cacheHitRate']} value={((totals?.cacheHitRate ?? 0) * 100).toFixed(1) + '%'} note={tFormat(t['metric.cacheHitNote'], { hits: compact(totals?.cacheHits ?? 0, lang), total: compact((totals?.cacheHits ?? 0) + (totals?.cacheMisses ?? 0), lang) })} accent="#b7dd64" />
841
+ <Metric label={t['metric.cacheHitRate']} value={((totals?.cacheHitRate ?? 0) * 100).toFixed(1) + '%'} note={tFormat(t['metric.cacheHitNote'], { hits: compact(totals?.cacheHits ?? 0, lang), total: compact((totals?.cacheHits ?? 0) + (totals?.cacheMisses ?? 0), lang) })} accent="var(--dsw-alias-state-success-primary)" />
825
842
  <Metric label={t['metric.prefixCacheHitRate']} value={((totals?.prefixCacheHitRate ?? 0) * 100).toFixed(1) + '%'} note={tFormat(t['metric.prefixCacheHitNote'], { cached: compact(totals?.cachedInputTokens ?? 0, lang), input: compact((totals?.inputTokens ?? 0) + (totals?.cachedInputTokens ?? 0), lang) })} />
826
843
  <Metric label={t['metric.tokens']} value={compact(totals?.tokens ?? 0, lang)} note={tFormat(t['metric.tokensNote'], { input: compact((totals?.inputTokens ?? 0) + (totals?.cachedInputTokens ?? 0), lang), output: compact(totals?.outputTokens ?? 0, lang) })} />
827
844
  <Metric label={t['metric.avgModelCalls']} value={(totals?.invocations ?? 0) > 0 ? ((totals?.calls ?? 0) / (totals?.invocations ?? 1)).toFixed(1) : '0'} note={tFormat(t['metric.avgModelCallsNote'], { attempts: compact(totals?.attempts ?? 0, lang), retries: compact(totals?.retries ?? 0, lang) })} />
828
845
  <Metric label={t['metric.scoringMode']} value={compact(totals?.topLogprobScores ?? 0, lang)} note={tFormat(t['metric.scoringModeNote'], { explicit: compact(totals?.explicitTagScores ?? 0, lang) })} />
829
846
  </section>
830
847
  <section style={{ ...dashboardCard, padding: '18px 20px 16px' }}><div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }}><strong>{t['chart.title']}</strong><span style={muted}>{sessionOnly ? t['stats.currentSession'] : t['stats.allSessions']}</span></div><TrendChart daily={data?.daily ?? []} days={days} lang={lang} /></section>
831
- <section style={{ ...dashboardCard, padding: '18px 18px 8px', overflow: 'hidden' }}><div style={{ display: 'flex', justifyContent: 'space-between', margin: '0 2px 12px' }}><strong>{t['table.title']}</strong><span style={muted}>{tFormat(t['table.toolCount'], { count: (data?.tools ?? []).length })}</span></div><div style={{ overflowX: 'auto' }}><table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13, minWidth: 760 }}><thead><tr style={{ textAlign: 'left', color: 'var(--dsw-text-secondary)', background: 'var(--dsw-surface-sunken)' }}>{[t['table.colTool'], t['table.colInvocations'], t['table.colSuccessRate'], t['table.colAvgDuration'], t['table.colModelCalls'], t['table.colTokens'], t['table.colCacheHits'], t['table.colEstimatedCost']].map(value => <th key={value} style={{ padding: '10px 12px', fontWeight: 500 }}>{value}</th>)}</tr></thead><tbody>{(data?.tools ?? []).map(tool => <tr key={tool.toolName} style={{ borderTop: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.1))' }}><td style={{ padding: '13px 12px' }}><span style={{ display: 'inline-block', width: 8, height: 8, borderRadius: '50%', background: toolColors[tool.toolName] ?? '#8691a8', marginRight: 8 }} /><strong>{labels[tool.toolName] ?? tool.toolName}</strong><div style={{ ...muted, margin: '3px 0 0 16px' }}>{tool.toolName}</div></td><td style={{ padding: '13px 12px' }}>{compact(tool.invocations, lang)}</td><td style={{ padding: '13px 12px', color: tool.successRate >= .9 ? '#77d49b' : tool.successRate >= .7 ? '#e3bd63' : '#ed7777' }}>{(tool.successRate * 100).toFixed(1)}%</td><td style={{ padding: '13px 12px' }}>{duration(tool.averageDurationMs)}</td><td style={{ padding: '13px 12px' }}>{compact(tool.calls, lang)}</td><td style={{ padding: '13px 12px' }}>{compact(tool.tokens, lang)}</td><td style={{ padding: '13px 12px' }}>{tool.cacheHits}/{tool.cacheHits + tool.cacheMisses}</td><td style={{ padding: '13px 12px' }}>{money(tool.estimatedCostUsd)}</td></tr>)}{(data?.tools.length ?? 0) === 0 && <tr><td colSpan={8} style={{ padding: 28, textAlign: 'center', ...muted }}>{t['table.empty']}</td></tr>}</tbody></table></div></section>
848
+ <section style={{ ...dashboardCard, padding: '18px 18px 8px', overflow: 'hidden' }}><div style={{ display: 'flex', justifyContent: 'space-between', margin: '0 2px 12px' }}><strong>{t['table.title']}</strong><span style={muted}>{tFormat(t['table.toolCount'], { count: (data?.tools ?? []).length })}</span></div><div style={{ overflowX: 'auto' }}><table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13, minWidth: 760 }}><thead><tr style={{ textAlign: 'left', color: 'var(--dsw-alias-label-secondary)', background: 'var(--dsw-alias-interactive-bg-hover)' }}>{[t['table.colTool'], t['table.colInvocations'], t['table.colSuccessRate'], t['table.colAvgDuration'], t['table.colModelCalls'], t['table.colTokens'], t['table.colCacheHits'], t['table.colEstimatedCost']].map(value => <th key={value} style={{ padding: '10px 12px', fontWeight: 500 }}>{value}</th>)}</tr></thead><tbody>{(data?.tools ?? []).map(tool => <tr key={tool.toolName} style={{ borderTop: '1px solid var(--dsw-alias-border-l2)' }}><td style={{ padding: '13px 12px' }}><span style={{ display: 'inline-block', width: 8, height: 8, borderRadius: '50%', background: toolColors[tool.toolName] ?? '#8691a8', marginRight: 8 }} /><strong>{labels[tool.toolName] ?? tool.toolName}</strong><div style={{ ...muted, margin: '3px 0 0 16px' }}>{tool.toolName}</div></td><td style={{ padding: '13px 12px' }}>{compact(tool.invocations, lang)}</td><td style={{ padding: '13px 12px', color: tool.successRate >= .9 ? 'var(--dsw-alias-state-success-primary)' : tool.successRate >= .7 ? 'var(--dsw-alias-state-warn-label)' : 'var(--dsw-alias-state-error-primary)' }}>{(tool.successRate * 100).toFixed(1)}%</td><td style={{ padding: '13px 12px' }}>{duration(tool.averageDurationMs)}</td><td style={{ padding: '13px 12px' }}>{compact(tool.calls, lang)}</td><td style={{ padding: '13px 12px' }}>{compact(tool.tokens, lang)}</td><td style={{ padding: '13px 12px' }}>{tool.cacheHits}/{tool.cacheHits + tool.cacheMisses}</td><td style={{ padding: '13px 12px' }}>{money(tool.estimatedCostUsd)}</td></tr>)}{(data?.tools.length ?? 0) === 0 && <tr><td colSpan={8} style={{ padding: 28, textAlign: 'center', ...muted }}>{t['table.empty']}</td></tr>}</tbody></table></div></section>
832
849
  <section style={{ display: 'grid', gridTemplateColumns: 'minmax(0,1fr) minmax(290px,.45fr)', gap: 16, alignItems: 'start' }}><div style={{ ...dashboardCard, padding: '18px 18px 8px', overflow: 'hidden' }}><div style={{ display: 'flex', justifyContent: 'space-between', margin: '0 2px 12px' }}><strong>{t['recent.title']}</strong><span style={muted}>{t['recent.maxCount']}</span></div><div style={{ maxHeight: 360, overflow: 'auto' }}>{(data?.recent ?? []).map(item => {
833
850
  const verdictInfo = item.verdict ? formatVerdictDetails(item.verdict, t) : undefined
834
851
  const failed = !item.success || (verdictInfo ? verdictInfo.isFailed : false)
835
852
  const detailsOpen = openDetails === item.id
836
- const panelStyle: React.CSSProperties = { margin: '8px 0 0 15px', border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.14))', borderRadius: 8, padding: '8px 10px', background: 'var(--dsw-surface-sunken)', fontSize: 11 }
853
+ const panelStyle: React.CSSProperties = { margin: '8px 0 0 15px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, padding: '8px 10px', background: 'var(--dsw-alias-interactive-bg-hover)', fontSize: 11 }
837
854
  // The row is a column: identity plus timing on the first line, then the two controls on
838
855
  // one line, then whichever panel they opened. Panels therefore span the whole row
839
856
  // instead of being squeezed into the identity column.
840
- return <div key={item.id} style={{ padding: '11px 8px', borderTop: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.1))' }}>
857
+ return <div key={item.id} style={{ padding: '11px 8px', borderTop: '1px solid var(--dsw-alias-border-l2)' }}>
841
858
  <div style={{ display: 'grid', gridTemplateColumns: 'minmax(170px,1fr) auto', gap: 12 }}>
842
859
  <div>
843
860
  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
844
- <span style={{ width: 7, height: 7, borderRadius: '50%', background: failed ? '#e76565' : '#59c985' }} />
861
+ <span style={{ width: 7, height: 7, borderRadius: '50%', background: failed ? 'var(--dsw-alias-state-error-primary)' : 'var(--dsw-alias-state-success-primary)' }} />
845
862
  <strong style={{ fontSize: 13 }}>{labels[item.toolName] ?? item.toolName}</strong>
846
863
  <span style={muted}>{item.provider}/{item.model}</span>
847
864
  </div>
848
- {!item.success && <div title={item.errorMessage} style={{ margin: '5px 0 0 15px', fontSize: 11, color: '#e76565', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{item.errorMessage ?? item.errorName}</div>}
865
+ {!item.success && <div title={item.errorMessage} style={{ margin: '5px 0 0 15px', fontSize: 11, color: 'var(--dsw-alias-state-error-primary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{item.errorMessage ?? item.errorName}</div>}
849
866
  {item.verdict && verdictInfo && <div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 6, margin: '6px 0 0 15px', fontSize: 11 }}>
850
- {verdictInfo.outcomeText && <span style={{ padding: '1px 5px', borderRadius: 4, fontWeight: 500, fontSize: 11, background: verdictInfo.isFailed ? 'rgba(231,101,101,.16)' : item.verdict.outcome === 'tie' ? 'rgba(227,189,99,.16)' : 'rgba(89,201,133,.16)', color: verdictInfo.isFailed ? '#e76565' : item.verdict.outcome === 'tie' ? '#e3bd63' : '#77d49b', border: `1px solid ${verdictInfo.isFailed ? 'rgba(231,101,101,.3)' : item.verdict.outcome === 'tie' ? 'rgba(227,189,99,.3)' : 'rgba(89,201,133,.3)'}` }}>{verdictInfo.outcomeText}</span>}
851
- {verdictInfo.phaseText && <span style={{ padding: '1px 5px', borderRadius: 4, background: 'var(--dsw-surface-sunken)', color: 'var(--dsw-text-secondary)', border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.1))' }}>{verdictInfo.phaseText}</span>}
852
- {verdictInfo.scoreText && <span style={{ color: (typeof item.verdict.threshold === 'number' && typeof item.verdict.score === 'number' && item.verdict.score < item.verdict.threshold) ? '#e76565' : 'var(--dsw-text-primary)' }}>{verdictInfo.scoreText}</span>}
853
- {verdictInfo.checkpointsText && <span style={{ color: 'var(--dsw-text-secondary)' }}>{verdictInfo.checkpointsText}</span>}
854
- {verdictInfo.criteriaText && <span style={{ color: 'var(--dsw-text-secondary)' }}>{verdictInfo.criteriaText}</span>}
855
- {verdictInfo.winnerText && <span style={{ color: 'var(--dsw-text-secondary)' }}>{verdictInfo.winnerText}</span>}
856
- {item.route && <span style={{ padding: '1px 5px', borderRadius: 4, fontSize: 11, background: 'rgba(120,140,220,.14)', color: 'var(--dsw-text-secondary)', border: '1px solid rgba(120,140,220,.3)' }}>{tFormat(t['recent.route.badge'], { stage: item.route.stage, destination: item.route.destination })}</span>}
857
- {item.stats.usageIncomplete === true && <span style={{ padding: '1px 5px', borderRadius: 4, fontSize: 11, background: 'rgba(227,189,99,.16)', color: '#e3bd63', border: '1px solid rgba(227,189,99,.3)' }}>{t['recent.detail.usageIncomplete']}</span>}
867
+ {verdictInfo.outcomeText && <span style={{ padding: '1px 5px', borderRadius: 4, fontWeight: 500, fontSize: 11, ...toneChip(verdictInfo.isFailed ? 'error' : item.verdict.outcome === 'tie' ? 'warn' : 'pass') }}>{verdictInfo.outcomeText}</span>}
868
+ {verdictInfo.phaseText && <span style={{ padding: '1px 5px', borderRadius: 4, background: 'var(--dsw-alias-interactive-bg-hover)', color: 'var(--dsw-alias-label-secondary)', border: '1px solid var(--dsw-alias-border-l2)' }}>{verdictInfo.phaseText}</span>}
869
+ {verdictInfo.scoreText && <span style={{ color: (typeof item.verdict.threshold === 'number' && typeof item.verdict.score === 'number' && item.verdict.score < item.verdict.threshold) ? 'var(--dsw-alias-state-error-primary)' : 'var(--dsw-alias-label-primary)' }}>{verdictInfo.scoreText}</span>}
870
+ {verdictInfo.checkpointsText && <span style={{ color: 'var(--dsw-alias-label-secondary)' }}>{verdictInfo.checkpointsText}</span>}
871
+ {verdictInfo.criteriaText && <span style={{ color: 'var(--dsw-alias-label-secondary)' }}>{verdictInfo.criteriaText}</span>}
872
+ {verdictInfo.winnerText && <span style={{ color: 'var(--dsw-alias-label-secondary)' }}>{verdictInfo.winnerText}</span>}
873
+ {item.route && <span style={{ padding: '1px 5px', borderRadius: 4, fontSize: 11, color: 'var(--dsw-alias-label-secondary)', ...toneChip('neutral') }}>{tFormat(t['recent.route.badge'], { stage: item.route.stage, destination: item.route.destination })}</span>}
874
+ {item.stats.usageIncomplete === true && <span style={{ padding: '1px 5px', borderRadius: 4, fontSize: 11, ...toneChip('warn') }}>{t['recent.detail.usageIncomplete']}</span>}
858
875
  </div>}
859
876
  </div>
860
877
  <div style={{ textAlign: 'right' }}><div style={{ fontSize: 12 }}>{duration(item.durationMs)}</div><div style={{ ...muted, marginTop: 3 }}>{dateTime(item.startedAt, lang)}</div></div>
@@ -863,37 +880,37 @@ export function StatisticsPage({ sessionId, rpc, isGlobal, blankComposerSeat }:
863
880
  that opened it: the snapshot used to render after a details block that merely
864
881
  happened to be expanded, which read as if it belonged to that block. */}
865
882
  <div style={{ display: 'flex', alignItems: 'center', gap: 8, margin: '6px 0 0 15px' }}>
866
- <button type="button" aria-expanded={snapshot?.id === item.id} onClick={() => void toggleSnapshot(item.id)} style={{ fontSize: 11, padding: '2px 7px', borderRadius: 5, cursor: 'pointer', color: 'var(--dsw-text-secondary)', background: 'var(--dsw-surface-sunken)', border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.14))' }}>{snapshot?.id === item.id ? t['recent.decisionHide'] : t['recent.decision']}</button>
867
- <button type="button" aria-expanded={detailsOpen} onClick={() => setOpenDetails(current => current === item.id ? null : item.id)} style={{ fontSize: 11, padding: '2px 7px', borderRadius: 5, cursor: 'pointer', color: 'var(--dsw-text-secondary)', background: 'var(--dsw-surface-sunken)', border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.14))' }}>{detailsOpen ? t['recent.detailsHide'] : t['recent.details']}</button>
883
+ <button type="button" aria-expanded={snapshot?.id === item.id} onClick={() => void toggleSnapshot(item.id)} style={{ fontSize: 11, padding: '2px 7px', borderRadius: 5, cursor: 'pointer', color: 'var(--dsw-alias-label-secondary)', background: 'var(--dsw-alias-interactive-bg-hover)', border: '1px solid var(--dsw-alias-border-l2)' }}>{snapshot?.id === item.id ? t['recent.decisionHide'] : t['recent.decision']}</button>
884
+ <button type="button" aria-expanded={detailsOpen} onClick={() => setOpenDetails(current => current === item.id ? null : item.id)} style={{ fontSize: 11, padding: '2px 7px', borderRadius: 5, cursor: 'pointer', color: 'var(--dsw-alias-label-secondary)', background: 'var(--dsw-alias-interactive-bg-hover)', border: '1px solid var(--dsw-alias-border-l2)' }}>{detailsOpen ? t['recent.detailsHide'] : t['recent.details']}</button>
868
885
  </div>
869
886
  {snapshot?.id === item.id && <div style={panelStyle}>
870
- {snapshot.error !== undefined && <div style={{ fontSize: 11, color: '#e76565' }}>{snapshot.error}</div>}
887
+ {snapshot.error !== undefined && <div style={{ fontSize: 11, color: 'var(--dsw-alias-state-error-primary)' }}>{snapshot.error}</div>}
871
888
  {snapshot.record === undefined && snapshot.error === undefined && <div style={{ ...muted, fontSize: 11 }}>{t['recent.decisionLoading']}</div>}
872
889
  {snapshot.record !== undefined && snapshot.record.calls.length === 0 && <div style={{ ...muted, fontSize: 11 }}>{t['recent.decisionEmpty']}</div>}
873
890
  {(snapshot.record?.calls ?? []).map((call, index) => <div key={index} style={{ marginBottom: index === snapshot.record!.calls.length - 1 ? 0 : 10 }}>
874
- <div style={{ fontSize: 11, color: 'var(--dsw-text-secondary)' }}>{call.label} · {call.channel}{call.score === undefined ? '' : ' · ' + formatPercentage(call.score)}</div>
891
+ <div style={{ fontSize: 11, color: 'var(--dsw-alias-label-secondary)' }}>{call.label} · {call.channel}{call.score === undefined ? '' : ' · ' + formatPercentage(call.score)}</div>
875
892
  <div style={{ fontSize: 11, marginTop: 4 }}>{t['recent.decisionPrompt']}</div>
876
- <pre style={{ margin: 0, maxHeight: 180, overflow: 'auto', fontSize: 11, whiteSpace: 'pre-wrap', wordBreak: 'break-word', background: 'rgba(0,0,0,.2)', padding: '6px 8px', borderRadius: 6 }}>{call.prompt}</pre>
893
+ <pre style={{ margin: 0, maxHeight: 180, overflow: 'auto', fontSize: 11, whiteSpace: 'pre-wrap', wordBreak: 'break-word', background: 'var(--dsw-alias-markdown-code-block)', padding: '6px 8px', borderRadius: 6 }}>{call.prompt}</pre>
877
894
  <div style={{ fontSize: 11, marginTop: 4 }}>{t['recent.decisionOutput']}</div>
878
- <pre style={{ margin: 0, maxHeight: 140, overflow: 'auto', fontSize: 11, whiteSpace: 'pre-wrap', wordBreak: 'break-word', background: 'rgba(0,0,0,.2)', padding: '6px 8px', borderRadius: 6 }}>{call.output}</pre>
895
+ <pre style={{ margin: 0, maxHeight: 140, overflow: 'auto', fontSize: 11, whiteSpace: 'pre-wrap', wordBreak: 'break-word', background: 'var(--dsw-alias-markdown-code-block)', padding: '6px 8px', borderRadius: 6 }}>{call.output}</pre>
879
896
  </div>)}
880
897
  </div>}
881
898
  {detailsOpen && <div style={panelStyle}>
882
899
  {(item.verdict?.criteria?.length ?? 0) > 0 && <div style={{ display: 'grid', gridTemplateColumns: 'minmax(120px,1fr) auto auto', gap: '4px 14px', marginBottom: 8 }}>
883
- <strong style={{ color: 'var(--dsw-text-secondary)' }}>{t['recent.detail.criterion']}</strong>
884
- <strong style={{ color: 'var(--dsw-text-secondary)' }}>{t['recent.detail.score']}</strong>
885
- <strong style={{ color: 'var(--dsw-text-secondary)' }}>{t['recent.detail.threshold']}</strong>
900
+ <strong style={{ color: 'var(--dsw-alias-label-secondary)' }}>{t['recent.detail.criterion']}</strong>
901
+ <strong style={{ color: 'var(--dsw-alias-label-secondary)' }}>{t['recent.detail.score']}</strong>
902
+ <strong style={{ color: 'var(--dsw-alias-label-secondary)' }}>{t['recent.detail.threshold']}</strong>
886
903
  {item.verdict!.criteria!.map(criterion => {
887
904
  const threshold = typeof item.verdict!.threshold === 'number' ? item.verdict!.threshold : undefined
888
905
  const missed = threshold !== undefined && criterion.score < threshold
889
906
  return <Fragment key={criterion.id}>
890
907
  <span>{criterion.id}</span>
891
- <span style={{ color: missed ? '#e76565' : '#77d49b' }}>{formatPercentage(criterion.score)}</span>
892
- <span style={{ color: 'var(--dsw-text-secondary)' }}>{threshold === undefined ? '—' : formatPercentage(threshold)}</span>
908
+ <span style={{ color: missed ? 'var(--dsw-alias-state-error-primary)' : 'var(--dsw-alias-state-success-primary)' }}>{formatPercentage(criterion.score)}</span>
909
+ <span style={{ color: 'var(--dsw-alias-label-secondary)' }}>{threshold === undefined ? '—' : formatPercentage(threshold)}</span>
893
910
  </Fragment>
894
911
  })}
895
912
  </div>}
896
- <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, color: 'var(--dsw-text-secondary)' }}>
913
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, color: 'var(--dsw-alias-label-secondary)' }}>
897
914
  <span>{tFormat(t['recent.detail.calls'], { calls: compact(item.stats.calls, lang) })}</span>
898
915
  <span>{tFormat(t['recent.detail.tokens'], { input: compact(item.stats.inputTokens, lang), cached: compact(item.stats.cachedInputTokens, lang), output: compact(item.stats.outputTokens, lang) })}</span>
899
916
  <span>{tFormat(t['recent.detail.scoreCache'], { hits: compact(item.stats.cacheHits, lang), misses: compact(item.stats.cacheMisses, lang) })}</span>
@@ -911,14 +928,14 @@ export function StatisticsPage({ sessionId, rpc, isGlobal, blankComposerSeat }:
911
928
  </div>}
912
929
  </div>
913
930
  })}{(data?.recent.length ?? 0) === 0 && <div style={{ padding: 24, textAlign: 'center', ...muted }}>{t['recent.empty']}</div>}</div></div>
914
- <div style={{ ...dashboardCard, padding: '18px' }}><strong>{t['models.title']}</strong><div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 10 }}>{(data?.models ?? []).map(model => <div key={model.provider + '\0' + model.model} style={{ padding: '11px 12px', borderRadius: 10, background: 'var(--dsw-surface-sunken)' }}><div style={{ fontWeight: 650, fontSize: 13, overflow: 'hidden', textOverflow: 'ellipsis' }}>{model.model}</div><div style={{ ...muted, marginTop: 3 }}>{model.provider}</div><div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 9, fontSize: 12 }}><span>{tFormat(t['models.calls'], { calls: compact(model.calls, lang) })}</span><span>{tFormat(t['models.tokens'], { tokens: compact(model.tokens, lang) })}</span><strong>{money(model.estimatedCostUsd)}</strong></div></div>)}{(data?.models.length ?? 0) === 0 && <div style={muted}>{t['models.empty']}</div>}</div></div></section>
931
+ <div style={{ ...dashboardCard, padding: '18px' }}><strong>{t['models.title']}</strong><div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 10 }}>{(data?.models ?? []).map(model => <div key={model.provider + '\0' + model.model} style={{ padding: '11px 12px', borderRadius: 10, background: 'var(--dsw-alias-interactive-bg-hover)' }}><div style={{ fontWeight: 650, fontSize: 13, overflow: 'hidden', textOverflow: 'ellipsis' }}>{model.model}</div><div style={{ ...muted, marginTop: 3 }}>{model.provider}</div><div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 9, fontSize: 12 }}><span>{tFormat(t['models.calls'], { calls: compact(model.calls, lang) })}</span><span>{tFormat(t['models.tokens'], { tokens: compact(model.tokens, lang) })}</span><strong>{money(model.estimatedCostUsd)}</strong></div></div>)}{(data?.models.length ?? 0) === 0 && <div style={muted}>{t['models.empty']}</div>}</div></div></section>
915
932
  </>}
916
933
  </div>
917
934
  </main>
918
935
  }
919
936
 
920
937
  export function VerifierSidebarIcon({ size, active }: { size: number; active?: boolean }) {
921
- return <span style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: size, height: size, color: active ? 'var(--dsw-alias-brand-primary, #4f8cff)' : 'currentColor' }}><IconDataOutline16 size={Math.min(18, size)} /></span>
938
+ return <span style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: size, height: size, color: active ? 'var(--dsw-alias-state-business-primary)' : 'currentColor' }}><IconDataOutline16 size={Math.min(18, size)} /></span>
922
939
  }
923
940
 
924
941
  export function GlobalVerifierDashboard({ rpc }: { rpc: any }) {
@@ -976,6 +993,43 @@ async function readVerifierActivity(rpc: any, sessionId: string, signal: AbortSi
976
993
  } catch { return null }
977
994
  }
978
995
 
996
+ /**
997
+ * Dock-card geometry, copied from the host's own composer-stack cards (Todo/Goal/Queue).
998
+ *
999
+ * Those cards share one width axis — the chat content column minus the composer clearances and the
1000
+ * four dock insets — and the fallbacks below only matter on a host that does not publish the three
1001
+ * custom properties. Without this clamp the status line stretches to the window edge and its text
1002
+ * sits far left of the composer it belongs to, which is exactly the misplacement it is here to fix.
1003
+ */
1004
+ const activityDock: React.CSSProperties = {
1005
+ boxSizing: 'border-box',
1006
+ width: 'calc(100% - var(--dsh-composer-side-clearance, 16px) - var(--dsh-composer-side-clearance, 16px) - var(--dsh-composer-dock-inset, 8px) - var(--dsh-composer-dock-inset, 8px) - var(--dsh-composer-dock-inset, 8px) - var(--dsh-composer-dock-inset, 8px))',
1007
+ margin: '0 auto',
1008
+ }
1009
+ const activityBar: React.CSSProperties = {
1010
+ boxSizing: 'border-box',
1011
+ display: 'flex',
1012
+ alignItems: 'center',
1013
+ gap: 10,
1014
+ width: '100%',
1015
+ maxWidth: 'calc(var(--dsh-composer-card-max-width, 952px) - 4 * var(--dsh-composer-dock-inset, 8px))',
1016
+ height: 36,
1017
+ margin: '0 auto',
1018
+ padding: '0 12px',
1019
+ border: '0.5px solid var(--dsw-alias-border-l1)',
1020
+ borderRadius: 12,
1021
+ background: 'var(--dsw-specific-tip)',
1022
+ }
1023
+ const activityTextStyle: React.CSSProperties = {
1024
+ minWidth: 0,
1025
+ overflow: 'hidden',
1026
+ textOverflow: 'ellipsis',
1027
+ whiteSpace: 'nowrap',
1028
+ fontSize: 13,
1029
+ lineHeight: '20px',
1030
+ color: 'var(--dsw-alias-label-secondary)',
1031
+ }
1032
+
979
1033
  /**
980
1034
  * The chat-visible half of the automatic stages: what the plugin is doing for this session right now.
981
1035
  *
@@ -1010,11 +1064,13 @@ export function VerifierActivityChip({ session, rpc }: { session?: { sessionId?:
1010
1064
  return () => { cancelled = true; clearInterval(timer); controller.abort() }
1011
1065
  }, [sessionId, running, showing, rpc])
1012
1066
  if (rendered === null) return null
1013
- const accent = rendered.tone === 'busy' ? '#4f8cff' : rendered.tone === 'ok' ? '#77d492' : 'var(--dsw-danger, #e85858)'
1067
+ const dotState: StateDotState = rendered.tone === 'busy' ? 'ongoing' : rendered.tone === 'ok' ? 'done' : 'error'
1014
1068
  return (
1015
- <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '2px 4px 6px', ...muted }}>
1016
- <span aria-hidden style={{ flex: '0 0 auto', width: 7, height: 7, borderRadius: '50%', background: accent, opacity: rendered.tone === 'busy' ? 0.65 : 1 }} />
1017
- <span>{rendered.text}</span>
1069
+ <div style={activityDock}>
1070
+ <div style={activityBar} role="status" aria-live="polite">
1071
+ <StateDot state={dotState} size={8} />
1072
+ <span style={activityTextStyle}>{rendered.text}</span>
1073
+ </div>
1018
1074
  </div>
1019
1075
  )
1020
1076
  }