@get-bb/plugin-sdk 0.4.34 → 0.4.35

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.
@@ -35,7 +35,7 @@ interface PluginRpcError {
35
35
  issues?: PluginRpcValidationIssue[];
36
36
  }
37
37
  /**
38
- * The validator-neutral subset of Standard Schema v1 used by plugin RPC.
38
+ * The validator-neutral subset of Standard Schema v1 used by plugin contracts.
39
39
  * Zod 4 schemas implement this interface directly; other validators can do
40
40
  * the same without becoming part of BB's public protocol.
41
41
  */
@@ -8,7 +8,7 @@
8
8
  import { z } from 'zod';
9
9
 
10
10
  /**
11
- * The validator-neutral subset of Standard Schema v1 used by plugin RPC.
11
+ * The validator-neutral subset of Standard Schema v1 used by plugin contracts.
12
12
  * Zod 4 schemas implement this interface directly; other validators can do
13
13
  * the same without becoming part of BB's public protocol.
14
14
  */
@@ -459,7 +459,7 @@ type JsonValue = string | number | boolean | null | JsonValue[] | {
459
459
  };
460
460
 
461
461
  /**
462
- * The validator-neutral subset of Standard Schema v1 used by plugin RPC.
462
+ * The validator-neutral subset of Standard Schema v1 used by plugin contracts.
463
463
  * Zod 4 schemas implement this interface directly; other validators can do
464
464
  * the same without becoming part of BB's public protocol.
465
465
  */
@@ -491,11 +491,6 @@ interface PluginRpcMethodContract<InputSchema extends StandardSchemaV1 = Standar
491
491
  readonly output: OutputSchema;
492
492
  }
493
493
 
