@get-bb/plugin-sdk 0.4.30 → 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.
@@ -61,9 +61,393 @@ interface ProviderNativeRootsInputLike {
61
61
  readonly project?: readonly ProviderNativeRootInput[];
62
62
  }
63
63
 
64
+ declare const environmentSchema: z.ZodObject<{
65
+ baseBranch: z.ZodNullable<z.ZodString>;
66
+ branchName: z.ZodNullable<z.ZodString>;
67
+ createdAt: z.ZodNumber;
68
+ defaultBranch: z.ZodNullable<z.ZodString>;
69
+ hostId: z.ZodString;
70
+ id: z.ZodString;
71
+ isGitRepo: z.ZodBoolean;
72
+ isWorktree: z.ZodBoolean;
73
+ managed: z.ZodBoolean;
74
+ mergeBaseBranch: z.ZodNullable<z.ZodString>;
75
+ name: z.ZodNullable<z.ZodString>;
76
+ path: z.ZodNullable<z.ZodString>;
77
+ projectId: z.ZodString;
78
+ status: z.ZodEnum<{
79
+ destroyed: "destroyed";
80
+ destroying: "destroying";
81
+ error: "error";
82
+ provisioning: "provisioning";
83
+ ready: "ready";
84
+ retiring: "retiring";
85
+ }>;
86
+ updatedAt: z.ZodNumber;
87
+ workspaceProvisionType: z.ZodEnum<{
88
+ "managed-worktree": "managed-worktree";
89
+ personal: "personal";
90
+ unmanaged: "unmanaged";
91
+ }>;
92
+ }, z.core.$strip>;
93
+ type Environment = z.infer<typeof environmentSchema>;
94
+
95
+ declare const hostSchema: z.ZodObject<{
96
+ createdAt: z.ZodNumber;
97
+ id: z.ZodString;
98
+ lastRejectedProtocolVersion: z.ZodNullable<z.ZodNumber>;
99
+ lastSeenAt: z.ZodNullable<z.ZodNumber>;
100
+ maxPermissionMode: z.ZodEnum<{
101
+ "accept-edits": "accept-edits";
102
+ auto: "auto";
103
+ full: "full";
104
+ }>;
105
+ name: z.ZodString;
106
+ status: z.ZodEnum<{
107
+ connected: "connected";
108
+ disconnected: "disconnected";
109
+ }>;
110
+ type: z.ZodEnum<{
111
+ persistent: "persistent";
112
+ }>;
113
+ updatedAt: z.ZodNumber;
114
+ }, z.core.$strip>;
115
+ type Host = z.infer<typeof hostSchema>;
116
+
117
+ declare const projectSchema: z.ZodObject<{
118
+ createdAt: z.ZodNumber;
119
+ gitRemoteUrl: z.ZodNullable<z.ZodString>;
120
+ id: z.ZodString;
121
+ kind: z.ZodEnum<{
122
+ personal: "personal";
123
+ standard: "standard";
124
+ }>;
125
+ name: z.ZodString;
126
+ updatedAt: z.ZodNumber;
127
+ }, z.core.$strip>;
128
+ type Project = z.infer<typeof projectSchema>;
129
+
130
+ declare const reasoningLevelSchema: z.ZodEnum<{
131
+ high: "high";
132
+ low: "low";
133
+ max: "max";
134
+ medium: "medium";
135
+ none: "none";
136
+ ultra: "ultra";
137
+ ultracode: "ultracode";
138
+ xhigh: "xhigh";
139
+ }>;
140
+ type ReasoningLevel = z.infer<typeof reasoningLevelSchema>;
141
+ declare const serviceTierSchema: z.ZodEnum<{
142
+ default: "default";
143
+ fast: "fast";
144
+ }>;
145
+ type ServiceTier = z.infer<typeof serviceTierSchema>;
146
+ declare const permissionModeSchema: z.ZodEnum<{
147
+ "accept-edits": "accept-edits";
148
+ auto: "auto";
149
+ full: "full";
150
+ }>;
151
+ type PermissionMode = z.infer<typeof permissionModeSchema>;
152
+ declare const promptInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
153
+ mentions: z.ZodDefault<z.ZodArray<z.ZodObject<{
154
+ end: z.ZodNumber;
155
+ resource: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodDiscriminatedUnion<[z.ZodObject<{
156
+ kind: z.ZodLiteral<"thread">;
157
+ label: z.ZodString;
158
+ projectId: z.ZodOptional<z.ZodString>;
159
+ threadId: z.ZodString;
160
+ }, z.core.$strip>, z.ZodObject<{
161
+ kind: z.ZodLiteral<"project">;
162
+ label: z.ZodString;
163
+ projectId: z.ZodString;
164
+ }, z.core.$strip>, z.ZodObject<{
165
+ kind: z.ZodLiteral<"section">;
166
+ label: z.ZodString;
167
+ sectionId: z.ZodString;
168
+ }, z.core.$strip>, z.ZodObject<{
169
+ entryKind: z.ZodEnum<{
170
+ directory: "directory";
171
+ file: "file";
172
+ }>;
173
+ kind: z.ZodLiteral<"path">;
174
+ label: z.ZodString;
175
+ path: z.ZodString;
176
+ source: z.ZodEnum<{
177
+ "thread-storage": "thread-storage";
178
+ workspace: "workspace";
179
+ }>;
180
+ }, z.core.$strip>, z.ZodObject<{
181
+ argumentHint: z.ZodNullable<z.ZodString>;
182
+ kind: z.ZodLiteral<"command">;
183
+ label: z.ZodString;
184
+ name: z.ZodString;
185
+ origin: z.ZodEnum<{
186
+ builtin: "builtin";
187
+ project: "project";
188
+ user: "user";
189
+ }>;
190
+ source: z.ZodEnum<{
191
+ command: "command";
192
+ skill: "skill";
193
+ }>;
194
+ trigger: z.ZodEnum<{
195
+ "/": "/";
196
+ }>;
197
+ }, z.core.$strip>, z.ZodObject<{
198
+ icon: z.ZodOptional<z.ZodNullable<z.ZodString>>;
199
+ itemId: z.ZodString;
200
+ kind: z.ZodLiteral<"plugin">;
201
+ label: z.ZodString;
202
+ pluginId: z.ZodString;
203
+ }, z.core.$strip>], "kind">>;
204
+ start: z.ZodNumber;
205
+ }, z.core.$strip>>>;
206
+ text: z.ZodString;
207
+ type: z.ZodLiteral<"text">;
208
+ visibility: z.ZodOptional<z.ZodEnum<{
209
+ "agent-only": "agent-only";
210
+ }>>;
211
+ }, z.core.$strip>, z.ZodObject<{
212
+ type: z.ZodLiteral<"image">;
213
+ url: z.ZodString;
214
+ visibility: z.ZodOptional<z.ZodEnum<{
215
+ "agent-only": "agent-only";
216
+ }>>;
217
+ }, z.core.$strip>, z.ZodObject<{
218
+ path: z.ZodString;
219
+ type: z.ZodLiteral<"localImage">;
220
+ visibility: z.ZodOptional<z.ZodEnum<{
221
+ "agent-only": "agent-only";
222
+ }>>;
223
+ }, z.core.$strip>, z.ZodObject<{
224
+ mimeType: z.ZodOptional<z.ZodString>;
225
+ name: z.ZodOptional<z.ZodString>;
226
+ path: z.ZodString;
227
+ sizeBytes: z.ZodOptional<z.ZodNumber>;
228
+ type: z.ZodLiteral<"localFile">;
229
+ visibility: z.ZodOptional<z.ZodEnum<{
230
+ "agent-only": "agent-only";
231
+ }>>;
232
+ }, z.core.$strip>], "type">;
233
+ type PromptInput = z.infer<typeof promptInputSchema>;
234
+ declare const callerExecutionInputSourceSchema: z.ZodEnum<{
235
+ "client-preference": "client-preference";
236
+ explicit: "explicit";
237
+ }>;
238
+ type CallerExecutionInputSource = z.infer<typeof callerExecutionInputSourceSchema>;
239
+
64
240
  declare const PROVIDER_FORK_VALUES: readonly ["none", "tip", "checkpoint"];
