@superdoc-dev/sdk 1.0.0-alpha.43 → 1.0.0-alpha.45

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,90 +1,52 @@
1
1
  import type { InvokeOptions } from './runtime/process.js';
2
2
  export type ToolProvider = 'openai' | 'anthropic' | 'vercel' | 'generic';
3
- export type ToolGroup = 'core' | 'format' | 'create' | 'tables' | 'sections' | 'lists' | 'comments' | 'trackChanges' | 'toc' | 'images' | 'history' | 'session';
4
- export type ToolChooserMode = 'essential' | 'all';
5
- export type ToolChooserInput = {
6
- provider: ToolProvider;
7
- groups?: ToolGroup[];
8
- /** Default: 'essential'. When 'essential', only essential tools are returned (plus any from `groups`). */
9
- mode?: ToolChooserMode;
10
- /** Whether to include the discover_tools meta-tool. Default: true when mode='essential', false when mode='all'. */
11
- includeDiscoverTool?: boolean;
12
- };
13
3
  export type ToolCatalog = {
14
4
  contractVersion: string;
15
5
  generatedAt: string | null;
16
- namePolicyVersion: string;
17
- exposureVersion: string;
18
6
  toolCount: number;
19
7
  tools: ToolCatalogEntry[];
20
8
  };
21
- type ToolCatalogEntry = {
9
+ type OperationEntry = {
22
10
  operationId: string;
11
+ intentAction: string;
12
+ required?: string[];
13
+ requiredOneOf?: string[][];
14
+ };
15
+ type ToolCatalogEntry = {
23
16
  toolName: string;
24
- profile: string;
25
- source: string;
26
17
  description: string;
27
18
  inputSchema: Record<string, unknown>;
28
- outputSchema: Record<string, unknown>;
29
19
  mutates: boolean;
30
- category: string;
31
- essential?: boolean;
32
- capabilities: string[];
33
- constraints?: Record<string, unknown>;
34
- errors: string[];
35
- examples: unknown[];
36
- commandTokens: string[];
37
- profileTags: string[];
38
- requiredCapabilities: string[];
39
- sessionRequirements: {
40
- requiresOpenContext: boolean;
41
- supportsSessionTargeting: boolean;
42
- };
43
- intentId?: string;
20
+ operations: OperationEntry[];
44
21
  };
45
- /** All available tool groups from the policy. */
46
- export declare function getAvailableGroups(): ToolGroup[];
47
22
  export declare function getToolCatalog(): Promise<ToolCatalog>;
48
23
  export declare function listTools(provider: ToolProvider): Promise<unknown[]>;
49
- export declare function resolveToolOperation(toolName: string): Promise<string | null>;
24
+ export type ToolChooserInput = {
25
+ provider: ToolProvider;
26
+ };
50
27
  /**
51
- * Select tools for a specific provider.
28
+ * Select all intent tools for a specific provider.
52
29
  *
53
- * **mode='essential'** (default): Returns only essential tools + discover_tools.
54
- * Pass `groups` to additionally load all tools from those categories.
55
- *
56
- * **mode='all'**: Returns all tools from requested groups (or all groups if
57
- * `groups` is omitted). No discover_tools included by default.
30
+ * Returns all intent tools in the requested provider format.
58
31
  *
59
32
  * @example
60
33
  * ```ts
61
- * // Default: 5 essential tools + discover_tools
62
34
  * const { tools } = await chooseTools({ provider: 'openai' });
63
- *
64
- * // Essential + all comment tools
65
- * const { tools } = await chooseTools({ provider: 'openai', groups: ['comments'] });
66
- *
67
- * // All tools (old behavior)
68
- * const { tools } = await chooseTools({ provider: 'openai', mode: 'all' });
69
35
  * ```
70
36
  */
71
37
  export declare function chooseTools(input: ToolChooserInput): Promise<{
72
38
  tools: unknown[];
73
- selected: Array<{
74
- operationId: string;
75
- toolName: string;
76
- category: string;
77
- mutates: boolean;
78
- }>;
79
39
  meta: {
80
40
  provider: ToolProvider;
81
- mode: string;
82
- groups: string[];
83
- selectedCount: number;
41
+ toolCount: number;
84
42
  };
85
43
  }>;
86
44
  export declare function dispatchSuperDocTool(client: {
87
45
  doc: Record<string, unknown>;
88
46
  }, toolName: string, args?: Record<string, unknown>, invokeOptions?: InvokeOptions): Promise<unknown>;
47
+ /**
48
+ * Read the bundled system prompt for intent tools.
49
+ */
50
+ export declare function getSystemPrompt(): Promise<string>;
89
51
  export {};
90
52
  //# 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":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;AAWzE,MAAM,MAAM,WAAW,GAAG;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;CAC5B,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B,CAAC;AA6CF,wBAAsB,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC,CAE3D;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAU1E;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,YAAY,CAAC;CACxB,CAAC;AAEF;;;;;;;;;GASG;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,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC,CAWD;AAiID,wBAAsB,oBAAoB,CACxC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EACxC,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAClC,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,OAAO,CAAC,CA0BlB;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAUvD"}
package/dist/tools.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import { readFile } from 'node:fs/promises';
2
- import { readFileSync } from 'node:fs';
3
2
  import path from 'node:path';
4
3
  import { fileURLToPath } from 'node:url';
5
- import { CONTRACT } from './generated/contract.js';
6
4
  import { SuperDocCliError } from './runtime/errors.js';
5
+ import { dispatchIntentTool } from './generated/intent-dispatch.generated.js';
7
6
  // Resolve tools directory relative to package root (works from both src/ and dist/)
8
7
  const toolsDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'tools');
