@get-bb/plugin-sdk 0.4.15 → 0.4.16

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.
@@ -1,10 +1,25 @@
1
1
  // src/internal/host-policy.ts
2
- import { z as z2 } from "zod";
2
+ import { z as z3 } from "zod";
3
3
 
4
4
  // ../domain/src/plugin-icon.ts
5
5
  function isPluginOwnedIconPath(icon) {
6
6
  return icon.startsWith("./");
7
7
  }
8
+ var PLUGIN_ICON_MAX_BYTES = 32 * 1024;
9
+ var NAMESPACED_GLYPH_PATTERN = /^[a-z0-9-]+\/[a-z0-9][a-z0-9-]*$/u;
10
+ function isNamespacedGlyph(glyph) {
11
+ return NAMESPACED_GLYPH_PATTERN.test(glyph);
12
+ }
13
+ function parseNamespacedGlyph(glyph) {
14
+ if (!isNamespacedGlyph(glyph)) {
15
+ return null;
16
+ }
17
+ const separator = glyph.indexOf("/");
18
+ return {
19
+ pluginId: glyph.slice(0, separator),
20
+ name: glyph.slice(separator + 1)
21
+ };
22
+ }
8
23
 
9
24
  // ../domain/src/plugin-cli.ts
10
25
  var RESERVED_BB_CLI_COMMANDS = [
@@ -26,6 +41,213 @@ import { z } from "zod";
26
41
  var PROVIDER_FORK_VALUES = ["none", "tip", "checkpoint"];
27
42
  var providerForkSchema = z.enum(PROVIDER_FORK_VALUES);
28
43
 
44
+ // ../domain/src/provider-skill-roots.ts
45
+ function isAbsoluteProviderSkillRootPath(value) {
46
+ if (value.length === 0) {
47
+ return false;
48
+ }
49
+ const normalized = value.replaceAll("\\", "/");
50
+ const drive = /^[a-zA-Z]:\//u.exec(normalized);
51
+ const rest = drive ? normalized.slice(drive[0].length) : normalized.slice(1);
52
+ if (!drive && !normalized.startsWith("/")) {
53
+ return false;
54
+ }
55
+ return rest.split("/").every((segment) => segment !== "" && segment !== "." && segment !== "..");
56
+ }
57
+ function isRelativeProviderSkillRootPath(value) {
58
+ if (value.length === 0) {
59
+ return false;
60
+ }
61
+ const normalized = value.replaceAll("\\", "/");
62
+ return !normalized.startsWith("/") && !/^[a-zA-Z]:\//u.test(normalized) && normalized.split("/").every((segment) => segment !== "" && segment !== "." && segment !== "..");
63
+ }
64
+
65
+ // ../domain/src/native-roots.ts
66
+ import { z as z2 } from "zod";
67
+ var PROVIDER_NATIVE_ROOTS_MAX = 32;
68
+ var PROVIDER_NATIVE_ROOT_NAME_PREFIX_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,62}:$/u;
69
+ var nativeRootNamePrefixSchema = z2.string().refine(
70
+ (value) => value === "" || PROVIDER_NATIVE_ROOT_NAME_PREFIX_PATTERN.test(value),
71
+ "A root name prefix is a plugin-name-like token ending in ':'"
72
+ );
73
+ var nativeRootManifestPathSchema = z2.string().min(1).refine(
74
+ isRelativeProviderSkillRootPath,
75
+ "A manifest marker is a relative path without dot segments"
76
+ );
77
+ var relativeNativeRootPathSchema = z2.string().min(1).refine(
78
+ isRelativeProviderSkillRootPath,
79
+ "Roots must be relative paths without dot segments"
80
+ );
81
+ var absoluteNativeRootPathSchema = z2.string().min(1).refine(
82
+ isAbsoluteProviderSkillRootPath,
83
+ "Absolute roots must be absolute paths without dot segments"
84
+ );
85
+ var providerNativeRootInputSchema = z2.union([
86
+ z2.string().min(1),
87
+ z2.object({
88
+ path: z2.string().min(1),
89
+ /** Skills nest in subdirectories (the agent scans recursively). */
90
+ recursive: z2.boolean().optional(),
91
+ /**
92
+ * Scan the same relative directory in every ancestor of the workspace
93
+ * up to the repository root (`project` roots only).
94
+ */
95
+ ancestors: z2.boolean().optional(),
96
+ /**
97
+ * Prepended to every name under the root, a vendor plugin's
98
+ * `plugin-name:`; a prefixed root is listed as a plugin root.
99
+ */
100
+ namePrefix: nativeRootNamePrefixSchema.optional(),
101
+ /**
102
+ * A file, relative to a skill directory under this root, that marks the
103
+ * directory as a vendor plugin rather than a skill (Claude's
104
+ * `.claude-plugin/plugin.json`): bb skips such a directory. The plugin
105
+ * that knows the vendor layout declares it; core names no vendor path.
106
+ */
107
+ skipIfManifest: nativeRootManifestPathSchema.optional()
108
+ }).strict()
109
+ ]);
110
+ var providerNativeRootsInputSchema = z2.object({
111
+ user: z2.array(providerNativeRootInputSchema).optional(),
112
+ project: z2.array(providerNativeRootInputSchema).optional()
113
+ }).strict();
114
+ var providerNativeRootSchema = z2.object({
115
+ path: z2.string().min(1),
116
+ recursive: z2.boolean(),
117
+ ancestors: z2.boolean(),
118
+ namePrefix: nativeRootNamePrefixSchema,
119
+ /** Absent: every skill-shaped directory under the root is a skill. */
120
+ skipIfManifest: nativeRootManifestPathSchema.optional()
121
+ }).strict();
122
+ function uniqueByPath(roots) {
123
+ return new Set(roots.map((root) => root.path)).size === roots.length;
124
+ }
125
+ function nativeRootSideSchema(side) {
126
+ return z2.array(
127
+ providerNativeRootSchema.extend({ path: relativeNativeRootPathSchema }).superRefine((root, context) => {
128
+ if (root.ancestors && side !== "project") {
129
+ context.addIssue({
130
+ code: "custom",
131
+ message: "Only project roots may walk ancestors"
132
+ });
133
+ }
134
+ })
135
+ ).max(PROVIDER_NATIVE_ROOTS_MAX).refine(uniqueByPath, "Roots must not repeat a path");
136
+ }
137
+ var providerNativeRootsSchema = z2.object({
138
+ user: nativeRootSideSchema("user"),
139
+ project: nativeRootSideSchema("project")
140
+ }).strict();
141
+ var EMPTY_PROVIDER_NATIVE_ROOTS = Object.freeze({
142
+ user: Object.freeze([]),
143
+ project: Object.freeze([])
144
+ });
145
+ function normalizeProviderNativeRoot(entry) {
146
+ if (typeof entry === "string") {
147
+ return { path: entry, recursive: false, ancestors: false, namePrefix: "" };
148
+ }
149
+ return {
150
+ path: entry.path,
151
+ recursive: entry.recursive ?? false,
152
+ ancestors: entry.ancestors ?? false,
153
+ namePrefix: entry.namePrefix ?? "",
154
+ ...entry.skipIfManifest === void 0 ? {} : { skipIfManifest: entry.skipIfManifest }
155
+ };
156
+ }
157
+ function normalizeProviderNativeRoots(roots) {
158
+ return {
159
+ user: (roots?.user ?? []).map(normalizeProviderNativeRoot),
160
+ project: (roots?.project ?? []).map(normalizeProviderNativeRoot)
161
+ };
162
+ }
163
+ var providerResolvedNativeRootShapeSchema = z2.enum([
164
+ "skills",
165
+ "skill",
166
+ "skill-file",
167
+ "commands",
168
+ "command-file"
169
+ ]);
170
+ var resolvedNativeRootFieldsSchema = z2.object({
171
+ path: absoluteNativeRootPathSchema,
172
+ origin: z2.enum(["user", "project"]),
173
+ recursive: z2.boolean(),
174
+ /** Only with origin `project`, for a path inside the workspace. */
175
+ ancestors: z2.boolean(),
176
+ namePrefix: nativeRootNamePrefixSchema,
177
+ shape: providerResolvedNativeRootShapeSchema,
178
+ /**
179
+ * `skill-file` only: the skill name when the file's frontmatter names
180
+ * none. A vendor plugin's root SKILL.md takes the plugin's name; absent
181
+ * means the parent directory's name.
182
+ */
183
+ fallbackName: z2.string().min(1).optional(),
184
+ /**
185
+ * `skills` only: a file, relative to a skill directory under this root,
186
+ * that marks the directory as a vendor plugin rather than a skill; the
187
+ * daemon skips such a directory (see the declared root's `skipIfManifest`).
188
+ */
189
+ skipIfManifest: nativeRootManifestPathSchema.optional()
190
+ }).strict();
191
+ var providerResolvedNativeRootSchema = resolvedNativeRootFieldsSchema.superRefine((root, context) => {
192
+ if (root.skipIfManifest !== void 0 && root.shape !== "skills") {
193
+ context.addIssue({
194
+ code: "custom",
195
+ message: "Only a skills root carries a manifest marker"
196
+ });
197
+ }
198
+ if (root.ancestors && root.origin !== "project") {
199
+ context.addIssue({
200
+ code: "custom",
201
+ message: "Only project roots may walk ancestors"
202
+ });
203
+ }
204
+ if (root.fallbackName !== void 0 && root.shape !== "skill-file") {
205
+ context.addIssue({
206
+ code: "custom",
207
+ message: "Only a skill-file root carries a fallback name"
208
+ });
209
+ }
210
+ });
211
+ var providerResolvedNativeRootInputSchema = resolvedNativeRootFieldsSchema.partial({
212
+ recursive: true,
213
+ ancestors: true,
214
+ namePrefix: true,
215
+ shape: true
216
+ });
217
+ var resolvedSkillShapes = /* @__PURE__ */ new Set([
218
+ "skills",
219
+ "skill",
220
+ "skill-file"
221
+ ]);
222
+ var resolvedCommandShapes = /* @__PURE__ */ new Set([
223
+ "commands",
224
+ "command-file"
225
+ ]);
226
+ var PROVIDER_RESOLVED_NATIVE_ROOTS_MAX = 256;
227
+ var providerResolvedNativeRootsSchema = z2.object({
228
+ skills: z2.array(
229
+ providerResolvedNativeRootSchema.refine(
230
+ (root) => resolvedSkillShapes.has(root.shape),
231
+ "A skills root needs a skill shape"
232
+ )
233
+ ).max(PROVIDER_RESOLVED_NATIVE_ROOTS_MAX),
234
+ commands: z2.array(
235
+ providerResolvedNativeRootSchema.refine(
236
+ (root) => resolvedCommandShapes.has(root.shape),
237
+ "A commands root needs a command shape"
238
+ )
239
+ ).max(PROVIDER_RESOLVED_NATIVE_ROOTS_MAX)
240
+ }).strict();
241
+ var EMPTY_PROVIDER_RESOLVED_NATIVE_ROOTS = Object.freeze({
242
+ skills: Object.freeze([]),
243
+ commands: Object.freeze([])
244
+ });
245
+ var providerNativeRootSetSchema = z2.object({
246
+ skills: providerNativeRootsSchema,
247
+ commands: providerNativeRootsSchema,
248
+ resolved: providerResolvedNativeRootsSchema
249
+ }).strict();
250
+
29
251
  // src/backend-contract.ts