65
241
  type ProviderFork = (typeof PROVIDER_FORK_VALUES)[number];
66
242
 
243
+ declare const threadQueuedMessageSchema: z.ZodObject<{
244
+ content: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
245
+ mentions: z.ZodDefault<z.ZodArray<z.ZodObject<{
246
+ end: z.ZodNumber;
247
+ resource: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodDiscriminatedUnion<[z.ZodObject<{
248
+ kind: z.ZodLiteral<"thread">;
249
+ label: z.ZodString;
250
+ projectId: z.ZodOptional<z.ZodString>;
251
+ threadId: z.ZodString;
252
+ }, z.core.$strip>, z.ZodObject<{
253
+ kind: z.ZodLiteral<"project">;
254
+ label: z.ZodString;
255
+ projectId: z.ZodString;
256
+ }, z.core.$strip>, z.ZodObject<{
257
+ kind: z.ZodLiteral<"section">;
258
+ label: z.ZodString;
259
+ sectionId: z.ZodString;
260
+ }, z.core.$strip>, z.ZodObject<{
261
+ entryKind: z.ZodEnum<{
262
+ directory: "directory";
263
+ file: "file";
264
+ }>;
265
+ kind: z.ZodLiteral<"path">;
266
+ label: z.ZodString;
267
+ path: z.ZodString;
268
+ source: z.ZodEnum<{
269
+ "thread-storage": "thread-storage";
270
+ workspace: "workspace";
271
+ }>;
272
+ }, z.core.$strip>, z.ZodObject<{
273
+ argumentHint: z.ZodNullable<z.ZodString>;
274
+ kind: z.ZodLiteral<"command">;
275
+ label: z.ZodString;
276
+ name: z.ZodString;
277
+ origin: z.ZodEnum<{
278
+ builtin: "builtin";
279
+ project: "project";
280
+ user: "user";
281
+ }>;
282
+ source: z.ZodEnum<{
283
+ command: "command";
284
+ skill: "skill";
285
+ }>;
286
+ trigger: z.ZodEnum<{
287
+ "/": "/";
288
+ }>;
289
+ }, z.core.$strip>, z.ZodObject<{
290
+ icon: z.ZodOptional<z.ZodNullable<z.ZodString>>;
291
+ itemId: z.ZodString;
292
+ kind: z.ZodLiteral<"plugin">;
293
+ label: z.ZodString;
294
+ pluginId: z.ZodString;
295
+ }, z.core.$strip>], "kind">>;
296
+ start: z.ZodNumber;
297
+ }, z.core.$strip>>>;
298
+ text: z.ZodString;
299
+ type: z.ZodLiteral<"text">;
300
+ visibility: z.ZodOptional<z.ZodEnum<{
301
+ "agent-only": "agent-only";
302
+ }>>;
303
+ }, z.core.$strip>, z.ZodObject<{
304
+ type: z.ZodLiteral<"image">;
305
+ url: z.ZodString;
306
+ visibility: z.ZodOptional<z.ZodEnum<{
307
+ "agent-only": "agent-only";
308
+ }>>;
309
+ }, z.core.$strip>, z.ZodObject<{
310
+ path: z.ZodString;
311
+ type: z.ZodLiteral<"localImage">;
312
+ visibility: z.ZodOptional<z.ZodEnum<{
313
+ "agent-only": "agent-only";
314
+ }>>;
315
+ }, z.core.$strip>, z.ZodObject<{
316
+ mimeType: z.ZodOptional<z.ZodString>;
317
+ name: z.ZodOptional<z.ZodString>;
318
+ path: z.ZodString;
319
+ sizeBytes: z.ZodOptional<z.ZodNumber>;
320
+ type: z.ZodLiteral<"localFile">;
321
+ visibility: z.ZodOptional<z.ZodEnum<{
322
+ "agent-only": "agent-only";
323
+ }>>;
324
+ }, z.core.$strip>], "type">>;
325
+ createdAt: z.ZodNumber;
326
+ editable: z.ZodBoolean;
327
+ failureReason: z.ZodNullable<z.ZodString>;
328
+ groupWithNext: z.ZodBoolean;
329
+ id: z.ZodString;
330
+ model: z.ZodString;
331
+ payload: z.ZodDiscriminatedUnion<[z.ZodObject<{
332
+ kind: z.ZodLiteral<"inline">;
333
+ }, z.core.$strip>, z.ZodObject<{
334
+ attempt: z.ZodNumber;
335
+ kind: z.ZodLiteral<"retry">;
336
+ reason: z.ZodString;
337
+ retryOfTurnRequestId: z.ZodString;
338
+ }, z.core.$strip>], "kind">;
339
+ permissionMode: z.ZodEnum<{
340
+ "accept-edits": "accept-edits";
341
+ auto: "auto";
342
+ full: "full";
343
+ }>;
344
+ reasoningLevel: z.ZodEnum<{
345
+ high: "high";
346
+ low: "low";
347
+ max: "max";
348
+ medium: "medium";
349
+ none: "none";
350
+ ultra: "ultra";
351
+ ultracode: "ultracode";
352
+ xhigh: "xhigh";
353
+ }>;
354
+ sendAt: z.ZodNullable<z.ZodNumber>;
355
+ serviceTier: z.ZodEnum<{
356
+ default: "default";
357
+ fast: "fast";
358
+ }>;
359
+ threadId: z.ZodString;
360
+ updatedAt: z.ZodNumber;
361
+ waitingOn: z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodObject<{
362
+ kind: z.ZodLiteral<"time">;
363
+ }, z.core.$strip>, z.ZodObject<{
364
+ kind: z.ZodLiteral<"thread-busy">;
365
+ }, z.core.$strip>, z.ZodObject<{
366
+ kind: z.ZodLiteral<"turn-starting">;
367
+ }, z.core.$strip>, z.ZodObject<{
368
+ kind: z.ZodLiteral<"provisioning">;
369
+ }, z.core.$strip>, z.ZodObject<{
370
+ hostName: z.ZodString;
371
+ kind: z.ZodLiteral<"host-offline">;
372
+ }, z.core.$strip>, z.ZodObject<{
373
+ kind: z.ZodLiteral<"interaction">;
374
+ }, z.core.$strip>, z.ZodObject<{
375
+ kind: z.ZodLiteral<"plugin">;
376
+ pluginId: z.ZodString;
377
+ reason: z.ZodString;
378
+ }, z.core.$strip>], "kind">>;
379
+ }, z.core.$strip>;
380
+ type ThreadQueuedMessage = z.infer<typeof threadQueuedMessageSchema>;
381
+
382
+ declare const threadCreateOriginSchema: z.ZodEnum<{
383
+ app: "app";
384
+ cli: "cli";
385
+ plugin: "plugin";
386
+ sdk: "sdk";
387
+ }>;
388
+ type ThreadCreateOrigin = z.infer<typeof threadCreateOriginSchema>;
389
+ type ExecutionInputFieldSource = CallerExecutionInputSource;
390
+ declare const startedOnBehalfOfSchema: z.ZodObject<{
391
+ initiator: z.ZodEnum<{
392
+ agent: "agent";
393
+ system: "system";
394
+ }>;
395
+ senderThreadId: z.ZodString;
396
+ }, z.core.$strip>;
397
+ type StartedOnBehalfOf = z.infer<typeof startedOnBehalfOfSchema>;
398
+ declare const threadResponseSchema: z.ZodObject<{
399
+ activeBackgroundAgentCount: z.ZodNumber;
400
+ archivedAt: z.ZodNullable<z.ZodNumber>;
401
+ canSpawnChild: z.ZodBoolean;
402
+ createdAt: z.ZodNumber;
403
+ deletedAt: z.ZodNullable<z.ZodNumber>;
404
+ environmentId: z.ZodNullable<z.ZodString>;
405
+ id: z.ZodString;
406
+ lastReadAt: z.ZodNullable<z.ZodNumber>;
407
+ latestAttentionAt: z.ZodNumber;
408
+ originKind: z.ZodNullable<z.ZodEnum<{
409
+ fork: "fork";
410
+ }>>;
411
+ originPluginId: z.ZodNullable<z.ZodString>;
412
+ parentThreadId: z.ZodNullable<z.ZodString>;
413
+ pinnedAt: z.ZodNullable<z.ZodNumber>;
414
+ projectId: z.ZodString;
415
+ providerId: z.ZodString;
416
+ queuedMessageCount: z.ZodNumber;
417
+ runtime: z.ZodObject<{
418
+ displayStatus: z.ZodEnum<{
419
+ "host-reconnecting": "host-reconnecting";
420
+ "waiting-for-host": "waiting-for-host";
421
+ active: "active";
422
+ error: "error";
423
+ idle: "idle";
424
+ pending: "pending";
425
+ provisioning: "provisioning";
426
+ starting: "starting";
427
+ stopping: "stopping";
428
+ }>;
429
+ hostReconnectGraceExpiresAt: z.ZodNullable<z.ZodNumber>;
430
+ }, z.core.$strip>;
431
+ sectionId: z.ZodNullable<z.ZodString>;
432
+ sourceThreadId: z.ZodNullable<z.ZodString>;
433
+ status: z.ZodEnum<{
434
+ active: "active";
435
+ error: "error";
436
+ idle: "idle";
437
+ pending: "pending";
438
+ starting: "starting";
439
+ stopping: "stopping";
440
+ }>;
441
+ title: z.ZodNullable<z.ZodString>;
442
+ titleFallback: z.ZodNullable<z.ZodString>;
443
+ updatedAt: z.ZodNumber;
444
+ visibility: z.ZodEnum<{
445
+ hidden: "hidden";
446
+ visible: "visible";
447
+ }>;
448
+ }, z.core.$strip>;
449
+ type ThreadResponse = z.infer<typeof threadResponseSchema>;
450
+
67
451
  /**
68
452
  * A value that survives a JSON round trip without coercion or data loss.
69
453
  *
@@ -75,7 +459,7 @@ type JsonValue = string | number | boolean | null | JsonValue[] | {
75
459
  };
76
460
 
77
461
  /**
78
- * 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.
79
463
  * Zod 4 schemas implement this interface directly; other validators can do
80
464
  * the same without becoming part of BB's public protocol.
81
465
  */
