@superdoc/sdk 2.5.0 → 2.7.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.
Files changed (55) hide show
  1. package/dist/agent/actions.cjs +1869 -160
  2. package/dist/agent/actions.d.ts +77 -10
  3. package/dist/agent/actions.js +1870 -161
  4. package/dist/agent/catalog.cjs +102 -9
  5. package/dist/agent/catalog.d.ts +243 -0
  6. package/dist/agent/catalog.js +99 -9
  7. package/dist/agent/doc-snapshot.cjs +200 -2
  8. package/dist/agent/doc-snapshot.d.ts +91 -0
  9. package/dist/agent/doc-snapshot.js +199 -2
  10. package/dist/agent/runtime.cjs +9 -1
  11. package/dist/agent/runtime.d.ts +8 -0
  12. package/dist/agent/runtime.js +9 -1
  13. package/dist/embedded-tools.generated.cjs +5 -5
  14. package/dist/embedded-tools.generated.js +5 -5
  15. package/dist/generated/client.cjs +754 -770
  16. package/dist/generated/client.d.ts +14 -9
  17. package/dist/generated/client.js +754 -770
  18. package/dist/generated/contract.cjs +16207 -274
  19. package/dist/generated/contract.d.ts +38 -0
  20. package/dist/generated/contract.js +17443 -1503
  21. package/dist/index.cjs +5 -4
  22. package/dist/index.d.ts +2 -2
  23. package/dist/index.js +5 -4
  24. package/dist/introspection.cjs +59 -0
  25. package/dist/introspection.d.ts +3 -0
  26. package/dist/introspection.js +53 -0
  27. package/dist/runtime/document-rpc.cjs +328 -0
  28. package/dist/runtime/document-rpc.d.ts +24 -0
  29. package/dist/runtime/document-rpc.js +312 -0
  30. package/dist/runtime/embedded-cli.cjs +5 -68
  31. package/dist/runtime/embedded-cli.js +5 -67
  32. package/dist/runtime/embedded-document-host.cjs +28 -0
  33. package/dist/runtime/embedded-document-host.d.ts +1 -0
  34. package/dist/runtime/embedded-document-host.js +23 -0
  35. package/dist/runtime/embedded-platform.cjs +102 -0
  36. package/dist/runtime/embedded-platform.d.ts +5 -0
  37. package/dist/runtime/embedded-platform.js +93 -0
  38. package/dist/runtime/host.cjs +128 -16
  39. package/dist/runtime/host.d.ts +11 -5
  40. package/dist/runtime/host.js +126 -16
  41. package/dist/runtime/process.cjs +34 -5
  42. package/dist/runtime/process.d.ts +12 -3
  43. package/dist/runtime/process.js +33 -5
  44. package/dist/runtime/transport-common.cjs +1 -0
  45. package/dist/runtime/transport-common.d.ts +28 -10
  46. package/dist/runtime/transport-common.js +1 -1
  47. package/package.json +7 -6
  48. package/tools/__pycache__/__init__.cpython-311.pyc +0 -0
  49. package/tools/__pycache__/intent_dispatch_generated.cpython-311.pyc +0 -0
  50. package/tools/catalog.json +2 -2
  51. package/tools/tools-policy.json +1 -1
  52. package/tools/tools.anthropic.json +2 -2
  53. package/tools/tools.generic.json +2 -2
  54. package/tools/tools.openai.json +2 -2
  55. package/tools/tools.vercel.json +2 -2
@@ -2,8 +2,10 @@ import { spawn } from 'node:child_process';
2
2
  import { createInterface } from 'node:readline';
3
3
  import { buildOperationArgv, resolveInvocation, } from './transport-common.js';
4
4
  import { SuperDocCliError } from './errors.js';
5
+ import { DOCUMENT_RPC_FEATURES, DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE, DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE, buildDocumentInvokeParams, buildDocumentOpenParams, canOpenWithDocumentRpc, documentRpcRequestSupportsResponseTimeout, documentRpcEnabled, mapDocumentLifecycleResult, mapDocumentOpenResult, supportsDocumentRpc, } from './document-rpc.js';
5
6
  const HOST_PROTOCOL_VERSION = '1.0';
6
- const REQUIRED_FEATURES = ['cli.invoke', 'host.shutdown'];
7
+ const CLI_HOST_REQUIRED_FEATURES = ['cli.invoke', 'host.shutdown'];
8
+ const DOCUMENT_HOST_REQUIRED_FEATURES = [...DOCUMENT_RPC_FEATURES, 'host.shutdown'];
7
9
  const CHANGE_MODES = ['direct', 'tracked'];
8
10
  const FORWARD_HOST_STDERR = typeof process !== 'undefined' && typeof process.env?.SUPERDOC_DEBUG_TEXT_REWRITE === 'string'
9
11
  ? process.env.SUPERDOC_DEBUG_TEXT_REWRITE === '1'
@@ -18,6 +20,21 @@ const HOST_DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
18
20
  // abort. The buffer absorbs JSON-RPC serialization, stdio drain, and event-
19
21
  // loop latency.
20
22
  const WATCHDOG_HEADROOM_MS = 5_000;
