opencastle 0.31.7 → 0.32.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 (220) hide show
  1. package/README.md +4 -1
  2. package/bin/cli.mjs +15 -0
  3. package/dist/cli/agents.d.ts.map +1 -1
  4. package/dist/cli/agents.js +19 -5
  5. package/dist/cli/agents.js.map +1 -1
  6. package/dist/cli/artifacts-cli.d.ts +3 -0
  7. package/dist/cli/artifacts-cli.d.ts.map +1 -0
  8. package/dist/cli/artifacts-cli.js +36 -0
  9. package/dist/cli/artifacts-cli.js.map +1 -0
  10. package/dist/cli/baselines.d.ts.map +1 -1
  11. package/dist/cli/baselines.js +11 -0
  12. package/dist/cli/baselines.js.map +1 -1
  13. package/dist/cli/convoy/artifacts.d.ts +25 -0
  14. package/dist/cli/convoy/artifacts.d.ts.map +1 -0
  15. package/dist/cli/convoy/artifacts.js +129 -0
  16. package/dist/cli/convoy/artifacts.js.map +1 -0
  17. package/dist/cli/convoy/artifacts.test.d.ts +2 -0
  18. package/dist/cli/convoy/artifacts.test.d.ts.map +1 -0
  19. package/dist/cli/convoy/artifacts.test.js +169 -0
  20. package/dist/cli/convoy/artifacts.test.js.map +1 -0
  21. package/dist/cli/convoy/compaction.d.ts +23 -0
  22. package/dist/cli/convoy/compaction.d.ts.map +1 -0
  23. package/dist/cli/convoy/compaction.js +117 -0
  24. package/dist/cli/convoy/compaction.js.map +1 -0
  25. package/dist/cli/convoy/compaction.test.d.ts +2 -0
  26. package/dist/cli/convoy/compaction.test.d.ts.map +1 -0
  27. package/dist/cli/convoy/compaction.test.js +205 -0
  28. package/dist/cli/convoy/compaction.test.js.map +1 -0
  29. package/dist/cli/convoy/contracts.d.ts +22 -0
  30. package/dist/cli/convoy/contracts.d.ts.map +1 -0
  31. package/dist/cli/convoy/contracts.js +254 -0
  32. package/dist/cli/convoy/contracts.js.map +1 -0
  33. package/dist/cli/convoy/contracts.test.d.ts +2 -0
  34. package/dist/cli/convoy/contracts.test.d.ts.map +1 -0
  35. package/dist/cli/convoy/contracts.test.js +239 -0
  36. package/dist/cli/convoy/contracts.test.js.map +1 -0
  37. package/dist/cli/convoy/dag-analysis.d.ts +40 -0
  38. package/dist/cli/convoy/dag-analysis.d.ts.map +1 -0
  39. package/dist/cli/convoy/dag-analysis.js +282 -0
  40. package/dist/cli/convoy/dag-analysis.js.map +1 -0
  41. package/dist/cli/convoy/dag-analysis.test.d.ts +2 -0
  42. package/dist/cli/convoy/dag-analysis.test.d.ts.map +1 -0
  43. package/dist/cli/convoy/dag-analysis.test.js +289 -0
  44. package/dist/cli/convoy/dag-analysis.test.js.map +1 -0
  45. package/dist/cli/convoy/effort-scaling.d.ts +20 -0
  46. package/dist/cli/convoy/effort-scaling.d.ts.map +1 -0
  47. package/dist/cli/convoy/effort-scaling.js +82 -0
  48. package/dist/cli/convoy/effort-scaling.js.map +1 -0
  49. package/dist/cli/convoy/effort-scaling.test.d.ts +2 -0
  50. package/dist/cli/convoy/effort-scaling.test.d.ts.map +1 -0
  51. package/dist/cli/convoy/effort-scaling.test.js +120 -0
  52. package/dist/cli/convoy/effort-scaling.test.js.map +1 -0
  53. package/dist/cli/convoy/engine.d.ts.map +1 -1
  54. package/dist/cli/convoy/engine.js +280 -6
  55. package/dist/cli/convoy/engine.js.map +1 -1
  56. package/dist/cli/convoy/engine.test.js +155 -18
  57. package/dist/cli/convoy/engine.test.js.map +1 -1
  58. package/dist/cli/convoy/event-schemas.d.ts.map +1 -1
  59. package/dist/cli/convoy/event-schemas.js +55 -0
  60. package/dist/cli/convoy/event-schemas.js.map +1 -1
  61. package/dist/cli/convoy/isolation.d.ts +27 -0
  62. package/dist/cli/convoy/isolation.d.ts.map +1 -0
  63. package/dist/cli/convoy/isolation.js +120 -0
  64. package/dist/cli/convoy/isolation.js.map +1 -0
  65. package/dist/cli/convoy/isolation.test.d.ts +2 -0
  66. package/dist/cli/convoy/isolation.test.d.ts.map +1 -0
  67. package/dist/cli/convoy/isolation.test.js +105 -0
  68. package/dist/cli/convoy/isolation.test.js.map +1 -0
  69. package/dist/cli/convoy/review-stages.d.ts +9 -0
  70. package/dist/cli/convoy/review-stages.d.ts.map +1 -0
  71. package/dist/cli/convoy/review-stages.js +134 -0
  72. package/dist/cli/convoy/review-stages.js.map +1 -0
  73. package/dist/cli/convoy/review-stages.test.d.ts +2 -0
  74. package/dist/cli/convoy/review-stages.test.d.ts.map +1 -0
  75. package/dist/cli/convoy/review-stages.test.js +197 -0
  76. package/dist/cli/convoy/review-stages.test.js.map +1 -0
  77. package/dist/cli/convoy/skill-refinement.d.ts +39 -0
  78. package/dist/cli/convoy/skill-refinement.d.ts.map +1 -0
  79. package/dist/cli/convoy/skill-refinement.js +239 -0
  80. package/dist/cli/convoy/skill-refinement.js.map +1 -0
  81. package/dist/cli/convoy/skill-refinement.test.d.ts +2 -0
  82. package/dist/cli/convoy/skill-refinement.test.d.ts.map +1 -0
  83. package/dist/cli/convoy/skill-refinement.test.js +230 -0
  84. package/dist/cli/convoy/skill-refinement.test.js.map +1 -0
  85. package/dist/cli/convoy/spec-builder.d.ts +1 -0
  86. package/dist/cli/convoy/spec-builder.d.ts.map +1 -1
  87. package/dist/cli/convoy/spec-builder.js +11 -0
  88. package/dist/cli/convoy/spec-builder.js.map +1 -1
  89. package/dist/cli/convoy/spec-builder.test.js +54 -0
  90. package/dist/cli/convoy/spec-builder.test.js.map +1 -1
  91. package/dist/cli/convoy/store.d.ts +3 -2
  92. package/dist/cli/convoy/store.d.ts.map +1 -1
  93. package/dist/cli/convoy/store.js +20 -2
  94. package/dist/cli/convoy/store.js.map +1 -1
  95. package/dist/cli/convoy/store.test.js +15 -15
  96. package/dist/cli/convoy/store.test.js.map +1 -1
  97. package/dist/cli/convoy/tdd-gate.d.ts +15 -0
  98. package/dist/cli/convoy/tdd-gate.d.ts.map +1 -0
  99. package/dist/cli/convoy/tdd-gate.js +119 -0
  100. package/dist/cli/convoy/tdd-gate.js.map +1 -0
  101. package/dist/cli/convoy/tdd-gate.test.d.ts +2 -0
  102. package/dist/cli/convoy/tdd-gate.test.d.ts.map +1 -0
  103. package/dist/cli/convoy/tdd-gate.test.js +227 -0
  104. package/dist/cli/convoy/tdd-gate.test.js.map +1 -0
  105. package/dist/cli/convoy/types.d.ts +91 -0
  106. package/dist/cli/convoy/types.d.ts.map +1 -1
  107. package/dist/cli/convoy/types.js +8 -0
  108. package/dist/cli/convoy/types.js.map +1 -1
  109. package/dist/cli/insights.d.ts +3 -0
  110. package/dist/cli/insights.d.ts.map +1 -0
  111. package/dist/cli/insights.js +94 -0
  112. package/dist/cli/insights.js.map +1 -0
  113. package/dist/cli/lesson.d.ts.map +1 -1
  114. package/dist/cli/lesson.js +7 -0
  115. package/dist/cli/lesson.js.map +1 -1
  116. package/dist/cli/log.d.ts.map +1 -1
  117. package/dist/cli/log.js +7 -0
  118. package/dist/cli/log.js.map +1 -1
  119. package/dist/cli/package-config.d.ts +12 -0
  120. package/dist/cli/package-config.d.ts.map +1 -0
  121. package/dist/cli/package-config.js +37 -0
  122. package/dist/cli/package-config.js.map +1 -0
  123. package/dist/cli/package.d.ts +23 -0
  124. package/dist/cli/package.d.ts.map +1 -0
  125. package/dist/cli/package.js +285 -0
  126. package/dist/cli/package.js.map +1 -0
  127. package/dist/cli/package.test.d.ts +2 -0
  128. package/dist/cli/package.test.d.ts.map +1 -0
  129. package/dist/cli/package.test.js +236 -0
  130. package/dist/cli/package.test.js.map +1 -0
  131. package/dist/cli/pipeline.d.ts +6 -0
  132. package/dist/cli/pipeline.d.ts.map +1 -1
  133. package/dist/cli/pipeline.js +15 -2
  134. package/dist/cli/pipeline.js.map +1 -1
  135. package/dist/cli/run/schema.d.ts.map +1 -1
  136. package/dist/cli/run/schema.js +32 -0
  137. package/dist/cli/run/schema.js.map +1 -1
  138. package/dist/cli/run/schema.test.js +51 -0
  139. package/dist/cli/run/schema.test.js.map +1 -1
  140. package/dist/cli/skills.d.ts +3 -0
  141. package/dist/cli/skills.d.ts.map +1 -0
  142. package/dist/cli/skills.js +107 -0
  143. package/dist/cli/skills.js.map +1 -0
  144. package/dist/cli/types.d.ts +4 -1
  145. package/dist/cli/types.d.ts.map +1 -1
  146. package/dist/dashboard/scripts/etl.d.ts.map +1 -1
  147. package/dist/dashboard/scripts/etl.js +44 -11
  148. package/dist/dashboard/scripts/etl.js.map +1 -1
  149. package/package.json +2 -1
  150. package/src/cli/agents.ts +20 -5
  151. package/src/cli/artifacts-cli.ts +41 -0
  152. package/src/cli/baselines.ts +12 -0
  153. package/src/cli/convoy/artifacts.test.ts +201 -0
  154. package/src/cli/convoy/artifacts.ts +186 -0
  155. package/src/cli/convoy/compaction.test.ts +245 -0
  156. package/src/cli/convoy/compaction.ts +164 -0
  157. package/src/cli/convoy/contracts.test.ts +279 -0
  158. package/src/cli/convoy/contracts.ts +280 -0
  159. package/src/cli/convoy/dag-analysis.test.ts +349 -0
  160. package/src/cli/convoy/dag-analysis.ts +371 -0
  161. package/src/cli/convoy/effort-scaling.test.ts +140 -0
  162. package/src/cli/convoy/effort-scaling.ts +90 -0
  163. package/src/cli/convoy/engine.test.ts +175 -18
  164. package/src/cli/convoy/engine.ts +301 -7
  165. package/src/cli/convoy/event-schemas.ts +55 -0
  166. package/src/cli/convoy/isolation.test.ts +137 -0
  167. package/src/cli/convoy/isolation.ts +165 -0
  168. package/src/cli/convoy/review-stages.test.ts +235 -0
  169. package/src/cli/convoy/review-stages.ts +166 -0
  170. package/src/cli/convoy/skill-refinement.test.ts +277 -0
  171. package/src/cli/convoy/skill-refinement.ts +306 -0
  172. package/src/cli/convoy/spec-builder.test.ts +61 -0
  173. package/src/cli/convoy/spec-builder.ts +9 -0
  174. package/src/cli/convoy/store.test.ts +15 -15
  175. package/src/cli/convoy/store.ts +26 -4
  176. package/src/cli/convoy/tdd-gate.test.ts +281 -0
  177. package/src/cli/convoy/tdd-gate.ts +154 -0
  178. package/src/cli/convoy/types.ts +51 -0
  179. package/src/cli/insights.ts +99 -0
  180. package/src/cli/lesson.ts +8 -0
  181. package/src/cli/log.ts +8 -0
  182. package/src/cli/package-config.ts +48 -0
  183. package/src/cli/package.test.ts +276 -0
  184. package/src/cli/package.ts +329 -0
  185. package/src/cli/pipeline.ts +21 -2
  186. package/src/cli/run/schema.test.ts +58 -0
  187. package/src/cli/run/schema.ts +33 -0
  188. package/src/cli/skills.ts +121 -0
  189. package/src/cli/types.ts +4 -1
  190. package/src/dashboard/dist/_astro/index.D6quLrA6.css +1 -0
  191. package/src/dashboard/dist/data/convoy-list.json +21 -7
  192. package/src/dashboard/dist/data/convoys/demo-api-v2.json +3 -3
  193. package/src/dashboard/dist/data/convoys/demo-auth-revamp.json +5 -5
  194. package/src/dashboard/dist/data/convoys/demo-convoy-1.json +2 -2
  195. package/src/dashboard/dist/data/convoys/demo-convoy-2.json +1 -1
  196. package/src/dashboard/dist/data/convoys/demo-dashboard-ui.json +7 -7
  197. package/src/dashboard/dist/data/convoys/demo-data-pipeline.json +3 -3
  198. package/src/dashboard/dist/data/convoys/demo-deploy-ci.json +2 -2
  199. package/src/dashboard/dist/data/convoys/demo-docs-update.json +2 -2
  200. package/src/dashboard/dist/data/convoys/demo-perf-opt.json +4 -4
  201. package/src/dashboard/dist/index.html +306 -33
  202. package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
  203. package/src/dashboard/public/data/convoy-list.json +21 -7
  204. package/src/dashboard/public/data/convoys/demo-api-v2.json +3 -3
  205. package/src/dashboard/public/data/convoys/demo-auth-revamp.json +5 -5
  206. package/src/dashboard/public/data/convoys/demo-convoy-1.json +2 -2
  207. package/src/dashboard/public/data/convoys/demo-convoy-2.json +1 -1
  208. package/src/dashboard/public/data/convoys/demo-dashboard-ui.json +7 -7
  209. package/src/dashboard/public/data/convoys/demo-data-pipeline.json +3 -3
  210. package/src/dashboard/public/data/convoys/demo-deploy-ci.json +2 -2
  211. package/src/dashboard/public/data/convoys/demo-docs-update.json +2 -2
  212. package/src/dashboard/public/data/convoys/demo-perf-opt.json +4 -4
  213. package/src/dashboard/scripts/etl.test.ts +14 -0
  214. package/src/dashboard/scripts/etl.ts +48 -16
  215. package/src/dashboard/scripts/generate-demo-db.ts +18 -10
  216. package/src/dashboard/src/pages/index.astro +348 -45
  217. package/src/dashboard/src/styles/dashboard.css +56 -0
  218. package/src/orchestrator/prompts/assess-complexity.prompt.md +13 -0
  219. package/src/orchestrator/prompts/generate-convoy.prompt.md +19 -0
  220. package/src/dashboard/dist/_astro/index.BRDFmNzR.css +0 -1
