@hotmeshio/long-tail 0.8.4 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/api/escalations/index.d.ts +1 -1
- package/build/api/escalations/index.js +2 -1
- package/build/api/escalations/resolve.d.ts +28 -0
- package/build/api/escalations/resolve.js +98 -0
- package/build/api/roles.d.ts +21 -0
- package/build/api/roles.js +50 -0
- package/build/lib/db/index.js +12 -1
- package/build/lib/db/migrate.js +27 -0
- package/build/lib/db/schemas/021_role_list_schema.sql +27 -0
- package/build/modules/defaults.d.ts +2 -0
- package/build/modules/defaults.js +3 -1
- package/build/routes/escalations/resolve.js +18 -0
- package/build/routes/roles.js +20 -0
- package/build/sdk/index.d.ts +8 -0
- package/build/sdk/index.js +1 -0
- package/build/services/escalation/crud.d.ts +36 -0
- package/build/services/escalation/crud.js +53 -0
- package/build/services/maintenance/index.js +22 -0
- package/build/services/role/index.d.ts +13 -1
- package/build/services/role/index.js +27 -0
- package/build/services/role/sql.d.ts +14 -2
- package/build/services/role/sql.js +51 -3
- package/build/services/role/types.d.ts +27 -0
- package/build/types/maintenance.d.ts +6 -1
- package/dashboard/dist/assets/{AdminDashboard-B-JptT54.js → AdminDashboard-5bWcBxyS.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-B-JptT54.js.map → AdminDashboard-5bWcBxyS.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-B1Jc6iJk.js → AgentConfigPage-CVg_tFbf.js} +6 -6
- package/dashboard/dist/assets/{AgentConfigPage-B1Jc6iJk.js.map → AgentConfigPage-CVg_tFbf.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-CWpclCOY.js → AgentDetailPage-CLlkIoRQ.js} +4 -4
- package/dashboard/dist/assets/{AgentDetailPage-CWpclCOY.js.map → AgentDetailPage-CLlkIoRQ.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-DyAf5GA2.js → AgentsPage-CjruF_ek.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-DyAf5GA2.js.map → AgentsPage-CjruF_ek.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-h6awiuVk.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-h6awiuVk.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-B1RdaNuY.js → BotPicker-CGEFHHDE.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-B1RdaNuY.js.map → BotPicker-CGEFHHDE.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-CxHDnyjX.js → CapabilitiesPage-BI1tNYun.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-CxHDnyjX.js.map → CapabilitiesPage-BI1tNYun.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-Ds0e8cCO.js → CollapsibleSection-B9f52Z2K.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-Ds0e8cCO.js.map → CollapsibleSection-B9f52Z2K.js.map} +1 -1
- package/dashboard/dist/assets/{CopyableId-blg-gZvT.js → CopyableId-uot2mE_g.js} +2 -2
- package/dashboard/dist/assets/{CopyableId-blg-gZvT.js.map → CopyableId-uot2mE_g.js.map} +1 -1
- package/dashboard/dist/assets/CountdownTimer-C71erQ67.js +2 -0
- package/dashboard/dist/assets/CountdownTimer-C71erQ67.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-D8UVO-3A.js → CredentialsPage-smk5H_mP.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-D8UVO-3A.js.map → CredentialsPage-smk5H_mP.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-ZesYXxCH.js → CronLabel-CCJLmkIq.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-ZesYXxCH.js.map → CronLabel-CCJLmkIq.js.map} +1 -1
- package/dashboard/dist/assets/CustomDurationPicker-DQ28g5GB.js +2 -0
- package/dashboard/dist/assets/CustomDurationPicker-DQ28g5GB.js.map +1 -0
- package/dashboard/dist/assets/DataTable-Byn3oyT9.js +2 -0
- package/dashboard/dist/assets/DataTable-Byn3oyT9.js.map +1 -0
- package/dashboard/dist/assets/{DropZone-SgiK1WOl.js → DropZone-C_Mc4UrO.js} +2 -2
- package/dashboard/dist/assets/{DropZone-SgiK1WOl.js.map → DropZone-C_Mc4UrO.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-CO8S6vdb.js → ElapsedCell-Cw1uvJKx.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-CO8S6vdb.js.map → ElapsedCell-Cw1uvJKx.js.map} +1 -1
- package/dashboard/dist/assets/EscalationListSchemaPage-3v2tiCnq.js +20 -0
- package/dashboard/dist/assets/EscalationListSchemaPage-3v2tiCnq.js.map +1 -0
- package/dashboard/dist/assets/EscalationSchemaPage-DIq2pMED.js +9 -0
- package/dashboard/dist/assets/EscalationSchemaPage-DIq2pMED.js.map +1 -0
- package/dashboard/dist/assets/{EscalationsOverview-DMpRvVql.js → EscalationsOverview-BqOZ1v8w.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-DMpRvVql.js.map → EscalationsOverview-BqOZ1v8w.js.map} +1 -1
- package/dashboard/dist/assets/EventTable-CyfHfOWj.js +2 -0
- package/dashboard/dist/assets/EventTable-CyfHfOWj.js.map +1 -0
- package/dashboard/dist/assets/{FilterBar-B4Mk2PvT.js → FilterBar-DDIn8TOs.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-B4Mk2PvT.js.map → FilterBar-DDIn8TOs.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-CTuul-av.js → GraphInvokePage-DbNUaWhH.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-CTuul-av.js.map → GraphInvokePage-DbNUaWhH.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-BKFpOaHX.js → HomePage-Cj9sLt4Z.js} +2 -2
- package/dashboard/dist/assets/{HomePage-BKFpOaHX.js.map → HomePage-Cj9sLt4Z.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-Dz2_neVF.js → ListToolbar-D7PoQOZC.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-Dz2_neVF.js.map → ListToolbar-D7PoQOZC.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-Do-vaEqV.js → McpOverview-D9g1iJyn.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-Do-vaEqV.js.map → McpOverview-D9g1iJyn.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-HwDGNSbN.js → McpQueryDetailPage-DwS1pXdT.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-HwDGNSbN.js.map → McpQueryDetailPage-DwS1pXdT.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-DxMUpQ7R.js → McpQueryPage-DZQ1z6-H.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-DxMUpQ7R.js.map → McpQueryPage-DZQ1z6-H.js.map} +1 -1
- package/dashboard/dist/assets/McpRunDetailPage-Bx6w39lr.js +2 -0
- package/dashboard/dist/assets/{McpRunDetailPage-BzzZIXIk.js.map → McpRunDetailPage-Bx6w39lr.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-D3J42d2i.js → McpRunsPage-C-oS_tTh.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-D3J42d2i.js.map → McpRunsPage-C-oS_tTh.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-SErKwgkb.js → NamespacePill-DhgC3EUO.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-SErKwgkb.js.map → NamespacePill-DhgC3EUO.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-CQzX6jM1.js +2 -0
- package/dashboard/dist/assets/OperationsPage-CQzX6jM1.js.map +1 -0
- package/dashboard/dist/assets/OperatorDashboard-Cqf87ojd.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-BqJn8wEZ.js.map → OperatorDashboard-Cqf87ojd.js.map} +1 -1
- package/dashboard/dist/assets/PageHeader-sGNTmSXX.js +2 -0
- package/dashboard/dist/assets/PageHeader-sGNTmSXX.js.map +1 -0
- package/dashboard/dist/assets/{PageHeaderWithStats-W3akV6nB.js → PageHeaderWithStats-87STTAJX.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-W3akV6nB.js.map → PageHeaderWithStats-87STTAJX.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-C8sT2xuE.js → ProcessDetailPage-CplnDXkz.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-C8sT2xuE.js.map → ProcessDetailPage-CplnDXkz.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-eA8buUgd.js → ProcessesListPage-DFOJC27f.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-eA8buUgd.js.map → ProcessesListPage-DFOJC27f.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-VrWOaPlg.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-VrWOaPlg.js.map +1 -0
- package/dashboard/dist/assets/{RolePill-DHWixsQR.js → RolePill-C91qbDOW.js} +2 -2
- package/dashboard/dist/assets/{RolePill-DHWixsQR.js.map → RolePill-C91qbDOW.js.map} +1 -1
- package/dashboard/dist/assets/{RolesPage-BQNQxsMY.js → RolesPage-DdI1rVAP.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-BQNQxsMY.js.map → RolesPage-DdI1rVAP.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-mhusrlYE.js → RunAsSelector-C6EozzKV.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-mhusrlYE.js.map → RunAsSelector-C6EozzKV.js.map} +1 -1
- package/dashboard/dist/assets/SlidePanel-BpWveV1T.js +2 -0
- package/dashboard/dist/assets/SlidePanel-BpWveV1T.js.map +1 -0
- package/dashboard/dist/assets/{StatusBadge-DZ9137bI.js → StatusBadge-CEQMRmSQ.js} +2 -2
- package/dashboard/dist/assets/StatusBadge-CEQMRmSQ.js.map +1 -0
- package/dashboard/dist/assets/{StickyPagination-CqT9Q1I6.js → StickyPagination-BEgkYJ0-.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-CqT9Q1I6.js.map → StickyPagination-BEgkYJ0-.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-jOJaoupp.js → StreamMessageDetail-DWVPtzj7.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-jOJaoupp.js.map → StreamMessageDetail-DWVPtzj7.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-BrfmWgAj.js → SwimlaneTimeline-4NrDJNYc.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-BrfmWgAj.js.map → SwimlaneTimeline-4NrDJNYc.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-CGGC3DTR.js → TagInput-Csm36_pS.js} +2 -2
- package/dashboard/dist/assets/{TagInput-CGGC3DTR.js.map → TagInput-Csm36_pS.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-Cus_JHIC.js → TaskDetailPage-BvdaAUtF.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-Cus_JHIC.js.map → TaskDetailPage-BvdaAUtF.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-D6omQJNY.js → TaskQueuePill-5XdNaqwM.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-D6omQJNY.js.map → TaskQueuePill-5XdNaqwM.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-B7GK8AoX.js → TasksListPage-BxA161LH.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-B7GK8AoX.js.map → TasksListPage-BxA161LH.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-C606LzT3.js → TimeAgo-ChwJ4fjZ.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-C606LzT3.js.map → TimeAgo-ChwJ4fjZ.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-hfh6zvH-.js → TimestampCell-C7KzmvyC.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-hfh6zvH-.js.map → TimestampCell-C7KzmvyC.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-BW0iRwZu.js → ToolPill-CAwj7EPw.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-BW0iRwZu.js.map → ToolPill-CAwj7EPw.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-CsPsyvQq.js → ToolTestPanel-ISkuXtkT.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-CsPsyvQq.js.map → ToolTestPanel-ISkuXtkT.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-Diz6r7zr.js → TopicDetailPage-D8uZ8Wbs.js} +3 -3
- package/dashboard/dist/assets/{TopicDetailPage-Diz6r7zr.js.map → TopicDetailPage-D8uZ8Wbs.js.map} +1 -1
- package/dashboard/dist/assets/TopicsPage-rj88vA_1.js +2 -0
- package/dashboard/dist/assets/{TopicsPage-B6fn7fFh.js.map → TopicsPage-rj88vA_1.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-Cc-5QH5q.js → UserName-D03Lh6Aj.js} +2 -2
- package/dashboard/dist/assets/{UserName-Cc-5QH5q.js.map → UserName-D03Lh6Aj.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-LD9WzPee.js +2 -0
- package/dashboard/dist/assets/WorkflowExecutionPage-LD9WzPee.js.map +1 -0
- package/dashboard/dist/assets/{WorkflowPill-BtNBSplz.js → WorkflowPill-fodIWnT3.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-BtNBSplz.js.map → WorkflowPill-fodIWnT3.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-CHCM6hUa.js → WorkflowsDashboard-DH0jitAb.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-CHCM6hUa.js.map → WorkflowsDashboard-DH0jitAb.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-WeOvl8-_.js → WorkflowsOverview-D6BVaH9e.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-WeOvl8-_.js.map → WorkflowsOverview-D6BVaH9e.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-6GfhVDpv.js → YamlWorkflowDetailPage-D_CfNEqW.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-6GfhVDpv.js.map → YamlWorkflowDetailPage-D_CfNEqW.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-DqcY3ocP.js → YamlWorkflowsPage-BMJgtcAq.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-DqcY3ocP.js.map → YamlWorkflowsPage-BMJgtcAq.js.map} +1 -1
- package/dashboard/dist/assets/{agents-DlzvjNxE.js → agents-Ci0tZDO3.js} +2 -2
- package/dashboard/dist/assets/{agents-DlzvjNxE.js.map → agents-Ci0tZDO3.js.map} +1 -1
- package/dashboard/dist/assets/{bots-CWMN-KUI.js → bots-DZyt1oO4.js} +2 -2
- package/dashboard/dist/assets/{bots-CWMN-KUI.js.map → bots-DZyt1oO4.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-_WSZ7cxO.js → capabilities-DzVKeWyC.js} +2 -2
- package/dashboard/dist/assets/{capabilities-_WSZ7cxO.js.map → capabilities-DzVKeWyC.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-_k9nWZy3.js → controlplane-CQzrn-rD.js} +2 -2
- package/dashboard/dist/assets/{controlplane-_k9nWZy3.js.map → controlplane-CQzrn-rD.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-s29Q4mrF.js +2 -0
- package/dashboard/dist/assets/{escalation-columns-Dyrf6Tdp.js.map → escalation-columns-s29Q4mrF.js.map} +1 -1
- package/dashboard/dist/assets/{index-kKufOPLo.js → index-3SrgIA7w.js} +2 -2
- package/dashboard/dist/assets/{index-kKufOPLo.js.map → index-3SrgIA7w.js.map} +1 -1
- package/dashboard/dist/assets/{index-Dd4Si1Rl.js → index-B2dB90aC.js} +2 -2
- package/dashboard/dist/assets/{index-Dd4Si1Rl.js.map → index-B2dB90aC.js.map} +1 -1
- package/dashboard/dist/assets/index-BSOcWfeR.js +5 -0
- package/dashboard/dist/assets/index-BSOcWfeR.js.map +1 -0
- package/dashboard/dist/assets/{index-uUYXmvFf.js → index-BWy4UvaT.js} +2 -2
- package/dashboard/dist/assets/{index-uUYXmvFf.js.map → index-BWy4UvaT.js.map} +1 -1
- package/dashboard/dist/assets/{index-C3h0qGzG.js → index-BqKxJ2fB.js} +2 -2
- package/dashboard/dist/assets/{index-C3h0qGzG.js.map → index-BqKxJ2fB.js.map} +1 -1
- package/dashboard/dist/assets/{index-DwWP-Z_c.js → index-C9moIiIq.js} +5 -5
- package/dashboard/dist/assets/{index-DwWP-Z_c.js.map → index-C9moIiIq.js.map} +1 -1
- package/dashboard/dist/assets/{index-Dc-Ye2wk.js → index-CcKXMU-s.js} +2 -2
- package/dashboard/dist/assets/{index-Dc-Ye2wk.js.map → index-CcKXMU-s.js.map} +1 -1
- package/dashboard/dist/assets/index-DIPe82gq.css +1 -0
- package/dashboard/dist/assets/{index-D8GcHxNJ.js → index-DOHGHMP7.js} +2 -2
- package/dashboard/dist/assets/{index-D8GcHxNJ.js.map → index-DOHGHMP7.js.map} +1 -1
- package/dashboard/dist/assets/{index-C5Yc3TT7.js → index-DTn4XzYN.js} +2 -2
- package/dashboard/dist/assets/{index-C5Yc3TT7.js.map → index-DTn4XzYN.js.map} +1 -1
- package/dashboard/dist/assets/index-Dv8mzG9B.js +2 -0
- package/dashboard/dist/assets/index-Dv8mzG9B.js.map +1 -0
- package/dashboard/dist/assets/{index-Ex5tEte8.js → index-LwodswlU.js} +2 -2
- package/dashboard/dist/assets/{index-Ex5tEte8.js.map → index-LwodswlU.js.map} +1 -1
- package/dashboard/dist/assets/{index-C7VXLKJs.js → index-kPipMHZx.js} +2 -2
- package/dashboard/dist/assets/{index-C7VXLKJs.js.map → index-kPipMHZx.js.map} +1 -1
- package/dashboard/dist/assets/{index-BiApvBEz.js → index-sdc9ecdW.js} +2 -2
- package/dashboard/dist/assets/{index-BiApvBEz.js.map → index-sdc9ecdW.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-fYoEF4Lx.js → knowledge-fi5z5YLq.js} +2 -2
- package/dashboard/dist/assets/{knowledge-fYoEF4Lx.js.map → knowledge-fi5z5YLq.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-1lzlgtS8.js → mcp-CH8Tj9Pw.js} +2 -2
- package/dashboard/dist/assets/{mcp-1lzlgtS8.js.map → mcp-CH8Tj9Pw.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-nlrAz7m_.js → mcp-query-6alwNh7K.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-nlrAz7m_.js.map → mcp-query-6alwNh7K.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-Ihi7EKXi.js → pipelines-Dqh9PLUw.js} +2 -2
- package/dashboard/dist/assets/{pipelines-Ihi7EKXi.js.map → pipelines-Dqh9PLUw.js.map} +1 -1
- package/dashboard/dist/assets/roles-dJKBZUBh.js +2 -0
- package/dashboard/dist/assets/roles-dJKBZUBh.js.map +1 -0
- package/dashboard/dist/assets/{tasks-Crk9Y3C4.js → tasks-BYz490un.js} +2 -2
- package/dashboard/dist/assets/{tasks-Crk9Y3C4.js.map → tasks-BYz490un.js.map} +1 -1
- package/dashboard/dist/assets/{topics-CtfcCyw9.js → topics-Dt975EWi.js} +2 -2
- package/dashboard/dist/assets/{topics-CtfcCyw9.js.map → topics-Dt975EWi.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-Dn_vN2xE.js → useEventHooks-ZT6RV2Gj.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-Dn_vN2xE.js.map → useEventHooks-ZT6RV2Gj.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-Cfs3vKWk.js → useNamespace-B_G1iE9i.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-Cfs3vKWk.js.map → useNamespace-B_G1iE9i.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-ymobxH47.js → useYamlActivityEvents-DGkvIadL.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-ymobxH47.js.map → useYamlActivityEvents-DGkvIadL.js.map} +1 -1
- package/dashboard/dist/assets/{users-DngGIiC_.js → users-C2jopvM1.js} +2 -2
- package/dashboard/dist/assets/{users-DngGIiC_.js.map → users-C2jopvM1.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-DOIDKgo6.js → vendor-icons-BXhWQC7l.js} +161 -121
- package/dashboard/dist/assets/vendor-icons-BXhWQC7l.js.map +1 -0
- package/dashboard/dist/assets/{workflows-DuUspMAl.js → workflows-By8Wgvig.js} +2 -2
- package/dashboard/dist/assets/{workflows-DuUspMAl.js.map → workflows-By8Wgvig.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-DXQCiOsP.js → yaml-workflows-Cq_vp41d.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-DXQCiOsP.js.map → yaml-workflows-Cq_vp41d.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/api/http/escalations.md +20 -0
- package/docs/api/sdk/escalations.md +37 -2
- package/docs/dashboard.md +16 -3
- package/docs/data.md +1 -1
- package/docs/hitl-guide.md +240 -21
- package/docs/maintenance.md +6 -3
- package/docs/oauth-and-delegation.md +1 -1
- package/package.json +3 -3
- package/dashboard/dist/assets/AvailableEscalationsPage-DDwAgklw.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-DDwAgklw.js.map +0 -1
- package/dashboard/dist/assets/CustomDurationPicker-OKlwH5a_.js +0 -2
- package/dashboard/dist/assets/CustomDurationPicker-OKlwH5a_.js.map +0 -1
- package/dashboard/dist/assets/DataTable-ZWgVQg7F.js +0 -2
- package/dashboard/dist/assets/DataTable-ZWgVQg7F.js.map +0 -1
- package/dashboard/dist/assets/EscalationSchemaPage-Br5sD8-N.js +0 -9
- package/dashboard/dist/assets/EscalationSchemaPage-Br5sD8-N.js.map +0 -1
- package/dashboard/dist/assets/EventTable-BARJeiS2.js +0 -2
- package/dashboard/dist/assets/EventTable-BARJeiS2.js.map +0 -1
- package/dashboard/dist/assets/McpRunDetailPage-BzzZIXIk.js +0 -2
- package/dashboard/dist/assets/OperationsPage-BaM-QDhy.js +0 -2
- package/dashboard/dist/assets/OperationsPage-BaM-QDhy.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-BqJn8wEZ.js +0 -2
- package/dashboard/dist/assets/PageHeader-DJo7yMUR.js +0 -2
- package/dashboard/dist/assets/PageHeader-DJo7yMUR.js.map +0 -1
- package/dashboard/dist/assets/RoleDetailPage-aR-ror5v.js +0 -11
- package/dashboard/dist/assets/RoleDetailPage-aR-ror5v.js.map +0 -1
- package/dashboard/dist/assets/StatusBadge-DZ9137bI.js.map +0 -1
- package/dashboard/dist/assets/TopicsPage-B6fn7fFh.js +0 -2
- package/dashboard/dist/assets/WorkflowExecutionPage-BFq7UdhA.js +0 -2
- package/dashboard/dist/assets/WorkflowExecutionPage-BFq7UdhA.js.map +0 -1
- package/dashboard/dist/assets/escalation-CK8ElYzD.js +0 -2
- package/dashboard/dist/assets/escalation-CK8ElYzD.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-Dyrf6Tdp.js +0 -2
- package/dashboard/dist/assets/index-BdqKh0UK.css +0 -1
- package/dashboard/dist/assets/index-OV1JoB_P.js +0 -6
- package/dashboard/dist/assets/index-OV1JoB_P.js.map +0 -1
- package/dashboard/dist/assets/index-ZBj6Xgim.js +0 -2
- package/dashboard/dist/assets/index-ZBj6Xgim.js.map +0 -1
- package/dashboard/dist/assets/roles-D4Wn7kE9.js +0 -2
- package/dashboard/dist/assets/roles-D4Wn7kE9.js.map +0 -1
- package/dashboard/dist/assets/useCollapsedSections-BU5HULGs.js +0 -2
- package/dashboard/dist/assets/useCollapsedSections-BU5HULGs.js.map +0 -1
- package/dashboard/dist/assets/vendor-icons-DOIDKgo6.js.map +0 -1
|
@@ -3,7 +3,7 @@ export { listEscalations, listAvailableEscalations, listDistinctTypes, listFacet
|
|
|
3
3
|
export { getEscalation, getEscalationsByWorkflowId, escalateToRole } from './single';
|
|
4
4
|
export { claimEscalation, releaseEscalation } from './claim';
|
|
5
5
|
export { releaseExpiredClaims, updatePriority, bulkClaim, bulkAssign, bulkEscalate, bulkTriage } from './bulk';
|
|
6
|
-
export { resolveEscalation, resolveBySignalKey, resolveByIds } from './resolve';
|
|
6
|
+
export { resolveEscalation, resolveBySignalKey, resolveByIds, resolveAllOrNone } from './resolve';
|
|
7
7
|
export { findByMetadata, claimByMetadata, resolveByMetadata } from './metadata';
|
|
8
8
|
export { cancelSingleEscalation, bulkCancel } from './cancel';
|
|
9
9
|
export { searchByFacets, claimGroups, claimByFacets } from './facets';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.claimByFacets = exports.claimGroups = exports.searchByFacets = exports.bulkCancel = exports.cancelSingleEscalation = exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveByIds = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getStationMetrics = exports.getEscalationStats = exports.listFacetKeys = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
|
|
3
|
+
exports.claimByFacets = exports.claimGroups = exports.searchByFacets = exports.bulkCancel = exports.cancelSingleEscalation = exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveAllOrNone = exports.resolveByIds = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getStationMetrics = exports.getEscalationStats = exports.listFacetKeys = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
|
|
4
4
|
var create_1 = require("./create");
|
|
5
5
|
Object.defineProperty(exports, "createEscalation", { enumerable: true, get: function () { return create_1.createEscalation; } });
|
|
6
6
|
var list_1 = require("./list");
|
|
@@ -28,6 +28,7 @@ var resolve_1 = require("./resolve");
|
|
|
28
28
|
Object.defineProperty(exports, "resolveEscalation", { enumerable: true, get: function () { return resolve_1.resolveEscalation; } });
|
|
29
29
|
Object.defineProperty(exports, "resolveBySignalKey", { enumerable: true, get: function () { return resolve_1.resolveBySignalKey; } });
|
|
30
30
|
Object.defineProperty(exports, "resolveByIds", { enumerable: true, get: function () { return resolve_1.resolveByIds; } });
|
|
31
|
+
Object.defineProperty(exports, "resolveAllOrNone", { enumerable: true, get: function () { return resolve_1.resolveAllOrNone; } });
|
|
31
32
|
var metadata_1 = require("./metadata");
|
|
32
33
|
Object.defineProperty(exports, "findByMetadata", { enumerable: true, get: function () { return metadata_1.findByMetadata; } });
|
|
33
34
|
Object.defineProperty(exports, "claimByMetadata", { enumerable: true, get: function () { return metadata_1.claimByMetadata; } });
|
|
@@ -44,3 +44,31 @@ export declare function resolveByIds(input: {
|
|
|
44
44
|
resolverPayload: Record<string, any>;
|
|
45
45
|
metadata?: Record<string, any>;
|
|
46
46
|
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
47
|
+
/**
|
|
48
|
+
* Atomic all-or-none bulk resolve with PER-ROW payloads — the gang-handoff
|
|
49
|
+
* sibling of {@link resolveByIds}. Every listed escalation must be pending and
|
|
50
|
+
* writable by the caller, or NOTHING resolves. Unlike resolveByIds, rows
|
|
51
|
+
* backing a live `condition()` waiter are first-class: each is woken with its
|
|
52
|
+
* own payload inside the single atomic statement (same wake contract as
|
|
53
|
+
* single resolve).
|
|
54
|
+
*
|
|
55
|
+
* RBAC matches resolveByIds: per-item write scope, 404 non-disclosure when any
|
|
56
|
+
* id is missing or out of scope, nothing resolved. `requireClaimed` adds a
|
|
57
|
+
* SQL-level assertion that every row is currently assigned to the caller —
|
|
58
|
+
* for claim-then-resolve flows (a broker resolving its claimed gang), this
|
|
59
|
+
* closes the window where another principal re-claims a member mid-flight.
|
|
60
|
+
*
|
|
61
|
+
* Rows whose resolution requires a non-atomic path (legacy signal routing via
|
|
62
|
+
* `metadata.signal_id` / `metadata.signal_routing`, which need a separate
|
|
63
|
+
* workflow signal) block the batch with 409 — bulk-resolving them would strand
|
|
64
|
+
* their workflows. Re-run/triage rows (workflow_type without any signal shape)
|
|
65
|
+
* resolve as bookkeeping acknowledgements, matching resolveByIds semantics.
|
|
66
|
+
*/
|
|
67
|
+
export declare function resolveAllOrNone(input: {
|
|
68
|
+
items: Array<{
|
|
69
|
+
id: string;
|
|
70
|
+
resolverPayload: Record<string, any>;
|
|
71
|
+
}>;
|
|
72
|
+
metadata?: Record<string, any>;
|
|
73
|
+
requireClaimed?: boolean;
|
|
74
|
+
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.resolveEscalation = resolveEscalation;
|
|
37
37
|
exports.resolveBySignalKey = resolveBySignalKey;
|
|
38
38
|
exports.resolveByIds = resolveByIds;
|
|
39
|
+
exports.resolveAllOrNone = resolveAllOrNone;
|
|
39
40
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
40
41
|
const taskService = __importStar(require("../../services/task"));
|
|
41
42
|
const escalation_strategy_1 = require("../../services/escalation-strategy");
|
|
@@ -202,6 +203,103 @@ async function resolveByIds(input, auth) {
|
|
|
202
203
|
return { status: 500, error: err.message };
|
|
203
204
|
}
|
|
204
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Atomic all-or-none bulk resolve with PER-ROW payloads — the gang-handoff
|
|
208
|
+
* sibling of {@link resolveByIds}. Every listed escalation must be pending and
|
|
209
|
+
* writable by the caller, or NOTHING resolves. Unlike resolveByIds, rows
|
|
210
|
+
* backing a live `condition()` waiter are first-class: each is woken with its
|
|
211
|
+
* own payload inside the single atomic statement (same wake contract as
|
|
212
|
+
* single resolve).
|
|
213
|
+
*
|
|
214
|
+
* RBAC matches resolveByIds: per-item write scope, 404 non-disclosure when any
|
|
215
|
+
* id is missing or out of scope, nothing resolved. `requireClaimed` adds a
|
|
216
|
+
* SQL-level assertion that every row is currently assigned to the caller —
|
|
217
|
+
* for claim-then-resolve flows (a broker resolving its claimed gang), this
|
|
218
|
+
* closes the window where another principal re-claims a member mid-flight.
|
|
219
|
+
*
|
|
220
|
+
* Rows whose resolution requires a non-atomic path (legacy signal routing via
|
|
221
|
+
* `metadata.signal_id` / `metadata.signal_routing`, which need a separate
|
|
222
|
+
* workflow signal) block the batch with 409 — bulk-resolving them would strand
|
|
223
|
+
* their workflows. Re-run/triage rows (workflow_type without any signal shape)
|
|
224
|
+
* resolve as bookkeeping acknowledgements, matching resolveByIds semantics.
|
|
225
|
+
*/
|
|
226
|
+
async function resolveAllOrNone(input, auth) {
|
|
227
|
+
try {
|
|
228
|
+
const { items, metadata, requireClaimed } = input;
|
|
229
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
230
|
+
return { status: 400, error: 'items must be a non-empty array' };
|
|
231
|
+
}
|
|
232
|
+
if (items.length > defaults_1.ESCALATION_BULK_RESOLVE_MAX) {
|
|
233
|
+
return { status: 400, error: `items exceeds the maximum of ${defaults_1.ESCALATION_BULK_RESOLVE_MAX}` };
|
|
234
|
+
}
|
|
235
|
+
const wellFormed = items.every((i) => i && typeof i.id === 'string' && i.id.length > 0
|
|
236
|
+
&& i.resolverPayload && typeof i.resolverPayload === 'object');
|
|
237
|
+
if (!wellFormed) {
|
|
238
|
+
return { status: 400, error: 'every item requires an id and a resolverPayload object' };
|
|
239
|
+
}
|
|
240
|
+
const ids = items.map((i) => i.id);
|
|
241
|
+
if (new Set(ids).size !== ids.length) {
|
|
242
|
+
return { status: 400, error: 'items must not repeat ids' };
|
|
243
|
+
}
|
|
244
|
+
// One indexed read serves every pre-flight gate below (scope, path shape,
|
|
245
|
+
// redaction schema). Role and signal routing are immutable per escalation,
|
|
246
|
+
// so none of these checks race the guarded statement that follows — and
|
|
247
|
+
// row STATE (pending, assignee) is re-asserted inside that statement.
|
|
248
|
+
const rows = await escalationService.getEscalationsByIds(ids);
|
|
249
|
+
const rowById = new Map(rows.map((r) => [r.id, r]));
|
|
250
|
+
// Per-item write scope, same rule as resolveByIds: write_all roles may
|
|
251
|
+
// resolve any item in the role; write_self only items assigned to the
|
|
252
|
+
// caller. A missing id or any out-of-scope item → 404 (non-disclosure),
|
|
253
|
+
// nothing resolved. Global bypasses; the store then reports precise
|
|
254
|
+
// per-row blockers (including not-found) via the 409 below.
|
|
255
|
+
const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
|
|
256
|
+
if (!writeScope.global) {
|
|
257
|
+
const allSet = new Set(writeScope.allRoles);
|
|
258
|
+
const selfSet = new Set(writeScope.selfRoles);
|
|
259
|
+
const writable = (r) => (!!r.role && allSet.has(r.role)) ||
|
|
260
|
+
(!!r.role && selfSet.has(r.role) && r.assigned_to === auth.userId);
|
|
261
|
+
if (rows.length !== ids.length || !rows.every(writable)) {
|
|
262
|
+
return { status: 404, error: 'One or more escalations not found' };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Fail loud on rows the atomic statement cannot wake: legacy signal shapes
|
|
266
|
+
// resume their workflow via a separate handle.signal, so bulk-resolving the
|
|
267
|
+
// row would mark it settled while the workflow stays parked forever.
|
|
268
|
+
const unsupported = rows.filter((r) => {
|
|
269
|
+
const meta = (r.metadata ?? {});
|
|
270
|
+
return !r.signal_key && (meta.signal_id || meta.signal_routing?.signalId);
|
|
271
|
+
});
|
|
272
|
+
if (unsupported.length > 0) {
|
|
273
|
+
const failed = unsupported.map((r) => ({ id: r.id, reason: 'unsupported-resolution-path' }));
|
|
274
|
+
// error rides inside data too: routes serialize `data ?? { error }`, and
|
|
275
|
+
// the 409 body must carry BOTH the message and the blocking ids
|
|
276
|
+
const error = 'One or more escalations require the single-resolve path (legacy signal routing)';
|
|
277
|
+
return { status: 409, error, data: { error, failedIds: failed.map((f) => f.id), failed } };
|
|
278
|
+
}
|
|
279
|
+
// Per-row password redaction against each row's own form schema — the
|
|
280
|
+
// payload enters the signal store and the audit record; plaintext never.
|
|
281
|
+
const serviceItems = await Promise.all(items.map(async (item) => ({
|
|
282
|
+
id: item.id,
|
|
283
|
+
resolverPayload: await redactPasswords(item.resolverPayload, rowById.get(item.id)?.metadata?.form_schema),
|
|
284
|
+
})));
|
|
285
|
+
const result = await escalationService.resolveEscalationsAllOrNone(serviceItems, metadata, requireClaimed ? auth.userId : undefined);
|
|
286
|
+
if (!result.ok) {
|
|
287
|
+
const error = 'One or more escalations blocked the batch; nothing was resolved';
|
|
288
|
+
return {
|
|
289
|
+
status: 409,
|
|
290
|
+
error,
|
|
291
|
+
data: { error, failedIds: result.failed.map((f) => f.id), failed: result.failed },
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
status: 200,
|
|
296
|
+
data: { resolved: result.escalations.length, escalationIds: result.escalations.map((e) => e.id) },
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
return { status: 500, error: err.message };
|
|
301
|
+
}
|
|
302
|
+
}
|
|
205
303
|
// ── Resolution paths ─────────────────────────────────────────────────────
|
|
206
304
|
/** Path A: lightweight conditionLT signal — inject $escalation_id and signal the running workflow. */
|
|
207
305
|
async function resolveViaConditionSignal(escalation, resolverPayload, metadata) {
|
package/build/api/roles.d.ts
CHANGED
|
@@ -86,6 +86,8 @@ export declare function updateRole(input: {
|
|
|
86
86
|
/** The escalation resolve FORM schema (the JIT UI). Fields may carry x-lt-bind. */
|
|
87
87
|
form_schema?: Record<string, any> | null;
|
|
88
88
|
metadata_schema?: Record<string, any> | null;
|
|
89
|
+
/** The escalation LIST schema — rich formatting for this role's list page. */
|
|
90
|
+
list_schema?: Record<string, any> | null;
|
|
89
91
|
properties?: Record<string, any> | null;
|
|
90
92
|
ops_visible?: boolean;
|
|
91
93
|
parent_role?: string | null;
|
|
@@ -129,6 +131,25 @@ export declare function getRoleSchema(input: {
|
|
|
129
131
|
export declare function listRoleSchemaVersions(input: {
|
|
130
132
|
role: string;
|
|
131
133
|
}): Promise<LTApiResult>;
|
|
134
|
+
/**
|
|
135
|
+
* Fetch a role's LIST schema (independent version lineage from the form schema).
|
|
136
|
+
* With `version`, returns that immutable snapshot — a missing version is a 404.
|
|
137
|
+
* Without `version`, returns the live (latest) list schema with its version.
|
|
138
|
+
*
|
|
139
|
+
* @returns `{ status: 200, data: RoleListSchemaVersion }` on success
|
|
140
|
+
*/
|
|
141
|
+
export declare function getRoleListSchema(input: {
|
|
142
|
+
role: string;
|
|
143
|
+
version?: number;
|
|
144
|
+
}): Promise<LTApiResult>;
|
|
145
|
+
/**
|
|
146
|
+
* List a role's LIST schema version history, newest first.
|
|
147
|
+
*
|
|
148
|
+
* @returns `{ status: 200, data: { versions: RoleListSchemaVersionSummary[] } }`
|
|
149
|
+
*/
|
|
150
|
+
export declare function listRoleListSchemaVersions(input: {
|
|
151
|
+
role: string;
|
|
152
|
+
}): Promise<LTApiResult>;
|
|
132
153
|
/**
|
|
133
154
|
* Delete a role from the system. Requires admin privileges.
|
|
134
155
|
*
|
package/build/api/roles.js
CHANGED
|
@@ -44,6 +44,8 @@ exports.replaceEscalationTargets = replaceEscalationTargets;
|
|
|
44
44
|
exports.updateRole = updateRole;
|
|
45
45
|
exports.getRoleSchema = getRoleSchema;
|
|
46
46
|
exports.listRoleSchemaVersions = listRoleSchemaVersions;
|
|
47
|
+
exports.getRoleListSchema = getRoleListSchema;
|
|
48
|
+
exports.listRoleListSchemaVersions = listRoleListSchemaVersions;
|
|
47
49
|
exports.deleteRole = deleteRole;
|
|
48
50
|
const roleService = __importStar(require("../services/role"));
|
|
49
51
|
const facet_sql_1 = require("../services/escalation/facet-sql");
|
|
@@ -244,6 +246,7 @@ async function updateRole(input) {
|
|
|
244
246
|
description: input.description,
|
|
245
247
|
form_schema: input.form_schema,
|
|
246
248
|
metadata_schema: input.metadata_schema,
|
|
249
|
+
list_schema: input.list_schema,
|
|
247
250
|
properties: input.properties,
|
|
248
251
|
ops_visible: input.ops_visible,
|
|
249
252
|
parent_role: input.parent_role,
|
|
@@ -320,6 +323,53 @@ async function listRoleSchemaVersions(input) {
|
|
|
320
323
|
return { status: 500, error: err.message };
|
|
321
324
|
}
|
|
322
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Fetch a role's LIST schema (independent version lineage from the form schema).
|
|
328
|
+
* With `version`, returns that immutable snapshot — a missing version is a 404.
|
|
329
|
+
* Without `version`, returns the live (latest) list schema with its version.
|
|
330
|
+
*
|
|
331
|
+
* @returns `{ status: 200, data: RoleListSchemaVersion }` on success
|
|
332
|
+
*/
|
|
333
|
+
async function getRoleListSchema(input) {
|
|
334
|
+
try {
|
|
335
|
+
if (!input.role) {
|
|
336
|
+
return { status: 400, error: 'role is required' };
|
|
337
|
+
}
|
|
338
|
+
if (input.version !== undefined && (!Number.isInteger(input.version) || input.version < 1)) {
|
|
339
|
+
return { status: 400, error: 'version must be a positive integer' };
|
|
340
|
+
}
|
|
341
|
+
const schema = await roleService.getRoleListSchema(input.role, input.version);
|
|
342
|
+
if (!schema) {
|
|
343
|
+
return {
|
|
344
|
+
status: 404,
|
|
345
|
+
error: input.version !== undefined
|
|
346
|
+
? `List schema version ${input.version} not found for role '${input.role}'`
|
|
347
|
+
: `Role '${input.role}' not found`,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
return { status: 200, data: schema };
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
return { status: 500, error: err.message };
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* List a role's LIST schema version history, newest first.
|
|
358
|
+
*
|
|
359
|
+
* @returns `{ status: 200, data: { versions: RoleListSchemaVersionSummary[] } }`
|
|
360
|
+
*/
|
|
361
|
+
async function listRoleListSchemaVersions(input) {
|
|
362
|
+
try {
|
|
363
|
+
if (!input.role) {
|
|
364
|
+
return { status: 400, error: 'role is required' };
|
|
365
|
+
}
|
|
366
|
+
const versions = await roleService.listRoleListSchemaVersions(input.role);
|
|
367
|
+
return { status: 200, data: { versions } };
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
return { status: 500, error: err.message };
|
|
371
|
+
}
|
|
372
|
+
}
|
|
323
373
|
/**
|
|
324
374
|
* Delete a role from the system. Requires admin privileges.
|
|
325
375
|
*
|
package/build/lib/db/index.js
CHANGED
|
@@ -8,7 +8,18 @@ const config_1 = require("../../modules/config");
|
|
|
8
8
|
let pool = null;
|
|
9
9
|
function getPool() {
|
|
10
10
|
if (!pool) {
|
|
11
|
-
pool = new pg_1.Pool(
|
|
11
|
+
pool = new pg_1.Pool({
|
|
12
|
+
...config_1.postgres_options,
|
|
13
|
+
// Long-tail's shared tables live in public. Postgres's default
|
|
14
|
+
// search_path is `"$user", public`: if any schema shares the connecting
|
|
15
|
+
// role's name (e.g. a HotMesh app schema named after the DB user), every
|
|
16
|
+
// unqualified statement — including CREATE TABLE in migrations — silently
|
|
17
|
+
// resolves there instead of public, forking the data across two schemas.
|
|
18
|
+
// Pinning the session search_path makes resolution independent of role
|
|
19
|
+
// and schema naming. HotMesh connections (getConnection) are unaffected:
|
|
20
|
+
// the engine fully qualifies its app schemas.
|
|
21
|
+
options: '-c search_path=public',
|
|
22
|
+
});
|
|
12
23
|
}
|
|
13
24
|
return pool;
|
|
14
25
|
}
|
package/build/lib/db/migrate.js
CHANGED
|
@@ -47,7 +47,34 @@ async function migrate() {
|
|
|
47
47
|
// Uses a dedicated client so the lock is held for the entire sequence.
|
|
48
48
|
const client = await pool.connect();
|
|
49
49
|
try {
|
|
50
|
+
// Migrations define the shared public tables, so resolution is pinned on
|
|
51
|
+
// this client even if a future caller wires a pool without the pool-level
|
|
52
|
+
// search_path option. Without this, a schema named after the DB role
|
|
53
|
+
// (default search_path `"$user", public`) captures every unqualified
|
|
54
|
+
// CREATE TABLE below.
|
|
55
|
+
await client.query('SET search_path TO public');
|
|
50
56
|
await client.query('SELECT pg_advisory_lock(8675309)');
|
|
57
|
+
// Fail loudly on a pre-existing split: an lt_migrations table in any other
|
|
58
|
+
// schema means a prior boot ran with `"$user"` shadowing public and forked
|
|
59
|
+
// the tables. Booting through it would either fork the data further or
|
|
60
|
+
// re-run every migration against empty public tables while the live rows
|
|
61
|
+
// sit in the shadow schema. An operator must consolidate first (move the
|
|
62
|
+
// live lt_* tables to public, archive the stale copies).
|
|
63
|
+
const drift = await client.query(`
|
|
64
|
+
SELECT n.nspname
|
|
65
|
+
FROM pg_class c
|
|
66
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
67
|
+
WHERE c.relname = 'lt_migrations' AND c.relkind = 'r' AND n.nspname <> 'public'
|
|
68
|
+
`);
|
|
69
|
+
if (drift.rows.length > 0) {
|
|
70
|
+
const schemas = drift.rows.map((r) => r.nspname).join(', ');
|
|
71
|
+
throw new Error(`[migrate] lt_migrations exists in non-public schema(s): ${schemas}. ` +
|
|
72
|
+
'Long-tail tables must live in the public schema. This usually means the ' +
|
|
73
|
+
'database user shares a name with another schema, so the default ' +
|
|
74
|
+
'search_path ("$user", public) captured earlier migrations. Consolidate ' +
|
|
75
|
+
'the schemas (ALTER TABLE ... SET SCHEMA public for the live lt_* tables, ' +
|
|
76
|
+
'archive stale public copies) before starting the app.');
|
|
77
|
+
}
|
|
51
78
|
// ensure migration tracking table
|
|
52
79
|
await client.query(`
|
|
53
80
|
CREATE TABLE IF NOT EXISTS lt_migrations (
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-- Migration 021: Role escalations LIST schema
|
|
2
|
+
--
|
|
3
|
+
-- list_schema is the list-page analog of form_schema: it richly formats the
|
|
4
|
+
-- escalation LIST page when the list is scoped to exactly one role that owns a
|
|
5
|
+
-- list_schema (opt-in — absent leaves the engineer table unchanged). Unlike
|
|
6
|
+
-- form_schema it is NOT pinned per-escalation; the list always renders the
|
|
7
|
+
-- role's latest.
|
|
8
|
+
--
|
|
9
|
+
-- It versions INDEPENDENTLY of form_schema / metadata_schema: its own snapshot
|
|
10
|
+
-- table (lt_role_list_schemas) and its own counter (current_list_schema_version),
|
|
11
|
+
-- so editing the list view never advances the resolve form's version. The write
|
|
12
|
+
-- rides the same atomic UPDATE_ROLE_METADATA statement (services/role/sql.ts),
|
|
13
|
+
-- which bumps current_list_schema_version and appends a snapshot only when the
|
|
14
|
+
-- provided list_schema actually differs.
|
|
15
|
+
|
|
16
|
+
ALTER TABLE lt_roles
|
|
17
|
+
ADD COLUMN IF NOT EXISTS list_schema JSONB,
|
|
18
|
+
ADD COLUMN IF NOT EXISTS current_list_schema_version INTEGER;
|
|
19
|
+
|
|
20
|
+
CREATE TABLE IF NOT EXISTS lt_role_list_schemas (
|
|
21
|
+
role TEXT NOT NULL REFERENCES lt_roles(role) ON DELETE CASCADE,
|
|
22
|
+
version INTEGER NOT NULL,
|
|
23
|
+
list_schema JSONB,
|
|
24
|
+
change_summary TEXT,
|
|
25
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
26
|
+
PRIMARY KEY (role, version)
|
|
27
|
+
);
|
|
@@ -49,6 +49,8 @@ export declare const QUERY_LIMIT_MAX: number;
|
|
|
49
49
|
export declare const YAML_LIST_LIMIT: number;
|
|
50
50
|
/** Default page size for YAML workflow version history. */
|
|
51
51
|
export declare const YAML_VERSION_LIMIT: number;
|
|
52
|
+
/** Maximum items per atomic bulk resolve (POST /escalations/resolve-all-or-none). */
|
|
53
|
+
export declare const ESCALATION_BULK_RESOLVE_MAX: number;
|
|
52
54
|
/**
|
|
53
55
|
* Claim duration presets shown in the dashboard (minutes).
|
|
54
56
|
* Override via JSON array, e.g. LT_CLAIM_DURATION_OPTIONS='[15,30,60,480]'
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Grouped by domain so related knobs live together.
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.YAML_LINE_WIDTH = exports.STOP_WORDS = exports.CLAIM_DURATION_OPTIONS = exports.YAML_VERSION_LIMIT = exports.YAML_LIST_LIMIT = exports.QUERY_LIMIT_MAX = exports.QUERY_LIMIT_DEFAULT = exports.CONFIG_CACHE_TTL_MS = exports.MCP_TOOL_TIMEOUT_MS = exports.WORKFLOW_SYNC_TIMEOUT_MS = exports.JOB_EXPIRE_SECS = exports.WORKFLOW_EXPIRE_SECS = exports.TOOL_ARG_LIMIT_CAP = exports.TOOL_ROUNDS_MCP_QUERY = exports.TOOL_ROUNDS_TRIAGE = exports.TOOL_RESULT_MAX_CHARS = exports.LLM_MAX_INPUT_CHARS = exports.LLM_MAX_ARRAY_ITEMS = exports.LLM_MAX_TOKENS_VISION = exports.LLM_MAX_TOKENS_JSON = exports.LLM_MAX_TOKENS_DEFAULT = exports.LLM_BASE_URL = exports.LLM_MODEL_SECONDARY = exports.LLM_MODEL_PRIMARY = void 0;
|
|
12
|
+
exports.YAML_LINE_WIDTH = exports.STOP_WORDS = exports.CLAIM_DURATION_OPTIONS = exports.ESCALATION_BULK_RESOLVE_MAX = exports.YAML_VERSION_LIMIT = exports.YAML_LIST_LIMIT = exports.QUERY_LIMIT_MAX = exports.QUERY_LIMIT_DEFAULT = exports.CONFIG_CACHE_TTL_MS = exports.MCP_TOOL_TIMEOUT_MS = exports.WORKFLOW_SYNC_TIMEOUT_MS = exports.JOB_EXPIRE_SECS = exports.WORKFLOW_EXPIRE_SECS = exports.TOOL_ARG_LIMIT_CAP = exports.TOOL_ROUNDS_MCP_QUERY = exports.TOOL_ROUNDS_TRIAGE = exports.TOOL_RESULT_MAX_CHARS = exports.LLM_MAX_INPUT_CHARS = exports.LLM_MAX_ARRAY_ITEMS = exports.LLM_MAX_TOKENS_VISION = exports.LLM_MAX_TOKENS_JSON = exports.LLM_MAX_TOKENS_DEFAULT = exports.LLM_BASE_URL = exports.LLM_MODEL_SECONDARY = exports.LLM_MODEL_PRIMARY = void 0;
|
|
13
13
|
function envInt(key, fallback) {
|
|
14
14
|
const v = process.env[key];
|
|
15
15
|
if (v === undefined || v === '')
|
|
@@ -67,6 +67,8 @@ exports.YAML_LIST_LIMIT = envInt('LT_YAML_LIST_LIMIT', 50);
|
|
|
67
67
|
/** Default page size for YAML workflow version history. */
|
|
68
68
|
exports.YAML_VERSION_LIMIT = envInt('LT_YAML_VERSION_LIMIT', 20);
|
|
69
69
|
// ── Escalation ──────────────────────────────────────────────────────
|
|
70
|
+
/** Maximum items per atomic bulk resolve (POST /escalations/resolve-all-or-none). */
|
|
71
|
+
exports.ESCALATION_BULK_RESOLVE_MAX = envInt('LT_ESCALATION_BULK_RESOLVE_MAX', 100);
|
|
70
72
|
/**
|
|
71
73
|
* Claim duration presets shown in the dashboard (minutes).
|
|
72
74
|
* Override via JSON array, e.g. LT_CLAIM_DURATION_OPTIONS='[15,30,60,480]'
|
|
@@ -66,6 +66,24 @@ function registerResolveRoutes(router) {
|
|
|
66
66
|
}, req.auth);
|
|
67
67
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
68
68
|
});
|
|
69
|
+
/**
|
|
70
|
+
* POST /api/escalations/resolve-all-or-none
|
|
71
|
+
* Atomic bulk resolve with per-row payloads: every listed escalation resolves
|
|
72
|
+
* with its own resolverPayload in one statement, or nothing resolves. Rows
|
|
73
|
+
* backing a live condition() waiter are woken with their own payload (same
|
|
74
|
+
* wake contract as /:id/resolve). 409 returns { error, failedIds, failed }
|
|
75
|
+
* naming exactly the rows that blocked the batch. Max 100 items per call.
|
|
76
|
+
* Body: { items: Array<{ id: string, resolverPayload: Record<string, any> }>,
|
|
77
|
+
* metadata?: Record<string, any>, requireClaimed?: boolean }
|
|
78
|
+
*/
|
|
79
|
+
router.post('/resolve-all-or-none', async (req, res) => {
|
|
80
|
+
const result = await api.resolveAllOrNone({
|
|
81
|
+
items: req.body?.items,
|
|
82
|
+
metadata: req.body?.metadata,
|
|
83
|
+
requireClaimed: req.body?.requireClaimed,
|
|
84
|
+
}, req.auth);
|
|
85
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
86
|
+
});
|
|
69
87
|
/**
|
|
70
88
|
* POST /api/escalations/:id/resolve
|
|
71
89
|
* Resolve a pending escalation with a human-provided payload. Routes by
|
package/build/routes/roles.js
CHANGED
|
@@ -123,6 +123,26 @@ router.get('/:role/schema/versions', async (req, res) => {
|
|
|
123
123
|
const result = await api.listRoleSchemaVersions({ role: req.params.role });
|
|
124
124
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
125
125
|
});
|
|
126
|
+
/**
|
|
127
|
+
* GET /api/roles/:role/list-schema
|
|
128
|
+
* Fetch the role's LIST schema (rich list-page formatting). `?version=N` pins a
|
|
129
|
+
* snapshot; omitted, the live (latest) list schema is returned with its version.
|
|
130
|
+
* Versions independently from the resolve form schema.
|
|
131
|
+
*/
|
|
132
|
+
router.get('/:role/list-schema', async (req, res) => {
|
|
133
|
+
const versionParam = req.query.version;
|
|
134
|
+
const version = versionParam !== undefined ? Number(versionParam) : undefined;
|
|
135
|
+
const result = await api.getRoleListSchema({ role: req.params.role, version });
|
|
136
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
137
|
+
});
|
|
138
|
+
/**
|
|
139
|
+
* GET /api/roles/:role/list-schema/versions
|
|
140
|
+
* List the role's LIST schema version history (newest first).
|
|
141
|
+
*/
|
|
142
|
+
router.get('/:role/list-schema/versions', async (req, res) => {
|
|
143
|
+
const result = await api.listRoleListSchemaVersions({ role: req.params.role });
|
|
144
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
145
|
+
});
|
|
126
146
|
/**
|
|
127
147
|
* GET /api/roles/:role/escalation-targets
|
|
128
148
|
* Get allowed escalation targets for a specific role.
|
package/build/sdk/index.d.ts
CHANGED
|
@@ -153,6 +153,14 @@ export declare function createClient(options?: LTClientOptions): {
|
|
|
153
153
|
resolverPayload: Record<string, any>;
|
|
154
154
|
metadata?: Record<string, any>;
|
|
155
155
|
}, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
|
|
156
|
+
resolveAllOrNone: (input: {
|
|
157
|
+
items: Array<{
|
|
158
|
+
id: string;
|
|
159
|
+
resolverPayload: Record<string, any>;
|
|
160
|
+
}>;
|
|
161
|
+
metadata?: Record<string, any>;
|
|
162
|
+
requireClaimed?: boolean;
|
|
163
|
+
}, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
|
|
156
164
|
releaseExpired: typeof escalationsApi.releaseExpiredClaims;
|
|
157
165
|
updatePriority: (input: {
|
|
158
166
|
ids: string[];
|
package/build/sdk/index.js
CHANGED
|
@@ -128,6 +128,7 @@ function createClient(options = {}) {
|
|
|
128
128
|
resolve: bindAuth(escalationsApi.resolveEscalation, auth),
|
|
129
129
|
resolveBySignalKey: bindAuth(escalationsApi.resolveBySignalKey, auth),
|
|
130
130
|
resolveByIds: bindAuth(escalationsApi.resolveByIds, auth),
|
|
131
|
+
resolveAllOrNone: bindAuth(escalationsApi.resolveAllOrNone, auth),
|
|
131
132
|
releaseExpired: escalationsApi.releaseExpiredClaims,
|
|
132
133
|
updatePriority: bindAuth(escalationsApi.updatePriority, auth),
|
|
133
134
|
bulkClaim: bindAuth(escalationsApi.bulkClaim, auth),
|
|
@@ -36,6 +36,42 @@ export declare function resolveEscalation(id: string, resolverPayload: Record<st
|
|
|
36
36
|
* `resolveEscalation` (which delivers the signal).
|
|
37
37
|
*/
|
|
38
38
|
export declare function resolveEscalationsByIds(ids: string[], resolverPayload: Record<string, any>, metadata?: Record<string, any>): Promise<LTEscalationRecord[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Resolve a SET of escalations atomically — every listed row resolves, each
|
|
41
|
+
* with its OWN resolverPayload, or none do. One SQL statement locks the rows
|
|
42
|
+
* in deterministic id order, applies per-row payloads, and commits each
|
|
43
|
+
* waiter's wake WITH its resolve. Unlike {@link resolveEscalationsByIds},
|
|
44
|
+
* rows backing a live `condition()` waiter are first-class here: each is
|
|
45
|
+
* woken with its own payload as the condition's return value.
|
|
46
|
+
*
|
|
47
|
+
* On failure NOTHING is written and `failed` names exactly the rows that
|
|
48
|
+
* blocked the batch (`not-found`, `already-resolved`, `already-cancelled`,
|
|
49
|
+
* `already-expired`, `assignee-mismatch`) — rows that were themselves
|
|
50
|
+
* resolvable stay pending and are not listed.
|
|
51
|
+
*
|
|
52
|
+
* `assertAssignee` (optional) additionally requires every row to be assigned
|
|
53
|
+
* to that principal, asserted inside the same guarded statement — closing the
|
|
54
|
+
* claim-race window for claim-then-resolve flows.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveEscalationsAllOrNone(items: Array<{
|
|
57
|
+
id: string;
|
|
58
|
+
resolverPayload: Record<string, any>;
|
|
59
|
+
}>, metadata?: Record<string, any>, assertAssignee?: string): Promise<{
|
|
60
|
+
ok: true;
|
|
61
|
+
escalations: LTEscalationRecord[];
|
|
62
|
+
} | {
|
|
63
|
+
ok: false;
|
|
64
|
+
failed: Array<{
|
|
65
|
+
id: string;
|
|
66
|
+
reason: string;
|
|
67
|
+
}>;
|
|
68
|
+
}>;
|
|
69
|
+
/**
|
|
70
|
+
* Fetch full records for a set of escalation IDs in one indexed query.
|
|
71
|
+
* Used by bulk orchestrators that need per-row context beyond the scope
|
|
72
|
+
* fields — signal routing shape, `metadata.form_schema` for redaction.
|
|
73
|
+
*/
|
|
74
|
+
export declare function getEscalationsByIds(ids: string[]): Promise<LTEscalationRecord[]>;
|
|
39
75
|
/**
|
|
40
76
|
* Look up an efficient (atomic) escalation by its `signal_key` — the signal id
|
|
41
77
|
* passed to `conditionLT(signalId, config)` / `condition(signalId, config)`.
|
|
@@ -4,6 +4,8 @@ exports.createEscalation = createEscalation;
|
|
|
4
4
|
exports.claimEscalation = claimEscalation;
|
|
5
5
|
exports.resolveEscalation = resolveEscalation;
|
|
6
6
|
exports.resolveEscalationsByIds = resolveEscalationsByIds;
|
|
7
|
+
exports.resolveEscalationsAllOrNone = resolveEscalationsAllOrNone;
|
|
8
|
+
exports.getEscalationsByIds = getEscalationsByIds;
|
|
7
9
|
exports.getEscalationBySignalKey = getEscalationBySignalKey;
|
|
8
10
|
exports.resolveEscalationBySignalKey = resolveEscalationBySignalKey;
|
|
9
11
|
exports.cancelEscalation = cancelEscalation;
|
|
@@ -165,6 +167,57 @@ async function resolveEscalationsByIds(ids, resolverPayload, metadata) {
|
|
|
165
167
|
}
|
|
166
168
|
return records;
|
|
167
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Resolve a SET of escalations atomically — every listed row resolves, each
|
|
172
|
+
* with its OWN resolverPayload, or none do. One SQL statement locks the rows
|
|
173
|
+
* in deterministic id order, applies per-row payloads, and commits each
|
|
174
|
+
* waiter's wake WITH its resolve. Unlike {@link resolveEscalationsByIds},
|
|
175
|
+
* rows backing a live `condition()` waiter are first-class here: each is
|
|
176
|
+
* woken with its own payload as the condition's return value.
|
|
177
|
+
*
|
|
178
|
+
* On failure NOTHING is written and `failed` names exactly the rows that
|
|
179
|
+
* blocked the batch (`not-found`, `already-resolved`, `already-cancelled`,
|
|
180
|
+
* `already-expired`, `assignee-mismatch`) — rows that were themselves
|
|
181
|
+
* resolvable stay pending and are not listed.
|
|
182
|
+
*
|
|
183
|
+
* `assertAssignee` (optional) additionally requires every row to be assigned
|
|
184
|
+
* to that principal, asserted inside the same guarded statement — closing the
|
|
185
|
+
* claim-race window for claim-then-resolve flows.
|
|
186
|
+
*/
|
|
187
|
+
async function resolveEscalationsAllOrNone(items, metadata, assertAssignee) {
|
|
188
|
+
if (items.length === 0)
|
|
189
|
+
return { ok: true, escalations: [] };
|
|
190
|
+
const client = await (0, client_1.escalations)();
|
|
191
|
+
const result = await client.resolveAllOrNone({ items, metadata, assertAssignee });
|
|
192
|
+
if (!result.ok)
|
|
193
|
+
return { ok: false, failed: result.failed };
|
|
194
|
+
const records = (0, map_1.toEscalationRecords)(result.entries);
|
|
195
|
+
for (const escalation of records) {
|
|
196
|
+
publishEscalationChange({
|
|
197
|
+
type: 'escalation.resolved',
|
|
198
|
+
source: 'service',
|
|
199
|
+
workflowId: escalation.workflow_id || '',
|
|
200
|
+
workflowName: escalation.workflow_type || '',
|
|
201
|
+
taskQueue: escalation.task_queue || '',
|
|
202
|
+
escalationId: escalation.id,
|
|
203
|
+
status: 'resolved',
|
|
204
|
+
data: {},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return { ok: true, escalations: records };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Fetch full records for a set of escalation IDs in one indexed query.
|
|
211
|
+
* Used by bulk orchestrators that need per-row context beyond the scope
|
|
212
|
+
* fields — signal routing shape, `metadata.form_schema` for redaction.
|
|
213
|
+
*/
|
|
214
|
+
async function getEscalationsByIds(ids) {
|
|
215
|
+
if (ids.length === 0)
|
|
216
|
+
return [];
|
|
217
|
+
const client = await (0, client_1.escalations)();
|
|
218
|
+
const rows = await client.list({ ids, limit: LOOKUP_LIMIT });
|
|
219
|
+
return (0, map_1.toEscalationRecords)(rows);
|
|
220
|
+
}
|
|
168
221
|
/**
|
|
169
222
|
* Look up an efficient (atomic) escalation by its `signal_key` — the signal id
|
|
170
223
|
* passed to `conditionLT(signalId, config)` / `condition(signalId, config)`.
|
|
@@ -38,6 +38,7 @@ const hotmesh_1 = require("@hotmeshio/hotmesh");
|
|
|
38
38
|
const db_1 = require("../../lib/db");
|
|
39
39
|
const logger_1 = require("../../lib/logger");
|
|
40
40
|
const dbaService = __importStar(require("../dba"));
|
|
41
|
+
const client_1 = require("../escalation/client");
|
|
41
42
|
const CRON_TOPIC = 'lt.maintenance.prune';
|
|
42
43
|
const CRON_ID = 'lt-maintenance-nightly';
|
|
43
44
|
/**
|
|
@@ -84,6 +85,27 @@ async function executeRule(appId, rule) {
|
|
|
84
85
|
streams: false,
|
|
85
86
|
});
|
|
86
87
|
break;
|
|
88
|
+
// Age out terminal escalation rows (resolved/cancelled/expired). The
|
|
89
|
+
// engine-owned prune deletes at most one batch per call in one atomic
|
|
90
|
+
// statement; loop until the horizon is drained. Pending rows and live
|
|
91
|
+
// condition() waiters are never touched — every engine state transition
|
|
92
|
+
// guards on status='pending', so terminal rows are inert audit records.
|
|
93
|
+
case rule.target === 'escalations' && rule.action === 'delete': {
|
|
94
|
+
const client = await (0, client_1.escalations)();
|
|
95
|
+
let deleted = 0;
|
|
96
|
+
let batch;
|
|
97
|
+
do {
|
|
98
|
+
({ deleted: batch } = await client.prune({
|
|
99
|
+
olderThan: rule.olderThan,
|
|
100
|
+
statuses: rule.statuses,
|
|
101
|
+
}));
|
|
102
|
+
deleted += batch;
|
|
103
|
+
} while (batch > 0);
|
|
104
|
+
if (deleted > 0) {
|
|
105
|
+
logger_1.loggerRegistry.info(`[lt-maintenance] escalations pruned: ${deleted} (olderThan: ${rule.olderThan})`);
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
87
109
|
default:
|
|
88
110
|
logger_1.loggerRegistry.warn(`[lt-maintenance] unknown rule, skipping: ${JSON.stringify(rule)}`);
|
|
89
111
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EscalationChain, RoleDetail, RoleSchemaVersion, RoleSchemaVersionSummary, UpdateRoleInput } from './types';
|
|
1
|
+
import type { EscalationChain, RoleDetail, RoleSchemaVersion, RoleSchemaVersionSummary, RoleListSchemaVersion, RoleListSchemaVersionSummary, UpdateRoleInput } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Get the roles a given source role can escalate to.
|
|
4
4
|
*/
|
|
@@ -60,6 +60,18 @@ export declare function listRoleSchemaVersions(role: string): Promise<RoleSchema
|
|
|
60
60
|
* Returns null when the role (or the requested version) does not exist.
|
|
61
61
|
*/
|
|
62
62
|
export declare function getRoleSchema(role: string, version?: number): Promise<RoleSchemaVersion | null>;
|
|
63
|
+
/**
|
|
64
|
+
* List the list-schema version history for a role (newest first). The schema is
|
|
65
|
+
* elided — presence flag only; fetch a full snapshot via getRoleListSchema.
|
|
66
|
+
*/
|
|
67
|
+
export declare function listRoleListSchemaVersions(role: string): Promise<RoleListSchemaVersionSummary[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Fetch a role's list schema. With a version, reads the immutable snapshot from
|
|
70
|
+
* lt_role_list_schemas; without one, the live (latest) column — a role that has
|
|
71
|
+
* never versioned its list schema still answers with version null. Returns null
|
|
72
|
+
* when the role (or the requested version) does not exist.
|
|
73
|
+
*/
|
|
74
|
+
export declare function getRoleListSchema(role: string, version?: number): Promise<RoleListSchemaVersion | null>;
|
|
63
75
|
/**
|
|
64
76
|
* Fetch a role's live form_schema — the escalation form the dashboard renders.
|
|
65
77
|
*/
|