23
+ export function mapCliInvocationResult(operation, value) {
24
+ const envelopeKey = operation.responseEnvelopeKey;
25
+ if (envelopeKey === null || envelopeKey === undefined)
26
+ return value;
27
+ if (typeof envelopeKey !== 'string' || envelopeKey.length === 0) {
28
+ throw new SuperDocCliError('Generated operation has invalid response envelope metadata.', {
29
+ code: 'HOST_PROTOCOL_ERROR',
30
+ details: { operationId: operation.operationId, responseEnvelopeKey: envelopeKey },
31
+ });
32
+ }
33
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
34
+ return value;
35
+ const extracted = value[envelopeKey];
36
+ return extracted === undefined ? value : extracted;
37
+ }
21
38
  /**
22
39
  * Builds the argv passed to `spawn` for `superdoc host --stdio`. Propagates
23
40
  * `requestTimeoutMs` to the host via `--request-timeout-ms`, since the SDK
@@ -32,6 +49,9 @@ export function buildHostSpawnArgs(prefixArgs, options) {
32
49
  }
33
50
  return args;
34
51
  }
52
+ export function buildDocumentHostSpawnArgs(prefixArgs) {
53
+ return [...prefixArgs];
54
+ }
35
55
  /**
36
56
  * Computes the JS-side watchdog timeout for a single JSON-RPC request.
37
57
  *
@@ -57,11 +77,10 @@ export function resolveJsWatchdogTimeout(watchdogTimeoutMs, requestTimeoutMs, ti
57
77
  }
58
78
  return Math.max(watchdogTimeoutMs, HOST_DEFAULT_REQUEST_TIMEOUT_MS + WATCHDOG_HEADROOM_MS);
59
79
  }
60
- /**
61
- * Transport that communicates with a long-lived CLI host process over JSON-RPC stdio.
62
- */
80
+ /** Transport for the legacy CLI host or the structured document host. */
63
81
  export class HostTransport {
64
- cliBin;
82
+ hostBin;
83
+ processMode;
65
84
  env;
66
85
  startupTimeoutMs;
67
86
  shutdownTimeoutMs;
@@ -70,14 +89,18 @@ export class HostTransport {
70
89
  maxQueueDepth;
71
90
  defaultChangeMode;
72
91
  user;
92
+ documentRpcEnabled;
73
93
  child = null;
74
94
  stdoutReader = null;
75
95
  pending = new Map();
76
96
  nextRequestId = 1;
77
97
  connecting = null;
78
98
  stopping = false;
99
+ hostFeatures = new Set();
100
+ documentRpcSessions = new Set();
79
101
  constructor(options) {
80
- this.cliBin = options.cliBin;
102
+ this.hostBin = options.hostBin;
103
+ this.processMode = options.processMode ?? 'cli';
81
104
  this.env = options.env;
82
105
  this.startupTimeoutMs = options.startupTimeoutMs ?? 5_000;
83
106
  this.shutdownTimeoutMs = options.shutdownTimeoutMs ?? 5_000;
@@ -90,8 +113,9 @@ export class HostTransport {
90
113
  details: { defaultChangeMode: options.defaultChangeMode },
91
114
  });
92
115
  }
93
- this.defaultChangeMode = options.defaultChangeMode;
116
+ this.defaultChangeMode = options.defaultChangeMode ?? undefined;
94
117
  this.user = options.user;
118
+ this.documentRpcEnabled = this.processMode === 'document' || documentRpcEnabled(options.env);
95
119
  }
96
120
  async connect() {
97
121
  await this.ensureConnected();
@@ -122,6 +146,53 @@ export class HostTransport {
122
146
  }
123
147
  async invoke(operation, params = {}, options = {}) {
124
148
  await this.ensureConnected();
149
+ const shouldOpenThroughCliTimeoutFallback = this.processMode === 'cli' &&
150
+ (this.requestTimeoutMs !== undefined ||
151
+ (options.timeoutMs !== undefined && !this.hostFeatures.has(DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE)));
152
+ if (this.documentRpcEnabled &&
153
+ supportsDocumentRpc(this.hostFeatures) &&
154
+ (this.defaultChangeMode === undefined || this.hostFeatures.has(DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE)) &&
155
+ operation.operationId === 'doc.open' &&
156
+ canOpenWithDocumentRpc(params, options) &&
157
+ !shouldOpenThroughCliTimeoutFallback) {
158
+ const opened = buildDocumentOpenParams(params, this.user, this.defaultChangeMode);
159
+ const requestTimeoutMs = this.resolveDocumentRequestTimeout(options.timeoutMs);
160
+ const response = await this.sendJsonRpcRequest('document.open', opened.params, this.resolveWatchdogTimeout(options.timeoutMs), { requestTimeoutMs });
161
+ const result = mapDocumentOpenResult(response, opened.sessionId, params.doc);
162
+ this.documentRpcSessions.add(opened.sessionId);
163
+ return result;
164
+ }
165
+ const sessionId = typeof params.sessionId === 'string' ? params.sessionId : undefined;
166
+ if (sessionId !== undefined && this.documentRpcSessions.has(sessionId)) {
167
+ const request = buildDocumentInvokeParams(sessionId, operation, params, options, this.hostFeatures);
168
+ const supportsResponseTimeout = documentRpcRequestSupportsResponseTimeout(operation, request);
169
+ const requestTimeoutMs = supportsResponseTimeout
170
+ ? this.resolveDocumentRequestTimeout(options.timeoutMs)
171
+ : undefined;
172
+ const response = await this.sendJsonRpcRequest(request.method, request.params, this.resolveWatchdogTimeout(options.timeoutMs), { requestTimeoutMs });
173
+ const result = mapDocumentLifecycleResult(operation.operationId, response, sessionId, operation.operationId === 'doc.save' ? request.params.path === undefined : undefined);
174
+ if (operation.operationId === 'doc.close')
175
+ this.documentRpcSessions.delete(sessionId);
176
+ return result;
177
+ }
178
+ if (this.processMode === 'document') {
179
+ throw new SuperDocCliError(`Standalone document host does not support ${operation.operationId} through structured RPC v0.`, {
180
+ code: 'DOCUMENT_RPC_INPUT_UNSUPPORTED',
181
+ details: { operationId: operation.operationId },
182
+ });
183
+ }
184
+ const cliHostRequestTimeoutMs = this.requestTimeoutMs ?? HOST_DEFAULT_REQUEST_TIMEOUT_MS;
185
+ if (options.timeoutMs !== undefined && options.timeoutMs > cliHostRequestTimeoutMs) {
186
+ throw new SuperDocCliError(`CLI host cannot honor timeoutMs=${options.timeoutMs} above its ${cliHostRequestTimeoutMs}ms request ceiling.`, {
187
+ code: 'DOCUMENT_RPC_INPUT_UNSUPPORTED',
188
+ details: {
189
+ feature: DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE,
190
+ operationId: operation.operationId,
191
+ timeoutMs: options.timeoutMs,
192
+ hostRequestTimeoutMs: cliHostRequestTimeoutMs,
193
+ },
194
+ });
195
+ }
125
196
  const argv = buildOperationArgv(operation, params, options, this.requestTimeoutMs, this.defaultChangeMode, this.user);
126
197
  const stdinBase64 = options.stdinBytes ? Buffer.from(options.stdinBytes).toString('base64') : '';
127
198
  const watchdogTimeout = this.resolveWatchdogTimeout(options.timeoutMs);
@@ -136,16 +207,16 @@ export class HostTransport {
136
207
  });
137
208
  }
