@hotmeshio/long-tail 0.10.13 → 0.11.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 (373) hide show
  1. package/README.md +2 -4
  2. package/build/api/escalations/analytics.d.ts +6 -0
  3. package/build/api/escalations/analytics.js +123 -0
  4. package/build/api/escalations/index.d.ts +1 -0
  5. package/build/api/escalations/index.js +4 -1
  6. package/build/api/escalations/metadata.js +1 -1
  7. package/build/api/escalations/resolve.js +4 -4
  8. package/build/api/roles.d.ts +4 -0
  9. package/build/api/roles.js +8 -0
  10. package/build/api/scan-codes/choice.d.ts +11 -0
  11. package/build/api/scan-codes/choice.js +136 -0
  12. package/build/api/scan-codes/context.d.ts +24 -0
  13. package/build/api/scan-codes/context.js +86 -0
  14. package/build/api/scan-codes/execute.d.ts +1 -0
  15. package/build/api/scan-codes/execute.js +49 -244
  16. package/build/api/scan-codes/identity.d.ts +18 -0
  17. package/build/api/scan-codes/identity.js +116 -0
  18. package/build/api/scan-codes/index.d.ts +2 -0
  19. package/build/api/scan-codes/index.js +6 -1
  20. package/build/api/scan-codes/locate.d.ts +14 -0
  21. package/build/api/scan-codes/locate.js +127 -0
  22. package/build/api/scan-codes/present.d.ts +9 -0
  23. package/build/api/scan-codes/present.js +59 -0
  24. package/build/api/scan-codes/verbs.d.ts +15 -0
  25. package/build/api/scan-codes/verbs.js +205 -0
  26. package/build/bin/ltc.js +33 -0
  27. package/build/lib/cli/commands/escalations.d.ts +31 -0
  28. package/build/lib/cli/commands/escalations.js +88 -0
  29. package/build/lib/db/schemas/028_role_entity_facet.sql +18 -0
  30. package/build/lib/db/schemas/029_scan_scheme_kind.sql +31 -0
  31. package/build/modules/config.d.ts +5 -0
  32. package/build/modules/config.js +12 -0
  33. package/build/routes/escalations/acting.d.ts +12 -0
  34. package/build/routes/escalations/acting.js +25 -0
  35. package/build/routes/escalations/analytics.d.ts +8 -0
  36. package/build/routes/escalations/analytics.js +63 -0
  37. package/build/routes/escalations/index.js +3 -0
  38. package/build/routes/escalations/resolve.js +8 -1
  39. package/build/routes/escalations/single.js +13 -4
  40. package/build/routes/roles.js +1 -1
  41. package/build/routes/scan-codes.js +28 -2
  42. package/build/scripts/ortho-populate.js +14 -0
  43. package/build/scripts/printer-cycle.d.ts +25 -0
  44. package/build/scripts/printer-cycle.js +169 -0
  45. package/build/scripts/twin-farm.js +19 -5
  46. package/build/sdk/index.d.ts +2 -0
  47. package/build/sdk/index.js +3 -0
  48. package/build/services/escalation/aggregate-sql.d.ts +36 -0
  49. package/build/services/escalation/aggregate-sql.js +253 -0
  50. package/build/services/escalation/aggregate-validate.d.ts +57 -0
  51. package/build/services/escalation/aggregate-validate.js +218 -0
  52. package/build/services/escalation/aggregates.d.ts +26 -0
  53. package/build/services/escalation/aggregates.js +204 -0
  54. package/build/services/escalation/client.js +57 -21
  55. package/build/services/escalation/facet-sql.js +17 -0
  56. package/build/services/escalation/index.d.ts +2 -0
  57. package/build/services/escalation/index.js +8 -1
  58. package/build/services/escalation/metrics-cache.d.ts +18 -0
  59. package/build/services/escalation/metrics-cache.js +47 -1
  60. package/build/services/escalation/queries.js +2 -0
  61. package/build/services/escalation/resolver-validation.d.ts +1 -1
  62. package/build/services/escalation/resolver-validation.js +12 -5
  63. package/build/services/escalation/submit-guard.d.ts +29 -0
  64. package/build/services/escalation/submit-guard.js +57 -0
  65. package/build/services/iam/acting-identity.d.ts +16 -0
  66. package/build/services/iam/acting-identity.js +60 -0
  67. package/build/services/role/index.js +2 -0
  68. package/build/services/role/sql.d.ts +10 -2
  69. package/build/services/role/sql.js +14 -1
  70. package/build/services/role/types.d.ts +16 -0
  71. package/build/services/scan-code/read.js +4 -0
  72. package/build/services/scan-code/sql.d.ts +4 -4
  73. package/build/services/scan-code/sql.js +14 -8
  74. package/build/services/scan-code/validate.d.ts +2 -0
  75. package/build/services/scan-code/validate.js +86 -0
  76. package/build/services/scan-code/write.d.ts +4 -0
  77. package/build/services/scan-code/write.js +16 -2
  78. package/build/services/user/crud.d.ts +6 -0
  79. package/build/services/user/crud.js +16 -0
  80. package/build/services/user/index.d.ts +1 -1
  81. package/build/services/user/index.js +2 -1
  82. package/build/services/user/sql.d.ts +8 -0
  83. package/build/services/user/sql.js +9 -1
  84. package/build/shared/form-validation/index.d.ts +4 -0
  85. package/build/shared/form-validation/index.js +4 -0
  86. package/build/shared/form-validation/x-lt-labels.d.ts +28 -0
  87. package/build/shared/form-validation/x-lt-labels.js +42 -0
  88. package/build/shared/form-validation/x-lt-query.d.ts +56 -0
  89. package/build/shared/form-validation/x-lt-query.js +49 -0
  90. package/build/shared/form-validation/x-lt-submit-guard.d.ts +46 -0
  91. package/build/shared/form-validation/x-lt-submit-guard.js +25 -0
  92. package/build/shared/form-validation/x-lt-submit-on-claim.d.ts +22 -0
  93. package/build/shared/form-validation/x-lt-submit-on-claim.js +28 -0
  94. package/build/system/mcp-servers/admin/escalations.js +34 -0
  95. package/build/system/mcp-servers/admin/scan-codes.js +17 -1
  96. package/build/system/mcp-servers/admin/schemas.d.ts +643 -25
  97. package/build/system/mcp-servers/admin/schemas.js +127 -19
  98. package/build/system/mcp-servers/admin/users.js +3 -1
  99. package/build/system/mcp-servers/human-queue-schemas.d.ts +2 -2
  100. package/build/system/seed/tool-manifests-admin.d.ts +2283 -0
  101. package/build/system/seed/tool-manifests-admin.js +8 -5
  102. package/build/tsconfig.tsbuildinfo +1 -1
  103. package/build/types/analytics.d.ts +154 -0
  104. package/build/types/analytics.js +14 -0
  105. package/build/types/facets.d.ts +13 -0
  106. package/build/types/index.d.ts +5 -2
  107. package/build/types/index.js +5 -1
  108. package/build/types/scan-choice.d.ts +49 -0
  109. package/build/types/scan-choice.js +9 -0
  110. package/build/types/scan-code.d.ts +72 -1
  111. package/build/types/scan-code.js +30 -1
  112. package/dashboard/dist/assets/{AdminDashboard-Bv3pHBzn.js → AdminDashboard-DJ3LJQ09.js} +2 -2
  113. package/dashboard/dist/assets/{AdminDashboard-Bv3pHBzn.js.map → AdminDashboard-DJ3LJQ09.js.map} +1 -1
  114. package/dashboard/dist/assets/{AgentConfigPage-Dk2zlvDb.js → AgentConfigPage-BMMvaD0z.js} +2 -2
  115. package/dashboard/dist/assets/{AgentConfigPage-Dk2zlvDb.js.map → AgentConfigPage-BMMvaD0z.js.map} +1 -1
  116. package/dashboard/dist/assets/{AgentDetailPage-DUno6EGs.js → AgentDetailPage-C6wfsnRe.js} +2 -2
  117. package/dashboard/dist/assets/{AgentDetailPage-DUno6EGs.js.map → AgentDetailPage-C6wfsnRe.js.map} +1 -1
  118. package/dashboard/dist/assets/AgentsPage-Cwqka7nE.js +2 -0
  119. package/dashboard/dist/assets/{AgentsPage-CToVu1qj.js.map → AgentsPage-Cwqka7nE.js.map} +1 -1
  120. package/dashboard/dist/assets/AvailableEscalationsPage-CVlG_AVC.js +2 -0
  121. package/dashboard/dist/assets/AvailableEscalationsPage-CVlG_AVC.js.map +1 -0
  122. package/dashboard/dist/assets/{BotPicker-DQOpVB4M.js → BotPicker-BBkJBXZB.js} +2 -2
  123. package/dashboard/dist/assets/{BotPicker-DQOpVB4M.js.map → BotPicker-BBkJBXZB.js.map} +1 -1
  124. package/dashboard/dist/assets/{CapabilitiesPage-Dmf46PUJ.js → CapabilitiesPage-wxmE4917.js} +2 -2
  125. package/dashboard/dist/assets/{CapabilitiesPage-Dmf46PUJ.js.map → CapabilitiesPage-wxmE4917.js.map} +1 -1
  126. package/dashboard/dist/assets/{CollapsibleSection-CDADefUY.js → CollapsibleSection-B3FsrHwH.js} +2 -2
  127. package/dashboard/dist/assets/{CollapsibleSection-CDADefUY.js.map → CollapsibleSection-B3FsrHwH.js.map} +1 -1
  128. package/dashboard/dist/assets/CountdownTimer-B3nCqJdC.js +2 -0
  129. package/dashboard/dist/assets/CountdownTimer-B3nCqJdC.js.map +1 -0
  130. package/dashboard/dist/assets/{CredentialsPage-8LIEvomi.js → CredentialsPage-Bv875IHe.js} +2 -2
  131. package/dashboard/dist/assets/{CredentialsPage-8LIEvomi.js.map → CredentialsPage-Bv875IHe.js.map} +1 -1
  132. package/dashboard/dist/assets/{CronLabel-CK3WSsM5.js → CronLabel-CSzjWuwj.js} +2 -2
  133. package/dashboard/dist/assets/{CronLabel-CK3WSsM5.js.map → CronLabel-CSzjWuwj.js.map} +1 -1
  134. package/dashboard/dist/assets/{CustomDurationPicker-FPmlA6cE.js → CustomDurationPicker-CCpqf3e4.js} +2 -2
  135. package/dashboard/dist/assets/{CustomDurationPicker-FPmlA6cE.js.map → CustomDurationPicker-CCpqf3e4.js.map} +1 -1
  136. package/dashboard/dist/assets/{DropZone-CL9gA7pu.js → DropZone-CJkk-U3o.js} +2 -2
  137. package/dashboard/dist/assets/{DropZone-CL9gA7pu.js.map → DropZone-CJkk-U3o.js.map} +1 -1
  138. package/dashboard/dist/assets/{ElapsedCell-BZVkVxE0.js → ElapsedCell-WV6pUaKo.js} +2 -2
  139. package/dashboard/dist/assets/{ElapsedCell-BZVkVxE0.js.map → ElapsedCell-WV6pUaKo.js.map} +1 -1
  140. package/dashboard/dist/assets/EntityTimelinePanel-DrL86Yrf.js +2 -0
  141. package/dashboard/dist/assets/EntityTimelinePanel-DrL86Yrf.js.map +1 -0
  142. package/dashboard/dist/assets/{EscalationListSchemaPage-kYLdUtZX.js → EscalationListSchemaPage-CTQCALw4.js} +3 -3
  143. package/dashboard/dist/assets/{EscalationListSchemaPage-kYLdUtZX.js.map → EscalationListSchemaPage-CTQCALw4.js.map} +1 -1
  144. package/dashboard/dist/assets/{EscalationSchemaPage-kUmCDivo.js → EscalationSchemaPage-D_UgP0pv.js} +3 -3
  145. package/dashboard/dist/assets/{EscalationSchemaPage-kUmCDivo.js.map → EscalationSchemaPage-D_UgP0pv.js.map} +1 -1
  146. package/dashboard/dist/assets/{EscalationsOverview-D17auO-m.js → EscalationsOverview-DuIUyxr0.js} +2 -2
  147. package/dashboard/dist/assets/{EscalationsOverview-D17auO-m.js.map → EscalationsOverview-DuIUyxr0.js.map} +1 -1
  148. package/dashboard/dist/assets/EventTable-BV6unphX.js +2 -0
  149. package/dashboard/dist/assets/EventTable-BV6unphX.js.map +1 -0
  150. package/dashboard/dist/assets/FilterBar-DYFgzXNB.js +2 -0
  151. package/dashboard/dist/assets/FilterBar-DYFgzXNB.js.map +1 -0
  152. package/dashboard/dist/assets/{GraphInvokePage-D11uOnZi.js → GraphInvokePage-BGHVUipJ.js} +2 -2
  153. package/dashboard/dist/assets/{GraphInvokePage-D11uOnZi.js.map → GraphInvokePage-BGHVUipJ.js.map} +1 -1
  154. package/dashboard/dist/assets/HomePage-B-S2cK3E.js +2 -0
  155. package/dashboard/dist/assets/{HomePage-Bcq10Bxx.js.map → HomePage-B-S2cK3E.js.map} +1 -1
  156. package/dashboard/dist/assets/ListToolbar-Bl7F3A28.js +2 -0
  157. package/dashboard/dist/assets/{ListToolbar-IPXsTb3Q.js.map → ListToolbar-Bl7F3A28.js.map} +1 -1
  158. package/dashboard/dist/assets/{McpOverview-Dx2j0KkF.js → McpOverview-_T9IsCD_.js} +2 -2
  159. package/dashboard/dist/assets/{McpOverview-Dx2j0KkF.js.map → McpOverview-_T9IsCD_.js.map} +1 -1
  160. package/dashboard/dist/assets/{McpQueryDetailPage-CzF_bna8.js → McpQueryDetailPage-DtsVqKPw.js} +2 -2
  161. package/dashboard/dist/assets/{McpQueryDetailPage-CzF_bna8.js.map → McpQueryDetailPage-DtsVqKPw.js.map} +1 -1
  162. package/dashboard/dist/assets/McpQueryPage-DX5u-1bx.js +2 -0
  163. package/dashboard/dist/assets/{McpQueryPage-CNsuiw_q.js.map → McpQueryPage-DX5u-1bx.js.map} +1 -1
  164. package/dashboard/dist/assets/McpRunDetailPage-BhcqqtYS.js +2 -0
  165. package/dashboard/dist/assets/McpRunDetailPage-BhcqqtYS.js.map +1 -0
  166. package/dashboard/dist/assets/{McpRunsPage-CK96s3Nt.js → McpRunsPage-L-1y8wnX.js} +2 -2
  167. package/dashboard/dist/assets/{McpRunsPage-CK96s3Nt.js.map → McpRunsPage-L-1y8wnX.js.map} +1 -1
  168. package/dashboard/dist/assets/{NamespacePill-B9Vc_Pz2.js → NamespacePill-in1SLd0W.js} +2 -2
  169. package/dashboard/dist/assets/{NamespacePill-B9Vc_Pz2.js.map → NamespacePill-in1SLd0W.js.map} +1 -1
  170. package/dashboard/dist/assets/OperationsPage-i8KcDpKo.js +3 -0
  171. package/dashboard/dist/assets/OperationsPage-i8KcDpKo.js.map +1 -0
  172. package/dashboard/dist/assets/OperatorDashboard-CIbDzOlh.js +2 -0
  173. package/dashboard/dist/assets/{OperatorDashboard-C-jE0IRA.js.map → OperatorDashboard-CIbDzOlh.js.map} +1 -1
  174. package/dashboard/dist/assets/{PageHeader-Ce4GcIYo.js → PageHeader-BJgWMZtw.js} +2 -2
  175. package/dashboard/dist/assets/{PageHeader-Ce4GcIYo.js.map → PageHeader-BJgWMZtw.js.map} +1 -1
  176. package/dashboard/dist/assets/{PageHeaderWithStats-Ck0KZvXD.js → PageHeaderWithStats-Ccio4vyG.js} +2 -2
  177. package/dashboard/dist/assets/{PageHeaderWithStats-Ck0KZvXD.js.map → PageHeaderWithStats-Ccio4vyG.js.map} +1 -1
  178. package/dashboard/dist/assets/{PersonaDetailPage-CJ6jqo7h.js → PersonaDetailPage-BOaf0TW1.js} +2 -2
  179. package/dashboard/dist/assets/{PersonaDetailPage-CJ6jqo7h.js.map → PersonaDetailPage-BOaf0TW1.js.map} +1 -1
  180. package/dashboard/dist/assets/{PersonasPage-D8hL3kL5.js → PersonasPage-DSdRb90f.js} +2 -2
  181. package/dashboard/dist/assets/{PersonasPage-D8hL3kL5.js.map → PersonasPage-DSdRb90f.js.map} +1 -1
  182. package/dashboard/dist/assets/{ProcessDetailPage-Vnx8Z5_y.js → ProcessDetailPage-DN9NqfeP.js} +2 -2
  183. package/dashboard/dist/assets/{ProcessDetailPage-Vnx8Z5_y.js.map → ProcessDetailPage-DN9NqfeP.js.map} +1 -1
  184. package/dashboard/dist/assets/{ProcessesListPage-DRYtvS3N.js → ProcessesListPage-CfQEWaD4.js} +2 -2
  185. package/dashboard/dist/assets/{ProcessesListPage-DRYtvS3N.js.map → ProcessesListPage-CfQEWaD4.js.map} +1 -1
  186. package/dashboard/dist/assets/RoleDetailPage-BC5HL7ls.js +8 -0
  187. package/dashboard/dist/assets/RoleDetailPage-BC5HL7ls.js.map +1 -0
  188. package/dashboard/dist/assets/{RolePill-CnuBPhPw.js → RolePill-BNAP-uK6.js} +2 -2
  189. package/dashboard/dist/assets/{RolePill-CnuBPhPw.js.map → RolePill-BNAP-uK6.js.map} +1 -1
  190. package/dashboard/dist/assets/RolesPage-DCnW0VmD.js +2 -0
  191. package/dashboard/dist/assets/RolesPage-DCnW0VmD.js.map +1 -0
  192. package/dashboard/dist/assets/RunAsSelector-twbuX7fB.js +2 -0
  193. package/dashboard/dist/assets/{RunAsSelector-CS3KQ-BF.js.map → RunAsSelector-twbuX7fB.js.map} +1 -1
  194. package/dashboard/dist/assets/ScanCodesPage-CPNg6K--.js +2 -0
  195. package/dashboard/dist/assets/ScanCodesPage-CPNg6K--.js.map +1 -0
  196. package/dashboard/dist/assets/ScanSchemeDetailPage-DyLmCamO.js +2 -0
  197. package/dashboard/dist/assets/ScanSchemeDetailPage-DyLmCamO.js.map +1 -0
  198. package/dashboard/dist/assets/ScanStationPage-DoZccU5B.js +2 -0
  199. package/dashboard/dist/assets/ScanStationPage-DoZccU5B.js.map +1 -0
  200. package/dashboard/dist/assets/{ScopeBadge-BUpYpS3x.js → ScopeBadge-BG7KkZ8Q.js} +2 -2
  201. package/dashboard/dist/assets/{ScopeBadge-BUpYpS3x.js.map → ScopeBadge-BG7KkZ8Q.js.map} +1 -1
  202. package/dashboard/dist/assets/SegmentedTabs-DXNzY05q.js +2 -0
  203. package/dashboard/dist/assets/SegmentedTabs-DXNzY05q.js.map +1 -0
  204. package/dashboard/dist/assets/StickyPagination-DIeYMPPd.js +2 -0
  205. package/dashboard/dist/assets/StickyPagination-DIeYMPPd.js.map +1 -0
  206. package/dashboard/dist/assets/{StreamMessageDetail-DyJyZwBl.js → StreamMessageDetail-gU4CUeQI.js} +2 -2
  207. package/dashboard/dist/assets/{StreamMessageDetail-DyJyZwBl.js.map → StreamMessageDetail-gU4CUeQI.js.map} +1 -1
  208. package/dashboard/dist/assets/{SwimlaneTimeline-BlzV74V5.js → SwimlaneTimeline-Br-nBA4l.js} +2 -2
  209. package/dashboard/dist/assets/{SwimlaneTimeline-BlzV74V5.js.map → SwimlaneTimeline-Br-nBA4l.js.map} +1 -1
  210. package/dashboard/dist/assets/{TagInput-BbZ25nNe.js → TagInput-CvM5n6ww.js} +2 -2
  211. package/dashboard/dist/assets/{TagInput-BbZ25nNe.js.map → TagInput-CvM5n6ww.js.map} +1 -1
  212. package/dashboard/dist/assets/{TaskDetailPage-B0p_Z4J_.js → TaskDetailPage-_9cr_R0u.js} +2 -2
  213. package/dashboard/dist/assets/{TaskDetailPage-B0p_Z4J_.js.map → TaskDetailPage-_9cr_R0u.js.map} +1 -1
  214. package/dashboard/dist/assets/{TaskQueuePill-D0QKBrN3.js → TaskQueuePill-D4JPHXOq.js} +2 -2
  215. package/dashboard/dist/assets/{TaskQueuePill-D0QKBrN3.js.map → TaskQueuePill-D4JPHXOq.js.map} +1 -1
  216. package/dashboard/dist/assets/{TasksListPage-CiGR2QiQ.js → TasksListPage-DVENC9Jp.js} +2 -2
  217. package/dashboard/dist/assets/{TasksListPage-CiGR2QiQ.js.map → TasksListPage-DVENC9Jp.js.map} +1 -1
  218. package/dashboard/dist/assets/{TimeAgo-D_DA9ucC.js → TimeAgo-C9yYnOui.js} +2 -2
  219. package/dashboard/dist/assets/{TimeAgo-D_DA9ucC.js.map → TimeAgo-C9yYnOui.js.map} +1 -1
  220. package/dashboard/dist/assets/{TimestampCell-C7rkC9nh.js → TimestampCell-DoB8cYgw.js} +2 -2
  221. package/dashboard/dist/assets/{TimestampCell-C7rkC9nh.js.map → TimestampCell-DoB8cYgw.js.map} +1 -1
  222. package/dashboard/dist/assets/{ToolPill-8xY9rkoc.js → ToolPill-BUjbC-Tj.js} +2 -2
  223. package/dashboard/dist/assets/{ToolPill-8xY9rkoc.js.map → ToolPill-BUjbC-Tj.js.map} +1 -1
  224. package/dashboard/dist/assets/{ToolTestPanel-E8efYGYs.js → ToolTestPanel-CBtsIP8K.js} +2 -2
  225. package/dashboard/dist/assets/{ToolTestPanel-E8efYGYs.js.map → ToolTestPanel-CBtsIP8K.js.map} +1 -1
  226. package/dashboard/dist/assets/{TopicDetailPage-B455XrRc.js → TopicDetailPage-mR4Jaz83.js} +4 -4
  227. package/dashboard/dist/assets/{TopicDetailPage-B455XrRc.js.map → TopicDetailPage-mR4Jaz83.js.map} +1 -1
  228. package/dashboard/dist/assets/TopicsPage-CP7HFxIC.js +2 -0
  229. package/dashboard/dist/assets/{TopicsPage-1VAc29Qw.js.map → TopicsPage-CP7HFxIC.js.map} +1 -1
  230. package/dashboard/dist/assets/{UserName-DikzjQHJ.js → UserName-BnLF6yhz.js} +2 -2
  231. package/dashboard/dist/assets/{UserName-DikzjQHJ.js.map → UserName-BnLF6yhz.js.map} +1 -1
  232. package/dashboard/dist/assets/WorkflowExecutionPage-xL956RZi.js +2 -0
  233. package/dashboard/dist/assets/WorkflowExecutionPage-xL956RZi.js.map +1 -0
  234. package/dashboard/dist/assets/{WorkflowPill-Da406nEj.js → WorkflowPill-tXU0-eXu.js} +2 -2
  235. package/dashboard/dist/assets/{WorkflowPill-Da406nEj.js.map → WorkflowPill-tXU0-eXu.js.map} +1 -1
  236. package/dashboard/dist/assets/WorkflowsDashboard-B-u7zFKM.js +2 -0
  237. package/dashboard/dist/assets/{WorkflowsDashboard-BMQd_0tW.js.map → WorkflowsDashboard-B-u7zFKM.js.map} +1 -1
  238. package/dashboard/dist/assets/{WorkflowsOverview-CdxjVifp.js → WorkflowsOverview-CqZVarK-.js} +2 -2
  239. package/dashboard/dist/assets/{WorkflowsOverview-CdxjVifp.js.map → WorkflowsOverview-CqZVarK-.js.map} +1 -1
  240. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DNVffIXa.js → YamlWorkflowDetailPage-DiFLavW0.js} +10 -10
  241. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DNVffIXa.js.map → YamlWorkflowDetailPage-DiFLavW0.js.map} +1 -1
  242. package/dashboard/dist/assets/{YamlWorkflowsPage-BGP8wuos.js → YamlWorkflowsPage-BuQM6uZA.js} +2 -2
  243. package/dashboard/dist/assets/{YamlWorkflowsPage-BGP8wuos.js.map → YamlWorkflowsPage-BuQM6uZA.js.map} +1 -1
  244. package/dashboard/dist/assets/{agents-BZJLIJ4h.js → agents-DU9GPovp.js} +2 -2
  245. package/dashboard/dist/assets/{agents-BZJLIJ4h.js.map → agents-DU9GPovp.js.map} +1 -1
  246. package/dashboard/dist/assets/{bots-ClLcDGJb.js → bots-Cy-Hmg7w.js} +2 -2
  247. package/dashboard/dist/assets/{bots-ClLcDGJb.js.map → bots-Cy-Hmg7w.js.map} +1 -1
  248. package/dashboard/dist/assets/capabilities-sSJJqrp-.js +2 -0
  249. package/dashboard/dist/assets/{capabilities-IaCcS7ge.js.map → capabilities-sSJJqrp-.js.map} +1 -1
  250. package/dashboard/dist/assets/{controlplane-Zi4m_MSx.js → controlplane-TVCjDHQ-.js} +2 -2
  251. package/dashboard/dist/assets/{controlplane-Zi4m_MSx.js.map → controlplane-TVCjDHQ-.js.map} +1 -1
  252. package/dashboard/dist/assets/escalation-columns-BpkjKYGd.js +2 -0
  253. package/dashboard/dist/assets/escalation-columns-BpkjKYGd.js.map +1 -0
  254. package/dashboard/dist/assets/index-5TblaCXQ.css +1 -0
  255. package/dashboard/dist/assets/index-7BP6FaCZ.js +2 -0
  256. package/dashboard/dist/assets/{index-Dldi3Q3M.js.map → index-7BP6FaCZ.js.map} +1 -1
  257. package/dashboard/dist/assets/{index-BzkE48xJ.js → index-B7IaBPBg.js} +4 -4
  258. package/dashboard/dist/assets/{index-BzkE48xJ.js.map → index-B7IaBPBg.js.map} +1 -1
  259. package/dashboard/dist/assets/index-BE0phFw0.js +2 -0
  260. package/dashboard/dist/assets/{index-CLmZaNTK.js.map → index-BE0phFw0.js.map} +1 -1
  261. package/dashboard/dist/assets/{index-BqGnhkh0.js → index-BYNN9N1T.js} +2 -2
  262. package/dashboard/dist/assets/{index-BqGnhkh0.js.map → index-BYNN9N1T.js.map} +1 -1
  263. package/dashboard/dist/assets/{index-kiKZresb.js → index-CLPVqbVY.js} +2 -2
  264. package/dashboard/dist/assets/{index-kiKZresb.js.map → index-CLPVqbVY.js.map} +1 -1
  265. package/dashboard/dist/assets/{index-C3o4OisO.js → index-Cfv86_L9.js} +2 -2
  266. package/dashboard/dist/assets/{index-C3o4OisO.js.map → index-Cfv86_L9.js.map} +1 -1
  267. package/dashboard/dist/assets/{index-yGNt29V9.js → index-CtwbQ_-m.js} +2 -2
  268. package/dashboard/dist/assets/{index-yGNt29V9.js.map → index-CtwbQ_-m.js.map} +1 -1
  269. package/dashboard/dist/assets/index-DOai950R.js +3 -0
  270. package/dashboard/dist/assets/index-DOai950R.js.map +1 -0
  271. package/dashboard/dist/assets/index-DTtHNC-D.js +5 -0
  272. package/dashboard/dist/assets/{index-CvNBdkB1.js.map → index-DTtHNC-D.js.map} +1 -1
  273. package/dashboard/dist/assets/{index-CJu2PBNx.js → index-DsWnVyVN.js} +2 -2
  274. package/dashboard/dist/assets/{index-CJu2PBNx.js.map → index-DsWnVyVN.js.map} +1 -1
  275. package/dashboard/dist/assets/{index-D0zFReGL.js → index-Dy-MMYti.js} +2 -2
  276. package/dashboard/dist/assets/{index-D0zFReGL.js.map → index-Dy-MMYti.js.map} +1 -1
  277. package/dashboard/dist/assets/index-errgQT5x.js +65 -0
  278. package/dashboard/dist/assets/index-errgQT5x.js.map +1 -0
  279. package/dashboard/dist/assets/{index-CmnNqpWg.js → index-fgBDhnI6.js} +2 -2
  280. package/dashboard/dist/assets/{index-CmnNqpWg.js.map → index-fgBDhnI6.js.map} +1 -1
  281. package/dashboard/dist/assets/{knowledge-DWLxGTib.js → knowledge-arh4NfmL.js} +2 -2
  282. package/dashboard/dist/assets/{knowledge-DWLxGTib.js.map → knowledge-arh4NfmL.js.map} +1 -1
  283. package/dashboard/dist/assets/{mcp-B6AARZ2I.js → mcp-QGf5jHCo.js} +2 -2
  284. package/dashboard/dist/assets/{mcp-B6AARZ2I.js.map → mcp-QGf5jHCo.js.map} +1 -1
  285. package/dashboard/dist/assets/{mcp-query-DrkvBU3a.js → mcp-query-BEJN1nMZ.js} +2 -2
  286. package/dashboard/dist/assets/{mcp-query-DrkvBU3a.js.map → mcp-query-BEJN1nMZ.js.map} +1 -1
  287. package/dashboard/dist/assets/{personas-Cmz2SAEU.js → personas-DYtw1COf.js} +2 -2
  288. package/dashboard/dist/assets/{personas-Cmz2SAEU.js.map → personas-DYtw1COf.js.map} +1 -1
  289. package/dashboard/dist/assets/{pipelines-CSgfVCMy.js → pipelines-Byi6PnbE.js} +2 -2
  290. package/dashboard/dist/assets/{pipelines-CSgfVCMy.js.map → pipelines-Byi6PnbE.js.map} +1 -1
  291. package/dashboard/dist/assets/{tasks-UOtUkDk-.js → tasks-D0SQdENz.js} +2 -2
  292. package/dashboard/dist/assets/{tasks-UOtUkDk-.js.map → tasks-D0SQdENz.js.map} +1 -1
  293. package/dashboard/dist/assets/{topics-Df5hGh6o.js → topics-DgDv5ZXP.js} +2 -2
  294. package/dashboard/dist/assets/{topics-Df5hGh6o.js.map → topics-DgDv5ZXP.js.map} +1 -1
  295. package/dashboard/dist/assets/useEventHooks-DZaYzE7s.js +2 -0
  296. package/dashboard/dist/assets/{useEventHooks-PDbuSJ12.js.map → useEventHooks-DZaYzE7s.js.map} +1 -1
  297. package/dashboard/dist/assets/{useNamespace-tZSOlge3.js → useNamespace-C1OqiNXN.js} +2 -2
  298. package/dashboard/dist/assets/{useNamespace-tZSOlge3.js.map → useNamespace-C1OqiNXN.js.map} +1 -1
  299. package/dashboard/dist/assets/{useYamlActivityEvents-CINLzE32.js → useYamlActivityEvents-CkmXDm-Z.js} +2 -2
  300. package/dashboard/dist/assets/{useYamlActivityEvents-CINLzE32.js.map → useYamlActivityEvents-CkmXDm-Z.js.map} +1 -1
  301. package/dashboard/dist/assets/{users-G8qwyZKS.js → users-D4DCFPH1.js} +2 -2
  302. package/dashboard/dist/assets/{users-G8qwyZKS.js.map → users-D4DCFPH1.js.map} +1 -1
  303. package/dashboard/dist/assets/{vendor-icons-BpZiWH2f.js → vendor-icons-BxuyFxH2.js} +156 -136
  304. package/dashboard/dist/assets/vendor-icons-BxuyFxH2.js.map +1 -0
  305. package/dashboard/dist/assets/{workflows-DbPqi1TW.js → workflows-qyc0ku_5.js} +2 -2
  306. package/dashboard/dist/assets/{workflows-DbPqi1TW.js.map → workflows-qyc0ku_5.js.map} +1 -1
  307. package/dashboard/dist/assets/{yaml-workflows-DozD8hpR.js → yaml-workflows-DWFww9Xn.js} +2 -2
  308. package/dashboard/dist/assets/{yaml-workflows-DozD8hpR.js.map → yaml-workflows-DWFww9Xn.js.map} +1 -1
  309. package/dashboard/dist/index.html +3 -3
  310. package/docs/api/http/escalations.md +133 -0
  311. package/docs/api/http/roles.md +6 -0
  312. package/docs/api/mcp/admin.md +53 -0
  313. package/docs/api/sdk/escalations.md +58 -0
  314. package/docs/cli.md +11 -0
  315. package/docs/dashboard.md +22 -20
  316. package/docs/data.md +12 -4
  317. package/docs/design-principles.md +9 -7
  318. package/docs/escalation-analytics.md +251 -0
  319. package/docs/hitl/roles.md +1 -1
  320. package/docs/hitl/x-lt-embed.md +8 -4
  321. package/docs/hitl/x-lt-footer.md +67 -0
  322. package/docs/hitl/x-lt-list-schema.md +10 -3
  323. package/docs/hitl-guide.md +11 -2
  324. package/docs/iam.md +12 -0
  325. package/docs/operations.md +17 -0
  326. package/docs/scan-codes.md +112 -0
  327. package/docs/sdk.md +1 -1
  328. package/package.json +2 -1
  329. package/dashboard/dist/assets/AgentsPage-CToVu1qj.js +0 -2
  330. package/dashboard/dist/assets/AvailableEscalationsPage-DldboL4W.js +0 -2
  331. package/dashboard/dist/assets/AvailableEscalationsPage-DldboL4W.js.map +0 -1
  332. package/dashboard/dist/assets/CountdownTimer-DPjIkerB.js +0 -2
  333. package/dashboard/dist/assets/CountdownTimer-DPjIkerB.js.map +0 -1
  334. package/dashboard/dist/assets/EventTable-CwPWl163.js +0 -2
  335. package/dashboard/dist/assets/EventTable-CwPWl163.js.map +0 -1
  336. package/dashboard/dist/assets/FilterBar-D7GghUsU.js +0 -2
  337. package/dashboard/dist/assets/FilterBar-D7GghUsU.js.map +0 -1
  338. package/dashboard/dist/assets/HomePage-Bcq10Bxx.js +0 -2
  339. package/dashboard/dist/assets/ListToolbar-IPXsTb3Q.js +0 -2
  340. package/dashboard/dist/assets/McpQueryPage-CNsuiw_q.js +0 -2
  341. package/dashboard/dist/assets/McpRunDetailPage-BJguEMS0.js +0 -2
  342. package/dashboard/dist/assets/McpRunDetailPage-BJguEMS0.js.map +0 -1
  343. package/dashboard/dist/assets/OperationsPage-DvWrLuH6.js +0 -2
  344. package/dashboard/dist/assets/OperationsPage-DvWrLuH6.js.map +0 -1
  345. package/dashboard/dist/assets/OperatorDashboard-C-jE0IRA.js +0 -2
  346. package/dashboard/dist/assets/RoleDetailPage-BKkUvTO-.js +0 -8
  347. package/dashboard/dist/assets/RoleDetailPage-BKkUvTO-.js.map +0 -1
  348. package/dashboard/dist/assets/RolesPage-BHzWK9n6.js +0 -2
  349. package/dashboard/dist/assets/RolesPage-BHzWK9n6.js.map +0 -1
  350. package/dashboard/dist/assets/RunAsSelector-CS3KQ-BF.js +0 -2
  351. package/dashboard/dist/assets/ScanCodesPage-BW4P4zWf.js +0 -2
  352. package/dashboard/dist/assets/ScanCodesPage-BW4P4zWf.js.map +0 -1
  353. package/dashboard/dist/assets/ScanSchemeDetailPage-BLykA4j-.js +0 -2
  354. package/dashboard/dist/assets/ScanSchemeDetailPage-BLykA4j-.js.map +0 -1
  355. package/dashboard/dist/assets/StickyPagination-hMrnLXde.js +0 -2
  356. package/dashboard/dist/assets/StickyPagination-hMrnLXde.js.map +0 -1
  357. package/dashboard/dist/assets/TopicsPage-1VAc29Qw.js +0 -2
  358. package/dashboard/dist/assets/WorkflowExecutionPage-C15GSAoe.js +0 -2
  359. package/dashboard/dist/assets/WorkflowExecutionPage-C15GSAoe.js.map +0 -1
  360. package/dashboard/dist/assets/WorkflowsDashboard-BMQd_0tW.js +0 -2
  361. package/dashboard/dist/assets/capabilities-IaCcS7ge.js +0 -2
  362. package/dashboard/dist/assets/escalation-columns-BY2mg9DY.js +0 -2
  363. package/dashboard/dist/assets/escalation-columns-BY2mg9DY.js.map +0 -1
  364. package/dashboard/dist/assets/index-CLmZaNTK.js +0 -2
  365. package/dashboard/dist/assets/index-CfAi1OsI.js +0 -65
  366. package/dashboard/dist/assets/index-CfAi1OsI.js.map +0 -1
  367. package/dashboard/dist/assets/index-CvNBdkB1.js +0 -5
  368. package/dashboard/dist/assets/index-Dldi3Q3M.js +0 -2
  369. package/dashboard/dist/assets/index-mKcQGi2B.js +0 -3
  370. package/dashboard/dist/assets/index-mKcQGi2B.js.map +0 -1
  371. package/dashboard/dist/assets/index-zQiUPfxA.css +0 -1
  372. package/dashboard/dist/assets/useEventHooks-PDbuSJ12.js +0 -2
  373. package/dashboard/dist/assets/vendor-icons-BpZiWH2f.js.map +0 -1
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // Analytics SQL — grouped/temporal builders over the escalation queue. Every
4
+ // escalation is one open interval [created_at, ended_at): ended_at is the
5
+ // instant the row left the live set (COALESCE(resolved_at, updated_at) for
6
+ // terminal rows — resolve stamps resolved_at, cancel/expire stamp updated_at
7
+ // in the same guarded transition). The three read modes are projections of
8
+ // that interval: membership at an instant, dwell over a window, and the
9
+ // per-entity timeline.
10
+ //
11
+ // Composition rules, same as facet-sql.ts: every value is parameterized;
12
+ // only validated column names / facet keys and the closed status whitelist
13
+ // are interpolated. Statuses inline as literals (never parameters) so the
14
+ // planner can prove the idx_hmsh_esc_ended_at partial predicate
15
+ // (status <> 'pending') from `status NOT IN ('pending', ...)`, and the
16
+ // terminal-interval branch is written explicitly (not COALESCE-folded) so it
17
+ // can BitmapOr with the pending stats index instead of scanning history.
18
+ // ---------------------------------------------------------------------------
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.AnalyticsInputError = void 0;
21
+ exports.buildAggregateQuery = buildAggregateQuery;
22
+ exports.buildTimelineQuery = buildTimelineQuery;
23
+ const facet_sql_1 = require("./facet-sql");
24
+ const aggregate_validate_1 = require("./aggregate-validate");
25
+ const types_1 = require("../../types");
26
+ var aggregate_validate_2 = require("./aggregate-validate");
27
+ Object.defineProperty(exports, "AnalyticsInputError", { enumerable: true, get: function () { return aggregate_validate_2.AnalyticsInputError; } });
28
+ /** SQL string literal — values come from our own lt_roles rows, escaped anyway. */
29
+ function sqlLiteral(value) {
30
+ return `'${value.replace(/'/g, "''")}'`;
31
+ }
32
+ /**
33
+ * The derived state label: roles sourcing states from their subtypes
34
+ * contribute COALESCE(subtype, role); everyone else IS their state.
35
+ */
36
+ function stateExpr(sources) {
37
+ const subtypeRoles = Object.keys(sources).filter((r) => sources[r] === 'subtype').sort();
38
+ if (subtypeRoles.length === 0)
39
+ return 'role';
40
+ return `CASE WHEN role IN (${subtypeRoles.map(sqlLiteral).join(', ')}) THEN COALESCE(subtype, role) ELSE role END`;
41
+ }
42
+ /** Validated statuses as an inline literal list, e.g. `'pending', 'resolved'`. */
43
+ function statusLiterals(statuses) {
44
+ return statuses.map((s) => `'${s}'`).join(', ');
45
+ }
46
+ /** The projected-facet output alias; the key is FACET_KEY-validated. */
47
+ function facetAlias(key) {
48
+ return `"facet_${key}"`;
49
+ }
50
+ /** The instant a row left the live set; NULL while live. */
51
+ function endedAtExpr(liveList) {
52
+ return `CASE WHEN status NOT IN (${liveList}) THEN COALESCE(resolved_at, updated_at) END`;
53
+ }
54
+ /**
55
+ * Interval-overlap predicate: the row was open at some point after `fromParam`
56
+ * (live rows are open now; terminal rows ended after it). Branch-explicit so
57
+ * each side has an index (stats_pending / ended_at partial).
58
+ */
59
+ function overlapsAfter(liveList, fromParam) {
60
+ return `(status IN (${liveList}) OR (status NOT IN (${liveList}) AND COALESCE(resolved_at, updated_at) > ${fromParam}))`;
61
+ }
62
+ /** ORDER BY over the RESULT groups: measure aliases and grouped keys only. */
63
+ function buildAggregateOrder(orderBy, measureKind, columns, facets, stateGrouped = false) {
64
+ if (!orderBy || !orderBy.length)
65
+ return 'sample_count DESC';
66
+ const parts = [];
67
+ for (const o of orderBy) {
68
+ const dir = o.direction === 'desc' ? 'DESC' : 'ASC';
69
+ if (o.field === 'state' && stateGrouped) {
70
+ parts.push(`state_label ${dir}`);
71
+ }
72
+ else if (o.field === 'count') {
73
+ if (measureKind !== 'membership') {
74
+ throw new aggregate_validate_1.AnalyticsInputError('orderBy "count" applies to membership; use "dwellSeconds" for dwell');
75
+ }
76
+ parts.push(`"count" ${dir}`);
77
+ }
78
+ else if (o.field === 'dwellSeconds') {
79
+ if (measureKind !== 'dwell') {
80
+ throw new aggregate_validate_1.AnalyticsInputError('orderBy "dwellSeconds" applies to dwell; use "count" for membership');
81
+ }
82
+ parts.push(`dwell_seconds ${dir}`);
83
+ }
84
+ else if (o.field === 'sampleCount') {
85
+ parts.push(`sample_count ${dir}`);
86
+ }
87
+ else if (columns.includes(o.field)) {
88
+ parts.push(`${o.field} ${dir}`);
89
+ }
90
+ else if (facets.includes(o.field)) {
91
+ parts.push(`${facetAlias(o.field)} ${dir} NULLS LAST`);
92
+ }
93
+ else {
94
+ throw new aggregate_validate_1.AnalyticsInputError(`orderBy field "${o.field}" is not a measure (count, dwellSeconds, sampleCount) or a grouped key`);
95
+ }
96
+ }
97
+ return parts.join(', ');
98
+ }
99
+ /**
100
+ * Build the grouped aggregate (membership or dwell) as one parameterized
101
+ * query. `query.entity` must be RESOLVED by the caller before building
102
+ * (aggregates.ts turns it into roles + opts.stateSources); the builder
103
+ * rejects an unresolved entity so it can never be silently ignored.
104
+ */
105
+ function buildAggregateQuery(input, opts = {}) {
106
+ const query = (0, aggregate_validate_1.requireCleanFilter)(input.query);
107
+ if (query.entity !== undefined) {
108
+ throw new aggregate_validate_1.AnalyticsInputError('query.entity must be resolved to roles before building SQL');
109
+ }
110
+ const { columns, facets, state } = (0, aggregate_validate_1.resolveGroupBy)(input.groupBy);
111
+ if (state && input.states !== undefined) {
112
+ throw new aggregate_validate_1.AnalyticsInputError('groupBy.state and states[] are two labeling mechanisms — use one: state derives labels from the roles\' entity_state_source');
113
+ }
114
+ if (state && !opts.stateSources) {
115
+ throw new aggregate_validate_1.AnalyticsInputError('groupBy.state requires a role scope — set query.entity (the derived system) or query.role/roles');
116
+ }
117
+ (0, aggregate_validate_1.validateStates)(input.states, columns, facets);
118
+ const live = (0, aggregate_validate_1.resolveLiveStatuses)(input.liveStatuses);
119
+ const liveList = statusLiterals(live);
120
+ const { pageLimit, offset } = (0, aggregate_validate_1.resolvePage)(input.limit, input.offset);
121
+ const measure = input.measure;
122
+ if (measure?.kind !== 'membership' && measure?.kind !== 'dwell') {
123
+ throw new aggregate_validate_1.AnalyticsInputError('measure.kind must be "membership" or "dwell"');
124
+ }
125
+ const params = [];
126
+ const where = (0, facet_sql_1.buildFacetWhere)(query, params);
127
+ const order = buildAggregateOrder(input.orderBy, measure.kind, columns, facets, state);
128
+ const groupSelect = [];
129
+ const groupKeys = [];
130
+ if (state) {
131
+ groupSelect.push(`${stateExpr(opts.stateSources)} AS state_label`);
132
+ groupKeys.push('state_label');
133
+ }
134
+ for (const c of columns) {
135
+ groupSelect.push(c);
136
+ groupKeys.push(c);
137
+ }
138
+ for (const key of facets) {
139
+ groupSelect.push(`(metadata->>'${key}') AS ${facetAlias(key)}`);
140
+ groupKeys.push(facetAlias(key));
141
+ }
142
+ const groupBySql = groupKeys.length ? `GROUP BY ${groupKeys.join(', ')}\n` : '';
143
+ if (measure.kind === 'membership') {
144
+ const asOf = (0, aggregate_validate_1.resolveAsOf)(measure.asOf);
145
+ if (input.distinctBy !== undefined)
146
+ (0, aggregate_validate_1.requireFacetKey)(input.distinctBy, 'distinctBy');
147
+ const countExpr = input.distinctBy
148
+ ? `count(DISTINCT (metadata->>'${input.distinctBy}'))::int`
149
+ : `count(*)::int`;
150
+ let intervalPred;
151
+ if (asOf === null) {
152
+ // Now-anchored: the live set IS the membership — same shape and index
153
+ // (stats_pending) as the Pace Board's live counts.
154
+ intervalPred = `status IN (${liveList})`;
155
+ }
156
+ else {
157
+ params.push(asOf.toISOString());
158
+ const t = `$${params.length}::timestamptz`;
159
+ intervalPred = `created_at <= ${t}\n AND ${overlapsAfter(liveList, t)}`;
160
+ }
161
+ params.push(pageLimit + 1, offset);
162
+ const sql = `SELECT ${[...groupSelect, `${countExpr} AS "count"`, 'count(*)::int AS sample_count'].join(',\n ')}
163
+ FROM public.lt_escalations
164
+ WHERE ${where}
165
+ AND ${intervalPred}
166
+ ${groupBySql}ORDER BY ${order}
167
+ LIMIT $${params.length - 1} OFFSET $${params.length}`;
168
+ return { sql, params, pageLimit, columns, facets, stateGrouped: state, measureKind: 'membership', nowAnchored: asOf === null };
169
+ }
170
+ // Dwell: clip each overlapping interval to the window, sum open-seconds.
171
+ if (input.distinctBy !== undefined) {
172
+ throw new aggregate_validate_1.AnalyticsInputError('distinctBy is a membership concept — for per-entity dwell, put the entity facet in groupBy.facets');
173
+ }
174
+ const win = (0, aggregate_validate_1.resolveWindow)(measure.window, 'measure.window');
175
+ if (!win)
176
+ throw new aggregate_validate_1.AnalyticsInputError('measure.window is required for dwell');
177
+ params.push(win.from.toISOString());
178
+ const fromP = `$${params.length}::timestamptz`;
179
+ params.push(win.to.toISOString());
180
+ const toP = `$${params.length}::timestamptz`;
181
+ const spanSelect = [
182
+ ...groupSelect,
183
+ `GREATEST(created_at, ${fromP}) AS s`,
184
+ // Open rows clamp to now, not to the window end: a caller may round the
185
+ // window end into the near future (cache-key stability), and an open row
186
+ // must not accrue seconds that haven't elapsed.
187
+ `LEAST(COALESCE(${endedAtExpr(liveList)}, LEAST(NOW(), ${toP})), ${toP}) AS e`,
188
+ ];
189
+ params.push(pageLimit + 1, offset);
190
+ const sql = `WITH span AS (
191
+ SELECT ${spanSelect.join(',\n ')}
192
+ FROM public.lt_escalations
193
+ WHERE ${where}
194
+ AND created_at < ${toP}
195
+ AND ${overlapsAfter(liveList, fromP)}
196
+ )
197
+ SELECT ${[...groupKeys, 'SUM(EXTRACT(EPOCH FROM (e - s)))::float8 AS dwell_seconds', 'count(*)::int AS sample_count'].join(',\n ')}
198
+ FROM span
199
+ ${groupBySql}ORDER BY ${order}
200
+ LIMIT $${params.length - 1} OFFSET $${params.length}`;
201
+ return { sql, params, pageLimit, columns, facets, stateGrouped: state, measureKind: 'dwell', nowAnchored: win.nowAnchored };
202
+ }
203
+ /** Build the single-entity interval sequence, created_at-ordered, gaps preserved. */
204
+ function buildTimelineQuery(input) {
205
+ (0, aggregate_validate_1.requireFacetKey)(input?.facet?.key, 'facet.key');
206
+ if (typeof input.facet.value !== 'string' || input.facet.value.length === 0) {
207
+ throw new aggregate_validate_1.AnalyticsInputError('facet.value must be a non-empty string (entity facets are matched as JSON strings)');
208
+ }
209
+ const query = (0, aggregate_validate_1.requireCleanFilter)(input.query);
210
+ if (query.entity !== undefined) {
211
+ throw new aggregate_validate_1.AnalyticsInputError('query.entity must be resolved to roles before building SQL');
212
+ }
213
+ // select.columns narrows which columns the mapper surfaces; omitted = all.
214
+ const { columns, facets } = (0, aggregate_validate_1.resolveGroupBy)({
215
+ columns: input.select?.columns ?? [...types_1.GROUPABLE_COLUMNS],
216
+ facets: input.select?.facets,
217
+ });
218
+ const live = (0, aggregate_validate_1.resolveLiveStatuses)(input.liveStatuses);
219
+ const liveList = statusLiterals(live);
220
+ const win = (0, aggregate_validate_1.resolveWindow)(input.window);
221
+ const { order, before } = (0, aggregate_validate_1.resolveTimelinePage)(input.order, input.before);
222
+ const { pageLimit } = (0, aggregate_validate_1.resolvePage)(input.limit, undefined);
223
+ // GIN-served entity match; buildFacetWhere composes any extra filter after it.
224
+ const params = [JSON.stringify({ [input.facet.key]: input.facet.value })];
225
+ const clauses = [`metadata @> $1::jsonb`, (0, facet_sql_1.buildFacetWhere)(query, params)];
226
+ let clampEnd = 'NOW()';
227
+ if (win) {
228
+ params.push(win.from.toISOString());
229
+ const fromP = `$${params.length}::timestamptz`;
230
+ params.push(win.to.toISOString());
231
+ const toP = `$${params.length}::timestamptz`;
232
+ clauses.push(`created_at < ${toP}`, overlapsAfter(liveList, fromP));
233
+ clampEnd = `LEAST(NOW(), ${toP})`;
234
+ }
235
+ // The "load earlier" cursor: strictly-older intervals than the oldest the
236
+ // client already holds. One entity's rows are few; the sort is trivial.
237
+ if (before) {
238
+ params.push(before.toISOString());
239
+ clauses.push(`created_at < $${params.length}::timestamptz`);
240
+ }
241
+ const ended = endedAtExpr(liveList);
242
+ const facetSelect = facets.map((key) => `(metadata->>'${key}') AS ${facetAlias(key)}`);
243
+ params.push(pageLimit + 1);
244
+ const sql = `SELECT ${['role', 'subtype', 'status', ...facetSelect].join(', ')},
245
+ created_at AS started_at,
246
+ ${ended} AS ended_at,
247
+ EXTRACT(EPOCH FROM (COALESCE(${ended}, ${clampEnd}) - created_at))::float8 AS duration_seconds
248
+ FROM public.lt_escalations
249
+ WHERE ${clauses.join('\n AND ')}
250
+ ORDER BY created_at ${order === 'desc' ? 'DESC' : 'ASC'}
251
+ LIMIT $${params.length}`;
252
+ return { sql, params, pageLimit, columns, facets, nowAnchored: !win || win.nowAnchored };
253
+ }
@@ -0,0 +1,57 @@
1
+ import type { AnalyticsWindow, FacetGroupBy, FacetQuery, GroupableColumn, StateMatch } from '../../types';
2
+ /** A caller-input problem — mapped to HTTP 400 by the api layer. */
3
+ export declare class AnalyticsInputError extends Error {
4
+ readonly status = 400;
5
+ constructor(message: string);
6
+ }
7
+ export declare function requireFacetKey(key: string, label: string): string;
8
+ /**
9
+ * The statuses the caller considers live, validated against the closed status
10
+ * set. Values are later inlined as SQL literals, so this whitelist is also the
11
+ * injection boundary.
12
+ */
13
+ export declare function resolveLiveStatuses(liveStatuses?: string[]): string[];
14
+ /**
15
+ * The analytics filter accepts the WHAT of a FacetQuery (role/roles or entity,
16
+ * facets, block, range, exists) and rejects everything that describes liveness
17
+ * or row paging — those are derived from the measure and the group page
18
+ * instead. `entity` is validated here and RESOLVED by the service (to the
19
+ * roles declaring it) before any SQL is built.
20
+ */
21
+ export declare function requireCleanFilter(query: (FacetQuery & {
22
+ entity?: string;
23
+ }) | undefined, label?: string): FacetQuery & {
24
+ entity?: string;
25
+ };
26
+ /** Timeline paging direction; `before` is a strict created_at upper bound. */
27
+ export declare function resolveTimelinePage(order: 'asc' | 'desc' | undefined, before: Date | string | undefined): {
28
+ order: 'asc' | 'desc';
29
+ before: Date | null;
30
+ };
31
+ export declare function toInstant(value: Date | string, label: string): Date;
32
+ /** Half-open [from, to): ordered, and bounded by LT_ANALYTICS_MAX_WINDOW_DAYS. */
33
+ export declare function resolveWindow(window: AnalyticsWindow | undefined, label?: string): {
34
+ from: Date;
35
+ to: Date;
36
+ nowAnchored: boolean;
37
+ } | null;
38
+ /** Membership anchor: null = now (the cheap live-set path); a past instant otherwise. */
39
+ export declare function resolveAsOf(asOf: Date | string | undefined): Date | null;
40
+ export declare function resolveGroupBy(groupBy: FacetGroupBy | undefined): {
41
+ columns: GroupableColumn[];
42
+ facets: string[];
43
+ state: boolean;
44
+ };
45
+ /**
46
+ * A state label that references a group key the query never produces can never
47
+ * match — that is a caller bug, so it throws rather than silently never labeling.
48
+ */
49
+ export declare function validateStates(states: Array<{
50
+ name: string;
51
+ match: StateMatch;
52
+ }> | undefined, columns: GroupableColumn[], facets: string[]): void;
53
+ /** Result-group paging, hard-capped at LT_ANALYTICS_MAX_GROUPS. */
54
+ export declare function resolvePage(limit?: number, offset?: number): {
55
+ pageLimit: number;
56
+ offset: number;
57
+ };
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // Analytics input validation — every constraint checked loudly up front so the
4
+ // SQL builders (aggregate-sql.ts) compose only proven-safe pieces. Unlike the
5
+ // legacy row paths (buildFacetWhere silently drops malformed range/exists
6
+ // entries), the analytics surface rejects: a filter the caller believes is
7
+ // applied but isn't would silently change what an aggregate MEANS.
8
+ // ---------------------------------------------------------------------------
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.AnalyticsInputError = void 0;
11
+ exports.requireFacetKey = requireFacetKey;
12
+ exports.resolveLiveStatuses = resolveLiveStatuses;
13
+ exports.requireCleanFilter = requireCleanFilter;
14
+ exports.resolveTimelinePage = resolveTimelinePage;
15
+ exports.toInstant = toInstant;
16
+ exports.resolveWindow = resolveWindow;
17
+ exports.resolveAsOf = resolveAsOf;
18
+ exports.resolveGroupBy = resolveGroupBy;
19
+ exports.validateStates = validateStates;
20
+ exports.resolvePage = resolvePage;
21
+ const config_1 = require("../../modules/config");
22
+ const facet_sql_1 = require("./facet-sql");
23
+ const types_1 = require("../../types");
24
+ /** A caller-input problem — mapped to HTTP 400 by the api layer. */
25
+ class AnalyticsInputError extends Error {
26
+ constructor(message) {
27
+ super(message);
28
+ this.status = 400;
29
+ this.name = 'AnalyticsInputError';
30
+ }
31
+ }
32
+ exports.AnalyticsInputError = AnalyticsInputError;
33
+ const VALID_STATUSES = new Set(['pending', 'resolved', 'cancelled', 'expired']);
34
+ const RANGE_OPS = new Set(['<', '<=', '>', '>=', '=']);
35
+ /** Clock-skew tolerance when rejecting a future asOf. */
36
+ const FUTURE_SKEW_MS = 5_000;
37
+ /** anyOf targets an explicit entity set (a table page) — never an unbounded list. */
38
+ const MAX_ANY_OF = 200;
39
+ function requireFacetKey(key, label) {
40
+ if (typeof key !== 'string' || !facet_sql_1.FACET_KEY.test(key)) {
41
+ throw new AnalyticsInputError(`${label} "${key}" is not a valid facet key (letters, digits, underscore only)`);
42
+ }
43
+ return key;
44
+ }
45
+ /**
46
+ * The statuses the caller considers live, validated against the closed status
47
+ * set. Values are later inlined as SQL literals, so this whitelist is also the
48
+ * injection boundary.
49
+ */
50
+ function resolveLiveStatuses(liveStatuses) {
51
+ const list = liveStatuses ?? ['pending'];
52
+ if (!Array.isArray(list) || list.length === 0) {
53
+ throw new AnalyticsInputError('liveStatuses must be a non-empty array when provided');
54
+ }
55
+ const out = [];
56
+ for (const s of list) {
57
+ if (!VALID_STATUSES.has(s)) {
58
+ throw new AnalyticsInputError(`liveStatuses contains unknown status "${s}" (valid: ${[...VALID_STATUSES].join(', ')})`);
59
+ }
60
+ if (!out.includes(s))
61
+ out.push(s);
62
+ }
63
+ return out;
64
+ }
65
+ /**
66
+ * The analytics filter accepts the WHAT of a FacetQuery (role/roles or entity,
67
+ * facets, block, range, exists) and rejects everything that describes liveness
68
+ * or row paging — those are derived from the measure and the group page
69
+ * instead. `entity` is validated here and RESOLVED by the service (to the
70
+ * roles declaring it) before any SQL is built.
71
+ */
72
+ function requireCleanFilter(query, label = 'query') {
73
+ const q = query ?? {};
74
+ if (q.entity !== undefined) {
75
+ requireFacetKey(q.entity, `${label}.entity`);
76
+ if (q.role !== undefined || q.roles !== undefined) {
77
+ throw new AnalyticsInputError(`${label}.entity and ${label}.role/roles are two scoping mechanisms — use one: entity derives the roles from their entity_facet declarations`);
78
+ }
79
+ }
80
+ for (const field of ['status', 'available', 'jeopardy']) {
81
+ if (q[field] !== undefined) {
82
+ throw new AnalyticsInputError(`${label}.${field} is not accepted in analytics — liveness derives from the interval; use liveStatuses and the measure anchor`);
83
+ }
84
+ }
85
+ for (const field of ['orderBy', 'limit', 'offset']) {
86
+ if (q[field] !== undefined) {
87
+ throw new AnalyticsInputError(`${label}.${field} is not accepted in analytics — order and paginate the result groups with the top-level orderBy/limit/offset`);
88
+ }
89
+ }
90
+ for (const r of q.range ?? []) {
91
+ requireFacetKey(r?.facet, `${label}.range facet`);
92
+ if (!RANGE_OPS.has(r?.op)) {
93
+ throw new AnalyticsInputError(`${label}.range op "${r?.op}" is not one of < <= > >= =`);
94
+ }
95
+ }
96
+ for (const key of q.exists ?? []) {
97
+ requireFacetKey(key, `${label}.exists key`);
98
+ }
99
+ for (const [key, value] of Object.entries(q.prefix ?? {})) {
100
+ requireFacetKey(key, `${label}.prefix key`);
101
+ if (typeof value !== 'string' || value.length === 0) {
102
+ throw new AnalyticsInputError(`${label}.prefix["${key}"] must be a non-empty string`);
103
+ }
104
+ }
105
+ if (q.anyOf !== undefined) {
106
+ if (!Array.isArray(q.anyOf) || q.anyOf.length === 0) {
107
+ throw new AnalyticsInputError(`${label}.anyOf must be a non-empty array of facet sets`);
108
+ }
109
+ if (q.anyOf.length > MAX_ANY_OF) {
110
+ throw new AnalyticsInputError(`${label}.anyOf holds ${q.anyOf.length} entries; the maximum is ${MAX_ANY_OF} — page the target set`);
111
+ }
112
+ for (const entry of q.anyOf) {
113
+ if (!entry || typeof entry !== 'object' || Object.keys(entry).length === 0) {
114
+ throw new AnalyticsInputError(`${label}.anyOf entries must be non-empty facet objects`);
115
+ }
116
+ for (const key of Object.keys(entry))
117
+ requireFacetKey(key, `${label}.anyOf key`);
118
+ }
119
+ }
120
+ return q;
121
+ }
122
+ /** Timeline paging direction; `before` is a strict created_at upper bound. */
123
+ function resolveTimelinePage(order, before) {
124
+ if (order !== undefined && order !== 'asc' && order !== 'desc') {
125
+ throw new AnalyticsInputError('order must be "asc" or "desc"');
126
+ }
127
+ return {
128
+ order: order ?? 'asc',
129
+ before: before === undefined ? null : toInstant(before, 'before'),
130
+ };
131
+ }
132
+ function toInstant(value, label) {
133
+ const d = value instanceof Date ? value : new Date(value);
134
+ if (Number.isNaN(d.getTime())) {
135
+ throw new AnalyticsInputError(`${label} is not a parseable instant: "${value}"`);
136
+ }
137
+ return d;
138
+ }
139
+ /** Half-open [from, to): ordered, and bounded by LT_ANALYTICS_MAX_WINDOW_DAYS. */
140
+ function resolveWindow(window, label = 'window') {
141
+ if (!window)
142
+ return null;
143
+ const from = toInstant(window.from, `${label}.from`);
144
+ const to = toInstant(window.to, `${label}.to`);
145
+ if (from.getTime() >= to.getTime()) {
146
+ throw new AnalyticsInputError(`${label} is empty: from must be before to`);
147
+ }
148
+ const spanDays = (to.getTime() - from.getTime()) / 86_400_000;
149
+ if (spanDays > config_1.config.LT_ANALYTICS_MAX_WINDOW_DAYS) {
150
+ throw new AnalyticsInputError(`${label} spans ${spanDays.toFixed(1)} days; the maximum is ${config_1.config.LT_ANALYTICS_MAX_WINDOW_DAYS} (LT_ANALYTICS_MAX_WINDOW_DAYS)`);
151
+ }
152
+ return { from, to, nowAnchored: to.getTime() >= Date.now() };
153
+ }
154
+ /** Membership anchor: null = now (the cheap live-set path); a past instant otherwise. */
155
+ function resolveAsOf(asOf) {
156
+ if (asOf === undefined)
157
+ return null;
158
+ const d = toInstant(asOf, 'measure.asOf');
159
+ if (d.getTime() > Date.now() + FUTURE_SKEW_MS) {
160
+ throw new AnalyticsInputError('measure.asOf is in the future — membership can only be reconstructed for past instants');
161
+ }
162
+ return d;
163
+ }
164
+ function resolveGroupBy(groupBy) {
165
+ const columns = [];
166
+ for (const c of groupBy?.columns ?? []) {
167
+ if (!types_1.GROUPABLE_COLUMNS.includes(c)) {
168
+ throw new AnalyticsInputError(`groupBy.columns contains "${c}" (valid: ${types_1.GROUPABLE_COLUMNS.join(', ')})`);
169
+ }
170
+ if (!columns.includes(c))
171
+ columns.push(c);
172
+ }
173
+ const facets = [];
174
+ for (const key of groupBy?.facets ?? []) {
175
+ requireFacetKey(key, 'groupBy.facets key');
176
+ if (!facets.includes(key))
177
+ facets.push(key);
178
+ }
179
+ return { columns, facets, state: groupBy?.state === true };
180
+ }
181
+ /**
182
+ * A state label that references a group key the query never produces can never
183
+ * match — that is a caller bug, so it throws rather than silently never labeling.
184
+ */
185
+ function validateStates(states, columns, facets) {
186
+ for (const s of states ?? []) {
187
+ if (!s?.name)
188
+ throw new AnalyticsInputError('states entries require a name');
189
+ const m = s.match ?? {};
190
+ if ((m.role !== undefined || m.roles !== undefined) && !columns.includes('role')) {
191
+ throw new AnalyticsInputError(`state "${s.name}" matches on role, but groupBy.columns does not include "role"`);
192
+ }
193
+ if (m.subtype !== undefined && !columns.includes('subtype')) {
194
+ throw new AnalyticsInputError(`state "${s.name}" matches on subtype, but groupBy.columns does not include "subtype"`);
195
+ }
196
+ for (const key of Object.keys(m.facets ?? {})) {
197
+ if (!facets.includes(key)) {
198
+ throw new AnalyticsInputError(`state "${s.name}" matches on facet "${key}", but groupBy.facets does not include it`);
199
+ }
200
+ }
201
+ for (const key of m.exists ?? []) {
202
+ if (!facets.includes(key)) {
203
+ throw new AnalyticsInputError(`state "${s.name}" requires facet "${key}", but groupBy.facets does not include it`);
204
+ }
205
+ }
206
+ }
207
+ }
208
+ /** Result-group paging, hard-capped at LT_ANALYTICS_MAX_GROUPS. */
209
+ function resolvePage(limit, offset) {
210
+ const cap = config_1.config.LT_ANALYTICS_MAX_GROUPS;
211
+ if (limit !== undefined && (!Number.isInteger(limit) || limit <= 0)) {
212
+ throw new AnalyticsInputError('limit must be a positive integer');
213
+ }
214
+ if (offset !== undefined && (!Number.isInteger(offset) || offset < 0)) {
215
+ throw new AnalyticsInputError('offset must be a non-negative integer');
216
+ }
217
+ return { pageLimit: Math.min(limit ?? cap, cap), offset: offset ?? 0 };
218
+ }
@@ -0,0 +1,26 @@
1
+ import type { AggregateByFacetsInput, AggregateByFacetsResult, TimelineByFacetInput, TimelineByFacetResult } from '../../types';
2
+ /** Called on every escalation write (via invalidateEscalationAggregates). */
3
+ export declare function clearNowAnchoredAnalyticsCache(): void;
4
+ /** Test hook: drop BOTH analytics caches. */
5
+ export declare function resetAnalyticsCaches(): void;
6
+ /**
7
+ * The roles declaring `entity_facet = key` — the entity's SYSTEM — with each
8
+ * role's state naming. Loud when nothing declares the key: an empty system is
9
+ * a config gap, not an empty result.
10
+ */
11
+ export declare function resolveEntitySystem(entityKey: string): Promise<Array<{
12
+ role: string;
13
+ source: 'role' | 'subtype';
14
+ }>>;
15
+ /**
16
+ * Grouped aggregate over the escalation intervals — membership at an instant
17
+ * or dwell over a window, grouped by columns + facets (+ the derived state
18
+ * label). One call replaces N countByFacets round-trips.
19
+ */
20
+ export declare function aggregateByFacets(input: AggregateByFacetsInput): Promise<AggregateByFacetsResult>;
21
+ /**
22
+ * One entity's ordered interval sequence — `[created_at, ended_at)` spans with
23
+ * durations. Gaps between consecutive intervals are untracked time and are
24
+ * deliberately preserved, not filled.
25
+ */
26
+ export declare function timelineByFacet(input: TimelineByFacetInput): Promise<TimelineByFacetResult>;