@lokascript/framework 2.0.0 → 2.2.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 (138) 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 +1438 -21
  11. package/dist/api/index.js.map +1 -1
  12. package/dist/core/index.js +247 -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/tokenization/index.js +133 -0
  18. package/dist/core/tokenization/index.js.map +1 -1
  19. package/dist/core/tokenization/morphology/base-normalizer.d.ts +94 -0
  20. package/dist/core/tokenization/morphology/base-normalizer.d.ts.map +1 -0
  21. package/dist/core/tokenization/morphology/index.d.ts +3 -1
  22. package/dist/core/tokenization/morphology/index.d.ts.map +1 -1
  23. package/dist/core/types.d.ts +81 -2
  24. package/dist/core/types.d.ts.map +1 -1
  25. package/dist/core/types.js +40 -0
  26. package/dist/core/types.js.map +1 -1
  27. package/dist/feedback/confidence-gate.d.ts +28 -0
  28. package/dist/feedback/confidence-gate.d.ts.map +1 -0
  29. package/dist/feedback/feedback-formatter.d.ts +21 -0
  30. package/dist/feedback/feedback-formatter.d.ts.map +1 -0
  31. package/dist/feedback/index.d.ts +11 -0
  32. package/dist/feedback/index.d.ts.map +1 -0
  33. package/dist/feedback/pattern-tracker.d.ts +47 -0
  34. package/dist/feedback/pattern-tracker.d.ts.map +1 -0
  35. package/dist/feedback/types.d.ts +113 -0
  36. package/dist/feedback/types.d.ts.map +1 -0
  37. package/dist/generation/index.js +2 -1
  38. package/dist/generation/index.js.map +1 -1
  39. package/dist/index.cjs +3264 -132
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.ts +8 -2
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +3223 -132
  44. package/dist/index.js.map +1 -1
  45. package/dist/ir/explicit-parser.d.ts +86 -0
  46. package/dist/ir/explicit-parser.d.ts.map +1 -0
  47. package/dist/ir/explicit-renderer.d.ts +34 -0
  48. package/dist/ir/explicit-renderer.d.ts.map +1 -0
  49. package/dist/ir/from-interchange.d.ts +51 -0
  50. package/dist/ir/from-interchange.d.ts.map +1 -0
  51. package/dist/ir/index.d.ts +31 -0
  52. package/dist/ir/index.d.ts.map +1 -0
  53. package/dist/ir/index.js +1934 -0
  54. package/dist/ir/index.js.map +1 -0
  55. package/dist/ir/json-schema.d.ts +40 -0
  56. package/dist/ir/json-schema.d.ts.map +1 -0
  57. package/dist/ir/protocol-json.d.ts +73 -0
  58. package/dist/ir/protocol-json.d.ts.map +1 -0
  59. package/dist/ir/references.d.ts +19 -0
  60. package/dist/ir/references.d.ts.map +1 -0
  61. package/dist/ir/to-runtime-ast.d.ts +53 -0
  62. package/dist/ir/to-runtime-ast.d.ts.map +1 -0
  63. package/dist/ir/types.d.ts +151 -0
  64. package/dist/ir/types.d.ts.map +1 -0
  65. package/dist/parsing/index.js +74 -10
  66. package/dist/parsing/index.js.map +1 -1
  67. package/dist/prompts/index.d.ts +9 -0
  68. package/dist/prompts/index.d.ts.map +1 -0
  69. package/dist/prompts/prompt-generator.d.ts +49 -0
  70. package/dist/prompts/prompt-generator.d.ts.map +1 -0
  71. package/dist/prompts/types.d.ts +64 -0
  72. package/dist/prompts/types.d.ts.map +1 -0
  73. package/dist/schema/command-schema.d.ts +17 -0
  74. package/dist/schema/command-schema.d.ts.map +1 -1
  75. package/dist/schema/index.js.map +1 -1
  76. package/dist/testing/index.js +8673 -8260
  77. package/dist/testing/index.js.map +1 -1
  78. package/dist/training/index.d.ts +11 -0
  79. package/dist/training/index.d.ts.map +1 -0
  80. package/dist/training/jsonl-writer.d.ts +42 -0
  81. package/dist/training/jsonl-writer.d.ts.map +1 -0
  82. package/dist/training/schema-synthesizer.d.ts +20 -0
  83. package/dist/training/schema-synthesizer.d.ts.map +1 -0
  84. package/dist/training/types.d.ts +73 -0
  85. package/dist/training/types.d.ts.map +1 -0
  86. package/package.json +5 -1
  87. package/src/aot/index.ts +1 -0
  88. package/src/aot/registry-bridge.test.ts +104 -0
  89. package/src/aot/registry-bridge.ts +66 -0
  90. package/src/api/create-dsl.test.ts +306 -0
  91. package/src/api/create-dsl.ts +21 -0
  92. package/src/api/dispatcher.test.ts +66 -0
  93. package/src/api/dispatcher.ts +77 -9
  94. package/src/api/domain-registry.ts +120 -4
  95. package/src/core/pattern-matching/pattern-matcher.test.ts +131 -0
  96. package/src/core/pattern-matching/pattern-matcher.ts +102 -19
  97. package/src/core/tokenization/morphology/base-normalizer.ts +249 -0
  98. package/src/core/tokenization/morphology/index.ts +3 -1
  99. package/src/core/types.ts +147 -2
  100. package/src/feedback/confidence-gate.test.ts +111 -0
  101. package/src/feedback/confidence-gate.ts +70 -0
  102. package/src/feedback/feedback-formatter.test.ts +198 -0
  103. package/src/feedback/feedback-formatter.ts +298 -0
  104. package/src/feedback/index.ts +24 -0
  105. package/src/feedback/pattern-tracker.test.ts +198 -0
  106. package/src/feedback/pattern-tracker.ts +140 -0
  107. package/src/feedback/types.ts +149 -0
  108. package/src/generation/pattern-generator.ts +2 -2
  109. package/src/index.ts +29 -0
  110. package/src/integration/llm-lse-integration.test.ts +457 -0
  111. package/src/ir/conformance.test.ts +120 -0
  112. package/src/ir/explicit-parser.test.ts +612 -0
  113. package/src/ir/explicit-parser.ts +793 -0
  114. package/src/ir/explicit-renderer.test.ts +229 -0
  115. package/src/ir/explicit-renderer.ts +198 -0
  116. package/src/ir/from-interchange.test.ts +853 -0
  117. package/src/ir/from-interchange.ts +594 -0
  118. package/src/ir/index.ts +77 -0
  119. package/src/ir/json-schema.test.ts +281 -0
  120. package/src/ir/json-schema.ts +299 -0
  121. package/src/ir/protocol-json.test.ts +1187 -0
  122. package/src/ir/protocol-json.ts +751 -0
  123. package/src/ir/references.test.ts +38 -0
  124. package/src/ir/references.ts +33 -0
  125. package/src/ir/round-trip.test.ts +557 -0
  126. package/src/ir/to-runtime-ast.ts +203 -0
  127. package/src/ir/types.ts +191 -0
  128. package/src/prompts/index.ts +15 -0
  129. package/src/prompts/prompt-generator.test.ts +386 -0
  130. package/src/prompts/prompt-generator.ts +531 -0
  131. package/src/prompts/types.ts +89 -0
  132. package/src/schema/command-schema.ts +19 -0
  133. package/src/training/index.ts +13 -0
  134. package/src/training/jsonl-writer.test.ts +138 -0
  135. package/src/training/jsonl-writer.ts +67 -0
  136. package/src/training/schema-synthesizer.test.ts +250 -0
  137. package/src/training/schema-synthesizer.ts +306 -0
  138. 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,455 @@ 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
