@ai-react-markdown/engine 2.10.0 → 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,8 +5050,22 @@ function escapeLatexPipesInUnclosed(text) {
4988
5050
  const tail = text.substring(unclosedStart + delimLen);
4989
5051
  return before + delim + replaceUnescapedPipes(tail);
4990
5052
  }
4991
- function truncateUnclosedLatexBlock(text, unclosedStart = findUnclosedDelimiterStart(text, "double-only")) {
5053
+ function opensMathFlow(text, pos, runStartsAtLineStart) {
5054
+ let i = pos;
5055
+ let spaces = 0;
5056
+ while (i > 0) {
5057
+ const prev = text[i - 1];
5058
+ if (prev === "\n" || prev === "\r") return true;
5059
+ i -= 1;
5060
+ if (text[i] !== " ") return false;
5061
+ spaces += 1;
5062
+ if (spaces > 3) return false;
5063
+ }
5064
+ return runStartsAtLineStart;
5065
+ }
5066
+ function truncateUnclosedLatexBlock(text, runStartsAtLineStart, unclosedStart = findUnclosedDelimiterStart(text, "double-only")) {
4992
5067
  if (unclosedStart === -1) return text;
5068
+ if (!opensMathFlow(text, unclosedStart, runStartsAtLineStart)) return text;
4993
5069
  return text.substring(0, unclosedStart).trimEnd();
4994
5070
  }
4995
5071
  function escapeTextUnderscores(text) {
@@ -5034,7 +5110,8 @@ function convertSingleToDoubleDollar(text) {
5034
5110
  }
5035
5111
  function preprocessLaTeX(str) {
5036
5112
  if (!hasLatexTrigger(str)) return str;
5037
- 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;
5038
5115
  }
5039
5116
  function hasLatexTrigger(str) {
5040
5117
  return str.includes("$") || str.includes("\\[") || str.includes("\\(");
@@ -5063,42 +5140,240 @@ function hasUnclosedTextCommand(text) {
5063
5140
  }
5064
5141
  var RESIDUAL_OPEN_BRACKET_RE = /(?<!!)\\\[/;
5065
5142
  var LEADING_DOUBLE_DOLLAR_RE = /^\s*\$\$/;
5066
- 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) {
5067
5211
  const segments = splitByProtectedRegions(slice);
5068
5212
  const parts = [];
5069
5213
  let quiescent = true;
5070
5214
  let truncatedAtSeamStart = false;
5071
5215
  for (let index = 0; index < segments.length; index++) {
5072
5216
  const segment = segments[index];
5073
- if (segment.isCode) {
5217
+ if (segment.kind !== "text") {
5074
5218
  parts.push(segment.text);
5075
5219
  continue;
5076
5220
  }
5077
- let text = segment.text;
5078
- text = escapeMhchemCommands(text);
5079
- text = escapeCurrencyDollarSigns(text);
5080
- text = convertLatexDelimiters(text);
5081
- if (probe && RESIDUAL_OPEN_BRACKET_RE.test(text)) quiescent = false;
5082
- text = escapeLatexPipes(text);
5083
- if (probe && findUnclosedDelimiterStart(text, "both") !== -1) quiescent = false;
5084
- text = escapeLatexPipesInUnclosed(text);
5085
- if (probe && hasUnclosedTextCommand(text)) quiescent = false;
5086
- text = escapeTextUnderscores(text);
5087
- text = convertSingleToDoubleDollar(text);
5088
- let unclosedDouble;
5089
- if (probe || index === 0 && LEADING_DOUBLE_DOLLAR_RE.test(text)) {
5090
- unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
5091
- if (unclosedDouble !== -1) {
5092
- quiescent = false;
5093
- if (index === 0 && text.slice(0, unclosedDouble).trim() === "") {
5094
- 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 });
5095
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 });
5096
5268
  }
5269
+ continue;
5097
5270
  }
5098
- text = truncateUnclosedLatexBlock(text, unclosedDouble);
5099
- 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
+ );
5100
5330
  }
5101
- 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);
5102
5377
  }
