dsh-llm-verifier 0.6.0 → 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.
Files changed (61) hide show
  1. package/README.md +3 -0
  2. package/lib/{auto-DAMeKRkK.js → auto-caXDU2pw.js} +19 -1
  3. package/lib/{auto-DAMeKRkK.js.map → auto-caXDU2pw.js.map} +1 -1
  4. package/lib/client.js +493 -331
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +201 -52
  7. package/lib/index.js.map +1 -1
  8. package/lib/replay.js +1 -1
  9. package/lib/types/client-i18n.d.ts +36 -11
  10. package/lib/types/client-i18n.d.ts.map +1 -1
  11. package/lib/types/client-i18n.js +50 -9
  12. package/lib/types/client-i18n.js.map +1 -1
  13. package/lib/types/client.d.ts +17 -7
  14. package/lib/types/client.d.ts.map +1 -1
  15. package/lib/types/client.js +165 -44
  16. package/lib/types/client.js.map +1 -1
  17. package/lib/types/decisions.d.ts +8 -0
  18. package/lib/types/decisions.d.ts.map +1 -1
  19. package/lib/types/decisions.js +13 -1
  20. package/lib/types/decisions.js.map +1 -1
  21. package/lib/types/index.d.ts.map +1 -1
  22. package/lib/types/index.js +47 -31
  23. package/lib/types/index.js.map +1 -1
  24. package/lib/types/process-selection.d.ts +27 -5
  25. package/lib/types/process-selection.d.ts.map +1 -1
  26. package/lib/types/process-selection.js +17 -8
  27. package/lib/types/process-selection.js.map +1 -1
  28. package/lib/types/router.d.ts +22 -0
  29. package/lib/types/router.d.ts.map +1 -1
  30. package/lib/types/router.js +20 -0
  31. package/lib/types/router.js.map +1 -1
  32. package/lib/types/statistics.d.ts +8 -0
  33. package/lib/types/statistics.d.ts.map +1 -1
  34. package/lib/types/statistics.js +1 -1
  35. package/lib/types/statistics.js.map +1 -1
  36. package/lib/types/verifier-activity.d.ts +189 -0
  37. package/lib/types/verifier-activity.d.ts.map +1 -0
  38. package/lib/types/verifier-activity.js +223 -0
  39. package/lib/types/verifier-activity.js.map +1 -0
  40. package/package.json +1 -1
  41. package/src/client-i18n.ts +55 -16
  42. package/src/client.test.ts +101 -16
  43. package/src/client.tsx +238 -103
  44. package/src/decisions.test.ts +16 -0
  45. package/src/decisions.ts +22 -1
  46. package/src/index.test.ts +32 -0
  47. package/src/index.ts +48 -33
  48. package/src/process-selection.test.ts +13 -0
  49. package/src/process-selection.ts +39 -14
  50. package/src/router.test.ts +46 -0
  51. package/src/router.ts +31 -0
  52. package/src/statistics.test.ts +16 -0
  53. package/src/statistics.ts +9 -1
  54. package/src/verifier-activity.test.ts +164 -0
  55. package/src/verifier-activity.ts +321 -0
  56. package/lib/types/process-activity.d.ts +0 -109
  57. package/lib/types/process-activity.d.ts.map +0 -1
  58. package/lib/types/process-activity.js +0 -135
  59. package/lib/types/process-activity.js.map +0 -1
  60. package/src/process-activity.test.ts +0 -91
  61. package/src/process-activity.ts +0 -177
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Button, IconDataOutline16, IconRefreshOutline16, Input } from '@deepseek-ai/dsh-client-ui-primitives';
2
+ import { Button, IconDataOutline16, IconRefreshOutline16, Input, StateDot } from '@deepseek-ai/dsh-client-ui-primitives';
3
3
  import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
4
- import { zh, en, dictionaries, toolLabels, tFormat, useLanguage, detectLanguage, compact, money, duration, dateTime, resolveCacheDirOnSave, sameSettingValue, sectionForSave, WORST_CASE_ROUTE_CALLS_PER_JUDGE, WORST_CASE_FINAL_CALLS_PER_JUDGE, WORST_CASE_TASK_PER_JUDGE, WORST_CASE_SESSION_PER_JUDGE, WORST_CASE_CRITERIA_PER_COMPARISON, computeJudgeCount, computeWorstCaseBudget, evaluateBudgetWarning, isVerdictFailed, formatPercentage, formatVerdictDetails, processActivityText, } from "./client-i18n.js";
4
+ import { zh, en, dictionaries, toolLabels, tFormat, useLanguage, detectLanguage, compact, money, duration, dateTime, resolveCacheDirOnSave, sameSettingValue, sectionForSave, WORST_CASE_ROUTE_CALLS_PER_JUDGE, WORST_CASE_FINAL_CALLS_PER_JUDGE, WORST_CASE_TASK_PER_JUDGE, WORST_CASE_SESSION_PER_JUDGE, WORST_CASE_CRITERIA_PER_COMPARISON, computeJudgeCount, computeWorstCaseBudget, evaluateBudgetWarning, isVerdictFailed, formatPercentage, formatVerdictDetails, verifierActivityText, } from "./client-i18n.js";
5
5
  import { CRITERIA_PRESETS, DEFAULT_GROUND_TRUTH_NOTE, buildPairwisePrompt } from "./core.js";
6
6
  import { MAX_EXTRA_JUDGES, normalizeExtraJudges, judgeIdentity, judgeConflict, addExtraJudge, removeExtraJudge, serializeExtraJudges, } from "./client-judges.js";
7
7
  import { FIELDS, PROFILES, SECTIONS, acceptsNumber, activeProfile, applyProfile, isFieldChanged, issueMap, issueMessageKey, recommendedBudgets, resetField, sectionSummary, renderSections, textIssue, validateValues, valuesFromView, } from "./client-fields.js";
