Repository navigation
fix(core): preserve SDK plugins across location eviction - #35725
Merged
Merged
Conversation
This was referenced Jul 19, 2026
jinhuang712
pushed a commit
to jinhuang712/opencode
that referenced
this pull request
Sep 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue for this PR
Related to #35556, but fixes a separate SDK application-lifetime defect discovered while investigating it.
Type of change
What does this PR do?
An embedded OpenCode instance previously built one graph for its SDK plugin registrar and another graph for its HTTP routes and lazy Locations. A raw mutable store bridged those graphs. Every layer wrapping that store installed a finalizer that cleared it, so evicting one Location permanently removed the host's SDK plugins.
This replaces the bridge with one scoped application layer:
SdkPlugins.layerowns a private registry; rawStore,makeStore, andlayerWithStoreare removed.createRoutes()now owns the global application services, router, and lazy Location map together.ManagedRuntimeso one application instance has one memo map and one disposal scope; lazy Locations reuse the same host-global plugin registry.DELETE /api/debug/locationendpoint (debug.evictLocationon the generated client) that disposes a Location's cached services, both as a debug tool and as the public eviction trigger the regression test needs.How did you verify your code works?
OpenCode.create()->opencode.plugin(...)-> boot a Location viaplugin.list-> evict it via the new debug endpoint -> await the rebooted Location's committed plugin generation (plugin.updatedevent) -> assert the SDK plugin is still listed. Verified this test fails on the pre-fix base (origin/v2plus only the debug-endpoint plumbing backported): the rebooted generation contains internal plugins but not the SDK plugin. It passes deterministically on this branch (3/3 runs).LocationServiceMapinvalidation. Note this one does not reproduce the original two-graph defect on its own; the sdk-next test above is the authoritative regression.cd packages/core && bun run test test/location-layer.test.ts test/config/plugin.test.ts(18 pass)cd packages/sdk-next && bun run test test/embedded.test.ts(7 pass)bun turbo typecheck --concurrency=3(31 tasks pass), plus regenerated the client viabun run generateafter the protocol changeNotes
This intentionally does not fix #35556's transient initial-generation readiness race; that remains separate.