@hotmeshio/long-tail 0.10.9 → 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.
- 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/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/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/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 +91 -0
- package/build/system/mcp-servers/admin/schemas.js +42 -2
- package/build/system/seed/tool-manifests-admin.d.ts +975 -0
- package/build/system/seed/tool-manifests-admin.js +11 -0
- 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/user.d.ts +2 -0
- package/dashboard/dist/assets/{AdminDashboard-DWSYdAeW.js → AdminDashboard-cGYuppUP.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-DWSYdAeW.js.map → AdminDashboard-cGYuppUP.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-Dm6gL76m.js → AgentConfigPage-DwaUyoCy.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-Dm6gL76m.js.map → AgentConfigPage-DwaUyoCy.js.map} +1 -1
- package/dashboard/dist/assets/AgentDetailPage-DxpFaltM.js +4 -0
- package/dashboard/dist/assets/AgentDetailPage-DxpFaltM.js.map +1 -0
- package/dashboard/dist/assets/{AgentsPage-DKaDRk-Y.js → AgentsPage-ByOoA8oT.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-DKaDRk-Y.js.map → AgentsPage-ByOoA8oT.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-B-aHvGKD.js → AvailableEscalationsPage-C1vtTvqP.js} +2 -2
- package/dashboard/dist/assets/{AvailableEscalationsPage-B-aHvGKD.js.map → AvailableEscalationsPage-C1vtTvqP.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-DRv_orzI.js → BotPicker-Dohv8dhR.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-DRv_orzI.js.map → BotPicker-Dohv8dhR.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-DqRVzMol.js → CapabilitiesPage-DeijFG3J.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-DqRVzMol.js.map → CapabilitiesPage-DeijFG3J.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-D0psvw8i.js → CollapsibleSection-BPNwS4eO.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-D0psvw8i.js.map → CollapsibleSection-BPNwS4eO.js.map} +1 -1
- package/dashboard/dist/assets/CountdownTimer-BF71ldGF.js +2 -0
- package/dashboard/dist/assets/CountdownTimer-BF71ldGF.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-Axdci1z-.js → CredentialsPage-oeNmMzTM.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-Axdci1z-.js.map → CredentialsPage-oeNmMzTM.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-BjZ_wZA8.js → CronLabel-CebR1m2k.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-BjZ_wZA8.js.map → CronLabel-CebR1m2k.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-CkUCMUfk.js → CustomDurationPicker-Bh9QdBVw.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-CkUCMUfk.js.map → CustomDurationPicker-Bh9QdBVw.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-_tmC3jEe.js → DropZone-Bavzt2QR.js} +2 -2
- package/dashboard/dist/assets/{DropZone-_tmC3jEe.js.map → DropZone-Bavzt2QR.js.map} +1 -1
- package/dashboard/dist/assets/ElapsedCell-Caa6CU_y.js +2 -0
- package/dashboard/dist/assets/{ElapsedCell-BE_CN5sP.js.map → ElapsedCell-Caa6CU_y.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListSchemaPage-CgcVIiYR.js → EscalationListSchemaPage-CAGfq0eK.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage-CgcVIiYR.js.map → EscalationListSchemaPage-CAGfq0eK.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-BaAn1Hcr.js → EscalationSchemaPage-qDD4ur6P.js} +3 -3
- package/dashboard/dist/assets/{EscalationSchemaPage-BaAn1Hcr.js.map → EscalationSchemaPage-qDD4ur6P.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-Dbq7tKzr.js → EscalationsOverview-CC1wRTzy.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-Dbq7tKzr.js.map → EscalationsOverview-CC1wRTzy.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-0RUK9jqt.js → EventTable-4JeTcXr_.js} +2 -2
- package/dashboard/dist/assets/{EventTable-0RUK9jqt.js.map → EventTable-4JeTcXr_.js.map} +1 -1
- package/dashboard/dist/assets/{FilterBar-B_dy2Jhj.js → FilterBar-BOV8DWIO.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-B_dy2Jhj.js.map → FilterBar-BOV8DWIO.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-CvLYp1nL.js → GraphInvokePage-C0i1speW.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-CvLYp1nL.js.map → GraphInvokePage-C0i1speW.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-B6s1ZeyA.js → HomePage-HPCd8SjF.js} +2 -2
- package/dashboard/dist/assets/{HomePage-B6s1ZeyA.js.map → HomePage-HPCd8SjF.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-DVhlAKZ3.js → ListToolbar-BFHVOba6.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-DVhlAKZ3.js.map → ListToolbar-BFHVOba6.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-Ca8cpc8g.js → McpOverview-Chn2zIXr.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-Ca8cpc8g.js.map → McpOverview-Chn2zIXr.js.map} +1 -1
- package/dashboard/dist/assets/McpQueryDetailPage-Cv1FKrvw.js +5 -0
- package/dashboard/dist/assets/McpQueryDetailPage-Cv1FKrvw.js.map +1 -0
- package/dashboard/dist/assets/McpQueryPage-Dzn8JE4E.js +2 -0
- package/dashboard/dist/assets/McpQueryPage-Dzn8JE4E.js.map +1 -0
- package/dashboard/dist/assets/{McpRunDetailPage-C76mfUtG.js → McpRunDetailPage-CuNtR4Ed.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-C76mfUtG.js.map → McpRunDetailPage-CuNtR4Ed.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-B43BqEyw.js → McpRunsPage-C8iifeKN.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-B43BqEyw.js.map → McpRunsPage-C8iifeKN.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-B5gHotsd.js → NamespacePill-ep9Zmokr.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-B5gHotsd.js.map → NamespacePill-ep9Zmokr.js.map} +1 -1
- package/dashboard/dist/assets/{OperationsPage-DbHzQCuE.js → OperationsPage-flrZ-EJ9.js} +2 -2
- package/dashboard/dist/assets/{OperationsPage-DbHzQCuE.js.map → OperationsPage-flrZ-EJ9.js.map} +1 -1
- package/dashboard/dist/assets/OperatorDashboard-Dhs2eo8p.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-B70nS_Ah.js.map → OperatorDashboard-Dhs2eo8p.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-DrbNMGxm.js → PageHeader-Bw8RNkqR.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-DrbNMGxm.js.map → PageHeader-Bw8RNkqR.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-SbQkaQLG.js → PageHeaderWithStats-NR_4idn6.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-SbQkaQLG.js.map → PageHeaderWithStats-NR_4idn6.js.map} +1 -1
- package/dashboard/dist/assets/PersonaDetailPage-DKILoUqL.js +2 -0
- package/dashboard/dist/assets/PersonaDetailPage-DKILoUqL.js.map +1 -0
- package/dashboard/dist/assets/PersonasPage-qvzHTrFw.js +2 -0
- package/dashboard/dist/assets/PersonasPage-qvzHTrFw.js.map +1 -0
- package/dashboard/dist/assets/ProcessDetailPage-C3LfeKvi.js +2 -0
- package/dashboard/dist/assets/ProcessDetailPage-C3LfeKvi.js.map +1 -0
- package/dashboard/dist/assets/{ProcessesListPage-Di0n8TR4.js → ProcessesListPage-OoZ_u4ty.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-Di0n8TR4.js.map → ProcessesListPage-OoZ_u4ty.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-D70jTLZm.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-D70jTLZm.js.map +1 -0
- package/dashboard/dist/assets/{RolePill-Cz-AAWqA.js → RolePill-TySZq7dB.js} +2 -2
- package/dashboard/dist/assets/{RolePill-Cz-AAWqA.js.map → RolePill-TySZq7dB.js.map} +1 -1
- package/dashboard/dist/assets/RolesPage-CNueMVuJ.js +2 -0
- package/dashboard/dist/assets/RolesPage-CNueMVuJ.js.map +1 -0
- package/dashboard/dist/assets/{RunAsSelector-DFv0VNVZ.js → RunAsSelector-CwWEpiSF.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-DFv0VNVZ.js.map → RunAsSelector-CwWEpiSF.js.map} +1 -1
- package/dashboard/dist/assets/ScopeBadge-C7Uixtmk.js +2 -0
- package/dashboard/dist/assets/ScopeBadge-C7Uixtmk.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-DvIF3kQK.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-D5zxe_l9.js.map → StickyPagination-DvIF3kQK.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-BmBfNSEr.js → StreamMessageDetail-DkFK_XGD.js} +2 -2
- package/dashboard/dist/assets/StreamMessageDetail-DkFK_XGD.js.map +1 -0
- package/dashboard/dist/assets/{SwimlaneTimeline-CA0TKDIi.js → SwimlaneTimeline-CMw-Et6h.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-CA0TKDIi.js.map → SwimlaneTimeline-CMw-Et6h.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-Bf9dZSG6.js → TagInput-D8gSp48D.js} +2 -2
- package/dashboard/dist/assets/{TagInput-Bf9dZSG6.js.map → TagInput-D8gSp48D.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-C15nADUW.js → TaskDetailPage-BhXuChOf.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-C15nADUW.js.map → TaskDetailPage-BhXuChOf.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-DfiBYQy2.js → TaskQueuePill-CHqolBmU.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-DfiBYQy2.js.map → TaskQueuePill-CHqolBmU.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-DQdAayqc.js → TasksListPage-6JHQQ5QZ.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-DQdAayqc.js.map → TasksListPage-6JHQQ5QZ.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-CvslqTB7.js → TimeAgo-CInx-vzY.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-CvslqTB7.js.map → TimeAgo-CInx-vzY.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-BZCQQONl.js → TimestampCell-Fq17Zrsu.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-BZCQQONl.js.map → TimestampCell-Fq17Zrsu.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-gA4sXukc.js → ToolPill-BbgMDhaE.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-gA4sXukc.js.map → ToolPill-BbgMDhaE.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-BgLVc6R5.js → ToolTestPanel-DhjEe59s.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-BgLVc6R5.js.map → ToolTestPanel-DhjEe59s.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-9A0aPb4t.js → TopicDetailPage-DbIdmQpQ.js} +3 -3
- package/dashboard/dist/assets/{TopicDetailPage-9A0aPb4t.js.map → TopicDetailPage-DbIdmQpQ.js.map} +1 -1
- package/dashboard/dist/assets/TopicsPage-CAP-GUbr.js +2 -0
- package/dashboard/dist/assets/{TopicsPage-o0EZxKbR.js.map → TopicsPage-CAP-GUbr.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-DKMLxBJ5.js → UserName-DggcLkSc.js} +2 -2
- package/dashboard/dist/assets/{UserName-DKMLxBJ5.js.map → UserName-DggcLkSc.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-C-snB_dD.js → WorkflowExecutionPage-Bmra5fVt.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-C-snB_dD.js.map → WorkflowExecutionPage-Bmra5fVt.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-Djt2AtZk.js → WorkflowPill-CcUUVugA.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-Djt2AtZk.js.map → WorkflowPill-CcUUVugA.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-BRQmgsLR.js → WorkflowsDashboard-BQsZAGYU.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-BRQmgsLR.js.map → WorkflowsDashboard-BQsZAGYU.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-DmMdIRHQ.js → WorkflowsOverview-CeliraHZ.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-DmMdIRHQ.js.map → WorkflowsOverview-CeliraHZ.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-Bj7QkCry.js → YamlWorkflowDetailPage-DGxlonrV.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-Bj7QkCry.js.map → YamlWorkflowDetailPage-DGxlonrV.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-BIi77mE0.js → YamlWorkflowsPage-DnT8UiyQ.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-BIi77mE0.js.map → YamlWorkflowsPage-DnT8UiyQ.js.map} +1 -1
- package/dashboard/dist/assets/{agents-EBIsjLCr.js → agents-mRV7m4sh.js} +2 -2
- package/dashboard/dist/assets/{agents-EBIsjLCr.js.map → agents-mRV7m4sh.js.map} +1 -1
- package/dashboard/dist/assets/{bots-BXzxybS6.js → bots-SONCZlCM.js} +2 -2
- package/dashboard/dist/assets/{bots-BXzxybS6.js.map → bots-SONCZlCM.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-CnprG6q_.js → capabilities-DZjCMN32.js} +2 -2
- package/dashboard/dist/assets/{capabilities-CnprG6q_.js.map → capabilities-DZjCMN32.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-DiC0xtNq.js → controlplane-RQNXjna2.js} +2 -2
- package/dashboard/dist/assets/{controlplane-DiC0xtNq.js.map → controlplane-RQNXjna2.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-D0aYpJkF.js → escalation-columns-XLEcZRCg.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-D0aYpJkF.js.map → escalation-columns-XLEcZRCg.js.map} +1 -1
- package/dashboard/dist/assets/index-2BwHxjCw.css +1 -0
- package/dashboard/dist/assets/{index-CcMIca5t.js → index-C6GsrvX1.js} +2 -2
- package/dashboard/dist/assets/{index-CcMIca5t.js.map → index-C6GsrvX1.js.map} +1 -1
- package/dashboard/dist/assets/{index-C7KY8GTF.js → index-CK-6m8e_.js} +2 -2
- package/dashboard/dist/assets/{index-C7KY8GTF.js.map → index-CK-6m8e_.js.map} +1 -1
- package/dashboard/dist/assets/index-CkeSxuZ0.js +2 -0
- package/dashboard/dist/assets/{index-CjBdB8Ht.js.map → index-CkeSxuZ0.js.map} +1 -1
- package/dashboard/dist/assets/{index-B6ldRx_s.js → index-CxVGlkK6.js} +2 -2
- package/dashboard/dist/assets/{index-B6ldRx_s.js.map → index-CxVGlkK6.js.map} +1 -1
- package/dashboard/dist/assets/{index-DVmC_UfQ.js → index-D2CfSx_x.js} +4 -4
- package/dashboard/dist/assets/{index-DVmC_UfQ.js.map → index-D2CfSx_x.js.map} +1 -1
- package/dashboard/dist/assets/{index-BHAjQoOy.js → index-DFJF8aWz.js} +3 -3
- package/dashboard/dist/assets/{index-BHAjQoOy.js.map → index-DFJF8aWz.js.map} +1 -1
- package/dashboard/dist/assets/index-DYKRtstT.js +2 -0
- package/dashboard/dist/assets/index-DYKRtstT.js.map +1 -0
- package/dashboard/dist/assets/index-DY_hlZCQ.js +2 -0
- package/dashboard/dist/assets/index-DY_hlZCQ.js.map +1 -0
- package/dashboard/dist/assets/{index-Dht0ihGT.js → index-DbZpL10z.js} +2 -2
- package/dashboard/dist/assets/{index-Dht0ihGT.js.map → index-DbZpL10z.js.map} +1 -1
- package/dashboard/dist/assets/{index-CSTwyrwu.js → index-Dh3CVChT.js} +18 -18
- package/dashboard/dist/assets/index-Dh3CVChT.js.map +1 -0
- package/dashboard/dist/assets/{index-gBnGEubX.js → index-DqsLfO-0.js} +2 -2
- package/dashboard/dist/assets/{index-gBnGEubX.js.map → index-DqsLfO-0.js.map} +1 -1
- package/dashboard/dist/assets/{index-BzRXMicE.js → index-iHV3d7o3.js} +2 -2
- package/dashboard/dist/assets/{index-BzRXMicE.js.map → index-iHV3d7o3.js.map} +1 -1
- package/dashboard/dist/assets/{index-ovFEZ9SV.js → index-ymm58cTW.js} +2 -2
- package/dashboard/dist/assets/{index-ovFEZ9SV.js.map → index-ymm58cTW.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-W5QSKkYQ.js → knowledge-Bawo8u7l.js} +2 -2
- package/dashboard/dist/assets/{knowledge-W5QSKkYQ.js.map → knowledge-Bawo8u7l.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-n877HyOj.js → mcp-CKIpbpRZ.js} +2 -2
- package/dashboard/dist/assets/{mcp-n877HyOj.js.map → mcp-CKIpbpRZ.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-C9kV4sUm.js → mcp-query-vld4hYxQ.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-C9kV4sUm.js.map → mcp-query-vld4hYxQ.js.map} +1 -1
- package/dashboard/dist/assets/personas-BsARc3df.js +2 -0
- package/dashboard/dist/assets/personas-BsARc3df.js.map +1 -0
- package/dashboard/dist/assets/{pipelines-CBR68T8R.js → pipelines-BTSL-5ko.js} +2 -2
- package/dashboard/dist/assets/{pipelines-CBR68T8R.js.map → pipelines-BTSL-5ko.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-DHpWqKMk.js → tasks-DggQdNbj.js} +2 -2
- package/dashboard/dist/assets/{tasks-DHpWqKMk.js.map → tasks-DggQdNbj.js.map} +1 -1
- package/dashboard/dist/assets/{topics-CaLtyqSh.js → topics-DzAKa34X.js} +2 -2
- package/dashboard/dist/assets/{topics-CaLtyqSh.js.map → topics-DzAKa34X.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-B8p56JlB.js → useEventHooks-5uI9oI5W.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-B8p56JlB.js.map → useEventHooks-5uI9oI5W.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-ej71FCvu.js → useNamespace-CEKzFIqe.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-ej71FCvu.js.map → useNamespace-CEKzFIqe.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-DgpiFXib.js → useYamlActivityEvents-hHgX1Z50.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-DgpiFXib.js.map → useYamlActivityEvents-hHgX1Z50.js.map} +1 -1
- package/dashboard/dist/assets/{users-CHnjI_Ca.js → users-AO8lUyNr.js} +2 -2
- package/dashboard/dist/assets/{users-CHnjI_Ca.js.map → users-AO8lUyNr.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-Bg6eJX4Z.js → vendor-icons-DN9yGSuM.js} +141 -131
- package/dashboard/dist/assets/vendor-icons-DN9yGSuM.js.map +1 -0
- package/dashboard/dist/assets/{workflows-DtnXUL0U.js → workflows-PtnlRK09.js} +2 -2
- package/dashboard/dist/assets/{workflows-DtnXUL0U.js.map → workflows-PtnlRK09.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-DDclYAID.js → yaml-workflows-DPu3Y8vG.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-DDclYAID.js.map → yaml-workflows-DPu3Y8vG.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/api/http/personas.md +160 -0
- package/docs/cli.md +12 -0
- package/docs/dashboard.md +16 -1
- package/docs/design-principles.md +9 -5
- package/docs/hitl/x-lt-list-schema.md +2 -3
- package/docs/iam.md +47 -0
- 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/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/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/OperatorDashboard-B70nS_Ah.js +0 -2
- 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/TopicsPage-o0EZxKbR.js +0 -2
- 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/vendor-icons-Bg6eJX4Z.js.map +0 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Personas API
|
|
2
|
+
|
|
3
|
+
A persona is a named bundle of roles with a per-role **relationship** scope — shorthand for adding a user to each linked role at the linked scope. Assignment fans out to ordinary role memberships (`lt_user_roles` stays the single source of authorization truth); the persona records why the memberships exist. Full semantics — provenance, highest-allowance union, overlay-on-reassign — are in [iam.md](../../iam.md#personas).
|
|
4
|
+
|
|
5
|
+
All endpoints require authentication plus role-management access (`admin` type, `superadmin`, or the `engineer` role) — the same audience that manages users and roles.
|
|
6
|
+
|
|
7
|
+
Relationship values map onto the member scope lattice:
|
|
8
|
+
|
|
9
|
+
| relationship | read_scope | write_scope |
|
|
10
|
+
|--------------|-----------|-------------|
|
|
11
|
+
| `write-all` | `all` | `all` |
|
|
12
|
+
| `write-self` | `all` | `self` |
|
|
13
|
+
| `read-all` | `all` | `none` |
|
|
14
|
+
|
|
15
|
+
`write-none` is accepted anywhere as a synonym for `read-all`.
|
|
16
|
+
|
|
17
|
+
## List personas
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
GET /api/personas
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Response 200:**
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"personas": [
|
|
28
|
+
{
|
|
29
|
+
"id": "a1b2c3d4-...",
|
|
30
|
+
"key": "production-manager",
|
|
31
|
+
"title": "Production Manager",
|
|
32
|
+
"description": "Runs the pipeline: works design and review, watches print and the fleet.",
|
|
33
|
+
"roles": [
|
|
34
|
+
{ "role": "design", "relationship": "write-all" },
|
|
35
|
+
{ "role": "review", "relationship": "write-all" },
|
|
36
|
+
{ "role": "print", "relationship": "read-all" }
|
|
37
|
+
],
|
|
38
|
+
"user_count": 2,
|
|
39
|
+
"created_at": "2026-07-01T08:00:00.000Z",
|
|
40
|
+
"updated_at": "2026-07-01T08:00:00.000Z"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Get a persona
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
GET /api/personas/:key
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Response 200:** the persona record plus `assignees` — `[{ id, external_id, display_name, email, assigned_at }]`. **404** when the key is unknown.
|
|
53
|
+
|
|
54
|
+
## Create a persona
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
POST /api/personas
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Body:**
|
|
61
|
+
|
|
62
|
+
| Field | Type | Description |
|
|
63
|
+
|-------|------|-------------|
|
|
64
|
+
| `key` | `string` | Required. Stable identifier, role-name alphabet (`^[a-z][a-z0-9_-]*$`) |
|
|
65
|
+
| `title` | `string` | Display title |
|
|
66
|
+
| `description` | `string` | The day-in-the-life, one paragraph |
|
|
67
|
+
|
|
68
|
+
**Response 201** with the created record. **409** when the key already exists.
|
|
69
|
+
|
|
70
|
+
## Update a persona
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
PATCH /api/personas/:key
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
PATCH semantics on `title` and `description` — omitted fields keep their values; `null` clears.
|
|
77
|
+
|
|
78
|
+
## Delete a persona
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
DELETE /api/personas/:key
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Memberships the persona sustains are removed — or re-homed to a sibling persona the user still holds — before the persona row is deleted. Direct grants are never touched.
|
|
85
|
+
|
|
86
|
+
**Response 200:** `{ "deleted": true, "recompute": { "granted": 0, "refreshed": 0, "raised": 0, "removed": 3 } }`
|
|
87
|
+
|
|
88
|
+
## Link a role
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
PUT /api/personas/:key/roles/:role
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Body:** `{ "relationship": "write-all" }` — also updates an existing link's relationship. The role is created if absent, and every current holder's memberships are reconciled in the same transaction.
|
|
95
|
+
|
|
96
|
+
**Response 201:** `{ "role": "design", "relationship": "write-all", "recompute": { ... } }`
|
|
97
|
+
|
|
98
|
+
## Unlink a role
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
DELETE /api/personas/:key/roles/:role
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Removes the link and reconciles every holder. **Response 200:** `{ "unlinked": true, "recompute": { ... } }`
|
|
105
|
+
|
|
106
|
+
## Seed personas (declarative)
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
POST /api/personas/seed
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Body:** `{ "personas": [{ "key", "title?", "description?", "roles": [{ "role", "relationship" }] }] }`
|
|
113
|
+
|
|
114
|
+
Idempotent and authoritative per spec: title/description are overlaid, role links are synced (links absent from the spec are pruned), linked roles are ensured as FK targets, and every current holder is reconciled. The SDK twin is `lt.personas.seed(specs)` — declare personas in the same seed pass that declares roles and `default_pins`.
|
|
115
|
+
|
|
116
|
+
**Response 200:** `{ "personas": 2, "links": 6, "recompute": { ... } }`
|
|
117
|
+
|
|
118
|
+
## Personas for a user
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
GET /api/users/:id/personas
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Response 200:**
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"personas": [
|
|
129
|
+
{ "id": "a1b2c3d4-...", "key": "production-manager", "title": "Production Manager", "description": "...", "roles": [ ... ], "assigned_at": "2026-07-10T12:00:00.000Z" }
|
|
130
|
+
],
|
|
131
|
+
"roles": [
|
|
132
|
+
{ "role": "design", "read_scope": "all", "write_scope": "all", "granted_by_persona": "production-manager" },
|
|
133
|
+
{ "role": "review", "read_scope": "all", "write_scope": "all", "granted_by_persona": null }
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`roles` is the composed role/scope map: every membership with the persona sustaining it (`null` = direct grant).
|
|
139
|
+
|
|
140
|
+
## Assign a persona
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
POST /api/users/:id/personas
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Body:** `{ "persona": "production-manager" }`
|
|
147
|
+
|
|
148
|
+
Idempotent — re-assigning overlays fresh from the persona's current role links. Highest allowance wins across held personas; a direct grant's scope is only ever raised toward the union, never lowered.
|
|
149
|
+
|
|
150
|
+
**Response 200:** `{ "assigned": true, "recompute": { "granted": 3, "refreshed": 0, "raised": 1, "removed": 0 } }` — `granted` = memberships inserted, `refreshed` = persona-sustained rows overlaid, `raised` = direct rows lifted to the union, `removed` = sustained rows no held persona still grants.
|
|
151
|
+
|
|
152
|
+
## Unassign a persona
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
DELETE /api/users/:id/personas/:key
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Removes only memberships the persona sustains; rows another held persona still grants are re-homed to it. Direct grants — including a membership added directly on top of a persona grant — are never touched.
|
|
159
|
+
|
|
160
|
+
**Response 200:** `{ "unassigned": true, "recompute": { ... } }`. **404** when the user does not hold the persona.
|
package/docs/cli.md
CHANGED
|
@@ -140,6 +140,18 @@ ltc roles save-schema reviewer --file schema.json --summary "Added lotNumber"
|
|
|
140
140
|
# Save the escalation form schema (a change creates the next version)
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
+
### Personas
|
|
144
|
+
|
|
145
|
+
Personas bundle roles into one-step assignments — each link carries a relationship scope (`write-all`, `write-self`, `read-all`). See [iam.md](iam.md#personas) for the semantics.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
ltc personas list # Personas with role and holder counts
|
|
149
|
+
ltc personas get production-manager # Role links + assignees
|
|
150
|
+
ltc personas assign production-manager USER_ID
|
|
151
|
+
ltc personas unassign production-manager USER_ID
|
|
152
|
+
ltc personas for-user USER_ID # Personas held + composed role/scope map
|
|
153
|
+
```
|
|
154
|
+
|
|
143
155
|
### Compiler
|
|
144
156
|
|
|
145
157
|
```bash
|
package/docs/dashboard.md
CHANGED
|
@@ -57,6 +57,7 @@ The LLM authoring add-on. Appears when an Anthropic key is configured.
|
|
|
57
57
|
|------|-------|---------|
|
|
58
58
|
| **Accounts** | `/admin/users` | User accounts and service accounts (bots). Create, edit, assign roles, manage API keys. |
|
|
59
59
|
| **Roles** | `/admin/roles` | Define roles — the queues, forms, and membership that connect workflows to people. Escalation chains, capacity settings, and versioned schemas live here. |
|
|
60
|
+
| **Personas** | `/admin/personas` | Bundle roles into named, one-step assignments. Each linked role carries a relationship scope (write-all, write-self, read-all); assigning a persona composes the member's whole surface from its roles' pins and schemas. |
|
|
60
61
|
|
|
61
62
|
### Infrastructure
|
|
62
63
|
|
|
@@ -210,8 +211,9 @@ User Accounts and Service Accounts live on the same page, separated by a tab tog
|
|
|
210
211
|
- **User Accounts** — human operators. Create users, assign display names, and grant roles. Roles determine which escalations a user can see and claim, and which workflows they can invoke from the dashboard. A `member` grant carries a work-surface scope (read/write breadth) chosen from the Scope picker; the five named profiles are documented under [Role Detail](#role-detail).
|
|
211
212
|
- **Service Accounts** — programmatic callers (bots, CI pipelines, external systems). Each service account has an API key for authentication. Assign roles to control access just like human users. Service accounts with the `reviewer` role can claim and resolve escalations programmatically.
|
|
212
213
|
- **Role assignment** — both account types participate in the same role system. Click any account to edit roles, change display name, or manage credentials.
|
|
214
|
+
- **Personas** — the role panel also assigns [personas](#personas): pick one to add the account to every linked role at the linked scope in one step. Memberships a persona sustains show a mask icon; direct grants show none and survive persona unassignment.
|
|
213
215
|
|
|
214
|
-
**API:** `GET /api/users` lists accounts. `POST /api/users` creates. `PUT /api/users/:id/roles` assigns roles.
|
|
216
|
+
**API:** `GET /api/users` lists accounts. `POST /api/users` creates. `PUT /api/users/:id/roles` assigns roles. `GET/POST /api/users/:id/personas` reads and assigns personas.
|
|
215
217
|
|
|
216
218
|
### Roles
|
|
217
219
|
|
|
@@ -276,6 +278,19 @@ Accessible at `/admin/roles/:role/list-schema`. The versioned rich view for the
|
|
|
276
278
|
|
|
277
279
|
**API:** `GET /api/roles/:role/list-schema` (latest or `?version=`), `GET /api/roles/:role/list-schema/versions`, and `PATCH /api/roles/:role` with `list_schema` (+ optional `change_summary`).
|
|
278
280
|
|
|
281
|
+
### Personas
|
|
282
|
+
|
|
283
|
+
Accessible at `/admin/personas` for admins, superadmins, and engineers — the same audience that manages [Accounts](#accounts) and [Roles](#roles). A persona is a named bundle of roles with a per-role relationship scope: assigning it adds a user to every linked role at the linked scope in one step, and each role's own pins and schemas compose the member's sidebar and forms — the composition ("this human runs the floor, which means these roles") is a first-class, auditable record. Full semantics — provenance, highest-allowance union, overlay-on-reassign — are in [iam.md](iam.md#personas).
|
|
284
|
+
|
|
285
|
+
**Master list** — the working surface. One row per persona: display title, key, description, a preview of the linked role pills, and the holder count; search matches all of them. Select a row to target the **Assignees** panel on the right — assign a user there and they join every linked role in one step, sidebar pins and forms composing from the roles. Unassign from the same panel, or from the account's role panel on [Accounts](#accounts). The row's pencil opens configuration.
|
|
286
|
+
|
|
287
|
+
**Configuration** (`/admin/personas/:key`) —
|
|
288
|
+
|
|
289
|
+
- **Identity** — title and description, PATCH-saved.
|
|
290
|
+
- **Roles** — the bundle's rules. Each link shows the role pill, its scope badge, and a relationship picker (`Write · act on all`, `Write · act on self`, `Read-only observer`); changing a relationship or unlinking reconciles every holder immediately. Link new roles from the role picker.
|
|
291
|
+
|
|
292
|
+
**API:** `GET /api/personas` lists. `POST /api/personas` creates; `PATCH`/`DELETE /api/personas/:key` edit and remove. `PUT`/`DELETE /api/personas/:key/roles/:role` manage links. `POST /api/personas/seed` applies a declarative spec set idempotently.
|
|
293
|
+
|
|
279
294
|
### DB Maintenance
|
|
280
295
|
|
|
281
296
|
Database housekeeping tools for keeping PostgreSQL healthy under sustained workflow load.
|
|
@@ -207,11 +207,15 @@ Nothing loops, nothing floats.
|
|
|
207
207
|
owner, urgency, age — always renders. Enrichment columns (workflow,
|
|
208
208
|
metadata facets) return only when the table's container has room
|
|
209
209
|
(`showFrom: '@split' | '@wall'`); below that they fold into card pairs.
|
|
210
|
-
The list is a jumping-off spot: the
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
210
|
+
The list is a jumping-off spot: the ELK-style drill the whole product
|
|
211
|
+
surfaces.
|
|
212
|
+
6. **Refining is a ROW gesture, never a cell tax.** Cells spend their full
|
|
213
|
+
width on data. One always-visible magnifier per row (or board card)
|
|
214
|
+
opens the refine dialog: the row's metadata-bound facts as full-width
|
|
215
|
+
touch rows, multi-select ANDing facets, with filter-in-role /
|
|
216
|
+
search-everywhere / add-to-filters actions. Per-cell icon pairs are a
|
|
217
|
+
defect — twelve columns × two icons starves every value into an
|
|
218
|
+
ellipsis. Hover-only affordances do not exist on the floor.
|
|
215
219
|
7. **Disclosure order**: drop priority-3 columns, fold priority-2 into
|
|
216
220
|
pairs, keep priority-1 always.
|
|
217
221
|
8. **Touch parity**: every hover-only affordance has a tap equivalent —
|
|
@@ -99,7 +99,7 @@ The table renders through the same engine as the platform's built-in tables: pad
|
|
|
99
99
|
|
|
100
100
|
The seeded `print` role (ortho pipeline) is the reference example for this layout, including fold priorities.
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
Cells carry data only — the full column width belongs to the value, with the full text on hover. Refining is a **row** gesture: every row with at least one pure metadata binding (`{{metadata.key}}`) carries a magnifier that opens the refine dialog — the row's metadata-bound facts as touch-sized rows, each a toggle. Pick one or several (selections AND into one query) and drill: **Filter <role>** narrows this role's queue, **Search everywhere** spans every role, **Add to filters** merges the selection into the live filter set. A row with a single fact arrives preselected — two taps to drill.
|
|
103
103
|
|
|
104
104
|
### `"facet-board"` — entity board
|
|
105
105
|
|
|
@@ -127,7 +127,6 @@ Cards are fully interactive (`x-lt-group-by` should be a `metadata.*` path so th
|
|
|
127
127
|
|
|
128
128
|
- **Click** opens the group's latest row in the detail view.
|
|
129
129
|
- **⇧ click** adds the entity's facet to the live filter set — additive, so shift-clicking narrows the board in place and the facet chips row shows each condition with its own remove.
|
|
130
|
-
- **
|
|
131
|
-
- **Fields bound to a pure `{{metadata.KEY}}` token** carry the same hover pair the table's metadata cells offer: **filter** (narrows within the current role; ⇧ click adds to the current filters) and **search** (the facet across all roles).
|
|
130
|
+
- **The header magnifier** opens the card's refine dialog — the entity identity plus every field bound to a pure `{{metadata.KEY}}` token, as touch-sized toggle rows. Selections AND into one query: **Filter <role>**, **Search everywhere**, or **Add to filters** (merge into the live set). The same row gesture the facet table carries, always visible — no hover required.
|
|
132
131
|
|
|
133
132
|
The reference: `examples/seed-fleet-sim.ts` — one advert per machine, a `format: "age"` "Since" field, and role default pins (see [pinned-views.md](pinned-views.md)).
|
package/docs/iam.md
CHANGED
|
@@ -41,6 +41,53 @@ Scope makes limited-surface users first-class. A workflow assigns an escalation
|
|
|
41
41
|
|
|
42
42
|
See the [Roles API](api/http/roles.md#work-surface-scope) for the assignment contract and [roles.md](hitl/roles.md#role-based-routing) in the HITL guide for the workflow-side flow.
|
|
43
43
|
|
|
44
|
+
## Personas
|
|
45
|
+
|
|
46
|
+
A persona is a named bundle of roles with a per-role **relationship** scope — shorthand for "add this user to these roles with these scoped privileges." Assigning a persona performs the equivalent of adding the user to each linked role at the linked scope; because each role carries its own pins, list schema, and form schema, one assignment composes the member's entire dashboard surface. Personas are a suggestion, the same way a `default_pins` entry is: admins can always hand-tune memberships per user through the existing users/roles surfaces, and the persona is the repeatable 95% path.
|
|
47
|
+
|
|
48
|
+
The relationship names a point on the member scope lattice (a persona grant is always `type=member`):
|
|
49
|
+
|
|
50
|
+
| relationship | read_scope | write_scope | Grants |
|
|
51
|
+
|--------------|-----------|-------------|--------|
|
|
52
|
+
| `write-all` | `all` | `all` | Full worker — claim, resolve, submit forms |
|
|
53
|
+
| `write-self` | `all` | `self` | Sees the whole queue, acts only on own assignments |
|
|
54
|
+
| `read-all` | `all` | `none` | Observer — sees the pond, the pins, the counts; cannot act |
|
|
55
|
+
|
|
56
|
+
`write-none` is accepted everywhere as a synonym for `read-all`.
|
|
57
|
+
|
|
58
|
+
### Membership stays the source of truth
|
|
59
|
+
|
|
60
|
+
Personas are not a second authorization path. Assignment fans out to ordinary `lt_user_roles` rows; every RBAC query reads memberships exactly as before. The persona record explains *why* the memberships exist:
|
|
61
|
+
|
|
62
|
+
- **Provenance.** Each membership carries `granted_by_persona` — the persona sustaining it, or null for a direct grant. Unassigning a persona removes only the rows it sustains. A row a sibling persona still grants is re-homed to that persona instead of being removed. A direct role-add on a persona-sustained row takes the row over (provenance flips to direct), so a later unassign never removes a membership an admin set by hand.
|
|
63
|
+
- **Highest allowance wins.** A user holding several personas (or a direct grant) gets the union per role — the membership row stores the highest scope any grantor supplies. Direct grants are only ever raised toward the persona union, never lowered.
|
|
64
|
+
- **Overlay on re-assign.** Assignment is idempotent: re-assigning a persona reconciles the user's memberships against the persona's *current* role links, so "apply and reapply" is one call. Editing a persona's links (or its link relationships) reconciles every current holder in the same transaction.
|
|
65
|
+
- **Deletion is exact.** Deleting a persona removes the memberships it sustains (re-homing rows a sibling persona still grants) before the persona row goes away; direct grants are untouched.
|
|
66
|
+
|
|
67
|
+
### Declaring personas
|
|
68
|
+
|
|
69
|
+
Personas are managed three equivalent ways — dashboard (**Admin → Personas**, visible to admins, superadmins, and engineers, the same audience that manages roles and users), API/SDK, and a declarative seed pass that lives in git beside the role declarations:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
await lt.personas.seed([
|
|
73
|
+
{
|
|
74
|
+
key: 'production-manager',
|
|
75
|
+
title: 'Production Manager',
|
|
76
|
+
description: 'Runs the pipeline: works design and review, watches print and the fleet.',
|
|
77
|
+
roles: [
|
|
78
|
+
{ role: 'design', relationship: 'write-all' },
|
|
79
|
+
{ role: 'review', relationship: 'write-all' },
|
|
80
|
+
{ role: 'print', relationship: 'read-all' },
|
|
81
|
+
{ role: 'fleet-servicer', relationship: 'read-all' },
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
]);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The seed is idempotent and authoritative per spec: title/description are overlaid, role links are synced (links absent from the spec are pruned), linked roles are ensured as FK targets, and every current holder is reconciled — re-running the seed after editing a spec re-applies it to everyone holding the persona.
|
|
88
|
+
|
|
89
|
+
HTTP surface: `GET/POST /api/personas`, `GET/PATCH/DELETE /api/personas/:key`, `PUT/DELETE /api/personas/:key/roles/:role`, `POST /api/personas/seed`, and per-user `GET/POST /api/users/:id/personas` plus `DELETE /api/users/:id/personas/:key`. `GET /api/users/:id/personas` returns the personas held and the composed role/scope map (each row naming its sustaining persona, or null for a direct grant) — the profile-page view. The same operations exist as `lt.personas.*` in the SDK, `ltc personas` in the CLI, and `*_persona` tools on the `long-tail-admin` MCP server.
|
|
90
|
+
|
|
44
91
|
### Enforcement
|
|
45
92
|
|
|
46
93
|
Scope folds into the SQL, so it is atomic with no TOCTOU window. Read scope becomes part of the escalation search query — a member sees a row when `role ∈ allRoles OR (role ∈ selfRoles AND assigned_to = me)`. Write scope folds into the atomic resolve-by-metadata query and gates the by-id claim/resolve/cancel paths. `assigned_to` is indexed, so `self` scope scales to large queues.
|
package/docs/sdk.md
CHANGED
|
@@ -68,6 +68,7 @@ The client mirrors the REST route structure:
|
|
|
68
68
|
| `lt.yamlWorkflows` | `/api/yaml-workflows` | `list`, `get`, `create`, `deploy`, `activate`, `invoke`, `archive` |
|
|
69
69
|
| `lt.users` | `/api/users` | `list`, `get`, `create`, `update`, `delete`, `getRoles`, `addRole`, `removeRole` |
|
|
70
70
|
| `lt.roles` | `/api/roles` | `list`, `listWithDetails`, `create`, `delete`, `getEscalationChains` |
|
|
71
|
+
| `lt.personas` | `/api/personas` | `list`, `get`, `create`, `update`, `delete`, `linkRole`, `unlinkRole`, `assign`, `unassign`, `forUser`, `seed` |
|
|
71
72
|
| `lt.auth` | `/api/auth` | `login` |
|
|
72
73
|
| `lt.mcp` | `/api/mcp` | `listServers`, `createServer`, `getServer`, `connectServer`, `listTools`, `callTool` |
|
|
73
74
|
| `lt.pipelines` | `/api/pipelines` | `listEntities`, `listJobs`, `getExecution`, `interrupt` |
|
package/package.json
CHANGED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{j as e,a as b}from"./vendor-query-B2UbickB.js";import{a as R,b as K,c as M}from"./agents-EBIsjLCr.js";import{u as U,j as I,c as O,N as V}from"./index-CSTwyrwu.js";import{u as F,E as _,C as E}from"./CronLabel-BjZ_wZA8.js";import{j as H}from"./useEventHooks-B8p56JlB.js";import{W as y}from"./WorkflowPill-Djt2AtZk.js";import{c as z,B as G,a0 as J,g as X,b1 as Y,T as Q,b2 as Z,R as A,aj as ee,l as se,b3 as te}from"./vendor-icons-Bg6eJX4Z.js";import{e as ae,a as P}from"./vendor-react-BpdAFEQc.js";function ne(a){return new Date(a).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"})}function u({icon:a,color:n,children:i,actions:o}){return e.jsxs("div",{className:"flex items-center justify-between mb-3 pb-2 border-b border-surface-border",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(a,{className:`w-4 h-4 ${n}`,strokeWidth:1.5}),e.jsx("h2",{className:"section-h2",children:i})]}),o]})}function g({text:a}){return e.jsx("p",{className:"text-2xs text-text-quaternary py-2",children:a})}function j({to:a,label:n}){const i=P();return e.jsx("button",{onClick:()=>i(a),className:"text-text-quaternary hover:text-accent transition-colors",title:`View ${n}`,"aria-label":`View ${n}`,children:e.jsx(te,{className:"w-3.5 h-3.5",strokeWidth:1.5})})}let ie=0;function re(a){const[n,i]=b.useState([]),[o,v]=b.useState(!1),x=b.useCallback(s=>{var d,m,p;if((d=s.type)!=null&&d.startsWith("mesh."))return;const f=s.activityName||((m=s.data)==null?void 0:m.domain)||((p=s.workflowId)==null?void 0:p.slice(0,16))||"";i(h=>[{id:++ie,type:s.type,timestamp:s.timestamp,label:f},...h].slice(0,10)),v(!0),setTimeout(()=>v(!1),2e3)},[]);return O(a?`${V}.system.agent.${a}.>`:"",x),{events:n,pulse:o}}function he(){var w,k,$,S,C;const{id:a}=ae(),n=P(),{data:i}=U(),o=I(),x=(o!==null?o:!!((w=i==null?void 0:i.ai)!=null&&w.enabled))?"Agent":"Automation",{data:s,isLoading:f}=R(a??null),{data:d}=F(a??null),m=K(),p=M(),{events:h,pulse:q}=re(a);if(H(),f)return e.jsxs("div",{className:"animate-pulse space-y-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-48"}),e.jsx("div",{className:"h-40 bg-surface-sunken rounded"})]});if(!s)return e.jsxs("div",{className:"flex flex-col items-center justify-center py-20 text-center",children:[e.jsx(z,{className:"w-12 h-12 text-text-quaternary mb-4",strokeWidth:1}),e.jsxs("h2",{className:"heading-3 mb-2",children:[x," not found"]}),e.jsxs("button",{onClick:()=>n("/agents"),className:"text-sm text-accent hover:text-accent-hover transition-colors",children:["Back to ",x.toLowerCase(),"s"]})]});const T=s.status==="active"?"bg-status-success":s.status==="paused"?"bg-status-warning":s.status==="error"?"bg-status-error":"bg-surface-border",r=(d==null?void 0:d.subscriptions)??[],B=s.status==="active"?"Active":s.status==="paused"?"Paused":s.status==="error"?"Error":"Inactive",c=(k=s.behaviors)==null?void 0:k.schedules,N=($=s.behaviors)==null?void 0:$.cron,l=(c==null?void 0:c.length)||(N?1:0),D=()=>{const t=[`Pause "${s.id}"?`,"","This will:",r.length>0?`• Stop ${r.length} event subscription${r.length!==1?"s":""}`:null,l>0?`• Stop ${l} schedule${l!==1?"s":""}`:null,"","Knowledge and workflow history are preserved. You can reactivate anytime."].filter(Boolean).join(`
|
|
2
|
-
`);confirm(t)&&m.mutate({id:s.id,status:"paused"})},L=()=>{const t=[`Delete "${s.id}"?`,"","This permanently removes:","• The agent configuration",r.length>0?`• ${r.length} event subscription${r.length!==1?"s":""}`:null,l>0?`• ${l} schedule${l!==1?"s":""}`:null,"","Knowledge entries and workflow history are preserved."].filter(Boolean).join(`
|
|
3
|
-
`);confirm(t)&&p.mutate(s.id,{onSuccess:()=>n("/agents")})};return e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-start justify-between mb-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("h1",{className:"heading-1",children:[x,": ",s.id]}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:agents.md"},className:"text-text-quaternary hover:text-accent transition-colors mt-1",title:"Docs",children:e.jsx(G,{className:"w-4 h-4",strokeWidth:1.5})})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("div",{className:"flex items-center gap-1.5 mr-2",children:[e.jsx("span",{className:`w-2 h-2 rounded-full dot-ring ${T}`}),e.jsx("span",{className:"text-xs text-text-secondary capitalize",children:B})]}),e.jsxs("button",{onClick:()=>n(`/agents/${s.id}/edit`),className:"flex items-center gap-1.5 px-4 py-1.5 text-xs rounded-md bg-accent text-text-inverse hover:bg-accent-hover transition-colors",children:[e.jsx(J,{className:"w-3 h-3"})," Edit"]}),s.status!=="active"&&e.jsxs("button",{onClick:()=>m.mutate({id:s.id,status:"active"}),className:"flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md text-status-success hover:bg-status-success/10 transition-colors",children:[e.jsx(X,{className:"w-3 h-3"})," Activate"]}),s.status==="active"&&e.jsxs("button",{onClick:D,className:"flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md text-text-tertiary hover:text-text-primary hover:bg-surface-hover transition-colors",children:[e.jsx(Y,{className:"w-3 h-3"})," Pause"]}),e.jsxs("button",{onClick:L,className:"flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md text-status-error/60 hover:text-status-error hover:bg-status-error/10 transition-colors",children:[e.jsx(Q,{className:"w-3 h-3"})," Delete"]})]})]}),s.description&&e.jsx("div",{className:"border-l-2 border-accent/30 pl-4 py-1 mb-10",children:e.jsx("p",{className:"text-sm text-text-secondary italic leading-relaxed",children:s.description})}),(s.goals||s.rules)&&e.jsxs("div",{className:"mb-10",children:[e.jsx(u,{icon:Z,color:"text-accent",actions:e.jsx(j,{to:`/agents/${s.id}/edit?step=2`,label:"Motivation"}),children:"Motivation"}),e.jsxs("div",{className:"grid grid-cols-2 gap-x-14 bg-surface-sunken/20 rounded-lg px-5 py-4",children:[s.goals&&e.jsxs("div",{children:[e.jsx("p",{className:"text-2xs text-text-quaternary uppercase tracking-widest mb-1.5",children:"Goals"}),e.jsx("p",{className:"text-sm text-text-primary leading-relaxed",children:s.goals})]}),s.rules&&e.jsxs("div",{children:[e.jsx("p",{className:"text-2xs text-text-quaternary uppercase tracking-widest mb-1.5",children:"Rules"}),e.jsx("p",{className:"text-sm text-text-primary leading-relaxed",children:s.rules})]})]})]}),e.jsxs("div",{className:"grid grid-cols-1 @wall:grid-cols-3 gap-x-14 gap-y-10",children:[e.jsxs("div",{children:[e.jsxs(u,{icon:A,color:"text-accent",actions:e.jsx(j,{to:`/agents/${s.id}/edit?step=4`,label:"Subscriptions"}),children:["Subscriptions (",r.length,")"]}),r.length===0?e.jsx(g,{text:"No event subscriptions"}):e.jsx("div",{className:"divide-y divide-surface-border/30",children:r.map(t=>e.jsxs("div",{className:"flex items-center py-2",children:[e.jsx("div",{className:"flex-1 min-w-0",children:e.jsx(_,{topic:t.topic})}),e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[e.jsx("span",{className:"text-text-quaternary text-2xs",children:"→"}),e.jsx(y,{type:t.reaction_type==="capability"?t.tool_name:t.workflow_type||t.reaction_type,variant:t.reaction_type==="pipeline"?"pipeline":t.reaction_type==="capability"?"capability":"durable"})]})]},t.id))})]}),e.jsxs("div",{children:[e.jsxs(u,{icon:ee,color:"text-accent",actions:e.jsx(j,{to:`/agents/${s.id}/edit?step=5`,label:"Schedules"}),children:["Schedules (",l,")"]}),c!=null&&c.length?e.jsx("div",{className:"divide-y divide-surface-border/30",children:c.map((t,W)=>e.jsxs("div",{className:"flex items-center py-2",children:[e.jsx("div",{className:"flex-1 min-w-0",children:e.jsx(E,{cron:t.cron})}),e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[e.jsx("span",{className:"text-text-quaternary text-2xs",children:"→"}),e.jsx(y,{type:t.workflow_type||"workflow"})]})]},W))}):N?e.jsxs("div",{className:"flex items-center py-1",children:[e.jsx("div",{className:"flex-1 min-w-0",children:e.jsx(E,{cron:N})}),s.workflow_type&&e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[e.jsx("span",{className:"text-text-quaternary text-2xs",children:"→"}),e.jsx(y,{type:s.workflow_type})]})]}):e.jsx(g,{text:"No schedules configured"})]}),e.jsxs("div",{className:"space-y-10",children:[e.jsxs("div",{children:[e.jsx(u,{icon:se,color:"text-accent",actions:e.jsx(j,{to:s.knowledge_domain?`/knowledge?domain=${s.knowledge_domain}`:`/agents/${s.id}/edit?step=3`,label:"Knowledge"}),children:"Knowledge"}),s.knowledge_domain?e.jsxs("button",{onClick:()=>n(`/knowledge?domain=${s.knowledge_domain}`),className:"group text-left flex items-center gap-3",children:[e.jsx("span",{className:"text-sm font-mono text-text-primary group-hover:text-accent transition-colors",children:s.knowledge_domain}),e.jsxs("span",{className:"text-2xs text-text-quaternary",children:[((C=(S=s.stats)==null?void 0:S.knowledge_count)==null?void 0:C.toLocaleString())??0," entries"]})]}):e.jsx(g,{text:"No knowledge domain"})]}),e.jsxs("div",{children:[e.jsx(u,{icon:A,color:"text-accent",actions:e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("span",{className:`w-1.5 h-1.5 rounded-full dot-ring transition-colors duration-500 ${q?"bg-status-success":"bg-surface-border"}`}),e.jsx("span",{className:"text-2xs text-text-quaternary",children:"live"})]}),children:"Activity"}),h.length===0?e.jsx(g,{text:"Events appear here as the agent runs"}):e.jsx("div",{className:"space-y-1.5",children:h.map(t=>e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-0.5",children:[e.jsx("span",{className:"text-2xs text-text-primary font-mono truncate flex-1",children:t.label||"—"}),e.jsx("span",{className:"text-2xs text-text-quaternary shrink-0 ml-2",children:ne(t.timestamp)})]}),e.jsx(_,{topic:t.type})]},t.id))})]})]})]})]})}export{he as AgentDetailPage};
|
|
4
|
-
//# sourceMappingURL=AgentDetailPage-D-Xbyrb6.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AgentDetailPage-D-Xbyrb6.js","sources":["../../src/pages/agents/AgentDetailPage.tsx"],"sourcesContent":["import { useState, useCallback } from 'react';\nimport { useParams, useNavigate } from 'react-router-dom';\nimport {\n Bot, Play, Pause, Trash2, ArrowUpRight, Pencil, BookOpen,\n Radio, Clock, Brain, Compass,\n} from 'lucide-react';\nimport { useAgent, useUpdateAgent, useDeleteAgent } from '../../api/agents';\nimport { useSettings } from '../../api/settings';\nimport { getAiOverride } from '../../lib/view-as';\nimport { useAgentSubscriptions } from '../../api/agent-subscriptions';\nimport { useAgentEvents } from '../../hooks/useEventHooks';\nimport { useEventSubscription } from '../../hooks/useEventContext';\nimport { NATS_SUBJECT_PREFIX } from '../../lib/nats/config';\nimport { EventTopicPill } from '../../components/common/display/EventTopicPill';\nimport { CronLabel } from '../../components/common/display/CronLabel';\nimport { WorkflowPill } from '../../components/common/display/WorkflowPill';\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction formatTime(iso: string): string {\n return new Date(iso).toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });\n}\n\nfunction SectionHeader({ icon: Icon, color, children, actions }: { icon: React.ElementType; color: string; children: React.ReactNode; actions?: React.ReactNode }) {\n return (\n <div className=\"flex items-center justify-between mb-3 pb-2 border-b border-surface-border\">\n <div className=\"flex items-center gap-2\">\n <Icon className={`w-4 h-4 ${color}`} strokeWidth={1.5} />\n <h2 className=\"section-h2\">{children}</h2>\n </div>\n {actions}\n </div>\n );\n}\n\nfunction EmptyHint({ text }: { text: string }) {\n return <p className=\"text-2xs text-text-quaternary py-2\">{text}</p>;\n}\n\n/** Per-section deeplink — jumps straight to that section instead of Edit → navigate. */\nfunction SectionViewLink({ to, label }: { to: string; label: string }) {\n const navigate = useNavigate();\n return (\n <button\n onClick={() => navigate(to)}\n className=\"text-text-quaternary hover:text-accent transition-colors\"\n title={`View ${label}`}\n aria-label={`View ${label}`}\n >\n <ArrowUpRight className=\"w-3.5 h-3.5\" strokeWidth={1.5} />\n </button>\n );\n}\n\n// ── Live feed ───────────────────────────────────────────────────────────────\n\ninterface FeedEvent { id: number; type: string; timestamp: string; label: string; }\nlet feedCounter = 0;\n\nfunction useAgentFeed(agentName?: string) {\n const [events, setEvents] = useState<FeedEvent[]>([]);\n const [pulse, setPulse] = useState(false);\n const handler = useCallback((event: any) => {\n if (event.type?.startsWith('mesh.')) return;\n const label = event.activityName || event.data?.domain || event.workflowId?.slice(0, 16) || '';\n setEvents((prev) => [{ id: ++feedCounter, type: event.type, timestamp: event.timestamp, label }, ...prev].slice(0, 10));\n setPulse(true);\n setTimeout(() => setPulse(false), 2000);\n }, []);\n useEventSubscription(\n agentName ? `${NATS_SUBJECT_PREFIX}.system.agent.${agentName}.>` : '',\n handler,\n );\n return { events, pulse };\n}\n\n// ── Page ─────────────────────────────────────────────────────────────────────\n\nexport function AgentDetailPage() {\n const { id } = useParams<{ id: string }>();\n const navigate = useNavigate();\n const { data: settings } = useSettings();\n const aiOverride = getAiOverride();\n const aiEnabled = aiOverride !== null ? aiOverride : !!settings?.ai?.enabled;\n const label = aiEnabled ? 'Agent' : 'Automation';\n const { data: agent, isLoading } = useAgent(id ?? null);\n const { data: subsData } = useAgentSubscriptions(id ?? null);\n const updateMutation = useUpdateAgent();\n const deleteMutation = useDeleteAgent();\n const { events: liveEvents, pulse } = useAgentFeed(id);\n useAgentEvents();\n\n if (isLoading) {\n return <div className=\"animate-pulse space-y-4\"><div className=\"h-8 bg-surface-sunken rounded w-48\" /><div className=\"h-40 bg-surface-sunken rounded\" /></div>;\n }\n\n if (!agent) {\n return (\n <div className=\"flex flex-col items-center justify-center py-20 text-center\">\n <Bot className=\"w-12 h-12 text-text-quaternary mb-4\" strokeWidth={1} />\n <h2 className=\"heading-3 mb-2\">{label} not found</h2>\n <button onClick={() => navigate('/agents')} className=\"text-sm text-accent hover:text-accent-hover transition-colors\">Back to {label.toLowerCase()}s</button>\n </div>\n );\n }\n\n const statusDot = agent.status === 'active' ? 'bg-status-success' : agent.status === 'paused' ? 'bg-status-warning' : agent.status === 'error' ? 'bg-status-error' : 'bg-surface-border';\n const subs = subsData?.subscriptions ?? [];\n const statusLabel = agent.status === 'active' ? 'Active' : agent.status === 'paused' ? 'Paused' : agent.status === 'error' ? 'Error' : 'Inactive';\n const schedules = (agent.behaviors as any)?.schedules as any[] | undefined;\n const legacyCron = agent.behaviors?.cron;\n const schedCount = schedules?.length || (legacyCron ? 1 : 0);\n\n const handlePause = () => {\n const msg = [\n `Pause \"${agent.id}\"?`,\n '',\n 'This will:',\n subs.length > 0 ? `• Stop ${subs.length} event subscription${subs.length !== 1 ? 's' : ''}` : null,\n schedCount > 0 ? `• Stop ${schedCount} schedule${schedCount !== 1 ? 's' : ''}` : null,\n '',\n 'Knowledge and workflow history are preserved. You can reactivate anytime.',\n ].filter(Boolean).join('\\n');\n if (confirm(msg)) updateMutation.mutate({ id: agent.id, status: 'paused' as any });\n };\n\n const handleDelete = () => {\n const msg = [\n `Delete \"${agent.id}\"?`,\n '',\n 'This permanently removes:',\n '• The agent configuration',\n subs.length > 0 ? `• ${subs.length} event subscription${subs.length !== 1 ? 's' : ''}` : null,\n schedCount > 0 ? `• ${schedCount} schedule${schedCount !== 1 ? 's' : ''}` : null,\n '',\n 'Knowledge entries and workflow history are preserved.',\n ].filter(Boolean).join('\\n');\n if (confirm(msg)) deleteMutation.mutate(agent.id, { onSuccess: () => navigate('/agents') });\n };\n\n return (\n <div>\n {/* ── Hero ──────────────────────────────────────────────────────────── */}\n <div className=\"flex items-start justify-between mb-3\">\n <div className=\"flex items-center gap-2\">\n <h1 className=\"heading-1\">{label}: {agent.id}</h1>\n <button onClick={() => { window.location.hash = '#docs:agents.md'; }} className=\"text-text-quaternary hover:text-accent transition-colors mt-1\" title=\"Docs\">\n <BookOpen className=\"w-4 h-4\" strokeWidth={1.5} />\n </button>\n </div>\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center gap-1.5 mr-2\">\n <span className={`w-2 h-2 rounded-full dot-ring ${statusDot}`} />\n <span className=\"text-xs text-text-secondary capitalize\">{statusLabel}</span>\n </div>\n <button onClick={() => navigate(`/agents/${agent.id}/edit`)} className=\"flex items-center gap-1.5 px-4 py-1.5 text-xs rounded-md bg-accent text-text-inverse hover:bg-accent-hover transition-colors\">\n <Pencil className=\"w-3 h-3\" /> Edit\n </button>\n {agent.status !== 'active' && (\n <button onClick={() => updateMutation.mutate({ id: agent.id, status: 'active' as any })} className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md text-status-success hover:bg-status-success/10 transition-colors\">\n <Play className=\"w-3 h-3\" /> Activate\n </button>\n )}\n {agent.status === 'active' && (\n <button onClick={handlePause} className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md text-text-tertiary hover:text-text-primary hover:bg-surface-hover transition-colors\">\n <Pause className=\"w-3 h-3\" /> Pause\n </button>\n )}\n <button onClick={handleDelete} className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md text-status-error/60 hover:text-status-error hover:bg-status-error/10 transition-colors\">\n <Trash2 className=\"w-3 h-3\" /> Delete\n </button>\n </div>\n </div>\n\n {/* Description callout */}\n {agent.description && (\n <div className=\"border-l-2 border-accent/30 pl-4 py-1 mb-10\">\n <p className=\"text-sm text-text-secondary italic leading-relaxed\">{agent.description}</p>\n </div>\n )}\n\n {/* Motivation */}\n {(agent.goals || agent.rules) && (\n <div className=\"mb-10\">\n <SectionHeader\n icon={Compass}\n color=\"text-accent\"\n actions={<SectionViewLink to={`/agents/${agent.id}/edit?step=2`} label=\"Motivation\" />}\n >Motivation</SectionHeader>\n <div className=\"grid grid-cols-2 gap-x-14 bg-surface-sunken/20 rounded-lg px-5 py-4\">\n {agent.goals && (\n <div>\n <p className=\"text-2xs text-text-quaternary uppercase tracking-widest mb-1.5\">Goals</p>\n <p className=\"text-sm text-text-primary leading-relaxed\">{agent.goals}</p>\n </div>\n )}\n {agent.rules && (\n <div>\n <p className=\"text-2xs text-text-quaternary uppercase tracking-widest mb-1.5\">Rules</p>\n <p className=\"text-sm text-text-primary leading-relaxed\">{agent.rules}</p>\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* ── 3-column grid ─────────────────────────────────────────────────── */}\n <div className=\"grid grid-cols-1 @wall:grid-cols-3 gap-x-14 gap-y-10\">\n\n {/* Col 1: Subscriptions */}\n <div>\n <SectionHeader\n icon={Radio}\n color=\"text-accent\"\n actions={<SectionViewLink to={`/agents/${agent.id}/edit?step=4`} label=\"Subscriptions\" />}\n >\n Subscriptions ({subs.length})\n </SectionHeader>\n {subs.length === 0 ? (\n <EmptyHint text=\"No event subscriptions\" />\n ) : (\n <div className=\"divide-y divide-surface-border/30\">\n {subs.map((sub: any) => (\n <div key={sub.id} className=\"flex items-center py-2\">\n <div className=\"flex-1 min-w-0\"><EventTopicPill topic={sub.topic} /></div>\n <div className=\"flex items-center gap-1.5 shrink-0\">\n <span className=\"text-text-quaternary text-2xs\">→</span>\n <WorkflowPill\n type={sub.reaction_type === 'capability' ? sub.tool_name : sub.workflow_type || sub.reaction_type}\n variant={sub.reaction_type === 'pipeline' ? 'pipeline' : sub.reaction_type === 'capability' ? 'capability' : 'durable'}\n />\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Col 2: Schedules */}\n <div>\n <SectionHeader\n icon={Clock}\n color=\"text-accent\"\n actions={<SectionViewLink to={`/agents/${agent.id}/edit?step=5`} label=\"Schedules\" />}\n >Schedules ({schedCount})</SectionHeader>\n {schedules?.length ? (\n <div className=\"divide-y divide-surface-border/30\">\n {schedules.map((s: any, i: number) => (\n <div key={i} className=\"flex items-center py-2\">\n <div className=\"flex-1 min-w-0\"><CronLabel cron={s.cron} /></div>\n <div className=\"flex items-center gap-1.5 shrink-0\"><span className=\"text-text-quaternary text-2xs\">→</span><WorkflowPill type={s.workflow_type || 'workflow'} /></div>\n </div>\n ))}\n </div>\n ) : legacyCron ? (\n <div className=\"flex items-center py-1\">\n <div className=\"flex-1 min-w-0\"><CronLabel cron={legacyCron} /></div>\n {agent.workflow_type && <div className=\"flex items-center gap-1.5 shrink-0\"><span className=\"text-text-quaternary text-2xs\">→</span><WorkflowPill type={agent.workflow_type} /></div>}\n </div>\n ) : (\n <EmptyHint text=\"No schedules configured\" />\n )}\n </div>\n\n {/* Col 3: Knowledge + Activity */}\n <div className=\"space-y-10\">\n <div>\n <SectionHeader\n icon={Brain}\n color=\"text-accent\"\n actions={\n <SectionViewLink\n to={agent.knowledge_domain ? `/knowledge?domain=${agent.knowledge_domain}` : `/agents/${agent.id}/edit?step=3`}\n label=\"Knowledge\"\n />\n }\n >Knowledge</SectionHeader>\n {agent.knowledge_domain ? (\n <button onClick={() => navigate(`/knowledge?domain=${agent.knowledge_domain}`)} className=\"group text-left flex items-center gap-3\">\n <span className=\"text-sm font-mono text-text-primary group-hover:text-accent transition-colors\">{agent.knowledge_domain}</span>\n <span className=\"text-2xs text-text-quaternary\">{agent.stats?.knowledge_count?.toLocaleString() ?? 0} entries</span>\n </button>\n ) : (\n <EmptyHint text=\"No knowledge domain\" />\n )}\n </div>\n\n <div>\n <SectionHeader icon={Radio} color=\"text-accent\"\n actions={\n <div className=\"flex items-center gap-1.5\">\n <span className={`w-1.5 h-1.5 rounded-full dot-ring transition-colors duration-500 ${pulse ? 'bg-status-success' : 'bg-surface-border'}`} />\n <span className=\"text-2xs text-text-quaternary\">live</span>\n </div>\n }\n >\n Activity\n </SectionHeader>\n {liveEvents.length === 0 ? (\n <EmptyHint text=\"Events appear here as the agent runs\" />\n ) : (\n <div className=\"space-y-1.5\">\n {liveEvents.map((ev) => (\n <div key={ev.id}>\n <div className=\"flex items-center justify-between mb-0.5\">\n <span className=\"text-2xs text-text-primary font-mono truncate flex-1\">{ev.label || '—'}</span>\n <span className=\"text-2xs text-text-quaternary shrink-0 ml-2\">{formatTime(ev.timestamp)}</span>\n </div>\n <EventTopicPill topic={ev.type} />\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"names":["formatTime","iso","SectionHeader","Icon","color","children","actions","jsxs","jsx","EmptyHint","text","SectionViewLink","to","label","navigate","useNavigate","ArrowUpRight","feedCounter","useAgentFeed","agentName","events","setEvents","useState","pulse","setPulse","handler","useCallback","event","_a","_b","_c","prev","useEventSubscription","NATS_SUBJECT_PREFIX","AgentDetailPage","id","useParams","settings","useSettings","aiOverride","getAiOverride","agent","isLoading","useAgent","subsData","useAgentSubscriptions","updateMutation","useUpdateAgent","deleteMutation","useDeleteAgent","liveEvents","useAgentEvents","Bot","statusDot","subs","statusLabel","schedules","legacyCron","schedCount","handlePause","msg","handleDelete","BookOpen","Pencil","Play","Pause","Trash2","Compass","Radio","sub","EventTopicPill","WorkflowPill","Clock","s","i","CronLabel","Brain","_e","_d","ev"],"mappings":"ufAmBA,SAASA,GAAWC,EAAqB,CACvC,OAAO,IAAI,KAAKA,CAAG,EAAE,mBAAmB,QAAS,CAAE,OAAQ,GAAO,KAAM,UAAW,OAAQ,UAAW,OAAQ,UAAW,CAC3H,CAEA,SAASC,EAAc,CAAE,KAAMC,EAAM,MAAAC,EAAO,SAAAC,EAAU,QAAAC,GAA6G,CACjK,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,6EACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,0BACb,SAAA,CAAAC,MAACL,GAAK,UAAW,WAAWC,CAAK,GAAI,YAAa,IAAK,EACvDI,EAAAA,IAAC,KAAA,CAAG,UAAU,aAAc,SAAAH,CAAA,CAAS,CAAA,EACvC,EACCC,CAAA,EACH,CAEJ,CAEA,SAASG,EAAU,CAAE,KAAAC,GAA0B,CAC7C,OAAOF,EAAAA,IAAC,IAAA,CAAE,UAAU,qCAAsC,SAAAE,EAAK,CACjE,CAGA,SAASC,EAAgB,CAAE,GAAAC,EAAI,MAAAC,GAAwC,CACrE,MAAMC,EAAWC,EAAA,EACjB,OACEP,EAAAA,IAAC,SAAA,CACC,QAAS,IAAMM,EAASF,CAAE,EAC1B,UAAU,2DACV,MAAO,QAAQC,CAAK,GACpB,aAAY,QAAQA,CAAK,GAEzB,SAAAL,EAAAA,IAACQ,GAAA,CAAa,UAAU,cAAc,YAAa,GAAA,CAAK,CAAA,CAAA,CAG9D,CAKA,IAAIC,GAAc,EAElB,SAASC,GAAaC,EAAoB,CACxC,KAAM,CAACC,EAAQC,CAAS,EAAIC,EAAAA,SAAsB,CAAA,CAAE,EAC9C,CAACC,EAAOC,CAAQ,EAAIF,EAAAA,SAAS,EAAK,EAClCG,EAAUC,cAAaC,GAAe,WAC1C,IAAIC,EAAAD,EAAM,OAAN,MAAAC,EAAY,WAAW,SAAU,OACrC,MAAMf,EAAQc,EAAM,gBAAgBE,EAAAF,EAAM,OAAN,YAAAE,EAAY,WAAUC,EAAAH,EAAM,aAAN,YAAAG,EAAkB,MAAM,EAAG,MAAO,GAC5FT,EAAWU,GAAS,CAAC,CAAE,GAAI,EAAEd,GAAa,KAAMU,EAAM,KAAM,UAAWA,EAAM,UAAW,MAAAd,GAAS,GAAGkB,CAAI,EAAE,MAAM,EAAG,EAAE,CAAC,EACtHP,EAAS,EAAI,EACb,WAAW,IAAMA,EAAS,EAAK,EAAG,GAAI,CACxC,EAAG,CAAA,CAAE,EACL,OAAAQ,EACEb,EAAY,GAAGc,CAAmB,iBAAiBd,CAAS,KAAO,GACnEM,CAAA,EAEK,CAAE,OAAAL,EAAQ,MAAAG,CAAA,CACnB,CAIO,SAASW,IAAkB,eAChC,KAAM,CAAE,GAAAC,CAAA,EAAOC,GAAA,EACTtB,EAAWC,EAAA,EACX,CAAE,KAAMsB,CAAA,EAAaC,EAAA,EACrBC,EAAaC,EAAA,EAEb3B,GADY0B,IAAe,KAAOA,EAAa,CAAC,GAACX,EAAAS,GAAA,YAAAA,EAAU,KAAV,MAAAT,EAAc,UAC3C,QAAU,aAC9B,CAAE,KAAMa,EAAO,UAAAC,GAAcC,EAASR,GAAM,IAAI,EAChD,CAAE,KAAMS,CAAA,EAAaC,EAAsBV,GAAM,IAAI,EACrDW,EAAiBC,EAAA,EACjBC,EAAiBC,EAAA,EACjB,CAAE,OAAQC,EAAY,MAAA3B,CAAA,EAAUL,GAAaiB,CAAE,EAGrD,GAFAgB,EAAA,EAEIT,EACF,OAAOnC,EAAAA,KAAC,MAAA,CAAI,UAAU,0BAA0B,SAAA,CAAAC,EAAAA,IAAC,MAAA,CAAI,UAAU,oCAAA,CAAqC,EAAEA,EAAAA,IAAC,MAAA,CAAI,UAAU,gCAAA,CAAiC,CAAA,EAAE,EAG1J,GAAI,CAACiC,EACH,OACElC,EAAAA,KAAC,MAAA,CAAI,UAAU,8DACb,SAAA,CAAAC,EAAAA,IAAC4C,EAAA,CAAI,UAAU,sCAAsC,YAAa,EAAG,EACrE7C,EAAAA,KAAC,KAAA,CAAG,UAAU,iBAAkB,SAAA,CAAAM,EAAM,YAAA,EAAU,EAChDN,OAAC,UAAO,QAAS,IAAMO,EAAS,SAAS,EAAG,UAAU,gEAAgE,SAAA,CAAA,WAASD,EAAM,YAAA,EAAc,GAAA,CAAA,CAAC,CAAA,EACtJ,EAIJ,MAAMwC,EAAYZ,EAAM,SAAW,SAAW,oBAAsBA,EAAM,SAAW,SAAW,oBAAsBA,EAAM,SAAW,QAAU,kBAAoB,oBAC/Ja,GAAOV,GAAA,YAAAA,EAAU,gBAAiB,CAAA,EAClCW,EAAcd,EAAM,SAAW,SAAW,SAAWA,EAAM,SAAW,SAAW,SAAWA,EAAM,SAAW,QAAU,QAAU,WACjIe,GAAa3B,EAAAY,EAAM,YAAN,YAAAZ,EAAyB,UACtC4B,GAAa3B,EAAAW,EAAM,YAAN,YAAAX,EAAiB,KAC9B4B,GAAaF,GAAA,YAAAA,EAAW,UAAWC,EAAa,EAAI,GAEpDE,EAAc,IAAM,CACxB,MAAMC,EAAM,CACV,UAAUnB,EAAM,EAAE,KAClB,GACA,aACAa,EAAK,OAAS,EAAI,UAAUA,EAAK,MAAM,sBAAsBA,EAAK,SAAW,EAAI,IAAM,EAAE,GAAK,KAC9FI,EAAa,EAAI,UAAUA,CAAU,YAAYA,IAAe,EAAI,IAAM,EAAE,GAAK,KACjF,GACA,2EAAA,EACA,OAAO,OAAO,EAAE,KAAK;AAAA,CAAI,EACvB,QAAQE,CAAG,GAAGd,EAAe,OAAO,CAAE,GAAIL,EAAM,GAAI,OAAQ,QAAA,CAAiB,CACnF,EAEMoB,EAAe,IAAM,CACzB,MAAMD,EAAM,CACV,WAAWnB,EAAM,EAAE,KACnB,GACA,4BACA,4BACAa,EAAK,OAAS,EAAI,KAAKA,EAAK,MAAM,sBAAsBA,EAAK,SAAW,EAAI,IAAM,EAAE,GAAK,KACzFI,EAAa,EAAI,KAAKA,CAAU,YAAYA,IAAe,EAAI,IAAM,EAAE,GAAK,KAC5E,GACA,uDAAA,EACA,OAAO,OAAO,EAAE,KAAK;AAAA,CAAI,EACvB,QAAQE,CAAG,GAAGZ,EAAe,OAAOP,EAAM,GAAI,CAAE,UAAW,IAAM3B,EAAS,SAAS,EAAG,CAC5F,EAEA,cACG,MAAA,CAEC,SAAA,CAAAP,EAAAA,KAAC,MAAA,CAAI,UAAU,wCACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,0BACb,SAAA,CAAAA,EAAAA,KAAC,KAAA,CAAG,UAAU,YAAa,SAAA,CAAAM,EAAM,KAAG4B,EAAM,EAAA,EAAG,EAC7CjC,MAAC,SAAA,CAAO,QAAS,IAAM,CAAE,OAAO,SAAS,KAAO,iBAAmB,EAAG,UAAU,gEAAgE,MAAM,OACpJ,SAAAA,EAAAA,IAACsD,EAAA,CAAS,UAAU,UAAU,YAAa,GAAA,CAAK,CAAA,CAClD,CAAA,EACF,EACAvD,EAAAA,KAAC,MAAA,CAAI,UAAU,0BACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,iCACb,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAK,UAAW,iCAAiC6C,CAAS,GAAI,EAC/D7C,EAAAA,IAAC,OAAA,CAAK,UAAU,yCAA0C,SAAA+C,CAAA,CAAY,CAAA,EACxE,EACAhD,EAAAA,KAAC,SAAA,CAAO,QAAS,IAAMO,EAAS,WAAW2B,EAAM,EAAE,OAAO,EAAG,UAAU,+HACrE,SAAA,CAAAjC,EAAAA,IAACuD,EAAA,CAAO,UAAU,SAAA,CAAU,EAAE,OAAA,EAChC,EACCtB,EAAM,SAAW,iBACf,SAAA,CAAO,QAAS,IAAMK,EAAe,OAAO,CAAE,GAAIL,EAAM,GAAI,OAAQ,SAAiB,EAAG,UAAU,4HACjG,SAAA,CAAAjC,EAAAA,IAACwD,EAAA,CAAK,UAAU,SAAA,CAAU,EAAE,WAAA,EAC9B,EAEDvB,EAAM,SAAW,UAChBlC,EAAAA,KAAC,UAAO,QAASoD,EAAa,UAAU,+IACtC,SAAA,CAAAnD,EAAAA,IAACyD,EAAA,CAAM,UAAU,SAAA,CAAU,EAAE,QAAA,EAC/B,EAEF1D,EAAAA,KAAC,SAAA,CAAO,QAASsD,EAAc,UAAU,mJACvC,SAAA,CAAArD,EAAAA,IAAC0D,EAAA,CAAO,UAAU,SAAA,CAAU,EAAE,SAAA,CAAA,CAChC,CAAA,CAAA,CACF,CAAA,EACF,EAGCzB,EAAM,aACLjC,MAAC,MAAA,CAAI,UAAU,8CACb,SAAAA,EAAAA,IAAC,IAAA,CAAE,UAAU,qDAAsD,SAAAiC,EAAM,WAAA,CAAY,EACvF,GAIAA,EAAM,OAASA,EAAM,QACrBlC,OAAC,MAAA,CAAI,UAAU,QACb,SAAA,CAAAC,EAAAA,IAACN,EAAA,CACC,KAAMiE,EACN,MAAM,cACN,cAAUxD,EAAA,CAAgB,GAAI,WAAW8B,EAAM,EAAE,eAAgB,MAAM,YAAA,CAAa,EACrF,SAAA,YAAA,CAAA,EACDlC,EAAAA,KAAC,MAAA,CAAI,UAAU,sEACZ,SAAA,CAAAkC,EAAM,cACJ,MAAA,CACC,SAAA,CAAAjC,EAAAA,IAAC,IAAA,CAAE,UAAU,iEAAiE,SAAA,QAAK,EACnFA,EAAAA,IAAC,IAAA,CAAE,UAAU,4CAA6C,WAAM,KAAA,CAAM,CAAA,EACxE,EAEDiC,EAAM,OACLlC,EAAAA,KAAC,MAAA,CACC,SAAA,CAAAC,EAAAA,IAAC,IAAA,CAAE,UAAU,iEAAiE,SAAA,QAAK,EACnFA,EAAAA,IAAC,IAAA,CAAE,UAAU,4CAA6C,WAAM,KAAA,CAAM,CAAA,CAAA,CACxE,CAAA,CAAA,CAEJ,CAAA,EACF,EAIFD,EAAAA,KAAC,MAAA,CAAI,UAAU,uDAGb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAA,EAAAA,KAACL,EAAA,CACC,KAAMkE,EACN,MAAM,cACN,cAAUzD,EAAA,CAAgB,GAAI,WAAW8B,EAAM,EAAE,eAAgB,MAAM,eAAA,CAAgB,EACxF,SAAA,CAAA,kBACiBa,EAAK,OAAO,GAAA,CAAA,CAAA,EAE7BA,EAAK,SAAW,QACd7C,EAAA,CAAU,KAAK,yBAAyB,EAEzCD,EAAAA,IAAC,OAAI,UAAU,oCACZ,WAAK,IAAK6D,GACT9D,EAAAA,KAAC,MAAA,CAAiB,UAAU,yBAC1B,SAAA,CAAAC,EAAAA,IAAC,MAAA,CAAI,UAAU,iBAAiB,SAAAA,EAAAA,IAAC8D,GAAe,MAAOD,EAAI,MAAO,CAAA,CAAE,EACpE9D,EAAAA,KAAC,MAAA,CAAI,UAAU,qCACb,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAK,UAAU,gCAAgC,SAAA,IAAC,EACjDA,EAAAA,IAAC+D,EAAA,CACC,KAAMF,EAAI,gBAAkB,aAAeA,EAAI,UAAYA,EAAI,eAAiBA,EAAI,cACpF,QAASA,EAAI,gBAAkB,WAAa,WAAaA,EAAI,gBAAkB,aAAe,aAAe,SAAA,CAAA,CAC/G,CAAA,CACF,CAAA,GARQA,EAAI,EASd,CACD,CAAA,CACH,CAAA,EAEJ,SAGC,MAAA,CACC,SAAA,CAAA9D,EAAAA,KAACL,EAAA,CACC,KAAMsE,GACN,MAAM,cACN,cAAU7D,EAAA,CAAgB,GAAI,WAAW8B,EAAM,EAAE,eAAgB,MAAM,WAAA,CAAY,EACpF,SAAA,CAAA,cAAYiB,EAAW,GAAA,CAAA,CAAA,EACvBF,GAAA,MAAAA,EAAW,OACVhD,MAAC,MAAA,CAAI,UAAU,oCACZ,SAAAgD,EAAU,IAAI,CAACiB,EAAQC,IACtBnE,EAAAA,KAAC,MAAA,CAAY,UAAU,yBACrB,SAAA,CAAAC,EAAAA,IAAC,MAAA,CAAI,UAAU,iBAAiB,SAAAA,EAAAA,IAACmE,GAAU,KAAMF,EAAE,KAAM,CAAA,CAAE,EAC3DlE,EAAAA,KAAC,MAAA,CAAI,UAAU,qCAAqC,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAK,UAAU,gCAAgC,SAAA,IAAC,EAAOA,EAAAA,IAAC+D,EAAA,CAAa,KAAME,EAAE,eAAiB,UAAA,CAAY,CAAA,CAAA,CAAE,CAAA,CAAA,EAFzJC,CAGV,CACD,CAAA,CACH,EACEjB,EACFlD,OAAC,MAAA,CAAI,UAAU,yBACb,SAAA,CAAAC,EAAAA,IAAC,OAAI,UAAU,iBAAiB,eAACmE,EAAA,CAAU,KAAMlB,EAAY,CAAA,CAAE,EAC9DhB,EAAM,eAAiBlC,OAAC,MAAA,CAAI,UAAU,qCAAqC,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAK,UAAU,gCAAgC,SAAA,IAAC,EAAOA,EAAAA,IAAC+D,EAAA,CAAa,KAAM9B,EAAM,aAAA,CAAe,CAAA,CAAA,CAAE,CAAA,CAAA,CACjL,EAEAjC,EAAAA,IAACC,EAAA,CAAU,KAAK,yBAAA,CAA0B,CAAA,EAE9C,EAGAF,EAAAA,KAAC,MAAA,CAAI,UAAU,aACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAC,EAAAA,IAACN,EAAA,CACC,KAAM0E,GACN,MAAM,cACN,QACEpE,EAAAA,IAACG,EAAA,CACC,GAAI8B,EAAM,iBAAmB,qBAAqBA,EAAM,gBAAgB,GAAK,WAAWA,EAAM,EAAE,eAChG,MAAM,WAAA,CAAA,EAGX,SAAA,WAAA,CAAA,EACAA,EAAM,iBACLlC,EAAAA,KAAC,SAAA,CAAO,QAAS,IAAMO,EAAS,qBAAqB2B,EAAM,gBAAgB,EAAE,EAAG,UAAU,0CACxF,SAAA,CAAAjC,EAAAA,IAAC,OAAA,CAAK,UAAU,gFAAiF,SAAAiC,EAAM,iBAAiB,EACxHlC,EAAAA,KAAC,OAAA,CAAK,UAAU,gCAAiC,SAAA,GAAAsE,GAAAC,EAAArC,EAAM,QAAN,YAAAqC,EAAa,kBAAb,YAAAD,EAA8B,mBAAoB,EAAE,UAAA,CAAA,CAAQ,CAAA,CAAA,CAC/G,EAEArE,EAAAA,IAACC,EAAA,CAAU,KAAK,qBAAA,CAAsB,CAAA,EAE1C,SAEC,MAAA,CACC,SAAA,CAAAD,EAAAA,IAACN,EAAA,CAAc,KAAMkE,EAAO,MAAM,cAChC,QACE7D,EAAAA,KAAC,MAAA,CAAI,UAAU,4BACb,SAAA,CAAAC,MAAC,QAAK,UAAW,oEAAoEe,EAAQ,oBAAsB,mBAAmB,GAAI,EAC1If,EAAAA,IAAC,OAAA,CAAK,UAAU,gCAAgC,SAAA,MAAA,CAAI,CAAA,EACtD,EAEH,SAAA,UAAA,CAAA,EAGA0C,EAAW,SAAW,EACrB1C,MAACC,EAAA,CAAU,KAAK,uCAAuC,EAEvDD,MAAC,MAAA,CAAI,UAAU,cACZ,SAAA0C,EAAW,IAAK6B,UACd,MAAA,CACC,SAAA,CAAAxE,EAAAA,KAAC,MAAA,CAAI,UAAU,2CACb,SAAA,CAAAC,MAAC,OAAA,CAAK,UAAU,uDAAwD,SAAAuE,EAAG,OAAS,IAAI,QACvF,OAAA,CAAK,UAAU,8CAA+C,SAAA/E,GAAW+E,EAAG,SAAS,CAAA,CAAE,CAAA,EAC1F,EACAvE,EAAAA,IAAC8D,EAAA,CAAe,MAAOS,EAAG,IAAA,CAAM,CAAA,GALxBA,EAAG,EAMb,CACD,CAAA,CACH,CAAA,CAAA,CAEJ,CAAA,CAAA,CACF,CAAA,CAAA,CACF,CAAA,EACF,CAEJ"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as s,a as k}from"./vendor-query-B2UbickB.js";import{D as X}from"./DataTable-BTu2AaEl.js";import{Q as $,m as S,K as R,b as W,P as G,g as tt,_ as et,$ as st,a0 as rt}from"./index-CSTwyrwu.js";import{a as Y}from"./StatusBadge-BbqghFMi.js";import{S as K}from"./StickyPagination-D5zxe_l9.js";import{ab as J,ac as z}from"./vendor-icons-Bg6eJX4Z.js";import{L as C}from"./vendor-react-BpdAFEQc.js";const nt=new Set(["__proto__","constructor","prototype"]);function B(t){const e=[];for(const r of t.split(".")){const n=r.match(/^([^[\]]*)((?:\[\d+\])*)$/);if(!n)throw new Error(`invalid x-lt-bind path segment: "${r}"`);const[,a,l]=n;if(a){if(nt.has(a))throw new Error(`x-lt-bind path may not target "${a}"`);e.push({key:a})}for(const i of l.match(/\d+/g)??[])e.push({index:Number(i)})}if(e.length===0)throw new Error(`empty x-lt-bind path: "${t}"`);return e}function at(t,e,r){const n=B(e);let a=t;for(let i=0;i<n.length-1;i++){const x=n[i],c=n[i+1],o="key"in x?x.key:x.index,m="index"in c;(a[o]==null||typeof a[o]!="object")&&(a[o]=m?[]:{}),a=a[o]}const l=n[n.length-1];a["key"in l?l.key:l.index]=r}function D(t,e){let r=t;for(const n of B(e)){if(r==null||typeof r!="object")return;r=r["key"in n?n.key:n.index]}return r}function Q(t,e){const r=e==null?void 0:e["x-lt-bind"];return typeof r=="string"&&r.length>0?r:t}function Mt(t,e){const r=e==null?void 0:e.properties;if(!r)return{...t};const n={};for(const[a,l]of Object.entries(r))a in t&&at(n,Q(a,l),t[a]);return n}function $t(t,e){const r=e==null?void 0:e.properties;if(!r)return t?{...t}:{};const n={};for(const[a,l]of Object.entries(r)){const i=D(t,Q(a,l));i!==void 0&&(n[a]=i)}return n}const it=["escalation","metadata","envelope","payload","resolver"],ot=/\{\{\s*([^{}]+?)\s*\}\}/g,M="—";function q(t){if(t==null)return M;if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return String(t);try{return JSON.stringify(t)}catch{return M}}function g(t,e){return t.replace(ot,(r,n)=>{const a=n.indexOf("."),l=a===-1?n:n.slice(0,a);if(!it.includes(l))return M;const i=e[l];if(a===-1)return q(i);try{return q(D(i,n.slice(a+1)))}catch{return M}})}function lt(t){const e=t==null?void 0:t["x-lt-help"];if(typeof e=="string"&&e.trim().length>0)return e;const r=t==null?void 0:t["x-lt-context"];return typeof r=="string"&&r.trim().length>0?r:null}function Dt(t,e){const r=lt(t);return r?g(r,e):null}function Lt({isTerminal:t,status:e,claimed:r,claimedByMe:n}){return t?e==="cancelled"?"This escalation was cancelled. The record is shown for reference.":"This escalation has been resolved. The submitted form is shown for reference.":n?"You have claimed this escalation. Fill out the form and submit to resolve it.":r?"Another user has claimed this escalation. It unlocks if their claim expires or is released.":"Claim this escalation to enable the form."}function ct(t){return!!(t.assigned_to&&t.assigned_until&&new Date(t.assigned_until)>new Date)}function At(t){return!t.workflow_type}const F=[{text:"text-rose-400",bg:"bg-rose-400/[0.08]"},{text:"text-pink-400",bg:"bg-pink-400/[0.08]"},{text:"text-fuchsia-400",bg:"bg-fuchsia-400/[0.08]"},{text:"text-purple-400",bg:"bg-purple-400/[0.08]"},{text:"text-violet-400",bg:"bg-violet-400/[0.08]"},{text:"text-indigo-400",bg:"bg-indigo-400/[0.08]"},{text:"text-blue-400",bg:"bg-blue-400/[0.08]"},{text:"text-sky-400",bg:"bg-sky-400/[0.08]"},{text:"text-cyan-400",bg:"bg-cyan-400/[0.08]"},{text:"text-teal-400",bg:"bg-teal-400/[0.08]"},{text:"text-emerald-400",bg:"bg-emerald-400/[0.08]"},{text:"text-green-400",bg:"bg-green-400/[0.08]"},{text:"text-lime-400",bg:"bg-lime-400/[0.08]"},{text:"text-yellow-400",bg:"bg-yellow-400/[0.08]"},{text:"text-amber-400",bg:"bg-amber-400/[0.08]"},{text:"text-orange-400",bg:"bg-orange-400/[0.08]"},{text:"text-red-400",bg:"bg-red-400/[0.08]"},{text:"text-stone-400",bg:"bg-stone-400/[0.08]"},{text:"text-rose-300",bg:"bg-rose-300/[0.08]"},{text:"text-violet-300",bg:"bg-violet-300/[0.08]"},{text:"text-sky-300",bg:"bg-sky-300/[0.08]"},{text:"text-teal-300",bg:"bg-teal-300/[0.08]"},{text:"text-amber-300",bg:"bg-amber-300/[0.08]"},{text:"text-pink-300",bg:"bg-pink-300/[0.08]"}];function xt(t){let e=0;for(let r=0;r<t.length;r++)e=(e<<5)-e+t.charCodeAt(r)|0;return Math.abs(e)}function dt(t){return F[xt(t)%F.length]}function ut(t){const[,e]=k.useState(0);k.useEffect(()=>{if(!t)return;const r=window.setInterval(()=>e(n=>n+1),6e4);return()=>window.clearInterval(r)},[t])}function mt(t){var e;return[...t["x-lt-columns"]??[],...((e=t["x-lt-card"])==null?void 0:e.fields)??[]].some(r=>r.format==="age")}function T(t){const e=r=>{if(!r)return null;try{const n=JSON.parse(r);return n&&typeof n=="object"?n:null}catch{return null}};return{escalation:t,metadata:t.metadata??null,envelope:e(t.envelope),payload:e(t.escalation_payload),resolver:e(t.resolver_payload)}}const ft=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/,U="—";function L({raw:t,format:e}){if(!t||t===U)return s.jsx("span",{className:"text-text-quaternary",children:U});if(e==="age"){const r=new Date(t);if(!Number.isNaN(r.getTime()))return s.jsx("span",{title:r.toLocaleString(),className:"tabular-nums whitespace-nowrap",children:G(t)})}return ft.test(t)?s.jsx(W,{date:t,format:"datetime",className:"text-text-primary"}):s.jsx(s.Fragment,{children:t})}const pt=30;function Z(t){return t.status==="pending"&&!ct(t)}function A({row:t,def:e,onView:r,prominent:n,forceView:a}){const l=(e==null?void 0:e.action)??"claim",i=a?"view":l,x=l===i&&(e!=null&&e.label)?e.label:i==="claim"?"Claim":"View",c=tt(),[o,m]=k.useState("");if(i==="claim"&&!Z(t))return null;const d=f=>{if(f.stopPropagation(),i==="view"){r==null||r();return}m(""),c.mutate({id:t.id,durationMinutes:(e==null?void 0:e.durationMinutes)??pt},{onSuccess:()=>r==null?void 0:r(),onError:b=>m(b.message)})},u=n?"btn-primary text-xs shrink-0 disabled:opacity-50":"px-2 py-0.5 rounded text-2xs font-medium text-accent/70 border border-accent/30 hover:text-accent hover:border-accent/60 hover:bg-accent/5 transition-colors disabled:opacity-40 whitespace-nowrap";return s.jsxs("span",{className:"inline-flex flex-col items-end gap-0.5",children:[s.jsx("button",{type:"button",onClick:d,disabled:c.isPending,className:u,"data-testid":"row-action-button",children:c.isPending?"Claiming…":x}),o&&s.jsx("span",{className:"text-2xs text-status-error","data-testid":"row-action-error",children:o})]})}function bt({esc:t,card:e,rowAction:r,onOpen:n,forceView:a}){const l=T(t),i=e.title?g(e.title,l):t.type;return s.jsxs("div",{children:[s.jsxs("div",{className:"flex items-start justify-between gap-6",children:[s.jsxs("button",{onClick:n,className:"text-left group min-w-0",children:[s.jsx("h3",{className:"heading-2 group-hover:text-accent transition-colors leading-tight",children:i}),e.subtitle&&s.jsx("p",{className:"text-xs text-text-tertiary mt-1.5",children:g(e.subtitle,l)})]}),s.jsx(A,{row:t,def:r,onView:n,prominent:!0,forceView:a})]}),e.fields&&e.fields.length>0&&s.jsx("dl",{className:"flex flex-wrap gap-x-10 gap-y-4 mt-6",children:e.fields.map((x,c)=>s.jsxs("div",{children:[s.jsx("dt",{className:"text-2xs font-semibold uppercase tracking-widest text-text-tertiary",children:x.label}),s.jsx("dd",{className:"text-xs text-text-primary mt-1",children:s.jsx(L,{raw:g(x.value,l)})})]},c))}),e.body&&s.jsx("div",{className:"mt-7 pt-6 border-t border-surface-border/50",children:s.jsx($,{content:g(e.body,l)})})]})}function gt({role:t,def:e,onRowClick:r}){var o,m;const[n,a]=k.useState(!1),l=R({role:t,status:e.status??"resolved",sort_by:"resolved_at",order:"desc",limit:e.limit??25,enabled:n}),i=((o=l.data)==null?void 0:o.escalations)??[],x=((m=l.data)==null?void 0:m.total)??0,c=e.row??{};return n?l.isLoading?s.jsx("p",{className:"text-xs text-text-tertiary italic",children:"Loading history…"}):i.length===0?s.jsx("p",{className:"text-xs text-text-tertiary italic",children:"No past revisions."}):s.jsxs("div",{children:[s.jsx("div",{className:"divide-y divide-surface-border/40",children:i.map(d=>{const u=T(d);return s.jsxs("button",{onClick:()=>r==null?void 0:r(d),className:"w-full text-left py-2.5 group flex items-center gap-3",children:[s.jsx("span",{className:`w-1.5 h-1.5 shrink-0 rounded-full dot-ring ${Y[d.status]??"bg-status-pending"}`,title:d.status}),s.jsxs("span",{className:"min-w-0 flex-1",children:[s.jsx("span",{className:"block text-xs text-text-primary group-hover:text-accent transition-colors truncate",children:c.title?g(c.title,u):d.type}),c.subtitle&&s.jsx("span",{className:"block text-2xs text-text-tertiary truncate",children:g(c.subtitle,u)})]}),c.meta?s.jsx("span",{className:"text-2xs text-text-tertiary shrink-0",children:g(c.meta,u)}):d.resolved_at&&s.jsx(W,{date:d.resolved_at,format:"relative",className:"text-2xs text-text-tertiary shrink-0 whitespace-nowrap"})]},d.id)})}),x>i.length&&s.jsxs("p",{className:"text-2xs text-text-quaternary mt-3",children:["Showing ",i.length," of ",x,"."]})]}):s.jsx("button",{onClick:()=>a(!0),className:"text-xs text-accent hover:underline","data-testid":"load-history",children:"Load full history →"})}function ht({schema:t,rows:e,role:r,onRowClick:n,onAddFacet:a,forceView:l}){const i=t["x-lt-columns"]??[],x=t["x-lt-row-action"],c=[{key:"_status",label:"",priority:1,className:"w-10",render:o=>s.jsx("span",{className:`w-1.5 h-1.5 inline-block rounded-full dot-ring ${Y[o.status]??"bg-status-pending"}`,title:o.status})},...i.map((o,m)=>({key:`col-${m}`,label:o.label,priority:o.priority??(m===0?1:2),render:d=>{var j,p;const u=T(d),f=(j=o.value.match(O))==null?void 0:j[1],b=f?(p=d.metadata)==null?void 0:p[f]:void 0,w=f!=null&&b!==void 0&&b!==null&&b!=="",h=s.jsx(L,{raw:g(o.value,u),format:o.format});return w?s.jsxs("span",{className:"inline-flex items-center gap-1 min-w-0 max-w-full",children:[s.jsx("span",{className:"truncate",children:h}),s.jsxs("span",{className:"inline-flex items-center gap-px shrink-0 opacity-50 group-hover/row:opacity-100 transition-opacity",children:[s.jsx(C,{to:S(f,b,r),onClick:y=>{y.stopPropagation(),y.shiftKey&&a&&(y.preventDefault(),a(f,b))},className:"p-0.5 rounded text-text-quaternary hover:text-accent transition-colors",title:`Filter ${r}: ${f} = ${String(b)}${a?" · ⇧ click adds to current filters":""}`,"data-testid":"facet-column-filter",children:s.jsx(J,{className:"w-3 h-3"})}),s.jsx(C,{to:S(f,b),onClick:y=>y.stopPropagation(),className:"p-0.5 rounded text-text-quaternary hover:text-accent transition-colors",title:`Search all: ${f} = ${String(b)}`,"data-testid":"facet-column-search",children:s.jsx(z,{className:"w-3 h-3"})})]})]}):h}})),{key:"_action",label:"",priority:1,className:"w-24 text-right",render:o=>s.jsx(A,{row:o,def:x,onView:()=>n==null?void 0:n(o),forceView:l})}];return s.jsx(X,{columns:c,data:e,layout:"fixed",keyFn:o=>o.id,onRowClick:n,emptyMessage:"No pending items."})}function P({children:t}){return s.jsx("p",{className:"text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-3",children:t})}function yt(t,e){var l;const r=e.indexOf("."),n=e.startsWith("metadata.")?e.slice(9):null,a=new Map;for(const i of t){const x=T(i);let c;try{c=r===-1?x[e]:D(x[e.slice(0,r)],e.slice(r+1))}catch{c=void 0}if(c==null||c==="")continue;const o=String(c),m=n?((l=i.metadata)==null?void 0:l[n])??c:c,d=a.get(o);d?(d.count+=1,new Date(i.created_at)>new Date(d.latest.created_at)&&(d.latest=i)):a.set(o,{key:o,rawValue:m,latest:i,count:1})}return[...a.values()].sort((i,x)=>i.key.localeCompare(x.key))}const O=/^\{\{\s*metadata\.([a-zA-Z0-9_]+)\s*\}\}$/;function jt({schema:t,rows:e,role:r,onOpenDetail:n,onOpenGroup:a,onAddFacet:l,forceView:i}){const x=t["x-lt-group-by"],c=t["x-lt-card"]??{},o=t["x-lt-row-action"];if(!x)return s.jsx("p",{className:"text-xs text-text-tertiary italic",children:"facet-board needs an x-lt-group-by path."});const m=x.startsWith("metadata.")?x.slice(9):null,d=yt(e,x);return d.length===0?s.jsx("p",{className:"text-xs text-text-tertiary italic",children:"No entities in scope."}):s.jsx("div",{className:"grid grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] gap-4","data-testid":"facet-board",children:d.map(u=>{const f=T(u.latest),b=c.title?g(c.title,f):u.key,w=c.state?g(c.state,f):u.latest.subtype||u.latest.status,h=dt(w),j=p=>{if(p.shiftKey&&m&&l){l(m,u.rawValue);return}n==null||n(u.latest)};return s.jsxs("div",{role:"button",tabIndex:0,onClick:j,onKeyDown:p=>{(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),j(p))},title:m?"Open the latest item · ⇧ click to filter the board":"Open the latest item",className:"group/card border-l-2 border-accent/30 bg-surface-sunken/40 rounded-[0.125em] px-4 py-3.5 cursor-pointer transition-colors hover:bg-surface-sunken/70 focus:outline-none focus-visible:ring-1 focus-visible:ring-accent/40","data-testid":"facet-board-card",children:[s.jsxs("div",{className:"flex items-center justify-between gap-2 mb-2.5",children:[s.jsx("span",{className:"text-2xs font-semibold uppercase tracking-wider text-text-secondary truncate",children:b}),s.jsx("span",{className:"shrink-0 px-1.5 py-0.5 rounded text-2xs font-mono font-medium",style:{color:h.text,backgroundColor:h.bg},title:w,children:w})]}),c.fields&&c.fields.length>0&&s.jsx("dl",{className:"space-y-1",children:c.fields.map((p,y)=>{var I,H;const v=(I=p.value.match(O))==null?void 0:I[1],N=v?(H=u.latest.metadata)==null?void 0:H[v]:void 0,V=v!=null&&N!==void 0&&N!==null&&N!=="";return s.jsxs("div",{className:"group/frow flex items-baseline justify-between gap-3",children:[s.jsx("dt",{className:"text-2xs uppercase tracking-wider text-text-quaternary shrink-0",children:p.label}),s.jsxs("dd",{className:"min-w-0 flex items-baseline gap-1 text-2xs text-text-primary",children:[s.jsx("span",{className:"truncate",children:s.jsx(L,{raw:g(p.value,f),format:p.format})}),s.jsx("span",{className:"shrink-0 w-9 self-center flex items-center justify-end gap-px",children:V&&(a||l)&&s.jsxs("span",{className:"flex items-center gap-px opacity-0 group-hover/frow:opacity-100 transition-opacity",children:[s.jsx("button",{onClick:_=>{_.stopPropagation(),_.shiftKey&&l?l(v,N):a==null||a(S(v,N,r))},className:"p-0.5 rounded text-text-quaternary hover:text-accent transition-colors",title:`Filter ${r}: ${v} = ${String(N)} · ⇧ click adds to current filters`,"data-testid":"facet-field-filter",children:s.jsx(J,{className:"w-3 h-3"})}),s.jsx("button",{onClick:_=>{_.stopPropagation(),a==null||a(S(v,N))},className:"p-0.5 rounded text-text-quaternary hover:text-accent transition-colors",title:`Search all: ${v} = ${String(N)}`,"data-testid":"facet-field-search",children:s.jsx(z,{className:"w-3 h-3"})})]})})]})]},y)})}),(i||((o==null?void 0:o.action)??"claim")==="view"||Z(u.latest))&&s.jsx("div",{className:"mt-3 flex justify-end",children:s.jsx(A,{row:u.latest,def:o,onView:()=>n==null?void 0:n(u.latest),forceView:i})})]},u.key)})})}function It({role:t,listSchema:e,activeEscalations:r,onRowClick:n,onOpenGroup:a,onAddFacet:l,forceViewAction:i,total:x,page:c,totalPages:o,pageSize:m,onPageChange:d,onPageSizeChange:u}){const f=e["x-lt-layout"],b=e["x-lt-active"]??{},w=e["x-lt-row-action"],h=r[0],j=e["x-lt-help"];ut(mt(e));const p=s.jsxs("div",{className:"border border-surface-border/60 bg-surface-sunken rounded-[0.25em] p-4",children:[s.jsx(P,{children:"Active"}),h?s.jsx(bt,{esc:h,card:b,rowAction:w,onOpen:()=>n==null?void 0:n(h),forceView:i}):s.jsx("p",{className:"text-xs text-text-tertiary italic",children:"No active item right now."})]}),y=j&&h?s.jsx("div",{className:"mb-8",children:s.jsx($,{content:g(j,T(h))})}):j?s.jsx("div",{className:"mb-8",children:s.jsx($,{content:j})}):null;if(f==="facet-table"){const v=x??r.length;return s.jsxs("div",{children:[s.jsx(ht,{schema:e,rows:r,role:t,onRowClick:n,onAddFacet:l,forceView:i}),c!==void 0&&o!==void 0&&d&&s.jsx(K,{page:c,totalPages:o,onPageChange:d,total:v,pageSize:m??25,onPageSizeChange:u})]})}return f==="facet-board"?s.jsxs("div",{children:[y,s.jsx(jt,{schema:e,rows:r,role:t,onOpenDetail:n,onOpenGroup:a,onAddFacet:l,forceView:i}),c!==void 0&&o!==void 0&&d&&o>1&&s.jsx(K,{page:c,totalPages:o,onPageChange:d,total:x??r.length,pageSize:m??25,onPageSizeChange:u})]}):f==="active-history"?s.jsxs("div",{children:[y,s.jsxs("div",{className:"grid grid-cols-1 @split:grid-cols-[1.6fr_1fr] gap-8 @split:gap-12 items-start",children:[p,s.jsxs("div",{className:"@split:pt-4",children:[s.jsx(P,{children:"History"}),s.jsx(gt,{role:t,def:e["x-lt-history"]??{},onRowClick:n})]})]})]}):s.jsxs("div",{children:[y,p]})}const E=[{maxMs:0,color:"text-status-error"},{maxMs:300*1e3,color:"text-status-error"},{maxMs:900*1e3,color:"text-status-warning"},{maxMs:1/0,color:"text-text-secondary"}];function vt(t){if(t<=0)return E[0].color;for(const e of E)if(t<e.maxMs)return e.color;return E[E.length-1].color}function Ht({until:t}){const[e,r]=k.useState(()=>new Date(t).getTime()-Date.now());k.useEffect(()=>{const a=setInterval(()=>{r(new Date(t).getTime()-Date.now())},1e3);return()=>clearInterval(a)},[t]);const n=k.useMemo(()=>{const a=new Date(t);return[{label:"ms",value:String(Math.max(0,e))},{label:"until",value:a.toISOString()},{label:"local",value:et(a,"MMM d, yyyy h:mm:ss a")}]},[t,e]);return s.jsx(st,{options:n,children:s.jsx("span",{className:`text-xs font-mono ${vt(e)}`,children:rt(e)})})}export{Ht as C,It as E,it as H,At as a,Dt as b,g as c,Lt as d,$t as e,D as g,ct as i,Mt as m,T as r};
|
|
2
|
-
//# sourceMappingURL=CountdownTimer-BhF3oqS8.js.map
|