@hotmeshio/long-tail 0.10.2 → 0.10.4

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 (397) hide show
  1. package/README.md +1 -0
  2. package/build/api/bot-accounts.d.ts +4 -0
  3. package/build/api/bot-accounts.js +3 -1
  4. package/build/api/escalations/metadata.js +21 -1
  5. package/build/api/escalations/resolve.d.ts +8 -0
  6. package/build/api/escalations/resolve.js +82 -3
  7. package/build/api/roles.d.ts +2 -0
  8. package/build/api/roles.js +4 -0
  9. package/build/bin/ltc.js +10 -1
  10. package/build/index.js +7 -1
  11. package/build/lib/cli/client.d.ts +4 -1
  12. package/build/lib/cli/client.js +4 -2
  13. package/build/lib/db/schemas/023_role_enforce_schema.sql +18 -0
  14. package/build/lib/typed-resolution.d.ts +35 -0
  15. package/build/lib/typed-resolution.js +37 -0
  16. package/build/modules/branding.d.ts +21 -2
  17. package/build/modules/branding.js +22 -1
  18. package/build/modules/config.d.ts +1 -0
  19. package/build/modules/config.js +5 -0
  20. package/build/routes/bot-accounts.js +3 -1
  21. package/build/routes/settings.js +21 -0
  22. package/build/sdk/index.d.ts +4 -0
  23. package/build/sdk/index.js +14 -0
  24. package/build/services/escalation/crud.d.ts +18 -6
  25. package/build/services/escalation/crud.js +18 -2
  26. package/build/services/escalation/index.d.ts +1 -0
  27. package/build/services/escalation/index.js +1 -0
  28. package/build/services/escalation/resolver-validation.d.ts +28 -0
  29. package/build/services/escalation/resolver-validation.js +95 -0
  30. package/build/services/escalation/sql.d.ts +12 -2
  31. package/build/services/escalation/sql.js +22 -2
  32. package/build/services/iam/bots.d.ts +4 -1
  33. package/build/services/iam/bots.js +5 -3
  34. package/build/services/iam/sql.d.ts +2 -2
  35. package/build/services/iam/sql.js +15 -1
  36. package/build/services/role/enforcement-cache.d.ts +21 -0
  37. package/build/services/role/enforcement-cache.js +0 -0
  38. package/build/services/role/index.js +5 -0
  39. package/build/services/role/sql.d.ts +12 -2
  40. package/build/services/role/sql.js +15 -2
  41. package/build/services/role/types.d.ts +9 -0
  42. package/build/shared/form-validation/derive-field-label.d.ts +14 -0
  43. package/build/shared/form-validation/derive-field-label.js +31 -0
  44. package/build/shared/form-validation/field-validator.d.ts +45 -0
  45. package/build/shared/form-validation/field-validator.js +214 -0
  46. package/build/shared/form-validation/index.d.ts +12 -0
  47. package/build/shared/form-validation/index.js +28 -0
  48. package/build/shared/form-validation/validate-resolver-payload.d.ts +41 -0
  49. package/build/shared/form-validation/validate-resolver-payload.js +62 -0
  50. package/build/shared/form-validation/x-lt-bind.d.ts +41 -0
  51. package/build/shared/form-validation/x-lt-bind.js +110 -0
  52. package/build/shared/form-validation/x-lt-help.d.ts +21 -0
  53. package/build/shared/form-validation/x-lt-help.js +91 -0
  54. package/build/shared/form-validation/x-lt-show-if.d.ts +7 -0
  55. package/build/shared/form-validation/x-lt-show-if.js +84 -0
  56. package/build/system/mcp-servers/admin/schemas.d.ts +3 -0
  57. package/build/system/mcp-servers/admin/schemas.js +1 -0
  58. package/build/system/mcp-servers/admin/users.js +3 -1
  59. package/build/system/mcp-servers/human-queue-schemas.d.ts +4 -4
  60. package/build/system/mcp-servers/human-queue-schemas.js +4 -1
  61. package/build/system/mcp-servers/human-queue.js +41 -2
  62. package/build/tsconfig.tsbuildinfo +1 -1
  63. package/build/types/sdk.d.ts +6 -1
  64. package/build/types/startup.d.ts +26 -0
  65. package/build/types/validation.d.ts +43 -0
  66. package/build/types/validation.js +28 -0
  67. package/dashboard/dist/assets/{AdminDashboard-Dy031e9g.js → AdminDashboard-Cma2NGsF.js} +2 -2
  68. package/dashboard/dist/assets/{AdminDashboard-Dy031e9g.js.map → AdminDashboard-Cma2NGsF.js.map} +1 -1
  69. package/dashboard/dist/assets/AgentConfigPage-CbIhM-fa.js +16 -0
  70. package/dashboard/dist/assets/AgentConfigPage-CbIhM-fa.js.map +1 -0
  71. package/dashboard/dist/assets/AgentDetailPage-DuLEKzRT.js +4 -0
  72. package/dashboard/dist/assets/AgentDetailPage-DuLEKzRT.js.map +1 -0
  73. package/dashboard/dist/assets/AgentsPage-DZO9i7Nl.js +2 -0
  74. package/dashboard/dist/assets/AgentsPage-DZO9i7Nl.js.map +1 -0
  75. package/dashboard/dist/assets/AvailableEscalationsPage-DlVoNd8B.js +2 -0
  76. package/dashboard/dist/assets/AvailableEscalationsPage-DlVoNd8B.js.map +1 -0
  77. package/dashboard/dist/assets/{BotPicker-2QEIpc-K.js → BotPicker-CidlGr6f.js} +2 -2
  78. package/dashboard/dist/assets/{BotPicker-2QEIpc-K.js.map → BotPicker-CidlGr6f.js.map} +1 -1
  79. package/dashboard/dist/assets/CapabilitiesPage-BMNNTa0D.js +2 -0
  80. package/dashboard/dist/assets/{CapabilitiesPage-lQJGZ_-t.js.map → CapabilitiesPage-BMNNTa0D.js.map} +1 -1
  81. package/dashboard/dist/assets/{CollapsibleSection-CHWP0WI6.js → CollapsibleSection-W9F9lziT.js} +2 -2
  82. package/dashboard/dist/assets/{CollapsibleSection-CHWP0WI6.js.map → CollapsibleSection-W9F9lziT.js.map} +1 -1
  83. package/dashboard/dist/assets/ConfirmDeleteModal-BUh-Dzba.js +2 -0
  84. package/dashboard/dist/assets/{ConfirmDeleteModal-C0x0HcHX.js.map → ConfirmDeleteModal-BUh-Dzba.js.map} +1 -1
  85. package/dashboard/dist/assets/{CopyableId-uot2mE_g.js → CopyableId-B1mxKfVb.js} +2 -2
  86. package/dashboard/dist/assets/{CopyableId-uot2mE_g.js.map → CopyableId-B1mxKfVb.js.map} +1 -1
  87. package/dashboard/dist/assets/CountdownTimer-lkv_7msa.js +2 -0
  88. package/dashboard/dist/assets/{CountdownTimer-TXsRlTU4.js.map → CountdownTimer-lkv_7msa.js.map} +1 -1
  89. package/dashboard/dist/assets/{CredentialsPage-BKxS-1sA.js → CredentialsPage-DyOgfnPB.js} +2 -2
  90. package/dashboard/dist/assets/CredentialsPage-DyOgfnPB.js.map +1 -0
  91. package/dashboard/dist/assets/{CronLabel-Bd2G0Rap.js → CronLabel-geum95dj.js} +2 -2
  92. package/dashboard/dist/assets/{CronLabel-Bd2G0Rap.js.map → CronLabel-geum95dj.js.map} +1 -1
  93. package/dashboard/dist/assets/CustomDurationPicker-ClufO2ew.js +2 -0
  94. package/dashboard/dist/assets/CustomDurationPicker-ClufO2ew.js.map +1 -0
  95. package/dashboard/dist/assets/DataTable-BTu2AaEl.js +2 -0
  96. package/dashboard/dist/assets/DataTable-BTu2AaEl.js.map +1 -0
  97. package/dashboard/dist/assets/{DropZone-DNDEgMpJ.js → DropZone-C3hOjJtC.js} +2 -2
  98. package/dashboard/dist/assets/{DropZone-DNDEgMpJ.js.map → DropZone-C3hOjJtC.js.map} +1 -1
  99. package/dashboard/dist/assets/{ElapsedCell-g7GO7f8L.js → ElapsedCell-DFOW9T4b.js} +2 -2
  100. package/dashboard/dist/assets/{ElapsedCell-g7GO7f8L.js.map → ElapsedCell-DFOW9T4b.js.map} +1 -1
  101. package/dashboard/dist/assets/EscalationListSchemaPage-_IhqXIQu.js +20 -0
  102. package/dashboard/dist/assets/EscalationListSchemaPage-_IhqXIQu.js.map +1 -0
  103. package/dashboard/dist/assets/EscalationSchemaPage-BCC7iscN.js +9 -0
  104. package/dashboard/dist/assets/EscalationSchemaPage-BCC7iscN.js.map +1 -0
  105. package/dashboard/dist/assets/EscalationsOverview-CvIdOtoP.js +2 -0
  106. package/dashboard/dist/assets/EscalationsOverview-CvIdOtoP.js.map +1 -0
  107. package/dashboard/dist/assets/EventTable-DJWJ7J0m.js +2 -0
  108. package/dashboard/dist/assets/EventTable-DJWJ7J0m.js.map +1 -0
  109. package/dashboard/dist/assets/FilterBar-BR3ztZ8y.js +2 -0
  110. package/dashboard/dist/assets/FilterBar-BR3ztZ8y.js.map +1 -0
  111. package/dashboard/dist/assets/GraphInvokePage-Cwn-8Glr.js +2 -0
  112. package/dashboard/dist/assets/GraphInvokePage-Cwn-8Glr.js.map +1 -0
  113. package/dashboard/dist/assets/HomePage-D8RVntg0.js +2 -0
  114. package/dashboard/dist/assets/HomePage-D8RVntg0.js.map +1 -0
  115. package/dashboard/dist/assets/ListToolbar-DuoEdVtK.js +2 -0
  116. package/dashboard/dist/assets/ListToolbar-DuoEdVtK.js.map +1 -0
  117. package/dashboard/dist/assets/McpOverview-NXKsGiWU.js +2 -0
  118. package/dashboard/dist/assets/McpOverview-NXKsGiWU.js.map +1 -0
  119. package/dashboard/dist/assets/McpQueryDetailPage-BC-iXfIx.js +5 -0
  120. package/dashboard/dist/assets/McpQueryDetailPage-BC-iXfIx.js.map +1 -0
  121. package/dashboard/dist/assets/{McpQueryPage-CsKNXUhb.js → McpQueryPage-BPJmFYOY.js} +2 -2
  122. package/dashboard/dist/assets/{McpQueryPage-CsKNXUhb.js.map → McpQueryPage-BPJmFYOY.js.map} +1 -1
  123. package/dashboard/dist/assets/McpRunDetailPage-B49nUXEl.js +2 -0
  124. package/dashboard/dist/assets/McpRunDetailPage-B49nUXEl.js.map +1 -0
  125. package/dashboard/dist/assets/McpRunsPage-Ce_XIYpP.js +2 -0
  126. package/dashboard/dist/assets/McpRunsPage-Ce_XIYpP.js.map +1 -0
  127. package/dashboard/dist/assets/NamespacePill-LWvZXt_g.js +2 -0
  128. package/dashboard/dist/assets/NamespacePill-LWvZXt_g.js.map +1 -0
  129. package/dashboard/dist/assets/OperationsPage-DI7AQO9f.js +2 -0
  130. package/dashboard/dist/assets/OperationsPage-DI7AQO9f.js.map +1 -0
  131. package/dashboard/dist/assets/OperatorDashboard-DpXjhs-3.js +2 -0
  132. package/dashboard/dist/assets/{OperatorDashboard-_axS17fp.js.map → OperatorDashboard-DpXjhs-3.js.map} +1 -1
  133. package/dashboard/dist/assets/PageHeader-DvEmMMP6.js +2 -0
  134. package/dashboard/dist/assets/PageHeader-DvEmMMP6.js.map +1 -0
  135. package/dashboard/dist/assets/PageHeaderWithStats-X0eAmu-x.js +2 -0
  136. package/dashboard/dist/assets/PageHeaderWithStats-X0eAmu-x.js.map +1 -0
  137. package/dashboard/dist/assets/{PriorityBadge-B4ykBH7f.js → PriorityBadge-CCERr9vQ.js} +2 -2
  138. package/dashboard/dist/assets/PriorityBadge-CCERr9vQ.js.map +1 -0
  139. package/dashboard/dist/assets/ProcessDetailPage-DpYSMRj1.js +2 -0
  140. package/dashboard/dist/assets/ProcessDetailPage-DpYSMRj1.js.map +1 -0
  141. package/dashboard/dist/assets/{ProcessesListPage-CoQEi_Su.js → ProcessesListPage-CQ9tSw7U.js} +2 -2
  142. package/dashboard/dist/assets/{ProcessesListPage-CoQEi_Su.js.map → ProcessesListPage-CQ9tSw7U.js.map} +1 -1
  143. package/dashboard/dist/assets/RoleDetailPage-DKeQKDkW.js +8 -0
  144. package/dashboard/dist/assets/RoleDetailPage-DKeQKDkW.js.map +1 -0
  145. package/dashboard/dist/assets/RolePill-CWhoLwef.js +2 -0
  146. package/dashboard/dist/assets/RolePill-CWhoLwef.js.map +1 -0
  147. package/dashboard/dist/assets/RolesPage-D-6kIact.js +2 -0
  148. package/dashboard/dist/assets/RolesPage-D-6kIact.js.map +1 -0
  149. package/dashboard/dist/assets/RunAsSelector-DzYcpQTm.js +2 -0
  150. package/dashboard/dist/assets/RunAsSelector-DzYcpQTm.js.map +1 -0
  151. package/dashboard/dist/assets/StatCard-DBANWIss.js +2 -0
  152. package/dashboard/dist/assets/StatCard-DBANWIss.js.map +1 -0
  153. package/dashboard/dist/assets/StatusBadge-BbqghFMi.js +2 -0
  154. package/dashboard/dist/assets/StatusBadge-BbqghFMi.js.map +1 -0
  155. package/dashboard/dist/assets/{StickyPagination-Cd83AHd_.js → StickyPagination-BTGfmGV3.js} +2 -2
  156. package/dashboard/dist/assets/{StickyPagination-Cd83AHd_.js.map → StickyPagination-BTGfmGV3.js.map} +1 -1
  157. package/dashboard/dist/assets/StreamMessageDetail-DqRpzA_6.js +2 -0
  158. package/dashboard/dist/assets/StreamMessageDetail-DqRpzA_6.js.map +1 -0
  159. package/dashboard/dist/assets/SwimlaneTimeline-CA106TYo.js +2 -0
  160. package/dashboard/dist/assets/SwimlaneTimeline-CA106TYo.js.map +1 -0
  161. package/dashboard/dist/assets/TagInput-CoR4MzsW.js +2 -0
  162. package/dashboard/dist/assets/TagInput-CoR4MzsW.js.map +1 -0
  163. package/dashboard/dist/assets/{TaskDetailPage-DnjhW7qv.js → TaskDetailPage-BRg-w2Ev.js} +2 -2
  164. package/dashboard/dist/assets/{TaskDetailPage-DnjhW7qv.js.map → TaskDetailPage-BRg-w2Ev.js.map} +1 -1
  165. package/dashboard/dist/assets/TaskQueuePill-Dw3YN8M-.js +2 -0
  166. package/dashboard/dist/assets/TaskQueuePill-Dw3YN8M-.js.map +1 -0
  167. package/dashboard/dist/assets/{TasksListPage-DySfZFgs.js → TasksListPage-kB_j9G8o.js} +2 -2
  168. package/dashboard/dist/assets/{TasksListPage-DySfZFgs.js.map → TasksListPage-kB_j9G8o.js.map} +1 -1
  169. package/dashboard/dist/assets/{TimeAgo-BKOl-7Tt.js → TimeAgo-D7dSIY7-.js} +2 -2
  170. package/dashboard/dist/assets/{TimeAgo-BKOl-7Tt.js.map → TimeAgo-D7dSIY7-.js.map} +1 -1
  171. package/dashboard/dist/assets/TimestampCell-BZe6bx18.js +2 -0
  172. package/dashboard/dist/assets/TimestampCell-BZe6bx18.js.map +1 -0
  173. package/dashboard/dist/assets/ToolPill-xX-buQtY.js +2 -0
  174. package/dashboard/dist/assets/ToolPill-xX-buQtY.js.map +1 -0
  175. package/dashboard/dist/assets/ToolTestPanel-U3NaF_T_.js +2 -0
  176. package/dashboard/dist/assets/ToolTestPanel-U3NaF_T_.js.map +1 -0
  177. package/dashboard/dist/assets/TopicDetailPage-BpALvXP1.js +9 -0
  178. package/dashboard/dist/assets/TopicDetailPage-BpALvXP1.js.map +1 -0
  179. package/dashboard/dist/assets/TopicsPage-Cy3chaca.js +2 -0
  180. package/dashboard/dist/assets/{TopicsPage-5yW63ELl.js.map → TopicsPage-Cy3chaca.js.map} +1 -1
  181. package/dashboard/dist/assets/{UserName-DcXhOSVm.js → UserName-VXM1oHMC.js} +2 -2
  182. package/dashboard/dist/assets/{UserName-DcXhOSVm.js.map → UserName-VXM1oHMC.js.map} +1 -1
  183. package/dashboard/dist/assets/WorkflowExecutionPage-BCq2cR1V.js +2 -0
  184. package/dashboard/dist/assets/WorkflowExecutionPage-BCq2cR1V.js.map +1 -0
  185. package/dashboard/dist/assets/WorkflowPill-CQ1AmhlC.js +2 -0
  186. package/dashboard/dist/assets/WorkflowPill-CQ1AmhlC.js.map +1 -0
  187. package/dashboard/dist/assets/{WorkflowsDashboard-C0Bbrd5M.js → WorkflowsDashboard-BbM69MnB.js} +2 -2
  188. package/dashboard/dist/assets/{WorkflowsDashboard-C0Bbrd5M.js.map → WorkflowsDashboard-BbM69MnB.js.map} +1 -1
  189. package/dashboard/dist/assets/WorkflowsOverview-D5HthpAW.js +2 -0
  190. package/dashboard/dist/assets/WorkflowsOverview-D5HthpAW.js.map +1 -0
  191. package/dashboard/dist/assets/{YamlWorkflowDetailPage-BcbrYhDI.js → YamlWorkflowDetailPage-CDDVMYWi.js} +10 -10
  192. package/dashboard/dist/assets/{YamlWorkflowDetailPage-BcbrYhDI.js.map → YamlWorkflowDetailPage-CDDVMYWi.js.map} +1 -1
  193. package/dashboard/dist/assets/YamlWorkflowsPage-FFjWwydj.js +2 -0
  194. package/dashboard/dist/assets/YamlWorkflowsPage-FFjWwydj.js.map +1 -0
  195. package/dashboard/dist/assets/{agents-CopQ3t5o.js → agents-BwiSNu73.js} +2 -2
  196. package/dashboard/dist/assets/{agents-CopQ3t5o.js.map → agents-BwiSNu73.js.map} +1 -1
  197. package/dashboard/dist/assets/bots-ClgSrxel.js +2 -0
  198. package/dashboard/dist/assets/bots-ClgSrxel.js.map +1 -0
  199. package/dashboard/dist/assets/{capabilities-5JRieYUy.js → capabilities-JUaKsby4.js} +2 -2
  200. package/dashboard/dist/assets/{capabilities-5JRieYUy.js.map → capabilities-JUaKsby4.js.map} +1 -1
  201. package/dashboard/dist/assets/{controlplane-B4rT6tIw.js → controlplane-CuTQ7eUE.js} +2 -2
  202. package/dashboard/dist/assets/{controlplane-B4rT6tIw.js.map → controlplane-CuTQ7eUE.js.map} +1 -1
  203. package/dashboard/dist/assets/escalation-columns-CS0c7cAr.js +2 -0
  204. package/dashboard/dist/assets/escalation-columns-CS0c7cAr.js.map +1 -0
  205. package/dashboard/dist/assets/index-BBA4AxmL.js +2 -0
  206. package/dashboard/dist/assets/index-BBA4AxmL.js.map +1 -0
  207. package/dashboard/dist/assets/index-C-8xXyvr.js +63 -0
  208. package/dashboard/dist/assets/index-C-8xXyvr.js.map +1 -0
  209. package/dashboard/dist/assets/index-C1t0YIXC.js +5 -0
  210. package/dashboard/dist/assets/index-C1t0YIXC.js.map +1 -0
  211. package/dashboard/dist/assets/index-C9o2UNhC.js +2 -0
  212. package/dashboard/dist/assets/index-C9o2UNhC.js.map +1 -0
  213. package/dashboard/dist/assets/index-CBj-vstO.js +2 -0
  214. package/dashboard/dist/assets/{index-Dd1RxGNw.js.map → index-CBj-vstO.js.map} +1 -1
  215. package/dashboard/dist/assets/index-CUguKMXZ.js +2 -0
  216. package/dashboard/dist/assets/index-CUguKMXZ.js.map +1 -0
  217. package/dashboard/dist/assets/index-Cavwd8UU.js +6 -0
  218. package/dashboard/dist/assets/index-Cavwd8UU.js.map +1 -0
  219. package/dashboard/dist/assets/index-CnsPBTXL.js +2 -0
  220. package/dashboard/dist/assets/index-CnsPBTXL.js.map +1 -0
  221. package/dashboard/dist/assets/index-CoPWHo_H.js +2 -0
  222. package/dashboard/dist/assets/index-CoPWHo_H.js.map +1 -0
  223. package/dashboard/dist/assets/index-CwQVaa7x.js +2 -0
  224. package/dashboard/dist/assets/index-CwQVaa7x.js.map +1 -0
  225. package/dashboard/dist/assets/index-DSa-6Ux8.js +2 -0
  226. package/dashboard/dist/assets/index-DSa-6Ux8.js.map +1 -0
  227. package/dashboard/dist/assets/index-DeMs_aBT.css +1 -0
  228. package/dashboard/dist/assets/index-Ikj_NTuy.js +5 -0
  229. package/dashboard/dist/assets/index-Ikj_NTuy.js.map +1 -0
  230. package/dashboard/dist/assets/index-Xd1jcHe7.js +2 -0
  231. package/dashboard/dist/assets/index-Xd1jcHe7.js.map +1 -0
  232. package/dashboard/dist/assets/{knowledge-B9bmoXOe.js → knowledge-BHBt-6kW.js} +2 -2
  233. package/dashboard/dist/assets/{knowledge-B9bmoXOe.js.map → knowledge-BHBt-6kW.js.map} +1 -1
  234. package/dashboard/dist/assets/{mcp-DYMFBOFz.js → mcp-CBcz4BCS.js} +2 -2
  235. package/dashboard/dist/assets/{mcp-DYMFBOFz.js.map → mcp-CBcz4BCS.js.map} +1 -1
  236. package/dashboard/dist/assets/{mcp-query-wloSpyPM.js → mcp-query-C4fKTOYo.js} +2 -2
  237. package/dashboard/dist/assets/{mcp-query-wloSpyPM.js.map → mcp-query-C4fKTOYo.js.map} +1 -1
  238. package/dashboard/dist/assets/{pipelines-DBXX6TEi.js → pipelines-CmAAH3B6.js} +2 -2
  239. package/dashboard/dist/assets/{pipelines-DBXX6TEi.js.map → pipelines-CmAAH3B6.js.map} +1 -1
  240. package/dashboard/dist/assets/role-display-Dvo2lDpU.js +2 -0
  241. package/dashboard/dist/assets/role-display-Dvo2lDpU.js.map +1 -0
  242. package/dashboard/dist/assets/task-queues-7-sHKPsN.js +2 -0
  243. package/dashboard/dist/assets/task-queues-7-sHKPsN.js.map +1 -0
  244. package/dashboard/dist/assets/{tasks-CD3-C7tP.js → tasks-BAEsqcz2.js} +2 -2
  245. package/dashboard/dist/assets/{tasks-CD3-C7tP.js.map → tasks-BAEsqcz2.js.map} +1 -1
  246. package/dashboard/dist/assets/{topics-D8pKi6zA.js → topics-CY0pJ6_o.js} +2 -2
  247. package/dashboard/dist/assets/{topics-D8pKi6zA.js.map → topics-CY0pJ6_o.js.map} +1 -1
  248. package/dashboard/dist/assets/{useEventHooks-DV9pLDOW.js → useEventHooks-BlpwRvHO.js} +2 -2
  249. package/dashboard/dist/assets/{useEventHooks-DV9pLDOW.js.map → useEventHooks-BlpwRvHO.js.map} +1 -1
  250. package/dashboard/dist/assets/{useNamespace-DykYhZY9.js → useNamespace-CoNUlZ6Z.js} +2 -2
  251. package/dashboard/dist/assets/{useNamespace-DykYhZY9.js.map → useNamespace-CoNUlZ6Z.js.map} +1 -1
  252. package/dashboard/dist/assets/{useYamlActivityEvents-By044UAv.js → useYamlActivityEvents-8GUn5Wie.js} +2 -2
  253. package/dashboard/dist/assets/{useYamlActivityEvents-By044UAv.js.map → useYamlActivityEvents-8GUn5Wie.js.map} +1 -1
  254. package/dashboard/dist/assets/{users-mI9goDjj.js → users-uGtDRGxR.js} +2 -2
  255. package/dashboard/dist/assets/{users-mI9goDjj.js.map → users-uGtDRGxR.js.map} +1 -1
  256. package/dashboard/dist/assets/{vendor-icons-ihWGa7_d.js → vendor-icons-DKCAtTJ9.js} +135 -130
  257. package/dashboard/dist/assets/vendor-icons-DKCAtTJ9.js.map +1 -0
  258. package/dashboard/dist/assets/{workflows-DCUilz68.js → workflows-DoX-AtHN.js} +2 -2
  259. package/dashboard/dist/assets/{workflows-DCUilz68.js.map → workflows-DoX-AtHN.js.map} +1 -1
  260. package/dashboard/dist/assets/{yaml-workflows-DWNMP1PT.js → yaml-workflows-DM3jVem2.js} +2 -2
  261. package/dashboard/dist/assets/{yaml-workflows-DWNMP1PT.js.map → yaml-workflows-DM3jVem2.js.map} +1 -1
  262. package/dashboard/dist/index.html +3 -3
  263. package/docs/api/http/escalations.md +15 -1
  264. package/docs/api/http/roles.md +2 -0
  265. package/docs/design-principles.md +234 -0
  266. package/docs/hitl/design.md +228 -0
  267. package/docs/hitl/form.md +21 -1
  268. package/docs/hitl/iframe.md +1 -1
  269. package/docs/hitl/x-lt-display.md +56 -0
  270. package/docs/hitl/x-lt-layout.md +20 -1
  271. package/docs/hitl/x-lt-list-schema.md +8 -1
  272. package/docs/hitl/x-lt-widget.md +23 -0
  273. package/docs/hitl-guide.md +4 -2
  274. package/docs/schema-enforcement.md +149 -0
  275. package/package.json +1 -1
  276. package/dashboard/dist/assets/AgentConfigPage-CqmtYBbC.js +0 -16
  277. package/dashboard/dist/assets/AgentConfigPage-CqmtYBbC.js.map +0 -1
  278. package/dashboard/dist/assets/AgentDetailPage-oYLBrN74.js +0 -4
  279. package/dashboard/dist/assets/AgentDetailPage-oYLBrN74.js.map +0 -1
  280. package/dashboard/dist/assets/AgentsPage-CHczRx5N.js +0 -2
  281. package/dashboard/dist/assets/AgentsPage-CHczRx5N.js.map +0 -1
  282. package/dashboard/dist/assets/AvailableEscalationsPage-DDVuivEE.js +0 -2
  283. package/dashboard/dist/assets/AvailableEscalationsPage-DDVuivEE.js.map +0 -1
  284. package/dashboard/dist/assets/CapabilitiesPage-lQJGZ_-t.js +0 -2
  285. package/dashboard/dist/assets/ConfirmDeleteModal-C0x0HcHX.js +0 -2
  286. package/dashboard/dist/assets/CountdownTimer-TXsRlTU4.js +0 -2
  287. package/dashboard/dist/assets/CredentialsPage-BKxS-1sA.js.map +0 -1
  288. package/dashboard/dist/assets/CustomDurationPicker-j4bBrDJ6.js +0 -2
  289. package/dashboard/dist/assets/CustomDurationPicker-j4bBrDJ6.js.map +0 -1
  290. package/dashboard/dist/assets/DataTable-Byn3oyT9.js +0 -2
  291. package/dashboard/dist/assets/DataTable-Byn3oyT9.js.map +0 -1
  292. package/dashboard/dist/assets/EscalationListSchemaPage-CIJ-OgVC.js +0 -20
  293. package/dashboard/dist/assets/EscalationListSchemaPage-CIJ-OgVC.js.map +0 -1
  294. package/dashboard/dist/assets/EscalationSchemaPage-C7dHgG8e.js +0 -9
  295. package/dashboard/dist/assets/EscalationSchemaPage-C7dHgG8e.js.map +0 -1
  296. package/dashboard/dist/assets/EscalationsOverview-D8NrATg2.js +0 -2
  297. package/dashboard/dist/assets/EscalationsOverview-D8NrATg2.js.map +0 -1
  298. package/dashboard/dist/assets/EventTable-DBV1P2ME.js +0 -2
  299. package/dashboard/dist/assets/EventTable-DBV1P2ME.js.map +0 -1
  300. package/dashboard/dist/assets/FilterBar-B5Vk5tBF.js +0 -2
  301. package/dashboard/dist/assets/FilterBar-B5Vk5tBF.js.map +0 -1
  302. package/dashboard/dist/assets/GraphInvokePage-DbHvggQ_.js +0 -2
  303. package/dashboard/dist/assets/GraphInvokePage-DbHvggQ_.js.map +0 -1
  304. package/dashboard/dist/assets/HomePage-BpexnDO_.js +0 -2
  305. package/dashboard/dist/assets/HomePage-BpexnDO_.js.map +0 -1
  306. package/dashboard/dist/assets/ListToolbar-KC6LGtKP.js +0 -2
  307. package/dashboard/dist/assets/ListToolbar-KC6LGtKP.js.map +0 -1
  308. package/dashboard/dist/assets/McpOverview-BDZC9Hvl.js +0 -2
  309. package/dashboard/dist/assets/McpOverview-BDZC9Hvl.js.map +0 -1
  310. package/dashboard/dist/assets/McpQueryDetailPage-CXhgX1pS.js +0 -5
  311. package/dashboard/dist/assets/McpQueryDetailPage-CXhgX1pS.js.map +0 -1
  312. package/dashboard/dist/assets/McpRunDetailPage-BM3mUSlH.js +0 -2
  313. package/dashboard/dist/assets/McpRunDetailPage-BM3mUSlH.js.map +0 -1
  314. package/dashboard/dist/assets/McpRunsPage-f5hDbpLo.js +0 -2
  315. package/dashboard/dist/assets/McpRunsPage-f5hDbpLo.js.map +0 -1
  316. package/dashboard/dist/assets/NamespacePill-C4u6Go1W.js +0 -2
  317. package/dashboard/dist/assets/NamespacePill-C4u6Go1W.js.map +0 -1
  318. package/dashboard/dist/assets/OperationsPage-D37cZ8Qh.js +0 -2
  319. package/dashboard/dist/assets/OperationsPage-D37cZ8Qh.js.map +0 -1
  320. package/dashboard/dist/assets/OperatorDashboard-_axS17fp.js +0 -2
  321. package/dashboard/dist/assets/PageHeader-Bxa1XFz0.js +0 -2
  322. package/dashboard/dist/assets/PageHeader-Bxa1XFz0.js.map +0 -1
  323. package/dashboard/dist/assets/PageHeaderWithStats-D_sO0MqJ.js +0 -2
  324. package/dashboard/dist/assets/PageHeaderWithStats-D_sO0MqJ.js.map +0 -1
  325. package/dashboard/dist/assets/PriorityBadge-B4ykBH7f.js.map +0 -1
  326. package/dashboard/dist/assets/ProcessDetailPage-D8pAWUXx.js +0 -2
  327. package/dashboard/dist/assets/ProcessDetailPage-D8pAWUXx.js.map +0 -1
  328. package/dashboard/dist/assets/RoleDetailPage-ufbypNNR.js +0 -8
  329. package/dashboard/dist/assets/RoleDetailPage-ufbypNNR.js.map +0 -1
  330. package/dashboard/dist/assets/RolePill-DLwmVMam.js +0 -2
  331. package/dashboard/dist/assets/RolePill-DLwmVMam.js.map +0 -1
  332. package/dashboard/dist/assets/RolesPage-CxpXm4Sw.js +0 -2
  333. package/dashboard/dist/assets/RolesPage-CxpXm4Sw.js.map +0 -1
  334. package/dashboard/dist/assets/RunAsSelector-BebF8Lib.js +0 -2
  335. package/dashboard/dist/assets/RunAsSelector-BebF8Lib.js.map +0 -1
  336. package/dashboard/dist/assets/SlidePanel-BDVDuwIf.js +0 -2
  337. package/dashboard/dist/assets/SlidePanel-BDVDuwIf.js.map +0 -1
  338. package/dashboard/dist/assets/StatCard-DrdlUeQz.js +0 -2
  339. package/dashboard/dist/assets/StatCard-DrdlUeQz.js.map +0 -1
  340. package/dashboard/dist/assets/StatusBadge-CEQMRmSQ.js +0 -2
  341. package/dashboard/dist/assets/StatusBadge-CEQMRmSQ.js.map +0 -1
  342. package/dashboard/dist/assets/StreamMessageDetail-BIXbrZM0.js +0 -2
  343. package/dashboard/dist/assets/StreamMessageDetail-BIXbrZM0.js.map +0 -1
  344. package/dashboard/dist/assets/SwimlaneTimeline-CC5qZjYo.js +0 -2
  345. package/dashboard/dist/assets/SwimlaneTimeline-CC5qZjYo.js.map +0 -1
  346. package/dashboard/dist/assets/TagInput-C2gi47M8.js +0 -2
  347. package/dashboard/dist/assets/TagInput-C2gi47M8.js.map +0 -1
  348. package/dashboard/dist/assets/TaskQueuePill-BPP0Almh.js +0 -2
  349. package/dashboard/dist/assets/TaskQueuePill-BPP0Almh.js.map +0 -1
  350. package/dashboard/dist/assets/TimestampCell-DGOsba-A.js +0 -2
  351. package/dashboard/dist/assets/TimestampCell-DGOsba-A.js.map +0 -1
  352. package/dashboard/dist/assets/ToolPill-D74TkYB-.js +0 -2
  353. package/dashboard/dist/assets/ToolPill-D74TkYB-.js.map +0 -1
  354. package/dashboard/dist/assets/ToolTestPanel-Bez6OyRL.js +0 -2
  355. package/dashboard/dist/assets/ToolTestPanel-Bez6OyRL.js.map +0 -1
  356. package/dashboard/dist/assets/TopicDetailPage-BHccgkU_.js +0 -9
  357. package/dashboard/dist/assets/TopicDetailPage-BHccgkU_.js.map +0 -1
  358. package/dashboard/dist/assets/TopicsPage-5yW63ELl.js +0 -2
  359. package/dashboard/dist/assets/WorkflowExecutionPage-CDDLled5.js +0 -2
  360. package/dashboard/dist/assets/WorkflowExecutionPage-CDDLled5.js.map +0 -1
  361. package/dashboard/dist/assets/WorkflowPill-Fjc2q9U9.js +0 -2
  362. package/dashboard/dist/assets/WorkflowPill-Fjc2q9U9.js.map +0 -1
  363. package/dashboard/dist/assets/WorkflowsOverview-D8MkSMex.js +0 -2
  364. package/dashboard/dist/assets/WorkflowsOverview-D8MkSMex.js.map +0 -1
  365. package/dashboard/dist/assets/YamlWorkflowsPage-9Ki54d-9.js +0 -2
  366. package/dashboard/dist/assets/YamlWorkflowsPage-9Ki54d-9.js.map +0 -1
  367. package/dashboard/dist/assets/bots-CAYJYL9E.js +0 -2
  368. package/dashboard/dist/assets/bots-CAYJYL9E.js.map +0 -1
  369. package/dashboard/dist/assets/escalation-columns-BbS5X61l.js +0 -2
  370. package/dashboard/dist/assets/escalation-columns-BbS5X61l.js.map +0 -1
  371. package/dashboard/dist/assets/index-B68A3v-S.js +0 -2
  372. package/dashboard/dist/assets/index-B68A3v-S.js.map +0 -1
  373. package/dashboard/dist/assets/index-BAi89Xpm.js +0 -2
  374. package/dashboard/dist/assets/index-BAi89Xpm.js.map +0 -1
  375. package/dashboard/dist/assets/index-BJ0jjFsI.js +0 -5
  376. package/dashboard/dist/assets/index-BJ0jjFsI.js.map +0 -1
  377. package/dashboard/dist/assets/index-BOJMUk4F.js +0 -2
  378. package/dashboard/dist/assets/index-BOJMUk4F.js.map +0 -1
  379. package/dashboard/dist/assets/index-BrgYykPV.js +0 -2
  380. package/dashboard/dist/assets/index-BrgYykPV.js.map +0 -1
  381. package/dashboard/dist/assets/index-BxgtIqvl.js +0 -2
  382. package/dashboard/dist/assets/index-BxgtIqvl.js.map +0 -1
  383. package/dashboard/dist/assets/index-C0cHXaX-.css +0 -1
  384. package/dashboard/dist/assets/index-CDHDoHYz.js +0 -6
  385. package/dashboard/dist/assets/index-CDHDoHYz.js.map +0 -1
  386. package/dashboard/dist/assets/index-CUZJHXew.js +0 -2
  387. package/dashboard/dist/assets/index-CUZJHXew.js.map +0 -1
  388. package/dashboard/dist/assets/index-D5n_e59T.js +0 -2
  389. package/dashboard/dist/assets/index-D5n_e59T.js.map +0 -1
  390. package/dashboard/dist/assets/index-DIgiIoou.js +0 -63
  391. package/dashboard/dist/assets/index-DIgiIoou.js.map +0 -1
  392. package/dashboard/dist/assets/index-Dd1RxGNw.js +0 -2
  393. package/dashboard/dist/assets/index-iOb3sGsC.js +0 -5
  394. package/dashboard/dist/assets/index-iOb3sGsC.js.map +0 -1
  395. package/dashboard/dist/assets/index-mcAkQblr.js +0 -2
  396. package/dashboard/dist/assets/index-mcAkQblr.js.map +0 -1
  397. package/dashboard/dist/assets/vendor-icons-ihWGa7_d.js.map +0 -1
