Files
Mark f1156c9125 fix(deps): patch eventsource so Bun stops breaking the runtime integration job (#6334)
## What

Fixes the intermittently-red `test / integration / runtime` **bun** leg.
Three commits, smallest blast radius first:

1. **`ci(runtime)`** — pin `bun-version` from `latest` to `1.3.14` so a
Bun release can't change module-resolution behaviour between runs. (Only
`bun-version: latest` in the repo.)
2. **`fix(deps)`** — **this is the actual fix.** Patch
`eventsource@3.0.7` to drop its `bun` export condition, via `pnpm patch`
+ `patchedDependencies`.
3. **`refactor(runtime)`** — module-graph hygiene: load the MCP SSE
transport lazily. Explicitly **not** a behaviour fix; commit 2 is.

## Root cause

```
TypeError: require() async module ".../eventsource@3.0.7/node_modules/eventsource/dist/index.js" is unsupported. use "await import()" instead.
    at .../@modelcontextprotocol/sdk/dist/cjs/client/sse.js:4:7
    at .../@ag-ui/mcp-apps-middleware/dist/index.js:1:983
    at processTicksAndRejections (unknown:7:39)
```

- `eventsource@3.0.7` maps its `bun` export condition to the **ESM**
build (`dist/index.js`). Bun resolves `bun` **before** `require`, so a
CJS `require("eventsource")` receives an async ESM module and throws.
The package ships a real CJS build (`dist/index.cjs`) behind `require`,
but Bun never reaches it.
- Two CJS consumers in our graph hit this: the MCP SDK's own
`dist/cjs/client/sse.js`, and `@ag-ui/mcp-apps-middleware@0.0.3` — a
CJS-only package (`main: ./dist/index.js`, no `exports`, no `type:
module`) that `require`s that SDK path unconditionally at module load.
- **Why intermittent:** it's a load-order race. If the ESM graph fully
evaluates `eventsource` first, the later CJS `require` can be served
synchronously and the run passes; otherwise it throws.

Dropping the `bun` key makes Bun fall through to `import` for ESM
consumers (same `dist/index.js` as before — no behaviour change) and to
`require` for CJS consumers (`dist/index.cjs`, which is what they need).
Only `bun` is touched; `deno`/`source`/`import`/`require`/`default` are
left alone.

**A version bump is not an alternative:** `eventsource@4.1.0` still
ships the same `bun` → ESM mapping.

## Patch diff

`patches/eventsource@3.0.7.patch` (header abridged — the file carries
the full rationale and an explicit deletion criterion so it doesn't
become permanent by accident):

```diff
# Drops the `bun` export condition from eventsource.
# ...
# DELETE THIS PATCH WHEN: eventsource drops the `bun` condition or points it at
# dist/index.cjs, OR Bun stops preferring `bun` over `require` for CJS requires.
diff --git a/package.json b/package.json
@@ -10,7 +10,6 @@
   "exports": {
     ".": {
       "deno": "./dist/index.js",
-      "bun": "./dist/index.js",
       "source": "./src/index.ts",
       "import": "./dist/index.js",
       "require": "./dist/index.cjs",
```

Root `package.json` gains:

```json
"patchedDependencies": { "eventsource@3.0.7": "patches/eventsource@3.0.7.patch" }
```

This repo had no `patches/` precedent (it uses `pnpm.overrides`), so
this sets one — hence the minimal one-line patch and the documented
removal criterion.

## Red-green proof

All four states. Local runs are the **same command on the same
machine**, differing only by whether the patch is applied. Bun 1.3.14,
macOS arm64, run from `packages/runtime`:

```sh
bun test src/v2/runtime/__tests__/integration/bun/bun-servers.integration.test.ts
```

A single green run proves nothing here — it's a race — so both local
states are N=20.

### 1. CI-RED

