From f0453aa8c4748fbf4297bebd64f14d031e05b537 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Tue, 1 Sep 2026 23:27:01 +0000 Subject: [PATCH] fix(dev): keep the optional runtime peer out of emitted declarations and disambiguate the document stage locator --- .changeset/main-ci-declaration-fix.md | 5 ++++ .../agent-bundle/src/dev/runtime-routes.ts | 27 ++++++++++--------- .../agent-bundle/tests/runtime-routes.test.ts | 15 +++++------ .../workbench/tests/runtime-inspector.test.ts | 4 +-- 4 files changed, 27 insertions(+), 24 deletions(-) create mode 100644 .changeset/main-ci-declaration-fix.md diff --git a/.changeset/main-ci-declaration-fix.md b/.changeset/main-ci-declaration-fix.md new file mode 100644 index 000000000..b526e6a21 --- /dev/null +++ b/.changeset/main-ci-declaration-fix.md @@ -0,0 +1,5 @@ +--- +"agent-bundle": patch +--- + +Keep the optional `@agent-bundle/runtime` peer out of the dev server's emitted declarations: the Agent Document route now consumes the peer through an opaque structural loader, so consumers without the optional peer compile against the packed root types again. diff --git a/packages/agent-bundle/src/dev/runtime-routes.ts b/packages/agent-bundle/src/dev/runtime-routes.ts index c4958dfd7..658e77311 100644 --- a/packages/agent-bundle/src/dev/runtime-routes.ts +++ b/packages/agent-bundle/src/dev/runtime-routes.ts @@ -1,7 +1,5 @@ import type { IncomingMessage, ServerResponse } from 'node:http'; -import type { AgentRenderEvent, AgentRenderLimits } from '@agent-bundle/runtime'; - import { DevRuntimeGenerationConflictError, DevRuntimeUnavailableError, @@ -33,12 +31,18 @@ type Route = | Readonly<{ readonly id: string; readonly kind: 'run' | 'document' | 'flight' | 'replay' }> | Readonly<{ readonly generation: string; readonly kind: 'asset'; readonly path: readonly string[]; readonly surfaceId: string }>; +/** + * Structural view of the optional `@agent-bundle/runtime` peer. The peer's own + * types must never appear here: this interface reaches the emitted root + * declarations, and consumers without the optional peer installed could no + * longer compile against them. Events stay opaque — this route only + * re-serializes them. + */ export interface AgentDocumentRuntimeModule { - readonly DEFAULT_AGENT_RENDER_LIMITS: AgentRenderLimits; - readonly decodeAgentFlightStream: ( + readonly decodeAgentFlight: ( flight: ReadableStream, - options?: Readonly<{ readonly limits?: Partial; readonly signal?: AbortSignal }>, - ) => ReadableStream; + signal: AbortSignal, + ) => AsyncIterable; } export interface RuntimeRoutesOptions { @@ -56,8 +60,8 @@ let agentDocumentRuntimePromise: Promise | undefined const loadAgentDocumentRuntime = async (): Promise => { agentDocumentRuntimePromise ??= import('@agent-bundle/runtime') .then((runtime) => Object.freeze({ - DEFAULT_AGENT_RENDER_LIMITS: runtime.DEFAULT_AGENT_RENDER_LIMITS, - decodeAgentFlightStream: runtime.decodeAgentFlightStream, + decodeAgentFlight: (flight: ReadableStream, signal: AbortSignal) => + runtime.decodeAgentFlightStream(flight, { limits: runtime.DEFAULT_AGENT_RENDER_LIMITS, signal }), })) .catch((error: unknown) => { agentDocumentRuntimePromise = undefined; @@ -427,12 +431,9 @@ export class RuntimeRoutes { controller.close(); }, }); - const events: AgentRenderEvent[] = []; + const events: unknown[] = []; let responseBytes = Buffer.byteLength('{"events":[]}'); - for await (const event of runtime.decodeAgentFlightStream(flight, { - limits: runtime.DEFAULT_AGENT_RENDER_LIMITS, - signal: abortController.signal, - })) { + for await (const event of runtime.decodeAgentFlight(flight, abortController.signal)) { const eventBytes = Buffer.byteLength(JSON.stringify(event)); const separatorBytes = events.length === 0 ? 0 : 1; if (responseBytes + separatorBytes + eventBytes > agentDocumentResponseLimit) { diff --git a/packages/agent-bundle/tests/runtime-routes.test.ts b/packages/agent-bundle/tests/runtime-routes.test.ts index fb350cf9c..e1105d064 100644 --- a/packages/agent-bundle/tests/runtime-routes.test.ts +++ b/packages/agent-bundle/tests/runtime-routes.test.ts @@ -202,13 +202,12 @@ const renderReadyFlight = async (): Promise => new Promise((resolve, }); const realAgentDocumentRuntime = async () => ({ - DEFAULT_AGENT_RENDER_LIMITS, - decodeAgentFlightStream, + decodeAgentFlight: (flight: ReadableStream, signal: AbortSignal) => + decodeAgentFlightStream(flight, { limits: DEFAULT_AGENT_RENDER_LIMITS, signal }), }); const emptyAgentDocumentRuntime = async () => ({ - DEFAULT_AGENT_RENDER_LIMITS, - decodeAgentFlightStream: () => new ReadableStream({ + decodeAgentFlight: () => new ReadableStream({ start(controller) { controller.close(); }, @@ -327,12 +326,11 @@ it('aborts decoding when Agent Document events exceed the aggregate response bud const largeText = 'x'.repeat(512 * 1024); const server = await start(new MemoryRuntime(), { loadAgentDocumentRuntime: async () => ({ - DEFAULT_AGENT_RENDER_LIMITS, - decodeAgentFlightStream: (_flight, options) => { + decodeAgentFlight: (_flight: ReadableStream, signal: AbortSignal) => { let sequence = 0; return new ReadableStream({ start(controller) { - options?.signal?.addEventListener('abort', () => { + signal.addEventListener('abort', () => { aborted = true; controller.error(new DOMException('Agent render was aborted', 'AbortError')); }, { once: true }); @@ -393,8 +391,7 @@ it('returns honest diagnostics when the Agent runtime is absent or stored Flight const invalid = await start(new MemoryRuntime(), { loadAgentDocumentRuntime: async () => ({ - DEFAULT_AGENT_RENDER_LIMITS, - decodeAgentFlightStream: () => { + decodeAgentFlight: () => { throw new Error('invalid Flight'); }, }), diff --git a/packages/workbench/tests/runtime-inspector.test.ts b/packages/workbench/tests/runtime-inspector.test.ts index 283fc5f2b..690ddc5c0 100644 --- a/packages/workbench/tests/runtime-inspector.test.ts +++ b/packages/workbench/tests/runtime-inspector.test.ts @@ -113,8 +113,8 @@ describe('Runtime inspector', () => { await page.getByRole('tab', { name: 'Document' }).click(); await page.getByRole('heading', { name: 'Customer document' }).waitFor({ timeout: 5_000 }); - expect(await page.getByLabel('Agent Document').textContent()).toContain('Version 1 · success'); - expect(await page.getByLabel('Agent Document').textContent()).toContain('Loaded · 1 / 1'); + expect(await page.getByLabel('Agent Document', { exact: true }).textContent()).toContain('Version 1 · success'); + expect(await page.getByLabel('Agent Document', { exact: true }).textContent()).toContain('Loaded · 1 / 1'); await page.getByRole('tab', { name: 'Protocol' }).click(); await page.getByText('Provider MCP protocol', { exact: true }).waitFor({ timeout: 5_000 });