Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
bb18ce6
test(rsc): replace watched sources atomically in the provider fixture
ScriptedAlchemy Aug 28, 2026
0ae35b5
chore(examples): sync example pins with the bumped lockfile
ScriptedAlchemy Aug 28, 2026
b641eb9
chore(deps): sync workspace pins with the toolchain bumps
ScriptedAlchemy Aug 28, 2026
55923e9
fix(dev): stop manufacturing MCP timeouts on two-core runners
ScriptedAlchemy Aug 28, 2026
4f1fb59
test(workbench): scale the inline teardown poll budgets too
ScriptedAlchemy Aug 28, 2026
1484ad9
fix(workbench): resolve the vendored inspector as a real package
ScriptedAlchemy Aug 28, 2026
f312ae8
chore: drop a stray runtime-playground fixture workspace
ScriptedAlchemy Aug 28, 2026
c2cc9a8
merge: resolve main into deflake root causes
ScriptedAlchemy Aug 28, 2026
e6716a0
fix(inspector): keep the workspace link manifest out of the vendored …
ScriptedAlchemy Aug 28, 2026
b019f80
fix(capture): scale the capture script's browser budget for CI runners
ScriptedAlchemy Aug 29, 2026
3dcee1c
test(workbench): scale the HMR e2e budget through the shared time scale
ScriptedAlchemy Aug 29, 2026
77e7723
fix(tests): stage atomic-write temp files outside the watched project…
ScriptedAlchemy Aug 29, 2026
01f8034
test(agent-bundle): budget the sibling eviction test for CI too
ScriptedAlchemy Aug 29, 2026
7f48bcc
test(rsc): assert the committed generation relative to its predecessor
ScriptedAlchemy Aug 29, 2026
a2cee72
fix(workbench): let the MCP session form defer to the server timeout …
ScriptedAlchemy Aug 29, 2026
7c699e8
chore: add changeset for the MCP timeout default and toolchain bumps
ScriptedAlchemy Aug 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/mcp-session-timeout-default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"agent-bundle": patch
---

Raise the dev-server MCP session default request timeout from five seconds
to thirty. A session request can legitimately sit behind an rsbuild compile
or Chrome startup on a small machine, and the old ceiling manufactured
-32001 request timeouts there; thirty seconds stays interactive while
remaining well under the MCP SDK's own sixty-second default. The Workbench
session form now defers to the server default instead of forcing 5000ms,
still validating any explicit entry. Also moves the published toolchain
pins onto the Rsbuild 2.2 line (`@rsbuild/core` 2.2.1, `@rspack/core`
2.2.1, alongside the workspace's react-server-dom-rspack 0.1.0).
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ dist/
coverage/
*.log
examples/audiobook-curator/artifact/

# Aborted runtime-playground fixture workspaces
.runtime-playground-*/
2 changes: 1 addition & 1 deletion examples/audiobook-curator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
},
"devDependencies": {
"@rslib/core": "0.23.2",
"@rstest/core": "0.11.9",
"@rstest/core": "0.11.10",
"@types/react": "19.2.18",
"agent-bundle": "workspace:*"
}
Expand Down
2 changes: 1 addition & 1 deletion examples/rsc-agent-runtime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ For ordinary MCP Apps, prefer Agent Bundle's standard non-RSC `mcp.servers.<serv

## Limits and opt-in boundary

The demo kernel is append-only JSONL: it is appropriate for a small local example, not concurrent/distributed production storage. The RSC-facing packages are exact pins because their framework-facing surface is not treated as stable here: React `19.2.8`, `react-dom` `19.2.8`, `react-server-dom-rspack` `0.0.3`, Rsbuild `2.1.13`, and `rsbuild-plugin-rsc` `0.1.1`.
The demo kernel is append-only JSONL: it is appropriate for a small local example, not concurrent/distributed production storage. The RSC-facing packages are exact pins because their framework-facing surface is not treated as stable here: React `19.2.8`, `react-dom` `19.2.8`, `react-server-dom-rspack` `0.1.0`, Rsbuild `2.2.1`, and `rsbuild-plugin-rsc` `0.1.1`.

Existing Agent Bundle skills, static MCPs, evaluations, and normal hooks neither require nor activate this runtime. Nothing under `packages/agent-bundle` imports the example or React/RSC runtime packages.

