@lokascript/framework 4.5.0 → 4.6.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.
package/dist/index.js CHANGED
@@ -2818,7 +2818,53 @@ function semanticValueToAST(value) {
2818
2818
  }
2819
2819
  }
2820
2820
 
2821
+ // src/api/reading.ts
2822
+ function roleValues(node) {
2823
+ const out = /* @__PURE__ */ new Map();
2824
+ for (const [role, value] of node.roles) {
2825
+ const v = value;
2826
+ out.set(role, String(v?.value ?? v?.raw ?? value));
2827
+ }
2828
+ return out;
2829
+ }
2830
+ function readingDifference(source, back) {
2831
+ if (back.action !== source.action) return `reads as ${back.action}`;
2832
+ const want = roleValues(source);
2833
+ const got = roleValues(back);
2834
+ const parts = [];
2835
+ for (const [role, value] of want) {
2836
+ if (!got.has(role)) parts.push(`${role} lost`);
2837
+ else if (got.get(role) !== value) parts.push(`${role}: ${value} \u2192 ${got.get(role)}`);
2838
+ }
2839
+ for (const role of got.keys()) if (!want.has(role)) parts.push(`${role} added`);
2840
+ return parts.join(", ");
2841
+ }
2842
+
2821
2843
  // src/api/create-dsl.ts
2844
+ var UnreadInputError = class extends Error {
2845
+ constructor(input, language, reading, unread) {
2846
+ super(`"${input}" (${language}) reads as ${reading.action} only by leaving "${unread}" unread`);
2847
+ this.input = input;
2848
+ this.language = language;
2849
+ this.reading = reading;
2850
+ this.unread = unread;
2851
+ this.code = "unread-input";
2852
+ this.name = "UnreadInputError";
2853
+ }
2854
+ };
2855
+ var TranslationReadBackError = class extends Error {
2856
+ constructor(partial, from, to, source, reading, why) {
2857
+ super(`translation ${from} \u2192 ${to} does not read back as the source (${why}): "${partial}"`);
2858
+ this.partial = partial;
2859
+ this.from = from;
2860
+ this.to = to;
2861
+ this.source = source;
2862
+ this.reading = reading;
2863
+ this.why = why;
2864
+ this.code = "translation-does-not-read-back";
2865
+ this.name = "TranslationReadBackError";
2866
+ }
2867
+ };
2822
2868
  var DSLRegistry = class {
2823
2869
  /**
2824
2870
  * @param partiallyTranslatedActions Actions permitted to lack a keyword in
@@ -2903,25 +2949,50 @@ var MultilingualDSLImpl = class {
2903
2949
  const profile = {
2904
2950
  code: language
2905
2951
  };
2952
+ const total = tokens.tokens.length;
2953
+ let best = null;
2954
+ let longestPartial = null;
2906
2955
  for (const pattern of patterns) {
2956
+ const mark = tokens.mark();
2907
2957
  const match = this.matcher.matchPattern(tokens, pattern, profile);
2908
- if (match) {
2909
- const node = {
2910
- kind: "command",
2911
- action: pattern.command,
2912
- roles: match.captured,
2913
- metadata: {
2914
- sourceLanguage: language,
2915
- sourceText: input,
2916
- patternId: pattern.id,
2917
- confidence: match.confidence
2918
- }
2919
- };
2920
- return { node, confidence: match.confidence };
2958
+ tokens.reset(mark);
2959
+ if (!match) continue;
2960
+ if (match.consumedTokens < total) {
2961
+ if (!longestPartial || match.consumedTokens > longestPartial.consumedTokens)
2962
+ longestPartial = match;
2963
+ continue;
2921
2964
  }
2965
+ if (!best || match.pattern.priority > best.pattern.priority || match.pattern.priority === best.pattern.priority && match.confidence > best.confidence + 1e-3)
2966
+ best = match;
2967
+ }
2968
+ if (best) {
2969
+ const node = this.nodeOf(best, input, language);
2970
+ return { node, confidence: best.confidence };
2971
+ }
2972
+ if (longestPartial) {
2973
+ const unread = tokens.tokens.slice(longestPartial.consumedTokens).map((t) => t.value).join(" ");
2974
+ throw new UnreadInputError(
2975
+ input,
2976
+ language,
2977
+ this.nodeOf(longestPartial, input, language),
2978
+ unread
2979
+ );
2922
2980
  }
2923
2981
  throw new Error(`No pattern matched for input: ${input}`);
2924
2982
  }
2983
+ nodeOf(match, input, language) {
2984
+ return {
2985
+ kind: "command",
2986
+ action: match.pattern.command,
2987
+ roles: match.captured,
2988
+ metadata: {
2989
+ sourceLanguage: language,
2990
+ sourceText: input,
2991
+ patternId: match.pattern.id,
2992
+ confidence: match.confidence
2993
+ }
2994
+ };
2995
+ }
2925
2996
  validate(input, language) {
2926
2997
  try {
2927
2998
  const result = this.parseWithConfidence(input, language);
@@ -2932,11 +3003,12 @@ var MultilingualDSLImpl = class {
2932
3003
  } catch (error) {
2933
3004
  return {
2934
3005
  valid: false,
2935
- errors: [error instanceof Error ? error.message : String(error)]
3006
+ errors: [error instanceof Error ? error.message : String(error)],
3007
+ ...error instanceof UnreadInputError ? { errorCode: error.code } : {}
2936
3008
  };
2937
3009
  }
2938
3010
  }
2939
- translate(input, fromLanguage, toLanguage) {
3011
+ translate(input, fromLanguage, toLanguage, options) {
2940
3012
  if (isExplicitSyntax(input)) return input;
2941
3013
  let node;
2942
3014
  try {
@@ -2948,14 +3020,46 @@ var MultilingualDSLImpl = class {
2948
3020
  throw parseError;
2949
3021
  }
2950
3022
  const rendered = this.registry.getSupportedLanguages().includes(toLanguage) ? this.render(node, toLanguage) : null;
2951
- if (rendered != null) return rendered;
3023
+ if (rendered != null) return this.readsBack(rendered, node, fromLanguage, toLanguage, options);
2952
3024
  if (this.hasGrammarProfile(fromLanguage) && this.hasGrammarProfile(toLanguage)) {
2953
- return this.transformer.transform(input, fromLanguage, toLanguage);
3025
+ return this.readsBack(
3026
+ this.transformer.transform(input, fromLanguage, toLanguage),
3027
+ node,
3028
+ fromLanguage,
3029
+ toLanguage,
3030
+ options
3031
+ );
2954
3032
  }
2955
3033
  throw new Error(
2956
3034
  `translate() could not render action "${node.action}" in language "${toLanguage}": no custom renderer, domain renderer, or schema pattern covers it, and no grammar profile is configured for the transformer fallback. Either add "${toLanguage}" to the DSL's languages/renderer, or set 'grammarProfile' on its LanguageConfig in createMultilingualDSL() (parse/validate/compile do not need it).`
2957
3035
  );
2958
3036
  }
3037
+ /**
3038
+ * `rendered`, read in `to`, must be `source`: the same action and role values, or, when the DSL
3039
+ * compiles, the same code (a parser may keep a translated word its generator normalizes: bdd ja
3040
+ * `表示` is `visible`). The rule is the domain gate's (lokascript-domains). A translation used to be
3041
+ * returned whatever it read as: ja/ko bdd `select` dropped its value, and nothing said so.
3042
+ */
3043
+ readsBack(rendered, source, from, to, options) {
3044
+ if (options?.lossy === "allow") return rendered;
3045
+ let back;
3046
+ try {
3047
+ back = this.parseWithConfidence(rendered, to).node;
3048
+ } catch (error) {
3049
+ const why = error instanceof Error ? error.message : String(error);
3050
+ throw new TranslationReadBackError(rendered, from, to, source, null, why);
3051
+ }
3052
+ const difference = readingDifference(source, back);
3053
+ if (!difference) return rendered;
3054
+ if (this.codeGenerator) {
3055
+ try {
3056
+ if (this.codeGenerator.generate(back) === this.codeGenerator.generate(source))
3057
+ return rendered;
3058
+ } catch {
3059
+ }
3060
+ }
3061
+ throw new TranslationReadBackError(rendered, from, to, source, back, difference);
3062
+ }
2959
3063
  hasGrammarProfile(language) {
2960
3064
  return this.profileProvider.getProfile(language) != null;
2961
3065
  }
@@ -2981,7 +3085,8 @@ var MultilingualDSLImpl = class {
2981
3085
  } catch (error) {
2982
3086
  return {
2983
3087
  ok: false,
2984
- errors: [error instanceof Error ? error.message : String(error)]
3088
+ errors: [error instanceof Error ? error.message : String(error)],
3089
+ ...error instanceof UnreadInputError ? { errorCode: error.code } : {}
2985
3090
  };
2986
3091
  }
2987
3092
  }
@@ -3908,6 +4013,8 @@ var DomainRegistry = class {
3908
4013
  return jsonResponse({ error: `Unknown operation: ${parsed.operation}` }, true);
3909
4014
  }
3910
4015
  } catch (error) {
4016
+ if (error instanceof UnreadInputError) return unreadInputResponse(error);
4017
+ if (error instanceof TranslationReadBackError) return readBackResponse(error);
3911
4018
  const message = error instanceof Error ? error.message : String(error);
3912
4019
  return jsonResponse({ error: `${descriptor.name} tool error: ${message}` }, true);
3913
4020
  }
