@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/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
|
-
|
|
4
|
-
export
|
|
5
|
-
|
|
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
|
|
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
|
-
* //
|
|
57
|
-
* const { tools } = await chooseTools({ provider: 'openai',
|
|
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
|
-
*
|
|
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
|
-
*
|
|
72
|
-
*
|
|
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
|
|
84
|
+
* Read the packaged SDK system prompt (default preset: legacy).
|
|
77
85
|
*
|
|
78
|
-
*
|
|
79
|
-
* suitable for embedded LLM usage (OpenAI, Anthropic, Vercel APIs).
|
|
80
|
-
*
|
|
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
|
|
92
|
+
* Read the packaged MCP system prompt for intent tools (default preset: legacy).
|
|
85
93
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
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
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"
|
|
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
|
-
*
|
|
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
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
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
|
-
* //
|
|
97
|
-
* const { tools } = await chooseTools({ provider: 'openai',
|
|
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
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
*
|
|
51
|
+
* Return the raw tool array for a provider from a preset (default: legacy).
|
|
177
52
|
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
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
|
|
184
|
-
const
|
|
185
|
-
|
|
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
|
-
*
|
|
64
|
+
* Dispatch a tool call against a bound document handle using the default
|
|
65
|
+
* preset (`legacy`).
|
|
221
66
|
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
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
|
-
*
|
|
253
|
-
*
|
|
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
|
-
|
|
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
|
|
80
|
+
* Read the packaged SDK system prompt (default preset: legacy).
|
|
294
81
|
*
|
|
295
|
-
*
|
|
296
|
-
* suitable for embedded LLM usage (OpenAI, Anthropic, Vercel APIs).
|
|
297
|
-
*
|
|
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
|
-
|
|
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
|
|
90
|
+
* Read the packaged MCP system prompt for intent tools (default preset: legacy).
|
|
313
91
|
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
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
|
-
|
|
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.
|
|
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-
|
|
30
|
-
"@superdoc-dev/sdk-darwin-
|
|
31
|
-
"@superdoc-dev/sdk-linux-
|
|
32
|
-
"@superdoc-dev/sdk-
|
|
33
|
-
"@superdoc-dev/sdk-
|
|
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"
|
|
Binary file
|
|
Binary file
|