@superdoc-dev/sdk 1.0.0-alpha.30 → 1.0.0-alpha.32

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.js CHANGED
@@ -80,24 +80,10 @@ async function loadToolNameMap() {
80
80
  async function loadCatalog() {
81
81
  return readJson('catalog.json');
82
82
  }
83
- function normalizeFeatures(features) {
84
- return {
85
- hasTables: Boolean(features?.hasTables),
86
- hasLists: Boolean(features?.hasLists),
87
- hasComments: Boolean(features?.hasComments),
88
- hasTrackedChanges: Boolean(features?.hasTrackedChanges),
89
- isEmptyDocument: Boolean(features?.isEmptyDocument),
90
- };
91
- }
92
- function stableSortByPhasePriority(entries, priorityOrder) {
93
- const priority = new Map(priorityOrder.map((category, index) => [category, index]));
94
- return [...entries].sort((a, b) => {
95
- const aPriority = priority.get(a.category) ?? Number.MAX_SAFE_INTEGER;
96
- const bPriority = priority.get(b.category) ?? Number.MAX_SAFE_INTEGER;
97
- if (aPriority !== bPriority)
98
- return aPriority - bPriority;
99
- return a.toolName.localeCompare(b.toolName);
100
- });
83
+ /** All available tool groups from the policy. */
84
+ export function getAvailableGroups() {
85
+ const policy = loadPolicy();
86
+ return policy.groups;
101
87
  }
102
88
  const OPERATION_INDEX = Object.fromEntries(Object.entries(CONTRACT.operations).map(([id, op]) => [id, op]));
103
89
  function validateDispatchArgs(operationId, args) {
@@ -190,26 +176,16 @@ function resolveDocApiMethod(client, operationId) {
190
176
  }
191
177
  return cursor;
192
178
  }
193
- export async function getToolCatalog(options = {}) {
194
- const catalog = await loadCatalog();
195
- if (!options.profile)
196
- return catalog;
197
- return {
198
- ...catalog,
199
- profiles: {
200
- intent: options.profile === 'intent' ? catalog.profiles.intent : { name: 'intent', tools: [] },
201
- operation: options.profile === 'operation' ? catalog.profiles.operation : { name: 'operation', tools: [] },
202
- },
203
- };
179
+ export async function getToolCatalog() {
180
+ return loadCatalog();
204
181
  }
205
- export async function listTools(provider, options = {}) {
206
- const profile = options.profile ?? 'intent';
182
+ export async function listTools(provider) {
207
183
  const bundle = await loadProviderBundle(provider);
208
- const tools = bundle.profiles[profile];
184
+ const tools = bundle.tools;
209
185
  if (!Array.isArray(tools)) {
210
- throw new SuperDocCliError('Tool provider bundle is missing profile tools.', {
186
+ throw new SuperDocCliError('Tool provider bundle is missing tools array.', {
211
187
  code: 'TOOLS_ASSET_INVALID',
212
- details: { provider, profile },
188
+ details: { provider },
213
189
  });
214
190
  }
215
191
  return tools;
@@ -218,110 +194,60 @@ export async function resolveToolOperation(toolName) {
218
194
  const map = await loadToolNameMap();
219
195
  return typeof map[toolName] === 'string' ? map[toolName] : null;
220
196
  }
221
- export function inferDocumentFeatures(infoResult) {
222
- if (!isRecord(infoResult)) {
223
- return {
224
- hasTables: false,
225
- hasLists: false,
226
- hasComments: false,
227
- hasTrackedChanges: false,
228
- isEmptyDocument: false,
229
- };
230
- }
231
- const counts = isRecord(infoResult.counts) ? infoResult.counts : {};
232
- const words = typeof counts.words === 'number' ? counts.words : 0;
233
- const paragraphs = typeof counts.paragraphs === 'number' ? counts.paragraphs : 0;
234
- const tables = typeof counts.tables === 'number' ? counts.tables : 0;
235
- const comments = typeof counts.comments === 'number' ? counts.comments : 0;
236
- const lists = typeof counts.lists === 'number' ? counts.lists : typeof counts.listItems === 'number' ? counts.listItems : 0;
237
- const trackedChanges = typeof counts.trackedChanges === 'number'
238
- ? counts.trackedChanges
239
- : typeof counts.tracked_changes === 'number'
240
- ? counts.tracked_changes
241
- : 0;
242
- return {
243
- hasTables: tables > 0,
244
- hasLists: lists > 0,
245
- hasComments: comments > 0,
246
- hasTrackedChanges: trackedChanges > 0,
247
- isEmptyDocument: words === 0 && paragraphs <= 1,
248
- };
249
- }
197
+ /**
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.
205
+ *
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
+ * ```
217
+ */
250
218
  export async function chooseTools(input) {
251
219
  const catalog = await loadCatalog();
252
220
  const policy = loadPolicy();
253
- const profile = input.profile ?? 'intent';
254
- const phase = input.taskContext?.phase ?? 'read';
255
- const phasePolicy = policy.phases[phase];
256
- const featureMap = normalizeFeatures(input.documentFeatures);
257
- const maxTools = Math.max(1, input.budget?.maxTools ?? policy.defaults.maxToolsByProfile[profile]);
258
- const minReadTools = Math.max(0, input.budget?.minReadTools ?? policy.defaults.minReadTools);
259
- const includeCategories = new Set(input.policy?.includeCategories ?? phasePolicy.include);
260
- const excludeCategories = new Set([...(input.policy?.excludeCategories ?? []), ...phasePolicy.exclude]);
261
- const allowMutatingTools = input.policy?.allowMutatingTools ?? phase === 'mutate';
262
- const excluded = [];
263
- const profileTools = catalog.profiles[profile].tools;
264
- const indexByToolName = new Map(profileTools.map((tool) => [tool.toolName, tool]));
265
- let candidates = profileTools.filter((tool) => {
266
- if (tool.requiredCapabilities.some((capability) => !featureMap[capability])) {
267
- excluded.push({ toolName: tool.toolName, reason: 'missing-required-capability' });
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;
268
233
  return false;
269
- }
270
- if (!allowMutatingTools && tool.mutates) {
271
- excluded.push({ toolName: tool.toolName, reason: 'mutations-disabled' });
272
- return false;
273
- }
274
- if (includeCategories.size > 0 && !includeCategories.has(tool.category)) {
275
- excluded.push({ toolName: tool.toolName, reason: 'category-not-included' });
276
- return false;
277
- }
278
- if (excludeCategories.has(tool.category)) {
279
- excluded.push({ toolName: tool.toolName, reason: 'phase-category-excluded' });
280
- return false;
281
- }
282
- return true;
283
- });
284
- const forceExclude = new Set(input.policy?.forceExclude ?? []);
285
- candidates = candidates.filter((tool) => {
286
- if (!forceExclude.has(tool.toolName))
287
- return true;
288
- excluded.push({ toolName: tool.toolName, reason: 'force-excluded' });
289
- return false;
290
- });
291
- // Resolve forceInclude tools — these are guaranteed slots exempt from budget trimming.
292
- const forcedToolNames = new Set(input.policy?.forceInclude ?? []);
293
- const forcedTools = [];
294
- for (const forcedToolName of forcedToolNames) {
295
- const forced = indexByToolName.get(forcedToolName);
296
- if (!forced) {
297
- excluded.push({ toolName: forcedToolName, reason: 'not-in-profile' });
298
- continue;
299
- }
300
- candidates.push(forced);
301
- forcedTools.push(forced);
302
- }
303
- candidates = [...new Map(candidates.map((tool) => [tool.toolName, tool])).values()];
304
- // Start with forceInclude tools — they always occupy a slot.
305
- const selected = [...forcedTools];
306
- const selectedNames = new Set(selected.map((tool) => tool.toolName));
307
- const foundationalIds = new Set(policy.defaults.foundationalOperationIds);
308
- const foundational = candidates.filter((tool) => foundationalIds.has(tool.operationId) && !selectedNames.has(tool.toolName));
309
- for (const tool of foundational) {
310
- if (selected.length >= minReadTools || selected.length >= maxTools)
311
- break;
312
- selected.push(tool);
313
- selectedNames.add(tool.toolName);
234
+ });
314
235
  }
315
- const remaining = stableSortByPhasePriority(candidates.filter((tool) => !selectedNames.has(tool.toolName)), phasePolicy.priority);
316
- for (const tool of remaining) {
317
- if (selected.length >= maxTools) {
318
- excluded.push({ toolName: tool.toolName, reason: 'budget-trim' });
319
- continue;
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]);
320
242
  }
321
- selected.push(tool);
243
+ else {
244
+ groups = new Set(policy.groups);
245
+ }
246
+ selected = catalog.tools.filter((tool) => groups.has(tool.category));
322
247
  }
248
+ // Build provider-formatted tools from the provider bundle
323
249
  const bundle = await loadProviderBundle(input.provider);
324
- const providerTools = Array.isArray(bundle.profiles[profile]) ? bundle.profiles[profile] : [];
250
+ const providerTools = Array.isArray(bundle.tools) ? bundle.tools : [];
325
251
  const providerIndex = new Map(providerTools
326
252
  .filter((tool) => isRecord(tool))
327
253
  .map((tool) => [extractProviderToolName(tool), tool])
@@ -329,6 +255,14 @@ export async function chooseTools(input) {
329
255
  const selectedProviderTools = selected
330
256
  .map((tool) => providerIndex.get(tool.toolName))
331
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);
263
+ }
264
+ }
265
+ const resolvedGroups = mode === 'essential' ? (input.groups ?? []) : (input.groups ?? policy.groups);
332
266
  return {
333
267
  tools: selectedProviderTools,
334
268
  selected: selected.map((tool) => ({
@@ -336,17 +270,12 @@ export async function chooseTools(input) {
336
270
  toolName: tool.toolName,
337
271
  category: tool.category,
338
272
  mutates: tool.mutates,
339
- profile: tool.profile,
340
273
  })),
341
- excluded,
342
- selectionMeta: {
343
- profile,
344
- phase,
345
- maxTools,
346
- minReadTools,
347
- selectedCount: selected.length,
348
- decisionVersion: policy.defaults.chooserDecisionVersion,
274
+ meta: {
349
275
  provider: input.provider,
276
+ mode,
277
+ groups: [...resolvedGroups],
278
+ selectedCount: selectedProviderTools.length,
350
279
  },
351
280
  };
352
281
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc-dev/sdk",
3
- "version": "1.0.0-alpha.30",
3
+ "version": "1.0.0-alpha.32",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -25,11 +25,11 @@
25
25
  "typescript": "^5.9.2"
26
26
  },
27
27
  "optionalDependencies": {
28
- "@superdoc-dev/sdk-darwin-arm64": "1.0.0-alpha.30",
29
- "@superdoc-dev/sdk-linux-x64": "1.0.0-alpha.30",
30
- "@superdoc-dev/sdk-linux-arm64": "1.0.0-alpha.30",
31
- "@superdoc-dev/sdk-darwin-x64": "1.0.0-alpha.30",
32
- "@superdoc-dev/sdk-windows-x64": "1.0.0-alpha.30"
28
+ "@superdoc-dev/sdk-darwin-arm64": "1.0.0-alpha.32",
29
+ "@superdoc-dev/sdk-darwin-x64": "1.0.0-alpha.32",
30
+ "@superdoc-dev/sdk-linux-arm64": "1.0.0-alpha.32",
31
+ "@superdoc-dev/sdk-linux-x64": "1.0.0-alpha.32",
32
+ "@superdoc-dev/sdk-windows-x64": "1.0.0-alpha.32"
33
33
  },
34
34
  "publishConfig": {
35
35
  "access": "public"