@superdoc/sdk 2.26.0 → 2.27.0-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # @superdoc/sdk
2
2
 
3
- Node.js SDK for reading and editing DOCX files with SuperDoc.
3
+ Node.js SDK for reading and editing DOCX files with SuperDoc, without a
4
+ browser. Query, edit, comment, and review tracked changes from your server,
5
+ or give an LLM SuperDoc tools with the agent toolkit. Edits write back to
6
+ OOXML, so the output opens in Word. For an interactive editor, use
7
+ [`superdoc`](https://www.npmjs.com/package/superdoc).
8
+
9
+ [Documentation](https://docs.superdoc.dev/agents/automation/node-sdk) · [GitHub](https://github.com/superdoc/docx-editor)
4
10
 
5
11
  ## Install
6
12
 
@@ -8,23 +14,37 @@ Node.js SDK for reading and editing DOCX files with SuperDoc.
8
14
  npm install @superdoc/sdk
9
15
  ```
10
16
 
17
+ The SDK embeds the SuperDoc runtime for your platform. You do not need a
18
+ separate CLI installation.
19
+
11
20
  ## Quick start
12
21
 
13
22
  ```js
14
23
  import { SuperDocClient } from '@superdoc/sdk';
15
24
 
16
- const client = new SuperDocClient();
17
- await client.connect();
25
+ const client = new SuperDocClient({ user: { name: 'Contract bot', email: 'bot@example.com' } });
18
26
 
19
- const document = await client.open({ doc: './contract.docx' });
20
- const markdown = await document.getMarkdown();
27
+ try {
28
+ await client.connect();
29
+ const doc = await client.open({ doc: './contract.docx' });
21
30
 
22
- console.log(markdown);
31
+ try {
32
+ const receipt = await doc.trackChanges.decide({ decision: 'accept', target: { kind: 'all' } });
33
+ if (!receipt.success) throw new Error('Accepting tracked changes failed.');
23
34
 
24
- await document.close();
25
- await client.dispose();
35
+ await doc.save({ out: './contract.accepted.docx', force: true });
36
+ } finally {
37
+ await doc.close({ discard: true });
38
+ }
39
+ } finally {
40
+ await client.dispose();
41
+ }
26
42
  ```
27
43
 
44
+ Save to a new path so the source stays unchanged. `force: true` lets the save
45
+ replace an earlier output. `close({ discard: true })` then closes the session
46
+ without writing over the source.
47
+
28
48
  ## Client lifetime
29
49
 
30
50
  One client owns one SuperDoc host process. `connect()` starts it once; every
@@ -37,6 +57,97 @@ room sync on every call. To cut round trips further, batch related edits with
37
57
  `document.mutations.apply()` or arbitrary operations with
38
58
  `document.plan.execute()`.
39
59
 
60
+ ## Common tasks
61
+
62
+ ### Read the document
63
+
64
+ ```js
65
+ const markdown = await doc.getMarkdown();
66
+ const text = await doc.getText({});
67
+ ```
68
+
69
+ ### Find and replace text as a tracked change
70
+
71
+ `query.match` returns a complete target. Pass it to the mutation instead of
72
+ building one yourself:
73
+
74
+ ```js
75
+ const match = await doc.query.match({
76
+ select: { type: 'text', pattern: 'termination' },
77
+ require: 'first',
78
+ });
79
+ const item = match.items[0];
80
+ if (!item || item.matchKind !== 'text') throw new Error('Text not found.');
81
+
82
+ const operation = await doc.replace({
83
+ target: item.target,
84
+ text: 'cancellation',
85
+ changeMode: 'tracked',
86
+ });
87
+ const receipt = 'receipt' in operation ? operation.receipt : operation;
88
+ if (!receipt.success) throw new Error('Creating the tracked change failed.');
89
+ ```
90
+
91
+ ### Give an LLM SuperDoc tools
92
+
93
+ `createAgentToolkit` returns tool definitions, a matching system prompt, and a
94
+ `dispatch` function, so the three cannot disagree:
95
+
96
+ ```js
97
+ import { createAgentToolkit } from '@superdoc/sdk';
98
+
99
+ const { tools, systemPrompt, dispatch } = await createAgentToolkit({
100
+ provider: 'openai', // 'openai' | 'anthropic' | 'vercel' | 'generic'
101
+ preset: 'core',
102
+ enforceChangeMode: 'tracked',
103
+ });
104
+
105
+ // For each tool call in an OpenAI Chat Completions reply:
106
+ const result = await dispatch(doc, call.function.name, JSON.parse(call.function.arguments));
107
+ ```
108
+
109
+ OpenAI sends tool arguments as a JSON string, so parse them before calling
110
+ `dispatch`. Anthropic and Vercel pass an object already.
111
+
112
+ `enforceChangeMode: 'tracked'` hides actions that cannot record a tracked
113
+ change and records every other edit as tracked. See
114
+ [build an agent](https://docs.superdoc.dev/agents/build/build-an-agent) for the
115
+ full loop.
116
+
117
+ ## Common mistakes
118
+
119
+ | Wrong | Right |
120
+ | --- | --- |
121
+ | Creating a client per operation | Create one per service and keep document handles open |
122
+ | Passing OpenAI's JSON-string arguments to `dispatch` | `JSON.parse` them first. `dispatch` takes an object |
123
+ | Omitting `preset` in `createAgentToolkit` | Pass `preset: 'core'`. Omitting it selects the older `legacy` tools |
124
+ | Omitting `user` | Name the workflow so tracked changes show who proposed them |
125
+ | Reusing a `query.match` target after an edit | Query again. Targets are bound to the revision that produced them |
126
+ | Building targets from OOXML IDs such as `w14:paraId` | Use the address or target the API returns |
127
+ | Saving over the source while testing | Save to a new path with `out` |
128
+ | Retrying a failed receipt automatically | Inspect `executedOperations` first. Part of the edit may have committed |
129
+ | Importing `superdoc` in Node.js | Use this package. `superdoc` is the browser editor |
130
+
131
+ ## Related packages
132
+
133
+ - [`superdoc`](https://www.npmjs.com/package/superdoc): browser DOCX editor for reviewing the SDK's output.
134
+ - [`@superdoc/mcp`](https://www.npmjs.com/package/@superdoc/mcp): the same tools for an existing MCP client, without writing an agent loop.
135
+ - [`@superdoc/cli`](https://www.npmjs.com/package/@superdoc/cli): the same operations from a terminal or script.
136
+ - [`superdoc-sdk`](https://pypi.org/project/superdoc-sdk/) on PyPI and [`superdoc-sdk`](https://crates.io/crates/superdoc-sdk) on crates.io: Python and Rust SDKs.
137
+
138
+ ## Documentation
139
+
140
+ - [Node.js SDK guide](https://docs.superdoc.dev/agents/automation/node-sdk)
141
+ - [Build an agent](https://docs.superdoc.dev/agents/build/build-an-agent)
142
+ - [Document API mental model](https://docs.superdoc.dev/document-api/mental-model)
143
+ - [Document comparison](https://docs.superdoc.dev/document-api/document-comparison)
144
+ - [Agent safety](https://docs.superdoc.dev/agents/operate/safety)
145
+ - [`llms.txt`](https://docs.superdoc.dev/llms.txt): machine-readable index of the documentation for coding agents.
146
+
147
+ ## Advanced
148
+
149
+ ### State directory
150
+
40
151
  For read-only hosts and containers, configure a writable state directory:
41
152
 
42
153
  ```js
@@ -73,6 +184,8 @@ the SDK parent can leave temporary state behind; the operating system or host
73
184
  operator must reclaim it. A new client gets a separate private root and does
74
185
  not sweep another client's state.
75
186
 
187
+ ### Authenticated collaboration rooms
188
+
76
189
  For token-authenticated Hocuspocus or y-websocket rooms, pass the credential as
77
190
  an option on that document's open request:
78
191
 
@@ -94,9 +207,41 @@ The credential applies only to that open request. It is not part of the
94
207
  collaboration profile or command-line arguments. Per-open token authentication
95
208
  is not supported for Liveblocks.
96
209
 
97
- See the [Node.js SDK guide](https://docs.superdoc.dev/agents/automation/node-sdk) for a complete workflow.
210
+ ### Attributed change records
98
211
 
99
- ## Discover footer paragraphs
212
+ Give each session a stable actor id with `userId`. Pass an application-signed
213
+ claim, if you have one, as the `identityClaim` open option; like the
214
+ collaboration token, it is sent only with that open request and is never
215
+ written to arguments or state. SuperDoc records the claim but does not verify
216
+ it.
217
+
218
+ ```js
219
+ const document = await client.open(
220
+ { doc: './contract.docx', userId: 'user-42', collaboration },
221
+ { collaborationAuth: { type: 'token', token: userToken }, identityClaim: signedClaim },
222
+ );
223
+ ```
224
+
225
+ Every local and remote edit produces a change record. Read them with
226
+ `document.changes.list({ after, waitMs })`; `waitMs` waits on the host without
227
+ blocking other requests. To be told about new records, subscribe to host
228
+ notifications:
229
+
230
+ ```js
231
+ const notifications = client.subscribe();
232
+ for await (const notification of notifications) {
233
+ if (notification.method === 'event.change') handle(notification.params.records);
234
+ }
235
+ ```
236
+
237
+ Each subscription holds up to `capacity` notifications (default 64). If you
238
+ fall behind, the next read rejects with `NOTIFICATIONS_LAGGED`; resynchronize
239
+ with `changes.list({ after })` from the last cursor you processed, then keep
240
+ iterating. A host disconnect rejects one read with `HOST_DISCONNECTED`.
241
+ `notifications.close()` unsubscribes and `client.dispose()` ends every
242
+ subscription.
243
+
244
+ ### Discover footer paragraphs
100
245
 
101
246
  `blocks.list({ in: story })` lists top-level blocks. To discover paragraphs
102
247
  inside footer tables, including tables wrapped in block content controls, use
@@ -132,7 +277,47 @@ if (matches.total === 1) {
132
277
  }
133
278
  ```
134
279
 
135
- ## Tracked comparison with direct style definitions
280
+ ### Inspecting textbox sections
281
+
282
+ `superdoc_inspect` includes discovered text-bearing textbox stories in `textboxes` when
283
+ block inspection is enabled. Each entry contains its explicit `story` locator,
284
+ its story-local `total`, and normalized `blocks` with text, identities, and list
285
+ markers. The top-level `blocks` array and its ordinals continue to refer to the
286
+ body; textbox block ordinals belong to their containing story.
287
+
288
+ The block offset, limit, text cap, and block filters also apply within each
289
+ textbox. Count-only inspections and inspections excluding the blocks domain do
290
+ not collect textbox text. Check `diagnostics` for incomplete story discovery or
291
+ reads. To edit a discovered heading, use `query.match` with its story locator
292
+ and pass the returned reference to `replace`; a textbox ordinal is not a body
293
+ action selector.
294
+
295
+ ### Rejectable comparison styles
296
+
297
+ For standalone V2 comparisons, use the tracked style policy when reviewers need to
298
+ reject proposed styling as well as text:
299
+
300
+ ```js
301
+ const diff = await older.diff.compare({ targetSnapshot, styleChangeMode: 'tracked' });
302
+ const result = await older.diff.apply({ diff, changeMode: 'tracked', styleChangeMode: 'tracked' });
303
+ await older.save({ out: 'comparison.docx' });
304
+ ```
305
+
306
+ The saved file contains separate proposed style definitions and tracked references
307
+ that Word can accept or reject. Rejection restores the original text and effective
308
+ styling. Unused definitions may remain after a decision, and proposed styles retain
309
+ their distinct names. Inspect `result.styleChanges` and `result.diagnostics`.
310
+
311
+ This policy supports non-default paragraph and character styles in the main body,
312
+ including inherited styles and outline-based TOCs. TOCs that select an affected style
313
+ by identity (`\o`) or name (`\t`) are refused. Defaults, table styles, affected styles in other stories,
314
+ combined table topology changes, pre-existing revisions, and definitions that activate already-used unresolved style
315
+ references are refused. Numbering
316
+ and settings retain their restrictions. Versioned suggest-only review sessions have
317
+ a separate policy and refuse this option. Read eligibility before applying and
318
+ handle apply-time refusals without saving a result.
319
+
320
+ ### Tracked comparison with direct style definitions
136
321
 
137
322
  For standalone V2 comparisons, `styleChangeMode: 'direct'` lets you explicitly
138
323
  carry the target style definitions while applying body edits as tracked changes:
@@ -147,12 +332,12 @@ await older.save({ out: 'comparison.docx' });
147
332
  Set the policy on both calls: compare reports eligibility for that policy;
148
333
  apply requires its own explicit consent. Style definitions are applied directly
149
334
  and remain if the body revisions are rejected in Word. A style-only comparison
150
- creates no body revisions. Without this option, changed style definitions still
335
+ creates no body revisions. Without an explicit style policy, changed definitions still
151
336
  block tracked apply. Unsupported numbering, settings, and other package changes
152
337
  retain their existing restrictions. Suggest-only versioned review sessions
153
338
  refuse this direct-style option.
154
339
 
155
- ## Tracked comparison with exact-file review
340
+ ### Tracked comparison with exact-file review
156
341
 
157
342
  The SDK's `diff.capture` and `diff.compare` describe a snapshot comparison. A
158
343
  blocked tracked `diff.apply` cannot turn that snapshot into a complete review
@@ -216,7 +401,7 @@ endpoints; other supported changes become one whole-document proposal. A stale
216
401
  base or invalid DOCX returns a refusal without a partial apply. The host owns
217
402
  authorization, durable storage, reviewer presentation, and decisions.
218
403
 
219
- ## Agent execution evidence
404
+ ### Agent execution evidence
220
405
 
221
406
  The core preset's `agent_apply`, `agent_verify`, and supported named actions
222
407
  collect the facts their targets and checks need. CLI-backed sessions
@@ -262,19 +447,7 @@ automatically replay the write.
262
447
 
263
448
  ## License
264
449
 
265
- AGPL-3.0. Commercial licenses are available from [SuperDoc](https://www.superdoc.dev).
266
-
267
- ## Inspecting textbox sections
450
+ AGPL-3.0. [Commercial licenses](https://www.superdoc.dev/#pricing) are
451
+ available for proprietary deployments.
268
452
 
269
- `superdoc_inspect` includes discovered text-bearing textbox stories in `textboxes` when
270
- block inspection is enabled. Each entry contains its explicit `story` locator,
271
- its story-local `total`, and normalized `blocks` with text, identities, and list
272
- markers. The top-level `blocks` array and its ordinals continue to refer to the
273
- body; textbox block ordinals belong to their containing story.
274
-
275
- The block offset, limit, text cap, and block filters also apply within each
276
- textbox. Count-only inspections and inspections excluding the blocks domain do
277
- not collect textbox text. Check `diagnostics` for incomplete story discovery or
278
- reads. To edit a discovered heading, use `query.match` with its story locator
279
- and pass the returned reference to `replace`; a textbox ordinal is not a body
280
- action selector.
453
+ The retained legacy comparison adapter refuses tracked style policy with `CAPABILITY_UNSUPPORTED`; use the current typed engine through the standalone SDK.
@@ -388,10 +388,6 @@ export interface ResolveActionPolicyOptions {
388
388
  ignoreUnknownActions?: boolean;
389
389
  }
390
390
  export declare function resolveActionPolicy(options?: ListAgentToolsOptions, resolveOptions?: ResolveActionPolicyOptions): ResolvedActionPolicy;
391
- /** @deprecated Use {@link resolveActionPolicy}; kept for callers that only need the union. */
392
- export declare function validateExclusions(options?: ListAgentToolsOptions): {
393
- excludedActions: Set<string>;
394
- };
395
391
  /**
396
392
  * Tools advertised to the LLM. Only PUBLIC_AGENT_TOOL_NAMES (currently
397
393
  * superdoc_inspect + superdoc_perform_action) are returned; the remaining
@@ -614,10 +614,6 @@ export function resolveActionPolicy(options, resolveOptions) {
614
614
  allowDirectActions,
615
615
  };
616
616
  }
617
- /** @deprecated Use {@link resolveActionPolicy}; kept for callers that only need the union. */
618
- export function validateExclusions(options) {
619
- return { excludedActions: resolveActionPolicy(options).excludedActions };
620
- }
621
617
  /**
622
618
  * Tools advertised to the LLM. Only PUBLIC_AGENT_TOOL_NAMES (currently
623
619
  * superdoc_inspect + superdoc_perform_action) are returned; the remaining
@@ -2,7 +2,6 @@
2
2
 
3
3
  var contract = require('./contract.cjs');
4
4
 
5
- /* eslint-disable */
6
5
  // Auto-generated by packages/sdk/codegen/src/generate-node.mjs
7
6
  // ---------------------------------------------------------------------------
8
7
  // Raw API (all operations — internal use only)
@@ -303,6 +302,13 @@ function createDocApi(runtime) {
303
302
  undo: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.history.undo"], params, options),
304
303
  redo: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.history.redo"], params, options),
305
304
  },
305
+ changes: {
306
+ list: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.changes.list"], params, options),
307
+ get: (params, options) => runtime.invoke(contract.CONTRACT.operations["doc.changes.get"], params, options),
308
+ resolve: (params, options) => runtime.invoke(contract.CONTRACT.operations["doc.changes.resolve"], params, options),
309
+ actors: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.changes.actors"], params, options),
310
+ snapshot: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.changes.snapshot"], params, options),
311
+ },
306
312
  images: {
307
313
  list: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.images.list"], params, options),
308
314
  get: (params, options) => runtime.invoke(contract.CONTRACT.operations["doc.images.get"], params, options),
@@ -891,6 +897,13 @@ function createBoundDocApi(runtime) {
891
897
  undo: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.history.undo"], params, options),
892
898
  redo: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.history.redo"], params, options),
893
899
  },
900
+ changes: {
901
+ list: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.changes.list"], params, options),
902
+ get: (params, options) => runtime.invoke(contract.CONTRACT.operations["doc.changes.get"], params, options),
903
+ resolve: (params, options) => runtime.invoke(contract.CONTRACT.operations["doc.changes.resolve"], params, options),
904
+ actors: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.changes.actors"], params, options),
905
+ snapshot: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.changes.snapshot"], params, options),
906
+ },
894
907
  images: {
895
908
  list: (params = {}, options) => runtime.invoke(contract.CONTRACT.operations["doc.images.list"], params, options),
896
909
  get: (params, options) => runtime.invoke(contract.CONTRACT.operations["doc.images.get"], params, options),