@hotmeshio/long-tail 0.10.9 → 0.10.11

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 (301) hide show
  1. package/build/api/escalations/list.js +11 -4
  2. package/build/api/index.d.ts +1 -0
  3. package/build/api/index.js +2 -1
  4. package/build/api/personas.d.ts +121 -0
  5. package/build/api/personas.js +341 -0
  6. package/build/api/roles.d.ts +2 -0
  7. package/build/api/roles.js +4 -0
  8. package/build/bin/ltc.js +23 -0
  9. package/build/lib/cli/commands/personas.d.ts +16 -0
  10. package/build/lib/cli/commands/personas.js +42 -0
  11. package/build/lib/db/schemas/024_personas.sql +53 -0
  12. package/build/lib/db/schemas/025_ops_home_default.sql +11 -0
  13. package/build/modules/features.d.ts +9 -0
  14. package/build/modules/features.js +1 -0
  15. package/build/routes/index.js +2 -0
  16. package/build/routes/personas.d.ts +2 -0
  17. package/build/routes/personas.js +118 -0
  18. package/build/routes/users.js +35 -0
  19. package/build/sdk/index.d.ts +15 -0
  20. package/build/sdk/index.js +16 -0
  21. package/build/services/persona/index.d.ts +92 -0
  22. package/build/services/persona/index.js +217 -0
  23. package/build/services/persona/sql.d.ts +24 -0
  24. package/build/services/persona/sql.js +158 -0
  25. package/build/services/persona/types.d.ts +23 -0
  26. package/build/services/persona/types.js +33 -0
  27. package/build/services/role/index.js +1 -0
  28. package/build/services/role/sql.d.ts +6 -2
  29. package/build/services/role/sql.js +11 -2
  30. package/build/services/role/types.d.ts +9 -0
  31. package/build/services/user/sql.d.ts +8 -5
  32. package/build/services/user/sql.js +15 -5
  33. package/build/system/mcp-servers/admin/index.d.ts +1 -0
  34. package/build/system/mcp-servers/admin/index.js +5 -2
  35. package/build/system/mcp-servers/admin/personas.d.ts +8 -0
  36. package/build/system/mcp-servers/admin/personas.js +186 -0
  37. package/build/system/mcp-servers/admin/schemas.d.ts +94 -0
  38. package/build/system/mcp-servers/admin/schemas.js +43 -2
  39. package/build/system/mcp-servers/admin/users.js +1 -0
  40. package/build/system/seed/tool-manifests-admin.d.ts +1057 -0
  41. package/build/system/seed/tool-manifests-admin.js +12 -1
  42. package/build/tsconfig.tsbuildinfo +1 -1
  43. package/build/types/index.d.ts +1 -0
  44. package/build/types/persona.d.ts +56 -0
  45. package/build/types/persona.js +2 -0
  46. package/build/types/startup.d.ts +8 -0
  47. package/build/types/user.d.ts +2 -0
  48. package/dashboard/dist/assets/{AdminDashboard-DWSYdAeW.js → AdminDashboard-CkTbRfY9.js} +2 -2
  49. package/dashboard/dist/assets/{AdminDashboard-DWSYdAeW.js.map → AdminDashboard-CkTbRfY9.js.map} +1 -1
  50. package/dashboard/dist/assets/{AgentConfigPage-Dm6gL76m.js → AgentConfigPage-Cm8FRpXi.js} +2 -2
  51. package/dashboard/dist/assets/{AgentConfigPage-Dm6gL76m.js.map → AgentConfigPage-Cm8FRpXi.js.map} +1 -1
  52. package/dashboard/dist/assets/AgentDetailPage-sQ1-UT46.js +4 -0
  53. package/dashboard/dist/assets/AgentDetailPage-sQ1-UT46.js.map +1 -0
  54. package/dashboard/dist/assets/{AgentsPage-DKaDRk-Y.js → AgentsPage-bSx_mdae.js} +2 -2
  55. package/dashboard/dist/assets/{AgentsPage-DKaDRk-Y.js.map → AgentsPage-bSx_mdae.js.map} +1 -1
  56. package/dashboard/dist/assets/AutoGrowTextarea-y-oNY51_.js +2 -0
  57. package/dashboard/dist/assets/AutoGrowTextarea-y-oNY51_.js.map +1 -0
  58. package/dashboard/dist/assets/AvailableEscalationsPage-8pMC7Xv6.js +2 -0
  59. package/dashboard/dist/assets/AvailableEscalationsPage-8pMC7Xv6.js.map +1 -0
  60. package/dashboard/dist/assets/{BotPicker-DRv_orzI.js → BotPicker-C4uGH2x9.js} +2 -2
  61. package/dashboard/dist/assets/{BotPicker-DRv_orzI.js.map → BotPicker-C4uGH2x9.js.map} +1 -1
  62. package/dashboard/dist/assets/{CapabilitiesPage-DqRVzMol.js → CapabilitiesPage-BRoOeKcc.js} +2 -2
  63. package/dashboard/dist/assets/{CapabilitiesPage-DqRVzMol.js.map → CapabilitiesPage-BRoOeKcc.js.map} +1 -1
  64. package/dashboard/dist/assets/{CollapsibleSection-D0psvw8i.js → CollapsibleSection-CIj8Adc9.js} +2 -2
  65. package/dashboard/dist/assets/{CollapsibleSection-D0psvw8i.js.map → CollapsibleSection-CIj8Adc9.js.map} +1 -1
  66. package/dashboard/dist/assets/{ConfirmDeleteModal-DD7SEMeX.js → ConfirmDeleteModal-Ugi_9Ukq.js} +2 -2
  67. package/dashboard/dist/assets/{ConfirmDeleteModal-DD7SEMeX.js.map → ConfirmDeleteModal-Ugi_9Ukq.js.map} +1 -1
  68. package/dashboard/dist/assets/{CopyableId-BpB-NvDh.js → CopyableId-GUEdiiL5.js} +2 -2
  69. package/dashboard/dist/assets/{CopyableId-BpB-NvDh.js.map → CopyableId-GUEdiiL5.js.map} +1 -1
  70. package/dashboard/dist/assets/CountdownTimer-D3t0XMhE.js +2 -0
  71. package/dashboard/dist/assets/CountdownTimer-D3t0XMhE.js.map +1 -0
  72. package/dashboard/dist/assets/{CredentialsPage-Axdci1z-.js → CredentialsPage-B73Si6K1.js} +2 -2
  73. package/dashboard/dist/assets/{CredentialsPage-Axdci1z-.js.map → CredentialsPage-B73Si6K1.js.map} +1 -1
  74. package/dashboard/dist/assets/{CronLabel-BjZ_wZA8.js → CronLabel-Ca1aHolt.js} +2 -2
  75. package/dashboard/dist/assets/{CronLabel-BjZ_wZA8.js.map → CronLabel-Ca1aHolt.js.map} +1 -1
  76. package/dashboard/dist/assets/{CustomDurationPicker-CkUCMUfk.js → CustomDurationPicker-_9E9x1YN.js} +2 -2
  77. package/dashboard/dist/assets/{CustomDurationPicker-CkUCMUfk.js.map → CustomDurationPicker-_9E9x1YN.js.map} +1 -1
  78. package/dashboard/dist/assets/{DropZone-_tmC3jEe.js → DropZone-NYn6TWHz.js} +2 -2
  79. package/dashboard/dist/assets/{DropZone-_tmC3jEe.js.map → DropZone-NYn6TWHz.js.map} +1 -1
  80. package/dashboard/dist/assets/ElapsedCell-karxia93.js +2 -0
  81. package/dashboard/dist/assets/{ElapsedCell-BE_CN5sP.js.map → ElapsedCell-karxia93.js.map} +1 -1
  82. package/dashboard/dist/assets/{EscalationListSchemaPage-CgcVIiYR.js → EscalationListSchemaPage-CDaNmbUA.js} +3 -3
  83. package/dashboard/dist/assets/{EscalationListSchemaPage-CgcVIiYR.js.map → EscalationListSchemaPage-CDaNmbUA.js.map} +1 -1
  84. package/dashboard/dist/assets/{EscalationSchemaPage-BaAn1Hcr.js → EscalationSchemaPage-k-2hFT2t.js} +3 -3
  85. package/dashboard/dist/assets/{EscalationSchemaPage-BaAn1Hcr.js.map → EscalationSchemaPage-k-2hFT2t.js.map} +1 -1
  86. package/dashboard/dist/assets/{EscalationsOverview-Dbq7tKzr.js → EscalationsOverview-C1e7IPn_.js} +2 -2
  87. package/dashboard/dist/assets/{EscalationsOverview-Dbq7tKzr.js.map → EscalationsOverview-C1e7IPn_.js.map} +1 -1
  88. package/dashboard/dist/assets/{EventTable-0RUK9jqt.js → EventTable-CIrrtL7_.js} +2 -2
  89. package/dashboard/dist/assets/{EventTable-0RUK9jqt.js.map → EventTable-CIrrtL7_.js.map} +1 -1
  90. package/dashboard/dist/assets/{FilterBar-B_dy2Jhj.js → FilterBar-CzjtH74v.js} +2 -2
  91. package/dashboard/dist/assets/{FilterBar-B_dy2Jhj.js.map → FilterBar-CzjtH74v.js.map} +1 -1
  92. package/dashboard/dist/assets/{GraphInvokePage-CvLYp1nL.js → GraphInvokePage-LMpWw8z1.js} +2 -2
  93. package/dashboard/dist/assets/{GraphInvokePage-CvLYp1nL.js.map → GraphInvokePage-LMpWw8z1.js.map} +1 -1
  94. package/dashboard/dist/assets/HomePage-Kl2DBG6l.js +2 -0
  95. package/dashboard/dist/assets/HomePage-Kl2DBG6l.js.map +1 -0
  96. package/dashboard/dist/assets/ListToolbar-B2fub0DY.js +2 -0
  97. package/dashboard/dist/assets/ListToolbar-B2fub0DY.js.map +1 -0
  98. package/dashboard/dist/assets/{McpOverview-Ca8cpc8g.js → McpOverview-odYnCZCC.js} +2 -2
  99. package/dashboard/dist/assets/{McpOverview-Ca8cpc8g.js.map → McpOverview-odYnCZCC.js.map} +1 -1
  100. package/dashboard/dist/assets/McpQueryDetailPage-BtqwHUAg.js +5 -0
  101. package/dashboard/dist/assets/McpQueryDetailPage-BtqwHUAg.js.map +1 -0
  102. package/dashboard/dist/assets/McpQueryPage-CPkVwNIK.js +2 -0
  103. package/dashboard/dist/assets/McpQueryPage-CPkVwNIK.js.map +1 -0
  104. package/dashboard/dist/assets/{McpRunDetailPage-C76mfUtG.js → McpRunDetailPage-D-Rz9MRw.js} +2 -2
  105. package/dashboard/dist/assets/{McpRunDetailPage-C76mfUtG.js.map → McpRunDetailPage-D-Rz9MRw.js.map} +1 -1
  106. package/dashboard/dist/assets/{McpRunsPage-B43BqEyw.js → McpRunsPage-rdpZ2yMh.js} +2 -2
  107. package/dashboard/dist/assets/{McpRunsPage-B43BqEyw.js.map → McpRunsPage-rdpZ2yMh.js.map} +1 -1
  108. package/dashboard/dist/assets/{Modal-CA2Iv02H.js → Modal-Dr1Xy0j0.js} +2 -2
  109. package/dashboard/dist/assets/{Modal-CA2Iv02H.js.map → Modal-Dr1Xy0j0.js.map} +1 -1
  110. package/dashboard/dist/assets/{NamespacePill-B5gHotsd.js → NamespacePill-GwO-WfhX.js} +2 -2
  111. package/dashboard/dist/assets/{NamespacePill-B5gHotsd.js.map → NamespacePill-GwO-WfhX.js.map} +1 -1
  112. package/dashboard/dist/assets/OperationsPage-DlyGLPAs.js +2 -0
  113. package/dashboard/dist/assets/OperationsPage-DlyGLPAs.js.map +1 -0
  114. package/dashboard/dist/assets/OperatorDashboard-Csei5vj-.js +2 -0
  115. package/dashboard/dist/assets/OperatorDashboard-Csei5vj-.js.map +1 -0
  116. package/dashboard/dist/assets/{PageHeader-DrbNMGxm.js → PageHeader-Bo9BU9pK.js} +2 -2
  117. package/dashboard/dist/assets/{PageHeader-DrbNMGxm.js.map → PageHeader-Bo9BU9pK.js.map} +1 -1
  118. package/dashboard/dist/assets/{PageHeaderWithStats-SbQkaQLG.js → PageHeaderWithStats-8_5cOSKL.js} +2 -2
  119. package/dashboard/dist/assets/{PageHeaderWithStats-SbQkaQLG.js.map → PageHeaderWithStats-8_5cOSKL.js.map} +1 -1
  120. package/dashboard/dist/assets/PersonaDetailPage--ZL6Or6v.js +2 -0
  121. package/dashboard/dist/assets/PersonaDetailPage--ZL6Or6v.js.map +1 -0
  122. package/dashboard/dist/assets/PersonasPage-Bdcoay3P.js +2 -0
  123. package/dashboard/dist/assets/PersonasPage-Bdcoay3P.js.map +1 -0
  124. package/dashboard/dist/assets/ProcessDetailPage-HrmVQq7S.js +2 -0
  125. package/dashboard/dist/assets/ProcessDetailPage-HrmVQq7S.js.map +1 -0
  126. package/dashboard/dist/assets/{ProcessesListPage-Di0n8TR4.js → ProcessesListPage-_xmj6Bo-.js} +2 -2
  127. package/dashboard/dist/assets/{ProcessesListPage-Di0n8TR4.js.map → ProcessesListPage-_xmj6Bo-.js.map} +1 -1
  128. package/dashboard/dist/assets/RoleDetailPage-C0ju2-4K.js +8 -0
  129. package/dashboard/dist/assets/RoleDetailPage-C0ju2-4K.js.map +1 -0
  130. package/dashboard/dist/assets/{RolePill-Cz-AAWqA.js → RolePill-CQlnEWkK.js} +2 -2
  131. package/dashboard/dist/assets/{RolePill-Cz-AAWqA.js.map → RolePill-CQlnEWkK.js.map} +1 -1
  132. package/dashboard/dist/assets/RolesPage-CKluVSpB.js +2 -0
  133. package/dashboard/dist/assets/RolesPage-CKluVSpB.js.map +1 -0
  134. package/dashboard/dist/assets/{RunAsSelector-DFv0VNVZ.js → RunAsSelector-DdfecQ1U.js} +2 -2
  135. package/dashboard/dist/assets/{RunAsSelector-DFv0VNVZ.js.map → RunAsSelector-DdfecQ1U.js.map} +1 -1
  136. package/dashboard/dist/assets/ScopeBadge-BBYkevuR.js +2 -0
  137. package/dashboard/dist/assets/ScopeBadge-BBYkevuR.js.map +1 -0
  138. package/dashboard/dist/assets/StatCard-Ca-ngCaD.js +2 -0
  139. package/dashboard/dist/assets/StatCard-Ca-ngCaD.js.map +1 -0
  140. package/dashboard/dist/assets/{StatusBadge-BbqghFMi.js → StatusBadge-BwUvykDo.js} +2 -2
  141. package/dashboard/dist/assets/{StatusBadge-BbqghFMi.js.map → StatusBadge-BwUvykDo.js.map} +1 -1
  142. package/dashboard/dist/assets/{StickyPagination-D5zxe_l9.js → StickyPagination-cLddIdtT.js} +2 -2
  143. package/dashboard/dist/assets/{StickyPagination-D5zxe_l9.js.map → StickyPagination-cLddIdtT.js.map} +1 -1
  144. package/dashboard/dist/assets/{StreamMessageDetail-BmBfNSEr.js → StreamMessageDetail-CZXWDawC.js} +2 -2
  145. package/dashboard/dist/assets/StreamMessageDetail-CZXWDawC.js.map +1 -0
  146. package/dashboard/dist/assets/{SwimlaneTimeline-CA0TKDIi.js → SwimlaneTimeline-fWY0gaf3.js} +2 -2
  147. package/dashboard/dist/assets/{SwimlaneTimeline-CA0TKDIi.js.map → SwimlaneTimeline-fWY0gaf3.js.map} +1 -1
  148. package/dashboard/dist/assets/{TagInput-Bf9dZSG6.js → TagInput-CDKbslLx.js} +2 -2
  149. package/dashboard/dist/assets/{TagInput-Bf9dZSG6.js.map → TagInput-CDKbslLx.js.map} +1 -1
  150. package/dashboard/dist/assets/{TaskDetailPage-C15nADUW.js → TaskDetailPage-BjE4G2yn.js} +2 -2
  151. package/dashboard/dist/assets/{TaskDetailPage-C15nADUW.js.map → TaskDetailPage-BjE4G2yn.js.map} +1 -1
  152. package/dashboard/dist/assets/{TaskQueuePill-DfiBYQy2.js → TaskQueuePill-G3RuYRNZ.js} +2 -2
  153. package/dashboard/dist/assets/{TaskQueuePill-DfiBYQy2.js.map → TaskQueuePill-G3RuYRNZ.js.map} +1 -1
  154. package/dashboard/dist/assets/{TasksListPage-DQdAayqc.js → TasksListPage-KAhmCWB2.js} +2 -2
  155. package/dashboard/dist/assets/{TasksListPage-DQdAayqc.js.map → TasksListPage-KAhmCWB2.js.map} +1 -1
  156. package/dashboard/dist/assets/{TimeAgo-CvslqTB7.js → TimeAgo-C_H2TyZw.js} +2 -2
  157. package/dashboard/dist/assets/{TimeAgo-CvslqTB7.js.map → TimeAgo-C_H2TyZw.js.map} +1 -1
  158. package/dashboard/dist/assets/{TimestampCell-BZCQQONl.js → TimestampCell-COG_4Flg.js} +2 -2
  159. package/dashboard/dist/assets/{TimestampCell-BZCQQONl.js.map → TimestampCell-COG_4Flg.js.map} +1 -1
  160. package/dashboard/dist/assets/{ToolPill-gA4sXukc.js → ToolPill-ChSyRJZC.js} +2 -2
  161. package/dashboard/dist/assets/{ToolPill-gA4sXukc.js.map → ToolPill-ChSyRJZC.js.map} +1 -1
  162. package/dashboard/dist/assets/{ToolTestPanel-BgLVc6R5.js → ToolTestPanel-BPdJECW0.js} +2 -2
  163. package/dashboard/dist/assets/{ToolTestPanel-BgLVc6R5.js.map → ToolTestPanel-BPdJECW0.js.map} +1 -1
  164. package/dashboard/dist/assets/{TopicDetailPage-9A0aPb4t.js → TopicDetailPage-DAqx5JqA.js} +3 -3
  165. package/dashboard/dist/assets/{TopicDetailPage-9A0aPb4t.js.map → TopicDetailPage-DAqx5JqA.js.map} +1 -1
  166. package/dashboard/dist/assets/{TopicsPage-o0EZxKbR.js → TopicsPage-DoGqAdPm.js} +2 -2
  167. package/dashboard/dist/assets/{TopicsPage-o0EZxKbR.js.map → TopicsPage-DoGqAdPm.js.map} +1 -1
  168. package/dashboard/dist/assets/{UserName-DKMLxBJ5.js → UserName-CKO_rk1R.js} +2 -2
  169. package/dashboard/dist/assets/{UserName-DKMLxBJ5.js.map → UserName-CKO_rk1R.js.map} +1 -1
  170. package/dashboard/dist/assets/{WorkflowExecutionPage-C-snB_dD.js → WorkflowExecutionPage-D3VQ9iQV.js} +2 -2
  171. package/dashboard/dist/assets/{WorkflowExecutionPage-C-snB_dD.js.map → WorkflowExecutionPage-D3VQ9iQV.js.map} +1 -1
  172. package/dashboard/dist/assets/{WorkflowPill-Djt2AtZk.js → WorkflowPill-CNbwug5p.js} +2 -2
  173. package/dashboard/dist/assets/{WorkflowPill-Djt2AtZk.js.map → WorkflowPill-CNbwug5p.js.map} +1 -1
  174. package/dashboard/dist/assets/{WorkflowsDashboard-BRQmgsLR.js → WorkflowsDashboard-DdJ86We6.js} +2 -2
  175. package/dashboard/dist/assets/{WorkflowsDashboard-BRQmgsLR.js.map → WorkflowsDashboard-DdJ86We6.js.map} +1 -1
  176. package/dashboard/dist/assets/{WorkflowsOverview-DmMdIRHQ.js → WorkflowsOverview-ZfFBzePp.js} +2 -2
  177. package/dashboard/dist/assets/{WorkflowsOverview-DmMdIRHQ.js.map → WorkflowsOverview-ZfFBzePp.js.map} +1 -1
  178. package/dashboard/dist/assets/{YamlWorkflowDetailPage-Bj7QkCry.js → YamlWorkflowDetailPage-4bd5yXdA.js} +2 -2
  179. package/dashboard/dist/assets/{YamlWorkflowDetailPage-Bj7QkCry.js.map → YamlWorkflowDetailPage-4bd5yXdA.js.map} +1 -1
  180. package/dashboard/dist/assets/{YamlWorkflowsPage-BIi77mE0.js → YamlWorkflowsPage-iHPNXjQj.js} +2 -2
  181. package/dashboard/dist/assets/{YamlWorkflowsPage-BIi77mE0.js.map → YamlWorkflowsPage-iHPNXjQj.js.map} +1 -1
  182. package/dashboard/dist/assets/{agents-EBIsjLCr.js → agents-DjD5U3Wb.js} +2 -2
  183. package/dashboard/dist/assets/{agents-EBIsjLCr.js.map → agents-DjD5U3Wb.js.map} +1 -1
  184. package/dashboard/dist/assets/{bots-BXzxybS6.js → bots-DiljVapH.js} +2 -2
  185. package/dashboard/dist/assets/{bots-BXzxybS6.js.map → bots-DiljVapH.js.map} +1 -1
  186. package/dashboard/dist/assets/{capabilities-CnprG6q_.js → capabilities-DV_5ghHT.js} +2 -2
  187. package/dashboard/dist/assets/{capabilities-CnprG6q_.js.map → capabilities-DV_5ghHT.js.map} +1 -1
  188. package/dashboard/dist/assets/{controlplane-DiC0xtNq.js → controlplane-BKMvfWCB.js} +2 -2
  189. package/dashboard/dist/assets/{controlplane-DiC0xtNq.js.map → controlplane-BKMvfWCB.js.map} +1 -1
  190. package/dashboard/dist/assets/escalation-columns-BQpCaJId.js +2 -0
  191. package/dashboard/dist/assets/escalation-columns-BQpCaJId.js.map +1 -0
  192. package/dashboard/dist/assets/{index-BzRXMicE.js → index-B-WkiNRN.js} +2 -2
  193. package/dashboard/dist/assets/{index-BzRXMicE.js.map → index-B-WkiNRN.js.map} +1 -1
  194. package/dashboard/dist/assets/index-BO-7S9Xw.js +2 -0
  195. package/dashboard/dist/assets/index-BO-7S9Xw.js.map +1 -0
  196. package/dashboard/dist/assets/{index-C7KY8GTF.js → index-BaV3l0UE.js} +2 -2
  197. package/dashboard/dist/assets/{index-C7KY8GTF.js.map → index-BaV3l0UE.js.map} +1 -1
  198. package/dashboard/dist/assets/{index-B6ldRx_s.js → index-BsOxMMmb.js} +2 -2
  199. package/dashboard/dist/assets/{index-B6ldRx_s.js.map → index-BsOxMMmb.js.map} +1 -1
  200. package/dashboard/dist/assets/index-CI8rrKjx.js +2 -0
  201. package/dashboard/dist/assets/{index-CjBdB8Ht.js.map → index-CI8rrKjx.js.map} +1 -1
  202. package/dashboard/dist/assets/index-CItsikDw.js +2 -0
  203. package/dashboard/dist/assets/index-CItsikDw.js.map +1 -0
  204. package/dashboard/dist/assets/{index-CcMIca5t.js → index-CStR4uIU.js} +2 -2
  205. package/dashboard/dist/assets/{index-CcMIca5t.js.map → index-CStR4uIU.js.map} +1 -1
  206. package/dashboard/dist/assets/{index-CSTwyrwu.js → index-DbTD-dio.js} +26 -26
  207. package/dashboard/dist/assets/index-DbTD-dio.js.map +1 -0
  208. package/dashboard/dist/assets/{index-gBnGEubX.js → index-DhATO1cS.js} +2 -2
  209. package/dashboard/dist/assets/{index-gBnGEubX.js.map → index-DhATO1cS.js.map} +1 -1
  210. package/dashboard/dist/assets/index-DnlsDaAw.css +1 -0
  211. package/dashboard/dist/assets/{index-ovFEZ9SV.js → index-Tx_ETzcH.js} +2 -2
  212. package/dashboard/dist/assets/{index-ovFEZ9SV.js.map → index-Tx_ETzcH.js.map} +1 -1
  213. package/dashboard/dist/assets/{index-DVmC_UfQ.js → index-kzIIeVbx.js} +4 -4
  214. package/dashboard/dist/assets/{index-DVmC_UfQ.js.map → index-kzIIeVbx.js.map} +1 -1
  215. package/dashboard/dist/assets/{index-BHAjQoOy.js → index-nIrYejxe.js} +3 -3
  216. package/dashboard/dist/assets/{index-BHAjQoOy.js.map → index-nIrYejxe.js.map} +1 -1
  217. package/dashboard/dist/assets/{index-Dht0ihGT.js → index-xZqQxqu2.js} +2 -2
  218. package/dashboard/dist/assets/{index-Dht0ihGT.js.map → index-xZqQxqu2.js.map} +1 -1
  219. package/dashboard/dist/assets/{knowledge-W5QSKkYQ.js → knowledge-BocTnSiA.js} +2 -2
  220. package/dashboard/dist/assets/{knowledge-W5QSKkYQ.js.map → knowledge-BocTnSiA.js.map} +1 -1
  221. package/dashboard/dist/assets/{mcp-n877HyOj.js → mcp-C1KBjKQ_.js} +2 -2
  222. package/dashboard/dist/assets/{mcp-n877HyOj.js.map → mcp-C1KBjKQ_.js.map} +1 -1
  223. package/dashboard/dist/assets/{mcp-query-C9kV4sUm.js → mcp-query-B3cjukkA.js} +2 -2
  224. package/dashboard/dist/assets/{mcp-query-C9kV4sUm.js.map → mcp-query-B3cjukkA.js.map} +1 -1
  225. package/dashboard/dist/assets/personas-DiA2bJaX.js +2 -0
  226. package/dashboard/dist/assets/personas-DiA2bJaX.js.map +1 -0
  227. package/dashboard/dist/assets/{pipelines-CBR68T8R.js → pipelines-Dt2d2EFR.js} +2 -2
  228. package/dashboard/dist/assets/{pipelines-CBR68T8R.js.map → pipelines-Dt2d2EFR.js.map} +1 -1
  229. package/dashboard/dist/assets/{tasks-DHpWqKMk.js → tasks-cDh2hNml.js} +2 -2
  230. package/dashboard/dist/assets/{tasks-DHpWqKMk.js.map → tasks-cDh2hNml.js.map} +1 -1
  231. package/dashboard/dist/assets/{topics-CaLtyqSh.js → topics-yFlGF-u1.js} +2 -2
  232. package/dashboard/dist/assets/{topics-CaLtyqSh.js.map → topics-yFlGF-u1.js.map} +1 -1
  233. package/dashboard/dist/assets/{useEventHooks-B8p56JlB.js → useEventHooks-D2R_ckS8.js} +2 -2
  234. package/dashboard/dist/assets/{useEventHooks-B8p56JlB.js.map → useEventHooks-D2R_ckS8.js.map} +1 -1
  235. package/dashboard/dist/assets/{useFilterParams-CvRyYHJN.js → useFilterParams-B7suPsIK.js} +2 -2
  236. package/dashboard/dist/assets/{useFilterParams-CvRyYHJN.js.map → useFilterParams-B7suPsIK.js.map} +1 -1
  237. package/dashboard/dist/assets/{useNamespace-ej71FCvu.js → useNamespace-u-V1GVUD.js} +2 -2
  238. package/dashboard/dist/assets/{useNamespace-ej71FCvu.js.map → useNamespace-u-V1GVUD.js.map} +1 -1
  239. package/dashboard/dist/assets/{useYamlActivityEvents-DgpiFXib.js → useYamlActivityEvents-BhOZeqgE.js} +2 -2
  240. package/dashboard/dist/assets/{useYamlActivityEvents-DgpiFXib.js.map → useYamlActivityEvents-BhOZeqgE.js.map} +1 -1
  241. package/dashboard/dist/assets/{users-CHnjI_Ca.js → users-BoXPwdee.js} +2 -2
  242. package/dashboard/dist/assets/{users-CHnjI_Ca.js.map → users-BoXPwdee.js.map} +1 -1
  243. package/dashboard/dist/assets/{vendor-icons-Bg6eJX4Z.js → vendor-icons-D71XjkNy.js} +133 -128
  244. package/dashboard/dist/assets/vendor-icons-D71XjkNy.js.map +1 -0
  245. package/dashboard/dist/assets/{vendor-react-BpdAFEQc.js → vendor-react-C1YOyCSV.js} +2 -2
  246. package/dashboard/dist/assets/{vendor-react-BpdAFEQc.js.map → vendor-react-C1YOyCSV.js.map} +1 -1
  247. package/dashboard/dist/assets/{workflows-DtnXUL0U.js → workflows-mCoyKuLZ.js} +2 -2
  248. package/dashboard/dist/assets/{workflows-DtnXUL0U.js.map → workflows-mCoyKuLZ.js.map} +1 -1
  249. package/dashboard/dist/assets/{yaml-workflows-DDclYAID.js → yaml-workflows-CWxXGDpn.js} +2 -2
  250. package/dashboard/dist/assets/{yaml-workflows-DDclYAID.js.map → yaml-workflows-CWxXGDpn.js.map} +1 -1
  251. package/dashboard/dist/index.html +4 -4
  252. package/docs/api/http/personas.md +160 -0
  253. package/docs/cli.md +12 -0
  254. package/docs/dashboard.md +25 -3
  255. package/docs/design-principles.md +13 -5
  256. package/docs/hitl/pinned-views.md +3 -3
  257. package/docs/hitl/x-lt-list-schema.md +2 -3
  258. package/docs/iam.md +47 -0
  259. package/docs/operations.md +1 -1
  260. package/docs/sdk.md +1 -0
  261. package/package.json +1 -1
  262. package/dashboard/dist/assets/AgentDetailPage-D-Xbyrb6.js +0 -4
  263. package/dashboard/dist/assets/AgentDetailPage-D-Xbyrb6.js.map +0 -1
  264. package/dashboard/dist/assets/AvailableEscalationsPage-B-aHvGKD.js +0 -2
  265. package/dashboard/dist/assets/AvailableEscalationsPage-B-aHvGKD.js.map +0 -1
  266. package/dashboard/dist/assets/CountdownTimer-BhF3oqS8.js +0 -2
  267. package/dashboard/dist/assets/CountdownTimer-BhF3oqS8.js.map +0 -1
  268. package/dashboard/dist/assets/ElapsedCell-BE_CN5sP.js +0 -2
  269. package/dashboard/dist/assets/HomePage-B6s1ZeyA.js +0 -2
  270. package/dashboard/dist/assets/HomePage-B6s1ZeyA.js.map +0 -1
  271. package/dashboard/dist/assets/ListToolbar-DVhlAKZ3.js +0 -2
  272. package/dashboard/dist/assets/ListToolbar-DVhlAKZ3.js.map +0 -1
  273. package/dashboard/dist/assets/McpQueryDetailPage-D0r2lyZg.js +0 -5
  274. package/dashboard/dist/assets/McpQueryDetailPage-D0r2lyZg.js.map +0 -1
  275. package/dashboard/dist/assets/McpQueryPage-DPP6BB3d.js +0 -2
  276. package/dashboard/dist/assets/McpQueryPage-DPP6BB3d.js.map +0 -1
  277. package/dashboard/dist/assets/OperationsPage-DbHzQCuE.js +0 -2
  278. package/dashboard/dist/assets/OperationsPage-DbHzQCuE.js.map +0 -1
  279. package/dashboard/dist/assets/OperatorDashboard-B70nS_Ah.js +0 -2
  280. package/dashboard/dist/assets/OperatorDashboard-B70nS_Ah.js.map +0 -1
  281. package/dashboard/dist/assets/ProcessDetailPage-eLLUCWFf.js +0 -2
  282. package/dashboard/dist/assets/ProcessDetailPage-eLLUCWFf.js.map +0 -1
  283. package/dashboard/dist/assets/RoleDetailPage-Dhn9zU5l.js +0 -8
  284. package/dashboard/dist/assets/RoleDetailPage-Dhn9zU5l.js.map +0 -1
  285. package/dashboard/dist/assets/RolesPage-BnAwjUiG.js +0 -2
  286. package/dashboard/dist/assets/RolesPage-BnAwjUiG.js.map +0 -1
  287. package/dashboard/dist/assets/StatCard-DBANWIss.js +0 -2
  288. package/dashboard/dist/assets/StatCard-DBANWIss.js.map +0 -1
  289. package/dashboard/dist/assets/StreamMessageDetail-BmBfNSEr.js.map +0 -1
  290. package/dashboard/dist/assets/escalation-columns-D0aYpJkF.js +0 -2
  291. package/dashboard/dist/assets/escalation-columns-D0aYpJkF.js.map +0 -1
  292. package/dashboard/dist/assets/index-B2iZG_HP.css +0 -1
  293. package/dashboard/dist/assets/index-CSTwyrwu.js.map +0 -1
  294. package/dashboard/dist/assets/index-CjBdB8Ht.js +0 -2
  295. package/dashboard/dist/assets/index-D-7mT0ew.js +0 -2
  296. package/dashboard/dist/assets/index-D-7mT0ew.js.map +0 -1
  297. package/dashboard/dist/assets/index-DDA7FvqT.js +0 -2
  298. package/dashboard/dist/assets/index-DDA7FvqT.js.map +0 -1
  299. package/dashboard/dist/assets/role-display-Dvo2lDpU.js +0 -2
  300. package/dashboard/dist/assets/role-display-Dvo2lDpU.js.map +0 -1
  301. package/dashboard/dist/assets/vendor-icons-Bg6eJX4Z.js.map +0 -1
