@get-bb/plugin-sdk 0.4.30 → 0.4.34

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
  *
@@ -143,6 +527,151 @@ type PluginSettingDescriptor = {
143
527
  };
144
528
  type PluginSettingDescriptors = Record<string, PluginSettingDescriptor>;
145
529
  type PluginSettingValue = string | boolean;
530
+ /**
531
+ * What a `message.dispatch` hook answers.
532
+ *
533
+ * `proceed` lets the attempt continue. `wait` QUEUES the message as a row
534
+ * whose `waitingOn` names this plugin and carries `reason` verbatim; the
535
+ * row stays queued until `sendAt` comes due, capacity frees, the user sends
536
+ * it now, or the orphan sweep clears it because this plugin is no longer
537
+ * running. `sendAt` (epoch ms) sets the row's own `sendAt`, so core's due sweep
538
+ * re-attempts at that instant without the plugin holding a timer of its own —
539
+ * which is what a rate-limit window wants. `reject` refuses the attempt
540
+ * outright: `message` is shown to the user verbatim.
541
+ *
542
+ * There is deliberately no "handled it myself" answer and no amendment arm —
543
+ * a hook is a decision, never an owner or an author of the work.
544
+ */
545
+ type MessageDispatchHookDecision = {
546
+ action: "proceed";
547
+ } | {
548
+ action: "wait";
549
+ reason: string;
550
+ sendAt?: number | null;
551
+ } | {
552
+ action: "reject";
553
+ message: string;
554
+ };
555
+ /**
556
+ * The execution tuple as core resolved it before this hook ran. `model` and
557
+ * the three option fields are null only when no default has been resolved for
558
+ * them yet; `providerId` is always resolved.
559
+ */
560
+ interface PluginDispatchExecution {
561
+ providerId: string;
562
+ model: string | null;
563
+ reasoningLevel: ReasoningLevel | null;
564
+ serviceTier: ServiceTier | null;
565
+ permissionMode: PermissionMode | null;
566
+ }
567
+ /**
568
+ * Where each execution value came from. `explicit` is a user choice,
569
+ * `client-preference` a remembered client default, and null means core
570
+ * resolved it from project/provider defaults. A hook that must not act against
571
+ * a deliberate choice checks for `explicit` here.
572
+ */
573
+ interface PluginDispatchExecutionSources {
574
+ providerId: ExecutionInputFieldSource | null;
575
+ model: ExecutionInputFieldSource | null;
576
+ reasoningLevel: ExecutionInputFieldSource | null;
577
+ serviceTier: ExecutionInputFieldSource | null;
578
+ permissionMode: ExecutionInputFieldSource | null;
579
+ }
580
+ /**
581
+ * The prompt this dispatch carries. `blocks` is the message itself; `text` is
582
+ * the concatenated text of its text blocks, which is what a rules-based hook
583
+ * actually wants to match on.
584
+ */
585
+ interface PluginDispatchInput {
586
+ blocks: readonly PromptInput[];
587
+ text: string;
588
+ }
589
+ /**
590
+ * How this attempt would reach the provider.
591
+ *
592
+ * `start-turn` is a dispatch that begins a turn: a thread's first message, a
593
+ * plain send to an idle or `pending` thread, or a steer-mode message that
594
+ * found no running turn to join. `join-turn` is an injection into a turn that
595
+ * is already executing.
596
+ *
597
+ * Decision powers are identical for both — a steer is hooked exactly like a
598
+ * send, uniformly. A hook that limits concurrency proceeds on `join-turn`: the
599
+ * thread already holds its slot, so joining it asks for nothing new.
600
+ */
601
+ type PluginDispatchAttemptKind = "join-turn" | "start-turn";
602
+ /**
603
+ * What core hands a `message.dispatch` hook: the one checkpoint, run before a
604
+ * message reaches a provider. The exception is a user's explicit Send-now on a
605
+ * queued row, which bypasses the pass by design — it is the user overriding
606
+ * policy, and a policy that could veto its own override would not be one.
607
+ *
608
+ * It runs identically whether the attempt is inline (someone just sent) or
609
+ * from a drain (a queued row became eligible again), and whether the message
610
+ * is a thread's first, a follow-up, a steer, or a retry of a failed turn. A
611
+ * handler must therefore be idempotent for one logical dispatch: passes re-run
612
+ * on every drain, on restart, and on retry.
613
+ */
614
+ interface MessageDispatchHookContext {
615
+ /**
616
+ * The target thread. Never null: thread creation is unhooked — it is a cheap
617
+ * row — so by the time the first message is decided about, the thread exists
618
+ * in `pending`, with its provider resolved and nothing provisioned.
619
+ */
620
+ thread: ThreadResponse;
621
+ project: Project;
622
+ /** Null until an environment is chosen (a queued or not-yet-provisioned thread). */
623
+ environment: Environment | null;
624
+ /**
625
+ * The machine the work will run on. Resolved from the environment when one
626
+ * is attached, and before that from the start intent the thread was created
627
+ * with — so a per-host policy counts a cold start against the pool it is
628
+ * about to occupy. Null only when neither names a machine.
629
+ */
630
+ host: Host | null;
631
+ input: PluginDispatchInput;
632
+ requestedExecution: PluginDispatchExecution;
633
+ /** Where each execution value came from. */
634
+ executionSources: PluginDispatchExecutionSources;
635
+ /** Whether this attempt starts a turn or joins a running one. */
636
+ attempt: PluginDispatchAttemptKind;
637
+ /**
638
+ * The queued row this attempt is re-trying, or null when the attempt is
639
+ * inline and no row has ever existed for it.
640
+ *
641
+ * This is how a hook tells a fresh send from a re-attempt of something it
642
+ * already decided about — the replacement for the old
643
+ * `isReleaseReevaluation`/`hold` pair. A hook that counts in-flight work
644
+ * should treat the two identically; a hook that logs should not
645
+ * double-count.
646
+ */
647
+ queuedMessage: ThreadQueuedMessage | null;
648
+ /** How the dispatch was requested; null for internal/core-driven sends. */
649
+ origin: ThreadCreateOrigin | null;
650
+ originPluginId: string | null;
651
+ startedOnBehalfOf: StartedOnBehalfOf | null;
652
+ parentThreadId: string | null;
653
+ }
654
+ /**
655
+ * The hooks a plugin can answer, each mapping its key to the context core
656
+ * hands the handler and the decision core acts on. `on()` and the handler type
657
+ * derive from this map — and so does the server's hook registry — so a
658
+ * half-added hook does not compile.
659
+ *
660
+ * One hook today: `message.dispatch`, THE admission checkpoint, run identically
661
+ * for a thread's first message, a follow-up, a steer, a retry, and every
662
+ * re-attempt a drain makes. It replaced the earlier `thread.create` +
663
+ * `turn.submit` pair, whose split was an accident of where the code happened to
664
+ * branch rather than a difference a plugin needed to see — the attempt's own
665
+ * `attempt` kind carries what actually differs.
666
+ */
667
+ interface PluginHookSignatures {
668
+ "message.dispatch": {
669
+ context: MessageDispatchHookContext;
670
+ decision: MessageDispatchHookDecision;
671
+ };
672
+ }
673
+ type PluginHookName = keyof PluginHookSignatures;
674
+ type PluginHookHandler<K extends PluginHookName> = (context: PluginHookSignatures[K]["context"]) => PluginHookSignatures[K]["decision"] | Promise<PluginHookSignatures[K]["decision"]>;
146
675
  interface PluginCliOutputLimitError {
147
676
  code: "plugin_cli_output_too_large";
148
677
  message: string;
@@ -764,6 +1293,24 @@ declare function agentToolIconRefusalMessage(toolName: string, problem: string):
764
1293
  * that entry.
765
1294
  */
766
1295
  declare function providerWithoutBridgeMessage(providerId: string): string;
1296
+ /**
1297
+ * Files a hook handler under its key in a per-hook record.
1298
+ *
1299
+ * The record is a mapped type over the hook-name union, so writing to it
1300
+ * through a generic key is not expressible soundly in TypeScript: this call
1301
+ * site knows `handler` matches `hook`, but the checker only knows both range
1302
+ * over the union and so demands their intersection. The erasure is confined to
1303
+ * this one function; every READ is sound, because a slot is typed for its own
1304
+ * hook and the runner builds the context for the hook it read the handler from.
1305
+ *
1306
+ * Shared by the real host (`plugin-api.ts`) and the fake one so both register
1307
+ * hooks by the same rule, which is the point of every other helper here.
1308
+ */
1309
+ declare function storePluginHook<K extends PluginHookName>(records: {
1310
+ [N in PluginHookName]: PluginHookHandler<N> | null;
1311
+ }, hook: K, handler: PluginHookHandler<K>): void;
1312
+ /** The refusal a second handler for one hook from one plugin gets. */
1313
+ declare function pluginHookAlreadyRegisteredMessage(hook: PluginHookName): string;
767
1314
 
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 };
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 };
769
1316
  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";
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { ReactNode, ComponentType } from 'react';
9
9
  import { RenderResult } from '@testing-library/react';
