@beyondwork/docx-react-component 1.0.120 → 1.0.121

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 (116) hide show
  1. package/README.md +1 -0
  2. package/dist/api/public-types.cjs +1713 -55
  3. package/dist/api/public-types.d.cts +2 -2
  4. package/dist/api/public-types.d.ts +2 -2
  5. package/dist/api/public-types.js +6 -6
  6. package/dist/api/v3.cjs +4958 -406
  7. package/dist/api/v3.d.cts +3 -3
  8. package/dist/api/v3.d.ts +3 -3
  9. package/dist/api/v3.js +14 -14
  10. package/dist/{canonical-document-fNawStsc.d.cts → canonical-document-ByIqTd4s.d.cts} +9 -1
  11. package/dist/{canonical-document-fNawStsc.d.ts → canonical-document-ByIqTd4s.d.ts} +9 -1
  12. package/dist/{chunk-5RNMPLXU.js → chunk-37SEJQ3G.js} +4 -4
  13. package/dist/{chunk-FXGQM2JB.js → chunk-3OFSP2IX.js} +11 -5
  14. package/dist/{chunk-U5BSQXF4.js → chunk-3OHVK2D6.js} +70 -12
  15. package/dist/{chunk-AUQDC5BD.js → chunk-3TUQCHYT.js} +101 -2
  16. package/dist/{chunk-SJSMYEMU.js → chunk-B4YHWFE3.js} +3 -3
  17. package/dist/{chunk-XC56YLIS.js → chunk-C2LWJ4CZ.js} +4 -0
  18. package/dist/{chunk-VDIUVT46.js → chunk-CX42VC67.js} +1 -1
  19. package/dist/{chunk-KCHEAX4Z.js → chunk-EMDH4IQN.js} +148 -70
  20. package/dist/{chunk-TMQGWF7R.js → chunk-G3B2OBCZ.js} +352 -17
  21. package/dist/{chunk-VCL5MJMZ.js → chunk-GON2DNTE.js} +149 -28
  22. package/dist/{chunk-WVZX4NYG.js → chunk-GZW2ERUO.js} +601 -47
  23. package/dist/{chunk-WDNEPRFW.js → chunk-ICX54W4U.js} +1 -1
  24. package/dist/{chunk-FIGWJ43K.js → chunk-IT2DK3A7.js} +1883 -90
  25. package/dist/{chunk-2ZWFQ74R.js → chunk-OBCP6VTG.js} +1 -1
  26. package/dist/{chunk-FLNQY74K.js → chunk-OYGMRRR7.js} +1 -1
  27. package/dist/{chunk-MPYYBRVN.js → chunk-PCXTMEGY.js} +782 -124
  28. package/dist/{chunk-4JNUMMM7.js → chunk-PGGPPZ65.js} +17 -2
  29. package/dist/{chunk-KHZNNBTN.js → chunk-QFU7ZOAD.js} +43 -39
  30. package/dist/{chunk-4ZNQFWFM.js → chunk-QIO6V46H.js} +84 -4
  31. package/dist/{chunk-IQ2VJEF6.js → chunk-QNGJRZ2D.js} +1 -1
  32. package/dist/{chunk-BM5NSDII.js → chunk-S4ANOS2M.js} +2 -2
  33. package/dist/{chunk-AQA7OZ2R.js → chunk-TFSXOIAI.js} +959 -43
  34. package/dist/{chunk-NQZUGMLW.js → chunk-TMU7JMXX.js} +184 -32
  35. package/dist/{chunk-KD5K5XIA.js → chunk-UHQOUTAX.js} +568 -88
  36. package/dist/{chunk-327AIUXL.js → chunk-UWDWGQH5.js} +11 -4
  37. package/dist/{chunk-BBB4GSDB.js → chunk-XVFENXLK.js} +2 -2
  38. package/dist/{chunk-MUEN2WOG.js → chunk-ZKSDVHGH.js} +6 -3
  39. package/dist/compare.cjs +17 -2
  40. package/dist/compare.d.cts +1 -1
  41. package/dist/compare.d.ts +1 -1
  42. package/dist/compare.js +3 -3
  43. package/dist/core/commands/formatting-commands.d.cts +2 -2
  44. package/dist/core/commands/formatting-commands.d.ts +2 -2
  45. package/dist/core/commands/image-commands.cjs +814 -45
  46. package/dist/core/commands/image-commands.d.cts +2 -2
  47. package/dist/core/commands/image-commands.d.ts +2 -2
  48. package/dist/core/commands/image-commands.js +8 -8
  49. package/dist/core/commands/section-layout-commands.d.cts +2 -2
  50. package/dist/core/commands/section-layout-commands.d.ts +2 -2
  51. package/dist/core/commands/style-commands.d.cts +2 -2
  52. package/dist/core/commands/style-commands.d.ts +2 -2
  53. package/dist/core/commands/table-structure-commands.cjs +750 -42
  54. package/dist/core/commands/table-structure-commands.d.cts +2 -2
  55. package/dist/core/commands/table-structure-commands.d.ts +2 -2
  56. package/dist/core/commands/table-structure-commands.js +6 -6
  57. package/dist/core/commands/text-commands.cjs +910 -57
  58. package/dist/core/commands/text-commands.d.cts +2 -2
  59. package/dist/core/commands/text-commands.d.ts +2 -2
  60. package/dist/core/commands/text-commands.js +8 -8
  61. package/dist/core/selection/mapping.d.cts +2 -2
  62. package/dist/core/selection/mapping.d.ts +2 -2
  63. package/dist/core/state/editor-state.cjs +17 -2
  64. package/dist/core/state/editor-state.d.cts +2 -2
  65. package/dist/core/state/editor-state.d.ts +2 -2
  66. package/dist/core/state/editor-state.js +2 -2
  67. package/dist/index.cjs +6203 -625
  68. package/dist/index.d.cts +5 -5
  69. package/dist/index.d.ts +5 -5
  70. package/dist/index.js +299 -67
  71. package/dist/io/docx-session.cjs +354 -102
  72. package/dist/io/docx-session.d.cts +4 -4
  73. package/dist/io/docx-session.d.ts +4 -4
  74. package/dist/io/docx-session.js +5 -5
  75. package/dist/legal.cjs +183 -31
  76. package/dist/legal.d.cts +1 -1
  77. package/dist/legal.d.ts +1 -1
  78. package/dist/legal.js +3 -3
  79. package/dist/{loader-CaohrhNl.d.ts → loader-BF8ju_LK.d.ts} +22 -4
  80. package/dist/{loader-BpCyGnZl.d.cts → loader-g54WRvj1.d.cts} +22 -4
  81. package/dist/{public-types-Dpch9JG0.d.cts → public-types-D_y4Ptcj.d.cts} +747 -21
  82. package/dist/{public-types-C948HNVF.d.ts → public-types-Dl1jiWjk.d.ts} +747 -21
  83. package/dist/public-types.cjs +1713 -55
  84. package/dist/public-types.d.cts +2 -2
  85. package/dist/public-types.d.ts +2 -2
  86. package/dist/public-types.js +6 -6
  87. package/dist/runtime/collab.cjs +4 -0
  88. package/dist/runtime/collab.d.cts +3 -3
  89. package/dist/runtime/collab.d.ts +3 -3
  90. package/dist/runtime/collab.js +2 -2
  91. package/dist/runtime/document-runtime.cjs +3699 -507
  92. package/dist/runtime/document-runtime.d.cts +2 -2
  93. package/dist/runtime/document-runtime.d.ts +2 -2
  94. package/dist/runtime/document-runtime.js +18 -18
  95. package/dist/{session-Dqg17V3s.d.ts → session-C1EPAkcI.d.ts} +3 -3
  96. package/dist/{session-BlXE5zxz.d.cts → session-D15QOO0Q.d.cts} +3 -3
  97. package/dist/session.cjs +951 -104
  98. package/dist/session.d.cts +5 -5
  99. package/dist/session.d.ts +5 -5
  100. package/dist/session.js +10 -8
  101. package/dist/tailwind.cjs +1752 -91
  102. package/dist/tailwind.d.cts +2 -2
  103. package/dist/tailwind.d.ts +2 -2
  104. package/dist/tailwind.js +10 -10
  105. package/dist/{types-C9vZVpKy.d.cts → types-BoSRp2Vg.d.cts} +2 -2
  106. package/dist/{types-B1tlF1bq.d.ts → types-DEvRwq9C.d.ts} +2 -2
  107. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +3 -3
  108. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +3 -3
  109. package/dist/ui-tailwind/editor-surface/search-plugin.js +7 -7
  110. package/dist/ui-tailwind/theme/editor-theme.css +5 -5
  111. package/dist/ui-tailwind.cjs +1752 -91
  112. package/dist/ui-tailwind.d.cts +8 -8
  113. package/dist/ui-tailwind.d.ts +8 -8
  114. package/dist/ui-tailwind.js +10 -10
  115. package/package.json +17 -5
  116. package/dist/ui-tailwind/theme/tokens.css +0 -382
@@ -17,18 +17,18 @@ import {
17
17
  resolveScopeRange,
18
18
  scopeSpecificity,
19
19
  searchDocument
20
- } from "./chunk-MPYYBRVN.js";
20
+ } from "./chunk-PCXTMEGY.js";
21
21
  import {
22
22
  BROADCAST_COMMAND_TYPES,
23
23
  COMMAND_EVENT_SCHEMA_VERSION,
24
24
  LOCAL_ONLY_COMMAND_TYPES,
25
25
  getRemoteCursorStates
26
- } from "./chunk-XC56YLIS.js";
26
+ } from "./chunk-C2LWJ4CZ.js";
27
27
  import {
28
28
  buildPageAnchorElementId,
29
29
  createUiApi,
30
30
  emitUxResponse
31
- } from "./chunk-VCL5MJMZ.js";
31
+ } from "./chunk-GON2DNTE.js";
32
32
  import {
33
33
  DEFAULT_PX_PER_TWIP,
34
34
  DEFAULT_REGISTRY_ENTRIES,
@@ -36,23 +36,23 @@ import {
36
36
  LAYCACHE_SCHEMA_VERSION,
37
37
  LAYOUT_ENGINE_VERSION,
38
38
  createScopeTagRegistry
39
- } from "./chunk-TMQGWF7R.js";
39
+ } from "./chunk-G3B2OBCZ.js";
40
40
  import {
41
41
  chartModelStore,
42
42
  createFormattingContext
43
- } from "./chunk-AUQDC5BD.js";
43
+ } from "./chunk-3TUQCHYT.js";
44
44
  import {
45
45
  deriveDocumentStats
46
- } from "./chunk-FLNQY74K.js";
46
+ } from "./chunk-OYGMRRR7.js";
47
47
  import {
48
48
  DocxSession
49
- } from "./chunk-BM5NSDII.js";
49
+ } from "./chunk-S4ANOS2M.js";
50
50
  import {
51
51
  WORKFLOW_PAYLOAD_PART_PATH,
52
52
  buildEditorStateXml,
53
53
  parseEditorStateXml,
54
54
  parseWorkflowPayloadEnvelopeFromPackage
55
- } from "./chunk-KCHEAX4Z.js";
55
+ } from "./chunk-EMDH4IQN.js";
56
56
  import {
57
57
  EMU_PER_PX,
58
58
  GRADIENT_STOP_UNITS,
@@ -61,7 +61,7 @@ import {
61
61
  createHeaderFooterStoryKey,
62
62
  createNoteStoryKey,
63
63
  validateEditableTargetRef
64
- } from "./chunk-AQA7OZ2R.js";
64
+ } from "./chunk-TFSXOIAI.js";
65
65
  import {
66
66
  readOpcPackage
67
67
  } from "./chunk-OL2UEHRP.js";