+ const collect = options.collectDiagnostics === true;
1700
+ const diagnostics = [];
1701
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
1702
+ throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
1703
+ }
1704
+ const content = trimmed.slice(1, -1).trim();
1705
+ if (!content) {
1706
+ throw new Error("Empty explicit statement");
1707
+ }
1708
+ const tokens = tokenizeExplicit(content);
1709
+ if (tokens.length === 0) {
1710
+ throw new Error("No command specified in explicit statement");
1711
+ }
1712
+ const command = tokens[0].toLowerCase();
1713
+ const roles = /* @__PURE__ */ new Map();
1714
+ const refSet = options.referenceSet ?? DEFAULT_REFERENCES;
1715
+ const schema = options.schemaLookup?.getSchema(command);
1716
+ const validRoleNames = schema ? new Set(schema.roles.map((r) => r.role)) : null;
1717
+ for (let i = 1; i < tokens.length; i++) {
1718
+ const token = tokens[i];
1719
+ if (token.startsWith("+") || token.startsWith("~")) {
1720
+ const enabled = token.startsWith("+");
1721
+ const flagName = token.slice(1);
1722
+ if (!flagName) {
1723
+ if (collect) {
1724
+ diagnostics.push({
1725
+ severity: "error",
1726
+ code: "EMPTY_FLAG",
1727
+ message: `Empty flag name: "${token}"`,
1728
+ source: "schema"
1729
+ });
1730
+ continue;
1731
+ }
1732
+ throw new Error(`Empty flag name: "${token}"`);
1733
+ }
1734
+ if (validRoleNames && !validRoleNames.has(flagName)) {
1735
+ const roleList = [...validRoleNames].join(", ");
1736
+ if (collect) {
1737
+ diagnostics.push({
1738
+ severity: "error",
1739
+ code: "UNKNOWN_FLAG",
1740
+ message: `Unknown flag "${flagName}" for command "${command}"`,
1741
+ source: "schema",
1742
+ suggestions: [...validRoleNames].map((r) => `+${r}`)
1743
+ });
1744
+ roles.set(flagName, createFlag(flagName, enabled));
1745
+ continue;
1746
+ }
1747
+ throw new Error(
1748
+ `Unknown flag "${flagName}" for command "${command}". Valid roles: ${roleList}`
1749
+ );
1750
+ }
1751
+ roles.set(flagName, createFlag(flagName, enabled));
1752
+ continue;
1753
+ }
1754
+ const colonIndex = token.indexOf(":");
1755
+ if (colonIndex === -1) {
1756
+ if (collect) {
1757
+ diagnostics.push({
1758
+ severity: "error",
1759
+ code: "INVALID_ROLE_FORMAT",
1760
+ message: `Invalid role format: "${token}". Expected role:value or +flag`,
1761
+ source: "schema"
1762
+ });
1763
+ continue;
1764
+ }
1765
+ throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
1766
+ }
1767
+ const roleName = token.slice(0, colonIndex);
1768
+ if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
1769
+ const roleList = [...validRoleNames].join(", ");
1770
+ if (collect) {
1771
+ diagnostics.push({
1772
+ severity: "error",
1773
+ code: "UNKNOWN_ROLE",
1774
+ message: `Unknown role "${roleName}" for command "${command}"`,
1775
+ source: "schema",
1776
+ suggestions: [...validRoleNames]
1777
+ });
1778
+ } else {
1779
+ throw new Error(
1780
+ `Unknown role "${roleName}" for command "${command}". Valid roles: ${roleList}`
1781
+ );
1782
+ }
1783
+ }
1784
+ const role = roleName;
1785
+ const valueStr = token.slice(colonIndex + 1);
1786
+ if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[") && isNestedCommand(valueStr)) {
1787
+ const nestedEnd = findMatchingBracket(token, colonIndex + 1);
1788
+ const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
1789
+ roles.set(role, { type: "expression", raw: nestedSyntax });
1790
+ continue;
1791
+ }
1792
+ const value = parseExplicitValue(valueStr, refSet);
1793
+ roles.set(role, value);
1794
+ }
1795
+ if (schema && command !== "on") {
1796
+ for (const roleSpec of schema.roles) {
1797
+ if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
1798
+ if (collect) {
1799
+ diagnostics.push({
1800
+ severity: "error",
1801
+ code: "MISSING_REQUIRED_ROLE",
1802
+ message: `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`,
1803
+ source: "schema"
1804
+ });
1805
+ } else {
1806
+ throw new Error(
1807
+ `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`
1808
+ );
1809
+ }
1810
+ }
1811
+ }
1812
+ }
1813
+ const attachDiagnostics = (node) => {
1814
+ if (diagnostics.length === 0) return node;
1815
+ return { ...node, diagnostics };
1816
+ };
1817
+ switch (command) {
1818
+ case "on": {
1819
+ const eventValue = roles.get("event");
1820
+ if (!eventValue) {
1821
+ if (collect) {
1822
+ diagnostics.push({
1823
+ severity: "error",
1824
+ code: "MISSING_EVENT_ROLE",
1825
+ message: "Event handler requires event role: [on event:click ...]",
1826
+ source: "schema"
1827
+ });
1828
+ return attachDiagnostics(
1829
+ createEventHandlerNode("on", roles, [], {
1830
+ sourceLanguage: "explicit"
1831
+ })
1832
+ );
1833
+ }
1834
+ throw new Error("Event handler requires event role: [on event:click ...]");
1835
+ }
1836
+ const body = extractStructuralBody(roles, "body", options);
1837
+ roles.delete("body");
1838
+ return attachDiagnostics(
1839
+ createEventHandlerNode("on", roles, body, {
1840
+ sourceLanguage: "explicit"
1841
+ })
1842
+ );
1843
+ }
1844
+ case "if": {
1845
+ const thenBranch = extractStructuralBody(roles, "then", options);
1846
+ const elseBranch = extractStructuralBody(roles, "else", options);
1847
+ roles.delete("then");
1848
+ roles.delete("else");
1849
+ return attachDiagnostics(
1850
+ createConditionalNode(
1851
+ command,
1852
+ roles,
1853
+ thenBranch,
1854
+ elseBranch.length > 0 ? elseBranch : void 0,
1855
+ {
1856
+ sourceLanguage: "explicit"
1857
+ }
1858
+ )
1859
+ );
1860
+ }
1861
+ case "repeat": {
1862
+ const loopBody = extractStructuralBody(roles, "loop-body", options);
1863
+ const loopVariantValue = roles.get("loop-variant");
1864
+ const variableValue = roles.get("variable");
1865
+ const indexVariableValue = roles.get("index-variable");
1866
+ roles.delete("loop-body");
1867
+ roles.delete("loop-variant");
1868
+ roles.delete("variable");
1869
+ roles.delete("index-variable");
1870
+ let variant;
1871
+ if (loopVariantValue && loopVariantValue.type === "literal" && typeof loopVariantValue.value === "string") {
1872
+ variant = loopVariantValue.value;
1873
+ } else if (roles.has("quantity")) {
1874
+ variant = "times";
1875
+ } else if (roles.has("source")) {
1876
+ variant = "for";
1877
+ } else if (roles.has("condition")) {
1878
+ variant = "while";
1879
+ } else {
1880
+ variant = "forever";
1881
+ }
1882
+ const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
1883
+ const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
1884
+ return attachDiagnostics(
1885
+ createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {
1886
+ sourceLanguage: "explicit"
1887
+ })
1888
+ );
1889
+ }
1890
+ default:
1891
+ return attachDiagnostics(
1892
+ createCommandNode(command, roles, {
1893
+ sourceLanguage: "explicit"
1894
+ })
1895
+ );
1896
+ }
1897
+ }
1898
+ function isExplicitSyntax(input) {
1899
+ const trimmed = input.trim();
1900
+ return trimmed.startsWith("[") && trimmed.endsWith("]");
1901
+ }
1902
+ function extractStructuralBody(roles, roleName, options) {
1903
+ const value = roles.get(roleName);
1904
+ if (!value) return [];
1905
+ if (value.type === "expression") {
1906
+ return [parseExplicit(value.raw, options)];
1907
+ }
1908
+ return [];
1909
+ }
1910
+ function inferSelectorKind(value) {
1911
+ if (!value) return void 0;
1912
+ if (value.startsWith("#")) return "id";
1913
+ if (value.startsWith(".")) return "class";
1914
+ if (value.startsWith("[")) return "attribute";
1915
+ if (value.startsWith("<") || value.startsWith("*")) return "element";
1916
+ if (/[>+~ ]/.test(value) && value.length > 1) return "complex";
1917
+ return void 0;
1918
+ }
1919
+ function tokenizeExplicit(content) {
1920
+ const tokens = [];
1921
+ let current = "";
1922
+ let inString = false;
1923
+ let stringChar = "";
1924
+ let bracketDepth = 0;
1925
+ for (let i = 0; i < content.length; i++) {
1926
+ const char = content[i];
1927
+ if (inString) {
1928
+ current += char;
1929
+ if (char === stringChar && content[i - 1] !== "\\") {
1930
+ inString = false;
1931
+ }
1932
+ continue;
1933
+ }
1934
+ if (char === '"' || char === "'") {
1935
+ inString = true;
1936
+ stringChar = char;
1937
+ current += char;
1938
+ continue;
1939
+ }
1940
+ if (char === "[") {
1941
+ bracketDepth++;
1942
+ current += char;
1943
+ continue;
1944
+ }
1945
+ if (char === "]") {
1946
+ bracketDepth--;
1947
+ current += char;
1948
+ continue;
1949
+ }
1950
+ if (char === " " && bracketDepth === 0) {
1951
+ if (current) {
1952
+ tokens.push(current);
1953
+ current = "";
1954
+ }
1955
+ continue;
1956
+ }
1957
+ current += char;
1958
+ }
1959
+ if (current) {
1960
+ tokens.push(current);
1961
+ }
1962
+ return tokens;
1963
+ }
1964
+ function parseExplicitValue(valueStr, referenceSet) {
1965
+ if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
1966
+ return createSelector(valueStr, inferSelectorKind(valueStr));
1967
+ }
1968
+ if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
1969
+ const inner = valueStr.slice(1, -1);
1970
+ return createLiteral(inner, "string");
1971
+ }
1972
+ if (valueStr === "true") return createLiteral(true, "boolean");
1973
+ if (valueStr === "false") return createLiteral(false, "boolean");
1974
+ const lowerRef = valueStr.toLowerCase();
1975
+ if (isValidReference2(lowerRef, referenceSet)) {
1976
+ return createReference(lowerRef);
1977
+ }
1978
+ const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
1979
+ if (numMatch) {
1980
+ const num = parseFloat(numMatch[1]);
1981
+ const suffix = numMatch[2];
1982
+ if (suffix) {
1983
+ return createLiteral(valueStr, "duration");
1984
+ }
1985
+ return createLiteral(num, "number");
1986
+ }
1987
+ return createLiteral(valueStr);
1988
+ }
1989
+ function findMatchingBracket(str, start) {
1990
+ let depth = 0;
1991
+ for (let i = start; i < str.length; i++) {
1992
+ if (str[i] === "[") depth++;
1993
+ else if (str[i] === "]") {
1994
+ depth--;
1995
+ if (depth === 0) return i;
1996
+ }
1997
+ }
1998
+ return str.length - 1;
1999
+ }
2000
+
2001
+ // src/ir/explicit-renderer.ts
2002
+ function renderExplicit(node) {
2003
+ let result;
2004
+ if (node.kind === "compound") {
2005
+ const compoundNode = node;
2006
+ const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit(stmt));
2007
+ result = renderedStatements.join(` ${compoundNode.chainType} `);
2008
+ } else {
2009
+ result = renderBracketCommand(node);
2010
+ }
2011
+ if (node.annotations && node.annotations.length > 0) {
2012
+ const annParts = node.annotations.map(
2013
+ (ann) => ann.value !== void 0 ? `@${ann.name}(${ann.value})` : `@${ann.name}`
2014
+ );
2015
+ return annParts.join(" ") + " " + result;
2016
+ }
2017
+ return result;
2018
+ }
2019
+ function renderBracketCommand(node) {
2020
+ const parts = [node.action];
2021
+ for (const [role, value] of node.roles) {
2022
+ if (value.type === "flag") {
2023
+ parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);
2024
+ } else {
2025
+ parts.push(`${role}:${valueToString(value)}`);
2026
+ }
2027
+ }
2028
+ if (node.kind === "event-handler") {
2029
+ const eventNode = node;
2030
+ if (eventNode.body && eventNode.body.length > 0) {
2031
+ const bodyParts = eventNode.body.map((n) => renderExplicit(n));
2032
+ parts.push(`body:${bodyParts.join(" ")}`);
2033
+ }
2034
+ }
2035
+ if (node.kind === "conditional") {
2036
+ const condNode = node;
2037
+ if (condNode.thenBranch && condNode.thenBranch.length > 0) {
2038
+ const thenParts = condNode.thenBranch.map((n) => renderExplicit(n));
2039
+ parts.push(`then:${thenParts.join(" ")}`);
2040
+ }
2041
+ if (condNode.elseBranch && condNode.elseBranch.length > 0) {
2042
+ const elseParts = condNode.elseBranch.map((n) => renderExplicit(n));
2043
+ parts.push(`else:${elseParts.join(" ")}`);
2044
+ }
2045
+ }
2046
+ if (node.kind === "loop") {
2047
+ const loopNode = node;
2048
+ if (loopNode.loopVariant) {
2049
+ parts.push(`loop-variant:${loopNode.loopVariant}`);
2050
+ }
2051
+ if (loopNode.loopVariable) {
2052
+ parts.push(`variable:${loopNode.loopVariable}`);
2053
+ }
2054
+ if (loopNode.indexVariable) {
2055
+ parts.push(`index-variable:${loopNode.indexVariable}`);
2056
+ }
2057
+ if (loopNode.body && loopNode.body.length > 0) {
2058
+ const bodyParts = loopNode.body.map((n) => renderExplicit(n));
2059
+ parts.push(`loop-body:${bodyParts.join(" ")}`);
2060
+ }
2061
+ }
2062
+ if (node.kind === "command") {
2063
+ const cmd = node;
2064
+ if (cmd.body && cmd.body.length > 0) {
2065
+ const bodyParts = cmd.body.map((n) => renderExplicit(n));
2066
+ parts.push(`body:${bodyParts.join(" ")}`);
2067
+ }
2068
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
2069
+ const catchParts = cmd.catchBranch.map((n) => renderExplicit(n));
2070
+ parts.push(`catch:${catchParts.join(" ")}`);
2071
+ }
2072
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
2073
+ const finallyParts = cmd.finallyBranch.map((n) => renderExplicit(n));
2074
+ parts.push(`finally:${finallyParts.join(" ")}`);
2075
+ }
2076
+ if (cmd.asyncVariant) {
2077
+ parts.push(`async-variant:${cmd.asyncVariant}`);
2078
+ }
2079
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
2080
+ const asyncParts = cmd.asyncBody.map((n) => renderExplicit(n));
2081
+ parts.push(`async-body:${asyncParts.join(" ")}`);
2082
+ }
2083
+ }
2084
+ return `[${parts.join(" ")}]`;
2085
+ }
2086
+ function valueToString(value) {
2087
+ switch (value.type) {
2088
+ case "literal":
2089
+ if (typeof value.value === "string") {
2090
+ if (value.dataType === "string" || /\s/.test(value.value)) {
2091
+ return `"${value.value}"`;
2092
+ }
2093
+ return value.value;
2094
+ }
2095
+ return String(value.value);
2096
+ case "selector":
2097
+ return value.value;
2098
+ case "reference":
2099
+ return value.value;
2100
+ case "property-path":
2101
+ return `${valueToString(value.object)}'s ${value.property}`;
2102
+ case "expression":
2103
+ return value.raw;
2104
+ case "flag":
2105
+ return value.name;
2106
+ }
2107
+ }
2108
+
1537
2109
  // src/api/create-dsl.ts