- This branch before the patch, run
[30835752558](https://github.com/CopilotKit/CopilotKit/actions/runs/30835752558)
@ `5456e308b9` — `runtime / node` success, **`runtime / bun` failure**:

```
4 | const eventsource_1 = require("eventsource");
TypeError: require() async module "/home/runner/work/CopilotKit/CopilotKit/node_modules/.pnpm/eventsource@3.0.7/node_modules/eventsource/dist/index.js" is unsupported. use "await import()" instead.
 0 pass
 1 fail
```

- Also on `main` @ `26a23bbf3a`, run
[30825667393](https://github.com/CopilotKit/CopilotKit/actions/runs/30825667393)
— same leg, same failure.

### 2. LOCAL-RED (eventsource UNPATCHED, N=20)

```
run  1:  72 pass  0 fail
run  2:   0 pass  1 fail
run  3:   0 pass  1 fail
run  4:   0 pass  1 fail
run  5:   0 pass  1 fail
run  6:   0 pass  1 fail
run  7:   0 pass  1 fail
run  8:   0 pass  1 fail
run  9:   0 pass  1 fail
run 10:  72 pass  0 fail
run 11:   0 pass  1 fail
run 12:   0 pass  1 fail
run 13:  72 pass  0 fail
run 14:   0 pass  1 fail
run 15:   0 pass  1 fail
run 16:  72 pass  0 fail
run 17:   0 pass  1 fail
run 18:  72 pass  0 fail
run 19:   0 pass  1 fail
run 20:   0 pass  1 fail
LOCAL-RED TOTAL: pass=5 fail=15  (out of 20)
```

### 3. LOCAL-GREEN (eventsource PATCHED, N=20)

```
run  1:  72 pass  0 fail
run  2:  72 pass  0 fail
run  3:  72 pass  0 fail
run  4:  72 pass  0 fail
run  5:  72 pass  0 fail
run  6:  72 pass  0 fail
run  7:  72 pass  0 fail
run  8:  72 pass  0 fail
run  9:  72 pass  0 fail
run 10:  72 pass  0 fail
run 11:  72 pass  0 fail
run 12:  72 pass  0 fail
run 13:  72 pass  0 fail
run 14:  72 pass  0 fail
run 15:  72 pass  0 fail
run 16:  72 pass  0 fail
run 17:  72 pass  0 fail
run 18:  72 pass  0 fail
run 19:  72 pass  0 fail
run 20:  72 pass  0 fail
LOCAL-GREEN TOTAL: pass=20 fail=0  (out of 20)
```

**5/20 → 20/20.**

### 4. CI-GREEN

The `test / integration / runtime` bun leg on this PR is the
load-bearing evidence. See checks below.

## Clean-install verification

A patch that only works incrementally is worthless in CI, so this was
verified from scratch — every `node_modules` in the workspace deleted,
then `pnpm install --frozen-lockfile`:

- Install exited **0** with `--frozen-lockfile` (lockfile is
self-consistent; no drift).
- Exactly one `eventsource` entry in the store, and it is the patched
one:

`node_modules/.pnpm/eventsource@3.0.7_patch_hash=427032a8df76e38f39988ff5fb919a02ccb70eb8a235e2b484007e4bebb1e67e/`
- Resolved `package.json` in the store after clean install:

`{"deno":"./dist/index.js","source":"./src/index.ts","import":"./dist/index.js","require":"./dist/index.cjs","default":"./dist/index.js"}`
— `bun` absent, everything else intact.
- Lockfile records it deterministically:
`patchedDependencies.eventsource@3.0.7` with `hash: 427032a8...` and
`path: patches/eventsource@3.0.7.patch`, and the dependency edge
resolves as `eventsource@3.0.7(patch_hash=427032a8...)`.
- `--frozen-lockfile` accepted the lockfile verbatim (it does not
rewrite), so the lockfile is self-consistent with the manifests.
- The comment header on the patch file does not break pnpm's patch
applier.
- **Lockfile diff is scoped to eventsource — 9 lines, 3 hunks, nothing
else.** An earlier revision of this branch carried incidental drift
(`vue-component-type-helpers` 3.3.8→3.3.9 and a `vite` peer-range
narrowing) picked up by a non-frozen install; that has been reverted so
the diff contains only the patch wiring.

## Tests

All from `packages/runtime`, with the patch applied:

| Suite | Command | Result |
|---|---|---|
| Full runtime suite | `pnpm exec vitest run` | **130 files / 1835 tests
passed**, 0 failed |
| Node integration (other CI leg) | `pnpm exec vitest run
src/v2/runtime/__tests__/integration/node-servers.integration.test.ts` |
**153 passed** |
| MCP + SSE transport | `pnpm exec vitest run
src/agent/__tests__/mcp-servers-integration.test.ts
src/agent/__tests__/mcp-clients.test.ts
src/v2/runtime/__tests__/mcp-apps-middleware-integration.test.ts` | **3
files / 22 passed** |
| Bun integration | `bun test .../bun-servers.integration.test.ts` |
**20/20** (was 5/20) |

Non-Bun consumers are unaffected by construction — Node never reads the
`bun` export condition — and the Node suites above confirm it. The SSE
path stays covered: `mcp-servers-integration.test.ts` exercises
`mcpServers: [{ type: "sse", url }]`, so it executes the new `await
import()`, which sits **outside** the `try/catch` that swallows
per-server connection failures.

## Module-graph proof for commit 3

Commit 3 is hygiene, so it gets its own narrower proof. Probe: Bun
populates `require.cache` with the resolved path of every module
actually loaded, so importing one module and inspecting that cache shows
whether `eventsource` entered the graph. Two controls run every time so
it can't pass vacuously.

```ts
const target = process.argv[2]!;
await import(target);
const keys = Object.keys(require.cache).filter(
  (k) => /eventsource/.test(k) && !/eventsource-parser/.test(k),
);
console.log(`${target}\n  eventsource loaded: ${keys.length > 0 ? "YES" : "NO"}`);
```

| Module | before commit 3 | after commit 3 |
|---|---|---|
| `@copilotkit/shared` (negative control) | NO | NO |
| `@modelcontextprotocol/sdk/client/sse.js` (positive control) | YES |
YES |
| `../src/agent/index.ts` (subject, non-SSE path) | **YES** | **NO** |

Both controls hold steady; only the subject flips. Measured on its own,
commit 3 does **not** move the bun pass rate (5/20 before, 3/20 after
within noise) — which is exactly why commit 2 exists.

## Typing

No `as any`, no `@ts-ignore`. `const { SSEClientTransport } = await
import(...)` keeps the class fully typed — TypeScript resolves
dynamic-import types statically. `packages/runtime/tsconfig.json`
already sets `"module": "es2022"` with the comment *"so dynamic import()
typechecks"*, so the pattern is anticipated.

Two adjacent bare `let` declarations (`transport`, `mcpClient`) gained
explicit annotations (`MCPTransport | undefined`, `MCPClient`) because
editors surface them as implicit-any suggestions. Both pre-existed on
`main`. Verified: `tsc --noEmit` clean; `tsc --noEmit --strict` error
set **identical to baseline** (3 pre-existing unrelated `TS2769`s);
`oxlint` warnings **unchanged from baseline** (2, both pre-existing).

`SSEClientTransport` is `@deprecated` in SDK 1.29.0 in favour of
`StreamableHTTPClientTransport`. That deprecation pre-exists on `main`
and is left alone: `type: "sse"` is documented public config, SSE and
Streamable HTTP are different wire protocols, and the SDK's own note
says clients "may need to support both transports during the migration
period." Migrating is a user-facing change for its own PR.

## Gates run

- `pnpm exec oxfmt --check packages/runtime/src/agent/index.ts` — clean
- `pnpm exec oxlint packages/runtime/src/agent/index.ts` — 0 errors, 2
warnings (both pre-existing on `main`)
- `pnpm nx run @copilotkit/runtime:check-types` — pass
- `pnpm exec commitlint --from HEAD~3 --to HEAD` — pass
- `pnpm install --frozen-lockfile` from a fully wiped workspace — exit 0
2026-08-03 13:33:48 -07:00

182 lines
7.1 KiB
JSON

{
"name": "CopilotKit",
"private": true,
"scripts": {
"build": "nx run-many -t build --projects=packages/** --exclude=@copilotkit/demo-agents",
"build:examples": "nx run-many -t build --projects=examples/**",
"build:storybook": "nx run-many -t storybook:build --projects=examples/v2/*/storybook",
"build:vue": "nx run @copilotkit/vue:build",
"check-types": "nx run-many -t check-types",
"clean": "git clean -fdX --exclude=\"!.env\"",
"lint": "oxlint .",
"format": "oxfmt --write .",
"check-format": "oxfmt --check .",
"test": "nx run-many -t test --projects=packages/**",
"test:watch": "pnpm run test && nx watch --all -- pnpm run test",
"test:coverage": "nx run-many -t test:coverage --projects=packages/**",
"dev": "pnpm run build && nx watch --projects=packages/** -- pnpm run build",
"dev:examples": "pnpm run build:examples && nx watch --projects=examples/** -- pnpm run build:examples",
"storybook:angular": "pnpm -C examples/v2/angular/storybook dev",
"storybook:react": "pnpm -C examples/v2/react/storybook dev",
"storybook:vue": "nx run @copilotkit-storybook/vue:dev",
"storybook": "pnpm storybook:react",
"docs": "pnpm -C examples/v2/docs dev",
"demo:angular": "nx run-many -t dev --projects=examples/v2/angular/demo,examples/v2/angular/demo-server",
"demo:react": "pnpm -C examples/v2/react/demo dev",
"demo:vue": "nx run @copilotkit/vue-demo:dev",
"release:prepare:dry": "tsx scripts/release/prepare-release.ts --scope monorepo --bump patch --dry-run",
"release:prerelease:dry": "tsx scripts/release/prerelease.ts --scope monorepo --dry-run",
"verify:channels-umbrella": "nx run @copilotkit/channels:build && tsx scripts/release/verify-channels-umbrella.ts",
"verify:channels-umbrella:registry": "nx run @copilotkit/channels:build && tsx scripts/release/verify-channels-umbrella.ts --registry",
"verify:angular-package": "nx run @copilotkit/angular:build && tsx scripts/release/verify-angular-package.ts",
"verify:runtime-package": "nx run @copilotkit/runtime:build && tsx scripts/release/verify-runtime-package.ts",
"prepare": "lefthook install",
"graph": "nx graph",
"publint": "nx run-many -t publint --projects=packages/**",
"attw": "nx run-many -t attw --projects=packages/**",
"check:packages": "nx run-many -t publint,attw --projects=packages/**",
"validate:model-names": "tsx scripts/validate-doc-model-names.ts",
"check:plugin-skills": "tsx scripts/sync-plugin-skills.ts --check",
"generate:channel-native-catalogs": "node scripts/channel-native-catalogs.mjs generate",
"check:channel-native-catalogs": "node scripts/channel-native-catalogs.mjs check",
"audit:channel-native-catalogs": "node scripts/channel-native-catalogs.mjs audit",
"sync:plugin-skills": "tsx scripts/sync-plugin-skills.ts",
"parity:sync": "tsx examples/integrations/_parity/sync.ts",
"parity:verify": "tsx examples/integrations/_parity/verify.ts",
"parity:check": "tsx examples/integrations/_parity/verify.ts --no-color"
},
"devDependencies": {
"@anthropic-ai/claude-code": "2.1.207",
"@arethetypeswrong/cli": "^0.18.2",
"@commitlint/cli": "^20.3.0",
"@commitlint/config-conventional": "^20.3.0",
"@size-limit/file": "12.1.0",
"@storybook/addon-docs": "^10.2.10",
"@storybook/addon-webpack5-compiler-swc": "^4.0.2",
"@storybook/react-webpack5": "^10.2.10",
"@types/jscodeshift": "^17.3.0",
"@types/node": "^18.11.17",
"@types/semver": "^7.7.1",
"@vitest/coverage-v8": "^3.2.4",
"browserslist": "^4.24.0",
"danger": "^12.3.3",
"es-check": "9.6.4",
"glob": "^10.3.12",
"install": "^0.13.0",
"jscodeshift": "^17.3.0",
"lefthook": "^2.1.1",
"npm": "^10.7.0",
"nx": "22.7.5",
"oxfmt": "^0.36.0",
"oxlint": "^1.51.0",
"publint": "^0.3.17",
"remark": "^15.0.1",
"remark-mdx": "^3.1.1",
"remark-parse": "^11.0.0",
"semver": "^7.8.1",
"size-limit": "12.1.0",
"storybook": "^10.2.10",
"ts-node": "^10.9.2",
"tsdown": "^0.20.3",
"tsx": "^4.21.0",
"typescript": "^5.2.3",
"unified": "^11.0.5",
"unist-util-visit": "^5.1.0",
"vitest": "^4.1.3"
},
"engines": {
"node": ">=18"
},
"packageManager": "pnpm@10.33.4",
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"react",
"react-dom"
],
"allowedVersions": {
"react": "*",
"react-dom": "*"
}
},
"overrides": {
"@ag-ui/mcp-middleware>@ag-ui/client": "0.0.53",
"streamdown>react": "^19.0.0",
"@types/react": "19.1.8",
"@types/react-dom": "^19.0.2",
"react": "19.2.3",
"react-dom": "19.2.3",
"next@<=15.4.11": "15.4.11",
"next@>=15.5.0 <15.5.15": "15.5.15",
"send@<=0.19.0": "0.19.0",
"path-to-regexp@<=0.1.12": "0.1.13",
"serve-static@<=1.16.0": "1.16.0",
"prismjs@<=1.30.0": "1.30.0",
"pino@<=10.1.1": "10.1.1",
"@copilotkit/license-verifier": "~0.5.0",
"next": "^16.0.10",
"defu@<=6.1.4": ">=6.1.5",
"minimatch": ">=9.0.6",
"minimatch@>=10.0.0 <10.2.1": ">=10.2.1",
"handlebars": ">=4.7.9",
"axios": ">=1.15.0",
"tar": ">=7.5.11",
"node-forge": ">=1.4.0",
"hono": ">=4.11.7",
"dompurify": ">=3.3.2",
"vite@>=6.0.0 <6.4.2": ">=6.4.2",
"vite@>=7.0.0 <7.3.2": "7.3.2",
"esbuild": ">=0.25.4",
"fast-xml-parser": ">=4.5.2",
"lodash": ">=4.18.1",
"lodash-es": ">=4.18.1",
"basic-ftp": ">=5.2.0",
"@hono/node-server": ">=1.19.13",
"flatted": ">=3.4.0",
"body-parser": ">=1.20.3",
"serialize-javascript": ">=7.0.3",
"socket.io-parser": ">=4.2.6",
"svgo": ">=3.3.3",
"@isaacs/brace-expansion": ">=5.0.1",
"@modelcontextprotocol/sdk": ">=1.26.0",
"immutable@>=3.0.0 <3.8.3": ">=3.8.3",
"immutable@>=5.0.0 <5.1.5": ">=5.1.5",
"rollup@>=4.0.0 <4.59.0": ">=4.59.0",
"brace-expansion@>=2.0.0 <2.0.3": ">=2.0.3",
"ajv@>=8.0.0 <8.18.0": ">=8.18.0",
"bn.js": ">=5.2.3",
"picomatch@>=4.0.0 <4.0.4": ">=4.0.4",
"js-yaml": ">=4.1.1",
"jsondiffpatch": ">=0.7.2",
"yaml@>=2.0.0 <2.8.3": ">=2.8.3",
"zod": ">=3.22.3",
"cookie": ">=0.7.0",
"diff@>=4.0.0 <4.0.4": ">=4.0.4",
"diff@>=5.0.0 <5.2.2": ">=5.2.2",
"diff@>=8.0.0 <8.0.3": ">=8.0.3",
"qs": ">=6.14.2",
"@octokit/plugin-paginate-rest": ">=9.2.2",
"@octokit/request": ">=8.4.1",
"@octokit/request-error": ">=5.1.1",
"express": ">=4.20.0",
"@tootallnate/once": ">=3.0.1",
"file-type": ">=21.3.1",
"@langchain/community": ">=1.1.14",
"langsmith": ">=0.5.18",
"next@>=16.0.0 <16.2.3": "16.2.3",
"validator": ">=13.15.20",
"markdown-it": ">=14.1.1",
"mdast-util-to-hast@>=13.0.0 <13.2.1": ">=13.2.1",
"@smithy/config-resolver": ">=4.4.0",
"defu": ">=6.1.5",
"ai": ">=5.0.52",
"@ai-sdk/mcp": "1.0.21",
"storybook@>=8.0.0 <8.6.17": "8.6.17",
"path-to-regexp@>=8.0.0 <8.4.0": ">=8.4.0"
},
"patchedDependencies": {
"eventsource@3.0.7": "patches/eventsource@3.0.7.patch"
}
}
}