opencastle 0.25.0 → 0.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/cli/bootstrap.d.ts +8 -0
  2. package/dist/cli/bootstrap.d.ts.map +1 -0
  3. package/dist/cli/bootstrap.js +358 -0
  4. package/dist/cli/bootstrap.js.map +1 -0
  5. package/dist/cli/bootstrap.test.d.ts +6 -0
  6. package/dist/cli/bootstrap.test.d.ts.map +1 -0
  7. package/dist/cli/bootstrap.test.js +196 -0
  8. package/dist/cli/bootstrap.test.js.map +1 -0
  9. package/dist/cli/convoy/engine.d.ts.map +1 -1
  10. package/dist/cli/convoy/engine.js +63 -0
  11. package/dist/cli/convoy/engine.js.map +1 -1
  12. package/dist/cli/convoy/export.d.ts.map +1 -1
  13. package/dist/cli/convoy/export.js +4 -0
  14. package/dist/cli/convoy/export.js.map +1 -1
  15. package/dist/cli/init.d.ts.map +1 -1
  16. package/dist/cli/init.js +15 -9
  17. package/dist/cli/init.js.map +1 -1
  18. package/dist/cli/run.d.ts.map +1 -1
  19. package/dist/cli/run.js +4 -0
  20. package/dist/cli/run.js.map +1 -1
  21. package/package.json +1 -1
  22. package/src/cli/bootstrap.test.ts +286 -0
  23. package/src/cli/bootstrap.ts +472 -0
  24. package/src/cli/convoy/engine.ts +63 -0
  25. package/src/cli/convoy/export.ts +8 -0
  26. package/src/cli/init.ts +17 -12
  27. package/src/cli/run.ts +4 -0
  28. package/src/dashboard/dist/_astro/{index.Cq68OHaZ.css → index.DtnyD8a5.css} +1 -1
  29. package/src/dashboard/dist/index.html +61 -4
  30. package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
  31. package/src/dashboard/src/pages/index.astro +60 -3
  32. package/src/dashboard/src/styles/dashboard.css +4 -0
  33. package/src/orchestrator/agents/team-lead.agent.md +4 -2
  34. package/src/orchestrator/customizations/README.md +3 -3
  35. package/src/orchestrator/customizations/agents/agent-registry.md +1 -1
  36. package/src/orchestrator/customizations/project/docs-structure.md +1 -1
  37. package/src/orchestrator/customizations/project/roadmap.md +1 -1
  38. package/src/orchestrator/customizations/project/tracker-config.md +1 -1
  39. package/src/orchestrator/customizations/project.instructions.md +2 -2
  40. package/src/orchestrator/customizations/stack/api-config.md +1 -1
  41. package/src/orchestrator/customizations/stack/cms-config.md +2 -2
  42. package/src/orchestrator/customizations/stack/data-pipeline-config.md +1 -1
  43. package/src/orchestrator/customizations/stack/database-config.md +2 -2
  44. package/src/orchestrator/customizations/stack/deployment-config.md +1 -1
  45. package/src/orchestrator/customizations/stack/notifications-config.md +1 -1
  46. package/src/orchestrator/customizations/stack/testing-config.md +1 -1
  47. package/src/orchestrator/instructions/general.instructions.md +2 -0
  48. package/src/orchestrator/prompts/bootstrap-customizations.prompt.md +127 -132
  49. package/src/orchestrator/skills/agent-hooks/SKILL.md +7 -2
