@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/api/index.js CHANGED
@@ -11,6 +11,65 @@ function createReference(value) {
11
11
  function createPropertyPath(object, property) {
12
12
  return { type: "property-path", object, property };
13
13
  }
14
+ function createFlag(name, enabled = true) {
15
+ return { type: "flag", name, enabled };
16
+ }
17
+ function createCommandNode(action, roles, metadata) {
18
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
19
+ const node = {
20
+ kind: "command",
21
+ action,
22
+ roles: rolesMap
23
+ };
24
+ if (metadata) {
25
+ return { ...node, metadata };
26
+ }
27
+ return node;
28
+ }
29
+ function createEventHandlerNode(action, roles, body, metadata, eventModifiers) {
30
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
31
+ const base = {
32
+ kind: "event-handler",
33
+ action,
34
+ roles: rolesMap,
35
+ body
36
+ };
37
+ return {
38
+ ...base,
39
+ ...eventModifiers && { eventModifiers },
40
+ ...metadata && { metadata }
41
+ };
42
+ }
43
+ function createConditionalNode(action, roles, thenBranch, elseBranch, metadata) {
44
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
45
+ const base = {
46
+ kind: "conditional",
47
+ action,
48
+ roles: rolesMap,
49
+ thenBranch
50
+ };
51
+ return {
52
+ ...base,
53
+ ...elseBranch && { elseBranch },
54
+ ...metadata && { metadata }
55
+ };
56
+ }
57
+ function createLoopNode(action, roles, loopVariant, body, loopVariable, indexVariable, metadata) {
58
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
59
+ const base = {
60
+ kind: "loop",
61
+ action,
62
+ roles: rolesMap,
63
+ loopVariant,
64
+ body
65
+ };
66
+ return {
67
+ ...base,
68
+ ...loopVariable && { loopVariable },
69
+ ...indexVariable && { indexVariable },
70
+ ...metadata && { metadata }
71
+ };
72
+ }
14
73
 
15
74
  // src/core/logger.ts
16
75
  var DebugLogger = class {
@@ -224,7 +283,7 @@ var _PatternMatcher = class _PatternMatcher {
224
283
  * a token and the immediately following pattern token fails, the matcher
225
284
  * resets to before the optional role and retries the failed token.
226
285
  */
227
- matchTokenSequence(tokens, patternTokens, captured) {
286
+ matchTokenSequence(tokens, patternTokens, captured, parentStopMarkers) {
228
287
  const firstPatternToken = patternTokens[0];
229
288
  const patternExpectsConjunction = firstPatternToken?.type === "literal" && (firstPatternToken.value === "and" || firstPatternToken.value === "then" || firstPatternToken.alternatives?.includes("and") || firstPatternToken.alternatives?.includes("then"));
230
289
  if (this.currentProfile?.code === "ar" && !patternExpectsConjunction) {
@@ -248,6 +307,9 @@ var _PatternMatcher = class _PatternMatcher {
248
307
  );
249
308
  if (patternToken.type === "role" && patternToken.greedy) {
250
309
  const stopMarkers = this.collectStopMarkers(patternTokens, i + 1);
310
+ if (parentStopMarkers) {
311
+ for (const m of parentStopMarkers) stopMarkers.add(m);
312
+ }
251
313
  const values = [];
252
314
  while (!tokens.isAtEnd()) {
253
315
  const nextToken = tokens.peek();
@@ -267,8 +329,58 @@ var _PatternMatcher = class _PatternMatcher {
267
329
  return false;
268
330
  }
269
331
  }
332
+ if (patternToken.type === "group") {
333
+ const siblingStopMarkers = this.collectStopMarkers(patternTokens, i + 1);
334
+ if (parentStopMarkers) {
335
+ for (const m of parentStopMarkers) siblingStopMarkers.add(m);
336
+ }
337
+ const groupMatched = this.matchGroupToken(
338
+ tokens,
339
+ patternToken,
340
+ captured,
341
+ siblingStopMarkers
342
+ );
343
+ if (groupMatched) {
344
+ prevOptionalMark = null;
345
+ prevOptionalRole = null;
346
+ continue;
347
+ }
348
+ if (this.isOptional(patternToken)) {
349
+ continue;
350
+ }
351
+ return false;
352
+ }
270
353
  const isOptionalRole = patternToken.type === "role" && patternToken.optional === true;
271
354
  const markBefore = isOptionalRole ? tokens.mark() : null;
355
+ const isRoleToken = patternToken.type === "role";
356
+ if (isRoleToken && !isOptionalRole) {
357
+ const nextPT = i + 1 < patternTokens.length ? patternTokens[i + 1] : null;
358
+ if (nextPT?.type === "literal") {
359
+ const currentToken = tokens.peek();
360
+ if (currentToken && this.getMatchType(currentToken, nextPT.value) !== "none") {
361
+ this.logger.debug(
362
+ " >> MARKER-STEAL GUARD: current token matches next literal, skipping role"
363
+ );
364
+ this.logger.debug(" >> Token match FAILED");
365
+ if (this.isOptional(patternToken)) {
366
+ continue;
367
+ }
368
+ if (prevOptionalMark && prevOptionalRole) {
369
+ this.logger.debug(" >> BACKTRACKING: undoing optional role", prevOptionalRole);
370
+ tokens.reset(prevOptionalMark);
371
+ captured.delete(prevOptionalRole);
372
+ prevOptionalMark = null;
373
+ prevOptionalRole = null;
374
+ const retryMatched = this.matchPatternToken(tokens, patternToken, captured);
375
+ this.logger.debug(" >> Backtrack retry result:", retryMatched);
376
+ if (retryMatched) {
377
+ continue;
378
+ }
379
+ }
380
+ return false;
381
+ }
382
+ }
383
+ }
272
384
  const matched = this.matchPatternToken(tokens, patternToken, captured);
273
385
  this.logger.debug(" >> Match result:", matched);
274
386
  if (matched) {
@@ -389,7 +501,7 @@ var _PatternMatcher = class _PatternMatcher {
389
501
  const possessiveValue = this.tryMatchPossessiveExpression(tokens);
390
502
  if (possessiveValue) {
391
503
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
392
- if (!patternToken.expectedTypes.includes(possessiveValue.type) && !patternToken.expectedTypes.includes("expression")) {
504
+ if (!isTypeCompatible(possessiveValue.type, patternToken.expectedTypes)) {
393
505
  return patternToken.optional || false;
394
506
  }
395
507
  }
@@ -399,7 +511,7 @@ var _PatternMatcher = class _PatternMatcher {
399
511
  const methodCallValue = this.tryMatchMethodCallExpression(tokens);
400
512
  if (methodCallValue) {
401
513
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
402
- if (!patternToken.expectedTypes.includes(methodCallValue.type) && !patternToken.expectedTypes.includes("expression")) {
514
+ if (!isTypeCompatible(methodCallValue.type, patternToken.expectedTypes)) {
403
515
  return patternToken.optional || false;
404
516
  }
405
517
  }
@@ -419,7 +531,7 @@ var _PatternMatcher = class _PatternMatcher {
419
531
  const propertyAccessValue = this.tryMatchPropertyAccessExpression(tokens);
420
532
  if (propertyAccessValue) {
421
533
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
422
- if (!patternToken.expectedTypes.includes(propertyAccessValue.type) && !patternToken.expectedTypes.includes("expression")) {
534
+ if (!isTypeCompatible(propertyAccessValue.type, patternToken.expectedTypes)) {
423
535
  return patternToken.optional || false;
424
536
  }
425
537
  }
@@ -739,10 +851,15 @@ var _PatternMatcher = class _PatternMatcher {
739
851
  * (e.g., a value like 'hello') to sit between groups without blocking later
740
852
  * marker matches.
741
853
  */
742
- matchGroupToken(tokens, patternToken, captured) {
854
+ matchGroupToken(tokens, patternToken, captured, parentStopMarkers) {
743
855
  const mark = tokens.mark();
744
856
  const capturedBefore = new Set(captured.keys());
745
- const success = this.matchTokenSequence(tokens, patternToken.tokens, captured);
857
+ const success = this.matchTokenSequence(
858
+ tokens,
859
+ patternToken.tokens,
860
+ captured,
861
+ parentStopMarkers
862
+ );
746
863
  if (success) return true;
747
864
  tokens.reset(mark);
748
865
  for (const role of captured.keys()) {
@@ -764,7 +881,12 @@ var _PatternMatcher = class _PatternMatcher {
764
881
  "- skipping intervening tokens"
765
882
  );
766
883
  for (let s = 0; s < offset; s++) tokens.advance();
767
- const retrySuccess = this.matchTokenSequence(tokens, patternToken.tokens, captured);
884
+ const retrySuccess = this.matchTokenSequence(
885
+ tokens,
886
+ patternToken.tokens,
887
+ captured,
888
+ parentStopMarkers
889
+ );
768
890
  if (retrySuccess) return true;
769
891
  tokens.reset(mark);
770
892
  for (const role of captured.keys()) {
@@ -831,7 +953,7 @@ var _PatternMatcher = class _PatternMatcher {
831
953
  break;
832
954
  }
833
955
  }
834
- break;
956
+ if (!pt.optional) break;
835
957
  }
836
958
  }
837
959
  return markers;
@@ -841,8 +963,9 @@ var _PatternMatcher = class _PatternMatcher {
841
963
  */
842
964
  isStopMarker(token, stopMarkers) {
843
965
  if (stopMarkers.size === 0) return false;
844
- const value = (token.normalized || token.value).toLowerCase();
845
- return stopMarkers.has(value);
966
+ if (stopMarkers.has(token.value.toLowerCase())) return true;
967
+ if (token.normalized && stopMarkers.has(token.normalized.toLowerCase())) return true;
968
+ return false;
846
969
  }
847
970
  /**
848
971
  * Convert a language token to a semantic value.
@@ -1241,7 +1364,7 @@ function addRoleWithMarker(tokens, roleSpec, profile) {
1241
1364
  if (marker) {
1242
1365
  const markerInfo = profile.roleMarkers?.[roleSpec.role];
1243
1366
  const defaultPosition = profile.wordOrder === "SOV" ? "after" : "before";
1244
- const position = markerInfo?.position ?? defaultPosition;
1367
+ const position = roleSpec.markerPosition ?? markerInfo?.position ?? defaultPosition;
1245
1368
  if (isOptional) {
1246
1369
  if (position === "before") {
1247
1370
  tokens.push({
@@ -1534,6 +1657,368 @@ var InMemoryProfileProvider = class {
1534
1657
  }
1535
1658
  };
1536
1659
 
1660
+ // src/ir/references.ts
1661
+ var DEFAULT_REFERENCES = /* @__PURE__ */ new Set([
1662
+ "me",
1663
+ "you",
1664
+ "it",
1665
+ "result",
1666
+ "event",
1667
+ "target",
1668
+ "body"
1669
+ ]);
1670
+ function isValidReference2(value, referenceSet = DEFAULT_REFERENCES) {
1671
+ return referenceSet.has(value);
1672
+ }
1673
+
1674
+ // src/ir/explicit-parser.ts
1675
+ var STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
1676
+ "body",
1677
+ "then",
1678
+ "else",
1679
+ "condition",
1680
+ "loop-body",
1681
+ "variable",
1682
+ "catch",
1683
+ // v1.2: try/catch/finally
1684
+ "finally"
1685
+ // v1.2: try/catch/finally
1686
+ ]);
1687
+ function isNestedCommand(value) {
1688
+ const inner = value.slice(1, -1);
1689
+ let depth = 0;
1690
+ for (const ch of inner) {
1691
+ if (ch === "[") depth++;
1692
+ else if (ch === "]") depth--;
1693
+ else if (depth === 0 && (ch === " " || ch === ":")) return true;
1694
+ }
1695
+ return false;
1696
+ }
1697
+ function parseExplicit(input, options = {}) {
1698
+ const trimmed = input.trim();
1699
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
1700
+ throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
1701
+ }
1702
+ const content = trimmed.slice(1, -1).trim();
1703
+ if (!content) {
1704
+ throw new Error("Empty explicit statement");
1705
+ }
1706
+ const tokens = tokenizeExplicit(content);
1707
+ if (tokens.length === 0) {
1708
+ throw new Error("No command specified in explicit statement");
1709
+ }
1710
+ const command = tokens[0].toLowerCase();
1711
+ const roles = /* @__PURE__ */ new Map();
1712
+ const refSet = options.referenceSet ?? DEFAULT_REFERENCES;
1713
+ const schema = options.schemaLookup?.getSchema(command);
1714
+ const validRoleNames = schema ? new Set(schema.roles.map((r) => r.role)) : null;
1715
+ for (let i = 1; i < tokens.length; i++) {
1716
+ const token = tokens[i];
1717
+ if (token.startsWith("+") || token.startsWith("~")) {
1718
+ const enabled = token.startsWith("+");
1719
+ const flagName = token.slice(1);
1720
+ if (!flagName) {
1721
+ throw new Error(`Empty flag name: "${token}"`);
1722
+ }
1723
+ if (validRoleNames && !validRoleNames.has(flagName)) {
1724
+ const roleList = [...validRoleNames].join(", ");
1725
+ throw new Error(
1726
+ `Unknown flag "${flagName}" for command "${command}". Valid roles: ${roleList}`
1727
+ );
1728
+ }
1729
+ roles.set(flagName, createFlag(flagName, enabled));
1730
+ continue;
1731
+ }
1732
+ const colonIndex = token.indexOf(":");
1733
+ if (colonIndex === -1) {
1734
+ throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
1735
+ }
1736
+ const roleName = token.slice(0, colonIndex);
1737
+ if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
1738
+ const roleList = [...validRoleNames].join(", ");
1739
+ throw new Error(
1740
+ `Unknown role "${roleName}" for command "${command}". Valid roles: ${roleList}`
1741
+ );
1742
+ }
1743
+ const role = roleName;
1744
+ const valueStr = token.slice(colonIndex + 1);
1745
+ if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[") && isNestedCommand(valueStr)) {
1746
+ const nestedEnd = findMatchingBracket(token, colonIndex + 1);
1747
+ const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
1748
+ roles.set(role, { type: "expression", raw: nestedSyntax });
1749
+ continue;
1750
+ }
1751
+ const value = parseExplicitValue(valueStr, refSet);
1752
+ roles.set(role, value);
1753
+ }
1754
+ if (schema && command !== "on") {
1755
+ for (const roleSpec of schema.roles) {
1756
+ if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
1757
+ throw new Error(
1758
+ `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`
1759
+ );
1760
+ }
1761
+ }
1762
+ }
1763
+ switch (command) {
1764
+ case "on": {
1765
+ const eventValue = roles.get("event");
1766
+ if (!eventValue) {
1767
+ throw new Error("Event handler requires event role: [on event:click ...]");
1768
+ }
1769
+ const body = extractStructuralBody(roles, "body", options);
1770
+ roles.delete("body");
1771
+ return createEventHandlerNode("on", roles, body, {
1772
+ sourceLanguage: "explicit"
1773
+ });
1774
+ }
1775
+ case "if": {
1776
+ const thenBranch = extractStructuralBody(roles, "then", options);
1777
+ const elseBranch = extractStructuralBody(roles, "else", options);
1778
+ roles.delete("then");
1779
+ roles.delete("else");
1780
+ return createConditionalNode(
1781
+ command,
1782
+ roles,
1783
+ thenBranch,
1784
+ elseBranch.length > 0 ? elseBranch : void 0,
1785
+ {
1786
+ sourceLanguage: "explicit"
1787
+ }
1788
+ );
1789
+ }
1790
+ case "repeat": {
1791
+ const loopBody = extractStructuralBody(roles, "loop-body", options);
1792
+ const loopVariantValue = roles.get("loop-variant");
1793
+ const variableValue = roles.get("variable");
1794
+ const indexVariableValue = roles.get("index-variable");
1795
+ roles.delete("loop-body");
1796
+ roles.delete("loop-variant");
1797
+ roles.delete("variable");
1798
+ roles.delete("index-variable");
1799
+ let variant;
1800
+ if (loopVariantValue && loopVariantValue.type === "literal" && typeof loopVariantValue.value === "string") {
1801
+ variant = loopVariantValue.value;
1802
+ } else if (roles.has("quantity")) {
1803
+ variant = "times";
1804
+ } else if (roles.has("source")) {
1805
+ variant = "for";
1806
+ } else if (roles.has("condition")) {
1807
+ variant = "while";
1808
+ } else {
1809
+ variant = "forever";
1810
+ }
1811
+ const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
1812
+ const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
1813
+ return createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {
1814
+ sourceLanguage: "explicit"
1815
+ });
1816
+ }
1817
+ default:
1818
+ return createCommandNode(command, roles, {
1819
+ sourceLanguage: "explicit"
1820
+ });
1821
+ }
1822
+ }
1823
+ function isExplicitSyntax(input) {
1824
+ const trimmed = input.trim();
1825
+ return trimmed.startsWith("[") && trimmed.endsWith("]");
1826
+ }
1827
+ function extractStructuralBody(roles, roleName, options) {
1828
+ const value = roles.get(roleName);
1829
+ if (!value) return [];
1830
+ if (value.type === "expression") {
1831
+ return [parseExplicit(value.raw, options)];
1832
+ }
1833
+ return [];
1834
+ }
1835
+ function inferSelectorKind(value) {
1836
+ if (!value) return void 0;
1837
+ if (value.startsWith("#")) return "id";
1838
+ if (value.startsWith(".")) return "class";
1839
+ if (value.startsWith("[")) return "attribute";
1840
+ if (value.startsWith("<") || value.startsWith("*")) return "element";
1841
+ if (/[>+~ ]/.test(value) && value.length > 1) return "complex";
1842
+ return void 0;
1843
+ }
1844
+ function tokenizeExplicit(content) {
1845
+ const tokens = [];
1846
+ let current = "";
1847
+ let inString = false;
1848
+ let stringChar = "";
1849
+ let bracketDepth = 0;
1850
+ for (let i = 0; i < content.length; i++) {
1851
+ const char = content[i];
1852
+ if (inString) {
1853
+ current += char;
1854
+ if (char === stringChar && content[i - 1] !== "\\") {
1855
+ inString = false;
1856
+ }
1857
+ continue;
1858
+ }
1859
+ if (char === '"' || char === "'") {
1860
+ inString = true;
1861
+ stringChar = char;
1862
+ current += char;
1863
+ continue;
1864
+ }
1865
+ if (char === "[") {
1866
+ bracketDepth++;
1867
+ current += char;
1868
+ continue;
1869
+ }
1870
+ if (char === "]") {
1871
+ bracketDepth--;
1872
+ current += char;
1873
+ continue;
1874
+ }
1875
+ if (char === " " && bracketDepth === 0) {
1876
+ if (current) {
1877
+ tokens.push(current);
1878
+ current = "";
1879
+ }
1880
+ continue;
1881
+ }
1882
+ current += char;
1883
+ }
1884
+ if (current) {
1885
+ tokens.push(current);
1886
+ }
1887
+ return tokens;
1888
+ }
1889
+ function parseExplicitValue(valueStr, referenceSet) {
1890
+ if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
1891
+ return createSelector(valueStr, inferSelectorKind(valueStr));
1892
+ }
1893
+ if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
1894
+ const inner = valueStr.slice(1, -1);
1895
+ return createLiteral(inner, "string");
1896
+ }
1897
+ if (valueStr === "true") return createLiteral(true, "boolean");
1898
+ if (valueStr === "false") return createLiteral(false, "boolean");
1899
+ const lowerRef = valueStr.toLowerCase();
1900
+ if (isValidReference2(lowerRef, referenceSet)) {
1901
+ return createReference(lowerRef);
1902
+ }
1903
+ const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
1904
+ if (numMatch) {
1905
+ const num = parseFloat(numMatch[1]);
1906
+ const suffix = numMatch[2];
1907
+ if (suffix) {
1908
+ return createLiteral(valueStr, "duration");
1909
+ }
1910
+ return createLiteral(num, "number");
1911
+ }
1912
+ return createLiteral(valueStr);
1913
+ }
1914
+ function findMatchingBracket(str, start) {
1915
+ let depth = 0;
1916
+ for (let i = start; i < str.length; i++) {
1917
+ if (str[i] === "[") depth++;
1918
+ else if (str[i] === "]") {
1919
+ depth--;
1920
+ if (depth === 0) return i;
1921
+ }
1922
+ }
1923
+ return str.length - 1;
1924
+ }
1925
+
1926
+ // src/ir/explicit-renderer.ts
1927
+ function renderExplicit(node) {
1928
+ if (node.kind === "compound") {
1929
+ const compoundNode = node;
1930
+ const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit(stmt));
1931
+ return renderedStatements.join(` ${compoundNode.chainType} `);
1932
+ }
1933
+ const parts = [node.action];
1934
+ for (const [role, value] of node.roles) {
1935
+ if (value.type === "flag") {
1936
+ parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);
1937
+ } else {
1938
+ parts.push(`${role}:${valueToString(value)}`);
1939
+ }
1940
+ }
1941
+ if (node.kind === "event-handler") {
1942
+ const eventNode = node;
1943
+ if (eventNode.body && eventNode.body.length > 0) {
1944
+ const bodyParts = eventNode.body.map((n) => renderExplicit(n));
1945
+ parts.push(`body:${bodyParts.join(" ")}`);
1946
+ }
1947
+ }
1948
+ if (node.kind === "conditional") {
1949
+ const condNode = node;
1950
+ if (condNode.thenBranch && condNode.thenBranch.length > 0) {
1951
+ const thenParts = condNode.thenBranch.map((n) => renderExplicit(n));
1952
+ parts.push(`then:${thenParts.join(" ")}`);
1953
+ }
1954
+ if (condNode.elseBranch && condNode.elseBranch.length > 0) {
1955
+ const elseParts = condNode.elseBranch.map((n) => renderExplicit(n));
1956
+ parts.push(`else:${elseParts.join(" ")}`);
1957
+ }
1958
+ }
1959
+ if (node.kind === "loop") {
1960
+ const loopNode = node;
1961
+ if (loopNode.loopVariant) {
1962
+ parts.push(`loop-variant:${loopNode.loopVariant}`);
1963
+ }
1964
+ if (loopNode.loopVariable) {
1965
+ parts.push(`variable:${loopNode.loopVariable}`);
1966
+ }
1967
+ if (loopNode.indexVariable) {
1968
+ parts.push(`index-variable:${loopNode.indexVariable}`);
1969
+ }
1970
+ if (loopNode.body && loopNode.body.length > 0) {
1971
+ const bodyParts = loopNode.body.map((n) => renderExplicit(n));
1972
+ parts.push(`loop-body:${bodyParts.join(" ")}`);
1973
+ }
1974
+ }
1975
+ if (node.kind === "command") {
1976
+ const cmd = node;
1977
+ if (cmd.body && cmd.body.length > 0) {
1978
+ const bodyParts = cmd.body.map((n) => renderExplicit(n));
1979
+ parts.push(`body:${bodyParts.join(" ")}`);
1980
+ }
1981
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
1982
+ const catchParts = cmd.catchBranch.map((n) => renderExplicit(n));
1983
+ parts.push(`catch:${catchParts.join(" ")}`);
1984
+ }
1985
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
1986
+ const finallyParts = cmd.finallyBranch.map((n) => renderExplicit(n));
1987
+ parts.push(`finally:${finallyParts.join(" ")}`);
1988
+ }
1989
+ if (cmd.asyncVariant) {
1990
+ parts.push(`async-variant:${cmd.asyncVariant}`);
1991
+ }
1992
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
1993
+ const asyncParts = cmd.asyncBody.map((n) => renderExplicit(n));
1994
+ parts.push(`async-body:${asyncParts.join(" ")}`);
1995
+ }
1996
+ }
1997
+ return `[${parts.join(" ")}]`;
1998
+ }
1999
+ function valueToString(value) {
2000
+ switch (value.type) {
2001
+ case "literal":
2002
+ if (typeof value.value === "string") {
2003
+ if (value.dataType === "string" || /\s/.test(value.value)) {
2004
+ return `"${value.value}"`;
2005
+ }
2006
+ return value.value;
2007
+ }
2008
+ return String(value.value);
2009
+ case "selector":
2010
+ return value.value;
2011
+ case "reference":
2012
+ return value.value;
2013
+ case "property-path":
2014
+ return `${valueToString(value.object)}'s ${value.property}`;
2015
+ case "expression":
2016
+ return value.raw;
2017
+ case "flag":
2018
+ return value.name;
2019
+ }
2020
+ }
2021
+
1537
2022
  // src/api/create-dsl.ts
1538
2023
  var DSLRegistry = class {
1539
2024
  constructor(config) {
@@ -1571,12 +2056,23 @@ var MultilingualDSLImpl = class {
1571
2056
  if (config.codeGenerator) {
1572
2057
  this.codeGenerator = config.codeGenerator;
1573
2058
  }
2059
+ const schemaMap = new Map(config.schemas.map((s) => [s.action, s]));
2060
+ this.schemaLookup = {
2061
+ getSchema: (action) => schemaMap.get(action)
2062
+ };
1574
2063
  }
1575
2064
  parse(input, language) {
1576
2065
  const result = this.parseWithConfidence(input, language);
1577
2066
  return result.node;
1578
2067
  }
1579
2068
  parseWithConfidence(input, language) {
2069
+ if (isExplicitSyntax(input)) {
2070
+ const node = parseExplicit(input, { schemaLookup: this.schemaLookup });
2071
+ if (!this.schemaLookup.getSchema(node.action)) {
2072
+ throw new Error(`No schema for action "${node.action}" in this DSL`);
2073
+ }
2074
+ return { node, confidence: 1 };
2075
+ }
1580
2076
  const tokenizer = this.registry.getTokenizer(language);
1581
2077
  if (!tokenizer) {
1582
2078
  throw new Error(`No tokenizer registered for language: ${language}`);
@@ -1620,6 +2116,7 @@ var MultilingualDSLImpl = class {
1620
2116
  }
1621
2117
  }
1622
2118
  translate(input, fromLanguage, toLanguage) {
2119
+ if (isExplicitSyntax(input)) return input;
1623
2120
  return this.transformer.transform(input, fromLanguage, toLanguage);
1624
2121
  }
1625
2122
  compile(input, language) {
@@ -1683,6 +2180,723 @@ function createMultilingualDSL(config) {
1683
2180
  return new MultilingualDSLImpl(config, registry, transformer);
1684
2181
  }
1685
2182
 
2183
+ // src/prompts/prompt-generator.ts
2184
+ function generatePrompt(config) {
2185
+ const outputFormat = config.outputFormat ?? "both";
2186
+ const examplesPerCommand = config.examplesPerCommand ?? 2;
2187
+ const sections = [
2188
+ buildProtocolSection(),
2189
+ buildValueTypeSection(),
2190
+ buildCommandsSection(config.schemas, outputFormat, examplesPerCommand),
2191
+ buildOutputFormatSection(outputFormat),
2192
+ buildErrorRecoverySection()
2193
+ ];
2194
+ let finalSections = sections;
2195
+ if (config.maxTokens && config.maxTokens > 0) {
2196
+ finalSections = truncateToTokenBudget(sections, config.maxTokens);
2197
+ }
2198
+ const text = finalSections.map((s) => `## ${s.title}
2199
+
2200
+ ${s.content}`).join("\n\n---\n\n");
2201
+ const totalRoles = config.schemas.reduce((sum, s) => sum + s.roles.length, 0);
2202
+ const approximateTokens = estimateTokens(text);
2203
+ const metadata = {
2204
+ domain: config.domain,
2205
+ commandCount: config.schemas.length,
2206
+ roleCount: totalRoles,
2207
+ approximateTokens
2208
+ };
2209
+ return { text, sections: finalSections, metadata };
2210
+ }
2211
+ function generateExamples(schema, count = 2) {
2212
+ const combos = generateRoleCombinations(schema);
2213
+ const examples = [];
2214
+ for (let i = 0; i < Math.min(count, combos.length); i++) {
2215
+ const combo = combos[i];
2216
+ const roles = /* @__PURE__ */ new Map();
2217
+ for (const role of combo) {
2218
+ const sample = sampleValue(role);
2219
+ roles.set(role.role, sample);
2220
+ }
2221
+ const nodeRoles = /* @__PURE__ */ new Map();
2222
+ for (const [roleName, sample] of roles) {
2223
+ nodeRoles.set(roleName, toSemanticValue(sample));
2224
+ }
2225
+ const node = createCommandNode(schema.action, nodeRoles);
2226
+ const explicit = renderExplicit(node);
2227
+ const jsonRoles = {};
2228
+ for (const [roleName, sample] of roles) {
2229
+ jsonRoles[roleName] = sample;
2230
+ }
2231
+ const json = { action: schema.action, roles: jsonRoles };
2232
+ examples.push({ explicit, json });
2233
+ }
2234
+ return examples;
2235
+ }
2236
+ function buildProtocolSection() {
2237
+ const content = `LokaScript Explicit Syntax (LSE) is a bracket-based format for imperative commands.
2238
+
2239
+ ### Syntax
2240
+
2241
+ \`\`\`
2242
+ [command role1:value1 role2:value2 +flag1 ~flag2]
2243
+ \`\`\`
2244
+
2245
+ - **Command**: The first token inside brackets (lowercased)
2246
+ - **Role pair**: \`name:value\` \u2014 a named semantic role with a typed value (no space around colon)
2247
+ - **Enabled flag**: \`+name\` \u2014 boolean attribute present
2248
+ - **Disabled flag**: \`~name\` \u2014 boolean attribute negated
2249
+ - **Nested body**: \`body:[command ...]\` \u2014 a bracket command inside a role value
2250
+
2251
+ ### Rules
2252
+
2253
+ 1. Commands are always lowercased
2254
+ 2. Role names preserve their original case
2255
+ 3. No spaces around the colon in role:value pairs
2256
+ 4. Strings with spaces must be quoted: \`patient:"hello world"\`
2257
+ 5. Selectors start with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`
2258
+ 6. Output must be valid bracket syntax: \`[action role:value ...]\``;
2259
+ return {
2260
+ id: "protocol",
2261
+ title: "LSE Protocol",
2262
+ content,
2263
+ approximateTokens: estimateTokens(content)
2264
+ };
2265
+ }
2266
+ function buildValueTypeSection() {
2267
+ const content = `Values in LSE are classified by their syntactic form (first match wins):
2268
+
2269
+ | Type | Syntax | Example |
2270
+ |------|--------|---------|
2271
+ | Selector | Starts with \`#\` \`.\` \`[\` \`@\` \`*\` | \`#button\`, \`.active\`, \`[data-id]\` |
2272
+ | String | Quoted with \`"\` or \`'\` | \`"hello world"\`, \`'json'\` |
2273
+ | Boolean | Exact: \`true\` / \`false\` | \`visible:true\` |
2274
+ | Reference | Built-in name | \`me\`, \`you\`, \`it\`, \`result\`, \`event\`, \`target\`, \`body\` |
2275
+ | Duration | Number + suffix | \`500ms\`, \`2s\`, \`1m\`, \`1h\` |
2276
+ | Number | Digits with optional decimal | \`5\`, \`3.14\`, \`-1\` |
2277
+ | Plain | Fallback (any non-whitespace) | \`/api/users\`, \`json\`, \`production\` |
2278
+
2279
+ **Important**: Classification is by prefix, not by intent. \`#true\` is a selector (not boolean). \`event\` as a value is a reference (not plain text).`;
2280
+ return {
2281
+ id: "value-types",
2282
+ title: "Value Types",
2283
+ content,
2284
+ approximateTokens: estimateTokens(content)
2285
+ };
2286
+ }
2287
+ function buildCommandsSection(schemas, outputFormat, examplesPerCommand) {
2288
+ const parts = [];
2289
+ for (const schema of schemas) {
2290
+ parts.push(formatCommandDoc(schema, outputFormat, examplesPerCommand));
2291
+ }
2292
+ const content = parts.join("\n\n");
2293
+ return {
2294
+ id: "commands",
2295
+ title: "Available Commands",
2296
+ content,
2297
+ approximateTokens: estimateTokens(content)
2298
+ };
2299
+ }
2300
+ function buildOutputFormatSection(outputFormat) {
2301
+ let content;
2302
+ if (outputFormat === "explicit") {
2303
+ content = `Output valid LSE bracket syntax. Each command must be wrapped in brackets:
2304
+
2305
+ \`\`\`
2306
+ [command role:value ...]
2307
+ \`\`\`
2308
+
2309
+ Do NOT output JSON. Only use bracket syntax.`;
2310
+ } else if (outputFormat === "json") {
2311
+ content = `Output valid JSON in the LLM-simplified format:
2312
+
2313
+ \`\`\`json
2314
+ {
2315
+ "action": "command-name",
2316
+ "roles": {
2317
+ "roleName": { "type": "valueType", "value": "theValue" }
2318
+ }
2319
+ }
2320
+ \`\`\`
2321
+
2322
+ Valid value types: \`selector\`, \`literal\`, \`reference\`, \`expression\`.
2323
+ Do NOT output bracket syntax. Only use JSON.`;
2324
+ } else {
2325
+ content = `You may output EITHER format:
2326
+
2327
+ **Bracket syntax** (preferred for single commands):
2328
+ \`\`\`
2329
+ [command role:value ...]
2330
+ \`\`\`
2331
+
2332
+ **JSON format** (preferred when structured data is needed):
2333
+ \`\`\`json
2334
+ {
2335
+ "action": "command-name",
2336
+ "roles": {
2337
+ "roleName": { "type": "valueType", "value": "theValue" }
2338
+ }
2339
+ }
2340
+ \`\`\`
2341
+
2342
+ Both formats are equally valid. Use whichever is more natural for the context.`;
2343
+ }
2344
+ return {
2345
+ id: "output-format",
2346
+ title: "Output Format",
2347
+ content,
2348
+ approximateTokens: estimateTokens(content)
2349
+ };
2350
+ }
2351
+ function buildErrorRecoverySection() {
2352
+ const content = `If you're unsure about a command or role:
2353
+
2354
+ 1. **Unknown command**: Use the closest matching command from the Available Commands section. Do not invent commands.
2355
+ 2. **Unknown role**: Use only the roles listed for each command. Do not add roles not in the schema.
2356
+ 3. **Ambiguous value type**: When in doubt, use \`expression\` type (it's the most permissive).
2357
+ 4. **Missing required role**: Always include all required roles. Check the Required/Optional labels.
2358
+ 5. **Selector vs plain value**: If a value starts with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`, it's a selector. URLs like \`/api/data\` are plain values.
2359
+
2360
+ When generating LSE, prefer explicit role labeling over positional guessing. Every value should have a role name.`;
2361
+ return {
2362
+ id: "error-recovery",
2363
+ title: "Error Recovery",
2364
+ content,
2365
+ approximateTokens: estimateTokens(content)
2366
+ };
2367
+ }
2368
+ function formatCommandDoc(schema, outputFormat, examplesPerCommand) {
2369
+ const lines = [];
2370
+ lines.push(`### \`${schema.action}\` \u2014 ${schema.description}`);
2371
+ lines.push("");
2372
+ const required = schema.roles.filter((r) => r.required);
2373
+ const optional = schema.roles.filter((r) => !r.required);
2374
+ if (required.length > 0) {
2375
+ lines.push("**Required roles:**");
2376
+ for (const role of required) {
2377
+ lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
2378
+ }
2379
+ }
2380
+ if (optional.length > 0) {
2381
+ lines.push("**Optional roles:**");
2382
+ for (const role of optional) {
2383
+ lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
2384
+ }
2385
+ }
2386
+ const examples = generateExamples(schema, examplesPerCommand);
2387
+ if (examples.length > 0) {
2388
+ lines.push("");
2389
+ if (outputFormat === "explicit" || outputFormat === "both") {
2390
+ lines.push("**Bracket syntax:**");
2391
+ for (const ex of examples) {
2392
+ lines.push(`\`\`\`
2393
+ ${ex.explicit}
2394
+ \`\`\``);
2395
+ }
2396
+ }
2397
+ if (outputFormat === "json" || outputFormat === "both") {
2398
+ lines.push("**JSON format:**");
2399
+ for (const ex of examples) {
2400
+ lines.push(`\`\`\`json
2401
+ ${JSON.stringify(ex.json, null, 2)}
2402
+ \`\`\``);
2403
+ }
2404
+ }
2405
+ }
2406
+ return lines.join("\n");
2407
+ }
2408
+ function formatExpectedTypes(role) {
2409
+ return role.expectedTypes.join(" | ");
2410
+ }
2411
+ function generateRoleCombinations(schema) {
2412
+ const required = schema.roles.filter((r) => r.required);
2413
+ const optional = schema.roles.filter((r) => !r.required);
2414
+ const combos = [];
2415
+ if (required.length > 0) {
2416
+ combos.push(required);
2417
+ }
2418
+ for (const opt of optional) {
2419
+ combos.push([...required, opt]);
2420
+ }
2421
+ if (optional.length >= 2) {
2422
+ combos.push([...required, ...optional]);
2423
+ }
2424
+ if (combos.length === 0) {
2425
+ combos.push(schema.roles.slice());
2426
+ }
2427
+ return combos;
2428
+ }
2429
+ function sampleValue(role) {
2430
+ const primaryType = role.expectedTypes[0] || "expression";
2431
+ switch (primaryType) {
2432
+ case "selector":
2433
+ return sampleSelector(role.role);
2434
+ case "literal":
2435
+ return sampleLiteral(role.role);
2436
+ case "reference":
2437
+ return { type: "reference", value: "me" };
2438
+ case "expression":
2439
+ return sampleExpression(role.role);
2440
+ case "flag":
2441
+ return { type: "flag", value: true };
2442
+ default:
2443
+ return { type: "literal", value: "example" };
2444
+ }
2445
+ }
2446
+ function sampleSelector(roleName) {
2447
+ const selectors = {
2448
+ patient: ".active",
2449
+ destination: "#output",
2450
+ source: "#input",
2451
+ target: "#target"
2452
+ };
2453
+ return { type: "selector", value: selectors[roleName] || `#${roleName}` };
2454
+ }
2455
+ function sampleLiteral(roleName) {
2456
+ const literals = {
2457
+ duration: "30s",
2458
+ interval: "5s",
2459
+ delay: "500ms",
2460
+ timeout: "10s",
2461
+ quantity: 5,
2462
+ limit: 10,
2463
+ style: "json",
2464
+ manner: "rolling",
2465
+ format: "html"
2466
+ };
2467
+ return { type: "literal", value: literals[roleName] || roleName };
2468
+ }
2469
+ function sampleExpression(roleName) {
2470
+ const expressions = {
2471
+ source: "/api/data",
2472
+ destination: "#output",
2473
+ patient: ".active",
2474
+ instrument: "toUpperCase",
2475
+ condition: "age > 18",
2476
+ style: "json",
2477
+ duration: "5s",
2478
+ url: "/api/users"
2479
+ };
2480
+ return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
2481
+ }
2482
+ function toSemanticValue(sample) {
2483
+ switch (sample.type) {
2484
+ case "selector":
2485
+ return {
2486
+ type: "selector",
2487
+ value: String(sample.value),
2488
+ selectorKind: detectSelectorKind(String(sample.value))
2489
+ };
2490
+ case "literal":
2491
+ return {
2492
+ type: "literal",
2493
+ value: sample.value,
2494
+ dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
2495
+ };
2496
+ case "reference":
2497
+ return { type: "reference", value: String(sample.value) };
2498
+ case "expression":
2499
+ return { type: "expression", value: String(sample.value), raw: String(sample.value) };
2500
+ case "flag":
2501
+ return { type: "flag", value: sample.value };
2502
+ default:
2503
+ return { type: "literal", value: String(sample.value), dataType: "string" };
2504
+ }
2505
+ }
2506
+ function detectSelectorKind(selector) {
2507
+ if (selector.startsWith("#")) return "id";
2508
+ if (selector.startsWith(".")) return "class";
2509
+ if (selector.startsWith("[")) return "attribute";
2510
+ return "complex";
2511
+ }
2512
+ function estimateTokens(text) {
2513
+ return Math.ceil(text.length / 4);
2514
+ }
2515
+ function truncateToTokenBudget(sections, maxTokens) {
2516
+ const result = [];
2517
+ let budget = maxTokens;
2518
+ for (const section of sections) {
2519
+ if (section.approximateTokens <= budget) {
2520
+ result.push(section);
2521
+ budget -= section.approximateTokens;
2522
+ } else {
2523
+ const charBudget = Math.max(budget * 4, 40);
2524
+ const truncatedContent = section.content.slice(0, charBudget) + "\n\n*(truncated)*";
2525
+ result.push({
2526
+ ...section,
2527
+ content: truncatedContent,
2528
+ approximateTokens: estimateTokens(truncatedContent)
2529
+ });
2530
+ break;
2531
+ }
2532
+ }
2533
+ return result;
2534
+ }
2535
+
2536
+ // src/training/schema-synthesizer.ts
2537
+ function synthesizeFromSchemas(schemas, config) {
2538
+ const maxPerCommand = config.maxPairsPerCommand ?? 5;
2539
+ const minQuality = config.minQuality ?? 0.5;
2540
+ const shuffleRoles = config.shuffleRoles ?? true;
2541
+ const languages = config.languages ?? ["en"];
2542
+ const pairs = [];
2543
+ let idCounter = 0;
2544
+ for (const schema of schemas) {
2545
+ const combos = generateRoleCombinations2(schema);
2546
+ for (const language of languages) {
2547
+ let commandPairCount = 0;
2548
+ for (const combo of combos) {
2549
+ if (commandPairCount >= maxPerCommand) break;
2550
+ const pair = buildTrainingPair(
2551
+ schema,
2552
+ combo,
2553
+ language,
2554
+ config.domain,
2555
+ `${config.domain}-synth-${idCounter++}`
2556
+ );
2557
+ if (pair.quality >= minQuality) {
2558
+ pairs.push(pair);
2559
+ commandPairCount++;
2560
+ }
2561
+ if (shuffleRoles && combo.length >= 2 && commandPairCount < maxPerCommand) {
2562
+ const shuffled = [...combo].reverse();
2563
+ const shuffledPair = buildTrainingPair(
2564
+ schema,
2565
+ shuffled,
2566
+ language,
2567
+ config.domain,
2568
+ `${config.domain}-synth-${idCounter++}`
2569
+ );
2570
+ if (shuffledPair.quality >= minQuality) {
2571
+ pairs.push(shuffledPair);
2572
+ commandPairCount++;
2573
+ }
2574
+ }
2575
+ }
2576
+ }
2577
+ }
2578
+ const metadata = {
2579
+ domain: config.domain,
2580
+ commandCount: schemas.length,
2581
+ pairCount: pairs.length,
2582
+ bySource: { synthetic: pairs.length },
2583
+ languages
2584
+ };
2585
+ return { pairs, metadata };
2586
+ }
2587
+ function generateRoleCombinations2(schema) {
2588
+ const required = schema.roles.filter((r) => r.required);
2589
+ const optional = schema.roles.filter((r) => !r.required);
2590
+ const combos = [];
2591
+ if (required.length > 0) {
2592
+ combos.push(required);
2593
+ }
2594
+ for (const opt of optional) {
2595
+ combos.push([...required, opt]);
2596
+ }
2597
+ if (optional.length >= 2) {
2598
+ combos.push([...required, ...optional]);
2599
+ }
2600
+ if (combos.length === 0) {
2601
+ combos.push(schema.roles.slice());
2602
+ }
2603
+ return combos;
2604
+ }
2605
+ function buildTrainingPair(schema, roles, language, domain, id) {
2606
+ const sampledRoles = /* @__PURE__ */ new Map();
2607
+ for (const role of roles) {
2608
+ sampledRoles.set(role.role, sampleValue2(role));
2609
+ }
2610
+ const nodeRoles = /* @__PURE__ */ new Map();
2611
+ for (const [name, sample] of sampledRoles) {
2612
+ nodeRoles.set(name, toSemanticValue2(sample));
2613
+ }
2614
+ const node = createCommandNode(schema.action, nodeRoles);
2615
+ const explicit = renderExplicit(node);
2616
+ const jsonRoles = {};
2617
+ for (const [name, sample] of sampledRoles) {
2618
+ jsonRoles[name] = sample;
2619
+ }
2620
+ const json = { action: schema.action, roles: jsonRoles };
2621
+ const natural = buildNaturalForm(schema, sampledRoles, language);
2622
+ const requiredCount = schema.roles.filter((r) => r.required).length;
2623
+ const filledRequired = roles.filter((r) => r.required).length;
2624
+ const quality = requiredCount > 0 ? filledRequired / requiredCount : 0.8;
2625
+ return {
2626
+ id,
2627
+ natural,
2628
+ language,
2629
+ explicit,
2630
+ json,
2631
+ domain,
2632
+ action: schema.action,
2633
+ source: "synthetic",
2634
+ quality
2635
+ };
2636
+ }
2637
+ function buildNaturalForm(schema, roles, language) {
2638
+ const parts = [schema.action];
2639
+ for (const roleSpec of schema.roles) {
2640
+ const sample = roles.get(roleSpec.role);
2641
+ if (!sample) continue;
2642
+ const marker = roleSpec.markerOverride?.[language];
2643
+ if (marker) {
2644
+ parts.push(marker);
2645
+ }
2646
+ parts.push(String(sample.value));
2647
+ }
2648
+ return parts.join(" ");
2649
+ }
2650
+ function sampleValue2(role) {
2651
+ const primaryType = role.expectedTypes[0] || "expression";
2652
+ switch (primaryType) {
2653
+ case "selector":
2654
+ return sampleSelector2(role.role);
2655
+ case "literal":
2656
+ return sampleLiteral2(role.role);
2657
+ case "reference":
2658
+ return { type: "reference", value: "me" };
2659
+ case "expression":
2660
+ return sampleExpression2(role.role);
2661
+ case "flag":
2662
+ return { type: "flag", value: true };
2663
+ default:
2664
+ return { type: "literal", value: "example" };
2665
+ }
2666
+ }
2667
+ function sampleSelector2(roleName) {
2668
+ const selectors = {
2669
+ patient: ".active",
2670
+ destination: "#output",
2671
+ source: "#input",
2672
+ target: "#target"
2673
+ };
2674
+ return { type: "selector", value: selectors[roleName] || `#${roleName}` };
2675
+ }
2676
+ function sampleLiteral2(roleName) {
2677
+ const literals = {
2678
+ duration: "30s",
2679
+ interval: "5s",
2680
+ delay: "500ms",
2681
+ timeout: "10s",
2682
+ quantity: 5,
2683
+ limit: 10,
2684
+ style: "json",
2685
+ manner: "rolling",
2686
+ format: "html"
2687
+ };
2688
+ return { type: "literal", value: literals[roleName] || roleName };
2689
+ }
2690
+ function sampleExpression2(roleName) {
2691
+ const expressions = {
2692
+ source: "/api/data",
2693
+ destination: "#output",
2694
+ patient: ".active",
2695
+ instrument: "toUpperCase",
2696
+ condition: "age > 18",
2697
+ style: "json",
2698
+ duration: "5s",
2699
+ url: "/api/users"
2700
+ };
2701
+ return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
2702
+ }
2703
+ function toSemanticValue2(sample) {
2704
+ switch (sample.type) {
2705
+ case "selector":
2706
+ return {
2707
+ type: "selector",
2708
+ value: String(sample.value),
2709
+ selectorKind: detectSelectorKind2(String(sample.value))
2710
+ };
2711
+ case "literal":
2712
+ return {
2713
+ type: "literal",
2714
+ value: sample.value,
2715
+ dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
2716
+ };
2717
+ case "reference":
2718
+ return { type: "reference", value: String(sample.value) };
2719
+ case "expression":
2720
+ return { type: "expression", value: String(sample.value), raw: String(sample.value) };
2721
+ case "flag":
2722
+ return { type: "flag", value: sample.value };
2723
+ default:
2724
+ return { type: "literal", value: String(sample.value), dataType: "string" };
2725
+ }
2726
+ }
2727
+ function detectSelectorKind2(selector) {
2728
+ if (selector.startsWith("#")) return "id";
2729
+ if (selector.startsWith(".")) return "class";
2730
+ if (selector.startsWith("[")) return "attribute";
2731
+ return "complex";
2732
+ }
2733
+
2734
+ // src/feedback/feedback-formatter.ts
2735
+ function buildFeedback(input, inputFormat, diagnostics, schemaLookup, action) {
2736
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
2737
+ const feedbackDiagnostics = diagnostics.map((d) => toFeedbackDiagnostic(d));
2738
+ const hints = generateHints(feedbackDiagnostics, schemaLookup, action);
2739
+ let schemaInfo;
2740
+ let correctedExample;
2741
+ if (action && schemaLookup) {
2742
+ const schema = schemaLookup.getSchema(action);
2743
+ if (schema) {
2744
+ schemaInfo = extractSchemaInfo(schema);
2745
+ if (hasErrors) {
2746
+ correctedExample = generateCorrectedExample(schema);
2747
+ }
2748
+ }
2749
+ }
2750
+ return {
2751
+ accepted: !hasErrors,
2752
+ input: { format: inputFormat, text: input },
2753
+ diagnostics: feedbackDiagnostics,
2754
+ hints,
2755
+ ...schemaInfo && { schema: schemaInfo },
2756
+ ...correctedExample && { correctedExample }
2757
+ };
2758
+ }
2759
+ function toFeedbackDiagnostic(d) {
2760
+ const fixType = classifyFixType(d);
2761
+ return {
2762
+ severity: d.severity,
2763
+ code: d.code || "unknown",
2764
+ message: d.message,
2765
+ ...d.suggestions?.[0] && { suggestion: d.suggestions[0] },
2766
+ ...fixType && { fixType }
2767
+ };
2768
+ }
2769
+ function classifyFixType(d) {
2770
+ const code = (d.code || "").toLowerCase();
2771
+ const msg = d.message.toLowerCase();
2772
+ if (code.includes("missing-role") || code.includes("missing_role") || msg.includes("required role") || msg.includes("missing role")) {
2773
+ return "missing_role";
2774
+ }
2775
+ if (code.includes("invalid-type") || code.includes("invalid_type") || code.includes("type-mismatch") || msg.includes("invalid type") || msg.includes("expected type")) {
2776
+ return "invalid_type";
2777
+ }
2778
+ if (code.includes("unknown-command") || code.includes("unknown_command") || msg.includes("unknown command") || msg.includes("not recognized") && msg.includes("command")) {
2779
+ return "unknown_command";
2780
+ }
2781
+ if (code.includes("unknown-role") || code.includes("unknown_role") || msg.includes("unknown role") || msg.includes("not recognized") && msg.includes("role")) {
2782
+ return "unknown_role";
2783
+ }
2784
+ if (code.includes("parse") || code.includes("syntax") || msg.includes("syntax error") || msg.includes("parse error") || msg.includes("unexpected")) {
2785
+ return "syntax_error";
2786
+ }
2787
+ return void 0;
2788
+ }
2789
+ function generateHints(diagnostics, schemaLookup, action) {
2790
+ const hints = [];
2791
+ const schema = action && schemaLookup ? schemaLookup.getSchema(action) : void 0;
2792
+ for (const d of diagnostics) {
2793
+ if (d.severity !== "error") continue;
2794
+ switch (d.fixType) {
2795
+ case "missing_role": {
2796
+ if (schema) {
2797
+ const required = schema.roles.filter((r) => r.required).map((r) => `'${r.role}' (${r.description}, type: ${r.expectedTypes.join("|")})`).join(", ");
2798
+ hints.push(`Required roles for '${action}': ${required}`);
2799
+ } else {
2800
+ hints.push("Check that all required roles are present.");
2801
+ }
2802
+ break;
2803
+ }
2804
+ case "unknown_command": {
2805
+ hints.push(
2806
+ `Command not recognized. Ensure you're using a valid command from the domain schema.`
2807
+ );
2808
+ break;
2809
+ }
2810
+ case "unknown_role": {
2811
+ if (schema) {
2812
+ const validRoles = schema.roles.map((r) => `'${r.role}'`).join(", ");
2813
+ hints.push(`Valid roles for '${action}': ${validRoles}`);
2814
+ } else {
2815
+ hints.push("Use only roles defined in the command schema.");
2816
+ }
2817
+ break;
2818
+ }
2819
+ case "invalid_type": {
2820
+ hints.push(
2821
+ "Check value types: selectors start with #.[@*, strings use quotes, numbers are plain digits."
2822
+ );
2823
+ break;
2824
+ }
2825
+ case "syntax_error": {
2826
+ hints.push("Ensure bracket syntax: [command role:value ...]. No spaces around the colon.");
2827
+ break;
2828
+ }
2829
+ default: {
2830
+ if (d.suggestion) {
2831
+ hints.push(d.suggestion);
2832
+ }
2833
+ }
2834
+ }
2835
+ }
2836
+ return [...new Set(hints)];
2837
+ }
2838
+ function extractSchemaInfo(schema) {
2839
+ const requiredRoles = [];
2840
+ const optionalRoles = [];
2841
+ const roleDescriptions = {};
2842
+ for (const role of schema.roles) {
2843
+ if (role.required) {
2844
+ requiredRoles.push(role.role);
2845
+ } else {
2846
+ optionalRoles.push(role.role);
2847
+ }
2848
+ roleDescriptions[role.role] = role.description;
2849
+ }
2850
+ return {
2851
+ action: schema.action,
2852
+ requiredRoles,
2853
+ optionalRoles,
2854
+ roleDescriptions
2855
+ };
2856
+ }
2857
+ function generateCorrectedExample(schema) {
2858
+ const roles = /* @__PURE__ */ new Map();
2859
+ const jsonRoles = {};
2860
+ for (const role of schema.roles) {
2861
+ if (!role.required) continue;
2862
+ const sample = sampleDefaultValue(role.role, role.expectedTypes[0] || "expression");
2863
+ roles.set(role.role, toSemanticValue3(sample));
2864
+ jsonRoles[role.role] = sample;
2865
+ }
2866
+ const node = createCommandNode(schema.action, roles);
2867
+ const explicit = renderExplicit(node);
2868
+ const json = { action: schema.action, roles: jsonRoles };
2869
+ return { explicit, json };
2870
+ }
2871
+ function sampleDefaultValue(roleName, expectedType) {
2872
+ switch (expectedType) {
2873
+ case "selector":
2874
+ return { type: "selector", value: `#${roleName}` };
2875
+ case "expression":
2876
+ return { type: "expression", value: `<${roleName}-value>` };
2877
+ case "literal":
2878
+ return { type: "literal", value: `<${roleName}-value>` };
2879
+ case "reference":
2880
+ return { type: "reference", value: "me" };
2881
+ default:
2882
+ return { type: "expression", value: `<${roleName}-value>` };
2883
+ }
2884
+ }
2885
+ function toSemanticValue3(sample) {
2886
+ switch (sample.type) {
2887
+ case "selector":
2888
+ return { type: "selector", value: sample.value, selectorKind: "id" };
2889
+ case "expression":
2890
+ return { type: "expression", value: sample.value, raw: sample.value };
2891
+ case "literal":
2892
+ return { type: "literal", value: sample.value, dataType: "string" };
2893
+ case "reference":
2894
+ return { type: "reference", value: sample.value };
2895
+ default:
2896
+ return { type: "literal", value: sample.value, dataType: "string" };
2897
+ }
2898
+ }
2899
+
1686
2900
  // src/api/domain-registry.ts
1687
2901
  var DomainRegistry = class {
1688
2902
  constructor() {
@@ -1700,6 +2914,17 @@ var DomainRegistry = class {
1700
2914
  }
1701
2915
  this.descriptors.set(descriptor.name, descriptor);
1702
2916
  }
2917
+ /**
2918
+ * Attach schemas to an already-registered domain.
2919
+ * Useful for lazy loading schemas after initial registration.
2920
+ */
2921
+ setSchemas(name, schemas) {
2922
+ const existing = this.descriptors.get(name);
2923
+ if (!existing) {
2924
+ throw new Error(`Domain not registered: ${name}`);
2925
+ }
2926
+ this.descriptors.set(name, { ...existing, schemas });
2927
+ }
1703
2928
  /**
1704
2929
  * Get all registered domain names.
1705
2930
  */
@@ -1769,6 +2994,71 @@ var DomainRegistry = class {
1769
2994
  return this.getDSL(descriptor);
1770
2995
  }
1771
2996
  // ---------------------------------------------------------------------------
2997
+ // LLM ↔ LSE Convenience Methods
2998
+ // ---------------------------------------------------------------------------
2999
+ /**
3000
+ * Generate an LLM system prompt for a domain from its schemas.
3001
+ * Returns null if the domain is not registered or has no schemas.
3002
+ */
3003
+ generatePrompt(domain, config) {
3004
+ const descriptor = this.descriptors.get(domain);
3005
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
3006
+ return generatePrompt({
3007
+ domain,
3008
+ description: descriptor.description,
3009
+ schemas: descriptor.schemas,
3010
+ ...config
3011
+ });
3012
+ }
3013
+ /**
3014
+ * Synthesize training data for a domain from its schemas.
3015
+ * Returns null if the domain is not registered or has no schemas.
3016
+ */
3017
+ generateTrainingData(domain, config) {
3018
+ const descriptor = this.descriptors.get(domain);
3019
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
3020
+ return synthesizeFromSchemas(descriptor.schemas, {
3021
+ domain,
3022
+ ...config
3023
+ });
3024
+ }
3025
+ /**
3026
+ * Build structured feedback for a failed LSE input in a domain.
3027
+ * Returns null if the domain is not registered or has no schemas.
3028
+ */
3029
+ buildFeedback(domain, input, inputFormat, diagnostics) {
3030
+ const descriptor = this.descriptors.get(domain);
3031
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
3032
+ const schemaLookup = this.createSchemaLookup(descriptor.schemas);
3033
+ let action;
3034
+ try {
3035
+ if (inputFormat === "explicit") {
3036
+ const node = parseExplicit(input, { schemaLookup });
3037
+ action = node.action;
3038
+ }
3039
+ } catch {
3040
+ }
3041
+ return buildFeedback(input, inputFormat, diagnostics, schemaLookup, action);
3042
+ }
3043
+ /**
3044
+ * Get schemas for a domain (if available).
3045
+ */
3046
+ getSchemas(domain) {
3047
+ const descriptor = this.descriptors.get(domain);
3048
+ if (!descriptor?.schemas) return null;
3049
+ return descriptor.schemas;
3050
+ }
3051
+ /**
3052
+ * Create a SchemaLookup from a domain's schemas.
3053
+ */
3054
+ createSchemaLookup(schemas) {
3055
+ const map = /* @__PURE__ */ new Map();
3056
+ for (const s of schemas) {
3057
+ map.set(s.action, s);
3058
+ }
3059
+ return { getSchema: (action) => map.get(action) };
3060
+ }
3061
+ // ---------------------------------------------------------------------------
1772
3062
  // Private: DSL/renderer lifecycle
1773
3063
  // ---------------------------------------------------------------------------
1774
3064
  async getDSL(descriptor) {
@@ -1868,7 +3158,7 @@ function generateToolDefinitions(desc) {
1868
3158
  case "parse":
1869
3159
  tools.push({
1870
3160
  name: `parse_${desc.name}`,
1871
- description: `Parse a ${desc.description} input into a semantic representation. Supports: ${langList}.`,
3161
+ description: `Parse a ${desc.description} input into a semantic representation (action, roles, confidence). Supports: ${langList}.`,
1872
3162
  inputSchema: {
1873
3163
  type: "object",
1874
3164
  properties: {
@@ -1889,7 +3179,7 @@ function generateToolDefinitions(desc) {
1889
3179
  case "compile":
1890
3180
  tools.push({
1891
3181
  name: `compile_${desc.name}`,
1892
- description: `Compile a ${desc.description} input to target code. Supports: ${langList}.`,
3182
+ description: `Compile a ${desc.description} input to ${desc.outputDescription ?? "target code"}. Supports: ${langList}.`,
1893
3183
  inputSchema: {
1894
3184
  type: "object",
1895
3185
  properties: {
@@ -1910,7 +3200,7 @@ function generateToolDefinitions(desc) {
1910
3200
  case "validate":
1911
3201
  tools.push({
1912
3202
  name: `validate_${desc.name}`,
1913
- description: `Validate ${desc.description} syntax. Returns whether it parses successfully and any errors. Supports: ${langList}.`,
3203
+ description: `Check ${desc.description} syntax for errors. Returns {valid, errors[]}. Supports: ${langList}.`,
1914
3204
  inputSchema: {
1915
3205
  type: "object",
1916
3206
  properties: {
@@ -1931,7 +3221,7 @@ function generateToolDefinitions(desc) {
1931
3221
  case "translate":
1932
3222
  tools.push({
1933
3223
  name: `translate_${desc.name}`,
1934
- description: `Translate ${desc.description} input between natural languages. Parses in source language and renders in target language.`,
3224
+ description: `Translate ${desc.description} input between natural languages (requires 'from' and 'to' codes). Supports: ${langList}.`,
1935
3225
  inputSchema: {
1936
3226
  type: "object",
1937
3227
  properties: {
@@ -1997,9 +3287,9 @@ var CrossDomainDispatcher = class {
1997
3287
  if (domainNames.length === 0) return null;
1998
3288
  const candidates = [];
1999
3289
  for (const name of domainNames) {
2000
- const dsl = await this.registry.getDSLForDomain(name);
2001
- if (!dsl) continue;
2002
3290
  try {
3291
+ const dsl = await this.registry.getDSLForDomain(name);
3292
+ if (!dsl) continue;
2003
3293
  const { node, confidence } = dsl.parseWithConfidence(input, language);
2004
3294
  if (confidence >= this.minConfidence) {
2005
3295
  candidates.push({ domain: name, node, confidence, dsl });
@@ -2008,10 +3298,16 @@ var CrossDomainDispatcher = class {
2008
3298
  }
2009
3299
  }
2010
3300
  if (candidates.length === 0) return null;
2011
- candidates.sort((a, b) => {
2012
- if (b.confidence !== a.confidence) return b.confidence - a.confidence;
2013
- return this.getPriorityIndex(a.domain) - this.getPriorityIndex(b.domain);
2014
- });
3301
+ if (this.priority.length > 0) {
3302
+ candidates.sort((a, b) => {
3303
+ const pa = this.getPriorityIndex(a.domain);
3304
+ const pb = this.getPriorityIndex(b.domain);
3305
+ if (pa !== pb) return pa - pb;
3306
+ return b.confidence - a.confidence;
3307
+ });
3308
+ } else {
3309
+ candidates.sort((a, b) => b.confidence - a.confidence);
3310
+ }
2015
3311
  return candidates[0];
2016
3312
  }
2017
3313
  /**
@@ -2055,6 +3351,40 @@ var CrossDomainDispatcher = class {
2055
3351
  const result = detected.dsl.compile(input, language);
2056
3352
  return { ...result, domain: detected.domain };
2057
3353
  }
3354
+ /**
3355
+ * Compile multi-line input where each line may belong to a different domain.
3356
+ * Detects the domain for each line and compiles it, returning per-line results.
3357
+ * Empty lines and comment lines are skipped.
3358
+ */
3359
+ async compileComposite(input, language = "en") {
3360
+ const lines = input.split("\n");
3361
+ const statements = [];
3362
+ const errors = [];
3363
+ for (let i = 0; i < lines.length; i++) {
3364
+ const line = lines[i];
3365
+ if (isSkippableLine(line)) continue;
3366
+ const trimmed = line.trim();
3367
+ const detected = await this.detect(trimmed, language);
3368
+ if (detected) {
3369
+ const result = detected.dsl.compile(trimmed, language);
3370
+ statements.push({
3371
+ line: i + 1,
3372
+ input: trimmed,
3373
+ domain: detected.domain,
3374
+ ok: result.ok,
3375
+ code: result.code,
3376
+ errors: result.errors
3377
+ });
3378
+ } else {
3379
+ errors.push({
3380
+ line: i + 1,
3381
+ input: trimmed,
3382
+ message: "No domain matched this input"
3383
+ });
3384
+ }
3385
+ }
3386
+ return { statements, errors };
3387
+ }
2058
3388
  /**
2059
3389
  * Validate input against all domains, returning the best match.
2060
3390
  */