@@ -1,10 +1,10 @@
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.BRDFmNzR.css"></head> <body> <script>(function(){const overallStats = {"convoyCounts":{"total":7,"running":1,"done":5,"failed":0,"gate_failed":0},"durationStats":{"avg_sec":2950.0000052154064,"p95_sec":3720.000019669533,"max_sec":5879.9999713897705},"tokenCostTotals":{"total_tokens":226750,"total_cost_usd":22.67},"topAgents":[{"agent":"Developer","task_count":6,"total_tokens":70100},{"agent":"Reviewer","task_count":5,"total_tokens":16250},{"agent":"UI/UX Expert","task_count":3,"total_tokens":32300},{"agent":"Testing Expert","task_count":3,"total_tokens":28800},{"agent":"DevOps Expert","task_count":3,"total_tokens":6400}],"topModels":[{"model":"claude-sonnet-4-6","task_count":21,"total_tokens":177650},{"model":"claude-haiku-3-5","task_count":5,"total_tokens":17700},{"model":"claude-opus-4-6","task_count":4,"total_tokens":40700}],"dlqSummary":{"count":3,"top_failure_types":[{"type":"timeout","count":1},{"type":"review_blocked","count":1},{"type":"gate_failed","count":1}]}};
2
- const convoyList = [{"id":"demo-deploy-ci","name":"CI/CD Pipeline Setup","status":"running","created_at":"2026-03-11T08:00:00.000Z","finished_at":null,"total_tokens":null,"total_cost_usd":null},{"id":"demo-docs-update","name":"Documentation Refresh","status":"done","created_at":"2026-02-28T15:00:00.000Z","finished_at":"2026-02-28T15:22:00.000Z","total_tokens":14800,"total_cost_usd":1.48},{"id":"demo-data-pipeline","name":"Analytics ETL Pipeline","status":"done","created_at":"2026-02-22T13:00:00.000Z","finished_at":"2026-02-22T13:38:00.000Z","total_tokens":28900,"total_cost_usd":2.89},{"id":"demo-perf-opt","name":"Frontend Performance Boost","status":"done","created_at":"2026-02-17T10:00:00.000Z","finished_at":"2026-02-17T11:02:00.000Z","total_tokens":37200,"total_cost_usd":3.72},{"id":"demo-api-v2","name":"REST API v2 Migration","status":"gate_failed","created_at":"2026-02-12T16:00:00.000Z","finished_at":"2026-02-12T16:28:00.000Z","total_tokens":24600,"total_cost_usd":2.46},{"id":"demo-dashboard-ui","name":"Observability Dashboard UI","status":"done","created_at":"2026-02-07T14:00:00.000Z","finished_at":"2026-02-07T15:38:00.000Z","total_tokens":78400,"total_cost_usd":7.84},{"id":"demo-auth-revamp","name":"Auth System Revamp","status":"done","created_at":"2026-02-03T09:00:00.000Z","finished_at":"2026-02-03T09:47:00.000Z","total_tokens":42850,"total_cost_usd":4.28}];
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.D6quLrA6.css"></head> <body> <script>(function(){const overallStats = {"convoyCounts":{"total":7,"running":1,"done":5,"failed":0,"gate_failed":0},"durationStats":{"avg_sec":2950.0000052154064,"p95_sec":3720.000019669533,"max_sec":5879.9999713897705},"tokenCostTotals":{"total_tokens":226750,"total_cost_usd":22.67},"topAgents":[{"agent":"Developer","task_count":6,"total_tokens":70100},{"agent":"Reviewer","task_count":5,"total_tokens":16250},{"agent":"UI/UX Expert","task_count":3,"total_tokens":32300},{"agent":"Testing Expert","task_count":3,"total_tokens":28800},{"agent":"DevOps Expert","task_count":3,"total_tokens":6400}],"topModels":[{"model":"claude-sonnet-4-6","task_count":21,"total_tokens":177650},{"model":"claude-haiku-3-5","task_count":5,"total_tokens":17700},{"model":"claude-opus-4-6","task_count":4,"total_tokens":40700}],"dlqSummary":{"count":3,"top_failure_types":[{"type":"timeout","count":1},{"type":"review_blocked","count":1},{"type":"gate_failed","count":1}]}};
2
+ const convoyList = [{"id":"demo-deploy-ci","name":"CI/CD Pipeline Setup","status":"running","created_at":"2026-03-11T08:00:00.000Z","finished_at":null,"total_tokens":null,"total_cost_usd":null,"task_count":3,"pipeline_id":null},{"id":"demo-docs-update","name":"Documentation Refresh","status":"done","created_at":"2026-02-28T15:00:00.000Z","finished_at":"2026-02-28T15:22:00.000Z","total_tokens":14800,"total_cost_usd":1.48,"task_count":3,"pipeline_id":null},{"id":"demo-data-pipeline","name":"Analytics ETL Pipeline","status":"done","created_at":"2026-02-22T13:00:00.000Z","finished_at":"2026-02-22T13:38:00.000Z","total_tokens":28900,"total_cost_usd":2.89,"task_count":4,"pipeline_id":null},{"id":"demo-perf-opt","name":"Frontend Performance Boost","status":"done","created_at":"2026-02-17T10:00:00.000Z","finished_at":"2026-02-17T11:02:00.000Z","total_tokens":37200,"total_cost_usd":3.72,"task_count":5,"pipeline_id":null},{"id":"demo-api-v2","name":"REST API v2 Migration","status":"gate_failed","created_at":"2026-02-12T16:00:00.000Z","finished_at":"2026-02-12T16:28:00.000Z","total_tokens":24600,"total_cost_usd":2.46,"task_count":3,"pipeline_id":null},{"id":"demo-dashboard-ui","name":"Observability Dashboard UI","status":"done","created_at":"2026-02-07T14:00:00.000Z","finished_at":"2026-02-07T15:38:00.000Z","total_tokens":78400,"total_cost_usd":7.84,"task_count":7,"pipeline_id":"demo-pipeline-1"},{"id":"demo-auth-revamp","name":"Auth System Revamp","status":"done","created_at":"2026-02-03T09:00:00.000Z","finished_at":"2026-02-03T09:47:00.000Z","total_tokens":42850,"total_cost_usd":4.28,"task_count":5,"pipeline_id":"demo-pipeline-1"}];
3
3
 