30
252
  var PLUGIN_CLI_OUTPUT_MAX_BYTES = 1024 * 1024;
31
253
 
@@ -57,31 +279,38 @@ var PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,63}$/;
57
279
  var PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES = 64 * 1024;
58
280
  var SETTING_KEY_PATTERN = /^[a-zA-Z0-9_-]+$/;
59
281
  var settingsBaseFields = {
60
- label: z2.string().min(1),
61
- description: z2.string().min(1).optional()
282
+ label: z3.string().min(1),
283
+ description: z3.string().min(1).optional()
62
284
  };
63
- var settingDescriptorSchema = z2.discriminatedUnion("type", [
64
- z2.object({
65
- type: z2.literal("string"),
285
+ var settingDescriptorSchema = z3.discriminatedUnion("type", [
286
+ z3.object({
287
+ type: z3.literal("string"),
66
288
  ...settingsBaseFields,
67
- secret: z2.literal(true).optional(),
68
- default: z2.string().optional()
69
- }).strict(),
70
- z2.object({
71
- type: z2.literal("boolean"),
289
+ secret: z3.literal(true).optional(),
290
+ experimental_multiline: z3.boolean().optional(),
291
+ default: z3.string().optional()
292
+ }).strict().refine(
293
+ (descriptor) => !(descriptor.secret === true && descriptor.experimental_multiline === true),
294
+ {
295
+ message: "a secret setting cannot be experimental_multiline",
296
+ path: ["experimental_multiline"]
297
+ }
298
+ ),
299
+ z3.object({
300
+ type: z3.literal("boolean"),
72
301
  ...settingsBaseFields,
73
- default: z2.boolean().optional()
302
+ default: z3.boolean().optional()
74
303
  }).strict(),
75
- z2.object({
76
- type: z2.literal("select"),
304
+ z3.object({
305
+ type: z3.literal("select"),
77
306
  ...settingsBaseFields,
78
- options: z2.array(z2.string().min(1)).min(1),
79
- default: z2.string().optional()
307
+ options: z3.array(z3.string().min(1)).min(1),
308
+ default: z3.string().optional()
80
309
  }).strict(),
81
- z2.object({
82
- type: z2.literal("project"),
310
+ z3.object({
311
+ type: z3.literal("project"),
83
312
  ...settingsBaseFields,
84
- default: z2.string().optional()
313
+ default: z3.string().optional()
85
314
  }).strict()
86
315
  ]);
87
316
  function registerSettingDescriptors(target, added) {
@@ -335,7 +564,7 @@ function requireNonBlankString(args) {
335
564
  function validateProviderStrings(providerId, value) {
336
565
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
337
566
  throw new Error(
338
- `provider "${providerId}" experimental_strings must be an object`
567
+ `provider "${providerId}" strings must be an object`
339
568
  );
340
569
  }
341
570
  const record = Object.fromEntries(
@@ -343,12 +572,12 @@ function validateProviderStrings(providerId, value) {
343
572
  );
344
573
  const required = (field) => requireNonBlankString({
345
574
  providerId,
346
- field: `experimental_strings.${field}`,
575
+ field: `strings.${field}`,
347
576
  value: record[field]
348
577
  });
349
578
  const optional = (field) => record[field] === void 0 ? void 0 : requireNonBlankString({
350
579
  providerId,
351
- field: `experimental_strings.${field}`,
580
+ field: `strings.${field}`,
352
581
  value: record[field]
353
582
  });
354
583
  let iconTint;
@@ -356,7 +585,7 @@ function validateProviderStrings(providerId, value) {
356
585
  const tint = record.iconTint;
357
586
  if (typeof tint !== "object" || tint === null || Array.isArray(tint)) {
358
587
  throw new Error(
359
- `provider "${providerId}" experimental_strings.iconTint must be { light, dark }`
588
+ `provider "${providerId}" strings.iconTint must be { light, dark }`
360
589
  );
361
590
  }
362
591
  const tintRecord = Object.fromEntries(
@@ -365,12 +594,12 @@ function validateProviderStrings(providerId, value) {
365
594
  iconTint = Object.freeze({
366
595
  light: requireNonBlankString({
367
596
  providerId,
368
- field: "experimental_strings.iconTint.light",
597
+ field: "strings.iconTint.light",
369
598
  value: tintRecord.light
370
599
  }),
371
600
  dark: requireNonBlankString({
372
601
  providerId,
373
- field: "experimental_strings.iconTint.dark",
602
+ field: "strings.iconTint.dark",
374
603
  value: tintRecord.dark
375
604
  })
376
605
  });
@@ -437,42 +666,42 @@ function validateProviderOptionDescriptors(args) {
437
666
  function validateProviderExtensionKinds(providerId, value) {
438
667
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
439
668
  throw new Error(
440
- `provider "${providerId}" experimental_extensionKinds must be an object keyed by kind name`
669
+ `provider "${providerId}" extensionKinds must be an object keyed by kind name`
441
670
  );
442
671
  }
443
672
  const entries = Object.entries(value);
444
673
  if (entries.length > PROVIDER_EXTENSION_KINDS_MAX) {
445
674
  throw new Error(
446
- `provider "${providerId}" experimental_extensionKinds declares more than ${PROVIDER_EXTENSION_KINDS_MAX} kinds`
675
+ `provider "${providerId}" extensionKinds declares more than ${PROVIDER_EXTENSION_KINDS_MAX} kinds`
447
676
  );
448
677
  }
449
678
  const normalized = {};
450
679
  for (const [name, declaration] of entries) {
451
680
  if (!PROVIDER_EXTENSION_KIND_NAME_PATTERN.test(name)) {
452
681
  throw new Error(
453
- `provider "${providerId}" experimental_extensionKinds name ${JSON.stringify(name)} must match ${PROVIDER_EXTENSION_KIND_NAME_PATTERN}`
682
+ `provider "${providerId}" extensionKinds name ${JSON.stringify(name)} must match ${PROVIDER_EXTENSION_KIND_NAME_PATTERN}`
454
683
  );
455
684
  }
456
685
  if (typeof declaration !== "object" || declaration === null || Array.isArray(declaration)) {
457
686
  throw new Error(
458
- `provider "${providerId}" experimental_extensionKinds.${name} must be { item?, state? }`
687
+ `provider "${providerId}" extensionKinds.${name} must be { item?, state? }`
459
688
  );
460
689
  }
461
690
  const item = Reflect.get(declaration, "item");
462
691
  const state = Reflect.get(declaration, "state");
463
692
  if (item === void 0 && state === void 0) {
464
693
  throw new Error(
465
- `provider "${providerId}" experimental_extensionKinds.${name} must declare an item schema, a state schema, or both`
694
+ `provider "${providerId}" extensionKinds.${name} must declare an item schema, a state schema, or both`
466
695
  );
467
696
  }
468
697
  if (item !== void 0 && !isStandardSchema(item)) {
469
698
  throw new Error(
470
- `provider "${providerId}" experimental_extensionKinds.${name}.item must be a Standard Schema v1 validator`
699
+ `provider "${providerId}" extensionKinds.${name}.item must be a Standard Schema v1 validator`
471
700
  );
472
701
  }
473
702
  if (state !== void 0 && !isStandardSchema(state)) {
474
703
  throw new Error(
475
- `provider "${providerId}" experimental_extensionKinds.${name}.state must be a Standard Schema v1 validator`
704
+ `provider "${providerId}" extensionKinds.${name}.state must be a Standard Schema v1 validator`
476
705
  );
477
706
  }
478
707
  normalized[name] = Object.freeze({
@@ -488,57 +717,98 @@ var PROVIDER_ENV_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/u;
488
717
  function validateProviderEnvPassthrough(providerId, value) {
489
718
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
490
719
  throw new Error(
491
- `provider "${providerId}" experimental_env must be { passthrough: [...] }`
720
+ `provider "${providerId}" env must be { passthrough: [...] }`
492
721
  );
493
722
  }
494
723
  const passthrough = Reflect.get(value, "passthrough");
495
724
  if (!Array.isArray(passthrough)) {
496
725
  throw new Error(
497
- `provider "${providerId}" experimental_env.passthrough must be an array of variable names`
726
+ `provider "${providerId}" env.passthrough must be an array of variable names`
498
727
  );
499
728
  }
500
729
  if (passthrough.length > PROVIDER_ENV_PASSTHROUGH_MAX) {
501
730
  throw new Error(
502
- `provider "${providerId}" experimental_env.passthrough names more than ${PROVIDER_ENV_PASSTHROUGH_MAX} variables`
731
+ `provider "${providerId}" env.passthrough names more than ${PROVIDER_ENV_PASSTHROUGH_MAX} variables`
503
732
  );
504
733
  }
505
734
  const seen = /* @__PURE__ */ new Set();
506
735
  for (const name of passthrough) {
507
736
  if (typeof name !== "string" || !PROVIDER_ENV_NAME_PATTERN.test(name)) {
508
737
  throw new Error(
509
- `provider "${providerId}" experimental_env.passthrough entries must match ${PROVIDER_ENV_NAME_PATTERN}`
738
+ `provider "${providerId}" env.passthrough entries must match ${PROVIDER_ENV_NAME_PATTERN}`
510
739
  );
511
740
  }
512
741
  if (seen.has(name)) {
513
742
  throw new Error(
514
- `provider "${providerId}" experimental_env.passthrough repeats ${JSON.stringify(name)}`
743
+ `provider "${providerId}" env.passthrough repeats ${JSON.stringify(name)}`
515
744
  );
516
745
  }
517
746
  seen.add(name);
518
747
  }
519
748
  return Object.freeze([...seen]);
520
749
  }
750
+ function validateProviderNativeRoots(providerId, field, value) {
751
+ const input = providerNativeRootsInputSchema.safeParse(value);
752
+ if (!input.success) {
753
+ const issue = input.error.issues[0];
754
+ const where = issue?.path.length ? `.${issue.path.join(".")}` : "";
755
+ throw new Error(
756
+ `provider "${providerId}" ${field}${where} ${issue?.message ?? "is invalid"}`
757
+ );
758
+ }
759
+ const normalized = normalizeProviderNativeRoots(input.data);
760
+ const wire = providerNativeRootsSchema.safeParse(normalized);
761
+ if (!wire.success) {
762
+ const issue = wire.error.issues[0];
763
+ const where = issue?.path.length ? `.${issue.path.join(".")}` : "";
764
+ throw new Error(
765
+ `provider "${providerId}" ${field}${where} ${issue?.message ?? "is invalid"}`
766
+ );
767
+ }
768
+ return Object.freeze({
769
+ user: Object.freeze(wire.data.user.map((root) => Object.freeze(root))),
770
+ project: Object.freeze(wire.data.project.map((root) => Object.freeze(root)))
771
+ });
772
+ }
773
+ var PROVIDER_MODEL_CATALOG_SCOPES = [
774
+ "host",
775
+ "workspace"
776
+ ];
777
+ function validateProviderModelCatalogScope(providerId, value) {
778
+ if (value === void 0) {
779
+ return "workspace";
780
+ }
781
+ if (typeof value !== "string" || !PROVIDER_MODEL_CATALOG_SCOPES.includes(value)) {
782
+ throw new Error(
783
+ `provider "${providerId}" models.scope must be one of ${PROVIDER_MODEL_CATALOG_SCOPES.join(", ")}`
784
+ );
785
+ }
786
+ return value;
787
+ }
521
788
  function validateProviderFallbackModels(providerId, value) {
522
789
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
523
790
  throw new Error(
524
- `provider "${providerId}" experimental_models must be { fallback: [...] }`
791
+ `provider "${providerId}" models must be an object`
525
792
  );
526
793
  }
527
794
  const fallback = Reflect.get(value, "fallback");
795
+ if (fallback === void 0) {
796
+ return void 0;
797
+ }
528
798
  if (!Array.isArray(fallback)) {
529
799
  throw new Error(
530
- `provider "${providerId}" experimental_models.fallback must be an array`
800
+ `provider "${providerId}" models.fallback must be an array`
531
801
  );
532
802
  }
533
803
  if (fallback.length > PROVIDER_FALLBACK_MODELS_MAX) {
534
804
  throw new Error(
535
- `provider "${providerId}" experimental_models.fallback lists more than ${PROVIDER_FALLBACK_MODELS_MAX} models`
805
+ `provider "${providerId}" models.fallback lists more than ${PROVIDER_FALLBACK_MODELS_MAX} models`
536
806
  );
537
807
  }
538
808
  const seen = /* @__PURE__ */ new Set();
539
809
  let defaults = 0;
540
810
  const normalized = fallback.map((entry, index) => {
541
- const field = `experimental_models.fallback[${index}]`;
811
+ const field = `models.fallback[${index}]`;
542
812
  if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
543
813
  throw new Error(`provider "${providerId}" ${field} must be an object`);
544
814
  }
@@ -552,7 +822,7 @@ function validateProviderFallbackModels(providerId, value) {
552
822
  });
553
823
  if (seen.has(id)) {
554
824
  throw new Error(
555
- `provider "${providerId}" experimental_models.fallback id ${JSON.stringify(id)} is duplicated`
825
+ `provider "${providerId}" models.fallback id ${JSON.stringify(id)} is duplicated`
556
826
  );
557
827
  }
558
828
  seen.add(id);
@@ -628,11 +898,153 @@ function validateProviderFallbackModels(providerId, value) {
628
898
  });
629
899
  if (normalized.length > 0 && defaults !== 1) {
630
900
  throw new Error(
631
- `provider "${providerId}" experimental_models.fallback must mark exactly one model isDefault (found ${defaults})`
901
+ `provider "${providerId}" models.fallback must mark exactly one model isDefault (found ${defaults})`
632
902
  );
633
903
  }
634
904
  return Object.freeze(normalized);
635
905
  }
906
+ var AI_SERVICE_KINDS = /* @__PURE__ */ new Set(["inference", "voice"]);
907
+ var SERVER_DIRECT_AI_SERVICE_IDS = Object.freeze([
908
+ "openai",
909
+ "amazon-bedrock",
910
+ "ant-ling",
911
+ "anthropic",
912
+ "azure-openai-responses",
913
+ "baseten",
914
+ "cerebras",
915
+ "cloudflare-ai-gateway",
916
+ "cloudflare-workers-ai",
917
+ "deepseek",
918
+ "fireworks",
919
+ "github-copilot",
920
+ "google",
921
+ "google-vertex",
922
+ "groq",
923
+ "huggingface",
924
+ "kimi-coding",
925
+ "minimax",
926
+ "minimax-cn",
927
+ "mistral",
928
+ "moonshotai",
929
+ "moonshotai-cn",
930
+ "nvidia",
931
+ "openai-codex",
932
+ "opencode",
933
+ "opencode-go",
934
+ "openrouter",
935
+ "qwen-token-plan",
936
+ "qwen-token-plan-cn",
937
+ "radius",
938
+ "together",
939
+ "vercel-ai-gateway",
940
+ "xai",
941
+ "xiaomi",
942
+ "xiaomi-token-plan-ams",
943
+ "xiaomi-token-plan-cn",
944
+ "xiaomi-token-plan-sgp",
945
+ "zai",
946
+ "zai-coding-cn"
947
+ ]);
948
+ function validatePluginAiServiceDeclaration(declaration) {
949
+ if (typeof declaration !== "object" || declaration === null) {
950
+ throw new Error("AI service declaration must be an object");
951
+ }
952
+ const id = declaration.id;
953
+ if (typeof id !== "string" || !PROVIDER_ID_PATTERN.test(id)) {
954
+ throw new Error(
955
+ `invalid AI service id ${JSON.stringify(id)} \u2014 use 2-64 lowercase letters, digits, and "-", starting with a letter or digit`
956
+ );
957
+ }
958
+ const displayName = typeof declaration.displayName === "string" ? declaration.displayName.trim() : "";
959
+ if (displayName.length === 0 || displayName.length > 64) {
960
+ throw new Error(
961
+ `AI service "${id}" displayName must be 1-64 characters`
962
+ );
963
+ }
964
+ const kinds = declaration.kinds;
965
+ if (!Array.isArray(kinds) || kinds.length === 0) {
966
+ throw new Error(`AI service "${id}" must declare at least one kind`);
967
+ }
968
+ const seen = /* @__PURE__ */ new Set();
969
+ for (const kind of kinds) {
970
+ if (typeof kind !== "string" || !AI_SERVICE_KINDS.has(kind)) {
971
+ throw new Error(
972
+ `AI service "${id}" kind ${JSON.stringify(kind)} is not one of: ${[...AI_SERVICE_KINDS].join(", ")}`
973
+ );
974
+ }
975
+ if (seen.has(kind)) {
976
+ throw new Error(`AI service "${id}" declares kind "${kind}" twice`);
977
+ }
978
+ seen.add(kind);
979
+ }
980
+ return Object.freeze({
981
+ id,
982
+ displayName,
983
+ kinds: Object.freeze([...seen])
984
+ });
985
+ }
986
+ function assertAiServiceRegistrable(args) {
987
+ if (SERVER_DIRECT_AI_SERVICE_IDS.includes(args.id)) {
988
+ throw new Error(
989
+ `AI service id "${args.id}" is reserved: the server serves it directly, so a plugin cannot register it`
990
+ );
991
+ }
992
+ if (args.hostArtifact !== null) {
993
+ return { artifact: args.hostArtifact, problem: null };
994
+ }
995
+ if (args.hostArtifactProblem !== null) {
996
+ return { artifact: null, problem: args.hostArtifactProblem };
997
+ }
998
+ throw new Error(
999
+ `AI service "${args.id}" needs a bb.host entry to run on: this plugin declares none`
1000
+ );
1001
+ }
1002
+ function aiServiceAlreadyRegisteredMessage(id) {
1003
+ return `AI service "${id}" is already registered; a plugin cannot shadow an existing service.`;
1004
+ }
1005
+ function providerAlreadyRegisteredMessage(id) {
1006
+ return `Provider "${id}" is already registered; a plugin cannot shadow an existing provider.`;
1007
+ }
1008
+ var RENAMED_PROVIDER_DECLARATION_FIELDS = Object.freeze({
1009
+ experimental_family: "family",
1010
+ experimental_strings: "strings",
1011
+ experimental_serviceTiers: "serviceTiers",
1012
+ experimental_reasoningLevels: "reasoningLevels",
1013
+ experimental_extensionKinds: "extensionKinds",
1014
+ experimental_models: "models",
1015
+ experimental_env: "env",
1016
+ experimental_deriveProviderOptions: "deriveProviderOptions"
1017
+ });
1018
+ var MOVED_PROVIDER_CAPABILITY_FIELDS = Object.freeze({
1019
+ experimental_providerHealth: "maintenance.health",
1020
+ experimental_providerUsage: "maintenance.usage",
1021
+ experimental_providerInstallation: "maintenance.installation"
1022
+ });
1023
+ var READ_EXPERIMENTAL_PROVIDER_DECLARATION_FIELDS = /* @__PURE__ */ new Set([
1024
+ "experimental_bridgeOptions",
1025
+ "experimental_visibility",
1026
+ "experimental_nativeSkillRoots",
1027
+ "experimental_nativeCommandRoots",
1028
+ "experimental_resolvesNativeRoots"
1029
+ ]);
1030
+ var RENAMED_PROVIDER_FIELDS_SDK_VERSION = "0.4.16";
1031
+ function rejectStaleExperimentalFields(args) {
1032
+ for (const key of Object.keys(args.value)) {
1033
+ if (!key.startsWith("experimental_") || args.read.has(key)) {
1034
+ continue;
1035
+ }
1036
+ const replacement = Object.hasOwn(args.renamed, key) ? args.renamed[key] : void 0;
1037
+ const field = `${args.scope}${key}`;
1038
+ if (replacement === void 0) {
1039
+ throw new Error(
1040
+ `provider "${args.providerId}": unknown declaration field "${field}"`
1041
+ );
1042
+ }
1043
+ throw new Error(
1044
+ `provider "${args.providerId}": "${field}" was ${args.verb} to "${replacement}" in SDK ${RENAMED_PROVIDER_FIELDS_SDK_VERSION}`
1045
+ );
1046
+ }
1047
+ }
636
1048
  function validatePluginProviderDeclaration(declaration) {
637
1049
  if (typeof declaration !== "object" || declaration === null) {
638
1050
  throw new Error("provider declaration must be an object");
@@ -643,10 +1055,18 @@ function validatePluginProviderDeclaration(declaration) {
643
1055
  `invalid provider id ${JSON.stringify(id)} \u2014 use 2-64 lowercase letters, digits, and "-", starting with a letter or digit`
644
1056
  );
645
1057
  }
646
- const family = declaration.experimental_family;
1058
+ rejectStaleExperimentalFields({
1059
+ providerId: id,
1060
+ value: declaration,
1061
+ scope: "",
1062
+ read: READ_EXPERIMENTAL_PROVIDER_DECLARATION_FIELDS,
1063
+ renamed: RENAMED_PROVIDER_DECLARATION_FIELDS,
1064
+ verb: "renamed"
1065
+ });
1066
+ const family = declaration.family;
647
1067
  if (family !== void 0 && (typeof family !== "string" || !PROVIDER_ID_PATTERN.test(family))) {
648
1068
  throw new Error(
649
- `provider "${id}" experimental_family must use the provider id grammar (2-64 lowercase letters, digits, and "-")`
1069
+ `provider "${id}" family must use the provider id grammar (2-64 lowercase letters, digits, and "-")`
650
1070
  );
651
1071
  }
652
1072
  const displayName = typeof declaration.displayName === "string" ? declaration.displayName.trim() : "";
@@ -659,14 +1079,16 @@ function validatePluginProviderDeclaration(declaration) {
659
1079
  if (declaration.icon !== void 0) {
660
1080
  if (typeof declaration.icon !== "string" || declaration.icon.trim() === "") {
661
1081
  throw new Error(
662
- `provider "${id}" icon must be a non-blank string \u2014 a named host glyph ("Zap") or a plugin-relative path ("./icons/agent.svg")`
1082
+ `provider "${id}" icon must be a non-blank string \u2014 a named host glyph ("Zap"), a plugin-relative path ("./icons/agent.svg"), or a declared icon ("<pluginId>/<name>")`
663
1083
  );
664
1084
  }
665
1085
  if (isPluginOwnedIconPath(declaration.icon)) {
666
1086
  icon = validateProviderRelativePath(declaration.icon, `"${id}" icon`);
1087
+ } else if (isNamespacedGlyph(declaration.icon)) {
1088
+ icon = declaration.icon;
667
1089
  } else if (/[/\\]/u.test(declaration.icon)) {
668
1090
  throw new Error(
669
- `provider "${id}" icon looks like a path but does not start with "./" \u2014 use "./icons/agent.svg" for a plugin file, or a bare host glyph name like "Zap"`
1091
+ `provider "${id}" icon looks like a path but does not start with "./" \u2014 use "./icons/agent.svg" for a plugin file, "<pluginId>/<name>" for a declared icon, or a bare host glyph name like "Zap"`
670
1092
  );
671
1093
  } else {
672
1094
  icon = declaration.icon;
@@ -676,24 +1098,29 @@ function validatePluginProviderDeclaration(declaration) {
676
1098
  if (typeof capabilities !== "object" || capabilities === null) {
677
1099
  throw new Error(`provider "${id}" capabilities must be an object`);
678
1100
  }
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
- );
1101
+ rejectStaleExperimentalFields({
1102
+ providerId: id,
1103
+ value: capabilities,
1104
+ scope: "capabilities.",
1105
+ read: /* @__PURE__ */ new Set(),
1106
+ renamed: MOVED_PROVIDER_CAPABILITY_FIELDS,
1107
+ verb: "moved"
1108
+ });
1109
+ const maintenance = declaration.maintenance ?? {};
1110
+ if (typeof maintenance !== "object" || maintenance === null) {
1111
+ throw new Error(`provider "${id}" maintenance must be an object`);
691
1112
  }
692
- if (typeof experimentalProviderInstallation !== "boolean") {
693
- throw new Error(
694
- `provider "${id}" capabilities.experimental_providerInstallation must be a boolean`
695
- );
1113
+ for (const key of ["health", "usage", "installation"]) {
1114
+ const value = maintenance[key];
1115
+ if (value !== void 0 && typeof value !== "boolean") {
1116
+ throw new Error(`provider "${id}" maintenance.${key} must be a boolean`);
1117
+ }
696
1118
  }
1119
+ const normalizedMaintenance = Object.freeze({
1120
+ health: maintenance.health ?? false,
1121
+ usage: maintenance.usage ?? false,
1122
+ installation: maintenance.installation ?? false
1123
+ });
697
1124
  const booleanCapabilityFields = [
698
1125
  "supportsServiceTier",
699
1126
  "supportsNativeUserQuestion",
@@ -714,9 +1141,6 @@ function validatePluginProviderDeclaration(declaration) {
714
1141
  );
715
1142
  }
716
1143
  const normalizedCapabilities = Object.freeze({
717
- experimental_providerHealth: experimentalProviderHealth,
718
- experimental_providerUsage: experimentalProviderUsage,
719
- experimental_providerInstallation: experimentalProviderInstallation,
720
1144
  supportsServiceTier: capabilities.supportsServiceTier,
721
1145
  supportsNativeUserQuestion: capabilities.supportsNativeUserQuestion,
722
1146
  fork: capabilities.fork,
@@ -755,60 +1179,87 @@ function validatePluginProviderDeclaration(declaration) {
755
1179
  `provider "${id}" experimental_visibility must be "always" or "installed"`
756
1180
  );
757
1181
  }
758
- if (visibility === "installed" && !normalizedCapabilities.experimental_providerHealth) {
1182
+ if (visibility === "installed" && !normalizedMaintenance.health) {
759
1183
  throw new Error(
760
- `provider "${id}" experimental_visibility "installed" requires experimental_providerHealth`
1184
+ `provider "${id}" experimental_visibility "installed" requires maintenance.health`
761
1185
  );
762
1186
  }
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({
1187
+ const strings = declaration.strings === void 0 ? void 0 : validateProviderStrings(id, declaration.strings);
1188
+ const serviceTiers = declaration.serviceTiers === void 0 ? void 0 : validateProviderOptionDescriptors({
765
1189
  providerId: id,
766
- field: "experimental_serviceTiers",
767
- value: declaration.experimental_serviceTiers
1190
+ field: "serviceTiers",
1191
+ value: declaration.serviceTiers
768
1192
  });
769
- const reasoningLevels = declaration.experimental_reasoningLevels === void 0 ? void 0 : validateProviderOptionDescriptors({
1193
+ const reasoningLevels = declaration.reasoningLevels === void 0 ? void 0 : validateProviderOptionDescriptors({
770
1194
  providerId: id,
771
- field: "experimental_reasoningLevels",
772
- value: declaration.experimental_reasoningLevels
1195
+ field: "reasoningLevels",
1196
+ value: declaration.reasoningLevels
773
1197
  });
774
- const extensionKinds = declaration.experimental_extensionKinds === void 0 ? void 0 : validateProviderExtensionKinds(
1198
+ const extensionKinds = declaration.extensionKinds === void 0 ? void 0 : validateProviderExtensionKinds(
1199
+ id,
1200
+ declaration.extensionKinds
1201
+ );
1202
+ const fallbackModels = declaration.models === void 0 ? void 0 : validateProviderFallbackModels(id, declaration.models);
1203
+ const modelCatalogScope = validateProviderModelCatalogScope(
1204
+ id,
1205
+ declaration.models?.scope
1206
+ );
1207
+ const envPassthrough = declaration.env === void 0 ? void 0 : validateProviderEnvPassthrough(id, declaration.env);
1208
+ const nativeSkillRoots = declaration.experimental_nativeSkillRoots === void 0 ? void 0 : validateProviderNativeRoots(
1209
+ id,
1210
+ "experimental_nativeSkillRoots",
1211
+ declaration.experimental_nativeSkillRoots
1212
+ );
1213
+ const nativeCommandRoots = declaration.experimental_nativeCommandRoots === void 0 ? void 0 : validateProviderNativeRoots(
775
1214
  id,
776
- declaration.experimental_extensionKinds
1215
+ "experimental_nativeCommandRoots",
1216
+ declaration.experimental_nativeCommandRoots
777
1217
  );
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;
1218
+ const resolvesNativeRoots = declaration.experimental_resolvesNativeRoots;
1219
+ if (resolvesNativeRoots !== void 0 && typeof resolvesNativeRoots !== "boolean") {
1220
+ throw new Error(
1221
+ `provider "${id}" experimental_resolvesNativeRoots must be a boolean`
1222
+ );
1223
+ }
1224
+ const deriveProviderOptions = declaration.deriveProviderOptions;
781
1225
  if (deriveProviderOptions !== void 0 && typeof deriveProviderOptions !== "function") {
782
1226
  throw new Error(
783
- `provider "${id}" experimental_deriveProviderOptions must be a function (context) => providerOptions`
1227
+ `provider "${id}" deriveProviderOptions must be a function (context) => providerOptions`
784
1228
  );
785
1229
  }
786
1230
  return Object.freeze({
787
1231
  id,
788
1232
  displayName,
789
- ...family === void 0 ? {} : { experimental_family: family },
1233
+ ...family === void 0 ? {} : { family },
790
1234
  ...icon === void 0 ? {} : { icon },
791
1235
  ...bridgeOptions === void 0 ? {} : { experimental_bridgeOptions: bridgeOptions },
792
1236
  experimental_visibility: visibility,
1237
+ maintenance: normalizedMaintenance,
793
1238
  capabilities: normalizedCapabilities,
794
1239
  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 }
1240
+ ...strings === void 0 ? {} : { strings },
1241
+ ...serviceTiers === void 0 ? {} : { serviceTiers },
1242
+ ...reasoningLevels === void 0 ? {} : { reasoningLevels },
1243
+ ...extensionKinds === void 0 ? {} : { extensionKinds },
1244
+ models: Object.freeze({
1245
+ ...fallbackModels === void 0 ? {} : { fallback: fallbackModels },
1246
+ scope: modelCatalogScope
1247
+ }),
1248
+ ...envPassthrough === void 0 ? {} : { env: Object.freeze({ passthrough: envPassthrough }) },
1249
+ ...nativeSkillRoots === void 0 ? {} : { experimental_nativeSkillRoots: nativeSkillRoots },
1250
+ ...nativeCommandRoots === void 0 ? {} : { experimental_nativeCommandRoots: nativeCommandRoots },
1251
+ experimental_resolvesNativeRoots: resolvesNativeRoots ?? false,
1252
+ ...deriveProviderOptions === void 0 ? {} : { deriveProviderOptions }
802
1253
  });
803
1254
  }
804
1255
  function deriveValidatedProviderOptions(args) {
805
- const hook = args.declaration.experimental_deriveProviderOptions;
1256
+ const hook = args.declaration.deriveProviderOptions;
806
1257
  if (hook === void 0) return Object.freeze({});
807
1258
  const result = hook(args.context);
808
1259
  return normalizeProviderBridgeOptions(
809
1260
  args.declaration.id,
810
1261
  result,
811
- "experimental_deriveProviderOptions result"
1262
+ "deriveProviderOptions result"
812
1263
  );
813
1264
  }
814
1265
  function isStandardSchema(value) {
@@ -976,6 +1427,90 @@ function assertNoRecursiveJsonSchemaReferences(schema, subject) {
976
1427
  }
977
1428
  visit(schema);
978
1429
  }
1430
+ var RENAMED_AGENT_TOOL_FIELDS = /* @__PURE__ */ new Map([
1431
+ [
1432
+ "experimental_presentation",
1433
+ '"experimental_presentation" was renamed to "presentation" in SDK 0.4.16'
1434
+ ],
1435
+ [
1436
+ "experimental_statusLabels",
1437
+ '"experimental_statusLabels" was folded into "presentation" (labels) in SDK 0.4.16'
1438
+ ]
1439
+ ]);
1440
+ function rejectStaleAgentToolFields(toolName, tool) {
1441
+ const unknownKeys = [];
1442
+ for (const key of Object.keys(tool).sort()) {
1443
+ const renamed = RENAMED_AGENT_TOOL_FIELDS.get(key);
1444
+ if (renamed !== void 0) {
1445
+ throw new Error(`registerTool: ${renamed} (tool "${toolName}")`);
1446
+ }
1447
+ if (key.startsWith("experimental_")) {
1448
+ unknownKeys.push(key);
1449
+ }
1450
+ }
1451
+ if (unknownKeys.length > 0) {
1452
+ throw new Error(
1453
+ `registerTool: tool "${toolName}" contains unknown field${unknownKeys.length === 1 ? "" : "s"}: ${unknownKeys.join(", ")}`
1454
+ );
1455
+ }
1456
+ }
1457
+ function parsePluginAgentToolPresentation(toolName, value) {
1458
+ if (value === void 0) {
1459
+ return null;
1460
+ }
1461
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1462
+ throw new Error(
1463
+ `tool "${toolName}" presentation must be an object`
1464
+ );
1465
+ }
1466
+ const declared = value;
1467
+ const presentation = {};
1468
+ if (declared.label !== void 0) {
1469
+ const label = declared.label;
1470
+ if (typeof label !== "object" || label === null || typeof label.pending !== "string" || typeof label.completed !== "string") {
1471
+ throw new Error(
1472
+ `tool "${toolName}" presentation.label must provide pending and completed strings`
1473
+ );
1474
+ }
1475
+ const { pending, completed } = label;
1476
+ if (pending.trim().length === 0 || completed.trim().length === 0 || pending.length > PLUGIN_AGENT_STATUS_LABEL_MAX_CHARS || completed.length > PLUGIN_AGENT_STATUS_LABEL_MAX_CHARS) {
1477
+ throw new Error(
1478
+ `tool "${toolName}" presentation.label strings must be non-empty and at most ${PLUGIN_AGENT_STATUS_LABEL_MAX_CHARS} characters`
1479
+ );
1480
+ }
1481
+ presentation.label = { pending, completed };
1482
+ }
1483
+ if (declared.icon !== void 0) {
1484
+ const icon = declared.icon;
1485
+ if (typeof icon !== "object" || icon === null || typeof icon.glyph !== "string" || icon.glyph.trim().length === 0) {
1486
+ throw new Error(
1487
+ `tool "${toolName}" presentation.icon must be { glyph: string }`
1488
+ );
1489
+ }
1490
+ presentation.icon = { glyph: icon.glyph };
1491
+ }
1492
+ if (declared.suppress !== void 0) {
1493
+ if (typeof declared.suppress !== "boolean") {
1494
+ throw new Error(
1495
+ `tool "${toolName}" presentation.suppress must be a boolean`
1496
+ );
1497
+ }
1498
+ presentation.suppress = declared.suppress;
1499
+ }
1500
+ if (declared.tint !== void 0) {
1501
+ const tint = declared.tint;
1502
+ if (typeof tint !== "object" || tint === null || typeof tint.light !== "string" || typeof tint.dark !== "string") {
1503
+ throw new Error(
1504
+ `tool "${toolName}" presentation.tint must provide light and dark strings`
1505
+ );
1506
+ }
1507
+ presentation.tint = {
1508
+ light: tint.light,
1509
+ dark: tint.dark
1510
+ };
1511
+ }
1512
+ return presentation;
1513
+ }
979
1514
  function summarizeParseIssues(error) {
980
1515
  const issues = error?.issues;
981
1516
  if (Array.isArray(issues) && issues.length > 0) {
@@ -1056,6 +1591,25 @@ function isResponseLike(value) {
1056
1591
  const candidate = value;
1057
1592
  return typeof candidate.status === "number" && typeof candidate.headers === "object" && candidate.headers !== null && typeof candidate.arrayBuffer === "function" && typeof candidate.clone === "function";
1058
1593
  }
1594
+ function undeclaredIconProblem(pluginId, declaredIconNames, glyph) {
1595
+ const parsed = parseNamespacedGlyph(glyph);
1596
+ if (parsed === null) {
1597
+ return null;
1598
+ }
1599
+ if (parsed.pluginId !== pluginId || !declaredIconNames.has(parsed.name)) {
1600
+ return `"${glyph}" is not an icon declared by plugin "${pluginId}"`;
1601
+ }
1602
+ return null;
1603
+ }
1604
+ function providerIconRefusalMessage(providerId, problem) {
1605
+ return `provider "${providerId}" icon ${problem}`;
1606
+ }
1607
+ function agentToolIconRefusalMessage(toolName, problem) {
1608
+ return `tool "${toolName}" presentation.icon ${problem}`;
1609
+ }
1610
+ function providerWithoutBridgeMessage(providerId) {
1611
+ return `provider "${providerId}" has no bridge to run on: this plugin declares no "bb.host" entry in its manifest`;
1612
+ }
1059
1613
  export {
1060
1614
  AGENT_TOOL_NAME_PATTERN,
1061
1615
  BACKGROUND_NAME_PATTERN,
@@ -1078,8 +1632,12 @@ export {
1078
1632
  RESERVED_AGENT_TOOL_NAMES,
1079
1633
  RESERVED_BB_CLI_COMMANDS,
1080
1634
  RPC_METHOD_PATTERN,
1635
+ SERVER_DIRECT_AI_SERVICE_IDS,
1081
1636
  SETTING_KEY_PATTERN,
1082
1637
  adoptHttpRouteResponse,
1638
+ agentToolIconRefusalMessage,
1639
+ aiServiceAlreadyRegisteredMessage,
1640
+ assertAiServiceRegistrable,
1083
1641
  assertNoRecursiveJsonSchemaReferences,
1084
1642
  deriveValidatedProviderOptions,
1085
1643
  enforcePluginCliOutputLimit,
@@ -1087,9 +1645,16 @@ export {
1087
1645
  isStandardSchema,
1088
1646
  isZodSchemaLike,
1089
1647
  normalizeMentionProviderTriggers,
1648
+ parsePluginAgentToolPresentation,
1649
+ providerAlreadyRegisteredMessage,
1650
+ providerIconRefusalMessage,
1651
+ providerWithoutBridgeMessage,
1090
1652
  readRpcMethodContract,
1091
1653
  registerSettingDescriptors,
1654
+ rejectStaleAgentToolFields,
1092
1655
  summarizeParseIssues,
1656
+ undeclaredIconProblem,
1657
+ validatePluginAiServiceDeclaration,
1093
1658
  validatePluginProviderDeclaration,
1094
1659
  validateSettingsUpdate
1095
1660
  };