@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
package/README.md
CHANGED
|
@@ -302,6 +302,7 @@ All modes share PostgreSQL and scale independently. See [Cloud Deployment](https
|
|
|
302
302
|
| [Compiler](https://github.com/hotmeshio/long-tail/blob/main/docs/compiler.md) | `ltc compile` — durable TypeScript to YAML DAGs |
|
|
303
303
|
| [CLI](https://github.com/hotmeshio/long-tail/blob/main/docs/cli.md) | `ltc` — terminal access to workflows, escalations, knowledge, MCP |
|
|
304
304
|
| [Escalation Strategies](https://github.com/hotmeshio/long-tail/blob/main/docs/escalation-strategies.md) | Default, MCP triage, custom handlers |
|
|
305
|
+
| [Faceted Routing](https://github.com/hotmeshio/long-tail/blob/main/docs/faceted-routing.md) | Query and atomically claim the queue by facets; dispatcher pattern |
|
|
305
306
|
| [SDK](https://github.com/hotmeshio/long-tail/blob/main/docs/sdk.md) | Embedded usage, `createClient`, event subscriptions |
|
|
306
307
|
| [Architecture](https://github.com/hotmeshio/long-tail/blob/main/docs/architecture.md) | Project structure, conventions, discovery |
|
|
307
308
|
| [Cloud](https://github.com/hotmeshio/long-tail/blob/main/docs/cloud.md) | AWS ECS, GCP Cloud Run, Docker |
|
|
@@ -236,7 +236,15 @@ async function startTriageWorkflow(escalation, hint, userId, client) {
|
|
|
236
236
|
}
|
|
237
237
|
catch { }
|
|
238
238
|
}
|
|
239
|
-
|
|
239
|
+
// Deterministic id: an escalation is terminal-once, so `triage-<id>` is unique for
|
|
240
|
+
// its lifetime. A retry re-targets the same triage job (HotMesh upserts jobs by
|
|
241
|
+
// (workflowId, app_id)) instead of spawning a duplicate. NOTE: the bulk flow
|
|
242
|
+
// resolves the set FIRST (bulkResolveForTriage) then starts triage per row, so a
|
|
243
|
+
// crash between the set-resolve and a given start leaves that escalation
|
|
244
|
+
// resolved-but-not-triaged — a retry's resolveMany returns only freshly-resolved
|
|
245
|
+
// rows and will not re-drive it. Closing that gap needs a durable orchestrator
|
|
246
|
+
// (resolve+triage in one workflow); tracked as a follow-up, not fixed here.
|
|
247
|
+
const triageWorkflowId = `triage-${escalation.id}`;
|
|
240
248
|
const triageEnvelope = {
|
|
241
249
|
data: {
|
|
242
250
|
escalationId: escalation.id,
|
|
@@ -37,7 +37,6 @@ exports.cancelSingleEscalation = cancelSingleEscalation;
|
|
|
37
37
|
exports.bulkCancel = bulkCancel;
|
|
38
38
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
39
39
|
const helpers_1 = require("./helpers");
|
|
40
|
-
const userService = __importStar(require("../../services/user"));
|
|
41
40
|
/**
|
|
42
41
|
* Cancel a single escalation. The caller must hold global escalation access
|
|
43
42
|
* or the escalation's role.
|
|
@@ -52,12 +51,11 @@ async function cancelSingleEscalation(input, auth) {
|
|
|
52
51
|
const escalation = await escalationService.getEscalation(id);
|
|
53
52
|
if (!escalation)
|
|
54
53
|
return { status: 404, error: 'Escalation not found' };
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
54
|
+
// Cancel ("delete") is a write verb: write_all may cancel any item in the role,
|
|
55
|
+
// write_self may cancel an item assigned to them, read-only members may not.
|
|
56
|
+
const denied = await (0, helpers_1.assertWriteAccess)(auth.userId, escalation);
|
|
57
|
+
if (denied)
|
|
58
|
+
return denied;
|
|
61
59
|
const cancelled = await escalationService.cancelEscalation(id);
|
|
62
60
|
if (!cancelled) {
|
|
63
61
|
return { status: 409, error: 'Escalation is not cancellable (already terminal)' };
|
|
@@ -36,7 +36,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.claimEscalation = claimEscalation;
|
|
37
37
|
exports.releaseEscalation = releaseEscalation;
|
|
38
38
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
39
|
-
const userService = __importStar(require("../../services/user"));
|
|
40
39
|
const helpers_1 = require("./helpers");
|
|
41
40
|
/**
|
|
42
41
|
* Claim a pending escalation for the authenticated user.
|
|
@@ -57,20 +56,16 @@ async function claimEscalation(input, auth) {
|
|
|
57
56
|
if (!escalation) {
|
|
58
57
|
return { status: 404, error: 'Escalation not found' };
|
|
59
58
|
}
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
error: `You must have the "${escalation.role}" role to claim this escalation`,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
}
|
|
59
|
+
// Write-scope gate: write_all may claim any item in the role; write_self may
|
|
60
|
+
// only (re)claim an item already assigned to them (extension); read-only and
|
|
61
|
+
// non-members are denied. Global access bypasses.
|
|
62
|
+
const writeDenied = await (0, helpers_1.assertWriteAccess)(auth.userId, escalation);
|
|
63
|
+
if (writeDenied) {
|
|
64
|
+
// Unhappy path: a global caller may JIT-provision their own role membership
|
|
65
|
+
// (ensureRoleMembership requires global authority, so this no-ops otherwise).
|
|
66
|
+
const provisioned = await (0, helpers_1.ensureRoleMembership)(auth.userId, escalation.role, auth.userId, provisionIfAbsent);
|
|
67
|
+
if (!provisioned)
|
|
68
|
+
return writeDenied;
|
|
74
69
|
}
|
|
75
70
|
const result = await escalationService.claimEscalation(id, auth.userId, durationMinutes);
|
|
76
71
|
if (!result) {
|
|
@@ -94,6 +89,14 @@ async function claimEscalation(input, auth) {
|
|
|
94
89
|
*/
|
|
95
90
|
async function releaseEscalation(input, auth) {
|
|
96
91
|
try {
|
|
92
|
+
const escalation = await escalationService.getEscalation(input.id);
|
|
93
|
+
if (!escalation)
|
|
94
|
+
return { status: 404, error: 'Escalation not found' };
|
|
95
|
+
// Releasing returns the item to the pool — a queue-management verb. A
|
|
96
|
+
// self-scope owner cannot release (it would forfeit access to their own item).
|
|
97
|
+
const manageDenied = await (0, helpers_1.assertQueueManageAccess)(auth.userId, escalation.role);
|
|
98
|
+
if (manageDenied)
|
|
99
|
+
return manageDenied;
|
|
97
100
|
const result = await escalationService.releaseEscalation(input.id, auth.userId);
|
|
98
101
|
if (!result) {
|
|
99
102
|
return { status: 409, error: 'Escalation not found or not claimed by you' };
|
|
@@ -26,4 +26,10 @@ export declare function createEscalation(input: {
|
|
|
26
26
|
envelope?: string;
|
|
27
27
|
metadata?: Record<string, any>;
|
|
28
28
|
escalation_payload?: string;
|
|
29
|
+
origin_id?: string;
|
|
30
|
+
parent_id?: string;
|
|
31
|
+
task_id?: string;
|
|
32
|
+
workflow_id?: string;
|
|
33
|
+
task_queue?: string;
|
|
34
|
+
workflow_type?: string;
|
|
29
35
|
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
@@ -35,7 +35,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.createEscalation = createEscalation;
|
|
37
37
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
38
|
-
const userService = __importStar(require("../../services/user"));
|
|
39
38
|
const helpers_1 = require("./helpers");
|
|
40
39
|
// ── Create ────────────────────────────────────────────────────────────────
|
|
41
40
|
/**
|
|
@@ -65,13 +64,11 @@ async function createEscalation(input, auth) {
|
|
|
65
64
|
if (!role || typeof role !== 'string') {
|
|
66
65
|
return { status: 400, error: 'role is required' };
|
|
67
66
|
}
|
|
68
|
-
// RBAC:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return { status: 403, error: `You must hold the "${role}" role or be a superadmin to create escalations for it` };
|
|
74
|
-
}
|
|
67
|
+
// RBAC: creating an escalation injects work into a role's queue — a write_all
|
|
68
|
+
// (or global) action. read-only and self-scope members may not create.
|
|
69
|
+
const denied = await (0, helpers_1.assertQueueManageAccess)(auth.userId, role);
|
|
70
|
+
if (denied) {
|
|
71
|
+
return { status: 403, error: `You must have write access to the "${role}" role or be a superadmin to create escalations for it` };
|
|
75
72
|
}
|
|
76
73
|
const escalation = await escalationService.createEscalation({
|
|
77
74
|
type,
|
|
@@ -82,6 +79,12 @@ async function createEscalation(input, auth) {
|
|
|
82
79
|
envelope: input.envelope ?? '{}',
|
|
83
80
|
metadata: input.metadata,
|
|
84
81
|
escalation_payload: input.escalation_payload,
|
|
82
|
+
origin_id: input.origin_id,
|
|
83
|
+
parent_id: input.parent_id,
|
|
84
|
+
task_id: input.task_id,
|
|
85
|
+
workflow_id: input.workflow_id,
|
|
86
|
+
task_queue: input.task_queue,
|
|
87
|
+
workflow_type: input.workflow_type,
|
|
85
88
|
});
|
|
86
89
|
// Event published by service layer (services/escalation/crud.ts)
|
|
87
90
|
return { status: 201, data: escalation };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LTApiAuth, LTApiResult } from '../../types/sdk';
|
|
2
|
+
import type { FacetQuery } from '../../types/facets';
|
|
3
|
+
/** Item-level faceted search over a pond, scoped to the caller's role. */
|
|
4
|
+
export declare function searchByFacets(query: FacetQuery, auth: LTApiAuth): Promise<LTApiResult>;
|
|
5
|
+
/** Batch-claim complete origin groups in priority order, assigned to the calling operator. */
|
|
6
|
+
export declare function claimGroups(input: {
|
|
7
|
+
query: FacetQuery;
|
|
8
|
+
limit?: number;
|
|
9
|
+
durationMinutes?: number;
|
|
10
|
+
sizeFacet?: string;
|
|
11
|
+
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
12
|
+
/** Batch-claim individual rows (FOR UPDATE SKIP LOCKED), assigned to the calling operator. */
|
|
13
|
+
export declare function claimByFacets(input: {
|
|
14
|
+
query: FacetQuery;
|
|
15
|
+
limit?: number;
|
|
16
|
+
durationMinutes?: number;
|
|
17
|
+
allOrNone?: boolean;
|
|
18
|
+
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.searchByFacets = searchByFacets;
|
|
37
|
+
exports.claimGroups = claimGroups;
|
|
38
|
+
exports.claimByFacets = claimByFacets;
|
|
39
|
+
const escalationService = __importStar(require("../../services/escalation"));
|
|
40
|
+
const userService = __importStar(require("../../services/user"));
|
|
41
|
+
// ── Faceted escalation surface (public) ──────────────────────────────────────
|
|
42
|
+
//
|
|
43
|
+
// The faceted-routing read/claim primitives, exposed on the public API so example
|
|
44
|
+
// workflows (and dependent projects) reach the pond the same way the dashboard,
|
|
45
|
+
// MCP, and CLI do — through api/, which calls services/. Faceted routing is the
|
|
46
|
+
// whole-pond dispatcher pattern: searching surfaces OTHER operators' items and
|
|
47
|
+
// claiming takes ownership of unassigned work. So the gate is the broad scope, not
|
|
48
|
+
// mere membership — read_all to search the pond, write_all to claim from it. A
|
|
49
|
+
// global principal (superadmin / admin) passes unfiltered. Self-scope members work
|
|
50
|
+
// only their own assigned items, through the by-id paths — not the pond surface.
|
|
51
|
+
async function callerMaySearchPond(userId, role) {
|
|
52
|
+
if (await userService.hasGlobalEscalationAccess(userId))
|
|
53
|
+
return true;
|
|
54
|
+
const scope = await userService.getRoleScope(userId, role);
|
|
55
|
+
return scope?.read === 'all';
|
|
56
|
+
}
|
|
57
|
+
async function callerMayClaimPond(userId, role) {
|
|
58
|
+
if (await userService.hasGlobalEscalationAccess(userId))
|
|
59
|
+
return true;
|
|
60
|
+
const scope = await userService.getRoleScope(userId, role);
|
|
61
|
+
return scope?.write === 'all';
|
|
62
|
+
}
|
|
63
|
+
/** Item-level faceted search over a pond, scoped to the caller's role. */
|
|
64
|
+
async function searchByFacets(query, auth) {
|
|
65
|
+
try {
|
|
66
|
+
if (!query?.role)
|
|
67
|
+
return { status: 400, error: 'role is required' };
|
|
68
|
+
if (!(await callerMaySearchPond(auth.userId, query.role))) {
|
|
69
|
+
return { status: 403, error: `You must hold the "${query.role}" role with full (read_all) scope or be a superadmin` };
|
|
70
|
+
}
|
|
71
|
+
const result = await escalationService.searchByFacets(query);
|
|
72
|
+
return { status: 200, data: result };
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
return { status: 500, error: err.message };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Batch-claim complete origin groups in priority order, assigned to the calling operator. */
|
|
79
|
+
async function claimGroups(input, auth) {
|
|
80
|
+
try {
|
|
81
|
+
if (!input.query?.role)
|
|
82
|
+
return { status: 400, error: 'role is required' };
|
|
83
|
+
if (!(await callerMayClaimPond(auth.userId, input.query.role))) {
|
|
84
|
+
return { status: 403, error: `You must hold the "${input.query.role}" role with claim (write_all) scope or be a superadmin` };
|
|
85
|
+
}
|
|
86
|
+
const groups = await escalationService.claimGroups(input.query, auth.userId, {
|
|
87
|
+
limit: input.limit,
|
|
88
|
+
durationMinutes: input.durationMinutes,
|
|
89
|
+
sizeFacet: input.sizeFacet,
|
|
90
|
+
});
|
|
91
|
+
return { status: 200, data: { groups } };
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
return { status: 500, error: err.message };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Batch-claim individual rows (FOR UPDATE SKIP LOCKED), assigned to the calling operator. */
|
|
98
|
+
async function claimByFacets(input, auth) {
|
|
99
|
+
try {
|
|
100
|
+
if (!input.query?.role)
|
|
101
|
+
return { status: 400, error: 'role is required' };
|
|
102
|
+
if (!(await callerMayClaimPond(auth.userId, input.query.role))) {
|
|
103
|
+
return { status: 403, error: `You must hold the "${input.query.role}" role with claim (write_all) scope or be a superadmin` };
|
|
104
|
+
}
|
|
105
|
+
const claimed = await escalationService.claimByFacets(input.query, auth.userId, {
|
|
106
|
+
limit: input.limit,
|
|
107
|
+
durationMinutes: input.durationMinutes,
|
|
108
|
+
allOrNone: input.allOrNone,
|
|
109
|
+
});
|
|
110
|
+
return { status: 200, data: { claimed } };
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
return { status: 500, error: err.message };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -1,6 +1,73 @@
|
|
|
1
1
|
export { hasGlobalEscalationAccess } from '../../services/user';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Read-scope partition for escalation search/list/stats. Splits the user's roles
|
|
4
|
+
* into the breadth at which they may SEARCH:
|
|
5
|
+
* - `global` → sees every role's queue (superadmin, admin/admin); no role filter
|
|
6
|
+
* - `allRoles` → roles where read_scope='all' (whole queue visible)
|
|
7
|
+
* - `selfRoles`→ roles where read_scope='self' (only items assigned to them)
|
|
8
|
+
*
|
|
9
|
+
* The caller feeds allRoles + selfRoles + the user's id into the scoped search
|
|
10
|
+
* predicate. `effectiveScope` folds admin/superadmin memberships to read='all'.
|
|
11
|
+
*/
|
|
12
|
+
export interface EscalationReadScope {
|
|
13
|
+
global: boolean;
|
|
14
|
+
allRoles: string[];
|
|
15
|
+
selfRoles: string[];
|
|
16
|
+
}
|
|
17
|
+
export declare function getEscalationReadScope(userId: string): Promise<EscalationReadScope>;
|
|
3
18
|
export declare function validateIds(ids: unknown): ids is string[];
|
|
19
|
+
/**
|
|
20
|
+
* Write-scope partition for metadata-driven resolve. Splits the user's roles by
|
|
21
|
+
* the breadth at which they may ACT (claim/ack/delete):
|
|
22
|
+
* - `global` → may act on any role's queue; no filter
|
|
23
|
+
* - `allRoles` → write_scope='all' (act on any item in the role)
|
|
24
|
+
* - `selfRoles`→ write_scope='self' (act only on items assigned to them)
|
|
25
|
+
* write_scope='none' roles are excluded — read-only memberships cannot act.
|
|
26
|
+
*/
|
|
27
|
+
export interface EscalationWriteScope {
|
|
28
|
+
global: boolean;
|
|
29
|
+
allRoles: string[];
|
|
30
|
+
selfRoles: string[];
|
|
31
|
+
}
|
|
32
|
+
export declare function getEscalationWriteScope(userId: string): Promise<EscalationWriteScope>;
|
|
33
|
+
/**
|
|
34
|
+
* Authorize a READ of one escalation (get single). Global access sees all;
|
|
35
|
+
* otherwise the user must hold the role with read_scope='all', OR read_scope='self'
|
|
36
|
+
* with the escalation assigned to them. Returns an error result, or null if allowed.
|
|
37
|
+
*/
|
|
38
|
+
export declare function assertReadAccess(userId: string, escalation: {
|
|
39
|
+
role: string;
|
|
40
|
+
assigned_to?: string | null;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
status: number;
|
|
43
|
+
error: string;
|
|
44
|
+
} | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Authorize a WRITE on one escalation (claim/ack/delete by id). Global access may
|
|
47
|
+
* act on all; otherwise write_scope='all' may act on any item in the role, and
|
|
48
|
+
* write_scope='self' may act only on items assigned to the user. read-only
|
|
49
|
+
* ('none') and non-members are denied. Returns an error result, or null if allowed.
|
|
50
|
+
*
|
|
51
|
+
* This is an authorization gate over durable ownership (a self-scope user cannot
|
|
52
|
+
* reassign items to themselves), so the subsequent atomic mutation is the single
|
|
53
|
+
* source of truth — no claim race is introduced here.
|
|
54
|
+
*/
|
|
55
|
+
export declare function assertWriteAccess(userId: string, escalation: {
|
|
56
|
+
role: string;
|
|
57
|
+
assigned_to?: string | null;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
status: number;
|
|
60
|
+
error: string;
|
|
61
|
+
} | null>;
|
|
62
|
+
/**
|
|
63
|
+
* Require write_scope='all' (or global) for a queue-management verb — release and
|
|
64
|
+
* escalate move an item out of the user's hands, so self-scope owners (who only
|
|
65
|
+
* fill in their own item) and read-only members may not perform them.
|
|
66
|
+
*/
|
|
67
|
+
export declare function assertQueueManageAccess(userId: string, role: string): Promise<{
|
|
68
|
+
status: number;
|
|
69
|
+
error: string;
|
|
70
|
+
} | null>;
|
|
4
71
|
export declare function checkBulkPermission(userId: string, ids: string[]): Promise<{
|
|
5
72
|
allowed: true;
|
|
6
73
|
} | {
|
|
@@ -19,6 +86,8 @@ export interface ProvisionIfAbsent {
|
|
|
19
86
|
roles?: Array<{
|
|
20
87
|
role: string;
|
|
21
88
|
type?: string;
|
|
89
|
+
read_scope?: string;
|
|
90
|
+
write_scope?: string;
|
|
22
91
|
}>;
|
|
23
92
|
}
|
|
24
93
|
/**
|
|
@@ -34,8 +34,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.hasGlobalEscalationAccess = void 0;
|
|
37
|
-
exports.
|
|
37
|
+
exports.getEscalationReadScope = getEscalationReadScope;
|
|
38
38
|
exports.validateIds = validateIds;
|
|
39
|
+
exports.getEscalationWriteScope = getEscalationWriteScope;
|
|
40
|
+
exports.assertReadAccess = assertReadAccess;
|
|
41
|
+
exports.assertWriteAccess = assertWriteAccess;
|
|
42
|
+
exports.assertQueueManageAccess = assertQueueManageAccess;
|
|
39
43
|
exports.checkBulkPermission = checkBulkPermission;
|
|
40
44
|
exports.resolveAssignee = resolveAssignee;
|
|
41
45
|
exports.ensureRoleMembership = ensureRoleMembership;
|
|
@@ -47,15 +51,98 @@ const publish_1 = require("../../lib/events/publish");
|
|
|
47
51
|
// Re-export from service layer for use by escalation API modules
|
|
48
52
|
var user_1 = require("../../services/user");
|
|
49
53
|
Object.defineProperty(exports, "hasGlobalEscalationAccess", { enumerable: true, get: function () { return user_1.hasGlobalEscalationAccess; } });
|
|
50
|
-
async function
|
|
51
|
-
if (await userService.hasGlobalEscalationAccess(userId))
|
|
52
|
-
return
|
|
54
|
+
async function getEscalationReadScope(userId) {
|
|
55
|
+
if (await userService.hasGlobalEscalationAccess(userId)) {
|
|
56
|
+
return { global: true, allRoles: [], selfRoles: [] };
|
|
57
|
+
}
|
|
53
58
|
const userRoles = await userService.getUserRoles(userId);
|
|
54
|
-
|
|
59
|
+
const allRoles = [];
|
|
60
|
+
const selfRoles = [];
|
|
61
|
+
for (const r of userRoles) {
|
|
62
|
+
const eff = userService.effectiveScope(r.type, r.read_scope, r.write_scope);
|
|
63
|
+
if (eff.read === 'all')
|
|
64
|
+
allRoles.push(r.role);
|
|
65
|
+
else
|
|
66
|
+
selfRoles.push(r.role);
|
|
67
|
+
}
|
|
68
|
+
return { global: false, allRoles, selfRoles };
|
|
55
69
|
}
|
|
56
70
|
function validateIds(ids) {
|
|
57
71
|
return Array.isArray(ids) && ids.length > 0;
|
|
58
72
|
}
|
|
73
|
+
async function getEscalationWriteScope(userId) {
|
|
74
|
+
if (await userService.hasGlobalEscalationAccess(userId)) {
|
|
75
|
+
return { global: true, allRoles: [], selfRoles: [] };
|
|
76
|
+
}
|
|
77
|
+
const userRoles = await userService.getUserRoles(userId);
|
|
78
|
+
const allRoles = [];
|
|
79
|
+
const selfRoles = [];
|
|
80
|
+
for (const r of userRoles) {
|
|
81
|
+
const eff = userService.effectiveScope(r.type, r.read_scope, r.write_scope);
|
|
82
|
+
if (eff.write === 'all')
|
|
83
|
+
allRoles.push(r.role);
|
|
84
|
+
else if (eff.write === 'self')
|
|
85
|
+
selfRoles.push(r.role);
|
|
86
|
+
}
|
|
87
|
+
return { global: false, allRoles, selfRoles };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Authorize a READ of one escalation (get single). Global access sees all;
|
|
91
|
+
* otherwise the user must hold the role with read_scope='all', OR read_scope='self'
|
|
92
|
+
* with the escalation assigned to them. Returns an error result, or null if allowed.
|
|
93
|
+
*/
|
|
94
|
+
async function assertReadAccess(userId, escalation) {
|
|
95
|
+
if (await userService.hasGlobalEscalationAccess(userId))
|
|
96
|
+
return null;
|
|
97
|
+
const scope = await userService.getRoleScope(userId, escalation.role);
|
|
98
|
+
if (!scope)
|
|
99
|
+
return { status: 403, error: 'Not authorized to view this escalation' };
|
|
100
|
+
if (scope.read === 'all')
|
|
101
|
+
return null;
|
|
102
|
+
if (escalation.assigned_to && escalation.assigned_to === userId)
|
|
103
|
+
return null;
|
|
104
|
+
return { status: 403, error: 'Not authorized to view this escalation' };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Authorize a WRITE on one escalation (claim/ack/delete by id). Global access may
|
|
108
|
+
* act on all; otherwise write_scope='all' may act on any item in the role, and
|
|
109
|
+
* write_scope='self' may act only on items assigned to the user. read-only
|
|
110
|
+
* ('none') and non-members are denied. Returns an error result, or null if allowed.
|
|
111
|
+
*
|
|
112
|
+
* This is an authorization gate over durable ownership (a self-scope user cannot
|
|
113
|
+
* reassign items to themselves), so the subsequent atomic mutation is the single
|
|
114
|
+
* source of truth — no claim race is introduced here.
|
|
115
|
+
*/
|
|
116
|
+
async function assertWriteAccess(userId, escalation) {
|
|
117
|
+
if (await userService.hasGlobalEscalationAccess(userId))
|
|
118
|
+
return null;
|
|
119
|
+
const scope = await userService.getRoleScope(userId, escalation.role);
|
|
120
|
+
const write = scope ? scope.write : 'none';
|
|
121
|
+
if (write === 'all')
|
|
122
|
+
return null;
|
|
123
|
+
if (write === 'self') {
|
|
124
|
+
if (escalation.assigned_to && escalation.assigned_to === userId)
|
|
125
|
+
return null;
|
|
126
|
+
return {
|
|
127
|
+
status: 403,
|
|
128
|
+
error: `You may only act on escalations assigned to you in the "${escalation.role}" role`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return { status: 403, error: `You do not have write access to the "${escalation.role}" role` };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Require write_scope='all' (or global) for a queue-management verb — release and
|
|
135
|
+
* escalate move an item out of the user's hands, so self-scope owners (who only
|
|
136
|
+
* fill in their own item) and read-only members may not perform them.
|
|
137
|
+
*/
|
|
138
|
+
async function assertQueueManageAccess(userId, role) {
|
|
139
|
+
if (await userService.hasGlobalEscalationAccess(userId))
|
|
140
|
+
return null;
|
|
141
|
+
const scope = await userService.getRoleScope(userId, role);
|
|
142
|
+
if (scope && scope.write === 'all')
|
|
143
|
+
return null;
|
|
144
|
+
return { status: 403, error: `You do not have permission to manage the "${role}" queue` };
|
|
145
|
+
}
|
|
59
146
|
async function checkBulkPermission(userId, ids) {
|
|
60
147
|
if (await userService.hasGlobalEscalationAccess(userId))
|
|
61
148
|
return { allowed: true };
|
|
@@ -92,7 +179,9 @@ async function resolveAssignee(assignee, auth, provisionIfAbsent) {
|
|
|
92
179
|
if (!hasAuthority) {
|
|
93
180
|
return { error: { status: 403, error: 'Only superadmin or admin can provision users on claim' } };
|
|
94
181
|
}
|
|
95
|
-
// Provision the user
|
|
182
|
+
// Provision the user. The onboarding/customer flow declares the work-surface
|
|
183
|
+
// scope (e.g. read_self + write_self) so the provisioned user sees and acts on
|
|
184
|
+
// only their own pre-claimed item.
|
|
96
185
|
const created = await userService.createUser({
|
|
97
186
|
external_id: assignee,
|
|
98
187
|
display_name: provisionIfAbsent.displayName || assignee,
|
|
@@ -100,6 +189,8 @@ async function resolveAssignee(assignee, auth, provisionIfAbsent) {
|
|
|
100
189
|
roles: (provisionIfAbsent.roles || []).map((r) => ({
|
|
101
190
|
role: r.role,
|
|
102
191
|
type: (r.type || 'member'),
|
|
192
|
+
read_scope: r.read_scope,
|
|
193
|
+
write_scope: r.write_scope,
|
|
103
194
|
})),
|
|
104
195
|
});
|
|
105
196
|
return { userId: created.id };
|
|
@@ -121,7 +212,10 @@ async function ensureRoleMembership(userId, requiredRole, callerUserId, provisio
|
|
|
121
212
|
return false;
|
|
122
213
|
// Add the role — idempotent (ON CONFLICT DO NOTHING)
|
|
123
214
|
try {
|
|
124
|
-
await userService.addUserRole(userId, requiredRole, (declaredRole.type || 'member')
|
|
215
|
+
await userService.addUserRole(userId, requiredRole, (declaredRole.type || 'member'), {
|
|
216
|
+
read_scope: declaredRole.read_scope,
|
|
217
|
+
write_scope: declaredRole.write_scope,
|
|
218
|
+
});
|
|
125
219
|
}
|
|
126
220
|
catch { /* already has it */ }
|
|
127
221
|
return true;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { createEscalation } from './create';
|
|
2
|
-
export { listEscalations, listAvailableEscalations, listDistinctTypes, getEscalationStats } from './list';
|
|
2
|
+
export { listEscalations, listAvailableEscalations, listDistinctTypes, listFacetKeys, getEscalationStats } from './list';
|
|
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 } from './resolve';
|
|
6
|
+
export { resolveEscalation, resolveBySignalKey, resolveByIds } from './resolve';
|
|
7
7
|
export { findByMetadata, claimByMetadata, resolveByMetadata } from './metadata';
|
|
8
8
|
export { cancelSingleEscalation, bulkCancel } from './cancel';
|
|
9
|
+
export { searchByFacets, claimGroups, claimByFacets } from './facets';
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.bulkCancel = exports.cancelSingleEscalation = exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = 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.getEscalationStats = 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.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.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");
|
|
7
7
|
Object.defineProperty(exports, "listEscalations", { enumerable: true, get: function () { return list_1.listEscalations; } });
|
|
8
8
|
Object.defineProperty(exports, "listAvailableEscalations", { enumerable: true, get: function () { return list_1.listAvailableEscalations; } });
|
|
9
9
|
Object.defineProperty(exports, "listDistinctTypes", { enumerable: true, get: function () { return list_1.listDistinctTypes; } });
|
|
10
|
+
Object.defineProperty(exports, "listFacetKeys", { enumerable: true, get: function () { return list_1.listFacetKeys; } });
|
|
10
11
|
Object.defineProperty(exports, "getEscalationStats", { enumerable: true, get: function () { return list_1.getEscalationStats; } });
|
|
11
12
|
var single_1 = require("./single");
|
|
12
13
|
Object.defineProperty(exports, "getEscalation", { enumerable: true, get: function () { return single_1.getEscalation; } });
|
|
@@ -25,6 +26,7 @@ Object.defineProperty(exports, "bulkTriage", { enumerable: true, get: function (
|
|
|
25
26
|
var resolve_1 = require("./resolve");
|
|
26
27
|
Object.defineProperty(exports, "resolveEscalation", { enumerable: true, get: function () { return resolve_1.resolveEscalation; } });
|
|
27
28
|
Object.defineProperty(exports, "resolveBySignalKey", { enumerable: true, get: function () { return resolve_1.resolveBySignalKey; } });
|
|
29
|
+
Object.defineProperty(exports, "resolveByIds", { enumerable: true, get: function () { return resolve_1.resolveByIds; } });
|
|
28
30
|
var metadata_1 = require("./metadata");
|
|
29
31
|
Object.defineProperty(exports, "findByMetadata", { enumerable: true, get: function () { return metadata_1.findByMetadata; } });
|
|
30
32
|
Object.defineProperty(exports, "claimByMetadata", { enumerable: true, get: function () { return metadata_1.claimByMetadata; } });
|
|
@@ -32,3 +34,7 @@ Object.defineProperty(exports, "resolveByMetadata", { enumerable: true, get: fun
|
|
|
32
34
|
var cancel_1 = require("./cancel");
|
|
33
35
|
Object.defineProperty(exports, "cancelSingleEscalation", { enumerable: true, get: function () { return cancel_1.cancelSingleEscalation; } });
|
|
34
36
|
Object.defineProperty(exports, "bulkCancel", { enumerable: true, get: function () { return cancel_1.bulkCancel; } });
|
|
37
|
+
var facets_1 = require("./facets");
|
|
38
|
+
Object.defineProperty(exports, "searchByFacets", { enumerable: true, get: function () { return facets_1.searchByFacets; } });
|
|
39
|
+
Object.defineProperty(exports, "claimGroups", { enumerable: true, get: function () { return facets_1.claimGroups; } });
|
|
40
|
+
Object.defineProperty(exports, "claimByFacets", { enumerable: true, get: function () { return facets_1.claimByFacets; } });
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import type { LTApiResult, LTApiAuth } from '../../types/sdk';
|
|
2
|
+
import type { FacetRange, FacetOrder } from '../../types';
|
|
3
|
+
/** Faceted-query elements a human caller may add to the scoped list/available query. */
|
|
4
|
+
export interface FacetInput {
|
|
5
|
+
roles?: string[];
|
|
6
|
+
facets?: Record<string, any>;
|
|
7
|
+
block?: Record<string, any>[];
|
|
8
|
+
range?: FacetRange[];
|
|
9
|
+
exists?: string[];
|
|
10
|
+
available?: boolean;
|
|
11
|
+
orderBy?: FacetOrder[];
|
|
12
|
+
}
|
|
2
13
|
/**
|
|
3
14
|
* List escalations with optional filters.
|
|
4
15
|
*
|
|
@@ -31,7 +42,7 @@ export declare function listEscalations(input: {
|
|
|
31
42
|
sort_by?: string;
|
|
32
43
|
order?: string;
|
|
33
44
|
search?: string;
|
|
34
|
-
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
45
|
+
} & FacetInput, auth: LTApiAuth): Promise<LTApiResult>;
|
|
35
46
|
/**
|
|
36
47
|
* List escalations available for claim (pending and not actively claimed).
|
|
37
48
|
*
|
|
@@ -59,7 +70,15 @@ export declare function listAvailableEscalations(input: {
|
|
|
59
70
|
sort_by?: string;
|
|
60
71
|
order?: string;
|
|
61
72
|
search?: string;
|
|
62
|
-
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
73
|
+
} & FacetInput, auth: LTApiAuth): Promise<LTApiResult>;
|
|
74
|
+
/**
|
|
75
|
+
* List the distinct top-level metadata facet KEYS visible to the caller. Powers the
|
|
76
|
+
* faceted-query UI's key autocomplete — only keys that actually exist in the caller's
|
|
77
|
+
* (role-scoped) escalations are offered, never description-only text.
|
|
78
|
+
*
|
|
79
|
+
* @returns `{ status: 200, data: { keys: string[] } }`
|
|
80
|
+
*/
|
|
81
|
+
export declare function listFacetKeys(_input: unknown, auth: LTApiAuth): Promise<LTApiResult>;
|
|
63
82
|
/**
|
|
64
83
|
* List all distinct escalation type values.
|
|
65
84
|
*
|