4
4
  window.__DASHBOARD_DATA__ = { overallStats, convoyList }
5
5
  })();</script> <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" aria-label="Export dashboard 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>
6
6
  Export
7
- </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 dash-sidebar__link--active" href="#overall-section" data-section="overall-section" data-view="home" aria-label="Overview section">Overview</a></li> <li><a class="dash-sidebar__link" href="#convoy-list-section" data-section="convoy-list-section" data-view="home" aria-label="Convoys section">Convoys</a></li> <li><a class="dash-sidebar__link" href="#tasks-section" data-section="tasks-section" data-view="detail" aria-label="Tasks section">Tasks</a></li> <li><a class="dash-sidebar__link" href="#pipeline-section" data-section="pipeline-section" data-view="detail" aria-label="Pipeline section">Pipeline</a></li> <li><a class="dash-sidebar__link" href="#agent-section" data-section="agent-section" data-view="detail" aria-label="Agents section">Agents</a></li> <li><a class="dash-sidebar__link" href="#tier-section" data-section="tier-section" data-view="detail" aria-label="Tiers section">Tiers</a></li> <li><a class="dash-sidebar__link" href="#model-section" data-section="model-section" data-view="detail" aria-label="Models section">Models</a></li> <li><a class="dash-sidebar__link" href="#quality-section" data-section="quality-section" data-view="detail" aria-label="Quality section">Quality</a></li> <li><a class="dash-sidebar__link" href="#reliability-section" data-section="reliability-section" data-view="detail" aria-label="Reliability section">Reliability</a></li> <li><a class="dash-sidebar__link" href="#drift-section" data-section="drift-section" data-view="detail" aria-label="Drift section">Drift</a></li> <li><a class="dash-sidebar__link" href="#outputs-section" data-section="outputs-section" data-view="detail" aria-label="Outputs section">Outputs</a></li> <li><a class="dash-sidebar__link" href="#execution-section" data-section="execution-section" data-view="detail" aria-label="Execution Log section">Execution Log</a></li> <li><a class="dash-sidebar__link" href="#event-timeline-section" data-section="event-timeline-section" data-view="detail" aria-label="Event Log section">Event Log</a></li> <li><a class="dash-sidebar__link" href="#panel-section" data-section="panel-section" data-view="detail" aria-label="Panel Reviews section">Panel Reviews</a></li> <li><a class="dash-sidebar__link" href="#reviews-section" data-section="reviews-section" data-view="detail" aria-label="Fast Reviews section">Fast Reviews</a></li> </ul> </nav> <main class="dash-main"> <nav class="breadcrumbs" id="breadcrumbs" data-view-hidden> <a class="breadcrumbs__link" href="#" id="breadcrumbs-home">Observability</a> <span class="breadcrumbs__separator">/</span> <span class="breadcrumbs__current" id="breadcrumbs-convoy"></span> </nav> <div class="view-home" id="view-home"> <!-- Overall Stats Section --> <section class="overall-stats" id="overall-section" data-nav-section> <div class="overall-stats__header"> <h2 class="overall-stats__title">Overall Stats</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: How all runs behave across your project." data-tooltip="How all runs behave across your project."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> </div> <div class="overall-stats__grid"> <div class="overall-kpi" id="overall-total-runs"> <span class="overall-kpi__label">Total Runs <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Number of convoy runs executed" data-tooltip="Number of convoy runs executed"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span></span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-running"> <span class="overall-kpi__label">Running Now</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-success-rate"> <span class="overall-kpi__label">Success Rate</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-avg-duration"> <span class="overall-kpi__label">Avg Duration</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-tokens"> <span class="overall-kpi__label">Total Tokens</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-cost"> <span class="overall-kpi__label">Total Cost</span> <span class="overall-kpi__value">&mdash;</span> </div> </div> </section> <!-- Convoy List Section --> <section class="convoy-list-section" id="convoy-list-section"> <div class="convoy-list-section__header"> <h2>Convoys</h2> <p class="convoy-list-section__desc">All convoy runs across your project</p> </div> <div class="convoy-list-filters" id="convoy-list-filters"> <div class="convoy-list-filters__group"> <label for="cl-filter-search">Search</label> <input class="convoy-list-filters__input" type="text" id="cl-filter-search" placeholder="Convoy name…"> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-status">Status</label> <select class="convoy-list-filters__select" id="cl-filter-status"> <option value="">All</option> <option value="done">Done</option> <option value="running">Running</option> <option value="failed">Failed</option> <option value="gate-failed">Gate Failed</option> <option value="pending">Pending</option> </select> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-from">From</label> <input class="convoy-list-filters__date" type="date" id="cl-filter-from"> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-to">To</label> <input class="convoy-list-filters__date" type="date" id="cl-filter-to"> </div> <button class="convoy-list-filters__reset" type="button" id="cl-filter-reset">Reset</button> </div> <div id="convoy-list-table-wrap"></div> <div class="convoy-list-empty" id="convoy-list-empty" style="display:none"> <div class="convoy-list-empty__icon"> <svg width="40" height="40" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="6" y="6" width="28" height="28" rx="4"></rect><line x1="14" y1="20" x2="26" y2="20" opacity="0.5"></line></svg> </div> <p class="convoy-list-empty__text">No convoys match your filters</p> </div> </section> </div><!-- .view-home --> <div class="view-convoy-detail" id="view-convoy-detail" data-view-hidden> <section class="convoy-detail-hero" id="convoy-detail-hero"> <div class="convoy-detail-hero__title" id="detail-hero-title"></div> <span class="convoy-detail-hero__status" id="detail-hero-status"></span> <div class="convoy-detail-hero__meta" id="detail-hero-meta"></div> </section> <!-- Tasks Section --> <section class="chart-card" id="tasks-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Tasks</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Individual units of work in this convoy." data-tooltip="Individual units of work in this convoy."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc" id="tasks-section-desc">Task breakdown for the selected convoy</p> </div> <div class="chart-card__body" id="tasks-section-body"> <div id="task-summary-cards" class="task-summary-cards"></div> <div id="phase-breakdown" class="phase-breakdown"></div> <div id="task-table-wrap" class="task-table-wrap"></div> </div> </section> <!-- Pipeline View --> <section class="chart-card" id="pipeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Task Pipeline</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Task flow across execution phases." data-tooltip="Task flow across execution phases."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Task flow across execution phases</p> </div> <div class="chart-card__body" id="pipeline-view"></div> </section> <!-- Charts Row: Agent + Outcomes --> <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">Task count per agent, stacked by outcome</p> </div> <div class="chart-card__body" id="agent-chart"></div> </section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Delegation Outcomes</h2> <p class="chart-card__desc">Task outcome distribution</p> </div> <div class="chart-card__body" id="delegation-outcome-chart"></div> </section> </div> <!-- Charts Row: Tiers + Mechanism --> <div class="charts-row" id="tier-section" data-nav-section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Tier Distribution</h2> <p class="chart-card__desc">Model tier breakdown</p> </div> <div class="chart-card__body" id="tier-chart"></div> </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> </section> </div> <!-- Model Usage --> <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">Tasks by model</p> </div> <div class="chart-card__body" id="model-chart"></div> </section> <!-- Quality Section --> <section class="chart-card" id="quality-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Quality / Review</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Code review results and dispute resolution." data-tooltip="Code review results and dispute resolution."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Code review results and quality gate outcomes for the selected convoy</p> </div> <div class="chart-card__body"> <div id="quality-cards" class="task-summary-cards"></div> <div id="quality-review-table"></div> </div> </section> <!-- Reliability Section --> <section class="chart-card" id="reliability-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Reliability</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Errors, retry attempts, and safety mechanisms." data-tooltip="Errors, retry attempts, and safety mechanisms."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Retry queue and error overview for the selected convoy</p> </div> <div class="chart-card__body"> <div id="reliability-dlq-card"></div> <div id="reliability-dlq-table"></div> <div style="margin-top:24px"> <h3 style="font-size:1rem;font-weight:600;color:var(--text-secondary);margin:0 0 12px">Error Overview</h3> <div id="reliability-error-overview"></div> </div> </div> </section> <!-- Drift Section --> <section class="chart-card" id="drift-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Drift</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: How far the actual work deviated from the original plan." data-tooltip="How far the actual work deviated from the original plan."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Plan adherence and deviation metrics for the selected convoy</p> </div> <div class="chart-card__body"> <div id="drift-cards" class="task-summary-cards"></div> <div id="drift-secret-banner" style="display:none"></div> </div> </section> <!-- Outputs Section --> <section class="chart-card" id="outputs-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Outputs &amp; Artifacts</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Files, data, and summaries produced by this convoy." data-tooltip="Files, data, and summaries produced by this convoy."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Files, summaries, and structured data produced by tasks in this convoy</p> </div> <div class="chart-card__body"> <div id="outputs-cards" class="task-summary-cards"></div> <div id="artifact-table-wrap"></div> </div> </section> <!-- Event Timeline Section --> <section class="chart-card" id="event-timeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Event Timeline</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Chronological log of everything that happened during this run." data-tooltip="Chronological log of everything that happened during this run."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Convoy events in reverse chronological order</p> </div> <div class="chart-card__body"> <div id="event-timeline-filters" class="timeline-filters"></div> <div id="event-timeline-list"></div> <div id="event-timeline-load-more" style="display:none;text-align:center;padding:16px"> <button class="dash-btn dash-btn--ghost" id="event-timeline-more-btn" type="button">Load more</button> </div> </div> </section> <!-- Execution Log --> <section class="chart-card" id="execution-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Execution Log</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Step-by-step trace of recent task activity." data-tooltip="Step-by-step trace of recent task activity."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Recent agent activity, step by step</p> </div> <div class="chart-card__body" id="execution-log"></div> </section> <!-- Panel Reviews --> <section class="chart-card" id="panel-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Panel Reviews</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Quality gate verdicts from majority-vote panels." data-tooltip="Quality gate verdicts from majority-vote panels."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Quality gate verdicts and fix items</p> </div> <div class="chart-card__body" id="panel-chart"></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> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Single-reviewer quality gate results." data-tooltip="Single-reviewer quality gate results."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <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> </section> </div><!-- .view-convoy-detail --> </main> </div> </body></html> <script>(function(){const base = "/";
7
+ </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 dash-sidebar__link--active" href="#overall-section" data-section="overall-section" data-view="home" aria-label="Overview section">Overview</a></li> <li><a class="dash-sidebar__link" href="#convoy-list-section" data-section="convoy-list-section" data-view="home" aria-label="Convoys section">Convoys</a></li> <li><a class="dash-sidebar__link" href="#activity-timeline-section" data-section="activity-timeline-section" data-view="home" aria-label="Activity Timeline section">Activity</a></li> <li><a class="dash-sidebar__link" href="#sessions-section" data-section="sessions-section" data-view="home" aria-label="Sessions section">Sessions</a></li> <li><a class="dash-sidebar__link" href="#tasks-section" data-section="tasks-section" data-view="detail" aria-label="Tasks section">Tasks</a></li> <li><a class="dash-sidebar__link" href="#pipeline-section" data-section="pipeline-section" data-view="detail" aria-label="Pipeline section">Pipeline</a></li> <li><a class="dash-sidebar__link" href="#agent-section" data-section="agent-section" data-view="detail" aria-label="Agents section">Agents</a></li> <li><a class="dash-sidebar__link" href="#tier-section" data-section="tier-section" data-view="detail" aria-label="Tiers section">Tiers</a></li> <li><a class="dash-sidebar__link" href="#model-section" data-section="model-section" data-view="detail" aria-label="Models section">Models</a></li> <li><a class="dash-sidebar__link" href="#quality-section" data-section="quality-section" data-view="detail" aria-label="Quality section">Quality</a></li> <li><a class="dash-sidebar__link" href="#reliability-section" data-section="reliability-section" data-view="detail" aria-label="Reliability section">Reliability</a></li> <li><a class="dash-sidebar__link" href="#drift-section" data-section="drift-section" data-view="detail" aria-label="Drift section">Drift</a></li> <li><a class="dash-sidebar__link" href="#outputs-section" data-section="outputs-section" data-view="detail" aria-label="Outputs section">Outputs</a></li> <li><a class="dash-sidebar__link" href="#execution-section" data-section="execution-section" data-view="detail" aria-label="Execution Log section">Execution Log</a></li> <li><a class="dash-sidebar__link" href="#event-timeline-section" data-section="event-timeline-section" data-view="detail" aria-label="Event Log section">Event Log</a></li> <li><a class="dash-sidebar__link" href="#reviews-section" data-section="reviews-section" data-view="detail" aria-label="Fast Reviews section">Fast Reviews</a></li> <li><a class="dash-sidebar__link" href="#panel-section" data-section="panel-section" data-view="detail" aria-label="Panel Reviews section">Panel Reviews</a></li> </ul> </nav> <main class="dash-main"> <nav class="breadcrumbs" id="breadcrumbs" data-view-hidden> <a class="breadcrumbs__link" href="#" id="breadcrumbs-home">Observability</a> <span class="breadcrumbs__separator">/</span> <span class="breadcrumbs__current" id="breadcrumbs-convoy"></span> </nav> <div class="view-home" id="view-home"> <!-- Overall Stats Section --> <section class="overall-stats" id="overall-section" data-nav-section> <div class="overall-stats__header"> <h2 class="overall-stats__title">Overall Stats</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: How all runs behave across your project." data-tooltip="How all runs behave across your project."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> </div> <div class="overall-stats__grid"> <div class="overall-kpi" id="overall-total-runs"> <span class="overall-kpi__label">Total Runs <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Number of convoy runs executed" data-tooltip="Number of convoy runs executed"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span></span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-running"> <span class="overall-kpi__label">Running Now</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-success-rate"> <span class="overall-kpi__label">Success Rate</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-avg-duration"> <span class="overall-kpi__label">Avg Duration</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-tokens"> <span class="overall-kpi__label">Total Tokens</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-cost"> <span class="overall-kpi__label">Total Cost</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-tasks"> <span class="overall-kpi__label">Total Tasks</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-retries"> <span class="overall-kpi__label">Total Retries</span> <span class="overall-kpi__value">&mdash;</span> </div> </div> </section> <!-- Activity Timeline --> <section class="chart-card" id="activity-timeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Activity Timeline</h2> <p class="chart-card__desc">Convoy runs over time</p> </div> <div class="chart-card__body" id="activity-timeline-chart"></div> </section> <!-- Convoy List Section --> <section class="convoy-list-section" id="convoy-list-section"> <div class="convoy-list-section__header"> <h2>Convoys</h2> <p class="convoy-list-section__desc">All convoy runs across your project</p> </div> <div class="convoy-list-filters" id="convoy-list-filters"> <div class="convoy-list-filters__group"> <label for="cl-filter-search">Search</label> <input class="convoy-list-filters__input" type="text" id="cl-filter-search" placeholder="Convoy name…"> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-status">Status</label> <select class="convoy-list-filters__select" id="cl-filter-status"> <option value="">All</option> <option value="done">Done</option> <option value="running">Running</option> <option value="failed">Failed</option> <option value="gate-failed">Gate Failed</option> <option value="pending">Pending</option> </select> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-from">From</label> <input class="convoy-list-filters__date" type="date" id="cl-filter-from"> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-to">To</label> <input class="convoy-list-filters__date" type="date" id="cl-filter-to"> </div> <button class="convoy-list-filters__reset" type="button" id="cl-filter-reset">Reset</button> </div> <div id="convoy-list-table-wrap"></div> <div class="convoy-list-empty" id="convoy-list-empty" style="display:none"> <div class="convoy-list-empty__icon"> <svg width="40" height="40" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="6" y="6" width="28" height="28" rx="4"></rect><line x1="14" y1="20" x2="26" y2="20" opacity="0.5"></line></svg> </div> <p class="convoy-list-empty__text">No convoys match your filters</p> </div> </section> <!-- Recent Sessions --> <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">Latest agent sessions from observability log</p> </div> <div class="chart-card__body chart-card__body--table" id="sessions-table"></div> </section> </div><!-- .view-home --> <div class="view-convoy-detail" id="view-convoy-detail" data-view-hidden> <section class="convoy-detail-hero" id="convoy-detail-hero"> <div class="convoy-detail-hero__top"> <div class="convoy-detail-hero__title" id="detail-hero-title"></div> <span class="convoy-detail-hero__status" id="detail-hero-status"></span> </div> <div class="convoy-detail-hero__meta" id="detail-hero-meta"></div> </section> <!-- Tasks Section --> <section class="chart-card" id="tasks-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Tasks</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Individual units of work in this convoy." data-tooltip="Individual units of work in this convoy."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc" id="tasks-section-desc">Task breakdown for the selected convoy</p> </div> <div class="chart-card__body" id="tasks-section-body"> <div id="task-summary-cards" class="task-summary-cards"></div> <div id="phase-breakdown" class="phase-breakdown"></div> <div id="task-table-wrap" class="task-table-wrap"></div> </div> </section> <!-- Pipeline View --> <section class="chart-card" id="pipeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Task Pipeline</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Visualizes how tasks progress through sequential execution phases." data-tooltip="Visualizes how tasks progress through sequential execution phases — from initial setup through integration, validation, and final quality gate."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Task flow across execution phases</p> </div> <div class="chart-card__body" id="pipeline-view"></div> </section> <!-- Charts Row: Agent + Outcomes --> <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">Task count per agent, stacked by outcome</p> </div> <div class="chart-card__body" id="agent-chart"></div> </section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Delegation Outcomes</h2> <p class="chart-card__desc">Task outcome distribution</p> </div> <div class="chart-card__body" id="delegation-outcome-chart"></div> </section> </div> <!-- Charts Row: Tiers + Mechanism --> <div class="charts-row" id="tier-section" data-nav-section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Tier Distribution</h2> <p class="chart-card__desc">Model tier breakdown</p> </div> <div class="chart-card__body" id="tier-chart"></div> </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> </section> </div> <!-- Model Usage --> <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">Tasks by model</p> </div> <div class="chart-card__body" id="model-chart"></div> </section> <!-- Quality Section --> <section class="chart-card" id="quality-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Quality / Review</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Code review results and dispute resolution." data-tooltip="Code review results and dispute resolution."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Code review results and quality gate outcomes for the selected convoy</p> </div> <div class="chart-card__body"> <div id="quality-cards" class="task-summary-cards"></div> <div id="quality-review-table"></div> </div> </section> <!-- Reliability Section --> <section class="chart-card" id="reliability-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Reliability</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Errors, retry attempts, and safety mechanisms." data-tooltip="Errors, retry attempts, and safety mechanisms."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Retry queue and error overview for the selected convoy</p> </div> <div class="chart-card__body"> <div id="reliability-dlq-card"></div> <div id="reliability-dlq-table"></div> <div style="margin-top:24px"> <h3 style="font-size:1rem;font-weight:600;color:var(--text-secondary);margin:0 0 12px">Error Overview</h3> <div id="reliability-error-overview"></div> </div> </div> </section> <!-- Drift Section --> <section class="chart-card" id="drift-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Drift</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: How far the actual work deviated from the original plan." data-tooltip="How far the actual work deviated from the original plan."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Plan adherence and deviation metrics for the selected convoy</p> </div> <div class="chart-card__body"> <div id="drift-cards" class="task-summary-cards"></div> <div id="drift-secret-banner" style="display:none"></div> </div> </section> <!-- Outputs Section --> <section class="chart-card" id="outputs-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Outputs &amp; Artifacts</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Files, data, and summaries produced by this convoy." data-tooltip="Files, data, and summaries produced by this convoy."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Files, summaries, and structured data produced by tasks in this convoy</p> </div> <div class="chart-card__body"> <div id="outputs-cards" class="task-summary-cards"></div> <div id="artifact-table-wrap"></div> </div> </section> <!-- Event Timeline Section --> <section class="chart-card" id="event-timeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Event Timeline</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Chronological log of everything that happened during this run." data-tooltip="Chronological log of everything that happened during this run."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Convoy events in chronological order</p> </div> <div class="chart-card__body"> <div id="event-timeline-filters" class="timeline-filters"></div> <div id="event-timeline-list"></div> <div id="event-timeline-load-more" style="display:none;text-align:center;padding:16px"> <button class="dash-btn dash-btn--ghost" id="event-timeline-more-btn" type="button">Load more</button> </div> </div> </section> <!-- Execution Log --> <section class="chart-card" id="execution-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Execution Log</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Step-by-step trace of recent task activity." data-tooltip="Step-by-step trace of recent task activity."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Recent agent activity, step by step</p> </div> <div class="chart-card__body" id="execution-log"></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> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Single-reviewer quality gate results." data-tooltip="Single-reviewer quality gate results."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <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> </section> <!-- Panel Reviews --> <section class="chart-card" id="panel-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Panel Reviews</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Quality gate verdicts from majority-vote panels." data-tooltip="Quality gate verdicts from majority-vote panels."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Quality gate verdicts and fix items</p> </div> <div class="chart-card__body" id="panel-chart"></div> </section> </div><!-- .view-convoy-detail --> </main> </div> </body></html> <script>(function(){const base = "/";
8
8
 
