@get-bb/plugin-sdk 0.5.15 → 0.5.23

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.
@@ -1677,26 +1677,71 @@ interface PluginMentionProviderRegistration {
1677
1677
  experimental_images?: readonly ExperimentalPluginMentionImage[];
1678
1678
  }>;
1679
1679
  }
1680
+ /** Options for one `complete` call. */
1681
+ interface PluginAiCompleteOptions {
1682
+ /**
1683
+ * Aborted when bb stops waiting: the task timed out (5 seconds for titles
1684
+ * and commit messages) or the request was cancelled. Pass it to `fetch`.
1685
+ */
1686
+ readonly signal: AbortSignal;
1687
+ }
1688
+ /** Options for one `transcribe` call. */
1689
+ interface PluginAiTranscribeOptions {
1690
+ /** Aborted when bb stops waiting (10 seconds) or the request was cancelled. */
1691
+ readonly signal: AbortSignal;
1692
+ /**
1693
+ * Vocabulary the speaker is likely to use (names, identifiers), for
1694
+ * services that accept a transcription prompt; `null` when there is none.
1695
+ */
1696
+ readonly hint: string | null;
1697
+ }
1680
1698
  /**
1681
- * What a plugin's AI service does. `inference` answers bb's server-side helper
1682
- * completions (thread titles, commit messages: a prompt and a JSON Schema in,
1683
- * a structured value out); `voice` transcribes recorded speech.
1699
+ * Whether a service can answer right now. `message` is shown to the user
1700
+ * beside the service ("Sign in to your bb account") and should say how to
1701
+ * make it ready.
1684
1702
  */
1685
- type PluginAiServiceKind = "inference" | "voice";
1703
+ type PluginAiServiceStatus = {
1704
+ readonly ready: true;
1705
+ } | {
1706
+ readonly ready: false;
1707
+ readonly message: string;
1708
+ };
1686
1709
  /**
1687
- * An AI service a plugin offers from its `bb.host` entry, which implements
1688
- * `experimental_aiServicesHostContract` (`@get-bb/plugin-sdk/ai-services`).
1689
- * The user selects it with `BB_INFERENCE` / `BB_TRANSCRIPTION` set to
1690
- * `<id>/<model>`; core calls the plugin's host entry on the primary host with
1691
- * the `id` on every request, so one entry can serve several services.
1710
+ * An AI service bb can use for its helper tasks: thread titles and commit
1711
+ * messages (`complete`) and voice input (`transcribe`). The user picks a
1712
+ * service per task in Settings → AI services or with
1713
+ * `bb settings ai-services set`. The functions run in the plugin's server
1714
+ * process; a plugin that needs host-local state (a login file, a local model)
1715
+ * reaches its own `bb.host` entry through `bb.hosts.experimental_client`.
1716
+ *
1717
+ * bb owns the prompts and cleans up replies (think blocks, quotes, labels,
1718
+ * extra lines), so a service returns the model's text as-is. The plugin owns
1719
+ * everything behind the function: which model, which API, any retries.
1720
+ * Failure is a rejected promise.
1692
1721
  */
1693
1722
  interface PluginAiServiceDeclaration {
1694
- /** The `<serviceId>` segment of the user's setting; stable, lowercase. */
1723
+ /**
1724
+ * Stable, lowercase id, unique within this plugin. bb identifies a service
1725
+ * by plugin id and service id, so another plugin may use the same id.
1726
+ * `automatic` and `off` are reserved.
1727
+ */
1695
1728
  readonly id: string;
1696
- /** Shown beside the id wherever the setting's options are listed. */
1729
+ /** Shown in the picker; 1-64 characters. */
1697
1730
  readonly displayName: string;
1698
- /** Which kinds this service answers; a kind it lacks is not offered. */
1699
- readonly kinds: readonly PluginAiServiceKind[];
1731
+ /**
1732
+ * Answer one prompt with plain text. Offered for thread titles and commit
1733
+ * messages. Declare `complete`, `transcribe`, or both.
1734
+ */
1735
+ readonly complete?: (prompt: string, options: PluginAiCompleteOptions) => Promise<string>;
1736
+ /** Transcribe recorded speech to text. Offered for voice input. */
1737
+ readonly transcribe?: (audio: File, options: PluginAiTranscribeOptions) => Promise<string>;
1738
+ /**
1739
+ * Report whether the service can answer. bb calls it for the picker's
1740
+ * status line and to decide whether Automatic skips the service and
1741
+ * whether the microphone shows; results are cached for a few seconds.
1742
+ * Omit it when the service is always ready.
1743
+ */
1744
+ readonly status?: () => Promise<PluginAiServiceStatus>;
1700
1745
  }
