Skip to content

Commit 23d129e

Browse files
test(rstest): pool policy from the #576 audit — worker-root teardown, stale-dist guard, isolation and timeouts in every pool, adapter libId, membership moves (#587)
* test(rstest): shared pool policy — worker-root teardown, stale-dist guard, isolation setup in every pool, explicit timeouts, adapter libId (#576) - rstest.global-setup.ts: tag each invocation with AGENT_BUNDLE_RSTEST_RUN_ID (relayed to workers), record it in the worker-root owner marker, and remove exactly that run's /tmp/ab-rstest-* roots in teardown. - scripts/dist-freshness.mjs + rstest.dist-freshness.setup.ts + scripts/check-dist-fresh.mjs: newest-mtime src-vs-dist guard for every `pnpm build` output, run in every pool's globalSetup and ahead of `pnpm typecheck`; ensureRuntimeExamplePayload rebuilds a stale payload. - rstest.pools.ts: shared globalSetup chains, setup file, poolTimeouts (hookTimeout = testTimeout), process-pool worker cap and time scale; route-unit/projection load rstest.setup.ts before the generated registry; rstest.config.ts gets the process-pool cap and 30 s floors. - rstest.rslib.ts passes the lib's id as libId; mapped vs ignored adapter fields documented; rstest-rslib-adapter.test.ts and rstest-pool-configs.test.ts pin the resolved configs. - projection/mcp-lineage.test.ts imports agent-bundle/test from src. * test(rstest): move the two compiler-running files to the integration pool and the installed-host contract proofs to their own pool (#576) prebuilt-payload.test.ts and test-browser-rstest.test.ts run real builds; host-adapters.native.test.ts asserts nothing without AGENT_BUNDLE_NATIVE_HOST_CONTRACTS=1 and now runs on one worker through rstest.native-host.config.ts (`pnpm test:native-host`) instead of as 29 skips per integration run. Shards still partition the pool (46/46). * test(rstest): build before every pool in the test chain and the native smoke; document the freshness bound (#576 self-review) Every pool now refuses a missing or stale dist, so `pnpm test` builds once up front and runs the integration pool prebuilt, native-host-smoke.yml builds before its unit-pool smoke, and the AGENTS.md gate starts with `pnpm build`. dist-freshness.mjs names the one false negative the newest-mtime comparison admits.
1 parent 97a5bfa commit 23d129e

30 files changed

Lines changed: 1681 additions & 147 deletions

‎.github/workflows/native-host-smoke.yml‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,17 @@ jobs:
5050
if: inputs.host == 'both' || inputs.host == matrix.host
5151
with:
5252
node-version: 22.19.0
53+
# One build for every pool below, on a runner that starts from a fresh
54+
# checkout: every pool refuses to start over a missing or stale dist
55+
# (rstest.dist-freshness.setup.ts), `test:integration:run` reads the
56+
# prebuilt dist (AGENT_BUNDLE_*_PREBUILT=1), and the host install proofs
57+
# spawn packages/agent-bundle/dist/cli.js (tests/support/host-install.ts).
58+
- run: pnpm build
59+
if: inputs.host == 'both' || inputs.host == matrix.host
5360
# The source smokes run each pool's script with that pool's files. A single
5461
# `pnpm test -- <files>` cannot target them: pnpm appends the arguments to
55-
# the end of the `test` chain (`test:unit && test:route-unit &&
56-
# test:projection && test:integration`), so only the last command receives
62+
# the end of the `test` chain (`build && test:unit && test:route-unit &&
63+
# test:projection && test:integration:run`), so only the last command receives
5764
# them and unit, route-unit and projection run unfiltered (~3.5 min of
5865
# unrelated tests); and rstest's CLI parser (cac) keeps everything after
5966
# `--` out of the positional filters, so the integration pool ran
@@ -67,13 +74,6 @@ jobs:
6774
AGENT_BUNDLE_NATIVE_CLAUDE_SMOKE: ${{ matrix.host == 'claude' && '1' || '' }}
6875
AGENT_BUNDLE_NATIVE_CODEX_SMOKE: ${{ matrix.host == 'codex' && '1' || '' }}
6976
run: pnpm test:unit ${{ matrix.unit_tests }}
70-
# One build for every dist consumer below, on a runner that starts from a
71-
# fresh checkout: `test:integration:run` reads the prebuilt dist
72-
# (AGENT_BUNDLE_*_PREBUILT=1), and the host install proofs spawn
73-
# packages/agent-bundle/dist/cli.js (tests/support/host-install.ts). The
74-
# unit pool above is build-free, so it runs first and fails fast.
75-
- run: pnpm build
76-
if: inputs.host == 'both' || inputs.host == matrix.host
7777
- name: ${{ matrix.host }} source signed-in smoke (integration pool)
7878
if: matrix.integration_tests != '' && (inputs.host == 'both' || inputs.host == matrix.host)
7979
env:

‎AGENTS.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@
4444
for false positives from prose in `docs/**` and from strings that merely
4545
contain the name: `Symbol('epoch-staging')` in `dev/epoch-store.ts` was the
4646
only match for a 343-line dead file, which is why it read as reachable.
47-
- Gate before pushing: `pnpm typecheck && pnpm lint && pnpm test:unit`, plus
48-
`pnpm build` first if the change touches `packages/rsc-runtime`.
47+
- Gate before pushing: `pnpm build && pnpm typecheck && pnpm lint && pnpm
48+
test:unit`. Every Rstest pool and `pnpm typecheck` refuse to start over a
49+
`dist` older than its sources (`scripts/dist-freshness.mjs`), so the build
50+
comes first whatever the change touched.
4951

5052
## Workbench platform scope
5153

‎package.json‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,19 @@
1010
"packageManager": "pnpm@11.23.0",
1111
"scripts": {
1212
"build": "pnpm --filter rsc-markdown-stream build && pnpm --filter @agent-bundle/runtime build && pnpm --filter agent-bundle build && pnpm --filter create-agent-bundle build",
13-
"test": "pnpm test:unit && pnpm test:route-unit && pnpm test:projection && pnpm test:integration",
13+
"test": "pnpm build && pnpm test:unit && pnpm test:route-unit && pnpm test:projection && pnpm test:integration:run",
1414
"test:unit": "rstest --config rstest.unit.config.ts",
1515
"test:route-unit": "rstest --config rstest.route-unit.config.ts",
1616
"test:projection": "rstest --config rstest.projection.config.ts",
1717
"test:integration": "pnpm build && pnpm test:integration:run",
1818
"test:integration:run": "AGENT_BUNDLE_WORKBENCH_PREBUILT=1 AGENT_BUNDLE_PACKAGE_PREBUILT=1 rstest --config rstest.integration.config.ts",
1919
"test:evidence": "pnpm build && AGENT_BUNDLE_WORKBENCH_PREBUILT=1 AGENT_BUNDLE_PACKAGE_PREBUILT=1 rstest --config rstest.evidence.config.ts",
2020
"test:mcp-conformance": "pnpm build && AGENT_BUNDLE_MCP_CONFORMANCE=1 rstest --config rstest.mcp-conformance.config.ts",
21+
"test:native-host": "pnpm build && AGENT_BUNDLE_NATIVE_HOST_CONTRACTS=1 rstest --config rstest.native-host.config.ts",
2122
"test:watch": "rstest --config rstest.config.ts --watch",
2223
"lint": "rslint .",
2324
"bench:hook-cold-start": "node scripts/measure-hook-cold-start.mjs",
24-
"typecheck": "tsc --noEmit && tsc --project packages/workbench/tsconfig.json && tsc --project packages/create-agent-bundle/tsconfig.json && tsc --project packages/rsc-markdown-stream/tsconfig.json",
25+
"typecheck": "node scripts/check-dist-fresh.mjs && tsc --noEmit && tsc --project packages/workbench/tsconfig.json && tsc --project packages/create-agent-bundle/tsconfig.json && tsc --project packages/rsc-markdown-stream/tsconfig.json",
2526
"check": "pnpm build && pnpm test:unit && pnpm test:route-unit && pnpm test:projection && pnpm test:integration:run && pnpm lint && pnpm typecheck",
2627
"check:local-ci": "node scripts/local-ci.mjs",
2728
"check:host-cli": "node scripts/host-cli-pins.mjs verify",

‎packages/agent-bundle/rslib.config.ts‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,22 @@ const esmNodeGlobalsPlugin: RsbuildPlugin = {
4343
*/
4444
const buildCacheDirectory = process.env['AGENT_BUNDLE_RSLIB_CACHE_DIRECTORY'];
4545

46+
/**
47+
* The `id` of the single lib entry. To Rslib an id is a name: it labels the
48+
* Rsbuild environment the entry becomes (`esm` when unset —
49+
* `composeRsbuildEnvironments` in @rslib/core), so it shows in build logs,
50+
* selects the entry for `rslib build --lib`, and keys the persistent build
51+
* cache's version (`<environment>-<mode>`, Rsbuild's cache plugin); it
52+
* changes no emitted file. It exists so `rstest.rslib.ts` can pass it as the
53+
* adapter's `libId`: without one, `@rstest/adapter-rslib` reads none of this
54+
* entry's fields into the pools' test build.
55+
*/
56+
export const agentBundleLibId = 'esm-node';
57+
4658
export default defineConfig({
4759
lib: [
4860
{
61+
id: agentBundleLibId,
4962
bundle: true,
5063
// One `.d.ts` per source module. Bundling them per entry
5164
// (`dts: { bundle: true }`, API Extractor) was measured and rejected:
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
import { existsSync } from 'node:fs';
2+
import { lstat, mkdir, mkdtemp, readdir, rm, utimes, writeFile } from 'node:fs/promises';
3+
import { tmpdir } from 'node:os';
4+
import { dirname, join, resolve } from 'node:path';
5+
6+
import { afterEach, describe, expect, it } from '@rstest/core';
7+
8+
import {
9+
assertFreshDist,
10+
checkDistFreshness,
11+
distFreshness,
12+
formatDistFreshnessFailure,
13+
isSkippedInputDirectory,
14+
newestEntry,
15+
runtimeExampleBuildOutputs,
16+
workspaceBuildOutputs,
17+
type DistDescriptor,
18+
type DistFreshness,
19+
} from '../../../scripts/dist-freshness.mjs';
20+
21+
const workspaceRoot = process.cwd();
22+
23+
/** Fixture clock: sources written first, dist built after, then one later edit. */
24+
const sourceTime = new Date('2026-01-01T00:00:00Z');
25+
const buildTime = new Date('2026-01-02T00:00:00Z');
26+
const editTime = new Date('2026-01-03T00:00:00Z');
27+
28+
const temporaryRoots: string[] = [];
29+
30+
afterEach(async () => {
31+
await Promise.all(temporaryRoots.splice(0).map((root) => rm(root, { force: true, recursive: true })));
32+
});
33+
34+
/** Writes empty files at `paths` (creating directories) under `root`. */
35+
const writeFiles = async (root: string, paths: readonly string[]): Promise<void> => {
36+
for (const path of paths) {
37+
await mkdir(dirname(join(root, path)), { recursive: true });
38+
await writeFile(join(root, path), '');
39+
}
40+
};
41+
42+
/** Sets `mtime` on `path` and, for a directory, everything beneath it — children first, since creating entries had left the directories at "now". */
43+
const stampTree = async (path: string, mtime: Date): Promise<void> => {
44+
if ((await lstat(path)).isDirectory()) {
45+
for (const entry of await readdir(path)) await stampTree(join(path, entry), mtime);
46+
}
47+
await utimes(path, mtime, mtime);
48+
};
49+
50+
interface PackageFixture {
51+
readonly descriptor: DistDescriptor;
52+
readonly root: string;
53+
}
54+
55+
/**
56+
* A package whose sources predate its dist: `src` (two levels), the Rslib
57+
* config, the manifest, a declared-but-absent `tsconfig.json`, and a dist
58+
* with a nested chunk directory. It sits one level below the temporary
59+
* root so a test can add a sibling package (`../shared-lib`).
60+
*/
61+
const createPackageFixture = async (): Promise<PackageFixture> => {
62+
const base = await mkdtemp(join(tmpdir(), 'dist-freshness-'));
63+
temporaryRoots.push(base);
64+
const root = join(base, 'pkg');
65+
await writeFiles(root, ['src/index.ts', 'src/nested/util.ts', 'rslib.config.ts', 'package.json', 'dist/index.js', 'dist/chunks/shared.js']);
66+
await stampTree(root, sourceTime);
67+
await stampTree(join(root, 'dist'), buildTime);
68+
return {
69+
descriptor: {
70+
name: 'fixture',
71+
root,
72+
inputs: ['src', 'rslib.config.ts', 'package.json', 'tsconfig.json'],
73+
output: 'dist',
74+
},
75+
root,
76+
};
77+
};
78+
79+
const touch = (path: string, mtime: Date): Promise<void> => utimes(path, mtime, mtime);
80+
81+
describe('distFreshness', () => {
82+
it('is fresh when every input predates the newest built file, ignoring declared inputs that do not exist', async () => {
83+
const { descriptor, root } = await createPackageFixture();
84+
const result = distFreshness(descriptor);
85+
expect(result.status).toBe('fresh');
86+
expect(result.output).toBe(join(root, 'dist'));
87+
expect(new Date(result.newestInput.mtimeMs)).toEqual(sourceTime);
88+
expect(new Date(result.newestOutput?.mtimeMs ?? 0)).toEqual(buildTime);
89+
expect(result.newestOutput?.path.startsWith(join(root, 'dist'))).toBe(true);
90+
});
91+
92+
it('is stale when a nested source file is newer than the dist, and names that file', async () => {
93+
const { descriptor, root } = await createPackageFixture();
94+
await touch(join(root, 'src/nested/util.ts'), editTime);
95+
const result = distFreshness(descriptor);
96+
expect(result.status).toBe('stale');
97+
expect(result.newestInput).toEqual({ mtimeMs: editTime.getTime(), path: join(root, 'src/nested/util.ts') });
98+
});
99+
100+
it('is stale when a build config file input is newer than the dist', async () => {
101+
const { descriptor, root } = await createPackageFixture();
102+
await touch(join(root, 'rslib.config.ts'), editTime);
103+
expect(distFreshness(descriptor).status).toBe('stale');
104+
});
105+
106+
it('counts input directory mtimes, so a deleted or renamed source is noticed', async () => {
107+
const { descriptor, root } = await createPackageFixture();
108+
await touch(join(root, 'src'), editTime);
109+
const result = distFreshness(descriptor);
110+
expect(result.status).toBe('stale');
111+
expect(result.newestInput.path).toBe(join(root, 'src'));
112+
});
113+
114+
it('is missing when the dist is absent, empty, or holds only empty directories', async () => {
115+
const { descriptor, root } = await createPackageFixture();
116+
await rm(join(root, 'dist'), { recursive: true });
117+
expect(distFreshness(descriptor)).toMatchObject({ newestOutput: undefined, status: 'missing' });
118+
await mkdir(join(root, 'dist'));
119+
expect(distFreshness(descriptor).status).toBe('missing');
120+
await mkdir(join(root, 'dist/chunks'));
121+
expect(distFreshness(descriptor).status).toBe('missing');
122+
});
123+
124+
it('skips node_modules, dist, .rstest-temp and dot-directories inside inputs', async () => {
125+
const { descriptor, root } = await createPackageFixture();
126+
const skipped = ['src/node_modules/dep/index.js', 'src/dist/out.js', 'src/.rstest-temp/chunk.js', 'src/.cache/entry.js'];
127+
await writeFiles(root, skipped);
128+
for (const path of skipped) await stampTree(join(root, path, '..'), editTime);
129+
// Creating the directories moved `src` itself to "now"; put it back so
130+
// only the skipped trees are newer than the dist.
131+
await touch(join(root, 'src'), sourceTime);
132+
const result = distFreshness(descriptor);
133+
expect(result.status).toBe('fresh');
134+
expect(new Date(result.newestInput.mtimeMs)).toEqual(sourceTime);
135+
for (const name of ['node_modules', 'dist', '.rstest-temp', '.git', '.agent-bundle']) {
136+
expect(isSkippedInputDirectory(name), name).toBe(true);
137+
}
138+
for (const name of ['src', 'lib', 'chunks', 'dist-tools']) {
139+
expect(isSkippedInputDirectory(name), name).toBe(false);
140+
}
141+
});
142+
143+
it('walks an input root that is itself a dist, so a rebuilt workspace dependency flags a bundle that embeds it', async () => {
144+
const { descriptor, root } = await createPackageFixture();
145+
await writeFiles(root, ['../shared-lib/dist/index.js']);
146+
const sharedDist = resolve(root, '../shared-lib/dist');
147+
await stampTree(sharedDist, editTime);
148+
const result = distFreshness({ ...descriptor, inputs: [...descriptor.inputs, '../shared-lib/dist'] });
149+
expect(result.status).toBe('stale');
150+
// The directory and its file carry the same stamp; either is the evidence.
151+
expect(result.newestInput.path.startsWith(sharedDist)).toBe(true);
152+
expect(new Date(result.newestInput.mtimeMs)).toEqual(editTime);
153+
});
154+
155+
it('ignores dot-directories inside the dist, so a temp writer there cannot mask staleness', async () => {
156+
const { descriptor, root } = await createPackageFixture();
157+
await touch(join(root, 'src/index.ts'), editTime);
158+
await writeFiles(root, ['dist/.rstest-temp/scratch.js']);
159+
await stampTree(join(root, 'dist/.rstest-temp'), new Date('2027-01-01T00:00:00Z'));
160+
const result = distFreshness(descriptor);
161+
expect(result.status).toBe('stale');
162+
expect(new Date(result.newestOutput?.mtimeMs ?? 0)).toEqual(buildTime);
163+
});
164+
165+
it('rejects a descriptor none of whose inputs exist', async () => {
166+
const { descriptor } = await createPackageFixture();
167+
expect(() => distFreshness({ ...descriptor, inputs: ['nope', 'also-nope.ts'] })).toThrow(/none of the 2 declared inputs of fixture exist/u);
168+
});
169+
});
170+
171+
describe('newestEntry', () => {
172+
it('returns the file itself for a file, undefined for a missing path, and the newest file of a tree', async () => {
173+
const { root } = await createPackageFixture();
174+
expect(newestEntry(join(root, 'package.json'))).toEqual({ mtimeMs: sourceTime.getTime(), path: join(root, 'package.json') });
175+
expect(newestEntry(join(root, 'missing'))).toBeUndefined();
176+
await touch(join(root, 'dist/chunks/shared.js'), editTime);
177+
expect(newestEntry(join(root, 'dist'))).toEqual({ mtimeMs: editTime.getTime(), path: join(root, 'dist/chunks/shared.js') });
178+
});
179+
});
180+
181+
describe('formatDistFreshnessFailure and assertFreshDist', () => {
182+
const fresh: DistFreshness = {
183+
name: 'fresh-package',
184+
newestInput: { mtimeMs: sourceTime.getTime(), path: '/repo/packages/fresh/src/index.ts' },
185+
newestOutput: { mtimeMs: buildTime.getTime(), path: '/repo/packages/fresh/dist/index.js' },
186+
output: '/repo/packages/fresh/dist',
187+
status: 'fresh',
188+
};
189+
const stale: DistFreshness = {
190+
name: '@agent-bundle/runtime',
191+
newestInput: { mtimeMs: editTime.getTime(), path: '/repo/packages/rsc-runtime/src/index.ts' },
192+
newestOutput: { mtimeMs: buildTime.getTime(), path: '/repo/packages/rsc-runtime/dist/index.js' },
193+
output: '/repo/packages/rsc-runtime/dist',
194+
status: 'stale',
195+
};
196+
const missing: DistFreshness = {
197+
name: 'agent-bundle-workbench',
198+
newestInput: { mtimeMs: sourceTime.getTime(), path: '/repo/packages/workbench/src/main.tsx' },
199+
newestOutput: undefined,
200+
output: '/repo/packages/workbench/dist',
201+
status: 'missing',
202+
};
203+
204+
it('names every stale or missing output with its evidence and ends with the rebuild instruction', () => {
205+
const message = formatDistFreshnessFailure([fresh, stale, missing], { relativeTo: '/repo' });
206+
expect(message).not.toContain('fresh-package');
207+
expect(message).toContain(
208+
' @agent-bundle/runtime: stale — packages/rsc-runtime/src/index.ts (2026-01-03T00:00:00.000Z)'
209+
+ ' is newer than packages/rsc-runtime/dist/index.js (2026-01-02T00:00:00.000Z)',
210+
);
211+
expect(message).toContain(' agent-bundle-workbench: missing — packages/workbench/dist has no built files');
212+
expect(message.endsWith('run `pnpm build`.')).toBe(true);
213+
});
214+
215+
it('prints paths outside relativeTo as they are', () => {
216+
expect(formatDistFreshnessFailure([stale], { relativeTo: '/elsewhere' })).toContain('/repo/packages/rsc-runtime/src/index.ts');
217+
});
218+
219+
it('formats an all-fresh result set to the empty string', () => {
220+
expect(formatDistFreshnessFailure([fresh])).toBe('');
221+
});
222+
223+
it('assertFreshDist throws the message for a stale fixture and returns for a fresh one', async () => {
224+
const { descriptor, root } = await createPackageFixture();
225+
expect(() => assertFreshDist([descriptor])).not.toThrow();
226+
await touch(join(root, 'src/index.ts'), editTime);
227+
expect(() => assertFreshDist([descriptor], { relativeTo: root })).toThrow(
228+
/^Built output is stale or missing[\s\S]*fixture: stale — src\/index\.ts[\s\S]*run `pnpm build`\.$/u,
229+
);
230+
expect(checkDistFreshness([descriptor]).map((result) => result.status)).toEqual(['stale']);
231+
});
232+
});
233+
234+
describe('workspace descriptors', () => {
235+
it('lists every dist `pnpm build` produces, in build order, and every declared input exists in the repository', () => {
236+
const outputs = workspaceBuildOutputs(workspaceRoot);
237+
expect(outputs.map((output) => output.name)).toEqual([
238+
'rsc-markdown-stream',
239+
'@agent-bundle/runtime',
240+
'agent-bundle-workbench',
241+
'agent-bundle',
242+
'create-agent-bundle',
243+
]);
244+
for (const output of outputs) {
245+
expect(output.output).toBe('dist');
246+
expect(output.root.startsWith(join(workspaceRoot, 'packages'))).toBe(true);
247+
for (const input of output.inputs) {
248+
expect(existsSync(resolve(output.root, input)), `${output.name}: ${input}`).toBe(true);
249+
}
250+
expect(output.inputs).toContain('src');
251+
expect(output.inputs).toContain('package.json');
252+
expect(output.inputs).toContain('../../pnpm-lock.yaml');
253+
}
254+
});
255+
256+
it('gives agent-bundle the Workbench build inputs its Rslib build copies from', () => {
257+
const agentBundle = workspaceBuildOutputs(workspaceRoot).find((output) => output.name === 'agent-bundle');
258+
expect(agentBundle?.inputs).toEqual(expect.arrayContaining(['../workbench/src', '../workbench/index.html', '../workbench/rsbuild.config.ts', '../workbench/THIRD_PARTY_NOTICES']));
259+
expect(agentBundle?.inputs).not.toContain('../workbench/dist');
260+
});
261+
262+
it('describes the runtime example per payload tree, bundling the workspace runtime dists as inputs', () => {
263+
const outputs = runtimeExampleBuildOutputs(workspaceRoot, ['app', 'runtime']);
264+
expect(outputs.map((output) => output.output)).toEqual(['dist/app', 'dist/runtime']);
265+
for (const output of outputs) {
266+
expect(output.root).toBe(join(workspaceRoot, 'examples/rsc-agent-runtime'));
267+
expect(output.inputs).toEqual(expect.arrayContaining(['src', 'rsbuild.config.ts', '../../packages/rsc-runtime/dist', '../../packages/rsc-markdown-stream/dist']));
268+
// The dist inputs may be absent on a cold tree; every tracked input exists.
269+
for (const input of output.inputs.filter((path) => !path.endsWith('/dist'))) {
270+
expect(existsSync(resolve(output.root, input)), input).toBe(true);
271+
}
272+
}
273+
expect(runtimeExampleBuildOutputs(workspaceRoot)).toHaveLength(2);
274+
});
275+
});

‎packages/agent-bundle/tests/projection/mcp-lineage.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, expect, it } from '@rstest/core';
22
import { createAgentLineageRegistry } from '@agent-bundle/runtime/lineage';
3-
import { openInMemoryMcpServer } from 'agent-bundle/test';
3+
import { openInMemoryMcpServer } from '../../src/test/mcp.ts';
44

55
const root = 'session-root';
66
const child = 'agent-child';

0 commit comments

Comments
 (0)