@hotmeshio/long-tail 0.10.14 → 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 (332) hide show
  1. package/build/api/escalations/analytics.d.ts +6 -0
  2. package/build/api/escalations/analytics.js +123 -0
  3. package/build/api/escalations/index.d.ts +1 -0
  4. package/build/api/escalations/index.js +4 -1
  5. package/build/api/roles.d.ts +4 -0
  6. package/build/api/roles.js +8 -0
  7. package/build/api/scan-codes/choice.d.ts +11 -0
  8. package/build/api/scan-codes/choice.js +136 -0
  9. package/build/api/scan-codes/context.d.ts +24 -0
  10. package/build/api/scan-codes/context.js +86 -0
  11. package/build/api/scan-codes/execute.d.ts +1 -0
  12. package/build/api/scan-codes/execute.js +49 -244
  13. package/build/api/scan-codes/identity.d.ts +18 -0
  14. package/build/api/scan-codes/identity.js +116 -0
  15. package/build/api/scan-codes/index.d.ts +2 -0
  16. package/build/api/scan-codes/index.js +6 -1
  17. package/build/api/scan-codes/locate.d.ts +14 -0
  18. package/build/api/scan-codes/locate.js +127 -0
  19. package/build/api/scan-codes/present.d.ts +9 -0
  20. package/build/api/scan-codes/present.js +59 -0
  21. package/build/api/scan-codes/verbs.d.ts +15 -0
  22. package/build/api/scan-codes/verbs.js +205 -0
  23. package/build/bin/ltc.js +33 -0
  24. package/build/lib/cli/commands/escalations.d.ts +31 -0
  25. package/build/lib/cli/commands/escalations.js +88 -0
  26. package/build/lib/db/schemas/028_role_entity_facet.sql +18 -0
  27. package/build/lib/db/schemas/029_scan_scheme_kind.sql +31 -0
  28. package/build/modules/config.d.ts +5 -0
  29. package/build/modules/config.js +12 -0
  30. package/build/routes/escalations/acting.d.ts +12 -0
  31. package/build/routes/escalations/acting.js +25 -0
  32. package/build/routes/escalations/analytics.d.ts +8 -0
  33. package/build/routes/escalations/analytics.js +63 -0
  34. package/build/routes/escalations/index.js +3 -0
  35. package/build/routes/escalations/resolve.js +8 -1
  36. package/build/routes/escalations/single.js +13 -4
  37. package/build/routes/roles.js +1 -1
  38. package/build/routes/scan-codes.js +28 -2
  39. package/build/scripts/ortho-populate.js +14 -0
  40. package/build/scripts/printer-cycle.d.ts +25 -0
  41. package/build/scripts/printer-cycle.js +169 -0
  42. package/build/scripts/twin-farm.js +19 -5
  43. package/build/sdk/index.d.ts +2 -0
  44. package/build/sdk/index.js +3 -0
  45. package/build/services/escalation/aggregate-sql.d.ts +36 -0
  46. package/build/services/escalation/aggregate-sql.js +253 -0
  47. package/build/services/escalation/aggregate-validate.d.ts +57 -0
  48. package/build/services/escalation/aggregate-validate.js +218 -0
  49. package/build/services/escalation/aggregates.d.ts +26 -0
  50. package/build/services/escalation/aggregates.js +204 -0
  51. package/build/services/escalation/client.js +57 -21
  52. package/build/services/escalation/facet-sql.js +17 -0
  53. package/build/services/escalation/index.d.ts +2 -0
  54. package/build/services/escalation/index.js +8 -1
  55. package/build/services/escalation/metrics-cache.d.ts +18 -0
  56. package/build/services/escalation/metrics-cache.js +47 -1
  57. package/build/services/escalation/queries.js +2 -0
  58. package/build/services/iam/acting-identity.d.ts +16 -0
  59. package/build/services/iam/acting-identity.js +60 -0
  60. package/build/services/role/index.js +2 -0
  61. package/build/services/role/sql.d.ts +10 -2
  62. package/build/services/role/sql.js +14 -1
  63. package/build/services/role/types.d.ts +16 -0
  64. package/build/services/scan-code/read.js +4 -0
  65. package/build/services/scan-code/sql.d.ts +4 -4
  66. package/build/services/scan-code/sql.js +14 -8
  67. package/build/services/scan-code/validate.d.ts +2 -0
  68. package/build/services/scan-code/validate.js +86 -0
  69. package/build/services/scan-code/write.d.ts +4 -0
  70. package/build/services/scan-code/write.js +16 -2
  71. package/build/services/user/crud.d.ts +6 -0
  72. package/build/services/user/crud.js +16 -0
  73. package/build/services/user/index.d.ts +1 -1
  74. package/build/services/user/index.js +2 -1
  75. package/build/services/user/sql.d.ts +8 -0
  76. package/build/services/user/sql.js +9 -1
  77. package/build/system/mcp-servers/admin/escalations.js +34 -0
  78. package/build/system/mcp-servers/admin/scan-codes.js +17 -1
  79. package/build/system/mcp-servers/admin/schemas.d.ts +643 -25
  80. package/build/system/mcp-servers/admin/schemas.js +127 -19
  81. package/build/system/mcp-servers/admin/users.js +3 -1
  82. package/build/system/mcp-servers/human-queue-schemas.d.ts +2 -2
  83. package/build/system/seed/tool-manifests-admin.d.ts +2283 -0
  84. package/build/system/seed/tool-manifests-admin.js +8 -5
  85. package/build/tsconfig.tsbuildinfo +1 -1
  86. package/build/types/analytics.d.ts +154 -0
  87. package/build/types/analytics.js +14 -0
  88. package/build/types/facets.d.ts +13 -0
  89. package/build/types/index.d.ts +5 -2
  90. package/build/types/index.js +5 -1
  91. package/build/types/scan-choice.d.ts +49 -0
  92. package/build/types/scan-choice.js +9 -0
  93. package/build/types/scan-code.d.ts +72 -1
  94. package/build/types/scan-code.js +30 -1
  95. package/dashboard/dist/assets/{AdminDashboard-Chg69wlx.js → AdminDashboard-DJ3LJQ09.js} +2 -2
  96. package/dashboard/dist/assets/{AdminDashboard-Chg69wlx.js.map → AdminDashboard-DJ3LJQ09.js.map} +1 -1
  97. package/dashboard/dist/assets/{AgentConfigPage-DRTFwbfC.js → AgentConfigPage-BMMvaD0z.js} +2 -2
  98. package/dashboard/dist/assets/{AgentConfigPage-DRTFwbfC.js.map → AgentConfigPage-BMMvaD0z.js.map} +1 -1
  99. package/dashboard/dist/assets/{AgentDetailPage-JKByfFIi.js → AgentDetailPage-C6wfsnRe.js} +3 -3
  100. package/dashboard/dist/assets/{AgentDetailPage-JKByfFIi.js.map → AgentDetailPage-C6wfsnRe.js.map} +1 -1
  101. package/dashboard/dist/assets/{AgentsPage-BlgEmKhA.js → AgentsPage-Cwqka7nE.js} +2 -2
  102. package/dashboard/dist/assets/{AgentsPage-BlgEmKhA.js.map → AgentsPage-Cwqka7nE.js.map} +1 -1
  103. package/dashboard/dist/assets/AvailableEscalationsPage-CVlG_AVC.js +2 -0
  104. package/dashboard/dist/assets/AvailableEscalationsPage-CVlG_AVC.js.map +1 -0
  105. package/dashboard/dist/assets/{BotPicker-CCVcz4Cq.js → BotPicker-BBkJBXZB.js} +2 -2
  106. package/dashboard/dist/assets/{BotPicker-CCVcz4Cq.js.map → BotPicker-BBkJBXZB.js.map} +1 -1
  107. package/dashboard/dist/assets/{CapabilitiesPage-Czi9jUwD.js → CapabilitiesPage-wxmE4917.js} +2 -2
  108. package/dashboard/dist/assets/{CapabilitiesPage-Czi9jUwD.js.map → CapabilitiesPage-wxmE4917.js.map} +1 -1
  109. package/dashboard/dist/assets/{CollapsibleSection-8YE965QX.js → CollapsibleSection-B3FsrHwH.js} +2 -2
  110. package/dashboard/dist/assets/{CollapsibleSection-8YE965QX.js.map → CollapsibleSection-B3FsrHwH.js.map} +1 -1
  111. package/dashboard/dist/assets/{CountdownTimer-Bxvq3D7j.js → CountdownTimer-B3nCqJdC.js} +2 -2
  112. package/dashboard/dist/assets/{CountdownTimer-Bxvq3D7j.js.map → CountdownTimer-B3nCqJdC.js.map} +1 -1
  113. package/dashboard/dist/assets/{CredentialsPage-Bn7_SX6-.js → CredentialsPage-Bv875IHe.js} +2 -2
  114. package/dashboard/dist/assets/{CredentialsPage-Bn7_SX6-.js.map → CredentialsPage-Bv875IHe.js.map} +1 -1
  115. package/dashboard/dist/assets/{CronLabel-Bn4mX_Yn.js → CronLabel-CSzjWuwj.js} +2 -2
  116. package/dashboard/dist/assets/{CronLabel-Bn4mX_Yn.js.map → CronLabel-CSzjWuwj.js.map} +1 -1
  117. package/dashboard/dist/assets/{CustomDurationPicker-pMcrFyW6.js → CustomDurationPicker-CCpqf3e4.js} +2 -2
  118. package/dashboard/dist/assets/{CustomDurationPicker-pMcrFyW6.js.map → CustomDurationPicker-CCpqf3e4.js.map} +1 -1
  119. package/dashboard/dist/assets/{DropZone-zCEGjjk-.js → DropZone-CJkk-U3o.js} +2 -2
  120. package/dashboard/dist/assets/{DropZone-zCEGjjk-.js.map → DropZone-CJkk-U3o.js.map} +1 -1
  121. package/dashboard/dist/assets/{ElapsedCell-BGO7IXEg.js → ElapsedCell-WV6pUaKo.js} +2 -2
  122. package/dashboard/dist/assets/{ElapsedCell-BGO7IXEg.js.map → ElapsedCell-WV6pUaKo.js.map} +1 -1
  123. package/dashboard/dist/assets/EntityTimelinePanel-DrL86Yrf.js +2 -0
  124. package/dashboard/dist/assets/EntityTimelinePanel-DrL86Yrf.js.map +1 -0
  125. package/dashboard/dist/assets/{EscalationListSchemaPage-CFDxbd50.js → EscalationListSchemaPage-CTQCALw4.js} +3 -3
  126. package/dashboard/dist/assets/{EscalationListSchemaPage-CFDxbd50.js.map → EscalationListSchemaPage-CTQCALw4.js.map} +1 -1
  127. package/dashboard/dist/assets/{EscalationSchemaPage-DnXPnDkz.js → EscalationSchemaPage-D_UgP0pv.js} +3 -3
  128. package/dashboard/dist/assets/{EscalationSchemaPage-DnXPnDkz.js.map → EscalationSchemaPage-D_UgP0pv.js.map} +1 -1
  129. package/dashboard/dist/assets/{EscalationsOverview-BZMVXrkK.js → EscalationsOverview-DuIUyxr0.js} +2 -2
  130. package/dashboard/dist/assets/{EscalationsOverview-BZMVXrkK.js.map → EscalationsOverview-DuIUyxr0.js.map} +1 -1
  131. package/dashboard/dist/assets/{EventTable-Bfi-f8as.js → EventTable-BV6unphX.js} +2 -2
  132. package/dashboard/dist/assets/{EventTable-Bfi-f8as.js.map → EventTable-BV6unphX.js.map} +1 -1
  133. package/dashboard/dist/assets/FilterBar-DYFgzXNB.js +2 -0
  134. package/dashboard/dist/assets/{FilterBar-Do7yR4xM.js.map → FilterBar-DYFgzXNB.js.map} +1 -1
  135. package/dashboard/dist/assets/{GraphInvokePage-d1ie4T7f.js → GraphInvokePage-BGHVUipJ.js} +2 -2
  136. package/dashboard/dist/assets/{GraphInvokePage-d1ie4T7f.js.map → GraphInvokePage-BGHVUipJ.js.map} +1 -1
  137. package/dashboard/dist/assets/HomePage-B-S2cK3E.js +2 -0
  138. package/dashboard/dist/assets/{HomePage-BEeodImU.js.map → HomePage-B-S2cK3E.js.map} +1 -1
  139. package/dashboard/dist/assets/ListToolbar-Bl7F3A28.js +2 -0
  140. package/dashboard/dist/assets/{ListToolbar-CLtw-1QN.js.map → ListToolbar-Bl7F3A28.js.map} +1 -1
  141. package/dashboard/dist/assets/{McpOverview-DIsy9TB3.js → McpOverview-_T9IsCD_.js} +2 -2
  142. package/dashboard/dist/assets/{McpOverview-DIsy9TB3.js.map → McpOverview-_T9IsCD_.js.map} +1 -1
  143. package/dashboard/dist/assets/{McpQueryDetailPage-6PomdsyD.js → McpQueryDetailPage-DtsVqKPw.js} +2 -2
  144. package/dashboard/dist/assets/{McpQueryDetailPage-6PomdsyD.js.map → McpQueryDetailPage-DtsVqKPw.js.map} +1 -1
  145. package/dashboard/dist/assets/{McpQueryPage-YMdQCXm9.js → McpQueryPage-DX5u-1bx.js} +2 -2
  146. package/dashboard/dist/assets/{McpQueryPage-YMdQCXm9.js.map → McpQueryPage-DX5u-1bx.js.map} +1 -1
  147. package/dashboard/dist/assets/{McpRunDetailPage-DsD1Gm2o.js → McpRunDetailPage-BhcqqtYS.js} +2 -2
  148. package/dashboard/dist/assets/{McpRunDetailPage-DsD1Gm2o.js.map → McpRunDetailPage-BhcqqtYS.js.map} +1 -1
  149. package/dashboard/dist/assets/{McpRunsPage--a5Jgb33.js → McpRunsPage-L-1y8wnX.js} +2 -2
  150. package/dashboard/dist/assets/{McpRunsPage--a5Jgb33.js.map → McpRunsPage-L-1y8wnX.js.map} +1 -1
  151. package/dashboard/dist/assets/{NamespacePill-wV6L4VW1.js → NamespacePill-in1SLd0W.js} +2 -2
  152. package/dashboard/dist/assets/{NamespacePill-wV6L4VW1.js.map → NamespacePill-in1SLd0W.js.map} +1 -1
  153. package/dashboard/dist/assets/OperationsPage-i8KcDpKo.js +3 -0
  154. package/dashboard/dist/assets/OperationsPage-i8KcDpKo.js.map +1 -0
  155. package/dashboard/dist/assets/OperatorDashboard-CIbDzOlh.js +2 -0
  156. package/dashboard/dist/assets/{OperatorDashboard-CkLfbxEh.js.map → OperatorDashboard-CIbDzOlh.js.map} +1 -1
  157. package/dashboard/dist/assets/{PageHeader-BtIr6r-8.js → PageHeader-BJgWMZtw.js} +2 -2
  158. package/dashboard/dist/assets/{PageHeader-BtIr6r-8.js.map → PageHeader-BJgWMZtw.js.map} +1 -1
  159. package/dashboard/dist/assets/{PageHeaderWithStats-C2sGqFQG.js → PageHeaderWithStats-Ccio4vyG.js} +2 -2
  160. package/dashboard/dist/assets/{PageHeaderWithStats-C2sGqFQG.js.map → PageHeaderWithStats-Ccio4vyG.js.map} +1 -1
  161. package/dashboard/dist/assets/{PersonaDetailPage-CCmOgKQb.js → PersonaDetailPage-BOaf0TW1.js} +2 -2
  162. package/dashboard/dist/assets/{PersonaDetailPage-CCmOgKQb.js.map → PersonaDetailPage-BOaf0TW1.js.map} +1 -1
  163. package/dashboard/dist/assets/{PersonasPage-BJkSuDTN.js → PersonasPage-DSdRb90f.js} +2 -2
  164. package/dashboard/dist/assets/{PersonasPage-BJkSuDTN.js.map → PersonasPage-DSdRb90f.js.map} +1 -1
  165. package/dashboard/dist/assets/{ProcessDetailPage-DT9BlJXK.js → ProcessDetailPage-DN9NqfeP.js} +2 -2
  166. package/dashboard/dist/assets/{ProcessDetailPage-DT9BlJXK.js.map → ProcessDetailPage-DN9NqfeP.js.map} +1 -1
  167. package/dashboard/dist/assets/{ProcessesListPage-DgSGzTDT.js → ProcessesListPage-CfQEWaD4.js} +2 -2
  168. package/dashboard/dist/assets/{ProcessesListPage-DgSGzTDT.js.map → ProcessesListPage-CfQEWaD4.js.map} +1 -1
  169. package/dashboard/dist/assets/RoleDetailPage-BC5HL7ls.js +8 -0
  170. package/dashboard/dist/assets/RoleDetailPage-BC5HL7ls.js.map +1 -0
  171. package/dashboard/dist/assets/{RolePill-BBYjhAPj.js → RolePill-BNAP-uK6.js} +2 -2
  172. package/dashboard/dist/assets/{RolePill-BBYjhAPj.js.map → RolePill-BNAP-uK6.js.map} +1 -1
  173. package/dashboard/dist/assets/RolesPage-DCnW0VmD.js +2 -0
  174. package/dashboard/dist/assets/RolesPage-DCnW0VmD.js.map +1 -0
  175. package/dashboard/dist/assets/{RunAsSelector-CateuvcC.js → RunAsSelector-twbuX7fB.js} +2 -2
  176. package/dashboard/dist/assets/{RunAsSelector-CateuvcC.js.map → RunAsSelector-twbuX7fB.js.map} +1 -1
  177. package/dashboard/dist/assets/ScanCodesPage-CPNg6K--.js +2 -0
  178. package/dashboard/dist/assets/ScanCodesPage-CPNg6K--.js.map +1 -0
  179. package/dashboard/dist/assets/ScanSchemeDetailPage-DyLmCamO.js +2 -0
  180. package/dashboard/dist/assets/ScanSchemeDetailPage-DyLmCamO.js.map +1 -0
  181. package/dashboard/dist/assets/ScanStationPage-DoZccU5B.js +2 -0
  182. package/dashboard/dist/assets/ScanStationPage-DoZccU5B.js.map +1 -0
  183. package/dashboard/dist/assets/{ScopeBadge-xs2Nhjy4.js → ScopeBadge-BG7KkZ8Q.js} +2 -2
  184. package/dashboard/dist/assets/{ScopeBadge-xs2Nhjy4.js.map → ScopeBadge-BG7KkZ8Q.js.map} +1 -1
  185. package/dashboard/dist/assets/{StickyPagination-Cg-QdYWL.js → StickyPagination-DIeYMPPd.js} +2 -2
  186. package/dashboard/dist/assets/{StickyPagination-Cg-QdYWL.js.map → StickyPagination-DIeYMPPd.js.map} +1 -1
  187. package/dashboard/dist/assets/{StreamMessageDetail-DEeuh4_-.js → StreamMessageDetail-gU4CUeQI.js} +2 -2
  188. package/dashboard/dist/assets/{StreamMessageDetail-DEeuh4_-.js.map → StreamMessageDetail-gU4CUeQI.js.map} +1 -1
  189. package/dashboard/dist/assets/{SwimlaneTimeline-DMY0jL2F.js → SwimlaneTimeline-Br-nBA4l.js} +2 -2
  190. package/dashboard/dist/assets/{SwimlaneTimeline-DMY0jL2F.js.map → SwimlaneTimeline-Br-nBA4l.js.map} +1 -1
  191. package/dashboard/dist/assets/{TagInput-BMmiV-MD.js → TagInput-CvM5n6ww.js} +2 -2
  192. package/dashboard/dist/assets/{TagInput-BMmiV-MD.js.map → TagInput-CvM5n6ww.js.map} +1 -1
  193. package/dashboard/dist/assets/{TaskDetailPage-CPbkIvfF.js → TaskDetailPage-_9cr_R0u.js} +2 -2
  194. package/dashboard/dist/assets/{TaskDetailPage-CPbkIvfF.js.map → TaskDetailPage-_9cr_R0u.js.map} +1 -1
  195. package/dashboard/dist/assets/{TaskQueuePill-DLgYaw1x.js → TaskQueuePill-D4JPHXOq.js} +2 -2
  196. package/dashboard/dist/assets/{TaskQueuePill-DLgYaw1x.js.map → TaskQueuePill-D4JPHXOq.js.map} +1 -1
  197. package/dashboard/dist/assets/{TasksListPage-CtHudKhQ.js → TasksListPage-DVENC9Jp.js} +2 -2
  198. package/dashboard/dist/assets/{TasksListPage-CtHudKhQ.js.map → TasksListPage-DVENC9Jp.js.map} +1 -1
  199. package/dashboard/dist/assets/{TimeAgo-BdHOizWg.js → TimeAgo-C9yYnOui.js} +2 -2
  200. package/dashboard/dist/assets/{TimeAgo-BdHOizWg.js.map → TimeAgo-C9yYnOui.js.map} +1 -1
  201. package/dashboard/dist/assets/{TimestampCell-FVukzKRe.js → TimestampCell-DoB8cYgw.js} +2 -2
  202. package/dashboard/dist/assets/{TimestampCell-FVukzKRe.js.map → TimestampCell-DoB8cYgw.js.map} +1 -1
  203. package/dashboard/dist/assets/{ToolPill-BvS53hxP.js → ToolPill-BUjbC-Tj.js} +2 -2
  204. package/dashboard/dist/assets/{ToolPill-BvS53hxP.js.map → ToolPill-BUjbC-Tj.js.map} +1 -1
  205. package/dashboard/dist/assets/{ToolTestPanel-HN0OCYTL.js → ToolTestPanel-CBtsIP8K.js} +2 -2
  206. package/dashboard/dist/assets/{ToolTestPanel-HN0OCYTL.js.map → ToolTestPanel-CBtsIP8K.js.map} +1 -1
  207. package/dashboard/dist/assets/{TopicDetailPage-DHdSpN61.js → TopicDetailPage-mR4Jaz83.js} +2 -2
  208. package/dashboard/dist/assets/{TopicDetailPage-DHdSpN61.js.map → TopicDetailPage-mR4Jaz83.js.map} +1 -1
  209. package/dashboard/dist/assets/TopicsPage-CP7HFxIC.js +2 -0
  210. package/dashboard/dist/assets/{TopicsPage-Clr6Tl5P.js.map → TopicsPage-CP7HFxIC.js.map} +1 -1
  211. package/dashboard/dist/assets/{UserName-BUVYyIAS.js → UserName-BnLF6yhz.js} +2 -2
  212. package/dashboard/dist/assets/{UserName-BUVYyIAS.js.map → UserName-BnLF6yhz.js.map} +1 -1
  213. package/dashboard/dist/assets/{WorkflowExecutionPage-kncWFfe5.js → WorkflowExecutionPage-xL956RZi.js} +2 -2
  214. package/dashboard/dist/assets/{WorkflowExecutionPage-kncWFfe5.js.map → WorkflowExecutionPage-xL956RZi.js.map} +1 -1
  215. package/dashboard/dist/assets/{WorkflowPill-BPC_Dc_I.js → WorkflowPill-tXU0-eXu.js} +2 -2
  216. package/dashboard/dist/assets/{WorkflowPill-BPC_Dc_I.js.map → WorkflowPill-tXU0-eXu.js.map} +1 -1
  217. package/dashboard/dist/assets/{WorkflowsDashboard-4ypPDGKx.js → WorkflowsDashboard-B-u7zFKM.js} +2 -2
  218. package/dashboard/dist/assets/{WorkflowsDashboard-4ypPDGKx.js.map → WorkflowsDashboard-B-u7zFKM.js.map} +1 -1
  219. package/dashboard/dist/assets/{WorkflowsOverview-CeQlZkkd.js → WorkflowsOverview-CqZVarK-.js} +2 -2
  220. package/dashboard/dist/assets/{WorkflowsOverview-CeQlZkkd.js.map → WorkflowsOverview-CqZVarK-.js.map} +1 -1
  221. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DLAlEzwT.js → YamlWorkflowDetailPage-DiFLavW0.js} +2 -2
  222. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DLAlEzwT.js.map → YamlWorkflowDetailPage-DiFLavW0.js.map} +1 -1
  223. package/dashboard/dist/assets/{YamlWorkflowsPage-DQhenKFT.js → YamlWorkflowsPage-BuQM6uZA.js} +2 -2
  224. package/dashboard/dist/assets/{YamlWorkflowsPage-DQhenKFT.js.map → YamlWorkflowsPage-BuQM6uZA.js.map} +1 -1
  225. package/dashboard/dist/assets/{agents-s3feet3W.js → agents-DU9GPovp.js} +2 -2
  226. package/dashboard/dist/assets/{agents-s3feet3W.js.map → agents-DU9GPovp.js.map} +1 -1
  227. package/dashboard/dist/assets/{bots-CkSd2rn9.js → bots-Cy-Hmg7w.js} +2 -2
  228. package/dashboard/dist/assets/{bots-CkSd2rn9.js.map → bots-Cy-Hmg7w.js.map} +1 -1
  229. package/dashboard/dist/assets/{capabilities-DA_r09tM.js → capabilities-sSJJqrp-.js} +2 -2
  230. package/dashboard/dist/assets/{capabilities-DA_r09tM.js.map → capabilities-sSJJqrp-.js.map} +1 -1
  231. package/dashboard/dist/assets/{controlplane-C9c25q21.js → controlplane-TVCjDHQ-.js} +2 -2
  232. package/dashboard/dist/assets/{controlplane-C9c25q21.js.map → controlplane-TVCjDHQ-.js.map} +1 -1
  233. package/dashboard/dist/assets/escalation-columns-BpkjKYGd.js +2 -0
  234. package/dashboard/dist/assets/escalation-columns-BpkjKYGd.js.map +1 -0
  235. package/dashboard/dist/assets/index-5TblaCXQ.css +1 -0
  236. package/dashboard/dist/assets/{index-WxE3ONia.js → index-7BP6FaCZ.js} +2 -2
  237. package/dashboard/dist/assets/{index-WxE3ONia.js.map → index-7BP6FaCZ.js.map} +1 -1
  238. package/dashboard/dist/assets/{index-C7ABSOU2.js → index-B7IaBPBg.js} +4 -4
  239. package/dashboard/dist/assets/{index-C7ABSOU2.js.map → index-B7IaBPBg.js.map} +1 -1
  240. package/dashboard/dist/assets/{index-BU5FFAJo.js → index-BE0phFw0.js} +2 -2
  241. package/dashboard/dist/assets/{index-BU5FFAJo.js.map → index-BE0phFw0.js.map} +1 -1
  242. package/dashboard/dist/assets/{index-C-MmR-jf.js → index-BYNN9N1T.js} +2 -2
  243. package/dashboard/dist/assets/{index-C-MmR-jf.js.map → index-BYNN9N1T.js.map} +1 -1
  244. package/dashboard/dist/assets/{index-S13oPvuS.js → index-CLPVqbVY.js} +2 -2
  245. package/dashboard/dist/assets/{index-S13oPvuS.js.map → index-CLPVqbVY.js.map} +1 -1
  246. package/dashboard/dist/assets/{index-Cw9poaez.js → index-Cfv86_L9.js} +2 -2
  247. package/dashboard/dist/assets/{index-Cw9poaez.js.map → index-Cfv86_L9.js.map} +1 -1
  248. package/dashboard/dist/assets/{index-Bo5TtX9l.js → index-CtwbQ_-m.js} +2 -2
  249. package/dashboard/dist/assets/{index-Bo5TtX9l.js.map → index-CtwbQ_-m.js.map} +1 -1
  250. package/dashboard/dist/assets/index-DOai950R.js +3 -0
  251. package/dashboard/dist/assets/index-DOai950R.js.map +1 -0
  252. package/dashboard/dist/assets/{index-Cmxzk4ay.js → index-DTtHNC-D.js} +2 -2
  253. package/dashboard/dist/assets/{index-Cmxzk4ay.js.map → index-DTtHNC-D.js.map} +1 -1
  254. package/dashboard/dist/assets/{index--7Vdkzwd.js → index-DsWnVyVN.js} +2 -2
  255. package/dashboard/dist/assets/{index--7Vdkzwd.js.map → index-DsWnVyVN.js.map} +1 -1
  256. package/dashboard/dist/assets/{index-DvoMXQqO.js → index-Dy-MMYti.js} +2 -2
  257. package/dashboard/dist/assets/{index-DvoMXQqO.js.map → index-Dy-MMYti.js.map} +1 -1
  258. package/dashboard/dist/assets/index-errgQT5x.js +65 -0
  259. package/dashboard/dist/assets/index-errgQT5x.js.map +1 -0
  260. package/dashboard/dist/assets/{index-BIcYbDVf.js → index-fgBDhnI6.js} +2 -2
  261. package/dashboard/dist/assets/{index-BIcYbDVf.js.map → index-fgBDhnI6.js.map} +1 -1
  262. package/dashboard/dist/assets/{knowledge-Bpln0zy_.js → knowledge-arh4NfmL.js} +2 -2
  263. package/dashboard/dist/assets/{knowledge-Bpln0zy_.js.map → knowledge-arh4NfmL.js.map} +1 -1
  264. package/dashboard/dist/assets/{mcp-BqDq3RZH.js → mcp-QGf5jHCo.js} +2 -2
  265. package/dashboard/dist/assets/{mcp-BqDq3RZH.js.map → mcp-QGf5jHCo.js.map} +1 -1
  266. package/dashboard/dist/assets/{mcp-query-DGue75R6.js → mcp-query-BEJN1nMZ.js} +2 -2
  267. package/dashboard/dist/assets/{mcp-query-DGue75R6.js.map → mcp-query-BEJN1nMZ.js.map} +1 -1
  268. package/dashboard/dist/assets/{personas-B8z_sTiJ.js → personas-DYtw1COf.js} +2 -2
  269. package/dashboard/dist/assets/{personas-B8z_sTiJ.js.map → personas-DYtw1COf.js.map} +1 -1
  270. package/dashboard/dist/assets/{pipelines-DYgNd9nB.js → pipelines-Byi6PnbE.js} +2 -2
  271. package/dashboard/dist/assets/{pipelines-DYgNd9nB.js.map → pipelines-Byi6PnbE.js.map} +1 -1
  272. package/dashboard/dist/assets/{tasks-CJdjjhgO.js → tasks-D0SQdENz.js} +2 -2
  273. package/dashboard/dist/assets/{tasks-CJdjjhgO.js.map → tasks-D0SQdENz.js.map} +1 -1
  274. package/dashboard/dist/assets/{topics-DPrL8Dtq.js → topics-DgDv5ZXP.js} +2 -2
  275. package/dashboard/dist/assets/{topics-DPrL8Dtq.js.map → topics-DgDv5ZXP.js.map} +1 -1
  276. package/dashboard/dist/assets/useEventHooks-DZaYzE7s.js +2 -0
  277. package/dashboard/dist/assets/{useEventHooks-DXCgDgiu.js.map → useEventHooks-DZaYzE7s.js.map} +1 -1
  278. package/dashboard/dist/assets/{useNamespace-DnFZfy7N.js → useNamespace-C1OqiNXN.js} +2 -2
  279. package/dashboard/dist/assets/{useNamespace-DnFZfy7N.js.map → useNamespace-C1OqiNXN.js.map} +1 -1
  280. package/dashboard/dist/assets/{useYamlActivityEvents-kx5CLENv.js → useYamlActivityEvents-CkmXDm-Z.js} +2 -2
  281. package/dashboard/dist/assets/{useYamlActivityEvents-kx5CLENv.js.map → useYamlActivityEvents-CkmXDm-Z.js.map} +1 -1
  282. package/dashboard/dist/assets/{users-DKng7vAG.js → users-D4DCFPH1.js} +2 -2
  283. package/dashboard/dist/assets/{users-DKng7vAG.js.map → users-D4DCFPH1.js.map} +1 -1
  284. package/dashboard/dist/assets/{vendor-icons-CN-bAo9U.js → vendor-icons-BxuyFxH2.js} +152 -137
  285. package/dashboard/dist/assets/vendor-icons-BxuyFxH2.js.map +1 -0
  286. package/dashboard/dist/assets/{workflows-C4V0rMkM.js → workflows-qyc0ku_5.js} +2 -2
  287. package/dashboard/dist/assets/{workflows-C4V0rMkM.js.map → workflows-qyc0ku_5.js.map} +1 -1
  288. package/dashboard/dist/assets/{yaml-workflows-D3PAiJ9d.js → yaml-workflows-DWFww9Xn.js} +2 -2
  289. package/dashboard/dist/assets/{yaml-workflows-D3PAiJ9d.js.map → yaml-workflows-DWFww9Xn.js.map} +1 -1
  290. package/dashboard/dist/index.html +3 -3
  291. package/docs/api/http/escalations.md +133 -0
  292. package/docs/api/http/roles.md +6 -0
  293. package/docs/api/mcp/admin.md +53 -0
  294. package/docs/api/sdk/escalations.md +58 -0
  295. package/docs/cli.md +11 -0
  296. package/docs/dashboard.md +22 -20
  297. package/docs/data.md +12 -4
  298. package/docs/escalation-analytics.md +251 -0
  299. package/docs/hitl/roles.md +1 -1
  300. package/docs/hitl/x-lt-footer.md +1 -2
  301. package/docs/hitl-guide.md +3 -3
  302. package/docs/iam.md +12 -0
  303. package/docs/operations.md +17 -0
  304. package/docs/scan-codes.md +112 -0
  305. package/docs/sdk.md +1 -1
  306. package/package.json +2 -1
  307. package/dashboard/dist/assets/AvailableEscalationsPage-C_7y692Q.js +0 -2
  308. package/dashboard/dist/assets/AvailableEscalationsPage-C_7y692Q.js.map +0 -1
  309. package/dashboard/dist/assets/FilterBar-Do7yR4xM.js +0 -2
  310. package/dashboard/dist/assets/HomePage-BEeodImU.js +0 -2
  311. package/dashboard/dist/assets/ListToolbar-CLtw-1QN.js +0 -2
  312. package/dashboard/dist/assets/OperationsPage-B2v9k3DT.js +0 -2
  313. package/dashboard/dist/assets/OperationsPage-B2v9k3DT.js.map +0 -1
  314. package/dashboard/dist/assets/OperatorDashboard-CkLfbxEh.js +0 -2
  315. package/dashboard/dist/assets/RoleDetailPage-DMphTvro.js +0 -8
  316. package/dashboard/dist/assets/RoleDetailPage-DMphTvro.js.map +0 -1
  317. package/dashboard/dist/assets/RolesPage-gi_kaot1.js +0 -2
  318. package/dashboard/dist/assets/RolesPage-gi_kaot1.js.map +0 -1
  319. package/dashboard/dist/assets/ScanCodesPage-CQ8RxMug.js +0 -2
  320. package/dashboard/dist/assets/ScanCodesPage-CQ8RxMug.js.map +0 -1
  321. package/dashboard/dist/assets/ScanSchemeDetailPage-puSbuxcV.js +0 -2
  322. package/dashboard/dist/assets/ScanSchemeDetailPage-puSbuxcV.js.map +0 -1
  323. package/dashboard/dist/assets/TopicsPage-Clr6Tl5P.js +0 -2
  324. package/dashboard/dist/assets/escalation-columns-Cp3UrT48.js +0 -2
  325. package/dashboard/dist/assets/escalation-columns-Cp3UrT48.js.map +0 -1
  326. package/dashboard/dist/assets/index--DG-qQBT.js +0 -65
  327. package/dashboard/dist/assets/index--DG-qQBT.js.map +0 -1
  328. package/dashboard/dist/assets/index-DEbY57_c.js +0 -3
  329. package/dashboard/dist/assets/index-DEbY57_c.js.map +0 -1
  330. package/dashboard/dist/assets/index-DnV6LSeY.css +0 -1
  331. package/dashboard/dist/assets/useEventHooks-DXCgDgiu.js +0 -2
  332. package/dashboard/dist/assets/vendor-icons-CN-bAo9U.js.map +0 -1
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // Escalation analytics service — grouped/temporal reads over the interval
4
+ // time-series (see aggregate-sql.ts for the SQL semantics). Pure read surface:
5
+ // no lifecycle change, no write path.
6
+ //
7
+ // Caching splits by anchor. A now-anchored result (membership now, a window
8
+ // reaching now) is pace-board-grade: short TTL, cleared by
9
+ // invalidateEscalationAggregates() on every escalation write on this
10
+ // container. A fully-past anchor is immutable under every long-tail write
11
+ // path — created_at is set at insert and terminal transitions stamp NOW(),
12
+ // which is strictly after any past anchor — so those entries survive
13
+ // invalidation and live on the longer TTL (bounding the documented SDK
14
+ // post-terminal-touch caveat rather than caching forever). Keys canonicalize
15
+ // the whole input, so both caches are LRU-bounded.
16
+ // ---------------------------------------------------------------------------
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.clearNowAnchoredAnalyticsCache = clearNowAnchoredAnalyticsCache;
19
+ exports.resetAnalyticsCaches = resetAnalyticsCaches;
20
+ exports.resolveEntitySystem = resolveEntitySystem;
21
+ exports.aggregateByFacets = aggregateByFacets;
22
+ exports.timelineByFacet = timelineByFacet;
23
+ const db_1 = require("../../lib/db");
24
+ const config_1 = require("../../modules/config");
25
+ const facets_1 = require("./facets");
26
+ const metrics_cache_1 = require("./metrics-cache");
27
+ const aggregate_sql_1 = require("./aggregate-sql");
28
+ const aggregate_validate_1 = require("./aggregate-validate");
29
+ const nowAnchoredCache = new metrics_cache_1.BoundedTtlCache(config_1.config.LT_ANALYTICS_CACHE_TTL_MS, config_1.config.LT_ANALYTICS_CACHE_MAX_ENTRIES);
30
+ const pastAnchoredCache = new metrics_cache_1.BoundedTtlCache(config_1.config.LT_ANALYTICS_PAST_CACHE_TTL_MS, config_1.config.LT_ANALYTICS_CACHE_MAX_ENTRIES);
31
+ /** Key-order-independent stringify, so `{a,b}` and `{b,a}` share one entry. */
32
+ function canonical(value) {
33
+ return JSON.stringify(value, (_key, v) => v && typeof v === 'object' && !Array.isArray(v)
34
+ ? Object.fromEntries(Object.entries(v).sort(([a], [b]) => (a < b ? -1 : 1)))
35
+ : v);
36
+ }
37
+ /** Called on every escalation write (via invalidateEscalationAggregates). */
38
+ function clearNowAnchoredAnalyticsCache() {
39
+ nowAnchoredCache.clear();
40
+ }
41
+ /** Test hook: drop BOTH analytics caches. */
42
+ function resetAnalyticsCaches() {
43
+ nowAnchoredCache.clear();
44
+ pastAnchoredCache.clear();
45
+ }
46
+ function matchesState(row, m) {
47
+ if (m.role !== undefined && row.role !== m.role)
48
+ return false;
49
+ if (m.roles !== undefined && !m.roles.includes(row.role))
50
+ return false;
51
+ if (m.subtype !== undefined && row.subtype !== m.subtype)
52
+ return false;
53
+ for (const [key, value] of Object.entries(m.facets ?? {})) {
54
+ if (row.facets[key] == null || row.facets[key] !== String(value))
55
+ return false;
56
+ }
57
+ for (const key of m.exists ?? []) {
58
+ if (row.facets[key] == null)
59
+ return false;
60
+ }
61
+ return true;
62
+ }
63
+ function toAggregateRow(raw, built, states) {
64
+ const facets = {};
65
+ for (const key of built.facets)
66
+ facets[key] = raw[`facet_${key}`] ?? null;
67
+ const row = { facets, sampleCount: Number(raw.sample_count) };
68
+ for (const column of built.columns)
69
+ row[column] = raw[column] ?? undefined;
70
+ if (built.measureKind === 'membership')
71
+ row.count = Number(raw.count);
72
+ else
73
+ row.dwellSeconds = Number(raw.dwell_seconds);
74
+ if (built.stateGrouped) {
75
+ row.state = raw.state_label;
76
+ }
77
+ else {
78
+ const label = (states ?? []).find((s) => matchesState(row, s.match));
79
+ if (label)
80
+ row.state = label.name;
81
+ }
82
+ return row;
83
+ }
84
+ function toIso(value) {
85
+ return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
86
+ }
87
+ function toTimelineInterval(raw, built) {
88
+ const facets = {};
89
+ for (const key of built.facets)
90
+ facets[key] = raw[`facet_${key}`] ?? null;
91
+ const interval = {
92
+ role: raw.role,
93
+ facets,
94
+ startedAt: toIso(raw.started_at),
95
+ endedAt: raw.ended_at == null ? null : toIso(raw.ended_at),
96
+ durationSeconds: Number(raw.duration_seconds),
97
+ };
98
+ if (built.columns.includes('subtype'))
99
+ interval.subtype = raw.subtype ?? undefined;
100
+ if (built.columns.includes('status'))
101
+ interval.status = raw.status;
102
+ return interval;
103
+ }
104
+ // ── Entity-system resolution ─────────────────────────────────────────────────
105
+ const ENTITY_SYSTEM_SQL = `
106
+ SELECT role, entity_state_source FROM lt_roles WHERE entity_facet = $1 ORDER BY role`;
107
+ const STATE_SOURCES_SQL = `
108
+ SELECT role, entity_state_source FROM lt_roles WHERE role = ANY($1::text[])`;
109
+ /**
110
+ * The roles declaring `entity_facet = key` — the entity's SYSTEM — with each
111
+ * role's state naming. Loud when nothing declares the key: an empty system is
112
+ * a config gap, not an empty result.
113
+ */
114
+ async function resolveEntitySystem(entityKey) {
115
+ (0, aggregate_validate_1.requireFacetKey)(entityKey, 'query.entity');
116
+ await (0, facets_1.ensureFacetReady)();
117
+ const { rows } = await (0, db_1.getPool)().query(ENTITY_SYSTEM_SQL, [entityKey]);
118
+ if (rows.length === 0) {
119
+ throw new aggregate_sql_1.AnalyticsInputError(`no roles declare entity_facet "${entityKey}" — set Entity on the roles this entity moves through`);
120
+ }
121
+ return rows.map((r) => ({
122
+ role: r.role,
123
+ source: r.entity_state_source === 'subtype' ? 'subtype' : 'role',
124
+ }));
125
+ }
126
+ /** State naming for an explicit role list (roles missing from lt_roles default to 'role'). */
127
+ async function stateSourcesFor(roles) {
128
+ const { rows } = await (0, db_1.getPool)().query(STATE_SOURCES_SQL, [roles]);
129
+ const sources = {};
130
+ for (const role of roles)
131
+ sources[role] = 'role';
132
+ for (const r of rows)
133
+ sources[r.role] = r.entity_state_source === 'subtype' ? 'subtype' : 'role';
134
+ return sources;
135
+ }
136
+ /**
137
+ * Resolve `query.entity` (and `groupBy.state`) into concrete scope: the
138
+ * system's roles replace `entity` on the filter (plus `exists: [entityKey]`
139
+ * so rows without the entity stay out), and per-role state sources ride along
140
+ * for the builder. The resolved system joins the cache key, so a role-config
141
+ * edit changes the key rather than serving a stale state shape.
142
+ */
143
+ async function resolveScope(query, needsStateSources) {
144
+ if (query.entity !== undefined) {
145
+ const system = await resolveEntitySystem(query.entity);
146
+ const { entity, ...rest } = query;
147
+ const resolved = {
148
+ ...rest,
149
+ roles: system.map((s) => s.role),
150
+ exists: [...new Set([...(rest.exists ?? []), entity])],
151
+ };
152
+ const sources = {};
153
+ for (const s of system)
154
+ sources[s.role] = s.source;
155
+ return { query: resolved, stateSources: sources, cacheSuffix: `:${canonical(system)}` };
156
+ }
157
+ if (needsStateSources) {
158
+ const roles = query.role ? [query.role] : query.roles ?? [];
159
+ if (roles.length === 0)
160
+ return { query, cacheSuffix: '' }; // builder rejects loudly
161
+ const sources = await stateSourcesFor(roles);
162
+ return { query, stateSources: sources, cacheSuffix: `:${canonical(sources)}` };
163
+ }
164
+ return { query, cacheSuffix: '' };
165
+ }
166
+ /**
167
+ * Grouped aggregate over the escalation intervals — membership at an instant
168
+ * or dwell over a window, grouped by columns + facets (+ the derived state
169
+ * label). One call replaces N countByFacets round-trips.
170
+ */
171
+ async function aggregateByFacets(input) {
172
+ const scope = await resolveScope(input.query ?? {}, input.groupBy?.state === true);
173
+ const effective = { ...input, query: scope.query };
174
+ // Validates loudly BEFORE any cache entry.
175
+ const built = (0, aggregate_sql_1.buildAggregateQuery)(effective, { stateSources: scope.stateSources });
176
+ const cache = built.nowAnchored ? nowAnchoredCache : pastAnchoredCache;
177
+ return (await cache.resolve(`agg:${canonical(input)}${scope.cacheSuffix}`, async () => {
178
+ await (0, facets_1.ensureFacetReady)();
179
+ const { rows } = await (0, db_1.getPool)().query(built.sql, built.params);
180
+ return {
181
+ groups: rows.slice(0, built.pageLimit).map((r) => toAggregateRow(r, built, input.states)),
182
+ overflow: rows.length > built.pageLimit,
183
+ };
184
+ }));
185
+ }
186
+ /**
187
+ * One entity's ordered interval sequence — `[created_at, ended_at)` spans with
188
+ * durations. Gaps between consecutive intervals are untracked time and are
189
+ * deliberately preserved, not filled.
190
+ */
191
+ async function timelineByFacet(input) {
192
+ const scope = await resolveScope(input.query ?? {}, false);
193
+ const effective = { ...input, query: scope.query };
194
+ const built = (0, aggregate_sql_1.buildTimelineQuery)(effective);
195
+ const cache = built.nowAnchored ? nowAnchoredCache : pastAnchoredCache;
196
+ return (await cache.resolve(`tl:${canonical(input)}${scope.cacheSuffix}`, async () => {
197
+ await (0, facets_1.ensureFacetReady)();
198
+ const { rows } = await (0, db_1.getPool)().query(built.sql, built.params);
199
+ return {
200
+ intervals: rows.slice(0, built.pageLimit).map((r) => toTimelineInterval(r, built)),
201
+ overflow: rows.length > built.pageLimit,
202
+ };
203
+ }));
204
+ }
@@ -97,16 +97,24 @@ async function installEscalationCompatView() {
97
97
  await client.query('SELECT pg_advisory_unlock($1)', [COMPAT_VIEW_LOCK_ID]).catch(() => { });
98
98
  client.release();
99
99
  }
