@superdoc/sdk 2.9.1-next.2 → 2.10.0-next.10

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 (57) hide show
  1. package/README.md +45 -0
  2. package/dist/action-primitives/doc-index.cjs +6 -4
  3. package/dist/action-primitives/doc-index.js +6 -4
  4. package/dist/action-primitives/engine.cjs +6 -2
  5. package/dist/action-primitives/engine.js +6 -2
  6. package/dist/action-primitives/receipt.d.ts +4 -0
  7. package/dist/action-primitives/tools/structure-insert.d.ts +1 -1
  8. package/dist/agent/actions.cjs +384 -373
  9. package/dist/agent/actions.d.ts +5 -0
  10. package/dist/agent/actions.js +385 -374
  11. package/dist/agent/catalog.cjs +15 -0
  12. package/dist/agent/catalog.js +15 -0
  13. package/dist/agent/doc-snapshot.cjs +205 -86
  14. package/dist/agent/doc-snapshot.d.ts +11 -0
  15. package/dist/agent/doc-snapshot.js +203 -86
  16. package/dist/agent/execution-context.cjs +385 -0
  17. package/dist/agent/execution-context.d.ts +97 -0
  18. package/dist/agent/execution-context.js +376 -0
  19. package/dist/agent/runtime.cjs +123 -117
  20. package/dist/agent/runtime.d.ts +3 -0
  21. package/dist/agent/runtime.js +124 -118
  22. package/dist/agent/v2-preset-compat.cjs +5 -1
  23. package/dist/agent/v2-preset-compat.js +4 -1
  24. package/dist/embedded-tools.generated.cjs +5 -5
  25. package/dist/embedded-tools.generated.js +5 -5
  26. package/dist/generated/client.cjs +2 -0
  27. package/dist/generated/client.d.ts +85 -0
  28. package/dist/generated/client.js +2 -0
  29. package/dist/generated/contract.cjs +1796 -1297
  30. package/dist/generated/contract.js +1797 -1297
  31. package/dist/index.cjs +23 -0
  32. package/dist/index.d.ts +7 -2
  33. package/dist/index.js +23 -0
  34. package/dist/presets/core.cjs +1 -1
  35. package/dist/presets/core.js +1 -1
  36. package/dist/runtime/document-evidence.cjs +40 -0
  37. package/dist/runtime/document-evidence.d.ts +13 -0
  38. package/dist/runtime/document-evidence.js +30 -0
  39. package/dist/runtime/document-rpc.cjs +27 -0
  40. package/dist/runtime/document-rpc.d.ts +2 -0
  41. package/dist/runtime/document-rpc.js +25 -0
  42. package/dist/runtime/host.cjs +65 -4
  43. package/dist/runtime/host.d.ts +3 -0
  44. package/dist/runtime/host.js +66 -5
  45. package/dist/runtime/process.cjs +38 -0
  46. package/dist/runtime/process.d.ts +16 -0
  47. package/dist/runtime/process.js +38 -0
  48. package/dist/runtime/sdk-version.generated.cjs +1 -1
  49. package/dist/runtime/sdk-version.generated.d.ts +1 -1
  50. package/dist/runtime/sdk-version.generated.js +1 -1
  51. package/package.json +10 -8
  52. package/tools/catalog.json +89 -0
  53. package/tools/tools-policy.json +1 -1
  54. package/tools/tools.anthropic.json +89 -0
  55. package/tools/tools.generic.json +89 -0
  56. package/tools/tools.openai.json +89 -0
  57. package/tools/tools.vercel.json +89 -0
package/dist/index.cjs CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var documentEvidence = require('./runtime/document-evidence.cjs');
3
4
  var client = require('./generated/client.cjs');
4
5
  var contract = require('./generated/contract.cjs');
5
6
  var process = require('./runtime/process.cjs');
@@ -37,6 +38,23 @@ class BoundRuntime {
37
38
  }
38
39
  return this.runtime.invoke(operation, { ...params, sessionId: this.sessionId }, options);
39
40
  }
41
+ async replaceFile(source, options = {}) {
42
+ if (this.closed) {
43
+ throw new errors.SuperDocCliError('Document handle is closed; cannot replace its file.', {
44
+ code: 'DOCUMENT_CLOSED',
45
+ details: { sessionId: this.sessionId },
46
+ });
47
+ }
48
+ return this.runtime.replaceFile(this.sessionId, source, options);
49
+ }
50
+ supportsDocumentFacts() {
51
+ return !this.closed && this.runtime.supportsDocumentFacts(this.sessionId);
52
+ }
53
+ async readRevision() {
54
+ if (this.closed)
55
+ throw new errors.SuperDocCliError('Document handle is closed; cannot read its revision.', { code: 'DOCUMENT_CLOSED' });
56
+ return this.runtime.readDocumentRevision(this.sessionId);
57
+ }
40
58
  markClosed() {
41
59
  this.closed = true;
42
60
  }
