diff --git a/docs/api_to_audit.md b/docs/api_to_audit.md index 749eab4a73..57d22ead47 100644 --- a/docs/api_to_audit.md +++ b/docs/api_to_audit.md @@ -655,7 +655,7 @@ protocol`'s `assembler`, `conformance`, and `testing` subpaths. (`turnId`, `itemId`, `id`, `parentToolCallId`) and drops `providerCheckpointId`. Confirm the defaults against a third-party bridge's goldens before fixing them. -3. **Surface size.** 33 value exports plus 43 types. The JSON-RPC harness +3. **Surface size.** 33 value exports plus 57 types. The JSON-RPC harness duplicates a little of the bridge kit's envelope parsing; fold or keep deliberately. 4. **Replay profile shape.** `ReplayProviderProfile` is the three seams the @@ -676,6 +676,21 @@ protocol`'s `assembler`, `conformance`, and `testing` subpaths. recording. It is a textual substitution of a unique temp path; confirm no bridge emits that path in a form the substitution misses (URL-encoded, JSON-escaped backslashes on Windows). +7. **The canonical event vocabulary by name.** The kit exports `ThreadEvent`, + `ThreadEventItem`, `ThreadEventItemPresentation` (+ its label, icon and + tint parts) and the named item kinds (`ThreadEventDelegationItem`, + `ThreadEventExtensionItem`, `ThreadEventFileReadItem`, + `ThreadEventSearchItem`, `ThreadEventPlanStepsItem`, + `ThreadEventWebSearchItem`, `ThreadEventWebFetchItem`, + `ThreadEventBackgroundTaskItem`) as types, re-exported from `@bb/domain` + and inlined into the bundled declarations. Before this a plugin test named + the event type as `ReturnType[number]`. + They are types only: a bridge never constructs an event (the assembler + does), so no `experimental_` value ships with them. Audit: the persisted + vocabulary now has a second public home beside `ProviderInfo` on the root + entry; a breaking change to an item shape is a breaking change to the kit. + Decide whether the kit should pin a grammar version in its exports (the + assembler already names `ASSEMBLER_GRAMMAR_VERSIONS`) before stabilizing. ## `app.experimental_useProviders` (`@get-bb/plugin-sdk/app`) diff --git a/examples/plugins/echo-provider/provider-bridge.stream.test.ts b/examples/plugins/echo-provider/provider-bridge.stream.test.ts index e1d14d0fbb..b0eef634fe 100644 --- a/examples/plugins/echo-provider/provider-bridge.stream.test.ts +++ b/examples/plugins/echo-provider/provider-bridge.stream.test.ts @@ -33,6 +33,7 @@ import type { BridgeJsonRpcObject, BridgeJsonRpcOutputMessage, BridgeJsonRpcTestHarness, + ThreadEvent, } from "@get-bb/plugin-sdk/provider-bridge/testing"; import { handleLine } from "./src/provider-bridge.js"; @@ -45,16 +46,9 @@ import { ECHO_STAMP_TOOL_PRESENTATION, } from "./src/vocabulary.js"; -/** - * The canonical event type, derived from the kit's collector. The kit does - * not export `ThreadEvent` by name (its vocabulary lives in bb's private - * domain package), so a plugin test names it this way. - */ -type AssembledEvent = ReturnType< - BridgeDeltaEventCollector["assembleMessage"] ->[number]; +/** An item lifecycle event: what the assembler builds from `item.open`/`item.close`. */ type ItemEvent = Extract< - AssembledEvent, + ThreadEvent, { type: "item/started" | "item/completed" } >; @@ -160,13 +154,13 @@ function answerToolCall( return params; } -function assembledEvents(): AssembledEvent[] { +function assembledEvents(): ThreadEvent[] { return harness.messages.flatMap((message) => collector.assembleMessage(message), ); } -function itemEvents(events: AssembledEvent[]): ItemEvent[] { +function itemEvents(events: ThreadEvent[]): ItemEvent[] { return events.filter( (event): event is ItemEvent => event.type === "item/started" || event.type === "item/completed", @@ -174,7 +168,7 @@ function itemEvents(events: AssembledEvent[]): ItemEvent[] { } function completedItem( - events: AssembledEvent[], + events: ThreadEvent[], type: T, ): Extract { const event = itemEvents(events).find( diff --git a/packages/plugin-sdk/src/__tests__/bundled-types.test.ts b/packages/plugin-sdk/src/__tests__/bundled-types.test.ts index e19d74af7e..73d47e472e 100644 --- a/packages/plugin-sdk/src/__tests__/bundled-types.test.ts +++ b/packages/plugin-sdk/src/__tests__/bundled-types.test.ts @@ -91,4 +91,33 @@ describe("bundled plugin SDK declarations", () => { ); expect(declarations[5]).toContain("interface ExperimentalHostEntryHarness"); }); + + it("names the canonical event vocabulary in the provider-bridge testing kit", async () => { + // A bridge's tests assert on what the assembler built; the types they + // narrow to are re-exported from @bb/domain and must arrive inlined, not + // as an import a plugin cannot resolve. + const testing = await readFile( + new URL( + "../../bundled-types/bb-plugin-sdk-provider-bridge-testing.d.ts", + import.meta.url, + ), + "utf8", + ); + expect(testing).not.toMatch(/from ['"]@bb\//u); + expect(testing).not.toMatch(/import\(['"]@bb\//u); + for (const name of [ + "ThreadEvent", + "ThreadEventItem", + "ThreadEventItemPresentation", + "ThreadEventDelegationItem", + "ThreadEventExtensionItem", + ]) { + expect(testing).toMatch( + new RegExp(`(?:type|interface) ${name}\\b`, "u"), + ); + expect(testing).toMatch( + new RegExp(`export type \\{[^}]*\\b${name}\\b[^}]*\\}`, "u"), + ); + } + }); }); diff --git a/packages/plugin-sdk/src/provider-bridge-testing.ts b/packages/plugin-sdk/src/provider-bridge-testing.ts index 06a3e3d4b9..0ba69a4f81 100644 --- a/packages/plugin-sdk/src/provider-bridge-testing.ts +++ b/packages/plugin-sdk/src/provider-bridge-testing.ts @@ -131,3 +131,26 @@ export type { BridgeRecordingDirection, BridgeRecordingEntry, } from "@bb/provider-bridge-protocol/bridge-kit"; + +// The canonical event vocabulary, by name. A bridge never constructs these +// (the assembler does), but a bridge's tests assert on what the assembler +// built — `ThreadEvent` is what every collector, replay and parity function +// here returns, and the item and presentation types are what an assertion +// narrows to. Re-exported from bb's domain package and inlined into the +// published declarations, like `PromptInput` on the root entry. +export type { + ThreadEvent, + ThreadEventBackgroundTaskItem, + ThreadEventDelegationItem, + ThreadEventExtensionItem, + ThreadEventFileReadItem, + ThreadEventItem, + ThreadEventItemPresentation, + ThreadEventItemPresentationIcon, + ThreadEventItemPresentationLabel, + ThreadEventItemPresentationTint, + ThreadEventPlanStepsItem, + ThreadEventSearchItem, + ThreadEventWebFetchItem, + ThreadEventWebSearchItem, +} from "@bb/domain";