@hotmeshio/long-tail 0.10.8 → 0.10.10

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 (296) hide show
  1. package/build/api/escalations/bulk.js +7 -1
  2. package/build/api/escalations/helpers.d.ts +10 -1
  3. package/build/api/escalations/helpers.js +52 -12
  4. package/build/api/index.d.ts +1 -0
  5. package/build/api/index.js +2 -1
  6. package/build/api/personas.d.ts +121 -0
  7. package/build/api/personas.js +341 -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/events/matching.d.ts +10 -0
  13. package/build/lib/events/matching.js +14 -0
  14. package/build/lib/events/publish.d.ts +8 -2
  15. package/build/lib/events/publish.js +9 -2
  16. package/build/lib/events/socketio.d.ts +8 -2
  17. package/build/lib/events/socketio.js +36 -3
  18. package/build/lib/events/system-events.js +9 -1
  19. package/build/routes/index.js +2 -0
  20. package/build/routes/personas.d.ts +2 -0
  21. package/build/routes/personas.js +118 -0
  22. package/build/routes/users.js +35 -0
  23. package/build/sdk/index.d.ts +15 -0
  24. package/build/sdk/index.js +16 -0
  25. package/build/services/escalation/crud.js +40 -2
  26. package/build/services/orchestrator/condition.d.ts +1 -1
  27. package/build/services/orchestrator/condition.js +1 -1
  28. package/build/services/persona/index.d.ts +92 -0
  29. package/build/services/persona/index.js +217 -0
  30. package/build/services/persona/sql.d.ts +24 -0
  31. package/build/services/persona/sql.js +158 -0
  32. package/build/services/persona/types.d.ts +23 -0
  33. package/build/services/persona/types.js +33 -0
  34. package/build/services/topics/system-topics.d.ts +0 -4
  35. package/build/services/topics/system-topics.js +57 -14
  36. package/build/services/user/sql.d.ts +8 -5
  37. package/build/services/user/sql.js +15 -5
  38. package/build/system/mcp-servers/admin/index.d.ts +1 -0
  39. package/build/system/mcp-servers/admin/index.js +5 -2
  40. package/build/system/mcp-servers/admin/personas.d.ts +8 -0
  41. package/build/system/mcp-servers/admin/personas.js +186 -0
  42. package/build/system/mcp-servers/admin/schemas.d.ts +91 -0
  43. package/build/system/mcp-servers/admin/schemas.js +42 -2
  44. package/build/system/seed/tool-manifests-admin.d.ts +975 -0
  45. package/build/system/seed/tool-manifests-admin.js +11 -0
  46. package/build/tsconfig.tsbuildinfo +1 -1
  47. package/build/types/events.d.ts +11 -3
  48. package/build/types/index.d.ts +1 -0
  49. package/build/types/persona.d.ts +56 -0
  50. package/build/types/persona.js +2 -0
  51. package/build/types/user.d.ts +2 -0
  52. package/dashboard/dist/assets/{AdminDashboard-D0HdEkT9.js → AdminDashboard-cGYuppUP.js} +2 -2
  53. package/dashboard/dist/assets/{AdminDashboard-D0HdEkT9.js.map → AdminDashboard-cGYuppUP.js.map} +1 -1
  54. package/dashboard/dist/assets/{AgentConfigPage-CtURUMOV.js → AgentConfigPage-DwaUyoCy.js} +2 -2
  55. package/dashboard/dist/assets/{AgentConfigPage-CtURUMOV.js.map → AgentConfigPage-DwaUyoCy.js.map} +1 -1
  56. package/dashboard/dist/assets/AgentDetailPage-DxpFaltM.js +4 -0
  57. package/dashboard/dist/assets/AgentDetailPage-DxpFaltM.js.map +1 -0
  58. package/dashboard/dist/assets/{AgentsPage-DBequneI.js → AgentsPage-ByOoA8oT.js} +2 -2
  59. package/dashboard/dist/assets/{AgentsPage-DBequneI.js.map → AgentsPage-ByOoA8oT.js.map} +1 -1
  60. package/dashboard/dist/assets/AutoGrowTextarea-y-oNY51_.js +2 -0
  61. package/dashboard/dist/assets/AutoGrowTextarea-y-oNY51_.js.map +1 -0
  62. package/dashboard/dist/assets/{AvailableEscalationsPage-Bk7fFV2e.js → AvailableEscalationsPage-C1vtTvqP.js} +2 -2
  63. package/dashboard/dist/assets/AvailableEscalationsPage-C1vtTvqP.js.map +1 -0
  64. package/dashboard/dist/assets/{BotPicker-BMXokkH7.js → BotPicker-Dohv8dhR.js} +2 -2
  65. package/dashboard/dist/assets/{BotPicker-BMXokkH7.js.map → BotPicker-Dohv8dhR.js.map} +1 -1
  66. package/dashboard/dist/assets/{CapabilitiesPage-w-D6xraE.js → CapabilitiesPage-DeijFG3J.js} +2 -2
  67. package/dashboard/dist/assets/{CapabilitiesPage-w-D6xraE.js.map → CapabilitiesPage-DeijFG3J.js.map} +1 -1
  68. package/dashboard/dist/assets/{CollapsibleSection-IPL1H1bQ.js → CollapsibleSection-BPNwS4eO.js} +2 -2
  69. package/dashboard/dist/assets/{CollapsibleSection-IPL1H1bQ.js.map → CollapsibleSection-BPNwS4eO.js.map} +1 -1
  70. package/dashboard/dist/assets/CountdownTimer-BF71ldGF.js +2 -0
  71. package/dashboard/dist/assets/CountdownTimer-BF71ldGF.js.map +1 -0
  72. package/dashboard/dist/assets/{CredentialsPage-DR48hDES.js → CredentialsPage-oeNmMzTM.js} +2 -2
  73. package/dashboard/dist/assets/{CredentialsPage-DR48hDES.js.map → CredentialsPage-oeNmMzTM.js.map} +1 -1
  74. package/dashboard/dist/assets/{CronLabel-KQEj0cDS.js → CronLabel-CebR1m2k.js} +2 -2
  75. package/dashboard/dist/assets/{CronLabel-KQEj0cDS.js.map → CronLabel-CebR1m2k.js.map} +1 -1
  76. package/dashboard/dist/assets/CustomDurationPicker-Bh9QdBVw.js +2 -0
  77. package/dashboard/dist/assets/CustomDurationPicker-Bh9QdBVw.js.map +1 -0
  78. package/dashboard/dist/assets/{DropZone-BrnAkQxF.js → DropZone-Bavzt2QR.js} +2 -2
  79. package/dashboard/dist/assets/{DropZone-BrnAkQxF.js.map → DropZone-Bavzt2QR.js.map} +1 -1
  80. package/dashboard/dist/assets/ElapsedCell-Caa6CU_y.js +2 -0
  81. package/dashboard/dist/assets/{ElapsedCell-Cflz7Pb5.js.map → ElapsedCell-Caa6CU_y.js.map} +1 -1
  82. package/dashboard/dist/assets/{EscalationListSchemaPage-BTBZ8OBY.js → EscalationListSchemaPage-CAGfq0eK.js} +3 -3
  83. package/dashboard/dist/assets/{EscalationListSchemaPage-BTBZ8OBY.js.map → EscalationListSchemaPage-CAGfq0eK.js.map} +1 -1
  84. package/dashboard/dist/assets/{EscalationSchemaPage-Cd9fR2EA.js → EscalationSchemaPage-qDD4ur6P.js} +2 -2
  85. package/dashboard/dist/assets/{EscalationSchemaPage-Cd9fR2EA.js.map → EscalationSchemaPage-qDD4ur6P.js.map} +1 -1
  86. package/dashboard/dist/assets/{EscalationsOverview-BzNFcA0i.js → EscalationsOverview-CC1wRTzy.js} +2 -2
  87. package/dashboard/dist/assets/{EscalationsOverview-BzNFcA0i.js.map → EscalationsOverview-CC1wRTzy.js.map} +1 -1
  88. package/dashboard/dist/assets/{EventTable-S2BJqOt2.js → EventTable-4JeTcXr_.js} +2 -2
  89. package/dashboard/dist/assets/{EventTable-S2BJqOt2.js.map → EventTable-4JeTcXr_.js.map} +1 -1
  90. package/dashboard/dist/assets/{FilterBar-CeXwNVn6.js → FilterBar-BOV8DWIO.js} +2 -2
  91. package/dashboard/dist/assets/{FilterBar-CeXwNVn6.js.map → FilterBar-BOV8DWIO.js.map} +1 -1
  92. package/dashboard/dist/assets/{GraphInvokePage-CBjXjOLX.js → GraphInvokePage-C0i1speW.js} +2 -2
  93. package/dashboard/dist/assets/{GraphInvokePage-CBjXjOLX.js.map → GraphInvokePage-C0i1speW.js.map} +1 -1
  94. package/dashboard/dist/assets/HomePage-HPCd8SjF.js +2 -0
  95. package/dashboard/dist/assets/HomePage-HPCd8SjF.js.map +1 -0
  96. package/dashboard/dist/assets/{ListToolbar-Dx6DuFdR.js → ListToolbar-BFHVOba6.js} +2 -2
  97. package/dashboard/dist/assets/{ListToolbar-Dx6DuFdR.js.map → ListToolbar-BFHVOba6.js.map} +1 -1
  98. package/dashboard/dist/assets/{McpOverview-Cj8_UyWJ.js → McpOverview-Chn2zIXr.js} +2 -2
  99. package/dashboard/dist/assets/{McpOverview-Cj8_UyWJ.js.map → McpOverview-Chn2zIXr.js.map} +1 -1
  100. package/dashboard/dist/assets/McpQueryDetailPage-Cv1FKrvw.js +5 -0
  101. package/dashboard/dist/assets/McpQueryDetailPage-Cv1FKrvw.js.map +1 -0
  102. package/dashboard/dist/assets/McpQueryPage-Dzn8JE4E.js +2 -0
  103. package/dashboard/dist/assets/McpQueryPage-Dzn8JE4E.js.map +1 -0
  104. package/dashboard/dist/assets/{McpRunDetailPage-BqhRwkGk.js → McpRunDetailPage-CuNtR4Ed.js} +2 -2
  105. package/dashboard/dist/assets/{McpRunDetailPage-BqhRwkGk.js.map → McpRunDetailPage-CuNtR4Ed.js.map} +1 -1
  106. package/dashboard/dist/assets/{McpRunsPage-CNXYU4iX.js → McpRunsPage-C8iifeKN.js} +2 -2
  107. package/dashboard/dist/assets/{McpRunsPage-CNXYU4iX.js.map → McpRunsPage-C8iifeKN.js.map} +1 -1
  108. package/dashboard/dist/assets/{NamespacePill-DQGzN0Ph.js → NamespacePill-ep9Zmokr.js} +2 -2
  109. package/dashboard/dist/assets/{NamespacePill-DQGzN0Ph.js.map → NamespacePill-ep9Zmokr.js.map} +1 -1
  110. package/dashboard/dist/assets/{OperationsPage-C7UB_d-X.js → OperationsPage-flrZ-EJ9.js} +2 -2
  111. package/dashboard/dist/assets/{OperationsPage-C7UB_d-X.js.map → OperationsPage-flrZ-EJ9.js.map} +1 -1
  112. package/dashboard/dist/assets/OperatorDashboard-Dhs2eo8p.js +2 -0
  113. package/dashboard/dist/assets/OperatorDashboard-Dhs2eo8p.js.map +1 -0
  114. package/dashboard/dist/assets/{PageHeader-DlcE3U5L.js → PageHeader-Bw8RNkqR.js} +2 -2
  115. package/dashboard/dist/assets/{PageHeader-DlcE3U5L.js.map → PageHeader-Bw8RNkqR.js.map} +1 -1
  116. package/dashboard/dist/assets/{PageHeaderWithStats-BMhnR3-9.js → PageHeaderWithStats-NR_4idn6.js} +2 -2
  117. package/dashboard/dist/assets/{PageHeaderWithStats-BMhnR3-9.js.map → PageHeaderWithStats-NR_4idn6.js.map} +1 -1
  118. package/dashboard/dist/assets/PersonaDetailPage-DKILoUqL.js +2 -0
  119. package/dashboard/dist/assets/PersonaDetailPage-DKILoUqL.js.map +1 -0
  120. package/dashboard/dist/assets/PersonasPage-qvzHTrFw.js +2 -0
  121. package/dashboard/dist/assets/PersonasPage-qvzHTrFw.js.map +1 -0
  122. package/dashboard/dist/assets/ProcessDetailPage-C3LfeKvi.js +2 -0
  123. package/dashboard/dist/assets/ProcessDetailPage-C3LfeKvi.js.map +1 -0
  124. package/dashboard/dist/assets/{ProcessesListPage-DatKHVkd.js → ProcessesListPage-OoZ_u4ty.js} +2 -2
  125. package/dashboard/dist/assets/{ProcessesListPage-DatKHVkd.js.map → ProcessesListPage-OoZ_u4ty.js.map} +1 -1
  126. package/dashboard/dist/assets/RoleDetailPage-D70jTLZm.js +8 -0
  127. package/dashboard/dist/assets/RoleDetailPage-D70jTLZm.js.map +1 -0
  128. package/dashboard/dist/assets/{RolePill-dzo14hB_.js → RolePill-TySZq7dB.js} +2 -2
  129. package/dashboard/dist/assets/{RolePill-dzo14hB_.js.map → RolePill-TySZq7dB.js.map} +1 -1
  130. package/dashboard/dist/assets/RolesPage-CNueMVuJ.js +2 -0
  131. package/dashboard/dist/assets/RolesPage-CNueMVuJ.js.map +1 -0
  132. package/dashboard/dist/assets/{RunAsSelector-VbBn0o7s.js → RunAsSelector-CwWEpiSF.js} +2 -2
  133. package/dashboard/dist/assets/{RunAsSelector-VbBn0o7s.js.map → RunAsSelector-CwWEpiSF.js.map} +1 -1
  134. package/dashboard/dist/assets/ScopeBadge-C7Uixtmk.js +2 -0
  135. package/dashboard/dist/assets/ScopeBadge-C7Uixtmk.js.map +1 -0
  136. package/dashboard/dist/assets/StatCard-Ca-ngCaD.js +2 -0
  137. package/dashboard/dist/assets/StatCard-Ca-ngCaD.js.map +1 -0
  138. package/dashboard/dist/assets/{StatusBadge-BbqghFMi.js → StatusBadge-BwUvykDo.js} +2 -2
  139. package/dashboard/dist/assets/{StatusBadge-BbqghFMi.js.map → StatusBadge-BwUvykDo.js.map} +1 -1
  140. package/dashboard/dist/assets/{StickyPagination-BI8tDf2V.js → StickyPagination-DvIF3kQK.js} +2 -2
  141. package/dashboard/dist/assets/{StickyPagination-BI8tDf2V.js.map → StickyPagination-DvIF3kQK.js.map} +1 -1
  142. package/dashboard/dist/assets/{StreamMessageDetail-BObbEz7P.js → StreamMessageDetail-DkFK_XGD.js} +2 -2
  143. package/dashboard/dist/assets/StreamMessageDetail-DkFK_XGD.js.map +1 -0
  144. package/dashboard/dist/assets/{SwimlaneTimeline-BHjAtOGC.js → SwimlaneTimeline-CMw-Et6h.js} +2 -2
  145. package/dashboard/dist/assets/{SwimlaneTimeline-BHjAtOGC.js.map → SwimlaneTimeline-CMw-Et6h.js.map} +1 -1
  146. package/dashboard/dist/assets/{TagInput-cyhTZTwe.js → TagInput-D8gSp48D.js} +2 -2
  147. package/dashboard/dist/assets/{TagInput-cyhTZTwe.js.map → TagInput-D8gSp48D.js.map} +1 -1
  148. package/dashboard/dist/assets/{TaskDetailPage-B-E1dqUw.js → TaskDetailPage-BhXuChOf.js} +2 -2
  149. package/dashboard/dist/assets/{TaskDetailPage-B-E1dqUw.js.map → TaskDetailPage-BhXuChOf.js.map} +1 -1
  150. package/dashboard/dist/assets/{TaskQueuePill-_zIpG4bG.js → TaskQueuePill-CHqolBmU.js} +2 -2
  151. package/dashboard/dist/assets/{TaskQueuePill-_zIpG4bG.js.map → TaskQueuePill-CHqolBmU.js.map} +1 -1
  152. package/dashboard/dist/assets/{TasksListPage-urJ4XiUi.js → TasksListPage-6JHQQ5QZ.js} +2 -2
  153. package/dashboard/dist/assets/{TasksListPage-urJ4XiUi.js.map → TasksListPage-6JHQQ5QZ.js.map} +1 -1
  154. package/dashboard/dist/assets/{TimeAgo-D6Dg0SQs.js → TimeAgo-CInx-vzY.js} +2 -2
  155. package/dashboard/dist/assets/{TimeAgo-D6Dg0SQs.js.map → TimeAgo-CInx-vzY.js.map} +1 -1
  156. package/dashboard/dist/assets/{TimestampCell-DG__Gr-e.js → TimestampCell-Fq17Zrsu.js} +2 -2
  157. package/dashboard/dist/assets/{TimestampCell-DG__Gr-e.js.map → TimestampCell-Fq17Zrsu.js.map} +1 -1
  158. package/dashboard/dist/assets/{ToolPill-CKHYG6eV.js → ToolPill-BbgMDhaE.js} +2 -2
  159. package/dashboard/dist/assets/{ToolPill-CKHYG6eV.js.map → ToolPill-BbgMDhaE.js.map} +1 -1
  160. package/dashboard/dist/assets/{ToolTestPanel-D-hGLf95.js → ToolTestPanel-DhjEe59s.js} +2 -2
  161. package/dashboard/dist/assets/{ToolTestPanel-D-hGLf95.js.map → ToolTestPanel-DhjEe59s.js.map} +1 -1
  162. package/dashboard/dist/assets/{TopicDetailPage-B10i4xV2.js → TopicDetailPage-DbIdmQpQ.js} +3 -3
  163. package/dashboard/dist/assets/{TopicDetailPage-B10i4xV2.js.map → TopicDetailPage-DbIdmQpQ.js.map} +1 -1
  164. package/dashboard/dist/assets/TopicsPage-CAP-GUbr.js +2 -0
  165. package/dashboard/dist/assets/{TopicsPage-C5THflVQ.js.map → TopicsPage-CAP-GUbr.js.map} +1 -1
  166. package/dashboard/dist/assets/{UserName-CZTpouta.js → UserName-DggcLkSc.js} +2 -2
  167. package/dashboard/dist/assets/{UserName-CZTpouta.js.map → UserName-DggcLkSc.js.map} +1 -1
  168. package/dashboard/dist/assets/{WorkflowExecutionPage-mbnj6saI.js → WorkflowExecutionPage-Bmra5fVt.js} +2 -2
  169. package/dashboard/dist/assets/{WorkflowExecutionPage-mbnj6saI.js.map → WorkflowExecutionPage-Bmra5fVt.js.map} +1 -1
  170. package/dashboard/dist/assets/{WorkflowPill-DlSVXnnj.js → WorkflowPill-CcUUVugA.js} +2 -2
  171. package/dashboard/dist/assets/{WorkflowPill-DlSVXnnj.js.map → WorkflowPill-CcUUVugA.js.map} +1 -1
  172. package/dashboard/dist/assets/{WorkflowsDashboard-DC_9GrpY.js → WorkflowsDashboard-BQsZAGYU.js} +2 -2
  173. package/dashboard/dist/assets/{WorkflowsDashboard-DC_9GrpY.js.map → WorkflowsDashboard-BQsZAGYU.js.map} +1 -1
  174. package/dashboard/dist/assets/{WorkflowsOverview-ClSH1UwG.js → WorkflowsOverview-CeliraHZ.js} +2 -2
  175. package/dashboard/dist/assets/{WorkflowsOverview-ClSH1UwG.js.map → WorkflowsOverview-CeliraHZ.js.map} +1 -1
  176. package/dashboard/dist/assets/{YamlWorkflowDetailPage-BkgoB7oD.js → YamlWorkflowDetailPage-DGxlonrV.js} +2 -2
  177. package/dashboard/dist/assets/{YamlWorkflowDetailPage-BkgoB7oD.js.map → YamlWorkflowDetailPage-DGxlonrV.js.map} +1 -1
  178. package/dashboard/dist/assets/{YamlWorkflowsPage-Db_REA2f.js → YamlWorkflowsPage-DnT8UiyQ.js} +2 -2
  179. package/dashboard/dist/assets/{YamlWorkflowsPage-Db_REA2f.js.map → YamlWorkflowsPage-DnT8UiyQ.js.map} +1 -1
  180. package/dashboard/dist/assets/{agents-Dvb8j9AB.js → agents-mRV7m4sh.js} +2 -2
  181. package/dashboard/dist/assets/{agents-Dvb8j9AB.js.map → agents-mRV7m4sh.js.map} +1 -1
  182. package/dashboard/dist/assets/{bots-D9jYHjBM.js → bots-SONCZlCM.js} +2 -2
  183. package/dashboard/dist/assets/{bots-D9jYHjBM.js.map → bots-SONCZlCM.js.map} +1 -1
  184. package/dashboard/dist/assets/{capabilities-BAto9JcQ.js → capabilities-DZjCMN32.js} +2 -2
  185. package/dashboard/dist/assets/{capabilities-BAto9JcQ.js.map → capabilities-DZjCMN32.js.map} +1 -1
  186. package/dashboard/dist/assets/{controlplane-2Aic2Tfm.js → controlplane-RQNXjna2.js} +2 -2
  187. package/dashboard/dist/assets/{controlplane-2Aic2Tfm.js.map → controlplane-RQNXjna2.js.map} +1 -1
  188. package/dashboard/dist/assets/{escalation-columns-DPKPJtw3.js → escalation-columns-XLEcZRCg.js} +2 -2
  189. package/dashboard/dist/assets/{escalation-columns-DPKPJtw3.js.map → escalation-columns-XLEcZRCg.js.map} +1 -1
  190. package/dashboard/dist/assets/index-2BwHxjCw.css +1 -0
  191. package/dashboard/dist/assets/{index-BZbDwh_Y.js → index-C6GsrvX1.js} +2 -2
  192. package/dashboard/dist/assets/{index-BZbDwh_Y.js.map → index-C6GsrvX1.js.map} +1 -1
  193. package/dashboard/dist/assets/{index-BzQIzZfd.js → index-CK-6m8e_.js} +2 -2
  194. package/dashboard/dist/assets/{index-BzQIzZfd.js.map → index-CK-6m8e_.js.map} +1 -1
  195. package/dashboard/dist/assets/index-CkeSxuZ0.js +2 -0
  196. package/dashboard/dist/assets/{index-B5JgykZD.js.map → index-CkeSxuZ0.js.map} +1 -1
  197. package/dashboard/dist/assets/{index-BW7-Q9Qf.js → index-CxVGlkK6.js} +2 -2
  198. package/dashboard/dist/assets/{index-BW7-Q9Qf.js.map → index-CxVGlkK6.js.map} +1 -1
  199. package/dashboard/dist/assets/index-D2CfSx_x.js +5 -0
  200. package/dashboard/dist/assets/index-D2CfSx_x.js.map +1 -0
  201. package/dashboard/dist/assets/{index-4zRmxPwA.js → index-DFJF8aWz.js} +3 -3
  202. package/dashboard/dist/assets/{index-4zRmxPwA.js.map → index-DFJF8aWz.js.map} +1 -1
  203. package/dashboard/dist/assets/index-DYKRtstT.js +2 -0
  204. package/dashboard/dist/assets/index-DYKRtstT.js.map +1 -0
  205. package/dashboard/dist/assets/index-DY_hlZCQ.js +2 -0
  206. package/dashboard/dist/assets/index-DY_hlZCQ.js.map +1 -0
  207. package/dashboard/dist/assets/{index-KkikDsJ2.js → index-DbZpL10z.js} +2 -2
  208. package/dashboard/dist/assets/{index-KkikDsJ2.js.map → index-DbZpL10z.js.map} +1 -1
  209. package/dashboard/dist/assets/index-Dh3CVChT.js +63 -0
  210. package/dashboard/dist/assets/index-Dh3CVChT.js.map +1 -0
  211. package/dashboard/dist/assets/{index-peRHL38z.js → index-DqsLfO-0.js} +2 -2
  212. package/dashboard/dist/assets/{index-peRHL38z.js.map → index-DqsLfO-0.js.map} +1 -1
  213. package/dashboard/dist/assets/{index-C-T1Wq0Z.js → index-iHV3d7o3.js} +2 -2
  214. package/dashboard/dist/assets/{index-C-T1Wq0Z.js.map → index-iHV3d7o3.js.map} +1 -1
  215. package/dashboard/dist/assets/{index-CeM012DJ.js → index-ymm58cTW.js} +2 -2
  216. package/dashboard/dist/assets/{index-CeM012DJ.js.map → index-ymm58cTW.js.map} +1 -1
  217. package/dashboard/dist/assets/{knowledge-DO8idX0e.js → knowledge-Bawo8u7l.js} +2 -2
  218. package/dashboard/dist/assets/{knowledge-DO8idX0e.js.map → knowledge-Bawo8u7l.js.map} +1 -1
  219. package/dashboard/dist/assets/{mcp-BGpQlkFp.js → mcp-CKIpbpRZ.js} +2 -2
  220. package/dashboard/dist/assets/{mcp-BGpQlkFp.js.map → mcp-CKIpbpRZ.js.map} +1 -1
  221. package/dashboard/dist/assets/{mcp-query-DT0SCB9l.js → mcp-query-vld4hYxQ.js} +2 -2
  222. package/dashboard/dist/assets/{mcp-query-DT0SCB9l.js.map → mcp-query-vld4hYxQ.js.map} +1 -1
  223. package/dashboard/dist/assets/personas-BsARc3df.js +2 -0
  224. package/dashboard/dist/assets/personas-BsARc3df.js.map +1 -0
  225. package/dashboard/dist/assets/{pipelines-CYk7cN6-.js → pipelines-BTSL-5ko.js} +2 -2
  226. package/dashboard/dist/assets/{pipelines-CYk7cN6-.js.map → pipelines-BTSL-5ko.js.map} +1 -1
  227. package/dashboard/dist/assets/{tasks-B4Y5xbF0.js → tasks-DggQdNbj.js} +2 -2
  228. package/dashboard/dist/assets/{tasks-B4Y5xbF0.js.map → tasks-DggQdNbj.js.map} +1 -1
  229. package/dashboard/dist/assets/{topics-CVnqDw3B.js → topics-DzAKa34X.js} +2 -2
  230. package/dashboard/dist/assets/{topics-CVnqDw3B.js.map → topics-DzAKa34X.js.map} +1 -1
  231. package/dashboard/dist/assets/useEventHooks-5uI9oI5W.js +2 -0
  232. package/dashboard/dist/assets/useEventHooks-5uI9oI5W.js.map +1 -0
  233. package/dashboard/dist/assets/{useNamespace-B1t3hsGf.js → useNamespace-CEKzFIqe.js} +2 -2
  234. package/dashboard/dist/assets/{useNamespace-B1t3hsGf.js.map → useNamespace-CEKzFIqe.js.map} +1 -1
  235. package/dashboard/dist/assets/{useYamlActivityEvents-B8Ru07xQ.js → useYamlActivityEvents-hHgX1Z50.js} +2 -2
  236. package/dashboard/dist/assets/{useYamlActivityEvents-B8Ru07xQ.js.map → useYamlActivityEvents-hHgX1Z50.js.map} +1 -1
  237. package/dashboard/dist/assets/{users-C8H4jjTq.js → users-AO8lUyNr.js} +2 -2
  238. package/dashboard/dist/assets/{users-C8H4jjTq.js.map → users-AO8lUyNr.js.map} +1 -1
  239. package/dashboard/dist/assets/{vendor-icons-BE_WrH2z.js → vendor-icons-DN9yGSuM.js} +134 -129
  240. package/dashboard/dist/assets/vendor-icons-DN9yGSuM.js.map +1 -0
  241. package/dashboard/dist/assets/{workflows-BAdI5k--.js → workflows-PtnlRK09.js} +2 -2
  242. package/dashboard/dist/assets/{workflows-BAdI5k--.js.map → workflows-PtnlRK09.js.map} +1 -1
  243. package/dashboard/dist/assets/{yaml-workflows-Bf2aQS-M.js → yaml-workflows-DPu3Y8vG.js} +2 -2
  244. package/dashboard/dist/assets/{yaml-workflows-Bf2aQS-M.js.map → yaml-workflows-DPu3Y8vG.js.map} +1 -1
  245. package/dashboard/dist/index.html +3 -3
  246. package/docs/api/http/escalations.md +1 -1
  247. package/docs/api/http/personas.md +160 -0
  248. package/docs/api/sdk/escalations.md +2 -2
  249. package/docs/cli.md +12 -0
  250. package/docs/dashboard.md +16 -1
  251. package/docs/design-principles.md +9 -5
  252. package/docs/events.md +4 -2
  253. package/docs/hitl/escalation.md +1 -1
  254. package/docs/hitl/x-lt-list-schema.md +12 -5
  255. package/docs/iam.md +47 -0
  256. package/docs/sdk.md +1 -0
  257. package/package.json +1 -1
  258. package/dashboard/dist/assets/AgentDetailPage-C5AnjpD9.js +0 -4
  259. package/dashboard/dist/assets/AgentDetailPage-C5AnjpD9.js.map +0 -1
  260. package/dashboard/dist/assets/AvailableEscalationsPage-Bk7fFV2e.js.map +0 -1
  261. package/dashboard/dist/assets/CountdownTimer-D-CpxYDO.js +0 -2
  262. package/dashboard/dist/assets/CountdownTimer-D-CpxYDO.js.map +0 -1
  263. package/dashboard/dist/assets/CustomDurationPicker-_KceggR9.js +0 -2
  264. package/dashboard/dist/assets/CustomDurationPicker-_KceggR9.js.map +0 -1
  265. package/dashboard/dist/assets/ElapsedCell-Cflz7Pb5.js +0 -2
  266. package/dashboard/dist/assets/HomePage-NMLaXTvf.js +0 -2
  267. package/dashboard/dist/assets/HomePage-NMLaXTvf.js.map +0 -1
  268. package/dashboard/dist/assets/McpQueryDetailPage-Bqh8GChr.js +0 -5
  269. package/dashboard/dist/assets/McpQueryDetailPage-Bqh8GChr.js.map +0 -1
  270. package/dashboard/dist/assets/McpQueryPage-CqFkBwBR.js +0 -2
  271. package/dashboard/dist/assets/McpQueryPage-CqFkBwBR.js.map +0 -1
  272. package/dashboard/dist/assets/OperatorDashboard-DwbPAPaf.js +0 -2
  273. package/dashboard/dist/assets/OperatorDashboard-DwbPAPaf.js.map +0 -1
  274. package/dashboard/dist/assets/ProcessDetailPage-ChGWcwSp.js +0 -2
  275. package/dashboard/dist/assets/ProcessDetailPage-ChGWcwSp.js.map +0 -1
  276. package/dashboard/dist/assets/RoleDetailPage-GJ_coHsM.js +0 -8
  277. package/dashboard/dist/assets/RoleDetailPage-GJ_coHsM.js.map +0 -1
  278. package/dashboard/dist/assets/RolesPage-CPKw336d.js +0 -2
  279. package/dashboard/dist/assets/RolesPage-CPKw336d.js.map +0 -1
  280. package/dashboard/dist/assets/StatCard-DBANWIss.js +0 -2
  281. package/dashboard/dist/assets/StatCard-DBANWIss.js.map +0 -1
  282. package/dashboard/dist/assets/StreamMessageDetail-BObbEz7P.js.map +0 -1
  283. package/dashboard/dist/assets/TopicsPage-C5THflVQ.js +0 -2
  284. package/dashboard/dist/assets/index-B5JgykZD.js +0 -2
  285. package/dashboard/dist/assets/index-CKxFjMtw.js +0 -63
  286. package/dashboard/dist/assets/index-CKxFjMtw.js.map +0 -1
  287. package/dashboard/dist/assets/index-Pmvw74oY.css +0 -1
  288. package/dashboard/dist/assets/index-_Q1Wf2A8.js +0 -2
  289. package/dashboard/dist/assets/index-_Q1Wf2A8.js.map +0 -1
  290. package/dashboard/dist/assets/index-jkVeGCkD.js +0 -5
  291. package/dashboard/dist/assets/index-jkVeGCkD.js.map +0 -1
  292. package/dashboard/dist/assets/index-oHU-ramT.js +0 -2
  293. package/dashboard/dist/assets/index-oHU-ramT.js.map +0 -1
  294. package/dashboard/dist/assets/useEventHooks-D8bVEx-w.js +0 -2
  295. package/dashboard/dist/assets/useEventHooks-D8bVEx-w.js.map +0 -1
  296. package/dashboard/dist/assets/vendor-icons-BE_WrH2z.js.map +0 -1
