@hotmeshio/long-tail 0.5.12 → 0.6.2

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 (337) 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-C5gfVEeL.js +2 -0
  87. package/dashboard/dist/assets/{AdminDashboard-LTiYLuzq.js.map → AdminDashboard-C5gfVEeL.js.map} +1 -1
  88. package/dashboard/dist/assets/{AgentConfigPage-nlOudfpZ.js → AgentConfigPage-CWEnuiGN.js} +6 -6
  89. package/dashboard/dist/assets/{AgentConfigPage-nlOudfpZ.js.map → AgentConfigPage-CWEnuiGN.js.map} +1 -1
  90. package/dashboard/dist/assets/{AgentDetailPage-BSUnDvGp.js → AgentDetailPage-B6IE27iK.js} +3 -3
  91. package/dashboard/dist/assets/AgentDetailPage-B6IE27iK.js.map +1 -0
  92. package/dashboard/dist/assets/AgentsPage-Blhh2RsK.js +2 -0
  93. package/dashboard/dist/assets/{AgentsPage-DpS0cCYe.js.map → AgentsPage-Blhh2RsK.js.map} +1 -1
  94. package/dashboard/dist/assets/AvailableEscalationsPage-ClwAle76.js +2 -0
  95. package/dashboard/dist/assets/AvailableEscalationsPage-ClwAle76.js.map +1 -0
  96. package/dashboard/dist/assets/{BotPicker-BhKmq7M7.js → BotPicker-DSozotGk.js} +2 -2
  97. package/dashboard/dist/assets/{BotPicker-BhKmq7M7.js.map → BotPicker-DSozotGk.js.map} +1 -1
  98. package/dashboard/dist/assets/CapabilitiesPage-CrS_DTA1.js +2 -0
  99. package/dashboard/dist/assets/CapabilitiesPage-CrS_DTA1.js.map +1 -0
  100. package/dashboard/dist/assets/{CollapsibleSection-D7SVwNVk.js → CollapsibleSection-1i6bOQ1L.js} +2 -2
  101. package/dashboard/dist/assets/{CollapsibleSection-D7SVwNVk.js.map → CollapsibleSection-1i6bOQ1L.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-BxJio06E.js} +2 -2
  105. package/dashboard/dist/assets/{CredentialsPage-fat5Z2U9.js.map → CredentialsPage-BxJio06E.js.map} +1 -1
  106. package/dashboard/dist/assets/{CronLabel-OOP4P2mJ.js → CronLabel-BAfIHHz4.js} +2 -2
  107. package/dashboard/dist/assets/{CronLabel-OOP4P2mJ.js.map → CronLabel-BAfIHHz4.js.map} +1 -1
  108. package/dashboard/dist/assets/{CustomDurationPicker-COacSAXO.js → CustomDurationPicker-BLF0NFJK.js} +2 -2
  109. package/dashboard/dist/assets/{CustomDurationPicker-COacSAXO.js.map → CustomDurationPicker-BLF0NFJK.js.map} +1 -1
  110. package/dashboard/dist/assets/{DataTable-D9yuBv0w.js → DataTable-ZWgVQg7F.js} +2 -2
  111. package/dashboard/dist/assets/DataTable-ZWgVQg7F.js.map +1 -0
  112. package/dashboard/dist/assets/{DropZone-Bfw2YLVS.js → DropZone-CEx_wk-g.js} +2 -2
  113. package/dashboard/dist/assets/{DropZone-Bfw2YLVS.js.map → DropZone-CEx_wk-g.js.map} +1 -1
  114. package/dashboard/dist/assets/{ElapsedCell-CkfCD-m7.js → ElapsedCell-nKGJvznH.js} +2 -2
  115. package/dashboard/dist/assets/{ElapsedCell-CkfCD-m7.js.map → ElapsedCell-nKGJvznH.js.map} +1 -1
  116. package/dashboard/dist/assets/{EscalationsOverview-BmI8jxlh.js → EscalationsOverview-bNNVA6R2.js} +2 -2
  117. package/dashboard/dist/assets/{EscalationsOverview-BmI8jxlh.js.map → EscalationsOverview-bNNVA6R2.js.map} +1 -1
  118. package/dashboard/dist/assets/{EventTable-CeKfFk3m.js → EventTable-BCuDdqlU.js} +2 -2
  119. package/dashboard/dist/assets/{EventTable-CeKfFk3m.js.map → EventTable-BCuDdqlU.js.map} +1 -1
  120. package/dashboard/dist/assets/FilterBar-BQ2JGg8U.js +2 -0
  121. package/dashboard/dist/assets/FilterBar-BQ2JGg8U.js.map +1 -0
  122. package/dashboard/dist/assets/GraphInvokePage-BPAvqHls.js +2 -0
  123. package/dashboard/dist/assets/GraphInvokePage-BPAvqHls.js.map +1 -0
  124. package/dashboard/dist/assets/HomePage-BZ1YTrjR.js +2 -0
  125. package/dashboard/dist/assets/HomePage-BZ1YTrjR.js.map +1 -0
  126. package/dashboard/dist/assets/{ListToolbar-BJaCzAXS.js → ListToolbar-DIJZfEgu.js} +2 -2
  127. package/dashboard/dist/assets/{ListToolbar-BJaCzAXS.js.map → ListToolbar-DIJZfEgu.js.map} +1 -1
  128. package/dashboard/dist/assets/{McpOverview-CvnDG5OB.js → McpOverview-DCfSCfPg.js} +2 -2
  129. package/dashboard/dist/assets/{McpOverview-CvnDG5OB.js.map → McpOverview-DCfSCfPg.js.map} +1 -1
  130. package/dashboard/dist/assets/{McpQueryDetailPage-DwmSIcIL.js → McpQueryDetailPage-mb7rmtNe.js} +2 -2
  131. package/dashboard/dist/assets/{McpQueryDetailPage-DwmSIcIL.js.map → McpQueryDetailPage-mb7rmtNe.js.map} +1 -1
  132. package/dashboard/dist/assets/{McpQueryPage-CU6FMEEk.js → McpQueryPage-D9b1T0I3.js} +2 -2
  133. package/dashboard/dist/assets/{McpQueryPage-CU6FMEEk.js.map → McpQueryPage-D9b1T0I3.js.map} +1 -1
  134. package/dashboard/dist/assets/{McpRunDetailPage-8a6lsehv.js → McpRunDetailPage-Bz2UyuNm.js} +2 -2
  135. package/dashboard/dist/assets/{McpRunDetailPage-8a6lsehv.js.map → McpRunDetailPage-Bz2UyuNm.js.map} +1 -1
  136. package/dashboard/dist/assets/McpRunsPage-Dw7w_ZO-.js +2 -0
  137. package/dashboard/dist/assets/McpRunsPage-Dw7w_ZO-.js.map +1 -0
  138. package/dashboard/dist/assets/{NamespacePill-D6btr6e8.js → NamespacePill-D27vqDXy.js} +2 -2
  139. package/dashboard/dist/assets/{NamespacePill-D6btr6e8.js.map → NamespacePill-D27vqDXy.js.map} +1 -1
  140. package/dashboard/dist/assets/OperatorDashboard-CTzSDW3N.js +2 -0
  141. package/dashboard/dist/assets/{OperatorDashboard-g9o7I925.js.map → OperatorDashboard-CTzSDW3N.js.map} +1 -1
  142. package/dashboard/dist/assets/PageHeader-B_1zTx2q.js +2 -0
  143. package/dashboard/dist/assets/PageHeader-B_1zTx2q.js.map +1 -0
  144. package/dashboard/dist/assets/{PageHeaderWithStats-DJQCZ0FR.js → PageHeaderWithStats-D-cPg7UD.js} +2 -2
  145. package/dashboard/dist/assets/{PageHeaderWithStats-DJQCZ0FR.js.map → PageHeaderWithStats-D-cPg7UD.js.map} +1 -1
  146. package/dashboard/dist/assets/PriorityBadge-B4ykBH7f.js +2 -0
  147. package/dashboard/dist/assets/PriorityBadge-B4ykBH7f.js.map +1 -0
  148. package/dashboard/dist/assets/{ProcessDetailPage-ByyWCs2A.js → ProcessDetailPage-olG2CS-h.js} +2 -2
  149. package/dashboard/dist/assets/{ProcessDetailPage-ByyWCs2A.js.map → ProcessDetailPage-olG2CS-h.js.map} +1 -1
  150. package/dashboard/dist/assets/{ProcessesListPage-BTsXds4l.js → ProcessesListPage-CUZSL06Y.js} +2 -2
  151. package/dashboard/dist/assets/{ProcessesListPage-BTsXds4l.js.map → ProcessesListPage-CUZSL06Y.js.map} +1 -1
  152. package/dashboard/dist/assets/RolePill-B-V-31lC.js +2 -0
  153. package/dashboard/dist/assets/RolePill-B-V-31lC.js.map +1 -0
  154. package/dashboard/dist/assets/RolesPage-DLnaW6iD.js +2 -0
  155. package/dashboard/dist/assets/RolesPage-DLnaW6iD.js.map +1 -0
  156. package/dashboard/dist/assets/RowActions-BA9L9djf.js +2 -0
  157. package/dashboard/dist/assets/RowActions-BA9L9djf.js.map +1 -0
  158. package/dashboard/dist/assets/{RunAsSelector-BzkZXpSX.js → RunAsSelector-BoLHF4fy.js} +2 -2
  159. package/dashboard/dist/assets/{RunAsSelector-BzkZXpSX.js.map → RunAsSelector-BoLHF4fy.js.map} +1 -1
  160. package/dashboard/dist/assets/StickyPagination-BEMY1L-H.js +2 -0
  161. package/dashboard/dist/assets/StickyPagination-BEMY1L-H.js.map +1 -0
  162. package/dashboard/dist/assets/{StreamMessageDetail-B6ch-6SY.js → StreamMessageDetail-CVRVWXcU.js} +2 -2
  163. package/dashboard/dist/assets/{StreamMessageDetail-B6ch-6SY.js.map → StreamMessageDetail-CVRVWXcU.js.map} +1 -1
  164. package/dashboard/dist/assets/{SwimlaneTimeline-CjvfUkls.js → SwimlaneTimeline-DDpU0-Jn.js} +2 -2
  165. package/dashboard/dist/assets/{SwimlaneTimeline-CjvfUkls.js.map → SwimlaneTimeline-DDpU0-Jn.js.map} +1 -1
  166. package/dashboard/dist/assets/{TagInput-CZSeyesi.js → TagInput-BHVfG307.js} +2 -2
  167. package/dashboard/dist/assets/{TagInput-CZSeyesi.js.map → TagInput-BHVfG307.js.map} +1 -1
  168. package/dashboard/dist/assets/{TaskDetailPage-BjHU6opi.js → TaskDetailPage-B_xQtJMg.js} +2 -2
  169. package/dashboard/dist/assets/{TaskDetailPage-BjHU6opi.js.map → TaskDetailPage-B_xQtJMg.js.map} +1 -1
  170. package/dashboard/dist/assets/{TaskQueuePill-CQrXrSxk.js → TaskQueuePill-CGlxXqWt.js} +2 -2
  171. package/dashboard/dist/assets/{TaskQueuePill-CQrXrSxk.js.map → TaskQueuePill-CGlxXqWt.js.map} +1 -1
  172. package/dashboard/dist/assets/{TasksListPage-Bem44zd0.js → TasksListPage-CsrdIOKk.js} +2 -2
  173. package/dashboard/dist/assets/{TasksListPage-Bem44zd0.js.map → TasksListPage-CsrdIOKk.js.map} +1 -1
  174. package/dashboard/dist/assets/TimeAgo-DmicaxjT.js +2 -0
  175. package/dashboard/dist/assets/{TimeAgo-CUIYqSt_.js.map → TimeAgo-DmicaxjT.js.map} +1 -1
  176. package/dashboard/dist/assets/{TimestampCell--tnreI1i.js → TimestampCell-DeK89eRV.js} +2 -2
  177. package/dashboard/dist/assets/{TimestampCell--tnreI1i.js.map → TimestampCell-DeK89eRV.js.map} +1 -1
  178. package/dashboard/dist/assets/{ToolPill-O09jsTQM.js → ToolPill-NV7PxSpA.js} +2 -2
  179. package/dashboard/dist/assets/{ToolPill-O09jsTQM.js.map → ToolPill-NV7PxSpA.js.map} +1 -1
  180. package/dashboard/dist/assets/{ToolTestPanel-OD99H7Pk.js → ToolTestPanel-D3s2n9-9.js} +2 -2
  181. package/dashboard/dist/assets/{ToolTestPanel-OD99H7Pk.js.map → ToolTestPanel-D3s2n9-9.js.map} +1 -1
  182. package/dashboard/dist/assets/{TopicDetailPage-rh_dsTJI.js → TopicDetailPage-qyv7UWT_.js} +3 -3
  183. package/dashboard/dist/assets/TopicDetailPage-qyv7UWT_.js.map +1 -0
  184. package/dashboard/dist/assets/TopicsPage-CEUfMoed.js +2 -0
  185. package/dashboard/dist/assets/TopicsPage-CEUfMoed.js.map +1 -0
  186. package/dashboard/dist/assets/{UserName-C__c8BN0.js → UserName-Bn_BxI8K.js} +2 -2
  187. package/dashboard/dist/assets/{UserName-C__c8BN0.js.map → UserName-Bn_BxI8K.js.map} +1 -1
  188. package/dashboard/dist/assets/{WorkflowExecutionPage-Bw6QdtUO.js → WorkflowExecutionPage-DAk2RqpB.js} +2 -2
  189. package/dashboard/dist/assets/{WorkflowExecutionPage-Bw6QdtUO.js.map → WorkflowExecutionPage-DAk2RqpB.js.map} +1 -1
  190. package/dashboard/dist/assets/{WorkflowPill-CS-IsR0U.js → WorkflowPill-DDR0B6DN.js} +2 -2
  191. package/dashboard/dist/assets/{WorkflowPill-CS-IsR0U.js.map → WorkflowPill-DDR0B6DN.js.map} +1 -1
  192. package/dashboard/dist/assets/WorkflowsDashboard-DfBgM80t.js +2 -0
  193. package/dashboard/dist/assets/WorkflowsDashboard-DfBgM80t.js.map +1 -0
  194. package/dashboard/dist/assets/{WorkflowsOverview-BRc0QbA7.js → WorkflowsOverview--Dkoo9Jq.js} +2 -2
  195. package/dashboard/dist/assets/{WorkflowsOverview-BRc0QbA7.js.map → WorkflowsOverview--Dkoo9Jq.js.map} +1 -1
  196. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DdzspNQR.js → YamlWorkflowDetailPage-B6HYbfFj.js} +25 -25
  197. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DdzspNQR.js.map → YamlWorkflowDetailPage-B6HYbfFj.js.map} +1 -1
  198. package/dashboard/dist/assets/{YamlWorkflowsPage-Djw5ZbgQ.js → YamlWorkflowsPage-CJE-PxC0.js} +2 -2
  199. package/dashboard/dist/assets/{YamlWorkflowsPage-Djw5ZbgQ.js.map → YamlWorkflowsPage-CJE-PxC0.js.map} +1 -1
  200. package/dashboard/dist/assets/{agents-xstNkF6c.js → agents-DUciUZpt.js} +2 -2
  201. package/dashboard/dist/assets/{agents-xstNkF6c.js.map → agents-DUciUZpt.js.map} +1 -1
  202. package/dashboard/dist/assets/{bots-BztAI0s_.js → bots-BnyqCfuC.js} +2 -2
  203. package/dashboard/dist/assets/{bots-BztAI0s_.js.map → bots-BnyqCfuC.js.map} +1 -1
  204. package/dashboard/dist/assets/{capabilities-CEDwtxNL.js → capabilities-CkTWWT0q.js} +2 -2
  205. package/dashboard/dist/assets/{capabilities-CEDwtxNL.js.map → capabilities-CkTWWT0q.js.map} +1 -1
  206. package/dashboard/dist/assets/{controlplane-BlGEsRKe.js → controlplane-D9HaFZsC.js} +2 -2
  207. package/dashboard/dist/assets/{controlplane-BlGEsRKe.js.map → controlplane-D9HaFZsC.js.map} +1 -1
  208. package/dashboard/dist/assets/{escalation-iI4QKlP-.js → escalation-De9JnTKI.js} +2 -2
  209. package/dashboard/dist/assets/{escalation-iI4QKlP-.js.map → escalation-De9JnTKI.js.map} +1 -1
  210. package/dashboard/dist/assets/escalation-columns-Df7Pluzg.js +2 -0
  211. package/dashboard/dist/assets/escalation-columns-Df7Pluzg.js.map +1 -0
  212. package/dashboard/dist/assets/index-7hqtQI43.js +2 -0
  213. package/dashboard/dist/assets/index-7hqtQI43.js.map +1 -0
  214. package/dashboard/dist/assets/{index-CAqEy2Db.js → index-B07xLlyz.js} +2 -2
  215. package/dashboard/dist/assets/{index-CAqEy2Db.js.map → index-B07xLlyz.js.map} +1 -1
  216. package/dashboard/dist/assets/{index-D1xVY4JR.js → index-B1kc6itM.js} +24 -24
  217. package/dashboard/dist/assets/index-B1kc6itM.js.map +1 -0
  218. package/dashboard/dist/assets/index-BAhkWcOq.js +9 -0
  219. package/dashboard/dist/assets/index-BAhkWcOq.js.map +1 -0
  220. package/dashboard/dist/assets/{index-Dsi5o7dx.js → index-BsIaUbKD.js} +2 -2
  221. package/dashboard/dist/assets/{index-Dsi5o7dx.js.map → index-BsIaUbKD.js.map} +1 -1
  222. package/dashboard/dist/assets/index-BxdiKgnH.js +6 -0
  223. package/dashboard/dist/assets/index-BxdiKgnH.js.map +1 -0
  224. package/dashboard/dist/assets/{index-B7VC0vjf.js → index-BzMsh6Ui.js} +2 -2
  225. package/dashboard/dist/assets/{index-B7VC0vjf.js.map → index-BzMsh6Ui.js.map} +1 -1
  226. package/dashboard/dist/assets/index-CN0efCE9.js +6 -0
  227. package/dashboard/dist/assets/index-CN0efCE9.js.map +1 -0
  228. package/dashboard/dist/assets/{index-B5qsp8lb.js → index-CgD9fA9P.js} +2 -2
  229. package/dashboard/dist/assets/{index-B5qsp8lb.js.map → index-CgD9fA9P.js.map} +1 -1
  230. package/dashboard/dist/assets/{index-CUfgt3mV.js → index-Cqjf-yVP.js} +2 -2
  231. package/dashboard/dist/assets/{index-CUfgt3mV.js.map → index-Cqjf-yVP.js.map} +1 -1
  232. package/dashboard/dist/assets/{index-DjJ6VCuc.js → index-DNugmBs7.js} +2 -2
  233. package/dashboard/dist/assets/{index-DjJ6VCuc.js.map → index-DNugmBs7.js.map} +1 -1
  234. package/dashboard/dist/assets/{index-BUmnJ4eT.js → index-DjxTV3cn.js} +2 -2
  235. package/dashboard/dist/assets/{index-BUmnJ4eT.js.map → index-DjxTV3cn.js.map} +1 -1
  236. package/dashboard/dist/assets/index-DpAS4-DA.js +2 -0
  237. package/dashboard/dist/assets/index-DpAS4-DA.js.map +1 -0
  238. package/dashboard/dist/assets/index-nO0cim3_.css +1 -0
  239. package/dashboard/dist/assets/{knowledge-BKg0cr39.js → knowledge-D4SvdNv5.js} +2 -2
  240. package/dashboard/dist/assets/{knowledge-BKg0cr39.js.map → knowledge-D4SvdNv5.js.map} +1 -1
  241. package/dashboard/dist/assets/{mcp-CVdHpKlz.js → mcp-DVxt4T9J.js} +2 -2
  242. package/dashboard/dist/assets/{mcp-CVdHpKlz.js.map → mcp-DVxt4T9J.js.map} +1 -1
  243. package/dashboard/dist/assets/{mcp-query-CYxZcbwQ.js → mcp-query-CLP9EnnU.js} +2 -2
  244. package/dashboard/dist/assets/{mcp-query-CYxZcbwQ.js.map → mcp-query-CLP9EnnU.js.map} +1 -1
  245. package/dashboard/dist/assets/{pipelines-BOEv7P6O.js → pipelines-C_x0LTBN.js} +2 -2
  246. package/dashboard/dist/assets/{pipelines-BOEv7P6O.js.map → pipelines-C_x0LTBN.js.map} +1 -1
  247. package/dashboard/dist/assets/{roles-c5cV2QYQ.js → roles-QaGFZQ58.js} +2 -2
  248. package/dashboard/dist/assets/{roles-c5cV2QYQ.js.map → roles-QaGFZQ58.js.map} +1 -1
  249. package/dashboard/dist/assets/{tasks-JV3BjeEF.js → tasks-1eTuP02d.js} +2 -2
  250. package/dashboard/dist/assets/{tasks-JV3BjeEF.js.map → tasks-1eTuP02d.js.map} +1 -1
  251. package/dashboard/dist/assets/{topics-BDaPnhnQ.js → topics-B3p8K0PW.js} +2 -2
  252. package/dashboard/dist/assets/{topics-BDaPnhnQ.js.map → topics-B3p8K0PW.js.map} +1 -1
  253. package/dashboard/dist/assets/{useEventHooks-a7Dnh8G4.js → useEventHooks-BNZky4xM.js} +2 -2
  254. package/dashboard/dist/assets/{useEventHooks-a7Dnh8G4.js.map → useEventHooks-BNZky4xM.js.map} +1 -1
  255. package/dashboard/dist/assets/{useNamespace-BSwEV0e9.js → useNamespace-Dx81_JM0.js} +2 -2
  256. package/dashboard/dist/assets/{useNamespace-BSwEV0e9.js.map → useNamespace-Dx81_JM0.js.map} +1 -1
  257. package/dashboard/dist/assets/{useYamlActivityEvents-BuqvL-cn.js → useYamlActivityEvents-D5T3TbE7.js} +2 -2
  258. package/dashboard/dist/assets/{useYamlActivityEvents-BuqvL-cn.js.map → useYamlActivityEvents-D5T3TbE7.js.map} +1 -1
  259. package/dashboard/dist/assets/users-BEMysNdR.js +2 -0
  260. package/dashboard/dist/assets/users-BEMysNdR.js.map +1 -0
  261. package/dashboard/dist/assets/{vendor-icons-D5ycKPA2.js → vendor-icons--Ivmp-RX.js} +132 -112
  262. package/dashboard/dist/assets/vendor-icons--Ivmp-RX.js.map +1 -0
  263. package/dashboard/dist/assets/{workflows-DF-9Voyi.js → workflows-C4rjvQbI.js} +2 -2
  264. package/dashboard/dist/assets/{workflows-DF-9Voyi.js.map → workflows-C4rjvQbI.js.map} +1 -1
  265. package/dashboard/dist/assets/{yaml-workflows-2roKaSSK.js → yaml-workflows-4rJ5v5rJ.js} +2 -2
  266. package/dashboard/dist/assets/{yaml-workflows-2roKaSSK.js.map → yaml-workflows-4rJ5v5rJ.js.map} +1 -1
  267. package/dashboard/dist/index.html +3 -3
  268. package/docs/api/http/escalations.md +131 -10
  269. package/docs/api/http/roles.md +44 -9
  270. package/docs/api/http/users.md +15 -6
  271. package/docs/api/mcp/admin.md +8 -2
  272. package/docs/api/mcp/human-queue.md +34 -2
  273. package/docs/api/sdk/escalations.md +155 -2
  274. package/docs/api/sdk/roles.md +11 -0
  275. package/docs/api/sdk/users.md +26 -1
  276. package/docs/auth.md +1 -1
  277. package/docs/dashboard.md +5 -4
  278. package/docs/data.md +9 -2
  279. package/docs/faceted-routing.md +221 -0
  280. package/docs/hitl-guide.md +50 -0
  281. package/docs/iam.md +31 -0
  282. package/docs/mcp.md +8 -0
  283. package/package.json +2 -2
  284. package/dashboard/dist/assets/AdminDashboard-LTiYLuzq.js +0 -2
  285. package/dashboard/dist/assets/AgentDetailPage-BSUnDvGp.js.map +0 -1
  286. package/dashboard/dist/assets/AgentsPage-DpS0cCYe.js +0 -2
  287. package/dashboard/dist/assets/AvailableEscalationsPage-BEylRqjP.js +0 -2
  288. package/dashboard/dist/assets/AvailableEscalationsPage-BEylRqjP.js.map +0 -1
  289. package/dashboard/dist/assets/CapabilitiesPage-DLIleX2L.js +0 -2
  290. package/dashboard/dist/assets/CapabilitiesPage-DLIleX2L.js.map +0 -1
  291. package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js +0 -2
  292. package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js.map +0 -1
  293. package/dashboard/dist/assets/DataTable-D9yuBv0w.js.map +0 -1
  294. package/dashboard/dist/assets/FilterBar-Ck4K4rzu.js +0 -2
  295. package/dashboard/dist/assets/FilterBar-Ck4K4rzu.js.map +0 -1
  296. package/dashboard/dist/assets/GraphInvokePage-BZBPBCSq.js +0 -2
  297. package/dashboard/dist/assets/GraphInvokePage-BZBPBCSq.js.map +0 -1
  298. package/dashboard/dist/assets/HomePage-dM7U2tl6.js +0 -2
  299. package/dashboard/dist/assets/HomePage-dM7U2tl6.js.map +0 -1
  300. package/dashboard/dist/assets/McpRunsPage-CPMYWgm_.js +0 -2
  301. package/dashboard/dist/assets/McpRunsPage-CPMYWgm_.js.map +0 -1
  302. package/dashboard/dist/assets/OperatorDashboard-g9o7I925.js +0 -2
  303. package/dashboard/dist/assets/PageHeader-Dk2Pe44Q.js +0 -2
  304. package/dashboard/dist/assets/PageHeader-Dk2Pe44Q.js.map +0 -1
  305. package/dashboard/dist/assets/PriorityBadge-DfQY9St9.js +0 -2
  306. package/dashboard/dist/assets/PriorityBadge-DfQY9St9.js.map +0 -1
  307. package/dashboard/dist/assets/RolePill-Du-aNnjR.js +0 -2
  308. package/dashboard/dist/assets/RolePill-Du-aNnjR.js.map +0 -1
  309. package/dashboard/dist/assets/RolesPage-B2Kdf-L9.js +0 -2
  310. package/dashboard/dist/assets/RolesPage-B2Kdf-L9.js.map +0 -1
  311. package/dashboard/dist/assets/RowActions-Dg-Fsm5O.js +0 -2
  312. package/dashboard/dist/assets/RowActions-Dg-Fsm5O.js.map +0 -1
  313. package/dashboard/dist/assets/StickyPagination-BWhFSr2d.js +0 -2
  314. package/dashboard/dist/assets/StickyPagination-BWhFSr2d.js.map +0 -1
  315. package/dashboard/dist/assets/TimeAgo-CUIYqSt_.js +0 -2
  316. package/dashboard/dist/assets/TopicDetailPage-rh_dsTJI.js.map +0 -1
  317. package/dashboard/dist/assets/TopicsPage-CD_40_v-.js +0 -2
  318. package/dashboard/dist/assets/TopicsPage-CD_40_v-.js.map +0 -1
  319. package/dashboard/dist/assets/WorkflowsDashboard-BgI-iOP6.js +0 -2
  320. package/dashboard/dist/assets/WorkflowsDashboard-BgI-iOP6.js.map +0 -1
  321. package/dashboard/dist/assets/escalation-columns-nVYVt7Gl.js +0 -2
  322. package/dashboard/dist/assets/escalation-columns-nVYVt7Gl.js.map +0 -1
  323. package/dashboard/dist/assets/index-CMviPjXQ.js +0 -9
  324. package/dashboard/dist/assets/index-CMviPjXQ.js.map +0 -1
  325. package/dashboard/dist/assets/index-CP0JEQhZ.js +0 -6
  326. package/dashboard/dist/assets/index-CP0JEQhZ.js.map +0 -1
  327. package/dashboard/dist/assets/index-ClvYSNZd.css +0 -1
  328. package/dashboard/dist/assets/index-D1xVY4JR.js.map +0 -1
  329. package/dashboard/dist/assets/index-DPLy-jwO.js +0 -2
  330. package/dashboard/dist/assets/index-DPLy-jwO.js.map +0 -1
  331. package/dashboard/dist/assets/index-DzVYjoex.js +0 -6
  332. package/dashboard/dist/assets/index-DzVYjoex.js.map +0 -1
  333. package/dashboard/dist/assets/index-FuhiJuHL.js +0 -2
  334. package/dashboard/dist/assets/index-FuhiJuHL.js.map +0 -1
  335. package/dashboard/dist/assets/users-Pn85K_3C.js +0 -2
  336. package/dashboard/dist/assets/users-Pn85K_3C.js.map +0 -1
  337. package/dashboard/dist/assets/vendor-icons-D5ycKPA2.js.map +0 -1
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_WRITE_SCOPE = exports.DEFAULT_READ_SCOPE = exports.WRITE_SCOPES = exports.READ_SCOPES = void 0;
4
+ exports.isValidReadScope = isValidReadScope;
5
+ exports.isValidWriteScope = isValidWriteScope;
6
+ exports.isValidScopePair = isValidScopePair;
7
+ exports.effectiveScope = effectiveScope;
8
+ // ─── Role scope: the work-surface axes for an open-role membership ───────────
9
+ //
10
+ // A membership's `type` (member/admin/superadmin) is the management/global tier.
11
+ // Orthogonal to it, a member also carries two scope axes that gate the four task
12
+ // queue verbs:
13
+ //
14
+ // read_scope (self | all) → governs SEARCH (which items appear)
15
+ // write_scope (none | self | all) → governs CLAIM / ACK / DELETE (which items
16
+ // the user may act on)
17
+ //
18
+ // `self` = items assigned to the user (`assigned_to = userId`). `all` = the whole
19
+ // role queue. The only constraint is write ⊆ read: you cannot act on what you
20
+ // cannot see (enforced in the DB by chk_lt_user_roles_scope). admin/superadmin
21
+ // ignore scope and always operate on all.
22
+ exports.READ_SCOPES = ['self', 'all'];
23
+ exports.WRITE_SCOPES = ['none', 'self', 'all'];
24
+ /** Defaults preserve legacy `member` behavior (full role worker). */
25
+ exports.DEFAULT_READ_SCOPE = 'all';
26
+ exports.DEFAULT_WRITE_SCOPE = 'all';
27
+ function isValidReadScope(scope) {
28
+ return exports.READ_SCOPES.includes(scope);
29
+ }
30
+ function isValidWriteScope(scope) {
31
+ return exports.WRITE_SCOPES.includes(scope);
32
+ }
33
+ /**
34
+ * Is (read, write) a valid point in the lattice? Enforces write ⊆ read:
35
+ * write='all' requires read='all'. (write='self'/'none' allowed under read='self'.)
36
+ */
37
+ function isValidScopePair(read, write) {
38
+ return !(write === 'all' && read === 'self');
39
+ }
40
+ /**
41
+ * Resolve the scope a membership actually grants. The management tier
42
+ * (admin/superadmin) short-circuits both axes to `all` — those users always
43
+ * search and act on the whole role queue regardless of stored scope columns.
44
+ */
45
+ function effectiveScope(type, read, write) {
46
+ if (type === 'admin' || type === 'superadmin') {
47
+ return { read: 'all', write: 'all' };
48
+ }
49
+ return { read, write };
50
+ }
@@ -20,23 +20,40 @@ async function seedAdmin(input) {
20
20
  catch { /* ON CONFLICT DO NOTHING */ }
21
21
  const existing = await (0, crud_1.getUserByExternalId)(externalId);
22
22
  if (existing) {
23
- // Ensure existing user has the superadmin role
24
- const roles = await (0, roles_1.getUserRoles)(existing.id);
25
- const hasSuperadmin = roles.some((r) => r.role === 'superadmin' && r.type === 'superadmin');
26
- if (!hasSuperadmin) {
27
- await (0, roles_1.addUserRole)(existing.id, 'superadmin', 'superadmin');
28
- logger_1.loggerRegistry.info(`[seed-admin] granted superadmin role to ${externalId}`);
23
+ return ensureSuperadmin(existing.id, externalId);
24
+ }
25
+ // Create. Concurrent container startups can both reach here; external_id UNIQUE
26
+ // is the arbiter — on the loser's 23505, adopt the winner's row instead of
27
+ // crashing the boot sequence.
28
+ try {
29
+ const user = await (0, crud_1.createUser)({
30
+ external_id: externalId,
31
+ email: email || undefined,
32
+ display_name: displayName || externalId,
33
+ password: password || undefined,
34
+ roles: [{ role: 'superadmin', type: 'superadmin' }],
35
+ });
36
+ logger_1.loggerRegistry.info(`[seed-admin] created superadmin user: ${externalId}`);
37
+ return user.id;
38
+ }
39
+ catch (err) {
40
+ if (err?.code === '23505') {
41
+ const winner = await (0, crud_1.getUserByExternalId)(externalId);
42
+ if (winner) {
43
+ return ensureSuperadmin(winner.id, externalId);
44
+ }
29
45
  }
30
- logger_1.loggerRegistry.info(`[seed-admin] ${externalId} already exists, skipping creation`);
31
- return existing.id;
46
+ throw err;
47
+ }
48
+ }
49
+ /** Ensure an existing user carries the superadmin role; returns its id. */
50
+ async function ensureSuperadmin(userId, externalId) {
51
+ const roles = await (0, roles_1.getUserRoles)(userId);
52
+ const hasSuperadmin = roles.some((r) => r.role === 'superadmin' && r.type === 'superadmin');
53
+ if (!hasSuperadmin) {
54
+ await (0, roles_1.addUserRole)(userId, 'superadmin', 'superadmin');
55
+ logger_1.loggerRegistry.info(`[seed-admin] granted superadmin role to ${externalId}`);
32
56
  }
33
- const user = await (0, crud_1.createUser)({
34
- external_id: externalId,
35
- email: email || undefined,
36
- display_name: displayName || externalId,
37
- password: password || undefined,
38
- roles: [{ role: 'superadmin', type: 'superadmin' }],
39
- });
40
- logger_1.loggerRegistry.info(`[seed-admin] created superadmin user: ${externalId}`);
41
- return user.id;
57
+ logger_1.loggerRegistry.info(`[seed-admin] ${externalId} already exists, skipping creation`);
58
+ return userId;
42
59
  }
@@ -1,24 +1,35 @@
1
1
  /** Ensure a role name exists in the lt_roles lookup table (FK target). */
2
2
  export declare const ENSURE_ROLE_EXISTS = "INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING";
3
3
  /** Fetch roles for a single user, ordered by creation time. */
4
- export declare const GET_ROLES_BY_USER_ID = "SELECT role, type, created_at FROM lt_user_roles WHERE user_id = $1 ORDER BY created_at";
4
+ export declare const GET_ROLES_BY_USER_ID = "SELECT role, type, read_scope, write_scope, created_at FROM lt_user_roles WHERE user_id = $1 ORDER BY created_at";
5
5
  /** Fetch a user row by external_id. */
6
6
  export declare const GET_USER_BY_EXTERNAL_ID = "SELECT * FROM lt_users WHERE external_id = $1";
7
- export declare const INSERT_USER = "INSERT INTO lt_users (external_id, email, display_name, status, metadata, password_hash, oauth_provider, oauth_provider_id)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n RETURNING *";
7
+ /**
8
+ * Create a user and its role assignments in ONE atomic statement. The user row,
9
+ * the lt_roles FK targets ($9 = role names), and the lt_user_roles links
10
+ * ($9 names × $10 types × $11 read_scope × $12 write_scope) all commit together —
11
+ * a failure can never leave a half-provisioned user with a partial role set, and
12
+ * there is no per-role N+1. All four are passed as parallel text[] arrays (empty
13
+ * arrays → no roles). Postgres checks the lt_user_roles→lt_roles FK at statement
14
+ * end, after the sibling role-ensure CTE has run, so brand-new roles are valid
15
+ * FK targets.
16
+ */
17
+ export declare const CREATE_USER_WITH_ROLES = "WITH new_user AS (\n INSERT INTO lt_users (external_id, email, display_name, status, metadata, password_hash, oauth_provider, oauth_provider_id)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n RETURNING *\n ), ensured_roles AS (\n INSERT INTO lt_roles (role)\n SELECT DISTINCT unnest($9::text[])\n ON CONFLICT DO NOTHING\n ), assigned_roles AS (\n INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope)\n SELECT (SELECT id FROM new_user), x.role, x.type, x.read_scope, x.write_scope\n FROM unnest($9::text[], $10::text[], $11::text[], $12::text[]) AS x(role, type, read_scope, write_scope)\n ON CONFLICT DO NOTHING\n )\n SELECT * FROM new_user";
8
18
  export declare const GET_USER_BY_EMAIL = "SELECT * FROM lt_users WHERE email = $1 LIMIT 1";
9
- export declare const INSERT_USER_ROLE_IGNORE = "INSERT INTO lt_user_roles (user_id, role, type) VALUES ($1, $2, $3)\n ON CONFLICT DO NOTHING";
10
19
  export declare const GET_USER_BY_ID = "SELECT * FROM lt_users WHERE id = $1";
11
20
  /** Check if a user exists by id. Lightweight — returns only the id column. */
12
21
  export declare const VERIFY_USER_BY_ID = "SELECT id FROM lt_users WHERE id = $1 LIMIT 1";
13
22
  /** Fetch user + roles in a single query. Returns one row per role (or one row with nulls if no roles). */
14
- export declare const GET_USER_WITH_ROLES = "SELECT u.id, u.external_id, u.display_name, u.status, u.metadata,\n r.role, r.type AS role_type\n FROM lt_users u\n LEFT JOIN lt_user_roles r ON r.user_id = u.id\n WHERE u.external_id = $1\n ORDER BY r.created_at";
23
+ export declare const GET_USER_WITH_ROLES = "SELECT u.id, u.external_id, u.display_name, u.status, u.metadata,\n r.role, r.type AS role_type, r.read_scope, r.write_scope\n FROM lt_users u\n LEFT JOIN lt_user_roles r ON r.user_id = u.id\n WHERE u.external_id = $1\n ORDER BY r.created_at";
15
24
  export declare const DELETE_USER_BY_ID = "DELETE FROM lt_users WHERE id = $1";
16
25
  /** Batch-load roles for many users (avoids N+1). */
17
- export declare const GET_ROLES_BY_USER_IDS = "SELECT user_id, role, type, created_at FROM lt_user_roles WHERE user_id = ANY($1) ORDER BY created_at";
18
- /** Upsert a user–role assignment, promoting the type if the row exists. */
19
- export declare const UPSERT_USER_ROLE = "INSERT INTO lt_user_roles (user_id, role, type) VALUES ($1, $2, $3)\n ON CONFLICT (user_id, role) DO UPDATE SET type = EXCLUDED.type\n RETURNING role, type, created_at";
26
+ export declare const GET_ROLES_BY_USER_IDS = "SELECT user_id, role, type, read_scope, write_scope, created_at FROM lt_user_roles WHERE user_id = ANY($1) ORDER BY created_at";
27
+ /** Upsert a user–role assignment, promoting the type/scope if the row exists. */
28
+ export declare const UPSERT_USER_ROLE = "INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope) VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (user_id, role) DO UPDATE SET\n type = EXCLUDED.type,\n read_scope = EXCLUDED.read_scope,\n write_scope = EXCLUDED.write_scope\n RETURNING role, type, read_scope, write_scope, created_at";
20
29
  export declare const DELETE_USER_ROLE = "DELETE FROM lt_user_roles WHERE user_id = $1 AND role = $2";
21
30
  export declare const HAS_ROLE = "SELECT 1 FROM lt_user_roles WHERE user_id = $1 AND role = $2 LIMIT 1";
22
31
  export declare const HAS_ROLE_TYPE = "SELECT 1 FROM lt_user_roles WHERE user_id = $1 AND type = $2 LIMIT 1";
32
+ /** Fetch the management tier + scope for one (user, role) — used for write enforcement. */
33
+ export declare const GET_ROLE_SCOPE = "SELECT type, read_scope, write_scope FROM lt_user_roles WHERE user_id = $1 AND role = $2 LIMIT 1";
23
34
  /** Check if a user is an admin (or superadmin) of a specific role group. */
24
35
  export declare const IS_GROUP_ADMIN = "SELECT 1 FROM lt_user_roles\n WHERE user_id = $1\n AND (\n (role = $2 AND type IN ('admin', 'superadmin'))\n OR type = 'superadmin'\n )\n LIMIT 1";
@@ -1,26 +1,46 @@
1
1
  "use strict";
2
2
  // ─── Shared queries (used by multiple modules) ──────────────────────────────
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.IS_GROUP_ADMIN = exports.HAS_ROLE_TYPE = exports.HAS_ROLE = exports.DELETE_USER_ROLE = exports.UPSERT_USER_ROLE = exports.GET_ROLES_BY_USER_IDS = exports.DELETE_USER_BY_ID = exports.GET_USER_WITH_ROLES = exports.VERIFY_USER_BY_ID = exports.GET_USER_BY_ID = exports.INSERT_USER_ROLE_IGNORE = exports.GET_USER_BY_EMAIL = exports.INSERT_USER = exports.GET_USER_BY_EXTERNAL_ID = exports.GET_ROLES_BY_USER_ID = exports.ENSURE_ROLE_EXISTS = void 0;
4
+ exports.IS_GROUP_ADMIN = exports.GET_ROLE_SCOPE = exports.HAS_ROLE_TYPE = exports.HAS_ROLE = exports.DELETE_USER_ROLE = exports.UPSERT_USER_ROLE = exports.GET_ROLES_BY_USER_IDS = exports.DELETE_USER_BY_ID = exports.GET_USER_WITH_ROLES = exports.VERIFY_USER_BY_ID = exports.GET_USER_BY_ID = exports.GET_USER_BY_EMAIL = exports.CREATE_USER_WITH_ROLES = exports.GET_USER_BY_EXTERNAL_ID = exports.GET_ROLES_BY_USER_ID = exports.ENSURE_ROLE_EXISTS = void 0;
5
5
  /** Ensure a role name exists in the lt_roles lookup table (FK target). */
6
6
  exports.ENSURE_ROLE_EXISTS = 'INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING';
7
7
  /** Fetch roles for a single user, ordered by creation time. */
8
- exports.GET_ROLES_BY_USER_ID = 'SELECT role, type, created_at FROM lt_user_roles WHERE user_id = $1 ORDER BY created_at';
8
+ exports.GET_ROLES_BY_USER_ID = 'SELECT role, type, read_scope, write_scope, created_at FROM lt_user_roles WHERE user_id = $1 ORDER BY created_at';
9
9
  /** Fetch a user row by external_id. */
10
10
  exports.GET_USER_BY_EXTERNAL_ID = 'SELECT * FROM lt_users WHERE external_id = $1';
11
11
  // ─── User CRUD ───────────────────────────────────────────────────────────────
12
- exports.INSERT_USER = `INSERT INTO lt_users (external_id, email, display_name, status, metadata, password_hash, oauth_provider, oauth_provider_id)
13
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
14
- RETURNING *`;
12
+ /**
13
+ * Create a user and its role assignments in ONE atomic statement. The user row,
14
+ * the lt_roles FK targets ($9 = role names), and the lt_user_roles links
15
+ * ($9 names × $10 types × $11 read_scope × $12 write_scope) all commit together —
16
+ * a failure can never leave a half-provisioned user with a partial role set, and
17
+ * there is no per-role N+1. All four are passed as parallel text[] arrays (empty
18
+ * arrays → no roles). Postgres checks the lt_user_roles→lt_roles FK at statement
19
+ * end, after the sibling role-ensure CTE has run, so brand-new roles are valid
20
+ * FK targets.
21
+ */
22
+ exports.CREATE_USER_WITH_ROLES = `WITH new_user AS (
23
+ INSERT INTO lt_users (external_id, email, display_name, status, metadata, password_hash, oauth_provider, oauth_provider_id)
24
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
25
+ RETURNING *
26
+ ), ensured_roles AS (
27
+ INSERT INTO lt_roles (role)
28
+ SELECT DISTINCT unnest($9::text[])
29
+ ON CONFLICT DO NOTHING
30
+ ), assigned_roles AS (
31
+ INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope)
32
+ SELECT (SELECT id FROM new_user), x.role, x.type, x.read_scope, x.write_scope
33
+ FROM unnest($9::text[], $10::text[], $11::text[], $12::text[]) AS x(role, type, read_scope, write_scope)
34
+ ON CONFLICT DO NOTHING
35
+ )
36
+ SELECT * FROM new_user`;
15
37
  exports.GET_USER_BY_EMAIL = `SELECT * FROM lt_users WHERE email = $1 LIMIT 1`;
16
- exports.INSERT_USER_ROLE_IGNORE = `INSERT INTO lt_user_roles (user_id, role, type) VALUES ($1, $2, $3)
17
- ON CONFLICT DO NOTHING`;
18
38
  exports.GET_USER_BY_ID = 'SELECT * FROM lt_users WHERE id = $1';
19
39
  /** Check if a user exists by id. Lightweight — returns only the id column. */
20
40
  exports.VERIFY_USER_BY_ID = 'SELECT id FROM lt_users WHERE id = $1 LIMIT 1';
21
41
  /** Fetch user + roles in a single query. Returns one row per role (or one row with nulls if no roles). */
22
42
  exports.GET_USER_WITH_ROLES = `SELECT u.id, u.external_id, u.display_name, u.status, u.metadata,
23
- r.role, r.type AS role_type
43
+ r.role, r.type AS role_type, r.read_scope, r.write_scope
24
44
  FROM lt_users u
25
45
  LEFT JOIN lt_user_roles r ON r.user_id = u.id
26
46
  WHERE u.external_id = $1
@@ -28,14 +48,19 @@ exports.GET_USER_WITH_ROLES = `SELECT u.id, u.external_id, u.display_name, u.sta
28
48
  exports.DELETE_USER_BY_ID = 'DELETE FROM lt_users WHERE id = $1';
29
49
  // ─── Role management ─────────────────────────────────────────────────────────
30
50
  /** Batch-load roles for many users (avoids N+1). */
31
- exports.GET_ROLES_BY_USER_IDS = 'SELECT user_id, role, type, created_at FROM lt_user_roles WHERE user_id = ANY($1) ORDER BY created_at';
32
- /** Upsert a user–role assignment, promoting the type if the row exists. */
33
- exports.UPSERT_USER_ROLE = `INSERT INTO lt_user_roles (user_id, role, type) VALUES ($1, $2, $3)
34
- ON CONFLICT (user_id, role) DO UPDATE SET type = EXCLUDED.type
35
- RETURNING role, type, created_at`;
51
+ exports.GET_ROLES_BY_USER_IDS = 'SELECT user_id, role, type, read_scope, write_scope, created_at FROM lt_user_roles WHERE user_id = ANY($1) ORDER BY created_at';
52
+ /** Upsert a user–role assignment, promoting the type/scope if the row exists. */
53
+ exports.UPSERT_USER_ROLE = `INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope) VALUES ($1, $2, $3, $4, $5)
54
+ ON CONFLICT (user_id, role) DO UPDATE SET
55
+ type = EXCLUDED.type,
56
+ read_scope = EXCLUDED.read_scope,
57
+ write_scope = EXCLUDED.write_scope
58
+ RETURNING role, type, read_scope, write_scope, created_at`;
36
59
  exports.DELETE_USER_ROLE = 'DELETE FROM lt_user_roles WHERE user_id = $1 AND role = $2';
37
60
  exports.HAS_ROLE = 'SELECT 1 FROM lt_user_roles WHERE user_id = $1 AND role = $2 LIMIT 1';
38
61
  exports.HAS_ROLE_TYPE = 'SELECT 1 FROM lt_user_roles WHERE user_id = $1 AND type = $2 LIMIT 1';
62
+ /** Fetch the management tier + scope for one (user, role) — used for write enforcement. */
63
+ exports.GET_ROLE_SCOPE = 'SELECT type, read_scope, write_scope FROM lt_user_roles WHERE user_id = $1 AND role = $2 LIMIT 1';
39
64
  // ─── RBAC ────────────────────────────────────────────────────────────────────
40
65
  /** Check if a user is an admin (or superadmin) of a specific role group. */
41
66
  exports.IS_GROUP_ADMIN = `SELECT 1 FROM lt_user_roles
@@ -18,34 +18,51 @@ async function ssoProvision(identity, ssoConfig) {
18
18
  const ltRoles = resolveRoles(identity.roles || [], defaultRoleType, ssoConfig.roleMap);
19
19
  const existing = await (0, crud_1.getUserByExternalId)(identity.externalId);
20
20
  if (existing) {
21
- // Sync roles: ensure all resolved roles exist on the user
22
- const currentRoles = await (0, roles_1.getUserRoles)(existing.id);
23
- for (const lr of ltRoles) {
24
- const has = currentRoles.some((r) => r.role === lr.role);
25
- if (!has) {
26
- await (0, roles_1.addUserRole)(existing.id, lr.role, lr.type);
27
- }
28
- }
29
- const updatedRoles = await (0, roles_1.getUserRoles)(existing.id);
21
+ return syncExistingUser(existing.id, ltRoles);
22
+ }
23
+ // Create new user. Two concurrent first-logins for the same identity can both
24
+ // pass the existence check above; external_id UNIQUE is the arbiter. The loser's
25
+ // insert raises 23505 — adopt the winner's committed row and sync roles rather
26
+ // than failing the login.
27
+ try {
28
+ const user = await (0, crud_1.createUser)({
29
+ external_id: identity.externalId,
30
+ email: identity.email || undefined,
31
+ display_name: identity.displayName || identity.externalId,
32
+ roles: ltRoles.map((r) => ({ role: r.role, type: r.type })),
33
+ metadata: identity.metadata,
34
+ });
35
+ logger_1.loggerRegistry.info(`[lt-sso] provisioned user: ${identity.externalId} → ${user.id}`);
30
36
  return {
31
- userId: existing.id,
32
- roles: updatedRoles.map((r) => ({ role: r.role, type: r.type })),
33
- created: false,
37
+ userId: user.id,
38
+ roles: (user.roles || []).map((r) => ({ role: r.role, type: r.type })),
39
+ created: true,
34
40
  };
35
41
  }
36
- // Create new user
37
- const user = await (0, crud_1.createUser)({
38
- external_id: identity.externalId,
39
- email: identity.email || undefined,
40
- display_name: identity.displayName || identity.externalId,
41
- roles: ltRoles.map((r) => ({ role: r.role, type: r.type })),
42
- metadata: identity.metadata,
43
- });
44
- logger_1.loggerRegistry.info(`[lt-sso] provisioned user: ${identity.externalId} → ${user.id}`);
42
+ catch (err) {
43
+ if (err?.code === '23505') {
44
+ const winner = await (0, crud_1.getUserByExternalId)(identity.externalId);
45
+ if (winner) {
46
+ return syncExistingUser(winner.id, ltRoles);
47
+ }
48
+ }
49
+ throw err;
50
+ }
51
+ }
52
+ /** Ensure all resolved roles exist on an already-provisioned user. */
53
+ async function syncExistingUser(userId, ltRoles) {
54
+ const currentRoles = await (0, roles_1.getUserRoles)(userId);
55
+ for (const lr of ltRoles) {
56
+ const has = currentRoles.some((r) => r.role === lr.role);
57
+ if (!has) {
58
+ await (0, roles_1.addUserRole)(userId, lr.role, lr.type);
59
+ }
60
+ }
61
+ const updatedRoles = await (0, roles_1.getUserRoles)(userId);
45
62
  return {
46
- userId: user.id,
47
- roles: (user.roles || []).map((r) => ({ role: r.role, type: r.type })),
48
- created: true,
63
+ userId,
64
+ roles: updatedRoles.map((r) => ({ role: r.role, type: r.type })),
65
+ created: false,
49
66
  };
50
67
  }
51
68
  function resolveRoles(hostRoles, defaultRoleType, roleMap) {
@@ -1,5 +1,12 @@
1
- import type { LTRoleType, LTUserStatus } from '../../types';
1
+ import type { LTReadScope, LTRoleType, LTUserStatus, LTWriteScope } from '../../types';
2
2
  export declare const VALID_ROLE_TYPES: LTRoleType[];
3
+ /** A role grant on user create: management tier + optional work-surface scope. */
4
+ export interface RoleGrantInput {
5
+ role: string;
6
+ type: LTRoleType;
7
+ read_scope?: LTReadScope;
8
+ write_scope?: LTWriteScope;
9
+ }
3
10
  export interface CreateUserInput {
4
11
  external_id: string;
5
12
  email?: string;
@@ -7,10 +14,7 @@ export interface CreateUserInput {
7
14
  password?: string;
8
15
  status?: LTUserStatus;
9
16
  metadata?: Record<string, any>;
10
- roles?: {
11
- role: string;
12
- type: LTRoleType;
13
- }[];
17
+ roles?: RoleGrantInput[];
14
18
  /** OAuth identity link (set during OAuth auto-provisioning). */
15
19
  oauth_provider?: string;
16
20
  oauth_provider_id?: string;
@@ -81,6 +81,9 @@ function registerEscalationTools(server) {
81
81
  'assigned_to, priority), free-text `search`, and sorting. `search` matches ' +
82
82
  'across description, type/subtype, role, workflow/origin id, and metadata values ' +
83
83
  '(e.g. a correlation key like an order id) server-side over the full result set. ' +
84
+ 'Also supports FACETED metadata query — `facets` (metadata @> containment), `block` ' +
85
+ '(exclusion), `range` (numeric), `exists` (key present), `roles`, `available`, and ' +
86
+ '`orderBy` (incl. metadata.<key>) — all role-scoped and run in SQL. ' +
84
87
  'Returns full records including metadata, workflow linkage, assignment, and signal_key.',
85
88
  inputSchema: schemas_1.findEscalationsSchema,
86
89
  }, async (args) => {
@@ -96,6 +99,14 @@ function registerEscalationTools(server) {
96
99
  offset: args.offset,
97
100
  sort_by: args.sort_by,
98
101
  order: args.order,
102
+ // Faceted metadata query (composes with role-scope in SQL).
103
+ roles: args.roles,
104
+ facets: args.facets,
105
+ block: args.block,
106
+ range: args.range,
107
+ exists: args.exists,
108
+ available: args.available,
109
+ orderBy: args.orderBy,
99
110
  }, await systemAuth());
100
111
  if (result.error) {
101
112
  return { content: [{ type: 'text', text: JSON.stringify({ error: result.error }) }], isError: true };
@@ -220,6 +231,60 @@ function registerEscalationTools(server) {
220
231
  }
221
232
  return { content: [{ type: 'text', text: JSON.stringify(result.data) }] };
222
233
  });
234
+ // mirrors POST /api/escalations/resolve-by-ids
235
+ server.registerTool('resolve_by_ids', {
236
+ title: 'Resolve by IDs',
237
+ description: 'Resolve a SET of escalations by id in one guarded statement. RBAC-scoped: ' +
238
+ 'callers may only resolve rows whose role they hold. For bookkeeping rows woken ' +
239
+ 'collectively (no per-row signal delivery).',
240
+ inputSchema: schemas_1.resolveByIdsSchema,
241
+ }, async (args) => {
242
+ const result = await escalationApi.resolveByIds({ ids: args.ids, resolverPayload: args.resolverPayload, metadata: args.metadata }, await systemAuth());
243
+ if (result.error) {
244
+ return { content: [{ type: 'text', text: JSON.stringify({ error: result.error }) }], isError: true };
245
+ }
246
+ return { content: [{ type: 'text', text: JSON.stringify(result.data) }] };
247
+ });
248
+ // mirrors POST /api/escalations/search-by-facets
249
+ server.registerTool('search_by_facets', {
250
+ title: 'Search by Facets',
251
+ description: 'Faceted search over a pond, scoped to the caller\'s role. Filter by status, ' +
252
+ 'availability, and metadata facets; sort by columns; page with limit/offset.',
253
+ inputSchema: schemas_1.searchByFacetsSchema,
254
+ }, async (args) => {
255
+ const result = await escalationApi.searchByFacets(args, await systemAuth());
256
+ if (result.error) {
257
+ return { content: [{ type: 'text', text: JSON.stringify({ error: result.error }) }], isError: true };
258
+ }
259
+ return { content: [{ type: 'text', text: JSON.stringify(result.data) }] };
260
+ });
261
+ // mirrors POST /api/escalations/claim-groups
262
+ server.registerTool('claim_groups', {
263
+ title: 'Claim Groups',
264
+ description: 'Batch-claim complete origin groups (orders) in priority order over a pond, ' +
265
+ 'assigned to the calling principal. RBAC-scoped to the pond role.',
266
+ inputSchema: schemas_1.claimGroupsSchema,
267
+ }, async (args) => {
268
+ const result = await escalationApi.claimGroups({ query: args.query, limit: args.limit, durationMinutes: args.durationMinutes, sizeFacet: args.sizeFacet }, await systemAuth());
269
+ if (result.error) {
270
+ return { content: [{ type: 'text', text: JSON.stringify({ error: result.error }) }], isError: true };
271
+ }
272
+ return { content: [{ type: 'text', text: JSON.stringify(result.data) }] };
273
+ });
274
+ // mirrors POST /api/escalations/claim-by-facets
275
+ server.registerTool('claim_by_facets', {
276
+ title: 'Claim by Facets',
277
+ description: 'Batch-claim individual rows matching a facet query (FOR UPDATE SKIP LOCKED), ' +
278
+ 'assigned to the calling principal. With allOrNone, commits only the full set. ' +
279
+ 'RBAC-scoped to the pond role.',
280
+ inputSchema: schemas_1.claimByFacetsSchema,
281
+ }, async (args) => {
282
+ const result = await escalationApi.claimByFacets({ query: args.query, limit: args.limit, durationMinutes: args.durationMinutes, allOrNone: args.allOrNone }, await systemAuth());
283
+ if (result.error) {
284
+ return { content: [{ type: 'text', text: JSON.stringify({ error: result.error }) }], isError: true };
285
+ }
286
+ return { content: [{ type: 'text', text: JSON.stringify(result.data) }] };
287
+ });
223
288
  // mirrors PATCH /api/escalations/:id/escalate
224
289
  server.registerTool('escalate_escalation', {
225
290
  title: 'Escalate Escalation',