@@ -61,6 +79,8 @@ class SuperDocDocumentCore {
61
79
  this._openResult = openResult;
62
80
  this.client = client$1;
63
81
  attachBoundDocApi(this, client.createBoundDocApi(this.boundRuntime));
82
+ documentEvidence.registerDocumentRevisionReader(this, () => this.boundRuntime.readRevision());
83
+ documentEvidence.registerDocumentFactCapability(this, () => this.boundRuntime.supportsDocumentFacts());
64
84
  }
65
85
  get sessionId() {
66
86
  return this._sessionId;
@@ -72,6 +92,9 @@ class SuperDocDocumentCore {
72
92
  async save(params = {}, options = {}) {
73
93
  return this.boundRuntime.invoke(contract.CONTRACT.operations['doc.save'], params, options);
74
94
  }
95
+ async replaceFile(source, options = {}) {
96
+ return this.boundRuntime.replaceFile(source, options);
97
+ }
75
98
  async close(params = {}, options = {}) {
76
99
  const result = await this.boundRuntime.invoke(contract.CONTRACT.operations['doc.close'], params, options);
77
100
  this.boundRuntime.markClosed();
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type BoundDocApi, type DocCloseBoundParams, type DocCloseResult, type DocFormatApplyBoundParams, type DocFormatApplyResult, type DocOpenParams as GeneratedDocOpenParams, type DocOpenResult, type DocSaveBoundParams, type DocSaveResult } from './generated/client.js';
2
- import { SuperDocRuntime, type SuperDocClientOptions, type DocOpenOptions, type InvokeOptions, type OperationSpec, type RuntimeInvoker } from './runtime/process.js';
2
+ import { SuperDocRuntime, type SuperDocClientOptions, type DocOpenOptions, type InvokeOptions, type OperationSpec, type ReplaceFileOptions, type ReplaceFileResult, type ReplaceFileSource, type RuntimeInvoker } from './runtime/process.js';
3
3
  /**
4
4
  * Wraps a raw runtime and injects a fixed sessionId into every invoke call.
5
5
  * Implements RuntimeInvoker so generated code can use it directly.
@@ -12,6 +12,9 @@ declare class BoundRuntime implements RuntimeInvoker {
12
12
  private closed;
13
13
  constructor(runtime: SuperDocRuntime, sessionId: string);
14
14
  invoke<TData = unknown>(operation: OperationSpec, params?: Record<string, unknown>, options?: InvokeOptions): Promise<TData>;
15
+ replaceFile(source: ReplaceFileSource, options?: ReplaceFileOptions): Promise<ReplaceFileResult>;
16
+ supportsDocumentFacts(): boolean;
17
+ readRevision(): Promise<string | undefined>;
15
18
  markClosed(): void;
16
19
  }
17
20
  export interface DocFormatRangeBoundParams extends Omit<DocFormatApplyBoundParams, 'inline'> {
@@ -33,6 +36,7 @@ declare class SuperDocDocumentCore {
33
36
  /** Read-only snapshot of the initial doc.open response metadata. */
34
37
  get openResult(): DocOpenResult;
35
38
  save(params?: DocSaveBoundParams, options?: InvokeOptions): Promise<DocSaveResult>;
39
+ replaceFile(source: ReplaceFileSource, options?: ReplaceFileOptions): Promise<ReplaceFileResult>;
36
40
  close(params?: DocCloseBoundParams, options?: InvokeOptions): Promise<DocCloseResult>;
37
41
  /** @internal */
38
42
  markClosed(): void;
@@ -42,6 +46,7 @@ type SuperDocDocumentInstance = SuperDocDocumentCore & BoundDocApi;
42
46
  export declare const SuperDocDocument: new (boundRuntime: BoundRuntime, sessionId: string, openResult: DocOpenResult, client: SuperDocClient) => SuperDocDocumentInstance;
43
47
  export type SuperDocDocument = SuperDocDocumentInstance;
44
48
  export type DocOpenParams = GeneratedDocOpenParams;
49
+ export type { ReplaceFileOptions, ReplaceFileResult, ReplaceFileSource };
45
50
  export interface DocDescribeCommandParams {
46
51
  operationId: string;
47
52
  [key: string]: unknown;
@@ -89,7 +94,7 @@ export { dispatchIntentTool } from './generated/intent-dispatch.generated.js';
89
94
  export { wrapV2PresetCompat } from './agent/v2-preset-compat.js';
90
95
  export type { BoundDocApi } from './generated/client.js';
91
96
  export type { ActionName } from './agent/actions.js';
92
- export type { AgentReceipt } from './agent/runtime.js';
97
+ export type { AgentReceipt, AgentApplyArgs, AgentVerifyArgs } from './agent/runtime.js';
93
98
  export type { GetSystemPromptOptions, GetToolsOptions, GetToolsResult, PresetDescriptor } from './presets.js';
94
99
  export { createAgentToolkit } from './tools.js';
95
100
  export type { AgentToolkit, CreateAgentToolkitInput } from './tools.js';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { registerDocumentRevisionReader, registerDocumentFactCapability } from './runtime/document-evidence.js';
1
2
  import { createDocApi, createBoundDocApi, } from './generated/client.js';
2
3
  import { CONTRACT } from './generated/contract.js';
3
4
  import { SuperDocRuntime, } from './runtime/process.js';
@@ -29,6 +30,23 @@ class BoundRuntime {
29
30
  }
30
31
  return this.runtime.invoke(operation, { ...params, sessionId: this.sessionId }, options);
31
32
  }
33
+ async replaceFile(source, options = {}) {
34
+ if (this.closed) {
35
+ throw new SuperDocCliError('Document handle is closed; cannot replace its file.', {
36
+ code: 'DOCUMENT_CLOSED',
37
+ details: { sessionId: this.sessionId },
38
+ });
39
+ }
40
+ return this.runtime.replaceFile(this.sessionId, source, options);
41
+ }
42
+ supportsDocumentFacts() {
43
+ return !this.closed && this.runtime.supportsDocumentFacts(this.sessionId);
44
+ }
45
+ async readRevision() {
46
+ if (this.closed)
47
+ throw new SuperDocCliError('Document handle is closed; cannot read its revision.', { code: 'DOCUMENT_CLOSED' });
48
+ return this.runtime.readDocumentRevision(this.sessionId);
49
+ }
32
50
  markClosed() {
33
51
  this.closed = true;
34
52
  }
@@ -53,6 +71,8 @@ class SuperDocDocumentCore {
53
71
  this._openResult = openResult;
54
72
  this.client = client;
55
73
  attachBoundDocApi(this, createBoundDocApi(this.boundRuntime));
74
+ registerDocumentRevisionReader(this, () => this.boundRuntime.readRevision());
75
+ registerDocumentFactCapability(this, () => this.boundRuntime.supportsDocumentFacts());
56
76
  }
57
77
  get sessionId() {
58
78
  return this._sessionId;
@@ -64,6 +84,9 @@ class SuperDocDocumentCore {
64
84
  async save(params = {}, options = {}) {
65
85
  return this.boundRuntime.invoke(CONTRACT.operations['doc.save'], params, options);
66
86
  }
87
+ async replaceFile(source, options = {}) {
88
+ return this.boundRuntime.replaceFile(source, options);
89
+ }
67
90
  async close(params = {}, options = {}) {
68
91
  const result = await this.boundRuntime.invoke(CONTRACT.operations['doc.close'], params, options);
69
92
  this.boundRuntime.markClosed();
@@ -245,7 +245,7 @@ function compactAgentReceipt(receipt) {
245
245
  : {}),
246
246
  ...(verification
247
247
  ? {
248
- verificationPassed: verification.every((entry) => entry.passed),
248
+ verificationPassed: verification.length > 0 && verification.every((entry) => entry.passed),
249
249
  verification: verification.map((entry) => ({
250
250
  check: pickScalarFields(entry.check, undefined, 6),
251
251
  passed: entry.passed,
@@ -241,7 +241,7 @@ function compactAgentReceipt(receipt) {
241
241
  : {}),
242
242
  ...(verification
243
243
  ? {
244
- verificationPassed: verification.every((entry) => entry.passed),
244
+ verificationPassed: verification.length > 0 && verification.every((entry) => entry.passed),
245
245
  verification: verification.map((entry) => ({
246
246
  check: pickScalarFields(entry.check, undefined, 6),
247
247
  passed: entry.passed,
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ const mutationRevisions = new WeakMap();
4
+ function registerMutationRevision(result, revision) {
5
+ if (result && typeof result === 'object')
6
+ mutationRevisions.set(result, revision);
7
+ }
8
+ function getMutationRevision(result) {
9
+ return result && typeof result === 'object' ? mutationRevisions.get(result) : undefined;
10
+ }
11
+ // Keep transport capabilities off the public document API and tied to the live
12
+ // handle. Undefined means unsupported, never a cached or unknown revision.
13
+ const revisionReaders = new WeakMap();
14
+ function registerDocumentRevisionReader(document, reader) {
15
+ revisionReaders.set(document, reader);
16
+ }
17
+ function getDocumentRevisionReader(document) {
18
+ return revisionReaders.get(document);
19
+ }
20
+ const factCapabilities = new WeakMap();
21
+ function registerDocumentFactCapability(document, supported) {
22
+ factCapabilities.set(document, supported);
23
+ }
24
+ function supportsDocumentFacts(document) {
25
+ return factCapabilities.get(document)?.() === true;
26
+ }
27
+ function forwardDocumentEvidence(source, target) {
28
+ const reader = getDocumentRevisionReader(source);
29
+ if (reader)
30
+ registerDocumentRevisionReader(target, reader);
31
+ registerDocumentFactCapability(target, () => supportsDocumentFacts(source));
32
+ }
33
+
34
+ exports.forwardDocumentEvidence = forwardDocumentEvidence;
35
+ exports.getDocumentRevisionReader = getDocumentRevisionReader;
36
+ exports.getMutationRevision = getMutationRevision;
37
+ exports.registerDocumentFactCapability = registerDocumentFactCapability;
38
+ exports.registerDocumentRevisionReader = registerDocumentRevisionReader;
39
+ exports.registerMutationRevision = registerMutationRevision;
40
+ exports.supportsDocumentFacts = supportsDocumentFacts;
@@ -0,0 +1,13 @@
1
+ type RevisionReader = () => Promise<string | undefined>;
2
+ type MutationRevision = {
3
+ before: string;
4
+ after: string;
5
+ };
6
+ export declare function registerMutationRevision(result: unknown, revision: MutationRevision): void;
7
+ export declare function getMutationRevision(result: unknown): MutationRevision | undefined;
8
+ export declare function registerDocumentRevisionReader(document: object, reader: RevisionReader): void;
9
+ export declare function getDocumentRevisionReader(document: object): RevisionReader | undefined;
10
+ export declare function registerDocumentFactCapability(document: object, supported: () => boolean): void;
11
+ export declare function supportsDocumentFacts(document: object): boolean;
12
+ export declare function forwardDocumentEvidence(source: object, target: object): void;
13
+ export {};
@@ -0,0 +1,30 @@
1
+ const mutationRevisions = new WeakMap();
2
+ export function registerMutationRevision(result, revision) {
3
+ if (result && typeof result === 'object')
4
+ mutationRevisions.set(result, revision);
5
+ }
6
+ export function getMutationRevision(result) {
7
+ return result && typeof result === 'object' ? mutationRevisions.get(result) : undefined;
8
+ }
9
+ // Keep transport capabilities off the public document API and tied to the live
10
+ // handle. Undefined means unsupported, never a cached or unknown revision.
11
+ const revisionReaders = new WeakMap();
12
+ export function registerDocumentRevisionReader(document, reader) {
13
+ revisionReaders.set(document, reader);
14
+ }
15
+ export function getDocumentRevisionReader(document) {
16
+ return revisionReaders.get(document);
17
+ }
18
+ const factCapabilities = new WeakMap();
19
+ export function registerDocumentFactCapability(document, supported) {
20
+ factCapabilities.set(document, supported);
21
+ }
22
+ export function supportsDocumentFacts(document) {
23
+ return factCapabilities.get(document)?.() === true;
24
+ }
25
+ export function forwardDocumentEvidence(source, target) {
26
+ const reader = getDocumentRevisionReader(source);
27
+ if (reader)
28
+ registerDocumentRevisionReader(target, reader);
29
+ registerDocumentFactCapability(target, () => supportsDocumentFacts(source));
30
+ }
@@ -5,6 +5,7 @@ var errors = require('./errors.cjs');
5
5
  var transportCommon = require('./transport-common.cjs');
6
6
 
7
7
  const DOCUMENT_RPC_FEATURES = ['document.open', 'document.invoke', 'document.save', 'document.close'];
8
+ const DOCUMENT_RPC_REPLACE_FILE_FEATURE = 'document.replaceFile';
8
9
  const DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE = 'document.open.defaultChangeMode';
9
10
  const DOCUMENT_RPC_SOURCE_SAVE_FEATURE = 'document.save.source';
10
11
  const DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE = 'host.request.timeoutMs';
@@ -103,6 +104,30 @@ function mapDocumentOpenResult(response, sessionId, path) {
103
104
  dirty: false,
104
105
  };
105
106
  }
107
+ function mapDocumentReplaceFileResult(response, sessionId) {
108
+ if (typeof response !== 'object' || response == null || Array.isArray(response)) {
109
+ throw new errors.SuperDocCliError('Host returned invalid document.replaceFile result.', {
110
+ code: 'HOST_PROTOCOL_ERROR',
111
+ details: { result: response },
112
+ });
113
+ }
114
+ const record = response;
115
+ if (record.sessionId !== sessionId ||
116
+ record.replaced !== true ||
117
+ typeof record.byteLength !== 'number' ||
118
+ typeof record.revision !== 'string') {
119
+ throw new errors.SuperDocCliError('Host returned invalid document.replaceFile session metadata.', {
120
+ code: 'HOST_PROTOCOL_ERROR',
121
+ details: { expectedSessionId: sessionId, result: response },
122
+ });
123
+ }
124
+ return {
125
+ contextId: sessionId,
126
+ runtime: 'v2',
127
+ replaced: true,
128
+ document: { byteLength: record.byteLength, revision: record.revision },
129
+ };
130
+ }
106
131
  function supportsTrackedMode(operation) {
107
132
  return operation.supportsTrackedMode === true || operation.supportsConditionalTrackedMode === true;
108
133
  }
@@ -316,6 +341,7 @@ function mapDocumentLifecycleResult(operationId, response, sessionId, expectedIn
316
341
 
317
342
  exports.DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE = DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE;
318
343
  exports.DOCUMENT_RPC_FEATURES = DOCUMENT_RPC_FEATURES;
344
+ exports.DOCUMENT_RPC_REPLACE_FILE_FEATURE = DOCUMENT_RPC_REPLACE_FILE_FEATURE;
319
345
  exports.DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE = DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE;
320
346
  exports.DOCUMENT_RPC_SOURCE_SAVE_FEATURE = DOCUMENT_RPC_SOURCE_SAVE_FEATURE;
321
347
  exports.buildDocumentInvokeParams = buildDocumentInvokeParams;
@@ -325,4 +351,5 @@ exports.documentRpcEnabled = documentRpcEnabled;
325
351
  exports.documentRpcRequestSupportsResponseTimeout = documentRpcRequestSupportsResponseTimeout;
326
352
  exports.mapDocumentLifecycleResult = mapDocumentLifecycleResult;
327
353
  exports.mapDocumentOpenResult = mapDocumentOpenResult;
354
+ exports.mapDocumentReplaceFileResult = mapDocumentReplaceFileResult;
328
355
  exports.supportsDocumentRpc = supportsDocumentRpc;
@@ -1,5 +1,6 @@
1
1
  import { type ChangeMode, type InvokeOptions, type OperationSpec, type UserIdentity } from './transport-common.js';
2
2
  export declare const DOCUMENT_RPC_FEATURES: readonly ["document.open", "document.invoke", "document.save", "document.close"];
3
+ export declare const DOCUMENT_RPC_REPLACE_FILE_FEATURE = "document.replaceFile";
3
4
  export declare const DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE = "document.open.defaultChangeMode";
4
5
  export declare const DOCUMENT_RPC_SOURCE_SAVE_FEATURE = "document.save.source";
5
6
  export declare const DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE = "host.request.timeoutMs";
@@ -15,6 +16,7 @@ export declare function buildDocumentOpenParams(params: Record<string, unknown>,
15
16
  readonly params: Record<string, unknown>;
16
17
  };
17
18
  export declare function mapDocumentOpenResult(response: unknown, sessionId: string, path: string): Record<string, unknown>;
19
+ export declare function mapDocumentReplaceFileResult(response: unknown, sessionId: string): Record<string, unknown>;
18
20
  export declare function buildDocumentInvokeParams(sessionId: string, operation: OperationSpec, params: Record<string, unknown>, options: InvokeOptions, hostFeatures?: ReadonlySet<string>): {
19
21
  readonly method: string;
20
22
  readonly params: Record<string, unknown>;
@@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto';
2
2
  import { SuperDocCliError } from './errors.js';
3
3
  import { applyOperationParamAliases, } from './transport-common.js';
4
4
  export const DOCUMENT_RPC_FEATURES = ['document.open', 'document.invoke', 'document.save', 'document.close'];
5
+ export const DOCUMENT_RPC_REPLACE_FILE_FEATURE = 'document.replaceFile';
5
6
  export const DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE = 'document.open.defaultChangeMode';
6
7
  export const DOCUMENT_RPC_SOURCE_SAVE_FEATURE = 'document.save.source';
7
8
  export const DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE = 'host.request.timeoutMs';
@@ -100,6 +101,30 @@ export function mapDocumentOpenResult(response, sessionId, path) {
100
101
  dirty: false,
101
102
  };
102
103
  }
104
+ export function mapDocumentReplaceFileResult(response, sessionId) {
105
+ if (typeof response !== 'object' || response == null || Array.isArray(response)) {
106
+ throw new SuperDocCliError('Host returned invalid document.replaceFile result.', {
107
+ code: 'HOST_PROTOCOL_ERROR',
108
+ details: { result: response },
109
+ });
110
+ }
111
+ const record = response;
112
+ if (record.sessionId !== sessionId ||
113
+ record.replaced !== true ||
114
+ typeof record.byteLength !== 'number' ||
115
+ typeof record.revision !== 'string') {
116
+ throw new SuperDocCliError('Host returned invalid document.replaceFile session metadata.', {
117
+ code: 'HOST_PROTOCOL_ERROR',
118
+ details: { expectedSessionId: sessionId, result: response },
119
+ });
120
+ }
121
+ return {
122
+ contextId: sessionId,
123
+ runtime: 'v2',
124
+ replaced: true,
125
+ document: { byteLength: record.byteLength, revision: record.revision },
126
+ };
127
+ }
103
128
  function supportsTrackedMode(operation) {
104
129
  return operation.supportsTrackedMode === true || operation.supportsConditionalTrackedMode === true;
105
130
  }
@@ -4,6 +4,7 @@ var node_child_process = require('node:child_process');
4
4
  var node_readline = require('node:readline');
5
5
  var transportCommon = require('./transport-common.cjs');
6
6
  var errors = require('./errors.cjs');
7
+ var documentEvidence = require('./document-evidence.cjs');
7
8
  var documentRpc = require('./document-rpc.cjs');
8
9
 
9
10
  const HOST_PROTOCOL_VERSION = '1.0';
@@ -130,6 +131,28 @@ class HostTransport {
130
131
  async connect() {
131
132
  await this.runWhileActive(() => this.ensureConnected());
132
133
  }
134
+ supportsDocumentFacts(sessionId) {
135
+ return (this.documentRpcSessions.has(sessionId) &&
136
+ this.hostFeatures.has('document.currentRevision') &&
137
+ this.hostFeatures.has('document.executionFacts') &&
138
+ this.hostFeatures.has('document.mutationRevision'));
139
+ }
140
+ async readDocumentRevision(sessionId) {
141
+ return this.runWhileActive(async () => {
142
+ if (!this.documentRpcSessions.has(sessionId) || !this.hostFeatures.has('document.currentRevision')) {
143
+ return undefined;
144
+ }
145
+ await this.ensureConnected();
146
+ const revision = await this.sendJsonRpcRequest('document.currentRevision', { sessionId }, this.resolveWatchdogTimeout(undefined), { requestTimeoutMs: this.resolveDocumentRequestTimeout(undefined) });
147
+ if (typeof revision !== 'string' || revision.trim().length === 0 || revision === 'unknown') {
148
+ throw new errors.SuperDocCliError('Document host returned an invalid current revision.', {
149
+ code: 'HOST_PROTOCOL_ERROR',
150
+ details: { sessionId },
151
+ });
152
+ }
153
+ return revision;
154
+ });
155
+ }
133
156
  async dispose() {
134
157
  if (this.disposePromise)
135
158
  return this.disposePromise;
@@ -187,6 +210,34 @@ class HostTransport {
187
210
  async invoke(operation, params = {}, options = {}) {
188
211
  return this.runWhileActive(() => this.invokeWhileActive(operation, params, options));
189
212
  }
213
+ async replaceFile(sessionId, path, options = {}) {
214
+ return this.runWhileActive(async () => {
215
+ await this.ensureConnected();
216
+ if (!this.hostFeatures.has(documentRpc.DOCUMENT_RPC_REPLACE_FILE_FEATURE)) {
217
+ throw new errors.SuperDocCliError('The connected host does not support document replacement.', {
218
+ code: 'CAPABILITY_UNSUPPORTED',
219
+ details: { feature: documentRpc.DOCUMENT_RPC_REPLACE_FILE_FEATURE },
220
+ });
221
+ }
222
+ const tracksDirectSession = this.documentRpcSessions.has(sessionId);
223
+ if (tracksDirectSession)
224
+ this.beginDocumentRpcMutation(sessionId);
225
+ try {
226
+ const response = await this.sendJsonRpcRequest(documentRpc.DOCUMENT_RPC_REPLACE_FILE_FEATURE, { sessionId, path }, this.resolveWatchdogTimeout(options.timeoutMs));
227
+ const result = documentRpc.mapDocumentReplaceFileResult(response, sessionId);
228
+ if (tracksDirectSession)
229
+ this.settleDocumentRpcMutation(sessionId, 'applied');
230
+ return result;
231
+ }
232
+ catch (error) {
233
+ if (tracksDirectSession) {
234
+ const indeterminate = error instanceof errors.SuperDocCliError && ['TIMEOUT', 'HOST_TIMEOUT', 'HOST_DISCONNECTED'].includes(error.code);
235
+ this.settleDocumentRpcMutation(sessionId, indeterminate ? 'indeterminate' : 'not-applied');
236
+ }
237
+ throw error;
238
+ }
239
+ });
240
+ }
190
241
  async invokeWhileActive(operation, params, options) {
191
242
  const collaborationAuth = options.collaborationAuth === undefined ? undefined : transportCommon.normalizeCollaborationAuth(options.collaborationAuth);
192
243
  if (collaborationAuth !== undefined) {
@@ -240,12 +291,17 @@ class HostTransport {
240
291
  ? request.params.options
241
292
  : undefined;
242
293
  const tracksMutation = request.method === 'document.invoke' && documentOperation.mutates === true && requestOptions?.dryRun !== true;
294
+ const includeRevision = request.method === 'document.invoke' &&
295
+ documentOperation.mutates === true &&
296
+ this.hostFeatures.has('document.mutationRevision');
243
297
  if (tracksMutation)
244
298
  this.beginDocumentRpcMutation(sessionId);
245
299
  let result;
246
300
  try {
247
- const response = await this.sendJsonRpcRequest(request.method, request.params, this.resolveWatchdogTimeout(options.timeoutMs), { requestTimeoutMs });
248
- result = documentRpc.mapDocumentLifecycleResult(operation.operationId, response, sessionId, operation.operationId === 'doc.save' ? request.params.path === undefined : undefined);
301
+ const response = await this.sendJsonRpcRequest(request.method, { ...request.params, ...(includeRevision ? { includeRevision: true } : {}) }, this.resolveWatchdogTimeout(options.timeoutMs), { requestTimeoutMs });
302
+ result = documentRpc.mapDocumentLifecycleResult(operation.operationId, includeRevision ? response.result : response, sessionId, operation.operationId === 'doc.save' ? request.params.path === undefined : undefined);
303
+ if (includeRevision)
304
+ documentEvidence.registerMutationRevision(result, response.revision);
249
305
  }
250
306
  catch (error) {
251
307
  if (tracksMutation) {
@@ -285,8 +341,11 @@ class HostTransport {
285
341
  details: { operationId: operation.operationId },
286
342
  });
287
343
  }
344
+ const supportsRequestTimeoutMetadata = this.hostFeatures.has(documentRpc.DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE);
288
345
  const cliHostRequestTimeoutMs = this.requestTimeoutMs ?? HOST_DEFAULT_REQUEST_TIMEOUT_MS;
289
- if (options.timeoutMs !== undefined && options.timeoutMs > cliHostRequestTimeoutMs) {
346
+ if (!supportsRequestTimeoutMetadata &&
347
+ options.timeoutMs !== undefined &&
348
+ options.timeoutMs > cliHostRequestTimeoutMs) {
290
349
  throw new errors.SuperDocCliError(`CLI host cannot honor timeoutMs=${options.timeoutMs} above its ${cliHostRequestTimeoutMs}ms request ceiling.`, {
291
350
  code: 'DOCUMENT_RPC_INPUT_UNSUPPORTED',
292
351
  details: {
@@ -304,7 +363,9 @@ class HostTransport {
304
363
  argv,
305
364
  stdinBase64,
306
365
  ...(collaborationAuth === undefined ? {} : { collaborationAuth }),
307
- }, watchdogTimeout);
366
+ }, watchdogTimeout, {
367
+ requestTimeoutMs: supportsRequestTimeoutMetadata ? options.timeoutMs : undefined,
368
+ });
308
369
  if (typeof response !== 'object' || response == null || Array.isArray(response)) {
309
370
  throw new errors.SuperDocCliError('Host returned invalid cli.invoke result.', {
310
371
  code: 'HOST_PROTOCOL_ERROR',
@@ -61,9 +61,12 @@ export declare class HostTransport {
61
61
  processMode?: 'cli' | 'document';
62
62
  } & SuperDocClientOptions);
63
63
  connect(): Promise<void>;
64
+ supportsDocumentFacts(sessionId: string): boolean;
65
+ readDocumentRevision(sessionId: string): Promise<string | undefined>;
64
66
  dispose(): Promise<void>;
65
67
  private disposeAfterActiveCalls;
66
68
  invoke<TData = unknown>(operation: OperationSpec, params?: Record<string, unknown>, options?: TransportInvokeOptions): Promise<TData>;
69
+ replaceFile(sessionId: string, path: string, options?: TransportInvokeOptions): Promise<Record<string, unknown>>;
67
70
  private invokeWhileActive;
68
71
  private runWhileActive;
69
72
  private ensureConnected;
@@ -2,7 +2,8 @@ import { spawn } from 'node:child_process';
2
2
  import { createInterface } from 'node:readline';
3
3
  import { buildOperationArgv, normalizeCollaborationAuth, 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, DOCUMENT_RPC_SOURCE_SAVE_FEATURE, buildDocumentInvokeParams, buildDocumentOpenParams, canOpenWithDocumentRpc, documentRpcRequestSupportsResponseTimeout, documentRpcEnabled, mapDocumentLifecycleResult, mapDocumentOpenResult, supportsDocumentRpc, } from './document-rpc.js';
5
+ import { registerMutationRevision } from './document-evidence.js';
6
+ import { DOCUMENT_RPC_FEATURES, DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE, DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE, DOCUMENT_RPC_REPLACE_FILE_FEATURE, DOCUMENT_RPC_SOURCE_SAVE_FEATURE, buildDocumentInvokeParams, buildDocumentOpenParams, canOpenWithDocumentRpc, documentRpcRequestSupportsResponseTimeout, documentRpcEnabled, mapDocumentLifecycleResult, mapDocumentOpenResult, mapDocumentReplaceFileResult, supportsDocumentRpc, } from './document-rpc.js';
6
7
  const HOST_PROTOCOL_VERSION = '1.0';
7
8
  const CLI_HOST_REQUIRED_FEATURES = ['cli.invoke', 'host.shutdown'];
8
9
  export const COLLABORATION_AUTH_PER_OPEN_FEATURE = 'collaboration.auth.perOpen';
@@ -127,6 +128,28 @@ export class HostTransport {
127
128
  async connect() {
128
129
  await this.runWhileActive(() => this.ensureConnected());
129
130
  }
131
+ supportsDocumentFacts(sessionId) {
132
+ return (this.documentRpcSessions.has(sessionId) &&
133
+ this.hostFeatures.has('document.currentRevision') &&
134
+ this.hostFeatures.has('document.executionFacts') &&
135
+ this.hostFeatures.has('document.mutationRevision'));
136
+ }
137
+ async readDocumentRevision(sessionId) {
138
+ return this.runWhileActive(async () => {
139
+ if (!this.documentRpcSessions.has(sessionId) || !this.hostFeatures.has('document.currentRevision')) {
140
+ return undefined;
141
+ }
142
+ await this.ensureConnected();
143
+ const revision = await this.sendJsonRpcRequest('document.currentRevision', { sessionId }, this.resolveWatchdogTimeout(undefined), { requestTimeoutMs: this.resolveDocumentRequestTimeout(undefined) });
144
+ if (typeof revision !== 'string' || revision.trim().length === 0 || revision === 'unknown') {
145
+ throw new SuperDocCliError('Document host returned an invalid current revision.', {
146
+ code: 'HOST_PROTOCOL_ERROR',
147
+ details: { sessionId },
148
+ });
149
+ }
150
+ return revision;
151
+ });
152
+ }
130
153
  async dispose() {
131
154
  if (this.disposePromise)
132
155
  return this.disposePromise;
@@ -184,6 +207,34 @@ export class HostTransport {
184
207
  async invoke(operation, params = {}, options = {}) {
185
208
  return this.runWhileActive(() => this.invokeWhileActive(operation, params, options));
186
209
  }
210
+ async replaceFile(sessionId, path, options = {}) {
211
+ return this.runWhileActive(async () => {
212
+ await this.ensureConnected();
213
+ if (!this.hostFeatures.has(DOCUMENT_RPC_REPLACE_FILE_FEATURE)) {
214
+ throw new SuperDocCliError('The connected host does not support document replacement.', {
215
+ code: 'CAPABILITY_UNSUPPORTED',
216
+ details: { feature: DOCUMENT_RPC_REPLACE_FILE_FEATURE },
217
+ });
218
+ }
219
+ const tracksDirectSession = this.documentRpcSessions.has(sessionId);
220
+ if (tracksDirectSession)
221
+ this.beginDocumentRpcMutation(sessionId);
222
+ try {
223
+ const response = await this.sendJsonRpcRequest(DOCUMENT_RPC_REPLACE_FILE_FEATURE, { sessionId, path }, this.resolveWatchdogTimeout(options.timeoutMs));
224
+ const result = mapDocumentReplaceFileResult(response, sessionId);
225
+ if (tracksDirectSession)
226
+ this.settleDocumentRpcMutation(sessionId, 'applied');
227
+ return result;
228
+ }
229
+ catch (error) {
230
+ if (tracksDirectSession) {
231
+ const indeterminate = error instanceof SuperDocCliError && ['TIMEOUT', 'HOST_TIMEOUT', 'HOST_DISCONNECTED'].includes(error.code);
232
+ this.settleDocumentRpcMutation(sessionId, indeterminate ? 'indeterminate' : 'not-applied');
233
+ }
234
+ throw error;
235
+ }
236
+ });
237
+ }
187
238
  async invokeWhileActive(operation, params, options) {
188
239
  const collaborationAuth = options.collaborationAuth === undefined ? undefined : normalizeCollaborationAuth(options.collaborationAuth);
189
240
  if (collaborationAuth !== undefined) {
@@ -237,12 +288,17 @@ export class HostTransport {
237
288
  ? request.params.options
238
289
  : undefined;
239
290
  const tracksMutation = request.method === 'document.invoke' && documentOperation.mutates === true && requestOptions?.dryRun !== true;
291
+ const includeRevision = request.method === 'document.invoke' &&
292
+ documentOperation.mutates === true &&
293
+ this.hostFeatures.has('document.mutationRevision');
240
294
  if (tracksMutation)
241
295
  this.beginDocumentRpcMutation(sessionId);
242
296
  let result;
243
297
  try {
244
- const response = await this.sendJsonRpcRequest(request.method, request.params, this.resolveWatchdogTimeout(options.timeoutMs), { requestTimeoutMs });
245
- result = mapDocumentLifecycleResult(operation.operationId, response, sessionId, operation.operationId === 'doc.save' ? request.params.path === undefined : undefined);
298
+ const response = await this.sendJsonRpcRequest(request.method, { ...request.params, ...(includeRevision ? { includeRevision: true } : {}) }, this.resolveWatchdogTimeout(options.timeoutMs), { requestTimeoutMs });
299
+ result = mapDocumentLifecycleResult(operation.operationId, includeRevision ? response.result : response, sessionId, operation.operationId === 'doc.save' ? request.params.path === undefined : undefined);
300
+ if (includeRevision)
301
+ registerMutationRevision(result, response.revision);
246
302
  }
247
303
  catch (error) {
248
304
  if (tracksMutation) {
@@ -282,8 +338,11 @@ export class HostTransport {
282
338
  details: { operationId: operation.operationId },
283
339
  });
284
340
  }
341
+ const supportsRequestTimeoutMetadata = this.hostFeatures.has(DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE);
285
342
  const cliHostRequestTimeoutMs = this.requestTimeoutMs ?? HOST_DEFAULT_REQUEST_TIMEOUT_MS;
286
- if (options.timeoutMs !== undefined && options.timeoutMs > cliHostRequestTimeoutMs) {
343
+ if (!supportsRequestTimeoutMetadata &&
344
+ options.timeoutMs !== undefined &&
345
+ options.timeoutMs > cliHostRequestTimeoutMs) {
287
346
  throw new SuperDocCliError(`CLI host cannot honor timeoutMs=${options.timeoutMs} above its ${cliHostRequestTimeoutMs}ms request ceiling.`, {
288
347
  code: 'DOCUMENT_RPC_INPUT_UNSUPPORTED',
289
348
  details: {
@@ -301,7 +360,9 @@ export class HostTransport {
301
360
  argv,
302
361
  stdinBase64,
303
362
  ...(collaborationAuth === undefined ? {} : { collaborationAuth }),
304
- }, watchdogTimeout);
363
+ }, watchdogTimeout, {
364
+ requestTimeoutMs: supportsRequestTimeoutMetadata ? options.timeoutMs : undefined,
365
+ });
305
366
  if (typeof response !== 'object' || response == null || Array.isArray(response)) {
306
367
  throw new SuperDocCliError('Host returned invalid cli.invoke result.', {
307
368
  code: 'HOST_PROTOCOL_ERROR',