@ansonlai/docx-redline-js 0.8.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +221 -12
- package/CHANGELOG.md +803 -586
- package/README.md +1018 -901
- package/core/current-properties.js +136 -0
- package/core/paragraph-text.js +211 -209
- package/core/types.js +9 -3
- package/core/word-revision-semantics.js +256 -0
- package/dist/docx-redline-js.esm.js +4551 -967
- package/dist/docx-redline-js.esm.js.map +4 -4
- package/dist/docx-redline-js.esm.min.js +112 -106
- package/dist/docx-redline-js.esm.min.js.map +4 -4
- package/dist/docx-redline.bundle.cjs +4690 -1106
- package/dist/docx-redline.bundle.cjs.map +4 -4
- package/dist/docx-redline.bundle.js +4690 -1106
- package/dist/docx-redline.bundle.js.map +4 -4
- package/docs/AGENT_FAST_START.md +2 -0
- package/docs/AGENT_KNOWLEDGE_BASE.md +23 -4
- package/docs/SKILL_AUTHORING.md +165 -150
- package/docs/TESTING.md +1142 -1059
- package/docs/schemas/document-operations.schema.json +221 -168
- package/document/docx-document.js +1022 -1010
- package/engine/oxml-engine.js +567 -560
- package/engine/paragraph-mark-revisions.js +292 -0
- package/engine/reconstruction-mapper.js +312 -311
- package/engine/reconstruction-mode.js +183 -183
- package/engine/reconstruction-writer.js +67 -10
- package/engine/rpr-helpers.js +42 -0
- package/engine/run-builders.js +0 -73
- package/engine/surgical-diff-application.js +604 -605
- package/engine/table-mode.js +63 -63
- package/index.d.ts +51 -1
- package/index.js +47 -3
- package/node/cli-help.js +1 -0
- package/node/cli.js +992 -974
- package/orchestration/list-structural-fallback.js +67 -9
- package/package.json +1 -1
- package/pipeline/diff-engine.js +130 -1
- package/pipeline/ingestion-export.js +227 -243
- package/pipeline/ingestion-paragraph.js +93 -98
- package/pipeline/list-generation.js +415 -419
- package/pipeline/pipeline.js +206 -204
- package/pipeline/serialization.js +111 -94
- package/scripts/export-validation-fixtures.mjs +19 -6
- package/scripts/generate-word-revision-semantics-matrix.mjs +134 -0
- package/scripts/lib/word-coverage-catalogue.mjs +7 -2
- package/scripts/render-multilevel-bullet-visual.ps1 +2 -2
- package/scripts/word-com-corpus-visual-suite.ps1 +2 -2
- package/scripts/word-com-differential.ps1 +443 -335
- package/scripts/word-com-visual-suite.ps1 +2 -2
- package/scripts/word-revision-semantics-matrix.ps1 +108 -0
- package/services/batch-operation-orchestrator.js +26 -0
- package/services/capture-engine.js +228 -226
- package/services/document-operation-applier.js +215 -0
- package/services/document-operation-contract.js +628 -537
- package/services/document-operation-mutations.js +789 -46
- package/services/error-recovery.js +17 -0
- package/services/list-conversion-diagnostics.js +296 -0
- package/services/list-format-change.js +586 -0
- package/services/list-format-mutation.js +416 -0
- package/services/list-format.js +305 -0
- package/services/numbering-allocation.js +642 -0
- package/services/numbering-helpers.js +354 -524
- package/services/operation-batch-compiler.js +612 -436
- package/services/operation-heuristics.js +197 -173
- package/services/operation-preflight.js +1 -0
- package/services/receipt-collector.js +288 -288
- package/services/revision-comment-management.js +634 -594
- package/services/revision-fidelity-verifier.js +498 -0
- package/services/standalone-docx-plumbing.js +627 -631
- package/services/standalone-operation-runner.d.ts +606 -427
- package/services/table-reconciliation.js +103 -49
package/docs/AGENT_FAST_START.md
CHANGED
|
@@ -41,6 +41,8 @@ If the user requests deleted text, search with `--view rejected` first. Otherwis
|
|
|
41
41
|
|
|
42
42
|
- Require `completion: true` and a non-null `outputPath`; the CLI computes this from write, operation, commit, and localized-verification results.
|
|
43
43
|
- On failure, follow `error.recovery.action`; never retry unchanged arguments.
|
|
44
|
+
- `REVISION_FIDELITY_VIOLATION` (Reject All would not restore the source): retry
|
|
45
|
+
narrower per `recovery.retryGuidance`; never pass `--revision-fidelity off`.
|
|
44
46
|
- Use `retryPlan.base: "original"` to replay a batch and `"output"` to retain
|
|
45
47
|
committed progressive work and retry the reported indexes.
|
|
46
48
|
- The source is not overwritten unless `--in-place` is explicit.
|
|
@@ -462,10 +462,14 @@ fragment to insert. Do not choose a type from its English name alone.
|
|
|
462
462
|
|---|---|
|
|
463
463
|
| Change text within one paragraph or replace its content | `{ type: 'redline', target, modified }` (`replace` is a compatibility alias) |
|
|
464
464
|
| Delete a whole paragraph | `{ type: 'delete', target }` (normalized to a redline with `modified: ''`) |
|
|
465
|
-
| Change native list structure | `{ type: 'list-change', target, modified: '<complete Markdown list>' }` |
|
|
465
|
+
| Change native list structure | `{ type: 'list-change', target, modified: '<complete Markdown list>' }`. It inserts, removes and nests items; it never changes the list's kind or numbering format (retyped markers warn `LIST_FORMAT_NOT_APPLIED` or fail `UNSUPPORTED_LIST_FORMAT_CHANGE` with a `change-format` `suggestedOperation`): use `list-format` `change-format` for that |
|
|
466
466
|
| Reconcile a Word table | `{ type: 'table-reconciliation', target, modified: '<complete Markdown table>' }` |
|
|
467
467
|
| Comment or highlight existing text | `comment` with `commentContent`, or `highlight` with `textToHighlight` |
|
|
468
|
-
| Change character or paragraph formatting | `character-format`/`format` with `textToFormat` and `properties`, or `paragraph-format` with `properties` |
|
|
468
|
+
| Change character or paragraph formatting | `character-format`/`format` with `textToFormat` and `properties`, or `paragraph-format` with `properties`; add `textOccurrence` (1-based, default 1) to format a later match of `textToFormat` inside the targeted paragraph (out of range fails `PATCH_SOURCE_NOT_FOUND`; independent of `target.occurrence`, which selects the paragraph; legacy calls without `textOccurrence` still read `target.occurrence`; capability `format-text-occurrence-v1`) |
|
|
469
|
+
| Put an existing paragraph in (or out of) an existing list | `{ type: 'paragraph-format', target, properties: { numbering: { numId, level } } }` with `numId`/`level` copied from an inspected list paragraph of the target list, or `numbering: null` to remove the paragraph's direct list binding. A tracked `w:pPrChange` keeps the previous `w:numPr`, so Reject All restores the old membership and Accept All shows the new one. It binds only: it never creates a list, marker text is not stripped, and sibling numbering is not remapped. Refused `PARAGRAPH_NUMBERING_NOT_FOUND` (unknown `numId` or undefined level) and `PARAGRAPH_FORMAT_REVISION_CONFLICT` (another author's pending paragraph-property change). Capability `paragraph-numbering-binding-v1` |
|
|
470
|
+
| Turn typed headers (`A.`, `1.`, `I.`, `-` typed as text) into a real list | `{ type: 'list-format', target, targetEnd?, list: { mode: 'convert-plain' } }` on the plain paragraphs that carry the typed markers. Each marker becomes a tracked deletion and the paragraph a list item with that label (tracked `w:pPrChange`, same paragraph); the marker's value is the start value, consecutive markers share one list, body paragraphs in the range stay plain, and a later `list-format` in the batch continuing the count joins the list. A lone `I.`/`C.` is ambiguous (letter or roman): pass `list.format`. `strip: 'none'` with `format` (and `start`) binds a paragraph without a typed marker and leaves its text alone. Do not express this as a `redline` that adds or keeps marker text. A changed-text header (`1. Old` -> `1. New`) is a `redline` of that paragraph that keeps the typed marker exactly (`modified: '1. New'`, or `replacements`) plus this `list-format` in the same batch, in either order: the redline runs first and the list-format strips the unchanged marker, so Accept All shows label `1.` with `New` and Reject All the literal `1. Old`. A redline in that batch that adds, removes or changes the marker, spans paragraphs, or leaves the text unchanged is refused before mutation (`LIST_FORMAT_COMPOSITION_CONFLICT`). Capability `list-format-v1` (contract version 9) |
|
|
471
|
+
| Change a list's kind or numbering style (bullets to `1.`, `1.` to `A.`, a nested level to `(i)`) | `{ type: 'list-format', target, list: { mode: 'change-format', format, lvlText?, level?, start?, scope? } }` with `target` on any item of the list. A NEW definition is allocated (levels, indentation and fonts copied from the list; only `level`, default the target's level, is redefined) and every item is rebound with a tracked `w:pPrChange`: Reject All restores the old labels, Accept All shows the new ones; the shared definition is never edited. `scope: 'list'` (default) rebinds every item, including items outside `target` (`result.list.items[].inTarget`); `scope: 'range'` with `targetEnd` changes only a range that runs to the end of the list and continues its count. `lvlText` keeps the source text unless given (`"%1."`, `"(%2)"`; a bullet's character). Do not express this as a `list-change`/`redline` that retypes the markers. `scope: 'list'` counts as a write to every item it rebinds: in the same batch each of those items may also carry one single-paragraph `redline` that adds no marker (it runs first); any other write to them fails `LIST_FORMAT_COMPOSITION_CONFLICT` (`reason: list-rebind`), so send it in a separate apply or use `scope: 'range'`. Capability `list-format-change-v1` (contract version 9) |
|
|
472
|
+
| Make a list continue an earlier list (`1. 2.` / `1. 2.` -> `1. 2.` / `3. 4.`) | `{ type: 'list-format', target, list: { mode: 'join', continueFrom: { paragraphId, fingerprint } } }` with `target` on the later list and `continueFrom` on an item of the earlier one; items keep their levels and take the earlier list's format. `scope: 'range'` joins only a range that runs to the end of the later list. Capability `list-format-change-v1` |
|
|
469
473
|
| Counterpropose a paragraph wholly deleted by another author | `restore` with a rejected-view target |
|
|
470
474
|
| Insert text inside another author's rejected-view content | `insert` with `target.revisionView: 'rejected'`, an exact `anchor`, and `existingRevisions: 'slice-cross-author'` |
|
|
471
475
|
|
|
@@ -514,6 +518,9 @@ revision markup internally before writing, so do not add `preflight` or baseline
|
|
|
514
518
|
2. Build one final operation per target. Use complete `modified` text for broad
|
|
515
519
|
semantic revisions or localized `replacements` for small literal changes in
|
|
516
520
|
an inspected target. Consolidate multiple changes to one paragraph.
|
|
521
|
+
Paragraphs with soft line breaks (`\n` in `exactText`) accept `replacements`
|
|
522
|
+
whose `find` lies within one line; the breaks are preserved. A `find` or
|
|
523
|
+
`replace` that spans or adds a line break is refused.
|
|
517
524
|
3. Run `apply` once per stable batch. Independent strong targets bind against
|
|
518
525
|
the batch-start document and do not need bottom-up sorting. Use captures only
|
|
519
526
|
for intentional created-content dependencies.
|
|
@@ -767,13 +774,25 @@ When the CLI or runner returns an error code, follow these specific recovery act
|
|
|
767
774
|
| `ANCHOR_NOT_FOUND` / `AMBIGUOUS_ANCHOR` | A comment or rejected-view insertion anchor was not uniquely matched. | For comments, narrow `textToComment` or omit it to anchor the whole paragraph. For rejected-view insertion, copy exact rejected text and provide `anchor.occurrence`. |
|
|
768
775
|
| `OVERLAPPING_SOURCE_TARGETS` / `OVERLAPPING_TEXT_EDITS` | Multiple complete text operations target the same batch-start source. | Consolidate all changes to that paragraph into one `redline` or `replace`; the library cannot choose between incompatible complete desired states. |
|
|
769
776
|
| `REVISION_ORDER_CONFLICT` | A text rewrite and formatting/highlight operation overlap the same source. | Consolidate or split the work at an intentional created-content dependency; do not try a different arbitrary order. |
|
|
777
|
+
| `LIST_FORMAT_COMPOSITION_CONFLICT` | A `redline` and a `list-format` on the same paragraph do not compose (`reason`: `marker-changed` with `sourceMarker`/`modifiedMarker`, `redline-unchanged`, `redline-shape` for a multi-paragraph, ranged, anchored, `list-change` or structured redline), or a `list-format` with `scope: 'list'` rebinds an item another operation also writes (`list-rebind`, `rebindingOperationIndexes`). Detected at batch compilation; nothing was written. | `recovery.action: split_text_and_list_changes` (`recovery.hint`). Keep the typed marker byte-identical in the redline (the list-format strips it) and express a new label through `list-format`; drop a no-op redline; send other writes to rebound items in a separate apply or use `scope: 'range'`. |
|
|
770
778
|
| `CAPTURE_FANOUT_CONFLICT` | Multiple mutating consumers share one capture without distinct selectors. | Give consumers distinct selectors, chain them explicitly, or split the batch. |
|
|
771
779
|
| `EXISTING_REVISIONS` | Target paragraph contains tracked changes from another author. | Fails closed to protect third-party review marks. If editing inside that reviewer's pending insertion is intended, pass `--existing-revisions slice-cross-author` (or `existingRevisions: 'slice-cross-author'`). Do not pass `accept-all-first` without explicit user authorization. |
|
|
772
|
-
| `PATCH_ROUNDTRIP_MISMATCH` | A cross-author surgical edit did not reconstruct the requested modified text exactly. | Treat the operation as unapplied. Re-extract the exact paragraph text and split the edit into a narrower operation that does not cross the reported structural boundary. |
|
|
780
|
+
| `PATCH_ROUNDTRIP_MISMATCH` | A cross-author surgical edit did not reconstruct the requested modified text exactly, or a localized `replacements` operation would not leave the compiled accepted text in the target (checked before commit on every path). | Treat the operation as unapplied. Re-extract the exact paragraph text and split the edit into a narrower operation that does not cross the reported structural boundary. |
|
|
773
781
|
| `FOREIGN_PARAGRAPH_MARK_DELETION` | A normal edit attempted to write into a paragraph wholly deleted by another reviewer. | Use an explicit `restore` operation if the user intends to counterpropose that paragraph; otherwise leave the deletion unresolved. |
|
|
774
782
|
| `RESTORATION_STATE_REQUIRED` / `RESTORATION_COUNT_MISMATCH` | A `restore` target is not a wholly foreign-deleted paragraph, or its replacement count does not match the paragraph range. | Re-inspect the document and target the deleted paragraph by stable descriptor; provide exactly one replacement string per source paragraph. |
|
|
775
783
|
| `REJECTED_INSERTION_STATE_REQUIRED` / `UNSAFE_REVISION_BOUNDARY` | An explicit rejected-view insertion did not resolve to supported plain run text inside a wholly foreign-deleted paragraph. | Do not fall back to a generic edit. Narrow the exact anchor/offset, or handle comments, bookmarks, fields, hyperlinks, moves, or other structural boundaries manually. |
|
|
776
784
|
| `GENERATED_OOXML_INVALID` | The operation introduced a new validation error relative to its baseline. | Treat the operation as unapplied and inspect `generatedIssues`; correct the generating operation or builder rather than repairing or accepting the source document's unrelated baseline defects. |
|
|
785
|
+
| `LIST_MARKER_NOT_BOUND` (warning) / `UNSUPPORTED_LIST_CONVERSION` (error) | A plain paragraph was edited so its text starts with a list marker (`A.`, `1.`, `iv.`, `-`) its original did not carry, but text edits cannot bind a list: the marker would be literal text and `list` stays `null`. Warning (`diagnostics[]` plus a `CODE: message` string in `warnings`) by default; error under the strict policy (`--profile agent`, `--atomic`, or `strictListConversion: true`). Separate from `REVISION_FIDELITY_VIOLATION`. Fields: `listMarker`, `requestedList`, `targetExcerpt`. Capability `list-conversion-diagnostics-v1`. | The error applied nothing. To create the list item, send `recovery.suggestedOperation` (a `list-format` `convert-plain` request) with the same target and leave the text unmarked (`recovery.action: use_list_format_convert_plain`); if the original already carries another marker, retype it with a `redline` first and convert with `strip: "marker"` in a later batch. If literal marker text is intended, repeat with `listMarkers: "literal"`. Do not treat a warning-only `ok` as a list conversion. |
|
|
786
|
+
| `LIST_MARKER_NOT_FOUND` / `LIST_MARKER_AMBIGUOUS` / `LIST_MARKER_UNSUPPORTED` (error) | `list-format` `convert-plain` found no typed marker to strip; a lone roman-looking letter (`I.`, `C.`, `D.`) reads as letter or roman (`formats` lists both); or the marker shape is unsupported (`reason`: `mixed` markers in one range, `multilevel` `2.1.`, `parenthesized` `(a)`, `zero`, `format-mismatch`, `marker-shape` when the marker sits in a hyperlink, field, content control or pending revision). Nothing was written. | NOT_FOUND: target the marker paragraphs, or use `strip: "none"` with `list.format`. AMBIGUOUS: repeat with `list.format` from `recovery.allowedValues`. UNSUPPORTED: split mixed ranges into one operation per format; leave other shapes literal or convert them in the host. |
|
|
787
|
+
| `LIST_FORMAT_TARGET_IS_LIST` / `LIST_FORMAT_STYLE_NUMBERED` (error) | `convert-plain` converts plain paragraphs only: the paragraph already has a direct `w:numPr`, or its paragraph style (or a `basedOn` ancestor) carries numbering. Nothing was written. | Narrow the range to plain paragraphs. To move an existing list item, use `paragraph-format` `numbering`; style-linked numbering is changed in the host. |
|
|
788
|
+
| `LIST_FORMAT_NOT_APPLIED` (warning) / `UNSUPPORTED_LIST_FORMAT_CHANGE` (error) | A multi-line `list-change`/`redline` kept a list item's text with a marker whose kind or format (bullet vs numbered, decimal vs `A.`) differs from the numbering definition the item keeps (`sourceList.{numId,level,kind,format}`); the old definition is not replaced. Same warning/strict policy as above. Inserted items are not compared. | `recovery.action: use_list_format_change_format`: send `recovery.suggestedOperation` (a `list-format` `change-format` request, scope `list`) with a target on one item of the list; `list-change` never applies a format change itself. Text changes go in single-paragraph `redline`s without the new markers, which may share the batch with the `change-format` (they run first). Or pass `listMarkers: "literal"` to accept the result. |
|
|
789
|
+
| `LIST_FORMAT_TARGET_NOT_LIST` / `LIST_FORMAT_MIXED_RANGE` (error) | `change-format`/`join`: the target range has no list item; or it mixes list items with plain paragraphs (`reason: non-list`), spans several lists (`several-lists`, `numIds`), or, for `change-format` without `list.level`, several levels (`levels`, `recovery.allowedValues`). Tracked-deleted paragraphs are skipped, never rebound. Nothing was written. | NOT_LIST: use `convert-plain` (typed markers) or `paragraph-format` `numbering`. MIXED: target one item of the list (scope `list`), one operation per list, or pass `list.level`. |
|
|
790
|
+
| `LIST_FORMAT_STYLE_LINKED` / `LIST_FORMAT_DEFINITION_UNSUPPORTED` (error) | The list's definition is style-linked (`w:numStyleLink`/`w:styleLink`, a level `w:pStyle` such as heading numbering, or a paragraph style that binds it), so items can belong to it without a direct binding; or the definition cannot be rebound exactly (`reason`: `missing` numbering part or numId, `level-override` `w:lvlOverride/w:lvl`, `level-restart` `w:lvlRestart` in a `scope: 'range'` split, `undefined-level`). Nothing was written. | Change these lists in the host. `missing` on a bare call: pass the package's `word/numbering.xml` as `numberingXml`. `level-restart`: use `scope: 'list'`. |
|
|
791
|
+
| `LIST_FORMAT_SPLIT_UNSUPPORTED` / `LIST_FORMAT_JOIN_INVALID` (error) | The rebinding would renumber paragraphs it does not name, or Word could not show the same count: `mid-sequence` (a `scope: 'range'` that stops before the list's last item; `followingParagraphIndexes`, `lastListParagraphIndex`), `nested-restart` (a split that starts below a level that later restarts inside the range), `shared-definition` (another list shares the abstract definition, which Word counts as one list), `interleaved` (the continued list has items after the joined ones), `textbox-member`; join: `continueFrom` `after-target`, `not-a-list`, `deleted`, `inside-target`. Nothing was written. | Extend `targetEnd` to `lastListParagraphIndex` or use `scope: 'list'`; start a split at a top-level item; join a list to the list that immediately precedes it. A `LIST_FORMAT_UNCHANGED` warning (`ok`, no change) means the list already has the requested format or already continues `continueFrom`. |
|
|
792
|
+
| `INVALID_OPERATION` with `field: properties.<key>` (error) | `paragraph-format.properties` contained keys outside `alignment`, `keepNext`, `keepLines`, `pageBreakBefore`, `style`, `numbering` (0.9.0; it was the `UNKNOWN_PARAGRAPH_FORMAT_PROPERTY` warning in 0.8.x). Nothing was applied; `properties` lists the rejected keys. | Remove the unsupported key, or use the dedicated operation (`list-format`, `format`). |
|
|
793
|
+
| `PARAGRAPH_NUMBERING_NOT_FOUND` (error) | `paragraph-format` `numbering.numId` is not defined in the document's numbering part (and was not allocated earlier in the batch), its `abstractNum` is missing, or the level is not defined. `reason` is `unknown-num`, `missing-abstract` or `undefined-level`. Nothing was changed. | Inspect a paragraph already in the target list and copy its `list.numId` and `list.level`. A binding never creates a list. |
|
|
794
|
+
| `PARAGRAPH_FORMAT_REVISION_CONFLICT` (error) | The target paragraph already has a pending `w:pPrChange` by another author; overwriting its snapshot would change what Reject All restores. A pending change by the same author keeps its earliest snapshot. | Ask the user to accept or reject the existing formatting change first. |
|
|
795
|
+
| `REVISION_FIDELITY_VIOLATION` | Word's Reject All of the operation's output would not reproduce the source (`violations[].invariant`: `REJECT_PARAGRAPH_COUNT`, `REJECT_PARAGRAPH_TEXT`, `REJECT_PARAGRAPH_PROPS`, `REJECT_NUMBERING_REFS` (also: a bound or restored numId with no `w:num`/`w:abstractNum`/`w:lvl` in the output numbering), `REJECT_WRAPPERS`, `ACCEPT_INTENT` (a plain-text `redline`'s Accept All target does not equal its complete `modified` text); for `generateRedlines: false` operations also `ACCEPT_NEIGHBORS` and `UNTRACKED_CREATED_REVISION`, the latter meaning the untracked edit wrote a tracked revision the source lacked). Refused by default (`enforce`); `verifyRevisionFidelity: 'warn'` / `--revision-fidelity warn` applies and appends the same text to `warnings`. Each violation has `explanation`, `expected`/`actual` excerpts and `source.sourceParagraphIndex` (the 1-based extraction index) with a text excerpt. | Nothing was applied. Follow `recovery.retryGuidance` for the reported invariant: target one paragraph, use `replacements` for literal edits, keep breaks/tabs and hyperlink boundaries, and move formatting or list changes into their own operation. If a narrower retry is also refused, report the operation and `violations`; never disable verification to force it through. For `UNTRACKED_CREATED_REVISION`, retry once with the fresh extraction; if it repeats, report it (or make the edit tracked with `generateRedlines: true` if a reviewable change is acceptable). |
|
|
777
796
|
| `UNSAFE_DELETED_TABLE_ROW` / `UNSUPPORTED_MOVE_REVISION` / `SECTION_BREAK_PARAGRAPH` / `UNSAFE_PARAGRAPH_PLACEMENT` | Paragraph restoration cannot preserve the source structural boundary safely. | Do not retry as an ordinary redline. Resolve the row/move/section/placement condition manually or narrow the restoration to a safe paragraph. |
|
|
778
797
|
| `PART_NOT_FOUND` / `PART_AMBIGUOUS` | The `part` selector matched no header/footer, or several. | Re-run `inspect` and use `headersFooters[].path`, or add `section`. |
|
|
779
798
|
| `FIELD_EDIT_REFUSED` | The edit would change a field (PAGE, NUMPAGES, DATE) in a header or footer. | Edit the text around the field. |
|
|
@@ -960,7 +979,7 @@ directly into `word/document.xml`.
|
|
|
960
979
|
1. Call `configureXmlProvider` first in Node.js.
|
|
961
980
|
2. `applyRedlineToOxml` is async.
|
|
962
981
|
3. Paragraph APIs expect paragraph-level OOXML, not full `word/document.xml` in all cases.
|
|
963
|
-
4. List operations may return `numberingXml` that must be merged into package parts.
|
|
982
|
+
4. List operations may return `numberingXml` that must be merged into package parts. `ensureNumberingArtifactsInZip` always merges them into an existing `word/numbering.xml` (the former `mergeNumberingXml` option is ignored); a conflicting id fails with `NUMBERING_ID_CONFLICT`, so pass `numberingXml` or a shared `numberingIdState` to every call.
|
|
964
983
|
That replacement behavior is deprecated and will become an error in the next major version.
|
|
965
984
|
5. `useNativeApi: true` means standalone mode cannot fully handle that operation path.
|
|
966
985
|
6. `deleteCommentsByAuthorInOoxml` removes definitions and linked anchors only when they are present in the same OOXML payload. In a real `.docx`, `word/comments.xml` and `word/document.xml` are separate parts and must both be updated by the package integration layer.
|
package/docs/SKILL_AUTHORING.md
CHANGED
|
@@ -1,150 +1,165 @@
|
|
|
1
|
-
# Skill and Harness Authoring Contract
|
|
2
|
-
|
|
3
|
-
Use this page to create or update an agent skill, MCP server, or custom harness
|
|
4
|
-
for `@ansonlai/docx-redline-js`. Ordinary editing agents should load
|
|
5
|
-
[Agent Fast Start](AGENT_FAST_START.md), not this design contract.
|
|
6
|
-
|
|
7
|
-
> [!WARNING]
|
|
8
|
-
> Before 1.0, every integration must identify and pin its exact tested library
|
|
9
|
-
> release and required CLI contract/capabilities. Treat any different release or
|
|
10
|
-
> contract as unverified until compatibility tests pass.
|
|
11
|
-
|
|
12
|
-
Example declaration for a contract-8 shell integration that uses localized
|
|
13
|
-
operations over stdin:
|
|
14
|
-
|
|
15
|
-
```json
|
|
16
|
-
{
|
|
17
|
-
"testedLibraryRelease": "0.7.1",
|
|
18
|
-
"requiredCliContractVersion": 8,
|
|
19
|
-
"requiredCapabilities": [
|
|
20
|
-
"operations-stdin",
|
|
21
|
-
"agent-safety-profile-v2",
|
|
22
|
-
"recovery-envelope-v1",
|
|
23
|
-
"localized-replacements-v1"
|
|
24
|
-
]
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Runtime negotiation
|
|
29
|
-
|
|
30
|
-
Run `docx-redline version` before the first document operation. Require CLI
|
|
31
|
-
contract version
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
1
|
+
# Skill and Harness Authoring Contract
|
|
2
|
+
|
|
3
|
+
Use this page to create or update an agent skill, MCP server, or custom harness
|
|
4
|
+
for `@ansonlai/docx-redline-js`. Ordinary editing agents should load
|
|
5
|
+
[Agent Fast Start](AGENT_FAST_START.md), not this design contract.
|
|
6
|
+
|
|
7
|
+
> [!WARNING]
|
|
8
|
+
> Before 1.0, every integration must identify and pin its exact tested library
|
|
9
|
+
> release and required CLI contract/capabilities. Treat any different release or
|
|
10
|
+
> contract as unverified until compatibility tests pass.
|
|
11
|
+
|
|
12
|
+
Example declaration for a contract-8 shell integration that uses localized
|
|
13
|
+
operations over stdin:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"testedLibraryRelease": "0.7.1",
|
|
18
|
+
"requiredCliContractVersion": 8,
|
|
19
|
+
"requiredCapabilities": [
|
|
20
|
+
"operations-stdin",
|
|
21
|
+
"agent-safety-profile-v2",
|
|
22
|
+
"recovery-envelope-v1",
|
|
23
|
+
"localized-replacements-v1"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Runtime negotiation
|
|
29
|
+
|
|
30
|
+
Run `docx-redline version` before the first document operation. Require CLI
|
|
31
|
+
contract version 9 only when the integration sends `list-format` operations (see
|
|
32
|
+
List structure); otherwise contract version 8 suffices. Require
|
|
33
|
+
only the capabilities the integration actually uses. Check once per stable
|
|
34
|
+
runtime, fail closed when a requirement is absent, and never inspect bundles or
|
|
35
|
+
ZIP parts as a fallback.
|
|
36
|
+
|
|
37
|
+
### List structure (optional)
|
|
38
|
+
|
|
39
|
+
Keep list structure out of an ordinary editing prompt. A wrapper that converts typed headers, changes a list's
|
|
40
|
+
format, or joins lists:
|
|
41
|
+
|
|
42
|
+
- negotiates `list-format-v1` (`convert-plain`) and, only if used, `list-format-change-v1` (`change-format`,
|
|
43
|
+
`join`);
|
|
44
|
+
- without the capability keeps its host path, never a `redline` or `list-change` that retypes markers (that
|
|
45
|
+
warns, or fails under the agent profile, and binds nothing);
|
|
46
|
+
- follows `use_list_format_*` recovery by sending `recovery.suggestedOperation` with the inspected target;
|
|
47
|
+
- sends a changed-text header (`1. Old` -> `1. New`) as a marker-preserving `redline` plus a `list-format` of that
|
|
48
|
+
paragraph in one batch, and splits them on `LIST_FORMAT_COMPOSITION_CONFLICT`.
|
|
49
|
+
|
|
50
|
+
Do not expose negotiated but unused capabilities as choices in the editing
|
|
51
|
+
prompt. `command-help-v1`, restore shortcuts, speculative apply, and localized
|
|
52
|
+
change summaries are optional requirements only for integrations that use them.
|
|
53
|
+
|
|
54
|
+
## Safety invariants
|
|
55
|
+
|
|
56
|
+
Every generated integration must:
|
|
57
|
+
|
|
58
|
+
- inspect narrowly, retain `exactText`, and target with `paragraphId` plus
|
|
59
|
+
`fingerprint`;
|
|
60
|
+
- use strict targeting, package validation, and a derived output path;
|
|
61
|
+
- never overwrite the source unless `--in-place` is explicitly requested;
|
|
62
|
+
- require `completion: true` and a non-null `outputPath` before reporting
|
|
63
|
+
success;
|
|
64
|
+
- follow `error.recovery.action` and never retry unchanged failed arguments; and
|
|
65
|
+
- require explicit authorization before accepting/rejecting another reviewer's
|
|
66
|
+
work or deleting comments.
|
|
67
|
+
|
|
68
|
+
The CLI computes `completion` from write status, operation outcomes, commit
|
|
69
|
+
disposition, and localized accepted-view verification. Preserve the detailed
|
|
70
|
+
results for audit and recovery, but do not make the editing agent re-evaluate
|
|
71
|
+
those fields during ordinary success handling.
|
|
72
|
+
|
|
73
|
+
## Workflow policy choices
|
|
74
|
+
|
|
75
|
+
Keep these exceptions separate from the default workflow:
|
|
76
|
+
|
|
77
|
+
| Choice | Default | Override only when |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| Transaction | progressive | Partial output is unacceptable (`--atomic`). |
|
|
80
|
+
| Existing revisions | `merge-same-author` | Editing inside another reviewer's pending insertion is intended (`slice-cross-author`). |
|
|
81
|
+
| Reviewer identity | `AI Redliner` is a valid visible fallback | The host supplies a preferred flag, operation author, or environment value. |
|
|
82
|
+
| Output | derived sibling | The caller deliberately selects another destination. |
|
|
83
|
+
|
|
84
|
+
Check `effectiveOptions` when an integration deliberately sets a policy.
|
|
85
|
+
|
|
86
|
+
## Transport choices
|
|
87
|
+
|
|
88
|
+
A UTF-8 operations file and serializer-backed stdin are peer transports:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
docx-redline apply input.docx --operations operations.json --profile agent --output reviewed.docx
|
|
92
|
+
node emit-operations.mjs | docx-redline apply input.docx --operations - --profile agent --compact --output reviewed.docx
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Use `JSON.stringify` or a structured tool API. Never demonstrate `echo`, a
|
|
96
|
+
heredoc, or shell-interpolated document text. In JavaScript/TypeScript runtimes
|
|
97
|
+
(Node.js, browsers, Cloudflare Workers, Deno, Bun, or sandboxed environments like
|
|
98
|
+
n8n), an in-process wrapper should call `openDocx(uint8Array)` directly from
|
|
99
|
+
`@ansonlai/docx-redline-js` (or `@ansonlai/docx-redline-js/bundle` for zero-dependency
|
|
100
|
+
standalone sandboxes) and return the structured result instead of recreating CLI
|
|
101
|
+
serialization, ZIP handling, targeting, validation, or rollback.
|
|
102
|
+
|
|
103
|
+
## Ordinary generated workflow
|
|
104
|
+
|
|
105
|
+
The first executable editing example in a generated skill must be focused
|
|
106
|
+
extraction:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
docx-redline extract input.docx --search "renewal notice" --around 3
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Then apply once using the inspected strong target. Use complete `modified` text
|
|
113
|
+
for a broad semantic revision or `replacements` for a small literal change in
|
|
114
|
+
that target. Keep requested relative wording relative unless the user asks for
|
|
115
|
+
a concrete value.
|
|
116
|
+
|
|
117
|
+
Follow `selection.nextAfter` when extraction is truncated. Output projections must retain
|
|
118
|
+
`selection`, target descriptors, `humanReference`, and revision
|
|
119
|
+
cues.
|
|
120
|
+
|
|
121
|
+
### Observed restore branch
|
|
122
|
+
|
|
123
|
+
If the user requests restoration, search `--view rejected` first. Otherwise
|
|
124
|
+
follow an alternate-view `selection.hint` when returned. Restore only the
|
|
125
|
+
inspected strong target. Require `restore-shortcuts-v1` only if the integration
|
|
126
|
+
uses inline `--restore`; canonical operations remain available.
|
|
127
|
+
|
|
128
|
+
### Optional speculative execution
|
|
129
|
+
|
|
130
|
+
Do not make speculative apply part of an ordinary generated workflow. A harness
|
|
131
|
+
may opt into `speculative-search-apply-v1` only when it owns a measured confidence
|
|
132
|
+
policy and accepts the recovery cost of a miss. Keep anchor selection,
|
|
133
|
+
directional windows, occurrence handling, and exact matching semantics out of
|
|
134
|
+
the ordinary editing prompt; link to the knowledge base or CLI help instead.
|
|
135
|
+
|
|
136
|
+
## Recovery contract
|
|
137
|
+
|
|
138
|
+
Lead failure handling with these runtime fields:
|
|
139
|
+
|
|
140
|
+
1. `error.recovery.action`
|
|
141
|
+
2. `error.recovery.requiresReinspection`
|
|
142
|
+
3. `error.recovery.requiresUserAuthorization`
|
|
143
|
+
4. `error.recovery.sameArgumentsSafe`
|
|
144
|
+
|
|
145
|
+
Use `retryPlan.base` and its operation indexes to select the original or partial
|
|
146
|
+
output package. Do not reproduce an error-code decision tree in the skill.
|
|
147
|
+
|
|
148
|
+
## Presentation rules
|
|
149
|
+
|
|
150
|
+
Use `humanReference`, `provision`, or `nearestHeading` in completion reports.
|
|
151
|
+
Paragraph indexes and `P55`-style references are machine coordinates, not
|
|
152
|
+
locations a document user can follow. Keep the exact machine target internally.
|
|
153
|
+
|
|
154
|
+
## Generation checklist
|
|
155
|
+
|
|
156
|
+
- Exact tested release, contract 8 (9 only with `list-format`), and the minimum capability set are pinned.
|
|
157
|
+
- Runtime compatibility is checked once.
|
|
158
|
+
- The first ordinary command is focused extraction.
|
|
159
|
+
- Apply uses the inspected target with `modified` or localized `replacements`.
|
|
160
|
+
- Restore appears only as a user-requested or observed-view branch.
|
|
161
|
+
- Speculative execution is absent unless the harness owns a measured policy.
|
|
162
|
+
- Ordinary success requires `completion: true` and a non-null `outputPath`.
|
|
163
|
+
- Recovery, authorization, source immutability, and structured serialization
|
|
164
|
+
invariants are preserved.
|
|
165
|
+
- Wrappers delegate mutation and OOXML packaging to the library.
|