| <!doctype html>
|
| <html lang="en">
|
| <head>
|
| <meta charset="utf-8">
|
| <meta name="viewport" content="width=device-width, initial-scale=1">
|
| <title>Agentic Reproducibility Engine | Research Intelligence</title>
|
| <style>
|
| :root {
|
| color-scheme: dark;
|
| --bg-void: #020208;
|
| --bg-deep: #060610;
|
| --bg-panel: rgba(8, 10, 20, 0.88);
|
| --bg-panel-hover: rgba(14, 17, 30, 0.92);
|
| --bg-panel-border: rgba(120, 143, 255, 0.11);
|
| --bg-panel-border-hot: rgba(85, 139, 255, 0.42);
|
| --bg-input: rgba(255, 255, 255, 0.05);
|
| --text-primary: #eef2ff;
|
| --text-secondary: #9ca6c4;
|
| --text-tertiary: #4a5070;
|
| --text-data: #7dd3fc;
|
| --accent-orbital: #4d7cff;
|
| --accent-orbital-glow: rgba(77, 124, 255, 0.22);
|
| --accent-orbital-dim: rgba(77, 124, 255, 0.08);
|
| --accent-scan: #00d4ff;
|
| --accent-scan-glow: rgba(0, 212, 255, 0.16);
|
| --verify: #7cf2b7;
|
| --warning: #fbbf24;
|
| --critical: #ef4444;
|
| --agent-planner: #a78bfa;
|
| --agent-reader: #60a5fa;
|
| --agent-retriever: #34d399;
|
| --agent-auditor: #fb923c;
|
| --agent-experiment: #fbbf24;
|
| --agent-code: #22d3ee;
|
| --agent-verifier: #f87171;
|
| --agent-report: #c084fc;
|
| --radius-sm: 10px;
|
| --radius-md: 14px;
|
| --radius-lg: 22px;
|
| --shadow-soft: 0 24px 70px rgba(0, 0, 0, 0.36);
|
| --font-mono: "Cascadia Mono", "SF Mono", Consolas, ui-monospace, monospace;
|
| --font-sans: "Aptos", "Segoe UI", system-ui, sans-serif;
|
| }
|
|
|
| * {
|
| box-sizing: border-box;
|
| }
|
|
|
| html,
|
| body {
|
| margin: 0;
|
| width: 100%;
|
| min-height: 100%;
|
| background: var(--bg-void);
|
| color: var(--text-primary);
|
| font-family: var(--font-sans);
|
| font-size: 16px;
|
| line-height: 1.45;
|
| -webkit-font-smoothing: antialiased;
|
| -moz-osx-font-smoothing: grayscale;
|
| text-rendering: geometricPrecision;
|
| overflow: hidden;
|
| }
|
|
|
| body {
|
| background-color: var(--bg-void);
|
| background-image:
|
| radial-gradient(ellipse 80% 50% at 50% 0%, rgba(30, 40, 80, 0.28) 0%, transparent 70%),
|
| radial-gradient(ellipse 60% 40% at 86% 90%, rgba(70, 45, 20, 0.08) 0%, transparent 60%),
|
| repeating-linear-gradient(0deg, transparent, transparent 79px, rgba(100, 120, 255, 0.012) 79px, rgba(100, 120, 255, 0.012) 80px),
|
| repeating-linear-gradient(90deg, transparent, transparent 79px, rgba(100, 120, 255, 0.012) 79px, rgba(100, 120, 255, 0.012) 80px);
|
| }
|
|
|
| ::selection {
|
| background: rgba(0, 212, 255, 0.28);
|
| color: #ffffff;
|
| }
|
|
|
| ::-webkit-scrollbar {
|
| width: 10px;
|
| height: 10px;
|
| }
|
|
|
| ::-webkit-scrollbar-track {
|
| background: rgba(255, 255, 255, 0.03);
|
| }
|
|
|
| ::-webkit-scrollbar-thumb {
|
| border: 2px solid rgba(2, 2, 8, 0.92);
|
| border-radius: 999px;
|
| background: rgba(142, 157, 190, 0.46);
|
| }
|
|
|
| ::-webkit-scrollbar-thumb:hover {
|
| background: rgba(0, 212, 255, 0.55);
|
| }
|
|
|
| button,
|
| input,
|
| textarea,
|
| select {
|
| font: inherit;
|
| }
|
|
|
| button {
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-sm);
|
| background: rgba(255, 255, 255, 0.035);
|
| color: var(--text-secondary);
|
| min-height: 34px;
|
| padding: 0 12px;
|
| cursor: pointer;
|
| font-family: var(--font-sans);
|
| font-size: 0.72rem;
|
| font-weight: 750;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| transition: color 0.18s ease, background 0.18s ease, border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
|
| }
|
|
|
| button:hover {
|
| color: var(--text-primary);
|
| border-color: var(--bg-panel-border-hot);
|
| background: rgba(77, 124, 255, 0.08);
|
| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
| }
|
|
|
| button:active {
|
| transform: translateY(1px);
|
| }
|
|
|
| button:disabled {
|
| opacity: 0.45;
|
| cursor: not-allowed;
|
| transform: none;
|
| }
|
|
|
| button.primary {
|
| color: #ffffff;
|
| border-color: rgba(77, 124, 255, 0.55);
|
| background: linear-gradient(135deg, rgba(58, 112, 255, 0.98), rgba(0, 196, 255, 0.76));
|
| box-shadow: 0 14px 40px rgba(77, 124, 255, 0.26);
|
| }
|
|
|
| label,
|
| .label-mono {
|
| display: block;
|
| font-family: var(--font-mono);
|
| font-weight: 700;
|
| font-size: 0.68rem;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| color: var(--text-secondary);
|
| }
|
|
|
| input[type="text"],
|
| textarea {
|
| width: 100%;
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-sm);
|
| background: var(--bg-input);
|
| color: var(--text-primary);
|
| outline: none;
|
| font-family: var(--font-mono);
|
| }
|
|
|
| input[type="text"] {
|
| height: 37px;
|
| padding: 0 12px;
|
| font-size: 0.76rem;
|
| }
|
|
|
| textarea {
|
| min-height: 210px;
|
| max-height: 28vh;
|
| resize: vertical;
|
| padding: 12px;
|
| line-height: 1.62;
|
| font-size: 0.75rem;
|
| }
|
|
|
| input:focus,
|
| textarea:focus {
|
| border-color: rgba(0, 212, 255, 0.42);
|
| background: rgba(0, 212, 255, 0.04);
|
| box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.055);
|
| }
|
|
|
| .app {
|
| height: 100vh;
|
| display: grid;
|
| grid-template-rows: 62px 1fr 38px;
|
| overflow: hidden;
|
| }
|
|
|
| .topbar,
|
| .statusbar {
|
| background: rgba(2, 2, 8, 0.88);
|
| backdrop-filter: blur(18px) saturate(130%);
|
| border-color: var(--bg-panel-border);
|
| display: flex;
|
| align-items: center;
|
| justify-content: space-between;
|
| flex-shrink: 0;
|
| }
|
|
|
| .topbar {
|
| border-bottom: 1px solid var(--bg-panel-border);
|
| padding: 0 20px;
|
| z-index: 20;
|
| }
|
|
|
| .statusbar {
|
| border-top: 1px solid var(--bg-panel-border);
|
| padding: 0 18px;
|
| font-family: var(--font-mono);
|
| font-size: 0.68rem;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| color: var(--text-tertiary);
|
| z-index: 20;
|
| }
|
|
|
| .brand {
|
| display: flex;
|
| align-items: center;
|
| gap: 12px;
|
| min-width: 0;
|
| }
|
|
|
| .brand-orb {
|
| width: 28px;
|
| height: 28px;
|
| border-radius: 50%;
|
| border: 1.5px solid var(--accent-orbital);
|
| background: var(--accent-orbital-dim);
|
| display: grid;
|
| place-items: center;
|
| box-shadow: 0 0 20px rgba(77, 124, 255, 0.14);
|
| }
|
|
|
| .brand-orb::before {
|
| content: "";
|
| width: 9px;
|
| height: 9px;
|
| border-radius: 50%;
|
| background: var(--accent-orbital);
|
| box-shadow: 0 0 14px var(--accent-orbital);
|
| }
|
|
|
| .brand-title {
|
| font-family: var(--font-mono);
|
| font-weight: 800;
|
| letter-spacing: 0;
|
| font-size: 0.88rem;
|
| color: var(--text-primary);
|
| white-space: nowrap;
|
| }
|
|
|
| .brand-sub {
|
| color: var(--text-tertiary);
|
| font-size: 0.76rem;
|
| white-space: nowrap;
|
| }
|
|
|
| .top-actions {
|
| display: flex;
|
| align-items: center;
|
| gap: 10px;
|
| flex-wrap: wrap;
|
| justify-content: flex-end;
|
| }
|
|
|
| .nav-pill {
|
| min-width: 86px;
|
| background: transparent;
|
| }
|
|
|
| .nav-pill.active {
|
| color: #ffffff;
|
| background: rgba(77, 124, 255, 0.18);
|
| border-color: rgba(77, 124, 255, 0.45);
|
| }
|
|
|
| .classification {
|
| color: var(--text-tertiary);
|
| font-family: var(--font-sans);
|
| font-size: 0.72rem;
|
| font-weight: 700;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| }
|
|
|
| .global-search {
|
| flex: 1;
|
| max-width: 360px;
|
| min-width: 220px;
|
| height: 38px;
|
| display: flex;
|
| align-items: center;
|
| gap: 9px;
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-sm);
|
| background: rgba(255, 255, 255, 0.035);
|
| padding: 0 10px;
|
| color: var(--text-tertiary);
|
| }
|
|
|
| .global-search input {
|
| min-width: 0;
|
| height: auto;
|
| border: 0;
|
| background: transparent;
|
| padding: 0;
|
| color: var(--text-secondary);
|
| font-family: var(--font-sans);
|
| font-size: 0.76rem;
|
| }
|
|
|
| .global-search input:focus {
|
| border: 0;
|
| box-shadow: none;
|
| background: transparent;
|
| }
|
|
|
| .global-search kbd {
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: 6px;
|
| padding: 2px 5px;
|
| color: var(--text-tertiary);
|
| font-family: var(--font-mono);
|
| font-size: 0.58rem;
|
| white-space: nowrap;
|
| }
|
|
|
| .layout {
|
| min-height: 0;
|
| display: grid;
|
| grid-template-columns: minmax(282px, 330px) minmax(0, 1fr) minmax(350px, 430px);
|
| overflow: hidden;
|
| }
|
|
|
| .glass-panel {
|
| background: var(--bg-panel);
|
| backdrop-filter: blur(18px) saturate(125%);
|
| border-color: var(--bg-panel-border);
|
| min-height: 0;
|
| overflow: hidden;
|
| }
|
|
|
| .left-panel {
|
| border-right: 1px solid var(--bg-panel-border);
|
| display: flex;
|
| flex-direction: column;
|
| overflow: auto;
|
| }
|
|
|
| .right-panel {
|
| border-left: 1px solid var(--bg-panel-border);
|
| display: flex;
|
| flex-direction: column;
|
| }
|
|
|
| .panel-section {
|
| padding: 17px 18px;
|
| border-bottom: 1px solid var(--bg-panel-border);
|
| }
|
|
|
| .panel-scroll {
|
| min-height: 0;
|
| overflow: auto;
|
| padding: 17px 18px;
|
| }
|
|
|
| .section-title {
|
| display: flex;
|
| align-items: center;
|
| gap: 9px;
|
| margin-bottom: 14px;
|
| }
|
|
|
| .section-dot {
|
| width: 7px;
|
| height: 7px;
|
| border-radius: 50%;
|
| background: var(--accent-orbital);
|
| box-shadow: 0 0 16px var(--accent-orbital);
|
| }
|
|
|
| .input-grid {
|
| display: grid;
|
| gap: 12px;
|
| }
|
|
|
| .paper-box {
|
| position: relative;
|
| }
|
|
|
| .input-actions {
|
| display: grid;
|
| grid-template-columns: 1fr 1fr 1fr;
|
| gap: 8px;
|
| }
|
|
|
| .api-grid {
|
| display: grid;
|
| gap: 8px;
|
| }
|
|
|
| .api-buttons {
|
| display: grid;
|
| grid-template-columns: 1fr 1fr;
|
| gap: 8px;
|
| }
|
|
|
| .metric-grid {
|
| display: grid;
|
| grid-template-columns: 1fr 1fr;
|
| gap: 9px;
|
| margin-top: 14px;
|
| }
|
|
|
| .data-card {
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-md);
|
| background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.022));
|
| padding: 11px 12px;
|
| }
|
|
|
| .data-card .k {
|
| color: var(--text-tertiary);
|
| font-family: var(--font-mono);
|
| font-size: 0.62rem;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| margin-bottom: 6px;
|
| }
|
|
|
| .data-card .v {
|
| color: var(--text-primary);
|
| font-family: var(--font-mono);
|
| font-size: 0.84rem;
|
| line-height: 1.32;
|
| overflow-wrap: anywhere;
|
| }
|
|
|
| .autonomy-wrap {
|
| display: grid;
|
| grid-template-columns: 62px 1fr;
|
| gap: 12px;
|
| align-items: center;
|
| }
|
|
|
| .autonomy-number {
|
| height: 62px;
|
| border-radius: var(--radius-md);
|
| border: 1px solid rgba(77, 124, 255, 0.32);
|
| background: linear-gradient(180deg, rgba(77, 124, 255, 0.18), rgba(77, 124, 255, 0.08));
|
| color: var(--accent-scan);
|
| display: grid;
|
| place-items: center;
|
| font-family: var(--font-mono);
|
| font-size: 1.65rem;
|
| font-weight: 800;
|
| box-shadow: inset 0 0 18px rgba(77, 124, 255, 0.08);
|
| }
|
|
|
| input[type="range"] {
|
| width: 100%;
|
| accent-color: var(--accent-orbital);
|
| }
|
|
|
| .ticks {
|
| display: grid;
|
| grid-template-columns: repeat(4, 1fr);
|
| color: var(--text-tertiary);
|
| font-family: var(--font-sans);
|
| font-size: 0.62rem;
|
| text-align: center;
|
| margin-top: 4px;
|
| }
|
|
|
| .mode-copy {
|
| color: var(--text-secondary);
|
| font-size: 0.74rem;
|
| line-height: 1.45;
|
| margin-top: 10px;
|
| }
|
|
|
| .run-action {
|
| width: 100%;
|
| height: 48px;
|
| font-size: 0.82rem;
|
| letter-spacing: 0;
|
| }
|
|
|
| .top-run {
|
| width: auto;
|
| min-width: 150px;
|
| height: 42px;
|
| padding: 0 20px;
|
| border-radius: 12px;
|
| font-size: 0.78rem;
|
| box-shadow: 0 14px 42px rgba(0, 128, 255, 0.26);
|
| }
|
|
|
| .error-box {
|
| display: none;
|
| margin-top: 10px;
|
| color: #fecdd3;
|
| border: 1px solid rgba(239, 68, 68, 0.26);
|
| background: rgba(239, 68, 68, 0.06);
|
| border-radius: var(--radius-sm);
|
| padding: 10px 11px;
|
| font-size: 0.75rem;
|
| line-height: 1.4;
|
| }
|
|
|
| .center {
|
| min-width: 0;
|
| min-height: 0;
|
| position: relative;
|
| display: grid;
|
| grid-template-rows: minmax(280px, 46vh) 36px 1fr;
|
| background: var(--bg-void);
|
| overflow: hidden;
|
| }
|
|
|
| .center.live-compact {
|
| grid-template-rows: 36px minmax(0, 1fr);
|
| }
|
|
|
| .runtime-state {
|
| display: none;
|
| }
|
|
|
| .mission-canvas {
|
| position: relative;
|
| overflow: hidden;
|
| background:
|
| radial-gradient(ellipse at center, rgba(18, 48, 86, 0.62) 0%, rgba(2, 2, 8, 0.92) 74%),
|
| radial-gradient(circle at 46% 45%, rgba(0, 212, 255, 0.08), transparent 34%),
|
| #071324;
|
| display: grid;
|
| place-items: center;
|
| isolation: isolate;
|
| }
|
|
|
| .canvas-grid {
|
| position: absolute;
|
| inset: 0;
|
| opacity: 0.12;
|
| background-image:
|
| linear-gradient(rgba(117, 167, 255, 0.08) 1px, transparent 1px),
|
| linear-gradient(90deg, rgba(117, 167, 255, 0.07) 1px, transparent 1px);
|
| background-size: 82px 82px;
|
| mask-image: radial-gradient(circle at center, black 0%, black 45%, transparent 78%);
|
| }
|
|
|
| .orbit-ring {
|
| position: absolute;
|
| width: 72vmin;
|
| height: 72vmin;
|
| border-radius: 50%;
|
| border: 1px solid rgba(77, 124, 255, 0.08);
|
| animation: orbitSpin 140s linear infinite;
|
| }
|
|
|
| .orbit-ring.two {
|
| width: 52vmin;
|
| height: 52vmin;
|
| border-color: rgba(0, 212, 255, 0.08);
|
| transform: rotate(28deg) scaleY(0.62);
|
| animation-duration: 180s;
|
| }
|
|
|
| .paper-target {
|
| position: relative;
|
| width: min(640px, 78%);
|
| border: 1px solid rgba(117, 167, 255, 0.16);
|
| border-radius: var(--radius-lg);
|
| background:
|
| linear-gradient(180deg, rgba(15, 31, 58, 0.92), rgba(5, 8, 16, 0.96)),
|
| #101824;
|
| box-shadow: var(--shadow-soft), inset 0 1px 0 rgba(237, 244, 248, 0.07);
|
| padding: 22px;
|
| z-index: 4;
|
| transform: none;
|
| }
|
|
|
| .paper-target::before {
|
| content: "";
|
| position: absolute;
|
| left: -1px;
|
| top: 26px;
|
| bottom: 26px;
|
| width: 3px;
|
| background: var(--accent-scan);
|
| box-shadow: 0 0 18px var(--accent-scan);
|
| }
|
|
|
| .target-header {
|
| display: flex;
|
| align-items: center;
|
| justify-content: space-between;
|
| gap: 14px;
|
| margin-bottom: 18px;
|
| }
|
|
|
| .target-title {
|
| font-family: var(--font-mono);
|
| letter-spacing: 0;
|
| font-size: 0.78rem;
|
| text-transform: uppercase;
|
| color: var(--text-primary);
|
| }
|
|
|
| .chip {
|
| display: inline-flex;
|
| align-items: center;
|
| min-height: 24px;
|
| border: 1px solid rgba(77, 124, 255, 0.3);
|
| border-radius: 999px;
|
| background: rgba(77, 124, 255, 0.09);
|
| color: var(--text-data);
|
| padding: 0 8px;
|
| font-family: var(--font-sans);
|
| font-size: 0.64rem;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| }
|
|
|
| .paper-lines {
|
| display: grid;
|
| gap: 9px;
|
| }
|
|
|
| .paper-line {
|
| height: 10px;
|
| border-radius: 999px;
|
| background: rgba(228, 230, 239, 0.12);
|
| }
|
|
|
| .paper-line:nth-child(1) { width: 92%; }
|
| .paper-line:nth-child(2) { width: 74%; }
|
| .paper-line:nth-child(3) { width: 86%; }
|
| .paper-line:nth-child(4) { width: 62%; }
|
|
|
| .resolver-nodes {
|
| display: grid;
|
| grid-template-columns: repeat(4, 1fr);
|
| gap: 10px;
|
| margin-top: 22px;
|
| }
|
|
|
| .resolver-node {
|
| border: 1px solid rgba(117, 167, 255, 0.12);
|
| border-radius: var(--radius-md);
|
| background: rgba(255, 255, 255, 0.04);
|
| padding: 9px;
|
| min-height: 64px;
|
| }
|
|
|
| .resolver-node .name {
|
| color: var(--text-tertiary);
|
| font-family: var(--font-mono);
|
| font-size: 0.58rem;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| margin-bottom: 8px;
|
| }
|
|
|
| .resolver-node .state {
|
| color: var(--text-secondary);
|
| font-family: var(--font-mono);
|
| font-size: 0.72rem;
|
| }
|
|
|
| .resolver-node.ok {
|
| border-color: rgba(124, 242, 183, 0.38);
|
| background: rgba(124, 242, 183, 0.045);
|
| }
|
|
|
| .resolver-node.ok .state {
|
| color: var(--verify);
|
| }
|
|
|
| .resolver-node.failed,
|
| .resolver-node.missing {
|
| border-color: rgba(239, 68, 68, 0.32);
|
| background: rgba(239, 68, 68, 0.045);
|
| }
|
|
|
| .resolver-node.failed .state,
|
| .resolver-node.missing .state {
|
| color: var(--critical);
|
| }
|
|
|
| .scan-line {
|
| position: absolute;
|
| left: 0;
|
| right: 0;
|
| height: 1px;
|
| background: linear-gradient(90deg, transparent 0%, var(--accent-scan) 15%, rgba(255, 255, 255, 0.9) 50%, var(--accent-scan) 85%, transparent 100%);
|
| box-shadow: 0 0 8px var(--accent-scan), 0 0 24px var(--accent-scan-glow), 0 -4px 16px rgba(0, 212, 255, 0.05), 0 4px 16px rgba(0, 212, 255, 0.05);
|
| animation: scanLine 3.4s ease-in-out infinite;
|
| pointer-events: none;
|
| z-index: 10;
|
| display: none;
|
| }
|
|
|
| .running .scan-line {
|
| display: block;
|
| }
|
|
|
| .canvas-strip {
|
| min-height: 36px;
|
| display: flex;
|
| align-items: center;
|
| justify-content: space-between;
|
| background: rgba(4, 17, 30, 0.95);
|
| border-top: 1px solid var(--bg-panel-border);
|
| border-bottom: 1px solid var(--bg-panel-border);
|
| padding: 0 12px;
|
| font-family: var(--font-sans);
|
| font-size: 0.64rem;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| }
|
|
|
| .strip-left,
|
| .strip-right {
|
| display: flex;
|
| align-items: center;
|
| gap: 14px;
|
| min-width: 0;
|
| }
|
|
|
| .strip-blue {
|
| color: var(--accent-scan);
|
| }
|
|
|
| .strip-green {
|
| color: var(--verify);
|
| }
|
|
|
| .strip-amber {
|
| color: var(--warning);
|
| }
|
|
|
| .console-bottom {
|
| min-height: 0;
|
| display: grid;
|
| grid-template-columns: minmax(0, 1fr) minmax(280px, 38%);
|
| overflow: hidden;
|
| }
|
|
|
| .trace-panel,
|
| .evidence-panel {
|
| min-height: 0;
|
| overflow: hidden;
|
| display: flex;
|
| flex-direction: column;
|
| background: rgba(2, 2, 8, 0.64);
|
| }
|
|
|
| .trace-panel {
|
| border-right: 1px solid var(--bg-panel-border);
|
| }
|
|
|
| .subhead {
|
| height: 42px;
|
| display: flex;
|
| align-items: center;
|
| justify-content: space-between;
|
| padding: 0 14px;
|
| border-bottom: 1px solid var(--bg-panel-border);
|
| }
|
|
|
| .trace-list,
|
| .evidence-list {
|
| min-height: 0;
|
| overflow: auto;
|
| padding: 10px 12px;
|
| display: grid;
|
| gap: 8px;
|
| align-content: start;
|
| }
|
|
|
| .trace-item,
|
| .evidence-item {
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-md);
|
| background: linear-gradient(180deg, rgba(13, 17, 32, 0.84), rgba(8, 10, 20, 0.72));
|
| padding: 9px 10px;
|
| animation: slideIn 0.22s ease-out both;
|
| }
|
|
|
| .trace-item {
|
| border-left: 2px solid var(--accent-orbital);
|
| }
|
|
|
| .trace-item.tool_call,
|
| .trace-item.tool_request_validation {
|
| border-left-color: var(--accent-scan);
|
| }
|
|
|
| .trace-item.verifier_repair_requested,
|
| .trace-item.error {
|
| border-left-color: var(--critical);
|
| }
|
|
|
| .trace-meta {
|
| display: flex;
|
| flex-wrap: wrap;
|
| gap: 9px;
|
| color: var(--text-tertiary);
|
| font-family: var(--font-sans);
|
| font-size: 0.58rem;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| margin-bottom: 5px;
|
| }
|
|
|
| .trace-message,
|
| .evidence-title {
|
| color: var(--text-primary);
|
| font-size: 0.78rem;
|
| line-height: 1.42;
|
| }
|
|
|
| details.payload {
|
| margin-top: 7px;
|
| }
|
|
|
| details.payload summary {
|
| cursor: pointer;
|
| color: var(--text-tertiary);
|
| font-family: var(--font-sans);
|
| font-size: 0.6rem;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| }
|
|
|
| details.payload pre {
|
| margin: 7px 0 0;
|
| max-height: 150px;
|
| overflow: auto;
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-sm);
|
| background: rgba(0, 0, 0, 0.35);
|
| padding: 8px;
|
| color: var(--text-secondary);
|
| font-family: var(--font-mono);
|
| font-size: 0.65rem;
|
| line-height: 1.45;
|
| }
|
|
|
| .evidence-meta {
|
| display: flex;
|
| align-items: center;
|
| justify-content: space-between;
|
| gap: 10px;
|
| margin-bottom: 6px;
|
| }
|
|
|
| .evidence-link {
|
| display: block;
|
| color: var(--accent-scan);
|
| text-decoration: none;
|
| overflow-wrap: anywhere;
|
| font-family: var(--font-mono);
|
| font-size: 0.65rem;
|
| margin-top: 6px;
|
| }
|
|
|
| .agent-feed {
|
| display: grid;
|
| gap: 2px;
|
| }
|
|
|
| .agent-row {
|
| display: grid;
|
| grid-template-columns: 36px 112px minmax(0, 1fr);
|
| align-items: center;
|
| gap: 8px;
|
| min-height: 38px;
|
| padding: 6px 10px;
|
| border-left: 2px solid transparent;
|
| border-radius: var(--radius-sm);
|
| color: var(--text-tertiary);
|
| transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease;
|
| }
|
|
|
| .agent-row.active {
|
| background: rgba(0, 212, 255, 0.045);
|
| color: var(--text-primary);
|
| }
|
|
|
| .agent-row.done {
|
| color: var(--text-primary);
|
| }
|
|
|
| .agent-index {
|
| display: flex;
|
| align-items: center;
|
| gap: 7px;
|
| font-family: var(--font-mono);
|
| font-size: 0.7rem;
|
| font-weight: 800;
|
| }
|
|
|
| .agent-dot {
|
| width: 8px;
|
| height: 8px;
|
| border-radius: 50%;
|
| border: 1px solid var(--text-tertiary);
|
| }
|
|
|
| .agent-row.active .agent-dot {
|
| animation: agentPulse 1.2s ease-in-out infinite;
|
| }
|
|
|
| .agent-name {
|
| font-family: var(--font-mono);
|
| font-weight: 800;
|
| font-size: 0.72rem;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| }
|
|
|
| .agent-state {
|
| color: inherit;
|
| font-size: 0.72rem;
|
| white-space: nowrap;
|
| overflow: hidden;
|
| text-overflow: ellipsis;
|
| }
|
|
|
| .report-card {
|
| margin-top: 16px;
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-md);
|
| background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.026));
|
| padding: 13px;
|
| }
|
|
|
| .report-tabs {
|
| display: grid;
|
| grid-template-columns: repeat(3, 1fr);
|
| gap: 6px;
|
| margin-bottom: 10px;
|
| }
|
|
|
| .report-tabs button.active {
|
| color: white;
|
| background: rgba(77, 124, 255, 0.16);
|
| border-color: rgba(77, 124, 255, 0.38);
|
| }
|
|
|
| .report-output {
|
| max-height: 34vh;
|
| overflow: auto;
|
| margin: 0;
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-sm);
|
| background: rgba(0, 0, 0, 0.28);
|
| padding: 11px;
|
| white-space: pre-wrap;
|
| color: var(--text-primary);
|
| font-family: var(--font-mono);
|
| font-size: 0.68rem;
|
| line-height: 1.5;
|
| }
|
|
|
| .rubric-grid,
|
| .gap-list,
|
| .tool-list {
|
| display: grid;
|
| gap: 8px;
|
| }
|
|
|
| .bar-row {
|
| display: grid;
|
| gap: 5px;
|
| }
|
|
|
| .bar-label {
|
| display: flex;
|
| justify-content: space-between;
|
| gap: 8px;
|
| color: var(--text-secondary);
|
| font-size: 0.7rem;
|
| }
|
|
|
| .bar-track {
|
| height: 6px;
|
| border-radius: 999px;
|
| background: rgba(255, 255, 255, 0.08);
|
| overflow: hidden;
|
| }
|
|
|
| .bar-fill {
|
| height: 100%;
|
| width: 0;
|
| background: linear-gradient(90deg, var(--accent-orbital), var(--accent-scan));
|
| }
|
|
|
| .list-item {
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-sm);
|
| background: rgba(255, 255, 255, 0.035);
|
| padding: 9px 10px;
|
| color: var(--text-secondary);
|
| font-size: 0.73rem;
|
| line-height: 1.42;
|
| overflow-wrap: anywhere;
|
| }
|
|
|
| .list-item strong {
|
| display: block;
|
| color: var(--text-primary);
|
| margin-bottom: 4px;
|
| font-family: var(--font-mono);
|
| font-size: 0.7rem;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| }
|
|
|
| .status-chip {
|
| display: inline-flex;
|
| align-items: center;
|
| justify-content: center;
|
| min-height: 24px;
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: 999px;
|
| padding: 0 8px;
|
| color: var(--text-tertiary);
|
| font-family: var(--font-sans);
|
| font-size: 0.6rem;
|
| font-weight: 800;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| white-space: nowrap;
|
| }
|
|
|
| .status-chip.ok {
|
| color: var(--verify);
|
| border-color: rgba(124, 242, 183, 0.28);
|
| background: rgba(124, 242, 183, 0.06);
|
| }
|
|
|
| .status-chip.bad {
|
| color: var(--critical);
|
| border-color: rgba(239, 68, 68, 0.3);
|
| background: rgba(239, 68, 68, 0.06);
|
| }
|
|
|
| .workspace-shell {
|
| min-height: 0;
|
| overflow: auto;
|
| padding: 24px;
|
| background:
|
| radial-gradient(ellipse 70% 50% at 70% 10%, rgba(77, 124, 255, 0.12), transparent 65%),
|
| radial-gradient(circle at 12% 88%, rgba(0, 212, 255, 0.07), transparent 34%),
|
| var(--bg-void);
|
| }
|
|
|
| .workspace-view {
|
| max-width: 1480px;
|
| margin: 0 auto;
|
| display: none;
|
| animation: slideIn 0.24s ease-out both;
|
| }
|
|
|
| .workspace-view.active {
|
| display: block;
|
| }
|
|
|
| .workspace-header {
|
| display: flex;
|
| justify-content: space-between;
|
| gap: 20px;
|
| align-items: end;
|
| margin-bottom: 20px;
|
| }
|
|
|
| .workspace-kicker {
|
| color: var(--accent-scan);
|
| font-family: var(--font-mono);
|
| font-size: 0.68rem;
|
| font-weight: 800;
|
| letter-spacing: 0;
|
| text-transform: uppercase;
|
| margin-bottom: 8px;
|
| }
|
|
|
| .workspace-title {
|
| margin: 0;
|
| color: var(--text-primary);
|
| font-family: var(--font-sans);
|
| font-size: clamp(1.6rem, 3vw, 2.7rem);
|
| line-height: 1.04;
|
| letter-spacing: 0;
|
| }
|
|
|
| .workspace-subtitle {
|
| margin: 10px 0 0;
|
| max-width: 760px;
|
| color: var(--text-secondary);
|
| font-size: 0.94rem;
|
| }
|
|
|
| .view-actions {
|
| display: flex;
|
| flex-wrap: wrap;
|
| gap: 10px;
|
| justify-content: flex-end;
|
| }
|
|
|
| .feature-grid {
|
| display: grid;
|
| grid-template-columns: repeat(12, minmax(0, 1fr));
|
| gap: 16px;
|
| }
|
|
|
| .feature-card {
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-lg);
|
| background:
|
| linear-gradient(180deg, rgba(255, 255, 255, 0.058), rgba(255, 255, 255, 0.026)),
|
| rgba(7, 9, 18, 0.88);
|
| box-shadow: var(--shadow-soft);
|
| padding: 18px;
|
| overflow: hidden;
|
| }
|
|
|
| .feature-card.compact {
|
| border-radius: var(--radius-md);
|
| padding: 14px;
|
| }
|
|
|
| .span-3 { grid-column: span 3; }
|
| .span-4 { grid-column: span 4; }
|
| .span-5 { grid-column: span 5; }
|
| .span-6 { grid-column: span 6; }
|
| .span-7 { grid-column: span 7; }
|
| .span-8 { grid-column: span 8; }
|
| .span-12 { grid-column: 1 / -1; }
|
|
|
| .hero-audit {
|
| min-height: 0;
|
| display: grid;
|
| align-content: start;
|
| gap: 14px;
|
| position: relative;
|
| background:
|
| radial-gradient(circle at 70% 34%, rgba(0, 212, 255, 0.18), transparent 32%),
|
| linear-gradient(135deg, rgba(12, 28, 54, 0.96), rgba(5, 6, 16, 0.95));
|
| }
|
|
|
| .hero-audit::before {
|
| content: "";
|
| position: absolute;
|
| inset: 18px;
|
| border: 1px solid rgba(0, 212, 255, 0.16);
|
| border-radius: 18px;
|
| background-image:
|
| linear-gradient(rgba(125, 211, 252, 0.07) 1px, transparent 1px),
|
| linear-gradient(90deg, rgba(125, 211, 252, 0.06) 1px, transparent 1px);
|
| background-size: 38px 38px;
|
| mask-image: linear-gradient(135deg, transparent 0%, black 22%, black 70%, transparent 100%);
|
| opacity: 0.5;
|
| pointer-events: none;
|
| }
|
|
|
| .hero-audit > * {
|
| position: relative;
|
| z-index: 1;
|
| }
|
|
|
| .dashboard-live-grid {
|
| display: grid;
|
| grid-template-columns: minmax(0, 1.05fr) minmax(280px, 0.95fr);
|
| gap: 14px;
|
| align-items: stretch;
|
| position: relative;
|
| z-index: 1;
|
| }
|
|
|
| .dashboard-copy {
|
| display: grid;
|
| gap: 12px;
|
| align-content: start;
|
| }
|
|
|
| .dashboard-copy .workspace-title {
|
| font-size: clamp(1.28rem, 2.1vw, 2.05rem);
|
| line-height: 1.12;
|
| }
|
|
|
| .dashboard-stream-panel {
|
| border: 1px solid rgba(117, 167, 255, 0.16);
|
| border-radius: var(--radius-md);
|
| background: rgba(0, 0, 0, 0.22);
|
| padding: 12px;
|
| min-height: 300px;
|
| display: grid;
|
| grid-template-rows: auto 1fr;
|
| min-width: 0;
|
| }
|
|
|
| .dashboard-stream-panel .subhead {
|
| height: auto;
|
| min-height: 34px;
|
| padding: 0 0 10px;
|
| border-bottom-color: rgba(117, 167, 255, 0.12);
|
| }
|
|
|
| .dashboard-trace-list,
|
| .dashboard-agent-feed {
|
| padding: 10px 0 0;
|
| max-height: 280px;
|
| overflow: auto;
|
| }
|
|
|
| .dashboard-agent-feed {
|
| display: grid;
|
| align-content: start;
|
| gap: 4px;
|
| }
|
|
|
| .metric-row {
|
| display: grid;
|
| grid-template-columns: repeat(4, minmax(0, 1fr));
|
| gap: 12px;
|
| }
|
|
|
| .mini-stat {
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-md);
|
| background: rgba(255, 255, 255, 0.035);
|
| padding: 13px;
|
| }
|
|
|
| .mini-stat .value {
|
| color: var(--text-primary);
|
| font-family: var(--font-mono);
|
| font-size: 1.35rem;
|
| font-weight: 850;
|
| }
|
|
|
| .mini-stat .label {
|
| margin-top: 4px;
|
| color: var(--text-tertiary);
|
| font-size: 0.68rem;
|
| text-transform: uppercase;
|
| }
|
|
|
| .table-panel {
|
| display: grid;
|
| gap: 8px;
|
| }
|
|
|
| .table-head,
|
| .audit-row {
|
| display: grid;
|
| grid-template-columns: minmax(220px, 1.7fr) 0.7fr 0.6fr 0.8fr;
|
| gap: 12px;
|
| align-items: center;
|
| }
|
|
|
| .table-head {
|
| color: var(--text-tertiary);
|
| font-family: var(--font-mono);
|
| font-size: 0.62rem;
|
| text-transform: uppercase;
|
| padding: 0 8px 4px;
|
| }
|
|
|
| .audit-row {
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-md);
|
| background: rgba(255, 255, 255, 0.035);
|
| padding: 11px 12px;
|
| cursor: pointer;
|
| transition: transform 0.16s ease, border-color 0.16s ease, background 0.16s ease;
|
| }
|
|
|
| .audit-row:hover {
|
| transform: translateY(-1px);
|
| border-color: rgba(0, 212, 255, 0.28);
|
| background: rgba(0, 212, 255, 0.04);
|
| }
|
|
|
| .audit-title {
|
| color: var(--text-primary);
|
| font-weight: 760;
|
| line-height: 1.28;
|
| }
|
|
|
| .audit-meta {
|
| margin-top: 4px;
|
| color: var(--text-tertiary);
|
| font-family: var(--font-mono);
|
| font-size: 0.64rem;
|
| }
|
|
|
| .score-pill {
|
| display: inline-grid;
|
| place-items: center;
|
| width: 52px;
|
| height: 52px;
|
| border-radius: 50%;
|
| border: 1px solid rgba(124, 242, 183, 0.34);
|
| background: radial-gradient(circle, rgba(124, 242, 183, 0.13), rgba(124, 242, 183, 0.03));
|
| color: var(--verify);
|
| font-family: var(--font-mono);
|
| font-weight: 850;
|
| }
|
|
|
| .score-pill.warn {
|
| border-color: rgba(251, 191, 36, 0.34);
|
| background: radial-gradient(circle, rgba(251, 191, 36, 0.14), rgba(251, 191, 36, 0.03));
|
| color: var(--warning);
|
| }
|
|
|
| .score-pill.bad {
|
| border-color: rgba(239, 68, 68, 0.34);
|
| background: radial-gradient(circle, rgba(239, 68, 68, 0.12), rgba(239, 68, 68, 0.03));
|
| color: #fecdd3;
|
| }
|
|
|
| .form-grid {
|
| display: grid;
|
| grid-template-columns: repeat(2, minmax(0, 1fr));
|
| gap: 14px;
|
| }
|
|
|
| .form-field {
|
| display: grid;
|
| gap: 7px;
|
| }
|
|
|
| .form-field select,
|
| .form-field input[type="url"] {
|
| width: 100%;
|
| height: 38px;
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-sm);
|
| background: var(--bg-input);
|
| color: var(--text-primary);
|
| padding: 0 11px;
|
| outline: none;
|
| }
|
|
|
| .switch-list {
|
| display: grid;
|
| gap: 9px;
|
| }
|
|
|
| .switch-row {
|
| display: flex;
|
| justify-content: space-between;
|
| gap: 12px;
|
| align-items: center;
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-sm);
|
| background: rgba(255, 255, 255, 0.028);
|
| padding: 10px;
|
| color: var(--text-secondary);
|
| font-size: 0.76rem;
|
| }
|
|
|
| .switch-row input {
|
| accent-color: var(--accent-orbital);
|
| }
|
|
|
| .segmented {
|
| display: inline-grid;
|
| grid-auto-flow: column;
|
| gap: 5px;
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-sm);
|
| padding: 5px;
|
| background: rgba(255, 255, 255, 0.03);
|
| }
|
|
|
| .segmented button.on {
|
| color: white;
|
| background: rgba(77, 124, 255, 0.2);
|
| border-color: rgba(77, 124, 255, 0.42);
|
| }
|
|
|
| .radar-wrap {
|
| min-height: 340px;
|
| display: grid;
|
| place-items: center;
|
| }
|
|
|
| .radar-wrap svg {
|
| max-width: 100%;
|
| height: auto;
|
| overflow: visible;
|
| }
|
|
|
| .axis-list {
|
| display: grid;
|
| gap: 8px;
|
| }
|
|
|
| .axis-button {
|
| display: grid;
|
| grid-template-columns: 1fr 48px;
|
| gap: 10px;
|
| align-items: center;
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-sm);
|
| background: rgba(255, 255, 255, 0.035);
|
| padding: 10px;
|
| text-align: left;
|
| }
|
|
|
| .axis-button.active {
|
| border-color: rgba(0, 212, 255, 0.34);
|
| background: rgba(0, 212, 255, 0.055);
|
| }
|
|
|
| .result-tabs {
|
| display: flex;
|
| flex-wrap: wrap;
|
| gap: 8px;
|
| margin-bottom: 12px;
|
| }
|
|
|
| .result-tabs button.active {
|
| color: white;
|
| background: rgba(77, 124, 255, 0.16);
|
| border-color: rgba(77, 124, 255, 0.4);
|
| }
|
|
|
| .artifact-grid,
|
| .library-grid,
|
| .benchmark-grid {
|
| display: grid;
|
| grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
| gap: 12px;
|
| }
|
|
|
| .artifact-card,
|
| .library-card,
|
| .benchmark-card {
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-md);
|
| background: rgba(255, 255, 255, 0.034);
|
| padding: 13px;
|
| }
|
|
|
| .library-card {
|
| cursor: pointer;
|
| transition: transform 0.16s ease, border-color 0.16s ease;
|
| }
|
|
|
| .library-card:hover {
|
| transform: translateY(-2px);
|
| border-color: rgba(0, 212, 255, 0.3);
|
| }
|
|
|
| .bar-chart {
|
| display: grid;
|
| gap: 10px;
|
| }
|
|
|
| .chart-row {
|
| display: grid;
|
| grid-template-columns: 76px 1fr 48px;
|
| align-items: center;
|
| gap: 10px;
|
| color: var(--text-secondary);
|
| font-size: 0.72rem;
|
| }
|
|
|
| .chart-bar {
|
| height: 10px;
|
| border-radius: 999px;
|
| background: rgba(255, 255, 255, 0.08);
|
| overflow: hidden;
|
| }
|
|
|
| .chart-fill {
|
| height: 100%;
|
| width: 0;
|
| border-radius: inherit;
|
| background: linear-gradient(90deg, var(--accent-orbital), var(--accent-scan));
|
| }
|
|
|
| .copilot-drawer {
|
| position: fixed;
|
| top: 74px;
|
| right: 16px;
|
| bottom: 50px;
|
| width: min(390px, calc(100vw - 32px));
|
| z-index: 40;
|
| border: 1px solid rgba(117, 167, 255, 0.18);
|
| border-radius: var(--radius-lg);
|
| background: rgba(6, 7, 15, 0.96);
|
| backdrop-filter: blur(18px) saturate(130%);
|
| box-shadow: 0 30px 90px rgba(0, 0, 0, 0.54);
|
| display: grid;
|
| grid-template-rows: auto 1fr auto;
|
| overflow: hidden;
|
| }
|
|
|
| .copilot-head,
|
| .copilot-foot {
|
| padding: 14px;
|
| border-bottom: 1px solid var(--bg-panel-border);
|
| }
|
|
|
| .copilot-foot {
|
| border-top: 1px solid var(--bg-panel-border);
|
| border-bottom: 0;
|
| display: grid;
|
| grid-template-columns: 1fr auto;
|
| gap: 8px;
|
| }
|
|
|
| .copilot-body {
|
| min-height: 0;
|
| overflow: auto;
|
| padding: 14px;
|
| display: grid;
|
| gap: 10px;
|
| align-content: start;
|
| }
|
|
|
| .copilot-msg {
|
| border: 1px solid var(--bg-panel-border);
|
| border-radius: var(--radius-md);
|
| background: rgba(255, 255, 255, 0.035);
|
| padding: 10px;
|
| color: var(--text-secondary);
|
| font-size: 0.76rem;
|
| line-height: 1.44;
|
| }
|
|
|
| .copilot-msg.user {
|
| border-color: rgba(77, 124, 255, 0.32);
|
| background: rgba(77, 124, 255, 0.08);
|
| color: var(--text-primary);
|
| }
|
|
|
| .suggestion-grid {
|
| display: grid;
|
| gap: 8px;
|
| grid-template-columns: 1fr 1fr;
|
| margin-top: 10px;
|
| }
|
|
|
| .view-hidden {
|
| display: none;
|
| }
|
|
|
| .empty {
|
| color: var(--text-tertiary);
|
| font-size: 0.74rem;
|
| line-height: 1.45;
|
| padding: 8px 0;
|
| }
|
|
|
| .hidden {
|
| display: none;
|
| }
|
|
|
| @keyframes scanLine {
|
| 0% { top: -2px; opacity: 0; }
|
| 6% { opacity: 0.85; }
|
| 94% { opacity: 0.85; }
|
| 100% { top: 100%; opacity: 0; }
|
| }
|
|
|
| @keyframes orbitSpin {
|
| from { rotate: 0deg; }
|
| to { rotate: 360deg; }
|
| }
|
|
|
| @keyframes agentPulse {
|
| 0%, 100% { transform: scale(1); opacity: 1; }
|
| 50% { transform: scale(1.55); opacity: 0.58; }
|
| }
|
|
|
| @keyframes slideIn {
|
| from { transform: translateX(12px); opacity: 0; }
|
| to { transform: none; opacity: 1; }
|
| }
|
|
|
| @media (max-width: 1500px) {
|
| .classification {
|
| display: none;
|
| }
|
|
|
| .nav-pill {
|
| min-width: 74px;
|
| padding: 0 8px;
|
| }
|
|
|
| .top-run {
|
| min-width: 128px;
|
| }
|
| }
|
|
|
| @media (max-width: 1180px) {
|
| html,
|
| body {
|
| overflow: auto;
|
| }
|
|
|
| .app {
|
| height: auto;
|
| min-height: 100vh;
|
| grid-template-rows: auto auto auto;
|
| }
|
|
|
| .topbar,
|
| .statusbar {
|
| min-height: 54px;
|
| flex-wrap: wrap;
|
| gap: 10px;
|
| }
|
|
|
| .layout {
|
| grid-template-columns: 1fr;
|
| overflow: visible;
|
| }
|
|
|
| .workspace-shell {
|
| padding: 16px;
|
| }
|
|
|
| .feature-grid,
|
| .form-grid,
|
| .metric-row {
|
| grid-template-columns: 1fr;
|
| }
|
|
|
| .span-3,
|
| .span-4,
|
| .span-5,
|
| .span-6,
|
| .span-7,
|
| .span-8,
|
| .span-12 {
|
| grid-column: 1 / -1;
|
| }
|
|
|
| .workspace-header,
|
| .table-head,
|
| .audit-row {
|
| grid-template-columns: 1fr;
|
| }
|
|
|
| .dashboard-live-grid {
|
| grid-template-columns: 1fr;
|
| }
|
|
|
| .left-panel,
|
| .right-panel {
|
| border: 0;
|
| }
|
|
|
| .center {
|
| min-height: 900px;
|
| }
|
| }
|
|
|
| @media (max-width: 720px) {
|
| .topbar {
|
| padding: 12px;
|
| }
|
|
|
| .global-search {
|
| order: 3;
|
| max-width: none;
|
| width: 100%;
|
| }
|
|
|
| .brand {
|
| align-items: flex-start;
|
| }
|
|
|
| .brand-sub,
|
| .classification {
|
| display: none;
|
| }
|
|
|
| .top-actions {
|
| width: 100%;
|
| display: grid;
|
| grid-template-columns: repeat(2, minmax(0, 1fr));
|
| }
|
|
|
| .layout {
|
| grid-template-columns: 1fr;
|
| }
|
|
|
| .console-bottom,
|
| .resolver-nodes,
|
| .input-actions,
|
| .metric-grid {
|
| grid-template-columns: 1fr;
|
| }
|
| }
|
| </style>
|
| </head>
|
| <body>
|
| <div id="app" class="app">
|
| <header class="topbar">
|
| <div class="brand">
|
| <div class="brand-orb" aria-hidden="true"></div>
|
| <div>
|
| <span class="brand-title">AGENTIC ENGINE</span>
|
| <span class="brand-sub">Research Reproducibility Intelligence</span>
|
| </div>
|
| </div>
|
| <div class="top-actions">
|
| <button id="runBtn" class="primary run-action top-run" type="button">Run Audit</button>
|
| <button id="stopBtn" class="nav-pill" type="button" disabled>Stop</button>
|
| <button class="nav-pill active" data-view="dashboard" type="button">Dashboard</button>
|
| <button class="nav-pill" data-view="new" type="button">New</button>
|
| <button class="nav-pill" data-view="live" type="button">Live Audit</button>
|
| <button class="nav-pill" data-view="results" type="button">Results</button>
|
| <button class="nav-pill" data-view="library" type="button">Library</button>
|
| <button class="nav-pill" data-view="benchmarks" type="button">Benchmarks</button>
|
| <button id="copilotBtn" class="nav-pill" type="button">Copilot</button>
|
| </div>
|
| </header>
|
|
|
| <main id="liveView" class="layout view-hidden">
|
| <aside class="left-panel glass-panel">
|
| <section class="panel-section">
|
| <div class="section-title">
|
| <span class="section-dot"></span>
|
| <span class="label-mono">Paper Acquisition</span>
|
| </div>
|
| <div class="input-grid">
|
| <div class="paper-box">
|
| <label for="paperInput">Target paper</label>
|
| <textarea id="paperInput" spellcheck="true"></textarea>
|
| </div>
|
| <input id="fileInput" class="hidden" type="file" accept=".txt,.md,.tex,.bib,.json,.csv">
|
| <div class="input-actions">
|
| <button id="sampleBtn" type="button">Sample</button>
|
| <button id="fileBtn" type="button">Load</button>
|
| <button id="clearBtn" type="button">Clear</button>
|
| </div>
|
| </div>
|
| </section>
|
|
|
| <section class="panel-section">
|
| <div class="section-title">
|
| <span class="section-dot"></span>
|
| <span class="label-mono">Agent API</span>
|
| </div>
|
| <div class="api-grid">
|
| <input id="apiBase" type="text" spellcheck="false" placeholder="http://127.0.0.1:8080">
|
| <div class="api-buttons">
|
| <button id="saveApi" type="button">Save</button>
|
| <button id="healthBtn" type="button">Health</button>
|
| </div>
|
| <span id="healthStatus" class="status-chip">not checked</span>
|
| </div>
|
| </section>
|
|
|
| <section class="panel-section">
|
| <div class="section-title">
|
| <span class="section-dot"></span>
|
| <span class="label-mono">Autonomy</span>
|
| </div>
|
| <div class="autonomy-wrap">
|
| <div id="autonomyValue" class="autonomy-number">2</div>
|
| <div>
|
| <input id="autonomySlider" type="range" min="0" max="3" step="1" value="2">
|
| <div class="ticks"><span>L0</span><span>L1</span><span>L2</span><span>L3</span></div>
|
| </div>
|
| </div>
|
| <p id="modeCopy" class="mode-copy"></p>
|
| <div id="errorBox" class="error-box"></div>
|
| </section>
|
|
|
| <section class="panel-scroll">
|
| <div class="section-title">
|
| <span class="section-dot"></span>
|
| <span class="label-mono">Run Telemetry</span>
|
| </div>
|
| <div class="metric-grid">
|
| <div class="data-card"><div class="k">Run ID</div><div id="runId" class="v">idle</div></div>
|
| <div class="data-card"><div class="k">Timer</div><div id="runTimer" class="v">00:00</div></div>
|
| <div class="data-card"><div class="k">Score</div><div id="score" class="v">--</div></div>
|
| <div class="data-card"><div class="k">Decision</div><div id="decision" class="v">-</div></div>
|
| </div>
|
| </section>
|
| </aside>
|
|
|
| <section id="center" class="center live-compact">
|
| <div class="runtime-state" aria-hidden="true">
|
| <span id="modelId">mock-qwen3.5-27b</span>
|
| <span id="runStatus">Standby</span>
|
| <div id="resolverLanes"></div>
|
| </div>
|
|
|
| <div class="canvas-strip">
|
| <div class="strip-left">
|
| <span class="strip-blue">Audit Pipeline</span>
|
| <span id="traceCount">0 events</span>
|
| <span id="evidenceCount" class="strip-green">0 evidence</span>
|
| </div>
|
| <div class="strip-right">
|
| <span id="canvasScore" class="strip-amber">score pending</span>
|
| <button id="downloadBtn" type="button">JSON</button>
|
| </div>
|
| </div>
|
|
|
| <div class="console-bottom">
|
| <div class="trace-panel">
|
| <div class="subhead">
|
| <span class="label-mono">Analysis Trace</span>
|
| <span class="status-chip">SSE + REST</span>
|
| </div>
|
| <div id="traceList" class="trace-list">
|
| <div class="empty">No trace events yet.</div>
|
| </div>
|
| </div>
|
| <div class="evidence-panel">
|
| <div class="subhead">
|
| <span class="label-mono">Evidence</span>
|
| <span class="status-chip">live resolvers</span>
|
| </div>
|
| <div id="evidenceRows" class="evidence-list">
|
| <div class="empty">No live evidence resolved yet.</div>
|
| </div>
|
| </div>
|
| </div>
|
| </section>
|
|
|
| <aside class="right-panel glass-panel">
|
| <section class="panel-section">
|
| <div class="section-title">
|
| <span class="section-dot" style="background:#ff7d90;box-shadow:0 0 16px rgba(255,125,144,0.7);"></span>
|
| <span class="label-mono">Intelligence Report</span>
|
| </div>
|
| <div id="agentFeed" class="agent-feed"></div>
|
| </section>
|
|
|
| <section class="panel-scroll">
|
| <div class="report-card">
|
| <div class="report-tabs">
|
| <button id="tabReport" class="active" type="button">Report</button>
|
| <button id="tabRubric" type="button">Rubric</button>
|
| <button id="tabTools" type="button">Tools</button>
|
| </div>
|
| <pre id="report" class="report-output">Run an audit to generate the verifier-backed report.</pre>
|
| <div id="rubricPane" class="rubric-grid hidden"></div>
|
| <div id="toolsPane" class="tool-list hidden"></div>
|
| </div>
|
|
|
| <div class="report-card">
|
| <div class="label-mono" style="margin-bottom:10px;">Verifier Watchlist</div>
|
| <div id="gapsList" class="gap-list">
|
| <div class="empty">Verifier gaps will appear after scoring.</div>
|
| </div>
|
| </div>
|
|
|
| <div class="report-card">
|
| <div class="label-mono" style="margin-bottom:10px;">Run JSON</div>
|
| <pre id="rawJson" class="report-output" style="max-height:220px;">{}</pre>
|
| </div>
|
| </section>
|
| </aside>
|
| </main>
|
|
|
| <main id="workspaceShell" class="workspace-shell">
|
| <section id="dashboardView" class="workspace-view active">
|
| <div class="workspace-header">
|
| <div>
|
| <div class="workspace-kicker">Workspace dashboard</div>
|
| <h1 class="workspace-title">Agentic Reproducibility Engine</h1>
|
| <p class="workspace-subtitle">Submit papers, monitor multi-agent audits, inspect resolver-backed evidence, and package audit-ready reproducibility reports from one operational workspace.</p>
|
| </div>
|
| <div class="view-actions">
|
| <button id="dashboardNewBtn" class="primary" type="button">New Audit</button>
|
| <button id="dashboardRunBtn" type="button">Resume Live Audit</button>
|
| </div>
|
| </div>
|
|
|
| <div class="feature-grid">
|
| <article class="feature-card hero-audit span-7">
|
| <div class="dashboard-live-grid">
|
| <div class="dashboard-copy">
|
| <div class="workspace-kicker">Evidence-first audit orchestration</div>
|
| <h2 class="workspace-title">The engine turns a paper into validated evidence, reproducibility risk scores, consistency challenges, and an audit-ready report.</h2>
|
| <p class="workspace-subtitle">Every run streams structured reasoning artifacts, approved tool calls, resolver outputs, critique loops, and report provenance as the workflow executes.</p>
|
| <div class="view-actions" style="justify-content:flex-start;">
|
| <button class="primary" data-action="run-current" type="button">Run Current Paper</button>
|
| <button data-view="results" type="button">Open Results</button>
|
| </div>
|
| <div id="dashboardAgentFeed" class="dashboard-agent-feed agent-feed"></div>
|
| </div>
|
| <div class="dashboard-stream-panel">
|
| <div class="subhead">
|
| <span class="label-mono">Live Execution Stream</span>
|
| <span class="status-chip">SSE</span>
|
| </div>
|
| <div id="dashboardTraceList" class="trace-list dashboard-trace-list">
|
| <div class="empty">Run an audit to stream model calls, tool calls, consistency checks, and report assembly here.</div>
|
| </div>
|
| </div>
|
| </div>
|
| </article>
|
|
|
| <aside class="feature-card span-5">
|
| <div class="section-title"><span class="section-dot"></span><span class="label-mono">Autonomy</span></div>
|
| <div class="autonomy-wrap">
|
| <div id="dashboardAutonomyValue" class="autonomy-number">2</div>
|
| <div>
|
| <input id="dashboardAutonomySlider" type="range" min="0" max="3" step="1" value="2">
|
| <div class="ticks"><span>L0</span><span>L1</span><span>L2</span><span>L3</span></div>
|
| </div>
|
| </div>
|
| <p id="dashboardModeCopy" class="mode-copy"></p>
|
| <div class="report-card" style="margin-top:14px;">
|
| <div class="label-mono" style="margin-bottom:10px;">Current Run Control</div>
|
| <div class="view-actions" style="justify-content:flex-start;">
|
| <button class="primary" data-action="run-current" type="button">Run Audit</button>
|
| <button id="dashboardStopBtn" type="button" disabled>Stop Audit</button>
|
| </div>
|
| </div>
|
| <div class="section-title"><span class="section-dot"></span><span class="label-mono">Runtime Snapshot</span></div>
|
| <div id="dashboardMetrics" class="metric-row"></div>
|
| <div class="report-card" style="margin-top:14px;">
|
| <div class="label-mono" style="margin-bottom:10px;">Autonomy Contract</div>
|
| <div class="switch-list">
|
| <div class="switch-row"><span>Each agent receives its own prompt and context.</span><span class="status-chip ok">active</span></div>
|
| <div class="switch-row"><span>Each agent makes a model call.</span><span class="status-chip ok">active</span></div>
|
| <div class="switch-row"><span>Agents request tools through the orchestrator.</span><span class="status-chip ok">gated</span></div>
|
| <div class="switch-row"><span>Verifier can send work back before final report.</span><span class="status-chip ok">enabled</span></div>
|
| </div>
|
| </div>
|
| </aside>
|
|
|
| <article class="feature-card span-8">
|
| <div class="workspace-header" style="margin-bottom:12px;">
|
| <div>
|
| <div class="workspace-kicker">Recent audits</div>
|
| <h2 class="target-title">Library preview</h2>
|
| </div>
|
| <button data-view="library" type="button">Open Library</button>
|
| </div>
|
| <div id="recentAuditRows" class="table-panel"></div>
|
| </article>
|
|
|
| <article class="feature-card span-4">
|
| <div class="section-title"><span class="section-dot"></span><span class="label-mono">Resolver Coverage</span></div>
|
| <div class="bar-chart" id="dashboardResolverBars"></div>
|
| </article>
|
| </div>
|
| </section>
|
|
|
| <section id="newView" class="workspace-view">
|
| <div class="workspace-header">
|
| <div>
|
| <div class="workspace-kicker">New audit</div>
|
| <h1 class="workspace-title">Submit a paper and configure autonomy</h1>
|
| <p class="workspace-subtitle">Paste an arXiv ID, DOI, URL, or paper notes. The frontend fills the live audit payload and the backend handles the agent/tool execution.</p>
|
| </div>
|
| <div class="view-actions">
|
| <button id="newAuditStart" class="primary" type="button">Start Autonomous Audit</button>
|
| <button id="saveBatchBtn" type="button">Save Batch Draft</button>
|
| </div>
|
| </div>
|
|
|
| <div class="feature-grid">
|
| <article class="feature-card span-7">
|
| <div class="form-grid">
|
| <div class="form-field">
|
| <label for="newAuditReference">Paper reference</label>
|
| <input id="newAuditReference" type="text" spellcheck="false" value="2006.11239" placeholder="arXiv ID, DOI, URL, or title">
|
| </div>
|
| <div class="form-field">
|
| <label for="newAuditTitle">Working title</label>
|
| <input id="newAuditTitle" type="text" value="Denoising Diffusion Probabilistic Models" placeholder="Optional title">
|
| </div>
|
| <div class="form-field">
|
| <label for="newAuditModel">Model endpoint</label>
|
| <select id="newAuditModel">
|
| <option>Environment default</option>
|
| <option>Qwen3.5-27B via vLLM</option>
|
| <option>Gemini API test runtime</option>
|
| <option>Mock runtime</option>
|
| </select>
|
| </div>
|
| <div class="form-field">
|
| <label for="newAuditDepth">Audit depth</label>
|
| <select id="newAuditDepth">
|
| <option value="quick">Quick evidence pass</option>
|
| <option value="full" selected>Full reproducibility audit</option>
|
| <option value="forensic">Forensic verifier pass</option>
|
| </select>
|
| </div>
|
| <div class="form-field span-12">
|
| <label for="newAuditNotes">Paper notes, repo, dataset, or claims</label>
|
| <textarea id="newAuditNotes" spellcheck="true" style="max-height:none;min-height:220px;">Code: https://github.com/hojonathanho/diffusion
|
| Data: CIFAR-10, LSUN, CelebA-HQ
|
|
|
| Audit focus: verify dataset availability, code provenance, experiment detail, and reproduction scaffold feasibility.</textarea>
|
| </div>
|
| </div>
|
| </article>
|
|
|
| <aside class="feature-card span-5">
|
| <div class="section-title"><span class="section-dot"></span><span class="label-mono">Autonomy</span></div>
|
| <div class="autonomy-wrap">
|
| <div id="newAutonomyValue" class="autonomy-number">2</div>
|
| <div>
|
| <input id="newAutonomySlider" type="range" min="0" max="3" step="1" value="2">
|
| <div class="ticks"><span>L0</span><span>L1</span><span>L2</span><span>L3</span></div>
|
| </div>
|
| </div>
|
| <p id="newModeCopy" class="mode-copy"></p>
|
| <div class="report-card" style="margin-top:14px;">
|
| <div class="label-mono" style="margin-bottom:10px;">Autonomy Contract</div>
|
| <div class="switch-list">
|
| <div class="switch-row"><span>Separate model turns for each role.</span><span class="status-chip ok">active</span></div>
|
| <div class="switch-row"><span>Tool requests are validated before execution.</span><span class="status-chip ok">gated</span></div>
|
| <div class="switch-row"><span>Verifier can trigger targeted repair work.</span><span class="status-chip ok">enabled</span></div>
|
| </div>
|
| </div>
|
| <div class="section-title"><span class="section-dot"></span><span class="label-mono">Live Evidence Sources</span></div>
|
| <div class="switch-list">
|
| <label class="switch-row"><span>arXiv metadata and paper records</span><input class="source-toggle" type="checkbox" value="arxiv" checked></label>
|
| <label class="switch-row"><span>DOI / Crossref resolver</span><input class="source-toggle" type="checkbox" value="doi" checked></label>
|
| <label class="switch-row"><span>GitHub repository resolver</span><input class="source-toggle" type="checkbox" value="github" checked></label>
|
| <label class="switch-row"><span>Dataset resolver</span><input class="source-toggle" type="checkbox" value="dataset" checked></label>
|
| <label class="switch-row"><span>Verifier repair loop</span><input id="newAuditVerifier" type="checkbox" checked></label>
|
| </div>
|
| <div class="report-card">
|
| <div class="label-mono" style="margin-bottom:10px;">Input actions</div>
|
| <div class="view-actions" style="justify-content:flex-start;">
|
| <button id="newUseSample" type="button">Use Sample</button>
|
| <button id="newLoadFile" type="button">Load File</button>
|
| <button id="newViewArxiv" type="button">View arXiv</button>
|
| </div>
|
| </div>
|
| </aside>
|
| </div>
|
| </section>
|
|
|
| <section id="resultsView" class="workspace-view">
|
| <div class="workspace-header">
|
| <div>
|
| <div class="workspace-kicker">Audit results</div>
|
| <h1 id="resultsTitle" class="workspace-title">Verifier-backed reproducibility report</h1>
|
| <p id="resultsSubtitle" class="workspace-subtitle">Run an audit to replace this with live model, evidence, and report data.</p>
|
| </div>
|
| <div class="view-actions">
|
| <button id="exportPdfBtn" type="button">Export PDF</button>
|
| <button id="shareRunBtn" type="button">Share</button>
|
| <button id="askCopilotFromResults" class="primary" type="button">Ask Copilot</button>
|
| </div>
|
| </div>
|
|
|
| <div class="feature-grid">
|
| <article class="feature-card span-5">
|
| <div class="radar-wrap" id="radarChart"></div>
|
| </article>
|
| <article class="feature-card span-3">
|
| <div id="resultsSummary"></div>
|
| <div class="axis-list" id="axisList" style="margin-top:14px;"></div>
|
| </article>
|
| <article class="feature-card span-4">
|
| <div class="section-title"><span class="section-dot"></span><span class="label-mono">Evidence Explorer</span></div>
|
| <div id="resultsEvidence" class="evidence-list" style="padding:0;max-height:340px;"></div>
|
| </article>
|
|
|
| <article class="feature-card span-12">
|
| <div class="result-tabs">
|
| <button class="active" data-results-tab="report" type="button">Report</button>
|
| <button data-results-tab="evidence" type="button">Evidence Explorer</button>
|
| <button data-results-tab="scaffold" type="button">Replication Scaffold</button>
|
| <button data-results-tab="methods" type="button">Method Notes</button>
|
| </div>
|
| <div id="resultTabContent"></div>
|
| </article>
|
| </div>
|
| </section>
|
|
|
| <section id="libraryView" class="workspace-view">
|
| <div class="workspace-header">
|
| <div>
|
| <div class="workspace-kicker">Audit library</div>
|
| <h1 class="workspace-title">Saved audit runs</h1>
|
| <p class="workspace-subtitle">Search completed local runs and saved drafts by title, arXiv ID, decision, or evidence source.</p>
|
| </div>
|
| <div class="view-actions">
|
| <button id="exportCsvBtn" type="button">Export CSV</button>
|
| <button data-view="new" class="primary" type="button">New Audit</button>
|
| </div>
|
| </div>
|
| <div class="feature-card">
|
| <div class="workspace-header" style="margin-bottom:14px;align-items:center;">
|
| <div class="global-search" style="max-width:420px;flex:none;">
|
| <span aria-hidden="true">Search</span>
|
| <input id="librarySearch" type="text" spellcheck="false" placeholder="Search title, author, arXiv id">
|
| </div>
|
| <div class="segmented" id="tierFilter">
|
| <button class="on" data-tier="all" type="button">All</button>
|
| <button data-tier="1" type="button">Tier 1</button>
|
| <button data-tier="2" type="button">Tier 2</button>
|
| <button data-tier="3" type="button">Tier 3</button>
|
| </div>
|
| </div>
|
| <div id="libraryGrid" class="library-grid"></div>
|
| </div>
|
| </section>
|
|
|
| <section id="benchmarksView" class="workspace-view">
|
| <div class="workspace-header">
|
| <div>
|
| <div class="workspace-kicker">Benchmarks and insights</div>
|
| <h1 class="workspace-title">Evidence discipline for reproducibility scoring</h1>
|
| <p class="workspace-subtitle">Benchmarks expose resolver coverage, score distribution, tool availability, and AMD/Qwen deployment proof points for judges.</p>
|
| </div>
|
| <div class="view-actions">
|
| <button data-view="results" type="button">Open Results</button>
|
| <button data-view="live" class="primary" type="button">Live Trace</button>
|
| </div>
|
| </div>
|
| <div class="feature-grid">
|
| <article class="feature-card span-5">
|
| <div class="section-title"><span class="section-dot"></span><span class="label-mono">Score Distribution</span></div>
|
| <div id="benchmarkBars" class="bar-chart"></div>
|
| </article>
|
| <article class="feature-card span-7">
|
| <div class="benchmark-grid" id="benchmarkCards"></div>
|
| </article>
|
| <article class="feature-card span-12">
|
| <div class="section-title"><span class="section-dot"></span><span class="label-mono">Tool and deployment readiness</span></div>
|
| <div class="artifact-grid" id="resolverMatrix"></div>
|
| </article>
|
| </div>
|
| </section>
|
| </main>
|
|
|
| <aside id="copilotDrawer" class="copilot-drawer hidden" aria-label="Copilot">
|
| <div class="copilot-head">
|
| <div class="workspace-header" style="margin:0;align-items:center;">
|
| <div>
|
| <div class="workspace-kicker">Copilot</div>
|
| <div id="copilotContext" class="target-title">Workspace assistant</div>
|
| </div>
|
| <button id="closeCopilot" type="button">Close</button>
|
| </div>
|
| <div class="suggestion-grid">
|
| <button data-copilot="Why did the score change?" type="button">Score Reason</button>
|
| <button data-copilot="What should I fix first?" type="button">Fix First</button>
|
| <button data-copilot="Draft a reproducibility statement." type="button">Statement</button>
|
| <button data-copilot="Summarize resolver evidence." type="button">Evidence</button>
|
| </div>
|
| </div>
|
| <div id="copilotThread" class="copilot-body"></div>
|
| <div class="copilot-foot">
|
| <input id="copilotInput" type="text" spellcheck="true" placeholder="Ask about this audit">
|
| <button id="sendCopilot" class="primary" type="button">Send</button>
|
| </div>
|
| </aside>
|
|
|
| <footer class="statusbar">
|
| <div><span style="color:var(--accent-orbital);">AGENTIC ENGINE</span> <span>v0.1</span></div>
|
| <div><span>LangGraph-style runtime</span> / <span>Qwen endpoint ready</span> / <span>tool-gated autonomy</span></div>
|
| <div id="footerStatus">standby</div>
|
| </footer>
|
| </div>
|
|
|
| <script>
|
| const DEMO_PAPER = `# Denoising Diffusion Probabilistic Models
|
|
|
| arXiv: 2006.11239
|
| DOI: 10.48550/arXiv.2006.11239
|
| Code: https://github.com/hojonathanho/diffusion
|
| Data: CIFAR-10, LSUN, CelebA-HQ
|
|
|
| The paper evaluates a diffusion-based generative modeling approach against image generation
|
| benchmarks and reports sample quality, likelihood estimates, and ablation results.`;
|
|
|
| const AUTONOMY = {
|
| 0: "LOCAL TRACE: agents emit structured artifacts; external tools remain blocked.",
|
| 1: "MODEL ANALYSIS: parser plus model-backed agents, no network retrieval.",
|
| 2: "LIVE EVIDENCE: arXiv, DOI, GitHub, and dataset resolvers enabled.",
|
| 3: "FULL AUTONOMY: live evidence, verifier repair, and code/data planning enabled."
|
| };
|
|
|
| const AGENTS = [
|
| ["planner", "PLANNER", "var(--agent-planner)"],
|
| ["paper_reader", "READER", "var(--agent-reader)"],
|
| ["evidence_retriever", "RETRIEVER", "var(--agent-retriever)"],
|
| ["reproducibility_auditor", "AUDITOR", "var(--agent-auditor)"],
|
| ["experiment_planner", "EXPERIMENT", "var(--agent-experiment)"],
|
| ["code_data_agent", "CODE DATA", "var(--agent-code)"],
|
| ["verifier", "VERIFIER", "var(--agent-verifier)"],
|
| ["report_agent", "REPORT", "var(--agent-report)"]
|
| ];
|
|
|
| const RESOLVERS = [
|
| ["arxiv_resolver", "ARXIV"],
|
| ["doi_resolver", "DOI"],
|
| ["github_resolver", "GITHUB"],
|
| ["dataset_resolver", "DATASET"]
|
| ];
|
|
|
| const DEFAULT_AXES = [
|
| { label: "Data Assets", score: 78 },
|
| { label: "Software", score: 75 },
|
| { label: "Methods", score: 68 },
|
| { label: "Compute", score: 71 },
|
| { label: "Statistics", score: 82 },
|
| { label: "Environment", score: 64 },
|
| { label: "Provenance", score: 86 },
|
| { label: "Robustness", score: 58 }
|
| ];
|
|
|
| const BENCHMARK_BUCKETS = [
|
| { label: "90-100", count: 9 },
|
| { label: "80-89", count: 28 },
|
| { label: "70-79", count: 42 },
|
| { label: "60-69", count: 35 },
|
| { label: "<60", count: 18 }
|
| ];
|
|
|
| const READINESS_CARDS = [
|
| { title: "arXiv resolver", status: "live", body: "Uses arXiv identifiers and paper metadata as source-grounded evidence." },
|
| { title: "DOI resolver", status: "live", body: "Checks DOI-style citations and normalizes DOI URLs into report provenance." },
|
| { title: "GitHub resolver", status: "live", body: "Resolves repository URLs, names, license signals, and reachable code evidence." },
|
| { title: "Dataset resolver", status: "live", body: "Flags named public datasets and dataset repository links for audit scoring." },
|
| { title: "Qwen/vLLM target", status: "deployable", body: "Frontend keeps an endpoint contract for AMD MI300X Qwen serving through the Agent API." },
|
| { title: "Gemini local test", status: "online", body: "Current local backend can run model-backed agents through the Gemini API key." }
|
| ];
|
|
|
| const SUPPRESSED_LIBRARY_IDS = new Set(["2006.11239", "2403.18012", "2302.13971", "2103.00020"]);
|
| const SUPPRESSED_LIBRARY_TITLES = ["denoising diffusion probabilistic models"];
|
|
|
| const els = {
|
| app: document.getElementById("app"),
|
| liveView: document.getElementById("liveView"),
|
| workspaceShell: document.getElementById("workspaceShell"),
|
| center: document.getElementById("center"),
|
| apiBase: document.getElementById("apiBase"),
|
| saveApi: document.getElementById("saveApi"),
|
| healthBtn: document.getElementById("healthBtn"),
|
| healthStatus: document.getElementById("healthStatus"),
|
| paperInput: document.getElementById("paperInput"),
|
| fileInput: document.getElementById("fileInput"),
|
| fileBtn: document.getElementById("fileBtn"),
|
| sampleBtn: document.getElementById("sampleBtn"),
|
| clearBtn: document.getElementById("clearBtn"),
|
| autonomySlider: document.getElementById("autonomySlider"),
|
| autonomyValue: document.getElementById("autonomyValue"),
|
| modeCopy: document.getElementById("modeCopy"),
|
| runBtn: document.getElementById("runBtn"),
|
| stopBtn: document.getElementById("stopBtn"),
|
| errorBox: document.getElementById("errorBox"),
|
| runId: document.getElementById("runId"),
|
| runTimer: document.getElementById("runTimer"),
|
| score: document.getElementById("score"),
|
| canvasScore: document.getElementById("canvasScore"),
|
| decision: document.getElementById("decision"),
|
| modelId: document.getElementById("modelId"),
|
| runStatus: document.getElementById("runStatus"),
|
| traceCount: document.getElementById("traceCount"),
|
| evidenceCount: document.getElementById("evidenceCount"),
|
| resolverLanes: document.getElementById("resolverLanes"),
|
| traceList: document.getElementById("traceList"),
|
| evidenceRows: document.getElementById("evidenceRows"),
|
| agentFeed: document.getElementById("agentFeed"),
|
| report: document.getElementById("report"),
|
| rawJson: document.getElementById("rawJson"),
|
| rubricPane: document.getElementById("rubricPane"),
|
| toolsPane: document.getElementById("toolsPane"),
|
| gapsList: document.getElementById("gapsList"),
|
| tabReport: document.getElementById("tabReport"),
|
| tabRubric: document.getElementById("tabRubric"),
|
| tabTools: document.getElementById("tabTools"),
|
| downloadBtn: document.getElementById("downloadBtn"),
|
| dashboardNewBtn: document.getElementById("dashboardNewBtn"),
|
| dashboardRunBtn: document.getElementById("dashboardRunBtn"),
|
| dashboardStopBtn: document.getElementById("dashboardStopBtn"),
|
| dashboardAutonomySlider: document.getElementById("dashboardAutonomySlider"),
|
| dashboardAutonomyValue: document.getElementById("dashboardAutonomyValue"),
|
| dashboardModeCopy: document.getElementById("dashboardModeCopy"),
|
| dashboardAgentFeed: document.getElementById("dashboardAgentFeed"),
|
| dashboardTraceList: document.getElementById("dashboardTraceList"),
|
| dashboardMetrics: document.getElementById("dashboardMetrics"),
|
| dashboardResolverBars: document.getElementById("dashboardResolverBars"),
|
| recentAuditRows: document.getElementById("recentAuditRows"),
|
| newAutonomySlider: document.getElementById("newAutonomySlider"),
|
| newAutonomyValue: document.getElementById("newAutonomyValue"),
|
| newModeCopy: document.getElementById("newModeCopy"),
|
| newAuditReference: document.getElementById("newAuditReference"),
|
| newAuditTitle: document.getElementById("newAuditTitle"),
|
| newAuditModel: document.getElementById("newAuditModel"),
|
| newAuditDepth: document.getElementById("newAuditDepth"),
|
| newAuditNotes: document.getElementById("newAuditNotes"),
|
| newAuditStart: document.getElementById("newAuditStart"),
|
| saveBatchBtn: document.getElementById("saveBatchBtn"),
|
| newUseSample: document.getElementById("newUseSample"),
|
| newLoadFile: document.getElementById("newLoadFile"),
|
| newViewArxiv: document.getElementById("newViewArxiv"),
|
| resultsTitle: document.getElementById("resultsTitle"),
|
| resultsSubtitle: document.getElementById("resultsSubtitle"),
|
| resultsSummary: document.getElementById("resultsSummary"),
|
| radarChart: document.getElementById("radarChart"),
|
| axisList: document.getElementById("axisList"),
|
| resultsEvidence: document.getElementById("resultsEvidence"),
|
| resultTabContent: document.getElementById("resultTabContent"),
|
| exportPdfBtn: document.getElementById("exportPdfBtn"),
|
| shareRunBtn: document.getElementById("shareRunBtn"),
|
| askCopilotFromResults: document.getElementById("askCopilotFromResults"),
|
| librarySearch: document.getElementById("librarySearch"),
|
| tierFilter: document.getElementById("tierFilter"),
|
| libraryGrid: document.getElementById("libraryGrid"),
|
| exportCsvBtn: document.getElementById("exportCsvBtn"),
|
| benchmarkBars: document.getElementById("benchmarkBars"),
|
| benchmarkCards: document.getElementById("benchmarkCards"),
|
| resolverMatrix: document.getElementById("resolverMatrix"),
|
| copilotBtn: document.getElementById("copilotBtn"),
|
| copilotDrawer: document.getElementById("copilotDrawer"),
|
| closeCopilot: document.getElementById("closeCopilot"),
|
| copilotContext: document.getElementById("copilotContext"),
|
| copilotThread: document.getElementById("copilotThread"),
|
| copilotInput: document.getElementById("copilotInput"),
|
| sendCopilot: document.getElementById("sendCopilot"),
|
| footerStatus: document.getElementById("footerStatus")
|
| };
|
|
|
| let latestResult = null;
|
| let traceEvents = [];
|
| let agentState = Object.fromEntries(AGENTS.map(([id]) => [id, { status: "queued", message: "STANDBY" }]));
|
| let timer = null;
|
| let startedAt = 0;
|
| let currentView = "dashboard";
|
| let selectedAxis = 1;
|
| let selectedResultsTab = "report";
|
| let libraryTier = "all";
|
| let activeRunAbort = null;
|
|
|
| function defaultApiBase() {
|
| const params = new URLSearchParams(window.location.search);
|
| const queryApi = params.get("api");
|
| if (queryApi) {
|
| localStorage.setItem("agenticEngine.apiBase", queryApi);
|
| return queryApi;
|
| }
|
| const stored = localStorage.getItem("agenticEngine.apiBase");
|
| if (stored) return stored;
|
| if (window.location.protocol === "file:") return "http://localhost:8080";
|
| if (window.location.hostname.endsWith(".hf.space")) return "";
|
| return window.location.origin;
|
| }
|
|
|
| function cleanBase(value) {
|
| return value.trim().replace(/\/+$/, "");
|
| }
|
|
|
| function updateAutonomy() {
|
| const level = Number(this && this.value !== undefined ? this.value : autonomyLevel());
|
| setAutonomyLevel(level);
|
| }
|
|
|
| function autonomyLevel() {
|
| return Number(els.dashboardAutonomySlider.value || els.newAutonomySlider.value || els.autonomySlider.value || 2);
|
| }
|
|
|
| function setAutonomyLevel(level) {
|
| const normalized = String(clamp(Number(level), 0, 3));
|
| els.autonomySlider.value = normalized;
|
| els.dashboardAutonomySlider.value = normalized;
|
| els.newAutonomySlider.value = normalized;
|
| els.autonomyValue.textContent = normalized;
|
| els.dashboardAutonomyValue.textContent = normalized;
|
| els.newAutonomyValue.textContent = normalized;
|
| els.modeCopy.textContent = AUTONOMY[Number(normalized)];
|
| els.dashboardModeCopy.textContent = AUTONOMY[Number(normalized)];
|
| els.newModeCopy.textContent = AUTONOMY[Number(normalized)];
|
| }
|
|
|
| function setHealth(label, state) {
|
| els.healthStatus.textContent = label;
|
| els.healthStatus.className = `status-chip ${state || ""}`.trim();
|
| }
|
|
|
| function showError(message) {
|
| els.errorBox.textContent = message;
|
| els.errorBox.style.display = message ? "block" : "none";
|
| }
|
|
|
| function renderAgentFeed() {
|
| const html = AGENTS.map(([id, label, color], index) => {
|
| const state = agentState[id] || { status: "queued", message: "STANDBY" };
|
| const cls = state.status === "complete" ? "done" : state.status === "thinking" ? "active" : "";
|
| const dotStyle = state.status === "queued"
|
| ? "border-color:var(--text-tertiary);background:transparent;"
|
| : `border-color:${color};background:${color};box-shadow:0 0 8px ${color};`;
|
| return `<div class="agent-row ${cls}" style="border-left-color:${state.status === "queued" ? "transparent" : color};">
|
| <div class="agent-index"><span>${String(index + 1).padStart(2, "0")}</span><span class="agent-dot" style="${dotStyle}"></span></div>
|
| <div class="agent-name">${escapeHtml(label)}</div>
|
| <div class="agent-state">${escapeHtml(state.message || "STANDBY")}</div>
|
| </div>`;
|
| }).join("");
|
| els.agentFeed.innerHTML = html;
|
| els.dashboardAgentFeed.innerHTML = html;
|
| }
|
|
|
| function renderResolverLanes(statusMap) {
|
| els.resolverLanes.innerHTML = RESOLVERS.map(([id, label]) => {
|
| const status = statusMap[id] || "pending";
|
| return `<div class="resolver-node ${escapeAttr(status)}">
|
| <div class="name">${escapeHtml(label)}</div>
|
| <div class="state">${escapeHtml(status)}</div>
|
| </div>`;
|
| }).join("");
|
| }
|
|
|
| function resetRunView() {
|
| latestResult = null;
|
| traceEvents = [];
|
| agentState = Object.fromEntries(AGENTS.map(([id]) => [id, { status: "queued", message: "STANDBY" }]));
|
| els.runId.textContent = "queued";
|
| els.score.textContent = "--";
|
| els.canvasScore.textContent = "score pending";
|
| els.decision.textContent = "-";
|
| els.runStatus.textContent = "RUNNING";
|
| els.traceCount.textContent = "0 events";
|
| els.evidenceCount.textContent = "0 evidence";
|
| els.traceList.innerHTML = '<div class="empty">Waiting for first model or tool event.</div>';
|
| els.dashboardTraceList.innerHTML = '<div class="empty">Waiting for first model or tool event.</div>';
|
| els.evidenceRows.innerHTML = '<div class="empty">No live evidence resolved yet.</div>';
|
| els.report.textContent = "Agents are running.";
|
| els.rawJson.textContent = "{}";
|
| els.rubricPane.innerHTML = '<div class="empty">Rubric checks are pending.</div>';
|
| els.toolsPane.innerHTML = '<div class="empty">No tool calls yet.</div>';
|
| els.gapsList.innerHTML = '<div class="empty">Verifier gaps will appear after scoring.</div>';
|
| renderAgentFeed();
|
| renderResolverLanes({});
|
| showError("");
|
| }
|
|
|
| function startTimer() {
|
| startedAt = Date.now();
|
| stopTimer();
|
| timer = setInterval(() => {
|
| const seconds = Math.floor((Date.now() - startedAt) / 1000);
|
| els.runTimer.textContent = `${String(Math.floor(seconds / 60)).padStart(2, "0")}:${String(seconds % 60).padStart(2, "0")}`;
|
| }, 500);
|
| }
|
|
|
| function stopTimer() {
|
| if (timer) clearInterval(timer);
|
| timer = null;
|
| }
|
|
|
| function setRunning(running) {
|
| els.runBtn.disabled = running;
|
| els.healthBtn.disabled = running;
|
| els.stopBtn.disabled = !running;
|
| els.dashboardStopBtn.disabled = !running;
|
| els.center.classList.toggle("running", running);
|
| els.runBtn.textContent = running ? "Running Audit" : "Run Audit";
|
| els.footerStatus.textContent = running ? "agent pipeline running" : "standby";
|
| if (running) startTimer();
|
| else stopTimer();
|
| }
|
|
|
| function stopAudit() {
|
| if (activeRunAbort) {
|
| activeRunAbort.abort();
|
| activeRunAbort = null;
|
| }
|
| els.runStatus.textContent = "STOPPED";
|
| els.footerStatus.textContent = "audit stopped";
|
| els.report.textContent = "Audit stopped by user before final report generation.";
|
| appendTrace({
|
| event_type: "audit_stopped",
|
| actor: "orchestrator",
|
| message: "User stopped the active audit stream.",
|
| created_at: new Date().toISOString(),
|
| payload: {}
|
| });
|
| setRunning(false);
|
| }
|
|
|
| function markAgent(event) {
|
| const actor = event.actor || "";
|
| if (agentState[actor]) {
|
| agentState[actor] = {
|
| status: event.event_type === "agent_output" ? "complete" : "thinking",
|
| message: event.event_type === "agent_output" ? "Complete" : event.message || "Processing"
|
| };
|
| }
|
| if (event.event_type === "tool_call" && actor === "evidence_bundle") {
|
| agentState.evidence_retriever = { status: "complete", message: "Evidence bundled" };
|
| }
|
| renderAgentFeed();
|
| }
|
|
|
| function appendTrace(event) {
|
| traceEvents.push(event);
|
| markAgent(event);
|
| els.traceCount.textContent = `${traceEvents.length} events`;
|
| if (traceEvents.length === 1) els.traceList.innerHTML = "";
|
| if (traceEvents.length === 1) els.dashboardTraceList.innerHTML = "";
|
| const item = createTraceItem(event);
|
| const dashboardItem = createTraceItem(event);
|
| els.traceList.appendChild(item);
|
| els.dashboardTraceList.appendChild(dashboardItem);
|
| if (els.dashboardTraceList.childElementCount > 18 && els.dashboardTraceList.firstElementChild) {
|
| els.dashboardTraceList.firstElementChild.remove();
|
| }
|
| els.traceList.scrollTop = els.traceList.scrollHeight;
|
| els.dashboardTraceList.scrollTop = els.dashboardTraceList.scrollHeight;
|
| const payload = event.payload || {};
|
| if (payload.manifest && payload.manifest.run_id) {
|
| els.runId.textContent = payload.manifest.run_id;
|
| }
|
| }
|
|
|
| function createTraceItem(event) {
|
| const payload = event.payload || {};
|
| const hasPayload = Object.keys(payload).length > 0;
|
| const item = document.createElement("article");
|
| item.className = `trace-item ${event.event_type || ""}`;
|
| item.innerHTML = `<div class="trace-meta">
|
| <span>${escapeHtml(event.event_type || "event")}</span>
|
| <span>${escapeHtml(event.actor || "agent")}</span>
|
| <span>${timeOnly(event.created_at)}</span>
|
| </div>
|
| <div class="trace-message">${escapeHtml(event.message || "")}</div>
|
| ${hasPayload ? `<details class="payload"><summary>payload</summary><pre>${escapeHtml(JSON.stringify(payload, null, 2))}</pre></details>` : ""}`;
|
| return item;
|
| }
|
|
|
| function renderResult(result) {
|
| latestResult = result;
|
| const manifest = result.manifest || {};
|
| const artifacts = result.artifacts || {};
|
| const scorecard = artifacts.audit_scorecard || {};
|
| const evidenceBundle = artifacts.evidence_bundle || {};
|
| const evidence = evidenceBundle.evidence || [];
|
| const score = Number(scorecard.score);
|
|
|
| els.runId.textContent = manifest.run_id || "-";
|
| els.score.textContent = Number.isFinite(score) ? `${score}/100` : "--";
|
| els.canvasScore.textContent = Number.isFinite(score) ? `score ${score}/100` : "score pending";
|
| els.decision.textContent = scorecard.decision || manifest.status || "-";
|
| els.modelId.textContent = manifest.model_id || "model pending";
|
| els.runStatus.textContent = String(scorecard.decision || manifest.status || "complete").toUpperCase();
|
| els.evidenceCount.textContent = `${evidence.length} evidence`;
|
| els.report.textContent = result.report_markdown || "No report returned.";
|
| els.rawJson.textContent = JSON.stringify(result, null, 2);
|
|
|
| if (!traceEvents.length && Array.isArray(result.trace)) result.trace.forEach(appendTrace);
|
| renderEvidence(evidence);
|
| renderResolverLanes(evidenceBundle.resolver_status || {});
|
| renderRubric(scorecard.rubric || {});
|
| renderGaps(scorecard.gaps || evidenceBundle.missing || []);
|
| renderTools(result.tools || []);
|
| renderAgentsComplete(result.agents || []);
|
| persistRun(result);
|
| renderWorkspaceViews();
|
| setRunning(false);
|
| }
|
|
|
| function renderAgentsComplete(agents) {
|
| for (const agent of agents) {
|
| if (agentState[agent.agent_id]) {
|
| agentState[agent.agent_id] = { status: "complete", message: agent.summary || "Complete" };
|
| }
|
| }
|
| renderAgentFeed();
|
| }
|
|
|
| function renderEvidence(evidence) {
|
| if (!evidence.length) {
|
| els.evidenceRows.innerHTML = '<div class="empty">No live resolver evidence was verified.</div>';
|
| return;
|
| }
|
| els.evidenceRows.innerHTML = evidence.map((item) => {
|
| const source = item.source_type || item.resolver || "source";
|
| const confidence = item.confidence || item.status || "-";
|
| const title = item.title || item.id || item.full_name || item.doi || item.arxiv_id || "-";
|
| const url = item.url || item.html_url || item.doi_url || "";
|
| return `<article class="evidence-item">
|
| <div class="evidence-meta"><span class="status-chip ok">${escapeHtml(source)}</span><span class="status-chip">${escapeHtml(confidence)}</span></div>
|
| <div class="evidence-title">${escapeHtml(title)}</div>
|
| ${url ? `<a class="evidence-link" href="${escapeAttr(url)}" target="_blank" rel="noreferrer">${escapeHtml(url)}</a>` : ""}
|
| </article>`;
|
| }).join("");
|
| }
|
|
|
| function renderRubric(rubric) {
|
| const entries = Object.entries(rubric);
|
| if (!entries.length) {
|
| els.rubricPane.innerHTML = '<div class="empty">Rubric checks are pending.</div>';
|
| return;
|
| }
|
| els.rubricPane.innerHTML = entries.map(([key, value]) => {
|
| const pass = Boolean(value);
|
| return `<div class="bar-row">
|
| <div class="bar-label"><span>${escapeHtml(key.replaceAll("_", " "))}</span><span>${pass ? "PASS" : "GAP"}</span></div>
|
| <div class="bar-track"><div class="bar-fill" style="width:${pass ? 100 : 22}%"></div></div>
|
| </div>`;
|
| }).join("");
|
| }
|
|
|
| function renderGaps(gaps) {
|
| if (!gaps.length) {
|
| els.gapsList.innerHTML = '<div class="list-item"><strong>No Blocking Gaps</strong>Verifier did not identify unresolved scorecard gaps.</div>';
|
| return;
|
| }
|
| els.gapsList.innerHTML = gaps.map((gap, index) => `<div class="list-item"><strong>Gap ${index + 1}</strong>${escapeHtml(gap)}</div>`).join("");
|
| }
|
|
|
| function renderTools(tools) {
|
| if (!tools.length) {
|
| els.toolsPane.innerHTML = '<div class="empty">No tool calls yet.</div>';
|
| return;
|
| }
|
| els.toolsPane.innerHTML = tools.map((tool) => `<div class="list-item">
|
| <strong>${escapeHtml(tool.tool_id || "tool")} / ${escapeHtml(tool.status || "-")}</strong>
|
| ${escapeHtml(tool.summary || "")}
|
| <details class="payload"><summary>output</summary><pre>${escapeHtml(JSON.stringify(tool.output || {}, null, 2))}</pre></details>
|
| </div>`).join("");
|
| }
|
|
|
| function switchView(view) {
|
| currentView = view;
|
| const live = view === "live";
|
| els.liveView.classList.toggle("view-hidden", !live);
|
| els.workspaceShell.classList.toggle("hidden", live);
|
| document.querySelectorAll(".nav-pill[data-view]").forEach((button) => {
|
| button.classList.toggle("active", button.dataset.view === view);
|
| });
|
| document.querySelectorAll(".workspace-view").forEach((panel) => {
|
| panel.classList.toggle("active", panel.id === `${view}View`);
|
| });
|
| if (view === "dashboard") renderDashboard();
|
| if (view === "results") renderResultsView();
|
| if (view === "library") renderLibrary();
|
| if (view === "benchmarks") renderBenchmarks();
|
| els.footerStatus.textContent = live ? "live audit console" : `${view} view`;
|
| }
|
|
|
| function renderWorkspaceViews() {
|
| renderDashboard();
|
| renderResultsView();
|
| renderLibrary();
|
| renderBenchmarks();
|
| }
|
|
|
| function summarizeResult(result) {
|
| const manifest = result.manifest || {};
|
| const scorecard = (result.artifacts && result.artifacts.audit_scorecard) || {};
|
| const evidence = (((result.artifacts || {}).evidence_bundle || {}).evidence || []);
|
| const score = Number(scorecard.score);
|
| const paperId = extractPaperId(manifest.run_id || "") || extractPaperId(els.paperInput.value) || "local";
|
| return {
|
| id: manifest.run_id || paperId,
|
| paperId,
|
| title: extractPaperTitle(els.paperInput.value) || "Local audit run",
|
| authors: manifest.model_id || "agent runtime",
|
| score: Number.isFinite(score) ? score : 0,
|
| decision: scorecard.decision || manifest.status || "complete",
|
| tier: scoreTier(score),
|
| date: "now",
|
| evidenceCount: evidence.length,
|
| raw: result
|
| };
|
| }
|
|
|
| function persistRun(result) {
|
| try {
|
| const item = summarizeResult(result);
|
| if (isSuppressedLibraryItem(item)) return;
|
| const current = savedRuns().filter((run) => run.id !== item.id);
|
| localStorage.setItem("agenticEngine.library", JSON.stringify([item, ...current].slice(0, 12)));
|
| } catch (error) {
|
| console.warn("Could not persist run", error);
|
| }
|
| }
|
|
|
| function savedRuns() {
|
| try {
|
| const parsed = JSON.parse(localStorage.getItem("agenticEngine.library") || "[]");
|
| if (!Array.isArray(parsed)) return [];
|
| const filtered = parsed.filter((item) => !isSuppressedLibraryItem(item));
|
| if (filtered.length !== parsed.length) {
|
| localStorage.setItem("agenticEngine.library", JSON.stringify(filtered));
|
| }
|
| return filtered;
|
| } catch {
|
| return [];
|
| }
|
| }
|
|
|
| function isSuppressedLibraryItem(item) {
|
| const id = String(item.paperId || item.id || "").trim();
|
| const title = String(item.title || "").toLowerCase().trim();
|
| return SUPPRESSED_LIBRARY_IDS.has(id) || SUPPRESSED_LIBRARY_TITLES.some((sampleTitle) => title.includes(sampleTitle));
|
| }
|
|
|
| function allLibraryRuns() {
|
| const seen = new Set();
|
| return savedRuns().filter((item) => {
|
| const key = item.id || item.paperId || item.title;
|
| if (seen.has(key)) return false;
|
| seen.add(key);
|
| return true;
|
| });
|
| }
|
|
|
| function emptyResult() {
|
| return {
|
| manifest: { run_id: "no-run-selected", status: "idle", model_id: "model pending" },
|
| agents: [],
|
| tools: [],
|
| artifacts: {
|
| audit_scorecard: {
|
| decision: "pending",
|
| gaps: [],
|
| rubric: {}
|
| },
|
| evidence_bundle: {
|
| resolver_status: {},
|
| evidence: []
|
| }
|
| },
|
| report_markdown: "Run an audit or open a saved run to generate a verifier-backed reproducibility report."
|
| };
|
| }
|
|
|
| function renderDashboard() {
|
| const latest = latestResult ? summarizeResult(latestResult) : allLibraryRuns()[0];
|
| const agentCalls = latestResult && Array.isArray(latestResult.agents) ? latestResult.agents.length : "--";
|
| const toolCalls = latestResult && Array.isArray(latestResult.tools) ? latestResult.tools.length : "--";
|
| const evidenceCount = latest && typeof latest.evidenceCount === "number" ? latest.evidenceCount : "--";
|
| els.dashboardMetrics.innerHTML = [
|
| ["Latest score", latest && latest.score ? `${latest.score}/100` : "--"],
|
| ["Agent calls", agentCalls],
|
| ["Tool calls", toolCalls],
|
| ["Evidence", evidenceCount]
|
| ].map(([label, value]) => `<div class="mini-stat"><div class="value">${escapeHtml(value)}</div><div class="label">${escapeHtml(label)}</div></div>`).join("");
|
| els.recentAuditRows.innerHTML = renderAuditTable(allLibraryRuns().slice(0, 5));
|
| els.dashboardResolverBars.innerHTML = resolverCoverageBars();
|
| }
|
|
|
| function renderAuditTable(rows) {
|
| if (!rows.length) {
|
| return `<div class="table-head"><div>Paper</div><div>Score</div><div>Tier</div><div>Decision</div></div><div class="empty">No saved audits yet.</div>`;
|
| }
|
| const body = rows.map((row, index) => `<div class="audit-row" data-library-index="${index}">
|
| <div><div class="audit-title">${escapeHtml(row.title)}</div><div class="audit-meta">arXiv:${escapeHtml(row.paperId || row.id)} / ${escapeHtml(row.authors || "unknown")}</div></div>
|
| <div><span class="score-pill ${scoreTone(row.score)}">${escapeHtml(row.score || "--")}</span></div>
|
| <div><span class="status-chip ${row.tier === 1 ? "ok" : row.tier === 3 ? "bad" : ""}">Tier ${escapeHtml(row.tier || "-")}</span></div>
|
| <div>${escapeHtml(row.decision || "pending")}</div>
|
| </div>`).join("");
|
| return `<div class="table-head"><div>Paper</div><div>Score</div><div>Tier</div><div>Decision</div></div>${body}`;
|
| }
|
|
|
| function resolverCoverageBars() {
|
| const bundle = latestResult && latestResult.artifacts ? latestResult.artifacts.evidence_bundle || {} : {};
|
| const status = bundle.resolver_status || {};
|
| const values = RESOLVERS.map(([id, label]) => {
|
| const state = status[id] || (latestResult ? "pending" : "idle");
|
| const score = state === "ok" || state === "resolved" ? 100 : state === "missing" || state === "failed" ? 24 : latestResult ? 48 : 0;
|
| return { label, score, state };
|
| });
|
| return values.map((item) => `<div class="chart-row">
|
| <span>${escapeHtml(item.label)}</span>
|
| <div class="chart-bar"><div class="chart-fill" style="width:${item.score}%"></div></div>
|
| <span>${escapeHtml(item.state)}</span>
|
| </div>`).join("");
|
| }
|
|
|
| function renderResultsView() {
|
| const result = latestResult || emptyResult();
|
| const manifest = result.manifest || {};
|
| const scorecard = (result.artifacts && result.artifacts.audit_scorecard) || {};
|
| const evidence = (((result.artifacts || {}).evidence_bundle || {}).evidence || []);
|
| const score = Number(scorecard.score);
|
| const axes = axesForResult(result);
|
| const selected = axes[selectedAxis] || axes[0];
|
|
|
| els.resultsTitle.textContent = extractPaperTitle(els.paperInput.value) || "Verifier-backed reproducibility report";
|
| els.resultsSubtitle.textContent = `${manifest.run_id || "demo run"} / ${manifest.model_id || "model pending"} / ${scorecard.decision || "pending"}`;
|
| els.resultsSummary.innerHTML = `<div class="mini-stat">
|
| <div class="value">${Number.isFinite(score) ? score : "--"}</div>
|
| <div class="label">Overall score</div>
|
| </div>
|
| <div class="report-card">
|
| <div class="label-mono" style="margin-bottom:8px;">Selected axis</div>
|
| <div class="audit-title">${escapeHtml(selected.label)}</div>
|
| <div class="audit-meta">${escapeHtml(selected.score)} / 100, verifier weighted</div>
|
| </div>`;
|
| els.radarChart.innerHTML = radarSvg(axes);
|
| els.axisList.innerHTML = axes.map((axis, index) => `<button class="axis-button ${index === selectedAxis ? "active" : ""}" data-axis="${index}" type="button">
|
| <span>${escapeHtml(axis.label)}</span><strong>${escapeHtml(axis.score)}</strong>
|
| </button>`).join("");
|
| els.resultsEvidence.innerHTML = evidenceCards(evidence);
|
| document.querySelectorAll("[data-results-tab]").forEach((button) => {
|
| button.classList.toggle("active", button.dataset.resultsTab === selectedResultsTab);
|
| });
|
| renderResultTab(result);
|
| }
|
|
|
| function axesForResult(result) {
|
| const scorecard = (result.artifacts && result.artifacts.audit_scorecard) || {};
|
| const score = Number(scorecard.score);
|
| if (!Number.isFinite(score)) return DEFAULT_AXES;
|
| return DEFAULT_AXES.map((axis, index) => {
|
| const offset = [6, 2, -8, -4, 4, -10, 8, -12][index] || 0;
|
| return { label: axis.label, score: clamp(Math.round(score * 0.78 + axis.score * 0.22 + offset), 12, 98) };
|
| });
|
| }
|
|
|
| function radarSvg(axes) {
|
| const size = 360;
|
| const cx = size / 2;
|
| const cy = size / 2;
|
| const radius = 132;
|
| const point = (index, scale) => {
|
| const angle = Math.PI * 2 * index / axes.length - Math.PI / 2;
|
| return [cx + Math.cos(angle) * radius * scale, cy + Math.sin(angle) * radius * scale];
|
| };
|
| const poly = axes.map((axis, index) => point(index, axis.score / 100).map((v) => v.toFixed(1)).join(",")).join(" ");
|
| const rings = [0.25, 0.5, 0.75, 1].map((scale) => `<polygon points="${axes.map((_, index) => point(index, scale).map((v) => v.toFixed(1)).join(",")).join(" ")}" fill="none" stroke="rgba(125,211,252,0.14)" stroke-width="1" />`).join("");
|
| const spokes = axes.map((_, index) => {
|
| const [x, y] = point(index, 1);
|
| return `<line x1="${cx}" y1="${cy}" x2="${x.toFixed(1)}" y2="${y.toFixed(1)}" stroke="rgba(125,211,252,0.12)" />`;
|
| }).join("");
|
| const labels = axes.map((axis, index) => {
|
| const [x, y] = point(index, 1.18);
|
| return `<text x="${x.toFixed(1)}" y="${y.toFixed(1)}" text-anchor="middle" fill="#9ca6c4" font-size="10" font-family="monospace">${escapeHtml(axis.label)}</text>`;
|
| }).join("");
|
| const dots = axes.map((axis, index) => {
|
| const [x, y] = point(index, axis.score / 100);
|
| const active = index === selectedAxis;
|
| return `<circle cx="${x.toFixed(1)}" cy="${y.toFixed(1)}" r="${active ? 6 : 4}" fill="${active ? "#00d4ff" : "#7cf2b7"}" opacity="0.94" />`;
|
| }).join("");
|
| return `<svg viewBox="0 0 ${size} ${size}" role="img" aria-label="Reproducibility radar chart">
|
| ${rings}${spokes}
|
| <polygon points="${poly}" fill="rgba(0,212,255,0.16)" stroke="#00d4ff" stroke-width="2" />
|
| ${dots}${labels}
|
| </svg>`;
|
| }
|
|
|
| function renderResultTab(result) {
|
| const reportText = result.report_markdown || "Run an audit to generate the verifier-backed report.";
|
| const evidence = (((result.artifacts || {}).evidence_bundle || {}).evidence || []);
|
| const scorecard = ((result.artifacts || {}).audit_scorecard || {});
|
| if (selectedResultsTab === "report") {
|
| els.resultTabContent.innerHTML = `<pre class="report-output" style="max-height:520px;">${escapeHtml(reportText)}</pre>`;
|
| } else if (selectedResultsTab === "evidence") {
|
| els.resultTabContent.innerHTML = `<div class="artifact-grid">${evidenceCards(evidence)}</div>`;
|
| } else if (selectedResultsTab === "scaffold") {
|
| els.resultTabContent.innerHTML = `<div class="artifact-grid">
|
| <div class="artifact-card"><strong>fetch_evidence.py</strong><p class="mode-copy">Structured commands for arXiv, DOI, GitHub, and dataset fetches based on resolver output.</p></div>
|
| <div class="artifact-card"><strong>reproduce.md</strong><p class="mode-copy">Environment, data, seed, and figure checklist generated from the Code/Data Agent.</p></div>
|
| <div class="artifact-card"><strong>audit_scorecard.json</strong><p class="mode-copy">Score, rubric flags, gaps, and verifier objections.</p></div>
|
| </div>
|
| <pre class="report-output" style="margin-top:12px;">python fetch_evidence.py --paper "${escapeHtml(extractPaperId(els.paperInput.value) || "paper")}"
|
| python run_repro_check.py --scorecard audit_scorecard.json</pre>`;
|
| } else {
|
| const gaps = scorecard.gaps || [];
|
| els.resultTabContent.innerHTML = `<div class="artifact-grid">
|
| <div class="artifact-card"><strong>Verifier objections</strong><p class="mode-copy">${escapeHtml(gaps.length ? gaps.join(" ") : "No unresolved verifier gaps in the current result.")}</p></div>
|
| <div class="artifact-card"><strong>Autonomy level</strong><p class="mode-copy">${escapeHtml(AUTONOMY[autonomyLevel()])}</p></div>
|
| <div class="artifact-card"><strong>Repair policy</strong><p class="mode-copy">Verifier repair requests remain visible in the trace and can return work to upstream agents before report finalization.</p></div>
|
| </div>`;
|
| }
|
| }
|
|
|
| function evidenceCards(evidence) {
|
| if (!evidence.length) return '<div class="empty">No live evidence resolved yet.</div>';
|
| return evidence.map((item) => {
|
| const source = item.source_type || item.resolver || "source";
|
| const title = item.title || item.id || item.full_name || item.doi || item.arxiv_id || "evidence";
|
| const url = item.url || item.html_url || item.doi_url || "";
|
| return `<article class="artifact-card">
|
| <div class="evidence-meta"><span class="status-chip ok">${escapeHtml(source)}</span><span class="status-chip">${escapeHtml(item.confidence || item.status || "seen")}</span></div>
|
| <div class="audit-title">${escapeHtml(title)}</div>
|
| ${url ? `<a class="evidence-link" href="${escapeAttr(url)}" target="_blank" rel="noreferrer">${escapeHtml(url)}</a>` : ""}
|
| </article>`;
|
| }).join("");
|
| }
|
|
|
| function renderLibrary() {
|
| const rows = filteredLibraryRuns();
|
| els.libraryGrid.innerHTML = rows.map((item, index) => `<article class="library-card" data-library-card="${index}">
|
| <div class="evidence-meta"><span class="status-chip ${item.tier === 1 ? "ok" : item.tier === 3 ? "bad" : ""}">Tier ${escapeHtml(item.tier || "-")}</span><span class="score-pill ${scoreTone(item.score)}">${escapeHtml(item.score || "--")}</span></div>
|
| <div class="audit-title">${escapeHtml(item.title)}</div>
|
| <div class="audit-meta">arXiv:${escapeHtml(item.paperId || item.id)} / ${escapeHtml(item.authors || "unknown")}</div>
|
| <p class="mode-copy">${escapeHtml(item.decision || "pending")} / ${escapeHtml(item.date || "saved")}</p>
|
| </article>`).join("") || `<div class="empty">${allLibraryRuns().length ? "No matching audits." : "No saved audits yet. Run or save an audit to populate this library."}</div>`;
|
| }
|
|
|
| function filteredLibraryRuns() {
|
| const query = (els.librarySearch.value || "").toLowerCase().trim();
|
| return allLibraryRuns().filter((item) => {
|
| const haystack = `${item.title} ${item.id} ${item.paperId || ""} ${item.authors || ""} ${item.decision || ""}`.toLowerCase();
|
| const tierOk = libraryTier === "all" || String(item.tier) === libraryTier;
|
| return tierOk && (!query || haystack.includes(query));
|
| });
|
| }
|
|
|
| function renderBenchmarks() {
|
| const max = Math.max(...BENCHMARK_BUCKETS.map((bucket) => bucket.count));
|
| els.benchmarkBars.innerHTML = BENCHMARK_BUCKETS.map((bucket) => `<div class="chart-row">
|
| <span>${escapeHtml(bucket.label)}</span>
|
| <div class="chart-bar"><div class="chart-fill" style="width:${Math.round(bucket.count / max * 100)}%"></div></div>
|
| <span>${bucket.count}</span>
|
| </div>`).join("");
|
| els.benchmarkCards.innerHTML = [
|
| ["142", "Audits tracked", "Library and public benchmark examples"],
|
| ["8", "Score axes", "Data, software, methods, compute, statistics, environment, provenance, robustness"],
|
| ["4", "Live resolvers", "arXiv, DOI, GitHub, dataset evidence"],
|
| ["1", "Public app", "Static HTML frontend backed by Agent API"]
|
| ].map(([value, label, body]) => `<div class="benchmark-card"><div class="mini-stat"><div class="value">${value}</div><div class="label">${escapeHtml(label)}</div></div><p class="mode-copy">${escapeHtml(body)}</p></div>`).join("");
|
| els.resolverMatrix.innerHTML = READINESS_CARDS.map((card) => `<div class="artifact-card">
|
| <div class="evidence-meta"><strong>${escapeHtml(card.title)}</strong><span class="status-chip ok">${escapeHtml(card.status)}</span></div>
|
| <p class="mode-copy">${escapeHtml(card.body)}</p>
|
| </div>`).join("");
|
| }
|
|
|
| function openLibraryItem(index, rows) {
|
| rows = rows || allLibraryRuns();
|
| const item = rows[index];
|
| if (!item) return;
|
| latestResult = item.raw || emptyResult();
|
| renderResultsView();
|
| switchView("results");
|
| }
|
|
|
| function buildPaperFromIntake() {
|
| const sources = Array.from(document.querySelectorAll(".source-toggle:checked")).map((input) => input.value).join(", ");
|
| const title = els.newAuditTitle.value.trim() || els.newAuditReference.value.trim() || "Untitled research artifact";
|
| return `# ${title}
|
|
|
| Reference: ${els.newAuditReference.value.trim()}
|
| Model target: ${els.newAuditModel.value}
|
| Audit depth: ${els.newAuditDepth.value}
|
| Enabled sources: ${sources || "none"}
|
| Verifier repair loop: ${document.getElementById("newAuditVerifier").checked ? "enabled" : "disabled"}
|
|
|
| ${els.newAuditNotes.value.trim()}`;
|
| }
|
|
|
| function startConfiguredAudit() {
|
| const depth = els.newAuditDepth.value;
|
| els.paperInput.value = buildPaperFromIntake();
|
| setAutonomyLevel(depth === "forensic" ? 3 : depth === "quick" ? 1 : 2);
|
| switchView("dashboard");
|
| runAudit();
|
| }
|
|
|
| function saveBatchDraft() {
|
| const draft = {
|
| id: `draft-${Date.now()}`,
|
| paperId: els.newAuditReference.value.trim() || "draft",
|
| title: els.newAuditTitle.value.trim() || "Batch draft",
|
| authors: "local draft",
|
| score: 0,
|
| decision: "draft",
|
| tier: 3,
|
| date: "saved"
|
| };
|
| if (isSuppressedLibraryItem(draft)) {
|
| renderLibrary();
|
| switchView("library");
|
| return;
|
| }
|
| const current = savedRuns().filter((item) => item.id !== draft.id);
|
| localStorage.setItem("agenticEngine.library", JSON.stringify([draft, ...current].slice(0, 12)));
|
| renderLibrary();
|
| switchView("library");
|
| }
|
|
|
| function openCopilot(context) {
|
| els.copilotDrawer.classList.remove("hidden");
|
| els.copilotContext.textContent = context || `${currentView} context`;
|
| if (!els.copilotThread.childElementCount) {
|
| appendCopilot("assistant", "I can explain score drivers, identify the next reproducibility fix, draft report language, or summarize resolver evidence from the current run.");
|
| }
|
| }
|
|
|
| function appendCopilot(role, message) {
|
| const node = document.createElement("div");
|
| node.className = `copilot-msg ${role === "user" ? "user" : ""}`;
|
| node.innerHTML = escapeHtml(message);
|
| els.copilotThread.appendChild(node);
|
| els.copilotThread.scrollTop = els.copilotThread.scrollHeight;
|
| }
|
|
|
| function askCopilot(question) {
|
| const q = (question || els.copilotInput.value || "").trim();
|
| if (!q) return;
|
| appendCopilot("user", q);
|
| appendCopilot("assistant", copilotAnswer(q));
|
| els.copilotInput.value = "";
|
| }
|
|
|
| function copilotAnswer(question) {
|
| const result = latestResult || emptyResult();
|
| const scorecard = ((result.artifacts || {}).audit_scorecard || {});
|
| const evidence = (((result.artifacts || {}).evidence_bundle || {}).evidence || []);
|
| const score = Number(scorecard.score);
|
| const gaps = scorecard.gaps || [];
|
| const lower = question.toLowerCase();
|
| if (lower.includes("fix")) {
|
| return gaps.length
|
| ? `Fix the first verifier gap: ${gaps[0]} Then rerun at L2 or L3 so the retriever and verifier can validate the new evidence.`
|
| : "The strongest next fix is to package exact data snapshots, code commit hashes, and environment pins even when the current score has no blocking verifier gaps.";
|
| }
|
| if (lower.includes("statement") || lower.includes("draft")) {
|
| return "Reproducibility statement: all claims should list data source, code repository, environment pins, random seeds, and figure-level reproduction commands. The engine should attach resolver URLs and verifier gaps as provenance.";
|
| }
|
| if (lower.includes("evidence")) {
|
| return `Current run has ${evidence.length} evidence item(s). The resolver layer should include arXiv, DOI, GitHub, and dataset records before a strong audit-ready decision is trusted.`;
|
| }
|
| return `Current score is ${Number.isFinite(score) ? `${score}/100` : "pending"}. The decision is ${scorecard.decision || "pending"} because the verifier weighs live evidence coverage, scaffold feasibility, and unresolved provenance gaps.`;
|
| }
|
|
|
| function exportCsv() {
|
| const rows = allLibraryRuns();
|
| const csv = ["id,title,score,tier,decision", ...rows.map((row) => [row.paperId || row.id, row.title, row.score || "", row.tier || "", row.decision || ""].map(csvCell).join(","))].join("\n");
|
| downloadBlob(csv, "agentic-engine-library.csv", "text/csv");
|
| }
|
|
|
| function shareRun() {
|
| const text = `${location.origin}${location.pathname}#${latestResult && latestResult.manifest ? latestResult.manifest.run_id : "agentic-engine"}`;
|
| if (navigator.clipboard) navigator.clipboard.writeText(text).catch(() => {});
|
| els.footerStatus.textContent = "share link copied";
|
| }
|
|
|
| function downloadBlob(content, filename, type) {
|
| const blob = new Blob([content], { type });
|
| const url = URL.createObjectURL(blob);
|
| const a = document.createElement("a");
|
| a.href = url;
|
| a.download = filename;
|
| a.click();
|
| URL.revokeObjectURL(url);
|
| }
|
|
|
| function csvCell(value) {
|
| return `"${String(value).replaceAll('"', '""')}"`;
|
| }
|
|
|
| function scoreTier(score) {
|
| if (!Number.isFinite(Number(score))) return 3;
|
| if (Number(score) >= 80) return 1;
|
| if (Number(score) >= 65) return 2;
|
| return 3;
|
| }
|
|
|
| function scoreTone(score) {
|
| const value = Number(score);
|
| if (!Number.isFinite(value)) return "bad";
|
| if (value >= 75) return "";
|
| if (value >= 60) return "warn";
|
| return "bad";
|
| }
|
|
|
| function extractPaperTitle(text) {
|
| const line = String(text || "").split(/\r?\n/).map((part) => part.trim()).find(Boolean);
|
| return line ? line.replace(/^#\s*/, "") : "";
|
| }
|
|
|
| function extractPaperId(text) {
|
| const match = String(text || "").match(/(?:arXiv[:\s]*)?(\d{4}\.\d{4,5})(?:v\d+)?/i);
|
| return match ? match[1] : "";
|
| }
|
|
|
| function clamp(value, min, max) {
|
| return Math.max(min, Math.min(max, value));
|
| }
|
|
|
| async function checkHealth() {
|
| const base = cleanBase(els.apiBase.value);
|
| if (!base) {
|
| setHealth("set api", "bad");
|
| return;
|
| }
|
| setHealth("checking", "");
|
| try {
|
| const response = await fetch(`${base}/health`, { headers: { "Accept": "application/json" } });
|
| if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
| const payload = await response.json();
|
| const model = payload.model && payload.model.model_id ? payload.model.model_id : "online";
|
| els.modelId.textContent = model;
|
| setHealth("online", "ok");
|
| } catch (error) {
|
| setHealth("unavailable", "bad");
|
| showError(`Health check failed: ${error.message}`);
|
| }
|
| }
|
|
|
| async function runAudit() {
|
| const base = cleanBase(els.apiBase.value);
|
| if (!base) {
|
| showError("Set the Agent API URL first. For local FastAPI, use http://localhost:8080.");
|
| return;
|
| }
|
| localStorage.setItem("agenticEngine.apiBase", base);
|
| resetRunView();
|
| setRunning(true);
|
| activeRunAbort = new AbortController();
|
| const payload = {
|
| paper_text: els.paperInput.value.trim() || DEMO_PAPER,
|
| autonomy_level: autonomyLevel(),
|
| allow_network: null
|
| };
|
| try {
|
| await runStream(base, payload, activeRunAbort.signal);
|
| } catch (streamError) {
|
| if (streamError.name === "AbortError") {
|
| activeRunAbort = null;
|
| return;
|
| }
|
| try {
|
| const response = await fetch(`${base}/runs`, {
|
| method: "POST",
|
| headers: { "Content-Type": "application/json", "Accept": "application/json" },
|
| body: JSON.stringify(payload),
|
| signal: activeRunAbort ? activeRunAbort.signal : undefined
|
| });
|
| if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
| renderResult(await response.json());
|
| } catch (fallbackError) {
|
| if (fallbackError.name === "AbortError") {
|
| activeRunAbort = null;
|
| return;
|
| }
|
| showError(`Run failed: ${fallbackError.message}. Streaming error: ${streamError.message}`);
|
| setRunning(false);
|
| }
|
| } finally {
|
| activeRunAbort = null;
|
| }
|
| }
|
|
|
| async function runStream(base, payload, signal) {
|
| const response = await fetch(`${base}/runs/stream`, {
|
| method: "POST",
|
| headers: { "Content-Type": "application/json", "Accept": "text/event-stream" },
|
| body: JSON.stringify(payload),
|
| signal
|
| });
|
| if (!response.ok || !response.body) throw new Error(`HTTP ${response.status}`);
|
| const reader = response.body.getReader();
|
| const decoder = new TextDecoder();
|
| let buffer = "";
|
| while (true) {
|
| const { value, done } = await reader.read();
|
| if (done) break;
|
| buffer += decoder.decode(value, { stream: true });
|
| buffer = processSseBuffer(buffer);
|
| }
|
| processSseBuffer(buffer + "\n\n");
|
| }
|
|
|
| function processSseBuffer(buffer) {
|
| const blocks = buffer.split("\n\n");
|
| const remainder = blocks.pop() || "";
|
| for (const block of blocks) {
|
| if (!block.trim()) continue;
|
| const parsed = parseSseBlock(block);
|
| if (!parsed) continue;
|
| if (parsed.event === "trace") appendTrace(parsed.data);
|
| if (parsed.event === "result") renderResult(parsed.data);
|
| if (parsed.event === "error") throw new Error(parsed.data.detail || "stream error");
|
| }
|
| return remainder;
|
| }
|
|
|
| function parseSseBlock(block) {
|
| let event = "message";
|
| const data = [];
|
| for (const line of block.split("\n")) {
|
| if (line.startsWith("event:")) event = line.slice(6).trim();
|
| if (line.startsWith("data:")) data.push(line.slice(5).trim());
|
| }
|
| if (!data.length) return null;
|
| return { event, data: JSON.parse(data.join("\n")) };
|
| }
|
|
|
| function setTab(active) {
|
| const mapping = [
|
| ["report", els.tabReport, els.report],
|
| ["rubric", els.tabRubric, els.rubricPane],
|
| ["tools", els.tabTools, els.toolsPane]
|
| ];
|
| for (const [id, button, pane] of mapping) {
|
| button.classList.toggle("active", id === active);
|
| pane.classList.toggle("hidden", id !== active);
|
| }
|
| }
|
|
|
| function downloadLatest() {
|
| const data = latestResult || { status: "no-run" };
|
| const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
|
| const url = URL.createObjectURL(blob);
|
| const a = document.createElement("a");
|
| a.href = url;
|
| a.download = `${latestResult && latestResult.manifest ? latestResult.manifest.run_id : "agentic-engine-run"}.json`;
|
| a.click();
|
| URL.revokeObjectURL(url);
|
| }
|
|
|
| function loadTextFile(file) {
|
| const reader = new FileReader();
|
| reader.onload = () => { els.paperInput.value = String(reader.result || ""); };
|
| reader.readAsText(file);
|
| }
|
|
|
| function timeOnly(value) {
|
| if (!value) return "";
|
| const date = new Date(value);
|
| if (Number.isNaN(date.valueOf())) return "";
|
| return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
| }
|
|
|
| function escapeHtml(value) {
|
| return String(value)
|
| .replaceAll("&", "&")
|
| .replaceAll("<", "<")
|
| .replaceAll(">", ">")
|
| .replaceAll('"', """)
|
| .replaceAll("'", "'");
|
| }
|
|
|
| function escapeAttr(value) {
|
| return escapeHtml(value).replaceAll("`", "`");
|
| }
|
|
|
| els.apiBase.value = defaultApiBase();
|
| els.paperInput.value = DEMO_PAPER;
|
| updateAutonomy();
|
| renderAgentFeed();
|
| renderResolverLanes({});
|
| renderWorkspaceViews();
|
| renderResultsView();
|
|
|
| els.autonomySlider.addEventListener("input", updateAutonomy);
|
| els.dashboardAutonomySlider.addEventListener("input", updateAutonomy);
|
| els.newAutonomySlider.addEventListener("input", updateAutonomy);
|
| els.sampleBtn.addEventListener("click", () => { els.paperInput.value = DEMO_PAPER; });
|
| els.clearBtn.addEventListener("click", () => { els.paperInput.value = ""; els.paperInput.focus(); });
|
| els.fileBtn.addEventListener("click", () => els.fileInput.click());
|
| els.fileInput.addEventListener("change", () => {
|
| const file = els.fileInput.files && els.fileInput.files[0];
|
| if (file) loadTextFile(file);
|
| els.fileInput.value = "";
|
| });
|
| els.saveApi.addEventListener("click", () => {
|
| localStorage.setItem("agenticEngine.apiBase", cleanBase(els.apiBase.value));
|
| setHealth("saved", "ok");
|
| });
|
| els.healthBtn.addEventListener("click", checkHealth);
|
| els.runBtn.addEventListener("click", runAudit);
|
| els.stopBtn.addEventListener("click", stopAudit);
|
| els.downloadBtn.addEventListener("click", downloadLatest);
|
| els.tabReport.addEventListener("click", () => setTab("report"));
|
| els.tabRubric.addEventListener("click", () => setTab("rubric"));
|
| els.tabTools.addEventListener("click", () => setTab("tools"));
|
| els.dashboardNewBtn.addEventListener("click", () => switchView("new"));
|
| els.dashboardRunBtn.addEventListener("click", () => switchView("live"));
|
| els.dashboardStopBtn.addEventListener("click", stopAudit);
|
| els.newAuditStart.addEventListener("click", startConfiguredAudit);
|
| els.saveBatchBtn.addEventListener("click", saveBatchDraft);
|
| els.newUseSample.addEventListener("click", () => {
|
| els.newAuditReference.value = "2006.11239";
|
| els.newAuditTitle.value = "Denoising Diffusion Probabilistic Models";
|
| els.newAuditNotes.value = "Code: https://github.com/hojonathanho/diffusion\nData: CIFAR-10, LSUN, CelebA-HQ\n\nAudit focus: verify dataset availability, code provenance, experiment detail, and reproduction scaffold feasibility.";
|
| });
|
| els.newLoadFile.addEventListener("click", () => els.fileInput.click());
|
| els.newViewArxiv.addEventListener("click", () => {
|
| const id = extractPaperId(els.newAuditReference.value);
|
| if (id) window.open(`https://arxiv.org/abs/${id}`, "_blank", "noopener,noreferrer");
|
| });
|
| els.exportPdfBtn.addEventListener("click", () => window.print());
|
| els.shareRunBtn.addEventListener("click", shareRun);
|
| els.askCopilotFromResults.addEventListener("click", () => openCopilot("results report"));
|
| els.librarySearch.addEventListener("input", renderLibrary);
|
| els.tierFilter.addEventListener("click", (event) => {
|
| const button = event.target.closest("button[data-tier]");
|
| if (!button) return;
|
| libraryTier = button.dataset.tier;
|
| els.tierFilter.querySelectorAll("button").forEach((item) => item.classList.toggle("on", item === button));
|
| renderLibrary();
|
| });
|
| els.exportCsvBtn.addEventListener("click", exportCsv);
|
| els.copilotBtn.addEventListener("click", () => openCopilot(`${currentView} context`));
|
| els.closeCopilot.addEventListener("click", () => els.copilotDrawer.classList.add("hidden"));
|
| els.sendCopilot.addEventListener("click", () => askCopilot());
|
| els.copilotInput.addEventListener("keydown", (event) => {
|
| if (event.key === "Enter") askCopilot();
|
| });
|
|
|
| document.addEventListener("click", (event) => {
|
| const viewButton = event.target.closest("[data-view]");
|
| if (viewButton) {
|
| switchView(viewButton.dataset.view);
|
| return;
|
| }
|
| const runAction = event.target.closest("[data-action='run-current']");
|
| if (runAction) {
|
| runAudit();
|
| return;
|
| }
|
| const axisButton = event.target.closest("[data-axis]");
|
| if (axisButton) {
|
| selectedAxis = Number(axisButton.dataset.axis);
|
| renderResultsView();
|
| return;
|
| }
|
| const resultTab = event.target.closest("[data-results-tab]");
|
| if (resultTab) {
|
| selectedResultsTab = resultTab.dataset.resultsTab;
|
| document.querySelectorAll("[data-results-tab]").forEach((button) => button.classList.toggle("active", button === resultTab));
|
| renderResultTab(latestResult || emptyResult());
|
| return;
|
| }
|
| const libraryRow = event.target.closest("[data-library-index]");
|
| if (libraryRow) {
|
| openLibraryItem(Number(libraryRow.dataset.libraryIndex), allLibraryRuns().slice(0, 5));
|
| return;
|
| }
|
| const libraryCard = event.target.closest("[data-library-card]");
|
| if (libraryCard) {
|
| openLibraryItem(Number(libraryCard.dataset.libraryCard), filteredLibraryRuns());
|
| return;
|
| }
|
| const copilotSuggestion = event.target.closest("[data-copilot]");
|
| if (copilotSuggestion) {
|
| askCopilot(copilotSuggestion.dataset.copilot);
|
| }
|
| });
|
| </script>
|
| </body>
|
| </html>
|
|
|