diff --git a/.changeset/deslop-wave1-delta.md b/.changeset/deslop-wave1-delta.md new file mode 100644 index 000000000..d11f9d6b0 --- /dev/null +++ b/.changeset/deslop-wave1-delta.md @@ -0,0 +1,6 @@ +--- +'agent-bundle': patch +'@agent-bundle/rsc-runtime': patch +--- + +Deslop pass over the Wave 1 delta: `config/normalize.ts` reuses the shared `core/freeze.ts` `deepFreeze` instead of a local copy, the internal `configClaimedSources` helper is no longer exported, the dev-lock URL publication settles through one named cleanup, and the rsc-runtime CLI binding drops a redundant `Object.freeze` (the request store snapshots and freezes capabilities itself). No behavior changes. diff --git a/packages/agent-bundle/src/config/normalize.ts b/packages/agent-bundle/src/config/normalize.ts index 146acad71..d85ac8f34 100644 --- a/packages/agent-bundle/src/config/normalize.ts +++ b/packages/agent-bundle/src/config/normalize.ts @@ -4,6 +4,7 @@ import { readFile } from 'node:fs/promises'; import { basename, extname, relative, resolve } from 'node:path'; import { digest } from '../core/digest.ts'; +import { deepFreeze } from '../core/freeze.ts'; import { isInside } from '../core/paths.ts'; import { defaultGeneratedRuntime, @@ -583,18 +584,6 @@ const normalizeScripts = ( }); }; -const deepFreeze = (value: Value): Value => { - if (typeof value !== 'object' || value === null || Object.isFrozen(value)) { - return value; - } - - for (const child of Object.values(value)) { - deepFreeze(child); - } - - return Object.freeze(value); -}; - export const configExtensionFiniteJsonDiagnosticMessage = 'A registered config extension must contain strict finite JSON data.'; const finiteJsonExtensionErrors = new WeakSet(); diff --git a/packages/agent-bundle/src/dev/dev-lock.ts b/packages/agent-bundle/src/dev/dev-lock.ts index ef0959d71..e3af580f0 100644 --- a/packages/agent-bundle/src/dev/dev-lock.ts +++ b/packages/agent-bundle/src/dev/dev-lock.ts @@ -346,20 +346,13 @@ export class DevLock { })(); this.#publishingUrl = url; this.#publishPromise = publishPromise; - void publishPromise.then( - () => { - if (this.#publishPromise === publishPromise) { - this.#publishPromise = undefined; - this.#publishingUrl = undefined; - } - }, - () => { - if (this.#publishPromise === publishPromise) { - this.#publishPromise = undefined; - this.#publishingUrl = undefined; - } - }, - ); + const settlePublication = (): void => { + if (this.#publishPromise === publishPromise) { + this.#publishPromise = undefined; + this.#publishingUrl = undefined; + } + }; + void publishPromise.then(settlePublication, settlePublication); return publishPromise; } diff --git a/packages/agent-bundle/src/routes/graph.ts b/packages/agent-bundle/src/routes/graph.ts index b11808049..8c26dd278 100644 --- a/packages/agent-bundle/src/routes/graph.ts +++ b/packages/agent-bundle/src/routes/graph.ts @@ -186,7 +186,7 @@ const claimedModuleEntry = (value: unknown): string | undefined => { * examples declare `scripts` entries under `src/scripts/`; this rule keeps * their layouts route-free without a migration. */ -export const configClaimedSources = ( +const configClaimedSources = ( projectRoot: string, config: Readonly, ): ReadonlySet => { diff --git a/packages/agent-bundle/tests/normalization.test.ts b/packages/agent-bundle/tests/normalization.test.ts index 9324a1b8a..903b2a147 100644 --- a/packages/agent-bundle/tests/normalization.test.ts +++ b/packages/agent-bundle/tests/normalization.test.ts @@ -136,9 +136,9 @@ it('normalizes registered extensions and validates registered script and hook ta targetRegistry: NormalizationTargetRegistry, ) => Diagnostic[]; - // Pin flip (#94 stages 1-2): this fixture is rooted at the workspace, whose - // package.json version (0.0.0) differs from the fixture's plugin.version, - // so the AB4008 mismatch warning is the only expected diagnostic. + // This fixture is rooted at the workspace, whose package.json version + // (0.0.0) differs from the fixture's plugin.version, so the AB4008 + // mismatch warning (#94) is the only expected diagnostic. expect(sourceValidator(loaded, { skills: [] }, extensionRegistry).map(({ code }) => code)).toEqual(['AB4008']); const model = await normalizeProject(loaded, { skills: [] }, extensionRegistry); @@ -258,8 +258,8 @@ it('reports unknown hook and script targets through the target registry', () => registry: NormalizationTargetRegistry, ) => Diagnostic[]; - // Pin flip (#94 stages 1-2): the workspace root package.json version - // (0.0.0) differs from the fixture's plugin.version, adding AB4008. + // The workspace root package.json version (0.0.0) differs from the + // fixture's plugin.version, adding the AB4008 mismatch warning (#94). expect(sourceValidator(loaded, { skills: [] }, targetRegistry).map(({ code }) => code)).toEqual([ 'AB4008', 'AB4203', @@ -753,8 +753,8 @@ it('validates the assets configuration shape, containment, and literal existence expect(diagnosticsFor('assets')).toEqual(['AB4600']); expect(diagnosticsFor([''])).toEqual(['AB4600']); expect(diagnosticsFor([42])).toEqual(['AB4600']); - // Pin flip (#94 stages 1-2): fixtures rooted at the workspace also report - // the AB4008 plugin.version/package version mismatch warning. + // Fixtures rooted at the workspace also report the AB4008 + // plugin.version/package version mismatch warning (#94). expect(diagnosticsFor(['../outside'], process.cwd())).toEqual(['AB4601', 'AB4008']); expect(diagnosticsFor(['definitely-missing-asset-entry'], process.cwd())).toEqual(['AB4602', 'AB4008']); expect(diagnosticsFor(['definitely-missing/*.svg'], process.cwd())).toEqual(['AB4008']); diff --git a/packages/rsc-runtime/README.md b/packages/rsc-runtime/README.md index 11e3bd25e..b208e6224 100644 --- a/packages/rsc-runtime/README.md +++ b/packages/rsc-runtime/README.md @@ -25,7 +25,8 @@ const result = lowerMcpResult( ``` The package exports `Hook`, `Mcp`, `lowerHookResult`, `lowerMcpResult`, -`createRscRequestContext`, `agent`, `runAgentRequest`, and `AgentRequestError`. It does not own an +`createRscRequestContext`, `agent`, `runAgentRequest`, `available`, +`unavailable`, and `AgentRequestError`. It does not own an RSC renderer, application state, transport, persistence, or host packaging. React 19 is a peer dependency and Node 22.19 or newer is required. diff --git a/packages/rsc-runtime/src/cli.ts b/packages/rsc-runtime/src/cli.ts index b011243a9..60cc0cd3b 100644 --- a/packages/rsc-runtime/src/cli.ts +++ b/packages/rsc-runtime/src/cli.ts @@ -35,12 +35,12 @@ export const runRscCli = async ( signal.throwIfAborted(); const cwd = process.cwd(); const result = await runAgentRequest({ - capabilities: Object.freeze({ + capabilities: { command: unavailable(), filesystem: unavailable(), network: unavailable(), projectRoot: available({ root: cwd }, 'derived'), - }), + }, host: unavailable('unsupported-surface'), invocation: { kind: 'cli', diff --git a/packages/workbench/tests/overview.e2e.test.ts b/packages/workbench/tests/overview.e2e.test.ts index 740596fea..4241a26dc 100644 --- a/packages/workbench/tests/overview.e2e.test.ts +++ b/packages/workbench/tests/overview.e2e.test.ts @@ -311,7 +311,7 @@ e2e('offers the host-owned MCP playground handoff only after a selected Runtime return (await response.json() as { readonly status: RuntimeStatus }).status; }, fixture.url); const initialProjectSource = await readProjectSource(); - // Pin flip (#94 stages 1-2): source status now carries the package identity derived from package.json. + // Source status carries the package identity derived from package.json (#94). expect(initialProjectSource).toEqual({ diagnostics: [], packageName: '@agent-bundle/rsc-agent-runtime-demo', revision: sourceRevision, state: 'ready' }); const expectRuntimeProfileInspection = async (preview: Locator, expectedSourceRevision: string): Promise => { await expect(preview.getByLabel('Simulated MCP App profile')).toContainText('Portable MCP Apps'); diff --git a/rstest.worker-isolation.ts b/rstest.worker-isolation.ts index 6d7372d51..7ada03076 100644 --- a/rstest.worker-isolation.ts +++ b/rstest.worker-isolation.ts @@ -21,7 +21,7 @@ export const rstestWorkerCacheDirectory = (name: string): string => { export const isolateWorkerEnvironment = (): void => { const root = rstestWorkerRoot(); const cache = rstestWorkerCacheDirectory('xdg'); - const env = process['env']; + const env = process.env; env['TMPDIR'] = root; env['TMP'] = root; env['TEMP'] = root;