@superdoc-dev/sdk 1.14.0 → 1.15.0-next.2

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/dist/tools.d.ts CHANGED
@@ -1,29 +1,25 @@
1
+ /**
2
+ * Public LLM-tools API. Thin layer over the preset registry — every call here
3
+ * resolves a preset (defaulting to `legacy` for backwards compat) and delegates
4
+ * to it.
5
+ *
6
+ * Presets are the unit of swapping. To add a new tool surface (e.g. handwritten
7
+ * "core" tools, prompt-caching variant, lazy-load experiment), register a new
8
+ * descriptor in `presets.ts` — no changes here required.
9
+ */
1
10
  import type { BoundDocApi } from './generated/client.js';
2
11
  import type { InvokeOptions } from './runtime/process.js';
3
- export type ToolProvider = 'openai' | 'anthropic' | 'vercel' | 'generic';
4
- export type ToolCatalog = {
5
- contractVersion: string;
6
- generatedAt: string | null;
7
- toolCount: number;
8
- tools: ToolCatalogEntry[];
9
- };
10
- type OperationEntry = {
11
- operationId: string;
12
- intentAction: string;
13
- required?: string[];
14
- requiredOneOf?: string[][];
15
- };
16
- type ToolCatalogEntry = {
17
- toolName: string;
18
- description: string;
19
- inputSchema: Record<string, unknown>;
20
- mutates: boolean;
21
- operations: OperationEntry[];
22
- };
23
- export declare function getToolCatalog(): Promise<ToolCatalog>;
24
- export declare function listTools(provider: ToolProvider): Promise<unknown[]>;
12
+ import { DEFAULT_PRESET, getPreset, listPresets, type CacheStrategy, type ToolCatalog, type ToolCatalogEntry, type ToolCatalogOperation, type ToolProvider } from './presets.js';
13
+ export { DEFAULT_PRESET, getPreset, listPresets };
14
+ export type { CacheStrategy, ToolCatalog, ToolCatalogEntry, ToolCatalogOperation, ToolProvider };
25
15
  export type ToolChooserInput = {
26
16
  provider: ToolProvider;
17
+ /**
18
+ * Preset ID to load tools from. Defaults to {@link DEFAULT_PRESET}
19
+ * (`'legacy'`) for backwards compatibility. Use {@link listPresets} to
20
+ * discover available presets.
21
+ */
22
+ preset?: string;
27
23
  /**
28
24
  * When `true`, applies provider-specific prompt-caching markers to the
29
25
  * returned tools so subsequent identical requests reuse the cached prefix.
@@ -40,53 +36,65 @@ export type ToolChooserInput = {
40
36
  */
41
37
  cache?: boolean;
42
38
  };
43
- export type CacheStrategy = 'explicit' | 'automatic' | 'unsupported' | 'disabled';
44
39
  /**
45
- * Select all intent tools for a specific provider.
46
- *
47
- * Returns all intent tools in the requested provider format. Pass
48
- * `cache: true` to apply provider-specific caching markers (see
49
- * {@link ToolChooserInput.cache}).
40
+ * Select tools for a specific provider from a preset.
50
41
  *
51
42
  * @example
52
43
  * ```ts
44
+ * // Default — legacy preset, no cache markers.
45
+ * const { tools, meta } = await chooseTools({ provider: 'vercel' });
46
+ *
53
47
  * // Anthropic — last tool gets cache_control automatically.
54
48
  * const { tools, meta } = await chooseTools({ provider: 'anthropic', cache: true });
55
49
  *
56
- * // OpenAI caching is automatic when prompts exceed 1024 tokens.
57
- * const { tools } = await chooseTools({ provider: 'openai', cache: true });
50
+ * // Pick a specific preset by ID.
51
+ * const { tools, meta } = await chooseTools({ provider: 'openai', preset: 'legacy' });
58
52
  * ```
59
53
  */
60
54
  export declare function chooseTools(input: ToolChooserInput): Promise<{
61
55
  tools: unknown[];
62
56
  meta: {
63
57
  provider: ToolProvider;
58
+ preset: string;
64
59
  toolCount: number;
65
60
  cacheStrategy: CacheStrategy;
66
61
  };
67
62
  }>;
