@get-bb/plugin-sdk 0.4.24 → 0.4.28

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.
@@ -17,26 +17,6 @@ declare const threadEventItemPresentationLabelSchema: z.ZodObject<{
17
17
  pending: z.ZodString;
18
18
  }, z.core.$strip>;
19
19
  type ThreadEventItemPresentationLabel = z.infer<typeof threadEventItemPresentationLabelSchema>;
20
- /**
21
- * The row's leading icon, by name. Two vocabularies share the one field:
22
- *
23
- * - a host glyph (`{ glyph: "FileText" }`), the same names the plugin
24
- * branding and provider declaration icons use;
25
- * - a plugin-declared icon (`{ glyph: "echo-provider/receipt" }`), the
26
- * namespaced form `"<pluginId>/<name>"` that names an entry of the
27
- * plugin's manifest map `bb.branding.experimental_icons`
28
- * (`NAMESPACED_GLYPH_PATTERN` in plugin-icon.ts). The server rejects at
29
- * ingest a namespaced glyph that is not the emitting plugin's own declared
30
- * icon; clients resolve the name against the plugin inventory they hold
31
- * and draw the SVG tinted with `currentColor`.
32
- *
33
- * Both are names, never bytes or paths: the row persists the name and
34
- * follows the plugin's current map at render time. If the plugin is gone or
35
- * the name is unknown when the row renders, the icon is simply not found and
36
- * the per-kind fallback glyph draws instead — accepted, so a persisted row
37
- * never depends on a file that may have moved. The schema stays a plain
38
- * non-blank string on purpose: persisted rows must parse forever.
39
- */
40
20
  declare const threadEventItemPresentationIconSchema: z.ZodObject<{
41
21
  glyph: z.ZodString;
42
22
  }, z.core.$strip>;
@@ -114,15 +94,6 @@ declare const threadEventWebFetchItemSchema: z.ZodObject<{
114
94
  url: z.ZodString;
115
95
  }, z.core.$strip>;
116
96
  type ThreadEventWebFetchItem = z.infer<typeof threadEventWebFetchItemSchema>;
