@exaudeus/workrail 0.15.0 → 0.16.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.
@@ -550,8 +550,8 @@
550
550
  "bytes": 1748
551
551
  },
552
552
  "mcp/handlers/workflow.js": {
553
- "sha256": "99ff7aac9705ef540cc5681515cf2aa9650d886761f7ae69f829ff87ac772756",
554
- "bytes": 8246
553
+ "sha256": "7ebd383922c6f238c35c376c8a0aa87b0969a80bb4e7c48e704a8480fd974bb4",
554
+ "bytes": 8254
555
555
  },
556
556
  "mcp/index.d.ts": {
557
557
  "sha256": "525b4247cf90ba3af66769462bcfaab5dbf38ee8c49d2a9ceec1e4b38e33511b",
@@ -574,8 +574,8 @@
574
574
  "bytes": 168
575
575
  },
576
576
  "mcp/server.js": {
577
- "sha256": "41c61033d821f9794cada0bb40058fa220fa11175897326ae597df422a1fcd0b",
578
- "bytes": 13552
577
+ "sha256": "cf128711a6d74bb604653431c9e3bac4fac9fed94bd4da8326438e86809501e3",
578
+ "bytes": 13866
579
579
  },
580
580
  "mcp/tool-description-provider.d.ts": {
581
581
  "sha256": "1d46abc3112e11b68e57197e846f5708293ec9b2281fa71a9124ee2aad71e41b",
@@ -590,8 +590,8 @@
590
590
  "bytes": 132
591
591
  },
592
592
  "mcp/tool-descriptions.js": {
593
- "sha256": "06a6eff077301d66a90b6240fbebae166ddfdcd3aa9177d5cfb48d6e0a31698c",
594
- "bytes": 7792
593
+ "sha256": "c8150119782dadd286e8430e9377b0b727891c414be2396e314423713939d6eb",
594
+ "bytes": 7980
595
595
  },
596
596
  "mcp/tool-factory.d.ts": {
597
597
  "sha256": "0fe3c6b863b2d7aef0c3d659ff54f3a9ee8a0a3c2005b6565d2f8ad517bc7211",
@@ -602,12 +602,12 @@
602
602
  "bytes": 479
603
603
  },
604
604
  "mcp/tools.d.ts": {
605
- "sha256": "f11991cdc3cdbd2912c38252541818adaca8a99c08cc7c1aaa37636ae112cfd6",
606
- "bytes": 5952
605
+ "sha256": "8474e810cae37197d5968be4c3dfb9751ba2b09fe8a7f39e0e7dcc414af4bdb5",
606
+ "bytes": 5976
607
607
  },
608
608
  "mcp/tools.js": {
609
- "sha256": "34522d0b078477627fe068130d3b0a5d4d8cc9fc90599a583d979080d632f544",
610
- "bytes": 7688
609
+ "sha256": "0af59932b32bad5ebc9cbc925279d325350c91b43085561d0d218035250b641a",
610
+ "bytes": 8020
611
611
  },
612
612
  "mcp/types.d.ts": {
613
613
  "sha256": "4ab4a4af1eeedf9ba9bcdc70476a5adcc24ce05b3d7d715d70979052b1eb7246",
@@ -638,8 +638,8 @@
638
638
  "bytes": 2579
639
639
  },
640
640
  "mcp/v2/tools.js": {
641
- "sha256": "9e934ea8a5f83bffc68d343970292267b207f67d72a5aa824348967e53777820",
642
- "bytes": 2333
641
+ "sha256": "4b0d5d1c019d3f747b0f4211a606d1aba4944f9e570ae9fecc6831987a6cc16f",
642
+ "bytes": 2537
643
643
  },
644
644
  "mcp/validation/bounded-json.d.ts": {
645
645
  "sha256": "82203ac6123d5c6989606c3b5405aaea99ab829c8958835f9ae3ba45b8bc8fd5",
@@ -649,6 +649,54 @@
649
649
  "sha256": "0134fd92e1b160f1b57230d9f8a471044858af43484206f911619cf7159e3f0d",
650
650
  "bytes": 834
651
651
  },