63
+ /** Return the full tool catalog for a preset (default: legacy). */
64
+ export declare function getToolCatalog(preset?: string): Promise<ToolCatalog>;
68
65
  /**
69
- * Dispatch a tool call against a bound document handle.
66
+ * Return the raw tool array for a provider from a preset (default: legacy).
67
+ *
68
+ * No cache markers are applied. Use {@link chooseTools} when you need cache
69
+ * markers and metadata.
70
+ */
71
+ export declare function listTools(provider: ToolProvider, preset?: string): Promise<unknown[]>;
72
+ /**
73
+ * Dispatch a tool call against a bound document handle using the default
74
+ * preset (`legacy`).
75
+ *
76
+ * The document handle injects session targeting automatically; tool arguments
77
+ * should not contain `doc` or `sessionId`.
70
78
  *
71
- * The document handle injects session targeting automatically.
72
- * Tool arguments should not contain `doc` or `sessionId`.
79
+ * For preset-aware dispatch e.g. when comparing two presets — call
80
+ * `getPreset('id').dispatch(...)` directly.
73
81
  */
74
82
  export declare function dispatchSuperDocTool(documentHandle: BoundDocApi, toolName: string, args?: Record<string, unknown>, invokeOptions?: InvokeOptions): Promise<unknown>;
75
83
  /**
76
- * Read the bundled SDK system prompt for intent tools.
84
+ * Read the packaged SDK system prompt (default preset: legacy).
77
85
  *
78
- * This prompt includes a persona preamble ("You are a document editing assistant…")
79
- * suitable for embedded LLM usage (OpenAI, Anthropic, Vercel APIs).
80
- * For MCP server instructions, use {@link getMcpPrompt} instead.
86
+ * Includes a persona preamble ("You are a document editing assistant…")
87
+ * suitable for embedded LLM usage (OpenAI, Anthropic, Vercel APIs). For MCP
88
+ * server instructions, use {@link getMcpPrompt} instead.
81
89
  */
82
- export declare function getSystemPrompt(): Promise<string>;
90
+ export declare function getSystemPrompt(preset?: string): Promise<string>;
83
91
  /**
84
- * Read the bundled MCP system prompt for intent tools.
92
+ * Read the packaged MCP system prompt for intent tools (default preset: legacy).
85
93
  *
86
- * This prompt omits the persona preamble and includes session lifecycle
87
- * instructions (open/save/close) suitable for MCP server `instructions`.
94
+ * Omits the persona preamble and includes session lifecycle instructions
95
+ * (open/save/close) suitable for MCP server `instructions`.
88
96
  */
89
- export declare function getMcpPrompt(): Promise<string>;
97
+ export declare function getMcpPrompt(preset?: string): Promise<string>;
90
98
  /**
91
99
  * Anthropic content block representation of the system prompt with optional
92
100
  * `cache_control` for prompt caching.
@@ -133,7 +141,7 @@ export type SystemPromptForProviderResult = {
133
141
  */
134
142
  export declare function getSystemPromptForProvider(input: {
135
143
  provider: ToolProvider;
144
+ preset?: string;
136
145
  cache?: boolean;
137
146
  }): Promise<SystemPromptForProviderResult>;
