@get-bb/plugin-sdk 0.4.9 → 0.4.11
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/README.md +43 -2
- package/bundled-types/bb-plugin-sdk-app.d.ts +253 -28
- package/bundled-types/bb-plugin-sdk-internal-file-navigation-validation.d.ts +42 -0
- package/bundled-types/bb-plugin-sdk-internal-host-policy.d.ts +209 -16
- package/bundled-types/bb-plugin-sdk-provider-bridge-testing.d.ts +3314 -0
- package/bundled-types/bb-plugin-sdk-provider-bridge.d.ts +1362 -327
- package/bundled-types/bb-plugin-sdk-testing-app.d.ts +38 -2
- package/bundled-types/bb-plugin-sdk.d.ts +2259 -526
- package/dist/app.js +10 -0
- package/dist/internal/file-navigation-validation.js +135 -0
- package/dist/internal/host-policy.js +467 -4
- package/dist/internal/plugin-app-collector.js +22 -1
- package/dist/provider-bridge-testing.js +5777 -0
- package/dist/provider-bridge.js +2906 -2136
- package/dist/testing/app.js +334 -2
- package/dist/testing/index.js +481 -21
- package/package.json +13 -1
|
@@ -54,6 +54,7 @@ var PLUGIN_AGENT_DYNAMIC_INSTRUCTIONS_MAX_CHARS = 4096;
|
|
|
54
54
|
var PLUGIN_AGENT_TOOL_PARAMETERS_MAX_BYTES = 128 * 1024;
|
|
55
55
|
var MENTION_PROVIDER_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
56
56
|
var PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,63}$/;
|
|
57
|
+
var PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES = 64 * 1024;
|
|
57
58
|
var SETTING_KEY_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
58
59
|
var settingsBaseFields = {
|
|
59
60
|
label: z2.string().min(1),
|
|
@@ -255,6 +256,383 @@ function validateProviderLiteralArray(args) {
|
|
|
255
256
|
}
|
|
256
257
|
return Object.freeze(normalized);
|
|
257
258
|
}
|
|
259
|
+
function normalizeProviderBridgeOptions(providerId, value, label = "experimental_bridgeOptions") {
|
|
260
|
+
const active = /* @__PURE__ */ new Set();
|
|
261
|
+
function visit(current, path) {
|
|
262
|
+
if (current === null || typeof current === "string" || typeof current === "boolean") {
|
|
263
|
+
return current;
|
|
264
|
+
}
|
|
265
|
+
if (typeof current === "number") {
|
|
266
|
+
if (!Number.isFinite(current)) {
|
|
267
|
+
throw new Error(
|
|
268
|
+
`provider "${providerId}" ${label}${path} must be finite JSON`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return current;
|
|
272
|
+
}
|
|
273
|
+
if (typeof current !== "object") {
|
|
274
|
+
throw new Error(
|
|
275
|
+
`provider "${providerId}" ${label}${path} must be JSON`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
if (active.has(current)) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`provider "${providerId}" experimental_bridgeOptions must not contain cycles`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
active.add(current);
|
|
284
|
+
try {
|
|
285
|
+
if (Array.isArray(current)) {
|
|
286
|
+
const normalized3 = current.map(
|
|
287
|
+
(entry, index) => visit(entry, `${path}[${index}]`)
|
|
288
|
+
);
|
|
289
|
+
Object.freeze(normalized3);
|
|
290
|
+
return normalized3;
|
|
291
|
+
}
|
|
292
|
+
const prototype = Object.getPrototypeOf(current);
|
|
293
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
`provider "${providerId}" ${label}${path} must contain only plain JSON objects`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
const normalized2 = Object.fromEntries(
|
|
299
|
+
Object.entries(current).map(([key, entry]) => [
|
|
300
|
+
key,
|
|
301
|
+
visit(entry, `${path}.${key}`)
|
|
302
|
+
])
|
|
303
|
+
);
|
|
304
|
+
Object.freeze(normalized2);
|
|
305
|
+
return normalized2;
|
|
306
|
+
} finally {
|
|
307
|
+
active.delete(current);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const normalized = visit(value, "");
|
|
311
|
+
if (normalized === null || Array.isArray(normalized) || typeof normalized !== "object") {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`provider "${providerId}" ${label} must be an object`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
if (Buffer.byteLength(JSON.stringify(normalized), "utf8") > PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES) {
|
|
317
|
+
throw new Error(
|
|
318
|
+
`provider "${providerId}" ${label} exceeds ${PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES} bytes`
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
return normalized;
|
|
322
|
+
}
|
|
323
|
+
var PROVIDER_STRING_MAX_CHARS = 512;
|
|
324
|
+
var PROVIDER_EXTENSION_KIND_NAME_PATTERN = /^[a-z0-9-]+$/u;
|
|
325
|
+
var PROVIDER_EXTENSION_KINDS_MAX = 32;
|
|
326
|
+
function requireNonBlankString(args) {
|
|
327
|
+
const { providerId, field, value } = args;
|
|
328
|
+
if (typeof value !== "string" || value.trim().length === 0 || value.length > PROVIDER_STRING_MAX_CHARS) {
|
|
329
|
+
throw new Error(
|
|
330
|
+
`provider "${providerId}" ${field} must be a non-blank string of at most ${PROVIDER_STRING_MAX_CHARS} characters`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
return value;
|
|
334
|
+
}
|
|
335
|
+
function validateProviderStrings(providerId, value) {
|
|
336
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
337
|
+
throw new Error(
|
|
338
|
+
`provider "${providerId}" experimental_strings must be an object`
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
const record = Object.fromEntries(
|
|
342
|
+
Object.entries(value)
|
|
343
|
+
);
|
|
344
|
+
const required = (field) => requireNonBlankString({
|
|
345
|
+
providerId,
|
|
346
|
+
field: `experimental_strings.${field}`,
|
|
347
|
+
value: record[field]
|
|
348
|
+
});
|
|
349
|
+
const optional = (field) => record[field] === void 0 ? void 0 : requireNonBlankString({
|
|
350
|
+
providerId,
|
|
351
|
+
field: `experimental_strings.${field}`,
|
|
352
|
+
value: record[field]
|
|
353
|
+
});
|
|
354
|
+
let iconTint;
|
|
355
|
+
if (record.iconTint !== void 0) {
|
|
356
|
+
const tint = record.iconTint;
|
|
357
|
+
if (typeof tint !== "object" || tint === null || Array.isArray(tint)) {
|
|
358
|
+
throw new Error(
|
|
359
|
+
`provider "${providerId}" experimental_strings.iconTint must be { light, dark }`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
const tintRecord = Object.fromEntries(
|
|
363
|
+
Object.entries(tint)
|
|
364
|
+
);
|
|
365
|
+
iconTint = Object.freeze({
|
|
366
|
+
light: requireNonBlankString({
|
|
367
|
+
providerId,
|
|
368
|
+
field: "experimental_strings.iconTint.light",
|
|
369
|
+
value: tintRecord.light
|
|
370
|
+
}),
|
|
371
|
+
dark: requireNonBlankString({
|
|
372
|
+
providerId,
|
|
373
|
+
field: "experimental_strings.iconTint.dark",
|
|
374
|
+
value: tintRecord.dark
|
|
375
|
+
})
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
const brandPrefix = optional("brandPrefix");
|
|
379
|
+
const planModeCopy = optional("planModeCopy");
|
|
380
|
+
return Object.freeze({
|
|
381
|
+
signInHint: required("signInHint"),
|
|
382
|
+
expiredHint: required("expiredHint"),
|
|
383
|
+
installUrl: required("installUrl"),
|
|
384
|
+
...brandPrefix === void 0 ? {} : { brandPrefix },
|
|
385
|
+
...planModeCopy === void 0 ? {} : { planModeCopy },
|
|
386
|
+
...iconTint === void 0 ? {} : { iconTint }
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
function validateProviderOptionDescriptors(args) {
|
|
390
|
+
const { providerId, field, value } = args;
|
|
391
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
392
|
+
throw new Error(
|
|
393
|
+
`provider "${providerId}" ${field} must be a non-empty array of { id, label, description? }`
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
const seen = /* @__PURE__ */ new Set();
|
|
397
|
+
const normalized = value.map(
|
|
398
|
+
(entry, index) => {
|
|
399
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
400
|
+
throw new Error(
|
|
401
|
+
`provider "${providerId}" ${field}[${index}] must be { id, label, description? }`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
const record = Object.fromEntries(
|
|
405
|
+
Object.entries(entry)
|
|
406
|
+
);
|
|
407
|
+
const id = requireNonBlankString({
|
|
408
|
+
providerId,
|
|
409
|
+
field: `${field}[${index}].id`,
|
|
410
|
+
value: record.id
|
|
411
|
+
});
|
|
412
|
+
if (seen.has(id)) {
|
|
413
|
+
throw new Error(
|
|
414
|
+
`provider "${providerId}" ${field} id ${JSON.stringify(id)} is duplicated`
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
seen.add(id);
|
|
418
|
+
const label = requireNonBlankString({
|
|
419
|
+
providerId,
|
|
420
|
+
field: `${field}[${index}].label`,
|
|
421
|
+
value: record.label
|
|
422
|
+
});
|
|
423
|
+
const description = record.description === void 0 ? void 0 : requireNonBlankString({
|
|
424
|
+
providerId,
|
|
425
|
+
field: `${field}[${index}].description`,
|
|
426
|
+
value: record.description
|
|
427
|
+
});
|
|
428
|
+
return Object.freeze({
|
|
429
|
+
id,
|
|
430
|
+
label,
|
|
431
|
+
...description === void 0 ? {} : { description }
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
);
|
|
435
|
+
return Object.freeze(normalized);
|
|
436
|
+
}
|
|
437
|
+
function validateProviderExtensionKinds(providerId, value) {
|
|
438
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
439
|
+
throw new Error(
|
|
440
|
+
`provider "${providerId}" experimental_extensionKinds must be an object keyed by kind name`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
const entries = Object.entries(value);
|
|
444
|
+
if (entries.length > PROVIDER_EXTENSION_KINDS_MAX) {
|
|
445
|
+
throw new Error(
|
|
446
|
+
`provider "${providerId}" experimental_extensionKinds declares more than ${PROVIDER_EXTENSION_KINDS_MAX} kinds`
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
const normalized = {};
|
|
450
|
+
for (const [name, declaration] of entries) {
|
|
451
|
+
if (!PROVIDER_EXTENSION_KIND_NAME_PATTERN.test(name)) {
|
|
452
|
+
throw new Error(
|
|
453
|
+
`provider "${providerId}" experimental_extensionKinds name ${JSON.stringify(name)} must match ${PROVIDER_EXTENSION_KIND_NAME_PATTERN}`
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
if (typeof declaration !== "object" || declaration === null || Array.isArray(declaration)) {
|
|
457
|
+
throw new Error(
|
|
458
|
+
`provider "${providerId}" experimental_extensionKinds.${name} must be { item?, state? }`
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
const item = Reflect.get(declaration, "item");
|
|
462
|
+
const state = Reflect.get(declaration, "state");
|
|
463
|
+
if (item === void 0 && state === void 0) {
|
|
464
|
+
throw new Error(
|
|
465
|
+
`provider "${providerId}" experimental_extensionKinds.${name} must declare an item schema, a state schema, or both`
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
if (item !== void 0 && !isStandardSchema(item)) {
|
|
469
|
+
throw new Error(
|
|
470
|
+
`provider "${providerId}" experimental_extensionKinds.${name}.item must be a Standard Schema v1 validator`
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
if (state !== void 0 && !isStandardSchema(state)) {
|
|
474
|
+
throw new Error(
|
|
475
|
+
`provider "${providerId}" experimental_extensionKinds.${name}.state must be a Standard Schema v1 validator`
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
normalized[name] = Object.freeze({
|
|
479
|
+
...item === void 0 ? {} : { item },
|
|
480
|
+
...state === void 0 ? {} : { state }
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
return Object.freeze(normalized);
|
|
484
|
+
}
|
|
485
|
+
var PROVIDER_FALLBACK_MODELS_MAX = 64;
|
|
486
|
+
var PROVIDER_ENV_PASSTHROUGH_MAX = 32;
|
|
487
|
+
var PROVIDER_ENV_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/u;
|
|
488
|
+
function validateProviderEnvPassthrough(providerId, value) {
|
|
489
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
490
|
+
throw new Error(
|
|
491
|
+
`provider "${providerId}" experimental_env must be { passthrough: [...] }`
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
const passthrough = Reflect.get(value, "passthrough");
|
|
495
|
+
if (!Array.isArray(passthrough)) {
|
|
496
|
+
throw new Error(
|
|
497
|
+
`provider "${providerId}" experimental_env.passthrough must be an array of variable names`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
if (passthrough.length > PROVIDER_ENV_PASSTHROUGH_MAX) {
|
|
501
|
+
throw new Error(
|
|
502
|
+
`provider "${providerId}" experimental_env.passthrough names more than ${PROVIDER_ENV_PASSTHROUGH_MAX} variables`
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
const seen = /* @__PURE__ */ new Set();
|
|
506
|
+
for (const name of passthrough) {
|
|
507
|
+
if (typeof name !== "string" || !PROVIDER_ENV_NAME_PATTERN.test(name)) {
|
|
508
|
+
throw new Error(
|
|
509
|
+
`provider "${providerId}" experimental_env.passthrough entries must match ${PROVIDER_ENV_NAME_PATTERN}`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
if (seen.has(name)) {
|
|
513
|
+
throw new Error(
|
|
514
|
+
`provider "${providerId}" experimental_env.passthrough repeats ${JSON.stringify(name)}`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
seen.add(name);
|
|
518
|
+
}
|
|
519
|
+
return Object.freeze([...seen]);
|
|
520
|
+
}
|
|
521
|
+
function validateProviderFallbackModels(providerId, value) {
|
|
522
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
523
|
+
throw new Error(
|
|
524
|
+
`provider "${providerId}" experimental_models must be { fallback: [...] }`
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
const fallback = Reflect.get(value, "fallback");
|
|
528
|
+
if (!Array.isArray(fallback)) {
|
|
529
|
+
throw new Error(
|
|
530
|
+
`provider "${providerId}" experimental_models.fallback must be an array`
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
if (fallback.length > PROVIDER_FALLBACK_MODELS_MAX) {
|
|
534
|
+
throw new Error(
|
|
535
|
+
`provider "${providerId}" experimental_models.fallback lists more than ${PROVIDER_FALLBACK_MODELS_MAX} models`
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
const seen = /* @__PURE__ */ new Set();
|
|
539
|
+
let defaults = 0;
|
|
540
|
+
const normalized = fallback.map((entry, index) => {
|
|
541
|
+
const field = `experimental_models.fallback[${index}]`;
|
|
542
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
543
|
+
throw new Error(`provider "${providerId}" ${field} must be an object`);
|
|
544
|
+
}
|
|
545
|
+
const record = Object.fromEntries(
|
|
546
|
+
Object.entries(entry)
|
|
547
|
+
);
|
|
548
|
+
const id = requireNonBlankString({
|
|
549
|
+
providerId,
|
|
550
|
+
field: `${field}.id`,
|
|
551
|
+
value: record.id
|
|
552
|
+
});
|
|
553
|
+
if (seen.has(id)) {
|
|
554
|
+
throw new Error(
|
|
555
|
+
`provider "${providerId}" experimental_models.fallback id ${JSON.stringify(id)} is duplicated`
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
seen.add(id);
|
|
559
|
+
const displayName = requireNonBlankString({
|
|
560
|
+
providerId,
|
|
561
|
+
field: `${field}.displayName`,
|
|
562
|
+
value: record.displayName
|
|
563
|
+
});
|
|
564
|
+
const description = requireNonBlankString({
|
|
565
|
+
providerId,
|
|
566
|
+
field: `${field}.description`,
|
|
567
|
+
value: record.description
|
|
568
|
+
});
|
|
569
|
+
const efforts = record.supportedReasoningEfforts;
|
|
570
|
+
if (!Array.isArray(efforts) || efforts.length === 0) {
|
|
571
|
+
throw new Error(
|
|
572
|
+
`provider "${providerId}" ${field}.supportedReasoningEfforts must be a non-empty array`
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
const levels = /* @__PURE__ */ new Set();
|
|
576
|
+
const supportedReasoningEfforts = efforts.map(
|
|
577
|
+
(effort, effortIndex) => {
|
|
578
|
+
if (typeof effort !== "object" || effort === null || Array.isArray(effort)) {
|
|
579
|
+
throw new Error(
|
|
580
|
+
`provider "${providerId}" ${field}.supportedReasoningEfforts[${effortIndex}] must be { reasoningEffort, description }`
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
const reasoningEffort = Reflect.get(effort, "reasoningEffort");
|
|
584
|
+
if (typeof reasoningEffort !== "string" || !PLUGIN_PROVIDER_REASONING_LEVEL_VALUES.includes(
|
|
585
|
+
reasoningEffort
|
|
586
|
+
)) {
|
|
587
|
+
throw new Error(
|
|
588
|
+
`provider "${providerId}" ${field}.supportedReasoningEfforts[${effortIndex}].reasoningEffort must be one of ${PLUGIN_PROVIDER_REASONING_LEVEL_VALUES.join(", ")}`
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
const level = reasoningEffort;
|
|
592
|
+
if (levels.has(level)) {
|
|
593
|
+
throw new Error(
|
|
594
|
+
`provider "${providerId}" ${field}.supportedReasoningEfforts repeats ${JSON.stringify(level)}`
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
levels.add(level);
|
|
598
|
+
return Object.freeze({
|
|
599
|
+
reasoningEffort: level,
|
|
600
|
+
description: requireNonBlankString({
|
|
601
|
+
providerId,
|
|
602
|
+
field: `${field}.supportedReasoningEfforts[${effortIndex}].description`,
|
|
603
|
+
value: Reflect.get(effort, "description")
|
|
604
|
+
})
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
);
|
|
608
|
+
const defaultReasoningEffort = record.defaultReasoningEffort;
|
|
609
|
+
if (typeof defaultReasoningEffort !== "string" || !levels.has(defaultReasoningEffort)) {
|
|
610
|
+
throw new Error(
|
|
611
|
+
`provider "${providerId}" ${field}.defaultReasoningEffort must be one of its supportedReasoningEfforts`
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
if (typeof record.isDefault !== "boolean") {
|
|
615
|
+
throw new Error(
|
|
616
|
+
`provider "${providerId}" ${field}.isDefault must be a boolean`
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
if (record.isDefault) defaults += 1;
|
|
620
|
+
return Object.freeze({
|
|
621
|
+
id,
|
|
622
|
+
displayName,
|
|
623
|
+
description,
|
|
624
|
+
supportedReasoningEfforts: Object.freeze(supportedReasoningEfforts),
|
|
625
|
+
defaultReasoningEffort,
|
|
626
|
+
isDefault: record.isDefault
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
if (normalized.length > 0 && defaults !== 1) {
|
|
630
|
+
throw new Error(
|
|
631
|
+
`provider "${providerId}" experimental_models.fallback must mark exactly one model isDefault (found ${defaults})`
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
return Object.freeze(normalized);
|
|
635
|
+
}
|
|
258
636
|
function validatePluginProviderDeclaration(declaration) {
|
|
259
637
|
if (typeof declaration !== "object" || declaration === null) {
|
|
260
638
|
throw new Error("provider declaration must be an object");
|
|
@@ -265,6 +643,12 @@ function validatePluginProviderDeclaration(declaration) {
|
|
|
265
643
|
`invalid provider id ${JSON.stringify(id)} \u2014 use 2-64 lowercase letters, digits, and "-", starting with a letter or digit`
|
|
266
644
|
);
|
|
267
645
|
}
|
|
646
|
+
const family = declaration.experimental_family;
|
|
647
|
+
if (family !== void 0 && (typeof family !== "string" || !PROVIDER_ID_PATTERN.test(family))) {
|
|
648
|
+
throw new Error(
|
|
649
|
+
`provider "${id}" experimental_family must use the provider id grammar (2-64 lowercase letters, digits, and "-")`
|
|
650
|
+
);
|
|
651
|
+
}
|
|
268
652
|
const displayName = typeof declaration.displayName === "string" ? declaration.displayName.trim() : "";
|
|
269
653
|
if (displayName.length === 0 || displayName.length > PLUGIN_PROVIDER_DISPLAY_NAME_MAX_CHARS) {
|
|
270
654
|
throw new Error(
|
|
@@ -292,13 +676,30 @@ function validatePluginProviderDeclaration(declaration) {
|
|
|
292
676
|
if (typeof capabilities !== "object" || capabilities === null) {
|
|
293
677
|
throw new Error(`provider "${id}" capabilities must be an object`);
|
|
294
678
|
}
|
|
679
|
+
const experimentalProviderHealth = capabilities.experimental_providerHealth ?? false;
|
|
680
|
+
const experimentalProviderUsage = capabilities.experimental_providerUsage ?? false;
|
|
681
|
+
const experimentalProviderInstallation = capabilities.experimental_providerInstallation ?? false;
|
|
682
|
+
if (typeof experimentalProviderHealth !== "boolean") {
|
|
683
|
+
throw new Error(
|
|
684
|
+
`provider "${id}" capabilities.experimental_providerHealth must be a boolean`
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
if (typeof experimentalProviderUsage !== "boolean") {
|
|
688
|
+
throw new Error(
|
|
689
|
+
`provider "${id}" capabilities.experimental_providerUsage must be a boolean`
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
if (typeof experimentalProviderInstallation !== "boolean") {
|
|
693
|
+
throw new Error(
|
|
694
|
+
`provider "${id}" capabilities.experimental_providerInstallation must be a boolean`
|
|
695
|
+
);
|
|
696
|
+
}
|
|
295
697
|
const booleanCapabilityFields = [
|
|
296
698
|
"supportsServiceTier",
|
|
297
699
|
"supportsNativeUserQuestion",
|
|
298
700
|
"supportsManualCompaction",
|
|
299
701
|
"supportsThreadArchive",
|
|
300
|
-
"supportsThreadRename"
|
|
301
|
-
"supportsWorkflows"
|
|
702
|
+
"supportsThreadRename"
|
|
302
703
|
];
|
|
303
704
|
for (const field of booleanCapabilityFields) {
|
|
304
705
|
if (typeof capabilities[field] !== "boolean") {
|
|
@@ -313,13 +714,15 @@ function validatePluginProviderDeclaration(declaration) {
|
|
|
313
714
|
);
|
|
314
715
|
}
|
|
315
716
|
const normalizedCapabilities = Object.freeze({
|
|
717
|
+
experimental_providerHealth: experimentalProviderHealth,
|
|
718
|
+
experimental_providerUsage: experimentalProviderUsage,
|
|
719
|
+
experimental_providerInstallation: experimentalProviderInstallation,
|
|
316
720
|
supportsServiceTier: capabilities.supportsServiceTier,
|
|
317
721
|
supportsNativeUserQuestion: capabilities.supportsNativeUserQuestion,
|
|
318
722
|
fork: capabilities.fork,
|
|
319
723
|
supportsManualCompaction: capabilities.supportsManualCompaction,
|
|
320
724
|
supportsThreadArchive: capabilities.supportsThreadArchive,
|
|
321
725
|
supportsThreadRename: capabilities.supportsThreadRename,
|
|
322
|
-
supportsWorkflows: capabilities.supportsWorkflows,
|
|
323
726
|
permissionModes: validateProviderLiteralArray({
|
|
324
727
|
providerId: id,
|
|
325
728
|
field: "capabilities.permissionModes",
|
|
@@ -342,14 +745,72 @@ function validatePluginProviderDeclaration(declaration) {
|
|
|
342
745
|
allowed: PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES,
|
|
343
746
|
requireNonEmpty: false
|
|
344
747
|
});
|
|
748
|
+
const bridgeOptions = declaration.experimental_bridgeOptions === void 0 ? void 0 : normalizeProviderBridgeOptions(
|
|
749
|
+
id,
|
|
750
|
+
declaration.experimental_bridgeOptions
|
|
751
|
+
);
|
|
752
|
+
const visibility = declaration.experimental_visibility ?? "always";
|
|
753
|
+
if (visibility !== "always" && visibility !== "installed") {
|
|
754
|
+
throw new Error(
|
|
755
|
+
`provider "${id}" experimental_visibility must be "always" or "installed"`
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
if (visibility === "installed" && !normalizedCapabilities.experimental_providerHealth) {
|
|
759
|
+
throw new Error(
|
|
760
|
+
`provider "${id}" experimental_visibility "installed" requires experimental_providerHealth`
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
const strings = declaration.experimental_strings === void 0 ? void 0 : validateProviderStrings(id, declaration.experimental_strings);
|
|
764
|
+
const serviceTiers = declaration.experimental_serviceTiers === void 0 ? void 0 : validateProviderOptionDescriptors({
|
|
765
|
+
providerId: id,
|
|
766
|
+
field: "experimental_serviceTiers",
|
|
767
|
+
value: declaration.experimental_serviceTiers
|
|
768
|
+
});
|
|
769
|
+
const reasoningLevels = declaration.experimental_reasoningLevels === void 0 ? void 0 : validateProviderOptionDescriptors({
|
|
770
|
+
providerId: id,
|
|
771
|
+
field: "experimental_reasoningLevels",
|
|
772
|
+
value: declaration.experimental_reasoningLevels
|
|
773
|
+
});
|
|
774
|
+
const extensionKinds = declaration.experimental_extensionKinds === void 0 ? void 0 : validateProviderExtensionKinds(
|
|
775
|
+
id,
|
|
776
|
+
declaration.experimental_extensionKinds
|
|
777
|
+
);
|
|
778
|
+
const fallbackModels = declaration.experimental_models === void 0 ? void 0 : validateProviderFallbackModels(id, declaration.experimental_models);
|
|
779
|
+
const envPassthrough = declaration.experimental_env === void 0 ? void 0 : validateProviderEnvPassthrough(id, declaration.experimental_env);
|
|
780
|
+
const deriveProviderOptions = declaration.experimental_deriveProviderOptions;
|
|
781
|
+
if (deriveProviderOptions !== void 0 && typeof deriveProviderOptions !== "function") {
|
|
782
|
+
throw new Error(
|
|
783
|
+
`provider "${id}" experimental_deriveProviderOptions must be a function (context) => providerOptions`
|
|
784
|
+
);
|
|
785
|
+
}
|
|
345
786
|
return Object.freeze({
|
|
346
787
|
id,
|
|
347
788
|
displayName,
|
|
789
|
+
...family === void 0 ? {} : { experimental_family: family },
|
|
348
790
|
...icon === void 0 ? {} : { icon },
|
|
791
|
+
...bridgeOptions === void 0 ? {} : { experimental_bridgeOptions: bridgeOptions },
|
|
792
|
+
experimental_visibility: visibility,
|
|
349
793
|
capabilities: normalizedCapabilities,
|
|
350
|
-
composerActions
|
|
794
|
+
composerActions,
|
|
795
|
+
...strings === void 0 ? {} : { experimental_strings: strings },
|
|
796
|
+
...serviceTiers === void 0 ? {} : { experimental_serviceTiers: serviceTiers },
|
|
797
|
+
...reasoningLevels === void 0 ? {} : { experimental_reasoningLevels: reasoningLevels },
|
|
798
|
+
...extensionKinds === void 0 ? {} : { experimental_extensionKinds: extensionKinds },
|
|
799
|
+
...fallbackModels === void 0 ? {} : { experimental_models: Object.freeze({ fallback: fallbackModels }) },
|
|
800
|
+
...envPassthrough === void 0 ? {} : { experimental_env: Object.freeze({ passthrough: envPassthrough }) },
|
|
801
|
+
...deriveProviderOptions === void 0 ? {} : { experimental_deriveProviderOptions: deriveProviderOptions }
|
|
351
802
|
});
|
|
352
803
|
}
|
|
804
|
+
function deriveValidatedProviderOptions(args) {
|
|
805
|
+
const hook = args.declaration.experimental_deriveProviderOptions;
|
|
806
|
+
if (hook === void 0) return Object.freeze({});
|
|
807
|
+
const result = hook(args.context);
|
|
808
|
+
return normalizeProviderBridgeOptions(
|
|
809
|
+
args.declaration.id,
|
|
810
|
+
result,
|
|
811
|
+
"experimental_deriveProviderOptions result"
|
|
812
|
+
);
|
|
813
|
+
}
|
|
353
814
|
function isStandardSchema(value) {
|
|
354
815
|
if (typeof value !== "object" || value === null) return false;
|
|
355
816
|
const standard = Reflect.get(value, "~standard");
|
|
@@ -608,6 +1069,7 @@ export {
|
|
|
608
1069
|
PLUGIN_AGENT_TOOL_PARAMETERS_MAX_BYTES,
|
|
609
1070
|
PLUGIN_HTTP_METHODS,
|
|
610
1071
|
PLUGIN_MENTION_TRIGGER_VALUES,
|
|
1072
|
+
PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES,
|
|
611
1073
|
PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES,
|
|
612
1074
|
PLUGIN_PROVIDER_DISPLAY_NAME_MAX_CHARS,
|
|
613
1075
|
PLUGIN_PROVIDER_PERMISSION_MODE_VALUES,
|
|
@@ -619,6 +1081,7 @@ export {
|
|
|
619
1081
|
SETTING_KEY_PATTERN,
|
|
620
1082
|
adoptHttpRouteResponse,
|
|
621
1083
|
assertNoRecursiveJsonSchemaReferences,
|
|
1084
|
+
deriveValidatedProviderOptions,
|
|
622
1085
|
enforcePluginCliOutputLimit,
|
|
623
1086
|
isPluginMentionTrigger,
|
|
624
1087
|
isStandardSchema,
|
|
@@ -276,6 +276,7 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
276
276
|
const kind = "slots.navPanel";
|
|
277
277
|
const id = requireSlotId(kind, registration?.id);
|
|
278
278
|
requireUniqueId(kind, seenIds.navPanel, id);
|
|
279
|
+
const panelId = id;
|
|
279
280
|
const path = requireNonEmptyString(kind, "path", registration.path);
|
|
280
281
|
if (!PLUGIN_SLOT_ID_PATTERN.test(path)) {
|
|
281
282
|
throw new Error(
|
|
@@ -311,8 +312,25 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
311
312
|
`${fixedTabKind}: "layout" must be "padded" or "flush" when set`
|
|
312
313
|
);
|
|
313
314
|
}
|
|
315
|
+
const fixedTabPanelId = requireNonEmptyString(
|
|
316
|
+
fixedTabKind,
|
|
317
|
+
"panelId",
|
|
318
|
+
fixedTab?.panelId
|
|
319
|
+
);
|
|
320
|
+
if (fixedTabPanelId !== panelId) {
|
|
321
|
+
throw new Error(
|
|
322
|
+
`${fixedTabKind}: "panelId" must match its containing navPanel id ${JSON.stringify(panelId)}`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
const experimentalTarget = fixedTab?.experimental_target;
|
|
326
|
+
if (experimentalTarget !== void 0 && (typeof experimentalTarget !== "object" || experimentalTarget === null || typeof Reflect.get(experimentalTarget, "validate") !== "function")) {
|
|
327
|
+
throw new Error(
|
|
328
|
+
`${fixedTabKind}: "experimental_target.validate" must be a function when set`
|
|
329
|
+
);
|
|
330
|
+
}
|
|
314
331
|
return {
|
|
315
332
|
id: id2,
|
|
333
|
+
panelId: fixedTabPanelId,
|
|
316
334
|
title: requireNonEmptyString(
|
|
317
335
|
fixedTabKind,
|
|
318
336
|
"title",
|
|
@@ -324,7 +342,10 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
324
342
|
fixedTab?.icon
|
|
325
343
|
),
|
|
326
344
|
component: requireComponent(fixedTabKind, fixedTab?.component),
|
|
327
|
-
...layout === void 0 ? {} : { layout }
|
|
345
|
+
...layout === void 0 ? {} : { layout },
|
|
346
|
+
...experimentalTarget === void 0 ? {} : {
|
|
347
|
+
experimental_target: experimentalTarget
|
|
348
|
+
}
|
|
328
349
|
};
|
|
329
350
|
});
|
|
330
351
|
})();
|