9
9
  // ── Data Loading ──────────────────────────────────────────
10
10
 
@@ -43,6 +43,7 @@ Export
43
43
  standard: '#a78bfa',
44
44
  utility: '#3b82f6',
45
45
  economy: '#64748b',
46
+ unknown: '#94a3b8',
46
47
  };
47
48
 
48
49
  const MODEL_COLORS = {
@@ -175,6 +176,8 @@ Export
175
176
  }
176
177
  renderOverallStats();
177
178
  renderConvoyList();
179
+ renderActivityTimeline();
180
+ renderRecentSessions();
178
181
  } catch {
179
182
  // Non-fatal
180
183
  }
@@ -360,6 +363,14 @@ Export
360
363
  }
361
364
 
362
365
  // ── Convoy List ─────────────────────────────────────────
366
+ var convoyListCurrentPage = 1;
367
+ var CONVOY_LIST_PAGE_SIZE = 10;
368
+ var convoyChainExpanded = {};
369
+ var executionLogPageSize = 20;
370
+ var executionLogCurrentPage = 1;
371
+ var executionLogAllSortedTasks = [];
372
+ var executionLogLimit = 10;
373
+
363
374
  function renderConvoyList() {
364
375
  const data = window.__DASHBOARD_DATA__;
365
376
  const list = data?.convoyList ?? [];
@@ -380,36 +391,112 @@ Export
380
391
 
381
392
  if (filtered.length === 0) {
382
393
  wrap.innerHTML = '';
394
+ var existingPag = document.getElementById('convoy-list-pagination');
395
+ if (existingPag) existingPag.remove();
383
396
  if (emptyEl) emptyEl.style.display = '';
384
397
  return;
385
398
  }
386
399
  if (emptyEl) emptyEl.style.display = 'none';
387
400
 
388
401
  const statusBadgeClass = (s) => {
389
- const map = { done: '--done', running: '--running', failed: '--failed', 'gate-failed': '--gate-failed', pending: '--pending' };
402
+ const map = { done: '--done', running: '--running', failed: '--failed', 'gate-failed': '--gate-failed', gate_failed: '--gate-failed', pending: '--pending' };
390
403
  return 'status-badge ' + (map[s] || '');
391
404
  };
392
405
 
406
+ // Group convoys by pipeline_id (non-null pipeline_id forms a chain)
407
+ var grouped = [];
408
+ var pipelineMap = {};
409
+ for (var gi = 0; gi < filtered.length; gi++) {
410
+ var gc = filtered[gi];
411
+ if (gc.pipeline_id) {
412
+ if (!pipelineMap[gc.pipeline_id]) {
413
+ pipelineMap[gc.pipeline_id] = { pipeline_id: gc.pipeline_id, convoys: [] };
414
+ grouped.push(pipelineMap[gc.pipeline_id]);
415
+ }
416
+ pipelineMap[gc.pipeline_id].convoys.push(gc);
417
+ } else {
418
+ grouped.push(gc);
419
+ }
420
+ }
421
+
422
+ // Pagination on grouped top-level items
423
+ var totalPages = Math.ceil(grouped.length / CONVOY_LIST_PAGE_SIZE);
424
+ if (convoyListCurrentPage > totalPages) convoyListCurrentPage = 1;
425
+ var pageStart = (convoyListCurrentPage - 1) * CONVOY_LIST_PAGE_SIZE;
426
+ var paginated = grouped.slice(pageStart, pageStart + CONVOY_LIST_PAGE_SIZE);
427
+
393
428
  let html = '<table class="convoy-list-table"><thead><tr>' +
394
429
  '<th>Name</th><th>Status</th><th>Tasks</th><th>Created</th><th>Duration</th>' +
395
430
  '</tr></thead><tbody>';
396
431
 
397
- for (const c of filtered) {
398
- const name = escapeHtml(c.name || c.id);
399
- const dateStr = c.created_at ? formatTime(c.created_at) : '\u2014';
400
- const duration = c.started_at && c.finished_at ? formatDuration(c.started_at, c.finished_at) : (c.status === 'running' ? 'In progress' : '\u2014');
401
- const taskCount = c.task_count ?? c.taskCount ?? '\u2014';
402
- html += '<tr data-convoy-id="' + escapeHtml(c.id) + '" class="task-row--clickable">' +
403
- '<td><strong>' + name + '</strong></td>' +
404
- '<td><span class="' + statusBadgeClass(c.status) + '">' + escapeHtml(c.status || 'unknown') + '</span></td>' +
405
- '<td>' + taskCount + '</td>' +
406
- '<td>' + dateStr + '</td>' +
407
- '<td>' + (duration || '\u2014') + '</td>' +
408
- '</tr>';
432
+ for (var pi = 0; pi < paginated.length; pi++) {
433
+ var item = paginated[pi];
434
+ if (item.convoys) {
435
+ // Pipeline group row
436
+ var grpConvoys = item.convoys;
437
+ var grpPipelineId = item.pipeline_id;
438
+ var isExpanded = !!convoyChainExpanded[grpPipelineId];
439
+ var grpStatus = (function(cs) {
440
+ if (cs.some(function(x) { return x.status === 'failed' || x.status === 'gate_failed'; })) return 'gate_failed';
441
+ if (cs.some(function(x) { return x.status === 'running'; })) return 'running';
442
+ if (cs.every(function(x) { return x.status === 'done'; })) return 'done';
443
+ return cs[0] ? cs[0].status : 'pending';
444
+ })(grpConvoys);
445
+ var grpTasks = grpConvoys.reduce(function(s, x) { return s + (x.task_count || 0); }, 0);
446
+ var grpEarliestCreated = grpConvoys.reduce(function(a, x) { return (!a || (x.created_at && x.created_at < a)) ? x.created_at : a; }, null);
447
+ var grpLatestFinished = grpConvoys.reduce(function(a, x) { return (!a || (x.finished_at && x.finished_at > a)) ? x.finished_at : a; }, null);
448
+ var grpAllFinished = grpConvoys.every(function(x) { return !!x.finished_at; });
449
+ var grpDuration = grpEarliestCreated && grpAllFinished && grpLatestFinished ? formatDuration(grpEarliestCreated, grpLatestFinished) : (grpStatus === 'running' ? 'In progress' : '\u2014');
450
+ var grpName = grpConvoys[0].pipeline_name || grpPipelineId.replace(/-/g, ' ').replace(/\b\w/g, function(l) { return l.toUpperCase(); });
451
+ var toggleIcon = isExpanded ? '\u25bc' : '\u25b6';
452
+ html += '<tr class="convoy-chain-row task-row--clickable" data-pipeline-group-id="' + escapeHtml(grpPipelineId) + '" aria-expanded="' + isExpanded + '" role="row">' +
453
+ '<td><span class="convoy-chain-toggle">' + toggleIcon + '</span><strong>' + escapeHtml(grpName) + '</strong></td>' +
454
+ '<td><span class="' + statusBadgeClass(grpStatus) + '">' + escapeHtml(grpStatus) + '</span></td>' +
455
+ '<td>' + grpTasks + '</td>' +
456
+ '<td>' + (grpEarliestCreated ? formatTime(grpEarliestCreated) : '\u2014') + '</td>' +
457
+ '<td>' + grpDuration + '</td>' +
458
+ '</tr>';
459
+ if (isExpanded) {
460
+ for (var ci = 0; ci < grpConvoys.length; ci++) {
461
+ var cc = grpConvoys[ci];
462
+ var ccName = escapeHtml(cc.name || cc.id);
463
+ var ccDate = cc.created_at ? formatTime(cc.created_at) : '\u2014';
464
+ var ccDur = cc.started_at && cc.finished_at ? formatDuration(cc.started_at, cc.finished_at) : (cc.status === 'running' ? 'In progress' : '\u2014');
465
+ var ccTasks = cc.task_count != null ? cc.task_count : '\u2014';
466
+ html += '<tr data-convoy-id="' + escapeHtml(cc.id) + '" class="task-row--clickable convoy-chain-child">' +
467
+ '<td><strong>' + ccName + '</strong></td>' +
468
+ '<td><span class="' + statusBadgeClass(cc.status) + '">' + escapeHtml(cc.status || 'unknown') + '</span></td>' +
469
+ '<td>' + ccTasks + '</td>' +
470
+ '<td>' + ccDate + '</td>' +
471
+ '<td>' + ccDur + '</td>' +
472
+ '</tr>';
473
+ }
474
+ }
475
+ } else {
476
+ var name = escapeHtml(item.name || item.id);
477
+ var dateStr = item.created_at ? formatTime(item.created_at) : '\u2014';
478
+ var duration = item.started_at && item.finished_at ? formatDuration(item.started_at, item.finished_at) : (item.status === 'running' ? 'In progress' : '\u2014');
479
+ var taskCount = item.task_count != null ? item.task_count : '\u2014';
480
+ html += '<tr data-convoy-id="' + escapeHtml(item.id) + '" class="task-row--clickable">' +
481
+ '<td><strong>' + name + '</strong></td>' +
482
+ '<td><span class="' + statusBadgeClass(item.status) + '">' + escapeHtml(item.status || 'unknown') + '</span></td>' +
483
+ '<td>' + taskCount + '</td>' +
484
+ '<td>' + dateStr + '</td>' +
485
+ '<td>' + (duration || '\u2014') + '</td>' +
486
+ '</tr>';
487
+ }
409
488
  }
410
489
  html += '</tbody></table>';
411
490
  wrap.innerHTML = html;
412
491
 
492
+ wrap.querySelectorAll('tr[data-pipeline-group-id]').forEach(row => {
493
+ row.addEventListener('click', () => {
494
+ const groupId = row.dataset.pipelineGroupId;
495
+ convoyChainExpanded[groupId] = !convoyChainExpanded[groupId];
496
+ renderConvoyList();
497
+ });
498
+ });
499
+
413
500
  wrap.querySelectorAll('tr[data-convoy-id]').forEach(row => {
414
501
  row.addEventListener('click', () => {
415
502
  const id = row.dataset.convoyId;
@@ -417,6 +504,26 @@ Export
417
504
  showConvoyDetailView(id, nameCell ? nameCell.textContent : id);
418
505
  });
419
506
  });
507
+
508
+ // Render pagination controls
509
+ var oldPag = document.getElementById('convoy-list-pagination');
510
+ if (oldPag) oldPag.remove();
511
+ if (grouped.length > CONVOY_LIST_PAGE_SIZE) {
512
+ var pagEl = document.createElement('div');
513
+ pagEl.id = 'convoy-list-pagination';
514
+ pagEl.className = 'convoy-list-pagination';
515
+ pagEl.innerHTML =
516
+ '<button class="dash-btn dash-btn--ghost" id="convoy-list-prev" type="button"' + (convoyListCurrentPage === 1 ? ' disabled' : '') + '>Previous</button>' +
517
+ '<span class="convoy-list-pagination__info">Page ' + convoyListCurrentPage + ' of ' + totalPages + '</span>' +
518
+ '<button class="dash-btn dash-btn--ghost" id="convoy-list-next" type="button"' + (convoyListCurrentPage === totalPages ? ' disabled' : '') + '>Next</button>';
519
+ wrap.after(pagEl);
520
+ pagEl.querySelector('#convoy-list-prev').addEventListener('click', function() {
521
+ if (convoyListCurrentPage > 1) { convoyListCurrentPage--; renderConvoyList(); }
522
+ });
523
+ pagEl.querySelector('#convoy-list-next').addEventListener('click', function() {
524
+ if (convoyListCurrentPage < totalPages) { convoyListCurrentPage++; renderConvoyList(); }
525
+ });
526
+ }
420
527
  }
421
528
 
422
529
  // ── Overall Stats Rendering ──────────────────────────────
@@ -439,8 +546,28 @@ Export
439
546
 
440
547
  const avgSec = ds.avg_sec;
441
548
  setKpiValue('overall-avg-duration', avgSec != null ? formatDurationSec(avgSec) : '\u2014');
442
- setKpiValue('overall-total-tokens', formatTokens(tc.total_tokens || 0));
443
- setKpiValue('overall-total-cost', '$' + (tc.total_cost_usd || 0).toFixed(2));
549
+ var totalTokens = (tc && tc.total_tokens) || 0;
550
+ var totalCost = (tc && tc.total_cost_usd) || 0;
551
+ setKpiValue('overall-total-tokens', formatTokens(totalTokens));
552
+ setKpiValue('overall-total-cost', '$' + totalCost.toFixed(2));
553
+
554
+ // Fallback: if overall stats show 0 tokens, sum from convoy list
555
+ if (totalTokens === 0) {
556
+ var convoyListFallback = (window.__DASHBOARD_DATA__ && window.__DASHBOARD_DATA__.convoyList) || [];
557
+ if (convoyListFallback.length > 0) {
558
+ var sumTokens = convoyListFallback.reduce(function(s, c) { return s + (c.total_tokens != null ? c.total_tokens : 0); }, 0);
559
+ var sumCost = convoyListFallback.reduce(function(s, c) { return s + (c.total_cost_usd != null ? c.total_cost_usd : 0); }, 0);
560
+ if (sumTokens > 0) {
561
+ setKpiValue('overall-total-tokens', formatTokens(sumTokens));
562
+ setKpiValue('overall-total-cost', '$' + sumCost.toFixed(2));
563
+ }
564
+ }
565
+ }
566
+
567
+ // Total tasks and retries
568
+ var tt = stats.taskTotals || {};
569
+ setKpiValue('overall-total-tasks', String(tt.totalTasks || 0));
570
+ setKpiValue('overall-total-retries', String(tt.totalRetries || 0));
444
571
  }