10
- import { PluginHomepageSectionRegistration, PluginSettingsSectionRegistration, PluginNavPanelRegistration, PluginThreadPanelActionRegistration, PluginNewThreadPanelActionRegistration, ComposerCustomization, PluginPendingInteractionRegistration, PluginSidebarFooterActionRegistration, PluginThreadListRegistration, PluginThreadHeaderActionRegistration, PluginFileOpenerRegistration, PluginSourceCodeRendererRegistration, PluginDiffRendererRegistration, PluginMessageDirectiveRegistration, PluginMessageActionRegistration, PluginProviderIconRegistration, PluginTimelineRendererRegistration, PluginContentScriptRegistration, PluginComposerScope, PluginComposerTextEffect, PluginComposerMention, PluginComposerThreadRowStatus, ExperimentalOpenFixedTabOptions, JsonValue, BbNavigate, ExperimentalFileOpenOptions, PluginAppDefinition, PluginRpcContract, StandardSchemaV1InferInput, PluginRpcResult, PluginRealtimeConnectionState, PluginSidebarThreadsState, PluginProvidersState, PluginCodeThemeState, PluginSidebarPullRequest, PluginSidebarThreadActions } from '@get-bb/plugin-sdk';
10
+ import { PluginHomepageSectionRegistration, PluginSettingsSectionRegistration, PluginNavPanelRegistration, PluginThreadPanelActionRegistration, PluginNewThreadPanelActionRegistration, ComposerCustomization, PluginPendingInteractionRegistration, PluginSidebarFooterActionRegistration, ExperimentalSidebarNavigationRegistration, PluginThreadListRegistration, PluginThreadHeaderActionRegistration, PluginFileOpenerRegistration, PluginSourceCodeRendererRegistration, PluginDiffRendererRegistration, PluginMessageDirectiveRegistration, PluginMessageActionRegistration, PluginProviderIconRegistration, PluginTimelineRendererRegistration, PluginContentScriptRegistration, PluginComposerScope, PluginComposerTextEffect, PluginComposerMention, PluginComposerThreadRowStatus, ExperimentalOpenFixedTabOptions, JsonValue, BbNavigate, ExperimentalFileOpenOptions, PluginAppDefinition, PluginRpcContract, StandardSchemaV1InferInput, PluginRpcResult, PluginRealtimeConnectionState, PluginSidebarThreadsState, PluginProvidersState, PluginCodeThemeState, PluginSidebarPullRequest, PluginSidebarThreadActions } from '@get-bb/plugin-sdk';
11
11
 
