@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.
- package/build/api/escalations/list.js +11 -4
- package/build/api/index.d.ts +1 -0
- package/build/api/index.js +2 -1
- package/build/api/personas.d.ts +121 -0
- package/build/api/personas.js +341 -0
- package/build/api/roles.d.ts +2 -0
- package/build/api/roles.js +4 -0
- package/build/bin/ltc.js +23 -0
- package/build/lib/cli/commands/personas.d.ts +16 -0
- package/build/lib/cli/commands/personas.js +42 -0
- package/build/lib/db/schemas/024_personas.sql +53 -0
- package/build/lib/db/schemas/025_ops_home_default.sql +11 -0
- package/build/modules/features.d.ts +9 -0
- package/build/modules/features.js +1 -0
- package/build/routes/index.js +2 -0
- package/build/routes/personas.d.ts +2 -0
- package/build/routes/personas.js +118 -0
- package/build/routes/users.js +35 -0
- package/build/sdk/index.d.ts +15 -0
- package/build/sdk/index.js +16 -0
- package/build/services/persona/index.d.ts +92 -0
- package/build/services/persona/index.js +217 -0
- package/build/services/persona/sql.d.ts +24 -0
- package/build/services/persona/sql.js +158 -0
- package/build/services/persona/types.d.ts +23 -0
- package/build/services/persona/types.js +33 -0
- package/build/services/role/index.js +1 -0
- package/build/services/role/sql.d.ts +6 -2
- package/build/services/role/sql.js +11 -2
- package/build/services/role/types.d.ts +9 -0
- package/build/services/user/sql.d.ts +8 -5
- package/build/services/user/sql.js +15 -5
- package/build/system/mcp-servers/admin/index.d.ts +1 -0
- package/build/system/mcp-servers/admin/index.js +5 -2
- package/build/system/mcp-servers/admin/personas.d.ts +8 -0
- package/build/system/mcp-servers/admin/personas.js +186 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +94 -0
- package/build/system/mcp-servers/admin/schemas.js +43 -2
- package/build/system/mcp-servers/admin/users.js +1 -0
- package/build/system/seed/tool-manifests-admin.d.ts +1057 -0
- package/build/system/seed/tool-manifests-admin.js +12 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/index.d.ts +1 -0
- package/build/types/persona.d.ts +56 -0
- package/build/types/persona.js +2 -0
- package/build/types/startup.d.ts +8 -0
- package/build/types/user.d.ts +2 -0
- package/dashboard/dist/assets/{AdminDashboard-DWSYdAeW.js → AdminDashboard-CkTbRfY9.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-DWSYdAeW.js.map → AdminDashboard-CkTbRfY9.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-Dm6gL76m.js → AgentConfigPage-Cm8FRpXi.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-Dm6gL76m.js.map → AgentConfigPage-Cm8FRpXi.js.map} +1 -1
- package/dashboard/dist/assets/AgentDetailPage-sQ1-UT46.js +4 -0
- package/dashboard/dist/assets/AgentDetailPage-sQ1-UT46.js.map +1 -0
- package/dashboard/dist/assets/{AgentsPage-DKaDRk-Y.js → AgentsPage-bSx_mdae.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-DKaDRk-Y.js.map → AgentsPage-bSx_mdae.js.map} +1 -1
- package/dashboard/dist/assets/AutoGrowTextarea-y-oNY51_.js +2 -0
- package/dashboard/dist/assets/AutoGrowTextarea-y-oNY51_.js.map +1 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-8pMC7Xv6.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-8pMC7Xv6.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-DRv_orzI.js → BotPicker-C4uGH2x9.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-DRv_orzI.js.map → BotPicker-C4uGH2x9.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-DqRVzMol.js → CapabilitiesPage-BRoOeKcc.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-DqRVzMol.js.map → CapabilitiesPage-BRoOeKcc.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-D0psvw8i.js → CollapsibleSection-CIj8Adc9.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-D0psvw8i.js.map → CollapsibleSection-CIj8Adc9.js.map} +1 -1
- package/dashboard/dist/assets/{ConfirmDeleteModal-DD7SEMeX.js → ConfirmDeleteModal-Ugi_9Ukq.js} +2 -2
- package/dashboard/dist/assets/{ConfirmDeleteModal-DD7SEMeX.js.map → ConfirmDeleteModal-Ugi_9Ukq.js.map} +1 -1
- package/dashboard/dist/assets/{CopyableId-BpB-NvDh.js → CopyableId-GUEdiiL5.js} +2 -2
- package/dashboard/dist/assets/{CopyableId-BpB-NvDh.js.map → CopyableId-GUEdiiL5.js.map} +1 -1
- package/dashboard/dist/assets/CountdownTimer-D3t0XMhE.js +2 -0
- package/dashboard/dist/assets/CountdownTimer-D3t0XMhE.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-Axdci1z-.js → CredentialsPage-B73Si6K1.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-Axdci1z-.js.map → CredentialsPage-B73Si6K1.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-BjZ_wZA8.js → CronLabel-Ca1aHolt.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-BjZ_wZA8.js.map → CronLabel-Ca1aHolt.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-CkUCMUfk.js → CustomDurationPicker-_9E9x1YN.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-CkUCMUfk.js.map → CustomDurationPicker-_9E9x1YN.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-_tmC3jEe.js → DropZone-NYn6TWHz.js} +2 -2
- package/dashboard/dist/assets/{DropZone-_tmC3jEe.js.map → DropZone-NYn6TWHz.js.map} +1 -1
- package/dashboard/dist/assets/ElapsedCell-karxia93.js +2 -0
- package/dashboard/dist/assets/{ElapsedCell-BE_CN5sP.js.map → ElapsedCell-karxia93.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListSchemaPage-CgcVIiYR.js → EscalationListSchemaPage-CDaNmbUA.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage-CgcVIiYR.js.map → EscalationListSchemaPage-CDaNmbUA.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-BaAn1Hcr.js → EscalationSchemaPage-k-2hFT2t.js} +3 -3
- package/dashboard/dist/assets/{EscalationSchemaPage-BaAn1Hcr.js.map → EscalationSchemaPage-k-2hFT2t.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-Dbq7tKzr.js → EscalationsOverview-C1e7IPn_.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-Dbq7tKzr.js.map → EscalationsOverview-C1e7IPn_.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-0RUK9jqt.js → EventTable-CIrrtL7_.js} +2 -2
- package/dashboard/dist/assets/{EventTable-0RUK9jqt.js.map → EventTable-CIrrtL7_.js.map} +1 -1
- package/dashboard/dist/assets/{FilterBar-B_dy2Jhj.js → FilterBar-CzjtH74v.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-B_dy2Jhj.js.map → FilterBar-CzjtH74v.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-CvLYp1nL.js → GraphInvokePage-LMpWw8z1.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-CvLYp1nL.js.map → GraphInvokePage-LMpWw8z1.js.map} +1 -1
- package/dashboard/dist/assets/HomePage-Kl2DBG6l.js +2 -0
- package/dashboard/dist/assets/HomePage-Kl2DBG6l.js.map +1 -0
- package/dashboard/dist/assets/ListToolbar-B2fub0DY.js +2 -0
- package/dashboard/dist/assets/ListToolbar-B2fub0DY.js.map +1 -0
- package/dashboard/dist/assets/{McpOverview-Ca8cpc8g.js → McpOverview-odYnCZCC.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-Ca8cpc8g.js.map → McpOverview-odYnCZCC.js.map} +1 -1
- package/dashboard/dist/assets/McpQueryDetailPage-BtqwHUAg.js +5 -0
- package/dashboard/dist/assets/McpQueryDetailPage-BtqwHUAg.js.map +1 -0
- package/dashboard/dist/assets/McpQueryPage-CPkVwNIK.js +2 -0
- package/dashboard/dist/assets/McpQueryPage-CPkVwNIK.js.map +1 -0
- package/dashboard/dist/assets/{McpRunDetailPage-C76mfUtG.js → McpRunDetailPage-D-Rz9MRw.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-C76mfUtG.js.map → McpRunDetailPage-D-Rz9MRw.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-B43BqEyw.js → McpRunsPage-rdpZ2yMh.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-B43BqEyw.js.map → McpRunsPage-rdpZ2yMh.js.map} +1 -1
- package/dashboard/dist/assets/{Modal-CA2Iv02H.js → Modal-Dr1Xy0j0.js} +2 -2
- package/dashboard/dist/assets/{Modal-CA2Iv02H.js.map → Modal-Dr1Xy0j0.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-B5gHotsd.js → NamespacePill-GwO-WfhX.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-B5gHotsd.js.map → NamespacePill-GwO-WfhX.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-DlyGLPAs.js +2 -0
- package/dashboard/dist/assets/OperationsPage-DlyGLPAs.js.map +1 -0
- package/dashboard/dist/assets/OperatorDashboard-Csei5vj-.js +2 -0
- package/dashboard/dist/assets/OperatorDashboard-Csei5vj-.js.map +1 -0
- package/dashboard/dist/assets/{PageHeader-DrbNMGxm.js → PageHeader-Bo9BU9pK.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-DrbNMGxm.js.map → PageHeader-Bo9BU9pK.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-SbQkaQLG.js → PageHeaderWithStats-8_5cOSKL.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-SbQkaQLG.js.map → PageHeaderWithStats-8_5cOSKL.js.map} +1 -1
- package/dashboard/dist/assets/PersonaDetailPage--ZL6Or6v.js +2 -0
- package/dashboard/dist/assets/PersonaDetailPage--ZL6Or6v.js.map +1 -0
- package/dashboard/dist/assets/PersonasPage-Bdcoay3P.js +2 -0
- package/dashboard/dist/assets/PersonasPage-Bdcoay3P.js.map +1 -0
- package/dashboard/dist/assets/ProcessDetailPage-HrmVQq7S.js +2 -0
- package/dashboard/dist/assets/ProcessDetailPage-HrmVQq7S.js.map +1 -0
- package/dashboard/dist/assets/{ProcessesListPage-Di0n8TR4.js → ProcessesListPage-_xmj6Bo-.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-Di0n8TR4.js.map → ProcessesListPage-_xmj6Bo-.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-C0ju2-4K.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-C0ju2-4K.js.map +1 -0
- package/dashboard/dist/assets/{RolePill-Cz-AAWqA.js → RolePill-CQlnEWkK.js} +2 -2
- package/dashboard/dist/assets/{RolePill-Cz-AAWqA.js.map → RolePill-CQlnEWkK.js.map} +1 -1
- package/dashboard/dist/assets/RolesPage-CKluVSpB.js +2 -0
- package/dashboard/dist/assets/RolesPage-CKluVSpB.js.map +1 -0
- package/dashboard/dist/assets/{RunAsSelector-DFv0VNVZ.js → RunAsSelector-DdfecQ1U.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-DFv0VNVZ.js.map → RunAsSelector-DdfecQ1U.js.map} +1 -1
- package/dashboard/dist/assets/ScopeBadge-BBYkevuR.js +2 -0
- package/dashboard/dist/assets/ScopeBadge-BBYkevuR.js.map +1 -0
- package/dashboard/dist/assets/StatCard-Ca-ngCaD.js +2 -0
- package/dashboard/dist/assets/StatCard-Ca-ngCaD.js.map +1 -0
- package/dashboard/dist/assets/{StatusBadge-BbqghFMi.js → StatusBadge-BwUvykDo.js} +2 -2
- package/dashboard/dist/assets/{StatusBadge-BbqghFMi.js.map → StatusBadge-BwUvykDo.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-D5zxe_l9.js → StickyPagination-cLddIdtT.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-D5zxe_l9.js.map → StickyPagination-cLddIdtT.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-BmBfNSEr.js → StreamMessageDetail-CZXWDawC.js} +2 -2
- package/dashboard/dist/assets/StreamMessageDetail-CZXWDawC.js.map +1 -0
- package/dashboard/dist/assets/{SwimlaneTimeline-CA0TKDIi.js → SwimlaneTimeline-fWY0gaf3.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-CA0TKDIi.js.map → SwimlaneTimeline-fWY0gaf3.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-Bf9dZSG6.js → TagInput-CDKbslLx.js} +2 -2
- package/dashboard/dist/assets/{TagInput-Bf9dZSG6.js.map → TagInput-CDKbslLx.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-C15nADUW.js → TaskDetailPage-BjE4G2yn.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-C15nADUW.js.map → TaskDetailPage-BjE4G2yn.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-DfiBYQy2.js → TaskQueuePill-G3RuYRNZ.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-DfiBYQy2.js.map → TaskQueuePill-G3RuYRNZ.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-DQdAayqc.js → TasksListPage-KAhmCWB2.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-DQdAayqc.js.map → TasksListPage-KAhmCWB2.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-CvslqTB7.js → TimeAgo-C_H2TyZw.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-CvslqTB7.js.map → TimeAgo-C_H2TyZw.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-BZCQQONl.js → TimestampCell-COG_4Flg.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-BZCQQONl.js.map → TimestampCell-COG_4Flg.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-gA4sXukc.js → ToolPill-ChSyRJZC.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-gA4sXukc.js.map → ToolPill-ChSyRJZC.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-BgLVc6R5.js → ToolTestPanel-BPdJECW0.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-BgLVc6R5.js.map → ToolTestPanel-BPdJECW0.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-9A0aPb4t.js → TopicDetailPage-DAqx5JqA.js} +3 -3
- package/dashboard/dist/assets/{TopicDetailPage-9A0aPb4t.js.map → TopicDetailPage-DAqx5JqA.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-o0EZxKbR.js → TopicsPage-DoGqAdPm.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-o0EZxKbR.js.map → TopicsPage-DoGqAdPm.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-DKMLxBJ5.js → UserName-CKO_rk1R.js} +2 -2
- package/dashboard/dist/assets/{UserName-DKMLxBJ5.js.map → UserName-CKO_rk1R.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-C-snB_dD.js → WorkflowExecutionPage-D3VQ9iQV.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-C-snB_dD.js.map → WorkflowExecutionPage-D3VQ9iQV.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-Djt2AtZk.js → WorkflowPill-CNbwug5p.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-Djt2AtZk.js.map → WorkflowPill-CNbwug5p.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-BRQmgsLR.js → WorkflowsDashboard-DdJ86We6.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-BRQmgsLR.js.map → WorkflowsDashboard-DdJ86We6.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-DmMdIRHQ.js → WorkflowsOverview-ZfFBzePp.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-DmMdIRHQ.js.map → WorkflowsOverview-ZfFBzePp.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-Bj7QkCry.js → YamlWorkflowDetailPage-4bd5yXdA.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-Bj7QkCry.js.map → YamlWorkflowDetailPage-4bd5yXdA.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-BIi77mE0.js → YamlWorkflowsPage-iHPNXjQj.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-BIi77mE0.js.map → YamlWorkflowsPage-iHPNXjQj.js.map} +1 -1
- package/dashboard/dist/assets/{agents-EBIsjLCr.js → agents-DjD5U3Wb.js} +2 -2
- package/dashboard/dist/assets/{agents-EBIsjLCr.js.map → agents-DjD5U3Wb.js.map} +1 -1
- package/dashboard/dist/assets/{bots-BXzxybS6.js → bots-DiljVapH.js} +2 -2
- package/dashboard/dist/assets/{bots-BXzxybS6.js.map → bots-DiljVapH.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-CnprG6q_.js → capabilities-DV_5ghHT.js} +2 -2
- package/dashboard/dist/assets/{capabilities-CnprG6q_.js.map → capabilities-DV_5ghHT.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-DiC0xtNq.js → controlplane-BKMvfWCB.js} +2 -2
- package/dashboard/dist/assets/{controlplane-DiC0xtNq.js.map → controlplane-BKMvfWCB.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-BQpCaJId.js +2 -0
- package/dashboard/dist/assets/escalation-columns-BQpCaJId.js.map +1 -0
- package/dashboard/dist/assets/{index-BzRXMicE.js → index-B-WkiNRN.js} +2 -2
- package/dashboard/dist/assets/{index-BzRXMicE.js.map → index-B-WkiNRN.js.map} +1 -1
- package/dashboard/dist/assets/index-BO-7S9Xw.js +2 -0
- package/dashboard/dist/assets/index-BO-7S9Xw.js.map +1 -0
- package/dashboard/dist/assets/{index-C7KY8GTF.js → index-BaV3l0UE.js} +2 -2
- package/dashboard/dist/assets/{index-C7KY8GTF.js.map → index-BaV3l0UE.js.map} +1 -1
- package/dashboard/dist/assets/{index-B6ldRx_s.js → index-BsOxMMmb.js} +2 -2
- package/dashboard/dist/assets/{index-B6ldRx_s.js.map → index-BsOxMMmb.js.map} +1 -1
- package/dashboard/dist/assets/index-CI8rrKjx.js +2 -0
- package/dashboard/dist/assets/{index-CjBdB8Ht.js.map → index-CI8rrKjx.js.map} +1 -1
- package/dashboard/dist/assets/index-CItsikDw.js +2 -0
- package/dashboard/dist/assets/index-CItsikDw.js.map +1 -0
- package/dashboard/dist/assets/{index-CcMIca5t.js → index-CStR4uIU.js} +2 -2
- package/dashboard/dist/assets/{index-CcMIca5t.js.map → index-CStR4uIU.js.map} +1 -1
- package/dashboard/dist/assets/{index-CSTwyrwu.js → index-DbTD-dio.js} +26 -26
- package/dashboard/dist/assets/index-DbTD-dio.js.map +1 -0
- package/dashboard/dist/assets/{index-gBnGEubX.js → index-DhATO1cS.js} +2 -2
- package/dashboard/dist/assets/{index-gBnGEubX.js.map → index-DhATO1cS.js.map} +1 -1
- package/dashboard/dist/assets/index-DnlsDaAw.css +1 -0
- package/dashboard/dist/assets/{index-ovFEZ9SV.js → index-Tx_ETzcH.js} +2 -2
- package/dashboard/dist/assets/{index-ovFEZ9SV.js.map → index-Tx_ETzcH.js.map} +1 -1
- package/dashboard/dist/assets/{index-DVmC_UfQ.js → index-kzIIeVbx.js} +4 -4
- package/dashboard/dist/assets/{index-DVmC_UfQ.js.map → index-kzIIeVbx.js.map} +1 -1
- package/dashboard/dist/assets/{index-BHAjQoOy.js → index-nIrYejxe.js} +3 -3
- package/dashboard/dist/assets/{index-BHAjQoOy.js.map → index-nIrYejxe.js.map} +1 -1
- package/dashboard/dist/assets/{index-Dht0ihGT.js → index-xZqQxqu2.js} +2 -2
- package/dashboard/dist/assets/{index-Dht0ihGT.js.map → index-xZqQxqu2.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-W5QSKkYQ.js → knowledge-BocTnSiA.js} +2 -2
- package/dashboard/dist/assets/{knowledge-W5QSKkYQ.js.map → knowledge-BocTnSiA.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-n877HyOj.js → mcp-C1KBjKQ_.js} +2 -2
- package/dashboard/dist/assets/{mcp-n877HyOj.js.map → mcp-C1KBjKQ_.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-C9kV4sUm.js → mcp-query-B3cjukkA.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-C9kV4sUm.js.map → mcp-query-B3cjukkA.js.map} +1 -1
- package/dashboard/dist/assets/personas-DiA2bJaX.js +2 -0
- package/dashboard/dist/assets/personas-DiA2bJaX.js.map +1 -0
- package/dashboard/dist/assets/{pipelines-CBR68T8R.js → pipelines-Dt2d2EFR.js} +2 -2
- package/dashboard/dist/assets/{pipelines-CBR68T8R.js.map → pipelines-Dt2d2EFR.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-DHpWqKMk.js → tasks-cDh2hNml.js} +2 -2
- package/dashboard/dist/assets/{tasks-DHpWqKMk.js.map → tasks-cDh2hNml.js.map} +1 -1
- package/dashboard/dist/assets/{topics-CaLtyqSh.js → topics-yFlGF-u1.js} +2 -2
- package/dashboard/dist/assets/{topics-CaLtyqSh.js.map → topics-yFlGF-u1.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-B8p56JlB.js → useEventHooks-D2R_ckS8.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-B8p56JlB.js.map → useEventHooks-D2R_ckS8.js.map} +1 -1
- package/dashboard/dist/assets/{useFilterParams-CvRyYHJN.js → useFilterParams-B7suPsIK.js} +2 -2
- package/dashboard/dist/assets/{useFilterParams-CvRyYHJN.js.map → useFilterParams-B7suPsIK.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-ej71FCvu.js → useNamespace-u-V1GVUD.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-ej71FCvu.js.map → useNamespace-u-V1GVUD.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-DgpiFXib.js → useYamlActivityEvents-BhOZeqgE.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-DgpiFXib.js.map → useYamlActivityEvents-BhOZeqgE.js.map} +1 -1
- package/dashboard/dist/assets/{users-CHnjI_Ca.js → users-BoXPwdee.js} +2 -2
- package/dashboard/dist/assets/{users-CHnjI_Ca.js.map → users-BoXPwdee.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-Bg6eJX4Z.js → vendor-icons-D71XjkNy.js} +133 -128
- package/dashboard/dist/assets/vendor-icons-D71XjkNy.js.map +1 -0
- package/dashboard/dist/assets/{vendor-react-BpdAFEQc.js → vendor-react-C1YOyCSV.js} +2 -2
- package/dashboard/dist/assets/{vendor-react-BpdAFEQc.js.map → vendor-react-C1YOyCSV.js.map} +1 -1
- package/dashboard/dist/assets/{workflows-DtnXUL0U.js → workflows-mCoyKuLZ.js} +2 -2
- package/dashboard/dist/assets/{workflows-DtnXUL0U.js.map → workflows-mCoyKuLZ.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-DDclYAID.js → yaml-workflows-CWxXGDpn.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-DDclYAID.js.map → yaml-workflows-CWxXGDpn.js.map} +1 -1
- package/dashboard/dist/index.html +4 -4
- package/docs/api/http/personas.md +160 -0
- package/docs/cli.md +12 -0
- package/docs/dashboard.md +25 -3
- package/docs/design-principles.md +13 -5
- package/docs/hitl/pinned-views.md +3 -3
- package/docs/hitl/x-lt-list-schema.md +2 -3
- package/docs/iam.md +47 -0
- package/docs/operations.md +1 -1
- package/docs/sdk.md +1 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/AgentDetailPage-D-Xbyrb6.js +0 -4
- package/dashboard/dist/assets/AgentDetailPage-D-Xbyrb6.js.map +0 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-B-aHvGKD.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-B-aHvGKD.js.map +0 -1
- package/dashboard/dist/assets/CountdownTimer-BhF3oqS8.js +0 -2
- package/dashboard/dist/assets/CountdownTimer-BhF3oqS8.js.map +0 -1
- package/dashboard/dist/assets/ElapsedCell-BE_CN5sP.js +0 -2
- package/dashboard/dist/assets/HomePage-B6s1ZeyA.js +0 -2
- package/dashboard/dist/assets/HomePage-B6s1ZeyA.js.map +0 -1
- package/dashboard/dist/assets/ListToolbar-DVhlAKZ3.js +0 -2
- package/dashboard/dist/assets/ListToolbar-DVhlAKZ3.js.map +0 -1
- package/dashboard/dist/assets/McpQueryDetailPage-D0r2lyZg.js +0 -5
- package/dashboard/dist/assets/McpQueryDetailPage-D0r2lyZg.js.map +0 -1
- package/dashboard/dist/assets/McpQueryPage-DPP6BB3d.js +0 -2
- package/dashboard/dist/assets/McpQueryPage-DPP6BB3d.js.map +0 -1
- package/dashboard/dist/assets/OperationsPage-DbHzQCuE.js +0 -2
- package/dashboard/dist/assets/OperationsPage-DbHzQCuE.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-B70nS_Ah.js +0 -2
- package/dashboard/dist/assets/OperatorDashboard-B70nS_Ah.js.map +0 -1
- package/dashboard/dist/assets/ProcessDetailPage-eLLUCWFf.js +0 -2
- package/dashboard/dist/assets/ProcessDetailPage-eLLUCWFf.js.map +0 -1
- package/dashboard/dist/assets/RoleDetailPage-Dhn9zU5l.js +0 -8
- package/dashboard/dist/assets/RoleDetailPage-Dhn9zU5l.js.map +0 -1
- package/dashboard/dist/assets/RolesPage-BnAwjUiG.js +0 -2
- package/dashboard/dist/assets/RolesPage-BnAwjUiG.js.map +0 -1
- package/dashboard/dist/assets/StatCard-DBANWIss.js +0 -2
- package/dashboard/dist/assets/StatCard-DBANWIss.js.map +0 -1
- package/dashboard/dist/assets/StreamMessageDetail-BmBfNSEr.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-D0aYpJkF.js +0 -2
- package/dashboard/dist/assets/escalation-columns-D0aYpJkF.js.map +0 -1
- package/dashboard/dist/assets/index-B2iZG_HP.css +0 -1
- package/dashboard/dist/assets/index-CSTwyrwu.js.map +0 -1
- package/dashboard/dist/assets/index-CjBdB8Ht.js +0 -2
- package/dashboard/dist/assets/index-D-7mT0ew.js +0 -2
- package/dashboard/dist/assets/index-D-7mT0ew.js.map +0 -1
- package/dashboard/dist/assets/index-DDA7FvqT.js +0 -2
- package/dashboard/dist/assets/index-DDA7FvqT.js.map +0 -1
- package/dashboard/dist/assets/role-display-Dvo2lDpU.js +0 -2
- package/dashboard/dist/assets/role-display-Dvo2lDpU.js.map +0 -1
- package/dashboard/dist/assets/vendor-icons-Bg6eJX4Z.js.map +0 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const LIST_PERSONAS = "SELECT p.id, p.key, p.title, p.description, r.roles, u.user_count, p.created_at, p.updated_at\n FROM lt_personas p\n LEFT JOIN LATERAL (\n SELECT COALESCE(json_agg(json_build_object('role', pr.role, 'relationship', pr.relationship)\n ORDER BY pr.created_at, pr.role), '[]'::json) AS roles\n FROM lt_persona_roles pr WHERE pr.persona_id = p.id\n ) r ON true\n LEFT JOIN LATERAL (\n SELECT COUNT(*)::int AS user_count FROM lt_user_personas up WHERE up.persona_id = p.id\n ) u ON true\n ORDER BY p.key";
|
|
2
|
+
export declare const GET_PERSONA_BY_KEY = "SELECT p.id, p.key, p.title, p.description, r.roles, u.user_count, p.created_at, p.updated_at\n FROM lt_personas p\n LEFT JOIN LATERAL (\n SELECT COALESCE(json_agg(json_build_object('role', pr.role, 'relationship', pr.relationship)\n ORDER BY pr.created_at, pr.role), '[]'::json) AS roles\n FROM lt_persona_roles pr WHERE pr.persona_id = p.id\n ) r ON true\n LEFT JOIN LATERAL (\n SELECT COUNT(*)::int AS user_count FROM lt_user_personas up WHERE up.persona_id = p.id\n ) u ON true\n WHERE p.key = $1";
|
|
3
|
+
export declare const CREATE_PERSONA = "INSERT INTO lt_personas (key, title, description) VALUES ($1, $2, $3)\n RETURNING id, key, title, description, created_at, updated_at";
|
|
4
|
+
/** Declarative upsert for the seed pass — the spec is authoritative for title/description. */
|
|
5
|
+
export declare const UPSERT_PERSONA = "INSERT INTO lt_personas (key, title, description) VALUES ($1, $2, $3)\n ON CONFLICT (key) DO UPDATE SET\n title = EXCLUDED.title,\n description = EXCLUDED.description,\n updated_at = NOW()\n RETURNING id";
|
|
6
|
+
/** PATCH semantics: $2/$4 are set-sentinels gating whether $3/$5 write. */
|
|
7
|
+
export declare const UPDATE_PERSONA = "UPDATE lt_personas SET\n title = CASE WHEN $2 THEN $3 ELSE title END,\n description = CASE WHEN $4 THEN $5 ELSE description END,\n updated_at = NOW()\n WHERE key = $1\n RETURNING id, key, title, description, created_at, updated_at";
|
|
8
|
+
export declare const DELETE_PERSONA = "DELETE FROM lt_personas WHERE id = $1";
|
|
9
|
+
export declare const GET_PERSONA_ASSIGNEES = "SELECT us.id, us.external_id, us.display_name, us.email, up.created_at AS assigned_at\n FROM lt_user_personas up\n JOIN lt_users us ON us.id = up.user_id\n WHERE up.persona_id = $1\n ORDER BY up.created_at, us.external_id";
|
|
10
|
+
export declare const UPSERT_PERSONA_ROLE = "INSERT INTO lt_persona_roles (persona_id, role, relationship) VALUES ($1, $2, $3)\n ON CONFLICT (persona_id, role) DO UPDATE SET relationship = EXCLUDED.relationship\n RETURNING role, relationship";
|
|
11
|
+
export declare const DELETE_PERSONA_ROLE = "DELETE FROM lt_persona_roles WHERE persona_id = $1 AND role = $2";
|
|
12
|
+
/** Replace a persona's links declaratively: prune links not in $2, upsert the rest. */
|
|
13
|
+
export declare const SYNC_PERSONA_ROLES = "WITH incoming AS (\n SELECT x.role, x.relationship\n FROM unnest($2::text[], $3::text[]) AS x(role, relationship)\n ), pruned AS (\n DELETE FROM lt_persona_roles pr\n WHERE pr.persona_id = $1 AND pr.role NOT IN (SELECT role FROM incoming)\n )\n INSERT INTO lt_persona_roles (persona_id, role, relationship)\n SELECT $1, role, relationship FROM incoming\n ON CONFLICT (persona_id, role) DO UPDATE SET relationship = EXCLUDED.relationship";
|
|
14
|
+
export declare const INSERT_USER_PERSONA = "INSERT INTO lt_user_personas (user_id, persona_id) VALUES ($1, $2)\n ON CONFLICT DO NOTHING";
|
|
15
|
+
export declare const DELETE_USER_PERSONA = "DELETE FROM lt_user_personas WHERE user_id = $1 AND persona_id = $2";
|
|
16
|
+
export declare const LIST_PERSONA_HOLDER_IDS = "SELECT user_id FROM lt_user_personas WHERE persona_id = $1";
|
|
17
|
+
/** Users with a membership row still sustained by this persona (drift guard for delete). */
|
|
18
|
+
export declare const LIST_SUSTAINED_USER_IDS = "SELECT DISTINCT user_id FROM lt_user_roles WHERE granted_by_persona = $1";
|
|
19
|
+
export declare const RECOMPUTE_PERSONA_MEMBERSHIPS = "WITH affected AS (\n SELECT unnest($1::uuid[]) AS user_id\n ), grants AS (\n SELECT up.user_id, pr.role,\n MAX(CASE pr.relationship WHEN 'write-all' THEN 2 WHEN 'write-self' THEN 1 ELSE 0 END) AS write_rank,\n (MIN(pr.persona_id::text))::uuid AS sustaining_persona\n FROM lt_user_personas up\n JOIN lt_persona_roles pr ON pr.persona_id = up.persona_id\n JOIN affected a ON a.user_id = up.user_id\n GROUP BY up.user_id, pr.role\n ), removed AS (\n DELETE FROM lt_user_roles ur\n USING affected a\n WHERE ur.user_id = a.user_id\n AND ur.granted_by_persona IS NOT NULL\n AND NOT EXISTS (SELECT 1 FROM grants g WHERE g.user_id = ur.user_id AND g.role = ur.role)\n RETURNING ur.user_id, ur.role\n ), refreshed AS (\n UPDATE lt_user_roles ur\n SET read_scope = 'all',\n write_scope = CASE g.write_rank WHEN 2 THEN 'all' WHEN 1 THEN 'self' ELSE 'none' END,\n granted_by_persona = g.sustaining_persona\n FROM grants g\n WHERE ur.user_id = g.user_id AND ur.role = g.role\n AND ur.granted_by_persona IS NOT NULL\n RETURNING ur.user_id, ur.role\n ), raised AS (\n UPDATE lt_user_roles ur\n SET read_scope = 'all',\n write_scope = CASE\n WHEN g.write_rank = 2 THEN 'all'\n WHEN g.write_rank = 1 AND ur.write_scope = 'none' THEN 'self'\n ELSE ur.write_scope\n END\n FROM grants g\n WHERE ur.user_id = g.user_id AND ur.role = g.role\n AND ur.granted_by_persona IS NULL\n AND (ur.read_scope <> 'all'\n OR g.write_rank > CASE ur.write_scope WHEN 'all' THEN 2 WHEN 'self' THEN 1 ELSE 0 END)\n RETURNING ur.user_id, ur.role\n ), granted AS (\n INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope, granted_by_persona)\n SELECT g.user_id, g.role, 'member', 'all',\n CASE g.write_rank WHEN 2 THEN 'all' WHEN 1 THEN 'self' ELSE 'none' END,\n g.sustaining_persona\n FROM grants g\n WHERE NOT EXISTS (SELECT 1 FROM lt_user_roles ur WHERE ur.user_id = g.user_id AND ur.role = g.role)\n RETURNING user_id, role\n )\n SELECT\n (SELECT COUNT(*)::int FROM granted) AS granted,\n (SELECT COUNT(*)::int FROM refreshed) AS refreshed,\n (SELECT COUNT(*)::int FROM raised) AS raised,\n (SELECT COUNT(*)::int FROM removed) AS removed";
|
|
20
|
+
export declare const GET_USER_PERSONAS = "SELECT p.id, p.key, p.title, p.description, r.roles, up.created_at AS assigned_at\n FROM lt_user_personas up\n JOIN lt_personas p ON p.id = up.persona_id\n LEFT JOIN LATERAL (\n SELECT COALESCE(json_agg(json_build_object('role', pr.role, 'relationship', pr.relationship)\n ORDER BY pr.created_at, pr.role), '[]'::json) AS roles\n FROM lt_persona_roles pr WHERE pr.persona_id = p.id\n ) r ON true\n WHERE up.user_id = $1\n ORDER BY p.key";
|
|
21
|
+
/** The composed role/scope map: every membership with its sustaining persona key (null = direct). */
|
|
22
|
+
export declare const GET_USER_COMPOSED_ROLES = "SELECT ur.role, ur.read_scope, ur.write_scope, p.key AS granted_by_persona\n FROM lt_user_roles ur\n LEFT JOIN lt_personas p ON p.id = ur.granted_by_persona\n WHERE ur.user_id = $1\n ORDER BY ur.role";
|
|
23
|
+
export declare const GET_PERSONA_ID_BY_KEY = "SELECT id FROM lt_personas WHERE key = $1";
|
|
24
|
+
export declare const ENSURE_ROLES_EXIST = "INSERT INTO lt_roles (role) SELECT DISTINCT unnest($1::text[]) ON CONFLICT DO NOTHING";
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ─── Persona CRUD ─────────────────────────────────────────────────────────────
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.ENSURE_ROLES_EXIST = exports.GET_PERSONA_ID_BY_KEY = exports.GET_USER_COMPOSED_ROLES = exports.GET_USER_PERSONAS = exports.RECOMPUTE_PERSONA_MEMBERSHIPS = exports.LIST_SUSTAINED_USER_IDS = exports.LIST_PERSONA_HOLDER_IDS = exports.DELETE_USER_PERSONA = exports.INSERT_USER_PERSONA = exports.SYNC_PERSONA_ROLES = exports.DELETE_PERSONA_ROLE = exports.UPSERT_PERSONA_ROLE = exports.GET_PERSONA_ASSIGNEES = exports.DELETE_PERSONA = exports.UPDATE_PERSONA = exports.UPSERT_PERSONA = exports.CREATE_PERSONA = exports.GET_PERSONA_BY_KEY = exports.LIST_PERSONAS = void 0;
|
|
5
|
+
/** Aggregated role links for one persona, as a JSON array column. */
|
|
6
|
+
const ROLES_LATERAL = `LEFT JOIN LATERAL (
|
|
7
|
+
SELECT COALESCE(json_agg(json_build_object('role', pr.role, 'relationship', pr.relationship)
|
|
8
|
+
ORDER BY pr.created_at, pr.role), '[]'::json) AS roles
|
|
9
|
+
FROM lt_persona_roles pr WHERE pr.persona_id = p.id
|
|
10
|
+
) r ON true
|
|
11
|
+
LEFT JOIN LATERAL (
|
|
12
|
+
SELECT COUNT(*)::int AS user_count FROM lt_user_personas up WHERE up.persona_id = p.id
|
|
13
|
+
) u ON true`;
|
|
14
|
+
exports.LIST_PERSONAS = `SELECT p.id, p.key, p.title, p.description, r.roles, u.user_count, p.created_at, p.updated_at
|
|
15
|
+
FROM lt_personas p
|
|
16
|
+
${ROLES_LATERAL}
|
|
17
|
+
ORDER BY p.key`;
|
|
18
|
+
exports.GET_PERSONA_BY_KEY = `SELECT p.id, p.key, p.title, p.description, r.roles, u.user_count, p.created_at, p.updated_at
|
|
19
|
+
FROM lt_personas p
|
|
20
|
+
${ROLES_LATERAL}
|
|
21
|
+
WHERE p.key = $1`;
|
|
22
|
+
exports.CREATE_PERSONA = `INSERT INTO lt_personas (key, title, description) VALUES ($1, $2, $3)
|
|
23
|
+
RETURNING id, key, title, description, created_at, updated_at`;
|
|
24
|
+
/** Declarative upsert for the seed pass — the spec is authoritative for title/description. */
|
|
25
|
+
exports.UPSERT_PERSONA = `INSERT INTO lt_personas (key, title, description) VALUES ($1, $2, $3)
|
|
26
|
+
ON CONFLICT (key) DO UPDATE SET
|
|
27
|
+
title = EXCLUDED.title,
|
|
28
|
+
description = EXCLUDED.description,
|
|
29
|
+
updated_at = NOW()
|
|
30
|
+
RETURNING id`;
|
|
31
|
+
/** PATCH semantics: $2/$4 are set-sentinels gating whether $3/$5 write. */
|
|
32
|
+
exports.UPDATE_PERSONA = `UPDATE lt_personas SET
|
|
33
|
+
title = CASE WHEN $2 THEN $3 ELSE title END,
|
|
34
|
+
description = CASE WHEN $4 THEN $5 ELSE description END,
|
|
35
|
+
updated_at = NOW()
|
|
36
|
+
WHERE key = $1
|
|
37
|
+
RETURNING id, key, title, description, created_at, updated_at`;
|
|
38
|
+
exports.DELETE_PERSONA = 'DELETE FROM lt_personas WHERE id = $1';
|
|
39
|
+
exports.GET_PERSONA_ASSIGNEES = `SELECT us.id, us.external_id, us.display_name, us.email, up.created_at AS assigned_at
|
|
40
|
+
FROM lt_user_personas up
|
|
41
|
+
JOIN lt_users us ON us.id = up.user_id
|
|
42
|
+
WHERE up.persona_id = $1
|
|
43
|
+
ORDER BY up.created_at, us.external_id`;
|
|
44
|
+
// ─── Role links ───────────────────────────────────────────────────────────────
|
|
45
|
+
exports.UPSERT_PERSONA_ROLE = `INSERT INTO lt_persona_roles (persona_id, role, relationship) VALUES ($1, $2, $3)
|
|
46
|
+
ON CONFLICT (persona_id, role) DO UPDATE SET relationship = EXCLUDED.relationship
|
|
47
|
+
RETURNING role, relationship`;
|
|
48
|
+
exports.DELETE_PERSONA_ROLE = 'DELETE FROM lt_persona_roles WHERE persona_id = $1 AND role = $2';
|
|
49
|
+
/** Replace a persona's links declaratively: prune links not in $2, upsert the rest. */
|
|
50
|
+
exports.SYNC_PERSONA_ROLES = `WITH incoming AS (
|
|
51
|
+
SELECT x.role, x.relationship
|
|
52
|
+
FROM unnest($2::text[], $3::text[]) AS x(role, relationship)
|
|
53
|
+
), pruned AS (
|
|
54
|
+
DELETE FROM lt_persona_roles pr
|
|
55
|
+
WHERE pr.persona_id = $1 AND pr.role NOT IN (SELECT role FROM incoming)
|
|
56
|
+
)
|
|
57
|
+
INSERT INTO lt_persona_roles (persona_id, role, relationship)
|
|
58
|
+
SELECT $1, role, relationship FROM incoming
|
|
59
|
+
ON CONFLICT (persona_id, role) DO UPDATE SET relationship = EXCLUDED.relationship`;
|
|
60
|
+
// ─── Assignment ───────────────────────────────────────────────────────────────
|
|
61
|
+
exports.INSERT_USER_PERSONA = `INSERT INTO lt_user_personas (user_id, persona_id) VALUES ($1, $2)
|
|
62
|
+
ON CONFLICT DO NOTHING`;
|
|
63
|
+
exports.DELETE_USER_PERSONA = 'DELETE FROM lt_user_personas WHERE user_id = $1 AND persona_id = $2';
|
|
64
|
+
exports.LIST_PERSONA_HOLDER_IDS = 'SELECT user_id FROM lt_user_personas WHERE persona_id = $1';
|
|
65
|
+
/** Users with a membership row still sustained by this persona (drift guard for delete). */
|
|
66
|
+
exports.LIST_SUSTAINED_USER_IDS = 'SELECT DISTINCT user_id FROM lt_user_roles WHERE granted_by_persona = $1';
|
|
67
|
+
// ─── Recompute: materialize persona grants into lt_user_roles ────────────────
|
|
68
|
+
//
|
|
69
|
+
// The single reconciliation statement behind assign, unassign, link edits,
|
|
70
|
+
// persona deletion, and seeding. For every user in $1::uuid[], it derives the
|
|
71
|
+
// highest-allowance union of the user's currently-held personas and reconciles
|
|
72
|
+
// lt_user_roles against it:
|
|
73
|
+
//
|
|
74
|
+
// removed — persona-sustained rows no held persona still grants
|
|
75
|
+
// refreshed — persona-sustained rows overlaid fresh (union scope, re-homed
|
|
76
|
+
// provenance so unassigning one persona never drops a role a
|
|
77
|
+
// sibling persona still grants)
|
|
78
|
+
// raised — DIRECT rows lifted to at least the union (highest allowance
|
|
79
|
+
// wins); scope is never lowered, type and provenance untouched
|
|
80
|
+
// granted — missing rows inserted as scoped members
|
|
81
|
+
//
|
|
82
|
+
// Direct rows (granted_by_persona IS NULL) are never deleted here — persona
|
|
83
|
+
// removal must not touch hand-tuned memberships. Write-rank encoding:
|
|
84
|
+
// write-all=2 (all), write-self=1 (self), read-all=0 (none).
|
|
85
|
+
exports.RECOMPUTE_PERSONA_MEMBERSHIPS = `WITH affected AS (
|
|
86
|
+
SELECT unnest($1::uuid[]) AS user_id
|
|
87
|
+
), grants AS (
|
|
88
|
+
SELECT up.user_id, pr.role,
|
|
89
|
+
MAX(CASE pr.relationship WHEN 'write-all' THEN 2 WHEN 'write-self' THEN 1 ELSE 0 END) AS write_rank,
|
|
90
|
+
(MIN(pr.persona_id::text))::uuid AS sustaining_persona
|
|
91
|
+
FROM lt_user_personas up
|
|
92
|
+
JOIN lt_persona_roles pr ON pr.persona_id = up.persona_id
|
|
93
|
+
JOIN affected a ON a.user_id = up.user_id
|
|
94
|
+
GROUP BY up.user_id, pr.role
|
|
95
|
+
), removed AS (
|
|
96
|
+
DELETE FROM lt_user_roles ur
|
|
97
|
+
USING affected a
|
|
98
|
+
WHERE ur.user_id = a.user_id
|
|
99
|
+
AND ur.granted_by_persona IS NOT NULL
|
|
100
|
+
AND NOT EXISTS (SELECT 1 FROM grants g WHERE g.user_id = ur.user_id AND g.role = ur.role)
|
|
101
|
+
RETURNING ur.user_id, ur.role
|
|
102
|
+
), refreshed AS (
|
|
103
|
+
UPDATE lt_user_roles ur
|
|
104
|
+
SET read_scope = 'all',
|
|
105
|
+
write_scope = CASE g.write_rank WHEN 2 THEN 'all' WHEN 1 THEN 'self' ELSE 'none' END,
|
|
106
|
+
granted_by_persona = g.sustaining_persona
|
|
107
|
+
FROM grants g
|
|
108
|
+
WHERE ur.user_id = g.user_id AND ur.role = g.role
|
|
109
|
+
AND ur.granted_by_persona IS NOT NULL
|
|
110
|
+
RETURNING ur.user_id, ur.role
|
|
111
|
+
), raised AS (
|
|
112
|
+
UPDATE lt_user_roles ur
|
|
113
|
+
SET read_scope = 'all',
|
|
114
|
+
write_scope = CASE
|
|
115
|
+
WHEN g.write_rank = 2 THEN 'all'
|
|
116
|
+
WHEN g.write_rank = 1 AND ur.write_scope = 'none' THEN 'self'
|
|
117
|
+
ELSE ur.write_scope
|
|
118
|
+
END
|
|
119
|
+
FROM grants g
|
|
120
|
+
WHERE ur.user_id = g.user_id AND ur.role = g.role
|
|
121
|
+
AND ur.granted_by_persona IS NULL
|
|
122
|
+
AND (ur.read_scope <> 'all'
|
|
123
|
+
OR g.write_rank > CASE ur.write_scope WHEN 'all' THEN 2 WHEN 'self' THEN 1 ELSE 0 END)
|
|
124
|
+
RETURNING ur.user_id, ur.role
|
|
125
|
+
), granted AS (
|
|
126
|
+
INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope, granted_by_persona)
|
|
127
|
+
SELECT g.user_id, g.role, 'member', 'all',
|
|
128
|
+
CASE g.write_rank WHEN 2 THEN 'all' WHEN 1 THEN 'self' ELSE 'none' END,
|
|
129
|
+
g.sustaining_persona
|
|
130
|
+
FROM grants g
|
|
131
|
+
WHERE NOT EXISTS (SELECT 1 FROM lt_user_roles ur WHERE ur.user_id = g.user_id AND ur.role = g.role)
|
|
132
|
+
RETURNING user_id, role
|
|
133
|
+
)
|
|
134
|
+
SELECT
|
|
135
|
+
(SELECT COUNT(*)::int FROM granted) AS granted,
|
|
136
|
+
(SELECT COUNT(*)::int FROM refreshed) AS refreshed,
|
|
137
|
+
(SELECT COUNT(*)::int FROM raised) AS raised,
|
|
138
|
+
(SELECT COUNT(*)::int FROM removed) AS removed`;
|
|
139
|
+
// ─── forUser ──────────────────────────────────────────────────────────────────
|
|
140
|
+
exports.GET_USER_PERSONAS = `SELECT p.id, p.key, p.title, p.description, r.roles, up.created_at AS assigned_at
|
|
141
|
+
FROM lt_user_personas up
|
|
142
|
+
JOIN lt_personas p ON p.id = up.persona_id
|
|
143
|
+
LEFT JOIN LATERAL (
|
|
144
|
+
SELECT COALESCE(json_agg(json_build_object('role', pr.role, 'relationship', pr.relationship)
|
|
145
|
+
ORDER BY pr.created_at, pr.role), '[]'::json) AS roles
|
|
146
|
+
FROM lt_persona_roles pr WHERE pr.persona_id = p.id
|
|
147
|
+
) r ON true
|
|
148
|
+
WHERE up.user_id = $1
|
|
149
|
+
ORDER BY p.key`;
|
|
150
|
+
/** The composed role/scope map: every membership with its sustaining persona key (null = direct). */
|
|
151
|
+
exports.GET_USER_COMPOSED_ROLES = `SELECT ur.role, ur.read_scope, ur.write_scope, p.key AS granted_by_persona
|
|
152
|
+
FROM lt_user_roles ur
|
|
153
|
+
LEFT JOIN lt_personas p ON p.id = ur.granted_by_persona
|
|
154
|
+
WHERE ur.user_id = $1
|
|
155
|
+
ORDER BY ur.role`;
|
|
156
|
+
// ─── Shared ───────────────────────────────────────────────────────────────────
|
|
157
|
+
exports.GET_PERSONA_ID_BY_KEY = 'SELECT id FROM lt_personas WHERE key = $1';
|
|
158
|
+
exports.ENSURE_ROLES_EXIST = `INSERT INTO lt_roles (role) SELECT DISTINCT unnest($1::text[]) ON CONFLICT DO NOTHING`;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { LTPersonaRelationship, LTWriteScope } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Canonical relationship values, ordered by allowance (highest first). A
|
|
4
|
+
* persona grant is always a `member` membership; the relationship selects the
|
|
5
|
+
* point on the work-surface scope lattice the membership stores.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PERSONA_RELATIONSHIPS: LTPersonaRelationship[];
|
|
8
|
+
/** Normalize an input relationship (canonical or alias) — null if unknown. */
|
|
9
|
+
export declare function normalizeRelationship(value: string): LTPersonaRelationship | null;
|
|
10
|
+
/**
|
|
11
|
+
* The membership scope a relationship grants. Read is always `all` (an
|
|
12
|
+
* observer sees the whole pond); write is the axis the relationship selects.
|
|
13
|
+
*/
|
|
14
|
+
export declare function relationshipToWriteScope(relationship: LTPersonaRelationship): LTWriteScope;
|
|
15
|
+
export interface CreatePersonaInput {
|
|
16
|
+
key: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface UpdatePersonaInput {
|
|
21
|
+
title?: string | null;
|
|
22
|
+
description?: string | null;
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PERSONA_RELATIONSHIPS = void 0;
|
|
4
|
+
exports.normalizeRelationship = normalizeRelationship;
|
|
5
|
+
exports.relationshipToWriteScope = relationshipToWriteScope;
|
|
6
|
+
/**
|
|
7
|
+
* Canonical relationship values, ordered by allowance (highest first). A
|
|
8
|
+
* persona grant is always a `member` membership; the relationship selects the
|
|
9
|
+
* point on the work-surface scope lattice the membership stores.
|
|
10
|
+
*/
|
|
11
|
+
exports.PERSONA_RELATIONSHIPS = ['write-all', 'write-self', 'read-all'];
|
|
12
|
+
/** Input synonyms accepted at every surface and normalized to canonical values. */
|
|
13
|
+
const RELATIONSHIP_ALIASES = {
|
|
14
|
+
'write-none': 'read-all',
|
|
15
|
+
};
|
|
16
|
+
/** Normalize an input relationship (canonical or alias) — null if unknown. */
|
|
17
|
+
function normalizeRelationship(value) {
|
|
18
|
+
if (exports.PERSONA_RELATIONSHIPS.includes(value)) {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
return RELATIONSHIP_ALIASES[value] ?? null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The membership scope a relationship grants. Read is always `all` (an
|
|
25
|
+
* observer sees the whole pond); write is the axis the relationship selects.
|
|
26
|
+
*/
|
|
27
|
+
function relationshipToWriteScope(relationship) {
|
|
28
|
+
switch (relationship) {
|
|
29
|
+
case 'write-all': return 'all';
|
|
30
|
+
case 'write-self': return 'self';
|
|
31
|
+
case 'read-all': return 'none';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -149,6 +149,7 @@ async function updateRoleMetadata(role, input) {
|
|
|
149
149
|
provided('list_schema'), input.list_schema != null ? JSON.stringify(input.list_schema) : null,
|
|
150
150
|
provided('default_pins'), input.default_pins != null ? JSON.stringify(input.default_pins) : null,
|
|
151
151
|
provided('enforce_schema'), input.enforce_schema ?? null,
|
|
152
|
+
provided('ops_home_default'), input.ops_home_default ?? null,
|
|
152
153
|
]);
|
|
153
154
|
if (!rows[0])
|
|
154
155
|
return null;
|
|
@@ -46,8 +46,12 @@ export declare const DELETE_ROLE = "\n DELETE FROM lt_roles WHERE role = $1";
|
|
|
46
46
|
* enforce_schema ($33 = provided sentinel, $34 = value) is unversioned config:
|
|
47
47
|
* the server-side resolver-validation opt-in. The service layer invalidates
|
|
48
48
|
* the enforcement cache after this statement commits.
|
|
49
|
+
*
|
|
50
|
+
* ops_home_default ($35 = provided sentinel, $36 = value) is single-holder:
|
|
51
|
+
* setting it true clears the flag on every other role in the same statement
|
|
52
|
+
* (the home_clear CTE touches only OTHER rows, so the two UPDATEs never meet).
|
|
49
53
|
*/
|
|
50
|
-
export declare const UPDATE_ROLE_METADATA = "\n WITH updated AS (\n UPDATE lt_roles SET\n title = CASE WHEN $2::boolean THEN $3 ELSE title END,\n description = CASE WHEN $4::boolean THEN $5 ELSE description END,\n form_schema = CASE WHEN $6::boolean THEN $7::jsonb ELSE form_schema END,\n metadata_schema = CASE WHEN $8::boolean THEN $9::jsonb ELSE metadata_schema END,\n properties = CASE WHEN $10::boolean THEN COALESCE($11::jsonb, '{}'::jsonb) ELSE properties END,\n ops_visible = CASE WHEN $12::boolean THEN $13::boolean ELSE ops_visible END,\n parent_role = CASE WHEN $14::boolean THEN $15 ELSE parent_role END,\n sla_minutes = CASE WHEN $16::boolean THEN $17::numeric ELSE sla_minutes END,\n target_per_hour = CASE WHEN $18::boolean THEN $19::numeric ELSE target_per_hour END,\n worker_count = CASE WHEN $20::boolean THEN $21::int ELSE worker_count END,\n priority_threshold_minutes\n = CASE WHEN $22::boolean THEN $23::numeric ELSE priority_threshold_minutes END,\n priority_facet = CASE WHEN $24::boolean THEN $25 ELSE priority_facet END,\n list_schema = CASE WHEN $29::boolean THEN $30::jsonb ELSE list_schema END,\n default_pins = CASE WHEN $31::boolean THEN $32::jsonb ELSE default_pins END,\n enforce_schema = CASE WHEN $33::boolean THEN COALESCE($34::boolean, false) ELSE enforce_schema END,\n current_schema_version = CASE\n WHEN ($6::boolean AND $7::jsonb IS DISTINCT FROM form_schema)\n OR ($8::boolean AND $9::jsonb IS DISTINCT FROM metadata_schema)\n THEN COALESCE(current_schema_version, 0) + 1\n ELSE current_schema_version END,\n current_list_schema_version = CASE\n WHEN $29::boolean AND $30::jsonb IS DISTINCT FROM list_schema\n THEN COALESCE(current_list_schema_version, 0) + 1\n ELSE current_list_schema_version END\n WHERE role = $1\n RETURNING\n role, title, description, form_schema, metadata_schema, properties,\n ops_visible, parent_role, sla_minutes, target_per_hour, worker_count,\n priority_threshold_minutes, priority_facet,\n current_schema_version, list_schema, current_list_schema_version,\n default_pins, enforce_schema\n ), snapshot AS (\n INSERT INTO lt_role_schemas (role, version, form_schema, metadata_schema, change_summary)\n SELECT role, current_schema_version, form_schema, metadata_schema, $26\n FROM updated\n WHERE ($6::boolean OR $8::boolean) AND current_schema_version IS NOT NULL\n ON CONFLICT (role, version) DO NOTHING\n ), list_snapshot AS (\n INSERT INTO lt_role_list_schemas (role, version, list_schema, change_summary)\n SELECT role, current_list_schema_version, list_schema, $26\n FROM updated\n WHERE $29::boolean AND current_list_schema_version IS NOT NULL\n ON CONFLICT (role, version) DO NOTHING\n ), upstream_prune AS (\n DELETE FROM lt_role_upstreams\n WHERE $27::boolean AND role = $1 AND upstream_role <> ALL($28::text[])\n ), upstream_add AS (\n INSERT INTO lt_role_upstreams (role, upstream_role)\n SELECT $1, u FROM unnest($28::text[]) AS u\n WHERE $27::boolean\n ON CONFLICT DO NOTHING\n )\n SELECT * FROM updated";
|
|
54
|
+
export declare const UPDATE_ROLE_METADATA = "\n WITH home_clear AS (\n UPDATE lt_roles SET ops_home_default = false\n WHERE $35::boolean AND COALESCE($36::boolean, false) AND role <> $1 AND ops_home_default\n ), updated AS (\n UPDATE lt_roles SET\n title = CASE WHEN $2::boolean THEN $3 ELSE title END,\n description = CASE WHEN $4::boolean THEN $5 ELSE description END,\n form_schema = CASE WHEN $6::boolean THEN $7::jsonb ELSE form_schema END,\n metadata_schema = CASE WHEN $8::boolean THEN $9::jsonb ELSE metadata_schema END,\n properties = CASE WHEN $10::boolean THEN COALESCE($11::jsonb, '{}'::jsonb) ELSE properties END,\n ops_visible = CASE WHEN $12::boolean THEN $13::boolean ELSE ops_visible END,\n parent_role = CASE WHEN $14::boolean THEN $15 ELSE parent_role END,\n sla_minutes = CASE WHEN $16::boolean THEN $17::numeric ELSE sla_minutes END,\n target_per_hour = CASE WHEN $18::boolean THEN $19::numeric ELSE target_per_hour END,\n worker_count = CASE WHEN $20::boolean THEN $21::int ELSE worker_count END,\n priority_threshold_minutes\n = CASE WHEN $22::boolean THEN $23::numeric ELSE priority_threshold_minutes END,\n priority_facet = CASE WHEN $24::boolean THEN $25 ELSE priority_facet END,\n list_schema = CASE WHEN $29::boolean THEN $30::jsonb ELSE list_schema END,\n default_pins = CASE WHEN $31::boolean THEN $32::jsonb ELSE default_pins END,\n enforce_schema = CASE WHEN $33::boolean THEN COALESCE($34::boolean, false) ELSE enforce_schema END,\n ops_home_default = CASE WHEN $35::boolean THEN COALESCE($36::boolean, false) ELSE ops_home_default END,\n current_schema_version = CASE\n WHEN ($6::boolean AND $7::jsonb IS DISTINCT FROM form_schema)\n OR ($8::boolean AND $9::jsonb IS DISTINCT FROM metadata_schema)\n THEN COALESCE(current_schema_version, 0) + 1\n ELSE current_schema_version END,\n current_list_schema_version = CASE\n WHEN $29::boolean AND $30::jsonb IS DISTINCT FROM list_schema\n THEN COALESCE(current_list_schema_version, 0) + 1\n ELSE current_list_schema_version END\n WHERE role = $1\n RETURNING\n role, title, description, form_schema, metadata_schema, properties,\n ops_visible, ops_home_default, parent_role, sla_minutes, target_per_hour, worker_count,\n priority_threshold_minutes, priority_facet,\n current_schema_version, list_schema, current_list_schema_version,\n default_pins, enforce_schema\n ), snapshot AS (\n INSERT INTO lt_role_schemas (role, version, form_schema, metadata_schema, change_summary)\n SELECT role, current_schema_version, form_schema, metadata_schema, $26\n FROM updated\n WHERE ($6::boolean OR $8::boolean) AND current_schema_version IS NOT NULL\n ON CONFLICT (role, version) DO NOTHING\n ), list_snapshot AS (\n INSERT INTO lt_role_list_schemas (role, version, list_schema, change_summary)\n SELECT role, current_list_schema_version, list_schema, $26\n FROM updated\n WHERE $29::boolean AND current_list_schema_version IS NOT NULL\n ON CONFLICT (role, version) DO NOTHING\n ), upstream_prune AS (\n DELETE FROM lt_role_upstreams\n WHERE $27::boolean AND role = $1 AND upstream_role <> ALL($28::text[])\n ), upstream_add AS (\n INSERT INTO lt_role_upstreams (role, upstream_role)\n SELECT $1, u FROM unnest($28::text[]) AS u\n WHERE $27::boolean\n ON CONFLICT DO NOTHING\n )\n SELECT * FROM updated";
|
|
51
55
|
export declare const GET_ROLE_FORM_SCHEMA = "\n SELECT form_schema FROM lt_roles WHERE role = $1";
|
|
52
56
|
/**
|
|
53
57
|
* The set of roles with server-side resolver validation turned on. Small and
|
|
@@ -71,7 +75,7 @@ export declare const GET_ROLE_LIST_SCHEMA_VERSION = "\n SELECT s.role, s.versio
|
|
|
71
75
|
* never versioned its list schema still answers, with version NULL).
|
|
72
76
|
*/
|
|
73
77
|
export declare const GET_ROLE_LIST_SCHEMA_CURRENT = "\n SELECT r.role, r.current_list_schema_version AS version, r.list_schema,\n s.change_summary, s.created_at,\n r.current_list_schema_version AS latest_version\n FROM lt_roles r\n LEFT JOIN lt_role_list_schemas s\n ON s.role = r.role AND s.version = r.current_list_schema_version\n WHERE r.role = $1";
|
|
74
|
-
export declare const LIST_ROLES_WITH_DETAILS = "\n WITH\n user_counts AS (\n SELECT role, COUNT(DISTINCT user_id)::int AS cnt\n FROM lt_user_roles\n GROUP BY role\n ),\n chain_counts AS (\n SELECT role, COUNT(*)::int AS cnt\n FROM (\n SELECT source_role AS role FROM lt_config_role_escalations\n UNION ALL\n SELECT target_role AS role FROM lt_config_role_escalations\n ) c\n GROUP BY role\n ),\n workflow_counts AS (\n SELECT role, COUNT(*)::int AS cnt\n FROM lt_config_roles\n GROUP BY role\n ),\n upstreams AS (\n SELECT role, array_agg(upstream_role ORDER BY upstream_role) AS ups\n FROM lt_role_upstreams\n GROUP BY role\n )\n SELECT\n r.role,\n r.title,\n r.description,\n r.form_schema,\n r.metadata_schema,\n r.properties,\n r.ops_visible,\n r.parent_role,\n r.sla_minutes,\n r.target_per_hour,\n r.worker_count,\n r.priority_threshold_minutes,\n r.priority_facet,\n r.current_schema_version,\n r.list_schema,\n r.current_list_schema_version,\n r.default_pins,\n r.enforce_schema,\n COALESCE(up.ups, '{}') AS upstream_roles,\n COALESCE(uc.cnt, 0) AS user_count,\n COALESCE(cc.cnt, 0) AS chain_count,\n COALESCE(wc.cnt, 0) AS workflow_count\n FROM lt_roles r\n LEFT JOIN user_counts uc ON uc.role = r.role\n LEFT JOIN chain_counts cc ON cc.role = r.role\n LEFT JOIN workflow_counts wc ON wc.role = r.role\n LEFT JOIN upstreams up ON up.role = r.role\n ORDER BY r.role";
|
|
78
|
+
export declare const LIST_ROLES_WITH_DETAILS = "\n WITH\n user_counts AS (\n SELECT role, COUNT(DISTINCT user_id)::int AS cnt\n FROM lt_user_roles\n GROUP BY role\n ),\n chain_counts AS (\n SELECT role, COUNT(*)::int AS cnt\n FROM (\n SELECT source_role AS role FROM lt_config_role_escalations\n UNION ALL\n SELECT target_role AS role FROM lt_config_role_escalations\n ) c\n GROUP BY role\n ),\n workflow_counts AS (\n SELECT role, COUNT(*)::int AS cnt\n FROM lt_config_roles\n GROUP BY role\n ),\n upstreams AS (\n SELECT role, array_agg(upstream_role ORDER BY upstream_role) AS ups\n FROM lt_role_upstreams\n GROUP BY role\n )\n SELECT\n r.role,\n r.title,\n r.description,\n r.form_schema,\n r.metadata_schema,\n r.properties,\n r.ops_visible,\n r.ops_home_default,\n r.parent_role,\n r.sla_minutes,\n r.target_per_hour,\n r.worker_count,\n r.priority_threshold_minutes,\n r.priority_facet,\n r.current_schema_version,\n r.list_schema,\n r.current_list_schema_version,\n r.default_pins,\n r.enforce_schema,\n COALESCE(up.ups, '{}') AS upstream_roles,\n COALESCE(uc.cnt, 0) AS user_count,\n COALESCE(cc.cnt, 0) AS chain_count,\n COALESCE(wc.cnt, 0) AS workflow_count\n FROM lt_roles r\n LEFT JOIN user_counts uc ON uc.role = r.role\n LEFT JOIN chain_counts cc ON cc.role = r.role\n LEFT JOIN workflow_counts wc ON wc.role = r.role\n LEFT JOIN upstreams up ON up.role = r.role\n ORDER BY r.role";
|
|
75
79
|
export declare const COUNT_USER_ROLE_REFS = "\n SELECT COUNT(*)::int AS cnt FROM lt_user_roles WHERE role = $1";
|
|
76
80
|
export declare const COUNT_CHAIN_REFS = "\n SELECT COUNT(*)::int AS cnt\n FROM lt_config_role_escalations\n WHERE source_role = $1 OR target_role = $1";
|
|
77
81
|
export declare const COUNT_WORKFLOW_REFS = "\n SELECT COUNT(*)::int AS cnt FROM lt_config_roles WHERE role = $1";
|
|
@@ -80,9 +80,16 @@ exports.DELETE_ROLE = `
|
|
|
80
80
|
* enforce_schema ($33 = provided sentinel, $34 = value) is unversioned config:
|
|
81
81
|
* the server-side resolver-validation opt-in. The service layer invalidates
|
|
82
82
|
* the enforcement cache after this statement commits.
|
|
83
|
+
*
|
|
84
|
+
* ops_home_default ($35 = provided sentinel, $36 = value) is single-holder:
|
|
85
|
+
* setting it true clears the flag on every other role in the same statement
|
|
86
|
+
* (the home_clear CTE touches only OTHER rows, so the two UPDATEs never meet).
|
|
83
87
|
*/
|
|
84
88
|
exports.UPDATE_ROLE_METADATA = `
|
|
85
|
-
WITH
|
|
89
|
+
WITH home_clear AS (
|
|
90
|
+
UPDATE lt_roles SET ops_home_default = false
|
|
91
|
+
WHERE $35::boolean AND COALESCE($36::boolean, false) AND role <> $1 AND ops_home_default
|
|
92
|
+
), updated AS (
|
|
86
93
|
UPDATE lt_roles SET
|
|
87
94
|
title = CASE WHEN $2::boolean THEN $3 ELSE title END,
|
|
88
95
|
description = CASE WHEN $4::boolean THEN $5 ELSE description END,
|
|
@@ -100,6 +107,7 @@ exports.UPDATE_ROLE_METADATA = `
|
|
|
100
107
|
list_schema = CASE WHEN $29::boolean THEN $30::jsonb ELSE list_schema END,
|
|
101
108
|
default_pins = CASE WHEN $31::boolean THEN $32::jsonb ELSE default_pins END,
|
|
102
109
|
enforce_schema = CASE WHEN $33::boolean THEN COALESCE($34::boolean, false) ELSE enforce_schema END,
|
|
110
|
+
ops_home_default = CASE WHEN $35::boolean THEN COALESCE($36::boolean, false) ELSE ops_home_default END,
|
|
103
111
|
current_schema_version = CASE
|
|
104
112
|
WHEN ($6::boolean AND $7::jsonb IS DISTINCT FROM form_schema)
|
|
105
113
|
OR ($8::boolean AND $9::jsonb IS DISTINCT FROM metadata_schema)
|
|
@@ -112,7 +120,7 @@ exports.UPDATE_ROLE_METADATA = `
|
|
|
112
120
|
WHERE role = $1
|
|
113
121
|
RETURNING
|
|
114
122
|
role, title, description, form_schema, metadata_schema, properties,
|
|
115
|
-
ops_visible, parent_role, sla_minutes, target_per_hour, worker_count,
|
|
123
|
+
ops_visible, ops_home_default, parent_role, sla_minutes, target_per_hour, worker_count,
|
|
116
124
|
priority_threshold_minutes, priority_facet,
|
|
117
125
|
current_schema_version, list_schema, current_list_schema_version,
|
|
118
126
|
default_pins, enforce_schema
|
|
@@ -247,6 +255,7 @@ exports.LIST_ROLES_WITH_DETAILS = `
|
|
|
247
255
|
r.metadata_schema,
|
|
248
256
|
r.properties,
|
|
249
257
|
r.ops_visible,
|
|
258
|
+
r.ops_home_default,
|
|
250
259
|
r.parent_role,
|
|
251
260
|
r.sla_minutes,
|
|
252
261
|
r.target_per_hour,
|
|
@@ -20,6 +20,13 @@ export interface RoleDetail {
|
|
|
20
20
|
/** Free-form user-owned bag. No reserved keys — use for icons, colors, tags, etc. */
|
|
21
21
|
properties: Record<string, any>;
|
|
22
22
|
ops_visible: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Marks this role's sequence as the HOME page's default Pace Board segment.
|
|
25
|
+
* Single-holder: setting it on one role clears it everywhere else. Only
|
|
26
|
+
* meaningful while ops_visible is on; unset anywhere → the home board shows
|
|
27
|
+
* the primary (first) segment.
|
|
28
|
+
*/
|
|
29
|
+
ops_home_default: boolean;
|
|
23
30
|
parent_role: string | null;
|
|
24
31
|
/** Target resolution time (minutes). Part of the ops triangle. */
|
|
25
32
|
sla_minutes: number | null;
|
|
@@ -134,6 +141,8 @@ export interface UpdateRoleInput {
|
|
|
134
141
|
}[] | null;
|
|
135
142
|
properties?: Record<string, any> | null;
|
|
136
143
|
ops_visible?: boolean;
|
|
144
|
+
/** Make this role's sequence the home Pace Board's default segment (single-holder). */
|
|
145
|
+
ops_home_default?: boolean;
|
|
137
146
|
parent_role?: string | null;
|
|
138
147
|
sla_minutes?: number | null;
|
|
139
148
|
target_per_hour?: number | null;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** Ensure a role name exists in the lt_roles lookup table (FK target). */
|
|
2
2
|
export declare const ENSURE_ROLE_EXISTS = "INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING";
|
|
3
|
-
/** Fetch roles for a single user, ordered by creation time.
|
|
4
|
-
|
|
3
|
+
/** Fetch roles for a single user, ordered by creation time. granted_by_persona
|
|
4
|
+
* is the sustaining persona's key (null = direct grant). */
|
|
5
|
+
export declare const GET_ROLES_BY_USER_ID = "SELECT r.role, r.type, r.read_scope, r.write_scope, r.created_at, p.key AS granted_by_persona\n FROM lt_user_roles r\n LEFT JOIN lt_personas p ON p.id = r.granted_by_persona\n WHERE r.user_id = $1 ORDER BY r.created_at";
|
|
5
6
|
/** Fetch a user row by external_id. */
|
|
6
7
|
export declare const GET_USER_BY_EXTERNAL_ID = "SELECT * FROM lt_users WHERE external_id = $1";
|
|
7
8
|
/**
|
|
@@ -23,9 +24,11 @@ export declare const VERIFY_USER_BY_ID = "SELECT id FROM lt_users WHERE id = $1
|
|
|
23
24
|
export declare const GET_USER_WITH_ROLES = "SELECT u.id, u.external_id, u.display_name, u.status, u.metadata,\n r.role, r.type AS role_type, r.read_scope, r.write_scope\n FROM lt_users u\n LEFT JOIN lt_user_roles r ON r.user_id = u.id\n WHERE u.external_id = $1\n ORDER BY r.created_at";
|
|
24
25
|
export declare const DELETE_USER_BY_ID = "DELETE FROM lt_users WHERE id = $1";
|
|
25
26
|
/** Batch-load roles for many users (avoids N+1). */
|
|
26
|
-
export declare const GET_ROLES_BY_USER_IDS = "SELECT user_id, role, type, read_scope, write_scope, created_at FROM lt_user_roles WHERE user_id = ANY($1) ORDER BY created_at";
|
|
27
|
-
/** Upsert a user–role assignment, promoting the type/scope if the row exists.
|
|
28
|
-
|
|
27
|
+
export declare const GET_ROLES_BY_USER_IDS = "SELECT r.user_id, r.role, r.type, r.read_scope, r.write_scope, r.created_at, p.key AS granted_by_persona\n FROM lt_user_roles r\n LEFT JOIN lt_personas p ON p.id = r.granted_by_persona\n WHERE r.user_id = ANY($1) ORDER BY r.created_at";
|
|
28
|
+
/** Upsert a user–role assignment, promoting the type/scope if the row exists.
|
|
29
|
+
* A direct grant takes the row over: granted_by_persona resets to NULL, so a
|
|
30
|
+
* later persona unassign can never remove a membership an admin set by hand. */
|
|
31
|
+
export declare const UPSERT_USER_ROLE = "INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope) VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (user_id, role) DO UPDATE SET\n type = EXCLUDED.type,\n read_scope = EXCLUDED.read_scope,\n write_scope = EXCLUDED.write_scope,\n granted_by_persona = NULL\n RETURNING role, type, read_scope, write_scope, created_at";
|
|
29
32
|
export declare const DELETE_USER_ROLE = "DELETE FROM lt_user_roles WHERE user_id = $1 AND role = $2";
|
|
30
33
|
export declare const HAS_ROLE = "SELECT 1 FROM lt_user_roles WHERE user_id = $1 AND role = $2 LIMIT 1";
|
|
31
34
|
export declare const HAS_ROLE_TYPE = "SELECT 1 FROM lt_user_roles WHERE user_id = $1 AND type = $2 LIMIT 1";
|
|
@@ -4,8 +4,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.IS_GROUP_ADMIN = exports.GET_ROLE_SCOPE = exports.HAS_ROLE_TYPE = exports.HAS_ROLE = exports.DELETE_USER_ROLE = exports.UPSERT_USER_ROLE = exports.GET_ROLES_BY_USER_IDS = exports.DELETE_USER_BY_ID = exports.GET_USER_WITH_ROLES = exports.VERIFY_USER_BY_ID = exports.GET_USER_BY_ID = exports.GET_USER_BY_EMAIL = exports.CREATE_USER_WITH_ROLES = exports.GET_USER_BY_EXTERNAL_ID = exports.GET_ROLES_BY_USER_ID = exports.ENSURE_ROLE_EXISTS = void 0;
|
|
5
5
|
/** Ensure a role name exists in the lt_roles lookup table (FK target). */
|
|
6
6
|
exports.ENSURE_ROLE_EXISTS = 'INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING';
|
|
7
|
-
/** Fetch roles for a single user, ordered by creation time.
|
|
8
|
-
|
|
7
|
+
/** Fetch roles for a single user, ordered by creation time. granted_by_persona
|
|
8
|
+
* is the sustaining persona's key (null = direct grant). */
|
|
9
|
+
exports.GET_ROLES_BY_USER_ID = `SELECT r.role, r.type, r.read_scope, r.write_scope, r.created_at, p.key AS granted_by_persona
|
|
10
|
+
FROM lt_user_roles r
|
|
11
|
+
LEFT JOIN lt_personas p ON p.id = r.granted_by_persona
|
|
12
|
+
WHERE r.user_id = $1 ORDER BY r.created_at`;
|
|
9
13
|
/** Fetch a user row by external_id. */
|
|
10
14
|
exports.GET_USER_BY_EXTERNAL_ID = 'SELECT * FROM lt_users WHERE external_id = $1';
|
|
11
15
|
// ─── User CRUD ───────────────────────────────────────────────────────────────
|
|
@@ -48,13 +52,19 @@ exports.GET_USER_WITH_ROLES = `SELECT u.id, u.external_id, u.display_name, u.sta
|
|
|
48
52
|
exports.DELETE_USER_BY_ID = 'DELETE FROM lt_users WHERE id = $1';
|
|
49
53
|
// ─── Role management ─────────────────────────────────────────────────────────
|
|
50
54
|
/** Batch-load roles for many users (avoids N+1). */
|
|
51
|
-
exports.GET_ROLES_BY_USER_IDS =
|
|
52
|
-
|
|
55
|
+
exports.GET_ROLES_BY_USER_IDS = `SELECT r.user_id, r.role, r.type, r.read_scope, r.write_scope, r.created_at, p.key AS granted_by_persona
|
|
56
|
+
FROM lt_user_roles r
|
|
57
|
+
LEFT JOIN lt_personas p ON p.id = r.granted_by_persona
|
|
58
|
+
WHERE r.user_id = ANY($1) ORDER BY r.created_at`;
|
|
59
|
+
/** Upsert a user–role assignment, promoting the type/scope if the row exists.
|
|
60
|
+
* A direct grant takes the row over: granted_by_persona resets to NULL, so a
|
|
61
|
+
* later persona unassign can never remove a membership an admin set by hand. */
|
|
53
62
|
exports.UPSERT_USER_ROLE = `INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope) VALUES ($1, $2, $3, $4, $5)
|
|
54
63
|
ON CONFLICT (user_id, role) DO UPDATE SET
|
|
55
64
|
type = EXCLUDED.type,
|
|
56
65
|
read_scope = EXCLUDED.read_scope,
|
|
57
|
-
write_scope = EXCLUDED.write_scope
|
|
66
|
+
write_scope = EXCLUDED.write_scope,
|
|
67
|
+
granted_by_persona = NULL
|
|
58
68
|
RETURNING role, type, read_scope, write_scope, created_at`;
|
|
59
69
|
exports.DELETE_USER_ROLE = 'DELETE FROM lt_user_roles WHERE user_id = $1 AND role = $2';
|
|
60
70
|
exports.HAS_ROLE = 'SELECT 1 FROM lt_user_roles WHERE user_id = $1 AND role = $2 LIMIT 1';
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* mcp-servers.ts — routes/mcp.ts
|
|
12
12
|
* yaml-workflows.ts — routes/yaml-workflows/
|
|
13
13
|
* users.ts — routes/users.ts + routes/roles.ts
|
|
14
|
+
* personas.ts — routes/personas.ts + persona sub-routes on routes/users.ts
|
|
14
15
|
* maintenance.ts — routes/dba.ts
|
|
15
16
|
* agents.ts — routes/agents.ts
|
|
16
17
|
* agent-subscriptions.ts — routes/agents.ts (subscription sub-routes)
|
|
@@ -16,6 +16,7 @@ exports.stopAdminServer = stopAdminServer;
|
|
|
16
16
|
* mcp-servers.ts — routes/mcp.ts
|
|
17
17
|
* yaml-workflows.ts — routes/yaml-workflows/
|
|
18
18
|
* users.ts — routes/users.ts + routes/roles.ts
|
|
19
|
+
* personas.ts — routes/personas.ts + persona sub-routes on routes/users.ts
|
|
19
20
|
* maintenance.ts — routes/dba.ts
|
|
20
21
|
* agents.ts — routes/agents.ts
|
|
21
22
|
* agent-subscriptions.ts — routes/agents.ts (subscription sub-routes)
|
|
@@ -44,6 +45,7 @@ const workflows_1 = require("./workflows");
|
|
|
44
45
|
const mcp_servers_1 = require("./mcp-servers");
|
|
45
46
|
const yaml_workflows_1 = require("./yaml-workflows");
|
|
46
47
|
const users_1 = require("./users");
|
|
48
|
+
const personas_1 = require("./personas");
|
|
47
49
|
const maintenance_1 = require("./maintenance");
|
|
48
50
|
const agents_1 = require("./agents");
|
|
49
51
|
const agent_subscriptions_1 = require("./agent-subscriptions");
|
|
@@ -56,7 +58,7 @@ const exports_1 = require("./exports");
|
|
|
56
58
|
const overview_1 = require("./overview");
|
|
57
59
|
const diagnostics_1 = require("./diagnostics");
|
|
58
60
|
const ortho_1 = require("./ortho");
|
|
59
|
-
const TOOL_COUNT =
|
|
61
|
+
const TOOL_COUNT = 100;
|
|
60
62
|
let server = null;
|
|
61
63
|
async function createAdminServer(options) {
|
|
62
64
|
if (server && !options?.fresh)
|
|
@@ -73,6 +75,7 @@ async function createAdminServer(options) {
|
|
|
73
75
|
(0, mcp_servers_1.registerMcpServerTools)(instance); // 4 tools
|
|
74
76
|
(0, yaml_workflows_1.registerYamlWorkflowTools)(instance); // 5 tools
|
|
75
77
|
(0, users_1.registerUserTools)(instance); // 10 tools (users + roles + role schemas)
|
|
78
|
+
(0, personas_1.registerPersonaTools)(instance); // 10 tools (persona CRUD + links + assignment)
|
|
76
79
|
(0, maintenance_1.registerMaintenanceTools)(instance); // 1 tool
|
|
77
80
|
(0, agents_1.registerAgentTools)(instance); // 5 tools
|
|
78
81
|
(0, agent_subscriptions_1.registerAgentSubscriptionTools)(instance); // 3 tools
|
|
@@ -85,7 +88,7 @@ async function createAdminServer(options) {
|
|
|
85
88
|
(0, overview_1.registerOverviewTools)(instance); // 1 tool
|
|
86
89
|
(0, diagnostics_1.registerDiagnosticsTools)(instance); // 3 tools
|
|
87
90
|
(0, ortho_1.registerOrthoTools)(instance); // 4 tools
|
|
88
|
-
// Total:
|
|
91
|
+
// Total: 100
|
|
89
92
|
logger_1.loggerRegistry.info(`[lt-mcp:admin] ${name} ready (${TOOL_COUNT} tools registered)`);
|
|
90
93
|
return instance;
|
|
91
94
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persona management tools — mirrors routes/personas.ts plus the persona
|
|
3
|
+
* sub-routes on routes/users.ts. A persona is a named bundle of roles with a
|
|
4
|
+
* per-role relationship scope; assigning one fans out to ordinary scoped role
|
|
5
|
+
* memberships (lt_user_roles stays the single source of authorization truth).
|
|
6
|
+
*/
|
|
7
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
+
export declare function registerPersonaTools(server: McpServer): void;
|