titan-agent 5.1.2 → 5.2.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 (99) hide show
  1. package/README.md +7 -7
  2. package/dist/gateway/metrics.js +20 -1
  3. package/dist/gateway/metrics.js.map +1 -1
  4. package/dist/gateway/server.js +5 -1
  5. package/dist/gateway/server.js.map +1 -1
  6. package/dist/utils/constants.js +1 -1
  7. package/dist/utils/constants.js.map +1 -1
  8. package/package.json +1 -1
  9. package/ui/dist/assets/{AuditPanel-B84Mp16G.js → AuditPanel-DLy0WJQZ.js} +1 -1
  10. package/ui/dist/assets/{AutonomyPanel-DOtiTFxV.js → AutonomyPanel-DjFAQGns.js} +1 -1
  11. package/ui/dist/assets/{AutopilotPanel-nTb1Dnru.js → AutopilotPanel-nBluaHA4.js} +1 -1
  12. package/ui/dist/assets/{AutoresearchPanel-D46mX8VF.js → AutoresearchPanel-BDy8y_Cs.js} +1 -1
  13. package/ui/dist/assets/{BackupPanel-DGM1XXbG.js → BackupPanel-B_Fv2pJA.js} +1 -1
  14. package/ui/dist/assets/{BrowserPanel-Cn1tTN3y.js → BrowserPanel-DOCT3-Rq.js} +1 -1
  15. package/ui/dist/assets/{CPAgents-CEraUkME.js → CPAgents-CpkHf0b8.js} +1 -1
  16. package/ui/dist/assets/{CPDashboard-B_yidGAe.js → CPDashboard-CnMd6qNK.js} +1 -1
  17. package/ui/dist/assets/{CPFiles-BBS8jtYH.js → CPFiles-BDRjJpYl.js} +1 -1
  18. package/ui/dist/assets/{CPGoals-DL5v21TZ.js → CPGoals-2DrwHk62.js} +1 -1
  19. package/ui/dist/assets/{CPInbox-CyLQJBYF.js → CPInbox-C6l2o4FD.js} +1 -1
  20. package/ui/dist/assets/{CPSocial-BkEtQ1Um.js → CPSocial-Cea6NptR.js} +1 -1
  21. package/ui/dist/assets/{ChannelsPanel-CD2kHhA5.js → ChannelsPanel-5EhhyXeg.js} +1 -1
  22. package/ui/dist/assets/{CheckpointsPanel-BrUTFPu_.js → CheckpointsPanel-BVt2oEUe.js} +1 -1
  23. package/ui/dist/assets/{CommandPostHub-BPPaUv1B.js → CommandPostHub-PXKE62DN.js} +3 -3
  24. package/ui/dist/assets/{CronPanel-CsfQctFp.js → CronPanel-lAsoKavq.js} +1 -1
  25. package/ui/dist/assets/{DaemonPanel-CNUggBbL.js → DaemonPanel-xt08Rs10.js} +1 -1
  26. package/ui/dist/assets/{DataTable-DuAEp_QJ.js → DataTable-BHOu7fZP.js} +1 -1
  27. package/ui/dist/assets/{EmptyState-DFrAEZDm.js → EmptyState-Dk7BBthD.js} +1 -1
  28. package/ui/dist/assets/EvalHarnessPanel-CJv8CUDy.js +1 -0
  29. package/ui/dist/assets/{EvalPanel-DEX0a5-b.js → EvalPanel-D9rDf1bk.js} +1 -1
  30. package/ui/dist/assets/{FilesPanel-DATsiAqG.js → FilesPanel-CNrDLmix.js} +1 -1
  31. package/ui/dist/assets/{FleetPanel-QYQKqx4W.js → FleetPanel-DP_ji0AE.js} +1 -1
  32. package/ui/dist/assets/{HomelabPanel-DhuXd3ZD.js → HomelabPanel-B4bCsrBw.js} +1 -1
  33. package/ui/dist/assets/{InfraView-eS7cpESw.js → InfraView-C5OYx_9s.js} +2 -2
  34. package/ui/dist/assets/{InlineEditableField-zIAnW4AR.js → InlineEditableField-DyBCbIoN.js} +1 -1
  35. package/ui/dist/assets/{Input-bFsLI0fq.js → Input-DWnbv1Yh.js} +1 -1
  36. package/ui/dist/assets/{IntegrationsPanel-C_FswSRN.js → IntegrationsPanel-DsB6hjvE.js} +1 -1
  37. package/ui/dist/assets/{IntelligenceView-smQ6aBwx.js → IntelligenceView-PIFGvIg_.js} +2 -2
  38. package/ui/dist/assets/{LearningPanel-BEgF_iND.js → LearningPanel-D_S4HFX5.js} +1 -1
  39. package/ui/dist/assets/{LogsPanel-Br1P8ST6.js → LogsPanel-BnWNREPX.js} +1 -1
  40. package/ui/dist/assets/{McpPanel-ByvQ12J_.js → McpPanel-CIMxZ2Am.js} +1 -1
  41. package/ui/dist/assets/{MemoryGraphPanel-BGOeSaET.js → MemoryGraphPanel-DD7x4rrm.js} +1 -1
  42. package/ui/dist/assets/{MemoryWikiPanel-CR8btd66.js → MemoryWikiPanel-BPPVAH0b.js} +1 -1
  43. package/ui/dist/assets/{MeshPanel-BjkcSOMz.js → MeshPanel-CiuwR3oV.js} +1 -1
  44. package/ui/dist/assets/{NvidiaPanel-NYt42w7L.js → NvidiaPanel-DVntoRrH.js} +1 -1
  45. package/ui/dist/assets/{OrganismPanel-PHvISvVn.js → OrganismPanel-pqIKtHrW.js} +1 -1
  46. package/ui/dist/assets/{OverviewPanel-q35zdMr6.js → OverviewPanel-gqYRhmpF.js} +1 -1
  47. package/ui/dist/assets/{PageHeader-Cwn3OALc.js → PageHeader-CF75km05.js} +1 -1
  48. package/ui/dist/assets/{PaperclipPanel-BDpQki0d.js → PaperclipPanel-CwN5-cKg.js} +1 -1
  49. package/ui/dist/assets/{PersonasPanel-DxrGW5C4.js → PersonasPanel-ClC_TTGX.js} +1 -1
  50. package/ui/dist/assets/{RecipesPanel-CYRdBx5u.js → RecipesPanel-Di2l-eOe.js} +1 -1
  51. package/ui/dist/assets/{SecurityPanel-i1QMctV0.js → SecurityPanel-DjC4pXGM.js} +1 -1
  52. package/ui/dist/assets/{SelfImprovePanel-DbybAZWp.js → SelfImprovePanel-CNpCp5N4.js} +1 -1
  53. package/ui/dist/assets/{SelfProposalsPanel-DtcTUDDd.js → SelfProposalsPanel-BJL6Fjxo.js} +1 -1
  54. package/ui/dist/assets/{SessionsPanel-B7QmOizR.js → SessionsPanel-EAGKDQp0.js} +1 -1
  55. package/ui/dist/assets/{SessionsTab-BdJj_vsI.js → SessionsTab-tc0njI15.js} +1 -1
  56. package/ui/dist/assets/{SettingsPanel-DnEvJUFe.js → SettingsPanel-BdSGImIa.js} +1 -1
  57. package/ui/dist/assets/{SettingsView-C39dk_yr.js → SettingsView-DQB64bjy.js} +2 -2
  58. package/ui/dist/assets/{SkeletonLoader-CsiR8ED9.js → SkeletonLoader-P8SFCyGi.js} +1 -1
  59. package/ui/dist/assets/{SkillsPanel-DM4qBFDS.js → SkillsPanel-lDMl_8da.js} +1 -1
  60. package/ui/dist/assets/{SomaView-CWnPKEQI.js → SomaView-BG7YvBu2.js} +1 -1
  61. package/ui/dist/assets/{StatCard-CY8lgeWm.js → StatCard-Cv2u-yqA.js} +1 -1
  62. package/ui/dist/assets/{StatusBadge-CGvKbP7R.js → StatusBadge-JJeoEdCm.js} +1 -1
  63. package/ui/dist/assets/{TeamsPanel-Bf6GaUni.js → TeamsPanel-D-iCyyYd.js} +1 -1
  64. package/ui/dist/assets/{TelemetryPanel-JZ90gJXC.js → TelemetryPanel-DHNFyCwn.js} +1 -1
  65. package/ui/dist/assets/{TitanCanvas-Hk49NFcA.js → TitanCanvas-BhurNMK3.js} +3 -3
  66. package/ui/dist/assets/ToolsView-C8sWxLny.js +2 -0
  67. package/ui/dist/assets/{Tooltip-CcoZrKsl.js → Tooltip-D4IeQDJL.js} +1 -1
  68. package/ui/dist/assets/{TraceViewer-ojGf0drx.js → TraceViewer-CMd-Wi0z.js} +1 -1
  69. package/ui/dist/assets/{TrainingPanel-CWnP4H2l.js → TrainingPanel-CLtiBq2h.js} +1 -1
  70. package/ui/dist/assets/{VoiceOverlay-Dn6iaYgd.js → VoiceOverlay-BXPVdnJc.js} +1 -1
  71. package/ui/dist/assets/{VramPanel-CLd9Ggck.js → VramPanel-DjuwGUzA.js} +1 -1
  72. package/ui/dist/assets/{WatchView-CQBemwsm.js → WatchView-B7sDnMpl.js} +1 -1
  73. package/ui/dist/assets/{WorkTab-BOfTN-Bd.js → WorkTab-B5nQ4Y7A.js} +1 -1
  74. package/ui/dist/assets/{WorkflowsPanel-qzNS0p0u.js → WorkflowsPanel-2z0TeXyR.js} +1 -1
  75. package/ui/dist/assets/{arrow-left-c-8OFZUV.js → arrow-left-BKOkzkae.js} +1 -1
  76. package/ui/dist/assets/{chart-column-x6L66Qw7.js → chart-column-D39PCk17.js} +1 -1
  77. package/ui/dist/assets/{circle-check-big-WaW3U3Xl.js → circle-check-big-CMz0QouP.js} +1 -1
  78. package/ui/dist/assets/{dollar-sign-D2Oce4Ru.js → dollar-sign-Bu8fZOQl.js} +1 -1
  79. package/ui/dist/assets/{download-YvPDLlFJ.js → download-vvx6zJ-U.js} +1 -1
  80. package/ui/dist/assets/{eye-off-DIMcxsdQ.js → eye-off-BPXFIzlW.js} +1 -1
  81. package/ui/dist/assets/{funnel-DqD9srZu.js → funnel-Bqns-i8I.js} +1 -1
  82. package/ui/dist/assets/{git-branch-0FamUEbU.js → git-branch-CdmeqL8d.js} +1 -1
  83. package/ui/dist/assets/{index-NatBSFxj.js → index-C6oarzis.js} +2 -2
  84. package/ui/dist/assets/index-DsFoD9SP.css +1 -0
  85. package/ui/dist/assets/{legacy-DOO7F5cq.js → legacy-DFIaZTiF.js} +1 -1
  86. package/ui/dist/assets/{lightbulb-Bk6KlR6q.js → lightbulb-DOL6Q-iP.js} +1 -1
  87. package/ui/dist/assets/{pause-DDC_zUiJ.js → pause-B0XymOnS.js} +1 -1
  88. package/ui/dist/assets/{play-BPXbHToG.js → play-Dwp2l5HG.js} +1 -1
  89. package/ui/dist/assets/{plug-Dxp-sWVF.js → plug-DRlTjWqQ.js} +1 -1
  90. package/ui/dist/assets/{proxy-vU7v4NVM.js → proxy-sXxWK7WF.js} +1 -1
  91. package/ui/dist/assets/{square-Bn_0tYME.js → square-yh0jffQZ.js} +1 -1
  92. package/ui/dist/assets/{target-BrtxUtzl.js → target-GxtNG2RW.js} +1 -1
  93. package/ui/dist/assets/{toggle-right-CYphlpN5.js → toggle-right-CYQd_Ux1.js} +1 -1
  94. package/ui/dist/assets/{trash-2-C_Jsp23A.js → trash-2-B4jp_pAQ.js} +1 -1
  95. package/ui/dist/assets/{trending-up-DrtLViSm.js → trending-up-B26tNhFP.js} +1 -1
  96. package/ui/dist/assets/{trophy-DdRzAOfo.js → trophy-Bf3ZeSeb.js} +1 -1
  97. package/ui/dist/index.html +2 -2
  98. package/ui/dist/assets/ToolsView-Cq7Fuq3i.js +0 -2
  99. package/ui/dist/assets/index-D932CbpQ.css +0 -1
