-
Notifications
You must be signed in to change notification settings - Fork 117
Description
Description
Importing anything from deepagents in a Vite browser build fails with hard errors. The built dist/index.js is a single flat bundle that contains named imports from Node.js builtins. Vite externalizes node:* modules for browser compatibility, but named imports (as opposed to default imports) cause Rollup to fail because it verifies that the binding exists in the externalized module.
The library's architecture already supports browser use — BackendProtocol, StateBackend, StoreBackend, all middleware, and createDeepAgent have zero Node.js dependencies. The issue is purely packaging: the single barrel export bundles Node-only modules alongside browser-safe ones into a flat dist/index.js.
Package version: 1.8.1 (latest on npm)
Vite version: 7.3.1 (latest)
Reproduction
Minimal repro: https://github.com/BjoernSchotte/deepagents-vite-repro
git clone https://github.com/BjoernSchotte/deepagents-vite-repro.git
cd deepagents-vite-repro
bun install # or npm install
bun run build # or npx vite buildActual Behavior
The build fails with 3 sequential hard errors (each one hidden behind the previous):
Error 1: node:async_hooks (from @langchain/langgraph, not deepagents)
"AsyncLocalStorage" is not exported by "__vite-browser-external"
import { AsyncLocalStorage } from "node:async_hooks";
Error 2: "path" (from deepagents backends/utils.ts)
After stubbing node:async_hooks via resolve.alias:
"basename" is not exported by "__vite-browser-external"
import { basename } from "path";
Error 3: node:child_process (from deepagents backends/filesystem.ts / local-shell.ts)
After also stubbing path:
"spawn" is not exported by "__vite-browser-external"
import cp, { spawn } from "node:child_process";
After stubbing all 6 node:* modules + path via Vite resolve.alias, the build succeeds (840 modules, zero errors). The Node-only code paths are dead code when using createDeepAgent + StateBackend.
Why Named Imports Fail But Default Imports Don't
Vite externalizes unresolvable modules as __vite-browser-external (an empty object). Default imports (import fs from "node:fs") succeed because fs just becomes {} — Rollup doesn't verify property access at build time. Named imports (import { spawn } from "node:child_process") fail because Rollup verifies the binding exists as an export.
The built dist/index.js lines 5–18:
import micromatch from "micromatch"; // ✅ default import, warning only
import { basename } from "path"; // ❌ named import, HARD FAIL
import fs from "node:fs/promises"; // ✅ default import, warning only
import fs$1 from "node:fs"; // ✅ default import, warning only
import path from "node:path"; // ✅ default import, warning only
import cp, { spawn } from "node:child_process"; // ❌ named import `spawn`, HARD FAIL
import fg from "fast-glob"; // ✅ default import, warning only
import os from "node:os"; // ✅ default import, warning onlyOnly 2 of the 14 Node.js-related imports in dist/index.js cause hard build failures.
Suggested Fix
Minimal fix (one-line change)
Replace import { basename } from "path" in backends/utils.ts (line 10) with an inline function:
- import { basename } from "path";
+ function basename(p: string): string {
+ const i = p.lastIndexOf("/");
+ return i === -1 ? p : p.slice(i + 1);
+ }This eliminates Error 2. Error 3 (spawn) would still require consumer-side aliasing, but consumers would only need to stub node:child_process (and node:async_hooks which is a @langchain/langgraph issue, not deepagents).
Proper fix: deepagents/browser subpath export
Add a src/browser.ts barrel that re-exports only browser-safe modules (everything except config.ts, FilesystemBackend, LocalShellBackend, skills/loader.ts, middleware/agent-memory.ts). Add a second tsdown entry point and update the exports map:
"./browser": {
"import": { "types": "./dist/browser.d.ts", "default": "./dist/browser.js" }
}This would let browser consumers write:
import { createDeepAgent, StateBackend } from "deepagents/browser";with zero Vite configuration needed.
Workaround
Add resolve.alias entries in vite.config.ts to stub the Node builtins with modules that provide the named exports Rollup needs:
resolve: {
alias: {
'node:async_hooks': './src/stubs/async-hooks.ts', // for @langchain/langgraph
'node:fs/promises': './src/stubs/fs-promises.ts',
'node:fs': './src/stubs/fs.ts',
'node:path': './src/stubs/path.ts',
'node:child_process': './src/stubs/child-process.ts',
'node:os': './src/stubs/os.ts',
'path': './src/stubs/path.ts',
}
}The stubs provide the named exports so the build succeeds; the Node-only code is dead code when using createDeepAgent + StateBackend.