@get-bb/plugin-sdk 0.4.14 → 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.
- package/README.md +3 -3
- package/bundled-types/bb-plugin-sdk-ai-services.d.ts +141 -0
- package/bundled-types/bb-plugin-sdk-app.d.ts +144 -21
- package/bundled-types/bb-plugin-sdk-host.d.ts +479 -2
- package/bundled-types/bb-plugin-sdk-internal-composer-customization-validation.d.ts +10 -1
- package/bundled-types/bb-plugin-sdk-internal-host-policy.d.ts +339 -32
- package/bundled-types/bb-plugin-sdk-internal-plugin-app-collector.d.ts +2 -1
- package/bundled-types/bb-plugin-sdk-provider-bridge-acp.d.ts +807 -0
- package/bundled-types/bb-plugin-sdk-provider-bridge-testing.d.ts +1742 -211
- package/bundled-types/bb-plugin-sdk-provider-bridge.d.ts +2985 -2442
- package/bundled-types/bb-plugin-sdk-testing-app.d.ts +4 -3
- package/bundled-types/bb-plugin-sdk-testing.d.ts +551 -9
- package/bundled-types/bb-plugin-sdk.d.ts +1335 -1350
- package/dist/ai-services.js +91 -0
- package/dist/app.js +2 -2
- package/dist/host.js +14547 -1
- package/dist/internal/composer-customization-validation.js +11 -0
- package/dist/internal/host-policy.js +656 -91
- package/dist/internal/plugin-app-collector.js +58 -14
- package/dist/provider-bridge-acp.js +9753 -0
- package/dist/provider-bridge-testing.js +3355 -1771
- package/dist/provider-bridge-worker-entry.mjs +917 -0
- package/dist/provider-bridge.js +2255 -3718
- package/dist/replay-provider-child.mjs +650 -0
- package/dist/testing/app.js +63 -19
- package/dist/testing/index.js +803 -115
- package/package.json +14 -1
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
// src/internal/host-policy.ts
|
|
2
|
-
import { z as
|
|
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:
|
|
61
|
-
description:
|
|
282
|
+
label: z3.string().min(1),
|
|
283
|
+
description: z3.string().min(1).optional()
|
|
62
284
|
};
|
|
63
|
-
var settingDescriptorSchema =
|
|
64
|
-
|
|
65
|
-
type:
|
|
285
|
+
var settingDescriptorSchema = z3.discriminatedUnion("type", [
|
|
286
|
+
z3.object({
|
|
287
|
+
type: z3.literal("string"),
|
|
66
288
|
...settingsBaseFields,
|
|
67
|
-
secret:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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:
|
|
302
|
+
default: z3.boolean().optional()
|
|
74
303
|
}).strict(),
|
|
75
|
-
|
|
76
|
-
type:
|
|
304
|
+
z3.object({
|
|
305
|
+
type: z3.literal("select"),
|
|
77
306
|
...settingsBaseFields,
|
|
78
|
-
options:
|
|
79
|
-
default:
|
|
307
|
+
options: z3.array(z3.string().min(1)).min(1),
|
|
308
|
+
default: z3.string().optional()
|
|
80
309
|
}).strict(),
|
|
81
|
-
|
|
82
|
-
type:
|
|
310
|
+
z3.object({
|
|
311
|
+
type: z3.literal("project"),
|
|
83
312
|
...settingsBaseFields,
|
|
84
|
-
default:
|
|
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}"
|
|
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: `
|
|
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: `
|
|
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}"
|
|
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: "
|
|
597
|
+
field: "strings.iconTint.light",
|
|
369
598
|
value: tintRecord.light
|
|
370
599
|
}),
|
|
371
600
|
dark: requireNonBlankString({
|
|
372
601
|
providerId,
|
|
373
|
-
field: "
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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}"
|
|
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 = `
|
|
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}"
|
|
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}"
|
|
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
|
-
|
|
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}"
|
|
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")
|
|
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
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
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
|
-
|
|
693
|
-
|
|
694
|
-
|
|
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" && !
|
|
1182
|
+
if (visibility === "installed" && !normalizedMaintenance.health) {
|
|
759
1183
|
throw new Error(
|
|
760
|
-
`provider "${id}" experimental_visibility "installed" requires
|
|
1184
|
+
`provider "${id}" experimental_visibility "installed" requires maintenance.health`
|
|
761
1185
|
);
|
|
762
1186
|
}
|
|
763
|
-
const strings = declaration.
|
|
764
|
-
const serviceTiers = declaration.
|
|
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: "
|
|
767
|
-
value: declaration.
|
|
1190
|
+
field: "serviceTiers",
|
|
1191
|
+
value: declaration.serviceTiers
|
|
768
1192
|
});
|
|
769
|
-
const reasoningLevels = declaration.
|
|
1193
|
+
const reasoningLevels = declaration.reasoningLevels === void 0 ? void 0 : validateProviderOptionDescriptors({
|
|
770
1194
|
providerId: id,
|
|
771
|
-
field: "
|
|
772
|
-
value: declaration.
|
|
1195
|
+
field: "reasoningLevels",
|
|
1196
|
+
value: declaration.reasoningLevels
|
|
773
1197
|
});
|
|
774
|
-
const extensionKinds = declaration.
|
|
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
|
-
|
|
1215
|
+
"experimental_nativeCommandRoots",
|
|
1216
|
+
declaration.experimental_nativeCommandRoots
|
|
777
1217
|
);
|
|
778
|
-
const
|
|
779
|
-
|
|
780
|
-
|
|
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}"
|
|
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 ? {} : {
|
|
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 ? {} : {
|
|
796
|
-
...serviceTiers === void 0 ? {} : {
|
|
797
|
-
...reasoningLevels === void 0 ? {} : {
|
|
798
|
-
...extensionKinds === void 0 ? {} : {
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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.
|
|
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
|
-
"
|
|
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
|
};
|