@lokascript/framework 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/aot/index.d.ts +1 -0
  2. package/dist/aot/index.d.ts.map +1 -1
  3. package/dist/aot/registry-bridge.d.ts +27 -0
  4. package/dist/aot/registry-bridge.d.ts.map +1 -0
  5. package/dist/api/create-dsl.d.ts.map +1 -1
  6. package/dist/api/dispatcher.d.ts +22 -0
  7. package/dist/api/dispatcher.d.ts.map +1 -1
  8. package/dist/api/domain-registry.d.ts +41 -0
  9. package/dist/api/domain-registry.d.ts.map +1 -1
  10. package/dist/api/index.js +1351 -21
  11. package/dist/api/index.js.map +1 -1
  12. package/dist/core/index.js +114 -10
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/core/pattern-matching/index.js +74 -10
  15. package/dist/core/pattern-matching/index.js.map +1 -1
  16. package/dist/core/pattern-matching/pattern-matcher.d.ts.map +1 -1
  17. package/dist/core/types.d.ts +80 -2
  18. package/dist/core/types.d.ts.map +1 -1
  19. package/dist/core/types.js +40 -0
  20. package/dist/core/types.js.map +1 -1
  21. package/dist/feedback/confidence-gate.d.ts +28 -0
  22. package/dist/feedback/confidence-gate.d.ts.map +1 -0
  23. package/dist/feedback/feedback-formatter.d.ts +21 -0
  24. package/dist/feedback/feedback-formatter.d.ts.map +1 -0
  25. package/dist/feedback/index.d.ts +11 -0
  26. package/dist/feedback/index.d.ts.map +1 -0
  27. package/dist/feedback/pattern-tracker.d.ts +47 -0
  28. package/dist/feedback/pattern-tracker.d.ts.map +1 -0
  29. package/dist/feedback/types.d.ts +113 -0
  30. package/dist/feedback/types.d.ts.map +1 -0
  31. package/dist/generation/index.js +2 -1
  32. package/dist/generation/index.js.map +1 -1
  33. package/dist/index.cjs +2685 -71
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.ts +6 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +2652 -71
  38. package/dist/index.js.map +1 -1
  39. package/dist/ir/explicit-parser.d.ts +31 -0
  40. package/dist/ir/explicit-parser.d.ts.map +1 -0
  41. package/dist/ir/explicit-renderer.d.ts +17 -0
  42. package/dist/ir/explicit-renderer.d.ts.map +1 -0
  43. package/dist/ir/from-interchange.d.ts +51 -0
  44. package/dist/ir/from-interchange.d.ts.map +1 -0
  45. package/dist/ir/index.d.ts +29 -0
  46. package/dist/ir/index.d.ts.map +1 -0
  47. package/dist/ir/index.js +1557 -0
  48. package/dist/ir/index.js.map +1 -0
  49. package/dist/ir/json-schema.d.ts +40 -0
  50. package/dist/ir/json-schema.d.ts.map +1 -0
  51. package/dist/ir/protocol-json.d.ts +73 -0
  52. package/dist/ir/protocol-json.d.ts.map +1 -0
  53. package/dist/ir/references.d.ts +19 -0
  54. package/dist/ir/references.d.ts.map +1 -0
  55. package/dist/ir/types.d.ts +141 -0
  56. package/dist/ir/types.d.ts.map +1 -0
  57. package/dist/parsing/index.js +74 -10
  58. package/dist/parsing/index.js.map +1 -1
  59. package/dist/prompts/index.d.ts +9 -0
  60. package/dist/prompts/index.d.ts.map +1 -0
  61. package/dist/prompts/prompt-generator.d.ts +49 -0
  62. package/dist/prompts/prompt-generator.d.ts.map +1 -0
  63. package/dist/prompts/types.d.ts +64 -0
  64. package/dist/prompts/types.d.ts.map +1 -0
  65. package/dist/schema/command-schema.d.ts +17 -0
  66. package/dist/schema/command-schema.d.ts.map +1 -1
  67. package/dist/schema/index.js.map +1 -1
  68. package/dist/testing/index.js +3 -0
  69. package/dist/testing/index.js.map +1 -1
  70. package/dist/training/index.d.ts +11 -0
  71. package/dist/training/index.d.ts.map +1 -0
  72. package/dist/training/jsonl-writer.d.ts +42 -0
  73. package/dist/training/jsonl-writer.d.ts.map +1 -0
  74. package/dist/training/schema-synthesizer.d.ts +20 -0
  75. package/dist/training/schema-synthesizer.d.ts.map +1 -0
  76. package/dist/training/types.d.ts +73 -0
  77. package/dist/training/types.d.ts.map +1 -0
  78. package/package.json +5 -1
  79. package/src/aot/index.ts +1 -0
  80. package/src/aot/registry-bridge.test.ts +104 -0
  81. package/src/aot/registry-bridge.ts +66 -0
  82. package/src/api/create-dsl.test.ts +306 -0
  83. package/src/api/create-dsl.ts +21 -0
  84. package/src/api/dispatcher.test.ts +66 -0
  85. package/src/api/dispatcher.ts +77 -9
  86. package/src/api/domain-registry.ts +120 -4
  87. package/src/core/pattern-matching/pattern-matcher.test.ts +131 -0
  88. package/src/core/pattern-matching/pattern-matcher.ts +102 -19
  89. package/src/core/types.ts +145 -2
  90. package/src/feedback/confidence-gate.test.ts +111 -0
  91. package/src/feedback/confidence-gate.ts +70 -0
  92. package/src/feedback/feedback-formatter.test.ts +198 -0
  93. package/src/feedback/feedback-formatter.ts +298 -0
  94. package/src/feedback/index.ts +24 -0
  95. package/src/feedback/pattern-tracker.test.ts +198 -0
  96. package/src/feedback/pattern-tracker.ts +140 -0
  97. package/src/feedback/types.ts +149 -0
  98. package/src/generation/pattern-generator.ts +2 -2
  99. package/src/index.ts +22 -0
  100. package/src/integration/llm-lse-integration.test.ts +457 -0
  101. package/src/ir/explicit-parser.test.ts +306 -0
  102. package/src/ir/explicit-parser.ts +419 -0
  103. package/src/ir/explicit-renderer.test.ts +144 -0
  104. package/src/ir/explicit-renderer.ts +146 -0
  105. package/src/ir/from-interchange.test.ts +853 -0
  106. package/src/ir/from-interchange.ts +594 -0
  107. package/src/ir/index.ts +66 -0
  108. package/src/ir/json-schema.test.ts +281 -0
  109. package/src/ir/json-schema.ts +299 -0
  110. package/src/ir/protocol-json.test.ts +1185 -0
  111. package/src/ir/protocol-json.ts +745 -0
  112. package/src/ir/references.test.ts +38 -0
  113. package/src/ir/references.ts +33 -0
  114. package/src/ir/round-trip.test.ts +557 -0
  115. package/src/ir/types.ts +181 -0
  116. package/src/prompts/index.ts +15 -0
  117. package/src/prompts/prompt-generator.test.ts +386 -0
  118. package/src/prompts/prompt-generator.ts +531 -0
  119. package/src/prompts/types.ts +89 -0
  120. package/src/schema/command-schema.ts +19 -0
  121. package/src/training/index.ts +13 -0
  122. package/src/training/jsonl-writer.test.ts +138 -0
  123. package/src/training/jsonl-writer.ts +67 -0
  124. package/src/training/schema-synthesizer.test.ts +250 -0
  125. package/src/training/schema-synthesizer.ts +306 -0
  126. package/src/training/types.ts +103 -0