117
- /**
118
- * A file the agent read. The single most common generic tool in the
119
- * production corpus (Claude `Read`: 7,568 calls across 141 threads rendered
120
- * as an opaque `toolCall`), so it earns a core kind: clients show the path,
121
- * the permission matrix treats it as a read, and no tool-name table is
122
- * needed to recognise it. `cmd` carries the native shell form when the
123
- * provider read through a command (`cat`, `sed -n`) rather than a structured
124
- * tool, so the row can still show what actually ran.
125
- */
126
97
  declare const threadEventFileReadItemSchema: z.ZodObject<{
127
98
  cmd: z.ZodOptional<z.ZodString>;
128
99
  id: z.ZodString;
@@ -153,16 +124,6 @@ declare const threadEventFileReadItemSchema: z.ZodObject<{
153
124
  type: z.ZodLiteral<"fileRead">;
154
125
  }, z.core.$strip>;
155
126
  type ThreadEventFileReadItem = z.infer<typeof threadEventFileReadItemSchema>;
156
- /**
157
- * One kind for every exploration tool that is not a file read: grep, glob,
158
- * and directory listing, discriminated by `mode`. Claude `Grep` + `Glob` and
159
- * the shell `rg`/`ls`/`find` commands bridges already classify into
160
- * `command` activity intents all fold into it. `query` is the pattern: text
161
- * or a regex for `content`, a glob for `path`, and an optional filter for
162
- * `list` (empty when the whole directory is listed). `path` is the root the
163
- * search ran under when the provider named one. `cmd` carries the native
164
- * shell form when the provider searched through a command.
165
- */
166
127
  declare const threadEventSearchItemSchema: z.ZodObject<{
167
128
  cmd: z.ZodOptional<z.ZodString>;
168
129
  id: z.ZodString;
@@ -199,22 +160,6 @@ declare const threadEventSearchItemSchema: z.ZodObject<{
199
160
  type: z.ZodLiteral<"search">;
200
161
  }, z.core.$strip>;
201
162
  type ThreadEventSearchItem = z.infer<typeof threadEventSearchItemSchema>;
202
- /**
203
- * The agent delegated work to a child agent. One kind replaces the three
204
- * encodings in the production data — codex `spawnAgent`/`wait` tool calls,
205
- * the Claude `Agent` tool call with nested child turns, and backgrounded
206
- * `local_agent` background tasks — and the `thread/openWork` notification:
207
- * an open delegation IS open work.
208
- *
209
- * `childRef` is the provider-native id of the child (a codex agent id, a
210
- * Claude subagent id, a bb child thread id when the delegation became a bb
211
- * thread); child turns link back through their `parentToolCallId`.
212
- * `background: true` marks a delegation that outlives its spawning turn, in
213
- * which case its progress and terminal state ride the thread-scoped
214
- * `item/delegation/progress` and `item/delegation/completed` events exactly
215
- * as `backgroundTask` does; a foreground delegation settles through the
216
- * ordinary turn-scoped `item/completed`.
217
- */
218
163
  declare const threadEventDelegationItemSchema: z.ZodObject<{
219
164
  background: z.ZodBoolean;
220
165
  childRef: z.ZodString;
@@ -247,14 +192,6 @@ declare const threadEventDelegationItemSchema: z.ZodObject<{
247
192
  type: z.ZodLiteral<"delegation">;
248
193
  }, z.core.$strip>;
249
194
  type ThreadEventDelegationItem = z.infer<typeof threadEventDelegationItemSchema>;
250
- /**
251
- * A structured plan snapshot the agent maintains as an item: codex
252
- * `update_plan` (its `turn/plan/updated` notification reaches 295 threads in
253
- * the production corpus and the UI discards it today) and the Claude
254
- * `TaskCreate`/`TaskUpdate`/`TodoWrite` family. Each snapshot carries the
255
- * full step list; a later snapshot supersedes an earlier one. Distinct from
256
- * the `plan` item, which is the free-text plan-mode document.
257
- */
258
195
  declare const threadEventPlanStepsItemSchema: z.ZodObject<{
259
196
  explanation: z.ZodOptional<z.ZodString>;
260
197
  id: z.ZodString;
@@ -293,14 +230,6 @@ declare const threadEventPlanStepsItemSchema: z.ZodObject<{
293
230
  type: z.ZodLiteral<"planSteps">;
294
231
  }, z.core.$strip>;
295
232
  type ThreadEventPlanStepsItem = z.infer<typeof threadEventPlanStepsItemSchema>;
296
- /**
297
- * A plugin-defined item kind outside the core vocabulary
298
- * (`"<pluginId>/<name>"`, see provider-extension-kind.ts). The payload is
299
- * opaque JSON here; the server validates it against the owning plugin's
300
- * declared schema at ingest. `presentation` is REQUIRED — an
301
- * extension item has no core renderer to fall back on, so the declarative
302
- * base is the only thing every client can show.
303
- */
304
233
  declare const threadEventExtensionItemSchema: z.ZodObject<{
305
234
  id: z.ZodString;
306
235
  kind: z.ZodString & z.ZodType<`${string}/${string}`, string, z.core.$ZodTypeInternals<`${string}/${string}`, string>>;
@@ -331,13 +260,6 @@ declare const threadEventExtensionItemSchema: z.ZodObject<{
331
260
  type: z.ZodLiteral<"extension">;
332
261
  }, z.core.$strip>;
333
262
  type ThreadEventExtensionItem = z.infer<typeof threadEventExtensionItemSchema>;
334
- /**
335
- * A materialized provider background task. Dynamic workflows (taskType
336
- * "local_workflow"), backgrounded shell commands (taskType "local_bash"), and
337
- * backgrounded subagents (taskType "local_agent" / "local_subagent") become
338
- * items. The item id is derived from the provider task id and stays stable
339
- * across the started → progress* → completed lifecycle.
340
- */
341
263
  declare const threadEventBackgroundTaskItemSchema: z.ZodObject<{
342
264
  description: z.ZodString;
343
265
  error: z.ZodOptional<z.ZodString>;
@@ -982,7 +904,6 @@ declare const threadEventItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
982
904
  type: z.ZodLiteral<"extension">;
983
905
  }, z.core.$strip>], "type">;
984
906
  type ThreadEventItem = z.infer<typeof threadEventItemSchema>;
985
- /** All thread events — provider-originated or system-originated. */
986
907
  declare const threadEventSchema: z.ZodPipe<z.ZodUnknown, z.ZodUnion<readonly [z.ZodIntersection<z.ZodDiscriminatedUnion<[z.ZodObject<{
987
908
  threadId: z.ZodString;
988
909
  type: z.ZodLiteral<"thread/started">;
@@ -2950,6 +2871,7 @@ declare const threadEventSchema: z.ZodPipe<z.ZodUnknown, z.ZodUnion<readonly [z.
2950
2871
  turnId: z.ZodOptional<z.ZodString>;
2951
2872
  type: z.ZodLiteral<"system/manager/user_message">;
2952
2873
  }, z.core.$strip>, z.ZodObject<{
2874
+ cause: z.ZodOptional<z.ZodLiteral<"host-connection-lost">>;
2953
2875
  reason: z.ZodEnum<{
2954
2876
  "host-daemon-restarted": "host-daemon-restarted";
2955
2877
  "manual-stop": "manual-stop";
@@ -3394,12 +3316,6 @@ declare const promptInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
3394
3316
  }, z.core.$strip>], "type">;
3395
3317
  type PromptInput = z.infer<typeof promptInputSchema>;
3396
3318
 
3397
- /**
3398
- * The inclusive `[min, max]` range of `thread/delta` grammar versions a bridge
3399
- * speaks. Distinct from the JSON-RPC `protocolVersion`: the envelope can stay
3400
- * put while the delta vocabulary grows, and a bridge that speaks both v2 and
3401
- * v3 says so here instead of forcing a daemon bump.
3402
- */
3403
3319
  declare const bridgeGrammarVersionsSchema: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
3404
3320
  type BridgeGrammarVersions = z.infer<typeof bridgeGrammarVersionsSchema>;
3405
3321
 
@@ -4185,15 +4101,6 @@ declare const threadDeltaSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
4185
4101
  }, z.core.$strip>], "kind">;
4186
4102
  type ThreadDelta = z.infer<typeof threadDeltaSchema>;
4187
4103
 
4188
- /**
4189
- * The `thread/delta` grammar range this assembler speaks, reported to every
4190
- * bridge in the `initialize` params so the two sides negotiate a version
4191
- * (see `negotiateGrammarVersion` in the protocol). `[3, 3]`: the v2 dialects
4192
- * (`message.*`, `usage.turn`/`usage.exact`) are deleted, so a bridge whose
4193
- * range lacks 3 — including one that predates `grammarVersions` and reads as
4194
- * `[2, 2]` — is refused at the handshake with a legible error instead of
4195
- * connecting to an assembler that would drop its every stream.
4196
- */
4197
4104
  declare const ASSEMBLER_GRAMMAR_VERSIONS: BridgeGrammarVersions;
4198
4105
  interface DiffCumulativeTextArgs {
4199
4106
  nextText: string;
@@ -4205,35 +4112,10 @@ interface DiffCumulativeTextResult {
4205
4112
  reset: boolean;
4206
4113
  }
4207
4114
  interface CreateDeltaAssemblerOptions {
4208
- /** Provider id stamped onto provider/unhandled events. */
4209
4115
  providerId: string;
4210
- /**
4211
- * Entropy prefix for minted turn/item ids. Defaults to fresh per-assembler
4212
- * entropy so ids never collide across assembler (process) restarts; tests
4213
- * inject a fixed prefix for determinism.
4214
- */
4215
4116
  entropyPrefix?: string;
4216
- /**
4217
- * Minimum gap between emitted `item.progress` events per item key
4218
- * (`flush: true` bypasses it; `item.close` always emits). 500ms default —
4219
- * the cadence the claude bridge hand-rolled for background-task snapshots,
4220
- * now the central policy for every provider's progress stream.
4221
- */
4222
4117
  progressThrottleMs?: number;
4223
- /**
4224
- * Coalescing window for streamed-text events (assistant/reasoning/plan
4225
- * deltas and command/fileChange output deltas) per stream. Within the
4226
- * window consecutive deltas concatenate into one emitted event of the same
4227
- * type; the buffer flushes trailing-edge with no timers — on the thread's
4228
- * next traffic once the window elapsed, on stream close, and before ANY
4229
- * non-batchable event for the thread (the ordering barrier: coalescing
4230
- * never reorders text relative to item opens/closes, turn events, errors,
4231
- * or other streams' flushes). The first delta of a fresh stream always
4232
- * emits immediately, keeping time-to-first-token unchanged. 100ms default;
4233
- * 0 disables batching (one event per delta).
4234
- */
4235
4118
  textDeltaFlushMs?: number;
4236
- /** Clock override for tests. */
4237
4119
  now?: () => number;
4238
4120
  }
4239
4121
  interface AssembleDeltasArgs {
@@ -4242,87 +4124,37 @@ interface AssembleDeltasArgs {
4242
4124
  }
4243
4125
  interface DeltaAssembler {
4244
4126
  assemble(args: AssembleDeltasArgs): ThreadEvent[];
4245
- /** bb item id minted for a provider item id (command-plane lookup). */
4246
4127
  getBbItemId(threadId: string, providerItemId: string): string | undefined;
4247
- /** Provider item id behind a bb item id (reverse command-plane lookup). */
4248
4128
  getProviderItemId(threadId: string, bbItemId: string): string | undefined;
4249
- /** bb turn id minted for a vouched provider turn id. */
4250
4129
  getBbTurnId(threadId: string, providerTurnId: string): string | undefined;
4251
- /** Provider turn id behind a bb turn id (steer/interrupt reverse lookup). */
4252
4130
  getProviderTurnId(threadId: string, bbTurnId: string): string | undefined;
4253
4131
  getOpenTurnId(threadId: string): string | undefined;
4254
4132
  }
4255
4133
  declare function createDeltaAssembler(options: CreateDeltaAssemblerOptions): DeltaAssembler;
4256
4134
 
4257
- /**
4258
- * Test-side view of the runtime's delta assembly: bridge tests capture raw
4259
- * JSON-RPC output, and bridges emit `thread/delta` notifications rather than
4260
- * finished `ThreadEvent`s. These helpers run captured notifications through
4261
- * the real delta assembler — the exact translation the bridge protocol
4262
- * adapter performs — so assertions keep working against canonical
4263
- * `ThreadEvent`s.
4264
- */
4265
-
4266
4135
  interface CapturedBridgeNotification {
4267
4136
  method?: string;
4268
4137
  params?: unknown;
4269
4138
  }
4270
4139
  interface BridgeDeltaEventCollector {
4271
4140
  assembler: DeltaAssembler;
4272
- /** Canonical events for one captured notification (empty for non-deltas). */
4273
4141
  assembleMessage(message: CapturedBridgeNotification): ThreadEvent[];
4274
4142
  }
4275
4143
  declare function createBridgeDeltaEventCollector(providerId?: string): BridgeDeltaEventCollector;
4276
- /**
4277
- * All canonical events an ordered capture of bridge notifications assembles
4278
- * to. Builds a fresh assembler per call, so feed it the full capture (not an
4279
- * incremental slice) for deterministic ids.
4280
- */
4281
4144
  declare function assembleCapturedThreadEvents(messages: readonly CapturedBridgeNotification[], providerId?: string): ThreadEvent[];
4282
- /**
4283
- * Removed (SDK 0.4.16): the conformance kit assembles `thread/delta` itself.
4284
- * A transport hands `runBridgeConformance` the raw captured messages
4285
- * (`CapturedBridgeJsonRpcOutput.takeMessages`) and the run names its
4286
- * `providerId`; this stub stays one release so a suite written against the
4287
- * old transport shape fails with the replacement named, not a missing export.
4288
- */
4289
4145
  declare function toConformanceMessages(): never;
4290
4146
 
4291
- /**
4292
- * Transport abstraction the conformance kit drives. Black-box at the message
4293
- * level: lines in, JSON-RPC messages out. Two expected implementations — an
4294
- * in-process bridge (`send` = the bridge's exported line handler,
4295
- * `takeMessages` = the captured output's drain) and a spawned bridge binary
4296
- * (stdin write + stdout readline). The kit never sees which: it assembles
4297
- * the bridge's `thread/delta` notifications itself, through the runtime's
4298
- * real delta assembler, so the transport hands over raw wire messages only.
4299
- */
4300
4147
  interface BridgeConformanceTransport {
4301
- /** Deliver one raw line to the bridge. */
4302
4148
  send(line: string): void;
4303
- /**
4304
- * Every JSON-RPC message the bridge emitted since the last call, in order:
4305
- * responses, notifications (`thread/delta` included) and bridge-initiated
4306
- * requests, as parsed JSON.
4307
- */
4308
4149
  takeMessages(): unknown[];
4309
4150
  close?(): Promise<void> | void;
4310
4151
  }
4311
- /**
4312
- * Retired. The kit once read its canonical events from a notification the
4313
- * transport assembled under this method; it now assembles `thread/delta`
4314
- * itself and reads nothing under this name. Kept because SDK 0.4.x published
4315
- * it from `@get-bb/plugin-sdk/provider-bridge/testing`; removed at the next
4316
- * major version.
4317
- */
4318
4152
  declare const CONFORMANCE_ASSEMBLED_EVENT_METHOD = "conformance/assembledEvent";
4319
4153
  type ConformanceStatus = "fail" | "pass" | "skipped";
4320
4154
  interface ConformanceCheckResult {
4321
- /** Stable rule id, e.g. "rpc/unknown-method". */
4322
4155
  id: string;
4323
4156
  title: string;
4324
4157
  status: ConformanceStatus;
4325
- /** Failure or skip explanation; empty on pass. */
4326
4158
  detail: string;
4327
4159
  }
4328
4160
  interface ConformanceReport {
@@ -4331,119 +4163,35 @@ interface ConformanceReport {
4331
4163
  }
4332
4164
 
4333
4165
  interface ConformanceSessionFixture {
4334
- /** Workspace directory for the session under test. */
4335
4166
  cwd: string;
4336
- /** Prompt expected to elicit at least one assistant-message item. */
4337
4167
  promptInput: PromptInput[];
4338
- /**
4339
- * A prompt this provider accepts and completes locally, without producing
4340
- * any of the activity that opens a bb turn — Claude Code's `/clear` is the
4341
- * canonical example (#1431). Opting in enables
4342
- * `turn/settles-without-activity`.
4343
- *
4344
- * The kit cannot elicit this shape generically: only the bridge knows what
4345
- * its provider handles as zero work. A fixture that omits it produces no
4346
- * result for that rule rather than a skip, so bridges that have not opted in
4347
- * keep a fully green report.
4348
- */
4349
4168
  zeroWorkPromptInput?: PromptInput[];
4350
- /**
4351
- * A prompt that opens a turn and never settles it on its own, so the kit
4352
- * can interrupt it. Opting in enables `session/threads-independent` and
4353
- * `stop/interrupt-settles-before-result` (the kit names the turn to the
4354
- * bridge through its own assembler's reverse map). A fixture that omits it
4355
- * produces no result for those rules.
4356
- */
4357
4169
  interruptiblePromptInput?: PromptInput[];
4358
- /** Execution options for the session; the kit defaults to full mode. */
4359
4170
  options?: Record<string, unknown>;
4360
- /**
4361
- * The plugin's declared icons (`bb.branding.experimental_icons`): its
4362
- * plugin id and the declared names. Opting in enables
4363
- * `presentation/icon-namespaced-declared`, which fails when any item's
4364
- * `presentation.icon.glyph` is a namespaced glyph (`"<pluginId>/<name>"`)
4365
- * that names another plugin or an undeclared name — what the server
4366
- * would refuse at ingest with `provider/unhandled`. A `server: "bb"` tool
4367
- * row is not inspected: its presentation came from the plugin that
4368
- * registered the tool, and the server checks it against that plugin. A
4369
- * fixture that omits `icons` produces no result for the rule, like the
4370
- * other opt-in rules, so a bridge whose plugin declares no icons keeps a
4371
- * fully green report.
4372
- */
4373
4171
  icons?: {
4374
4172
  pluginId: string;
4375
4173
  names: readonly string[];
4376
4174
  };
4377
4175
  }
4378
4176
 
4379
- /**
4380
- * Recorded-traffic conformance.
4381
- *
4382
- * The scripted scenarios in `scenarios.ts` drive a bridge with a fake
4383
- * provider the kit authors wrote. This set drives it with what the provider
4384
- * CLI really emitted: a committed recording (`recordings/<provider>/<cell>`),
4385
- * replayed through the bridge by `testing/parity.ts`, checked with the same
4386
- * grammar rules. A bridge passes when the replay reproduces a complete,
4387
- * schema-valid, grammar-clean session for every recorded cell — so a
4388
- * translation change that only the real dialect exercises fails conformance,
4389
- * not just a golden.
4390
- *
4391
- * Pure over a replay's output, like `checkItemOpensBeforeDelta`: the caller
4392
- * owns the bridge transport and the replay; this module owns the verdicts.
4393
- */
4394
-
4395
- /** The cells every bridge is expected to reproduce (the live-QA matrix core). */
4396
4177
  declare const RECORDED_CONFORMANCE_CELLS: readonly ["turn-tools", "steer", "stop-interrupt", "approval-allow", "approval-deny", "user-question", "resume", "fork"];
4397
4178
  type RecordedConformanceCell = (typeof RECORDED_CONFORMANCE_CELLS)[number];
4398
4179
  interface RecordedCellReplay {
4399
4180
  provider: string;
4400
4181
  cell: string;
4401
- /** Events the replayed bridge output assembled to, in order. */
4402
4182
  events: readonly ThreadEvent[];
4403
- /**
4404
- * Events the recording's own bridge output assembled to: the turn count the
4405
- * replay must reach. A provider that legitimately refused a cell (an ACP
4406
- * agent without `session/fork`) recorded no turns, and the replay must
4407
- * reproduce that rather than invent one.
4408
- */
4409
4183
  recordedEvents: readonly ThreadEvent[];
4410
- /** Requests the harness had to answer for the bridge, or gates that timed out. */
4411
4184
  stalls: readonly string[];
4412
4185
  }
4413
- /**
4414
- * The verdicts for one replayed cell. Rule ids are `recorded/<cell>/<rule>`
4415
- * so a report lists every cell and a regression names the one it broke.
4416
- */
4417
4186
  declare function checkRecordedCellReplay(replay: RecordedCellReplay): ConformanceCheckResult[];
4418
4187
 
4419
4188
  interface RunBridgeConformanceOptions {
4420
4189
  transport: BridgeConformanceTransport;
4421
4190
  session: ConformanceSessionFixture;
4422
- /**
4423
- * The provider id the bridge's plugin registers. The kit assembles the
4424
- * bridge's `thread/delta` stream through the runtime's real delta
4425
- * assembler, and the canonical events it builds carry this id, as the
4426
- * runtime's would.
4427
- */
4428
4191
  providerId: string;
4429
- /** Per-wait timeout. Conformant bridges answer fast; keep this tight. */
4430
4192
  timeoutMs?: number;
4431
4193
  }
4432
- /**
4433
- * Drive one bridge through the conformance scenarios: JSON-RPC hygiene, the
4434
- * initialize handshake, then a shared session lifecycle (start → turn →
4435
- * grammar checks → release stop → resume with its identity → id-uniqueness
4436
- * → fork with its identity when the handshake declares fork → the opt-in
4437
- * rules), released at the end the way the runtime releases a thread it
4438
- * detaches. One transport for the whole run, mirroring a real bridge
4439
- * lifetime.
4440
- *
4441
- * Against a conformant bridge every result passes. Against a bridge that is
4442
- * not yet protocol-pure, the failures ARE the migration work list — run it
4443
- * before migrating and pin the report, then make it shrink.
4444
- */
4445
4194
  declare function runBridgeConformance(options: RunBridgeConformanceOptions): Promise<ConformanceReport>;
4446
- /** Compact single-line-per-rule rendering for test snapshots and logs. */
4447
4195
  declare function formatConformanceReport(report: ConformanceReport): string;
4448
4196
 
4449
4197
  type BridgeJsonRpcId = string | number;
@@ -4465,16 +4213,11 @@ interface BridgeJsonRpcOutputMessage {
4465
4213
  }
4466
4214
  interface CapturedBridgeJsonRpcOutput {
4467
4215
  messages: BridgeJsonRpcOutputMessage[];
4468
- /**
4469
- * Every message since the last call: the conformance transport's drain
4470
- * (`{ send: handleLine, takeMessages: output.takeMessages }`).
4471
- */
4472
4216
  takeMessages(): BridgeJsonRpcOutputMessage[];
4473
4217
  restore(): void;
4474
4218
  }
4475
4219
  interface BridgeJsonRpcTestHarness {
4476
4220
  messages: BridgeJsonRpcOutputMessage[];
4477
- /** Every message since the last call: the conformance transport's drain. */
4478
4221
  takeMessages(): BridgeJsonRpcOutputMessage[];
4479
4222
  flushWork(): Promise<void>;
4480
4223
  hasResponse(id: BridgeJsonRpcId): boolean;
@@ -4482,64 +4225,22 @@ interface BridgeJsonRpcTestHarness {
4482
4225
  sendRequest(id: BridgeJsonRpcId, method: string, params: BridgeJsonRpcObject): void;
4483
4226
  waitForResponse(id: BridgeJsonRpcId): Promise<BridgeJsonRpcOutputMessage>;
4484
4227
  }
4485
- /**
4486
- * Capture everything a bridge writes to stdout as parsed JSON-RPC messages.
4487
- * Patches `process.stdout.write` directly (no test-framework spy), so the
4488
- * kit runs under any runner; `restore()` puts the original writer back.
4489
- */
4490
4228
  declare function captureBridgeJsonRpcOutput(): CapturedBridgeJsonRpcOutput;
4491
4229
  declare function createBridgeJsonRpcTestHarness(handleLine: BridgeJsonRpcLineHandler): BridgeJsonRpcTestHarness;
4492
4230
 
4493
- /**
4494
- * Dual-path calibration support.
4495
- *
4496
- * A calibration replays one scripted provider session through both the legacy
4497
- * adapter and the canonical bridge, then diffs the two ThreadEvent streams.
4498
- * Anything the diff reports is either a deliberate, documented protocol
4499
- * difference (the bridge synthesizes item/started, announces thread/identity,
4500
- * …) or a regression — there is no third category, which is what makes these
4501
- * suites a graduation gate.
4502
- *
4503
- * Ids legitimately differ between the paths: the legacy adapter numbers from
4504
- * its process-lifetime translator ("turn-1", "claude-assistant-2"), while a
4505
- * canonical session mints per-session entropy ("bt3f9a2b1c-1-…") so ids stay
4506
- * unique across resumes (#1224). Normalization interns them by first-seen
4507
- * order instead of matching either scheme, so a stream that *reused* an id
4508
- * still diffs.
4509
- */
4510
4231
  interface NormalizeCalibrationEventsOptions {
4511
- /**
4512
- * Ids the provider itself owns (tool call ids, checkpoints) are identical on
4513
- * both paths and are left alone. Anything reaching the intern table is a
4514
- * translator- or bridge-minted id.
4515
- */
4516
4232
  internedIdFields?: readonly string[];
4517
4233
  }
4518
- /**
4519
- * Normalize one path's stream. Each stream gets its own interner, so the token
4520
- * a given id receives depends only on the order ids first appear — identical
4521
- * across paths when the streams agree, different the moment they do not.
4522
- */
4523
4234
  declare function normalizeCalibrationEvents(events: readonly ThreadEvent[], options?: NormalizeCalibrationEventsOptions): unknown[];
4524
- /** Compact `type` (+ item type) rendering for asserting a known-divergence list. */
4525
4235
  declare function describeCalibrationEvents(events: readonly unknown[]): string[];
4526
4236
 
4527
4237
  declare const BRIDGE_RECORDING_DIRECTIONS: readonly ["runtime→bridge", "bridge→runtime", "provider→bridge", "bridge→provider"];
4528
4238
  type BridgeRecordingDirection = (typeof BRIDGE_RECORDING_DIRECTIONS)[number];
4529
4239
  interface BridgeRecordingEntry {
4530
- /** Wall-clock milliseconds when the line crossed. */
4531
4240
  ts: number;
4532
- /**
4533
- * The recorder's start time, identifying the bridge process that wrote the
4534
- * entry. A thread can span several bridge processes (the runtime restarts a
4535
- * bridge, or releases and later resumes the thread), each appending to the
4536
- * same files with its own `seq`; `(run, seq)` orders entries exactly.
4537
- */
4538
4241
  run: number;
4539
- /** Process-wide monotonic counter across all four lanes. */
4540
4242
  seq: number;
4541
4243
  dir: BridgeRecordingDirection;
4542
- /** The raw line, without its terminator. */
4543
4244
  line: string;
4544
4245
  }
4545
4246
 
@@ -4558,22 +4259,9 @@ interface BridgeRecordingManifest {
4558
4259
  interface BridgeRecording {
4559
4260
  dir: string;
4560
4261
  manifest: BridgeRecordingManifest | null;
4561
- /** Every lane merged back into wire order: by bridge process, then seq. */
4562
4262
  entries: BridgeRecordingEntry[];
4563
4263
  }
4564
- /**
4565
- * The file a bridge change re-records its side of the wire into
4566
- * (`pnpm rerecord`): the `bridge→runtime` lane as THIS checkout's bridge
4567
- * emits it for the recording's provider and runtime lanes. The recorded lane
4568
- * itself is never rewritten — it is the recording, and a pre-migration
4569
- * checkout paces its replay from it — so the current expectation lives
4570
- * beside it. Absent until a bridge change first needs one.
4571
- */
4572
4264
  declare const CURRENT_BRIDGE_LANE_FILE = "bridge\u2192runtime.current.ndjson";
4573
- /**
4574
- * The recording with its `bridge→runtime` lane replaced by the current
4575
- * expectation when one exists: what the self-suite pins and compares.
4576
- */
4577
4265
  declare function withCurrentBridgeLane(recording: BridgeRecording): BridgeRecording;
4578
4266
  declare function readBridgeRecording(dir: string): BridgeRecording;
4579
4267
  interface RecordedCell {
@@ -4581,13 +4269,8 @@ interface RecordedCell {
4581
4269
  cell: string;
4582
4270
  dir: string;
4583
4271
  }
4584
- /**
4585
- * Every `<provider>/<cell>` directory under a recordings root that holds at
4586
- * least one lane, sorted for stable iteration.
4587
- */
4588
4272
  declare function listRecordedCells(root: string): RecordedCell[];
4589
4273
 
4590
- /** One stateful assembler: `thread/delta` notifications in, events out. */
4591
4274
  interface ParityAssembler {
4592
4275
  assembleMessage(message: {
4593
4276
  method?: string;
@@ -4595,140 +4278,55 @@ interface ParityAssembler {
4595
4278
  }): ThreadEvent[];
4596
4279
  }
4597
4280
  type CreateParityAssembler = (providerId: string) => ParityAssembler;
4598
- /** Project canonical events into timeline rows (the server's projection). */
4599
4281
  type ParityRowProjector = (args: {
4600
4282
  events: readonly ThreadEvent[];
4601
4283
  providerId: string;
4602
4284
  }) => unknown[];
4603
- /** A bridge process, ready to spawn: the bootstrap, the module, its scope. */
4604
4285
  interface ProviderBridgeLaunch {
4605
4286
  command: string;
4606
4287
  args: string[];
4607
4288
  cwd: string;
4608
- /** Added to the harness's own environment for the bridge process. */
4609
4289
  env: Record<string, string>;
4610
4290
  }
4611
4291
  interface ResolveProviderBridgeLaunchOptions {
4612
- /**
4613
- * The bridge module: the file whose `experimental_providerBridge` export the
4614
- * bootstrap runs. Absolute; a built artifact (`host.mjs`) or, with a
4615
- * TypeScript loader among `nodeArgs`, the source file.
4616
- */
4617
4292
  modulePath: string;
4618
- /** The plugin the bridge belongs to (its data and temp directories). */
4619
4293
  pluginId: string;
4620
- /** Working directory of the bridge process; defaults to the caller's. */
4621
4294
  cwd?: string;
4622
- /**
4623
- * The plugin data directory the bootstrap hands the bridge; defaults to a
4624
- * fresh temp directory per launch.
4625
- */
4626
4295
  dataDir?: string;
4627
- /**
4628
- * The provider-bridge bootstrap (`bridge-worker-entry`) that runs the
4629
- * module; defaults to the kit's own — the source entry in a bb checkout,
4630
- * the bundled one in the published SDK.
4631
- */
4632
4296
  bootstrapPath?: string;
4633
- /**
4634
- * Node flags before the bootstrap. Defaults: in a bb checkout (source
4635
- * bootstrap) `--conditions=source` plus the tsx loader; otherwise the tsx
4636
- * loader for a TypeScript module and nothing for a built one.
4637
- */
4638
4297
  nodeArgs?: string[];
4639
4298
  }
4640
- /**
4641
- * The process that runs one bridge module through the bootstrap — exactly the
4642
- * shape the runtime spawns, so a replayed bridge sees the argv, stdin framing
4643
- * and signal handling it gets in production.
4644
- */
4645
4299
  declare function resolveProviderBridgeLaunch(options: ResolveProviderBridgeLaunchOptions): ProviderBridgeLaunch;
4646
4300
  type ReplayDialect = "claude-cli" | "json-rpc" | "pi-rpc";
4647
- /**
4648
- * How a provider's bridge is pointed at the replay child. Codex reads its
4649
- * app-server command from env, Claude its CLI path from env, pi its RPC
4650
- * command from env (`pi-rpc`: JSON lines plus the extension channel on fds
4651
- * 3/4), and an ACP bridge its agent command from the launch spec inside
4652
- * `thread/start`. A bridge with no provider child (the echo example) needs no
4653
- * profile at all.
4654
- */
4655
4301
  interface ReplayProviderProfile {
4656
- /** The protocol the replay child speaks on its pipe. */
4657
4302
  dialect: ReplayDialect;
4658
- /** Environment the bridge reads the child's command from. */
4659
4303
  env(args: {
4660
4304
  replayCommand: string[];
4661
4305
  wrapperPath: string;
4662
4306
  stateDir: string;
4663
4307
  }): Record<string, string>;
4664
- /** Rewrite a recorded runtime request that carries the child's command. */
4665
4308
  rewriteRuntimeLine?(line: string, args: {
4666
4309
  replayCommand: string[];
4667
4310
  }): string;
4668
- /**
4669
- * Provider state a bridge reads outside its provider pipe, seeded before
4670
- * the replay starts (the Claude SDK forks by copying the source session's
4671
- * transcript from disk).
4672
- */
4673
4311
  prepareState?(args: {
4674
4312
  recording: BridgeRecording;
4675
4313
  stateDir: string;
4676
4314
  workspaceDir: string;
4677
4315
  }): void;
4678
4316
  }
4679
- /** A bridge that spawns no provider, or one whose child command is fixed. */
4680
4317
  declare const DEFAULT_REPLAY_PROFILE: ReplayProviderProfile;
4681
4318
  interface ReplayRecordingOptions {
4682
4319
  recordingDir: string;
4683
- /** The provider the recording belongs to; keys the assembler's ids. */
4684
4320
  providerId: string;
4685
- /** The bridge process to replay through (see `resolveProviderBridgeLaunch`). */
4686
4321
  bridge: ProviderBridgeLaunch;
4687
- /** How the bridge reaches the replay child; `DEFAULT_REPLAY_PROFILE` when omitted. */
4688
4322
  profile?: ReplayProviderProfile;
4689
4323
  createAssembler: CreateParityAssembler;
4690
- /**
4691
- * The assembler that plans the replay's gates from the recorded
4692
- * `bridge→runtime` lane; defaults to `createAssembler`. A re-recording run
4693
- * on a checkout whose grammar no longer accepts the whole recorded lane
4694
- * plans with the recording-time checkout's assembler instead.
4695
- */
4696
4324
  createPlanAssembler?: CreateParityAssembler;
4697
- /**
4698
- * Plan the replay's gates from the cell's current bridge lane
4699
- * (`bridge→runtime.current.ndjson`, see `withCurrentBridgeLane`) when one
4700
- * exists, instead of the recorded lane. The leg whose bridge wrote that
4701
- * lane parses all of it; the recording-time leg parses the recorded lane.
4702
- */
4703
4325
  planFromCurrentLane?: boolean;
4704
- /** Per-wait timeout for a gate or a response. */
4705
4326
  timeoutMs?: number;
4706
- /**
4707
- * The quiet period after which a request is sent even though the bridge
4708
- * has emitted fewer lines than the recording had before it — a divergent
4709
- * bridge pays this once per request instead of stalling. Only a plan from
4710
- * the recorded lane can be short for that reason: a plan from the current
4711
- * lane (`planFromCurrentLane`) was written by this very bridge, so a
4712
- * shortfall there is latency, never divergence, and the request waits for
4713
- * its events up to `timeoutMs` instead — a starved bridge (a loaded CI
4714
- * runner) still has provider lines to read, and a request sent on quiet
4715
- * alone lands before them, at a point the recording never had.
4716
- */
4717
4327
  orderTimeoutMs?: number;
4718
- /**
4719
- * Quiet period after the last bridge output before the replay is closed.
4720
- * An exact current-lane replay first waits for every planned event.
4721
- */
4722
4328
  settleMs?: number;
4723
- /**
4724
- * Quiet period a request waits for once the gates are met. The replay child
4725
- * plays every provider line before the request's cursor point a couple of
4726
- * milliseconds apart, so a short silence means the bridge has emitted all
4727
- * that the pre-request stream produces; without it a request the bridge
4728
- * acknowledges at once (a steer) lands at a load-dependent point.
4729
- */
4730
4329
  drainMs?: number;
4731
- /** Mirror the bridge's stderr (and the replay child's logs) here. */
4732
4330
  onStderr?: (text: string) => void;
4733
4331
  }
4734
4332
  interface ParityGrammarViolation {
@@ -4739,39 +4337,21 @@ interface ParityGrammarViolation {
4739
4337
  interface ParityRun {
4740
4338
  providerId: string;
4741
4339
  recordingDir: string;
4742
- /** Raw `bridge→runtime` lines, in order. */
4743
4340
  lines: string[];
4744
- /** When each line arrived, ms since the replay started (diagnostics). */
4745
4341
  lineTimes: number[];
4746
- /**
4747
- * For each line, the recorded `runtime→bridge` entry written last before
4748
- * it arrived (null before any was sent) — where the line sits in the
4749
- * recording's wire order, for a lane re-recorded through this bridge.
4750
- */
4751
4342
  lineAfter: Array<{
4752
4343
  run: number;
4753
4344
  seq: number;
4754
4345
  ts: number;
4755
4346
  } | null>;
4756
- /** Assembled events, minus the ones the grammar dropped (as the runtime does). */
4757
4347
  events: ThreadEvent[];
4758
4348
  grammarViolations: ParityGrammarViolation[];
4759
- /** Gates that timed out or requests that were never answered. */
4760
4349
  stalls: string[];
4761
4350
  stderr: string;
4762
4351
  exitCode: number | null;
4763
4352
  }
4764
- /** The id of the harness's own `initialize` request; never part of a recording. */
4765
4353
  declare const PARITY_INITIALIZE_ID = "parity-initialize";
4766
- /**
4767
- * Replay one recording through one bridge. Resolves when the bridge exits
4768
- * after the last recorded runtime line has been sent and answered.
4769
- */
4770
4354
  declare function replayRecording(options: ReplayRecordingOptions): Promise<ParityRun>;
4771
- /**
4772
- * The events the recorded `bridge→runtime` lane assembles to, without any
4773
- * bridge in the loop: the recording's own view of what the bridge emitted.
4774
- */
4775
4355
  declare function assembleRecordedEvents(recording: BridgeRecording, createAssembler: CreateParityAssembler, providerId: string): {
4776
4356
  events: ThreadEvent[];
4777
4357
  grammarViolations: ParityGrammarViolation[];
@@ -4781,7 +4361,6 @@ interface ParityAllowlistEntry {
4781
4361
  provider: string | "*";
4782
4362
  cell: string | "*";
4783
4363
  layer: "events" | "rows";
4784
- /** A JSON pointer over the normalized list, with `*` and `**` wildcards. */
4785
4364
  path: string;
4786
4365
  pr: string;
4787
4366
  reason: string;
@@ -4795,16 +4374,13 @@ interface ParityComparison {
4795
4374
  cell: string;
4796
4375
  events: ParityLayerDiff;
4797
4376
  rows: ParityLayerDiff;
4798
- /** Grammar drops, compared as `rule:eventType` multisets. */
4799
4377
  grammar: ParityLayerDiff;
4800
- /** Allowlist entries that matched this cell but masked nothing. */
4801
4378
  staleAllowlist: ParityAllowlistEntry[];
4802
4379
  passed: boolean;
4803
4380
  }
4804
4381
  interface ParityInputs {
4805
4382
  events: readonly ThreadEvent[];
4806
4383
  rows: readonly unknown[];
4807
- /** Events the grammar dropped; a regression when the lists differ. */
4808
4384
  grammarViolations?: readonly ParityGrammarViolation[];
4809
4385
  }
4810
4386
  declare function compareParity(oldRun: ParityInputs, newRun: ParityInputs, allowlist: readonly ParityAllowlistEntry[], scope: {
@@ -4812,13 +4388,9 @@ declare function compareParity(oldRun: ParityInputs, newRun: ParityInputs, allow
4812
4388
  cell: string;
4813
4389
  }): ParityComparison;
4814
4390
  interface ReplayRecordedCellsOptions {
4815
- /** The `<provider>/<cell>` tree to read (see `listRecordedCells`). */
4816
4391
  recordingsRoot: string;
4817
- /** Which recorded providers this bridge serves (`acp` serves `acp-*`). */
4818
4392
  servesProvider: (providerId: string) => boolean;
4819
- /** Cell names to replay; defaults to every cell of those providers. */
4820
4393
  cells?: readonly string[];
4821
- /** The bridge process and replay profile for one cell's provider. */
4822
4394
  bridge: (cell: RecordedCell) => {
4823
4395
  launch: ProviderBridgeLaunch;
4824
4396
  profile?: ReplayProviderProfile;
@@ -4830,22 +4402,11 @@ interface ReplayRecordedCellsOptions {
4830
4402
 
4831
4403
  type RerecordCurrentBridgeLaneOptions = Omit<ReplayRecordingOptions, "planFromCurrentLane">;
4832
4404
  interface RerecordCurrentBridgeLaneResult {
4833
- /** The file written, or null when the replay stalled and nothing was. */
4834
4405
  file: string | null;
4835
- /** Lines in the new lane (the harness's own handshake excluded). */
4836
4406
  lines: number;
4837
- /** Events the replay assembled. */
4838
4407
  events: number;
4839
- /** A stalled replay leaves the current lane untouched. */
4840
4408
  stalls: string[];
4841
4409
  }
4842
- /**
4843
- * Replay one recording through a bridge and write what the bridge emitted
4844
- * as the recording's current bridge lane. The replay plans its gates from
4845
- * the recorded lane (or `createPlanAssembler`'s view of it), never from a
4846
- * previous current lane: the new lane must reproduce the recorded session,
4847
- * not the last re-recording of it.
4848
- */
4849
4410
  declare function rerecordCurrentBridgeLane(options: RerecordCurrentBridgeLaneOptions): Promise<RerecordCurrentBridgeLaneResult>;
4850
4411
 
4851
4412
  export { ASSEMBLER_GRAMMAR_VERSIONS, CONFORMANCE_ASSEMBLED_EVENT_METHOD, CURRENT_BRIDGE_LANE_FILE, DEFAULT_REPLAY_PROFILE, PARITY_INITIALIZE_ID, RECORDED_CONFORMANCE_CELLS, assembleCapturedThreadEvents as experimental_assembleCapturedThreadEvents, assembleRecordedEvents as experimental_assembleRecordedEvents, captureBridgeJsonRpcOutput as experimental_captureBridgeJsonRpcOutput, checkRecordedCellReplay as experimental_checkRecordedCellReplay, compareParity as experimental_compareParity, createBridgeDeltaEventCollector as experimental_createBridgeDeltaEventCollector, createBridgeJsonRpcTestHarness as experimental_createBridgeJsonRpcTestHarness, createDeltaAssembler as experimental_createDeltaAssembler, describeCalibrationEvents as experimental_describeCalibrationEvents, formatConformanceReport as experimental_formatConformanceReport, listRecordedCells as experimental_listRecordedCells, normalizeCalibrationEvents as experimental_normalizeCalibrationEvents, readBridgeRecording as experimental_readBridgeRecording, replayRecording as experimental_replayRecording, rerecordCurrentBridgeLane as experimental_rerecordCurrentBridgeLane, resolveProviderBridgeLaunch as experimental_resolveProviderBridgeLaunch, runBridgeConformance as experimental_runBridgeConformance, toConformanceMessages as experimental_toConformanceMessages, withCurrentBridgeLane as experimental_withCurrentBridgeLane };