package/README.md CHANGED
@@ -231,6 +231,7 @@ All modes share PostgreSQL and scale independently. See [Cloud Deployment](https
231
231
  | [Compiler](https://github.com/hotmeshio/long-tail/blob/main/docs/compiler.md) | `ltc compile` — durable TypeScript to YAML DAGs |
232
232
  | [CLI](https://github.com/hotmeshio/long-tail/blob/main/docs/cli.md) | `ltc` — terminal access to workflows, escalations, knowledge, MCP |
233
233
  | [Escalation Strategies](https://github.com/hotmeshio/long-tail/blob/main/docs/escalation-strategies.md) | Default, MCP triage, custom handlers |
234
+ | [Schema Enforcement](https://github.com/hotmeshio/long-tail/blob/main/docs/schema-enforcement.md) | form_schema as an enforced API contract on every resolve surface |
234
235
  | [Faceted Routing](https://github.com/hotmeshio/long-tail/blob/main/docs/faceted-routing.md) | Query and atomically claim the queue by facets; dispatcher pattern |
235
236
  | [SDK](https://github.com/hotmeshio/long-tail/blob/main/docs/sdk.md) | Embedded usage, `createClient`, event subscriptions |
236
237
  | [Architecture](https://github.com/hotmeshio/long-tail/blob/main/docs/architecture.md) | Project structure, conventions, discovery |
@@ -4,11 +4,15 @@ import type { LTApiResult, LTApiAuth } from '../types/sdk';
4
4
  *
5
5
  * @param input.limit — maximum number of bots to return (default 50)
6
6
  * @param input.offset — number of bots to skip for pagination (default 0)
7
+ * @param input.status — filter by account status
8
+ * @param input.search — free-text match on display name, external id, or description
7
9
  * @returns `{ status: 200, data: Bot[] }` paginated list of bots
8
10
  */
9
11
  export declare function listBots(input: {
10
12
  limit?: number;
11
13
  offset?: number;
14
+ status?: string;
15
+ search?: string;
12
16
  }): Promise<LTApiResult>;
13
17
  /**
14
18
  * Retrieve a single bot account by ID.
@@ -51,13 +51,15 @@ const user_1 = require("../services/user");
51
51
  *
52
52
  * @param input.limit — maximum number of bots to return (default 50)
53
53
  * @param input.offset — number of bots to skip for pagination (default 0)
54
+ * @param input.status — filter by account status
55
+ * @param input.search — free-text match on display name, external id, or description
54
56
  * @returns `{ status: 200, data: Bot[] }` paginated list of bots
55
57
  */
56
58
  async function listBots(input) {
57
59
  try {
58
60
  const limit = input.limit ?? 50;
59
61
  const offset = input.offset ?? 0;
60
- const result = await iam.listBots(limit, offset);
62
+ const result = await iam.listBots(limit, offset, { status: input.status, search: input.search });
61
63
  return { status: 200, data: result };
62
64
  }
63
65
  catch (err) {
@@ -38,6 +38,9 @@ exports.claimByMetadata = claimByMetadata;
38
38
  exports.resolveByMetadata = resolveByMetadata;
39
39
  const escalationService = __importStar(require("../../services/escalation"));
40
40
  const helpers_1 = require("./helpers");
41
+ const resolver_validation_1 = require("../../services/escalation/resolver-validation");
42
+ const enforcement_cache_1 = require("../../services/role/enforcement-cache");
43
+ const resolve_1 = require("./resolve");
41
44
  /**
42
45
  * Find escalations by a metadata key-value pair.
43
46
  *
@@ -130,7 +133,24 @@ async function resolveByMetadata(input, auth) {
130
133
  const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
131
134
  const writeAllRoles = writeScope.global ? null : writeScope.allRoles;
132
135
  const writeSelfRoles = writeScope.global ? null : writeScope.selfRoles;
133
- const result = await escalationService.resolveByMetadataAtomic(input.key, input.value, resolveUserId, input.resolverPayload, input.metadata, writeAllRoles, writeSelfRoles);
136
+ // Schema enforcement rides the atomic statement: with the cached enforcing
137
+ // set empty (the common case) the single-call behavior is unchanged. When
138
+ // the picked row's role enforces, the statement returns the row WITHOUT
139
+ // writing; the payload validates here, then a second asserted pass claims
140
+ // + resolves the same row with pending re-checked inside the statement.
141
+ const enforcing = await (0, enforcement_cache_1.getEnforcingRoles)();
142
+ let result = await escalationService.resolveByMetadataAtomic(input.key, input.value, resolveUserId, input.resolverPayload, input.metadata, writeAllRoles, writeSelfRoles, enforcing.size > 0 ? [...enforcing] : null);
143
+ if (result.outcome === 'validation_required' && result.row) {
144
+ const report = await (0, resolver_validation_1.checkResolverPayload)(result.row, input.resolverPayload);
145
+ if (report)
146
+ return (0, resolve_1.validationFailure)(report);
147
+ result = await escalationService.resolveByMetadataAtomic(input.key, input.value, resolveUserId, input.resolverPayload, input.metadata, writeAllRoles, writeSelfRoles, null, result.row.id);
148
+ // The asserted row left pending between the two passes — a concurrent
149
+ // resolution won the row; surface it as the conflict it is.
150
+ if (result.outcome === 'not_found') {
151
+ return { status: 409, error: 'A concurrent resolution is already in progress for this escalation' };
152
+ }
153
+ }
134
154
  if (result.outcome === 'not_found') {
135
155
  return { status: 404, error: 'No pending escalation found for this metadata, or insufficient role permissions' };
136
156
  }
@@ -1,3 +1,4 @@
1
+ import type { ResolverSchemaViolationReport } from '../../services/escalation/resolver-validation';
1
2
  import type { LTApiResult, LTApiAuth } from '../../types/sdk';
2
3
  /**
3
4
  * Resolve a pending escalation with a human-provided payload.
@@ -72,3 +73,10 @@ export declare function resolveAllOrNone(input: {
72
73
  metadata?: Record<string, any>;
73
74
  requireClaimed?: boolean;
74
75
  }, auth: LTApiAuth): Promise<LTApiResult>;
76
+ /**
77
+ * The canonical 422 for a schema-validation rejection. The full body rides in
78
+ * `data` (routes serialize `data ?? { error }`), so every transport — HTTP,
79
+ * SDK, CLI, dashboard — receives the same shape: error, code, violations,
80
+ * role, schemaVersion. `code` also sits on the envelope for SDK branching.
81
+ */
82
+ export declare function validationFailure(report: ResolverSchemaViolationReport): LTApiResult;
@@ -37,6 +37,7 @@ exports.resolveEscalation = resolveEscalation;
37
37
  exports.resolveBySignalKey = resolveBySignalKey;
38
38
  exports.resolveByIds = resolveByIds;
39
39
  exports.resolveAllOrNone = resolveAllOrNone;
40
+ exports.validationFailure = validationFailure;
40
41
  const escalationService = __importStar(require("../../services/escalation"));
41
42
  const taskService = __importStar(require("../../services/task"));
42
43
  const escalation_strategy_1 = require("../../services/escalation-strategy");
@@ -45,6 +46,8 @@ const deployer_1 = require("../../services/yaml-workflow/deployer");
45
46
  const workers_1 = require("../../workers");
46
47
  const defaults_1 = require("../../modules/defaults");
47
48
  const helpers_1 = require("./helpers");
49
+ const resolver_validation_1 = require("../../services/escalation/resolver-validation");
50
+ const enforcement_cache_1 = require("../../services/role/enforcement-cache");
48
51
  // ── Orchestrator ─────────────────────────────────────────────────────────
49
52
  /**
50
53
  * Resolve a pending escalation with a human-provided payload.
@@ -90,6 +93,13 @@ async function resolveEscalation(input, auth) {
90
93
  const stale = (0, helpers_1.assertLiveClaimant)(auth.userId, escalation);
91
94
  if (stale)
92
95
  return stale;
96
+ // Schema enforcement (enforce_schema roles): validate the payload against
97
+ // the escalation's resolved form schema BEFORE any resolution path runs.
98
+ // Costs nothing when the role does not enforce (cached role set); the
99
+ // task-fallback envelope read only happens for enforcing roles.
100
+ const violation = await (0, resolver_validation_1.checkResolverPayload)(escalation, resolverPayload, () => reconstructEnvelope(escalation));
101
+ if (violation)
102
+ return validationFailure(violation);
93
103
  // The resolver payload is stored exactly as submitted — its shape is the
94
104
  // workflow's contract, formed by the caller (the React app maps the form to
95
105
  // the payload via x-lt-bind before submitting). No server-side transform.
@@ -161,6 +171,11 @@ async function resolveBySignalKey(input, auth) {
161
171
  const denied = await (0, helpers_1.assertWriteAccess)(auth.userId, escalation);
162
172
  if (denied)
163
173
  return { status: 404, error: 'Escalation not found' };
174
+ // Schema enforcement — the row (envelope included) is already loaded, so
175
+ // this costs zero extra reads beyond the cached schema.
176
+ const violation = await (0, resolver_validation_1.checkResolverPayload)(escalation, resolverPayload);
177
+ if (violation)
178
+ return validationFailure(violation);
164
179
  // The payload is delivered as the signal the parked workflow's condition()
165
180
  // receives — stored exactly as submitted (the caller formed its shape).
166
181
  return resolveViaSignalKey(escalation, resolverPayload, metadata);
@@ -195,17 +210,39 @@ async function resolveByIds(input, auth) {
195
210
  // resolve any item in the role; write_self roles may resolve only items assigned
196
211
  // to the caller (same rule single-resolve applies via assertWriteAccess). Global
197
212
  // bypasses. A missing id or any out-of-scope item → 404 (non-disclosure), and
198
- // nothing is resolved.
213
+ // nothing is resolved. The scope-row read is shared with the enforcement
214
+ // gate below — one indexed read serves both.
199
215
  const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
216
+ const enforcing = await (0, enforcement_cache_1.getEnforcingRoles)();
217
+ let scopeRows = null;
200
218
  if (!writeScope.global) {
201
219
  const allSet = new Set(writeScope.allRoles);
202
220
  const selfSet = new Set(writeScope.selfRoles);
203
- const rows = await escalationService.getEscalationScopeRows(ids);
221
+ scopeRows = await escalationService.getEscalationScopeRows(ids);
204
222
  const writable = (r) => allSet.has(r.role) || (selfSet.has(r.role) && r.assigned_to === auth.userId);
205
- if (rows.length !== ids.length || !rows.every(writable)) {
223
+ if (scopeRows.length !== ids.length || !scopeRows.every(writable)) {
206
224
  return { status: 404, error: 'One or more escalations not found' };
207
225
  }
208
226
  }
227
+ // Schema enforcement (enforce_schema roles): with the cached enforcing set
228
+ // empty this path adds zero reads. Otherwise the scope rows identify which
229
+ // targets enforce, and only those rows load in full for validation — the
230
+ // one payload is validated against each enforcing row's own context.
231
+ if (enforcing.size > 0) {
232
+ const rows = scopeRows ?? await escalationService.getEscalationScopeRows(ids);
233
+ const involved = rows.filter((r) => enforcing.has(r.role)).map((r) => r.id);
234
+ if (involved.length > 0) {
235
+ const fullRows = await escalationService.getEscalationsByIds(involved);
236
+ const failed = [];
237
+ for (const row of fullRows) {
238
+ const report = await (0, resolver_validation_1.checkResolverPayload)(row, resolverPayload);
239
+ if (report)
240
+ failed.push({ id: row.id, report });
241
+ }
242
+ if (failed.length > 0)
243
+ return bulkValidationFailure(failed);
244
+ }
245
+ }
209
246
  const resolved = await escalationService.resolveEscalationsByIds(ids, resolverPayload, metadata);
210
247
  return { status: 200, data: { resolved: resolved.length, escalationIds: resolved.map((e) => e.id) } };
211
248
  }
@@ -286,6 +323,24 @@ async function resolveAllOrNone(input, auth) {
286
323
  const error = 'One or more escalations require the single-resolve path (legacy signal routing)';
287
324
  return { status: 409, error, data: { error, failedIds: failed.map((f) => f.id), failed } };
288
325
  }
326
+ // Schema enforcement (enforce_schema roles): every item validates against
327
+ // its own row's context before ANYTHING resolves — all-or-none extends to
328
+ // validation. Rows are already in hand, so the only added reads are the
329
+ // cached schemas of enforcing roles.
330
+ const enforcing = await (0, enforcement_cache_1.getEnforcingRoles)();
331
+ if (enforcing.size > 0) {
332
+ const failed = [];
333
+ for (const item of items) {
334
+ const row = rowById.get(item.id);
335
+ if (!row || !row.role || !enforcing.has(row.role))
336
+ continue;
337
+ const report = await (0, resolver_validation_1.checkResolverPayload)(row, item.resolverPayload);
338
+ if (report)
339
+ failed.push({ id: item.id, report });
340
+ }
341
+ if (failed.length > 0)
342
+ return bulkValidationFailure(failed);
343
+ }
289
344
  // Per-row password redaction against each row's own form schema — the
290
345
  // payload enters the signal store and the audit record; plaintext never.
291
346
  const serviceItems = await Promise.all(items.map(async (item) => ({
@@ -448,6 +503,30 @@ function signaledResult(escalation, workflowId) {
448
503
  data: { signaled: true, escalationId: escalation.id, workflowId },
449
504
  };
450
505
  }
506
+ /**
507
+ * The canonical 422 for a schema-validation rejection. The full body rides in
508
+ * `data` (routes serialize `data ?? { error }`), so every transport — HTTP,
509
+ * SDK, CLI, dashboard — receives the same shape: error, code, violations,
510
+ * role, schemaVersion. `code` also sits on the envelope for SDK branching.
511
+ */
512
+ function validationFailure(report) {
513
+ const body = (0, resolver_validation_1.toValidationErrorBody)(report);
514
+ return { status: 422, error: body.error, code: body.code, data: body };
515
+ }
516
+ /**
517
+ * The bulk-surface 422: violations from every failing row, each tagged with
518
+ * its escalationId so callers can attribute them. Role/schemaVersion echo the
519
+ * first failing report (bulk resolves target homogeneous role groups).
520
+ */
521
+ function bulkValidationFailure(failed) {
522
+ const violations = failed.flatMap(({ id, report }) => report.violations.map((v) => ({ ...v, escalationId: id })));
523
+ const body = {
524
+ ...(0, resolver_validation_1.toValidationErrorBody)(failed[0].report),
525
+ error: `resolverPayload failed schema validation for ${failed.length} escalation${failed.length === 1 ? '' : 's'} (${violations.length} violation${violations.length === 1 ? '' : 's'})`,
526
+ violations,
527
+ };
528
+ return { status: 422, error: body.error, code: body.code, data: body };
529
+ }
451
530
  /** Replace password fields with ephemeral tokens so plaintext never enters the signal store. */
452
531
  async function redactPasswords(payload, formSchema) {
453
532
  if (!formSchema?.properties)
@@ -104,6 +104,8 @@ export declare function updateRole(input: {
104
104
  priority_threshold_minutes?: number | null;
105
105
  /** Escalation metadata key holding the age origin (ISO 8601 UTC timestamp). Falls back to created_at. */
106
106
  priority_facet?: string | null;
107
+ /** Turn server-side resolver schema validation on/off for this role. */
108
+ enforce_schema?: boolean;
107
109
  /** Replace the upstream-input set (omitted = preserve; null or [] = clear). */
108
110
  upstream_roles?: string[] | null;
109
111
  /** Recorded on the schema snapshot when this update changes a schema field. */
@@ -225,6 +225,9 @@ async function updateRole(input) {
225
225
  if (input.ops_visible !== undefined && typeof input.ops_visible !== 'boolean') {
226
226
  return { status: 400, error: 'ops_visible must be a boolean' };
227
227
  }
228
+ if (input.enforce_schema !== undefined && typeof input.enforce_schema !== 'boolean') {
229
+ return { status: 400, error: 'enforce_schema must be a boolean' };
230
+ }
228
231
  if (input.parent_role != null && input.parent_role === input.role) {
229
232
  return { status: 400, error: 'parent_role must reference a different role' };
230
233
  }
@@ -265,6 +268,7 @@ async function updateRole(input) {
265
268
  worker_count: input.worker_count,
266
269
  priority_threshold_minutes: input.priority_threshold_minutes,
267
270
  priority_facet: input.priority_facet,
271
+ enforce_schema: input.enforce_schema,
268
272
  upstream_roles: input.upstream_roles,
269
273
  change_summary: input.change_summary,
270
274
  });
package/build/bin/ltc.js CHANGED
@@ -62,7 +62,16 @@ const envLoaded = fs.existsSync(envPath);
62
62
  // ── Error handler ────────────────────────────────────────────────────────
63
63
  function handleError(err) {
64
64
  const msg = err.message || String(err);
65
- console.error(`\n ${picocolors_1.default.red('✗')} ${msg}\n`);
65
+ console.error(`\n ${picocolors_1.default.red('✗')} ${msg}`);
66
+ // Schema-validation rejections carry field-level violations — print each
67
+ // one the way the dashboard's errors panel lists them.
68
+ if (Array.isArray(err.violations)) {
69
+ for (const v of err.violations) {
70
+ const where = v.escalationId ? `${v.escalationId} · ${v.field}` : v.field;
71
+ console.error(` ${picocolors_1.default.dim('·')} ${picocolors_1.default.bold(where)} — ${v.message}`);
72
+ }
73
+ }
74
+ console.error('');
66
75
  process.exit(1);
67
76
  }
68
77
  function wrap(fn) {
package/build/index.js CHANGED
@@ -40,6 +40,7 @@ exports.registerMcpTool = exports.getToolContext = exports.getActivityIdentity =
40
40
  exports.LTExpressAdapter = exports.api = exports.KnowledgeApi = exports.resolveCredential = exports.mimeFromPath = exports.getStorageBackend = exports.listOAuthConnections = exports.getFreshAccessToken = exports.createClient = exports.CallbackEventAdapter = exports.builtinMcpServerFactories = exports.getSystemAgents = exports.getSystemWorkers = void 0;
41
41
  const config_1 = require("./modules/config");
42
42
  const logger_1 = require("./lib/logger");
43
+ const midnight_1 = require("./examples/themes/midnight");
43
44
  const start_1 = require("./start");
44
45
  // ─── Package Exports ─────────────────────────────────────────────────────────
45
46
  var start_2 = require("./start");
@@ -170,7 +171,12 @@ async function main() {
170
171
  escalation: {
171
172
  strategy: 'mcp',
172
173
  },
173
- branding: process.env.WHITE_LABEL ? { appName: process.env.WHITE_LABEL } : undefined,
174
+ branding: {
175
+ ...(process.env.WHITE_LABEL ? { appName: process.env.WHITE_LABEL } : {}),
176
+ // Midnight ships as a registered theme — the working proof that a
177
+ // deployment-supplied stylesheet controls the entire design system.
178
+ themes: [midnight_1.midnightTheme],
179
+ },
174
180
  telemetry: honeycombKey ? { honeycomb: { apiKey: honeycombKey } } : undefined,
175
181
  events: config_1.config.EVENT_TRANSPORT === 'nats'
176
182
  ? { nats: { url: config_1.config.NATS_URL, wsUrl: config_1.config.NATS_WS_URL || undefined, token: config_1.config.NATS_TOKEN || undefined } }
@@ -1,6 +1,9 @@
1
+ import { type LTFieldViolation } from '../../types/validation';
1
2
  export declare class CLIError extends Error {
2
3
  status?: number | undefined;
3
- constructor(message: string, status?: number | undefined);
4
+ /** Field-level violations from a schema_validation 422, when present. */
5
+ violations?: LTFieldViolation[];
6
+ constructor(message: string, status?: number | undefined, violations?: LTFieldViolation[]);
4
7
  }
5
8
  /** Authenticated fetch against the Long Tail API */
6
9
  export declare function apiFetch<T = any>(path: string, options?: RequestInit): Promise<T>;
@@ -3,11 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CLIError = void 0;
4
4
  exports.apiFetch = apiFetch;
5
5
  const auth_1 = require("./auth");
6
+ const validation_1 = require("../../types/validation");
6
7
  class CLIError extends Error {
7
- constructor(message, status) {
8
+ constructor(message, status, violations) {
8
9
  super(message);
9
10
  this.status = status;
10
11
  this.name = 'CLIError';
12
+ this.violations = violations;
11
13
  }
12
14
  }
13
15
  exports.CLIError = CLIError;
@@ -25,7 +27,7 @@ async function apiFetch(path, options) {
25
27
  });
26
28
  const body = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
27
29
  if (!res.ok) {
28
- throw new CLIError(body.error || `HTTP ${res.status}`, res.status);
30
+ throw new CLIError(body.error || `HTTP ${res.status}`, res.status, (0, validation_1.isValidationErrorBody)(body) ? body.violations : undefined);
29
31
  }
30
32
  return body;
31
33
  }
@@ -0,0 +1,18 @@
1
+ -- Migration 023: Role-level resolver schema enforcement
2
+ --
3
+ -- enforce_schema makes the role's form_schema an enforced API contract: every
4
+ -- resolve surface (by id, by signal key, by ids, all-or-none, by metadata, and
5
+ -- the MCP resolve tools) validates the submitted resolverPayload against the
6
+ -- escalation's resolved form schema (metadata.form_schema override, else the
7
+ -- pinned lt_role_schemas snapshot, else the role's latest) and rejects
8
+ -- violations with 422 before any state changes. The validation pass is the
9
+ -- same isomorphic code the dashboard runs pre-submission, so a payload that
10
+ -- passes the client panel passes the gate and a 422 carries the identical
11
+ -- field-error list.
12
+ --
13
+ -- Opt-in per role (default false) so surfaces migrate deliberately: flip it on
14
+ -- a role once its submitters (dashboard, sim workforce, webhooks) are known to
15
+ -- send complete payloads. Roles with it off resolve exactly as before.
16
+
17
+ ALTER TABLE lt_roles
18
+ ADD COLUMN IF NOT EXISTS enforce_schema BOOLEAN NOT NULL DEFAULT false;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Typed resolution parsing — the workflow-side half of the resolver contract.
3
+ *
4
+ * Server-side enforcement (enforce_schema roles) guarantees a resolution
5
+ * matched the role's form schema when it was submitted. This module gives
6
+ * workflow and activity code the consuming half: declare the payload shape
7
+ * once as a zod schema, and parse the resolution through it to get a TYPED
8
+ * value with runtime assurance — a drifted or legacy payload fails loud here
9
+ * instead of propagating `any` into business logic.
10
+ *
11
+ * const IntakeV1 = z.object({ customer: z.object({ name: z.string() }) });
12
+ * const intake = parseResolverPayload(IntakeV1, response);
13
+ * intake.customer.name; // typed AND runtime-checked
14
+ */
15
+ import type { ZodType } from 'zod';
16
+ import type { LTFieldViolation } from '../types/validation';
17
+ /**
18
+ * A resolution that failed its declared zod schema. Carries the same
19
+ * `{ field, message }` violation list the validation surfaces use, and the
20
+ * original ZodError as `cause`.
21
+ */
22
+ export declare class ResolverPayloadTypeError extends Error {
23
+ violations: LTFieldViolation[];
24
+ cause?: unknown;
25
+ constructor(violations: LTFieldViolation[], options?: {
26
+ cause?: unknown;
27
+ });
28
+ }
29
+ /**
30
+ * Parse a resolution payload through its declared zod schema. Returns the
31
+ * typed value; throws {@link ResolverPayloadTypeError} when the payload does
32
+ * not conform. The generic flows from the schema to the return value — no
33
+ * casting at the call site.
34
+ */
35
+ export declare function parseResolverPayload<T>(schema: ZodType<T>, payload: unknown): T;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ResolverPayloadTypeError = void 0;
4
+ exports.parseResolverPayload = parseResolverPayload;
5
+ /**
6
+ * A resolution that failed its declared zod schema. Carries the same
7
+ * `{ field, message }` violation list the validation surfaces use, and the
8
+ * original ZodError as `cause`.
9
+ */
10
+ class ResolverPayloadTypeError extends Error {
11
+ constructor(violations, options) {
12
+ const n = violations.length;
13
+ super(`resolver payload failed its declared schema (${n} violation${n === 1 ? '' : 's'}): `
14
+ + violations.map((v) => `${v.field}: ${v.message}`).join('; '));
15
+ this.name = 'ResolverPayloadTypeError';
16
+ this.violations = violations;
17
+ this.cause = options?.cause;
18
+ }
19
+ }
20
+ exports.ResolverPayloadTypeError = ResolverPayloadTypeError;
21
+ /**
22
+ * Parse a resolution payload through its declared zod schema. Returns the
23
+ * typed value; throws {@link ResolverPayloadTypeError} when the payload does
24
+ * not conform. The generic flows from the schema to the return value — no
25
+ * casting at the call site.
26
+ */
27
+ function parseResolverPayload(schema, payload) {
28
+ const result = schema.safeParse(payload);
29
+ if (!result.success) {
30
+ const violations = result.error.issues.map((issue) => ({
31
+ field: issue.path.length > 0 ? issue.path.join('.') : '(root)',
32
+ message: issue.message,
33
+ }));
34
+ throw new ResolverPayloadTypeError(violations, { cause: result.error });
35
+ }
36
+ return result.data;
37
+ }
@@ -4,10 +4,29 @@
4
4
  * The `appName` defaults to 'LongTail' and can be overridden via the
5
5
  * `branding` block of the `start()` config — no env-var required, since
6
6
  * this is a per-deployment product decision, not an ops toggle.
7
+ *
8
+ * `customCss` and `themes` carry the deployment's design-system overrides.
9
+ * Theme metadata (id, label, swatch, dark) rides the /api/settings JSON so
10
+ * the picker can render; the CSS itself is served separately at
11
+ * GET /api/settings/custom.css so the browser caches it as a stylesheet.
7
12
  */
8
- export declare function configureBranding(patch?: {
13
+ export interface LTBrandingTheme {
14
+ id: string;
15
+ label: string;
16
+ swatch: string;
17
+ css: string;
18
+ dark?: boolean;
19
+ }
20
+ export interface LTBrandingConfig {
9
21
  appName?: string;
10
- }): void;
22
+ customCss?: string;
23
+ themes?: LTBrandingTheme[];
24
+ }
25
+ export declare function configureBranding(patch?: LTBrandingConfig): void;
26
+ /** Metadata only — the CSS never rides the settings JSON. */
11
27
  export declare function getBranding(): {
12
28
  appName: string;
29
+ themes: Array<Omit<LTBrandingTheme, 'css'>>;
13
30
  };
31
+ /** The full stylesheet served at /api/settings/custom.css. */
32
+ export declare function getCustomCss(): string;
@@ -5,15 +5,36 @@
5
5
  * The `appName` defaults to 'LongTail' and can be overridden via the
6
6
  * `branding` block of the `start()` config — no env-var required, since
7
7
  * this is a per-deployment product decision, not an ops toggle.
8
+ *
9
+ * `customCss` and `themes` carry the deployment's design-system overrides.
10
+ * Theme metadata (id, label, swatch, dark) rides the /api/settings JSON so
11
+ * the picker can render; the CSS itself is served separately at
12
+ * GET /api/settings/custom.css so the browser caches it as a stylesheet.
8
13
  */
9
14
  Object.defineProperty(exports, "__esModule", { value: true });
10
15
  exports.configureBranding = configureBranding;
11
16
  exports.getBranding = getBranding;
17
+ exports.getCustomCss = getCustomCss;
12
18
  let _appName = 'LongTail';
19
+ let _customCss = '';
20
+ let _themes = [];
13
21
  function configureBranding(patch) {
14
22
  if (patch?.appName)
15
23
  _appName = patch.appName;
24
+ if (typeof patch?.customCss === 'string')
25
+ _customCss = patch.customCss;
26
+ if (Array.isArray(patch?.themes))
27
+ _themes = patch.themes;
16
28
  }
29
+ /** Metadata only — the CSS never rides the settings JSON. */
17
30
  function getBranding() {
18
- return { appName: _appName };
31
+ return {
32
+ appName: _appName,
33
+ themes: _themes.map(({ id, label, swatch, dark }) => ({ id, label, swatch, dark })),
34
+ };
35
+ }
36
+ /** The full stylesheet served at /api/settings/custom.css. */
37
+ function getCustomCss() {
38
+ const parts = [_customCss, ..._themes.map((t) => t.css)].filter(Boolean);
39
+ return parts.join('\n\n');
19
40
  }
@@ -12,5 +12,6 @@ export declare const config: {
12
12
  NODE_ENV: string;
13
13
  JWT_SECRET: string;
14
14
  HONEYCOMB_API_KEY: string;
15
+ ROLE_ENFORCEMENT_CACHE_TTL_MS: number;
15
16
  };
16
17
  export declare const postgres_options: Record<string, unknown>;
@@ -19,6 +19,11 @@ exports.config = {
19
19
  NODE_ENV: process.env.NODE_ENV || 'development',
20
20
  JWT_SECRET: process.env.JWT_SECRET || '',
21
21
  HONEYCOMB_API_KEY: process.env.HONEYCOMB_API_KEY || '',
22
+ // Resolver schema enforcement: TTL for the cached enforcing-role set and
23
+ // latest role form schemas. Bounds cross-container staleness after a role
24
+ // admin flips enforce_schema or edits a schema; pinned snapshots are
25
+ // immutable and cache indefinitely.
26
+ ROLE_ENFORCEMENT_CACHE_TTL_MS: parseInt(process.env.ROLE_ENFORCEMENT_CACHE_TTL_MS || '30000', 10),
22
27
  };
23
28
  exports.postgres_options = {
24
29
  host: exports.config.POSTGRES_HOST,
@@ -43,12 +43,14 @@ router.use(auth_1.requireBuilder);
43
43
  /**
44
44
  * GET /api/bot-accounts
45
45
  * List all bot accounts.
46
- * Query: ?limit=50&offset=0
46
+ * Query: ?limit=50&offset=0&status=active&search=mcp
47
47
  */
48
48
  router.get('/', async (req, res) => {
49
49
  const result = await api.listBots({
50
50
  limit: req.query.limit ? parseInt(req.query.limit, 10) : undefined,
51
51
  offset: req.query.offset ? parseInt(req.query.offset, 10) : undefined,
52
+ status: req.query.status || undefined,
53
+ search: req.query.search || undefined,
52
54
  });
53
55
  res.status(result.status).json(result.data ?? { error: result.error });
54
56
  });
@@ -33,8 +33,10 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ const crypto_1 = require("crypto");
36
37
  const express_1 = require("express");
37
38
  const api = __importStar(require("../api/settings"));
39
+ const branding_1 = require("../modules/branding");
38
40
  const router = (0, express_1.Router)();
39
41
  /**
40
42
  * GET /api/settings
@@ -44,4 +46,23 @@ router.get('/', async (req, res) => {
44
46
  const result = await api.getSettings(req);
45
47
  res.status(result.status).json(result.data ?? { error: result.error });
46
48
  });
49
+ /**
50
+ * GET /api/settings/custom.css
51
+ * The deployment's registered design-system overrides (branding.customCss +
52
+ * registered theme blocks). Always 200 text/css — empty when nothing is
53
+ * registered — so the dashboard references it unconditionally with no flash
54
+ * and no conditional logic. ETag lets the browser cache it across loads.
55
+ */
56
+ router.get('/custom.css', (req, res) => {
57
+ const css = (0, branding_1.getCustomCss)();
58
+ const etag = `"${(0, crypto_1.createHash)('sha1').update(css).digest('hex')}"`;
59
+ if (req.headers['if-none-match'] === etag) {
60
+ res.status(304).end();
61
+ return;
62
+ }
63
+ res.setHeader('Content-Type', 'text/css; charset=utf-8');
64
+ res.setHeader('ETag', etag);
65
+ res.setHeader('Cache-Control', 'no-cache');
66
+ res.status(200).send(css);
67
+ });
47
68
  exports.default = router;
@@ -466,3 +466,7 @@ export declare function createClient(options?: LTClientOptions): {
466
466
  };
467
467
  /** The return type of createClient(). */
468
468
  export type LTClient = ReturnType<typeof createClient>;
469
+ export { LT_ERROR_CODES, isValidationErrorBody, } from '../types/validation';
470
+ export type { LTErrorCode, LTFieldViolation, LTValidationErrorBody, } from '../types/validation';
471
+ export type { LTApiAuth, LTApiResult } from '../types/sdk';
472
+ export { parseResolverPayload, ResolverPayloadTypeError } from '../lib/typed-resolution';
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ResolverPayloadTypeError = exports.parseResolverPayload = exports.isValidationErrorBody = exports.LT_ERROR_CODES = void 0;
36
37
  exports.createClient = createClient;
37
38
  const tasksApi = __importStar(require("../api/tasks"));
38
39
  const escalationsApi = __importStar(require("../api/escalations"));
@@ -365,3 +366,16 @@ function createClient(options = {}) {
365
366
  },
366
367
  };
367
368
  }
369
+ // ── Canonical error shapes ──────────────────────────────────────────────
370
+ // A 422 from any resolve surface carries the full LTValidationErrorBody in
371
+ // `result.data` with `result.code === LT_ERROR_CODES.SCHEMA_VALIDATION`.
372
+ // `isValidationErrorBody(result.data)` narrows it for typed handling.
373
+ var validation_1 = require("../types/validation");
374
+ Object.defineProperty(exports, "LT_ERROR_CODES", { enumerable: true, get: function () { return validation_1.LT_ERROR_CODES; } });
375
+ Object.defineProperty(exports, "isValidationErrorBody", { enumerable: true, get: function () { return validation_1.isValidationErrorBody; } });
376
+ // ── Typed resolution parsing ────────────────────────────────────────────
377
+ // Workflow/activity code declares its payload shape as a zod schema and
378
+ // parses resolutions through it — typed values with runtime assurance.
379
+ var typed_resolution_1 = require("../lib/typed-resolution");
380
+ Object.defineProperty(exports, "parseResolverPayload", { enumerable: true, get: function () { return typed_resolution_1.parseResolverPayload; } });
381
+ Object.defineProperty(exports, "ResolverPayloadTypeError", { enumerable: true, get: function () { return typed_resolution_1.ResolverPayloadTypeError; } });