138
- export {};
139
147
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EACL,cAAc,EACd,SAAS,EACT,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,YAAY,EAAE,CAAC;AAMjG,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,YAAY,CAAC;IACvB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC;IAClE,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,IAAI,EAAE;QACJ,QAAQ,EAAE,YAAY,CAAC;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,aAAa,CAAC;KAC9B,CAAC;CACH,CAAC,CAeD;AAMD,mEAAmE;AACnE,wBAAsB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAE1E;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAG3F;AAMD;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CACxC,cAAc,EAAE,WAAW,EAC3B,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAClC,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,OAAO,CAAC,CAElB;AAMD;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtE;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnE;AAMD;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,KAAK,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE;QAAE,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC;CACvC,CAAC,CAAC;AAEH,MAAM,MAAM,6BAA6B,GACrC;IAAE,QAAQ,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,qBAAqB,CAAC;IAAC,aAAa,EAAE,aAAa,CAAA;CAAE,GACvF;IAAE,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,aAAa,CAAA;CAAE,CAAC;AAEjG;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,0BAA0B,CAAC,KAAK,EAAE;IACtD,QAAQ,EAAE,YAAY,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAqBzC"}
package/dist/tools.js CHANGED
@@ -1,330 +1,99 @@
1
- import { readFile } from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { SuperDocCliError } from './runtime/errors.js';
5
- import { dispatchIntentTool } from './generated/intent-dispatch.generated.js';
6
- // Resolve tools directory relative to package root (works from both src/ and dist/)
7
- const toolsDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'tools');
8
- const providerFileByName = {
9
- openai: 'tools.openai.json',
10
- anthropic: 'tools.anthropic.json',
11
- vercel: 'tools.vercel.json',
12
- generic: 'tools.generic.json',
13
- };
14
- const STRIP_EMPTY_OPTIONAL_ARGS = new Set(['parentId', 'parentCommentId', 'id', 'status']);
15
- function isRecord(value) {
16
- return typeof value === 'object' && value != null && !Array.isArray(value);
17
- }
18
- function isObviouslyCorruptedToolArgKey(key) {
19
- const trimmed = key.trim();
20
- return trimmed.length === 0 || !/[\p{L}\p{N}]/u.test(trimmed);
21
- }
22
- function stripCorruptedToolArgKeys(value) {
23
- if (Array.isArray(value)) {
24
- return value.map((item) => stripCorruptedToolArgKeys(item));
25
- }
26
- if (!isRecord(value))
27
- return value;
28
- const clean = {};
29
- for (const [key, entryValue] of Object.entries(value)) {
30
- if (isObviouslyCorruptedToolArgKey(key))
31
- continue;
32
- clean[key] = stripCorruptedToolArgKeys(entryValue);
33
- }
34
- return clean;
35
- }
36
- async function readJson(fileName) {
37
- const filePath = path.join(toolsDir, fileName);
38
- let raw = '';
39
- try {
40
- raw = await readFile(filePath, 'utf8');
41
- }
42
- catch (error) {
43
- throw new SuperDocCliError('Unable to load packaged tool artifact.', {
44
- code: 'TOOLS_ASSET_NOT_FOUND',
45
- details: {
46
- filePath,
47
- message: error instanceof Error ? error.message : String(error),
48
- },
49
- });
50
- }
51
- try {
52
- return JSON.parse(raw);
53
- }
54
- catch (error) {
55
- throw new SuperDocCliError('Packaged tool artifact is invalid JSON.', {
56
- code: 'TOOLS_ASSET_INVALID',
57
- details: {
58
- filePath,
59
- message: error instanceof Error ? error.message : String(error),
60
- },
61
- });
62
- }
63
- }
64
- async function loadProviderBundle(provider) {
65
- return readJson(providerFileByName[provider]);
66
- }
67
- async function loadCatalog() {
68
- return readJson('catalog.json');
69
- }
70
- export async function getToolCatalog() {
71
- return getCachedCatalog();
72
- }
73
- export async function listTools(provider) {
74
- const bundle = await loadProviderBundle(provider);
75
- const tools = bundle.tools;
76
- if (!Array.isArray(tools)) {
77
- throw new SuperDocCliError('Tool provider bundle is missing tools array.', {
78
- code: 'TOOLS_ASSET_INVALID',
79
- details: { provider },
80
- });
81
- }
82
- return tools;
83
- }
84
1
  /**
85
- * Select all intent tools for a specific provider.
2
+ * Public LLM-tools API. Thin layer over the preset registry — every call here
3
+ * resolves a preset (defaulting to `legacy` for backwards compat) and delegates
4
+ * to it.
86
5
  *
87
- * Returns all intent tools in the requested provider format. Pass
88
- * `cache: true` to apply provider-specific caching markers (see
89
- * {@link ToolChooserInput.cache}).
6
+ * Presets are the unit of swapping. To add a new tool surface (e.g. handwritten
7
+ * "core" tools, prompt-caching variant, lazy-load experiment), register a new
8
+ * descriptor in `presets.ts` — no changes here required.
9
+ */
10
+ import { DEFAULT_PRESET, getPreset, listPresets, } from './presets.js';
11
+ export { DEFAULT_PRESET, getPreset, listPresets };
12
+ /**
13
+ * Select tools for a specific provider from a preset.
90
14
  *
91
15
  * @example
92
16
  * ```ts
17
+ * // Default — legacy preset, no cache markers.
18
+ * const { tools, meta } = await chooseTools({ provider: 'vercel' });
19
+ *
93
20
  * // Anthropic — last tool gets cache_control automatically.
94
21
  * const { tools, meta } = await chooseTools({ provider: 'anthropic', cache: true });
95
22
  *
96
- * // OpenAI caching is automatic when prompts exceed 1024 tokens.
97
- * const { tools } = await chooseTools({ provider: 'openai', cache: true });
23
+ * // Pick a specific preset by ID.
24
+ * const { tools, meta } = await chooseTools({ provider: 'openai', preset: 'legacy' });
98
25
  * ```
99
26
  */