@@ -1 +1 @@
1
- :root{--bg-primary: #0a0a0f;--bg-secondary: #111118;--bg-tertiary: #1a1a24;--bg-card: rgba(255, 255, 255, .03);--bg-card-hover: rgba(255, 255, 255, .06);--text-primary: #f0f0f5;--text-secondary: #8a8a9a;--text-tertiary: #5a5a6e;--text-accent: #a78bfa;--gradient-accent: linear-gradient(135deg, #a78bfa 0%, #6366f1 50%, #3b82f6 100%);--gradient-glow: radial-gradient(ellipse 800px 400px at 50% 0%, rgba(99, 102, 241, .12) 0%, transparent 70%);--border-color: rgba(255, 255, 255, .06);--border-accent: rgba(167, 139, 250, .3);--color-success: #22c55e;--color-partial: #f59e0b;--color-failed: #ef4444;--color-redirected: #64748b;--color-premium: #f59e0b;--color-standard: #a78bfa;--color-utility: #3b82f6;--color-economy: #64748b;--accent-blue: #3b82f6;--accent-purple: #a78bfa;--accent-indigo: #6366f1;--max-width: 1280px;--transition-fast: .15s cubic-bezier(.4, 0, .2, 1);--transition-base: .3s cubic-bezier(.4, 0, .2, 1)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html{font-size:16px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Inter,Roboto,Helvetica,Arial,sans-serif;background-color:var(--bg-primary);color:var(--text-primary);line-height:1.6;overflow-x:hidden;min-height:100vh}.dash-header{position:sticky;top:0;z-index:50;background:#0a0a0fd9;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-bottom:1px solid var(--border-color)}.dash-header__inner{max-width:var(--max-width);margin:0 auto;padding:0 24px;height:56px;display:flex;align-items:center;justify-content:space-between}.dash-header__brand{display:flex;align-items:center;gap:10px}.dash-header__icon{width:32px;height:32px;border-radius:8px;object-fit:contain}.dash-header__title{font-size:1rem;font-weight:600;color:var(--text-primary)}.dash-layout{display:flex;max-width:var(--max-width);margin:0 auto;position:relative}.dash-sidebar{position:sticky;top:56px;height:calc(100vh - 56px);width:180px;flex-shrink:0;padding:24px 0 24px 24px;overflow-y:auto;display:none}@media(min-width:1024px){.dash-sidebar{display:block}}.dash-sidebar__list{list-style:none;display:flex;flex-direction:column;gap:2px}.dash-sidebar__link{display:block;padding:8px 16px;font-size:.8125rem;font-weight:500;color:var(--text-tertiary);text-decoration:none;border-radius:8px;transition:color var(--transition-fast),background var(--transition-fast)}.dash-sidebar__link:hover{color:var(--text-secondary);background:#ffffff0a}.dash-sidebar__link--active{color:var(--text-accent);background:#a78bfa14;font-weight:600}.dash-main{flex:1;min-width:0;max-width:var(--max-width);margin:0 auto;padding:24px;display:flex;flex-direction:column;gap:20px;position:relative}.dash-main:before{content:"";position:fixed;top:0;left:50%;transform:translate(-50%);width:100%;height:600px;background:var(--gradient-glow);pointer-events:none;z-index:0}.dash-main>*{position:relative;z-index:1}[data-nav-section]{scroll-margin-top:72px}.kpi-row{display:grid;grid-template-columns:1fr;gap:12px}@media(min-width:480px){.kpi-row{grid-template-columns:repeat(2,1fr)}}@media(min-width:960px){.kpi-row{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}}.kpi-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;padding:20px 24px;display:flex;flex-direction:column;gap:4px;transition:border-color var(--transition-fast)}.kpi-card:hover{border-color:#ffffff1a}.kpi-card__label{font-size:.75rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.kpi-card__value{font-size:2rem;font-weight:700;color:var(--text-primary);line-height:1.2;letter-spacing:-.02em}.kpi-card__sub{font-size:.75rem;color:var(--text-secondary);display:flex;align-items:center;gap:4px}.kpi-trend{font-weight:600}.kpi-trend--up{color:var(--color-success)}.kpi-trend--down{color:var(--color-failed)}.kpi-trend--neutral{color:var(--text-tertiary)}.chart-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;overflow:hidden;transition:border-color var(--transition-fast)}.chart-card:hover{border-color:#ffffff1a}.chart-card__header{padding:20px 24px 8px}.chart-card__title{font-size:.9375rem;font-weight:600;color:var(--text-primary)}.chart-card__desc{font-size:.75rem;color:var(--text-tertiary);margin-top:2px}.chart-card__body{padding:16px 24px 24px;min-height:120px}.chart-card__body--table{padding:0}.charts-row{display:grid;grid-template-columns:1fr;gap:20px}@media(min-width:768px){.charts-row{grid-template-columns:repeat(2,1fr)}}.bar-row{display:flex;align-items:center;gap:12px;padding:6px 0}.bar-row+.bar-row{border-top:1px solid rgba(255,255,255,.03)}.bar-label{font-size:.8125rem;color:var(--text-secondary);width:130px;flex-shrink:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bar-track{flex:1;height:24px;background:var(--bg-tertiary);border-radius:6px;display:flex;overflow:hidden}.bar-segment{height:100%;transition:width .8s cubic-bezier(.4,0,.2,1);min-width:0}.bar--success{background:var(--color-success)}.bar--partial{background:var(--color-partial)}.bar--failed{background:var(--color-failed)}.bar--premium{background:var(--color-premium)}.bar--standard{background:var(--color-standard)}.bar--utility{background:var(--color-utility)}.bar--economy{background:var(--color-economy)}.bar--accent{background:var(--accent-blue)}.bar-value{font-size:.8125rem;font-weight:600;color:var(--text-primary);width:36px;text-align:right;flex-shrink:0;font-variant-numeric:tabular-nums}.donut-container{display:flex;align-items:center;justify-content:center;gap:32px;flex-wrap:wrap}.donut-wrap{position:relative;width:180px;height:180px;flex-shrink:0}.donut-svg{width:100%;height:100%}.donut-svg circle{transition:stroke-dasharray .8s cubic-bezier(.4,0,.2,1),stroke-dashoffset .8s cubic-bezier(.4,0,.2,1)}.donut-center{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center}.donut-total{display:block;font-size:1.5rem;font-weight:700;color:var(--text-primary);line-height:1}.donut-total-label{display:block;font-size:.6875rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.08em;margin-top:2px}.donut-legend{display:flex;flex-direction:column;gap:10px}.legend-item{display:flex;align-items:center;gap:8px;font-size:.8125rem}.legend-dot{width:10px;height:10px;border-radius:3px;flex-shrink:0}.legend-name{color:var(--text-secondary);text-transform:capitalize}.legend-count{color:var(--text-tertiary);font-variant-numeric:tabular-nums;margin-left:auto}.timeline-svg{width:100%;height:auto;display:block}.timeline-svg text{font-family:inherit}.timeline-legend{display:flex;gap:16px;justify-content:center;margin-top:12px}.timeline-legend__item{display:flex;align-items:center;gap:6px;font-size:.75rem;color:var(--text-tertiary)}.timeline-legend__dot{width:8px;height:8px;border-radius:2px}.pipeline{display:flex;align-items:stretch;gap:0;overflow-x:auto;padding:8px 0}.pipeline-stage{flex:1;min-width:140px;display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px 12px;position:relative}.pipeline-stage:not(:last-child):after{content:"";position:absolute;right:-1px;top:50%;transform:translateY(-50%);width:2px;height:40%;background:var(--border-color)}.pipeline-stage__icon{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:1rem}.pipeline-stage__icon--pending{background:#64748b26;color:#94a3b8;border:1px solid rgba(100,116,139,.2)}.pipeline-stage__icon--active{background:#3b82f626;color:#60a5fa;border:1px solid rgba(59,130,246,.3);animation:pulse-glow 2s ease-in-out infinite}.pipeline-stage__icon--review{background:#f59e0b26;color:#fbbf24;border:1px solid rgba(245,158,11,.3)}.pipeline-stage__icon--done{background:#22c55e26;color:#4ade80;border:1px solid rgba(34,197,94,.3)}@keyframes pulse-glow{0%,to{box-shadow:0 0 #3b82f633}50%{box-shadow:0 0 12px 4px #3b82f626}}.pipeline-stage__count{font-size:1.5rem;font-weight:700;color:var(--text-primary);line-height:1}.pipeline-stage__label{font-size:.75rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.04em;font-weight:500}.pipeline-arrow{display:flex;align-items:center;color:var(--text-tertiary);font-size:1.25rem;padding:0 4px;flex-shrink:0}.exec-log{display:flex;flex-direction:column}.exec-step{display:flex;gap:16px;padding:14px 0;position:relative}.exec-step+.exec-step{border-top:1px solid rgba(255,255,255,.03)}.exec-step__indicator{display:flex;flex-direction:column;align-items:center;flex-shrink:0;width:32px}.exec-step__dot{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.6875rem;font-weight:700;flex-shrink:0}.exec-step__dot--success{background:#22c55e26;color:var(--color-success);border:1.5px solid rgba(34,197,94,.3)}.exec-step__dot--partial{background:#f59e0b26;color:var(--color-partial);border:1.5px solid rgba(245,158,11,.3)}.exec-step__dot--failed{background:#ef444426;color:var(--color-failed);border:1.5px solid rgba(239,68,68,.3)}.exec-step__line{flex:1;width:1.5px;background:var(--border-color);margin-top:4px}.exec-step__content{flex:1;min-width:0}.exec-step__header{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.exec-step__agent{font-size:.875rem;font-weight:600;color:var(--text-primary)}.exec-step__badge{display:inline-flex;align-items:center;padding:2px 8px;font-size:.6875rem;font-weight:600;border-radius:100px;text-transform:capitalize}.exec-step__badge--success{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.exec-step__badge--partial{background:#f59e0b1f;color:var(--color-partial);border:1px solid rgba(245,158,11,.2)}.exec-step__badge--failed{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.exec-step__task{font-size:.8125rem;color:var(--text-secondary);margin-top:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.exec-step__meta{display:flex;gap:16px;margin-top:6px;font-size:.6875rem;color:var(--text-tertiary)}.exec-step__meta-item{display:flex;align-items:center;gap:4px}.panel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px}.panel-item{background:var(--bg-tertiary);border-radius:8px;padding:16px;display:flex;flex-direction:column;gap:8px;border:1px solid transparent;transition:border-color var(--transition-fast)}.panel-item:hover{border-color:var(--border-color)}.panel-item__header{display:flex;align-items:center;justify-content:space-between}.panel-item__key{font-size:.8125rem;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.panel-item__verdict{font-size:.6875rem;font-weight:700;padding:2px 8px;border-radius:4px;text-transform:uppercase;letter-spacing:.04em}.panel-item__verdict--pass{background:#22c55e26;color:var(--color-success)}.panel-item__verdict--block{background:#ef444426;color:var(--color-failed)}.panel-item__votes{display:flex;gap:4px}.panel-item__vote{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.625rem;font-weight:700}.panel-item__vote--pass{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.panel-item__vote--block{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.panel-item__fixes{font-size:.6875rem;color:var(--text-tertiary)}.panel-item__meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px;padding-top:8px;border-top:1px solid var(--border-color)}.panel-item__meta-item{font-size:.625rem;color:var(--text-tertiary);white-space:nowrap}.sessions-table{width:100%;border-collapse:collapse;font-size:.8125rem}.sessions-table thead{position:sticky;top:0}.sessions-table th{padding:12px 16px;font-size:.6875rem;font-weight:600;color:var(--text-tertiary);text-align:left;text-transform:uppercase;letter-spacing:.06em;background:var(--bg-tertiary);border-bottom:1px solid var(--border-color)}.sessions-table th:last-child,.sessions-table td:last-child{text-align:right}.sessions-table th:nth-child(5),.sessions-table td:nth-child(5){text-align:right}.sessions-table td{padding:10px 16px;color:var(--text-secondary);border-bottom:1px solid rgba(255,255,255,.03);white-space:nowrap}.sessions-table tr:hover td{background:#ffffff05}.sessions-table .td-agent{font-weight:500;color:var(--text-primary)}.sessions-table .td-task{max-width:260px;overflow:hidden;text-overflow:ellipsis}.outcome-badge{display:inline-flex;align-items:center;padding:3px 10px;font-size:.6875rem;font-weight:600;border-radius:100px;text-transform:capitalize}.outcome-badge--success{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.outcome-badge--partial{background:#f59e0b1f;color:var(--color-partial);border:1px solid rgba(245,158,11,.2)}.outcome-badge--failed{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.td-num{font-variant-numeric:tabular-nums;text-align:right}.td-issue{font-size:.75rem;color:var(--text-accent);font-weight:500;font-variant-numeric:tabular-nums}.loading-skeleton{display:flex;align-items:center;justify-content:center;min-height:200px;color:var(--text-tertiary);font-size:.8125rem}.loading-skeleton:after{content:"Loading data…";animation:fade-pulse 1.5s ease-in-out infinite}@keyframes fade-pulse{0%,to{opacity:.4}50%{opacity:1}}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;text-align:center;gap:12px}.empty-state__icon{font-size:2rem;opacity:.4}.empty-state__text{font-size:.875rem;color:var(--text-tertiary);max-width:320px}.empty-state--enhanced{padding:56px 32px;gap:16px;border:1px dashed rgba(167,139,250,.15);border-radius:12px;background:radial-gradient(ellipse 300px 200px at 50% 30%,rgba(99,102,241,.04) 0%,transparent 70%),var(--bg-tertiary);position:relative;overflow:hidden}.empty-state--enhanced:before{content:"";position:absolute;inset:0;background:repeating-linear-gradient(0deg,transparent,transparent 23px,rgba(255,255,255,.015) 23px,rgba(255,255,255,.015) 24px);pointer-events:none}.empty-state__icon-wrap{width:64px;height:64px;display:flex;align-items:center;justify-content:center;border-radius:16px;background:#a78bfa0f;border:1px solid rgba(167,139,250,.12);color:var(--text-accent);animation:empty-breathe 4s ease-in-out infinite}@keyframes empty-breathe{0%,to{box-shadow:0 0 #a78bfa14;transform:scale(1)}50%{box-shadow:0 0 20px 4px #a78bfa0f;transform:scale(1.03)}}.empty-state__title{font-size:.9375rem;font-weight:600;color:var(--text-secondary);letter-spacing:-.01em}.empty-state__desc{font-size:.8125rem;color:var(--text-tertiary);max-width:380px;line-height:1.55}.kpi-card__hint{color:var(--text-tertiary);font-style:italic;font-size:.6875rem}.kpi-row--empty .kpi-card{border-style:dashed;border-color:#ffffff0a}.kpi-row--empty .kpi-card__value{color:var(--text-tertiary);opacity:.5}.welcome-banner{position:relative;background:var(--bg-secondary);border:1px solid transparent;border-radius:16px;padding:48px 40px;overflow:hidden;z-index:1}.welcome-banner:before{content:"";position:absolute;inset:-1px;border-radius:16px;padding:1px;background:linear-gradient(135deg,#a78bfa4d,#6366f126,#3b82f61a 60%,#a78bfa33);-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;pointer-events:none;z-index:0}.welcome-banner__glow{position:absolute;top:-60px;left:50%;transform:translate(-50%);width:500px;height:300px;background:radial-gradient(ellipse at center,rgba(167,139,250,.08) 0%,rgba(99,102,241,.04) 40%,transparent 70%);pointer-events:none;z-index:0}.welcome-banner__content{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;text-align:center;gap:20px}.welcome-banner__icon{width:72px;height:72px;display:flex;align-items:center;justify-content:center;border-radius:20px;background:#a78bfa14;border:1px solid rgba(167,139,250,.15);color:var(--text-accent);animation:welcome-float 6s ease-in-out infinite}@keyframes welcome-float{0%,to{transform:translateY(0);box-shadow:0 8px 32px #a78bfa14}50%{transform:translateY(-6px);box-shadow:0 16px 48px #a78bfa1f}}.welcome-banner__title{font-size:1.375rem;font-weight:700;color:var(--text-primary);letter-spacing:-.02em;line-height:1.3}.welcome-banner__subtitle{font-size:.9375rem;color:var(--text-secondary);max-width:480px;line-height:1.6}.welcome-banner__steps{display:flex;gap:20px;margin-top:12px;flex-wrap:wrap;justify-content:center}.welcome-step{display:flex;align-items:flex-start;gap:12px;text-align:left;padding:16px 20px;background:#ffffff05;border:1px solid rgba(255,255,255,.05);border-radius:12px;min-width:200px;max-width:220px;transition:border-color var(--transition-fast),background var(--transition-fast)}.welcome-step:hover{border-color:#a78bfa26;background:#ffffff08}.welcome-step__num{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:700;color:var(--text-accent);background:#a78bfa1a;border:1px solid rgba(167,139,250,.2);flex-shrink:0}.welcome-step__text{display:flex;flex-direction:column;gap:3px}.welcome-step__text strong{font-size:.8125rem;font-weight:600;color:var(--text-primary)}.welcome-step__text span{font-size:.75rem;color:var(--text-tertiary);line-height:1.4}@media(max-width:640px){.welcome-banner{padding:32px 24px}.welcome-banner__steps{flex-direction:column;align-items:center}.welcome-step{max-width:100%;width:100%}}@keyframes slide-up{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.dash-main>*{animation:slide-up .5s ease-out backwards}.dash-main>*:nth-child(1){animation-delay:0ms}.dash-main>*:nth-child(2){animation-delay:60ms}.dash-main>*:nth-child(3){animation-delay:.12s}.dash-main>*:nth-child(4){animation-delay:.18s}.dash-main>*:nth-child(5){animation-delay:.24s}.dash-main>*:nth-child(6){animation-delay:.3s}.dash-main>*:nth-child(7){animation-delay:.36s}.dash-main>*:nth-child(8){animation-delay:.42s}.dash-main>*:nth-child(9){animation-delay:.48s}.dash-main>*:nth-child(10){animation-delay:.54s}.dash-main>*:nth-child(11){animation-delay:.6s}@media(max-width:640px){.bar-label{width:90px;font-size:.75rem}.donut-container{flex-direction:column;align-items:center}.donut-wrap{width:150px;height:150px}.pipeline{gap:0}.pipeline-stage{min-width:100px;padding:12px 8px}.panel-grid{grid-template-columns:1fr}.sessions-table th:nth-child(3),.sessions-table td:nth-child(3){display:none}}.filter-bar{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;padding:16px 20px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px}.filter-group{display:flex;flex-direction:column;gap:4px;min-width:0}.filter-label{font-size:.6875rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.filter-input,.filter-select{height:34px;padding:0 10px;font-size:.8125rem;color:var(--text-primary);background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;outline:none;transition:border-color var(--transition-fast);font-family:inherit}.filter-input:focus,.filter-select:focus{border-color:var(--border-accent)}.filter-input{width:140px;color-scheme:dark}.filter-select{min-width:140px;cursor:pointer;appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%235a5a6e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;padding-right:28px}.filter-reset{height:34px;font-size:.75rem}.dash-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;font-size:.8125rem;font-weight:500;font-family:inherit;border:none;border-radius:8px;cursor:pointer;transition:background var(--transition-fast),color var(--transition-fast)}.dash-btn--ghost{color:var(--text-secondary);background:#ffffff0f}.dash-btn--ghost:hover{color:var(--text-primary);background:#ffffff1a}.dash-header__actions{display:flex;align-items:center;gap:8px}@media(max-width:480px){.dash-header__inner{padding:0 12px}.dash-main{padding:12px;gap:12px}.kpi-card,.chart-card__header{padding:14px 16px}.chart-card__body{padding:12px 16px 16px}.filter-bar{padding:12px;gap:8px}.filter-input,.filter-select{width:100%;min-width:unset}.filter-group{flex:1 1 calc(50% - 4px)}.filter-reset{width:100%}.dash-header__title{font-size:.875rem}.exec-step__meta{flex-direction:column;gap:2px}.sessions-table th:nth-child(5),.sessions-table td:nth-child(5),.sessions-table th:nth-child(6),.sessions-table td:nth-child(6),.sessions-table th:nth-child(7),.sessions-table td:nth-child(7),.sessions-table th:nth-child(8),.sessions-table td:nth-child(8){display:none}}@media(max-width:768px){.charts-row{grid-template-columns:1fr}.pipeline{flex-wrap:wrap;gap:8px}.pipeline-arrow{display:none}.pipeline-stage{flex:1 1 calc(50% - 4px);min-width:100px}.tier-chart .donut-container,.donut-container{flex-direction:column;align-items:center}.sessions-table{font-size:.75rem}.sessions-table th,.sessions-table td{padding:8px 6px}}.convoy-overview{display:flex;flex-wrap:wrap;gap:24px;margin-bottom:20px}.convoy-stat{display:flex;flex-direction:column;gap:4px}.convoy-stat__label{font-size:.75rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.convoy-stat__value{font-size:.95rem;color:var(--text-primary)}.convoy-progress{display:flex;align-items:center;gap:12px;margin-bottom:20px}.convoy-progress__bar{flex:1;height:8px;background:var(--bg-tertiary);border-radius:4px;overflow:hidden}.convoy-progress__fill{height:100%;background:var(--gradient-accent);border-radius:4px;transition:width var(--transition-base)}.convoy-progress__label{font-size:.8rem;color:var(--text-secondary);white-space:nowrap}.convoy-tasks{margin-top:8px}.convoy-chain{display:flex;align-items:stretch;gap:0;overflow-x:auto;padding:1rem 0 1.5rem;scrollbar-width:thin;scrollbar-color:var(--border-color) transparent}.convoy-chain::-webkit-scrollbar{height:4px}.convoy-chain::-webkit-scrollbar-track{background:transparent}.convoy-chain::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:2px}.convoy-chain__connector{display:flex;align-items:center;padding:0 .5rem;color:var(--text-tertiary);font-size:1.1rem;flex-shrink:0}.convoy-chain__node{display:flex;flex-direction:column;align-items:center;gap:6px;padding:12px 16px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:10px;min-width:140px;cursor:pointer;transition:background var(--transition-fast),border-color var(--transition-fast),transform var(--transition-fast),box-shadow var(--transition-fast);flex-shrink:0}.convoy-chain__node:hover{background:var(--bg-card-hover);transform:translateY(-2px);box-shadow:0 4px 12px #0000004d}.convoy-chain__node-name{font-size:.8rem;font-weight:600;color:var(--text-primary);text-align:center;word-break:break-word;max-width:120px}.convoy-chain__node-meta{font-size:.72rem;color:var(--text-tertiary);text-align:center}.convoy-chain__node--active{border-color:var(--accent-purple);box-shadow:0 0 0 1px var(--accent-purple),0 0 12px #a78bfa33;animation:convoy-pulse 2s ease-in-out infinite}.convoy-chain__node--done{border-color:#22c55e4d}.convoy-chain__node--failed{border-color:#ef44444d}.convoy-chain__node--pending{opacity:.6}@keyframes convoy-pulse{0%,to{box-shadow:0 0 0 1px var(--accent-purple),0 0 8px #a78bfa26}50%{box-shadow:0 0 0 1px var(--accent-purple),0 0 18px #a78bfa59}}@media(max-width:768px){.convoy-chain{flex-wrap:wrap;gap:8px}.convoy-chain__connector{display:none}.convoy-chain__node{flex:1 1 calc(50% - 4px);min-width:120px}}
1
+ :root{--bg-primary: #0a0a0f;--bg-secondary: #111118;--bg-tertiary: #1a1a24;--bg-card: rgba(255, 255, 255, .03);--bg-card-hover: rgba(255, 255, 255, .06);--text-primary: #f0f0f5;--text-secondary: #8a8a9a;--text-tertiary: #5a5a6e;--text-accent: #a78bfa;--gradient-accent: linear-gradient(135deg, #a78bfa 0%, #6366f1 50%, #3b82f6 100%);--gradient-glow: radial-gradient(ellipse 800px 400px at 50% 0%, rgba(99, 102, 241, .12) 0%, transparent 70%);--border-color: rgba(255, 255, 255, .06);--border-accent: rgba(167, 139, 250, .3);--color-success: #22c55e;--color-partial: #f59e0b;--color-failed: #ef4444;--color-redirected: #64748b;--color-premium: #f59e0b;--color-standard: #a78bfa;--color-utility: #3b82f6;--color-economy: #64748b;--accent-blue: #3b82f6;--accent-purple: #a78bfa;--accent-indigo: #6366f1;--max-width: 1280px;--transition-fast: .15s cubic-bezier(.4, 0, .2, 1);--transition-base: .3s cubic-bezier(.4, 0, .2, 1)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html{font-size:16px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Inter,Roboto,Helvetica,Arial,sans-serif;background-color:var(--bg-primary);color:var(--text-primary);line-height:1.6;overflow-x:hidden;min-height:100vh}.dash-header{position:sticky;top:0;z-index:50;background:#0a0a0fd9;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-bottom:1px solid var(--border-color)}.dash-header__inner{max-width:var(--max-width);margin:0 auto;padding:0 24px;height:56px;display:flex;align-items:center;justify-content:space-between}.dash-header__brand{display:flex;align-items:center;gap:10px}.dash-header__icon{width:32px;height:32px;border-radius:8px;object-fit:contain}.dash-header__title{font-size:1rem;font-weight:600;color:var(--text-primary)}.dash-layout{display:flex;max-width:var(--max-width);margin:0 auto;position:relative}.dash-sidebar{position:sticky;top:56px;height:calc(100vh - 56px);width:180px;flex-shrink:0;padding:24px 0 24px 24px;overflow-y:auto;display:none}@media(min-width:1024px){.dash-sidebar{display:block}}.dash-sidebar__list{list-style:none;display:flex;flex-direction:column;gap:2px}.dash-sidebar__link{display:block;padding:8px 16px;font-size:.8125rem;font-weight:500;color:var(--text-tertiary);text-decoration:none;border-radius:8px;transition:color var(--transition-fast),background var(--transition-fast)}.dash-sidebar__link:hover{color:var(--text-secondary);background:#ffffff0a}.dash-sidebar__link--active{color:var(--text-accent);background:#a78bfa14;font-weight:600}.dash-main{flex:1;min-width:0;max-width:var(--max-width);margin:0 auto;padding:24px;display:flex;flex-direction:column;gap:20px;position:relative}.dash-main:before{content:"";position:fixed;top:0;left:50%;transform:translate(-50%);width:100%;height:600px;background:var(--gradient-glow);pointer-events:none;z-index:0}.dash-main>*{position:relative;z-index:1}[data-nav-section]{scroll-margin-top:72px}.kpi-row{display:grid;grid-template-columns:1fr;gap:12px}@media(min-width:480px){.kpi-row{grid-template-columns:repeat(2,1fr)}}@media(min-width:960px){.kpi-row{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}}.kpi-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;padding:20px 24px;display:flex;flex-direction:column;gap:4px;transition:border-color var(--transition-fast)}.kpi-card:hover{border-color:#ffffff1a}.kpi-card__label{font-size:.75rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.kpi-card__value{font-size:2rem;font-weight:700;color:var(--text-primary);line-height:1.2;letter-spacing:-.02em}.kpi-card__sub{font-size:.75rem;color:var(--text-secondary);display:flex;align-items:center;gap:4px}.kpi-trend{font-weight:600}.kpi-trend--up{color:var(--color-success)}.kpi-trend--down{color:var(--color-failed)}.kpi-trend--neutral{color:var(--text-tertiary)}.chart-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;overflow:hidden;transition:border-color var(--transition-fast)}.chart-card:hover{border-color:#ffffff1a}.chart-card__header{padding:20px 24px 8px}.chart-card__title{font-size:.9375rem;font-weight:600;color:var(--text-primary)}.chart-card__desc{font-size:.75rem;color:var(--text-tertiary);margin-top:2px}.chart-card__body{padding:16px 24px 24px;min-height:120px}.chart-card__body--table{padding:0}.charts-row{display:grid;grid-template-columns:1fr;gap:20px}@media(min-width:768px){.charts-row{grid-template-columns:repeat(2,1fr)}}.bar-row{display:flex;align-items:center;gap:12px;padding:6px 0}.bar-row+.bar-row{border-top:1px solid rgba(255,255,255,.03)}.bar-label{font-size:.8125rem;color:var(--text-secondary);width:130px;flex-shrink:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bar-track{flex:1;height:24px;background:var(--bg-tertiary);border-radius:6px;display:flex;overflow:hidden}.bar-segment{height:100%;transition:width .8s cubic-bezier(.4,0,.2,1);min-width:0}.bar--success{background:var(--color-success)}.bar--partial{background:var(--color-partial)}.bar--failed{background:var(--color-failed)}.bar--premium{background:var(--color-premium)}.bar--standard{background:var(--color-standard)}.bar--utility{background:var(--color-utility)}.bar--economy{background:var(--color-economy)}.bar--accent{background:var(--accent-blue)}.bar-value{font-size:.8125rem;font-weight:600;color:var(--text-primary);width:36px;text-align:right;flex-shrink:0;font-variant-numeric:tabular-nums}.donut-container{display:flex;align-items:center;justify-content:center;gap:32px;flex-wrap:wrap}.donut-wrap{position:relative;width:180px;height:180px;flex-shrink:0}.donut-svg{width:100%;height:100%}.donut-svg circle{transition:stroke-dasharray .8s cubic-bezier(.4,0,.2,1),stroke-dashoffset .8s cubic-bezier(.4,0,.2,1)}.donut-center{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center}.donut-total{display:block;font-size:1.5rem;font-weight:700;color:var(--text-primary);line-height:1}.donut-total-label{display:block;font-size:.6875rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.08em;margin-top:2px}.donut-legend{display:flex;flex-direction:column;gap:10px}.legend-item{display:flex;align-items:center;gap:8px;font-size:.8125rem}.legend-dot{width:10px;height:10px;border-radius:3px;flex-shrink:0}.legend-name{color:var(--text-secondary);text-transform:capitalize}.legend-count{color:var(--text-tertiary);font-variant-numeric:tabular-nums;margin-left:auto}.timeline-svg{width:100%;height:auto;display:block}.timeline-svg text{font-family:inherit}.timeline-legend{display:flex;gap:16px;justify-content:center;margin-top:12px}.timeline-legend__item{display:flex;align-items:center;gap:6px;font-size:.75rem;color:var(--text-tertiary)}.timeline-legend__dot{width:8px;height:8px;border-radius:2px}.pipeline{display:flex;align-items:stretch;gap:0;overflow-x:auto;padding:8px 0}.pipeline-stage{flex:1;min-width:140px;display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px 12px;position:relative}.pipeline-stage:not(:last-child):after{content:"";position:absolute;right:-1px;top:50%;transform:translateY(-50%);width:2px;height:40%;background:var(--border-color)}.pipeline-stage__icon{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:1rem}.pipeline-stage__icon--pending{background:#64748b26;color:#94a3b8;border:1px solid rgba(100,116,139,.2)}.pipeline-stage__icon--active{background:#3b82f626;color:#60a5fa;border:1px solid rgba(59,130,246,.3);animation:pulse-glow 2s ease-in-out infinite}.pipeline-stage__icon--review{background:#f59e0b26;color:#fbbf24;border:1px solid rgba(245,158,11,.3)}.pipeline-stage__icon--done{background:#22c55e26;color:#4ade80;border:1px solid rgba(34,197,94,.3)}@keyframes pulse-glow{0%,to{box-shadow:0 0 #3b82f633}50%{box-shadow:0 0 12px 4px #3b82f626}}.pipeline-stage__count{font-size:1.5rem;font-weight:700;color:var(--text-primary);line-height:1}.pipeline-stage__label{font-size:.75rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.04em;font-weight:500}.pipeline-arrow{display:flex;align-items:center;color:var(--text-tertiary);font-size:1.25rem;padding:0 4px;flex-shrink:0}.exec-log{display:flex;flex-direction:column}.exec-step{display:flex;gap:16px;padding:14px 0;position:relative}.exec-step+.exec-step{border-top:1px solid rgba(255,255,255,.03)}.exec-step__indicator{display:flex;flex-direction:column;align-items:center;flex-shrink:0;width:32px}.exec-step__dot{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.6875rem;font-weight:700;flex-shrink:0}.exec-step__dot--success{background:#22c55e26;color:var(--color-success);border:1.5px solid rgba(34,197,94,.3)}.exec-step__dot--partial{background:#f59e0b26;color:var(--color-partial);border:1.5px solid rgba(245,158,11,.3)}.exec-step__dot--failed{background:#ef444426;color:var(--color-failed);border:1.5px solid rgba(239,68,68,.3)}.exec-step__line{flex:1;width:1.5px;background:var(--border-color);margin-top:4px}.exec-step__content{flex:1;min-width:0}.exec-step__header{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.exec-step__agent{font-size:.875rem;font-weight:600;color:var(--text-primary)}.exec-step__badge{display:inline-flex;align-items:center;padding:2px 8px;font-size:.6875rem;font-weight:600;border-radius:100px;text-transform:capitalize}.exec-step__badge--success{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.exec-step__badge--partial{background:#f59e0b1f;color:var(--color-partial);border:1px solid rgba(245,158,11,.2)}.exec-step__badge--failed{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.exec-step__task{font-size:.8125rem;color:var(--text-secondary);margin-top:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.exec-step__meta{display:flex;gap:16px;margin-top:6px;font-size:.6875rem;color:var(--text-tertiary)}.exec-step__meta-item{display:flex;align-items:center;gap:4px}.panel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px}.panel-item{background:var(--bg-tertiary);border-radius:8px;padding:16px;display:flex;flex-direction:column;gap:8px;border:1px solid transparent;transition:border-color var(--transition-fast)}.panel-item:hover{border-color:var(--border-color)}.panel-item__header{display:flex;align-items:center;justify-content:space-between}.panel-item__key{font-size:.8125rem;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.panel-item__verdict{font-size:.6875rem;font-weight:700;padding:2px 8px;border-radius:4px;text-transform:uppercase;letter-spacing:.04em}.panel-item__verdict--pass{background:#22c55e26;color:var(--color-success)}.panel-item__verdict--block{background:#ef444426;color:var(--color-failed)}.panel-item__votes{display:flex;gap:4px}.panel-item__vote{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.625rem;font-weight:700}.panel-item__vote--pass{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.panel-item__vote--block{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.panel-item__fixes{font-size:.6875rem;color:var(--text-tertiary)}.panel-item__meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px;padding-top:8px;border-top:1px solid var(--border-color)}.panel-item__meta-item{font-size:.625rem;color:var(--text-tertiary);white-space:nowrap}.sessions-table{width:100%;border-collapse:collapse;font-size:.8125rem}.sessions-table thead{position:sticky;top:0}.sessions-table th{padding:12px 16px;font-size:.6875rem;font-weight:600;color:var(--text-tertiary);text-align:left;text-transform:uppercase;letter-spacing:.06em;background:var(--bg-tertiary);border-bottom:1px solid var(--border-color)}.sessions-table th:last-child,.sessions-table td:last-child{text-align:right}.sessions-table th:nth-child(5),.sessions-table td:nth-child(5){text-align:right}.sessions-table td{padding:10px 16px;color:var(--text-secondary);border-bottom:1px solid rgba(255,255,255,.03);white-space:nowrap}.sessions-table tr:hover td{background:#ffffff05}.sessions-table .td-agent{font-weight:500;color:var(--text-primary)}.sessions-table .td-task{max-width:260px;overflow:hidden;text-overflow:ellipsis}.outcome-badge{display:inline-flex;align-items:center;padding:3px 10px;font-size:.6875rem;font-weight:600;border-radius:100px;text-transform:capitalize}.outcome-badge--success{background:#22c55e1f;color:var(--color-success);border:1px solid rgba(34,197,94,.2)}.outcome-badge--partial{background:#f59e0b1f;color:var(--color-partial);border:1px solid rgba(245,158,11,.2)}.outcome-badge--failed{background:#ef44441f;color:var(--color-failed);border:1px solid rgba(239,68,68,.2)}.td-num{font-variant-numeric:tabular-nums;text-align:right}.td-issue{font-size:.75rem;color:var(--text-accent);font-weight:500;font-variant-numeric:tabular-nums}.loading-skeleton{display:flex;align-items:center;justify-content:center;min-height:200px;color:var(--text-tertiary);font-size:.8125rem}.loading-skeleton:after{content:"Loading data…";animation:fade-pulse 1.5s ease-in-out infinite}@keyframes fade-pulse{0%,to{opacity:.4}50%{opacity:1}}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;text-align:center;gap:12px}.empty-state__icon{font-size:2rem;opacity:.4}.empty-state__text{font-size:.875rem;color:var(--text-tertiary);max-width:320px}.empty-state--enhanced{padding:56px 32px;gap:16px;border:1px dashed rgba(167,139,250,.15);border-radius:12px;background:radial-gradient(ellipse 300px 200px at 50% 30%,rgba(99,102,241,.04) 0%,transparent 70%),var(--bg-tertiary);position:relative;overflow:hidden}.empty-state--enhanced:before{content:"";position:absolute;inset:0;background:repeating-linear-gradient(0deg,transparent,transparent 23px,rgba(255,255,255,.015) 23px,rgba(255,255,255,.015) 24px);pointer-events:none}.empty-state__icon-wrap{width:64px;height:64px;display:flex;align-items:center;justify-content:center;border-radius:16px;background:#a78bfa0f;border:1px solid rgba(167,139,250,.12);color:var(--text-accent);animation:empty-breathe 4s ease-in-out infinite}@keyframes empty-breathe{0%,to{box-shadow:0 0 #a78bfa14;transform:scale(1)}50%{box-shadow:0 0 20px 4px #a78bfa0f;transform:scale(1.03)}}.empty-state__title{font-size:.9375rem;font-weight:600;color:var(--text-secondary);letter-spacing:-.01em}.empty-state__desc{font-size:.8125rem;color:var(--text-tertiary);max-width:380px;line-height:1.55}.kpi-card__hint{color:var(--text-tertiary);font-style:italic;font-size:.6875rem}.kpi-row--empty .kpi-card{border-style:dashed;border-color:#ffffff0a}.kpi-row--empty .kpi-card__value{color:var(--text-tertiary);opacity:.5}.welcome-banner{position:relative;background:var(--bg-secondary);border:1px solid transparent;border-radius:16px;padding:48px 40px;overflow:hidden;z-index:1}.welcome-banner:before{content:"";position:absolute;inset:-1px;border-radius:16px;padding:1px;background:linear-gradient(135deg,#a78bfa4d,#6366f126,#3b82f61a 60%,#a78bfa33);-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;pointer-events:none;z-index:0}.welcome-banner__glow{position:absolute;top:-60px;left:50%;transform:translate(-50%);width:500px;height:300px;background:radial-gradient(ellipse at center,rgba(167,139,250,.08) 0%,rgba(99,102,241,.04) 40%,transparent 70%);pointer-events:none;z-index:0}.welcome-banner__content{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;text-align:center;gap:20px}.welcome-banner__icon{width:72px;height:72px;display:flex;align-items:center;justify-content:center;border-radius:20px;background:#a78bfa14;border:1px solid rgba(167,139,250,.15);color:var(--text-accent);animation:welcome-float 6s ease-in-out infinite}@keyframes welcome-float{0%,to{transform:translateY(0);box-shadow:0 8px 32px #a78bfa14}50%{transform:translateY(-6px);box-shadow:0 16px 48px #a78bfa1f}}.welcome-banner__title{font-size:1.375rem;font-weight:700;color:var(--text-primary);letter-spacing:-.02em;line-height:1.3}.welcome-banner__subtitle{font-size:.9375rem;color:var(--text-secondary);max-width:480px;line-height:1.6}.welcome-banner__steps{display:flex;gap:20px;margin-top:12px;flex-wrap:wrap;justify-content:center}.welcome-step{display:flex;align-items:flex-start;gap:12px;text-align:left;padding:16px 20px;background:#ffffff05;border:1px solid rgba(255,255,255,.05);border-radius:12px;min-width:200px;max-width:220px;transition:border-color var(--transition-fast),background var(--transition-fast)}.welcome-step:hover{border-color:#a78bfa26;background:#ffffff08}.welcome-step__num{width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:700;color:var(--text-accent);background:#a78bfa1a;border:1px solid rgba(167,139,250,.2);flex-shrink:0}.welcome-step__text{display:flex;flex-direction:column;gap:3px}.welcome-step__text strong{font-size:.8125rem;font-weight:600;color:var(--text-primary)}.welcome-step__text span{font-size:.75rem;color:var(--text-tertiary);line-height:1.4}@media(max-width:640px){.welcome-banner{padding:32px 24px}.welcome-banner__steps{flex-direction:column;align-items:center}.welcome-step{max-width:100%;width:100%}}@keyframes slide-up{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.dash-main>*{animation:slide-up .5s ease-out backwards}.dash-main>*:nth-child(1){animation-delay:0ms}.dash-main>*:nth-child(2){animation-delay:60ms}.dash-main>*:nth-child(3){animation-delay:.12s}.dash-main>*:nth-child(4){animation-delay:.18s}.dash-main>*:nth-child(5){animation-delay:.24s}.dash-main>*:nth-child(6){animation-delay:.3s}.dash-main>*:nth-child(7){animation-delay:.36s}.dash-main>*:nth-child(8){animation-delay:.42s}.dash-main>*:nth-child(9){animation-delay:.48s}.dash-main>*:nth-child(10){animation-delay:.54s}.dash-main>*:nth-child(11){animation-delay:.6s}@media(max-width:640px){.bar-label{width:90px;font-size:.75rem}.donut-container{flex-direction:column;align-items:center}.donut-wrap{width:150px;height:150px}.pipeline{gap:0}.pipeline-stage{min-width:100px;padding:12px 8px}.panel-grid{grid-template-columns:1fr}.sessions-table th:nth-child(3),.sessions-table td:nth-child(3){display:none}}.filter-bar{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;padding:16px 20px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px}.filter-group{display:flex;flex-direction:column;gap:4px;min-width:0}.filter-label{font-size:.6875rem;font-weight:500;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.filter-input,.filter-select{height:34px;padding:0 10px;font-size:.8125rem;color:var(--text-primary);background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;outline:none;transition:border-color var(--transition-fast);font-family:inherit}.filter-input:focus,.filter-select:focus{border-color:var(--border-accent)}.filter-input{width:140px;color-scheme:dark}.filter-select{min-width:140px;cursor:pointer;appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%235a5a6e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;padding-right:28px}.filter-reset{height:34px;font-size:.75rem}.dash-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;font-size:.8125rem;font-weight:500;font-family:inherit;border:none;border-radius:8px;cursor:pointer;transition:background var(--transition-fast),color var(--transition-fast)}.dash-btn--ghost{color:var(--text-secondary);background:#ffffff0f}.dash-btn--ghost:hover{color:var(--text-primary);background:#ffffff1a}.dash-header__actions{display:flex;align-items:center;gap:8px}@media(max-width:480px){.dash-header__inner{padding:0 12px}.dash-main{padding:12px;gap:12px}.kpi-card,.chart-card__header{padding:14px 16px}.chart-card__body{padding:12px 16px 16px}.filter-bar{padding:12px;gap:8px}.filter-input,.filter-select{width:100%;min-width:unset}.filter-group{flex:1 1 calc(50% - 4px)}.filter-reset{width:100%}.dash-header__title{font-size:.875rem}.exec-step__meta{flex-direction:column;gap:2px}.sessions-table th:nth-child(5),.sessions-table td:nth-child(5),.sessions-table th:nth-child(6),.sessions-table td:nth-child(6),.sessions-table th:nth-child(7),.sessions-table td:nth-child(7),.sessions-table th:nth-child(8),.sessions-table td:nth-child(8){display:none}}@media(max-width:768px){.charts-row{grid-template-columns:1fr}.pipeline{flex-wrap:wrap;gap:8px}.pipeline-arrow{display:none}.pipeline-stage{flex:1 1 calc(50% - 4px);min-width:100px}.tier-chart .donut-container,.donut-container{flex-direction:column;align-items:center}.sessions-table{font-size:.75rem}.sessions-table th,.sessions-table td{padding:8px 6px}}.convoy-overview{display:flex;flex-wrap:wrap;gap:24px;margin-bottom:20px}.convoy-stat{display:flex;flex-direction:column;gap:4px}.convoy-stat__label{font-size:.75rem;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.05em}.convoy-stat__value{font-size:.95rem;color:var(--text-primary)}.convoy-stat__value--error{color:var(--color-failed)}.convoy-progress{display:flex;align-items:center;gap:12px;margin-bottom:20px}.convoy-progress__bar{flex:1;height:8px;background:var(--bg-tertiary);border-radius:4px;overflow:hidden}.convoy-progress__fill{height:100%;background:var(--gradient-accent);border-radius:4px;transition:width var(--transition-base)}.convoy-progress__label{font-size:.8rem;color:var(--text-secondary);white-space:nowrap}.convoy-tasks{margin-top:8px}.convoy-chain{display:flex;align-items:stretch;gap:0;overflow-x:auto;padding:1rem 0 1.5rem;scrollbar-width:thin;scrollbar-color:var(--border-color) transparent}.convoy-chain::-webkit-scrollbar{height:4px}.convoy-chain::-webkit-scrollbar-track{background:transparent}.convoy-chain::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:2px}.convoy-chain__connector{display:flex;align-items:center;padding:0 .5rem;color:var(--text-tertiary);font-size:1.1rem;flex-shrink:0}.convoy-chain__node{display:flex;flex-direction:column;align-items:center;gap:6px;padding:12px 16px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:10px;min-width:140px;cursor:pointer;transition:background var(--transition-fast),border-color var(--transition-fast),transform var(--transition-fast),box-shadow var(--transition-fast);flex-shrink:0}.convoy-chain__node:hover{background:var(--bg-card-hover);transform:translateY(-2px);box-shadow:0 4px 12px #0000004d}.convoy-chain__node-name{font-size:.8rem;font-weight:600;color:var(--text-primary);text-align:center;word-break:break-word;max-width:120px}.convoy-chain__node-meta{font-size:.72rem;color:var(--text-tertiary);text-align:center}.convoy-chain__node--active{border-color:var(--accent-purple);box-shadow:0 0 0 1px var(--accent-purple),0 0 12px #a78bfa33;animation:convoy-pulse 2s ease-in-out infinite}.convoy-chain__node--done{border-color:#22c55e4d}.convoy-chain__node--failed{border-color:#ef44444d}.convoy-chain__node--pending{opacity:.6}@keyframes convoy-pulse{0%,to{box-shadow:0 0 0 1px var(--accent-purple),0 0 8px #a78bfa26}50%{box-shadow:0 0 0 1px var(--accent-purple),0 0 18px #a78bfa59}}@media(max-width:768px){.convoy-chain{flex-wrap:wrap;gap:8px}.convoy-chain__connector{display:none}.convoy-chain__node{flex:1 1 calc(50% - 4px);min-width:120px}}
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Observability Dashboard — OpenCastle</title><meta name="description" content="Real-time observability for OpenCastle multi-agent orchestration — sessions, delegations, model tiers, and quality gates."><meta name="theme-color" content="#0a0a0f"><link rel="icon" type="image/png" sizes="192x192" href="/icon-192.png"><link rel="stylesheet" href="/_astro/index.Cq68OHaZ.css"></head> <body> <header class="dash-header"> <div class="dash-header__inner"> <div class="dash-header__brand"> <img class="dash-header__icon" src="/icon-192.png" alt="OpenCastle" width="32" height="32"> <h1 class="dash-header__title">Observability Dashboard</h1> </div> <div class="dash-header__actions"> <button class="dash-btn dash-btn--ghost" id="export-btn" type="button" title="Export data as JSON"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
1
+ <!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Observability Dashboard — OpenCastle</title><meta name="description" content="Real-time observability for OpenCastle multi-agent orchestration — sessions, delegations, model tiers, and quality gates."><meta name="theme-color" content="#0a0a0f"><link rel="icon" type="image/png" sizes="192x192" href="/icon-192.png"><link rel="stylesheet" href="/_astro/index.DtnyD8a5.css"></head> <body> <header class="dash-header"> <div class="dash-header__inner"> <div class="dash-header__brand"> <img class="dash-header__icon" src="/icon-192.png" alt="OpenCastle" width="32" height="32"> <h1 class="dash-header__title">Observability Dashboard</h1> </div> <div class="dash-header__actions"> <button class="dash-btn dash-btn--ghost" id="export-btn" type="button" title="Export data as JSON"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
2
2
  Export
3
3
  </button> </div> </div> </header> <div class="dash-layout"> <!-- Sidebar Navigation --> <nav class="dash-sidebar" id="dash-sidebar"> <ul class="dash-sidebar__list"> <li><a class="dash-sidebar__link" href="#convoy-section" data-section="convoy-section">Convoy</a></li> <li><a class="dash-sidebar__link" href="#convoy-pipeline-section" data-section="convoy-pipeline-section">Convoy Chain</a></li> <li><a class="dash-sidebar__link dash-sidebar__link--active" href="#kpi-row" data-section="kpi-row">Overview</a></li> <li><a class="dash-sidebar__link" href="#pipeline-section" data-section="pipeline-section">Task Flow</a></li> <li><a class="dash-sidebar__link" href="#agent-section" data-section="agent-section">Agents</a></li> <li><a class="dash-sidebar__link" href="#tier-section" data-section="tier-section">Tiers</a></li> <li><a class="dash-sidebar__link" href="#delegation-section" data-section="delegation-section">Delegations</a></li> <li><a class="dash-sidebar__link" href="#timeline-section" data-section="timeline-section">Timeline</a></li> <li><a class="dash-sidebar__link" href="#model-section" data-section="model-section">Models</a></li> <li><a class="dash-sidebar__link" href="#execution-section" data-section="execution-section">Exec Log</a></li> <li><a class="dash-sidebar__link" href="#panel-section" data-section="panel-section">Panels</a></li> <li><a class="dash-sidebar__link" href="#reviews-section" data-section="reviews-section">Reviews</a></li> <li><a class="dash-sidebar__link" href="#sessions-section" data-section="sessions-section">Sessions</a></li> </ul> </nav> <main class="dash-main"> <!-- Filter Bar --> <div class="filter-bar" id="filter-bar"> <div class="filter-group"> <label class="filter-label" for="filter-date-from">From</label> <input class="filter-input" type="date" id="filter-date-from"> </div> <div class="filter-group"> <label class="filter-label" for="filter-date-to">To</label> <input class="filter-input" type="date" id="filter-date-to"> </div> <div class="filter-group"> <label class="filter-label" for="filter-agent">Agent</label> <select class="filter-select" id="filter-agent"> <option value="">All agents</option> </select> </div> <div class="filter-group"> <label class="filter-label" for="filter-outcome">Outcome</label> <select class="filter-select" id="filter-outcome"> <option value="">All outcomes</option> <option value="success">Success</option> <option value="partial">Partial</option> <option value="failed">Failed</option> </select> </div> <div class="filter-group"> <label class="filter-label" for="filter-convoy">Convoy</label> <select class="filter-select" id="filter-convoy"> <option value="">All convoys</option> </select> </div> <div class="filter-group"> <label class="filter-label" for="filter-pipeline">Pipeline</label> <select class="filter-select" id="filter-pipeline"> <option value="">All</option> </select> </div> <button class="dash-btn dash-btn--ghost filter-reset" id="filter-reset" type="button">Reset</button> </div> <!-- Convoy Status Section --> <section class="chart-card convoy-status" id="convoy-section" data-nav-section style="display:none"> <div class="chart-card__header"> <h2 class="chart-card__title">Convoy Status</h2> <p class="chart-card__desc" id="convoy-desc">Select a convoy to view details</p> </div> <div class="chart-card__body" id="convoy-body"></div> </section> <!-- Convoy Pipeline (Chaining) Section --> <section class="chart-card" id="convoy-pipeline-section" data-nav-section style="display:none"> <div class="chart-card__header"> <h2 class="chart-card__title">Convoy Pipeline</h2> <p class="chart-card__desc" id="convoy-pipeline-desc">Pipeline convoy chain progress</p> </div> <div class="chart-card__body" id="convoy-pipeline-body"></div> </section> <!-- KPI Row --> <section class="kpi-row" id="kpi-row" data-nav-section> <div class="kpi-card" id="kpi-sessions"> <span class="kpi-card__label">Total Sessions</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-success"> <span class="kpi-card__label">Success Rate</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-delegations"> <span class="kpi-card__label">Total Delegations</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-duration"> <span class="kpi-card__label">Avg Duration</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-retries"> <span class="kpi-card__label">Total Retries</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-lessons"> <span class="kpi-card__label">Lessons Added</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> </section> <!-- Pipeline View (Steroids-inspired) --> <section class="chart-card" id="pipeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Task Pipeline</h2> <p class="chart-card__desc">Delegation flow across execution phases</p> </div> <div class="chart-card__body" id="pipeline-view"> <div class="loading-skeleton"></div> </div> </section> <!-- Charts Row 1 --> <div class="charts-row" id="agent-section" data-nav-section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Sessions by Agent</h2> <p class="chart-card__desc">Stacked by outcome</p> </div> <div class="chart-card__body" id="agent-chart"> <div class="loading-skeleton"></div> </div> </section> <section class="chart-card" id="tier-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Tier Distribution</h2> <p class="chart-card__desc">Delegation model tiers</p> </div> <div class="chart-card__body" id="tier-chart"> <div class="loading-skeleton"></div> </div> </section> </div> <!-- Charts Row: Delegation Insights --> <div class="charts-row" id="delegation-section" data-nav-section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Delegation Mechanism</h2> <p class="chart-card__desc">Sub-agent vs background split</p> </div> <div class="chart-card__body" id="mechanism-chart"> <div class="loading-skeleton"></div> </div> </section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Delegation Outcomes</h2> <p class="chart-card__desc">Success rate by delegation</p> </div> <div class="chart-card__body" id="delegation-outcome-chart"> <div class="loading-skeleton"></div> </div> </section> </div> <!-- Charts Row 2 --> <div class="charts-row" id="timeline-section" data-nav-section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Timeline</h2> <p class="chart-card__desc">Sessions and delegations over time</p> </div> <div class="chart-card__body" id="timeline-chart"> <div class="loading-skeleton"></div> </div> </section> <section class="chart-card" id="model-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Model Usage</h2> <p class="chart-card__desc">Sessions by model</p> </div> <div class="chart-card__body" id="model-chart"> <div class="loading-skeleton"></div> </div> </section> </div> <!-- Execution Log (Duvo-inspired) --> <section class="chart-card" id="execution-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Execution Log</h2> <p class="chart-card__desc">Recent agent activity, step by step</p> </div> <div class="chart-card__body" id="execution-log"> <div class="loading-skeleton"></div> </div> </section> <!-- Panel Results --> <section class="chart-card" id="panel-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Panel Reviews</h2> <p class="chart-card__desc">Quality gate verdicts and fix items</p> </div> <div class="chart-card__body" id="panel-chart"> <div class="loading-skeleton"></div> </div> </section> <!-- Fast Reviews --> <section class="chart-card" id="reviews-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Fast Reviews</h2> <p class="chart-card__desc">Single-reviewer quality gate results</p> </div> <div class="chart-card__body chart-card__body--table" id="reviews-table"> <div class="loading-skeleton"></div> </div> </section> <!-- Sessions Table --> <section class="chart-card" id="sessions-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Recent Sessions</h2> <p class="chart-card__desc">Last 15 sessions by timestamp</p> </div> <div class="chart-card__body chart-card__body--table" id="sessions-table"> <div class="loading-skeleton"></div> </div> </section> </main> </div> </body></html> <script>(function(){const base = "/";
4
4
 
@@ -72,6 +72,20 @@ Export
72
72
  return String(n);
73
73
  }
74
74
 
75
+ function formatDuration(startIso, endIso) {
76
+ if (!startIso || !endIso) return null;
77
+ const ms = new Date(endIso).getTime() - new Date(startIso).getTime();
78
+ if (ms < 0) return null;
79
+ const sec = Math.floor(ms / 1000);
80
+ if (sec < 60) return sec + 's';
81
+ const min = Math.floor(sec / 60);
82
+ const remSec = sec % 60;
83
+ if (min < 60) return min + 'm ' + remSec + 's';
84
+ const hr = Math.floor(min / 60);
85
+ const remMin = min % 60;
86
+ return hr + 'h ' + remMin + 'm';
87
+ }
88
+
75
89
  // ── SVG Icon Library (empty states) ────────────────────
76
90
 
77
91
  const EMPTY_ICONS = {
@@ -1167,6 +1181,20 @@ Export
1167
1181
  if (convoy.total_tokens != null) {
1168
1182
  html += '<div class="convoy-stat"><span class="convoy-stat__label">Tokens</span><span class="convoy-stat__value">' + formatTokens(convoy.total_tokens) + '</span></div>';
1169
1183
  }
1184
+ if (convoy.finished_at) {
1185
+ html += '<div class="convoy-stat"><span class="convoy-stat__label">Finished</span><span class="convoy-stat__value">' + formatTime(convoy.finished_at) + '</span></div>';
1186
+ }
1187
+ const convoyDur = formatDuration(convoy.started_at, convoy.finished_at);
1188
+ if (convoyDur) {
1189
+ html += '<div class="convoy-stat"><span class="convoy-stat__label">Duration</span><span class="convoy-stat__value">' + convoyDur + '</span></div>';
1190
+ }
1191
+ if (convoy.total_cost_usd != null && convoy.total_cost_usd > 0) {
1192
+ html += '<div class="convoy-stat"><span class="convoy-stat__label">Cost</span><span class="convoy-stat__value">$' + convoy.total_cost_usd.toFixed(2) + '</span></div>';
1193
+ }
1194
+ const failedCount = (s.failed || 0) + (s.timedOut || 0);
1195
+ if (failedCount > 0) {
1196
+ html += '<div class="convoy-stat"><span class="convoy-stat__label">Failed</span><span class="convoy-stat__value convoy-stat__value--error">' + failedCount + '</span></div>';
1197
+ }
1170
1198
  html += '</div>';
1171
1199
 
1172
1200
  html += '<div class="convoy-progress">';
@@ -1176,7 +1204,7 @@ Export
1176
1204
 
1177
1205
  if (convoy.tasks && convoy.tasks.length > 0) {
1178
1206
  html += '<table class="sessions-table convoy-tasks">';
1179
- html += '<thead><tr><th>Task</th><th>Phase</th><th>Agent</th><th>Adapter</th><th>Status</th><th>Retries</th><th>Tokens</th></tr></thead>';
1207
+ html += '<thead><tr><th>Task</th><th>Phase</th><th>Agent</th><th>Adapter</th><th>Status</th><th>Retries</th><th>Tokens</th><th>Duration</th></tr></thead>';
1180
1208
  html += '<tbody>';
1181
1209
  convoy.tasks.forEach(function(t) {
1182
1210
  const tStatus = t.status === 'done' ? 'success'
@@ -1190,6 +1218,7 @@ Export
1190
1218
  html += '<td><span class="outcome-badge outcome-badge--' + tStatus + '">' + escapeHtml(t.status) + '</span></td>';
1191
1219
  html += '<td class="td-num">' + (t.retries || 0) + '</td>';
1192
1220
  html += '<td class="td-num">' + (t.total_tokens != null ? formatTokens(t.total_tokens) : '\u2014') + '</td>';
1221
+ html += '<td class="td-num">' + (formatDuration(t.started_at, t.finished_at) || '\u2014') + '</td>';
1193
1222
  html += '</tr>';
1194
1223
  });
1195
1224
  html += '</tbody></table>';
@@ -1300,6 +1329,17 @@ Export
1300
1329
  '<div class="convoy-stat"><span class="convoy-stat__label">Finished</span>' +
1301
1330
  '<span class="convoy-stat__value">' + formatTime(pipeline.finished_at) + '</span></div>';
1302
1331
  }
1332
+ const pipelineDur = formatDuration(pipeline.started_at, pipeline.finished_at);
1333
+ if (pipelineDur) {
1334
+ html +=
1335
+ '<div class="convoy-stat"><span class="convoy-stat__label">Duration</span>' +
1336
+ '<span class="convoy-stat__value">' + pipelineDur + '</span></div>';
1337
+ }
1338
+ if (pipeline.total_cost_usd != null && pipeline.total_cost_usd > 0) {
1339
+ html +=
1340
+ '<div class="convoy-stat"><span class="convoy-stat__label">Cost</span>' +
1341
+ '<span class="convoy-stat__value">$' + pipeline.total_cost_usd.toFixed(2) + '</span></div>';
1342
+ }
1303
1343
  html += '</div>';
1304
1344
 
1305
1345
  // Convoy chain visualization
@@ -1499,8 +1539,25 @@ Export
1499
1539
  links.forEach((link) => {
1500
1540
  link.addEventListener('click', (e) => {
1501
1541
  e.preventDefault();
1502
- const target = document.getElementById(link.dataset.section);
1503
- if (target) {
1542
+ const sectionId = link.dataset.section;
1543
+
1544
+ // Auto-select latest convoy/pipeline if section is hidden
1545
+ if (sectionId === 'convoy-section') {
1546
+ const convoySelect = document.getElementById('filter-convoy');
1547
+ if (convoySelect && !convoySelect.value && convoySelect.options.length > 1) {
1548
+ convoySelect.value = convoySelect.options[1].value;
1549
+ applyFilters();
1550
+ }
1551
+ } else if (sectionId === 'convoy-pipeline-section') {
1552
+ const pipelineSelect = document.getElementById('filter-pipeline');
1553
+ if (pipelineSelect && !pipelineSelect.value && pipelineSelect.options.length > 1) {
1554
+ pipelineSelect.value = pipelineSelect.options[1].value;
1555
+ applyFilters();
1556
+ }
1557
+ }
1558
+
1559
+ const target = document.getElementById(sectionId);
1560
+ if (target && target.style.display !== 'none') {
1504
1561
  target.scrollIntoView({ behavior: 'smooth', block: 'start' });
1505
1562
  }
1506
1563
  });
@@ -1,25 +1,25 @@
1
1
  {
2
- "hash": "3ab8c4bc",
2
+ "hash": "0f52f4b3",
3
3
  "configHash": "30f8ea04",
4
- "lockfileHash": "b6a43efa",
5
- "browserHash": "2fa998a7",
4
+ "lockfileHash": "cf370ff3",
5
+ "browserHash": "cd4e9a91",
6
6
  "optimized": {
7
7
  "astro > cssesc": {
8
8
  "src": "../../../../../node_modules/cssesc/cssesc.js",
9
9
  "file": "astro___cssesc.js",
10
- "fileHash": "8902e5d4",
10
+ "fileHash": "cfa5042b",
11
11
  "needsInterop": true
12
12
  },
13
13
  "astro > aria-query": {
14
14
  "src": "../../../../../node_modules/aria-query/lib/index.js",
15
15
  "file": "astro___aria-query.js",
16
- "fileHash": "168eda0e",
16
+ "fileHash": "80fa9785",
17
17
  "needsInterop": true
18
18
  },
19
19
  "astro > axobject-query": {
20
20
  "src": "../../../../../node_modules/axobject-query/lib/index.js",
21
21
  "file": "astro___axobject-query.js",
22
- "fileHash": "65349e55",
22
+ "fileHash": "73e4f752",
23
23
  "needsInterop": true
24
24
  }
25
25
  },
@@ -332,6 +332,20 @@ const base = import.meta.env.BASE_URL;
332
332
  return String(n);
333
333
  }
334
334
 
335
+ function formatDuration(startIso, endIso) {
336
+ if (!startIso || !endIso) return null;
337
+ const ms = new Date(endIso).getTime() - new Date(startIso).getTime();
338
+ if (ms < 0) return null;
339
+ const sec = Math.floor(ms / 1000);
340
+ if (sec < 60) return sec + 's';
341
+ const min = Math.floor(sec / 60);
342
+ const remSec = sec % 60;
343
+ if (min < 60) return min + 'm ' + remSec + 's';
344
+ const hr = Math.floor(min / 60);
345
+ const remMin = min % 60;
346
+ return hr + 'h ' + remMin + 'm';
347
+ }
348
+
335
349
  // ── SVG Icon Library (empty states) ────────────────────
336
350
 
337
351
  const EMPTY_ICONS = {
@@ -1427,6 +1441,20 @@ const base = import.meta.env.BASE_URL;
1427
1441
  if (convoy.total_tokens != null) {
1428
1442
  html += '<div class="convoy-stat"><span class="convoy-stat__label">Tokens</span><span class="convoy-stat__value">' + formatTokens(convoy.total_tokens) + '</span></div>';
1429
1443
  }
1444
+ if (convoy.finished_at) {
1445
+ html += '<div class="convoy-stat"><span class="convoy-stat__label">Finished</span><span class="convoy-stat__value">' + formatTime(convoy.finished_at) + '</span></div>';
1446
+ }
1447
+ const convoyDur = formatDuration(convoy.started_at, convoy.finished_at);
1448
+ if (convoyDur) {
1449
+ html += '<div class="convoy-stat"><span class="convoy-stat__label">Duration</span><span class="convoy-stat__value">' + convoyDur + '</span></div>';
1450
+ }
1451
+ if (convoy.total_cost_usd != null && convoy.total_cost_usd > 0) {
1452
+ html += '<div class="convoy-stat"><span class="convoy-stat__label">Cost</span><span class="convoy-stat__value">$' + convoy.total_cost_usd.toFixed(2) + '</span></div>';
1453
+ }
1454
+ const failedCount = (s.failed || 0) + (s.timedOut || 0);
1455
+ if (failedCount > 0) {
1456
+ html += '<div class="convoy-stat"><span class="convoy-stat__label">Failed</span><span class="convoy-stat__value convoy-stat__value--error">' + failedCount + '</span></div>';
1457
+ }
1430
1458
  html += '</div>';
1431
1459
 
1432
1460
  html += '<div class="convoy-progress">';
@@ -1436,7 +1464,7 @@ const base = import.meta.env.BASE_URL;
1436
1464
 
1437
1465
  if (convoy.tasks && convoy.tasks.length > 0) {
1438
1466
  html += '<table class="sessions-table convoy-tasks">';
1439
- html += '<thead><tr><th>Task</th><th>Phase</th><th>Agent</th><th>Adapter</th><th>Status</th><th>Retries</th><th>Tokens</th></tr></thead>';
1467
+ html += '<thead><tr><th>Task</th><th>Phase</th><th>Agent</th><th>Adapter</th><th>Status</th><th>Retries</th><th>Tokens</th><th>Duration</th></tr></thead>';
1440
1468
  html += '<tbody>';
1441
1469
  convoy.tasks.forEach(function(t) {
1442
1470
  const tStatus = t.status === 'done' ? 'success'
@@ -1450,6 +1478,7 @@ const base = import.meta.env.BASE_URL;
1450
1478
  html += '<td><span class="outcome-badge outcome-badge--' + tStatus + '">' + escapeHtml(t.status) + '</span></td>';
1451
1479
  html += '<td class="td-num">' + (t.retries || 0) + '</td>';
1452
1480
  html += '<td class="td-num">' + (t.total_tokens != null ? formatTokens(t.total_tokens) : '\u2014') + '</td>';
1481
+ html += '<td class="td-num">' + (formatDuration(t.started_at, t.finished_at) || '\u2014') + '</td>';
1453
1482
  html += '</tr>';
1454
1483
  });
1455
1484
  html += '</tbody></table>';
@@ -1560,6 +1589,17 @@ const base = import.meta.env.BASE_URL;
1560
1589
  '<div class="convoy-stat"><span class="convoy-stat__label">Finished</span>' +
1561
1590
  '<span class="convoy-stat__value">' + formatTime(pipeline.finished_at) + '</span></div>';
1562
1591
  }
1592
+ const pipelineDur = formatDuration(pipeline.started_at, pipeline.finished_at);
1593
+ if (pipelineDur) {
1594
+ html +=
1595
+ '<div class="convoy-stat"><span class="convoy-stat__label">Duration</span>' +
1596
+ '<span class="convoy-stat__value">' + pipelineDur + '</span></div>';
1597
+ }
1598
+ if (pipeline.total_cost_usd != null && pipeline.total_cost_usd > 0) {
1599
+ html +=
1600
+ '<div class="convoy-stat"><span class="convoy-stat__label">Cost</span>' +
1601
+ '<span class="convoy-stat__value">$' + pipeline.total_cost_usd.toFixed(2) + '</span></div>';
1602
+ }
1563
1603
  html += '</div>';
1564
1604
 
1565
1605
  // Convoy chain visualization
@@ -1759,8 +1799,25 @@ const base = import.meta.env.BASE_URL;
1759
1799
  links.forEach((link) => {
1760
1800
  link.addEventListener('click', (e) => {
1761
1801
  e.preventDefault();
1762
- const target = document.getElementById(link.dataset.section);
1763
- if (target) {
1802
+ const sectionId = link.dataset.section;
1803
+
1804
+ // Auto-select latest convoy/pipeline if section is hidden
1805
+ if (sectionId === 'convoy-section') {
1806
+ const convoySelect = document.getElementById('filter-convoy');
1807
+ if (convoySelect && !convoySelect.value && convoySelect.options.length > 1) {
1808
+ convoySelect.value = convoySelect.options[1].value;
1809
+ applyFilters();
1810
+ }
1811
+ } else if (sectionId === 'convoy-pipeline-section') {
1812
+ const pipelineSelect = document.getElementById('filter-pipeline');
1813
+ if (pipelineSelect && !pipelineSelect.value && pipelineSelect.options.length > 1) {
1814
+ pipelineSelect.value = pipelineSelect.options[1].value;
1815
+ applyFilters();
1816
+ }
1817
+ }
1818
+
1819
+ const target = document.getElementById(sectionId);
1820
+ if (target && target.style.display !== 'none') {
1764
1821
  target.scrollIntoView({ behavior: 'smooth', block: 'start' });
1765
1822
  }
1766
1823
  });
@@ -1560,6 +1560,10 @@ body {
1560
1560
  color: var(--text-primary);
1561
1561
  }
1562
1562
 
1563
+ .convoy-stat__value--error {
1564
+ color: var(--color-failed);
1565
+ }
1566
+
1563
1567
  /* Progress bar */
1564
1568
  .convoy-progress {
1565
1569
  display: flex;
@@ -152,7 +152,7 @@ The convoy engine is the **mandatory** execution mechanism for all project-relat
152
152
  | Work type | Approach |
153
153
  |-----------|----------|
154
154
  | Features, bug fixes, refactors (any subtask count) | **Convoy execution** — always generate a `.convoy.yml` spec, even for 1-task fixes |
155
- | Utility prompts (`bootstrap-customizations`, `create-skill`, `generate-convoy`, `brainstorm`, `quick-refinement`) | **Direct** — these are meta/tooling operations, not project code changes |
155
+ | Utility prompts (`create-skill`, `generate-convoy`, `brainstorm`, `quick-refinement`) | **Direct** — these are meta/tooling operations, not project code changes |
156
156
 
157
157
  ### How to generate a convoy spec
158
158
 
@@ -247,8 +247,10 @@ For each task:
247
247
  - High-stakes: panel review (load panel-majority-vote skill)
248
248
  - Discovered issues tracked (not silently ignored)
249
249
  - Lessons captured (if agent retried anything)
250
+ - Agent expertise updated (AGENT-EXPERTISE.md)
251
+ - Knowledge graph appended (KNOWLEDGE-GRAPH.md)
250
252
  6. PASS → log review (⛔ hard gate — do NOT proceed until logged), move issue → Done
251
- FAIL → re-delegate with failure details (max 3 attempts)
253
+ FAIL → re-delegate with failure details (max 3 attempts → log DLQ in AGENT-FAILURES.md)
252
254
  ```
253
255
 
254
256
  Fast review auto-PASS: research-only tasks, docs-only, or ≤10 lines across ≤2 files with all deterministic gates passing.
@@ -32,7 +32,7 @@ Skills reference these files with relative links like `../../.opencastle/stack/a
32
32
 
33
33
  | File | Purpose |
34
34
  |------|---------|
35
- | _(created by `bootstrap-customizations` prompt based on detected technologies)_ | |
35
+ | _(created by `opencastle init` based on detected technologies)_ | |
36
36
 
37
37
  ### `project/` — Project management config
38
38
 
@@ -41,7 +41,7 @@ Skills reference these files with relative links like `../../.opencastle/stack/a
41
41
  | `docs-structure.md` | Project documentation directory tree and practices |
42
42
  | `roadmap.md` | Project roadmap with planned features and their status |
43
43
  | `decisions.md` | Architecture Decision Records (ADRs) for the project |
44
- | _(task tracker config created by `bootstrap-customizations` prompt)_ | |
44
+ | _(task tracker config created by `opencastle init`)_ | |
45
45
 
46
46
  ### `logs/` — Append-only NDJSON session logs
47
47
 
@@ -58,4 +58,4 @@ Update these files when the project changes — new tables, new API routes, new
58
58
 
59
59
  ## Bootstrap
60
60
 
61
- Run the `bootstrap-customizations` prompt to auto-discover the project's structure and populate these files. It will scan for frameworks, databases, CMS, deployment config, and task tracking, then generate the appropriate `stack/` and `project/` files.
61
+ Run `npx opencastle init` to auto-discover the project's structure and populate these files. It scans for frameworks, databases, CMS, deployment config, and task tracking, then generates the appropriate `stack/` and `project/` files.
@@ -3,7 +3,7 @@
3
3
 
4
4
  Project-specific agent-to-model assignments and scope examples referenced by the `team-lead-reference` skill.
5
5
 
6
- <!-- Populated by the `bootstrap-customizations` prompt based on project structure. -->
6
+ <!-- Populated by `opencastle init` based on project structure. -->
7
7
 
8
8
  ## Specialist Agent Registry
9
9
 
@@ -1,6 +1,6 @@
1
1
  # Documentation Structure
2
2
 
3
- <!-- Populated by the `bootstrap-customizations` prompt based on project structure. -->
3
+ <!-- Populated by `opencastle init` based on project structure. -->
4
4
 
5
5
  Project-specific documentation layout referenced by the `documentation-standards` skill.
6
6
 
@@ -1,4 +1,4 @@
1
- <!-- Populated by the bootstrap-customizations prompt or manually. -->
1
+ <!-- Populated by `opencastle init` or manually. -->
2
2
 
3
3
  # Project Roadmap
4
4
 
@@ -1,6 +1,6 @@
1
1
  # Task Tracker Configuration
2
2
 
3
- <!-- Populated by the `bootstrap-customizations` prompt.
3
+ <!-- Populated by `opencastle init`.
4
4
  Rename this file to match your tracker: linear-config.md, jira-config.md, etc. -->
5
5
 
6
6
  Project-specific task tracker details referenced by task management workflows.
@@ -1,8 +1,8 @@
1
1
  ```instructions
2
2
  # Project Context
3
3
 
4
- <!-- This file is populated by the `bootstrap-customizations` prompt.
5
- Run that prompt to auto-discover your project's structure, tech stack, and configuration. -->
4
+ <!-- This file is populated by `opencastle init`.
5
+ Re-run `npx opencastle init` to refresh the auto-detected configuration. -->
6
6
 
7
7
  ## Overview
8
8
 
@@ -1,6 +1,6 @@
1
1
  # API Configuration
2
2
 
3
- <!-- Populated by the `bootstrap-customizations` prompt based on detected API routes and Server Actions. -->
3
+ <!-- Populated by `opencastle init` based on detected API routes and Server Actions. -->
4
4
 
5
5
  Project-specific API inventory referenced by the `api-patterns` skill.
6
6
 
@@ -1,7 +1,7 @@
1
1
  # CMS Configuration
2
2
 
3
- <!-- Populated by the `bootstrap-customizations` prompt based on detected CMS.
4
- Rename this file to match your CMS: sanity-config.md, contentful-config.md, strapi-config.md, etc. -->
3
+ <!-- Populated by `opencastle init` based on detected CMS.
4
+ Renamed to match your CMS: sanity-config.md, contentful-config.md, strapi-config.md, etc. -->
5
5
 
6
6
  Project-specific CMS details referenced by the corresponding CMS skill (e.g., `sanity-cms`).
7
7
 
@@ -1,6 +1,6 @@
1
1
  # Data Pipeline Configuration
2
2
 
3
- <!-- Populated by the `bootstrap-customizations` prompt based on detected ETL/scraping infrastructure. -->
3
+ <!-- Populated by `opencastle init` based on detected ETL/scraping infrastructure. -->
4
4
 
5
5
  Project-specific pipeline details referenced by the `data-engineering` skill.
6
6
 
@@ -1,7 +1,7 @@
1
1
  # Database Configuration
2
2
 
3
- <!-- Populated by the `bootstrap-customizations` prompt based on detected database.
4
- Rename this file to match your provider: supabase-config.md, prisma-config.md, drizzle-config.md, etc. -->
3
+ <!-- Populated by `opencastle init` based on detected database.
4
+ Renamed to match your provider: supabase-config.md, prisma-config.md, drizzle-config.md, etc. -->
5
5
 
6
6
  Project-specific database details referenced by the corresponding database skill (e.g., `supabase-database`).
7
7
 
@@ -1,6 +1,6 @@
1
1
  # Deployment Configuration
2
2
 
3
- <!-- Populated by the `bootstrap-customizations` prompt based on detected deployment config. -->
3
+ <!-- Populated by `opencastle init` based on detected deployment config. -->
4
4
 
5
5
  Project-specific deployment infrastructure referenced by the `deployment-infrastructure` skill.
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Notifications Configuration
2
2
 
3
- <!-- Populated by the `bootstrap-customizations` prompt based on `.opencastle.json` → `stack.teamTools`. -->
3
+ <!-- Populated by `opencastle init` based on `.opencastle.json` → `stack.teamTools`. -->
4
4
 
5
5
  Project-specific messaging configuration referenced by the `slack-notifications` skill (or Teams equivalent).
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Testing Configuration
2
2
 
3
- <!-- Populated by the `bootstrap-customizations` prompt based on detected test infrastructure. -->
3
+ <!-- Populated by `opencastle init` based on detected test infrastructure. -->
4
4
 
5
5
  Project-specific testing details referenced by the `browser-testing` skill.
6
6
 
@@ -139,6 +139,8 @@ These rules apply to ALL specialist agents automatically. **Do not duplicate the
139
139
  - [ ] **Delegations logged** — `events.ndjson` has a `delegation` record for each delegation (Team Lead only)
140
140
  - [ ] **Reviews logged** — `events.ndjson` has a `review` record for each fast review (if any)
141
141
  - [ ] **Panels logged** — `events.ndjson` has a `panel` record for each panel review (if any)
142
+ - [ ] **Agent expertise updated** — `AGENT-EXPERTISE.md` updated for each delegation (strong/weak areas + file familiarity) (Team Lead only)
143
+ - [ ] **Knowledge graph appended** — `KNOWLEDGE-GRAPH.md` has new rows for file relationships discovered (Team Lead only)
142
144
 
143
145
  Load the **observability-logging** skill for CLI commands, Base Output Contract, and detailed schemas.
144
146