@@ -1894,6 +1894,7 @@ function toV3PageLocalStory(story, frame) {
1894
1894
  variant: story.variant,
1895
1895
  relationshipId: story.relationshipId,
1896
1896
  ...story.sectionIndex !== void 0 ? { sectionIndex: story.sectionIndex } : {},
1897
+ previewText: story.previewText,
1897
1898
  resolvedFields: story.resolvedFields.map(toV3PageLocalStoryField),
1898
1899
  anchoredObjects: story.anchoredObjects.map(toV3PageLocalStoryObject),
1899
1900
  measuredFrameHeightTwips: story.measuredFrameHeightTwips,
@@ -3819,19 +3820,24 @@ function getRuntimeTableSelectionDescriptor(snapshot) {
3819
3820
  const tableBlockIndex = tableDepth > 1 ? -1 : anchor.tableBlockIndex;
3820
3821
  const spansMultipleCells = anchor.rowIndex !== head.rowIndex || anchor.columnIndex !== head.columnIndex || !snapshot.selection.isCollapsed;
3821
3822
  const hasEditableTableTarget = target !== void 0 && tableBlockPath !== void 0;
3823
+ const nonEditableReason = anchor.nonEditableReason ?? head.nonEditableReason;
3824
+ const editability = nonEditableReason !== void 0 || tableDepth > 1 && !hasEditableTableTarget ? "non-editable" : "editable";
3822
3825
  const descriptor = {
3823
- editability: tableDepth > 1 && !hasEditableTableTarget ? "non-editable" : "editable",
3824
- ...tableDepth > 1 && !hasEditableTableTarget ? {
3826
+ editability,
3827
+ ...nonEditableReason !== void 0 ? {
3828
+ nonEditableReason,
3829
+ message: nonEditableReason === "vertical-merge-continuation-not-editable" ? "Vertical-merge continuation cells are synthetic layout cells; edit the visible restart cell instead." : "Nested table selection descriptors require a validated editable target path."
3830
+ } : tableDepth > 1 && !hasEditableTableTarget ? {
3825
3831
  nonEditableReason: "nested-table-not-editable",
3826
3832
  message: "Nested table selection descriptors require a validated editable target path."
3827
3833
  } : {},
3828
3834
  tableDepth,
3829
- ...target ? { storyKey: target.storyKey } : {},
3835
+ ...editability === "editable" && target ? { storyKey: target.storyKey } : {},
3830
3836
  ...tableBlockPath ? {
3831
3837
  tableBlockPath,
3832
- tableKey: `${target?.storyKey ?? "unknown"}:${tableBlockPath}`
3838
+ tableKey: `${editability === "editable" ? target?.storyKey ?? "unknown" : "unknown"}:${tableBlockPath}`
3833
3839
  } : {},
3834
- ...target ? {
3840
+ ...editability === "editable" && target ? {
3835
3841
  editableTarget: {
3836
3842
  targetKey: target.targetKey,
3837
3843
  kind: target.kind,
@@ -3884,7 +3890,8 @@ function findCellHit(blocks, position, tableDepth) {
3884
3890
  columnIndex: currentColumn,
3885
3891
  colspan: Math.max(1, cell.colspan),
3886
3892
  rowspan: Math.max(1, cell.rowspan),
3887
- ...editableTarget ? { editableTarget } : {}
3893
+ ...editableTarget && validTableEditableTarget(editableTarget) ? { editableTarget } : {},
3894
+ ...cell.verticalMerge === "continue" || editableTarget?.posture.blockers.includes("synthetic-layout-cell") ? { nonEditableReason: "vertical-merge-continuation-not-editable" } : {}
3888
3895
  };
3889
3896
  }
3890
3897
  }
@@ -4015,21 +4022,6 @@ function copyEnvelope(envelope) {
4015
4022
  ...envelope.sourceIdentity ? { sourceIdentity: { ...envelope.sourceIdentity } } : {}
4016
4023
  };
4017
4024
  }
4018
- var UNAVAILABLE_COVERAGE = {
4019
- status: "unavailable",
4020
- pageCount: 0,
4021
- pageMaterialization: { paginated: 0, unpaginated: 0 },
4022
- regionCount: 0,
4023
- sliceCount: 0,
4024
- lineCount: 0,
4025
- anchorCount: 0,
4026
- hitTargetCount: 0,
4027
- semanticEntryCount: 0,
4028
- editableTargetCount: 0,
4029
- replacementEnvelopeCount: 0,
4030
- objectHandleCount: 0,
4031
- precision: { exact: 0, "within-tolerance": 0, heuristic: 0 }
4032
- };
4033
4025
  function copyLayoutDivergence(divergence) {
4034
4026
  return {
4035
4027
  divergenceId: divergence.divergenceId,
@@ -4056,16 +4048,6 @@ function copyGraphOracleReference(reference) {
4056
4048
  };
4057
4049
  }
4058
4050
  function projectLayoutEvidence(runtime, pageIds) {
4059
- if (typeof runtime.layout?.getPageCount !== "function") {
4060
- return {
4061
- status: "unavailable",
4062
- pageCount: 0,
4063
- pageIds: [],
4064
- pagesWithDivergences: [],
4065
- divergenceIds: [],
4066
- divergences: []
4067
- };
4068
- }
4069
4051
  const pageCount = runtime.layout.getPageCount();
4070
4052
  const selectedPageIds = pageIds ? new Set(pageIds) : null;
4071
4053
  const pageIdOut = [];
@@ -4199,26 +4181,14 @@ function projectOracleEvidence(runtime, options, input) {
4199
4181
  }
4200
4182
  function projectDocumentPe2Evidence(runtime, options) {
4201
4183
  const layout = projectLayoutEvidence(runtime);
4202
- const geometry = runtime.geometry ? copyCoverage(runtime.geometry.getGeometryCoverage()) : UNAVAILABLE_COVERAGE;
4203
4184
  return {
4204
- geometry,
4185
+ geometry: copyCoverage(runtime.geometry.getGeometryCoverage()),
4205
4186
  layout,
4206
4187
  oracle: projectOracleEvidence(runtime, options, { pageIds: layout.pageIds })
4207
4188
  };
4208
4189
  }
4209
4190
  function projectScopePe2Evidence(runtime, handle, options) {
4210
4191
  const { scopeId } = handle;
4211
- if (!runtime.geometry) {
4212
- const layout2 = projectLayoutEvidence(runtime, []);
4213
- return {
4214
- geometry: {
4215
- coverage: UNAVAILABLE_COVERAGE,
4216
- reason: "geometry-index-unavailable"
4217
- },
4218
- layout: layout2,
4219
- oracle: projectOracleEvidence(runtime, options, { scopeId, pageIds: layout2.pageIds })
4220
- };
4221
- }
4222
4192
  const coverage = copyCoverage(runtime.geometry.getGeometryCoverage());
4223
4193
  const index = runtime.geometry.getGeometryIndex();
4224
4194
  if (!index) {
@@ -4651,17 +4621,19 @@ function capabilityBlockerDetailFor(code) {
4651
4621
  }
4652
4622
  function readbackBlockerDetailFor(code) {
4653
4623
  const unchangedPrefix = "apply-readback-unchanged:";
4624
+ const mismatchPrefix = "apply-readback-mismatch:";
4654
4625
  const unresolvablePrefix = "apply-readback-unresolvable:";
4655
4626
  const isUnchanged = code.startsWith(unchangedPrefix);
4627
+ const isMismatch = code.startsWith(mismatchPrefix);
4656
4628
  const isUnresolvable = code.startsWith(unresolvablePrefix);
4657
- if (!isUnchanged && !isUnresolvable) return null;
4658
- const scopeId = isUnchanged ? code.slice(unchangedPrefix.length) : code.slice(unresolvablePrefix.length);
4629
+ if (!isUnchanged && !isMismatch && !isUnresolvable) return null;
4630
+ const scopeId = isUnchanged ? code.slice(unchangedPrefix.length) : isMismatch ? code.slice(mismatchPrefix.length) : code.slice(unresolvablePrefix.length);
4659
4631
  return {
4660
4632
  code,
4661
4633
  category: "capability-prerequisite",
4662
4634
  scopeKind: "scope",
4663
- message: isUnchanged ? `Runtime dispatch completed, but authoritative readback showed target scope ${scopeId} content was unchanged.` : `Runtime dispatch completed, but target scope ${scopeId} could not be re-resolved for authoritative readback.`,
4664
- nextStep: isUnchanged ? "Treat this as a hard refusal for content mutation. Attach an explanation or issue, and wait for an L08 mutation/readback route before surfacing warn-and-proceed." : "Re-resolve the target with ai.resolveReference or ai.queryScopeAtPosition, then retry only with the returned handle's scopeId; otherwise attach an explanation or issue."
4635
+ message: isUnchanged ? `Runtime dispatch completed, but authoritative readback showed target scope ${scopeId} content was unchanged.` : isMismatch ? `Runtime dispatch completed, but authoritative readback did not match the requested replacement for target scope ${scopeId}.` : `Runtime dispatch completed, but target scope ${scopeId} could not be re-resolved for authoritative readback.`,
4636
+ nextStep: isUnchanged ? "Treat this as a hard refusal for content mutation. Attach an explanation or issue, and wait for an L08 mutation/readback route before surfacing warn-and-proceed." : isMismatch ? "Treat this as a hard refusal for content mutation. Re-resolve the target and retry only after the replacement path has a proven mutation/readback route." : "Re-resolve the target with ai.resolveReference or ai.queryScopeAtPosition, then retry only with the returned handle's scopeId; otherwise attach an explanation or issue."
4665
4637
  };
4666
4638
  }
4667
4639
  function projectBlockerDetails(blockers) {
@@ -4722,6 +4694,15 @@ function createReplacementFamily(runtime) {
4722
4694
  warnings: Object.freeze([TEXTBOX_TARGET_AVAILABLE_WARNING])
4723
4695
  };
4724
4696
  }
4697
+ const generatedOrLinkedBlockers = generatedOrLinkedReplacementBlockers(bundle, operation);
4698
+ if (generatedOrLinkedBlockers.length > 0) {
4699
+ return {
4700
+ proposalId,
4701
+ safe: false,
4702
+ posture: "hard-refusal",
4703
+ blockers: generatedOrLinkedBlockers
4704
+ };
4705
+ }
4725
4706
  const proposedContent = typeof input.proposedText === "string" ? { kind: "text", text: input.proposedText } : { kind: "text", text: "" };
4726
4707
  const verdict = composeScopeValidation({
4727
4708
  scope: compiled.scope,
@@ -4831,6 +4812,15 @@ function createReplacementFamily(runtime) {
4831
4812
  }
4832
4813
  };
4833
4814
  }
4815
+ function generatedOrLinkedReplacementBlockers(bundle, operation) {
4816
+ if (!bundle || operation !== "replace") return Object.freeze([]);
4817
+ const blockers = bundle.evidence.capabilities?.canReplaceText.blockers ?? [];
4818
+ return Object.freeze(
4819
+ blockers.filter(
4820
+ (blocker2) => blocker2 === "field-generated-text" || blocker2.startsWith("link-bookmark:")
4821
+ )
4822
+ );
4823
+ }
4834
4824
 
4835
4825
  // src/api/v3/ai/_metadata-audit.ts
4836
4826
  function snapshotDocumentHash(runtime) {
@@ -5658,7 +5648,7 @@ function createTableActionFamily(runtime) {
5658
5648
  );
5659
5649
  const surface = runtime.getRenderSnapshot().surface?.blocks ?? [];
5660
5650
  const currentTargets = collectEditableTargetRefs(runtime.getCanonicalDocument()).filter(
5661
- (target) => target.table && target.storyKey === "main" && supportedTableKeys.has(target.table.tableKey) && isResolvableCurrentTableActionTarget(runtime, surface, target)
5651
+ (target) => target.table && supportedTableKeys.has(target.table.tableKey) && isResolvableCurrentTableActionTarget(runtime, surface, target)
5662
5652
  );
5663
5653
  const seed = documentSeed(runtime);
5664
5654
  const byActionHandle = /* @__PURE__ */ new Map();
@@ -5710,18 +5700,19 @@ function createTableActionFamily(runtime) {
5710
5700
  if (warning) {
5711
5701
  return blockedResult(input, proposalId, warning);
5712
5702
  }
5713
- const targetBlocker = validateTableTarget(target, input.operation.kind);
5703
+ const targetBlocker = validateTableTarget(target, input.operation);
5714
5704
  if (targetBlocker) {
5715
5705
  return blockedResult(input, proposalId, targetBlocker);
5716
5706
  }
5717
5707
  if (isTableTextOperation(input.operation)) {
5718
5708
  const textContent = tableTextContent(input.operation);
5719
- if (textContent === null) {
5709
+ const fragmentContent = tableStructuredFragment(input.operation);
5710
+ if (textContent === null && fragmentContent === null) {
5720
5711
  return blockedResult(input, proposalId, {
5721
5712
  code: `table-action-text-content-required:${input.operation.kind}`,
5722
5713
  category: "unsupported-operation",
5723
- message: "Paste/drop table text actions require plain text content.",
5724
- nextStep: 'Retry with operation.content.kind="text" and a string text value; structured cell payloads are refused.',
5714
+ message: "Paste/drop table text actions require plain text or a canonical document fragment.",
5715
+ nextStep: 'Retry with operation.content.kind="text" and a string text value, or operation.content.kind="structured" with a CanonicalDocumentFragment payload.',
5725
5716
  actionHandle: input.actionHandle,
5726
5717
  operation: input.operation.kind,
5727
5718
  operationScope: target.table?.operationScope
@@ -5745,12 +5736,21 @@ function createTableActionFamily(runtime) {
5745
5736
  });
5746
5737
  }
5747
5738
  const before2 = runtime.getCanonicalDocument();
5748
- runtime.dispatch({
5749
- type: "table.apply-text",
5750
- operation: { type: "replace-text", text: textContent },
5751
- editableTarget: target,
5752
- origin: { source: "api", timestamp: nowUtc }
5753
- });
5739
+ if (fragmentContent) {
5740
+ runtime.dispatch({
5741
+ type: "fragment.insert",
5742
+ fragment: fragmentContent,
5743
+ editableTarget: target,
5744
+ origin: { source: "api", timestamp: nowUtc }
5745
+ });
5746
+ } else {
5747
+ runtime.dispatch({
5748
+ type: "table.apply-text",
5749
+ operation: { type: "replace-text", text: textContent ?? "" },
5750
+ editableTarget: target,
5751
+ origin: { source: "api", timestamp: nowUtc }
5752
+ });
5753
+ }
5754
5754
  const changed2 = runtime.getCanonicalDocument() !== before2;
5755
5755
  const afterReadback = tableTextReadback(readEditableTargetText(runtime.getCanonicalDocument(), target));
5756
5756
  return {
@@ -5760,7 +5760,7 @@ function createTableActionFamily(runtime) {
5760
5760
  actionHandle: input.actionHandle,
5761
5761
  operation: input.operation.kind,
5762
5762
  commandReference: {
5763
- command: "table.apply-text",
5763
+ command: fragmentContent ? "fragment.insert" : "table.apply-text",
5764
5764
  actorId: input.actorId ?? "v3-ai-api",
5765
5765
  origin: input.origin ?? "agent",
5766
5766
  emittedAtUtc: nowUtc
@@ -5775,14 +5775,15 @@ function createTableActionFamily(runtime) {
5775
5775
  }
5776
5776
  const resolution = resolveEditableTableStructureTarget({
5777
5777
  document: runtime.getCanonicalDocument(),
5778
- target
5778
+ target,
5779
+ selectionDescriptor: operationSelectionDescriptor(input.operation)
5779
5780
  });
5780
5781
  if (resolution.kind === "rejected") {
5781
5782
  return blockedResult(input, proposalId, {
5782
5783
  code: resolution.blockedReason.code,
5783
5784
  category: "blocked-target",
5784
5785
  message: resolution.blockedReason.message,
5785
- nextStep: "Use a supported top-level table-structure target from ai.listTableActions; nested, SDT, secondary-story, stale, or workflow-blocked targets remain refused.",
5786
+ nextStep: "Use a supported table-structure target from ai.listTableActions; nested, SDT, stale, wrong-story, or workflow-blocked targets remain refused.",
5786
5787
  actionHandle: input.actionHandle,
5787
5788
  operation: input.operation.kind,
5788
5789
  operationScope: target.table?.operationScope
@@ -5831,10 +5832,10 @@ function isSupportedTableActionEvidence(entry) {
5831
5832
  return isSupportedTableStructureEvidence(entry) || isSupportedTableTextEvidence(entry);
5832
5833
  }
5833
5834
  function isSupportedTableStructureEvidence(entry) {
5834
- return entry.commandFamily === "table-structure" && entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("table-structure-action") && Boolean(entry.table) && entry.storyKey === "main" && entry.table?.operationScope !== "text";
5835
+ return entry.commandFamily === "table-structure" && entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("table-structure-action") && Boolean(entry.table) && entry.table?.operationScope !== "text";
5835
5836
  }
5836
5837
  function isSupportedTableTextEvidence(entry) {
5837
- return entry.commandFamily === "text-leaf" && entry.runtimeCommand.status === "supported" && Boolean(entry.table) && entry.storyKey === "main" && entry.table?.operationScope === "text";
5838
+ return entry.commandFamily === "text-leaf" && entry.runtimeCommand.status === "supported" && Boolean(entry.table) && entry.table?.operationScope === "text";
5838
5839
  }
5839
5840
  function projectTableActionDescriptor(document, seed, entry) {
5840
5841
  if (!isSupportedTableActionEvidence(entry) || !entry.table) {
@@ -5876,7 +5877,8 @@ function projectCurrentTableTarget(document, seed, target) {
5876
5877
  reason: table.operationScope === "text" ? "l07:table-text-target-supported" : "l07:table-structure-target-supported"
5877
5878
  };
5878
5879
  }
5879
- function validateTableTarget(target, operation) {
5880
+ function validateTableTarget(target, operationInput) {
5881
+ const operation = operationInput.kind;
5880
5882
  if (operation === "paste-cell-content" || operation === "drop-cell-content") {
5881
5883
  if (target.commandFamily !== "text-leaf" || target.table?.operationScope !== "text") {
5882
5884
  return {
@@ -5912,6 +5914,9 @@ function validateTableTarget(target, operation) {
5912
5914
  }
5913
5915
  const supported = operationsForScope(target.table.operationScope);
5914
5916
  if (!supported.includes(operation)) {
5917
+ if (operation === "merge-cells" && target.table.operationScope === "cell" && operationSelectionDescriptor(operationInput) !== null) {
5918
+ return null;
5919
+ }
5915
5920
  const callable = callableOperationsForScope(target.table.operationScope);
5916
5921
  if (callable.includes(operation)) return null;
5917
5922
  return {
@@ -5926,18 +5931,7 @@ function validateTableTarget(target, operation) {
5926
5931
  return null;
5927
5932
  }
5928
5933
  function tableActionWarning(input, target) {
5929
- if ((input.operation.kind === "paste-cell-content" || input.operation.kind === "drop-cell-content") && input.operation.content?.kind === "structured") {
5930
- return {
5931
- code: `table-action-unsupported:structured-cell-content:${input.operation.kind}`,
5932
- category: "unsupported-operation",
5933
- message: "Structured paste/drop payloads are not accepted for command-native table text actions.",
5934
- nextStep: "Retry with plain text content, or use a product paste path that can produce a validated fragment command.",
5935
- actionHandle: input.actionHandle,
5936
- operation: input.operation.kind,
5937
- operationScope: target.table?.operationScope
5938
- };
5939
- }
5940
- if (input.operation.kind === "merge-cells" && target.table?.operationScope === "cell") {
5934
+ if (input.operation.kind === "merge-cells" && target.table?.operationScope === "cell" && operationSelectionDescriptor(input.operation) === null) {
5941
5935
  return {
5942
5936
  code: "table-action-unsupported:merge-cells:single-cell-target",
5943
5937
  category: "unsupported-operation",
@@ -5955,7 +5949,7 @@ function callableOperationsForScope(scope) {
5955
5949
  case "text":
5956
5950
  return TEXT_TRANSFER_OPERATIONS;
5957
5951
  case "cell":
5958
- return CELL_STRUCTURE_OPERATIONS;
5952
+ return [...CELL_STRUCTURE_OPERATIONS, "merge-cells"];
5959
5953
  default:
5960
5954
  return operationsForScope(scope);
5961
5955
  }
@@ -5980,7 +5974,7 @@ function resolveTableActionHandle(runtime, seed, actionHandle) {
5980
5974
  const document = runtime.getCanonicalDocument();
5981
5975
  const surface = runtime.getRenderSnapshot().surface?.blocks ?? [];
5982
5976
  const targets = collectEditableTargetRefs(document).filter(
5983
- (target) => target.table && target.storyKey === "main" && callableOperationsForScope(target.table.operationScope).length > 0 && isResolvableCurrentTableActionTarget(runtime, surface, target)
5977
+ (target) => target.table && callableOperationsForScope(target.table.operationScope).length > 0 && isResolvableCurrentTableActionTarget(runtime, surface, target)
5984
5978
  );
5985
5979
  return targets.find((target) => {
5986
5980
  return tableActionHandle(seed, target.targetKey) === actionHandle;
@@ -6013,7 +6007,7 @@ function hashOpaque(input) {
6013
6007
  return (h >>> 0).toString(36).padStart(7, "0");
6014
6008
  }
6015
6009
  function toTableStructureOperation(operation) {
6016
- const { kind, ...rest } = operation;
6010
+ const { kind, selectionDescriptor: _selectionDescriptor, ...rest } = operation;
6017
6011
  return {
6018
6012
  ...rest,
6019
6013
  type: kind
@@ -6025,6 +6019,19 @@ function isTableTextOperation(operation) {
6025
6019
  function tableTextContent(operation) {
6026
6020
  return operation.content?.kind === "text" ? operation.content.text : null;
6027
6021
  }
6022
+ function tableStructuredFragment(operation) {
6023
+ if (operation.content?.kind !== "structured") return null;
6024
+ return isCanonicalDocumentFragment(operation.content.value) ? operation.content.value : null;
6025
+ }
6026
+ function isCanonicalDocumentFragment(value) {
6027
+ return Boolean(
6028
+ value && typeof value === "object" && Array.isArray(value.blocks)
6029
+ );
6030
+ }
6031
+ function operationSelectionDescriptor(operation) {
6032
+ const descriptor = operation.selectionDescriptor;
6033
+ return descriptor ?? null;
6034
+ }
6028
6035
  function tableActionTargetReadback(actionHandle, target) {
6029
6036
  return {
6030
6037
  actionHandle,
@@ -6132,6 +6139,1791 @@ function documentSeed(runtime) {
6132
6139
  return state.documentId ?? "document";
6133
6140
  }
6134
6141
 
6142
+ // src/api/v3/ai/actions.ts
6143
+ function actionMethodMetadata(method, readOrMutate, auditCategory, contextPromptShape, uxIntent) {
6144
+ return {
6145
+ name: `ai.actions.${method}`,
6146
+ status: "live-with-adapter",
6147
+ sourceLayer: "ai",
6148
+ liveEvidence: {
6149
+ runnerTest: "test/api/v3/ai/ai-actions-category.test.ts",
6150
+ commit: "refactor-09-ai-actions-category"
6151
+ },
6152
+ uxIntent,
6153
+ agentMetadata: {
6154
+ readOrMutate,
6155
+ boundedScope: "scope",
6156
+ auditCategory,
6157
+ contextPromptShape
6158
+ },
6159
+ stateClass: "A-canonical",
6160
+ persistsTo: "canonical",
6161
+ ...readOrMutate === "mutate" ? { broadcastsVia: "crdt" } : {},
6162
+ rwdReference: `\xA7AI API \xA7 ai.actions.${method}`
6163
+ };
6164
+ }
6165
+ var discoverMetadata = actionMethodMetadata(
6166
+ "discover",
6167
+ "read",
6168
+ "actions-discover",
6169
+ "Read supported simple action methods, policy vocabulary, and optional target capability posture.",
6170
+ { uiVisible: false, expectsUxResponse: "none" }
6171
+ );
6172
+ var locateMetadata = actionMethodMetadata(
6173
+ "locate",
6174
+ "read",
6175
+ "actions-locate",
6176
+ "Find the first scope/table-text target by query; returns ScopeHandle or opaque table actionHandle, never offsets.",
6177
+ { uiVisible: false, expectsUxResponse: "none" }
6178
+ );
6179
+ var locateAllMetadata = actionMethodMetadata(
6180
+ "locateAll",
6181
+ "read",
6182
+ "actions-locate",
6183
+ "Find scope/table-text targets by query; table text matches include readback {text,isEmpty}.",
6184
+ { uiVisible: false, expectsUxResponse: "none" }
6185
+ );
6186
+ var rewriteMetadata = actionMethodMetadata(
6187
+ "rewrite",
6188
+ "mutate",
6189
+ "actions-rewrite",
6190
+ "Rewrite one target through applyReplacementScope or table actionHandle/table.apply-text.",
6191
+ {
6192
+ uiVisible: true,
6193
+ expectsUxResponse: "inline-change",
6194
+ expectedDelta: "target text changes"
6195
+ }
6196
+ );
6197
+ var rewriteAllMetadata = actionMethodMetadata(
6198
+ "rewriteAll",
6199
+ "mutate",
6200
+ "actions-rewrite",
6201
+ "Rewrite every locateAll match up to the supplied limit through the same scope/table text routes as rewrite.",
6202
+ {
6203
+ uiVisible: true,
6204
+ expectsUxResponse: "inline-change",
6205
+ expectedDelta: "matched target text changes"
6206
+ }
6207
+ );
6208
+ var insertTextMetadata = actionMethodMetadata(
6209
+ "insertText",
6210
+ "mutate",
6211
+ "actions-insert-text",
6212
+ "Insert text before or after a semantic scope through the scoped replacement path.",
6213
+ {
6214
+ uiVisible: true,
6215
+ expectsUxResponse: "inline-change",
6216
+ expectedDelta: "adjacent text is inserted"
6217
+ }
6218
+ );
6219
+ var flagMetadata = actionMethodMetadata(
6220
+ "flag",
6221
+ "mutate",
6222
+ "actions-flag",
6223
+ "Create one durable AI issue flag on a semantic scope.",
6224
+ {
6225
+ uiVisible: true,
6226
+ expectsUxResponse: "warning-added",
6227
+ expectedDelta: "issue rail shows a new issue card"
6228
+ }
6229
+ );
6230
+ var flagManyMetadata = actionMethodMetadata(
6231
+ "flagMany",
6232
+ "mutate",
6233
+ "actions-flag",
6234
+ "Create durable AI issue flags on locateAll scope matches.",
6235
+ {
6236
+ uiVisible: true,
6237
+ expectsUxResponse: "warning-added",
6238
+ expectedDelta: "issue rail shows new issue cards"
6239
+ }
6240
+ );
6241
+ var markMetadata = actionMethodMetadata(
6242
+ "mark",
6243
+ "mutate",
6244
+ "actions-mark",
6245
+ "Apply a scoped formatting action through applyScopeAction.",
6246
+ {
6247
+ uiVisible: true,
6248
+ expectsUxResponse: "inline-change",
6249
+ expectedDelta: "target formatting changes"
6250
+ }
6251
+ );
6252
+ var briefMetadata = actionMethodMetadata(
6253
+ "brief",
6254
+ "read",
6255
+ "actions-brief",
6256
+ "Read a compact document or target summary without invoking a model provider.",
6257
+ { uiVisible: false, expectsUxResponse: "none" }
6258
+ );
6259
+ var fieldRefreshMetadata = actionMethodMetadata(
6260
+ "fieldRefresh",
6261
+ "mutate",
6262
+ "actions-generated-content",
6263
+ "Refresh a supported generated field target through the modeled runtime field command.",
6264
+ {
6265
+ uiVisible: true,
6266
+ expectsUxResponse: "inline-change",
6267
+ expectedDelta: "generated field result refreshes"
6268
+ }
6269
+ );
6270
+ var tocRefreshMetadata = actionMethodMetadata(
6271
+ "tocRefresh",
6272
+ "mutate",
6273
+ "actions-generated-content",
6274
+ "Refresh a supported table-of-contents target through the modeled runtime TOC command.",
6275
+ {
6276
+ uiVisible: true,
6277
+ expectsUxResponse: "inline-change",
6278
+ expectedDelta: "table-of-contents result refreshes"
6279
+ }
6280
+ );
6281
+ var bookmarkEditMetadata = actionMethodMetadata(
6282
+ "bookmarkEdit",
6283
+ "mutate",
6284
+ "actions-link-bookmark",
6285
+ "Edit a supported bookmark content range through the modeled runtime bookmark command.",
6286
+ {
6287
+ uiVisible: true,
6288
+ expectsUxResponse: "inline-change",
6289
+ expectedDelta: "bookmark content changes"
6290
+ }
6291
+ );
6292
+ var hyperlinkDestinationEditMetadata = actionMethodMetadata(
6293
+ "hyperlinkDestinationEdit",
6294
+ "mutate",
6295
+ "actions-link-bookmark",
6296
+ "Update a supported hyperlink destination through the modeled runtime hyperlink command.",
6297
+ {
6298
+ uiVisible: true,
6299
+ expectsUxResponse: "inline-change",
6300
+ expectedDelta: "hyperlink destination changes"
6301
+ }
6302
+ );
6303
+ var listOperationMetadata = actionMethodMetadata(
6304
+ "listOperation",
6305
+ "mutate",
6306
+ "actions-list-operation",
6307
+ "Apply list toggle, indent, outdent, restart, or continue-numbering commands to paragraph-like scope handles.",
6308
+ {
6309
+ uiVisible: true,
6310
+ expectsUxResponse: "inline-change",
6311
+ expectedDelta: "list structure changes"
6312
+ }
6313
+ );
6314
+ var tableFragmentMetadata = actionMethodMetadata(
6315
+ "tableFragment",
6316
+ "mutate",
6317
+ "actions-table-fragment",
6318
+ "Apply structured table paste/drop through an opaque table actionHandle.",
6319
+ {
6320
+ uiVisible: true,
6321
+ expectsUxResponse: "inline-change",
6322
+ expectedDelta: "table cell fragment content changes"
6323
+ }
6324
+ );
6325
+ var tableSelectionMetadata = actionMethodMetadata(
6326
+ "tableSelection",
6327
+ "mutate",
6328
+ "actions-table-selection",
6329
+ "Apply table merge/split selection operations through an opaque table actionHandle.",
6330
+ {
6331
+ uiVisible: true,
6332
+ expectsUxResponse: "inline-change",
6333
+ expectedDelta: "table cell selection structure changes"
6334
+ }
6335
+ );
6336
+ var runPlanMetadata = actionMethodMetadata(
6337
+ "runPlan",
6338
+ "mutate",
6339
+ "actions-run-plan",
6340
+ "Validate or apply a host-generated structured plan by dispatching exact steps through existing action primitives; no free-form instruction interpretation.",
6341
+ {
6342
+ uiVisible: true,
6343
+ expectsUxResponse: "inline-change",
6344
+ expectedDelta: "planned step mutations are applied when mode is apply"
6345
+ }
6346
+ );
6347
+ var ACTION_METHODS = Object.freeze([
6348
+ "discover",
6349
+ "locate",
6350
+ "locateAll",
6351
+ "rewrite",
6352
+ "rewriteAll",
6353
+ "insertText",
6354
+ "flag",
6355
+ "flagMany",
6356
+ "mark",
6357
+ "brief",
6358
+ "fieldRefresh",
6359
+ "tocRefresh",
6360
+ "bookmarkEdit",
6361
+ "hyperlinkDestinationEdit",
6362
+ "listOperation",
6363
+ "tableFragment",
6364
+ "tableSelection",
6365
+ "runPlan"
6366
+ ]);
6367
+ var DEFAULT_LOCATE_LIMIT = 20;
6368
+ var DEFAULT_REWRITE_ALL_LIMIT = 10;
6369
+ var DEFAULT_TABLE_TEXT_SCOPE_LIMIT = 3;
6370
+ var DEFAULT_PLAN_STEP_LIMIT = 20;
6371
+ function createActionsFamily(runtime) {
6372
+ const category = {
6373
+ discover(input) {
6374
+ const policies = createPolicyFamily(runtime).listAIActions();
6375
+ if (!input?.target) {
6376
+ return {
6377
+ status: "supported",
6378
+ methods: ACTION_METHODS,
6379
+ policyActions: policies
6380
+ };
6381
+ }
6382
+ const resolved = resolveTarget(runtime, input.target);
6383
+ if (!resolved.ok) {
6384
+ return {
6385
+ status: "blocked",
6386
+ methods: ACTION_METHODS,
6387
+ policyActions: policies,
6388
+ blockers: resolved.blockers,
6389
+ blockerDetails: resolved.blockerDetails
6390
+ };
6391
+ }
6392
+ return {
6393
+ status: "supported",
6394
+ methods: ACTION_METHODS,
6395
+ policyActions: policies,
6396
+ target: summarizeTarget(resolved.target)
6397
+ };
6398
+ },
6399
+ locate(input) {
6400
+ const all = locateAll(runtime, { ...input, limit: input.limit ?? 1 });
6401
+ return {
6402
+ status: all.status,
6403
+ matches: all.matches.slice(0, 1),
6404
+ ...all.blockers ? { blockers: all.blockers } : {},
6405
+ ...all.blockerDetails ? { blockerDetails: all.blockerDetails } : {}
6406
+ };
6407
+ },
6408
+ locateAll(input) {
6409
+ return locateAll(runtime, input);
6410
+ },
6411
+ rewrite(input) {
6412
+ if (input.text === void 0) {
6413
+ return blockedApply(
6414
+ "actions:rewrite:text-required",
6415
+ "input",
6416
+ "Rewrite actions require a text value.",
6417
+ "Retry with a text string."
6418
+ );
6419
+ }
6420
+ const resolved = resolveTarget(runtime, input.target);
6421
+ if (!resolved.ok) return blockedApplyFromResolution(resolved);
6422
+ return applyRewrite(runtime, resolved.target, input);
6423
+ },
6424
+ rewriteAll(input) {
6425
+ const located = locateAll(runtime, {
6426
+ query: input.query,
6427
+ kind: input.kind,
6428
+ matchCase: input.matchCase,
6429
+ limit: input.limit ?? DEFAULT_REWRITE_ALL_LIMIT,
6430
+ includeTableText: input.includeTableText,
6431
+ tableTextScopeLimit: input.tableTextScopeLimit
6432
+ });
6433
+ if (located.status !== "found") {
6434
+ return blockedApply(
6435
+ located.blockers?.[0] ?? `actions:locate:not-found:${input.query}`,
6436
+ located.status === "blocked" ? "input" : "unresolved-target",
6437
+ located.status === "blocked" ? "The locate request was blocked." : "No scope or table text action matched the query.",
6438
+ "Call ai.actions.locateAll with a broader query, then retry with a returned handle or actionHandle.",
6439
+ located.blockerDetails
6440
+ );
6441
+ }
6442
+ const results = located.matches.map((match) => {
6443
+ const target = match.actionHandle ? { actionHandle: match.actionHandle } : match.handle ? { handle: match.handle } : null;
6444
+ if (!target) {
6445
+ return blockedApply(
6446
+ "actions:rewrite-all:match-without-target",
6447
+ "blocked",
6448
+ "A locate match did not carry a callable target.",
6449
+ "Refresh the locate result and retry."
6450
+ );
6451
+ }
6452
+ const resolved = resolveTarget(runtime, target);
6453
+ if (!resolved.ok) return blockedApplyFromResolution(resolved);
6454
+ return applyRewrite(runtime, resolved.target, input);
6455
+ });
6456
+ const applied = results.filter((result) => result.applied);
6457
+ return {
6458
+ status: applied.length === results.length ? "applied" : applied.length > 0 ? "partial" : "blocked",
6459
+ applied: applied.length > 0,
6460
+ changed: results.some((result) => result.changed),
6461
+ results: Object.freeze(results)
6462
+ };
6463
+ },
6464
+ insertText(input) {
6465
+ const resolved = resolveTarget(runtime, input.target);
6466
+ if (!resolved.ok) return blockedApplyFromResolution(resolved);
6467
+ if (resolved.target.kind !== "scope") {
6468
+ return blockedApply(
6469
+ "actions:insert-text:table-text-adjacent-unsupported",
6470
+ "unsupported",
6471
+ "Adjacent text insertion is not supported for table text action handles.",
6472
+ "Use ai.actions.rewrite with a table text actionHandle when replacing the cell text is acceptable."
6473
+ );
6474
+ }
6475
+ const result = createReplacementFamily(runtime).applyReplacementScope({
6476
+ targetScopeId: resolved.target.handle.scopeId,
6477
+ operation: input.position === "before" ? "insert-before" : "insert-after",
6478
+ proposedText: input.text,
6479
+ ...input.actionId ? { actionId: input.actionId } : {},
6480
+ ...input.reason ? { reason: input.reason } : {},
6481
+ ...input.preserve ? { preserve: input.preserve } : {},
6482
+ ...input.actorId ? { actorId: input.actorId } : {},
6483
+ ...input.origin ? { origin: input.origin } : {},
6484
+ ...input.proposalId ? { proposalId: input.proposalId } : {}
6485
+ });
6486
+ return projectApplyResult(result, resolved.target);
6487
+ },
6488
+ flag(input) {
6489
+ const resolved = resolveTarget(runtime, input.target);
6490
+ if (!resolved.ok) return blockedApplyFromResolution(resolved);
6491
+ if (resolved.target.kind !== "scope") {
6492
+ return blockedApply(
6493
+ "actions:flag:table-text-handle-unsupported",
6494
+ "unsupported",
6495
+ "Issue flags attach to semantic scopes, not table text action handles.",
6496
+ "Use the owning table or paragraph scope handle when creating an issue flag."
6497
+ );
6498
+ }
6499
+ const result = createAttachFamily(runtime).createIssue({
6500
+ scopeId: resolved.target.handle.scopeId,
6501
+ summary: input.summary,
6502
+ ...input.severity ? { severity: input.severity } : {},
6503
+ ...input.actorId ? { actorId: input.actorId } : {},
6504
+ ...input.origin ? { origin: input.origin } : {}
6505
+ });
6506
+ return {
6507
+ status: result.created ? "applied" : "blocked",
6508
+ applied: result.created,
6509
+ changed: result.created,
6510
+ target: summarizeTarget(resolved.target),
6511
+ proposalId: result.issueId,
6512
+ ...result.reason ? { blockers: Object.freeze([result.reason]) } : {},
6513
+ ...result.reason ? {
6514
+ blockerDetails: Object.freeze([
6515
+ blocker(
6516
+ result.reason,
6517
+ "unresolved-target",
6518
+ "The issue flag target could not be resolved.",
6519
+ "Refresh the scope handle and retry."
6520
+ )
6521
+ ])
6522
+ } : {},
6523
+ ...result.auditReference ? { auditReference: result.auditReference } : {}
6524
+ };
6525
+ },
6526
+ flagMany(input) {
6527
+ const located = locateAll(runtime, input);
6528
+ if (located.status !== "found") {
6529
+ return blockedApply(
6530
+ located.blockers?.[0] ?? `actions:locate:not-found:${input.query}`,
6531
+ located.status === "blocked" ? "input" : "unresolved-target",
6532
+ "No scope matched the flag query.",
6533
+ "Call ai.actions.locateAll with a broader query, then retry with a returned handle.",
6534
+ located.blockerDetails
6535
+ );
6536
+ }
6537
+ const results = located.matches.map((match) => {
6538
+ if (!match.handle) {
6539
+ return blockedApply(
6540
+ "actions:flag-many:table-text-handle-unsupported",
6541
+ "unsupported",
6542
+ "Issue flags attach to semantic scopes, not table text action handles.",
6543
+ "Use a semantic scope handle for issue flags."
6544
+ );
6545
+ }
6546
+ const resolved = resolveTarget(runtime, { handle: match.handle });
6547
+ if (!resolved.ok) return blockedApplyFromResolution(resolved);
6548
+ const result = createAttachFamily(runtime).createIssue({
6549
+ scopeId: match.handle.scopeId,
6550
+ summary: input.summary,
6551
+ ...input.severity ? { severity: input.severity } : {},
6552
+ ...input.actorId ? { actorId: input.actorId } : {},
6553
+ ...input.origin ? { origin: input.origin } : {}
6554
+ });
6555
+ return {
6556
+ status: result.created ? "applied" : "blocked",
6557
+ applied: result.created,
6558
+ changed: result.created,
6559
+ target: summarizeTarget(resolved.target),
6560
+ proposalId: result.issueId,
6561
+ ...result.auditReference ? { auditReference: result.auditReference } : {}
6562
+ };
6563
+ });
6564
+ const applied = results.filter((result) => result.applied);
6565
+ return {
6566
+ status: applied.length === results.length ? "applied" : applied.length > 0 ? "partial" : "blocked",
6567
+ applied: applied.length > 0,
6568
+ changed: results.some((result) => result.changed),
6569
+ results: Object.freeze(results)
6570
+ };
6571
+ },
6572
+ mark(input) {
6573
+ const resolved = resolveTarget(runtime, input.target);
6574
+ if (!resolved.ok) return blockedApplyFromResolution(resolved);
6575
+ if (resolved.target.kind !== "scope") {
6576
+ return blockedApply(
6577
+ "actions:mark:table-text-handle-unsupported",
6578
+ "unsupported",
6579
+ "Mark actions apply to semantic scopes, not table text action handles.",
6580
+ "Use a semantic scope handle for formatting marks."
6581
+ );
6582
+ }
6583
+ const result = createReplacementFamily(runtime).applyScopeAction({
6584
+ targetScopeId: resolved.target.handle.scopeId,
6585
+ action: input.action,
6586
+ ...input.actionId ? { actionId: input.actionId } : {},
6587
+ ...input.reason ? { reason: input.reason } : {},
6588
+ ...input.actorId ? { actorId: input.actorId } : {},
6589
+ ...input.origin ? { origin: input.origin } : {},
6590
+ ...input.proposalId ? { proposalId: input.proposalId } : {}
6591
+ });
6592
+ return projectApplyResult(result, resolved.target);
6593
+ },
6594
+ brief(input) {
6595
+ if (!input?.target) {
6596
+ return {
6597
+ status: "read",
6598
+ document: createInspectFamily(runtime).inspectDocument()
6599
+ };
6600
+ }
6601
+ const resolved = resolveTarget(runtime, input.target);
6602
+ if (!resolved.ok) {
6603
+ return {
6604
+ status: "blocked",
6605
+ blockers: resolved.blockers,
6606
+ blockerDetails: resolved.blockerDetails
6607
+ };
6608
+ }
6609
+ if (resolved.target.kind === "table-text") {
6610
+ const readback = resolved.target.action.readback;
6611
+ return {
6612
+ status: "read",
6613
+ target: summarizeTarget(resolved.target),
6614
+ ...readback ? { text: readback.text, excerpt: excerpt(readback.text) } : {}
6615
+ };
6616
+ }
6617
+ const text = resolved.target.scope.content.text;
6618
+ return {
6619
+ status: "read",
6620
+ target: summarizeTarget(resolved.target),
6621
+ text,
6622
+ excerpt: resolved.target.scope.content.excerpt ?? excerpt(text)
6623
+ };
6624
+ },
6625
+ fieldRefresh(input) {
6626
+ return applyModeledCommand(
6627
+ runtime,
6628
+ input.target,
6629
+ "fieldRefresh",
6630
+ {
6631
+ type: "field.refresh",
6632
+ options: { supportedOnly: input.supportedOnly ?? true },
6633
+ origin: actionOrigin(runtime, input)
6634
+ },
6635
+ input
6636
+ );
6637
+ },
6638
+ tocRefresh(input) {
6639
+ return applyModeledCommand(
6640
+ runtime,
6641
+ input.target,
6642
+ "tocRefresh",
6643
+ {
6644
+ type: "toc.refresh",
6645
+ options: {
6646
+ ...input.tocId ? { tocId: input.tocId } : {},
6647
+ ...input.mode ? { mode: input.mode } : {},
6648
+ ...input.maxLevel !== void 0 ? { maxLevel: input.maxLevel } : {}
6649
+ },
6650
+ origin: actionOrigin(runtime, input)
6651
+ },
6652
+ input
6653
+ );
6654
+ },
6655
+ bookmarkEdit(input) {
6656
+ if (input.text === void 0) {
6657
+ return blockedApply(
6658
+ "actions:bookmark-edit:text-required",
6659
+ "input",
6660
+ "Bookmark content edit actions require a text value.",
6661
+ "Retry with a text string."
6662
+ );
6663
+ }
6664
+ return applyModeledCommand(
6665
+ runtime,
6666
+ input.target,
6667
+ "bookmarkEdit",
6668
+ {
6669
+ type: "bookmark.edit-content",
6670
+ text: input.text,
6671
+ origin: actionOrigin(runtime, input)
6672
+ },
6673
+ input
6674
+ );
6675
+ },
6676
+ hyperlinkDestinationEdit(input) {
6677
+ if (!input.href) {
6678
+ return blockedApply(
6679
+ "actions:hyperlink-destination-edit:href-required",
6680
+ "input",
6681
+ "Hyperlink destination edit actions require a non-empty href.",
6682
+ "Retry with an href string."
6683
+ );
6684
+ }
6685
+ return applyModeledCommand(
6686
+ runtime,
6687
+ input.target,
6688
+ "hyperlinkDestinationEdit",
6689
+ {
6690
+ type: "hyperlink.update-destination",
6691
+ href: input.href,
6692
+ origin: actionOrigin(runtime, input)
6693
+ },
6694
+ input
6695
+ );
6696
+ },
6697
+ listOperation(input) {
6698
+ return applyListOperation(runtime, input);
6699
+ },
6700
+ tableFragment(input) {
6701
+ const result = createTableActionFamily(runtime).applyTableAction(input);
6702
+ return projectTableApplyResult(result);
6703
+ },
6704
+ tableSelection(input) {
6705
+ const result = createTableActionFamily(runtime).applyTableAction(input);
6706
+ return projectTableApplyResult(result);
6707
+ },
6708
+ runPlan(input) {
6709
+ return runPlan(runtime, input);
6710
+ }
6711
+ };
6712
+ const family = {
6713
+ actions() {
6714
+ return Object.freeze(category);
6715
+ }
6716
+ };
6717
+ return { actions: family.actions() };
6718
+ }
6719
+ function runPlan(runtime, input) {
6720
+ const mode = input.mode ?? "preview";
6721
+ const maxSteps = Math.max(0, input.maxSteps ?? DEFAULT_PLAN_STEP_LIMIT);
6722
+ if (!Array.isArray(input.steps) || input.steps.length === 0) {
6723
+ return blockedPlan(
6724
+ mode,
6725
+ "actions:plan:empty",
6726
+ "input",
6727
+ "Run plan requires at least one structured step.",
6728
+ "Have the host generate explicit steps with exact scope handles or opaque action handles."
6729
+ );
6730
+ }
6731
+ if (input.steps.length > maxSteps) {
6732
+ return blockedPlan(
6733
+ mode,
6734
+ `actions:plan:too-many-steps:${input.steps.length}`,
6735
+ "input",
6736
+ `Run plan received ${input.steps.length} steps, exceeding the limit of ${maxSteps}.`,
6737
+ "Split the host-generated plan into smaller calls and retry."
6738
+ );
6739
+ }
6740
+ const stopOnBlocker = input.stopOnBlocker ?? true;
6741
+ const results = [];
6742
+ for (const step of input.steps) {
6743
+ const result = runPlanStep(runtime, mode, step, input);
6744
+ results.push(result);
6745
+ if (stopOnBlocker && (result.status === "blocked" || result.status === "unsupported")) {
6746
+ break;
6747
+ }
6748
+ }
6749
+ return summarizePlanResult(mode, results);
6750
+ }
6751
+ function runPlanStep(runtime, mode, step, plan) {
6752
+ if (!step.id) {
6753
+ return blockedPlanStep(
6754
+ "unknown-step",
6755
+ step.kind,
6756
+ "actions:plan:step-id-required",
6757
+ "input",
6758
+ "Every plan step requires a stable id supplied by the host.",
6759
+ "Regenerate the plan with stable per-step ids."
6760
+ );
6761
+ }
6762
+ if (step.kind === "tableAction" || step.kind === "tableFragment" || step.kind === "tableSelection") {
6763
+ return runPlanTableActionStep(runtime, mode, step, plan);
6764
+ }
6765
+ const before = step.target ? readPlanTarget(runtime, step.target) : readDocumentPlanTarget(runtime);
6766
+ if (!before.ok) {
6767
+ return blockedPlanStepFromDetails(step.id, step.kind, before.blockerDetails);
6768
+ }
6769
+ const precondition = checkPlanPreconditions(step, before.readback);
6770
+ if (precondition) {
6771
+ return blockedPlanStepFromDetails(step.id, step.kind, [precondition], {
6772
+ target: before.target,
6773
+ beforeReadback: before.readback,
6774
+ tableAction: before.tableAction
6775
+ });
6776
+ }
6777
+ if (step.kind === "brief") {
6778
+ return {
6779
+ id: step.id,
6780
+ kind: step.kind,
6781
+ status: "read",
6782
+ applied: false,
6783
+ changed: false,
6784
+ ...before.target ? { target: before.target } : {},
6785
+ ...before.tableAction ? { tableAction: before.tableAction } : {},
6786
+ ...before.readback ? { beforeReadback: before.readback } : {}
6787
+ };
6788
+ }
6789
+ const capabilityBlocker = checkPlanStepCapability(runtime, step, before);
6790
+ if (capabilityBlocker) {
6791
+ return blockedPlanStepFromDetails(step.id, step.kind, [capabilityBlocker], {
6792
+ target: before.target,
6793
+ beforeReadback: before.readback,
6794
+ tableAction: before.tableAction
6795
+ });
6796
+ }
6797
+ if (mode === "preview") {
6798
+ return {
6799
+ id: step.id,
6800
+ kind: step.kind,
6801
+ status: "planned",
6802
+ applied: false,
6803
+ changed: false,
6804
+ ...before.target ? { target: before.target } : {},
6805
+ ...before.tableAction ? { tableAction: before.tableAction } : {},
6806
+ ...before.readback ? { beforeReadback: before.readback } : {}
6807
+ };
6808
+ }
6809
+ const applied = applyPlanStep(runtime, step, plan);
6810
+ const after = step.kind === "flag" ? before : step.target ? readPlanTarget(runtime, step.target) : before;
6811
+ return {
6812
+ id: step.id,
6813
+ kind: step.kind,
6814
+ status: applied.status === "unsupported" ? "unsupported" : applied.applied ? "applied" : "blocked",
6815
+ applied: applied.applied,
6816
+ changed: applied.changed,
6817
+ ...applied.target ?? before.target ? { target: applied.target ?? before.target } : {},
6818
+ ...before.tableAction ? { tableAction: before.tableAction } : {},
6819
+ ...before.readback ? { beforeReadback: before.readback } : {},
6820
+ ...after.ok && after.readback ? { afterReadback: after.readback } : {},
6821
+ ...applied.proposalId ? { proposalId: applied.proposalId } : {},
6822
+ ...applied.posture ? { posture: applied.posture } : {},
6823
+ ...applied.blockers ? { blockers: applied.blockers } : {},
6824
+ ...applied.blockerDetails ? { blockerDetails: applied.blockerDetails } : {},
6825
+ ...applied.auditReference ? { auditReference: applied.auditReference } : {},
6826
+ ...applied.commandReference ? { commandReference: applied.commandReference } : {}
6827
+ };
6828
+ }
6829
+ function runPlanTableActionStep(runtime, mode, step, plan) {
6830
+ const before = readPlanTableAction(runtime, step.actionHandle);
6831
+ if (!before.ok) {
6832
+ return blockedPlanStepFromDetails(step.id, step.kind, before.blockerDetails);
6833
+ }
6834
+ const tableAction = before.tableAction;
6835
+ if (!tableAction) {
6836
+ return blockedPlanStep(
6837
+ step.id,
6838
+ step.kind,
6839
+ `actions:plan:action-handle-not-found:${step.actionHandle}`,
6840
+ "unresolved-target",
6841
+ "No current table action matches the supplied opaque actionHandle.",
6842
+ "Have the host re-list table actions and regenerate the plan with a fresh actionHandle."
6843
+ );
6844
+ }
6845
+ const precondition = checkPlanPreconditions(step, before.readback);
6846
+ if (precondition) {
6847
+ return blockedPlanStepFromDetails(step.id, step.kind, [precondition], {
6848
+ beforeReadback: before.readback,
6849
+ tableAction
6850
+ });
6851
+ }
6852
+ const operationKind = step.operation?.kind;
6853
+ const operationFamilyBlocker = tablePlanOperationFamilyBlocker(step.kind, operationKind);
6854
+ if (operationFamilyBlocker) {
6855
+ return blockedPlanStepFromDetails(step.id, step.kind, [operationFamilyBlocker], {
6856
+ beforeReadback: before.readback,
6857
+ tableAction
6858
+ });
6859
+ }
6860
+ if (!operationKind || !tableAction.callableOperations.includes(operationKind)) {
6861
+ return blockedPlanStepFromDetails(
6862
+ step.id,
6863
+ step.kind,
6864
+ [
6865
+ blocker(
6866
+ `actions:plan:table-operation-unsupported:${step.id}`,
6867
+ "unsupported",
6868
+ "The table action handle does not advertise the requested operation as callable.",
6869
+ "Have the host re-list table actions and choose an operation from callableOperations."
6870
+ )
6871
+ ],
6872
+ {
6873
+ beforeReadback: before.readback,
6874
+ tableAction
6875
+ }
6876
+ );
6877
+ }
6878
+ if (mode === "preview") {
6879
+ return {
6880
+ id: step.id,
6881
+ kind: step.kind,
6882
+ status: "planned",
6883
+ applied: false,
6884
+ changed: false,
6885
+ tableAction,
6886
+ ...before.readback ? { beforeReadback: before.readback } : {}
6887
+ };
6888
+ }
6889
+ const result = createTableActionFamily(runtime).applyTableAction({
6890
+ actionHandle: step.actionHandle,
6891
+ operation: step.operation,
6892
+ ...step.scopeId ? { scopeId: step.scopeId } : {},
6893
+ actorId: step.actorId ?? plan.actorId,
6894
+ origin: step.origin ?? plan.origin,
6895
+ ...step.proposalId ? { proposalId: step.proposalId } : {}
6896
+ });
6897
+ const after = readPlanTableAction(runtime, step.actionHandle);
6898
+ return {
6899
+ id: step.id,
6900
+ kind: step.kind,
6901
+ status: result.applied ? "applied" : "blocked",
6902
+ applied: result.applied,
6903
+ changed: result.changed,
6904
+ tableAction,
6905
+ ...before.readback ? { beforeReadback: before.readback } : {},
6906
+ ...result.afterReadback ? { afterReadback: tableReadbackToPlanReadback(result.afterReadback) } : after.ok && after.readback ? { afterReadback: after.readback } : {},
6907
+ proposalId: result.proposalId,
6908
+ ...result.posture ? { posture: result.posture } : {},
6909
+ ...result.blockers ? { blockers: result.blockers } : {},
6910
+ ...result.blockerDetails ? {
6911
+ blockerDetails: Object.freeze(
6912
+ result.blockerDetails.map(
6913
+ (detail) => blocker(detail.code, "blocked", detail.message, detail.nextStep)
6914
+ )
6915
+ )
6916
+ } : {},
6917
+ ...result.commandReference ? { commandReference: result.commandReference } : {}
6918
+ };
6919
+ }
6920
+ function locateAll(runtime, input) {
6921
+ if (!input.query) {
6922
+ const detail = blocker(
6923
+ "actions:locate:query-required",
6924
+ "input",
6925
+ "Locate actions require a non-empty query.",
6926
+ "Retry with a non-empty query string."
6927
+ );
6928
+ return {
6929
+ status: "blocked",
6930
+ matches: Object.freeze([]),
6931
+ blockers: Object.freeze([detail.code]),
6932
+ blockerDetails: Object.freeze([detail])
6933
+ };
6934
+ }
6935
+ const compiler = createScopeCompilerService(runtime);
6936
+ const limit = Math.max(0, input.limit ?? DEFAULT_LOCATE_LIMIT);
6937
+ const matches = [];
6938
+ const scopes = compiler.compileAllScopes();
6939
+ const shouldScanTableText = input.includeTableText === true || input.includeTableText !== false && input.kind !== void 0 && isTableFamilyScope(input.kind);
6940
+ for (const scope of scopes) {
6941
+ if (matches.length >= limit) break;
6942
+ if (input.kind && scope.kind !== input.kind) continue;
6943
+ if (isTableFamilyScope(scope.kind)) continue;
6944
+ const text = scope.content.text;
6945
+ if (!textMatches(text, input.query, input.matchCase)) continue;
6946
+ matches.push({
6947
+ kind: scope.kind,
6948
+ text,
6949
+ excerpt: scope.content.excerpt ?? excerpt(text),
6950
+ handle: scope.handle,
6951
+ isEmpty: text.trim().length === 0
6952
+ });
6953
+ }
6954
+ if (matches.length < limit && shouldScanTableText) {
6955
+ const tableTextScopeLimit = Math.max(
6956
+ 0,
6957
+ input.tableTextScopeLimit ?? DEFAULT_TABLE_TEXT_SCOPE_LIMIT
6958
+ );
6959
+ let tableScopesScanned = 0;
6960
+ for (const scope of scopes) {
6961
+ if (matches.length >= limit || tableScopesScanned >= tableTextScopeLimit) break;
6962
+ if (scope.kind !== "table") continue;
6963
+ tableScopesScanned += 1;
6964
+ for (const action of tableTextActionsForScope(runtime, scope.handle)) {
6965
+ if (matches.length >= limit) break;
6966
+ const readback = action.readback;
6967
+ if (!readback || !textMatches(readback.text, input.query, input.matchCase)) continue;
6968
+ matches.push({
6969
+ kind: "table-text",
6970
+ text: readback.text,
6971
+ excerpt: excerpt(readback.text),
6972
+ actionHandle: action.actionHandle,
6973
+ readback,
6974
+ isEmpty: readback.isEmpty
6975
+ });
6976
+ }
6977
+ }
6978
+ }
6979
+ return {
6980
+ status: matches.length > 0 ? "found" : "not-found",
6981
+ matches: Object.freeze(matches),
6982
+ ...matches.length === 0 ? { blockers: Object.freeze([`actions:locate:not-found:${input.query}`]) } : {}
6983
+ };
6984
+ }
6985
+ function resolveTarget(runtime, target) {
6986
+ if ("actionHandle" in target) {
6987
+ const action = findTableAction(runtime, target.actionHandle);
6988
+ if (!action) {
6989
+ return blockedResolution(
6990
+ `actions:target:action-handle-not-found:${target.actionHandle}`,
6991
+ "unresolved-target",
6992
+ "No current table text action matches the supplied actionHandle.",
6993
+ "Call ai.actions.locateAll or ai.listTableActions again and retry with a fresh actionHandle."
6994
+ );
6995
+ }
6996
+ if (action.family !== "table-text") {
6997
+ return blockedResolution(
6998
+ `actions:target:action-handle-not-text:${target.actionHandle}`,
6999
+ "unsupported",
7000
+ "This actionHandle is not a table text action.",
7001
+ "Use table structure handles with ai.applyTableAction, or use a table text actionHandle for rewrite."
7002
+ );
7003
+ }
7004
+ return { ok: true, target: { kind: "table-text", action } };
7005
+ }
7006
+ if ("handle" in target) {
7007
+ const compiled = createScopeCompilerService(runtime).compileScopeById(target.handle.scopeId);
7008
+ if (!compiled) {
7009
+ return blockedResolution(
7010
+ `actions:target:scope-not-resolvable:${target.handle.scopeId}`,
7011
+ "unresolved-target",
7012
+ "The target scope no longer resolves in the current document.",
7013
+ "Refresh the scope handle with ai.actions.locateAll or ai.resolveReference, then retry."
7014
+ );
7015
+ }
7016
+ return { ok: true, target: { kind: "scope", scope: compiled.scope, handle: compiled.scope.handle } };
7017
+ }
7018
+ const located = locateAll(runtime, {
7019
+ query: target.query,
7020
+ kind: target.kind,
7021
+ matchCase: target.matchCase,
7022
+ includeTableText: target.includeTableText,
7023
+ tableTextScopeLimit: target.tableTextScopeLimit,
7024
+ limit: 2
7025
+ });
7026
+ if (located.status !== "found") {
7027
+ return blockedResolution(
7028
+ located.blockers?.[0] ?? `actions:target:not-found:${target.query}`,
7029
+ located.status === "blocked" ? "input" : "unresolved-target",
7030
+ located.status === "blocked" ? "The target query was blocked." : "No unique target matched the query.",
7031
+ "Call ai.actions.locateAll and retry with a returned handle or actionHandle.",
7032
+ located.blockerDetails
7033
+ );
7034
+ }
7035
+ if (located.matches.length > 1) {
7036
+ return blockedResolution(
7037
+ `actions:target:ambiguous:${target.query}`,
7038
+ "ambiguous-target",
7039
+ "The query matched more than one target.",
7040
+ "Call ai.actions.locateAll and retry with the exact returned handle or actionHandle."
7041
+ );
7042
+ }
7043
+ const [match] = located.matches;
7044
+ if (!match) {
7045
+ return blockedResolution(
7046
+ `actions:target:not-found:${target.query}`,
7047
+ "unresolved-target",
7048
+ "No target matched the query.",
7049
+ "Call ai.actions.locateAll with a broader query."
7050
+ );
7051
+ }
7052
+ if (match.actionHandle) return resolveTarget(runtime, { actionHandle: match.actionHandle });
7053
+ if (match.handle) return resolveTarget(runtime, { handle: match.handle });
7054
+ return blockedResolution(
7055
+ `actions:target:not-callable:${target.query}`,
7056
+ "blocked",
7057
+ "The locate match did not carry a callable target.",
7058
+ "Refresh the locate result and retry."
7059
+ );
7060
+ }
7061
+ function applyRewrite(runtime, target, input) {
7062
+ if (target.kind === "table-text") {
7063
+ const result2 = createTableActionFamily(runtime).applyTableAction({
7064
+ actionHandle: target.action.actionHandle,
7065
+ operation: { kind: "paste-cell-content", content: { kind: "text", text: input.text } },
7066
+ actorId: input.actorId,
7067
+ origin: input.origin,
7068
+ proposalId: input.proposalId ?? mockId(runtime.getSessionState().documentId, `actions-rewrite-${target.action.actionHandle}`)
7069
+ });
7070
+ return {
7071
+ status: result2.applied ? "applied" : "blocked",
7072
+ applied: result2.applied,
7073
+ changed: result2.changed,
7074
+ target: summarizeTarget(target),
7075
+ proposalId: result2.proposalId,
7076
+ posture: result2.posture,
7077
+ ...result2.blockers ? { blockers: result2.blockers } : {},
7078
+ ...result2.blockerDetails ? {
7079
+ blockerDetails: Object.freeze(
7080
+ result2.blockerDetails.map(
7081
+ (detail) => blocker(detail.code, "blocked", detail.message, detail.nextStep)
7082
+ )
7083
+ )
7084
+ } : {},
7085
+ ...result2.commandReference ? { commandReference: result2.commandReference } : {},
7086
+ ...result2.afterReadback ? { afterReadback: result2.afterReadback } : {}
7087
+ };
7088
+ }
7089
+ const beforeText = target.scope.content.text;
7090
+ const result = createReplacementFamily(runtime).applyReplacementScope({
7091
+ targetScopeId: target.handle.scopeId,
7092
+ operation: "replace",
7093
+ proposedText: input.text,
7094
+ ...input.actionId ? { actionId: input.actionId } : {},
7095
+ ...input.reason ? { reason: input.reason } : {},
7096
+ ...input.preserve ? { preserve: input.preserve } : {},
7097
+ ...input.actorId ? { actorId: input.actorId } : {},
7098
+ ...input.origin ? { origin: input.origin } : {},
7099
+ ...input.proposalId ? { proposalId: input.proposalId } : {}
7100
+ });
7101
+ return projectRewriteScopeResult(runtime, result, target, beforeText, input.text);
7102
+ }
7103
+ function projectApplyResult(result, target) {
7104
+ return {
7105
+ status: result.applied ? "applied" : "blocked",
7106
+ applied: result.applied,
7107
+ changed: result.applied,
7108
+ target: summarizeTarget(target),
7109
+ proposalId: result.proposalId,
7110
+ ...result.posture ? { posture: result.posture } : {},
7111
+ ...result.blockers ? { blockers: result.blockers } : {},
7112
+ ...result.blockerDetails ? {
7113
+ blockerDetails: Object.freeze(
7114
+ result.blockerDetails.map(
7115
+ (detail) => blocker(detail.code, "blocked", detail.message, detail.nextStep)
7116
+ )
7117
+ )
7118
+ } : {},
7119
+ ...result.auditReference ? { auditReference: result.auditReference } : {}
7120
+ };
7121
+ }
7122
+ function projectRewriteScopeResult(runtime, result, target, beforeText, proposedText) {
7123
+ if (!result.applied) return projectApplyResult(result, target);
7124
+ const compiledAfter = createScopeCompilerService(runtime).compileScopeById(
7125
+ target.handle.scopeId
7126
+ );
7127
+ if (!compiledAfter) {
7128
+ return blockedRewriteReadback(
7129
+ result,
7130
+ target,
7131
+ `actions:rewrite:authoritative-readback-unresolvable:${target.handle.scopeId}`,
7132
+ "The replacement primitive reported success, but the target scope could not be re-read afterwards.",
7133
+ "Treat the mutation as untrusted. Re-locate the target and retry only after the scope resolves, or create an issue flag instead."
7134
+ );
7135
+ }
7136
+ const afterText = compiledAfter.scope.content.text;
7137
+ const changed = afterText !== beforeText;
7138
+ const expectedPresent = proposedText.length === 0 ? afterText.length === 0 : afterText.includes(proposedText);
7139
+ if (!changed) {
7140
+ return blockedRewriteReadback(
7141
+ result,
7142
+ { ...target, scope: compiledAfter.scope, handle: compiledAfter.scope.handle },
7143
+ `actions:rewrite:authoritative-readback-unchanged:${target.handle.scopeId}`,
7144
+ "The replacement primitive reported success, but authoritative scope readback showed unchanged text.",
7145
+ "Treat the mutation as not applied. Retry with a narrower scope or create an issue flag; do not claim the replacement succeeded."
7146
+ );
7147
+ }
7148
+ if (!expectedPresent) {
7149
+ return blockedRewriteReadback(
7150
+ result,
7151
+ { ...target, scope: compiledAfter.scope, handle: compiledAfter.scope.handle },
7152
+ `actions:rewrite:authoritative-readback-mismatch:${target.handle.scopeId}`,
7153
+ "The replacement primitive reported success, but authoritative scope readback did not contain the proposed text.",
7154
+ "Treat the mutation as suspect. Re-read the target, inspect the exported package when available, and retry only with a verified target."
7155
+ );
7156
+ }
7157
+ return {
7158
+ status: "applied",
7159
+ applied: true,
7160
+ changed: true,
7161
+ target: summarizeTarget({
7162
+ ...target,
7163
+ scope: compiledAfter.scope,
7164
+ handle: compiledAfter.scope.handle
7165
+ }),
7166
+ proposalId: result.proposalId,
7167
+ ...result.posture ? { posture: result.posture } : {},
7168
+ ...result.auditReference ? { auditReference: result.auditReference } : {}
7169
+ };
7170
+ }
7171
+ function blockedRewriteReadback(result, target, code, message, nextStep) {
7172
+ const detail = blocker(code, "blocked", message, nextStep);
7173
+ return {
7174
+ status: "blocked",
7175
+ applied: false,
7176
+ changed: false,
7177
+ target: summarizeTarget(target),
7178
+ proposalId: result.proposalId,
7179
+ posture: "suspect-readback",
7180
+ blockers: Object.freeze([detail.code]),
7181
+ blockerDetails: Object.freeze([detail]),
7182
+ ...result.auditReference ? { auditReference: result.auditReference } : {}
7183
+ };
7184
+ }
7185
+ function summarizeTarget(target) {
7186
+ if (target.kind === "table-text") {
7187
+ return {
7188
+ kind: "table-text",
7189
+ actionHandle: target.action.actionHandle,
7190
+ ...target.action.readback ? { readback: target.action.readback } : {},
7191
+ canRewriteText: true,
7192
+ canInsertAdjacentText: false,
7193
+ canFlag: false,
7194
+ canMark: false
7195
+ };
7196
+ }
7197
+ return {
7198
+ kind: target.scope.kind,
7199
+ handle: target.handle,
7200
+ canRewriteText: canRewriteScopeText(target.scope),
7201
+ canInsertAdjacentText: canRewriteScopeText(target.scope),
7202
+ canFlag: true,
7203
+ canMark: canMarkScope(target.scope)
7204
+ };
7205
+ }
7206
+ function tableTextActionsForScope(runtime, handle) {
7207
+ const result = createTableActionFamily(runtime).listTableActions({
7208
+ handle,
7209
+ nowUtc: currentAuditTimestamp(runtime)
7210
+ });
7211
+ return result.actions.filter((action) => action.family === "table-text");
7212
+ }
7213
+ function findTableAction(runtime, actionHandle) {
7214
+ const action = findAnyTableAction(runtime, actionHandle);
7215
+ return action?.family === "table-text" ? action : null;
7216
+ }
7217
+ function findAnyTableAction(runtime, actionHandle) {
7218
+ const compiler = createScopeCompilerService(runtime);
7219
+ for (const scope of compiler.compileAllScopes()) {
7220
+ if (scope.kind !== "table") continue;
7221
+ const result = createTableActionFamily(runtime).listTableActions({
7222
+ handle: scope.handle,
7223
+ nowUtc: currentAuditTimestamp(runtime)
7224
+ });
7225
+ const action = result.actions.find(
7226
+ (candidate) => candidate.actionHandle === actionHandle
7227
+ );
7228
+ if (action) return action;
7229
+ }
7230
+ return null;
7231
+ }
7232
+ function readDocumentPlanTarget(runtime) {
7233
+ const document = createInspectFamily(runtime).inspectDocument();
7234
+ return {
7235
+ ok: true,
7236
+ readback: {
7237
+ text: document.semanticSummary,
7238
+ excerpt: document.semanticSummary,
7239
+ isEmpty: document.semanticSummary.trim().length === 0
7240
+ }
7241
+ };
7242
+ }
7243
+ function readPlanTarget(runtime, target) {
7244
+ if ("handle" in target) {
7245
+ const compiled = createScopeCompilerService(runtime).compileScopeById(target.handle.scopeId);
7246
+ if (!compiled) {
7247
+ return {
7248
+ ok: false,
7249
+ blockerDetails: Object.freeze([
7250
+ blocker(
7251
+ `actions:plan:target-not-resolvable:${target.handle.scopeId}`,
7252
+ "unresolved-target",
7253
+ "The plan step scope handle no longer resolves.",
7254
+ "Have the host re-locate the target and regenerate the plan with a fresh handle."
7255
+ )
7256
+ ])
7257
+ };
7258
+ }
7259
+ const text = compiled.scope.content.text;
7260
+ const resolved = {
7261
+ kind: "scope",
7262
+ scope: compiled.scope,
7263
+ handle: compiled.scope.handle
7264
+ };
7265
+ return {
7266
+ ok: true,
7267
+ target: summarizeTarget(resolved),
7268
+ readback: {
7269
+ text,
7270
+ excerpt: compiled.scope.content.excerpt ?? excerpt(text),
7271
+ isEmpty: text.trim().length === 0
7272
+ }
7273
+ };
7274
+ }
7275
+ if (!("actionHandle" in target) || typeof target.actionHandle !== "string") {
7276
+ return {
7277
+ ok: false,
7278
+ blockerDetails: Object.freeze([
7279
+ blocker(
7280
+ "actions:plan:exact-target-required",
7281
+ "input",
7282
+ "Plan steps accept only exact ScopeHandle or opaque actionHandle targets.",
7283
+ "Have the host resolve queries before calling runPlan and pass only returned handles."
7284
+ )
7285
+ ])
7286
+ };
7287
+ }
7288
+ return readPlanTableAction(runtime, target.actionHandle);
7289
+ }
7290
+ function readPlanTableAction(runtime, actionHandle) {
7291
+ const action = findAnyTableAction(runtime, actionHandle);
7292
+ if (!action) {
7293
+ return {
7294
+ ok: false,
7295
+ blockerDetails: Object.freeze([
7296
+ blocker(
7297
+ `actions:plan:action-handle-not-found:${actionHandle}`,
7298
+ "unresolved-target",
7299
+ "No current table action matches the supplied opaque actionHandle.",
7300
+ "Have the host call ai.listTableActions or ai.actions.locateAll again and regenerate the plan with a fresh actionHandle."
7301
+ )
7302
+ ])
7303
+ };
7304
+ }
7305
+ return {
7306
+ ok: true,
7307
+ tableAction: action,
7308
+ ...action.family === "table-text" ? { target: summarizeTarget({ kind: "table-text", action }) } : {},
7309
+ ...action.readback ? { readback: tableReadbackToPlanReadback(action.readback) } : {}
7310
+ };
7311
+ }
7312
+ function tableReadbackToPlanReadback(readback) {
7313
+ return {
7314
+ text: readback.text,
7315
+ excerpt: excerpt(readback.text),
7316
+ isEmpty: readback.isEmpty
7317
+ };
7318
+ }
7319
+ function checkPlanPreconditions(step, readback) {
7320
+ const preconditions = step.preconditions;
7321
+ if (!preconditions) return null;
7322
+ if (preconditions.expectedBeforeTextIncludes !== void 0 && !readback?.text?.includes(preconditions.expectedBeforeTextIncludes)) {
7323
+ return blocker(
7324
+ `actions:plan:precondition-text-mismatch:${step.id}`,
7325
+ "blocked",
7326
+ "The target readback did not contain the host-provided expected text before the step.",
7327
+ "Re-read the target and regenerate the plan from current document state."
7328
+ );
7329
+ }
7330
+ if (preconditions.expectedBeforeIsEmpty !== void 0 && readback?.isEmpty !== preconditions.expectedBeforeIsEmpty) {
7331
+ return blocker(
7332
+ `actions:plan:precondition-empty-mismatch:${step.id}`,
7333
+ "blocked",
7334
+ "The target empty/non-empty state no longer matches the host-provided precondition.",
7335
+ "Re-read the target and regenerate the plan from current document state."
7336
+ );
7337
+ }
7338
+ return null;
7339
+ }
7340
+ function checkPlanStepCapability(runtime, step, before) {
7341
+ const target = before.target;
7342
+ if (!target) {
7343
+ return blocker(
7344
+ `actions:plan:target-required:${step.id}`,
7345
+ "input",
7346
+ "The plan step requires an exact target handle.",
7347
+ "Regenerate the plan with a ScopeHandle or opaque actionHandle returned by L09."
7348
+ );
7349
+ }
7350
+ if (step.kind === "rewrite" && !target.canRewriteText) {
7351
+ return blocker(
7352
+ `actions:plan:target-not-rewriteable:${step.id}`,
7353
+ "unsupported",
7354
+ "The target capability posture does not allow text rewrite.",
7355
+ "Use a supported text target, table text actionHandle, or create an issue flag instead."
7356
+ );
7357
+ }
7358
+ if (step.kind === "insertText" && !target.canInsertAdjacentText) {
7359
+ return blocker(
7360
+ `actions:plan:target-not-insertable:${step.id}`,
7361
+ "unsupported",
7362
+ "The target capability posture does not allow adjacent text insertion.",
7363
+ "Use a supported semantic scope or choose rewrite/flag instead."
7364
+ );
7365
+ }
7366
+ if (step.kind === "flag" && !target.canFlag) {
7367
+ return blocker(
7368
+ `actions:plan:target-not-flaggable:${step.id}`,
7369
+ "unsupported",
7370
+ "The target capability posture does not allow issue flags.",
7371
+ "Use the owning semantic scope handle for issue flags."
7372
+ );
7373
+ }
7374
+ if (step.kind === "mark" && !target.canMark) {
7375
+ return blocker(
7376
+ `actions:plan:target-not-markable:${step.id}`,
7377
+ "unsupported",
7378
+ "The target capability posture does not allow formatting marks.",
7379
+ "Use a supported semantic scope or create an issue flag instead."
7380
+ );
7381
+ }
7382
+ if (step.kind === "fieldRefresh" || step.kind === "tocRefresh" || step.kind === "bookmarkEdit" || step.kind === "hyperlinkDestinationEdit") {
7383
+ const resolved = resolveScopeExactTarget(runtime, step.target, step.kind);
7384
+ if (!resolved.ok) return resolved.blockerDetails[0] ?? null;
7385
+ const targetRef = resolveModeledEditableTarget(runtime, resolved.target, step.kind);
7386
+ if (!targetRef.ok) return targetRef.blockerDetails[0] ?? null;
7387
+ return null;
7388
+ }
7389
+ if (step.kind === "listOperation") {
7390
+ const resolved = resolveScopeExactTarget(runtime, step.target, step.kind);
7391
+ if (!resolved.ok) return resolved.blockerDetails[0] ?? null;
7392
+ const scope = resolved.target.scope;
7393
+ if (scope.kind !== "list-item" && scope.kind !== "paragraph" && scope.kind !== "heading") {
7394
+ return blocker(
7395
+ `actions:list-operation:target-kind-unsupported:${scope.kind}`,
7396
+ "unsupported",
7397
+ "List operations require a paragraph-like scope handle.",
7398
+ "Use ai.actions.locateAll or ai.listScopes to select a paragraph, heading, or list-item handle."
7399
+ );
7400
+ }
7401
+ if (paragraphIndexFromHandle(scope.handle) === null) {
7402
+ return blockerWithOwner(
7403
+ `actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
7404
+ "blocked",
7405
+ "The list operation target did not resolve to a current paragraph index.",
7406
+ "Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
7407
+ "L08 semantic scope compiler"
7408
+ );
7409
+ }
7410
+ return null;
7411
+ }
7412
+ return null;
7413
+ }
7414
+ function applyPlanStep(runtime, step, plan) {
7415
+ switch (step.kind) {
7416
+ case "rewrite":
7417
+ return createActionsFamily(runtime).actions.rewrite({
7418
+ target: step.target,
7419
+ text: step.text,
7420
+ ...step.actionId ? { actionId: step.actionId } : {},
7421
+ ...step.reason ? { reason: step.reason } : {},
7422
+ ...step.preserve ? { preserve: step.preserve } : {},
7423
+ actorId: step.actorId ?? plan.actorId,
7424
+ origin: step.origin ?? plan.origin,
7425
+ ...step.proposalId ? { proposalId: step.proposalId } : {}
7426
+ });
7427
+ case "insertText":
7428
+ return createActionsFamily(runtime).actions.insertText({
7429
+ target: step.target,
7430
+ position: step.position,
7431
+ text: step.text,
7432
+ ...step.actionId ? { actionId: step.actionId } : {},
7433
+ ...step.reason ? { reason: step.reason } : {},
7434
+ ...step.preserve ? { preserve: step.preserve } : {},
7435
+ actorId: step.actorId ?? plan.actorId,
7436
+ origin: step.origin ?? plan.origin,
7437
+ ...step.proposalId ? { proposalId: step.proposalId } : {}
7438
+ });
7439
+ case "flag":
7440
+ return createActionsFamily(runtime).actions.flag({
7441
+ target: step.target,
7442
+ summary: step.summary,
7443
+ ...step.severity ? { severity: step.severity } : {},
7444
+ actorId: step.actorId ?? plan.actorId,
7445
+ origin: step.origin ?? plan.origin
7446
+ });
7447
+ case "mark":
7448
+ return createActionsFamily(runtime).actions.mark({
7449
+ target: step.target,
7450
+ action: step.action,
7451
+ ...step.actionId ? { actionId: step.actionId } : {},
7452
+ ...step.reason ? { reason: step.reason } : {},
7453
+ actorId: step.actorId ?? plan.actorId,
7454
+ origin: step.origin ?? plan.origin,
7455
+ ...step.proposalId ? { proposalId: step.proposalId } : {}
7456
+ });
7457
+ case "fieldRefresh":
7458
+ return createActionsFamily(runtime).actions.fieldRefresh({
7459
+ target: step.target,
7460
+ supportedOnly: step.supportedOnly,
7461
+ actorId: step.actorId ?? plan.actorId,
7462
+ origin: step.origin ?? plan.origin
7463
+ });
7464
+ case "tocRefresh":
7465
+ return createActionsFamily(runtime).actions.tocRefresh({
7466
+ target: step.target,
7467
+ ...step.tocId ? { tocId: step.tocId } : {},
7468
+ ...step.mode ? { mode: step.mode } : {},
7469
+ ...step.maxLevel !== void 0 ? { maxLevel: step.maxLevel } : {},
7470
+ actorId: step.actorId ?? plan.actorId,
7471
+ origin: step.origin ?? plan.origin
7472
+ });
7473
+ case "bookmarkEdit":
7474
+ return createActionsFamily(runtime).actions.bookmarkEdit({
7475
+ target: step.target,
7476
+ text: step.text,
7477
+ actorId: step.actorId ?? plan.actorId,
7478
+ origin: step.origin ?? plan.origin
7479
+ });
7480
+ case "hyperlinkDestinationEdit":
7481
+ return createActionsFamily(runtime).actions.hyperlinkDestinationEdit({
7482
+ target: step.target,
7483
+ href: step.href,
7484
+ actorId: step.actorId ?? plan.actorId,
7485
+ origin: step.origin ?? plan.origin
7486
+ });
7487
+ case "listOperation":
7488
+ return createActionsFamily(runtime).actions.listOperation({
7489
+ target: step.target,
7490
+ operation: step.operation,
7491
+ actorId: step.actorId ?? plan.actorId,
7492
+ origin: step.origin ?? plan.origin
7493
+ });
7494
+ }
7495
+ }
7496
+ function applyModeledCommand(runtime, targetInput, kind, commandInput, reference) {
7497
+ const resolvedScope = resolveScopeExactTarget(runtime, targetInput, kind);
7498
+ if (!resolvedScope.ok) return blockedApplyFromResolution(resolvedScope);
7499
+ const targetRef = resolveModeledEditableTarget(runtime, resolvedScope.target, kind);
7500
+ if (!targetRef.ok) {
7501
+ return blockedApplyFromResolution(targetRef);
7502
+ }
7503
+ const before = runtime.getCanonicalDocument();
7504
+ const command = {
7505
+ ...commandInput,
7506
+ editableTarget: targetRef.targetRef
7507
+ };
7508
+ runtime.dispatch(command);
7509
+ const after = runtime.getCanonicalDocument();
7510
+ const changed = after !== before;
7511
+ if (!changed) {
7512
+ return blockedApply(
7513
+ `actions:${actionSlug(kind)}:runtime-noop:${resolvedScope.target.handle.scopeId}`,
7514
+ "blocked",
7515
+ "The modeled runtime command was accepted but produced no document change.",
7516
+ `Route to ${ownerRouteForModeledAction(kind)} with the target handle and command family evidence; do not report this as a successful warning path.`,
7517
+ [
7518
+ blockerWithOwner(
7519
+ `actions:${actionSlug(kind)}:runtime-noop:${resolvedScope.target.handle.scopeId}`,
7520
+ "blocked",
7521
+ "The modeled runtime command was accepted but produced no document change.",
7522
+ `Route to ${ownerRouteForModeledAction(kind)} with the target handle and command family evidence; do not report this as a successful warning path.`,
7523
+ ownerRouteForModeledAction(kind)
7524
+ )
7525
+ ]
7526
+ );
7527
+ }
7528
+ const compiledAfter = createScopeCompilerService(runtime).compileScopeById(
7529
+ resolvedScope.target.handle.scopeId
7530
+ );
7531
+ const summarizedTarget = compiledAfter ? summarizeTarget({
7532
+ kind: "scope",
7533
+ scope: compiledAfter.scope,
7534
+ handle: compiledAfter.scope.handle
7535
+ }) : summarizeTarget(resolvedScope.target);
7536
+ return {
7537
+ status: "applied",
7538
+ applied: true,
7539
+ changed: true,
7540
+ target: summarizedTarget,
7541
+ commandReference: {
7542
+ command: command.type,
7543
+ actorId: reference?.actorId ?? commandActorId(commandInput.origin),
7544
+ origin: reference?.origin ?? commandOriginKind(commandInput.origin),
7545
+ emittedAtUtc: commandInput.origin?.timestamp ?? currentAuditTimestamp(runtime)
7546
+ }
7547
+ };
7548
+ }
7549
+ function applyListOperation(runtime, input) {
7550
+ const resolvedScope = resolveScopeExactTarget(runtime, input.target, "listOperation");
7551
+ if (!resolvedScope.ok) return blockedApplyFromResolution(resolvedScope);
7552
+ const scope = resolvedScope.target.scope;
7553
+ if (scope.kind !== "list-item" && scope.kind !== "paragraph" && scope.kind !== "heading") {
7554
+ return blockedApply(
7555
+ `actions:list-operation:target-kind-unsupported:${scope.kind}`,
7556
+ "unsupported",
7557
+ "List operations require a paragraph-like scope handle.",
7558
+ "Use ai.actions.locateAll or ai.listScopes to select a paragraph, heading, or list-item handle."
7559
+ );
7560
+ }
7561
+ const paragraphIndex = paragraphIndexFromHandle(scope.handle);
7562
+ if (paragraphIndex === null) {
7563
+ return blockedApply(
7564
+ `actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
7565
+ "blocked",
7566
+ "The list operation target did not resolve to a current paragraph index.",
7567
+ "Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
7568
+ [
7569
+ blockerWithOwner(
7570
+ `actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
7571
+ "blocked",
7572
+ "The list operation target did not resolve to a current paragraph index.",
7573
+ "Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
7574
+ "L08 semantic scope compiler"
7575
+ )
7576
+ ]
7577
+ );
7578
+ }
7579
+ const command = listCommandForOperation(input.operation, paragraphIndex, actionOrigin(runtime, input));
7580
+ const before = runtime.getCanonicalDocument();
7581
+ runtime.dispatch(command);
7582
+ const changed = runtime.getCanonicalDocument() !== before;
7583
+ if (!changed) {
7584
+ return blockedApply(
7585
+ `actions:list-operation:runtime-noop:${scope.handle.scopeId}`,
7586
+ "blocked",
7587
+ "The list runtime command produced no document change for the selected scope.",
7588
+ "Select a list-compatible paragraph and retry, or route to L03/L07 list command support with the scope handle.",
7589
+ [
7590
+ blockerWithOwner(
7591
+ `actions:list-operation:runtime-noop:${scope.handle.scopeId}`,
7592
+ "blocked",
7593
+ "The list runtime command produced no document change for the selected scope.",
7594
+ "Select a list-compatible paragraph and retry, or route to L03/L07 list command support with the scope handle.",
7595
+ "L03 numbering/list semantics and L07 runtime list commands"
7596
+ )
7597
+ ]
7598
+ );
7599
+ }
7600
+ const compiledAfter = createScopeCompilerService(runtime).compileScopeById(scope.handle.scopeId);
7601
+ return {
7602
+ status: "applied",
7603
+ applied: true,
7604
+ changed: true,
7605
+ target: compiledAfter ? summarizeTarget({ kind: "scope", scope: compiledAfter.scope, handle: compiledAfter.scope.handle }) : summarizeTarget(resolvedScope.target),
7606
+ commandReference: {
7607
+ command: command.type,
7608
+ actorId: input.actorId ?? "v3-ai-api",
7609
+ origin: input.origin ?? "agent",
7610
+ emittedAtUtc: command.origin?.timestamp ?? currentAuditTimestamp(runtime)
7611
+ }
7612
+ };
7613
+ }
7614
+ function projectTableApplyResult(result) {
7615
+ return {
7616
+ status: result.applied ? "applied" : "blocked",
7617
+ applied: result.applied,
7618
+ changed: result.changed,
7619
+ proposalId: result.proposalId,
7620
+ posture: result.posture,
7621
+ ...result.blockers ? { blockers: result.blockers } : {},
7622
+ ...result.blockerDetails ? {
7623
+ blockerDetails: Object.freeze(
7624
+ result.blockerDetails.map(
7625
+ (detail) => blocker(detail.code, "blocked", detail.message, detail.nextStep)
7626
+ )
7627
+ )
7628
+ } : {},
7629
+ ...result.commandReference ? { commandReference: result.commandReference } : {},
7630
+ ...result.afterReadback ? { afterReadback: result.afterReadback } : {}
7631
+ };
7632
+ }
7633
+ function resolveScopeExactTarget(runtime, targetInput, feature) {
7634
+ if ("actionHandle" in targetInput) {
7635
+ return blockedScopeResolution(
7636
+ `actions:${actionSlug(feature)}:scope-handle-required`,
7637
+ "unsupported",
7638
+ `${feature} requires a semantic scope handle, not a table actionHandle.`,
7639
+ "Use scope handles for generated-content, link/bookmark, and list actions; use table actionHandles only for table actions."
7640
+ );
7641
+ }
7642
+ const resolved = resolveTarget(runtime, { handle: targetInput.handle });
7643
+ if (!resolved.ok) return resolved;
7644
+ if (resolved.target.kind !== "scope") {
7645
+ return blockedScopeResolution(
7646
+ `actions:${actionSlug(feature)}:scope-target-required`,
7647
+ "unsupported",
7648
+ `${feature} requires a semantic scope handle.`,
7649
+ "Refresh the target through ai.actions.locateAll or ai.listScopes and retry with a ScopeHandle."
7650
+ );
7651
+ }
7652
+ return { ok: true, target: resolved.target };
7653
+ }
7654
+ function resolveModeledEditableTarget(runtime, target, kind) {
7655
+ const bundle = createScopeCompilerService(runtime).compileBundleById(
7656
+ target.handle.scopeId,
7657
+ currentAuditTimestamp(runtime)
7658
+ );
7659
+ const entries = bundle?.evidence.editableTargets?.entries ?? [];
7660
+ const candidates = entries.filter((entry) => entry.runtimeCommand.status === "supported").filter((entry) => modeledEntryMatches(kind, entry)).sort((left, right) => modeledEntryRank(kind, left) - modeledEntryRank(kind, right));
7661
+ if (candidates.length === 0) {
7662
+ return blockedEditableResolution(
7663
+ `actions:${actionSlug(kind)}:unsupported:${target.handle.scopeId}`,
7664
+ "unsupported",
7665
+ "The target scope does not expose a supported modeled command target for this action.",
7666
+ `Route to ${ownerRouteForModeledAction(kind)} with the scope handle and editable-target evidence; do not fall back to raw refs or broad replacement.`,
7667
+ [
7668
+ blockerWithOwner(
7669
+ `actions:${actionSlug(kind)}:unsupported:${target.handle.scopeId}`,
7670
+ "unsupported",
7671
+ "The target scope does not expose a supported modeled command target for this action.",
7672
+ `Route to ${ownerRouteForModeledAction(kind)} with the scope handle and editable-target evidence; do not fall back to raw refs or broad replacement.`,
7673
+ ownerRouteForModeledAction(kind)
7674
+ )
7675
+ ]
7676
+ );
7677
+ }
7678
+ const [candidate] = candidates;
7679
+ if (!candidate) {
7680
+ return blockedEditableResolution(
7681
+ `actions:${actionSlug(kind)}:unsupported:${target.handle.scopeId}`,
7682
+ "unsupported",
7683
+ "No supported target candidate was available.",
7684
+ `Route to ${ownerRouteForModeledAction(kind)}.`
7685
+ );
7686
+ }
7687
+ const currentTarget = collectEditableTargetRefs(runtime.getCanonicalDocument()).find((ref) => ref.targetKey === candidate.targetKey);
7688
+ if (!currentTarget) {
7689
+ return blockedEditableResolution(
7690
+ `actions:${actionSlug(kind)}:target-not-current:${target.handle.scopeId}`,
7691
+ "unresolved-target",
7692
+ "The supported editable target evidence is stale in the current document.",
7693
+ "Refresh the scope handle and action evidence before retrying."
7694
+ );
7695
+ }
7696
+ return { ok: true, targetRef: currentTarget };
7697
+ }
7698
+ function modeledEntryMatches(kind, entry) {
7699
+ switch (kind) {
7700
+ case "fieldRefresh":
7701
+ return entry.commandFamily === "field" && entry.runtimeCommand.intents.includes("field-update") && entry.kind !== "toc-region-refresh";
7702
+ case "tocRefresh":
7703
+ return entry.commandFamily === "field" && entry.runtimeCommand.intents.includes("toc-refresh");
7704
+ case "bookmarkEdit":
7705
+ return entry.commandFamily === "link-bookmark" && entry.kind === "bookmark-content-range" && entry.runtimeCommand.intents.includes("bookmark-update");
7706
+ case "hyperlinkDestinationEdit":
7707
+ return entry.commandFamily === "link-bookmark" && entry.kind === "hyperlink-destination" && entry.runtimeCommand.intents.includes("hyperlink-update");
7708
+ }
7709
+ }
7710
+ function modeledEntryRank(kind, entry) {
7711
+ if (kind === "fieldRefresh" && entry.kind === "field-region-refresh") return 0;
7712
+ if (kind === "tocRefresh" && entry.kind === "toc-region-refresh") return 0;
7713
+ return 1;
7714
+ }
7715
+ function tablePlanOperationFamilyBlocker(stepKind, operationKind) {
7716
+ if (stepKind === "tableFragment") {
7717
+ if (operationKind !== "paste-cell-content" && operationKind !== "drop-cell-content") {
7718
+ return blocker(
7719
+ "actions:plan:table-fragment-operation-required",
7720
+ "unsupported",
7721
+ "tableFragment plan steps require a paste-cell-content or drop-cell-content operation.",
7722
+ "Use tableAction for general table operations, or supply a fragment paste/drop operation."
7723
+ );
7724
+ }
7725
+ }
7726
+ if (stepKind === "tableSelection" && operationKind !== "merge-cells" && operationKind !== "split-cell") {
7727
+ return blocker(
7728
+ "actions:plan:table-selection-operation-required",
7729
+ "unsupported",
7730
+ "tableSelection plan steps require a merge-cells or split-cell operation.",
7731
+ "Use tableAction for other table operations, or supply a merge/split selection operation."
7732
+ );
7733
+ }
7734
+ return null;
7735
+ }
7736
+ function listCommandForOperation(operation, paragraphIndex, origin) {
7737
+ switch (operation.kind) {
7738
+ case "toggle":
7739
+ return {
7740
+ type: "list.toggle",
7741
+ kind: operation.listKind,
7742
+ paragraphIndexes: [paragraphIndex],
7743
+ origin
7744
+ };
7745
+ case "indent":
7746
+ return { type: "list.indent", paragraphIndexes: [paragraphIndex], origin };
7747
+ case "outdent":
7748
+ return { type: "list.outdent", paragraphIndexes: [paragraphIndex], origin };
7749
+ case "restart-numbering":
7750
+ return {
7751
+ type: "list.restart-numbering",
7752
+ paragraphIndex,
7753
+ ...operation.startAt !== void 0 ? { startAt: operation.startAt } : {},
7754
+ origin
7755
+ };
7756
+ case "continue-numbering":
7757
+ return { type: "list.continue-numbering", paragraphIndex, origin };
7758
+ }
7759
+ }
7760
+ function paragraphIndexFromHandle(handle) {
7761
+ const raw = handle.semanticPath[handle.semanticPath.length - 1];
7762
+ if (raw === void 0) return null;
7763
+ const index = Number(raw);
7764
+ return Number.isSafeInteger(index) && index >= 0 ? index : null;
7765
+ }
7766
+ function actionOrigin(runtime, input) {
7767
+ return { source: "api", timestamp: currentAuditTimestamp(runtime) };
7768
+ }
7769
+ function commandActorId(origin) {
7770
+ return origin?.source === "api" ? "v3-ai-api" : "v3-ai-api";
7771
+ }
7772
+ function commandOriginKind(_origin) {
7773
+ return "agent";
7774
+ }
7775
+ function actionSlug(feature) {
7776
+ switch (feature) {
7777
+ case "fieldRefresh":
7778
+ return "field-refresh";
7779
+ case "tocRefresh":
7780
+ return "toc-refresh";
7781
+ case "bookmarkEdit":
7782
+ return "bookmark-edit";
7783
+ case "hyperlinkDestinationEdit":
7784
+ return "hyperlink-destination-edit";
7785
+ case "listOperation":
7786
+ return "list-operation";
7787
+ }
7788
+ }
7789
+ function ownerRouteForModeledAction(kind) {
7790
+ switch (kind) {
7791
+ case "fieldRefresh":
7792
+ case "tocRefresh":
7793
+ return "L02 field identity, L03 generated-content semantics, L07 field refresh command";
7794
+ case "bookmarkEdit":
7795
+ case "hyperlinkDestinationEdit":
7796
+ return "L02 link/bookmark identity and L07 modeled link/bookmark commands";
7797
+ }
7798
+ }
7799
+ function isTableFamilyScope(kind) {
7800
+ return kind === "table" || kind === "table-row" || kind === "table-cell";
7801
+ }
7802
+ function canRewriteScopeText(scope) {
7803
+ return !isTableFamilyScope(scope.kind) && (scope.replaceability.level === "full" || scope.replaceability.level === "text-only");
7804
+ }
7805
+ function canMarkScope(scope) {
7806
+ return !isTableFamilyScope(scope.kind) && scope.replaceability.level !== "blocked" && scope.replaceability.level !== "preserve-only";
7807
+ }
7808
+ function textMatches(text, query, matchCase) {
7809
+ if (matchCase) return text.includes(query);
7810
+ return text.toLocaleLowerCase().includes(query.toLocaleLowerCase());
7811
+ }
7812
+ function excerpt(text) {
7813
+ const normalized = text.replace(/\s+/g, " ").trim();
7814
+ return normalized.length <= 120 ? normalized : `${normalized.slice(0, 117)}...`;
7815
+ }
7816
+ function blocker(code, category, message, nextStep) {
7817
+ return { code, category, message, nextStep };
7818
+ }
7819
+ function blockerWithOwner(code, category, message, nextStep, ownerRoute) {
7820
+ return { code, category, message, nextStep, ownerRoute };
7821
+ }
7822
+ function blockedResolution(code, category, message, nextStep, existingDetails) {
7823
+ const detail = existingDetails?.[0] ?? blocker(code, category, message, nextStep);
7824
+ return {
7825
+ ok: false,
7826
+ blockers: Object.freeze([detail.code]),
7827
+ blockerDetails: Object.freeze([detail])
7828
+ };
7829
+ }
7830
+ function blockedScopeResolution(code, category, message, nextStep, existingDetails) {
7831
+ const detail = existingDetails?.[0] ?? blocker(code, category, message, nextStep);
7832
+ return {
7833
+ ok: false,
7834
+ blockers: Object.freeze([detail.code]),
7835
+ blockerDetails: Object.freeze([detail])
7836
+ };
7837
+ }
7838
+ function blockedEditableResolution(code, category, message, nextStep, existingDetails) {
7839
+ const detail = existingDetails?.[0] ?? blocker(code, category, message, nextStep);
7840
+ return {
7841
+ ok: false,
7842
+ blockers: Object.freeze([detail.code]),
7843
+ blockerDetails: Object.freeze([detail])
7844
+ };
7845
+ }
7846
+ function blockedApply(code, category, message, nextStep, existingDetails) {
7847
+ const detail = existingDetails?.[0] ?? blocker(code, category, message, nextStep);
7848
+ return {
7849
+ status: category === "unsupported" ? "unsupported" : "blocked",
7850
+ applied: false,
7851
+ changed: false,
7852
+ blockers: Object.freeze([detail.code]),
7853
+ blockerDetails: Object.freeze([detail])
7854
+ };
7855
+ }
7856
+ function blockedApplyFromResolution(resolution) {
7857
+ const detail = resolution.blockerDetails[0];
7858
+ return {
7859
+ status: detail?.category === "unsupported" ? "unsupported" : "blocked",
7860
+ applied: false,
7861
+ changed: false,
7862
+ blockers: resolution.blockers,
7863
+ blockerDetails: resolution.blockerDetails
7864
+ };
7865
+ }
7866
+ function blockedPlan(mode, code, category, message, nextStep) {
7867
+ const detail = blocker(code, category, message, nextStep);
7868
+ return {
7869
+ status: "blocked",
7870
+ mode,
7871
+ applied: false,
7872
+ changed: false,
7873
+ steps: Object.freeze([]),
7874
+ supportedMethods: ACTION_METHODS,
7875
+ blockers: Object.freeze([detail.code]),
7876
+ blockerDetails: Object.freeze([detail])
7877
+ };
7878
+ }
7879
+ function blockedPlanStep(id, kind, code, category, message, nextStep) {
7880
+ return blockedPlanStepFromDetails(id, kind, [
7881
+ blocker(code, category, message, nextStep)
7882
+ ]);
7883
+ }
7884
+ function blockedPlanStepFromDetails(id, kind, details, context) {
7885
+ const first = details[0] ?? blocker(
7886
+ `actions:plan:step-blocked:${id}`,
7887
+ "blocked",
7888
+ "The plan step was blocked.",
7889
+ "Inspect blockerDetails and regenerate the plan if needed."
7890
+ );
7891
+ return {
7892
+ id,
7893
+ kind,
7894
+ status: first.category === "unsupported" ? "unsupported" : "blocked",
7895
+ applied: false,
7896
+ changed: false,
7897
+ ...context?.target ? { target: context.target } : {},
7898
+ ...context?.tableAction ? { tableAction: context.tableAction } : {},
7899
+ ...context?.beforeReadback ? { beforeReadback: context.beforeReadback } : {},
7900
+ blockers: Object.freeze(details.map((detail) => detail.code)),
7901
+ blockerDetails: Object.freeze([...details])
7902
+ };
7903
+ }
7904
+ function summarizePlanResult(mode, steps) {
7905
+ const blocked = steps.filter(
7906
+ (step) => step.status === "blocked" || step.status === "unsupported"
7907
+ );
7908
+ const progressed = steps.filter(
7909
+ (step) => step.status === "planned" || step.status === "read" || step.status === "applied"
7910
+ );
7911
+ const blockers = blocked.flatMap((step) => step.blockers ?? []);
7912
+ const blockerDetails = blocked.flatMap((step) => step.blockerDetails ?? []);
7913
+ const applied = steps.some((step) => step.applied);
7914
+ const changed = steps.some((step) => step.changed);
7915
+ return {
7916
+ status: blocked.length === 0 ? mode === "preview" ? "planned" : "applied" : progressed.length > 0 ? "partial" : "blocked",
7917
+ mode,
7918
+ applied,
7919
+ changed,
7920
+ steps: Object.freeze([...steps]),
7921
+ supportedMethods: ACTION_METHODS,
7922
+ ...blockers.length > 0 ? { blockers: Object.freeze(blockers) } : {},
7923
+ ...blockerDetails.length > 0 ? { blockerDetails: Object.freeze(blockerDetails) } : {}
7924
+ };
7925
+ }
7926
+
6135
7927
  // src/api/v3/_create.ts
6136
7928
  function createApiV3(handle, opts) {
6137
7929
  const ai = {
@@ -6147,7 +7939,8 @@ function createApiV3(handle, opts) {
6147
7939
  ...createEvaluateFamily(handle),
6148
7940
  ...createStatsFamily(handle),
6149
7941
  ...createOutlineFamily(handle),
6150
- ...createTableActionFamily(handle)
7942
+ ...createTableActionFamily(handle),
7943
+ ...createActionsFamily(handle)
6151
7944
  };
6152
7945
  const runtime = {
6153
7946
  document: createDocumentFamily(handle),