100
- // The one index long-tail still owns builds OUTSIDE the advisory lock:
100
+ // The indexes long-tail owns build OUTSIDE the advisory lock:
101
101
  // CREATE INDEX CONCURRENTLY waits on every transaction holding a snapshot,
102
102
  // and sibling containers parked inside `SELECT pg_advisory_lock(...)` hold
103
103
  // exactly that — building inside the critical section would stall the build
104
104
  // against its own waiters. Out here the build blocks nothing (writes proceed
105
105
  // during CONCURRENTLY) and nothing blocks it.
106
- await ensureResolvedCoverIndex();
106
+ await ensureAppIndex('idx_hmsh_esc_resolved_cover', ENSURE_RESOLVED_COVER_INDEX);
107
+ await ensureAppIndex('idx_hmsh_esc_ended_at', ENSURE_ENDED_AT_INDEX);
107
108
  }
109
+ /** Reports existence + validity so a crash-orphaned INVALID build is rebuilt. */
110
+ const APP_INDEX_STATE = `
111
+ SELECT i.indisvalid AS valid
112
+ FROM pg_class c
113
+ JOIN pg_index i ON i.indexrelid = c.oid
114
+ JOIN pg_namespace n ON n.oid = c.relnamespace
115
+ WHERE c.relname = $1 AND n.nspname = 'public'`;
108
116
  /**
109
- * Idempotent, concurrent-boot-safe build of idx_hmsh_esc_resolved_cover.
117
+ * Idempotent, concurrent-boot-safe build of one app-layer index.
110
118
  *
111
119
  * A crashed CONCURRENTLY build leaves an INVALID index behind, and
112
120
  * `CREATE INDEX CONCURRENTLY IF NOT EXISTS` would then skip it forever — so
@@ -114,20 +122,20 @@ async function installEscalationCompatView() {
114
122
  * A create that loses a cross-container race logs and defers to the next
115
123
  * boot, which observes the winner's valid index and no-ops.
116
124
  */