445
572
 
446
573
  function setKpiValue(id, value) {
@@ -461,7 +588,70 @@ Export
461
588
  return hr + 'h ' + remMin + 'm';
462
589
  }
463
590
 
591
+ function renderActivityTimeline() {
592
+ var el = document.getElementById('activity-timeline-chart');
593
+ if (!el) return;
594
+ var data = window.__DASHBOARD_DATA__;
595
+ var timeline = (data && data.overallStats && data.overallStats.activityTimeline) || [];
596
+ if (timeline.length === 0) {
597
+ el.innerHTML = emptyStateHtml('timeline', 'No activity yet', 'Convoy run activity will appear here as runs complete.');
598
+ return;
599
+ }
600
+ var maxCount = Math.max.apply(null, timeline.map(function(d) { return d.count; }));
601
+ var html = '<div class="activity-timeline">';
602
+ for (var i = 0; i < timeline.length; i++) {
603
+ var d = timeline[i];
604
+ var pct = maxCount > 0 ? Math.round((d.count / maxCount) * 100) : 0;
605
+ html += '<div class="bar-row">' +
606
+ '<span class="bar-label">' + formatShortDate(d.date) + '</span>' +
607
+ '<div class="bar-track"><div class="bar-segment bar--accent" style="width:' + pct + '%"></div></div>' +
608
+ '<span class="bar-value">' + d.count + '</span>' +
609
+ '</div>';
610
+ }
611
+ html += '</div>';
612
+ el.innerHTML = html;
613
+ }
614
+
615
+ async function renderRecentSessions() {
616
+ var el = document.getElementById('sessions-table');
617
+ if (!el) return;
618
+ try {
619
+ var allEvents = await loadNdjson(base + 'data/events.ndjson');
620
+ var sessions = allEvents.filter(function(e) { return e.type === 'session'; });
621
+ sessions.sort(function(a, b) {
622
+ return (b.timestamp || '').localeCompare(a.timestamp || '');
623
+ });
624
+ sessions = sessions.slice(0, 15);
625
+ if (sessions.length === 0) {
626
+ el.innerHTML = emptyStateHtml('sessions', 'No sessions recorded yet', 'Agent session data will appear here when sessions are logged via opencastle log.');
627
+ return;
628
+ }
629
+ var thead = '<thead><tr>' +
630
+ '<th>Agent</th><th>Task</th><th>Outcome</th><th>Duration</th><th>Retries</th><th>Files</th><th>Time</th>' +
631
+ '</tr></thead>';
632
+ var tbody = '<tbody>';
633
+ for (var i = 0; i < sessions.length; i++) {
634
+ var s = sessions[i];
635
+ var outcomeClass = s.outcome === 'success' ? 'outcome-badge--success' : s.outcome === 'failed' ? 'outcome-badge--failed' : 'outcome-badge--partial';
636
+ tbody += '<tr>' +
637
+ '<td class="td-agent">' + escapeHtml(s.agent || '\u2014') + '</td>' +
638
+ '<td class="td-task">' + escapeHtml(s.task || '\u2014') + '</td>' +
639
+ '<td><span class="outcome-badge ' + outcomeClass + '">' + escapeHtml(s.outcome || '\u2014') + '</span></td>' +
640
+ '<td>' + (s.duration_min != null ? s.duration_min + 'm' : '\u2014') + '</td>' +
641
+ '<td>' + (s.retries || 0) + '</td>' +
642
+ '<td>' + (s.files_changed || 0) + '</td>' +
643
+ '<td>' + (s.timestamp ? formatTime(s.timestamp) : '\u2014') + '</td>' +
644
+ '</tr>';
645
+ }
646
+ tbody += '</tbody>';
647
+ el.innerHTML = '<table class="sessions-table">' + thead + tbody + '</table>';
648
+ } catch (e) {
649
+ el.innerHTML = emptyStateHtml('sessions', 'No sessions recorded yet', 'Agent session data will appear here when sessions are logged.');
650
+ }
651
+ }
652
+
464
653
  async function loadConvoyDetail(convoyId) {
654
+ executionLogLimit = 10;
465
655
  if (!convoyId) {
466
656
  renderConvoyDetailHeader(null);
467
657
  ['pipeline-section', 'agent-section', 'tier-section', 'model-section', 'quality-section', 'reliability-section', 'drift-section', 'outputs-section', 'execution-section', 'event-timeline-section', 'panel-section', 'reviews-section'].forEach(function(id) {
@@ -716,6 +906,21 @@ Export
716
906
 
717
907
  function deriveTier(model) {
718
908
  if (!model) return 'unknown';
909
+ var TIER_MAP = {
910
+ 'claude-opus-4-6': 'premium',
911
+ 'claude-opus-4': 'premium',
912
+ 'claude-sonnet-4-6': 'standard',
913
+ 'claude-sonnet-4': 'standard',
914
+ 'claude-haiku-3-5': 'economy',
915
+ 'claude-haiku-4': 'economy',
916
+ 'gpt-5-mini': 'economy',
917
+ 'gpt-5.3-codex': 'standard',
918
+ 'gpt-5': 'standard',
919
+ 'gemini-3.1-pro': 'standard',
920
+ 'gemini-3.0-flash': 'economy',
921
+ };
922
+ if (TIER_MAP[model]) return TIER_MAP[model];
923
+ // Fallback heuristics for unknown models
719
924
  var m = model.toLowerCase();
720
925
  if (m.includes('opus')) return 'premium';
721
926
  if (m.includes('sonnet') || m.includes('pro')) return 'standard';
@@ -868,8 +1073,9 @@ Export
868
1073
  var mechOrder = ['sub-agent', 'background', 'unknown'];
869
1074
  var mechs = mechOrder.filter(function(m) { return mechCounts[m]; }).map(function(m) { return { name: m, count: mechCounts[m] }; });
870
1075
 
871
- if (mechs.length === 0) {
872
- el.innerHTML = emptyStateHtml('mechanism', 'No delegation data yet', 'The split between sub-agent and background delegations will be shown here.');
1076
+ var isAllUnknown = mechs.length === 0 || (mechs.length === 1 && mechs[0].name === 'unknown');
1077
+ if (isAllUnknown) {
1078
+ el.innerHTML = emptyStateHtml('mechanism', 'No delegation data', 'Delegation mechanism data is not available for this convoy.');
873
1079
  return;
874
1080
  }
875
1081
 
@@ -947,7 +1153,7 @@ Export
947
1153
  var models = Object.entries(modelCounts).sort(function(a, b) { return b[1] - a[1]; });
948
1154
 
949
1155
  if (models.length === 0) {
950
- el.innerHTML = emptyStateHtml('models', 'No model data yet', 'Model utilization across tasks will be shown here.');
1156
+ el.innerHTML = emptyStateHtml('models', tasks.length > 0 ? 'Model information unavailable' : 'No model data yet', tasks.length > 0 ? 'No model information was recorded for tasks in this convoy.' : 'Model utilization across tasks will be shown here.');
951
1157
  return;
952
1158
  }
953
1159
 
@@ -975,7 +1181,8 @@ Export
975
1181
  return;
976
1182
  }
977
1183
 
978
- var sorted = tasks.slice().filter(function(t) { return t.started_at; }).sort(function(a, b) { return new Date(b.started_at) - new Date(a.started_at); }).slice(0, 10);
1184
+ var allSorted = tasks.slice().filter(function(t) { return t.started_at; }).sort(function(a, b) { return new Date(a.started_at) - new Date(b.started_at); });
1185
+ var sorted = allSorted.slice(0, executionLogLimit);
979
1186
 
980
1187
  if (sorted.length === 0) {
981
1188
  el.innerHTML = emptyStateHtml('execLog', 'No execution history yet', 'A step-by-step trace of task activity will appear here.');
@@ -1017,6 +1224,15 @@ Export
1017
1224
  '</div>';
1018
1225
  }).join('') +
1019
1226
  '</div>';
1227
+ if (allSorted.length > executionLogLimit) {
1228
+ var remaining = allSorted.length - executionLogLimit;
1229
+ el.innerHTML += '<div style="text-align:center;padding:16px"><button class="dash-btn dash-btn--ghost" id="exec-log-more-btn" type="button">Show more (' + remaining + ' remaining)</button></div>';
1230
+ document.getElementById('exec-log-more-btn')?.addEventListener('click', function() {
1231
+ executionLogLimit = allSorted.length;
1232
+ var convoy = window.__SELECTED_CONVOY__;
1233
+ renderDetailExecutionLog((convoy && convoy.tasks) ? convoy.tasks : []);
1234
+ });
1235
+ }
1020
1236
  }
1021
1237
 
1022
1238
  // ── Convoy Detail: Panel Reviews ─────────────────────────
@@ -1028,7 +1244,7 @@ Export
1028
1244
  var panelTasks = (tasks || []).filter(function(t) { return t.panel_attempts > 0; });
1029
1245
 
1030
1246
  if (panelTasks.length === 0) {
1031
- el.innerHTML = emptyStateHtml('panels', 'No panel reviews yet', 'Quality gate verdicts from majority-vote panels will be shown here.');
1247
+ el.innerHTML = emptyStateHtml('panels', 'No panel reviews', 'This convoy completed without requiring panel reviews.');
1032
1248
  return;
1033
1249
  }
1034
1250
 
@@ -1064,7 +1280,7 @@ Export
1064
1280
  var reviewedTasks = (tasks || []).filter(function(t) { return t.review_level != null; });
1065
1281
 
1066
1282
  if (reviewedTasks.length === 0) {
1067
- el.innerHTML = emptyStateHtml('panels', 'No fast reviews yet', 'Single-reviewer quality gate results will be listed here.');
1283
+ el.innerHTML = emptyStateHtml('panels', 'No fast reviews', 'This convoy completed without fast review gates.');
1068
1284
  return;
1069
1285
  }
1070
1286
 
@@ -1169,13 +1385,27 @@ Export
1169
1385
 
1170
1386
  var dlqCount = detail.dlq_count || 0;
1171
1387
  var dlqEntries = detail.dlq_entries || [];
1388
+ var tasks = detail.tasks || [];
1172
1389
 
1173
1390
  var dlqCardEl = document.getElementById('reliability-dlq-card');
1174
1391
  if (dlqCardEl) {
1392
+ var totalRetries = tasks.reduce(function(s, t) { return s + (t.retries || 0); }, 0);
1393
+ var doneTasks = tasks.filter(function(t) { return t.status === 'done'; }).length;
1394
+ var successRate = tasks.length > 0 ? Math.round((doneTasks / tasks.length) * 100) + '%' : '\u2014';
1175
1395
  dlqCardEl.innerHTML =
1396
+ '<div class="task-summary-cards">' +
1176
1397
  '<div class="task-summary-card task-summary-card--' + (dlqCount > 0 ? 'errors' : 'done') + '">' +
1177
1398
  '<span class="task-summary-card__label">Retry Queue <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Tasks that failed too many times and need manual attention. Also known as Dead Letter Queue (DLQ)." data-tooltip="Tasks that failed too many times and need manual attention. Also known as Dead Letter Queue (DLQ).">' + INFO_ICON + '</span></span>' +
1178
1399
  '<span class="task-summary-card__value">' + dlqCount + '</span>' +
1400
+ '</div>' +
1401
+ '<div class="task-summary-card task-summary-card--running">' +
1402
+ '<span class="task-summary-card__label">Total Retries <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Total number of task retries across all tasks in this convoy." data-tooltip="Total number of task retries across all tasks in this convoy.">' + INFO_ICON + '</span></span>' +
1403
+ '<span class="task-summary-card__value">' + totalRetries + '</span>' +
1404
+ '</div>' +
1405
+ '<div class="task-summary-card task-summary-card--done">' +
1406
+ '<span class="task-summary-card__label">Task Success Rate <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Percentage of tasks that completed successfully." data-tooltip="Percentage of tasks that completed successfully.">' + INFO_ICON + '</span></span>' +
1407
+ '<span class="task-summary-card__value">' + successRate + '</span>' +
1408
+ '</div>' +
1179
1409
  '</div>';
1180
1410
  }
1181
1411
 
@@ -1205,7 +1435,6 @@ Export
1205
1435
 
1206
1436
  var errorEl = document.getElementById('reliability-error-overview');
1207
1437
  if (errorEl) {
1208
- var tasks = detail.tasks || [];
1209
1438
  var errorCategories = [
1210
1439
  { key: 'failed', label: 'Failed', color: '#ef4444' },
1211
1440
  { key: 'gate-failed', label: 'Quality Check Failed', color: '#f59e0b' },
@@ -1287,8 +1516,8 @@ Export
1287
1516
  if (!section) return;
1288
1517
  section.style.display = '';
1289
1518
 
1290
- var artifactCount = detail.artifact_count != null ? detail.artifact_count : 0;
1291
1519
  var artifacts = detail.artifacts || [];
1520
+ var artifactCount = artifacts.length;
1292
1521
 
1293
1522
  var cardsEl = document.getElementById('outputs-cards');
1294
1523
  if (cardsEl) {
@@ -1331,7 +1560,7 @@ Export
1331
1560
  }
1332
1561
 
1333
1562
  // ── Event Timeline Section ─────────────────────────────
1334
- var eventTimelinePageSize = 50;
1563
+ var eventTimelinePageSize = 20;
1335
1564
  var eventTimelineCurrentPage = 1;
1336
1565
  var eventTimelineActiveFilter = 'all';
1337
1566
  var eventTimelineAllEvents = [];
@@ -1368,7 +1597,10 @@ Export
1368
1597
  if (!section) return;
1369
1598
  section.style.display = '';
1370
1599
 
1371
- eventTimelineAllEvents = detail.events || [];
1600
+ eventTimelineAllEvents = (detail.events || []).slice().sort(function(a, b) {
1601
+ if (!a.created_at || !b.created_at) return 0;
1602
+ return a.created_at < b.created_at ? -1 : a.created_at > b.created_at ? 1 : 0;
1603
+ });
1372
1604
  eventTimelineCurrentPage = 1;
1373
1605
  eventTimelineActiveFilter = 'all';
1374
1606
 
@@ -1454,8 +1686,15 @@ Export
1454
1686
  });
1455
1687
 
1456
1688
  var loadMoreEl = document.getElementById('event-timeline-load-more');
1689
+ var loadMoreBtn = document.getElementById('event-timeline-more-btn');
1457
1690
  if (loadMoreEl) {
1458
- loadMoreEl.style.display = visible.length < filtered.length ? '' : 'none';
1691
+ if (visible.length < filtered.length) {
1692
+ loadMoreEl.style.display = '';
1693
+ var remaining = filtered.length - visible.length;
1694
+ if (loadMoreBtn) loadMoreBtn.textContent = 'Show more (' + remaining + ' remaining)';
1695
+ } else {
1696
+ loadMoreEl.style.display = 'none';
1697
+ }
1459
1698
  }
1460
1699
  }
1461
1700
 
@@ -1505,6 +1744,8 @@ Export
1505
1744
 
1506
1745
  renderOverallStats();
1507
1746
  renderConvoyList();
1747
+ renderActivityTimeline();
1748
+ renderRecentSessions();
1508
1749
 
1509
1750
  var loadMoreBtn = document.getElementById('event-timeline-more-btn');
1510
1751
  if (loadMoreBtn) {
@@ -1519,15 +1760,47 @@ Export
1519
1760
  showHomeView();
1520
1761
  });
1521
1762
 
1522
- document.getElementById('cl-filter-search')?.addEventListener('input', renderConvoyList);
1523
- document.getElementById('cl-filter-status')?.addEventListener('change', renderConvoyList);
1524
- document.getElementById('cl-filter-from')?.addEventListener('change', renderConvoyList);
1525
- document.getElementById('cl-filter-to')?.addEventListener('change', renderConvoyList);
1763
+ document.getElementById('export-btn')?.addEventListener('click', function() {
1764
+ var exportData = {
1765
+ overallStats: window.__DASHBOARD_DATA__ && window.__DASHBOARD_DATA__.overallStats,
1766
+ convoyList: window.__DASHBOARD_DATA__ && window.__DASHBOARD_DATA__.convoyList,
1767
+ };
1768
+ if (window.__SELECTED_CONVOY__) {
1769
+ exportData.selectedConvoy = window.__SELECTED_CONVOY__;
1770
+ }
1771
+ var blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
1772
+ var url = URL.createObjectURL(blob);
1773
+ var a = document.createElement('a');
1774
+ a.href = url;
1775
+ a.download = 'opencastle-dashboard-export.json';
1776
+ document.body.appendChild(a);
1777
+ a.click();
1778
+ document.body.removeChild(a);
1779
+ URL.revokeObjectURL(url);
1780
+ });
1781
+
1782
+ document.getElementById('cl-filter-search')?.addEventListener('input', function() {
1783
+ convoyListCurrentPage = 1;
1784
+ renderConvoyList();
1785
+ });
1786
+ document.getElementById('cl-filter-status')?.addEventListener('change', function() {
1787
+ convoyListCurrentPage = 1;
1788
+ renderConvoyList();
1789
+ });
1790
+ document.getElementById('cl-filter-from')?.addEventListener('change', function() {
1791
+ convoyListCurrentPage = 1;
1792
+ renderConvoyList();
1793
+ });
1794
+ document.getElementById('cl-filter-to')?.addEventListener('change', function() {
1795
+ convoyListCurrentPage = 1;
1796
+ renderConvoyList();
1797
+ });
1526
1798
  document.getElementById('cl-filter-reset')?.addEventListener('click', () => {
1527
1799
  const s = document.getElementById('cl-filter-search'); if (s) s.value = '';
1528
1800
  const st = document.getElementById('cl-filter-status'); if (st) st.value = '';
1529
1801
  const f = document.getElementById('cl-filter-from'); if (f) f.value = '';
1530
1802
  const t = document.getElementById('cl-filter-to'); if (t) t.value = '';
1803
+ convoyListCurrentPage = 1;
1531
1804
  renderConvoyList();
1532
1805
  });
1533
1806