@hotmeshio/long-tail 0.5.11 → 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/diagnostics.d.ts +5 -0
- package/build/api/diagnostics.js +5 -1
- 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 +23 -2
- package/build/api/escalations/list.js +111 -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/settings.d.ts +1 -1
- package/build/api/settings.js +3 -1
- package/build/api/users.d.ts +6 -0
- package/build/api/users.js +29 -1
- package/build/api/workflows/invocation.d.ts +6 -0
- package/build/api/workflows/invocation.js +6 -2
- 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/features.d.ts +19 -0
- package/build/modules/features.js +16 -0
- package/build/modules/version.d.ts +10 -5
- package/build/modules/version.js +27 -11
- package/build/routes/diagnostics.js +15 -2
- 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 +48 -0
- package/build/routes/escalations/resolve.js +22 -4
- package/build/routes/users.js +9 -5
- package/build/routes/workflows/invocation.js +12 -4
- package/build/sdk/index.d.ts +35 -2
- package/build/sdk/index.js +7 -0
- package/build/services/diagnostics/index.d.ts +40 -6
- package/build/services/diagnostics/index.js +54 -5
- 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 +47 -0
- package/build/services/escalation/queries.js +185 -2
- package/build/services/escalation/sql.d.ts +15 -2
- package/build/services/escalation/sql.js +80 -3
- 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/resolve.d.ts +5 -3
- package/build/services/task/resolve.js +9 -6
- package/build/services/task/sql.d.ts +30 -2
- package/build/services/task/sql.js +72 -2
- 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/start/adapters.js +3 -0
- package/build/system/mcp-servers/admin/diagnostics.d.ts +5 -4
- package/build/system/mcp-servers/admin/diagnostics.js +12 -10
- package/build/system/mcp-servers/admin/escalations.d.ts +6 -0
- package/build/system/mcp-servers/admin/escalations.js +247 -54
- package/build/system/mcp-servers/admin/index.js +3 -3
- package/build/system/mcp-servers/admin/schemas.d.ts +417 -3
- package/build/system/mcp-servers/admin/schemas.js +101 -5
- package/build/system/mcp-servers/admin/users.js +28 -2
- package/build/system/mcp-servers/admin/workflows.js +23 -12
- package/build/system/seed/tool-manifests-admin.d.ts +1324 -562
- package/build/system/seed/tool-manifests-admin.js +10 -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/startup.d.ts +9 -0
- package/build/types/user.d.ts +11 -0
- package/dashboard/dist/assets/AdminDashboard-2CZ8py9X.js +2 -0
- package/dashboard/dist/assets/{AdminDashboard-DZpduYdW.js.map → AdminDashboard-2CZ8py9X.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-SFnHMIP6.js → AgentConfigPage-D68sodgo.js} +7 -7
- package/dashboard/dist/assets/{AgentConfigPage-SFnHMIP6.js.map → AgentConfigPage-D68sodgo.js.map} +1 -1
- package/dashboard/dist/assets/AgentDetailPage-BmhuWbKi.js +4 -0
- package/dashboard/dist/assets/AgentDetailPage-BmhuWbKi.js.map +1 -0
- package/dashboard/dist/assets/AgentsPage-C4SKQVeU.js +2 -0
- package/dashboard/dist/assets/AgentsPage-C4SKQVeU.js.map +1 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-9RBEYwkt.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-9RBEYwkt.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-Dse2jW4b.js → BotPicker-D-FvXJOM.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-Dse2jW4b.js.map → BotPicker-D-FvXJOM.js.map} +1 -1
- package/dashboard/dist/assets/CapabilitiesPage-ByDf6WJr.js +2 -0
- package/dashboard/dist/assets/{CapabilitiesPage-DTfKCzBQ.js.map → CapabilitiesPage-ByDf6WJr.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-8vf38J6K.js → CollapsibleSection-BjGNYEvh.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-8vf38J6K.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-7C4M-hrH.js → CredentialsPage-DvCA-wQN.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-7C4M-hrH.js.map → CredentialsPage-DvCA-wQN.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-DrGWtaSY.js → CronLabel-DBMurd_c.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-DrGWtaSY.js.map → CronLabel-DBMurd_c.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-Vcmr4LiR.js → CustomDurationPicker-DHflFoEa.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-Vcmr4LiR.js.map → CustomDurationPicker-DHflFoEa.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-Dwpgbueb.js → DropZone-CUtdp99Z.js} +2 -2
- package/dashboard/dist/assets/{DropZone-Dwpgbueb.js.map → DropZone-CUtdp99Z.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-BNexhR5d.js → ElapsedCell-CSjcVOpo.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-BNexhR5d.js.map → ElapsedCell-CSjcVOpo.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-BI3DEwYP.js → EscalationsOverview-BUqRNJbK.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-BI3DEwYP.js.map → EscalationsOverview-BUqRNJbK.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-CwvX_L9t.js → EventTable-Chz-6XPE.js} +2 -2
- package/dashboard/dist/assets/{EventTable-CwvX_L9t.js.map → EventTable-Chz-6XPE.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-C-3b8WK0.js → GraphInvokePage-BXdgciWg.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-C-3b8WK0.js.map → GraphInvokePage-BXdgciWg.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-BS_-jcHr.js → HomePage-CEMxnk0r.js} +2 -2
- package/dashboard/dist/assets/{HomePage-BS_-jcHr.js.map → HomePage-CEMxnk0r.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-BH9rwQVE.js → ListToolbar-_73JCdbB.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-BH9rwQVE.js.map → ListToolbar-_73JCdbB.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-C9iOdE7E.js → McpOverview-DfdqxhSz.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-C9iOdE7E.js.map → McpOverview-DfdqxhSz.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-DMN-yqvJ.js → McpQueryDetailPage-DPAljieY.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-DMN-yqvJ.js.map → McpQueryDetailPage-DPAljieY.js.map} +1 -1
- package/dashboard/dist/assets/McpQueryPage-D5cSziYS.js +2 -0
- package/dashboard/dist/assets/McpQueryPage-D5cSziYS.js.map +1 -0
- package/dashboard/dist/assets/{McpRunDetailPage-Wb47sRvo.js → McpRunDetailPage-DwNb9wZg.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-Wb47sRvo.js.map → McpRunDetailPage-DwNb9wZg.js.map} +1 -1
- package/dashboard/dist/assets/McpRunsPage-O5H6yStl.js +2 -0
- package/dashboard/dist/assets/McpRunsPage-O5H6yStl.js.map +1 -0
- package/dashboard/dist/assets/{NamespacePill-DnJl4Lre.js → NamespacePill-uW-xOedj.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-DnJl4Lre.js.map → NamespacePill-uW-xOedj.js.map} +1 -1
- package/dashboard/dist/assets/OperatorDashboard-DYw9Ekv5.js +2 -0
- package/dashboard/dist/assets/OperatorDashboard-DYw9Ekv5.js.map +1 -0
- package/dashboard/dist/assets/PageHeader-DVOZjBfL.js +2 -0
- package/dashboard/dist/assets/PageHeader-DVOZjBfL.js.map +1 -0
- package/dashboard/dist/assets/{PageHeaderWithStats-BuKc9MKt.js → PageHeaderWithStats-DtTpDHOd.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-BuKc9MKt.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-CvPx-h8y.js → ProcessDetailPage-B0Dokcx9.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-CvPx-h8y.js.map → ProcessDetailPage-B0Dokcx9.js.map} +1 -1
- package/dashboard/dist/assets/ProcessesListPage-Cc87WIjL.js +2 -0
- package/dashboard/dist/assets/ProcessesListPage-Cc87WIjL.js.map +1 -0
- 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-W5ZjOHpg.js → RolesPage-cp6PiF08.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-W5ZjOHpg.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-CvxGTNGT.js → RunAsSelector-2LWkjSnO.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-CvxGTNGT.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-BTtw3GjX.js +2 -0
- package/dashboard/dist/assets/{StreamMessageDetail-BF0tM0cd.js.map → StreamMessageDetail-BTtw3GjX.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-Bv-KHY9s.js → SwimlaneTimeline-DdXxP4fO.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-Bv-KHY9s.js.map → SwimlaneTimeline-DdXxP4fO.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-B00uqhjX.js → TagInput-x9XTuqMb.js} +2 -2
- package/dashboard/dist/assets/{TagInput-B00uqhjX.js.map → TagInput-x9XTuqMb.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-Bzb_fuLy.js → TaskDetailPage-B8veYG4X.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-Bzb_fuLy.js.map → TaskDetailPage-B8veYG4X.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-CUsofm0X.js → TaskQueuePill-DSEQZ-HT.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-CUsofm0X.js.map → TaskQueuePill-DSEQZ-HT.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-CBezWaZL.js → TasksListPage-CsN1gZj5.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-CBezWaZL.js.map → TasksListPage-CsN1gZj5.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-CKHfTzat.js → TimeAgo-n182EcLx.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-CKHfTzat.js.map → TimeAgo-n182EcLx.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-Dsje8GH-.js → TimestampCell-2dlcQAfX.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-Dsje8GH-.js.map → TimestampCell-2dlcQAfX.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-DjvedZSn.js → ToolPill-B_e4eT77.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-DjvedZSn.js.map → ToolPill-B_e4eT77.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-3OcG18e5.js → ToolTestPanel-Bhnncc6M.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-3OcG18e5.js.map → ToolTestPanel-Bhnncc6M.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-DCtv91hH.js → TopicDetailPage-29lsNsqI.js} +3 -3
- package/dashboard/dist/assets/{TopicDetailPage-DCtv91hH.js.map → TopicDetailPage-29lsNsqI.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-DVGSNc_E.js → TopicsPage-KJ7_LAvo.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-DVGSNc_E.js.map → TopicsPage-KJ7_LAvo.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-xHAgkk8M.js → UserName-CMoFi9zb.js} +2 -2
- package/dashboard/dist/assets/{UserName-xHAgkk8M.js.map → UserName-CMoFi9zb.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-CWNxsS8a.js → WorkflowExecutionPage-Cymfx-Bp.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-CWNxsS8a.js.map → WorkflowExecutionPage-Cymfx-Bp.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-F1oKUzmc.js → WorkflowPill-B66_AnaY.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-F1oKUzmc.js.map → WorkflowPill-B66_AnaY.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowsDashboard-CqLN8spd.js +2 -0
- package/dashboard/dist/assets/WorkflowsDashboard-CqLN8spd.js.map +1 -0
- package/dashboard/dist/assets/{WorkflowsOverview-e7jWSj_n.js → WorkflowsOverview-0Rc2WT6q.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-e7jWSj_n.js.map → WorkflowsOverview-0Rc2WT6q.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-CdAeam76.js → YamlWorkflowDetailPage-CARQ7_LN.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-CdAeam76.js.map → YamlWorkflowDetailPage-CARQ7_LN.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-S_gKs2Di.js → YamlWorkflowsPage-B-7sz1nf.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-S_gKs2Di.js.map → YamlWorkflowsPage-B-7sz1nf.js.map} +1 -1
- package/dashboard/dist/assets/{agents-JE4aL07D.js → agents-zM43d5Yf.js} +2 -2
- package/dashboard/dist/assets/{agents-JE4aL07D.js.map → agents-zM43d5Yf.js.map} +1 -1
- package/dashboard/dist/assets/api-path-u2Jf9QXS.js +2 -0
- package/dashboard/dist/assets/api-path-u2Jf9QXS.js.map +1 -0
- package/dashboard/dist/assets/{bots-cAzKkDtQ.js → bots-D5BasOGz.js} +2 -2
- package/dashboard/dist/assets/{bots-cAzKkDtQ.js.map → bots-D5BasOGz.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-RBFXLAXF.js → capabilities-BT3PW9CZ.js} +2 -2
- package/dashboard/dist/assets/{capabilities-RBFXLAXF.js.map → capabilities-BT3PW9CZ.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-DGhv3rwI.js → controlplane-NpJr49rN.js} +2 -2
- package/dashboard/dist/assets/{controlplane-DGhv3rwI.js.map → controlplane-NpJr49rN.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-ne7dIaPa.js → escalation-DrzQolPG.js} +2 -2
- package/dashboard/dist/assets/{escalation-ne7dIaPa.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-CByjpYlU.js → index-BCT4kqGR.js} +2 -2
- package/dashboard/dist/assets/{index-CByjpYlU.js.map → index-BCT4kqGR.js.map} +1 -1
- package/dashboard/dist/assets/{index-CgyucK0e.js → index-C69XvBfg.js} +2 -2
- package/dashboard/dist/assets/{index-CgyucK0e.js.map → index-C69XvBfg.js.map} +1 -1
- package/dashboard/dist/assets/index-CHw4Fl0t.css +1 -0
- package/dashboard/dist/assets/index-C_NwHTMh.js +2 -0
- package/dashboard/dist/assets/index-C_NwHTMh.js.map +1 -0
- package/dashboard/dist/assets/{index-DgzgRTNg.js → index-Cq0trGqf.js} +2 -2
- package/dashboard/dist/assets/{index-DgzgRTNg.js.map → index-Cq0trGqf.js.map} +1 -1
- package/dashboard/dist/assets/{index-DPJW96d2.js → index-DE0r9SEX.js} +26 -26
- package/dashboard/dist/assets/index-DE0r9SEX.js.map +1 -0
- package/dashboard/dist/assets/{index-BTqhAT6p.js → index-DH_CiD0W.js} +2 -2
- package/dashboard/dist/assets/{index-BTqhAT6p.js.map → index-DH_CiD0W.js.map} +1 -1
- package/dashboard/dist/assets/{index-C2l4GcZ7.js → index-K23FdS-T.js} +3 -3
- package/dashboard/dist/assets/{index-C2l4GcZ7.js.map → index-K23FdS-T.js.map} +1 -1
- package/dashboard/dist/assets/index-f6K1UNrn.js +2 -0
- package/dashboard/dist/assets/index-f6K1UNrn.js.map +1 -0
- package/dashboard/dist/assets/{index-BbJuC9kg.js → index-qr1fndqM.js} +2 -2
- package/dashboard/dist/assets/{index-BbJuC9kg.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-CQDn-bLr.js → index-y-3ODtEE.js} +2 -2
- package/dashboard/dist/assets/{index-CQDn-bLr.js.map → index-y-3ODtEE.js.map} +1 -1
- package/dashboard/dist/assets/{index-QxAYCJ1S.js → index-y56Ds-LB.js} +2 -2
- package/dashboard/dist/assets/{index-QxAYCJ1S.js.map → index-y56Ds-LB.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-DzRpsm3D.js → knowledge-DSSATOTX.js} +2 -2
- package/dashboard/dist/assets/{knowledge-DzRpsm3D.js.map → knowledge-DSSATOTX.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-B1lc6nL4.js → mcp-CfJFVo5k.js} +2 -2
- package/dashboard/dist/assets/{mcp-B1lc6nL4.js.map → mcp-CfJFVo5k.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-CXuweaN3.js → mcp-query-DKVx2Q-X.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-CXuweaN3.js.map → mcp-query-DKVx2Q-X.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-D4VLWDvu.js → pipelines-BSWzE-6M.js} +2 -2
- package/dashboard/dist/assets/{pipelines-D4VLWDvu.js.map → pipelines-BSWzE-6M.js.map} +1 -1
- package/dashboard/dist/assets/{roles-CSpHRRu8.js → roles-t9HzhI5N.js} +2 -2
- package/dashboard/dist/assets/{roles-CSpHRRu8.js.map → roles-t9HzhI5N.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-CWUsKhM4.js → tasks-B7rUihoH.js} +2 -2
- package/dashboard/dist/assets/{tasks-CWUsKhM4.js.map → tasks-B7rUihoH.js.map} +1 -1
- package/dashboard/dist/assets/{topics-BXwstrzB.js → topics-PC2fEiyD.js} +2 -2
- package/dashboard/dist/assets/{topics-BXwstrzB.js.map → topics-PC2fEiyD.js.map} +1 -1
- package/dashboard/dist/assets/useDebouncedValue-BA5S7V54.js +2 -0
- package/dashboard/dist/assets/useDebouncedValue-BA5S7V54.js.map +1 -0
- package/dashboard/dist/assets/{useEventHooks-CjygrbYy.js → useEventHooks-DK9mw1cI.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-CjygrbYy.js.map → useEventHooks-DK9mw1cI.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-CsepDyR5.js → useNamespace-3IjtUtAw.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-CsepDyR5.js.map → useNamespace-3IjtUtAw.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-gWIG6akB.js → useYamlActivityEvents-uxukp3gO.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-gWIG6akB.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-Dj2F0Jrb.js → vendor-icons-Dy5Jpknf.js} +114 -109
- package/dashboard/dist/assets/vendor-icons-Dy5Jpknf.js.map +1 -0
- package/dashboard/dist/assets/{workflows-C7Cam2Ok.js → workflows-S7MF1nNk.js} +2 -2
- package/dashboard/dist/assets/{workflows-C7Cam2Ok.js.map → workflows-S7MF1nNk.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-ChUOE22W.js → yaml-workflows-WfdaMHNn.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-ChUOE22W.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 +164 -14
- 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-DZpduYdW.js +0 -2
- package/dashboard/dist/assets/AgentDetailPage-C-2iSaDs.js +0 -4
- package/dashboard/dist/assets/AgentDetailPage-C-2iSaDs.js.map +0 -1
- package/dashboard/dist/assets/AgentsPage-C3FztFd_.js +0 -2
- package/dashboard/dist/assets/AgentsPage-C3FztFd_.js.map +0 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-DIqdBCwe.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-DIqdBCwe.js.map +0 -1
- package/dashboard/dist/assets/CapabilitiesPage-DTfKCzBQ.js +0 -2
- package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js +0 -2
- package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js.map +0 -1
- package/dashboard/dist/assets/McpQueryPage-Db1LUKVs.js +0 -2
- package/dashboard/dist/assets/McpQueryPage-Db1LUKVs.js.map +0 -1
- package/dashboard/dist/assets/McpRunsPage--tgT5uo7.js +0 -2
- package/dashboard/dist/assets/McpRunsPage--tgT5uo7.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-BWVUbJkZ.js +0 -2
- package/dashboard/dist/assets/OperatorDashboard-BWVUbJkZ.js.map +0 -1
- package/dashboard/dist/assets/PageHeader-Bnt2iQQa.js +0 -2
- package/dashboard/dist/assets/PageHeader-Bnt2iQQa.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/ProcessesListPage-CWiRM_h3.js +0 -2
- package/dashboard/dist/assets/ProcessesListPage-CWiRM_h3.js.map +0 -1
- package/dashboard/dist/assets/RolePill-DQTT1s9v.js +0 -2
- package/dashboard/dist/assets/RolePill-DQTT1s9v.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/StreamMessageDetail-BF0tM0cd.js +0 -2
- package/dashboard/dist/assets/WorkflowsDashboard-M13lIaw7.js +0 -2
- package/dashboard/dist/assets/WorkflowsDashboard-M13lIaw7.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-DqTkKbCv.js +0 -2
- package/dashboard/dist/assets/escalation-columns-DqTkKbCv.js.map +0 -1
- package/dashboard/dist/assets/index-CEpw_Fh8.js +0 -2
- package/dashboard/dist/assets/index-CEpw_Fh8.js.map +0 -1
- package/dashboard/dist/assets/index-ClvYSNZd.css +0 -1
- package/dashboard/dist/assets/index-CucXk58o.js +0 -6
- package/dashboard/dist/assets/index-CucXk58o.js.map +0 -1
- package/dashboard/dist/assets/index-DPJW96d2.js.map +0 -1
- package/dashboard/dist/assets/index-DeZhNCf5.js +0 -2
- package/dashboard/dist/assets/index-DeZhNCf5.js.map +0 -1
- package/dashboard/dist/assets/index-Do8Xp3pa.js +0 -2
- package/dashboard/dist/assets/index-Do8Xp3pa.js.map +0 -1
- package/dashboard/dist/assets/users-DvhHbR0R.js +0 -2
- package/dashboard/dist/assets/users-DvhHbR0R.js.map +0 -1
- package/dashboard/dist/assets/vendor-icons-Dj2F0Jrb.js.map +0 -1
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
// `hmsh_escalations` here rather than composed from generic SDK calls.
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.RESOLVE_BY_METADATA_ATOMIC = exports.RELEASE_EXPIRED_CLAIMS = void 0;
|
|
14
|
+
exports.RESOLVE_BY_METADATA_ATOMIC = exports.COUNT_SEARCH_ESCALATIONS = exports.RELEASE_EXPIRED_CLAIMS = void 0;
|
|
15
|
+
exports.searchEscalationsQuery = searchEscalationsQuery;
|
|
15
16
|
/**
|
|
16
17
|
* Sweep expired claims back to the available pool.
|
|
17
18
|
*
|
|
@@ -31,6 +32,72 @@ SET assigned_to = NULL,
|
|
|
31
32
|
WHERE status = 'pending'
|
|
32
33
|
AND assigned_to IS NOT NULL
|
|
33
34
|
AND assigned_until <= NOW()`;
|
|
35
|
+
/**
|
|
36
|
+
* Free-text escalation search.
|
|
37
|
+
*
|
|
38
|
+
* The SDK's `client.list()` filters by structured columns only — it has no
|
|
39
|
+
* free-text predicate. Long-tail's contract needs a search box that matches a
|
|
40
|
+
* correlation key (order id, ticket id) or any visible field across the WHOLE
|
|
41
|
+
* result set, not just the current page. That is server-side SQL here (over the
|
|
42
|
+
* `lt_escalations` view, which adds the computed `available` flag), following the
|
|
43
|
+
* same raw-SQL-on-the-shared-table pattern as the atomic resolve below — so the
|
|
44
|
+
* filter flows SQL→service→API→route→dashboard rather than being faked client-side.
|
|
45
|
+
*
|
|
46
|
+
* All structured filters are optional and combine with the search term (AND).
|
|
47
|
+
* `metadata::text ILIKE` scans the JSONB as text (the GIN index is containment-only
|
|
48
|
+
* and cannot serve ILIKE) — bounded in practice by the other filters and the page.
|
|
49
|
+
*
|
|
50
|
+
* Role-scope visibility ($3 allRoles, $10 selfRoles, $11 meUserId):
|
|
51
|
+
* - global access → $3 NULL and $10 NULL → no role filter (sees everything)
|
|
52
|
+
* - read_all roles → row.role ∈ $3
|
|
53
|
+
* - read_self roles → row.role ∈ $10 AND row.assigned_to = $11 (only the user's own)
|
|
54
|
+
* The branches union, so a user with mixed scope (read_all on some roles, read_self
|
|
55
|
+
* on others) is filtered correctly in one pass. `assigned_to` is indexed
|
|
56
|
+
* (idx_lt_escalations_assigned), so the self-branch is index-served at scale.
|
|
57
|
+
*
|
|
58
|
+
* $1 status, $2 role, $3 allRoles (text[]), $4 type, $5 subtype, $6 priority,
|
|
59
|
+
* $7 assigned_to, $8 available (bool), $9 search, $10 selfRoles (text[]),
|
|
60
|
+
* $11 meUserId, $12 metadata (jsonb containment, GIN-served). The list query
|
|
61
|
+
* adds $13 limit, $14 offset.
|
|
62
|
+
*
|
|
63
|
+
* `metadata @> $12` is exact JSONB containment (the GIN index serves it) — this is
|
|
64
|
+
* how findByMetadata routes through the scoped query so its role-scope filter and
|
|
65
|
+
* count run in SQL (no client-side filtering), the same as free-text/self-scope.
|
|
66
|
+
*/
|
|
67
|
+
const SEARCH_ESCALATIONS_WHERE = `\
|
|
68
|
+
FROM public.lt_escalations
|
|
69
|
+
WHERE ($1::text IS NULL OR status = $1)
|
|
70
|
+
AND ($2::text IS NULL OR role = $2)
|
|
71
|
+
AND (
|
|
72
|
+
($3::text[] IS NULL AND $10::text[] IS NULL)
|
|
73
|
+
OR ($3::text[] IS NOT NULL AND role = ANY($3))
|
|
74
|
+
OR ($10::text[] IS NOT NULL AND role = ANY($10) AND assigned_to = $11)
|
|
75
|
+
)
|
|
76
|
+
AND ($4::text IS NULL OR type = $4)
|
|
77
|
+
AND ($5::text IS NULL OR subtype = $5)
|
|
78
|
+
AND ($6::int IS NULL OR priority = $6)
|
|
79
|
+
AND ($7::text IS NULL OR assigned_to = $7)
|
|
80
|
+
AND ($8::boolean IS NULL OR available = $8)
|
|
81
|
+
AND ($12::jsonb IS NULL OR metadata @> $12)
|
|
82
|
+
AND ($9::text IS NULL OR (
|
|
83
|
+
description ILIKE '%' || $9 || '%'
|
|
84
|
+
OR type ILIKE '%' || $9 || '%'
|
|
85
|
+
OR subtype ILIKE '%' || $9 || '%'
|
|
86
|
+
OR role ILIKE '%' || $9 || '%'
|
|
87
|
+
OR workflow_id ILIKE '%' || $9 || '%'
|
|
88
|
+
OR origin_id ILIKE '%' || $9 || '%'
|
|
89
|
+
OR id::text ILIKE '%' || $9 || '%'
|
|
90
|
+
OR metadata::text ILIKE '%' || $9 || '%'
|
|
91
|
+
))`;
|
|
92
|
+
/** Count matching the search WHERE (params $1–$12). */
|
|
93
|
+
exports.COUNT_SEARCH_ESCALATIONS = `SELECT COUNT(*)::int AS total\n${SEARCH_ESCALATIONS_WHERE}`;
|
|
94
|
+
/**
|
|
95
|
+
* Build the search SELECT. `orderBy` is composed from a whitelist by the caller
|
|
96
|
+
* (never raw user input), so it is safe to interpolate; everything else is bound.
|
|
97
|
+
*/
|
|
98
|
+
function searchEscalationsQuery(orderBy) {
|
|
99
|
+
return `SELECT *\n${SEARCH_ESCALATIONS_WHERE}\n ORDER BY ${orderBy}\n LIMIT $13 OFFSET $14`;
|
|
100
|
+
}
|
|
34
101
|
/**
|
|
35
102
|
* Atomic resolve by metadata with signal guard.
|
|
36
103
|
*
|
|
@@ -51,7 +118,13 @@ WHERE status = 'pending'
|
|
|
51
118
|
* The `resolved` CTE still skips signal_id rows — the workflow resolves durably.
|
|
52
119
|
*
|
|
53
120
|
* $1 = metadata filter (jsonb), $2 = userId, $3 = resolver_payload (jsonb),
|
|
54
|
-
* $4 = metadata patch (jsonb, nullable), $5 =
|
|
121
|
+
* $4 = metadata patch (jsonb, nullable), $5 = write_all roles (text[], null = global /
|
|
122
|
+
* no filter), $6 = write_self roles (text[], nullable).
|
|
123
|
+
*
|
|
124
|
+
* Write-scope is folded into the same FOR UPDATE statement (no TOCTOU): the row
|
|
125
|
+
* is resolvable if the caller has global access ($5 NULL), or the row's role is in
|
|
126
|
+
* their write_all set, or the row's role is in their write_self set AND it is
|
|
127
|
+
* already assigned to them ($2). assigned_to is indexed, so the self-branch scales.
|
|
55
128
|
*/
|
|
56
129
|
exports.RESOLVE_BY_METADATA_ATOMIC = `\
|
|
57
130
|
WITH target AS MATERIALIZED (
|
|
@@ -59,7 +132,11 @@ WITH target AS MATERIALIZED (
|
|
|
59
132
|
FROM public.hmsh_escalations
|
|
60
133
|
WHERE metadata @> $1::jsonb
|
|
61
134
|
AND status = 'pending'
|
|
62
|
-
AND (
|
|
135
|
+
AND (
|
|
136
|
+
$5::text[] IS NULL
|
|
137
|
+
OR role = ANY($5)
|
|
138
|
+
OR (role = ANY($6::text[]) AND assigned_to = $2)
|
|
139
|
+
)
|
|
63
140
|
ORDER BY priority ASC, created_at ASC
|
|
64
141
|
LIMIT 1
|
|
65
142
|
FOR UPDATE
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
export declare function ltResolveEscalation(input: {
|
|
9
9
|
escalationId: string;
|
|
10
10
|
resolverPayload: Record<string, any>;
|
|
11
|
+
/** Outcome patch merged into the row inside the same atomic resolve (see resolveEscalation). */
|
|
12
|
+
metadata?: Record<string, any>;
|
|
11
13
|
}): Promise<void>;
|
|
12
14
|
/**
|
|
13
15
|
* Claim an escalation to a specific user. Used for auto-assignment
|
|
@@ -47,7 +47,7 @@ const logger_1 = require("../../../lib/logger");
|
|
|
47
47
|
* retries this activity.
|
|
48
48
|
*/
|
|
49
49
|
async function ltResolveEscalation(input) {
|
|
50
|
-
const result = await escalationService.resolveEscalation(input.escalationId, input.resolverPayload);
|
|
50
|
+
const result = await escalationService.resolveEscalation(input.escalationId, input.resolverPayload, input.metadata);
|
|
51
51
|
if (!result) {
|
|
52
52
|
logger_1.loggerRegistry.warn(`[ltResolveEscalation] Escalation ${input.escalationId} already resolved or not found`);
|
|
53
53
|
}
|
|
@@ -53,9 +53,13 @@ async function resolveReRun(activities, envelope, existingTask, wf, taskQueue) {
|
|
|
53
53
|
const escalationId = envelope?.lt?.escalationId;
|
|
54
54
|
const isRealEscalation = isReRun && escalationId && !escalationId.startsWith('auto-triage-');
|
|
55
55
|
if (isRealEscalation) {
|
|
56
|
+
// Thread the outcome patch carried on the re-run envelope so it merges into the
|
|
57
|
+
// prior escalation's metadata inside this SAME atomic resolve — never a separate
|
|
58
|
+
// write. (resolve orchestrator Path E sets envelope.lt.escalationMetadata.)
|
|
56
59
|
await activities.ltResolveEscalation({
|
|
57
60
|
escalationId: escalationId,
|
|
58
61
|
resolverPayload: envelope.resolver,
|
|
62
|
+
metadata: envelope.lt?.escalationMetadata,
|
|
59
63
|
});
|
|
60
64
|
// escalation.resolved event published by service layer (services/escalation/crud.ts)
|
|
61
65
|
}
|
|
@@ -54,14 +54,20 @@ const getAvailableWorkSchema = zod_1.z.object({
|
|
|
54
54
|
role: zod_1.z.string().describe('Role to filter by'),
|
|
55
55
|
limit: zod_1.z.number().optional().default(10).describe('Max results to return'),
|
|
56
56
|
});
|
|
57
|
+
/** Outcome facets merged onto the row on resolve — see the field describe(). */
|
|
58
|
+
const outcomeMetadata = zod_1.z.record(zod_1.z.any()).optional().describe('Outcome facets to merge into the escalation\'s metadata on resolve — the durable, ' +
|
|
59
|
+
'queryable record of what happened (disposition, reviewer, timing). Distinct from ' +
|
|
60
|
+
'payload, which resumes the waiting workflow and is not indexed.');
|
|
57
61
|
const claimAndResolveSchema = zod_1.z.object({
|
|
58
62
|
escalation_id: zod_1.z.string().describe('The escalation ID to claim and resolve'),
|
|
59
63
|
resolver_id: zod_1.z.string().describe('Identifier for who/what is resolving'),
|
|
60
64
|
payload: zod_1.z.record(zod_1.z.any()).describe('Resolution payload data'),
|
|
65
|
+
metadata: outcomeMetadata,
|
|
61
66
|
});
|
|
62
67
|
const resolveEscalationSchema = zod_1.z.object({
|
|
63
68
|
escalation_id: zod_1.z.string().describe('The escalation ID to resolve'),
|
|
64
69
|
payload: zod_1.z.record(zod_1.z.any()).describe('Resolution payload data'),
|
|
70
|
+
metadata: outcomeMetadata,
|
|
65
71
|
});
|
|
66
72
|
const escalateAndWaitSchema = zod_1.z.object({
|
|
67
73
|
role: zod_1.z.string().describe('Target role for the escalation (e.g., "reviewer")'),
|
|
@@ -176,7 +182,7 @@ function registerHumanQueueTools(server) {
|
|
|
176
182
|
// ── claim_and_resolve ───────────────────────────────────────────────
|
|
177
183
|
server.registerTool('claim_and_resolve', {
|
|
178
184
|
title: 'Claim and Resolve',
|
|
179
|
-
description: 'Claim an escalation and immediately resolve it with a payload. Atomic operation.',
|
|
185
|
+
description: 'Claim an escalation and immediately resolve it with a payload. Atomic operation. Optionally pass `metadata` to record the outcome (disposition, timing) onto the row for the audit trail.',
|
|
180
186
|
inputSchema: claimAndResolveSchema,
|
|
181
187
|
}, async (args) => {
|
|
182
188
|
const claimed = await escalationService.claimEscalation(args.escalation_id, args.resolver_id, 5);
|
|
@@ -189,7 +195,7 @@ function registerHumanQueueTools(server) {
|
|
|
189
195
|
isError: true,
|
|
190
196
|
};
|
|
191
197
|
}
|
|
192
|
-
const resolved = await escalationService.resolveEscalation(args.escalation_id, args.payload);
|
|
198
|
+
const resolved = await escalationService.resolveEscalation(args.escalation_id, args.payload, args.metadata);
|
|
193
199
|
if (!resolved) {
|
|
194
200
|
return {
|
|
195
201
|
content: [{
|
|
@@ -213,10 +219,10 @@ function registerHumanQueueTools(server) {
|
|
|
213
219
|
// ── resolve_escalation ──────────────────────────────────────────────
|
|
214
220
|
server.registerTool('resolve_escalation', {
|
|
215
221
|
title: 'Resolve Escalation',
|
|
216
|
-
description: 'Resolve an already-claimed escalation with a payload. Use when the claim happened externally (e.g. via API).',
|
|
222
|
+
description: 'Resolve an already-claimed escalation with a payload. Use when the claim happened externally (e.g. via API). Optionally pass `metadata` to record the outcome (disposition, timing) onto the row for the audit trail.',
|
|
217
223
|
inputSchema: resolveEscalationSchema,
|
|
218
224
|
}, async (args) => {
|
|
219
|
-
const resolved = await escalationService.resolveEscalation(args.escalation_id, args.payload);
|
|
225
|
+
const resolved = await escalationService.resolveEscalation(args.escalation_id, args.payload, args.metadata);
|
|
220
226
|
if (!resolved) {
|
|
221
227
|
return {
|
|
222
228
|
content: [{
|
|
@@ -87,11 +87,15 @@ async function conditionLT(signalId, escalation) {
|
|
|
87
87
|
taskQueue: LT_ACTIVITY_QUEUE,
|
|
88
88
|
retry: { maximumAttempts: 3 },
|
|
89
89
|
});
|
|
90
|
-
// Strip $escalation_id before
|
|
91
|
-
|
|
90
|
+
// Strip the injected control keys ($escalation_id, $escalation_metadata) before the
|
|
91
|
+
// payload is returned to the caller. The outcome patch ($escalation_metadata, set by
|
|
92
|
+
// the resolve orchestrator's signal paths) rides INTO the single atomic resolve below
|
|
93
|
+
// so it merges in the same guarded UPDATE — never a separate write.
|
|
94
|
+
const { $escalation_id: _id, $escalation_metadata: metadata, ...resolverPayload } = raw;
|
|
92
95
|
await ltResolveEscalation({
|
|
93
96
|
escalationId,
|
|
94
97
|
resolverPayload: resolverPayload,
|
|
98
|
+
metadata: metadata,
|
|
95
99
|
});
|
|
96
100
|
return resolverPayload;
|
|
97
101
|
}
|
|
@@ -34,9 +34,8 @@ async function getAllEscalationChains() {
|
|
|
34
34
|
*/
|
|
35
35
|
async function addEscalationChain(sourceRole, targetRole) {
|
|
36
36
|
const pool = (0, db_1.getPool)();
|
|
37
|
-
|
|
38
|
-
await pool.query(sql_1.
|
|
39
|
-
await pool.query(sql_1.INSERT_ESCALATION_CHAIN, [sourceRole, targetRole]);
|
|
37
|
+
// One atomic statement: ensure both role FK targets + insert the chain link.
|
|
38
|
+
await pool.query(sql_1.ADD_ESCALATION_CHAIN, [sourceRole, targetRole]);
|
|
40
39
|
}
|
|
41
40
|
/**
|
|
42
41
|
* Remove a single escalation chain entry.
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export declare const GET_ESCALATION_TARGETS = "\n SELECT target_role\n FROM lt_config_role_escalations\n WHERE source_role = $1\n ORDER BY target_role";
|
|
2
2
|
export declare const GET_ALL_ESCALATION_CHAINS = "\n SELECT source_role, target_role\n FROM lt_config_role_escalations\n ORDER BY source_role, target_role";
|
|
3
3
|
export declare const INSERT_ESCALATION_CHAIN = "\n INSERT INTO lt_config_role_escalations (source_role, target_role)\n VALUES ($1, $2)\n ON CONFLICT DO NOTHING";
|
|
4
|
+
/**
|
|
5
|
+
* Add an escalation chain in ONE atomic statement: ensure both role FK targets
|
|
6
|
+
* exist, then insert the source→target link. Postgres checks the chain's FK to
|
|
7
|
+
* lt_roles at statement end (after the sibling ensure CTEs run), so brand-new
|
|
8
|
+
* roles are valid targets. Replaces the former ensure+ensure+insert three-call
|
|
9
|
+
* saga, which had no transaction guarding the writes together.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ADD_ESCALATION_CHAIN = "\n WITH ensured_source AS (\n INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING\n ), ensured_target AS (\n INSERT INTO lt_roles (role) VALUES ($2) ON CONFLICT DO NOTHING\n )\n INSERT INTO lt_config_role_escalations (source_role, target_role)\n VALUES ($1, $2)\n ON CONFLICT DO NOTHING";
|
|
4
12
|
export declare const DELETE_ESCALATION_CHAIN = "\n DELETE FROM lt_config_role_escalations\n WHERE source_role = $1 AND target_role = $2";
|
|
5
13
|
export declare const DELETE_ESCALATION_CHAINS_BY_SOURCE = "\n DELETE FROM lt_config_role_escalations\n WHERE source_role = $1";
|
|
6
14
|
export declare const CHECK_ESCALATION_CHAIN_EXISTS = "\n SELECT 1\n FROM lt_config_role_escalations\n WHERE source_role = $1 AND target_role = $2";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// ─── Escalation chain queries ───────────────────────────────────────────────
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.COUNT_ACTIVE_ESCALATION_REFS = exports.COUNT_WORKFLOW_REFS = exports.COUNT_CHAIN_REFS = exports.COUNT_USER_ROLE_REFS = exports.LIST_ROLES_WITH_DETAILS = exports.DELETE_ROLE = exports.LIST_ROLES = exports.ENSURE_ROLE_EXISTS = exports.CHECK_ESCALATION_CHAIN_EXISTS = exports.DELETE_ESCALATION_CHAINS_BY_SOURCE = exports.DELETE_ESCALATION_CHAIN = exports.INSERT_ESCALATION_CHAIN = exports.GET_ALL_ESCALATION_CHAINS = exports.GET_ESCALATION_TARGETS = void 0;
|
|
4
|
+
exports.COUNT_ACTIVE_ESCALATION_REFS = exports.COUNT_WORKFLOW_REFS = exports.COUNT_CHAIN_REFS = exports.COUNT_USER_ROLE_REFS = exports.LIST_ROLES_WITH_DETAILS = exports.DELETE_ROLE = exports.LIST_ROLES = exports.ENSURE_ROLE_EXISTS = exports.CHECK_ESCALATION_CHAIN_EXISTS = exports.DELETE_ESCALATION_CHAINS_BY_SOURCE = exports.DELETE_ESCALATION_CHAIN = exports.ADD_ESCALATION_CHAIN = exports.INSERT_ESCALATION_CHAIN = exports.GET_ALL_ESCALATION_CHAINS = exports.GET_ESCALATION_TARGETS = void 0;
|
|
5
5
|
exports.GET_ESCALATION_TARGETS = `
|
|
6
6
|
SELECT target_role
|
|
7
7
|
FROM lt_config_role_escalations
|
|
@@ -15,6 +15,22 @@ exports.INSERT_ESCALATION_CHAIN = `
|
|
|
15
15
|
INSERT INTO lt_config_role_escalations (source_role, target_role)
|
|
16
16
|
VALUES ($1, $2)
|
|
17
17
|
ON CONFLICT DO NOTHING`;
|
|
18
|
+
/**
|
|
19
|
+
* Add an escalation chain in ONE atomic statement: ensure both role FK targets
|
|
20
|
+
* exist, then insert the source→target link. Postgres checks the chain's FK to
|
|
21
|
+
* lt_roles at statement end (after the sibling ensure CTEs run), so brand-new
|
|
22
|
+
* roles are valid targets. Replaces the former ensure+ensure+insert three-call
|
|
23
|
+
* saga, which had no transaction guarding the writes together.
|
|
24
|
+
*/
|
|
25
|
+
exports.ADD_ESCALATION_CHAIN = `
|
|
26
|
+
WITH ensured_source AS (
|
|
27
|
+
INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING
|
|
28
|
+
), ensured_target AS (
|
|
29
|
+
INSERT INTO lt_roles (role) VALUES ($2) ON CONFLICT DO NOTHING
|
|
30
|
+
)
|
|
31
|
+
INSERT INTO lt_config_role_escalations (source_role, target_role)
|
|
32
|
+
VALUES ($1, $2)
|
|
33
|
+
ON CONFLICT DO NOTHING`;
|
|
18
34
|
exports.DELETE_ESCALATION_CHAIN = `
|
|
19
35
|
DELETE FROM lt_config_role_escalations
|
|
20
36
|
WHERE source_role = $1 AND target_role = $2`;
|
|
@@ -31,17 +31,25 @@ async function createTask(input) {
|
|
|
31
31
|
input.executing_as || null,
|
|
32
32
|
input.status || null,
|
|
33
33
|
]);
|
|
34
|
-
|
|
35
|
-
(0
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
// ON CONFLICT (workflow_id) makes this idempotent: a retried proxyActivity or
|
|
35
|
+
// re-driven saga re-targets the same row. `_inserted` (xmax = 0) is true only
|
|
36
|
+
// when this call actually inserted, so the created event fires exactly once.
|
|
37
|
+
const row = rows[0];
|
|
38
|
+
const inserted = row._inserted === true;
|
|
39
|
+
delete row._inserted;
|
|
40
|
+
const task = row;
|
|
41
|
+
if (inserted) {
|
|
42
|
+
(0, publish_1.publishTaskEvent)({
|
|
43
|
+
type: 'task.created',
|
|
44
|
+
source: 'service',
|
|
45
|
+
workflowId: task.workflow_id || '',
|
|
46
|
+
workflowName: task.workflow_type || '',
|
|
47
|
+
taskQueue: task.task_queue || '',
|
|
48
|
+
taskId: task.id,
|
|
49
|
+
originId: task.origin_id || undefined,
|
|
50
|
+
status: task.status || 'pending',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
45
53
|
return task;
|
|
46
54
|
}
|
|
47
55
|
async function updateTask(id, input) {
|
|
@@ -70,7 +78,7 @@ async function updateTask(id, input) {
|
|
|
70
78
|
values.push(JSON.stringify(input.milestones));
|
|
71
79
|
}
|
|
72
80
|
values.push(id);
|
|
73
|
-
const { rows } = await pool.query(
|
|
81
|
+
const { rows } = await pool.query((0, sql_1.updateTaskById)(sets.join(', '), idx), values);
|
|
74
82
|
const task = rows[0];
|
|
75
83
|
// Publish status-driven events from the single write path
|
|
76
84
|
if (input.status && task) {
|
|
@@ -148,8 +156,8 @@ async function listTasks(filters) {
|
|
|
148
156
|
const limit = filters.limit || 50;
|
|
149
157
|
const offset = filters.offset || 0;
|
|
150
158
|
const [countResult, dataResult] = await Promise.all([
|
|
151
|
-
pool.query(
|
|
152
|
-
pool.query(
|
|
159
|
+
pool.query((0, sql_1.listTasksCount)(where), values),
|
|
160
|
+
pool.query((0, sql_1.listTasksData)(where, idx++, idx++), [...values, limit, offset]),
|
|
153
161
|
]);
|
|
154
162
|
return {
|
|
155
163
|
tasks: dataResult.rows,
|
|
@@ -44,27 +44,9 @@ async function listProcesses(filters) {
|
|
|
44
44
|
having = `HAVING COUNT(*) > COUNT(*) FILTER (WHERE status = 'completed')
|
|
45
45
|
AND COUNT(*) FILTER (WHERE status = 'needs_intervention') = 0`;
|
|
46
46
|
}
|
|
47
|
-
// Count
|
|
48
|
-
const countSql = having
|
|
49
|
-
|
|
50
|
-
SELECT origin_id FROM lt_tasks ${where}
|
|
51
|
-
GROUP BY origin_id ${having}
|
|
52
|
-
) sub`
|
|
53
|
-
: `SELECT COUNT(DISTINCT origin_id) FROM lt_tasks ${where}`;
|
|
54
|
-
const dataSql = `SELECT
|
|
55
|
-
origin_id,
|
|
56
|
-
COUNT(*)::int AS task_count,
|
|
57
|
-
COUNT(*) FILTER (WHERE status = 'completed')::int AS completed,
|
|
58
|
-
COUNT(*) FILTER (WHERE status = 'needs_intervention')::int AS escalated,
|
|
59
|
-
array_agg(DISTINCT workflow_type) AS workflow_types,
|
|
60
|
-
MIN(created_at) AS started_at,
|
|
61
|
-
MAX(COALESCE(completed_at, created_at)) AS last_activity
|
|
62
|
-
FROM lt_tasks
|
|
63
|
-
${where}
|
|
64
|
-
GROUP BY origin_id
|
|
65
|
-
${having}
|
|
66
|
-
ORDER BY MAX(created_at) DESC
|
|
67
|
-
LIMIT $${idx++} OFFSET $${idx++}`;
|
|
47
|
+
// Count wraps the grouped set in a subquery when a status HAVING filter is present.
|
|
48
|
+
const countSql = (0, sql_1.listProcessesCount)(where, having);
|
|
49
|
+
const dataSql = (0, sql_1.listProcessesData)(where, having, idx++, idx++);
|
|
68
50
|
const dataParams = [...filterParams, limit, offset];
|
|
69
51
|
const [countResult, dataResult] = await Promise.all([
|
|
70
52
|
pool.query(countSql, filterParams),
|
|
@@ -4,9 +4,11 @@ import type { ResolvedHandle } from './types';
|
|
|
4
4
|
* HotMesh needs to get a workflow handle.
|
|
5
5
|
*
|
|
6
6
|
* 1. Look up lt_tasks by workflow_id — returns workflow_type and task_queue.
|
|
7
|
-
* 2. If no task record (e.g., orchestrators/containers
|
|
8
|
-
*
|
|
7
|
+
* 2. If no task record (e.g., orchestrators/containers, or a child running in
|
|
8
|
+
* another app), fall back to `<appId>.jobs` (entity) + lt_config_workflows
|
|
9
|
+
* (task_queue). `appId` selects the HotMesh namespace to scan — children of
|
|
10
|
+
* a workflow live under their own app's schema, not always `durable`.
|
|
9
11
|
* 3. If task_queue is null (pre-migration record), fall back to lt_config_workflows.
|
|
10
12
|
* 4. Throws if the workflow cannot be resolved.
|
|
11
13
|
*/
|
|
12
|
-
export declare function resolveWorkflowHandle(workflowId: string): Promise<ResolvedHandle>;
|
|
14
|
+
export declare function resolveWorkflowHandle(workflowId: string, appId?: string): Promise<ResolvedHandle>;
|
|
@@ -9,12 +9,14 @@ const sql_1 = require("./sql");
|
|
|
9
9
|
* HotMesh needs to get a workflow handle.
|
|
10
10
|
*
|
|
11
11
|
* 1. Look up lt_tasks by workflow_id — returns workflow_type and task_queue.
|
|
12
|
-
* 2. If no task record (e.g., orchestrators/containers
|
|
13
|
-
*
|
|
12
|
+
* 2. If no task record (e.g., orchestrators/containers, or a child running in
|
|
13
|
+
* another app), fall back to `<appId>.jobs` (entity) + lt_config_workflows
|
|
14
|
+
* (task_queue). `appId` selects the HotMesh namespace to scan — children of
|
|
15
|
+
* a workflow live under their own app's schema, not always `durable`.
|
|
14
16
|
* 3. If task_queue is null (pre-migration record), fall back to lt_config_workflows.
|
|
15
17
|
* 4. Throws if the workflow cannot be resolved.
|
|
16
18
|
*/
|
|
17
|
-
async function resolveWorkflowHandle(workflowId) {
|
|
19
|
+
async function resolveWorkflowHandle(workflowId, appId = 'durable') {
|
|
18
20
|
const pool = (0, db_1.getPool)();
|
|
19
21
|
// 1. Try lt_tasks first (leaf workflows)
|
|
20
22
|
const { rows } = await pool.query(sql_1.RESOLVE_TASK_BY_WORKFLOW_ID, [workflowId]);
|
|
@@ -29,9 +31,10 @@ async function resolveWorkflowHandle(workflowId) {
|
|
|
29
31
|
return { taskQueue: configRows[0].task_queue, workflowName: workflow_type };
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
|
-
// 2. Fall back to
|
|
33
|
-
// have no lt_tasks record but do have a job with an entity tag
|
|
34
|
-
|
|
34
|
+
// 2. Fall back to <appId>.jobs — handles orchestrators/containers that
|
|
35
|
+
// have no lt_tasks record but do have a job with an entity tag, and
|
|
36
|
+
// children running in a non-default namespace.
|
|
37
|
+
const { rows: jobRows } = await pool.query((0, sql_1.RESOLVE_JOB_ENTITY)(appId), [`hmsh:${appId}:j:${workflowId}`]);
|
|
35
38
|
if (jobRows.length > 0 && jobRows[0].entity) {
|
|
36
39
|
const entity = jobRows[0].entity;
|
|
37
40
|
// 2a. Try lt_config_workflows for task_queue
|
|
@@ -1,12 +1,40 @@
|
|
|
1
|
-
export declare const CREATE_TASK = "\n INSERT INTO lt_tasks\n (workflow_id, workflow_type, lt_type, task_queue, signal_id,\n parent_workflow_id, origin_id, parent_id, envelope, metadata, priority,\n trace_id, span_id, initiated_by, principal_type, executing_as, status)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, COALESCE($17, 'pending'))\n RETURNING
|
|
1
|
+
export declare const CREATE_TASK = "\n INSERT INTO lt_tasks\n (workflow_id, workflow_type, lt_type, task_queue, signal_id,\n parent_workflow_id, origin_id, parent_id, envelope, metadata, priority,\n trace_id, span_id, initiated_by, principal_type, executing_as, status)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, COALESCE($17, 'pending'))\n ON CONFLICT (workflow_id) DO UPDATE SET workflow_id = lt_tasks.workflow_id\n RETURNING *, (xmax = 0) AS _inserted";
|
|
2
2
|
export declare const APPEND_MILESTONES = "\n UPDATE lt_tasks\n SET milestones = milestones || $1::jsonb\n WHERE id = $2\n RETURNING *";
|
|
3
3
|
export declare const GET_TASK = "SELECT * FROM lt_tasks WHERE id = $1";
|
|
4
4
|
export declare const GET_TASK_BY_SIGNAL_ID = "SELECT * FROM lt_tasks WHERE signal_id = $1";
|
|
5
5
|
export declare const GET_TASK_BY_WORKFLOW_ID = "SELECT * FROM lt_tasks WHERE workflow_id = $1 ORDER BY created_at DESC LIMIT 1";
|
|
6
|
+
/**
|
|
7
|
+
* Build the partial-update statement for updateTask. The caller assembles the
|
|
8
|
+
* `SET` clause from whichever fields are present (each as a `$n` placeholder) and
|
|
9
|
+
* passes the placeholder index of the trailing `id` bind. One guarded UPDATE by
|
|
10
|
+
* primary key — no read-then-write.
|
|
11
|
+
*/
|
|
12
|
+
export declare function updateTaskById(setClause: string, idIdx: number): string;
|
|
13
|
+
/** Count query for listTasks. `where` is the caller-built `WHERE …` clause (or ''). */
|
|
14
|
+
export declare function listTasksCount(where: string): string;
|
|
15
|
+
/** Page query for listTasks. limit/offset placeholder indices follow the filter binds. */
|
|
16
|
+
export declare function listTasksData(where: string, limitIdx: number, offsetIdx: number): string;
|
|
6
17
|
export declare const RESOLVE_TASK_BY_WORKFLOW_ID = "SELECT workflow_type, task_queue FROM lt_tasks WHERE workflow_id = $1 ORDER BY created_at DESC LIMIT 1";
|
|
7
18
|
export declare const RESOLVE_CONFIG_TASK_QUEUE = "SELECT task_queue FROM lt_config_workflows WHERE workflow_type = $1";
|
|
8
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Look up a job's entity tag in a specific HotMesh namespace. The namespace is a
|
|
21
|
+
* Postgres schema name, so it is interpolated (not a bind param) — same pattern
|
|
22
|
+
* as the diagnostics queries. Callers pass the trusted app_id from config.
|
|
23
|
+
*/
|
|
24
|
+
export declare const RESOLVE_JOB_ENTITY: (appId: string) => string;
|
|
9
25
|
export declare const GET_PROCESS_TASKS = "SELECT * FROM lt_tasks WHERE origin_id = $1 ORDER BY created_at ASC";
|
|
26
|
+
/**
|
|
27
|
+
* Count query for listProcesses. `where` restricts which origin_ids are in scope;
|
|
28
|
+
* `having` (empty string when no status filter) aggregates per-origin counts, so the
|
|
29
|
+
* count must wrap the grouped set in a subquery. Both fragments carry only `$n` binds.
|
|
30
|
+
*/
|
|
31
|
+
export declare function listProcessesCount(where: string, having: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Page query for listProcesses — one row per origin_id with rolled-up task counts,
|
|
34
|
+
* workflow types, and activity window. limit/offset placeholder indices follow the
|
|
35
|
+
* filter binds.
|
|
36
|
+
*/
|
|
37
|
+
export declare function listProcessesData(where: string, having: string, limitIdx: number, offsetIdx: number): string;
|
|
10
38
|
/**
|
|
11
39
|
* Build the process-stats totals CTE query for a given interval.
|
|
12
40
|
* The interval value MUST come from the VALID_PERIODS whitelist.
|
|
@@ -4,16 +4,29 @@
|
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.GET_PROCESS_TASKS = exports.RESOLVE_JOB_ENTITY = exports.RESOLVE_CONFIG_TASK_QUEUE = exports.RESOLVE_TASK_BY_WORKFLOW_ID = exports.GET_TASK_BY_WORKFLOW_ID = exports.GET_TASK_BY_SIGNAL_ID = exports.GET_TASK = exports.APPEND_MILESTONES = exports.CREATE_TASK = void 0;
|
|
7
|
+
exports.updateTaskById = updateTaskById;
|
|
8
|
+
exports.listTasksCount = listTasksCount;
|
|
9
|
+
exports.listTasksData = listTasksData;
|
|
10
|
+
exports.listProcessesCount = listProcessesCount;
|
|
11
|
+
exports.listProcessesData = listProcessesData;
|
|
7
12
|
exports.processStatsTotals = processStatsTotals;
|
|
8
13
|
exports.processStatsByType = processStatsByType;
|
|
9
14
|
// ---- crud.ts queries ------------------------------------------------------
|
|
15
|
+
// A task is 1:1 with its workflow_id. ON CONFLICT (workflow_id) makes createTask
|
|
16
|
+
// idempotent under proxyActivity retry (maximumAttempts: 3) and re-driven sagas:
|
|
17
|
+
// a retry re-targets the SAME row instead of inserting a duplicate. The no-op
|
|
18
|
+
// DO UPDATE is required so RETURNING yields the existing row on conflict; the
|
|
19
|
+
// `(xmax = 0)` flag tells the caller whether this call actually inserted (so the
|
|
20
|
+
// `task.created` event fires exactly once). Requires the unique index from
|
|
21
|
+
// migration 012.
|
|
10
22
|
exports.CREATE_TASK = `
|
|
11
23
|
INSERT INTO lt_tasks
|
|
12
24
|
(workflow_id, workflow_type, lt_type, task_queue, signal_id,
|
|
13
25
|
parent_workflow_id, origin_id, parent_id, envelope, metadata, priority,
|
|
14
26
|
trace_id, span_id, initiated_by, principal_type, executing_as, status)
|
|
15
27
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, COALESCE($17, 'pending'))
|
|
16
|
-
|
|
28
|
+
ON CONFLICT (workflow_id) DO UPDATE SET workflow_id = lt_tasks.workflow_id
|
|
29
|
+
RETURNING *, (xmax = 0) AS _inserted`;
|
|
17
30
|
exports.APPEND_MILESTONES = `
|
|
18
31
|
UPDATE lt_tasks
|
|
19
32
|
SET milestones = milestones || $1::jsonb
|
|
@@ -22,12 +35,69 @@ exports.APPEND_MILESTONES = `
|
|
|
22
35
|
exports.GET_TASK = `SELECT * FROM lt_tasks WHERE id = $1`;
|
|
23
36
|
exports.GET_TASK_BY_SIGNAL_ID = `SELECT * FROM lt_tasks WHERE signal_id = $1`;
|
|
24
37
|
exports.GET_TASK_BY_WORKFLOW_ID = `SELECT * FROM lt_tasks WHERE workflow_id = $1 ORDER BY created_at DESC LIMIT 1`;
|
|
38
|
+
/**
|
|
39
|
+
* Build the partial-update statement for updateTask. The caller assembles the
|
|
40
|
+
* `SET` clause from whichever fields are present (each as a `$n` placeholder) and
|
|
41
|
+
* passes the placeholder index of the trailing `id` bind. One guarded UPDATE by
|
|
42
|
+
* primary key — no read-then-write.
|
|
43
|
+
*/
|
|
44
|
+
function updateTaskById(setClause, idIdx) {
|
|
45
|
+
return `UPDATE lt_tasks SET ${setClause} WHERE id = $${idIdx} RETURNING *`;
|
|
46
|
+
}
|
|
47
|
+
/** Count query for listTasks. `where` is the caller-built `WHERE …` clause (or ''). */
|
|
48
|
+
function listTasksCount(where) {
|
|
49
|
+
return `SELECT COUNT(*) FROM lt_tasks ${where}`;
|
|
50
|
+
}
|
|
51
|
+
/** Page query for listTasks. limit/offset placeholder indices follow the filter binds. */
|
|
52
|
+
function listTasksData(where, limitIdx, offsetIdx) {
|
|
53
|
+
return `SELECT * FROM lt_tasks ${where} ORDER BY created_at DESC LIMIT $${limitIdx} OFFSET $${offsetIdx}`;
|
|
54
|
+
}
|
|
25
55
|
// ---- resolve.ts queries ---------------------------------------------------
|
|
26
56
|
exports.RESOLVE_TASK_BY_WORKFLOW_ID = `SELECT workflow_type, task_queue FROM lt_tasks WHERE workflow_id = $1 ORDER BY created_at DESC LIMIT 1`;
|
|
27
57
|
exports.RESOLVE_CONFIG_TASK_QUEUE = `SELECT task_queue FROM lt_config_workflows WHERE workflow_type = $1`;
|
|
28
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Look up a job's entity tag in a specific HotMesh namespace. The namespace is a
|
|
60
|
+
* Postgres schema name, so it is interpolated (not a bind param) — same pattern
|
|
61
|
+
* as the diagnostics queries. Callers pass the trusted app_id from config.
|
|
62
|
+
*/
|
|
63
|
+
const RESOLVE_JOB_ENTITY = (appId) => `SELECT entity FROM "${appId}".jobs WHERE key = $1 LIMIT 1`;
|
|
64
|
+
exports.RESOLVE_JOB_ENTITY = RESOLVE_JOB_ENTITY;
|
|
29
65
|
// ---- process.ts queries ---------------------------------------------------
|
|
30
66
|
exports.GET_PROCESS_TASKS = `SELECT * FROM lt_tasks WHERE origin_id = $1 ORDER BY created_at ASC`;
|
|
67
|
+
/**
|
|
68
|
+
* Count query for listProcesses. `where` restricts which origin_ids are in scope;
|
|
69
|
+
* `having` (empty string when no status filter) aggregates per-origin counts, so the
|
|
70
|
+
* count must wrap the grouped set in a subquery. Both fragments carry only `$n` binds.
|
|
71
|
+
*/
|
|
72
|
+
function listProcessesCount(where, having) {
|
|
73
|
+
return having
|
|
74
|
+
? `SELECT COUNT(*) FROM (
|
|
75
|
+
SELECT origin_id FROM lt_tasks ${where}
|
|
76
|
+
GROUP BY origin_id ${having}
|
|
77
|
+
) sub`
|
|
78
|
+
: `SELECT COUNT(DISTINCT origin_id) FROM lt_tasks ${where}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Page query for listProcesses — one row per origin_id with rolled-up task counts,
|
|
82
|
+
* workflow types, and activity window. limit/offset placeholder indices follow the
|
|
83
|
+
* filter binds.
|
|
84
|
+
*/
|
|
85
|
+
function listProcessesData(where, having, limitIdx, offsetIdx) {
|
|
86
|
+
return `SELECT
|
|
87
|
+
origin_id,
|
|
88
|
+
COUNT(*)::int AS task_count,
|
|
89
|
+
COUNT(*) FILTER (WHERE status = 'completed')::int AS completed,
|
|
90
|
+
COUNT(*) FILTER (WHERE status = 'needs_intervention')::int AS escalated,
|
|
91
|
+
array_agg(DISTINCT workflow_type) AS workflow_types,
|
|
92
|
+
MIN(created_at) AS started_at,
|
|
93
|
+
MAX(COALESCE(completed_at, created_at)) AS last_activity
|
|
94
|
+
FROM lt_tasks
|
|
95
|
+
${where}
|
|
96
|
+
GROUP BY origin_id
|
|
97
|
+
${having}
|
|
98
|
+
ORDER BY MAX(created_at) DESC
|
|
99
|
+
LIMIT $${limitIdx} OFFSET $${offsetIdx}`;
|
|
100
|
+
}
|
|
31
101
|
/**
|
|
32
102
|
* Build the process-stats totals CTE query for a given interval.
|
|
33
103
|
* The interval value MUST come from the VALID_PERIODS whitelist.
|