titan-agent 6.1.0 → 6.2.0

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 (170) hide show
  1. package/README.md +25 -5
  2. package/dist/config/schema.js +23 -0
  3. package/dist/config/schema.js.map +1 -1
  4. package/dist/eval/harness.js +2 -1
  5. package/dist/eval/harness.js.map +1 -1
  6. package/dist/gateway/routes/telephony.js +497 -0
  7. package/dist/gateway/routes/telephony.js.map +1 -0
  8. package/dist/gateway/routes/tests.js +3 -1
  9. package/dist/gateway/routes/tests.js.map +1 -1
  10. package/dist/gateway/server.js +6 -0
  11. package/dist/gateway/server.js.map +1 -1
  12. package/dist/skills/builtin/phone_call.js +66 -0
  13. package/dist/skills/builtin/phone_call.js.map +1 -0
  14. package/dist/skills/registry.js +2 -0
  15. package/dist/skills/registry.js.map +1 -1
  16. package/dist/telephony/dograhClient.js +175 -0
  17. package/dist/telephony/dograhClient.js.map +1 -0
  18. package/dist/telephony/optOutStore.js +71 -0
  19. package/dist/telephony/optOutStore.js.map +1 -0
  20. package/dist/telephony/phonePolicy.js +120 -0
  21. package/dist/telephony/phonePolicy.js.map +1 -0
  22. package/dist/utils/constants.js +1 -1
  23. package/dist/utils/constants.js.map +1 -1
  24. package/dist/utils/safety.js +12 -1
  25. package/dist/utils/safety.js.map +1 -1
  26. package/package.json +1 -1
  27. package/ui/dist/assets/AuditPanel-DuxwAH5U.js +1 -0
  28. package/ui/dist/assets/{AutonomyPanel-zsQ6kIP7.js → AutonomyPanel-VWBgurnv.js} +1 -1
  29. package/ui/dist/assets/{AutopilotPanel-NlxdRleV.js → AutopilotPanel-Dw8MYOtj.js} +1 -1
  30. package/ui/dist/assets/{AutoresearchPanel-CHeEcUmN.js → AutoresearchPanel-CpEX0Zub.js} +1 -1
  31. package/ui/dist/assets/{BackupPanel-C7RHr9Gu.js → BackupPanel-n41_6i6x.js} +1 -1
  32. package/ui/dist/assets/{BrowserPanel-Cxu9bCLW.js → BrowserPanel-DEBTIt6h.js} +1 -1
  33. package/ui/dist/assets/{CPActivity-B0QSTuSq.js → CPActivity-em82PTiQ.js} +1 -1
  34. package/ui/dist/assets/{CPAgentDetail-BUqGIkaM.js → CPAgentDetail-Bd8lKqcL.js} +1 -1
  35. package/ui/dist/assets/{CPAgents-DO0RVLLA.js → CPAgents-GRBrDd6c.js} +1 -1
  36. package/ui/dist/assets/{CPApprovals-DRceMgME.js → CPApprovals-DdLaCgJZ.js} +1 -1
  37. package/ui/dist/assets/{CPCosts-D9l4mkiW.js → CPCosts-Cm8Hv9fv.js} +1 -1
  38. package/ui/dist/assets/{CPDashboard-DaCCdMjs.js → CPDashboard-Dm9vUKLu.js} +1 -1
  39. package/ui/dist/assets/{CPFiles-DTphWHsR.js → CPFiles-DxMn1x2m.js} +1 -1
  40. package/ui/dist/assets/{CPGoals-DeJBRV3M.js → CPGoals-Wr1tCn6y.js} +1 -1
  41. package/ui/dist/assets/{CPInbox-DsT5O636.js → CPInbox-CGYpK1rG.js} +2 -2
  42. package/ui/dist/assets/{CPIssueDetail-D8pG5Tnq.js → CPIssueDetail-dTU59sgQ.js} +1 -1
  43. package/ui/dist/assets/{CPIssues-oeffaZSb.js → CPIssues-wgjI2eqS.js} +1 -1
  44. package/ui/dist/assets/CPLayout-CBtGEYSs.js +2 -0
  45. package/ui/dist/assets/{CPOrg-CesGXL8r.js → CPOrg-eT6LNmyy.js} +1 -1
  46. package/ui/dist/assets/{CPRuns-CfYgZUGv.js → CPRuns-B3sdo5g2.js} +1 -1
  47. package/ui/dist/assets/{CPSocial-CK0qGSa0.js → CPSocial-B99Mfee_.js} +3 -3
  48. package/ui/dist/assets/{CPVoice-iIkC-KnQ.js → CPVoice-D4J6yFWu.js} +1 -1
  49. package/ui/dist/assets/{ChannelsPanel-DTS5SXlC.js → ChannelsPanel--lhZqYLD.js} +1 -1
  50. package/ui/dist/assets/{CheckpointsPanel-c5dwo2eW.js → CheckpointsPanel-DaOIUcEi.js} +1 -1
  51. package/ui/dist/assets/{CommandPostHub-BsdfjoBN.js → CommandPostHub-Pq9lQMW5.js} +3 -3
  52. package/ui/dist/assets/{CronPanel-BkeP_mIS.js → CronPanel-CywjWM7O.js} +1 -1
  53. package/ui/dist/assets/{DataTable-Gajb24Ni.js → DataTable-Bt0cxehB.js} +1 -1
  54. package/ui/dist/assets/{DreamPanel-CGrozxjo.js → DreamPanel-Ct7MuLzD.js} +1 -1
  55. package/ui/dist/assets/{EmptyState-B3lWjJU-.js → EmptyState-iLOrbdU7.js} +1 -1
  56. package/ui/dist/assets/{EvalHarnessPanel-DQ4O9caO.js → EvalHarnessPanel-7KKDzeXw.js} +2 -2
  57. package/ui/dist/assets/{EvalPanel-BOUHXllR.js → EvalPanel-70LzkLDO.js} +1 -1
  58. package/ui/dist/assets/{FileViewer-00_4kM0_.js → FileViewer-h6A7YYTm.js} +1 -1
  59. package/ui/dist/assets/{FilesPanel-DISpHxEJ.js → FilesPanel-l8qC6eLD.js} +1 -1
  60. package/ui/dist/assets/{FleetPanel-Vz7_88zK.js → FleetPanel-Z3ResHIb.js} +1 -1
  61. package/ui/dist/assets/{HomelabPanel-CCqGyERB.js → HomelabPanel-9QhrXzo1.js} +1 -1
  62. package/ui/dist/assets/InfraView-G1q6vF0Q.js +2 -0
  63. package/ui/dist/assets/{InlineEditableField-BGjdtpGQ.js → InlineEditableField-DsVfGRH2.js} +1 -1
  64. package/ui/dist/assets/{Input-CzDjGc5Y.js → Input-Bzh0-cXU.js} +1 -1
  65. package/ui/dist/assets/{IntegrationsPanel-BOe9ybbQ.js → IntegrationsPanel-DBOdLYJ8.js} +1 -1
  66. package/ui/dist/assets/IntelligenceView-J8Nbh4zs.js +2 -0
  67. package/ui/dist/assets/{LearningPanel-5zb6yYZW.js → LearningPanel-CiSLVxUX.js} +1 -1
  68. package/ui/dist/assets/{LogsPanel-C_1bqy9b.js → LogsPanel-CPAAItsq.js} +1 -1
  69. package/ui/dist/assets/{McpPanel-CY7_7erZ.js → McpPanel-MYWE2Zqs.js} +1 -1
  70. package/ui/dist/assets/{MemoryGraphPanel-DsF8er1w.js → MemoryGraphPanel-BFr51D_1.js} +1 -1
  71. package/ui/dist/assets/{MemoryWikiPanel-D4KPpOX7.js → MemoryWikiPanel-BA7GUxhg.js} +1 -1
  72. package/ui/dist/assets/{MeshPanel-C_cNMUcI.js → MeshPanel-5BDbwBVR.js} +1 -1
  73. package/ui/dist/assets/{MissionCanvas-BMw5bJcw.js → MissionCanvas-CrDZIQX6.js} +1 -1
  74. package/ui/dist/assets/{MissionChat-CrgPUYhU.js → MissionChat-BDTWMQjj.js} +1 -1
  75. package/ui/dist/assets/{MissionLibrary-ASkOLTWA.js → MissionLibrary-DEPH2eEu.js} +1 -1
  76. package/ui/dist/assets/{MissionStart-DDoZMkie.js → MissionStart-COLFZLHB.js} +1 -1
  77. package/ui/dist/assets/{Modal-DTzLKnUQ.js → Modal-nvni-LGR.js} +1 -1
  78. package/ui/dist/assets/{NvidiaPanel-dtkQr6gS.js → NvidiaPanel-BCgVrdWt.js} +1 -1
  79. package/ui/dist/assets/{OrganismPanel-DLfHCoYh.js → OrganismPanel-Cab4MV9i.js} +1 -1
  80. package/ui/dist/assets/{OverviewPanel-COcMqu-y.js → OverviewPanel-isNUxBki.js} +1 -1
  81. package/ui/dist/assets/{PageHeader-BXEGoqDX.js → PageHeader-BJ1jHtOy.js} +1 -1
  82. package/ui/dist/assets/{PersonaProfilesPanel-BKWsHE1j.js → PersonaProfilesPanel-BGiDuDPj.js} +1 -1
  83. package/ui/dist/assets/{PersonasPanel-tsXvN_8B.js → PersonasPanel-4-USk8fm.js} +1 -1
  84. package/ui/dist/assets/PhoneDeskPanel-CUz5YVUY.js +11 -0
  85. package/ui/dist/assets/{RecipesPanel-B8GOxD4_.js → RecipesPanel-DJd6Otdy.js} +1 -1
  86. package/ui/dist/assets/{SecurityPanel-CqhRhXZ8.js → SecurityPanel-CKqTnhNY.js} +1 -1
  87. package/ui/dist/assets/{SelfImprovePanel-Di0XhdJJ.js → SelfImprovePanel-BiCjKpTM.js} +1 -1
  88. package/ui/dist/assets/{SelfProposalsPanel-CbgdHeQY.js → SelfProposalsPanel-BQCwEHdF.js} +1 -1
  89. package/ui/dist/assets/{SessionsPanel-DBdDx51G.js → SessionsPanel-MTMSODP7.js} +1 -1
  90. package/ui/dist/assets/{SessionsTab-C8cEt83E.js → SessionsTab-CEFFZWMK.js} +1 -1
  91. package/ui/dist/assets/{SettingsPanel-BAenxGtq.js → SettingsPanel-CniESkQQ.js} +1 -1
  92. package/ui/dist/assets/SettingsView-2AwXqlvc.js +2 -0
  93. package/ui/dist/assets/{SkeletonLoader-DGQ0Q7vy.js → SkeletonLoader-IpWkLmoy.js} +1 -1
  94. package/ui/dist/assets/{SkillsPanel-r91e0ype.js → SkillsPanel-CCP-gCR4.js} +1 -1
  95. package/ui/dist/assets/{SomaView-CGoJDjEX.js → SomaView-BK_thK9H.js} +1 -1
  96. package/ui/dist/assets/{StatCard-D0Q7T1EQ.js → StatCard-I0fVw2CH.js} +1 -1
  97. package/ui/dist/assets/{StatusBadge-Do8GEljT.js → StatusBadge-e3tFf05B.js} +1 -1
  98. package/ui/dist/assets/{Tabs-B-g5Ghos.js → Tabs-vGpPiBzr.js} +1 -1
  99. package/ui/dist/assets/{TeamsPanel-BrT1eigj.js → TeamsPanel-AFIQBzRK.js} +1 -1
  100. package/ui/dist/assets/{TelemetryPanel-C9vV_ydx.js → TelemetryPanel-DKZGVmcp.js} +1 -1
  101. package/ui/dist/assets/{TitanCanvas-BPVdmdYo.js → TitanCanvas-CqvuuIjd.js} +4 -4
  102. package/ui/dist/assets/{ToolsView-COEuSnLh.js → ToolsView-DnIkrUvP.js} +2 -2
  103. package/ui/dist/assets/{Tooltip-Ip86jr39.js → Tooltip-DNc5AWi0.js} +1 -1
  104. package/ui/dist/assets/{TraceViewer-BcK-DNNV.js → TraceViewer-MD9YRFVc.js} +1 -1
  105. package/ui/dist/assets/{TrainingPanel-s5yKr9vs.js → TrainingPanel-BfXoR7_N.js} +1 -1
  106. package/ui/dist/assets/{VoiceOverlay-BKtgGRK8.js → VoiceOverlay-Bl81c4yf.js} +1 -1
  107. package/ui/dist/assets/{VramPanel-C2vulpkc.js → VramPanel-Ba8H8Qs2.js} +1 -1
  108. package/ui/dist/assets/{WatchView-GXZc0v1H.js → WatchView-CoLHPGd7.js} +1 -1
  109. package/ui/dist/assets/{WorkTab-CCQpoRi6.js → WorkTab-UppfR_AF.js} +1 -1
  110. package/ui/dist/assets/{WorkflowsPanel-CjU5wOPV.js → WorkflowsPanel-CdN7Epxr.js} +1 -1
  111. package/ui/dist/assets/{archive-Cty0CkR8.js → archive-DWgNklp0.js} +1 -1
  112. package/ui/dist/assets/{arrow-left-pfqo1Fw_.js → arrow-left-CW5NV7E0.js} +1 -1
  113. package/ui/dist/assets/{book-open-B6YYAKPS.js → book-open-DRI_Y6cW.js} +1 -1
  114. package/ui/dist/assets/{bot-CF5xQRRg.js → bot-DSGZRM5O.js} +1 -1
  115. package/ui/dist/assets/{chart-column-DmdEaxIF.js → chart-column-D6yK9tts.js} +1 -1
  116. package/ui/dist/assets/{check-B06kj6lQ.js → check-Cxp-6q4k.js} +1 -1
  117. package/ui/dist/assets/{chevron-right-BANsqvxz.js → chevron-right-DkIMxdwH.js} +1 -1
  118. package/ui/dist/assets/{chevron-up-DZTojCej.js → chevron-up-BbDu8x1d.js} +1 -1
  119. package/ui/dist/assets/{circle-check-big-2NAZFdp9.js → circle-check-big-DnJPBAGU.js} +1 -1
  120. package/ui/dist/assets/{circle-stop-AU_Yx5I3.js → circle-stop-DOe0OacY.js} +1 -1
  121. package/ui/dist/assets/clipboard-list-Dj5RQbT8.js +6 -0
  122. package/ui/dist/assets/{clock-DXDS0v3F.js → clock-BS8fh7Rt.js} +1 -1
  123. package/ui/dist/assets/{dollar-sign-DLLEescF.js → dollar-sign-DMXBeaTj.js} +1 -1
  124. package/ui/dist/assets/{download-CsU2Repn.js → download-BykARpbG.js} +1 -1
  125. package/ui/dist/assets/{external-link-Chx9uvWw.js → external-link-BfXhMi9L.js} +1 -1
  126. package/ui/dist/assets/{file-text-RZVxniN-.js → file-text-CUkSytWL.js} +1 -1
  127. package/ui/dist/assets/{flask-conical-BMH8pJKb.js → flask-conical-BS4cuENu.js} +1 -1
  128. package/ui/dist/assets/{folder-DgwmNgPQ.js → folder-BDwoUobN.js} +1 -1
  129. package/ui/dist/assets/{funnel-BR7hTVZW.js → funnel-xr6Dd1ub.js} +1 -1
  130. package/ui/dist/assets/{git-branch-B5R0dCYG.js → git-branch-C0Qk52W8.js} +1 -1
  131. package/ui/dist/assets/{index-BUCi9exS.js → index-CmaGnHzR.js} +30 -30
  132. package/ui/dist/assets/index-OZm1iJy9.css +1 -0
  133. package/ui/dist/assets/{layers-DGEsxpYY.js → layers-DPHYdeRk.js} +1 -1
  134. package/ui/dist/assets/{legacy-DgcX067d.js → legacy-DzagVk5C.js} +1 -1
  135. package/ui/dist/assets/{lightbulb-BvNv5S2z.js → lightbulb-Bj6cj6Xl.js} +1 -1
  136. package/ui/dist/assets/{list-todo-BYS3IIni.js → list-todo-Cm4jNFHV.js} +1 -1
  137. package/ui/dist/assets/{message-square-I2Wa53bg.js → message-square-BrWLAovN.js} +1 -1
  138. package/ui/dist/assets/{mic-CLF69wwf.js → mic-BXNK4d7X.js} +1 -1
  139. package/ui/dist/assets/{missions-a23pAr7Z.js → missions-DEjMYSre.js} +1 -1
  140. package/ui/dist/assets/{network-DPPIz6Io.js → network-CrQn1GKv.js} +1 -1
  141. package/ui/dist/assets/{pause-4M6u54u8.js → pause-DS5xgDMT.js} +1 -1
  142. package/ui/dist/assets/{pencil-CmthDu_c.js → pencil-DAI6FtFQ.js} +1 -1
  143. package/ui/dist/assets/{play-C5er2jT3.js → play-DxBy837b.js} +1 -1
  144. package/ui/dist/assets/{plug-Cj409xHk.js → plug-D0Bstfm0.js} +1 -1
  145. package/ui/dist/assets/{plus-C63FIH_4.js → plus-DxgNIt8h.js} +1 -1
  146. package/ui/dist/assets/{proxy-CV0RFkNZ.js → proxy-KYomRnLW.js} +1 -1
  147. package/ui/dist/assets/{radio-DJcw-8Fl.js → radio-2vniRFTO.js} +1 -1
  148. package/ui/dist/assets/{refresh-cw-D7RrQJQl.js → refresh-cw-BFBnnWj8.js} +1 -1
  149. package/ui/dist/assets/{rotate-ccw-wDOKy6Sz.js → rotate-ccw-B9wWlKRc.js} +1 -1
  150. package/ui/dist/assets/{save-Cy8s5sjb.js → save--wCYre_u.js} +1 -1
  151. package/ui/dist/assets/{search-BXNHN7Ra.js → search-DOD1aOm8.js} +1 -1
  152. package/ui/dist/assets/{send-DU7Jy0ph.js → send-Bb57UxT7.js} +1 -1
  153. package/ui/dist/assets/{server-Cg6QHqG3.js → server-CBsCfofY.js} +1 -1
  154. package/ui/dist/assets/{shield-check-nn5B33Zk.js → shield-check-B24N03VU.js} +1 -1
  155. package/ui/dist/assets/{terminal-D3qHXhYo.js → terminal-CZ77Swtc.js} +1 -1
  156. package/ui/dist/assets/{test-tube-qD4BUs0u.js → test-tube-CkkvIu5j.js} +1 -1
  157. package/ui/dist/assets/{toggle-right-B8VL5BEg.js → toggle-right-DgphAedS.js} +1 -1
  158. package/ui/dist/assets/{trash-2-BscnSMjc.js → trash-2-C5KNXS-O.js} +1 -1
  159. package/ui/dist/assets/{trending-up-CusGuaj_.js → trending-up-B3H8xrGD.js} +1 -1
  160. package/ui/dist/assets/{trophy-SEdQm3z6.js → trophy-C-xSgHD2.js} +1 -1
  161. package/ui/dist/assets/{volume-2-lPPI8coL.js → volume-2-BXwRjeve.js} +1 -1
  162. package/ui/dist/assets/{wifi-off-Deo-Hgei.js → wifi-off-DOm__Zn6.js} +1 -1
  163. package/ui/dist/index.html +2 -2
  164. package/ui/dist/sw.js +1 -1
  165. package/ui/dist/assets/AuditPanel-Dln-nzxR.js +0 -6
  166. package/ui/dist/assets/CPLayout-DVvrBIqI.js +0 -2
  167. package/ui/dist/assets/InfraView-KVwWRhIp.js +0 -2
  168. package/ui/dist/assets/IntelligenceView-CGUmFDYY.js +0 -2
  169. package/ui/dist/assets/SettingsView-DqklKAud.js +0 -2
  170. package/ui/dist/assets/index-CUxC9gxc.css +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/gateway/routes/telephony.ts"],"sourcesContent":["import { Router, type Request } from 'express';\nimport { loadConfig as loadTitanConfig } from '../../config/config.js';\nimport type { TitanConfig } from '../../config/schema.js';\nimport { readReceipts, writeReceipt } from '../../receipts/store.js';\nimport { createApproval, listApprovals } from '../../agent/commandPost.js';\nimport {\n createDograhClientFromConfig,\n summarizeDograhConfig,\n type DograhOutboundCallRequest,\n type DograhOutboundCallResult,\n type DograhWorkflowSummary,\n} from '../../telephony/dograhClient.js';\nimport { hashPhoneForApproval, normalizePhoneNumber, redactPhoneNumber, validateOutboundCallRequest } from '../../telephony/phonePolicy.js';\nimport { isTelephonyOptedOut, listTelephonyOptOuts, recordTelephonyOptOut } from '../../telephony/optOutStore.js';\n\ninterface DograhRouteClient {\n health: () => Promise<unknown>;\n listWorkflowSummaries: (status?: string) => Promise<DograhWorkflowSummary[]>;\n initiateOutboundCall: (call: DograhOutboundCallRequest) => Promise<DograhOutboundCallResult>;\n}\n\nexport interface TelephonyRouterDeps {\n loadConfig?: () => TitanConfig;\n createClient?: (config: TitanConfig['telephony']) => DograhRouteClient;\n}\n\nfunction safeString(value: unknown, max = 160): string | undefined {\n if (typeof value !== 'string') return undefined;\n return value.replace(/[\\r\\n\\t]+/g, ' ').slice(0, max).trim() || undefined;\n}\n\nfunction safeBool(value: unknown): boolean {\n return value === true || value === 'true';\n}\n\nfunction outboundResponseBody(policy: ReturnType<typeof validateOutboundCallRequest>) {\n return {\n toNumberRedacted: policy.toNumberRedacted,\n workflowId: policy.workflowId,\n purpose: policy.purpose,\n mode: policy.mode,\n requiresApproval: policy.requiresApproval,\n };\n}\n\nfunction safeActionId(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim();\n return /^[A-Za-z0-9_-]{8,80}$/.test(trimmed) ? trimmed : undefined;\n}\n\nfunction metaString(meta: Record<string, unknown> | undefined, key: string): string | undefined {\n const value = meta?.[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction verifyPriorApproval(approvalActionId: string | undefined, policy: ReturnType<typeof validateOutboundCallRequest>):\n | { ok: true }\n | { ok: false; status: number; error: string; message: string } {\n if (!approvalActionId) {\n return { ok: false, status: 403, error: 'telephony_approval_required', message: 'A matching approved Command Post approval is required before placing this outbound call.' };\n }\n const approval = listApprovals().find((item) => item.id === approvalActionId && item.type === 'custom');\n if (!approval || approval.payload?.category !== 'telephony_outbound_call') {\n return { ok: false, status: 403, error: 'telephony_approval_required', message: 'No matching Command Post approval was found for this outbound call.' };\n }\n if (approval.status !== 'approved') {\n return { ok: false, status: 403, error: 'telephony_approval_not_approved', message: 'The supplied outbound call approval has not been approved yet.' };\n }\n const consumed = readReceipts({ kind: 'approval_decision', limit: 1000 }).some((receipt) => receipt.parent_action_id === approvalActionId && receipt.status === 'ok');\n if (consumed) {\n return { ok: false, status: 409, error: 'telephony_approval_already_used', message: 'The supplied approval request was already used.' };\n }\n const payload = approval.payload ?? {};\n const expected: Record<string, string | undefined> = {\n mode: policy.mode,\n workflowId: policy.workflowId,\n toNumberRedacted: policy.toNumberRedacted,\n toNumberHash: policy.toNumberHash,\n toNumberOptOutHash: policy.toNumberOptOutHash,\n purpose: policy.purpose,\n };\n for (const [key, value] of Object.entries(expected)) {\n const actual = payload[key];\n if (value !== undefined && actual !== value) {\n return { ok: false, status: 409, error: 'telephony_approval_mismatch', message: 'The supplied approval request does not match this outbound call.' };\n }\n }\n return { ok: true };\n}\n\nconst campaignReservations = new Map<string, number>();\n\nfunction purgeOldCampaignReservations(now = Date.now()): void {\n const cutoff = now - 60 * 60 * 1000;\n for (const [id, ts] of campaignReservations.entries()) {\n if (ts < cutoff) campaignReservations.delete(id);\n }\n}\n\nfunction campaignCallsInLastHour(now = Date.now()): number {\n purgeOldCampaignReservations(now);\n const cutoff = now - 60 * 60 * 1000;\n const completed = readReceipts({ kind: 'tool_call', limit: 1000 }).filter((receipt) => {\n if (receipt.status !== 'ok') return false;\n if (metaString(receipt.meta, 'mode') !== 'campaign') return false;\n const ts = Date.parse(receipt.ts);\n return Number.isFinite(ts) && ts >= cutoff;\n }).length;\n return completed + campaignReservations.size;\n}\n\nfunction reserveCampaignCall(): string {\n const id = `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n campaignReservations.set(id, Date.now());\n return id;\n}\n\nfunction releaseCampaignReservation(id: string | undefined): void {\n if (id) campaignReservations.delete(id);\n}\n\nfunction firstString(...values: unknown[]): string | undefined {\n for (const value of values) {\n if (typeof value === 'string' && value.trim()) return value.trim();\n if (typeof value === 'number' && Number.isFinite(value)) return String(value);\n }\n return undefined;\n}\n\nfunction numberListContains(numbers: string[], candidate: string | null): boolean {\n if (!candidate) return false;\n return numbers.some((number) => normalizePhoneNumber(number) === candidate);\n}\n\nfunction hasOptOutIntent(transcript: string | undefined, keywords: string[]): string | undefined {\n if (!transcript) return undefined;\n const lower = transcript.toLowerCase();\n return keywords.find((keyword) => {\n const normalized = keyword.trim().toLowerCase();\n return normalized.length > 0 && new RegExp(`\\\\b${normalized.replace(/[.*+?^${}()|[\\\\]\\\\]/g, '\\\\$&')}\\\\b`, 'i').test(lower);\n });\n}\n\nfunction sanitizeTelephonyText(value: unknown): unknown {\n if (typeof value === 'string') {\n return value\n .replace(/\\+?\\d[\\d\\s().-]{7,}\\d/g, '[REDACTED_PHONE]')\n .replace(/Bearer\\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [REDACTED]')\n .replace(/\\b(?:api[_-]?key|authorization|token|secret)\\b\\s*[:=]\\s*[^\\s,;]+/gi, '$1=[REDACTED]')\n .replace(/dg_[A-Za-z0-9._~+/=-]+/g, '[REDACTED_DOGRAH_KEY]')\n .slice(0, 500);\n }\n if (Array.isArray(value)) return value.slice(0, 20).map(sanitizeTelephonyText);\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, nested] of Object.entries(value as Record<string, unknown>).slice(0, 50)) {\n if (/api[_-]?key|authorization|token|secret|password/i.test(key)) {\n out[key] = '[REDACTED]';\n } else {\n out[key] = sanitizeTelephonyText(nested);\n }\n }\n return out;\n }\n return value;\n}\n\nfunction verifyDograhWebhook(req: Request, config: TitanConfig): { ok: true } | { ok: false; status: number; error: string; message: string } {\n const expected = config.telephony.dograh.apiKey?.trim();\n if (!expected) {\n return { ok: false, status: 403, error: 'dograh_webhook_secret_missing', message: 'Dograh inbound webhooks require telephony.dograh.apiKey.' };\n }\n const auth = typeof req.headers.authorization === 'string' ? req.headers.authorization : '';\n const bearer = auth.toLowerCase().startsWith('bearer ') ? auth.slice(7).trim() : '';\n const headerToken = typeof req.headers['x-titan-telephony-token'] === 'string' ? req.headers['x-titan-telephony-token'].trim() : '';\n if (bearer !== expected && headerToken !== expected) {\n return { ok: false, status: 401, error: 'dograh_webhook_unauthorized', message: 'Dograh inbound webhook authentication failed.' };\n }\n return { ok: true };\n}\n\nfunction telephonyReceiptMeta(receipt: ReturnType<typeof readReceipts>[number]): Record<string, unknown> {\n return {\n actionId: receipt.action_id,\n ts: receipt.ts,\n kind: receipt.kind,\n status: receipt.status,\n summary: receipt.summary,\n source: typeof receipt.meta?.source === 'string' ? receipt.meta.source : undefined,\n mode: typeof receipt.meta?.mode === 'string' ? receipt.meta.mode : undefined,\n direction: typeof receipt.meta?.direction === 'string' ? receipt.meta.direction : undefined,\n toNumberRedacted: typeof receipt.meta?.toNumberRedacted === 'string' ? receipt.meta.toNumberRedacted : undefined,\n fromNumberRedacted: typeof receipt.meta?.fromNumberRedacted === 'string' ? receipt.meta.fromNumberRedacted : undefined,\n workflowId: typeof receipt.meta?.workflowId === 'string' ? receipt.meta.workflowId : undefined,\n workflowRunId: receipt.meta?.workflowRunId,\n callId: typeof receipt.meta?.callId === 'string' ? receipt.meta.callId : undefined,\n };\n}\n\nexport function createTelephonyRouter(deps: TelephonyRouterDeps = {}): Router {\n const router = Router();\n const loadConfig = deps.loadConfig ?? loadTitanConfig;\n const createClient = deps.createClient ?? createDograhClientFromConfig;\n\n router.get('/dograh/status', async (_req, res) => {\n try {\n const config = loadConfig();\n const telephony = config.telephony;\n const client = createClient(telephony);\n const health = await client.health();\n res.json({\n ok: Boolean((health as { ok?: boolean }).ok),\n provider: telephony.provider,\n config: summarizeDograhConfig(telephony),\n dograh: sanitizeTelephonyText(health),\n });\n } catch (error) {\n res.status(500).json({\n ok: false,\n error: 'dograh_status_failed',\n message: (error as Error).message,\n });\n }\n });\n\n router.get('/dograh/workflows', async (req, res) => {\n try {\n const status = typeof req.query.status === 'string' && req.query.status.trim()\n ? req.query.status.trim()\n : 'active';\n const config = loadConfig();\n const client = createClient(config.telephony);\n const workflows = await client.listWorkflowSummaries(status);\n res.json({ workflows, count: workflows.length });\n } catch (error) {\n res.status(502).json({\n error: 'dograh_workflows_unavailable',\n message: (error as Error).message,\n });\n }\n });\n\n router.get('/dograh/calls', (req, res) => {\n const limit = Number(req.query.limit ?? 100);\n const calls = readReceipts({ limit: Number.isFinite(limit) ? Math.min(Math.max(Math.floor(limit), 1), 500) : 100 })\n .filter((receipt) => receipt.meta?.source === 'dograh' || String(receipt.summary).toLowerCase().includes('dograh'))\n .map(telephonyReceiptMeta);\n res.json({ calls, count: calls.length });\n });\n\n router.get('/dograh/opt-outs', (_req, res) => {\n const optOuts = listTelephonyOptOuts().map((entry) => ({\n phoneRedacted: entry.phoneRedacted,\n reason: entry.reason,\n source: entry.source,\n recordedAt: entry.recordedAt,\n callId: entry.callId,\n }));\n res.json({ optOuts, count: optOuts.length });\n });\n\n router.post('/dograh/inbound', (req, res) => {\n try {\n const config = loadConfig();\n if (!config.telephony.enabled) {\n res.status(403).json({ ok: false, error: 'telephony_disabled', message: 'Telephony is disabled.' });\n return;\n }\n\n const webhookAuth = verifyDograhWebhook(req, config);\n if ('status' in webhookAuth) {\n writeReceipt({\n kind: 'error',\n status: 'fail',\n summary: 'Dograh inbound webhook rejected: authentication failed',\n meta: { source: 'dograh', direction: 'inbound', error: webhookAuth.error },\n });\n res.status(webhookAuth.status).json({ ok: false, error: webhookAuth.error, message: webhookAuth.message });\n return;\n }\n\n const fromNumber = normalizePhoneNumber(firstString(req.body?.fromNumber, req.body?.from, req.body?.callerNumber, req.body?.caller));\n const toNumber = normalizePhoneNumber(firstString(req.body?.toNumber, req.body?.to, req.body?.calledNumber, req.body?.called));\n const fromNumberRedacted = redactPhoneNumber(fromNumber);\n const toNumberRedacted = redactPhoneNumber(toNumber);\n const transcript = safeString(firstString(req.body?.transcript, req.body?.message, req.body?.text), 500);\n const callId = safeString(firstString(req.body?.callId, req.body?.call_id, req.body?.providerCallId, req.body?.provider_sid), 120);\n const workflowRunId = firstString(req.body?.workflowRunId, req.body?.workflow_run_id, req.body?.runId);\n\n if (!fromNumber || !toNumber) {\n writeReceipt({\n kind: 'error',\n status: 'fail',\n summary: 'Dograh inbound call blocked: invalid phone metadata',\n meta: { source: 'dograh', direction: 'inbound', fromNumberRedacted, toNumberRedacted, callId },\n });\n res.status(400).json({ ok: false, error: 'telephony_invalid_inbound_call', message: 'Inbound call requires valid E.164 caller and called numbers.' });\n return;\n }\n\n const isAdminLine = numberListContains(config.telephony.adminNumbers, toNumber);\n const isPublicLine = numberListContains(config.telephony.publicNumbers, toNumber) || !isAdminLine;\n const isAdminCaller = numberListContains(config.telephony.adminNumbers, fromNumber);\n const mode = isAdminLine ? 'admin' : 'receptionist';\n const optOutKeyword = hasOptOutIntent(transcript, config.telephony.optOutKeywords);\n let optOutRecorded = false;\n if (optOutKeyword && !isAdminLine) {\n recordTelephonyOptOut({\n phoneNumber: fromNumber,\n phoneHash: hashPhoneForApproval(config.telephony, fromNumber),\n reason: `Inbound caller said opt-out keyword: ${optOutKeyword}`,\n callId,\n });\n optOutRecorded = true;\n }\n\n if (isAdminLine && !isAdminCaller) {\n writeReceipt({\n kind: 'approval_decision',\n status: 'fail',\n summary: `Dograh admin caller rejected: ${fromNumberRedacted}`,\n meta: { source: 'dograh', direction: 'inbound', mode, fromNumberRedacted, toNumberRedacted, callId, workflowRunId },\n });\n res.status(403).json({\n ok: false,\n error: 'telephony_admin_caller_not_allowed',\n message: 'Caller is not authorized for the TITAN admin phone line.',\n mode,\n authorized: false,\n fromNumberRedacted,\n toNumberRedacted,\n });\n return;\n }\n\n writeReceipt({\n kind: 'tool_call',\n status: 'ok',\n summary: isAdminLine\n ? `Dograh admin call accepted: ${fromNumberRedacted}`\n : `Dograh receptionist call received: ${fromNumberRedacted}`,\n meta: {\n source: 'dograh',\n direction: 'inbound',\n mode,\n fromNumberRedacted,\n toNumberRedacted,\n callId,\n workflowRunId,\n optOutRecorded,\n transcriptSummary: transcript ? String(sanitizeTelephonyText(transcript)).slice(0, 120) : undefined,\n },\n });\n\n res.json({\n ok: true,\n mode: isAdminLine ? 'admin' : 'receptionist',\n authorized: isAdminLine && isAdminCaller,\n nextAction: isPublicLine && !isAdminLine ? 'record_message' : 'admin_control',\n fromNumberRedacted,\n toNumberRedacted,\n optOutRecorded,\n });\n } catch (error) {\n writeReceipt({\n kind: 'error',\n status: 'fail',\n summary: 'Dograh inbound call route failed',\n meta: { source: 'dograh', direction: 'inbound', error: (error as Error).message.slice(0, 200) },\n });\n res.status(500).json({ ok: false, error: 'dograh_inbound_route_failed', message: (error as Error).message });\n }\n });\n\n router.post('/dograh/call', async (req, res) => {\n try {\n const config = loadConfig();\n const policy = validateOutboundCallRequest(config.telephony, req.body ?? {});\n if (!policy.ok) {\n writeReceipt({\n kind: 'error',\n status: 'fail',\n summary: `Dograh outbound call blocked: ${policy.reason ?? 'policy failure'}`,\n meta: { mode: policy.mode, toNumberRedacted: policy.toNumberRedacted },\n });\n res.status(403).json({\n ok: false,\n error: 'telephony_policy_blocked',\n message: policy.reason,\n call: { toNumberRedacted: policy.toNumberRedacted, mode: policy.mode },\n });\n return;\n }\n\n const approved = safeBool(req.body?.approved);\n const approvalActionId = safeActionId(req.body?.approvalActionId);\n const approvalNote = safeString(req.body?.approvalNote, 200);\n const responseCall = outboundResponseBody(policy);\n\n if (policy.mode === 'campaign' && isTelephonyOptedOut(policy.toNumberHash, policy.toNumberOptOutHash)) {\n writeReceipt({\n kind: 'error',\n status: 'fail',\n summary: `Dograh campaign call blocked by opt-out: ${policy.toNumberRedacted}`,\n meta: { source: 'dograh', mode: policy.mode, toNumberRedacted: policy.toNumberRedacted, toNumberHash: policy.toNumberHash },\n });\n res.status(403).json({\n ok: false,\n error: 'telephony_policy_blocked',\n message: 'Destination number has opted out of campaign calls.',\n call: responseCall,\n });\n return;\n }\n\n if (policy.requiresApproval && !approved) {\n const approval = createApproval({\n type: 'custom',\n requestedBy: `phone-desk:${policy.mode}:${policy.toNumberHash?.slice(0, 12) ?? 'unknown'}`,\n payload: {\n category: 'telephony_outbound_call',\n mode: policy.mode,\n workflowId: policy.workflowId,\n toNumberRedacted: policy.toNumberRedacted,\n toNumberHash: policy.toNumberHash,\n toNumberOptOutHash: policy.toNumberOptOutHash,\n purpose: policy.purpose,\n source: 'dograh',\n },\n });\n const receipt = writeReceipt({\n kind: 'approval_request',\n status: 'pending',\n summary: `Dograh outbound call approval required: ${policy.toNumberRedacted}`,\n meta: {\n mode: policy.mode,\n workflowId: policy.workflowId,\n toNumberRedacted: policy.toNumberRedacted,\n toNumberHash: policy.toNumberHash,\n toNumberOptOutHash: policy.toNumberOptOutHash,\n purpose: policy.purpose,\n },\n });\n res.status(202).json({\n ok: false,\n status: 'pending_approval',\n approvalActionId: approval.id,\n receiptActionId: receipt.action_id,\n call: responseCall,\n });\n return;\n }\n\n if (policy.requiresApproval) {\n const approval = verifyPriorApproval(approvalActionId, policy);\n if ('status' in approval) {\n res.status(approval.status).json({\n ok: false,\n error: approval.error,\n message: approval.message,\n call: responseCall,\n });\n return;\n }\n }\n\n let campaignReservationId: string | undefined;\n if (policy.mode === 'campaign') {\n if (campaignCallsInLastHour() >= config.telephony.maxCallsPerHour) {\n writeReceipt({\n kind: 'error',\n status: 'fail',\n summary: `Dograh campaign call rate-limited: ${policy.toNumberRedacted}`,\n meta: { mode: policy.mode, workflowId: policy.workflowId, toNumberRedacted: policy.toNumberRedacted, toNumberHash: policy.toNumberHash },\n });\n res.status(429).json({\n ok: false,\n error: 'telephony_rate_limited',\n message: `Campaign outbound call limit reached: ${config.telephony.maxCallsPerHour}/hour.`,\n call: responseCall,\n });\n return;\n }\n campaignReservationId = reserveCampaignCall();\n }\n\n if (approved) {\n writeReceipt({\n kind: 'approval_decision',\n status: 'ok',\n parent_action_id: approvalActionId,\n summary: `Dograh outbound call approved: ${policy.toNumberRedacted}`,\n meta: {\n mode: policy.mode,\n workflowId: policy.workflowId,\n toNumberRedacted: policy.toNumberRedacted,\n toNumberHash: policy.toNumberHash,\n note: approvalNote,\n },\n });\n }\n\n const client = createClient(config.telephony);\n let result: DograhOutboundCallResult;\n try {\n result = await client.initiateOutboundCall({\n workflowId: policy.workflowId!,\n phoneNumber: policy.toNumber!,\n telephonyConfigurationId: policy.telephonyConfigurationId,\n fromPhoneNumberId: policy.fromPhoneNumberId,\n });\n } finally {\n releaseCampaignReservation(campaignReservationId);\n }\n\n if (!result.ok) {\n writeReceipt({\n kind: 'tool_call',\n status: 'fail',\n summary: `Dograh outbound call failed: ${policy.toNumberRedacted}`,\n meta: { status: result.status, error: result.error, workflowId: policy.workflowId },\n });\n res.status(502).json({\n ok: false,\n status: 'failed',\n error: 'dograh_call_failed',\n message: result.error,\n call: { ...responseCall, workflowRunId: result.workflowRunId },\n });\n return;\n }\n\n writeReceipt({\n kind: 'tool_call',\n status: 'ok',\n summary: `Dograh outbound call started: ${policy.toNumberRedacted}`,\n meta: { workflowRunId: result.workflowRunId, workflowId: policy.workflowId, mode: policy.mode },\n });\n res.json({\n ok: true,\n status: 'started',\n call: { ...responseCall, workflowRunId: result.workflowRunId },\n });\n } catch (error) {\n writeReceipt({\n kind: 'error',\n status: 'fail',\n summary: 'Dograh outbound call route failed',\n meta: { error: (error as Error).message.slice(0, 200) },\n });\n res.status(500).json({\n ok: false,\n error: 'dograh_call_route_failed',\n message: (error as Error).message,\n });\n }\n });\n\n return router;\n}\n"],"mappings":"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","names":[]}
@@ -272,6 +272,7 @@ function createTestsRouter() {
272
272
  ];
273
273
  const imperativeRe = /\b(?:add|open|show|pin|create|launch|put|give\s+me|i\s+want|let'?s\s+see|i\s+need)\b/i;
274
274
  const widgetNounRe = /\b(?:widget|panel|dashboard|hub|gallery|kitchen|scheduler)\b/i;
275
+ const evalRunId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
275
276
  const agentCall = async (input, testName) => {
276
277
  const hasExplicitIntent = imperativeRe.test(input) && widgetNounRe.test(input);
277
278
  const shortcut = hasExplicitIntent ? systemWidgetShortcuts.find((s) => s.pattern.test(input)) : null;
@@ -284,7 +285,8 @@ _____widget
284
285
  toolsUsed: []
285
286
  };
286
287
  }
287
- const userId = testName ? `eval-${testName.replace(/\s+/g, "-").toLowerCase()}` : "eval-harness";
288
+ const testSlug = testName ? testName.replace(/\s+/g, "-").toLowerCase() : "harness";
289
+ const userId = `eval-${evalRunId}-${testSlug}`;
288
290
  const result2 = await processMessage(input, "eval", userId, {});
289
291
  const content = result2.content || "";
290
292
  const toolsUsed = result2.toolsUsed ?? [];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/gateway/routes/tests.ts"],"sourcesContent":["/**\n * Tests Router\n *\n * Extracted from gateway/server.ts.\n * Consolidates all /api/test/*, /api/tests/*, /api/test-health/*, and /api/eval/* routes.\n */\n\nimport { Router, type Request, type Response } from 'express';\nimport logger from '../../utils/logger.js';\nimport { processMessage } from '../../agent/agent.js';\nimport { recordEvalSuiteResult, recordEvalTimeout, recordEvalError } from '../metrics.js';\n\nconst COMPONENT = 'TestsRouter';\n\nexport function createTestsRouter(): Router {\n const router = Router();\n\n // ── Test routes (/api/test/*) ───────────────────────────────────\n router.get('/test/health', async (_req, res) => {\n try {\n const { getTestHealthSummary } = await import('../../testing/testHealthMonitor.js');\n const summary = getTestHealthSummary();\n res.json(summary);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/test/stats', async (_req, res) => {\n try {\n const { getTestHealth } = await import('../../testing/testHealthMonitor.js');\n const stats = getTestHealth();\n res.json(stats);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/test/failing', async (_req, res) => {\n try {\n const { getFailingTests } = await import('../../testing/testHealthMonitor.js');\n const failing = getFailingTests();\n res.json(failing);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/test/flaky', async (_req, res) => {\n try {\n const { getFlakyTests } = await import('../../testing/testHealthMonitor.js');\n const threshold = _req.query.threshold ? parseFloat(_req.query.threshold as string) : 0.4;\n const flaky = getFlakyTests(threshold);\n res.json(flaky);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/test/runs', async (_req, res) => {\n try {\n const { getRecentTestRuns } = await import('../../testing/testHealthMonitor.js');\n const limit = _req.query.limit ? parseInt(_req.query.limit as string, 10) : 10;\n const runs = getRecentTestRuns(limit);\n res.json(runs);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/test/run', async (_req, res) => {\n try {\n const { runTestsDetailed } = await import('../../testing/testHealthMonitor.js');\n const { pattern, watch, coverage, timeout } = _req.body;\n const result = await runTestsDetailed({ pattern, watch, coverage, timeout });\n res.json(result);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/test/repair-history', async (_req, res) => {\n try {\n const { getRepairHistory } = await import('../../testing/repairValidator.js');\n const repairId = _req.query.repairId as string | undefined;\n const history = getRepairHistory(repairId);\n res.json(history);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n // ── Tests routes (/api/tests/*) ─────────────────────────────────\n router.get('/tests/health', async (_req, res) => {\n try {\n const { getTestHealthSummary } = await import('../../testing/testHealthMonitor.js');\n const summary = getTestHealthSummary();\n res.json(summary);\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.get('/tests/failing', async (_req, res) => {\n try {\n const { getFailingTests } = await import('../../testing/testHealthMonitor.js');\n const failing = getFailingTests();\n res.json({ tests: failing, count: failing.length });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.get('/tests/flaky', async (_req, res) => {\n try {\n const { getFlakyTests } = await import('../../testing/testHealthMonitor.js');\n const threshold = _req.query.threshold ? parseFloat(_req.query.threshold as string) : 0.4;\n const flaky = getFlakyTests(threshold);\n res.json({ tests: flaky, count: flaky.length, threshold });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.get('/tests/history', async (_req, res) => {\n try {\n const { getRecentTestRuns } = await import('../../testing/testHealthMonitor.js');\n const limit = _req.query.limit ? parseInt(_req.query.limit as string, 10) : 10;\n const runs = getRecentTestRuns(limit);\n res.json({ runs, count: runs.length });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.post('/tests/run', async (req, res) => {\n try {\n const { runTests } = await import('../../testing/testRunner.js');\n const { pattern, timeout } = req.body;\n const result = await runTests({ pattern, timeout });\n res.json(result);\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.post('/tests/validate-repair', async (req, res) => {\n try {\n const { validateRepair } = await import('../../testing/repairValidator.js');\n const { repairId, finding, affectedFiles } = req.body;\n\n if (!repairId || !finding) {\n res.status(400).json({ error: 'Missing required fields: repairId, finding' });\n return;\n }\n\n const result = await validateRepair({ repairId, finding, affectedFiles });\n res.json(result);\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.get('/tests/repair-history', async (req, res) => {\n try {\n const { getRepairHistory } = await import('../../testing/repairValidator.js');\n const { repairId } = req.query;\n const history = getRepairHistory(repairId as string | undefined);\n res.json({ history, count: history.length });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.post('/tests/validate-system-repair', async (req, res) => {\n try {\n const { validateSystemRepair } = await import('../../testing/repairValidator.js');\n const { repairType, target } = req.body;\n\n if (!repairType || !target) {\n res.status(400).json({ error: 'Missing required fields: repairType, target' });\n return;\n }\n\n const result = await validateSystemRepair({ repairType, target });\n res.json({ success: result.valid ?? false, repairType, target, ...result });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n // ── Test-Health routes (/api/test-health/*) ─────────────────────\n router.get('/test-health/summary', async (_req, res) => {\n try {\n const { getTestHealthSummary, getFlakyTests } = await import('../../testing/testHealthMonitor.js');\n const raw = getTestHealthSummary();\n const flaky = getFlakyTests().length;\n res.json({\n total: raw.total as number,\n passing: raw.passing as number,\n failing: raw.failing as number,\n flaky,\n coverage: raw.coveragePct as number,\n });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.get('/test-health/failing', async (_req, res) => {\n try {\n const { getFailingTests } = await import('../../testing/testHealthMonitor.js');\n const limit = _req.query.limit ? parseInt(_req.query.limit as string, 10) : 10;\n const names = getFailingTests().slice(0, limit);\n const tests = names.map(name => ({ name, suite: '', error: '', lastFailed: '', attempts: 0 }));\n res.json({ count: tests.length, tests });\n } catch (e) {\n res.status(500).json({ error: (e as Error).message, tests: [] });\n }\n });\n\n router.get('/test-health/flaky', async (_req, res) => {\n try {\n const { getFlakyTests } = await import('../../testing/testHealthMonitor.js');\n const threshold = _req.query.threshold ? parseFloat(_req.query.threshold as string) : 0.4;\n const limit = _req.query.limit ? parseInt(_req.query.limit as string, 10) : 10;\n const names = getFlakyTests(threshold).slice(0, limit);\n const tests = names.map(name => ({ name, suite: '', passRate: 0, runs: 0 }));\n res.json({ count: tests.length, threshold, tests });\n } catch (e) {\n res.status(500).json({ error: (e as Error).message, tests: [] });\n }\n });\n\n router.get('/test-health/history', async (_req, res) => {\n try {\n const { getRecentTestRuns } = await import('../../testing/testHealthMonitor.js');\n const limit = _req.query.limit ? parseInt(_req.query.limit as string, 10) : 5;\n const runs = getRecentTestRuns(limit);\n res.json({ count: runs.length, runs });\n } catch (e) {\n res.status(500).json({ error: (e as Error).message, runs: [] });\n }\n });\n\n router.post('/test-health/run', async (req, res) => {\n try {\n const { runTests } = await import('../../testing/testRunner.js');\n const { pattern, coverage, timeout } = req.body;\n const result = await runTests({ pattern, coverage, timeout });\n res.json(result);\n } catch (e) {\n res.status(500).json({ error: (e as Error).message, success: false });\n }\n });\n\n // ── Eval routes (/api/eval/*) ──────────────────────────────────\n // v5.3.1 hardening:\n // - Promise.race with configurable timeout (?timeoutMs=, default 10 min)\n // - 504 on timeout with {timedOut:true} so the CI gate sees an explicit\n // signal instead of a hung connection\n // - 500 on unhandled exception (gateway must not crash on a bad case)\n // - 404 (was 400) on unknown suite — semantically right, easier on CI\n // scripts that branch on resource-not-found vs validation\n // - X-Eval-Suite response header so /var/log filtering is one grep\n // - Prometheus counters: timeout_total + error_total updated on the\n // respective failure paths so the Trends tab can chart drift.\n router.post('/eval/run', async (req, res) => {\n const requestedSuite = (req.body as { suite?: string } | undefined)?.suite ?? '';\n const timeoutMs = (() => {\n const raw = req.query?.timeoutMs;\n if (typeof raw !== 'string') return 600_000; // 10 min default\n const n = Number(raw);\n // Guard against ridiculous values: clamp to [10s, 1hr].\n if (!Number.isFinite(n) || n <= 0) return 600_000;\n return Math.max(10_000, Math.min(3_600_000, Math.round(n)));\n })();\n if (requestedSuite) {\n res.setHeader('X-Eval-Suite', requestedSuite);\n }\n const tStart = Date.now();\n logger.info(COMPONENT, `/api/eval/run START suite=${requestedSuite || '(none)'} timeoutMs=${timeoutMs}`);\n\n try {\n const {\n runEvalSuite,\n WIDGET_CREATION_SUITE,\n SAFETY_SUITE,\n TOOL_ROUTING_SUITE,\n GATE_FORMAT_SUITE,\n PIPELINE_SUITE,\n ADVERSARIAL_SUITE,\n TOOL_ROUTING_V2_SUITE,\n SESSION_SUITE,\n WIDGET_V2_SUITE,\n GATE_FORMAT_V2_SUITE,\n CONTENT_SUITE,\n } = await import('../../eval/harness.js');\n\n // Local agent call — replicates the system-widget shortcut inline\n // so eval tests exercise the same fast-path users get via HTTP.\n // v6.0 widget-hijack tightening: same gate as gateway/server.ts +\n // agent/agent.ts — imperative verb + widget noun together, not just\n // a single trigger word. Bucket C panels (paperclip) removed.\n const systemWidgetShortcuts: Array<{ pattern: RegExp; source: string; name: string; w: number; h: number }> = [\n { pattern: /\\b(?:backups?|snapshots?|archives?)\\b/i, source: 'system:backup', name: 'Backup Manager', w: 6, h: 6 },\n { pattern: /\\b(?:training|train|specialists?|models?)\\b/i, source: 'system:training', name: 'Training Dashboard', w: 6, h: 6 },\n { pattern: /\\b(?:recipes?|playbooks?|workflows?|jarvis)\\b/i, source: 'system:recipes', name: 'Recipe Kitchen', w: 6, h: 6 },\n { pattern: /\\b(?:vram|gpu|nvidia)\\b/i, source: 'system:vram', name: 'VRAM Monitor', w: 6, h: 6 },\n { pattern: /\\b(?:teams?|members?|roles?|permissions?|rbac)\\b/i, source: 'system:teams', name: 'Team Hub', w: 6, h: 6 },\n { pattern: /\\b(?:cron|schedules?|jobs?|timers?)\\b/i, source: 'system:cron', name: 'Cron Scheduler', w: 6, h: 6 },\n { pattern: /\\b(?:checkpoints?|restores?|save state)\\b/i, source: 'system:checkpoints', name: 'Checkpoints', w: 6, h: 5 },\n { pattern: /\\b(?:organism|guardrails?)\\b/i, source: 'system:organism', name: 'Organism Monitor', w: 6, h: 6 },\n { pattern: /\\b(?:fleet|nodes?|routes?|mesh)\\b/i, source: 'system:fleet', name: 'Fleet Router', w: 6, h: 5 },\n { pattern: /\\b(?:captcha|form fill|web automation)\\b/i, source: 'system:browser', name: 'Browser Tools', w: 6, h: 5 },\n // v6.0 step 1 — paperclip Bucket C / killed\n { pattern: /\\b(?:tests?|flaky|failing|coverage|eval)\\b/i, source: 'system:eval', name: 'Test Lab', w: 6, h: 6 },\n ];\n const imperativeRe = /\\b(?:add|open|show|pin|create|launch|put|give\\s+me|i\\s+want|let'?s\\s+see|i\\s+need)\\b/i;\n const widgetNounRe = /\\b(?:widget|panel|dashboard|hub|gallery|kitchen|scheduler)\\b/i;\n const agentCall = async (input: string, testName?: string) => {\n const hasExplicitIntent = imperativeRe.test(input) && widgetNounRe.test(input);\n const shortcut = hasExplicitIntent ? systemWidgetShortcuts.find(s => s.pattern.test(input)) : null;\n if (shortcut) {\n return {\n content: `Added the **${shortcut.name}** widget to your canvas.\\n\\n_____widget\\n{ \"name\": \"${shortcut.name}\", \"format\": \"system\", \"source\": \"${shortcut.source}\", \"w\": ${shortcut.w}, \"h\": ${shortcut.h} }`,\n toolsUsed: [],\n };\n }\n // Use a unique userId per test case so each test gets a fresh session\n // with its own token budget. Prevents budget bleed across tests.\n const userId = testName ? `eval-${testName.replace(/\\s+/g, '-').toLowerCase()}` : 'eval-harness';\n const result = await processMessage(input, 'eval', userId, {});\n const content = result.content || '';\n const toolsUsed = (result.toolsUsed as string[]) ?? [];\n return {\n content,\n toolsUsed,\n attemptedTools: result.attemptedTools ?? [],\n };\n };\n\n let cases;\n const suite = requestedSuite;\n switch (suite) {\n case 'widget-creation': cases = WIDGET_CREATION_SUITE; break;\n case 'safety': cases = SAFETY_SUITE; break;\n case 'tool-routing': cases = TOOL_ROUTING_SUITE; break;\n case 'gate-format': cases = GATE_FORMAT_SUITE; break;\n case 'pipeline': cases = PIPELINE_SUITE; break;\n case 'adversarial': cases = ADVERSARIAL_SUITE; break;\n case 'tool-routing-v2': cases = TOOL_ROUTING_V2_SUITE; break;\n case 'session': cases = SESSION_SUITE; break;\n case 'widget-v2': cases = WIDGET_V2_SUITE; break;\n case 'gate-format-v2': cases = GATE_FORMAT_V2_SUITE; break;\n case 'content': cases = CONTENT_SUITE; break;\n default:\n // 404 (was 400 in v5.3.0) — \"this suite resource doesn't exist\"\n // is semantically more accurate than \"your request was malformed\",\n // and lets CI scripts branch on resource-not-found.\n res.status(404).json({\n error: `Unknown suite: ${suite || '(empty)'}. Choose: widget-creation, safety, tool-routing, gate-format, pipeline, adversarial, tool-routing-v2, session, widget-v2, gate-format-v2, content.`,\n });\n return;\n }\n\n // Promise.race: whichever finishes first wins. The timeout branch\n // resolves with a sentinel object so we don't have to reach into\n // the AbortController plumbing of every downstream tool.\n const TIMEOUT = Symbol('eval-timeout');\n const result = await Promise.race([\n runEvalSuite(suite, cases, agentCall),\n new Promise<typeof TIMEOUT>(resolve => {\n setTimeout(() => resolve(TIMEOUT), timeoutMs);\n }),\n ]);\n\n if (result === TIMEOUT) {\n const elapsed = Date.now() - tStart;\n logger.warn(COMPONENT, `/api/eval/run TIMEOUT suite=${suite} elapsedMs=${elapsed} timeoutMs=${timeoutMs}`);\n try { recordEvalTimeout(suite); } catch { /* metrics best-effort */ }\n res.status(504).json({\n suite,\n passed: 0,\n total: 0,\n results: [],\n timedOut: true,\n timeoutMs,\n elapsedMs: elapsed,\n error: `Eval suite '${suite}' timed out after ${timeoutMs}ms`,\n });\n return;\n }\n\n // Publish to Prometheus so suite regressions surface in /metrics over time.\n try { recordEvalSuiteResult(suite, result.passed, result.total); } catch { /* metrics best-effort */ }\n const elapsed = Date.now() - tStart;\n logger.info(COMPONENT, `/api/eval/run DONE suite=${suite} passed=${result.passed}/${result.total} elapsedMs=${elapsed}`);\n res.json(result);\n } catch (e) {\n const err = e as Error;\n const elapsed = Date.now() - tStart;\n logger.warn(COMPONENT, `/api/eval/run ERROR suite=${requestedSuite || '(none)'} elapsedMs=${elapsed} message=${err.message}`);\n try { recordEvalError(requestedSuite || 'unknown', err.name || 'unknown'); } catch { /* metrics best-effort */ }\n // Return the actual message — eval is an internal/admin endpoint, not\n // user-facing chat. Generic \"something went wrong\" hid real bugs from\n // the CI gate.\n res.status(500).json({\n suite: requestedSuite || undefined,\n error: err.message || 'Eval run failed',\n errorClass: err.name || 'Error',\n });\n }\n });\n\n router.get('/eval/suites', async (_req, res) => {\n res.json({ suites: [\n 'widget-creation', 'safety', 'tool-routing', 'gate-format',\n 'pipeline', 'adversarial', 'tool-routing-v2', 'session',\n 'widget-v2', 'gate-format-v2', 'content',\n ] });\n });\n\n return router;\n}\n"],"mappings":"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","names":["result","elapsed"]}
1
+ {"version":3,"sources":["../../../src/gateway/routes/tests.ts"],"sourcesContent":["/**\n * Tests Router\n *\n * Extracted from gateway/server.ts.\n * Consolidates all /api/test/*, /api/tests/*, /api/test-health/*, and /api/eval/* routes.\n */\n\nimport { Router, type Request, type Response } from 'express';\nimport logger from '../../utils/logger.js';\nimport { processMessage } from '../../agent/agent.js';\nimport { recordEvalSuiteResult, recordEvalTimeout, recordEvalError } from '../metrics.js';\n\nconst COMPONENT = 'TestsRouter';\n\nexport function createTestsRouter(): Router {\n const router = Router();\n\n // ── Test routes (/api/test/*) ───────────────────────────────────\n router.get('/test/health', async (_req, res) => {\n try {\n const { getTestHealthSummary } = await import('../../testing/testHealthMonitor.js');\n const summary = getTestHealthSummary();\n res.json(summary);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/test/stats', async (_req, res) => {\n try {\n const { getTestHealth } = await import('../../testing/testHealthMonitor.js');\n const stats = getTestHealth();\n res.json(stats);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/test/failing', async (_req, res) => {\n try {\n const { getFailingTests } = await import('../../testing/testHealthMonitor.js');\n const failing = getFailingTests();\n res.json(failing);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/test/flaky', async (_req, res) => {\n try {\n const { getFlakyTests } = await import('../../testing/testHealthMonitor.js');\n const threshold = _req.query.threshold ? parseFloat(_req.query.threshold as string) : 0.4;\n const flaky = getFlakyTests(threshold);\n res.json(flaky);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/test/runs', async (_req, res) => {\n try {\n const { getRecentTestRuns } = await import('../../testing/testHealthMonitor.js');\n const limit = _req.query.limit ? parseInt(_req.query.limit as string, 10) : 10;\n const runs = getRecentTestRuns(limit);\n res.json(runs);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/test/run', async (_req, res) => {\n try {\n const { runTestsDetailed } = await import('../../testing/testHealthMonitor.js');\n const { pattern, watch, coverage, timeout } = _req.body;\n const result = await runTestsDetailed({ pattern, watch, coverage, timeout });\n res.json(result);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/test/repair-history', async (_req, res) => {\n try {\n const { getRepairHistory } = await import('../../testing/repairValidator.js');\n const repairId = _req.query.repairId as string | undefined;\n const history = getRepairHistory(repairId);\n res.json(history);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n // ── Tests routes (/api/tests/*) ─────────────────────────────────\n router.get('/tests/health', async (_req, res) => {\n try {\n const { getTestHealthSummary } = await import('../../testing/testHealthMonitor.js');\n const summary = getTestHealthSummary();\n res.json(summary);\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.get('/tests/failing', async (_req, res) => {\n try {\n const { getFailingTests } = await import('../../testing/testHealthMonitor.js');\n const failing = getFailingTests();\n res.json({ tests: failing, count: failing.length });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.get('/tests/flaky', async (_req, res) => {\n try {\n const { getFlakyTests } = await import('../../testing/testHealthMonitor.js');\n const threshold = _req.query.threshold ? parseFloat(_req.query.threshold as string) : 0.4;\n const flaky = getFlakyTests(threshold);\n res.json({ tests: flaky, count: flaky.length, threshold });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.get('/tests/history', async (_req, res) => {\n try {\n const { getRecentTestRuns } = await import('../../testing/testHealthMonitor.js');\n const limit = _req.query.limit ? parseInt(_req.query.limit as string, 10) : 10;\n const runs = getRecentTestRuns(limit);\n res.json({ runs, count: runs.length });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.post('/tests/run', async (req, res) => {\n try {\n const { runTests } = await import('../../testing/testRunner.js');\n const { pattern, timeout } = req.body;\n const result = await runTests({ pattern, timeout });\n res.json(result);\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.post('/tests/validate-repair', async (req, res) => {\n try {\n const { validateRepair } = await import('../../testing/repairValidator.js');\n const { repairId, finding, affectedFiles } = req.body;\n\n if (!repairId || !finding) {\n res.status(400).json({ error: 'Missing required fields: repairId, finding' });\n return;\n }\n\n const result = await validateRepair({ repairId, finding, affectedFiles });\n res.json(result);\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.get('/tests/repair-history', async (req, res) => {\n try {\n const { getRepairHistory } = await import('../../testing/repairValidator.js');\n const { repairId } = req.query;\n const history = getRepairHistory(repairId as string | undefined);\n res.json({ history, count: history.length });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.post('/tests/validate-system-repair', async (req, res) => {\n try {\n const { validateSystemRepair } = await import('../../testing/repairValidator.js');\n const { repairType, target } = req.body;\n\n if (!repairType || !target) {\n res.status(400).json({ error: 'Missing required fields: repairType, target' });\n return;\n }\n\n const result = await validateSystemRepair({ repairType, target });\n res.json({ success: result.valid ?? false, repairType, target, ...result });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n // ── Test-Health routes (/api/test-health/*) ─────────────────────\n router.get('/test-health/summary', async (_req, res) => {\n try {\n const { getTestHealthSummary, getFlakyTests } = await import('../../testing/testHealthMonitor.js');\n const raw = getTestHealthSummary();\n const flaky = getFlakyTests().length;\n res.json({\n total: raw.total as number,\n passing: raw.passing as number,\n failing: raw.failing as number,\n flaky,\n coverage: raw.coveragePct as number,\n });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.get('/test-health/failing', async (_req, res) => {\n try {\n const { getFailingTests } = await import('../../testing/testHealthMonitor.js');\n const limit = _req.query.limit ? parseInt(_req.query.limit as string, 10) : 10;\n const names = getFailingTests().slice(0, limit);\n const tests = names.map(name => ({ name, suite: '', error: '', lastFailed: '', attempts: 0 }));\n res.json({ count: tests.length, tests });\n } catch (e) {\n res.status(500).json({ error: (e as Error).message, tests: [] });\n }\n });\n\n router.get('/test-health/flaky', async (_req, res) => {\n try {\n const { getFlakyTests } = await import('../../testing/testHealthMonitor.js');\n const threshold = _req.query.threshold ? parseFloat(_req.query.threshold as string) : 0.4;\n const limit = _req.query.limit ? parseInt(_req.query.limit as string, 10) : 10;\n const names = getFlakyTests(threshold).slice(0, limit);\n const tests = names.map(name => ({ name, suite: '', passRate: 0, runs: 0 }));\n res.json({ count: tests.length, threshold, tests });\n } catch (e) {\n res.status(500).json({ error: (e as Error).message, tests: [] });\n }\n });\n\n router.get('/test-health/history', async (_req, res) => {\n try {\n const { getRecentTestRuns } = await import('../../testing/testHealthMonitor.js');\n const limit = _req.query.limit ? parseInt(_req.query.limit as string, 10) : 5;\n const runs = getRecentTestRuns(limit);\n res.json({ count: runs.length, runs });\n } catch (e) {\n res.status(500).json({ error: (e as Error).message, runs: [] });\n }\n });\n\n router.post('/test-health/run', async (req, res) => {\n try {\n const { runTests } = await import('../../testing/testRunner.js');\n const { pattern, coverage, timeout } = req.body;\n const result = await runTests({ pattern, coverage, timeout });\n res.json(result);\n } catch (e) {\n res.status(500).json({ error: (e as Error).message, success: false });\n }\n });\n\n // ── Eval routes (/api/eval/*) ──────────────────────────────────\n // v5.3.1 hardening:\n // - Promise.race with configurable timeout (?timeoutMs=, default 10 min)\n // - 504 on timeout with {timedOut:true} so the CI gate sees an explicit\n // signal instead of a hung connection\n // - 500 on unhandled exception (gateway must not crash on a bad case)\n // - 404 (was 400) on unknown suite — semantically right, easier on CI\n // scripts that branch on resource-not-found vs validation\n // - X-Eval-Suite response header so /var/log filtering is one grep\n // - Prometheus counters: timeout_total + error_total updated on the\n // respective failure paths so the Trends tab can chart drift.\n router.post('/eval/run', async (req, res) => {\n const requestedSuite = (req.body as { suite?: string } | undefined)?.suite ?? '';\n const timeoutMs = (() => {\n const raw = req.query?.timeoutMs;\n if (typeof raw !== 'string') return 600_000; // 10 min default\n const n = Number(raw);\n // Guard against ridiculous values: clamp to [10s, 1hr].\n if (!Number.isFinite(n) || n <= 0) return 600_000;\n return Math.max(10_000, Math.min(3_600_000, Math.round(n)));\n })();\n if (requestedSuite) {\n res.setHeader('X-Eval-Suite', requestedSuite);\n }\n const tStart = Date.now();\n logger.info(COMPONENT, `/api/eval/run START suite=${requestedSuite || '(none)'} timeoutMs=${timeoutMs}`);\n\n try {\n const {\n runEvalSuite,\n WIDGET_CREATION_SUITE,\n SAFETY_SUITE,\n TOOL_ROUTING_SUITE,\n GATE_FORMAT_SUITE,\n PIPELINE_SUITE,\n ADVERSARIAL_SUITE,\n TOOL_ROUTING_V2_SUITE,\n SESSION_SUITE,\n WIDGET_V2_SUITE,\n GATE_FORMAT_V2_SUITE,\n CONTENT_SUITE,\n } = await import('../../eval/harness.js');\n\n // Local agent call — replicates the system-widget shortcut inline\n // so eval tests exercise the same fast-path users get via HTTP.\n // v6.0 widget-hijack tightening: same gate as gateway/server.ts +\n // agent/agent.ts — imperative verb + widget noun together, not just\n // a single trigger word. Bucket C panels (paperclip) removed.\n const systemWidgetShortcuts: Array<{ pattern: RegExp; source: string; name: string; w: number; h: number }> = [\n { pattern: /\\b(?:backups?|snapshots?|archives?)\\b/i, source: 'system:backup', name: 'Backup Manager', w: 6, h: 6 },\n { pattern: /\\b(?:training|train|specialists?|models?)\\b/i, source: 'system:training', name: 'Training Dashboard', w: 6, h: 6 },\n { pattern: /\\b(?:recipes?|playbooks?|workflows?|jarvis)\\b/i, source: 'system:recipes', name: 'Recipe Kitchen', w: 6, h: 6 },\n { pattern: /\\b(?:vram|gpu|nvidia)\\b/i, source: 'system:vram', name: 'VRAM Monitor', w: 6, h: 6 },\n { pattern: /\\b(?:teams?|members?|roles?|permissions?|rbac)\\b/i, source: 'system:teams', name: 'Team Hub', w: 6, h: 6 },\n { pattern: /\\b(?:cron|schedules?|jobs?|timers?)\\b/i, source: 'system:cron', name: 'Cron Scheduler', w: 6, h: 6 },\n { pattern: /\\b(?:checkpoints?|restores?|save state)\\b/i, source: 'system:checkpoints', name: 'Checkpoints', w: 6, h: 5 },\n { pattern: /\\b(?:organism|guardrails?)\\b/i, source: 'system:organism', name: 'Organism Monitor', w: 6, h: 6 },\n { pattern: /\\b(?:fleet|nodes?|routes?|mesh)\\b/i, source: 'system:fleet', name: 'Fleet Router', w: 6, h: 5 },\n { pattern: /\\b(?:captcha|form fill|web automation)\\b/i, source: 'system:browser', name: 'Browser Tools', w: 6, h: 5 },\n // v6.0 step 1 — paperclip Bucket C / killed\n { pattern: /\\b(?:tests?|flaky|failing|coverage|eval)\\b/i, source: 'system:eval', name: 'Test Lab', w: 6, h: 6 },\n ];\n const imperativeRe = /\\b(?:add|open|show|pin|create|launch|put|give\\s+me|i\\s+want|let'?s\\s+see|i\\s+need)\\b/i;\n const widgetNounRe = /\\b(?:widget|panel|dashboard|hub|gallery|kitchen|scheduler)\\b/i;\n const evalRunId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n const agentCall = async (input: string, testName?: string) => {\n const hasExplicitIntent = imperativeRe.test(input) && widgetNounRe.test(input);\n const shortcut = hasExplicitIntent ? systemWidgetShortcuts.find(s => s.pattern.test(input)) : null;\n if (shortcut) {\n return {\n content: `Added the **${shortcut.name}** widget to your canvas.\\n\\n_____widget\\n{ \"name\": \"${shortcut.name}\", \"format\": \"system\", \"source\": \"${shortcut.source}\", \"w\": ${shortcut.w}, \"h\": ${shortcut.h} }`,\n toolsUsed: [],\n };\n }\n // Use a unique userId per eval run AND test case so repeated local/CI\n // evals cannot inherit old sessions, token budgets, or pending approvals.\n // Stable per-test names alone were not enough: rerunning the same suite\n // reused old approval state and produced false negatives.\n const testSlug = testName ? testName.replace(/\\s+/g, '-').toLowerCase() : 'harness';\n const userId = `eval-${evalRunId}-${testSlug}`;\n const result = await processMessage(input, 'eval', userId, {});\n const content = result.content || '';\n const toolsUsed = (result.toolsUsed as string[]) ?? [];\n return {\n content,\n toolsUsed,\n attemptedTools: result.attemptedTools ?? [],\n };\n };\n\n let cases;\n const suite = requestedSuite;\n switch (suite) {\n case 'widget-creation': cases = WIDGET_CREATION_SUITE; break;\n case 'safety': cases = SAFETY_SUITE; break;\n case 'tool-routing': cases = TOOL_ROUTING_SUITE; break;\n case 'gate-format': cases = GATE_FORMAT_SUITE; break;\n case 'pipeline': cases = PIPELINE_SUITE; break;\n case 'adversarial': cases = ADVERSARIAL_SUITE; break;\n case 'tool-routing-v2': cases = TOOL_ROUTING_V2_SUITE; break;\n case 'session': cases = SESSION_SUITE; break;\n case 'widget-v2': cases = WIDGET_V2_SUITE; break;\n case 'gate-format-v2': cases = GATE_FORMAT_V2_SUITE; break;\n case 'content': cases = CONTENT_SUITE; break;\n default:\n // 404 (was 400 in v5.3.0) — \"this suite resource doesn't exist\"\n // is semantically more accurate than \"your request was malformed\",\n // and lets CI scripts branch on resource-not-found.\n res.status(404).json({\n error: `Unknown suite: ${suite || '(empty)'}. Choose: widget-creation, safety, tool-routing, gate-format, pipeline, adversarial, tool-routing-v2, session, widget-v2, gate-format-v2, content.`,\n });\n return;\n }\n\n // Promise.race: whichever finishes first wins. The timeout branch\n // resolves with a sentinel object so we don't have to reach into\n // the AbortController plumbing of every downstream tool.\n const TIMEOUT = Symbol('eval-timeout');\n const result = await Promise.race([\n runEvalSuite(suite, cases, agentCall),\n new Promise<typeof TIMEOUT>(resolve => {\n setTimeout(() => resolve(TIMEOUT), timeoutMs);\n }),\n ]);\n\n if (result === TIMEOUT) {\n const elapsed = Date.now() - tStart;\n logger.warn(COMPONENT, `/api/eval/run TIMEOUT suite=${suite} elapsedMs=${elapsed} timeoutMs=${timeoutMs}`);\n try { recordEvalTimeout(suite); } catch { /* metrics best-effort */ }\n res.status(504).json({\n suite,\n passed: 0,\n total: 0,\n results: [],\n timedOut: true,\n timeoutMs,\n elapsedMs: elapsed,\n error: `Eval suite '${suite}' timed out after ${timeoutMs}ms`,\n });\n return;\n }\n\n // Publish to Prometheus so suite regressions surface in /metrics over time.\n try { recordEvalSuiteResult(suite, result.passed, result.total); } catch { /* metrics best-effort */ }\n const elapsed = Date.now() - tStart;\n logger.info(COMPONENT, `/api/eval/run DONE suite=${suite} passed=${result.passed}/${result.total} elapsedMs=${elapsed}`);\n res.json(result);\n } catch (e) {\n const err = e as Error;\n const elapsed = Date.now() - tStart;\n logger.warn(COMPONENT, `/api/eval/run ERROR suite=${requestedSuite || '(none)'} elapsedMs=${elapsed} message=${err.message}`);\n try { recordEvalError(requestedSuite || 'unknown', err.name || 'unknown'); } catch { /* metrics best-effort */ }\n // Return the actual message — eval is an internal/admin endpoint, not\n // user-facing chat. Generic \"something went wrong\" hid real bugs from\n // the CI gate.\n res.status(500).json({\n suite: requestedSuite || undefined,\n error: err.message || 'Eval run failed',\n errorClass: err.name || 'Error',\n });\n }\n });\n\n router.get('/eval/suites', async (_req, res) => {\n res.json({ suites: [\n 'widget-creation', 'safety', 'tool-routing', 'gate-format',\n 'pipeline', 'adversarial', 'tool-routing-v2', 'session',\n 'widget-v2', 'gate-format-v2', 'content',\n ] });\n });\n\n return router;\n}\n"],"mappings":"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","names":["result","elapsed"]}
@@ -87,6 +87,7 @@ import { createMeshRouter } from "./routes/mesh.js";
87
87
  import { createTestsRouter } from "./routes/tests.js";
88
88
  import { createSystemRouter } from "./routes/systemRouter.js";
89
89
  import { createVoiceRouter } from "./routes/voiceRouter.js";
90
+ import { createTelephonyRouter } from "./routes/telephony.js";
90
91
  import { createSocialRouter } from "./routes/socialRouter.js";
91
92
  import { createWatchRouter } from "./routes/watchRouter.js";
92
93
  import { setupSSEFlush } from "../utils/sseFlush.js";
@@ -998,6 +999,10 @@ async function startGateway(options) {
998
999
  next();
999
1000
  return;
1000
1001
  }
1002
+ if (req.path === "/telephony/dograh/inbound") {
1003
+ next();
1004
+ return;
1005
+ }
1001
1006
  const header = req.headers.authorization;
1002
1007
  const token = header?.startsWith("Bearer ") ? header.slice(7) : req.query.token;
1003
1008
  if (isValidToken(token, cfg)) {
@@ -1718,6 +1723,7 @@ async function startGateway(options) {
1718
1723
  onUserMessage: (id, content) => missionHandleUserMessage(id, content),
1719
1724
  onStatusChange: (id, status) => missionHandleStatusChange(id, status)
1720
1725
  }));
1726
+ app.use("/api/telephony", createTelephonyRouter());
1721
1727
  reattachMissionBridgesOnStartup().catch((err) => {
1722
1728
  logger.warn("Gateway", `Mission bridge re-attach on boot failed: ${err.message}`);
1723
1729
  });