@get-bb/plugin-sdk 0.4.6 → 0.4.9
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 +8 -0
- package/bundled-types/bb-plugin-sdk-app.d.ts +375 -112
- package/bundled-types/bb-plugin-sdk-host.d.ts +124 -0
- package/bundled-types/bb-plugin-sdk-internal-composer-customization-validation.d.ts +6 -1
- package/bundled-types/bb-plugin-sdk-internal-host-policy.d.ts +154 -2
- package/bundled-types/bb-plugin-sdk-internal-plugin-app-collector.d.ts +39 -0
- package/bundled-types/bb-plugin-sdk-provider-bridge.d.ts +4042 -0
- package/bundled-types/bb-plugin-sdk-testing-app.d.ts +4 -1
- package/bundled-types/bb-plugin-sdk-testing-host.d.ts +47 -0
- package/bundled-types/bb-plugin-sdk-testing.d.ts +19 -2
- package/bundled-types/bb-plugin-sdk.d.ts +6179 -5252
- package/dist/app.js +4 -0
- package/dist/host.js +13 -0
- package/dist/index.js +13 -1
- package/dist/internal/composer-customization-validation.js +9 -0
- package/dist/internal/host-policy.js +389 -18
- package/dist/internal/plugin-app-collector.js +555 -0
- package/dist/provider-bridge.js +6096 -0
- package/dist/testing/app.js +399 -257
- package/dist/testing/host.js +165 -0
- package/dist/testing/index.js +530 -27
- package/package.json +33 -4
package/dist/app.js
CHANGED
|
@@ -4,6 +4,8 @@ var definePluginApp = runtime.definePluginApp;
|
|
|
4
4
|
var ThreadChat = runtime.ThreadChat;
|
|
5
5
|
var Markdown = runtime.Markdown;
|
|
6
6
|
var experimental_NewThreadComposer = runtime.experimental_NewThreadComposer;
|
|
7
|
+
var experimental_SourceCode = runtime.experimental_SourceCode;
|
|
8
|
+
var experimental_Diff = runtime.experimental_Diff;
|
|
7
9
|
var useRpc = runtime.useRpc;
|
|
8
10
|
var useRealtime = runtime.useRealtime;
|
|
9
11
|
var useRealtimeConnectionState = runtime.useRealtimeConnectionState;
|
|
@@ -20,7 +22,9 @@ export {
|
|
|
20
22
|
Markdown,
|
|
21
23
|
ThreadChat,
|
|
22
24
|
definePluginApp,
|
|
25
|
+
experimental_Diff,
|
|
23
26
|
experimental_NewThreadComposer,
|
|
27
|
+
experimental_SourceCode,
|
|
24
28
|
experimental_useSidebarThreadActions,
|
|
25
29
|
experimental_useSidebarThreadPullRequest,
|
|
26
30
|
experimental_useSidebarThreadSplit,
|
package/dist/host.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/host-contract.ts
|
|
2
|
+
function experimental_defineHostEntry(args) {
|
|
3
|
+
return {
|
|
4
|
+
experimental_apiVersion: 1,
|
|
5
|
+
contract: args.contract,
|
|
6
|
+
handlers: args.handlers,
|
|
7
|
+
...args.experimental_signals === void 0 ? {} : { experimental_signals: args.experimental_signals },
|
|
8
|
+
...args.dispose === void 0 ? {} : { dispose: args.dispose }
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
experimental_defineHostEntry
|
|
13
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
// src/backend-contract.ts
|
|
2
2
|
var PLUGIN_CLI_OUTPUT_MAX_BYTES = 1024 * 1024;
|
|
3
3
|
|
|
4
|
+
// src/host-contract.ts
|
|
5
|
+
function experimental_defineHostEntry(args) {
|
|
6
|
+
return {
|
|
7
|
+
experimental_apiVersion: 1,
|
|
8
|
+
contract: args.contract,
|
|
9
|
+
handlers: args.handlers,
|
|
10
|
+
...args.experimental_signals === void 0 ? {} : { experimental_signals: args.experimental_signals },
|
|
11
|
+
...args.dispose === void 0 ? {} : { dispose: args.dispose }
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
4
15
|
// src/rpc-contract.ts
|
|
5
16
|
function defineRpcContract(contract) {
|
|
6
17
|
return contract;
|
|
7
18
|
}
|
|
8
19
|
export {
|
|
9
20
|
PLUGIN_CLI_OUTPUT_MAX_BYTES,
|
|
10
|
-
defineRpcContract
|
|
21
|
+
defineRpcContract,
|
|
22
|
+
experimental_defineHostEntry
|
|
11
23
|
};
|
|
@@ -40,6 +40,14 @@ function requireSlotId(kind, value) {
|
|
|
40
40
|
}
|
|
41
41
|
return value;
|
|
42
42
|
}
|
|
43
|
+
function requireProviderId(kind, value) {
|
|
44
|
+
if (typeof value !== "string" || !PLUGIN_SLOT_ID_PATTERN.test(value)) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`${kind}: "providerId" must match ${String(PLUGIN_SLOT_ID_PATTERN)}, got ${JSON.stringify(value)}`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
43
51
|
function requireMessageDirectiveId(kind, value) {
|
|
44
52
|
if (typeof value !== "string" || !PLUGIN_MESSAGE_DIRECTIVE_ID_PATTERN.test(value)) {
|
|
45
53
|
throw new Error(
|
|
@@ -233,6 +241,7 @@ export {
|
|
|
233
241
|
requireMessageDirectiveId,
|
|
234
242
|
requireNonEmptyString,
|
|
235
243
|
requireOptionalString,
|
|
244
|
+
requireProviderId,
|
|
236
245
|
requireSlotId,
|
|
237
246
|
requireUniqueId
|
|
238
247
|
};
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// src/internal/host-policy.ts
|
|
2
|
-
import { z } from "zod";
|
|
2
|
+
import { z as z2 } from "zod";
|
|
3
|
+
|
|
4
|
+
// ../domain/src/plugin-icon.ts
|
|
5
|
+
function isPluginOwnedIconPath(icon) {
|
|
6
|
+
return icon.startsWith("./");
|
|
7
|
+
}
|
|
3
8
|
|
|
4
9
|
// ../domain/src/plugin-cli.ts
|
|
5
10
|
var RESERVED_BB_CLI_COMMANDS = [
|
|
@@ -16,6 +21,11 @@ var RESERVED_BB_CLI_COMMANDS = [
|
|
|
16
21
|
"thread"
|
|
17
22
|
];
|
|
18
23
|
|
|
24
|
+
// ../domain/src/provider-fork.ts
|
|
25
|
+
import { z } from "zod";
|
|
26
|
+
var PROVIDER_FORK_VALUES = ["none", "tip", "checkpoint"];
|
|
27
|
+
var providerForkSchema = z.enum(PROVIDER_FORK_VALUES);
|
|
28
|
+
|
|
19
29
|
// src/backend-contract.ts
|
|
20
30
|
var PLUGIN_CLI_OUTPUT_MAX_BYTES = 1024 * 1024;
|
|
21
31
|
|
|
@@ -43,33 +53,34 @@ var PLUGIN_AGENT_SELECTION_MAX_IDS = 256;
|
|
|
43
53
|
var PLUGIN_AGENT_DYNAMIC_INSTRUCTIONS_MAX_CHARS = 4096;
|
|
44
54
|
var PLUGIN_AGENT_TOOL_PARAMETERS_MAX_BYTES = 128 * 1024;
|
|
45
55
|
var MENTION_PROVIDER_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
56
|
+
var PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,63}$/;
|
|
46
57
|
var SETTING_KEY_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
47
58
|
var settingsBaseFields = {
|
|
48
|
-
label:
|
|
49
|
-
description:
|
|
59
|
+
label: z2.string().min(1),
|
|
60
|
+
description: z2.string().min(1).optional()
|
|
50
61
|
};
|
|
51
|
-
var settingDescriptorSchema =
|
|
52
|
-
|
|
53
|
-
type:
|
|
62
|
+
var settingDescriptorSchema = z2.discriminatedUnion("type", [
|
|
63
|
+
z2.object({
|
|
64
|
+
type: z2.literal("string"),
|
|
54
65
|
...settingsBaseFields,
|
|
55
|
-
secret:
|
|
56
|
-
default:
|
|
66
|
+
secret: z2.literal(true).optional(),
|
|
67
|
+
default: z2.string().optional()
|
|
57
68
|
}).strict(),
|
|
58
|
-
|
|
59
|
-
type:
|
|
69
|
+
z2.object({
|
|
70
|
+
type: z2.literal("boolean"),
|
|
60
71
|
...settingsBaseFields,
|
|
61
|
-
default:
|
|
72
|
+
default: z2.boolean().optional()
|
|
62
73
|
}).strict(),
|
|
63
|
-
|
|
64
|
-
type:
|
|
74
|
+
z2.object({
|
|
75
|
+
type: z2.literal("select"),
|
|
65
76
|
...settingsBaseFields,
|
|
66
|
-
options:
|
|
67
|
-
default:
|
|
77
|
+
options: z2.array(z2.string().min(1)).min(1),
|
|
78
|
+
default: z2.string().optional()
|
|
68
79
|
}).strict(),
|
|
69
|
-
|
|
70
|
-
type:
|
|
80
|
+
z2.object({
|
|
81
|
+
type: z2.literal("project"),
|
|
71
82
|
...settingsBaseFields,
|
|
72
|
-
default:
|
|
83
|
+
default: z2.string().optional()
|
|
73
84
|
}).strict()
|
|
74
85
|
]);
|
|
75
86
|
function registerSettingDescriptors(target, added) {
|
|
@@ -174,6 +185,171 @@ function normalizeMentionProviderTriggers(providerId, triggers) {
|
|
|
174
185
|
}
|
|
175
186
|
return normalized;
|
|
176
187
|
}
|
|
188
|
+
var PLUGIN_PROVIDER_DISPLAY_NAME_MAX_CHARS = 80;
|
|
189
|
+
var PLUGIN_PROVIDER_PERMISSION_MODE_VALUES = [
|
|
190
|
+
"accept-edits",
|
|
191
|
+
"auto",
|
|
192
|
+
"full"
|
|
193
|
+
];
|
|
194
|
+
var PLUGIN_PROVIDER_REASONING_LEVEL_VALUES = [
|
|
195
|
+
"none",
|
|
196
|
+
"low",
|
|
197
|
+
"medium",
|
|
198
|
+
"high",
|
|
199
|
+
"xhigh",
|
|
200
|
+
"ultracode",
|
|
201
|
+
"max",
|
|
202
|
+
"ultra"
|
|
203
|
+
];
|
|
204
|
+
var PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES = [
|
|
205
|
+
"plan",
|
|
206
|
+
"goal"
|
|
207
|
+
];
|
|
208
|
+
function validateProviderRelativePath(value, label) {
|
|
209
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
210
|
+
throw new Error(`provider ${label} must be a non-blank relative path`);
|
|
211
|
+
}
|
|
212
|
+
if (value.includes("\\")) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`provider ${label} must use "/" separators, got ${JSON.stringify(value)}`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
if (value.startsWith("/")) {
|
|
218
|
+
throw new Error(
|
|
219
|
+
`provider ${label} must be relative, got ${JSON.stringify(value)}`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
if (value.split("/").some((segment) => segment === "..")) {
|
|
223
|
+
throw new Error(
|
|
224
|
+
`provider ${label} must not escape the plugin directory, got ${JSON.stringify(value)}`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return value;
|
|
228
|
+
}
|
|
229
|
+
function validateProviderLiteralArray(args) {
|
|
230
|
+
const { providerId, field, value, allowed, requireNonEmpty } = args;
|
|
231
|
+
if (!Array.isArray(value)) {
|
|
232
|
+
throw new Error(`provider "${providerId}" ${field} must be an array`);
|
|
233
|
+
}
|
|
234
|
+
if (requireNonEmpty && value.length === 0) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`provider "${providerId}" ${field} must include at least one entry`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
const seen = /* @__PURE__ */ new Set();
|
|
240
|
+
const normalized = [];
|
|
241
|
+
for (const entry of value) {
|
|
242
|
+
if (typeof entry !== "string" || !allowed.includes(entry)) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
`provider "${providerId}" ${field} entry ${JSON.stringify(entry)} is invalid; use one of ${allowed.join(", ")}`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
const literal = entry;
|
|
248
|
+
if (seen.has(literal)) {
|
|
249
|
+
throw new Error(
|
|
250
|
+
`provider "${providerId}" ${field} entry ${JSON.stringify(entry)} is duplicated`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
seen.add(literal);
|
|
254
|
+
normalized.push(literal);
|
|
255
|
+
}
|
|
256
|
+
return Object.freeze(normalized);
|
|
257
|
+
}
|
|
258
|
+
function validatePluginProviderDeclaration(declaration) {
|
|
259
|
+
if (typeof declaration !== "object" || declaration === null) {
|
|
260
|
+
throw new Error("provider declaration must be an object");
|
|
261
|
+
}
|
|
262
|
+
const id = declaration.id;
|
|
263
|
+
if (typeof id !== "string" || !PROVIDER_ID_PATTERN.test(id)) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
`invalid provider id ${JSON.stringify(id)} \u2014 use 2-64 lowercase letters, digits, and "-", starting with a letter or digit`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
const displayName = typeof declaration.displayName === "string" ? declaration.displayName.trim() : "";
|
|
269
|
+
if (displayName.length === 0 || displayName.length > PLUGIN_PROVIDER_DISPLAY_NAME_MAX_CHARS) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
`provider "${id}" displayName must be 1-${PLUGIN_PROVIDER_DISPLAY_NAME_MAX_CHARS} non-blank characters`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
let icon;
|
|
275
|
+
if (declaration.icon !== void 0) {
|
|
276
|
+
if (typeof declaration.icon !== "string" || declaration.icon.trim() === "") {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`provider "${id}" icon must be a non-blank string \u2014 a named host glyph ("Zap") or a plugin-relative path ("./icons/agent.svg")`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
if (isPluginOwnedIconPath(declaration.icon)) {
|
|
282
|
+
icon = validateProviderRelativePath(declaration.icon, `"${id}" icon`);
|
|
283
|
+
} else if (/[/\\]/u.test(declaration.icon)) {
|
|
284
|
+
throw new Error(
|
|
285
|
+
`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"`
|
|
286
|
+
);
|
|
287
|
+
} else {
|
|
288
|
+
icon = declaration.icon;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const capabilities = declaration.capabilities;
|
|
292
|
+
if (typeof capabilities !== "object" || capabilities === null) {
|
|
293
|
+
throw new Error(`provider "${id}" capabilities must be an object`);
|
|
294
|
+
}
|
|
295
|
+
const booleanCapabilityFields = [
|
|
296
|
+
"supportsServiceTier",
|
|
297
|
+
"supportsNativeUserQuestion",
|
|
298
|
+
"supportsManualCompaction",
|
|
299
|
+
"supportsThreadArchive",
|
|
300
|
+
"supportsThreadRename",
|
|
301
|
+
"supportsWorkflows"
|
|
302
|
+
];
|
|
303
|
+
for (const field of booleanCapabilityFields) {
|
|
304
|
+
if (typeof capabilities[field] !== "boolean") {
|
|
305
|
+
throw new Error(
|
|
306
|
+
`provider "${id}" capabilities.${field} must be a boolean`
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (!PROVIDER_FORK_VALUES.includes(capabilities.fork)) {
|
|
311
|
+
throw new Error(
|
|
312
|
+
`provider "${id}" capabilities.fork must be one of ${PROVIDER_FORK_VALUES.join(", ")}`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
const normalizedCapabilities = Object.freeze({
|
|
316
|
+
supportsServiceTier: capabilities.supportsServiceTier,
|
|
317
|
+
supportsNativeUserQuestion: capabilities.supportsNativeUserQuestion,
|
|
318
|
+
fork: capabilities.fork,
|
|
319
|
+
supportsManualCompaction: capabilities.supportsManualCompaction,
|
|
320
|
+
supportsThreadArchive: capabilities.supportsThreadArchive,
|
|
321
|
+
supportsThreadRename: capabilities.supportsThreadRename,
|
|
322
|
+
supportsWorkflows: capabilities.supportsWorkflows,
|
|
323
|
+
permissionModes: validateProviderLiteralArray({
|
|
324
|
+
providerId: id,
|
|
325
|
+
field: "capabilities.permissionModes",
|
|
326
|
+
value: capabilities.permissionModes,
|
|
327
|
+
allowed: PLUGIN_PROVIDER_PERMISSION_MODE_VALUES,
|
|
328
|
+
requireNonEmpty: true
|
|
329
|
+
}),
|
|
330
|
+
reasoningLevels: validateProviderLiteralArray({
|
|
331
|
+
providerId: id,
|
|
332
|
+
field: "capabilities.reasoningLevels",
|
|
333
|
+
value: capabilities.reasoningLevels,
|
|
334
|
+
allowed: PLUGIN_PROVIDER_REASONING_LEVEL_VALUES,
|
|
335
|
+
requireNonEmpty: true
|
|
336
|
+
})
|
|
337
|
+
});
|
|
338
|
+
const composerActions = validateProviderLiteralArray({
|
|
339
|
+
providerId: id,
|
|
340
|
+
field: "composerActions",
|
|
341
|
+
value: declaration.composerActions,
|
|
342
|
+
allowed: PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES,
|
|
343
|
+
requireNonEmpty: false
|
|
344
|
+
});
|
|
345
|
+
return Object.freeze({
|
|
346
|
+
id,
|
|
347
|
+
displayName,
|
|
348
|
+
...icon === void 0 ? {} : { icon },
|
|
349
|
+
capabilities: normalizedCapabilities,
|
|
350
|
+
composerActions
|
|
351
|
+
});
|
|
352
|
+
}
|
|
177
353
|
function isStandardSchema(value) {
|
|
178
354
|
if (typeof value !== "object" || value === null) return false;
|
|
179
355
|
const standard = Reflect.get(value, "~standard");
|
|
@@ -202,6 +378,143 @@ function readRpcMethodContract(method, value) {
|
|
|
202
378
|
function isZodSchemaLike(value) {
|
|
203
379
|
return typeof value === "object" && value !== null && typeof value.safeParse === "function";
|
|
204
380
|
}
|
|
381
|
+
var SINGLE_SCHEMA_KEYWORDS = [
|
|
382
|
+
"additionalItems",
|
|
383
|
+
"additionalProperties",
|
|
384
|
+
"contains",
|
|
385
|
+
"contentSchema",
|
|
386
|
+
"else",
|
|
387
|
+
"if",
|
|
388
|
+
"items",
|
|
389
|
+
"not",
|
|
390
|
+
"propertyNames",
|
|
391
|
+
"then",
|
|
392
|
+
"unevaluatedItems",
|
|
393
|
+
"unevaluatedProperties"
|
|
394
|
+
];
|
|
395
|
+
var SCHEMA_ARRAY_KEYWORDS = [
|
|
396
|
+
"allOf",
|
|
397
|
+
"anyOf",
|
|
398
|
+
"oneOf",
|
|
399
|
+
"prefixItems"
|
|
400
|
+
];
|
|
401
|
+
var SCHEMA_MAP_KEYWORDS = [
|
|
402
|
+
"$defs",
|
|
403
|
+
"definitions",
|
|
404
|
+
"dependentSchemas",
|
|
405
|
+
"patternProperties",
|
|
406
|
+
"properties"
|
|
407
|
+
];
|
|
408
|
+
function isJsonSchemaObject(value) {
|
|
409
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
410
|
+
}
|
|
411
|
+
function decodeJsonPointerToken(token) {
|
|
412
|
+
return token.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
413
|
+
}
|
|
414
|
+
function resolveLocalJsonSchemaReference(document, anchors, reference) {
|
|
415
|
+
if (!reference.startsWith("#")) return void 0;
|
|
416
|
+
let pointer;
|
|
417
|
+
try {
|
|
418
|
+
pointer = decodeURIComponent(reference.slice(1));
|
|
419
|
+
} catch {
|
|
420
|
+
return void 0;
|
|
421
|
+
}
|
|
422
|
+
if (pointer.length === 0) return document;
|
|
423
|
+
if (!pointer.startsWith("/")) return anchors.get(pointer);
|
|
424
|
+
let current = document;
|
|
425
|
+
for (const encodedToken of pointer.slice(1).split("/")) {
|
|
426
|
+
const token = decodeJsonPointerToken(encodedToken);
|
|
427
|
+
if (Array.isArray(current)) {
|
|
428
|
+
if (!/^(0|[1-9][0-9]*)$/.test(token)) return void 0;
|
|
429
|
+
current = current[Number(token)];
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
if (!isJsonSchemaObject(current) || !Object.hasOwn(current, token)) {
|
|
433
|
+
return void 0;
|
|
434
|
+
}
|
|
435
|
+
current = current[token];
|
|
436
|
+
}
|
|
437
|
+
return current;
|
|
438
|
+
}
|
|
439
|
+
function forEachJsonSchemaChild(schema, visit) {
|
|
440
|
+
for (const keyword of SINGLE_SCHEMA_KEYWORDS) {
|
|
441
|
+
const child = schema[keyword];
|
|
442
|
+
if (Array.isArray(child)) {
|
|
443
|
+
for (const entry of child) visit(entry);
|
|
444
|
+
} else {
|
|
445
|
+
visit(child);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
for (const keyword of SCHEMA_ARRAY_KEYWORDS) {
|
|
449
|
+
const children = schema[keyword];
|
|
450
|
+
if (!Array.isArray(children)) continue;
|
|
451
|
+
for (const child of children) visit(child);
|
|
452
|
+
}
|
|
453
|
+
for (const keyword of SCHEMA_MAP_KEYWORDS) {
|
|
454
|
+
const children = schema[keyword];
|
|
455
|
+
if (!isJsonSchemaObject(children)) continue;
|
|
456
|
+
for (const child of Object.values(children)) visit(child);
|
|
457
|
+
}
|
|
458
|
+
const dependencies = schema.dependencies;
|
|
459
|
+
if (isJsonSchemaObject(dependencies)) {
|
|
460
|
+
for (const dependency of Object.values(dependencies)) {
|
|
461
|
+
if (!Array.isArray(dependency)) visit(dependency);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function assertNoRecursiveJsonSchemaReferences(schema, subject) {
|
|
466
|
+
if (!isJsonSchemaObject(schema)) return;
|
|
467
|
+
const document = schema;
|
|
468
|
+
const anchors = /* @__PURE__ */ new Map();
|
|
469
|
+
const indexed = /* @__PURE__ */ new Set();
|
|
470
|
+
function indexAnchors(candidate) {
|
|
471
|
+
if (!isJsonSchemaObject(candidate) || indexed.has(candidate)) return;
|
|
472
|
+
indexed.add(candidate);
|
|
473
|
+
for (const keyword of ["$anchor", "$dynamicAnchor"]) {
|
|
474
|
+
const anchor = candidate[keyword];
|
|
475
|
+
if (typeof anchor === "string") anchors.set(anchor, candidate);
|
|
476
|
+
}
|
|
477
|
+
for (const keyword of ["$id", "id"]) {
|
|
478
|
+
const id = candidate[keyword];
|
|
479
|
+
if (typeof id === "string" && /^#[^/]+$/.test(id)) {
|
|
480
|
+
anchors.set(id.slice(1), candidate);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
forEachJsonSchemaChild(candidate, indexAnchors);
|
|
484
|
+
}
|
|
485
|
+
indexAnchors(document);
|
|
486
|
+
const visited = /* @__PURE__ */ new Set();
|
|
487
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
488
|
+
function visit(candidate, viaReference) {
|
|
489
|
+
if (typeof candidate === "boolean" || !isJsonSchemaObject(candidate)) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (visiting.has(candidate)) {
|
|
493
|
+
throw new Error(
|
|
494
|
+
`${subject} contains recursive JSON Schema ${viaReference?.keyword ?? "$ref"} ${JSON.stringify(viaReference?.value ?? "#")}`
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
if (visited.has(candidate)) return;
|
|
498
|
+
visiting.add(candidate);
|
|
499
|
+
for (const keyword of ["$ref", "$recursiveRef", "$dynamicRef"]) {
|
|
500
|
+
const reference = candidate[keyword];
|
|
501
|
+
if (typeof reference === "string" && reference.startsWith("#")) {
|
|
502
|
+
const target = resolveLocalJsonSchemaReference(
|
|
503
|
+
document,
|
|
504
|
+
anchors,
|
|
505
|
+
reference
|
|
506
|
+
);
|
|
507
|
+
if (target !== void 0) {
|
|
508
|
+
visit(target, { keyword, value: reference });
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
forEachJsonSchemaChild(candidate, visit);
|
|
513
|
+
visiting.delete(candidate);
|
|
514
|
+
visited.add(candidate);
|
|
515
|
+
}
|
|
516
|
+
visit(schema);
|
|
517
|
+
}
|
|
205
518
|
function summarizeParseIssues(error) {
|
|
206
519
|
const issues = error?.issues;
|
|
207
520
|
if (Array.isArray(issues) && issues.length > 0) {
|
|
@@ -232,6 +545,56 @@ function enforcePluginCliOutputLimit(result, jsonOutput) {
|
|
|
232
545
|
error
|
|
233
546
|
} : { exitCode: 1, stdout: "", stderr: error.message, error };
|
|
234
547
|
}
|
|
548
|
+
function adoptHttpRouteResponse(value) {
|
|
549
|
+
if (value instanceof Response) return value;
|
|
550
|
+
if (!isResponseLike(value)) {
|
|
551
|
+
throw new Error("http route handler must return a Response");
|
|
552
|
+
}
|
|
553
|
+
const status = value.status;
|
|
554
|
+
const isNullBodyStatus = status === 101 || status === 204 || status === 205 || status === 304;
|
|
555
|
+
const init = {
|
|
556
|
+
status,
|
|
557
|
+
statusText: typeof value.statusText === "string" ? value.statusText : "",
|
|
558
|
+
headers: new Headers(value.headers)
|
|
559
|
+
};
|
|
560
|
+
if (isNullBodyStatus || value.body === null) {
|
|
561
|
+
return new Response(null, init);
|
|
562
|
+
}
|
|
563
|
+
return new Response(adoptBodyStream(value), init);
|
|
564
|
+
}
|
|
565
|
+
function adoptBodyStream(value) {
|
|
566
|
+
const source = value.body;
|
|
567
|
+
if (!isReadableStreamLike(source)) {
|
|
568
|
+
return new ReadableStream({
|
|
569
|
+
async start(controller) {
|
|
570
|
+
controller.enqueue(new Uint8Array(await value.arrayBuffer()));
|
|
571
|
+
controller.close();
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
const reader = source.getReader();
|
|
576
|
+
return new ReadableStream({
|
|
577
|
+
async pull(controller) {
|
|
578
|
+
const { done, value: chunk } = await reader.read();
|
|
579
|
+
if (done) {
|
|
580
|
+
controller.close();
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
controller.enqueue(chunk);
|
|
584
|
+
},
|
|
585
|
+
async cancel(reason) {
|
|
586
|
+
await reader.cancel(reason);
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
function isReadableStreamLike(value) {
|
|
591
|
+
return value !== null && typeof value === "object" && typeof value.getReader === "function";
|
|
592
|
+
}
|
|
593
|
+
function isResponseLike(value) {
|
|
594
|
+
if (value === null || typeof value !== "object") return false;
|
|
595
|
+
const candidate = value;
|
|
596
|
+
return typeof candidate.status === "number" && typeof candidate.headers === "object" && candidate.headers !== null && typeof candidate.arrayBuffer === "function" && typeof candidate.clone === "function";
|
|
597
|
+
}
|
|
235
598
|
export {
|
|
236
599
|
AGENT_TOOL_NAME_PATTERN,
|
|
237
600
|
BACKGROUND_NAME_PATTERN,
|
|
@@ -245,10 +608,17 @@ export {
|
|
|
245
608
|
PLUGIN_AGENT_TOOL_PARAMETERS_MAX_BYTES,
|
|
246
609
|
PLUGIN_HTTP_METHODS,
|
|
247
610
|
PLUGIN_MENTION_TRIGGER_VALUES,
|
|
611
|
+
PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES,
|
|
612
|
+
PLUGIN_PROVIDER_DISPLAY_NAME_MAX_CHARS,
|
|
613
|
+
PLUGIN_PROVIDER_PERMISSION_MODE_VALUES,
|
|
614
|
+
PLUGIN_PROVIDER_REASONING_LEVEL_VALUES,
|
|
615
|
+
PROVIDER_ID_PATTERN,
|
|
248
616
|
RESERVED_AGENT_TOOL_NAMES,
|
|
249
617
|
RESERVED_BB_CLI_COMMANDS,
|
|
250
618
|
RPC_METHOD_PATTERN,
|
|
251
619
|
SETTING_KEY_PATTERN,
|
|
620
|
+
adoptHttpRouteResponse,
|
|
621
|
+
assertNoRecursiveJsonSchemaReferences,
|
|
252
622
|
enforcePluginCliOutputLimit,
|
|
253
623
|
isPluginMentionTrigger,
|
|
254
624
|
isStandardSchema,
|
|
@@ -257,5 +627,6 @@ export {
|
|
|
257
627
|
readRpcMethodContract,
|
|
258
628
|
registerSettingDescriptors,
|
|
259
629
|
summarizeParseIssues,
|
|
630
|
+
validatePluginProviderDeclaration,
|
|
260
631
|
validateSettingsUpdate
|
|
261
632
|
};
|