Skip to content

[AAASM-3532] 🐛 (adapters): Patch frameworks via shim instead of mutating frozen ESM#175

Merged
Chisanan232 merged 3 commits into
masterfrom
v0.0.1/AAASM-3532/fix/adapter_frozen_esm
Jun 22, 2026
Merged

[AAASM-3532] 🐛 (adapters): Patch frameworks via shim instead of mutating frozen ESM#175
Chisanan232 merged 3 commits into
masterfrom
v0.0.1/AAASM-3532/fix/adapter_frozen_esm

Conversation

@Chisanan232

Copy link
Copy Markdown
Contributor

Target

  • Task summary:

    Fix the Vercel AI SDK (and audit the other framework) adapters crashing with
    Cannot assign to read only property 'tool' when a consumer has the real ai
    5.x/6.x installed. initAssembly() patched governance by assigning onto the
    frozen ES-module namespace, which throws.

  • Task tickets:

  • Key point change:

    • Root cause: src/hooks/ai-sdk.ts did module.tool = createPatchedToolFactory(...).
      A real ai package loaded via import() is an ES module whose namespace is a frozen
      exotic object — named exports are non-writable — so the assignment throws.
    • Fix: new applyGovernedToolFactory() installs the governed factory without
      mutating the frozen namespace
      . It assigns in place only for writable plain objects
      (the unit suite's loadModule fakes); for the frozen-ESM case it falls back to a
      mutable shim copy { ...module, tool: governed } (the AAASM-3525 approach). A new
      mutatedOriginal flag makes unpatch restore the original only when it was mutated.
    • Audit result: only the Vercel adapter mutated the module namespace. langgraph.ts,
      mastra.ts, and openai-agents.ts mutate a class prototype (e.g.
      module.StateGraph.prototype.compile), not the namespace binding, which is a normal
      mutable object — verified safe through a frozen namespace, so they need no change.
    • Test env: ai is now an installable devDependency (removed from the .pnpmfile.cjs
      strip list) so the new regression test exercises the real frozen ai module.

Effecting Scope

  • Action Types:
    • 🔧 Fixing bug
      • 🟢 No breaking change
  • Scopes:
    • 🤖 Framework hooks
    • 🧪 Testing
      • 🧪 Unit testing
    • 🚀 Building
      • 🔗 Dependencies
      • 📦 Project configurations
  • Additional description:
    No public API change; the SDK now patches the real ai package without crashing.

Description

  • src/hooks/ai-sdk.ts:185,210 — replace the frozen-namespace mutation with a shim-aware
    applyGovernedToolFactory(); unpatch only restores when the original was mutated in place.
  • tests/vercel-ai-real-module.test.ts — new regression suite driving patchVercelAiSdk
    against the real frozen ai namespace via the default loader (not a shim/fake): it
    proves patching no longer crashes, the real ai.tool export is untouched, and deny/allow
    governance flows through the governed factory. This test crashes under the pre-fix code.
  • .pnpmfile.cjs / package.json — install real ai (^6.0.0) as a devDependency; keep
    @langchain/langgraph / @mastra/core stripped (their adapters never crashed).

Validation

pnpm install, pnpm test (330 passed, 2 skipped), pnpm lint, pnpm typecheck, pnpm build — all green.

Closes AAASM-3532

🤖 Generated with Claude Code

Chisanan232 and others added 3 commits June 22, 2026 14:10
The Vercel adapter assigned `module.tool = governed` onto the loaded `ai`
module. When a consumer has the real `ai` 5.x/6.x installed, its namespace is
a frozen ES-module exotic object whose named exports are non-writable, so the
assignment throws `Cannot assign to read only property 'tool'` and crashes
`initAssembly()`.

Install the governed factory through `applyGovernedToolFactory`: assign in
place only for writable plain objects (the unit suite's loadModule fakes), and
otherwise fall back to a mutable shim copy `{ ...module, tool: governed }` —
the approach the AAASM-3525 driver proved works — never touching the frozen
namespace. `unpatch` restores the original only when it was mutated in place.

Refs AAASM-3532

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `.pnpmfile.cjs` from PR #174 stripped `ai` from this repo's install to
avoid the frozen-ESM crash in the unit suite. With AAASM-3532 fixed, that
workaround is no longer needed for `ai`: add `ai` as a devDependency and drop
it from the strip list so the real-module regression test exercises the genuine
frozen namespace. `@langchain/langgraph` / `@mastra/core` stay stripped — their
adapters mutate a class prototype (not the namespace) and never crashed.

Refs AAASM-3532

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drive `patchVercelAiSdk` against the real, frozen `ai` namespace via the
default loader (no loadModule fake) and assert: patching no longer crashes,
the original `ai.tool` export is left untouched, and governance (deny/allow)
flows through the governed factory on the shim copy. This test crashes with
`Cannot assign to read only property 'tool'` under the pre-fix code. Reset the
new `mutatedOriginal` patch-state field in the existing suite's teardown.

Refs AAASM-3532

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.33333% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/hooks/ai-sdk.ts 93.33% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@sonarqubecloud

Copy link
Copy Markdown

@Chisanan232

Copy link
Copy Markdown
Contributor Author

Claude Code — review result

All CI checks green, mergeable (state BLOCKED = awaiting the required approval only).

Root cause: the Vercel AI adapter assigned module.tool = governed directly onto the frozen ai ESM namespaceTypeError at runtime (surfaced when ai@6 became installable).

Fix reviewed: applyGovernedToolFactory() assigns in place only for writable plain objects; for a frozen/readonly namespace it falls back to a mutable shim copy ({ ...module, tool: governed }) and records mutatedOriginal for correct unpatch. The audit confirming langgraph.ts / mastra.ts / openai-agents.ts mutate class prototypes (writable, unaffected) is the right scoping — no over-reach.

Coverage: new tests/vercel-ai-real-module.test.ts exercises the real frozen-ESM path (would have caught the original crash); ai@^6 is now an installable devDep. 330 passed / 2 skipped; lint/typecheck/build green.

Ready to merge. Closes AAASM-3532.

@Chisanan232 Chisanan232 merged commit 2994ce1 into master Jun 22, 2026
16 checks passed
@Chisanan232 Chisanan232 deleted the v0.0.1/AAASM-3532/fix/adapter_frozen_esm branch June 22, 2026 07:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant