@ai-react-markdown/engine 2.10.1 → 2.11.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/README.md CHANGED
@@ -90,7 +90,7 @@ for (const frame of ['# Hello', '# Hello\n\nworld', '# Hello\n\nworld and more']
90
90
 
91
91
  ## Verification
92
92
 
93
- The incremental engine ships with a five-layer equivalence stack (fixture pins, fuzz arbiter, direction battery, exhaustive census, arbiter-sensitivity meta-suite) plus a five-leg release-gate soak (`scripts/soak/fiveleg.sh`, from the repo root, with a fresh seed base); the full record lives in `src/experiments/prefixFreeze/README.md`. Every reachable divergence found so far is pinned as a deterministic test.
93
+ The incremental engine ships with a five-layer equivalence stack (fixture pins, fuzz arbiter, direction battery, exhaustive census, arbiter-sensitivity meta-suite) plus a six-leg release-gate soak (`scripts/soak/soak.sh`, from the repo root, with a fresh seed base); the full record lives in `src/experiments/prefixFreeze/README.md`. Every reachable divergence found so far is pinned as a deterministic test.
94
94
 
95
95
  ## Runtime support
96
96
 
package/dist/index.cjs CHANGED
@@ -31,6 +31,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  DEFAULT_PAYLOAD: () => DEFAULT_PAYLOAD,
34
+ ENGINE_PLACEHOLDER_TAGS: () => ENGINE_PLACEHOLDER_TAGS,
35
+ ENGINE_PROVENANCE_PROPERTY: () => ENGINE_PROVENANCE_PROPERTY,
34
36
  PIPELINE_STAGES: () => PIPELINE_STAGES,
35
37
  SENTINEL_FN_CONTENT: () => SENTINEL_FN_CONTENT,
36
38
  SENTINEL_LINK_URL: () => SENTINEL_LINK_URL,
@@ -75,6 +77,7 @@ __export(src_exports, {
75
77
  preprocessLaTeX: () => preprocessLaTeX,
76
78
  rehypeFooterAdorn: () => rehypeFooterAdorn,
77
79
  rehypeRebaseHashLinks: () => rehypeRebaseHashLinks_default,
80
+ rehypeVerifyEngineTags: () => rehypeVerifyEngineTags,
78
81
  removeComments: () => removeComments,
79
82
  sanitizeCrossChunkUrl: () => sanitizeCrossChunkUrl,
80
83
  sanitizeSchema: () => sanitizeSchema,
@@ -2790,6 +2793,44 @@ function rehypeUnwrapCrossChunkImages() {
2790
2793
 
2791
2794
  // src/components/pluginChain.ts
2792
2795
  var import_rehype_sanitize = __toESM(require("rehype-sanitize"), 1);
2796
+
2797
+ // src/components/rehypeVerifyEngineTags.ts
2798
+ var ENGINE_PLACEHOLDER_TAGS = /* @__PURE__ */ new Set([
2799
+ "footnote-sup",
2800
+ "cross-chunk-link",
2801
+ "cross-chunk-image"
2802
+ ]);
2803
+ var ENGINE_PROVENANCE_PROPERTY = "engineProvenance";
2804
+ function walk(parent, provenance) {
2805
+ const children = parent.children;
2806
+ let i = 0;
2807
+ while (i < children.length) {
2808
+ const node = children[i];
2809
+ if (node.type === "element" && ENGINE_PLACEHOLDER_TAGS.has(node.tagName)) {
2810
+ const props = node.properties ?? {};
2811
+ const stamped = props[ENGINE_PROVENANCE_PROPERTY];
2812
+ const genuine = provenance !== "" && typeof stamped === "string" && stamped === provenance;
2813
+ if (genuine) {
2814
+ delete props[ENGINE_PROVENANCE_PROPERTY];
2815
+ walk(node, provenance);
2816
+ i += 1;
2817
+ } else {
2818
+ children.splice(i, 1, ...node.children);
2819
+ }
2820
+ continue;
2821
+ }
2822
+ if (node.type === "element") walk(node, provenance);
2823
+ i += 1;
2824
+ }
2825
+ }
2826
+ function rehypeVerifyEngineTags(options) {
2827
+ const provenance = options?.provenance ?? "";
2828
+ return function transformer(tree) {
2829
+ walk(tree, provenance);
2830
+ };
2831
+ }
2832
+
2833
+ // src/components/pluginChain.ts
2793
2834
  var import_remark_breaks = __toESM(require("remark-breaks"), 1);
2794
2835
  var import_remark_cjk_friendly = __toESM(require("remark-cjk-friendly"), 1);
2795
2836
  var import_remark_cjk_friendly_gfm_strikethrough = __toESM(require("remark-cjk-friendly-gfm-strikethrough"), 1);
@@ -2895,10 +2936,13 @@ function buildCoreRemarkPlugins(enginePlugins) {
2895
2936
  ...DISPLAY_OPTIMIZE_CHAIN.filter(([name]) => selected.has(name)).map(([, plugin]) => plugin)
2896
2937
  ];
2897
2938
  }
2898
- function buildCoreRehypePlugins(sanitizeSchema2, clobberPrefix) {
2939
+ function buildCoreRehypePlugins(sanitizeSchema2, clobberPrefix, options) {
2899
2940
  return [
2900
2941
  // Allow raw HTML through so rehype-sanitize can handle it.
2901
2942
  [import_rehype_raw.default, { passThrough: [] }],
2943
+ // Unwrap forged engine placeholders BEFORE sanitize admits their tag
2944
+ // names. Only when the caller holds a credential (see the option's doc).
2945
+ ...options ? [[rehypeVerifyEngineTags, { provenance: options.provenance }]] : [],
2902
2946
  // Sanitize HTML while allowing <mark> (highlight), KaTeX class names,
2903
2947
  // and any extra protocols the caller permitted via the `sanitizeSchema`
2904
2948
  // prop. Override `clobberPrefix` with the instance-scoped value — the
@@ -2938,6 +2982,10 @@ function buildCoreRemarkRehypeOptions(enableDefinitionList) {
2938
2982
  }
2939
2983
 
2940
2984
  // src/components/customMdastHandlers.ts
2985
+ function provenanceProps(s) {
2986
+ const provenance = s.options.provenance;
2987
+ return typeof provenance === "string" ? { engineProvenance: provenance } : {};
2988
+ }
2941
2989
  function localDefProps(s, id) {
2942
2990
  const def = s.definitionById.get(id);
2943
2991
  if (!def || typeof def.url !== "string" || def.url === SENTINEL_LINK_URL) return {};
@@ -2976,7 +3024,8 @@ function buildCrossChunkHandlers() {
2976
3024
  label: node.label ?? node.identifier,
2977
3025
  referenceType: node.referenceType,
2978
3026
  documentId: s.options.documentId,
2979
- ...localDefProps(s, id)
3027
+ ...localDefProps(s, id),
3028
+ ...provenanceProps(s)
2980
3029
  },
2981
3030
  children: s.all(node)
2982
3031
  };
@@ -2994,7 +3043,8 @@ function buildCrossChunkHandlers() {
2994
3043
  referenceType: node.referenceType,
2995
3044
  alt: node.alt ?? "",
2996
3045
  documentId: s.options.documentId,
2997
- ...localDefProps(s, id)
3046
+ ...localDefProps(s, id),
3047
+ ...provenanceProps(s)
2998
3048
  },
2999
3049
  children: []
3000
3050
  };
@@ -3011,7 +3061,8 @@ function buildCrossChunkHandlers() {
3011
3061
  properties: {
3012
3062
  label: node.identifier,
3013
3063
  localOccurrence,
3014
- documentId: s.options.documentId
3064
+ documentId: s.options.documentId,
3065
+ ...provenanceProps(s)
3015
3066
  },
3016
3067
  children: []
3017
3068
  };
@@ -3029,7 +3080,8 @@ function buildCrossChunkHandlers() {
3029
3080
  // first client frame), where the local synthetic footer is what
3030
3081
  // renders, so marks and footer agree (core-render-02).
3031
3082
  localNumber: s.footnoteOrder.indexOf(id) + 1,
3032
- documentId: s.options.documentId
3083
+ documentId: s.options.documentId,
3084
+ ...provenanceProps(s)
3033
3085
  },
3034
3086
  children: []
3035
3087
  };
@@ -4689,6 +4741,12 @@ var createSmoothStreamController = (options = {}) => {
4689
4741
  };
4690
4742
 
4691
4743
  // src/preprocessors/latex.ts
4744
+ function isHardBoundary(kind) {
4745
+ return kind === "code" || kind === "literal" || kind === "multilineTag";
4746
+ }
4747
+ function hasLineEnding(text) {
4748
+ return text.includes("\n") || text.includes("\r");
4749
+ }
4692
4750
  function getRepeatedMarkerLength(content, start, marker) {
4693
4751
  let end = start;
4694
4752
  while (end < content.length && content[end] === marker) {
@@ -4756,11 +4814,11 @@ function splitByProtectedRegions(content) {
4756
4814
  let multilineFenceMarker = null;
4757
4815
  let multilineFenceLength = 0;
4758
4816
  let multilineFenceIndent = 0;
4759
- function pushProtected(start, end) {
4817
+ function pushProtected(start, end, kind) {
4760
4818
  if (start > lastIndex) {
4761
- segments.push({ text: content.substring(lastIndex, start), isCode: false });
4819
+ segments.push({ kind: "text", text: content.substring(lastIndex, start) });
4762
4820
  }
4763
- segments.push({ text: content.substring(start, end), isCode: true });
4821
+ segments.push({ kind, text: content.substring(start, end) });
4764
4822
  lastIndex = end;
4765
4823
  }
4766
4824
  let i = 0;
@@ -4771,7 +4829,7 @@ function splitByProtectedRegions(content) {
4771
4829
  const runLen = getRepeatedMarkerLength(content, i, multilineFenceMarker);
4772
4830
  const closerIndent = lineIndentBefore(content, i);
4773
4831
  if (runLen >= multilineFenceLength && closerIndent !== -1 && closerIndent <= multilineFenceIndent + 3 && restOfLineIsBlank(content, i + runLen)) {
4774
- pushProtected(multilineStart, i + runLen);
4832
+ pushProtected(multilineStart, i + runLen, "code");
4775
4833
  multilineStart = -1;
4776
4834
  multilineFenceMarker = null;
4777
4835
  multilineFenceLength = 0;
@@ -4798,7 +4856,7 @@ function splitByProtectedRegions(content) {
4798
4856
  if (char === "`") {
4799
4857
  const closeIdx = findClosingBacktickRun(content, i + runLen, runLen);
4800
4858
  if (closeIdx !== -1) {
4801
- pushProtected(i, closeIdx + runLen);
4859
+ pushProtected(i, closeIdx + runLen, "code");
4802
4860
  i = closeIdx + runLen;
4803
4861
  continue;
4804
4862
  }
@@ -4823,7 +4881,11 @@ function splitByProtectedRegions(content) {
4823
4881
  endIndex = content.length;
4824
4882
  }
4825
4883
  }
4826
- pushProtected(i, endIndex);
4884
+ pushProtected(
4885
+ i,
4886
+ endIndex,
4887
+ isOpeningPairedTag ? "literal" : hasLineEnding(content.substring(i, endIndex)) ? "multilineTag" : "tag"
4888
+ );
4827
4889
  i = endIndex;
4828
4890
  continue;
4829
4891
  }
@@ -4831,10 +4893,10 @@ function splitByProtectedRegions(content) {
4831
4893
  i += 1;
4832
4894
  }
4833
4895
  if (multilineStart !== -1) {
4834
- pushProtected(multilineStart, content.length);
4896
+ pushProtected(multilineStart, content.length, "code");
4835
4897
  }
4836
4898
  if (lastIndex < content.length) {
4837
- segments.push({ text: content.substring(lastIndex), isCode: false });
4899
+ segments.push({ kind: "text", text: content.substring(lastIndex) });
4838
4900
  }
4839
4901
  return segments;
4840
4902
  }
@@ -4988,20 +5050,22 @@ function escapeLatexPipesInUnclosed(text) {
4988
5050
  const tail = text.substring(unclosedStart + delimLen);
4989
5051
  return before + delim + replaceUnescapedPipes(tail);
4990
5052
  }
4991
- function opensMathFlow(text, pos) {
5053
+ function opensMathFlow(text, pos, runStartsAtLineStart) {
4992
5054
  let i = pos;
4993
5055
  let spaces = 0;
4994
- while (i > 0 && text[i - 1] !== "\n") {
5056
+ while (i > 0) {
5057
+ const prev = text[i - 1];
5058
+ if (prev === "\n" || prev === "\r") return true;
4995
5059
  i -= 1;
4996
5060
  if (text[i] !== " ") return false;
4997
5061
  spaces += 1;
4998
5062
  if (spaces > 3) return false;
4999
5063
  }
5000
- return true;
5064
+ return runStartsAtLineStart;
5001
5065
  }
5002
- function truncateUnclosedLatexBlock(text, unclosedStart = findUnclosedDelimiterStart(text, "double-only")) {
5066
+ function truncateUnclosedLatexBlock(text, runStartsAtLineStart, unclosedStart = findUnclosedDelimiterStart(text, "double-only")) {
5003
5067
  if (unclosedStart === -1) return text;
5004
- if (!opensMathFlow(text, unclosedStart)) return text;
5068
+ if (!opensMathFlow(text, unclosedStart, runStartsAtLineStart)) return text;
5005
5069
  return text.substring(0, unclosedStart).trimEnd();
5006
5070
  }
5007
5071
  function escapeTextUnderscores(text) {
@@ -5046,7 +5110,8 @@ function convertSingleToDoubleDollar(text) {
5046
5110
  }
5047
5111
  function preprocessLaTeX(str) {
5048
5112
  if (!hasLatexTrigger(str)) return str;
5049
- return processSlice(str, false).out;
5113
+ const mask = selectMask(str);
5114
+ return (mask === null ? processSlice(str, { legacy: true, probe: false }) : processSlice(str, { probe: false, mask })).out;
5050
5115
  }
5051
5116
  function hasLatexTrigger(str) {
5052
5117
  return str.includes("$") || str.includes("\\[") || str.includes("\\(");
@@ -5075,42 +5140,240 @@ function hasUnclosedTextCommand(text) {
5075
5140
  }
5076
5141
  var RESIDUAL_OPEN_BRACKET_RE = /(?<!!)\\\[/;
5077
5142
  var LEADING_DOUBLE_DOLLAR_RE = /^\s*\$\$/;
5078
- function processSlice(slice, probe = true) {
5143
+ var PUA_START = 57344;
5144
+ var PUA_END = 63743;
5145
+ var PUA_SIZE = PUA_END - PUA_START + 1;
5146
+ function selectMask(source) {
5147
+ const first = String.fromCharCode(PUA_START);
5148
+ if (source.indexOf(first) === -1) return first;
5149
+ const seen = new Uint8Array(PUA_SIZE);
5150
+ for (let i = 0; i < source.length; i++) {
5151
+ const c = source.charCodeAt(i);
5152
+ if (c >= PUA_START && c <= PUA_END) seen[c - PUA_START] = 1;
5153
+ }
5154
+ for (let k = 0; k < PUA_SIZE; k++) if (seen[k] === 0) return String.fromCharCode(PUA_START + k);
5155
+ return null;
5156
+ }
5157
+ var PuaPresence = class {
5158
+ bits = new Uint8Array(PUA_SIZE);
5159
+ distinct = 0;
5160
+ reset() {
5161
+ this.bits.fill(0);
5162
+ this.distinct = 0;
5163
+ }
5164
+ add(text, from) {
5165
+ for (let i = from; i < text.length; i++) {
5166
+ const c = text.charCodeAt(i);
5167
+ if (c >= PUA_START && c <= PUA_END) {
5168
+ const k = c - PUA_START;
5169
+ if (this.bits[k] === 0) {
5170
+ this.bits[k] = 1;
5171
+ this.distinct += 1;
5172
+ }
5173
+ }
5174
+ }
5175
+ }
5176
+ select() {
5177
+ if (this.distinct === 0) return String.fromCharCode(PUA_START);
5178
+ if (this.distinct >= PUA_SIZE) return null;
5179
+ for (let k = 0; k < PUA_SIZE; k++) if (this.bits[k] === 0) return String.fromCharCode(PUA_START + k);
5180
+ return null;
5181
+ }
5182
+ };
5183
+ function transformRun(input, probe, runStartsAtLineStart, seamEligible) {
5184
+ let text = input;
5185
+ let tailSensitive = false;
5186
+ let truncatedAtSeamStart = false;
5187
+ text = escapeMhchemCommands(text);
5188
+ text = escapeCurrencyDollarSigns(text);
5189
+ text = convertLatexDelimiters(text);
5190
+ if (probe && RESIDUAL_OPEN_BRACKET_RE.test(text)) tailSensitive = true;
5191
+ text = escapeLatexPipes(text);
5192
+ if (probe && findUnclosedDelimiterStart(text, "both") !== -1) tailSensitive = true;
5193
+ text = escapeLatexPipesInUnclosed(text);
5194
+ if (probe && hasUnclosedTextCommand(text)) tailSensitive = true;
5195
+ text = escapeTextUnderscores(text);
5196
+ text = convertSingleToDoubleDollar(text);
5197
+ let unclosedDouble;
5198
+ if (probe || seamEligible && LEADING_DOUBLE_DOLLAR_RE.test(text)) {
5199
+ unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
5200
+ if (unclosedDouble !== -1) {
5201
+ tailSensitive = true;
5202
+ if (seamEligible && opensMathFlow(text, unclosedDouble, runStartsAtLineStart) && text.slice(0, unclosedDouble).trim() === "") {
5203
+ truncatedAtSeamStart = true;
5204
+ }
5205
+ }
5206
+ }
5207
+ text = truncateUnclosedLatexBlock(text, runStartsAtLineStart, unclosedDouble);
5208
+ return { out: text, tailSensitive, truncatedAtSeamStart };
5209
+ }
5210
+ function processSliceLegacy(slice, probe) {
5079
5211
  const segments = splitByProtectedRegions(slice);
5080
5212
  const parts = [];
5081
5213
  let quiescent = true;
5082
5214
  let truncatedAtSeamStart = false;
5083
5215
  for (let index = 0; index < segments.length; index++) {
5084
5216
  const segment = segments[index];
5085
- if (segment.isCode) {
5217
+ if (segment.kind !== "text") {
5086
5218
  parts.push(segment.text);
5087
5219
  continue;
5088
5220
  }
5089
- let text = segment.text;
5090
- text = escapeMhchemCommands(text);
5091
- text = escapeCurrencyDollarSigns(text);
5092
- text = convertLatexDelimiters(text);
5093
- if (probe && RESIDUAL_OPEN_BRACKET_RE.test(text)) quiescent = false;
5094
- text = escapeLatexPipes(text);
5095
- if (probe && findUnclosedDelimiterStart(text, "both") !== -1) quiescent = false;
5096
- text = escapeLatexPipesInUnclosed(text);
5097
- if (probe && hasUnclosedTextCommand(text)) quiescent = false;
5098
- text = escapeTextUnderscores(text);
5099
- text = convertSingleToDoubleDollar(text);
5100
- let unclosedDouble;
5101
- if (probe || index === 0 && LEADING_DOUBLE_DOLLAR_RE.test(text)) {
5102
- unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
5103
- if (unclosedDouble !== -1) {
5104
- quiescent = false;
5105
- if (index === 0 && opensMathFlow(text, unclosedDouble) && text.slice(0, unclosedDouble).trim() === "") {
5106
- truncatedAtSeamStart = true;
5221
+ const r = transformRun(segment.text, probe, true, index === 0);
5222
+ if (r.tailSensitive) quiescent = false;
5223
+ if (r.truncatedAtSeamStart) truncatedAtSeamStart = true;
5224
+ parts.push(r.out);
5225
+ }
5226
+ return { out: parts.join(""), quiescent, truncatedAtSeamStart, degradedReason: null };
5227
+ }
5228
+ var SCOPE_DEPTH_CAP = 8;
5229
+ var VOID_TAGS2 = /* @__PURE__ */ new Set(["br", "hr", "img", "wbr", "input", "source"]);
5230
+ var TAG_NAME_RE = /^<(\/?)([A-Za-z][A-Za-z0-9]*)/;
5231
+ function tagInfo(tag) {
5232
+ const m = TAG_NAME_RE.exec(tag);
5233
+ const closing = m?.[1] === "/";
5234
+ const name = (m?.[2] ?? "").toLowerCase();
5235
+ const opensScope = !closing && !tag.endsWith("/>") && !VOID_TAGS2.has(name);
5236
+ return { name, closing, opensScope };
5237
+ }
5238
+ function buildRunTree(segments) {
5239
+ const root2 = [];
5240
+ const stack = [];
5241
+ const current = () => stack.length > 0 ? stack[stack.length - 1].children : root2;
5242
+ const unwind = () => {
5243
+ while (stack.length > 0) {
5244
+ const frame = stack.pop();
5245
+ current().push({ type: "atom", text: frame.open }, ...frame.children);
5246
+ }
5247
+ };
5248
+ for (const segment of segments) {
5249
+ if (segment.kind === "tag") {
5250
+ const info = tagInfo(segment.text);
5251
+ if (info.closing) {
5252
+ const top = stack[stack.length - 1];
5253
+ if (top !== void 0 && top.name === info.name) {
5254
+ stack.pop();
5255
+ const parent = current();
5256
+ if (top.suppressed) {
5257
+ parent.push({ type: "atom", text: top.open }, ...top.children, { type: "atom", text: segment.text });
5258
+ } else {
5259
+ parent.push({ type: "scope", open: top.open, close: segment.text, children: top.children });
5260
+ }
5261
+ } else {
5262
+ current().push({ type: "atom", text: segment.text });
5107
5263
  }
5264
+ } else if (info.opensScope) {
5265
+ stack.push({ name: info.name, open: segment.text, suppressed: stack.length >= SCOPE_DEPTH_CAP, children: [] });
5266
+ } else {
5267
+ current().push({ type: "atom", text: segment.text });
5108
5268
  }
5269
+ continue;
5109
5270
  }
5110
- text = truncateUnclosedLatexBlock(text, unclosedDouble);
5111
- parts.push(text);
5271
+ const t = segment.text;
5272
+ let start = 0;
5273
+ for (let i = 0; i < t.length; i++) {
5274
+ const c = t.charCodeAt(i);
5275
+ if (c !== 10 && c !== 13) continue;
5276
+ if (i > start) current().push({ type: "text", text: t.slice(start, i) });
5277
+ const end = c === 13 && t.charCodeAt(i + 1) === 10 ? i + 2 : i + 1;
5278
+ unwind();
5279
+ root2.push({ type: "text", text: t.slice(i, end) });
5280
+ start = end;
5281
+ i = end - 1;
5282
+ }
5283
+ if (start < t.length) current().push({ type: "text", text: t.slice(start) });
5284
+ }
5285
+ unwind();
5286
+ return root2;
5287
+ }
5288
+ var restoreFailureInjector = null;
5289
+ function restore(out, atoms, mask) {
5290
+ if (restoreFailureInjector !== null && restoreFailureInjector(atoms)) return null;
5291
+ let result = "";
5292
+ let k = 0;
5293
+ let last = 0;
5294
+ for (; ; ) {
5295
+ const idx = out.indexOf(mask, last);
5296
+ if (idx === -1) break;
5297
+ if (k >= atoms.length) return null;
5298
+ result += out.slice(last, idx) + atoms[k];
5299
+ k += 1;
5300
+ last = idx + 1;
5301
+ }
5302
+ return result + out.slice(last);
5303
+ }
5304
+ function emitRun(nodes, mask) {
5305
+ let text = "";
5306
+ const atoms = [];
5307
+ for (const node of nodes) {
5308
+ if (node.type === "text") {
5309
+ text += node.text;
5310
+ } else if (node.type === "atom") {
5311
+ atoms.push(node.text);
5312
+ text += mask;
5313
+ } else {
5314
+ const inner = emitRun(node.children, mask);
5315
+ if (inner === null) return null;
5316
+ const transformed = transformRun(inner.text, false, false, false);
5317
+ const restored = restore(transformed.out, inner.atoms, mask);
5318
+ if (restored === null) return null;
5319
+ atoms.push(node.open + restored + node.close);
5320
+ text += mask;
5321
+ }
5322
+ }
5323
+ return { text, atoms };
5324
+ }
5325
+ function reportRestoreViolation() {
5326
+ if (false) {
5327
+ console.error(
5328
+ "[ai-react-markdown] LaTeX preprocessor: atom restoration violated its invariant (more masks in the output than atoms); the slice was re-processed on the legacy path. This is an engine defect \u2014 please report it."
5329
+ );
5112
5330
  }
5113
- return { out: parts.join(""), quiescent, truncatedAtSeamStart };
5331
+ }
5332
+ function processSliceDefault(slice, probe, mask) {
5333
+ const segments = splitByProtectedRegions(slice);
5334
+ const parts = [];
5335
+ let quiescent = true;
5336
+ let truncatedAtSeamStart = false;
5337
+ let offset = 0;
5338
+ let i = 0;
5339
+ while (i < segments.length) {
5340
+ const segment = segments[i];
5341
+ if (isHardBoundary(segment.kind)) {
5342
+ parts.push(segment.text);
5343
+ offset += segment.text.length;
5344
+ i += 1;
5345
+ continue;
5346
+ }
5347
+ const runStart = offset;
5348
+ const runSegments = [];
5349
+ while (i < segments.length && !isHardBoundary(segments[i].kind)) {
5350
+ runSegments.push(segments[i]);
5351
+ offset += segments[i].text.length;
5352
+ i += 1;
5353
+ }
5354
+ const before = runStart === 0 ? -1 : slice.charCodeAt(runStart - 1);
5355
+ const runStartsAtLineStart = before === -1 || before === 10 || before === 13;
5356
+ const seamEligible = runStart === 0;
5357
+ const emitted = emitRun(buildRunTree(runSegments), mask);
5358
+ if (emitted === null) {
5359
+ reportRestoreViolation();
5360
+ return { ...processSliceLegacy(slice, probe), degradedReason: "restore-invariant" };
5361
+ }
5362
+ const r = transformRun(emitted.text, probe, runStartsAtLineStart, seamEligible);
5363
+ const restored = restore(r.out, emitted.atoms, mask);
5364
+ if (restored === null) {
5365
+ reportRestoreViolation();
5366
+ return { ...processSliceLegacy(slice, probe), degradedReason: "restore-invariant" };
5367
+ }
5368
+ if (r.tailSensitive) quiescent = false;
5369
+ if (r.truncatedAtSeamStart) truncatedAtSeamStart = true;
5370
+ parts.push(restored);
5371
+ }
5372
+ return { out: parts.join(""), quiescent, truncatedAtSeamStart, degradedReason: null };
5373
+ }
5374
+ function processSlice(slice, options) {
5375
+ if (options.legacy === true) return processSliceLegacy(slice, options.probe);
5376
+ return processSliceDefault(slice, options.probe, options.mask);
5114
5377
  }
5115
5378
  function isBlankRawLine(text, from, to) {
5116
5379
  for (let i = from; i < to; i++) {
@@ -5126,7 +5389,7 @@ function findRawSafeCut(active) {
5126
5389
  let backtickHazard = false;
5127
5390
  let latentLt = false;
5128
5391
  for (const segment of segments) {
5129
- if (segment.isCode) {
5392
+ if (segment.kind !== "text") {
5130
5393
  if (segment.text.includes(">")) latentLt = false;
5131
5394
  offset += segment.text.length;
5132
5395
  continue;
@@ -5161,12 +5424,28 @@ function createIncrementalLatexPreprocessor(options) {
5161
5424
  const freezeThreshold = options?.freezeThreshold ?? DEFAULT_FREEZE_ATTEMPT_THRESHOLD;
5162
5425
  const onAttempt = options?.onAttempt;
5163
5426
  const backoff = options?.backoff ?? true;
5427
+ const onDegrade = options?.onDegrade;
5164
5428
  let prevSource = "";
5165
5429
  let prevOutput = "";
5166
5430
  let frozenSrcEnd = 0;
5167
5431
  let frozenOut = "";
5168
5432
  let triggered = false;
5169
5433
  let nextAttemptLen = 0;
5434
+ let lineageDegraded = false;
5435
+ const presence = new PuaPresence();
5436
+ const commit = (source, out) => {
5437
+ prevSource = source;
5438
+ prevOutput = out;
5439
+ return out;
5440
+ };
5441
+ const legacyWhole = (source) => processSlice(source, { legacy: true, probe: false }).out;
5442
+ const degrade = (source, reason) => {
5443
+ lineageDegraded = true;
5444
+ frozenSrcEnd = 0;
5445
+ frozenOut = "";
5446
+ onDegrade?.(reason);
5447
+ return commit(source, legacyWhole(source));
5448
+ };
5170
5449
  return function incrementalPreprocessLaTeX(source) {
5171
5450
  if (source === prevSource) return prevOutput;
5172
5451
  const isAppend = source.length > prevSource.length && source.startsWith(prevSource);
@@ -5175,16 +5454,20 @@ function createIncrementalLatexPreprocessor(options) {
5175
5454
  frozenOut = "";
5176
5455
  triggered = false;
5177
5456
  nextAttemptLen = 0;
5457
+ lineageDegraded = false;
5458
+ presence.reset();
5459
+ presence.add(source, 0);
5460
+ } else {
5461
+ presence.add(source, prevSource.length);
5178
5462
  }
5179
5463
  if (!triggered) {
5180
5464
  const checkFrom = isAppend ? Math.max(0, prevSource.length - 1) : 0;
5181
- if (!hasLatexTrigger(source.slice(checkFrom))) {
5182
- prevSource = source;
5183
- prevOutput = source;
5184
- return source;
5185
- }
5465
+ if (!hasLatexTrigger(source.slice(checkFrom))) return commit(source, source);
5186
5466
  triggered = true;
5187
5467
  }
5468
+ if (lineageDegraded) return commit(source, legacyWhole(source));
5469
+ const mask = presence.select();
5470
+ if (mask === null) return degrade(source, "mask-exhausted");
5188
5471
  let active = source.slice(frozenSrcEnd);
5189
5472
  if (active.length > freezeThreshold && active.length >= nextAttemptLen) {
5190
5473
  const activeLength = active.length;
@@ -5199,18 +5482,20 @@ function createIncrementalLatexPreprocessor(options) {
5199
5482
  };
5200
5483
  const cut = findRawSafeCut(active);
5201
5484
  if (cut > 0) {
5202
- const candidate = processSlice(active.slice(0, cut));
5485
+ const candidate = processSlice(active.slice(0, cut), { probe: true, mask });
5486
+ if (candidate.degradedReason !== null) {
5487
+ onAttempt?.({ activeLength, frozenBytes: 0 });
5488
+ return degrade(source, candidate.degradedReason);
5489
+ }
5203
5490
  if (candidate.quiescent) freeze(cut, candidate);
5204
5491
  }
5205
5492
  nextAttemptLen = advanced || !backoff ? 0 : active.length * 2;
5206
5493
  onAttempt?.({ activeLength, frozenBytes });
5207
5494
  }
5208
- const tail = processSlice(active, false);
5495
+ const tail = processSlice(active, { probe: false, mask });
5496
+ if (tail.degradedReason !== null) return degrade(source, tail.degradedReason);
5209
5497
  const head = tail.truncatedAtSeamStart ? frozenOut.replace(/\s+$/, "") : frozenOut;
5210
- const out = head + tail.out;
5211
- prevSource = source;
5212
- prevOutput = out;
5213
- return out;
5498
+ return commit(source, head + tail.out);
5214
5499
  };
5215
5500
  }
5216
5501
 
@@ -5236,6 +5521,8 @@ function createRemendPreprocessor(options) {
5236
5521
  // Annotate the CommonJS export names for ESM import in node:
5237
5522
  0 && (module.exports = {
5238
5523
  DEFAULT_PAYLOAD,
5524
+ ENGINE_PLACEHOLDER_TAGS,
5525
+ ENGINE_PROVENANCE_PROPERTY,
5239
5526
  PIPELINE_STAGES,
5240
5527
  SENTINEL_FN_CONTENT,
5241
5528
  SENTINEL_LINK_URL,
@@ -5280,6 +5567,7 @@ function createRemendPreprocessor(options) {
5280
5567
  preprocessLaTeX,
5281
5568
  rehypeFooterAdorn,
5282
5569
  rehypeRebaseHashLinks,
5570
+ rehypeVerifyEngineTags,
5283
5571
  removeComments,
5284
5572
  sanitizeCrossChunkUrl,
5285
5573
  sanitizeSchema,