@hotmeshio/long-tail 0.18.1 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/build/api/escalations/index.d.ts +1 -1
  2. package/build/api/escalations/index.js +2 -1
  3. package/build/api/escalations/list.d.ts +11 -0
  4. package/build/api/escalations/list.js +31 -2
  5. package/build/api/users.d.ts +11 -0
  6. package/build/api/users.js +19 -0
  7. package/build/api/workflows/config.d.ts +5 -1
  8. package/build/api/workflows/config.js +9 -1
  9. package/build/api/workflows/index.d.ts +3 -0
  10. package/build/api/workflows/index.js +7 -1
  11. package/build/api/workflows/input-validation.d.ts +13 -0
  12. package/build/api/workflows/input-validation.js +38 -0
  13. package/build/api/workflows/invocable.d.ts +19 -0
  14. package/build/api/workflows/invocable.js +106 -0
  15. package/build/api/workflows/invocation.js +6 -0
  16. package/build/lib/db/schemas/034_workflow_input_schema.sql +13 -0
  17. package/build/lib/uuid.d.ts +11 -0
  18. package/build/lib/uuid.js +20 -0
  19. package/build/routes/escalations/list.js +10 -1
  20. package/build/routes/users.js +9 -0
  21. package/build/routes/workflows/config.js +2 -0
  22. package/build/routes/workflows/discovery.js +14 -0
  23. package/build/scripts/ortho-populate.d.ts +3 -1
  24. package/build/scripts/ortho-populate.js +15 -5
  25. package/build/sdk/index.d.ts +1 -0
  26. package/build/services/config/cache.js +2 -0
  27. package/build/services/config/read.js +4 -0
  28. package/build/services/config/sql.d.ts +2 -2
  29. package/build/services/config/sql.js +7 -5
  30. package/build/services/config/write.js +11 -1
  31. package/build/services/escalation/batch.js +3 -0
  32. package/build/services/escalation/bulk.js +8 -0
  33. package/build/services/escalation/crud.js +26 -0
  34. package/build/services/escalation/queries.d.ts +14 -1
  35. package/build/services/escalation/queries.js +49 -7
  36. package/build/services/escalation/sql.d.ts +4 -2
  37. package/build/services/escalation/sql.js +4 -0
  38. package/build/services/interceptor/index.js +7 -0
  39. package/build/services/invocation-access.d.ts +18 -0
  40. package/build/services/invocation-access.js +32 -0
  41. package/build/services/role/types.d.ts +1 -1
  42. package/build/services/role/types.js +1 -1
  43. package/build/services/user/auth.js +2 -2
  44. package/build/services/user/crud.d.ts +13 -0
  45. package/build/services/user/crud.js +26 -3
  46. package/build/services/user/index.d.ts +1 -1
  47. package/build/services/user/index.js +2 -1
  48. package/build/services/user/sql.d.ts +11 -5
  49. package/build/services/user/sql.js +18 -6
  50. package/build/services/workflow-invocation.js +4 -9
  51. package/build/shared/form-validation/index.d.ts +1 -0
  52. package/build/shared/form-validation/index.js +1 -0
  53. package/build/shared/form-validation/invoke-context.d.ts +7 -0
  54. package/build/shared/form-validation/invoke-context.js +11 -0
  55. package/build/shared/form-validation/validate-resolver-payload.d.ts +1 -1
  56. package/build/shared/form-validation/validate-resolver-payload.js +1 -0
  57. package/build/shared/form-validation/x-lt-help.d.ts +2 -2
  58. package/build/shared/form-validation/x-lt-help.js +2 -2
  59. package/build/shared/form-validation/x-lt-show-if.d.ts +1 -0
  60. package/build/shared/form-validation/x-lt-show-if.js +3 -0
  61. package/build/start/workers.js +2 -0
  62. package/build/system/mcp-servers/admin/workflows.js +15 -2
  63. package/build/tsconfig.tsbuildinfo +1 -1
  64. package/build/types/config.d.ts +6 -0
  65. package/build/types/index.d.ts +2 -0
  66. package/build/types/index.js +4 -1
  67. package/build/types/startup.d.ts +10 -0
  68. package/build/types/validation.d.ts +4 -2
  69. package/build/types/workflow-icons.d.ts +86 -0
  70. package/build/types/workflow-icons.js +92 -0
  71. package/dashboard/dist/assets/{AdminDashboard-DDY8bceq.js → AdminDashboard-Drs-9TRM.js} +2 -2
  72. package/dashboard/dist/assets/{AdminDashboard-DDY8bceq.js.map → AdminDashboard-Drs-9TRM.js.map} +1 -1
  73. package/dashboard/dist/assets/{AgentConfigPage-D47sHnup.js → AgentConfigPage-BDPDZ7-v.js} +3 -3
  74. package/dashboard/dist/assets/{AgentConfigPage-D47sHnup.js.map → AgentConfigPage-BDPDZ7-v.js.map} +1 -1
  75. package/dashboard/dist/assets/{AgentDetailPage-DwrfByA2.js → AgentDetailPage-CGBulM4_.js} +3 -3
  76. package/dashboard/dist/assets/{AgentDetailPage-DwrfByA2.js.map → AgentDetailPage-CGBulM4_.js.map} +1 -1
  77. package/dashboard/dist/assets/AgentsPage-D77JeViQ.js +2 -0
  78. package/dashboard/dist/assets/{AgentsPage-CLSjlyXy.js.map → AgentsPage-D77JeViQ.js.map} +1 -1
  79. package/dashboard/dist/assets/{AutoGrowTextarea-DgEQeRQw.js → AutoGrowTextarea-7X3T9La8.js} +2 -2
  80. package/dashboard/dist/assets/{AutoGrowTextarea-DgEQeRQw.js.map → AutoGrowTextarea-7X3T9La8.js.map} +1 -1
  81. package/dashboard/dist/assets/AvailableEscalationsPage-CqM_MOMy.js +2 -0
  82. package/dashboard/dist/assets/AvailableEscalationsPage-CqM_MOMy.js.map +1 -0
  83. package/dashboard/dist/assets/{BotPicker-CbL7gQFq.js → BotPicker-CC5vI_Sv.js} +2 -2
  84. package/dashboard/dist/assets/{BotPicker-CbL7gQFq.js.map → BotPicker-CC5vI_Sv.js.map} +1 -1
  85. package/dashboard/dist/assets/BulkAssignModal-BXiJnlqt.js +2 -0
  86. package/dashboard/dist/assets/BulkAssignModal-BXiJnlqt.js.map +1 -0
  87. package/dashboard/dist/assets/{CapabilitiesPage-Q9Pe0y_p.js → CapabilitiesPage-D9TC2iKy.js} +2 -2
  88. package/dashboard/dist/assets/{CapabilitiesPage-Q9Pe0y_p.js.map → CapabilitiesPage-D9TC2iKy.js.map} +1 -1
  89. package/dashboard/dist/assets/{CollapsibleSection-BZOtaWPR.js → CollapsibleSection-D10lGw_u.js} +2 -2
  90. package/dashboard/dist/assets/{CollapsibleSection-BZOtaWPR.js.map → CollapsibleSection-D10lGw_u.js.map} +1 -1
  91. package/dashboard/dist/assets/{ConfirmDeleteModal-6CidwpPx.js → ConfirmDeleteModal-BJdLN6No.js} +2 -2
  92. package/dashboard/dist/assets/{ConfirmDeleteModal-6CidwpPx.js.map → ConfirmDeleteModal-BJdLN6No.js.map} +1 -1
  93. package/dashboard/dist/assets/{CopyableId-4Lqlc5cU.js → CopyableId-Bn02zYxt.js} +2 -2
  94. package/dashboard/dist/assets/{CopyableId-4Lqlc5cU.js.map → CopyableId-Bn02zYxt.js.map} +1 -1
  95. package/dashboard/dist/assets/CountdownTimer-D9B-BjQ7.js +2 -0
  96. package/dashboard/dist/assets/CountdownTimer-D9B-BjQ7.js.map +1 -0
  97. package/dashboard/dist/assets/{CredentialsPage-dxjrUEyO.js → CredentialsPage-hniUAFBK.js} +2 -2
  98. package/dashboard/dist/assets/{CredentialsPage-dxjrUEyO.js.map → CredentialsPage-hniUAFBK.js.map} +1 -1
  99. package/dashboard/dist/assets/{CronLabel-D8oo8eaA.js → CronLabel-v9DCD2FX.js} +2 -2
  100. package/dashboard/dist/assets/{CronLabel-D8oo8eaA.js.map → CronLabel-v9DCD2FX.js.map} +1 -1
  101. package/dashboard/dist/assets/{DataTable-DFPMeqyQ.js → DataTable-7C7riavf.js} +2 -2
  102. package/dashboard/dist/assets/{DataTable-DFPMeqyQ.js.map → DataTable-7C7riavf.js.map} +1 -1
  103. package/dashboard/dist/assets/{DropZone-CWCh5ItZ.js → DropZone-XlcY8hEH.js} +2 -2
  104. package/dashboard/dist/assets/{DropZone-CWCh5ItZ.js.map → DropZone-XlcY8hEH.js.map} +1 -1
  105. package/dashboard/dist/assets/{ElapsedCell-FHHQ69G3.js → ElapsedCell-_xvVTK8N.js} +2 -2
  106. package/dashboard/dist/assets/{ElapsedCell-FHHQ69G3.js.map → ElapsedCell-_xvVTK8N.js.map} +1 -1
  107. package/dashboard/dist/assets/{EmptyState-lpUFYfun.js → EmptyState-DgC_6njU.js} +2 -2
  108. package/dashboard/dist/assets/{EmptyState-lpUFYfun.js.map → EmptyState-DgC_6njU.js.map} +1 -1
  109. package/dashboard/dist/assets/EntityTimelinePanel-1lT3jkCM.js +2 -0
  110. package/dashboard/dist/assets/EntityTimelinePanel-1lT3jkCM.js.map +1 -0
  111. package/dashboard/dist/assets/{EscalationListSchemaPage-D5L5lDNb.js → EscalationListSchemaPage-BeQUTUEO.js} +3 -3
  112. package/dashboard/dist/assets/{EscalationListSchemaPage-D5L5lDNb.js.map → EscalationListSchemaPage-BeQUTUEO.js.map} +1 -1
  113. package/dashboard/dist/assets/EscalationListView-uuZRMSLB.js +2 -0
  114. package/dashboard/dist/assets/EscalationListView-uuZRMSLB.js.map +1 -0
  115. package/dashboard/dist/assets/{EscalationSchemaPage-bYbfEpSB.js → EscalationSchemaPage-D4VlEwyS.js} +3 -3
  116. package/dashboard/dist/assets/{EscalationSchemaPage-bYbfEpSB.js.map → EscalationSchemaPage-D4VlEwyS.js.map} +1 -1
  117. package/dashboard/dist/assets/{EscalationsOverview-DjtIjL1b.js → EscalationsOverview-Bx6Zrzm-.js} +2 -2
  118. package/dashboard/dist/assets/{EscalationsOverview-DjtIjL1b.js.map → EscalationsOverview-Bx6Zrzm-.js.map} +1 -1
  119. package/dashboard/dist/assets/{EventTable-CihVTREC.js → EventTable-KEC3YWBf.js} +2 -2
  120. package/dashboard/dist/assets/{EventTable-CihVTREC.js.map → EventTable-KEC3YWBf.js.map} +1 -1
  121. package/dashboard/dist/assets/{FilterBar-Fin1IqHc.js → FilterBar-xwN_b5fj.js} +2 -2
  122. package/dashboard/dist/assets/{FilterBar-Fin1IqHc.js.map → FilterBar-xwN_b5fj.js.map} +1 -1
  123. package/dashboard/dist/assets/{GraphInvokePage-D77ILPd3.js → GraphInvokePage-DV7Y0fe2.js} +2 -2
  124. package/dashboard/dist/assets/{GraphInvokePage-D77ILPd3.js.map → GraphInvokePage-DV7Y0fe2.js.map} +1 -1
  125. package/dashboard/dist/assets/HomePage-G785SWcW.js +2 -0
  126. package/dashboard/dist/assets/HomePage-G785SWcW.js.map +1 -0
  127. package/dashboard/dist/assets/ListToolbar-zPVYPg3W.js +2 -0
  128. package/dashboard/dist/assets/{ListToolbar-DyHaJBTk.js.map → ListToolbar-zPVYPg3W.js.map} +1 -1
  129. package/dashboard/dist/assets/{McpOverview-qz7Ivf0G.js → McpOverview-CMGUq8hx.js} +2 -2
  130. package/dashboard/dist/assets/{McpOverview-qz7Ivf0G.js.map → McpOverview-CMGUq8hx.js.map} +1 -1
  131. package/dashboard/dist/assets/{McpQueryDetailPage-D6MB4CZM.js → McpQueryDetailPage-CdhLhaKn.js} +3 -3
  132. package/dashboard/dist/assets/{McpQueryDetailPage-D6MB4CZM.js.map → McpQueryDetailPage-CdhLhaKn.js.map} +1 -1
  133. package/dashboard/dist/assets/McpQueryPage-CNSNMagY.js +2 -0
  134. package/dashboard/dist/assets/{McpQueryPage-BqqRF5cQ.js.map → McpQueryPage-CNSNMagY.js.map} +1 -1
  135. package/dashboard/dist/assets/{McpRunDetailPage-DxLEhzhn.js → McpRunDetailPage-DBm8hUaW.js} +2 -2
  136. package/dashboard/dist/assets/{McpRunDetailPage-DxLEhzhn.js.map → McpRunDetailPage-DBm8hUaW.js.map} +1 -1
  137. package/dashboard/dist/assets/{McpRunsPage-BOsS4Hdf.js → McpRunsPage-B6rb5QBf.js} +2 -2
  138. package/dashboard/dist/assets/{McpRunsPage-BOsS4Hdf.js.map → McpRunsPage-B6rb5QBf.js.map} +1 -1
  139. package/dashboard/dist/assets/NamespacePill-Bb3revqD.js +2 -0
  140. package/dashboard/dist/assets/{NamespacePill-CJikgGzE.js.map → NamespacePill-Bb3revqD.js.map} +1 -1
  141. package/dashboard/dist/assets/OperationsPage-Dxk1fAMg.js +3 -0
  142. package/dashboard/dist/assets/OperationsPage-Dxk1fAMg.js.map +1 -0
  143. package/dashboard/dist/assets/OperatorDashboard-DBOscDnf.js +2 -0
  144. package/dashboard/dist/assets/{OperatorDashboard-C5rlijvh.js.map → OperatorDashboard-DBOscDnf.js.map} +1 -1
  145. package/dashboard/dist/assets/{PageHeader-DZmymFBS.js → PageHeader-Di5Sz3an.js} +2 -2
  146. package/dashboard/dist/assets/{PageHeader-DZmymFBS.js.map → PageHeader-Di5Sz3an.js.map} +1 -1
  147. package/dashboard/dist/assets/{PageHeaderWithStats-DuAm6YK2.js → PageHeaderWithStats-DutKJ-h2.js} +2 -2
  148. package/dashboard/dist/assets/{PageHeaderWithStats-DuAm6YK2.js.map → PageHeaderWithStats-DutKJ-h2.js.map} +1 -1
  149. package/dashboard/dist/assets/{PersonaDetailPage-JBdVvT5g.js → PersonaDetailPage-BGRdGpeR.js} +2 -2
  150. package/dashboard/dist/assets/{PersonaDetailPage-JBdVvT5g.js.map → PersonaDetailPage-BGRdGpeR.js.map} +1 -1
  151. package/dashboard/dist/assets/PersonasPage-DRzOZcVa.js +2 -0
  152. package/dashboard/dist/assets/PersonasPage-DRzOZcVa.js.map +1 -0
  153. package/dashboard/dist/assets/{PriorityBadge-BETdj4M-.js → PriorityBadge-r33mpDju.js} +2 -2
  154. package/dashboard/dist/assets/{PriorityBadge-BETdj4M-.js.map → PriorityBadge-r33mpDju.js.map} +1 -1
  155. package/dashboard/dist/assets/{ProcessDetailPage-CHnzOzQn.js → ProcessDetailPage-BLGjZXpM.js} +2 -2
  156. package/dashboard/dist/assets/{ProcessDetailPage-CHnzOzQn.js.map → ProcessDetailPage-BLGjZXpM.js.map} +1 -1
  157. package/dashboard/dist/assets/ProcessesListPage-5NnysKFC.js +2 -0
  158. package/dashboard/dist/assets/{ProcessesListPage-DuXTS5-v.js.map → ProcessesListPage-5NnysKFC.js.map} +1 -1
  159. package/dashboard/dist/assets/ResolverForm-Dy0yirtP.js +2 -0
  160. package/dashboard/dist/assets/ResolverForm-Dy0yirtP.js.map +1 -0
  161. package/dashboard/dist/assets/RoleDetailPage-C_LIoe1j.js +8 -0
  162. package/dashboard/dist/assets/RoleDetailPage-C_LIoe1j.js.map +1 -0
  163. package/dashboard/dist/assets/{RolePill-C9LSs1aK.js → RolePill-DhnVPK4q.js} +2 -2
  164. package/dashboard/dist/assets/{RolePill-C9LSs1aK.js.map → RolePill-DhnVPK4q.js.map} +1 -1
  165. package/dashboard/dist/assets/RolesPage-BmW1LzkU.js +2 -0
  166. package/dashboard/dist/assets/{RolesPage-h77-JOLd.js.map → RolesPage-BmW1LzkU.js.map} +1 -1
  167. package/dashboard/dist/assets/{RowActions-eYrzgQSj.js → RowActions-Bdd7z4eB.js} +2 -2
  168. package/dashboard/dist/assets/{RowActions-eYrzgQSj.js.map → RowActions-Bdd7z4eB.js.map} +1 -1
  169. package/dashboard/dist/assets/RunAsSelector-BwZTJ4Yj.js +2 -0
  170. package/dashboard/dist/assets/{RunAsSelector-DoLFJ0mZ.js.map → RunAsSelector-BwZTJ4Yj.js.map} +1 -1
  171. package/dashboard/dist/assets/ScanCodesPage-FiYIYB-m.js +2 -0
  172. package/dashboard/dist/assets/{ScanCodesPage-b5NZM_-f.js.map → ScanCodesPage-FiYIYB-m.js.map} +1 -1
  173. package/dashboard/dist/assets/{ScanSchemeDetailPage-t7X6ekaW.js → ScanSchemeDetailPage-BiAqtD-a.js} +2 -2
  174. package/dashboard/dist/assets/{ScanSchemeDetailPage-t7X6ekaW.js.map → ScanSchemeDetailPage-BiAqtD-a.js.map} +1 -1
  175. package/dashboard/dist/assets/ScanStationPage-8CdOtbCD.js +2 -0
  176. package/dashboard/dist/assets/{ScanStationPage-r02hkTAG.js.map → ScanStationPage-8CdOtbCD.js.map} +1 -1
  177. package/dashboard/dist/assets/{ScopeBadge-CjyC7iyO.js → ScopeBadge-CXUg0FHo.js} +2 -2
  178. package/dashboard/dist/assets/{ScopeBadge-CjyC7iyO.js.map → ScopeBadge-CXUg0FHo.js.map} +1 -1
  179. package/dashboard/dist/assets/{SegmentedTabs-DXNzY05q.js → SegmentedTabs-BpJh4Hwn.js} +2 -2
  180. package/dashboard/dist/assets/{SegmentedTabs-DXNzY05q.js.map → SegmentedTabs-BpJh4Hwn.js.map} +1 -1
  181. package/dashboard/dist/assets/{StatCard-BXQ8dD9Y.js → StatCard-nWY4sndl.js} +2 -2
  182. package/dashboard/dist/assets/{StatCard-BXQ8dD9Y.js.map → StatCard-nWY4sndl.js.map} +1 -1
  183. package/dashboard/dist/assets/{StatusBadge-BDeUwPtb.js → StatusBadge-DHVwxre2.js} +2 -2
  184. package/dashboard/dist/assets/{StatusBadge-BDeUwPtb.js.map → StatusBadge-DHVwxre2.js.map} +1 -1
  185. package/dashboard/dist/assets/{StickyPagination-BGHQbp0_.js → StickyPagination-DRuqKhZt.js} +2 -2
  186. package/dashboard/dist/assets/{StickyPagination-BGHQbp0_.js.map → StickyPagination-DRuqKhZt.js.map} +1 -1
  187. package/dashboard/dist/assets/{StreamMessageDetail-C-wfdbLJ.js → StreamMessageDetail-JwlbRiMn.js} +2 -2
  188. package/dashboard/dist/assets/{StreamMessageDetail-C-wfdbLJ.js.map → StreamMessageDetail-JwlbRiMn.js.map} +1 -1
  189. package/dashboard/dist/assets/{SwimlaneTimeline-Dnoz-me3.js → SwimlaneTimeline-hu3c4bCJ.js} +2 -2
  190. package/dashboard/dist/assets/{SwimlaneTimeline-Dnoz-me3.js.map → SwimlaneTimeline-hu3c4bCJ.js.map} +1 -1
  191. package/dashboard/dist/assets/{TagInput-D-vLSMQC.js → TagInput-BZ8vb2oW.js} +2 -2
  192. package/dashboard/dist/assets/{TagInput-D-vLSMQC.js.map → TagInput-BZ8vb2oW.js.map} +1 -1
  193. package/dashboard/dist/assets/{TaskDetailPage-c9IiAhVJ.js → TaskDetailPage-NqVZF3jW.js} +2 -2
  194. package/dashboard/dist/assets/{TaskDetailPage-c9IiAhVJ.js.map → TaskDetailPage-NqVZF3jW.js.map} +1 -1
  195. package/dashboard/dist/assets/{TaskQueuePill-sFRMrQ5D.js → TaskQueuePill-Bxir8WRl.js} +2 -2
  196. package/dashboard/dist/assets/{TaskQueuePill-sFRMrQ5D.js.map → TaskQueuePill-Bxir8WRl.js.map} +1 -1
  197. package/dashboard/dist/assets/TasksListPage-BQNaSzCn.js +2 -0
  198. package/dashboard/dist/assets/{TasksListPage-Dor8LSc5.js.map → TasksListPage-BQNaSzCn.js.map} +1 -1
  199. package/dashboard/dist/assets/TimeAgo-Cv35DOiI.js +2 -0
  200. package/dashboard/dist/assets/{TimeAgo-Dd8vmRnM.js.map → TimeAgo-Cv35DOiI.js.map} +1 -1
  201. package/dashboard/dist/assets/{TimestampCell-qJfC6-iT.js → TimestampCell-DHUXey9s.js} +2 -2
  202. package/dashboard/dist/assets/{TimestampCell-qJfC6-iT.js.map → TimestampCell-DHUXey9s.js.map} +1 -1
  203. package/dashboard/dist/assets/{ToolPill-OQ3mC11d.js → ToolPill-FkgTXIhq.js} +2 -2
  204. package/dashboard/dist/assets/{ToolPill-OQ3mC11d.js.map → ToolPill-FkgTXIhq.js.map} +1 -1
  205. package/dashboard/dist/assets/ToolTestPanel-DvuLFWqQ.js +2 -0
  206. package/dashboard/dist/assets/{ToolTestPanel-CEGY5jss.js.map → ToolTestPanel-DvuLFWqQ.js.map} +1 -1
  207. package/dashboard/dist/assets/{TopicDetailPage-SI80ZTVv.js → TopicDetailPage-TXX3QNSo.js} +3 -3
  208. package/dashboard/dist/assets/{TopicDetailPage-SI80ZTVv.js.map → TopicDetailPage-TXX3QNSo.js.map} +1 -1
  209. package/dashboard/dist/assets/{TopicsPage-DiVB8tNr.js → TopicsPage-h-oyTeEJ.js} +2 -2
  210. package/dashboard/dist/assets/{TopicsPage-DiVB8tNr.js.map → TopicsPage-h-oyTeEJ.js.map} +1 -1
  211. package/dashboard/dist/assets/UserName-BceRCeDF.js +2 -0
  212. package/dashboard/dist/assets/UserName-BceRCeDF.js.map +1 -0
  213. package/dashboard/dist/assets/WorkflowExecutionPage-CmHGcaN9.js +2 -0
  214. package/dashboard/dist/assets/{WorkflowExecutionPage-e-Q57yiJ.js.map → WorkflowExecutionPage-CmHGcaN9.js.map} +1 -1
  215. package/dashboard/dist/assets/{WorkflowPill-Dh3hIW1l.js → WorkflowPill--AYMW7u_.js} +2 -2
  216. package/dashboard/dist/assets/WorkflowPill--AYMW7u_.js.map +1 -0
  217. package/dashboard/dist/assets/WorkflowsDashboard-B0r2lQXk.js +2 -0
  218. package/dashboard/dist/assets/{WorkflowsDashboard-DLKzN80h.js.map → WorkflowsDashboard-B0r2lQXk.js.map} +1 -1
  219. package/dashboard/dist/assets/WorkflowsOverview-BTMc2PJ_.js +2 -0
  220. package/dashboard/dist/assets/{WorkflowsOverview-J-ITQiO5.js.map → WorkflowsOverview-BTMc2PJ_.js.map} +1 -1
  221. package/dashboard/dist/assets/{YamlWorkflowDetailPage-B_-ahhf1.js → YamlWorkflowDetailPage-DUiCVnZp.js} +2 -2
  222. package/dashboard/dist/assets/{YamlWorkflowDetailPage-B_-ahhf1.js.map → YamlWorkflowDetailPage-DUiCVnZp.js.map} +1 -1
  223. package/dashboard/dist/assets/YamlWorkflowsPage-BAuXfwi0.js +2 -0
  224. package/dashboard/dist/assets/{YamlWorkflowsPage-DQNeERXU.js.map → YamlWorkflowsPage-BAuXfwi0.js.map} +1 -1
  225. package/dashboard/dist/assets/{agents-BsJ-MizQ.js → agents-BvmLCk4h.js} +2 -2
  226. package/dashboard/dist/assets/{agents-BsJ-MizQ.js.map → agents-BvmLCk4h.js.map} +1 -1
  227. package/dashboard/dist/assets/{bots-q1UYqaIh.js → bots-f_tyhl4I.js} +2 -2
  228. package/dashboard/dist/assets/{bots-q1UYqaIh.js.map → bots-f_tyhl4I.js.map} +1 -1
  229. package/dashboard/dist/assets/capabilities-DNo5bqLx.js +2 -0
  230. package/dashboard/dist/assets/{capabilities-BvjPXUcJ.js.map → capabilities-DNo5bqLx.js.map} +1 -1
  231. package/dashboard/dist/assets/{controlplane-BwlNpoJ_.js → controlplane-Dyyj2Irb.js} +2 -2
  232. package/dashboard/dist/assets/{controlplane-BwlNpoJ_.js.map → controlplane-Dyyj2Irb.js.map} +1 -1
  233. package/dashboard/dist/assets/escalation-columns-DIigeTOK.js +2 -0
  234. package/dashboard/dist/assets/escalation-columns-DIigeTOK.js.map +1 -0
  235. package/dashboard/dist/assets/index-BMlQYOE1.js +67 -0
  236. package/dashboard/dist/assets/index-BMlQYOE1.js.map +1 -0
  237. package/dashboard/dist/assets/index-BNZknxFY.js +2 -0
  238. package/dashboard/dist/assets/{index-cvmt1plS.js.map → index-BNZknxFY.js.map} +1 -1
  239. package/dashboard/dist/assets/index-Bpvfr4OW.css +1 -0
  240. package/dashboard/dist/assets/{index-CldVY0UQ.js → index-C-Xr2V3F.js} +2 -2
  241. package/dashboard/dist/assets/{index-CldVY0UQ.js.map → index-C-Xr2V3F.js.map} +1 -1
  242. package/dashboard/dist/assets/index-CegPUKpc.js +3 -0
  243. package/dashboard/dist/assets/index-CegPUKpc.js.map +1 -0
  244. package/dashboard/dist/assets/{index-O9uuK2yO.js → index-CsKiRyhy.js} +2 -2
  245. package/dashboard/dist/assets/{index-O9uuK2yO.js.map → index-CsKiRyhy.js.map} +1 -1
  246. package/dashboard/dist/assets/index-D7ESqn22.js +2 -0
  247. package/dashboard/dist/assets/{index-BhzsR_gZ.js.map → index-D7ESqn22.js.map} +1 -1
  248. package/dashboard/dist/assets/{index-Hbbox1A4.js → index-DL83W7Gu.js} +2 -2
  249. package/dashboard/dist/assets/{index-Hbbox1A4.js.map → index-DL83W7Gu.js.map} +1 -1
  250. package/dashboard/dist/assets/index-DPLssnMW.js +6 -0
  251. package/dashboard/dist/assets/index-DPLssnMW.js.map +1 -0
  252. package/dashboard/dist/assets/{index-DsKqDMs4.js → index-DWse7ERF.js} +2 -2
  253. package/dashboard/dist/assets/{index-DsKqDMs4.js.map → index-DWse7ERF.js.map} +1 -1
  254. package/dashboard/dist/assets/index-DoETPlJP.js +11 -0
  255. package/dashboard/dist/assets/index-DoETPlJP.js.map +1 -0
  256. package/dashboard/dist/assets/{index-8Gmtvd8e.js → index-DvxxrFBo.js} +2 -2
  257. package/dashboard/dist/assets/{index-8Gmtvd8e.js.map → index-DvxxrFBo.js.map} +1 -1
  258. package/dashboard/dist/assets/{index-DDHoDZRg.js → index-PcyGjmBm.js} +2 -2
  259. package/dashboard/dist/assets/{index-DDHoDZRg.js.map → index-PcyGjmBm.js.map} +1 -1
  260. package/dashboard/dist/assets/{index-BvGIyZUt.js → index-e2X-s0EQ.js} +2 -2
  261. package/dashboard/dist/assets/{index-BvGIyZUt.js.map → index-e2X-s0EQ.js.map} +1 -1
  262. package/dashboard/dist/assets/{knowledge-DM0tNphj.js → knowledge-Coo6CKzo.js} +2 -2
  263. package/dashboard/dist/assets/{knowledge-DM0tNphj.js.map → knowledge-Coo6CKzo.js.map} +1 -1
  264. package/dashboard/dist/assets/{mcp-CQ_TD2P9.js → mcp-Cz5eFnP_.js} +2 -2
  265. package/dashboard/dist/assets/{mcp-CQ_TD2P9.js.map → mcp-Cz5eFnP_.js.map} +1 -1
  266. package/dashboard/dist/assets/{mcp-query-BP7KSGhc.js → mcp-query-BtnuO4LF.js} +2 -2
  267. package/dashboard/dist/assets/{mcp-query-BP7KSGhc.js.map → mcp-query-BtnuO4LF.js.map} +1 -1
  268. package/dashboard/dist/assets/{personas-DclHdHKK.js → personas-C2-LgpHQ.js} +2 -2
  269. package/dashboard/dist/assets/{personas-DclHdHKK.js.map → personas-C2-LgpHQ.js.map} +1 -1
  270. package/dashboard/dist/assets/{pipelines-LwuKJfj-.js → pipelines-TuXQvUA-.js} +2 -2
  271. package/dashboard/dist/assets/{pipelines-LwuKJfj-.js.map → pipelines-TuXQvUA-.js.map} +1 -1
  272. package/dashboard/dist/assets/resolver-payload-C1dXB3n6.js +2 -0
  273. package/dashboard/dist/assets/resolver-payload-C1dXB3n6.js.map +1 -0
  274. package/dashboard/dist/assets/seed-form-json-DvPsISxD.js +2 -0
  275. package/dashboard/dist/assets/seed-form-json-DvPsISxD.js.map +1 -0
  276. package/dashboard/dist/assets/{tasks-CDs-Li2v.js → tasks-OUzpu__z.js} +2 -2
  277. package/dashboard/dist/assets/{tasks-CDs-Li2v.js.map → tasks-OUzpu__z.js.map} +1 -1
  278. package/dashboard/dist/assets/{topics-BeOSelBX.js → topics-B8bdrBaW.js} +2 -2
  279. package/dashboard/dist/assets/{topics-BeOSelBX.js.map → topics-B8bdrBaW.js.map} +1 -1
  280. package/dashboard/dist/assets/useDebouncedValue-Cp5ZOtVE.js +2 -0
  281. package/dashboard/dist/assets/{useDebouncedValue-DhhAKjDC.js.map → useDebouncedValue-Cp5ZOtVE.js.map} +1 -1
  282. package/dashboard/dist/assets/{useFilterParams-crmlBw_l.js → useFilterParams-DBRw1Hcv.js} +2 -2
  283. package/dashboard/dist/assets/{useFilterParams-crmlBw_l.js.map → useFilterParams-DBRw1Hcv.js.map} +1 -1
  284. package/dashboard/dist/assets/{useNamespace-C-E2gNEK.js → useNamespace-DtQe3wZ5.js} +2 -2
  285. package/dashboard/dist/assets/{useNamespace-C-E2gNEK.js.map → useNamespace-DtQe3wZ5.js.map} +1 -1
  286. package/dashboard/dist/assets/{useYamlActivityEvents-CmLzX3YU.js → useYamlActivityEvents-CS22c15X.js} +2 -2
  287. package/dashboard/dist/assets/{useYamlActivityEvents-CmLzX3YU.js.map → useYamlActivityEvents-CS22c15X.js.map} +1 -1
  288. package/dashboard/dist/assets/{vendor-icons-R9b9gQ04.js → vendor-icons-DeBdvyKJ.js} +368 -143
  289. package/dashboard/dist/assets/vendor-icons-DeBdvyKJ.js.map +1 -0
  290. package/dashboard/dist/assets/vendor-query-DvWWuNIB.js +18 -0
  291. package/dashboard/dist/assets/vendor-query-DvWWuNIB.js.map +1 -0
  292. package/dashboard/dist/assets/{vendor-react-CdqEC2zv.js → vendor-react-_9U4mKFr.js} +2 -2
  293. package/dashboard/dist/assets/{vendor-react-CdqEC2zv.js.map → vendor-react-_9U4mKFr.js.map} +1 -1
  294. package/dashboard/dist/assets/{yaml-workflows-BgAsc6-I.js → yaml-workflows-HQIki3hI.js} +2 -2
  295. package/dashboard/dist/assets/{yaml-workflows-BgAsc6-I.js.map → yaml-workflows-HQIki3hI.js.map} +1 -1
  296. package/dashboard/dist/index.html +5 -5
  297. package/docs/api/http/workflows.md +25 -1
  298. package/docs/code-owned-configuration.md +1 -1
  299. package/docs/dashboard.md +179 -80
  300. package/docs/events.md +3 -1
  301. package/docs/faceted-routing.md +5 -1
  302. package/docs/hitl/iframe.md +35 -6
  303. package/docs/hitl/invoke-form.md +98 -0
  304. package/docs/hitl/x-lt-list-schema.md +3 -0
  305. package/docs/hitl/x-lt-show-if.md +11 -0
  306. package/docs/hitl-guide.md +4 -2
  307. package/docs/iam.md +1 -1
  308. package/docs/operations.md +61 -60
  309. package/docs/scan-codes.md +30 -3
  310. package/docs/workflows.md +1 -1
  311. package/package.json +1 -1
  312. package/dashboard/dist/assets/AgentsPage-CLSjlyXy.js +0 -2
  313. package/dashboard/dist/assets/AvailableEscalationsPage-D2ukvuQh.js +0 -2
  314. package/dashboard/dist/assets/AvailableEscalationsPage-D2ukvuQh.js.map +0 -1
  315. package/dashboard/dist/assets/BulkAssignModal-CMSgvHIs.js +0 -2
  316. package/dashboard/dist/assets/BulkAssignModal-CMSgvHIs.js.map +0 -1
  317. package/dashboard/dist/assets/CountdownTimer-B5lM7PfE.js +0 -2
  318. package/dashboard/dist/assets/CountdownTimer-B5lM7PfE.js.map +0 -1
  319. package/dashboard/dist/assets/EntityTimelinePanel-B5NuHnPF.js +0 -2
  320. package/dashboard/dist/assets/EntityTimelinePanel-B5NuHnPF.js.map +0 -1
  321. package/dashboard/dist/assets/HomePage-8XsQFqKL.js +0 -2
  322. package/dashboard/dist/assets/HomePage-8XsQFqKL.js.map +0 -1
  323. package/dashboard/dist/assets/ListToolbar-DyHaJBTk.js +0 -2
  324. package/dashboard/dist/assets/McpQueryPage-BqqRF5cQ.js +0 -2
  325. package/dashboard/dist/assets/NamespacePill-CJikgGzE.js +0 -2
  326. package/dashboard/dist/assets/OperationsPage-Cj1mJLMC.js +0 -3
  327. package/dashboard/dist/assets/OperationsPage-Cj1mJLMC.js.map +0 -1
  328. package/dashboard/dist/assets/OperatorDashboard-C5rlijvh.js +0 -2
  329. package/dashboard/dist/assets/PersonasPage-Xp_Gt6El.js +0 -2
  330. package/dashboard/dist/assets/PersonasPage-Xp_Gt6El.js.map +0 -1
  331. package/dashboard/dist/assets/ProcessesListPage-DuXTS5-v.js +0 -2
  332. package/dashboard/dist/assets/RoleDetailPage-C9GGHNf4.js +0 -8
  333. package/dashboard/dist/assets/RoleDetailPage-C9GGHNf4.js.map +0 -1
  334. package/dashboard/dist/assets/RolesPage-h77-JOLd.js +0 -2
  335. package/dashboard/dist/assets/RunAsSelector-DoLFJ0mZ.js +0 -2
  336. package/dashboard/dist/assets/ScanCodesPage-b5NZM_-f.js +0 -2
  337. package/dashboard/dist/assets/ScanStationPage-r02hkTAG.js +0 -2
  338. package/dashboard/dist/assets/TasksListPage-Dor8LSc5.js +0 -2
  339. package/dashboard/dist/assets/TimeAgo-Dd8vmRnM.js +0 -2
  340. package/dashboard/dist/assets/ToolTestPanel-CEGY5jss.js +0 -2
  341. package/dashboard/dist/assets/UserName-Dmm456xT.js +0 -2
  342. package/dashboard/dist/assets/UserName-Dmm456xT.js.map +0 -1
  343. package/dashboard/dist/assets/WorkflowExecutionPage-e-Q57yiJ.js +0 -2
  344. package/dashboard/dist/assets/WorkflowPill-Dh3hIW1l.js.map +0 -1
  345. package/dashboard/dist/assets/WorkflowsDashboard-DLKzN80h.js +0 -2
  346. package/dashboard/dist/assets/WorkflowsOverview-J-ITQiO5.js +0 -2
  347. package/dashboard/dist/assets/YamlWorkflowsPage-DQNeERXU.js +0 -2
  348. package/dashboard/dist/assets/capabilities-BvjPXUcJ.js +0 -2
  349. package/dashboard/dist/assets/escalation-columns-Bo3PfIZj.js +0 -2
  350. package/dashboard/dist/assets/escalation-columns-Bo3PfIZj.js.map +0 -1
  351. package/dashboard/dist/assets/index-B8JfGtQB.js +0 -3
  352. package/dashboard/dist/assets/index-B8JfGtQB.js.map +0 -1
  353. package/dashboard/dist/assets/index-Bb9ZZUyB.js +0 -5
  354. package/dashboard/dist/assets/index-Bb9ZZUyB.js.map +0 -1
  355. package/dashboard/dist/assets/index-BhzsR_gZ.js +0 -2
  356. package/dashboard/dist/assets/index-D88VWEVp.js +0 -67
  357. package/dashboard/dist/assets/index-D88VWEVp.js.map +0 -1
  358. package/dashboard/dist/assets/index-DBvkaa-B.js +0 -6
  359. package/dashboard/dist/assets/index-DBvkaa-B.js.map +0 -1
  360. package/dashboard/dist/assets/index-cvmt1plS.js +0 -2
  361. package/dashboard/dist/assets/index-qU_dXqP4.css +0 -1
  362. package/dashboard/dist/assets/useDebouncedValue-DhhAKjDC.js +0 -2
  363. package/dashboard/dist/assets/vendor-icons-R9b9gQ04.js.map +0 -1
  364. package/dashboard/dist/assets/vendor-query-RUZdMvN6.js +0 -18
  365. package/dashboard/dist/assets/vendor-query-RUZdMvN6.js.map +0 -1
  366. package/dashboard/dist/assets/workflows-CYv1CTU0.js +0 -2
  367. package/dashboard/dist/assets/workflows-CYv1CTU0.js.map +0 -1