1701
1746
 
1702
1747
  declare function pluginCliCollisionWarning(pluginId: string, commandName: string): string | null;
@@ -1733,57 +1778,24 @@ declare const PLUGIN_PROVIDER_PERMISSION_MODE_VALUES: readonly ["accept-edits",
1733
1778
  declare const PLUGIN_PROVIDER_REASONING_LEVEL_VALUES: readonly ["none", "low", "medium", "high", "xhigh", "ultracode", "max", "ultra"];
1734
1779
  declare const PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES: readonly ["plan", "goal"];
1735
1780
  /**
1736
- * AI-service ids the server serves itself: `openai` transcription and the
1737
- * builtin inference providers (pi-ai 0.84). A plugin cannot register one —
1738
- * it would capture the user's prompts and audio. This list is the one source
1739
- * for both the fake host and production (`isServerDirectAiServiceId`);
1740
- * apps/server/test/services/plugins/plugin-ai-services.test.ts pins it to
1741
- * pi-ai's provider registry, so a pi-ai bump must move it in the same change.
1781
+ * A validated `bb.experimental_aiServices.register` declaration. Absent
1782
+ * functions are `null` so hosts never have to distinguish missing from
1783
+ * undefined.
1742
1784
  */
1743
- declare const SERVER_DIRECT_AI_SERVICE_IDS: readonly string[];
1785
+ interface NormalizedPluginAiService {
1786
+ readonly id: string;
1787
+ readonly displayName: string;
1788
+ readonly complete: NonNullable<PluginAiServiceDeclaration["complete"]> | null;
1789
+ readonly transcribe: NonNullable<PluginAiServiceDeclaration["transcribe"]> | null;
1790
+ readonly status: NonNullable<PluginAiServiceDeclaration["status"]> | null;
1791
+ }
1744
1792
  /**
1745
1793
  * Validate one `bb.experimental_aiServices.register` declaration the same
1746
1794
  * way in the production host and the fake host. Throws on the first problem;
1747
1795
  * returns a normalized, frozen copy carrying only contract fields.
1748
1796
  */
1749
- declare function validatePluginAiServiceDeclaration(declaration: PluginAiServiceDeclaration): PluginAiServiceDeclaration;
1750
- /**
1751
- * What an AI service binds to, decided at the
1752
- * `bb.experimental_aiServices.register` call: the plugin's built `bb.host`
1753
- * artifact, or — when the plugin declares an entry that failed to build —
1754
- * nothing yet, with the build problem. An unbound service is staged so the
1755
- * factory completes; the load then fails on that problem before the staged
1756
- * registrations flush, so the service never goes live, while a provider the
1757
- * same factory declared can still be retained as unavailable.
1758
- */
1759
- type AiServiceHostBinding<THostArtifact> = {
1760
- readonly artifact: THostArtifact;
1761
- readonly problem: null;
1762
- } | {
1763
- readonly artifact: null;
1764
- readonly problem: string;
1765
- };
1766
- /**
1767
- * The refusals a host makes at `bb.experimental_aiServices.register` before
1768
- * it stages the declaration: a reserved server-direct id, and a plugin with
1769
- * no `bb.host` entry for the service to run on. A plugin whose declared
1770
- * entry failed to build is not refused here: the service is staged unbound,
1771
- * carrying the build problem, so the load fails on that problem — the
1772
- * actionable one — after the factory instead of at this call, and a
1773
- * provider the same factory declares is listed as unavailable rather than
1774
- * lost. Returns what the service binds to. The production host and the fake
1775
- * host both call this, so they refuse identically;
1776
- * apps/server/test/services/plugins/plugin-ai-services.test.ts pins the
1777
- * messages.
1778
- */
1779
- declare function assertAiServiceRegistrable<THostArtifact>(args: {
1780
- id: string;
1781
- /** The plugin's built `bb.host` artifact, or null when it has none. */
1782
- hostArtifact: THostArtifact | null;
1783
- /** Why the artifact is missing when the plugin declared an entry that failed to build. */
1784
- hostArtifactProblem: string | null;
1785
- }): AiServiceHostBinding<THostArtifact>;
1786
- /** The collision a second registration of a live AI-service id raises. */
1797
+ declare function validatePluginAiServiceDeclaration(declaration: PluginAiServiceDeclaration): NormalizedPluginAiService;
1798
+ /** The collision a plugin's second registration of one AI-service id raises. */
1787
1799
  declare function aiServiceAlreadyRegisteredMessage(id: string): string;
1788
1800
  /** The collision a second registration of a live provider id raises. */
1789
1801
  declare function providerAlreadyRegisteredMessage(id: string): string;
@@ -2069,5 +2081,5 @@ declare function normalizePluginAgentConfiguration(args: {
2069
2081
  declare function validateRpcValue(schema: StandardSchemaV1, value: unknown, phase: "input" | "output", fail: (error: PluginRpcError) => never): Promise<unknown>;
2070
2082
  declare function normalizeRpcJsonResult(value: unknown, fail: (error: PluginRpcError) => never): JsonValue;
2071
2083
 
2072
- export { ENVIRONMENT_PROVIDER_DESCRIPTION_MAX_CHARS, ENVIRONMENT_PROVIDER_DISPLAY_NAME_MAX_CHARS, ENVIRONMENT_PROVIDER_ID_PATTERN, ENVIRONMENT_PROVIDER_REQUIREMENT_NAMES, KV_VALUE_MAX_BYTES, MACHINE_PROVIDER_DESCRIPTION_MAX_CHARS, PLUGIN_AGENT_STATUS_LABEL_MAX_CHARS, PLUGIN_MENTION_TRIGGER_VALUES, PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES, PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES, PLUGIN_PROVIDER_DISPLAY_NAME_MAX_CHARS, PLUGIN_PROVIDER_ENV_MAX_ENTRIES, PLUGIN_PROVIDER_ENV_NAME_PATTERN, PLUGIN_PROVIDER_PERMISSION_MODE_VALUES, PLUGIN_PROVIDER_REASONING_LEVEL_VALUES, PROVIDER_ID_PATTERN, RESERVED_AGENT_TOOL_NAMES, RESERVED_BB_CLI_COMMANDS, SERVER_DIRECT_AI_SERVICE_IDS, SETTING_KEY_PATTERN, adoptHttpRouteResponse, aiServiceAlreadyRegisteredMessage, assertAiServiceRegistrable, coerceStoredPluginSettingValue, deriveValidatedProviderOptions, enforcePluginCliOutputLimit, environmentCompositionSchema, isPluginMentionTrigger, isStandardSchema, normalizeAgentToolRegistration, normalizeCliRegistration, normalizeHttpRouteRegistration, normalizeInteractionRequest, normalizeMentionProviderRegistration, normalizePluginAgentConfiguration, normalizeRealtimePayload, normalizeRpcJsonResult, normalizeRpcRegistration, normalizeWebSocketRouteRegistration, parsePluginRowPresentation, pluginCliCollisionWarning, pluginHookAlreadyRegisteredMessage, providerAlreadyRegisteredMessage, providerIconRefusalMessage, providerWithoutBridgeMessage, publishRpcMethod, readRpcPublicationOptions, registerSettingDescriptors, runPluginStorageMigrations, storePluginHook, summarizeStandardIssues, undeclaredIconProblem, validateBackgroundServiceRegistration, validatePluginAiServiceDeclaration, validatePluginEnvironmentProviderDeclaration, validatePluginMachineProviderDeclaration, validatePluginProviderDeclaration, validatePluginProviderEnvEntries, validateProviderEnvContribution, validateRpcValue, validateScheduleRegistration, validateServerAccessProviderDeclaration, validateSettingsUpdate };
2073
- export type { AiServiceHostBinding, NormalizedPluginEnvironmentComposition, NormalizedPluginEnvironmentProvider, NormalizedPluginEnvironmentProviderRequirements, NormalizedPluginInteractionRequest, NormalizedPluginMachineProvider, NormalizedPluginProviderDeclaration };
2084
+ export { ENVIRONMENT_PROVIDER_DESCRIPTION_MAX_CHARS, ENVIRONMENT_PROVIDER_DISPLAY_NAME_MAX_CHARS, ENVIRONMENT_PROVIDER_ID_PATTERN, ENVIRONMENT_PROVIDER_REQUIREMENT_NAMES, KV_VALUE_MAX_BYTES, MACHINE_PROVIDER_DESCRIPTION_MAX_CHARS, PLUGIN_AGENT_STATUS_LABEL_MAX_CHARS, PLUGIN_MENTION_TRIGGER_VALUES, PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES, PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES, PLUGIN_PROVIDER_DISPLAY_NAME_MAX_CHARS, PLUGIN_PROVIDER_ENV_MAX_ENTRIES, PLUGIN_PROVIDER_ENV_NAME_PATTERN, PLUGIN_PROVIDER_PERMISSION_MODE_VALUES, PLUGIN_PROVIDER_REASONING_LEVEL_VALUES, PROVIDER_ID_PATTERN, RESERVED_AGENT_TOOL_NAMES, RESERVED_BB_CLI_COMMANDS, SETTING_KEY_PATTERN, adoptHttpRouteResponse, aiServiceAlreadyRegisteredMessage, coerceStoredPluginSettingValue, deriveValidatedProviderOptions, enforcePluginCliOutputLimit, environmentCompositionSchema, isPluginMentionTrigger, isStandardSchema, normalizeAgentToolRegistration, normalizeCliRegistration, normalizeHttpRouteRegistration, normalizeInteractionRequest, normalizeMentionProviderRegistration, normalizePluginAgentConfiguration, normalizeRealtimePayload, normalizeRpcJsonResult, normalizeRpcRegistration, normalizeWebSocketRouteRegistration, parsePluginRowPresentation, pluginCliCollisionWarning, pluginHookAlreadyRegisteredMessage, providerAlreadyRegisteredMessage, providerIconRefusalMessage, providerWithoutBridgeMessage, publishRpcMethod, readRpcPublicationOptions, registerSettingDescriptors, runPluginStorageMigrations, storePluginHook, summarizeStandardIssues, undeclaredIconProblem, validateBackgroundServiceRegistration, validatePluginAiServiceDeclaration, validatePluginEnvironmentProviderDeclaration, validatePluginMachineProviderDeclaration, validatePluginProviderDeclaration, validatePluginProviderEnvEntries, validateProviderEnvContribution, validateRpcValue, validateScheduleRegistration, validateServerAccessProviderDeclaration, validateSettingsUpdate };
2085
+ export type { NormalizedPluginAiService, NormalizedPluginEnvironmentComposition, NormalizedPluginEnvironmentProvider, NormalizedPluginEnvironmentProviderRequirements, NormalizedPluginInteractionRequest, NormalizedPluginMachineProvider, NormalizedPluginProviderDeclaration };
@@ -1657,6 +1657,7 @@ declare const BRIDGE_JSON_RPC_ERRORS: {
1657
1657
  readonly NO_ACTIVE_TURN: -32001;
1658
1658
  readonly SESSION_NOT_RESTORABLE: -32002;
1659
1659
  readonly FORK_CHECKPOINT_UNSUPPORTED: -32003;
1660
+ readonly MISSING_EXECUTABLE: -32004;
1660
1661
  };
1661
1662
  declare const providerRecoveryHintSchema: z.ZodObject<{
1662
1663
  kind: z.ZodEnum<{
@@ -35,11 +35,11 @@ type CollectedPluginProviderIconRegistration = Omit<PluginProviderIconRegistrati
35
35
  *
36
36
  * - {@link installTestPluginRuntime} fills `globalThis.__bbPluginRuntime.
37
37
  * pluginSdkApp` with a test implementation of the `@get-bb/plugin-sdk/app`
38
- * surface (the same seam `bb plugin build` shims to the real app). It must
39
- * run BEFORE the plugin's `app.tsx` module evaluates, because that module
40
- * binds the runtime at import time — so import `app.tsx` through
41
- * {@link loadPluginApp}'s thunk form, or call the installer from a vitest
42
- * setup file when you prefer static imports.
38
+ * surface (the same seam `bb plugin build` shims to the real app). The
39
+ * `@get-bb/plugin-sdk/app` exports look the runtime up when they are called
40
+ * or rendered, so import order does not matter: install the runtime any time
41
+ * before the first hook runs ({@link loadPluginApp} and {@link renderSlot}
42
+ * install it for you).
43
43
  * - {@link loadPluginApp} runs the definition's setup against a validating
44
44
  * collector (ported from the BB app's interpreter, same error messages)
45
45
  * and returns the typed slot registrations.
@@ -161,9 +161,11 @@ interface SidebarNavigationCall {
161
161
  openInSplit?: boolean;
162
162
  }
163
163
  /**
164
- * Install the test runtime at `globalThis.__bbPluginRuntime.pluginSdkApp`.
165
- * Idempotent per module instance; must run before the plugin's `app.tsx`
166
- * (and therefore `@get-bb/plugin-sdk/app`) is imported.
164
+ * Install the test runtime at `globalThis.__bbPluginRuntime.pluginSdkApp`,
165
+ * plus the React the `@get-bb/plugin-sdk/app` components render through.
166
+ * Idempotent per module instance. Call it before the first SDK hook runs or
167
+ * SDK component renders; when the plugin's modules are imported does not
168
+ * matter.
167
169
  */
168
170
  declare function installTestPluginRuntime(): void;
169
171
  interface CapturedPluginApp {
@@ -200,9 +202,8 @@ type PluginAppModule = {
200
202
  type PluginAppSource = PluginAppDefinition | PluginAppModule | (() => Promise<PluginAppDefinition | PluginAppModule>);
201
203
  /**
202
204
  * Install the test runtime, resolve the plugin app definition, and capture
203
- * its slot registrations. Pass a thunk (`() => import("../app.tsx")`) so the
204
- * plugin module evaluates after the runtime is installed — a static import
205
- * would bind `definePluginApp` before the installer runs.
205
+ * its slot registrations. Pass the imported module, its default export, or a
206
+ * thunk (`() => import("../app.tsx")`).
206
207
  */
207
208
  declare function loadPluginApp(source: PluginAppSource): Promise<CapturedPluginApp>;
208
209
  interface ContentScriptTestMountOptions {
@@ -256,6 +257,8 @@ interface RenderSlotOptions<Contract extends PluginRpcContract = PluginRpcContra
256
257
  projectId?: string | null;
257
258
  threadId?: string | null;
258
259
  };
260
+ /** `experimental_usePluginId()` value; defaults to `test-plugin`. */
261
+ pluginId?: string;
259
262
  /** Initial `useRealtimeConnectionState()` value; defaults to `connected`. */
260
263
  realtimeConnectionState?: PluginRealtimeConnectionState;
261
264
  /** Initial state for this render's isolated composer scope and view. */
@@ -1371,10 +1371,8 @@ interface CreateFakePluginHostOptions {
1371
1371
  sharedPortTunnelIdentities?: Record<string, PluginSharedPortTunnelIdentity>;
1372
1372
  /**
1373
1373
  * Whether the plugin's manifest declares a `bb.host` entry. Production
1374
- * refuses `bb.providers.register` (the provider would have no bridge to
1375
- * run on) and `experimental_aiServices.register` (the service would have
1376
- * nothing to run on) without one; the fake applies the same rules.
1377
- * Defaults to true.
1374
+ * refuses `bb.providers.register` without one (the provider would have no
1375
+ * bridge to run on); the fake applies the same rule. Defaults to true.
1378
1376
  */
1379
1377
  experimental_hostEntry?: boolean;
1380
1378
  /**