12
12
  /**
13
13
  * `@get-bb/plugin-sdk/testing/app` — the frontend plugin test harness. Tests a
@@ -91,6 +91,14 @@ interface ComposerLog {
91
91
  quotes: string[];
92
92
  mentions: PluginComposerMention[];
93
93
  focusCount: number;
94
+ /**
95
+ * Every `experimental_submit` the plugin ran, in order. The harness composer
96
+ * has no submit pipeline of its own, so it records the options and clears the
97
+ * draft — enough to assert what a picker scheduled and that it tidied up.
98
+ */
99
+ submits: Array<{
100
+ sendAt: number;
101
+ }>;
94
102
  }
95
103
  /** One recorded `experimental_useSidebarThreadActions()` call. */
96
104
  interface SidebarActionCall {
@@ -116,6 +124,7 @@ interface CapturedPluginApp {
116
124
  composerCustomizations: ComposerCustomization[];
117
125
  pendingInteractions: PluginPendingInteractionRegistration[];
118
126
  sidebarFooterActions: PluginSidebarFooterActionRegistration[];
127
+ experimentalSidebarNavigations: ExperimentalSidebarNavigationRegistration[];
119
128
  threadLists: PluginThreadListRegistration[];
120
129
  threadHeaderActions: PluginThreadHeaderActionRegistration[];
121
130
  fileOpeners: PluginFileOpenerRegistration[];