117
- async function ensureResolvedCoverIndex() {
125
+ async function ensureAppIndex(name, ddl) {
118
126
  const pool = (0, db_1.getPool)();
119
127
  try {
120
- const { rows } = await pool.query(RESOLVED_COVER_INDEX_STATE);
128
+ const { rows } = await pool.query(APP_INDEX_STATE, [name]);
121
129
  if (rows[0]?.valid === true)
122
130
  return;
123
131
  if (rows.length > 0) {
124
- await pool.query('DROP INDEX IF EXISTS idx_hmsh_esc_resolved_cover');
132
+ await pool.query(`DROP INDEX IF EXISTS ${name}`);
125
133
  }
126
- await pool.query(ENSURE_RESOLVED_COVER_INDEX);
127
- logger_1.loggerRegistry.info('[escalation] idx_hmsh_esc_resolved_cover ensured');
134
+ await pool.query(ddl);
135
+ logger_1.loggerRegistry.info(`[escalation] ${name} ensured`);
128
136
  }
129
137
  catch (err) {
130
- logger_1.loggerRegistry.warn(`[escalation] resolved-cover index build deferred to next boot: ${err.message}`);
138
+ logger_1.loggerRegistry.warn(`[escalation] ${name} index build deferred to next boot: ${err.message}`);
131
139
  }
132
140
  }
133
141
  // Migrate legacy `lt_escalations` rows into `hmsh_escalations` (idempotent),
@@ -188,16 +196,38 @@ BEGIN
188
196
  END IF;
189
197
  END $$;`;
190
198
  // `available` mirrors the legacy isEffectivelyClaimed/isAvailable heuristic so
191
- // existing `SELECT *` consumers are unaffected; the column is additive.
199
+ // existing `SELECT *` consumers are unaffected; the columns are additive.
200
+ //
201
+ // `ended_at` is the instant the row left the live set: resolve stamps
202
+ // resolved_at in its guarded transition; cancel and expire stamp only
203
+ // updated_at in theirs, and no long-tail write path touches a terminal row
204
+ // afterward, so COALESCE(resolved_at, updated_at) is the transition instant
205
+ // for all three terminal statuses. (The SDK's updateEscalation /
206
+ // appendEscalationMilestones carry no status guard, so a post-terminal touch
207
+ // WOULD skew a cancelled/expired end — documented contract, not enforced.)
208
+ // Analytics SQL inlines the equivalent expression rather than reading this
209
+ // column (so non-default liveStatuses compose and the partial index predicate
210
+ // stays provable); the view column serves external and debugging consumers.
211
+ // If the SDK ever ships a physical ended_at column, the SELECT * expansion
212
+ // collides with this alias — remediation is dropping the view-level column
213
+ // and reading the (strictly better) real one.
214
+ //
215
+ // DROP+CREATE (not CREATE OR REPLACE) because the base table's column set
216
+ // moves with SDK migrations: REPLACE re-expands SELECT * and errors the
217
+ // moment a new base column lands before the computed aliases. The swap runs
218
+ // inside the install transaction, so readers see the old view or the new
219
+ // one, never a gap.
192
220
  const CREATE_COMPAT_VIEW = `
193
- CREATE OR REPLACE VIEW public.lt_escalations AS
221
+ DROP VIEW IF EXISTS public.lt_escalations;
222
+ CREATE VIEW public.lt_escalations AS
194
223
  SELECT *,
195
- (assigned_to IS NULL OR assigned_until IS NULL OR assigned_until <= NOW()) AS available
224
+ (assigned_to IS NULL OR assigned_until IS NULL OR assigned_until <= NOW()) AS available,
225
+ (CASE WHEN status <> 'pending' THEN COALESCE(resolved_at, updated_at) END) AS ended_at
196
226
  FROM public.hmsh_escalations;`;
197
227
  // Index ownership — hmsh 0.25.0 ships the general aggregate indexes on
198
228
  // hmsh_escalations (idx_hmsh_esc_stats_pending covers the pending backlog
199
229
  // incl. assigned_to/assigned_until; _stats_created and _stats_resolved bound
200
- // the created/resolved windows). Long-tail keeps exactly ONE app-layer index:
230
+ // the created/resolved windows). Long-tail keeps exactly TWO app-layer indexes:
201
231
  //
202
232
  // idx_hmsh_esc_resolved_cover (role, resolved_at DESC, claimed_at, created_at)
203
233
  // WHERE status = 'resolved'
@@ -206,6 +236,15 @@ CREATE OR REPLACE VIEW public.lt_escalations AS
206
236
  // trailing claimed_at/created_at feed PERCENTILE_CONT(resolved_at -
207
237
  // claimed_at) and (claimed_at - created_at) from the index.
208
238
  //
239
+ // idx_hmsh_esc_ended_at ((COALESCE(resolved_at, updated_at)))
240
+ // WHERE status <> 'pending'
241
+ // Serves the analytics interval scans (aggregate-sql.ts): dwell and
242
+ // past-membership need "terminal rows whose end instant is after the
243
+ // window/asOf" — without this, every such query scans all terminal
244
+ // history, growing without bound. Write cost is the cheapest possible:
245
+ // inserts are 'pending' (excluded by the predicate), so exactly one
246
+ // index write occurs per row, at its terminal transition.
247
+ //
209
248
  // Everything else this branch once created is dropped below: the pending
210
249
  // index duplicated _stats_pending, and the claimed cover had no consuming
211
250
  // query — both were pure write amplification on the hot claim/resolve path.
@@ -218,15 +257,12 @@ const DROP_SUPERSEDED_PENDING_INDEX = `
218
257
  DROP INDEX IF EXISTS idx_hmsh_esc_pending_role_created`;
219
258
  const DROP_UNCONSUMED_CLAIMED_COVER_INDEX = `
220
259
  DROP INDEX IF EXISTS idx_hmsh_esc_claimed_cover`;
221
- // ── The station-percentile cover ─────────────────────────────────────────────
222
- /** Reports existence + validity so a crash-orphaned INVALID build is rebuilt. */
223
- const RESOLVED_COVER_INDEX_STATE = `
224
- SELECT i.indisvalid AS valid
225
- FROM pg_class c
226
- JOIN pg_index i ON i.indexrelid = c.oid
227
- JOIN pg_namespace n ON n.oid = c.relnamespace
228
- WHERE c.relname = 'idx_hmsh_esc_resolved_cover' AND n.nspname = 'public'`;
260
+ // ── The app-layer indexes (built via ensureAppIndex) ────────────────────────
229
261
  const ENSURE_RESOLVED_COVER_INDEX = `\
230
262
  CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_hmsh_esc_resolved_cover
231
263
  ON public.hmsh_escalations (role, resolved_at DESC, claimed_at, created_at)
232
264
  WHERE status = 'resolved'`;
265
+ const ENSURE_ENDED_AT_INDEX = `\
266
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_hmsh_esc_ended_at
267
+ ON public.hmsh_escalations ((COALESCE(resolved_at, updated_at)))
268
+ WHERE status <> 'pending'`;
@@ -22,6 +22,10 @@ exports.FACET_KEY = /^[a-zA-Z0-9_]+$/;
22
22
  function metaExpr(key, numeric = false) {
23
23
  return numeric ? `(metadata->>'${key}')::numeric` : `(metadata->>'${key}')`;
24
24
  }
25
+ /** Escape LIKE wildcards so a prefix VALUE is matched literally. */
26
+ function escapeLikePrefix(value) {
27
+ return value.replace(/[\\%_]/g, (c) => `\\${c}`);
28
+ }
25
29
  /**
26
30
  * Build the WHERE clause for a faceted query, pushing bound params into `params`.
27
31
  * Every value is parameterized; only validated column names / metadata keys and
@@ -51,10 +55,23 @@ function buildFacetWhere(q, params, opts = {}) {
51
55
  params.push(JSON.stringify(q.facets));
52
56
  clauses.push(`metadata @> $${params.length}::jsonb`); // GIN-served
53
57
  }
58
+ if (q.anyOf && q.anyOf.length) {
59
+ params.push(q.anyOf.map((a) => JSON.stringify(a)));
60
+ clauses.push(`metadata @> ANY($${params.length}::jsonb[])`); // GIN-served OR
61
+ }
54
62
  if (q.block && q.block.length) {
55
63
  params.push(q.block.map((b) => JSON.stringify(b)));
56
64
  clauses.push(`NOT (metadata @> ANY($${params.length}::jsonb[]))`);
57
65
  }
66
+ // Case-insensitive prefix on facet VALUES — the locate affordance. No
67
+ // dedicated index by design: the predicate composes into scans the rest of
68
+ // the filter already bounds (the live set, or an analytics window).
69
+ for (const [key, value] of Object.entries(q.prefix ?? {})) {
70
+ if (!exports.FACET_KEY.test(key) || typeof value !== 'string' || !value)
71
+ continue;
72
+ params.push(`${escapeLikePrefix(value)}%`);
73
+ clauses.push(`${metaExpr(key)} ILIKE $${params.length}`);
74
+ }
58
75
  for (const r of q.range ?? []) {
59
76
  if (!exports.FACET_KEY.test(r.facet) || !RANGE_OPS.has(r.op))
60
77
  continue;
@@ -5,3 +5,5 @@ export * from './queries';
5
5
  export * from './resolver-validation';
6
6
  export { ensureEscalationCompatView } from './client';
7
7
  export { ensureFacetReady, searchByFacets, searchGroups, countByFacets, claimGroups, claimByFacets, } from './facets';
8
+ export { aggregateByFacets, timelineByFacet, resolveEntitySystem, resetAnalyticsCaches, } from './aggregates';
9
+ export { AnalyticsInputError } from './aggregate-validate';
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.claimByFacets = exports.claimGroups = exports.countByFacets = exports.searchGroups = exports.searchByFacets = exports.ensureFacetReady = exports.ensureEscalationCompatView = void 0;
17
+ exports.AnalyticsInputError = exports.resetAnalyticsCaches = exports.resolveEntitySystem = exports.timelineByFacet = exports.aggregateByFacets = exports.claimByFacets = exports.claimGroups = exports.countByFacets = exports.searchGroups = exports.searchByFacets = exports.ensureFacetReady = exports.ensureEscalationCompatView = void 0;
18
18
  __exportStar(require("./types"), exports);
19
19
  __exportStar(require("./crud"), exports);
20
20
  __exportStar(require("./bulk"), exports);
@@ -29,3 +29,10 @@ Object.defineProperty(exports, "searchGroups", { enumerable: true, get: function
29
29
  Object.defineProperty(exports, "countByFacets", { enumerable: true, get: function () { return facets_1.countByFacets; } });
30
30
  Object.defineProperty(exports, "claimGroups", { enumerable: true, get: function () { return facets_1.claimGroups; } });
31
31
  Object.defineProperty(exports, "claimByFacets", { enumerable: true, get: function () { return facets_1.claimByFacets; } });
32
+ var aggregates_1 = require("./aggregates");
33
+ Object.defineProperty(exports, "aggregateByFacets", { enumerable: true, get: function () { return aggregates_1.aggregateByFacets; } });
34
+ Object.defineProperty(exports, "timelineByFacet", { enumerable: true, get: function () { return aggregates_1.timelineByFacet; } });
35
+ Object.defineProperty(exports, "resolveEntitySystem", { enumerable: true, get: function () { return aggregates_1.resolveEntitySystem; } });
36
+ Object.defineProperty(exports, "resetAnalyticsCaches", { enumerable: true, get: function () { return aggregates_1.resetAnalyticsCaches; } });
37
+ var aggregate_validate_1 = require("./aggregate-validate");
38
+ Object.defineProperty(exports, "AnalyticsInputError", { enumerable: true, get: function () { return aggregate_validate_1.AnalyticsInputError; } });
@@ -12,3 +12,21 @@ export declare class TtlCache<T> {
12
12
  resolve(key: string, compute: () => Promise<T>): Promise<T>;
13
13
  clear(): void;
14
14
  }
15
+ /**
16
+ * TtlCache with an LRU bound — for key spaces the caller doesn't control.
17
+ *
18
+ * The analytics caches key by a canonical stringify of the whole query input,
19
+ * so distinct keys are unbounded; the expired-entry sweep alone can't bound
20
+ * memory the way it does for TtlCache's small role-set × period key space.
21
+ * Same single-flight and reject-evict semantics; a hit refreshes recency
22
+ * (Map delete+set, the enforcement-cache pattern), and inserting past
23
+ * `maxEntries` evicts the least-recently-used entry.
24
+ */
25
+ export declare class BoundedTtlCache<T> {
26
+ private readonly ttlMs;
27
+ private readonly maxEntries;
28
+ private readonly store;
29
+ constructor(ttlMs: number, maxEntries: number);
30
+ resolve(key: string, compute: () => Promise<T>): Promise<T>;
31
+ clear(): void;
32
+ }
@@ -12,7 +12,7 @@
12
12
  // container drift within the TTL is acceptable. No cross-node coordination.
13
13
  // ---------------------------------------------------------------------------
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.TtlCache = void 0;
15
+ exports.BoundedTtlCache = exports.TtlCache = void 0;
16
16
  class TtlCache {
17
17
  constructor(ttlMs) {
18
18
  this.ttlMs = ttlMs;
@@ -51,3 +51,49 @@ class TtlCache {
51
51
  }
52
52
  }
53
53
  exports.TtlCache = TtlCache;
54
+ /**
55
+ * TtlCache with an LRU bound — for key spaces the caller doesn't control.
56
+ *
57
+ * The analytics caches key by a canonical stringify of the whole query input,
58
+ * so distinct keys are unbounded; the expired-entry sweep alone can't bound
59
+ * memory the way it does for TtlCache's small role-set × period key space.
60
+ * Same single-flight and reject-evict semantics; a hit refreshes recency
61
+ * (Map delete+set, the enforcement-cache pattern), and inserting past
62
+ * `maxEntries` evicts the least-recently-used entry.
63
+ */
64
+ class BoundedTtlCache {
65
+ constructor(ttlMs, maxEntries) {
66
+ this.ttlMs = ttlMs;
67
+ this.maxEntries = maxEntries;
68
+ this.store = new Map();
69
+ }
70
+ async resolve(key, compute) {
71
+ const hit = this.store.get(key);
72
+ if (hit && Date.now() - hit.at < this.ttlMs) {
73
+ this.store.delete(key); // refresh recency
74
+ this.store.set(key, hit);
75
+ return hit.value;
76
+ }
77
+ for (const [k, entry] of this.store) {
78
+ if (Date.now() - entry.at >= this.ttlMs)
79
+ this.store.delete(k);
80
+ }
81
+ while (this.store.size >= this.maxEntries) {
82
+ const oldest = this.store.keys().next().value;
83
+ this.store.delete(oldest);
84
+ }
85
+ const value = compute();
86
+ this.store.set(key, { at: Date.now(), value });
87
+ try {
88
+ return await value;
89
+ }
90
+ catch (err) {
91
+ this.store.delete(key);
92
+ throw err;
93
+ }
94
+ }
95
+ clear() {
96
+ this.store.clear();
97
+ }
98
+ }
99
+ exports.BoundedTtlCache = BoundedTtlCache;
@@ -17,6 +17,7 @@ const facet_sql_1 = require("./facet-sql");
17
17
  const types_1 = require("./types");
18
18
  const sql_1 = require("./sql");
19
19
  const metrics_cache_1 = require("./metrics-cache");
20
+ const aggregates_1 = require("./aggregates");
20
21
  /**
21
22
  * Default sort is priority ASC, created_at ASC. A user-chosen `sort_by` maps to
22
23
  * a single column (DESC unless `order='asc'`), matching the legacy behavior.
@@ -380,6 +381,7 @@ const stationPeriodCache = new metrics_cache_1.TtlCache(STATION_PERIOD_CACHE_TTL
380
381
  function invalidateEscalationAggregates() {
381
382
  stationPeriodCache.clear();
382
383
  escalationStatsCache.clear();
384
+ (0, aggregates_1.clearNowAnchoredAnalyticsCache)();
383
385
  }
384
386
  /** Test hook: drop cached period metrics AND escalation stats so tests observe fresh rows. */
385
387
  function resetStationMetricsCache() {
@@ -0,0 +1,16 @@
1
+ import type { LTApiAuth } from '../../types/sdk';
2
+ /** The acting-auth resolution result: an auth to act as, or the loud reason not to. */
3
+ export type ActingAuthResult = {
4
+ ok: true;
5
+ auth: LTApiAuth;
6
+ } | {
7
+ ok: false;
8
+ error: string;
9
+ };
10
+ /**
11
+ * Exchange a supplied acting grant for the person it names. A supplied token
12
+ * that fails — wrong label, expired, exhausted, or a vanished user — is a
13
+ * terminal error, never a silent fall-back to the session identity: that
14
+ * would misattribute whatever follows.
15
+ */
16
+ export declare function resolveActingAuth(actingToken: string): Promise<ActingAuthResult>;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.resolveActingAuth = resolveActingAuth;
37
+ const userService = __importStar(require("../user"));
38
+ const ephemeral_1 = require("./ephemeral");
39
+ const types_1 = require("../../types");
40
+ /**
41
+ * Exchange a supplied acting grant for the person it names. A supplied token
42
+ * that fails — wrong label, expired, exhausted, or a vanished user — is a
43
+ * terminal error, never a silent fall-back to the session identity: that
44
+ * would misattribute whatever follows.
45
+ */
46
+ async function resolveActingAuth(actingToken) {
47
+ const parsed = (0, ephemeral_1.parseEphemeralToken)(actingToken);
48
+ if (!parsed || parsed.label !== types_1.ACTING_IDENTITY_LABEL) {
49
+ return { ok: false, error: 'actingToken is not an acting-identity grant' };
50
+ }
51
+ const userId = await (0, ephemeral_1.exchangeEphemeralToken)(actingToken);
52
+ if (!userId) {
53
+ return { ok: false, error: 'acting identity expired — scan your badge again' };
54
+ }
55
+ const user = await userService.getUser(userId);
56
+ if (!user || user.status !== 'active') {
57
+ return { ok: false, error: 'acting identity is no longer an active user' };
58
+ }
59
+ return { ok: true, auth: { userId: user.id } };
60
+ }
@@ -150,6 +150,8 @@ async function updateRoleMetadata(role, input) {
150
150
  provided('default_pins'), input.default_pins != null ? JSON.stringify(input.default_pins) : null,
151
151
  provided('enforce_schema'), input.enforce_schema ?? null,
152
152
  provided('ops_home_default'), input.ops_home_default ?? null,
153
+ provided('entity_facet'), input.entity_facet ?? null,
154
+ provided('entity_state_source'), input.entity_state_source ?? null,
153
155
  ]);
154
156
  if (!rows[0])
155
157
  return null;
@@ -50,8 +50,16 @@ export declare const DELETE_ROLE = "\n DELETE FROM lt_roles WHERE role = $1";
50
50
  * ops_home_default ($35 = provided sentinel, $36 = value) is single-holder:
51
51
  * setting it true clears the flag on every other role in the same statement
52
52
  * (the home_clear CTE touches only OTHER rows, so the two UPDATEs never meet).
53
+ *
54
+ * entity_facet ($37 = provided sentinel, $38 = value) is unversioned config:
55
+ * the metadata key naming the entity that moves through this role (powers the
56
+ * analytics surfaces — entity counts, per-entity dwell, timelines).
57
+ *
58
+ * entity_state_source ($39 = provided sentinel, $40 = value) is unversioned
59
+ * config: 'role' | 'subtype' — how this role names its contribution to the
60
+ * entity's state space (the station itself, or its subtypes).
53
61
  */
54
- export declare const UPDATE_ROLE_METADATA = "\n WITH home_clear AS (\n UPDATE lt_roles SET ops_home_default = false\n WHERE $35::boolean AND COALESCE($36::boolean, false) AND role <> $1 AND ops_home_default\n ), updated AS (\n UPDATE lt_roles SET\n title = CASE WHEN $2::boolean THEN $3 ELSE title END,\n description = CASE WHEN $4::boolean THEN $5 ELSE description END,\n form_schema = CASE WHEN $6::boolean THEN $7::jsonb ELSE form_schema END,\n metadata_schema = CASE WHEN $8::boolean THEN $9::jsonb ELSE metadata_schema END,\n properties = CASE WHEN $10::boolean THEN COALESCE($11::jsonb, '{}'::jsonb) ELSE properties END,\n ops_visible = CASE WHEN $12::boolean THEN $13::boolean ELSE ops_visible END,\n parent_role = CASE WHEN $14::boolean THEN $15 ELSE parent_role END,\n sla_minutes = CASE WHEN $16::boolean THEN $17::numeric ELSE sla_minutes END,\n target_per_hour = CASE WHEN $18::boolean THEN $19::numeric ELSE target_per_hour END,\n worker_count = CASE WHEN $20::boolean THEN $21::int ELSE worker_count END,\n priority_threshold_minutes\n = CASE WHEN $22::boolean THEN $23::numeric ELSE priority_threshold_minutes END,\n priority_facet = CASE WHEN $24::boolean THEN $25 ELSE priority_facet END,\n list_schema = CASE WHEN $29::boolean THEN $30::jsonb ELSE list_schema END,\n default_pins = CASE WHEN $31::boolean THEN $32::jsonb ELSE default_pins END,\n enforce_schema = CASE WHEN $33::boolean THEN COALESCE($34::boolean, false) ELSE enforce_schema END,\n ops_home_default = CASE WHEN $35::boolean THEN COALESCE($36::boolean, false) ELSE ops_home_default END,\n current_schema_version = CASE\n WHEN ($6::boolean AND $7::jsonb IS DISTINCT FROM form_schema)\n OR ($8::boolean AND $9::jsonb IS DISTINCT FROM metadata_schema)\n THEN COALESCE(current_schema_version, 0) + 1\n ELSE current_schema_version END,\n current_list_schema_version = CASE\n WHEN $29::boolean AND $30::jsonb IS DISTINCT FROM list_schema\n THEN COALESCE(current_list_schema_version, 0) + 1\n ELSE current_list_schema_version END\n WHERE role = $1\n RETURNING\n role, title, description, form_schema, metadata_schema, properties,\n ops_visible, ops_home_default, parent_role, sla_minutes, target_per_hour, worker_count,\n priority_threshold_minutes, priority_facet,\n current_schema_version, list_schema, current_list_schema_version,\n default_pins, enforce_schema\n ), snapshot AS (\n INSERT INTO lt_role_schemas (role, version, form_schema, metadata_schema, change_summary)\n SELECT role, current_schema_version, form_schema, metadata_schema, $26\n FROM updated\n WHERE ($6::boolean OR $8::boolean) AND current_schema_version IS NOT NULL\n ON CONFLICT (role, version) DO NOTHING\n ), list_snapshot AS (\n INSERT INTO lt_role_list_schemas (role, version, list_schema, change_summary)\n SELECT role, current_list_schema_version, list_schema, $26\n FROM updated\n WHERE $29::boolean AND current_list_schema_version IS NOT NULL\n ON CONFLICT (role, version) DO NOTHING\n ), upstream_prune AS (\n DELETE FROM lt_role_upstreams\n WHERE $27::boolean AND role = $1 AND upstream_role <> ALL($28::text[])\n ), upstream_add AS (\n INSERT INTO lt_role_upstreams (role, upstream_role)\n SELECT $1, u FROM unnest($28::text[]) AS u\n WHERE $27::boolean\n ON CONFLICT DO NOTHING\n )\n SELECT * FROM updated";
62
+ export declare const UPDATE_ROLE_METADATA = "\n WITH home_clear AS (\n UPDATE lt_roles SET ops_home_default = false\n WHERE $35::boolean AND COALESCE($36::boolean, false) AND role <> $1 AND ops_home_default\n ), updated AS (\n UPDATE lt_roles SET\n title = CASE WHEN $2::boolean THEN $3 ELSE title END,\n description = CASE WHEN $4::boolean THEN $5 ELSE description END,\n form_schema = CASE WHEN $6::boolean THEN $7::jsonb ELSE form_schema END,\n metadata_schema = CASE WHEN $8::boolean THEN $9::jsonb ELSE metadata_schema END,\n properties = CASE WHEN $10::boolean THEN COALESCE($11::jsonb, '{}'::jsonb) ELSE properties END,\n ops_visible = CASE WHEN $12::boolean THEN $13::boolean ELSE ops_visible END,\n parent_role = CASE WHEN $14::boolean THEN $15 ELSE parent_role END,\n sla_minutes = CASE WHEN $16::boolean THEN $17::numeric ELSE sla_minutes END,\n target_per_hour = CASE WHEN $18::boolean THEN $19::numeric ELSE target_per_hour END,\n worker_count = CASE WHEN $20::boolean THEN $21::int ELSE worker_count END,\n priority_threshold_minutes\n = CASE WHEN $22::boolean THEN $23::numeric ELSE priority_threshold_minutes END,\n priority_facet = CASE WHEN $24::boolean THEN $25 ELSE priority_facet END,\n entity_facet = CASE WHEN $37::boolean THEN $38 ELSE entity_facet END,\n entity_state_source\n = CASE WHEN $39::boolean THEN COALESCE($40, 'role') ELSE entity_state_source END,\n list_schema = CASE WHEN $29::boolean THEN $30::jsonb ELSE list_schema END,\n default_pins = CASE WHEN $31::boolean THEN $32::jsonb ELSE default_pins END,\n enforce_schema = CASE WHEN $33::boolean THEN COALESCE($34::boolean, false) ELSE enforce_schema END,\n ops_home_default = CASE WHEN $35::boolean THEN COALESCE($36::boolean, false) ELSE ops_home_default END,\n current_schema_version = CASE\n WHEN ($6::boolean AND $7::jsonb IS DISTINCT FROM form_schema)\n OR ($8::boolean AND $9::jsonb IS DISTINCT FROM metadata_schema)\n THEN COALESCE(current_schema_version, 0) + 1\n ELSE current_schema_version END,\n current_list_schema_version = CASE\n WHEN $29::boolean AND $30::jsonb IS DISTINCT FROM list_schema\n THEN COALESCE(current_list_schema_version, 0) + 1\n ELSE current_list_schema_version END\n WHERE role = $1\n RETURNING\n role, title, description, form_schema, metadata_schema, properties,\n ops_visible, ops_home_default, parent_role, sla_minutes, target_per_hour, worker_count,\n priority_threshold_minutes, priority_facet, entity_facet, entity_state_source,\n current_schema_version, list_schema, current_list_schema_version,\n default_pins, enforce_schema\n ), snapshot AS (\n INSERT INTO lt_role_schemas (role, version, form_schema, metadata_schema, change_summary)\n SELECT role, current_schema_version, form_schema, metadata_schema, $26\n FROM updated\n WHERE ($6::boolean OR $8::boolean) AND current_schema_version IS NOT NULL\n ON CONFLICT (role, version) DO NOTHING\n ), list_snapshot AS (\n INSERT INTO lt_role_list_schemas (role, version, list_schema, change_summary)\n SELECT role, current_list_schema_version, list_schema, $26\n FROM updated\n WHERE $29::boolean AND current_list_schema_version IS NOT NULL\n ON CONFLICT (role, version) DO NOTHING\n ), upstream_prune AS (\n DELETE FROM lt_role_upstreams\n WHERE $27::boolean AND role = $1 AND upstream_role <> ALL($28::text[])\n ), upstream_add AS (\n INSERT INTO lt_role_upstreams (role, upstream_role)\n SELECT $1, u FROM unnest($28::text[]) AS u\n WHERE $27::boolean\n ON CONFLICT DO NOTHING\n )\n SELECT * FROM updated";
55
63
  export declare const GET_ROLE_FORM_SCHEMA = "\n SELECT form_schema FROM lt_roles WHERE role = $1";
56
64
  /**
57
65
  * The set of roles with server-side resolver validation turned on. Small and
@@ -75,7 +83,7 @@ export declare const GET_ROLE_LIST_SCHEMA_VERSION = "\n SELECT s.role, s.versio
75
83
  * never versioned its list schema still answers, with version NULL).
76
84
  */
77
85
  export declare const GET_ROLE_LIST_SCHEMA_CURRENT = "\n SELECT r.role, r.current_list_schema_version AS version, r.list_schema,\n s.change_summary, s.created_at,\n r.current_list_schema_version AS latest_version\n FROM lt_roles r\n LEFT JOIN lt_role_list_schemas s\n ON s.role = r.role AND s.version = r.current_list_schema_version\n WHERE r.role = $1";
78
- export declare const LIST_ROLES_WITH_DETAILS = "\n WITH\n user_counts AS (\n SELECT role, COUNT(DISTINCT user_id)::int AS cnt\n FROM lt_user_roles\n GROUP BY role\n ),\n chain_counts AS (\n SELECT role, COUNT(*)::int AS cnt\n FROM (\n SELECT source_role AS role FROM lt_config_role_escalations\n UNION ALL\n SELECT target_role AS role FROM lt_config_role_escalations\n ) c\n GROUP BY role\n ),\n workflow_counts AS (\n SELECT role, COUNT(*)::int AS cnt\n FROM lt_config_roles\n GROUP BY role\n ),\n upstreams AS (\n SELECT role, array_agg(upstream_role ORDER BY upstream_role) AS ups\n FROM lt_role_upstreams\n GROUP BY role\n )\n SELECT\n r.role,\n r.title,\n r.description,\n r.form_schema,\n r.metadata_schema,\n r.properties,\n r.ops_visible,\n r.ops_home_default,\n r.parent_role,\n r.sla_minutes,\n r.target_per_hour,\n r.worker_count,\n r.priority_threshold_minutes,\n r.priority_facet,\n r.current_schema_version,\n r.list_schema,\n r.current_list_schema_version,\n r.default_pins,\n r.enforce_schema,\n COALESCE(up.ups, '{}') AS upstream_roles,\n COALESCE(uc.cnt, 0) AS user_count,\n COALESCE(cc.cnt, 0) AS chain_count,\n COALESCE(wc.cnt, 0) AS workflow_count\n FROM lt_roles r\n LEFT JOIN user_counts uc ON uc.role = r.role\n LEFT JOIN chain_counts cc ON cc.role = r.role\n LEFT JOIN workflow_counts wc ON wc.role = r.role\n LEFT JOIN upstreams up ON up.role = r.role\n ORDER BY r.role";
86
+ export declare const LIST_ROLES_WITH_DETAILS = "\n WITH\n user_counts AS (\n SELECT role, COUNT(DISTINCT user_id)::int AS cnt\n FROM lt_user_roles\n GROUP BY role\n ),\n chain_counts AS (\n SELECT role, COUNT(*)::int AS cnt\n FROM (\n SELECT source_role AS role FROM lt_config_role_escalations\n UNION ALL\n SELECT target_role AS role FROM lt_config_role_escalations\n ) c\n GROUP BY role\n ),\n workflow_counts AS (\n SELECT role, COUNT(*)::int AS cnt\n FROM lt_config_roles\n GROUP BY role\n ),\n upstreams AS (\n SELECT role, array_agg(upstream_role ORDER BY upstream_role) AS ups\n FROM lt_role_upstreams\n GROUP BY role\n )\n SELECT\n r.role,\n r.title,\n r.description,\n r.form_schema,\n r.metadata_schema,\n r.properties,\n r.ops_visible,\n r.ops_home_default,\n r.parent_role,\n r.sla_minutes,\n r.target_per_hour,\n r.worker_count,\n r.priority_threshold_minutes,\n r.priority_facet,\n r.entity_facet,\n r.entity_state_source,\n r.current_schema_version,\n r.list_schema,\n r.current_list_schema_version,\n r.default_pins,\n r.enforce_schema,\n COALESCE(up.ups, '{}') AS upstream_roles,\n COALESCE(uc.cnt, 0) AS user_count,\n COALESCE(cc.cnt, 0) AS chain_count,\n COALESCE(wc.cnt, 0) AS workflow_count\n FROM lt_roles r\n LEFT JOIN user_counts uc ON uc.role = r.role\n LEFT JOIN chain_counts cc ON cc.role = r.role\n LEFT JOIN workflow_counts wc ON wc.role = r.role\n LEFT JOIN upstreams up ON up.role = r.role\n ORDER BY r.role";
79
87
  export declare const COUNT_USER_ROLE_REFS = "\n SELECT COUNT(*)::int AS cnt FROM lt_user_roles WHERE role = $1";
80
88
  export declare const COUNT_CHAIN_REFS = "\n SELECT COUNT(*)::int AS cnt\n FROM lt_config_role_escalations\n WHERE source_role = $1 OR target_role = $1";
81
89
  export declare const COUNT_WORKFLOW_REFS = "\n SELECT COUNT(*)::int AS cnt FROM lt_config_roles WHERE role = $1";