@syrin/cli 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/dist/cli/commands/config.d.ts +47 -0
- package/dist/cli/commands/config.js +360 -0
- package/dist/cli/commands/dev.d.ts +6 -0
- package/dist/cli/commands/dev.js +67 -15
- package/dist/cli/commands/doctor.js +49 -13
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.js +89 -18
- package/dist/cli/commands/status.d.ts +10 -0
- package/dist/cli/commands/status.js +162 -0
- package/dist/cli/index.js +211 -12
- package/dist/cli/prompts/init-prompt.d.ts +18 -0
- package/dist/cli/prompts/init-prompt.js +159 -99
- package/dist/cli/utils/command-error-handler.js +2 -5
- package/dist/config/env-checker.d.ts +12 -2
- package/dist/config/env-checker.js +88 -38
- package/dist/config/env-templates.d.ts +15 -0
- package/dist/config/env-templates.js +49 -0
- package/dist/config/generator.js +17 -0
- package/dist/config/global-loader.d.ts +50 -0
- package/dist/config/global-loader.js +244 -0
- package/dist/config/loader.d.ts +28 -0
- package/dist/config/loader.js +95 -9
- package/dist/config/merger.d.ts +37 -0
- package/dist/config/merger.js +68 -0
- package/dist/config/schema.d.ts +26 -1
- package/dist/config/schema.js +73 -8
- package/dist/config/types.d.ts +19 -0
- package/dist/config/types.js +26 -1
- package/dist/constants/messages.d.ts +7 -0
- package/dist/constants/messages.js +8 -0
- package/dist/constants/paths.d.ts +6 -0
- package/dist/constants/paths.js +10 -0
- package/dist/events/emitter.js +7 -7
- package/dist/index.js +0 -0
- package/dist/presentation/config-ui.d.ts +34 -0
- package/dist/presentation/config-ui.js +139 -0
- package/dist/presentation/doctor-ui.d.ts +11 -0
- package/dist/presentation/doctor-ui.js +52 -1
- package/dist/presentation/init-ui.d.ts +9 -0
- package/dist/presentation/init-ui.js +33 -0
- package/dist/runtime/analysis/analyser.js +2 -2
- package/dist/runtime/analysis/rules/warnings/w104-generic-description.d.ts +1 -1
- package/dist/runtime/analysis/rules/warnings/w104-generic-description.js +1 -1
- package/dist/runtime/dev/event-mapper.js +19 -3
- package/dist/runtime/dev/session.d.ts +4 -0
- package/dist/runtime/dev/session.js +52 -3
- package/dist/runtime/llm/ollama.js +4 -4
- package/dist/runtime/mcp/client/manager.js +3 -3
- package/dist/runtime/sandbox/executor.js +5 -5
- package/dist/runtime/test/orchestrator.js +4 -4
- package/dist/utils/editor.d.ts +37 -0
- package/dist/utils/editor.js +137 -0
- package/dist/utils/logger.d.ts +24 -6
- package/dist/utils/logger.js +51 -8
- package/package.json +1 -1
- package/dist/runtime/analysis/rules/errors/e001-missing-output-schema.d.ts +0 -22
- package/dist/runtime/analysis/rules/errors/e001-missing-output-schema.js +0 -30
- package/dist/runtime/analysis/rules/errors/e002-underspecified-input.d.ts +0 -24
- package/dist/runtime/analysis/rules/errors/e002-underspecified-input.js +0 -52
- package/dist/runtime/analysis/rules/errors/e003-type-mismatch.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e003-type-mismatch.js +0 -73
- package/dist/runtime/analysis/rules/errors/e004-free-text-propagation.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e004-free-text-propagation.js +0 -47
- package/dist/runtime/analysis/rules/errors/e005-tool-ambiguity.d.ts +0 -25
- package/dist/runtime/analysis/rules/errors/e005-tool-ambiguity.js +0 -73
- package/dist/runtime/analysis/rules/errors/e006-param-not-in-description.d.ts +0 -22
- package/dist/runtime/analysis/rules/errors/e006-param-not-in-description.js +0 -57
- package/dist/runtime/analysis/rules/errors/e007-output-not-guaranteed.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e007-output-not-guaranteed.js +0 -56
- package/dist/runtime/analysis/rules/errors/e008-circular-dependency.d.ts +0 -22
- package/dist/runtime/analysis/rules/errors/e008-circular-dependency.js +0 -84
- package/dist/runtime/analysis/rules/errors/e009-implicit-user-input.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e009-implicit-user-input.js +0 -89
- package/dist/runtime/analysis/rules/errors/e010-non-serializable.d.ts +0 -25
- package/dist/runtime/analysis/rules/errors/e010-non-serializable.js +0 -46
- package/dist/runtime/analysis/rules/errors/e011-missing-tool-description.d.ts +0 -24
- package/dist/runtime/analysis/rules/errors/e011-missing-tool-description.js +0 -33
- package/dist/runtime/analysis/rules/errors/e012-side-effect-detected.d.ts +0 -39
- package/dist/runtime/analysis/rules/errors/e012-side-effect-detected.js +0 -40
- package/dist/runtime/analysis/rules/errors/e013-non-deterministic-output.d.ts +0 -37
- package/dist/runtime/analysis/rules/errors/e013-non-deterministic-output.js +0 -34
- package/dist/runtime/analysis/rules/errors/e013-output-explosion.d.ts +0 -39
- package/dist/runtime/analysis/rules/errors/e013-output-explosion.js +0 -36
- package/dist/runtime/analysis/rules/errors/e014-hidden-dependency.d.ts +0 -42
- package/dist/runtime/analysis/rules/errors/e014-hidden-dependency.js +0 -46
- package/dist/runtime/analysis/rules/errors/e014-output-explosion.d.ts +0 -39
- package/dist/runtime/analysis/rules/errors/e014-output-explosion.js +0 -36
- package/dist/runtime/analysis/rules/errors/e015-hidden-dependency.d.ts +0 -42
- package/dist/runtime/analysis/rules/errors/e015-hidden-dependency.js +0 -46
- package/dist/runtime/analysis/rules/errors/e015-unbounded-execution.d.ts +0 -44
- package/dist/runtime/analysis/rules/errors/e015-unbounded-execution.js +0 -66
- package/dist/runtime/analysis/rules/errors/e016-output-validation-failed.d.ts +0 -43
- package/dist/runtime/analysis/rules/errors/e016-output-validation-failed.js +0 -42
- package/dist/runtime/analysis/rules/errors/e016-unbounded-execution.d.ts +0 -44
- package/dist/runtime/analysis/rules/errors/e016-unbounded-execution.js +0 -66
- package/dist/runtime/analysis/rules/errors/e017-input-validation-failed.d.ts +0 -57
- package/dist/runtime/analysis/rules/errors/e017-input-validation-failed.js +0 -80
- package/dist/runtime/analysis/rules/errors/e017-output-validation-failed.d.ts +0 -43
- package/dist/runtime/analysis/rules/errors/e017-output-validation-failed.js +0 -42
- package/dist/runtime/analysis/rules/errors/e018-input-validation-failed.d.ts +0 -57
- package/dist/runtime/analysis/rules/errors/e018-input-validation-failed.js +0 -80
- package/dist/runtime/analysis/rules/errors/e018-tool-execution-failed.d.ts +0 -38
- package/dist/runtime/analysis/rules/errors/e018-tool-execution-failed.js +0 -37
- package/dist/runtime/analysis/rules/errors/e019-tool-execution-failed.d.ts +0 -38
- package/dist/runtime/analysis/rules/errors/e019-tool-execution-failed.js +0 -37
- package/dist/runtime/analysis/rules/errors/e019-unexpected-test-result.d.ts +0 -65
- package/dist/runtime/analysis/rules/errors/e019-unexpected-test-result.js +0 -109
- package/dist/runtime/analysis/rules/errors/e020-unexpected-test-result.d.ts +0 -65
- package/dist/runtime/analysis/rules/errors/e020-unexpected-test-result.js +0 -109
- package/dist/runtime/analysis/rules/warnings/w001-implicit-dependency.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w001-implicit-dependency.js +0 -39
- package/dist/runtime/analysis/rules/warnings/w002-free-text-without-normalization.d.ts +0 -24
- package/dist/runtime/analysis/rules/warnings/w002-free-text-without-normalization.js +0 -40
- package/dist/runtime/analysis/rules/warnings/w003-missing-examples.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w003-missing-examples.js +0 -84
- package/dist/runtime/analysis/rules/warnings/w004-overloaded-responsibility.d.ts +0 -23
- package/dist/runtime/analysis/rules/warnings/w004-overloaded-responsibility.js +0 -96
- package/dist/runtime/analysis/rules/warnings/w005-generic-description.d.ts +0 -53
- package/dist/runtime/analysis/rules/warnings/w005-generic-description.js +0 -108
- package/dist/runtime/analysis/rules/warnings/w006-optional-as-required.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w006-optional-as-required.js +0 -44
- package/dist/runtime/analysis/rules/warnings/w007-broad-output-schema.d.ts +0 -23
- package/dist/runtime/analysis/rules/warnings/w007-broad-output-schema.js +0 -37
- package/dist/runtime/analysis/rules/warnings/w008-multiple-entry-points.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w008-multiple-entry-points.js +0 -97
- package/dist/runtime/analysis/rules/warnings/w009-hidden-side-effects.d.ts +0 -23
- package/dist/runtime/analysis/rules/warnings/w009-hidden-side-effects.js +0 -88
- package/dist/runtime/analysis/rules/warnings/w010-output-not-reusable.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w010-output-not-reusable.js +0 -81
- package/dist/runtime/analysis/rules/warnings/w021-weak-schema.d.ts +0 -40
- package/dist/runtime/analysis/rules/warnings/w021-weak-schema.js +0 -32
- package/dist/runtime/analysis/rules/warnings/w022-high-entropy-output.d.ts +0 -39
- package/dist/runtime/analysis/rules/warnings/w022-high-entropy-output.js +0 -36
- package/dist/runtime/analysis/rules/warnings/w023-unstable-defaults.d.ts +0 -38
- package/dist/runtime/analysis/rules/warnings/w023-unstable-defaults.js +0 -36
- package/dist/runtime/test/dependency-tracker.d.ts +0 -66
- package/dist/runtime/test/dependency-tracker.js +0 -80
- package/dist/runtime/test/formatters.d.ts +0 -18
- package/dist/runtime/test/formatters.js +0 -172
- package/dist/runtime/test/input-generator.d.ts +0 -33
- package/dist/runtime/test/input-generator.js +0 -498
- package/dist/runtime/test/mcp-root-detector.d.ts +0 -31
- package/dist/runtime/test/mcp-root-detector.js +0 -105
- package/dist/runtime/test/retry-tester.d.ts +0 -44
- package/dist/runtime/test/retry-tester.js +0 -103
- package/dist/runtime/test/synthetic-input-generator.d.ts +0 -11
- package/dist/runtime/test/synthetic-input-generator.js +0 -154
- package/dist/runtime/test/test-runner.d.ts +0 -28
- package/dist/runtime/test/test-runner.js +0 -55
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E005: Hard Tool Ambiguity
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Two or more tools:
|
|
6
|
-
* - Overlapping descriptions
|
|
7
|
-
* - Overlapping schemas
|
|
8
|
-
* - No clear differentiator
|
|
9
|
-
*
|
|
10
|
-
* Why:
|
|
11
|
-
* - Tool selection becomes nondeterministic
|
|
12
|
-
* - Agent behavior changes across runs/models
|
|
13
|
-
*/
|
|
14
|
-
import { BaseRule } from '../base.js';
|
|
15
|
-
import type { AnalysisContext, Diagnostic } from '../../types.js';
|
|
16
|
-
declare class E005ToolAmbiguityRule extends BaseRule {
|
|
17
|
-
readonly id: "E005";
|
|
18
|
-
readonly severity: "error";
|
|
19
|
-
readonly ruleName = "Hard Tool Ambiguity";
|
|
20
|
-
readonly description = "Multiple tools match the same intent. LLM tool selection is ambiguous.";
|
|
21
|
-
check(ctx: AnalysisContext): Diagnostic[];
|
|
22
|
-
}
|
|
23
|
-
export declare const E005ToolAmbiguity: E005ToolAmbiguityRule;
|
|
24
|
-
export {};
|
|
25
|
-
//# sourceMappingURL=e005-tool-ambiguity.d.ts.map
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E005: Hard Tool Ambiguity
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Two or more tools:
|
|
6
|
-
* - Overlapping descriptions
|
|
7
|
-
* - Overlapping schemas
|
|
8
|
-
* - No clear differentiator
|
|
9
|
-
*
|
|
10
|
-
* Why:
|
|
11
|
-
* - Tool selection becomes nondeterministic
|
|
12
|
-
* - Agent behavior changes across runs/models
|
|
13
|
-
*/
|
|
14
|
-
import { BaseRule } from '../base.js';
|
|
15
|
-
import { ERROR_CODES } from '../error-codes.js';
|
|
16
|
-
/**
|
|
17
|
-
* Calculate similarity between two sets of tokens.
|
|
18
|
-
*/
|
|
19
|
-
function tokenSimilarity(tokens1, tokens2) {
|
|
20
|
-
if (tokens1.size === 0 || tokens2.size === 0) {
|
|
21
|
-
return 0.0;
|
|
22
|
-
}
|
|
23
|
-
const intersection = new Set([...tokens1].filter(t => tokens2.has(t)));
|
|
24
|
-
const union = new Set([...tokens1, ...tokens2]);
|
|
25
|
-
return intersection.size / union.size;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Calculate schema overlap between two tools.
|
|
29
|
-
*/
|
|
30
|
-
function schemaOverlap(inputs1, inputs2) {
|
|
31
|
-
const names1 = new Set(inputs1.map(i => i.name.toLowerCase()));
|
|
32
|
-
const names2 = new Set(inputs2.map(i => i.name.toLowerCase()));
|
|
33
|
-
const nameOverlapSet = new Set([...names1].filter(n => names2.has(n)));
|
|
34
|
-
const nameOverlap = nameOverlapSet.size;
|
|
35
|
-
const totalNames = new Set([...names1, ...names2]).size;
|
|
36
|
-
if (totalNames === 0) {
|
|
37
|
-
return 0.0;
|
|
38
|
-
}
|
|
39
|
-
return nameOverlap / totalNames;
|
|
40
|
-
}
|
|
41
|
-
class E005ToolAmbiguityRule extends BaseRule {
|
|
42
|
-
id = ERROR_CODES.E005;
|
|
43
|
-
severity = 'error';
|
|
44
|
-
ruleName = 'Hard Tool Ambiguity';
|
|
45
|
-
description = 'Multiple tools match the same intent. LLM tool selection is ambiguous.';
|
|
46
|
-
check(ctx) {
|
|
47
|
-
const diagnostics = [];
|
|
48
|
-
for (let i = 0; i < ctx.tools.length; i++) {
|
|
49
|
-
const tool1 = ctx.tools[i];
|
|
50
|
-
if (!tool1)
|
|
51
|
-
continue;
|
|
52
|
-
for (let j = i + 1; j < ctx.tools.length; j++) {
|
|
53
|
-
const tool2 = ctx.tools[j];
|
|
54
|
-
if (!tool2)
|
|
55
|
-
continue;
|
|
56
|
-
// Calculate description similarity
|
|
57
|
-
const descSimilarity = tokenSimilarity(tool1.descriptionTokens, tool2.descriptionTokens);
|
|
58
|
-
// Calculate schema overlap
|
|
59
|
-
const inputOverlap = schemaOverlap(tool1.inputs, tool2.inputs);
|
|
60
|
-
const outputOverlap = schemaOverlap(tool1.outputs, tool2.outputs);
|
|
61
|
-
const schemaOverlapScore = (inputOverlap + outputOverlap) / 2;
|
|
62
|
-
// If both description and schema are very similar, it's ambiguous
|
|
63
|
-
// Threshold: >0.7 description similarity AND >0.5 schema overlap
|
|
64
|
-
if (descSimilarity > 0.7 && schemaOverlapScore > 0.5) {
|
|
65
|
-
diagnostics.push(this.createDiagnostic(`Multiple tools match the same intent: "${tool1.name}", "${tool2.name}". LLM tool selection is ambiguous.`, undefined, undefined, `Differentiate "${tool1.name}" and "${tool2.name}" by making their descriptions and schemas more distinct.`));
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return diagnostics;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
export const E005ToolAmbiguity = new E005ToolAmbiguityRule();
|
|
73
|
-
//# sourceMappingURL=e005-tool-ambiguity.js.map
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E006: Required Input Not Mentioned in Description
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Required input exists
|
|
6
|
-
* - Tool description does not reference it directly or indirectly
|
|
7
|
-
*
|
|
8
|
-
* Why:
|
|
9
|
-
* - LLM does not know parameter exists or matters
|
|
10
|
-
*/
|
|
11
|
-
import { BaseRule } from '../base.js';
|
|
12
|
-
import type { AnalysisContext, Diagnostic } from '../../types.js';
|
|
13
|
-
declare class E006ParamNotInDescriptionRule extends BaseRule {
|
|
14
|
-
readonly id: "E006";
|
|
15
|
-
readonly severity: "error";
|
|
16
|
-
readonly ruleName = "Required Input Not Mentioned in Description";
|
|
17
|
-
readonly description = "Required parameter is not referenced in tool description. LLM may not know parameter exists.";
|
|
18
|
-
check(ctx: AnalysisContext): Diagnostic[];
|
|
19
|
-
}
|
|
20
|
-
export declare const E006ParamNotInDescription: E006ParamNotInDescriptionRule;
|
|
21
|
-
export {};
|
|
22
|
-
//# sourceMappingURL=e006-param-not-in-description.d.ts.map
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E006: Required Input Not Mentioned in Description
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Required input exists
|
|
6
|
-
* - Tool description does not reference it directly or indirectly
|
|
7
|
-
*
|
|
8
|
-
* Why:
|
|
9
|
-
* - LLM does not know parameter exists or matters
|
|
10
|
-
*/
|
|
11
|
-
import { BaseRule } from '../base.js';
|
|
12
|
-
import { ERROR_CODES } from '../error-codes.js';
|
|
13
|
-
/**
|
|
14
|
-
* Check if a parameter name appears in the description (case-insensitive).
|
|
15
|
-
*/
|
|
16
|
-
function isParameterMentioned(description, paramName) {
|
|
17
|
-
const descLower = description.toLowerCase();
|
|
18
|
-
const paramLower = paramName.toLowerCase();
|
|
19
|
-
// Exact match
|
|
20
|
-
if (descLower.includes(paramLower)) {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
// Check if words from param name appear in description
|
|
24
|
-
const paramWords = paramLower.split(/\W+/).filter(w => w.length > 2);
|
|
25
|
-
const descWords = descLower.split(/\W+/);
|
|
26
|
-
for (const word of paramWords) {
|
|
27
|
-
if (descWords.includes(word)) {
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
class E006ParamNotInDescriptionRule extends BaseRule {
|
|
34
|
-
id = ERROR_CODES.E006;
|
|
35
|
-
severity = 'error';
|
|
36
|
-
ruleName = 'Required Input Not Mentioned in Description';
|
|
37
|
-
description = 'Required parameter is not referenced in tool description. LLM may not know parameter exists.';
|
|
38
|
-
check(ctx) {
|
|
39
|
-
const diagnostics = [];
|
|
40
|
-
for (const tool of ctx.tools) {
|
|
41
|
-
const description = tool.description || '';
|
|
42
|
-
for (const input of tool.inputs) {
|
|
43
|
-
// Only check required inputs
|
|
44
|
-
if (!input.required) {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
// Check if parameter is mentioned in description
|
|
48
|
-
if (!isParameterMentioned(description, input.name)) {
|
|
49
|
-
diagnostics.push(this.createDiagnostic(`Required parameter "${input.name}" is not referenced in "${tool.name}" description.`, tool.name, input.name, `Mention "${input.name}" in the tool description so the LLM knows it exists and is required.`));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return diagnostics;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
export const E006ParamNotInDescription = new E006ParamNotInDescriptionRule();
|
|
57
|
-
//# sourceMappingURL=e006-param-not-in-description.js.map
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E007: Output Used Downstream but Not Guaranteed
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Tool output is optional / nullable
|
|
6
|
-
* - Used downstream without fallback
|
|
7
|
-
*
|
|
8
|
-
* Why:
|
|
9
|
-
* - Silent null propagation
|
|
10
|
-
* - Hard-to-debug failures
|
|
11
|
-
*/
|
|
12
|
-
import { BaseRule } from '../base.js';
|
|
13
|
-
import type { AnalysisContext, Diagnostic } from '../../types.js';
|
|
14
|
-
declare class E007OutputNotGuaranteedRule extends BaseRule {
|
|
15
|
-
readonly id: "E007";
|
|
16
|
-
readonly severity: "error";
|
|
17
|
-
readonly ruleName = "Output Used Downstream but Not Guaranteed";
|
|
18
|
-
readonly description = "Output of tool is not guaranteed, but is used by downstream tools without fallback.";
|
|
19
|
-
check(ctx: AnalysisContext): Diagnostic[];
|
|
20
|
-
}
|
|
21
|
-
export declare const E007OutputNotGuaranteed: E007OutputNotGuaranteedRule;
|
|
22
|
-
export {};
|
|
23
|
-
//# sourceMappingURL=e007-output-not-guaranteed.d.ts.map
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E007: Output Used Downstream but Not Guaranteed
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Tool output is optional / nullable
|
|
6
|
-
* - Used downstream without fallback
|
|
7
|
-
*
|
|
8
|
-
* Why:
|
|
9
|
-
* - Silent null propagation
|
|
10
|
-
* - Hard-to-debug failures
|
|
11
|
-
*/
|
|
12
|
-
import { BaseRule } from '../base.js';
|
|
13
|
-
import { ERROR_CODES } from '../error-codes.js';
|
|
14
|
-
class E007OutputNotGuaranteedRule extends BaseRule {
|
|
15
|
-
id = ERROR_CODES.E007;
|
|
16
|
-
severity = 'error';
|
|
17
|
-
ruleName = 'Output Used Downstream but Not Guaranteed';
|
|
18
|
-
description = 'Output of tool is not guaranteed, but is used by downstream tools without fallback.';
|
|
19
|
-
check(ctx) {
|
|
20
|
-
const diagnostics = [];
|
|
21
|
-
// Only check high-confidence dependencies (>= 0.8)
|
|
22
|
-
const highConfidenceDeps = ctx.dependencies.filter(d => d.confidence >= 0.8);
|
|
23
|
-
for (const dep of highConfidenceDeps) {
|
|
24
|
-
// Find the output field
|
|
25
|
-
const fromTool = ctx.indexes.toolIndex.get(dep.fromTool.toLowerCase());
|
|
26
|
-
if (!fromTool) {
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
const fromField = fromTool.outputs.find(f => f.name === dep.fromField);
|
|
30
|
-
if (!fromField) {
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
// Find the downstream input field
|
|
34
|
-
const toTool = ctx.indexes.toolIndex.get(dep.toTool.toLowerCase());
|
|
35
|
-
if (!toTool) {
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
const toField = toTool.inputs.find(f => f.name === dep.toField);
|
|
39
|
-
if (!toField) {
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
// Check if output is not guaranteed and downstream requires it without fallback
|
|
43
|
-
// Optional upstream always triggers when downstream requires it
|
|
44
|
-
// Nullable upstream only triggers when downstream is required AND not nullable (can't handle null)
|
|
45
|
-
if ((!fromField.required && toField.required === true) ||
|
|
46
|
-
(fromField.nullable === true &&
|
|
47
|
-
toField.required === true &&
|
|
48
|
-
toField.nullable !== true)) {
|
|
49
|
-
diagnostics.push(this.createDiagnostic(`Output of "${dep.fromTool}" (field: "${dep.fromField}") is nullable but is used by "${dep.toTool}" without fallback.`, dep.fromTool, dep.fromField, `Make the output of "${dep.fromTool}" non-nullable, or ensure "${dep.toTool}" handles null values.`));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return diagnostics;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
export const E007OutputNotGuaranteed = new E007OutputNotGuaranteedRule();
|
|
56
|
-
//# sourceMappingURL=e007-output-not-guaranteed.js.map
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E008: Circular Tool Dependency
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Tool dependency graph contains a cycle
|
|
6
|
-
*
|
|
7
|
-
* Why:
|
|
8
|
-
* - LLMs cannot reason about cycles
|
|
9
|
-
* - Execution becomes undefined
|
|
10
|
-
*/
|
|
11
|
-
import { BaseRule } from '../base.js';
|
|
12
|
-
import type { AnalysisContext, Diagnostic } from '../../types.js';
|
|
13
|
-
declare class E008CircularDependencyRule extends BaseRule {
|
|
14
|
-
readonly id: "E008";
|
|
15
|
-
readonly severity: "error";
|
|
16
|
-
readonly ruleName = "Circular Tool Dependency";
|
|
17
|
-
readonly description = "Circular dependency detected between tools. Execution becomes undefined.";
|
|
18
|
-
check(ctx: AnalysisContext): Diagnostic[];
|
|
19
|
-
}
|
|
20
|
-
export declare const E008CircularDependency: E008CircularDependencyRule;
|
|
21
|
-
export {};
|
|
22
|
-
//# sourceMappingURL=e008-circular-dependency.d.ts.map
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E008: Circular Tool Dependency
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Tool dependency graph contains a cycle
|
|
6
|
-
*
|
|
7
|
-
* Why:
|
|
8
|
-
* - LLMs cannot reason about cycles
|
|
9
|
-
* - Execution becomes undefined
|
|
10
|
-
*/
|
|
11
|
-
import { BaseRule } from '../base.js';
|
|
12
|
-
import { ERROR_CODES } from '../error-codes.js';
|
|
13
|
-
/**
|
|
14
|
-
* Detect cycles in the dependency graph using DFS.
|
|
15
|
-
*/
|
|
16
|
-
function detectCycles(dependencies) {
|
|
17
|
-
// Build adjacency list
|
|
18
|
-
const graph = new Map();
|
|
19
|
-
for (const dep of dependencies) {
|
|
20
|
-
if (!graph.has(dep.fromTool)) {
|
|
21
|
-
graph.set(dep.fromTool, []);
|
|
22
|
-
}
|
|
23
|
-
graph.get(dep.fromTool).push(dep.toTool);
|
|
24
|
-
}
|
|
25
|
-
const cycles = [];
|
|
26
|
-
const visited = new Set();
|
|
27
|
-
const recStack = new Set();
|
|
28
|
-
function dfs(tool, path) {
|
|
29
|
-
visited.add(tool);
|
|
30
|
-
recStack.add(tool);
|
|
31
|
-
path.push(tool);
|
|
32
|
-
const neighbors = graph.get(tool) || [];
|
|
33
|
-
for (const neighbor of neighbors) {
|
|
34
|
-
if (!visited.has(neighbor)) {
|
|
35
|
-
dfs(neighbor, [...path]);
|
|
36
|
-
}
|
|
37
|
-
else if (recStack.has(neighbor)) {
|
|
38
|
-
// Found a cycle
|
|
39
|
-
const cycleStart = path.indexOf(neighbor);
|
|
40
|
-
if (cycleStart !== -1) {
|
|
41
|
-
cycles.push([...path.slice(cycleStart), neighbor]);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
recStack.delete(tool);
|
|
46
|
-
}
|
|
47
|
-
// Check all tools
|
|
48
|
-
for (const dep of dependencies) {
|
|
49
|
-
if (!visited.has(dep.fromTool)) {
|
|
50
|
-
dfs(dep.fromTool, []);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return cycles;
|
|
54
|
-
}
|
|
55
|
-
class E008CircularDependencyRule extends BaseRule {
|
|
56
|
-
id = ERROR_CODES.E008;
|
|
57
|
-
severity = 'error';
|
|
58
|
-
ruleName = 'Circular Tool Dependency';
|
|
59
|
-
description = 'Circular dependency detected between tools. Execution becomes undefined.';
|
|
60
|
-
check(ctx) {
|
|
61
|
-
const diagnostics = [];
|
|
62
|
-
// Only check high-confidence dependencies (>= 0.8) for cycles
|
|
63
|
-
const highConfidenceDeps = ctx.dependencies.filter(d => d.confidence >= 0.8);
|
|
64
|
-
if (highConfidenceDeps.length === 0) {
|
|
65
|
-
return diagnostics;
|
|
66
|
-
}
|
|
67
|
-
const cycles = detectCycles(highConfidenceDeps);
|
|
68
|
-
// Report each cycle
|
|
69
|
-
const reportedCycles = new Set();
|
|
70
|
-
for (const cycle of cycles) {
|
|
71
|
-
// Create a canonical representation of the cycle (sort a copy to avoid mutating original)
|
|
72
|
-
const cycleKey = [...cycle].sort().join(' → ');
|
|
73
|
-
if (reportedCycles.has(cycleKey)) {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
reportedCycles.add(cycleKey);
|
|
77
|
-
const cycleStr = cycle.join(' → ');
|
|
78
|
-
diagnostics.push(this.createDiagnostic(`Circular dependency detected between tools: ${cycleStr}.`, undefined, undefined, `Break the cycle by removing or restructuring dependencies between: ${cycle.join(', ')}.`));
|
|
79
|
-
}
|
|
80
|
-
return diagnostics;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
export const E008CircularDependency = new E008CircularDependencyRule();
|
|
84
|
-
//# sourceMappingURL=e008-circular-dependency.js.map
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E009: Tool Depends on User Input Indirectly
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Tool expects data
|
|
6
|
-
* - Only source is implicit user memory / conversation
|
|
7
|
-
* - No explicit tool provides it
|
|
8
|
-
*
|
|
9
|
-
* Why:
|
|
10
|
-
* - Relies on hallucinated context
|
|
11
|
-
*/
|
|
12
|
-
import { BaseRule } from '../base.js';
|
|
13
|
-
import type { AnalysisContext, Diagnostic } from '../../types.js';
|
|
14
|
-
declare class E009ImplicitUserInputRule extends BaseRule {
|
|
15
|
-
readonly id: "E009";
|
|
16
|
-
readonly severity: "error";
|
|
17
|
-
readonly ruleName = "Tool Depends on User Input Indirectly";
|
|
18
|
-
readonly description = "Tool depends on implicit user context with no explicit source.";
|
|
19
|
-
check(ctx: AnalysisContext): Diagnostic[];
|
|
20
|
-
}
|
|
21
|
-
export declare const E009ImplicitUserInput: E009ImplicitUserInputRule;
|
|
22
|
-
export {};
|
|
23
|
-
//# sourceMappingURL=e009-implicit-user-input.d.ts.map
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E009: Tool Depends on User Input Indirectly
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Tool expects data
|
|
6
|
-
* - Only source is implicit user memory / conversation
|
|
7
|
-
* - No explicit tool provides it
|
|
8
|
-
*
|
|
9
|
-
* Why:
|
|
10
|
-
* - Relies on hallucinated context
|
|
11
|
-
*/
|
|
12
|
-
import { BaseRule } from '../base.js';
|
|
13
|
-
import { ERROR_CODES } from '../error-codes.js';
|
|
14
|
-
class E009ImplicitUserInputRule extends BaseRule {
|
|
15
|
-
id = ERROR_CODES.E009;
|
|
16
|
-
severity = 'error';
|
|
17
|
-
ruleName = 'Tool Depends on User Input Indirectly';
|
|
18
|
-
description = 'Tool depends on implicit user context with no explicit source.';
|
|
19
|
-
check(ctx) {
|
|
20
|
-
const diagnostics = [];
|
|
21
|
-
for (const tool of ctx.tools) {
|
|
22
|
-
for (const input of tool.inputs) {
|
|
23
|
-
if (!input.required) {
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
// Check if there's any tool that provides this input
|
|
27
|
-
// Look for output fields with similar names
|
|
28
|
-
const fieldName = input.name.toLowerCase();
|
|
29
|
-
let hasExplicitSource = false;
|
|
30
|
-
for (const otherTool of ctx.tools) {
|
|
31
|
-
if (otherTool.name === tool.name) {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
for (const output of otherTool.outputs) {
|
|
35
|
-
const outputName = output.name.toLowerCase();
|
|
36
|
-
// Check for exact match or token-based match
|
|
37
|
-
if (outputName === fieldName) {
|
|
38
|
-
hasExplicitSource = true;
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
// Token-based matching: split on non-alphanumeric and camelCase boundaries
|
|
42
|
-
const fieldTokens = new Set(fieldName
|
|
43
|
-
.split(/[^\w]+|(?<=[a-z])(?=[A-Z])/)
|
|
44
|
-
.filter(t => t.length > 0));
|
|
45
|
-
const outputTokens = new Set(outputName
|
|
46
|
-
.split(/[^\w]+|(?<=[a-z])(?=[A-Z])/)
|
|
47
|
-
.filter(t => t.length > 0));
|
|
48
|
-
// Check for token intersection
|
|
49
|
-
const hasTokenMatch = Array.from(fieldTokens).some(token => outputTokens.has(token) && token.length >= 3) ||
|
|
50
|
-
Array.from(outputTokens).some(token => fieldTokens.has(token) && token.length >= 3);
|
|
51
|
-
if (hasTokenMatch) {
|
|
52
|
-
hasExplicitSource = true;
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (hasExplicitSource) {
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
// Also check dependencies
|
|
61
|
-
if (!hasExplicitSource) {
|
|
62
|
-
const hasDependency = ctx.dependencies.some(d => d.toTool === tool.name &&
|
|
63
|
-
d.toField === input.name &&
|
|
64
|
-
d.confidence >= 0.6);
|
|
65
|
-
if (hasDependency) {
|
|
66
|
-
hasExplicitSource = true;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// If no explicit source found and input name suggests user data
|
|
70
|
-
const userDataIndicators = [
|
|
71
|
-
'user',
|
|
72
|
-
'person',
|
|
73
|
-
'name',
|
|
74
|
-
'email',
|
|
75
|
-
'location',
|
|
76
|
-
'address',
|
|
77
|
-
'preference',
|
|
78
|
-
];
|
|
79
|
-
const looksLikeUserData = userDataIndicators.some(indicator => fieldName.includes(indicator));
|
|
80
|
-
if (!hasExplicitSource && looksLikeUserData) {
|
|
81
|
-
diagnostics.push(this.createDiagnostic(`Tool "${tool.name}" depends on implicit user context (parameter: "${input.name}") with no explicit source.`, tool.name, input.name, `Create an explicit tool to provide "${input.name}", or ensure it's clearly documented as user input.`));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return diagnostics;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
export const E009ImplicitUserInput = new E009ImplicitUserInputRule();
|
|
89
|
-
//# sourceMappingURL=e009-implicit-user-input.js.map
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E010: Non-Serializable Output
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Output schema contains:
|
|
6
|
-
* - functions
|
|
7
|
-
* - class instances
|
|
8
|
-
* - unsupported types
|
|
9
|
-
*
|
|
10
|
-
* Why:
|
|
11
|
-
* - Breaks MCP contract
|
|
12
|
-
* - Breaks recording & replay later
|
|
13
|
-
*/
|
|
14
|
-
import { BaseRule } from '../base.js';
|
|
15
|
-
import type { AnalysisContext, Diagnostic } from '../../types.js';
|
|
16
|
-
declare class E010NonSerializableRule extends BaseRule {
|
|
17
|
-
readonly id: "E010";
|
|
18
|
-
readonly severity: "error";
|
|
19
|
-
readonly ruleName = "Non-Serializable Output";
|
|
20
|
-
readonly description = "Output of tool is not serializable. Breaks MCP contract.";
|
|
21
|
-
check(ctx: AnalysisContext): Diagnostic[];
|
|
22
|
-
}
|
|
23
|
-
export declare const E010NonSerializable: E010NonSerializableRule;
|
|
24
|
-
export {};
|
|
25
|
-
//# sourceMappingURL=e010-non-serializable.d.ts.map
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E010: Non-Serializable Output
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Output schema contains:
|
|
6
|
-
* - functions
|
|
7
|
-
* - class instances
|
|
8
|
-
* - unsupported types
|
|
9
|
-
*
|
|
10
|
-
* Why:
|
|
11
|
-
* - Breaks MCP contract
|
|
12
|
-
* - Breaks recording & replay later
|
|
13
|
-
*/
|
|
14
|
-
import { BaseRule } from '../base.js';
|
|
15
|
-
import { ERROR_CODES } from '../error-codes.js';
|
|
16
|
-
/**
|
|
17
|
-
* Check if a type is non-serializable.
|
|
18
|
-
*/
|
|
19
|
-
function isNonSerializableType(type) {
|
|
20
|
-
const nonSerializableTypes = new Set([
|
|
21
|
-
'function',
|
|
22
|
-
'undefined',
|
|
23
|
-
'symbol',
|
|
24
|
-
'bigint',
|
|
25
|
-
]);
|
|
26
|
-
return nonSerializableTypes.has(type.toLowerCase());
|
|
27
|
-
}
|
|
28
|
-
class E010NonSerializableRule extends BaseRule {
|
|
29
|
-
id = ERROR_CODES.E010;
|
|
30
|
-
severity = 'error';
|
|
31
|
-
ruleName = 'Non-Serializable Output';
|
|
32
|
-
description = 'Output of tool is not serializable. Breaks MCP contract.';
|
|
33
|
-
check(ctx) {
|
|
34
|
-
const diagnostics = [];
|
|
35
|
-
for (const tool of ctx.tools) {
|
|
36
|
-
for (const output of tool.outputs) {
|
|
37
|
-
if (isNonSerializableType(output.type)) {
|
|
38
|
-
diagnostics.push(this.createDiagnostic(`Output of "${tool.name}" (field: "${output.name}") has non-serializable type "${output.type}".`, tool.name, output.name, `Change the output type to a serializable type (string, number, boolean, object, array).`));
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return diagnostics;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
export const E010NonSerializable = new E010NonSerializableRule();
|
|
46
|
-
//# sourceMappingURL=e010-non-serializable.js.map
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E011: Missing Tool Description
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Tool has no description OR
|
|
6
|
-
* - Tool description is empty or only whitespace
|
|
7
|
-
*
|
|
8
|
-
* Why:
|
|
9
|
-
* - LLM cannot understand what the tool does
|
|
10
|
-
* - Tool selection becomes ambiguous
|
|
11
|
-
* - Critical for tool discovery and usage
|
|
12
|
-
*/
|
|
13
|
-
import { BaseRule } from '../base.js';
|
|
14
|
-
import type { AnalysisContext, Diagnostic } from '../../types.js';
|
|
15
|
-
declare class E011MissingToolDescriptionRule extends BaseRule {
|
|
16
|
-
readonly id: "E011";
|
|
17
|
-
readonly severity: "error";
|
|
18
|
-
readonly ruleName = "Missing Tool Description";
|
|
19
|
-
readonly description = "Tool is missing a description. LLM cannot understand what the tool does.";
|
|
20
|
-
check(ctx: AnalysisContext): Diagnostic[];
|
|
21
|
-
}
|
|
22
|
-
export declare const E011MissingToolDescription: E011MissingToolDescriptionRule;
|
|
23
|
-
export {};
|
|
24
|
-
//# sourceMappingURL=e011-missing-tool-description.d.ts.map
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E011: Missing Tool Description
|
|
3
|
-
*
|
|
4
|
-
* Condition:
|
|
5
|
-
* - Tool has no description OR
|
|
6
|
-
* - Tool description is empty or only whitespace
|
|
7
|
-
*
|
|
8
|
-
* Why:
|
|
9
|
-
* - LLM cannot understand what the tool does
|
|
10
|
-
* - Tool selection becomes ambiguous
|
|
11
|
-
* - Critical for tool discovery and usage
|
|
12
|
-
*/
|
|
13
|
-
import { BaseRule } from '../base.js';
|
|
14
|
-
import { ERROR_CODES } from '../error-codes.js';
|
|
15
|
-
class E011MissingToolDescriptionRule extends BaseRule {
|
|
16
|
-
id = ERROR_CODES.E011;
|
|
17
|
-
severity = 'error';
|
|
18
|
-
ruleName = 'Missing Tool Description';
|
|
19
|
-
description = 'Tool is missing a description. LLM cannot understand what the tool does.';
|
|
20
|
-
check(ctx) {
|
|
21
|
-
const diagnostics = [];
|
|
22
|
-
for (const tool of ctx.tools) {
|
|
23
|
-
// Check if description is missing or empty/whitespace only
|
|
24
|
-
const hasDescription = Boolean(tool.description && tool.description.trim().length > 0);
|
|
25
|
-
if (!hasDescription) {
|
|
26
|
-
diagnostics.push(this.createDiagnostic(`Tool "${tool.name}" is missing a description.`, tool.name, undefined, `Add a clear description to "${tool.name}" explaining what it does, what inputs it expects, and what it returns.`));
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return diagnostics;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
export const E011MissingToolDescription = new E011MissingToolDescriptionRule();
|
|
33
|
-
//# sourceMappingURL=e011-missing-tool-description.js.map
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E012: Side Effect Detected
|
|
3
|
-
*
|
|
4
|
-
* Condition: Tool attempts filesystem writes to project files (not temp directory)
|
|
5
|
-
*
|
|
6
|
-
* Why this is fatal:
|
|
7
|
-
* - Tools should not mutate project state
|
|
8
|
-
* - Breaks isolation and testability
|
|
9
|
-
* - Makes behavior unpredictable
|
|
10
|
-
*/
|
|
11
|
-
import { BaseRule } from '../base.js';
|
|
12
|
-
import type { AnalysisContext, Diagnostic } from '../../types.js';
|
|
13
|
-
/**
|
|
14
|
-
* Context for behavioral validation results.
|
|
15
|
-
* This extends the static analysis context with runtime observation data.
|
|
16
|
-
*/
|
|
17
|
-
export interface BehavioralContext {
|
|
18
|
-
/** Tool name */
|
|
19
|
-
toolName: string;
|
|
20
|
-
/** Side effects detected */
|
|
21
|
-
sideEffects: Array<{
|
|
22
|
-
operation: string;
|
|
23
|
-
path: string;
|
|
24
|
-
}>;
|
|
25
|
-
}
|
|
26
|
-
declare class E012SideEffectDetectedRule extends BaseRule {
|
|
27
|
-
readonly id: "E012";
|
|
28
|
-
readonly severity: "error";
|
|
29
|
-
readonly ruleName = "Side Effect Detected";
|
|
30
|
-
readonly description = "Tool attempted filesystem write to project files. Tools should not mutate project state.";
|
|
31
|
-
check(_ctx: AnalysisContext): Diagnostic[];
|
|
32
|
-
/**
|
|
33
|
-
* Check with behavioral context (called from test orchestrator).
|
|
34
|
-
*/
|
|
35
|
-
checkWithBehavioralContext(behavioralCtx: BehavioralContext): Diagnostic[];
|
|
36
|
-
}
|
|
37
|
-
export declare const E012SideEffectDetected: E012SideEffectDetectedRule;
|
|
38
|
-
export {};
|
|
39
|
-
//# sourceMappingURL=e012-side-effect-detected.d.ts.map
|