package/README.md CHANGED
@@ -178,15 +178,14 @@ curl -fsSL https://raw.githubusercontent.com/Djtony707/TITAN/main/install.sh | b
178
178
  **Or if you like typing:**
179
179
 
180
180
  ```bash
181
- # v5.0 ships on the @next tag for the first week of feedback,
182
- # so the existing 25 k+ v4.x installs aren't auto-upgraded.
183
- npm install -g titan-agent@next
184
- titan onboard # Interactive setup
181
+ # v5.2.x is on @latest as of 2026-04-26. v4.13.x users running
182
+ # `npm update -g titan-agent` will pick it up; new installs get it
183
+ # by default.
184
+ npm install -g titan-agent
185
+ titan onboard # Interactive setup (now asks for telemetry consent)
185
186
  titan gateway # Launch at http://localhost:48420
186
187
  ```
187
188
 
188
- After ~1 week of real-world feedback, `5.0.0` promotes to `@latest`.
189
-
190
189
  **Or Docker:**
191
190
 
192
191
  ```bash
@@ -228,7 +227,8 @@ Start in supervised mode. Review what it does. Don't give it access to systems y
228
227
 
229
228
  ## 📊 The Numbers
230
229
 
231
- - **Version:** 5.0.0 "Spacewalk"
230
+ - **Version:** 5.2.1 "Spacewalk: Trajectory Eval"
231
+ - **Tests:** 481 deterministic unit + integration tests (zero LLM calls), pass in under 5 s
232
232
  - **Widget templates:** 110 across 25 categories
233
233
  - **Skills:** 143 loaded
234
234
  - **Tools:** 248 across all skills
@@ -185,6 +185,20 @@ const titanModelRequestsTotal = new Counter(
185
185
  "titan_model_requests_total",
186
186
  "Total model requests by model and provider"
187
187
  );
188
+ const titanEvalPassRate = new Gauge(
189
+ "titan_eval_pass_rate",
190
+ "Pass rate (0-100) of the most recent eval suite run, labelled by suite"
191
+ );
192
+ const titanEvalCasesTotal = new Counter(
193
+ "titan_eval_cases_total",
194
+ "Total eval cases executed, by suite and outcome"
195
+ );
196
+ function recordEvalSuiteResult(suite, passed, total) {
197
+ const rate = total > 0 ? Math.round(passed / total * 100) : 0;
198
+ titanEvalPassRate.set(rate, { suite });
199
+ titanEvalCasesTotal.increment({ suite, outcome: "passed" }, passed);
200
+ titanEvalCasesTotal.increment({ suite, outcome: "failed" }, Math.max(0, total - passed));
201
+ }
188
202
  const allMetrics = [
189
203
  titanRequestsTotal,
190
204
  titanRequestDuration,
@@ -192,7 +206,9 @@ const allMetrics = [
192
206
  titanErrorsTotal,
193
207
  titanActiveSessions,
194
208
  titanToolCallsTotal,
195
- titanModelRequestsTotal
209
+ titanModelRequestsTotal,
210
+ titanEvalPassRate,
211
+ titanEvalCasesTotal
196
212
  ];
197
213
  function serializePrometheus() {
198
214
  return allMetrics.map((m) => m.serialize()).join("\n\n") + "\n";
@@ -226,9 +242,12 @@ export {
226
242
  Gauge,
227
243
  Histogram,
228
244
  getMetricsSummary,
245
+ recordEvalSuiteResult,
229
246
  serializePrometheus,
230
247
  titanActiveSessions,
231
248
  titanErrorsTotal,
249
+ titanEvalCasesTotal,
250
+ titanEvalPassRate,
232
251
  titanModelRequestsTotal,
233
252
  titanRequestDuration,
234
253
  titanRequestsTotal,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/gateway/metrics.ts"],"sourcesContent":["/**\n * TITAN — Prometheus Metrics Engine\n * Zero-dependency metrics collection with Prometheus text exposition format.\n */\n\n// ── Metric Types ─────────────────────────────────────────────────────\n\ntype Labels = Record<string, string>;\n\nfunction labelsKey(labels?: Labels): string {\n if (!labels || Object.keys(labels).length === 0) return '';\n return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${k}=\"${v}\"`).join(',');\n}\n\nexport class Counter {\n readonly name: string;\n readonly help: string;\n private values = new Map<string, number>();\n\n constructor(name: string, help: string) {\n this.name = name;\n this.help = help;\n }\n\n increment(labels?: Labels, amount = 1): void {\n const key = labelsKey(labels);\n this.values.set(key, (this.values.get(key) || 0) + amount);\n }\n\n get(labels?: Labels): number {\n return this.values.get(labelsKey(labels)) || 0;\n }\n\n serialize(): string {\n const lines: string[] = [\n `# HELP ${this.name} ${this.help}`,\n `# TYPE ${this.name} counter`,\n ];\n if (this.values.size === 0) {\n lines.push(`${this.name} 0`);\n } else {\n for (const [key, val] of this.values) {\n const lbl = key ? `{${key}}` : '';\n lines.push(`${this.name}${lbl} ${val}`);\n }\n }\n return lines.join('\\n');\n }\n\n /** Get all label combinations and their values */\n getAll(): Array<{ labels: Labels; value: number }> {\n const result: Array<{ labels: Labels; value: number }> = [];\n for (const [key, value] of this.values) {\n const labels: Labels = {};\n if (key) {\n for (const pair of key.split(',')) {\n const [k, v] = pair.split('=');\n labels[k] = v.replace(/\"/g, '');\n }\n }\n result.push({ labels, value });\n }\n return result;\n }\n}\n\nconst DEFAULT_BUCKETS = [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];\n\nexport class Histogram {\n readonly name: string;\n readonly help: string;\n readonly buckets: number[];\n // key → { bucketCounts, sum, count }\n private data = new Map<string, { bucketCounts: number[]; sum: number; count: number }>();\n\n constructor(name: string, help: string, buckets = DEFAULT_BUCKETS) {\n this.name = name;\n this.help = help;\n this.buckets = [...buckets].sort((a, b) => a - b);\n }\n\n observe(value: number, labels?: Labels): void {\n const key = labelsKey(labels);\n let entry = this.data.get(key);\n if (!entry) {\n entry = { bucketCounts: new Array(this.buckets.length).fill(0), sum: 0, count: 0 };\n this.data.set(key, entry);\n }\n entry.sum += value;\n entry.count++;\n for (let i = 0; i < this.buckets.length; i++) {\n if (value <= this.buckets[i]) {\n entry.bucketCounts[i]++;\n break;\n }\n }\n }\n\n get(labels?: Labels): { sum: number; count: number; buckets: Record<string, number> } {\n const key = labelsKey(labels);\n const entry = this.data.get(key);\n if (!entry) return { sum: 0, count: 0, buckets: {} };\n const buckets: Record<string, number> = {};\n let cumulative = 0;\n for (let i = 0; i < this.buckets.length; i++) {\n cumulative += entry.bucketCounts[i];\n buckets[String(this.buckets[i])] = cumulative;\n }\n buckets['+Inf'] = entry.count;\n return { sum: entry.sum, count: entry.count, buckets };\n }\n\n serialize(): string {\n const lines: string[] = [\n `# HELP ${this.name} ${this.help}`,\n `# TYPE ${this.name} histogram`,\n ];\n if (this.data.size === 0) {\n // Emit empty histogram\n for (const b of this.buckets) {\n lines.push(`${this.name}_bucket{le=\"${b}\"} 0`);\n }\n lines.push(`${this.name}_bucket{le=\"+Inf\"} 0`);\n lines.push(`${this.name}_sum 0`);\n lines.push(`${this.name}_count 0`);\n } else {\n for (const [key, entry] of this.data) {\n const baseLabels = key ? `${key},` : '';\n let cumulative = 0;\n for (let i = 0; i < this.buckets.length; i++) {\n cumulative += entry.bucketCounts[i];\n lines.push(`${this.name}_bucket{${baseLabels}le=\"${this.buckets[i]}\"} ${cumulative}`);\n }\n lines.push(`${this.name}_bucket{${baseLabels}le=\"+Inf\"} ${entry.count}`);\n const lbl = key ? `{${key}}` : '';\n lines.push(`${this.name}_sum${lbl} ${entry.sum}`);\n lines.push(`${this.name}_count${lbl} ${entry.count}`);\n }\n }\n return lines.join('\\n');\n }\n}\n\nexport class Gauge {\n readonly name: string;\n readonly help: string;\n private values = new Map<string, number>();\n\n constructor(name: string, help: string) {\n this.name = name;\n this.help = help;\n }\n\n set(value: number, labels?: Labels): void {\n this.values.set(labelsKey(labels), value);\n }\n\n inc(labels?: Labels, amount = 1): void {\n const key = labelsKey(labels);\n this.values.set(key, (this.values.get(key) || 0) + amount);\n }\n\n dec(labels?: Labels, amount = 1): void {\n const key = labelsKey(labels);\n this.values.set(key, (this.values.get(key) || 0) - amount);\n }\n\n get(labels?: Labels): number {\n return this.values.get(labelsKey(labels)) || 0;\n }\n\n serialize(): string {\n const lines: string[] = [\n `# HELP ${this.name} ${this.help}`,\n `# TYPE ${this.name} gauge`,\n ];\n if (this.values.size === 0) {\n lines.push(`${this.name} 0`);\n } else {\n for (const [key, val] of this.values) {\n const lbl = key ? `{${key}}` : '';\n lines.push(`${this.name}${lbl} ${val}`);\n }\n }\n return lines.join('\\n');\n }\n}\n\n// ── Pre-defined TITAN Metrics ────────────────────────────────────────\n\nexport const titanRequestsTotal = new Counter(\n 'titan_requests_total',\n 'Total number of requests handled',\n);\n\nexport const titanRequestDuration = new Histogram(\n 'titan_request_duration_seconds',\n 'Request duration in seconds',\n);\n\nexport const titanTokensTotal = new Counter(\n 'titan_tokens_total',\n 'Total tokens consumed',\n);\n\nexport const titanErrorsTotal = new Counter(\n 'titan_errors_total',\n 'Total errors encountered',\n);\n\nexport const titanActiveSessions = new Gauge(\n 'titan_active_sessions',\n 'Number of currently active sessions',\n);\n\nexport const titanToolCallsTotal = new Counter(\n 'titan_tool_calls_total',\n 'Total tool invocations',\n);\n\nexport const titanModelRequestsTotal = new Counter(\n 'titan_model_requests_total',\n 'Total model requests by model and provider',\n);\n\n// ── Registry & Serialization ─────────────────────────────────────────\n\nconst allMetrics = [\n titanRequestsTotal,\n titanRequestDuration,\n titanTokensTotal,\n titanErrorsTotal,\n titanActiveSessions,\n titanToolCallsTotal,\n titanModelRequestsTotal,\n];\n\nexport function serializePrometheus(): string {\n return allMetrics.map(m => m.serialize()).join('\\n\\n') + '\\n';\n}\n\n/** JSON summary for the dashboard telemetry panel */\nexport function getMetricsSummary(): {\n totalRequests: number;\n avgLatencyMs: number;\n topTools: Array<{ tool: string; count: number }>;\n errorRate: number;\n totalErrors: number;\n /**\n * Token counts. The dashboard displays `.total`; `.prompt` + `.completion`\n * are kept for per-bucket breakdowns and Prometheus label parity.\n */\n totalTokens: { prompt: number; completion: number; total: number };\n} {\n // Total requests\n let totalRequests = 0;\n for (const entry of titanRequestsTotal.getAll()) {\n totalRequests += entry.value;\n }\n\n // Average latency\n const duration = titanRequestDuration.get();\n const avgLatencyMs = duration.count > 0 ? (duration.sum / duration.count) * 1000 : 0;\n\n // Top 5 tools by usage\n const toolEntries = titanToolCallsTotal.getAll()\n .map(e => ({ tool: e.labels['tool'] || 'unknown', count: e.value }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 5);\n\n // Error rate\n let totalErrors = 0;\n for (const entry of titanErrorsTotal.getAll()) {\n totalErrors += entry.value;\n }\n const errorRate = totalRequests > 0 ? totalErrors / totalRequests : 0;\n\n // Tokens\n const promptTokens = titanTokensTotal.get({ type: 'prompt' });\n const completionTokens = titanTokensTotal.get({ type: 'completion' });\n\n return {\n totalRequests,\n avgLatencyMs: Math.round(avgLatencyMs * 100) / 100,\n topTools: toolEntries,\n errorRate: Math.round(errorRate * 10000) / 10000,\n totalErrors,\n totalTokens: { prompt: promptTokens, completion: completionTokens, total: promptTokens + completionTokens },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/gateway/metrics.ts"],"sourcesContent":["/**\n * TITAN — Prometheus Metrics Engine\n * Zero-dependency metrics collection with Prometheus text exposition format.\n */\n\n// ── Metric Types ─────────────────────────────────────────────────────\n\ntype Labels = Record<string, string>;\n\nfunction labelsKey(labels?: Labels): string {\n if (!labels || Object.keys(labels).length === 0) return '';\n return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${k}=\"${v}\"`).join(',');\n}\n\nexport class Counter {\n readonly name: string;\n readonly help: string;\n private values = new Map<string, number>();\n\n constructor(name: string, help: string) {\n this.name = name;\n this.help = help;\n }\n\n increment(labels?: Labels, amount = 1): void {\n const key = labelsKey(labels);\n this.values.set(key, (this.values.get(key) || 0) + amount);\n }\n\n get(labels?: Labels): number {\n return this.values.get(labelsKey(labels)) || 0;\n }\n\n serialize(): string {\n const lines: string[] = [\n `# HELP ${this.name} ${this.help}`,\n `# TYPE ${this.name} counter`,\n ];\n if (this.values.size === 0) {\n lines.push(`${this.name} 0`);\n } else {\n for (const [key, val] of this.values) {\n const lbl = key ? `{${key}}` : '';\n lines.push(`${this.name}${lbl} ${val}`);\n }\n }\n return lines.join('\\n');\n }\n\n /** Get all label combinations and their values */\n getAll(): Array<{ labels: Labels; value: number }> {\n const result: Array<{ labels: Labels; value: number }> = [];\n for (const [key, value] of this.values) {\n const labels: Labels = {};\n if (key) {\n for (const pair of key.split(',')) {\n const [k, v] = pair.split('=');\n labels[k] = v.replace(/\"/g, '');\n }\n }\n result.push({ labels, value });\n }\n return result;\n }\n}\n\nconst DEFAULT_BUCKETS = [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];\n\nexport class Histogram {\n readonly name: string;\n readonly help: string;\n readonly buckets: number[];\n // key → { bucketCounts, sum, count }\n private data = new Map<string, { bucketCounts: number[]; sum: number; count: number }>();\n\n constructor(name: string, help: string, buckets = DEFAULT_BUCKETS) {\n this.name = name;\n this.help = help;\n this.buckets = [...buckets].sort((a, b) => a - b);\n }\n\n observe(value: number, labels?: Labels): void {\n const key = labelsKey(labels);\n let entry = this.data.get(key);\n if (!entry) {\n entry = { bucketCounts: new Array(this.buckets.length).fill(0), sum: 0, count: 0 };\n this.data.set(key, entry);\n }\n entry.sum += value;\n entry.count++;\n for (let i = 0; i < this.buckets.length; i++) {\n if (value <= this.buckets[i]) {\n entry.bucketCounts[i]++;\n break;\n }\n }\n }\n\n get(labels?: Labels): { sum: number; count: number; buckets: Record<string, number> } {\n const key = labelsKey(labels);\n const entry = this.data.get(key);\n if (!entry) return { sum: 0, count: 0, buckets: {} };\n const buckets: Record<string, number> = {};\n let cumulative = 0;\n for (let i = 0; i < this.buckets.length; i++) {\n cumulative += entry.bucketCounts[i];\n buckets[String(this.buckets[i])] = cumulative;\n }\n buckets['+Inf'] = entry.count;\n return { sum: entry.sum, count: entry.count, buckets };\n }\n\n serialize(): string {\n const lines: string[] = [\n `# HELP ${this.name} ${this.help}`,\n `# TYPE ${this.name} histogram`,\n ];\n if (this.data.size === 0) {\n // Emit empty histogram\n for (const b of this.buckets) {\n lines.push(`${this.name}_bucket{le=\"${b}\"} 0`);\n }\n lines.push(`${this.name}_bucket{le=\"+Inf\"} 0`);\n lines.push(`${this.name}_sum 0`);\n lines.push(`${this.name}_count 0`);\n } else {\n for (const [key, entry] of this.data) {\n const baseLabels = key ? `${key},` : '';\n let cumulative = 0;\n for (let i = 0; i < this.buckets.length; i++) {\n cumulative += entry.bucketCounts[i];\n lines.push(`${this.name}_bucket{${baseLabels}le=\"${this.buckets[i]}\"} ${cumulative}`);\n }\n lines.push(`${this.name}_bucket{${baseLabels}le=\"+Inf\"} ${entry.count}`);\n const lbl = key ? `{${key}}` : '';\n lines.push(`${this.name}_sum${lbl} ${entry.sum}`);\n lines.push(`${this.name}_count${lbl} ${entry.count}`);\n }\n }\n return lines.join('\\n');\n }\n}\n\nexport class Gauge {\n readonly name: string;\n readonly help: string;\n private values = new Map<string, number>();\n\n constructor(name: string, help: string) {\n this.name = name;\n this.help = help;\n }\n\n set(value: number, labels?: Labels): void {\n this.values.set(labelsKey(labels), value);\n }\n\n inc(labels?: Labels, amount = 1): void {\n const key = labelsKey(labels);\n this.values.set(key, (this.values.get(key) || 0) + amount);\n }\n\n dec(labels?: Labels, amount = 1): void {\n const key = labelsKey(labels);\n this.values.set(key, (this.values.get(key) || 0) - amount);\n }\n\n get(labels?: Labels): number {\n return this.values.get(labelsKey(labels)) || 0;\n }\n\n serialize(): string {\n const lines: string[] = [\n `# HELP ${this.name} ${this.help}`,\n `# TYPE ${this.name} gauge`,\n ];\n if (this.values.size === 0) {\n lines.push(`${this.name} 0`);\n } else {\n for (const [key, val] of this.values) {\n const lbl = key ? `{${key}}` : '';\n lines.push(`${this.name}${lbl} ${val}`);\n }\n }\n return lines.join('\\n');\n }\n}\n\n// ── Pre-defined TITAN Metrics ────────────────────────────────────────\n\nexport const titanRequestsTotal = new Counter(\n 'titan_requests_total',\n 'Total number of requests handled',\n);\n\nexport const titanRequestDuration = new Histogram(\n 'titan_request_duration_seconds',\n 'Request duration in seconds',\n);\n\nexport const titanTokensTotal = new Counter(\n 'titan_tokens_total',\n 'Total tokens consumed',\n);\n\nexport const titanErrorsTotal = new Counter(\n 'titan_errors_total',\n 'Total errors encountered',\n);\n\nexport const titanActiveSessions = new Gauge(\n 'titan_active_sessions',\n 'Number of currently active sessions',\n);\n\nexport const titanToolCallsTotal = new Counter(\n 'titan_tool_calls_total',\n 'Total tool invocations',\n);\n\nexport const titanModelRequestsTotal = new Counter(\n 'titan_model_requests_total',\n 'Total model requests by model and provider',\n);\n\n/**\n * Eval-suite pass rate, 0–100, labelled by suite name. Updated by the\n * /api/eval/run endpoint after each run completes. Lets ops graph\n * regressions over time and alert when a suite drops below threshold.\n *\n * Use the helper `recordEvalSuiteResult(suite, passed, total)` instead of\n * touching this gauge directly so the rate calc + zero-total guard stay\n * in one place.\n */\nexport const titanEvalPassRate = new Gauge(\n 'titan_eval_pass_rate',\n 'Pass rate (0-100) of the most recent eval suite run, labelled by suite',\n);\n\n/** Total eval cases executed, labelled by suite. Counter so a graph\n * shows whether the suite is actually being exercised. */\nexport const titanEvalCasesTotal = new Counter(\n 'titan_eval_cases_total',\n 'Total eval cases executed, by suite and outcome',\n);\n\n/**\n * Record the outcome of an eval suite run on the metrics gauges + counter.\n * Safe to call with `total=0` (gauge stays at 0, no divide-by-zero).\n */\nexport function recordEvalSuiteResult(suite: string, passed: number, total: number): void {\n const rate = total > 0 ? Math.round((passed / total) * 100) : 0;\n titanEvalPassRate.set(rate, { suite });\n titanEvalCasesTotal.increment({ suite, outcome: 'passed' }, passed);\n titanEvalCasesTotal.increment({ suite, outcome: 'failed' }, Math.max(0, total - passed));\n}\n\n// ── Registry & Serialization ─────────────────────────────────────────\n\nconst allMetrics = [\n titanRequestsTotal,\n titanRequestDuration,\n titanTokensTotal,\n titanErrorsTotal,\n titanActiveSessions,\n titanToolCallsTotal,\n titanModelRequestsTotal,\n titanEvalPassRate,\n titanEvalCasesTotal,\n];\n\nexport function serializePrometheus(): string {\n return allMetrics.map(m => m.serialize()).join('\\n\\n') + '\\n';\n}\n\n/** JSON summary for the dashboard telemetry panel */\nexport function getMetricsSummary(): {\n totalRequests: number;\n avgLatencyMs: number;\n topTools: Array<{ tool: string; count: number }>;\n errorRate: number;\n totalErrors: number;\n /**\n * Token counts. The dashboard displays `.total`; `.prompt` + `.completion`\n * are kept for per-bucket breakdowns and Prometheus label parity.\n */\n totalTokens: { prompt: number; completion: number; total: number };\n} {\n // Total requests\n let totalRequests = 0;\n for (const entry of titanRequestsTotal.getAll()) {\n totalRequests += entry.value;\n }\n\n // Average latency\n const duration = titanRequestDuration.get();\n const avgLatencyMs = duration.count > 0 ? (duration.sum / duration.count) * 1000 : 0;\n\n // Top 5 tools by usage\n const toolEntries = titanToolCallsTotal.getAll()\n .map(e => ({ tool: e.labels['tool'] || 'unknown', count: e.value }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 5);\n\n // Error rate\n let totalErrors = 0;\n for (const entry of titanErrorsTotal.getAll()) {\n totalErrors += entry.value;\n }\n const errorRate = totalRequests > 0 ? totalErrors / totalRequests : 0;\n\n // Tokens\n const promptTokens = titanTokensTotal.get({ type: 'prompt' });\n const completionTokens = titanTokensTotal.get({ type: 'completion' });\n\n return {\n totalRequests,\n avgLatencyMs: Math.round(avgLatencyMs * 100) / 100,\n topTools: toolEntries,\n errorRate: Math.round(errorRate * 10000) / 10000,\n totalErrors,\n totalTokens: { prompt: promptTokens, completion: completionTokens, total: promptTokens + completionTokens },\n };\n}\n"],"mappings":"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","names":[]}
@@ -45,7 +45,7 @@ import { TITAN_VERSION, TITAN_NAME, TITAN_LOGS_DIR, TITAN_HOME } from "../utils/
45
45
  import { collectSystemProfile, recordStartupAnalytics, startHeartbeatAnalytics } from "../analytics/collector.js";
46
46
  import { getUpdateInfo } from "../utils/updater.js";
47
47
  import { getMissionControlHTML } from "./dashboard.js";
48
- import { serializePrometheus, getMetricsSummary, titanRequestsTotal, titanRequestDuration, titanErrorsTotal, titanActiveSessions, titanToolCallsTotal, titanTokensTotal, titanModelRequestsTotal } from "./metrics.js";
48
+ import { serializePrometheus, getMetricsSummary, titanRequestsTotal, titanRequestDuration, titanErrorsTotal, titanActiveSessions, titanToolCallsTotal, titanTokensTotal, titanModelRequestsTotal, recordEvalSuiteResult } from "./metrics.js";
49
49
  import { initSlashCommands, handleSlashCommand } from "./slashCommands.js";
50
50
  import { initMcpServers, listMcpServers, addMcpServer, removeMcpServer, setMcpServerEnabled, getMcpStatus, BUILTIN_PRESETS } from "../mcp/registry.js";
51
51
  import { connectMcpServer, testMcpServer } from "../mcp/client.js";
@@ -2354,6 +2354,10 @@ _____widget
2354
2354
  return;
2355
2355
  }
2356
2356
  const result = await runEvalSuite(suite, cases, agentCall);
2357
+ try {
2358
+ recordEvalSuiteResult(suite, result.passed, result.total);
2359
+ } catch {
2360
+ }
2357
2361
  res.json(result);
2358
2362
  } catch (e) {
2359
2363
  logger.error(COMPONENT, `Endpoint error: ${e.message}`);