@hotmeshio/long-tail 0.5.12 → 0.6.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 (303) hide show
  1. package/README.md +1 -0
  2. package/build/api/escalations/bulk.js +9 -1
  3. package/build/api/escalations/cancel.js +5 -7
  4. package/build/api/escalations/claim.js +18 -15
  5. package/build/api/escalations/create.d.ts +6 -0
  6. package/build/api/escalations/create.js +11 -8
  7. package/build/api/escalations/facets.d.ts +18 -0
  8. package/build/api/escalations/facets.js +115 -0
  9. package/build/api/escalations/helpers.d.ts +70 -1
  10. package/build/api/escalations/helpers.js +101 -7
  11. package/build/api/escalations/index.d.ts +3 -2
  12. package/build/api/escalations/index.js +7 -1
  13. package/build/api/escalations/list.d.ts +21 -2
  14. package/build/api/escalations/list.js +109 -10
  15. package/build/api/escalations/metadata.js +22 -27
  16. package/build/api/escalations/resolve.d.ts +19 -1
  17. package/build/api/escalations/resolve.js +111 -26
  18. package/build/api/escalations/single.js +8 -8
  19. package/build/api/users.d.ts +6 -0
  20. package/build/api/users.js +29 -1
  21. package/build/bin/ltc.js +40 -0
  22. package/build/lib/cli/commands/escalations.d.ts +36 -0
  23. package/build/lib/cli/commands/escalations.js +98 -0
  24. package/build/lib/db/schemas/001_schema.sql +10 -5
  25. package/build/lib/db/schemas/012_lt_tasks_workflow_id_unique.sql +20 -0
  26. package/build/lib/db/schemas/013_role_scope.sql +37 -0
  27. package/build/modules/version.d.ts +10 -5
  28. package/build/modules/version.js +27 -11
  29. package/build/routes/escalations/facets.d.ts +7 -0
  30. package/build/routes/escalations/facets.js +81 -0
  31. package/build/routes/escalations/index.js +3 -0
  32. package/build/routes/escalations/list.js +46 -0
  33. package/build/routes/escalations/resolve.js +22 -4
  34. package/build/routes/users.js +9 -5
  35. package/build/sdk/index.d.ts +33 -2
  36. package/build/sdk/index.js +7 -0
  37. package/build/services/escalation/crud.d.ts +56 -6
  38. package/build/services/escalation/crud.js +102 -31
  39. package/build/services/escalation/facet-sql.d.ts +18 -0
  40. package/build/services/escalation/facet-sql.js +118 -0
  41. package/build/services/escalation/facets.d.ts +45 -0
  42. package/build/services/escalation/facets.js +218 -0
  43. package/build/services/escalation/index.d.ts +1 -0
  44. package/build/services/escalation/index.js +8 -1
  45. package/build/services/escalation/queries.d.ts +45 -0
  46. package/build/services/escalation/queries.js +107 -6
  47. package/build/services/escalation/sql.d.ts +10 -4
  48. package/build/services/escalation/sql.js +36 -8
  49. package/build/services/interceptor/activities/escalation.d.ts +2 -0
  50. package/build/services/interceptor/activities/escalation.js +1 -1
  51. package/build/services/interceptor/lifecycle.js +4 -0
  52. package/build/services/mcp/server-tools.js +10 -4
  53. package/build/services/orchestrator/condition.js +6 -2
  54. package/build/services/role/index.js +2 -3
  55. package/build/services/role/sql.d.ts +8 -0
  56. package/build/services/role/sql.js +17 -1
  57. package/build/services/task/crud.js +22 -14
  58. package/build/services/task/process.js +3 -21
  59. package/build/services/task/sql.d.ts +24 -1
  60. package/build/services/task/sql.js +65 -1
  61. package/build/services/user/crud.d.ts +1 -0
  62. package/build/services/user/crud.js +29 -11
  63. package/build/services/user/index.d.ts +2 -1
  64. package/build/services/user/index.js +12 -1
  65. package/build/services/user/rbac.d.ts +18 -0
  66. package/build/services/user/rbac.js +27 -0
  67. package/build/services/user/roles.d.ts +7 -2
  68. package/build/services/user/roles.js +21 -5
  69. package/build/services/user/scope.d.ts +22 -0
  70. package/build/services/user/scope.js +50 -0
  71. package/build/services/user/seed-admin.js +34 -17
  72. package/build/services/user/sql.d.ts +18 -7
  73. package/build/services/user/sql.js +38 -13
  74. package/build/services/user/sso-provision.js +41 -24
  75. package/build/services/user/types.d.ts +9 -5
  76. package/build/system/mcp-servers/admin/escalations.js +65 -0
  77. package/build/system/mcp-servers/admin/schemas.d.ts +325 -0
  78. package/build/system/mcp-servers/admin/schemas.js +63 -2
  79. package/build/system/mcp-servers/admin/users.js +28 -2
  80. package/build/tsconfig.tsbuildinfo +1 -1
  81. package/build/types/envelope.d.ts +7 -0
  82. package/build/types/facets.d.ts +60 -0
  83. package/build/types/facets.js +10 -0
  84. package/build/types/index.d.ts +2 -1
  85. package/build/types/user.d.ts +11 -0
  86. package/dashboard/dist/assets/AdminDashboard-2CZ8py9X.js +2 -0
  87. package/dashboard/dist/assets/{AdminDashboard-LTiYLuzq.js.map → AdminDashboard-2CZ8py9X.js.map} +1 -1
  88. package/dashboard/dist/assets/{AgentConfigPage-nlOudfpZ.js → AgentConfigPage-D68sodgo.js} +2 -2
  89. package/dashboard/dist/assets/{AgentConfigPage-nlOudfpZ.js.map → AgentConfigPage-D68sodgo.js.map} +1 -1
  90. package/dashboard/dist/assets/{AgentDetailPage-BSUnDvGp.js → AgentDetailPage-BmhuWbKi.js} +2 -2
  91. package/dashboard/dist/assets/{AgentDetailPage-BSUnDvGp.js.map → AgentDetailPage-BmhuWbKi.js.map} +1 -1
  92. package/dashboard/dist/assets/{AgentsPage-DpS0cCYe.js → AgentsPage-C4SKQVeU.js} +2 -2
  93. package/dashboard/dist/assets/{AgentsPage-DpS0cCYe.js.map → AgentsPage-C4SKQVeU.js.map} +1 -1
  94. package/dashboard/dist/assets/AvailableEscalationsPage-9RBEYwkt.js +2 -0
  95. package/dashboard/dist/assets/AvailableEscalationsPage-9RBEYwkt.js.map +1 -0
  96. package/dashboard/dist/assets/{BotPicker-BhKmq7M7.js → BotPicker-D-FvXJOM.js} +2 -2
  97. package/dashboard/dist/assets/{BotPicker-BhKmq7M7.js.map → BotPicker-D-FvXJOM.js.map} +1 -1
  98. package/dashboard/dist/assets/{CapabilitiesPage-DLIleX2L.js → CapabilitiesPage-ByDf6WJr.js} +2 -2
  99. package/dashboard/dist/assets/{CapabilitiesPage-DLIleX2L.js.map → CapabilitiesPage-ByDf6WJr.js.map} +1 -1
  100. package/dashboard/dist/assets/{CollapsibleSection-D7SVwNVk.js → CollapsibleSection-BjGNYEvh.js} +2 -2
  101. package/dashboard/dist/assets/{CollapsibleSection-D7SVwNVk.js.map → CollapsibleSection-BjGNYEvh.js.map} +1 -1
  102. package/dashboard/dist/assets/CopyableId-blg-gZvT.js +2 -0
  103. package/dashboard/dist/assets/CopyableId-blg-gZvT.js.map +1 -0
  104. package/dashboard/dist/assets/{CredentialsPage-fat5Z2U9.js → CredentialsPage-DvCA-wQN.js} +2 -2
  105. package/dashboard/dist/assets/{CredentialsPage-fat5Z2U9.js.map → CredentialsPage-DvCA-wQN.js.map} +1 -1
  106. package/dashboard/dist/assets/{CronLabel-OOP4P2mJ.js → CronLabel-DBMurd_c.js} +2 -2
  107. package/dashboard/dist/assets/{CronLabel-OOP4P2mJ.js.map → CronLabel-DBMurd_c.js.map} +1 -1
  108. package/dashboard/dist/assets/{CustomDurationPicker-COacSAXO.js → CustomDurationPicker-DHflFoEa.js} +2 -2
  109. package/dashboard/dist/assets/{CustomDurationPicker-COacSAXO.js.map → CustomDurationPicker-DHflFoEa.js.map} +1 -1
  110. package/dashboard/dist/assets/{DropZone-Bfw2YLVS.js → DropZone-CUtdp99Z.js} +2 -2
  111. package/dashboard/dist/assets/{DropZone-Bfw2YLVS.js.map → DropZone-CUtdp99Z.js.map} +1 -1
  112. package/dashboard/dist/assets/{ElapsedCell-CkfCD-m7.js → ElapsedCell-CSjcVOpo.js} +2 -2
  113. package/dashboard/dist/assets/{ElapsedCell-CkfCD-m7.js.map → ElapsedCell-CSjcVOpo.js.map} +1 -1
  114. package/dashboard/dist/assets/{EscalationsOverview-BmI8jxlh.js → EscalationsOverview-BUqRNJbK.js} +2 -2
  115. package/dashboard/dist/assets/{EscalationsOverview-BmI8jxlh.js.map → EscalationsOverview-BUqRNJbK.js.map} +1 -1
  116. package/dashboard/dist/assets/{EventTable-CeKfFk3m.js → EventTable-Chz-6XPE.js} +2 -2
  117. package/dashboard/dist/assets/{EventTable-CeKfFk3m.js.map → EventTable-Chz-6XPE.js.map} +1 -1
  118. package/dashboard/dist/assets/{GraphInvokePage-BZBPBCSq.js → GraphInvokePage-BXdgciWg.js} +2 -2
  119. package/dashboard/dist/assets/{GraphInvokePage-BZBPBCSq.js.map → GraphInvokePage-BXdgciWg.js.map} +1 -1
  120. package/dashboard/dist/assets/{HomePage-dM7U2tl6.js → HomePage-CEMxnk0r.js} +2 -2
  121. package/dashboard/dist/assets/{HomePage-dM7U2tl6.js.map → HomePage-CEMxnk0r.js.map} +1 -1
  122. package/dashboard/dist/assets/{ListToolbar-BJaCzAXS.js → ListToolbar-_73JCdbB.js} +2 -2
  123. package/dashboard/dist/assets/{ListToolbar-BJaCzAXS.js.map → ListToolbar-_73JCdbB.js.map} +1 -1
  124. package/dashboard/dist/assets/{McpOverview-CvnDG5OB.js → McpOverview-DfdqxhSz.js} +2 -2
  125. package/dashboard/dist/assets/{McpOverview-CvnDG5OB.js.map → McpOverview-DfdqxhSz.js.map} +1 -1
  126. package/dashboard/dist/assets/{McpQueryDetailPage-DwmSIcIL.js → McpQueryDetailPage-DPAljieY.js} +2 -2
  127. package/dashboard/dist/assets/{McpQueryDetailPage-DwmSIcIL.js.map → McpQueryDetailPage-DPAljieY.js.map} +1 -1
  128. package/dashboard/dist/assets/{McpQueryPage-CU6FMEEk.js → McpQueryPage-D5cSziYS.js} +2 -2
  129. package/dashboard/dist/assets/{McpQueryPage-CU6FMEEk.js.map → McpQueryPage-D5cSziYS.js.map} +1 -1
  130. package/dashboard/dist/assets/{McpRunDetailPage-8a6lsehv.js → McpRunDetailPage-DwNb9wZg.js} +2 -2
  131. package/dashboard/dist/assets/{McpRunDetailPage-8a6lsehv.js.map → McpRunDetailPage-DwNb9wZg.js.map} +1 -1
  132. package/dashboard/dist/assets/{McpRunsPage-CPMYWgm_.js → McpRunsPage-O5H6yStl.js} +2 -2
  133. package/dashboard/dist/assets/{McpRunsPage-CPMYWgm_.js.map → McpRunsPage-O5H6yStl.js.map} +1 -1
  134. package/dashboard/dist/assets/{NamespacePill-D6btr6e8.js → NamespacePill-uW-xOedj.js} +2 -2
  135. package/dashboard/dist/assets/{NamespacePill-D6btr6e8.js.map → NamespacePill-uW-xOedj.js.map} +1 -1
  136. package/dashboard/dist/assets/OperatorDashboard-DYw9Ekv5.js +2 -0
  137. package/dashboard/dist/assets/{OperatorDashboard-g9o7I925.js.map → OperatorDashboard-DYw9Ekv5.js.map} +1 -1
  138. package/dashboard/dist/assets/PageHeader-DVOZjBfL.js +2 -0
  139. package/dashboard/dist/assets/PageHeader-DVOZjBfL.js.map +1 -0
  140. package/dashboard/dist/assets/{PageHeaderWithStats-DJQCZ0FR.js → PageHeaderWithStats-DtTpDHOd.js} +2 -2
  141. package/dashboard/dist/assets/{PageHeaderWithStats-DJQCZ0FR.js.map → PageHeaderWithStats-DtTpDHOd.js.map} +1 -1
  142. package/dashboard/dist/assets/PriorityBadge-B4ykBH7f.js +2 -0
  143. package/dashboard/dist/assets/PriorityBadge-B4ykBH7f.js.map +1 -0
  144. package/dashboard/dist/assets/{ProcessDetailPage-ByyWCs2A.js → ProcessDetailPage-B0Dokcx9.js} +2 -2
  145. package/dashboard/dist/assets/{ProcessDetailPage-ByyWCs2A.js.map → ProcessDetailPage-B0Dokcx9.js.map} +1 -1
  146. package/dashboard/dist/assets/{ProcessesListPage-BTsXds4l.js → ProcessesListPage-Cc87WIjL.js} +2 -2
  147. package/dashboard/dist/assets/{ProcessesListPage-BTsXds4l.js.map → ProcessesListPage-Cc87WIjL.js.map} +1 -1
  148. package/dashboard/dist/assets/RolePill-BaW-30TC.js +2 -0
  149. package/dashboard/dist/assets/RolePill-BaW-30TC.js.map +1 -0
  150. package/dashboard/dist/assets/{RolesPage-B2Kdf-L9.js → RolesPage-cp6PiF08.js} +2 -2
  151. package/dashboard/dist/assets/{RolesPage-B2Kdf-L9.js.map → RolesPage-cp6PiF08.js.map} +1 -1
  152. package/dashboard/dist/assets/RowActions-BA9L9djf.js +2 -0
  153. package/dashboard/dist/assets/RowActions-BA9L9djf.js.map +1 -0
  154. package/dashboard/dist/assets/{RunAsSelector-BzkZXpSX.js → RunAsSelector-2LWkjSnO.js} +2 -2
  155. package/dashboard/dist/assets/{RunAsSelector-BzkZXpSX.js.map → RunAsSelector-2LWkjSnO.js.map} +1 -1
  156. package/dashboard/dist/assets/{StickyPagination-BWhFSr2d.js → StickyPagination-BQEm--kI.js} +2 -2
  157. package/dashboard/dist/assets/{StickyPagination-BWhFSr2d.js.map → StickyPagination-BQEm--kI.js.map} +1 -1
  158. package/dashboard/dist/assets/{StreamMessageDetail-B6ch-6SY.js → StreamMessageDetail-BTtw3GjX.js} +2 -2
  159. package/dashboard/dist/assets/{StreamMessageDetail-B6ch-6SY.js.map → StreamMessageDetail-BTtw3GjX.js.map} +1 -1
  160. package/dashboard/dist/assets/{SwimlaneTimeline-CjvfUkls.js → SwimlaneTimeline-DdXxP4fO.js} +2 -2
  161. package/dashboard/dist/assets/{SwimlaneTimeline-CjvfUkls.js.map → SwimlaneTimeline-DdXxP4fO.js.map} +1 -1
  162. package/dashboard/dist/assets/{TagInput-CZSeyesi.js → TagInput-x9XTuqMb.js} +2 -2
  163. package/dashboard/dist/assets/{TagInput-CZSeyesi.js.map → TagInput-x9XTuqMb.js.map} +1 -1
  164. package/dashboard/dist/assets/{TaskDetailPage-BjHU6opi.js → TaskDetailPage-B8veYG4X.js} +2 -2
  165. package/dashboard/dist/assets/{TaskDetailPage-BjHU6opi.js.map → TaskDetailPage-B8veYG4X.js.map} +1 -1
  166. package/dashboard/dist/assets/{TaskQueuePill-CQrXrSxk.js → TaskQueuePill-DSEQZ-HT.js} +2 -2
  167. package/dashboard/dist/assets/{TaskQueuePill-CQrXrSxk.js.map → TaskQueuePill-DSEQZ-HT.js.map} +1 -1
  168. package/dashboard/dist/assets/{TasksListPage-Bem44zd0.js → TasksListPage-CsN1gZj5.js} +2 -2
  169. package/dashboard/dist/assets/{TasksListPage-Bem44zd0.js.map → TasksListPage-CsN1gZj5.js.map} +1 -1
  170. package/dashboard/dist/assets/{TimeAgo-CUIYqSt_.js → TimeAgo-n182EcLx.js} +2 -2
  171. package/dashboard/dist/assets/{TimeAgo-CUIYqSt_.js.map → TimeAgo-n182EcLx.js.map} +1 -1
  172. package/dashboard/dist/assets/{TimestampCell--tnreI1i.js → TimestampCell-2dlcQAfX.js} +2 -2
  173. package/dashboard/dist/assets/{TimestampCell--tnreI1i.js.map → TimestampCell-2dlcQAfX.js.map} +1 -1
  174. package/dashboard/dist/assets/{ToolPill-O09jsTQM.js → ToolPill-B_e4eT77.js} +2 -2
  175. package/dashboard/dist/assets/{ToolPill-O09jsTQM.js.map → ToolPill-B_e4eT77.js.map} +1 -1
  176. package/dashboard/dist/assets/{ToolTestPanel-OD99H7Pk.js → ToolTestPanel-Bhnncc6M.js} +2 -2
  177. package/dashboard/dist/assets/{ToolTestPanel-OD99H7Pk.js.map → ToolTestPanel-Bhnncc6M.js.map} +1 -1
  178. package/dashboard/dist/assets/{TopicDetailPage-rh_dsTJI.js → TopicDetailPage-29lsNsqI.js} +2 -2
  179. package/dashboard/dist/assets/{TopicDetailPage-rh_dsTJI.js.map → TopicDetailPage-29lsNsqI.js.map} +1 -1
  180. package/dashboard/dist/assets/{TopicsPage-CD_40_v-.js → TopicsPage-KJ7_LAvo.js} +2 -2
  181. package/dashboard/dist/assets/{TopicsPage-CD_40_v-.js.map → TopicsPage-KJ7_LAvo.js.map} +1 -1
  182. package/dashboard/dist/assets/{UserName-C__c8BN0.js → UserName-CMoFi9zb.js} +2 -2
  183. package/dashboard/dist/assets/{UserName-C__c8BN0.js.map → UserName-CMoFi9zb.js.map} +1 -1
  184. package/dashboard/dist/assets/{WorkflowExecutionPage-Bw6QdtUO.js → WorkflowExecutionPage-Cymfx-Bp.js} +2 -2
  185. package/dashboard/dist/assets/{WorkflowExecutionPage-Bw6QdtUO.js.map → WorkflowExecutionPage-Cymfx-Bp.js.map} +1 -1
  186. package/dashboard/dist/assets/{WorkflowPill-CS-IsR0U.js → WorkflowPill-B66_AnaY.js} +2 -2
  187. package/dashboard/dist/assets/{WorkflowPill-CS-IsR0U.js.map → WorkflowPill-B66_AnaY.js.map} +1 -1
  188. package/dashboard/dist/assets/{WorkflowsDashboard-BgI-iOP6.js → WorkflowsDashboard-CqLN8spd.js} +2 -2
  189. package/dashboard/dist/assets/{WorkflowsDashboard-BgI-iOP6.js.map → WorkflowsDashboard-CqLN8spd.js.map} +1 -1
  190. package/dashboard/dist/assets/{WorkflowsOverview-BRc0QbA7.js → WorkflowsOverview-0Rc2WT6q.js} +2 -2
  191. package/dashboard/dist/assets/{WorkflowsOverview-BRc0QbA7.js.map → WorkflowsOverview-0Rc2WT6q.js.map} +1 -1
  192. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DdzspNQR.js → YamlWorkflowDetailPage-CARQ7_LN.js} +2 -2
  193. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DdzspNQR.js.map → YamlWorkflowDetailPage-CARQ7_LN.js.map} +1 -1
  194. package/dashboard/dist/assets/{YamlWorkflowsPage-Djw5ZbgQ.js → YamlWorkflowsPage-B-7sz1nf.js} +2 -2
  195. package/dashboard/dist/assets/{YamlWorkflowsPage-Djw5ZbgQ.js.map → YamlWorkflowsPage-B-7sz1nf.js.map} +1 -1
  196. package/dashboard/dist/assets/{agents-xstNkF6c.js → agents-zM43d5Yf.js} +2 -2
  197. package/dashboard/dist/assets/{agents-xstNkF6c.js.map → agents-zM43d5Yf.js.map} +1 -1
  198. package/dashboard/dist/assets/{bots-BztAI0s_.js → bots-D5BasOGz.js} +2 -2
  199. package/dashboard/dist/assets/{bots-BztAI0s_.js.map → bots-D5BasOGz.js.map} +1 -1
  200. package/dashboard/dist/assets/{capabilities-CEDwtxNL.js → capabilities-BT3PW9CZ.js} +2 -2
  201. package/dashboard/dist/assets/{capabilities-CEDwtxNL.js.map → capabilities-BT3PW9CZ.js.map} +1 -1
  202. package/dashboard/dist/assets/{controlplane-BlGEsRKe.js → controlplane-NpJr49rN.js} +2 -2
  203. package/dashboard/dist/assets/{controlplane-BlGEsRKe.js.map → controlplane-NpJr49rN.js.map} +1 -1
  204. package/dashboard/dist/assets/{escalation-iI4QKlP-.js → escalation-DrzQolPG.js} +2 -2
  205. package/dashboard/dist/assets/{escalation-iI4QKlP-.js.map → escalation-DrzQolPG.js.map} +1 -1
  206. package/dashboard/dist/assets/escalation-columns-QzwmtD60.js +2 -0
  207. package/dashboard/dist/assets/escalation-columns-QzwmtD60.js.map +1 -0
  208. package/dashboard/dist/assets/index-B5w4Gjyk.js +6 -0
  209. package/dashboard/dist/assets/index-B5w4Gjyk.js.map +1 -0
  210. package/dashboard/dist/assets/{index-CMviPjXQ.js → index-BCT4kqGR.js} +2 -2
  211. package/dashboard/dist/assets/{index-CMviPjXQ.js.map → index-BCT4kqGR.js.map} +1 -1
  212. package/dashboard/dist/assets/{index-B7VC0vjf.js → index-C69XvBfg.js} +2 -2
  213. package/dashboard/dist/assets/{index-B7VC0vjf.js.map → index-C69XvBfg.js.map} +1 -1
  214. package/dashboard/dist/assets/index-CHw4Fl0t.css +1 -0
  215. package/dashboard/dist/assets/{index-CUfgt3mV.js → index-C_NwHTMh.js} +2 -2
  216. package/dashboard/dist/assets/{index-CUfgt3mV.js.map → index-C_NwHTMh.js.map} +1 -1
  217. package/dashboard/dist/assets/{index-CAqEy2Db.js → index-Cq0trGqf.js} +2 -2
  218. package/dashboard/dist/assets/{index-CAqEy2Db.js.map → index-Cq0trGqf.js.map} +1 -1
  219. package/dashboard/dist/assets/{index-D1xVY4JR.js → index-DE0r9SEX.js} +22 -22
  220. package/dashboard/dist/assets/index-DE0r9SEX.js.map +1 -0
  221. package/dashboard/dist/assets/{index-DjJ6VCuc.js → index-DH_CiD0W.js} +2 -2
  222. package/dashboard/dist/assets/{index-DjJ6VCuc.js.map → index-DH_CiD0W.js.map} +1 -1
  223. package/dashboard/dist/assets/{index-DzVYjoex.js → index-K23FdS-T.js} +2 -2
  224. package/dashboard/dist/assets/{index-DzVYjoex.js.map → index-K23FdS-T.js.map} +1 -1
  225. package/dashboard/dist/assets/{index-FuhiJuHL.js → index-f6K1UNrn.js} +2 -2
  226. package/dashboard/dist/assets/{index-FuhiJuHL.js.map → index-f6K1UNrn.js.map} +1 -1
  227. package/dashboard/dist/assets/{index-B5qsp8lb.js → index-qr1fndqM.js} +2 -2
  228. package/dashboard/dist/assets/{index-B5qsp8lb.js.map → index-qr1fndqM.js.map} +1 -1
  229. package/dashboard/dist/assets/index-vKTvJ_li.js +2 -0
  230. package/dashboard/dist/assets/index-vKTvJ_li.js.map +1 -0
  231. package/dashboard/dist/assets/{index-BUmnJ4eT.js → index-y-3ODtEE.js} +2 -2
  232. package/dashboard/dist/assets/{index-BUmnJ4eT.js.map → index-y-3ODtEE.js.map} +1 -1
  233. package/dashboard/dist/assets/{index-Dsi5o7dx.js → index-y56Ds-LB.js} +2 -2
  234. package/dashboard/dist/assets/{index-Dsi5o7dx.js.map → index-y56Ds-LB.js.map} +1 -1
  235. package/dashboard/dist/assets/{knowledge-BKg0cr39.js → knowledge-DSSATOTX.js} +2 -2
  236. package/dashboard/dist/assets/{knowledge-BKg0cr39.js.map → knowledge-DSSATOTX.js.map} +1 -1
  237. package/dashboard/dist/assets/{mcp-CVdHpKlz.js → mcp-CfJFVo5k.js} +2 -2
  238. package/dashboard/dist/assets/{mcp-CVdHpKlz.js.map → mcp-CfJFVo5k.js.map} +1 -1
  239. package/dashboard/dist/assets/{mcp-query-CYxZcbwQ.js → mcp-query-DKVx2Q-X.js} +2 -2
  240. package/dashboard/dist/assets/{mcp-query-CYxZcbwQ.js.map → mcp-query-DKVx2Q-X.js.map} +1 -1
  241. package/dashboard/dist/assets/{pipelines-BOEv7P6O.js → pipelines-BSWzE-6M.js} +2 -2
  242. package/dashboard/dist/assets/{pipelines-BOEv7P6O.js.map → pipelines-BSWzE-6M.js.map} +1 -1
  243. package/dashboard/dist/assets/{roles-c5cV2QYQ.js → roles-t9HzhI5N.js} +2 -2
  244. package/dashboard/dist/assets/{roles-c5cV2QYQ.js.map → roles-t9HzhI5N.js.map} +1 -1
  245. package/dashboard/dist/assets/{tasks-JV3BjeEF.js → tasks-B7rUihoH.js} +2 -2
  246. package/dashboard/dist/assets/{tasks-JV3BjeEF.js.map → tasks-B7rUihoH.js.map} +1 -1
  247. package/dashboard/dist/assets/{topics-BDaPnhnQ.js → topics-PC2fEiyD.js} +2 -2
  248. package/dashboard/dist/assets/{topics-BDaPnhnQ.js.map → topics-PC2fEiyD.js.map} +1 -1
  249. package/dashboard/dist/assets/{useEventHooks-a7Dnh8G4.js → useEventHooks-DK9mw1cI.js} +2 -2
  250. package/dashboard/dist/assets/{useEventHooks-a7Dnh8G4.js.map → useEventHooks-DK9mw1cI.js.map} +1 -1
  251. package/dashboard/dist/assets/{useNamespace-BSwEV0e9.js → useNamespace-3IjtUtAw.js} +2 -2
  252. package/dashboard/dist/assets/{useNamespace-BSwEV0e9.js.map → useNamespace-3IjtUtAw.js.map} +1 -1
  253. package/dashboard/dist/assets/{useYamlActivityEvents-BuqvL-cn.js → useYamlActivityEvents-uxukp3gO.js} +2 -2
  254. package/dashboard/dist/assets/{useYamlActivityEvents-BuqvL-cn.js.map → useYamlActivityEvents-uxukp3gO.js.map} +1 -1
  255. package/dashboard/dist/assets/users-CC4B5Fm5.js +2 -0
  256. package/dashboard/dist/assets/users-CC4B5Fm5.js.map +1 -0
  257. package/dashboard/dist/assets/{vendor-icons-D5ycKPA2.js → vendor-icons-Dy5Jpknf.js} +2 -2
  258. package/dashboard/dist/assets/{vendor-icons-D5ycKPA2.js.map → vendor-icons-Dy5Jpknf.js.map} +1 -1
  259. package/dashboard/dist/assets/{workflows-DF-9Voyi.js → workflows-S7MF1nNk.js} +2 -2
  260. package/dashboard/dist/assets/{workflows-DF-9Voyi.js.map → workflows-S7MF1nNk.js.map} +1 -1
  261. package/dashboard/dist/assets/{yaml-workflows-2roKaSSK.js → yaml-workflows-WfdaMHNn.js} +2 -2
  262. package/dashboard/dist/assets/{yaml-workflows-2roKaSSK.js.map → yaml-workflows-WfdaMHNn.js.map} +1 -1
  263. package/dashboard/dist/index.html +3 -3
  264. package/docs/api/http/escalations.md +131 -10
  265. package/docs/api/http/roles.md +44 -9
  266. package/docs/api/http/users.md +15 -6
  267. package/docs/api/mcp/admin.md +8 -2
  268. package/docs/api/mcp/human-queue.md +34 -2
  269. package/docs/api/sdk/escalations.md +155 -2
  270. package/docs/api/sdk/roles.md +11 -0
  271. package/docs/api/sdk/users.md +26 -1
  272. package/docs/auth.md +1 -1
  273. package/docs/dashboard.md +5 -4
  274. package/docs/data.md +9 -2
  275. package/docs/faceted-routing.md +221 -0
  276. package/docs/hitl-guide.md +50 -0
  277. package/docs/iam.md +31 -0
  278. package/docs/mcp.md +8 -0
  279. package/package.json +2 -2
  280. package/dashboard/dist/assets/AdminDashboard-LTiYLuzq.js +0 -2
  281. package/dashboard/dist/assets/AvailableEscalationsPage-BEylRqjP.js +0 -2
  282. package/dashboard/dist/assets/AvailableEscalationsPage-BEylRqjP.js.map +0 -1
  283. package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js +0 -2
  284. package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js.map +0 -1
  285. package/dashboard/dist/assets/OperatorDashboard-g9o7I925.js +0 -2
  286. package/dashboard/dist/assets/PageHeader-Dk2Pe44Q.js +0 -2
  287. package/dashboard/dist/assets/PageHeader-Dk2Pe44Q.js.map +0 -1
  288. package/dashboard/dist/assets/PriorityBadge-DfQY9St9.js +0 -2
  289. package/dashboard/dist/assets/PriorityBadge-DfQY9St9.js.map +0 -1
  290. package/dashboard/dist/assets/RolePill-Du-aNnjR.js +0 -2
  291. package/dashboard/dist/assets/RolePill-Du-aNnjR.js.map +0 -1
  292. package/dashboard/dist/assets/RowActions-Dg-Fsm5O.js +0 -2
  293. package/dashboard/dist/assets/RowActions-Dg-Fsm5O.js.map +0 -1
  294. package/dashboard/dist/assets/escalation-columns-nVYVt7Gl.js +0 -2
  295. package/dashboard/dist/assets/escalation-columns-nVYVt7Gl.js.map +0 -1
  296. package/dashboard/dist/assets/index-CP0JEQhZ.js +0 -6
  297. package/dashboard/dist/assets/index-CP0JEQhZ.js.map +0 -1
  298. package/dashboard/dist/assets/index-ClvYSNZd.css +0 -1
  299. package/dashboard/dist/assets/index-D1xVY4JR.js.map +0 -1
  300. package/dashboard/dist/assets/index-DPLy-jwO.js +0 -2
  301. package/dashboard/dist/assets/index-DPLy-jwO.js.map +0 -1
  302. package/dashboard/dist/assets/users-Pn85K_3C.js +0 -2
  303. package/dashboard/dist/assets/users-Pn85K_3C.js.map +0 -1