138
209
  const resultRecord = response;
139
- return resultRecord.data;
210
+ return mapCliInvocationResult(operation, resultRecord.data);
140
211
  }
141
212
  async ensureConnected() {
142
- if (this.child && !this.child.killed) {
143
- return;
144
- }
145
213
  if (this.connecting) {
146
214
  await this.connecting;
147
215
  return;
148
216
  }
217
+ if (this.child && !this.child.killed) {
218
+ return;
219
+ }
149
220
  this.connecting = this.startHostProcess();
150
221
  try {
151
222
  await this.connecting;
@@ -155,12 +226,14 @@ export class HostTransport {
155
226
  }
156
227
  }
157
228
  async startHostProcess() {
158
- const { command, prefixArgs } = resolveInvocation(this.cliBin);
159
- const args = buildHostSpawnArgs(prefixArgs, { requestTimeoutMs: this.requestTimeoutMs });
229
+ const { command, prefixArgs } = resolveInvocation(this.hostBin);
230
+ const args = this.processMode === 'document'
231
+ ? buildDocumentHostSpawnArgs(prefixArgs)
232
+ : buildHostSpawnArgs(prefixArgs, { requestTimeoutMs: this.requestTimeoutMs });
160
233
  const child = spawn(command, args, {
161
234
  env: {
162
235
  ...process.env,
163
- ...(this.env ?? {}),
236
+ ...this.env,
164
237
  },
165
238
  stdio: ['pipe', 'pipe', 'pipe'],
166
239
  });
@@ -239,7 +312,14 @@ export class HostTransport {
239
312
  details: { features },
240
313
  });
241
314
  }