package/dist/index.js CHANGED
@@ -14,6 +14,9 @@ function createPropertyPath(object, property) {
14
14
  function createExpression(raw) {
15
15
  return { type: "expression", raw };
16
16
  }
17
+ function createFlag(name, enabled = true) {
18
+ return { type: "flag", name, enabled };
19
+ }
17
20
  function createCommandNode(action, roles, metadata) {
18
21
  const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
19
22
  const node = {
@@ -65,6 +68,7 @@ function createCompoundNode(statements, chainType = "sequential", metadata) {
65
68
  return metadata ? { ...base, metadata } : base;
66
69
  }
67
70
  function extractValue(value) {
71
+ if (value.type === "flag") return value.name;
68
72
  if ("raw" in value && value.raw !== void 0) return String(value.raw);
69
73
  if ("value" in value && value.value !== void 0) return String(value.value);
70
74
  if (value.type === "property-path") return `${extractValue(value.object)}.${value.property}`;
@@ -91,6 +95,38 @@ function createLoopNode(action, roles, loopVariant, body, loopVariable, indexVar
91
95
  ...metadata && { metadata }
92
96
  };
93
97
  }
98
+ function createTryNode(body, catchBranch, finallyBranch, metadata) {
99
+ return {
100
+ kind: "command",
101
+ action: "try",
102
+ roles: /* @__PURE__ */ new Map(),
103
+ body,
104
+ ...catchBranch && catchBranch.length > 0 ? { catchBranch } : {},
105
+ ...finallyBranch && finallyBranch.length > 0 ? { finallyBranch } : {},
106
+ ...metadata ? { metadata } : {}
107
+ };
108
+ }
109
+ function createAsyncNode(variant, asyncBody, metadata) {
110
+ return {
111
+ kind: "command",
112
+ action: variant,
113
+ roles: /* @__PURE__ */ new Map(),
114
+ asyncVariant: variant,
115
+ asyncBody,
116
+ ...metadata ? { metadata } : {}
117
+ };
118
+ }
119
+ function createMatchNode(roles, arms, defaultArm, metadata) {
120
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
121
+ return {
122
+ kind: "command",
123
+ action: "match",
124
+ roles: rolesMap,
125
+ arms,
126
+ ...defaultArm && defaultArm.length > 0 ? { defaultArm } : {},
127
+ ...metadata ? { metadata } : {}
128
+ };
129
+ }
94
130
 
95
131
  // src/core/logger.ts
96
132
  var DebugLogger = class {
@@ -346,7 +382,7 @@ var _PatternMatcher = class _PatternMatcher {
346
382
  * a token and the immediately following pattern token fails, the matcher
347
383
  * resets to before the optional role and retries the failed token.
348
384
  */
349
- matchTokenSequence(tokens, patternTokens, captured) {
385
+ matchTokenSequence(tokens, patternTokens, captured, parentStopMarkers) {
350
386
  const firstPatternToken = patternTokens[0];
351
387
  const patternExpectsConjunction = firstPatternToken?.type === "literal" && (firstPatternToken.value === "and" || firstPatternToken.value === "then" || firstPatternToken.alternatives?.includes("and") || firstPatternToken.alternatives?.includes("then"));
352
388
  if (this.currentProfile?.code === "ar" && !patternExpectsConjunction) {
@@ -370,6 +406,9 @@ var _PatternMatcher = class _PatternMatcher {
370
406
  );
371
407
  if (patternToken.type === "role" && patternToken.greedy) {
372
408
  const stopMarkers = this.collectStopMarkers(patternTokens, i + 1);
409
+ if (parentStopMarkers) {
410
+ for (const m of parentStopMarkers) stopMarkers.add(m);
411
+ }
373
412
  const values = [];
374
413
  while (!tokens.isAtEnd()) {
375
414
  const nextToken = tokens.peek();
@@ -389,8 +428,58 @@ var _PatternMatcher = class _PatternMatcher {
389
428
  return false;
390
429
  }
391
430
  }
431
+ if (patternToken.type === "group") {
432
+ const siblingStopMarkers = this.collectStopMarkers(patternTokens, i + 1);
433
+ if (parentStopMarkers) {
434
+ for (const m of parentStopMarkers) siblingStopMarkers.add(m);
435
+ }
436
+ const groupMatched = this.matchGroupToken(
437
+ tokens,
438
+ patternToken,
439
+ captured,
440
+ siblingStopMarkers
441
+ );
442
+ if (groupMatched) {
443
+ prevOptionalMark = null;
444
+ prevOptionalRole = null;
445
+ continue;
446
+ }
447
+ if (this.isOptional(patternToken)) {
448
+ continue;
449
+ }
450
+ return false;
451
+ }
392
452
  const isOptionalRole = patternToken.type === "role" && patternToken.optional === true;
393
453
  const markBefore = isOptionalRole ? tokens.mark() : null;
454
+ const isRoleToken = patternToken.type === "role";
455
+ if (isRoleToken && !isOptionalRole) {
456
+ const nextPT = i + 1 < patternTokens.length ? patternTokens[i + 1] : null;
457
+ if (nextPT?.type === "literal") {
458
+ const currentToken = tokens.peek();
459
+ if (currentToken && this.getMatchType(currentToken, nextPT.value) !== "none") {
460
+ this.logger.debug(
461
+ " >> MARKER-STEAL GUARD: current token matches next literal, skipping role"
462
+ );
463
+ this.logger.debug(" >> Token match FAILED");
464
+ if (this.isOptional(patternToken)) {
465
+ continue;
466
+ }
467
+ if (prevOptionalMark && prevOptionalRole) {
468
+ this.logger.debug(" >> BACKTRACKING: undoing optional role", prevOptionalRole);
469
+ tokens.reset(prevOptionalMark);
470
+ captured.delete(prevOptionalRole);
471
+ prevOptionalMark = null;
472
+ prevOptionalRole = null;
473
+ const retryMatched = this.matchPatternToken(tokens, patternToken, captured);
474
+ this.logger.debug(" >> Backtrack retry result:", retryMatched);
475
+ if (retryMatched) {
476
+ continue;
477
+ }
478
+ }
479
+ return false;
480
+ }
481
+ }
482
+ }
394
483
  const matched = this.matchPatternToken(tokens, patternToken, captured);
395
484
  this.logger.debug(" >> Match result:", matched);
396
485
  if (matched) {
@@ -511,7 +600,7 @@ var _PatternMatcher = class _PatternMatcher {
511
600
  const possessiveValue = this.tryMatchPossessiveExpression(tokens);
512
601
  if (possessiveValue) {
513
602
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
514
- if (!patternToken.expectedTypes.includes(possessiveValue.type) && !patternToken.expectedTypes.includes("expression")) {
603
+ if (!isTypeCompatible(possessiveValue.type, patternToken.expectedTypes)) {
515
604
  return patternToken.optional || false;
516
605
  }
517
606
  }
@@ -521,7 +610,7 @@ var _PatternMatcher = class _PatternMatcher {
521
610
  const methodCallValue = this.tryMatchMethodCallExpression(tokens);
522
611
  if (methodCallValue) {
523
612
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
524
- if (!patternToken.expectedTypes.includes(methodCallValue.type) && !patternToken.expectedTypes.includes("expression")) {
613
+ if (!isTypeCompatible(methodCallValue.type, patternToken.expectedTypes)) {
525
614
  return patternToken.optional || false;
526
615
  }
527
616
  }
@@ -541,7 +630,7 @@ var _PatternMatcher = class _PatternMatcher {
541
630
  const propertyAccessValue = this.tryMatchPropertyAccessExpression(tokens);
542
631
  if (propertyAccessValue) {
543
632
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
544
- if (!patternToken.expectedTypes.includes(propertyAccessValue.type) && !patternToken.expectedTypes.includes("expression")) {
633
+ if (!isTypeCompatible(propertyAccessValue.type, patternToken.expectedTypes)) {
545
634
  return patternToken.optional || false;
546
635
  }
547
636
  }
@@ -861,10 +950,15 @@ var _PatternMatcher = class _PatternMatcher {
861
950
  * (e.g., a value like 'hello') to sit between groups without blocking later
862
951
  * marker matches.
863
952
  */
864
- matchGroupToken(tokens, patternToken, captured) {
953
+ matchGroupToken(tokens, patternToken, captured, parentStopMarkers) {
865
954
  const mark = tokens.mark();
866
955
  const capturedBefore = new Set(captured.keys());
867
- const success = this.matchTokenSequence(tokens, patternToken.tokens, captured);
956
+ const success = this.matchTokenSequence(
957
+ tokens,
958
+ patternToken.tokens,
959
+ captured,
960
+ parentStopMarkers
961
+ );
868
962
  if (success) return true;
869
963
  tokens.reset(mark);
870
964
  for (const role of captured.keys()) {
@@ -886,7 +980,12 @@ var _PatternMatcher = class _PatternMatcher {
886
980
  "- skipping intervening tokens"
887
981
  );
888
982
  for (let s = 0; s < offset; s++) tokens.advance();
889
- const retrySuccess = this.matchTokenSequence(tokens, patternToken.tokens, captured);
983
+ const retrySuccess = this.matchTokenSequence(
984
+ tokens,
985
+ patternToken.tokens,
986
+ captured,
987
+ parentStopMarkers
988
+ );
890
989
  if (retrySuccess) return true;
891
990
  tokens.reset(mark);
892
991
  for (const role of captured.keys()) {
@@ -953,7 +1052,7 @@ var _PatternMatcher = class _PatternMatcher {
953
1052
  break;
954
1053
  }
955
1054
  }
956
- break;
1055
+ if (!pt.optional) break;
957
1056
  }
958
1057
  }
959
1058
  return markers;
@@ -963,8 +1062,9 @@ var _PatternMatcher = class _PatternMatcher {
963
1062
  */
964
1063
  isStopMarker(token, stopMarkers) {
965
1064
  if (stopMarkers.size === 0) return false;
966
- const value = (token.normalized || token.value).toLowerCase();
967
- return stopMarkers.has(value);
1065
+ if (stopMarkers.has(token.value.toLowerCase())) return true;
1066
+ if (token.normalized && stopMarkers.has(token.normalized.toLowerCase())) return true;
1067
+ return false;
968
1068
  }
969
1069
  /**
970
1070
  * Convert a language token to a semantic value.
@@ -1369,7 +1469,7 @@ function addRoleWithMarker(tokens, roleSpec, profile) {
1369
1469
  if (marker) {
1370
1470
  const markerInfo = profile.roleMarkers?.[roleSpec.role];
1371
1471
  const defaultPosition = profile.wordOrder === "SOV" ? "after" : "before";
1372
- const position = markerInfo?.position ?? defaultPosition;
1472
+ const position = roleSpec.markerPosition ?? markerInfo?.position ?? defaultPosition;
1373
1473
  if (isOptional) {
1374
1474
  if (position === "before") {
1375
1475
  tokens.push({
@@ -1909,6 +2009,1419 @@ function createTokenizerContext(tokenizer) {
1909
2009
  return ctx;
1910
2010
  }
1911
2011
 
2012
+ // src/ir/references.ts
2013
+ var DEFAULT_REFERENCES = /* @__PURE__ */ new Set([
2014
+ "me",
2015
+ "you",
2016
+ "it",
2017
+ "result",
2018
+ "event",
2019
+ "target",
2020
+ "body"
2021
+ ]);
2022
+ function isValidReference2(value, referenceSet = DEFAULT_REFERENCES) {
2023
+ return referenceSet.has(value);
2024
+ }
2025
+
2026
+ // src/ir/explicit-parser.ts
2027
+ var STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
2028
+ "body",
2029
+ "then",
2030
+ "else",
2031
+ "condition",
2032
+ "loop-body",
2033
+ "variable",
2034
+ "catch",
2035
+ // v1.2: try/catch/finally
2036
+ "finally"
2037
+ // v1.2: try/catch/finally
2038
+ ]);
2039
+ function isNestedCommand(value) {
2040
+ const inner = value.slice(1, -1);
2041
+ let depth = 0;
2042
+ for (const ch of inner) {
2043
+ if (ch === "[") depth++;
2044
+ else if (ch === "]") depth--;
2045
+ else if (depth === 0 && (ch === " " || ch === ":")) return true;
2046
+ }
2047
+ return false;
2048
+ }
2049
+ function parseExplicit(input, options = {}) {
2050
+ const trimmed = input.trim();
2051
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
2052
+ throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
2053
+ }
2054
+ const content = trimmed.slice(1, -1).trim();
2055
+ if (!content) {
2056
+ throw new Error("Empty explicit statement");
2057
+ }
2058
+ const tokens = tokenizeExplicit(content);
2059
+ if (tokens.length === 0) {
2060
+ throw new Error("No command specified in explicit statement");
2061
+ }
2062
+ const command = tokens[0].toLowerCase();
2063
+ const roles = /* @__PURE__ */ new Map();
2064
+ const refSet = options.referenceSet ?? DEFAULT_REFERENCES;
2065
+ const schema = options.schemaLookup?.getSchema(command);
2066
+ const validRoleNames = schema ? new Set(schema.roles.map((r) => r.role)) : null;
2067
+ for (let i = 1; i < tokens.length; i++) {
2068
+ const token = tokens[i];
2069
+ if (token.startsWith("+") || token.startsWith("~")) {
2070
+ const enabled = token.startsWith("+");
2071
+ const flagName = token.slice(1);
2072
+ if (!flagName) {
2073
+ throw new Error(`Empty flag name: "${token}"`);
2074
+ }
2075
+ if (validRoleNames && !validRoleNames.has(flagName)) {
2076
+ const roleList = [...validRoleNames].join(", ");
2077
+ throw new Error(
2078
+ `Unknown flag "${flagName}" for command "${command}". Valid roles: ${roleList}`
2079
+ );
2080
+ }
2081
+ roles.set(flagName, createFlag(flagName, enabled));
2082
+ continue;
2083
+ }
2084
+ const colonIndex = token.indexOf(":");
2085
+ if (colonIndex === -1) {
2086
+ throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
2087
+ }
2088
+ const roleName = token.slice(0, colonIndex);
2089
+ if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
2090
+ const roleList = [...validRoleNames].join(", ");
2091
+ throw new Error(
2092
+ `Unknown role "${roleName}" for command "${command}". Valid roles: ${roleList}`
2093
+ );
2094
+ }
2095
+ const role = roleName;
2096
+ const valueStr = token.slice(colonIndex + 1);
2097
+ if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[") && isNestedCommand(valueStr)) {
2098
+ const nestedEnd = findMatchingBracket(token, colonIndex + 1);
2099
+ const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
2100
+ roles.set(role, { type: "expression", raw: nestedSyntax });
2101
+ continue;
2102
+ }
2103
+ const value = parseExplicitValue(valueStr, refSet);
2104
+ roles.set(role, value);
2105
+ }
2106
+ if (schema && command !== "on") {
2107
+ for (const roleSpec of schema.roles) {
2108
+ if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
2109
+ throw new Error(
2110
+ `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`
2111
+ );
2112
+ }
2113
+ }
2114
+ }
2115
+ switch (command) {
2116
+ case "on": {
2117
+ const eventValue = roles.get("event");
2118
+ if (!eventValue) {
2119
+ throw new Error("Event handler requires event role: [on event:click ...]");
2120
+ }
2121
+ const body = extractStructuralBody(roles, "body", options);
2122
+ roles.delete("body");
2123
+ return createEventHandlerNode("on", roles, body, {
2124
+ sourceLanguage: "explicit"
2125
+ });
2126
+ }
2127
+ case "if": {
2128
+ const thenBranch = extractStructuralBody(roles, "then", options);
2129
+ const elseBranch = extractStructuralBody(roles, "else", options);
2130
+ roles.delete("then");
2131
+ roles.delete("else");
2132
+ return createConditionalNode(
2133
+ command,
2134
+ roles,
2135
+ thenBranch,
2136
+ elseBranch.length > 0 ? elseBranch : void 0,
2137
+ {
2138
+ sourceLanguage: "explicit"
2139
+ }
2140
+ );
2141
+ }
2142
+ case "repeat": {
2143
+ const loopBody = extractStructuralBody(roles, "loop-body", options);
2144
+ const loopVariantValue = roles.get("loop-variant");
2145
+ const variableValue = roles.get("variable");
2146
+ const indexVariableValue = roles.get("index-variable");
2147
+ roles.delete("loop-body");
2148
+ roles.delete("loop-variant");
2149
+ roles.delete("variable");
2150
+ roles.delete("index-variable");
2151
+ let variant;
2152
+ if (loopVariantValue && loopVariantValue.type === "literal" && typeof loopVariantValue.value === "string") {
2153
+ variant = loopVariantValue.value;
2154
+ } else if (roles.has("quantity")) {
2155
+ variant = "times";
2156
+ } else if (roles.has("source")) {
2157
+ variant = "for";
2158
+ } else if (roles.has("condition")) {
2159
+ variant = "while";
2160
+ } else {
2161
+ variant = "forever";
2162
+ }
2163
+ const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
2164
+ const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
2165
+ return createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {
2166
+ sourceLanguage: "explicit"
2167
+ });
2168
+ }
2169
+ default:
2170
+ return createCommandNode(command, roles, {
2171
+ sourceLanguage: "explicit"
2172
+ });
2173
+ }
2174
+ }
2175
+ function isExplicitSyntax(input) {
2176
+ const trimmed = input.trim();
2177
+ return trimmed.startsWith("[") && trimmed.endsWith("]");
2178
+ }
2179
+ function extractStructuralBody(roles, roleName, options) {
2180
+ const value = roles.get(roleName);
2181
+ if (!value) return [];
2182
+ if (value.type === "expression") {
2183
+ return [parseExplicit(value.raw, options)];
2184
+ }
2185
+ return [];
2186
+ }
2187
+ function inferSelectorKind(value) {
2188
+ if (!value) return void 0;
2189
+ if (value.startsWith("#")) return "id";
2190
+ if (value.startsWith(".")) return "class";
2191
+ if (value.startsWith("[")) return "attribute";
2192
+ if (value.startsWith("<") || value.startsWith("*")) return "element";
2193
+ if (/[>+~ ]/.test(value) && value.length > 1) return "complex";
2194
+ return void 0;
2195
+ }
2196
+ function tokenizeExplicit(content) {
2197
+ const tokens = [];
2198
+ let current = "";
2199
+ let inString = false;
2200
+ let stringChar = "";
2201
+ let bracketDepth = 0;
2202
+ for (let i = 0; i < content.length; i++) {
2203
+ const char = content[i];
2204
+ if (inString) {
2205
+ current += char;
2206
+ if (char === stringChar && content[i - 1] !== "\\") {
2207
+ inString = false;
2208
+ }
2209
+ continue;
2210
+ }
2211
+ if (char === '"' || char === "'") {
2212
+ inString = true;
2213
+ stringChar = char;
2214
+ current += char;
2215
+ continue;
2216
+ }
2217
+ if (char === "[") {
2218
+ bracketDepth++;
2219
+ current += char;
2220
+ continue;
2221
+ }
2222
+ if (char === "]") {
2223
+ bracketDepth--;
2224
+ current += char;
2225
+ continue;
2226
+ }
2227
+ if (char === " " && bracketDepth === 0) {
2228
+ if (current) {
2229
+ tokens.push(current);
2230
+ current = "";
2231
+ }
2232
+ continue;
2233
+ }
2234
+ current += char;
2235
+ }
2236
+ if (current) {
2237
+ tokens.push(current);
2238
+ }
2239
+ return tokens;
2240
+ }
2241
+ function parseExplicitValue(valueStr, referenceSet) {
2242
+ if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
2243
+ return createSelector(valueStr, inferSelectorKind(valueStr));
2244
+ }
2245
+ if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
2246
+ const inner = valueStr.slice(1, -1);
2247
+ return createLiteral(inner, "string");
2248
+ }
2249
+ if (valueStr === "true") return createLiteral(true, "boolean");
2250
+ if (valueStr === "false") return createLiteral(false, "boolean");
2251
+ const lowerRef = valueStr.toLowerCase();
2252
+ if (isValidReference2(lowerRef, referenceSet)) {
2253
+ return createReference(lowerRef);
2254
+ }
2255
+ const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
2256
+ if (numMatch) {
2257
+ const num = parseFloat(numMatch[1]);
2258
+ const suffix = numMatch[2];
2259
+ if (suffix) {
2260
+ return createLiteral(valueStr, "duration");
2261
+ }
2262
+ return createLiteral(num, "number");
2263
+ }
2264
+ return createLiteral(valueStr);
2265
+ }
2266
+ function findMatchingBracket(str, start) {
2267
+ let depth = 0;
2268
+ for (let i = start; i < str.length; i++) {
2269
+ if (str[i] === "[") depth++;
2270
+ else if (str[i] === "]") {
2271
+ depth--;
2272
+ if (depth === 0) return i;
2273
+ }
2274
+ }
2275
+ return str.length - 1;
2276
+ }
2277
+
2278
+ // src/ir/explicit-renderer.ts
2279
+ function renderExplicit(node) {
2280
+ if (node.kind === "compound") {
2281
+ const compoundNode = node;
2282
+ const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit(stmt));
2283
+ return renderedStatements.join(` ${compoundNode.chainType} `);
2284
+ }
2285
+ const parts = [node.action];
2286
+ for (const [role, value] of node.roles) {
2287
+ if (value.type === "flag") {
2288
+ parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);
2289
+ } else {
2290
+ parts.push(`${role}:${valueToString(value)}`);
2291
+ }
2292
+ }
2293
+ if (node.kind === "event-handler") {
2294
+ const eventNode = node;
2295
+ if (eventNode.body && eventNode.body.length > 0) {
2296
+ const bodyParts = eventNode.body.map((n) => renderExplicit(n));
2297
+ parts.push(`body:${bodyParts.join(" ")}`);
2298
+ }
2299
+ }
2300
+ if (node.kind === "conditional") {
2301
+ const condNode = node;
2302
+ if (condNode.thenBranch && condNode.thenBranch.length > 0) {
2303
+ const thenParts = condNode.thenBranch.map((n) => renderExplicit(n));
2304
+ parts.push(`then:${thenParts.join(" ")}`);
2305
+ }
2306
+ if (condNode.elseBranch && condNode.elseBranch.length > 0) {
2307
+ const elseParts = condNode.elseBranch.map((n) => renderExplicit(n));
2308
+ parts.push(`else:${elseParts.join(" ")}`);
2309
+ }
2310
+ }
2311
+ if (node.kind === "loop") {
2312
+ const loopNode = node;
2313
+ if (loopNode.loopVariant) {
2314
+ parts.push(`loop-variant:${loopNode.loopVariant}`);
2315
+ }
2316
+ if (loopNode.loopVariable) {
2317
+ parts.push(`variable:${loopNode.loopVariable}`);
2318
+ }
2319
+ if (loopNode.indexVariable) {
2320
+ parts.push(`index-variable:${loopNode.indexVariable}`);
2321
+ }
2322
+ if (loopNode.body && loopNode.body.length > 0) {
2323
+ const bodyParts = loopNode.body.map((n) => renderExplicit(n));
2324
+ parts.push(`loop-body:${bodyParts.join(" ")}`);
2325
+ }
2326
+ }
2327
+ if (node.kind === "command") {
2328
+ const cmd = node;
2329
+ if (cmd.body && cmd.body.length > 0) {
2330
+ const bodyParts = cmd.body.map((n) => renderExplicit(n));
2331
+ parts.push(`body:${bodyParts.join(" ")}`);
2332
+ }
2333
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
2334
+ const catchParts = cmd.catchBranch.map((n) => renderExplicit(n));
2335
+ parts.push(`catch:${catchParts.join(" ")}`);
2336
+ }
2337
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
2338
+ const finallyParts = cmd.finallyBranch.map((n) => renderExplicit(n));
2339
+ parts.push(`finally:${finallyParts.join(" ")}`);
2340
+ }
2341
+ if (cmd.asyncVariant) {
2342
+ parts.push(`async-variant:${cmd.asyncVariant}`);
2343
+ }
2344
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
2345
+ const asyncParts = cmd.asyncBody.map((n) => renderExplicit(n));
2346
+ parts.push(`async-body:${asyncParts.join(" ")}`);
2347
+ }
2348
+ }
2349
+ return `[${parts.join(" ")}]`;
2350
+ }
2351
+ function valueToString(value) {
2352
+ switch (value.type) {
2353
+ case "literal":
2354
+ if (typeof value.value === "string") {
2355
+ if (value.dataType === "string" || /\s/.test(value.value)) {
2356
+ return `"${value.value}"`;
2357
+ }
2358
+ return value.value;
2359
+ }
2360
+ return String(value.value);
2361
+ case "selector":
2362
+ return value.value;
2363
+ case "reference":
2364
+ return value.value;
2365
+ case "property-path":
2366
+ return `${valueToString(value.object)}'s ${value.property}`;
2367
+ case "expression":
2368
+ return value.raw;
2369
+ case "flag":
2370
+ return value.name;
2371
+ }
2372
+ }
2373
+
2374
+ // src/ir/json-schema.ts
2375
+ var VALID_VALUE_TYPES = /* @__PURE__ */ new Set([
2376
+ "selector",
2377
+ "literal",
2378
+ "reference",
2379
+ "expression",
2380
+ "property-path",
2381
+ "flag"
2382
+ ]);
2383
+ function validateSemanticJSON(input) {
2384
+ const diagnostics = [];
2385
+ if (!input.action || typeof input.action !== "string") {
2386
+ diagnostics.push({
2387
+ severity: "error",
2388
+ code: "INVALID_ACTION",
2389
+ message: 'Field "action" is required and must be a string.',
2390
+ suggestion: 'Provide a command name like "toggle", "add", "set", etc.'
2391
+ });
2392
+ return diagnostics;
2393
+ }
2394
+ if (input.roles && typeof input.roles === "object") {
2395
+ for (const [role, value] of Object.entries(input.roles)) {
2396
+ if (!value || typeof value !== "object") {
2397
+ diagnostics.push({
2398
+ severity: "error",
2399
+ code: "INVALID_ROLE_VALUE",
2400
+ message: `Role "${role}" must be an object with "type" and "value" fields.`,
2401
+ suggestion: 'Use: { "type": "selector", "value": ".active" }'
2402
+ });
2403
+ continue;
2404
+ }
2405
+ const v = value;
2406
+ if (!VALID_VALUE_TYPES.has(v.type)) {
2407
+ diagnostics.push({
2408
+ severity: "error",
2409
+ code: "INVALID_VALUE_TYPE",
2410
+ message: `Role "${role}" has invalid type "${v.type}".`,
2411
+ suggestion: "Valid types: selector, literal, reference, expression, property-path."
2412
+ });
2413
+ }
2414
+ if (v.value === void 0 || v.value === null) {
2415
+ diagnostics.push({
2416
+ severity: "error",
2417
+ code: "MISSING_VALUE",
2418
+ message: `Role "${role}" is missing the "value" field.`
2419
+ });
2420
+ }
2421
+ }
2422
+ }
2423
+ if (input.trigger) {
2424
+ if (!input.trigger.event || typeof input.trigger.event !== "string") {
2425
+ diagnostics.push({
2426
+ severity: "error",
2427
+ code: "INVALID_TRIGGER",
2428
+ message: 'Trigger "event" is required and must be a string.',
2429
+ suggestion: 'Use an event name like "click", "mouseover", "keydown".'
2430
+ });
2431
+ }
2432
+ }
2433
+ return diagnostics;
2434
+ }
2435
+ function jsonToSemanticNode(input) {
2436
+ const roles = /* @__PURE__ */ new Map();
2437
+ if (input.roles) {
2438
+ for (const [role, value] of Object.entries(input.roles)) {
2439
+ roles.set(role, convertJSONValue(value));
2440
+ }
2441
+ }
2442
+ if (input.trigger) {
2443
+ roles.set("event", createLiteral(input.trigger.event, "string"));
2444
+ const bodyRoles = new Map(roles);
2445
+ bodyRoles.delete("event");
2446
+ const bodyNode = createCommandNode(input.action, bodyRoles);
2447
+ return createEventHandlerNode(
2448
+ "on",
2449
+ roles,
2450
+ [bodyNode],
2451
+ { sourceLanguage: "json" },
2452
+ input.trigger.modifiers ?? {}
2453
+ );
2454
+ }
2455
+ return createCommandNode(input.action, roles, {
2456
+ sourceLanguage: "json"
2457
+ });
2458
+ }
2459
+ function semanticNodeToJSON(node) {
2460
+ if (node.kind === "compound") {
2461
+ const compound = node;
2462
+ if (compound.statements.length > 0) {
2463
+ return semanticNodeToJSON(compound.statements[0]);
2464
+ }
2465
+ return { action: "compound", roles: {} };
2466
+ }
2467
+ if (node.kind === "event-handler") {
2468
+ const eventNode = node;
2469
+ const bodyAction = eventNode.body?.[0]?.action ?? node.action;
2470
+ const roles2 = {};
2471
+ if (eventNode.body?.[0]) {
2472
+ for (const [role, value] of eventNode.body[0].roles) {
2473
+ roles2[role] = semanticValueToJSON(value);
2474
+ }
2475
+ }
2476
+ const eventValue = node.roles.get("event");
2477
+ const eventName = eventValue ? "value" in eventValue ? String(eventValue.value) : "raw" in eventValue ? eventValue.raw : "unknown" : "unknown";
2478
+ const result = {
2479
+ action: bodyAction === "on" ? eventNode.body?.[0]?.action ?? "on" : bodyAction,
2480
+ roles: roles2,
2481
+ trigger: {
2482
+ event: eventName
2483
+ }
2484
+ };
2485
+ if (eventNode.eventModifiers && Object.keys(eventNode.eventModifiers).length > 0) {
2486
+ result.trigger.modifiers = eventNode.eventModifiers;
2487
+ }
2488
+ return result;
2489
+ }
2490
+ const roles = {};
2491
+ for (const [role, value] of node.roles) {
2492
+ roles[role] = semanticValueToJSON(value);
2493
+ }
2494
+ return { action: node.action, roles };
2495
+ }
2496
+ function convertJSONValue(value) {
2497
+ switch (value.type) {
2498
+ case "selector":
2499
+ return createSelector(String(value.value), detectSelectorKind(String(value.value)));
2500
+ case "literal":
2501
+ return createLiteral(
2502
+ value.value,
2503
+ typeof value.value === "number" ? "number" : typeof value.value === "boolean" ? "boolean" : "string"
2504
+ );
2505
+ case "reference":
2506
+ return createReference(String(value.value));
2507
+ case "expression":
2508
+ return createExpression(String(value.value));
2509
+ case "property-path":
2510
+ return createExpression(String(value.value));
2511
+ case "flag":
2512
+ return createFlag(String(value.value), true);
2513
+ default:
2514
+ return createLiteral(String(value.value), "string");
2515
+ }
2516
+ }
2517
+ function semanticValueToJSON(value) {
2518
+ switch (value.type) {
2519
+ case "selector":
2520
+ return { type: "selector", value: value.value };
2521
+ case "literal":
2522
+ return { type: "literal", value: value.value };
2523
+ case "reference":
2524
+ return { type: "reference", value: value.value };
2525
+ case "expression":
2526
+ return { type: "expression", value: value.raw };
2527
+ case "property-path":
2528
+ return {
2529
+ type: "property-path",
2530
+ value: `${semanticValueToJSON(value.object).value}.${value.property}`
2531
+ };
2532
+ case "flag":
2533
+ return { type: "flag", value: value.enabled };
2534
+ }
2535
+ }
2536
+ function detectSelectorKind(selector) {
2537
+ if (selector.startsWith("#")) return "id";
2538
+ if (selector.startsWith(".")) return "class";
2539
+ if (selector.startsWith("[")) return "attribute";
2540
+ return "complex";
2541
+ }
2542
+
2543
+ // src/ir/protocol-json.ts
2544
+ function toProtocolJSON(node) {
2545
+ const roles = rolesToProtocol(node.roles);
2546
+ let result;
2547
+ switch (node.kind) {
2548
+ case "command": {
2549
+ const cmd = node;
2550
+ result = { kind: "command", action: node.action, roles };
2551
+ if (cmd.body && cmd.body.length > 0) {
2552
+ result.body = cmd.body.map(toProtocolJSON);
2553
+ }
2554
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
2555
+ result.catchBranch = cmd.catchBranch.map(toProtocolJSON);
2556
+ }
2557
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
2558
+ result.finallyBranch = cmd.finallyBranch.map(toProtocolJSON);
2559
+ }
2560
+ if (cmd.asyncVariant) {
2561
+ result.asyncVariant = cmd.asyncVariant;
2562
+ }
2563
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
2564
+ result.asyncBody = cmd.asyncBody.map(toProtocolJSON);
2565
+ }
2566
+ if (cmd.arms && cmd.arms.length > 0) {
2567
+ result.arms = cmd.arms.map((arm) => ({
2568
+ pattern: valueToProtocol(arm.pattern),
2569
+ body: arm.body.map(toProtocolJSON)
2570
+ }));
2571
+ }
2572
+ if (cmd.defaultArm && cmd.defaultArm.length > 0) {
2573
+ result.defaultArm = cmd.defaultArm.map(toProtocolJSON);
2574
+ }
2575
+ break;
2576
+ }
2577
+ case "event-handler": {
2578
+ const eh = node;
2579
+ result = {
2580
+ kind: "event-handler",
2581
+ action: node.action,
2582
+ roles,
2583
+ body: eh.body.map(toProtocolJSON)
2584
+ };
2585
+ break;
2586
+ }
2587
+ case "compound": {
2588
+ const c = node;
2589
+ result = {
2590
+ kind: "compound",
2591
+ action: "compound",
2592
+ statements: c.statements.map(toProtocolJSON),
2593
+ chainType: c.chainType
2594
+ };
2595
+ break;
2596
+ }
2597
+ case "conditional": {
2598
+ const cond = node;
2599
+ result = { kind: "command", action: node.action, roles };
2600
+ result.thenBranch = cond.thenBranch.map(toProtocolJSON);
2601
+ if (cond.elseBranch && cond.elseBranch.length > 0) {
2602
+ result.elseBranch = cond.elseBranch.map(toProtocolJSON);
2603
+ }
2604
+ break;
2605
+ }
2606
+ case "loop": {
2607
+ const loop = node;
2608
+ result = { kind: "command", action: node.action, roles };
2609
+ result.loopVariant = loop.loopVariant;
2610
+ result.loopBody = loop.body.map(toProtocolJSON);
2611
+ if (loop.loopVariable) result.loopVariable = loop.loopVariable;
2612
+ if (loop.indexVariable) result.indexVariable = loop.indexVariable;
2613
+ break;
2614
+ }
2615
+ }
2616
+ if (node.annotations && node.annotations.length > 0) {
2617
+ result.annotations = node.annotations.map(
2618
+ (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
2619
+ );
2620
+ }
2621
+ if (node.diagnostics && node.diagnostics.length > 0) {
2622
+ result.diagnostics = node.diagnostics.map((d) => ({
2623
+ level: d.level,
2624
+ role: d.role,
2625
+ message: d.message,
2626
+ code: d.code
2627
+ }));
2628
+ }
2629
+ return result;
2630
+ }
2631
+ function rolesToProtocol(roles) {
2632
+ const result = {};
2633
+ for (const [role, value] of roles) {
2634
+ result[role] = valueToProtocol(value);
2635
+ }
2636
+ return result;
2637
+ }
2638
+ function valueToProtocol(value) {
2639
+ switch (value.type) {
2640
+ case "selector": {
2641
+ const PROTOCOL_SELECTOR_KINDS = /* @__PURE__ */ new Set([
2642
+ "id",
2643
+ "class",
2644
+ "attribute",
2645
+ "element",
2646
+ "complex"
2647
+ ]);
2648
+ const sk = "selectorKind" in value ? value.selectorKind : void 0;
2649
+ const sv = { type: "selector", value: value.value };
2650
+ if (sk && PROTOCOL_SELECTOR_KINDS.has(sk)) {
2651
+ sv.selectorKind = sk;
2652
+ }
2653
+ return sv;
2654
+ }
2655
+ case "literal":
2656
+ return value.dataType ? { type: "literal", value: value.value, dataType: value.dataType } : { type: "literal", value: value.value };
2657
+ case "reference":
2658
+ return { type: "reference", value: value.value };
2659
+ case "expression":
2660
+ return { type: "expression", raw: value.raw };
2661
+ case "property-path":
2662
+ return { type: "expression", raw: extractValue(value) };
2663
+ case "flag":
2664
+ return { type: "flag", name: value.name, enabled: value.enabled };
2665
+ }
2666
+ }
2667
+ function fromProtocolJSON(json) {
2668
+ const roles = new Map(
2669
+ Object.entries(json.roles ?? {}).map(([role, val]) => [role, valueFromProtocol(val)])
2670
+ );
2671
+ const annotations = json.annotations?.map(
2672
+ (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
2673
+ );
2674
+ const diagnostics = json.diagnostics?.map((d) => ({
2675
+ level: d.level,
2676
+ role: d.role,
2677
+ message: d.message,
2678
+ code: d.code
2679
+ }));
2680
+ if (json.trigger) {
2681
+ const eventName = json.trigger.event;
2682
+ roles.set("event", createLiteral(eventName, "string"));
2683
+ const bodyRoles = new Map(roles);
2684
+ bodyRoles.delete("event");
2685
+ const bodyNode = createCommandNode(json.action, bodyRoles);
2686
+ const node2 = createEventHandlerNode(
2687
+ "on",
2688
+ roles,
2689
+ [bodyNode],
2690
+ { sourceLanguage: "json" },
2691
+ json.trigger.modifiers ?? {}
2692
+ );
2693
+ return applyV12Metadata(node2, annotations, diagnostics);
2694
+ }
2695
+ const kind = json.kind ?? "command";
2696
+ if (kind === "event-handler") {
2697
+ const body = (json.body ?? []).map(fromProtocolJSON);
2698
+ const node2 = createEventHandlerNode(json.action, roles, body);
2699
+ return applyV12Metadata(node2, annotations, diagnostics);
2700
+ }
2701
+ if (kind === "compound") {
2702
+ const statements = (json.statements ?? []).map(fromProtocolJSON);
2703
+ const node2 = createCompoundNode(
2704
+ statements,
2705
+ json.chainType ?? "sequential"
2706
+ );
2707
+ return applyV12Metadata(node2, annotations, diagnostics);
2708
+ }
2709
+ if (json.thenBranch && json.thenBranch.length > 0) {
2710
+ const thenBranch = json.thenBranch.map(fromProtocolJSON);
2711
+ const elseBranch = json.elseBranch ? json.elseBranch.map(fromProtocolJSON) : void 0;
2712
+ const node2 = createConditionalNode(json.action, roles, thenBranch, elseBranch);
2713
+ return applyV12Metadata(node2, annotations, diagnostics);
2714
+ }
2715
+ if (json.loopVariant) {
2716
+ const loopBody = (json.loopBody ?? []).map(fromProtocolJSON);
2717
+ const node2 = createLoopNode(
2718
+ json.action,
2719
+ roles,
2720
+ json.loopVariant,
2721
+ loopBody,
2722
+ json.loopVariable,
2723
+ json.indexVariable
2724
+ );
2725
+ return applyV12Metadata(node2, annotations, diagnostics);
2726
+ }
2727
+ if (json.catchBranch || json.finallyBranch) {
2728
+ const body = (json.body ?? []).map(fromProtocolJSON);
2729
+ const catchBranch = json.catchBranch ? json.catchBranch.map(fromProtocolJSON) : void 0;
2730
+ const finallyBranch = json.finallyBranch ? json.finallyBranch.map(fromProtocolJSON) : void 0;
2731
+ const node2 = createTryNode(body, catchBranch, finallyBranch);
2732
+ return applyV12Metadata(node2, annotations, diagnostics);
2733
+ }
2734
+ if (json.asyncVariant) {
2735
+ const asyncBody = (json.asyncBody ?? []).map(fromProtocolJSON);
2736
+ const node2 = createAsyncNode(json.asyncVariant, asyncBody);
2737
+ return applyV12Metadata(node2, annotations, diagnostics);
2738
+ }
2739
+ if (json.arms && json.arms.length > 0) {
2740
+ const arms = json.arms.map((arm) => ({
2741
+ pattern: valueFromProtocol(arm.pattern),
2742
+ body: arm.body.map(fromProtocolJSON)
2743
+ }));
2744
+ const defaultArm = json.defaultArm ? json.defaultArm.map(fromProtocolJSON) : void 0;
2745
+ const node2 = createMatchNode(roles, arms, defaultArm);
2746
+ return applyV12Metadata(node2, annotations, diagnostics);
2747
+ }
2748
+ if (json.body && json.body.length > 0 && kind === "command") {
2749
+ const body = json.body.map(fromProtocolJSON);
2750
+ const node2 = createTryNode(body);
2751
+ return applyV12Metadata(node2, annotations, diagnostics);
2752
+ }
2753
+ const node = createCommandNode(json.action, roles);
2754
+ return applyV12Metadata(node, annotations, diagnostics);
2755
+ }
2756
+ function applyV12Metadata(node, annotations, diagnostics) {
2757
+ if ((!annotations || annotations.length === 0) && (!diagnostics || diagnostics.length === 0)) {
2758
+ return node;
2759
+ }
2760
+ return {
2761
+ ...node,
2762
+ ...annotations && annotations.length > 0 ? { annotations } : {},
2763
+ ...diagnostics && diagnostics.length > 0 ? { diagnostics } : {}
2764
+ };
2765
+ }
2766
+ function valueFromProtocol(value) {
2767
+ switch (value.type) {
2768
+ case "selector":
2769
+ return createSelector(String(value.value ?? ""), value.selectorKind);
2770
+ case "literal":
2771
+ return createLiteral(
2772
+ value.value !== void 0 ? value.value : "",
2773
+ value.dataType
2774
+ );
2775
+ case "reference":
2776
+ return createReference(String(value.value ?? ""));
2777
+ case "expression":
2778
+ return createExpression(value.raw ?? String(value.value ?? ""));
2779
+ case "flag":
2780
+ return createFlag(
2781
+ value.name ?? String(value.value ?? ""),
2782
+ value.enabled ?? (typeof value.value === "boolean" ? value.value : true)
2783
+ );
2784
+ case "property-path":
2785
+ return createExpression(String(value.value ?? value.raw ?? ""));
2786
+ }
2787
+ }
2788
+ function toEnvelopeJSON(envelope) {
2789
+ const result = {
2790
+ lseVersion: envelope.lseVersion,
2791
+ nodes: envelope.nodes.map(toProtocolJSON)
2792
+ };
2793
+ if (envelope.features && envelope.features.length > 0) {
2794
+ result.features = [...envelope.features];
2795
+ }
2796
+ return result;
2797
+ }
2798
+ function fromEnvelopeJSON(json) {
2799
+ return {
2800
+ lseVersion: json.lseVersion,
2801
+ ...json.features && json.features.length > 0 ? { features: json.features } : {},
2802
+ nodes: json.nodes.map(fromProtocolJSON)
2803
+ };
2804
+ }
2805
+ function isEnvelope(json) {
2806
+ return typeof json === "object" && json !== null && "lseVersion" in json && "nodes" in json && Array.isArray(json.nodes);
2807
+ }
2808
+ var VALID_KINDS = /* @__PURE__ */ new Set(["command", "event-handler", "compound"]);
2809
+ var VALID_VALUE_TYPES2 = /* @__PURE__ */ new Set([
2810
+ "selector",
2811
+ "literal",
2812
+ "reference",
2813
+ "expression",
2814
+ "property-path",
2815
+ "flag"
2816
+ ]);
2817
+ var VALID_CHAIN_TYPES = /* @__PURE__ */ new Set(["then", "and", "async", "sequential", "pipe"]);
2818
+ var VALID_ASYNC_VARIANTS = /* @__PURE__ */ new Set(["all", "race"]);
2819
+ var VALID_DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set(["error", "warning"]);
2820
+ function validateProtocolJSON(json) {
2821
+ const diagnostics = [];
2822
+ if (typeof json !== "object" || json === null) {
2823
+ return [{ severity: "error", code: "INVALID_ROOT", message: "Root must be a JSON object" }];
2824
+ }
2825
+ const node = json;
2826
+ const kind = "kind" in node ? String(node.kind) : "command";
2827
+ if ("kind" in node && !VALID_KINDS.has(kind)) {
2828
+ diagnostics.push({
2829
+ severity: "error",
2830
+ code: "INVALID_KIND",
2831
+ message: `Invalid kind "${node.kind}". Must be one of: command, event-handler, compound`
2832
+ });
2833
+ }
2834
+ if (!("action" in node)) {
2835
+ diagnostics.push({
2836
+ severity: "error",
2837
+ code: "MISSING_ACTION",
2838
+ message: "Missing required field: action"
2839
+ });
2840
+ } else if (typeof node.action !== "string" || node.action.length === 0) {
2841
+ diagnostics.push({
2842
+ severity: "error",
2843
+ code: "INVALID_ACTION",
2844
+ message: "action must be a non-empty string"
2845
+ });
2846
+ }
2847
+ if (!("roles" in node) && kind !== "compound") {
2848
+ diagnostics.push({
2849
+ severity: "error",
2850
+ code: "MISSING_ROLES",
2851
+ message: "Missing required field: roles"
2852
+ });
2853
+ } else if ("roles" in node && (typeof node.roles !== "object" || node.roles === null || Array.isArray(node.roles))) {
2854
+ diagnostics.push({
2855
+ severity: "error",
2856
+ code: "INVALID_ROLES",
2857
+ message: "roles must be a plain object"
2858
+ });
2859
+ } else if ("roles" in node && node.roles) {
2860
+ for (const [role, value] of Object.entries(node.roles)) {
2861
+ if (typeof value !== "object" || value === null) {
2862
+ diagnostics.push({
2863
+ severity: "error",
2864
+ code: "INVALID_ROLE_VALUE",
2865
+ message: `Role "${role}" value must be an object`
2866
+ });
2867
+ continue;
2868
+ }
2869
+ const v = value;
2870
+ if (!("type" in v) || !VALID_VALUE_TYPES2.has(String(v.type))) {
2871
+ diagnostics.push({
2872
+ severity: "error",
2873
+ code: "INVALID_VALUE_TYPE",
2874
+ message: `Role "${role}" has invalid type "${v.type}"`
2875
+ });
2876
+ }
2877
+ if (v.type === "flag") {
2878
+ const hasName = typeof v.name === "string";
2879
+ const hasEnabled = typeof v.enabled === "boolean";
2880
+ const hasValue = "value" in v;
2881
+ if (!hasName && !hasValue) {
2882
+ diagnostics.push({
2883
+ severity: "error",
2884
+ code: "MISSING_FLAG_NAME",
2885
+ message: `Flag role "${role}" missing required field: name (or value)`
2886
+ });
2887
+ }
2888
+ if (!hasEnabled && !hasValue) {
2889
+ diagnostics.push({
2890
+ severity: "error",
2891
+ code: "MISSING_FLAG_ENABLED",
2892
+ message: `Flag role "${role}" missing required field: enabled (or value)`
2893
+ });
2894
+ }
2895
+ }
2896
+ if (v.type === "expression" && typeof v.raw !== "string" && !("value" in v)) {
2897
+ diagnostics.push({
2898
+ severity: "error",
2899
+ code: "MISSING_EXPRESSION_RAW",
2900
+ message: `Expression role "${role}" missing required field: raw (or value)`
2901
+ });
2902
+ }
2903
+ }
2904
+ }
2905
+ if ("trigger" in node && node.trigger) {
2906
+ const trigger = node.trigger;
2907
+ if (typeof trigger !== "object" || trigger === null) {
2908
+ diagnostics.push({
2909
+ severity: "error",
2910
+ code: "INVALID_TRIGGER",
2911
+ message: "trigger must be an object"
2912
+ });
2913
+ } else if (!trigger.event || typeof trigger.event !== "string") {
2914
+ diagnostics.push({
2915
+ severity: "error",
2916
+ code: "INVALID_TRIGGER",
2917
+ message: "trigger.event is required and must be a non-empty string"
2918
+ });
2919
+ }
2920
+ }
2921
+ if (kind === "event-handler" && !("trigger" in node)) {
2922
+ if (!("body" in node) || !Array.isArray(node.body)) {
2923
+ diagnostics.push({
2924
+ severity: "error",
2925
+ code: "MISSING_BODY",
2926
+ message: "event-handler node requires a body array"
2927
+ });
2928
+ }
2929
+ }
2930
+ if (kind === "compound") {
2931
+ if (!("statements" in node) || !Array.isArray(node.statements)) {
2932
+ diagnostics.push({
2933
+ severity: "error",
2934
+ code: "MISSING_STATEMENTS",
2935
+ message: "compound node requires a statements array"
2936
+ });
2937
+ }
2938
+ if ("chainType" in node && !VALID_CHAIN_TYPES.has(String(node.chainType))) {
2939
+ diagnostics.push({
2940
+ severity: "error",
2941
+ code: "INVALID_CHAIN_TYPE",
2942
+ message: `Invalid chainType "${node.chainType}". Must be one of: then, and, async, sequential, pipe`
2943
+ });
2944
+ }
2945
+ }
2946
+ if ("diagnostics" in node) {
2947
+ if (!Array.isArray(node.diagnostics)) {
2948
+ diagnostics.push({
2949
+ severity: "error",
2950
+ code: "INVALID_DIAGNOSTICS",
2951
+ message: "diagnostics must be an array"
2952
+ });
2953
+ } else {
2954
+ for (let i = 0; i < node.diagnostics.length; i++) {
2955
+ const d = node.diagnostics[i];
2956
+ if (!d || typeof d !== "object") {
2957
+ diagnostics.push({
2958
+ severity: "error",
2959
+ code: "INVALID_DIAGNOSTIC_ENTRY",
2960
+ message: `diagnostics[${i}] must be an object`
2961
+ });
2962
+ continue;
2963
+ }
2964
+ if (!VALID_DIAGNOSTIC_LEVELS.has(String(d.level))) {
2965
+ diagnostics.push({
2966
+ severity: "error",
2967
+ code: "INVALID_DIAGNOSTIC_LEVEL",
2968
+ message: `diagnostics[${i}].level must be "error" or "warning"`
2969
+ });
2970
+ }
2971
+ if (typeof d.role !== "string") {
2972
+ diagnostics.push({
2973
+ severity: "error",
2974
+ code: "MISSING_DIAGNOSTIC_ROLE",
2975
+ message: `diagnostics[${i}] missing required field: role`
2976
+ });
2977
+ }
2978
+ if (typeof d.message !== "string") {
2979
+ diagnostics.push({
2980
+ severity: "error",
2981
+ code: "MISSING_DIAGNOSTIC_MESSAGE",
2982
+ message: `diagnostics[${i}] missing required field: message`
2983
+ });
2984
+ }
2985
+ if (typeof d.code !== "string") {
2986
+ diagnostics.push({
2987
+ severity: "error",
2988
+ code: "MISSING_DIAGNOSTIC_CODE",
2989
+ message: `diagnostics[${i}] missing required field: code`
2990
+ });
2991
+ }
2992
+ }
2993
+ }
2994
+ }
2995
+ if ("annotations" in node) {
2996
+ if (!Array.isArray(node.annotations)) {
2997
+ diagnostics.push({
2998
+ severity: "error",
2999
+ code: "INVALID_ANNOTATIONS",
3000
+ message: "annotations must be an array"
3001
+ });
3002
+ } else {
3003
+ for (let i = 0; i < node.annotations.length; i++) {
3004
+ const a = node.annotations[i];
3005
+ if (!a || typeof a !== "object") {
3006
+ diagnostics.push({
3007
+ severity: "error",
3008
+ code: "INVALID_ANNOTATION_ENTRY",
3009
+ message: `annotations[${i}] must be an object`
3010
+ });
3011
+ continue;
3012
+ }
3013
+ if (typeof a.name !== "string" || a.name.length === 0) {
3014
+ diagnostics.push({
3015
+ severity: "error",
3016
+ code: "MISSING_ANNOTATION_NAME",
3017
+ message: `annotations[${i}] missing required field: name`
3018
+ });
3019
+ }
3020
+ }
3021
+ }
3022
+ }
3023
+ if ("asyncVariant" in node && !VALID_ASYNC_VARIANTS.has(String(node.asyncVariant))) {
3024
+ diagnostics.push({
3025
+ severity: "error",
3026
+ code: "INVALID_ASYNC_VARIANT",
3027
+ message: `Invalid asyncVariant "${node.asyncVariant}". Must be "all" or "race"`
3028
+ });
3029
+ }
3030
+ if ("arms" in node) {
3031
+ if (!Array.isArray(node.arms)) {
3032
+ diagnostics.push({
3033
+ severity: "error",
3034
+ code: "INVALID_ARMS",
3035
+ message: "arms must be an array"
3036
+ });
3037
+ } else {
3038
+ for (let i = 0; i < node.arms.length; i++) {
3039
+ const arm = node.arms[i];
3040
+ if (!arm || typeof arm !== "object") {
3041
+ diagnostics.push({
3042
+ severity: "error",
3043
+ code: "INVALID_ARM_ENTRY",
3044
+ message: `arms[${i}] must be an object`
3045
+ });
3046
+ continue;
3047
+ }
3048
+ if (!arm.pattern || typeof arm.pattern !== "object") {
3049
+ diagnostics.push({
3050
+ severity: "error",
3051
+ code: "MISSING_ARM_PATTERN",
3052
+ message: `arms[${i}] missing required field: pattern`
3053
+ });
3054
+ }
3055
+ if (!Array.isArray(arm.body)) {
3056
+ diagnostics.push({
3057
+ severity: "error",
3058
+ code: "MISSING_ARM_BODY",
3059
+ message: `arms[${i}] missing required field: body (array)`
3060
+ });
3061
+ }
3062
+ }
3063
+ }
3064
+ }
3065
+ return diagnostics;
3066
+ }
3067
+
3068
+ // src/ir/from-interchange.ts
3069
+ function fromInterchangeNode(node) {
3070
+ if (!node || typeof node !== "object") {
3071
+ return createCommandNode("unknown", {});
3072
+ }
3073
+ switch (node.type) {
3074
+ case "event":
3075
+ return convertEvent(node);
3076
+ case "command":
3077
+ return convertCommand(node);
3078
+ case "if":
3079
+ return convertIf(node);
3080
+ case "repeat":
3081
+ return convertRepeat(node);
3082
+ case "foreach":
3083
+ return convertForEach(node);
3084
+ case "while":
3085
+ return convertWhile(node);
3086
+ // Expression nodes — wrap as `get` command with patient role
3087
+ default:
3088
+ return createCommandNode("get", { patient: convertValue(node) });
3089
+ }
3090
+ }
3091
+ function convertEvent(node) {
3092
+ const event = String(node.event ?? "click");
3093
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3094
+ const body = bodyNodes.map(fromInterchangeNode);
3095
+ const roles = {
3096
+ event: createLiteral(event)
3097
+ };
3098
+ if (node.target && typeof node.target === "object") {
3099
+ roles.source = convertValue(node.target);
3100
+ }
3101
+ const modifiers = node.modifiers;
3102
+ let eventModifiers;
3103
+ if (modifiers) {
3104
+ eventModifiers = bridgeEventModifiers(modifiers);
3105
+ if (modifiers.prevent) roles.prevent = createLiteral(true);
3106
+ if (modifiers.stop) roles.stop = createLiteral(true);
3107
+ if (modifiers.passive) roles.passive = createLiteral(true);
3108
+ if (modifiers.capture) roles.capture = createLiteral(true);
3109
+ }
3110
+ return createEventHandlerNode("on", roles, body, void 0, eventModifiers);
3111
+ }
3112
+ function convertCommand(node) {
3113
+ const name = String(node.name ?? "unknown");
3114
+ const args = Array.isArray(node.args) ? node.args : [];
3115
+ const target = node.target && typeof node.target === "object" ? node.target : void 0;
3116
+ const existingRoles = node.roles;
3117
+ const modifiers = node.modifiers;
3118
+ let roles;
3119
+ if (existingRoles && typeof existingRoles === "object" && Object.keys(existingRoles).length > 0) {
3120
+ roles = {};
3121
+ for (const [role, value] of Object.entries(existingRoles)) {
3122
+ if (value && typeof value === "object" && "type" in value) {
3123
+ roles[role] = convertValue(value);
3124
+ }
3125
+ }
3126
+ } else {
3127
+ roles = inferRoles(name, args, modifiers, target);
3128
+ }
3129
+ return createCommandNode(name, roles);
3130
+ }
3131
+ function convertIf(node) {
3132
+ const condition = node.condition && typeof node.condition === "object" ? convertValue(node.condition) : createExpression("true");
3133
+ const thenBody = Array.isArray(node.thenBranch) ? node.thenBranch : [];
3134
+ const thenBranch = thenBody.map(fromInterchangeNode);
3135
+ let elseBranch;
3136
+ const elseIfBranches = node.elseIfBranches;
3137
+ if (elseIfBranches && elseIfBranches.length > 0) {
3138
+ elseBranch = [chainElseIfs(elseIfBranches, node.elseBranch)];
3139
+ } else if (Array.isArray(node.elseBranch) && node.elseBranch.length > 0) {
3140
+ elseBranch = node.elseBranch.map(fromInterchangeNode);
3141
+ }
3142
+ const roles = { condition };
3143
+ return createConditionalNode("if", roles, thenBranch, elseBranch);
3144
+ }
3145
+ function chainElseIfs(branches, finalElse) {
3146
+ const [first, ...rest] = branches;
3147
+ const condition = convertValue(first.condition);
3148
+ const thenBranch = first.body.map(fromInterchangeNode);
3149
+ let elseBranch;
3150
+ if (rest.length > 0) {
3151
+ elseBranch = [chainElseIfs(rest, finalElse)];
3152
+ } else if (finalElse && finalElse.length > 0) {
3153
+ elseBranch = finalElse.map(fromInterchangeNode);
3154
+ }
3155
+ return createConditionalNode("if", { condition }, thenBranch, elseBranch);
3156
+ }
3157
+ function convertRepeat(node) {
3158
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3159
+ const body = bodyNodes.map(fromInterchangeNode);
3160
+ const roles = {};
3161
+ let variant;
3162
+ if (node.count !== void 0 && node.count !== null) {
3163
+ variant = "times";
3164
+ if (typeof node.count === "number") {
3165
+ roles.quantity = createLiteral(node.count, "number");
3166
+ } else if (typeof node.count === "object") {
3167
+ roles.quantity = convertValue(node.count);
3168
+ }
3169
+ } else if (node.whileCondition && typeof node.whileCondition === "object") {
3170
+ variant = "while";
3171
+ roles.condition = convertValue(node.whileCondition);
3172
+ } else if (typeof node.untilEvent === "string") {
3173
+ variant = "until";
3174
+ roles.condition = createLiteral(node.untilEvent);
3175
+ } else {
3176
+ variant = "forever";
3177
+ }
3178
+ return createLoopNode("repeat", roles, variant, body);
3179
+ }
3180
+ function convertForEach(node) {
3181
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3182
+ const body = bodyNodes.map(fromInterchangeNode);
3183
+ const roles = {};
3184
+ if (node.collection && typeof node.collection === "object") {
3185
+ roles.source = convertValue(node.collection);
3186
+ }
3187
+ const itemName = typeof node.itemName === "string" ? node.itemName : void 0;
3188
+ const indexName = typeof node.indexName === "string" ? node.indexName : void 0;
3189
+ return createLoopNode("repeat", roles, "for", body, itemName, indexName);
3190
+ }
3191
+ function convertWhile(node) {
3192
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3193
+ const body = bodyNodes.map(fromInterchangeNode);
3194
+ const roles = {};
3195
+ if (node.condition && typeof node.condition === "object") {
3196
+ roles.condition = convertValue(node.condition);
3197
+ }
3198
+ return createLoopNode("repeat", roles, "while", body);
3199
+ }
3200
+ function convertValue(node) {
3201
+ switch (node.type) {
3202
+ case "literal":
3203
+ return convertLiteral(node);
3204
+ case "selector":
3205
+ return createSelector(String(node.value ?? ""), inferSelectorKind2(String(node.value ?? "")));
3206
+ case "identifier":
3207
+ return convertIdentifier(node);
3208
+ case "variable":
3209
+ return convertVariable(node);
3210
+ case "possessive":
3211
+ return createPropertyPath(
3212
+ node.object && typeof node.object === "object" ? convertValue(node.object) : createReference("me"),
3213
+ String(node.property ?? "")
3214
+ );
3215
+ // Complex expressions → collapse to raw string
3216
+ case "binary":
3217
+ case "unary":
3218
+ case "member":
3219
+ case "call":
3220
+ case "positional":
3221
+ return createExpression(renderExpr(node));
3222
+ default:
3223
+ if ("value" in node) return createLiteral(String(node.value ?? ""));
3224
+ return createExpression(renderExpr(node));
3225
+ }
3226
+ }
3227
+ function convertLiteral(node) {
3228
+ const value = node.value;
3229
+ if (value === null || value === void 0) return createLiteral("null");
3230
+ const dataType = typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : void 0;
3231
+ return createLiteral(value, dataType);
3232
+ }
3233
+ function convertIdentifier(node) {
3234
+ const value = String(node.value ?? "");
3235
+ if (DEFAULT_REFERENCES.has(value)) {
3236
+ return createReference(value);
3237
+ }
3238
+ return createLiteral(value);
3239
+ }
3240
+ function convertVariable(node) {
3241
+ const name = String(node.name ?? "");
3242
+ const scope = String(node.scope ?? "local");
3243
+ switch (scope) {
3244
+ case "element":
3245
+ return createExpression(`:${name}`);
3246
+ case "global":
3247
+ return createExpression(`$${name}`);
3248
+ default:
3249
+ return createExpression(name);
3250
+ }
3251
+ }
3252
+ function renderExpr(node) {
3253
+ if (!node || typeof node !== "object") return "";
3254
+ switch (node.type) {
3255
+ case "literal": {
3256
+ const v = node.value;
3257
+ if (v === null || v === void 0) return "null";
3258
+ if (typeof v === "string") return /\s/.test(v) ? `"${v}"` : v;
3259
+ return String(v);
3260
+ }
3261
+ case "identifier":
3262
+ return String(node.value ?? "");
3263
+ case "selector":
3264
+ return String(node.value ?? "");
3265
+ case "variable": {
3266
+ const name = String(node.name ?? "");
3267
+ const scope = String(node.scope ?? "local");
3268
+ if (scope === "element") return `:${name}`;
3269
+ if (scope === "global") return `$${name}`;
3270
+ return name;
3271
+ }
3272
+ case "binary": {
3273
+ const left = node.left && typeof node.left === "object" ? renderExpr(node.left) : "";
3274
+ const right = node.right && typeof node.right === "object" ? renderExpr(node.right) : "";
3275
+ return `${left} ${node.operator ?? ""} ${right}`;
3276
+ }
3277
+ case "unary": {
3278
+ const operand = node.operand && typeof node.operand === "object" ? renderExpr(node.operand) : "";
3279
+ return `${node.operator ?? ""} ${operand}`;
3280
+ }
3281
+ case "member": {
3282
+ const obj = node.object && typeof node.object === "object" ? renderExpr(node.object) : "";
3283
+ if (node.computed && node.property && typeof node.property === "object") {
3284
+ return `${obj}[${renderExpr(node.property)}]`;
3285
+ }
3286
+ const prop = typeof node.property === "string" ? node.property : node.property && typeof node.property === "object" ? renderExpr(node.property) : "";
3287
+ return `${obj}.${prop}`;
3288
+ }
3289
+ case "possessive": {
3290
+ const obj = node.object && typeof node.object === "object" ? renderExpr(node.object) : "";
3291
+ return `${obj}'s ${node.property ?? ""}`;
3292
+ }
3293
+ case "call": {
3294
+ const callee = node.callee && typeof node.callee === "object" ? renderExpr(node.callee) : "";
3295
+ const args = Array.isArray(node.args) ? node.args.map((a) => renderExpr(a)).join(", ") : "";
3296
+ return `${callee}(${args})`;
3297
+ }
3298
+ case "positional": {
3299
+ const target = node.target && typeof node.target === "object" ? renderExpr(node.target) : "";
3300
+ return target ? `${node.position ?? ""} ${target}` : String(node.position ?? "");
3301
+ }
3302
+ default:
3303
+ if ("value" in node) return String(node.value ?? "");
3304
+ return "";
3305
+ }
3306
+ }
3307
+ function inferRoles(name, args, modifiers, target) {
3308
+ const roles = {};
3309
+ switch (name) {
3310
+ case "set": {
3311
+ if (args[0]) roles.destination = convertValue(args[0]);
3312
+ const toVal = modifiers?.to;
3313
+ if (toVal && typeof toVal === "object" && "type" in toVal) {
3314
+ roles.patient = convertValue(toVal);
3315
+ } else if (args[1]) {
3316
+ roles.patient = convertValue(args[1]);
3317
+ }
3318
+ break;
3319
+ }
3320
+ case "put": {
3321
+ if (args[0]) roles.patient = convertValue(args[0]);
3322
+ if (modifiers) {
3323
+ for (const key of ["into", "before", "after"]) {
3324
+ const val = modifiers[key];
3325
+ if (val && typeof val === "object" && "type" in val) {
3326
+ roles.destination = convertValue(val);
3327
+ roles.method = createLiteral(key);
3328
+ break;
3329
+ }
3330
+ }
3331
+ }
3332
+ if (!roles.destination && target) {
3333
+ roles.destination = convertValue(target);
3334
+ }
3335
+ break;
3336
+ }
3337
+ case "increment":
3338
+ case "decrement": {
3339
+ if (args[0]) roles.destination = convertValue(args[0]);
3340
+ const byVal = modifiers?.by;
3341
+ if (byVal && typeof byVal === "object" && "type" in byVal) {
3342
+ roles.quantity = convertValue(byVal);
3343
+ } else if (args[1]) {
3344
+ roles.quantity = convertValue(args[1]);
3345
+ }
3346
+ break;
3347
+ }
3348
+ case "fetch": {
3349
+ if (args[0]) roles.source = convertValue(args[0]);
3350
+ const asVal = modifiers?.as;
3351
+ if (asVal && typeof asVal === "object" && "type" in asVal) {
3352
+ roles.responseType = convertValue(asVal);
3353
+ } else if (typeof asVal === "string") {
3354
+ roles.responseType = createLiteral(asVal);
3355
+ }
3356
+ break;
3357
+ }
3358
+ case "wait":
3359
+ case "settle": {
3360
+ if (args[0]) roles.duration = convertValue(args[0]);
3361
+ break;
3362
+ }
3363
+ case "toggle":
3364
+ case "add":
3365
+ case "show":
3366
+ case "hide": {
3367
+ if (args[0]) roles.patient = convertValue(args[0]);
3368
+ if (target) roles.destination = convertValue(target);
3369
+ break;
3370
+ }
3371
+ case "remove": {
3372
+ if (args[0]) roles.patient = convertValue(args[0]);
3373
+ if (target) roles.source = convertValue(target);
3374
+ break;
3375
+ }
3376
+ case "send":
3377
+ case "trigger": {
3378
+ if (args[0]) roles.patient = convertValue(args[0]);
3379
+ if (target) roles.destination = convertValue(target);
3380
+ break;
3381
+ }
3382
+ case "log": {
3383
+ if (args[0]) roles.patient = convertValue(args[0]);
3384
+ break;
3385
+ }
3386
+ default: {
3387
+ if (args[0]) roles.patient = convertValue(args[0]);
3388
+ if (target) roles.destination = convertValue(target);
3389
+ break;
3390
+ }
3391
+ }
3392
+ return roles;
3393
+ }
3394
+ function inferSelectorKind2(value) {
3395
+ if (!value) return void 0;
3396
+ if (value.startsWith("#")) return "id";
3397
+ if (value.startsWith(".")) return "class";
3398
+ if (value.startsWith("[")) return "attribute";
3399
+ if (value.startsWith("<") || value.startsWith("*")) return "element";
3400
+ if (/[>\+~ ]/.test(value) && value.length > 1) return "complex";
3401
+ return void 0;
3402
+ }
3403
+ function inferValueFromString(value) {
3404
+ if (DEFAULT_REFERENCES.has(value)) return createReference(value);
3405
+ const kind = inferSelectorKind2(value);
3406
+ if (kind) return createSelector(value, kind);
3407
+ return createLiteral(value);
3408
+ }
3409
+ function bridgeEventModifiers(mods) {
3410
+ const once = mods.once === true ? true : void 0;
3411
+ const debounce = typeof mods.debounce === "number" ? mods.debounce : void 0;
3412
+ const throttle = typeof mods.throttle === "number" ? mods.throttle : void 0;
3413
+ const from = typeof mods.from === "string" ? inferValueFromString(mods.from) : void 0;
3414
+ if (once === void 0 && debounce === void 0 && throttle === void 0 && from === void 0) {
3415
+ return void 0;
3416
+ }
3417
+ return {
3418
+ ...once !== void 0 ? { once } : {},
3419
+ ...debounce !== void 0 ? { debounce } : {},
3420
+ ...throttle !== void 0 ? { throttle } : {},
3421
+ ...from !== void 0 ? { from } : {}
3422
+ };
3423
+ }
3424
+
1912
3425
  // src/api/create-dsl.ts
1913
3426
  var DSLRegistry = class {
1914
3427
  constructor(config) {
@@ -1946,12 +3459,23 @@ var MultilingualDSLImpl = class {
1946
3459
  if (config.codeGenerator) {
1947
3460
  this.codeGenerator = config.codeGenerator;
1948
3461
  }
3462
+ const schemaMap = new Map(config.schemas.map((s) => [s.action, s]));
3463
+ this.schemaLookup = {
3464
+ getSchema: (action) => schemaMap.get(action)
3465
+ };
1949
3466
  }
1950
3467
  parse(input, language) {
1951
3468
  const result = this.parseWithConfidence(input, language);
1952
3469
  return result.node;
1953
3470
  }
1954
3471
  parseWithConfidence(input, language) {
3472
+ if (isExplicitSyntax(input)) {
3473
+ const node = parseExplicit(input, { schemaLookup: this.schemaLookup });
3474
+ if (!this.schemaLookup.getSchema(node.action)) {
3475
+ throw new Error(`No schema for action "${node.action}" in this DSL`);
3476
+ }
3477
+ return { node, confidence: 1 };
3478
+ }
1955
3479
  const tokenizer = this.registry.getTokenizer(language);
1956
3480
  if (!tokenizer) {
1957
3481
  throw new Error(`No tokenizer registered for language: ${language}`);
@@ -1987,75 +3511,802 @@ var MultilingualDSLImpl = class {
1987
3511
  valid: true,
1988
3512
  node: result.node
1989
3513
  };
1990
- } catch (error) {
3514
+ } catch (error) {
3515
+ return {
3516
+ valid: false,
3517
+ errors: [error instanceof Error ? error.message : String(error)]
3518
+ };
3519
+ }
3520
+ }
3521
+ translate(input, fromLanguage, toLanguage) {
3522
+ if (isExplicitSyntax(input)) return input;
3523
+ return this.transformer.transform(input, fromLanguage, toLanguage);
3524
+ }
3525
+ compile(input, language) {
3526
+ if (!this.codeGenerator) {
3527
+ return {
3528
+ ok: false,
3529
+ errors: ["No code generator configured for this DSL"]
3530
+ };
3531
+ }
3532
+ try {
3533
+ const result = this.parseWithConfidence(input, language);
3534
+ const code = this.codeGenerator.generate(result.node);
3535
+ return {
3536
+ ok: true,
3537
+ code,
3538
+ node: result.node,
3539
+ metadata: {
3540
+ parser: "semantic",
3541
+ confidence: result.confidence
3542
+ }
3543
+ };
3544
+ } catch (error) {
3545
+ return {
3546
+ ok: false,
3547
+ errors: [error instanceof Error ? error.message : String(error)]
3548
+ };
3549
+ }
3550
+ }
3551
+ getSupportedLanguages() {
3552
+ return this.registry.getSupportedLanguages();
3553
+ }
3554
+ };
3555
+ function createDefaultDictionary(config) {
3556
+ const translations = {};
3557
+ for (const lang of config.languages) {
3558
+ const keywords = {};
3559
+ for (const [canonical, keywordDef] of Object.entries(lang.patternProfile.keywords)) {
3560
+ keywords[canonical] = keywordDef.primary;
3561
+ }
3562
+ translations[lang.code] = keywords;
3563
+ }
3564
+ return new InMemoryDictionary(translations);
3565
+ }
3566
+ function createDefaultProfileProvider(config) {
3567
+ const profiles = {};
3568
+ for (const lang of config.languages) {
3569
+ if (lang.grammarProfile) {
3570
+ profiles[lang.code] = lang.grammarProfile;
3571
+ }
3572
+ }
3573
+ return new InMemoryProfileProvider(profiles);
3574
+ }
3575
+ function createMultilingualDSL(config) {
3576
+ const dictionary = config.dictionary ?? createDefaultDictionary(config);
3577
+ const profileProvider = config.profileProvider ?? createDefaultProfileProvider(config);
3578
+ const transformer = new GrammarTransformer({
3579
+ dictionary,
3580
+ profileProvider
3581
+ });
3582
+ const registry = new DSLRegistry(config);
3583
+ return new MultilingualDSLImpl(config, registry, transformer);
3584
+ }
3585
+
3586
+ // src/prompts/prompt-generator.ts
3587
+ function generatePrompt(config) {
3588
+ const outputFormat = config.outputFormat ?? "both";
3589
+ const examplesPerCommand = config.examplesPerCommand ?? 2;
3590
+ const sections = [
3591
+ buildProtocolSection(),
3592
+ buildValueTypeSection(),
3593
+ buildCommandsSection(config.schemas, outputFormat, examplesPerCommand),
3594
+ buildOutputFormatSection(outputFormat),
3595
+ buildErrorRecoverySection()
3596
+ ];
3597
+ let finalSections = sections;
3598
+ if (config.maxTokens && config.maxTokens > 0) {
3599
+ finalSections = truncateToTokenBudget(sections, config.maxTokens);
3600
+ }
3601
+ const text = finalSections.map((s) => `## ${s.title}
3602
+
3603
+ ${s.content}`).join("\n\n---\n\n");
3604
+ const totalRoles = config.schemas.reduce((sum, s) => sum + s.roles.length, 0);
3605
+ const approximateTokens = estimateTokens(text);
3606
+ const metadata = {
3607
+ domain: config.domain,
3608
+ commandCount: config.schemas.length,
3609
+ roleCount: totalRoles,
3610
+ approximateTokens
3611
+ };
3612
+ return { text, sections: finalSections, metadata };
3613
+ }
3614
+ function generateExamples(schema, count = 2) {
3615
+ const combos = generateRoleCombinations(schema);
3616
+ const examples = [];
3617
+ for (let i = 0; i < Math.min(count, combos.length); i++) {
3618
+ const combo = combos[i];
3619
+ const roles = /* @__PURE__ */ new Map();
3620
+ for (const role of combo) {
3621
+ const sample = sampleValue(role);
3622
+ roles.set(role.role, sample);
3623
+ }
3624
+ const nodeRoles = /* @__PURE__ */ new Map();
3625
+ for (const [roleName, sample] of roles) {
3626
+ nodeRoles.set(roleName, toSemanticValue(sample));
3627
+ }
3628
+ const node = createCommandNode(schema.action, nodeRoles);
3629
+ const explicit = renderExplicit(node);
3630
+ const jsonRoles = {};
3631
+ for (const [roleName, sample] of roles) {
3632
+ jsonRoles[roleName] = sample;
3633
+ }
3634
+ const json = { action: schema.action, roles: jsonRoles };
3635
+ examples.push({ explicit, json });
3636
+ }
3637
+ return examples;
3638
+ }
3639
+ function generateProtocolReference() {
3640
+ const protocol = buildProtocolSection();
3641
+ const valueTypes = buildValueTypeSection();
3642
+ return `# LokaScript Explicit Syntax (LSE) Quick Reference
3643
+
3644
+ ${protocol.content}
3645
+
3646
+ ${valueTypes.content}`;
3647
+ }
3648
+ function buildProtocolSection() {
3649
+ const content = `LokaScript Explicit Syntax (LSE) is a bracket-based format for imperative commands.
3650
+
3651
+ ### Syntax
3652
+
3653
+ \`\`\`
3654
+ [command role1:value1 role2:value2 +flag1 ~flag2]
3655
+ \`\`\`
3656
+
3657
+ - **Command**: The first token inside brackets (lowercased)
3658
+ - **Role pair**: \`name:value\` \u2014 a named semantic role with a typed value (no space around colon)
3659
+ - **Enabled flag**: \`+name\` \u2014 boolean attribute present
3660
+ - **Disabled flag**: \`~name\` \u2014 boolean attribute negated
3661
+ - **Nested body**: \`body:[command ...]\` \u2014 a bracket command inside a role value
3662
+
3663
+ ### Rules
3664
+
3665
+ 1. Commands are always lowercased
3666
+ 2. Role names preserve their original case
3667
+ 3. No spaces around the colon in role:value pairs
3668
+ 4. Strings with spaces must be quoted: \`patient:"hello world"\`
3669
+ 5. Selectors start with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`
3670
+ 6. Output must be valid bracket syntax: \`[action role:value ...]\``;
3671
+ return {
3672
+ id: "protocol",
3673
+ title: "LSE Protocol",
3674
+ content,
3675
+ approximateTokens: estimateTokens(content)
3676
+ };
3677
+ }
3678
+ function buildValueTypeSection() {
3679
+ const content = `Values in LSE are classified by their syntactic form (first match wins):
3680
+
3681
+ | Type | Syntax | Example |
3682
+ |------|--------|---------|
3683
+ | Selector | Starts with \`#\` \`.\` \`[\` \`@\` \`*\` | \`#button\`, \`.active\`, \`[data-id]\` |
3684
+ | String | Quoted with \`"\` or \`'\` | \`"hello world"\`, \`'json'\` |
3685
+ | Boolean | Exact: \`true\` / \`false\` | \`visible:true\` |
3686
+ | Reference | Built-in name | \`me\`, \`you\`, \`it\`, \`result\`, \`event\`, \`target\`, \`body\` |
3687
+ | Duration | Number + suffix | \`500ms\`, \`2s\`, \`1m\`, \`1h\` |
3688
+ | Number | Digits with optional decimal | \`5\`, \`3.14\`, \`-1\` |
3689
+ | Plain | Fallback (any non-whitespace) | \`/api/users\`, \`json\`, \`production\` |
3690
+
3691
+ **Important**: Classification is by prefix, not by intent. \`#true\` is a selector (not boolean). \`event\` as a value is a reference (not plain text).`;
3692
+ return {
3693
+ id: "value-types",
3694
+ title: "Value Types",
3695
+ content,
3696
+ approximateTokens: estimateTokens(content)
3697
+ };
3698
+ }
3699
+ function buildCommandsSection(schemas, outputFormat, examplesPerCommand) {
3700
+ const parts = [];
3701
+ for (const schema of schemas) {
3702
+ parts.push(formatCommandDoc(schema, outputFormat, examplesPerCommand));
3703
+ }
3704
+ const content = parts.join("\n\n");
3705
+ return {
3706
+ id: "commands",
3707
+ title: "Available Commands",
3708
+ content,
3709
+ approximateTokens: estimateTokens(content)
3710
+ };
3711
+ }
3712
+ function buildOutputFormatSection(outputFormat) {
3713
+ let content;
3714
+ if (outputFormat === "explicit") {
3715
+ content = `Output valid LSE bracket syntax. Each command must be wrapped in brackets:
3716
+
3717
+ \`\`\`
3718
+ [command role:value ...]
3719
+ \`\`\`
3720
+
3721
+ Do NOT output JSON. Only use bracket syntax.`;
3722
+ } else if (outputFormat === "json") {
3723
+ content = `Output valid JSON in the LLM-simplified format:
3724
+
3725
+ \`\`\`json
3726
+ {
3727
+ "action": "command-name",
3728
+ "roles": {
3729
+ "roleName": { "type": "valueType", "value": "theValue" }
3730
+ }
3731
+ }
3732
+ \`\`\`
3733
+
3734
+ Valid value types: \`selector\`, \`literal\`, \`reference\`, \`expression\`.
3735
+ Do NOT output bracket syntax. Only use JSON.`;
3736
+ } else {
3737
+ content = `You may output EITHER format:
3738
+
3739
+ **Bracket syntax** (preferred for single commands):
3740
+ \`\`\`
3741
+ [command role:value ...]
3742
+ \`\`\`
3743
+
3744
+ **JSON format** (preferred when structured data is needed):
3745
+ \`\`\`json
3746
+ {
3747
+ "action": "command-name",
3748
+ "roles": {
3749
+ "roleName": { "type": "valueType", "value": "theValue" }
3750
+ }
3751
+ }
3752
+ \`\`\`
3753
+
3754
+ Both formats are equally valid. Use whichever is more natural for the context.`;
3755
+ }
3756
+ return {
3757
+ id: "output-format",
3758
+ title: "Output Format",
3759
+ content,
3760
+ approximateTokens: estimateTokens(content)
3761
+ };
3762
+ }
3763
+ function buildErrorRecoverySection() {
3764
+ const content = `If you're unsure about a command or role:
3765
+
3766
+ 1. **Unknown command**: Use the closest matching command from the Available Commands section. Do not invent commands.
3767
+ 2. **Unknown role**: Use only the roles listed for each command. Do not add roles not in the schema.
3768
+ 3. **Ambiguous value type**: When in doubt, use \`expression\` type (it's the most permissive).
3769
+ 4. **Missing required role**: Always include all required roles. Check the Required/Optional labels.
3770
+ 5. **Selector vs plain value**: If a value starts with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`, it's a selector. URLs like \`/api/data\` are plain values.
3771
+
3772
+ When generating LSE, prefer explicit role labeling over positional guessing. Every value should have a role name.`;
3773
+ return {
3774
+ id: "error-recovery",
3775
+ title: "Error Recovery",
3776
+ content,
3777
+ approximateTokens: estimateTokens(content)
3778
+ };
3779
+ }
3780
+ function formatCommandDoc(schema, outputFormat, examplesPerCommand) {
3781
+ const lines = [];
3782
+ lines.push(`### \`${schema.action}\` \u2014 ${schema.description}`);
3783
+ lines.push("");
3784
+ const required = schema.roles.filter((r) => r.required);
3785
+ const optional = schema.roles.filter((r) => !r.required);
3786
+ if (required.length > 0) {
3787
+ lines.push("**Required roles:**");
3788
+ for (const role of required) {
3789
+ lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
3790
+ }
3791
+ }
3792
+ if (optional.length > 0) {
3793
+ lines.push("**Optional roles:**");
3794
+ for (const role of optional) {
3795
+ lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
3796
+ }
3797
+ }
3798
+ const examples = generateExamples(schema, examplesPerCommand);
3799
+ if (examples.length > 0) {
3800
+ lines.push("");
3801
+ if (outputFormat === "explicit" || outputFormat === "both") {
3802
+ lines.push("**Bracket syntax:**");
3803
+ for (const ex of examples) {
3804
+ lines.push(`\`\`\`
3805
+ ${ex.explicit}
3806
+ \`\`\``);
3807
+ }
3808
+ }
3809
+ if (outputFormat === "json" || outputFormat === "both") {
3810
+ lines.push("**JSON format:**");
3811
+ for (const ex of examples) {
3812
+ lines.push(`\`\`\`json
3813
+ ${JSON.stringify(ex.json, null, 2)}
3814
+ \`\`\``);
3815
+ }
3816
+ }
3817
+ }
3818
+ return lines.join("\n");
3819
+ }
3820
+ function formatExpectedTypes(role) {
3821
+ return role.expectedTypes.join(" | ");
3822
+ }
3823
+ function generateRoleCombinations(schema) {
3824
+ const required = schema.roles.filter((r) => r.required);
3825
+ const optional = schema.roles.filter((r) => !r.required);
3826
+ const combos = [];
3827
+ if (required.length > 0) {
3828
+ combos.push(required);
3829
+ }
3830
+ for (const opt of optional) {
3831
+ combos.push([...required, opt]);
3832
+ }
3833
+ if (optional.length >= 2) {
3834
+ combos.push([...required, ...optional]);
3835
+ }
3836
+ if (combos.length === 0) {
3837
+ combos.push(schema.roles.slice());
3838
+ }
3839
+ return combos;
3840
+ }
3841
+ function sampleValue(role) {
3842
+ const primaryType = role.expectedTypes[0] || "expression";
3843
+ switch (primaryType) {
3844
+ case "selector":
3845
+ return sampleSelector(role.role);
3846
+ case "literal":
3847
+ return sampleLiteral(role.role);
3848
+ case "reference":
3849
+ return { type: "reference", value: "me" };
3850
+ case "expression":
3851
+ return sampleExpression(role.role);
3852
+ case "flag":
3853
+ return { type: "flag", value: true };
3854
+ default:
3855
+ return { type: "literal", value: "example" };
3856
+ }
3857
+ }
3858
+ function sampleSelector(roleName) {
3859
+ const selectors = {
3860
+ patient: ".active",
3861
+ destination: "#output",
3862
+ source: "#input",
3863
+ target: "#target"
3864
+ };
3865
+ return { type: "selector", value: selectors[roleName] || `#${roleName}` };
3866
+ }
3867
+ function sampleLiteral(roleName) {
3868
+ const literals = {
3869
+ duration: "30s",
3870
+ interval: "5s",
3871
+ delay: "500ms",
3872
+ timeout: "10s",
3873
+ quantity: 5,
3874
+ limit: 10,
3875
+ style: "json",
3876
+ manner: "rolling",
3877
+ format: "html"
3878
+ };
3879
+ return { type: "literal", value: literals[roleName] || roleName };
3880
+ }
3881
+ function sampleExpression(roleName) {
3882
+ const expressions = {
3883
+ source: "/api/data",
3884
+ destination: "#output",
3885
+ patient: ".active",
3886
+ instrument: "toUpperCase",
3887
+ condition: "age > 18",
3888
+ style: "json",
3889
+ duration: "5s",
3890
+ url: "/api/users"
3891
+ };
3892
+ return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
3893
+ }
3894
+ function toSemanticValue(sample) {
3895
+ switch (sample.type) {
3896
+ case "selector":
3897
+ return {
3898
+ type: "selector",
3899
+ value: String(sample.value),
3900
+ selectorKind: detectSelectorKind2(String(sample.value))
3901
+ };
3902
+ case "literal":
1991
3903
  return {
1992
- valid: false,
1993
- errors: [error instanceof Error ? error.message : String(error)]
3904
+ type: "literal",
3905
+ value: sample.value,
3906
+ dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
1994
3907
  };
3908
+ case "reference":
3909
+ return { type: "reference", value: String(sample.value) };
3910
+ case "expression":
3911
+ return { type: "expression", value: String(sample.value), raw: String(sample.value) };
3912
+ case "flag":
3913
+ return { type: "flag", value: sample.value };
3914
+ default:
3915
+ return { type: "literal", value: String(sample.value), dataType: "string" };
3916
+ }
3917
+ }
3918
+ function detectSelectorKind2(selector) {
3919
+ if (selector.startsWith("#")) return "id";
3920
+ if (selector.startsWith(".")) return "class";
3921
+ if (selector.startsWith("[")) return "attribute";
3922
+ return "complex";
3923
+ }
3924
+ function estimateTokens(text) {
3925
+ return Math.ceil(text.length / 4);
3926
+ }
3927
+ function truncateToTokenBudget(sections, maxTokens) {
3928
+ const result = [];
3929
+ let budget = maxTokens;
3930
+ for (const section of sections) {
3931
+ if (section.approximateTokens <= budget) {
3932
+ result.push(section);
3933
+ budget -= section.approximateTokens;
3934
+ } else {
3935
+ const charBudget = Math.max(budget * 4, 40);
3936
+ const truncatedContent = section.content.slice(0, charBudget) + "\n\n*(truncated)*";
3937
+ result.push({
3938
+ ...section,
3939
+ content: truncatedContent,
3940
+ approximateTokens: estimateTokens(truncatedContent)
3941
+ });
3942
+ break;
1995
3943
  }
1996
3944
  }
1997
- translate(input, fromLanguage, toLanguage) {
1998
- return this.transformer.transform(input, fromLanguage, toLanguage);
3945
+ return result;
3946
+ }
3947
+
3948
+ // src/training/schema-synthesizer.ts
3949
+ function synthesizeFromSchemas(schemas, config) {
3950
+ const maxPerCommand = config.maxPairsPerCommand ?? 5;
3951
+ const minQuality = config.minQuality ?? 0.5;
3952
+ const shuffleRoles = config.shuffleRoles ?? true;
3953
+ const languages = config.languages ?? ["en"];
3954
+ const pairs = [];
3955
+ let idCounter = 0;
3956
+ for (const schema of schemas) {
3957
+ const combos = generateRoleCombinations2(schema);
3958
+ for (const language of languages) {
3959
+ let commandPairCount = 0;
3960
+ for (const combo of combos) {
3961
+ if (commandPairCount >= maxPerCommand) break;
3962
+ const pair = buildTrainingPair(
3963
+ schema,
3964
+ combo,
3965
+ language,
3966
+ config.domain,
3967
+ `${config.domain}-synth-${idCounter++}`
3968
+ );
3969
+ if (pair.quality >= minQuality) {
3970
+ pairs.push(pair);
3971
+ commandPairCount++;
3972
+ }
3973
+ if (shuffleRoles && combo.length >= 2 && commandPairCount < maxPerCommand) {
3974
+ const shuffled = [...combo].reverse();
3975
+ const shuffledPair = buildTrainingPair(
3976
+ schema,
3977
+ shuffled,
3978
+ language,
3979
+ config.domain,
3980
+ `${config.domain}-synth-${idCounter++}`
3981
+ );
3982
+ if (shuffledPair.quality >= minQuality) {
3983
+ pairs.push(shuffledPair);
3984
+ commandPairCount++;
3985
+ }
3986
+ }
3987
+ }
3988
+ }
1999
3989
  }
2000
- compile(input, language) {
2001
- if (!this.codeGenerator) {
2002
- return {
2003
- ok: false,
2004
- errors: ["No code generator configured for this DSL"]
2005
- };
3990
+ const metadata = {
3991
+ domain: config.domain,
3992
+ commandCount: schemas.length,
3993
+ pairCount: pairs.length,
3994
+ bySource: { synthetic: pairs.length },
3995
+ languages
3996
+ };
3997
+ return { pairs, metadata };
3998
+ }
3999
+ function generateRoleCombinations2(schema) {
4000
+ const required = schema.roles.filter((r) => r.required);
4001
+ const optional = schema.roles.filter((r) => !r.required);
4002
+ const combos = [];
4003
+ if (required.length > 0) {
4004
+ combos.push(required);
4005
+ }
4006
+ for (const opt of optional) {
4007
+ combos.push([...required, opt]);
4008
+ }
4009
+ if (optional.length >= 2) {
4010
+ combos.push([...required, ...optional]);
4011
+ }
4012
+ if (combos.length === 0) {
4013
+ combos.push(schema.roles.slice());
4014
+ }
4015
+ return combos;
4016
+ }
4017
+ function buildTrainingPair(schema, roles, language, domain, id) {
4018
+ const sampledRoles = /* @__PURE__ */ new Map();
4019
+ for (const role of roles) {
4020
+ sampledRoles.set(role.role, sampleValue2(role));
4021
+ }
4022
+ const nodeRoles = /* @__PURE__ */ new Map();
4023
+ for (const [name, sample] of sampledRoles) {
4024
+ nodeRoles.set(name, toSemanticValue2(sample));
4025
+ }
4026
+ const node = createCommandNode(schema.action, nodeRoles);
4027
+ const explicit = renderExplicit(node);
4028
+ const jsonRoles = {};
4029
+ for (const [name, sample] of sampledRoles) {
4030
+ jsonRoles[name] = sample;
4031
+ }
4032
+ const json = { action: schema.action, roles: jsonRoles };
4033
+ const natural = buildNaturalForm(schema, sampledRoles, language);
4034
+ const requiredCount = schema.roles.filter((r) => r.required).length;
4035
+ const filledRequired = roles.filter((r) => r.required).length;
4036
+ const quality = requiredCount > 0 ? filledRequired / requiredCount : 0.8;
4037
+ return {
4038
+ id,
4039
+ natural,
4040
+ language,
4041
+ explicit,
4042
+ json,
4043
+ domain,
4044
+ action: schema.action,
4045
+ source: "synthetic",
4046
+ quality
4047
+ };
4048
+ }
4049
+ function buildNaturalForm(schema, roles, language) {
4050
+ const parts = [schema.action];
4051
+ for (const roleSpec of schema.roles) {
4052
+ const sample = roles.get(roleSpec.role);
4053
+ if (!sample) continue;
4054
+ const marker = roleSpec.markerOverride?.[language];
4055
+ if (marker) {
4056
+ parts.push(marker);
2006
4057
  }
2007
- try {
2008
- const result = this.parseWithConfidence(input, language);
2009
- const code = this.codeGenerator.generate(result.node);
4058
+ parts.push(String(sample.value));
4059
+ }
4060
+ return parts.join(" ");
4061
+ }
4062
+ function sampleValue2(role) {
4063
+ const primaryType = role.expectedTypes[0] || "expression";
4064
+ switch (primaryType) {
4065
+ case "selector":
4066
+ return sampleSelector2(role.role);
4067
+ case "literal":
4068
+ return sampleLiteral2(role.role);
4069
+ case "reference":
4070
+ return { type: "reference", value: "me" };
4071
+ case "expression":
4072
+ return sampleExpression2(role.role);
4073
+ case "flag":
4074
+ return { type: "flag", value: true };
4075
+ default:
4076
+ return { type: "literal", value: "example" };
4077
+ }
4078
+ }
4079
+ function sampleSelector2(roleName) {
4080
+ const selectors = {
4081
+ patient: ".active",
4082
+ destination: "#output",
4083
+ source: "#input",
4084
+ target: "#target"
4085
+ };
4086
+ return { type: "selector", value: selectors[roleName] || `#${roleName}` };
4087
+ }
4088
+ function sampleLiteral2(roleName) {
4089
+ const literals = {
4090
+ duration: "30s",
4091
+ interval: "5s",
4092
+ delay: "500ms",
4093
+ timeout: "10s",
4094
+ quantity: 5,
4095
+ limit: 10,
4096
+ style: "json",
4097
+ manner: "rolling",
4098
+ format: "html"
4099
+ };
4100
+ return { type: "literal", value: literals[roleName] || roleName };
4101
+ }
4102
+ function sampleExpression2(roleName) {
4103
+ const expressions = {
4104
+ source: "/api/data",
4105
+ destination: "#output",
4106
+ patient: ".active",
4107
+ instrument: "toUpperCase",
4108
+ condition: "age > 18",
4109
+ style: "json",
4110
+ duration: "5s",
4111
+ url: "/api/users"
4112
+ };
4113
+ return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
4114
+ }
4115
+ function toSemanticValue2(sample) {
4116
+ switch (sample.type) {
4117
+ case "selector":
2010
4118
  return {
2011
- ok: true,
2012
- code,
2013
- node: result.node,
2014
- metadata: {
2015
- parser: "semantic",
2016
- confidence: result.confidence
2017
- }
4119
+ type: "selector",
4120
+ value: String(sample.value),
4121
+ selectorKind: detectSelectorKind3(String(sample.value))
2018
4122
  };
2019
- } catch (error) {
4123
+ case "literal":
2020
4124
  return {
2021
- ok: false,
2022
- errors: [error instanceof Error ? error.message : String(error)]
4125
+ type: "literal",
4126
+ value: sample.value,
4127
+ dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
2023
4128
  };
4129
+ case "reference":
4130
+ return { type: "reference", value: String(sample.value) };
4131
+ case "expression":
4132
+ return { type: "expression", value: String(sample.value), raw: String(sample.value) };
4133
+ case "flag":
4134
+ return { type: "flag", value: sample.value };
4135
+ default:
4136
+ return { type: "literal", value: String(sample.value), dataType: "string" };
4137
+ }
4138
+ }
4139
+ function detectSelectorKind3(selector) {
4140
+ if (selector.startsWith("#")) return "id";
4141
+ if (selector.startsWith(".")) return "class";
4142
+ if (selector.startsWith("[")) return "attribute";
4143
+ return "complex";
4144
+ }
4145
+
4146
+ // src/feedback/feedback-formatter.ts
4147
+ function buildFeedback(input, inputFormat, diagnostics, schemaLookup, action) {
4148
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
4149
+ const feedbackDiagnostics = diagnostics.map((d) => toFeedbackDiagnostic(d));
4150
+ const hints = generateHints(feedbackDiagnostics, schemaLookup, action);
4151
+ let schemaInfo;
4152
+ let correctedExample;
4153
+ if (action && schemaLookup) {
4154
+ const schema = schemaLookup.getSchema(action);
4155
+ if (schema) {
4156
+ schemaInfo = extractSchemaInfo(schema);
4157
+ if (hasErrors) {
4158
+ correctedExample = generateCorrectedExample(schema);
4159
+ }
2024
4160
  }
2025
4161
  }
2026
- getSupportedLanguages() {
2027
- return this.registry.getSupportedLanguages();
4162
+ return {
4163
+ accepted: !hasErrors,
4164
+ input: { format: inputFormat, text: input },
4165
+ diagnostics: feedbackDiagnostics,
4166
+ hints,
4167
+ ...schemaInfo && { schema: schemaInfo },
4168
+ ...correctedExample && { correctedExample }
4169
+ };
4170
+ }
4171
+ function toFeedbackDiagnostic(d) {
4172
+ const fixType = classifyFixType(d);
4173
+ return {
4174
+ severity: d.severity,
4175
+ code: d.code || "unknown",
4176
+ message: d.message,
4177
+ ...d.suggestions?.[0] && { suggestion: d.suggestions[0] },
4178
+ ...fixType && { fixType }
4179
+ };
4180
+ }
4181
+ function classifyFixType(d) {
4182
+ const code = (d.code || "").toLowerCase();
4183
+ const msg = d.message.toLowerCase();
4184
+ if (code.includes("missing-role") || code.includes("missing_role") || msg.includes("required role") || msg.includes("missing role")) {
4185
+ return "missing_role";
2028
4186
  }
2029
- };
2030
- function createDefaultDictionary(config) {
2031
- const translations = {};
2032
- for (const lang of config.languages) {
2033
- const keywords = {};
2034
- for (const [canonical, keywordDef] of Object.entries(lang.patternProfile.keywords)) {
2035
- keywords[canonical] = keywordDef.primary;
4187
+ if (code.includes("invalid-type") || code.includes("invalid_type") || code.includes("type-mismatch") || msg.includes("invalid type") || msg.includes("expected type")) {
4188
+ return "invalid_type";
4189
+ }
4190
+ if (code.includes("unknown-command") || code.includes("unknown_command") || msg.includes("unknown command") || msg.includes("not recognized") && msg.includes("command")) {
4191
+ return "unknown_command";
4192
+ }
4193
+ if (code.includes("unknown-role") || code.includes("unknown_role") || msg.includes("unknown role") || msg.includes("not recognized") && msg.includes("role")) {
4194
+ return "unknown_role";
4195
+ }
4196
+ if (code.includes("parse") || code.includes("syntax") || msg.includes("syntax error") || msg.includes("parse error") || msg.includes("unexpected")) {
4197
+ return "syntax_error";
4198
+ }
4199
+ return void 0;
4200
+ }
4201
+ function generateHints(diagnostics, schemaLookup, action) {
4202
+ const hints = [];
4203
+ const schema = action && schemaLookup ? schemaLookup.getSchema(action) : void 0;
4204
+ for (const d of diagnostics) {
4205
+ if (d.severity !== "error") continue;
4206
+ switch (d.fixType) {
4207
+ case "missing_role": {
4208
+ if (schema) {
4209
+ const required = schema.roles.filter((r) => r.required).map((r) => `'${r.role}' (${r.description}, type: ${r.expectedTypes.join("|")})`).join(", ");
4210
+ hints.push(`Required roles for '${action}': ${required}`);
4211
+ } else {
4212
+ hints.push("Check that all required roles are present.");
4213
+ }
4214
+ break;
4215
+ }
4216
+ case "unknown_command": {
4217
+ hints.push(
4218
+ `Command not recognized. Ensure you're using a valid command from the domain schema.`
4219
+ );
4220
+ break;
4221
+ }
4222
+ case "unknown_role": {
4223
+ if (schema) {
4224
+ const validRoles = schema.roles.map((r) => `'${r.role}'`).join(", ");
4225
+ hints.push(`Valid roles for '${action}': ${validRoles}`);
4226
+ } else {
4227
+ hints.push("Use only roles defined in the command schema.");
4228
+ }
4229
+ break;
4230
+ }
4231
+ case "invalid_type": {
4232
+ hints.push(
4233
+ "Check value types: selectors start with #.[@*, strings use quotes, numbers are plain digits."
4234
+ );
4235
+ break;
4236
+ }
4237
+ case "syntax_error": {
4238
+ hints.push("Ensure bracket syntax: [command role:value ...]. No spaces around the colon.");
4239
+ break;
4240
+ }
4241
+ default: {
4242
+ if (d.suggestion) {
4243
+ hints.push(d.suggestion);
4244
+ }
4245
+ }
2036
4246
  }
2037
- translations[lang.code] = keywords;
2038
4247
  }
2039
- return new InMemoryDictionary(translations);
4248
+ return [...new Set(hints)];
2040
4249
  }
2041
- function createDefaultProfileProvider(config) {
2042
- const profiles = {};
2043
- for (const lang of config.languages) {
2044
- if (lang.grammarProfile) {
2045
- profiles[lang.code] = lang.grammarProfile;
4250
+ function extractSchemaInfo(schema) {
4251
+ const requiredRoles = [];
4252
+ const optionalRoles = [];
4253
+ const roleDescriptions = {};
4254
+ for (const role of schema.roles) {
4255
+ if (role.required) {
4256
+ requiredRoles.push(role.role);
4257
+ } else {
4258
+ optionalRoles.push(role.role);
2046
4259
  }
4260
+ roleDescriptions[role.role] = role.description;
2047
4261
  }
2048
- return new InMemoryProfileProvider(profiles);
4262
+ return {
4263
+ action: schema.action,
4264
+ requiredRoles,
4265
+ optionalRoles,
4266
+ roleDescriptions
4267
+ };
2049
4268
  }
2050
- function createMultilingualDSL(config) {
2051
- const dictionary = config.dictionary ?? createDefaultDictionary(config);
2052
- const profileProvider = config.profileProvider ?? createDefaultProfileProvider(config);
2053
- const transformer = new GrammarTransformer({
2054
- dictionary,
2055
- profileProvider
2056
- });
2057
- const registry = new DSLRegistry(config);
2058
- return new MultilingualDSLImpl(config, registry, transformer);
4269
+ function generateCorrectedExample(schema) {
4270
+ const roles = /* @__PURE__ */ new Map();
4271
+ const jsonRoles = {};
4272
+ for (const role of schema.roles) {
4273
+ if (!role.required) continue;
4274
+ const sample = sampleDefaultValue(role.role, role.expectedTypes[0] || "expression");
4275
+ roles.set(role.role, toSemanticValue3(sample));
4276
+ jsonRoles[role.role] = sample;
4277
+ }
4278
+ const node = createCommandNode(schema.action, roles);
4279
+ const explicit = renderExplicit(node);
4280
+ const json = { action: schema.action, roles: jsonRoles };
4281
+ return { explicit, json };
4282
+ }
4283
+ function sampleDefaultValue(roleName, expectedType) {
4284
+ switch (expectedType) {
4285
+ case "selector":
4286
+ return { type: "selector", value: `#${roleName}` };
4287
+ case "expression":
4288
+ return { type: "expression", value: `<${roleName}-value>` };
4289
+ case "literal":
4290
+ return { type: "literal", value: `<${roleName}-value>` };
4291
+ case "reference":
4292
+ return { type: "reference", value: "me" };
4293
+ default:
4294
+ return { type: "expression", value: `<${roleName}-value>` };
4295
+ }
4296
+ }
4297
+ function toSemanticValue3(sample) {
4298
+ switch (sample.type) {
4299
+ case "selector":
4300
+ return { type: "selector", value: sample.value, selectorKind: "id" };
4301
+ case "expression":
4302
+ return { type: "expression", value: sample.value, raw: sample.value };
4303
+ case "literal":
4304
+ return { type: "literal", value: sample.value, dataType: "string" };
4305
+ case "reference":
4306
+ return { type: "reference", value: sample.value };
4307
+ default:
4308
+ return { type: "literal", value: sample.value, dataType: "string" };
4309
+ }
2059
4310
  }
2060
4311
 
2061
4312
  // src/api/domain-registry.ts
@@ -2075,6 +4326,17 @@ var DomainRegistry = class {
2075
4326
  }
2076
4327
  this.descriptors.set(descriptor.name, descriptor);
2077
4328
  }
4329
+ /**
4330
+ * Attach schemas to an already-registered domain.
4331
+ * Useful for lazy loading schemas after initial registration.
4332
+ */
4333
+ setSchemas(name, schemas) {
4334
+ const existing = this.descriptors.get(name);
4335
+ if (!existing) {
4336
+ throw new Error(`Domain not registered: ${name}`);
4337
+ }
4338
+ this.descriptors.set(name, { ...existing, schemas });
4339
+ }
2078
4340
  /**
2079
4341
  * Get all registered domain names.
2080
4342
  */
@@ -2144,6 +4406,71 @@ var DomainRegistry = class {
2144
4406
  return this.getDSL(descriptor);
2145
4407
  }
2146
4408
  // ---------------------------------------------------------------------------
4409
+ // LLM ↔ LSE Convenience Methods
4410
+ // ---------------------------------------------------------------------------
4411
+ /**
4412
+ * Generate an LLM system prompt for a domain from its schemas.
4413
+ * Returns null if the domain is not registered or has no schemas.
4414
+ */
4415
+ generatePrompt(domain, config) {
4416
+ const descriptor = this.descriptors.get(domain);
4417
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
4418
+ return generatePrompt({
4419
+ domain,
4420
+ description: descriptor.description,
4421
+ schemas: descriptor.schemas,
4422
+ ...config
4423
+ });
4424
+ }
4425
+ /**
4426
+ * Synthesize training data for a domain from its schemas.
4427
+ * Returns null if the domain is not registered or has no schemas.
4428
+ */
4429
+ generateTrainingData(domain, config) {
4430
+ const descriptor = this.descriptors.get(domain);
4431
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
4432
+ return synthesizeFromSchemas(descriptor.schemas, {
4433
+ domain,
4434
+ ...config
4435
+ });
4436
+ }
4437
+ /**
4438
+ * Build structured feedback for a failed LSE input in a domain.
4439
+ * Returns null if the domain is not registered or has no schemas.
4440
+ */
4441
+ buildFeedback(domain, input, inputFormat, diagnostics) {
4442
+ const descriptor = this.descriptors.get(domain);
4443
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
4444
+ const schemaLookup = this.createSchemaLookup(descriptor.schemas);
4445
+ let action;
4446
+ try {
4447
+ if (inputFormat === "explicit") {
4448
+ const node = parseExplicit(input, { schemaLookup });
4449
+ action = node.action;
4450
+ }
4451
+ } catch {
4452
+ }
4453
+ return buildFeedback(input, inputFormat, diagnostics, schemaLookup, action);
4454
+ }
4455
+ /**
4456
+ * Get schemas for a domain (if available).
4457
+ */
4458
+ getSchemas(domain) {
4459
+ const descriptor = this.descriptors.get(domain);
4460
+ if (!descriptor?.schemas) return null;
4461
+ return descriptor.schemas;
4462
+ }
4463
+ /**
4464
+ * Create a SchemaLookup from a domain's schemas.
4465
+ */
4466
+ createSchemaLookup(schemas) {
4467
+ const map = /* @__PURE__ */ new Map();
4468
+ for (const s of schemas) {
4469
+ map.set(s.action, s);
4470
+ }
4471
+ return { getSchema: (action) => map.get(action) };
4472
+ }
4473
+ // ---------------------------------------------------------------------------
2147
4474
  // Private: DSL/renderer lifecycle
2148
4475
  // ---------------------------------------------------------------------------
2149
4476
  async getDSL(descriptor) {
@@ -2243,7 +4570,7 @@ function generateToolDefinitions(desc) {
2243
4570
  case "parse":
2244
4571
  tools.push({
2245
4572
  name: `parse_${desc.name}`,
2246
- description: `Parse a ${desc.description} input into a semantic representation. Supports: ${langList}.`,
4573
+ description: `Parse a ${desc.description} input into a semantic representation (action, roles, confidence). Supports: ${langList}.`,
2247
4574
  inputSchema: {
2248
4575
  type: "object",
2249
4576
  properties: {
@@ -2264,7 +4591,7 @@ function generateToolDefinitions(desc) {
2264
4591
  case "compile":
2265
4592
  tools.push({
2266
4593
  name: `compile_${desc.name}`,
2267
- description: `Compile a ${desc.description} input to target code. Supports: ${langList}.`,
4594
+ description: `Compile a ${desc.description} input to ${desc.outputDescription ?? "target code"}. Supports: ${langList}.`,
2268
4595
  inputSchema: {
2269
4596
  type: "object",
2270
4597
  properties: {
@@ -2285,7 +4612,7 @@ function generateToolDefinitions(desc) {
2285
4612
  case "validate":
2286
4613
  tools.push({
2287
4614
  name: `validate_${desc.name}`,
2288
- description: `Validate ${desc.description} syntax. Returns whether it parses successfully and any errors. Supports: ${langList}.`,
4615
+ description: `Check ${desc.description} syntax for errors. Returns {valid, errors[]}. Supports: ${langList}.`,
2289
4616
  inputSchema: {
2290
4617
  type: "object",
2291
4618
  properties: {
@@ -2306,7 +4633,7 @@ function generateToolDefinitions(desc) {
2306
4633
  case "translate":
2307
4634
  tools.push({
2308
4635
  name: `translate_${desc.name}`,
2309
- description: `Translate ${desc.description} input between natural languages. Parses in source language and renders in target language.`,
4636
+ description: `Translate ${desc.description} input between natural languages (requires 'from' and 'to' codes). Supports: ${langList}.`,
2310
4637
  inputSchema: {
2311
4638
  type: "object",
2312
4639
  properties: {
@@ -2372,9 +4699,9 @@ var CrossDomainDispatcher = class {
2372
4699
  if (domainNames.length === 0) return null;
2373
4700
  const candidates = [];
2374
4701
  for (const name of domainNames) {
2375
- const dsl = await this.registry.getDSLForDomain(name);
2376
- if (!dsl) continue;
2377
4702
  try {
4703
+ const dsl = await this.registry.getDSLForDomain(name);
4704
+ if (!dsl) continue;
2378
4705
  const { node, confidence } = dsl.parseWithConfidence(input, language);
2379
4706
  if (confidence >= this.minConfidence) {
2380
4707
  candidates.push({ domain: name, node, confidence, dsl });
@@ -2383,10 +4710,16 @@ var CrossDomainDispatcher = class {
2383
4710
  }
2384
4711
  }
2385
4712
  if (candidates.length === 0) return null;
2386
- candidates.sort((a, b) => {
2387
- if (b.confidence !== a.confidence) return b.confidence - a.confidence;
2388
- return this.getPriorityIndex(a.domain) - this.getPriorityIndex(b.domain);
2389
- });
4713
+ if (this.priority.length > 0) {
4714
+ candidates.sort((a, b) => {
4715
+ const pa = this.getPriorityIndex(a.domain);
4716
+ const pb = this.getPriorityIndex(b.domain);
4717
+ if (pa !== pb) return pa - pb;
4718
+ return b.confidence - a.confidence;
4719
+ });
4720
+ } else {
4721
+ candidates.sort((a, b) => b.confidence - a.confidence);
4722
+ }
2390
4723
  return candidates[0];
2391
4724
  }
2392
4725
  /**
@@ -2430,6 +4763,40 @@ var CrossDomainDispatcher = class {
2430
4763
  const result = detected.dsl.compile(input, language);
2431
4764
  return { ...result, domain: detected.domain };
2432
4765
  }
4766
+ /**
4767
+ * Compile multi-line input where each line may belong to a different domain.
4768
+ * Detects the domain for each line and compiles it, returning per-line results.
4769
+ * Empty lines and comment lines are skipped.
4770
+ */
4771
+ async compileComposite(input, language = "en") {
4772
+ const lines = input.split("\n");
4773
+ const statements = [];
4774
+ const errors = [];
4775
+ for (let i = 0; i < lines.length; i++) {
4776
+ const line = lines[i];
4777
+ if (isSkippableLine(line)) continue;
4778
+ const trimmed = line.trim();
4779
+ const detected = await this.detect(trimmed, language);
4780
+ if (detected) {
4781
+ const result = detected.dsl.compile(trimmed, language);
4782
+ statements.push({
4783
+ line: i + 1,
4784
+ input: trimmed,
4785
+ domain: detected.domain,
4786
+ ok: result.ok,
4787
+ code: result.code,
4788
+ errors: result.errors
4789
+ });
4790
+ } else {
4791
+ errors.push({
4792
+ line: i + 1,
4793
+ input: trimmed,
4794
+ message: "No domain matched this input"
4795
+ });
4796
+ }
4797
+ }
4798
+ return { statements, errors };
4799
+ }
2433
4800
  /**
2434
4801
  * Validate input against all domains, returning the best match.
2435
4802
  */
@@ -2711,6 +5078,30 @@ ${body}
2711
5078
  }
2712
5079
  };
2713
5080
 
5081
+ // src/aot/registry-bridge.ts
5082
+ async function registryToAOTBackends(registry) {
5083
+ const backends = [];
5084
+ for (const name of registry.getDomainNames()) {
5085
+ const descriptor = registry.getDescriptor(name);
5086
+ if (!descriptor?.scanConfig) continue;
5087
+ const dsl = await registry.getDSLForDomain(name);
5088
+ if (!dsl) continue;
5089
+ const scanConfig = {
5090
+ domain: name,
5091
+ attributes: [...descriptor.scanConfig.attributes],
5092
+ ...descriptor.scanConfig.scriptTypes ? { scriptTypes: [...descriptor.scanConfig.scriptTypes] } : {},
5093
+ defaultLanguage: descriptor.scanConfig.defaultLanguage ?? "en"
5094
+ };
5095
+ const codeGenerator = {
5096
+ generate(node) {
5097
+ return JSON.stringify({ action: node.action, roles: Object.fromEntries(node.roles) });
5098
+ }
5099
+ };
5100
+ backends.push({ domain: name, dsl, codeGenerator, scanConfig });
5101
+ }
5102
+ return backends;
5103
+ }
5104
+
2714
5105
  // src/schema/command-schema.ts
2715
5106
  function defineCommand(schema) {
2716
5107
  return {
@@ -4002,6 +6393,163 @@ function normalized(stem, confidence, metadata) {
4002
6393
  return { stem, confidence };
4003
6394
  }
4004
6395
 
6396
+ // src/training/jsonl-writer.ts
6397
+ function toJSONL(pairs) {
6398
+ return pairs.map((pair) => JSON.stringify(toJSONLRow(pair))).join("\n");
6399
+ }
6400
+ function toJSONLRow(pair) {
6401
+ return {
6402
+ id: pair.id,
6403
+ prompt: pair.natural,
6404
+ completion: pair.explicit,
6405
+ metadata: {
6406
+ domain: pair.domain,
6407
+ action: pair.action,
6408
+ language: pair.language,
6409
+ source: pair.source,
6410
+ quality: pair.quality,
6411
+ ...pair.confidence !== void 0 && { confidence: pair.confidence }
6412
+ }
6413
+ };
6414
+ }
6415
+ function parseJSONL(text) {
6416
+ return text.split("\n").filter((line) => line.trim().length > 0).map((line) => JSON.parse(line));
6417
+ }
6418
+
6419
+ // src/feedback/confidence-gate.ts
6420
+ function needsDisambiguation(confidence, low = 0.5, high = 0.7) {
6421
+ return confidence >= low && confidence < high;
6422
+ }
6423
+ function buildDisambiguation(input, candidates) {
6424
+ const sortedCandidates = [...candidates].sort((a, b) => b.confidence - a.confidence);
6425
+ const disambiguationCandidates = sortedCandidates.map((c, i) => ({
6426
+ action: c.action,
6427
+ confidence: c.confidence,
6428
+ explicit: renderExplicit(c.node),
6429
+ description: c.description || `${c.action} (option ${String.fromCharCode(97 + i)})`
6430
+ }));
6431
+ const optionLines = disambiguationCandidates.map(
6432
+ (c, i) => ` (${String.fromCharCode(97 + i)}) ${c.explicit} \u2014 ${c.description} (conf: ${c.confidence.toFixed(2)})`
6433
+ );
6434
+ const question = [
6435
+ `Your input parsed with confidence ${sortedCandidates[0]?.confidence.toFixed(2) ?? "?"}.`,
6436
+ ...optionLines,
6437
+ "Which did you mean?"
6438
+ ].join("\n");
6439
+ return {
6440
+ input,
6441
+ candidates: disambiguationCandidates,
6442
+ question
6443
+ };
6444
+ }
6445
+
6446
+ // src/feedback/pattern-tracker.ts
6447
+ var PatternTracker = class {
6448
+ constructor(maxEvents = 1e3) {
6449
+ this.events = [];
6450
+ this.maxEvents = maxEvents;
6451
+ }
6452
+ /**
6453
+ * Record a pattern event.
6454
+ */
6455
+ record(event) {
6456
+ this.events.push(event);
6457
+ if (this.events.length > this.maxEvents) {
6458
+ this.events = this.events.slice(this.events.length - this.maxEvents);
6459
+ }
6460
+ }
6461
+ /**
6462
+ * Get hit rate by command action.
6463
+ */
6464
+ getHitRateByCommand() {
6465
+ return this.computeHitRate((e) => e.action);
6466
+ }
6467
+ /**
6468
+ * Get hit rate by language.
6469
+ */
6470
+ getHitRateByLanguage() {
6471
+ return this.computeHitRate((e) => e.language);
6472
+ }
6473
+ /**
6474
+ * Get the most frequent failure diagnostic codes.
6475
+ */
6476
+ getTopFailures(limit = 10) {
6477
+ const codeMap = /* @__PURE__ */ new Map();
6478
+ for (const event of this.events) {
6479
+ if (event.outcome !== "rejected" || !event.diagnosticCodes) continue;
6480
+ for (const code of event.diagnosticCodes) {
6481
+ const entry = codeMap.get(code) || { count: 0, actions: /* @__PURE__ */ new Set() };
6482
+ entry.count++;
6483
+ entry.actions.add(event.action);
6484
+ codeMap.set(code, entry);
6485
+ }
6486
+ }
6487
+ return Array.from(codeMap.entries()).map(([code, { count, actions }]) => ({
6488
+ code,
6489
+ count,
6490
+ actions: Array.from(actions)
6491
+ })).sort((a, b) => b.count - a.count).slice(0, limit);
6492
+ }
6493
+ /**
6494
+ * Get a complete summary of tracking data.
6495
+ */
6496
+ getSummary() {
6497
+ const byOutcome = {};
6498
+ for (const event of this.events) {
6499
+ byOutcome[event.outcome] = (byOutcome[event.outcome] || 0) + 1;
6500
+ }
6501
+ return {
6502
+ totalEvents: this.events.length,
6503
+ byOutcome,
6504
+ byCommand: this.getHitRateByCommand(),
6505
+ byLanguage: this.getHitRateByLanguage(),
6506
+ topFailures: this.getTopFailures()
6507
+ };
6508
+ }
6509
+ /**
6510
+ * Export all events as JSONL.
6511
+ */
6512
+ exportJSONL() {
6513
+ return this.events.map((e) => JSON.stringify(e)).join("\n");
6514
+ }
6515
+ /**
6516
+ * Get the number of recorded events.
6517
+ */
6518
+ get size() {
6519
+ return this.events.length;
6520
+ }
6521
+ /**
6522
+ * Clear all events.
6523
+ */
6524
+ clear() {
6525
+ this.events = [];
6526
+ }
6527
+ // ---------------------------------------------------------------------------
6528
+ // Private
6529
+ // ---------------------------------------------------------------------------
6530
+ computeHitRate(keyFn) {
6531
+ const map = /* @__PURE__ */ new Map();
6532
+ for (const event of this.events) {
6533
+ const key = keyFn(event);
6534
+ const entry = map.get(key) || { attempts: 0, successes: 0 };
6535
+ entry.attempts++;
6536
+ if (event.outcome === "accepted") {
6537
+ entry.successes++;
6538
+ }
6539
+ map.set(key, entry);
6540
+ }
6541
+ const result = {};
6542
+ for (const [key, { attempts, successes }] of map) {
6543
+ result[key] = {
6544
+ attempts,
6545
+ successes,
6546
+ rate: attempts > 0 ? successes / attempts : 0
6547
+ };
6548
+ }
6549
+ return result;
6550
+ }
6551
+ };
6552
+
4005
6553
  // src/parsing/multi-statement.ts
4006
6554
  function createMultiStatementParser(dsl, config) {
4007
6555
  return new MultiStatementParserImpl(dsl, config);
@@ -4245,6 +6793,7 @@ export {
4245
6793
  CrossDomainDispatcher,
4246
6794
  DEFAULT_OPERATORS,
4247
6795
  DEFAULT_PUNCTUATION,
6796
+ DEFAULT_REFERENCES,
4248
6797
  DomainAwareScanner,
4249
6798
  DomainRegistry,
4250
6799
  GrammarTransformer,
@@ -4256,24 +6805,31 @@ export {
4256
6805
  NumberExtractor,
4257
6806
  OperatorExtractor,
4258
6807
  PatternMatcher,
6808
+ PatternTracker,
4259
6809
  PunctuationExtractor,
4260
6810
  StringLiteralExtractor,
4261
6811
  TokenStreamImpl,
4262
6812
  UnicodeIdentifierExtractor,
4263
6813
  WhitespaceExtractor,
4264
6814
  accumulateBlocks,
6815
+ buildDisambiguation,
6816
+ buildFeedback,
4265
6817
  buildPhrase,
4266
6818
  buildTablesFromProfiles,
4267
6819
  combineClassifiers,
6820
+ convertValue,
6821
+ createAsyncNode,
4268
6822
  createCommandNode,
4269
6823
  createCompoundNode,
4270
6824
  createConditionalNode,
4271
6825
  createDiagnosticCollector,
4272
6826
  createEventHandlerNode,
4273
6827
  createExpression,
6828
+ createFlag,
4274
6829
  createLatinCharClassifiers,
4275
6830
  createLiteral,
4276
6831
  createLoopNode,
6832
+ createMatchNode,
4277
6833
  createMultiStatementParser,
4278
6834
  createMultilingualDSL,
4279
6835
  createPosition,
@@ -4284,6 +6840,7 @@ export {
4284
6840
  createSimpleTokenizer,
4285
6841
  createToken,
4286
6842
  createTokenizerContext,
6843
+ createTryNode,
4287
6844
  createUnicodeRangeClassifier,
4288
6845
  defineCommand,
4289
6846
  defineRole,
@@ -4295,9 +6852,15 @@ export {
4295
6852
  extractUrl,
4296
6853
  extractValue,
4297
6854
  filterBySeverity,
6855
+ fromEnvelopeJSON,
4298
6856
  fromError,
6857
+ fromInterchangeNode,
6858
+ fromProtocolJSON,
6859
+ generateExamples,
4299
6860
  generatePattern,
4300
6861
  generatePatternVariants,
6862
+ generatePrompt,
6863
+ generateProtocolReference,
4301
6864
  getAllPossessiveKeywords,
4302
6865
  getDefaultExtractors,
4303
6866
  getPossessiveReference,
@@ -4310,6 +6873,8 @@ export {
4310
6873
  isClassName,
4311
6874
  isContextAwareExtractor,
4312
6875
  isDigit,
6876
+ isEnvelope,
6877
+ isExplicitSyntax,
4313
6878
  isIdSelector,
4314
6879
  isNumericValue,
4315
6880
  isPossessiveKeyword,
@@ -4319,17 +6884,33 @@ export {
4319
6884
  isSelectorStart,
4320
6885
  isTypeCompatible,
4321
6886
  isUrlStart,
6887
+ isValidReference2 as isValidReference,
4322
6888
  isVariableRef,
4323
6889
  isWhitespace,
4324
6890
  joinTokens,
6891
+ jsonToSemanticNode,
4325
6892
  lookupKeyword,
4326
6893
  lookupMarker,
4327
6894
  matchBest,
4328
6895
  matchPattern,
6896
+ needsDisambiguation,
4329
6897
  noChange,
4330
6898
  normalized,
6899
+ parseExplicit,
6900
+ parseJSONL,
4331
6901
  patternMatcher,
6902
+ registryToAOTBackends,
6903
+ renderExplicit,
6904
+ renderExpr,
4332
6905
  reorderRoles,
6906
+ semanticNodeToJSON,
6907
+ synthesizeFromSchemas,
6908
+ toEnvelopeJSON,
6909
+ toJSONL,
6910
+ toJSONLRow,
6911
+ toProtocolJSON,
6912
+ validateProtocolJSON,
6913
+ validateSemanticJSON,
4333
6914
  validateValueType,
4334
6915
  withDefaultExtractors
4335
6916
  };