@dotsetlabs/bellwether 0.10.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/CHANGELOG.md +291 -0
- package/LICENSE +21 -0
- package/README.md +739 -0
- package/dist/auth/credentials.d.ts +64 -0
- package/dist/auth/credentials.js +218 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.js +6 -0
- package/dist/auth/keychain.d.ts +64 -0
- package/dist/auth/keychain.js +268 -0
- package/dist/baseline/ab-testing.d.ts +80 -0
- package/dist/baseline/ab-testing.js +236 -0
- package/dist/baseline/ai-compatibility-scorer.d.ts +95 -0
- package/dist/baseline/ai-compatibility-scorer.js +606 -0
- package/dist/baseline/calibration.d.ts +77 -0
- package/dist/baseline/calibration.js +136 -0
- package/dist/baseline/category-matching.d.ts +85 -0
- package/dist/baseline/category-matching.js +289 -0
- package/dist/baseline/change-impact-analyzer.d.ts +98 -0
- package/dist/baseline/change-impact-analyzer.js +592 -0
- package/dist/baseline/comparator.d.ts +64 -0
- package/dist/baseline/comparator.js +916 -0
- package/dist/baseline/confidence.d.ts +55 -0
- package/dist/baseline/confidence.js +122 -0
- package/dist/baseline/converter.d.ts +61 -0
- package/dist/baseline/converter.js +585 -0
- package/dist/baseline/dependency-analyzer.d.ts +89 -0
- package/dist/baseline/dependency-analyzer.js +567 -0
- package/dist/baseline/deprecation-tracker.d.ts +133 -0
- package/dist/baseline/deprecation-tracker.js +322 -0
- package/dist/baseline/diff.d.ts +55 -0
- package/dist/baseline/diff.js +1584 -0
- package/dist/baseline/documentation-scorer.d.ts +205 -0
- package/dist/baseline/documentation-scorer.js +466 -0
- package/dist/baseline/embeddings.d.ts +118 -0
- package/dist/baseline/embeddings.js +251 -0
- package/dist/baseline/error-analyzer.d.ts +198 -0
- package/dist/baseline/error-analyzer.js +721 -0
- package/dist/baseline/evaluation/evaluator.d.ts +42 -0
- package/dist/baseline/evaluation/evaluator.js +323 -0
- package/dist/baseline/evaluation/expanded-dataset.d.ts +45 -0
- package/dist/baseline/evaluation/expanded-dataset.js +1164 -0
- package/dist/baseline/evaluation/golden-dataset.d.ts +58 -0
- package/dist/baseline/evaluation/golden-dataset.js +717 -0
- package/dist/baseline/evaluation/index.d.ts +15 -0
- package/dist/baseline/evaluation/index.js +15 -0
- package/dist/baseline/evaluation/types.d.ts +186 -0
- package/dist/baseline/evaluation/types.js +8 -0
- package/dist/baseline/external-dependency-detector.d.ts +181 -0
- package/dist/baseline/external-dependency-detector.js +524 -0
- package/dist/baseline/golden-output.d.ts +162 -0
- package/dist/baseline/golden-output.js +636 -0
- package/dist/baseline/health-scorer.d.ts +174 -0
- package/dist/baseline/health-scorer.js +451 -0
- package/dist/baseline/incremental-checker.d.ts +97 -0
- package/dist/baseline/incremental-checker.js +174 -0
- package/dist/baseline/index.d.ts +31 -0
- package/dist/baseline/index.js +42 -0
- package/dist/baseline/migration-generator.d.ts +137 -0
- package/dist/baseline/migration-generator.js +554 -0
- package/dist/baseline/migrations.d.ts +60 -0
- package/dist/baseline/migrations.js +197 -0
- package/dist/baseline/performance-tracker.d.ts +214 -0
- package/dist/baseline/performance-tracker.js +577 -0
- package/dist/baseline/pr-comment-generator.d.ts +117 -0
- package/dist/baseline/pr-comment-generator.js +546 -0
- package/dist/baseline/response-fingerprint.d.ts +127 -0
- package/dist/baseline/response-fingerprint.js +728 -0
- package/dist/baseline/response-schema-tracker.d.ts +129 -0
- package/dist/baseline/response-schema-tracker.js +420 -0
- package/dist/baseline/risk-scorer.d.ts +54 -0
- package/dist/baseline/risk-scorer.js +434 -0
- package/dist/baseline/saver.d.ts +89 -0
- package/dist/baseline/saver.js +554 -0
- package/dist/baseline/scenario-generator.d.ts +151 -0
- package/dist/baseline/scenario-generator.js +905 -0
- package/dist/baseline/schema-compare.d.ts +86 -0
- package/dist/baseline/schema-compare.js +557 -0
- package/dist/baseline/schema-evolution.d.ts +189 -0
- package/dist/baseline/schema-evolution.js +467 -0
- package/dist/baseline/semantic.d.ts +203 -0
- package/dist/baseline/semantic.js +908 -0
- package/dist/baseline/synonyms.d.ts +60 -0
- package/dist/baseline/synonyms.js +386 -0
- package/dist/baseline/telemetry.d.ts +165 -0
- package/dist/baseline/telemetry.js +294 -0
- package/dist/baseline/test-pruner.d.ts +120 -0
- package/dist/baseline/test-pruner.js +387 -0
- package/dist/baseline/types.d.ts +449 -0
- package/dist/baseline/types.js +5 -0
- package/dist/baseline/version.d.ts +138 -0
- package/dist/baseline/version.js +206 -0
- package/dist/cache/index.d.ts +5 -0
- package/dist/cache/index.js +5 -0
- package/dist/cache/response-cache.d.ts +151 -0
- package/dist/cache/response-cache.js +287 -0
- package/dist/ci/index.d.ts +60 -0
- package/dist/ci/index.js +342 -0
- package/dist/cli/commands/auth.d.ts +12 -0
- package/dist/cli/commands/auth.js +352 -0
- package/dist/cli/commands/badge.d.ts +3 -0
- package/dist/cli/commands/badge.js +74 -0
- package/dist/cli/commands/baseline-accept.d.ts +15 -0
- package/dist/cli/commands/baseline-accept.js +178 -0
- package/dist/cli/commands/baseline-migrate.d.ts +12 -0
- package/dist/cli/commands/baseline-migrate.js +164 -0
- package/dist/cli/commands/baseline.d.ts +14 -0
- package/dist/cli/commands/baseline.js +449 -0
- package/dist/cli/commands/beta.d.ts +10 -0
- package/dist/cli/commands/beta.js +231 -0
- package/dist/cli/commands/check.d.ts +11 -0
- package/dist/cli/commands/check.js +820 -0
- package/dist/cli/commands/cloud/badge.d.ts +3 -0
- package/dist/cli/commands/cloud/badge.js +74 -0
- package/dist/cli/commands/cloud/diff.d.ts +6 -0
- package/dist/cli/commands/cloud/diff.js +79 -0
- package/dist/cli/commands/cloud/history.d.ts +6 -0
- package/dist/cli/commands/cloud/history.js +102 -0
- package/dist/cli/commands/cloud/link.d.ts +9 -0
- package/dist/cli/commands/cloud/link.js +119 -0
- package/dist/cli/commands/cloud/login.d.ts +7 -0
- package/dist/cli/commands/cloud/login.js +499 -0
- package/dist/cli/commands/cloud/projects.d.ts +6 -0
- package/dist/cli/commands/cloud/projects.js +44 -0
- package/dist/cli/commands/cloud/shared.d.ts +7 -0
- package/dist/cli/commands/cloud/shared.js +42 -0
- package/dist/cli/commands/cloud/teams.d.ts +8 -0
- package/dist/cli/commands/cloud/teams.js +169 -0
- package/dist/cli/commands/cloud/upload.d.ts +8 -0
- package/dist/cli/commands/cloud/upload.js +181 -0
- package/dist/cli/commands/contract.d.ts +11 -0
- package/dist/cli/commands/contract.js +280 -0
- package/dist/cli/commands/discover.d.ts +3 -0
- package/dist/cli/commands/discover.js +82 -0
- package/dist/cli/commands/eval.d.ts +9 -0
- package/dist/cli/commands/eval.js +187 -0
- package/dist/cli/commands/explore.d.ts +11 -0
- package/dist/cli/commands/explore.js +437 -0
- package/dist/cli/commands/feedback.d.ts +9 -0
- package/dist/cli/commands/feedback.js +174 -0
- package/dist/cli/commands/golden.d.ts +12 -0
- package/dist/cli/commands/golden.js +407 -0
- package/dist/cli/commands/history.d.ts +10 -0
- package/dist/cli/commands/history.js +202 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.js +219 -0
- package/dist/cli/commands/interview.d.ts +3 -0
- package/dist/cli/commands/interview.js +903 -0
- package/dist/cli/commands/link.d.ts +10 -0
- package/dist/cli/commands/link.js +169 -0
- package/dist/cli/commands/login.d.ts +7 -0
- package/dist/cli/commands/login.js +499 -0
- package/dist/cli/commands/preset.d.ts +33 -0
- package/dist/cli/commands/preset.js +297 -0
- package/dist/cli/commands/profile.d.ts +33 -0
- package/dist/cli/commands/profile.js +286 -0
- package/dist/cli/commands/registry.d.ts +11 -0
- package/dist/cli/commands/registry.js +146 -0
- package/dist/cli/commands/shared.d.ts +79 -0
- package/dist/cli/commands/shared.js +196 -0
- package/dist/cli/commands/teams.d.ts +8 -0
- package/dist/cli/commands/teams.js +169 -0
- package/dist/cli/commands/test.d.ts +9 -0
- package/dist/cli/commands/test.js +500 -0
- package/dist/cli/commands/upload.d.ts +8 -0
- package/dist/cli/commands/upload.js +223 -0
- package/dist/cli/commands/validate-config.d.ts +6 -0
- package/dist/cli/commands/validate-config.js +35 -0
- package/dist/cli/commands/verify.d.ts +11 -0
- package/dist/cli/commands/verify.js +283 -0
- package/dist/cli/commands/watch.d.ts +12 -0
- package/dist/cli/commands/watch.js +253 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +178 -0
- package/dist/cli/interactive.d.ts +47 -0
- package/dist/cli/interactive.js +216 -0
- package/dist/cli/output/terminal-reporter.d.ts +19 -0
- package/dist/cli/output/terminal-reporter.js +104 -0
- package/dist/cli/output.d.ts +226 -0
- package/dist/cli/output.js +438 -0
- package/dist/cli/utils/env.d.ts +5 -0
- package/dist/cli/utils/env.js +14 -0
- package/dist/cli/utils/progress.d.ts +59 -0
- package/dist/cli/utils/progress.js +206 -0
- package/dist/cli/utils/server-context.d.ts +10 -0
- package/dist/cli/utils/server-context.js +36 -0
- package/dist/cloud/auth.d.ts +144 -0
- package/dist/cloud/auth.js +374 -0
- package/dist/cloud/client.d.ts +24 -0
- package/dist/cloud/client.js +65 -0
- package/dist/cloud/http-client.d.ts +38 -0
- package/dist/cloud/http-client.js +215 -0
- package/dist/cloud/index.d.ts +23 -0
- package/dist/cloud/index.js +25 -0
- package/dist/cloud/mock-client.d.ts +107 -0
- package/dist/cloud/mock-client.js +545 -0
- package/dist/cloud/types.d.ts +515 -0
- package/dist/cloud/types.js +15 -0
- package/dist/config/defaults.d.ts +160 -0
- package/dist/config/defaults.js +169 -0
- package/dist/config/loader.d.ts +24 -0
- package/dist/config/loader.js +122 -0
- package/dist/config/template.d.ts +42 -0
- package/dist/config/template.js +647 -0
- package/dist/config/validator.d.ts +2112 -0
- package/dist/config/validator.js +658 -0
- package/dist/constants/cloud.d.ts +107 -0
- package/dist/constants/cloud.js +110 -0
- package/dist/constants/core.d.ts +521 -0
- package/dist/constants/core.js +556 -0
- package/dist/constants/testing.d.ts +1283 -0
- package/dist/constants/testing.js +1568 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +10 -0
- package/dist/contract/index.d.ts +6 -0
- package/dist/contract/index.js +5 -0
- package/dist/contract/validator.d.ts +177 -0
- package/dist/contract/validator.js +574 -0
- package/dist/cost/index.d.ts +6 -0
- package/dist/cost/index.js +5 -0
- package/dist/cost/tracker.d.ts +134 -0
- package/dist/cost/tracker.js +313 -0
- package/dist/discovery/discovery.d.ts +16 -0
- package/dist/discovery/discovery.js +173 -0
- package/dist/discovery/types.d.ts +51 -0
- package/dist/discovery/types.js +2 -0
- package/dist/docs/agents.d.ts +3 -0
- package/dist/docs/agents.js +995 -0
- package/dist/docs/contract.d.ts +51 -0
- package/dist/docs/contract.js +1681 -0
- package/dist/docs/generator.d.ts +4 -0
- package/dist/docs/generator.js +4 -0
- package/dist/docs/html-reporter.d.ts +9 -0
- package/dist/docs/html-reporter.js +757 -0
- package/dist/docs/index.d.ts +10 -0
- package/dist/docs/index.js +11 -0
- package/dist/docs/junit-reporter.d.ts +18 -0
- package/dist/docs/junit-reporter.js +210 -0
- package/dist/docs/report.d.ts +14 -0
- package/dist/docs/report.js +44 -0
- package/dist/docs/sarif-reporter.d.ts +19 -0
- package/dist/docs/sarif-reporter.js +335 -0
- package/dist/docs/shared.d.ts +35 -0
- package/dist/docs/shared.js +162 -0
- package/dist/docs/templates.d.ts +12 -0
- package/dist/docs/templates.js +76 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.js +6 -0
- package/dist/errors/retry.d.ts +92 -0
- package/dist/errors/retry.js +323 -0
- package/dist/errors/types.d.ts +321 -0
- package/dist/errors/types.js +584 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +32 -0
- package/dist/interview/dependency-resolver.d.ts +11 -0
- package/dist/interview/dependency-resolver.js +32 -0
- package/dist/interview/interviewer.d.ts +232 -0
- package/dist/interview/interviewer.js +1939 -0
- package/dist/interview/mock-response-generator.d.ts +7 -0
- package/dist/interview/mock-response-generator.js +102 -0
- package/dist/interview/orchestrator.d.ts +237 -0
- package/dist/interview/orchestrator.js +1296 -0
- package/dist/interview/rate-limiter.d.ts +15 -0
- package/dist/interview/rate-limiter.js +55 -0
- package/dist/interview/response-validator.d.ts +10 -0
- package/dist/interview/response-validator.js +132 -0
- package/dist/interview/schema-inferrer.d.ts +8 -0
- package/dist/interview/schema-inferrer.js +71 -0
- package/dist/interview/schema-test-generator.d.ts +71 -0
- package/dist/interview/schema-test-generator.js +834 -0
- package/dist/interview/smart-value-generator.d.ts +155 -0
- package/dist/interview/smart-value-generator.js +554 -0
- package/dist/interview/stateful-test-runner.d.ts +19 -0
- package/dist/interview/stateful-test-runner.js +106 -0
- package/dist/interview/types.d.ts +561 -0
- package/dist/interview/types.js +2 -0
- package/dist/llm/anthropic.d.ts +41 -0
- package/dist/llm/anthropic.js +355 -0
- package/dist/llm/client.d.ts +123 -0
- package/dist/llm/client.js +42 -0
- package/dist/llm/factory.d.ts +38 -0
- package/dist/llm/factory.js +145 -0
- package/dist/llm/fallback.d.ts +140 -0
- package/dist/llm/fallback.js +379 -0
- package/dist/llm/index.d.ts +18 -0
- package/dist/llm/index.js +15 -0
- package/dist/llm/ollama.d.ts +37 -0
- package/dist/llm/ollama.js +330 -0
- package/dist/llm/openai.d.ts +25 -0
- package/dist/llm/openai.js +320 -0
- package/dist/llm/token-budget.d.ts +161 -0
- package/dist/llm/token-budget.js +395 -0
- package/dist/logging/logger.d.ts +70 -0
- package/dist/logging/logger.js +130 -0
- package/dist/metrics/collector.d.ts +106 -0
- package/dist/metrics/collector.js +547 -0
- package/dist/metrics/index.d.ts +7 -0
- package/dist/metrics/index.js +7 -0
- package/dist/metrics/prometheus.d.ts +20 -0
- package/dist/metrics/prometheus.js +241 -0
- package/dist/metrics/types.d.ts +209 -0
- package/dist/metrics/types.js +5 -0
- package/dist/persona/builtins.d.ts +54 -0
- package/dist/persona/builtins.js +219 -0
- package/dist/persona/index.d.ts +8 -0
- package/dist/persona/index.js +8 -0
- package/dist/persona/loader.d.ts +30 -0
- package/dist/persona/loader.js +190 -0
- package/dist/persona/types.d.ts +144 -0
- package/dist/persona/types.js +5 -0
- package/dist/persona/validation.d.ts +94 -0
- package/dist/persona/validation.js +332 -0
- package/dist/prompts/index.d.ts +5 -0
- package/dist/prompts/index.js +5 -0
- package/dist/prompts/templates.d.ts +180 -0
- package/dist/prompts/templates.js +431 -0
- package/dist/registry/client.d.ts +49 -0
- package/dist/registry/client.js +191 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.js +6 -0
- package/dist/registry/types.d.ts +140 -0
- package/dist/registry/types.js +6 -0
- package/dist/scenarios/evaluator.d.ts +43 -0
- package/dist/scenarios/evaluator.js +206 -0
- package/dist/scenarios/index.d.ts +10 -0
- package/dist/scenarios/index.js +9 -0
- package/dist/scenarios/loader.d.ts +20 -0
- package/dist/scenarios/loader.js +285 -0
- package/dist/scenarios/types.d.ts +153 -0
- package/dist/scenarios/types.js +8 -0
- package/dist/security/index.d.ts +17 -0
- package/dist/security/index.js +18 -0
- package/dist/security/payloads.d.ts +61 -0
- package/dist/security/payloads.js +268 -0
- package/dist/security/security-tester.d.ts +42 -0
- package/dist/security/security-tester.js +582 -0
- package/dist/security/types.d.ts +166 -0
- package/dist/security/types.js +8 -0
- package/dist/transport/base-transport.d.ts +59 -0
- package/dist/transport/base-transport.js +38 -0
- package/dist/transport/http-transport.d.ts +67 -0
- package/dist/transport/http-transport.js +238 -0
- package/dist/transport/mcp-client.d.ts +141 -0
- package/dist/transport/mcp-client.js +496 -0
- package/dist/transport/sse-transport.d.ts +88 -0
- package/dist/transport/sse-transport.js +316 -0
- package/dist/transport/stdio-transport.d.ts +43 -0
- package/dist/transport/stdio-transport.js +238 -0
- package/dist/transport/types.d.ts +125 -0
- package/dist/transport/types.js +16 -0
- package/dist/utils/concurrency.d.ts +123 -0
- package/dist/utils/concurrency.js +213 -0
- package/dist/utils/formatters.d.ts +16 -0
- package/dist/utils/formatters.js +37 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/jsonpath.d.ts +87 -0
- package/dist/utils/jsonpath.js +326 -0
- package/dist/utils/markdown.d.ts +113 -0
- package/dist/utils/markdown.js +265 -0
- package/dist/utils/network.d.ts +14 -0
- package/dist/utils/network.js +17 -0
- package/dist/utils/sanitize.d.ts +92 -0
- package/dist/utils/sanitize.js +191 -0
- package/dist/utils/semantic.d.ts +194 -0
- package/dist/utils/semantic.js +1051 -0
- package/dist/utils/smart-truncate.d.ts +94 -0
- package/dist/utils/smart-truncate.js +361 -0
- package/dist/utils/timeout.d.ts +153 -0
- package/dist/utils/timeout.js +205 -0
- package/dist/utils/yaml-parser.d.ts +58 -0
- package/dist/utils/yaml-parser.js +86 -0
- package/dist/validation/index.d.ts +32 -0
- package/dist/validation/index.js +32 -0
- package/dist/validation/semantic-test-generator.d.ts +50 -0
- package/dist/validation/semantic-test-generator.js +176 -0
- package/dist/validation/semantic-types.d.ts +66 -0
- package/dist/validation/semantic-types.js +94 -0
- package/dist/validation/semantic-validator.d.ts +38 -0
- package/dist/validation/semantic-validator.js +340 -0
- package/dist/verification/index.d.ts +6 -0
- package/dist/verification/index.js +5 -0
- package/dist/verification/types.d.ts +133 -0
- package/dist/verification/types.js +5 -0
- package/dist/verification/verifier.d.ts +30 -0
- package/dist/verification/verifier.js +309 -0
- package/dist/version.d.ts +19 -0
- package/dist/version.js +48 -0
- package/dist/workflow/auto-generator.d.ts +27 -0
- package/dist/workflow/auto-generator.js +513 -0
- package/dist/workflow/discovery.d.ts +40 -0
- package/dist/workflow/discovery.js +195 -0
- package/dist/workflow/executor.d.ts +82 -0
- package/dist/workflow/executor.js +611 -0
- package/dist/workflow/index.d.ts +10 -0
- package/dist/workflow/index.js +10 -0
- package/dist/workflow/loader.d.ts +24 -0
- package/dist/workflow/loader.js +194 -0
- package/dist/workflow/state-tracker.d.ts +98 -0
- package/dist/workflow/state-tracker.js +424 -0
- package/dist/workflow/types.d.ts +337 -0
- package/dist/workflow/types.js +5 -0
- package/package.json +94 -0
- package/schemas/bellwether-check.schema.json +651 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized timeout management utilities.
|
|
3
|
+
*
|
|
4
|
+
* Provides consistent timeout handling across all async operations
|
|
5
|
+
* to prevent indefinite hangs and improve reliability.
|
|
6
|
+
*/
|
|
7
|
+
import { getLogger } from '../logging/logger.js';
|
|
8
|
+
import { TIMEOUTS, INTERVIEW, WORKFLOW } from '../constants.js';
|
|
9
|
+
const logger = getLogger('timeout');
|
|
10
|
+
/**
|
|
11
|
+
* Default timeout values in milliseconds.
|
|
12
|
+
*
|
|
13
|
+
* These reference the centralized constants where applicable,
|
|
14
|
+
* ensuring consistency across the codebase.
|
|
15
|
+
*/
|
|
16
|
+
export const DEFAULT_TIMEOUTS = {
|
|
17
|
+
/** Timeout for individual tool calls - uses INTERVIEW.TOOL_TIMEOUT */
|
|
18
|
+
toolCall: INTERVIEW.TOOL_TIMEOUT,
|
|
19
|
+
/** Timeout for LLM API calls - uses TIMEOUTS.INTERVIEW */
|
|
20
|
+
llmCall: TIMEOUTS.INTERVIEW,
|
|
21
|
+
/** Timeout for state snapshots - uses WORKFLOW.STATE_SNAPSHOT_TIMEOUT */
|
|
22
|
+
stateSnapshot: WORKFLOW.STATE_SNAPSHOT_TIMEOUT,
|
|
23
|
+
/** Timeout for individual probe tool calls - uses WORKFLOW.PROBE_TOOL_TIMEOUT */
|
|
24
|
+
probeTool: WORKFLOW.PROBE_TOOL_TIMEOUT,
|
|
25
|
+
/** Timeout for resource reads - uses INTERVIEW.RESOURCE_TIMEOUT */
|
|
26
|
+
resourceRead: INTERVIEW.RESOURCE_TIMEOUT,
|
|
27
|
+
/** Timeout for HTTP requests - uses TIMEOUTS.CLOUD_API */
|
|
28
|
+
httpRequest: TIMEOUTS.CLOUD_API,
|
|
29
|
+
/** Timeout for SSE connection establishment */
|
|
30
|
+
sseConnect: 10000,
|
|
31
|
+
/** Timeout for interview question generation (longer for local models) */
|
|
32
|
+
questionGeneration: 120000,
|
|
33
|
+
/** Timeout for response analysis */
|
|
34
|
+
responseAnalysis: 60000,
|
|
35
|
+
/** Timeout for profile synthesis */
|
|
36
|
+
profileSynthesis: 120000,
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Error class for timeout errors.
|
|
40
|
+
*/
|
|
41
|
+
export class TimeoutError extends Error {
|
|
42
|
+
operationName;
|
|
43
|
+
timeoutMs;
|
|
44
|
+
constructor(operationName, timeoutMs, message) {
|
|
45
|
+
super(message ?? `${operationName} timed out after ${timeoutMs}ms`);
|
|
46
|
+
this.name = 'TimeoutError';
|
|
47
|
+
this.operationName = operationName;
|
|
48
|
+
this.timeoutMs = timeoutMs;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Error class for aborted operations.
|
|
53
|
+
*/
|
|
54
|
+
export class AbortError extends Error {
|
|
55
|
+
operationName;
|
|
56
|
+
constructor(operationName, message) {
|
|
57
|
+
super(message ?? `${operationName} was aborted`);
|
|
58
|
+
this.name = 'AbortError';
|
|
59
|
+
this.operationName = operationName;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if an AbortSignal is aborted and throw AbortError if so.
|
|
64
|
+
*
|
|
65
|
+
* @param signal - The AbortSignal to check
|
|
66
|
+
* @param operationName - Name of the operation for error messages
|
|
67
|
+
* @throws AbortError if the signal is aborted
|
|
68
|
+
*/
|
|
69
|
+
export function checkAborted(signal, operationName) {
|
|
70
|
+
if (signal?.aborted) {
|
|
71
|
+
throw new AbortError(operationName);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Wrap a promise with a timeout.
|
|
76
|
+
*
|
|
77
|
+
* @param promise - The promise to wrap
|
|
78
|
+
* @param timeoutMs - Timeout in milliseconds
|
|
79
|
+
* @param operationName - Name of the operation for error messages
|
|
80
|
+
* @returns The promise result or throws TimeoutError
|
|
81
|
+
*/
|
|
82
|
+
export async function withTimeout(promise, timeoutMs, operationName) {
|
|
83
|
+
let timeoutId;
|
|
84
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
85
|
+
timeoutId = setTimeout(() => {
|
|
86
|
+
logger.warn({ operationName, timeoutMs }, 'Operation timed out');
|
|
87
|
+
reject(new TimeoutError(operationName, timeoutMs));
|
|
88
|
+
}, timeoutMs);
|
|
89
|
+
});
|
|
90
|
+
try {
|
|
91
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
clearTimeout(timeoutId);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Wrap a promise with a timeout and return a result object instead of throwing.
|
|
99
|
+
*
|
|
100
|
+
* @param promise - The promise to wrap
|
|
101
|
+
* @param timeoutMs - Timeout in milliseconds
|
|
102
|
+
* @param operationName - Name of the operation
|
|
103
|
+
* @returns Object with either result or error
|
|
104
|
+
*/
|
|
105
|
+
export async function withTimeoutResult(promise, timeoutMs, operationName) {
|
|
106
|
+
try {
|
|
107
|
+
const result = await withTimeout(promise, timeoutMs, operationName);
|
|
108
|
+
return { success: true, result };
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return {
|
|
112
|
+
success: false,
|
|
113
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Create an abort controller that automatically aborts after a timeout.
|
|
119
|
+
*
|
|
120
|
+
* @param timeoutMs - Timeout in milliseconds
|
|
121
|
+
* @param operationName - Name of the operation
|
|
122
|
+
* @returns AbortController and cleanup function
|
|
123
|
+
*/
|
|
124
|
+
export function createTimeoutAbortController(timeoutMs, operationName) {
|
|
125
|
+
const controller = new AbortController();
|
|
126
|
+
const timeoutId = setTimeout(() => {
|
|
127
|
+
logger.debug({ operationName, timeoutMs }, 'Aborting due to timeout');
|
|
128
|
+
controller.abort(new TimeoutError(operationName, timeoutMs));
|
|
129
|
+
}, timeoutMs);
|
|
130
|
+
return {
|
|
131
|
+
controller,
|
|
132
|
+
cleanup: () => clearTimeout(timeoutId),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Execute multiple promises with individual timeouts.
|
|
137
|
+
* Returns results for all, including those that timed out.
|
|
138
|
+
*
|
|
139
|
+
* @param operations - Array of operations with their timeouts
|
|
140
|
+
* @returns Array of results (either success with value or failure with error)
|
|
141
|
+
*/
|
|
142
|
+
export async function withTimeoutAll(operations) {
|
|
143
|
+
return Promise.all(operations.map(async ({ promise, timeoutMs, operationName }) => {
|
|
144
|
+
try {
|
|
145
|
+
const result = await withTimeout(promise, timeoutMs, operationName);
|
|
146
|
+
return { success: true, result };
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
152
|
+
operationName,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Execute a function with a timeout, retrying on timeout up to maxRetries.
|
|
159
|
+
*
|
|
160
|
+
* @param fn - Function to execute
|
|
161
|
+
* @param timeoutMs - Timeout per attempt
|
|
162
|
+
* @param operationName - Name of the operation
|
|
163
|
+
* @param maxRetries - Maximum number of retries (default: 1)
|
|
164
|
+
* @returns The function result
|
|
165
|
+
*/
|
|
166
|
+
export async function withTimeoutRetry(fn, timeoutMs, operationName, maxRetries = 1) {
|
|
167
|
+
let lastError;
|
|
168
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
169
|
+
try {
|
|
170
|
+
return await withTimeout(fn(), timeoutMs, operationName);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
174
|
+
if (error instanceof TimeoutError && attempt < maxRetries) {
|
|
175
|
+
logger.debug({ operationName, attempt: attempt + 1, maxRetries }, 'Retrying after timeout');
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
throw lastError;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
throw lastError ?? new Error('Unexpected retry loop exit');
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Create a deadline-based timeout manager.
|
|
185
|
+
* Useful for operations with multiple steps that should complete within a total time.
|
|
186
|
+
*
|
|
187
|
+
* @param totalTimeoutMs - Total time allowed for all operations
|
|
188
|
+
* @param operationName - Name of the overall operation
|
|
189
|
+
* @returns Deadline manager
|
|
190
|
+
*/
|
|
191
|
+
export function createDeadline(totalTimeoutMs, operationName) {
|
|
192
|
+
const startTime = Date.now();
|
|
193
|
+
const deadline = startTime + totalTimeoutMs;
|
|
194
|
+
return {
|
|
195
|
+
getRemainingMs: () => Math.max(0, deadline - Date.now()),
|
|
196
|
+
isExpired: () => Date.now() >= deadline,
|
|
197
|
+
getTimeoutFor: (maxMs) => Math.min(maxMs, Math.max(0, deadline - Date.now())),
|
|
198
|
+
checkDeadline: () => {
|
|
199
|
+
if (Date.now() >= deadline) {
|
|
200
|
+
throw new TimeoutError(operationName, totalTimeoutMs, `${operationName} exceeded total deadline of ${totalTimeoutMs}ms`);
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=timeout.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure YAML parsing utility.
|
|
3
|
+
*
|
|
4
|
+
* Provides safe YAML parsing with protection against:
|
|
5
|
+
* - Alias bombs (billion laughs attack) via maxAliasCount
|
|
6
|
+
* - Excessive nesting via depth validation
|
|
7
|
+
* - Circular references
|
|
8
|
+
*/
|
|
9
|
+
import { type ParseOptions } from 'yaml';
|
|
10
|
+
/**
|
|
11
|
+
* Default security limits for YAML parsing.
|
|
12
|
+
*/
|
|
13
|
+
export declare const YAML_SECURITY_LIMITS: {
|
|
14
|
+
/** Maximum number of aliases to resolve (prevents billion laughs attack) */
|
|
15
|
+
MAX_ALIAS_COUNT: number;
|
|
16
|
+
/** Maximum nesting depth for parsed structures */
|
|
17
|
+
MAX_DEPTH: number;
|
|
18
|
+
/** Maximum size of input in characters */
|
|
19
|
+
MAX_INPUT_SIZE: number;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Options for secure YAML parsing.
|
|
23
|
+
*/
|
|
24
|
+
export interface SecureYamlOptions {
|
|
25
|
+
/** Maximum number of aliases to resolve (default: 100) */
|
|
26
|
+
maxAliasCount?: number;
|
|
27
|
+
/** Maximum nesting depth (default: 50) */
|
|
28
|
+
maxDepth?: number;
|
|
29
|
+
/** Maximum input size in characters (default: 10MB) */
|
|
30
|
+
maxInputSize?: number;
|
|
31
|
+
/** Additional yaml parse options */
|
|
32
|
+
parseOptions?: ParseOptions;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Parse YAML with security protections.
|
|
36
|
+
*
|
|
37
|
+
* @param content - YAML content to parse
|
|
38
|
+
* @param options - Security options
|
|
39
|
+
* @returns Parsed YAML value
|
|
40
|
+
* @throws Error if parsing fails or security limits are exceeded
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const data = parseYamlSecure(content);
|
|
45
|
+
* const config = parseYamlSecure(content, { maxDepth: 10 });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseYamlSecure<T = unknown>(content: string, options?: SecureYamlOptions): T;
|
|
49
|
+
/**
|
|
50
|
+
* Parse YAML with strict security settings.
|
|
51
|
+
* Use this for parsing untrusted input.
|
|
52
|
+
*
|
|
53
|
+
* @param content - YAML content to parse
|
|
54
|
+
* @returns Parsed YAML value
|
|
55
|
+
* @throws Error if parsing fails or security limits are exceeded
|
|
56
|
+
*/
|
|
57
|
+
export declare function parseYamlStrict<T = unknown>(content: string): T;
|
|
58
|
+
//# sourceMappingURL=yaml-parser.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure YAML parsing utility.
|
|
3
|
+
*
|
|
4
|
+
* Provides safe YAML parsing with protection against:
|
|
5
|
+
* - Alias bombs (billion laughs attack) via maxAliasCount
|
|
6
|
+
* - Excessive nesting via depth validation
|
|
7
|
+
* - Circular references
|
|
8
|
+
*/
|
|
9
|
+
import { parse as yamlParse } from 'yaml';
|
|
10
|
+
/**
|
|
11
|
+
* Default security limits for YAML parsing.
|
|
12
|
+
*/
|
|
13
|
+
export const YAML_SECURITY_LIMITS = {
|
|
14
|
+
/** Maximum number of aliases to resolve (prevents billion laughs attack) */
|
|
15
|
+
MAX_ALIAS_COUNT: 100,
|
|
16
|
+
/** Maximum nesting depth for parsed structures */
|
|
17
|
+
MAX_DEPTH: 50,
|
|
18
|
+
/** Maximum size of input in characters */
|
|
19
|
+
MAX_INPUT_SIZE: 10 * 1024 * 1024, // 10MB
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Check the depth of a nested structure.
|
|
23
|
+
* Throws if depth exceeds the limit.
|
|
24
|
+
*/
|
|
25
|
+
function validateDepth(value, maxDepth, currentDepth = 0) {
|
|
26
|
+
if (currentDepth > maxDepth) {
|
|
27
|
+
throw new Error(`YAML nesting depth exceeds maximum of ${maxDepth}`);
|
|
28
|
+
}
|
|
29
|
+
if (Array.isArray(value)) {
|
|
30
|
+
for (const item of value) {
|
|
31
|
+
validateDepth(item, maxDepth, currentDepth + 1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else if (value !== null && typeof value === 'object') {
|
|
35
|
+
for (const key of Object.keys(value)) {
|
|
36
|
+
validateDepth(value[key], maxDepth, currentDepth + 1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parse YAML with security protections.
|
|
42
|
+
*
|
|
43
|
+
* @param content - YAML content to parse
|
|
44
|
+
* @param options - Security options
|
|
45
|
+
* @returns Parsed YAML value
|
|
46
|
+
* @throws Error if parsing fails or security limits are exceeded
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const data = parseYamlSecure(content);
|
|
51
|
+
* const config = parseYamlSecure(content, { maxDepth: 10 });
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export function parseYamlSecure(content, options) {
|
|
55
|
+
const maxAliasCount = options?.maxAliasCount ?? YAML_SECURITY_LIMITS.MAX_ALIAS_COUNT;
|
|
56
|
+
const maxDepth = options?.maxDepth ?? YAML_SECURITY_LIMITS.MAX_DEPTH;
|
|
57
|
+
const maxInputSize = options?.maxInputSize ?? YAML_SECURITY_LIMITS.MAX_INPUT_SIZE;
|
|
58
|
+
// Check input size
|
|
59
|
+
if (content.length > maxInputSize) {
|
|
60
|
+
throw new Error(`YAML input size (${content.length} bytes) exceeds maximum of ${maxInputSize} bytes`);
|
|
61
|
+
}
|
|
62
|
+
// Parse with alias protection
|
|
63
|
+
const parsed = yamlParse(content, {
|
|
64
|
+
maxAliasCount,
|
|
65
|
+
...options?.parseOptions,
|
|
66
|
+
});
|
|
67
|
+
// Validate nesting depth
|
|
68
|
+
validateDepth(parsed, maxDepth);
|
|
69
|
+
return parsed;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Parse YAML with strict security settings.
|
|
73
|
+
* Use this for parsing untrusted input.
|
|
74
|
+
*
|
|
75
|
+
* @param content - YAML content to parse
|
|
76
|
+
* @returns Parsed YAML value
|
|
77
|
+
* @throws Error if parsing fails or security limits are exceeded
|
|
78
|
+
*/
|
|
79
|
+
export function parseYamlStrict(content) {
|
|
80
|
+
return parseYamlSecure(content, {
|
|
81
|
+
maxAliasCount: 10,
|
|
82
|
+
maxDepth: 20,
|
|
83
|
+
maxInputSize: 1024 * 1024, // 1MB
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=yaml-parser.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic validation module for Bellwether.
|
|
3
|
+
*
|
|
4
|
+
* This module provides semantic type inference and validation for MCP tool
|
|
5
|
+
* parameters, enabling more intelligent testing of input validation.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import {
|
|
10
|
+
* inferSemanticType,
|
|
11
|
+
* validateSemanticValue,
|
|
12
|
+
* generateSemanticTests,
|
|
13
|
+
* } from './validation/index.js';
|
|
14
|
+
*
|
|
15
|
+
* // Infer semantic type from parameter name and description
|
|
16
|
+
* const inference = inferSemanticType('email', 'User email address');
|
|
17
|
+
* // { paramName: 'email', inferredType: 'email', confidence: 0.9, ... }
|
|
18
|
+
*
|
|
19
|
+
* // Validate a value against the inferred type
|
|
20
|
+
* const result = validateSemanticValue('email', 'invalid', 'email');
|
|
21
|
+
* // { isValid: false, issue: 'Invalid email format: invalid' }
|
|
22
|
+
*
|
|
23
|
+
* // Generate semantic validation tests for a tool
|
|
24
|
+
* const { tests, inferences } = generateSemanticTests(tool);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export type { SemanticType, SemanticInference, SemanticValidationResult, SemanticPatternDefinition, } from './semantic-types.js';
|
|
28
|
+
export { SEMANTIC_PATTERNS, getAllSemanticTypes, isSemanticType, } from './semantic-types.js';
|
|
29
|
+
export { inferSemanticType, validateSemanticValue, validateAllParameters, } from './semantic-validator.js';
|
|
30
|
+
export type { SemanticTestResult, SemanticTestOptions, } from './semantic-test-generator.js';
|
|
31
|
+
export { generateSemanticTests, getInvalidValuesForType, getTestableSemanticTypes, } from './semantic-test-generator.js';
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic validation module for Bellwether.
|
|
3
|
+
*
|
|
4
|
+
* This module provides semantic type inference and validation for MCP tool
|
|
5
|
+
* parameters, enabling more intelligent testing of input validation.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import {
|
|
10
|
+
* inferSemanticType,
|
|
11
|
+
* validateSemanticValue,
|
|
12
|
+
* generateSemanticTests,
|
|
13
|
+
* } from './validation/index.js';
|
|
14
|
+
*
|
|
15
|
+
* // Infer semantic type from parameter name and description
|
|
16
|
+
* const inference = inferSemanticType('email', 'User email address');
|
|
17
|
+
* // { paramName: 'email', inferredType: 'email', confidence: 0.9, ... }
|
|
18
|
+
*
|
|
19
|
+
* // Validate a value against the inferred type
|
|
20
|
+
* const result = validateSemanticValue('email', 'invalid', 'email');
|
|
21
|
+
* // { isValid: false, issue: 'Invalid email format: invalid' }
|
|
22
|
+
*
|
|
23
|
+
* // Generate semantic validation tests for a tool
|
|
24
|
+
* const { tests, inferences } = generateSemanticTests(tool);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
// Constants and utilities
|
|
28
|
+
export { SEMANTIC_PATTERNS, getAllSemanticTypes, isSemanticType, } from './semantic-types.js';
|
|
29
|
+
// Validation functions
|
|
30
|
+
export { inferSemanticType, validateSemanticValue, validateAllParameters, } from './semantic-validator.js';
|
|
31
|
+
export { generateSemanticTests, getInvalidValuesForType, getTestableSemanticTypes, } from './semantic-test-generator.js';
|
|
32
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate test cases based on semantic type inference.
|
|
3
|
+
*
|
|
4
|
+
* This module generates validation tests for parameters with inferred
|
|
5
|
+
* semantic types, testing that tools properly reject invalid values.
|
|
6
|
+
*/
|
|
7
|
+
import type { MCPTool } from '../transport/types.js';
|
|
8
|
+
import type { InterviewQuestion } from '../interview/types.js';
|
|
9
|
+
import type { SemanticType, SemanticInference } from './semantic-types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Result of generating semantic tests for a tool.
|
|
12
|
+
*/
|
|
13
|
+
export interface SemanticTestResult {
|
|
14
|
+
/** Generated test cases */
|
|
15
|
+
tests: InterviewQuestion[];
|
|
16
|
+
/** Semantic inferences for each parameter */
|
|
17
|
+
inferences: SemanticInference[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Options for semantic test generation.
|
|
21
|
+
*/
|
|
22
|
+
export interface SemanticTestOptions {
|
|
23
|
+
/** Minimum confidence threshold for generating tests (0-1, default: 0.5) */
|
|
24
|
+
minConfidence?: number;
|
|
25
|
+
/** Maximum invalid values to test per parameter (default: 2) */
|
|
26
|
+
maxInvalidValuesPerParam?: number;
|
|
27
|
+
/** Skip semantic tests entirely */
|
|
28
|
+
skipSemanticTests?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Generate semantic validation tests for a tool.
|
|
32
|
+
*
|
|
33
|
+
* Analyzes the tool's input schema, infers semantic types for parameters,
|
|
34
|
+
* and generates test cases with invalid values to verify proper validation.
|
|
35
|
+
*
|
|
36
|
+
* @param tool - The MCP tool to generate tests for
|
|
37
|
+
* @param options - Configuration options
|
|
38
|
+
* @returns Generated tests and semantic inferences
|
|
39
|
+
*/
|
|
40
|
+
export declare function generateSemanticTests(tool: MCPTool, options?: SemanticTestOptions): SemanticTestResult;
|
|
41
|
+
/**
|
|
42
|
+
* Get the invalid test values for a semantic type.
|
|
43
|
+
* Useful for external modules that need access to the test values.
|
|
44
|
+
*/
|
|
45
|
+
export declare function getInvalidValuesForType(type: SemanticType): readonly string[];
|
|
46
|
+
/**
|
|
47
|
+
* Get all semantic types that have invalid test values defined.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getTestableSemanticTypes(): SemanticType[];
|
|
50
|
+
//# sourceMappingURL=semantic-test-generator.d.ts.map
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate test cases based on semantic type inference.
|
|
3
|
+
*
|
|
4
|
+
* This module generates validation tests for parameters with inferred
|
|
5
|
+
* semantic types, testing that tools properly reject invalid values.
|
|
6
|
+
*/
|
|
7
|
+
import { inferSemanticType } from './semantic-validator.js';
|
|
8
|
+
import { SEMANTIC_VALIDATION } from '../constants.js';
|
|
9
|
+
/**
|
|
10
|
+
* Invalid test values for each semantic type.
|
|
11
|
+
* These values should be rejected by tools that properly validate input.
|
|
12
|
+
*/
|
|
13
|
+
const INVALID_VALUES = {
|
|
14
|
+
date_iso8601: ['not-a-date', '2024/01/15', '01-15-2024', '2024-13-45', ''],
|
|
15
|
+
date_month: ['not-a-month', '2024/01', '01-2024', '2024-13', ''],
|
|
16
|
+
datetime: ['not-a-datetime', '2024-01-15 10:30', 'tomorrow', 'now', ''],
|
|
17
|
+
timestamp: ['not-a-timestamp', '-12345', 'abc', '1.5.3', ''],
|
|
18
|
+
amount_currency: ['not-an-amount', 'free', 'hundred dollars', 'N/A', ''],
|
|
19
|
+
percentage: ['not-a-percentage', 'half', 'none', 'all', ''],
|
|
20
|
+
identifier: ['', ' ', '\n\t', '\x00'],
|
|
21
|
+
email: ['not-an-email', 'missing@domain', '@nodomain.com', 'no-at-sign', ''],
|
|
22
|
+
url: ['not-a-url', 'missing-protocol.com', '://no-scheme', 'http://', ''],
|
|
23
|
+
phone: ['not-a-phone', 'abc', '123', 'call-me', ''],
|
|
24
|
+
ip_address: ['not-an-ip', '999.999.999.999', 'localhost', '1.2.3.4.5', ''],
|
|
25
|
+
file_path: [], // Don't test - too OS-specific
|
|
26
|
+
json: ['not-json', '{invalid}', '{"missing": }', '{key: "no-quotes"}', ''],
|
|
27
|
+
base64: ['not-base64!!!', '====', 'has spaces', '@#$%', ''],
|
|
28
|
+
regex: ['[invalid', '(unclosed', '*invalid', '\\', ''],
|
|
29
|
+
unknown: [],
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Generate semantic validation tests for a tool.
|
|
33
|
+
*
|
|
34
|
+
* Analyzes the tool's input schema, infers semantic types for parameters,
|
|
35
|
+
* and generates test cases with invalid values to verify proper validation.
|
|
36
|
+
*
|
|
37
|
+
* @param tool - The MCP tool to generate tests for
|
|
38
|
+
* @param options - Configuration options
|
|
39
|
+
* @returns Generated tests and semantic inferences
|
|
40
|
+
*/
|
|
41
|
+
export function generateSemanticTests(tool, options = {}) {
|
|
42
|
+
const { minConfidence = SEMANTIC_VALIDATION.MIN_CONFIDENCE_THRESHOLD, maxInvalidValuesPerParam = SEMANTIC_VALIDATION.MAX_INVALID_VALUES_PER_PARAM, skipSemanticTests = false, } = options;
|
|
43
|
+
const tests = [];
|
|
44
|
+
const inferences = [];
|
|
45
|
+
if (skipSemanticTests) {
|
|
46
|
+
return { tests, inferences };
|
|
47
|
+
}
|
|
48
|
+
const schema = tool.inputSchema;
|
|
49
|
+
if (!schema?.properties) {
|
|
50
|
+
return { tests, inferences };
|
|
51
|
+
}
|
|
52
|
+
const requiredParams = schema.required ?? [];
|
|
53
|
+
for (const [paramName, propSchema] of Object.entries(schema.properties)) {
|
|
54
|
+
// Only infer semantic types for string parameters
|
|
55
|
+
if (propSchema.type !== 'string' && propSchema.type !== undefined) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Infer semantic type
|
|
59
|
+
const inference = inferSemanticType(paramName, propSchema.description, propSchema.format);
|
|
60
|
+
// Only generate tests for high-confidence inferences
|
|
61
|
+
if (inference.confidence < minConfidence || inference.inferredType === 'unknown') {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
inferences.push(inference);
|
|
65
|
+
// Get invalid values for this semantic type
|
|
66
|
+
const invalidValues = INVALID_VALUES[inference.inferredType];
|
|
67
|
+
if (invalidValues.length === 0)
|
|
68
|
+
continue;
|
|
69
|
+
// Build base args with required params
|
|
70
|
+
const baseArgs = {};
|
|
71
|
+
for (const req of requiredParams) {
|
|
72
|
+
if (req !== paramName) {
|
|
73
|
+
// Provide a valid placeholder for other required params
|
|
74
|
+
baseArgs[req] = generatePlaceholderValue(req, schema.properties[req]);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Generate tests with invalid semantic values
|
|
78
|
+
const valuesToTest = invalidValues.slice(0, maxInvalidValuesPerParam);
|
|
79
|
+
for (const invalidValue of valuesToTest) {
|
|
80
|
+
tests.push({
|
|
81
|
+
description: `Semantic validation: invalid ${formatSemanticType(inference.inferredType)} for "${paramName}"`,
|
|
82
|
+
category: 'error_handling',
|
|
83
|
+
args: {
|
|
84
|
+
...baseArgs,
|
|
85
|
+
[paramName]: invalidValue,
|
|
86
|
+
},
|
|
87
|
+
metadata: {
|
|
88
|
+
semanticType: inference.inferredType,
|
|
89
|
+
expectedBehavior: 'reject',
|
|
90
|
+
confidence: inference.confidence,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return { tests, inferences };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Generate a placeholder value for a required parameter.
|
|
99
|
+
* Used to provide valid values for other params when testing one param.
|
|
100
|
+
*/
|
|
101
|
+
function generatePlaceholderValue(paramName, propSchema) {
|
|
102
|
+
if (!propSchema)
|
|
103
|
+
return 'test';
|
|
104
|
+
const type = propSchema.type;
|
|
105
|
+
switch (type) {
|
|
106
|
+
case 'string':
|
|
107
|
+
return generateStringPlaceholder(paramName, propSchema);
|
|
108
|
+
case 'number':
|
|
109
|
+
case 'integer':
|
|
110
|
+
return 1;
|
|
111
|
+
case 'boolean':
|
|
112
|
+
return true;
|
|
113
|
+
case 'array':
|
|
114
|
+
return [];
|
|
115
|
+
case 'object':
|
|
116
|
+
return {};
|
|
117
|
+
default:
|
|
118
|
+
return 'test';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Generate a placeholder string value based on parameter name and format.
|
|
123
|
+
*/
|
|
124
|
+
function generateStringPlaceholder(paramName, propSchema) {
|
|
125
|
+
// Use format hint if available
|
|
126
|
+
if (propSchema.format === 'date')
|
|
127
|
+
return '2024-01-15';
|
|
128
|
+
if (propSchema.format === 'date-time')
|
|
129
|
+
return '2024-01-15T12:00:00Z';
|
|
130
|
+
if (propSchema.format === 'email')
|
|
131
|
+
return 'test@example.com';
|
|
132
|
+
if (propSchema.format === 'uri' || propSchema.format === 'url')
|
|
133
|
+
return 'https://example.com';
|
|
134
|
+
if (propSchema.format === 'uuid')
|
|
135
|
+
return '550e8400-e29b-41d4-a716-446655440000';
|
|
136
|
+
// Infer from name
|
|
137
|
+
const lowerName = paramName.toLowerCase();
|
|
138
|
+
if (lowerName.includes('date'))
|
|
139
|
+
return '2024-01-15';
|
|
140
|
+
if (lowerName.includes('email'))
|
|
141
|
+
return 'test@example.com';
|
|
142
|
+
if (lowerName.includes('url') || lowerName.includes('uri'))
|
|
143
|
+
return 'https://example.com';
|
|
144
|
+
if (lowerName.includes('id'))
|
|
145
|
+
return 'test-id-123';
|
|
146
|
+
if (lowerName.includes('path') || lowerName.includes('file'))
|
|
147
|
+
return '/tmp/test';
|
|
148
|
+
if (lowerName.includes('phone'))
|
|
149
|
+
return '+1234567890';
|
|
150
|
+
return 'test';
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Format semantic type for display in test descriptions.
|
|
154
|
+
*/
|
|
155
|
+
function formatSemanticType(type) {
|
|
156
|
+
return type
|
|
157
|
+
.split('_')
|
|
158
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
159
|
+
.join(' ');
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get the invalid test values for a semantic type.
|
|
163
|
+
* Useful for external modules that need access to the test values.
|
|
164
|
+
*/
|
|
165
|
+
export function getInvalidValuesForType(type) {
|
|
166
|
+
return INVALID_VALUES[type] ?? [];
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get all semantic types that have invalid test values defined.
|
|
170
|
+
*/
|
|
171
|
+
export function getTestableSemanticTypes() {
|
|
172
|
+
return Object.entries(INVALID_VALUES)
|
|
173
|
+
.filter(([, values]) => values.length > 0)
|
|
174
|
+
.map(([type]) => type);
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=semantic-test-generator.js.map
|