494
- /**
495
- * Declarative settings descriptors (`bb.settings.define`). Deliberately plain
496
- * data — not zod — so the host can render settings forms and the CLI can
497
- * parse values without executing plugin code.
498
- */
499
494
  type PluginSettingDescriptor = {
500
495
  type: "string";
501
496
  label: string;
@@ -507,22 +502,30 @@ type PluginSettingDescriptor = {
507
502
  * be multi-line.
508
503
  */
509
504
  experimental_multiline?: boolean;
505
+ /** Synchronously validate without transforming a proposed value. */
506
+ experimental_schema?: StandardSchemaV1<string, string>;
510
507
  default?: string;
511
508
  } | {
512
509
  type: "boolean";
513
510
  label: string;
514
511
  description?: string;
512
+ /** Synchronously validate without transforming a proposed value. */
513
+ experimental_schema?: StandardSchemaV1<boolean, boolean>;
515
514
  default?: boolean;
516
515
  } | {
517
516
  type: "select";
518
517
  label: string;
519
518
  description?: string;
520
519
  options: string[];
520
+ /** Synchronously validate without transforming a proposed value. */
521
+ experimental_schema?: StandardSchemaV1<string, string>;
521
522
  default?: string;
522
523
  } | {
523
524
  type: "project";
524
525
  label: string;
525
526
  description?: string;
527
+ /** Synchronously validate without transforming a proposed value. */
528
+ experimental_schema?: StandardSchemaV1<string, string>;
526
529
  default?: string;
527
530
  };
528
531
  type PluginSettingDescriptors = Record<string, PluginSettingDescriptor>;
@@ -1227,7 +1230,12 @@ declare function isStandardSchema(value: unknown): value is StandardSchemaV1;
1227
1230
  declare function readRpcMethodContract(method: string, value: unknown): PluginRpcMethodContract;
1228
1231
  /** Duck-typed zod detection: plugin sources may carry their own zod copy,
1229
1232
  * so instanceof is useless — anything with safeParse is treated as zod. */
1230
- declare function isZodSchemaLike(value: unknown): boolean;
1233
+ type ZodSchemaLike = {
1234
+ safeParse: z.ZodType["safeParse"];
1235
+ toJSONSchema?: z.ZodType["toJSONSchema"];
1236
+ };
1237
+ declare function isZodSchemaLike(value: unknown): value is ZodSchemaLike;
1238
+ declare function zodSchemaToJsonSchema(schema: ZodSchemaLike): unknown;
1231
1239
  /**
1232
1240
  * Reject recursive local references before a tool schema reaches a provider.
1233
1241
  * Some providers reject the complete tool list when any one schema contains a
@@ -1312,5 +1320,5 @@ declare function storePluginHook<K extends PluginHookName>(records: {
1312
1320
  /** The refusal a second handler for one hook from one plugin gets. */
1313
1321
  declare function pluginHookAlreadyRegisteredMessage(hook: PluginHookName): string;
1314
1322
 
1315
- export { AGENT_TOOL_NAME_PATTERN, BACKGROUND_NAME_PATTERN, CLI_COMMAND_NAME_PATTERN, KV_VALUE_MAX_BYTES, MENTION_PROVIDER_ID_PATTERN, PLUGIN_AGENT_DYNAMIC_INSTRUCTIONS_MAX_CHARS, PLUGIN_AGENT_SELECTION_MAX_IDS, PLUGIN_AGENT_STATIC_INSTRUCTIONS_MAX_CHARS, PLUGIN_AGENT_STATUS_LABEL_MAX_CHARS, PLUGIN_AGENT_TOOL_PARAMETERS_MAX_BYTES, PLUGIN_HTTP_METHODS, PLUGIN_MENTION_TRIGGER_VALUES, PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES, PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES, PLUGIN_PROVIDER_DISPLAY_NAME_MAX_CHARS, PLUGIN_PROVIDER_PERMISSION_MODE_VALUES, PLUGIN_PROVIDER_REASONING_LEVEL_VALUES, PROVIDER_ID_PATTERN, RESERVED_AGENT_TOOL_NAMES, RESERVED_BB_CLI_COMMANDS, RPC_METHOD_PATTERN, SERVER_DIRECT_AI_SERVICE_IDS, SETTING_KEY_PATTERN, adoptHttpRouteResponse, agentToolIconRefusalMessage, aiServiceAlreadyRegisteredMessage, assertAiServiceRegistrable, assertNoRecursiveJsonSchemaReferences, deriveValidatedProviderOptions, enforcePluginCliOutputLimit, isPluginMentionTrigger, isStandardSchema, isZodSchemaLike, normalizeMentionProviderTriggers, parsePluginAgentToolPresentation, pluginCliCollisionWarning, pluginHookAlreadyRegisteredMessage, providerAlreadyRegisteredMessage, providerIconRefusalMessage, providerWithoutBridgeMessage, readRpcMethodContract, registerSettingDescriptors, rejectStaleAgentToolFields, storePluginHook, summarizeParseIssues, undeclaredIconProblem, validatePluginAiServiceDeclaration, validatePluginProviderDeclaration, validateSettingsUpdate };
1323
+ export { AGENT_TOOL_NAME_PATTERN, BACKGROUND_NAME_PATTERN, CLI_COMMAND_NAME_PATTERN, KV_VALUE_MAX_BYTES, MENTION_PROVIDER_ID_PATTERN, PLUGIN_AGENT_DYNAMIC_INSTRUCTIONS_MAX_CHARS, PLUGIN_AGENT_SELECTION_MAX_IDS, PLUGIN_AGENT_STATIC_INSTRUCTIONS_MAX_CHARS, PLUGIN_AGENT_STATUS_LABEL_MAX_CHARS, PLUGIN_AGENT_TOOL_PARAMETERS_MAX_BYTES, PLUGIN_HTTP_METHODS, PLUGIN_MENTION_TRIGGER_VALUES, PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES, PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES, PLUGIN_PROVIDER_DISPLAY_NAME_MAX_CHARS, PLUGIN_PROVIDER_PERMISSION_MODE_VALUES, PLUGIN_PROVIDER_REASONING_LEVEL_VALUES, PROVIDER_ID_PATTERN, RESERVED_AGENT_TOOL_NAMES, RESERVED_BB_CLI_COMMANDS, RPC_METHOD_PATTERN, SERVER_DIRECT_AI_SERVICE_IDS, SETTING_KEY_PATTERN, adoptHttpRouteResponse, agentToolIconRefusalMessage, aiServiceAlreadyRegisteredMessage, assertAiServiceRegistrable, assertNoRecursiveJsonSchemaReferences, deriveValidatedProviderOptions, enforcePluginCliOutputLimit, isPluginMentionTrigger, isStandardSchema, isZodSchemaLike, normalizeMentionProviderTriggers, parsePluginAgentToolPresentation, pluginCliCollisionWarning, pluginHookAlreadyRegisteredMessage, providerAlreadyRegisteredMessage, providerIconRefusalMessage, providerWithoutBridgeMessage, readRpcMethodContract, registerSettingDescriptors, rejectStaleAgentToolFields, storePluginHook, summarizeParseIssues, undeclaredIconProblem, validatePluginAiServiceDeclaration, validatePluginProviderDeclaration, validateSettingsUpdate, zodSchemaToJsonSchema };
1316
1324
  export type { AiServiceHostBinding, NormalizedPluginProviderDeclaration };
@@ -74,7 +74,7 @@ type JsonValue = string | number | boolean | null | JsonValue[] | {
74
74
  };
75
75
 
76
76
  /**
77
- * The validator-neutral subset of Standard Schema v1 used by plugin RPC.
77
+ * The validator-neutral subset of Standard Schema v1 used by plugin contracts.
78
78
  * Zod 4 schemas implement this interface directly; other validators can do
79
79
  * the same without becoming part of BB's public protocol.
80
80
  */
@@ -44,6 +44,7 @@ interface ProviderNativeRootsInputLike {
44
44
 
45
45
  declare const appSettingsSchema: z$1.ZodObject<{
46
46
  defaultProviderId: z$1.ZodNullable<z$1.ZodString>;
47
+ managedBranchPrefix: z$1.ZodString;
47
48
  providerOrder: z$1.ZodArray<z$1.ZodString>;
48
49
  showKeyboardHints: z$1.ZodBoolean;
49
50
  showUnhandledProviderEvents: z$1.ZodBoolean;
@@ -68,6 +69,7 @@ declare const appKeybindingOverridesSchema: z$1.ZodArray<z$1.ZodObject<{
68
69
  "modelPicker.cycleReasoning": "modelPicker.cycleReasoning";
69
70
  "modelPicker.cycleReasoningBackward": "modelPicker.cycleReasoningBackward";
70
71
  "modelPicker.toggle": "modelPicker.toggle";
72
+ "notifications.open": "notifications.open";
71
73
  "palette.open": "palette.open";
72
74
  "pane.close": "pane.close";
73
75
  "pane.focus.1": "pane.focus.1";
@@ -8324,6 +8326,7 @@ declare const systemConfigResponseSchema: z$1.ZodObject<{
8324
8326
  "modelPicker.cycleReasoning": "modelPicker.cycleReasoning";
8325
8327
  "modelPicker.cycleReasoningBackward": "modelPicker.cycleReasoningBackward";
8326
8328
  "modelPicker.toggle": "modelPicker.toggle";
8329
+ "notifications.open": "notifications.open";
8327
8330
  "palette.open": "palette.open";
8328
8331
  "pane.close": "pane.close";
8329
8332
  "pane.focus.1": "pane.focus.1";
@@ -8422,6 +8425,7 @@ declare const systemConfigResponseSchema: z$1.ZodObject<{
8422
8425
  }, z$1.core.$strip>;
8423
8426
  generalSettings: z$1.ZodObject<{
8424
8427
  defaultProviderId: z$1.ZodNullable<z$1.ZodString>;
8428
+ managedBranchPrefix: z$1.ZodString;
8425
8429
  providerOrder: z$1.ZodArray<z$1.ZodString>;
8426
8430
  showKeyboardHints: z$1.ZodBoolean;
8427
8431
  showUnhandledProviderEvents: z$1.ZodBoolean;
@@ -8445,6 +8449,7 @@ declare const systemConfigResponseSchema: z$1.ZodObject<{
8445
8449
  "modelPicker.cycleReasoning": "modelPicker.cycleReasoning";
8446
8450
  "modelPicker.cycleReasoningBackward": "modelPicker.cycleReasoningBackward";
8447
8451
  "modelPicker.toggle": "modelPicker.toggle";
8452
+ "notifications.open": "notifications.open";
8448
8453
  "palette.open": "palette.open";
8449
8454
  "pane.close": "pane.close";
8450
8455
  "pane.focus.1": "pane.focus.1";
@@ -8518,6 +8523,7 @@ declare const systemConfigResponseSchema: z$1.ZodObject<{
8518
8523
  "modelPicker.cycleReasoning": "modelPicker.cycleReasoning";
8519
8524
  "modelPicker.cycleReasoningBackward": "modelPicker.cycleReasoningBackward";
8520
8525
  "modelPicker.toggle": "modelPicker.toggle";
8526
+ "notifications.open": "notifications.open";
8521
8527
  "palette.open": "palette.open";
8522
8528
  "pane.close": "pane.close";
8523
8529
  "pane.focus.1": "pane.focus.1";
@@ -12418,7 +12424,7 @@ interface PluginRpcError {
12418
12424
  issues?: PluginRpcValidationIssue[];
12419
12425
  }
12420
12426
  /**
12421
- * The validator-neutral subset of Standard Schema v1 used by plugin RPC.
12427
+ * The validator-neutral subset of Standard Schema v1 used by plugin contracts.
12422
12428
  * Zod 4 schemas implement this interface directly; other validators can do
12423
12429
  * the same without becoming part of BB's public protocol.
12424
12430
  */
@@ -15977,11 +15983,6 @@ interface PluginLogger {
15977
15983
  warn(message: string): void;
15978
15984
  error(message: string): void;
15979
15985
  }
15980
- /**
15981
- * Declarative settings descriptors (`bb.settings.define`). Deliberately plain
15982
- * data — not zod — so the host can render settings forms and the CLI can
15983
- * parse values without executing plugin code.
15984
- */
15985
15986
  type PluginSettingDescriptor = {
15986
15987
  type: "string";
15987
15988
  label: string;
@@ -15993,22 +15994,30 @@ type PluginSettingDescriptor = {
15993
15994
  * be multi-line.
15994
15995
  */
15995
15996
  experimental_multiline?: boolean;
15997
+ /** Synchronously validate without transforming a proposed value. */
15998
+ experimental_schema?: StandardSchemaV1<string, string>;
15996
15999
  default?: string;
15997
16000
  } | {
15998
16001
  type: "boolean";
15999
16002
  label: string;
16000
16003
  description?: string;
16004
+ /** Synchronously validate without transforming a proposed value. */
16005
+ experimental_schema?: StandardSchemaV1<boolean, boolean>;
16001
16006
  default?: boolean;
16002
16007
  } | {
16003
16008
  type: "select";
16004
16009
  label: string;
16005
16010
  description?: string;
16006
16011
  options: string[];
16012
+ /** Synchronously validate without transforming a proposed value. */
16013
+ experimental_schema?: StandardSchemaV1<string, string>;
16007
16014
  default?: string;
16008
16015
  } | {
16009
16016
  type: "project";
16010
16017
  label: string;
16011
16018
  description?: string;
16019
+ /** Synchronously validate without transforming a proposed value. */
16020
+ experimental_schema?: StandardSchemaV1<string, string>;
16012
16021
  default?: string;
16013
16022
  };
16014
16023
  type PluginSettingDescriptors = Record<string, PluginSettingDescriptor>;
@@ -16025,7 +16034,11 @@ type PluginSettingValueOf<D extends PluginSettingDescriptor> = D extends {
16025
16034
  interface PluginSettingsHandle<Ds extends Record<string, PluginSettingDescriptor>> {
16026
16035
  /** Load-safe: callable inside the factory. */
16027
16036
  get(): Promise<PluginSettingsValues<Ds>>;
16028
- /** Fires after values change through the settings route/CLI. */
16037
+ /** Validate and persist this handle's fields; `null` unsets a stored value. */
16038
+ experimental_set(values: Partial<{
16039
+ [K in keyof Ds]: PluginSettingValueOf<Ds[K]> | null;
16040
+ }>): Promise<PluginSettingsValues<Ds>>;
16041
+ /** Fires after effective values change through any settings write. */
16029
16042
  onChange(listener: (next: PluginSettingsValues<Ds>, prev: PluginSettingsValues<Ds>) => void): void;
16030
16043
  }
16031
16044
  interface PluginSettings {
@@ -293,12 +293,19 @@ var settingsBaseFields = {
293
293
  label: z3.string().min(1),
294
294
  description: z3.string().min(1).optional()
295
295
  };
296
+ var stringSettingSchemaSchema = z3.custom(
297
+ (value) => isStandardSchema(value)
298
+ );
299
+ var booleanSettingSchemaSchema = z3.custom(
300
+ (value) => isStandardSchema(value)
301
+ );
296
302
  var settingDescriptorSchema = z3.discriminatedUnion("type", [
297
303
  z3.object({
298
304
  type: z3.literal("string"),
299
305
  ...settingsBaseFields,
300
306
  secret: z3.literal(true).optional(),
301
307
  experimental_multiline: z3.boolean().optional(),
308
+ experimental_schema: stringSettingSchemaSchema.optional(),
302
309
  default: z3.string().optional()
303
310
  }).strict().refine(
304
311
  (descriptor) => !(descriptor.secret === true && descriptor.experimental_multiline === true),
@@ -310,17 +317,20 @@ var settingDescriptorSchema = z3.discriminatedUnion("type", [
310
317
  z3.object({
311
318
  type: z3.literal("boolean"),
312
319
  ...settingsBaseFields,
320
+ experimental_schema: booleanSettingSchemaSchema.optional(),
313
321
  default: z3.boolean().optional()
314
322
  }).strict(),
315
323
  z3.object({
316
324
  type: z3.literal("select"),
317
325
  ...settingsBaseFields,
318
326
  options: z3.array(z3.string().min(1)).min(1),
327
+ experimental_schema: stringSettingSchemaSchema.optional(),
319
328
  default: z3.string().optional()
320
329
  }).strict(),
321
330
  z3.object({
322
331
  type: z3.literal("project"),
323
332
  ...settingsBaseFields,
333
+ experimental_schema: stringSettingSchemaSchema.optional(),
324
334
  default: z3.string().optional()
325
335
  }).strict()
326
336
  ]);
@@ -349,11 +359,39 @@ function registerSettingDescriptors(target, added) {
349
359
  `default for setting "${key}" must be one of its options`
350
360
  );
351
361
  }
362
+ if (descriptor.default !== void 0) {
363
+ const errors = validateSettingsUpdate(
364
+ { [key]: descriptor },
365
+ { [key]: descriptor.default }
366
+ );
367
+ if (errors.length > 0) {
368
+ throw new Error(`invalid default for setting "${key}": ${errors[0]}`);
369
+ }
370
+ }
352
371
  validated[key] = descriptor;
353
372
  }
354
373
  Object.assign(target, validated);
355
374
  return validated;
356
375
  }
376
+ function settingSchemaError(key, schema, value) {
377
+ if (schema === void 0) return null;
378
+ try {
379
+ const result = schema["~standard"].validate(value);
380
+ if (result instanceof Promise) {
381
+ return `schema for setting "${key}" must validate synchronously`;
382
+ }
383
+ if (result.issues !== void 0) {
384
+ return result.issues[0]?.message ?? `schema for setting "${key}" rejected the value`;
385
+ }
386
+ if (result.value !== value) {
387
+ return `schema for setting "${key}" must not transform its value`;
388
+ }
389
+ return null;
390
+ } catch (error) {
391
+ const detail = error instanceof Error ? error.message : String(error);
392
+ return `schema for setting "${key}" failed: ${detail}`;
393
+ }
394
+ }
357
395
  function validateSettingsUpdate(descriptors, values) {
358
396
  const errors = [];
359
397
  for (const [key, value] of Object.entries(values)) {
@@ -366,6 +404,15 @@ function validateSettingsUpdate(descriptors, values) {
366
404
  if (descriptor.type === "boolean") {
367
405
  if (typeof value !== "boolean") {
368
406
  errors.push(`setting "${key}" expects a boolean`);
407
+ continue;
408
+ }
409
+ const validationError2 = settingSchemaError(
410
+ key,
411
+ descriptor.experimental_schema,
412
+ value
413
+ );
414
+ if (validationError2 !== null) {
415
+ errors.push(validationError2);
369
416
  }
370
417
  continue;
371
418
  }
@@ -377,6 +424,15 @@ function validateSettingsUpdate(descriptors, values) {
377
424
  errors.push(
378
425
  `setting "${key}" must be one of: ${descriptor.options.join(", ")}`
379
426
  );
427
+ continue;
428
+ }
429
+ const validationError = settingSchemaError(
430
+ key,
431
+ descriptor.experimental_schema,
432
+ value
433
+ );
434
+ if (validationError !== null) {
435
+ errors.push(validationError);
380
436
  }
381
437
  }
382
438
  return errors;
@@ -1290,6 +1346,12 @@ function readRpcMethodContract(method, value) {
1290
1346
  function isZodSchemaLike(value) {
1291
1347
  return typeof value === "object" && value !== null && typeof value.safeParse === "function";
1292
1348
  }
1349
+ function zodSchemaToJsonSchema(schema) {
1350
+ if (typeof schema.toJSONSchema === "function") {
1351
+ return schema.toJSONSchema({ io: "input" });
1352
+ }
1353
+ return z3.toJSONSchema(schema, { io: "input" });
1354
+ }
1293
1355
  var SINGLE_SCHEMA_KEYWORDS = [
1294
1356
  "additionalItems",
1295
1357
  "additionalProperties",
@@ -1663,5 +1725,6 @@ export {
1663
1725
  undeclaredIconProblem,
1664
1726
  validatePluginAiServiceDeclaration,
1665
1727
  validatePluginProviderDeclaration,
1666
- validateSettingsUpdate
1728
+ validateSettingsUpdate,
1729
+ zodSchemaToJsonSchema
1667
1730
  };
@@ -6,7 +6,6 @@ import { join } from "node:path";
6
6
  import Database from "better-sqlite3";
7
7
  import { CronExpressionParser } from "cron-parser";
8
8
  import { Hono } from "hono";
9
- import { z as z4 } from "zod";
10
9
 
11
10
  // ../domain/src/plugin-interaction-limits.ts
12
11
  var PLUGIN_INTERACTION_MAX_TITLE_LENGTH = 160;
@@ -307,12 +306,19 @@ var settingsBaseFields = {
307
306
  label: z3.string().min(1),
308
307
  description: z3.string().min(1).optional()
309
308
  };
309
+ var stringSettingSchemaSchema = z3.custom(
310
+ (value) => isStandardSchema(value)
311
+ );
312
+ var booleanSettingSchemaSchema = z3.custom(
313
+ (value) => isStandardSchema(value)
314
+ );
310
315
  var settingDescriptorSchema = z3.discriminatedUnion("type", [
311
316
  z3.object({
312
317
  type: z3.literal("string"),
313
318
  ...settingsBaseFields,
314
319
  secret: z3.literal(true).optional(),
315
320
  experimental_multiline: z3.boolean().optional(),
321
+ experimental_schema: stringSettingSchemaSchema.optional(),
316
322
  default: z3.string().optional()
317
323
  }).strict().refine(
318
324
  (descriptor) => !(descriptor.secret === true && descriptor.experimental_multiline === true),
@@ -324,17 +330,20 @@ var settingDescriptorSchema = z3.discriminatedUnion("type", [
324
330
  z3.object({
325
331
  type: z3.literal("boolean"),
326
332
  ...settingsBaseFields,
333
+ experimental_schema: booleanSettingSchemaSchema.optional(),
327
334
  default: z3.boolean().optional()
328
335
  }).strict(),
329
336
  z3.object({
330
337
  type: z3.literal("select"),
331
338
  ...settingsBaseFields,
332
339
  options: z3.array(z3.string().min(1)).min(1),
340
+ experimental_schema: stringSettingSchemaSchema.optional(),
333
341
  default: z3.string().optional()
334
342
  }).strict(),
335
343
  z3.object({
336
344
  type: z3.literal("project"),
337
345
  ...settingsBaseFields,
346
+ experimental_schema: stringSettingSchemaSchema.optional(),
338
347
  default: z3.string().optional()
339
348
  }).strict()
340
349
  ]);
@@ -363,11 +372,39 @@ function registerSettingDescriptors(target, added) {
363
372
  `default for setting "${key}" must be one of its options`
364
373
  );
365
374
  }
375
+ if (descriptor.default !== void 0) {
376
+ const errors = validateSettingsUpdate(
377
+ { [key]: descriptor },
378
+ { [key]: descriptor.default }
379
+ );
380
+ if (errors.length > 0) {
381
+ throw new Error(`invalid default for setting "${key}": ${errors[0]}`);
382
+ }
383
+ }
366
384
  validated[key] = descriptor;
367
385
  }
368
386
  Object.assign(target, validated);
369
387
  return validated;
370
388
  }
389
+ function settingSchemaError(key, schema, value) {
390
+ if (schema === void 0) return null;
391
+ try {
392
+ const result = schema["~standard"].validate(value);
393
+ if (result instanceof Promise) {
394
+ return `schema for setting "${key}" must validate synchronously`;
395
+ }
396
+ if (result.issues !== void 0) {
397
+ return result.issues[0]?.message ?? `schema for setting "${key}" rejected the value`;
398
+ }
399
+ if (result.value !== value) {
400
+ return `schema for setting "${key}" must not transform its value`;
401
+ }
402
+ return null;
403
+ } catch (error) {
404
+ const detail = error instanceof Error ? error.message : String(error);
405
+ return `schema for setting "${key}" failed: ${detail}`;
406
+ }
407
+ }
371
408
  function validateSettingsUpdate(descriptors, values) {
372
409
  const errors = [];
373
410
  for (const [key, value] of Object.entries(values)) {
@@ -380,6 +417,15 @@ function validateSettingsUpdate(descriptors, values) {
380
417
  if (descriptor.type === "boolean") {
381
418
  if (typeof value !== "boolean") {
382
419
  errors.push(`setting "${key}" expects a boolean`);
420
+ continue;
421
+ }
422
+ const validationError2 = settingSchemaError(
423
+ key,
424
+ descriptor.experimental_schema,
425
+ value
426
+ );
427
+ if (validationError2 !== null) {
428
+ errors.push(validationError2);
383
429
  }
384
430
  continue;
385
431
  }
@@ -391,6 +437,15 @@ function validateSettingsUpdate(descriptors, values) {
391
437
  errors.push(
392
438
  `setting "${key}" must be one of: ${descriptor.options.join(", ")}`
393
439
  );
440
+ continue;
441
+ }
442
+ const validationError = settingSchemaError(
443
+ key,
444
+ descriptor.experimental_schema,
445
+ value
446
+ );
447
+ if (validationError !== null) {
448
+ errors.push(validationError);
394
449
  }
395
450
  }
396
451
  return errors;
@@ -1294,6 +1349,12 @@ function readRpcMethodContract(method, value) {
1294
1349
  function isZodSchemaLike(value) {
1295
1350
  return typeof value === "object" && value !== null && typeof value.safeParse === "function";
1296
1351
  }
1352
+ function zodSchemaToJsonSchema(schema) {
1353
+ if (typeof schema.toJSONSchema === "function") {
1354
+ return schema.toJSONSchema({ io: "input" });
1355
+ }
1356
+ return z3.toJSONSchema(schema, { io: "input" });
1357
+ }
1297
1358
  var SINGLE_SCHEMA_KEYWORDS = [
1298
1359
  "additionalItems",
1299
1360
  "additionalProperties",
@@ -2082,7 +2143,9 @@ function createFakePluginHostInternal(options, sharedState) {
2082
2143
  "ALTER TABLE _bb_migrations ADD COLUMN statement_hash TEXT"
2083
2144
  );
2084
2145
  }
2085
- const rows = database.prepare("SELECT id, statement_hash FROM _bb_migrations ORDER BY id").all();
2146
+ const rows = database.prepare(
2147
+ "SELECT id, statement_hash FROM _bb_migrations ORDER BY id"
2148
+ ).all();
2086
2149
  const applied = /* @__PURE__ */ new Map();
2087
2150
  for (const row of rows) applied.set(row.id, row.statement_hash);
2088
2151
  const statementHashes = statements.map(migrationStatementHash);
@@ -2119,20 +2182,57 @@ function createFakePluginHostInternal(options, sharedState) {
2119
2182
  const settingsDescriptors = {};
2120
2183
  const settingsListeners = [];
2121
2184
  const storedSettings = persistentState.storedSettings;
2185
+ async function setSettingsValues(values) {
2186
+ const errors = validateSettingsUpdate(settingsDescriptors, values);
2187
+ if (errors.length > 0) {
2188
+ throw new Error(errors.join("; "));
2189
+ }
2190
+ const prev = readSettingsValues(settingsDescriptors, storedSettings);
2191
+ for (const [key, value] of Object.entries(values)) {
2192
+ if (value === null) storedSettings.delete(key);
2193
+ else if (typeof value === "string" || typeof value === "boolean") {
2194
+ storedSettings.set(key, value);
2195
+ } else {
2196
+ throw new Error(`setting "${key}" has an unsupported value`);
2197
+ }
2198
+ }
2199
+ const next = readSettingsValues(settingsDescriptors, storedSettings);
2200
+ if (JSON.stringify(next) === JSON.stringify(prev)) return;
2201
+ for (const listener of settingsListeners) {
2202
+ try {
2203
+ listener(next, prev);
2204
+ } catch (error) {
2205
+ emitLog(
2206
+ "warn",
2207
+ `settings onChange listener failed: ${errorMessage(error)}`
2208
+ );
2209
+ }
2210
+ }
2211
+ }
2122
2212
  const settings = {
2123
2213
  define(descriptors) {
2124
2214
  assertLive();
2125
- registerSettingDescriptors(
2215
+ const validated = registerSettingDescriptors(
2126
2216
  settingsDescriptors,
2127
2217
  descriptors
2128
2218
  );
2129
2219
  return {
2130
2220
  async get() {
2131
2221
  assertLive();
2132
- return readSettingsValues(
2133
- settingsDescriptors,
2134
- storedSettings
2135
- );
2222
+ return readSettingsValues(validated, storedSettings);
2223
+ },
2224
+ async experimental_set(values) {
2225
+ assertLive();
2226
+ const rawValues = {};
2227
+ for (const [key, value] of Object.entries(values)) {
2228
+ rawValues[key] = value;
2229
+ }
2230
+ const errors = validateSettingsUpdate(validated, rawValues);
2231
+ if (errors.length > 0) {
2232
+ throw new Error(errors.join("; "));
2233
+ }
2234
+ await setSettingsValues(rawValues);
2235
+ return readSettingsValues(validated, storedSettings);
2136
2236
  },
2137
2237
  onChange(listener) {
2138
2238
  assertLive();
@@ -2462,9 +2562,7 @@ function createFakePluginHostInternal(options, sharedState) {
2462
2562
  let parse;
2463
2563
  if (isZodSchemaLike(parameters)) {
2464
2564
  try {
2465
- inputSchema = z4.toJSONSchema(parameters, {
2466
- io: "input"
2467
- });
2565
+ inputSchema = zodSchemaToJsonSchema(parameters);
2468
2566
  } catch (error) {
2469
2567
  throw new Error(
2470
2568
  `tool "${name}" parameters look like a zod schema but could not be converted to JSON Schema (${errorMessage(error)}) \u2014 use zod 4, or pass a plain JSON-schema object`
@@ -2978,27 +3076,7 @@ function createFakePluginHostInternal(options, sharedState) {
2978
3076
  pending.resolve({ outcome: "cancelled", reason: "user" });
2979
3077
  },
2980
3078
  async setSettings(values) {
2981
- const errors = validateSettingsUpdate(settingsDescriptors, values);
2982
- if (errors.length > 0) {
2983
- throw new Error(errors.join("; "));
2984
- }
2985
- const prev = readSettingsValues(settingsDescriptors, storedSettings);
2986
- for (const [key, value] of Object.entries(values)) {
2987
- if (value === null) storedSettings.delete(key);
2988
- else storedSettings.set(key, value);
2989
- }
2990
- const next = readSettingsValues(settingsDescriptors, storedSettings);
2991
- if (JSON.stringify(next) === JSON.stringify(prev)) return;
2992
- for (const listener of settingsListeners) {
2993
- try {
2994
- listener(next, prev);
2995
- } catch (error) {
2996
- emitLog(
2997
- "warn",
2998
- `settings onChange listener failed: ${errorMessage(error)}`
2999
- );
3000
- }
3001
- }
3079
+ await setSettingsValues(values);
3002
3080
  },
3003
3081
  async callRpc(method, input) {
3004
3082
  const record = rpcHandlers.get(method);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@get-bb/plugin-sdk",
3
- "version": "0.4.34",
3
+ "version": "0.4.35",
4
4
  "homepage": "https://github.com/get-bb/bb#readme",
5
5
  "bugs": {
6
6
  "url": "https://github.com/get-bb/bb/issues"