@superdoc/mcp 1.2.0-next.1 → 1.2.0-next.10

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 (2) hide show
  1. package/dist/index.js +374 -84
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -31314,7 +31314,7 @@ var init_host = __esm(() => {
31314
31314
  });
31315
31315
 
31316
31316
  // ../../packages/sdk/langs/node/src/runtime/sdk-version.generated.ts
31317
- var SDK_VERSION = "2.15.0-next.1";
31317
+ var SDK_VERSION = "2.15.0-next.10";
31318
31318
 
31319
31319
  // ../../packages/sdk/langs/node/src/runtime/embedded-platform.ts
31320
31320
  import { chmodSync, existsSync } from "node:fs";
@@ -31891,7 +31891,7 @@ var init_embedded_tools_generated = __esm(() => {
31891
31891
  "tools": [
31892
31892
  {
31893
31893
  "toolName": "superdoc_get_content",
31894
- "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \\"blocks\\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \\"blocks\\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \\"text\\", \\"markdown\\", and \\"html\\" return compact strings. Actions \\"markdown_projection\\" and \\"html_projection\\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \\"info\\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \\"blocks\\" action supports pagination via \\"offset\\" and \\"limit\\", filtering via \\"nodeTypes\\", and numbering projection via \\"reviewMode\\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"blocks\\"}\\n 2. {\\"action\\":\\"blocks\\",\\"includeText\\":true,\\"offset\\":0,\\"limit\\":20,\\"reviewMode\\":\\"final\\"}\\n 3. {\\"action\\":\\"blocks\\",\\"offset\\":0,\\"limit\\":20,\\"nodeTypes\\":[\\"heading\\",\\"paragraph\\"]}\\n 4. {\\"action\\":\\"text\\"}\\n 5. {\\"action\\":\\"info\\"}\\n 6. {\\"action\\":\\"html_projection\\",\\"reviewMode\\":\\"redline\\",\\"includeSourceMap\\":true}\\n 7. {\\"action\\":\\"markdown_projection\\",\\"reviewMode\\":\\"original\\",\\"scope\\":{\\"kind\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"paragraph-id\\"},\\"includeSourceMap\\":true}",
31894
+ "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \\"blocks\\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Paragraph-class blocks include tabCount, the number of U+0009 tabs in the complete text even if the 80-character preview trims or truncates them. If tabCount is positive, read includeText:true before editing; do not treat a preview space as proof that there is no tab. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \\"blocks\\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \\"text\\", \\"markdown\\", and \\"html\\" return compact strings. Actions \\"markdown_projection\\" and \\"html_projection\\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \\"info\\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \\"blocks\\" action supports pagination via \\"offset\\" and \\"limit\\", filtering via \\"nodeTypes\\", and numbering projection via \\"reviewMode\\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"blocks\\"}\\n 2. {\\"action\\":\\"blocks\\",\\"includeText\\":true,\\"offset\\":0,\\"limit\\":20,\\"reviewMode\\":\\"final\\"}\\n 3. {\\"action\\":\\"blocks\\",\\"offset\\":0,\\"limit\\":20,\\"nodeTypes\\":[\\"heading\\",\\"paragraph\\"]}\\n 4. {\\"action\\":\\"text\\"}\\n 5. {\\"action\\":\\"info\\"}\\n 6. {\\"action\\":\\"html_projection\\",\\"reviewMode\\":\\"redline\\",\\"includeSourceMap\\":true}\\n 7. {\\"action\\":\\"markdown_projection\\",\\"reviewMode\\":\\"original\\",\\"scope\\":{\\"kind\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"paragraph-id\\"},\\"includeSourceMap\\":true}",
31895
31895
  "inputSchema": {
31896
31896
  "type": "object",
31897
31897
  "properties": {
@@ -38169,7 +38169,7 @@ var init_embedded_tools_generated = __esm(() => {
38169
38169
  },
38170
38170
  {
38171
38171
  "toolName": "superdoc_mutations",
38172
- "description": "All steps succeed or all fail; no partial application. Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. Each step has an id, an op, a \\"where\\" clause for targeting ({by:\\"select\\", select:{...}, require:\\"first\\"|\\"exactlyOne\\"|\\"all\\"} or {by:\\"ref\\", ref:\\"...\\"} or {by:\\"block\\", nodeType:\\"paragraph\\", nodeId:\\"...\\"}), and \\"args\\" with operation-specific parameters. Use {by:\\"block\\", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action \\"blocks\\" without relying on text matching. For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:\\"blocks\\" and includeText:true, then rewrite the matching block by nodeId. Use {by:\\"select\\"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. For create steps, \\"where\\" targets an existing anchor block and args.position (\\"before\\" or \\"after\\") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. For format.apply with require \\"all\\", use a node selector to format every heading or paragraph at once: {by:\\"select\\", select:{type:\\"node\\", nodeType:\\"heading\\"}, require:\\"all\\"}. Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. Action \\"preview\\" dry-runs the plan. Action \\"apply\\" executes it. If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"apply\\",\\"atomic\\":true,\\"changeMode\\":\\"direct\\",\\"steps\\":[{\\"id\\":\\"s1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"old term\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"new term\\"}}},{\\"id\\":\\"s2\\",\\"op\\":\\"text.delete\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\" (deprecated)\\"},\\"require\\":\\"all\\"},\\"args\\":{}}]}\\n 2. {\\"action\\":\\"apply\\",\\"steps\\":[{\\"id\\":\\"r1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"<nodeId>\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"Updated clause text.\\"}}},{\\"id\\":\\"f1\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"node\\",\\"nodeType\\":\\"heading\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"color\\":\\"#FF0000\\"}}},{\\"id\\":\\"f2\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"Confidential Information\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"bold\\":true}}}]}",
38172
+ "description": "All steps succeed or all fail; no partial application. Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. Each step has an id, an op, a \\"where\\" clause for targeting ({by:\\"select\\", select:{...}, require:\\"first\\"|\\"exactlyOne\\"|\\"all\\"} or {by:\\"ref\\", ref:\\"...\\"} or {by:\\"block\\", nodeType:\\"paragraph\\", nodeId:\\"...\\"}), and \\"args\\" with operation-specific parameters. Use {by:\\"block\\", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action \\"blocks\\" without relying on text matching. For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:\\"blocks\\" and includeText:true, then rewrite the matching block by nodeId. For a placeholder or name within a tab-separated paragraph, use a text.rewrite where.by:\\"select\\" targeting only the exact placeholder; a whole-block plain-text rewrite omitting tabs deletes those controls. Use {by:\\"select\\"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. For create steps, \\"where\\" targets an existing anchor block and args.position (\\"before\\" or \\"after\\") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. For format.apply with require \\"all\\", use a node selector to format every heading or paragraph at once: {by:\\"select\\", select:{type:\\"node\\", nodeType:\\"heading\\"}, require:\\"all\\"}. Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. Action \\"preview\\" dry-runs the plan. Action \\"apply\\" executes it. If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"apply\\",\\"atomic\\":true,\\"changeMode\\":\\"direct\\",\\"steps\\":[{\\"id\\":\\"s1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"old term\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"new term\\"}}},{\\"id\\":\\"s2\\",\\"op\\":\\"text.delete\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\" (deprecated)\\"},\\"require\\":\\"all\\"},\\"args\\":{}}]}\\n 2. {\\"action\\":\\"apply\\",\\"steps\\":[{\\"id\\":\\"r1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"<nodeId>\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"Updated clause text.\\"}}},{\\"id\\":\\"f1\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"node\\",\\"nodeType\\":\\"heading\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"color\\":\\"#FF0000\\"}}},{\\"id\\":\\"f2\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"Confidential Information\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"bold\\":true}}}]}",
38173
38173
  "inputSchema": {
38174
38174
  "type": "object",
38175
38175
  "properties": {
@@ -42255,7 +42255,7 @@ var init_embedded_tools_generated = __esm(() => {
42255
42255
  "type": "function",
42256
42256
  "function": {
42257
42257
  "name": "superdoc_get_content",
42258
- "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \\"blocks\\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \\"blocks\\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \\"text\\", \\"markdown\\", and \\"html\\" return compact strings. Actions \\"markdown_projection\\" and \\"html_projection\\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \\"info\\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \\"blocks\\" action supports pagination via \\"offset\\" and \\"limit\\", filtering via \\"nodeTypes\\", and numbering projection via \\"reviewMode\\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"blocks\\"}\\n 2. {\\"action\\":\\"blocks\\",\\"includeText\\":true,\\"offset\\":0,\\"limit\\":20,\\"reviewMode\\":\\"final\\"}\\n 3. {\\"action\\":\\"blocks\\",\\"offset\\":0,\\"limit\\":20,\\"nodeTypes\\":[\\"heading\\",\\"paragraph\\"]}\\n 4. {\\"action\\":\\"text\\"}\\n 5. {\\"action\\":\\"info\\"}\\n 6. {\\"action\\":\\"html_projection\\",\\"reviewMode\\":\\"redline\\",\\"includeSourceMap\\":true}\\n 7. {\\"action\\":\\"markdown_projection\\",\\"reviewMode\\":\\"original\\",\\"scope\\":{\\"kind\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"paragraph-id\\"},\\"includeSourceMap\\":true}",
42258
+ "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \\"blocks\\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Paragraph-class blocks include tabCount, the number of U+0009 tabs in the complete text even if the 80-character preview trims or truncates them. If tabCount is positive, read includeText:true before editing; do not treat a preview space as proof that there is no tab. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \\"blocks\\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \\"text\\", \\"markdown\\", and \\"html\\" return compact strings. Actions \\"markdown_projection\\" and \\"html_projection\\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \\"info\\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \\"blocks\\" action supports pagination via \\"offset\\" and \\"limit\\", filtering via \\"nodeTypes\\", and numbering projection via \\"reviewMode\\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"blocks\\"}\\n 2. {\\"action\\":\\"blocks\\",\\"includeText\\":true,\\"offset\\":0,\\"limit\\":20,\\"reviewMode\\":\\"final\\"}\\n 3. {\\"action\\":\\"blocks\\",\\"offset\\":0,\\"limit\\":20,\\"nodeTypes\\":[\\"heading\\",\\"paragraph\\"]}\\n 4. {\\"action\\":\\"text\\"}\\n 5. {\\"action\\":\\"info\\"}\\n 6. {\\"action\\":\\"html_projection\\",\\"reviewMode\\":\\"redline\\",\\"includeSourceMap\\":true}\\n 7. {\\"action\\":\\"markdown_projection\\",\\"reviewMode\\":\\"original\\",\\"scope\\":{\\"kind\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"paragraph-id\\"},\\"includeSourceMap\\":true}",
42259
42259
  "parameters": {
42260
42260
  "type": "object",
42261
42261
  "properties": {
@@ -48137,7 +48137,7 @@ var init_embedded_tools_generated = __esm(() => {
48137
48137
  "type": "function",
48138
48138
  "function": {
48139
48139
  "name": "superdoc_mutations",
48140
- "description": "All steps succeed or all fail; no partial application. Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. Each step has an id, an op, a \\"where\\" clause for targeting ({by:\\"select\\", select:{...}, require:\\"first\\"|\\"exactlyOne\\"|\\"all\\"} or {by:\\"ref\\", ref:\\"...\\"} or {by:\\"block\\", nodeType:\\"paragraph\\", nodeId:\\"...\\"}), and \\"args\\" with operation-specific parameters. Use {by:\\"block\\", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action \\"blocks\\" without relying on text matching. For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:\\"blocks\\" and includeText:true, then rewrite the matching block by nodeId. Use {by:\\"select\\"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. For create steps, \\"where\\" targets an existing anchor block and args.position (\\"before\\" or \\"after\\") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. For format.apply with require \\"all\\", use a node selector to format every heading or paragraph at once: {by:\\"select\\", select:{type:\\"node\\", nodeType:\\"heading\\"}, require:\\"all\\"}. Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. Action \\"preview\\" dry-runs the plan. Action \\"apply\\" executes it. If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"apply\\",\\"atomic\\":true,\\"changeMode\\":\\"direct\\",\\"steps\\":[{\\"id\\":\\"s1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"old term\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"new term\\"}}},{\\"id\\":\\"s2\\",\\"op\\":\\"text.delete\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\" (deprecated)\\"},\\"require\\":\\"all\\"},\\"args\\":{}}]}\\n 2. {\\"action\\":\\"apply\\",\\"steps\\":[{\\"id\\":\\"r1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"<nodeId>\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"Updated clause text.\\"}}},{\\"id\\":\\"f1\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"node\\",\\"nodeType\\":\\"heading\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"color\\":\\"#FF0000\\"}}},{\\"id\\":\\"f2\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"Confidential Information\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"bold\\":true}}}]}",
48140
+ "description": "All steps succeed or all fail; no partial application. Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. Each step has an id, an op, a \\"where\\" clause for targeting ({by:\\"select\\", select:{...}, require:\\"first\\"|\\"exactlyOne\\"|\\"all\\"} or {by:\\"ref\\", ref:\\"...\\"} or {by:\\"block\\", nodeType:\\"paragraph\\", nodeId:\\"...\\"}), and \\"args\\" with operation-specific parameters. Use {by:\\"block\\", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action \\"blocks\\" without relying on text matching. For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:\\"blocks\\" and includeText:true, then rewrite the matching block by nodeId. For a placeholder or name within a tab-separated paragraph, use a text.rewrite where.by:\\"select\\" targeting only the exact placeholder; a whole-block plain-text rewrite omitting tabs deletes those controls. Use {by:\\"select\\"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. For create steps, \\"where\\" targets an existing anchor block and args.position (\\"before\\" or \\"after\\") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. For format.apply with require \\"all\\", use a node selector to format every heading or paragraph at once: {by:\\"select\\", select:{type:\\"node\\", nodeType:\\"heading\\"}, require:\\"all\\"}. Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. Action \\"preview\\" dry-runs the plan. Action \\"apply\\" executes it. If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"apply\\",\\"atomic\\":true,\\"changeMode\\":\\"direct\\",\\"steps\\":[{\\"id\\":\\"s1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"old term\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"new term\\"}}},{\\"id\\":\\"s2\\",\\"op\\":\\"text.delete\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\" (deprecated)\\"},\\"require\\":\\"all\\"},\\"args\\":{}}]}\\n 2. {\\"action\\":\\"apply\\",\\"steps\\":[{\\"id\\":\\"r1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"<nodeId>\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"Updated clause text.\\"}}},{\\"id\\":\\"f1\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"node\\",\\"nodeType\\":\\"heading\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"color\\":\\"#FF0000\\"}}},{\\"id\\":\\"f2\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"Confidential Information\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"bold\\":true}}}]}",
48141
48141
  "parameters": {
48142
48142
  "type": "object",
48143
48143
  "properties": {
@@ -51875,7 +51875,7 @@ var init_embedded_tools_generated = __esm(() => {
51875
51875
  "tools": [
51876
51876
  {
51877
51877
  "name": "superdoc_get_content",
51878
- "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \\"blocks\\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \\"blocks\\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \\"text\\", \\"markdown\\", and \\"html\\" return compact strings. Actions \\"markdown_projection\\" and \\"html_projection\\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \\"info\\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \\"blocks\\" action supports pagination via \\"offset\\" and \\"limit\\", filtering via \\"nodeTypes\\", and numbering projection via \\"reviewMode\\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"blocks\\"}\\n 2. {\\"action\\":\\"blocks\\",\\"includeText\\":true,\\"offset\\":0,\\"limit\\":20,\\"reviewMode\\":\\"final\\"}\\n 3. {\\"action\\":\\"blocks\\",\\"offset\\":0,\\"limit\\":20,\\"nodeTypes\\":[\\"heading\\",\\"paragraph\\"]}\\n 4. {\\"action\\":\\"text\\"}\\n 5. {\\"action\\":\\"info\\"}\\n 6. {\\"action\\":\\"html_projection\\",\\"reviewMode\\":\\"redline\\",\\"includeSourceMap\\":true}\\n 7. {\\"action\\":\\"markdown_projection\\",\\"reviewMode\\":\\"original\\",\\"scope\\":{\\"kind\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"paragraph-id\\"},\\"includeSourceMap\\":true}",
51878
+ "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \\"blocks\\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Paragraph-class blocks include tabCount, the number of U+0009 tabs in the complete text even if the 80-character preview trims or truncates them. If tabCount is positive, read includeText:true before editing; do not treat a preview space as proof that there is no tab. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \\"blocks\\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \\"text\\", \\"markdown\\", and \\"html\\" return compact strings. Actions \\"markdown_projection\\" and \\"html_projection\\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \\"info\\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \\"blocks\\" action supports pagination via \\"offset\\" and \\"limit\\", filtering via \\"nodeTypes\\", and numbering projection via \\"reviewMode\\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"blocks\\"}\\n 2. {\\"action\\":\\"blocks\\",\\"includeText\\":true,\\"offset\\":0,\\"limit\\":20,\\"reviewMode\\":\\"final\\"}\\n 3. {\\"action\\":\\"blocks\\",\\"offset\\":0,\\"limit\\":20,\\"nodeTypes\\":[\\"heading\\",\\"paragraph\\"]}\\n 4. {\\"action\\":\\"text\\"}\\n 5. {\\"action\\":\\"info\\"}\\n 6. {\\"action\\":\\"html_projection\\",\\"reviewMode\\":\\"redline\\",\\"includeSourceMap\\":true}\\n 7. {\\"action\\":\\"markdown_projection\\",\\"reviewMode\\":\\"original\\",\\"scope\\":{\\"kind\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"paragraph-id\\"},\\"includeSourceMap\\":true}",
51879
51879
  "input_schema": {
51880
51880
  "type": "object",
51881
51881
  "properties": {
@@ -57733,7 +57733,7 @@ var init_embedded_tools_generated = __esm(() => {
57733
57733
  },
57734
57734
  {
57735
57735
  "name": "superdoc_mutations",
57736
- "description": "All steps succeed or all fail; no partial application. Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. Each step has an id, an op, a \\"where\\" clause for targeting ({by:\\"select\\", select:{...}, require:\\"first\\"|\\"exactlyOne\\"|\\"all\\"} or {by:\\"ref\\", ref:\\"...\\"} or {by:\\"block\\", nodeType:\\"paragraph\\", nodeId:\\"...\\"}), and \\"args\\" with operation-specific parameters. Use {by:\\"block\\", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action \\"blocks\\" without relying on text matching. For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:\\"blocks\\" and includeText:true, then rewrite the matching block by nodeId. Use {by:\\"select\\"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. For create steps, \\"where\\" targets an existing anchor block and args.position (\\"before\\" or \\"after\\") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. For format.apply with require \\"all\\", use a node selector to format every heading or paragraph at once: {by:\\"select\\", select:{type:\\"node\\", nodeType:\\"heading\\"}, require:\\"all\\"}. Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. Action \\"preview\\" dry-runs the plan. Action \\"apply\\" executes it. If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"apply\\",\\"atomic\\":true,\\"changeMode\\":\\"direct\\",\\"steps\\":[{\\"id\\":\\"s1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"old term\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"new term\\"}}},{\\"id\\":\\"s2\\",\\"op\\":\\"text.delete\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\" (deprecated)\\"},\\"require\\":\\"all\\"},\\"args\\":{}}]}\\n 2. {\\"action\\":\\"apply\\",\\"steps\\":[{\\"id\\":\\"r1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"<nodeId>\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"Updated clause text.\\"}}},{\\"id\\":\\"f1\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"node\\",\\"nodeType\\":\\"heading\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"color\\":\\"#FF0000\\"}}},{\\"id\\":\\"f2\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"Confidential Information\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"bold\\":true}}}]}",
57736
+ "description": "All steps succeed or all fail; no partial application. Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. Each step has an id, an op, a \\"where\\" clause for targeting ({by:\\"select\\", select:{...}, require:\\"first\\"|\\"exactlyOne\\"|\\"all\\"} or {by:\\"ref\\", ref:\\"...\\"} or {by:\\"block\\", nodeType:\\"paragraph\\", nodeId:\\"...\\"}), and \\"args\\" with operation-specific parameters. Use {by:\\"block\\", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action \\"blocks\\" without relying on text matching. For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:\\"blocks\\" and includeText:true, then rewrite the matching block by nodeId. For a placeholder or name within a tab-separated paragraph, use a text.rewrite where.by:\\"select\\" targeting only the exact placeholder; a whole-block plain-text rewrite omitting tabs deletes those controls. Use {by:\\"select\\"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. For create steps, \\"where\\" targets an existing anchor block and args.position (\\"before\\" or \\"after\\") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. For format.apply with require \\"all\\", use a node selector to format every heading or paragraph at once: {by:\\"select\\", select:{type:\\"node\\", nodeType:\\"heading\\"}, require:\\"all\\"}. Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. Action \\"preview\\" dry-runs the plan. Action \\"apply\\" executes it. If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"apply\\",\\"atomic\\":true,\\"changeMode\\":\\"direct\\",\\"steps\\":[{\\"id\\":\\"s1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"old term\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"new term\\"}}},{\\"id\\":\\"s2\\",\\"op\\":\\"text.delete\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\" (deprecated)\\"},\\"require\\":\\"all\\"},\\"args\\":{}}]}\\n 2. {\\"action\\":\\"apply\\",\\"steps\\":[{\\"id\\":\\"r1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"<nodeId>\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"Updated clause text.\\"}}},{\\"id\\":\\"f1\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"node\\",\\"nodeType\\":\\"heading\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"color\\":\\"#FF0000\\"}}},{\\"id\\":\\"f2\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"Confidential Information\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"bold\\":true}}}]}",
57737
57737
  "input_schema": {
57738
57738
  "type": "object",
57739
57739
  "properties": {
@@ -61469,7 +61469,7 @@ var init_embedded_tools_generated = __esm(() => {
61469
61469
  "type": "function",
61470
61470
  "function": {
61471
61471
  "name": "superdoc_get_content",
61472
- "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \\"blocks\\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \\"blocks\\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \\"text\\", \\"markdown\\", and \\"html\\" return compact strings. Actions \\"markdown_projection\\" and \\"html_projection\\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \\"info\\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \\"blocks\\" action supports pagination via \\"offset\\" and \\"limit\\", filtering via \\"nodeTypes\\", and numbering projection via \\"reviewMode\\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"blocks\\"}\\n 2. {\\"action\\":\\"blocks\\",\\"includeText\\":true,\\"offset\\":0,\\"limit\\":20,\\"reviewMode\\":\\"final\\"}\\n 3. {\\"action\\":\\"blocks\\",\\"offset\\":0,\\"limit\\":20,\\"nodeTypes\\":[\\"heading\\",\\"paragraph\\"]}\\n 4. {\\"action\\":\\"text\\"}\\n 5. {\\"action\\":\\"info\\"}\\n 6. {\\"action\\":\\"html_projection\\",\\"reviewMode\\":\\"redline\\",\\"includeSourceMap\\":true}\\n 7. {\\"action\\":\\"markdown_projection\\",\\"reviewMode\\":\\"original\\",\\"scope\\":{\\"kind\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"paragraph-id\\"},\\"includeSourceMap\\":true}",
61472
+ "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \\"blocks\\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Paragraph-class blocks include tabCount, the number of U+0009 tabs in the complete text even if the 80-character preview trims or truncates them. If tabCount is positive, read includeText:true before editing; do not treat a preview space as proof that there is no tab. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \\"blocks\\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \\"text\\", \\"markdown\\", and \\"html\\" return compact strings. Actions \\"markdown_projection\\" and \\"html_projection\\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \\"info\\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \\"blocks\\" action supports pagination via \\"offset\\" and \\"limit\\", filtering via \\"nodeTypes\\", and numbering projection via \\"reviewMode\\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"blocks\\"}\\n 2. {\\"action\\":\\"blocks\\",\\"includeText\\":true,\\"offset\\":0,\\"limit\\":20,\\"reviewMode\\":\\"final\\"}\\n 3. {\\"action\\":\\"blocks\\",\\"offset\\":0,\\"limit\\":20,\\"nodeTypes\\":[\\"heading\\",\\"paragraph\\"]}\\n 4. {\\"action\\":\\"text\\"}\\n 5. {\\"action\\":\\"info\\"}\\n 6. {\\"action\\":\\"html_projection\\",\\"reviewMode\\":\\"redline\\",\\"includeSourceMap\\":true}\\n 7. {\\"action\\":\\"markdown_projection\\",\\"reviewMode\\":\\"original\\",\\"scope\\":{\\"kind\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"paragraph-id\\"},\\"includeSourceMap\\":true}",
61473
61473
  "parameters": {
61474
61474
  "type": "object",
61475
61475
  "properties": {
@@ -67351,7 +67351,7 @@ var init_embedded_tools_generated = __esm(() => {
67351
67351
  "type": "function",
67352
67352
  "function": {
67353
67353
  "name": "superdoc_mutations",
67354
- "description": "All steps succeed or all fail; no partial application. Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. Each step has an id, an op, a \\"where\\" clause for targeting ({by:\\"select\\", select:{...}, require:\\"first\\"|\\"exactlyOne\\"|\\"all\\"} or {by:\\"ref\\", ref:\\"...\\"} or {by:\\"block\\", nodeType:\\"paragraph\\", nodeId:\\"...\\"}), and \\"args\\" with operation-specific parameters. Use {by:\\"block\\", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action \\"blocks\\" without relying on text matching. For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:\\"blocks\\" and includeText:true, then rewrite the matching block by nodeId. Use {by:\\"select\\"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. For create steps, \\"where\\" targets an existing anchor block and args.position (\\"before\\" or \\"after\\") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. For format.apply with require \\"all\\", use a node selector to format every heading or paragraph at once: {by:\\"select\\", select:{type:\\"node\\", nodeType:\\"heading\\"}, require:\\"all\\"}. Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. Action \\"preview\\" dry-runs the plan. Action \\"apply\\" executes it. If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"apply\\",\\"atomic\\":true,\\"changeMode\\":\\"direct\\",\\"steps\\":[{\\"id\\":\\"s1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"old term\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"new term\\"}}},{\\"id\\":\\"s2\\",\\"op\\":\\"text.delete\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\" (deprecated)\\"},\\"require\\":\\"all\\"},\\"args\\":{}}]}\\n 2. {\\"action\\":\\"apply\\",\\"steps\\":[{\\"id\\":\\"r1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"<nodeId>\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"Updated clause text.\\"}}},{\\"id\\":\\"f1\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"node\\",\\"nodeType\\":\\"heading\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"color\\":\\"#FF0000\\"}}},{\\"id\\":\\"f2\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"Confidential Information\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"bold\\":true}}}]}",
67354
+ "description": "All steps succeed or all fail; no partial application. Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. Each step has an id, an op, a \\"where\\" clause for targeting ({by:\\"select\\", select:{...}, require:\\"first\\"|\\"exactlyOne\\"|\\"all\\"} or {by:\\"ref\\", ref:\\"...\\"} or {by:\\"block\\", nodeType:\\"paragraph\\", nodeId:\\"...\\"}), and \\"args\\" with operation-specific parameters. Use {by:\\"block\\", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action \\"blocks\\" without relying on text matching. For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:\\"blocks\\" and includeText:true, then rewrite the matching block by nodeId. For a placeholder or name within a tab-separated paragraph, use a text.rewrite where.by:\\"select\\" targeting only the exact placeholder; a whole-block plain-text rewrite omitting tabs deletes those controls. Use {by:\\"select\\"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. For create steps, \\"where\\" targets an existing anchor block and args.position (\\"before\\" or \\"after\\") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. For format.apply with require \\"all\\", use a node selector to format every heading or paragraph at once: {by:\\"select\\", select:{type:\\"node\\", nodeType:\\"heading\\"}, require:\\"all\\"}. Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. Action \\"preview\\" dry-runs the plan. Action \\"apply\\" executes it. If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"apply\\",\\"atomic\\":true,\\"changeMode\\":\\"direct\\",\\"steps\\":[{\\"id\\":\\"s1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"old term\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"new term\\"}}},{\\"id\\":\\"s2\\",\\"op\\":\\"text.delete\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\" (deprecated)\\"},\\"require\\":\\"all\\"},\\"args\\":{}}]}\\n 2. {\\"action\\":\\"apply\\",\\"steps\\":[{\\"id\\":\\"r1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"<nodeId>\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"Updated clause text.\\"}}},{\\"id\\":\\"f1\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"node\\",\\"nodeType\\":\\"heading\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"color\\":\\"#FF0000\\"}}},{\\"id\\":\\"f2\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"Confidential Information\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"bold\\":true}}}]}",
67355
67355
  "parameters": {
67356
67356
  "type": "object",
67357
67357
  "properties": {
@@ -71089,7 +71089,7 @@ var init_embedded_tools_generated = __esm(() => {
71089
71089
  "tools": [
71090
71090
  {
71091
71091
  "name": "superdoc_get_content",
71092
- "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \\"blocks\\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \\"blocks\\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \\"text\\", \\"markdown\\", and \\"html\\" return compact strings. Actions \\"markdown_projection\\" and \\"html_projection\\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \\"info\\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \\"blocks\\" action supports pagination via \\"offset\\" and \\"limit\\", filtering via \\"nodeTypes\\", and numbering projection via \\"reviewMode\\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"blocks\\"}\\n 2. {\\"action\\":\\"blocks\\",\\"includeText\\":true,\\"offset\\":0,\\"limit\\":20,\\"reviewMode\\":\\"final\\"}\\n 3. {\\"action\\":\\"blocks\\",\\"offset\\":0,\\"limit\\":20,\\"nodeTypes\\":[\\"heading\\",\\"paragraph\\"]}\\n 4. {\\"action\\":\\"text\\"}\\n 5. {\\"action\\":\\"info\\"}\\n 6. {\\"action\\":\\"html_projection\\",\\"reviewMode\\":\\"redline\\",\\"includeSourceMap\\":true}\\n 7. {\\"action\\":\\"markdown_projection\\",\\"reviewMode\\":\\"original\\",\\"scope\\":{\\"kind\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"paragraph-id\\"},\\"includeSourceMap\\":true}",
71092
+ "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \\"blocks\\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Paragraph-class blocks include tabCount, the number of U+0009 tabs in the complete text even if the 80-character preview trims or truncates them. If tabCount is positive, read includeText:true before editing; do not treat a preview space as proof that there is no tab. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \\"blocks\\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \\"text\\", \\"markdown\\", and \\"html\\" return compact strings. Actions \\"markdown_projection\\" and \\"html_projection\\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \\"info\\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \\"blocks\\" action supports pagination via \\"offset\\" and \\"limit\\", filtering via \\"nodeTypes\\", and numbering projection via \\"reviewMode\\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"blocks\\"}\\n 2. {\\"action\\":\\"blocks\\",\\"includeText\\":true,\\"offset\\":0,\\"limit\\":20,\\"reviewMode\\":\\"final\\"}\\n 3. {\\"action\\":\\"blocks\\",\\"offset\\":0,\\"limit\\":20,\\"nodeTypes\\":[\\"heading\\",\\"paragraph\\"]}\\n 4. {\\"action\\":\\"text\\"}\\n 5. {\\"action\\":\\"info\\"}\\n 6. {\\"action\\":\\"html_projection\\",\\"reviewMode\\":\\"redline\\",\\"includeSourceMap\\":true}\\n 7. {\\"action\\":\\"markdown_projection\\",\\"reviewMode\\":\\"original\\",\\"scope\\":{\\"kind\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"paragraph-id\\"},\\"includeSourceMap\\":true}",
71093
71093
  "parameters": {
71094
71094
  "type": "object",
71095
71095
  "properties": {
@@ -77115,7 +77115,7 @@ var init_embedded_tools_generated = __esm(() => {
77115
77115
  },
77116
77116
  {
77117
77117
  "name": "superdoc_mutations",
77118
- "description": "All steps succeed or all fail; no partial application. Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. Each step has an id, an op, a \\"where\\" clause for targeting ({by:\\"select\\", select:{...}, require:\\"first\\"|\\"exactlyOne\\"|\\"all\\"} or {by:\\"ref\\", ref:\\"...\\"} or {by:\\"block\\", nodeType:\\"paragraph\\", nodeId:\\"...\\"}), and \\"args\\" with operation-specific parameters. Use {by:\\"block\\", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action \\"blocks\\" without relying on text matching. For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:\\"blocks\\" and includeText:true, then rewrite the matching block by nodeId. Use {by:\\"select\\"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. For create steps, \\"where\\" targets an existing anchor block and args.position (\\"before\\" or \\"after\\") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. For format.apply with require \\"all\\", use a node selector to format every heading or paragraph at once: {by:\\"select\\", select:{type:\\"node\\", nodeType:\\"heading\\"}, require:\\"all\\"}. Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. Action \\"preview\\" dry-runs the plan. Action \\"apply\\" executes it. If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"apply\\",\\"atomic\\":true,\\"changeMode\\":\\"direct\\",\\"steps\\":[{\\"id\\":\\"s1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"old term\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"new term\\"}}},{\\"id\\":\\"s2\\",\\"op\\":\\"text.delete\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\" (deprecated)\\"},\\"require\\":\\"all\\"},\\"args\\":{}}]}\\n 2. {\\"action\\":\\"apply\\",\\"steps\\":[{\\"id\\":\\"r1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"<nodeId>\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"Updated clause text.\\"}}},{\\"id\\":\\"f1\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"node\\",\\"nodeType\\":\\"heading\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"color\\":\\"#FF0000\\"}}},{\\"id\\":\\"f2\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"Confidential Information\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"bold\\":true}}}]}",
77118
+ "description": "All steps succeed or all fail; no partial application. Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. Each step has an id, an op, a \\"where\\" clause for targeting ({by:\\"select\\", select:{...}, require:\\"first\\"|\\"exactlyOne\\"|\\"all\\"} or {by:\\"ref\\", ref:\\"...\\"} or {by:\\"block\\", nodeType:\\"paragraph\\", nodeId:\\"...\\"}), and \\"args\\" with operation-specific parameters. Use {by:\\"block\\", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action \\"blocks\\" without relying on text matching. For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:\\"blocks\\" and includeText:true, then rewrite the matching block by nodeId. For a placeholder or name within a tab-separated paragraph, use a text.rewrite where.by:\\"select\\" targeting only the exact placeholder; a whole-block plain-text rewrite omitting tabs deletes those controls. Use {by:\\"select\\"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. For create steps, \\"where\\" targets an existing anchor block and args.position (\\"before\\" or \\"after\\") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. For format.apply with require \\"all\\", use a node selector to format every heading or paragraph at once: {by:\\"select\\", select:{type:\\"node\\", nodeType:\\"heading\\"}, require:\\"all\\"}. Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. Action \\"preview\\" dry-runs the plan. Action \\"apply\\" executes it. If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.\\n\\nEXAMPLES:\\n 1. {\\"action\\":\\"apply\\",\\"atomic\\":true,\\"changeMode\\":\\"direct\\",\\"steps\\":[{\\"id\\":\\"s1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"old term\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"new term\\"}}},{\\"id\\":\\"s2\\",\\"op\\":\\"text.delete\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\" (deprecated)\\"},\\"require\\":\\"all\\"},\\"args\\":{}}]}\\n 2. {\\"action\\":\\"apply\\",\\"steps\\":[{\\"id\\":\\"r1\\",\\"op\\":\\"text.rewrite\\",\\"where\\":{\\"by\\":\\"block\\",\\"nodeType\\":\\"paragraph\\",\\"nodeId\\":\\"<nodeId>\\"},\\"args\\":{\\"replacement\\":{\\"text\\":\\"Updated clause text.\\"}}},{\\"id\\":\\"f1\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"node\\",\\"nodeType\\":\\"heading\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"color\\":\\"#FF0000\\"}}},{\\"id\\":\\"f2\\",\\"op\\":\\"format.apply\\",\\"where\\":{\\"by\\":\\"select\\",\\"select\\":{\\"type\\":\\"text\\",\\"pattern\\":\\"Confidential Information\\"},\\"require\\":\\"all\\"},\\"args\\":{\\"inline\\":{\\"bold\\":true}}}]}",
77119
77119
  "parameters": {
77120
77120
  "type": "object",
77121
77121
  "properties": {
@@ -80918,13 +80918,13 @@ var init_embedded_tools_generated = __esm(() => {
80918
80918
 
80919
80919
  Every editing tool needs a **target** telling the API *where* to apply the change. There are three ways to get one:
80920
80920
 
80921
- - **From blocks data**: Each block has a \`ref\` (pass directly to superdoc_edit or superdoc_format), a \`nodeId\` (for building \`at\` positions with superdoc_create or \`where: {by: "block", ...}\` in superdoc_mutations), and optional full \`text\` when you call \`superdoc_get_content({action: "blocks", includeText: true})\`.
80921
+ - **From blocks data**: Each block has a \`ref\` (pass directly to superdoc_edit or superdoc_format), a \`nodeId\` (for building \`at\` positions with superdoc_create or \`where: {by: "block", ...}\` in superdoc_mutations), and optional full \`text\` when you call \`superdoc_get_content({action: "blocks", includeText: true})\`. Paragraph-class blocks have \`tabCount\` for the complete text; a compact \`textPreview\` can trim or truncate tabs. If \`tabCount > 0\`, read with \`includeText: true\` before editing.
80922
80922
  - **From superdoc_search**: Returns \`handle.ref\` covering the matched text. Use search when you need to find text patterns, not when you already know which block to target.
80923
80923
  - **From superdoc_create**: Returns \`nodeId\` and \`ref\`. The ref is valid for one immediate format call. For subsequent operations, re-fetch blocks to get fresh refs.
80924
80924
 
80925
80925
  **Refs expire after any mutation** between separate tool calls. Within a superdoc_mutations batch, selectors resolve automatically — no manual re-searching between steps.
80926
80926
 
80927
- **Critical targeting rule:** when rewriting an entire paragraph, clause, or other known block, first read \`superdoc_get_content({action: "blocks", includeText: true})\`, identify the block's \`nodeId\`, then use \`where: {by: "block", nodeType, nodeId}\` in \`superdoc_mutations\`. Do NOT use a shortened text selector to rewrite a whole clause.
80927
+ **Critical targeting rule:** when rewriting an entire paragraph, clause, or other known block, first read \`superdoc_get_content({action: "blocks", includeText: true})\`, identify the block's \`nodeId\`, then use \`where: {by: "block", nodeType, nodeId}\` in \`superdoc_mutations\`. Do NOT use a shortened text selector to rewrite a whole clause. Conversely, to change only a name or placeholder, select that exact text with \`superdoc_search\` and call \`superdoc_edit({action: "replace", ref: "<handle.ref>", text: "new name"})\`, or use a \`text.rewrite\` step with \`where: {by: "select", select: {type: "text", pattern: "old name"}, require: "exactlyOne"}\`. A whole-block plain-text rewrite that omits tabs deletes them; do not use it for a placeholder edit. Preserving tabs does not guarantee a visually aligned column if longer text crosses tab stops.
80928
80928
 
80929
80929
  ## Citation-grade output reads
80930
80930
 
@@ -81403,13 +81403,13 @@ One format.apply step per block. Combine \`inline\`, \`alignment\`, and \`scope:
81403
81403
 
81404
81404
  Every editing tool needs a **target** telling the API *where* to apply the change. There are three ways to get one:
81405
81405
 
81406
- - **From blocks data**: Each block has a \`ref\` (pass directly to superdoc_edit or superdoc_format), a \`nodeId\` (for building \`at\` positions with superdoc_create or \`where: {by: "block", ...}\` in superdoc_mutations), and optional full \`text\` when you call \`superdoc_get_content({action: "blocks", includeText: true})\`.
81406
+ - **From blocks data**: Each block has a \`ref\` (pass directly to superdoc_edit or superdoc_format), a \`nodeId\` (for building \`at\` positions with superdoc_create or \`where: {by: "block", ...}\` in superdoc_mutations), and optional full \`text\` when you call \`superdoc_get_content({action: "blocks", includeText: true})\`. Paragraph-class blocks have \`tabCount\` for the complete text; a compact \`textPreview\` can trim or truncate tabs. If \`tabCount > 0\`, read with \`includeText: true\` before editing.
81407
81407
  - **From superdoc_search**: Returns \`handle.ref\` covering the matched text. Use search when you need to find text patterns, not when you already know which block to target.
81408
81408
  - **From superdoc_create**: Returns \`nodeId\` and \`ref\`. The ref is valid for one immediate format call. For subsequent operations, re-fetch blocks to get fresh refs.
81409
81409
 
81410
81410
  **Refs expire after any mutation** between separate tool calls. Within a superdoc_mutations batch, selectors resolve automatically — no manual re-searching between steps.
81411
81411
 
81412
- **Critical targeting rule:** when rewriting an entire paragraph, clause, or other known block, first read \`superdoc_get_content({action: "blocks", includeText: true})\`, identify the block's \`nodeId\`, then use \`where: {by: "block", nodeType, nodeId}\` in \`superdoc_mutations\`. Do NOT use a shortened text selector to rewrite a whole clause.
81412
+ **Critical targeting rule:** when rewriting an entire paragraph, clause, or other known block, first read \`superdoc_get_content({action: "blocks", includeText: true})\`, identify the block's \`nodeId\`, then use \`where: {by: "block", nodeType, nodeId}\` in \`superdoc_mutations\`. Do NOT use a shortened text selector to rewrite a whole clause. Conversely, to change only a name or placeholder, select that exact text with \`superdoc_search\` and call \`superdoc_edit({action: "replace", ref: "<handle.ref>", text: "new name"})\`, or use a \`text.rewrite\` step with \`where: {by: "select", select: {type: "text", pattern: "old name"}, require: "exactlyOne"}\`. A whole-block plain-text rewrite that omits tabs deletes them; do not use it for a placeholder edit. Preserving tabs does not guarantee a visually aligned column if longer text crosses tab stops.
81413
81413
 
81414
81414
  ## Citation-grade output reads
81415
81415
 
@@ -82195,6 +82195,7 @@ function normalizeSnapshotBlock(value, fallbackOrdinal, blockTextLimit) {
82195
82195
  nodeType: asString(rec.nodeType, "paragraph"),
82196
82196
  text,
82197
82197
  textPreview,
82198
+ ...typeof rec.tabCount === "number" && Number.isInteger(rec.tabCount) && rec.tabCount >= 0 ? { tabCount: rec.tabCount } : {},
82198
82199
  styleId: typeof rec.styleId === "string" ? rec.styleId : null,
82199
82200
  headingLevel: typeof rec.headingLevel === "number" ? rec.headingLevel : undefined,
82200
82201
  ...asRecord(rec.numbering) ? {
@@ -92894,7 +92895,7 @@ var init_actions = __esm(() => {
92894
92895
  operations: ["comments.create"]
92895
92896
  },
92896
92897
  rewrite_block: {
92897
- hint: "selector, text, changeMode? — REPLACES the whole block text using the document API's formatting preservation. For appending a NEW paragraph/clause use insert_paragraphs, and for small wording edits prefer replace_text with a minimal find span",
92898
+ hint: "selector, text, changeMode? — REPLACES the whole block text. Omitted tab controls are deleted: for a tab-aligned paragraph, use replace_text with a minimal find span to change a name/placeholder instead. For appending a NEW paragraph/clause use insert_paragraphs",
92898
92899
  group: "Text/structure actions",
92899
92900
  groupOrder: 7,
92900
92901
  args: ["text", "selector", "changeMode", "evidence"],
@@ -96324,7 +96325,7 @@ var init_operation_definitions = __esm(() => {
96324
96325
  },
96325
96326
  get_content: {
96326
96327
  toolName: "superdoc_get_content",
96327
- description: "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. " + 'Action "blocks" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. ' + 'When you need to evaluate or rewrite existing paragraphs or clauses, prefer action "blocks" with includeText:true so you can identify the correct block and then target it by nodeId. ' + 'Actions "text", "markdown", and "html" return compact strings. Actions "markdown_projection" and "html_projection" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. ' + "Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. " + 'Action "info" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. ' + 'The "blocks" action supports pagination via "offset" and "limit", filtering via "nodeTypes", and numbering projection via "reviewMode". Other actions ignore pagination and filtering parameters. ' + "This tool never modifies the document. " + "Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.",
96328
+ description: "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. " + 'Action "blocks" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Paragraph-class blocks include tabCount, the number of U+0009 tabs in the complete text even if the 80-character preview trims or truncates them. If tabCount is positive, read includeText:true before editing; do not treat a preview space as proof that there is no tab. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. ' + 'When you need to evaluate or rewrite existing paragraphs or clauses, prefer action "blocks" with includeText:true so you can identify the correct block and then target it by nodeId. ' + 'Actions "text", "markdown", and "html" return compact strings. Actions "markdown_projection" and "html_projection" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. ' + "Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. " + 'Action "info" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. ' + 'The "blocks" action supports pagination via "offset" and "limit", filtering via "nodeTypes", and numbering projection via "reviewMode". Other actions ignore pagination and filtering parameters. ' + "This tool never modifies the document. " + "Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.",
96328
96329
  inputExamples: [
96329
96330
  { action: "blocks" },
96330
96331
  { action: "blocks", includeText: true, offset: 0, limit: 20, reviewMode: "final" },
@@ -96616,7 +96617,7 @@ Complete new body.`,
96616
96617
  section: { toolName: "superdoc_section", description: "Page layout, margins, columns" },
96617
96618
  mutations: {
96618
96619
  toolName: "superdoc_mutations",
96619
- description: "All steps succeed or all fail; no partial application. " + "Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. " + "Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. " + 'Each step has an id, an op, a "where" clause for targeting ({by:"select", select:{...}, require:"first"|"exactlyOne"|"all"} or {by:"ref", ref:"..."} or {by:"block", nodeType:"paragraph", nodeId:"..."}), and "args" with operation-specific parameters. ' + 'Use {by:"block", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action "blocks" without relying on text matching. ' + 'For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:"blocks" and includeText:true, then rewrite the matching block by nodeId. ' + 'Use {by:"select"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. ' + 'For create steps, "where" targets an existing anchor block and args.position ("before" or "after") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. ' + 'For format.apply with require "all", use a node selector to format every heading or paragraph at once: {by:"select", select:{type:"node", nodeType:"heading"}, require:"all"}. ' + "Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. " + 'Action "preview" dry-runs the plan. Action "apply" executes it. ' + "If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. " + "Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.",
96620
+ description: "All steps succeed or all fail; no partial application. " + "Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. " + "Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. " + 'Each step has an id, an op, a "where" clause for targeting ({by:"select", select:{...}, require:"first"|"exactlyOne"|"all"} or {by:"ref", ref:"..."} or {by:"block", nodeType:"paragraph", nodeId:"..."}), and "args" with operation-specific parameters. ' + 'Use {by:"block", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action "blocks" without relying on text matching. ' + 'For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:"blocks" and includeText:true, then rewrite the matching block by nodeId. ' + 'For a placeholder or name within a tab-separated paragraph, use a text.rewrite where.by:"select" targeting only the exact placeholder; a whole-block plain-text rewrite omitting tabs deletes those controls. ' + 'Use {by:"select"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. ' + 'For create steps, "where" targets an existing anchor block and args.position ("before" or "after") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. ' + 'For format.apply with require "all", use a node selector to format every heading or paragraph at once: {by:"select", select:{type:"node", nodeType:"heading"}, require:"all"}. ' + "Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. " + 'Action "preview" dry-runs the plan. Action "apply" executes it. ' + "If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. " + "Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.",
96620
96621
  inputExamples: [
96621
96622
  {
96622
96623
  action: "apply",
@@ -109129,6 +109130,11 @@ var init_schemas4 = __esm(() => {
109129
109130
  oneOf: [{ type: "string" }, { type: "null" }],
109130
109131
  description: "Full flattened block text when requested with includeText."
109131
109132
  },
109133
+ tabCount: {
109134
+ type: "integer",
109135
+ minimum: 0,
109136
+ description: "Tabs in complete paragraph text, even beyond the preview."
109137
+ },
109132
109138
  isEmpty: { type: "boolean" },
109133
109139
  styleId: { oneOf: [{ type: "string" }, { type: "null" }], description: "Named paragraph style." },
109134
109140
  fontFamily: { type: "string", description: "Font family from first text run." },
@@ -124582,6 +124588,13 @@ function parseItem(source, item, ctx) {
124582
124588
  return [];
124583
124589
  case "r":
124584
124590
  return parseRun(source, item, ctx);
124591
+ case "br":
124592
+ if (!isWordprocessingElement(item, ctx.namespaceBindings)) {
124593
+ return [buildDescendingContainer(source, item, ctx, "unknown", "fail-closed")];
124594
+ }
124595
+ return [makeContainerPiece(item, ctx, "unknown", "fail-closed", [
124596
+ buildControlPiece(item, ctx, null, item.byteStart, item.byteEnd, item.charStart, item.charEnd, "", "")
124597
+ ])];
124585
124598
  case "ins":
124586
124599
  case "del":
124587
124600
  case "moveFrom":
@@ -125588,6 +125601,9 @@ function projectElement(xml, elementStart, element, deleted, instruction, namesp
125588
125601
  if (element.selfClosing) {
125589
125602
  if (localName === "r")
125590
125603
  projectRun2(xml, elementStart, element, deleted, instruction, namespaceScopes, collector);
125604
+ if (localName === "br" && isWordprocessingElement2(xml, elementStart, element, namespaceScopes, collector.inferredWordPrefix))
125605
+ appendControl(collector, `
125606
+ `, deleted);
125591
125607
  return;
125592
125608
  }
125593
125609
  switch (localName) {
@@ -125599,6 +125615,16 @@ function projectElement(xml, elementStart, element, deleted, instruction, namesp
125599
125615
  case "r":
125600
125616
  projectRun2(xml, elementStart, element, deleted, instruction, namespaceScopes, collector);
125601
125617
  return;
125618
+ case "br":
125619
+ if (isWordprocessingElement2(xml, elementStart, element, namespaceScopes, collector.inferredWordPrefix)) {
125620
+ appendControl(collector, `
125621
+ `, deleted);
125622
+ return;
125623
+ }
125624
+ withNamespaceScope(namespaceScopes, elementStart, element.openEnd, () => {
125625
+ projectChildSequence(xml, element.openEnd, element.closeStart, deleted, instruction, namespaceScopes, collector);
125626
+ });
125627
+ return;
125602
125628
  case "del":
125603
125629
  case "moveFrom":
125604
125630
  withNamespaceScope(namespaceScopes, elementStart, element.openEnd, () => {
@@ -133951,6 +133977,50 @@ var init_note_xml_validation = __esm(() => {
133951
133977
  });
133952
133978
 
133953
133979
  // ../../../v2/document-compare/src/replayability.ts
133980
+ function preservedBookmarkRunTokens(xml) {
133981
+ const markerTags = [...xml.matchAll(/<([\w.-]+:)?bookmark(Start|End)\b[^<>]*?\/>/g)];
133982
+ const markerOpenings = [...xml.matchAll(/<(?:[\w.-]+:)?bookmark(?:Start|End)\b/g)];
133983
+ if (markerTags.length !== 2 || markerOpenings.length !== 2 || markerTags[0][2] !== "Start" || markerTags[1][2] !== "End")
133984
+ return null;
133985
+ let valid = true;
133986
+ let depth = 0;
133987
+ const markers = [];
133988
+ const parser = new import_saxes3.SaxesParser({
133989
+ xmlns: true,
133990
+ additionalNamespaces: replayAdditionalNamespaces(xml)
133991
+ });
133992
+ parser.on("error", () => {
133993
+ valid = false;
133994
+ });
133995
+ parser.on("opentag", (tag) => {
133996
+ depth += 1;
133997
+ if (tag.local !== "bookmarkStart" && tag.local !== "bookmarkEnd")
133998
+ return;
133999
+ const attributes = Object.values(tag.attributes);
134000
+ const id = attributes.find((attribute) => attribute.uri === W_NS && attribute.local === "id")?.value;
134001
+ const name = attributes.find((attribute) => attribute.uri === W_NS && attribute.local === "name")?.value ?? null;
134002
+ if (tag.uri !== W_NS || !tag.isSelfClosing || depth !== 2 || !id || !/^\d+$/.test(id) || tag.local === "bookmarkStart" && !name || tag.local === "bookmarkEnd" && name !== null)
134003
+ valid = false;
134004
+ markers.push({ kind: tag.local, id: id ?? "", name });
134005
+ });
134006
+ parser.on("closetag", () => {
134007
+ depth -= 1;
134008
+ });
134009
+ try {
134010
+ parser.write(`<replay>${xml}</replay>`).close();
134011
+ } catch {
134012
+ valid = false;
134013
+ }
134014
+ if (!valid || depth !== 0 || markers.length !== 2 || markers[0].kind !== "bookmarkStart" || markers[1].kind !== "bookmarkEnd" || markers[0].id !== markers[1].id)
134015
+ return null;
134016
+ return {
134017
+ startXml: markerTags[0][0],
134018
+ endXml: markerTags[1][0],
134019
+ leadingXml: xml.slice(0, markerTags[0].index),
134020
+ trailingXml: xml.slice(markerTags[1].index + markerTags[1][0].length),
134021
+ strippedXml: xml.slice(0, markerTags[0].index) + xml.slice(markerTags[0].index + markerTags[0][0].length, markerTags[1].index) + xml.slice(markerTags[1].index + markerTags[1][0].length)
134022
+ };
134023
+ }
133954
134024
  function replayAdditionalNamespaces(xml) {
133955
134025
  const implicitNamespaces = { w: W_NS };
133956
134026
  const firstElement = /<([A-Za-z_][\w.-]*)(?::[A-Za-z_][\w.-]*)?(?=[\s/>])/.exec(xml);
@@ -134078,6 +134148,13 @@ function parseDirectRunSequence(xml, allowRevisions) {
134078
134148
  originalText += value;
134079
134149
  }
134080
134150
  };
134151
+ const isProofingBoundary = (tag) => {
134152
+ if (tag.uri !== W_NS || tag.local !== "proofErr" || !tag.isSelfClosing)
134153
+ return false;
134154
+ const attributes = Object.values(tag.attributes);
134155
+ const typeAttributes = attributes.filter((attribute) => typeof attribute !== "string" && attribute.uri === W_NS && attribute.local === "type");
134156
+ return typeAttributes.length === 1 && typeof typeAttributes[0] !== "string" && PROOFING_BOUNDARY_TYPES.has(typeAttributes[0].value) && attributes.every((attribute) => typeof attribute !== "string" && (attribute === typeAttributes[0] || attribute.uri === "http://www.w3.org/2000/xmlns/" && (attribute.local === tag.prefix || attribute.local === "w" || tag.prefix === "" && attribute.local === "xmlns") && attribute.value === W_NS));
134157
+ };
134081
134158
  const parser = new import_saxes3.SaxesParser({
134082
134159
  xmlns: true,
134083
134160
  additionalNamespaces: replayAdditionalNamespaces(xml)
@@ -134094,6 +134171,10 @@ function parseDirectRunSequence(xml, allowRevisions) {
134094
134171
  return;
134095
134172
  }
134096
134173
  if (parent === "wrapper") {
134174
+ if (isProofingBoundary(tag)) {
134175
+ stack.push("control");
134176
+ return;
134177
+ }
134097
134178
  rootCount += 1;
134098
134179
  if (tag.uri === W_NS && tag.local === "r") {
134099
134180
  stack.push("run");
@@ -134124,6 +134205,10 @@ function parseDirectRunSequence(xml, allowRevisions) {
134124
134205
  return;
134125
134206
  }
134126
134207
  if (parent === "revision") {
134208
+ if (isProofingBoundary(tag)) {
134209
+ stack.push("control");
134210
+ return;
134211
+ }
134127
134212
  if (tag.uri !== W_NS || tag.local !== "r")
134128
134213
  valid = false;
134129
134214
  activeRevisionRunCount += 1;
@@ -134550,13 +134635,14 @@ function validateRunContents(xml, options) {
134550
134635
  if (xml === "")
134551
134636
  return { ok: true };
134552
134637
  const replayXml = stripAllowedNoteReferences(xml, options);
134553
- if (replayXml === null || UNSUPPORTED_XML.test(replayXml) || RELATIONSHIP_REFERENCE.test(replayXml)) {
134638
+ const withoutBookmarks = replayXml !== null && /<(?:[\w.-]+:)?bookmark(?:Start|End)\b/.test(replayXml) ? preservedBookmarkRunTokens(replayXml)?.strippedXml ?? null : replayXml;
134639
+ if (withoutBookmarks === null || UNSUPPORTED_XML.test(withoutBookmarks) || RELATIONSHIP_REFERENCE.test(withoutBookmarks)) {
134554
134640
  return { ok: false, reason: "target runs require unsupported structural or relationship replay" };
134555
134641
  }
134556
- const namespaceResult = validateReplayNamespaces(replayXml, "target runs use namespace prefixes that cannot be replayed safely");
134642
+ const namespaceResult = validateReplayNamespaces(withoutBookmarks.replace(PROOFING_BOUNDARY_TAG_RE, ""), "target runs use namespace prefixes that cannot be replayed safely");
134557
134643
  if (!namespaceResult.ok)
134558
134644
  return namespaceResult;
134559
- if (!parseDirectRunSequence(replayXml, false)) {
134645
+ if (!parseDirectRunSequence(withoutBookmarks, false)) {
134560
134646
  return { ok: false, reason: "target runs contain unsupported inline markup" };
134561
134647
  }
134562
134648
  return { ok: true };
@@ -134580,7 +134666,7 @@ function validateCompareReplayPayload(payload, options = {}) {
134580
134666
  if (payload.targetRevisionReplay.policy !== "preserve-target-revision-set" || payload.targetRunXml === undefined) {
134581
134667
  return { ok: false, reason: "target revision replay policy is incomplete" };
134582
134668
  }
134583
- const namespaceResult = validateReplayNamespaces(payload.targetRunXml, "target revision runs use namespace prefixes that cannot be replayed safely");
134669
+ const namespaceResult = validateReplayNamespaces(payload.targetRunXml.replace(PROOFING_BOUNDARY_TAG_RE, ""), "target revision runs use namespace prefixes that cannot be replayed safely");
134584
134670
  if (!namespaceResult.ok)
134585
134671
  return namespaceResult;
134586
134672
  const inspected = inspectDirectParagraphRevisionRuns(payload.targetRunXml);
@@ -134600,12 +134686,14 @@ function validateCompareReplayPayload(payload, options = {}) {
134600
134686
  }
134601
134687
  return { ok: true };
134602
134688
  }
134603
- var import_saxes3, UNSUPPORTED_XML, RELATIONSHIP_REFERENCE, W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", DIRECT_RUN_PROPERTY_ELEMENTS;
134689
+ var import_saxes3, UNSUPPORTED_XML, RELATIONSHIP_REFERENCE, W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", PROOFING_BOUNDARY_TYPES, PROOFING_BOUNDARY_TAG_RE, DIRECT_RUN_PROPERTY_ELEMENTS;
134604
134690
  var init_replayability = __esm(() => {
134605
134691
  init_note_xml_validation();
134606
134692
  import_saxes3 = __toESM(require_saxes(), 1);
134607
- UNSUPPORTED_XML = /<(?:[\w.-]+:)?(?:hyperlink|drawing|pict|object|fldSimple|fldChar|instrText|commentReference|commentRangeStart|commentRangeEnd|bookmarkStart|bookmarkEnd|permStart|permEnd|sdt|customXml|oMath|oMathPara|footnoteReference|endnoteReference|ins|del|moveFrom|moveTo|pPrChange|rPrChange|sectPr)\b/i;
134693
+ UNSUPPORTED_XML = /<(?:[\w.-]+:)?(?:hyperlink|drawing|pict|object|fldSimple|fldChar|instrText|commentReference|commentRangeStart|commentRangeEnd|permStart|permEnd|sdt|customXml|oMath|oMathPara|footnoteReference|endnoteReference|ins|del|moveFrom|moveTo|pPrChange|rPrChange|sectPr)\b/i;
134608
134694
  RELATIONSHIP_REFERENCE = /\s(?:r:id|r:embed|r:link)\s*=/i;
134695
+ PROOFING_BOUNDARY_TYPES = new Set(["spellStart", "spellEnd", "gramStart", "gramEnd"]);
134696
+ PROOFING_BOUNDARY_TAG_RE = /<(?:[A-Za-z_][\w.-]*:)?proofErr\b(?:[^>"']|"[^"]*"|'[^']*')*\/>/g;
134609
134697
  DIRECT_RUN_PROPERTY_ELEMENTS = new Set([
134610
134698
  "rStyle",
134611
134699
  "rFonts",
@@ -134745,7 +134833,10 @@ function canonicalName(namespace, localName2) {
134745
134833
  }
134746
134834
  function computeRunSignature(runXml) {
134747
134835
  const canonicalXml = canonicalizeXmlForSignature(runXml);
134748
- const normalized = normalizeRunXmlForSignature(canonicalXml.replace(REVISION_ATTR_RE, "").replace(COMMENT_MARKUP_RE, (markup, name, attributes) => isWordprocessingElement3(name, attributes, canonicalXml) ? "" : markup).replace(NOTE_REFERENCE_MARKUP_RE, (markup, runName, runAttributes, _rPrName, nestedName, nestedAttributes, standaloneName, standaloneAttributes) => {
134836
+ const normalized = normalizeRunXmlForSignature(canonicalXml.replace(REVISION_ATTR_RE, (attribute, offset, xml) => {
134837
+ const precedingTag = xml.slice(xml.lastIndexOf("<", offset), offset);
134838
+ return /^<w:proofErr(?:\s|$)/.test(precedingTag) ? attribute : "";
134839
+ }).replace(REVISION_ELEMENT_RE, (markup) => markup.replace(REVISION_ID_ATTR_RE, "")).replace(COMMENT_MARKUP_RE, (markup, name, attributes) => isWordprocessingElement3(name, attributes, canonicalXml) ? "" : markup).replace(NOTE_REFERENCE_MARKUP_RE, (markup, runName, runAttributes, _rPrName, nestedName, nestedAttributes, standaloneName, standaloneAttributes) => {
134749
134840
  const name = nestedName ?? standaloneName;
134750
134841
  const attributes = nestedAttributes ?? standaloneAttributes;
134751
134842
  if (!isWordprocessingElement3(name, attributes, canonicalXml))
@@ -134772,7 +134863,7 @@ function normalizeRunXmlForSignature(runXml) {
134772
134863
  continue;
134773
134864
  const index = match.index ?? 0;
134774
134865
  if (index > lastIndex) {
134775
- const interstitial = runXml.slice(lastIndex, index);
134866
+ const interstitial = stripProofingBoundaries(runXml.slice(lastIndex, index));
134776
134867
  if (interstitial.trim().length > 0) {
134777
134868
  pieces.push(interstitial);
134778
134869
  activeTextProperties = "";
@@ -134801,12 +134892,15 @@ function normalizeRunXmlForSignature(runXml) {
134801
134892
  if (!sawRun)
134802
134893
  return runXml;
134803
134894
  if (lastIndex < runXml.length) {
134804
- const trailing = runXml.slice(lastIndex);
134895
+ const trailing = stripProofingBoundaries(runXml.slice(lastIndex));
134805
134896
  if (trailing.trim().length > 0)
134806
134897
  pieces.push(trailing);
134807
134898
  }
134808
134899
  return pieces.join("");
134809
134900
  }
134901
+ function stripProofingBoundaries(xml) {
134902
+ return xml.replace(PROOFING_BOUNDARY_MARKUP_RE, "");
134903
+ }
134810
134904
  function normalizeRunControls(content, namespaceContext) {
134811
134905
  return [...content.matchAll(RUN_CONTROL_RE)].filter((match) => isWordprocessingElement3(match[1], match[3], namespaceContext)).map((match) => {
134812
134906
  const attributes = [...match[3].matchAll(ATTRIBUTE_RE)].filter((attribute) => attribute[1] !== "xmlns" && !attribute[1].startsWith("xmlns:")).map((attribute) => {
@@ -134839,10 +134933,13 @@ function isWordprocessingElement3(name, attributes, namespaceContext) {
134839
134933
  return true;
134840
134934
  return (declaration[2] ?? declaration[3] ?? "") === WORDPROCESSINGML_NAMESPACE2;
134841
134935
  }
134842
- var REVISION_ATTR_RE, COMMENT_MARKUP_RE, NOTE_REFERENCE_MARKUP_RE, RUN_RE, RUN_PROPERTIES_RE, EMPTY_RUN_PROPERTIES_RE, TEXTISH_RE, RUN_CONTROL_RE, ATTRIBUTE_RE, WORDPROCESSINGML_NAMESPACE2 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", WORDPROCESSINGML_2010_NAMESPACE = "http://schemas.microsoft.com/office/word/2010/wordml", MARKUP_COMPATIBILITY_NAMESPACE = "http://schemas.openxmlformats.org/markup-compatibility/2006", XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace", XML_TAG_RE;
134936
+ var REVISION_ATTR_RE, REVISION_ELEMENT_RE, REVISION_ID_ATTR_RE, COMMENT_MARKUP_RE, PROOFING_BOUNDARY_MARKUP_RE, NOTE_REFERENCE_MARKUP_RE, RUN_RE, RUN_PROPERTIES_RE, EMPTY_RUN_PROPERTIES_RE, TEXTISH_RE, RUN_CONTROL_RE, ATTRIBUTE_RE, WORDPROCESSINGML_NAMESPACE2 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", WORDPROCESSINGML_2010_NAMESPACE = "http://schemas.microsoft.com/office/word/2010/wordml", MARKUP_COMPATIBILITY_NAMESPACE = "http://schemas.openxmlformats.org/markup-compatibility/2006", XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace", XML_TAG_RE;
134843
134937
  var init_run_signature = __esm(() => {
134844
- REVISION_ATTR_RE = /\s+(?:w:rsid\w*|w14:paraId|w14:textId|w:id)="[^"]*"/g;
134938
+ REVISION_ATTR_RE = /\s+(?:w:rsid\w*|w14:paraId|w14:textId)="[^"]*"/g;
134939
+ REVISION_ELEMENT_RE = /<w:(?:ins|del|moveFrom|moveTo)\b[^>]*>/g;
134940
+ REVISION_ID_ATTR_RE = /\s+w:id="[^"]*"/g;
134845
134941
  COMMENT_MARKUP_RE = /<((?:[\w.-]+:)?(?:commentRangeStart|commentRangeEnd|commentReference))\b([^>]*)\/>/g;
134942
+ PROOFING_BOUNDARY_MARKUP_RE = /<w:proofErr w:type="(?:spellStart|spellEnd|gramStart|gramEnd)"\/>/g;
134846
134943
  NOTE_REFERENCE_MARKUP_RE = /<((?:[\w.-]+:)?r)\b([^>]*)>(?:<((?:[\w.-]+:)?rPr)\b[^>]*(?:\/>|>[\s\S]*?<\/\3>))?<((?:[\w.-]+:)?(?:footnoteReference|endnoteReference))\b([^>]*)\/><\/\1>|<((?:[\w.-]+:)?(?:footnoteReference|endnoteReference))\b([^>]*)\/>/g;
134847
134944
  RUN_RE = /<((?:[\w.-]+:)?r)\b([^>]*)>([\s\S]*?)<\/\1>/g;
134848
134945
  RUN_PROPERTIES_RE = /<((?:[\w.-]+:)?rPr)\b([^>]*)(?:\/>|>[\s\S]*?<\/\1>)/g;
@@ -141958,7 +142055,7 @@ var init_story_slice = __esm(() => {
141958
142055
  init_source_coverage_chunks();
141959
142056
  DEFAULT_MAX_XML_BYTES = 64 * 1024;
141960
142057
  TEXT_DECODER = new TextDecoder("utf-8", { fatal: false });
141961
- NODE_BUFFER = typeof globalThis !== "undefined" && typeof globalThis.Buffer?.byteLength === "function" ? globalThis.Buffer : null;
142058
+ NODE_BUFFER = typeof globalThis !== "undefined" && typeof globalThis.process?.versions?.node === "string" && typeof globalThis.Buffer?.byteLength === "function" ? globalThis.Buffer : null;
141962
142059
  CHUNK_SPAN_CACHE = new WeakMap;
141963
142060
  });
141964
142061
 
@@ -178481,6 +178578,37 @@ function canonicalizeDependencyValue(value) {
178481
178578
  }
178482
178579
  return value;
178483
178580
  }
178581
+ function locateSingleResidentBodyChild(state, storyId, partUri, chunk, paragraphByteStart, paragraphByteEnd, containingBodyChildOrdinal) {
178582
+ const story = state.stories.get(storyId)?.descriptor;
178583
+ const part = state.parts.get(partUri)?.descriptor;
178584
+ if (storyId !== state.mainStoryId || story?.storyId !== storyId || story.storyKind !== "main" || story.ownerPartId !== partUri || story.ownerPartUri !== partUri || part?.partId !== partUri || part.partUri !== partUri || chunk.storyId !== storyId || chunk.partUri !== partUri || chunk.storyRevision !== story.revision || !state.packageBytes.has(partUri))
178585
+ return null;
178586
+ const residentByteLength = state.packageBytes.byteLength(partUri);
178587
+ const { byteStart, byteEnd, bodyChildStartOrdinal, bodyChildEndOrdinal, contentBlockStartOrdinal, contentBlockEndOrdinal, firstChildKind, lastChildKind, bodyChildCount, contentBlockCount, markerChildCount, opaqueAnchorCount, containsTrivia, triviaByteSpans } = chunk;
178588
+ if (residentByteLength == null || !Number.isSafeInteger(residentByteLength) || !Number.isSafeInteger(byteStart) || !Number.isSafeInteger(byteEnd) || byteStart < 0 || byteEnd <= byteStart || byteEnd > residentByteLength || !Number.isSafeInteger(paragraphByteStart) || !Number.isSafeInteger(paragraphByteEnd) || paragraphByteStart < byteStart || paragraphByteEnd <= paragraphByteStart || paragraphByteEnd > byteEnd || !Number.isSafeInteger(containingBodyChildOrdinal) || containingBodyChildOrdinal < 0 || bodyChildCount !== 1 || bodyChildStartOrdinal !== containingBodyChildOrdinal || bodyChildEndOrdinal !== containingBodyChildOrdinal || contentBlockCount !== 1 || contentBlockStartOrdinal == null || !Number.isSafeInteger(contentBlockStartOrdinal) || contentBlockStartOrdinal < 0 || contentBlockEndOrdinal !== contentBlockStartOrdinal || markerChildCount !== 0 || opaqueAnchorCount !== 0 || firstChildKind !== lastChildKind || firstChildKind !== "p" && firstChildKind !== "tbl" && firstChildKind !== "sdt")
178589
+ return null;
178590
+ let ownerByteStart = byteStart;
178591
+ if (triviaByteSpans.length === 0) {
178592
+ if (containsTrivia)
178593
+ return null;
178594
+ } else {
178595
+ if (!containsTrivia || triviaByteSpans.length !== 1)
178596
+ return null;
178597
+ const [triviaStart, triviaEnd] = triviaByteSpans[0];
178598
+ if (triviaStart !== byteStart || !Number.isSafeInteger(triviaEnd) || triviaEnd <= triviaStart || triviaEnd >= byteEnd)
178599
+ return null;
178600
+ ownerByteStart = triviaEnd;
178601
+ }
178602
+ if (paragraphByteStart < ownerByteStart)
178603
+ return null;
178604
+ return {
178605
+ status: "ok",
178606
+ bodyChildOrdinal: bodyChildStartOrdinal,
178607
+ localName: firstChildKind,
178608
+ byteStart: ownerByteStart,
178609
+ byteEnd
178610
+ };
178611
+ }
178484
178612
  function locateExactResidentBodyChild(state, storyId, partUri, byteStart, byteEnd, cache) {
178485
178613
  const chunks = findChunksContainingRange(state.sourceCoverage.get(storyId)?.chunks ?? [], partUri, byteStart, byteEnd);
178486
178614
  if (chunks.length === 0)
@@ -178539,14 +178667,20 @@ function locateContainingResidentBodyChild(state, storyId, partUri, paragraphByt
178539
178667
  const chunks = findChunksContainingRange(state.sourceCoverage.get(storyId)?.chunks ?? [], partUri, paragraphByteStart, paragraphByteEnd);
178540
178668
  if (chunks.length === 0)
178541
178669
  return { status: "failed", reason: "body-child-not-found" };
178542
- const bytes = state.packageBytes.get(partUri);
178543
- if (!bytes)
178544
- return { status: "failed", reason: "body-child-not-found" };
178670
+ let bytes;
178545
178671
  const matches = [];
178546
178672
  for (const chunk of chunks) {
178547
178673
  const relativeOrdinal = containingBodyChildOrdinal - chunk.bodyChildStartOrdinal;
178548
178674
  if (relativeOrdinal < 0 || containingBodyChildOrdinal > chunk.bodyChildEndOrdinal)
178549
178675
  continue;
178676
+ const singleChild = locateSingleResidentBodyChild(state, storyId, partUri, chunk, paragraphByteStart, paragraphByteEnd, containingBodyChildOrdinal);
178677
+ if (singleChild) {
178678
+ matches.push(singleChild);
178679
+ continue;
178680
+ }
178681
+ bytes ?? (bytes = state.packageBytes.get(partUri));
178682
+ if (!bytes)
178683
+ return { status: "failed", reason: "body-child-not-found" };
178550
178684
  const chunkXml = TD24.decode(bytes.subarray(chunk.byteStart, chunk.byteEnd));
178551
178685
  const spans = listTopLevelElementSpans2(chunkXml);
178552
178686
  const span = spans[relativeOrdinal];
@@ -212431,10 +212565,16 @@ function prepareParagraphDelete(state, op) {
212431
212565
  return { ok: true, noOp: true, target, bytes, xml, paraXml, successor };
212432
212566
  }
212433
212567
  const structural = classifyParagraphStructural(paraXml, {
212434
- allowRelationshipBearing: true
212568
+ allowRelationshipBearing: true,
212569
+ allowInlineSdtDeletion: changeMode === "direct"
212435
212570
  });
212436
212571
  if (!structural.ok)
212437
212572
  return { ok: false, reason: structural.reason };
212573
+ if (changeMode === "direct") {
212574
+ const sdtFailure = validateDirectParagraphInlineSdtDeletion(bytes, target.entry.byteStart, paraXml);
212575
+ if (sdtFailure)
212576
+ return { ok: false, reason: sdtFailure };
212577
+ }
212438
212578
  const namespaces = /(?:drawing|pict)/.test(paraXml) ? namespaceBindingsAtPosition(bytes, target.entry.byteStart, matchParagraphOpenTag(paraXml) ?? "") : undefined;
212439
212579
  const hasDrawing = namespaces && collectXmlElementRanges(paraXml, {
212440
212580
  fragment: true,
@@ -214170,6 +214310,30 @@ function tableOrdinalAtOrBeforeByte(catalog, byteOffset) {
214170
214310
  }
214171
214311
  return low - 1;
214172
214312
  }
214313
+ function validateDirectParagraphInlineSdtDeletion(bytes, paragraphStartByte, paraXml) {
214314
+ const sdtTags = paraXml.match(/<\s*(?:[\w.-]+:)?sdt\b/g);
214315
+ if (!sdtTags)
214316
+ return null;
214317
+ const namespaces = namespaceBindingsAtPosition(bytes, paragraphStartByte, matchParagraphOpenTag(paraXml) ?? "");
214318
+ const pieces = parseParagraphInlineMap(paraXml, namespaces).pieces;
214319
+ let sdtCount = 0;
214320
+ for (const piece of pieces) {
214321
+ if (piece.kind !== "container" || piece.containerKind !== "sdt")
214322
+ continue;
214323
+ sdtCount += 1;
214324
+ const policy = classifySdtMutationPolicy(piece);
214325
+ if (!policy.ok)
214326
+ return policy.reason;
214327
+ if (!policy.wrapperDeletable)
214328
+ return "text-range-in-sdt";
214329
+ const blocked = findBlockingSdtChild(piece.children);
214330
+ if (blocked)
214331
+ return blocked;
214332
+ }
214333
+ if (sdtCount !== sdtTags.length)
214334
+ return "text-range-in-sdt";
214335
+ return null;
214336
+ }
214173
214337
  function classifyParagraphStructural(paraXml, options = {}) {
214174
214338
  if (/<[\w.-]+:moveFrom\b/.test(paraXml) || /<[\w.-]+:moveTo\b/.test(paraXml)) {
214175
214339
  return { ok: false, reason: "paragraph-tracked-wrapper-unsupported" };
@@ -214177,7 +214341,7 @@ function classifyParagraphStructural(paraXml, options = {}) {
214177
214341
  if (!options.fieldsVerified && (/<[\w.-]+:fldChar\b/.test(paraXml) || /<[\w.-]+:instrText\b/.test(paraXml) || /<[\w.-]+:fldSimple\b/.test(paraXml))) {
214178
214342
  return { ok: false, reason: "paragraph-tracked-wrapper-unsupported" };
214179
214343
  }
214180
- if (/<[\w.-]+:sdt\b/.test(paraXml)) {
214344
+ if (!options.allowInlineSdtDeletion && /<[\w.-]+:sdt\b/.test(paraXml)) {
214181
214345
  return { ok: false, reason: "paragraph-tracked-wrapper-unsupported" };
214182
214346
  }
214183
214347
  let relationshipBearing = false;
@@ -214964,6 +215128,7 @@ var init_compile_paragraph_ops = __esm(() => {
214964
215128
  init_sd_document_state();
214965
215129
  init_text_offset_mapping();
214966
215130
  init_paragraph_inline_model();
215131
+ init_compile_edit_text_inline_plan();
214967
215132
  init_structural_boundary_map();
214968
215133
  init_paragraph_section_collapse();
214969
215134
  init_paragraph_delete_cell_boundary();
@@ -219917,15 +220082,31 @@ function canonicalizeCompareBodyRunXml(xml, referenceSteps) {
219917
220082
  let result = "";
219918
220083
  let cursor = 0;
219919
220084
  for (const token of tokens) {
219920
- const canonicalGap = canonicalizeWordprocessingReplayXml(xml.slice(cursor, token.start));
220085
+ const canonicalGap = canonicalizeProofingRunGap(xml.slice(cursor, token.start));
219921
220086
  if (canonicalGap === null)
219922
220087
  return null;
219923
220088
  result += canonicalGap + token.xml;
219924
220089
  cursor = token.end;
219925
220090
  }
219926
- const canonicalTail = canonicalizeWordprocessingReplayXml(xml.slice(cursor));
220091
+ const canonicalTail = canonicalizeProofingRunGap(xml.slice(cursor));
219927
220092
  return canonicalTail === null ? null : result + canonicalTail;
219928
220093
  }
220094
+ function canonicalizeProofingRunGap(xml) {
220095
+ const markerRe = /<(?:[A-Za-z_][\w.-]*:)?proofErr\b(?:[^>"']|"[^"]*"|'[^']*')*\/>/g;
220096
+ let result = "";
220097
+ let cursor = 0;
220098
+ for (const marker of xml.matchAll(markerRe)) {
220099
+ const index = marker.index ?? 0;
220100
+ const before = canonicalizeWordprocessingReplayXml(xml.slice(cursor, index));
220101
+ const canonicalMarker = canonicalizeWordprocessingReplayXml(marker[0]);
220102
+ if (before === null || canonicalMarker === null)
220103
+ return null;
220104
+ result += before + canonicalMarker;
220105
+ cursor = index + marker[0].length;
220106
+ }
220107
+ const trailing = canonicalizeWordprocessingReplayXml(xml.slice(cursor));
220108
+ return trailing === null ? null : result + trailing;
220109
+ }
219929
220110
  function collectProtectedReferenceTokens(xml, referenceSteps) {
219930
220111
  const tokens = [];
219931
220112
  for (let index = 0;index < referenceSteps.length; index += 1) {
@@ -220731,6 +220912,26 @@ function runComparePayloadInsertCommit(input, storyId, step2, changeMode, option
220731
220912
  });
220732
220913
  return withParagraphInsertionCatalogRefs(input, receipt2, paragraph.wordIds, storyId);
220733
220914
  }
220915
+ function unchangedBookmarkReferenceSteps(oldRuns, targetRuns) {
220916
+ const oldHasMarker = /<(?:[\w.-]+:)?bookmark(?:Start|End)\b/.test(oldRuns);
220917
+ const targetHasMarker = /<(?:[\w.-]+:)?bookmark(?:Start|End)\b/.test(targetRuns);
220918
+ if (!oldHasMarker && !targetHasMarker)
220919
+ return [];
220920
+ const oldBookmark = preservedBookmarkRunTokens(oldRuns);
220921
+ const targetBookmark = preservedBookmarkRunTokens(targetRuns);
220922
+ if (!oldBookmark || !targetBookmark || oldBookmark.startXml !== targetBookmark.startXml || oldBookmark.endXml !== targetBookmark.endXml || oldBookmark.leadingXml !== targetBookmark.leadingXml || oldBookmark.trailingXml !== targetBookmark.trailingXml)
220923
+ return null;
220924
+ const startBindings = replayFragmentNamespaceBindings(oldBookmark.startXml);
220925
+ const endBindings = replayFragmentNamespaceBindings(oldBookmark.endXml);
220926
+ const bookmarkBindings = { ...startBindings, ...endBindings };
220927
+ return [{
220928
+ kind: "bookmark-start",
220929
+ beforeXml: oldBookmark.startXml,
220930
+ afterXml: oldBookmark.startXml,
220931
+ beforeNamespaceBindings: bookmarkBindings,
220932
+ afterNamespaceBindings: bookmarkBindings
220933
+ }];
220934
+ }
220734
220935
  function runCompareBodyEditCommit(input, storyId, step2, changeMode, options, pushHistory, referenceSteps) {
220735
220936
  const { state, txId } = input;
220736
220937
  const partUri = storyPartUriFromStoryId2(storyId);
@@ -220748,6 +220949,14 @@ function runCompareBodyEditCommit(input, storyId, step2, changeMode, options, pu
220748
220949
  const pPrXml = findFirstElement(paraXml, "pPr")?.xml ?? "";
220749
220950
  const targetParagraphPropertiesXml = step2.targetParagraphPropertiesXml === undefined ? undefined : rewriteCanonicalWordprocessingPrefix(step2.targetParagraphPropertiesXml, paragraphWrapper.wordprocessingPrefix);
220750
220951
  const newRuns = rewriteCanonicalWordprocessingPrefix(step2.targetRunXml ?? renderParagraphRuns(step2.text), paragraphWrapper.wordprocessingPrefix);
220952
+ const openEnd = paragraphWrapper.contentStart;
220953
+ const closeStart = paraXml.lastIndexOf("</");
220954
+ const innerXml = closeStart > openEnd ? paraXml.slice(openEnd, closeStart) : "";
220955
+ const oldRunsRaw = stripElements(innerXml, "pPr");
220956
+ const stableBookmarkSteps = unchangedBookmarkReferenceSteps(oldRunsRaw, newRuns);
220957
+ if (stableBookmarkSteps === null && referenceSteps.length === 0)
220958
+ return rejected14(txId, "unsupported-context");
220959
+ const protectedReferenceSteps = [...referenceSteps, ...stableBookmarkSteps ?? []];
220751
220960
  let newParaXml;
220752
220961
  const createdRefs = [];
220753
220962
  if (changeMode === "tracked") {
@@ -220763,12 +220972,10 @@ function runCompareBodyEditCommit(input, storyId, step2, changeMode, options, pu
220763
220972
  storyId
220764
220973
  });
220765
220974
  }
220766
- const openEnd = paragraphWrapper.contentStart;
220767
- const closeStart = paraXml.lastIndexOf("</");
220768
- const innerXml = closeStart > openEnd ? paraXml.slice(openEnd, closeStart) : "";
220769
- const oldRunsRaw = stripElements(innerXml, "pPr");
220770
220975
  const oldRunsDel = oldRunsRaw.replace(/<((?:[\w.-]+:)?)t(\b[^>]*)>/g, "<$1delText$2>").replace(/<\/((?:[\w.-]+:)?)t>/g, "</$1delText>");
220771
- const protectedRuns = renderTrackedRunsWithProtectedReferences(oldRunsRaw, newRuns, referenceSteps, stamps, paragraphWrapper.wordprocessingPrefix);
220976
+ const protectedRuns = renderTrackedRunsWithProtectedReferences(oldRunsRaw, newRuns, protectedReferenceSteps, stamps, paragraphWrapper.wordprocessingPrefix);
220977
+ if (!protectedRuns && stableBookmarkSteps?.length)
220978
+ return rejected14(txId, "unsupported-context");
220772
220979
  const delXml = protectedRuns?.xml ?? (oldRunsDel ? wrapTracked("del", oldRunsDel, stamps.del, "rsidDel", paragraphWrapper.wordprocessingPrefix) : "");
220773
220980
  const insXml = protectedRuns ? "" : newRuns ? wrapTracked("ins", newRuns, stamps.ins, "rsidR", paragraphWrapper.wordprocessingPrefix) : "";
220774
220981
  const trackedPPrXml = buildTrackedParagraphProperties(targetParagraphPropertiesXml, pPrXml, stamps.paragraphProperties ?? stamps.ins, paragraphWrapper.wordprocessingPrefix);
@@ -220852,6 +221059,10 @@ function runCompareBodyFormatEditCommit(input, storyId, step2, changeMode, optio
220852
221059
  const innerXml = closeStart > openEnd ? paraXml.slice(openEnd, closeStart) : "";
220853
221060
  const oldRunsRaw = stripElements(innerXml, "pPr");
220854
221061
  const paragraphPropertiesChanged = !equivalentParagraphProperties(targetPPrXml, pPrXml);
221062
+ const stableBookmarkSteps = unchangedBookmarkReferenceSteps(oldRunsRaw, targetRunXml);
221063
+ if (stableBookmarkSteps === null && referenceSteps.length === 0)
221064
+ return rejected14(txId, "unsupported-context");
221065
+ const protectedReferenceSteps = [...referenceSteps, ...stableBookmarkSteps ?? []];
220855
221066
  const baseSimpleRuns = scanSimpleRunFormatting(oldRunsRaw);
220856
221067
  const targetSimpleRuns = scanSimpleRunFormatting(targetRunXml);
220857
221068
  const paragraphPropertiesOnly = paragraphPropertiesChanged && baseSimpleRuns?.text === targetSimpleRuns?.text && computeRunSignature(targetRunXml) === computeRunSignature(oldRunsRaw);
@@ -220915,7 +221126,9 @@ function runCompareBodyFormatEditCommit(input, storyId, step2, changeMode, optio
220915
221126
  if (!stamps)
220916
221127
  return rejected14(txId, "no-author-configured");
220917
221128
  const oldRunsDel = oldRunsRaw.replace(/<((?:[\w.-]+:)?)t(\b[^>]*)>/g, "<$1delText$2>").replace(/<\/((?:[\w.-]+:)?)t>/g, "</$1delText>");
220918
- const protectedRuns = renderTrackedRunsWithProtectedReferences(oldRunsRaw, targetRunXml, referenceSteps, stamps, paragraphWrapper.wordprocessingPrefix);
221129
+ const protectedRuns = renderTrackedRunsWithProtectedReferences(oldRunsRaw, targetRunXml, protectedReferenceSteps, stamps, paragraphWrapper.wordprocessingPrefix);
221130
+ if (!protectedRuns && stableBookmarkSteps?.length)
221131
+ return rejected14(txId, "unsupported-context");
220919
221132
  const delXml = protectedRuns?.xml ?? (oldRunsDel ? wrapTracked("del", oldRunsDel, stamps.del, "rsidDel", paragraphWrapper.wordprocessingPrefix) : "");
220920
221133
  const insXml = protectedRuns ? "" : targetRunXml ? wrapTracked("ins", targetRunXml, stamps.ins, "rsidR", paragraphWrapper.wordprocessingPrefix) : "";
220921
221134
  if (delXml || insXml) {
@@ -239364,7 +239577,7 @@ function toRoman(ordinal) {
239364
239577
  }
239365
239578
  function extractParagraphText(paraXml) {
239366
239579
  let out = "";
239367
- for (const match of paraXml.matchAll(/<[\w.-]+:t\b[^>]*>([\s\S]*?)<\/[\w.-]+:t>/g)) {
239580
+ for (const match of paraXml.matchAll(/<[\w.-]+:t\b[^>]*?(?<!\/)>([\s\S]*?)<\/[\w.-]+:t>/g)) {
239368
239581
  out += decodeXmlText(match[1] ?? "");
239369
239582
  }
239370
239583
  return out;
@@ -246913,7 +247126,7 @@ function prepareHistoryReplayMutation(state, entry, direction, patchGroup) {
246913
247126
  ...contentControlRewrite.addedBlockIds,
246914
247127
  ...contentControlRewrite.contentBlockIds,
246915
247128
  ...contentControlRewrite.leadingBlockIds
246916
- ])] : collectPatchGroupTargetBlockIds(patchGroup);
247129
+ ])] : patchGroup.source.kind === "compareApply" ? [...new Set(patchGroup.source.bodySteps.map((step2) => step2.blockId))] : collectPatchGroupTargetBlockIds(patchGroup);
246917
247130
  const currentBaseRevisions = {
246918
247131
  package: state.packageRevision,
246919
247132
  perPart: {}
@@ -247167,6 +247380,8 @@ function collectPatchGroupTargetBlockIds(patchGroup) {
247167
247380
  return [...blockIds];
247168
247381
  }
247169
247382
  function inferHistoryStorySurfaceFamily(sourceKind) {
247383
+ if (sourceKind === "compareApply")
247384
+ return "compareApply";
247170
247385
  if (sourceKind === "editText")
247171
247386
  return "storyText";
247172
247387
  if (sourceKind.startsWith("list"))
@@ -251364,7 +251579,7 @@ var init_operation_definitions2 = __esm(() => {
251364
251579
  },
251365
251580
  get_content: {
251366
251581
  toolName: "superdoc_get_content",
251367
- description: "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. " + 'Action "blocks" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. ' + 'When you need to evaluate or rewrite existing paragraphs or clauses, prefer action "blocks" with includeText:true so you can identify the correct block and then target it by nodeId. ' + 'Actions "text", "markdown", and "html" return compact strings. Actions "markdown_projection" and "html_projection" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. ' + "Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. " + 'Action "info" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. ' + 'The "blocks" action supports pagination via "offset" and "limit", filtering via "nodeTypes", and numbering projection via "reviewMode". Other actions ignore pagination and filtering parameters. ' + "This tool never modifies the document. " + "Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.",
251582
+ description: "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. " + 'Action "blocks" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Paragraph-class blocks include tabCount, the number of U+0009 tabs in the complete text even if the 80-character preview trims or truncates them. If tabCount is positive, read includeText:true before editing; do not treat a preview space as proof that there is no tab. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. ' + 'When you need to evaluate or rewrite existing paragraphs or clauses, prefer action "blocks" with includeText:true so you can identify the correct block and then target it by nodeId. ' + 'Actions "text", "markdown", and "html" return compact strings. Actions "markdown_projection" and "html_projection" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. ' + "Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. " + 'Action "info" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. ' + 'The "blocks" action supports pagination via "offset" and "limit", filtering via "nodeTypes", and numbering projection via "reviewMode". Other actions ignore pagination and filtering parameters. ' + "This tool never modifies the document. " + "Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.",
251368
251583
  inputExamples: [
251369
251584
  { action: "blocks" },
251370
251585
  { action: "blocks", includeText: true, offset: 0, limit: 20, reviewMode: "final" },
@@ -251656,7 +251871,7 @@ Complete new body.`,
251656
251871
  section: { toolName: "superdoc_section", description: "Page layout, margins, columns" },
251657
251872
  mutations: {
251658
251873
  toolName: "superdoc_mutations",
251659
- description: "All steps succeed or all fail; no partial application. " + "Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. " + "Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. " + 'Each step has an id, an op, a "where" clause for targeting ({by:"select", select:{...}, require:"first"|"exactlyOne"|"all"} or {by:"ref", ref:"..."} or {by:"block", nodeType:"paragraph", nodeId:"..."}), and "args" with operation-specific parameters. ' + 'Use {by:"block", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action "blocks" without relying on text matching. ' + 'For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:"blocks" and includeText:true, then rewrite the matching block by nodeId. ' + 'Use {by:"select"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. ' + 'For create steps, "where" targets an existing anchor block and args.position ("before" or "after") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. ' + 'For format.apply with require "all", use a node selector to format every heading or paragraph at once: {by:"select", select:{type:"node", nodeType:"heading"}, require:"all"}. ' + "Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. " + 'Action "preview" dry-runs the plan. Action "apply" executes it. ' + "If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. " + "Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.",
251874
+ description: "All steps succeed or all fail; no partial application. " + "Execute multiple operations atomically in one batch. Use this for any workflow needing 2+ changes. " + "Supported step types: text (text.rewrite, text.insert, text.delete), format (format.apply), create (create.heading, create.paragraph, create.table), assert. " + 'Each step has an id, an op, a "where" clause for targeting ({by:"select", select:{...}, require:"first"|"exactlyOne"|"all"} or {by:"ref", ref:"..."} or {by:"block", nodeType:"paragraph", nodeId:"..."}), and "args" with operation-specific parameters. ' + 'Use {by:"block", nodeType, nodeId} when you want to rewrite, delete, format, or anchor against a whole known block from superdoc_get_content action "blocks" without relying on text matching. ' + 'For full-paragraph or full-clause rewrites, first call superdoc_get_content with action:"blocks" and includeText:true, then rewrite the matching block by nodeId. ' + 'For a placeholder or name within a tab-separated paragraph, use a text.rewrite where.by:"select" targeting only the exact placeholder; a whole-block plain-text rewrite omitting tabs deletes those controls. ' + 'Use {by:"select"} only for substring edits, discovery, or insertion relative to a sentence fragment; do NOT use a shortened text selector to replace an entire known block. ' + 'For create steps, "where" targets an existing anchor block and args.position ("before" or "after") controls placement. Sequential creates targeting the same anchor maintain correct order via internal position mapping. ' + 'For format.apply with require "all", use a node selector to format every heading or paragraph at once: {by:"select", select:{type:"node", nodeType:"heading"}, require:"all"}. ' + "Selectors resolve at compile time (before execution). This means format.apply steps CANNOT target content created by earlier create steps in the same batch. Split creates and formatting into separate batches: first a mutations call with creates, then a mutations call with format.apply. " + 'Action "preview" dry-runs the plan. Action "apply" executes it. ' + "If a selector matches nothing, the failure reports the step id plus selector details so you can retry with a shorter or more distinctive anchor. " + "Do NOT create two steps that target overlapping text in the same block; combine them into a single text.rewrite step.",
251660
251875
  inputExamples: [
251661
251876
  {
251662
251877
  action: "apply",
@@ -262080,6 +262295,11 @@ var init_schemas5 = __esm(() => {
262080
262295
  oneOf: [{ type: "string" }, { type: "null" }],
262081
262296
  description: "Full flattened block text when requested with includeText."
262082
262297
  },
262298
+ tabCount: {
262299
+ type: "integer",
262300
+ minimum: 0,
262301
+ description: "Tabs in complete paragraph text, even beyond the preview."
262302
+ },
262083
262303
  isEmpty: { type: "boolean" },
262084
262304
  styleId: { oneOf: [{ type: "string" }, { type: "null" }], description: "Named paragraph style." },
262085
262305
  fontFamily: { type: "string", description: "Font family from first text run." },
@@ -280493,12 +280713,12 @@ function wrapRangeWithMarkupInParagraphXml(input) {
280493
280713
  const middleText = run.text.slice(sliceStart, sliceEnd);
280494
280714
  const afterText = run.text.slice(sliceEnd);
280495
280715
  if (beforeText.length > 0)
280496
- beforeSegment.push(renderRun(run.kind, run.prefix, beforeText, run.rPr, run.leafXml));
280716
+ beforeSegment.push(renderRun(run.kind, run.prefix, beforeText, run.rPr, run.leafXml, run.open));
280497
280717
  if (middleText.length > 0) {
280498
- wrappedRuns.push(renderRun(run.kind, run.prefix, middleText, run.rPr, run.leafXml));
280718
+ wrappedRuns.push(renderRun(run.kind, run.prefix, middleText, run.rPr, run.leafXml, run.open));
280499
280719
  }
280500
280720
  if (afterText.length > 0)
280501
- afterSegment.push(renderRun(run.kind, run.prefix, afterText, run.rPr, run.leafXml));
280721
+ afterSegment.push(renderRun(run.kind, run.prefix, afterText, run.rPr, run.leafXml, run.open));
280502
280722
  segmentCursor = run.xmlEnd;
280503
280723
  }
280504
280724
  if (wrappedRuns.length === 0) {
@@ -280528,6 +280748,8 @@ function replaceRangeInParagraphXml(input) {
280528
280748
  const gapXml = input.paragraphXml.slice(segmentCursor, run.xmlStart);
280529
280749
  if (index === span.value.firstRunIdx)
280530
280750
  beforeSegment.push(gapXml);
280751
+ else if (gapXml.trim())
280752
+ return { ok: false, reason: "unsupported-context" };
280531
280753
  }
280532
280754
  const sliceStart = index === span.value.firstRunIdx ? span.value.startInFirstRun : 0;
280533
280755
  const sliceEnd = index === span.value.lastRunIdx ? span.value.endInLastRun : run.text.length;
@@ -280998,6 +281220,7 @@ function parsePlainRun2(runXml, textStart, xmlStart, xmlEnd) {
280998
281220
  xmlStart,
280999
281221
  xmlEnd,
281000
281222
  xml: runXml,
281223
+ open: run.open,
281001
281224
  prefix: run.prefix,
281002
281225
  textStart,
281003
281226
  textEnd: textStart + text.length,
@@ -281334,15 +281557,15 @@ function buildRPrChange(prefix, stamp, previousChildren) {
281334
281557
  const previous = `<${prefix}:rPr>${serializeTopLevelChildren(previousChildren)}</${prefix}:rPr>`;
281335
281558
  return `<${prefix}:rPrChange w:id="${stamp.revisionId}" w:author="${escapeXmlAttribute3(stamp.author.name)}"${initials} w:date="${escapeXmlAttribute3(stamp.date)}">${previous}</${prefix}:rPrChange>`;
281336
281559
  }
281337
- function renderRun(kind, prefix, text, rPrXml, leafXml) {
281560
+ function renderRun(kind, prefix, text, rPrXml, leafXml, runOpen = `<${prefix}:r>`) {
281338
281561
  if (kind !== "text") {
281339
281562
  if (text.length === 0)
281340
281563
  return "";
281341
281564
  const fallbackLeaf = kind === "tab" ? `<${prefix}:tab/>` : `<${prefix}:${kind}/>`;
281342
- return `<${prefix}:r>${rPrXml}${leafXml ?? fallbackLeaf}</${prefix}:r>`;
281565
+ return `${runOpen}${rPrXml}${leafXml ?? fallbackLeaf}</${prefix}:r>`;
281343
281566
  }
281344
281567
  const preserve = /^\s|\s$/.test(text) ? ' xml:space="preserve"' : "";
281345
- return `<${prefix}:r>${rPrXml}<${prefix}:t${preserve}>${escapeXmlText3(text)}</${prefix}:t></${prefix}:r>`;
281568
+ return `${runOpen}${rPrXml}<${prefix}:t${preserve}>${escapeXmlText3(text)}</${prefix}:t></${prefix}:r>`;
281346
281569
  }
281347
281570
  function ensureHyperlinkRunStyle2(rPrXml, fallbackPrefix, styleId = "Hyperlink") {
281348
281571
  const escapedStyleId = escapeXmlAttribute3(styleId);
@@ -285282,6 +285505,9 @@ function createParagraphIdBatchAllocator(context, count) {
285282
285505
  function collectPackageParaIds(context) {
285283
285506
  const used = new Set;
285284
285507
  for (const partUri of listSDDocumentSessionInternalPartUris(context.session)) {
285508
+ const contentType = getSDDocumentSessionInternalPartDescriptor(context.session, partUri)?.contentType?.toLowerCase();
285509
+ if (!partUri.toLowerCase().endsWith(".xml") && !contentType?.endsWith("+xml") && contentType !== "application/xml")
285510
+ continue;
285285
285511
  const bytes = getSDDocumentSessionInternalPartBytes(context.session, partUri);
285286
285512
  if (!bytes)
285287
285513
  continue;
@@ -302565,11 +302791,16 @@ function projectTableListEntry(ordinal, nodeId, text, includeText) {
302565
302791
  }
302566
302792
  function projectParagraphListEntry(input) {
302567
302793
  const trimmed = input.text.trim();
302794
+ let tabCount = 0;
302795
+ for (let position = input.text.indexOf("\t");position !== -1; position = input.text.indexOf("\t", position + 1)) {
302796
+ tabCount++;
302797
+ }
302568
302798
  const item = {
302569
302799
  ordinal: input.ordinal,
302570
302800
  nodeId: input.nodeId,
302571
302801
  nodeType: input.nodeType,
302572
302802
  textPreview: trimmed.length > 80 ? `${trimmed.slice(0, 77)}…` : trimmed,
302803
+ tabCount,
302573
302804
  isEmpty: trimmed.length === 0
302574
302805
  };
302575
302806
  if (input.styleId)
@@ -328985,7 +329216,7 @@ class V2ContentControlsAdapter {
328985
329216
  }
328986
329217
  setTextValue(input, options) {
328987
329218
  const loaded = this.load();
328988
- const control = this.requireBodyControl(input.target, loaded);
329219
+ const control = options?.changeMode === "tracked" ? this.requireBodyControl(input.target, loaded) : this.requireControl(input.target, loaded);
328989
329220
  if (control.info.controlType !== "text") {
328990
329221
  throw operationError("TYPE_MISMATCH", `contentControls.text.setValue requires control type text, but found "${control.info.controlType}".`);
328991
329222
  }
@@ -329005,9 +329236,14 @@ class V2ContentControlsAdapter {
329005
329236
  }
329006
329237
  if (dryRunRequested(input, options))
329007
329238
  return this.validateDryRun(input, options, control.target);
329239
+ const part = loaded.parts.find((candidate2) => candidate2.storyId === control.storyId);
329008
329240
  const replacement = replaceSdtContentXml(control, buildTextReplacementContentXml(value2, control.info.kind, createParaIdAllocator(this.context), control.contentXml, false));
329009
- const nextXml = replaceRange2(loaded.documentXml, control.start, control.end, replacement);
329010
- return this.commitDocument(nextXml, "content-control-replace-content", input, options, control.target);
329241
+ const nextXml = replaceRange2(part.xml, control.start, control.end, replacement);
329242
+ return this.commitDocument(nextXml, "content-control-replace-content", input, options, control.target, {
329243
+ partUri: part.partUri,
329244
+ storySurface: { family: "storyBlocks", contentControlRewrite: true, targetBlockIds: [] },
329245
+ preserveRenderCommit: this.context.isCollaborativeSession()
329246
+ });
329011
329247
  }
329012
329248
  setDateValue(input, options) {
329013
329249
  const loaded = this.load();
@@ -329228,12 +329464,14 @@ class V2ContentControlsAdapter {
329228
329464
  }
329229
329465
  mutateControl(target, input, options, transform2, prefix = "content-control-mutate", validate) {
329230
329466
  const loaded = this.load();
329231
- const control = this.requireBodyControl(target, loaded);
329232
- validate?.(control, loaded);
329233
329467
  const tracked = options?.changeMode === "tracked";
329468
+ const storyAware = prefix === "content-control-pr" || prefix === "content-control-unwrap";
329469
+ const control = tracked || !storyAware ? this.requireBodyControl(target, loaded) : this.requireControl(target, loaded);
329470
+ const part = loaded.parts.find((candidate2) => candidate2.storyId === control.storyId);
329471
+ validate?.(control, loaded);
329234
329472
  if (tracked) {
329235
329473
  this.validateTrackedControlMutation(input, options);
329236
- if (hasTrackedControlAncestor(loaded.documentXml, control) || !["content-control-delete", "content-control-unwrap", "content-control-repeat-delete", "content-control-pr"].includes(prefix)) {
329474
+ if (hasTrackedControlAncestor(part.xml, control) || !["content-control-delete", "content-control-unwrap", "content-control-repeat-delete", "content-control-pr"].includes(prefix)) {
329237
329475
  this.requireDirectControlStructure(input, options);
329238
329476
  }
329239
329477
  }
@@ -329246,12 +329484,12 @@ class V2ContentControlsAdapter {
329246
329484
  let replacement = typeof transformed === "string" ? transformed : transformed.xml;
329247
329485
  let createdRefs = typeof transformed === "string" ? undefined : transformed.createdRefs;
329248
329486
  if (tracked && typeof transformed === "string") {
329249
- const namespaces = collectXmlElementRanges(loaded.documentXml).ranges.find((range) => range.start === control.start)?.namespaceBindings;
329487
+ const namespaces = collectXmlElementRanges(part.xml).ranges.find((range) => range.start === control.start)?.namespaceBindings;
329250
329488
  const prepared = prepareTrackedContentControlXml({
329251
329489
  context: this.context,
329252
329490
  beforeXml: control.xml,
329253
329491
  afterXml: replacement,
329254
- kind: trackedControlPlacement(loaded.documentXml, control),
329492
+ kind: trackedControlPlacement(part.xml, control),
329255
329493
  ownership: prefix === "content-control-unwrap" || prefix === "content-control-repeat-delete",
329256
329494
  namespaceBindings: new Map(Object.entries(namespaces ?? {})),
329257
329495
  expectedRevision: expectedRevisionFrom(input, options),
@@ -329262,8 +329500,13 @@ class V2ContentControlsAdapter {
329262
329500
  replacement = prepared.xml;
329263
329501
  createdRefs = prepared.wordIds.map((entityId) => ({ kind: "trackedChange", entityId, storyId: control.storyId }));
329264
329502
  }
329265
- const nextXml = replaceRange2(loaded.documentXml, control.start, control.end, replacement);
329266
- return this.commitDocument(nextXml, prefix, input, options, control.target, { createdRefs });
329503
+ const nextXml = replaceRange2(part.xml, control.start, control.end, replacement);
329504
+ return this.commitDocument(nextXml, prefix, input, options, control.target, {
329505
+ partUri: part.partUri,
329506
+ storySurface: { family: "storyBlocks", contentControlRewrite: true, targetBlockIds: [] },
329507
+ preserveRenderCommit: this.context.isCollaborativeSession(),
329508
+ createdRefs
329509
+ });
329267
329510
  }
329268
329511
  insertRepeatingItem(input, options, insertIndex) {
329269
329512
  this.validateTrackedControlMutation(input, options);
@@ -331068,7 +331311,7 @@ function wrapSelectionInXml(context, documentXml, target, sdtXml, kind, replacem
331068
331311
  }
331069
331312
  };
331070
331313
  }
331071
- if (kind === "inline" && !hasReplacementContent && selectedTextRunCount(para.xml, target.start.offset, target.end.offset) > 1) {
331314
+ if (kind === "inline" && !hasReplacementContent) {
331072
331315
  const template = splitSdtTemplateAroundContent(sdtXml);
331073
331316
  if (!template) {
331074
331317
  return {
@@ -331089,6 +331332,18 @@ function wrapSelectionInXml(context, documentXml, target, sdtXml, kind, replacem
331089
331332
  if (wrappedRuns.ok) {
331090
331333
  return { ok: true, xml: replaceRange2(documentXml, para.start, para.end, wrappedRuns.xml) };
331091
331334
  }
331335
+ return { ok: false, failure: { code: "CAPABILITY_UNAVAILABLE", message: "Content-control selection spans unsupported inline structures." } };
331336
+ }
331337
+ if (kind === "inline" && hasReplacementContent) {
331338
+ const replaced = replaceRangeInParagraphXml({
331339
+ paragraphXml: para.xml,
331340
+ rangeStart: target.start.offset,
331341
+ rangeEnd: target.end.offset,
331342
+ replacementXml: replaceSdtContentInTemplate(sdtXml, buildReplacementContentXml2(replacementContent))
331343
+ });
331344
+ if (replaced.ok) {
331345
+ return { ok: true, xml: replaceRange2(documentXml, para.start, para.end, replaced.xml) };
331346
+ }
331092
331347
  return {
331093
331348
  ok: false,
331094
331349
  failure: {
@@ -331105,15 +331360,6 @@ function wrapSelectionInXml(context, documentXml, target, sdtXml, kind, replacem
331105
331360
  return { ok: false, failure: { code: "CAPABILITY_UNAVAILABLE", message: "Content-control selection spans unsupported inline structures." } };
331106
331361
  return { ok: true, xml: replaceRange2(documentXml, para.start, para.end, wrapped) };
331107
331362
  }
331108
- function selectedTextRunCount(paragraphXml, start, end) {
331109
- const runs = new Set;
331110
- for (const piece of parseParagraphInlineMap(paragraphXml).leaves()) {
331111
- if (piece.kind === "text" && piece.textRole === "visible" && piece.visibleStart < end && piece.visibleEnd > start) {
331112
- runs.add(piece.runCharStart);
331113
- }
331114
- }
331115
- return runs.size;
331116
- }
331117
331363
  function splitSdtTemplateAroundContent(sdtXml) {
331118
331364
  const sdtRange = findFirstSdtElementRange(sdtXml);
331119
331365
  if (!sdtRange)
@@ -393765,6 +394011,7 @@ function createHocuspocusSingleDocAdapter(config2) {
393765
394011
  token: typeof config2.token === "string" || config2.token == null ? config2.token ?? null : config2.token,
393766
394012
  parameters: config2.params ?? {},
393767
394013
  preserveConnection: false,
394014
+ broadcast: config2.broadcast ?? true,
393768
394015
  quiet: true,
393769
394016
  onSynced() {
393770
394017
  if (!hasSynced) {
@@ -407312,7 +407559,7 @@ function parseRun2(runXml, textTagLocalName = "t", inheritedNamespaceBindings =
407312
407559
  atomKind: INLINE_ATOM_KIND.noteRef,
407313
407560
  id: attrLocal2(next.openTagInner, "id"),
407314
407561
  xml: next.xml,
407315
- meta: { kind: local },
407562
+ meta: { kind: local, runAttributes: attributes },
407316
407563
  visibleLength: 0
407317
407564
  });
407318
407565
  } else if (local === "sym" || local === "noBreakHyphen") {
@@ -407323,7 +407570,7 @@ function parseRun2(runXml, textTagLocalName = "t", inheritedNamespaceBindings =
407323
407570
  atomKind: INLINE_ATOM_KIND.preserved,
407324
407571
  id: null,
407325
407572
  xml: next.xml,
407326
- meta: { tag: local, container: "run-child" },
407573
+ meta: { tag: local, container: "run-child", runAttributes: attributes },
407327
407574
  visibleLength: 0
407328
407575
  });
407329
407576
  }
@@ -420555,7 +420802,9 @@ function serializeBlockXml(block, extraAtoms = [], context = { inherited: CANONI
420555
420802
  const atomXml = atom.kind === "synthetic-comment-marker" ? `<${w}:${atom.meta.marker} ${w}:id="${xmlEscape(String(atom.meta.commentId))}"/>` : atom.xml;
420556
420803
  const attributedBreakXml = runBackedAttributedBreakXml(atom);
420557
420804
  if (atomEmissionMode(atom) === "run-child") {
420558
- sb.push(serializeInlineRunAtom(attributedBreakXml ?? atomXml, runAttributes, w));
420805
+ const atomRunAttributes = atom.meta.runAttributes;
420806
+ const attributes = atomRunAttributes && typeof atomRunAttributes === "object" && !Array.isArray(atomRunAttributes) ? atomRunAttributes : runAttributes;
420807
+ sb.push(serializeInlineRunAtom(attributedBreakXml ?? atomXml, attributes, w));
420559
420808
  } else {
420560
420809
  sb.push(atomXml);
420561
420810
  }
@@ -424846,7 +425095,7 @@ function hasStrictMirrorPrerequisites(prepared, options) {
424846
425095
  case "storyAnchor":
424847
425096
  return options.session != null;
424848
425097
  case "compareApply":
424849
- return false;
425098
+ return options.session != null;
424850
425099
  default: {
424851
425100
  const exhaustive = extension2.family;
424852
425101
  return exhaustive;
@@ -424944,13 +425193,24 @@ function runFamilyMirror(prepared, extension2, options, directStoryText, tracked
424944
425193
  return mirrorPreparedTrackedDecide(prepared, trackedDecision);
424945
425194
  }
424946
425195
  if (extension2.family === "compareApply") {
424947
- return {
424948
- mirrored: false,
424949
- failure: {
424950
- storyId: prepared.resolvedTargets.stories[0] ?? "",
424951
- message: "story-surface mirror for the compareApply family is not supported (apply-deferred)"
424952
- }
424953
- };
425196
+ if (!options.session) {
425197
+ return {
425198
+ mirrored: false,
425199
+ mirrorPath: "session-sync",
425200
+ failure: {
425201
+ storyId: prepared.resolvedTargets.stories[0] ?? "",
425202
+ message: "compareApply session sync requires an authoritative session"
425203
+ }
425204
+ };
425205
+ }
425206
+ const sessionSync = mirrorStorySurfaceFromSession({
425207
+ session: options.session,
425208
+ prepared,
425209
+ extension: extension2,
425210
+ storyIds: storyIdsRequiringShards(prepared, extension2),
425211
+ resolveStoryShard: options.resolveStoryShard
425212
+ });
425213
+ return { ...sessionSync, mirrorPath: "session-sync" };
424954
425214
  }
424955
425215
  if (extension2.family === "storyAnchor" && prepared.source.kind === "insertComment" && !prepared.source.materialize) {
424956
425216
  return mirrorPreparedCommentInsert(prepared, extension2, options);
@@ -425718,6 +425978,9 @@ function admitPreparedStorySurfaceMutation(prepared, options) {
425718
425978
  }
425719
425979
  if (!touchesOwnedStoryPart(prepared))
425720
425980
  return { ok: true };
425981
+ if (extension2.family === "compareApply") {
425982
+ return validateCompareApplySessionSync(prepared, extension2, options);
425983
+ }
425721
425984
  if (extension2.family === "storyText") {
425722
425985
  if (extension2.historyReplay || extension2.resyncFromSession || extension2.sessionSync) {
425723
425986
  return validateStoryTextSessionSync(prepared, extension2, options);
@@ -425786,6 +426049,32 @@ function admitPreparedStorySurfaceMutation(prepared, options) {
425786
426049
  }
425787
426050
  return { ok: true };
425788
426051
  }
426052
+ function validateCompareApplySessionSync(prepared, extension2, options) {
426053
+ const source = prepared.source;
426054
+ if (source.kind !== "compareApply" || !options.session) {
426055
+ return { ok: false, message: "compareApply session sync requires an authoritative session" };
426056
+ }
426057
+ if (options.isSessionProjectionCurrent?.() !== true) {
426058
+ return { ok: false, message: "compareApply requires a current collaborative session projection" };
426059
+ }
426060
+ const storyId = String(source.storyId);
426061
+ const ownership = prepared.resolvedTargets.storyPartOwnership;
426062
+ if (!extension2.storyLocal || prepared.resolvedTargets.stories.length !== 1 || String(prepared.resolvedTargets.stories[0]) !== storyId || ownership.length !== 1 || String(ownership[0]?.storyId) !== storyId || prepared.resolvedTargets.partUris.length !== 1 || String(prepared.resolvedTargets.partUris[0]) !== String(ownership[0]?.partUri)) {
426063
+ return { ok: false, message: "compareApply collaboration requires one owned story part" };
426064
+ }
426065
+ if (source.bodySteps.length === 0 || source.bodySteps.some((step3) => step3.kind !== "edit") || source.storyGroups?.length || source.storyPackageOperations?.length || source.commentSteps?.length || source.packageRewrites?.length || source.relationshipBackedBodyRewrites?.length || source.headerFooterLifecycle || source.headerFooterSlotSteps?.length || source.tableTopologySteps?.length || source.tableBlockSteps?.length || source.tablePropertySteps?.length || source.sectionReplaySteps?.length || source.referenceReplaySteps?.length) {
426066
+ return { ok: false, message: "compareApply collaboration supports paragraph text edits only" };
426067
+ }
426068
+ const targetBlockIds2 = [...new Set(source.bodySteps.map((step3) => step3.blockId))];
426069
+ if (targetBlockIds2.length !== extension2.targetBlockIds.length || targetBlockIds2.some((blockId) => !extension2.targetBlockIds.includes(blockId))) {
426070
+ return { ok: false, message: "compareApply target blocks do not match its prepared refresh scope" };
426071
+ }
426072
+ const shard = options.resolveStoryShard(storyId);
426073
+ if (!shard || targetBlockIds2.some((blockId) => !findBlockMap(shard, blockId))) {
426074
+ return { ok: false, message: "compareApply target blocks are missing from the story shard" };
426075
+ }
426076
+ return { ok: true };
426077
+ }
425789
426078
  function validateStoryTextSessionSync(prepared, extension2, options) {
425790
426079
  if (!options.session) {
425791
426080
  return { ok: false, message: "storyText session sync requires an authoritative session" };
@@ -427422,6 +427711,7 @@ function createCollabV2SessionProjectionController(options) {
427422
427711
  const storyBackend = createStorySurfaceExecutionBackend({
427423
427712
  delegate: packageBackend,
427424
427713
  session: options.session,
427714
+ isSessionProjectionCurrent: () => !destroyed && !remoteProjectionPending && !deferredSessionAuthoritativeRemotePending && sessionStoryAlignment === "aligned",
427425
427715
  resolveStoryShard(storyId) {
427426
427716
  const handle = options.dispatcherRegistry.findStoryShardByStoryId?.(storyId);
427427
427717
  return handle ? collabUnitTargetOrDoc(handle) : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc/mcp",
3
- "version": "1.2.0-next.1",
3
+ "version": "1.2.0-next.10",
4
4
  "description": "Model Context Protocol server for SuperDoc, giving an AI agent tools to read and edit .docx files.",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0",
@@ -24,13 +24,13 @@
24
24
  ],
25
25
  "dependencies": {
26
26
  "@modelcontextprotocol/sdk": "^1.26.0",
27
- "@superdoc/sdk": "2.15.0-next.1",
27
+ "@superdoc/sdk": "2.15.0-next.10",
28
28
  "debug": "^4.4.3",
29
29
  "zod": "^4.3.6"
30
30
  },
31
31
  "devDependencies": {
32
- "superdoc": "2.18.0-next.1",
33
- "@superdoc/docx-engine": "0.17.0-next.1",
32
+ "superdoc": "2.18.0-next.10",
33
+ "@superdoc/docx-engine": "0.17.0-next.12",
34
34
  "@superdoc/document-api": "0.1.0-alpha.0",
35
35
  "@types/bun": "^1.3.8",
36
36
  "@types/node": "22.19.2",