bpmnlint-plugin-camunda-compat 2.59.2 → 2.60.1
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/package.json
CHANGED
|
@@ -15,10 +15,12 @@ const { annotateRule } = require('../helper');
|
|
|
15
15
|
* or a part like `toolCallResult.statusCode`), a script task or called decision
|
|
16
16
|
* `resultVariable`, or a connector `resultVariable`/`resultExpression` header.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* `toolCallResult`
|
|
21
|
-
*
|
|
18
|
+
* A wrong-casing near-miss (`toolcallresult`, `TOOLCALLRESULT`, ...) is always
|
|
19
|
+
* reported on the element that wrote it, even when a sibling write already
|
|
20
|
+
* sets `toolCallResult` correctly — case-sensitive engines never read it.
|
|
21
|
+
* Other miswrites are reported individually per row only while no valid
|
|
22
|
+
* `toolCallResult` exists. When the flow writes none at all, the rule reports
|
|
23
|
+
* once on the entry activity, since there's no single offending element to
|
|
22
24
|
* point to (the agent gets no completion signal and may retry or hallucinate
|
|
23
25
|
* an outcome). Results written from arbitrary FEEL expressions are not
|
|
24
26
|
* statically detectable.
|
|
@@ -47,27 +49,39 @@ module.exports = skipInNonExecutableProcess(function(config = {}) {
|
|
|
47
49
|
|
|
48
50
|
const hasResult = channels.some(isToolCallResultChannel);
|
|
49
51
|
if (!hasResult) {
|
|
50
|
-
const casingMismatch = channels.find(isToolCallResultCasingMismatch);
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
// Every channel here is a miswrite (none matched toolCallResult), so
|
|
54
|
+
// each is reported on the element that wrote it. A wrong-casing
|
|
55
|
+
// near-miss gets the more specific guidance.
|
|
56
|
+
for (const channel of channels) {
|
|
57
|
+
if (isToolCallResultCasingMismatch(channel)) {
|
|
58
|
+
reportErrors(channel.element, reporter, {
|
|
59
|
+
message: `Wrong casing "${getCasingMismatchText(channel)}": use toolCallResult (case-sensitive).`,
|
|
60
|
+
data: { type: ERROR_TYPES.AGENT_TOOL_OUTPUT_KEY_CASING_INVALID },
|
|
61
|
+
path: getChannelPath(channel),
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
reportErrors(channel.element, reporter, {
|
|
65
|
+
message: '"toolCallResult" output is not mapped.',
|
|
66
|
+
data: { type: ERROR_TYPES.AGENT_TOOL_OUTPUT_KEY_INVALID },
|
|
67
|
+
path: getChannelPath(channel),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Miscased near-misses are reported even when a sibling already sets
|
|
75
|
+
// toolCallResult correctly. Casing variants never satisfy
|
|
76
|
+
// isToolCallResultChannel, so the overwrite check below ignores them.
|
|
77
|
+
for (const channel of channels) {
|
|
78
|
+
if (isToolCallResultCasingMismatch(channel)) {
|
|
79
|
+
reportErrors(channel.element, reporter, {
|
|
80
|
+
message: `Wrong casing "${getCasingMismatchText(channel)}": use toolCallResult (case-sensitive).`,
|
|
55
81
|
data: { type: ERROR_TYPES.AGENT_TOOL_OUTPUT_KEY_CASING_INVALID },
|
|
56
|
-
path: getChannelPath(
|
|
82
|
+
path: getChannelPath(channel),
|
|
57
83
|
});
|
|
58
|
-
return;
|
|
59
84
|
}
|
|
60
|
-
|
|
61
|
-
// Every channel here is a miswrite (none matched toolCallResult), so
|
|
62
|
-
// the first one is a real misdirected write; report on the element
|
|
63
|
-
// that actually wrote it, not the tool's entry.
|
|
64
|
-
const misdirected = channels[ 0 ];
|
|
65
|
-
reportErrors(misdirected.element, reporter, {
|
|
66
|
-
message: '"toolCallResult" output is not mapped.',
|
|
67
|
-
data: { type: ERROR_TYPES.AGENT_TOOL_OUTPUT_KEY_INVALID },
|
|
68
|
-
path: getChannelPath(misdirected),
|
|
69
|
-
});
|
|
70
|
-
return;
|
|
71
85
|
}
|
|
72
86
|
|
|
73
87
|
// toolCallResult is set somewhere, but assigning it more than once
|
|
@@ -267,7 +281,7 @@ function isToolCallResultChannel({ kind, value }) {
|
|
|
267
281
|
// case-insensitive name matching for fromAi().
|
|
268
282
|
function isToolCallResultCasingMismatch({ kind, value }) {
|
|
269
283
|
if (kind === 'resultExpression') {
|
|
270
|
-
return
|
|
284
|
+
return !!getResultExpressionCasingMismatch(value);
|
|
271
285
|
}
|
|
272
286
|
|
|
273
287
|
const lower = value.toLowerCase();
|
|
@@ -276,13 +290,21 @@ function isToolCallResultCasingMismatch({ kind, value }) {
|
|
|
276
290
|
return isCasingVariant && !isToolCallResultChannel({ kind, value });
|
|
277
291
|
}
|
|
278
292
|
|
|
293
|
+
// The first miscased toolCallResult token in a FEEL expression, or null when
|
|
294
|
+
// every occurrence is cased correctly. Tokens are judged individually, so a
|
|
295
|
+
// correct occurrence never excuses a miscased sibling in the same expression.
|
|
296
|
+
function getResultExpressionCasingMismatch(value) {
|
|
297
|
+
const tokens = value.match(/\btoolcallresult\b/gi) || [];
|
|
298
|
+
|
|
299
|
+
return tokens.find(token => token !== 'toolCallResult') || null;
|
|
300
|
+
}
|
|
301
|
+
|
|
279
302
|
// The mismatched word itself, not the whole expression, for resultExpression
|
|
280
303
|
// channels (a connector header value is a full FEEL expression, e.g.
|
|
281
304
|
// `={toolcallresult: response.body}`).
|
|
282
305
|
function getCasingMismatchText({ kind, value }) {
|
|
283
306
|
if (kind === 'resultExpression') {
|
|
284
|
-
|
|
285
|
-
return match ? match[0] : value;
|
|
307
|
+
return getResultExpressionCasingMismatch(value) || value;
|
|
286
308
|
}
|
|
287
309
|
|
|
288
310
|
return value;
|