@@ -0,0 +1,53 @@
1
+ -- Migration 024: Personas — named role bundles with per-role relationship scope
2
+ --
3
+ -- A persona is a suggestion, the same way a default pin is: shorthand for "add
4
+ -- this user to these roles with these scoped privileges." Assigning a persona
5
+ -- fans out to ordinary lt_user_roles memberships (one per linked role, mapped
6
+ -- from the persona's relationship onto the read/write scope lattice), so
7
+ -- lt_user_roles remains the single source of authorization truth — no second
8
+ -- ACL path, no hot-path changes.
9
+ --
10
+ -- lt_personas — the bundle: stable key, display title, description.
11
+ -- lt_persona_roles — the bundle's role links; relationship maps onto the
12
+ -- membership scope lattice: write-all → (all, all),
13
+ -- write-self → (all, self), read-all → (all, none).
14
+ -- lt_user_personas — who holds which persona (the auditable "why").
15
+ -- lt_user_roles.granted_by_persona — provenance: NULL means the membership was
16
+ -- granted directly; non-NULL names the persona currently
17
+ -- sustaining the row. Unassigning a persona removes only
18
+ -- rows it sustains (re-homing rows another held persona
19
+ -- still grants); direct grants are never touched, and a
20
+ -- direct grant on a persona-sustained role takes the row
21
+ -- over (provenance flips to NULL).
22
+
23
+ CREATE TABLE IF NOT EXISTS lt_personas (
24
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
25
+ key TEXT NOT NULL UNIQUE,
26
+ title TEXT,
27
+ description TEXT,
28
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
29
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
30
+ );
31
+
32
+ CREATE TABLE IF NOT EXISTS lt_persona_roles (
33
+ persona_id UUID NOT NULL REFERENCES lt_personas(id) ON DELETE CASCADE,
34
+ role TEXT NOT NULL REFERENCES lt_roles(role) ON DELETE CASCADE,
35
+ relationship TEXT NOT NULL CHECK (relationship IN ('write-all', 'write-self', 'read-all')),
36
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
37
+ PRIMARY KEY (persona_id, role)
38
+ );
39
+
40
+ CREATE TABLE IF NOT EXISTS lt_user_personas (
41
+ user_id UUID NOT NULL REFERENCES lt_users(id) ON DELETE CASCADE,
42
+ persona_id UUID NOT NULL REFERENCES lt_personas(id) ON DELETE CASCADE,
43
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
44
+ PRIMARY KEY (user_id, persona_id)
45
+ );
46
+
47
+ ALTER TABLE lt_user_roles
48
+ ADD COLUMN IF NOT EXISTS granted_by_persona UUID REFERENCES lt_personas(id) ON DELETE SET NULL;
49
+
50
+ CREATE INDEX IF NOT EXISTS idx_lt_persona_roles_role ON lt_persona_roles (role);
51
+ CREATE INDEX IF NOT EXISTS idx_lt_user_personas_persona ON lt_user_personas (persona_id);
52
+ CREATE INDEX IF NOT EXISTS idx_lt_user_roles_granted_by_persona
53
+ ON lt_user_roles (granted_by_persona) WHERE granted_by_persona IS NOT NULL;
@@ -0,0 +1,11 @@
1
+ -- Migration 025: Home Pace Board default segment
2
+ --
3
+ -- ops_home_default marks the role whose sequence leads the HOME page's Pace
4
+ -- Board. The board's full view lives on /operations; the home embed shows one
5
+ -- segment, and this flag lets an operator pick which one — a subordinate
6
+ -- setting under ops_visible on Role Detail. Single-holder: setting it on one
7
+ -- role clears it everywhere else in the same atomic statement. When no role
8
+ -- carries it, the home board falls back to the primary (first) segment.
9
+
10
+ ALTER TABLE lt_roles
11
+ ADD COLUMN IF NOT EXISTS ops_home_default BOOLEAN NOT NULL DEFAULT false;
@@ -12,6 +12,15 @@ export interface FeatureFlags {
12
12
  * Default: true. Set `features.dbMaintenance: false` in start config to hide it.
13
13
  */
14
14
  dbMaintenance: boolean;
15
+ /**
16
+ * The Pace Board is readable by every authenticated user: station metrics
17
+ * (aggregate queue-shape counts and trends — never item content) span all
18
+ * roles for any login, and the board leads everyone's home page. Default:
19
+ * true. Set `features.publicPaceBoard: false` to scope metrics back to role
20
+ * membership — the right call for deployments that provision external
21
+ * one-time accounts (e.g. a customer resolving a single form).
22
+ */
23
+ publicPaceBoard: boolean;
15
24
  }
16
25
  /** Merge a partial set of flags from the start config. Unspecified flags keep their default. */
17
26
  export declare function configureFeatureFlags(patch?: Partial<FeatureFlags>): void;
@@ -4,6 +4,7 @@ exports.configureFeatureFlags = configureFeatureFlags;
4
4
  exports.getFeatureFlags = getFeatureFlags;
5
5
  const DEFAULT_FLAGS = {
6
6
  dbMaintenance: true,
7
+ publicPaceBoard: true,
7
8
  };
8
9
  let flags = { ...DEFAULT_FLAGS };
9
10
  /** Merge a partial set of flags from the start config. Unspecified flags keep their default. */
@@ -16,6 +16,7 @@ const exports_1 = __importDefault(require("./exports"));
16
16
  const users_1 = __importDefault(require("./users"));
17
17
  const me_1 = __importDefault(require("./me"));
18
18
  const roles_1 = __importDefault(require("./roles"));
19
+ const personas_1 = __importDefault(require("./personas"));
19
20
  const dba_1 = __importDefault(require("./dba"));
20
21
  const maintenance_1 = __importDefault(require("./maintenance"));
21
22
  const mcp_1 = __importDefault(require("./mcp"));
@@ -54,6 +55,7 @@ router.use('/config/maintenance', maintenance_1.default);
54
55
  router.use('/users', users_1.default);
55
56
  router.use('/me', me_1.default);
56
57
  router.use('/roles', roles_1.default);
58
+ router.use('/personas', personas_1.default);
57
59
  router.use('/dba', dba_1.default);
58
60
  router.use('/mcp', mcp_1.default);
59
61
  router.use('/insight', insight_1.default);
@@ -0,0 +1,2 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const express_1 = require("express");
37
+ const auth_1 = require("../modules/auth");
38
+ const api = __importStar(require("../api/personas"));
39
+ const router = (0, express_1.Router)();
40
+ // Personas are role bundles — a management surface. Reads and writes share the
41
+ // role-management gate (admin type, superadmin, engineer), the same audience
42
+ // that manages roles and users.
43
+ router.use(auth_1.requireRoleManager);
44
+ /**
45
+ * GET /api/personas
46
+ * List all personas with role links and holder counts.
47
+ */
48
+ router.get('/', async (_req, res) => {
49
+ const result = await api.listPersonas();
50
+ res.status(result.status).json(result.data ?? { error: result.error });
51
+ });
52
+ /**
53
+ * POST /api/personas
54
+ * Create a persona.
55
+ * Body: { key, title?, description? }
56
+ */
57
+ router.post('/', async (req, res) => {
58
+ const result = await api.createPersona(req.body || {});
59
+ res.status(result.status).json(result.data ?? { error: result.error });
60
+ });
61
+ /**
62
+ * POST /api/personas/seed
63
+ * Declarative, idempotent seed — upsert personas, sync role links, reconcile holders.
64
+ * Body: { personas: [{ key, title?, description?, roles: [{ role, relationship }] }] }
65
+ */
66
+ router.post('/seed', async (req, res) => {
67
+ const result = await api.seedPersonas(req.body || {});
68
+ res.status(result.status).json(result.data ?? { error: result.error });
69
+ });
70
+ /**
71
+ * GET /api/personas/:key
72
+ * Get one persona with role links and assignees.
73
+ */
74
+ router.get('/:key', async (req, res) => {
75
+ const result = await api.getPersona({ key: req.params.key });
76
+ res.status(result.status).json(result.data ?? { error: result.error });
77
+ });
78
+ /**
79
+ * PATCH /api/personas/:key
80
+ * Update title/description (PATCH semantics; null clears).
81
+ */
82
+ router.patch('/:key', async (req, res) => {
83
+ const result = await api.updatePersona({ key: req.params.key, ...(req.body || {}) });
84
+ res.status(result.status).json(result.data ?? { error: result.error });
85
+ });
86
+ /**
87
+ * DELETE /api/personas/:key
88
+ * Delete a persona. Sustained memberships are removed or re-homed; direct grants untouched.
89
+ */
90
+ router.delete('/:key', async (req, res) => {
91
+ const result = await api.deletePersona({ key: req.params.key });
92
+ res.status(result.status).json(result.data ?? { error: result.error });
93
+ });
94
+ /**
95
+ * PUT /api/personas/:key/roles/:role
96
+ * Link a role (or change its relationship).
97
+ * Body: { relationship: 'write-all' | 'write-self' | 'read-all' } ('write-none' = read-all)
98
+ */
99
+ router.put('/:key/roles/:role', async (req, res) => {
100
+ const result = await api.linkPersonaRole({
101
+ key: req.params.key,
102
+ role: req.params.role,
103
+ relationship: (req.body || {}).relationship,
104
+ });
105
+ res.status(result.status).json(result.data ?? { error: result.error });
106
+ });
107
+ /**
108
+ * DELETE /api/personas/:key/roles/:role
109
+ * Unlink a role from a persona.
110
+ */
111
+ router.delete('/:key/roles/:role', async (req, res) => {
112
+ const result = await api.unlinkPersonaRole({
113
+ key: req.params.key,
114
+ role: req.params.role,
115
+ });
116
+ res.status(result.status).json(result.data ?? { error: result.error });
117
+ });
118
+ exports.default = router;
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  const express_1 = require("express");
37
37
  const auth_1 = require("../modules/auth");
38
38
  const api = __importStar(require("../api/users"));
39
+ const personasApi = __importStar(require("../api/personas"));
39
40
  const router = (0, express_1.Router)();
40
41
  // ── User CRUD ─────────────────────────────────────────────────────────────────
41
42
  /**
@@ -144,4 +145,38 @@ router.delete('/:id/roles/:role', auth_1.requireAdmin, async (req, res) => {
144
145
  const result = await api.removeUserRole({ id: req.params.id, role: req.params.role });
145
146
  res.status(result.status).json(result.data ?? { error: result.error });
146
147
  });
148
+ // ── Persona assignment ───────────────────────────────────────────────────────
149
+ // A persona assignment is shorthand for scoped role memberships, so the gate is
150
+ // the persona-management gate (admin type, superadmin, engineer).
151
+ /**
152
+ * GET /api/users/:id/personas
153
+ * The personas a user holds plus the composed role/scope map.
154
+ */
155
+ router.get('/:id/personas', async (req, res) => {
156
+ const result = await personasApi.getUserPersonas({ id: req.params.id });
157
+ res.status(result.status).json(result.data ?? { error: result.error });
158
+ });
159
+ /**
160
+ * POST /api/users/:id/personas
161
+ * Assign a persona to a user (idempotent; re-assigning overlays fresh).
162
+ * Body: { persona: '<key>' }
163
+ */
164
+ router.post('/:id/personas', auth_1.requireRoleManager, async (req, res) => {
165
+ const result = await personasApi.assignPersona({
166
+ id: req.params.id,
167
+ key: (req.body || {}).persona,
168
+ });
169
+ res.status(result.status).json(result.data ?? { error: result.error });
170
+ });
171
+ /**
172
+ * DELETE /api/users/:id/personas/:key
173
+ * Unassign a persona — removes only memberships the persona sustains.
174
+ */
175
+ router.delete('/:id/personas/:key', auth_1.requireRoleManager, async (req, res) => {
176
+ const result = await personasApi.unassignPersona({
177
+ id: req.params.id,
178
+ key: req.params.key,
179
+ });
180
+ res.status(result.status).json(result.data ?? { error: result.error });
181
+ });
147
182
  exports.default = router;
@@ -4,6 +4,7 @@ import * as workflowsApi from '../api/workflows';
4
4
  import * as yamlWorkflowsApi from '../api/yaml-workflows';
5
5
  import * as usersApi from '../api/users';
6
6
  import * as rolesApi from '../api/roles';
7
+ import * as personasApi from '../api/personas';
7
8
  import * as authApi from '../api/auth';
8
9
  import * as mcpApi from '../api/mcp';
9
10
  import * as pipelinesApi from '../api/pipelines';
@@ -299,6 +300,20 @@ export declare function createClient(options?: LTClientOptions): {
299
300
  getEscalationTargets: typeof rolesApi.getEscalationTargets;
300
301
  replaceEscalationTargets: typeof rolesApi.replaceEscalationTargets;
301
302
  };
303
+ personas: {
304
+ list: typeof personasApi.listPersonas;
305
+ get: typeof personasApi.getPersona;
306
+ create: typeof personasApi.createPersona;
307
+ update: typeof personasApi.updatePersona;
308
+ delete: typeof personasApi.deletePersona;
309
+ linkRole: typeof personasApi.linkPersonaRole;
310
+ unlinkRole: typeof personasApi.unlinkPersonaRole;
311
+ assign: typeof personasApi.assignPersona;
312
+ unassign: typeof personasApi.unassignPersona;
313
+ forUser: typeof personasApi.getUserPersonas;
314
+ /** Declarative, idempotent seed — the static-config twin of the API surface. */
315
+ seed: (personas: Parameters<typeof personasApi.seedPersonas>[0]["personas"]) => Promise<LTApiResult<any>>;
316
+ };
302
317
  auth: {
303
318
  login: typeof authApi.login;
304
319
  };
@@ -42,6 +42,7 @@ const yamlWorkflowsApi = __importStar(require("../api/yaml-workflows"));
42
42
  const usersApi = __importStar(require("../api/users"));
43
43
  const meApi = __importStar(require("../api/me"));
44
44
  const rolesApi = __importStar(require("../api/roles"));
45
+ const personasApi = __importStar(require("../api/personas"));
45
46
  const authApi = __importStar(require("../api/auth"));
46
47
  const mcpApi = __importStar(require("../api/mcp"));
47
48
  const pipelinesApi = __importStar(require("../api/pipelines"));
@@ -217,6 +218,21 @@ function createClient(options = {}) {
217
218
  getEscalationTargets: rolesApi.getEscalationTargets,
218
219
  replaceEscalationTargets: rolesApi.replaceEscalationTargets,
219
220
  },
221
+ // ── Personas — role bundles with per-role relationship scope ───────────
222
+ personas: {
223
+ list: personasApi.listPersonas,
224
+ get: personasApi.getPersona,
225
+ create: personasApi.createPersona,
226
+ update: personasApi.updatePersona,
227
+ delete: personasApi.deletePersona,
228
+ linkRole: personasApi.linkPersonaRole,
229
+ unlinkRole: personasApi.unlinkPersonaRole,
230
+ assign: personasApi.assignPersona,
231
+ unassign: personasApi.unassignPersona,
232
+ forUser: personasApi.getUserPersonas,
233
+ /** Declarative, idempotent seed — the static-config twin of the API surface. */
234
+ seed: (personas) => personasApi.seedPersonas({ personas }),
235
+ },
220
236
  // ── Auth ───────────────────────────────────────────────────────────────
221
237
  auth: {
222
238
  login: authApi.login,
@@ -0,0 +1,92 @@
1
+ import type { LTPersonaRecord, LTPersonaRelationship, LTPersonaSpec, LTUserPersona, LTComposedRoleScope } from '../../types';
2
+ import type { CreatePersonaInput, UpdatePersonaInput } from './types';
3
+ export { PERSONA_RELATIONSHIPS, normalizeRelationship, relationshipToWriteScope, } from './types';
4
+ export type { CreatePersonaInput, UpdatePersonaInput } from './types';
5
+ /** Counts from one membership reconciliation pass. */
6
+ export interface PersonaRecomputeResult {
7
+ granted: number;
8
+ refreshed: number;
9
+ raised: number;
10
+ removed: number;
11
+ }
12
+ /** An assignee row on the persona detail surface. */
13
+ export interface PersonaAssignee {
14
+ id: string;
15
+ external_id: string;
16
+ display_name: string | null;
17
+ email: string | null;
18
+ assigned_at: Date;
19
+ }
20
+ export declare function listPersonas(): Promise<LTPersonaRecord[]>;
21
+ export declare function getPersona(key: string): Promise<(LTPersonaRecord & {
22
+ assignees: PersonaAssignee[];
23
+ }) | null>;
24
+ export declare function createPersona(input: CreatePersonaInput): Promise<LTPersonaRecord>;
25
+ export declare function updatePersona(key: string, input: UpdatePersonaInput): Promise<LTPersonaRecord | null>;
26
+ /**
27
+ * Delete a persona. Holds are removed and memberships reconciled BEFORE the
28
+ * persona row is deleted, so rows it sustained are dropped (or re-homed to a
29
+ * sibling persona) rather than silently converting into direct grants.
30
+ */
31
+ export declare function deletePersona(key: string): Promise<{
32
+ deleted: boolean;
33
+ recompute?: PersonaRecomputeResult;
34
+ }>;
35
+ /**
36
+ * Link a role to a persona (or update the link's relationship). The role is
37
+ * ensured as an FK target, and every current holder's memberships are
38
+ * reconciled in the same transaction.
39
+ */
40
+ export declare function linkPersonaRole(key: string, role: string, relationship: LTPersonaRelationship): Promise<{
41
+ role: string;
42
+ relationship: LTPersonaRelationship;
43
+ recompute: PersonaRecomputeResult;
44
+ } | null>;
45
+ /** Unlink a role from a persona and reconcile every holder's memberships. */
46
+ export declare function unlinkPersonaRole(key: string, role: string): Promise<{
47
+ unlinked: boolean;
48
+ personaFound: boolean;
49
+ recompute?: PersonaRecomputeResult;
50
+ }>;
51
+ /**
52
+ * Assign a persona to a user. Idempotent: re-assigning overlays fresh — the
53
+ * user's memberships are reconciled against the persona's CURRENT role links
54
+ * (union with every other persona they hold; direct grants only ever raised,
55
+ * never lowered or removed).
56
+ */
57
+ export declare function assignPersona(userId: string, key: string): Promise<PersonaRecomputeResult | null>;
58
+ /**
59
+ * Unassign a persona from a user. Removes only memberships the persona
60
+ * sustains — rows another held persona still grants are re-homed to it, and
61
+ * direct grants are never touched.
62
+ */
63
+ export declare function unassignPersona(userId: string, key: string): Promise<{
64
+ unassigned: boolean;
65
+ personaFound: boolean;
66
+ recompute?: PersonaRecomputeResult;
67
+ }>;
68
+ /** The personas a user holds plus the composed role/scope map their memberships form. */
69
+ export declare function getUserPersonas(userId: string): Promise<{
70
+ personas: LTUserPersona[];
71
+ roles: LTComposedRoleScope[];
72
+ }>;
73
+ /**
74
+ * Declarative, idempotent persona seeding — the static-config twin of the API
75
+ * surface (like roles + default_pins in the same seed pass). Each spec is
76
+ * authoritative for its persona: title/description overlaid, role links synced
77
+ * (links absent from the spec are pruned), linked roles ensured as FK targets,
78
+ * and every holder of a seeded persona reconciled — re-running the seed after
79
+ * editing a spec re-applies it to everyone holding the persona.
80
+ *
81
+ * Relationships must be canonical (normalize aliases at the API boundary).
82
+ */
83
+ export declare function seedPersonas(specs: (Omit<LTPersonaSpec, 'roles'> & {
84
+ roles: {
85
+ role: string;
86
+ relationship: LTPersonaRelationship;
87
+ }[];
88
+ })[]): Promise<{
89
+ personas: number;
90
+ links: number;
91
+ recompute: PersonaRecomputeResult;
92
+ }>;
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.relationshipToWriteScope = exports.normalizeRelationship = exports.PERSONA_RELATIONSHIPS = void 0;
4
+ exports.listPersonas = listPersonas;
5
+ exports.getPersona = getPersona;
6
+ exports.createPersona = createPersona;
7
+ exports.updatePersona = updatePersona;
8
+ exports.deletePersona = deletePersona;
9
+ exports.linkPersonaRole = linkPersonaRole;
10
+ exports.unlinkPersonaRole = unlinkPersonaRole;
11
+ exports.assignPersona = assignPersona;
12
+ exports.unassignPersona = unassignPersona;
13
+ exports.getUserPersonas = getUserPersonas;
14
+ exports.seedPersonas = seedPersonas;
15
+ const db_1 = require("../../lib/db");
16
+ const sql_1 = require("./sql");
17
+ var types_1 = require("./types");
18
+ Object.defineProperty(exports, "PERSONA_RELATIONSHIPS", { enumerable: true, get: function () { return types_1.PERSONA_RELATIONSHIPS; } });
19
+ Object.defineProperty(exports, "normalizeRelationship", { enumerable: true, get: function () { return types_1.normalizeRelationship; } });
20
+ Object.defineProperty(exports, "relationshipToWriteScope", { enumerable: true, get: function () { return types_1.relationshipToWriteScope; } });
21
+ const EMPTY_RECOMPUTE = { granted: 0, refreshed: 0, raised: 0, removed: 0 };
22
+ // ─── Internal helpers ─────────────────────────────────────────────────────────
23
+ /** Run `work` inside a single transaction on a dedicated client. */
24
+ async function withTransaction(work) {
25
+ const client = await (0, db_1.getPool)().connect();
26
+ try {
27
+ await client.query('BEGIN');
28
+ const result = await work((text, params) => client.query(text, params));
29
+ await client.query('COMMIT');
30
+ return result;
31
+ }
32
+ catch (err) {
33
+ await client.query('ROLLBACK').catch(() => { });
34
+ throw err;
35
+ }
36
+ finally {
37
+ client.release();
38
+ }
39
+ }
40
+ /** Reconcile lt_user_roles for a set of users against their held personas. */
41
+ async function recomputeMemberships(query, userIds) {
42
+ if (userIds.length === 0)
43
+ return { ...EMPTY_RECOMPUTE };
44
+ const { rows } = await query(sql_1.RECOMPUTE_PERSONA_MEMBERSHIPS, [userIds]);
45
+ return rows[0];
46
+ }
47
+ async function resolvePersonaId(query, key) {
48
+ const { rows } = await query(sql_1.GET_PERSONA_ID_BY_KEY, [key]);
49
+ return rows[0]?.id ?? null;
50
+ }
51
+ // ─── Persona CRUD ─────────────────────────────────────────────────────────────
52
+ async function listPersonas() {
53
+ const { rows } = await (0, db_1.getPool)().query(sql_1.LIST_PERSONAS);
54
+ return rows;
55
+ }
56
+ async function getPersona(key) {
57
+ const pool = (0, db_1.getPool)();
58
+ const { rows } = await pool.query(sql_1.GET_PERSONA_BY_KEY, [key]);
59
+ if (rows.length === 0)
60
+ return null;
61
+ const { rows: assignees } = await pool.query(sql_1.GET_PERSONA_ASSIGNEES, [rows[0].id]);
62
+ return { ...rows[0], assignees };
63
+ }
64
+ async function createPersona(input) {
65
+ const { rows } = await (0, db_1.getPool)().query(sql_1.CREATE_PERSONA, [
66
+ input.key,
67
+ input.title ?? null,
68
+ input.description ?? null,
69
+ ]);
70
+ return { ...rows[0], roles: [], user_count: 0 };
71
+ }
72
+ async function updatePersona(key, input) {
73
+ const { rows } = await (0, db_1.getPool)().query(sql_1.UPDATE_PERSONA, [
74
+ key,
75
+ input.title !== undefined, input.title ?? null,
76
+ input.description !== undefined, input.description ?? null,
77
+ ]);
78
+ if (rows.length === 0)
79
+ return null;
80
+ const detail = await getPersona(key);
81
+ return detail;
82
+ }
83
+ /**
84
+ * Delete a persona. Holds are removed and memberships reconciled BEFORE the
85
+ * persona row is deleted, so rows it sustained are dropped (or re-homed to a
86
+ * sibling persona) rather than silently converting into direct grants.
87
+ */
88
+ async function deletePersona(key) {
89
+ return withTransaction(async (query) => {
90
+ const personaId = await resolvePersonaId(query, key);
91
+ if (!personaId)
92
+ return { deleted: false };
93
+ const { rows: holders } = await query(`DELETE FROM lt_user_personas WHERE persona_id = $1 RETURNING user_id`, [personaId]);
94
+ // Guard against drift: also reconcile any user with a row still sustained
95
+ // by this persona, even if no hold record remained.
96
+ const { rows: sustained } = await query(sql_1.LIST_SUSTAINED_USER_IDS, [personaId]);
97
+ const affected = [...new Set([...holders, ...sustained].map((r) => r.user_id))];
98
+ const recompute = await recomputeMemberships(query, affected);
99
+ await query(sql_1.DELETE_PERSONA, [personaId]);
100
+ return { deleted: true, recompute };
101
+ });
102
+ }
103
+ // ─── Role links ───────────────────────────────────────────────────────────────
104
+ /**
105
+ * Link a role to a persona (or update the link's relationship). The role is
106
+ * ensured as an FK target, and every current holder's memberships are
107
+ * reconciled in the same transaction.
108
+ */
109
+ async function linkPersonaRole(key, role, relationship) {
110
+ return withTransaction(async (query) => {
111
+ const personaId = await resolvePersonaId(query, key);
112
+ if (!personaId)
113
+ return null;
114
+ await query(sql_1.ENSURE_ROLES_EXIST, [[role]]);
115
+ const { rows } = await query(sql_1.UPSERT_PERSONA_ROLE, [personaId, role, relationship]);
116
+ const { rows: holders } = await query(sql_1.LIST_PERSONA_HOLDER_IDS, [personaId]);
117
+ const recompute = await recomputeMemberships(query, holders.map((r) => r.user_id));
118
+ return { ...rows[0], recompute };
119
+ });
120
+ }
121
+ /** Unlink a role from a persona and reconcile every holder's memberships. */
122
+ async function unlinkPersonaRole(key, role) {
123
+ return withTransaction(async (query) => {
124
+ const personaId = await resolvePersonaId(query, key);
125
+ if (!personaId)
126
+ return { unlinked: false, personaFound: false };
127
+ const { rowCount } = await query(sql_1.DELETE_PERSONA_ROLE, [personaId, role]);
128
+ if ((rowCount ?? 0) === 0)
129
+ return { unlinked: false, personaFound: true };
130
+ const { rows: holders } = await query(sql_1.LIST_PERSONA_HOLDER_IDS, [personaId]);
131
+ const recompute = await recomputeMemberships(query, holders.map((r) => r.user_id));
132
+ return { unlinked: true, personaFound: true, recompute };
133
+ });
134
+ }
135
+ // ─── Assignment ───────────────────────────────────────────────────────────────
136
+ /**
137
+ * Assign a persona to a user. Idempotent: re-assigning overlays fresh — the
138
+ * user's memberships are reconciled against the persona's CURRENT role links
139
+ * (union with every other persona they hold; direct grants only ever raised,
140
+ * never lowered or removed).
141
+ */
142
+ async function assignPersona(userId, key) {
143
+ return withTransaction(async (query) => {
144
+ const personaId = await resolvePersonaId(query, key);
145
+ if (!personaId)
146
+ return null;
147
+ await query(sql_1.INSERT_USER_PERSONA, [userId, personaId]);
148
+ return recomputeMemberships(query, [userId]);
149
+ });
150
+ }
151
+ /**
152
+ * Unassign a persona from a user. Removes only memberships the persona
153
+ * sustains — rows another held persona still grants are re-homed to it, and
154
+ * direct grants are never touched.
155
+ */
156
+ async function unassignPersona(userId, key) {
157
+ return withTransaction(async (query) => {
158
+ const personaId = await resolvePersonaId(query, key);
159
+ if (!personaId)
160
+ return { unassigned: false, personaFound: false };
161
+ const { rowCount } = await query(sql_1.DELETE_USER_PERSONA, [userId, personaId]);
162
+ if ((rowCount ?? 0) === 0)
163
+ return { unassigned: false, personaFound: true };
164
+ const recompute = await recomputeMemberships(query, [userId]);
165
+ return { unassigned: true, personaFound: true, recompute };
166
+ });
167
+ }
168
+ /** The personas a user holds plus the composed role/scope map their memberships form. */
169
+ async function getUserPersonas(userId) {
170
+ const pool = (0, db_1.getPool)();
171
+ const [{ rows: personas }, { rows: roles }] = await Promise.all([
172
+ pool.query(sql_1.GET_USER_PERSONAS, [userId]),
173
+ pool.query(sql_1.GET_USER_COMPOSED_ROLES, [userId]),
174
+ ]);
175
+ return { personas, roles };
176
+ }
177
+ // ─── Declarative seed ─────────────────────────────────────────────────────────
178
+ /**
179
+ * Declarative, idempotent persona seeding — the static-config twin of the API
180
+ * surface (like roles + default_pins in the same seed pass). Each spec is
181
+ * authoritative for its persona: title/description overlaid, role links synced
182
+ * (links absent from the spec are pruned), linked roles ensured as FK targets,
183
+ * and every holder of a seeded persona reconciled — re-running the seed after
184
+ * editing a spec re-applies it to everyone holding the persona.
185
+ *
186
+ * Relationships must be canonical (normalize aliases at the API boundary).
187
+ */
188
+ async function seedPersonas(specs) {
189
+ if (specs.length === 0)
190
+ return { personas: 0, links: 0, recompute: { ...EMPTY_RECOMPUTE } };
191
+ return withTransaction(async (query) => {
192
+ const allRoles = [...new Set(specs.flatMap((s) => s.roles.map((r) => r.role)))];
193
+ if (allRoles.length > 0) {
194
+ await query(sql_1.ENSURE_ROLES_EXIST, [allRoles]);
195
+ }
196
+ const personaIds = [];
197
+ let links = 0;
198
+ for (const spec of specs) {
199
+ const { rows } = await query(sql_1.UPSERT_PERSONA, [
200
+ spec.key,
201
+ spec.title ?? null,
202
+ spec.description ?? null,
203
+ ]);
204
+ const personaId = rows[0].id;
205
+ personaIds.push(personaId);
206
+ await query(sql_1.SYNC_PERSONA_ROLES, [
207
+ personaId,
208
+ spec.roles.map((r) => r.role),
209
+ spec.roles.map((r) => r.relationship),
210
+ ]);
211
+ links += spec.roles.length;
212
+ }
213
+ const { rows: holders } = await query('SELECT DISTINCT user_id FROM lt_user_personas WHERE persona_id = ANY($1::uuid[])', [personaIds]);
214
+ const recompute = await recomputeMemberships(query, holders.map((r) => r.user_id));
215
+ return { personas: specs.length, links, recompute };
216
+ });
217
+ }