diff --git a/src/server/features/sam/SamChatAgent.ts b/src/server/features/sam/SamChatAgent.ts index aea921d..c2ddd4a 100644 --- a/src/server/features/sam/SamChatAgent.ts +++ b/src/server/features/sam/SamChatAgent.ts @@ -78,6 +78,12 @@ type SamContext = { * the app DB, so every session in a project shares them. */ export class SamChatAgent extends Think { + // SAM's toolset is the MCP tools from beforeTurn; it has no use for Think's + // workspace bash tool, whose just-bash dependency is stubbed out of the + // bundle anyway (see vite.config.ts) to keep ~30 MB of eagerly-evaluated + // source out of every isolate's baseline heap. + override workspaceBash = false; + // Session row + project, resolved once per DO lifetime (the binding is // immutable). Null until a turn/provider needs it — and left null when the // registry row is gone, which beforeTurn turns into a polite refusal. diff --git a/src/server/lib/just-bash-stub.ts b/src/server/lib/just-bash-stub.ts new file mode 100644 index 0000000..22325a8 --- /dev/null +++ b/src/server/lib/just-bash-stub.ts @@ -0,0 +1,33 @@ +/** + * Build-time stand-in for `just-bash`, wired up via the vite.config.ts alias. + * + * `@cloudflare/think` eagerly imports just-bash (~21 MB of source, plus + * turndown and the 8.6 MB @mixmark-io/domino DOM implementation) at module + * init, and SamChatAgent is re-exported from src/server.ts, so the whole chain + * lands in the main worker's startup module graph — raising every isolate's + * baseline heap toward the 128 MB limit (production OOM bursts on unrelated + * routes after each deploy). SAM only uses its own MCP toolset and disables + * Think's workspace bash tool (`workspaceBash = false`), so the real library + * is unreachable; this stub keeps it out of the bundle entirely. + * + * Remove once https://github.com/cloudflare/agents/issues/1673 lands and + * @cloudflare/think loads just-bash lazily. + */ +const STUBBED_MESSAGE = + "just-bash is stubbed out of the worker bundle (see vite.config.ts); " + + "the workspace bash tool is disabled for this deployment"; + +// Covers every named import in the dependency graph: `Bash` (Think's +// workspace bash tool) and `defineCommand` (agents/skills bash scripts). +// Both are only reached when a model invokes bash, which SAM never exposes. +// Must stay a class: call sites construct it with `new Bash({...})`. +// oxlint-disable-next-line typescript-eslint/no-extraneous-class +export class Bash { + constructor() { + throw new Error(STUBBED_MESSAGE); + } +} + +export function defineCommand(): never { + throw new Error(STUBBED_MESSAGE); +} diff --git a/src/server/workflows/site-audit-workflow-helpers.ts b/src/server/workflows/site-audit-workflow-helpers.ts index 82f7ec5..5b61602 100644 --- a/src/server/workflows/site-audit-workflow-helpers.ts +++ b/src/server/workflows/site-audit-workflow-helpers.ts @@ -1,4 +1,3 @@ -import { analyzeHtml } from "@/server/lib/audit/page-analyzer"; import type { StepPageResult } from "@/server/lib/audit/types"; import { isSameOrigin, normalizeUrl } from "@/server/lib/audit/url-utils"; @@ -31,6 +30,11 @@ export async function crawlPage( } const html = await response.text(); + // Dynamic import keeps cheerio (page-analyzer's HTML parser) out of the + // worker's startup module graph: SiteAuditWorkflow is re-exported from + // src/server.ts, so a static import would evaluate cheerio in every + // isolate's baseline heap, not just when an audit actually crawls. + const { analyzeHtml } = await import("@/server/lib/audit/page-analyzer"); const analysis = analyzeHtml( html, finalUrl, diff --git a/vite.config.ts b/vite.config.ts index 3e3dc65..26856d1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,3 +1,4 @@ +import { fileURLToPath } from "node:url"; import { tanstackStart } from "@tanstack/react-start/plugin/vite"; import { defineConfig, loadEnv } from "vite"; import tsConfigPaths from "vite-tsconfig-paths"; @@ -23,17 +24,19 @@ export default defineConfig(({ mode }) => { return { resolve: { alias: { - // TODO: Remove this workaround once fixed upstream — either turndown - // drops the bare require from its ESM build, or @cloudflare/think - // stops eagerly importing just-bash/turndown at module init + // TODO: Remove this workaround once @cloudflare/think stops eagerly + // importing just-bash at module init // (https://github.com/cloudflare/agents/issues/1673). // - // turndown's ESM build (pulled in via just-bash's html-to-markdown - // command) contains a bare CommonJS `require("@mixmark-io/domino")` - // that the Cloudflare Workers runtime rejects at deploy time (error - // 10021). Its CJS build goes through Vite's CommonJS transform, which - // rewrites that require into a bundled import. - turndown: "turndown/lib/turndown.cjs.js", + // just-bash (plus its turndown → @mixmark-io/domino chain, ~30 MB of + // source) is only used by Think's workspace bash tool, which SAM + // disables — but the eager import drags it into the main worker's + // startup module graph, inflating every isolate's baseline heap + // toward the 128 MB limit (production OOM bursts on unrelated + // routes). Alias it to a throwing stub so it never ships. + "just-bash": fileURLToPath( + new URL("./src/server/lib/just-bash-stub.ts", import.meta.url), + ), }, }, envPrefix: [