5103
5378
  function isBlankRawLine(text, from, to) {
5104
5379
  for (let i = from; i < to; i++) {
@@ -5114,7 +5389,7 @@ function findRawSafeCut(active) {
5114
5389
  let backtickHazard = false;
5115
5390
  let latentLt = false;
5116
5391
  for (const segment of segments) {
5117
- if (segment.isCode) {
5392
+ if (segment.kind !== "text") {
5118
5393
  if (segment.text.includes(">")) latentLt = false;
5119
5394
  offset += segment.text.length;
5120
5395
  continue;
@@ -5149,12 +5424,28 @@ function createIncrementalLatexPreprocessor(options) {
5149
5424
  const freezeThreshold = options?.freezeThreshold ?? DEFAULT_FREEZE_ATTEMPT_THRESHOLD;
5150
5425
  const onAttempt = options?.onAttempt;
5151
5426
  const backoff = options?.backoff ?? true;
5427
+ const onDegrade = options?.onDegrade;
5152
5428
  let prevSource = "";
5153
5429
  let prevOutput = "";
5154
5430
  let frozenSrcEnd = 0;
5155
5431
  let frozenOut = "";
5156
5432
  let triggered = false;
5157
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
+ };
5158
5449
  return function incrementalPreprocessLaTeX(source) {
5159
5450
  if (source === prevSource) return prevOutput;
5160
5451
  const isAppend = source.length > prevSource.length && source.startsWith(prevSource);
@@ -5163,16 +5454,20 @@ function createIncrementalLatexPreprocessor(options) {
5163
5454
  frozenOut = "";
5164
5455
  triggered = false;
5165
5456
  nextAttemptLen = 0;
5457
+ lineageDegraded = false;
5458
+ presence.reset();
5459
+ presence.add(source, 0);
5460
+ } else {
5461
+ presence.add(source, prevSource.length);
5166
5462
  }
5167
5463
  if (!triggered) {
5168
5464
  const checkFrom = isAppend ? Math.max(0, prevSource.length - 1) : 0;
5169
- if (!hasLatexTrigger(source.slice(checkFrom))) {
5170
- prevSource = source;
5171
- prevOutput = source;
5172
- return source;
5173
- }
5465
+ if (!hasLatexTrigger(source.slice(checkFrom))) return commit(source, source);
5174
5466
  triggered = true;
5175
5467
  }
5468
+ if (lineageDegraded) return commit(source, legacyWhole(source));
5469
+ const mask = presence.select();
5470
+ if (mask === null) return degrade(source, "mask-exhausted");
5176
5471
  let active = source.slice(frozenSrcEnd);
5177
5472
  if (active.length > freezeThreshold && active.length >= nextAttemptLen) {
5178
5473
  const activeLength = active.length;
@@ -5187,18 +5482,20 @@ function createIncrementalLatexPreprocessor(options) {
5187
5482
  };
5188
5483
  const cut = findRawSafeCut(active);
5189
5484
  if (cut > 0) {
5190
- 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
+ }
5191
5490
  if (candidate.quiescent) freeze(cut, candidate);
5192
5491
  }
5193
5492
  nextAttemptLen = advanced || !backoff ? 0 : active.length * 2;
5194
5493
  onAttempt?.({ activeLength, frozenBytes });
5195
5494
  }
5196
- const tail = processSlice(active, false);
5495
+ const tail = processSlice(active, { probe: false, mask });
5496
+ if (tail.degradedReason !== null) return degrade(source, tail.degradedReason);
5197
5497
  const head = tail.truncatedAtSeamStart ? frozenOut.replace(/\s+$/, "") : frozenOut;
5198
- const out = head + tail.out;
5199
- prevSource = source;
5200
- prevOutput = out;
5201
- return out;
5498
+ return commit(source, head + tail.out);
5202
5499
  };
5203
5500
  }
5204
5501
 
@@ -5224,6 +5521,8 @@ function createRemendPreprocessor(options) {
5224
5521
  // Annotate the CommonJS export names for ESM import in node:
5225
5522
  0 && (module.exports = {
5226
5523
  DEFAULT_PAYLOAD,
5524
+ ENGINE_PLACEHOLDER_TAGS,
5525
+ ENGINE_PROVENANCE_PROPERTY,
5227
5526
  PIPELINE_STAGES,
5228
5527
  SENTINEL_FN_CONTENT,
5229
5528
  SENTINEL_LINK_URL,
@@ -5268,6 +5567,7 @@ function createRemendPreprocessor(options) {
5268
5567
  preprocessLaTeX,
5269
5568
  rehypeFooterAdorn,
5270
5569
  rehypeRebaseHashLinks,
5570
+ rehypeVerifyEngineTags,
5271
5571
  removeComments,
5272
5572
  sanitizeCrossChunkUrl,
5273
5573
  sanitizeSchema,