9
8
  const providerFileByName = {
@@ -12,37 +11,9 @@ const providerFileByName = {
12
11
  vercel: 'tools.vercel.json',
13
12
  generic: 'tools.generic.json',
14
13
  };
15
- let _policyCache = null;
16
- function loadPolicy() {
17
- if (_policyCache)
18
- return _policyCache;
19
- const raw = readFileSync(path.join(toolsDir, 'tools-policy.json'), 'utf8');
20
- _policyCache = JSON.parse(raw);
21
- return _policyCache;
22
- }
23
14
  function isRecord(value) {
24
15
  return typeof value === 'object' && value != null && !Array.isArray(value);
25
16
  }
26
- function isPresent(value) {
27
- if (value == null)
28
- return false;
29
- if (Array.isArray(value))
30
- return value.length > 0;
31
- return true;
32
- }
33
- function extractProviderToolName(tool) {
34
- // Anthropic / Generic: top-level name
35
- if (typeof tool.name === 'string')
36
- return tool.name;
37
- // OpenAI / Vercel: nested under function.name
38
- if (isRecord(tool.function) && typeof tool.function.name === 'string') {
39
- return tool.function.name;
40
- }
41
- return null;
42
- }
43
- function invalidArgument(message, details) {
44
- throw new SuperDocCliError(message, { code: 'INVALID_ARGUMENT', details });
45
- }
46
17
  async function readJson(fileName) {
47
18
  const filePath = path.join(toolsDir, fileName);
48
19
  let raw = '';
@@ -74,87 +45,43 @@ async function readJson(fileName) {
74
45
  async function loadProviderBundle(provider) {
75
46
  return readJson(providerFileByName[provider]);
76
47
  }
77
- async function loadToolNameMap() {
78
- return readJson('tool-name-map.json');
79
- }
80
48
  async function loadCatalog() {
81
49
  return readJson('catalog.json');
82
50
  }
83
- /** All available tool groups from the policy. */
84
- export function getAvailableGroups() {
85
- const policy = loadPolicy();
86
- return policy.groups;
51
+ export async function getToolCatalog() {
52
+ return loadCatalog();
87
53
  }
88
- const OPERATION_INDEX = Object.fromEntries(Object.entries(CONTRACT.operations).map(([id, op]) => [id, op]));
89
- function validateDispatchArgs(operationId, args) {
90
- const operation = OPERATION_INDEX[operationId];
91
- if (!operation) {
92
- invalidArgument(`Unknown operation id ${operationId}.`);
93
- }
94
- // Unknown-param rejection
95
- const allowedParams = new Set(operation.params.map((param) => String(param.name)));
96
- for (const key of Object.keys(args)) {
97
- if (!allowedParams.has(key)) {
98
- invalidArgument(`Unexpected parameter ${key} for ${operationId}.`);
99
- }
100
- }
101
- // Required-param enforcement
102
- for (const param of operation.params) {
103
- if ('required' in param && Boolean(param.required) && args[param.name] == null) {
104
- invalidArgument(`Missing required parameter ${param.name} for ${operationId}.`);
105
- }
106
- }
107
- // Constraint validation (CLI handles schema-level type validation authoritatively)
108
- const constraints = 'constraints' in operation ? operation.constraints : undefined;
109
- if (!constraints || !isRecord(constraints))
110
- return;
111
- const mutuallyExclusive = Array.isArray(constraints.mutuallyExclusive) ? constraints.mutuallyExclusive : [];
112
- const requiresOneOf = Array.isArray(constraints.requiresOneOf) ? constraints.requiresOneOf : [];
113
- const requiredWhen = Array.isArray(constraints.requiredWhen) ? constraints.requiredWhen : [];
114
- for (const group of mutuallyExclusive) {
115
- if (!Array.isArray(group))
116
- continue;
117
- const present = group.filter((name) => isPresent(args[name]));
118
- if (present.length > 1) {
119
- invalidArgument(`Arguments are mutually exclusive for ${operationId}: ${group.join(', ')}`, {
120
- operationId,
121
- group,
122
- });
123
- }
124
- }
125
- for (const group of requiresOneOf) {
126
- if (!Array.isArray(group))
127
- continue;
128
- const hasAny = group.some((name) => isPresent(args[name]));
129
- if (!hasAny) {
130
- invalidArgument(`One of the following arguments is required for ${operationId}: ${group.join(', ')}`, {
131
- operationId,
132
- group,
133
- });
134
- }
135
- }
136
- for (const rule of requiredWhen) {
137
- if (!isRecord(rule))
138
- continue;
139
- const whenValue = args[rule.whenParam];
140
- let shouldRequire = false;
141
- if (Object.prototype.hasOwnProperty.call(rule, 'equals')) {
142
- shouldRequire = whenValue === rule.equals;
143
- }
144
- else if (Object.prototype.hasOwnProperty.call(rule, 'present')) {
145
- const present = rule.present === true;
146
- shouldRequire = present ? isPresent(whenValue) : !isPresent(whenValue);
147
- }
148
- else {
149
- shouldRequire = isPresent(whenValue);
150
- }
151
- if (shouldRequire && !isPresent(args[rule.param])) {
152
- invalidArgument(`Argument ${rule.param} is required by constraints for ${operationId}.`, {
153
- operationId,
154
- rule,
155
- });
156
- }
54
+ export async function listTools(provider) {
55
+ const bundle = await loadProviderBundle(provider);
56
+ const tools = bundle.tools;
57
+ if (!Array.isArray(tools)) {
58
+ throw new SuperDocCliError('Tool provider bundle is missing tools array.', {
59
+ code: 'TOOLS_ASSET_INVALID',
60
+ details: { provider },
61
+ });
157
62
  }
63
+ return tools;
64
+ }
65
+ /**
66
+ * Select all intent tools for a specific provider.
67
+ *
68
+ * Returns all intent tools in the requested provider format.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * const { tools } = await chooseTools({ provider: 'openai' });
73
+ * ```
74
+ */
75
+ export async function chooseTools(input) {
76
+ const bundle = await loadProviderBundle(input.provider);
77
+ const tools = Array.isArray(bundle.tools) ? bundle.tools : [];
78
+ return {
79
+ tools,
80
+ meta: {
81
+ provider: input.provider,
82
+ toolCount: tools.length,
83
+ },
84
+ };
158
85
  }
159
86
  function resolveDocApiMethod(client, operationId) {
160
87
  const tokens = operationId.split('.').slice(1);
@@ -176,126 +103,124 @@ function resolveDocApiMethod(client, operationId) {
176
103
  }
177
104
  return cursor;
178
105
  }
179
- export async function getToolCatalog() {
180
- return loadCatalog();
181
- }
182
- export async function listTools(provider) {
183
- const bundle = await loadProviderBundle(provider);
184
- const tools = bundle.tools;
185
- if (!Array.isArray(tools)) {
186
- throw new SuperDocCliError('Tool provider bundle is missing tools array.', {
187
- code: 'TOOLS_ASSET_INVALID',
188
- details: { provider },
189
- });
106
+ // Cached catalog instance — loaded once per process.
107
+ let _catalogCache = null;
108
+ async function getCachedCatalog() {
109
+ if (_catalogCache == null) {
110
+ _catalogCache = await loadCatalog();
190
111
  }
191
- return tools;
192
- }
193
- export async function resolveToolOperation(toolName) {
194
- const map = await loadToolNameMap();
195
- return typeof map[toolName] === 'string' ? map[toolName] : null;
112
+ return _catalogCache;
196
113
  }
197
114
  /**
198
- * Select tools for a specific provider.
199
- *
200
- * **mode='essential'** (default): Returns only essential tools + discover_tools.
201
- * Pass `groups` to additionally load all tools from those categories.
202
- *
203
- * **mode='all'**: Returns all tools from requested groups (or all groups if
204
- * `groups` is omitted). No discover_tools included by default.
115
+ * Validate tool arguments against the catalog schema.
205
116
  *
206
- * @example
207
- * ```ts
208
- * // Default: 5 essential tools + discover_tools
209
- * const { tools } = await chooseTools({ provider: 'openai' });
210
- *
211
- * // Essential + all comment tools
212
- * const { tools } = await chooseTools({ provider: 'openai', groups: ['comments'] });
213
- *
214
- * // All tools (old behavior)
215
- * const { tools } = await chooseTools({ provider: 'openai', mode: 'all' });
216
- * ```
117
+ * Checks three things in order:
118
+ * 1. No unknown keys (additionalProperties: false in merged schema)
119
+ * 2. All universally-required keys present (merged schema `required`)
120
+ * 3. All action-specific required keys present (per-operation `required`)
217
121
  */
218
- export async function chooseTools(input) {
219
- const catalog = await loadCatalog();
220
- const policy = loadPolicy();
221
- const mode = input.mode ?? policy.defaults.mode ?? 'essential';
222
- const includeDiscover = input.includeDiscoverTool ?? mode === 'essential';
223
- let selected;
224
- if (mode === 'essential') {
225
- // Essential tools + any explicitly requested groups
226
- const essentialNames = new Set(policy.essentialTools ?? []);
227
- const requestedGroups = input.groups ? new Set(input.groups) : null;
228
- selected = catalog.tools.filter((tool) => {
229
- if (essentialNames.has(tool.toolName))
230
- return true;
231
- if (requestedGroups && requestedGroups.has(tool.category))
232
- return true;
233
- return false;
122
+ function validateToolArgs(toolName, args, tool) {
123
+ const schema = tool.inputSchema;
124
+ const properties = isRecord(schema.properties) ? schema.properties : {};
125
+ const required = Array.isArray(schema.required) ? schema.required : [];
126
+ // 1. Reject unknown keys
127
+ const knownKeys = new Set(Object.keys(properties));
128
+ const unknownKeys = Object.keys(args).filter((k) => !knownKeys.has(k));
129
+ if (unknownKeys.length > 0) {
130
+ throw new SuperDocCliError(`Unknown argument(s) for ${toolName}: ${unknownKeys.join(', ')}`, {
131
+ code: 'INVALID_ARGUMENT',
132
+ details: { toolName, unknownKeys, knownKeys: [...knownKeys] },
234
133
  });
235
134
  }
236
- else {
237
- // mode='all': original behavior — filter by groups
238
- const alwaysInclude = new Set(policy.defaults.alwaysInclude ?? ['core']);
239
- let groups;
240
- if (input.groups) {
241
- groups = new Set([...input.groups, ...alwaysInclude]);
242
- }
243
- else {
244
- groups = new Set(policy.groups);
135
+ // 2. Reject missing universally-required keys
136
+ const missingKeys = required.filter((k) => args[k] == null);
137
+ if (missingKeys.length > 0) {
138
+ throw new SuperDocCliError(`Missing required argument(s) for ${toolName}: ${missingKeys.join(', ')}`, {
139
+ code: 'INVALID_ARGUMENT',
140
+ details: { toolName, missingKeys },
141
+ });
142
+ }
143
+ // 3. Reject missing per-operation required keys.
144
+ // For multi-action tools, resolve the operation by action; for single-op
145
+ // tools, use the sole operation entry.
146
+ const action = args.action;
147
+ let op;
148
+ if (typeof action === 'string' && tool.operations.length > 1) {
149
+ op = tool.operations.find((o) => o.intentAction === action);
150
+ }
151
+ else if (tool.operations.length === 1) {
152
+ op = tool.operations[0];
153
+ }
154
+ if (op) {
155
+ validateOperationRequired(toolName, action, args, op);
156
+ }
157
+ }
158
+ /**
159
+ * Check per-operation required constraints.
160
+ *
161
+ * Handles two shapes emitted by the codegen:
162
+ * - `required: string[]` — all listed keys must be present
163
+ * - `requiredOneOf: string[][]` — at least one branch must be fully satisfied
164
+ * (mirrors JSON Schema `oneOf` with per-branch `required` arrays)
165
+ */
166
+ function validateOperationRequired(toolName, action, args, op) {
167
+ const actionLabel = typeof action === 'string' ? ` action "${action}"` : '';
168
+ if (op.requiredOneOf && op.requiredOneOf.length > 0) {
169
+ const satisfied = op.requiredOneOf.some((branch) => branch.every((k) => args[k] != null));
170
+ if (!satisfied) {
171
+ const options = op.requiredOneOf.map((b) => b.join(' + ')).join(' | ');
172
+ throw new SuperDocCliError(`Missing required argument(s) for ${toolName}${actionLabel}: must provide one of: ${options}`, {
173
+ code: 'INVALID_ARGUMENT',
174
+ details: { toolName, action, requiredOneOf: op.requiredOneOf },
175
+ });
245
176
  }
246
- selected = catalog.tools.filter((tool) => groups.has(tool.category));
247
177
  }
248
- // Build provider-formatted tools from the provider bundle
249
- const bundle = await loadProviderBundle(input.provider);
250
- const providerTools = Array.isArray(bundle.tools) ? bundle.tools : [];
251
- const providerIndex = new Map(providerTools
252
- .filter((tool) => isRecord(tool))
253
- .map((tool) => [extractProviderToolName(tool), tool])
254
- .filter((entry) => entry[0] !== null));
255
- const selectedProviderTools = selected
256
- .map((tool) => providerIndex.get(tool.toolName))
257
- .filter((tool) => Boolean(tool));
258
- // Append discover_tools if requested
259
- if (includeDiscover) {
260
- const discoverTool = providerIndex.get('discover_tools');
261
- if (discoverTool) {
262
- selectedProviderTools.push(discoverTool);
178
+ else if (op.required && op.required.length > 0) {
179
+ const missingActionKeys = op.required.filter((k) => args[k] == null);
180
+ if (missingActionKeys.length > 0) {
181
+ throw new SuperDocCliError(`Missing required argument(s) for ${toolName}${actionLabel}: ${missingActionKeys.join(', ')}`, {
182
+ code: 'INVALID_ARGUMENT',
183
+ details: { toolName, action, missingKeys: missingActionKeys },
184
+ });
263
185
  }
264
186
  }
265
- const resolvedGroups = mode === 'essential' ? (input.groups ?? []) : (input.groups ?? policy.groups);
266
- return {
267
- tools: selectedProviderTools,
268
- selected: selected.map((tool) => ({
269
- operationId: tool.operationId,
270
- toolName: tool.toolName,
271
- category: tool.category,
272
- mutates: tool.mutates,
273
- })),
274
- meta: {
275
- provider: input.provider,
276
- mode,
277
- groups: [...resolvedGroups],
278
- selectedCount: selectedProviderTools.length,
279
- },
280
- };
281
187
  }
282
188
  export async function dispatchSuperDocTool(client, toolName, args = {}, invokeOptions) {
283
- const operationId = await resolveToolOperation(toolName);
284
- if (!operationId) {
285
- throw new SuperDocCliError(`Unknown SuperDoc tool: ${toolName}`, {
286
- code: 'TOOL_NOT_FOUND',
189
+ if (!isRecord(args)) {
190
+ throw new SuperDocCliError(`Tool arguments for ${toolName} must be an object.`, {
191
+ code: 'INVALID_ARGUMENT',
287
192
  details: { toolName },
288
193
  });
289
194
  }
290
- if (!isRecord(args)) {
291
- invalidArgument(`Tool arguments for ${toolName} must be an object.`);
195
+ // Validate against the tool schema before dispatch.
196
+ const catalog = await getCachedCatalog();
197
+ const tool = catalog.tools.find((t) => t.toolName === toolName);
198
+ if (tool == null) {
199
+ throw new SuperDocCliError(`Unknown tool: ${toolName}`, {
200
+ code: 'TOOL_DISPATCH_NOT_FOUND',
201
+ details: { toolName },
202
+ });
292
203
  }
204
+ validateToolArgs(toolName, args, tool);
293
205
  // Strip doc/sessionId — the SDK client manages session targeting after doc.open().
294
- // Models fill these in because the tool schemas expose them, but passing them
295
- // alongside an active session causes "stateless input.doc cannot be combined
296
- // with a session target" errors.
297
206
  const { doc: _doc, sessionId: _sid, ...cleanArgs } = args;
298
- validateDispatchArgs(operationId, cleanArgs);
299
- const method = resolveDocApiMethod(client, operationId);
300
- return method(cleanArgs, invokeOptions);
207
+ return dispatchIntentTool(toolName, cleanArgs, (operationId, input) => {
208
+ const method = resolveDocApiMethod(client, operationId);
209
+ return method(input, invokeOptions);
210
+ });
211
+ }
212
+ /**
213
+ * Read the bundled system prompt for intent tools.
214
+ */
215
+ export async function getSystemPrompt() {
216
+ const promptPath = path.join(toolsDir, 'system-prompt.md');
217
+ try {
218
+ return await readFile(promptPath, 'utf8');
219
+ }
220
+ catch {
221
+ throw new SuperDocCliError('System prompt not found.', {
222
+ code: 'TOOLS_ASSET_NOT_FOUND',
223
+ details: { filePath: promptPath },
224
+ });
225
+ }
301
226
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc-dev/sdk",
3
- "version": "1.0.0-alpha.43",
3
+ "version": "1.0.0-alpha.45",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -8,6 +8,7 @@
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
+ "bun": "./dist/index.js",
11
12
  "types": "./dist/index.d.ts",
12
13
  "import": "./dist/index.js",
13
14
  "require": "./dist/index.cjs"
@@ -25,11 +26,11 @@
25
26
  "typescript": "^5.9.2"
26
27
  },
27
28
  "optionalDependencies": {
28
- "@superdoc-dev/sdk-darwin-arm64": "1.0.0-alpha.43",
29
- "@superdoc-dev/sdk-darwin-x64": "1.0.0-alpha.43",
30
- "@superdoc-dev/sdk-linux-arm64": "1.0.0-alpha.43",
31
- "@superdoc-dev/sdk-linux-x64": "1.0.0-alpha.43",
32
- "@superdoc-dev/sdk-windows-x64": "1.0.0-alpha.43"
29
+ "@superdoc-dev/sdk-linux-arm64": "1.0.0-alpha.45",
30
+ "@superdoc-dev/sdk-linux-x64": "1.0.0-alpha.45",
31
+ "@superdoc-dev/sdk-darwin-arm64": "1.0.0-alpha.45",
32
+ "@superdoc-dev/sdk-windows-x64": "1.0.0-alpha.45",
33
+ "@superdoc-dev/sdk-darwin-x64": "1.0.0-alpha.45"
33
34
  },
34
35
  "publishConfig": {
35
36
  "access": "public"