From 6c6a142a9c7b613532340beb80fe4d3fb2a184ca Mon Sep 17 00:00:00 2001 From: Ricky Schema Cascade Date: Wed, 13 May 2026 21:26:54 +0200 Subject: [PATCH] fix(persona-kit): defer @relayfile/local-mount import to call site MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `@relayfile/local-mount` imports `@parcel/watcher` at module top, which loads a per-platform native binary at evaluation time (e.g. `@parcel/watcher-linux-x64-glibc` on Lambda). Eagerly importing it from `mount.ts` meant every `import('@agentworkforce/persona-kit')` evaluated the binary load — including server-side validation paths that never call `applyPersonaMount`. Concrete blast: cloud's deploy POST does `await import("@agentworkforce/persona-kit")` to call `parsePersonaSpec` on the uploaded bundle. The Lambda runtime is linux-x64-glibc, but OpenNext's tracer doesn't bundle @parcel/watcher's optional native prebuild, so the import resolution throws: "Persona validation is unavailable: @agentworkforce/persona-kit could not be loaded ... No prebuild or local build of @parcel/watcher found. Tried @parcel/watcher-linux-x64-glibc." Fix: lazy-import `@relayfile/local-mount` inside a small helper that `applyPersonaMount` awaits at call time. Mount is a CLI/runtime concern, not a server concern, so deferring is correct semantically and lets the native binary stay un-loaded on hosts that never apply a mount. The barrel `index.ts` re-exports stay intact — back-compat for any direct consumer of `applyPersonaMount`. The change is purely the import timing inside `mount.ts`. Verified: * 170/170 persona-kit tests pass. * 544 tests across the whole workforce repo pass (`pnpm run test`). * Probe script: `import('@agentworkforce/persona-kit')` followed by `parsePersonaSpec(...)` succeeds with NO `@parcel/watcher` load on the call path. Co-Authored-By: Claude Opus 4.7 --- packages/persona-kit/src/mount.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/persona-kit/src/mount.ts b/packages/persona-kit/src/mount.ts index ae8f340..e0660ab 100644 --- a/packages/persona-kit/src/mount.ts +++ b/packages/persona-kit/src/mount.ts @@ -1,6 +1,23 @@ -import { createMount } from '@relayfile/local-mount'; import type { ResolvedMountPolicy } from './plan.js'; +// `@relayfile/local-mount` pulls in `@parcel/watcher`, which loads a +// per-platform native binary at module evaluation time +// (`@parcel/watcher-linux-x64-glibc`, `-darwin-arm64`, …). When +// persona-kit is loaded server-side just to call `parsePersonaSpec` +// (e.g. cloud's `import('@agentworkforce/persona-kit')` to validate a +// deploy bundle), eagerly importing local-mount fails any host that +// doesn't ship a matching prebuild — most notably AWS Lambda's +// `@parcel/watcher-linux-x64-glibc`, which OpenNext doesn't bundle. +// +// Deferring the local-mount import to `applyPersonaMount`'s call site +// means the native binary only loads when a mount is actually being +// applied (which is a CLI/runtime concern, never a server-side +// validation concern). +async function loadCreateMount(): Promise { + const mod = await import('@relayfile/local-mount'); + return mod.createMount; +} + export interface PersonaMountHandle { /** * Working directory the harness should be spawned in. When the mount is @@ -68,6 +85,7 @@ export async function applyPersonaMount( 'applyPersonaMount: options.personaId is required when a mount policy is supplied' ); } + const createMount = await loadCreateMount(); const handle = await createMount(options.cwd, options.mountDir, { ignoredPatterns: [...mount.ignoredPatterns], readonlyPatterns: [...mount.readonlyPatterns],