@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/index.cjs +4 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/presets/legacy.cjs +297 -0
- package/dist/presets/legacy.d.ts +18 -0
- package/dist/presets/legacy.d.ts.map +1 -0
- package/dist/presets/legacy.js +291 -0
- package/dist/presets.cjs +60 -0
- package/dist/presets.d.ts +141 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +53 -0
- package/dist/tools.cjs +61 -291
- package/dist/tools.d.ts +51 -43
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +59 -290
- package/package.json +6 -6
- package/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/tools/__pycache__/intent_dispatch_generated.cpython-312.pyc +0 -0
package/dist/index.cjs
CHANGED
|
@@ -7,6 +7,7 @@ var errors = require('./runtime/errors.cjs');
|
|
|
7
7
|
var skills = require('./skills.cjs');
|
|
8
8
|
var tools = require('./tools.cjs');
|
|
9
9
|
var intentDispatch_generated = require('./generated/intent-dispatch.generated.cjs');
|
|
10
|
+
var presets = require('./presets.cjs');
|
|
10
11
|
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
12
13
|
// Session-bound runtime wrapper
|
|
@@ -172,6 +173,9 @@ exports.getSystemPromptForProvider = tools.getSystemPromptForProvider;
|
|
|
172
173
|
exports.getToolCatalog = tools.getToolCatalog;
|
|
173
174
|
exports.listTools = tools.listTools;
|
|
174
175
|
exports.dispatchIntentTool = intentDispatch_generated.dispatchIntentTool;
|
|
176
|
+
exports.DEFAULT_PRESET = presets.DEFAULT_PRESET;
|
|
177
|
+
exports.getPreset = presets.getPreset;
|
|
178
|
+
exports.listPresets = presets.listPresets;
|
|
175
179
|
exports.SuperDocClient = SuperDocClient;
|
|
176
180
|
exports.SuperDocDocument = SuperDocDocument;
|
|
177
181
|
exports.createSuperDocClient = createSuperDocClient;
|
package/dist/index.d.ts
CHANGED
|
@@ -81,8 +81,8 @@ export declare class SuperDocClient {
|
|
|
81
81
|
}
|
|
82
82
|
export declare function createSuperDocClient(options?: SuperDocClientOptions): SuperDocClient;
|
|
83
83
|
export { getSkill, installSkill, listSkills } from './skills.js';
|
|
84
|
-
export { chooseTools, dispatchSuperDocTool, getMcpPrompt, getSystemPrompt, getSystemPromptForProvider, getToolCatalog, listTools, } from './tools.js';
|
|
85
|
-
export type { AnthropicSystemPrompt, CacheStrategy, SystemPromptForProviderResult, ToolChooserInput, ToolProvider, } from './tools.js';
|
|
84
|
+
export { chooseTools, dispatchSuperDocTool, getMcpPrompt, getSystemPrompt, getSystemPromptForProvider, getToolCatalog, listTools, DEFAULT_PRESET, getPreset, listPresets, } from './tools.js';
|
|
85
|
+
export type { AnthropicSystemPrompt, CacheStrategy, SystemPromptForProviderResult, ToolCatalog, ToolCatalogEntry, ToolCatalogOperation, ToolChooserInput, ToolProvider, } from './tools.js';
|
|
86
86
|
export { dispatchIntentTool } from './generated/intent-dispatch.generated.js';
|
|
87
87
|
export { SuperDocCliError } from './runtime/errors.js';
|
|
88
88
|
export type { InvokeOptions, OperationSpec, OperationParamSpec, RuntimeInvoker, SuperDocClientOptions, } from './runtime/process.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -154,6 +154,6 @@ export function createSuperDocClient(options = {}) {
|
|
|
154
154
|
return new SuperDocClient(options);
|
|
155
155
|
}
|
|
156
156
|
export { getSkill, installSkill, listSkills } from './skills.js';
|
|
157
|
-
export { chooseTools, dispatchSuperDocTool, getMcpPrompt, getSystemPrompt, getSystemPromptForProvider, getToolCatalog, listTools, } from './tools.js';
|
|
157
|
+
export { chooseTools, dispatchSuperDocTool, getMcpPrompt, getSystemPrompt, getSystemPromptForProvider, getToolCatalog, listTools, DEFAULT_PRESET, getPreset, listPresets, } from './tools.js';
|
|
158
158
|
export { dispatchIntentTool } from './generated/intent-dispatch.generated.js';
|
|
159
159
|
export { SuperDocCliError } from './runtime/errors.js';
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var promises = require('node:fs/promises');
|
|
4
|
+
var path = require('node:path');
|
|
5
|
+
var node_url = require('node:url');
|
|
6
|
+
var errors = require('../runtime/errors.cjs');
|
|
7
|
+
var intentDispatch_generated = require('../generated/intent-dispatch.generated.cjs');
|
|
8
|
+
|
|
9
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
10
|
+
/**
|
|
11
|
+
* Legacy preset — wraps the existing codegen-emitted intent tools verbatim.
|
|
12
|
+
*
|
|
13
|
+
* The legacy preset is a read-through over the packaged tool artifacts in
|
|
14
|
+
* `packages/sdk/tools/` (catalog, per-provider tool JSON, system prompts) and
|
|
15
|
+
* delegates dispatch to the codegen-emitted `dispatchIntentTool`. It is the
|
|
16
|
+
* default preset returned by `chooseTools()` when callers omit `preset`.
|
|
17
|
+
*
|
|
18
|
+
* Nothing in this file relocates or rewrites the packaged artifacts. The whole
|
|
19
|
+
* point of the read-through wrapper is that running `generate:all` continues
|
|
20
|
+
* to refresh `packages/sdk/tools/*.json` in place; the legacy preset picks up
|
|
21
|
+
* the new files on the next call.
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
// Resolve tools directory relative to package root (works from both src/ and dist/)
|
|
26
|
+
const toolsDir = path.resolve(path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('presets/legacy.cjs', document.baseURI).href)))), '..', '..', 'tools');
|
|
27
|
+
const providerFileByName = {
|
|
28
|
+
openai: 'tools.openai.json',
|
|
29
|
+
anthropic: 'tools.anthropic.json',
|
|
30
|
+
vercel: 'tools.vercel.json',
|
|
31
|
+
generic: 'tools.generic.json',
|
|
32
|
+
};
|
|
33
|
+
const STRIP_EMPTY_OPTIONAL_ARGS = new Set(['parentId', 'parentCommentId', 'id', 'status']);
|
|
34
|
+
function isRecord(value) {
|
|
35
|
+
return typeof value === 'object' && value != null && !Array.isArray(value);
|
|
36
|
+
}
|
|
37
|
+
function isObviouslyCorruptedToolArgKey(key) {
|
|
38
|
+
const trimmed = key.trim();
|
|
39
|
+
return trimmed.length === 0 || !/[\p{L}\p{N}]/u.test(trimmed);
|
|
40
|
+
}
|
|
41
|
+
function stripCorruptedToolArgKeys(value) {
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
return value.map((item) => stripCorruptedToolArgKeys(item));
|
|
44
|
+
}
|
|
45
|
+
if (!isRecord(value))
|
|
46
|
+
return value;
|
|
47
|
+
const clean = {};
|
|
48
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
49
|
+
if (isObviouslyCorruptedToolArgKey(key))
|
|
50
|
+
continue;
|
|
51
|
+
clean[key] = stripCorruptedToolArgKeys(entryValue);
|
|
52
|
+
}
|
|
53
|
+
return clean;
|
|
54
|
+
}
|
|
55
|
+
async function readJson(fileName) {
|
|
56
|
+
const filePath = path.join(toolsDir, fileName);
|
|
57
|
+
let raw = '';
|
|
58
|
+
try {
|
|
59
|
+
raw = await promises.readFile(filePath, 'utf8');
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
throw new errors.SuperDocCliError('Unable to load packaged tool artifact.', {
|
|
63
|
+
code: 'TOOLS_ASSET_NOT_FOUND',
|
|
64
|
+
details: { filePath, message: error instanceof Error ? error.message : String(error) },
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(raw);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
throw new errors.SuperDocCliError('Packaged tool artifact is invalid JSON.', {
|
|
72
|
+
code: 'TOOLS_ASSET_INVALID',
|
|
73
|
+
details: { filePath, message: error instanceof Error ? error.message : String(error) },
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function readProviderTools(provider) {
|
|
78
|
+
return readJson(providerFileByName[provider]);
|
|
79
|
+
}
|
|
80
|
+
// Cached catalog instance — loaded once per process.
|
|
81
|
+
let _catalogCache = null;
|
|
82
|
+
async function getCachedCatalog() {
|
|
83
|
+
if (_catalogCache == null) {
|
|
84
|
+
_catalogCache = await readJson('catalog.json');
|
|
85
|
+
}
|
|
86
|
+
return _catalogCache;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Apply provider-specific caching markers to the tools array. Clones the last
|
|
90
|
+
* entry instead of mutating the input. Anthropic gets an explicit
|
|
91
|
+
* `cache_control` on the last tool; other providers pass through.
|
|
92
|
+
*/
|
|
93
|
+
function applyCacheMarkers(tools, provider, cacheRequested) {
|
|
94
|
+
if (!cacheRequested) {
|
|
95
|
+
return { tools, cacheStrategy: 'disabled' };
|
|
96
|
+
}
|
|
97
|
+
if (provider === 'anthropic') {
|
|
98
|
+
if (tools.length === 0)
|
|
99
|
+
return { tools, cacheStrategy: 'explicit' };
|
|
100
|
+
// Anthropic: marking the LAST tool with cache_control caches the entire
|
|
101
|
+
// tools block (and everything before it in the request — system prompt
|
|
102
|
+
// first if it also has cache_control). Shallow-spread the last entry so we
|
|
103
|
+
// don't mutate the cached tool list in place.
|
|
104
|
+
const next = tools.slice(0, -1);
|
|
105
|
+
const last = {
|
|
106
|
+
...tools[tools.length - 1],
|
|
107
|
+
cache_control: { type: 'ephemeral' },
|
|
108
|
+
};
|
|
109
|
+
next.push(last);
|
|
110
|
+
return { tools: next, cacheStrategy: 'explicit' };
|
|
111
|
+
}
|
|
112
|
+
if (provider === 'openai') {
|
|
113
|
+
// OpenAI caches prompts ≥ 1024 tokens automatically. No marker needed,
|
|
114
|
+
// but we still report cacheStrategy:'automatic' so callers can branch on
|
|
115
|
+
// it (e.g. for measurement).
|
|
116
|
+
return { tools, cacheStrategy: 'automatic' };
|
|
117
|
+
}
|
|
118
|
+
// vercel / generic — depends on underlying model.
|
|
119
|
+
return { tools, cacheStrategy: 'unsupported' };
|
|
120
|
+
}
|
|
121
|
+
function resolveDocApiMethod(documentHandle, operationId) {
|
|
122
|
+
const tokens = operationId.split('.').slice(1);
|
|
123
|
+
let cursor = documentHandle;
|
|
124
|
+
for (const token of tokens) {
|
|
125
|
+
if (!isRecord(cursor) || !(token in cursor)) {
|
|
126
|
+
throw new errors.SuperDocCliError(`No SDK doc method found for operation ${operationId}.`, {
|
|
127
|
+
code: 'TOOL_DISPATCH_NOT_FOUND',
|
|
128
|
+
details: { operationId, token },
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
cursor = cursor[token];
|
|
132
|
+
}
|
|
133
|
+
if (typeof cursor !== 'function') {
|
|
134
|
+
throw new errors.SuperDocCliError(`Resolved member for ${operationId} is not callable.`, {
|
|
135
|
+
code: 'TOOL_DISPATCH_NOT_FOUND',
|
|
136
|
+
details: { operationId },
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return cursor;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Validate tool arguments against the catalog schema.
|
|
143
|
+
*
|
|
144
|
+
* Checks three things in order:
|
|
145
|
+
* 1. No unknown keys (additionalProperties: false in merged schema)
|
|
146
|
+
* 2. All universally-required keys present (merged schema `required`)
|
|
147
|
+
* 3. All action-specific required keys present (per-operation `required`)
|
|
148
|
+
*/
|
|
149
|
+
function validateToolArgs(toolName, args, tool) {
|
|
150
|
+
const schema = tool.inputSchema;
|
|
151
|
+
const properties = isRecord(schema.properties) ? schema.properties : {};
|
|
152
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
153
|
+
// 1. Reject unknown keys (additionalProperties: false in merged schema)
|
|
154
|
+
const knownKeys = new Set(Object.keys(properties));
|
|
155
|
+
const unknownKeys = Object.keys(args).filter((k) => !knownKeys.has(k));
|
|
156
|
+
if (unknownKeys.length > 0) {
|
|
157
|
+
throw new errors.SuperDocCliError(`Unknown argument(s) for ${toolName}: ${unknownKeys.join(', ')}`, {
|
|
158
|
+
code: 'INVALID_ARGUMENT',
|
|
159
|
+
details: { toolName, unknownKeys, knownKeys: [...knownKeys] },
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// 2. Reject missing universally-required keys (merged schema `required`)
|
|
163
|
+
const missingKeys = required.filter((k) => args[k] == null);
|
|
164
|
+
if (missingKeys.length > 0) {
|
|
165
|
+
throw new errors.SuperDocCliError(`Missing required argument(s) for ${toolName}: ${missingKeys.join(', ')}`, {
|
|
166
|
+
code: 'INVALID_ARGUMENT',
|
|
167
|
+
details: { toolName, missingKeys },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// 3. Reject missing per-operation required keys. For multi-action tools,
|
|
171
|
+
// resolve the operation by action; for single-op tools, use the sole entry.
|
|
172
|
+
const action = args.action;
|
|
173
|
+
let op;
|
|
174
|
+
if (typeof action === 'string' && tool.operations.length > 1) {
|
|
175
|
+
op = tool.operations.find((o) => o.intentAction === action);
|
|
176
|
+
}
|
|
177
|
+
else if (tool.operations.length === 1) {
|
|
178
|
+
op = tool.operations[0];
|
|
179
|
+
}
|
|
180
|
+
if (op) {
|
|
181
|
+
validateOperationRequired(toolName, action, args, op);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Check per-operation required constraints. Handles both flat `required: string[]`
|
|
186
|
+
* and discriminated `requiredOneOf: string[][]` shapes emitted by codegen.
|
|
187
|
+
*/
|
|
188
|
+
function validateOperationRequired(toolName, action, args, op) {
|
|
189
|
+
const actionLabel = typeof action === 'string' ? ` action "${action}"` : '';
|
|
190
|
+
if (op.requiredOneOf && op.requiredOneOf.length > 0) {
|
|
191
|
+
const satisfied = op.requiredOneOf.some((branch) => branch.every((k) => args[k] != null));
|
|
192
|
+
if (!satisfied) {
|
|
193
|
+
const options = op.requiredOneOf.map((b) => b.join(' + ')).join(' | ');
|
|
194
|
+
throw new errors.SuperDocCliError(`Missing required argument(s) for ${toolName}${actionLabel}: must provide one of: ${options}`, {
|
|
195
|
+
code: 'INVALID_ARGUMENT',
|
|
196
|
+
details: { toolName, action, requiredOneOf: op.requiredOneOf },
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else if (op.required && op.required.length > 0) {
|
|
201
|
+
const missingActionKeys = op.required.filter((k) => args[k] == null);
|
|
202
|
+
if (missingActionKeys.length > 0) {
|
|
203
|
+
throw new errors.SuperDocCliError(`Missing required argument(s) for ${toolName}${actionLabel}: ${missingActionKeys.join(', ')}`, {
|
|
204
|
+
code: 'INVALID_ARGUMENT',
|
|
205
|
+
details: { toolName, action, missingKeys: missingActionKeys },
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function legacyGetTools(provider, options) {
|
|
211
|
+
const { tools } = await readProviderTools(provider);
|
|
212
|
+
// Fail fast on malformed provider artifacts so agents don't silently boot
|
|
213
|
+
// with zero tools. Matches the pre-presets behavior of the public
|
|
214
|
+
// `listTools` path (TOOLS_ASSET_INVALID).
|
|
215
|
+
if (!Array.isArray(tools)) {
|
|
216
|
+
throw new errors.SuperDocCliError('Tool provider bundle is missing tools array.', {
|
|
217
|
+
code: 'TOOLS_ASSET_INVALID',
|
|
218
|
+
details: { provider },
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return applyCacheMarkers(tools, provider, options?.cache === true);
|
|
222
|
+
}
|
|
223
|
+
async function legacyGetCatalog() {
|
|
224
|
+
return getCachedCatalog();
|
|
225
|
+
}
|
|
226
|
+
async function legacyGetSystemPrompt() {
|
|
227
|
+
const promptPath = path.join(toolsDir, 'system-prompt.md');
|
|
228
|
+
try {
|
|
229
|
+
return await promises.readFile(promptPath, 'utf8');
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
throw new errors.SuperDocCliError('System prompt not found.', {
|
|
233
|
+
code: 'TOOLS_ASSET_NOT_FOUND',
|
|
234
|
+
details: { filePath: promptPath },
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async function legacyGetMcpPrompt() {
|
|
239
|
+
const promptPath = path.join(toolsDir, 'system-prompt-mcp.md');
|
|
240
|
+
try {
|
|
241
|
+
return await promises.readFile(promptPath, 'utf8');
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
throw new errors.SuperDocCliError('MCP system prompt not found.', {
|
|
245
|
+
code: 'TOOLS_ASSET_NOT_FOUND',
|
|
246
|
+
details: { filePath: promptPath },
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async function legacyDispatch(documentHandle, toolName, args, invokeOptions) {
|
|
251
|
+
if (!isRecord(args)) {
|
|
252
|
+
throw new errors.SuperDocCliError(`Tool arguments for ${toolName} must be an object.`, {
|
|
253
|
+
code: 'INVALID_ARGUMENT',
|
|
254
|
+
details: { toolName },
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
const sanitizedArgs = stripCorruptedToolArgKeys(args);
|
|
258
|
+
if (!isRecord(sanitizedArgs)) {
|
|
259
|
+
throw new errors.SuperDocCliError(`Tool arguments for ${toolName} must be an object.`, {
|
|
260
|
+
code: 'INVALID_ARGUMENT',
|
|
261
|
+
details: { toolName },
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
const catalog = await getCachedCatalog();
|
|
265
|
+
const tool = catalog.tools.find((t) => t.toolName === toolName);
|
|
266
|
+
if (tool == null) {
|
|
267
|
+
throw new errors.SuperDocCliError(`Unknown tool: ${toolName}`, {
|
|
268
|
+
code: 'TOOL_DISPATCH_NOT_FOUND',
|
|
269
|
+
details: { toolName },
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
validateToolArgs(toolName, sanitizedArgs, tool);
|
|
273
|
+
// Strip empty strings for known optional ID/enum params that LLMs fill with ""
|
|
274
|
+
// instead of omitting. Only target params where "" is never a valid value.
|
|
275
|
+
const cleanArgs = {};
|
|
276
|
+
for (const [key, value] of Object.entries(sanitizedArgs)) {
|
|
277
|
+
if (value === '' && STRIP_EMPTY_OPTIONAL_ARGS.has(key))
|
|
278
|
+
continue;
|
|
279
|
+
cleanArgs[key] = value;
|
|
280
|
+
}
|
|
281
|
+
return intentDispatch_generated.dispatchIntentTool(toolName, cleanArgs, (operationId, input) => {
|
|
282
|
+
const method = resolveDocApiMethod(documentHandle, operationId);
|
|
283
|
+
return method(input, invokeOptions);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
const legacyPreset = {
|
|
287
|
+
id: 'legacy',
|
|
288
|
+
description: 'Codegen-emitted intent tools (default). Wraps packages/sdk/tools/ artifacts verbatim.',
|
|
289
|
+
supportsCacheControl: true,
|
|
290
|
+
getTools: legacyGetTools,
|
|
291
|
+
getCatalog: legacyGetCatalog,
|
|
292
|
+
getSystemPrompt: legacyGetSystemPrompt,
|
|
293
|
+
getMcpPrompt: legacyGetMcpPrompt,
|
|
294
|
+
dispatch: legacyDispatch,
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
exports.legacyPreset = legacyPreset;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy preset — wraps the existing codegen-emitted intent tools verbatim.
|
|
3
|
+
*
|
|
4
|
+
* The legacy preset is a read-through over the packaged tool artifacts in
|
|
5
|
+
* `packages/sdk/tools/` (catalog, per-provider tool JSON, system prompts) and
|
|
6
|
+
* delegates dispatch to the codegen-emitted `dispatchIntentTool`. It is the
|
|
7
|
+
* default preset returned by `chooseTools()` when callers omit `preset`.
|
|
8
|
+
*
|
|
9
|
+
* Nothing in this file relocates or rewrites the packaged artifacts. The whole
|
|
10
|
+
* point of the read-through wrapper is that running `generate:all` continues
|
|
11
|
+
* to refresh `packages/sdk/tools/*.json` in place; the legacy preset picks up
|
|
12
|
+
* the new files on the next call.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
import type { PresetDescriptor } from '../presets.js';
|
|
17
|
+
export declare const legacyPreset: PresetDescriptor;
|
|
18
|
+
//# sourceMappingURL=legacy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy.d.ts","sourceRoot":"","sources":["../../src/presets/legacy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AASH,OAAO,KAAK,EAGV,gBAAgB,EAKjB,MAAM,eAAe,CAAC;AAqTvB,eAAO,MAAM,YAAY,EAAE,gBAU1B,CAAC"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy preset — wraps the existing codegen-emitted intent tools verbatim.
|
|
3
|
+
*
|
|
4
|
+
* The legacy preset is a read-through over the packaged tool artifacts in
|
|
5
|
+
* `packages/sdk/tools/` (catalog, per-provider tool JSON, system prompts) and
|
|
6
|
+
* delegates dispatch to the codegen-emitted `dispatchIntentTool`. It is the
|
|
7
|
+
* default preset returned by `chooseTools()` when callers omit `preset`.
|
|
8
|
+
*
|
|
9
|
+
* Nothing in this file relocates or rewrites the packaged artifacts. The whole
|
|
10
|
+
* point of the read-through wrapper is that running `generate:all` continues
|
|
11
|
+
* to refresh `packages/sdk/tools/*.json` in place; the legacy preset picks up
|
|
12
|
+
* the new files on the next call.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
import { readFile } from 'node:fs/promises';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
import { SuperDocCliError } from '../runtime/errors.js';
|
|
20
|
+
import { dispatchIntentTool } from '../generated/intent-dispatch.generated.js';
|
|
21
|
+
// Resolve tools directory relative to package root (works from both src/ and dist/)
|
|
22
|
+
const toolsDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'tools');
|
|
23
|
+
const providerFileByName = {
|
|
24
|
+
openai: 'tools.openai.json',
|
|
25
|
+
anthropic: 'tools.anthropic.json',
|
|
26
|
+
vercel: 'tools.vercel.json',
|
|
27
|
+
generic: 'tools.generic.json',
|
|
28
|
+
};
|
|
29
|
+
const STRIP_EMPTY_OPTIONAL_ARGS = new Set(['parentId', 'parentCommentId', 'id', 'status']);
|
|
30
|
+
function isRecord(value) {
|
|
31
|
+
return typeof value === 'object' && value != null && !Array.isArray(value);
|
|
32
|
+
}
|
|
33
|
+
function isObviouslyCorruptedToolArgKey(key) {
|
|
34
|
+
const trimmed = key.trim();
|
|
35
|
+
return trimmed.length === 0 || !/[\p{L}\p{N}]/u.test(trimmed);
|
|
36
|
+
}
|
|
37
|
+
function stripCorruptedToolArgKeys(value) {
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
return value.map((item) => stripCorruptedToolArgKeys(item));
|
|
40
|
+
}
|
|
41
|
+
if (!isRecord(value))
|
|
42
|
+
return value;
|
|
43
|
+
const clean = {};
|
|
44
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
45
|
+
if (isObviouslyCorruptedToolArgKey(key))
|
|
46
|
+
continue;
|
|
47
|
+
clean[key] = stripCorruptedToolArgKeys(entryValue);
|
|
48
|
+
}
|
|
49
|
+
return clean;
|
|
50
|
+
}
|
|
51
|
+
async function readJson(fileName) {
|
|
52
|
+
const filePath = path.join(toolsDir, fileName);
|
|
53
|
+
let raw = '';
|
|
54
|
+
try {
|
|
55
|
+
raw = await readFile(filePath, 'utf8');
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
throw new SuperDocCliError('Unable to load packaged tool artifact.', {
|
|
59
|
+
code: 'TOOLS_ASSET_NOT_FOUND',
|
|
60
|
+
details: { filePath, message: error instanceof Error ? error.message : String(error) },
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(raw);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new SuperDocCliError('Packaged tool artifact is invalid JSON.', {
|
|
68
|
+
code: 'TOOLS_ASSET_INVALID',
|
|
69
|
+
details: { filePath, message: error instanceof Error ? error.message : String(error) },
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function readProviderTools(provider) {
|
|
74
|
+
return readJson(providerFileByName[provider]);
|
|
75
|
+
}
|
|
76
|
+
// Cached catalog instance — loaded once per process.
|
|
77
|
+
let _catalogCache = null;
|
|
78
|
+
async function getCachedCatalog() {
|
|
79
|
+
if (_catalogCache == null) {
|
|
80
|
+
_catalogCache = await readJson('catalog.json');
|
|
81
|
+
}
|
|
82
|
+
return _catalogCache;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Apply provider-specific caching markers to the tools array. Clones the last
|
|
86
|
+
* entry instead of mutating the input. Anthropic gets an explicit
|
|
87
|
+
* `cache_control` on the last tool; other providers pass through.
|
|
88
|
+
*/
|
|
89
|
+
function applyCacheMarkers(tools, provider, cacheRequested) {
|
|
90
|
+
if (!cacheRequested) {
|
|
91
|
+
return { tools, cacheStrategy: 'disabled' };
|
|
92
|
+
}
|
|
93
|
+
if (provider === 'anthropic') {
|
|
94
|
+
if (tools.length === 0)
|
|
95
|
+
return { tools, cacheStrategy: 'explicit' };
|
|
96
|
+
// Anthropic: marking the LAST tool with cache_control caches the entire
|
|
97
|
+
// tools block (and everything before it in the request — system prompt
|
|
98
|
+
// first if it also has cache_control). Shallow-spread the last entry so we
|
|
99
|
+
// don't mutate the cached tool list in place.
|
|
100
|
+
const next = tools.slice(0, -1);
|
|
101
|
+
const last = {
|
|
102
|
+
...tools[tools.length - 1],
|
|
103
|
+
cache_control: { type: 'ephemeral' },
|
|
104
|
+
};
|
|
105
|
+
next.push(last);
|
|
106
|
+
return { tools: next, cacheStrategy: 'explicit' };
|
|
107
|
+
}
|
|
108
|
+
if (provider === 'openai') {
|
|
109
|
+
// OpenAI caches prompts ≥ 1024 tokens automatically. No marker needed,
|
|
110
|
+
// but we still report cacheStrategy:'automatic' so callers can branch on
|
|
111
|
+
// it (e.g. for measurement).
|
|
112
|
+
return { tools, cacheStrategy: 'automatic' };
|
|
113
|
+
}
|
|
114
|
+
// vercel / generic — depends on underlying model.
|
|
115
|
+
return { tools, cacheStrategy: 'unsupported' };
|
|
116
|
+
}
|
|
117
|
+
function resolveDocApiMethod(documentHandle, operationId) {
|
|
118
|
+
const tokens = operationId.split('.').slice(1);
|
|
119
|
+
let cursor = documentHandle;
|
|
120
|
+
for (const token of tokens) {
|
|
121
|
+
if (!isRecord(cursor) || !(token in cursor)) {
|
|
122
|
+
throw new SuperDocCliError(`No SDK doc method found for operation ${operationId}.`, {
|
|
123
|
+
code: 'TOOL_DISPATCH_NOT_FOUND',
|
|
124
|
+
details: { operationId, token },
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
cursor = cursor[token];
|
|
128
|
+
}
|
|
129
|
+
if (typeof cursor !== 'function') {
|
|
130
|
+
throw new SuperDocCliError(`Resolved member for ${operationId} is not callable.`, {
|
|
131
|
+
code: 'TOOL_DISPATCH_NOT_FOUND',
|
|
132
|
+
details: { operationId },
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return cursor;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Validate tool arguments against the catalog schema.
|
|
139
|
+
*
|
|
140
|
+
* Checks three things in order:
|
|
141
|
+
* 1. No unknown keys (additionalProperties: false in merged schema)
|
|
142
|
+
* 2. All universally-required keys present (merged schema `required`)
|
|
143
|
+
* 3. All action-specific required keys present (per-operation `required`)
|
|
144
|
+
*/
|
|
145
|
+
function validateToolArgs(toolName, args, tool) {
|
|
146
|
+
const schema = tool.inputSchema;
|
|
147
|
+
const properties = isRecord(schema.properties) ? schema.properties : {};
|
|
148
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
149
|
+
// 1. Reject unknown keys (additionalProperties: false in merged schema)
|
|
150
|
+
const knownKeys = new Set(Object.keys(properties));
|
|
151
|
+
const unknownKeys = Object.keys(args).filter((k) => !knownKeys.has(k));
|
|
152
|
+
if (unknownKeys.length > 0) {
|
|
153
|
+
throw new SuperDocCliError(`Unknown argument(s) for ${toolName}: ${unknownKeys.join(', ')}`, {
|
|
154
|
+
code: 'INVALID_ARGUMENT',
|
|
155
|
+
details: { toolName, unknownKeys, knownKeys: [...knownKeys] },
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
// 2. Reject missing universally-required keys (merged schema `required`)
|
|
159
|
+
const missingKeys = required.filter((k) => args[k] == null);
|
|
160
|
+
if (missingKeys.length > 0) {
|
|
161
|
+
throw new SuperDocCliError(`Missing required argument(s) for ${toolName}: ${missingKeys.join(', ')}`, {
|
|
162
|
+
code: 'INVALID_ARGUMENT',
|
|
163
|
+
details: { toolName, missingKeys },
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
// 3. Reject missing per-operation required keys. For multi-action tools,
|
|
167
|
+
// resolve the operation by action; for single-op tools, use the sole entry.
|
|
168
|
+
const action = args.action;
|
|
169
|
+
let op;
|
|
170
|
+
if (typeof action === 'string' && tool.operations.length > 1) {
|
|
171
|
+
op = tool.operations.find((o) => o.intentAction === action);
|
|
172
|
+
}
|
|
173
|
+
else if (tool.operations.length === 1) {
|
|
174
|
+
op = tool.operations[0];
|
|
175
|
+
}
|
|
176
|
+
if (op) {
|
|
177
|
+
validateOperationRequired(toolName, action, args, op);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Check per-operation required constraints. Handles both flat `required: string[]`
|
|
182
|
+
* and discriminated `requiredOneOf: string[][]` shapes emitted by codegen.
|
|
183
|
+
*/
|
|
184
|
+
function validateOperationRequired(toolName, action, args, op) {
|
|
185
|
+
const actionLabel = typeof action === 'string' ? ` action "${action}"` : '';
|
|
186
|
+
if (op.requiredOneOf && op.requiredOneOf.length > 0) {
|
|
187
|
+
const satisfied = op.requiredOneOf.some((branch) => branch.every((k) => args[k] != null));
|
|
188
|
+
if (!satisfied) {
|
|
189
|
+
const options = op.requiredOneOf.map((b) => b.join(' + ')).join(' | ');
|
|
190
|
+
throw new SuperDocCliError(`Missing required argument(s) for ${toolName}${actionLabel}: must provide one of: ${options}`, {
|
|
191
|
+
code: 'INVALID_ARGUMENT',
|
|
192
|
+
details: { toolName, action, requiredOneOf: op.requiredOneOf },
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else if (op.required && op.required.length > 0) {
|
|
197
|
+
const missingActionKeys = op.required.filter((k) => args[k] == null);
|
|
198
|
+
if (missingActionKeys.length > 0) {
|
|
199
|
+
throw new SuperDocCliError(`Missing required argument(s) for ${toolName}${actionLabel}: ${missingActionKeys.join(', ')}`, {
|
|
200
|
+
code: 'INVALID_ARGUMENT',
|
|
201
|
+
details: { toolName, action, missingKeys: missingActionKeys },
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async function legacyGetTools(provider, options) {
|
|
207
|
+
const { tools } = await readProviderTools(provider);
|
|
208
|
+
// Fail fast on malformed provider artifacts so agents don't silently boot
|
|
209
|
+
// with zero tools. Matches the pre-presets behavior of the public
|
|
210
|
+
// `listTools` path (TOOLS_ASSET_INVALID).
|
|
211
|
+
if (!Array.isArray(tools)) {
|
|
212
|
+
throw new SuperDocCliError('Tool provider bundle is missing tools array.', {
|
|
213
|
+
code: 'TOOLS_ASSET_INVALID',
|
|
214
|
+
details: { provider },
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return applyCacheMarkers(tools, provider, options?.cache === true);
|
|
218
|
+
}
|
|
219
|
+
async function legacyGetCatalog() {
|
|
220
|
+
return getCachedCatalog();
|
|
221
|
+
}
|
|
222
|
+
async function legacyGetSystemPrompt() {
|
|
223
|
+
const promptPath = path.join(toolsDir, 'system-prompt.md');
|
|
224
|
+
try {
|
|
225
|
+
return await readFile(promptPath, 'utf8');
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
throw new SuperDocCliError('System prompt not found.', {
|
|
229
|
+
code: 'TOOLS_ASSET_NOT_FOUND',
|
|
230
|
+
details: { filePath: promptPath },
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function legacyGetMcpPrompt() {
|
|
235
|
+
const promptPath = path.join(toolsDir, 'system-prompt-mcp.md');
|
|
236
|
+
try {
|
|
237
|
+
return await readFile(promptPath, 'utf8');
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
throw new SuperDocCliError('MCP system prompt not found.', {
|
|
241
|
+
code: 'TOOLS_ASSET_NOT_FOUND',
|
|
242
|
+
details: { filePath: promptPath },
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async function legacyDispatch(documentHandle, toolName, args, invokeOptions) {
|
|
247
|
+
if (!isRecord(args)) {
|
|
248
|
+
throw new SuperDocCliError(`Tool arguments for ${toolName} must be an object.`, {
|
|
249
|
+
code: 'INVALID_ARGUMENT',
|
|
250
|
+
details: { toolName },
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
const sanitizedArgs = stripCorruptedToolArgKeys(args);
|
|
254
|
+
if (!isRecord(sanitizedArgs)) {
|
|
255
|
+
throw new SuperDocCliError(`Tool arguments for ${toolName} must be an object.`, {
|
|
256
|
+
code: 'INVALID_ARGUMENT',
|
|
257
|
+
details: { toolName },
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
const catalog = await getCachedCatalog();
|
|
261
|
+
const tool = catalog.tools.find((t) => t.toolName === toolName);
|
|
262
|
+
if (tool == null) {
|
|
263
|
+
throw new SuperDocCliError(`Unknown tool: ${toolName}`, {
|
|
264
|
+
code: 'TOOL_DISPATCH_NOT_FOUND',
|
|
265
|
+
details: { toolName },
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
validateToolArgs(toolName, sanitizedArgs, tool);
|
|
269
|
+
// Strip empty strings for known optional ID/enum params that LLMs fill with ""
|
|
270
|
+
// instead of omitting. Only target params where "" is never a valid value.
|
|
271
|
+
const cleanArgs = {};
|
|
272
|
+
for (const [key, value] of Object.entries(sanitizedArgs)) {
|
|
273
|
+
if (value === '' && STRIP_EMPTY_OPTIONAL_ARGS.has(key))
|
|
274
|
+
continue;
|
|
275
|
+
cleanArgs[key] = value;
|
|
276
|
+
}
|
|
277
|
+
return dispatchIntentTool(toolName, cleanArgs, (operationId, input) => {
|
|
278
|
+
const method = resolveDocApiMethod(documentHandle, operationId);
|
|
279
|
+
return method(input, invokeOptions);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
export const legacyPreset = {
|
|
283
|
+
id: 'legacy',
|
|
284
|
+
description: 'Codegen-emitted intent tools (default). Wraps packages/sdk/tools/ artifacts verbatim.',
|
|
285
|
+
supportsCacheControl: true,
|
|
286
|
+
getTools: legacyGetTools,
|
|
287
|
+
getCatalog: legacyGetCatalog,
|
|
288
|
+
getSystemPrompt: legacyGetSystemPrompt,
|
|
289
|
+
getMcpPrompt: legacyGetMcpPrompt,
|
|
290
|
+
dispatch: legacyDispatch,
|
|
291
|
+
};
|