@hotmeshio/long-tail 0.5.6 → 0.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/api/diagnostics.d.ts +15 -0
- package/build/api/diagnostics.js +33 -0
- package/build/api/escalations/cancel.d.ts +20 -0
- package/build/api/escalations/cancel.js +93 -0
- package/build/api/escalations/index.d.ts +1 -0
- package/build/api/escalations/index.js +4 -1
- package/build/api/escalations/resolve.js +2 -0
- package/build/api/workflows/invocation.js +6 -0
- package/build/lib/events/publish.d.ts +1 -1
- package/build/routes/diagnostics.d.ts +2 -0
- package/build/routes/diagnostics.js +88 -0
- package/build/routes/escalations/bulk.js +4 -0
- package/build/routes/escalations/single.js +9 -0
- package/build/routes/index.js +2 -0
- package/build/services/diagnostics/index.d.ts +52 -0
- package/build/services/diagnostics/index.js +169 -0
- package/build/services/diagnostics/patterns.d.ts +19 -0
- package/build/services/diagnostics/patterns.js +147 -0
- package/build/services/escalation/bulk.d.ts +9 -0
- package/build/services/escalation/bulk.js +21 -0
- package/build/services/escalation/crud.d.ts +17 -0
- package/build/services/escalation/crud.js +57 -0
- package/build/services/orchestrator/condition.d.ts +1 -1
- package/build/services/orchestrator/condition.js +3 -0
- package/build/system/mcp-servers/admin/diagnostics.d.ts +10 -0
- package/build/system/mcp-servers/admin/diagnostics.js +94 -0
- package/build/system/mcp-servers/admin/index.js +4 -2
- package/build/system/mcp-servers/admin/schemas.d.ts +36 -0
- package/build/system/mcp-servers/admin/schemas.js +16 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/dashboard/dist/assets/AdminDashboard-0LSslIQ7.js +2 -0
- package/dashboard/dist/assets/{AdminDashboard-DIQEViVY.js.map → AdminDashboard-0LSslIQ7.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-Caf5-th-.js → AgentConfigPage-hqBr1540.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-Caf5-th-.js.map → AgentConfigPage-hqBr1540.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-_a4tKaM3.js → AgentDetailPage-bnXR1ezD.js} +2 -2
- package/dashboard/dist/assets/{AgentDetailPage-_a4tKaM3.js.map → AgentDetailPage-bnXR1ezD.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-BNlAkEIK.js → AgentsPage-tJIlrnfM.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-BNlAkEIK.js.map → AgentsPage-tJIlrnfM.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-B7gEfcw3.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-B7gEfcw3.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-DBvd1yFL.js → BotPicker-fFE4GAaO.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-DBvd1yFL.js.map → BotPicker-fFE4GAaO.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-BIdG9RTG.js → CapabilitiesPage-DotZ7iQc.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-BIdG9RTG.js.map → CapabilitiesPage-DotZ7iQc.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-CggyKhyZ.js → CollapsibleSection-D97SYypF.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-CggyKhyZ.js.map → CollapsibleSection-D97SYypF.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-B4sXGLRY.js → CredentialsPage-C08U6dlv.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-B4sXGLRY.js.map → CredentialsPage-C08U6dlv.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-GcS5qzZr.js → CronLabel-C2TuCl4h.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-GcS5qzZr.js.map → CronLabel-C2TuCl4h.js.map} +1 -1
- package/dashboard/dist/assets/CustomDurationPicker-C3zGsESx.js +2 -0
- package/dashboard/dist/assets/CustomDurationPicker-C3zGsESx.js.map +1 -0
- package/dashboard/dist/assets/{ElapsedCell-BFOMsgwF.js → ElapsedCell-DF4m0cv_.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-BFOMsgwF.js.map → ElapsedCell-DF4m0cv_.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-BfjW6haL.js → EscalationsOverview-blGOYKtJ.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-BfjW6haL.js.map → EscalationsOverview-blGOYKtJ.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-N0XmLk3C.js → EventTable-DzrMH-ux.js} +2 -2
- package/dashboard/dist/assets/{EventTable-N0XmLk3C.js.map → EventTable-DzrMH-ux.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-DHiGHiCk.js → GraphInvokePage-QVAUHmjI.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-DHiGHiCk.js.map → GraphInvokePage-QVAUHmjI.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-jckQolbx.js → HomePage-DvYgvCcO.js} +2 -2
- package/dashboard/dist/assets/{HomePage-jckQolbx.js.map → HomePage-DvYgvCcO.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-B71DqaXr.js → ListToolbar-D6tQ3ZG2.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-B71DqaXr.js.map → ListToolbar-D6tQ3ZG2.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-CPD6NRGt.js → McpOverview-BClbGYb8.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-CPD6NRGt.js.map → McpOverview-BClbGYb8.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-YyqXw6Dt.js → McpQueryDetailPage-DHjfDRdm.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-YyqXw6Dt.js.map → McpQueryDetailPage-DHjfDRdm.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-BWf45EYD.js → McpQueryPage-qtEuMDrd.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-BWf45EYD.js.map → McpQueryPage-qtEuMDrd.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-Cv43tkjz.js → McpRunDetailPage-DRbIZMU2.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-Cv43tkjz.js.map → McpRunDetailPage-DRbIZMU2.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-BFards4K.js → McpRunsPage-CMfwXQ4R.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-BFards4K.js.map → McpRunsPage-CMfwXQ4R.js.map} +1 -1
- package/dashboard/dist/assets/{OperatorDashboard-BJZxuOKI.js → OperatorDashboard-Dq2pfV68.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-BJZxuOKI.js.map → OperatorDashboard-Dq2pfV68.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-BjkLfyrm.js → ProcessDetailPage-DjQuNERz.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-BjkLfyrm.js.map → ProcessDetailPage-DjQuNERz.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-DOzeduP3.js → ProcessesListPage-vJimii8-.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-DOzeduP3.js.map → ProcessesListPage-vJimii8-.js.map} +1 -1
- package/dashboard/dist/assets/{RolesPage-DSSQrGTh.js → RolesPage-CFCN_mRF.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-DSSQrGTh.js.map → RolesPage-CFCN_mRF.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-CM7lk1YB.js → RunAsSelector-sBxAvlRf.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-CM7lk1YB.js.map → RunAsSelector-sBxAvlRf.js.map} +1 -1
- package/dashboard/dist/assets/{StatusBadge-XQlNFwmH.js → StatusBadge-Bl3FXVmF.js} +2 -2
- package/dashboard/dist/assets/{StatusBadge-XQlNFwmH.js.map → StatusBadge-Bl3FXVmF.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-DtCgJNpV.js → SwimlaneTimeline-2Eet4OOL.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-DtCgJNpV.js.map → SwimlaneTimeline-2Eet4OOL.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-BC9xnyLT.js → TaskDetailPage-CgFN5uWn.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-BC9xnyLT.js.map → TaskDetailPage-CgFN5uWn.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-DtbLfUS-.js → TasksListPage-qj25rRZL.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-DtbLfUS-.js.map → TasksListPage-qj25rRZL.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-xrDlFnPy.js → TimeAgo-C7LylVjP.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-xrDlFnPy.js.map → TimeAgo-C7LylVjP.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-BneATvjR.js → TimestampCell-CrCtoBlo.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-BneATvjR.js.map → TimestampCell-CrCtoBlo.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-1lQ_GQod.js → ToolTestPanel-DYGlB-QW.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-1lQ_GQod.js.map → ToolTestPanel-DYGlB-QW.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-CDtlXyut.js → TopicDetailPage-DLY6X_R6.js} +2 -2
- package/dashboard/dist/assets/{TopicDetailPage-CDtlXyut.js.map → TopicDetailPage-DLY6X_R6.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-COX2kDKK.js → TopicsPage-C5nOLsLx.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-COX2kDKK.js.map → TopicsPage-C5nOLsLx.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-Cb2WvhCN.js → UserName-CgUZyiwm.js} +2 -2
- package/dashboard/dist/assets/{UserName-Cb2WvhCN.js.map → UserName-CgUZyiwm.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-BWCTOTXF.js → WorkflowExecutionPage-CDr5CTM2.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-BWCTOTXF.js.map → WorkflowExecutionPage-CDr5CTM2.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-DOaDSC98.js → WorkflowsDashboard-tCBP0i0Y.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-DOaDSC98.js.map → WorkflowsDashboard-tCBP0i0Y.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-CeE7a51J.js → WorkflowsOverview-CaMeZwde.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-CeE7a51J.js.map → WorkflowsOverview-CaMeZwde.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-cNpR0PHg.js → YamlWorkflowDetailPage-C3uyCiIX.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-cNpR0PHg.js.map → YamlWorkflowDetailPage-C3uyCiIX.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-DWvNfzji.js → YamlWorkflowsPage-DRnYgsvA.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-DWvNfzji.js.map → YamlWorkflowsPage-DRnYgsvA.js.map} +1 -1
- package/dashboard/dist/assets/{agents-BvTiBKBQ.js → agents-HMkTc425.js} +2 -2
- package/dashboard/dist/assets/{agents-BvTiBKBQ.js.map → agents-HMkTc425.js.map} +1 -1
- package/dashboard/dist/assets/{bots-CeMR1Lnp.js → bots-CDe2jXNx.js} +2 -2
- package/dashboard/dist/assets/{bots-CeMR1Lnp.js.map → bots-CDe2jXNx.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-D7uJlnxj.js → capabilities-DhnM7Ztb.js} +2 -2
- package/dashboard/dist/assets/{capabilities-D7uJlnxj.js.map → capabilities-DhnM7Ztb.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-Cf6lZsSF.js → controlplane-BwIJ9ZC_.js} +2 -2
- package/dashboard/dist/assets/{controlplane-Cf6lZsSF.js.map → controlplane-BwIJ9ZC_.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-BCFBWGP-.js → escalation-Djk5XKmX.js} +2 -2
- package/dashboard/dist/assets/{escalation-BCFBWGP-.js.map → escalation-Djk5XKmX.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-Do7k4fte.js → escalation-columns-BKqSywVA.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-Do7k4fte.js.map → escalation-columns-BKqSywVA.js.map} +1 -1
- package/dashboard/dist/assets/{index-eWNx4UQ8.js → index-BFQ2t-p1.js} +22 -22
- package/dashboard/dist/assets/index-BFQ2t-p1.js.map +1 -0
- package/dashboard/dist/assets/{index-D3SjB3UM.js → index-BNkpns_E.js} +2 -2
- package/dashboard/dist/assets/{index-D3SjB3UM.js.map → index-BNkpns_E.js.map} +1 -1
- package/dashboard/dist/assets/{index-BS5pcmBR.js → index-BW-fIquS.js} +2 -2
- package/dashboard/dist/assets/{index-BS5pcmBR.js.map → index-BW-fIquS.js.map} +1 -1
- package/dashboard/dist/assets/{index-C51Jzvoz.js → index-BYpWOUEe.js} +2 -2
- package/dashboard/dist/assets/{index-C51Jzvoz.js.map → index-BYpWOUEe.js.map} +1 -1
- package/dashboard/dist/assets/{index-BDhEpETz.js → index-CPIYW81T.js} +2 -2
- package/dashboard/dist/assets/{index-BDhEpETz.js.map → index-CPIYW81T.js.map} +1 -1
- package/dashboard/dist/assets/{index-B216fPN_.js → index-Cydgj_Co.js} +2 -2
- package/dashboard/dist/assets/{index-B216fPN_.js.map → index-Cydgj_Co.js.map} +1 -1
- package/dashboard/dist/assets/{index-C1p_rvkY.js → index-DBi9Q9Hw.js} +2 -2
- package/dashboard/dist/assets/{index-C1p_rvkY.js.map → index-DBi9Q9Hw.js.map} +1 -1
- package/dashboard/dist/assets/{index-8C9YLM_u.js → index-DRIJM1L-.js} +2 -2
- package/dashboard/dist/assets/{index-8C9YLM_u.js.map → index-DRIJM1L-.js.map} +1 -1
- package/dashboard/dist/assets/index-DSki_Ub6.css +1 -0
- package/dashboard/dist/assets/{index-gKm3HAgO.js → index-Dhy2PVAw.js} +2 -2
- package/dashboard/dist/assets/{index-gKm3HAgO.js.map → index-Dhy2PVAw.js.map} +1 -1
- package/dashboard/dist/assets/index-E2hQ8DIi.js +6 -0
- package/dashboard/dist/assets/index-E2hQ8DIi.js.map +1 -0
- package/dashboard/dist/assets/{index-D0fjUKvx.js → index-mEQ6kCnt.js} +2 -2
- package/dashboard/dist/assets/{index-D0fjUKvx.js.map → index-mEQ6kCnt.js.map} +1 -1
- package/dashboard/dist/assets/{index-oulO44Iy.js → index-rUST2YsO.js} +2 -2
- package/dashboard/dist/assets/{index-oulO44Iy.js.map → index-rUST2YsO.js.map} +1 -1
- package/dashboard/dist/assets/{index-DSjbScPX.js → index-u08vQUaw.js} +2 -2
- package/dashboard/dist/assets/{index-DSjbScPX.js.map → index-u08vQUaw.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-Bje8_ndK.js → knowledge-jQE3dBVi.js} +2 -2
- package/dashboard/dist/assets/{knowledge-Bje8_ndK.js.map → knowledge-jQE3dBVi.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-Bpl6p11J.js → mcp-DtEGfZiz.js} +2 -2
- package/dashboard/dist/assets/{mcp-Bpl6p11J.js.map → mcp-DtEGfZiz.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-DMXj-t5f.js → mcp-query-CiTLVuDC.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-DMXj-t5f.js.map → mcp-query-CiTLVuDC.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-CFh9nBxn.js → pipelines-ClNdzuUK.js} +2 -2
- package/dashboard/dist/assets/{pipelines-CFh9nBxn.js.map → pipelines-ClNdzuUK.js.map} +1 -1
- package/dashboard/dist/assets/{roles-CtjQQWSQ.js → roles-BmHbRaPT.js} +2 -2
- package/dashboard/dist/assets/{roles-CtjQQWSQ.js.map → roles-BmHbRaPT.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-Nz9lVCT1.js → tasks-BPONxLcq.js} +2 -2
- package/dashboard/dist/assets/{tasks-Nz9lVCT1.js.map → tasks-BPONxLcq.js.map} +1 -1
- package/dashboard/dist/assets/{topics-CiG2N8Z_.js → topics-XREPRxwo.js} +2 -2
- package/dashboard/dist/assets/{topics-CiG2N8Z_.js.map → topics-XREPRxwo.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-Bimqt7hU.js → useEventHooks-M7SaEfel.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-Bimqt7hU.js.map → useEventHooks-M7SaEfel.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-FrG0Monz.js → useNamespace-BQHGkdBv.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-FrG0Monz.js.map → useNamespace-BQHGkdBv.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-JmD4dH5n.js → useYamlActivityEvents-B7JtUGSE.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-JmD4dH5n.js.map → useYamlActivityEvents-B7JtUGSE.js.map} +1 -1
- package/dashboard/dist/assets/{users-DgyonYq6.js → users-BpjSNhQe.js} +2 -2
- package/dashboard/dist/assets/{users-DgyonYq6.js.map → users-BpjSNhQe.js.map} +1 -1
- package/dashboard/dist/assets/{workflows-BOwGINtF.js → workflows-k4jE0ECC.js} +2 -2
- package/dashboard/dist/assets/{workflows-BOwGINtF.js.map → workflows-k4jE0ECC.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-I1tcjCFK.js → yaml-workflows-DpviSLEL.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-I1tcjCFK.js.map → yaml-workflows-DpviSLEL.js.map} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/docs/api/http/escalations.md +89 -5
- package/docs/api/sdk/escalations.md +60 -1
- package/docs/hitl-guide.md +65 -1
- package/package.json +2 -2
- package/dashboard/dist/assets/AdminDashboard-DIQEViVY.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-Cqy5ECm4.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-Cqy5ECm4.js.map +0 -1
- package/dashboard/dist/assets/CustomDurationPicker-CUYE36-K.js +0 -2
- package/dashboard/dist/assets/CustomDurationPicker-CUYE36-K.js.map +0 -1
- package/dashboard/dist/assets/index-BCfzac3h.css +0 -1
- package/dashboard/dist/assets/index-BUu3WKvk.js +0 -6
- package/dashboard/dist/assets/index-BUu3WKvk.js.map +0 -1
- package/dashboard/dist/assets/index-eWNx4UQ8.js.map +0 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchPatterns = matchPatterns;
|
|
4
|
+
/**
|
|
5
|
+
* Match event stream + stream messages against known failure patterns.
|
|
6
|
+
* Returns findings ordered by severity (critical first).
|
|
7
|
+
*/
|
|
8
|
+
function matchPatterns(execution, workerMessages, engineMessages, escalationRow) {
|
|
9
|
+
const findings = [];
|
|
10
|
+
const events = execution.events ?? [];
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
// ── Derive open signals ──────────────────────────────────────────────────
|
|
13
|
+
const signalCompletions = new Set(events
|
|
14
|
+
.filter(e => e.event_type === 'workflow_execution_signaled')
|
|
15
|
+
.map(e => e.attributes.timeline_key)
|
|
16
|
+
.filter(Boolean));
|
|
17
|
+
const openSignals = events.filter(e => e.event_type === 'signal_wait_started' &&
|
|
18
|
+
!signalCompletions.has(e.attributes.timeline_key));
|
|
19
|
+
// ── Dead-lettered messages ────────────────────────────────────────────────
|
|
20
|
+
const deadWorker = workerMessages.filter(m => m.dead_lettered_at);
|
|
21
|
+
const deadEngine = engineMessages.filter(m => m.dead_lettered_at);
|
|
22
|
+
// ── Reservation leaks: claimed but not ACK'd for > 30s ───────────────────
|
|
23
|
+
const leaks = workerMessages.filter(m => m.reserved_at && !m.expired_at && !m.dead_lettered_at &&
|
|
24
|
+
now - new Date(m.reserved_at).getTime() > 30_000);
|
|
25
|
+
// ── Pattern: orphaned_signal ──────────────────────────────────────────────
|
|
26
|
+
if (openSignals.length > 0 && !escalationRow) {
|
|
27
|
+
const workerMsg = workerMessages.find(m => m.expired_at && m.aid?.endsWith('/worker'));
|
|
28
|
+
const payload = safeParseJson(workerMsg?.message);
|
|
29
|
+
const queueConfig = payload?.data?.queueConfig;
|
|
30
|
+
const signalId = payload?.data?.signalId;
|
|
31
|
+
findings.push({
|
|
32
|
+
condition: 'orphaned_signal',
|
|
33
|
+
confidence: 0.98,
|
|
34
|
+
severity: 'critical',
|
|
35
|
+
evidence: [
|
|
36
|
+
`${openSignals.length} open signal(s) with no matching resolution`,
|
|
37
|
+
`No escalation row in hmsh_escalations for this workflow`,
|
|
38
|
+
queueConfig == null
|
|
39
|
+
? `Worker result missing queueConfig — escalation INSERT was skipped (pre-0.22 SDK)`
|
|
40
|
+
: `Worker result has queueConfig but escalation INSERT did not complete`,
|
|
41
|
+
...(signalId ? [`Signal key: ${signalId}`] : []),
|
|
42
|
+
],
|
|
43
|
+
treatment: [
|
|
44
|
+
{ action: 'create_escalation_row', note: 'INSERT missing row into public.hmsh_escalations' },
|
|
45
|
+
...(signalId ? [{ action: 'resolve_by_signal_key', signal_key: signalId, note: 'POST /api/escalations/resolve-by-signal-key after inserting escalation row' }] : []),
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// ── Pattern: normal_wait ──────────────────────────────────────────────────
|
|
50
|
+
// Two paths reach here:
|
|
51
|
+
// 1. Open signal in event stream (signal_wait_started) + pending escalation
|
|
52
|
+
// 2. No terminal event + pending escalation (conditionLT path — no signal_wait_started in export)
|
|
53
|
+
const isRunning = !events.some(e => e.event_type === 'workflow_execution_completed' || e.event_type === 'workflow_execution_failed');
|
|
54
|
+
const isNormalWait = (openSignals.length > 0 && escalationRow?.status === 'pending') ||
|
|
55
|
+
(isRunning && events.length > 0 && escalationRow?.status === 'pending');
|
|
56
|
+
if (isNormalWait) {
|
|
57
|
+
findings.push({
|
|
58
|
+
condition: 'normal_wait',
|
|
59
|
+
confidence: 0.99,
|
|
60
|
+
severity: 'info',
|
|
61
|
+
evidence: [
|
|
62
|
+
`Workflow suspended — escalation exists (status: pending)`,
|
|
63
|
+
...(escalationRow.role ? [`Role: ${escalationRow.role}, Type: ${escalationRow.type}`] : []),
|
|
64
|
+
...(escalationRow.id ? [`Escalation id: ${escalationRow.id}`] : []),
|
|
65
|
+
],
|
|
66
|
+
treatment: [{ action: 'none', note: 'Waiting for human to claim and resolve escalation' }],
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// ── Pattern: dead_lettered_activity ──────────────────────────────────────
|
|
70
|
+
if (deadWorker.length > 0 || deadEngine.length > 0) {
|
|
71
|
+
const samples = [...deadWorker, ...deadEngine].slice(0, 3);
|
|
72
|
+
findings.push({
|
|
73
|
+
condition: 'dead_lettered_activity',
|
|
74
|
+
confidence: 0.99,
|
|
75
|
+
severity: 'critical',
|
|
76
|
+
evidence: [
|
|
77
|
+
`${deadWorker.length} worker message(s) dead-lettered, ${deadEngine.length} engine message(s) dead-lettered`,
|
|
78
|
+
...samples.map(m => `aid: ${m.aid ?? 'engine'}, retries: ${m.retry_attempt}/${m.max_retry_attempts}, stream: ${m.stream_name}`),
|
|
79
|
+
],
|
|
80
|
+
treatment: [{
|
|
81
|
+
action: 'investigate_dead_letter',
|
|
82
|
+
count: deadWorker.length + deadEngine.length,
|
|
83
|
+
note: 'Message exhausted retries — check worker health and DB connectivity',
|
|
84
|
+
}],
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// ── Pattern: reservation_leak ─────────────────────────────────────────────
|
|
88
|
+
if (leaks.length > 0) {
|
|
89
|
+
findings.push({
|
|
90
|
+
condition: 'reservation_leak',
|
|
91
|
+
confidence: 0.85,
|
|
92
|
+
severity: 'warning',
|
|
93
|
+
evidence: leaks.map(m => `aid: ${m.aid}, claimed by: ${m.reserved_by ?? 'unknown'}, open for ${Math.round((now - new Date(m.reserved_at).getTime()) / 1000)}s`),
|
|
94
|
+
treatment: [{ action: 'check_worker_health', note: 'Worker claimed message but never ACKd — check for crashed workers' }],
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// ── Pattern: terminal_failure ─────────────────────────────────────────────
|
|
98
|
+
const lastEvent = events.at(-1);
|
|
99
|
+
if (lastEvent?.event_type === 'workflow_execution_failed') {
|
|
100
|
+
const attrs = lastEvent.attributes;
|
|
101
|
+
findings.push({
|
|
102
|
+
condition: 'terminal_failure',
|
|
103
|
+
confidence: 0.99,
|
|
104
|
+
severity: 'critical',
|
|
105
|
+
evidence: [
|
|
106
|
+
`Workflow terminated with failure`,
|
|
107
|
+
...(attrs?.error ? [`Error: ${attrs.error}`] : []),
|
|
108
|
+
],
|
|
109
|
+
treatment: [{ action: 'none', note: 'Workflow terminated — review error and restart if needed' }],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// ── Pattern: never_started ────────────────────────────────────────────────
|
|
113
|
+
const nonStartEvents = events.filter(e => e.event_type !== 'workflow_execution_started');
|
|
114
|
+
if (events.length > 0 && nonStartEvents.length === 0) {
|
|
115
|
+
findings.push({
|
|
116
|
+
condition: 'never_started',
|
|
117
|
+
confidence: 0.90,
|
|
118
|
+
severity: 'critical',
|
|
119
|
+
evidence: [`Workflow created but no activities have run — engine may not be consuming this stream`],
|
|
120
|
+
treatment: [{ action: 'check_engine_health', note: 'Check engine health and stream backlog for this workflow type' }],
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// ── Default: healthy ──────────────────────────────────────────────────────
|
|
124
|
+
if (findings.length === 0) {
|
|
125
|
+
const isCompleted = events.some(e => e.event_type === 'workflow_execution_completed' || e.event_type === 'workflow_execution_failed');
|
|
126
|
+
findings.push({
|
|
127
|
+
condition: isCompleted ? 'completed' : 'running',
|
|
128
|
+
confidence: 0.95,
|
|
129
|
+
severity: 'info',
|
|
130
|
+
evidence: [isCompleted ? 'Workflow completed normally' : 'Workflow is running — no anomalies detected'],
|
|
131
|
+
treatment: [{ action: 'none', note: 'No intervention required' }],
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// Critical first
|
|
135
|
+
const order = { critical: 0, warning: 1, info: 2 };
|
|
136
|
+
return findings.sort((a, b) => order[a.severity] - order[b.severity]);
|
|
137
|
+
}
|
|
138
|
+
function safeParseJson(raw) {
|
|
139
|
+
if (!raw)
|
|
140
|
+
return null;
|
|
141
|
+
try {
|
|
142
|
+
return JSON.parse(raw);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -20,6 +20,15 @@ export declare function bulkAssignEscalations(ids: string[], targetUserId: strin
|
|
|
20
20
|
* Clears assignment on all affected rows.
|
|
21
21
|
*/
|
|
22
22
|
export declare function bulkEscalateToRole(ids: string[], targetRole: string): Promise<number>;
|
|
23
|
+
/**
|
|
24
|
+
* Bulk cancel escalations. Each row is cancelled individually; rows already in
|
|
25
|
+
* a terminal state are silently skipped by the SDK. Returns the count
|
|
26
|
+
* successfully cancelled.
|
|
27
|
+
*/
|
|
28
|
+
export declare function bulkCancelEscalations(ids: string[]): Promise<{
|
|
29
|
+
cancelled: number;
|
|
30
|
+
skipped: number;
|
|
31
|
+
}>;
|
|
23
32
|
/**
|
|
24
33
|
* Bulk resolve escalations for AI triage.
|
|
25
34
|
* Returns full records so the caller can start triage workflows. No signal is
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.bulkClaimEscalations = bulkClaimEscalations;
|
|
4
4
|
exports.bulkAssignEscalations = bulkAssignEscalations;
|
|
5
5
|
exports.bulkEscalateToRole = bulkEscalateToRole;
|
|
6
|
+
exports.bulkCancelEscalations = bulkCancelEscalations;
|
|
6
7
|
exports.bulkResolveForTriage = bulkResolveForTriage;
|
|
7
8
|
const client_1 = require("./client");
|
|
8
9
|
const map_1 = require("./map");
|
|
@@ -41,6 +42,26 @@ async function bulkEscalateToRole(ids, targetRole) {
|
|
|
41
42
|
const client = await (0, client_1.escalations)();
|
|
42
43
|
return client.escalateManyToRole({ ids, targetRole });
|
|
43
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Bulk cancel escalations. Each row is cancelled individually; rows already in
|
|
47
|
+
* a terminal state are silently skipped by the SDK. Returns the count
|
|
48
|
+
* successfully cancelled.
|
|
49
|
+
*/
|
|
50
|
+
async function bulkCancelEscalations(ids) {
|
|
51
|
+
if (ids.length === 0)
|
|
52
|
+
return { cancelled: 0, skipped: 0 };
|
|
53
|
+
const client = await (0, client_1.escalations)();
|
|
54
|
+
let cancelled = 0;
|
|
55
|
+
let skipped = 0;
|
|
56
|
+
await Promise.all(ids.map(async (id) => {
|
|
57
|
+
const result = await client.cancel(id);
|
|
58
|
+
if (result.ok)
|
|
59
|
+
cancelled++;
|
|
60
|
+
else
|
|
61
|
+
skipped++;
|
|
62
|
+
}));
|
|
63
|
+
return { cancelled, skipped };
|
|
64
|
+
}
|
|
44
65
|
/**
|
|
45
66
|
* Bulk resolve escalations for AI triage.
|
|
46
67
|
* Returns full records so the caller can start triage workflows. No signal is
|
|
@@ -32,6 +32,13 @@ export declare function getEscalationBySignalKey(signalKey: string): Promise<LTE
|
|
|
32
32
|
* pre-check (that would be a TOCTOU window) — the atomic resolve is the arbiter.
|
|
33
33
|
*/
|
|
34
34
|
export declare function resolveEscalationBySignalKey(signalKey: string, resolverPayload: Record<string, any>): Promise<LTEscalationRecord | null>;
|
|
35
|
+
/**
|
|
36
|
+
* Mark an escalation cancelled — used when the tied workflow has terminated and
|
|
37
|
+
* can never receive the resolution signal. The escalation is removed from the
|
|
38
|
+
* active queue but preserved for audit. Returns null when the row is missing or
|
|
39
|
+
* already terminal.
|
|
40
|
+
*/
|
|
41
|
+
export declare function cancelEscalation(id: string): Promise<LTEscalationRecord | null>;
|
|
35
42
|
/**
|
|
36
43
|
* Bulk update priority for a set of escalations. Only pending escalations are
|
|
37
44
|
* updated.
|
|
@@ -62,6 +69,16 @@ export declare function escalateToRole(id: string, targetRole: string): Promise<
|
|
|
62
69
|
export declare function getEscalation(id: string): Promise<LTEscalationRecord | null>;
|
|
63
70
|
export declare function getEscalationsByTaskId(taskId: string): Promise<LTEscalationRecord[]>;
|
|
64
71
|
export declare function getEscalationsByWorkflowId(workflowId: string): Promise<LTEscalationRecord[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Cancel all pending escalations tied to a workflow in one atomic UPDATE.
|
|
74
|
+
* Called after workflow termination so escalations don't remain in the queue.
|
|
75
|
+
*
|
|
76
|
+
* HotMesh's interrupt() is supposed to do this atomically, but it filters by
|
|
77
|
+
* `app_id = <engine-appId>`. Long-tail's Durable client uses app_id='durable'
|
|
78
|
+
* while escalations are stored with app_id='hmsh' — the WHERE never matches.
|
|
79
|
+
* This single UPDATE bypasses the SDK entirely and closes that gap.
|
|
80
|
+
*/
|
|
81
|
+
export declare function cancelEscalationsByWorkflowId(workflowId: string): Promise<number>;
|
|
65
82
|
export declare function updateEscalationMetadata(id: string, patch: Record<string, any>): Promise<LTEscalationRecord | null>;
|
|
66
83
|
export declare function enrichEscalationRouting(id: string, metadataPatch: Record<string, any>, workflowFields: {
|
|
67
84
|
workflowType?: string;
|
|
@@ -5,6 +5,7 @@ exports.claimEscalation = claimEscalation;
|
|
|
5
5
|
exports.resolveEscalation = resolveEscalation;
|
|
6
6
|
exports.getEscalationBySignalKey = getEscalationBySignalKey;
|
|
7
7
|
exports.resolveEscalationBySignalKey = resolveEscalationBySignalKey;
|
|
8
|
+
exports.cancelEscalation = cancelEscalation;
|
|
8
9
|
exports.updateEscalationsPriority = updateEscalationsPriority;
|
|
9
10
|
exports.getEscalationRoles = getEscalationRoles;
|
|
10
11
|
exports.releaseEscalation = releaseEscalation;
|
|
@@ -13,6 +14,7 @@ exports.escalateToRole = escalateToRole;
|
|
|
13
14
|
exports.getEscalation = getEscalation;
|
|
14
15
|
exports.getEscalationsByTaskId = getEscalationsByTaskId;
|
|
15
16
|
exports.getEscalationsByWorkflowId = getEscalationsByWorkflowId;
|
|
17
|
+
exports.cancelEscalationsByWorkflowId = cancelEscalationsByWorkflowId;
|
|
16
18
|
exports.updateEscalationMetadata = updateEscalationMetadata;
|
|
17
19
|
exports.enrichEscalationRouting = enrichEscalationRouting;
|
|
18
20
|
exports.getEscalationsByOriginId = getEscalationsByOriginId;
|
|
@@ -138,6 +140,30 @@ async function resolveEscalationBySignalKey(signalKey, resolverPayload) {
|
|
|
138
140
|
return null;
|
|
139
141
|
return resolveEscalation(escalation.id, resolverPayload);
|
|
140
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Mark an escalation cancelled — used when the tied workflow has terminated and
|
|
145
|
+
* can never receive the resolution signal. The escalation is removed from the
|
|
146
|
+
* active queue but preserved for audit. Returns null when the row is missing or
|
|
147
|
+
* already terminal.
|
|
148
|
+
*/
|
|
149
|
+
async function cancelEscalation(id) {
|
|
150
|
+
const client = await (0, client_1.escalations)();
|
|
151
|
+
const result = await client.cancel(id);
|
|
152
|
+
if (!result.ok)
|
|
153
|
+
return null;
|
|
154
|
+
const cancelled = (0, map_1.toEscalationRecord)(result.entry);
|
|
155
|
+
(0, publish_1.publishEscalationEvent)({
|
|
156
|
+
type: 'escalation.cancelled',
|
|
157
|
+
source: 'service',
|
|
158
|
+
workflowId: cancelled.workflow_id || '',
|
|
159
|
+
workflowName: cancelled.workflow_type || '',
|
|
160
|
+
taskQueue: cancelled.task_queue || '',
|
|
161
|
+
escalationId: cancelled.id,
|
|
162
|
+
status: 'cancelled',
|
|
163
|
+
data: { reason: 'workflow_terminated' },
|
|
164
|
+
});
|
|
165
|
+
return cancelled;
|
|
166
|
+
}
|
|
141
167
|
/**
|
|
142
168
|
* Bulk update priority for a set of escalations. Only pending escalations are
|
|
143
169
|
* updated.
|
|
@@ -227,6 +253,37 @@ async function getEscalationsByWorkflowId(workflowId) {
|
|
|
227
253
|
});
|
|
228
254
|
return (0, map_1.toEscalationRecords)(rows);
|
|
229
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Cancel all pending escalations tied to a workflow in one atomic UPDATE.
|
|
258
|
+
* Called after workflow termination so escalations don't remain in the queue.
|
|
259
|
+
*
|
|
260
|
+
* HotMesh's interrupt() is supposed to do this atomically, but it filters by
|
|
261
|
+
* `app_id = <engine-appId>`. Long-tail's Durable client uses app_id='durable'
|
|
262
|
+
* while escalations are stored with app_id='hmsh' — the WHERE never matches.
|
|
263
|
+
* This single UPDATE bypasses the SDK entirely and closes that gap.
|
|
264
|
+
*/
|
|
265
|
+
async function cancelEscalationsByWorkflowId(workflowId) {
|
|
266
|
+
await (0, client_1.ensureEscalationCompatView)();
|
|
267
|
+
const pool = (0, db_1.getPool)();
|
|
268
|
+
const { rows } = await pool.query(`UPDATE public.hmsh_escalations
|
|
269
|
+
SET status = 'cancelled', updated_at = NOW()
|
|
270
|
+
WHERE workflow_id = $1
|
|
271
|
+
AND status = 'pending'
|
|
272
|
+
RETURNING id, workflow_id, workflow_type, task_queue`, [workflowId]);
|
|
273
|
+
for (const row of rows) {
|
|
274
|
+
(0, publish_1.publishEscalationEvent)({
|
|
275
|
+
type: 'escalation.cancelled',
|
|
276
|
+
source: 'service',
|
|
277
|
+
workflowId: row.workflow_id || '',
|
|
278
|
+
workflowName: row.workflow_type || '',
|
|
279
|
+
taskQueue: row.task_queue || '',
|
|
280
|
+
escalationId: row.id,
|
|
281
|
+
status: 'cancelled',
|
|
282
|
+
data: { reason: 'workflow_terminated' },
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
return rows.length;
|
|
286
|
+
}
|
|
230
287
|
async function updateEscalationMetadata(id, patch) {
|
|
231
288
|
const client = await (0, client_1.escalations)();
|
|
232
289
|
const entry = await client.update({ id, metadata: patch });
|
|
@@ -36,4 +36,4 @@ import type { Types } from '@hotmeshio/hotmesh';
|
|
|
36
36
|
* const decision = await conditionLT<{ approved: boolean }>(signalId);
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
|
-
export declare function conditionLT<T = Record<string, any>>(signalId: string, escalation?: Types.ConditionQueueConfig): Promise<T>;
|
|
39
|
+
export declare function conditionLT<T = Record<string, any>>(signalId: string, escalation?: Types.ConditionQueueConfig): Promise<T | false | null>;
|
|
@@ -76,6 +76,9 @@ const LT_ACTIVITY_QUEUE = 'lt-interceptor';
|
|
|
76
76
|
*/
|
|
77
77
|
async function conditionLT(signalId, escalation) {
|
|
78
78
|
const raw = await hotmesh_1.Durable.workflow.condition(signalId, escalation);
|
|
79
|
+
// false = timeout, null = escalation was cancelled — propagate both as-is
|
|
80
|
+
if (raw === null || raw === false)
|
|
81
|
+
return raw;
|
|
79
82
|
const escalationId = raw.$escalation_id;
|
|
80
83
|
if (escalationId) {
|
|
81
84
|
// Resolve the escalation as a durable activity (crash-safe)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diagnostics tools — mirrors routes/diagnostics.ts
|
|
3
|
+
*
|
|
4
|
+
* Three tools:
|
|
5
|
+
* diagnose_job — full per-job diagnosis: events + stream messages + findings
|
|
6
|
+
* find_stalled_jobs — fleet-level: running jobs with no recent progress
|
|
7
|
+
* find_orphaned_signals — fleet-level: suspended jobs with no escalation row
|
|
8
|
+
*/
|
|
9
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
10
|
+
export declare function registerDiagnosticsTools(server: McpServer): void;
|
|
@@ -0,0 +1,94 @@
|
|
|
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.registerDiagnosticsTools = registerDiagnosticsTools;
|
|
37
|
+
const api = __importStar(require("../../../api/diagnostics"));
|
|
38
|
+
const schemas_1 = require("./schemas");
|
|
39
|
+
function registerDiagnosticsTools(server) {
|
|
40
|
+
server.registerTool('diagnose_job', {
|
|
41
|
+
title: 'Diagnose Job',
|
|
42
|
+
description: 'Complete diagnostic for a single workflow job. Combines the execution event ' +
|
|
43
|
+
'timeline (from exportWorkflowExecution) with the raw stream messages for that ' +
|
|
44
|
+
'job (engine + worker, including full JSONB payload, retry state, and timing) ' +
|
|
45
|
+
'and the escalation row from hmsh_escalations. Returns structured findings[] ' +
|
|
46
|
+
'with confidence, evidence, and treatment options. ' +
|
|
47
|
+
'Use when a workflow is stalled, stuck at a condition, has dead-lettered ' +
|
|
48
|
+
'messages, or shows unexpected behavior. Call find_stalled_jobs or ' +
|
|
49
|
+
'find_orphaned_signals first to surface job IDs, then call this per job.',
|
|
50
|
+
inputSchema: schemas_1.diagnoseJobSchema,
|
|
51
|
+
}, async (args) => {
|
|
52
|
+
const result = await api.diagnose({ workflowId: args.workflow_id, appId: args.app_id });
|
|
53
|
+
if (result.error) {
|
|
54
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: result.error }) }], isError: true };
|
|
55
|
+
}
|
|
56
|
+
return { content: [{ type: 'text', text: JSON.stringify(result.data) }] };
|
|
57
|
+
});
|
|
58
|
+
server.registerTool('find_stalled_jobs', {
|
|
59
|
+
title: 'Find Stalled Jobs',
|
|
60
|
+
description: 'Find all running jobs that have not progressed in N minutes. ' +
|
|
61
|
+
'Returns workflow_id, workflow_type, stalled_ms, created_at, updated_at. ' +
|
|
62
|
+
'Use as a first step to surface the scope of a stall incident. ' +
|
|
63
|
+
'Follow up with diagnose_job on individual results for root cause.',
|
|
64
|
+
inputSchema: schemas_1.findStalledJobsSchema,
|
|
65
|
+
}, async (args) => {
|
|
66
|
+
const result = await api.stalledJobs({
|
|
67
|
+
appId: args.app_id,
|
|
68
|
+
stalledMinutes: args.stalled_minutes,
|
|
69
|
+
workflowType: args.workflow_type,
|
|
70
|
+
limit: args.limit,
|
|
71
|
+
});
|
|
72
|
+
if (result.error) {
|
|
73
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: result.error }) }], isError: true };
|
|
74
|
+
}
|
|
75
|
+
return { content: [{ type: 'text', text: JSON.stringify(result.data) }] };
|
|
76
|
+
});
|
|
77
|
+
server.registerTool('find_orphaned_signals', {
|
|
78
|
+
title: 'Find Orphaned Signals',
|
|
79
|
+
description: 'Find running workflows that are suspended at a condition() call ' +
|
|
80
|
+
'(waiter Leg1 committed, signal registered) but have no corresponding ' +
|
|
81
|
+
'escalation row in hmsh_escalations. These are permanently stalled — ' +
|
|
82
|
+
'the workflow is waiting for a signal that nothing will ever send. ' +
|
|
83
|
+
'Returns job_id, signal_id, workflow_name, suspended_at, stalled_ms, ' +
|
|
84
|
+
'and missing_queue_config (true = pre-0.22 SDK, false = DB error at commit time). ' +
|
|
85
|
+
'Call diagnose_job on any result for the full picture and recovery options.',
|
|
86
|
+
inputSchema: schemas_1.findOrphanedSignalsSchema,
|
|
87
|
+
}, async (args) => {
|
|
88
|
+
const result = await api.orphanedSignals({ appId: args.app_id, limit: args.limit });
|
|
89
|
+
if (result.error) {
|
|
90
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: result.error }) }], isError: true };
|
|
91
|
+
}
|
|
92
|
+
return { content: [{ type: 'text', text: JSON.stringify(result.data) }] };
|
|
93
|
+
});
|
|
94
|
+
}
|
|
@@ -54,7 +54,8 @@ const topics_1 = require("./topics");
|
|
|
54
54
|
const settings_1 = require("./settings");
|
|
55
55
|
const exports_1 = require("./exports");
|
|
56
56
|
const overview_1 = require("./overview");
|
|
57
|
-
const
|
|
57
|
+
const diagnostics_1 = require("./diagnostics");
|
|
58
|
+
const TOOL_COUNT = 75;
|
|
58
59
|
let server = null;
|
|
59
60
|
async function createAdminServer(options) {
|
|
60
61
|
if (server && !options?.fresh)
|
|
@@ -81,7 +82,8 @@ async function createAdminServer(options) {
|
|
|
81
82
|
(0, settings_1.registerSettingsTools)(instance); // 1 tool
|
|
82
83
|
(0, exports_1.registerExportTools)(instance); // 4 tools
|
|
83
84
|
(0, overview_1.registerOverviewTools)(instance); // 1 tool
|
|
84
|
-
//
|
|
85
|
+
(0, diagnostics_1.registerDiagnosticsTools)(instance); // 3 tools
|
|
86
|
+
// Total: 75
|
|
85
87
|
logger_1.loggerRegistry.info(`[lt-mcp:admin] ${name} ready (${TOOL_COUNT} tools registered)`);
|
|
86
88
|
return instance;
|
|
87
89
|
}
|
|
@@ -1023,3 +1023,39 @@ export declare const getExportStatusSchema: z.ZodObject<{
|
|
|
1023
1023
|
}, {
|
|
1024
1024
|
workflow_id: string;
|
|
1025
1025
|
}>;
|
|
1026
|
+
export declare const diagnoseJobSchema: z.ZodObject<{
|
|
1027
|
+
workflow_id: z.ZodString;
|
|
1028
|
+
app_id: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
1029
|
+
}, "strip", z.ZodTypeAny, {
|
|
1030
|
+
app_id: string;
|
|
1031
|
+
workflow_id: string;
|
|
1032
|
+
}, {
|
|
1033
|
+
workflow_id: string;
|
|
1034
|
+
app_id?: string | undefined;
|
|
1035
|
+
}>;
|
|
1036
|
+
export declare const findStalledJobsSchema: z.ZodObject<{
|
|
1037
|
+
app_id: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
1038
|
+
stalled_minutes: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
1039
|
+
workflow_type: z.ZodOptional<z.ZodString>;
|
|
1040
|
+
limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
1041
|
+
}, "strip", z.ZodTypeAny, {
|
|
1042
|
+
app_id: string;
|
|
1043
|
+
limit: number;
|
|
1044
|
+
stalled_minutes: number;
|
|
1045
|
+
workflow_type?: string | undefined;
|
|
1046
|
+
}, {
|
|
1047
|
+
workflow_type?: string | undefined;
|
|
1048
|
+
app_id?: string | undefined;
|
|
1049
|
+
limit?: number | undefined;
|
|
1050
|
+
stalled_minutes?: number | undefined;
|
|
1051
|
+
}>;
|
|
1052
|
+
export declare const findOrphanedSignalsSchema: z.ZodObject<{
|
|
1053
|
+
app_id: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
1054
|
+
limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
1055
|
+
}, "strip", z.ZodTypeAny, {
|
|
1056
|
+
app_id: string;
|
|
1057
|
+
limit: number;
|
|
1058
|
+
}, {
|
|
1059
|
+
app_id?: string | undefined;
|
|
1060
|
+
limit?: number | undefined;
|
|
1061
|
+
}>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.listStreamMessagesSchema = exports.getStreamStatsSchema = exports.applyThrottleSchema = exports.rollCallSchema = exports.listAppsSchema = exports.revokeBotKeySchema = exports.createBotApiKeySchema = exports.deleteBotSchema = exports.updateBotSchema = exports.createBotSchema = exports.getBotSchema = exports.listBotsSchema = exports.deleteAgentSubscriptionSchema = exports.createAgentSubscriptionSchema = exports.listAgentSubscriptionsSchema = exports.deleteAgentSchema = exports.updateAgentSchema = exports.createAgentSchema = exports.getAgentSchema = exports.listAgentsSchema = exports.pruneSchema = exports.addEscalationChainSchema = exports.createRoleSchema = exports.listRolesSchema = exports.removeUserRoleSchema = exports.addUserRoleSchema = exports.createUserSchema = exports.listUsersSchema = exports.invokeYamlWorkflowSchema = exports.deployYamlWorkflowSchema = exports.createYamlWorkflowSchema = exports.getYamlWorkflowSchema = exports.listYamlWorkflowsSchema = exports.disconnectMcpServerSchema = exports.connectMcpServerSchema = exports.updateMcpServerSchema = exports.listMcpServersSchema = exports.getWorkflowStatusSchema = exports.invokeWorkflowSchema = exports.listDiscoveredWorkflowsSchema = exports.deleteWorkflowConfigSchema = exports.upsertWorkflowConfigSchema = exports.listWorkflowConfigsSchema = exports.bulkTriageSchema = exports.releaseExpiredClaimsSchema = exports.claimEscalationSchema = exports.getEscalationStatsSchema = exports.findEscalationsSchema = exports.getProcessDetailSchema = exports.findTasksSchema = void 0;
|
|
4
|
-
exports.getExportStatusSchema = exports.exportWorkflowExecutionSchema = exports.exportWorkflowStateSchema = exports.listExportJobsSchema = exports.getSettingsSchema = exports.updatePrioritySchema = exports.bulkEscalateSchema = exports.bulkAssignSchema = exports.bulkClaimSchema = exports.resolveByMetadataSchema = exports.claimByMetadataSchema = exports.findByMetadataSchema = exports.deleteTopicSchema = exports.updateTopicSchema = exports.createTopicSchema = exports.getTopicSchema = exports.listTopicsSchema = exports.interruptJobSchema = exports.getJobExecutionSchema = exports.listPipelineJobsSchema = exports.listPipelineEntitiesSchema = void 0;
|
|
4
|
+
exports.findOrphanedSignalsSchema = exports.findStalledJobsSchema = exports.diagnoseJobSchema = exports.getExportStatusSchema = exports.exportWorkflowExecutionSchema = exports.exportWorkflowStateSchema = exports.listExportJobsSchema = exports.getSettingsSchema = exports.updatePrioritySchema = exports.bulkEscalateSchema = exports.bulkAssignSchema = exports.bulkClaimSchema = exports.resolveByMetadataSchema = exports.claimByMetadataSchema = exports.findByMetadataSchema = exports.deleteTopicSchema = exports.updateTopicSchema = exports.createTopicSchema = exports.getTopicSchema = exports.listTopicsSchema = exports.interruptJobSchema = exports.getJobExecutionSchema = exports.listPipelineJobsSchema = exports.listPipelineEntitiesSchema = void 0;
|
|
5
5
|
/**
|
|
6
6
|
* Zod schemas for the admin MCP server tools.
|
|
7
7
|
*
|
|
@@ -415,3 +415,18 @@ exports.exportWorkflowExecutionSchema = zod_1.z.object({
|
|
|
415
415
|
exports.getExportStatusSchema = zod_1.z.object({
|
|
416
416
|
workflow_id: zod_1.z.string().describe('Workflow ID to check export status'),
|
|
417
417
|
});
|
|
418
|
+
// ── Diagnostics ───────────────────────────────────────────────────────────────
|
|
419
|
+
exports.diagnoseJobSchema = zod_1.z.object({
|
|
420
|
+
workflow_id: zod_1.z.string().describe('Workflow ID to diagnose'),
|
|
421
|
+
app_id: zod_1.z.string().optional().default('durable').describe('HotMesh namespace / DB schema (default: durable)'),
|
|
422
|
+
});
|
|
423
|
+
exports.findStalledJobsSchema = zod_1.z.object({
|
|
424
|
+
app_id: zod_1.z.string().optional().default('durable').describe('HotMesh namespace / DB schema (default: durable)'),
|
|
425
|
+
stalled_minutes: zod_1.z.number().int().min(1).optional().default(5).describe('Minimum minutes since last progress (default: 5)'),
|
|
426
|
+
workflow_type: zod_1.z.string().optional().describe('Filter by workflow function name'),
|
|
427
|
+
limit: zod_1.z.number().int().min(1).max(200).optional().default(50).describe('Max results (default: 50)'),
|
|
428
|
+
});
|
|
429
|
+
exports.findOrphanedSignalsSchema = zod_1.z.object({
|
|
430
|
+
app_id: zod_1.z.string().optional().default('durable').describe('HotMesh namespace / DB schema (default: durable)'),
|
|
431
|
+
limit: zod_1.z.number().int().min(1).max(500).optional().default(100).describe('Max results (default: 100)'),
|
|
432
|
+
});
|