@@ -166,7 +166,7 @@ async function bulkAssign(input, auth) {
166
166
  }
167
167
  const result = await escalationService.bulkAssignEscalationsByQuery({ role: query.role, facets: query.facets }, targetUserId, durationMinutes ?? 30);
168
168
  if (result.assigned > 0)
169
- (0, helpers_1.publishBulkClaimEvents)(result.ids, targetUserId);
169
+ (0, helpers_1.publishBulkClaimEvents)(result.ids, targetUserId, query.role);
170
170
  return { status: 200, data: { assigned: result.assigned, skipped: 0 } };
171
171
  }
172
172
  if (!(0, helpers_1.validateIds)(ids)) {
@@ -215,7 +215,13 @@ async function bulkEscalate(input, auth) {
215
215
  const perm = await (0, helpers_1.checkBulkPermission)(auth.userId, ids);
216
216
  if (!perm.allowed)
217
217
  return perm;
218
+ // Departing roles are read before the move so each reassignment can
219
+ // publish under BOTH queues — the one the row leaves and the one it
220
+ // joins (same contract as the single escalate path).
221
+ const beforeRows = await escalationService.getEscalationScopeRows(ids);
218
222
  const updated = await escalationService.bulkEscalateToRole(ids, targetRole);
223
+ if (updated > 0)
224
+ (0, helpers_1.publishBulkReassignEvents)(beforeRows, targetRole);
219
225
  return { status: 200, data: { updated } };
220
226
  }
221
227
  catch (err) {
@@ -141,4 +141,13 @@ export declare function resolveAssignee(assignee: string | undefined, auth: {
141
141
  * Only adds the role if `provisionIfAbsent` declares it and caller has authority.
142
142
  */
143
143
  export declare function ensureRoleMembership(userId: string, requiredRole: string, callerUserId: string, provisionIfAbsent?: ProvisionIfAbsent): Promise<boolean>;
144
- export declare function publishBulkClaimEvents(ids: string[], assignedTo: string): void;
144
+ /**
145
+ * Publish `reassigned` for a bulk role move: every row publishes under its
146
+ * departing role AND the target role, so subscribers scoped to either queue
147
+ * observe the move. Rows already in the target role publish once.
148
+ */
149
+ export declare function publishBulkReassignEvents(beforeRows: Array<{
150
+ id: string;
151
+ role: string;
152
+ }>, targetRole: string): void;
153
+ export declare function publishBulkClaimEvents(ids: string[], assignedTo: string, knownRole?: string): void;
@@ -44,6 +44,7 @@ exports.assertQueueManageAccess = assertQueueManageAccess;
44
44
  exports.checkBulkPermission = checkBulkPermission;
45
45
  exports.resolveAssignee = resolveAssignee;
46
46
  exports.ensureRoleMembership = ensureRoleMembership;
47
+ exports.publishBulkReassignEvents = publishBulkReassignEvents;
47
48
  exports.publishBulkClaimEvents = publishBulkClaimEvents;
48
49
  const escalationService = __importStar(require("../../services/escalation"));
49
50
  const userService = __importStar(require("../../services/user"));
@@ -254,17 +255,56 @@ async function ensureRoleMembership(userId, requiredRole, callerUserId, provisio
254
255
  catch { /* already has it */ }
255
256
  return true;
256
257
  }
257
- function publishBulkClaimEvents(ids, assignedTo) {
258
- for (const id of ids) {
259
- (0, publish_1.publishEscalationEvent)({
260
- type: 'escalation.claimed',
261
- source: 'api',
262
- workflowId: '',
263
- workflowName: '',
264
- taskQueue: '',
265
- escalationId: id,
266
- status: 'claimed',
267
- data: { assigned_to: assignedTo, bulk: true },
268
- });
258
+ /**
259
+ * Publish `reassigned` for a bulk role move: every row publishes under its
260
+ * departing role AND the target role, so subscribers scoped to either queue
261
+ * observe the move. Rows already in the target role publish once.
262
+ */
263
+ function publishBulkReassignEvents(beforeRows, targetRole) {
264
+ for (const row of beforeRows) {
265
+ const roles = row.role && row.role !== targetRole ? [row.role, targetRole] : [targetRole];
266
+ for (const role of roles) {
267
+ (0, publish_1.publishEscalationEvent)({
268
+ type: 'escalation.reassigned',
269
+ source: 'api',
270
+ workflowId: '',
271
+ workflowName: '',
272
+ taskQueue: '',
273
+ escalationId: row.id,
274
+ role,
275
+ status: 'pending',
276
+ data: { from_role: row.role, to_role: targetRole, bulk: true },
277
+ });
278
+ }
269
279
  }
270
280
  }
281
+ function publishBulkClaimEvents(ids, assignedTo, knownRole) {
282
+ // The claimed subject carries each row's role token. Query-form callers
283
+ // already know the single role; id-form callers resolve roles with one
284
+ // indexed read. Fire-and-forget, like every event publish.
285
+ void (async () => {
286
+ const roleById = new Map();
287
+ if (knownRole) {
288
+ for (const id of ids)
289
+ roleById.set(id, knownRole);
290
+ }
291
+ else {
292
+ const rows = await escalationService.getEscalationScopeRows(ids);
293
+ for (const row of rows)
294
+ roleById.set(row.id, row.role);
295
+ }
296
+ for (const id of ids) {
297
+ (0, publish_1.publishEscalationEvent)({
298
+ type: 'escalation.claimed',
299
+ source: 'api',
300
+ workflowId: '',
301
+ workflowName: '',
302
+ taskQueue: '',
303
+ escalationId: id,
304
+ role: roleById.get(id) ?? '',
305
+ status: 'claimed',
306
+ data: { assigned_to: assignedTo, bulk: true },
307
+ });
308
+ }
309
+ })().catch(() => { });
310
+ }
@@ -4,6 +4,7 @@ export * as workflows from './workflows';
4
4
  export * as yamlWorkflows from './yaml-workflows';
5
5
  export * as users from './users';
6
6
  export * as roles from './roles';
7
+ export * as personas from './personas';
7
8
  export * as auth from './auth';
8
9
  export * as mcp from './mcp';
9
10
  export * as pipelines from './pipelines';
@@ -33,13 +33,14 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.maintenance = exports.namespaces = exports.dba = exports.workflowSets = exports.botAccounts = exports.controlplane = exports.exports = exports.settings = exports.insight = exports.mcpRuns = exports.pipelines = exports.mcp = exports.auth = exports.roles = exports.users = exports.yamlWorkflows = exports.workflows = exports.escalations = exports.tasks = void 0;
36
+ exports.maintenance = exports.namespaces = exports.dba = exports.workflowSets = exports.botAccounts = exports.controlplane = exports.exports = exports.settings = exports.insight = exports.mcpRuns = exports.pipelines = exports.mcp = exports.auth = exports.personas = exports.roles = exports.users = exports.yamlWorkflows = exports.workflows = exports.escalations = exports.tasks = void 0;
37
37
  exports.tasks = __importStar(require("./tasks"));
38
38
  exports.escalations = __importStar(require("./escalations"));
39
39
  exports.workflows = __importStar(require("./workflows"));
40
40
  exports.yamlWorkflows = __importStar(require("./yaml-workflows"));
41
41
  exports.users = __importStar(require("./users"));
42
42
  exports.roles = __importStar(require("./roles"));
43
+ exports.personas = __importStar(require("./personas"));
43
44
  exports.auth = __importStar(require("./auth"));
44
45
  exports.mcp = __importStar(require("./mcp"));
45
46
  exports.pipelines = __importStar(require("./pipelines"));
@@ -0,0 +1,121 @@
1
+ import type { LTApiResult } from '../types/sdk';
2
+ /**
3
+ * List all personas with their role links and holder counts.
4
+ *
5
+ * @returns `{ status: 200, data: { personas: LTPersonaRecord[] } }` on success
6
+ */
7
+ export declare function listPersonas(): Promise<LTApiResult>;
8
+ /**
9
+ * Retrieve a single persona by key, including its role links and assignees.
10
+ *
11
+ * @param input.key — the persona's stable key
12
+ * @returns `{ status: 200, data: LTPersonaRecord & { assignees } }` or 404
13
+ */
14
+ export declare function getPersona(input: {
15
+ key: string;
16
+ }): Promise<LTApiResult>;
17
+ /**
18
+ * Create a persona. The key is trimmed, lowercased, and validated against
19
+ * `^[a-z][a-z0-9_-]*$` (the role-name alphabet).
20
+ *
21
+ * @returns `{ status: 201, data: LTPersonaRecord }` on success, 409 on duplicate key
22
+ */
23
+ export declare function createPersona(input: {
24
+ key: string;
25
+ title?: string;
26
+ description?: string;
27
+ }): Promise<LTApiResult>;
28
+ /**
29
+ * Update a persona's title/description with PATCH semantics — omitted fields
30
+ * keep their values; null clears.
31
+ *
32
+ * @returns `{ status: 200, data: LTPersonaRecord }` or 404
33
+ */
34
+ export declare function updatePersona(input: {
35
+ key: string;
36
+ title?: string | null;
37
+ description?: string | null;
38
+ }): Promise<LTApiResult>;
39
+ /**
40
+ * Delete a persona. Memberships it sustains are removed (or re-homed to a
41
+ * sibling persona the user still holds); direct grants are never touched.
42
+ *
43
+ * @returns `{ status: 200, data: { deleted: true, recompute } }` or 404
44
+ */
45
+ export declare function deletePersona(input: {
46
+ key: string;
47
+ }): Promise<LTApiResult>;
48
+ /**
49
+ * Link a role to a persona (or change the link's relationship). Every current
50
+ * holder's memberships are reconciled in the same transaction.
51
+ *
52
+ * @param input.relationship — write-all | write-self | read-all (write-none = read-all)
53
+ * @returns `{ status: 201, data: { role, relationship, recompute } }` or 404
54
+ */
55
+ export declare function linkPersonaRole(input: {
56
+ key: string;
57
+ role: string;
58
+ relationship: string;
59
+ }): Promise<LTApiResult>;
60
+ /**
61
+ * Unlink a role from a persona and reconcile every holder's memberships.
62
+ *
63
+ * @returns `{ status: 200, data: { unlinked: true, recompute } }` or 404
64
+ */
65
+ export declare function unlinkPersonaRole(input: {
66
+ key: string;
67
+ role: string;
68
+ }): Promise<LTApiResult>;
69
+ /**
70
+ * Assign a persona to a user — shorthand for adding the user to each linked
71
+ * role at the linked scope. Idempotent: re-assigning overlays fresh from the
72
+ * persona's current links. Direct grants are only ever raised, never lowered.
73
+ *
74
+ * @param input.id — the user's unique identifier
75
+ * @param input.key — the persona key
76
+ * @returns `{ status: 200, data: { assigned: true, recompute } }` or 404
77
+ */
78
+ export declare function assignPersona(input: {
79
+ id: string;
80
+ key: string;
81
+ }): Promise<LTApiResult>;
82
+ /**
83
+ * Unassign a persona from a user. Removes only the memberships the persona
84
+ * sustains; memberships another held persona still grants are re-homed to it,
85
+ * and direct grants are never touched.
86
+ *
87
+ * @returns `{ status: 200, data: { unassigned: true, recompute } }` or 404
88
+ */
89
+ export declare function unassignPersona(input: {
90
+ id: string;
91
+ key: string;
92
+ }): Promise<LTApiResult>;
93
+ /**
94
+ * The personas a user holds plus the composed role/scope map their memberships
95
+ * form (each row names the sustaining persona, or null for a direct grant).
96
+ *
97
+ * @returns `{ status: 200, data: { personas, roles } }` on success
98
+ */
99
+ export declare function getUserPersonas(input: {
100
+ id: string;
101
+ }): Promise<LTApiResult>;
102
+ /**
103
+ * Declarative, idempotent persona seeding — the static-config twin of the API
104
+ * surface. Each spec is authoritative for its persona: title/description
105
+ * overlaid, role links synced (absent links pruned), linked roles ensured, and
106
+ * every holder reconciled so re-running the seed re-applies edits.
107
+ *
108
+ * @param input.personas — array of { key, title?, description?, roles: [{ role, relationship }] }
109
+ * @returns `{ status: 200, data: { personas, links, recompute } }` on success
110
+ */
111
+ export declare function seedPersonas(input: {
112
+ personas: {
113
+ key: string;
114
+ title?: string;
115
+ description?: string;
116
+ roles?: {
117
+ role: string;
118
+ relationship: string;
119
+ }[];
120
+ }[];
121
+ }): Promise<LTApiResult>;
@@ -0,0 +1,341 @@
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
+ exports.listPersonas = listPersonas;
37
+ exports.getPersona = getPersona;
38
+ exports.createPersona = createPersona;
39
+ exports.updatePersona = updatePersona;
40
+ exports.deletePersona = deletePersona;
41
+ exports.linkPersonaRole = linkPersonaRole;
42
+ exports.unlinkPersonaRole = unlinkPersonaRole;
43
+ exports.assignPersona = assignPersona;
44
+ exports.unassignPersona = unassignPersona;
45
+ exports.getUserPersonas = getUserPersonas;
46
+ exports.seedPersonas = seedPersonas;
47
+ const personaService = __importStar(require("../services/persona"));
48
+ /** Persona keys share the role-name alphabet: kebab/underscore, letter-first. */
49
+ const PERSONA_KEY = /^[a-z][a-z0-9_-]*$/;
50
+ function validateKey(key) {
51
+ if (typeof key !== 'string' || !key.trim())
52
+ return null;
53
+ const trimmed = key.trim().toLowerCase();
54
+ return PERSONA_KEY.test(trimmed) ? trimmed : null;
55
+ }
56
+ const RELATIONSHIP_ERROR = 'relationship must be write-all, write-self, or read-all (write-none is accepted as read-all)';
57
+ /**
58
+ * List all personas with their role links and holder counts.
59
+ *
60
+ * @returns `{ status: 200, data: { personas: LTPersonaRecord[] } }` on success
61
+ */
62
+ async function listPersonas() {
63
+ try {
64
+ const personas = await personaService.listPersonas();
65
+ return { status: 200, data: { personas } };
66
+ }
67
+ catch (err) {
68
+ return { status: 500, error: err.message };
69
+ }
70
+ }
71
+ /**
72
+ * Retrieve a single persona by key, including its role links and assignees.
73
+ *
74
+ * @param input.key — the persona's stable key
75
+ * @returns `{ status: 200, data: LTPersonaRecord & { assignees } }` or 404
76
+ */
77
+ async function getPersona(input) {
78
+ try {
79
+ if (!input.key) {
80
+ return { status: 400, error: 'key is required' };
81
+ }
82
+ const persona = await personaService.getPersona(input.key);
83
+ if (!persona) {
84
+ return { status: 404, error: `Persona '${input.key}' not found` };
85
+ }
86
+ return { status: 200, data: persona };
87
+ }
88
+ catch (err) {
89
+ return { status: 500, error: err.message };
90
+ }
91
+ }
92
+ /**
93
+ * Create a persona. The key is trimmed, lowercased, and validated against
94
+ * `^[a-z][a-z0-9_-]*$` (the role-name alphabet).
95
+ *
96
+ * @returns `{ status: 201, data: LTPersonaRecord }` on success, 409 on duplicate key
97
+ */
98
+ async function createPersona(input) {
99
+ try {
100
+ const key = validateKey(input.key);
101
+ if (!key) {
102
+ return {
103
+ status: 400,
104
+ error: 'key must start with a letter and contain only lowercase letters, numbers, hyphens, and underscores',
105
+ };
106
+ }
107
+ const persona = await personaService.createPersona({
108
+ key,
109
+ title: input.title,
110
+ description: input.description,
111
+ });
112
+ return { status: 201, data: persona };
113
+ }
114
+ catch (err) {
115
+ if (err.code === '23505') {
116
+ return { status: 409, error: 'Persona with this key already exists' };
117
+ }
118
+ return { status: 500, error: err.message };
119
+ }
120
+ }
121
+ /**
122
+ * Update a persona's title/description with PATCH semantics — omitted fields
123
+ * keep their values; null clears.
124
+ *
125
+ * @returns `{ status: 200, data: LTPersonaRecord }` or 404
126
+ */
127
+ async function updatePersona(input) {
128
+ try {
129
+ if (!input.key) {
130
+ return { status: 400, error: 'key is required' };
131
+ }
132
+ const persona = await personaService.updatePersona(input.key, {
133
+ title: input.title,
134
+ description: input.description,
135
+ });
136
+ if (!persona) {
137
+ return { status: 404, error: `Persona '${input.key}' not found` };
138
+ }
139
+ return { status: 200, data: persona };
140
+ }
141
+ catch (err) {
142
+ return { status: 500, error: err.message };
143
+ }
144
+ }
145
+ /**
146
+ * Delete a persona. Memberships it sustains are removed (or re-homed to a
147
+ * sibling persona the user still holds); direct grants are never touched.
148
+ *
149
+ * @returns `{ status: 200, data: { deleted: true, recompute } }` or 404
150
+ */
151
+ async function deletePersona(input) {
152
+ try {
153
+ if (!input.key) {
154
+ return { status: 400, error: 'key is required' };
155
+ }
156
+ const result = await personaService.deletePersona(input.key);
157
+ if (!result.deleted) {
158
+ return { status: 404, error: `Persona '${input.key}' not found` };
159
+ }
160
+ return { status: 200, data: { deleted: true, recompute: result.recompute } };
161
+ }
162
+ catch (err) {
163
+ return { status: 500, error: err.message };
164
+ }
165
+ }
166
+ /**
167
+ * Link a role to a persona (or change the link's relationship). Every current
168
+ * holder's memberships are reconciled in the same transaction.
169
+ *
170
+ * @param input.relationship — write-all | write-self | read-all (write-none = read-all)
171
+ * @returns `{ status: 201, data: { role, relationship, recompute } }` or 404
172
+ */
173
+ async function linkPersonaRole(input) {
174
+ try {
175
+ if (!input.key || !input.role) {
176
+ return { status: 400, error: 'key and role are required' };
177
+ }
178
+ const role = input.role.trim().toLowerCase();
179
+ if (!PERSONA_KEY.test(role)) {
180
+ return {
181
+ status: 400,
182
+ error: 'Role must start with a letter and contain only lowercase letters, numbers, hyphens, and underscores',
183
+ };
184
+ }
185
+ const relationship = personaService.normalizeRelationship(input.relationship ?? '');
186
+ if (!relationship) {
187
+ return { status: 400, error: RELATIONSHIP_ERROR };
188
+ }
189
+ const result = await personaService.linkPersonaRole(input.key, role, relationship);
190
+ if (!result) {
191
+ return { status: 404, error: `Persona '${input.key}' not found` };
192
+ }
193
+ return { status: 201, data: result };
194
+ }
195
+ catch (err) {
196
+ return { status: 500, error: err.message };
197
+ }
198
+ }
199
+ /**
200
+ * Unlink a role from a persona and reconcile every holder's memberships.
201
+ *
202
+ * @returns `{ status: 200, data: { unlinked: true, recompute } }` or 404
203
+ */
204
+ async function unlinkPersonaRole(input) {
205
+ try {
206
+ if (!input.key || !input.role) {
207
+ return { status: 400, error: 'key and role are required' };
208
+ }
209
+ const result = await personaService.unlinkPersonaRole(input.key, input.role);
210
+ if (!result.personaFound) {
211
+ return { status: 404, error: `Persona '${input.key}' not found` };
212
+ }
213
+ if (!result.unlinked) {
214
+ return { status: 404, error: `Persona '${input.key}' does not link role '${input.role}'` };
215
+ }
216
+ return { status: 200, data: { unlinked: true, recompute: result.recompute } };
217
+ }
218
+ catch (err) {
219
+ return { status: 500, error: err.message };
220
+ }
221
+ }
222
+ /**
223
+ * Assign a persona to a user — shorthand for adding the user to each linked
224
+ * role at the linked scope. Idempotent: re-assigning overlays fresh from the
225
+ * persona's current links. Direct grants are only ever raised, never lowered.
226
+ *
227
+ * @param input.id — the user's unique identifier
228
+ * @param input.key — the persona key
229
+ * @returns `{ status: 200, data: { assigned: true, recompute } }` or 404
230
+ */
231
+ async function assignPersona(input) {
232
+ try {
233
+ if (!input.id || !input.key) {
234
+ return { status: 400, error: 'user id and persona key are required' };
235
+ }
236
+ const recompute = await personaService.assignPersona(input.id, input.key);
237
+ if (!recompute) {
238
+ return { status: 404, error: `Persona '${input.key}' not found` };
239
+ }
240
+ return { status: 200, data: { assigned: true, recompute } };
241
+ }
242
+ catch (err) {
243
+ if (err.code === '23503') {
244
+ return { status: 404, error: 'User not found' };
245
+ }
246
+ if (err.code === '22P02') {
247
+ return { status: 400, error: 'user id must be a UUID' };
248
+ }
249
+ return { status: 500, error: err.message };
250
+ }
251
+ }
252
+ /**
253
+ * Unassign a persona from a user. Removes only the memberships the persona
254
+ * sustains; memberships another held persona still grants are re-homed to it,
255
+ * and direct grants are never touched.
256
+ *
257
+ * @returns `{ status: 200, data: { unassigned: true, recompute } }` or 404
258
+ */
259
+ async function unassignPersona(input) {
260
+ try {
261
+ if (!input.id || !input.key) {
262
+ return { status: 400, error: 'user id and persona key are required' };
263
+ }
264
+ const result = await personaService.unassignPersona(input.id, input.key);
265
+ if (!result.personaFound) {
266
+ return { status: 404, error: `Persona '${input.key}' not found` };
267
+ }
268
+ if (!result.unassigned) {
269
+ return { status: 404, error: `User does not hold persona '${input.key}'` };
270
+ }
271
+ return { status: 200, data: { unassigned: true, recompute: result.recompute } };
272
+ }
273
+ catch (err) {
274
+ if (err.code === '22P02') {
275
+ return { status: 400, error: 'user id must be a UUID' };
276
+ }
277
+ return { status: 500, error: err.message };
278
+ }
279
+ }
280
+ /**
281
+ * The personas a user holds plus the composed role/scope map their memberships
282
+ * form (each row names the sustaining persona, or null for a direct grant).
283
+ *
284
+ * @returns `{ status: 200, data: { personas, roles } }` on success
285
+ */
286
+ async function getUserPersonas(input) {
287
+ try {
288
+ if (!input.id) {
289
+ return { status: 400, error: 'user id is required' };
290
+ }
291
+ const result = await personaService.getUserPersonas(input.id);
292
+ return { status: 200, data: result };
293
+ }
294
+ catch (err) {
295
+ if (err.code === '22P02') {
296
+ return { status: 400, error: 'user id must be a UUID' };
297
+ }
298
+ return { status: 500, error: err.message };
299
+ }
300
+ }
301
+ /**
302
+ * Declarative, idempotent persona seeding — the static-config twin of the API
303
+ * surface. Each spec is authoritative for its persona: title/description
304
+ * overlaid, role links synced (absent links pruned), linked roles ensured, and
305
+ * every holder reconciled so re-running the seed re-applies edits.
306
+ *
307
+ * @param input.personas — array of { key, title?, description?, roles: [{ role, relationship }] }
308
+ * @returns `{ status: 200, data: { personas, links, recompute } }` on success
309
+ */
310
+ async function seedPersonas(input) {
311
+ try {
312
+ if (!Array.isArray(input.personas)) {
313
+ return { status: 400, error: 'personas must be an array of persona specs' };
314
+ }
315
+ const specs = [];
316
+ for (const spec of input.personas) {
317
+ const key = validateKey(spec?.key);
318
+ if (!key) {
319
+ return { status: 400, error: `Invalid persona key: '${spec?.key}'` };
320
+ }
321
+ const roles = [];
322
+ for (const link of spec.roles ?? []) {
323
+ const role = validateKey(link?.role);
324
+ if (!role) {
325
+ return { status: 400, error: `Persona '${key}': invalid role '${link?.role}'` };
326
+ }
327
+ const relationship = personaService.normalizeRelationship(link?.relationship ?? '');
328
+ if (!relationship) {
329
+ return { status: 400, error: `Persona '${key}', role '${role}': ${RELATIONSHIP_ERROR}` };
330
+ }
331
+ roles.push({ role, relationship });
332
+ }
333
+ specs.push({ key, title: spec.title, description: spec.description, roles });
334
+ }
335
+ const result = await personaService.seedPersonas(specs);
336
+ return { status: 200, data: result };
337
+ }
338
+ catch (err) {
339
+ return { status: 500, error: err.message };
340
+ }
341
+ }
package/build/bin/ltc.js CHANGED
@@ -55,6 +55,7 @@ const kb = __importStar(require("../lib/cli/commands/knowledge"));
55
55
  const mcp = __importStar(require("../lib/cli/commands/mcp"));
56
56
  const usr = __importStar(require("../lib/cli/commands/users"));
57
57
  const roles = __importStar(require("../lib/cli/commands/roles"));
58
+ const personas = __importStar(require("../lib/cli/commands/personas"));
58
59
  const streams = __importStar(require("../lib/cli/commands/streams"));
59
60
  const pkg = require('../package.json');
60
61
  const envPath = path.resolve(process.cwd(), '.env');
@@ -309,6 +310,28 @@ rolesCmd.command('save-schema <role>')
309
310
  .option('--summary <text>', 'Change summary recorded on the new version')
310
311
  .option('--json', 'JSON output')
311
312
  .action(wrap(roles.saveRoleSchema));
313
+ // ── Personas ─────────────────────────────────────────────────────────────
314
+ const personasCmd = commander_1.program.command('personas').description('Personas — named role bundles with per-role relationship scope');
315
+ personasCmd.command('list')
316
+ .option('--json', 'JSON output')
317
+ .option('-q, --quiet', 'Keys only')
318
+ .action(wrap(personas.listPersonas));
319
+ personasCmd.command('get <key>')
320
+ .description('Show a persona with its role links and assignees')
321
+ .option('--json', 'JSON output')
322
+ .action(wrap(personas.getPersona));
323
+ personasCmd.command('assign <key> <userId>')
324
+ .description('Assign a persona to a user (idempotent; re-assigning overlays fresh)')
325
+ .option('--json', 'JSON output')
326
+ .action(wrap(personas.assignPersona));
327
+ personasCmd.command('unassign <key> <userId>')
328
+ .description('Unassign a persona — removes only the memberships it sustains')
329
+ .option('--json', 'JSON output')
330
+ .action(wrap(personas.unassignPersona));
331
+ personasCmd.command('for-user <userId>')
332
+ .description('Show the personas a user holds and the composed role/scope map')
333
+ .option('--json', 'JSON output')
334
+ .action(wrap(personas.getUserPersonas));
312
335
  // ── Streams ─────────────────────────────────────────────────────────────
313
336
  const streamsCmd = commander_1.program.command('streams').description('Browse stream messages (admin)');
314
337
  streamsCmd.command('list')
@@ -0,0 +1,16 @@
1
+ export declare function listPersonas(opts: {
2
+ json?: boolean;
3
+ quiet?: boolean;
4
+ }): Promise<void>;
5
+ export declare function getPersona(key: string, opts: {
6
+ json?: boolean;
7
+ }): Promise<void>;
8
+ export declare function assignPersona(key: string, userId: string, opts: {
9
+ json?: boolean;
10
+ }): Promise<void>;
11
+ export declare function unassignPersona(key: string, userId: string, opts: {
12
+ json?: boolean;
13
+ }): Promise<void>;
14
+ export declare function getUserPersonas(userId: string, opts: {
15
+ json?: boolean;
16
+ }): Promise<void>;