100
27
  export async function chooseTools(input) {
101
- const bundle = await loadProviderBundle(input.provider);
102
- const rawTools = Array.isArray(bundle.tools) ? bundle.tools : [];
103
- const cacheRequested = input.cache === true;
104
- const { tools, cacheStrategy } = applyCacheMarkers(rawTools, input.provider, cacheRequested);
28
+ const presetId = input.preset ?? DEFAULT_PRESET;
29
+ const preset = getPreset(presetId);
30
+ const { tools, cacheStrategy } = await preset.getTools(input.provider, {
31
+ cache: input.cache === true,
32
+ });
105
33
  return {
106
34
  tools,
107
35
  meta: {
108
36
  provider: input.provider,
37
+ preset: presetId,
109
38
  toolCount: tools.length,
110
39
  cacheStrategy,
111
40
  },
112
41
  };
113
42
  }
114
- /**
115
- * Apply provider-specific caching markers to the tools array. Mutates a clone,
116
- * never the input. Anthropic gets an explicit `cache_control` on the last
117
- * tool; other providers pass through.
118
- */
119
- function applyCacheMarkers(tools, provider, cacheRequested) {
120
- if (!cacheRequested) {
121
- return { tools, cacheStrategy: 'disabled' };
122
- }
123
- if (provider === 'anthropic') {
124
- if (tools.length === 0)
125
- return { tools, cacheStrategy: 'explicit' };
126
- // Anthropic: marking the LAST tool with cache_control caches the entire
127
- // tools block (and everything before it in the request — system prompt
128
- // first if it also has cache_control). Shallow-spread the last entry so we
129
- // don't mutate the cached bundle in place.
130
- const next = tools.slice(0, -1);
131
- const last = {
132
- ...tools[tools.length - 1],
133
- cache_control: { type: 'ephemeral' },
134
- };
135
- next.push(last);
136
- return { tools: next, cacheStrategy: 'explicit' };
137
- }
138
- if (provider === 'openai') {
139
- // OpenAI caches prompts ≥ 1024 tokens automatically. No marker needed,
140
- // but we still report cacheStrategy:'automatic' so callers can branch on
141
- // it (e.g. for measurement).
142
- return { tools, cacheStrategy: 'automatic' };
143
- }
144
- // vercel / generic — depends on underlying model.
145
- return { tools, cacheStrategy: 'unsupported' };
146
- }
147
- function resolveDocApiMethod(documentHandle, operationId) {
148
- const tokens = operationId.split('.').slice(1);
149
- let cursor = documentHandle;
150
- for (const token of tokens) {
151
- if (!isRecord(cursor) || !(token in cursor)) {
152
- throw new SuperDocCliError(`No SDK doc method found for operation ${operationId}.`, {
153
- code: 'TOOL_DISPATCH_NOT_FOUND',
154
- details: { operationId, token },
155
- });
156
- }
157
- cursor = cursor[token];
158
- }
159
- if (typeof cursor !== 'function') {
160
- throw new SuperDocCliError(`Resolved member for ${operationId} is not callable.`, {
161
- code: 'TOOL_DISPATCH_NOT_FOUND',
162
- details: { operationId },
163
- });
164
- }
165
- return cursor;
166
- }
167
- // Cached catalog instance — loaded once per process.
168
- let _catalogCache = null;
169
- async function getCachedCatalog() {
170
- if (_catalogCache == null) {
171
- _catalogCache = await loadCatalog();
172
- }
173
- return _catalogCache;
43
+ // ---------------------------------------------------------------------------
44
+ // Catalog + listings (preset-scoped; default to legacy)
45
+ // ---------------------------------------------------------------------------
46
+ /** Return the full tool catalog for a preset (default: legacy). */
47
+ export async function getToolCatalog(preset) {
48
+ return getPreset(preset ?? DEFAULT_PRESET).getCatalog();
174
49
  }
175
50
  /**
176
- * Validate tool arguments against the catalog schema.
51
+ * Return the raw tool array for a provider from a preset (default: legacy).
177
52
  *
178
- * Checks three things in order:
179
- * 1. No unknown keys (additionalProperties: false in merged schema)
180
- * 2. All universally-required keys present (merged schema `required`)
181
- * 3. All action-specific required keys present (per-operation `required`)
53
+ * No cache markers are applied. Use {@link chooseTools} when you need cache
54
+ * markers and metadata.
182
55
  */
183
- function validateToolArgs(toolName, args, tool) {
184
- const schema = tool.inputSchema;
185
- const properties = isRecord(schema.properties) ? schema.properties : {};
186
- const required = Array.isArray(schema.required) ? schema.required : [];
187
- // 1. Reject unknown keys
188
- const knownKeys = new Set(Object.keys(properties));
189
- const unknownKeys = Object.keys(args).filter((k) => !knownKeys.has(k));
190
- if (unknownKeys.length > 0) {
191
- throw new SuperDocCliError(`Unknown argument(s) for ${toolName}: ${unknownKeys.join(', ')}`, {
192
- code: 'INVALID_ARGUMENT',
193
- details: { toolName, unknownKeys, knownKeys: [...knownKeys] },
194
- });
195
- }
196
- // 2. Reject missing universally-required keys
197
- const missingKeys = required.filter((k) => args[k] == null);
198
- if (missingKeys.length > 0) {
199
- throw new SuperDocCliError(`Missing required argument(s) for ${toolName}: ${missingKeys.join(', ')}`, {
200
- code: 'INVALID_ARGUMENT',
201
- details: { toolName, missingKeys },
202
- });
203
- }
204
- // 3. Reject missing per-operation required keys.
205
- // For multi-action tools, resolve the operation by action; for single-op
206
- // tools, use the sole operation entry.
207
- const action = args.action;
208
- let op;
209
- if (typeof action === 'string' && tool.operations.length > 1) {
210
- op = tool.operations.find((o) => o.intentAction === action);
211
- }
212
- else if (tool.operations.length === 1) {
213
- op = tool.operations[0];
214
- }
215
- if (op) {
216
- validateOperationRequired(toolName, action, args, op);
217
- }
56
+ export async function listTools(provider, preset) {
57
+ const { tools } = await getPreset(preset ?? DEFAULT_PRESET).getTools(provider, { cache: false });
58
+ return tools;
218
59
  }
60
+ // ---------------------------------------------------------------------------
61
+ // Dispatch
62
+ // ---------------------------------------------------------------------------
219
63
  /**
220
- * Check per-operation required constraints.
64
+ * Dispatch a tool call against a bound document handle using the default
65
+ * preset (`legacy`).
221
66
  *
222
- * Handles two shapes emitted by the codegen:
223
- * - `required: string[]` — all listed keys must be present
224
- * - `requiredOneOf: string[][]` — at least one branch must be fully satisfied
225
- * (mirrors JSON Schema `oneOf` with per-branch `required` arrays)
226
- */
227
- function validateOperationRequired(toolName, action, args, op) {
228
- const actionLabel = typeof action === 'string' ? ` action "${action}"` : '';
229
- if (op.requiredOneOf && op.requiredOneOf.length > 0) {
230
- const satisfied = op.requiredOneOf.some((branch) => branch.every((k) => args[k] != null));
231
- if (!satisfied) {
232
- const options = op.requiredOneOf.map((b) => b.join(' + ')).join(' | ');
233
- throw new SuperDocCliError(`Missing required argument(s) for ${toolName}${actionLabel}: must provide one of: ${options}`, {
234
- code: 'INVALID_ARGUMENT',
235
- details: { toolName, action, requiredOneOf: op.requiredOneOf },
236
- });
237
- }
238
- }
239
- else if (op.required && op.required.length > 0) {
240
- const missingActionKeys = op.required.filter((k) => args[k] == null);
241
- if (missingActionKeys.length > 0) {
242
- throw new SuperDocCliError(`Missing required argument(s) for ${toolName}${actionLabel}: ${missingActionKeys.join(', ')}`, {
243
- code: 'INVALID_ARGUMENT',
244
- details: { toolName, action, missingKeys: missingActionKeys },
245
- });
246
- }
247
- }
248
- }
249
- /**
250
- * Dispatch a tool call against a bound document handle.
67
+ * The document handle injects session targeting automatically; tool arguments
68
+ * should not contain `doc` or `sessionId`.
251
69
  *
252
- * The document handle injects session targeting automatically.
253
- * Tool arguments should not contain `doc` or `sessionId`.
70
+ * For preset-aware dispatch e.g. when comparing two presets — call
71
+ * `getPreset('id').dispatch(...)` directly.
254
72
  */
255
73
  export async function dispatchSuperDocTool(documentHandle, toolName, args = {}, invokeOptions) {
256
- if (!isRecord(args)) {
257
- throw new SuperDocCliError(`Tool arguments for ${toolName} must be an object.`, {
258
- code: 'INVALID_ARGUMENT',
259
- details: { toolName },
260
- });
261
- }
262
- const sanitizedArgs = stripCorruptedToolArgKeys(args);
263
- if (!isRecord(sanitizedArgs)) {
264
- throw new SuperDocCliError(`Tool arguments for ${toolName} must be an object.`, {
265
- code: 'INVALID_ARGUMENT',
266
- details: { toolName },
267
- });
268
- }
269
- // Validate against the tool schema before dispatch.
270
- const catalog = await getCachedCatalog();
271
- const tool = catalog.tools.find((t) => t.toolName === toolName);
272
- if (tool == null) {
273
- throw new SuperDocCliError(`Unknown tool: ${toolName}`, {
274
- code: 'TOOL_DISPATCH_NOT_FOUND',
275
- details: { toolName },
276
- });
277
- }
278
- validateToolArgs(toolName, sanitizedArgs, tool);
279
- // Strip empty strings for known optional ID/enum params that LLMs fill with ""
280
- // instead of omitting. Only target params where "" is never a valid value.
281
- const cleanArgs = {};
282
- for (const [key, value] of Object.entries(sanitizedArgs)) {
283
- if (value === '' && STRIP_EMPTY_OPTIONAL_ARGS.has(key))
284
- continue;
285
- cleanArgs[key] = value;
286
- }
287
- return dispatchIntentTool(toolName, cleanArgs, (operationId, input) => {
288
- const method = resolveDocApiMethod(documentHandle, operationId);
289
- return method(input, invokeOptions);
290
- });
74
+ return getPreset(DEFAULT_PRESET).dispatch(documentHandle, toolName, args, invokeOptions);
291
75
  }
76
+ // ---------------------------------------------------------------------------
77
+ // System prompts (preset-scoped; default to legacy)
78
+ // ---------------------------------------------------------------------------
292
79
  /**
293
- * Read the bundled SDK system prompt for intent tools.
80
+ * Read the packaged SDK system prompt (default preset: legacy).
294
81
  *
295
- * This prompt includes a persona preamble ("You are a document editing assistant…")
296
- * suitable for embedded LLM usage (OpenAI, Anthropic, Vercel APIs).
297
- * For MCP server instructions, use {@link getMcpPrompt} instead.
82
+ * Includes a persona preamble ("You are a document editing assistant…")
83
+ * suitable for embedded LLM usage (OpenAI, Anthropic, Vercel APIs). For MCP
84
+ * server instructions, use {@link getMcpPrompt} instead.
298
85
  */
299
- export async function getSystemPrompt() {
300
- const promptPath = path.join(toolsDir, 'system-prompt.md');
301
- try {
302
- return await readFile(promptPath, 'utf8');
303
- }
304
- catch {
305
- throw new SuperDocCliError('System prompt not found.', {
306
- code: 'TOOLS_ASSET_NOT_FOUND',
307
- details: { filePath: promptPath },
308
- });
309
- }
86
+ export async function getSystemPrompt(preset) {
87
+ return getPreset(preset ?? DEFAULT_PRESET).getSystemPrompt();
310
88
  }
311
89
  /**
312
- * Read the bundled MCP system prompt for intent tools.
90
+ * Read the packaged MCP system prompt for intent tools (default preset: legacy).
313
91
  *
314
- * This prompt omits the persona preamble and includes session lifecycle
315
- * instructions (open/save/close) suitable for MCP server `instructions`.
92
+ * Omits the persona preamble and includes session lifecycle instructions
93
+ * (open/save/close) suitable for MCP server `instructions`.
316
94
  */
317
- export async function getMcpPrompt() {
318
- const promptPath = path.join(toolsDir, 'system-prompt-mcp.md');
319
- try {
320
- return await readFile(promptPath, 'utf8');
321
- }
322
- catch {
323
- throw new SuperDocCliError('MCP system prompt not found.', {
324
- code: 'TOOLS_ASSET_NOT_FOUND',
325
- details: { filePath: promptPath },
326
- });
327
- }
95
+ export async function getMcpPrompt(preset) {
96
+ return getPreset(preset ?? DEFAULT_PRESET).getMcpPrompt();
328
97
  }
329
98
  /**
330
99
  * Get the system prompt formatted for a specific LLM provider, with optional
@@ -351,7 +120,7 @@ export async function getMcpPrompt() {
351
120
  * ```
352
121
  */
353
122
  export async function getSystemPromptForProvider(input) {
354
- const text = await getSystemPrompt();
123
+ const text = await getSystemPrompt(input.preset);
355
124
  const cacheRequested = input.cache === true;
356
125
  if (input.provider === 'anthropic') {
357
126
  const block = { type: 'text', text };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc-dev/sdk",
3
- "version": "1.14.0",
3
+ "version": "1.15.0-next.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -26,11 +26,11 @@
26
26
  "typescript": "^5.9.2"
27
27
  },
28
28
  "optionalDependencies": {
29
- "@superdoc-dev/sdk-darwin-x64": "1.14.0",
30
- "@superdoc-dev/sdk-darwin-arm64": "1.14.0",
31
- "@superdoc-dev/sdk-linux-arm64": "1.14.0",
32
- "@superdoc-dev/sdk-linux-x64": "1.14.0",
33
- "@superdoc-dev/sdk-windows-x64": "1.14.0"
29
+ "@superdoc-dev/sdk-darwin-arm64": "1.15.0-next.2",
30
+ "@superdoc-dev/sdk-darwin-x64": "1.15.0-next.2",
31
+ "@superdoc-dev/sdk-linux-x64": "1.15.0-next.2",
32
+ "@superdoc-dev/sdk-windows-x64": "1.15.0-next.2",
33
+ "@superdoc-dev/sdk-linux-arm64": "1.15.0-next.2"
34
34
  },
35
35
  "publishConfig": {
36
36
  "access": "public"