1538
2110
  var DSLRegistry = class {
1539
2111
  constructor(config) {
@@ -1571,12 +2143,23 @@ var MultilingualDSLImpl = class {
1571
2143
  if (config.codeGenerator) {
1572
2144
  this.codeGenerator = config.codeGenerator;
1573
2145
  }
2146
+ const schemaMap = new Map(config.schemas.map((s) => [s.action, s]));
2147
+ this.schemaLookup = {
2148
+ getSchema: (action) => schemaMap.get(action)
2149
+ };
1574
2150
  }
1575
2151
  parse(input, language) {
1576
2152
  const result = this.parseWithConfidence(input, language);
1577
2153
  return result.node;
1578
2154
  }
1579
2155
  parseWithConfidence(input, language) {
2156
+ if (isExplicitSyntax(input)) {
2157
+ const node = parseExplicit(input, { schemaLookup: this.schemaLookup });
2158
+ if (!this.schemaLookup.getSchema(node.action)) {
2159
+ throw new Error(`No schema for action "${node.action}" in this DSL`);
2160
+ }
2161
+ return { node, confidence: 1 };
2162
+ }
1580
2163
  const tokenizer = this.registry.getTokenizer(language);
1581
2164
  if (!tokenizer) {
1582
2165
  throw new Error(`No tokenizer registered for language: ${language}`);
@@ -1620,6 +2203,7 @@ var MultilingualDSLImpl = class {
1620
2203
  }
1621
2204
  }
1622
2205
  translate(input, fromLanguage, toLanguage) {
2206
+ if (isExplicitSyntax(input)) return input;
1623
2207
  return this.transformer.transform(input, fromLanguage, toLanguage);
1624
2208
  }
1625
2209
  compile(input, language) {
@@ -1683,6 +2267,723 @@ function createMultilingualDSL(config) {
1683
2267
  return new MultilingualDSLImpl(config, registry, transformer);
1684
2268
  }
1685
2269
 
2270
+ // src/prompts/prompt-generator.ts
2271
+ function generatePrompt(config) {
2272
+ const outputFormat = config.outputFormat ?? "both";
2273
+ const examplesPerCommand = config.examplesPerCommand ?? 2;
2274
+ const sections = [
2275
+ buildProtocolSection(),
2276
+ buildValueTypeSection(),
2277
+ buildCommandsSection(config.schemas, outputFormat, examplesPerCommand),
2278
+ buildOutputFormatSection(outputFormat),
2279
+ buildErrorRecoverySection()
2280
+ ];
2281
+ let finalSections = sections;
2282
+ if (config.maxTokens && config.maxTokens > 0) {
2283
+ finalSections = truncateToTokenBudget(sections, config.maxTokens);
2284
+ }
2285
+ const text = finalSections.map((s) => `## ${s.title}
2286
+
2287
+ ${s.content}`).join("\n\n---\n\n");
2288
+ const totalRoles = config.schemas.reduce((sum, s) => sum + s.roles.length, 0);
2289
+ const approximateTokens = estimateTokens(text);
2290
+ const metadata = {
2291
+ domain: config.domain,
2292
+ commandCount: config.schemas.length,
2293
+ roleCount: totalRoles,
2294
+ approximateTokens
2295
+ };
2296
+ return { text, sections: finalSections, metadata };
2297
+ }
2298
+ function generateExamples(schema, count = 2) {
2299
+ const combos = generateRoleCombinations(schema);
2300
+ const examples = [];
2301
+ for (let i = 0; i < Math.min(count, combos.length); i++) {
2302
+ const combo = combos[i];
2303
+ const roles = /* @__PURE__ */ new Map();
2304
+ for (const role of combo) {
2305
+ const sample = sampleValue(role);
2306
+ roles.set(role.role, sample);
2307
+ }
2308
+ const nodeRoles = /* @__PURE__ */ new Map();
2309
+ for (const [roleName, sample] of roles) {
2310
+ nodeRoles.set(roleName, toSemanticValue(sample));
2311
+ }
2312
+ const node = createCommandNode(schema.action, nodeRoles);
2313
+ const explicit = renderExplicit(node);
2314
+ const jsonRoles = {};
2315
+ for (const [roleName, sample] of roles) {
2316
+ jsonRoles[roleName] = sample;
2317
+ }
2318
+ const json = { action: schema.action, roles: jsonRoles };
2319
+ examples.push({ explicit, json });
2320
+ }
2321
+ return examples;
2322
+ }
2323
+ function buildProtocolSection() {
2324
+ const content = `LokaScript Explicit Syntax (LSE) is a bracket-based format for imperative commands.
2325
+
2326
+ ### Syntax
2327
+
2328
+ \`\`\`
2329
+ [command role1:value1 role2:value2 +flag1 ~flag2]
2330
+ \`\`\`
2331
+
2332
+ - **Command**: The first token inside brackets (lowercased)
2333
+ - **Role pair**: \`name:value\` \u2014 a named semantic role with a typed value (no space around colon)
2334
+ - **Enabled flag**: \`+name\` \u2014 boolean attribute present
2335
+ - **Disabled flag**: \`~name\` \u2014 boolean attribute negated
2336
+ - **Nested body**: \`body:[command ...]\` \u2014 a bracket command inside a role value
2337
+
2338
+ ### Rules
2339
+
2340
+ 1. Commands are always lowercased
2341
+ 2. Role names preserve their original case
2342
+ 3. No spaces around the colon in role:value pairs
2343
+ 4. Strings with spaces must be quoted: \`patient:"hello world"\`
2344
+ 5. Selectors start with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`
2345
+ 6. Output must be valid bracket syntax: \`[action role:value ...]\``;
2346
+ return {
2347
+ id: "protocol",
2348
+ title: "LSE Protocol",
2349
+ content,
2350
+ approximateTokens: estimateTokens(content)
2351
+ };
2352
+ }
2353
+ function buildValueTypeSection() {
2354
+ const content = `Values in LSE are classified by their syntactic form (first match wins):
2355
+
2356
+ | Type | Syntax | Example |
2357
+ |------|--------|---------|
2358
+ | Selector | Starts with \`#\` \`.\` \`[\` \`@\` \`*\` | \`#button\`, \`.active\`, \`[data-id]\` |
2359
+ | String | Quoted with \`"\` or \`'\` | \`"hello world"\`, \`'json'\` |
2360
+ | Boolean | Exact: \`true\` / \`false\` | \`visible:true\` |
2361
+ | Reference | Built-in name | \`me\`, \`you\`, \`it\`, \`result\`, \`event\`, \`target\`, \`body\` |
2362
+ | Duration | Number + suffix | \`500ms\`, \`2s\`, \`1m\`, \`1h\` |
2363
+ | Number | Digits with optional decimal | \`5\`, \`3.14\`, \`-1\` |
2364
+ | Plain | Fallback (any non-whitespace) | \`/api/users\`, \`json\`, \`production\` |
2365
+
2366
+ **Important**: Classification is by prefix, not by intent. \`#true\` is a selector (not boolean). \`event\` as a value is a reference (not plain text).`;
2367
+ return {
2368
+ id: "value-types",
2369
+ title: "Value Types",
2370
+ content,
2371
+ approximateTokens: estimateTokens(content)
2372
+ };
2373
+ }
2374
+ function buildCommandsSection(schemas, outputFormat, examplesPerCommand) {
2375
+ const parts = [];
2376
+ for (const schema of schemas) {
2377
+ parts.push(formatCommandDoc(schema, outputFormat, examplesPerCommand));
2378
+ }
2379
+ const content = parts.join("\n\n");
2380
+ return {
2381
+ id: "commands",
2382
+ title: "Available Commands",
2383
+ content,
2384
+ approximateTokens: estimateTokens(content)
2385
+ };
2386
+ }
2387
+ function buildOutputFormatSection(outputFormat) {
2388
+ let content;
2389
+ if (outputFormat === "explicit") {
2390
+ content = `Output valid LSE bracket syntax. Each command must be wrapped in brackets:
2391
+
2392
+ \`\`\`
2393
+ [command role:value ...]
2394
+ \`\`\`
2395
+
2396
+ Do NOT output JSON. Only use bracket syntax.`;
2397
+ } else if (outputFormat === "json") {
2398
+ content = `Output valid JSON in the LLM-simplified format:
2399
+
2400
+ \`\`\`json
2401
+ {
2402
+ "action": "command-name",
2403
+ "roles": {
2404
+ "roleName": { "type": "valueType", "value": "theValue" }
2405
+ }
2406
+ }
2407
+ \`\`\`
2408
+
2409
+ Valid value types: \`selector\`, \`literal\`, \`reference\`, \`expression\`.
2410
+ Do NOT output bracket syntax. Only use JSON.`;
2411
+ } else {
2412
+ content = `You may output EITHER format:
2413
+
2414
+ **Bracket syntax** (preferred for single commands):
2415
+ \`\`\`
2416
+ [command role:value ...]
2417
+ \`\`\`
2418
+
2419
+ **JSON format** (preferred when structured data is needed):
2420
+ \`\`\`json
2421
+ {
2422
+ "action": "command-name",
2423
+ "roles": {
2424
+ "roleName": { "type": "valueType", "value": "theValue" }
2425
+ }
2426
+ }
2427
+ \`\`\`
2428
+
2429
+ Both formats are equally valid. Use whichever is more natural for the context.`;
2430
+ }
2431
+ return {
2432
+ id: "output-format",
2433
+ title: "Output Format",
2434
+ content,
2435
+ approximateTokens: estimateTokens(content)
2436
+ };
2437
+ }
2438
+ function buildErrorRecoverySection() {
2439
+ const content = `If you're unsure about a command or role:
2440
+
2441
+ 1. **Unknown command**: Use the closest matching command from the Available Commands section. Do not invent commands.
2442
+ 2. **Unknown role**: Use only the roles listed for each command. Do not add roles not in the schema.
2443
+ 3. **Ambiguous value type**: When in doubt, use \`expression\` type (it's the most permissive).
2444
+ 4. **Missing required role**: Always include all required roles. Check the Required/Optional labels.
2445
+ 5. **Selector vs plain value**: If a value starts with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`, it's a selector. URLs like \`/api/data\` are plain values.
2446
+
2447
+ When generating LSE, prefer explicit role labeling over positional guessing. Every value should have a role name.`;
2448
+ return {
2449
+ id: "error-recovery",
2450
+ title: "Error Recovery",
2451
+ content,
2452
+ approximateTokens: estimateTokens(content)
2453
+ };
2454
+ }
2455
+ function formatCommandDoc(schema, outputFormat, examplesPerCommand) {
2456
+ const lines = [];
2457
+ lines.push(`### \`${schema.action}\` \u2014 ${schema.description}`);
2458
+ lines.push("");
2459
+ const required = schema.roles.filter((r) => r.required);
2460
+ const optional = schema.roles.filter((r) => !r.required);
2461
+ if (required.length > 0) {
2462
+ lines.push("**Required roles:**");
2463
+ for (const role of required) {
2464
+ lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
2465
+ }
2466
+ }
2467
+ if (optional.length > 0) {
2468
+ lines.push("**Optional roles:**");
2469
+ for (const role of optional) {
2470
+ lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
2471
+ }
2472
+ }
2473
+ const examples = generateExamples(schema, examplesPerCommand);
2474
+ if (examples.length > 0) {
2475
+ lines.push("");
2476
+ if (outputFormat === "explicit" || outputFormat === "both") {
2477
+ lines.push("**Bracket syntax:**");
2478
+ for (const ex of examples) {
2479
+ lines.push(`\`\`\`
2480
+ ${ex.explicit}
2481
+ \`\`\``);
2482
+ }
2483
+ }
2484
+ if (outputFormat === "json" || outputFormat === "both") {
2485
+ lines.push("**JSON format:**");
2486
+ for (const ex of examples) {
2487
+ lines.push(`\`\`\`json
2488
+ ${JSON.stringify(ex.json, null, 2)}
2489
+ \`\`\``);
2490
+ }
2491
+ }
2492
+ }
2493
+ return lines.join("\n");
2494
+ }
2495
+ function formatExpectedTypes(role) {
2496
+ return role.expectedTypes.join(" | ");
2497
+ }
2498
+ function generateRoleCombinations(schema) {
2499
+ const required = schema.roles.filter((r) => r.required);
2500
+ const optional = schema.roles.filter((r) => !r.required);
2501
+ const combos = [];
2502
+ if (required.length > 0) {
2503
+ combos.push(required);
2504
+ }
2505
+ for (const opt of optional) {
2506
+ combos.push([...required, opt]);
2507
+ }
2508
+ if (optional.length >= 2) {
2509
+ combos.push([...required, ...optional]);
2510
+ }
2511
+ if (combos.length === 0) {
2512
+ combos.push(schema.roles.slice());
2513
+ }
2514
+ return combos;
2515
+ }
2516
+ function sampleValue(role) {
2517
+ const primaryType = role.expectedTypes[0] || "expression";
2518
+ switch (primaryType) {
2519
+ case "selector":
2520
+ return sampleSelector(role.role);
2521
+ case "literal":
2522
+ return sampleLiteral(role.role);
2523
+ case "reference":
2524
+ return { type: "reference", value: "me" };
2525
+ case "expression":
2526
+ return sampleExpression(role.role);
2527
+ case "flag":
2528
+ return { type: "flag", value: true };
2529
+ default:
2530
+ return { type: "literal", value: "example" };
2531
+ }
2532
+ }
2533
+ function sampleSelector(roleName) {
2534
+ const selectors = {
2535
+ patient: ".active",
2536
+ destination: "#output",
2537
+ source: "#input",
2538
+ target: "#target"
2539
+ };
2540
+ return { type: "selector", value: selectors[roleName] || `#${roleName}` };
2541
+ }
2542
+ function sampleLiteral(roleName) {
2543
+ const literals = {
2544
+ duration: "30s",
2545
+ interval: "5s",
2546
+ delay: "500ms",
2547
+ timeout: "10s",
2548
+ quantity: 5,
2549
+ limit: 10,
2550
+ style: "json",
2551
+ manner: "rolling",
2552
+ format: "html"
2553
+ };
2554
+ return { type: "literal", value: literals[roleName] || roleName };
2555
+ }
2556
+ function sampleExpression(roleName) {
2557
+ const expressions = {
2558
+ source: "/api/data",
2559
+ destination: "#output",
2560
+ patient: ".active",
2561
+ instrument: "toUpperCase",
2562
+ condition: "age > 18",
2563
+ style: "json",
2564
+ duration: "5s",
2565
+ url: "/api/users"
2566
+ };
2567
+ return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
2568
+ }
2569
+ function toSemanticValue(sample) {
2570
+ switch (sample.type) {
2571
+ case "selector":
2572
+ return {
2573
+ type: "selector",
2574
+ value: String(sample.value),
2575
+ selectorKind: detectSelectorKind(String(sample.value))
2576
+ };
2577
+ case "literal":
2578
+ return {
2579
+ type: "literal",
2580
+ value: sample.value,
2581
+ dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
2582
+ };
2583
+ case "reference":
2584
+ return { type: "reference", value: String(sample.value) };
2585
+ case "expression":
2586
+ return { type: "expression", value: String(sample.value), raw: String(sample.value) };
2587
+ case "flag":
2588
+ return { type: "flag", value: sample.value };
2589
+ default:
2590
+ return { type: "literal", value: String(sample.value), dataType: "string" };
2591
+ }
2592
+ }
2593
+ function detectSelectorKind(selector) {
2594
+ if (selector.startsWith("#")) return "id";
2595
+ if (selector.startsWith(".")) return "class";
2596
+ if (selector.startsWith("[")) return "attribute";
2597
+ return "complex";
2598
+ }
2599
+ function estimateTokens(text) {
2600
+ return Math.ceil(text.length / 4);
2601
+ }
2602
+ function truncateToTokenBudget(sections, maxTokens) {
2603
+ const result = [];
2604
+ let budget = maxTokens;
2605
+ for (const section of sections) {
2606
+ if (section.approximateTokens <= budget) {
2607
+ result.push(section);
2608
+ budget -= section.approximateTokens;
2609
+ } else {
2610
+ const charBudget = Math.max(budget * 4, 40);
2611
+ const truncatedContent = section.content.slice(0, charBudget) + "\n\n*(truncated)*";
2612
+ result.push({
2613
+ ...section,
2614
+ content: truncatedContent,
2615
+ approximateTokens: estimateTokens(truncatedContent)
2616
+ });
2617
+ break;
2618
+ }
2619
+ }
2620
+ return result;
2621
+ }
2622
+
2623
+ // src/training/schema-synthesizer.ts
2624
+ function synthesizeFromSchemas(schemas, config) {
2625
+ const maxPerCommand = config.maxPairsPerCommand ?? 5;
2626
+ const minQuality = config.minQuality ?? 0.5;
2627
+ const shuffleRoles = config.shuffleRoles ?? true;
2628
+ const languages = config.languages ?? ["en"];
2629
+ const pairs = [];
2630
+ let idCounter = 0;
2631
+ for (const schema of schemas) {
2632
+ const combos = generateRoleCombinations2(schema);
2633
+ for (const language of languages) {
2634
+ let commandPairCount = 0;
2635
+ for (const combo of combos) {
2636
+ if (commandPairCount >= maxPerCommand) break;
2637
+ const pair = buildTrainingPair(
2638
+ schema,
2639
+ combo,
2640
+ language,
2641
+ config.domain,
2642
+ `${config.domain}-synth-${idCounter++}`
2643
+ );
2644
+ if (pair.quality >= minQuality) {
2645
+ pairs.push(pair);
2646
+ commandPairCount++;
2647
+ }
2648
+ if (shuffleRoles && combo.length >= 2 && commandPairCount < maxPerCommand) {
2649
+ const shuffled = [...combo].reverse();
2650
+ const shuffledPair = buildTrainingPair(
2651
+ schema,
2652
+ shuffled,
2653
+ language,
2654
+ config.domain,
2655
+ `${config.domain}-synth-${idCounter++}`
2656
+ );
2657
+ if (shuffledPair.quality >= minQuality) {
2658
+ pairs.push(shuffledPair);
2659
+ commandPairCount++;
2660
+ }
2661
+ }
2662
+ }
2663
+ }
2664
+ }
2665
+ const metadata = {
2666
+ domain: config.domain,
2667
+ commandCount: schemas.length,
2668
+ pairCount: pairs.length,
2669
+ bySource: { synthetic: pairs.length },
2670
+ languages
2671
+ };
2672
+ return { pairs, metadata };
2673
+ }
2674
+ function generateRoleCombinations2(schema) {
2675
+ const required = schema.roles.filter((r) => r.required);
2676
+ const optional = schema.roles.filter((r) => !r.required);
2677
+ const combos = [];
2678
+ if (required.length > 0) {
2679
+ combos.push(required);
2680
+ }
2681
+ for (const opt of optional) {
2682
+ combos.push([...required, opt]);
2683
+ }
2684
+ if (optional.length >= 2) {
2685
+ combos.push([...required, ...optional]);
2686
+ }
2687
+ if (combos.length === 0) {
2688
+ combos.push(schema.roles.slice());
2689
+ }
2690
+ return combos;
2691
+ }
2692
+ function buildTrainingPair(schema, roles, language, domain, id) {
2693
+ const sampledRoles = /* @__PURE__ */ new Map();
2694
+ for (const role of roles) {
2695
+ sampledRoles.set(role.role, sampleValue2(role));
2696
+ }
2697
+ const nodeRoles = /* @__PURE__ */ new Map();
2698
+ for (const [name, sample] of sampledRoles) {
2699
+ nodeRoles.set(name, toSemanticValue2(sample));
2700
+ }
2701
+ const node = createCommandNode(schema.action, nodeRoles);
2702
+ const explicit = renderExplicit(node);
2703
+ const jsonRoles = {};
2704
+ for (const [name, sample] of sampledRoles) {
2705
+ jsonRoles[name] = sample;
2706
+ }
2707
+ const json = { action: schema.action, roles: jsonRoles };
2708
+ const natural = buildNaturalForm(schema, sampledRoles, language);
2709
+ const requiredCount = schema.roles.filter((r) => r.required).length;
2710
+ const filledRequired = roles.filter((r) => r.required).length;
2711
+ const quality = requiredCount > 0 ? filledRequired / requiredCount : 0.8;
2712
+ return {
2713
+ id,
2714
+ natural,
2715
+ language,
2716
+ explicit,
2717
+ json,
2718
+ domain,
2719
+ action: schema.action,
2720
+ source: "synthetic",
2721
+ quality
2722
+ };
2723
+ }
2724
+ function buildNaturalForm(schema, roles, language) {
2725
+ const parts = [schema.action];
2726
+ for (const roleSpec of schema.roles) {
2727
+ const sample = roles.get(roleSpec.role);
2728
+ if (!sample) continue;
2729
+ const marker = roleSpec.markerOverride?.[language];
2730
+ if (marker) {
2731
+ parts.push(marker);
2732
+ }
2733
+ parts.push(String(sample.value));
2734
+ }
2735
+ return parts.join(" ");
2736
+ }
2737
+ function sampleValue2(role) {
2738
+ const primaryType = role.expectedTypes[0] || "expression";
2739
+ switch (primaryType) {
2740
+ case "selector":
2741
+ return sampleSelector2(role.role);
2742
+ case "literal":
2743
+ return sampleLiteral2(role.role);
2744
+ case "reference":
2745
+ return { type: "reference", value: "me" };
2746
+ case "expression":
2747
+ return sampleExpression2(role.role);
2748
+ case "flag":
2749
+ return { type: "flag", value: true };
2750
+ default:
2751
+ return { type: "literal", value: "example" };
2752
+ }
2753
+ }
2754
+ function sampleSelector2(roleName) {
2755
+ const selectors = {
2756
+ patient: ".active",
2757
+ destination: "#output",
2758
+ source: "#input",
2759
+ target: "#target"
2760
+ };
2761
+ return { type: "selector", value: selectors[roleName] || `#${roleName}` };
2762
+ }
2763
+ function sampleLiteral2(roleName) {
2764
+ const literals = {
2765
+ duration: "30s",
2766
+ interval: "5s",
2767
+ delay: "500ms",
2768
+ timeout: "10s",
2769
+ quantity: 5,
2770
+ limit: 10,
2771
+ style: "json",
2772
+ manner: "rolling",
2773
+ format: "html"
2774
+ };
2775
+ return { type: "literal", value: literals[roleName] || roleName };
2776
+ }
2777
+ function sampleExpression2(roleName) {
2778
+ const expressions = {
2779
+ source: "/api/data",
2780
+ destination: "#output",
2781
+ patient: ".active",
2782
+ instrument: "toUpperCase",
2783
+ condition: "age > 18",
2784
+ style: "json",
2785
+ duration: "5s",
2786
+ url: "/api/users"
2787
+ };
2788
+ return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
2789
+ }
2790
+ function toSemanticValue2(sample) {
2791
+ switch (sample.type) {
2792
+ case "selector":
2793
+ return {
2794
+ type: "selector",
2795
+ value: String(sample.value),
2796
+ selectorKind: detectSelectorKind2(String(sample.value))
2797
+ };
2798
+ case "literal":
2799
+ return {
2800
+ type: "literal",
2801
+ value: sample.value,
2802
+ dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
2803
+ };
2804
+ case "reference":
2805
+ return { type: "reference", value: String(sample.value) };
2806
+ case "expression":
2807
+ return { type: "expression", value: String(sample.value), raw: String(sample.value) };
2808
+ case "flag":
2809
+ return { type: "flag", value: sample.value };
2810
+ default:
2811
+ return { type: "literal", value: String(sample.value), dataType: "string" };
2812
+ }
2813
+ }
2814
+ function detectSelectorKind2(selector) {
2815
+ if (selector.startsWith("#")) return "id";
2816
+ if (selector.startsWith(".")) return "class";
2817
+ if (selector.startsWith("[")) return "attribute";
2818
+ return "complex";
2819
+ }
2820
+
2821
+ // src/feedback/feedback-formatter.ts
2822
+ function buildFeedback(input, inputFormat, diagnostics, schemaLookup, action) {
2823
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
2824
+ const feedbackDiagnostics = diagnostics.map((d) => toFeedbackDiagnostic(d));
2825
+ const hints = generateHints(feedbackDiagnostics, schemaLookup, action);
2826
+ let schemaInfo;
2827
+ let correctedExample;
2828
+ if (action && schemaLookup) {
2829
+ const schema = schemaLookup.getSchema(action);
2830
+ if (schema) {
2831
+ schemaInfo = extractSchemaInfo(schema);
2832
+ if (hasErrors) {
2833
+ correctedExample = generateCorrectedExample(schema);
2834
+ }
2835
+ }
2836
+ }
2837
+ return {
2838
+ accepted: !hasErrors,
2839
+ input: { format: inputFormat, text: input },
2840
+ diagnostics: feedbackDiagnostics,
2841
+ hints,
2842
+ ...schemaInfo && { schema: schemaInfo },
2843
+ ...correctedExample && { correctedExample }
2844
+ };
2845
+ }
2846
+ function toFeedbackDiagnostic(d) {
2847
+ const fixType = classifyFixType(d);
2848
+ return {
2849
+ severity: d.severity,
2850
+ code: d.code || "unknown",
2851
+ message: d.message,
2852
+ ...d.suggestions?.[0] && { suggestion: d.suggestions[0] },
2853
+ ...fixType && { fixType }
2854
+ };
2855
+ }
2856
+ function classifyFixType(d) {
2857
+ const code = (d.code || "").toLowerCase();
2858
+ const msg = d.message.toLowerCase();
2859
+ if (code.includes("missing-role") || code.includes("missing_role") || msg.includes("required role") || msg.includes("missing role")) {
2860
+ return "missing_role";
2861
+ }
2862
+ if (code.includes("invalid-type") || code.includes("invalid_type") || code.includes("type-mismatch") || msg.includes("invalid type") || msg.includes("expected type")) {
2863
+ return "invalid_type";
2864
+ }
2865
+ if (code.includes("unknown-command") || code.includes("unknown_command") || msg.includes("unknown command") || msg.includes("not recognized") && msg.includes("command")) {
2866
+ return "unknown_command";
2867
+ }
2868
+ if (code.includes("unknown-role") || code.includes("unknown_role") || msg.includes("unknown role") || msg.includes("not recognized") && msg.includes("role")) {
2869
+ return "unknown_role";
2870
+ }
2871
+ if (code.includes("parse") || code.includes("syntax") || msg.includes("syntax error") || msg.includes("parse error") || msg.includes("unexpected")) {
2872
+ return "syntax_error";
2873
+ }
2874
+ return void 0;
2875
+ }
2876
+ function generateHints(diagnostics, schemaLookup, action) {
2877
+ const hints = [];
2878
+ const schema = action && schemaLookup ? schemaLookup.getSchema(action) : void 0;
2879
+ for (const d of diagnostics) {
2880
+ if (d.severity !== "error") continue;
2881
+ switch (d.fixType) {
2882
+ case "missing_role": {
2883
+ if (schema) {
2884
+ const required = schema.roles.filter((r) => r.required).map((r) => `'${r.role}' (${r.description}, type: ${r.expectedTypes.join("|")})`).join(", ");
2885
+ hints.push(`Required roles for '${action}': ${required}`);
2886
+ } else {
2887
+ hints.push("Check that all required roles are present.");
2888
+ }
2889
+ break;
2890
+ }
2891
+ case "unknown_command": {
2892
+ hints.push(
2893
+ `Command not recognized. Ensure you're using a valid command from the domain schema.`
2894
+ );
2895
+ break;
2896
+ }
2897
+ case "unknown_role": {
2898
+ if (schema) {
2899
+ const validRoles = schema.roles.map((r) => `'${r.role}'`).join(", ");
2900
+ hints.push(`Valid roles for '${action}': ${validRoles}`);
2901
+ } else {
2902
+ hints.push("Use only roles defined in the command schema.");
2903
+ }
2904
+ break;
2905
+ }
2906
+ case "invalid_type": {
2907
+ hints.push(
2908
+ "Check value types: selectors start with #.[@*, strings use quotes, numbers are plain digits."
2909
+ );
2910
+ break;
2911
+ }
2912
+ case "syntax_error": {
2913
+ hints.push("Ensure bracket syntax: [command role:value ...]. No spaces around the colon.");
2914
+ break;
2915
+ }
2916
+ default: {
2917
+ if (d.suggestion) {
2918
+ hints.push(d.suggestion);
2919
+ }
2920
+ }
2921
+ }
2922
+ }
2923
+ return [...new Set(hints)];
2924
+ }
2925
+ function extractSchemaInfo(schema) {
2926
+ const requiredRoles = [];
2927
+ const optionalRoles = [];
2928
+ const roleDescriptions = {};
2929
+ for (const role of schema.roles) {
2930
+ if (role.required) {
2931
+ requiredRoles.push(role.role);
2932
+ } else {
2933
+ optionalRoles.push(role.role);
2934
+ }
2935
+ roleDescriptions[role.role] = role.description;
2936
+ }
2937
+ return {
2938
+ action: schema.action,
2939
+ requiredRoles,
2940
+ optionalRoles,
2941
+ roleDescriptions
2942
+ };
2943
+ }
2944
+ function generateCorrectedExample(schema) {
2945
+ const roles = /* @__PURE__ */ new Map();
2946
+ const jsonRoles = {};
2947
+ for (const role of schema.roles) {
2948
+ if (!role.required) continue;
2949
+ const sample = sampleDefaultValue(role.role, role.expectedTypes[0] || "expression");
2950
+ roles.set(role.role, toSemanticValue3(sample));
2951
+ jsonRoles[role.role] = sample;
2952
+ }
2953
+ const node = createCommandNode(schema.action, roles);
2954
+ const explicit = renderExplicit(node);
2955
+ const json = { action: schema.action, roles: jsonRoles };
2956
+ return { explicit, json };
2957
+ }
2958
+ function sampleDefaultValue(roleName, expectedType) {
2959
+ switch (expectedType) {
2960
+ case "selector":
2961
+ return { type: "selector", value: `#${roleName}` };
2962
+ case "expression":
2963
+ return { type: "expression", value: `<${roleName}-value>` };
2964
+ case "literal":
2965
+ return { type: "literal", value: `<${roleName}-value>` };
2966
+ case "reference":
2967
+ return { type: "reference", value: "me" };
2968
+ default:
2969
+ return { type: "expression", value: `<${roleName}-value>` };
2970
+ }
2971
+ }
2972
+ function toSemanticValue3(sample) {
2973
+ switch (sample.type) {
2974
+ case "selector":
2975
+ return { type: "selector", value: sample.value, selectorKind: "id" };
2976
+ case "expression":
2977
+ return { type: "expression", value: sample.value, raw: sample.value };
2978
+ case "literal":
2979
+ return { type: "literal", value: sample.value, dataType: "string" };
2980
+ case "reference":
2981
+ return { type: "reference", value: sample.value };
2982
+ default:
2983
+ return { type: "literal", value: sample.value, dataType: "string" };
2984
+ }
2985
+ }
2986
+
1686
2987
  // src/api/domain-registry.ts
1687
2988
  var DomainRegistry = class {
1688
2989
  constructor() {
@@ -1700,6 +3001,17 @@ var DomainRegistry = class {
1700
3001
  }
1701
3002
  this.descriptors.set(descriptor.name, descriptor);
1702
3003
  }
3004
+ /**
3005
+ * Attach schemas to an already-registered domain.
3006
+ * Useful for lazy loading schemas after initial registration.
3007
+ */
3008
+ setSchemas(name, schemas) {
3009
+ const existing = this.descriptors.get(name);
3010
+ if (!existing) {
3011
+ throw new Error(`Domain not registered: ${name}`);
3012
+ }
3013
+ this.descriptors.set(name, { ...existing, schemas });
3014
+ }
1703
3015
  /**
1704
3016
  * Get all registered domain names.
1705
3017
  */
@@ -1769,6 +3081,71 @@ var DomainRegistry = class {
1769
3081
  return this.getDSL(descriptor);
1770
3082
  }
1771
3083
  // ---------------------------------------------------------------------------
3084
+ // LLM ↔ LSE Convenience Methods
3085
+ // ---------------------------------------------------------------------------
3086
+ /**
3087
+ * Generate an LLM system prompt for a domain from its schemas.
3088
+ * Returns null if the domain is not registered or has no schemas.
3089
+ */
3090
+ generatePrompt(domain, config) {
3091
+ const descriptor = this.descriptors.get(domain);
3092
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
3093
+ return generatePrompt({
3094
+ domain,
3095
+ description: descriptor.description,
3096
+ schemas: descriptor.schemas,
3097
+ ...config
3098
+ });
3099
+ }
3100
+ /**
3101
+ * Synthesize training data for a domain from its schemas.
3102
+ * Returns null if the domain is not registered or has no schemas.
3103
+ */
3104
+ generateTrainingData(domain, config) {
3105
+ const descriptor = this.descriptors.get(domain);
3106
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
3107
+ return synthesizeFromSchemas(descriptor.schemas, {
3108
+ domain,
3109
+ ...config
3110
+ });
3111
+ }
3112
+ /**
3113
+ * Build structured feedback for a failed LSE input in a domain.
3114
+ * Returns null if the domain is not registered or has no schemas.
3115
+ */
3116
+ buildFeedback(domain, input, inputFormat, diagnostics) {
3117
+ const descriptor = this.descriptors.get(domain);
3118
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
3119
+ const schemaLookup = this.createSchemaLookup(descriptor.schemas);
3120
+ let action;
3121
+ try {
3122
+ if (inputFormat === "explicit") {
3123
+ const node = parseExplicit(input, { schemaLookup });
3124
+ action = node.action;
3125
+ }
3126
+ } catch {
3127
+ }
3128
+ return buildFeedback(input, inputFormat, diagnostics, schemaLookup, action);
3129
+ }
3130
+ /**
3131
+ * Get schemas for a domain (if available).
3132
+ */
3133
+ getSchemas(domain) {
3134
+ const descriptor = this.descriptors.get(domain);
3135
+ if (!descriptor?.schemas) return null;
3136
+ return descriptor.schemas;
3137
+ }
3138
+ /**
3139
+ * Create a SchemaLookup from a domain's schemas.
3140
+ */
3141
+ createSchemaLookup(schemas) {
3142
+ const map = /* @__PURE__ */ new Map();
3143
+ for (const s of schemas) {
3144
+ map.set(s.action, s);
3145
+ }
3146
+ return { getSchema: (action) => map.get(action) };
3147
+ }
3148
+ // ---------------------------------------------------------------------------
1772
3149
  // Private: DSL/renderer lifecycle
1773
3150
  // ---------------------------------------------------------------------------
1774
3151
  async getDSL(descriptor) {
@@ -1868,7 +3245,7 @@ function generateToolDefinitions(desc) {
1868
3245
  case "parse":
1869
3246
  tools.push({
1870
3247
  name: `parse_${desc.name}`,
1871
- description: `Parse a ${desc.description} input into a semantic representation. Supports: ${langList}.`,
3248
+ description: `Parse a ${desc.description} input into a semantic representation (action, roles, confidence). Supports: ${langList}.`,
1872
3249
  inputSchema: {
1873
3250
  type: "object",
1874
3251
  properties: {
@@ -1889,7 +3266,7 @@ function generateToolDefinitions(desc) {
1889
3266
  case "compile":
1890
3267
  tools.push({
1891
3268
  name: `compile_${desc.name}`,
1892
- description: `Compile a ${desc.description} input to target code. Supports: ${langList}.`,
3269
+ description: `Compile a ${desc.description} input to ${desc.outputDescription ?? "target code"}. Supports: ${langList}.`,
1893
3270
  inputSchema: {
1894
3271
  type: "object",
1895
3272
  properties: {
@@ -1910,7 +3287,7 @@ function generateToolDefinitions(desc) {
1910
3287
  case "validate":
1911
3288
  tools.push({
1912
3289
  name: `validate_${desc.name}`,
1913
- description: `Validate ${desc.description} syntax. Returns whether it parses successfully and any errors. Supports: ${langList}.`,
3290
+ description: `Check ${desc.description} syntax for errors. Returns {valid, errors[]}. Supports: ${langList}.`,
1914
3291
  inputSchema: {
1915
3292
  type: "object",
1916
3293
  properties: {
@@ -1931,7 +3308,7 @@ function generateToolDefinitions(desc) {
1931
3308
  case "translate":
1932
3309
  tools.push({
1933
3310
  name: `translate_${desc.name}`,
1934
- description: `Translate ${desc.description} input between natural languages. Parses in source language and renders in target language.`,
3311
+ description: `Translate ${desc.description} input between natural languages (requires 'from' and 'to' codes). Supports: ${langList}.`,
1935
3312
  inputSchema: {
1936
3313
  type: "object",
1937
3314
  properties: {
@@ -1997,9 +3374,9 @@ var CrossDomainDispatcher = class {
1997
3374
  if (domainNames.length === 0) return null;
1998
3375
  const candidates = [];
1999
3376
  for (const name of domainNames) {
2000
- const dsl = await this.registry.getDSLForDomain(name);
2001
- if (!dsl) continue;
2002
3377
  try {
3378
+ const dsl = await this.registry.getDSLForDomain(name);
3379
+ if (!dsl) continue;
2003
3380
  const { node, confidence } = dsl.parseWithConfidence(input, language);
2004
3381
  if (confidence >= this.minConfidence) {
2005
3382
  candidates.push({ domain: name, node, confidence, dsl });
@@ -2008,10 +3385,16 @@ var CrossDomainDispatcher = class {
2008
3385
  }
2009
3386
  }
2010
3387
  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
- });
3388
+ if (this.priority.length > 0) {
3389
+ candidates.sort((a, b) => {
3390
+ const pa = this.getPriorityIndex(a.domain);
3391
+ const pb = this.getPriorityIndex(b.domain);
3392
+ if (pa !== pb) return pa - pb;
3393
+ return b.confidence - a.confidence;
3394
+ });
3395
+ } else {
3396
+ candidates.sort((a, b) => b.confidence - a.confidence);
3397
+ }
2015
3398
  return candidates[0];
2016
3399
  }
2017
3400
  /**
@@ -2055,6 +3438,40 @@ var CrossDomainDispatcher = class {
2055
3438
  const result = detected.dsl.compile(input, language);
2056
3439
  return { ...result, domain: detected.domain };
2057
3440
  }
3441
+ /**
3442
+ * Compile multi-line input where each line may belong to a different domain.
3443
+ * Detects the domain for each line and compiles it, returning per-line results.
3444
+ * Empty lines and comment lines are skipped.
3445
+ */
3446
+ async compileComposite(input, language = "en") {
3447
+ const lines = input.split("\n");
3448
+ const statements = [];
3449
+ const errors = [];
3450
+ for (let i = 0; i < lines.length; i++) {
3451
+ const line = lines[i];
3452
+ if (isSkippableLine(line)) continue;
3453
+ const trimmed = line.trim();
3454
+ const detected = await this.detect(trimmed, language);
3455
+ if (detected) {
3456
+ const result = detected.dsl.compile(trimmed, language);
3457
+ statements.push({
3458
+ line: i + 1,
3459
+ input: trimmed,
3460
+ domain: detected.domain,
3461
+ ok: result.ok,
3462
+ code: result.code,
3463
+ errors: result.errors
3464
+ });
3465
+ } else {
3466
+ errors.push({
3467
+ line: i + 1,
3468
+ input: trimmed,
3469
+ message: "No domain matched this input"
3470
+ });
3471
+ }
3472
+ }
3473
+ return { statements, errors };
3474
+ }
2058
3475
  /**
2059
3476
  * Validate input against all domains, returning the best match.
2060
3477
  */