@@ -15,7 +15,7 @@ const sectionTitle = { display: 'flex', alignItems: 'center', gap: 8, width: '10
15
15
  const sectionHeadingStyle = { fontSize: 14, fontWeight: 500, lineHeight: '22px' };
16
16
  const sectionSummaryStyle = { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', minWidth: 0, flex: '1 1 auto' };
17
17
  const badgeStyle = { 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)' };
18
- const linkButton = { border: 0, background: 'transparent', padding: 0, font: 'inherit', fontSize: 12, lineHeight: '18px', cursor: 'pointer', color: 'var(--dsw-alias-brand-primary, #4f8cff)' };
18
+ const linkButton = { border: 0, background: 'transparent', padding: 0, font: 'inherit', fontSize: 12, lineHeight: '18px', cursor: 'pointer', color: 'var(--dsw-alias-link)' };
19
19
  const row = { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '6px 24px', minHeight: 56, padding: '10px 0', borderBottom: '1px solid var(--dsw-alias-border-l2)' };
20
20
  const labelCell = { flex: '1 1 220px', minWidth: 0 };
21
21
  const controlCell = { flex: '0 1 268px', minWidth: 170, display: 'flex', justifyContent: 'flex-end' };
@@ -23,16 +23,31 @@ const fieldTitle = { fontSize: 14, fontWeight: 400, lineHeight: '22px', color: '
23
23
  const fieldHelp = { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)', marginTop: 2 };
24
24
  const fullLine = { flex: '1 1 100%', margin: '0 0 2px', fontSize: 12, lineHeight: '18px' };
25
25
  const unitStyle = { flex: '0 0 auto', fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' };
26
- const sliderStyle = { width: '100%', margin: 0, accentColor: 'var(--dsw-alias-brand-primary, #4f8cff)' };
26
+ const sliderStyle = { width: '100%', margin: 0, accentColor: 'var(--dsw-alias-state-business-primary)' };
27
27
  const toolbarStyle = { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8, padding: '10px 0', borderTop: '1px solid var(--dsw-alias-border-l2)' };
28
28
  const summaryLineStyle = { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' };
29
- const stickyBar = { 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)' };
29
+ const stickyBar = { 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)' };
30
30
  const statusStyle = { fontSize: 12, lineHeight: '18px', display: 'inline-flex', alignItems: 'center', gap: 8, minWidth: 0, flexWrap: 'wrap' };
31
- const selectStyle = { 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' };
32
- const toggleStyle = (enabled) => ({ 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)' });
33
- const toggleThumbStyle = (enabled) => ({ 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' });
34
- const dashboardCard = { 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)' };
35
- const muted = { color: 'var(--dsw-text-secondary)', fontSize: 12 };
31
+ const selectStyle = { 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' };
32
+ const toggleStyle = (enabled) => ({ 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)' });
33
+ const toggleThumbStyle = (enabled) => ({ 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' });
34
+ const dashboardCard = { 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)' };
35
+ const muted = { color: 'var(--dsw-alias-label-tertiary)', fontSize: 12 };
36
+ /** Chart palette rides the state aliases so bars and line stay legible in both themes. */
37
+ const chartBarColor = 'var(--dsw-alias-state-business-primary)';
38
+ const chartLineColor = 'var(--dsw-alias-state-warn-primary)';
39
+ function toneChip(tone) {
40
+ const token = tone === 'error' ? 'var(--dsw-alias-state-error-primary)'
41
+ : tone === 'warn' ? 'var(--dsw-alias-state-warn-primary)'
42
+ : tone === 'pass' ? 'var(--dsw-alias-state-success-primary)'
43
+ : 'var(--dsw-alias-state-business-primary)';
44
+ return {
45
+ background: `color-mix(in srgb, ${token} 14%, transparent)`,
46
+ border: `1px solid color-mix(in srgb, ${token} 32%, transparent)`,
47
+ // Amber's 500 step is a fill; its 600 step is the readable text rung.
48
+ color: tone === 'warn' ? 'var(--dsw-alias-state-warn-label)' : token,
49
+ };
50
+ }
36
51
  const toolColors = { verifier_route_classify: '#d97706', verifier_compare: '#4f8cff', verifier_select: '#8b6df6', verifier_track: '#2fc5c9', verifier_best_of_n: '#e2569b', verifier_current_session: '#f5a524' };
37
52
  function record(value) { return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : {}; }
38
53
  function values(view) { return valuesFromView(record(view.value)); }
@@ -324,7 +339,7 @@ export function VerifierSettings({ remote }) {
324
339
  const first = preset[0];
325
340
  if (!first)
326
341
  return null;
327
- return _jsxs("details", { style: { margin: '2px 0 6px 4px', fontSize: 12 }, children: [_jsx("summary", { style: { cursor: 'pointer', color: 'var(--dsw-text-secondary)' }, children: tFormat(t['field.criteriaPreset.previewSummary'], { count: preset.length }) }), _jsx("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 }, children: buildPairwisePrompt(t['field.criteriaPreset.sampleTask'], t['field.criteriaPreset.sampleA'], t['field.criteriaPreset.sampleB'], first, DEFAULT_GROUND_TRUTH_NOTE) })] });
342
+ return _jsxs("details", { style: { margin: '2px 0 6px 4px', fontSize: 12 }, children: [_jsx("summary", { style: { cursor: 'pointer', color: 'var(--dsw-alias-label-secondary)' }, children: tFormat(t['field.criteriaPreset.previewSummary'], { count: preset.length }) }), _jsx("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 }, children: buildPairwisePrompt(t['field.criteriaPreset.sampleTask'], t['field.criteriaPreset.sampleA'], t['field.criteriaPreset.sampleB'], first, DEFAULT_GROUND_TRUTH_NOTE) })] });
328
343
  })() : null;
329
344
  return _jsxs(Fragment, { children: [_jsxs("div", { style: row, "data-field": String(field.key), children: [_jsxs("div", { style: labelCell, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }, children: [_jsx("span", { style: fieldTitle, children: translate(field.titleKey) ?? String(field.key) }), changed && _jsx("span", { style: badgeStyle, children: t['settings.fieldChanged'] }), changed && _jsx("button", { type: "button", style: linkButton, disabled: busy, onClick: () => resetOne(field), children: t['settings.fieldReset'] })] }), _jsx("div", { style: fieldHelp, children: helpFor(field) })] }), _jsx("div", { style: controlCell, children: renderControl(field) }), message && _jsx("p", { style: { ...fullLine, color: 'var(--dsw-alias-state-error-primary)' }, children: issueText(message) }), warning && _jsx("p", { style: { ...fullLine, color: 'var(--dsw-alias-state-warn-label)' }, children: warning })] }), preview] }, String(field.key));
330
345
  };
@@ -350,7 +365,7 @@ export function VerifierSettings({ remote }) {
350
365
  const open = openSections[entry.section.id] === true;
351
366
  const summary = sectionSummary(entry.section.id, draft, translate, format);
352
367
  return _jsxs("section", { style: group, children: [_jsxs("button", { type: "button", style: sectionTitle, "aria-expanded": open, "aria-label": tFormat(translate(open ? 'settings.sectionCollapse' : 'settings.sectionExpand') ?? '', { title }), onClick: () => setSectionOpen(entry.section.id, !open), children: [_jsx("span", { style: sectionHeadingStyle, children: title }), entry.section.tier === 'advanced' && _jsx("span", { style: badgeStyle, children: t['settings.advancedBadge'] }), summary && _jsx("span", { style: sectionSummaryStyle, children: summary }), _jsx("span", { style: { marginLeft: summary ? 0 : 'auto', color: 'var(--dsw-alias-label-tertiary)', fontSize: 12 }, children: open ? '▾' : '▸' })] }), open && entry.fields.map(field => (field.kind === 'custom' ? renderJudges() : renderRow(field))), open && entry.section.id === 'tools' && !draft.enabled && _jsx("p", { style: { margin: '8px 0 0', fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-warn-label)' }, children: t['field.enabled.warnDisabled'] }), open && entry.section.id === 'autoVerify' && draft.autoVerifyMode !== 'manual' && _jsx("p", { style: { margin: '8px 0 0', fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-warn-label)' }, children: t['field.autoVerify.warnNotice'] }), open && entry.section.id === 'budgets' && _jsx("div", { style: { padding: '10px 0' }, children: _jsx(Button, { variant: "outline", disabled: busy, onClick: fillRecommended, children: t['settings.recommend'] }) })] }, entry.section.id);
353
- }), loaded.failures.length > 0 && _jsxs("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' }, children: [_jsx("div", { style: { fontWeight: 500, marginBottom: 3 }, children: t['settings.catalogFailures'] }), loaded.failures.map(failure => _jsx("div", { children: failure }, failure))] }), _jsx("div", { style: stickyBar, children: _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }, children: [issues.length > 0
368
+ }), loaded.failures.length > 0 && _jsxs("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' }, children: [_jsx("div", { style: { fontWeight: 500, marginBottom: 3 }, children: t['settings.catalogFailures'] }), loaded.failures.map(failure => _jsx("div", { children: failure }, failure))] }), _jsx("div", { style: stickyBar, children: _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }, children: [issues.length > 0
354
369
  ? _jsxs("span", { style: { ...statusStyle, color: 'var(--dsw-alias-state-error-primary)' }, children: [tFormat(t['settings.invalid.summary'], { count: issues.length }), _jsx("button", { type: "button", style: linkButton, onClick: () => jumpTo(issues[0].key), children: t['settings.jumpToIssue'] })] })
355
370
  : error
356
371
  ? _jsx("span", { style: { ...statusStyle, color: 'var(--dsw-alias-state-error-primary)' }, children: error })
@@ -379,9 +394,9 @@ function TrendChart({ daily, days, lang }) {
379
394
  const points = rows.map((row, index) => `${x(index)},${y(row.calls)}`).join(' ');
380
395
  const step = rows.length > 16 ? Math.ceil(rows.length / 7) : Math.max(1, Math.ceil(rows.length / 7));
381
396
  const barWidth = Math.max(3, Math.min(18, innerW / Math.max(rows.length, 1) * .55));
382
- return _jsxs("div", { style: { width: '100%', overflowX: 'auto' }, children: [_jsxs("svg", { viewBox: `0 0 ${width} ${height}`, style: { display: 'block', width: '100%', minWidth: 620, height: 'auto' }, "aria-label": t['chart.ariaLabel'], children: [[0, .25, .5, .75, 1].map(ratio => _jsxs("g", { children: [_jsx("line", { x1: pad.l, x2: width - pad.r, y1: pad.t + innerH * ratio, y2: pad.t + innerH * ratio, stroke: "rgba(148,163,184,.16)" }), _jsx("text", { x: pad.l - 8, y: pad.t + innerH * ratio + 4, textAnchor: "end", fontSize: "10", fill: "var(--dsw-text-secondary)", children: Math.round(max * (1 - ratio)) })] }, ratio)), rows.map((row, index) => _jsx("rect", { x: x(index) - barWidth / 2, y: y(row.invocations), width: barWidth, height: pad.t + innerH - y(row.invocations), rx: "2", fill: "#4f8cff", opacity: ".82" }, row.date)), _jsx("polyline", { points: points, fill: "none", stroke: "#5ed7e8", strokeWidth: "2.4", strokeLinejoin: "round", strokeLinecap: "round" }), rows.map((row, index) => index % step === 0 || index === rows.length - 1 ? _jsxs("text", { x: x(index), y: height - 14, textAnchor: "middle", fontSize: "10", fill: "var(--dsw-text-secondary)", children: [Number(row.date.slice(5, 7)), "/", Number(row.date.slice(8, 10))] }, row.date) : null)] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'center', gap: 18, ...muted }, children: [_jsxs("span", { children: [_jsx("i", { style: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: '#4f8cff', marginRight: 6 } }), t['chart.legendToolCalls']] }), _jsxs("span", { children: [_jsx("i", { style: { display: 'inline-block', width: 14, height: 2, background: '#5ed7e8', marginRight: 6, verticalAlign: 'middle' } }), t['chart.legendModelCalls']] })] })] });
397
+ return _jsxs("div", { style: { width: '100%', overflowX: 'auto' }, children: [_jsxs("svg", { viewBox: `0 0 ${width} ${height}`, style: { display: 'block', width: '100%', minWidth: 620, height: 'auto' }, "aria-label": t['chart.ariaLabel'], children: [[0, .25, .5, .75, 1].map(ratio => _jsxs("g", { children: [_jsx("line", { x1: pad.l, x2: width - pad.r, y1: pad.t + innerH * ratio, y2: pad.t + innerH * ratio, stroke: "var(--dsw-alias-border-l2)" }), _jsx("text", { x: pad.l - 8, y: pad.t + innerH * ratio + 4, textAnchor: "end", fontSize: "10", fill: "var(--dsw-alias-label-secondary)", children: Math.round(max * (1 - ratio)) })] }, ratio)), rows.map((row, index) => _jsx("rect", { x: x(index) - barWidth / 2, y: y(row.invocations), width: barWidth, height: pad.t + innerH - y(row.invocations), rx: "2", fill: chartBarColor, opacity: ".82" }, row.date)), _jsx("polyline", { points: points, fill: "none", stroke: chartLineColor, strokeWidth: "2.4", strokeLinejoin: "round", strokeLinecap: "round" }), rows.map((row, index) => index % step === 0 || index === rows.length - 1 ? _jsxs("text", { x: x(index), y: height - 14, textAnchor: "middle", fontSize: "10", fill: "var(--dsw-alias-label-secondary)", children: [Number(row.date.slice(5, 7)), "/", Number(row.date.slice(8, 10))] }, row.date) : null)] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'center', gap: 18, ...muted }, children: [_jsxs("span", { children: [_jsx("i", { style: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: chartBarColor, marginRight: 6 } }), t['chart.legendToolCalls']] }), _jsxs("span", { children: [_jsx("i", { style: { display: 'inline-block', width: 14, height: 2, background: chartLineColor, marginRight: 6, verticalAlign: 'middle' } }), t['chart.legendModelCalls']] })] })] });
383
398
  }
384
- export function StatisticsPage({ sessionId, rpc, isGlobal }) {
399
+ export function StatisticsPage({ sessionId, rpc, isGlobal, blankComposerSeat }) {
385
400
  const lang = useLanguage();
386
401
  const t = dictionaries[lang];
387
402
  const labels = toolLabels[lang];
@@ -395,6 +410,24 @@ export function StatisticsPage({ sessionId, rpc, isGlobal }) {
395
410
  // that loaded all of them would ship megabytes of text for rows nobody opened.
396
411
  const [snapshot, setSnapshot] = useState(null);
397
412
  const snapshotRequest = useRef(0);
413
+ // Details are a controlled disclosure rather than a per-row <details>: both entry points then sit
414
+ // on the SAME line and each panel opens directly beneath it, instead of the snapshot landing
415
+ // under a details block that happens to be expanded.
416
+ const [openDetails, setOpenDetails] = useState(null);
417
+ // A read-only dashboard has nothing to type into, yet the Conversation shell renders its
418
+ // composer for every View. The handle is only present on the Conversation View instance, and
419
+ // disposing it on unmount brings the real card back with its draft intact. Mount-only: the
420
+ // takeover belongs to this View's lifetime, and a host that rebuilds the injected props object
421
+ // must not make the seat flap between registrations.
422
+ const seatHandle = useRef(blankComposerSeat);
423
+ seatHandle.current = blankComposerSeat;
424
+ useEffect(() => {
425
+ const take = seatHandle.current;
426
+ if (typeof take !== 'function')
427
+ return undefined;
428
+ const dispose = take();
429
+ return () => { dispose(); };
430
+ }, []);
398
431
  const toggleSnapshot = async (id) => {
399
432
  if (snapshot?.id === id) {
400
433
  setSnapshot(null);
@@ -410,6 +443,13 @@ export function StatisticsPage({ sessionId, rpc, isGlobal }) {
410
443
  const result = await rpc.call('/api', 'llm-verifier/statistics', payload);
411
444
  if (result && result.ok === true)
412
445
  record = result.value.decision;
446
+ else if (result && result.ok === false) {
447
+ // A rejection is the host's real answer — "no snapshot for this row" is one of them —
448
+ // so it is shown as-is instead of being retried over the raw fetch endpoint.
449
+ if (snapshotRequest.current === request)
450
+ setSnapshot({ id, error: result.error?.message ?? t['recent.decisionMissing'] });
451
+ return;
452
+ }
413
453
  }
414
454
  catch (rpcError) {
415
455
  console.warn('[llm-verifier] decision rpc.call failed, trying fetch fallback:', rpcError);
@@ -555,18 +595,23 @@ export function StatisticsPage({ sessionId, rpc, isGlobal }) {
555
595
  return () => controller.abort();
556
596
  }, [days, sessionOnly, sessionId, refresh, rpc, t, queryKey]);
557
597
  const totals = data?.totals;
558
- return _jsx("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%)' }, children: _jsxs("div", { style: { maxWidth: 1180, margin: '0 auto', display: 'flex', flexDirection: 'column', gap: 16 }, children: [_jsxs("header", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16, flexWrap: 'wrap' }, children: [_jsxs("div", { children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10 }, children: [_jsx("span", { style: { display: 'grid', placeItems: 'center', width: 34, height: 34, borderRadius: 10, background: 'rgba(79,140,255,.14)', color: '#6da0ff' }, children: _jsx(IconDataOutline16, { size: 18 }) }), _jsx("h2", { style: { margin: 0, fontSize: 23 }, children: isGlobal ? t['global.panelTitle'] : t['stats.pageTitle'] })] }), _jsx("p", { style: { margin: '7px 0 0 44px', ...muted }, children: isGlobal ? t['global.panelIntro'] : tFormat(t['stats.updatedAt'], { time: data ? dateTime(data.generatedAt, lang) : '--' }) })] }), _jsxs("div", { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }, children: [_jsx("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))' }, children: [7, 30, 90].map(value => _jsx("button", { "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' }, children: tFormat(t['stats.daysUnit'], { days: value }) }, value)) }), Boolean(sessionId) && _jsx("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)' }, children: sessionOnly ? t['stats.currentSession'] : t['stats.allSessions'] }), _jsx("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)' }, children: probe?.busy === true ? t['probe.running'] : t['probe.button'] }), _jsx("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' }, children: _jsx(IconRefreshOutline16, { size: 16 }) })] })] }), error && _jsxs("div", { style: { ...dashboardCard, padding: 18, borderColor: 'var(--dsw-danger, #e85858)', color: 'var(--dsw-danger, #e85858)' }, children: [error, _jsx("div", { style: { ...muted, marginTop: 6 }, children: t['stats.hostRestartHint'] })] }), probe !== null && _jsxs("section", { style: { ...dashboardCard, padding: '16px 18px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [_jsx("strong", { children: t['probe.title'] }), _jsx("span", { style: muted, children: t['probe.note'] })] }), probe.error !== undefined && _jsx("div", { style: { marginTop: 10, fontSize: 12, color: '#e76565' }, children: probe.error }), probe.value !== undefined && _jsxs(_Fragment, { children: [_jsx("div", { style: { ...muted, marginTop: 8 }, children: tFormat(t['probe.rubric'], { source: probe.value.rubric.source, count: probe.value.rubric.count, file: probe.value.rubric.file ?? '' }) }), probe.value.rubric.error !== undefined && _jsx("div", { style: { marginTop: 6, fontSize: 12, color: '#e3bd63' }, children: tFormat(t['probe.rubricFallback'], { error: probe.value.rubric.error }) }), _jsx("div", { style: { marginTop: 10, display: 'flex', flexDirection: 'column', gap: 8 }, children: probe.value.judges.map(judge => _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center', fontSize: 12, padding: '9px 11px', borderRadius: 9, background: 'var(--dsw-surface-sunken)' }, children: [_jsx("span", { style: { color: judge.ok ? '#77d49b' : '#e76565', fontWeight: 600 }, children: judge.ok ? 'OK' : t['probe.failed'] }), _jsx("strong", { children: judge.label }), _jsxs("span", { style: muted, children: [judge.provider, "/", judge.model] }), judge.ok ? _jsxs(_Fragment, { children: [judge.channel !== undefined && _jsxs("span", { style: muted, children: [tFormat(t['probe.channel'], { channel: judge.channel }), judge.channelProbed === true ? ' · ' + t['probe.reprobed'] : ''] }), judge.scoreA !== undefined && judge.scoreB !== undefined && _jsx("span", { style: muted, children: tFormat(t['probe.scores'], { a: formatPercentage(judge.scoreA), b: formatPercentage(judge.scoreB) }) }), _jsx("span", { style: muted, children: tFormat(t['probe.latency'], { ms: String(judge.latencyMs) }) }), judge.calls !== undefined && _jsx("span", { style: muted, children: tFormat(t['probe.calls'], { calls: String(judge.calls) }) })] }) : _jsx("span", { style: { color: '#e76565' }, children: judge.error })] }, judge.label + judge.model)) })] })] }), loading && !data ? _jsx("div", { style: { ...dashboardCard, padding: 32, textAlign: 'center', ...muted }, children: t['stats.loading'] }) : _jsxs(_Fragment, { children: [_jsxs("section", { style: { ...dashboardCard, padding: '22px 24px', display: 'grid', gridTemplateColumns: 'minmax(220px,1.4fr) minmax(240px,1fr)', gap: 24, alignItems: 'center' }, children: [_jsxs("div", { children: [_jsx("div", { style: muted, children: tFormat(t['stats.costSummary'], { days }) }), _jsx("div", { style: { fontSize: 38, fontWeight: 780, letterSpacing: '-.03em', margin: '5px 0' }, children: money(totals?.estimatedCostUsd ?? 0) }), _jsx("div", { style: muted, children: tFormat(t['stats.callsSummary'], { invocations: compact(totals?.invocations ?? 0, lang), calls: compact(totals?.calls ?? 0, lang) }) })] }), _jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr auto', gap: '9px 16px', fontSize: 13 }, children: [_jsx("span", { style: muted, children: t['stats.successCalls'] }), _jsxs("strong", { children: [compact(totals?.successes ?? 0, lang), " ", _jsxs("small", { style: { color: '#77d49b' }, children: ["\u25B2 ", ((totals?.successRate ?? 0) * 100).toFixed(1), "%"] })] }), _jsx("span", { style: muted, children: t['stats.failedCalls'] }), _jsx("strong", { children: compact(totals?.failures ?? 0, lang) }), _jsx("span", { style: muted, children: t['stats.avgDuration'] }), _jsx("strong", { children: duration(totals?.averageDurationMs ?? 0) })] })] }), _jsxs("section", { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(180px,1fr))', gap: 12 }, children: [_jsx(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" }), _jsx(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) }) }), _jsx(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) }) }), _jsx(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) }) }), _jsx(Metric, { label: t['metric.scoringMode'], value: compact(totals?.topLogprobScores ?? 0, lang), note: tFormat(t['metric.scoringModeNote'], { explicit: compact(totals?.explicitTagScores ?? 0, lang) }) })] }), _jsxs("section", { style: { ...dashboardCard, padding: '18px 20px 16px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }, children: [_jsx("strong", { children: t['chart.title'] }), _jsx("span", { style: muted, children: sessionOnly ? t['stats.currentSession'] : t['stats.allSessions'] })] }), _jsx(TrendChart, { daily: data?.daily ?? [], days: days, lang: lang })] }), _jsxs("section", { style: { ...dashboardCard, padding: '18px 18px 8px', overflow: 'hidden' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', margin: '0 2px 12px' }, children: [_jsx("strong", { children: t['table.title'] }), _jsx("span", { style: muted, children: tFormat(t['table.toolCount'], { count: (data?.tools ?? []).length }) })] }), _jsx("div", { style: { overflowX: 'auto' }, children: _jsxs("table", { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13, minWidth: 760 }, children: [_jsx("thead", { children: _jsx("tr", { style: { textAlign: 'left', color: 'var(--dsw-text-secondary)', background: 'var(--dsw-surface-sunken)' }, children: [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 => _jsx("th", { style: { padding: '10px 12px', fontWeight: 500 }, children: value }, value)) }) }), _jsxs("tbody", { children: [(data?.tools ?? []).map(tool => _jsxs("tr", { style: { borderTop: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.1))' }, children: [_jsxs("td", { style: { padding: '13px 12px' }, children: [_jsx("span", { style: { display: 'inline-block', width: 8, height: 8, borderRadius: '50%', background: toolColors[tool.toolName] ?? '#8691a8', marginRight: 8 } }), _jsx("strong", { children: labels[tool.toolName] ?? tool.toolName }), _jsx("div", { style: { ...muted, margin: '3px 0 0 16px' }, children: tool.toolName })] }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.invocations, lang) }), _jsxs("td", { style: { padding: '13px 12px', color: tool.successRate >= .9 ? '#77d49b' : tool.successRate >= .7 ? '#e3bd63' : '#ed7777' }, children: [(tool.successRate * 100).toFixed(1), "%"] }), _jsx("td", { style: { padding: '13px 12px' }, children: duration(tool.averageDurationMs) }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.calls, lang) }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.tokens, lang) }), _jsxs("td", { style: { padding: '13px 12px' }, children: [tool.cacheHits, "/", tool.cacheHits + tool.cacheMisses] }), _jsx("td", { style: { padding: '13px 12px' }, children: money(tool.estimatedCostUsd) })] }, tool.toolName)), (data?.tools.length ?? 0) === 0 && _jsx("tr", { children: _jsx("td", { colSpan: 8, style: { padding: 28, textAlign: 'center', ...muted }, children: t['table.empty'] }) })] })] }) })] }), _jsxs("section", { style: { display: 'grid', gridTemplateColumns: 'minmax(0,1fr) minmax(290px,.45fr)', gap: 16, alignItems: 'start' }, children: [_jsxs("div", { style: { ...dashboardCard, padding: '18px 18px 8px', overflow: 'hidden' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', margin: '0 2px 12px' }, children: [_jsx("strong", { children: t['recent.title'] }), _jsx("span", { style: muted, children: t['recent.maxCount'] })] }), _jsxs("div", { style: { maxHeight: 360, overflow: 'auto' }, children: [(data?.recent ?? []).map(item => {
598
+ return _jsx("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%)' }, children: _jsxs("div", { style: { maxWidth: 1180, margin: '0 auto', display: 'flex', flexDirection: 'column', gap: 16 }, children: [_jsxs("header", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16, flexWrap: 'wrap' }, children: [_jsxs("div", { children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10 }, children: [_jsx("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)' }, children: _jsx(IconDataOutline16, { size: 18 }) }), _jsx("h2", { style: { margin: 0, fontSize: 23 }, children: isGlobal ? t['global.panelTitle'] : t['stats.pageTitle'] })] }), _jsx("p", { style: { margin: '7px 0 0 44px', ...muted }, children: isGlobal ? t['global.panelIntro'] : tFormat(t['stats.updatedAt'], { time: data ? dateTime(data.generatedAt, lang) : '--' }) })] }), _jsxs("div", { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }, children: [_jsx("div", { style: { display: 'flex', padding: 3, borderRadius: 10, background: 'var(--dsw-alias-bg-multi-select)', border: '1px solid var(--dsw-alias-border-l2)' }, children: [7, 30, 90].map(value => _jsx("button", { "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' }, children: tFormat(t['stats.daysUnit'], { days: value }) }, value)) }), Boolean(sessionId) && _jsx("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)' }, children: sessionOnly ? t['stats.currentSession'] : t['stats.allSessions'] }), _jsx("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)' }, children: probe?.busy === true ? t['probe.running'] : t['probe.button'] }), _jsx("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' }, children: _jsx(IconRefreshOutline16, { size: 16 }) })] })] }), error && _jsxs("div", { style: { ...dashboardCard, padding: 18, borderColor: 'var(--dsw-alias-state-error-primary)', color: 'var(--dsw-alias-state-error-primary)' }, children: [error, _jsx("div", { style: { ...muted, marginTop: 6 }, children: t['stats.hostRestartHint'] })] }), probe !== null && _jsxs("section", { style: { ...dashboardCard, padding: '16px 18px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [_jsx("strong", { children: t['probe.title'] }), _jsx("span", { style: muted, children: t['probe.note'] })] }), probe.error !== undefined && _jsx("div", { style: { marginTop: 10, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' }, children: probe.error }), probe.value !== undefined && _jsxs(_Fragment, { children: [_jsx("div", { style: { ...muted, marginTop: 8 }, children: tFormat(t['probe.rubric'], { source: probe.value.rubric.source, count: probe.value.rubric.count, file: probe.value.rubric.file ?? '' }) }), probe.value.rubric.error !== undefined && _jsx("div", { style: { marginTop: 6, fontSize: 12, color: 'var(--dsw-alias-state-warn-label)' }, children: tFormat(t['probe.rubricFallback'], { error: probe.value.rubric.error }) }), _jsx("div", { style: { marginTop: 10, display: 'flex', flexDirection: 'column', gap: 8 }, children: probe.value.judges.map(judge => _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center', fontSize: 12, padding: '9px 11px', borderRadius: 9, background: 'var(--dsw-alias-interactive-bg-hover)' }, children: [_jsx("span", { style: { color: judge.ok ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)', fontWeight: 600 }, children: judge.ok ? 'OK' : t['probe.failed'] }), _jsx("strong", { children: judge.label }), _jsxs("span", { style: muted, children: [judge.provider, "/", judge.model] }), judge.ok ? _jsxs(_Fragment, { children: [judge.channel !== undefined && _jsxs("span", { style: muted, children: [tFormat(t['probe.channel'], { channel: judge.channel }), judge.channelProbed === true ? ' · ' + t['probe.reprobed'] : ''] }), judge.scoreA !== undefined && judge.scoreB !== undefined && _jsx("span", { style: muted, children: tFormat(t['probe.scores'], { a: formatPercentage(judge.scoreA), b: formatPercentage(judge.scoreB) }) }), _jsx("span", { style: muted, children: tFormat(t['probe.latency'], { ms: String(judge.latencyMs) }) }), judge.calls !== undefined && _jsx("span", { style: muted, children: tFormat(t['probe.calls'], { calls: String(judge.calls) }) })] }) : _jsx("span", { style: { color: 'var(--dsw-alias-state-error-primary)' }, children: judge.error })] }, judge.label + judge.model)) })] })] }), loading && !data ? _jsx("div", { style: { ...dashboardCard, padding: 32, textAlign: 'center', ...muted }, children: t['stats.loading'] }) : _jsxs(_Fragment, { children: [_jsxs("section", { style: { ...dashboardCard, padding: '22px 24px', display: 'grid', gridTemplateColumns: 'minmax(220px,1.4fr) minmax(240px,1fr)', gap: 24, alignItems: 'center' }, children: [_jsxs("div", { children: [_jsx("div", { style: muted, children: tFormat(t['stats.costSummary'], { days }) }), _jsx("div", { style: { fontSize: 38, fontWeight: 780, letterSpacing: '-.03em', margin: '5px 0' }, children: money(totals?.estimatedCostUsd ?? 0) }), _jsx("div", { style: muted, children: tFormat(t['stats.callsSummary'], { invocations: compact(totals?.invocations ?? 0, lang), calls: compact(totals?.calls ?? 0, lang) }) })] }), _jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr auto', gap: '9px 16px', fontSize: 13 }, children: [_jsx("span", { style: muted, children: t['stats.successCalls'] }), _jsxs("strong", { children: [compact(totals?.successes ?? 0, lang), " ", _jsxs("small", { style: { color: 'var(--dsw-alias-state-success-primary)' }, children: ["\u25B2 ", ((totals?.successRate ?? 0) * 100).toFixed(1), "%"] })] }), _jsx("span", { style: muted, children: t['stats.failedCalls'] }), _jsx("strong", { children: compact(totals?.failures ?? 0, lang) }), _jsx("span", { style: muted, children: t['stats.avgDuration'] }), _jsx("strong", { children: duration(totals?.averageDurationMs ?? 0) })] })] }), _jsxs("section", { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(180px,1fr))', gap: 12 }, children: [_jsx(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)" }), _jsx(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) }) }), _jsx(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) }) }), _jsx(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) }) }), _jsx(Metric, { label: t['metric.scoringMode'], value: compact(totals?.topLogprobScores ?? 0, lang), note: tFormat(t['metric.scoringModeNote'], { explicit: compact(totals?.explicitTagScores ?? 0, lang) }) })] }), _jsxs("section", { style: { ...dashboardCard, padding: '18px 20px 16px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }, children: [_jsx("strong", { children: t['chart.title'] }), _jsx("span", { style: muted, children: sessionOnly ? t['stats.currentSession'] : t['stats.allSessions'] })] }), _jsx(TrendChart, { daily: data?.daily ?? [], days: days, lang: lang })] }), _jsxs("section", { style: { ...dashboardCard, padding: '18px 18px 8px', overflow: 'hidden' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', margin: '0 2px 12px' }, children: [_jsx("strong", { children: t['table.title'] }), _jsx("span", { style: muted, children: tFormat(t['table.toolCount'], { count: (data?.tools ?? []).length }) })] }), _jsx("div", { style: { overflowX: 'auto' }, children: _jsxs("table", { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13, minWidth: 760 }, children: [_jsx("thead", { children: _jsx("tr", { style: { textAlign: 'left', color: 'var(--dsw-alias-label-secondary)', background: 'var(--dsw-alias-interactive-bg-hover)' }, children: [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 => _jsx("th", { style: { padding: '10px 12px', fontWeight: 500 }, children: value }, value)) }) }), _jsxs("tbody", { children: [(data?.tools ?? []).map(tool => _jsxs("tr", { style: { borderTop: '1px solid var(--dsw-alias-border-l2)' }, children: [_jsxs("td", { style: { padding: '13px 12px' }, children: [_jsx("span", { style: { display: 'inline-block', width: 8, height: 8, borderRadius: '50%', background: toolColors[tool.toolName] ?? '#8691a8', marginRight: 8 } }), _jsx("strong", { children: labels[tool.toolName] ?? tool.toolName }), _jsx("div", { style: { ...muted, margin: '3px 0 0 16px' }, children: tool.toolName })] }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.invocations, lang) }), _jsxs("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)' }, children: [(tool.successRate * 100).toFixed(1), "%"] }), _jsx("td", { style: { padding: '13px 12px' }, children: duration(tool.averageDurationMs) }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.calls, lang) }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.tokens, lang) }), _jsxs("td", { style: { padding: '13px 12px' }, children: [tool.cacheHits, "/", tool.cacheHits + tool.cacheMisses] }), _jsx("td", { style: { padding: '13px 12px' }, children: money(tool.estimatedCostUsd) })] }, tool.toolName)), (data?.tools.length ?? 0) === 0 && _jsx("tr", { children: _jsx("td", { colSpan: 8, style: { padding: 28, textAlign: 'center', ...muted }, children: t['table.empty'] }) })] })] }) })] }), _jsxs("section", { style: { display: 'grid', gridTemplateColumns: 'minmax(0,1fr) minmax(290px,.45fr)', gap: 16, alignItems: 'start' }, children: [_jsxs("div", { style: { ...dashboardCard, padding: '18px 18px 8px', overflow: 'hidden' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', margin: '0 2px 12px' }, children: [_jsx("strong", { children: t['recent.title'] }), _jsx("span", { style: muted, children: t['recent.maxCount'] })] }), _jsxs("div", { style: { maxHeight: 360, overflow: 'auto' }, children: [(data?.recent ?? []).map(item => {
559
599
  const verdictInfo = item.verdict ? formatVerdictDetails(item.verdict, t) : undefined;
560
600
  const failed = !item.success || (verdictInfo ? verdictInfo.isFailed : false);
561
- return _jsxs("div", { style: { display: 'grid', gridTemplateColumns: 'minmax(170px,1fr) auto', gap: 12, padding: '11px 8px', borderTop: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.1))' }, children: [_jsxs("div", { children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: { width: 7, height: 7, borderRadius: '50%', background: failed ? '#e76565' : '#59c985' } }), _jsx("strong", { style: { fontSize: 13 }, children: labels[item.toolName] ?? item.toolName }), _jsxs("span", { style: muted, children: [item.provider, "/", item.model] })] }), !item.success && _jsx("div", { title: item.errorMessage, style: { margin: '5px 0 0 15px', fontSize: 11, color: '#e76565', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: item.errorMessage ?? item.errorName }), item.verdict && verdictInfo && _jsxs("div", { style: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 6, margin: '6px 0 0 15px', fontSize: 11 }, children: [verdictInfo.outcomeText && _jsx("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)'}` }, children: verdictInfo.outcomeText }), verdictInfo.phaseText && _jsx("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))' }, children: verdictInfo.phaseText }), verdictInfo.scoreText && _jsx("span", { style: { color: (typeof item.verdict.threshold === 'number' && typeof item.verdict.score === 'number' && item.verdict.score < item.verdict.threshold) ? '#e76565' : 'var(--dsw-text-primary)' }, children: verdictInfo.scoreText }), verdictInfo.checkpointsText && _jsx("span", { style: { color: 'var(--dsw-text-secondary)' }, children: verdictInfo.checkpointsText }), verdictInfo.criteriaText && _jsx("span", { style: { color: 'var(--dsw-text-secondary)' }, children: verdictInfo.criteriaText }), verdictInfo.winnerText && _jsx("span", { style: { color: 'var(--dsw-text-secondary)' }, children: verdictInfo.winnerText }), item.route && _jsx("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)' }, children: tFormat(t['recent.route.badge'], { stage: item.route.stage, destination: item.route.destination }) }), item.stats.usageIncomplete === true && _jsx("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)' }, children: t['recent.detail.usageIncomplete'] })] }), _jsx("div", { style: { margin: '6px 0 0 15px' }, children: _jsx("button", { type: "button", 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))' }, children: snapshot?.id === item.id ? t['recent.decisionHide'] : t['recent.decision'] }) }), _jsxs("details", { style: { margin: '6px 0 0 15px' }, children: [_jsx("summary", { style: { cursor: 'pointer', fontSize: 11, color: 'var(--dsw-text-secondary)' }, children: t['recent.details'] }), _jsxs("div", { style: { marginTop: 8, 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 }, children: [(item.verdict?.criteria?.length ?? 0) > 0 && _jsxs("div", { style: { display: 'grid', gridTemplateColumns: 'minmax(120px,1fr) auto auto', gap: '4px 14px', marginBottom: 8 }, children: [_jsx("strong", { style: { color: 'var(--dsw-text-secondary)' }, children: t['recent.detail.criterion'] }), _jsx("strong", { style: { color: 'var(--dsw-text-secondary)' }, children: t['recent.detail.score'] }), _jsx("strong", { style: { color: 'var(--dsw-text-secondary)' }, children: t['recent.detail.threshold'] }), item.verdict.criteria.map(criterion => {
562
- const threshold = typeof item.verdict.threshold === 'number' ? item.verdict.threshold : undefined;
563
- const missed = threshold !== undefined && criterion.score < threshold;
564
- return _jsxs(Fragment, { children: [_jsx("span", { children: criterion.id }), _jsx("span", { style: { color: missed ? '#e76565' : '#77d49b' }, children: formatPercentage(criterion.score) }), _jsx("span", { style: { color: 'var(--dsw-text-secondary)' }, children: threshold === undefined ? '—' : formatPercentage(threshold) })] }, criterion.id);
565
- })] }), _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 10, color: 'var(--dsw-text-secondary)' }, children: [_jsx("span", { children: tFormat(t['recent.detail.calls'], { calls: compact(item.stats.calls, lang) }) }), _jsx("span", { children: tFormat(t['recent.detail.tokens'], { input: compact(item.stats.inputTokens, lang), cached: compact(item.stats.cachedInputTokens, lang), output: compact(item.stats.outputTokens, lang) }) }), _jsx("span", { children: tFormat(t['recent.detail.scoreCache'], { hits: compact(item.stats.cacheHits, lang), misses: compact(item.stats.cacheMisses, lang) }) }), _jsx("span", { children: tFormat(t['recent.detail.prefixCache'], { rate: ((item.stats.inputTokens + item.stats.cachedInputTokens) > 0 ? (100 * item.stats.cachedInputTokens / (item.stats.inputTokens + item.stats.cachedInputTokens)).toFixed(0) : '0') + '%' }) }), _jsx("span", { children: tFormat(t['recent.detail.cost'], { cost: money(item.stats.estimatedCostUsd) }) }), item.route && _jsx("span", { children: tFormat(t['recent.detail.route'], { cycle: item.route.cycleId, trigger: item.route.trigger, stage: item.route.stage, destination: item.route.destination, attempt: item.route.attempt ?? 1 }) }), item.route && item.route.reservedCalls !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeReserved'], { reserved: compact(item.route.reservedCalls, lang), actual: compact(item.stats.calls, lang) }) }), item.route?.skipReason !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeSkip'], { reason: item.route.skipReason }) }), item.route?.replayed !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeProcess'], { replayed: item.route.replayed === 'candidate' ? t['recent.detail.replayCandidate'] : item.route.replayed === 'original' ? t['recent.detail.replayOriginal'] : t['recent.detail.replayNone'], generated: compact(item.route.generatedCalls ?? 0, lang), judges: compact(item.route.judgeCalls ?? 0, lang) }) }), item.route?.sameCandidate === true && _jsx("span", { children: t['recent.detail.routeSameCandidate'] }), item.route?.alternativeAugmented === true && _jsx("span", { children: t['recent.detail.routeAugmented'] }), item.route?.alternativeModel !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeAlternativeModel'], { model: item.route.alternativeModel }) }), (item.stats.channelFallbacks ?? 0) > 0 && _jsx("span", { children: tFormat(t['recent.detail.channelFallback'], { count: compact(item.stats.channelFallbacks ?? 0, lang) }) })] })] })] }), snapshot?.id === item.id && _jsxs("div", { style: { margin: '8px 0 2px 15px', border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.14))', borderRadius: 8, padding: '8px 10px', background: 'var(--dsw-surface-sunken)' }, children: [snapshot.error !== undefined && _jsx("div", { style: { fontSize: 11, color: '#e76565' }, children: snapshot.error }), snapshot.record === undefined && snapshot.error === undefined && _jsx("div", { style: { ...muted, fontSize: 11 }, children: t['recent.decisionLoading'] }), snapshot.record !== undefined && snapshot.record.calls.length === 0 && _jsx("div", { style: { ...muted, fontSize: 11 }, children: t['recent.decisionEmpty'] }), (snapshot.record?.calls ?? []).map((call, index) => _jsxs("div", { style: { marginBottom: index === snapshot.record.calls.length - 1 ? 0 : 10 }, children: [_jsxs("div", { style: { fontSize: 11, color: 'var(--dsw-text-secondary)' }, children: [call.label, " \u00B7 ", call.channel, call.score === undefined ? '' : ' · ' + formatPercentage(call.score)] }), _jsx("div", { style: { fontSize: 11, marginTop: 4 }, children: t['recent.decisionPrompt'] }), _jsx("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 }, children: call.prompt }), _jsx("div", { style: { fontSize: 11, marginTop: 4 }, children: t['recent.decisionOutput'] }), _jsx("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 }, children: call.output })] }, index))] })] }), _jsxs("div", { style: { textAlign: 'right' }, children: [_jsx("div", { style: { fontSize: 12 }, children: duration(item.durationMs) }), _jsx("div", { style: { ...muted, marginTop: 3 }, children: dateTime(item.startedAt, lang) })] })] }, item.id);
566
- }), (data?.recent.length ?? 0) === 0 && _jsx("div", { style: { padding: 24, textAlign: 'center', ...muted }, children: t['recent.empty'] })] })] }), _jsxs("div", { style: { ...dashboardCard, padding: '18px' }, children: [_jsx("strong", { children: t['models.title'] }), _jsxs("div", { style: { marginTop: 10, display: 'flex', flexDirection: 'column', gap: 10 }, children: [(data?.models ?? []).map(model => _jsxs("div", { style: { padding: '11px 12px', borderRadius: 10, background: 'var(--dsw-surface-sunken)' }, children: [_jsx("div", { style: { fontWeight: 650, fontSize: 13, overflow: 'hidden', textOverflow: 'ellipsis' }, children: model.model }), _jsx("div", { style: { ...muted, marginTop: 3 }, children: model.provider }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', marginTop: 9, fontSize: 12 }, children: [_jsx("span", { children: tFormat(t['models.calls'], { calls: compact(model.calls, lang) }) }), _jsx("span", { children: tFormat(t['models.tokens'], { tokens: compact(model.tokens, lang) }) }), _jsx("strong", { children: money(model.estimatedCostUsd) })] })] }, model.provider + '\0' + model.model)), (data?.models.length ?? 0) === 0 && _jsx("div", { style: muted, children: t['models.empty'] })] })] })] })] })] }) });
601
+ const detailsOpen = openDetails === item.id;
602
+ const panelStyle = { 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 };
603
+ // The row is a column: identity plus timing on the first line, then the two controls on
604
+ // one line, then whichever panel they opened. Panels therefore span the whole row
605
+ // instead of being squeezed into the identity column.
606
+ return _jsxs("div", { style: { padding: '11px 8px', borderTop: '1px solid var(--dsw-alias-border-l2)' }, children: [_jsxs("div", { style: { display: 'grid', gridTemplateColumns: 'minmax(170px,1fr) auto', gap: 12 }, children: [_jsxs("div", { children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: { width: 7, height: 7, borderRadius: '50%', background: failed ? 'var(--dsw-alias-state-error-primary)' : 'var(--dsw-alias-state-success-primary)' } }), _jsx("strong", { style: { fontSize: 13 }, children: labels[item.toolName] ?? item.toolName }), _jsxs("span", { style: muted, children: [item.provider, "/", item.model] })] }), !item.success && _jsx("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' }, children: item.errorMessage ?? item.errorName }), item.verdict && verdictInfo && _jsxs("div", { style: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 6, margin: '6px 0 0 15px', fontSize: 11 }, children: [verdictInfo.outcomeText && _jsx("span", { style: { padding: '1px 5px', borderRadius: 4, fontWeight: 500, fontSize: 11, ...toneChip(verdictInfo.isFailed ? 'error' : item.verdict.outcome === 'tie' ? 'warn' : 'pass') }, children: verdictInfo.outcomeText }), verdictInfo.phaseText && _jsx("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)' }, children: verdictInfo.phaseText }), verdictInfo.scoreText && _jsx("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)' }, children: verdictInfo.scoreText }), verdictInfo.checkpointsText && _jsx("span", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: verdictInfo.checkpointsText }), verdictInfo.criteriaText && _jsx("span", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: verdictInfo.criteriaText }), verdictInfo.winnerText && _jsx("span", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: verdictInfo.winnerText }), item.route && _jsx("span", { style: { padding: '1px 5px', borderRadius: 4, fontSize: 11, color: 'var(--dsw-alias-label-secondary)', ...toneChip('neutral') }, children: tFormat(t['recent.route.badge'], { stage: item.route.stage, destination: item.route.destination }) }), item.stats.usageIncomplete === true && _jsx("span", { style: { padding: '1px 5px', borderRadius: 4, fontSize: 11, ...toneChip('warn') }, children: t['recent.detail.usageIncomplete'] })] })] }), _jsxs("div", { style: { textAlign: 'right' }, children: [_jsx("div", { style: { fontSize: 12 }, children: duration(item.durationMs) }), _jsx("div", { style: { ...muted, marginTop: 3 }, children: dateTime(item.startedAt, lang) })] })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, margin: '6px 0 0 15px' }, children: [_jsx("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)' }, children: snapshot?.id === item.id ? t['recent.decisionHide'] : t['recent.decision'] }), _jsx("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)' }, children: detailsOpen ? t['recent.detailsHide'] : t['recent.details'] })] }), snapshot?.id === item.id && _jsxs("div", { style: panelStyle, children: [snapshot.error !== undefined && _jsx("div", { style: { fontSize: 11, color: 'var(--dsw-alias-state-error-primary)' }, children: snapshot.error }), snapshot.record === undefined && snapshot.error === undefined && _jsx("div", { style: { ...muted, fontSize: 11 }, children: t['recent.decisionLoading'] }), snapshot.record !== undefined && snapshot.record.calls.length === 0 && _jsx("div", { style: { ...muted, fontSize: 11 }, children: t['recent.decisionEmpty'] }), (snapshot.record?.calls ?? []).map((call, index) => _jsxs("div", { style: { marginBottom: index === snapshot.record.calls.length - 1 ? 0 : 10 }, children: [_jsxs("div", { style: { fontSize: 11, color: 'var(--dsw-alias-label-secondary)' }, children: [call.label, " \u00B7 ", call.channel, call.score === undefined ? '' : ' · ' + formatPercentage(call.score)] }), _jsx("div", { style: { fontSize: 11, marginTop: 4 }, children: t['recent.decisionPrompt'] }), _jsx("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 }, children: call.prompt }), _jsx("div", { style: { fontSize: 11, marginTop: 4 }, children: t['recent.decisionOutput'] }), _jsx("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 }, children: call.output })] }, index))] }), detailsOpen && _jsxs("div", { style: panelStyle, children: [(item.verdict?.criteria?.length ?? 0) > 0 && _jsxs("div", { style: { display: 'grid', gridTemplateColumns: 'minmax(120px,1fr) auto auto', gap: '4px 14px', marginBottom: 8 }, children: [_jsx("strong", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: t['recent.detail.criterion'] }), _jsx("strong", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: t['recent.detail.score'] }), _jsx("strong", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: t['recent.detail.threshold'] }), item.verdict.criteria.map(criterion => {
607
+ const threshold = typeof item.verdict.threshold === 'number' ? item.verdict.threshold : undefined;
608
+ const missed = threshold !== undefined && criterion.score < threshold;
609
+ return _jsxs(Fragment, { children: [_jsx("span", { children: criterion.id }), _jsx("span", { style: { color: missed ? 'var(--dsw-alias-state-error-primary)' : 'var(--dsw-alias-state-success-primary)' }, children: formatPercentage(criterion.score) }), _jsx("span", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: threshold === undefined ? '—' : formatPercentage(threshold) })] }, criterion.id);
610
+ })] }), _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 10, color: 'var(--dsw-alias-label-secondary)' }, children: [_jsx("span", { children: tFormat(t['recent.detail.calls'], { calls: compact(item.stats.calls, lang) }) }), _jsx("span", { children: tFormat(t['recent.detail.tokens'], { input: compact(item.stats.inputTokens, lang), cached: compact(item.stats.cachedInputTokens, lang), output: compact(item.stats.outputTokens, lang) }) }), _jsx("span", { children: tFormat(t['recent.detail.scoreCache'], { hits: compact(item.stats.cacheHits, lang), misses: compact(item.stats.cacheMisses, lang) }) }), _jsx("span", { children: tFormat(t['recent.detail.prefixCache'], { rate: ((item.stats.inputTokens + item.stats.cachedInputTokens) > 0 ? (100 * item.stats.cachedInputTokens / (item.stats.inputTokens + item.stats.cachedInputTokens)).toFixed(0) : '0') + '%' }) }), _jsx("span", { children: tFormat(t['recent.detail.cost'], { cost: money(item.stats.estimatedCostUsd) }) }), item.route && _jsx("span", { children: tFormat(t['recent.detail.route'], { cycle: item.route.cycleId, trigger: item.route.trigger, stage: item.route.stage, destination: item.route.destination, attempt: item.route.attempt ?? 1 }) }), item.route && item.route.reservedCalls !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeReserved'], { reserved: compact(item.route.reservedCalls, lang), actual: compact(item.stats.calls, lang) }) }), item.route?.skipReason !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeSkip'], { reason: item.route.skipReason }) }), item.route?.replayed !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeProcess'], { replayed: item.route.replayed === 'candidate' ? t['recent.detail.replayCandidate'] : item.route.replayed === 'original' ? t['recent.detail.replayOriginal'] : t['recent.detail.replayNone'], generated: compact(item.route.generatedCalls ?? 0, lang), judges: compact(item.route.judgeCalls ?? 0, lang) }) }), item.route?.sameCandidate === true && _jsx("span", { children: t['recent.detail.routeSameCandidate'] }), item.route?.alternativeAugmented === true && _jsx("span", { children: t['recent.detail.routeAugmented'] }), item.route?.alternativeModel !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeAlternativeModel'], { model: item.route.alternativeModel }) }), (item.stats.channelFallbacks ?? 0) > 0 && _jsx("span", { children: tFormat(t['recent.detail.channelFallback'], { count: compact(item.stats.channelFallbacks ?? 0, lang) }) })] })] })] }, item.id);
611
+ }), (data?.recent.length ?? 0) === 0 && _jsx("div", { style: { padding: 24, textAlign: 'center', ...muted }, children: t['recent.empty'] })] })] }), _jsxs("div", { style: { ...dashboardCard, padding: '18px' }, children: [_jsx("strong", { children: t['models.title'] }), _jsxs("div", { style: { marginTop: 10, display: 'flex', flexDirection: 'column', gap: 10 }, children: [(data?.models ?? []).map(model => _jsxs("div", { style: { padding: '11px 12px', borderRadius: 10, background: 'var(--dsw-alias-interactive-bg-hover)' }, children: [_jsx("div", { style: { fontWeight: 650, fontSize: 13, overflow: 'hidden', textOverflow: 'ellipsis' }, children: model.model }), _jsx("div", { style: { ...muted, marginTop: 3 }, children: model.provider }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', marginTop: 9, fontSize: 12 }, children: [_jsx("span", { children: tFormat(t['models.calls'], { calls: compact(model.calls, lang) }) }), _jsx("span", { children: tFormat(t['models.tokens'], { tokens: compact(model.tokens, lang) }) }), _jsx("strong", { children: money(model.estimatedCostUsd) })] })] }, model.provider + '\0' + model.model)), (data?.models.length ?? 0) === 0 && _jsx("div", { style: muted, children: t['models.empty'] })] })] })] })] })] }) });
567
612
  }
568
613
  export function VerifierSidebarIcon({ size, active }) {
569
- return _jsx("span", { style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: size, height: size, color: active ? 'var(--dsw-alias-brand-primary, #4f8cff)' : 'currentColor' }, children: _jsx(IconDataOutline16, { size: Math.min(18, size) }) });
614
+ return _jsx("span", { style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: size, height: size, color: active ? 'var(--dsw-alias-state-business-primary)' : 'currentColor' }, children: _jsx(IconDataOutline16, { size: Math.min(18, size) }) });
570
615
  }
571
616
  export function GlobalVerifierDashboard({ rpc }) {
572
617
  return _jsx(StatisticsPage, { rpc: rpc, isGlobal: true });
@@ -580,20 +625,22 @@ export function RightSidebarVerifierTitle() {
580
625
  return _jsx("span", { style: { fontSize: 13, fontWeight: 500 }, children: t['slot.statistics'] });
581
626
  }
582
627
  export const inject = ['slots', 'connection', 'remote', 'remote.session', 'remote.settings'];
583
- /** Poll interval of the process-selection chip while a turn runs (the answer is in-memory). */
584
- export const PROCESS_ACTIVITY_POLL_MS = 1_000;
628
+ /** Poll interval of the chat chip while a turn runs (the answer is in-memory and cheap). */
629
+ export const VERIFIER_ACTIVITY_POLL_MS = 1_000;
585
630
  /**
586
- * Read the in-flight process-selection cycle of one session.
631
+ * Read what the plugin is doing for one session (P06 cycle, routed review, final acceptance).
587
632
  *
588
- * The chip explains a pause the chat cannot explain by itself (P06 buffers the reply, so no text
589
- * streams while it runs), so a failed or refused read is SILENCE, never an error banner: the
590
- * composer must not grow noise because a dashboard endpoint was unavailable.
633
+ * The chip explains a pause the chat cannot explain by itself, so a failed or refused read is
634
+ * SILENCE, never an error banner: the composer must not grow noise because a dashboard endpoint was
635
+ * unavailable.
591
636
  * @param rpc - the host RPC handle, when the client exposes one.
592
637
  * @param sessionId - the session to ask about.
593
638
  * @param signal - abort signal of the owning effect.
594
639
  * @returns The activity view, or null when there is nothing (or nothing readable).
595
640
  */
596
- async function readProcessActivity(rpc, sessionId, signal) {
641
+ async function readVerifierActivity(rpc, sessionId, signal) {
642
+ // The wire kind is historical (it once meant P06 only) and is kept so a page holding the older
643
+ // client bundle keeps talking to the same endpoint.
597
644
  const payload = { kind: 'process', sessionId };
598
645
  try {
599
646
  if (rpc && typeof rpc.call === 'function') {
@@ -607,7 +654,7 @@ async function readProcessActivity(rpc, sessionId, signal) {
607
654
  catch (rpcError) {
608
655
  if (signal.aborted)
609
656
  return null;
610
- console.warn('[llm-verifier] process activity rpc.call failed, trying fetch fallback:', rpcError);
657
+ console.warn('[llm-verifier] activity rpc.call failed, trying fetch fallback:', rpcError);
611
658
  }
612
659
  }
613
660
  const response = await fetch('/api/llm-verifier/statistics', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload), signal });
@@ -620,20 +667,58 @@ async function readProcessActivity(rpc, sessionId, signal) {
620
667
  }
621
668
  }
622
669
  /**
623
- * The chat-visible half of P06: what the bought cycle is doing while the reply is being buffered.
670
+ * Dock-card geometry, copied from the host's own composer-stack cards (Todo/Goal/Queue).
671
+ *
672
+ * Those cards share one width axis — the chat content column minus the composer clearances and the
673
+ * four dock insets — and the fallbacks below only matter on a host that does not publish the three
674
+ * custom properties. Without this clamp the status line stretches to the window edge and its text
675
+ * sits far left of the composer it belongs to, which is exactly the misplacement it is here to fix.
676
+ */
677
+ const activityDock = {
678
+ boxSizing: 'border-box',
679
+ 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))',
680
+ margin: '0 auto',
681
+ };
682
+ const activityBar = {
683
+ boxSizing: 'border-box',
684
+ display: 'flex',
685
+ alignItems: 'center',
686
+ gap: 10,
687
+ width: '100%',
688
+ maxWidth: 'calc(var(--dsh-composer-card-max-width, 952px) - 4 * var(--dsh-composer-dock-inset, 8px))',
689
+ height: 36,
690
+ margin: '0 auto',
691
+ padding: '0 12px',
692
+ border: '0.5px solid var(--dsw-alias-border-l1)',
693
+ borderRadius: 12,
694
+ background: 'var(--dsw-specific-tip)',
695
+ };
696
+ const activityTextStyle = {
697
+ minWidth: 0,
698
+ overflow: 'hidden',
699
+ textOverflow: 'ellipsis',
700
+ whiteSpace: 'nowrap',
701
+ fontSize: 13,
702
+ lineHeight: '20px',
703
+ color: 'var(--dsw-alias-label-secondary)',
704
+ };
705
+ /**
706
+ * The chat-visible half of the automatic stages: what the plugin is doing for this session right now.
624
707
  *
625
- * Polls only while a read can change the answer — while the turn runs, and while a settled cycle is
626
- * still inside its server-side TTL — so an idle conversation makes no requests at all.
708
+ * It answers the pauses the chat cannot explain: a P06 cycle buffers the reply, a routed review and
709
+ * the final acceptance hold the turn open while the judges run, and a PASSING acceptance otherwise
710
+ * says nothing at all. Polls only while a read can change the answer — while the turn runs, and
711
+ * while something is still being shown — so an idle conversation makes no requests.
627
712
  * @param props - the input-dock owner values (the session snapshot) plus the injected RPC handle.
628
713
  */
629
- export function ProcessActivityChip({ session, rpc }) {
714
+ export function VerifierActivityChip({ session, rpc }) {
630
715
  const lang = useLanguage();
631
716
  const t = dictionaries[lang];
632
717
  const raw = session?.sessionId;
633
718
  const sessionId = raw === undefined || raw === null || String(raw) === '' ? undefined : String(raw);
634
719
  const running = session?.running === true;
635
720
  const [view, setView] = useState(null);
636
- const rendered = processActivityText(view, t);
721
+ const rendered = verifierActivityText(view, t);
637
722
  // Keyed on whether anything is RENDERED rather than on whether a read answered: an empty view has
638
723
  // to stop the polling too, or a conversation that once ran a cycle would poll forever.
639
724
  const showing = rendered !== null;
@@ -649,22 +734,57 @@ export function ProcessActivityChip({ session, rpc }) {
649
734
  let cancelled = false;
650
735
  const controller = new AbortController();
651
736
  const load = async () => {
652
- const value = await readProcessActivity(rpc, sessionId, controller.signal);
737
+ const value = await readVerifierActivity(rpc, sessionId, controller.signal);
653
738
  if (!cancelled)
654
739
  setView(value);
655
740
  };
656
741
  void load();
657
- const timer = setInterval(() => { void load(); }, PROCESS_ACTIVITY_POLL_MS);
742
+ const timer = setInterval(() => { void load(); }, VERIFIER_ACTIVITY_POLL_MS);
658
743
  return () => { cancelled = true; clearInterval(timer); controller.abort(); };
659
744
  }, [sessionId, running, showing, rpc]);
660
745
  if (rendered === null)
661
746
  return null;
662
- const accent = rendered.tone === 'busy' ? '#4f8cff' : rendered.tone === 'ok' ? '#77d492' : 'var(--dsw-danger, #e85858)';
663
- return (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, padding: '2px 4px 6px', ...muted }, children: [_jsx("span", { "aria-hidden": true, style: { flex: '0 0 auto', width: 7, height: 7, borderRadius: '50%', background: accent, opacity: rendered.tone === 'busy' ? 0.65 : 1 } }), _jsx("span", { children: rendered.text })] }));
747
+ const dotState = rendered.tone === 'busy' ? 'ongoing' : rendered.tone === 'ok' ? 'done' : 'error';
748
+ return (_jsx("div", { style: activityDock, children: _jsxs("div", { style: activityBar, role: "status", "aria-live": "polite", children: [_jsx(StateDot, { state: dotState, size: 8 }), _jsx("span", { style: activityTextStyle, children: rendered.text })] }) }));
664
749
  }
750
+ /**
751
+ * Marker the blank-composer takeover elects with; the value itself is never read.
752
+ *
753
+ * Any non-null selector result elects the entry, so a shared frozen object keeps the selector pure
754
+ * and allocation-free.
755
+ */
756
+ const BLANK_COMPOSER = Object.freeze({ blank: true });
665
757
  export function apply(ctx) {
666
758
  const connection = ctx.get('connection');
667
759
  const remote = ctx.remote;
760
+ /**
761
+ * Collapse the resident composer while the statistics View is the active one.
762
+ *
763
+ * The Conversation shell renders its composer for EVERY View — a View can only opt into the
764
+ * overlay geometry (`data-conversation-composer-overlay`), never into its absence — so the input
765
+ * card covers the very table the dashboard exists to show. The selector-routed composer chain is
766
+ * the host's own replacement point: while this View is mounted one entry elects an empty
767
+ * composer, and disposing that entry on unmount restores the real card with its draft intact.
768
+ * Tried LAST (`priority` is ascending) so a pending approval, question or subagent takeover still
769
+ * wins the seat. A host that does not know the key leaves the composer in place instead of
770
+ * failing the page.
771
+ * @returns Disposer that hands the seat back to the resident composer.
772
+ */
773
+ const blankComposerSeat = () => {
774
+ try {
775
+ const dispose = ctx.slots.register({
776
+ name: 'conversation.composer',
777
+ id: 'llm-verifier-blank',
778
+ priority: 1000,
779
+ select: () => BLANK_COMPOSER,
780
+ }, (() => null));
781
+ return typeof dispose === 'function' ? dispose : () => { };
782
+ }
783
+ catch (error) {
784
+ console.warn('[llm-verifier] composer takeover registration failed:', error);
785
+ return () => { };
786
+ }
787
+ };
668
788
  ctx.slots.inject('settings.section', () => ctx.slots.register({
669
789
  name: 'settings.section',
670
790
  id: 'llm-verifier',
@@ -677,16 +797,17 @@ export function apply(ctx) {
677
797
  id: 'llm-verifier-statistics',
678
798
  order: 30,
679
799
  label: () => (detectLanguage() === 'zh' ? zh['slot.statistics'] : en['slot.statistics']),
680
- inject: () => ({ rpc: connection.rpc }),
800
+ inject: () => ({ rpc: connection.rpc, blankComposerSeat }),
681
801
  }, StatisticsPage));
682
- // P06 buffers the main reply, so while a cycle runs the chat looks frozen. The dock sits directly
683
- // above the composer — exactly where the user is looking — and disappears on its own.
802
+ // P06 buffers the main reply (the chat looks frozen) and the routed/final stages hold the turn
803
+ // open with nothing to show. The dock sits directly above the composer — where the user is already
804
+ // looking — and disappears on its own.
684
805
  ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({
685
806
  name: 'conversation.input.dock',
686
- id: 'llm-verifier-process',
807
+ id: 'llm-verifier-activity',
687
808
  order: 30,
688
809
  inject: () => ({ rpc: connection.rpc }),
689
- }, ProcessActivityChip));
810
+ }, VerifierActivityChip));
690
811
  const VERIFIER_TAB_KIND = 'llm-verifier';
691
812
  const VERIFIER_TAB_ID = 'dsh-llm-verifier';
692
813
  const registerRightSidebar = (tabs) => {