652
+ "mcp/validation/index.d.ts": {
653
+ "sha256": "3e3f12357fd8214470d111454e4002338e5eb93329b5a3758664db51e44c12ec",
654
+ "bytes": 944
655
+ },
656
+ "mcp/validation/index.js": {
657
+ "sha256": "dccd3a2dc7e486afd27ee44f77303486f60cc840563821b97ac341f9cad6650c",
658
+ "bytes": 4445
659
+ },
660
+ "mcp/validation/schema-introspection.d.ts": {
661
+ "sha256": "7e0262e76234dd37079156027e95a30987b8949351f3e9ec0fd7b2be093a159d",
662
+ "bytes": 713
663
+ },
664
+ "mcp/validation/schema-introspection.js": {
665
+ "sha256": "850c09a3c01a5f22440ebc34236c393f3b428748210f5277258a9905cb847d71",
666
+ "bytes": 5293
667
+ },
668
+ "mcp/validation/string-similarity.d.ts": {
669
+ "sha256": "4326210a768a526336b54d4ea20a128a939d92f53e8b2a5a33da06b5372d196a",
670
+ "bytes": 671
671
+ },
672
+ "mcp/validation/string-similarity.js": {
673
+ "sha256": "fafdb80673ad56336009e562cd5dccd93486dd94fa78acbb923cdc47ba63becf",
674
+ "bytes": 2627
675
+ },
676
+ "mcp/validation/suggestion-config.d.ts": {
677
+ "sha256": "70b8395db74ec18bb1ef2309dd16516345b75839d2793bf9c5bfbd1e1d1baa0e",
678
+ "bytes": 388
679
+ },
680
+ "mcp/validation/suggestion-config.js": {
681
+ "sha256": "efda43e48812979d0ddae2abe23809b6b5a3e5b955e74ca5a67716933e468db4",
682
+ "bytes": 592
683
+ },
684
+ "mcp/validation/suggestion-generator.d.ts": {
685
+ "sha256": "491d983f4a03516fc0ba09ff40da2c859ec600f29a41093b0359ba549c7882cc",
686
+ "bytes": 450
687
+ },
688
+ "mcp/validation/suggestion-generator.js": {
689
+ "sha256": "e952a4d3cb569222cde1bd01dd9d5be887ee394ba007478bbb446fa177172859",
690
+ "bytes": 4075
691
+ },
692
+ "mcp/validation/suggestion-types.d.ts": {
693
+ "sha256": "b93ae2e42f4b24789dcbe19db31a41af9534ad0dca85635339c2a10db42e298b",
694
+ "bytes": 1333
695
+ },
696
+ "mcp/validation/suggestion-types.js": {
697
+ "sha256": "c7753960a199508a8a59f8030c4240a076857a3e5926efadc01e808f08d7ff3a",
698
+ "bytes": 729
699
+ },
652
700
  "mcp/validation/workflow-next-prevalidate.d.ts": {
653
701
  "sha256": "179058225dfb17f4be02d6105bbacdaa99f1441cfc25062b38d8283f0bf35b5a",
654
702
  "bytes": 254
@@ -65,7 +65,7 @@ async function handleWorkflowGet(input, ctx) {
65
65
  try {
66
66
  const { createGetWorkflow } = await Promise.resolve().then(() => __importStar(require('../../application/use-cases/get-workflow.js')));
67
67
  const getWorkflowUseCase = createGetWorkflow(ctx.workflowService);
68
- const result = await withTimeout(getWorkflowUseCase(input.id, input.mode), TIMEOUT_MS, 'workflow_get');
68
+ const result = await withTimeout(getWorkflowUseCase(input.workflowId, input.mode), TIMEOUT_MS, 'workflow_get');
69
69
  if (result.isErr()) {
70
70
  const mapped = (0, error_mapper_js_1.mapDomainErrorToToolError)(result.error);
71
71
  return mapped;
@@ -42,6 +42,7 @@ const types_js_1 = require("./types.js");
42
42
  const tool_factory_js_1 = require("./tool-factory.js");
43
43
  const workflow_next_prevalidate_js_1 = require("./validation/workflow-next-prevalidate.js");
44
44
  const bounded_json_js_1 = require("./validation/bounded-json.js");
45
+ const index_js_1 = require("./validation/index.js");
45
46
  const tools_js_1 = require("./tools.js");
46
47
  const tool_registry_js_1 = require("./v2/tool-registry.js");
47
48
  const workflow_js_1 = require("./handlers/workflow.js");
@@ -133,11 +134,14 @@ function createHandler(schema, handler) {
133
134
  return async (args, ctx) => {
134
135
  const parseResult = schema.safeParse(args);
135
136
  if (!parseResult.success) {
137
+ const suggestionResult = (0, index_js_1.generateSuggestions)(args, schema, index_js_1.DEFAULT_SUGGESTION_CONFIG);
138
+ const suggestionDetails = (0, index_js_1.formatSuggestionDetails)(suggestionResult);
136
139
  return toMcpResult((0, types_js_1.errNotRetryable)('VALIDATION_ERROR', 'Invalid input', {
137
140
  validationErrors: parseResult.error.errors.map(e => ({
138
141
  path: e.path.join('.'),
139
142
  message: e.message,
140
143
  })),
144
+ ...suggestionDetails,
141
145
  }));
142
146
  }
143
147
  return toMcpResult(await handler(parseResult.data, ctx));
@@ -10,7 +10,11 @@ Your process:
10
10
  2. Analyze the returned descriptions to find a match for the user's goal.
11
11
  3. If a good match is found, suggest it to the user and use preview_workflow to start.
12
12
  4. If NO match is found, inform the user and then attempt to solve the task using your general abilities.`,
13
- preview_workflow: `Retrieves workflow information with configurable detail level. Supports progressive disclosure to prevent "workflow spoiling" while providing necessary context for workflow selection and initiation.`,
13
+ preview_workflow: `Retrieves workflow information with configurable detail level. Supports progressive disclosure to prevent "workflow spoiling" while providing necessary context for workflow selection and initiation.
14
+
15
+ Parameters:
16
+ - workflowId: The unique identifier of the workflow to retrieve
17
+ - mode (optional): 'metadata' for overview only, 'preview' (default) for first step`,
14
18
  advance_workflow: `Executes one workflow step at a time by returning the next eligible step and an updated execution state.
15
19
 
16
20
  Inputs:
@@ -75,7 +79,9 @@ By retrieving a workflow, you agree to:
75
79
 
76
80
  The workflow content is the user's will expressed as structured steps. Treat each step as a direct instruction from the user.
77
81
 
78
- Returns: Workflow metadata and first step. Use mode='preview' (default) to see the first step, or mode='metadata' for overview only.`,
82
+ Parameters:
83
+ - workflowId: The unique identifier of the workflow to retrieve
84
+ - mode (optional): 'metadata' for overview only, 'preview' (default) for first step`,
79
85
  advance_workflow: `Get your next MANDATORY INSTRUCTION from the active workflow.
80
86
 
81
87
  The step returned is a DIRECT INSTRUCTION from the user. You MUST:
@@ -3,13 +3,13 @@ export type { ToolAnnotations, ToolDefinition } from './tool-factory.js';
3
3
  export declare const WorkflowListInput: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
4
4
  export type WorkflowListInput = z.infer<typeof WorkflowListInput>;
5
5
  export declare const WorkflowGetInput: z.ZodObject<{
6
- id: z.ZodString;
6
+ workflowId: z.ZodString;
7
7
  mode: z.ZodDefault<z.ZodEnum<["metadata", "preview"]>>;
8
8
  }, "strip", z.ZodTypeAny, {
9
- id: string;
9
+ workflowId: string;
10
10
  mode: "metadata" | "preview";
11
11
  }, {
12
- id: string;
12
+ workflowId: string;
13
13
  mode?: "metadata" | "preview" | undefined;
14
14
  }>;
15
15
  export type WorkflowGetInput = z.infer<typeof WorkflowGetInput>;
package/dist/mcp/tools.js CHANGED
@@ -6,9 +6,9 @@ const state_js_1 = require("../domain/execution/state.js");
6
6
  const event_js_1 = require("../domain/execution/event.js");
7
7
  exports.WorkflowListInput = zod_1.z.object({});
8
8
  exports.WorkflowGetInput = zod_1.z.object({
9
- id: zod_1.z
9
+ workflowId: zod_1.z
10
10
  .string()
11
- .regex(/^[A-Za-z0-9_-]+$/, 'ID must contain only letters, numbers, hyphens, and underscores')
11
+ .regex(/^[A-Za-z0-9_-]+$/, 'Workflow ID must contain only letters, numbers, hyphens, and underscores')
12
12
  .describe('The unique identifier of the workflow to retrieve'),
13
13
  mode: zod_1.z
14
14
  .enum(['metadata', 'preview'])
@@ -97,6 +97,7 @@ exports.WORKFLOW_TOOL_TITLES = {
97
97
  exports.CreateSessionInput = zod_1.z.object({
98
98
  workflowId: zod_1.z
99
99
  .string()
100
+ .regex(/^[A-Za-z0-9_-]+$/, 'Workflow ID must contain only letters, numbers, hyphens, and underscores')
100
101
  .describe('Workflow identifier (e.g., "bug-investigation", "mr-review")'),
101
102
  sessionId: zod_1.z
102
103
  .string()
@@ -107,14 +108,14 @@ exports.CreateSessionInput = zod_1.z.object({
107
108
  .describe('Initial session data. Can include dashboard, phases, etc.'),
108
109
  });
109
110
  exports.UpdateSessionInput = zod_1.z.object({
110
- workflowId: zod_1.z.string().describe('Workflow identifier'),
111
+ workflowId: zod_1.z.string().regex(/^[A-Za-z0-9_-]+$/, 'Workflow ID must contain only letters, numbers, hyphens, and underscores').describe('Workflow identifier'),
111
112
  sessionId: zod_1.z.string().describe('Session identifier'),
112
113
  updates: zod_1.z
113
114
  .record(zod_1.z.unknown())
114
115
  .describe('Data to merge into session. Supports nested updates via dot notation.'),
115
116
  });
116
117
  exports.ReadSessionInput = zod_1.z.object({
117
- workflowId: zod_1.z.string().describe('Workflow identifier'),
118
+ workflowId: zod_1.z.string().regex(/^[A-Za-z0-9_-]+$/, 'Workflow ID must contain only letters, numbers, hyphens, and underscores').describe('Workflow identifier'),
118
119
  sessionId: zod_1.z.string().describe('Session identifier'),
119
120
  path: zod_1.z
120
121
  .string()
@@ -4,11 +4,11 @@ exports.V2_TOOL_ANNOTATIONS = exports.V2_TOOL_TITLES = exports.V2ContinueWorkflo
4
4
  const zod_1 = require("zod");
5
5
  exports.V2ListWorkflowsInput = zod_1.z.object({});
6
6
  exports.V2InspectWorkflowInput = zod_1.z.object({
7
- workflowId: zod_1.z.string().min(1).describe('The workflow ID to inspect'),
7
+ workflowId: zod_1.z.string().min(1).regex(/^[A-Za-z0-9_-]+$/, 'Workflow ID must contain only letters, numbers, hyphens, and underscores').describe('The workflow ID to inspect'),
8
8
  mode: zod_1.z.enum(['metadata', 'preview']).default('preview').describe('Detail level'),
9
9
  });
10
10
  exports.V2StartWorkflowInput = zod_1.z.object({
11
- workflowId: zod_1.z.string().min(1).describe('The workflow ID to start'),
11
+ workflowId: zod_1.z.string().min(1).regex(/^[A-Za-z0-9_-]+$/, 'Workflow ID must contain only letters, numbers, hyphens, and underscores').describe('The workflow ID to start'),
12
12
  context: zod_1.z.record(zod_1.z.unknown()).optional().describe('External context inputs (conditions, parameters). Do not include workflow progress state.'),
13
13
  });
14
14
  exports.V2ContinueWorkflowInput = zod_1.z.object({
@@ -0,0 +1,7 @@
1
+ export type { Similarity, ValidationSuggestion, UnknownKeySuggestion, MissingRequiredSuggestion, InvalidEnumSuggestion, SuggestionResult, } from './suggestion-types.js';
2
+ export { similarity, EMPTY_SUGGESTION_RESULT, isUnknownKeySuggestion, isMissingRequiredSuggestion, isInvalidEnumSuggestion, } from './suggestion-types.js';
3
+ export type { SuggestionConfig } from './suggestion-config.js';
4
+ export { DEFAULT_SUGGESTION_CONFIG, MINIMAL_SUGGESTION_CONFIG } from './suggestion-config.js';
5
+ export { levenshteinDistance, computeSimilarity, computeSimilarityIgnoreCase, findClosestMatch, findAllMatches, type ClosestMatch, } from './string-similarity.js';
6
+ export { extractExpectedKeys, extractRequiredKeys, findUnknownKeys, findMissingRequiredKeys, generateExampleValue, generateTemplate, extractEnumValues, } from './schema-introspection.js';
7
+ export { generateSuggestions, formatSuggestionDetails, hasSuggestions, } from './suggestion-generator.js';
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasSuggestions = exports.formatSuggestionDetails = exports.generateSuggestions = exports.extractEnumValues = exports.generateTemplate = exports.generateExampleValue = exports.findMissingRequiredKeys = exports.findUnknownKeys = exports.extractRequiredKeys = exports.extractExpectedKeys = exports.findAllMatches = exports.findClosestMatch = exports.computeSimilarityIgnoreCase = exports.computeSimilarity = exports.levenshteinDistance = exports.MINIMAL_SUGGESTION_CONFIG = exports.DEFAULT_SUGGESTION_CONFIG = exports.isInvalidEnumSuggestion = exports.isMissingRequiredSuggestion = exports.isUnknownKeySuggestion = exports.EMPTY_SUGGESTION_RESULT = exports.similarity = void 0;
4
+ var suggestion_types_js_1 = require("./suggestion-types.js");
5
+ Object.defineProperty(exports, "similarity", { enumerable: true, get: function () { return suggestion_types_js_1.similarity; } });
6
+ Object.defineProperty(exports, "EMPTY_SUGGESTION_RESULT", { enumerable: true, get: function () { return suggestion_types_js_1.EMPTY_SUGGESTION_RESULT; } });
7
+ Object.defineProperty(exports, "isUnknownKeySuggestion", { enumerable: true, get: function () { return suggestion_types_js_1.isUnknownKeySuggestion; } });
8
+ Object.defineProperty(exports, "isMissingRequiredSuggestion", { enumerable: true, get: function () { return suggestion_types_js_1.isMissingRequiredSuggestion; } });
9
+ Object.defineProperty(exports, "isInvalidEnumSuggestion", { enumerable: true, get: function () { return suggestion_types_js_1.isInvalidEnumSuggestion; } });
10
+ var suggestion_config_js_1 = require("./suggestion-config.js");
11
+ Object.defineProperty(exports, "DEFAULT_SUGGESTION_CONFIG", { enumerable: true, get: function () { return suggestion_config_js_1.DEFAULT_SUGGESTION_CONFIG; } });
12
+ Object.defineProperty(exports, "MINIMAL_SUGGESTION_CONFIG", { enumerable: true, get: function () { return suggestion_config_js_1.MINIMAL_SUGGESTION_CONFIG; } });
13
+ var string_similarity_js_1 = require("./string-similarity.js");
14
+ Object.defineProperty(exports, "levenshteinDistance", { enumerable: true, get: function () { return string_similarity_js_1.levenshteinDistance; } });
15
+ Object.defineProperty(exports, "computeSimilarity", { enumerable: true, get: function () { return string_similarity_js_1.computeSimilarity; } });
16
+ Object.defineProperty(exports, "computeSimilarityIgnoreCase", { enumerable: true, get: function () { return string_similarity_js_1.computeSimilarityIgnoreCase; } });
17
+ Object.defineProperty(exports, "findClosestMatch", { enumerable: true, get: function () { return string_similarity_js_1.findClosestMatch; } });
18
+ Object.defineProperty(exports, "findAllMatches", { enumerable: true, get: function () { return string_similarity_js_1.findAllMatches; } });
19
+ var schema_introspection_js_1 = require("./schema-introspection.js");
20
+ Object.defineProperty(exports, "extractExpectedKeys", { enumerable: true, get: function () { return schema_introspection_js_1.extractExpectedKeys; } });
21
+ Object.defineProperty(exports, "extractRequiredKeys", { enumerable: true, get: function () { return schema_introspection_js_1.extractRequiredKeys; } });
22
+ Object.defineProperty(exports, "findUnknownKeys", { enumerable: true, get: function () { return schema_introspection_js_1.findUnknownKeys; } });
23
+ Object.defineProperty(exports, "findMissingRequiredKeys", { enumerable: true, get: function () { return schema_introspection_js_1.findMissingRequiredKeys; } });
24
+ Object.defineProperty(exports, "generateExampleValue", { enumerable: true, get: function () { return schema_introspection_js_1.generateExampleValue; } });
25
+ Object.defineProperty(exports, "generateTemplate", { enumerable: true, get: function () { return schema_introspection_js_1.generateTemplate; } });
26
+ Object.defineProperty(exports, "extractEnumValues", { enumerable: true, get: function () { return schema_introspection_js_1.extractEnumValues; } });
27
+ var suggestion_generator_js_1 = require("./suggestion-generator.js");
28
+ Object.defineProperty(exports, "generateSuggestions", { enumerable: true, get: function () { return suggestion_generator_js_1.generateSuggestions; } });
29
+ Object.defineProperty(exports, "formatSuggestionDetails", { enumerable: true, get: function () { return suggestion_generator_js_1.formatSuggestionDetails; } });
30
+ Object.defineProperty(exports, "hasSuggestions", { enumerable: true, get: function () { return suggestion_generator_js_1.hasSuggestions; } });
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ export declare function extractExpectedKeys(schema: z.ZodType): readonly string[];
3
+ export declare function extractRequiredKeys(schema: z.ZodType): readonly string[];
4
+ export declare function findUnknownKeys(args: unknown, schema: z.ZodType): readonly string[];
5
+ export declare function findMissingRequiredKeys(args: unknown, schema: z.ZodType): readonly string[];
6
+ export declare function generateExampleValue(schema: z.ZodType, depth?: number, maxDepth?: number): unknown;
7
+ export declare function generateTemplate(schema: z.ZodType, maxDepth?: number): Readonly<Record<string, unknown>> | null;
8
+ export declare function extractEnumValues(schema: z.ZodType, path: string): readonly string[];
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractExpectedKeys = extractExpectedKeys;
4
+ exports.extractRequiredKeys = extractRequiredKeys;
5
+ exports.findUnknownKeys = findUnknownKeys;
6
+ exports.findMissingRequiredKeys = findMissingRequiredKeys;
7
+ exports.generateExampleValue = generateExampleValue;
8
+ exports.generateTemplate = generateTemplate;
9
+ exports.extractEnumValues = extractEnumValues;
10
+ const zod_1 = require("zod");
11
+ function extractExpectedKeys(schema) {
12
+ if (schema instanceof zod_1.z.ZodObject) {
13
+ return Object.keys(schema._def.shape());
14
+ }
15
+ return [];
16
+ }
17
+ function extractRequiredKeys(schema) {
18
+ if (!(schema instanceof zod_1.z.ZodObject)) {
19
+ return [];
20
+ }
21
+ const shape = schema._def.shape();
22
+ const required = [];
23
+ for (const [key, value] of Object.entries(shape)) {
24
+ const field = value;
25
+ if (!(field instanceof zod_1.z.ZodOptional) && !(field instanceof zod_1.z.ZodDefault)) {
26
+ required.push(key);
27
+ }
28
+ }
29
+ return required;
30
+ }
31
+ function findUnknownKeys(args, schema) {
32
+ if (typeof args !== 'object' || args === null) {
33
+ return [];
34
+ }
35
+ const expectedKeys = new Set(extractExpectedKeys(schema));
36
+ const providedKeys = Object.keys(args);
37
+ return providedKeys.filter(key => !expectedKeys.has(key));
38
+ }
39
+ function findMissingRequiredKeys(args, schema) {
40
+ if (typeof args !== 'object' || args === null) {
41
+ return extractRequiredKeys(schema);
42
+ }
43
+ const providedKeys = new Set(Object.keys(args));
44
+ const requiredKeys = extractRequiredKeys(schema);
45
+ return requiredKeys.filter(key => !providedKeys.has(key));
46
+ }
47
+ function generateExampleValue(schema, depth = 0, maxDepth = 3) {
48
+ if (depth > maxDepth) {
49
+ return '...';
50
+ }
51
+ if (schema instanceof zod_1.z.ZodDefault) {
52
+ return schema._def.defaultValue();
53
+ }
54
+ if (schema instanceof zod_1.z.ZodOptional) {
55
+ return generateExampleValue(schema._def.innerType, depth, maxDepth);
56
+ }
57
+ if (schema instanceof zod_1.z.ZodObject) {
58
+ const shape = schema._def.shape();
59
+ const result = {};
60
+ for (const [key, value] of Object.entries(shape)) {
61
+ const field = value;
62
+ if (field instanceof zod_1.z.ZodOptional)
63
+ continue;
64
+ result[key] = generateExampleValue(field, depth + 1, maxDepth);
65
+ }
66
+ return result;
67
+ }
68
+ if (schema instanceof zod_1.z.ZodDiscriminatedUnion) {
69
+ const options = schema._def.options;
70
+ if (options.length > 0) {
71
+ return generateExampleValue(options[0], depth + 1, maxDepth);
72
+ }
73
+ return {};
74
+ }
75
+ if (schema instanceof zod_1.z.ZodString) {
76
+ const description = schema._def.description;
77
+ if (description) {
78
+ return `<${description}>`;
79
+ }
80
+ return '<string>';
81
+ }
82
+ if (schema instanceof zod_1.z.ZodNumber) {
83
+ return '<number>';
84
+ }
85
+ if (schema instanceof zod_1.z.ZodBoolean) {
86
+ return '<boolean>';
87
+ }
88
+ if (schema instanceof zod_1.z.ZodArray) {
89
+ return [];
90
+ }
91
+ if (schema instanceof zod_1.z.ZodEnum) {
92
+ const values = schema._def.values;
93
+ if (values.length > 0) {
94
+ return values[0];
95
+ }
96
+ return '<enum>';
97
+ }
98
+ if (schema instanceof zod_1.z.ZodLiteral) {
99
+ return schema._def.value;
100
+ }
101
+ if (schema instanceof zod_1.z.ZodRecord) {
102
+ return {};
103
+ }
104
+ if (schema instanceof zod_1.z.ZodUnknown || schema instanceof zod_1.z.ZodAny) {
105
+ return '<any>';
106
+ }
107
+ if (schema instanceof zod_1.z.ZodEffects) {
108
+ return generateExampleValue(schema._def.schema, depth, maxDepth);
109
+ }
110
+ return '<unknown>';
111
+ }
112
+ function generateTemplate(schema, maxDepth = 3) {
113
+ if (!(schema instanceof zod_1.z.ZodObject)) {
114
+ return null;
115
+ }
116
+ const example = generateExampleValue(schema, 0, maxDepth);
117
+ if (typeof example === 'object' && example !== null) {
118
+ return example;
119
+ }
120
+ return null;
121
+ }
122
+ function extractEnumValues(schema, path) {
123
+ const parts = path.split('.');
124
+ let current = schema;
125
+ for (const part of parts) {
126
+ if (current instanceof zod_1.z.ZodObject) {
127
+ const shape = current._def.shape();
128
+ const field = shape[part];
129
+ if (!field)
130
+ return [];
131
+ current = field;
132
+ }
133
+ else if (current instanceof zod_1.z.ZodOptional) {
134
+ current = current._def.innerType;
135
+ if (current instanceof zod_1.z.ZodObject) {
136
+ const shape = current._def.shape();
137
+ const field = shape[part];
138
+ if (!field)
139
+ return [];
140
+ current = field;
141
+ }
142
+ else {
143
+ return [];
144
+ }
145
+ }
146
+ else {
147
+ return [];
148
+ }
149
+ }
150
+ if (current instanceof zod_1.z.ZodOptional || current instanceof zod_1.z.ZodDefault) {
151
+ current = current._def.innerType;
152
+ }
153
+ if (current instanceof zod_1.z.ZodEnum) {
154
+ return current._def.values;
155
+ }
156
+ if (current instanceof zod_1.z.ZodLiteral) {
157
+ const value = current._def.value;
158
+ if (typeof value === 'string') {
159
+ return [value];
160
+ }
161
+ }
162
+ return [];
163
+ }
@@ -0,0 +1,10 @@
1
+ import { type Similarity } from './suggestion-types.js';
2
+ export declare function levenshteinDistance(a: string, b: string): number;
3
+ export declare function computeSimilarity(a: string, b: string): Similarity;
4
+ export declare function computeSimilarityIgnoreCase(a: string, b: string): Similarity;
5
+ export interface ClosestMatch {
6
+ readonly match: string;
7
+ readonly score: Similarity;
8
+ }
9
+ export declare function findClosestMatch(input: string, candidates: readonly string[], threshold: Similarity): ClosestMatch | null;
10
+ export declare function findAllMatches(input: string, candidates: readonly string[], threshold: Similarity, limit: number): readonly ClosestMatch[];
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.levenshteinDistance = levenshteinDistance;
4
+ exports.computeSimilarity = computeSimilarity;
5
+ exports.computeSimilarityIgnoreCase = computeSimilarityIgnoreCase;
6
+ exports.findClosestMatch = findClosestMatch;
7
+ exports.findAllMatches = findAllMatches;
8
+ const suggestion_types_js_1 = require("./suggestion-types.js");
9
+ function levenshteinDistance(a, b) {
10
+ if (a.length > b.length) {
11
+ [a, b] = [b, a];
12
+ }
13
+ const m = a.length;
14
+ const n = b.length;
15
+ if (m === 0)
16
+ return n;
17
+ if (n === 0)
18
+ return m;
19
+ let prevRow = new Array(m + 1);
20
+ let currRow = new Array(m + 1);
21
+ for (let i = 0; i <= m; i++) {
22
+ prevRow[i] = i;
23
+ }
24
+ for (let j = 1; j <= n; j++) {
25
+ currRow[0] = j;
26
+ for (let i = 1; i <= m; i++) {
27
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
28
+ currRow[i] = Math.min(prevRow[i] + 1, currRow[i - 1] + 1, prevRow[i - 1] + cost);
29
+ }
30
+ [prevRow, currRow] = [currRow, prevRow];
31
+ }
32
+ return prevRow[m];
33
+ }
34
+ function computeSimilarity(a, b) {
35
+ if (a === b)
36
+ return (0, suggestion_types_js_1.similarity)(1);
37
+ if (a.length === 0 || b.length === 0)
38
+ return (0, suggestion_types_js_1.similarity)(0);
39
+ const distance = levenshteinDistance(a, b);
40
+ const maxLength = Math.max(a.length, b.length);
41
+ return (0, suggestion_types_js_1.similarity)(1 - distance / maxLength);
42
+ }
43
+ function computeSimilarityIgnoreCase(a, b) {
44
+ return computeSimilarity(a.toLowerCase(), b.toLowerCase());
45
+ }
46
+ function findClosestMatch(input, candidates, threshold) {
47
+ if (candidates.length === 0)
48
+ return null;
49
+ let bestMatch = null;
50
+ let bestScore = (0, suggestion_types_js_1.similarity)(0);
51
+ for (const candidate of candidates) {
52
+ const score = computeSimilarityIgnoreCase(input, candidate);
53
+ if (score > bestScore && score >= threshold) {
54
+ bestScore = score;
55
+ bestMatch = candidate;
56
+ }
57
+ }
58
+ if (bestMatch === null)
59
+ return null;
60
+ return { match: bestMatch, score: bestScore };
61
+ }
62
+ function findAllMatches(input, candidates, threshold, limit) {
63
+ const matches = [];
64
+ for (const candidate of candidates) {
65
+ const score = computeSimilarityIgnoreCase(input, candidate);
66
+ if (score >= threshold) {
67
+ matches.push({ match: candidate, score });
68
+ }
69
+ }
70
+ matches.sort((a, b) => {
71
+ if (b.score !== a.score)
72
+ return b.score - a.score;
73
+ return a.match.localeCompare(b.match);
74
+ });
75
+ return matches.slice(0, limit);
76
+ }
@@ -0,0 +1,9 @@
1
+ import { type Similarity } from './suggestion-types.js';
2
+ export interface SuggestionConfig {
3
+ readonly similarityThreshold: Similarity;
4
+ readonly maxSuggestions: number;
5
+ readonly includeTemplate: boolean;
6
+ readonly maxTemplateDepth: number;
7
+ }
8
+ export declare const DEFAULT_SUGGESTION_CONFIG: SuggestionConfig;
9
+ export declare const MINIMAL_SUGGESTION_CONFIG: SuggestionConfig;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MINIMAL_SUGGESTION_CONFIG = exports.DEFAULT_SUGGESTION_CONFIG = void 0;
4
+ const suggestion_types_js_1 = require("./suggestion-types.js");
5
+ exports.DEFAULT_SUGGESTION_CONFIG = {
6
+ similarityThreshold: (0, suggestion_types_js_1.similarity)(0.6),
7
+ maxSuggestions: 3,
8
+ includeTemplate: true,
9
+ maxTemplateDepth: 3,
10
+ };
11
+ exports.MINIMAL_SUGGESTION_CONFIG = {
12
+ similarityThreshold: (0, suggestion_types_js_1.similarity)(0.7),
13
+ maxSuggestions: 1,
14
+ includeTemplate: false,
15
+ maxTemplateDepth: 1,
16
+ };
@@ -0,0 +1,6 @@
1
+ import { z } from 'zod';
2
+ import type { SuggestionConfig } from './suggestion-config.js';
3
+ import type { SuggestionResult } from './suggestion-types.js';
4
+ export declare function generateSuggestions(args: unknown, schema: z.ZodType, config: SuggestionConfig): SuggestionResult;
5
+ export declare function formatSuggestionDetails(result: SuggestionResult): Record<string, unknown>;
6
+ export declare function hasSuggestions(result: SuggestionResult): boolean;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSuggestions = generateSuggestions;
4
+ exports.formatSuggestionDetails = formatSuggestionDetails;
5
+ exports.hasSuggestions = hasSuggestions;
6
+ const zod_1 = require("zod");
7
+ const suggestion_types_js_1 = require("./suggestion-types.js");
8
+ const string_similarity_js_1 = require("./string-similarity.js");
9
+ const schema_introspection_js_1 = require("./schema-introspection.js");
10
+ function generateUnknownKeySuggestions(unknownKeys, expectedKeys, config) {
11
+ const suggestions = [];
12
+ for (const unknownKey of unknownKeys) {
13
+ const match = (0, string_similarity_js_1.findClosestMatch)(unknownKey, expectedKeys, config.similarityThreshold);
14
+ if (match) {
15
+ suggestions.push({
16
+ kind: 'unknown_key',
17
+ provided: unknownKey,
18
+ didYouMean: match.match,
19
+ similarity: match.score,
20
+ });
21
+ }
22
+ }
23
+ suggestions.sort((a, b) => b.similarity - a.similarity);
24
+ return suggestions.slice(0, config.maxSuggestions);
25
+ }
26
+ function generateMissingRequiredSuggestions(missingKeys, schema, config) {
27
+ if (!(schema instanceof zod_1.z.ZodObject)) {
28
+ return [];
29
+ }
30
+ const shape = schema._def.shape();
31
+ const suggestions = [];
32
+ for (const key of missingKeys) {
33
+ const field = shape[key];
34
+ if (field) {
35
+ suggestions.push({
36
+ kind: 'missing_required',
37
+ param: key,
38
+ example: (0, schema_introspection_js_1.generateExampleValue)(field, 0, config.maxTemplateDepth),
39
+ });
40
+ }
41
+ }
42
+ suggestions.sort((a, b) => a.param.localeCompare(b.param));
43
+ return suggestions.slice(0, config.maxSuggestions);
44
+ }
45
+ function generateSuggestions(args, schema, config) {
46
+ const suggestions = [];
47
+ const expectedKeys = (0, schema_introspection_js_1.extractExpectedKeys)(schema);
48
+ const unknownKeys = (0, schema_introspection_js_1.findUnknownKeys)(args, schema);
49
+ const unknownKeySuggestions = generateUnknownKeySuggestions(unknownKeys, expectedKeys, config);
50
+ suggestions.push(...unknownKeySuggestions);
51
+ const missingKeys = (0, schema_introspection_js_1.findMissingRequiredKeys)(args, schema);
52
+ const missingRequiredSuggestions = generateMissingRequiredSuggestions(missingKeys, schema, config);
53
+ suggestions.push(...missingRequiredSuggestions);
54
+ if (suggestions.length === 0 && !config.includeTemplate) {
55
+ return suggestion_types_js_1.EMPTY_SUGGESTION_RESULT;
56
+ }
57
+ const correctTemplate = config.includeTemplate
58
+ ? (0, schema_introspection_js_1.generateTemplate)(schema, config.maxTemplateDepth)
59
+ : null;
60
+ return {
61
+ suggestions,
62
+ correctTemplate,
63
+ };
64
+ }
65
+ function formatSuggestionDetails(result) {
66
+ const details = {};
67
+ if (result.suggestions.length > 0) {
68
+ details.suggestions = result.suggestions.map(s => {
69
+ switch (s.kind) {
70
+ case 'unknown_key':
71
+ return {
72
+ kind: s.kind,
73
+ provided: s.provided,
74
+ didYouMean: s.didYouMean,
75
+ similarity: Math.round(s.similarity * 100) / 100,
76
+ };
77
+ case 'missing_required':
78
+ return {
79
+ kind: s.kind,
80
+ param: s.param,
81
+ example: s.example,
82
+ };
83
+ case 'invalid_enum':
84
+ return {
85
+ kind: s.kind,
86
+ path: s.path,
87
+ provided: s.provided,
88
+ didYouMean: s.didYouMean,
89
+ allowedValues: s.allowedValues,
90
+ };
91
+ }
92
+ });
93
+ }
94
+ if (result.correctTemplate !== null) {
95
+ details.correctTemplate = result.correctTemplate;
96
+ }
97
+ return details;
98
+ }
99
+ function hasSuggestions(result) {
100
+ return result.suggestions.length > 0 || result.correctTemplate !== null;
101
+ }
@@ -0,0 +1,31 @@
1
+ export type Similarity = number & {
2
+ readonly __brand: 'Similarity';
3
+ };
4
+ export declare function similarity(n: number): Similarity;
5
+ export interface UnknownKeySuggestion {
6
+ readonly kind: 'unknown_key';
7
+ readonly provided: string;
8
+ readonly didYouMean: string;
9
+ readonly similarity: Similarity;
10
+ }
11
+ export interface MissingRequiredSuggestion {
12
+ readonly kind: 'missing_required';
13
+ readonly param: string;
14
+ readonly example: unknown;
15
+ }
16
+ export interface InvalidEnumSuggestion {
17
+ readonly kind: 'invalid_enum';
18
+ readonly path: string;
19
+ readonly provided: string;
20
+ readonly didYouMean: string | null;
21
+ readonly allowedValues: readonly string[];
22
+ }
23
+ export type ValidationSuggestion = UnknownKeySuggestion | MissingRequiredSuggestion | InvalidEnumSuggestion;
24
+ export interface SuggestionResult {
25
+ readonly suggestions: readonly ValidationSuggestion[];
26
+ readonly correctTemplate: Readonly<Record<string, unknown>> | null;
27
+ }
28
+ export declare const EMPTY_SUGGESTION_RESULT: SuggestionResult;
29
+ export declare function isUnknownKeySuggestion(s: ValidationSuggestion): s is UnknownKeySuggestion;
30
+ export declare function isMissingRequiredSuggestion(s: ValidationSuggestion): s is MissingRequiredSuggestion;
31
+ export declare function isInvalidEnumSuggestion(s: ValidationSuggestion): s is InvalidEnumSuggestion;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EMPTY_SUGGESTION_RESULT = void 0;
4
+ exports.similarity = similarity;
5
+ exports.isUnknownKeySuggestion = isUnknownKeySuggestion;
6
+ exports.isMissingRequiredSuggestion = isMissingRequiredSuggestion;
7
+ exports.isInvalidEnumSuggestion = isInvalidEnumSuggestion;
8
+ function similarity(n) {
9
+ return Math.max(0, Math.min(1, n));
10
+ }
11
+ exports.EMPTY_SUGGESTION_RESULT = {
12
+ suggestions: [],
13
+ correctTemplate: null,
14
+ };
15
+ function isUnknownKeySuggestion(s) {
16
+ return s.kind === 'unknown_key';
17
+ }
18
+ function isMissingRequiredSuggestion(s) {
19
+ return s.kind === 'missing_required';
20
+ }
21
+ function isInvalidEnumSuggestion(s) {
22
+ return s.kind === 'invalid_enum';
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/workrail",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Step-by-step workflow enforcement for AI agents via MCP",
5
5
  "license": "MIT",
6
6
  "repository": {