@@ -4033,6 +4140,7 @@ var DomainRegistry = class {
4033
4140
  ok: result.ok,
4034
4141
  code: result.code,
4035
4142
  errors: result.errors,
4143
+ ...result.errorCode === "unread-input" ? { diagnostics: [UNREAD_INPUT(result.errors)] } : {},
4036
4144
  language,
4037
4145
  input
4038
4146
  });
@@ -4045,6 +4153,7 @@ var DomainRegistry = class {
4045
4153
  return jsonResponse({
4046
4154
  valid: result.valid,
4047
4155
  errors: result.errors,
4156
+ ...result.errorCode === "unread-input" ? { diagnostics: [UNREAD_INPUT(result.errors)] } : {},
4048
4157
  language,
4049
4158
  [descriptor.inputLabel]: input
4050
4159
  });
@@ -4075,7 +4184,24 @@ var DomainRegistry = class {
4075
4184
  for (const [key, value] of node.roles) {
4076
4185
  roles[key] = value;
4077
4186
  }
4187
+ if (rendered != null) {
4188
+ let back = null;
4189
+ let why = "";
4190
+ try {
4191
+ back = dsl.parse(rendered, to);
4192
+ why = readingDifference(node, back);
4193
+ if (why && compiled.ok) {
4194
+ const again = dsl.compile(rendered, to);
4195
+ if (again.ok && again.code === compiled.code) why = "";
4196
+ }
4197
+ } catch (error) {
4198
+ why = error instanceof Error ? error.message : String(error);
4199
+ }
4200
+ if (why)
4201
+ return readBackResponse(new TranslationReadBackError(rendered, from, to, node, back, why));
4202
+ }
4078
4203
  return jsonResponse({
4204
+ ok: true,
4079
4205
  input: { [descriptor.inputLabel]: input, language: from },
4080
4206
  ...rendered != null && { rendered: { text: rendered, language: to } },
4081
4207
  semantic: { action: node.action, roles },
@@ -4155,7 +4281,7 @@ function generateToolDefinitions(desc) {
4155
4281
  case "translate":
4156
4282
  tools.push({
4157
4283
  name: `translate_${desc.name}`,
4158
- description: `Translate ${desc.description} input between natural languages (requires 'from' and 'to' codes). Supports: ${langList}.`,
4284
+ description: `Translate ${desc.description} input between natural languages (requires 'from' and 'to' codes). Supports: ${langList}. A rendering that does not read back as the source is REFUSED: ok is false, the diagnostic is TRANSLATION_DOES_NOT_READ_BACK, and its text is under \`partial\`, never \`rendered\`: do not present it as the translation. Input left unread is refused as UNREAD_INPUT.`,
4159
4285
  inputSchema: {
4160
4286
  type: "object",
4161
4287
  properties: {
@@ -4189,6 +4315,46 @@ function getString(args, name, defaultValue = "") {
4189
4315
  const value = args[name];
4190
4316
  return typeof value === "string" ? value : defaultValue;
4191
4317
  }
4318
+ function readingOf(node) {
4319
+ const roles = {};
4320
+ for (const [key, value] of node.roles) roles[key] = value;
4321
+ return { action: node.action, roles };
4322
+ }
4323
+ var UNREAD_INPUT = (errors) => ({
4324
+ severity: "error",
4325
+ code: "UNREAD_INPUT",
4326
+ message: errors?.[0] ?? "part of the input was not read",
4327
+ suggestion: "Part of the input belongs to no role of the command it reads as: remove it, or quote a value of several words."
4328
+ });
4329
+ function unreadInputResponse(error) {
4330
+ return jsonResponse(
4331
+ {
4332
+ ok: false,
4333
+ unread: error.unread,
4334
+ reading: readingOf(error.reading),
4335
+ diagnostics: [UNREAD_INPUT([error.message])]
4336
+ },
4337
+ true
4338
+ );
4339
+ }
4340
+ function readBackResponse(error) {
4341
+ return jsonResponse(
4342
+ {
4343
+ ok: false,
4344
+ partial: error.partial,
4345
+ ...error.reading ? { reading: readingOf(error.reading) } : {},
4346
+ diagnostics: [
4347
+ {
4348
+ severity: "error",
4349
+ code: "TRANSLATION_DOES_NOT_READ_BACK",
4350
+ message: error.message,
4351
+ suggestion: "The rendering would not mean the source in the target language: do not present `partial` as the translation."
4352
+ }
4353
+ ]
4354
+ },
4355
+ true
4356
+ );
4357
+ }
4192
4358
  function jsonResponse(data, isError) {
4193
4359
  return {
4194
4360
  content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
@@ -6599,7 +6765,9 @@ export {
6599
6765
  PunctuationExtractor,
6600
6766
  StringLiteralExtractor,
6601
6767
  TokenStreamImpl,
6768
+ TranslationReadBackError,
6602
6769
  UnicodeIdentifierExtractor,
6770
+ UnreadInputError,
6603
6771
  WhitespaceExtractor,
6604
6772
  accumulateBlocks,
6605
6773
  buildDisambiguation,