@superdoc/mcp 1.0.0-next.7 → 1.0.0-next.8

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 +183 -66
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -349815,7 +349815,7 @@ var init_host = __esm(() => {
349815
349815
  });
349816
349816
 
349817
349817
  // ../../packages/sdk/langs/node/src/runtime/sdk-version.generated.ts
349818
- var SDK_VERSION = "2.13.0-next.9";
349818
+ var SDK_VERSION = "2.13.0-next.10";
349819
349819
 
349820
349820
  // ../../packages/sdk/langs/node/src/runtime/embedded-platform.ts
349821
349821
  import { chmodSync, existsSync } from "node:fs";
@@ -691770,7 +691770,7 @@ function parseParagraphXml(xml, inheritedNamespaceBindings = {}, trackedWrapperI
691770
691770
  } else if (tagLocalName === "hyperlink") {
691771
691771
  const inner = stripOuterTag(next.xml);
691772
691772
  const closingTag = extractClosingTag(next.xml);
691773
- const innerSegments = parseInnerInline(inner, namespaceBindingsForElement(next.openTagInner, paragraphNamespaceBindings));
691773
+ const innerSegments = parseInnerInline(inner, namespaceBindingsForElement(next.openTagInner, paragraphNamespaceBindings), trackedWrapperIdentityState);
691774
691774
  segments.push({
691775
691775
  kind: "atom",
691776
691776
  atomKind: INLINE_ATOM_KIND.preserved,
@@ -708730,7 +708730,7 @@ function runStorySurfaceMirror(prepared, options, admission, paragraphOnlyReview
708730
708730
  };
708731
708731
  }
708732
708732
  }
708733
- const familyResult = runFamilyMirror(prepared, extension2, options, admission.directStoryText, paragraphOnlyReviewStoryIds);
708733
+ const familyResult = runFamilyMirror(prepared, extension2, options, admission.directStoryText, admission.trackedDecision, paragraphOnlyReviewStoryIds);
708734
708734
  if (!familyResult.mirrored) {
708735
708735
  return {
708736
708736
  txId: prepared.txId,
@@ -708788,7 +708788,7 @@ function hasStrictMirrorPrerequisites(prepared, options) {
708788
708788
  }
708789
708789
  }
708790
708790
  }
708791
- function runFamilyMirror(prepared, extension2, options, directStoryText, paragraphOnlyReviewStoryIds) {
708791
+ function runFamilyMirror(prepared, extension2, options, directStoryText, trackedDecision, paragraphOnlyReviewStoryIds) {
708792
708792
  if (!options.resolveStoryShard) {
708793
708793
  return {
708794
708794
  mirrored: false,
@@ -708857,7 +708857,17 @@ function runFamilyMirror(prepared, extension2, options, directStoryText, paragra
708857
708857
  });
708858
708858
  return { ...sessionSync, mirrorPath: "session-sync" };
708859
708859
  }
708860
- return mirrorPreparedTrackedDecide(prepared, extension2, options);
708860
+ if (!trackedDecision) {
708861
+ return {
708862
+ mirrored: false,
708863
+ mirrorPath: "story-tracked-decide",
708864
+ failure: {
708865
+ storyId: prepared.resolvedTargets.stories[0] ?? "",
708866
+ message: "storyTrackedDecide requires an admitted cross-story preflight"
708867
+ }
708868
+ };
708869
+ }
708870
+ return mirrorPreparedTrackedDecide(prepared, trackedDecision);
708861
708871
  }
708862
708872
  if (extension2.family === "compareApply") {
708863
708873
  return {
@@ -709072,30 +709082,11 @@ function mirrorPreparedCommentInsert(prepared, extension2, options) {
709072
709082
  });
709073
709083
  return { mirrored: true, mirrorPath: "session-sync" };
709074
709084
  }
709075
- function mirrorPreparedTrackedDecide(prepared, extension2, options) {
709076
- const validation = validatePreparedTrackedDecide(prepared, extension2, options);
709077
- if (!validation.ok) {
709078
- return {
709079
- mirrored: false,
709080
- mirrorPath: "story-tracked-decide",
709081
- failure: {
709082
- storyId: prepared.resolvedTargets.stories[0] ?? "",
709083
- message: validation.failureReason
709084
- }
709085
- };
709086
- }
709087
- const source = validation.source;
709088
- const storyDocs = validation.storyDocs;
709089
- for (const { storyId, doc: doc2 } of storyDocs) {
709090
- const targets = validation.targets.filter((target) => target.storyId === storyId);
709091
- if (targets.length === 0)
709092
- continue;
709093
- const result = applyTrackedTextDecisionsToStory({
709094
- doc: doc2,
709095
- targets,
709096
- decision: source.decision,
709097
- txId: prepared.txId,
709098
- removedCommentIds: collectInvalidatedCommentIds(prepared)
709085
+ function mirrorPreparedTrackedDecide(prepared, preflight2) {
709086
+ for (const { storyId, plan } of preflight2.stories) {
709087
+ const result = applyPreflightedTrackedTextDecisionsToStory({
709088
+ plan,
709089
+ txId: prepared.txId
709099
709090
  });
709100
709091
  if (!result.ok) {
709101
709092
  return {
@@ -709169,6 +709160,26 @@ function validatePreparedTrackedDecide(prepared, extension2, options) {
709169
709160
  }
709170
709161
  return { ok: true, source, storyDocs, targets: trackedTextDecide.targets };
709171
709162
  }
709163
+ function preflightPreparedTrackedDecide(input) {
709164
+ const stories = [];
709165
+ const removedCommentIds = collectInvalidatedCommentIds(input.prepared);
709166
+ for (const { storyId, doc: doc2 } of input.storyDocs) {
709167
+ const targets = input.targets.filter((target) => target.storyId === storyId);
709168
+ if (targets.length === 0)
709169
+ continue;
709170
+ const result = preflightTrackedTextDecisionsToStory({
709171
+ doc: doc2,
709172
+ targets,
709173
+ decision: input.source.decision,
709174
+ removedCommentIds
709175
+ });
709176
+ if (!result.ok) {
709177
+ return { ok: false, failureReason: `${storyId}: ${result.failureReason}` };
709178
+ }
709179
+ stories.push({ storyId, plan: result.plan });
709180
+ }
709181
+ return { ok: true, preflight: { stories } };
709182
+ }
709172
709183
  function isSupportedPreparedTrackedTextKind(kind) {
709173
709184
  return kind === "ins" || kind === "del";
709174
709185
  }
@@ -709205,47 +709216,133 @@ function isSupportedPreparedTrackedCarrier(target) {
709205
709216
  return true;
709206
709217
  return target.carrierKind === "field-insertion" && target.trackedKind === "ins";
709207
709218
  }
709208
- function applyTrackedTextDecisionsToStory(input) {
709219
+ function preflightTrackedTextDecisionsToStory(input) {
709209
709220
  const resolved = [];
709210
- for (const target of input.targets) {
709211
- const spans = findPreparedLiveTrackedTextSpans(input.doc, target);
709212
- const alreadyDecided = spans.length === 0 && hasTrackedDecisionLedgerEntry(input.doc, target.rsid);
709213
- if (spans.length === 0 && !alreadyDecided) {
709221
+ const usedSpanIds = new Set;
709222
+ const candidatesByTarget = input.targets.map((target) => ({
709223
+ target,
709224
+ spans: findPreparedLiveTrackedTextSpans(input.doc, target)
709225
+ }));
709226
+ for (const { target, spans } of candidatesByTarget) {
709227
+ const span = spans.find((candidate) => !usedSpanIds.has(liveTrackedTextSpanId(candidate)));
709228
+ const alreadyDecided = !span && spans.length === 0 && hasTrackedDecisionLedgerEntry(input.doc, target.rsid);
709229
+ if (!span && !alreadyDecided) {
709214
709230
  return { ok: false, failureReason: `tracked decision target ${target.rsid} is not a live tracked text change` };
709215
709231
  }
709216
- resolved.push({ target, spans });
709232
+ if (span)
709233
+ usedSpanIds.add(liveTrackedTextSpanId(span));
709234
+ resolved.push({ target, spans: span ? [span] : [] });
709235
+ }
709236
+ for (const { target, spans } of candidatesByTarget) {
709237
+ if (spans.some((span) => !usedSpanIds.has(liveTrackedTextSpanId(span)))) {
709238
+ return {
709239
+ ok: false,
709240
+ failureReason: `tracked decision target ${target.rsid} metadata does not cover every matching live carrier`
709241
+ };
709242
+ }
709217
709243
  }
709218
709244
  const live = resolved.filter((entry) => entry.spans.length > 0);
709219
- if (live.length === 0)
709245
+ try {
709246
+ const removedCommentCarrierIds = collectCommentCarrierIds(input.removedCommentIds);
709247
+ const work = live.flatMap((entry) => entry.spans.map((span) => {
709248
+ const block = findBlockMap(input.doc, span.blockId)?.block;
709249
+ if (!block)
709250
+ throw new Error(`target block ${span.blockId} is not present in the story shard`);
709251
+ return {
709252
+ target: entry.target,
709253
+ span,
709254
+ block,
709255
+ leftBoundaryAtoms: [],
709256
+ leftBoundaryAnchors: []
709257
+ };
709258
+ }));
709259
+ for (const item of work) {
709260
+ const { target, span, block, leftBoundaryAtoms, leftBoundaryAnchors } = item;
709261
+ const blockLength = getBlockText(block).length;
709262
+ if (span.startOffset < 0 || span.startOffset > blockLength) {
709263
+ throw new Error(`tracked text span ${target.rsid} starts outside block ${span.blockId}`);
709264
+ }
709265
+ if (span.trackedKind === "ins" && input.decision === "reject") {
709266
+ if (span.endOffset < span.startOffset || span.endOffset > blockLength) {
709267
+ throw new Error(`tracked insert span ${target.rsid} is outside block ${span.blockId}`);
709268
+ }
709269
+ }
709270
+ if (span.trackedKind === "del" && input.decision === "reject") {
709271
+ let reachedStart = false;
709272
+ const removedTrackedCarrierIds = trackedTextCarrierIds(span, false);
709273
+ getBlockAtoms(block).forEach((atom) => {
709274
+ if (reachedStart)
709275
+ return;
709276
+ const atomId = String(atom.get(ATOM_KEY.id) ?? "");
709277
+ if (atomId === span.startAtomId) {
709278
+ reachedStart = true;
709279
+ return;
709280
+ }
709281
+ if (atomId && Number(atom.get(ATOM_KEY.visibleOffset) ?? -1) === span.startOffset && Number(atom.get(ATOM_KEY.length) ?? 0) === 0) {
709282
+ if (removedCommentCarrierIds.has(atomId))
709283
+ return;
709284
+ if (removedTrackedCarrierIds.has(atomId)) {
709285
+ throw new Error(`left-boundary atom ${atomId} aliases a removed tracked carrier`);
709286
+ }
709287
+ leftBoundaryAtoms.push(atom);
709288
+ const anchorIdentity2 = rangeAnchorIdentityForAtom(atom);
709289
+ if (anchorIdentity2) {
709290
+ const matchingAnchors = getBlockAnchors(block).toArray().filter((anchor) => anchor.get(ANCHOR_KEY.id) === atom.get(ATOM_KEY.id) && anchor.get(ANCHOR_KEY.kind) === anchorIdentity2.kind && anchor.get(ANCHOR_KEY.boundary) === anchorIdentity2.boundary && Number(anchor.get(ANCHOR_KEY.visibleOffset) ?? -1) === span.startOffset);
709291
+ if (matchingAnchors.length !== 1) {
709292
+ throw new Error(`left-boundary atom ${atomId} does not resolve one exact anchor carrier`);
709293
+ }
709294
+ leftBoundaryAnchors.push(matchingAnchors[0]);
709295
+ }
709296
+ }
709297
+ });
709298
+ if (!reachedStart) {
709299
+ throw new Error(`tracked deletion start ${span.startAtomId} is not present in block ${span.blockId}`);
709300
+ }
709301
+ }
709302
+ }
709303
+ return {
709304
+ ok: true,
709305
+ plan: {
709306
+ doc: input.doc,
709307
+ decision: input.decision,
709308
+ removedCommentCarrierIds,
709309
+ resolved,
709310
+ work
709311
+ }
709312
+ };
709313
+ } catch (error50) {
709314
+ return { ok: false, failureReason: error50 instanceof Error ? error50.message : String(error50) };
709315
+ }
709316
+ }
709317
+ function liveTrackedTextSpanId(span) {
709318
+ return `${span.blockId}\x00${span.startAtomId}\x00${span.endAtomId}`;
709319
+ }
709320
+ function applyPreflightedTrackedTextDecisionsToStory(input) {
709321
+ if (input.plan.work.length === 0)
709220
709322
  return { ok: true, applied: false, alreadyDecided: true };
709221
709323
  try {
709222
- resolveStoryShardScope(input.doc).doc.transact(() => {
709223
- const work = live.flatMap((entry) => entry.spans.map((span) => ({ target: entry.target, span })));
709224
- for (const { target, span } of work) {
709225
- const found = findBlockMap(input.doc, span.blockId)?.block;
709226
- if (!found)
709227
- throw new Error(`target block ${span.blockId} is not present in the story shard`);
709228
- removeTrackedTextCarriers(found, span, input.decision === "reject" && target.carrierKind === "field-insertion");
709229
- removeCommentCarriersByIds(found, input.removedCommentIds);
709230
- }
709231
- const sorted = work.sort((left, right) => {
709324
+ resolveStoryShardScope(input.plan.doc).doc.transact(() => {
709325
+ const { decision, doc: doc2, removedCommentCarrierIds, resolved, work } = input.plan;
709326
+ for (const { target, span, block } of work) {
709327
+ removeTrackedTextCarriers(block, span, decision === "reject" && target.carrierKind === "field-insertion");
709328
+ removeCommentCarriersByIds(block, removedCommentCarrierIds);
709329
+ }
709330
+ const sorted = [...work].sort((left, right) => {
709232
709331
  if (left.span.blockId !== right.span.blockId)
709233
709332
  return left.span.blockId < right.span.blockId ? -1 : 1;
709234
709333
  if (left.span.startOffset !== right.span.startOffset)
709235
709334
  return right.span.startOffset - left.span.startOffset;
709236
- if (left.span.trackedKind === right.span.trackedKind)
709237
- return 0;
709335
+ if (left.span.trackedKind === right.span.trackedKind) {
709336
+ return right.span.startAtomIndex - left.span.startAtomIndex;
709337
+ }
709238
709338
  return left.span.trackedKind === "ins" ? -1 : 1;
709239
709339
  });
709240
- for (const { target, span } of sorted) {
709241
- if (span.trackedKind === "ins" && input.decision === "reject") {
709340
+ for (const { span, leftBoundaryAtoms, leftBoundaryAnchors } of sorted) {
709341
+ if (span.trackedKind === "ins" && decision === "reject") {
709242
709342
  const length3 = span.endOffset - span.startOffset;
709243
- if (length3 < 0) {
709244
- throw new Error(`tracked insert span ${target.rsid} is outside block ${span.blockId}`);
709245
- }
709246
709343
  if (length3 > 0) {
709247
709344
  deleteText2({
709248
- doc: input.doc,
709345
+ doc: doc2,
709249
709346
  blockId: span.blockId,
709250
709347
  offset: span.startOffset,
709251
709348
  length: length3,
@@ -709253,8 +709350,8 @@ function applyTrackedTextDecisionsToStory(input) {
709253
709350
  });
709254
709351
  }
709255
709352
  }
709256
- if (span.trackedKind === "del" && input.decision === "reject") {
709257
- const found = findBlockMap(input.doc, span.blockId)?.block;
709353
+ if (span.trackedKind === "del" && decision === "reject") {
709354
+ const found = findBlockMap(doc2, span.blockId)?.block;
709258
709355
  if (!found)
709259
709356
  throw new Error(`target block ${span.blockId} is not present in the story shard`);
709260
709357
  let offset = span.startOffset;
@@ -709266,7 +709363,7 @@ function applyTrackedTextDecisionsToStory(input) {
709266
709363
  continue;
709267
709364
  }
709268
709365
  insertText2({
709269
- doc: input.doc,
709366
+ doc: doc2,
709270
709367
  blockId: span.blockId,
709271
709368
  offset,
709272
709369
  text: segment.text,
@@ -709293,10 +709390,12 @@ function applyTrackedTextDecisionsToStory(input) {
709293
709390
  if (restoredBreakXml) {
709294
709391
  throw new Error(`attributed break restoration in ${span.blockId} is incomplete`);
709295
709392
  }
709393
+ leftBoundaryAtoms.forEach((atom) => atom.set(ATOM_KEY.visibleOffset, span.startOffset));
709394
+ leftBoundaryAnchors.forEach((anchor) => anchor.set(ANCHOR_KEY.visibleOffset, span.startOffset));
709296
709395
  }
709297
709396
  }
709298
709397
  for (const { target } of resolved) {
709299
- recordTrackedDecision(input.doc, target.rsid, input.decision, input.txId);
709398
+ recordTrackedDecision(doc2, target.rsid, decision, input.txId);
709300
709399
  }
709301
709400
  }, STORY_SURFACE_BACKEND_ORIGIN);
709302
709401
  } catch (error50) {
@@ -709375,6 +709474,7 @@ function collectLiveTrackedTextSpansInBlock(block, rsid, trackedKind) {
709375
709474
  blockId,
709376
709475
  startOffset: start.offset,
709377
709476
  endOffset: trackedKind === "del" ? start.offset + deletedTextLength2 : end.offset,
709477
+ startAtomIndex: start.index,
709378
709478
  startAtomId: start.id,
709379
709479
  endAtomId: end.id,
709380
709480
  preservedAtomIds: preservedForSpan.map((atom) => atom.id),
@@ -709386,24 +709486,38 @@ function collectLiveTrackedTextSpansInBlock(block, rsid, trackedKind) {
709386
709486
  return spans;
709387
709487
  }
709388
709488
  function removeTrackedTextCarriers(block, span, removeContainedAtoms) {
709389
- const ids = new Set([
709489
+ const ids = trackedTextCarrierIds(span, removeContainedAtoms);
709490
+ deleteYMapArrayEntriesByIds(getBlockAtoms(block), ATOM_KEY.id, ids);
709491
+ deleteYMapArrayEntriesByIds(getBlockAnchors(block), ANCHOR_KEY.id, ids);
709492
+ }
709493
+ function trackedTextCarrierIds(span, includeContainedAtoms) {
709494
+ return new Set([
709390
709495
  span.startAtomId,
709391
709496
  span.endAtomId,
709392
709497
  ...span.preservedAtomIds,
709393
- ...removeContainedAtoms ? span.containedAtomIds : []
709498
+ ...includeContainedAtoms ? span.containedAtomIds : []
709394
709499
  ]);
709395
- deleteYMapArrayEntriesByIds(getBlockAtoms(block), ATOM_KEY.id, ids);
709396
- deleteYMapArrayEntriesByIds(getBlockAnchors(block), ANCHOR_KEY.id, ids);
709397
709500
  }
709398
- function removeCommentCarriersByIds(block, commentIds) {
709399
- if (commentIds.size === 0)
709400
- return;
709501
+ function rangeAnchorIdentityForAtom(atom) {
709502
+ const atomKind = atom.get(ATOM_KEY.kind);
709503
+ if (atomKind !== INLINE_ATOM_KIND.commentMarker && atomKind !== INLINE_ATOM_KIND.bookmark)
709504
+ return null;
709505
+ const marker = String(asRecord6(atom.get(ATOM_KEY.meta)).marker ?? "");
709506
+ const boundary = marker.endsWith("End") ? "end" : marker.endsWith("Start") ? "start" : "point";
709507
+ return { kind: atomKind === INLINE_ATOM_KIND.commentMarker ? "comment" : "bookmark", boundary };
709508
+ }
709509
+ function collectCommentCarrierIds(commentIds) {
709401
709510
  const ids = new Set;
709402
709511
  for (const commentId of commentIds) {
709403
709512
  ids.add(`comment:${commentId}:start`);
709404
709513
  ids.add(`comment:${commentId}:end`);
709405
709514
  ids.add(`comment:${commentId}:ref`);
709406
709515
  }
709516
+ return ids;
709517
+ }
709518
+ function removeCommentCarriersByIds(block, ids) {
709519
+ if (ids.size === 0)
709520
+ return;
709407
709521
  deleteYMapArrayEntriesByIds(getBlockAtoms(block), ATOM_KEY.id, ids);
709408
709522
  deleteYMapArrayEntriesByIds(getBlockAnchors(block), ANCHOR_KEY.id, ids);
709409
709523
  }
@@ -709513,7 +709627,10 @@ function admitPreparedStorySurfaceMutation(prepared, options) {
709513
709627
  return options.session ? { ok: true } : { ok: false, message: "tracked-change decision session sync requires an authoritative session" };
709514
709628
  }
709515
709629
  const trackedDecision = validatePreparedTrackedDecide(prepared, extension2, options);
709516
- return trackedDecision.ok ? { ok: true } : { ok: false, message: trackedDecision.failureReason };
709630
+ if (!trackedDecision.ok)
709631
+ return { ok: false, message: trackedDecision.failureReason };
709632
+ const preflight2 = preflightPreparedTrackedDecide({ prepared, ...trackedDecision });
709633
+ return preflight2.ok ? { ok: true, trackedDecision: preflight2.preflight } : { ok: false, message: preflight2.failureReason };
709517
709634
  }
709518
709635
  if (extension2.family === "storyTable") {
709519
709636
  if (extension2.historyReplay) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc/mcp",
3
- "version": "1.0.0-next.7",
3
+ "version": "1.0.0-next.8",
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.13.0-next.9",
27
+ "@superdoc/sdk": "2.13.0-next.10",
28
28
  "debug": "^4.4.3",
29
29
  "zod": "^4.3.6"
30
30
  },
31
31
  "devDependencies": {
32
- "superdoc": "2.16.0-next.7",
33
- "@superdoc/docx-engine": "0.15.0-next.7",
32
+ "superdoc": "2.16.0-next.8",
33
+ "@superdoc/docx-engine": "0.15.0-next.8",
34
34
  "@superdoc/document-api": "0.1.0-alpha.0",
35
35
  "@types/bun": "^1.3.8",
36
36
  "@types/node": "22.19.2",