242
- for (const requiredFeature of REQUIRED_FEATURES) {
315
+ const requiredFeatures = this.processMode === 'document'
316
+ ? [
317
+ ...DOCUMENT_HOST_REQUIRED_FEATURES,
318
+ ...(this.defaultChangeMode === undefined ? [] : [DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE]),
319
+ ...(this.requestTimeoutMs === undefined ? [] : [DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE]),
320
+ ]
321
+ : CLI_HOST_REQUIRED_FEATURES;
322
+ for (const requiredFeature of requiredFeatures) {
243
323
  if (!features.includes(requiredFeature)) {
244
324
  throw new SuperDocCliError(`Host does not support required feature: ${requiredFeature}`, {
245
325
  code: 'HOST_HANDSHAKE_FAILED',
@@ -247,11 +327,27 @@ export class HostTransport {
247
327
  });
248
328
  }
249
329
  }
330
+ this.hostFeatures.clear();
331
+ for (const feature of features)
332
+ this.hostFeatures.add(feature);
250
333
  }
251
334
  resolveWatchdogTimeout(timeoutMsOverride) {
252
335
  return resolveJsWatchdogTimeout(this.watchdogTimeoutMs, this.requestTimeoutMs, timeoutMsOverride);
253
336
  }
254
- async sendJsonRpcRequest(method, params, watchdogTimeoutMs) {
337
+ resolveDocumentRequestTimeout(timeoutMsOverride) {
338
+ const requestTimeoutMs = timeoutMsOverride ?? this.requestTimeoutMs;
339
+ if (requestTimeoutMs === undefined)
340
+ return undefined;
341
+ if (this.hostFeatures.has(DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE))
342
+ return requestTimeoutMs;
343
+ if (this.processMode === 'cli' && timeoutMsOverride === undefined)
344
+ return undefined;
345
+ throw new SuperDocCliError(`Structured document RPC host does not support ${DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE}.`, {
346
+ code: 'DOCUMENT_RPC_INPUT_UNSUPPORTED',
347
+ details: { feature: DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE },
348
+ });
349
+ }
350
+ async sendJsonRpcRequest(method, params, watchdogTimeoutMs, metadata = {}) {
255
351
  const child = this.child;
256
352
  if (!child || !child.stdin.writable) {
257
353
  throw new SuperDocCliError('Host process is not available.', {
@@ -271,6 +367,7 @@ export class HostTransport {
271
367
  id,
272
368
  method,
273
369
  params,
370
+ ...(metadata.requestTimeoutMs === undefined ? {} : { requestTimeoutMs: metadata.requestTimeoutMs }),
274
371
  });
275
372
  const promise = new Promise((resolve, reject) => {
276
373
  const timer = setTimeout(() => {
@@ -346,6 +443,7 @@ export class HostTransport {
346
443
  const error = rawError;
347
444
  const data = error.data;
348
445
  const cliCode = typeof data?.cliCode === 'string' ? data.cliCode : undefined;
446
+ const domainCode = typeof data?.domainCode === 'string' ? data.domainCode : undefined;
349
447
  const cliMessage = typeof data?.message === 'string' ? data.message : undefined;
350
448
  const exitCode = typeof data?.exitCode === 'number' ? data.exitCode : undefined;
351
449
  if (cliCode) {
@@ -355,6 +453,16 @@ export class HostTransport {
355
453
  exitCode,
356
454
  });
357
455
  }
456
+ if (domainCode) {
457
+ return new SuperDocCliError(error.message, {
458
+ code: domainCode,
459
+ details: {
460
+ ...(data?.details === undefined ? {} : { domainDetails: data.details }),
461
+ ...(data?.stage === undefined ? {} : { stage: data.stage }),
462
+ ...(data?.byteLength === undefined ? {} : { byteLength: data.byteLength }),
463
+ },
464
+ });
465
+ }
358
466
  if (error.code === JSON_RPC_TIMEOUT_CODE) {
359
467
  return new SuperDocCliError(error.message, {
360
468
  code: 'TIMEOUT',
@@ -370,6 +478,8 @@ export class HostTransport {
370
478
  this.cleanupProcess(error);
371
479
  }
372
480
  cleanupProcess(error) {
481
+ this.hostFeatures.clear();
482
+ this.documentRpcSessions.clear();
373
483
  const child = this.child;
374
484
  if (child) {
375
485
  child.removeAllListeners();
@@ -2,21 +2,49 @@
2
2
 
3
3
  var host = require('./host.cjs');
4
4
  var embeddedCli = require('./embedded-cli.cjs');
5
+ var embeddedDocumentHost = require('./embedded-document-host.cjs');
5
6
  var debugTrace = require('./debug-trace.cjs');
7
+ var errors = require('./errors.cjs');
6
8
 
9
+ function resolveRuntimeProcess(options = {}, embedded = {
10
+ cli: embeddedCli.resolveEmbeddedCliBinary,
11
+ documentHost: embeddedDocumentHost.resolveEmbeddedDocumentHostBinary,
12
+ }) {
13
+ const configuredDocumentHostPath = options.documentHostPath;
14
+ if (configuredDocumentHostPath !== undefined && configuredDocumentHostPath.trim().length === 0) {
15
+ throw new errors.SuperDocCliError('documentHostPath must be a non-empty path.', {
16
+ code: 'INVALID_ARGUMENT',
17
+ });
18
+ }
19
+ const configuredDocumentHostBin = options.env?.SUPERDOC_SDK_DOCUMENT_HOST_BIN ?? process.env.SUPERDOC_SDK_DOCUMENT_HOST_BIN;
20
+ const documentHostBin = configuredDocumentHostPath ??
21
+ (configuredDocumentHostBin === 'embedded' ? embedded.documentHost() : configuredDocumentHostBin);
22
+ if (documentHostBin !== undefined && documentHostBin.trim().length === 0) {
23
+ throw new errors.SuperDocCliError('SUPERDOC_SDK_DOCUMENT_HOST_BIN must be a non-empty path.', {
24
+ code: 'INVALID_ARGUMENT',
25
+ });
26
+ }
27
+ if (documentHostBin !== undefined) {
28
+ return { hostBin: documentHostBin, processMode: 'document' };
29
+ }
30
+ return {
31
+ hostBin: options.env?.SUPERDOC_CLI_BIN ?? process.env.SUPERDOC_CLI_BIN ?? embedded.cli(),
32
+ processMode: 'cli',
33
+ };
34
+ }
7
35
  /**
8
- * Internal runtime that delegates CLI invocations to a persistent host transport.
36
+ * Internal runtime that delegates operations to a persistent host transport.
9
37
  *
10
- * Resolves the CLI binary and creates a {@link HostTransport} that communicates
11
- * with a long-lived `superdoc host --stdio` process.
38
+ * Uses the standalone document host when explicitly configured. Otherwise it
39
+ * preserves the embedded CLI compatibility path.
12
40
  */
13
41
  class SuperDocRuntime {
14
42
  transport;
15
43
  constructor(options = {}) {
16
- const cliBin = options.env?.SUPERDOC_CLI_BIN ?? process.env.SUPERDOC_CLI_BIN ?? embeddedCli.resolveEmbeddedCliBinary();
44
+ const runtimeProcess = resolveRuntimeProcess(options);
17
45
  this.transport = new host.HostTransport({
18
- cliBin,
19
46
  ...options,
47
+ ...runtimeProcess,
20
48
  });
21
49
  }
22
50
  async connect() {
@@ -59,3 +87,4 @@ class SuperDocRuntime {
59
87
  }
60
88
 
61
89
  exports.SuperDocRuntime = SuperDocRuntime;
90
+ exports.resolveRuntimeProcess = resolveRuntimeProcess;
@@ -1,9 +1,18 @@
1
1
  import type { DocumentRuntimeKind, InvokeOptions, OperationParamSpec, OperationSpec, RuntimeInvoker, SuperDocClientOptions } from './transport-common.js';
2
+ type RuntimeProcessResolution = {
3
+ hostBin: string;
4
+ processMode: 'cli' | 'document';
5
+ };
6
+ type EmbeddedRuntimeResolvers = {
7
+ cli: () => string;
8
+ documentHost: () => string;
9
+ };
10
+ export declare function resolveRuntimeProcess(options?: SuperDocClientOptions, embedded?: EmbeddedRuntimeResolvers): RuntimeProcessResolution;
2
11
  /**
3
- * Internal runtime that delegates CLI invocations to a persistent host transport.
12
+ * Internal runtime that delegates operations to a persistent host transport.
4
13
  *
5
- * Resolves the CLI binary and creates a {@link HostTransport} that communicates
6
- * with a long-lived `superdoc host --stdio` process.
14
+ * Uses the standalone document host when explicitly configured. Otherwise it
15
+ * preserves the embedded CLI compatibility path.
7
16
  */
8
17
  export declare class SuperDocRuntime {
9
18
  private readonly transport;
@@ -1,19 +1,47 @@
1
1
  import { HostTransport } from './host.js';
2
2
  import { resolveEmbeddedCliBinary } from './embedded-cli.js';
3
+ import { resolveEmbeddedDocumentHostBinary } from './embedded-document-host.js';
3
4
  import { writeSdkDebugTrace } from './debug-trace.js';
5
+ import { SuperDocCliError } from './errors.js';
6
+ export function resolveRuntimeProcess(options = {}, embedded = {
7
+ cli: resolveEmbeddedCliBinary,
8
+ documentHost: resolveEmbeddedDocumentHostBinary,
9
+ }) {
10
+ const configuredDocumentHostPath = options.documentHostPath;
11
+ if (configuredDocumentHostPath !== undefined && configuredDocumentHostPath.trim().length === 0) {
12
+ throw new SuperDocCliError('documentHostPath must be a non-empty path.', {
13
+ code: 'INVALID_ARGUMENT',
14
+ });
15
+ }
16
+ const configuredDocumentHostBin = options.env?.SUPERDOC_SDK_DOCUMENT_HOST_BIN ?? process.env.SUPERDOC_SDK_DOCUMENT_HOST_BIN;
17
+ const documentHostBin = configuredDocumentHostPath ??
18
+ (configuredDocumentHostBin === 'embedded' ? embedded.documentHost() : configuredDocumentHostBin);
19
+ if (documentHostBin !== undefined && documentHostBin.trim().length === 0) {
20
+ throw new SuperDocCliError('SUPERDOC_SDK_DOCUMENT_HOST_BIN must be a non-empty path.', {
21
+ code: 'INVALID_ARGUMENT',
22
+ });
23
+ }
24
+ if (documentHostBin !== undefined) {
25
+ return { hostBin: documentHostBin, processMode: 'document' };
26
+ }
27
+ return {
28
+ hostBin: options.env?.SUPERDOC_CLI_BIN ?? process.env.SUPERDOC_CLI_BIN ?? embedded.cli(),
29
+ processMode: 'cli',
30
+ };
31
+ }
4
32
  /**
5
- * Internal runtime that delegates CLI invocations to a persistent host transport.
33
+ * Internal runtime that delegates operations to a persistent host transport.
6
34
  *
7
- * Resolves the CLI binary and creates a {@link HostTransport} that communicates
8
- * with a long-lived `superdoc host --stdio` process.
35
+ * Uses the standalone document host when explicitly configured. Otherwise it
36
+ * preserves the embedded CLI compatibility path.
9
37
  */
10
38
  export class SuperDocRuntime {
11
39
  transport;
12
40
  constructor(options = {}) {
13
- const cliBin = options.env?.SUPERDOC_CLI_BIN ?? process.env.SUPERDOC_CLI_BIN ?? resolveEmbeddedCliBinary();
41
+ const runtimeProcess = resolveRuntimeProcess(options);
14
42
  this.transport = new HostTransport({
15
- cliBin,
16
43
  ...options,
44
+ ...runtimeProcess,
17
45
  });
18
46
  }
19
47
  async connect() {
@@ -163,5 +163,6 @@ function buildOperationArgv(operation, params, options, runtimeTimeoutMs, defaul
163
163
  return argv;
164
164
  }
165
165
 
166
+ exports.applyOperationParamAliases = applyOperationParamAliases;
166
167
  exports.buildOperationArgv = buildOperationArgv;
167
168
  exports.resolveInvocation = resolveInvocation;
@@ -34,6 +34,14 @@ export interface UserIdentity {
34
34
  }
35
35
  export interface SuperDocClientOptions {
36
36
  env?: Record<string, string | undefined>;
37
+ /**
38
+ * Standalone document-host entry to use instead of the embedded CLI.
39
+ *
40
+ * The host-only path supports local lifecycle methods and generated Document
41
+ * API operations through structured RPC. Interface-only conveniences fail
42
+ * without CLI fallback.
43
+ */
44
+ documentHostPath?: string;
37
45
  /**
38
46
  * Default document engine for `client.open(...)`. Explicit `runtime` on an
39
47
  * individual open call takes precedence.
@@ -42,11 +50,13 @@ export interface SuperDocClientOptions {
42
50
  startupTimeoutMs?: number;
43
51
  shutdownTimeoutMs?: number;
44
52
  /**
45
- * Upper bound (ms) on how long the host process may spend on a single
46
- * `cli.invoke` request before it kills the operation and returns a
47
- * `RequestTimeout` error. Propagated to the host via `--request-timeout-ms`
48
- * at spawn. Raise this for documents that legitimately need more than 30s
49
- * to process; the SDK widens its own JSON-RPC watchdog to match.
53
+ * Upper bound (ms) on how long the host process may spend on a request
54
+ * before it returns a `RequestTimeout` error. Retry-safe structured document
55
+ * requests carry this budget as transport metadata; requests that may apply
56
+ * non-idempotent side effects await a definitive result instead. CLI-host
57
+ * requests also receive the budget through `--request-timeout-ms` at spawn.
58
+ * Raise this for documents that legitimately need more than 30s to process;
59
+ * the SDK widens its own JSON-RPC watchdog to match.
50
60
  *
51
61
  * Defaults to the host's own default (30s) when unset.
52
62
  */
@@ -54,11 +64,13 @@ export interface SuperDocClientOptions {
54
64
  /**
55
65
  * JS-side watchdog (ms) the SDK waits for a host reply before giving up.
56
66
  * Independent of {@link requestTimeoutMs} (which controls the host-side
57
- * operation budget). Most callers should leave this at its default and use
58
- * {@link requestTimeoutMs} as the single operation-timeout knob
59
- * `resolveWatchdogTimeout` already widens the watchdog above the host
60
- * ceiling automatically. Override only when you need to detect a hung or
61
- * crashed host faster than the operation budget allows.
67
+ * operation budget). Structured requests that may apply non-idempotent side
68
+ * effects wait for a definitive host result instead of applying an unsafe
69
+ * response deadline. Most callers should leave this at its default and use
70
+ * {@link requestTimeoutMs} as the single timeout knob for retry-safe work
71
+ * `resolveWatchdogTimeout` already widens the watchdog above the host ceiling
72
+ * automatically. Override only when you need to detect a hung or crashed host
73
+ * faster than the operation budget allows.
62
74
  */
63
75
  watchdogTimeoutMs?: number;
64
76
  maxQueueDepth?: number;
@@ -70,6 +82,12 @@ export interface CliInvocation {
70
82
  prefixArgs: string[];
71
83
  }
72
84
  export declare function resolveInvocation(cliBin: string): CliInvocation;
85
+ /**
86
+ * Resolve contract-shaped param aliases onto their canonical generated names.
87
+ * Returns the same object when no alias applies. Throws on a conflicting
88
+ * canonical/alias pair so divergent values can never be silently coalesced.
89
+ */
90
+ export declare function applyOperationParamAliases(operation: OperationSpec, params: Record<string, unknown>): Record<string, unknown>;
73
91
  /**
74
92
  * Build the CLI argument vector for an operation invocation.
75
93
  *
@@ -54,7 +54,7 @@ function assertKnownTemplatesApplyParams(operation, params) {
54
54
  * Returns the same object when no alias applies. Throws on a conflicting
55
55
  * canonical/alias pair so divergent values can never be silently coalesced.
56
56
  */
57
- function applyOperationParamAliases(operation, params) {
57
+ export function applyOperationParamAliases(operation, params) {
58
58
  const aliasMap = OPERATION_PARAM_ALIASES[operation.operationId];
59
59
  if (!aliasMap)
60
60
  return params;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc/sdk",
3
- "version": "2.5.0",
3
+ "version": "2.7.0",
4
4
  "description": "Node SDK for SuperDoc, wrapping the SuperDoc CLI to read and edit .docx files from JavaScript and TypeScript.",
5
5
  "private": false,
6
6
  "license": "AGPL-3.0",
@@ -37,11 +37,11 @@
37
37
  "typescript": "^5.9.2"
38
38
  },
39
39
  "optionalDependencies": {
40
- "@superdoc/sdk-darwin-arm64": "2.5.0",
41
- "@superdoc/sdk-linux-x64": "2.5.0",
42
- "@superdoc/sdk-linux-arm64": "2.5.0",
43
- "@superdoc/sdk-darwin-x64": "2.5.0",
44
- "@superdoc/sdk-windows-x64": "2.5.0"
40
+ "@superdoc/sdk-darwin-arm64": "2.7.0",
41
+ "@superdoc/sdk-darwin-x64": "2.7.0",
42
+ "@superdoc/sdk-linux-x64": "2.7.0",
43
+ "@superdoc/sdk-linux-arm64": "2.7.0",
44
+ "@superdoc/sdk-windows-x64": "2.7.0"
45
45
  },
46
46
  "publishConfig": {
47
47
  "access": "public"
@@ -50,6 +50,7 @@
50
50
  "build": "rm -rf dist && node scripts/embed-prompts.mjs && node scripts/embed-tools.mjs && tsc && rollup -c rollup.cjs.config.mjs && rm -rf dist/prompts && mkdir -p dist/prompts && cp src/prompts/*.md dist/prompts/ && pnpm run audit:publish",
51
51
  "audit:publish": "node ../../../../scripts/audit-publish-artifact.mjs dist --label sdk-node-dist",
52
52
  "typecheck": "tsc --noEmit",
53
+ "test:document-host": "bun test src/runtime/__tests__/host-spawn-args.test.ts src/runtime/__tests__/document-rpc.test.ts src/__tests__/structured-document-rpc.e2e.test.ts src/__tests__/request-timeout-ms.e2e.test.ts",
53
54
  "smoke:product-action": "node scripts/product-action-smoke.mjs"
54
55
  }
55
56
  }
@@ -5,7 +5,7 @@
5
5
  "tools": [
6
6
  {
7
7
  "toolName": "superdoc_get_content",
8
- "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \"blocks\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \"blocks\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \"text\", \"markdown\", and \"html\" return compact strings. Actions \"markdown_projection\" and \"html_projection\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \"info\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \"blocks\" action supports pagination via \"offset\" and \"limit\", and filtering via \"nodeTypes\". Other actions ignore these parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\n\nEXAMPLES:\n 1. {\"action\":\"blocks\"}\n 2. {\"action\":\"blocks\",\"includeText\":true,\"offset\":0,\"limit\":20}\n 3. {\"action\":\"blocks\",\"offset\":0,\"limit\":20,\"nodeTypes\":[\"heading\",\"paragraph\"]}\n 4. {\"action\":\"text\"}\n 5. {\"action\":\"info\"}\n 6. {\"action\":\"html_projection\",\"reviewMode\":\"redline\",\"includeSourceMap\":true}\n 7. {\"action\":\"markdown_projection\",\"reviewMode\":\"original\",\"scope\":{\"kind\":\"block\",\"nodeType\":\"paragraph\",\"nodeId\":\"paragraph-id\"},\"includeSourceMap\":true}",
8
+ "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \"blocks\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \"blocks\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \"text\", \"markdown\", and \"html\" return compact strings. Actions \"markdown_projection\" and \"html_projection\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \"info\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \"blocks\" action supports pagination via \"offset\" and \"limit\", filtering via \"nodeTypes\", and numbering projection via \"reviewMode\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\n\nEXAMPLES:\n 1. {\"action\":\"blocks\"}\n 2. {\"action\":\"blocks\",\"includeText\":true,\"offset\":0,\"limit\":20,\"reviewMode\":\"final\"}\n 3. {\"action\":\"blocks\",\"offset\":0,\"limit\":20,\"nodeTypes\":[\"heading\",\"paragraph\"]}\n 4. {\"action\":\"text\"}\n 5. {\"action\":\"info\"}\n 6. {\"action\":\"html_projection\",\"reviewMode\":\"redline\",\"includeSourceMap\":true}\n 7. {\"action\":\"markdown_projection\",\"reviewMode\":\"original\",\"scope\":{\"kind\":\"block\",\"nodeType\":\"paragraph\",\"nodeId\":\"paragraph-id\"},\"includeSourceMap\":true}",
9
9
  "inputSchema": {
10
10
  "type": "object",
11
11
  "properties": {
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "reviewMode": {
35
35
  "$ref": "#/$defs/SDProjectionReviewMode",
36
- "description": "Only for actions 'markdown_projection', 'html_projection'. Omit for other actions."
36
+ "description": "Only for actions 'markdown_projection', 'html_projection', 'blocks'. Omit for other actions."
37
37
  },
38
38
  "scope": {
39
39
  "$ref": "#/$defs/SDProjectionScope",
@@ -43,5 +43,5 @@
43
43
  "mutates": true
44
44
  }
45
45
  ],
46
- "contractHash": "bfefe3880487f715579f3ba1bf11d94dd1af4151131b780a7d26dae65ca8c38e"
46
+ "contractHash": "570f35bff614f42af4610ad37ad4d21d776cdf8e3515c206d25d50c0102d7484"
47
47
  }
@@ -3,7 +3,7 @@
3
3
  "tools": [
4
4
  {
5
5
  "name": "superdoc_get_content",
6
- "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \"blocks\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \"blocks\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \"text\", \"markdown\", and \"html\" return compact strings. Actions \"markdown_projection\" and \"html_projection\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \"info\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \"blocks\" action supports pagination via \"offset\" and \"limit\", and filtering via \"nodeTypes\". Other actions ignore these parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\n\nEXAMPLES:\n 1. {\"action\":\"blocks\"}\n 2. {\"action\":\"blocks\",\"includeText\":true,\"offset\":0,\"limit\":20}\n 3. {\"action\":\"blocks\",\"offset\":0,\"limit\":20,\"nodeTypes\":[\"heading\",\"paragraph\"]}\n 4. {\"action\":\"text\"}\n 5. {\"action\":\"info\"}\n 6. {\"action\":\"html_projection\",\"reviewMode\":\"redline\",\"includeSourceMap\":true}\n 7. {\"action\":\"markdown_projection\",\"reviewMode\":\"original\",\"scope\":{\"kind\":\"block\",\"nodeType\":\"paragraph\",\"nodeId\":\"paragraph-id\"},\"includeSourceMap\":true}",
6
+ "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \"blocks\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \"blocks\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \"text\", \"markdown\", and \"html\" return compact strings. Actions \"markdown_projection\" and \"html_projection\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \"info\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \"blocks\" action supports pagination via \"offset\" and \"limit\", filtering via \"nodeTypes\", and numbering projection via \"reviewMode\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\n\nEXAMPLES:\n 1. {\"action\":\"blocks\"}\n 2. {\"action\":\"blocks\",\"includeText\":true,\"offset\":0,\"limit\":20,\"reviewMode\":\"final\"}\n 3. {\"action\":\"blocks\",\"offset\":0,\"limit\":20,\"nodeTypes\":[\"heading\",\"paragraph\"]}\n 4. {\"action\":\"text\"}\n 5. {\"action\":\"info\"}\n 6. {\"action\":\"html_projection\",\"reviewMode\":\"redline\",\"includeSourceMap\":true}\n 7. {\"action\":\"markdown_projection\",\"reviewMode\":\"original\",\"scope\":{\"kind\":\"block\",\"nodeType\":\"paragraph\",\"nodeId\":\"paragraph-id\"},\"includeSourceMap\":true}",
7
7
  "input_schema": {
8
8
  "type": "object",
9
9
  "properties": {
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "reviewMode": {
33
33
  "$ref": "#/$defs/SDProjectionReviewMode",
34
- "description": "Only for actions 'markdown_projection', 'html_projection'. Omit for other actions."
34
+ "description": "Only for actions 'markdown_projection', 'html_projection', 'blocks'. Omit for other actions."
35
35
  },
36
36
  "scope": {
37
37
  "$ref": "#/$defs/SDProjectionScope",
@@ -3,7 +3,7 @@
3
3
  "tools": [
4
4
  {
5
5
  "name": "superdoc_get_content",
6
- "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \"blocks\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \"blocks\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \"text\", \"markdown\", and \"html\" return compact strings. Actions \"markdown_projection\" and \"html_projection\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \"info\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \"blocks\" action supports pagination via \"offset\" and \"limit\", and filtering via \"nodeTypes\". Other actions ignore these parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\n\nEXAMPLES:\n 1. {\"action\":\"blocks\"}\n 2. {\"action\":\"blocks\",\"includeText\":true,\"offset\":0,\"limit\":20}\n 3. {\"action\":\"blocks\",\"offset\":0,\"limit\":20,\"nodeTypes\":[\"heading\",\"paragraph\"]}\n 4. {\"action\":\"text\"}\n 5. {\"action\":\"info\"}\n 6. {\"action\":\"html_projection\",\"reviewMode\":\"redline\",\"includeSourceMap\":true}\n 7. {\"action\":\"markdown_projection\",\"reviewMode\":\"original\",\"scope\":{\"kind\":\"block\",\"nodeType\":\"paragraph\",\"nodeId\":\"paragraph-id\"},\"includeSourceMap\":true}",
6
+ "description": "Read document content in various formats. Call this first in any workflow to understand document structure before making edits. Action \"blocks\" returns structured block data with nodeId, nodeType, textPreview, optional full text when includeText:true, formatting properties (fontFamily, fontSize, color, bold, underline, alignment), and ref handles for immediate use with superdoc_edit or superdoc_format. Its optional reviewMode selects final, original, or redline numbering metadata without filtering blocks. When you need to evaluate or rewrite existing paragraphs or clauses, prefer action \"blocks\" with includeText:true so you can identify the correct block and then target it by nodeId. Actions \"text\", \"markdown\", and \"html\" return compact strings. Actions \"markdown_projection\" and \"html_projection\" return detailed async projections with review modes, scopes, diagnostics, annotations, block maps, and optional source maps. Projection source targets use tracked coordinates and are valid only at evaluatedRevision; after any mutation, reproject before using them. Treat omitted or partially emitted annotations according to their status instead of guessing an anchor from rendered text. Action \"info\" returns document metadata: word count, paragraph count, page count, outline, available styles, and capability flags. The \"blocks\" action supports pagination via \"offset\" and \"limit\", filtering via \"nodeTypes\", and numbering projection via \"reviewMode\". Other actions ignore pagination and filtering parameters. This tool never modifies the document. Do NOT call superdoc_edit or superdoc_format without first reading blocks to get valid refs and formatting reference values.\n\nEXAMPLES:\n 1. {\"action\":\"blocks\"}\n 2. {\"action\":\"blocks\",\"includeText\":true,\"offset\":0,\"limit\":20,\"reviewMode\":\"final\"}\n 3. {\"action\":\"blocks\",\"offset\":0,\"limit\":20,\"nodeTypes\":[\"heading\",\"paragraph\"]}\n 4. {\"action\":\"text\"}\n 5. {\"action\":\"info\"}\n 6. {\"action\":\"html_projection\",\"reviewMode\":\"redline\",\"includeSourceMap\":true}\n 7. {\"action\":\"markdown_projection\",\"reviewMode\":\"original\",\"scope\":{\"kind\":\"block\",\"nodeType\":\"paragraph\",\"nodeId\":\"paragraph-id\"},\"includeSourceMap\":true}",
7
7
  "parameters": {
8
8
  "type": "object",
9
9
  "properties": {
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "reviewMode": {
33
33
  "$ref": "#/$defs/SDProjectionReviewMode",
34
- "description": "Only for actions 'markdown_projection', 'html_projection'. Omit for other actions."
34
+ "description": "Only for actions 'markdown_projection', 'html_projection', 'blocks'. Omit for other actions."
35
35
  },
36
36
  "scope": {
37
37
  "$ref": "#/$defs/SDProjectionScope",