@lokascript/framework 2.0.0 → 2.1.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.
Files changed (126) hide show
  1. package/dist/aot/index.d.ts +1 -0
  2. package/dist/aot/index.d.ts.map +1 -1
  3. package/dist/aot/registry-bridge.d.ts +27 -0
  4. package/dist/aot/registry-bridge.d.ts.map +1 -0
  5. package/dist/api/create-dsl.d.ts.map +1 -1
  6. package/dist/api/dispatcher.d.ts +22 -0
  7. package/dist/api/dispatcher.d.ts.map +1 -1
  8. package/dist/api/domain-registry.d.ts +41 -0
  9. package/dist/api/domain-registry.d.ts.map +1 -1
  10. package/dist/api/index.js +1351 -21
  11. package/dist/api/index.js.map +1 -1
  12. package/dist/core/index.js +114 -10
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/core/pattern-matching/index.js +74 -10
  15. package/dist/core/pattern-matching/index.js.map +1 -1
  16. package/dist/core/pattern-matching/pattern-matcher.d.ts.map +1 -1
  17. package/dist/core/types.d.ts +80 -2
  18. package/dist/core/types.d.ts.map +1 -1
  19. package/dist/core/types.js +40 -0
  20. package/dist/core/types.js.map +1 -1
  21. package/dist/feedback/confidence-gate.d.ts +28 -0
  22. package/dist/feedback/confidence-gate.d.ts.map +1 -0
  23. package/dist/feedback/feedback-formatter.d.ts +21 -0
  24. package/dist/feedback/feedback-formatter.d.ts.map +1 -0
  25. package/dist/feedback/index.d.ts +11 -0
  26. package/dist/feedback/index.d.ts.map +1 -0
  27. package/dist/feedback/pattern-tracker.d.ts +47 -0
  28. package/dist/feedback/pattern-tracker.d.ts.map +1 -0
  29. package/dist/feedback/types.d.ts +113 -0
  30. package/dist/feedback/types.d.ts.map +1 -0
  31. package/dist/generation/index.js +2 -1
  32. package/dist/generation/index.js.map +1 -1
  33. package/dist/index.cjs +2685 -71
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.ts +6 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +2652 -71
  38. package/dist/index.js.map +1 -1
  39. package/dist/ir/explicit-parser.d.ts +31 -0
  40. package/dist/ir/explicit-parser.d.ts.map +1 -0
  41. package/dist/ir/explicit-renderer.d.ts +17 -0
  42. package/dist/ir/explicit-renderer.d.ts.map +1 -0
  43. package/dist/ir/from-interchange.d.ts +51 -0
  44. package/dist/ir/from-interchange.d.ts.map +1 -0
  45. package/dist/ir/index.d.ts +29 -0
  46. package/dist/ir/index.d.ts.map +1 -0
  47. package/dist/ir/index.js +1557 -0
  48. package/dist/ir/index.js.map +1 -0
  49. package/dist/ir/json-schema.d.ts +40 -0
  50. package/dist/ir/json-schema.d.ts.map +1 -0
  51. package/dist/ir/protocol-json.d.ts +73 -0
  52. package/dist/ir/protocol-json.d.ts.map +1 -0
  53. package/dist/ir/references.d.ts +19 -0
  54. package/dist/ir/references.d.ts.map +1 -0
  55. package/dist/ir/types.d.ts +141 -0
  56. package/dist/ir/types.d.ts.map +1 -0
  57. package/dist/parsing/index.js +74 -10
  58. package/dist/parsing/index.js.map +1 -1
  59. package/dist/prompts/index.d.ts +9 -0
  60. package/dist/prompts/index.d.ts.map +1 -0
  61. package/dist/prompts/prompt-generator.d.ts +49 -0
  62. package/dist/prompts/prompt-generator.d.ts.map +1 -0
  63. package/dist/prompts/types.d.ts +64 -0
  64. package/dist/prompts/types.d.ts.map +1 -0
  65. package/dist/schema/command-schema.d.ts +17 -0
  66. package/dist/schema/command-schema.d.ts.map +1 -1
  67. package/dist/schema/index.js.map +1 -1
  68. package/dist/testing/index.js +3 -0
  69. package/dist/testing/index.js.map +1 -1
  70. package/dist/training/index.d.ts +11 -0
  71. package/dist/training/index.d.ts.map +1 -0
  72. package/dist/training/jsonl-writer.d.ts +42 -0
  73. package/dist/training/jsonl-writer.d.ts.map +1 -0
  74. package/dist/training/schema-synthesizer.d.ts +20 -0
  75. package/dist/training/schema-synthesizer.d.ts.map +1 -0
  76. package/dist/training/types.d.ts +73 -0
  77. package/dist/training/types.d.ts.map +1 -0
  78. package/package.json +5 -1
  79. package/src/aot/index.ts +1 -0
  80. package/src/aot/registry-bridge.test.ts +104 -0
  81. package/src/aot/registry-bridge.ts +66 -0
  82. package/src/api/create-dsl.test.ts +306 -0
  83. package/src/api/create-dsl.ts +21 -0
  84. package/src/api/dispatcher.test.ts +66 -0
  85. package/src/api/dispatcher.ts +77 -9
  86. package/src/api/domain-registry.ts +120 -4
  87. package/src/core/pattern-matching/pattern-matcher.test.ts +131 -0
  88. package/src/core/pattern-matching/pattern-matcher.ts +102 -19
  89. package/src/core/types.ts +145 -2
  90. package/src/feedback/confidence-gate.test.ts +111 -0
  91. package/src/feedback/confidence-gate.ts +70 -0
  92. package/src/feedback/feedback-formatter.test.ts +198 -0
  93. package/src/feedback/feedback-formatter.ts +298 -0
  94. package/src/feedback/index.ts +24 -0
  95. package/src/feedback/pattern-tracker.test.ts +198 -0
  96. package/src/feedback/pattern-tracker.ts +140 -0
  97. package/src/feedback/types.ts +149 -0
  98. package/src/generation/pattern-generator.ts +2 -2
  99. package/src/index.ts +22 -0
  100. package/src/integration/llm-lse-integration.test.ts +457 -0
  101. package/src/ir/explicit-parser.test.ts +306 -0
  102. package/src/ir/explicit-parser.ts +419 -0
  103. package/src/ir/explicit-renderer.test.ts +144 -0
  104. package/src/ir/explicit-renderer.ts +146 -0
  105. package/src/ir/from-interchange.test.ts +853 -0
  106. package/src/ir/from-interchange.ts +594 -0
  107. package/src/ir/index.ts +66 -0
  108. package/src/ir/json-schema.test.ts +281 -0
  109. package/src/ir/json-schema.ts +299 -0
  110. package/src/ir/protocol-json.test.ts +1185 -0
  111. package/src/ir/protocol-json.ts +745 -0
  112. package/src/ir/references.test.ts +38 -0
  113. package/src/ir/references.ts +33 -0
  114. package/src/ir/round-trip.test.ts +557 -0
  115. package/src/ir/types.ts +181 -0
  116. package/src/prompts/index.ts +15 -0
  117. package/src/prompts/prompt-generator.test.ts +386 -0
  118. package/src/prompts/prompt-generator.ts +531 -0
  119. package/src/prompts/types.ts +89 -0
  120. package/src/schema/command-schema.ts +19 -0
  121. package/src/training/index.ts +13 -0
  122. package/src/training/jsonl-writer.test.ts +138 -0
  123. package/src/training/jsonl-writer.ts +67 -0
  124. package/src/training/schema-synthesizer.test.ts +250 -0
  125. package/src/training/schema-synthesizer.ts +306 -0
  126. package/src/training/types.ts +103 -0
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Tests for Feedback Formatter
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import { buildFeedback } from './feedback-formatter';
7
+ import type { Diagnostic } from '../generation/diagnostics';
8
+ import type { SchemaLookup } from '../ir/types';
9
+ import { defineCommand, defineRole } from '../schema/command-schema';
10
+
11
+ // =============================================================================
12
+ // Test Helpers
13
+ // =============================================================================
14
+
15
+ const fetchSchema = defineCommand({
16
+ action: 'fetch',
17
+ description: 'Fetch data from a URL',
18
+ category: 'source',
19
+ primaryRole: 'source',
20
+ roles: [
21
+ defineRole({
22
+ role: 'source',
23
+ description: 'URL to fetch from',
24
+ required: true,
25
+ expectedTypes: ['expression'],
26
+ }),
27
+ defineRole({
28
+ role: 'style',
29
+ description: 'Response format',
30
+ required: false,
31
+ expectedTypes: ['expression'],
32
+ }),
33
+ ],
34
+ });
35
+
36
+ const schemaLookup: SchemaLookup = {
37
+ getSchema(action: string) {
38
+ if (action === 'fetch') return fetchSchema;
39
+ return undefined;
40
+ },
41
+ };
42
+
43
+ function makeDiagnostic(overrides?: Partial<Diagnostic>): Diagnostic {
44
+ return {
45
+ message: 'Test error',
46
+ severity: 'error',
47
+ ...overrides,
48
+ };
49
+ }
50
+
51
+ // =============================================================================
52
+ // buildFeedback()
53
+ // =============================================================================
54
+
55
+ describe('buildFeedback', () => {
56
+ it('accepts input with no errors', () => {
57
+ const result = buildFeedback('[fetch source:/api]', 'explicit', []);
58
+ expect(result.accepted).toBe(true);
59
+ expect(result.diagnostics.length).toBe(0);
60
+ });
61
+
62
+ it('rejects input with errors', () => {
63
+ const result = buildFeedback('[fetch]', 'explicit', [makeDiagnostic()]);
64
+ expect(result.accepted).toBe(false);
65
+ });
66
+
67
+ it('preserves input info', () => {
68
+ const result = buildFeedback('[fetch source:/api]', 'explicit', []);
69
+ expect(result.input.format).toBe('explicit');
70
+ expect(result.input.text).toBe('[fetch source:/api]');
71
+ });
72
+
73
+ it('handles JSON input format', () => {
74
+ const result = buildFeedback('{"action":"fetch"}', 'json', []);
75
+ expect(result.input.format).toBe('json');
76
+ });
77
+
78
+ // --- Diagnostic Classification ---
79
+
80
+ it('classifies missing_role fix type', () => {
81
+ const diag = makeDiagnostic({ code: 'missing-role', message: 'Required role source missing' });
82
+ const result = buildFeedback('[fetch]', 'explicit', [diag]);
83
+ expect(result.diagnostics[0].fixType).toBe('missing_role');
84
+ });
85
+
86
+ it('classifies unknown_command fix type', () => {
87
+ const diag = makeDiagnostic({ code: 'unknown-command', message: 'Unknown command xyz' });
88
+ const result = buildFeedback('[xyz]', 'explicit', [diag]);
89
+ expect(result.diagnostics[0].fixType).toBe('unknown_command');
90
+ });
91
+
92
+ it('classifies unknown_role fix type', () => {
93
+ const diag = makeDiagnostic({ code: 'unknown-role', message: 'Unknown role target' });
94
+ const result = buildFeedback('[fetch target:/api]', 'explicit', [diag]);
95
+ expect(result.diagnostics[0].fixType).toBe('unknown_role');
96
+ });
97
+
98
+ it('classifies invalid_type fix type', () => {
99
+ const diag = makeDiagnostic({ code: 'invalid-type', message: 'Invalid type for role' });
100
+ const result = buildFeedback('[fetch source:123]', 'explicit', [diag]);
101
+ expect(result.diagnostics[0].fixType).toBe('invalid_type');
102
+ });
103
+
104
+ it('classifies syntax_error fix type', () => {
105
+ const diag = makeDiagnostic({ code: 'parse-error', message: 'Unexpected token' });
106
+ const result = buildFeedback('[fetch', 'explicit', [diag]);
107
+ expect(result.diagnostics[0].fixType).toBe('syntax_error');
108
+ });
109
+
110
+ it('handles diagnostics with no code', () => {
111
+ const diag = makeDiagnostic({ code: undefined, message: 'Something went wrong' });
112
+ const result = buildFeedback('[fetch]', 'explicit', [diag]);
113
+ expect(result.diagnostics[0].code).toBe('unknown');
114
+ expect(result.diagnostics[0].fixType).toBeUndefined();
115
+ });
116
+
117
+ // --- Hints ---
118
+
119
+ it('generates hints for missing_role with schema', () => {
120
+ const diag = makeDiagnostic({ code: 'missing-role', message: 'Missing role source' });
121
+ const result = buildFeedback('[fetch]', 'explicit', [diag], schemaLookup, 'fetch');
122
+ expect(result.hints.length).toBeGreaterThan(0);
123
+ expect(result.hints[0]).toContain("'source'");
124
+ });
125
+
126
+ it('generates hints for unknown_role with schema', () => {
127
+ const diag = makeDiagnostic({ code: 'unknown-role', message: 'Unknown role target' });
128
+ const result = buildFeedback('[fetch target:/api]', 'explicit', [diag], schemaLookup, 'fetch');
129
+ expect(result.hints.some(h => h.includes("'source'"))).toBe(true);
130
+ });
131
+
132
+ it('generates generic hint for unknown_command', () => {
133
+ const diag = makeDiagnostic({ code: 'unknown-command', message: 'Unknown command' });
134
+ const result = buildFeedback('[xyz]', 'explicit', [diag]);
135
+ expect(result.hints.some(h => h.includes('not recognized'))).toBe(true);
136
+ });
137
+
138
+ it('deduplicates hints', () => {
139
+ const diags = [
140
+ makeDiagnostic({ code: 'missing-role', message: 'Missing role source' }),
141
+ makeDiagnostic({ code: 'missing-role', message: 'Missing role source again' }),
142
+ ];
143
+ const result = buildFeedback('[fetch]', 'explicit', diags, schemaLookup, 'fetch');
144
+ const unique = new Set(result.hints);
145
+ expect(unique.size).toBe(result.hints.length);
146
+ });
147
+
148
+ // --- Schema Info ---
149
+
150
+ it('includes schema info when action and schemaLookup provided', () => {
151
+ const result = buildFeedback('[fetch]', 'explicit', [makeDiagnostic()], schemaLookup, 'fetch');
152
+ expect(result.schema).toBeDefined();
153
+ expect(result.schema!.action).toBe('fetch');
154
+ expect(result.schema!.requiredRoles).toContain('source');
155
+ expect(result.schema!.optionalRoles).toContain('style');
156
+ });
157
+
158
+ it('omits schema when no schemaLookup', () => {
159
+ const result = buildFeedback('[fetch]', 'explicit', [makeDiagnostic()], undefined, 'fetch');
160
+ expect(result.schema).toBeUndefined();
161
+ });
162
+
163
+ it('omits schema when action not found', () => {
164
+ const result = buildFeedback('[xyz]', 'explicit', [makeDiagnostic()], schemaLookup, 'xyz');
165
+ expect(result.schema).toBeUndefined();
166
+ });
167
+
168
+ // --- Corrected Example ---
169
+
170
+ it('generates corrected example on error with schema', () => {
171
+ const diag = makeDiagnostic({ code: 'missing-role', message: 'Missing role' });
172
+ const result = buildFeedback('[fetch]', 'explicit', [diag], schemaLookup, 'fetch');
173
+ expect(result.correctedExample).toBeDefined();
174
+ expect(result.correctedExample!.explicit).toContain('[fetch');
175
+ expect(result.correctedExample!.json.action).toBe('fetch');
176
+ expect(result.correctedExample!.json.roles['source']).toBeDefined();
177
+ });
178
+
179
+ it('omits corrected example when accepted', () => {
180
+ const result = buildFeedback('[fetch source:/api]', 'explicit', [], schemaLookup, 'fetch');
181
+ expect(result.correctedExample).toBeUndefined();
182
+ });
183
+
184
+ // --- Warnings/Info ---
185
+
186
+ it('accepts with warnings (no errors)', () => {
187
+ const diag = makeDiagnostic({ severity: 'warning', message: 'Low confidence' });
188
+ const result = buildFeedback('[fetch source:/api]', 'explicit', [diag]);
189
+ expect(result.accepted).toBe(true);
190
+ });
191
+
192
+ it('includes info-level diagnostics', () => {
193
+ const diag = makeDiagnostic({ severity: 'info', message: 'Suggestion' });
194
+ const result = buildFeedback('[fetch source:/api]', 'explicit', [diag]);
195
+ expect(result.diagnostics.length).toBe(1);
196
+ expect(result.diagnostics[0].severity).toBe('info');
197
+ });
198
+ });
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Feedback Formatter
3
+ *
4
+ * Maps diagnostics to machine-actionable LSEFeedback with hints,
5
+ * corrected examples, and schema context. Enables LLMs to self-correct
6
+ * based on structured error information.
7
+ */
8
+
9
+ import type { Diagnostic } from '../generation/diagnostics';
10
+ import type { CommandSchema } from '../schema/command-schema';
11
+ import type { SchemaLookup, SemanticJSON, SemanticJSONValue } from '../ir/types';
12
+ import { renderExplicit } from '../ir/explicit-renderer';
13
+ import { createCommandNode } from '../core/types';
14
+ import type { LSEFeedback, FeedbackDiagnostic, FixType } from './types';
15
+
16
+ // =============================================================================
17
+ // Public API
18
+ // =============================================================================
19
+
20
+ /**
21
+ * Build structured feedback from diagnostics and optional schema context.
22
+ *
23
+ * @param input - The original input text
24
+ * @param inputFormat - Format of the input
25
+ * @param diagnostics - Error diagnostics from validation/parsing
26
+ * @param schemaLookup - Optional schema lookup for generating hints and corrections
27
+ * @param action - The attempted command action (if identifiable)
28
+ */
29
+ export function buildFeedback(
30
+ input: string,
31
+ inputFormat: 'explicit' | 'json' | 'natural',
32
+ diagnostics: readonly Diagnostic[],
33
+ schemaLookup?: SchemaLookup,
34
+ action?: string
35
+ ): LSEFeedback {
36
+ const hasErrors = diagnostics.some(d => d.severity === 'error');
37
+ const feedbackDiagnostics = diagnostics.map(d => toFeedbackDiagnostic(d));
38
+ const hints = generateHints(feedbackDiagnostics, schemaLookup, action);
39
+
40
+ let schemaInfo: LSEFeedback['schema'] | undefined;
41
+ let correctedExample: LSEFeedback['correctedExample'] | undefined;
42
+
43
+ if (action && schemaLookup) {
44
+ const schema = schemaLookup.getSchema(action);
45
+ if (schema) {
46
+ schemaInfo = extractSchemaInfo(schema);
47
+ if (hasErrors) {
48
+ correctedExample = generateCorrectedExample(schema);
49
+ }
50
+ }
51
+ }
52
+
53
+ return {
54
+ accepted: !hasErrors,
55
+ input: { format: inputFormat, text: input },
56
+ diagnostics: feedbackDiagnostics,
57
+ hints,
58
+ ...(schemaInfo && { schema: schemaInfo }),
59
+ ...(correctedExample && { correctedExample }),
60
+ };
61
+ }
62
+
63
+ // =============================================================================
64
+ // Diagnostic Classification
65
+ // =============================================================================
66
+
67
+ /**
68
+ * Map a framework Diagnostic to a FeedbackDiagnostic with fix type.
69
+ */
70
+ function toFeedbackDiagnostic(d: Diagnostic): FeedbackDiagnostic {
71
+ const fixType = classifyFixType(d);
72
+ return {
73
+ severity: d.severity,
74
+ code: d.code || 'unknown',
75
+ message: d.message,
76
+ ...(d.suggestions?.[0] && { suggestion: d.suggestions[0] }),
77
+ ...(fixType && { fixType }),
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Classify a diagnostic into a machine-actionable fix type.
83
+ */
84
+ function classifyFixType(d: Diagnostic): FixType | undefined {
85
+ const code = (d.code || '').toLowerCase();
86
+ const msg = d.message.toLowerCase();
87
+
88
+ // Missing required role
89
+ if (
90
+ code.includes('missing-role') ||
91
+ code.includes('missing_role') ||
92
+ msg.includes('required role') ||
93
+ msg.includes('missing role')
94
+ ) {
95
+ return 'missing_role';
96
+ }
97
+
98
+ // Invalid value type
99
+ if (
100
+ code.includes('invalid-type') ||
101
+ code.includes('invalid_type') ||
102
+ code.includes('type-mismatch') ||
103
+ msg.includes('invalid type') ||
104
+ msg.includes('expected type')
105
+ ) {
106
+ return 'invalid_type';
107
+ }
108
+
109
+ // Unknown command
110
+ if (
111
+ code.includes('unknown-command') ||
112
+ code.includes('unknown_command') ||
113
+ msg.includes('unknown command') ||
114
+ (msg.includes('not recognized') && msg.includes('command'))
115
+ ) {
116
+ return 'unknown_command';
117
+ }
118
+
119
+ // Unknown role
120
+ if (
121
+ code.includes('unknown-role') ||
122
+ code.includes('unknown_role') ||
123
+ msg.includes('unknown role') ||
124
+ (msg.includes('not recognized') && msg.includes('role'))
125
+ ) {
126
+ return 'unknown_role';
127
+ }
128
+
129
+ // Syntax error (catch-all for parse errors)
130
+ if (
131
+ code.includes('parse') ||
132
+ code.includes('syntax') ||
133
+ msg.includes('syntax error') ||
134
+ msg.includes('parse error') ||
135
+ msg.includes('unexpected')
136
+ ) {
137
+ return 'syntax_error';
138
+ }
139
+
140
+ return undefined;
141
+ }
142
+
143
+ // =============================================================================
144
+ // Hint Generation
145
+ // =============================================================================
146
+
147
+ function generateHints(
148
+ diagnostics: readonly FeedbackDiagnostic[],
149
+ schemaLookup?: SchemaLookup,
150
+ action?: string
151
+ ): string[] {
152
+ const hints: string[] = [];
153
+ const schema = action && schemaLookup ? schemaLookup.getSchema(action) : undefined;
154
+
155
+ for (const d of diagnostics) {
156
+ if (d.severity !== 'error') continue;
157
+
158
+ switch (d.fixType) {
159
+ case 'missing_role': {
160
+ if (schema) {
161
+ const required = schema.roles
162
+ .filter(r => r.required)
163
+ .map(r => `'${r.role}' (${r.description}, type: ${r.expectedTypes.join('|')})`)
164
+ .join(', ');
165
+ hints.push(`Required roles for '${action}': ${required}`);
166
+ } else {
167
+ hints.push('Check that all required roles are present.');
168
+ }
169
+ break;
170
+ }
171
+
172
+ case 'unknown_command': {
173
+ hints.push(
174
+ `Command not recognized. Ensure you're using a valid command from the domain schema.`
175
+ );
176
+ break;
177
+ }
178
+
179
+ case 'unknown_role': {
180
+ if (schema) {
181
+ const validRoles = schema.roles.map(r => `'${r.role}'`).join(', ');
182
+ hints.push(`Valid roles for '${action}': ${validRoles}`);
183
+ } else {
184
+ hints.push('Use only roles defined in the command schema.');
185
+ }
186
+ break;
187
+ }
188
+
189
+ case 'invalid_type': {
190
+ hints.push(
191
+ 'Check value types: selectors start with #.[@*, strings use quotes, numbers are plain digits.'
192
+ );
193
+ break;
194
+ }
195
+
196
+ case 'syntax_error': {
197
+ hints.push('Ensure bracket syntax: [command role:value ...]. No spaces around the colon.');
198
+ break;
199
+ }
200
+
201
+ default: {
202
+ if (d.suggestion) {
203
+ hints.push(d.suggestion);
204
+ }
205
+ }
206
+ }
207
+ }
208
+
209
+ // Deduplicate
210
+ return [...new Set(hints)];
211
+ }
212
+
213
+ // =============================================================================
214
+ // Schema Info Extraction
215
+ // =============================================================================
216
+
217
+ function extractSchemaInfo(schema: CommandSchema): LSEFeedback['schema'] {
218
+ const requiredRoles: string[] = [];
219
+ const optionalRoles: string[] = [];
220
+ const roleDescriptions: Record<string, string> = {};
221
+
222
+ for (const role of schema.roles) {
223
+ if (role.required) {
224
+ requiredRoles.push(role.role);
225
+ } else {
226
+ optionalRoles.push(role.role);
227
+ }
228
+ roleDescriptions[role.role] = role.description;
229
+ }
230
+
231
+ return {
232
+ action: schema.action,
233
+ requiredRoles,
234
+ optionalRoles,
235
+ roleDescriptions,
236
+ };
237
+ }
238
+
239
+ // =============================================================================
240
+ // Corrected Example Generation
241
+ // =============================================================================
242
+
243
+ function generateCorrectedExample(schema: CommandSchema): { explicit: string; json: SemanticJSON } {
244
+ const roles = new Map<string, unknown>();
245
+ const jsonRoles: Record<string, SemanticJSONValue> = {};
246
+
247
+ for (const role of schema.roles) {
248
+ if (!role.required) continue;
249
+ const sample = sampleDefaultValue(role.role, role.expectedTypes[0] || 'expression');
250
+ roles.set(role.role, toSemanticValue(sample));
251
+ jsonRoles[role.role] = sample as SemanticJSONValue;
252
+ }
253
+
254
+ const node = createCommandNode(schema.action, roles as never);
255
+ const explicit = renderExplicit(node);
256
+ const json: SemanticJSON = { action: schema.action, roles: jsonRoles };
257
+
258
+ return { explicit, json };
259
+ }
260
+
261
+ function sampleDefaultValue(
262
+ roleName: string,
263
+ expectedType: string
264
+ ): { type: string; value: string } {
265
+ switch (expectedType) {
266
+ case 'selector':
267
+ return { type: 'selector', value: `#${roleName}` };
268
+ case 'expression':
269
+ return { type: 'expression', value: `<${roleName}-value>` };
270
+ case 'literal':
271
+ return { type: 'literal', value: `<${roleName}-value>` };
272
+ case 'reference':
273
+ return { type: 'reference', value: 'me' };
274
+ default:
275
+ return { type: 'expression', value: `<${roleName}-value>` };
276
+ }
277
+ }
278
+
279
+ function toSemanticValue(sample: { type: string; value: string }): {
280
+ type: string;
281
+ value: string;
282
+ raw?: string;
283
+ selectorKind?: string;
284
+ dataType?: string;
285
+ } {
286
+ switch (sample.type) {
287
+ case 'selector':
288
+ return { type: 'selector', value: sample.value, selectorKind: 'id' };
289
+ case 'expression':
290
+ return { type: 'expression', value: sample.value, raw: sample.value };
291
+ case 'literal':
292
+ return { type: 'literal', value: sample.value, dataType: 'string' };
293
+ case 'reference':
294
+ return { type: 'reference', value: sample.value };
295
+ default:
296
+ return { type: 'literal', value: sample.value, dataType: 'string' };
297
+ }
298
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Feedback Loop
3
+ *
4
+ * Structured error feedback, confidence-aware disambiguation,
5
+ * and pattern hit-rate tracking for the LLM ↔ LSE integration.
6
+ */
7
+
8
+ export type {
9
+ LSEFeedback,
10
+ FeedbackDiagnostic,
11
+ FixType,
12
+ DisambiguationRequest,
13
+ DisambiguationCandidate,
14
+ PatternEvent,
15
+ HitRate,
16
+ PatternTrackerSummary,
17
+ TopFailure,
18
+ } from './types';
19
+
20
+ export { buildFeedback } from './feedback-formatter';
21
+
22
+ export { needsDisambiguation, buildDisambiguation } from './confidence-gate';
23
+
24
+ export { PatternTracker } from './pattern-tracker';