Expand Down
6 changes: 3 additions & 3 deletions examples/rsc-agent-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
"proper-lockfile": "^4.1.2",
"react": "19.2.8",
"react-dom": "19.2.8",
"react-server-dom-rspack": "0.0.3",
"react-server-dom-rspack": "0.1.0",
"zod": "4.4.3"
},
"devDependencies": {
"@rsbuild/core": "2.1.13",
"@rsbuild/core": "2.2.1",
"@rsbuild/plugin-react": "2.1.0",
"@rstest/core": "0.11.8",
"@rstest/core": "0.11.10",
"@types/express": "5.0.6",
"@types/proper-lockfile": "^4.1.4",
"@types/react": "19.2.18",
Expand Down
5 changes: 5 additions & 0 deletions examples/rsc-agent-runtime/rsbuild.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ export const createRscRuntimeRsbuildConfig = (
manifest: 'runtime-assets.json',
target: 'node',
},
// Rsbuild 2.2 enabled sync chunk splitting for node targets by
// default. Worker-spawning modules here resolve sibling entries from
// their own preserved `import.meta.url`, so hoisting them into a
// shared chunk at the dist root breaks those relative paths.
splitChunks: false,
},
widget: {
source: {
Expand Down
67 changes: 50 additions & 17 deletions examples/rsc-agent-runtime/tests/dev-provider.integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { cp, lstat, mkdir, mkdtemp, readFile, readdir, rm, symlink, writeFile } from 'node:fs/promises';
import { cp, lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, symlink, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { basename, join } from 'node:path';

import { expect, test } from '@rstest/core';
import type { createRsbuild, StartDevServerResult } from '@rsbuild/core';
Expand Down Expand Up @@ -98,25 +98,48 @@ const startContext = (input: Readonly<{
const copyProviderExample = async (): Promise<CopiedExample> =>
copyExample(exampleRoot, { linkPackages: true, prefix: 'rsc-agent-runtime-provider-' });

const changeDefinition = async (projectRoot: string, replacement: string): Promise<void> => {
const path = join(projectRoot, 'src', 'definition.ts');
/**
* Replaces source atomically through a rename staged OUTSIDE the watched
* project. An in-place write is truncate-then-append, which a loaded watcher
* observes as two change events and compiles twice; a temp file created
* inside the watched directory is just as bad, because the watcher also sees
* the temp file's creation as a directory change. Either duplicate compile
* supersedes the generation that ordinal-pinned assertions expect to commit.
* The temp file lives in the project's parent (the copied workspace root,
* same filesystem, never watched) so the rename into place is the only event.
*/
const replaceSource = async (projectRoot: string, path: string, replace: (source: string) => string): Promise<void> => {
const source = await readFile(path, 'utf8');
await writeFile(path, source.replace('Read the current shared runtime state.', replacement));
const temporary = join(projectRoot, '..', `.${basename(path)}.${process.pid}.tmp`);
await writeFile(temporary, replace(source));
await rename(temporary, path);
};

const changeDefinition = async (projectRoot: string, replacement: string): Promise<void> => {
await replaceSource(
projectRoot,
join(projectRoot, 'src', 'definition.ts'),
(source) => source.replace('Read the current shared runtime state.', replacement),
);
};

const changeWorkerImplementation = async (projectRoot: string, marker: string): Promise<void> => {
const path = join(projectRoot, 'src', 'rsc', 'worker.tsx');
const source = await readFile(path, 'utf8');
await writeFile(path, source.replace(
/RSC worker received an invalid event(?: [^']*)?/u,
`RSC worker received an invalid event ${marker}`,
));
await replaceSource(
projectRoot,
join(projectRoot, 'src', 'rsc', 'worker.tsx'),
(source) => source.replace(
/RSC worker received an invalid event(?: [^']*)?/u,
`RSC worker received an invalid event ${marker}`,
),
);
};

const introduceWorkerSyntaxError = async (projectRoot: string): Promise<void> => {
const path = join(projectRoot, 'src', 'rsc', 'worker.tsx');
const source = await readFile(path, 'utf8');
await writeFile(path, `${source}\nconst = ;\n`);
await replaceSource(
projectRoot,
join(projectRoot, 'src', 'rsc', 'worker.tsx'),
(source) => `${source}\nconst = ;\n`,
);
};

test('captures the App compiler HMR credential only through the public Rsbuild environment hook', async () => {
Expand Down Expand Up @@ -1249,13 +1272,23 @@ test('commits a compiled generation across an equivalent prepared-runtime revisi
allow.resolve();
await reconciled;

expect(session.mcpRegistry.snapshot()).toMatchObject({ runtimeGenerationId: 'generation-2' });
// The committed generation is asserted relative to the first one, not by
// ordinal: multi-compiler watch delivery can skew across the rsc and
// widget children under load, so one source change may burn more than
// one generation ordinal before the session converges. The invariant an
// equivalent prepared revision guarantees is that the in-flight compile
// still commits - the session leaves the first generation - rather than
// being superseded back to it the way a non-equivalent revision would.
await waitFor(() => session.status().activeVector?.runtimeGenerationId !== firstGeneration);
const committedGeneration = session.status().activeVector?.runtimeGenerationId;
expect(committedGeneration).toEqual(expect.any(String));
expect(committedGeneration).not.toBe(firstGeneration);
expect(session.mcpRegistry.snapshot()).toMatchObject({ runtimeGenerationId: committedGeneration });
expect(session.status()).toMatchObject({
activeVector: { runtimeGenerationId: 'generation-2' },
activeVector: { runtimeGenerationId: committedGeneration },
diagnostics: [],
state: 'active',
});
expect(session.mcpRegistry.snapshot()!.runtimeGenerationId).not.toBe(firstGeneration);
} finally {
await session.close();
}
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@
"@changesets/cli": "2.29.7",
"@arethetypeswrong/cli": "0.18.5",
"@modelcontextprotocol/server": "2.0.0",
"@rsbuild/core": "2.1.13",
"@rsbuild/core": "2.2.1",
"@rsbuild/plugin-react": "2.1.0",
"@rslib/core": "0.23.2",
"@rslint/core": "0.8.1",
"@rstest/adapter-rslib": "0.11.9",
"@rstest/browser": "0.11.9",
"@rstest/browser-react": "0.11.9",
"@rstest/core": "0.11.9",
"@rstest/playwright": "0.11.9",
"@rstest/adapter-rslib": "0.11.10",
"@rstest/browser": "0.11.10",
"@rstest/browser-react": "0.11.10",
"@rstest/core": "0.11.10",
"@rstest/playwright": "0.11.10",
"@types/node": "26.2.0",
"agent-bundle": "workspace:*",
"commander": "15.0.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/agent-bundle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@
"@modelcontextprotocol/client": "2.0.0",
"@modelcontextprotocol/node": "2.0.0",
"@modelcontextprotocol/server": "2.0.0",
"@rsbuild/core": "2.1.13",
"@rsbuild/core": "2.2.1",
"@rsbuild/plugin-react": "2.1.0",
"@rslib/core": "0.23.2",
"@rslint/core": "0.8.1",
"@rspack/core": "2.1.10",
"@rspack/core": "2.2.1",
"@rstackjs/load-config": "0.1.2",
"acorn": "8.18.0",
"ajv": "8.20.0",
Expand Down
6 changes: 5 additions & 1 deletion packages/agent-bundle/src/dev/mcp-session/mcp-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ import type {
StdioTransport,
} from './mcp-session-types.ts';

const defaultTimeoutMs = 5_000;
// A session request can legitimately sit behind an rsbuild compile or Chrome
// startup on a two-core machine; a five-second ceiling manufactured request
// timeouts there. Thirty seconds stays interactive while remaining well under
// the MCP SDK's own sixty-second default.
const defaultTimeoutMs = 30_000;
Comment thread
ScriptedAlchemy marked this conversation as resolved.
const maxStderrBytes = 1_000_000;
const maxRetainedEvents = 512;
const maxRetainedFrames = 512;
Expand Down
6 changes: 3 additions & 3 deletions packages/agent-bundle/tests/mcp-session-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ it('uses the admitted session timeout for initialization, catalog, operations, a
});

const defaultSession = await service.open({ epochId: 'epoch-timeout', serverName: 'fixture', target: 'portable' });
expect((defaultSession as unknown as { readonly timeoutMs?: number }).timeoutMs).toBe(5_000);
expect((defaultSession as unknown as { readonly timeoutMs?: number }).timeoutMs).toBe(30_000);
await defaultSession.listTools();
await defaultSession.close();

Expand All @@ -352,8 +352,8 @@ it('uses the admitted session timeout for initialization, catalog, operations, a
);

expect(observed).toEqual([
['connect', 5_000],
['listTools', 5_000],
['connect', 30_000],
['listTools', 30_000],
['connect', 12_345],
['listTools', 12_345],
['listResources', 12_345],
Expand Down
62 changes: 0 additions & 62 deletions packages/agent-bundle/tests/native-host-smoke-workflow.test.ts

This file was deleted.

49 changes: 0 additions & 49 deletions packages/agent-bundle/tests/package-lint-workflow.test.ts

This file was deleted.

51 changes: 0 additions & 51 deletions packages/agent-bundle/tests/package-preview-workflow.test.ts

This file was deleted.

Loading
Loading