@@ -178,7 +178,7 @@ async function ensureSideQuestRoles() {
178
178
  console.log(`[${ts()}] Entity declared on ${declared} role(s): order_id`);
179
179
  }
180
180
  // ── Side-quest escalations (standalone rows — no workflow behind them) ────────
181
- async function createSideEscalation(role, workflowId, orderId, runId, description, authorizedAt) {
181
+ async function createSideEscalation(role, workflowId, orderId, runId, description, authorizedAt, facility) {
182
182
  try {
183
183
  await api('POST', '/api/escalations', {
184
184
  type: 'ortho-stage',
@@ -186,8 +186,8 @@ async function createSideEscalation(role, workflowId, orderId, runId, descriptio
186
186
  role,
187
187
  description,
188
188
  workflow_id: workflowId,
189
- envelope: JSON.stringify({ order_id: orderId }),
190
- metadata: { order_id: orderId, source: 'populate', run_id: runId, authorized_at: authorizedAt },
189
+ envelope: JSON.stringify({ order_id: orderId, ...(facility ? { facility } : {}) }),
190
+ metadata: { order_id: orderId, source: 'populate', run_id: runId, authorized_at: authorizedAt, ...(facility ? { facility } : {}) },
191
191
  });
192
192
  return true;
193
193
  }
@@ -261,7 +261,7 @@ async function resolveBatch(batchTag, runId) {
261
261
  // order's authorized_at rides along from the ordering escalation.
262
262
  if (esc.role === 'ordering') {
263
263
  const orderId = esc.metadata?.order_id ?? '';
264
- await createSideEscalation('inserting', esc.workflow_id, orderId, runId, `Stuff inserts into shoes for ${orderId}`, esc.metadata?.authorized_at ?? new Date().toISOString());
264
+ await createSideEscalation('inserting', esc.workflow_id, orderId, runId, `Stuff inserts into shoes for ${orderId}`, esc.metadata?.authorized_at ?? new Date().toISOString(), esc.metadata?.facility);
265
265
  }
266
266
  }
267
267
  catch (err) {
@@ -275,6 +275,10 @@ async function resolveBatch(batchTag, runId) {
275
275
  return 0;
276
276
  }
277
277
  }
278
+ // Two manufacturing sites — each order is assigned one at arrival and every
279
+ // escalation it spawns carries it, so the Pace Board's facet scope
280
+ // (facility = north|south) has real data to narrow.
281
+ const FACILITIES = ['north', 'south'];
278
282
  async function runOrders(batchTag, runId, plan, target) {
279
283
  let done = false;
280
284
  const workflowIds = [];
@@ -295,6 +299,9 @@ async function runOrders(batchTag, runId, plan, target) {
295
299
  data: {
296
300
  order_id: order.orderId,
297
301
  item_type: 'insole-standard',
302
+ // Stamped on the envelope; the workflow applies it to every stage
303
+ // escalation's metadata as the facility facet.
304
+ facility: order.facility,
298
305
  metadata: { source: 'populate', run_id: runId, authorized_at: authorizedAt },
299
306
  },
300
307
  metadata: { source: 'populate', run_id: runId, shoes: order.shoes },
@@ -308,7 +315,7 @@ async function runOrders(batchTag, runId, plan, target) {
308
315
  continue;
309
316
  }
310
317
  if (order.shoes) {
311
- const ok = await createSideEscalation('ordering', `${order.wfId}-shoes`, order.orderId, runId, `Order shoes for ${order.orderId}`, authorizedAt);
318
+ const ok = await createSideEscalation('ordering', `${order.wfId}-shoes`, order.orderId, runId, `Order shoes for ${order.orderId}`, authorizedAt, order.facility);
312
319
  if (!ok)
313
320
  target.value -= 2;
314
321
  }
@@ -372,6 +379,9 @@ async function main() {
372
379
  wfId: `${batchTag}-${i}`,
373
380
  orderId: `ORD-${runId}-${i}`,
374
381
  shoes: hasShoes(i),
382
+ // Pseudo-random per order (hashed on the workflow id): reproducible across
383
+ // re-runs, roughly balanced across both sites.
384
+ facility: FACILITIES[Math.floor(jitter(`${batchTag}-${i}`, 'facility') * FACILITIES.length)],
375
385
  }));
376
386
  const shoeCount = plan.filter((o) => o.shoes).length;
377
387
  // Each order resolves 8 manufacturing stages; a shoe order adds
@@ -123,6 +123,7 @@ export declare function createClient(options?: LTClientOptions): {
123
123
  }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
124
124
  getStationMetrics: (input: {
125
125
  period?: string;
126
+ facets?: Record<string, unknown>;
126
127
  }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
127
128
  get: (input: {
128
129
  id: string;
@@ -16,6 +16,8 @@ async function loadAllConfigs() {
16
16
  consumes: c.consumes,
17
17
  toolTags: c.tool_tags || [],
18
18
  envelopeSchema: c.envelope_schema ?? null,
19
+ inputSchema: c.input_schema ?? null,
20
+ icon: c.icon ?? null,
19
21
  resolverSchema: c.resolver_schema ?? null,
20
22
  cronSchedule: c.cron_schedule ?? null,
21
23
  executeAs: c.execute_as ?? null,
@@ -26,6 +26,8 @@ async function getWorkflowConfig(workflowType) {
26
26
  consumes: wf.consumes || [],
27
27
  tool_tags: wf.tool_tags || [],
28
28
  envelope_schema: wf.envelope_schema ?? null,
29
+ input_schema: wf.input_schema ?? null,
30
+ icon: wf.icon ?? null,
29
31
  resolver_schema: wf.resolver_schema ?? null,
30
32
  cron_schedule: wf.cron_schedule ?? null,
31
33
  execute_as: wf.execute_as ?? null,
@@ -64,6 +66,8 @@ async function listWorkflowConfigs() {
64
66
  consumes: wf.consumes || [],
65
67
  tool_tags: wf.tool_tags || [],
66
68
  envelope_schema: wf.envelope_schema ?? null,
69
+ input_schema: wf.input_schema ?? null,
70
+ icon: wf.icon ?? null,
67
71
  resolver_schema: wf.resolver_schema ?? null,
68
72
  cron_schedule: wf.cron_schedule ?? null,
69
73
  execute_as: wf.execute_as ?? null,
@@ -5,13 +5,13 @@ export declare const LIST_ALL_WORKFLOWS = "SELECT * FROM lt_config_workflows ORD
5
5
  export declare const LIST_ALL_ROLES = "SELECT * FROM lt_config_roles ORDER BY workflow_type, role";
6
6
  export declare const LIST_ALL_INVOCATION_ROLES = "SELECT * FROM lt_config_invocation_roles ORDER BY workflow_type, role";
7
7
  export declare const ENSURE_ROLE_EXISTS = "INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING";
8
- export declare const UPSERT_WORKFLOW = "INSERT INTO lt_config_workflows\n (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe)\nVALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)\nON CONFLICT (workflow_type) DO UPDATE SET\n invocable = EXCLUDED.invocable,\n task_queue = EXCLUDED.task_queue,\n default_role = EXCLUDED.default_role,\n description = EXCLUDED.description,\n consumes = EXCLUDED.consumes,\n envelope_schema = EXCLUDED.envelope_schema,\n resolver_schema = EXCLUDED.resolver_schema,\n cron_schedule = EXCLUDED.cron_schedule,\n tool_tags = EXCLUDED.tool_tags,\n execute_as = EXCLUDED.execute_as,\n certified = EXCLUDED.certified,\n read_safe = EXCLUDED.read_safe";
8
+ export declare const UPSERT_WORKFLOW = "INSERT INTO lt_config_workflows\n (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon)\nVALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)\nON CONFLICT (workflow_type) DO UPDATE SET\n invocable = EXCLUDED.invocable,\n task_queue = EXCLUDED.task_queue,\n default_role = EXCLUDED.default_role,\n description = EXCLUDED.description,\n consumes = EXCLUDED.consumes,\n envelope_schema = EXCLUDED.envelope_schema,\n resolver_schema = EXCLUDED.resolver_schema,\n cron_schedule = EXCLUDED.cron_schedule,\n tool_tags = EXCLUDED.tool_tags,\n execute_as = EXCLUDED.execute_as,\n certified = EXCLUDED.certified,\n read_safe = EXCLUDED.read_safe,\n input_schema = EXCLUDED.input_schema,\n icon = EXCLUDED.icon";
9
9
  export declare const DELETE_CONFIG_ROLES = "DELETE FROM lt_config_roles WHERE workflow_type = $1";
10
10
  export declare const INSERT_CONFIG_ROLE = "INSERT INTO lt_config_roles (workflow_type, role) VALUES ($1, $2)";
11
11
  export declare const DELETE_INVOCATION_ROLES = "DELETE FROM lt_config_invocation_roles WHERE workflow_type = $1";
12
12
  export declare const INSERT_INVOCATION_ROLE = "INSERT INTO lt_config_invocation_roles (workflow_type, role) VALUES ($1, $2)";
13
13
  export declare const DELETE_WORKFLOW = "DELETE FROM lt_config_workflows WHERE workflow_type = $1";
14
- export declare const SEED_WORKFLOW_CONFIG = "INSERT INTO lt_config_workflows\n (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe)\nVALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)\nON CONFLICT (workflow_type) DO NOTHING";
14
+ export declare const SEED_WORKFLOW_CONFIG = "INSERT INTO lt_config_workflows\n (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon)\nVALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)\nON CONFLICT (workflow_type) DO NOTHING";
15
15
  export declare const SEED_CONFIG_ROLE = "INSERT INTO lt_config_roles (workflow_type, role) VALUES ($1, $2)\nON CONFLICT (workflow_type, role) DO NOTHING";
16
16
  export declare const SEED_INVOCATION_ROLE = "INSERT INTO lt_config_invocation_roles (workflow_type, role) VALUES ($1, $2)\nON CONFLICT (workflow_type, role) DO NOTHING";
17
17
  export declare const GET_PROVIDER_DATA = "SELECT workflow_type, data, completed_at\n FROM lt_tasks\n WHERE origin_id = $1\n AND workflow_type = ANY($2)\n AND status = 'completed'\n ORDER BY completed_at DESC";
@@ -23,8 +23,8 @@ exports.ENSURE_ROLE_EXISTS = `\
23
23
  INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING`;
24
24
  exports.UPSERT_WORKFLOW = `\
25
25
  INSERT INTO lt_config_workflows
26
- (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe)
27
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
26
+ (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon)
27
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
28
28
  ON CONFLICT (workflow_type) DO UPDATE SET
29
29
  invocable = EXCLUDED.invocable,
30
30
  task_queue = EXCLUDED.task_queue,
@@ -37,7 +37,9 @@ ON CONFLICT (workflow_type) DO UPDATE SET
37
37
  tool_tags = EXCLUDED.tool_tags,
38
38
  execute_as = EXCLUDED.execute_as,
39
39
  certified = EXCLUDED.certified,
40
- read_safe = EXCLUDED.read_safe`;
40
+ read_safe = EXCLUDED.read_safe,
41
+ input_schema = EXCLUDED.input_schema,
42
+ icon = EXCLUDED.icon`;
41
43
  exports.DELETE_CONFIG_ROLES = `\
42
44
  DELETE FROM lt_config_roles WHERE workflow_type = $1`;
43
45
  exports.INSERT_CONFIG_ROLE = `\
@@ -53,8 +55,8 @@ DELETE FROM lt_config_workflows WHERE workflow_type = $1`;
53
55
  // ------------------------------------------------------------------ //
54
56
  exports.SEED_WORKFLOW_CONFIG = `\
55
57
  INSERT INTO lt_config_workflows
56
- (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe)
57
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
58
+ (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon)
59
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
58
60
  ON CONFLICT (workflow_type) DO NOTHING`;
59
61
  exports.SEED_CONFIG_ROLE = `\
60
62
  INSERT INTO lt_config_roles (workflow_type, role) VALUES ($1, $2)
@@ -53,6 +53,8 @@ async function replaceWorkflowConfig(config, certified) {
53
53
  config.execute_as ?? null,
54
54
  certified,
55
55
  config.read_safe ?? false,
56
+ config.input_schema ?? null,
57
+ config.icon ?? null,
56
58
  ]);
57
59
  // Replace roles
58
60
  await client.query(sql_1.DELETE_CONFIG_ROLES, [config.workflow_type]);
@@ -114,6 +116,8 @@ async function seedWorkflowConfig(config) {
114
116
  config.execute_as ?? null,
115
117
  resolveCertified(config),
116
118
  config.read_safe ?? false,
119
+ config.input_schema ?? null,
120
+ config.icon ?? null,
117
121
  ]);
118
122
  const inserted = (rowCount ?? 0) > 0;
119
123
  if (inserted) {
@@ -144,6 +148,10 @@ async function seedWorkflowConfig(config) {
144
148
  drifts.push('envelope_schema');
145
149
  if (JSON.stringify(config.resolver_schema) !== JSON.stringify(existing.resolver_schema))
146
150
  drifts.push('resolver_schema');
151
+ if (JSON.stringify(config.input_schema ?? null) !== JSON.stringify(existing.input_schema ?? null))
152
+ drifts.push('input_schema');
153
+ if ((config.icon ?? null) !== (existing.icon ?? null))
154
+ drifts.push('icon');
147
155
  if (drifts.length) {
148
156
  logger_1.loggerRegistry.warn(`[long-tail] config drift: ${config.workflow_type} — ${drifts.join(', ')} differ between code and DB`);
149
157
  }
@@ -181,7 +189,9 @@ async function applyWorkflowConfig(config) {
181
189
  sortedEqual(existing.roles ?? [], config.roles ?? []) &&
182
190
  sortedEqual(existing.invocation_roles ?? [], config.invocation_roles ?? []) &&
183
191
  (0, node_util_1.isDeepStrictEqual)(existing.envelope_schema ?? null, config.envelope_schema ?? null) &&
184
- (0, node_util_1.isDeepStrictEqual)(existing.resolver_schema ?? null, config.resolver_schema ?? null);
192
+ (0, node_util_1.isDeepStrictEqual)(existing.resolver_schema ?? null, config.resolver_schema ?? null) &&
193
+ (0, node_util_1.isDeepStrictEqual)(existing.input_schema ?? null, config.input_schema ?? null) &&
194
+ (existing.icon ?? null) === (config.icon ?? null);
185
195
  if (unchanged)
186
196
  return 'unchanged';
187
197
  }
@@ -4,6 +4,7 @@ exports.resolveBatchItem = resolveBatchItem;
4
4
  exports.resolveBatchItemBySignalKey = resolveBatchItemBySignalKey;
5
5
  exports.resolveBatchItemByMetadata = resolveBatchItemByMetadata;
6
6
  const escalation_wire_1 = require("../../lib/events/escalation-wire");
7
+ const uuid_1 = require("../../lib/uuid");
7
8
  const client_1 = require("./client");
8
9
  const crud_1 = require("./crud");
9
10
  const map_1 = require("./map");
@@ -15,6 +16,8 @@ const map_1 = require("./map");
15
16
  * outcomes publish nothing — the row was untouched.
16
17
  */
17
18
  async function resolveBatchItem(id, itemKey, payload, metadata, assertClaim, resolvedBy) {
19
+ if (!(0, uuid_1.isUuid)(id))
20
+ return { outcome: 'not-found', remaining: -1, escalation: null };
18
21
  const client = await (0, client_1.escalations)();
19
22
  const result = await client.resolveBatchItem({
20
23
  id,
@@ -8,6 +8,7 @@ exports.bulkUnassignEscalations = bulkUnassignEscalations;
8
8
  exports.bulkEscalateToRole = bulkEscalateToRole;
9
9
  exports.bulkCancelEscalations = bulkCancelEscalations;
10
10
  exports.bulkResolveForTriage = bulkResolveForTriage;
11
+ const uuid_1 = require("../../lib/uuid");
11
12
  const db_1 = require("../../lib/db");
12
13
  const client_1 = require("./client");
13
14
  const map_1 = require("./map");
@@ -17,6 +18,7 @@ const sql_1 = require("./sql");
17
18
  * Items already claimed by another active user are skipped.
18
19
  */
19
20
  async function bulkClaimEscalations(ids, userId, durationMinutes = 30) {
21
+ ids = (0, uuid_1.onlyUuids)(ids);
20
22
  if (ids.length === 0)
21
23
  return { claimed: 0, skipped: 0 };
22
24
  const client = await (0, client_1.escalations)();
@@ -27,6 +29,7 @@ async function bulkClaimEscalations(ids, userId, durationMinutes = 30) {
27
29
  * Items already claimed by another active user are skipped.
28
30
  */
29
31
  async function bulkAssignEscalations(ids, targetUserId, durationMinutes = 30) {
32
+ ids = (0, uuid_1.onlyUuids)(ids);
30
33
  if (ids.length === 0)
31
34
  return { assigned: 0, skipped: 0 };
32
35
  const client = await (0, client_1.escalations)();
@@ -60,6 +63,7 @@ async function bulkAssignEscalationsByQuery(query, targetUserId, durationMinutes
60
63
  * returned row carries its pre-update assignee for the event.
61
64
  */
62
65
  async function bulkReassignEscalations(ids, targetUserId, durationMinutes = 30) {
66
+ ids = (0, uuid_1.onlyUuids)(ids);
63
67
  if (ids.length === 0)
64
68
  return { assigned: 0, skipped: 0, changes: [] };
65
69
  await (0, client_1.ensureEscalationCompatView)();
@@ -77,6 +81,7 @@ async function bulkReassignEscalations(ids, targetUserId, durationMinutes = 30)
77
81
  * statement; unclaimed and terminal rows are skipped.
78
82
  */
79
83
  async function bulkUnassignEscalations(ids) {
84
+ ids = (0, uuid_1.onlyUuids)(ids);
80
85
  if (ids.length === 0)
81
86
  return { unassigned: 0, skipped: 0, changes: [] };
82
87
  await (0, client_1.ensureEscalationCompatView)();
@@ -93,6 +98,7 @@ async function bulkUnassignEscalations(ids) {
93
98
  * Clears assignment on all affected rows.
94
99
  */
95
100
  async function bulkEscalateToRole(ids, targetRole) {
101
+ ids = (0, uuid_1.onlyUuids)(ids);
96
102
  if (ids.length === 0)
97
103
  return 0;
98
104
  const client = await (0, client_1.escalations)();
@@ -104,6 +110,7 @@ async function bulkEscalateToRole(ids, targetRole) {
104
110
  * successfully cancelled.
105
111
  */
106
112
  async function bulkCancelEscalations(ids) {
113
+ ids = (0, uuid_1.onlyUuids)(ids);
107
114
  if (ids.length === 0)
108
115
  return { cancelled: 0, skipped: 0 };
109
116
  const client = await (0, client_1.escalations)();
@@ -126,6 +133,7 @@ async function bulkCancelEscalations(ids) {
126
133
  * `pending`; only the returned rows enter triage.
127
134
  */
128
135
  async function bulkResolveForTriage(ids, hint) {
136
+ ids = (0, uuid_1.onlyUuids)(ids);
129
137
  if (ids.length === 0)
130
138
  return [];
131
139
  const client = await (0, client_1.escalations)();
@@ -29,6 +29,7 @@ const db_1 = require("../../lib/db");
29
29
  const publish_1 = require("../../lib/events/publish");
30
30
  const escalation_wire_1 = require("../../lib/events/escalation-wire");
31
31
  const escalation_1 = require("../../types/escalation");
32
+ const uuid_1 = require("../../lib/uuid");
32
33
  const client_1 = require("./client");
33
34
  const queries_1 = require("./queries");
34
35
  const map_1 = require("./map");
@@ -95,6 +96,8 @@ async function createEscalation(input) {
95
96
  * user re-claims (extends expiry). Returns null when the row is not claimable.
96
97
  */
97
98
  async function claimEscalation(id, userId, durationMinutes = 30) {
99
+ if (!(0, uuid_1.isUuid)(id))
100
+ return null;
98
101
  const client = await (0, client_1.escalations)();
99
102
  const result = await client.claim({ id, assignee: userId, durationMinutes });
100
103
  if (!result.ok)
@@ -132,6 +135,8 @@ async function claimEscalation(id, userId, durationMinutes = 30) {
132
135
  * the stored `resolver_payload`.
133
136
  */
134
137
  async function resolveEscalation(id, resolverPayload, metadata, assertClaim, resolvedBy) {
138
+ if (!(0, uuid_1.isUuid)(id))
139
+ return null;
135
140
  const client = await (0, client_1.escalations)();
136
141
  // `assertClaim` rides the SDK's guarded UPDATE: no active claim lock may
137
142
  // stand against this assignee, or the resolve blocks (claim-expired /
@@ -169,6 +174,7 @@ async function resolveEscalation(id, resolverPayload, metadata, assertClaim, res
169
174
  * `resolveEscalation` (which delivers the signal).
170
175
  */
171
176
  async function resolveEscalationsByIds(ids, resolverPayload, metadata) {
177
+ ids = (0, uuid_1.onlyUuids)(ids);
172
178
  if (ids.length === 0)
173
179
  return [];
174
180
  const client = await (0, client_1.escalations)();
@@ -209,6 +215,12 @@ async function resolveEscalationsByIds(ids, resolverPayload, metadata) {
209
215
  async function resolveEscalationsAllOrNone(items, metadata, assertAssignee, resolvedBy) {
210
216
  if (items.length === 0)
211
217
  return { ok: true, escalations: [] };
218
+ // A malformed id blocks the batch exactly like a well-formed missing one —
219
+ // and never reaches the uuid cast.
220
+ const malformed = items.filter((i) => !(0, uuid_1.isUuid)(i.id));
221
+ if (malformed.length > 0) {
222
+ return { ok: false, failed: malformed.map((i) => ({ id: i.id, reason: 'not-found' })) };
223
+ }
212
224
  const client = await (0, client_1.escalations)();
213
225
  const result = await client.resolveAllOrNone({ items, metadata, assertAssignee, resolvedBy });
214
226
  if (!result.ok)
@@ -235,6 +247,7 @@ async function resolveEscalationsAllOrNone(items, metadata, assertAssignee, reso
235
247
  * fields — signal routing shape, `metadata.form_schema` for redaction.
236
248
  */
237
249
  async function getEscalationsByIds(ids) {
250
+ ids = (0, uuid_1.onlyUuids)(ids);
238
251
  if (ids.length === 0)
239
252
  return [];
240
253
  const client = await (0, client_1.escalations)();
@@ -278,6 +291,8 @@ async function resolveEscalationBySignalKey(signalKey, resolverPayload, metadata
278
291
  * already terminal.
279
292
  */
280
293
  async function cancelEscalation(id) {
294
+ if (!(0, uuid_1.isUuid)(id))
295
+ return null;
281
296
  const client = await (0, client_1.escalations)();
282
297
  const result = await client.cancel(id);
283
298
  if (!result.ok)
@@ -301,6 +316,7 @@ async function cancelEscalation(id) {
301
316
  * updated.
302
317
  */
303
318
  async function updateEscalationsPriority(ids, priority) {
319
+ ids = (0, uuid_1.onlyUuids)(ids);
304
320
  if (ids.length === 0)
305
321
  return 0;
306
322
  const client = await (0, client_1.escalations)();
@@ -311,6 +327,7 @@ async function updateEscalationsPriority(ids, priority) {
311
327
  * Used for permission validation before bulk operations.
312
328
  */
313
329
  async function getEscalationRoles(ids) {
330
+ ids = (0, uuid_1.onlyUuids)(ids);
314
331
  if (ids.length === 0)
315
332
  return [];
316
333
  const client = await (0, client_1.escalations)();
@@ -323,6 +340,7 @@ async function getEscalationRoles(ids) {
323
340
  * write_self by ownership (assigned_to = caller). One query, no N+1.
324
341
  */
325
342
  async function getEscalationScopeRows(ids) {
343
+ ids = (0, uuid_1.onlyUuids)(ids);
326
344
  if (ids.length === 0)
327
345
  return [];
328
346
  const client = await (0, client_1.escalations)();
@@ -344,6 +362,8 @@ async function getEscalationScopeRows(ids) {
344
362
  * stays loud.
345
363
  */
346
364
  async function releaseEscalation(id, userId, opts) {
365
+ if (!(0, uuid_1.isUuid)(id))
366
+ return null;
347
367
  const client = await (0, client_1.escalations)();
348
368
  const result = await client.release({ id, assignee: userId });
349
369
  if (!result.ok)
@@ -381,6 +401,8 @@ async function releaseExpiredClaims() {
381
401
  * Clears the current assignment so it becomes available to the new role.
382
402
  */
383
403
  async function escalateToRole(id, targetRole) {
404
+ if (!(0, uuid_1.isUuid)(id))
405
+ return null;
384
406
  const client = await (0, client_1.escalations)();
385
407
  // The departing role is read before the move: the returned entry already
386
408
  // carries the target role, and role-scoped subscribers of the OLD queue
@@ -414,6 +436,8 @@ async function escalateToRole(id, targetRole) {
414
436
  return escalation;
415
437
  }
416
438
  async function getEscalation(id) {
439
+ if (!(0, uuid_1.isUuid)(id))
440
+ return null;
417
441
  const client = await (0, client_1.escalations)();
418
442
  const entry = await client.get(id);
419
443
  return entry ? (0, map_1.toEscalationRecord)(entry) : null;
@@ -474,6 +498,8 @@ async function cancelEscalationsByWorkflowId(workflowId) {
474
498
  return rows.length;
475
499
  }
476
500
  async function enrichEscalationRouting(id, metadataPatch, workflowFields) {
501
+ if (!(0, uuid_1.isUuid)(id))
502
+ return null;
477
503
  const client = await (0, client_1.escalations)();
478
504
  const entry = await client.update({
479
505
  id,
@@ -54,6 +54,19 @@ export declare function listFacetKeys(opts: {
54
54
  selfRoles?: string[];
55
55
  meUserId?: string;
56
56
  }): Promise<string[]>;
57
+ /**
58
+ * Distinct VALUES for one metadata facet key, visible to the caller — feeds
59
+ * the value picker (e.g. facility → [north, south]) so a bound scope offers
60
+ * real choices. Role-scoped with the same predicate as {@link listFacetKeys};
61
+ * the key is parameterized (never interpolated) and charset-validated, so a
62
+ * malformed key returns [] rather than reaching an unguarded path.
63
+ */
64
+ export declare function listFacetValues(key: string, opts: {
65
+ global?: boolean;
66
+ visibleRoles?: string[];
67
+ selfRoles?: string[];
68
+ meUserId?: string;
69
+ }): Promise<string[]>;
57
70
  export declare function getEscalationStats(visibleRoles?: string[], period?: string): Promise<EscalationStats>;
58
71
  export declare function listDistinctTypes(): Promise<string[]>;
59
72
  export declare function listEscalations(filters: {
@@ -108,4 +121,4 @@ export declare function listAvailableEscalations(filters: {
108
121
  export declare function invalidateEscalationAggregates(): void;
109
122
  /** Test hook: drop cached period metrics AND escalation stats so tests observe fresh rows. */
110
123
  export declare function resetStationMetricsCache(): void;
111
- export declare function getStationMetrics(visibleRoles: string[] | undefined, period?: string): Promise<StationMetric[]>;
124
+ export declare function getStationMetrics(visibleRoles: string[] | undefined, period?: string, facets?: Record<string, unknown>): Promise<StationMetric[]>;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.searchEscalationsFaceted = searchEscalationsFaceted;
4
4
  exports.getEscalationWithFormSchema = getEscalationWithFormSchema;
5
5
  exports.listFacetKeys = listFacetKeys;
6
+ exports.listFacetValues = listFacetValues;
6
7
  exports.getEscalationStats = getEscalationStats;
7
8
  exports.listDistinctTypes = listDistinctTypes;
8
9
  exports.listEscalations = listEscalations;
@@ -42,13 +43,13 @@ function buildSearchOrderBy(sortBy, order) {
42
43
  }
43
44
  return 'priority ASC, created_at ASC';
44
45
  }
45
- const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
46
+ const uuid_1 = require("../../lib/uuid");
46
47
  /**
47
48
  * The id arm of the correlation search binds a uuid param only when the term
48
49
  * parses as one — comparing `id::text = $term` casts the uuid column and has
49
50
  * no index path, which drags the whole OR into a full history scan.
50
51
  */
51
- const asUuidOrNull = (term) => term && UUID_RE.test(term) ? term : null;
52
+ const asUuidOrNull = (term) => term && uuid_1.UUID_RE.test(term) ? term : null;
52
53
  /**
53
54
  * Server-side free-text search over the `lt_escalations` view. Runs when a
54
55
  * caller supplies a non-empty `search` term — the SDK `client.list()` cannot do
@@ -155,6 +156,9 @@ async function searchEscalationsFaceted(opts) {
155
156
  * cast is guarded, so a non-numeric pin simply falls through to the latest form.
156
157
  */
157
158
  async function getEscalationWithFormSchema(id) {
159
+ // A non-UUID is definitionally not-found — it must never reach the uuid cast.
160
+ if (!(0, uuid_1.isUuid)(id))
161
+ return null;
158
162
  await (0, client_1.ensureEscalationCompatView)();
159
163
  const pool = (0, db_1.getPool)();
160
164
  const { rows } = await pool.query(`SELECT e.*, COALESCE(ps.form_schema, r.form_schema) AS resolved_form_schema
@@ -216,6 +220,39 @@ async function listFacetKeys(opts) {
216
220
  }
217
221
  return [...actualKeys].sort();
218
222
  }
223
+ // Mirrors the server-side facet key rule — metadata keys become a JSON path,
224
+ // so a lookup key is strictly validated before it reaches SQL.
225
+ const FACET_KEY_RE = /^[a-zA-Z0-9_]+$/;
226
+ /**
227
+ * Distinct VALUES for one metadata facet key, visible to the caller — feeds
228
+ * the value picker (e.g. facility → [north, south]) so a bound scope offers
229
+ * real choices. Role-scoped with the same predicate as {@link listFacetKeys};
230
+ * the key is parameterized (never interpolated) and charset-validated, so a
231
+ * malformed key returns [] rather than reaching an unguarded path.
232
+ */
233
+ async function listFacetValues(key, opts) {
234
+ if (!FACET_KEY_RE.test(key))
235
+ return [];
236
+ await (0, client_1.ensureEscalationCompatView)();
237
+ const pool = (0, db_1.getPool)();
238
+ const params = [key];
239
+ const clauses = ["jsonb_typeof(metadata) = 'object'", 'metadata ? $1'];
240
+ if (!opts.global) {
241
+ const ai = params.push(opts.visibleRoles?.length ? opts.visibleRoles : null);
242
+ const si = params.push(opts.selfRoles?.length ? opts.selfRoles : null);
243
+ const mi = params.push(opts.meUserId || null);
244
+ clauses.push(`(($${ai}::text[] IS NULL AND $${si}::text[] IS NULL)
245
+ OR ($${ai}::text[] IS NOT NULL AND role = ANY($${ai}))
246
+ OR ($${si}::text[] IS NOT NULL AND role = ANY($${si}) AND assigned_to = $${mi}))`);
247
+ }
248
+ const { rows } = await pool.query(`SELECT DISTINCT metadata->>$1 AS value
249
+ FROM public.lt_escalations
250
+ WHERE ${clauses.join('\n AND ')}
251
+ AND metadata->>$1 IS NOT NULL
252
+ ORDER BY value
253
+ LIMIT 200`, params);
254
+ return rows.map((r) => r.value);
255
+ }
219
256
  // Escalation stats back the home + overview surfaces and refresh on every
220
257
  // escalation event. As of hotmesh 0.25.0 the SDK stats query reads three
221
258
  // bounded, index-only sources (pending backlog, created window, resolved
@@ -388,7 +425,7 @@ function resetStationMetricsCache() {
388
425
  invalidateEscalationAggregates();
389
426
  }
390
427
  const toNum = (v) => (v != null ? Number(v) : null);
391
- async function getStationMetrics(visibleRoles, period) {
428
+ async function getStationMetrics(visibleRoles, period, facets) {
392
429
  await (0, client_1.ensureEscalationCompatView)();
393
430
  // hasOwnProperty: a caller-supplied period like 'constructor' must fall back
394
431
  // to 24h, not resolve to an inherited Object.prototype member.
@@ -396,13 +433,18 @@ async function getStationMetrics(visibleRoles, period) {
396
433
  ? types_1.VALID_PERIODS[period]
397
434
  : '24 hours';
398
435
  const roles = visibleRoles ?? null;
436
+ // Facet scope rides both queries as a GIN-served `metadata @>` arm; null = no
437
+ // filter. It never widens role scope — it only narrows within it.
438
+ const facetJson = facets && Object.keys(facets).length ? JSON.stringify(facets) : null;
399
439
  const pool = (0, db_1.getPool)();
400
440
  // Live counts: always fresh (cheap — bounded pending working set).
401
- // Period metrics: cached ~30s, single-flight (expensive percentile sort).
402
- const cacheKey = `${period ?? '24h'}::${roles ? [...roles].sort().join(',') : 'ALL'}`;
441
+ // Period metrics: cached ~30s, single-flight (expensive percentile sort). The
442
+ // facet scope is part of the cache key — a scoped board never reads unscoped
443
+ // rows, and vice versa.
444
+ const cacheKey = `${period ?? '24h'}::${roles ? [...roles].sort().join(',') : 'ALL'}::${facetJson ?? ''}`;
403
445
  const [countsResult, periodRows] = await Promise.all([
404
- pool.query(sql_1.STATION_LIVE_COUNTS_SQL, [roles]),
405
- stationPeriodCache.resolve(cacheKey, async () => (await pool.query(sql_1.STATION_PERIOD_METRICS_SQL, [roles, intervalStr])).rows),
446
+ pool.query(sql_1.STATION_LIVE_COUNTS_SQL, [roles, facetJson]),
447
+ stationPeriodCache.resolve(cacheKey, async () => (await pool.query(sql_1.STATION_PERIOD_METRICS_SQL, [roles, intervalStr, facetJson])).rows),
406
448
  ]);
407
449
  const periodByRole = new Map(periodRows.map((r) => [r.role, r]));
408
450
  return countsResult.rows.map((c) => {
@@ -80,6 +80,7 @@ export declare const RESOLVE_BY_METADATA_ATOMIC = "WITH target AS MATERIALIZED (
80
80
  * Live counts — pending, active claims, and priority (past-threshold) per role.
81
81
  *
82
82
  * $1 TEXT[] — role filter (NULL = all roles / superadmin).
83
+ * $2 JSONB — metadata facet scope (NULL = no filter; `metadata @>`, GIN-served).
83
84
  *
84
85
  * Hits idx_hmsh_esc_pending_role_created (role, created_at DESC) WHERE
85
86
  * status='pending'. The pending working set is bounded by real backlog depth,
@@ -102,13 +103,14 @@ export declare const RESOLVE_BY_METADATA_ATOMIC = "WITH target AS MATERIALIZED (
102
103
  * with a facet configured, items missing the key or holding an unparseable
103
104
  * value do not count (NULL age never passes the comparison).
104
105
  */
105
- export declare const STATION_LIVE_COUNTS_SQL = "\nWITH role_targets AS (\n SELECT role, sla_minutes, priority_threshold_minutes, priority_facet\n FROM lt_roles\n WHERE ($1::text[] IS NULL OR role = ANY($1::text[]))\n),\nlive_counts AS (\n SELECT\n e.role,\n COUNT(*) AS pending,\n COUNT(*) FILTER (WHERE e.assigned_to IS NOT NULL AND e.assigned_until > NOW()) AS claimed,\n COUNT(*) FILTER (\n WHERE COALESCE(rt.priority_threshold_minutes, rt.sla_minutes) IS NOT NULL\n AND (e.assigned_to IS NULL OR e.assigned_until IS NULL OR e.assigned_until <= NOW())\n AND CASE\n WHEN rt.priority_facet IS NULL THEN e.created_at\n WHEN pg_input_is_valid(e.metadata->>rt.priority_facet, 'timestamptz')\n THEN (e.metadata->>rt.priority_facet)::timestamptz\n ELSE NULL\n END < NOW() - (COALESCE(rt.priority_threshold_minutes, rt.sla_minutes) * INTERVAL '1 minute')\n ) AS priority_count\n FROM hmsh_escalations e\n JOIN role_targets rt ON rt.role = e.role\n WHERE e.status = 'pending'\n GROUP BY e.role\n)\nSELECT\n rt.role,\n COALESCE(lc.pending, 0) AS pending,\n COALESCE(lc.claimed, 0) AS claimed,\n COALESCE(lc.priority_count, 0) AS priority_count\nFROM role_targets rt\nLEFT JOIN live_counts lc ON lc.role = rt.role\nORDER BY rt.role\n";
106
+ export declare const STATION_LIVE_COUNTS_SQL = "\nWITH role_targets AS (\n SELECT role, sla_minutes, priority_threshold_minutes, priority_facet\n FROM lt_roles\n WHERE ($1::text[] IS NULL OR role = ANY($1::text[]))\n),\nlive_counts AS (\n SELECT\n e.role,\n COUNT(*) AS pending,\n COUNT(*) FILTER (WHERE e.assigned_to IS NOT NULL AND e.assigned_until > NOW()) AS claimed,\n COUNT(*) FILTER (\n WHERE COALESCE(rt.priority_threshold_minutes, rt.sla_minutes) IS NOT NULL\n AND (e.assigned_to IS NULL OR e.assigned_until IS NULL OR e.assigned_until <= NOW())\n AND CASE\n WHEN rt.priority_facet IS NULL THEN e.created_at\n WHEN pg_input_is_valid(e.metadata->>rt.priority_facet, 'timestamptz')\n THEN (e.metadata->>rt.priority_facet)::timestamptz\n ELSE NULL\n END < NOW() - (COALESCE(rt.priority_threshold_minutes, rt.sla_minutes) * INTERVAL '1 minute')\n ) AS priority_count\n FROM hmsh_escalations e\n JOIN role_targets rt ON rt.role = e.role\n WHERE e.status = 'pending'\n AND ($2::jsonb IS NULL OR e.metadata @> $2::jsonb)\n GROUP BY e.role\n)\nSELECT\n rt.role,\n COALESCE(lc.pending, 0) AS pending,\n COALESCE(lc.claimed, 0) AS claimed,\n COALESCE(lc.priority_count, 0) AS priority_count\nFROM role_targets rt\nLEFT JOIN live_counts lc ON lc.role = rt.role\nORDER BY rt.role\n";
106
107
  /**
107
108
  * Period metrics — resolved count, throughput efficiency, and P99/P50/avg/max
108
109
  * latency for wait (queue time) and work (processing) per role.
109
110
  *
110
111
  * $1 TEXT[] — role filter (NULL = all roles / superadmin).
111
112
  * $2 INTERVAL — time window (e.g. '24 hours', '15 minutes').
113
+ * $3 JSONB — metadata facet scope (NULL = no filter; `metadata @>`, GIN-served).
112
114
  *
113
115
  * Hits idx_hmsh_esc_resolved_cover (role, resolved_at DESC, claimed_at,
114
116
  * created_at) WHERE status='resolved' — a covering index, so the percentile
@@ -119,4 +121,4 @@ export declare const STATION_LIVE_COUNTS_SQL = "\nWITH role_targets AS (\n SELE
119
121
  * mix of resolved + in-flight — a clean "how did completed work perform?" read.
120
122
  * throughput_pct is NULL when the role has no target_per_hour (NULLIF guard).
121
123
  */
122
- export declare const STATION_PERIOD_METRICS_SQL = "\nWITH role_targets AS (\n SELECT role, target_per_hour\n FROM lt_roles\n WHERE ($1::text[] IS NULL OR role = ANY($1::text[]))\n),\nperiod_metrics AS (\n SELECT\n e.role,\n COUNT(*) AS resolved,\n ROUND((PERCENTILE_CONT(0.99) WITHIN GROUP (\n ORDER BY EXTRACT(EPOCH FROM (e.claimed_at - e.created_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS p99_wait_min,\n ROUND((PERCENTILE_CONT(0.50) WITHIN GROUP (\n ORDER BY EXTRACT(EPOCH FROM (e.claimed_at - e.created_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS p50_wait_min,\n ROUND((AVG(EXTRACT(EPOCH FROM (e.claimed_at - e.created_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS avg_wait_min,\n ROUND((MAX(EXTRACT(EPOCH FROM (e.claimed_at - e.created_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS max_wait_min,\n ROUND((PERCENTILE_CONT(0.99) WITHIN GROUP (\n ORDER BY EXTRACT(EPOCH FROM (e.resolved_at - e.claimed_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS p99_work_min,\n ROUND((PERCENTILE_CONT(0.50) WITHIN GROUP (\n ORDER BY EXTRACT(EPOCH FROM (e.resolved_at - e.claimed_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS p50_work_min,\n ROUND((AVG(EXTRACT(EPOCH FROM (e.resolved_at - e.claimed_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS avg_work_min,\n ROUND((MAX(EXTRACT(EPOCH FROM (e.resolved_at - e.claimed_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS max_work_min\n FROM hmsh_escalations e\n WHERE e.status = 'resolved'\n AND e.resolved_at >= NOW() - $2::interval\n AND ($1::text[] IS NULL OR e.role = ANY($1::text[]))\n GROUP BY e.role\n)\nSELECT\n rt.role,\n COALESCE(pm.resolved, 0) AS resolved,\n ROUND(\n (COALESCE(pm.resolved, 0)::numeric\n / NULLIF(rt.target_per_hour::numeric * EXTRACT(EPOCH FROM $2::interval) / 3600.0, 0)\n * 100\n )::numeric, 1\n ) AS throughput_pct,\n pm.p99_wait_min,\n pm.p50_wait_min,\n pm.avg_wait_min,\n pm.max_wait_min,\n pm.p99_work_min,\n pm.p50_work_min,\n pm.avg_work_min,\n pm.max_work_min\nFROM role_targets rt\nLEFT JOIN period_metrics pm ON pm.role = rt.role\nORDER BY rt.role\n";
124
+ export declare const STATION_PERIOD_METRICS_SQL = "\nWITH role_targets AS (\n SELECT role, target_per_hour\n FROM lt_roles\n WHERE ($1::text[] IS NULL OR role = ANY($1::text[]))\n),\nperiod_metrics AS (\n SELECT\n e.role,\n COUNT(*) AS resolved,\n ROUND((PERCENTILE_CONT(0.99) WITHIN GROUP (\n ORDER BY EXTRACT(EPOCH FROM (e.claimed_at - e.created_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS p99_wait_min,\n ROUND((PERCENTILE_CONT(0.50) WITHIN GROUP (\n ORDER BY EXTRACT(EPOCH FROM (e.claimed_at - e.created_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS p50_wait_min,\n ROUND((AVG(EXTRACT(EPOCH FROM (e.claimed_at - e.created_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS avg_wait_min,\n ROUND((MAX(EXTRACT(EPOCH FROM (e.claimed_at - e.created_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS max_wait_min,\n ROUND((PERCENTILE_CONT(0.99) WITHIN GROUP (\n ORDER BY EXTRACT(EPOCH FROM (e.resolved_at - e.claimed_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS p99_work_min,\n ROUND((PERCENTILE_CONT(0.50) WITHIN GROUP (\n ORDER BY EXTRACT(EPOCH FROM (e.resolved_at - e.claimed_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS p50_work_min,\n ROUND((AVG(EXTRACT(EPOCH FROM (e.resolved_at - e.claimed_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS avg_work_min,\n ROUND((MAX(EXTRACT(EPOCH FROM (e.resolved_at - e.claimed_at)) / 60)\n FILTER (WHERE e.claimed_at IS NOT NULL))::numeric, 3) AS max_work_min\n FROM hmsh_escalations e\n WHERE e.status = 'resolved'\n AND e.resolved_at >= NOW() - $2::interval\n AND ($1::text[] IS NULL OR e.role = ANY($1::text[]))\n AND ($3::jsonb IS NULL OR e.metadata @> $3::jsonb)\n GROUP BY e.role\n)\nSELECT\n rt.role,\n COALESCE(pm.resolved, 0) AS resolved,\n ROUND(\n (COALESCE(pm.resolved, 0)::numeric\n / NULLIF(rt.target_per_hour::numeric * EXTRACT(EPOCH FROM $2::interval) / 3600.0, 0)\n * 100\n )::numeric, 1\n ) AS throughput_pct,\n pm.p99_wait_min,\n pm.p50_wait_min,\n pm.avg_wait_min,\n pm.max_wait_min,\n pm.p99_work_min,\n pm.p50_work_min,\n pm.avg_work_min,\n pm.max_work_min\nFROM role_targets rt\nLEFT JOIN period_metrics pm ON pm.role = rt.role\nORDER BY rt.role\n";
@@ -270,6 +270,7 @@ LEFT JOIN written ON written.id = target.id`;
270
270
  * Live counts — pending, active claims, and priority (past-threshold) per role.
271
271
  *
272
272
  * $1 TEXT[] — role filter (NULL = all roles / superadmin).
273
+ * $2 JSONB — metadata facet scope (NULL = no filter; `metadata @>`, GIN-served).
273
274
  *
274
275
  * Hits idx_hmsh_esc_pending_role_created (role, created_at DESC) WHERE
275
276
  * status='pending'. The pending working set is bounded by real backlog depth,
@@ -316,6 +317,7 @@ live_counts AS (
316
317
  FROM hmsh_escalations e
317
318
  JOIN role_targets rt ON rt.role = e.role
318
319
  WHERE e.status = 'pending'
320
+ AND ($2::jsonb IS NULL OR e.metadata @> $2::jsonb)
319
321
  GROUP BY e.role
320
322
  )
321
323
  SELECT
@@ -333,6 +335,7 @@ ORDER BY rt.role
333
335
  *
334
336
  * $1 TEXT[] — role filter (NULL = all roles / superadmin).
335
337
  * $2 INTERVAL — time window (e.g. '24 hours', '15 minutes').
338
+ * $3 JSONB — metadata facet scope (NULL = no filter; `metadata @>`, GIN-served).
336
339
  *
337
340
  * Hits idx_hmsh_esc_resolved_cover (role, resolved_at DESC, claimed_at,
338
341
  * created_at) WHERE status='resolved' — a covering index, so the percentile
@@ -377,6 +380,7 @@ period_metrics AS (
377
380
  WHERE e.status = 'resolved'
378
381
  AND e.resolved_at >= NOW() - $2::interval
379
382
  AND ($1::text[] IS NULL OR e.role = ANY($1::text[]))
383
+ AND ($3::jsonb IS NULL OR e.metadata @> $3::jsonb)
380
384
  GROUP BY e.role
381
385
  )
382
386
  SELECT
@@ -47,6 +47,11 @@ const context_2 = require("../iam/context");
47
47
  const publish_1 = require("../../lib/events/publish");
48
48
  const envelope_1 = require("../iam/envelope");
49
49
  const DEFAULT_ACTIVITY_QUEUE = 'lt-interceptor';
50
+ /** The result payload a completed event carries: the workflow's `data` when it returned the envelope shape. */
51
+ function resultData(result) {
52
+ const data = result?.data;
53
+ return data && typeof data === 'object' && !Array.isArray(data) ? data : undefined;
54
+ }
50
55
  /**
51
56
  * Register the Long Tail interceptors in a single call.
52
57
  *
@@ -120,6 +125,7 @@ function createLTInterceptor(options) {
120
125
  workflowName: wf.workflowName,
121
126
  taskQueue: taskQueue0,
122
127
  status: 'completed',
128
+ data: resultData(result0),
123
129
  });
124
130
  return result0;
125
131
  }
@@ -138,6 +144,7 @@ function createLTInterceptor(options) {
138
144
  workflowName: wf.workflowName,
139
145
  taskQueue: taskQueue2,
140
146
  status: 'completed',
147
+ data: resultData(result2),
141
148
  });
142
149
  return result2;
143
150
  }