@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
package/dist/index.cjs CHANGED
@@ -25,6 +25,7 @@ __export(index_exports, {
25
25
  CrossDomainDispatcher: () => CrossDomainDispatcher,
26
26
  DEFAULT_OPERATORS: () => DEFAULT_OPERATORS,
27
27
  DEFAULT_PUNCTUATION: () => DEFAULT_PUNCTUATION,
28
+ DEFAULT_REFERENCES: () => DEFAULT_REFERENCES,
28
29
  DomainAwareScanner: () => DomainAwareScanner,
29
30
  DomainRegistry: () => DomainRegistry,
30
31
  GrammarTransformer: () => GrammarTransformer,
@@ -36,24 +37,31 @@ __export(index_exports, {
36
37
  NumberExtractor: () => NumberExtractor,
37
38
  OperatorExtractor: () => OperatorExtractor,
38
39
  PatternMatcher: () => PatternMatcher,
40
+ PatternTracker: () => PatternTracker,
39
41
  PunctuationExtractor: () => PunctuationExtractor,
40
42
  StringLiteralExtractor: () => StringLiteralExtractor,
41
43
  TokenStreamImpl: () => TokenStreamImpl,
42
44
  UnicodeIdentifierExtractor: () => UnicodeIdentifierExtractor,
43
45
  WhitespaceExtractor: () => WhitespaceExtractor,
44
46
  accumulateBlocks: () => accumulateBlocks,
47
+ buildDisambiguation: () => buildDisambiguation,
48
+ buildFeedback: () => buildFeedback,
45
49
  buildPhrase: () => buildPhrase,
46
50
  buildTablesFromProfiles: () => buildTablesFromProfiles,
47
51
  combineClassifiers: () => combineClassifiers,
52
+ convertValue: () => convertValue,
53
+ createAsyncNode: () => createAsyncNode,
48
54
  createCommandNode: () => createCommandNode,
49
55
  createCompoundNode: () => createCompoundNode,
50
56
  createConditionalNode: () => createConditionalNode,
51
57
  createDiagnosticCollector: () => createDiagnosticCollector,
52
58
  createEventHandlerNode: () => createEventHandlerNode,
53
59
  createExpression: () => createExpression,
60
+ createFlag: () => createFlag,
54
61
  createLatinCharClassifiers: () => createLatinCharClassifiers,
55
62
  createLiteral: () => createLiteral,
56
63
  createLoopNode: () => createLoopNode,
64
+ createMatchNode: () => createMatchNode,
57
65
  createMultiStatementParser: () => createMultiStatementParser,
58
66
  createMultilingualDSL: () => createMultilingualDSL,
59
67
  createPosition: () => createPosition,
@@ -64,6 +72,7 @@ __export(index_exports, {
64
72
  createSimpleTokenizer: () => createSimpleTokenizer,
65
73
  createToken: () => createToken,
66
74
  createTokenizerContext: () => createTokenizerContext,
75
+ createTryNode: () => createTryNode,
67
76
  createUnicodeRangeClassifier: () => createUnicodeRangeClassifier,
68
77
  defineCommand: () => defineCommand,
69
78
  defineRole: () => defineRole,
@@ -75,9 +84,15 @@ __export(index_exports, {
75
84
  extractUrl: () => extractUrl,
76
85
  extractValue: () => extractValue,
77
86
  filterBySeverity: () => filterBySeverity,
87
+ fromEnvelopeJSON: () => fromEnvelopeJSON,
78
88
  fromError: () => fromError,
89
+ fromInterchangeNode: () => fromInterchangeNode,
90
+ fromProtocolJSON: () => fromProtocolJSON,
91
+ generateExamples: () => generateExamples,
79
92
  generatePattern: () => generatePattern,
80
93
  generatePatternVariants: () => generatePatternVariants,
94
+ generatePrompt: () => generatePrompt,
95
+ generateProtocolReference: () => generateProtocolReference,
81
96
  getAllPossessiveKeywords: () => getAllPossessiveKeywords,
82
97
  getDefaultExtractors: () => getDefaultExtractors,
83
98
  getPossessiveReference: () => getPossessiveReference,
@@ -90,6 +105,8 @@ __export(index_exports, {
90
105
  isClassName: () => isClassName,
91
106
  isContextAwareExtractor: () => isContextAwareExtractor,
92
107
  isDigit: () => isDigit,
108
+ isEnvelope: () => isEnvelope,
109
+ isExplicitSyntax: () => isExplicitSyntax,
93
110
  isIdSelector: () => isIdSelector,
94
111
  isNumericValue: () => isNumericValue,
95
112
  isPossessiveKeyword: () => isPossessiveKeyword,
@@ -99,17 +116,33 @@ __export(index_exports, {
99
116
  isSelectorStart: () => isSelectorStart,
100
117
  isTypeCompatible: () => isTypeCompatible,
101
118
  isUrlStart: () => isUrlStart,
119
+ isValidReference: () => isValidReference2,
102
120
  isVariableRef: () => isVariableRef,
103
121
  isWhitespace: () => isWhitespace,
104
122
  joinTokens: () => joinTokens,
123
+ jsonToSemanticNode: () => jsonToSemanticNode,
105
124
  lookupKeyword: () => lookupKeyword,
106
125
  lookupMarker: () => lookupMarker,
107
126
  matchBest: () => matchBest,
108
127
  matchPattern: () => matchPattern,
128
+ needsDisambiguation: () => needsDisambiguation,
109
129
  noChange: () => noChange,
110
130
  normalized: () => normalized,
131
+ parseExplicit: () => parseExplicit,
132
+ parseJSONL: () => parseJSONL,
111
133
  patternMatcher: () => patternMatcher,
134
+ registryToAOTBackends: () => registryToAOTBackends,
135
+ renderExplicit: () => renderExplicit,
136
+ renderExpr: () => renderExpr,
112
137
  reorderRoles: () => reorderRoles,
138
+ semanticNodeToJSON: () => semanticNodeToJSON,
139
+ synthesizeFromSchemas: () => synthesizeFromSchemas,
140
+ toEnvelopeJSON: () => toEnvelopeJSON,
141
+ toJSONL: () => toJSONL,
142
+ toJSONLRow: () => toJSONLRow,
143
+ toProtocolJSON: () => toProtocolJSON,
144
+ validateProtocolJSON: () => validateProtocolJSON,
145
+ validateSemanticJSON: () => validateSemanticJSON,
113
146
  validateValueType: () => validateValueType,
114
147
  withDefaultExtractors: () => withDefaultExtractors
115
148
  });
@@ -131,6 +164,9 @@ function createPropertyPath(object, property) {
131
164
  function createExpression(raw) {
132
165
  return { type: "expression", raw };
133
166
  }
167
+ function createFlag(name, enabled = true) {
168
+ return { type: "flag", name, enabled };
169
+ }
134
170
  function createCommandNode(action, roles, metadata) {
135
171
  const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
136
172
  const node = {
@@ -182,6 +218,7 @@ function createCompoundNode(statements, chainType = "sequential", metadata) {
182
218
  return metadata ? { ...base, metadata } : base;
183
219
  }
184
220
  function extractValue(value) {
221
+ if (value.type === "flag") return value.name;
185
222
  if ("raw" in value && value.raw !== void 0) return String(value.raw);
186
223
  if ("value" in value && value.value !== void 0) return String(value.value);
187
224
  if (value.type === "property-path") return `${extractValue(value.object)}.${value.property}`;
@@ -208,6 +245,38 @@ function createLoopNode(action, roles, loopVariant, body, loopVariable, indexVar
208
245
  ...metadata && { metadata }
209
246
  };
210
247
  }
248
+ function createTryNode(body, catchBranch, finallyBranch, metadata) {
249
+ return {
250
+ kind: "command",
251
+ action: "try",
252
+ roles: /* @__PURE__ */ new Map(),
253
+ body,
254
+ ...catchBranch && catchBranch.length > 0 ? { catchBranch } : {},
255
+ ...finallyBranch && finallyBranch.length > 0 ? { finallyBranch } : {},
256
+ ...metadata ? { metadata } : {}
257
+ };
258
+ }
259
+ function createAsyncNode(variant, asyncBody, metadata) {
260
+ return {
261
+ kind: "command",
262
+ action: variant,
263
+ roles: /* @__PURE__ */ new Map(),
264
+ asyncVariant: variant,
265
+ asyncBody,
266
+ ...metadata ? { metadata } : {}
267
+ };
268
+ }
269
+ function createMatchNode(roles, arms, defaultArm, metadata) {
270
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
271
+ return {
272
+ kind: "command",
273
+ action: "match",
274
+ roles: rolesMap,
275
+ arms,
276
+ ...defaultArm && defaultArm.length > 0 ? { defaultArm } : {},
277
+ ...metadata ? { metadata } : {}
278
+ };
279
+ }
211
280
 
212
281
  // src/core/logger.ts
213
282
  var DebugLogger = class {
@@ -463,7 +532,7 @@ var _PatternMatcher = class _PatternMatcher {
463
532
  * a token and the immediately following pattern token fails, the matcher
464
533
  * resets to before the optional role and retries the failed token.
465
534
  */
466
- matchTokenSequence(tokens, patternTokens, captured) {
535
+ matchTokenSequence(tokens, patternTokens, captured, parentStopMarkers) {
467
536
  const firstPatternToken = patternTokens[0];
468
537
  const patternExpectsConjunction = firstPatternToken?.type === "literal" && (firstPatternToken.value === "and" || firstPatternToken.value === "then" || firstPatternToken.alternatives?.includes("and") || firstPatternToken.alternatives?.includes("then"));
469
538
  if (this.currentProfile?.code === "ar" && !patternExpectsConjunction) {
@@ -487,6 +556,9 @@ var _PatternMatcher = class _PatternMatcher {
487
556
  );
488
557
  if (patternToken.type === "role" && patternToken.greedy) {
489
558
  const stopMarkers = this.collectStopMarkers(patternTokens, i + 1);
559
+ if (parentStopMarkers) {
560
+ for (const m of parentStopMarkers) stopMarkers.add(m);
561
+ }
490
562
  const values = [];
491
563
  while (!tokens.isAtEnd()) {
492
564
  const nextToken = tokens.peek();
@@ -506,8 +578,58 @@ var _PatternMatcher = class _PatternMatcher {
506
578
  return false;
507
579
  }
508
580
  }
581
+ if (patternToken.type === "group") {
582
+ const siblingStopMarkers = this.collectStopMarkers(patternTokens, i + 1);
583
+ if (parentStopMarkers) {
584
+ for (const m of parentStopMarkers) siblingStopMarkers.add(m);
585
+ }
586
+ const groupMatched = this.matchGroupToken(
587
+ tokens,
588
+ patternToken,
589
+ captured,
590
+ siblingStopMarkers
591
+ );
592
+ if (groupMatched) {
593
+ prevOptionalMark = null;
594
+ prevOptionalRole = null;
595
+ continue;
596
+ }
597
+ if (this.isOptional(patternToken)) {
598
+ continue;
599
+ }
600
+ return false;
601
+ }
509
602
  const isOptionalRole = patternToken.type === "role" && patternToken.optional === true;
510
603
  const markBefore = isOptionalRole ? tokens.mark() : null;
604
+ const isRoleToken = patternToken.type === "role";
605
+ if (isRoleToken && !isOptionalRole) {
606
+ const nextPT = i + 1 < patternTokens.length ? patternTokens[i + 1] : null;
607
+ if (nextPT?.type === "literal") {
608
+ const currentToken = tokens.peek();
609
+ if (currentToken && this.getMatchType(currentToken, nextPT.value) !== "none") {
610
+ this.logger.debug(
611
+ " >> MARKER-STEAL GUARD: current token matches next literal, skipping role"
612
+ );
613
+ this.logger.debug(" >> Token match FAILED");
614
+ if (this.isOptional(patternToken)) {
615
+ continue;
616
+ }
617
+ if (prevOptionalMark && prevOptionalRole) {
618
+ this.logger.debug(" >> BACKTRACKING: undoing optional role", prevOptionalRole);
619
+ tokens.reset(prevOptionalMark);
620
+ captured.delete(prevOptionalRole);
621
+ prevOptionalMark = null;
622
+ prevOptionalRole = null;
623
+ const retryMatched = this.matchPatternToken(tokens, patternToken, captured);
624
+ this.logger.debug(" >> Backtrack retry result:", retryMatched);
625
+ if (retryMatched) {
626
+ continue;
627
+ }
628
+ }
629
+ return false;
630
+ }
631
+ }
632
+ }
511
633
  const matched = this.matchPatternToken(tokens, patternToken, captured);
512
634
  this.logger.debug(" >> Match result:", matched);
513
635
  if (matched) {
@@ -628,7 +750,7 @@ var _PatternMatcher = class _PatternMatcher {
628
750
  const possessiveValue = this.tryMatchPossessiveExpression(tokens);
629
751
  if (possessiveValue) {
630
752
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
631
- if (!patternToken.expectedTypes.includes(possessiveValue.type) && !patternToken.expectedTypes.includes("expression")) {
753
+ if (!isTypeCompatible(possessiveValue.type, patternToken.expectedTypes)) {
632
754
  return patternToken.optional || false;
633
755
  }
634
756
  }
@@ -638,7 +760,7 @@ var _PatternMatcher = class _PatternMatcher {
638
760
  const methodCallValue = this.tryMatchMethodCallExpression(tokens);
639
761
  if (methodCallValue) {
640
762
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
641
- if (!patternToken.expectedTypes.includes(methodCallValue.type) && !patternToken.expectedTypes.includes("expression")) {
763
+ if (!isTypeCompatible(methodCallValue.type, patternToken.expectedTypes)) {
642
764
  return patternToken.optional || false;
643
765
  }
644
766
  }
@@ -658,7 +780,7 @@ var _PatternMatcher = class _PatternMatcher {
658
780
  const propertyAccessValue = this.tryMatchPropertyAccessExpression(tokens);
659
781
  if (propertyAccessValue) {
660
782
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
661
- if (!patternToken.expectedTypes.includes(propertyAccessValue.type) && !patternToken.expectedTypes.includes("expression")) {
783
+ if (!isTypeCompatible(propertyAccessValue.type, patternToken.expectedTypes)) {
662
784
  return patternToken.optional || false;
663
785
  }
664
786
  }
@@ -978,10 +1100,15 @@ var _PatternMatcher = class _PatternMatcher {
978
1100
  * (e.g., a value like 'hello') to sit between groups without blocking later
979
1101
  * marker matches.
980
1102
  */
981
- matchGroupToken(tokens, patternToken, captured) {
1103
+ matchGroupToken(tokens, patternToken, captured, parentStopMarkers) {
982
1104
  const mark = tokens.mark();
983
1105
  const capturedBefore = new Set(captured.keys());
984
- const success = this.matchTokenSequence(tokens, patternToken.tokens, captured);
1106
+ const success = this.matchTokenSequence(
1107
+ tokens,
1108
+ patternToken.tokens,
1109
+ captured,
1110
+ parentStopMarkers
1111
+ );
985
1112
  if (success) return true;
986
1113
  tokens.reset(mark);
987
1114
  for (const role of captured.keys()) {
@@ -1003,7 +1130,12 @@ var _PatternMatcher = class _PatternMatcher {
1003
1130
  "- skipping intervening tokens"
1004
1131
  );
1005
1132
  for (let s = 0; s < offset; s++) tokens.advance();
1006
- const retrySuccess = this.matchTokenSequence(tokens, patternToken.tokens, captured);
1133
+ const retrySuccess = this.matchTokenSequence(
1134
+ tokens,
1135
+ patternToken.tokens,
1136
+ captured,
1137
+ parentStopMarkers
1138
+ );
1007
1139
  if (retrySuccess) return true;
1008
1140
  tokens.reset(mark);
1009
1141
  for (const role of captured.keys()) {
@@ -1070,7 +1202,7 @@ var _PatternMatcher = class _PatternMatcher {
1070
1202
  break;
1071
1203
  }
1072
1204
  }
1073
- break;
1205
+ if (!pt.optional) break;
1074
1206
  }
1075
1207
  }
1076
1208
  return markers;
@@ -1080,8 +1212,9 @@ var _PatternMatcher = class _PatternMatcher {
1080
1212
  */
1081
1213
  isStopMarker(token, stopMarkers) {
1082
1214
  if (stopMarkers.size === 0) return false;
1083
- const value = (token.normalized || token.value).toLowerCase();
1084
- return stopMarkers.has(value);
1215
+ if (stopMarkers.has(token.value.toLowerCase())) return true;
1216
+ if (token.normalized && stopMarkers.has(token.normalized.toLowerCase())) return true;
1217
+ return false;
1085
1218
  }
1086
1219
  /**
1087
1220
  * Convert a language token to a semantic value.
@@ -1486,7 +1619,7 @@ function addRoleWithMarker(tokens, roleSpec, profile) {
1486
1619
  if (marker) {
1487
1620
  const markerInfo = profile.roleMarkers?.[roleSpec.role];
1488
1621
  const defaultPosition = profile.wordOrder === "SOV" ? "after" : "before";
1489
- const position = markerInfo?.position ?? defaultPosition;
1622
+ const position = roleSpec.markerPosition ?? markerInfo?.position ?? defaultPosition;
1490
1623
  if (isOptional) {
1491
1624
  if (position === "before") {
1492
1625
  tokens.push({
@@ -2026,6 +2159,1419 @@ function createTokenizerContext(tokenizer) {
2026
2159
  return ctx;
2027
2160
  }
2028
2161
 
2162
+ // src/ir/references.ts
2163
+ var DEFAULT_REFERENCES = /* @__PURE__ */ new Set([
2164
+ "me",
2165
+ "you",
2166
+ "it",
2167
+ "result",
2168
+ "event",
2169
+ "target",
2170
+ "body"
2171
+ ]);
2172
+ function isValidReference2(value, referenceSet = DEFAULT_REFERENCES) {
2173
+ return referenceSet.has(value);
2174
+ }
2175
+
2176
+ // src/ir/explicit-parser.ts
2177
+ var STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
2178
+ "body",
2179
+ "then",
2180
+ "else",
2181
+ "condition",
2182
+ "loop-body",
2183
+ "variable",
2184
+ "catch",
2185
+ // v1.2: try/catch/finally
2186
+ "finally"
2187
+ // v1.2: try/catch/finally
2188
+ ]);
2189
+ function isNestedCommand(value) {
2190
+ const inner = value.slice(1, -1);
2191
+ let depth = 0;
2192
+ for (const ch of inner) {
2193
+ if (ch === "[") depth++;
2194
+ else if (ch === "]") depth--;
2195
+ else if (depth === 0 && (ch === " " || ch === ":")) return true;
2196
+ }
2197
+ return false;
2198
+ }
2199
+ function parseExplicit(input, options = {}) {
2200
+ const trimmed = input.trim();
2201
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
2202
+ throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
2203
+ }
2204
+ const content = trimmed.slice(1, -1).trim();
2205
+ if (!content) {
2206
+ throw new Error("Empty explicit statement");
2207
+ }
2208
+ const tokens = tokenizeExplicit(content);
2209
+ if (tokens.length === 0) {
2210
+ throw new Error("No command specified in explicit statement");
2211
+ }
2212
+ const command = tokens[0].toLowerCase();
2213
+ const roles = /* @__PURE__ */ new Map();
2214
+ const refSet = options.referenceSet ?? DEFAULT_REFERENCES;
2215
+ const schema = options.schemaLookup?.getSchema(command);
2216
+ const validRoleNames = schema ? new Set(schema.roles.map((r) => r.role)) : null;
2217
+ for (let i = 1; i < tokens.length; i++) {
2218
+ const token = tokens[i];
2219
+ if (token.startsWith("+") || token.startsWith("~")) {
2220
+ const enabled = token.startsWith("+");
2221
+ const flagName = token.slice(1);
2222
+ if (!flagName) {
2223
+ throw new Error(`Empty flag name: "${token}"`);
2224
+ }
2225
+ if (validRoleNames && !validRoleNames.has(flagName)) {
2226
+ const roleList = [...validRoleNames].join(", ");
2227
+ throw new Error(
2228
+ `Unknown flag "${flagName}" for command "${command}". Valid roles: ${roleList}`
2229
+ );
2230
+ }
2231
+ roles.set(flagName, createFlag(flagName, enabled));
2232
+ continue;
2233
+ }
2234
+ const colonIndex = token.indexOf(":");
2235
+ if (colonIndex === -1) {
2236
+ throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
2237
+ }
2238
+ const roleName = token.slice(0, colonIndex);
2239
+ if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
2240
+ const roleList = [...validRoleNames].join(", ");
2241
+ throw new Error(
2242
+ `Unknown role "${roleName}" for command "${command}". Valid roles: ${roleList}`
2243
+ );
2244
+ }
2245
+ const role = roleName;
2246
+ const valueStr = token.slice(colonIndex + 1);
2247
+ if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[") && isNestedCommand(valueStr)) {
2248
+ const nestedEnd = findMatchingBracket(token, colonIndex + 1);
2249
+ const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
2250
+ roles.set(role, { type: "expression", raw: nestedSyntax });
2251
+ continue;
2252
+ }
2253
+ const value = parseExplicitValue(valueStr, refSet);
2254
+ roles.set(role, value);
2255
+ }
2256
+ if (schema && command !== "on") {
2257
+ for (const roleSpec of schema.roles) {
2258
+ if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
2259
+ throw new Error(
2260
+ `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`
2261
+ );
2262
+ }
2263
+ }
2264
+ }
2265
+ switch (command) {
2266
+ case "on": {
2267
+ const eventValue = roles.get("event");
2268
+ if (!eventValue) {
2269
+ throw new Error("Event handler requires event role: [on event:click ...]");
2270
+ }
2271
+ const body = extractStructuralBody(roles, "body", options);
2272
+ roles.delete("body");
2273
+ return createEventHandlerNode("on", roles, body, {
2274
+ sourceLanguage: "explicit"
2275
+ });
2276
+ }
2277
+ case "if": {
2278
+ const thenBranch = extractStructuralBody(roles, "then", options);
2279
+ const elseBranch = extractStructuralBody(roles, "else", options);
2280
+ roles.delete("then");
2281
+ roles.delete("else");
2282
+ return createConditionalNode(
2283
+ command,
2284
+ roles,
2285
+ thenBranch,
2286
+ elseBranch.length > 0 ? elseBranch : void 0,
2287
+ {
2288
+ sourceLanguage: "explicit"
2289
+ }
2290
+ );
2291
+ }
2292
+ case "repeat": {
2293
+ const loopBody = extractStructuralBody(roles, "loop-body", options);
2294
+ const loopVariantValue = roles.get("loop-variant");
2295
+ const variableValue = roles.get("variable");
2296
+ const indexVariableValue = roles.get("index-variable");
2297
+ roles.delete("loop-body");
2298
+ roles.delete("loop-variant");
2299
+ roles.delete("variable");
2300
+ roles.delete("index-variable");
2301
+ let variant;
2302
+ if (loopVariantValue && loopVariantValue.type === "literal" && typeof loopVariantValue.value === "string") {
2303
+ variant = loopVariantValue.value;
2304
+ } else if (roles.has("quantity")) {
2305
+ variant = "times";
2306
+ } else if (roles.has("source")) {
2307
+ variant = "for";
2308
+ } else if (roles.has("condition")) {
2309
+ variant = "while";
2310
+ } else {
2311
+ variant = "forever";
2312
+ }
2313
+ const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
2314
+ const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
2315
+ return createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {
2316
+ sourceLanguage: "explicit"
2317
+ });
2318
+ }
2319
+ default:
2320
+ return createCommandNode(command, roles, {
2321
+ sourceLanguage: "explicit"
2322
+ });
2323
+ }
2324
+ }
2325
+ function isExplicitSyntax(input) {
2326
+ const trimmed = input.trim();
2327
+ return trimmed.startsWith("[") && trimmed.endsWith("]");
2328
+ }
2329
+ function extractStructuralBody(roles, roleName, options) {
2330
+ const value = roles.get(roleName);
2331
+ if (!value) return [];
2332
+ if (value.type === "expression") {
2333
+ return [parseExplicit(value.raw, options)];
2334
+ }
2335
+ return [];
2336
+ }
2337
+ function inferSelectorKind(value) {
2338
+ if (!value) return void 0;
2339
+ if (value.startsWith("#")) return "id";
2340
+ if (value.startsWith(".")) return "class";
2341
+ if (value.startsWith("[")) return "attribute";
2342
+ if (value.startsWith("<") || value.startsWith("*")) return "element";
2343
+ if (/[>+~ ]/.test(value) && value.length > 1) return "complex";
2344
+ return void 0;
2345
+ }
2346
+ function tokenizeExplicit(content) {
2347
+ const tokens = [];
2348
+ let current = "";
2349
+ let inString = false;
2350
+ let stringChar = "";
2351
+ let bracketDepth = 0;
2352
+ for (let i = 0; i < content.length; i++) {
2353
+ const char = content[i];
2354
+ if (inString) {
2355
+ current += char;
2356
+ if (char === stringChar && content[i - 1] !== "\\") {
2357
+ inString = false;
2358
+ }
2359
+ continue;
2360
+ }
2361
+ if (char === '"' || char === "'") {
2362
+ inString = true;
2363
+ stringChar = char;
2364
+ current += char;
2365
+ continue;
2366
+ }
2367
+ if (char === "[") {
2368
+ bracketDepth++;
2369
+ current += char;
2370
+ continue;
2371
+ }
2372
+ if (char === "]") {
2373
+ bracketDepth--;
2374
+ current += char;
2375
+ continue;
2376
+ }
2377
+ if (char === " " && bracketDepth === 0) {
2378
+ if (current) {
2379
+ tokens.push(current);
2380
+ current = "";
2381
+ }
2382
+ continue;
2383
+ }
2384
+ current += char;
2385
+ }
2386
+ if (current) {
2387
+ tokens.push(current);
2388
+ }
2389
+ return tokens;
2390
+ }
2391
+ function parseExplicitValue(valueStr, referenceSet) {
2392
+ if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
2393
+ return createSelector(valueStr, inferSelectorKind(valueStr));
2394
+ }
2395
+ if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
2396
+ const inner = valueStr.slice(1, -1);
2397
+ return createLiteral(inner, "string");
2398
+ }
2399
+ if (valueStr === "true") return createLiteral(true, "boolean");
2400
+ if (valueStr === "false") return createLiteral(false, "boolean");
2401
+ const lowerRef = valueStr.toLowerCase();
2402
+ if (isValidReference2(lowerRef, referenceSet)) {
2403
+ return createReference(lowerRef);
2404
+ }
2405
+ const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
2406
+ if (numMatch) {
2407
+ const num = parseFloat(numMatch[1]);
2408
+ const suffix = numMatch[2];
2409
+ if (suffix) {
2410
+ return createLiteral(valueStr, "duration");
2411
+ }
2412
+ return createLiteral(num, "number");
2413
+ }
2414
+ return createLiteral(valueStr);
2415
+ }
2416
+ function findMatchingBracket(str, start) {
2417
+ let depth = 0;
2418
+ for (let i = start; i < str.length; i++) {
2419
+ if (str[i] === "[") depth++;
2420
+ else if (str[i] === "]") {
2421
+ depth--;
2422
+ if (depth === 0) return i;
2423
+ }
2424
+ }
2425
+ return str.length - 1;
2426
+ }
2427
+
2428
+ // src/ir/explicit-renderer.ts
2429
+ function renderExplicit(node) {
2430
+ if (node.kind === "compound") {
2431
+ const compoundNode = node;
2432
+ const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit(stmt));
2433
+ return renderedStatements.join(` ${compoundNode.chainType} `);
2434
+ }
2435
+ const parts = [node.action];
2436
+ for (const [role, value] of node.roles) {
2437
+ if (value.type === "flag") {
2438
+ parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);
2439
+ } else {
2440
+ parts.push(`${role}:${valueToString(value)}`);
2441
+ }
2442
+ }
2443
+ if (node.kind === "event-handler") {
2444
+ const eventNode = node;
2445
+ if (eventNode.body && eventNode.body.length > 0) {
2446
+ const bodyParts = eventNode.body.map((n) => renderExplicit(n));
2447
+ parts.push(`body:${bodyParts.join(" ")}`);
2448
+ }
2449
+ }
2450
+ if (node.kind === "conditional") {
2451
+ const condNode = node;
2452
+ if (condNode.thenBranch && condNode.thenBranch.length > 0) {
2453
+ const thenParts = condNode.thenBranch.map((n) => renderExplicit(n));
2454
+ parts.push(`then:${thenParts.join(" ")}`);
2455
+ }
2456
+ if (condNode.elseBranch && condNode.elseBranch.length > 0) {
2457
+ const elseParts = condNode.elseBranch.map((n) => renderExplicit(n));
2458
+ parts.push(`else:${elseParts.join(" ")}`);
2459
+ }
2460
+ }
2461
+ if (node.kind === "loop") {
2462
+ const loopNode = node;
2463
+ if (loopNode.loopVariant) {
2464
+ parts.push(`loop-variant:${loopNode.loopVariant}`);
2465
+ }
2466
+ if (loopNode.loopVariable) {
2467
+ parts.push(`variable:${loopNode.loopVariable}`);
2468
+ }
2469
+ if (loopNode.indexVariable) {
2470
+ parts.push(`index-variable:${loopNode.indexVariable}`);
2471
+ }
2472
+ if (loopNode.body && loopNode.body.length > 0) {
2473
+ const bodyParts = loopNode.body.map((n) => renderExplicit(n));
2474
+ parts.push(`loop-body:${bodyParts.join(" ")}`);
2475
+ }
2476
+ }
2477
+ if (node.kind === "command") {
2478
+ const cmd = node;
2479
+ if (cmd.body && cmd.body.length > 0) {
2480
+ const bodyParts = cmd.body.map((n) => renderExplicit(n));
2481
+ parts.push(`body:${bodyParts.join(" ")}`);
2482
+ }
2483
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
2484
+ const catchParts = cmd.catchBranch.map((n) => renderExplicit(n));
2485
+ parts.push(`catch:${catchParts.join(" ")}`);
2486
+ }
2487
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
2488
+ const finallyParts = cmd.finallyBranch.map((n) => renderExplicit(n));
2489
+ parts.push(`finally:${finallyParts.join(" ")}`);
2490
+ }
2491
+ if (cmd.asyncVariant) {
2492
+ parts.push(`async-variant:${cmd.asyncVariant}`);
2493
+ }
2494
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
2495
+ const asyncParts = cmd.asyncBody.map((n) => renderExplicit(n));
2496
+ parts.push(`async-body:${asyncParts.join(" ")}`);
2497
+ }
2498
+ }
2499
+ return `[${parts.join(" ")}]`;
2500
+ }
2501
+ function valueToString(value) {
2502
+ switch (value.type) {
2503
+ case "literal":
2504
+ if (typeof value.value === "string") {
2505
+ if (value.dataType === "string" || /\s/.test(value.value)) {
2506
+ return `"${value.value}"`;
2507
+ }
2508
+ return value.value;
2509
+ }
2510
+ return String(value.value);
2511
+ case "selector":
2512
+ return value.value;
2513
+ case "reference":
2514
+ return value.value;
2515
+ case "property-path":
2516
+ return `${valueToString(value.object)}'s ${value.property}`;
2517
+ case "expression":
2518
+ return value.raw;
2519
+ case "flag":
2520
+ return value.name;
2521
+ }
2522
+ }
2523
+
2524
+ // src/ir/json-schema.ts
2525
+ var VALID_VALUE_TYPES = /* @__PURE__ */ new Set([
2526
+ "selector",
2527
+ "literal",
2528
+ "reference",
2529
+ "expression",
2530
+ "property-path",
2531
+ "flag"
2532
+ ]);
2533
+ function validateSemanticJSON(input) {
2534
+ const diagnostics = [];
2535
+ if (!input.action || typeof input.action !== "string") {
2536
+ diagnostics.push({
2537
+ severity: "error",
2538
+ code: "INVALID_ACTION",
2539
+ message: 'Field "action" is required and must be a string.',
2540
+ suggestion: 'Provide a command name like "toggle", "add", "set", etc.'
2541
+ });
2542
+ return diagnostics;
2543
+ }
2544
+ if (input.roles && typeof input.roles === "object") {
2545
+ for (const [role, value] of Object.entries(input.roles)) {
2546
+ if (!value || typeof value !== "object") {
2547
+ diagnostics.push({
2548
+ severity: "error",
2549
+ code: "INVALID_ROLE_VALUE",
2550
+ message: `Role "${role}" must be an object with "type" and "value" fields.`,
2551
+ suggestion: 'Use: { "type": "selector", "value": ".active" }'
2552
+ });
2553
+ continue;
2554
+ }
2555
+ const v = value;
2556
+ if (!VALID_VALUE_TYPES.has(v.type)) {
2557
+ diagnostics.push({
2558
+ severity: "error",
2559
+ code: "INVALID_VALUE_TYPE",
2560
+ message: `Role "${role}" has invalid type "${v.type}".`,
2561
+ suggestion: "Valid types: selector, literal, reference, expression, property-path."
2562
+ });
2563
+ }
2564
+ if (v.value === void 0 || v.value === null) {
2565
+ diagnostics.push({
2566
+ severity: "error",
2567
+ code: "MISSING_VALUE",
2568
+ message: `Role "${role}" is missing the "value" field.`
2569
+ });
2570
+ }
2571
+ }
2572
+ }
2573
+ if (input.trigger) {
2574
+ if (!input.trigger.event || typeof input.trigger.event !== "string") {
2575
+ diagnostics.push({
2576
+ severity: "error",
2577
+ code: "INVALID_TRIGGER",
2578
+ message: 'Trigger "event" is required and must be a string.',
2579
+ suggestion: 'Use an event name like "click", "mouseover", "keydown".'
2580
+ });
2581
+ }
2582
+ }
2583
+ return diagnostics;
2584
+ }
2585
+ function jsonToSemanticNode(input) {
2586
+ const roles = /* @__PURE__ */ new Map();
2587
+ if (input.roles) {
2588
+ for (const [role, value] of Object.entries(input.roles)) {
2589
+ roles.set(role, convertJSONValue(value));
2590
+ }
2591
+ }
2592
+ if (input.trigger) {
2593
+ roles.set("event", createLiteral(input.trigger.event, "string"));
2594
+ const bodyRoles = new Map(roles);
2595
+ bodyRoles.delete("event");
2596
+ const bodyNode = createCommandNode(input.action, bodyRoles);
2597
+ return createEventHandlerNode(
2598
+ "on",
2599
+ roles,
2600
+ [bodyNode],
2601
+ { sourceLanguage: "json" },
2602
+ input.trigger.modifiers ?? {}
2603
+ );
2604
+ }
2605
+ return createCommandNode(input.action, roles, {
2606
+ sourceLanguage: "json"
2607
+ });
2608
+ }
2609
+ function semanticNodeToJSON(node) {
2610
+ if (node.kind === "compound") {
2611
+ const compound = node;
2612
+ if (compound.statements.length > 0) {
2613
+ return semanticNodeToJSON(compound.statements[0]);
2614
+ }
2615
+ return { action: "compound", roles: {} };
2616
+ }
2617
+ if (node.kind === "event-handler") {
2618
+ const eventNode = node;
2619
+ const bodyAction = eventNode.body?.[0]?.action ?? node.action;
2620
+ const roles2 = {};
2621
+ if (eventNode.body?.[0]) {
2622
+ for (const [role, value] of eventNode.body[0].roles) {
2623
+ roles2[role] = semanticValueToJSON(value);
2624
+ }
2625
+ }
2626
+ const eventValue = node.roles.get("event");
2627
+ const eventName = eventValue ? "value" in eventValue ? String(eventValue.value) : "raw" in eventValue ? eventValue.raw : "unknown" : "unknown";
2628
+ const result = {
2629
+ action: bodyAction === "on" ? eventNode.body?.[0]?.action ?? "on" : bodyAction,
2630
+ roles: roles2,
2631
+ trigger: {
2632
+ event: eventName
2633
+ }
2634
+ };
2635
+ if (eventNode.eventModifiers && Object.keys(eventNode.eventModifiers).length > 0) {
2636
+ result.trigger.modifiers = eventNode.eventModifiers;
2637
+ }
2638
+ return result;
2639
+ }
2640
+ const roles = {};
2641
+ for (const [role, value] of node.roles) {
2642
+ roles[role] = semanticValueToJSON(value);
2643
+ }
2644
+ return { action: node.action, roles };
2645
+ }
2646
+ function convertJSONValue(value) {
2647
+ switch (value.type) {
2648
+ case "selector":
2649
+ return createSelector(String(value.value), detectSelectorKind(String(value.value)));
2650
+ case "literal":
2651
+ return createLiteral(
2652
+ value.value,
2653
+ typeof value.value === "number" ? "number" : typeof value.value === "boolean" ? "boolean" : "string"
2654
+ );
2655
+ case "reference":
2656
+ return createReference(String(value.value));
2657
+ case "expression":
2658
+ return createExpression(String(value.value));
2659
+ case "property-path":
2660
+ return createExpression(String(value.value));
2661
+ case "flag":
2662
+ return createFlag(String(value.value), true);
2663
+ default:
2664
+ return createLiteral(String(value.value), "string");
2665
+ }
2666
+ }
2667
+ function semanticValueToJSON(value) {
2668
+ switch (value.type) {
2669
+ case "selector":
2670
+ return { type: "selector", value: value.value };
2671
+ case "literal":
2672
+ return { type: "literal", value: value.value };
2673
+ case "reference":
2674
+ return { type: "reference", value: value.value };
2675
+ case "expression":
2676
+ return { type: "expression", value: value.raw };
2677
+ case "property-path":
2678
+ return {
2679
+ type: "property-path",
2680
+ value: `${semanticValueToJSON(value.object).value}.${value.property}`
2681
+ };
2682
+ case "flag":
2683
+ return { type: "flag", value: value.enabled };
2684
+ }
2685
+ }
2686
+ function detectSelectorKind(selector) {
2687
+ if (selector.startsWith("#")) return "id";
2688
+ if (selector.startsWith(".")) return "class";
2689
+ if (selector.startsWith("[")) return "attribute";
2690
+ return "complex";
2691
+ }
2692
+
2693
+ // src/ir/protocol-json.ts
2694
+ function toProtocolJSON(node) {
2695
+ const roles = rolesToProtocol(node.roles);
2696
+ let result;
2697
+ switch (node.kind) {
2698
+ case "command": {
2699
+ const cmd = node;
2700
+ result = { kind: "command", action: node.action, roles };
2701
+ if (cmd.body && cmd.body.length > 0) {
2702
+ result.body = cmd.body.map(toProtocolJSON);
2703
+ }
2704
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
2705
+ result.catchBranch = cmd.catchBranch.map(toProtocolJSON);
2706
+ }
2707
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
2708
+ result.finallyBranch = cmd.finallyBranch.map(toProtocolJSON);
2709
+ }
2710
+ if (cmd.asyncVariant) {
2711
+ result.asyncVariant = cmd.asyncVariant;
2712
+ }
2713
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
2714
+ result.asyncBody = cmd.asyncBody.map(toProtocolJSON);
2715
+ }
2716
+ if (cmd.arms && cmd.arms.length > 0) {
2717
+ result.arms = cmd.arms.map((arm) => ({
2718
+ pattern: valueToProtocol(arm.pattern),
2719
+ body: arm.body.map(toProtocolJSON)
2720
+ }));
2721
+ }
2722
+ if (cmd.defaultArm && cmd.defaultArm.length > 0) {
2723
+ result.defaultArm = cmd.defaultArm.map(toProtocolJSON);
2724
+ }
2725
+ break;
2726
+ }
2727
+ case "event-handler": {
2728
+ const eh = node;
2729
+ result = {
2730
+ kind: "event-handler",
2731
+ action: node.action,
2732
+ roles,
2733
+ body: eh.body.map(toProtocolJSON)
2734
+ };
2735
+ break;
2736
+ }
2737
+ case "compound": {
2738
+ const c = node;
2739
+ result = {
2740
+ kind: "compound",
2741
+ action: "compound",
2742
+ statements: c.statements.map(toProtocolJSON),
2743
+ chainType: c.chainType
2744
+ };
2745
+ break;
2746
+ }
2747
+ case "conditional": {
2748
+ const cond = node;
2749
+ result = { kind: "command", action: node.action, roles };
2750
+ result.thenBranch = cond.thenBranch.map(toProtocolJSON);
2751
+ if (cond.elseBranch && cond.elseBranch.length > 0) {
2752
+ result.elseBranch = cond.elseBranch.map(toProtocolJSON);
2753
+ }
2754
+ break;
2755
+ }
2756
+ case "loop": {
2757
+ const loop = node;
2758
+ result = { kind: "command", action: node.action, roles };
2759
+ result.loopVariant = loop.loopVariant;
2760
+ result.loopBody = loop.body.map(toProtocolJSON);
2761
+ if (loop.loopVariable) result.loopVariable = loop.loopVariable;
2762
+ if (loop.indexVariable) result.indexVariable = loop.indexVariable;
2763
+ break;
2764
+ }
2765
+ }
2766
+ if (node.annotations && node.annotations.length > 0) {
2767
+ result.annotations = node.annotations.map(
2768
+ (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
2769
+ );
2770
+ }
2771
+ if (node.diagnostics && node.diagnostics.length > 0) {
2772
+ result.diagnostics = node.diagnostics.map((d) => ({
2773
+ level: d.level,
2774
+ role: d.role,
2775
+ message: d.message,
2776
+ code: d.code
2777
+ }));
2778
+ }
2779
+ return result;
2780
+ }
2781
+ function rolesToProtocol(roles) {
2782
+ const result = {};
2783
+ for (const [role, value] of roles) {
2784
+ result[role] = valueToProtocol(value);
2785
+ }
2786
+ return result;
2787
+ }
2788
+ function valueToProtocol(value) {
2789
+ switch (value.type) {
2790
+ case "selector": {
2791
+ const PROTOCOL_SELECTOR_KINDS = /* @__PURE__ */ new Set([
2792
+ "id",
2793
+ "class",
2794
+ "attribute",
2795
+ "element",
2796
+ "complex"
2797
+ ]);
2798
+ const sk = "selectorKind" in value ? value.selectorKind : void 0;
2799
+ const sv = { type: "selector", value: value.value };
2800
+ if (sk && PROTOCOL_SELECTOR_KINDS.has(sk)) {
2801
+ sv.selectorKind = sk;
2802
+ }
2803
+ return sv;
2804
+ }
2805
+ case "literal":
2806
+ return value.dataType ? { type: "literal", value: value.value, dataType: value.dataType } : { type: "literal", value: value.value };
2807
+ case "reference":
2808
+ return { type: "reference", value: value.value };
2809
+ case "expression":
2810
+ return { type: "expression", raw: value.raw };
2811
+ case "property-path":
2812
+ return { type: "expression", raw: extractValue(value) };
2813
+ case "flag":
2814
+ return { type: "flag", name: value.name, enabled: value.enabled };
2815
+ }
2816
+ }
2817
+ function fromProtocolJSON(json) {
2818
+ const roles = new Map(
2819
+ Object.entries(json.roles ?? {}).map(([role, val]) => [role, valueFromProtocol(val)])
2820
+ );
2821
+ const annotations = json.annotations?.map(
2822
+ (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
2823
+ );
2824
+ const diagnostics = json.diagnostics?.map((d) => ({
2825
+ level: d.level,
2826
+ role: d.role,
2827
+ message: d.message,
2828
+ code: d.code
2829
+ }));
2830
+ if (json.trigger) {
2831
+ const eventName = json.trigger.event;
2832
+ roles.set("event", createLiteral(eventName, "string"));
2833
+ const bodyRoles = new Map(roles);
2834
+ bodyRoles.delete("event");
2835
+ const bodyNode = createCommandNode(json.action, bodyRoles);
2836
+ const node2 = createEventHandlerNode(
2837
+ "on",
2838
+ roles,
2839
+ [bodyNode],
2840
+ { sourceLanguage: "json" },
2841
+ json.trigger.modifiers ?? {}
2842
+ );
2843
+ return applyV12Metadata(node2, annotations, diagnostics);
2844
+ }
2845
+ const kind = json.kind ?? "command";
2846
+ if (kind === "event-handler") {
2847
+ const body = (json.body ?? []).map(fromProtocolJSON);
2848
+ const node2 = createEventHandlerNode(json.action, roles, body);
2849
+ return applyV12Metadata(node2, annotations, diagnostics);
2850
+ }
2851
+ if (kind === "compound") {
2852
+ const statements = (json.statements ?? []).map(fromProtocolJSON);
2853
+ const node2 = createCompoundNode(
2854
+ statements,
2855
+ json.chainType ?? "sequential"
2856
+ );
2857
+ return applyV12Metadata(node2, annotations, diagnostics);
2858
+ }
2859
+ if (json.thenBranch && json.thenBranch.length > 0) {
2860
+ const thenBranch = json.thenBranch.map(fromProtocolJSON);
2861
+ const elseBranch = json.elseBranch ? json.elseBranch.map(fromProtocolJSON) : void 0;
2862
+ const node2 = createConditionalNode(json.action, roles, thenBranch, elseBranch);
2863
+ return applyV12Metadata(node2, annotations, diagnostics);
2864
+ }
2865
+ if (json.loopVariant) {
2866
+ const loopBody = (json.loopBody ?? []).map(fromProtocolJSON);
2867
+ const node2 = createLoopNode(
2868
+ json.action,
2869
+ roles,
2870
+ json.loopVariant,
2871
+ loopBody,
2872
+ json.loopVariable,
2873
+ json.indexVariable
2874
+ );
2875
+ return applyV12Metadata(node2, annotations, diagnostics);
2876
+ }
2877
+ if (json.catchBranch || json.finallyBranch) {
2878
+ const body = (json.body ?? []).map(fromProtocolJSON);
2879
+ const catchBranch = json.catchBranch ? json.catchBranch.map(fromProtocolJSON) : void 0;
2880
+ const finallyBranch = json.finallyBranch ? json.finallyBranch.map(fromProtocolJSON) : void 0;
2881
+ const node2 = createTryNode(body, catchBranch, finallyBranch);
2882
+ return applyV12Metadata(node2, annotations, diagnostics);
2883
+ }
2884
+ if (json.asyncVariant) {
2885
+ const asyncBody = (json.asyncBody ?? []).map(fromProtocolJSON);
2886
+ const node2 = createAsyncNode(json.asyncVariant, asyncBody);
2887
+ return applyV12Metadata(node2, annotations, diagnostics);
2888
+ }
2889
+ if (json.arms && json.arms.length > 0) {
2890
+ const arms = json.arms.map((arm) => ({
2891
+ pattern: valueFromProtocol(arm.pattern),
2892
+ body: arm.body.map(fromProtocolJSON)
2893
+ }));
2894
+ const defaultArm = json.defaultArm ? json.defaultArm.map(fromProtocolJSON) : void 0;
2895
+ const node2 = createMatchNode(roles, arms, defaultArm);
2896
+ return applyV12Metadata(node2, annotations, diagnostics);
2897
+ }
2898
+ if (json.body && json.body.length > 0 && kind === "command") {
2899
+ const body = json.body.map(fromProtocolJSON);
2900
+ const node2 = createTryNode(body);
2901
+ return applyV12Metadata(node2, annotations, diagnostics);
2902
+ }
2903
+ const node = createCommandNode(json.action, roles);
2904
+ return applyV12Metadata(node, annotations, diagnostics);
2905
+ }
2906
+ function applyV12Metadata(node, annotations, diagnostics) {
2907
+ if ((!annotations || annotations.length === 0) && (!diagnostics || diagnostics.length === 0)) {
2908
+ return node;
2909
+ }
2910
+ return {
2911
+ ...node,
2912
+ ...annotations && annotations.length > 0 ? { annotations } : {},
2913
+ ...diagnostics && diagnostics.length > 0 ? { diagnostics } : {}
2914
+ };
2915
+ }
2916
+ function valueFromProtocol(value) {
2917
+ switch (value.type) {
2918
+ case "selector":
2919
+ return createSelector(String(value.value ?? ""), value.selectorKind);
2920
+ case "literal":
2921
+ return createLiteral(
2922
+ value.value !== void 0 ? value.value : "",
2923
+ value.dataType
2924
+ );
2925
+ case "reference":
2926
+ return createReference(String(value.value ?? ""));
2927
+ case "expression":
2928
+ return createExpression(value.raw ?? String(value.value ?? ""));
2929
+ case "flag":
2930
+ return createFlag(
2931
+ value.name ?? String(value.value ?? ""),
2932
+ value.enabled ?? (typeof value.value === "boolean" ? value.value : true)
2933
+ );
2934
+ case "property-path":
2935
+ return createExpression(String(value.value ?? value.raw ?? ""));
2936
+ }
2937
+ }
2938
+ function toEnvelopeJSON(envelope) {
2939
+ const result = {
2940
+ lseVersion: envelope.lseVersion,
2941
+ nodes: envelope.nodes.map(toProtocolJSON)
2942
+ };
2943
+ if (envelope.features && envelope.features.length > 0) {
2944
+ result.features = [...envelope.features];
2945
+ }
2946
+ return result;
2947
+ }
2948
+ function fromEnvelopeJSON(json) {
2949
+ return {
2950
+ lseVersion: json.lseVersion,
2951
+ ...json.features && json.features.length > 0 ? { features: json.features } : {},
2952
+ nodes: json.nodes.map(fromProtocolJSON)
2953
+ };
2954
+ }
2955
+ function isEnvelope(json) {
2956
+ return typeof json === "object" && json !== null && "lseVersion" in json && "nodes" in json && Array.isArray(json.nodes);
2957
+ }
2958
+ var VALID_KINDS = /* @__PURE__ */ new Set(["command", "event-handler", "compound"]);
2959
+ var VALID_VALUE_TYPES2 = /* @__PURE__ */ new Set([
2960
+ "selector",
2961
+ "literal",
2962
+ "reference",
2963
+ "expression",
2964
+ "property-path",
2965
+ "flag"
2966
+ ]);
2967
+ var VALID_CHAIN_TYPES = /* @__PURE__ */ new Set(["then", "and", "async", "sequential", "pipe"]);
2968
+ var VALID_ASYNC_VARIANTS = /* @__PURE__ */ new Set(["all", "race"]);
2969
+ var VALID_DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set(["error", "warning"]);
2970
+ function validateProtocolJSON(json) {
2971
+ const diagnostics = [];
2972
+ if (typeof json !== "object" || json === null) {
2973
+ return [{ severity: "error", code: "INVALID_ROOT", message: "Root must be a JSON object" }];
2974
+ }
2975
+ const node = json;
2976
+ const kind = "kind" in node ? String(node.kind) : "command";
2977
+ if ("kind" in node && !VALID_KINDS.has(kind)) {
2978
+ diagnostics.push({
2979
+ severity: "error",
2980
+ code: "INVALID_KIND",
2981
+ message: `Invalid kind "${node.kind}". Must be one of: command, event-handler, compound`
2982
+ });
2983
+ }
2984
+ if (!("action" in node)) {
2985
+ diagnostics.push({
2986
+ severity: "error",
2987
+ code: "MISSING_ACTION",
2988
+ message: "Missing required field: action"
2989
+ });
2990
+ } else if (typeof node.action !== "string" || node.action.length === 0) {
2991
+ diagnostics.push({
2992
+ severity: "error",
2993
+ code: "INVALID_ACTION",
2994
+ message: "action must be a non-empty string"
2995
+ });
2996
+ }
2997
+ if (!("roles" in node) && kind !== "compound") {
2998
+ diagnostics.push({
2999
+ severity: "error",
3000
+ code: "MISSING_ROLES",
3001
+ message: "Missing required field: roles"
3002
+ });
3003
+ } else if ("roles" in node && (typeof node.roles !== "object" || node.roles === null || Array.isArray(node.roles))) {
3004
+ diagnostics.push({
3005
+ severity: "error",
3006
+ code: "INVALID_ROLES",
3007
+ message: "roles must be a plain object"
3008
+ });
3009
+ } else if ("roles" in node && node.roles) {
3010
+ for (const [role, value] of Object.entries(node.roles)) {
3011
+ if (typeof value !== "object" || value === null) {
3012
+ diagnostics.push({
3013
+ severity: "error",
3014
+ code: "INVALID_ROLE_VALUE",
3015
+ message: `Role "${role}" value must be an object`
3016
+ });
3017
+ continue;
3018
+ }
3019
+ const v = value;
3020
+ if (!("type" in v) || !VALID_VALUE_TYPES2.has(String(v.type))) {
3021
+ diagnostics.push({
3022
+ severity: "error",
3023
+ code: "INVALID_VALUE_TYPE",
3024
+ message: `Role "${role}" has invalid type "${v.type}"`
3025
+ });
3026
+ }
3027
+ if (v.type === "flag") {
3028
+ const hasName = typeof v.name === "string";
3029
+ const hasEnabled = typeof v.enabled === "boolean";
3030
+ const hasValue = "value" in v;
3031
+ if (!hasName && !hasValue) {
3032
+ diagnostics.push({
3033
+ severity: "error",
3034
+ code: "MISSING_FLAG_NAME",
3035
+ message: `Flag role "${role}" missing required field: name (or value)`
3036
+ });
3037
+ }
3038
+ if (!hasEnabled && !hasValue) {
3039
+ diagnostics.push({
3040
+ severity: "error",
3041
+ code: "MISSING_FLAG_ENABLED",
3042
+ message: `Flag role "${role}" missing required field: enabled (or value)`
3043
+ });
3044
+ }
3045
+ }
3046
+ if (v.type === "expression" && typeof v.raw !== "string" && !("value" in v)) {
3047
+ diagnostics.push({
3048
+ severity: "error",
3049
+ code: "MISSING_EXPRESSION_RAW",
3050
+ message: `Expression role "${role}" missing required field: raw (or value)`
3051
+ });
3052
+ }
3053
+ }
3054
+ }
3055
+ if ("trigger" in node && node.trigger) {
3056
+ const trigger = node.trigger;
3057
+ if (typeof trigger !== "object" || trigger === null) {
3058
+ diagnostics.push({
3059
+ severity: "error",
3060
+ code: "INVALID_TRIGGER",
3061
+ message: "trigger must be an object"
3062
+ });
3063
+ } else if (!trigger.event || typeof trigger.event !== "string") {
3064
+ diagnostics.push({
3065
+ severity: "error",
3066
+ code: "INVALID_TRIGGER",
3067
+ message: "trigger.event is required and must be a non-empty string"
3068
+ });
3069
+ }
3070
+ }
3071
+ if (kind === "event-handler" && !("trigger" in node)) {
3072
+ if (!("body" in node) || !Array.isArray(node.body)) {
3073
+ diagnostics.push({
3074
+ severity: "error",
3075
+ code: "MISSING_BODY",
3076
+ message: "event-handler node requires a body array"
3077
+ });
3078
+ }
3079
+ }
3080
+ if (kind === "compound") {
3081
+ if (!("statements" in node) || !Array.isArray(node.statements)) {
3082
+ diagnostics.push({
3083
+ severity: "error",
3084
+ code: "MISSING_STATEMENTS",
3085
+ message: "compound node requires a statements array"
3086
+ });
3087
+ }
3088
+ if ("chainType" in node && !VALID_CHAIN_TYPES.has(String(node.chainType))) {
3089
+ diagnostics.push({
3090
+ severity: "error",
3091
+ code: "INVALID_CHAIN_TYPE",
3092
+ message: `Invalid chainType "${node.chainType}". Must be one of: then, and, async, sequential, pipe`
3093
+ });
3094
+ }
3095
+ }
3096
+ if ("diagnostics" in node) {
3097
+ if (!Array.isArray(node.diagnostics)) {
3098
+ diagnostics.push({
3099
+ severity: "error",
3100
+ code: "INVALID_DIAGNOSTICS",
3101
+ message: "diagnostics must be an array"
3102
+ });
3103
+ } else {
3104
+ for (let i = 0; i < node.diagnostics.length; i++) {
3105
+ const d = node.diagnostics[i];
3106
+ if (!d || typeof d !== "object") {
3107
+ diagnostics.push({
3108
+ severity: "error",
3109
+ code: "INVALID_DIAGNOSTIC_ENTRY",
3110
+ message: `diagnostics[${i}] must be an object`
3111
+ });
3112
+ continue;
3113
+ }
3114
+ if (!VALID_DIAGNOSTIC_LEVELS.has(String(d.level))) {
3115
+ diagnostics.push({
3116
+ severity: "error",
3117
+ code: "INVALID_DIAGNOSTIC_LEVEL",
3118
+ message: `diagnostics[${i}].level must be "error" or "warning"`
3119
+ });
3120
+ }
3121
+ if (typeof d.role !== "string") {
3122
+ diagnostics.push({
3123
+ severity: "error",
3124
+ code: "MISSING_DIAGNOSTIC_ROLE",
3125
+ message: `diagnostics[${i}] missing required field: role`
3126
+ });
3127
+ }
3128
+ if (typeof d.message !== "string") {
3129
+ diagnostics.push({
3130
+ severity: "error",
3131
+ code: "MISSING_DIAGNOSTIC_MESSAGE",
3132
+ message: `diagnostics[${i}] missing required field: message`
3133
+ });
3134
+ }
3135
+ if (typeof d.code !== "string") {
3136
+ diagnostics.push({
3137
+ severity: "error",
3138
+ code: "MISSING_DIAGNOSTIC_CODE",
3139
+ message: `diagnostics[${i}] missing required field: code`
3140
+ });
3141
+ }
3142
+ }
3143
+ }
3144
+ }
3145
+ if ("annotations" in node) {
3146
+ if (!Array.isArray(node.annotations)) {
3147
+ diagnostics.push({
3148
+ severity: "error",
3149
+ code: "INVALID_ANNOTATIONS",
3150
+ message: "annotations must be an array"
3151
+ });
3152
+ } else {
3153
+ for (let i = 0; i < node.annotations.length; i++) {
3154
+ const a = node.annotations[i];
3155
+ if (!a || typeof a !== "object") {
3156
+ diagnostics.push({
3157
+ severity: "error",
3158
+ code: "INVALID_ANNOTATION_ENTRY",
3159
+ message: `annotations[${i}] must be an object`
3160
+ });
3161
+ continue;
3162
+ }
3163
+ if (typeof a.name !== "string" || a.name.length === 0) {
3164
+ diagnostics.push({
3165
+ severity: "error",
3166
+ code: "MISSING_ANNOTATION_NAME",
3167
+ message: `annotations[${i}] missing required field: name`
3168
+ });
3169
+ }
3170
+ }
3171
+ }
3172
+ }
3173
+ if ("asyncVariant" in node && !VALID_ASYNC_VARIANTS.has(String(node.asyncVariant))) {
3174
+ diagnostics.push({
3175
+ severity: "error",
3176
+ code: "INVALID_ASYNC_VARIANT",
3177
+ message: `Invalid asyncVariant "${node.asyncVariant}". Must be "all" or "race"`
3178
+ });
3179
+ }
3180
+ if ("arms" in node) {
3181
+ if (!Array.isArray(node.arms)) {
3182
+ diagnostics.push({
3183
+ severity: "error",
3184
+ code: "INVALID_ARMS",
3185
+ message: "arms must be an array"
3186
+ });
3187
+ } else {
3188
+ for (let i = 0; i < node.arms.length; i++) {
3189
+ const arm = node.arms[i];
3190
+ if (!arm || typeof arm !== "object") {
3191
+ diagnostics.push({
3192
+ severity: "error",
3193
+ code: "INVALID_ARM_ENTRY",
3194
+ message: `arms[${i}] must be an object`
3195
+ });
3196
+ continue;
3197
+ }
3198
+ if (!arm.pattern || typeof arm.pattern !== "object") {
3199
+ diagnostics.push({
3200
+ severity: "error",
3201
+ code: "MISSING_ARM_PATTERN",
3202
+ message: `arms[${i}] missing required field: pattern`
3203
+ });
3204
+ }
3205
+ if (!Array.isArray(arm.body)) {
3206
+ diagnostics.push({
3207
+ severity: "error",
3208
+ code: "MISSING_ARM_BODY",
3209
+ message: `arms[${i}] missing required field: body (array)`
3210
+ });
3211
+ }
3212
+ }
3213
+ }
3214
+ }
3215
+ return diagnostics;
3216
+ }
3217
+
3218
+ // src/ir/from-interchange.ts
3219
+ function fromInterchangeNode(node) {
3220
+ if (!node || typeof node !== "object") {
3221
+ return createCommandNode("unknown", {});
3222
+ }
3223
+ switch (node.type) {
3224
+ case "event":
3225
+ return convertEvent(node);
3226
+ case "command":
3227
+ return convertCommand(node);
3228
+ case "if":
3229
+ return convertIf(node);
3230
+ case "repeat":
3231
+ return convertRepeat(node);
3232
+ case "foreach":
3233
+ return convertForEach(node);
3234
+ case "while":
3235
+ return convertWhile(node);
3236
+ // Expression nodes — wrap as `get` command with patient role
3237
+ default:
3238
+ return createCommandNode("get", { patient: convertValue(node) });
3239
+ }
3240
+ }
3241
+ function convertEvent(node) {
3242
+ const event = String(node.event ?? "click");
3243
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3244
+ const body = bodyNodes.map(fromInterchangeNode);
3245
+ const roles = {
3246
+ event: createLiteral(event)
3247
+ };
3248
+ if (node.target && typeof node.target === "object") {
3249
+ roles.source = convertValue(node.target);
3250
+ }
3251
+ const modifiers = node.modifiers;
3252
+ let eventModifiers;
3253
+ if (modifiers) {
3254
+ eventModifiers = bridgeEventModifiers(modifiers);
3255
+ if (modifiers.prevent) roles.prevent = createLiteral(true);
3256
+ if (modifiers.stop) roles.stop = createLiteral(true);
3257
+ if (modifiers.passive) roles.passive = createLiteral(true);
3258
+ if (modifiers.capture) roles.capture = createLiteral(true);
3259
+ }
3260
+ return createEventHandlerNode("on", roles, body, void 0, eventModifiers);
3261
+ }
3262
+ function convertCommand(node) {
3263
+ const name = String(node.name ?? "unknown");
3264
+ const args = Array.isArray(node.args) ? node.args : [];
3265
+ const target = node.target && typeof node.target === "object" ? node.target : void 0;
3266
+ const existingRoles = node.roles;
3267
+ const modifiers = node.modifiers;
3268
+ let roles;
3269
+ if (existingRoles && typeof existingRoles === "object" && Object.keys(existingRoles).length > 0) {
3270
+ roles = {};
3271
+ for (const [role, value] of Object.entries(existingRoles)) {
3272
+ if (value && typeof value === "object" && "type" in value) {
3273
+ roles[role] = convertValue(value);
3274
+ }
3275
+ }
3276
+ } else {
3277
+ roles = inferRoles(name, args, modifiers, target);
3278
+ }
3279
+ return createCommandNode(name, roles);
3280
+ }
3281
+ function convertIf(node) {
3282
+ const condition = node.condition && typeof node.condition === "object" ? convertValue(node.condition) : createExpression("true");
3283
+ const thenBody = Array.isArray(node.thenBranch) ? node.thenBranch : [];
3284
+ const thenBranch = thenBody.map(fromInterchangeNode);
3285
+ let elseBranch;
3286
+ const elseIfBranches = node.elseIfBranches;
3287
+ if (elseIfBranches && elseIfBranches.length > 0) {
3288
+ elseBranch = [chainElseIfs(elseIfBranches, node.elseBranch)];
3289
+ } else if (Array.isArray(node.elseBranch) && node.elseBranch.length > 0) {
3290
+ elseBranch = node.elseBranch.map(fromInterchangeNode);
3291
+ }
3292
+ const roles = { condition };
3293
+ return createConditionalNode("if", roles, thenBranch, elseBranch);
3294
+ }
3295
+ function chainElseIfs(branches, finalElse) {
3296
+ const [first, ...rest] = branches;
3297
+ const condition = convertValue(first.condition);
3298
+ const thenBranch = first.body.map(fromInterchangeNode);
3299
+ let elseBranch;
3300
+ if (rest.length > 0) {
3301
+ elseBranch = [chainElseIfs(rest, finalElse)];
3302
+ } else if (finalElse && finalElse.length > 0) {
3303
+ elseBranch = finalElse.map(fromInterchangeNode);
3304
+ }
3305
+ return createConditionalNode("if", { condition }, thenBranch, elseBranch);
3306
+ }
3307
+ function convertRepeat(node) {
3308
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3309
+ const body = bodyNodes.map(fromInterchangeNode);
3310
+ const roles = {};
3311
+ let variant;
3312
+ if (node.count !== void 0 && node.count !== null) {
3313
+ variant = "times";
3314
+ if (typeof node.count === "number") {
3315
+ roles.quantity = createLiteral(node.count, "number");
3316
+ } else if (typeof node.count === "object") {
3317
+ roles.quantity = convertValue(node.count);
3318
+ }
3319
+ } else if (node.whileCondition && typeof node.whileCondition === "object") {
3320
+ variant = "while";
3321
+ roles.condition = convertValue(node.whileCondition);
3322
+ } else if (typeof node.untilEvent === "string") {
3323
+ variant = "until";
3324
+ roles.condition = createLiteral(node.untilEvent);
3325
+ } else {
3326
+ variant = "forever";
3327
+ }
3328
+ return createLoopNode("repeat", roles, variant, body);
3329
+ }
3330
+ function convertForEach(node) {
3331
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3332
+ const body = bodyNodes.map(fromInterchangeNode);
3333
+ const roles = {};
3334
+ if (node.collection && typeof node.collection === "object") {
3335
+ roles.source = convertValue(node.collection);
3336
+ }
3337
+ const itemName = typeof node.itemName === "string" ? node.itemName : void 0;
3338
+ const indexName = typeof node.indexName === "string" ? node.indexName : void 0;
3339
+ return createLoopNode("repeat", roles, "for", body, itemName, indexName);
3340
+ }
3341
+ function convertWhile(node) {
3342
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3343
+ const body = bodyNodes.map(fromInterchangeNode);
3344
+ const roles = {};
3345
+ if (node.condition && typeof node.condition === "object") {
3346
+ roles.condition = convertValue(node.condition);
3347
+ }
3348
+ return createLoopNode("repeat", roles, "while", body);
3349
+ }
3350
+ function convertValue(node) {
3351
+ switch (node.type) {
3352
+ case "literal":
3353
+ return convertLiteral(node);
3354
+ case "selector":
3355
+ return createSelector(String(node.value ?? ""), inferSelectorKind2(String(node.value ?? "")));
3356
+ case "identifier":
3357
+ return convertIdentifier(node);
3358
+ case "variable":
3359
+ return convertVariable(node);
3360
+ case "possessive":
3361
+ return createPropertyPath(
3362
+ node.object && typeof node.object === "object" ? convertValue(node.object) : createReference("me"),
3363
+ String(node.property ?? "")
3364
+ );
3365
+ // Complex expressions → collapse to raw string
3366
+ case "binary":
3367
+ case "unary":
3368
+ case "member":
3369
+ case "call":
3370
+ case "positional":
3371
+ return createExpression(renderExpr(node));
3372
+ default:
3373
+ if ("value" in node) return createLiteral(String(node.value ?? ""));
3374
+ return createExpression(renderExpr(node));
3375
+ }
3376
+ }
3377
+ function convertLiteral(node) {
3378
+ const value = node.value;
3379
+ if (value === null || value === void 0) return createLiteral("null");
3380
+ const dataType = typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : void 0;
3381
+ return createLiteral(value, dataType);
3382
+ }
3383
+ function convertIdentifier(node) {
3384
+ const value = String(node.value ?? "");
3385
+ if (DEFAULT_REFERENCES.has(value)) {
3386
+ return createReference(value);
3387
+ }
3388
+ return createLiteral(value);
3389
+ }
3390
+ function convertVariable(node) {
3391
+ const name = String(node.name ?? "");
3392
+ const scope = String(node.scope ?? "local");
3393
+ switch (scope) {
3394
+ case "element":
3395
+ return createExpression(`:${name}`);
3396
+ case "global":
3397
+ return createExpression(`$${name}`);
3398
+ default:
3399
+ return createExpression(name);
3400
+ }
3401
+ }
3402
+ function renderExpr(node) {
3403
+ if (!node || typeof node !== "object") return "";
3404
+ switch (node.type) {
3405
+ case "literal": {
3406
+ const v = node.value;
3407
+ if (v === null || v === void 0) return "null";
3408
+ if (typeof v === "string") return /\s/.test(v) ? `"${v}"` : v;
3409
+ return String(v);
3410
+ }
3411
+ case "identifier":
3412
+ return String(node.value ?? "");
3413
+ case "selector":
3414
+ return String(node.value ?? "");
3415
+ case "variable": {
3416
+ const name = String(node.name ?? "");
3417
+ const scope = String(node.scope ?? "local");
3418
+ if (scope === "element") return `:${name}`;
3419
+ if (scope === "global") return `$${name}`;
3420
+ return name;
3421
+ }
3422
+ case "binary": {
3423
+ const left = node.left && typeof node.left === "object" ? renderExpr(node.left) : "";
3424
+ const right = node.right && typeof node.right === "object" ? renderExpr(node.right) : "";
3425
+ return `${left} ${node.operator ?? ""} ${right}`;
3426
+ }
3427
+ case "unary": {
3428
+ const operand = node.operand && typeof node.operand === "object" ? renderExpr(node.operand) : "";
3429
+ return `${node.operator ?? ""} ${operand}`;
3430
+ }
3431
+ case "member": {
3432
+ const obj = node.object && typeof node.object === "object" ? renderExpr(node.object) : "";
3433
+ if (node.computed && node.property && typeof node.property === "object") {
3434
+ return `${obj}[${renderExpr(node.property)}]`;
3435
+ }
3436
+ const prop = typeof node.property === "string" ? node.property : node.property && typeof node.property === "object" ? renderExpr(node.property) : "";
3437
+ return `${obj}.${prop}`;
3438
+ }
3439
+ case "possessive": {
3440
+ const obj = node.object && typeof node.object === "object" ? renderExpr(node.object) : "";
3441
+ return `${obj}'s ${node.property ?? ""}`;
3442
+ }
3443
+ case "call": {
3444
+ const callee = node.callee && typeof node.callee === "object" ? renderExpr(node.callee) : "";
3445
+ const args = Array.isArray(node.args) ? node.args.map((a) => renderExpr(a)).join(", ") : "";
3446
+ return `${callee}(${args})`;
3447
+ }
3448
+ case "positional": {
3449
+ const target = node.target && typeof node.target === "object" ? renderExpr(node.target) : "";
3450
+ return target ? `${node.position ?? ""} ${target}` : String(node.position ?? "");
3451
+ }
3452
+ default:
3453
+ if ("value" in node) return String(node.value ?? "");
3454
+ return "";
3455
+ }
3456
+ }
3457
+ function inferRoles(name, args, modifiers, target) {
3458
+ const roles = {};
3459
+ switch (name) {
3460
+ case "set": {
3461
+ if (args[0]) roles.destination = convertValue(args[0]);
3462
+ const toVal = modifiers?.to;
3463
+ if (toVal && typeof toVal === "object" && "type" in toVal) {
3464
+ roles.patient = convertValue(toVal);
3465
+ } else if (args[1]) {
3466
+ roles.patient = convertValue(args[1]);
3467
+ }
3468
+ break;
3469
+ }
3470
+ case "put": {
3471
+ if (args[0]) roles.patient = convertValue(args[0]);
3472
+ if (modifiers) {
3473
+ for (const key of ["into", "before", "after"]) {
3474
+ const val = modifiers[key];
3475
+ if (val && typeof val === "object" && "type" in val) {
3476
+ roles.destination = convertValue(val);
3477
+ roles.method = createLiteral(key);
3478
+ break;
3479
+ }
3480
+ }
3481
+ }
3482
+ if (!roles.destination && target) {
3483
+ roles.destination = convertValue(target);
3484
+ }
3485
+ break;
3486
+ }
3487
+ case "increment":
3488
+ case "decrement": {
3489
+ if (args[0]) roles.destination = convertValue(args[0]);
3490
+ const byVal = modifiers?.by;
3491
+ if (byVal && typeof byVal === "object" && "type" in byVal) {
3492
+ roles.quantity = convertValue(byVal);
3493
+ } else if (args[1]) {
3494
+ roles.quantity = convertValue(args[1]);
3495
+ }
3496
+ break;
3497
+ }
3498
+ case "fetch": {
3499
+ if (args[0]) roles.source = convertValue(args[0]);
3500
+ const asVal = modifiers?.as;
3501
+ if (asVal && typeof asVal === "object" && "type" in asVal) {
3502
+ roles.responseType = convertValue(asVal);
3503
+ } else if (typeof asVal === "string") {
3504
+ roles.responseType = createLiteral(asVal);
3505
+ }
3506
+ break;
3507
+ }
3508
+ case "wait":
3509
+ case "settle": {
3510
+ if (args[0]) roles.duration = convertValue(args[0]);
3511
+ break;
3512
+ }
3513
+ case "toggle":
3514
+ case "add":
3515
+ case "show":
3516
+ case "hide": {
3517
+ if (args[0]) roles.patient = convertValue(args[0]);
3518
+ if (target) roles.destination = convertValue(target);
3519
+ break;
3520
+ }
3521
+ case "remove": {
3522
+ if (args[0]) roles.patient = convertValue(args[0]);
3523
+ if (target) roles.source = convertValue(target);
3524
+ break;
3525
+ }
3526
+ case "send":
3527
+ case "trigger": {
3528
+ if (args[0]) roles.patient = convertValue(args[0]);
3529
+ if (target) roles.destination = convertValue(target);
3530
+ break;
3531
+ }
3532
+ case "log": {
3533
+ if (args[0]) roles.patient = convertValue(args[0]);
3534
+ break;
3535
+ }
3536
+ default: {
3537
+ if (args[0]) roles.patient = convertValue(args[0]);
3538
+ if (target) roles.destination = convertValue(target);
3539
+ break;
3540
+ }
3541
+ }
3542
+ return roles;
3543
+ }
3544
+ function inferSelectorKind2(value) {
3545
+ if (!value) return void 0;
3546
+ if (value.startsWith("#")) return "id";
3547
+ if (value.startsWith(".")) return "class";
3548
+ if (value.startsWith("[")) return "attribute";
3549
+ if (value.startsWith("<") || value.startsWith("*")) return "element";
3550
+ if (/[>\+~ ]/.test(value) && value.length > 1) return "complex";
3551
+ return void 0;
3552
+ }
3553
+ function inferValueFromString(value) {
3554
+ if (DEFAULT_REFERENCES.has(value)) return createReference(value);
3555
+ const kind = inferSelectorKind2(value);
3556
+ if (kind) return createSelector(value, kind);
3557
+ return createLiteral(value);
3558
+ }
3559
+ function bridgeEventModifiers(mods) {
3560
+ const once = mods.once === true ? true : void 0;
3561
+ const debounce = typeof mods.debounce === "number" ? mods.debounce : void 0;
3562
+ const throttle = typeof mods.throttle === "number" ? mods.throttle : void 0;
3563
+ const from = typeof mods.from === "string" ? inferValueFromString(mods.from) : void 0;
3564
+ if (once === void 0 && debounce === void 0 && throttle === void 0 && from === void 0) {
3565
+ return void 0;
3566
+ }
3567
+ return {
3568
+ ...once !== void 0 ? { once } : {},
3569
+ ...debounce !== void 0 ? { debounce } : {},
3570
+ ...throttle !== void 0 ? { throttle } : {},
3571
+ ...from !== void 0 ? { from } : {}
3572
+ };
3573
+ }
3574
+
2029
3575
  // src/api/create-dsl.ts
2030
3576
  var DSLRegistry = class {
2031
3577
  constructor(config) {
@@ -2063,12 +3609,23 @@ var MultilingualDSLImpl = class {
2063
3609
  if (config.codeGenerator) {
2064
3610
  this.codeGenerator = config.codeGenerator;
2065
3611
  }
3612
+ const schemaMap = new Map(config.schemas.map((s) => [s.action, s]));
3613
+ this.schemaLookup = {
3614
+ getSchema: (action) => schemaMap.get(action)
3615
+ };
2066
3616
  }
2067
3617
  parse(input, language) {
2068
3618
  const result = this.parseWithConfidence(input, language);
2069
3619
  return result.node;
2070
3620
  }
2071
3621
  parseWithConfidence(input, language) {
3622
+ if (isExplicitSyntax(input)) {
3623
+ const node = parseExplicit(input, { schemaLookup: this.schemaLookup });
3624
+ if (!this.schemaLookup.getSchema(node.action)) {
3625
+ throw new Error(`No schema for action "${node.action}" in this DSL`);
3626
+ }
3627
+ return { node, confidence: 1 };
3628
+ }
2072
3629
  const tokenizer = this.registry.getTokenizer(language);
2073
3630
  if (!tokenizer) {
2074
3631
  throw new Error(`No tokenizer registered for language: ${language}`);
@@ -2104,75 +3661,802 @@ var MultilingualDSLImpl = class {
2104
3661
  valid: true,
2105
3662
  node: result.node
2106
3663
  };
2107
- } catch (error) {
3664
+ } catch (error) {
3665
+ return {
3666
+ valid: false,
3667
+ errors: [error instanceof Error ? error.message : String(error)]
3668
+ };
3669
+ }
3670
+ }
3671
+ translate(input, fromLanguage, toLanguage) {
3672
+ if (isExplicitSyntax(input)) return input;
3673
+ return this.transformer.transform(input, fromLanguage, toLanguage);
3674
+ }
3675
+ compile(input, language) {
3676
+ if (!this.codeGenerator) {
3677
+ return {
3678
+ ok: false,
3679
+ errors: ["No code generator configured for this DSL"]
3680
+ };
3681
+ }
3682
+ try {
3683
+ const result = this.parseWithConfidence(input, language);
3684
+ const code = this.codeGenerator.generate(result.node);
3685
+ return {
3686
+ ok: true,
3687
+ code,
3688
+ node: result.node,
3689
+ metadata: {
3690
+ parser: "semantic",
3691
+ confidence: result.confidence
3692
+ }
3693
+ };
3694
+ } catch (error) {
3695
+ return {
3696
+ ok: false,
3697
+ errors: [error instanceof Error ? error.message : String(error)]
3698
+ };
3699
+ }
3700
+ }
3701
+ getSupportedLanguages() {
3702
+ return this.registry.getSupportedLanguages();
3703
+ }
3704
+ };
3705
+ function createDefaultDictionary(config) {
3706
+ const translations = {};
3707
+ for (const lang of config.languages) {
3708
+ const keywords = {};
3709
+ for (const [canonical, keywordDef] of Object.entries(lang.patternProfile.keywords)) {
3710
+ keywords[canonical] = keywordDef.primary;
3711
+ }
3712
+ translations[lang.code] = keywords;
3713
+ }
3714
+ return new InMemoryDictionary(translations);
3715
+ }
3716
+ function createDefaultProfileProvider(config) {
3717
+ const profiles = {};
3718
+ for (const lang of config.languages) {
3719
+ if (lang.grammarProfile) {
3720
+ profiles[lang.code] = lang.grammarProfile;
3721
+ }
3722
+ }
3723
+ return new InMemoryProfileProvider(profiles);
3724
+ }
3725
+ function createMultilingualDSL(config) {
3726
+ const dictionary = config.dictionary ?? createDefaultDictionary(config);
3727
+ const profileProvider = config.profileProvider ?? createDefaultProfileProvider(config);
3728
+ const transformer = new GrammarTransformer({
3729
+ dictionary,
3730
+ profileProvider
3731
+ });
3732
+ const registry = new DSLRegistry(config);
3733
+ return new MultilingualDSLImpl(config, registry, transformer);
3734
+ }
3735
+
3736
+ // src/prompts/prompt-generator.ts
3737
+ function generatePrompt(config) {
3738
+ const outputFormat = config.outputFormat ?? "both";
3739
+ const examplesPerCommand = config.examplesPerCommand ?? 2;
3740
+ const sections = [
3741
+ buildProtocolSection(),
3742
+ buildValueTypeSection(),
3743
+ buildCommandsSection(config.schemas, outputFormat, examplesPerCommand),
3744
+ buildOutputFormatSection(outputFormat),
3745
+ buildErrorRecoverySection()
3746
+ ];
3747
+ let finalSections = sections;
3748
+ if (config.maxTokens && config.maxTokens > 0) {
3749
+ finalSections = truncateToTokenBudget(sections, config.maxTokens);
3750
+ }
3751
+ const text = finalSections.map((s) => `## ${s.title}
3752
+
3753
+ ${s.content}`).join("\n\n---\n\n");
3754
+ const totalRoles = config.schemas.reduce((sum, s) => sum + s.roles.length, 0);
3755
+ const approximateTokens = estimateTokens(text);
3756
+ const metadata = {
3757
+ domain: config.domain,
3758
+ commandCount: config.schemas.length,
3759
+ roleCount: totalRoles,
3760
+ approximateTokens
3761
+ };
3762
+ return { text, sections: finalSections, metadata };
3763
+ }
3764
+ function generateExamples(schema, count = 2) {
3765
+ const combos = generateRoleCombinations(schema);
3766
+ const examples = [];
3767
+ for (let i = 0; i < Math.min(count, combos.length); i++) {
3768
+ const combo = combos[i];
3769
+ const roles = /* @__PURE__ */ new Map();
3770
+ for (const role of combo) {
3771
+ const sample = sampleValue(role);
3772
+ roles.set(role.role, sample);
3773
+ }
3774
+ const nodeRoles = /* @__PURE__ */ new Map();
3775
+ for (const [roleName, sample] of roles) {
3776
+ nodeRoles.set(roleName, toSemanticValue(sample));
3777
+ }
3778
+ const node = createCommandNode(schema.action, nodeRoles);
3779
+ const explicit = renderExplicit(node);
3780
+ const jsonRoles = {};
3781
+ for (const [roleName, sample] of roles) {
3782
+ jsonRoles[roleName] = sample;
3783
+ }
3784
+ const json = { action: schema.action, roles: jsonRoles };
3785
+ examples.push({ explicit, json });
3786
+ }
3787
+ return examples;
3788
+ }
3789
+ function generateProtocolReference() {
3790
+ const protocol = buildProtocolSection();
3791
+ const valueTypes = buildValueTypeSection();
3792
+ return `# LokaScript Explicit Syntax (LSE) Quick Reference
3793
+
3794
+ ${protocol.content}
3795
+
3796
+ ${valueTypes.content}`;
3797
+ }
3798
+ function buildProtocolSection() {
3799
+ const content = `LokaScript Explicit Syntax (LSE) is a bracket-based format for imperative commands.
3800
+
3801
+ ### Syntax
3802
+
3803
+ \`\`\`
3804
+ [command role1:value1 role2:value2 +flag1 ~flag2]
3805
+ \`\`\`
3806
+
3807
+ - **Command**: The first token inside brackets (lowercased)
3808
+ - **Role pair**: \`name:value\` \u2014 a named semantic role with a typed value (no space around colon)
3809
+ - **Enabled flag**: \`+name\` \u2014 boolean attribute present
3810
+ - **Disabled flag**: \`~name\` \u2014 boolean attribute negated
3811
+ - **Nested body**: \`body:[command ...]\` \u2014 a bracket command inside a role value
3812
+
3813
+ ### Rules
3814
+
3815
+ 1. Commands are always lowercased
3816
+ 2. Role names preserve their original case
3817
+ 3. No spaces around the colon in role:value pairs
3818
+ 4. Strings with spaces must be quoted: \`patient:"hello world"\`
3819
+ 5. Selectors start with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`
3820
+ 6. Output must be valid bracket syntax: \`[action role:value ...]\``;
3821
+ return {
3822
+ id: "protocol",
3823
+ title: "LSE Protocol",
3824
+ content,
3825
+ approximateTokens: estimateTokens(content)
3826
+ };
3827
+ }
3828
+ function buildValueTypeSection() {
3829
+ const content = `Values in LSE are classified by their syntactic form (first match wins):
3830
+
3831
+ | Type | Syntax | Example |
3832
+ |------|--------|---------|
3833
+ | Selector | Starts with \`#\` \`.\` \`[\` \`@\` \`*\` | \`#button\`, \`.active\`, \`[data-id]\` |
3834
+ | String | Quoted with \`"\` or \`'\` | \`"hello world"\`, \`'json'\` |
3835
+ | Boolean | Exact: \`true\` / \`false\` | \`visible:true\` |
3836
+ | Reference | Built-in name | \`me\`, \`you\`, \`it\`, \`result\`, \`event\`, \`target\`, \`body\` |
3837
+ | Duration | Number + suffix | \`500ms\`, \`2s\`, \`1m\`, \`1h\` |
3838
+ | Number | Digits with optional decimal | \`5\`, \`3.14\`, \`-1\` |
3839
+ | Plain | Fallback (any non-whitespace) | \`/api/users\`, \`json\`, \`production\` |
3840
+
3841
+ **Important**: Classification is by prefix, not by intent. \`#true\` is a selector (not boolean). \`event\` as a value is a reference (not plain text).`;
3842
+ return {
3843
+ id: "value-types",
3844
+ title: "Value Types",
3845
+ content,
3846
+ approximateTokens: estimateTokens(content)
3847
+ };
3848
+ }
3849
+ function buildCommandsSection(schemas, outputFormat, examplesPerCommand) {
3850
+ const parts = [];
3851
+ for (const schema of schemas) {
3852
+ parts.push(formatCommandDoc(schema, outputFormat, examplesPerCommand));
3853
+ }
3854
+ const content = parts.join("\n\n");
3855
+ return {
3856
+ id: "commands",
3857
+ title: "Available Commands",
3858
+ content,
3859
+ approximateTokens: estimateTokens(content)
3860
+ };
3861
+ }
3862
+ function buildOutputFormatSection(outputFormat) {
3863
+ let content;
3864
+ if (outputFormat === "explicit") {
3865
+ content = `Output valid LSE bracket syntax. Each command must be wrapped in brackets:
3866
+
3867
+ \`\`\`
3868
+ [command role:value ...]
3869
+ \`\`\`
3870
+
3871
+ Do NOT output JSON. Only use bracket syntax.`;
3872
+ } else if (outputFormat === "json") {
3873
+ content = `Output valid JSON in the LLM-simplified format:
3874
+
3875
+ \`\`\`json
3876
+ {
3877
+ "action": "command-name",
3878
+ "roles": {
3879
+ "roleName": { "type": "valueType", "value": "theValue" }
3880
+ }
3881
+ }
3882
+ \`\`\`
3883
+
3884
+ Valid value types: \`selector\`, \`literal\`, \`reference\`, \`expression\`.
3885
+ Do NOT output bracket syntax. Only use JSON.`;
3886
+ } else {
3887
+ content = `You may output EITHER format:
3888
+
3889
+ **Bracket syntax** (preferred for single commands):
3890
+ \`\`\`
3891
+ [command role:value ...]
3892
+ \`\`\`
3893
+
3894
+ **JSON format** (preferred when structured data is needed):
3895
+ \`\`\`json
3896
+ {
3897
+ "action": "command-name",
3898
+ "roles": {
3899
+ "roleName": { "type": "valueType", "value": "theValue" }
3900
+ }
3901
+ }
3902
+ \`\`\`
3903
+
3904
+ Both formats are equally valid. Use whichever is more natural for the context.`;
3905
+ }
3906
+ return {
3907
+ id: "output-format",
3908
+ title: "Output Format",
3909
+ content,
3910
+ approximateTokens: estimateTokens(content)
3911
+ };
3912
+ }
3913
+ function buildErrorRecoverySection() {
3914
+ const content = `If you're unsure about a command or role:
3915
+
3916
+ 1. **Unknown command**: Use the closest matching command from the Available Commands section. Do not invent commands.
3917
+ 2. **Unknown role**: Use only the roles listed for each command. Do not add roles not in the schema.
3918
+ 3. **Ambiguous value type**: When in doubt, use \`expression\` type (it's the most permissive).
3919
+ 4. **Missing required role**: Always include all required roles. Check the Required/Optional labels.
3920
+ 5. **Selector vs plain value**: If a value starts with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`, it's a selector. URLs like \`/api/data\` are plain values.
3921
+
3922
+ When generating LSE, prefer explicit role labeling over positional guessing. Every value should have a role name.`;
3923
+ return {
3924
+ id: "error-recovery",
3925
+ title: "Error Recovery",
3926
+ content,
3927
+ approximateTokens: estimateTokens(content)
3928
+ };
3929
+ }
3930
+ function formatCommandDoc(schema, outputFormat, examplesPerCommand) {
3931
+ const lines = [];
3932
+ lines.push(`### \`${schema.action}\` \u2014 ${schema.description}`);
3933
+ lines.push("");
3934
+ const required = schema.roles.filter((r) => r.required);
3935
+ const optional = schema.roles.filter((r) => !r.required);
3936
+ if (required.length > 0) {
3937
+ lines.push("**Required roles:**");
3938
+ for (const role of required) {
3939
+ lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
3940
+ }
3941
+ }
3942
+ if (optional.length > 0) {
3943
+ lines.push("**Optional roles:**");
3944
+ for (const role of optional) {
3945
+ lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
3946
+ }
3947
+ }
3948
+ const examples = generateExamples(schema, examplesPerCommand);
3949
+ if (examples.length > 0) {
3950
+ lines.push("");
3951
+ if (outputFormat === "explicit" || outputFormat === "both") {
3952
+ lines.push("**Bracket syntax:**");
3953
+ for (const ex of examples) {
3954
+ lines.push(`\`\`\`
3955
+ ${ex.explicit}
3956
+ \`\`\``);
3957
+ }
3958
+ }
3959
+ if (outputFormat === "json" || outputFormat === "both") {
3960
+ lines.push("**JSON format:**");
3961
+ for (const ex of examples) {
3962
+ lines.push(`\`\`\`json
3963
+ ${JSON.stringify(ex.json, null, 2)}
3964
+ \`\`\``);
3965
+ }
3966
+ }
3967
+ }
3968
+ return lines.join("\n");
3969
+ }
3970
+ function formatExpectedTypes(role) {
3971
+ return role.expectedTypes.join(" | ");
3972
+ }
3973
+ function generateRoleCombinations(schema) {
3974
+ const required = schema.roles.filter((r) => r.required);
3975
+ const optional = schema.roles.filter((r) => !r.required);
3976
+ const combos = [];
3977
+ if (required.length > 0) {
3978
+ combos.push(required);
3979
+ }
3980
+ for (const opt of optional) {
3981
+ combos.push([...required, opt]);
3982
+ }
3983
+ if (optional.length >= 2) {
3984
+ combos.push([...required, ...optional]);
3985
+ }
3986
+ if (combos.length === 0) {
3987
+ combos.push(schema.roles.slice());
3988
+ }
3989
+ return combos;
3990
+ }
3991
+ function sampleValue(role) {
3992
+ const primaryType = role.expectedTypes[0] || "expression";
3993
+ switch (primaryType) {
3994
+ case "selector":
3995
+ return sampleSelector(role.role);
3996
+ case "literal":
3997
+ return sampleLiteral(role.role);
3998
+ case "reference":
3999
+ return { type: "reference", value: "me" };
4000
+ case "expression":
4001
+ return sampleExpression(role.role);
4002
+ case "flag":
4003
+ return { type: "flag", value: true };
4004
+ default:
4005
+ return { type: "literal", value: "example" };
4006
+ }
4007
+ }
4008
+ function sampleSelector(roleName) {
4009
+ const selectors = {
4010
+ patient: ".active",
4011
+ destination: "#output",
4012
+ source: "#input",
4013
+ target: "#target"
4014
+ };
4015
+ return { type: "selector", value: selectors[roleName] || `#${roleName}` };
4016
+ }
4017
+ function sampleLiteral(roleName) {
4018
+ const literals = {
4019
+ duration: "30s",
4020
+ interval: "5s",
4021
+ delay: "500ms",
4022
+ timeout: "10s",
4023
+ quantity: 5,
4024
+ limit: 10,
4025
+ style: "json",
4026
+ manner: "rolling",
4027
+ format: "html"
4028
+ };
4029
+ return { type: "literal", value: literals[roleName] || roleName };
4030
+ }
4031
+ function sampleExpression(roleName) {
4032
+ const expressions = {
4033
+ source: "/api/data",
4034
+ destination: "#output",
4035
+ patient: ".active",
4036
+ instrument: "toUpperCase",
4037
+ condition: "age > 18",
4038
+ style: "json",
4039
+ duration: "5s",
4040
+ url: "/api/users"
4041
+ };
4042
+ return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
4043
+ }
4044
+ function toSemanticValue(sample) {
4045
+ switch (sample.type) {
4046
+ case "selector":
4047
+ return {
4048
+ type: "selector",
4049
+ value: String(sample.value),
4050
+ selectorKind: detectSelectorKind2(String(sample.value))
4051
+ };
4052
+ case "literal":
2108
4053
  return {
2109
- valid: false,
2110
- errors: [error instanceof Error ? error.message : String(error)]
4054
+ type: "literal",
4055
+ value: sample.value,
4056
+ dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
2111
4057
  };
4058
+ case "reference":
4059
+ return { type: "reference", value: String(sample.value) };
4060
+ case "expression":
4061
+ return { type: "expression", value: String(sample.value), raw: String(sample.value) };
4062
+ case "flag":
4063
+ return { type: "flag", value: sample.value };
4064
+ default:
4065
+ return { type: "literal", value: String(sample.value), dataType: "string" };
4066
+ }
4067
+ }
4068
+ function detectSelectorKind2(selector) {
4069
+ if (selector.startsWith("#")) return "id";
4070
+ if (selector.startsWith(".")) return "class";
4071
+ if (selector.startsWith("[")) return "attribute";
4072
+ return "complex";
4073
+ }
4074
+ function estimateTokens(text) {
4075
+ return Math.ceil(text.length / 4);
4076
+ }
4077
+ function truncateToTokenBudget(sections, maxTokens) {
4078
+ const result = [];
4079
+ let budget = maxTokens;
4080
+ for (const section of sections) {
4081
+ if (section.approximateTokens <= budget) {
4082
+ result.push(section);
4083
+ budget -= section.approximateTokens;
4084
+ } else {
4085
+ const charBudget = Math.max(budget * 4, 40);
4086
+ const truncatedContent = section.content.slice(0, charBudget) + "\n\n*(truncated)*";
4087
+ result.push({
4088
+ ...section,
4089
+ content: truncatedContent,
4090
+ approximateTokens: estimateTokens(truncatedContent)
4091
+ });
4092
+ break;
2112
4093
  }
2113
4094
  }
2114
- translate(input, fromLanguage, toLanguage) {
2115
- return this.transformer.transform(input, fromLanguage, toLanguage);
4095
+ return result;
4096
+ }
4097
+
4098
+ // src/training/schema-synthesizer.ts
4099
+ function synthesizeFromSchemas(schemas, config) {
4100
+ const maxPerCommand = config.maxPairsPerCommand ?? 5;
4101
+ const minQuality = config.minQuality ?? 0.5;
4102
+ const shuffleRoles = config.shuffleRoles ?? true;
4103
+ const languages = config.languages ?? ["en"];
4104
+ const pairs = [];
4105
+ let idCounter = 0;
4106
+ for (const schema of schemas) {
4107
+ const combos = generateRoleCombinations2(schema);
4108
+ for (const language of languages) {
4109
+ let commandPairCount = 0;
4110
+ for (const combo of combos) {
4111
+ if (commandPairCount >= maxPerCommand) break;
4112
+ const pair = buildTrainingPair(
4113
+ schema,
4114
+ combo,
4115
+ language,
4116
+ config.domain,
4117
+ `${config.domain}-synth-${idCounter++}`
4118
+ );
4119
+ if (pair.quality >= minQuality) {
4120
+ pairs.push(pair);
4121
+ commandPairCount++;
4122
+ }
4123
+ if (shuffleRoles && combo.length >= 2 && commandPairCount < maxPerCommand) {
4124
+ const shuffled = [...combo].reverse();
4125
+ const shuffledPair = buildTrainingPair(
4126
+ schema,
4127
+ shuffled,
4128
+ language,
4129
+ config.domain,
4130
+ `${config.domain}-synth-${idCounter++}`
4131
+ );
4132
+ if (shuffledPair.quality >= minQuality) {
4133
+ pairs.push(shuffledPair);
4134
+ commandPairCount++;
4135
+ }
4136
+ }
4137
+ }
4138
+ }
2116
4139
  }
2117
- compile(input, language) {
2118
- if (!this.codeGenerator) {
2119
- return {
2120
- ok: false,
2121
- errors: ["No code generator configured for this DSL"]
2122
- };
4140
+ const metadata = {
4141
+ domain: config.domain,
4142
+ commandCount: schemas.length,
4143
+ pairCount: pairs.length,
4144
+ bySource: { synthetic: pairs.length },
4145
+ languages
4146
+ };
4147
+ return { pairs, metadata };
4148
+ }
4149
+ function generateRoleCombinations2(schema) {
4150
+ const required = schema.roles.filter((r) => r.required);
4151
+ const optional = schema.roles.filter((r) => !r.required);
4152
+ const combos = [];
4153
+ if (required.length > 0) {
4154
+ combos.push(required);
4155
+ }
4156
+ for (const opt of optional) {
4157
+ combos.push([...required, opt]);
4158
+ }
4159
+ if (optional.length >= 2) {
4160
+ combos.push([...required, ...optional]);
4161
+ }
4162
+ if (combos.length === 0) {
4163
+ combos.push(schema.roles.slice());
4164
+ }
4165
+ return combos;
4166
+ }
4167
+ function buildTrainingPair(schema, roles, language, domain, id) {
4168
+ const sampledRoles = /* @__PURE__ */ new Map();
4169
+ for (const role of roles) {
4170
+ sampledRoles.set(role.role, sampleValue2(role));
4171
+ }
4172
+ const nodeRoles = /* @__PURE__ */ new Map();
4173
+ for (const [name, sample] of sampledRoles) {
4174
+ nodeRoles.set(name, toSemanticValue2(sample));
4175
+ }
4176
+ const node = createCommandNode(schema.action, nodeRoles);
4177
+ const explicit = renderExplicit(node);
4178
+ const jsonRoles = {};
4179
+ for (const [name, sample] of sampledRoles) {
4180
+ jsonRoles[name] = sample;
4181
+ }
4182
+ const json = { action: schema.action, roles: jsonRoles };
4183
+ const natural = buildNaturalForm(schema, sampledRoles, language);
4184
+ const requiredCount = schema.roles.filter((r) => r.required).length;
4185
+ const filledRequired = roles.filter((r) => r.required).length;
4186
+ const quality = requiredCount > 0 ? filledRequired / requiredCount : 0.8;
4187
+ return {
4188
+ id,
4189
+ natural,
4190
+ language,
4191
+ explicit,
4192
+ json,
4193
+ domain,
4194
+ action: schema.action,
4195
+ source: "synthetic",
4196
+ quality
4197
+ };
4198
+ }
4199
+ function buildNaturalForm(schema, roles, language) {
4200
+ const parts = [schema.action];
4201
+ for (const roleSpec of schema.roles) {
4202
+ const sample = roles.get(roleSpec.role);
4203
+ if (!sample) continue;
4204
+ const marker = roleSpec.markerOverride?.[language];
4205
+ if (marker) {
4206
+ parts.push(marker);
2123
4207
  }
2124
- try {
2125
- const result = this.parseWithConfidence(input, language);
2126
- const code = this.codeGenerator.generate(result.node);
4208
+ parts.push(String(sample.value));
4209
+ }
4210
+ return parts.join(" ");
4211
+ }
4212
+ function sampleValue2(role) {
4213
+ const primaryType = role.expectedTypes[0] || "expression";
4214
+ switch (primaryType) {
4215
+ case "selector":
4216
+ return sampleSelector2(role.role);
4217
+ case "literal":
4218
+ return sampleLiteral2(role.role);
4219
+ case "reference":
4220
+ return { type: "reference", value: "me" };
4221
+ case "expression":
4222
+ return sampleExpression2(role.role);
4223
+ case "flag":
4224
+ return { type: "flag", value: true };
4225
+ default:
4226
+ return { type: "literal", value: "example" };
4227
+ }
4228
+ }
4229
+ function sampleSelector2(roleName) {
4230
+ const selectors = {
4231
+ patient: ".active",
4232
+ destination: "#output",
4233
+ source: "#input",
4234
+ target: "#target"
4235
+ };
4236
+ return { type: "selector", value: selectors[roleName] || `#${roleName}` };
4237
+ }
4238
+ function sampleLiteral2(roleName) {
4239
+ const literals = {
4240
+ duration: "30s",
4241
+ interval: "5s",
4242
+ delay: "500ms",
4243
+ timeout: "10s",
4244
+ quantity: 5,
4245
+ limit: 10,
4246
+ style: "json",
4247
+ manner: "rolling",
4248
+ format: "html"
4249
+ };
4250
+ return { type: "literal", value: literals[roleName] || roleName };
4251
+ }
4252
+ function sampleExpression2(roleName) {
4253
+ const expressions = {
4254
+ source: "/api/data",
4255
+ destination: "#output",
4256
+ patient: ".active",
4257
+ instrument: "toUpperCase",
4258
+ condition: "age > 18",
4259
+ style: "json",
4260
+ duration: "5s",
4261
+ url: "/api/users"
4262
+ };
4263
+ return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
4264
+ }
4265
+ function toSemanticValue2(sample) {
4266
+ switch (sample.type) {
4267
+ case "selector":
2127
4268
  return {
2128
- ok: true,
2129
- code,
2130
- node: result.node,
2131
- metadata: {
2132
- parser: "semantic",
2133
- confidence: result.confidence
2134
- }
4269
+ type: "selector",
4270
+ value: String(sample.value),
4271
+ selectorKind: detectSelectorKind3(String(sample.value))
2135
4272
  };
2136
- } catch (error) {
4273
+ case "literal":
2137
4274
  return {
2138
- ok: false,
2139
- errors: [error instanceof Error ? error.message : String(error)]
4275
+ type: "literal",
4276
+ value: sample.value,
4277
+ dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
2140
4278
  };
4279
+ case "reference":
4280
+ return { type: "reference", value: String(sample.value) };
4281
+ case "expression":
4282
+ return { type: "expression", value: String(sample.value), raw: String(sample.value) };
4283
+ case "flag":
4284
+ return { type: "flag", value: sample.value };
4285
+ default:
4286
+ return { type: "literal", value: String(sample.value), dataType: "string" };
4287
+ }
4288
+ }
4289
+ function detectSelectorKind3(selector) {
4290
+ if (selector.startsWith("#")) return "id";
4291
+ if (selector.startsWith(".")) return "class";
4292
+ if (selector.startsWith("[")) return "attribute";
4293
+ return "complex";
4294
+ }
4295
+
4296
+ // src/feedback/feedback-formatter.ts
4297
+ function buildFeedback(input, inputFormat, diagnostics, schemaLookup, action) {
4298
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
4299
+ const feedbackDiagnostics = diagnostics.map((d) => toFeedbackDiagnostic(d));
4300
+ const hints = generateHints(feedbackDiagnostics, schemaLookup, action);
4301
+ let schemaInfo;
4302
+ let correctedExample;
4303
+ if (action && schemaLookup) {
4304
+ const schema = schemaLookup.getSchema(action);
4305
+ if (schema) {
4306
+ schemaInfo = extractSchemaInfo(schema);
4307
+ if (hasErrors) {
4308
+ correctedExample = generateCorrectedExample(schema);
4309
+ }
2141
4310
  }
2142
4311
  }
2143
- getSupportedLanguages() {
2144
- return this.registry.getSupportedLanguages();
4312
+ return {
4313
+ accepted: !hasErrors,
4314
+ input: { format: inputFormat, text: input },
4315
+ diagnostics: feedbackDiagnostics,
4316
+ hints,
4317
+ ...schemaInfo && { schema: schemaInfo },
4318
+ ...correctedExample && { correctedExample }
4319
+ };
4320
+ }
4321
+ function toFeedbackDiagnostic(d) {
4322
+ const fixType = classifyFixType(d);
4323
+ return {
4324
+ severity: d.severity,
4325
+ code: d.code || "unknown",
4326
+ message: d.message,
4327
+ ...d.suggestions?.[0] && { suggestion: d.suggestions[0] },
4328
+ ...fixType && { fixType }
4329
+ };
4330
+ }
4331
+ function classifyFixType(d) {
4332
+ const code = (d.code || "").toLowerCase();
4333
+ const msg = d.message.toLowerCase();
4334
+ if (code.includes("missing-role") || code.includes("missing_role") || msg.includes("required role") || msg.includes("missing role")) {
4335
+ return "missing_role";
2145
4336
  }
2146
- };
2147
- function createDefaultDictionary(config) {
2148
- const translations = {};
2149
- for (const lang of config.languages) {
2150
- const keywords = {};
2151
- for (const [canonical, keywordDef] of Object.entries(lang.patternProfile.keywords)) {
2152
- keywords[canonical] = keywordDef.primary;
4337
+ if (code.includes("invalid-type") || code.includes("invalid_type") || code.includes("type-mismatch") || msg.includes("invalid type") || msg.includes("expected type")) {
4338
+ return "invalid_type";
4339
+ }
4340
+ if (code.includes("unknown-command") || code.includes("unknown_command") || msg.includes("unknown command") || msg.includes("not recognized") && msg.includes("command")) {
4341
+ return "unknown_command";
4342
+ }
4343
+ if (code.includes("unknown-role") || code.includes("unknown_role") || msg.includes("unknown role") || msg.includes("not recognized") && msg.includes("role")) {
4344
+ return "unknown_role";
4345
+ }
4346
+ if (code.includes("parse") || code.includes("syntax") || msg.includes("syntax error") || msg.includes("parse error") || msg.includes("unexpected")) {
4347
+ return "syntax_error";
4348
+ }
4349
+ return void 0;
4350
+ }
4351
+ function generateHints(diagnostics, schemaLookup, action) {
4352
+ const hints = [];
4353
+ const schema = action && schemaLookup ? schemaLookup.getSchema(action) : void 0;
4354
+ for (const d of diagnostics) {
4355
+ if (d.severity !== "error") continue;
4356
+ switch (d.fixType) {
4357
+ case "missing_role": {
4358
+ if (schema) {
4359
+ const required = schema.roles.filter((r) => r.required).map((r) => `'${r.role}' (${r.description}, type: ${r.expectedTypes.join("|")})`).join(", ");
4360
+ hints.push(`Required roles for '${action}': ${required}`);
4361
+ } else {
4362
+ hints.push("Check that all required roles are present.");
4363
+ }
4364
+ break;
4365
+ }
4366
+ case "unknown_command": {
4367
+ hints.push(
4368
+ `Command not recognized. Ensure you're using a valid command from the domain schema.`
4369
+ );
4370
+ break;
4371
+ }
4372
+ case "unknown_role": {
4373
+ if (schema) {
4374
+ const validRoles = schema.roles.map((r) => `'${r.role}'`).join(", ");
4375
+ hints.push(`Valid roles for '${action}': ${validRoles}`);
4376
+ } else {
4377
+ hints.push("Use only roles defined in the command schema.");
4378
+ }
4379
+ break;
4380
+ }
4381
+ case "invalid_type": {
4382
+ hints.push(
4383
+ "Check value types: selectors start with #.[@*, strings use quotes, numbers are plain digits."
4384
+ );
4385
+ break;
4386
+ }
4387
+ case "syntax_error": {
4388
+ hints.push("Ensure bracket syntax: [command role:value ...]. No spaces around the colon.");
4389
+ break;
4390
+ }
4391
+ default: {
4392
+ if (d.suggestion) {
4393
+ hints.push(d.suggestion);
4394
+ }
4395
+ }
2153
4396
  }
2154
- translations[lang.code] = keywords;
2155
4397
  }
2156
- return new InMemoryDictionary(translations);
4398
+ return [...new Set(hints)];
2157
4399
  }
2158
- function createDefaultProfileProvider(config) {
2159
- const profiles = {};
2160
- for (const lang of config.languages) {
2161
- if (lang.grammarProfile) {
2162
- profiles[lang.code] = lang.grammarProfile;
4400
+ function extractSchemaInfo(schema) {
4401
+ const requiredRoles = [];
4402
+ const optionalRoles = [];
4403
+ const roleDescriptions = {};
4404
+ for (const role of schema.roles) {
4405
+ if (role.required) {
4406
+ requiredRoles.push(role.role);
4407
+ } else {
4408
+ optionalRoles.push(role.role);
2163
4409
  }
4410
+ roleDescriptions[role.role] = role.description;
2164
4411
  }
2165
- return new InMemoryProfileProvider(profiles);
4412
+ return {
4413
+ action: schema.action,
4414
+ requiredRoles,
4415
+ optionalRoles,
4416
+ roleDescriptions
4417
+ };
2166
4418
  }
2167
- function createMultilingualDSL(config) {
2168
- const dictionary = config.dictionary ?? createDefaultDictionary(config);
2169
- const profileProvider = config.profileProvider ?? createDefaultProfileProvider(config);
2170
- const transformer = new GrammarTransformer({
2171
- dictionary,
2172
- profileProvider
2173
- });
2174
- const registry = new DSLRegistry(config);
2175
- return new MultilingualDSLImpl(config, registry, transformer);
4419
+ function generateCorrectedExample(schema) {
4420
+ const roles = /* @__PURE__ */ new Map();
4421
+ const jsonRoles = {};
4422
+ for (const role of schema.roles) {
4423
+ if (!role.required) continue;
4424
+ const sample = sampleDefaultValue(role.role, role.expectedTypes[0] || "expression");
4425
+ roles.set(role.role, toSemanticValue3(sample));
4426
+ jsonRoles[role.role] = sample;
4427
+ }
4428
+ const node = createCommandNode(schema.action, roles);
4429
+ const explicit = renderExplicit(node);
4430
+ const json = { action: schema.action, roles: jsonRoles };
4431
+ return { explicit, json };
4432
+ }
4433
+ function sampleDefaultValue(roleName, expectedType) {
4434
+ switch (expectedType) {
4435
+ case "selector":
4436
+ return { type: "selector", value: `#${roleName}` };
4437
+ case "expression":
4438
+ return { type: "expression", value: `<${roleName}-value>` };
4439
+ case "literal":
4440
+ return { type: "literal", value: `<${roleName}-value>` };
4441
+ case "reference":
4442
+ return { type: "reference", value: "me" };
4443
+ default:
4444
+ return { type: "expression", value: `<${roleName}-value>` };
4445
+ }
4446
+ }
4447
+ function toSemanticValue3(sample) {
4448
+ switch (sample.type) {
4449
+ case "selector":
4450
+ return { type: "selector", value: sample.value, selectorKind: "id" };
4451
+ case "expression":
4452
+ return { type: "expression", value: sample.value, raw: sample.value };
4453
+ case "literal":
4454
+ return { type: "literal", value: sample.value, dataType: "string" };
4455
+ case "reference":
4456
+ return { type: "reference", value: sample.value };
4457
+ default:
4458
+ return { type: "literal", value: sample.value, dataType: "string" };
4459
+ }
2176
4460
  }
2177
4461
 
2178
4462
  // src/api/domain-registry.ts
@@ -2192,6 +4476,17 @@ var DomainRegistry = class {
2192
4476
  }
2193
4477
  this.descriptors.set(descriptor.name, descriptor);
2194
4478
  }
4479
+ /**
4480
+ * Attach schemas to an already-registered domain.
4481
+ * Useful for lazy loading schemas after initial registration.
4482
+ */
4483
+ setSchemas(name, schemas) {
4484
+ const existing = this.descriptors.get(name);
4485
+ if (!existing) {
4486
+ throw new Error(`Domain not registered: ${name}`);
4487
+ }
4488
+ this.descriptors.set(name, { ...existing, schemas });
4489
+ }
2195
4490
  /**
2196
4491
  * Get all registered domain names.
2197
4492
  */
@@ -2261,6 +4556,71 @@ var DomainRegistry = class {
2261
4556
  return this.getDSL(descriptor);
2262
4557
  }
2263
4558
  // ---------------------------------------------------------------------------
4559
+ // LLM ↔ LSE Convenience Methods
4560
+ // ---------------------------------------------------------------------------
4561
+ /**
4562
+ * Generate an LLM system prompt for a domain from its schemas.
4563
+ * Returns null if the domain is not registered or has no schemas.
4564
+ */
4565
+ generatePrompt(domain, config) {
4566
+ const descriptor = this.descriptors.get(domain);
4567
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
4568
+ return generatePrompt({
4569
+ domain,
4570
+ description: descriptor.description,
4571
+ schemas: descriptor.schemas,
4572
+ ...config
4573
+ });
4574
+ }
4575
+ /**
4576
+ * Synthesize training data for a domain from its schemas.
4577
+ * Returns null if the domain is not registered or has no schemas.
4578
+ */
4579
+ generateTrainingData(domain, config) {
4580
+ const descriptor = this.descriptors.get(domain);
4581
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
4582
+ return synthesizeFromSchemas(descriptor.schemas, {
4583
+ domain,
4584
+ ...config
4585
+ });
4586
+ }
4587
+ /**
4588
+ * Build structured feedback for a failed LSE input in a domain.
4589
+ * Returns null if the domain is not registered or has no schemas.
4590
+ */
4591
+ buildFeedback(domain, input, inputFormat, diagnostics) {
4592
+ const descriptor = this.descriptors.get(domain);
4593
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
4594
+ const schemaLookup = this.createSchemaLookup(descriptor.schemas);
4595
+ let action;
4596
+ try {
4597
+ if (inputFormat === "explicit") {
4598
+ const node = parseExplicit(input, { schemaLookup });
4599
+ action = node.action;
4600
+ }
4601
+ } catch {
4602
+ }
4603
+ return buildFeedback(input, inputFormat, diagnostics, schemaLookup, action);
4604
+ }
4605
+ /**
4606
+ * Get schemas for a domain (if available).
4607
+ */
4608
+ getSchemas(domain) {
4609
+ const descriptor = this.descriptors.get(domain);
4610
+ if (!descriptor?.schemas) return null;
4611
+ return descriptor.schemas;
4612
+ }
4613
+ /**
4614
+ * Create a SchemaLookup from a domain's schemas.
4615
+ */
4616
+ createSchemaLookup(schemas) {
4617
+ const map = /* @__PURE__ */ new Map();
4618
+ for (const s of schemas) {
4619
+ map.set(s.action, s);
4620
+ }
4621
+ return { getSchema: (action) => map.get(action) };
4622
+ }
4623
+ // ---------------------------------------------------------------------------
2264
4624
  // Private: DSL/renderer lifecycle
2265
4625
  // ---------------------------------------------------------------------------
2266
4626
  async getDSL(descriptor) {
@@ -2360,7 +4720,7 @@ function generateToolDefinitions(desc) {
2360
4720
  case "parse":
2361
4721
  tools.push({
2362
4722
  name: `parse_${desc.name}`,
2363
- description: `Parse a ${desc.description} input into a semantic representation. Supports: ${langList}.`,
4723
+ description: `Parse a ${desc.description} input into a semantic representation (action, roles, confidence). Supports: ${langList}.`,
2364
4724
  inputSchema: {
2365
4725
  type: "object",
2366
4726
  properties: {
@@ -2381,7 +4741,7 @@ function generateToolDefinitions(desc) {
2381
4741
  case "compile":
2382
4742
  tools.push({
2383
4743
  name: `compile_${desc.name}`,
2384
- description: `Compile a ${desc.description} input to target code. Supports: ${langList}.`,
4744
+ description: `Compile a ${desc.description} input to ${desc.outputDescription ?? "target code"}. Supports: ${langList}.`,
2385
4745
  inputSchema: {
2386
4746
  type: "object",
2387
4747
  properties: {
@@ -2402,7 +4762,7 @@ function generateToolDefinitions(desc) {
2402
4762
  case "validate":
2403
4763
  tools.push({
2404
4764
  name: `validate_${desc.name}`,
2405
- description: `Validate ${desc.description} syntax. Returns whether it parses successfully and any errors. Supports: ${langList}.`,
4765
+ description: `Check ${desc.description} syntax for errors. Returns {valid, errors[]}. Supports: ${langList}.`,
2406
4766
  inputSchema: {
2407
4767
  type: "object",
2408
4768
  properties: {
@@ -2423,7 +4783,7 @@ function generateToolDefinitions(desc) {
2423
4783
  case "translate":
2424
4784
  tools.push({
2425
4785
  name: `translate_${desc.name}`,
2426
- description: `Translate ${desc.description} input between natural languages. Parses in source language and renders in target language.`,
4786
+ description: `Translate ${desc.description} input between natural languages (requires 'from' and 'to' codes). Supports: ${langList}.`,
2427
4787
  inputSchema: {
2428
4788
  type: "object",
2429
4789
  properties: {
@@ -2489,9 +4849,9 @@ var CrossDomainDispatcher = class {
2489
4849
  if (domainNames.length === 0) return null;
2490
4850
  const candidates = [];
2491
4851
  for (const name of domainNames) {
2492
- const dsl = await this.registry.getDSLForDomain(name);
2493
- if (!dsl) continue;
2494
4852
  try {
4853
+ const dsl = await this.registry.getDSLForDomain(name);
4854
+ if (!dsl) continue;
2495
4855
  const { node, confidence } = dsl.parseWithConfidence(input, language);
2496
4856
  if (confidence >= this.minConfidence) {
2497
4857
  candidates.push({ domain: name, node, confidence, dsl });
@@ -2500,10 +4860,16 @@ var CrossDomainDispatcher = class {
2500
4860
  }
2501
4861
  }
2502
4862
  if (candidates.length === 0) return null;
2503
- candidates.sort((a, b) => {
2504
- if (b.confidence !== a.confidence) return b.confidence - a.confidence;
2505
- return this.getPriorityIndex(a.domain) - this.getPriorityIndex(b.domain);
2506
- });
4863
+ if (this.priority.length > 0) {
4864
+ candidates.sort((a, b) => {
4865
+ const pa = this.getPriorityIndex(a.domain);
4866
+ const pb = this.getPriorityIndex(b.domain);
4867
+ if (pa !== pb) return pa - pb;
4868
+ return b.confidence - a.confidence;
4869
+ });
4870
+ } else {
4871
+ candidates.sort((a, b) => b.confidence - a.confidence);
4872
+ }
2507
4873
  return candidates[0];
2508
4874
  }
2509
4875
  /**
@@ -2547,6 +4913,40 @@ var CrossDomainDispatcher = class {
2547
4913
  const result = detected.dsl.compile(input, language);
2548
4914
  return { ...result, domain: detected.domain };
2549
4915
  }
4916
+ /**
4917
+ * Compile multi-line input where each line may belong to a different domain.
4918
+ * Detects the domain for each line and compiles it, returning per-line results.
4919
+ * Empty lines and comment lines are skipped.
4920
+ */
4921
+ async compileComposite(input, language = "en") {
4922
+ const lines = input.split("\n");
4923
+ const statements = [];
4924
+ const errors = [];
4925
+ for (let i = 0; i < lines.length; i++) {
4926
+ const line = lines[i];
4927
+ if (isSkippableLine(line)) continue;
4928
+ const trimmed = line.trim();
4929
+ const detected = await this.detect(trimmed, language);
4930
+ if (detected) {
4931
+ const result = detected.dsl.compile(trimmed, language);
4932
+ statements.push({
4933
+ line: i + 1,
4934
+ input: trimmed,
4935
+ domain: detected.domain,
4936
+ ok: result.ok,
4937
+ code: result.code,
4938
+ errors: result.errors
4939
+ });
4940
+ } else {
4941
+ errors.push({
4942
+ line: i + 1,
4943
+ input: trimmed,
4944
+ message: "No domain matched this input"
4945
+ });
4946
+ }
4947
+ }
4948
+ return { statements, errors };
4949
+ }
2550
4950
  /**
2551
4951
  * Validate input against all domains, returning the best match.
2552
4952
  */
@@ -2828,6 +5228,30 @@ ${body}
2828
5228
  }
2829
5229
  };
2830
5230
 
5231
+ // src/aot/registry-bridge.ts
5232
+ async function registryToAOTBackends(registry) {
5233
+ const backends = [];
5234
+ for (const name of registry.getDomainNames()) {
5235
+ const descriptor = registry.getDescriptor(name);
5236
+ if (!descriptor?.scanConfig) continue;
5237
+ const dsl = await registry.getDSLForDomain(name);
5238
+ if (!dsl) continue;
5239
+ const scanConfig = {
5240
+ domain: name,
5241
+ attributes: [...descriptor.scanConfig.attributes],
5242
+ ...descriptor.scanConfig.scriptTypes ? { scriptTypes: [...descriptor.scanConfig.scriptTypes] } : {},
5243
+ defaultLanguage: descriptor.scanConfig.defaultLanguage ?? "en"
5244
+ };
5245
+ const codeGenerator = {
5246
+ generate(node) {
5247
+ return JSON.stringify({ action: node.action, roles: Object.fromEntries(node.roles) });
5248
+ }
5249
+ };
5250
+ backends.push({ domain: name, dsl, codeGenerator, scanConfig });
5251
+ }
5252
+ return backends;
5253
+ }
5254
+
2831
5255
  // src/schema/command-schema.ts
2832
5256
  function defineCommand(schema) {
2833
5257
  return {
@@ -4119,6 +6543,163 @@ function normalized(stem, confidence, metadata) {
4119
6543
  return { stem, confidence };
4120
6544
  }
4121
6545
 
6546
+ // src/training/jsonl-writer.ts
6547
+ function toJSONL(pairs) {
6548
+ return pairs.map((pair) => JSON.stringify(toJSONLRow(pair))).join("\n");
6549
+ }
6550
+ function toJSONLRow(pair) {
6551
+ return {
6552
+ id: pair.id,
6553
+ prompt: pair.natural,
6554
+ completion: pair.explicit,
6555
+ metadata: {
6556
+ domain: pair.domain,
6557
+ action: pair.action,
6558
+ language: pair.language,
6559
+ source: pair.source,
6560
+ quality: pair.quality,
6561
+ ...pair.confidence !== void 0 && { confidence: pair.confidence }
6562
+ }
6563
+ };
6564
+ }
6565
+ function parseJSONL(text) {
6566
+ return text.split("\n").filter((line) => line.trim().length > 0).map((line) => JSON.parse(line));
6567
+ }
6568
+
6569
+ // src/feedback/confidence-gate.ts
6570
+ function needsDisambiguation(confidence, low = 0.5, high = 0.7) {
6571
+ return confidence >= low && confidence < high;
6572
+ }
6573
+ function buildDisambiguation(input, candidates) {
6574
+ const sortedCandidates = [...candidates].sort((a, b) => b.confidence - a.confidence);
6575
+ const disambiguationCandidates = sortedCandidates.map((c, i) => ({
6576
+ action: c.action,
6577
+ confidence: c.confidence,
6578
+ explicit: renderExplicit(c.node),
6579
+ description: c.description || `${c.action} (option ${String.fromCharCode(97 + i)})`
6580
+ }));
6581
+ const optionLines = disambiguationCandidates.map(
6582
+ (c, i) => ` (${String.fromCharCode(97 + i)}) ${c.explicit} \u2014 ${c.description} (conf: ${c.confidence.toFixed(2)})`
6583
+ );
6584
+ const question = [
6585
+ `Your input parsed with confidence ${sortedCandidates[0]?.confidence.toFixed(2) ?? "?"}.`,
6586
+ ...optionLines,
6587
+ "Which did you mean?"
6588
+ ].join("\n");
6589
+ return {
6590
+ input,
6591
+ candidates: disambiguationCandidates,
6592
+ question
6593
+ };
6594
+ }
6595
+
6596
+ // src/feedback/pattern-tracker.ts
6597
+ var PatternTracker = class {
6598
+ constructor(maxEvents = 1e3) {
6599
+ this.events = [];
6600
+ this.maxEvents = maxEvents;
6601
+ }
6602
+ /**
6603
+ * Record a pattern event.
6604
+ */
6605
+ record(event) {
6606
+ this.events.push(event);
6607
+ if (this.events.length > this.maxEvents) {
6608
+ this.events = this.events.slice(this.events.length - this.maxEvents);
6609
+ }
6610
+ }
6611
+ /**
6612
+ * Get hit rate by command action.
6613
+ */
6614
+ getHitRateByCommand() {
6615
+ return this.computeHitRate((e) => e.action);
6616
+ }
6617
+ /**
6618
+ * Get hit rate by language.
6619
+ */
6620
+ getHitRateByLanguage() {
6621
+ return this.computeHitRate((e) => e.language);
6622
+ }
6623
+ /**
6624
+ * Get the most frequent failure diagnostic codes.
6625
+ */
6626
+ getTopFailures(limit = 10) {
6627
+ const codeMap = /* @__PURE__ */ new Map();
6628
+ for (const event of this.events) {
6629
+ if (event.outcome !== "rejected" || !event.diagnosticCodes) continue;
6630
+ for (const code of event.diagnosticCodes) {
6631
+ const entry = codeMap.get(code) || { count: 0, actions: /* @__PURE__ */ new Set() };
6632
+ entry.count++;
6633
+ entry.actions.add(event.action);
6634
+ codeMap.set(code, entry);
6635
+ }
6636
+ }
6637
+ return Array.from(codeMap.entries()).map(([code, { count, actions }]) => ({
6638
+ code,
6639
+ count,
6640
+ actions: Array.from(actions)
6641
+ })).sort((a, b) => b.count - a.count).slice(0, limit);
6642
+ }
6643
+ /**
6644
+ * Get a complete summary of tracking data.
6645
+ */
6646
+ getSummary() {
6647
+ const byOutcome = {};
6648
+ for (const event of this.events) {
6649
+ byOutcome[event.outcome] = (byOutcome[event.outcome] || 0) + 1;
6650
+ }
6651
+ return {
6652
+ totalEvents: this.events.length,
6653
+ byOutcome,
6654
+ byCommand: this.getHitRateByCommand(),
6655
+ byLanguage: this.getHitRateByLanguage(),
6656
+ topFailures: this.getTopFailures()
6657
+ };
6658
+ }
6659
+ /**
6660
+ * Export all events as JSONL.
6661
+ */
6662
+ exportJSONL() {
6663
+ return this.events.map((e) => JSON.stringify(e)).join("\n");
6664
+ }
6665
+ /**
6666
+ * Get the number of recorded events.
6667
+ */
6668
+ get size() {
6669
+ return this.events.length;
6670
+ }
6671
+ /**
6672
+ * Clear all events.
6673
+ */
6674
+ clear() {
6675
+ this.events = [];
6676
+ }
6677
+ // ---------------------------------------------------------------------------
6678
+ // Private
6679
+ // ---------------------------------------------------------------------------
6680
+ computeHitRate(keyFn) {
6681
+ const map = /* @__PURE__ */ new Map();
6682
+ for (const event of this.events) {
6683
+ const key = keyFn(event);
6684
+ const entry = map.get(key) || { attempts: 0, successes: 0 };
6685
+ entry.attempts++;
6686
+ if (event.outcome === "accepted") {
6687
+ entry.successes++;
6688
+ }
6689
+ map.set(key, entry);
6690
+ }
6691
+ const result = {};
6692
+ for (const [key, { attempts, successes }] of map) {
6693
+ result[key] = {
6694
+ attempts,
6695
+ successes,
6696
+ rate: attempts > 0 ? successes / attempts : 0
6697
+ };
6698
+ }
6699
+ return result;
6700
+ }
6701
+ };
6702
+
4122
6703
  // src/parsing/multi-statement.ts
4123
6704
  function createMultiStatementParser(dsl, config) {
4124
6705
  return new MultiStatementParserImpl(dsl, config);
@@ -4363,6 +6944,7 @@ function accumulateIndented(statements, config) {
4363
6944
  CrossDomainDispatcher,
4364
6945
  DEFAULT_OPERATORS,
4365
6946
  DEFAULT_PUNCTUATION,
6947
+ DEFAULT_REFERENCES,
4366
6948
  DomainAwareScanner,
4367
6949
  DomainRegistry,
4368
6950
  GrammarTransformer,
@@ -4374,24 +6956,31 @@ function accumulateIndented(statements, config) {
4374
6956
  NumberExtractor,
4375
6957
  OperatorExtractor,
4376
6958
  PatternMatcher,
6959
+ PatternTracker,
4377
6960
  PunctuationExtractor,
4378
6961
  StringLiteralExtractor,
4379
6962
  TokenStreamImpl,
4380
6963
  UnicodeIdentifierExtractor,
4381
6964
  WhitespaceExtractor,
4382
6965
  accumulateBlocks,
6966
+ buildDisambiguation,
6967
+ buildFeedback,
4383
6968
  buildPhrase,
4384
6969
  buildTablesFromProfiles,
4385
6970
  combineClassifiers,
6971
+ convertValue,
6972
+ createAsyncNode,
4386
6973
  createCommandNode,
4387
6974
  createCompoundNode,
4388
6975
  createConditionalNode,
4389
6976
  createDiagnosticCollector,
4390
6977
  createEventHandlerNode,
4391
6978
  createExpression,
6979
+ createFlag,
4392
6980
  createLatinCharClassifiers,
4393
6981
  createLiteral,
4394
6982
  createLoopNode,
6983
+ createMatchNode,
4395
6984
  createMultiStatementParser,
4396
6985
  createMultilingualDSL,
4397
6986
  createPosition,
@@ -4402,6 +6991,7 @@ function accumulateIndented(statements, config) {
4402
6991
  createSimpleTokenizer,
4403
6992
  createToken,
4404
6993
  createTokenizerContext,
6994
+ createTryNode,
4405
6995
  createUnicodeRangeClassifier,
4406
6996
  defineCommand,
4407
6997
  defineRole,
@@ -4413,9 +7003,15 @@ function accumulateIndented(statements, config) {
4413
7003
  extractUrl,
4414
7004
  extractValue,
4415
7005
  filterBySeverity,
7006
+ fromEnvelopeJSON,
4416
7007
  fromError,
7008
+ fromInterchangeNode,
7009
+ fromProtocolJSON,
7010
+ generateExamples,
4417
7011
  generatePattern,
4418
7012
  generatePatternVariants,
7013
+ generatePrompt,
7014
+ generateProtocolReference,
4419
7015
  getAllPossessiveKeywords,
4420
7016
  getDefaultExtractors,
4421
7017
  getPossessiveReference,
@@ -4428,6 +7024,8 @@ function accumulateIndented(statements, config) {
4428
7024
  isClassName,
4429
7025
  isContextAwareExtractor,
4430
7026
  isDigit,
7027
+ isEnvelope,
7028
+ isExplicitSyntax,
4431
7029
  isIdSelector,
4432
7030
  isNumericValue,
4433
7031
  isPossessiveKeyword,
@@ -4437,17 +7035,33 @@ function accumulateIndented(statements, config) {
4437
7035
  isSelectorStart,
4438
7036
  isTypeCompatible,
4439
7037
  isUrlStart,
7038
+ isValidReference,
4440
7039
  isVariableRef,
4441
7040
  isWhitespace,
4442
7041
  joinTokens,
7042
+ jsonToSemanticNode,
4443
7043
  lookupKeyword,
4444
7044
  lookupMarker,
4445
7045
  matchBest,
4446
7046
  matchPattern,
7047
+ needsDisambiguation,
4447
7048
  noChange,
4448
7049
  normalized,
7050
+ parseExplicit,
7051
+ parseJSONL,
4449
7052
  patternMatcher,
7053
+ registryToAOTBackends,
7054
+ renderExplicit,
7055
+ renderExpr,
4450
7056
  reorderRoles,
7057
+ semanticNodeToJSON,
7058
+ synthesizeFromSchemas,
7059
+ toEnvelopeJSON,
7060
+ toJSONL,
7061
+ toJSONLRow,
7062
+ toProtocolJSON,
7063
+ validateProtocolJSON,
7064
+ validateSemanticJSON,
4451
7065
  validateValueType,
4452
7066
  withDefaultExtractors
4453
7067
  });