@khanglvm/outline-cli 0.1.1

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.
@@ -0,0 +1,2346 @@
1
+ import { CliError } from "./errors.js";
2
+
3
+ const TYPES = {
4
+ string: (v) => typeof v === "string",
5
+ number: (v) => typeof v === "number" && Number.isFinite(v),
6
+ boolean: (v) => typeof v === "boolean",
7
+ object: (v) => !!v && typeof v === "object" && !Array.isArray(v),
8
+ array: (v) => Array.isArray(v),
9
+ null: (v) => v === null,
10
+ "string[]": (v) => Array.isArray(v) && v.every((x) => typeof x === "string"),
11
+ "string|string[]": (v) => typeof v === "string" || (Array.isArray(v) && v.every((x) => typeof x === "string")),
12
+ };
13
+
14
+ function fail(tool, issues) {
15
+ throw new CliError(`Invalid args for ${tool}`, {
16
+ code: "ARG_VALIDATION_FAILED",
17
+ tool,
18
+ issues,
19
+ });
20
+ }
21
+
22
+ function ensureObject(tool, args) {
23
+ if (!args || typeof args !== "object" || Array.isArray(args)) {
24
+ fail(tool, [{ path: "args", message: "must be an object" }]);
25
+ }
26
+ }
27
+
28
+ function validateSpec(tool, args, spec) {
29
+ const issues = [];
30
+ const properties = spec.properties || {};
31
+
32
+ if (!spec.allowUnknown) {
33
+ const allowed = new Set(Object.keys(properties));
34
+ allowed.add("compact");
35
+ for (const key of Object.keys(args)) {
36
+ if (!allowed.has(key)) {
37
+ issues.push({
38
+ path: `args.${key}`,
39
+ message: "is not allowed",
40
+ });
41
+ }
42
+ }
43
+ }
44
+
45
+ for (const key of spec.required || []) {
46
+ if (args[key] === undefined) {
47
+ issues.push({ path: `args.${key}`, message: "is required" });
48
+ }
49
+ }
50
+
51
+ for (const [key, rule] of Object.entries(properties)) {
52
+ const value = args[key];
53
+ if (value === undefined) {
54
+ continue;
55
+ }
56
+
57
+ const types = Array.isArray(rule.type) ? rule.type : [rule.type];
58
+ const match = types.some((type) => TYPES[type] && TYPES[type](value));
59
+ if (!match) {
60
+ issues.push({
61
+ path: `args.${key}`,
62
+ message: `must be ${types.join(" or ")}`,
63
+ });
64
+ continue;
65
+ }
66
+
67
+ if (rule.enum && !rule.enum.includes(value)) {
68
+ issues.push({
69
+ path: `args.${key}`,
70
+ message: `must be one of: ${rule.enum.join(", ")}`,
71
+ });
72
+ continue;
73
+ }
74
+
75
+ if (rule.min != null && typeof value === "number" && value < rule.min) {
76
+ issues.push({ path: `args.${key}`, message: `must be >= ${rule.min}` });
77
+ }
78
+ }
79
+
80
+ if (typeof spec.custom === "function") {
81
+ spec.custom(args, issues);
82
+ }
83
+
84
+ if (issues.length > 0) {
85
+ fail(tool, issues);
86
+ }
87
+ }
88
+
89
+ const SHARED_DOC_COMMON = {
90
+ collectionId: { type: "string" },
91
+ userId: { type: "string" },
92
+ statusFilter: { type: ["string", "string[]"] },
93
+ view: { type: "string", enum: ["ids", "summary", "full"] },
94
+ };
95
+
96
+ const DATA_ATTRIBUTE_DATA_TYPES = ["string", "number", "boolean", "list"];
97
+ const USER_ROLE_TYPES = ["admin", "member", "viewer", "guest"];
98
+
99
+ export const TOOL_ARG_SCHEMAS = {
100
+ "api.call": {
101
+ properties: {
102
+ method: { type: "string" },
103
+ endpoint: { type: "string" },
104
+ body: { type: "object" },
105
+ includePolicies: { type: "boolean" },
106
+ maxAttempts: { type: "number", min: 1 },
107
+ select: { type: ["string", "string[]"] },
108
+ performAction: { type: "boolean" },
109
+ readToken: { type: "string" },
110
+ },
111
+ custom(args, issues) {
112
+ if (!args.method && !args.endpoint) {
113
+ issues.push({ path: "args.method", message: "or args.endpoint is required" });
114
+ }
115
+ },
116
+ },
117
+ "auth.info": {
118
+ properties: {
119
+ includePolicies: { type: "boolean" },
120
+ view: { type: "string", enum: ["summary", "full"] },
121
+ },
122
+ },
123
+ "documents.search": {
124
+ properties: {
125
+ query: { type: "string" },
126
+ queries: { type: "string[]" },
127
+ mode: { type: "string", enum: ["semantic", "titles"] },
128
+ limit: { type: "number", min: 1 },
129
+ offset: { type: "number", min: 0 },
130
+ documentId: { type: "string" },
131
+ shareId: { type: "string" },
132
+ snippetMinWords: { type: "number", min: 1 },
133
+ snippetMaxWords: { type: "number", min: 1 },
134
+ sort: { type: "string" },
135
+ direction: { type: "string", enum: ["ASC", "DESC"] },
136
+ includePolicies: { type: "boolean" },
137
+ merge: { type: "boolean" },
138
+ concurrency: { type: "number", min: 1 },
139
+ ...SHARED_DOC_COMMON,
140
+ },
141
+ custom(args, issues) {
142
+ if (!args.query && !args.queries) {
143
+ issues.push({ path: "args.query", message: "or args.queries[] is required" });
144
+ }
145
+ },
146
+ },
147
+ "documents.list": {
148
+ properties: {
149
+ limit: { type: "number", min: 1 },
150
+ offset: { type: "number", min: 0 },
151
+ sort: { type: "string" },
152
+ direction: { type: "string", enum: ["ASC", "DESC"] },
153
+ parentDocumentId: { type: ["string", "null"] },
154
+ backlinkDocumentId: { type: "string" },
155
+ includePolicies: { type: "boolean" },
156
+ ...SHARED_DOC_COMMON,
157
+ },
158
+ },
159
+ "documents.backlinks": {
160
+ required: ["id"],
161
+ properties: {
162
+ id: { type: "string" },
163
+ limit: { type: "number", min: 1 },
164
+ offset: { type: "number", min: 0 },
165
+ sort: { type: "string" },
166
+ direction: { type: "string", enum: ["ASC", "DESC"] },
167
+ view: { type: "string", enum: ["ids", "summary", "full"] },
168
+ includePolicies: { type: "boolean" },
169
+ maxAttempts: { type: "number", min: 1 },
170
+ },
171
+ custom(args, issues) {
172
+ if (typeof args.id === "string" && args.id.trim().length === 0) {
173
+ issues.push({ path: "args.id", message: "must be a non-empty string" });
174
+ }
175
+ if (typeof args.limit === "number" && args.limit > 250) {
176
+ issues.push({ path: "args.limit", message: "must be <= 250" });
177
+ }
178
+ },
179
+ },
180
+ "documents.graph_neighbors": {
181
+ properties: {
182
+ id: { type: "string" },
183
+ ids: { type: "string[]" },
184
+ includeBacklinks: { type: "boolean" },
185
+ includeSearchNeighbors: { type: "boolean" },
186
+ searchQueries: { type: "string[]" },
187
+ limitPerSource: { type: "number", min: 1 },
188
+ view: { type: "string", enum: ["ids", "summary", "full"] },
189
+ maxAttempts: { type: "number", min: 1 },
190
+ },
191
+ custom(args, issues) {
192
+ const hasId = typeof args.id === "string" && args.id.trim().length > 0;
193
+ const hasIds = Array.isArray(args.ids) && args.ids.length > 0;
194
+
195
+ if (!hasId && !hasIds) {
196
+ issues.push({ path: "args.id", message: "or args.ids[] is required" });
197
+ }
198
+ if (Array.isArray(args.ids) && args.ids.length === 0) {
199
+ issues.push({ path: "args.ids", message: "must be a non-empty string[] when provided" });
200
+ }
201
+ if (hasId && Array.isArray(args.ids)) {
202
+ issues.push({ path: "args.ids", message: "cannot be combined with args.id" });
203
+ }
204
+ if (args.includeBacklinks === false && args.includeSearchNeighbors === false) {
205
+ issues.push({
206
+ path: "args.includeBacklinks",
207
+ message: "and includeSearchNeighbors cannot both be false",
208
+ });
209
+ }
210
+ if (Array.isArray(args.searchQueries)) {
211
+ if (args.searchQueries.length === 0) {
212
+ issues.push({ path: "args.searchQueries", message: "must be a non-empty string[] when provided" });
213
+ }
214
+ for (let i = 0; i < args.searchQueries.length; i += 1) {
215
+ if (typeof args.searchQueries[i] === "string" && args.searchQueries[i].trim().length === 0) {
216
+ issues.push({ path: `args.searchQueries[${i}]`, message: "must be a non-empty string" });
217
+ }
218
+ }
219
+ if (args.includeSearchNeighbors === false) {
220
+ issues.push({
221
+ path: "args.includeSearchNeighbors",
222
+ message: "must be true when args.searchQueries is provided",
223
+ });
224
+ }
225
+ }
226
+ if (typeof args.limitPerSource === "number" && args.limitPerSource > 100) {
227
+ issues.push({ path: "args.limitPerSource", message: "must be <= 100" });
228
+ }
229
+ },
230
+ },
231
+ "documents.graph_report": {
232
+ required: ["seedIds"],
233
+ properties: {
234
+ seedIds: { type: "string[]" },
235
+ depth: { type: "number", min: 0 },
236
+ maxNodes: { type: "number", min: 1 },
237
+ includeBacklinks: { type: "boolean" },
238
+ includeSearchNeighbors: { type: "boolean" },
239
+ limitPerSource: { type: "number", min: 1 },
240
+ view: { type: "string", enum: ["ids", "summary", "full"] },
241
+ maxAttempts: { type: "number", min: 1 },
242
+ },
243
+ custom(args, issues) {
244
+ if (Array.isArray(args.seedIds) && args.seedIds.length === 0) {
245
+ issues.push({ path: "args.seedIds", message: "must be a non-empty string[]" });
246
+ }
247
+ if (Array.isArray(args.seedIds)) {
248
+ for (let i = 0; i < args.seedIds.length; i += 1) {
249
+ if (typeof args.seedIds[i] === "string" && args.seedIds[i].trim().length === 0) {
250
+ issues.push({ path: `args.seedIds[${i}]`, message: "must be a non-empty string" });
251
+ }
252
+ }
253
+ }
254
+ if (typeof args.depth === "number" && args.depth > 6) {
255
+ issues.push({ path: "args.depth", message: "must be <= 6" });
256
+ }
257
+ if (typeof args.maxNodes === "number" && args.maxNodes > 500) {
258
+ issues.push({ path: "args.maxNodes", message: "must be <= 500" });
259
+ }
260
+ if (typeof args.limitPerSource === "number" && args.limitPerSource > 100) {
261
+ issues.push({ path: "args.limitPerSource", message: "must be <= 100" });
262
+ }
263
+ if (args.includeBacklinks === false && args.includeSearchNeighbors === false) {
264
+ issues.push({
265
+ path: "args.includeBacklinks",
266
+ message: "and includeSearchNeighbors cannot both be false",
267
+ });
268
+ }
269
+ },
270
+ },
271
+ "documents.issue_refs": {
272
+ properties: {
273
+ id: { type: "string" },
274
+ ids: { type: "string[]" },
275
+ issueDomains: { type: "string[]" },
276
+ keyPattern: { type: "string" },
277
+ view: { type: "string", enum: ["ids", "summary", "full"] },
278
+ maxAttempts: { type: "number", min: 1 },
279
+ },
280
+ custom(args, issues) {
281
+ const hasId = typeof args.id === "string" && args.id.trim().length > 0;
282
+ const hasIds = Array.isArray(args.ids) && args.ids.length > 0;
283
+
284
+ if (!hasId && !hasIds) {
285
+ issues.push({ path: "args.id", message: "or args.ids[] is required" });
286
+ }
287
+ if (typeof args.id === "string" && args.id.trim().length === 0) {
288
+ issues.push({ path: "args.id", message: "must be a non-empty string" });
289
+ }
290
+ if (Array.isArray(args.ids)) {
291
+ if (args.ids.length === 0) {
292
+ issues.push({ path: "args.ids", message: "must be a non-empty string[] when provided" });
293
+ }
294
+ for (let i = 0; i < args.ids.length; i += 1) {
295
+ if (typeof args.ids[i] === "string" && args.ids[i].trim().length === 0) {
296
+ issues.push({ path: `args.ids[${i}]`, message: "must be a non-empty string" });
297
+ }
298
+ }
299
+ }
300
+ if (Array.isArray(args.issueDomains)) {
301
+ if (args.issueDomains.length === 0) {
302
+ issues.push({ path: "args.issueDomains", message: "must be a non-empty string[] when provided" });
303
+ }
304
+ for (let i = 0; i < args.issueDomains.length; i += 1) {
305
+ if (
306
+ typeof args.issueDomains[i] === "string" &&
307
+ args.issueDomains[i].trim().length === 0
308
+ ) {
309
+ issues.push({ path: `args.issueDomains[${i}]`, message: "must be a non-empty string" });
310
+ }
311
+ }
312
+ }
313
+ if (typeof args.keyPattern === "string") {
314
+ if (args.keyPattern.trim().length === 0) {
315
+ issues.push({ path: "args.keyPattern", message: "must be a non-empty string when provided" });
316
+ } else {
317
+ try {
318
+ // eslint-disable-next-line no-new
319
+ new RegExp(args.keyPattern.trim(), "g");
320
+ } catch {
321
+ issues.push({ path: "args.keyPattern", message: "must be a valid regex pattern" });
322
+ }
323
+ }
324
+ }
325
+ },
326
+ },
327
+ "documents.issue_ref_report": {
328
+ properties: {
329
+ query: { type: "string" },
330
+ queries: { type: "string[]" },
331
+ collectionId: { type: "string" },
332
+ issueDomains: { type: "string[]" },
333
+ keyPattern: { type: "string" },
334
+ limit: { type: "number", min: 1 },
335
+ view: { type: "string", enum: ["ids", "summary", "full"] },
336
+ maxAttempts: { type: "number", min: 1 },
337
+ },
338
+ custom(args, issues) {
339
+ const hasQuery = typeof args.query === "string" && args.query.trim().length > 0;
340
+ const hasQueries = Array.isArray(args.queries) && args.queries.length > 0;
341
+
342
+ if (!hasQuery && !hasQueries) {
343
+ issues.push({ path: "args.query", message: "or args.queries[] is required" });
344
+ }
345
+ if (typeof args.query === "string" && args.query.trim().length === 0) {
346
+ issues.push({ path: "args.query", message: "must be a non-empty string when provided" });
347
+ }
348
+ if (Array.isArray(args.queries)) {
349
+ if (args.queries.length === 0) {
350
+ issues.push({ path: "args.queries", message: "must be a non-empty string[] when provided" });
351
+ }
352
+ for (let i = 0; i < args.queries.length; i += 1) {
353
+ if (typeof args.queries[i] === "string" && args.queries[i].trim().length === 0) {
354
+ issues.push({ path: `args.queries[${i}]`, message: "must be a non-empty string" });
355
+ }
356
+ }
357
+ }
358
+ if (typeof args.collectionId === "string" && args.collectionId.trim().length === 0) {
359
+ issues.push({ path: "args.collectionId", message: "must be a non-empty string when provided" });
360
+ }
361
+ if (Array.isArray(args.issueDomains)) {
362
+ if (args.issueDomains.length === 0) {
363
+ issues.push({ path: "args.issueDomains", message: "must be a non-empty string[] when provided" });
364
+ }
365
+ for (let i = 0; i < args.issueDomains.length; i += 1) {
366
+ if (
367
+ typeof args.issueDomains[i] === "string" &&
368
+ args.issueDomains[i].trim().length === 0
369
+ ) {
370
+ issues.push({ path: `args.issueDomains[${i}]`, message: "must be a non-empty string" });
371
+ }
372
+ }
373
+ }
374
+ if (typeof args.keyPattern === "string") {
375
+ if (args.keyPattern.trim().length === 0) {
376
+ issues.push({ path: "args.keyPattern", message: "must be a non-empty string when provided" });
377
+ } else {
378
+ try {
379
+ // eslint-disable-next-line no-new
380
+ new RegExp(args.keyPattern.trim(), "g");
381
+ } catch {
382
+ issues.push({ path: "args.keyPattern", message: "must be a valid regex pattern" });
383
+ }
384
+ }
385
+ }
386
+ if (typeof args.limit === "number" && args.limit > 100) {
387
+ issues.push({ path: "args.limit", message: "must be <= 100" });
388
+ }
389
+ },
390
+ },
391
+ "documents.info": {
392
+ properties: {
393
+ id: { type: "string" },
394
+ ids: { type: "string[]" },
395
+ shareId: { type: "string" },
396
+ includePolicies: { type: "boolean" },
397
+ concurrency: { type: "number", min: 1 },
398
+ view: { type: "string", enum: ["summary", "full"] },
399
+ armDelete: { type: "boolean" },
400
+ readTokenTtlSeconds: { type: "number", min: 60 },
401
+ },
402
+ custom(args, issues) {
403
+ if (!args.id && !args.ids && !args.shareId) {
404
+ issues.push({ path: "args.id", message: "or args.ids[] or args.shareId is required" });
405
+ }
406
+ },
407
+ },
408
+ "documents.create": {
409
+ properties: {
410
+ id: { type: "string" },
411
+ title: { type: "string" },
412
+ text: { type: "string" },
413
+ icon: { type: "string" },
414
+ color: { type: "string" },
415
+ collectionId: { type: "string" },
416
+ parentDocumentId: { type: "string" },
417
+ templateId: { type: "string" },
418
+ publish: { type: "boolean" },
419
+ fullWidth: { type: "boolean" },
420
+ dataAttributes: { type: "array" },
421
+ view: { type: "string", enum: ["summary", "full"] },
422
+ },
423
+ },
424
+ "documents.update": {
425
+ required: ["id"],
426
+ properties: {
427
+ id: { type: "string" },
428
+ title: { type: "string" },
429
+ text: { type: "string" },
430
+ icon: { type: "string" },
431
+ color: { type: "string" },
432
+ fullWidth: { type: "boolean" },
433
+ templateId: { type: "string" },
434
+ collectionId: { type: "string" },
435
+ insightsEnabled: { type: "boolean" },
436
+ editMode: { type: "string", enum: ["replace", "append", "prepend"] },
437
+ publish: { type: "boolean" },
438
+ dataAttributes: { type: "array" },
439
+ view: { type: "string", enum: ["summary", "full"] },
440
+ performAction: { type: "boolean" },
441
+ },
442
+ },
443
+ "collections.list": {
444
+ properties: {
445
+ query: { type: "string" },
446
+ limit: { type: "number", min: 1 },
447
+ offset: { type: "number", min: 0 },
448
+ sort: { type: "string" },
449
+ direction: { type: "string", enum: ["ASC", "DESC"] },
450
+ statusFilter: { type: ["string", "string[]"] },
451
+ includePolicies: { type: "boolean" },
452
+ view: { type: "string", enum: ["summary", "full"] },
453
+ },
454
+ },
455
+ "collections.info": {
456
+ properties: {
457
+ id: { type: "string" },
458
+ ids: { type: "string[]" },
459
+ includePolicies: { type: "boolean" },
460
+ concurrency: { type: "number", min: 1 },
461
+ view: { type: "string", enum: ["summary", "full"] },
462
+ },
463
+ custom(args, issues) {
464
+ if (!args.id && !args.ids) {
465
+ issues.push({ path: "args.id", message: "or args.ids[] is required" });
466
+ }
467
+ },
468
+ },
469
+ "collections.create": {
470
+ required: ["name"],
471
+ properties: {
472
+ name: { type: "string" },
473
+ description: { type: "string" },
474
+ permission: { type: "string" },
475
+ icon: { type: "string" },
476
+ color: { type: "string" },
477
+ sharing: { type: "boolean" },
478
+ view: { type: "string", enum: ["summary", "full"] },
479
+ },
480
+ },
481
+ "collections.update": {
482
+ required: ["id"],
483
+ properties: {
484
+ id: { type: "string" },
485
+ name: { type: "string" },
486
+ description: { type: "string" },
487
+ permission: { type: "string" },
488
+ icon: { type: "string" },
489
+ color: { type: "string" },
490
+ sharing: { type: "boolean" },
491
+ view: { type: "string", enum: ["summary", "full"] },
492
+ performAction: { type: "boolean" },
493
+ },
494
+ },
495
+ "documents.resolve": {
496
+ properties: {
497
+ query: { type: "string" },
498
+ queries: { type: "string[]" },
499
+ collectionId: { type: "string" },
500
+ limit: { type: "number", min: 1 },
501
+ strict: { type: "boolean" },
502
+ strictThreshold: { type: "number", min: 0 },
503
+ view: { type: "string", enum: ["ids", "summary", "full"] },
504
+ concurrency: { type: "number", min: 1 },
505
+ snippetMinWords: { type: "number", min: 1 },
506
+ snippetMaxWords: { type: "number", min: 1 },
507
+ excerptChars: { type: "number", min: 1 },
508
+ forceGroupedResult: { type: "boolean" },
509
+ maxAttempts: { type: "number", min: 1 },
510
+ },
511
+ custom(args, issues) {
512
+ if (!args.query && !args.queries) {
513
+ issues.push({ path: "args.query", message: "or args.queries[] is required" });
514
+ }
515
+ if (typeof args.strictThreshold === "number" && args.strictThreshold > 1) {
516
+ issues.push({ path: "args.strictThreshold", message: "must be <= 1" });
517
+ }
518
+ },
519
+ },
520
+ "documents.resolve_urls": {
521
+ properties: {
522
+ url: { type: "string" },
523
+ urls: { type: "string[]" },
524
+ collectionId: { type: "string" },
525
+ limit: { type: "number", min: 1 },
526
+ strict: { type: "boolean" },
527
+ strictHost: { type: "boolean" },
528
+ strictThreshold: { type: "number", min: 0 },
529
+ view: { type: "string", enum: ["ids", "summary", "full"] },
530
+ concurrency: { type: "number", min: 1 },
531
+ snippetMinWords: { type: "number", min: 1 },
532
+ snippetMaxWords: { type: "number", min: 1 },
533
+ excerptChars: { type: "number", min: 1 },
534
+ forceGroupedResult: { type: "boolean" },
535
+ maxAttempts: { type: "number", min: 1 },
536
+ },
537
+ custom(args, issues) {
538
+ if (!args.url && !args.urls) {
539
+ issues.push({ path: "args.url", message: "or args.urls[] is required" });
540
+ }
541
+ if (typeof args.strictThreshold === "number" && args.strictThreshold > 1) {
542
+ issues.push({ path: "args.strictThreshold", message: "must be <= 1" });
543
+ }
544
+ },
545
+ },
546
+ "documents.canonicalize_candidates": {
547
+ properties: {
548
+ query: { type: "string" },
549
+ queries: { type: "string[]" },
550
+ ids: { type: "string[]" },
551
+ collectionId: { type: "string" },
552
+ limit: { type: "number", min: 1 },
553
+ strict: { type: "boolean" },
554
+ strictThreshold: { type: "number", min: 0 },
555
+ titleSimilarityThreshold: { type: "number", min: 0 },
556
+ view: { type: "string", enum: ["ids", "summary", "full"] },
557
+ concurrency: { type: "number", min: 1 },
558
+ hydrateConcurrency: { type: "number", min: 1 },
559
+ snippetMinWords: { type: "number", min: 1 },
560
+ snippetMaxWords: { type: "number", min: 1 },
561
+ excerptChars: { type: "number", min: 1 },
562
+ maxAttempts: { type: "number", min: 1 },
563
+ },
564
+ custom(args, issues) {
565
+ if (!args.query && !args.queries && !args.ids) {
566
+ issues.push({ path: "args.query", message: "or args.queries[] or args.ids[] is required" });
567
+ }
568
+ if (Array.isArray(args.ids) && args.ids.length === 0) {
569
+ issues.push({ path: "args.ids", message: "must be a non-empty string[] when provided" });
570
+ }
571
+ if (typeof args.strictThreshold === "number" && args.strictThreshold > 1) {
572
+ issues.push({ path: "args.strictThreshold", message: "must be <= 1" });
573
+ }
574
+ if (typeof args.titleSimilarityThreshold === "number" && args.titleSimilarityThreshold > 1) {
575
+ issues.push({ path: "args.titleSimilarityThreshold", message: "must be <= 1" });
576
+ }
577
+ },
578
+ },
579
+ "collections.tree": {
580
+ required: ["collectionId"],
581
+ properties: {
582
+ collectionId: { type: "string" },
583
+ includeDrafts: { type: "boolean" },
584
+ maxDepth: { type: "number", min: 0 },
585
+ view: { type: "string", enum: ["summary", "full"] },
586
+ pageSize: { type: "number", min: 1 },
587
+ maxPages: { type: "number", min: 1 },
588
+ statusFilter: { type: ["string", "string[]"] },
589
+ sort: { type: "string" },
590
+ direction: { type: "string", enum: ["ASC", "DESC"] },
591
+ maxAttempts: { type: "number", min: 1 },
592
+ },
593
+ },
594
+ "search.expand": {
595
+ properties: {
596
+ query: { type: "string" },
597
+ queries: { type: "string[]" },
598
+ mode: { type: "string", enum: ["semantic", "titles"] },
599
+ limit: { type: "number", min: 1 },
600
+ expandLimit: { type: "number", min: 1 },
601
+ offset: { type: "number", min: 0 },
602
+ view: { type: "string", enum: ["ids", "summary", "full"] },
603
+ concurrency: { type: "number", min: 1 },
604
+ hydrateConcurrency: { type: "number", min: 1 },
605
+ collectionId: { type: "string" },
606
+ documentId: { type: "string" },
607
+ userId: { type: "string" },
608
+ sort: { type: "string" },
609
+ direction: { type: "string", enum: ["ASC", "DESC"] },
610
+ snippetMinWords: { type: "number", min: 1 },
611
+ snippetMaxWords: { type: "number", min: 1 },
612
+ contextChars: { type: "number", min: 1 },
613
+ excerptChars: { type: "number", min: 1 },
614
+ forceGroupedResult: { type: "boolean" },
615
+ maxAttempts: { type: "number", min: 1 },
616
+ },
617
+ custom(args, issues) {
618
+ if (!args.query && !args.queries) {
619
+ issues.push({ path: "args.query", message: "or args.queries[] is required" });
620
+ }
621
+ },
622
+ },
623
+ "search.research": {
624
+ properties: {
625
+ question: { type: "string" },
626
+ query: { type: "string" },
627
+ queries: { type: "string[]" },
628
+ collectionId: { type: "string" },
629
+ limitPerQuery: { type: "number", min: 1 },
630
+ offset: { type: "number", min: 0 },
631
+ includeTitleSearch: { type: "boolean" },
632
+ includeSemanticSearch: { type: "boolean" },
633
+ precisionMode: { type: "string", enum: ["balanced", "precision", "recall"] },
634
+ minScore: { type: "number", min: 0 },
635
+ diversify: { type: "boolean" },
636
+ diversityLambda: { type: "number", min: 0 },
637
+ rrfK: { type: "number", min: 1 },
638
+ expandLimit: { type: "number", min: 1 },
639
+ maxDocuments: { type: "number", min: 1 },
640
+ seenIds: { type: "string[]" },
641
+ view: { type: "string", enum: ["ids", "summary", "full"] },
642
+ perQueryView: { type: "string", enum: ["ids", "summary", "full"] },
643
+ perQueryHitLimit: { type: "number", min: 1 },
644
+ evidencePerDocument: { type: "number", min: 1 },
645
+ suggestedQueryLimit: { type: "number", min: 1 },
646
+ includePerQuery: { type: "boolean" },
647
+ includeExpanded: { type: "boolean" },
648
+ includeCoverage: { type: "boolean" },
649
+ includeBacklinks: { type: "boolean" },
650
+ backlinksLimit: { type: "number", min: 1 },
651
+ backlinksConcurrency: { type: "number", min: 1 },
652
+ concurrency: { type: "number", min: 1 },
653
+ hydrateConcurrency: { type: "number", min: 1 },
654
+ contextChars: { type: "number", min: 1 },
655
+ excerptChars: { type: "number", min: 1 },
656
+ snippetMinWords: { type: "number", min: 1 },
657
+ snippetMaxWords: { type: "number", min: 1 },
658
+ maxAttempts: { type: "number", min: 1 },
659
+ },
660
+ custom(args, issues) {
661
+ if (!args.question && !args.query && !args.queries) {
662
+ issues.push({ path: "args.question", message: "or args.query or args.queries[] is required" });
663
+ }
664
+ if (args.includeTitleSearch === false && args.includeSemanticSearch === false) {
665
+ issues.push({
666
+ path: "args.includeTitleSearch",
667
+ message: "and includeSemanticSearch cannot both be false",
668
+ });
669
+ }
670
+ if (typeof args.minScore === "number" && args.minScore > 1) {
671
+ issues.push({ path: "args.minScore", message: "must be <= 1" });
672
+ }
673
+ if (typeof args.diversityLambda === "number" && args.diversityLambda > 1) {
674
+ issues.push({ path: "args.diversityLambda", message: "must be <= 1" });
675
+ }
676
+ },
677
+ },
678
+ "documents.safe_update": {
679
+ required: ["id", "expectedRevision"],
680
+ properties: {
681
+ id: { type: "string" },
682
+ expectedRevision: { type: "number", min: 0 },
683
+ title: { type: "string" },
684
+ text: { type: "string" },
685
+ icon: { type: "string" },
686
+ color: { type: "string" },
687
+ fullWidth: { type: "boolean" },
688
+ templateId: { type: "string" },
689
+ collectionId: { type: "string" },
690
+ insightsEnabled: { type: "boolean" },
691
+ editMode: { type: "string", enum: ["replace", "append", "prepend"] },
692
+ publish: { type: "boolean" },
693
+ dataAttributes: { type: "array" },
694
+ view: { type: "string", enum: ["summary", "full"] },
695
+ excerptChars: { type: "number", min: 1 },
696
+ maxAttempts: { type: "number", min: 1 },
697
+ performAction: { type: "boolean" },
698
+ },
699
+ },
700
+ "documents.diff": {
701
+ required: ["id", "proposedText"],
702
+ properties: {
703
+ id: { type: "string" },
704
+ proposedText: { type: "string" },
705
+ includeFullHunks: { type: "boolean" },
706
+ hunkLimit: { type: "number", min: 1 },
707
+ hunkLineLimit: { type: "number", min: 1 },
708
+ maxAttempts: { type: "number", min: 1 },
709
+ },
710
+ },
711
+ "documents.apply_patch": {
712
+ required: ["id", "patch"],
713
+ properties: {
714
+ id: { type: "string" },
715
+ patch: { type: "string" },
716
+ expectedRevision: { type: "number", min: 0 },
717
+ mode: { type: "string", enum: ["unified", "replace"] },
718
+ title: { type: "string" },
719
+ view: { type: "string", enum: ["summary", "full"] },
720
+ excerptChars: { type: "number", min: 1 },
721
+ maxAttempts: { type: "number", min: 1 },
722
+ performAction: { type: "boolean" },
723
+ },
724
+ },
725
+ "documents.apply_patch_safe": {
726
+ required: ["id", "expectedRevision", "patch"],
727
+ properties: {
728
+ id: { type: "string" },
729
+ patch: { type: "string" },
730
+ expectedRevision: { type: "number", min: 0 },
731
+ mode: { type: "string", enum: ["unified", "replace"] },
732
+ title: { type: "string" },
733
+ view: { type: "string", enum: ["summary", "full"] },
734
+ excerptChars: { type: "number", min: 1 },
735
+ maxAttempts: { type: "number", min: 1 },
736
+ performAction: { type: "boolean" },
737
+ },
738
+ },
739
+ "documents.batch_update": {
740
+ required: ["updates"],
741
+ properties: {
742
+ updates: { type: "array" },
743
+ concurrency: { type: "number", min: 1 },
744
+ continueOnError: { type: "boolean" },
745
+ maxAttempts: { type: "number", min: 1 },
746
+ performAction: { type: "boolean" },
747
+ },
748
+ custom(args, issues) {
749
+ if (!Array.isArray(args.updates) || args.updates.length === 0) {
750
+ issues.push({ path: "args.updates", message: "must be a non-empty array" });
751
+ return;
752
+ }
753
+ for (let i = 0; i < args.updates.length; i += 1) {
754
+ const update = args.updates[i];
755
+ if (!update || typeof update !== "object" || Array.isArray(update)) {
756
+ issues.push({ path: `args.updates[${i}]`, message: "must be an object" });
757
+ continue;
758
+ }
759
+ if (!update.id || typeof update.id !== "string") {
760
+ issues.push({ path: `args.updates[${i}].id`, message: "is required and must be a string" });
761
+ }
762
+ if (
763
+ Object.prototype.hasOwnProperty.call(update, "expectedRevision") &&
764
+ !(typeof update.expectedRevision === "number" && Number.isFinite(update.expectedRevision))
765
+ ) {
766
+ issues.push({ path: `args.updates[${i}].expectedRevision`, message: "must be a number" });
767
+ }
768
+ if (
769
+ Object.prototype.hasOwnProperty.call(update, "editMode") &&
770
+ !["replace", "append", "prepend"].includes(update.editMode)
771
+ ) {
772
+ issues.push({ path: `args.updates[${i}].editMode`, message: "must be replace, append, or prepend" });
773
+ }
774
+ if (
775
+ Object.prototype.hasOwnProperty.call(update, "dataAttributes") &&
776
+ !Array.isArray(update.dataAttributes)
777
+ ) {
778
+ issues.push({ path: `args.updates[${i}].dataAttributes`, message: "must be an array" });
779
+ }
780
+ }
781
+ },
782
+ },
783
+ "documents.plan_batch_update": {
784
+ properties: {
785
+ id: { type: "string" },
786
+ ids: { type: "string[]" },
787
+ query: { type: "string" },
788
+ queries: { type: "string[]" },
789
+ collectionId: { type: "string" },
790
+ rules: { type: "array" },
791
+ find: { type: "string" },
792
+ replace: { type: "string" },
793
+ field: { type: "string", enum: ["title", "text", "both"] },
794
+ caseSensitive: { type: "boolean" },
795
+ wholeWord: { type: "boolean" },
796
+ all: { type: "boolean" },
797
+ includeTitleSearch: { type: "boolean" },
798
+ includeSemanticSearch: { type: "boolean" },
799
+ limitPerQuery: { type: "number", min: 1 },
800
+ offset: { type: "number", min: 0 },
801
+ maxDocuments: { type: "number", min: 1 },
802
+ readConcurrency: { type: "number", min: 1 },
803
+ includeUnchanged: { type: "boolean" },
804
+ hunkLimit: { type: "number", min: 1 },
805
+ hunkLineLimit: { type: "number", min: 1 },
806
+ snippetMinWords: { type: "number", min: 1 },
807
+ snippetMaxWords: { type: "number", min: 1 },
808
+ maxAttempts: { type: "number", min: 1 },
809
+ concurrency: { type: "number", min: 1 },
810
+ },
811
+ custom(args, issues) {
812
+ if (!args.id && !args.ids && !args.query && !args.queries) {
813
+ issues.push({
814
+ path: "args.ids",
815
+ message: "or args.query/args.queries[] is required",
816
+ });
817
+ }
818
+ if (!args.rules && !args.find) {
819
+ issues.push({ path: "args.rules", message: "or args.find is required" });
820
+ }
821
+ if (args.includeTitleSearch === false && args.includeSemanticSearch === false) {
822
+ issues.push({
823
+ path: "args.includeTitleSearch",
824
+ message: "and includeSemanticSearch cannot both be false",
825
+ });
826
+ }
827
+ },
828
+ },
829
+ "documents.delete": {
830
+ required: ["id", "readToken"],
831
+ properties: {
832
+ id: { type: "string" },
833
+ readToken: { type: "string" },
834
+ performAction: { type: "boolean" },
835
+ maxAttempts: { type: "number", min: 1 },
836
+ },
837
+ },
838
+ "documents.apply_batch_plan": {
839
+ required: ["plan", "confirmHash"],
840
+ properties: {
841
+ plan: { type: "object" },
842
+ confirmHash: { type: "string" },
843
+ dryRun: { type: "boolean" },
844
+ continueOnError: { type: "boolean" },
845
+ concurrency: { type: "number", min: 1 },
846
+ view: { type: "string", enum: ["summary", "full"] },
847
+ excerptChars: { type: "number", min: 1 },
848
+ maxAttempts: { type: "number", min: 1 },
849
+ performAction: { type: "boolean" },
850
+ },
851
+ },
852
+ "revisions.list": {
853
+ required: ["documentId"],
854
+ properties: {
855
+ documentId: { type: "string" },
856
+ limit: { type: "number", min: 1 },
857
+ offset: { type: "number", min: 0 },
858
+ sort: { type: "string" },
859
+ direction: { type: "string", enum: ["ASC", "DESC"] },
860
+ view: { type: "string", enum: ["summary", "full"] },
861
+ maxAttempts: { type: "number", min: 1 },
862
+ },
863
+ },
864
+ "revisions.diff": {
865
+ required: ["id", "baseRevisionId", "targetRevisionId"],
866
+ properties: {
867
+ id: { type: "string" },
868
+ baseRevisionId: { type: "string" },
869
+ targetRevisionId: { type: "string" },
870
+ includeFullHunks: { type: "boolean" },
871
+ hunkLimit: { type: "number", min: 1 },
872
+ hunkLineLimit: { type: "number", min: 1 },
873
+ view: { type: "string", enum: ["summary", "full"] },
874
+ maxAttempts: { type: "number", min: 1 },
875
+ },
876
+ },
877
+ "revisions.restore": {
878
+ required: ["id", "revisionId"],
879
+ properties: {
880
+ id: { type: "string" },
881
+ revisionId: { type: "string" },
882
+ collectionId: { type: "string" },
883
+ view: { type: "string", enum: ["summary", "full"] },
884
+ excerptChars: { type: "number", min: 1 },
885
+ maxAttempts: { type: "number", min: 1 },
886
+ performAction: { type: "boolean" },
887
+ },
888
+ },
889
+ "revisions.info": {
890
+ required: ["id"],
891
+ properties: {
892
+ id: { type: "string" },
893
+ includePolicies: { type: "boolean" },
894
+ view: { type: "string", enum: ["summary", "full"] },
895
+ maxAttempts: { type: "number", min: 1 },
896
+ },
897
+ },
898
+ "shares.list": {
899
+ properties: {
900
+ query: { type: "string" },
901
+ documentId: { type: "string" },
902
+ limit: { type: "number", min: 1 },
903
+ offset: { type: "number", min: 0 },
904
+ sort: { type: "string" },
905
+ direction: { type: "string", enum: ["ASC", "DESC"] },
906
+ includePolicies: { type: "boolean" },
907
+ view: { type: "string", enum: ["ids", "summary", "full"] },
908
+ maxAttempts: { type: "number", min: 1 },
909
+ },
910
+ },
911
+ "shares.info": {
912
+ properties: {
913
+ id: { type: "string" },
914
+ documentId: { type: "string" },
915
+ includePolicies: { type: "boolean" },
916
+ view: { type: "string", enum: ["summary", "full"] },
917
+ maxAttempts: { type: "number", min: 1 },
918
+ },
919
+ custom(args, issues) {
920
+ if (!args.id && !args.documentId) {
921
+ issues.push({ path: "args.id", message: "or args.documentId is required" });
922
+ }
923
+ if (args.id && args.documentId) {
924
+ issues.push({ path: "args.documentId", message: "cannot be combined with args.id" });
925
+ }
926
+ },
927
+ },
928
+ "shares.create": {
929
+ required: ["documentId"],
930
+ properties: {
931
+ documentId: { type: "string" },
932
+ includeChildDocuments: { type: "boolean" },
933
+ published: { type: "boolean" },
934
+ includePolicies: { type: "boolean" },
935
+ view: { type: "string", enum: ["summary", "full"] },
936
+ maxAttempts: { type: "number", min: 1 },
937
+ performAction: { type: "boolean" },
938
+ },
939
+ },
940
+ "shares.update": {
941
+ required: ["id", "published"],
942
+ properties: {
943
+ id: { type: "string" },
944
+ includeChildDocuments: { type: "boolean" },
945
+ published: { type: "boolean" },
946
+ includePolicies: { type: "boolean" },
947
+ view: { type: "string", enum: ["summary", "full"] },
948
+ maxAttempts: { type: "number", min: 1 },
949
+ performAction: { type: "boolean" },
950
+ },
951
+ },
952
+ "shares.revoke": {
953
+ required: ["id"],
954
+ properties: {
955
+ id: { type: "string" },
956
+ maxAttempts: { type: "number", min: 1 },
957
+ performAction: { type: "boolean" },
958
+ },
959
+ },
960
+ "templates.list": {
961
+ properties: {
962
+ collectionId: { type: "string" },
963
+ query: { type: "string" },
964
+ limit: { type: "number", min: 1 },
965
+ offset: { type: "number", min: 0 },
966
+ sort: { type: "string" },
967
+ direction: { type: "string", enum: ["ASC", "DESC"] },
968
+ includePolicies: { type: "boolean" },
969
+ view: { type: "string", enum: ["ids", "summary", "full"] },
970
+ maxAttempts: { type: "number", min: 1 },
971
+ },
972
+ },
973
+ "templates.info": {
974
+ properties: {
975
+ id: { type: "string" },
976
+ ids: { type: "string[]" },
977
+ includePolicies: { type: "boolean" },
978
+ concurrency: { type: "number", min: 1 },
979
+ view: { type: "string", enum: ["summary", "full"] },
980
+ maxAttempts: { type: "number", min: 1 },
981
+ },
982
+ custom(args, issues) {
983
+ if (!args.id && !args.ids) {
984
+ issues.push({ path: "args.id", message: "or args.ids[] is required" });
985
+ }
986
+ },
987
+ },
988
+ "templates.extract_placeholders": {
989
+ required: ["id"],
990
+ properties: {
991
+ id: { type: "string" },
992
+ maxAttempts: { type: "number", min: 1 },
993
+ },
994
+ custom(args, issues) {
995
+ if (typeof args.id === "string" && args.id.trim().length === 0) {
996
+ issues.push({ path: "args.id", message: "must be a non-empty string" });
997
+ }
998
+ },
999
+ },
1000
+ "templates.create": {
1001
+ required: ["title", "data"],
1002
+ properties: {
1003
+ title: { type: "string" },
1004
+ data: { type: "object" },
1005
+ icon: { type: "string" },
1006
+ color: { type: "string" },
1007
+ collectionId: { type: "string" },
1008
+ fullWidth: { type: "boolean" },
1009
+ includePolicies: { type: "boolean" },
1010
+ view: { type: "string", enum: ["summary", "full"] },
1011
+ maxAttempts: { type: "number", min: 1 },
1012
+ performAction: { type: "boolean" },
1013
+ },
1014
+ },
1015
+ "templates.update": {
1016
+ required: ["id"],
1017
+ properties: {
1018
+ id: { type: "string" },
1019
+ title: { type: "string" },
1020
+ data: { type: "object" },
1021
+ icon: { type: ["string", "null"] },
1022
+ color: { type: ["string", "null"] },
1023
+ collectionId: { type: ["string", "null"] },
1024
+ fullWidth: { type: "boolean" },
1025
+ includePolicies: { type: "boolean" },
1026
+ view: { type: "string", enum: ["summary", "full"] },
1027
+ maxAttempts: { type: "number", min: 1 },
1028
+ performAction: { type: "boolean" },
1029
+ },
1030
+ },
1031
+ "templates.delete": {
1032
+ required: ["id"],
1033
+ properties: {
1034
+ id: { type: "string" },
1035
+ maxAttempts: { type: "number", min: 1 },
1036
+ performAction: { type: "boolean" },
1037
+ },
1038
+ },
1039
+ "templates.restore": {
1040
+ required: ["id"],
1041
+ properties: {
1042
+ id: { type: "string" },
1043
+ includePolicies: { type: "boolean" },
1044
+ view: { type: "string", enum: ["summary", "full"] },
1045
+ maxAttempts: { type: "number", min: 1 },
1046
+ performAction: { type: "boolean" },
1047
+ },
1048
+ },
1049
+ "templates.duplicate": {
1050
+ required: ["id"],
1051
+ properties: {
1052
+ id: { type: "string" },
1053
+ title: { type: "string" },
1054
+ collectionId: { type: ["string", "null"] },
1055
+ includePolicies: { type: "boolean" },
1056
+ view: { type: "string", enum: ["summary", "full"] },
1057
+ maxAttempts: { type: "number", min: 1 },
1058
+ performAction: { type: "boolean" },
1059
+ },
1060
+ },
1061
+ "documents.templatize": {
1062
+ required: ["id"],
1063
+ properties: {
1064
+ id: { type: "string" },
1065
+ collectionId: { type: ["string", "null"] },
1066
+ publish: { type: "boolean" },
1067
+ includePolicies: { type: "boolean" },
1068
+ view: { type: "string", enum: ["summary", "full"] },
1069
+ maxAttempts: { type: "number", min: 1 },
1070
+ performAction: { type: "boolean" },
1071
+ },
1072
+ },
1073
+ "documents.import": {
1074
+ allowUnknown: true,
1075
+ properties: {
1076
+ collectionId: { type: "string" },
1077
+ parentDocumentId: { type: "string" },
1078
+ includePolicies: { type: "boolean" },
1079
+ maxAttempts: { type: "number", min: 1 },
1080
+ performAction: { type: "boolean" },
1081
+ },
1082
+ custom(args, issues) {
1083
+ const hasCollectionId =
1084
+ typeof args.collectionId === "string" && args.collectionId.trim().length > 0;
1085
+ const hasParentDocumentId =
1086
+ typeof args.parentDocumentId === "string" && args.parentDocumentId.trim().length > 0;
1087
+
1088
+ if (typeof args.collectionId === "string" && args.collectionId.trim().length === 0) {
1089
+ issues.push({ path: "args.collectionId", message: "must be a non-empty string when provided" });
1090
+ }
1091
+ if (
1092
+ typeof args.parentDocumentId === "string" &&
1093
+ args.parentDocumentId.trim().length === 0
1094
+ ) {
1095
+ issues.push({
1096
+ path: "args.parentDocumentId",
1097
+ message: "must be a non-empty string when provided",
1098
+ });
1099
+ }
1100
+ if (hasCollectionId && hasParentDocumentId) {
1101
+ issues.push({
1102
+ path: "args.parentDocumentId",
1103
+ message: "cannot be combined with args.collectionId",
1104
+ });
1105
+ }
1106
+ },
1107
+ },
1108
+ "documents.import_file": {
1109
+ allowUnknown: true,
1110
+ required: ["filePath"],
1111
+ properties: {
1112
+ filePath: { type: "string" },
1113
+ collectionId: { type: "string" },
1114
+ parentDocumentId: { type: "string" },
1115
+ publish: { type: "boolean" },
1116
+ contentType: { type: "string" },
1117
+ includePolicies: { type: "boolean" },
1118
+ maxAttempts: { type: "number", min: 1 },
1119
+ performAction: { type: "boolean" },
1120
+ },
1121
+ custom(args, issues) {
1122
+ const hasCollectionId =
1123
+ typeof args.collectionId === "string" && args.collectionId.trim().length > 0;
1124
+ const hasParentDocumentId =
1125
+ typeof args.parentDocumentId === "string" && args.parentDocumentId.trim().length > 0;
1126
+
1127
+ if (typeof args.filePath === "string" && args.filePath.trim().length === 0) {
1128
+ issues.push({ path: "args.filePath", message: "must be a non-empty string" });
1129
+ }
1130
+ if (typeof args.collectionId === "string" && args.collectionId.trim().length === 0) {
1131
+ issues.push({ path: "args.collectionId", message: "must be a non-empty string when provided" });
1132
+ }
1133
+ if (
1134
+ typeof args.parentDocumentId === "string" &&
1135
+ args.parentDocumentId.trim().length === 0
1136
+ ) {
1137
+ issues.push({
1138
+ path: "args.parentDocumentId",
1139
+ message: "must be a non-empty string when provided",
1140
+ });
1141
+ }
1142
+ if (hasCollectionId && hasParentDocumentId) {
1143
+ issues.push({
1144
+ path: "args.parentDocumentId",
1145
+ message: "cannot be combined with args.collectionId",
1146
+ });
1147
+ }
1148
+ },
1149
+ },
1150
+ "file_operations.list": {
1151
+ allowUnknown: true,
1152
+ properties: {
1153
+ includePolicies: { type: "boolean" },
1154
+ maxAttempts: { type: "number", min: 1 },
1155
+ },
1156
+ },
1157
+ "file_operations.info": {
1158
+ allowUnknown: true,
1159
+ required: ["id"],
1160
+ properties: {
1161
+ id: { type: "string" },
1162
+ includePolicies: { type: "boolean" },
1163
+ maxAttempts: { type: "number", min: 1 },
1164
+ },
1165
+ custom(args, issues) {
1166
+ if (typeof args.id === "string" && args.id.trim().length === 0) {
1167
+ issues.push({ path: "args.id", message: "must be a non-empty string" });
1168
+ }
1169
+ },
1170
+ },
1171
+ "file_operations.delete": {
1172
+ allowUnknown: true,
1173
+ required: ["id"],
1174
+ properties: {
1175
+ id: { type: "string" },
1176
+ maxAttempts: { type: "number", min: 1 },
1177
+ performAction: { type: "boolean" },
1178
+ },
1179
+ custom(args, issues) {
1180
+ if (typeof args.id === "string" && args.id.trim().length === 0) {
1181
+ issues.push({ path: "args.id", message: "must be a non-empty string" });
1182
+ }
1183
+ },
1184
+ },
1185
+ "oauth_clients.list": {
1186
+ properties: {
1187
+ limit: { type: "number", min: 1 },
1188
+ offset: { type: "number", min: 0 },
1189
+ includePolicies: { type: "boolean" },
1190
+ view: { type: "string", enum: ["ids", "summary", "full"] },
1191
+ maxAttempts: { type: "number", min: 1 },
1192
+ },
1193
+ custom(args, issues) {
1194
+ if (typeof args.limit === "number" && args.limit > 250) {
1195
+ issues.push({ path: "args.limit", message: "must be <= 250" });
1196
+ }
1197
+ },
1198
+ },
1199
+ "oauth_clients.info": {
1200
+ properties: {
1201
+ id: { type: "string" },
1202
+ clientId: { type: "string" },
1203
+ includePolicies: { type: "boolean" },
1204
+ view: { type: "string", enum: ["summary", "full"] },
1205
+ maxAttempts: { type: "number", min: 1 },
1206
+ },
1207
+ custom(args, issues) {
1208
+ const hasId = typeof args.id === "string" && args.id.trim().length > 0;
1209
+ const hasClientId = typeof args.clientId === "string" && args.clientId.trim().length > 0;
1210
+
1211
+ if (!hasId && !hasClientId) {
1212
+ issues.push({ path: "args.id", message: "or args.clientId is required" });
1213
+ }
1214
+ if (typeof args.id === "string" && args.id.trim().length === 0) {
1215
+ issues.push({ path: "args.id", message: "must be a non-empty string when provided" });
1216
+ }
1217
+ if (typeof args.clientId === "string" && args.clientId.trim().length === 0) {
1218
+ issues.push({ path: "args.clientId", message: "must be a non-empty string when provided" });
1219
+ }
1220
+ if (hasId && hasClientId) {
1221
+ issues.push({ path: "args.clientId", message: "cannot be combined with args.id" });
1222
+ }
1223
+ },
1224
+ },
1225
+ "oauth_clients.create": {
1226
+ required: ["name", "redirectUris"],
1227
+ properties: {
1228
+ name: { type: "string" },
1229
+ description: { type: "string" },
1230
+ developerName: { type: "string" },
1231
+ developerUrl: { type: "string" },
1232
+ avatarUrl: { type: "string" },
1233
+ redirectUris: { type: "string[]" },
1234
+ published: { type: "boolean" },
1235
+ includePolicies: { type: "boolean" },
1236
+ view: { type: "string", enum: ["summary", "full"] },
1237
+ maxAttempts: { type: "number", min: 1 },
1238
+ performAction: { type: "boolean" },
1239
+ },
1240
+ custom(args, issues) {
1241
+ if (typeof args.name === "string" && args.name.trim().length === 0) {
1242
+ issues.push({ path: "args.name", message: "must be a non-empty string" });
1243
+ }
1244
+ if (Array.isArray(args.redirectUris)) {
1245
+ if (args.redirectUris.length === 0) {
1246
+ issues.push({ path: "args.redirectUris", message: "must be a non-empty string[]" });
1247
+ }
1248
+ for (let i = 0; i < args.redirectUris.length; i += 1) {
1249
+ if (args.redirectUris[i].trim().length === 0) {
1250
+ issues.push({ path: `args.redirectUris[${i}]`, message: "must be a non-empty string" });
1251
+ }
1252
+ }
1253
+ }
1254
+ },
1255
+ },
1256
+ "oauth_clients.update": {
1257
+ required: ["id"],
1258
+ properties: {
1259
+ id: { type: "string" },
1260
+ name: { type: "string" },
1261
+ description: { type: "string" },
1262
+ developerName: { type: "string" },
1263
+ developerUrl: { type: "string" },
1264
+ avatarUrl: { type: "string" },
1265
+ redirectUris: { type: "string[]" },
1266
+ published: { type: "boolean" },
1267
+ includePolicies: { type: "boolean" },
1268
+ view: { type: "string", enum: ["summary", "full"] },
1269
+ maxAttempts: { type: "number", min: 1 },
1270
+ performAction: { type: "boolean" },
1271
+ },
1272
+ custom(args, issues) {
1273
+ if (typeof args.id === "string" && args.id.trim().length === 0) {
1274
+ issues.push({ path: "args.id", message: "must be a non-empty string" });
1275
+ }
1276
+ if (typeof args.name === "string" && args.name.trim().length === 0) {
1277
+ issues.push({ path: "args.name", message: "must be a non-empty string when provided" });
1278
+ }
1279
+ if (Array.isArray(args.redirectUris)) {
1280
+ if (args.redirectUris.length === 0) {
1281
+ issues.push({ path: "args.redirectUris", message: "must be a non-empty string[] when provided" });
1282
+ }
1283
+ for (let i = 0; i < args.redirectUris.length; i += 1) {
1284
+ if (args.redirectUris[i].trim().length === 0) {
1285
+ issues.push({ path: `args.redirectUris[${i}]`, message: "must be a non-empty string" });
1286
+ }
1287
+ }
1288
+ }
1289
+ },
1290
+ },
1291
+ "oauth_clients.rotate_secret": {
1292
+ required: ["id"],
1293
+ properties: {
1294
+ id: { type: "string" },
1295
+ includePolicies: { type: "boolean" },
1296
+ view: { type: "string", enum: ["summary", "full"] },
1297
+ maxAttempts: { type: "number", min: 1 },
1298
+ performAction: { type: "boolean" },
1299
+ },
1300
+ custom(args, issues) {
1301
+ if (typeof args.id === "string" && args.id.trim().length === 0) {
1302
+ issues.push({ path: "args.id", message: "must be a non-empty string" });
1303
+ }
1304
+ },
1305
+ },
1306
+ "oauth_clients.delete": {
1307
+ required: ["id"],
1308
+ properties: {
1309
+ id: { type: "string" },
1310
+ maxAttempts: { type: "number", min: 1 },
1311
+ performAction: { type: "boolean" },
1312
+ },
1313
+ custom(args, issues) {
1314
+ if (typeof args.id === "string" && args.id.trim().length === 0) {
1315
+ issues.push({ path: "args.id", message: "must be a non-empty string" });
1316
+ }
1317
+ },
1318
+ },
1319
+ "oauth_authentications.list": {
1320
+ properties: {
1321
+ limit: { type: "number", min: 1 },
1322
+ offset: { type: "number", min: 0 },
1323
+ includePolicies: { type: "boolean" },
1324
+ view: { type: "string", enum: ["ids", "summary", "full"] },
1325
+ maxAttempts: { type: "number", min: 1 },
1326
+ },
1327
+ custom(args, issues) {
1328
+ if (typeof args.limit === "number" && args.limit > 250) {
1329
+ issues.push({ path: "args.limit", message: "must be <= 250" });
1330
+ }
1331
+ },
1332
+ },
1333
+ "oauth_authentications.delete": {
1334
+ required: ["oauthClientId"],
1335
+ properties: {
1336
+ oauthClientId: { type: "string" },
1337
+ scope: { type: "string[]" },
1338
+ maxAttempts: { type: "number", min: 1 },
1339
+ performAction: { type: "boolean" },
1340
+ },
1341
+ custom(args, issues) {
1342
+ if (typeof args.oauthClientId === "string" && args.oauthClientId.trim().length === 0) {
1343
+ issues.push({ path: "args.oauthClientId", message: "must be a non-empty string" });
1344
+ }
1345
+ if (Array.isArray(args.scope)) {
1346
+ if (args.scope.length === 0) {
1347
+ issues.push({ path: "args.scope", message: "must be a non-empty string[] when provided" });
1348
+ }
1349
+ for (let i = 0; i < args.scope.length; i += 1) {
1350
+ if (args.scope[i].trim().length === 0) {
1351
+ issues.push({ path: `args.scope[${i}]`, message: "must be a non-empty string" });
1352
+ }
1353
+ }
1354
+ }
1355
+ },
1356
+ },
1357
+ "oauthClients.delete": {
1358
+ required: ["id"],
1359
+ properties: {
1360
+ id: { type: "string" },
1361
+ maxAttempts: { type: "number", min: 1 },
1362
+ performAction: { type: "boolean" },
1363
+ },
1364
+ custom(args, issues) {
1365
+ if (typeof args.id === "string" && args.id.trim().length === 0) {
1366
+ issues.push({ path: "args.id", message: "must be a non-empty string" });
1367
+ }
1368
+ },
1369
+ },
1370
+ "oauthAuthentications.delete": {
1371
+ required: ["oauthClientId"],
1372
+ properties: {
1373
+ oauthClientId: { type: "string" },
1374
+ scope: { type: "string[]" },
1375
+ maxAttempts: { type: "number", min: 1 },
1376
+ performAction: { type: "boolean" },
1377
+ },
1378
+ custom(args, issues) {
1379
+ if (typeof args.oauthClientId === "string" && args.oauthClientId.trim().length === 0) {
1380
+ issues.push({ path: "args.oauthClientId", message: "must be a non-empty string" });
1381
+ }
1382
+ if (Array.isArray(args.scope)) {
1383
+ if (args.scope.length === 0) {
1384
+ issues.push({ path: "args.scope", message: "must be a non-empty string[] when provided" });
1385
+ }
1386
+ for (let i = 0; i < args.scope.length; i += 1) {
1387
+ if (args.scope[i].trim().length === 0) {
1388
+ issues.push({ path: `args.scope[${i}]`, message: "must be a non-empty string" });
1389
+ }
1390
+ }
1391
+ }
1392
+ },
1393
+ },
1394
+ "documents.create_from_template": {
1395
+ required: ["templateId"],
1396
+ properties: {
1397
+ templateId: { type: "string" },
1398
+ title: { type: "string" },
1399
+ collectionId: { type: "string" },
1400
+ parentDocumentId: { type: "string" },
1401
+ publish: { type: "boolean" },
1402
+ placeholderValues: { type: "object" },
1403
+ strictPlaceholders: { type: "boolean" },
1404
+ view: { type: "string", enum: ["summary", "full"] },
1405
+ includePolicies: { type: "boolean" },
1406
+ maxAttempts: { type: "number", min: 1 },
1407
+ performAction: { type: "boolean" },
1408
+ },
1409
+ custom(args, issues) {
1410
+ if (typeof args.templateId === "string" && args.templateId.trim().length === 0) {
1411
+ issues.push({ path: "args.templateId", message: "must be a non-empty string" });
1412
+ }
1413
+
1414
+ for (const key of ["title", "collectionId", "parentDocumentId"]) {
1415
+ if (typeof args[key] === "string" && args[key].trim().length === 0) {
1416
+ issues.push({ path: `args.${key}`, message: "must be a non-empty string when provided" });
1417
+ }
1418
+ }
1419
+
1420
+ if (args.placeholderValues && typeof args.placeholderValues === "object" && !Array.isArray(args.placeholderValues)) {
1421
+ for (const [rawKey, rawValue] of Object.entries(args.placeholderValues)) {
1422
+ if (String(rawKey || "").trim().length === 0) {
1423
+ issues.push({
1424
+ path: "args.placeholderValues",
1425
+ message: "must not contain empty keys",
1426
+ });
1427
+ }
1428
+ if (typeof rawValue !== "string") {
1429
+ const keyPath = rawKey ? `args.placeholderValues.${rawKey}` : "args.placeholderValues";
1430
+ issues.push({ path: keyPath, message: "must be a string" });
1431
+ }
1432
+ }
1433
+ }
1434
+ },
1435
+ },
1436
+ "comments.list": {
1437
+ properties: {
1438
+ documentId: { type: "string" },
1439
+ collectionId: { type: "string" },
1440
+ parentCommentId: { type: "string" },
1441
+ includeAnchorText: { type: "boolean" },
1442
+ includeReplies: { type: "boolean" },
1443
+ limit: { type: "number", min: 1 },
1444
+ offset: { type: "number", min: 0 },
1445
+ sort: { type: "string" },
1446
+ direction: { type: "string", enum: ["ASC", "DESC"] },
1447
+ includePolicies: { type: "boolean" },
1448
+ view: { type: "string", enum: ["ids", "summary", "full"] },
1449
+ maxAttempts: { type: "number", min: 1 },
1450
+ },
1451
+ },
1452
+ "comments.info": {
1453
+ required: ["id"],
1454
+ properties: {
1455
+ id: { type: "string" },
1456
+ includeAnchorText: { type: "boolean" },
1457
+ includePolicies: { type: "boolean" },
1458
+ view: { type: "string", enum: ["summary", "full"] },
1459
+ maxAttempts: { type: "number", min: 1 },
1460
+ },
1461
+ },
1462
+ "comments.create": {
1463
+ required: ["documentId"],
1464
+ properties: {
1465
+ documentId: { type: "string" },
1466
+ text: { type: "string" },
1467
+ data: { type: "object" },
1468
+ parentCommentId: { type: "string" },
1469
+ includePolicies: { type: "boolean" },
1470
+ view: { type: "string", enum: ["summary", "full"] },
1471
+ maxAttempts: { type: "number", min: 1 },
1472
+ performAction: { type: "boolean" },
1473
+ },
1474
+ custom(args, issues) {
1475
+ if (!args.text && !args.data) {
1476
+ issues.push({ path: "args.text", message: "or args.data is required" });
1477
+ }
1478
+ },
1479
+ },
1480
+ "comments.update": {
1481
+ required: ["id"],
1482
+ properties: {
1483
+ id: { type: "string" },
1484
+ text: { type: "string" },
1485
+ data: { type: "object" },
1486
+ includePolicies: { type: "boolean" },
1487
+ view: { type: "string", enum: ["summary", "full"] },
1488
+ maxAttempts: { type: "number", min: 1 },
1489
+ performAction: { type: "boolean" },
1490
+ },
1491
+ custom(args, issues) {
1492
+ if (!args.text && !args.data) {
1493
+ issues.push({ path: "args.text", message: "or args.data is required" });
1494
+ }
1495
+ },
1496
+ },
1497
+ "comments.delete": {
1498
+ required: ["id"],
1499
+ properties: {
1500
+ id: { type: "string" },
1501
+ maxAttempts: { type: "number", min: 1 },
1502
+ performAction: { type: "boolean" },
1503
+ },
1504
+ },
1505
+ "data_attributes.list": {
1506
+ properties: {
1507
+ limit: { type: "number", min: 1 },
1508
+ offset: { type: "number", min: 0 },
1509
+ sort: { type: "string" },
1510
+ direction: { type: "string", enum: ["ASC", "DESC"] },
1511
+ includePolicies: { type: "boolean" },
1512
+ view: { type: "string", enum: ["ids", "summary", "full"] },
1513
+ maxAttempts: { type: "number", min: 1 },
1514
+ },
1515
+ custom(args, issues) {
1516
+ if (typeof args.limit === "number" && args.limit > 250) {
1517
+ issues.push({ path: "args.limit", message: "must be <= 250" });
1518
+ }
1519
+ },
1520
+ },
1521
+ "data_attributes.info": {
1522
+ required: ["id"],
1523
+ properties: {
1524
+ id: { type: "string" },
1525
+ includePolicies: { type: "boolean" },
1526
+ view: { type: "string", enum: ["summary", "full"] },
1527
+ maxAttempts: { type: "number", min: 1 },
1528
+ },
1529
+ },
1530
+ "data_attributes.create": {
1531
+ required: ["name", "dataType"],
1532
+ properties: {
1533
+ name: { type: "string" },
1534
+ description: { type: "string" },
1535
+ dataType: { type: "string", enum: DATA_ATTRIBUTE_DATA_TYPES },
1536
+ options: { type: "object" },
1537
+ pinned: { type: "boolean" },
1538
+ includePolicies: { type: "boolean" },
1539
+ view: { type: "string", enum: ["summary", "full"] },
1540
+ maxAttempts: { type: "number", min: 1 },
1541
+ performAction: { type: "boolean" },
1542
+ },
1543
+ custom(args, issues) {
1544
+ if (typeof args.name === "string" && args.name.trim().length === 0) {
1545
+ issues.push({ path: "args.name", message: "must be a non-empty string" });
1546
+ }
1547
+ },
1548
+ },
1549
+ "data_attributes.update": {
1550
+ required: ["id", "name"],
1551
+ properties: {
1552
+ id: { type: "string" },
1553
+ name: { type: "string" },
1554
+ description: { type: "string" },
1555
+ options: { type: "object" },
1556
+ pinned: { type: "boolean" },
1557
+ includePolicies: { type: "boolean" },
1558
+ view: { type: "string", enum: ["summary", "full"] },
1559
+ maxAttempts: { type: "number", min: 1 },
1560
+ performAction: { type: "boolean" },
1561
+ },
1562
+ custom(args, issues) {
1563
+ if (typeof args.name === "string" && args.name.trim().length === 0) {
1564
+ issues.push({ path: "args.name", message: "must be a non-empty string" });
1565
+ }
1566
+ },
1567
+ },
1568
+ "data_attributes.delete": {
1569
+ required: ["id"],
1570
+ properties: {
1571
+ id: { type: "string" },
1572
+ maxAttempts: { type: "number", min: 1 },
1573
+ performAction: { type: "boolean" },
1574
+ },
1575
+ },
1576
+ "comments.review_queue": {
1577
+ properties: {
1578
+ documentIds: { type: "string[]" },
1579
+ collectionId: { type: "string" },
1580
+ includeAnchorText: { type: "boolean" },
1581
+ includeReplies: { type: "boolean" },
1582
+ limitPerDocument: { type: "number", min: 1 },
1583
+ view: { type: "string", enum: ["summary", "full"] },
1584
+ maxAttempts: { type: "number", min: 1 },
1585
+ },
1586
+ custom(args, issues) {
1587
+ const hasDocumentIds = Array.isArray(args.documentIds) && args.documentIds.length > 0;
1588
+ const hasCollectionId = typeof args.collectionId === "string" && args.collectionId.length > 0;
1589
+ if (!hasDocumentIds && !hasCollectionId) {
1590
+ issues.push({ path: "args.documentIds", message: "or args.collectionId is required" });
1591
+ }
1592
+ if (Array.isArray(args.documentIds) && args.documentIds.length === 0) {
1593
+ issues.push({ path: "args.documentIds", message: "must be a non-empty string[] when provided" });
1594
+ }
1595
+ },
1596
+ },
1597
+ "federated.sync_manifest": {
1598
+ properties: {
1599
+ collectionId: { type: "string" },
1600
+ query: { type: "string" },
1601
+ since: { type: "string" },
1602
+ limit: { type: "number", min: 1 },
1603
+ offset: { type: "number", min: 0 },
1604
+ includeDrafts: { type: "boolean" },
1605
+ includeMemberships: { type: "boolean" },
1606
+ includePolicies: { type: "boolean" },
1607
+ view: { type: "string", enum: ["ids", "summary", "full"] },
1608
+ maxAttempts: { type: "number", min: 1 },
1609
+ },
1610
+ custom(args, issues) {
1611
+ if (typeof args.since === "string") {
1612
+ const parsed = Date.parse(args.since);
1613
+ if (!Number.isFinite(parsed) || !args.since.includes("T")) {
1614
+ issues.push({ path: "args.since", message: "must be an ISO-8601 timestamp" });
1615
+ }
1616
+ }
1617
+ },
1618
+ },
1619
+ "federated.sync_probe": {
1620
+ properties: {
1621
+ ids: { type: "string[]" },
1622
+ queries: { type: "string[]" },
1623
+ mode: { type: "string", enum: ["titles", "semantic", "both"] },
1624
+ collectionId: { type: "string" },
1625
+ limit: { type: "number", min: 1 },
1626
+ freshnessHours: { type: "number", min: 1 },
1627
+ includePolicies: { type: "boolean" },
1628
+ view: { type: "string", enum: ["summary", "full"] },
1629
+ maxAttempts: { type: "number", min: 1 },
1630
+ },
1631
+ custom(args, issues) {
1632
+ const hasIds = Array.isArray(args.ids) && args.ids.length > 0;
1633
+ const hasQueries = Array.isArray(args.queries) && args.queries.length > 0;
1634
+ if (!hasIds && !hasQueries) {
1635
+ issues.push({ path: "args.ids", message: "or args.queries[] is required" });
1636
+ }
1637
+ if (Array.isArray(args.ids) && args.ids.length === 0) {
1638
+ issues.push({ path: "args.ids", message: "must be a non-empty string[] when provided" });
1639
+ }
1640
+ if (Array.isArray(args.queries) && args.queries.length === 0) {
1641
+ issues.push({ path: "args.queries", message: "must be a non-empty string[] when provided" });
1642
+ }
1643
+ },
1644
+ },
1645
+ "federated.permission_snapshot": {
1646
+ required: ["ids"],
1647
+ properties: {
1648
+ ids: { type: "string[]" },
1649
+ includeCollectionMemberships: { type: "boolean" },
1650
+ includeDocumentMemberships: { type: "boolean" },
1651
+ includePolicies: { type: "boolean" },
1652
+ view: { type: "string", enum: ["summary", "full"] },
1653
+ maxAttempts: { type: "number", min: 1 },
1654
+ },
1655
+ custom(args, issues) {
1656
+ if (Array.isArray(args.ids) && args.ids.length === 0) {
1657
+ issues.push({ path: "args.ids", message: "must be a non-empty string[]" });
1658
+ }
1659
+ },
1660
+ },
1661
+ "documents.plan_terminology_refactor": {
1662
+ required: ["glossary"],
1663
+ properties: {
1664
+ glossary: { type: "array" },
1665
+ id: { type: "string" },
1666
+ ids: { type: "string[]" },
1667
+ query: { type: "string" },
1668
+ queries: { type: "string[]" },
1669
+ collectionId: { type: "string" },
1670
+ includeTitleSearch: { type: "boolean" },
1671
+ includeSemanticSearch: { type: "boolean" },
1672
+ limitPerQuery: { type: "number", min: 1 },
1673
+ offset: { type: "number", min: 0 },
1674
+ maxDocuments: { type: "number", min: 1 },
1675
+ readConcurrency: { type: "number", min: 1 },
1676
+ includeUnchanged: { type: "boolean" },
1677
+ hunkLimit: { type: "number", min: 1 },
1678
+ hunkLineLimit: { type: "number", min: 1 },
1679
+ excludeDocIds: { type: "string[]" },
1680
+ excludePatterns: { type: "string[]" },
1681
+ excludeCodeBlocks: { type: "boolean" },
1682
+ excludeInlineCode: { type: "boolean" },
1683
+ maxAttempts: { type: "number", min: 1 },
1684
+ },
1685
+ custom(args, issues) {
1686
+ if (!Array.isArray(args.glossary) || args.glossary.length === 0) {
1687
+ issues.push({ path: "args.glossary", message: "must be a non-empty array" });
1688
+ } else {
1689
+ const seenFind = new Set();
1690
+ for (let i = 0; i < args.glossary.length; i += 1) {
1691
+ const item = args.glossary[i];
1692
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
1693
+ issues.push({ path: `args.glossary[${i}]`, message: "must be an object" });
1694
+ continue;
1695
+ }
1696
+ const find = typeof item.find === "string" ? item.find.trim() : "";
1697
+ const replace = typeof item.replace === "string" ? item.replace.trim() : "";
1698
+ if (!find) {
1699
+ issues.push({ path: `args.glossary[${i}].find`, message: "is required and must be a non-empty string" });
1700
+ }
1701
+ if (typeof item.replace !== "string") {
1702
+ issues.push({ path: `args.glossary[${i}].replace`, message: "is required and must be a string" });
1703
+ }
1704
+ if (find && replace && find === replace) {
1705
+ issues.push({ path: `args.glossary[${i}].replace`, message: "must differ from find" });
1706
+ }
1707
+ if (find) {
1708
+ if (seenFind.has(find)) {
1709
+ issues.push({ path: `args.glossary[${i}].find`, message: "must be unique across glossary entries" });
1710
+ } else {
1711
+ seenFind.add(find);
1712
+ }
1713
+ }
1714
+ if (
1715
+ item.field !== undefined &&
1716
+ !["title", "text", "both"].includes(item.field)
1717
+ ) {
1718
+ issues.push({
1719
+ path: `args.glossary[${i}].field`,
1720
+ message: "must be one of: title, text, both",
1721
+ });
1722
+ }
1723
+ }
1724
+ }
1725
+
1726
+ const hasScopeId = typeof args.id === "string" && args.id.length > 0;
1727
+ const hasScopeIds = Array.isArray(args.ids) && args.ids.length > 0;
1728
+ const hasScopeQuery = typeof args.query === "string" && args.query.trim().length > 0;
1729
+ const hasScopeQueries = Array.isArray(args.queries) && args.queries.length > 0;
1730
+ if (!hasScopeId && !hasScopeIds && !hasScopeQuery && !hasScopeQueries) {
1731
+ issues.push({ path: "args.ids", message: "or args.query/args.queries[] is required" });
1732
+ }
1733
+ if (Array.isArray(args.ids) && args.ids.length === 0) {
1734
+ issues.push({ path: "args.ids", message: "must be a non-empty string[] when provided" });
1735
+ }
1736
+ if (Array.isArray(args.queries) && args.queries.length === 0) {
1737
+ issues.push({ path: "args.queries", message: "must be a non-empty string[] when provided" });
1738
+ }
1739
+ if (args.includeTitleSearch === false && args.includeSemanticSearch === false) {
1740
+ issues.push({
1741
+ path: "args.includeTitleSearch",
1742
+ message: "and includeSemanticSearch cannot both be false",
1743
+ });
1744
+ }
1745
+ if (Array.isArray(args.excludePatterns)) {
1746
+ for (let i = 0; i < args.excludePatterns.length; i += 1) {
1747
+ const pattern = args.excludePatterns[i];
1748
+ try {
1749
+ // Validate that each pattern is a compilable JS regex source string.
1750
+ new RegExp(pattern);
1751
+ } catch {
1752
+ issues.push({
1753
+ path: `args.excludePatterns[${i}]`,
1754
+ message: "must be a valid regex source string",
1755
+ });
1756
+ }
1757
+ }
1758
+ }
1759
+ },
1760
+ },
1761
+ "events.list": {
1762
+ properties: {
1763
+ actorId: { type: "string" },
1764
+ documentId: { type: "string" },
1765
+ collectionId: { type: "string" },
1766
+ name: { type: "string" },
1767
+ auditLog: { type: "boolean" },
1768
+ ip: { type: "string" },
1769
+ limit: { type: "number", min: 1 },
1770
+ offset: { type: "number", min: 0 },
1771
+ sort: { type: "string" },
1772
+ direction: { type: "string", enum: ["ASC", "DESC"] },
1773
+ includePolicies: { type: "boolean" },
1774
+ view: { type: "string", enum: ["ids", "summary", "full"] },
1775
+ maxAttempts: { type: "number", min: 1 },
1776
+ },
1777
+ },
1778
+ "documents.archived": {
1779
+ properties: {
1780
+ collectionId: { type: "string" },
1781
+ limit: { type: "number", min: 1 },
1782
+ offset: { type: "number", min: 0 },
1783
+ sort: { type: "string" },
1784
+ direction: { type: "string", enum: ["ASC", "DESC"] },
1785
+ includePolicies: { type: "boolean" },
1786
+ view: { type: "string", enum: ["ids", "summary", "full"] },
1787
+ maxAttempts: { type: "number", min: 1 },
1788
+ },
1789
+ },
1790
+ "documents.deleted": {
1791
+ properties: {
1792
+ limit: { type: "number", min: 1 },
1793
+ offset: { type: "number", min: 0 },
1794
+ sort: { type: "string" },
1795
+ direction: { type: "string", enum: ["ASC", "DESC"] },
1796
+ includePolicies: { type: "boolean" },
1797
+ view: { type: "string", enum: ["ids", "summary", "full"] },
1798
+ maxAttempts: { type: "number", min: 1 },
1799
+ },
1800
+ },
1801
+ "documents.archive": {
1802
+ required: ["id"],
1803
+ properties: {
1804
+ id: { type: "string" },
1805
+ includePolicies: { type: "boolean" },
1806
+ view: { type: "string", enum: ["summary", "full"] },
1807
+ maxAttempts: { type: "number", min: 1 },
1808
+ performAction: { type: "boolean" },
1809
+ },
1810
+ },
1811
+ "documents.restore": {
1812
+ required: ["id"],
1813
+ properties: {
1814
+ id: { type: "string" },
1815
+ collectionId: { type: "string" },
1816
+ revisionId: { type: "string" },
1817
+ includePolicies: { type: "boolean" },
1818
+ view: { type: "string", enum: ["summary", "full"] },
1819
+ maxAttempts: { type: "number", min: 1 },
1820
+ performAction: { type: "boolean" },
1821
+ },
1822
+ },
1823
+ "documents.permanent_delete": {
1824
+ required: ["id"],
1825
+ properties: {
1826
+ id: { type: "string" },
1827
+ includePolicies: { type: "boolean" },
1828
+ view: { type: "string", enum: ["summary", "full"] },
1829
+ maxAttempts: { type: "number", min: 1 },
1830
+ performAction: { type: "boolean" },
1831
+ },
1832
+ },
1833
+ "documents.empty_trash": {
1834
+ properties: {
1835
+ maxAttempts: { type: "number", min: 1 },
1836
+ performAction: { type: "boolean" },
1837
+ },
1838
+ },
1839
+ "webhooks.list": {
1840
+ properties: {
1841
+ event: { type: "string" },
1842
+ sort: { type: "string" },
1843
+ direction: { type: "string", enum: ["ASC", "DESC"] },
1844
+ limit: { type: "number", min: 1 },
1845
+ offset: { type: "number", min: 0 },
1846
+ includeSubscriptions: { type: "boolean" },
1847
+ includePolicies: { type: "boolean" },
1848
+ view: { type: "string", enum: ["ids", "summary", "full"] },
1849
+ maxAttempts: { type: "number", min: 1 },
1850
+ },
1851
+ },
1852
+ "webhooks.info": {
1853
+ required: ["id"],
1854
+ properties: {
1855
+ id: { type: "string" },
1856
+ includeSubscriptions: { type: "boolean" },
1857
+ includePolicies: { type: "boolean" },
1858
+ view: { type: "string", enum: ["summary", "full"] },
1859
+ maxAttempts: { type: "number", min: 1 },
1860
+ },
1861
+ },
1862
+ "webhooks.create": {
1863
+ required: ["name", "url", "events"],
1864
+ properties: {
1865
+ name: { type: "string" },
1866
+ url: { type: "string" },
1867
+ events: { type: "string[]" },
1868
+ includePolicies: { type: "boolean" },
1869
+ view: { type: "string", enum: ["summary", "full"] },
1870
+ maxAttempts: { type: "number", min: 1 },
1871
+ performAction: { type: "boolean" },
1872
+ },
1873
+ custom(args, issues) {
1874
+ if (Array.isArray(args.events) && args.events.length === 0) {
1875
+ issues.push({ path: "args.events", message: "must be a non-empty array" });
1876
+ }
1877
+ },
1878
+ },
1879
+ "webhooks.update": {
1880
+ required: ["id"],
1881
+ properties: {
1882
+ id: { type: "string" },
1883
+ name: { type: "string" },
1884
+ url: { type: "string" },
1885
+ events: { type: "string[]" },
1886
+ includePolicies: { type: "boolean" },
1887
+ view: { type: "string", enum: ["summary", "full"] },
1888
+ maxAttempts: { type: "number", min: 1 },
1889
+ performAction: { type: "boolean" },
1890
+ },
1891
+ custom(args, issues) {
1892
+ if (
1893
+ args.name === undefined &&
1894
+ args.url === undefined &&
1895
+ args.events === undefined
1896
+ ) {
1897
+ issues.push({ path: "args.name", message: "or args.url or args.events is required" });
1898
+ }
1899
+ if (Array.isArray(args.events) && args.events.length === 0) {
1900
+ issues.push({ path: "args.events", message: "must be a non-empty array" });
1901
+ }
1902
+ },
1903
+ },
1904
+ "webhooks.delete": {
1905
+ required: ["id"],
1906
+ properties: {
1907
+ id: { type: "string" },
1908
+ maxAttempts: { type: "number", min: 1 },
1909
+ performAction: { type: "boolean" },
1910
+ },
1911
+ },
1912
+ "users.list": {
1913
+ properties: {
1914
+ query: { type: "string" },
1915
+ role: { type: "string" },
1916
+ limit: { type: "number", min: 1 },
1917
+ offset: { type: "number", min: 0 },
1918
+ sort: { type: "string" },
1919
+ direction: { type: "string", enum: ["ASC", "DESC"] },
1920
+ includePolicies: { type: "boolean" },
1921
+ view: { type: "string", enum: ["ids", "summary", "full"] },
1922
+ maxAttempts: { type: "number", min: 1 },
1923
+ },
1924
+ },
1925
+ "users.info": {
1926
+ properties: {
1927
+ id: { type: "string" },
1928
+ ids: { type: "string[]" },
1929
+ email: { type: "string" },
1930
+ includePolicies: { type: "boolean" },
1931
+ concurrency: { type: "number", min: 1 },
1932
+ view: { type: "string", enum: ["summary", "full"] },
1933
+ maxAttempts: { type: "number", min: 1 },
1934
+ },
1935
+ custom(args, issues) {
1936
+ const hasId = typeof args.id === "string" && args.id.length > 0;
1937
+ const hasIds = Array.isArray(args.ids) && args.ids.length > 0;
1938
+
1939
+ if (!hasId && !hasIds && !args.email) {
1940
+ issues.push({ path: "args.id", message: "or args.ids[] or args.email is required" });
1941
+ }
1942
+ if (Array.isArray(args.ids) && args.ids.length === 0) {
1943
+ issues.push({ path: "args.ids", message: "must be a non-empty string[] when provided" });
1944
+ }
1945
+ if (hasId && Array.isArray(args.ids)) {
1946
+ issues.push({ path: "args.ids", message: "cannot be combined with args.id" });
1947
+ }
1948
+ },
1949
+ },
1950
+ "users.invite": {
1951
+ required: ["email"],
1952
+ properties: {
1953
+ email: { type: "string" },
1954
+ name: { type: "string" },
1955
+ role: { type: "string", enum: USER_ROLE_TYPES },
1956
+ includePolicies: { type: "boolean" },
1957
+ view: { type: "string", enum: ["summary", "full"] },
1958
+ maxAttempts: { type: "number", min: 1 },
1959
+ performAction: { type: "boolean" },
1960
+ },
1961
+ custom(args, issues) {
1962
+ if (typeof args.email === "string" && args.email.trim().length === 0) {
1963
+ issues.push({ path: "args.email", message: "must be a non-empty string" });
1964
+ }
1965
+ },
1966
+ },
1967
+ "users.update_role": {
1968
+ required: ["id", "role"],
1969
+ properties: {
1970
+ id: { type: "string" },
1971
+ role: { type: "string", enum: USER_ROLE_TYPES },
1972
+ includePolicies: { type: "boolean" },
1973
+ view: { type: "string", enum: ["summary", "full"] },
1974
+ maxAttempts: { type: "number", min: 1 },
1975
+ performAction: { type: "boolean" },
1976
+ },
1977
+ },
1978
+ "users.activate": {
1979
+ required: ["id"],
1980
+ properties: {
1981
+ id: { type: "string" },
1982
+ includePolicies: { type: "boolean" },
1983
+ view: { type: "string", enum: ["summary", "full"] },
1984
+ maxAttempts: { type: "number", min: 1 },
1985
+ performAction: { type: "boolean" },
1986
+ },
1987
+ },
1988
+ "users.suspend": {
1989
+ required: ["id"],
1990
+ properties: {
1991
+ id: { type: "string" },
1992
+ includePolicies: { type: "boolean" },
1993
+ view: { type: "string", enum: ["summary", "full"] },
1994
+ maxAttempts: { type: "number", min: 1 },
1995
+ performAction: { type: "boolean" },
1996
+ },
1997
+ },
1998
+ "groups.list": {
1999
+ properties: {
2000
+ query: { type: "string" },
2001
+ limit: { type: "number", min: 1 },
2002
+ offset: { type: "number", min: 0 },
2003
+ sort: { type: "string" },
2004
+ direction: { type: "string", enum: ["ASC", "DESC"] },
2005
+ includePolicies: { type: "boolean" },
2006
+ view: { type: "string", enum: ["ids", "summary", "full"] },
2007
+ maxAttempts: { type: "number", min: 1 },
2008
+ },
2009
+ },
2010
+ "groups.info": {
2011
+ properties: {
2012
+ id: { type: "string" },
2013
+ ids: { type: "string[]" },
2014
+ includePolicies: { type: "boolean" },
2015
+ concurrency: { type: "number", min: 1 },
2016
+ view: { type: "string", enum: ["summary", "full"] },
2017
+ maxAttempts: { type: "number", min: 1 },
2018
+ },
2019
+ custom(args, issues) {
2020
+ const hasId = typeof args.id === "string" && args.id.length > 0;
2021
+ const hasIds = Array.isArray(args.ids) && args.ids.length > 0;
2022
+
2023
+ if (!hasId && !hasIds) {
2024
+ issues.push({ path: "args.id", message: "or args.ids[] is required" });
2025
+ }
2026
+ if (Array.isArray(args.ids) && args.ids.length === 0) {
2027
+ issues.push({ path: "args.ids", message: "must be a non-empty string[] when provided" });
2028
+ }
2029
+ if (hasId && Array.isArray(args.ids)) {
2030
+ issues.push({ path: "args.ids", message: "cannot be combined with args.id" });
2031
+ }
2032
+ },
2033
+ },
2034
+ "groups.memberships": {
2035
+ required: ["id"],
2036
+ properties: {
2037
+ id: { type: "string" },
2038
+ limit: { type: "number", min: 1 },
2039
+ offset: { type: "number", min: 0 },
2040
+ sort: { type: "string" },
2041
+ direction: { type: "string", enum: ["ASC", "DESC"] },
2042
+ includePolicies: { type: "boolean" },
2043
+ view: { type: "string", enum: ["ids", "summary", "full"] },
2044
+ maxAttempts: { type: "number", min: 1 },
2045
+ },
2046
+ },
2047
+ "groups.create": {
2048
+ required: ["name"],
2049
+ properties: {
2050
+ name: { type: "string" },
2051
+ memberIds: { type: "string[]" },
2052
+ includePolicies: { type: "boolean" },
2053
+ view: { type: "string", enum: ["summary", "full"] },
2054
+ maxAttempts: { type: "number", min: 1 },
2055
+ performAction: { type: "boolean" },
2056
+ },
2057
+ custom(args, issues) {
2058
+ if (Array.isArray(args.memberIds) && args.memberIds.length === 0) {
2059
+ issues.push({ path: "args.memberIds", message: "must be a non-empty string[] when provided" });
2060
+ }
2061
+ },
2062
+ },
2063
+ "groups.update": {
2064
+ required: ["id"],
2065
+ properties: {
2066
+ id: { type: "string" },
2067
+ name: { type: "string" },
2068
+ includePolicies: { type: "boolean" },
2069
+ view: { type: "string", enum: ["summary", "full"] },
2070
+ maxAttempts: { type: "number", min: 1 },
2071
+ performAction: { type: "boolean" },
2072
+ },
2073
+ },
2074
+ "groups.delete": {
2075
+ required: ["id"],
2076
+ properties: {
2077
+ id: { type: "string" },
2078
+ maxAttempts: { type: "number", min: 1 },
2079
+ performAction: { type: "boolean" },
2080
+ },
2081
+ },
2082
+ "groups.add_user": {
2083
+ required: ["id", "userId"],
2084
+ properties: {
2085
+ id: { type: "string" },
2086
+ userId: { type: "string" },
2087
+ maxAttempts: { type: "number", min: 1 },
2088
+ performAction: { type: "boolean" },
2089
+ },
2090
+ },
2091
+ "groups.remove_user": {
2092
+ required: ["id", "userId"],
2093
+ properties: {
2094
+ id: { type: "string" },
2095
+ userId: { type: "string" },
2096
+ maxAttempts: { type: "number", min: 1 },
2097
+ performAction: { type: "boolean" },
2098
+ },
2099
+ },
2100
+ "collections.memberships": {
2101
+ required: ["id"],
2102
+ properties: {
2103
+ id: { type: "string" },
2104
+ limit: { type: "number", min: 1 },
2105
+ offset: { type: "number", min: 0 },
2106
+ sort: { type: "string" },
2107
+ direction: { type: "string", enum: ["ASC", "DESC"] },
2108
+ includePolicies: { type: "boolean" },
2109
+ view: { type: "string", enum: ["ids", "summary", "full"] },
2110
+ maxAttempts: { type: "number", min: 1 },
2111
+ },
2112
+ },
2113
+ "collections.group_memberships": {
2114
+ required: ["id"],
2115
+ properties: {
2116
+ id: { type: "string" },
2117
+ limit: { type: "number", min: 1 },
2118
+ offset: { type: "number", min: 0 },
2119
+ sort: { type: "string" },
2120
+ direction: { type: "string", enum: ["ASC", "DESC"] },
2121
+ includePolicies: { type: "boolean" },
2122
+ view: { type: "string", enum: ["ids", "summary", "full"] },
2123
+ maxAttempts: { type: "number", min: 1 },
2124
+ },
2125
+ },
2126
+ "collections.add_user": {
2127
+ required: ["id", "userId"],
2128
+ properties: {
2129
+ id: { type: "string" },
2130
+ userId: { type: "string" },
2131
+ maxAttempts: { type: "number", min: 1 },
2132
+ performAction: { type: "boolean" },
2133
+ },
2134
+ },
2135
+ "collections.remove_user": {
2136
+ required: ["id", "userId"],
2137
+ properties: {
2138
+ id: { type: "string" },
2139
+ userId: { type: "string" },
2140
+ maxAttempts: { type: "number", min: 1 },
2141
+ performAction: { type: "boolean" },
2142
+ },
2143
+ },
2144
+ "collections.add_group": {
2145
+ required: ["id", "groupId"],
2146
+ properties: {
2147
+ id: { type: "string" },
2148
+ groupId: { type: "string" },
2149
+ maxAttempts: { type: "number", min: 1 },
2150
+ performAction: { type: "boolean" },
2151
+ },
2152
+ },
2153
+ "collections.remove_group": {
2154
+ required: ["id", "groupId"],
2155
+ properties: {
2156
+ id: { type: "string" },
2157
+ groupId: { type: "string" },
2158
+ maxAttempts: { type: "number", min: 1 },
2159
+ performAction: { type: "boolean" },
2160
+ },
2161
+ },
2162
+ "documents.memberships": {
2163
+ required: ["id"],
2164
+ properties: {
2165
+ id: { type: "string" },
2166
+ limit: { type: "number", min: 1 },
2167
+ offset: { type: "number", min: 0 },
2168
+ sort: { type: "string" },
2169
+ direction: { type: "string", enum: ["ASC", "DESC"] },
2170
+ includePolicies: { type: "boolean" },
2171
+ view: { type: "string", enum: ["ids", "summary", "full"] },
2172
+ maxAttempts: { type: "number", min: 1 },
2173
+ },
2174
+ },
2175
+ "documents.users": {
2176
+ required: ["id"],
2177
+ properties: {
2178
+ id: { type: "string" },
2179
+ limit: { type: "number", min: 1 },
2180
+ offset: { type: "number", min: 0 },
2181
+ sort: { type: "string" },
2182
+ direction: { type: "string", enum: ["ASC", "DESC"] },
2183
+ includePolicies: { type: "boolean" },
2184
+ view: { type: "string", enum: ["ids", "summary", "full"] },
2185
+ maxAttempts: { type: "number", min: 1 },
2186
+ },
2187
+ },
2188
+ "documents.group_memberships": {
2189
+ required: ["id"],
2190
+ properties: {
2191
+ id: { type: "string" },
2192
+ limit: { type: "number", min: 1 },
2193
+ offset: { type: "number", min: 0 },
2194
+ sort: { type: "string" },
2195
+ direction: { type: "string", enum: ["ASC", "DESC"] },
2196
+ includePolicies: { type: "boolean" },
2197
+ view: { type: "string", enum: ["ids", "summary", "full"] },
2198
+ maxAttempts: { type: "number", min: 1 },
2199
+ },
2200
+ },
2201
+ "documents.add_user": {
2202
+ required: ["id", "userId"],
2203
+ properties: {
2204
+ id: { type: "string" },
2205
+ userId: { type: "string" },
2206
+ maxAttempts: { type: "number", min: 1 },
2207
+ performAction: { type: "boolean" },
2208
+ },
2209
+ },
2210
+ "documents.remove_user": {
2211
+ required: ["id", "userId"],
2212
+ properties: {
2213
+ id: { type: "string" },
2214
+ userId: { type: "string" },
2215
+ maxAttempts: { type: "number", min: 1 },
2216
+ performAction: { type: "boolean" },
2217
+ },
2218
+ },
2219
+ "documents.add_group": {
2220
+ required: ["id", "groupId"],
2221
+ properties: {
2222
+ id: { type: "string" },
2223
+ groupId: { type: "string" },
2224
+ maxAttempts: { type: "number", min: 1 },
2225
+ performAction: { type: "boolean" },
2226
+ },
2227
+ },
2228
+ "documents.remove_group": {
2229
+ required: ["id", "groupId"],
2230
+ properties: {
2231
+ id: { type: "string" },
2232
+ groupId: { type: "string" },
2233
+ maxAttempts: { type: "number", min: 1 },
2234
+ performAction: { type: "boolean" },
2235
+ },
2236
+ },
2237
+ "documents.answer": {
2238
+ properties: {
2239
+ question: { type: "string" },
2240
+ query: { type: "string" },
2241
+ collectionId: { type: "string" },
2242
+ documentId: { type: "string" },
2243
+ userId: { type: "string" },
2244
+ statusFilter: { type: ["string", "string[]"] },
2245
+ dateFilter: { type: "string", enum: ["day", "week", "month", "year"] },
2246
+ includePolicies: { type: "boolean" },
2247
+ includeEvidenceDocs: { type: "boolean" },
2248
+ view: { type: "string", enum: ["summary", "full"] },
2249
+ maxAttempts: { type: "number", min: 1 },
2250
+ },
2251
+ custom(args, issues) {
2252
+ const selected = args.question ?? args.query;
2253
+ if (typeof selected !== "string" || selected.trim().length === 0) {
2254
+ issues.push({ path: "args.question", message: "or args.query is required and must be non-empty" });
2255
+ }
2256
+ },
2257
+ },
2258
+ "documents.answer_batch": {
2259
+ properties: {
2260
+ question: { type: "string" },
2261
+ query: { type: "string" },
2262
+ questions: { type: "array" },
2263
+ collectionId: { type: "string" },
2264
+ documentId: { type: "string" },
2265
+ userId: { type: "string" },
2266
+ statusFilter: { type: ["string", "string[]"] },
2267
+ dateFilter: { type: "string", enum: ["day", "week", "month", "year"] },
2268
+ includePolicies: { type: "boolean" },
2269
+ includeEvidenceDocs: { type: "boolean" },
2270
+ view: { type: "string", enum: ["summary", "full"] },
2271
+ concurrency: { type: "number", min: 1 },
2272
+ maxAttempts: { type: "number", min: 1 },
2273
+ },
2274
+ custom(args, issues) {
2275
+ const hasSingle = args.question !== undefined || args.query !== undefined;
2276
+ const singleQuestion = args.question ?? args.query;
2277
+ const questionCount = Array.isArray(args.questions) ? args.questions.length : 0;
2278
+
2279
+ if (hasSingle && (typeof singleQuestion !== "string" || singleQuestion.trim().length === 0)) {
2280
+ issues.push({ path: "args.question", message: "or args.query must be a non-empty string when provided" });
2281
+ }
2282
+
2283
+ if (!hasSingle && questionCount === 0) {
2284
+ issues.push({ path: "args.questions", message: "or args.question or args.query is required" });
2285
+ }
2286
+
2287
+ if (args.questions === undefined) {
2288
+ return;
2289
+ }
2290
+
2291
+ if (!Array.isArray(args.questions)) {
2292
+ return;
2293
+ }
2294
+
2295
+ for (let i = 0; i < args.questions.length; i += 1) {
2296
+ const item = args.questions[i];
2297
+ if (typeof item === "string") {
2298
+ if (item.trim().length === 0) {
2299
+ issues.push({ path: `args.questions[${i}]`, message: "must be a non-empty string" });
2300
+ }
2301
+ continue;
2302
+ }
2303
+
2304
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
2305
+ issues.push({ path: `args.questions[${i}]`, message: "must be a string or object" });
2306
+ continue;
2307
+ }
2308
+
2309
+ const itemQuestion = item.question ?? item.query;
2310
+ if (typeof itemQuestion !== "string" || itemQuestion.trim().length === 0) {
2311
+ issues.push({ path: `args.questions[${i}].question`, message: "or .query must be a non-empty string" });
2312
+ }
2313
+ }
2314
+ },
2315
+ },
2316
+ "capabilities.map": {
2317
+ properties: {
2318
+ includePolicies: { type: "boolean" },
2319
+ includeRaw: { type: "boolean" },
2320
+ },
2321
+ },
2322
+ "documents.cleanup_test": {
2323
+ properties: {
2324
+ markerPrefix: { type: "string" },
2325
+ olderThanHours: { type: "number", min: 0 },
2326
+ dryRun: { type: "boolean" },
2327
+ maxPages: { type: "number", min: 1 },
2328
+ pageLimit: { type: "number", min: 1 },
2329
+ concurrency: { type: "number", min: 1 },
2330
+ allowUnsafePrefix: { type: "boolean" },
2331
+ includeErrors: { type: "boolean" },
2332
+ deleteMode: { type: "string", enum: ["safe", "direct"] },
2333
+ performAction: { type: "boolean" },
2334
+ },
2335
+ },
2336
+ };
2337
+
2338
+ export function validateToolArgs(toolName, args = {}) {
2339
+ const spec = TOOL_ARG_SCHEMAS[toolName];
2340
+ if (!spec) {
2341
+ return;
2342
+ }
2343
+
2344
+ ensureObject(toolName, args);
2345
+ validateSpec(toolName, args, spec);
2346
+ }