@@ -107,11 +491,6 @@ interface PluginRpcMethodContract<InputSchema extends StandardSchemaV1 = Standar
107
491
  readonly output: OutputSchema;
108
492
  }
109
493
 
110
- /**
111
- * Declarative settings descriptors (`bb.settings.define`). Deliberately plain
112
- * data — not zod — so the host can render settings forms and the CLI can
113
- * parse values without executing plugin code.
114
- */
115
494
  type PluginSettingDescriptor = {
116
495
  type: "string";
117
496
  label: string;
@@ -123,26 +502,179 @@ type PluginSettingDescriptor = {
123
502
  * be multi-line.
124
503
  */
125
504
  experimental_multiline?: boolean;
505
+ /** Synchronously validate without transforming a proposed value. */
506
+ experimental_schema?: StandardSchemaV1<string, string>;
126
507
  default?: string;
127
508
  } | {
128
509
  type: "boolean";
129
510
  label: string;
130
511
  description?: string;
512
+ /** Synchronously validate without transforming a proposed value. */
513
+ experimental_schema?: StandardSchemaV1<boolean, boolean>;
131
514
  default?: boolean;
132
515
  } | {
133
516
  type: "select";
134
517
  label: string;
135
518
  description?: string;
136
519
  options: string[];
520
+ /** Synchronously validate without transforming a proposed value. */
521
+ experimental_schema?: StandardSchemaV1<string, string>;
137
522
  default?: string;
138
523
  } | {
139
524
  type: "project";
140
525
  label: string;
141
526
  description?: string;
527
+ /** Synchronously validate without transforming a proposed value. */
528
+ experimental_schema?: StandardSchemaV1<string, string>;
142
529
  default?: string;
143
530
  };
144
531
  type PluginSettingDescriptors = Record<string, PluginSettingDescriptor>;
145
532
  type PluginSettingValue = string | boolean;
533
+ /**
534
+ * What a `message.dispatch` hook answers.
535
+ *
536
+ * `proceed` lets the attempt continue. `wait` QUEUES the message as a row
537
+ * whose `waitingOn` names this plugin and carries `reason` verbatim; the
538
+ * row stays queued until `sendAt` comes due, capacity frees, the user sends
539
+ * it now, or the orphan sweep clears it because this plugin is no longer
540
+ * running. `sendAt` (epoch ms) sets the row's own `sendAt`, so core's due sweep
541
+ * re-attempts at that instant without the plugin holding a timer of its own —
542
+ * which is what a rate-limit window wants. `reject` refuses the attempt
543
+ * outright: `message` is shown to the user verbatim.
544
+ *
545
+ * There is deliberately no "handled it myself" answer and no amendment arm —
546
+ * a hook is a decision, never an owner or an author of the work.
547
+ */
548
+ type MessageDispatchHookDecision = {
549
+ action: "proceed";
550
+ } | {
551
+ action: "wait";
552
+ reason: string;
553
+ sendAt?: number | null;
554
+ } | {
555
+ action: "reject";
556
+ message: string;
557
+ };
558
+ /**
559
+ * The execution tuple as core resolved it before this hook ran. `model` and
560
+ * the three option fields are null only when no default has been resolved for
561
+ * them yet; `providerId` is always resolved.
562
+ */
563
+ interface PluginDispatchExecution {
564
+ providerId: string;
565
+ model: string | null;
566
+ reasoningLevel: ReasoningLevel | null;
567
+ serviceTier: ServiceTier | null;
568
+ permissionMode: PermissionMode | null;
569
+ }
570
+ /**
571
+ * Where each execution value came from. `explicit` is a user choice,
572
+ * `client-preference` a remembered client default, and null means core
573
+ * resolved it from project/provider defaults. A hook that must not act against
574
+ * a deliberate choice checks for `explicit` here.
575
+ */
576
+ interface PluginDispatchExecutionSources {
577
+ providerId: ExecutionInputFieldSource | null;
578
+ model: ExecutionInputFieldSource | null;
579
+ reasoningLevel: ExecutionInputFieldSource | null;
580
+ serviceTier: ExecutionInputFieldSource | null;
581
+ permissionMode: ExecutionInputFieldSource | null;
582
+ }
583
+ /**
584
+ * The prompt this dispatch carries. `blocks` is the message itself; `text` is
585
+ * the concatenated text of its text blocks, which is what a rules-based hook
586
+ * actually wants to match on.
587
+ */
588
+ interface PluginDispatchInput {
589
+ blocks: readonly PromptInput[];
590
+ text: string;
591
+ }
592
+ /**
593
+ * How this attempt would reach the provider.
594
+ *
595
+ * `start-turn` is a dispatch that begins a turn: a thread's first message, a
596
+ * plain send to an idle or `pending` thread, or a steer-mode message that
597
+ * found no running turn to join. `join-turn` is an injection into a turn that
598
+ * is already executing.
599
+ *
600
+ * Decision powers are identical for both — a steer is hooked exactly like a
601
+ * send, uniformly. A hook that limits concurrency proceeds on `join-turn`: the
602
+ * thread already holds its slot, so joining it asks for nothing new.
603
+ */
604
+ type PluginDispatchAttemptKind = "join-turn" | "start-turn";
605
+ /**
606
+ * What core hands a `message.dispatch` hook: the one checkpoint, run before a
607
+ * message reaches a provider. The exception is a user's explicit Send-now on a
608
+ * queued row, which bypasses the pass by design — it is the user overriding
609
+ * policy, and a policy that could veto its own override would not be one.
610
+ *
611
+ * It runs identically whether the attempt is inline (someone just sent) or
612
+ * from a drain (a queued row became eligible again), and whether the message
613
+ * is a thread's first, a follow-up, a steer, or a retry of a failed turn. A
614
+ * handler must therefore be idempotent for one logical dispatch: passes re-run
615
+ * on every drain, on restart, and on retry.
616
+ */
617
+ interface MessageDispatchHookContext {
618
+ /**
619
+ * The target thread. Never null: thread creation is unhooked — it is a cheap
620
+ * row — so by the time the first message is decided about, the thread exists
621
+ * in `pending`, with its provider resolved and nothing provisioned.
622
+ */
623
+ thread: ThreadResponse;
624
+ project: Project;
625
+ /** Null until an environment is chosen (a queued or not-yet-provisioned thread). */
626
+ environment: Environment | null;
627
+ /**
628
+ * The machine the work will run on. Resolved from the environment when one
629
+ * is attached, and before that from the start intent the thread was created
630
+ * with — so a per-host policy counts a cold start against the pool it is
631
+ * about to occupy. Null only when neither names a machine.
632
+ */
633
+ host: Host | null;
634
+ input: PluginDispatchInput;
635
+ requestedExecution: PluginDispatchExecution;
636
+ /** Where each execution value came from. */
637
+ executionSources: PluginDispatchExecutionSources;
638
+ /** Whether this attempt starts a turn or joins a running one. */
639
+ attempt: PluginDispatchAttemptKind;
640
+ /**
641
+ * The queued row this attempt is re-trying, or null when the attempt is
642
+ * inline and no row has ever existed for it.
643
+ *
644
+ * This is how a hook tells a fresh send from a re-attempt of something it
645
+ * already decided about — the replacement for the old
646
+ * `isReleaseReevaluation`/`hold` pair. A hook that counts in-flight work
647
+ * should treat the two identically; a hook that logs should not
648
+ * double-count.
649
+ */
650
+ queuedMessage: ThreadQueuedMessage | null;
651
+ /** How the dispatch was requested; null for internal/core-driven sends. */
652
+ origin: ThreadCreateOrigin | null;
653
+ originPluginId: string | null;
654
+ startedOnBehalfOf: StartedOnBehalfOf | null;
655
+ parentThreadId: string | null;
656
+ }
657
+ /**
658
+ * The hooks a plugin can answer, each mapping its key to the context core
659
+ * hands the handler and the decision core acts on. `on()` and the handler type
660
+ * derive from this map — and so does the server's hook registry — so a
661
+ * half-added hook does not compile.
662
+ *
663
+ * One hook today: `message.dispatch`, THE admission checkpoint, run identically
664
+ * for a thread's first message, a follow-up, a steer, a retry, and every
665
+ * re-attempt a drain makes. It replaced the earlier `thread.create` +
666
+ * `turn.submit` pair, whose split was an accident of where the code happened to
667
+ * branch rather than a difference a plugin needed to see — the attempt's own
668
+ * `attempt` kind carries what actually differs.
669
+ */
670
+ interface PluginHookSignatures {
671
+ "message.dispatch": {
672
+ context: MessageDispatchHookContext;
673
+ decision: MessageDispatchHookDecision;
674
+ };
675
+ }
676
+ type PluginHookName = keyof PluginHookSignatures;
677
+ type PluginHookHandler<K extends PluginHookName> = (context: PluginHookSignatures[K]["context"]) => PluginHookSignatures[K]["decision"] | Promise<PluginHookSignatures[K]["decision"]>;
146
678
  interface PluginCliOutputLimitError {
147
679
  code: "plugin_cli_output_too_large";
148
680
  message: string;
@@ -698,7 +1230,12 @@ declare function isStandardSchema(value: unknown): value is StandardSchemaV1;
698
1230
  declare function readRpcMethodContract(method: string, value: unknown): PluginRpcMethodContract;
699
1231
  /** Duck-typed zod detection: plugin sources may carry their own zod copy,
700
1232
  * so instanceof is useless — anything with safeParse is treated as zod. */
701
- 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;
702
1239
  /**
703
1240
  * Reject recursive local references before a tool schema reaches a provider.
704
1241
  * Some providers reject the complete tool list when any one schema contains a
@@ -764,6 +1301,24 @@ declare function agentToolIconRefusalMessage(toolName: string, problem: string):
764
1301
  * that entry.
765
1302
  */
766
1303
  declare function providerWithoutBridgeMessage(providerId: string): string;
1304
+ /**
1305
+ * Files a hook handler under its key in a per-hook record.
1306
+ *
1307
+ * The record is a mapped type over the hook-name union, so writing to it
1308
+ * through a generic key is not expressible soundly in TypeScript: this call
1309
+ * site knows `handler` matches `hook`, but the checker only knows both range
1310
+ * over the union and so demands their intersection. The erasure is confined to
1311
+ * this one function; every READ is sound, because a slot is typed for its own
1312
+ * hook and the runner builds the context for the hook it read the handler from.
1313
+ *
1314
+ * Shared by the real host (`plugin-api.ts`) and the fake one so both register
1315
+ * hooks by the same rule, which is the point of every other helper here.
1316
+ */
1317
+ declare function storePluginHook<K extends PluginHookName>(records: {
1318
+ [N in PluginHookName]: PluginHookHandler<N> | null;
1319
+ }, hook: K, handler: PluginHookHandler<K>): void;
1320
+ /** The refusal a second handler for one hook from one plugin gets. */
1321
+ declare function pluginHookAlreadyRegisteredMessage(hook: PluginHookName): string;
767
1322
 
768
- 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, providerAlreadyRegisteredMessage, providerIconRefusalMessage, providerWithoutBridgeMessage, readRpcMethodContract, registerSettingDescriptors, rejectStaleAgentToolFields, 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 };
769
1324
  export type { AiServiceHostBinding, NormalizedPluginProviderDeclaration };
@@ -5,7 +5,7 @@
5
5
  // Confused by the API, or need a symbol that isn't here? Clone the BB repo
6
6
  // and read the real source: https://github.com/get-bb/bb
7
7
 
8
- import { PluginHomepageSectionRegistration, PluginSettingsSectionRegistration, PluginNavPanelRegistration, PluginThreadPanelActionRegistration, PluginNewThreadPanelActionRegistration, ComposerCustomization, PluginPendingInteractionRegistration, PluginSidebarFooterActionRegistration, PluginThreadListRegistration, PluginThreadHeaderActionRegistration, PluginFileOpenerRegistration, PluginSourceCodeRendererRegistration, PluginDiffRendererRegistration, PluginMessageDirectiveRegistration, PluginMessageActionRegistration, PluginCommandPaletteActionRegistration, PluginProviderIconRegistration, PluginTimelineRendererRegistration, PluginContentScriptRegistration, PluginAppDefinition } from '@get-bb/plugin-sdk';
8
+ import { PluginHomepageSectionRegistration, PluginSettingsSectionRegistration, PluginNavPanelRegistration, PluginThreadPanelActionRegistration, PluginNewThreadPanelActionRegistration, ComposerCustomization, PluginPendingInteractionRegistration, PluginSidebarFooterActionRegistration, ExperimentalSidebarNavigationRegistration, PluginThreadListRegistration, PluginThreadHeaderActionRegistration, PluginFileOpenerRegistration, PluginSourceCodeRendererRegistration, PluginDiffRendererRegistration, PluginMessageDirectiveRegistration, PluginMessageActionRegistration, PluginCommandPaletteActionRegistration, PluginProviderIconRegistration, PluginTimelineRendererRegistration, PluginContentScriptRegistration, PluginAppDefinition } from '@get-bb/plugin-sdk';
9
9
 
10
10
  /** Validated registrations produced by one plugin app setup execution. */
11
11
  interface CollectedPluginAppRegistrations {
@@ -17,6 +17,7 @@ interface CollectedPluginAppRegistrations {
17
17
  composerCustomizations: ComposerCustomization[];
18
18
  pendingInteractions: PluginPendingInteractionRegistration[];
19
19
  sidebarFooterActions: PluginSidebarFooterActionRegistration[];
20
+ experimentalSidebarNavigations: ExperimentalSidebarNavigationRegistration[];
20
21
  threadLists: PluginThreadListRegistration[];
21
22
  threadHeaderActions: PluginThreadHeaderActionRegistration[];
22
23
  fileOpeners: PluginFileOpenerRegistration[];
@@ -2587,7 +2587,6 @@ declare const threadEventSchema: z.ZodPipe<z.ZodUnknown, z.ZodUnion<readonly [z.
2587
2587
  threadId: z.ZodString;
2588
2588
  type: z.ZodLiteral<"client/thread/start">;
2589
2589
  }, z.core.$strip>, z.ZodObject<{
2590
- continuationOfRequestId: z.ZodOptional<z.ZodString>;
2591
2590
  direction: z.ZodLiteral<"outbound">;
2592
2591
  execution: z.ZodObject<{
2593
2592
  model: z.ZodString;
@@ -2794,6 +2793,8 @@ declare const threadEventSchema: z.ZodPipe<z.ZodUnknown, z.ZodUnion<readonly [z.
2794
2793
  params: z.ZodRecord<z.ZodString, z.ZodUnknown>;
2795
2794
  }, z.core.$strip>;
2796
2795
  requestId: z.ZodString;
2796
+ retryAttempt: z.ZodOptional<z.ZodNumber>;
2797
+ retryOfRequestId: z.ZodOptional<z.ZodString>;
2797
2798
  senderThreadId: z.ZodNullable<z.ZodString>;
2798
2799
  source: z.ZodEnum<{
2799
2800
  spawn: "spawn";