@@ -41,13 +41,14 @@ const router = (0, express_1.Router)();
41
41
  /**
42
42
  * GET /api/users
43
43
  * List users with optional filters.
44
- * Query: ?role=reviewer&roleType=admin&status=active&limit=50&offset=0
44
+ * Query: ?role=reviewer&roleType=admin&status=active&search=ada&limit=50&offset=0
45
45
  */
46
46
  router.get('/', async (req, res) => {
47
47
  const result = await api.listUsers({
48
48
  role: req.query.role,
49
49
  roleType: req.query.roleType,
50
50
  status: req.query.status,
51
+ search: req.query.search,
51
52
  limit: req.query.limit ? parseInt(req.query.limit, 10) : undefined,
52
53
  offset: req.query.offset ? parseInt(req.query.offset, 10) : undefined,
53
54
  });
@@ -99,15 +100,18 @@ router.get('/:id/roles', async (req, res) => {
99
100
  /**
100
101
  * POST /api/users/:id/roles
101
102
  * Add a role to a user.
102
- * Body: { role, type } — type must be superadmin, admin, or member
103
+ * Body: { role, type, read_scope?, write_scope? } — type must be superadmin,
104
+ * admin, or member. read_scope (self|all) and write_scope (none|self|all) refine
105
+ * a member's work surface; they default to all/all and are ignored for admin/superadmin.
103
106
  *
104
- * Scoping rules:
107
+ * Scoping rules (management tier):
105
108
  * - superadmin: can assign any role/type
106
109
  * - engineer: can assign up to admin type, but never superadmin type
107
110
  * - role/admin (non-builder): can only assign member/admin for roles they hold
111
+ * A caller who may assign a role may set any work-surface scope on it.
108
112
  */
109
113
  router.post('/:id/roles', auth_1.requireAdmin, async (req, res) => {
110
- const { role, type } = req.body || {};
114
+ const { role, type, read_scope, write_scope } = req.body || {};
111
115
  const userId = req.auth.userId;
112
116
  // Superadmin bypasses all scoping
113
117
  const { isSuperAdmin } = await Promise.resolve().then(() => __importStar(require('../services/user/rbac')));
@@ -129,7 +133,7 @@ router.post('/:id/roles', auth_1.requireAdmin, async (req, res) => {
129
133
  }
130
134
  }
131
135
  }
132
- const result = await api.addUserRole({ id: req.params.id, role, type });
136
+ const result = await api.addUserRole({ id: req.params.id, role, type, read_scope, write_scope });
133
137
  res.status(result.status).json(result.data ?? { error: result.error });
134
138
  });
135
139
  /**
@@ -82,6 +82,12 @@ export declare function createClient(options?: LTClientOptions): {
82
82
  envelope?: string;
83
83
  metadata?: Record<string, any>;
84
84
  escalation_payload?: string;
85
+ origin_id?: string;
86
+ parent_id?: string;
87
+ task_id?: string;
88
+ workflow_id?: string;
89
+ task_queue?: string;
90
+ workflow_type?: string;
85
91
  }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
86
92
  list: (input: {
87
93
  status?: string;
@@ -96,7 +102,7 @@ export declare function createClient(options?: LTClientOptions): {
96
102
  sort_by?: string;
97
103
  order?: string;
98
104
  search?: string;
99
- }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
105
+ } & import("../api/escalations/list").FacetInput, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
100
106
  listAvailable: (input: {
101
107
  role?: string;
102
108
  type?: string;
@@ -107,8 +113,9 @@ export declare function createClient(options?: LTClientOptions): {
107
113
  sort_by?: string;
108
114
  order?: string;
109
115
  search?: string;
110
- }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
116
+ } & import("../api/escalations/list").FacetInput, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
111
117
  listTypes: typeof escalationsApi.listDistinctTypes;
118
+ listFacetKeys: (input: unknown, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
112
119
  getStats: (input: {
113
120
  period?: string;
114
121
  }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
@@ -131,6 +138,17 @@ export declare function createClient(options?: LTClientOptions): {
131
138
  resolve: (input: {
132
139
  id: string;
133
140
  resolverPayload: Record<string, any>;
141
+ metadata?: Record<string, any>;
142
+ }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
143
+ resolveBySignalKey: (input: {
144
+ signalKey: string;
145
+ resolverPayload: Record<string, any>;
146
+ metadata?: Record<string, any>;
147
+ }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
148
+ resolveByIds: (input: {
149
+ ids: string[];
150
+ resolverPayload: Record<string, any>;
151
+ metadata?: Record<string, any>;
134
152
  }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
135
153
  releaseExpired: typeof escalationsApi.releaseExpiredClaims;
136
154
  updatePriority: (input: {
@@ -176,6 +194,19 @@ export declare function createClient(options?: LTClientOptions): {
176
194
  assignee?: string;
177
195
  metadata?: Record<string, any>;
178
196
  }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
197
+ searchByFacets: (input: import("..").FacetQuery, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
198
+ claimGroups: (input: {
199
+ query: import("..").FacetQuery;
200
+ limit?: number;
201
+ durationMinutes?: number;
202
+ sizeFacet?: string;
203
+ }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
204
+ claimByFacets: (input: {
205
+ query: import("..").FacetQuery;
206
+ limit?: number;
207
+ durationMinutes?: number;
208
+ allOrNone?: boolean;
209
+ }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
179
210
  };
180
211
  workflows: {
181
212
  invoke: (input: {
@@ -117,6 +117,7 @@ function createClient(options = {}) {
117
117
  list: bindAuth(escalationsApi.listEscalations, auth),
118
118
  listAvailable: bindAuth(escalationsApi.listAvailableEscalations, auth),
119
119
  listTypes: escalationsApi.listDistinctTypes,
120
+ listFacetKeys: bindAuth(escalationsApi.listFacetKeys, auth),
120
121
  getStats: bindAuth(escalationsApi.getEscalationStats, auth),
121
122
  get: bindAuth(escalationsApi.getEscalation, auth),
122
123
  getByWorkflowId: escalationsApi.getEscalationsByWorkflowId,
@@ -124,6 +125,8 @@ function createClient(options = {}) {
124
125
  claim: bindAuth(escalationsApi.claimEscalation, auth),
125
126
  release: bindAuth(escalationsApi.releaseEscalation, auth),
126
127
  resolve: bindAuth(escalationsApi.resolveEscalation, auth),
128
+ resolveBySignalKey: bindAuth(escalationsApi.resolveBySignalKey, auth),
129
+ resolveByIds: bindAuth(escalationsApi.resolveByIds, auth),
127
130
  releaseExpired: escalationsApi.releaseExpiredClaims,
128
131
  updatePriority: bindAuth(escalationsApi.updatePriority, auth),
129
132
  bulkClaim: bindAuth(escalationsApi.bulkClaim, auth),
@@ -133,6 +136,10 @@ function createClient(options = {}) {
133
136
  findByMetadata: bindAuth(escalationsApi.findByMetadata, auth),
134
137
  claimByMetadata: bindAuth(escalationsApi.claimByMetadata, auth),
135
138
  resolveByMetadata: bindAuth(escalationsApi.resolveByMetadata, auth),
139
+ // Faceted-routing surface (pond search + batch claim), RBAC-scoped to the caller's role.
140
+ searchByFacets: bindAuth(escalationsApi.searchByFacets, auth),
141
+ claimGroups: bindAuth(escalationsApi.claimGroups, auth),
142
+ claimByFacets: bindAuth(escalationsApi.claimByFacets, auth),
136
143
  },
137
144
  // ── Workflows ──────────────────────────────────────────────────────────
138
145
  workflows: {
@@ -12,8 +12,29 @@ export declare function claimEscalation(id: string, userId: string, durationMinu
12
12
  * orchestrator (api/escalations/resolve.ts); service-created rows have no
13
13
  * signal_key, so this never delivers a signal itself. Returns null when the
14
14
  * row is missing or already terminal.
15
+ *
16
+ * `metadata` (optional) is merged — not replaced — into the resolved row's
17
+ * GIN-indexed `metadata` in the same atomic UPDATE, and only on the winning
18
+ * resolve. It records "what actually happened" (outcome, duration, measured
19
+ * results) into the `@>`-queryable surface alongside the creation metadata
20
+ * ("what was intended"). Distinct from `resolverPayload`, which is delivered to
21
+ * the waiting workflow as `condition()`'s return value and is not GIN-indexed.
15
22
  */
16
- export declare function resolveEscalation(id: string, resolverPayload: Record<string, any>): Promise<LTEscalationRecord | null>;
23
+ export declare function resolveEscalation(id: string, resolverPayload: Record<string, any>, metadata?: Record<string, any>): Promise<LTEscalationRecord | null>;
24
+ /**
25
+ * Resolve a SET of escalations by id in ONE guarded statement
26
+ * (`UPDATE … WHERE id = ANY($ids) AND status='pending'`). Returns the rows that
27
+ * won the resolve (already-terminal ids are excluded by the guard), and publishes
28
+ * a resolved event per winner.
29
+ *
30
+ * Use this over a per-row `resolveEscalation` loop when every row takes the SAME
31
+ * resolverPayload and the rows are woken collectively (or are bookkeeping rows with
32
+ * no signal_key). NOTE: unlike the single `resolve`, this does NOT deliver per-row
33
+ * signals — `resolveMany` is UPDATE-only. Do not use it for signal_key adverts whose
34
+ * resolution must wake a parked workflow; those require the per-row `resolveEscalation`
35
+ * (which delivers the signal).
36
+ */
37
+ export declare function resolveEscalationsByIds(ids: string[], resolverPayload: Record<string, any>, metadata?: Record<string, any>): Promise<LTEscalationRecord[]>;
17
38
  /**
18
39
  * Look up an efficient (atomic) escalation by its `signal_key` — the signal id
19
40
  * passed to `conditionLT(signalId, config)` / `condition(signalId, config)`.
@@ -30,8 +51,11 @@ export declare function getEscalationBySignalKey(signalKey: string): Promise<LTE
30
51
  * is delegated to `resolveEscalation`, whose `client.resolve` uses FOR UPDATE +
31
52
  * `WHERE status = 'pending'` so exactly one concurrent caller commits. No status
32
53
  * pre-check (that would be a TOCTOU window) — the atomic resolve is the arbiter.
54
+ *
55
+ * `metadata` (optional) records the resolution outcome into the row's
56
+ * GIN-indexed metadata atomically — see {@link resolveEscalation}.
33
57
  */
34
- export declare function resolveEscalationBySignalKey(signalKey: string, resolverPayload: Record<string, any>): Promise<LTEscalationRecord | null>;
58
+ export declare function resolveEscalationBySignalKey(signalKey: string, resolverPayload: Record<string, any>, metadata?: Record<string, any>): Promise<LTEscalationRecord | null>;
35
59
  /**
36
60
  * Mark an escalation cancelled — used when the tied workflow has terminated and
37
61
  * can never receive the resolution signal. The escalation is removed from the
@@ -49,6 +73,16 @@ export declare function updateEscalationsPriority(ids: string[], priority: 1 | 2
49
73
  * Used for permission validation before bulk operations.
50
74
  */
51
75
  export declare function getEscalationRoles(ids: string[]): Promise<string[]>;
76
+ /**
77
+ * Fetch (id, role, assigned_to) for a set of escalation IDs — the minimal fields a
78
+ * write-scope gate needs to authorize a BULK action per item: write_all by role,
79
+ * write_self by ownership (assigned_to = caller). One query, no N+1.
80
+ */
81
+ export declare function getEscalationScopeRows(ids: string[]): Promise<{
82
+ id: string;
83
+ role: string;
84
+ assigned_to: string | null;
85
+ }[]>;
52
86
  /**
53
87
  * Release a single escalation claim back to the available pool.
54
88
  * Only the assigned user (or superadmin via route) may release.
@@ -79,7 +113,6 @@ export declare function getEscalationsByWorkflowId(workflowId: string): Promise<
79
113
  * This single UPDATE bypasses the SDK entirely and closes that gap.
80
114
  */
81
115
  export declare function cancelEscalationsByWorkflowId(workflowId: string): Promise<number>;
82
- export declare function updateEscalationMetadata(id: string, patch: Record<string, any>): Promise<LTEscalationRecord | null>;
83
116
  export declare function enrichEscalationRouting(id: string, metadataPatch: Record<string, any>, workflowFields: {
84
117
  workflowType?: string;
85
118
  workflowId?: string;
@@ -87,7 +120,20 @@ export declare function enrichEscalationRouting(id: string, metadataPatch: Recor
87
120
  taskId?: string;
88
121
  }): Promise<LTEscalationRecord | null>;
89
122
  export declare function getEscalationsByOriginId(originId: string): Promise<LTEscalationRecord[]>;
90
- export declare function findByMetadata(key: string, value: string, status?: string, limit?: number, offset?: number): Promise<{
123
+ /**
124
+ * Find escalations by a metadata key/value, RBAC-scoped IN SQL.
125
+ *
126
+ * `allowedRoles` flows into BOTH the page query and the count: `undefined` =
127
+ * global (no role filter); a `string[]` filters `role = ANY(...)` (an empty array
128
+ * matches nothing). Filtering in SQL — never client-side over a fetched page — is
129
+ * what keeps `total` correct and pagination intact (a client-side filter shrinks a
130
+ * page and reports the wrong total).
131
+ */
132
+ export declare function findByMetadata(key: string, value: string, status?: string, limit?: number, offset?: number, scope?: {
133
+ allRoles?: string[];
134
+ selfRoles?: string[];
135
+ meUserId?: string;
136
+ }): Promise<{
91
137
  escalations: LTEscalationRecord[];
92
138
  total: number;
93
139
  }>;
@@ -97,7 +143,11 @@ export declare function findByMetadata(key: string, value: string, status?: stri
97
143
  * match zero rows. Returns `{ escalation, isExtension, candidatesExist }` or
98
144
  * null when nothing was claimed.
99
145
  *
100
- * @param allowedRoles — roles the caller can claim (null = no filter / global)
146
+ * @param allowedRoles — write_all roles the caller can claim (null = global / no
147
+ * filter). The SDK applies a flat `role = ANY` filter and cannot express the
148
+ * write_self ownership predicate, so self-scope members are intentionally
149
+ * excluded from claim-by-metadata (their items are pre-claimed and resolved
150
+ * by id). Self-scope claim-by-metadata would require a HotMesh SDK predicate.
101
151
  */
102
152
  export declare function claimByMetadata(key: string, value: string, userId: string, durationMinutes?: number, metadata?: Record<string, any>, allowedRoles?: string[] | null): Promise<(ClaimResult & {
103
153
  candidatesExist: number;
@@ -131,4 +181,4 @@ export interface ResolveByMetadataResult {
131
181
  * business logic over the shared table, so it runs as one atomic statement on
132
182
  * `hmsh_escalations` rather than through the generic SDK resolve.
133
183
  */
134
- export declare function resolveByMetadataAtomic(key: string, value: string, userId: string, resolverPayload: Record<string, any>, metadata?: Record<string, any>, allowedRoles?: string[] | null): Promise<ResolveByMetadataResult>;
184
+ export declare function resolveByMetadataAtomic(key: string, value: string, userId: string, resolverPayload: Record<string, any>, metadata?: Record<string, any>, writeAllRoles?: string[] | null, writeSelfRoles?: string[] | null): Promise<ResolveByMetadataResult>;
@@ -3,11 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createEscalation = createEscalation;
4
4
  exports.claimEscalation = claimEscalation;
5
5
  exports.resolveEscalation = resolveEscalation;
6
+ exports.resolveEscalationsByIds = resolveEscalationsByIds;
6
7
  exports.getEscalationBySignalKey = getEscalationBySignalKey;
7
8
  exports.resolveEscalationBySignalKey = resolveEscalationBySignalKey;
8
9
  exports.cancelEscalation = cancelEscalation;
9
10
  exports.updateEscalationsPriority = updateEscalationsPriority;
10
11
  exports.getEscalationRoles = getEscalationRoles;
12
+ exports.getEscalationScopeRows = getEscalationScopeRows;
11
13
  exports.releaseEscalation = releaseEscalation;
12
14
  exports.releaseExpiredClaims = releaseExpiredClaims;
13
15
  exports.escalateToRole = escalateToRole;
@@ -15,7 +17,6 @@ exports.getEscalation = getEscalation;
15
17
  exports.getEscalationsByTaskId = getEscalationsByTaskId;
16
18
  exports.getEscalationsByWorkflowId = getEscalationsByWorkflowId;
17
19
  exports.cancelEscalationsByWorkflowId = cancelEscalationsByWorkflowId;
18
- exports.updateEscalationMetadata = updateEscalationMetadata;
19
20
  exports.enrichEscalationRouting = enrichEscalationRouting;
20
21
  exports.getEscalationsByOriginId = getEscalationsByOriginId;
21
22
  exports.findByMetadata = findByMetadata;
@@ -24,6 +25,7 @@ exports.resolveByMetadataAtomic = resolveByMetadataAtomic;
24
25
  const db_1 = require("../../lib/db");
25
26
  const publish_1 = require("../../lib/events/publish");
26
27
  const client_1 = require("./client");
28
+ const queries_1 = require("./queries");
27
29
  const map_1 = require("./map");
28
30
  const sql_1 = require("./sql");
29
31
  // All escalation state lives in `public.hmsh_escalations` (HotMesh 0.22.3),
@@ -94,10 +96,17 @@ async function claimEscalation(id, userId, durationMinutes = 30) {
94
96
  * orchestrator (api/escalations/resolve.ts); service-created rows have no
95
97
  * signal_key, so this never delivers a signal itself. Returns null when the
96
98
  * row is missing or already terminal.
99
+ *
100
+ * `metadata` (optional) is merged — not replaced — into the resolved row's
101
+ * GIN-indexed `metadata` in the same atomic UPDATE, and only on the winning
102
+ * resolve. It records "what actually happened" (outcome, duration, measured
103
+ * results) into the `@>`-queryable surface alongside the creation metadata
104
+ * ("what was intended"). Distinct from `resolverPayload`, which is delivered to
105
+ * the waiting workflow as `condition()`'s return value and is not GIN-indexed.
97
106
  */
98
- async function resolveEscalation(id, resolverPayload) {
107
+ async function resolveEscalation(id, resolverPayload, metadata) {
99
108
  const client = await (0, client_1.escalations)();
100
- const result = await client.resolve({ id, resolverPayload });
109
+ const result = await client.resolve({ id, resolverPayload, metadata });
101
110
  if (!result.ok)
102
111
  return null;
103
112
  const escalation = (0, map_1.toEscalationRecord)(result.entry);
@@ -113,6 +122,39 @@ async function resolveEscalation(id, resolverPayload) {
113
122
  });
114
123
  return escalation;
115
124
  }
125
+ /**
126
+ * Resolve a SET of escalations by id in ONE guarded statement
127
+ * (`UPDATE … WHERE id = ANY($ids) AND status='pending'`). Returns the rows that
128
+ * won the resolve (already-terminal ids are excluded by the guard), and publishes
129
+ * a resolved event per winner.
130
+ *
131
+ * Use this over a per-row `resolveEscalation` loop when every row takes the SAME
132
+ * resolverPayload and the rows are woken collectively (or are bookkeeping rows with
133
+ * no signal_key). NOTE: unlike the single `resolve`, this does NOT deliver per-row
134
+ * signals — `resolveMany` is UPDATE-only. Do not use it for signal_key adverts whose
135
+ * resolution must wake a parked workflow; those require the per-row `resolveEscalation`
136
+ * (which delivers the signal).
137
+ */
138
+ async function resolveEscalationsByIds(ids, resolverPayload, metadata) {
139
+ if (ids.length === 0)
140
+ return [];
141
+ const client = await (0, client_1.escalations)();
142
+ const rows = await client.resolveMany({ ids, resolverPayload, metadata });
143
+ const records = (0, map_1.toEscalationRecords)(rows);
144
+ for (const escalation of records) {
145
+ (0, publish_1.publishEscalationEvent)({
146
+ type: 'escalation.resolved',
147
+ source: 'service',
148
+ workflowId: escalation.workflow_id || '',
149
+ workflowName: escalation.workflow_type || '',
150
+ taskQueue: escalation.task_queue || '',
151
+ escalationId: escalation.id,
152
+ status: 'resolved',
153
+ data: {},
154
+ });
155
+ }
156
+ return records;
157
+ }
116
158
  /**
117
159
  * Look up an efficient (atomic) escalation by its `signal_key` — the signal id
118
160
  * passed to `conditionLT(signalId, config)` / `condition(signalId, config)`.
@@ -133,12 +175,15 @@ async function getEscalationBySignalKey(signalKey) {
133
175
  * is delegated to `resolveEscalation`, whose `client.resolve` uses FOR UPDATE +
134
176
  * `WHERE status = 'pending'` so exactly one concurrent caller commits. No status
135
177
  * pre-check (that would be a TOCTOU window) — the atomic resolve is the arbiter.
178
+ *
179
+ * `metadata` (optional) records the resolution outcome into the row's
180
+ * GIN-indexed metadata atomically — see {@link resolveEscalation}.
136
181
  */
137
- async function resolveEscalationBySignalKey(signalKey, resolverPayload) {
182
+ async function resolveEscalationBySignalKey(signalKey, resolverPayload, metadata) {
138
183
  const escalation = await getEscalationBySignalKey(signalKey);
139
184
  if (!escalation)
140
185
  return null;
141
- return resolveEscalation(escalation.id, resolverPayload);
186
+ return resolveEscalation(escalation.id, resolverPayload, metadata);
142
187
  }
143
188
  /**
144
189
  * Mark an escalation cancelled — used when the tied workflow has terminated and
@@ -185,6 +230,22 @@ async function getEscalationRoles(ids) {
185
230
  const rows = await client.list({ ids, limit: LOOKUP_LIMIT });
186
231
  return [...new Set(rows.map(r => r.role).filter((r) => !!r))];
187
232
  }
233
+ /**
234
+ * Fetch (id, role, assigned_to) for a set of escalation IDs — the minimal fields a
235
+ * write-scope gate needs to authorize a BULK action per item: write_all by role,
236
+ * write_self by ownership (assigned_to = caller). One query, no N+1.
237
+ */
238
+ async function getEscalationScopeRows(ids) {
239
+ if (ids.length === 0)
240
+ return [];
241
+ const client = await (0, client_1.escalations)();
242
+ const rows = await client.list({ ids, limit: LOOKUP_LIMIT });
243
+ return rows.map((r) => ({
244
+ id: r.id,
245
+ role: r.role,
246
+ assigned_to: r.assigned_to ?? null,
247
+ }));
248
+ }
188
249
  /**
189
250
  * Release a single escalation claim back to the available pool.
190
251
  * Only the assigned user (or superadmin via route) may release.
@@ -284,11 +345,6 @@ async function cancelEscalationsByWorkflowId(workflowId) {
284
345
  }
285
346
  return rows.length;
286
347
  }
287
- async function updateEscalationMetadata(id, patch) {
288
- const client = await (0, client_1.escalations)();
289
- const entry = await client.update({ id, metadata: patch });
290
- return entry ? (0, map_1.toEscalationRecord)(entry) : null;
291
- }
292
348
  async function enrichEscalationRouting(id, metadataPatch, workflowFields) {
293
349
  const client = await (0, client_1.escalations)();
294
350
  const entry = await client.update({
@@ -312,23 +368,31 @@ async function getEscalationsByOriginId(originId) {
312
368
  return (0, map_1.toEscalationRecords)(rows);
313
369
  }
314
370
  // --- Metadata candidate key lookups -----------------------------------------
315
- async function findByMetadata(key, value, status, limit = 50, offset = 0) {
316
- const client = await (0, client_1.escalations)();
317
- const metadata = { [key]: value };
318
- const [rows, total] = await Promise.all([
319
- client.list({
320
- metadata,
321
- status,
322
- orderBy: [
323
- { column: 'priority', direction: 'asc' },
324
- { column: 'created_at', direction: 'asc' },
325
- ],
326
- limit,
327
- offset,
328
- }),
329
- client.count({ metadata, status }),
330
- ]);
331
- return { escalations: (0, map_1.toEscalationRecords)(rows), total };
371
+ /**
372
+ * Find escalations by a metadata key/value, RBAC-scoped IN SQL.
373
+ *
374
+ * `allowedRoles` flows into BOTH the page query and the count: `undefined` =
375
+ * global (no role filter); a `string[]` filters `role = ANY(...)` (an empty array
376
+ * matches nothing). Filtering in SQL — never client-side over a fetched page — is
377
+ * what keeps `total` correct and pagination intact (a client-side filter shrinks a
378
+ * page and reports the wrong total).
379
+ */
380
+ async function findByMetadata(key, value, status, limit = 50, offset = 0, scope) {
381
+ // Route through the scoped list query so BOTH the role-scope filter
382
+ // (role ∈ allRoles OR (role ∈ selfRoles AND assigned_to = me)) AND the count
383
+ // run in SQL — never a client-side filter over a fetched page. allRoles travel
384
+ // as the flat `roles` filter; selfRoles + meUserId engage the raw-SQL scoped path.
385
+ return (0, queries_1.listEscalations)({
386
+ metadata: { [key]: value },
387
+ status: status,
388
+ visibleRoles: scope?.allRoles,
389
+ selfRoles: scope?.selfRoles,
390
+ meUserId: scope?.meUserId,
391
+ sort_by: 'priority',
392
+ order: 'asc',
393
+ limit,
394
+ offset,
395
+ });
332
396
  }
333
397
  /**
334
398
  * Atomic claim by metadata with inline RBAC and optional metadata merge.
@@ -336,7 +400,11 @@ async function findByMetadata(key, value, status, limit = 50, offset = 0) {
336
400
  * match zero rows. Returns `{ escalation, isExtension, candidatesExist }` or
337
401
  * null when nothing was claimed.
338
402
  *
339
- * @param allowedRoles — roles the caller can claim (null = no filter / global)
403
+ * @param allowedRoles — write_all roles the caller can claim (null = global / no
404
+ * filter). The SDK applies a flat `role = ANY` filter and cannot express the
405
+ * write_self ownership predicate, so self-scope members are intentionally
406
+ * excluded from claim-by-metadata (their items are pre-claimed and resolved
407
+ * by id). Self-scope claim-by-metadata would require a HotMesh SDK predicate.
340
408
  */
341
409
  async function claimByMetadata(key, value, userId, durationMinutes = 30, metadata, allowedRoles) {
342
410
  const client = await (0, client_1.escalations)();
@@ -377,14 +445,17 @@ async function claimByMetadata(key, value, userId, durationMinutes = 30, metadat
377
445
  * business logic over the shared table, so it runs as one atomic statement on
378
446
  * `hmsh_escalations` rather than through the generic SDK resolve.
379
447
  */
380
- async function resolveByMetadataAtomic(key, value, userId, resolverPayload, metadata, allowedRoles) {
448
+ async function resolveByMetadataAtomic(key, value, userId, resolverPayload, metadata, writeAllRoles, writeSelfRoles) {
381
449
  await (0, client_1.ensureEscalationCompatView)();
382
450
  const pool = (0, db_1.getPool)();
383
451
  const filter = JSON.stringify({ [key]: value });
384
452
  const payloadJson = JSON.stringify(resolverPayload);
385
453
  const metaPatch = metadata ? JSON.stringify(metadata) : null;
386
- const roles = allowedRoles ?? null;
387
- const { rows } = await pool.query(sql_1.RESOLVE_BY_METADATA_ATOMIC, [filter, userId, payloadJson, metaPatch, roles]);
454
+ // null write_all roles = global (no filter). A scoped caller passes its arrays
455
+ // (possibly empty); the write_self branch matches only rows assigned to userId.
456
+ const allRoles = writeAllRoles ?? null;
457
+ const selfRoles = writeSelfRoles ?? null;
458
+ const { rows } = await pool.query(sql_1.RESOLVE_BY_METADATA_ATOMIC, [filter, userId, payloadJson, metaPatch, allRoles, selfRoles]);
388
459
  if (rows.length === 0)
389
460
  return { outcome: 'not_found' };
390
461
  const row = rows[0];
@@ -0,0 +1,18 @@
1
+ import type { FacetOrder, FacetQuery } from '../../types';
2
+ /**
3
+ * Build the WHERE clause for a faceted query, pushing bound params into `params`.
4
+ * Every value is parameterized; only validated column names / metadata keys and
5
+ * a fixed operator set are interpolated. Returns `TRUE` when empty.
6
+ */
7
+ export declare function buildFacetWhere(q: FacetQuery, params: unknown[]): string;
8
+ /** Build an injection-safe ORDER BY mixing top-level columns and metadata facets. */
9
+ export declare function buildFacetOrder(orderBy?: FacetOrder[]): string;
10
+ /**
11
+ * Group rank for the all-or-nothing claim: each sort key aggregated across the
12
+ * group's members — `min` for ascending, `max` for descending — so a faceted
13
+ * order over members yields a stable order over orders. Defaults to the natural
14
+ * priority/FIFO order.
15
+ */
16
+ export declare function buildGroupOrder(orderBy?: FacetOrder[]): string;
17
+ /** Lazily-ensured index for the group aggregation (origin_id over the pending pool). */
18
+ export declare const ENSURE_ORIGIN_INDEX = "CREATE INDEX IF NOT EXISTS idx_hmsh_escalations_origin_pending\n ON public.hmsh_escalations (origin_id) WHERE status = 'pending'";
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // Faceted routing SQL — composable, injection-safe predicate + ORDER BY builders
4
+ // over the escalation queue. Reads go through the `public.lt_escalations` view
5
+ // (adds the computed `available` flag); atomic claims run on the shared
6
+ // `public.hmsh_escalations` table, the same raw-SQL-on-the-shared-table pattern
7
+ // as services/escalation/sql.ts. Metadata facets use the GIN index (`@>`).
8
+ // ---------------------------------------------------------------------------
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.ENSURE_ORIGIN_INDEX = void 0;
11
+ exports.buildFacetWhere = buildFacetWhere;
12
+ exports.buildFacetOrder = buildFacetOrder;
13
+ exports.buildGroupOrder = buildGroupOrder;
14
+ // Top-level columns a caller may sort/range on directly. Anything else must be a
15
+ // `metadata.<key>` facet. Keeps interpolation to a fixed, audited set.
16
+ const ORDER_COLUMNS = new Set(['priority', 'created_at', 'updated_at', 'status', 'role']);
17
+ const RANGE_OPS = new Set(['<', '<=', '>', '>=', '=']);
18
+ // Metadata keys are interpolated (as a JSON path), so they are strictly validated.
19
+ const FACET_KEY = /^[a-zA-Z0-9_]+$/;
20
+ /** A metadata extraction expression for a validated key (text or numeric). */
21
+ function metaExpr(key, numeric = false) {
22
+ return numeric ? `(metadata->>'${key}')::numeric` : `(metadata->>'${key}')`;
23
+ }
24
+ /**
25
+ * Build the WHERE clause for a faceted query, pushing bound params into `params`.
26
+ * Every value is parameterized; only validated column names / metadata keys and
27
+ * a fixed operator set are interpolated. Returns `TRUE` when empty.
28
+ */
29
+ function buildFacetWhere(q, params) {
30
+ const clauses = [];
31
+ if (q.role) {
32
+ params.push(q.role);
33
+ clauses.push(`role = $${params.length}`);
34
+ }
35
+ else if (q.roles && q.roles.length) {
36
+ params.push(q.roles);
37
+ clauses.push(`role = ANY($${params.length}::text[])`);
38
+ }
39
+ if (q.status) {
40
+ params.push(q.status);
41
+ clauses.push(`status = $${params.length}`);
42
+ }
43
+ if (q.facets && Object.keys(q.facets).length) {
44
+ params.push(JSON.stringify(q.facets));
45
+ clauses.push(`metadata @> $${params.length}::jsonb`); // GIN-served
46
+ }
47
+ if (q.block && q.block.length) {
48
+ params.push(q.block.map((b) => JSON.stringify(b)));
49
+ clauses.push(`NOT (metadata @> ANY($${params.length}::jsonb[]))`);
50
+ }
51
+ for (const r of q.range ?? []) {
52
+ if (!FACET_KEY.test(r.facet) || !RANGE_OPS.has(r.op))
53
+ continue;
54
+ params.push(r.value);
55
+ clauses.push(`${metaExpr(r.facet, true)} ${r.op} $${params.length}`);
56
+ }
57
+ for (const key of q.exists ?? []) {
58
+ if (!FACET_KEY.test(key))
59
+ continue;
60
+ clauses.push(`metadata ? '${key}'`);
61
+ }
62
+ if (q.available === true) {
63
+ clauses.push(`(assigned_to IS NULL OR assigned_until IS NULL OR assigned_until <= NOW())`);
64
+ }
65
+ else if (q.available === false) {
66
+ clauses.push(`(assigned_to IS NOT NULL AND assigned_until > NOW())`);
67
+ }
68
+ return clauses.length ? clauses.join('\n AND ') : 'TRUE';
69
+ }
70
+ const DEFAULT_ORDER = 'priority ASC, created_at ASC';
71
+ /** Build an injection-safe ORDER BY mixing top-level columns and metadata facets. */
72
+ function buildFacetOrder(orderBy) {
73
+ if (!orderBy || !orderBy.length)
74
+ return DEFAULT_ORDER;
75
+ const parts = [];
76
+ for (const o of orderBy) {
77
+ const dir = o.direction === 'desc' ? 'DESC' : 'ASC';
78
+ if (ORDER_COLUMNS.has(o.field)) {
79
+ parts.push(`${o.field} ${dir}`);
80
+ }
81
+ else if (o.field.startsWith('metadata.')) {
82
+ const key = o.field.slice('metadata.'.length);
83
+ if (!FACET_KEY.test(key))
84
+ continue;
85
+ parts.push(`${metaExpr(key, o.numeric)} ${dir} NULLS LAST`);
86
+ }
87
+ }
88
+ return parts.length ? parts.join(', ') : DEFAULT_ORDER;
89
+ }
90
+ /**
91
+ * Group rank for the all-or-nothing claim: each sort key aggregated across the
92
+ * group's members — `min` for ascending, `max` for descending — so a faceted
93
+ * order over members yields a stable order over orders. Defaults to the natural
94
+ * priority/FIFO order.
95
+ */
96
+ function buildGroupOrder(orderBy) {
97
+ if (!orderBy || !orderBy.length)
98
+ return 'min(priority) ASC, min(created_at) ASC';
99
+ const parts = [];
100
+ for (const o of orderBy) {
101
+ const desc = o.direction === 'desc';
102
+ const agg = desc ? 'max' : 'min';
103
+ const dir = desc ? 'DESC' : 'ASC';
104
+ if (ORDER_COLUMNS.has(o.field)) {
105
+ parts.push(`${agg}(${o.field}) ${dir}`);
106
+ }
107
+ else if (o.field.startsWith('metadata.')) {
108
+ const key = o.field.slice('metadata.'.length);
109
+ if (!FACET_KEY.test(key))
110
+ continue;
111
+ parts.push(`${agg}(${metaExpr(key, o.numeric)}) ${dir} NULLS LAST`);
112
+ }
113
+ }
114
+ return parts.length ? parts.join(', ') : 'min(priority) ASC, min(created_at) ASC';
115
+ }
116
+ /** Lazily-ensured index for the group aggregation (origin_id over the pending pool). */
117
+ exports.ENSURE_ORIGIN_INDEX = `CREATE INDEX IF NOT EXISTS idx_hmsh_escalations_origin_pending
118
+ ON public.hmsh_escalations (origin_id) WHERE status = 'pending'`;
@@ -0,0 +1,45 @@
1
+ import type { ClaimedGroup, FacetQuery, GroupSummary, LTEscalationRecord } from '../../types';
2
+ /** Ensure the compat view + the origin_id index exist before the first query. */
3
+ export declare function ensureFacetReady(): Promise<void>;
4
+ /** Item-level faceted search — filter/sort over top-level columns and metadata facets. */
5
+ export declare function searchByFacets(query: FacetQuery): Promise<{
6
+ escalations: LTEscalationRecord[];
7
+ total: number;
8
+ }>;
9
+ /**
10
+ * Order-level view of the pond — what a batched dispatcher reads to page by
11
+ * capacity. Each row is an order (origin) with its unit count, availability and
12
+ * completeness, so the app grabs only as many orders as it has free consumers.
13
+ */
14
+ export declare function searchGroups(query: FacetQuery, opts?: {
15
+ sizeFacet?: string;
16
+ limit?: number;
17
+ offset?: number;
18
+ }): Promise<GroupSummary[]>;
19
+ /** Aggregate count — capacity / in-flight soft-limit checks. */
20
+ export declare function countByFacets(query: FacetQuery): Promise<number>;
21
+ /**
22
+ * Pull a *page* of complete orders — the dispatcher primitive. Claims up to
23
+ * `limit` eligible orders (each all-or-nothing) in rank order, skipping any a
24
+ * competing dispatcher holds. Page `limit` = how many consumers/printers are
25
+ * free, so a dependent grabs exactly as much as it can distribute. `limit=1`
26
+ * claims a single order.
27
+ */
28
+ export declare function claimGroups(query: FacetQuery, consumer: string, opts?: {
29
+ limit?: number;
30
+ durationMinutes?: number;
31
+ sizeFacet?: string;
32
+ }): Promise<ClaimedGroup[]>;
33
+ /**
34
+ * Batch-claim *individual* escalations matching a facet query — the single-row
35
+ * sibling of `claimGroups`. Locks up to `limit` available rows in rank order with
36
+ * `FOR UPDATE SKIP LOCKED`, so concurrent claimers take disjoint sets without
37
+ * contention. With `allOrNone`, the claim commits only when the full `limit` was
38
+ * acquired (otherwise it rolls back and returns `[]`) — the all-or-none lock a
39
+ * dispatcher wants over a counted set it anticipated. Returns the claimed records.
40
+ */
41
+ export declare function claimByFacets(query: FacetQuery, consumer: string, opts?: {
42
+ limit?: number;
43
+ durationMinutes?: number;
44
+ allOrNone?: boolean;
45
+ }): Promise<LTEscalationRecord[]>;