@hotmeshio/long-tail 0.5.7 → 0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/api/escalations/metadata.js +17 -1
- package/build/services/escalation/crud.d.ts +10 -3
- package/build/services/escalation/crud.js +6 -1
- package/build/services/escalation/sql.d.ts +16 -6
- package/build/services/escalation/sql.js +21 -6
- package/build/services/yaml-workflow/deployer.js +5 -0
- package/build/services/yaml-workflow/pipe-validator.d.ts +44 -0
- package/build/services/yaml-workflow/pipe-validator.js +215 -0
- package/build/system/workflows/mcp-workflow-builder/index.js +15 -2
- package/build/tsconfig.tsbuildinfo +1 -1
- package/docs/hitl-guide.md +48 -0
- package/package.json +2 -1
|
@@ -125,10 +125,26 @@ async function resolveByMetadata(input, auth) {
|
|
|
125
125
|
if (result.outcome === 'not_found') {
|
|
126
126
|
return { status: 404, error: 'No pending escalation found for this metadata, or insufficient role permissions' };
|
|
127
127
|
}
|
|
128
|
+
if (result.outcome === 'conflict') {
|
|
129
|
+
return { status: 409, error: 'A concurrent resolution is already in progress for this escalation' };
|
|
130
|
+
}
|
|
128
131
|
if (result.outcome === 'resolved') {
|
|
129
132
|
return { status: 200, data: { escalation: result.escalation } };
|
|
130
133
|
}
|
|
131
|
-
//
|
|
134
|
+
// Atomic conditionLT escalation (signal_key set) — SDK resolve atomically marks
|
|
135
|
+
// resolved AND delivers the signal to the waiting condition(), resuming the workflow.
|
|
136
|
+
if (result.signalKey) {
|
|
137
|
+
const resolved = await escalationService.resolveEscalation(result.escalationId, input.resolverPayload);
|
|
138
|
+
if (!resolved) {
|
|
139
|
+
return { status: 409, error: 'Escalation not available for resolution' };
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
status: 200,
|
|
143
|
+
data: { signaled: true, escalationId: result.escalationId, workflowId: result.workflowId },
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// Legacy conditionLT escalation (metadata.signal_id set) — signal the workflow,
|
|
147
|
+
// conditionLT interceptor resolves durably via ltResolveEscalation.
|
|
132
148
|
const { createClient } = await Promise.resolve().then(() => __importStar(require('../../workers')));
|
|
133
149
|
const client = createClient();
|
|
134
150
|
const handle = await client.workflow.getHandle(result.taskQueue, result.workflowType, result.workflowId);
|
|
@@ -103,12 +103,19 @@ export declare function claimByMetadata(key: string, value: string, userId: stri
|
|
|
103
103
|
candidatesExist: number;
|
|
104
104
|
}) | null>;
|
|
105
105
|
export interface ResolveByMetadataResult {
|
|
106
|
-
/**
|
|
107
|
-
|
|
106
|
+
/**
|
|
107
|
+
* 'resolved' = done atomically in SQL (no signal backing).
|
|
108
|
+
* 'signal_required' = signal backing present, caller must deliver the signal.
|
|
109
|
+
* 'conflict' = signal_id row already claimed by a concurrent caller; skip re-signal.
|
|
110
|
+
* 'not_found' = no pending escalation matched the metadata filter.
|
|
111
|
+
*/
|
|
112
|
+
outcome: 'resolved' | 'signal_required' | 'conflict' | 'not_found';
|
|
108
113
|
/** The resolved escalation (when outcome = 'resolved') */
|
|
109
114
|
escalation?: LTEscalationRecord;
|
|
110
|
-
/**
|
|
115
|
+
/** Legacy conditionLT signal info (when signalId is set, caller uses handle.signal) */
|
|
111
116
|
signalId?: string;
|
|
117
|
+
/** Atomic conditionLT signal key (when signalKey is set, caller uses SDK resolve to atomically mark+signal) */
|
|
118
|
+
signalKey?: string;
|
|
112
119
|
escalationId?: string;
|
|
113
120
|
workflowId?: string;
|
|
114
121
|
workflowType?: string;
|
|
@@ -402,10 +402,15 @@ async function resolveByMetadataAtomic(key, value, userId, resolverPayload, meta
|
|
|
402
402
|
});
|
|
403
403
|
return { outcome: 'resolved', escalation };
|
|
404
404
|
}
|
|
405
|
+
// Signal_id row already claimed by a concurrent caller — skip re-signal to prevent duplicate delivery.
|
|
406
|
+
if (row.signal_id && row.signal_already_claimed) {
|
|
407
|
+
return { outcome: 'conflict', escalationId: row.target_id };
|
|
408
|
+
}
|
|
405
409
|
// Signal-backed escalation — return the signal info for the caller to deliver.
|
|
406
410
|
return {
|
|
407
411
|
outcome: 'signal_required',
|
|
408
|
-
signalId: row.signal_id,
|
|
412
|
+
signalId: row.signal_id ?? undefined,
|
|
413
|
+
signalKey: row.signal_key ?? undefined,
|
|
409
414
|
escalationId: row.target_id,
|
|
410
415
|
workflowId: row.target_workflow_id,
|
|
411
416
|
workflowType: row.target_workflow_type,
|
|
@@ -11,13 +11,23 @@ export declare const RELEASE_EXPIRED_CLAIMS = "UPDATE public.hmsh_escalations\nS
|
|
|
11
11
|
/**
|
|
12
12
|
* Atomic resolve by metadata with signal guard.
|
|
13
13
|
*
|
|
14
|
-
* Single query,
|
|
15
|
-
* 1. No
|
|
16
|
-
* 2. `metadata.signal_id` present →
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* Single query, four outcomes:
|
|
15
|
+
* 1. No signal backing → claim + resolve atomically. `resolved` is populated.
|
|
16
|
+
* 2. `metadata.signal_id` present, row unclaimed → claim the row (preventing concurrent
|
|
17
|
+
* duplicate signals via FOR UPDATE serialization), then return signal info so the
|
|
18
|
+
* caller can signal the workflow. `signal_already_claimed = false`.
|
|
19
|
+
* 3. `metadata.signal_id` present, row already claimed and claim not expired →
|
|
20
|
+
* a concurrent caller is handling the signal. `signal_already_claimed = true`;
|
|
21
|
+
* caller returns 409 without re-signaling.
|
|
22
|
+
* 4. `signal_key` present (atomic conditionLT) → `claimed` and `resolved` both skip.
|
|
23
|
+
* Caller invokes SDK resolve to atomically mark resolved + deliver the signal.
|
|
24
|
+
*
|
|
25
|
+
* Signal_id hardening: the `claimed` CTE runs for signal_id rows (previously excluded).
|
|
26
|
+
* This stamps `assigned_to` on the row inside the FOR UPDATE transaction, so a
|
|
27
|
+
* concurrent second caller sees `signal_already_claimed = true` and aborts.
|
|
28
|
+
* The `resolved` CTE still skips signal_id rows — the workflow resolves durably.
|
|
19
29
|
*
|
|
20
30
|
* $1 = metadata filter (jsonb), $2 = userId, $3 = resolver_payload (jsonb),
|
|
21
31
|
* $4 = metadata patch (jsonb, nullable), $5 = allowed roles (text[], null = no filter)
|
|
22
32
|
*/
|
|
23
|
-
export declare const RESOLVE_BY_METADATA_ATOMIC = "WITH target AS MATERIALIZED (\n SELECT *\n FROM public.hmsh_escalations\n WHERE metadata @> $1::jsonb\n AND status = 'pending'\n AND ($5::text[] IS NULL OR role = ANY($5))\n ORDER BY priority ASC, created_at ASC\n LIMIT 1\n FOR UPDATE\n),\nclaimed AS (\n UPDATE public.hmsh_escalations e\n SET assigned_to = COALESCE(e.assigned_to, $2),\n claimed_at = COALESCE(e.claimed_at, NOW()),\n assigned_until = CASE\n WHEN e.assigned_to IS NOT NULL AND e.assigned_until > NOW() THEN e.assigned_until\n ELSE NOW() + INTERVAL '5 minutes' END,\n claim_expires_at = CASE\n WHEN e.assigned_to IS NOT NULL AND e.assigned_until > NOW() THEN e.claim_expires_at\n ELSE NOW() + INTERVAL '5 minutes' END,\n metadata = CASE WHEN $4::jsonb IS NOT NULL\n THEN COALESCE(e.metadata, '{}'::jsonb) || $4::jsonb\n ELSE e.metadata END,\n updated_at = NOW()\n FROM target\n WHERE e.id = target.id\n AND
|
|
33
|
+
export declare const RESOLVE_BY_METADATA_ATOMIC = "WITH target AS MATERIALIZED (\n SELECT *\n FROM public.hmsh_escalations\n WHERE metadata @> $1::jsonb\n AND status = 'pending'\n AND ($5::text[] IS NULL OR role = ANY($5))\n ORDER BY priority ASC, created_at ASC\n LIMIT 1\n FOR UPDATE\n),\nclaimed AS (\n UPDATE public.hmsh_escalations e\n SET assigned_to = COALESCE(e.assigned_to, $2),\n claimed_at = COALESCE(e.claimed_at, NOW()),\n assigned_until = CASE\n WHEN e.assigned_to IS NOT NULL AND e.assigned_until > NOW() THEN e.assigned_until\n ELSE NOW() + INTERVAL '5 minutes' END,\n claim_expires_at = CASE\n WHEN e.assigned_to IS NOT NULL AND e.assigned_until > NOW() THEN e.claim_expires_at\n ELSE NOW() + INTERVAL '5 minutes' END,\n metadata = CASE WHEN $4::jsonb IS NOT NULL\n THEN COALESCE(e.metadata, '{}'::jsonb) || $4::jsonb\n ELSE e.metadata END,\n updated_at = NOW()\n FROM target\n WHERE e.id = target.id\n AND target.signal_key IS NULL\n RETURNING e.*\n),\nresolved AS (\n UPDATE public.hmsh_escalations e\n SET status = 'resolved',\n resolved_at = NOW(),\n resolver_payload = $3,\n updated_at = NOW()\n FROM claimed\n WHERE e.id = claimed.id\n AND (claimed.metadata->>'signal_id') IS NULL\n RETURNING e.*\n)\nSELECT\n resolved.*,\n target.id AS target_id,\n target.metadata->>'signal_id' AS signal_id,\n target.signal_key AS signal_key,\n target.workflow_id AS target_workflow_id,\n target.workflow_type AS target_workflow_type,\n target.task_queue AS target_task_queue,\n (target.assigned_to IS NOT NULL\n AND target.assigned_until IS NOT NULL\n AND target.assigned_until > NOW()) AS signal_already_claimed,\n CASE WHEN resolved.id IS NOT NULL THEN 'resolved' ELSE 'signal_required' END AS outcome\nFROM target\nLEFT JOIN resolved ON resolved.id = target.id";
|
|
@@ -34,11 +34,21 @@ WHERE status = 'pending'
|
|
|
34
34
|
/**
|
|
35
35
|
* Atomic resolve by metadata with signal guard.
|
|
36
36
|
*
|
|
37
|
-
* Single query,
|
|
38
|
-
* 1. No
|
|
39
|
-
* 2. `metadata.signal_id` present →
|
|
40
|
-
*
|
|
41
|
-
*
|
|
37
|
+
* Single query, four outcomes:
|
|
38
|
+
* 1. No signal backing → claim + resolve atomically. `resolved` is populated.
|
|
39
|
+
* 2. `metadata.signal_id` present, row unclaimed → claim the row (preventing concurrent
|
|
40
|
+
* duplicate signals via FOR UPDATE serialization), then return signal info so the
|
|
41
|
+
* caller can signal the workflow. `signal_already_claimed = false`.
|
|
42
|
+
* 3. `metadata.signal_id` present, row already claimed and claim not expired →
|
|
43
|
+
* a concurrent caller is handling the signal. `signal_already_claimed = true`;
|
|
44
|
+
* caller returns 409 without re-signaling.
|
|
45
|
+
* 4. `signal_key` present (atomic conditionLT) → `claimed` and `resolved` both skip.
|
|
46
|
+
* Caller invokes SDK resolve to atomically mark resolved + deliver the signal.
|
|
47
|
+
*
|
|
48
|
+
* Signal_id hardening: the `claimed` CTE runs for signal_id rows (previously excluded).
|
|
49
|
+
* This stamps `assigned_to` on the row inside the FOR UPDATE transaction, so a
|
|
50
|
+
* concurrent second caller sees `signal_already_claimed = true` and aborts.
|
|
51
|
+
* The `resolved` CTE still skips signal_id rows — the workflow resolves durably.
|
|
42
52
|
*
|
|
43
53
|
* $1 = metadata filter (jsonb), $2 = userId, $3 = resolver_payload (jsonb),
|
|
44
54
|
* $4 = metadata patch (jsonb, nullable), $5 = allowed roles (text[], null = no filter)
|
|
@@ -70,7 +80,7 @@ claimed AS (
|
|
|
70
80
|
updated_at = NOW()
|
|
71
81
|
FROM target
|
|
72
82
|
WHERE e.id = target.id
|
|
73
|
-
AND
|
|
83
|
+
AND target.signal_key IS NULL
|
|
74
84
|
RETURNING e.*
|
|
75
85
|
),
|
|
76
86
|
resolved AS (
|
|
@@ -81,15 +91,20 @@ resolved AS (
|
|
|
81
91
|
updated_at = NOW()
|
|
82
92
|
FROM claimed
|
|
83
93
|
WHERE e.id = claimed.id
|
|
94
|
+
AND (claimed.metadata->>'signal_id') IS NULL
|
|
84
95
|
RETURNING e.*
|
|
85
96
|
)
|
|
86
97
|
SELECT
|
|
87
98
|
resolved.*,
|
|
88
99
|
target.id AS target_id,
|
|
89
100
|
target.metadata->>'signal_id' AS signal_id,
|
|
101
|
+
target.signal_key AS signal_key,
|
|
90
102
|
target.workflow_id AS target_workflow_id,
|
|
91
103
|
target.workflow_type AS target_workflow_type,
|
|
92
104
|
target.task_queue AS target_task_queue,
|
|
105
|
+
(target.assigned_to IS NOT NULL
|
|
106
|
+
AND target.assigned_until IS NOT NULL
|
|
107
|
+
AND target.assigned_until > NOW()) AS signal_already_claimed,
|
|
93
108
|
CASE WHEN resolved.id IS NOT NULL THEN 'resolved' ELSE 'signal_required' END AS outcome
|
|
94
109
|
FROM target
|
|
95
110
|
LEFT JOIN resolved ON resolved.id = target.id`;
|
|
@@ -49,6 +49,7 @@ const logger_1 = require("../../lib/logger");
|
|
|
49
49
|
const system_events_1 = require("../../lib/events/system-events");
|
|
50
50
|
const namespaceService = __importStar(require("../namespace"));
|
|
51
51
|
const deployer_helpers_1 = require("./deployer-helpers");
|
|
52
|
+
const pipe_validator_1 = require("./pipe-validator");
|
|
52
53
|
// Re-export helpers so existing `import * from './deployer'` consumers keep working
|
|
53
54
|
var deployer_helpers_2 = require("./deployer-helpers");
|
|
54
55
|
Object.defineProperty(exports, "buildMergedYaml", { enumerable: true, get: function () { return deployer_helpers_2.buildMergedYaml; } });
|
|
@@ -90,6 +91,10 @@ async function deployAppId(appId, version) {
|
|
|
90
91
|
await namespaceService.registerNamespace(appId);
|
|
91
92
|
const mergedYaml = await (0, deployer_helpers_1.buildMergedYaml)(appId, version);
|
|
92
93
|
logger_1.loggerRegistry.debug(`[yaml-workflow] merged YAML for ${appId} v${version}:\n${mergedYaml}`);
|
|
94
|
+
// Structural guard: reject YAML with malformed @pipe rows before they reach
|
|
95
|
+
// the HotMesh engine. A bad pipe passes deploy() silently but causes
|
|
96
|
+
// collation errors on every execution, poisoning the stream.
|
|
97
|
+
(0, pipe_validator_1.validatePipeStructure)(mergedYaml);
|
|
93
98
|
const engine = await getEngine(appId);
|
|
94
99
|
try {
|
|
95
100
|
const manifest = await engine.deploy(mergedYaml);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural validator for HotMesh YAML pipe syntax.
|
|
3
|
+
*
|
|
4
|
+
* HotMesh collation errors are silent at deploy time but catastrophic at
|
|
5
|
+
* runtime — a single malformed @pipe in an activated workflow poisons the
|
|
6
|
+
* engine's stream, causing every execution attempt to fail with a
|
|
7
|
+
* collation-error until the database is wiped.
|
|
8
|
+
*
|
|
9
|
+
* This module provides:
|
|
10
|
+
* validatePipeStructure — throws on any structurally invalid @pipe
|
|
11
|
+
* repairPipeStructure — auto-repairs safe-to-fix patterns, then validates
|
|
12
|
+
*
|
|
13
|
+
* Call repairPipeStructure before storing or deploying any YAML.
|
|
14
|
+
*
|
|
15
|
+
* ## What constitutes a valid @pipe row
|
|
16
|
+
*
|
|
17
|
+
* An @pipe value is a YAML sequence. Each element (row) must be one of:
|
|
18
|
+
* 1. A YAML sequence (array) — operands row OR function call row
|
|
19
|
+
* 2. A YAML mapping with '@pipe' key — nested sub-pipe
|
|
20
|
+
*
|
|
21
|
+
* Invalid row types that cause collation errors:
|
|
22
|
+
* - A bare string: '{@object.create}' → must be ['{@object.create}']
|
|
23
|
+
* - A number/bool: 42, true → must be wrapped in an array
|
|
24
|
+
* - A double-nested sequence: [[...]] → row itself is an array of arrays
|
|
25
|
+
*/
|
|
26
|
+
export interface PipeViolation {
|
|
27
|
+
path: string;
|
|
28
|
+
rowIndex: number;
|
|
29
|
+
rowValue: unknown;
|
|
30
|
+
message: string;
|
|
31
|
+
repairable: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parse the YAML and check every @pipe for structural violations.
|
|
35
|
+
* Throws a descriptive error listing all violations if any are found.
|
|
36
|
+
*/
|
|
37
|
+
export declare function validatePipeStructure(yamlContent: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Auto-repair safe violations (bare strings/scalars → array rows), then
|
|
40
|
+
* validate that no unfixable violations remain.
|
|
41
|
+
*
|
|
42
|
+
* Returns the repaired YAML string. Throws if unfixable violations exist.
|
|
43
|
+
*/
|
|
44
|
+
export declare function repairPipeStructure(yamlContent: string): string;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Structural validator for HotMesh YAML pipe syntax.
|
|
4
|
+
*
|
|
5
|
+
* HotMesh collation errors are silent at deploy time but catastrophic at
|
|
6
|
+
* runtime — a single malformed @pipe in an activated workflow poisons the
|
|
7
|
+
* engine's stream, causing every execution attempt to fail with a
|
|
8
|
+
* collation-error until the database is wiped.
|
|
9
|
+
*
|
|
10
|
+
* This module provides:
|
|
11
|
+
* validatePipeStructure — throws on any structurally invalid @pipe
|
|
12
|
+
* repairPipeStructure — auto-repairs safe-to-fix patterns, then validates
|
|
13
|
+
*
|
|
14
|
+
* Call repairPipeStructure before storing or deploying any YAML.
|
|
15
|
+
*
|
|
16
|
+
* ## What constitutes a valid @pipe row
|
|
17
|
+
*
|
|
18
|
+
* An @pipe value is a YAML sequence. Each element (row) must be one of:
|
|
19
|
+
* 1. A YAML sequence (array) — operands row OR function call row
|
|
20
|
+
* 2. A YAML mapping with '@pipe' key — nested sub-pipe
|
|
21
|
+
*
|
|
22
|
+
* Invalid row types that cause collation errors:
|
|
23
|
+
* - A bare string: '{@object.create}' → must be ['{@object.create}']
|
|
24
|
+
* - A number/bool: 42, true → must be wrapped in an array
|
|
25
|
+
* - A double-nested sequence: [[...]] → row itself is an array of arrays
|
|
26
|
+
*/
|
|
27
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
30
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
31
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
32
|
+
}
|
|
33
|
+
Object.defineProperty(o, k2, desc);
|
|
34
|
+
}) : (function(o, m, k, k2) {
|
|
35
|
+
if (k2 === undefined) k2 = k;
|
|
36
|
+
o[k2] = m[k];
|
|
37
|
+
}));
|
|
38
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
39
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
40
|
+
}) : function(o, v) {
|
|
41
|
+
o["default"] = v;
|
|
42
|
+
});
|
|
43
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
44
|
+
var ownKeys = function(o) {
|
|
45
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
46
|
+
var ar = [];
|
|
47
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
48
|
+
return ar;
|
|
49
|
+
};
|
|
50
|
+
return ownKeys(o);
|
|
51
|
+
};
|
|
52
|
+
return function (mod) {
|
|
53
|
+
if (mod && mod.__esModule) return mod;
|
|
54
|
+
var result = {};
|
|
55
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
56
|
+
__setModuleDefault(result, mod);
|
|
57
|
+
return result;
|
|
58
|
+
};
|
|
59
|
+
})();
|
|
60
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
61
|
+
exports.validatePipeStructure = validatePipeStructure;
|
|
62
|
+
exports.repairPipeStructure = repairPipeStructure;
|
|
63
|
+
const jsYaml = __importStar(require("js-yaml"));
|
|
64
|
+
// ── internal traversal ────────────────────────────────────────────────────────
|
|
65
|
+
function isPlainObject(v) {
|
|
66
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
67
|
+
}
|
|
68
|
+
function collectPipeViolations(node, path, violations) {
|
|
69
|
+
if (Array.isArray(node)) {
|
|
70
|
+
node.forEach((item, i) => collectPipeViolations(item, `${path}[${i}]`, violations));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!isPlainObject(node))
|
|
74
|
+
return;
|
|
75
|
+
for (const [key, value] of Object.entries(node)) {
|
|
76
|
+
const childPath = `${path}.${key}`;
|
|
77
|
+
if (key === '@pipe') {
|
|
78
|
+
if (!Array.isArray(value)) {
|
|
79
|
+
violations.push({
|
|
80
|
+
path: childPath,
|
|
81
|
+
rowIndex: -1,
|
|
82
|
+
rowValue: value,
|
|
83
|
+
message: `@pipe value must be a sequence (array), got ${typeof value}`,
|
|
84
|
+
repairable: false,
|
|
85
|
+
});
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
value.forEach((row, i) => {
|
|
89
|
+
if (Array.isArray(row)) {
|
|
90
|
+
// Valid row — but check for double-nested arrays: [[...]]
|
|
91
|
+
if (row.length > 0 && row.every((el) => Array.isArray(el))) {
|
|
92
|
+
violations.push({
|
|
93
|
+
path: childPath,
|
|
94
|
+
rowIndex: i,
|
|
95
|
+
rowValue: row,
|
|
96
|
+
message: `row ${i} is a double-nested array (array of arrays) — each @pipe row must be a flat array`,
|
|
97
|
+
repairable: false,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (isPlainObject(row) && '@pipe' in row) {
|
|
102
|
+
// Valid nested sub-pipe — recurse into it
|
|
103
|
+
collectPipeViolations(row, `${childPath}[${i}]`, violations);
|
|
104
|
+
}
|
|
105
|
+
else if (typeof row === 'string') {
|
|
106
|
+
// '{@fn}' as bare string — repairable: wrap in array
|
|
107
|
+
violations.push({
|
|
108
|
+
path: childPath,
|
|
109
|
+
rowIndex: i,
|
|
110
|
+
rowValue: row,
|
|
111
|
+
message: `row ${i} is a bare string "${row}" — must be an array row like ['${row}']`,
|
|
112
|
+
repairable: true,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else if (typeof row === 'number' || typeof row === 'boolean') {
|
|
116
|
+
violations.push({
|
|
117
|
+
path: childPath,
|
|
118
|
+
rowIndex: i,
|
|
119
|
+
rowValue: row,
|
|
120
|
+
message: `row ${i} is a bare scalar ${JSON.stringify(row)} — must be wrapped in an array`,
|
|
121
|
+
repairable: true,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
violations.push({
|
|
126
|
+
path: childPath,
|
|
127
|
+
rowIndex: i,
|
|
128
|
+
rowValue: row,
|
|
129
|
+
message: `row ${i} has unexpected type ${typeof row}`,
|
|
130
|
+
repairable: false,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
collectPipeViolations(value, childPath, violations);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// ── public API ────────────────────────────────────────────────────────────────
|
|
141
|
+
/**
|
|
142
|
+
* Parse the YAML and check every @pipe for structural violations.
|
|
143
|
+
* Throws a descriptive error listing all violations if any are found.
|
|
144
|
+
*/
|
|
145
|
+
function validatePipeStructure(yamlContent) {
|
|
146
|
+
let parsed;
|
|
147
|
+
try {
|
|
148
|
+
parsed = jsYaml.load(yamlContent);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
throw new Error(`YAML parse error: ${err.message}`);
|
|
152
|
+
}
|
|
153
|
+
const violations = [];
|
|
154
|
+
collectPipeViolations(parsed, 'root', violations);
|
|
155
|
+
if (violations.length > 0) {
|
|
156
|
+
const detail = violations
|
|
157
|
+
.map((v) => ` • ${v.path}[${v.rowIndex}]: ${v.message}`)
|
|
158
|
+
.join('\n');
|
|
159
|
+
throw new Error(`Malformed @pipe structure — ${violations.length} violation(s) detected. ` +
|
|
160
|
+
`This YAML will cause HotMesh collation errors at runtime and must not be deployed.\n${detail}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Auto-repair safe violations (bare strings/scalars → array rows), then
|
|
165
|
+
* validate that no unfixable violations remain.
|
|
166
|
+
*
|
|
167
|
+
* Returns the repaired YAML string. Throws if unfixable violations exist.
|
|
168
|
+
*/
|
|
169
|
+
function repairPipeStructure(yamlContent) {
|
|
170
|
+
let parsed;
|
|
171
|
+
try {
|
|
172
|
+
parsed = jsYaml.load(yamlContent);
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
throw new Error(`YAML parse error: ${err.message}`);
|
|
176
|
+
}
|
|
177
|
+
const repairCount = { n: 0 };
|
|
178
|
+
repairNode(parsed, repairCount);
|
|
179
|
+
if (repairCount.n > 0) {
|
|
180
|
+
// Re-serialize — use lineWidth: -1 to prevent wrapping
|
|
181
|
+
yamlContent = jsYaml.dump(parsed, { lineWidth: -1, noRefs: true, quotingType: "'" });
|
|
182
|
+
}
|
|
183
|
+
// Final validation pass — throws if unfixable violations remain
|
|
184
|
+
validatePipeStructure(yamlContent);
|
|
185
|
+
return yamlContent;
|
|
186
|
+
}
|
|
187
|
+
function repairNode(node, count) {
|
|
188
|
+
if (Array.isArray(node)) {
|
|
189
|
+
node.forEach((item) => repairNode(item, count));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (!isPlainObject(node))
|
|
193
|
+
return;
|
|
194
|
+
for (const [key, value] of Object.entries(node)) {
|
|
195
|
+
if (key === '@pipe' && Array.isArray(value)) {
|
|
196
|
+
const rows = value;
|
|
197
|
+
for (let i = 0; i < rows.length; i++) {
|
|
198
|
+
const row = rows[i];
|
|
199
|
+
if (typeof row === 'string' || typeof row === 'number' || typeof row === 'boolean') {
|
|
200
|
+
rows[i] = [row];
|
|
201
|
+
count.n++;
|
|
202
|
+
}
|
|
203
|
+
else if (isPlainObject(row) && '@pipe' in row) {
|
|
204
|
+
repairNode(row, count);
|
|
205
|
+
}
|
|
206
|
+
else if (Array.isArray(row)) {
|
|
207
|
+
repairNode(row, count);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
repairNode(value, count);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.mcpWorkflowBuilder = mcpWorkflowBuilder;
|
|
37
37
|
const hotmesh_1 = require("@hotmeshio/hotmesh");
|
|
38
|
+
const pipe_validator_1 = require("../../../services/yaml-workflow/pipe-validator");
|
|
38
39
|
const activities = __importStar(require("./activities"));
|
|
39
40
|
const prompts_1 = require("./prompts");
|
|
40
41
|
const { loadBuilderTools, loadReferenceSection, callBuilderLLM, } = hotmesh_1.Durable.workflow.proxyActivities({
|
|
@@ -212,8 +213,20 @@ async function mcpWorkflowBuilder(envelope) {
|
|
|
212
213
|
});
|
|
213
214
|
continue;
|
|
214
215
|
}
|
|
215
|
-
// Fix known @pipe anti-patterns
|
|
216
|
-
|
|
216
|
+
// Fix known @pipe anti-patterns, then structurally validate —
|
|
217
|
+
// throws if unfixable violations remain, blocking deployment of poison YAML.
|
|
218
|
+
let fixedYaml = fixPipePatterns(result.yaml);
|
|
219
|
+
try {
|
|
220
|
+
fixedYaml = (0, pipe_validator_1.repairPipeStructure)(fixedYaml);
|
|
221
|
+
}
|
|
222
|
+
catch (pipeErr) {
|
|
223
|
+
messages.push({ role: 'assistant', content });
|
|
224
|
+
messages.push({
|
|
225
|
+
role: 'user',
|
|
226
|
+
content: `The generated YAML has structural @pipe errors that would cause runtime collation failures:\n${pipeErr.message}\n\nFix all @pipe rows: every row must be an array like ['{@fn}'] or [value1, value2]. Never use a bare string as a pipe row.`,
|
|
227
|
+
});
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
217
230
|
return {
|
|
218
231
|
type: 'return',
|
|
219
232
|
data: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../index.ts","../vitest.config.ts","../vitest.integration.config.ts","../adapters/express.ts","../api/agent-subscriptions.ts","../api/agents.ts","../api/auth-sso.ts","../api/auth.ts","../api/bot-accounts.ts","../api/capabilities.ts","../api/controlplane.ts","../api/dba.ts","../api/diagnostics.ts","../api/exports.ts","../api/files.ts","../api/index.ts","../api/insight.ts","../api/knowledge.ts","../api/maintenance.ts","../api/namespaces.ts","../api/overview.ts","../api/pipelines.ts","../api/roles.ts","../api/settings.ts","../api/tasks.ts","../api/topics.ts","../api/users.ts","../api/workflow-sets.ts","../api/escalations/bulk.ts","../api/escalations/cancel.ts","../api/escalations/claim.ts","../api/escalations/create.ts","../api/escalations/helpers.ts","../api/escalations/index.ts","../api/escalations/list.ts","../api/escalations/metadata.ts","../api/escalations/resolve.ts","../api/escalations/single.ts","../api/mcp/index.ts","../api/mcp/servers.ts","../api/mcp/tools.ts","../api/workflows/config.ts","../api/workflows/discovery.ts","../api/workflows/index.ts","../api/workflows/invocation.ts","../api/yaml-workflows/cron.ts","../api/yaml-workflows/crud.ts","../api/yaml-workflows/deploy.ts","../api/yaml-workflows/helpers.ts","../api/yaml-workflows/index.ts","../api/yaml-workflows/versions.ts","../bin/ltc.ts","../lib/cli/auth.ts","../lib/cli/client.ts","../lib/cli/compile.ts","../lib/cli/format.ts","../lib/cli/init.ts","../lib/cli/output.ts","../lib/cli/scanner.ts","../lib/cli/types.ts","../lib/cli/commands/escalations.ts","../lib/cli/commands/knowledge.ts","../lib/cli/commands/mcp.ts","../lib/cli/commands/pipelines.ts","../lib/cli/commands/status.ts","../lib/cli/commands/streams.ts","../lib/cli/commands/users.ts","../lib/cli/commands/workflows.ts","../lib/db/index.ts","../lib/db/migrate.ts","../lib/events/callback.ts","../lib/events/index.ts","../lib/events/matching.ts","../lib/events/memory.ts","../lib/events/nats-ws-proxy.ts","../lib/events/nats.ts","../lib/events/publish.ts","../lib/events/socketio.ts","../lib/events/system-events.ts","../lib/logger/index.ts","../lib/logger/pino.ts","../lib/storage/index.ts","../lib/storage/local.ts","../lib/storage/mime.ts","../lib/storage/s3.ts","../lib/storage/types.ts","../lib/telemetry/honeycomb.ts","../lib/telemetry/index.ts","../modules/auth.ts","../modules/config.ts","../modules/defaults.ts","../modules/ltconfig.ts","../modules/maintenance.ts","../modules/sso.ts","../modules/utils.ts","../routes/agents.ts","../routes/auth-sso.ts","../routes/auth.ts","../routes/bot-accounts.ts","../routes/capabilities.ts","../routes/controlplane.ts","../routes/dba.ts","../routes/delegation.ts","../routes/diagnostics.ts","../routes/docs.ts","../routes/exports.ts","../routes/file-browser.ts","../routes/files.ts","../routes/index.ts","../routes/insight.ts","../routes/knowledge.ts","../routes/maintenance.ts","../routes/mcp-endpoint.ts","../routes/mcp.ts","../routes/namespaces.ts","../routes/nats-credentials.ts","../routes/oauth.ts","../routes/overview.ts","../routes/pipelines.ts","../routes/roles.ts","../routes/settings.ts","../routes/tasks.ts","../routes/topics.ts","../routes/users.ts","../routes/workflow-sets.ts","../routes/escalations/bulk.ts","../routes/escalations/index.ts","../routes/escalations/list.ts","../routes/escalations/metadata.ts","../routes/escalations/resolve.ts","../routes/escalations/single.ts","../routes/workflows/config.ts","../routes/workflows/discovery.ts","../routes/workflows/index.ts","../routes/workflows/invocation.ts","../routes/yaml-workflows/cron.ts","../routes/yaml-workflows/crud.ts","../routes/yaml-workflows/deployment.ts","../routes/yaml-workflows/index.ts","../routes/yaml-workflows/versions.ts","../scripts/process-helpers.ts","../scripts/process.ts","../scripts/token.ts","../sdk/index.ts","../services/dba.ts","../services/hotmesh-utils.ts","../services/workflow-invocation.ts","../services/agent/index.ts","../services/agent/input-mapper.ts","../services/agent/sql.ts","../services/agent/subscription-sql.ts","../services/agent/subscriptions.ts","../services/agent/trigger-registry.ts","../services/auth/bot-api-key.ts","../services/auth/delegation.ts","../services/auth/index.ts","../services/auth/service-token.ts","../services/auth/sql.ts","../services/config/cache.ts","../services/config/index.ts","../services/config/provider.ts","../services/config/read.ts","../services/config/sql.ts","../services/config/write.ts","../services/controlplane/index.ts","../services/controlplane/quorum-bridge.ts","../services/controlplane/sql.ts","../services/controlplane/stream-messages-sql.ts","../services/controlplane/types.ts","../services/cron/index.ts","../services/diagnostics/index.ts","../services/diagnostics/patterns.ts","../services/escalation/bulk.ts","../services/escalation/client.ts","../services/escalation/crud.ts","../services/escalation/index.ts","../services/escalation/map.ts","../services/escalation/queries.ts","../services/escalation/sql.ts","../services/escalation/types.ts","../services/escalation-strategy/default.ts","../services/escalation-strategy/index.ts","../services/escalation-strategy/mcp.ts","../services/export/client.ts","../services/export/index.ts","../services/export/post-process.ts","../services/export/types.ts","../services/iam/activity.ts","../services/iam/bots.ts","../services/iam/context.ts","../services/iam/credentials.ts","../services/iam/envelope.ts","../services/iam/ephemeral.ts","../services/iam/index.ts","../services/iam/principal.ts","../services/iam/resolve.ts","../services/iam/sql.ts","../services/insight/index.ts","../services/insight/prompts.ts","../services/interceptor/activity-interceptor.ts","../services/interceptor/completion.ts","../services/interceptor/context.ts","../services/interceptor/escalation.ts","../services/interceptor/index.ts","../services/interceptor/lifecycle.ts","../services/interceptor/state.ts","../services/interceptor/types.ts","../services/interceptor/activities/config.ts","../services/interceptor/activities/escalation.ts","../services/interceptor/activities/index.ts","../services/interceptor/activities/task.ts","../services/interceptor/activities/workflow.ts","../services/llm/detect.ts","../services/llm/index.ts","../services/llm/translate.ts","../services/llm/types.ts","../services/llm/providers/anthropic.ts","../services/llm/providers/openai.ts","../services/maintenance/index.ts","../services/mcp/adapter.ts","../services/mcp/db.ts","../services/mcp/exposure.ts","../services/mcp/external-server.ts","../services/mcp/index.ts","../services/mcp/register-tool.ts","../services/mcp/seed-service-account.ts","../services/mcp/server-lifecycle.ts","../services/mcp/server-tools.ts","../services/mcp/server.ts","../services/mcp/sql.ts","../services/mcp/types.ts","../services/mcp/workflow-compiler-server.ts","../services/mcp/workflow-server.ts","../services/mcp/client/connection-dispatch.ts","../services/mcp/client/connection-lifecycle.ts","../services/mcp/client/connection-test.ts","../services/mcp/client/connection.ts","../services/mcp/client/index.ts","../services/mcp/client/tools.ts","../services/mcp/db-server/index.ts","../services/mcp/db-server/schemas.ts","../services/mcp/db-server/tools.ts","../services/mcp/playwright-server/index.ts","../services/mcp/playwright-server/lifecycle.ts","../services/mcp/playwright-server/schemas.ts","../services/mcp/playwright-server/tools.ts","../services/namespace/index.ts","../services/namespace/sql.ts","../services/namespace/types.ts","../services/oauth/crypto.ts","../services/oauth/db.ts","../services/oauth/index.ts","../services/oauth/sql.ts","../services/oauth/state.ts","../services/oauth/providers/anthropic.ts","../services/oauth/providers/github.ts","../services/oauth/providers/google.ts","../services/oauth/providers/index.ts","../services/oauth/providers/microsoft.ts","../services/oauth/providers/mock.ts","../services/oauth/providers/registry.ts","../services/oauth/providers/types.ts","../services/orchestrator/condition.ts","../services/orchestrator/index.ts","../services/orchestrator/types.ts","../services/overview/index.ts","../services/overview/sql.ts","../services/pipelines/enrichment.ts","../services/pipelines/events.ts","../services/pipelines/execution-builder.ts","../services/pipelines/index.ts","../services/pipelines/queries.ts","../services/pipelines/sql.ts","../services/pipelines/types.ts","../services/role/index.ts","../services/role/sql.ts","../services/role/types.ts","../services/task/crud.ts","../services/task/index.ts","../services/task/process.ts","../services/task/resolve.ts","../services/task/sql.ts","../services/task/types.ts","../services/topics/index.ts","../services/topics/sql.ts","../services/topics/system-topics.ts","../services/user/auth.ts","../services/user/crud.ts","../services/user/index.ts","../services/user/rbac.ts","../services/user/roles.ts","../services/user/seed-admin.ts","../services/user/sql.ts","../services/user/sso-provision.ts","../services/user/types.ts","../services/workers/registry.ts","../services/workflow-sets/db.ts","../services/workflow-sets/index.ts","../services/workflow-sets/sql.ts","../services/yaml-workflow/builder-regenerate.ts","../services/yaml-workflow/db-utils.ts","../services/yaml-workflow/db-versions.ts","../services/yaml-workflow/db.ts","../services/yaml-workflow/deployer-helpers.ts","../services/yaml-workflow/deployer.ts","../services/yaml-workflow/generator.ts","../services/yaml-workflow/index.ts","../services/yaml-workflow/input-analyzer-helpers.ts","../services/yaml-workflow/input-analyzer.ts","../services/yaml-workflow/invoke.ts","../services/yaml-workflow/sql.ts","../services/yaml-workflow/types.ts","../services/yaml-workflow/durable-compiler/index.ts","../services/yaml-workflow/durable-compiler/parser.ts","../services/yaml-workflow/durable-compiler/prompts.ts","../services/yaml-workflow/durable-compiler/types.ts","../services/yaml-workflow/pattern-detector/array-source.ts","../services/yaml-workflow/pattern-detector/collapse.ts","../services/yaml-workflow/pattern-detector/index.ts","../services/yaml-workflow/pattern-detector/run-detection.ts","../services/yaml-workflow/pattern-detector/types.ts","../services/yaml-workflow/pipeline/analyze.ts","../services/yaml-workflow/pipeline/extract-helpers.ts","../services/yaml-workflow/pipeline/extract.ts","../services/yaml-workflow/pipeline/index.ts","../services/yaml-workflow/pipeline/prompt-templates.ts","../services/yaml-workflow/pipeline/prompts.ts","../services/yaml-workflow/pipeline/validate.ts","../services/yaml-workflow/pipeline/build/dag-assembly.ts","../services/yaml-workflow/pipeline/build/dag.ts","../services/yaml-workflow/pipeline/build/index.ts","../services/yaml-workflow/pipeline/build/iteration.ts","../services/yaml-workflow/pipeline/build/metadata.ts","../services/yaml-workflow/pipeline/build/transform.ts","../services/yaml-workflow/pipeline/build/utils.ts","../services/yaml-workflow/pipeline/build/wiring.ts","../services/yaml-workflow/pipeline/compile/index.ts","../services/yaml-workflow/pipeline/compile/llm-call.ts","../services/yaml-workflow/pipeline/compile/parse-plan.ts","../services/yaml-workflow/pipeline/compile/summarize.ts","../services/yaml-workflow/workers/callbacks.ts","../services/yaml-workflow/workers/events.ts","../services/yaml-workflow/workers/index.ts","../services/yaml-workflow/workers/register.ts","../services/yaml-workflow/workers/scope.ts","../start/adapters.ts","../start/config.ts","../start/graph-workflows.ts","../start/index.ts","../start/server.ts","../start/socket-auth.ts","../start/workers.ts","../system/index.ts","../system/activities/claude-code.ts","../system/activities/file-storage.ts","../system/activities/http.ts","../system/activities/knowledge.ts","../system/activities/oauth.ts","../system/activities/schema-exchange.ts","../system/activities/sql.ts","../system/activities/triage/cache.ts","../system/activities/triage/context.ts","../system/activities/triage/discovery.ts","../system/activities/triage/index.ts","../system/activities/triage/llm.ts","../system/activities/triage/tools.ts","../system/mcp-servers/claude-code.ts","../system/mcp-servers/docs.ts","../system/mcp-servers/events.ts","../system/mcp-servers/file-storage.ts","../system/mcp-servers/http-fetch.ts","../system/mcp-servers/human-queue-schemas.ts","../system/mcp-servers/human-queue.ts","../system/mcp-servers/knowledge.ts","../system/mcp-servers/oauth.ts","../system/mcp-servers/schema-exchange.ts","../system/mcp-servers/translation.ts","../system/mcp-servers/vision-prompts.ts","../system/mcp-servers/vision.ts","../system/mcp-servers/workflow-compiler.ts","../system/mcp-servers/workflow.ts","../system/mcp-servers/admin/agent-subscriptions.ts","../system/mcp-servers/admin/agents.ts","../system/mcp-servers/admin/bot-accounts.ts","../system/mcp-servers/admin/controlplane.ts","../system/mcp-servers/admin/diagnostics.ts","../system/mcp-servers/admin/escalations.ts","../system/mcp-servers/admin/exports.ts","../system/mcp-servers/admin/index.ts","../system/mcp-servers/admin/maintenance.ts","../system/mcp-servers/admin/mcp-servers.ts","../system/mcp-servers/admin/overview.ts","../system/mcp-servers/admin/pipelines.ts","../system/mcp-servers/admin/schemas.ts","../system/mcp-servers/admin/settings.ts","../system/mcp-servers/admin/tasks.ts","../system/mcp-servers/admin/topics.ts","../system/mcp-servers/admin/users.ts","../system/mcp-servers/admin/workflow-config.ts","../system/mcp-servers/admin/workflows.ts","../system/mcp-servers/admin/yaml-workflows.ts","../system/mcp-servers/db-query/index.ts","../system/mcp-servers/db-query/schemas.ts","../system/mcp-servers/db-query/tools.ts","../system/seed/index.ts","../system/seed/tool-manifests-admin.ts","../system/seed/tool-manifests-data.ts","../system/seed/tool-manifests-escalation.ts","../system/seed/tool-manifests-events.ts","../system/seed/tool-manifests-knowledge.ts","../system/seed/tool-manifests-workflows.ts","../system/workflows/tool-result-guard.ts","../system/workflows/capability-invoke/activities.ts","../system/workflows/capability-invoke/index.ts","../system/workflows/mcp-deterministic/activities.ts","../system/workflows/mcp-deterministic/index.ts","../system/workflows/mcp-query/index.ts","../system/workflows/mcp-query/prompts.ts","../system/workflows/mcp-query/strategy-advisors.ts","../system/workflows/mcp-query/types.ts","../system/workflows/mcp-query/activities/caches.ts","../system/workflows/mcp-query/activities/discovery.ts","../system/workflows/mcp-query/activities/index.ts","../system/workflows/mcp-query/activities/llm.ts","../system/workflows/mcp-query/activities/tool-executor.ts","../system/workflows/mcp-query/activities/tool-loader.ts","../system/workflows/mcp-query-router/activities.ts","../system/workflows/mcp-query-router/index.ts","../system/workflows/mcp-query-router/prompts.ts","../system/workflows/mcp-triage/activities-proxy.ts","../system/workflows/mcp-triage/index.ts","../system/workflows/mcp-triage/prompts.ts","../system/workflows/mcp-triage/response-builders.ts","../system/workflows/mcp-triage/response.ts","../system/workflows/mcp-triage/types.ts","../system/workflows/mcp-triage-deterministic/activities.ts","../system/workflows/mcp-triage-deterministic/index.ts","../system/workflows/mcp-triage-router/activities.ts","../system/workflows/mcp-triage-router/index.ts","../system/workflows/mcp-workflow-builder/index.ts","../system/workflows/mcp-workflow-builder/prompts.ts","../system/workflows/mcp-workflow-builder/activities/caches.ts","../system/workflows/mcp-workflow-builder/activities/index.ts","../system/workflows/mcp-workflow-builder/activities/llm.ts","../system/workflows/mcp-workflow-builder/activities/tool-loader.ts","../system/workflows/mcp-workflow-planner/index.ts","../system/workflows/mcp-workflow-planner/prompts.ts","../system/workflows/mcp-workflow-planner/activities/analyze.ts","../system/workflows/mcp-workflow-planner/activities/index.ts","../system/workflows/mcp-workflow-planner/activities/persist.ts","../system/workflows/mcp-workflow-planner/activities/plan.ts","../system/workflows/shared/discovery.ts","../system/workflows/shared/index.ts","../system/workflows/shared/llm-caller.ts","../system/workflows/shared/prompts.ts","../system/workflows/shared/strategy-advisors.ts","../system/workflows/shared/tool-executor.ts","../system/workflows/shared/tool-loader.ts","../system/workflows/shared/types.ts","../types/agent.ts","../types/auth.ts","../types/config.ts","../types/delegation.ts","../types/discovery.ts","../types/envelope.ts","../types/escalation-strategy.ts","../types/escalation.ts","../types/events.ts","../types/export.ts","../types/express.d.ts","../types/index.ts","../types/logger.ts","../types/maintenance.ts","../types/mcp.ts","../types/oauth.ts","../types/sdk.ts","../types/startup.ts","../types/task.ts","../types/telemetry.ts","../types/tool-context.ts","../types/user.ts","../types/workflow-set.ts","../types/workflow.ts","../types/yaml-workflow.ts","../workers/index.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../index.ts","../vitest.config.ts","../vitest.integration.config.ts","../adapters/express.ts","../api/agent-subscriptions.ts","../api/agents.ts","../api/auth-sso.ts","../api/auth.ts","../api/bot-accounts.ts","../api/capabilities.ts","../api/controlplane.ts","../api/dba.ts","../api/diagnostics.ts","../api/exports.ts","../api/files.ts","../api/index.ts","../api/insight.ts","../api/knowledge.ts","../api/maintenance.ts","../api/namespaces.ts","../api/overview.ts","../api/pipelines.ts","../api/roles.ts","../api/settings.ts","../api/tasks.ts","../api/topics.ts","../api/users.ts","../api/workflow-sets.ts","../api/escalations/bulk.ts","../api/escalations/cancel.ts","../api/escalations/claim.ts","../api/escalations/create.ts","../api/escalations/helpers.ts","../api/escalations/index.ts","../api/escalations/list.ts","../api/escalations/metadata.ts","../api/escalations/resolve.ts","../api/escalations/single.ts","../api/mcp/index.ts","../api/mcp/servers.ts","../api/mcp/tools.ts","../api/workflows/config.ts","../api/workflows/discovery.ts","../api/workflows/index.ts","../api/workflows/invocation.ts","../api/yaml-workflows/cron.ts","../api/yaml-workflows/crud.ts","../api/yaml-workflows/deploy.ts","../api/yaml-workflows/helpers.ts","../api/yaml-workflows/index.ts","../api/yaml-workflows/versions.ts","../bin/ltc.ts","../lib/cli/auth.ts","../lib/cli/client.ts","../lib/cli/compile.ts","../lib/cli/format.ts","../lib/cli/init.ts","../lib/cli/output.ts","../lib/cli/scanner.ts","../lib/cli/types.ts","../lib/cli/commands/escalations.ts","../lib/cli/commands/knowledge.ts","../lib/cli/commands/mcp.ts","../lib/cli/commands/pipelines.ts","../lib/cli/commands/status.ts","../lib/cli/commands/streams.ts","../lib/cli/commands/users.ts","../lib/cli/commands/workflows.ts","../lib/db/index.ts","../lib/db/migrate.ts","../lib/events/callback.ts","../lib/events/index.ts","../lib/events/matching.ts","../lib/events/memory.ts","../lib/events/nats-ws-proxy.ts","../lib/events/nats.ts","../lib/events/publish.ts","../lib/events/socketio.ts","../lib/events/system-events.ts","../lib/logger/index.ts","../lib/logger/pino.ts","../lib/storage/index.ts","../lib/storage/local.ts","../lib/storage/mime.ts","../lib/storage/s3.ts","../lib/storage/types.ts","../lib/telemetry/honeycomb.ts","../lib/telemetry/index.ts","../modules/auth.ts","../modules/config.ts","../modules/defaults.ts","../modules/ltconfig.ts","../modules/maintenance.ts","../modules/sso.ts","../modules/utils.ts","../routes/agents.ts","../routes/auth-sso.ts","../routes/auth.ts","../routes/bot-accounts.ts","../routes/capabilities.ts","../routes/controlplane.ts","../routes/dba.ts","../routes/delegation.ts","../routes/diagnostics.ts","../routes/docs.ts","../routes/exports.ts","../routes/file-browser.ts","../routes/files.ts","../routes/index.ts","../routes/insight.ts","../routes/knowledge.ts","../routes/maintenance.ts","../routes/mcp-endpoint.ts","../routes/mcp.ts","../routes/namespaces.ts","../routes/nats-credentials.ts","../routes/oauth.ts","../routes/overview.ts","../routes/pipelines.ts","../routes/roles.ts","../routes/settings.ts","../routes/tasks.ts","../routes/topics.ts","../routes/users.ts","../routes/workflow-sets.ts","../routes/escalations/bulk.ts","../routes/escalations/index.ts","../routes/escalations/list.ts","../routes/escalations/metadata.ts","../routes/escalations/resolve.ts","../routes/escalations/single.ts","../routes/workflows/config.ts","../routes/workflows/discovery.ts","../routes/workflows/index.ts","../routes/workflows/invocation.ts","../routes/yaml-workflows/cron.ts","../routes/yaml-workflows/crud.ts","../routes/yaml-workflows/deployment.ts","../routes/yaml-workflows/index.ts","../routes/yaml-workflows/versions.ts","../scripts/process-helpers.ts","../scripts/process.ts","../scripts/token.ts","../sdk/index.ts","../services/dba.ts","../services/hotmesh-utils.ts","../services/workflow-invocation.ts","../services/agent/index.ts","../services/agent/input-mapper.ts","../services/agent/sql.ts","../services/agent/subscription-sql.ts","../services/agent/subscriptions.ts","../services/agent/trigger-registry.ts","../services/auth/bot-api-key.ts","../services/auth/delegation.ts","../services/auth/index.ts","../services/auth/service-token.ts","../services/auth/sql.ts","../services/config/cache.ts","../services/config/index.ts","../services/config/provider.ts","../services/config/read.ts","../services/config/sql.ts","../services/config/write.ts","../services/controlplane/index.ts","../services/controlplane/quorum-bridge.ts","../services/controlplane/sql.ts","../services/controlplane/stream-messages-sql.ts","../services/controlplane/types.ts","../services/cron/index.ts","../services/diagnostics/index.ts","../services/diagnostics/patterns.ts","../services/escalation/bulk.ts","../services/escalation/client.ts","../services/escalation/crud.ts","../services/escalation/index.ts","../services/escalation/map.ts","../services/escalation/queries.ts","../services/escalation/sql.ts","../services/escalation/types.ts","../services/escalation-strategy/default.ts","../services/escalation-strategy/index.ts","../services/escalation-strategy/mcp.ts","../services/export/client.ts","../services/export/index.ts","../services/export/post-process.ts","../services/export/types.ts","../services/iam/activity.ts","../services/iam/bots.ts","../services/iam/context.ts","../services/iam/credentials.ts","../services/iam/envelope.ts","../services/iam/ephemeral.ts","../services/iam/index.ts","../services/iam/principal.ts","../services/iam/resolve.ts","../services/iam/sql.ts","../services/insight/index.ts","../services/insight/prompts.ts","../services/interceptor/activity-interceptor.ts","../services/interceptor/completion.ts","../services/interceptor/context.ts","../services/interceptor/escalation.ts","../services/interceptor/index.ts","../services/interceptor/lifecycle.ts","../services/interceptor/state.ts","../services/interceptor/types.ts","../services/interceptor/activities/config.ts","../services/interceptor/activities/escalation.ts","../services/interceptor/activities/index.ts","../services/interceptor/activities/task.ts","../services/interceptor/activities/workflow.ts","../services/llm/detect.ts","../services/llm/index.ts","../services/llm/translate.ts","../services/llm/types.ts","../services/llm/providers/anthropic.ts","../services/llm/providers/openai.ts","../services/maintenance/index.ts","../services/mcp/adapter.ts","../services/mcp/db.ts","../services/mcp/exposure.ts","../services/mcp/external-server.ts","../services/mcp/index.ts","../services/mcp/register-tool.ts","../services/mcp/seed-service-account.ts","../services/mcp/server-lifecycle.ts","../services/mcp/server-tools.ts","../services/mcp/server.ts","../services/mcp/sql.ts","../services/mcp/types.ts","../services/mcp/workflow-compiler-server.ts","../services/mcp/workflow-server.ts","../services/mcp/client/connection-dispatch.ts","../services/mcp/client/connection-lifecycle.ts","../services/mcp/client/connection-test.ts","../services/mcp/client/connection.ts","../services/mcp/client/index.ts","../services/mcp/client/tools.ts","../services/mcp/db-server/index.ts","../services/mcp/db-server/schemas.ts","../services/mcp/db-server/tools.ts","../services/mcp/playwright-server/index.ts","../services/mcp/playwright-server/lifecycle.ts","../services/mcp/playwright-server/schemas.ts","../services/mcp/playwright-server/tools.ts","../services/namespace/index.ts","../services/namespace/sql.ts","../services/namespace/types.ts","../services/oauth/crypto.ts","../services/oauth/db.ts","../services/oauth/index.ts","../services/oauth/sql.ts","../services/oauth/state.ts","../services/oauth/providers/anthropic.ts","../services/oauth/providers/github.ts","../services/oauth/providers/google.ts","../services/oauth/providers/index.ts","../services/oauth/providers/microsoft.ts","../services/oauth/providers/mock.ts","../services/oauth/providers/registry.ts","../services/oauth/providers/types.ts","../services/orchestrator/condition.ts","../services/orchestrator/index.ts","../services/orchestrator/types.ts","../services/overview/index.ts","../services/overview/sql.ts","../services/pipelines/enrichment.ts","../services/pipelines/events.ts","../services/pipelines/execution-builder.ts","../services/pipelines/index.ts","../services/pipelines/queries.ts","../services/pipelines/sql.ts","../services/pipelines/types.ts","../services/role/index.ts","../services/role/sql.ts","../services/role/types.ts","../services/task/crud.ts","../services/task/index.ts","../services/task/process.ts","../services/task/resolve.ts","../services/task/sql.ts","../services/task/types.ts","../services/topics/index.ts","../services/topics/sql.ts","../services/topics/system-topics.ts","../services/user/auth.ts","../services/user/crud.ts","../services/user/index.ts","../services/user/rbac.ts","../services/user/roles.ts","../services/user/seed-admin.ts","../services/user/sql.ts","../services/user/sso-provision.ts","../services/user/types.ts","../services/workers/registry.ts","../services/workflow-sets/db.ts","../services/workflow-sets/index.ts","../services/workflow-sets/sql.ts","../services/yaml-workflow/builder-regenerate.ts","../services/yaml-workflow/db-utils.ts","../services/yaml-workflow/db-versions.ts","../services/yaml-workflow/db.ts","../services/yaml-workflow/deployer-helpers.ts","../services/yaml-workflow/deployer.ts","../services/yaml-workflow/generator.ts","../services/yaml-workflow/index.ts","../services/yaml-workflow/input-analyzer-helpers.ts","../services/yaml-workflow/input-analyzer.ts","../services/yaml-workflow/invoke.ts","../services/yaml-workflow/pipe-validator.ts","../services/yaml-workflow/sql.ts","../services/yaml-workflow/types.ts","../services/yaml-workflow/durable-compiler/index.ts","../services/yaml-workflow/durable-compiler/parser.ts","../services/yaml-workflow/durable-compiler/prompts.ts","../services/yaml-workflow/durable-compiler/types.ts","../services/yaml-workflow/pattern-detector/array-source.ts","../services/yaml-workflow/pattern-detector/collapse.ts","../services/yaml-workflow/pattern-detector/index.ts","../services/yaml-workflow/pattern-detector/run-detection.ts","../services/yaml-workflow/pattern-detector/types.ts","../services/yaml-workflow/pipeline/analyze.ts","../services/yaml-workflow/pipeline/extract-helpers.ts","../services/yaml-workflow/pipeline/extract.ts","../services/yaml-workflow/pipeline/index.ts","../services/yaml-workflow/pipeline/prompt-templates.ts","../services/yaml-workflow/pipeline/prompts.ts","../services/yaml-workflow/pipeline/validate.ts","../services/yaml-workflow/pipeline/build/dag-assembly.ts","../services/yaml-workflow/pipeline/build/dag.ts","../services/yaml-workflow/pipeline/build/index.ts","../services/yaml-workflow/pipeline/build/iteration.ts","../services/yaml-workflow/pipeline/build/metadata.ts","../services/yaml-workflow/pipeline/build/transform.ts","../services/yaml-workflow/pipeline/build/utils.ts","../services/yaml-workflow/pipeline/build/wiring.ts","../services/yaml-workflow/pipeline/compile/index.ts","../services/yaml-workflow/pipeline/compile/llm-call.ts","../services/yaml-workflow/pipeline/compile/parse-plan.ts","../services/yaml-workflow/pipeline/compile/summarize.ts","../services/yaml-workflow/workers/callbacks.ts","../services/yaml-workflow/workers/events.ts","../services/yaml-workflow/workers/index.ts","../services/yaml-workflow/workers/register.ts","../services/yaml-workflow/workers/scope.ts","../start/adapters.ts","../start/config.ts","../start/graph-workflows.ts","../start/index.ts","../start/server.ts","../start/socket-auth.ts","../start/workers.ts","../system/index.ts","../system/activities/claude-code.ts","../system/activities/file-storage.ts","../system/activities/http.ts","../system/activities/knowledge.ts","../system/activities/oauth.ts","../system/activities/schema-exchange.ts","../system/activities/sql.ts","../system/activities/triage/cache.ts","../system/activities/triage/context.ts","../system/activities/triage/discovery.ts","../system/activities/triage/index.ts","../system/activities/triage/llm.ts","../system/activities/triage/tools.ts","../system/mcp-servers/claude-code.ts","../system/mcp-servers/docs.ts","../system/mcp-servers/events.ts","../system/mcp-servers/file-storage.ts","../system/mcp-servers/http-fetch.ts","../system/mcp-servers/human-queue-schemas.ts","../system/mcp-servers/human-queue.ts","../system/mcp-servers/knowledge.ts","../system/mcp-servers/oauth.ts","../system/mcp-servers/schema-exchange.ts","../system/mcp-servers/translation.ts","../system/mcp-servers/vision-prompts.ts","../system/mcp-servers/vision.ts","../system/mcp-servers/workflow-compiler.ts","../system/mcp-servers/workflow.ts","../system/mcp-servers/admin/agent-subscriptions.ts","../system/mcp-servers/admin/agents.ts","../system/mcp-servers/admin/bot-accounts.ts","../system/mcp-servers/admin/controlplane.ts","../system/mcp-servers/admin/diagnostics.ts","../system/mcp-servers/admin/escalations.ts","../system/mcp-servers/admin/exports.ts","../system/mcp-servers/admin/index.ts","../system/mcp-servers/admin/maintenance.ts","../system/mcp-servers/admin/mcp-servers.ts","../system/mcp-servers/admin/overview.ts","../system/mcp-servers/admin/pipelines.ts","../system/mcp-servers/admin/schemas.ts","../system/mcp-servers/admin/settings.ts","../system/mcp-servers/admin/tasks.ts","../system/mcp-servers/admin/topics.ts","../system/mcp-servers/admin/users.ts","../system/mcp-servers/admin/workflow-config.ts","../system/mcp-servers/admin/workflows.ts","../system/mcp-servers/admin/yaml-workflows.ts","../system/mcp-servers/db-query/index.ts","../system/mcp-servers/db-query/schemas.ts","../system/mcp-servers/db-query/tools.ts","../system/seed/index.ts","../system/seed/tool-manifests-admin.ts","../system/seed/tool-manifests-data.ts","../system/seed/tool-manifests-escalation.ts","../system/seed/tool-manifests-events.ts","../system/seed/tool-manifests-knowledge.ts","../system/seed/tool-manifests-workflows.ts","../system/workflows/tool-result-guard.ts","../system/workflows/capability-invoke/activities.ts","../system/workflows/capability-invoke/index.ts","../system/workflows/mcp-deterministic/activities.ts","../system/workflows/mcp-deterministic/index.ts","../system/workflows/mcp-query/index.ts","../system/workflows/mcp-query/prompts.ts","../system/workflows/mcp-query/strategy-advisors.ts","../system/workflows/mcp-query/types.ts","../system/workflows/mcp-query/activities/caches.ts","../system/workflows/mcp-query/activities/discovery.ts","../system/workflows/mcp-query/activities/index.ts","../system/workflows/mcp-query/activities/llm.ts","../system/workflows/mcp-query/activities/tool-executor.ts","../system/workflows/mcp-query/activities/tool-loader.ts","../system/workflows/mcp-query-router/activities.ts","../system/workflows/mcp-query-router/index.ts","../system/workflows/mcp-query-router/prompts.ts","../system/workflows/mcp-triage/activities-proxy.ts","../system/workflows/mcp-triage/index.ts","../system/workflows/mcp-triage/prompts.ts","../system/workflows/mcp-triage/response-builders.ts","../system/workflows/mcp-triage/response.ts","../system/workflows/mcp-triage/types.ts","../system/workflows/mcp-triage-deterministic/activities.ts","../system/workflows/mcp-triage-deterministic/index.ts","../system/workflows/mcp-triage-router/activities.ts","../system/workflows/mcp-triage-router/index.ts","../system/workflows/mcp-workflow-builder/index.ts","../system/workflows/mcp-workflow-builder/prompts.ts","../system/workflows/mcp-workflow-builder/activities/caches.ts","../system/workflows/mcp-workflow-builder/activities/index.ts","../system/workflows/mcp-workflow-builder/activities/llm.ts","../system/workflows/mcp-workflow-builder/activities/tool-loader.ts","../system/workflows/mcp-workflow-planner/index.ts","../system/workflows/mcp-workflow-planner/prompts.ts","../system/workflows/mcp-workflow-planner/activities/analyze.ts","../system/workflows/mcp-workflow-planner/activities/index.ts","../system/workflows/mcp-workflow-planner/activities/persist.ts","../system/workflows/mcp-workflow-planner/activities/plan.ts","../system/workflows/shared/discovery.ts","../system/workflows/shared/index.ts","../system/workflows/shared/llm-caller.ts","../system/workflows/shared/prompts.ts","../system/workflows/shared/strategy-advisors.ts","../system/workflows/shared/tool-executor.ts","../system/workflows/shared/tool-loader.ts","../system/workflows/shared/types.ts","../types/agent.ts","../types/auth.ts","../types/config.ts","../types/delegation.ts","../types/discovery.ts","../types/envelope.ts","../types/escalation-strategy.ts","../types/escalation.ts","../types/events.ts","../types/export.ts","../types/express.d.ts","../types/index.ts","../types/logger.ts","../types/maintenance.ts","../types/mcp.ts","../types/oauth.ts","../types/sdk.ts","../types/startup.ts","../types/task.ts","../types/telemetry.ts","../types/tool-context.ts","../types/user.ts","../types/workflow-set.ts","../types/workflow.ts","../types/yaml-workflow.ts","../workers/index.ts"],"version":"5.9.3"}
|
package/docs/hitl-guide.md
CHANGED
|
@@ -594,6 +594,54 @@ metadata: {
|
|
|
594
594
|
|
|
595
595
|
---
|
|
596
596
|
|
|
597
|
+
## Resolving from System Code
|
|
598
|
+
|
|
599
|
+
When a backend service (not the dashboard UI) needs to resolve an escalation — for example, an ingress handler that receives a webhook or processes a domain event — use the escalation SDK methods directly.
|
|
600
|
+
|
|
601
|
+
### By escalation ID
|
|
602
|
+
|
|
603
|
+
Use when you already have the escalation UUID (e.g. stored in your own DB alongside the order):
|
|
604
|
+
|
|
605
|
+
```typescript
|
|
606
|
+
const result = await lt.escalations.resolve({
|
|
607
|
+
id: escalationId,
|
|
608
|
+
resolverPayload: { approved: true, targetStatus: 'ready' },
|
|
609
|
+
});
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
This routes through the full resolution path and works for all escalation types — atomic `conditionLT` (signal_key), legacy `conditionLT` (signal_id), and re-run-style escalations.
|
|
613
|
+
|
|
614
|
+
### By metadata key-value pair
|
|
615
|
+
|
|
616
|
+
Use when you know a domain identifier (e.g. `orderId`) but not the escalation UUID. `resolveByMetadata` finds the highest-priority pending escalation matching the key-value pair and resolves it atomically — no pre-flight lookup, no TOCTOU:
|
|
617
|
+
|
|
618
|
+
```typescript
|
|
619
|
+
const result = await lt.escalations.resolveByMetadata({
|
|
620
|
+
key: 'orderId',
|
|
621
|
+
value: orderId,
|
|
622
|
+
resolverPayload: { approved: true, targetStatus: 'ready' },
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
if (result.status === 404) {
|
|
626
|
+
// No pending escalation for this orderId
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
This works for all escalation types including atomic `conditionLT` rows (those with `signal_key` set). The routing is transparent — the caller does not need to know which pattern the workflow used.
|
|
631
|
+
|
|
632
|
+
### By signal key
|
|
633
|
+
|
|
634
|
+
When the signal key is deterministic and known to the caller (e.g. `station-done-${workflowId}`), use the direct signal-key path to skip the metadata lookup:
|
|
635
|
+
|
|
636
|
+
```typescript
|
|
637
|
+
await lt.escalations.resolveBySignalKey({
|
|
638
|
+
signalKey: `station-done-${workflowId}`,
|
|
639
|
+
resolverPayload: { approved: true },
|
|
640
|
+
});
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
---
|
|
644
|
+
|
|
597
645
|
## Cancelling Escalations
|
|
598
646
|
|
|
599
647
|
Escalations can be cancelled at any point before they are resolved. Cancellation is terminal — a cancelled escalation cannot be re-opened.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotmeshio/long-tail",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.10",
|
|
4
4
|
"description": "Long Tail Workflows — Durable AI workflows with human-in-the-loop escalation. Powered by PostgreSQL.",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -98,6 +98,7 @@
|
|
|
98
98
|
"@playwright/test": "^1.58.2",
|
|
99
99
|
"@types/bcryptjs": "^2.4.6",
|
|
100
100
|
"@types/express": "^5.0.0",
|
|
101
|
+
"@types/js-yaml": "^4.0.9",
|
|
101
102
|
"@types/jsonwebtoken": "^9.0.10",
|
|
102
103
|
"@types/node": "^20.19.33",
|
|
103
104
|
"@types/pg": "^8.10.0",
|