@hotmeshio/long-tail 0.5.12 → 0.6.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/README.md +1 -0
- package/build/api/escalations/bulk.js +9 -1
- package/build/api/escalations/cancel.js +5 -7
- package/build/api/escalations/claim.js +18 -15
- package/build/api/escalations/create.d.ts +6 -0
- package/build/api/escalations/create.js +11 -8
- package/build/api/escalations/facets.d.ts +18 -0
- package/build/api/escalations/facets.js +115 -0
- package/build/api/escalations/helpers.d.ts +70 -1
- package/build/api/escalations/helpers.js +101 -7
- package/build/api/escalations/index.d.ts +3 -2
- package/build/api/escalations/index.js +7 -1
- package/build/api/escalations/list.d.ts +21 -2
- package/build/api/escalations/list.js +109 -10
- package/build/api/escalations/metadata.js +22 -27
- package/build/api/escalations/resolve.d.ts +19 -1
- package/build/api/escalations/resolve.js +111 -26
- package/build/api/escalations/single.js +8 -8
- package/build/api/users.d.ts +6 -0
- package/build/api/users.js +29 -1
- package/build/bin/ltc.js +40 -0
- package/build/lib/cli/commands/escalations.d.ts +36 -0
- package/build/lib/cli/commands/escalations.js +98 -0
- package/build/lib/db/schemas/001_schema.sql +10 -5
- package/build/lib/db/schemas/012_lt_tasks_workflow_id_unique.sql +20 -0
- package/build/lib/db/schemas/013_role_scope.sql +37 -0
- package/build/modules/version.d.ts +10 -5
- package/build/modules/version.js +27 -11
- package/build/routes/escalations/facets.d.ts +7 -0
- package/build/routes/escalations/facets.js +81 -0
- package/build/routes/escalations/index.js +3 -0
- package/build/routes/escalations/list.js +46 -0
- package/build/routes/escalations/resolve.js +22 -4
- package/build/routes/users.js +9 -5
- package/build/sdk/index.d.ts +33 -2
- package/build/sdk/index.js +7 -0
- package/build/services/escalation/crud.d.ts +56 -6
- package/build/services/escalation/crud.js +102 -31
- package/build/services/escalation/facet-sql.d.ts +18 -0
- package/build/services/escalation/facet-sql.js +118 -0
- package/build/services/escalation/facets.d.ts +45 -0
- package/build/services/escalation/facets.js +218 -0
- package/build/services/escalation/index.d.ts +1 -0
- package/build/services/escalation/index.js +8 -1
- package/build/services/escalation/queries.d.ts +45 -0
- package/build/services/escalation/queries.js +107 -6
- package/build/services/escalation/sql.d.ts +10 -4
- package/build/services/escalation/sql.js +36 -8
- package/build/services/interceptor/activities/escalation.d.ts +2 -0
- package/build/services/interceptor/activities/escalation.js +1 -1
- package/build/services/interceptor/lifecycle.js +4 -0
- package/build/services/mcp/server-tools.js +10 -4
- package/build/services/orchestrator/condition.js +6 -2
- package/build/services/role/index.js +2 -3
- package/build/services/role/sql.d.ts +8 -0
- package/build/services/role/sql.js +17 -1
- package/build/services/task/crud.js +22 -14
- package/build/services/task/process.js +3 -21
- package/build/services/task/sql.d.ts +24 -1
- package/build/services/task/sql.js +65 -1
- package/build/services/user/crud.d.ts +1 -0
- package/build/services/user/crud.js +29 -11
- package/build/services/user/index.d.ts +2 -1
- package/build/services/user/index.js +12 -1
- package/build/services/user/rbac.d.ts +18 -0
- package/build/services/user/rbac.js +27 -0
- package/build/services/user/roles.d.ts +7 -2
- package/build/services/user/roles.js +21 -5
- package/build/services/user/scope.d.ts +22 -0
- package/build/services/user/scope.js +50 -0
- package/build/services/user/seed-admin.js +34 -17
- package/build/services/user/sql.d.ts +18 -7
- package/build/services/user/sql.js +38 -13
- package/build/services/user/sso-provision.js +41 -24
- package/build/services/user/types.d.ts +9 -5
- package/build/system/mcp-servers/admin/escalations.js +65 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +325 -0
- package/build/system/mcp-servers/admin/schemas.js +63 -2
- package/build/system/mcp-servers/admin/users.js +28 -2
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/envelope.d.ts +7 -0
- package/build/types/facets.d.ts +60 -0
- package/build/types/facets.js +10 -0
- package/build/types/index.d.ts +2 -1
- package/build/types/user.d.ts +11 -0
- package/dashboard/dist/assets/AdminDashboard-2CZ8py9X.js +2 -0
- package/dashboard/dist/assets/{AdminDashboard-LTiYLuzq.js.map → AdminDashboard-2CZ8py9X.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-nlOudfpZ.js → AgentConfigPage-D68sodgo.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-nlOudfpZ.js.map → AgentConfigPage-D68sodgo.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-BSUnDvGp.js → AgentDetailPage-BmhuWbKi.js} +2 -2
- package/dashboard/dist/assets/{AgentDetailPage-BSUnDvGp.js.map → AgentDetailPage-BmhuWbKi.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-DpS0cCYe.js → AgentsPage-C4SKQVeU.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-DpS0cCYe.js.map → AgentsPage-C4SKQVeU.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-9RBEYwkt.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-9RBEYwkt.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-BhKmq7M7.js → BotPicker-D-FvXJOM.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-BhKmq7M7.js.map → BotPicker-D-FvXJOM.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-DLIleX2L.js → CapabilitiesPage-ByDf6WJr.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-DLIleX2L.js.map → CapabilitiesPage-ByDf6WJr.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-D7SVwNVk.js → CollapsibleSection-BjGNYEvh.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-D7SVwNVk.js.map → CollapsibleSection-BjGNYEvh.js.map} +1 -1
- package/dashboard/dist/assets/CopyableId-blg-gZvT.js +2 -0
- package/dashboard/dist/assets/CopyableId-blg-gZvT.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-fat5Z2U9.js → CredentialsPage-DvCA-wQN.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-fat5Z2U9.js.map → CredentialsPage-DvCA-wQN.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-OOP4P2mJ.js → CronLabel-DBMurd_c.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-OOP4P2mJ.js.map → CronLabel-DBMurd_c.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-COacSAXO.js → CustomDurationPicker-DHflFoEa.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-COacSAXO.js.map → CustomDurationPicker-DHflFoEa.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-Bfw2YLVS.js → DropZone-CUtdp99Z.js} +2 -2
- package/dashboard/dist/assets/{DropZone-Bfw2YLVS.js.map → DropZone-CUtdp99Z.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-CkfCD-m7.js → ElapsedCell-CSjcVOpo.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-CkfCD-m7.js.map → ElapsedCell-CSjcVOpo.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-BmI8jxlh.js → EscalationsOverview-BUqRNJbK.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-BmI8jxlh.js.map → EscalationsOverview-BUqRNJbK.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-CeKfFk3m.js → EventTable-Chz-6XPE.js} +2 -2
- package/dashboard/dist/assets/{EventTable-CeKfFk3m.js.map → EventTable-Chz-6XPE.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-BZBPBCSq.js → GraphInvokePage-BXdgciWg.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-BZBPBCSq.js.map → GraphInvokePage-BXdgciWg.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-dM7U2tl6.js → HomePage-CEMxnk0r.js} +2 -2
- package/dashboard/dist/assets/{HomePage-dM7U2tl6.js.map → HomePage-CEMxnk0r.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-BJaCzAXS.js → ListToolbar-_73JCdbB.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-BJaCzAXS.js.map → ListToolbar-_73JCdbB.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-CvnDG5OB.js → McpOverview-DfdqxhSz.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-CvnDG5OB.js.map → McpOverview-DfdqxhSz.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-DwmSIcIL.js → McpQueryDetailPage-DPAljieY.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-DwmSIcIL.js.map → McpQueryDetailPage-DPAljieY.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-CU6FMEEk.js → McpQueryPage-D5cSziYS.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-CU6FMEEk.js.map → McpQueryPage-D5cSziYS.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-8a6lsehv.js → McpRunDetailPage-DwNb9wZg.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-8a6lsehv.js.map → McpRunDetailPage-DwNb9wZg.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-CPMYWgm_.js → McpRunsPage-O5H6yStl.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-CPMYWgm_.js.map → McpRunsPage-O5H6yStl.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-D6btr6e8.js → NamespacePill-uW-xOedj.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-D6btr6e8.js.map → NamespacePill-uW-xOedj.js.map} +1 -1
- package/dashboard/dist/assets/OperatorDashboard-DYw9Ekv5.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-g9o7I925.js.map → OperatorDashboard-DYw9Ekv5.js.map} +1 -1
- package/dashboard/dist/assets/PageHeader-DVOZjBfL.js +2 -0
- package/dashboard/dist/assets/PageHeader-DVOZjBfL.js.map +1 -0
- package/dashboard/dist/assets/{PageHeaderWithStats-DJQCZ0FR.js → PageHeaderWithStats-DtTpDHOd.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-DJQCZ0FR.js.map → PageHeaderWithStats-DtTpDHOd.js.map} +1 -1
- package/dashboard/dist/assets/PriorityBadge-B4ykBH7f.js +2 -0
- package/dashboard/dist/assets/PriorityBadge-B4ykBH7f.js.map +1 -0
- package/dashboard/dist/assets/{ProcessDetailPage-ByyWCs2A.js → ProcessDetailPage-B0Dokcx9.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-ByyWCs2A.js.map → ProcessDetailPage-B0Dokcx9.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-BTsXds4l.js → ProcessesListPage-Cc87WIjL.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-BTsXds4l.js.map → ProcessesListPage-Cc87WIjL.js.map} +1 -1
- package/dashboard/dist/assets/RolePill-BaW-30TC.js +2 -0
- package/dashboard/dist/assets/RolePill-BaW-30TC.js.map +1 -0
- package/dashboard/dist/assets/{RolesPage-B2Kdf-L9.js → RolesPage-cp6PiF08.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-B2Kdf-L9.js.map → RolesPage-cp6PiF08.js.map} +1 -1
- package/dashboard/dist/assets/RowActions-BA9L9djf.js +2 -0
- package/dashboard/dist/assets/RowActions-BA9L9djf.js.map +1 -0
- package/dashboard/dist/assets/{RunAsSelector-BzkZXpSX.js → RunAsSelector-2LWkjSnO.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-BzkZXpSX.js.map → RunAsSelector-2LWkjSnO.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-BWhFSr2d.js → StickyPagination-BQEm--kI.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-BWhFSr2d.js.map → StickyPagination-BQEm--kI.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-B6ch-6SY.js → StreamMessageDetail-BTtw3GjX.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-B6ch-6SY.js.map → StreamMessageDetail-BTtw3GjX.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-CjvfUkls.js → SwimlaneTimeline-DdXxP4fO.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-CjvfUkls.js.map → SwimlaneTimeline-DdXxP4fO.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-CZSeyesi.js → TagInput-x9XTuqMb.js} +2 -2
- package/dashboard/dist/assets/{TagInput-CZSeyesi.js.map → TagInput-x9XTuqMb.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-BjHU6opi.js → TaskDetailPage-B8veYG4X.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-BjHU6opi.js.map → TaskDetailPage-B8veYG4X.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-CQrXrSxk.js → TaskQueuePill-DSEQZ-HT.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-CQrXrSxk.js.map → TaskQueuePill-DSEQZ-HT.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-Bem44zd0.js → TasksListPage-CsN1gZj5.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-Bem44zd0.js.map → TasksListPage-CsN1gZj5.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-CUIYqSt_.js → TimeAgo-n182EcLx.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-CUIYqSt_.js.map → TimeAgo-n182EcLx.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell--tnreI1i.js → TimestampCell-2dlcQAfX.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell--tnreI1i.js.map → TimestampCell-2dlcQAfX.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-O09jsTQM.js → ToolPill-B_e4eT77.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-O09jsTQM.js.map → ToolPill-B_e4eT77.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-OD99H7Pk.js → ToolTestPanel-Bhnncc6M.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-OD99H7Pk.js.map → ToolTestPanel-Bhnncc6M.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-rh_dsTJI.js → TopicDetailPage-29lsNsqI.js} +2 -2
- package/dashboard/dist/assets/{TopicDetailPage-rh_dsTJI.js.map → TopicDetailPage-29lsNsqI.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-CD_40_v-.js → TopicsPage-KJ7_LAvo.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-CD_40_v-.js.map → TopicsPage-KJ7_LAvo.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-C__c8BN0.js → UserName-CMoFi9zb.js} +2 -2
- package/dashboard/dist/assets/{UserName-C__c8BN0.js.map → UserName-CMoFi9zb.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-Bw6QdtUO.js → WorkflowExecutionPage-Cymfx-Bp.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-Bw6QdtUO.js.map → WorkflowExecutionPage-Cymfx-Bp.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-CS-IsR0U.js → WorkflowPill-B66_AnaY.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-CS-IsR0U.js.map → WorkflowPill-B66_AnaY.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-BgI-iOP6.js → WorkflowsDashboard-CqLN8spd.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-BgI-iOP6.js.map → WorkflowsDashboard-CqLN8spd.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-BRc0QbA7.js → WorkflowsOverview-0Rc2WT6q.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-BRc0QbA7.js.map → WorkflowsOverview-0Rc2WT6q.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DdzspNQR.js → YamlWorkflowDetailPage-CARQ7_LN.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DdzspNQR.js.map → YamlWorkflowDetailPage-CARQ7_LN.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-Djw5ZbgQ.js → YamlWorkflowsPage-B-7sz1nf.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-Djw5ZbgQ.js.map → YamlWorkflowsPage-B-7sz1nf.js.map} +1 -1
- package/dashboard/dist/assets/{agents-xstNkF6c.js → agents-zM43d5Yf.js} +2 -2
- package/dashboard/dist/assets/{agents-xstNkF6c.js.map → agents-zM43d5Yf.js.map} +1 -1
- package/dashboard/dist/assets/{bots-BztAI0s_.js → bots-D5BasOGz.js} +2 -2
- package/dashboard/dist/assets/{bots-BztAI0s_.js.map → bots-D5BasOGz.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-CEDwtxNL.js → capabilities-BT3PW9CZ.js} +2 -2
- package/dashboard/dist/assets/{capabilities-CEDwtxNL.js.map → capabilities-BT3PW9CZ.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-BlGEsRKe.js → controlplane-NpJr49rN.js} +2 -2
- package/dashboard/dist/assets/{controlplane-BlGEsRKe.js.map → controlplane-NpJr49rN.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-iI4QKlP-.js → escalation-DrzQolPG.js} +2 -2
- package/dashboard/dist/assets/{escalation-iI4QKlP-.js.map → escalation-DrzQolPG.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-QzwmtD60.js +2 -0
- package/dashboard/dist/assets/escalation-columns-QzwmtD60.js.map +1 -0
- package/dashboard/dist/assets/index-B5w4Gjyk.js +6 -0
- package/dashboard/dist/assets/index-B5w4Gjyk.js.map +1 -0
- package/dashboard/dist/assets/{index-CMviPjXQ.js → index-BCT4kqGR.js} +2 -2
- package/dashboard/dist/assets/{index-CMviPjXQ.js.map → index-BCT4kqGR.js.map} +1 -1
- package/dashboard/dist/assets/{index-B7VC0vjf.js → index-C69XvBfg.js} +2 -2
- package/dashboard/dist/assets/{index-B7VC0vjf.js.map → index-C69XvBfg.js.map} +1 -1
- package/dashboard/dist/assets/index-CHw4Fl0t.css +1 -0
- package/dashboard/dist/assets/{index-CUfgt3mV.js → index-C_NwHTMh.js} +2 -2
- package/dashboard/dist/assets/{index-CUfgt3mV.js.map → index-C_NwHTMh.js.map} +1 -1
- package/dashboard/dist/assets/{index-CAqEy2Db.js → index-Cq0trGqf.js} +2 -2
- package/dashboard/dist/assets/{index-CAqEy2Db.js.map → index-Cq0trGqf.js.map} +1 -1
- package/dashboard/dist/assets/{index-D1xVY4JR.js → index-DE0r9SEX.js} +22 -22
- package/dashboard/dist/assets/index-DE0r9SEX.js.map +1 -0
- package/dashboard/dist/assets/{index-DjJ6VCuc.js → index-DH_CiD0W.js} +2 -2
- package/dashboard/dist/assets/{index-DjJ6VCuc.js.map → index-DH_CiD0W.js.map} +1 -1
- package/dashboard/dist/assets/{index-DzVYjoex.js → index-K23FdS-T.js} +2 -2
- package/dashboard/dist/assets/{index-DzVYjoex.js.map → index-K23FdS-T.js.map} +1 -1
- package/dashboard/dist/assets/{index-FuhiJuHL.js → index-f6K1UNrn.js} +2 -2
- package/dashboard/dist/assets/{index-FuhiJuHL.js.map → index-f6K1UNrn.js.map} +1 -1
- package/dashboard/dist/assets/{index-B5qsp8lb.js → index-qr1fndqM.js} +2 -2
- package/dashboard/dist/assets/{index-B5qsp8lb.js.map → index-qr1fndqM.js.map} +1 -1
- package/dashboard/dist/assets/index-vKTvJ_li.js +2 -0
- package/dashboard/dist/assets/index-vKTvJ_li.js.map +1 -0
- package/dashboard/dist/assets/{index-BUmnJ4eT.js → index-y-3ODtEE.js} +2 -2
- package/dashboard/dist/assets/{index-BUmnJ4eT.js.map → index-y-3ODtEE.js.map} +1 -1
- package/dashboard/dist/assets/{index-Dsi5o7dx.js → index-y56Ds-LB.js} +2 -2
- package/dashboard/dist/assets/{index-Dsi5o7dx.js.map → index-y56Ds-LB.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-BKg0cr39.js → knowledge-DSSATOTX.js} +2 -2
- package/dashboard/dist/assets/{knowledge-BKg0cr39.js.map → knowledge-DSSATOTX.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-CVdHpKlz.js → mcp-CfJFVo5k.js} +2 -2
- package/dashboard/dist/assets/{mcp-CVdHpKlz.js.map → mcp-CfJFVo5k.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-CYxZcbwQ.js → mcp-query-DKVx2Q-X.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-CYxZcbwQ.js.map → mcp-query-DKVx2Q-X.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-BOEv7P6O.js → pipelines-BSWzE-6M.js} +2 -2
- package/dashboard/dist/assets/{pipelines-BOEv7P6O.js.map → pipelines-BSWzE-6M.js.map} +1 -1
- package/dashboard/dist/assets/{roles-c5cV2QYQ.js → roles-t9HzhI5N.js} +2 -2
- package/dashboard/dist/assets/{roles-c5cV2QYQ.js.map → roles-t9HzhI5N.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-JV3BjeEF.js → tasks-B7rUihoH.js} +2 -2
- package/dashboard/dist/assets/{tasks-JV3BjeEF.js.map → tasks-B7rUihoH.js.map} +1 -1
- package/dashboard/dist/assets/{topics-BDaPnhnQ.js → topics-PC2fEiyD.js} +2 -2
- package/dashboard/dist/assets/{topics-BDaPnhnQ.js.map → topics-PC2fEiyD.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-a7Dnh8G4.js → useEventHooks-DK9mw1cI.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-a7Dnh8G4.js.map → useEventHooks-DK9mw1cI.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-BSwEV0e9.js → useNamespace-3IjtUtAw.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-BSwEV0e9.js.map → useNamespace-3IjtUtAw.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-BuqvL-cn.js → useYamlActivityEvents-uxukp3gO.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-BuqvL-cn.js.map → useYamlActivityEvents-uxukp3gO.js.map} +1 -1
- package/dashboard/dist/assets/users-CC4B5Fm5.js +2 -0
- package/dashboard/dist/assets/users-CC4B5Fm5.js.map +1 -0
- package/dashboard/dist/assets/{vendor-icons-D5ycKPA2.js → vendor-icons-Dy5Jpknf.js} +2 -2
- package/dashboard/dist/assets/{vendor-icons-D5ycKPA2.js.map → vendor-icons-Dy5Jpknf.js.map} +1 -1
- package/dashboard/dist/assets/{workflows-DF-9Voyi.js → workflows-S7MF1nNk.js} +2 -2
- package/dashboard/dist/assets/{workflows-DF-9Voyi.js.map → workflows-S7MF1nNk.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-2roKaSSK.js → yaml-workflows-WfdaMHNn.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-2roKaSSK.js.map → yaml-workflows-WfdaMHNn.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/api/http/escalations.md +131 -10
- package/docs/api/http/roles.md +44 -9
- package/docs/api/http/users.md +15 -6
- package/docs/api/mcp/admin.md +8 -2
- package/docs/api/mcp/human-queue.md +34 -2
- package/docs/api/sdk/escalations.md +155 -2
- package/docs/api/sdk/roles.md +11 -0
- package/docs/api/sdk/users.md +26 -1
- package/docs/auth.md +1 -1
- package/docs/dashboard.md +5 -4
- package/docs/data.md +9 -2
- package/docs/faceted-routing.md +221 -0
- package/docs/hitl-guide.md +50 -0
- package/docs/iam.md +31 -0
- package/docs/mcp.md +8 -0
- package/package.json +2 -2
- package/dashboard/dist/assets/AdminDashboard-LTiYLuzq.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-BEylRqjP.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-BEylRqjP.js.map +0 -1
- package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js +0 -2
- package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-g9o7I925.js +0 -2
- package/dashboard/dist/assets/PageHeader-Dk2Pe44Q.js +0 -2
- package/dashboard/dist/assets/PageHeader-Dk2Pe44Q.js.map +0 -1
- package/dashboard/dist/assets/PriorityBadge-DfQY9St9.js +0 -2
- package/dashboard/dist/assets/PriorityBadge-DfQY9St9.js.map +0 -1
- package/dashboard/dist/assets/RolePill-Du-aNnjR.js +0 -2
- package/dashboard/dist/assets/RolePill-Du-aNnjR.js.map +0 -1
- package/dashboard/dist/assets/RowActions-Dg-Fsm5O.js +0 -2
- package/dashboard/dist/assets/RowActions-Dg-Fsm5O.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-nVYVt7Gl.js +0 -2
- package/dashboard/dist/assets/escalation-columns-nVYVt7Gl.js.map +0 -1
- package/dashboard/dist/assets/index-CP0JEQhZ.js +0 -6
- package/dashboard/dist/assets/index-CP0JEQhZ.js.map +0 -1
- package/dashboard/dist/assets/index-ClvYSNZd.css +0 -1
- package/dashboard/dist/assets/index-D1xVY4JR.js.map +0 -1
- package/dashboard/dist/assets/index-DPLy-jwO.js +0 -2
- package/dashboard/dist/assets/index-DPLy-jwO.js.map +0 -1
- package/dashboard/dist/assets/users-Pn85K_3C.js +0 -2
- package/dashboard/dist/assets/users-Pn85K_3C.js.map +0 -1
|
@@ -35,11 +35,21 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.listEscalations = listEscalations;
|
|
37
37
|
exports.listAvailableEscalations = listAvailableEscalations;
|
|
38
|
+
exports.listFacetKeys = listFacetKeys;
|
|
38
39
|
exports.listDistinctTypes = listDistinctTypes;
|
|
39
40
|
exports.getEscalationStats = getEscalationStats;
|
|
40
41
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
41
42
|
const helpers_1 = require("./helpers");
|
|
42
|
-
|
|
43
|
+
/** True when the caller asked for anything the plain list path can't express. */
|
|
44
|
+
function hasFacetQuery(i) {
|
|
45
|
+
return !!(i.facets ||
|
|
46
|
+
i.block?.length ||
|
|
47
|
+
i.range?.length ||
|
|
48
|
+
i.exists?.length ||
|
|
49
|
+
i.orderBy?.length ||
|
|
50
|
+
i.roles?.length ||
|
|
51
|
+
i.available != null);
|
|
52
|
+
}
|
|
43
53
|
/**
|
|
44
54
|
* List escalations with optional filters.
|
|
45
55
|
*
|
|
@@ -61,10 +71,40 @@ const helpers_1 = require("./helpers");
|
|
|
61
71
|
*/
|
|
62
72
|
async function listEscalations(input, auth) {
|
|
63
73
|
try {
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
74
|
+
const scope = await (0, helpers_1.getEscalationReadScope)(auth.userId);
|
|
75
|
+
if (!scope.global && scope.allRoles.length === 0 && scope.selfRoles.length === 0) {
|
|
66
76
|
return { status: 200, data: { escalations: [], total: 0 } };
|
|
67
77
|
}
|
|
78
|
+
// Faceted path: the read-scope predicate composes with the full FacetQuery
|
|
79
|
+
// language (facets/block/range/exists/metadata-sort) IN SQL — the caller's
|
|
80
|
+
// role(s) can only NARROW within their scope, never widen past it.
|
|
81
|
+
if (hasFacetQuery(input)) {
|
|
82
|
+
const result = await escalationService.searchEscalationsFaceted({
|
|
83
|
+
global: scope.global,
|
|
84
|
+
visibleRoles: scope.global ? undefined : scope.allRoles,
|
|
85
|
+
selfRoles: scope.global ? undefined : scope.selfRoles,
|
|
86
|
+
meUserId: auth.userId,
|
|
87
|
+
facet: {
|
|
88
|
+
role: input.role,
|
|
89
|
+
roles: input.roles,
|
|
90
|
+
status: input.status,
|
|
91
|
+
available: input.available ?? (input.claimed ? false : undefined),
|
|
92
|
+
facets: input.facets,
|
|
93
|
+
block: input.block,
|
|
94
|
+
range: input.range,
|
|
95
|
+
exists: input.exists,
|
|
96
|
+
orderBy: input.orderBy,
|
|
97
|
+
},
|
|
98
|
+
type: input.type,
|
|
99
|
+
subtype: input.subtype,
|
|
100
|
+
priority: input.priority,
|
|
101
|
+
assigned_to: input.assigned_to,
|
|
102
|
+
search: input.search,
|
|
103
|
+
limit: input.limit ?? 50,
|
|
104
|
+
offset: input.offset ?? 0,
|
|
105
|
+
});
|
|
106
|
+
return { status: 200, data: result };
|
|
107
|
+
}
|
|
68
108
|
const result = await escalationService.listEscalations({
|
|
69
109
|
status: input.status,
|
|
70
110
|
role: input.role,
|
|
@@ -78,7 +118,9 @@ async function listEscalations(input, auth) {
|
|
|
78
118
|
sort_by: input.sort_by,
|
|
79
119
|
order: input.order,
|
|
80
120
|
search: input.search,
|
|
81
|
-
visibleRoles,
|
|
121
|
+
visibleRoles: scope.global ? undefined : scope.allRoles,
|
|
122
|
+
selfRoles: scope.global ? undefined : scope.selfRoles,
|
|
123
|
+
meUserId: auth.userId,
|
|
82
124
|
});
|
|
83
125
|
return { status: 200, data: result };
|
|
84
126
|
}
|
|
@@ -105,10 +147,37 @@ async function listEscalations(input, auth) {
|
|
|
105
147
|
*/
|
|
106
148
|
async function listAvailableEscalations(input, auth) {
|
|
107
149
|
try {
|
|
108
|
-
const
|
|
109
|
-
if (
|
|
150
|
+
const scope = await (0, helpers_1.getEscalationReadScope)(auth.userId);
|
|
151
|
+
if (!scope.global && scope.allRoles.length === 0 && scope.selfRoles.length === 0) {
|
|
110
152
|
return { status: 200, data: { escalations: [], total: 0 } };
|
|
111
153
|
}
|
|
154
|
+
// Faceted path — same as listEscalations but pinned to the available pool.
|
|
155
|
+
if (hasFacetQuery(input)) {
|
|
156
|
+
const result = await escalationService.searchEscalationsFaceted({
|
|
157
|
+
global: scope.global,
|
|
158
|
+
visibleRoles: scope.global ? undefined : scope.allRoles,
|
|
159
|
+
selfRoles: scope.global ? undefined : scope.selfRoles,
|
|
160
|
+
meUserId: auth.userId,
|
|
161
|
+
facet: {
|
|
162
|
+
role: input.role,
|
|
163
|
+
roles: input.roles,
|
|
164
|
+
status: 'pending',
|
|
165
|
+
available: true,
|
|
166
|
+
facets: input.facets,
|
|
167
|
+
block: input.block,
|
|
168
|
+
range: input.range,
|
|
169
|
+
exists: input.exists,
|
|
170
|
+
orderBy: input.orderBy,
|
|
171
|
+
},
|
|
172
|
+
type: input.type,
|
|
173
|
+
subtype: input.subtype,
|
|
174
|
+
priority: input.priority,
|
|
175
|
+
search: input.search,
|
|
176
|
+
limit: input.limit ?? 50,
|
|
177
|
+
offset: input.offset ?? 0,
|
|
178
|
+
});
|
|
179
|
+
return { status: 200, data: result };
|
|
180
|
+
}
|
|
112
181
|
const result = await escalationService.listAvailableEscalations({
|
|
113
182
|
role: input.role,
|
|
114
183
|
type: input.type,
|
|
@@ -119,7 +188,9 @@ async function listAvailableEscalations(input, auth) {
|
|
|
119
188
|
sort_by: input.sort_by,
|
|
120
189
|
order: input.order,
|
|
121
190
|
search: input.search,
|
|
122
|
-
visibleRoles,
|
|
191
|
+
visibleRoles: scope.global ? undefined : scope.allRoles,
|
|
192
|
+
selfRoles: scope.global ? undefined : scope.selfRoles,
|
|
193
|
+
meUserId: auth.userId,
|
|
123
194
|
});
|
|
124
195
|
return { status: 200, data: result };
|
|
125
196
|
}
|
|
@@ -127,6 +198,31 @@ async function listAvailableEscalations(input, auth) {
|
|
|
127
198
|
return { status: 500, error: err.message };
|
|
128
199
|
}
|
|
129
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* List the distinct top-level metadata facet KEYS visible to the caller. Powers the
|
|
203
|
+
* faceted-query UI's key autocomplete — only keys that actually exist in the caller's
|
|
204
|
+
* (role-scoped) escalations are offered, never description-only text.
|
|
205
|
+
*
|
|
206
|
+
* @returns `{ status: 200, data: { keys: string[] } }`
|
|
207
|
+
*/
|
|
208
|
+
async function listFacetKeys(_input, auth) {
|
|
209
|
+
try {
|
|
210
|
+
const scope = await (0, helpers_1.getEscalationReadScope)(auth.userId);
|
|
211
|
+
if (!scope.global && scope.allRoles.length === 0 && scope.selfRoles.length === 0) {
|
|
212
|
+
return { status: 200, data: { keys: [] } };
|
|
213
|
+
}
|
|
214
|
+
const keys = await escalationService.listFacetKeys({
|
|
215
|
+
global: scope.global,
|
|
216
|
+
visibleRoles: scope.global ? undefined : scope.allRoles,
|
|
217
|
+
selfRoles: scope.global ? undefined : scope.selfRoles,
|
|
218
|
+
meUserId: auth.userId,
|
|
219
|
+
});
|
|
220
|
+
return { status: 200, data: { keys } };
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
return { status: 500, error: err.message };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
130
226
|
/**
|
|
131
227
|
* List all distinct escalation type values.
|
|
132
228
|
*
|
|
@@ -150,8 +246,11 @@ async function listDistinctTypes() {
|
|
|
150
246
|
*/
|
|
151
247
|
async function getEscalationStats(input, auth) {
|
|
152
248
|
try {
|
|
153
|
-
|
|
154
|
-
|
|
249
|
+
// Aggregate stats reflect read_all roles only. read_self memberships get the
|
|
250
|
+
// single-item surface (auto user-mode), not a queue-overview dashboard, so
|
|
251
|
+
// their items are deliberately not aggregated here (and not leaked in counts).
|
|
252
|
+
const scope = await (0, helpers_1.getEscalationReadScope)(auth.userId);
|
|
253
|
+
if (!scope.global && scope.allRoles.length === 0) {
|
|
155
254
|
return {
|
|
156
255
|
status: 200,
|
|
157
256
|
data: {
|
|
@@ -164,7 +263,7 @@ async function getEscalationStats(input, auth) {
|
|
|
164
263
|
},
|
|
165
264
|
};
|
|
166
265
|
}
|
|
167
|
-
const stats = await escalationService.getEscalationStats(
|
|
266
|
+
const stats = await escalationService.getEscalationStats(scope.global ? undefined : scope.allRoles, input.period);
|
|
168
267
|
return { status: 200, data: stats };
|
|
169
268
|
}
|
|
170
269
|
catch (err) {
|
|
@@ -37,7 +37,6 @@ exports.findByMetadata = findByMetadata;
|
|
|
37
37
|
exports.claimByMetadata = claimByMetadata;
|
|
38
38
|
exports.resolveByMetadata = resolveByMetadata;
|
|
39
39
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
40
|
-
const userService = __importStar(require("../../services/user"));
|
|
41
40
|
const helpers_1 = require("./helpers");
|
|
42
41
|
/**
|
|
43
42
|
* Find escalations by a metadata key-value pair.
|
|
@@ -50,14 +49,14 @@ async function findByMetadata(input, auth) {
|
|
|
50
49
|
if (!input.key || !input.value) {
|
|
51
50
|
return { status: 400, error: 'key and value are required' };
|
|
52
51
|
}
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
// RBAC scoped IN SQL: the caller's read scope (read_all roles see every match;
|
|
53
|
+
// read_self roles see only matches assigned to them) reaches the scoped query's
|
|
54
|
+
// WHERE and COUNT. Global access → no filter. Never filter a fetched page
|
|
55
|
+
// controller-side — that shrinks the page and reports a wrong total.
|
|
56
|
+
const scope = await (0, helpers_1.getEscalationReadScope)(auth.userId);
|
|
57
|
+
const result = await escalationService.findByMetadata(input.key, input.value, input.status, input.limit, input.offset, scope.global
|
|
58
|
+
? undefined
|
|
59
|
+
: { allRoles: scope.allRoles, selfRoles: scope.selfRoles, meUserId: auth.userId });
|
|
61
60
|
return { status: 200, data: result };
|
|
62
61
|
}
|
|
63
62
|
catch (err) {
|
|
@@ -80,8 +79,12 @@ async function claimByMetadata(input, auth) {
|
|
|
80
79
|
if ('error' in resolved)
|
|
81
80
|
return resolved.error;
|
|
82
81
|
const claimUserId = resolved.userId;
|
|
83
|
-
//
|
|
84
|
-
|
|
82
|
+
// Write-scope: global → no filter; otherwise restrict to write_all roles. The
|
|
83
|
+
// SDK claim-by-metadata uses a flat role filter and cannot enforce write_self
|
|
84
|
+
// ownership, so self-scope members are excluded from this path (their items
|
|
85
|
+
// are pre-claimed and acted on by id).
|
|
86
|
+
const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
|
|
87
|
+
const allowedRoles = writeScope.global ? null : writeScope.allRoles;
|
|
85
88
|
const result = await escalationService.claimByMetadata(input.key, input.value, claimUserId, input.durationMinutes, input.metadata, allowedRoles);
|
|
86
89
|
if (!result) {
|
|
87
90
|
// No rows matched. Check if candidates existed (role mismatch vs no match).
|
|
@@ -120,8 +123,14 @@ async function resolveByMetadata(input, auth) {
|
|
|
120
123
|
if ('error' in resolved)
|
|
121
124
|
return resolved.error;
|
|
122
125
|
const resolveUserId = resolved.userId;
|
|
123
|
-
|
|
124
|
-
|
|
126
|
+
// Write-scope folds into the atomic resolve SQL: global → no filter; otherwise
|
|
127
|
+
// write_all roles match any item, write_self roles match only items assigned
|
|
128
|
+
// to the resolver. A non-global caller cannot pass a foreign assignee, so the
|
|
129
|
+
// self-branch (assigned_to = resolveUserId) is the caller's own items.
|
|
130
|
+
const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
|
|
131
|
+
const writeAllRoles = writeScope.global ? null : writeScope.allRoles;
|
|
132
|
+
const writeSelfRoles = writeScope.global ? null : writeScope.selfRoles;
|
|
133
|
+
const result = await escalationService.resolveByMetadataAtomic(input.key, input.value, resolveUserId, input.resolverPayload, input.metadata, writeAllRoles, writeSelfRoles);
|
|
125
134
|
if (result.outcome === 'not_found') {
|
|
126
135
|
return { status: 404, error: 'No pending escalation found for this metadata, or insufficient role permissions' };
|
|
127
136
|
}
|
|
@@ -161,17 +170,3 @@ async function resolveByMetadata(input, auth) {
|
|
|
161
170
|
return { status: 500, error: err.message };
|
|
162
171
|
}
|
|
163
172
|
}
|
|
164
|
-
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
165
|
-
/**
|
|
166
|
-
* Resolve the set of roles the caller is allowed to act on.
|
|
167
|
-
* Returns null for global access (superadmin/admin), or string[] for scoped users.
|
|
168
|
-
*/
|
|
169
|
-
async function resolveAllowedRoles(userId) {
|
|
170
|
-
if (await userService.hasGlobalEscalationAccess(userId))
|
|
171
|
-
return null;
|
|
172
|
-
const userRoles = await userService.getUserRoles(userId);
|
|
173
|
-
// Return the user's roles (may be empty → SQL filters out all rows).
|
|
174
|
-
// System/service accounts that need unrestricted access should be
|
|
175
|
-
// seeded with the superadmin role via start({ seed: { admin } }).
|
|
176
|
-
return userRoles.map(r => r.role);
|
|
177
|
-
}
|
|
@@ -15,7 +15,8 @@ import type { LTApiResult, LTApiAuth } from '../../types/sdk';
|
|
|
15
15
|
export declare function resolveEscalation(input: {
|
|
16
16
|
id: string;
|
|
17
17
|
resolverPayload: Record<string, any>;
|
|
18
|
-
|
|
18
|
+
metadata?: Record<string, any>;
|
|
19
|
+
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
19
20
|
/**
|
|
20
21
|
* Resolve an efficient (atomic) escalation directly by its `signal_key` and
|
|
21
22
|
* resume the waiting workflow in place. For webhook callers that know the
|
|
@@ -25,4 +26,21 @@ export declare function resolveEscalation(input: {
|
|
|
25
26
|
export declare function resolveBySignalKey(input: {
|
|
26
27
|
signalKey: string;
|
|
27
28
|
resolverPayload: Record<string, any>;
|
|
29
|
+
metadata?: Record<string, any>;
|
|
30
|
+
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a SET of escalations by id in one guarded statement (the public,
|
|
33
|
+
* set-based sibling of {@link resolveEscalation}). Delegates to the SDK's
|
|
34
|
+
* `client.resolveMany` via the service layer.
|
|
35
|
+
*
|
|
36
|
+
* RBAC: a scoped caller may only resolve rows whose role they hold. `role` is
|
|
37
|
+
* immutable per escalation, so this authorization read (`getEscalationRoles`)
|
|
38
|
+
* does not race the guarded `resolveMany` that follows — and global principals
|
|
39
|
+
* skip it entirely. Use for bookkeeping rows woken collectively; it does NOT
|
|
40
|
+
* deliver per-row signals (see services `resolveEscalationsByIds`).
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveByIds(input: {
|
|
43
|
+
ids: string[];
|
|
44
|
+
resolverPayload: Record<string, any>;
|
|
45
|
+
metadata?: Record<string, any>;
|
|
28
46
|
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.resolveEscalation = resolveEscalation;
|
|
37
37
|
exports.resolveBySignalKey = resolveBySignalKey;
|
|
38
|
+
exports.resolveByIds = resolveByIds;
|
|
38
39
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
39
40
|
const taskService = __importStar(require("../../services/task"));
|
|
40
41
|
const escalation_strategy_1 = require("../../services/escalation-strategy");
|
|
@@ -57,9 +58,9 @@ const helpers_1 = require("./helpers");
|
|
|
57
58
|
* Password fields in the resolver payload are replaced with ephemeral
|
|
58
59
|
* tokens (15-minute TTL) so plaintext never enters the signal store.
|
|
59
60
|
*/
|
|
60
|
-
async function resolveEscalation(input,
|
|
61
|
+
async function resolveEscalation(input, auth) {
|
|
61
62
|
try {
|
|
62
|
-
const { id, resolverPayload } = input;
|
|
63
|
+
const { id, resolverPayload, metadata } = input;
|
|
63
64
|
if (!resolverPayload) {
|
|
64
65
|
return { status: 400, error: 'resolverPayload is required' };
|
|
65
66
|
}
|
|
@@ -70,22 +71,38 @@ async function resolveEscalation(input, _auth) {
|
|
|
70
71
|
return { status: 409, error: 'Escalation is cancelled' };
|
|
71
72
|
if (escalation.status !== 'pending')
|
|
72
73
|
return { status: 409, error: 'Escalation not available for resolution' };
|
|
74
|
+
// Hybrid RBAC for resolve ("ack", a write verb). Non-disclosure first: an
|
|
75
|
+
// escalation the caller cannot even SEE returns 404 (matches resolveBySignalKey —
|
|
76
|
+
// do not reveal out-of-scope rows). One they CAN see but cannot act on returns an
|
|
77
|
+
// informative 403 (e.g. a write_self owner whose item isn't assigned to them).
|
|
78
|
+
// Global access bypasses both.
|
|
79
|
+
if (await (0, helpers_1.assertReadAccess)(auth.userId, escalation)) {
|
|
80
|
+
return { status: 404, error: 'Escalation not found' };
|
|
81
|
+
}
|
|
82
|
+
const denied = await (0, helpers_1.assertWriteAccess)(auth.userId, escalation);
|
|
83
|
+
if (denied)
|
|
84
|
+
return denied;
|
|
85
|
+
// `metadata` (the outcome patch) is never written separately — it rides WITH the
|
|
86
|
+
// resolverPayload to whichever path performs the resolve, so it merges inside the
|
|
87
|
+
// single status='pending'-guarded UPDATE (HotMesh `resolve(metadata)`). Paths that
|
|
88
|
+
// resolve here pass it as the 3rd arg; paths that resume a workflow carry it on the
|
|
89
|
+
// signal so the downstream resolve commits it atomically.
|
|
73
90
|
// Path A: conditionLT signal
|
|
74
91
|
const metadataSignalId = escalation.metadata?.signal_id;
|
|
75
92
|
if (metadataSignalId && escalation.workflow_id && escalation.task_queue && escalation.workflow_type) {
|
|
76
|
-
return resolveViaConditionSignal(escalation, resolverPayload);
|
|
93
|
+
return resolveViaConditionSignal(escalation, resolverPayload, metadata);
|
|
77
94
|
}
|
|
78
95
|
// Path B: waitFor signal routing
|
|
79
96
|
const signalRouting = escalation.metadata?.signal_routing;
|
|
80
97
|
if (signalRouting?.signalId) {
|
|
81
|
-
return resolveViaSignalRouting(escalation, resolverPayload);
|
|
98
|
+
return resolveViaSignalRouting(escalation, resolverPayload, metadata);
|
|
82
99
|
}
|
|
83
100
|
// Path 0: efficient (atomic) escalation — signal_key resumes in place.
|
|
84
101
|
// The row was written inside the workflow's Leg1 checkpoint via
|
|
85
102
|
// `condition(signalId, config)`. The SDK's resolve marks it resolved AND
|
|
86
103
|
// delivers the signal to `signal_key`, resuming THIS job — no re-run.
|
|
87
104
|
if (escalation.signal_key) {
|
|
88
|
-
return resolveViaSignalKey(escalation, resolverPayload);
|
|
105
|
+
return resolveViaSignalKey(escalation, resolverPayload, metadata);
|
|
89
106
|
}
|
|
90
107
|
// Path C: escalation strategy may redirect to triage
|
|
91
108
|
const envelope = await reconstructEnvelope(escalation);
|
|
@@ -93,16 +110,16 @@ async function resolveEscalation(input, _auth) {
|
|
|
93
110
|
if (strategy) {
|
|
94
111
|
const directive = await strategy.onResolution({ escalation, resolverPayload, envelope });
|
|
95
112
|
if (directive.action === 'triage') {
|
|
96
|
-
return resolveViaTriage(escalation, resolverPayload, directive.triageEnvelope);
|
|
113
|
+
return resolveViaTriage(escalation, resolverPayload, directive.triageEnvelope, metadata);
|
|
97
114
|
}
|
|
98
115
|
}
|
|
99
|
-
// Path D: notification-only — no workflow to restart
|
|
116
|
+
// Path D: notification-only — no workflow to restart. One atomic resolve.
|
|
100
117
|
if (!escalation.workflow_type || !escalation.task_queue) {
|
|
101
|
-
await escalationService.resolveEscalation(escalation.id, resolverPayload);
|
|
118
|
+
await escalationService.resolveEscalation(escalation.id, resolverPayload, metadata);
|
|
102
119
|
return { status: 200, data: { acknowledged: true, escalationId: escalation.id } };
|
|
103
120
|
}
|
|
104
121
|
// Path E: standard re-run
|
|
105
|
-
return resolveViaRerun(escalation, envelope, resolverPayload);
|
|
122
|
+
return resolveViaRerun(escalation, envelope, resolverPayload, metadata);
|
|
106
123
|
}
|
|
107
124
|
catch (err) {
|
|
108
125
|
return { status: 500, error: err.message };
|
|
@@ -116,7 +133,7 @@ async function resolveEscalation(input, _auth) {
|
|
|
116
133
|
*/
|
|
117
134
|
async function resolveBySignalKey(input, auth) {
|
|
118
135
|
try {
|
|
119
|
-
const { signalKey, resolverPayload } = input;
|
|
136
|
+
const { signalKey, resolverPayload, metadata } = input;
|
|
120
137
|
if (!signalKey)
|
|
121
138
|
return { status: 400, error: 'signalKey is required' };
|
|
122
139
|
if (!resolverPayload)
|
|
@@ -126,11 +143,52 @@ async function resolveBySignalKey(input, auth) {
|
|
|
126
143
|
return { status: 404, error: 'Escalation not found' };
|
|
127
144
|
if (escalation.status !== 'pending')
|
|
128
145
|
return { status: 409, error: 'Escalation not available for resolution' };
|
|
129
|
-
|
|
130
|
-
|
|
146
|
+
// Resolve is a write verb — scope-gate it (write_self may resolve only its own item).
|
|
147
|
+
const denied = await (0, helpers_1.assertWriteAccess)(auth.userId, escalation);
|
|
148
|
+
if (denied)
|
|
131
149
|
return { status: 404, error: 'Escalation not found' };
|
|
150
|
+
return resolveViaSignalKey(escalation, resolverPayload, metadata);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
return { status: 500, error: err.message };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Resolve a SET of escalations by id in one guarded statement (the public,
|
|
158
|
+
* set-based sibling of {@link resolveEscalation}). Delegates to the SDK's
|
|
159
|
+
* `client.resolveMany` via the service layer.
|
|
160
|
+
*
|
|
161
|
+
* RBAC: a scoped caller may only resolve rows whose role they hold. `role` is
|
|
162
|
+
* immutable per escalation, so this authorization read (`getEscalationRoles`)
|
|
163
|
+
* does not race the guarded `resolveMany` that follows — and global principals
|
|
164
|
+
* skip it entirely. Use for bookkeeping rows woken collectively; it does NOT
|
|
165
|
+
* deliver per-row signals (see services `resolveEscalationsByIds`).
|
|
166
|
+
*/
|
|
167
|
+
async function resolveByIds(input, auth) {
|
|
168
|
+
try {
|
|
169
|
+
const { ids, resolverPayload, metadata } = input;
|
|
170
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
171
|
+
return { status: 400, error: 'ids must be a non-empty array' };
|
|
132
172
|
}
|
|
133
|
-
|
|
173
|
+
if (!resolverPayload)
|
|
174
|
+
return { status: 400, error: 'resolverPayload is required' };
|
|
175
|
+
// Bulk resolve ("ack") is a write verb, gated PER ITEM: write_all roles may
|
|
176
|
+
// resolve any item in the role; write_self roles may resolve only items assigned
|
|
177
|
+
// to the caller (same rule single-resolve applies via assertWriteAccess). Global
|
|
178
|
+
// bypasses. A missing id or any out-of-scope item → 404 (non-disclosure), and
|
|
179
|
+
// nothing is resolved.
|
|
180
|
+
const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
|
|
181
|
+
if (!writeScope.global) {
|
|
182
|
+
const allSet = new Set(writeScope.allRoles);
|
|
183
|
+
const selfSet = new Set(writeScope.selfRoles);
|
|
184
|
+
const rows = await escalationService.getEscalationScopeRows(ids);
|
|
185
|
+
const writable = (r) => allSet.has(r.role) || (selfSet.has(r.role) && r.assigned_to === auth.userId);
|
|
186
|
+
if (rows.length !== ids.length || !rows.every(writable)) {
|
|
187
|
+
return { status: 404, error: 'One or more escalations not found' };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const resolved = await escalationService.resolveEscalationsByIds(ids, resolverPayload, metadata);
|
|
191
|
+
return { status: 200, data: { resolved: resolved.length, escalationIds: resolved.map((e) => e.id) } };
|
|
134
192
|
}
|
|
135
193
|
catch (err) {
|
|
136
194
|
return { status: 500, error: err.message };
|
|
@@ -138,11 +196,18 @@ async function resolveBySignalKey(input, auth) {
|
|
|
138
196
|
}
|
|
139
197
|
// ── Resolution paths ─────────────────────────────────────────────────────
|
|
140
198
|
/** Path A: lightweight conditionLT signal — inject $escalation_id and signal the running workflow. */
|
|
141
|
-
async function resolveViaConditionSignal(escalation, resolverPayload) {
|
|
199
|
+
async function resolveViaConditionSignal(escalation, resolverPayload, metadata) {
|
|
142
200
|
const signalId = escalation.metadata.signal_id;
|
|
143
201
|
const client = (0, workers_1.createClient)();
|
|
144
202
|
const handle = await client.workflow.getHandle(escalation.task_queue, escalation.workflow_type, escalation.workflow_id);
|
|
145
|
-
|
|
203
|
+
// The row is resolved downstream by the workflow's `conditionLT` → `ltResolveEscalation`.
|
|
204
|
+
// Carry the outcome patch on the signal ($escalation_metadata, symmetric to $escalation_id)
|
|
205
|
+
// so it merges inside that single atomic resolve — never a separate write here.
|
|
206
|
+
await handle.signal(signalId, {
|
|
207
|
+
...resolverPayload,
|
|
208
|
+
$escalation_id: escalation.id,
|
|
209
|
+
...(metadata ? { $escalation_metadata: metadata } : {}),
|
|
210
|
+
});
|
|
146
211
|
// Event published by service layer (services/escalation/crud.ts)
|
|
147
212
|
return signaledResult(escalation, escalation.workflow_id);
|
|
148
213
|
}
|
|
@@ -152,9 +217,11 @@ async function resolveViaConditionSignal(escalation, resolverPayload) {
|
|
|
152
217
|
* transaction, so the original job resumes in place (no re-run, no separate
|
|
153
218
|
* resolve activity). Password fields are redacted before they enter the signal.
|
|
154
219
|
*/
|
|
155
|
-
async function resolveViaSignalKey(escalation, resolverPayload) {
|
|
220
|
+
async function resolveViaSignalKey(escalation, resolverPayload, metadata) {
|
|
156
221
|
const signalPayload = await redactPasswords(resolverPayload, escalation.metadata?.form_schema);
|
|
157
|
-
|
|
222
|
+
// One atomic call: status→resolved, signal delivered, and the outcome patch merged
|
|
223
|
+
// into the GIN-indexed metadata — all inside the single WHERE-guarded UPDATE.
|
|
224
|
+
const resolved = await escalationService.resolveEscalation(escalation.id, signalPayload, metadata);
|
|
158
225
|
if (!resolved) {
|
|
159
226
|
return { status: 409, error: 'Escalation not available for resolution' };
|
|
160
227
|
}
|
|
@@ -162,15 +229,18 @@ async function resolveViaSignalKey(escalation, resolverPayload) {
|
|
|
162
229
|
return signaledResult(escalation, escalation.workflow_id || '');
|
|
163
230
|
}
|
|
164
231
|
/** Path B: waitFor signal escalation — signal via YAML engine or Durable handle. */
|
|
165
|
-
async function resolveViaSignalRouting(escalation, resolverPayload) {
|
|
232
|
+
async function resolveViaSignalRouting(escalation, resolverPayload, metadata) {
|
|
166
233
|
const signalRouting = escalation.metadata.signal_routing;
|
|
167
234
|
const signalPayload = await redactPasswords(resolverPayload, escalation.metadata?.form_schema);
|
|
168
235
|
if (signalRouting.engine === 'yaml' && signalRouting.hookTopic && signalRouting.appId) {
|
|
236
|
+
// YAML resolves transactionally inside the workflow — carry the patch on the signal
|
|
237
|
+
// so it commits with that resolve, never as a separate write.
|
|
169
238
|
const engine = await (0, deployer_1.getEngine)(signalRouting.appId);
|
|
170
239
|
await engine.signal(signalRouting.hookTopic, {
|
|
171
240
|
...signalPayload,
|
|
172
241
|
escalationId: escalation.id,
|
|
173
242
|
job_id: signalRouting.jobId,
|
|
243
|
+
...(metadata ? { $escalation_metadata: metadata } : {}),
|
|
174
244
|
});
|
|
175
245
|
}
|
|
176
246
|
else if (signalRouting.workflowId) {
|
|
@@ -178,16 +248,25 @@ async function resolveViaSignalRouting(escalation, resolverPayload) {
|
|
|
178
248
|
const handle = await client.workflow.getHandle(signalRouting.taskQueue, signalRouting.workflowType, signalRouting.workflowId);
|
|
179
249
|
await handle.signal(signalRouting.signalId, signalPayload);
|
|
180
250
|
}
|
|
181
|
-
//
|
|
251
|
+
// Durable resolves here — one atomic call carries the outcome patch.
|
|
252
|
+
// Persist the SAME redacted payload that was delivered to the signal: the raw
|
|
253
|
+
// resolverPayload (with plaintext passwords) must never land in resolver_payload.
|
|
254
|
+
// Ordering is crash-safe-forward: the signal is delivered BEFORE this resolve, so a
|
|
255
|
+
// crash in between leaves the row `pending` and a retry re-signals (a no-op to an
|
|
256
|
+
// already-resumed workflow) then resolves — never a resolved row with a stuck workflow.
|
|
182
257
|
if (signalRouting.engine !== 'yaml') {
|
|
183
|
-
await escalationService.resolveEscalation(escalation.id,
|
|
258
|
+
await escalationService.resolveEscalation(escalation.id, signalPayload, metadata);
|
|
184
259
|
}
|
|
185
260
|
// Event published by service layer (services/escalation/crud.ts)
|
|
186
261
|
return signaledResult(escalation, signalRouting.workflowId || signalRouting.appId);
|
|
187
262
|
}
|
|
188
263
|
/** Path C: escalation strategy directed triage — start a triage workflow. */
|
|
189
|
-
async function resolveViaTriage(escalation, resolverPayload, triageEnvelope) {
|
|
190
|
-
|
|
264
|
+
async function resolveViaTriage(escalation, resolverPayload, triageEnvelope, metadata) {
|
|
265
|
+
// Deterministic id: an escalation is terminal-once, so `triage-<id>` is unique for
|
|
266
|
+
// its lifetime. A retry after a mid-saga crash re-targets the SAME job key (HotMesh
|
|
267
|
+
// upserts jobs by (workflowId, app_id)) instead of spawning a duplicate triage —
|
|
268
|
+
// and the final resolve below is the `status='pending'`-guarded arbiter.
|
|
269
|
+
const triageWorkflowId = `triage-${escalation.id}`;
|
|
191
270
|
const client = (0, workers_1.createClient)();
|
|
192
271
|
await taskService.createTask({
|
|
193
272
|
workflow_id: triageWorkflowId,
|
|
@@ -212,7 +291,7 @@ async function resolveViaTriage(escalation, resolverPayload, triageEnvelope) {
|
|
|
212
291
|
await escalationService.resolveEscalation(escalation.id, {
|
|
213
292
|
...resolverPayload,
|
|
214
293
|
_lt: { ...resolverPayload._lt, triaged: true, triageWorkflowId },
|
|
215
|
-
});
|
|
294
|
+
}, metadata);
|
|
216
295
|
// Event published by service layer (services/escalation/crud.ts)
|
|
217
296
|
return {
|
|
218
297
|
status: 200,
|
|
@@ -220,10 +299,16 @@ async function resolveViaTriage(escalation, resolverPayload, triageEnvelope) {
|
|
|
220
299
|
};
|
|
221
300
|
}
|
|
222
301
|
/** Path E: standard re-run — inject resolver data and restart the original workflow. */
|
|
223
|
-
async function resolveViaRerun(escalation, envelope, resolverPayload) {
|
|
302
|
+
async function resolveViaRerun(escalation, envelope, resolverPayload, metadata) {
|
|
224
303
|
envelope.resolver = resolverPayload;
|
|
225
|
-
|
|
226
|
-
|
|
304
|
+
// Carry the outcome patch on the rerun envelope so the downstream resolve (when the
|
|
305
|
+
// re-run completes) commits it atomically — not a separate write to the old row.
|
|
306
|
+
envelope.lt = { ...envelope.lt, escalationId: escalation.id, escalationMetadata: metadata };
|
|
307
|
+
// Deterministic id (see resolveViaTriage): `rerun-<id>` is unique per escalation
|
|
308
|
+
// lifetime, so a retry re-targets the same job rather than spawning a duplicate
|
|
309
|
+
// re-run. The old escalation is resolved downstream by the interceptor when the
|
|
310
|
+
// re-run executes (services/interceptor/lifecycle.ts), which is `pending`-guarded.
|
|
311
|
+
const newWorkflowId = `rerun-${escalation.id}`;
|
|
227
312
|
const client = (0, workers_1.createClient)();
|
|
228
313
|
await client.workflow.start({
|
|
229
314
|
workflowName: escalation.workflow_type,
|
|
@@ -37,7 +37,6 @@ exports.getEscalation = getEscalation;
|
|
|
37
37
|
exports.getEscalationsByWorkflowId = getEscalationsByWorkflowId;
|
|
38
38
|
exports.escalateToRole = escalateToRole;
|
|
39
39
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
40
|
-
const userService = __importStar(require("../../services/user"));
|
|
41
40
|
const roleService = __importStar(require("../../services/role"));
|
|
42
41
|
const helpers_1 = require("./helpers");
|
|
43
42
|
// ── Single-escalation routes ───────────────────────────────────────────────
|
|
@@ -56,13 +55,9 @@ async function getEscalation(input, auth) {
|
|
|
56
55
|
if (!escalation) {
|
|
57
56
|
return { status: 404, error: 'Escalation not found' };
|
|
58
57
|
}
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
if (!userHasRole) {
|
|
63
|
-
return { status: 403, error: 'Not authorized to view this escalation' };
|
|
64
|
-
}
|
|
65
|
-
}
|
|
58
|
+
const denied = await (0, helpers_1.assertReadAccess)(auth.userId, escalation);
|
|
59
|
+
if (denied)
|
|
60
|
+
return denied;
|
|
66
61
|
return { status: 200, data: escalation };
|
|
67
62
|
}
|
|
68
63
|
catch (err) {
|
|
@@ -108,6 +103,11 @@ async function escalateToRole(input, auth) {
|
|
|
108
103
|
if (escalation.status !== 'pending') {
|
|
109
104
|
return { status: 409, error: 'Escalation is not pending' };
|
|
110
105
|
}
|
|
106
|
+
// Escalating routes the item to another role — a queue-management verb that
|
|
107
|
+
// self-scope owners and read-only members may not perform.
|
|
108
|
+
const manageDenied = await (0, helpers_1.assertQueueManageAccess)(auth.userId, escalation.role);
|
|
109
|
+
if (manageDenied)
|
|
110
|
+
return manageDenied;
|
|
111
111
|
const canEscalate = await roleService.canEscalateTo(auth.userId, escalation.role, targetRole);
|
|
112
112
|
if (!canEscalate) {
|
|
113
113
|
return { status: 403, error: 'Not authorized to escalate to this role' };
|
package/build/api/users.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { LTRoleType } from '../types';
|
|
|
6
6
|
* @param input.role — filter by role name
|
|
7
7
|
* @param input.roleType — filter by role type (superadmin, admin, member)
|
|
8
8
|
* @param input.status — filter by user status
|
|
9
|
+
* @param input.search — free-text match on display name, email, or external_id
|
|
9
10
|
* @param input.limit — maximum number of users to return
|
|
10
11
|
* @param input.offset — number of users to skip for pagination
|
|
11
12
|
* @returns `{ status: 200, data: User[] }` on success
|
|
@@ -14,6 +15,7 @@ export declare function listUsers(input: {
|
|
|
14
15
|
role?: string;
|
|
15
16
|
roleType?: LTRoleType;
|
|
16
17
|
status?: string;
|
|
18
|
+
search?: string;
|
|
17
19
|
limit?: number;
|
|
18
20
|
offset?: number;
|
|
19
21
|
}): Promise<LTApiResult>;
|
|
@@ -47,6 +49,8 @@ export declare function createUser(input: {
|
|
|
47
49
|
roles?: {
|
|
48
50
|
role: string;
|
|
49
51
|
type: string;
|
|
52
|
+
read_scope?: string;
|
|
53
|
+
write_scope?: string;
|
|
50
54
|
}[];
|
|
51
55
|
metadata?: Record<string, any>;
|
|
52
56
|
}): Promise<LTApiResult>;
|
|
@@ -103,6 +107,8 @@ export declare function addUserRole(input: {
|
|
|
103
107
|
id: string;
|
|
104
108
|
role: string;
|
|
105
109
|
type: string;
|
|
110
|
+
read_scope?: string;
|
|
111
|
+
write_scope?: string;
|
|
106
112
|
}): Promise<LTApiResult>;
|
|
107
113
|
/**
|
|
108
114
|
* Remove a role from a user. Requires admin privileges.
|