Repository navigation
feat(plugin-host): load upstream DSH plugins on Verve - #14
Merged
Merged
Conversation
The payload carried upstream schemastery 3.18.0 and cosmokit 1.8.1, and
every importer of `@deepseek-ai/schemastery` got them. Current dsh
packages (0.2.x) are written against DeepSeek Harness's forks of the
two: their config schemas mark live fields `.volatile()` and secrets
`.role('secret')`, and `cordis-plugin-loader` imports `updateVolatile`.
None of that exists in the upstream builds, so those packages failed
to import ("not a callable function"). The forks are supersets of what
rebon's own payload uses, so both names now resolve to them.
Every bare specifier in `provided-modules.json` answered every importer with the vendored build, so a dsh package from npm importing `@deepseek-ai/dsh-session` got the 0.0.1-era copy rebon's own loop is built from and failed on exports added since (`SessionSeq`, `SessionLogOffset`, `ToolCallId`, ...). The map is now two kinds. `owned` modules are the other half of a seat rebon implements (cordis, dsh-tools' scheduler symbol, dsh-llm's adapter base, the environment shims) and answer everyone. `payload` modules are what the vendored payload is built from: they answer the payload and this runtime, while a package installed from npm resolves its own copy and falls back to the vendored one only when it has none. The installer now installs every peer that is not owned, including the real `@deepseek-ai/dsh-settings` in place of the static shim.
Current dsh packages inject services that dsh's boot layer and session model provide, which rebon's realm did not have: `dsh-tool-fs`, `dsh-tool-bash` and `dsh-tool-skill` need `workingDirectory`, and `dsh-settings` needs `configEditor`, `profileContext` and `ctx.root.loader`. Rebon is that layer, so the realm answers them from its own facts rather than mounting dsh's: - `workingDirectory` is the workspace the session's scope was opened on; changing it from a plugin is refused, since a second answer to "where is this session" would not be shared by rebon's tools; - `loader`, `configEditor` and `profileContext` are the realm's entry table. An edit is checked against the plugin's schema, persisted as that entry through the `settings` seat's new `entryConfig` (which replaces only the caller's own `kernelPlugins.plugins` row), and its volatile fields committed into the running plugin as dsh's loader does. An ordinary field waits for the next load: what an entry registers is reported once per load. The prompt and tools seats also learn what these packages use: provider-function section text, resolved once the entry has loaded, dsh's named section and context orders, and `ctx.tools.get`.
The realm refuses an entry whose required service is not mounted yet, by design, so the order of `kernelPlugins.plugins` decided whether a dsh consumer (`dsh-tool-fs`) could mount at all: written before its provider (`dsh-fs-local`), it was refused. A marketplace install lists a package's kernel plugins by name, which makes that order arbitrary. A kernel plugin's manifest can now say which realm services it injects and which it provides, and the composition loads every entry after the entries providing what it injects, keeping written order otherwise. These are ordering facts, not capabilities: services in one realm are how dsh packages reach each other, and nothing here reaches rebon.
dsh packages reach each other through a realm they share: the filesystem tools inject the `fs` service a filesystem backend provides. Installed one package at a time they land in separate containers, each with its own realm and its own copy of the seams, and a consumer can never see its provider. A package naming a dsh bundle patch (`dsh.bundle.patch`, the format of dsh's own profile bundles) is now installed as one rebon package: its dependencies and every peer they declare that rebon does not own go into one `node_modules`, `probe.mjs --bundle` mounts its rows in one realm in the order their injections allow, and each row that mounts becomes a kernel plugin of the one manifest, with its row config as its default and its `inject`/`provides`. One package is one container, so the rows share a host as they shared one in dsh. Rows that cannot run are named with the reason (a service nobody provides, a Loader expression in their config, not installed) and left out.
A dsh package's browser half reaches its Host through `ctx.remote`,
which dsh's gateway answers in one shape: `{ ok, value }` or
`{ ok: false, error: { code, message, details } }`. In rebon the plane
is the Host, so `PluginPlane::call_remote(endpoint, args)` is that
gateway. Plane facts are answered in Rust (`pluginManager`, read-only;
`credentials.describe`, which reads whether a variable is set and
never its value). Realm facts are asked of the composition as
`kind: 'remote'`: `settings.*` through upstream `dsh-settings` with
its redaction and revision fence, `pluginInventory.list`, and
`rebon/clientModules`, rebon's counterpart of `__DSH_BOOT__`. A
settings write goes to the host serving its entry; listings are
gathered from every host. The Host events a client hears are
published on `compose:remote-event`.
A sample bundle of current dsh packages, in dsh's own bundle format, is installed from the npm registry the way rebon installs one, loaded on a real plane in an order that is the reverse of what its rows need, and used: dsh-tool-fs's read and write over dsh-fs-local, the storage and skills seams from a consumer in the same realm, dsh-settings' forms described and written through the client contract and applied live, a search through dsh-web-search-deepseek sent where that write said, and the plugin manager, inventory and credentials endpoints. The rows that cannot run here are asserted by name: dsh-tool-skill needs dsh's agent loop, and the PowerShell executor a `subprocess` provider. It needs the network, so it is run by hand.
How dsh finds, serves and boots a package's browser half; its slots and where each lands in rebon's UI; the client `ctx`; the remotes wire and how the plane answers it; which process runs host halves and which should run the client realm; and the recommendation: keep dsh's client libraries unchanged and implement the slot host natively, the way su-editor implements Obsidian's app. The composition runtime's README says how current upstream packages run on it.
Installing `@deepseek-ai/dsh-base` as a bundle runs the rows that are plugins on an engine and refuses the rest. Two kinds of refusal said the wrong thing: - dsh's own services for the seats rebon implements (`llm`, `tools`, `web`, `systemPrompt`, `commands`) failed as a name collision; they are now `[REBON_PROVIDES]`, which is what they are; - a row's `disabled` was ignored, so a row the bundle turns off was mounted. A literal `true` is now `[DISABLED]`, and a Loader expression is `[CONFIG_EXPRESSION]`, as an expression in its config already was. A registry test installs dsh-base and holds which rows run and that every other one names its reason.
dsh's shell executors (`dsh-pwsh-local`, `dsh-bash-local`) run commands through `ctx.subprocess`, and dsh's own backend, `dsh-subprocess-local`, is `child_process` plus native addons (node-pty, koffi for Windows job objects). A plugin on Verve has none of those, so the shell rows of a bundle could never mount. The realm now answers the seam: `spawn` returns dsh's live handle, collected output is read by offset with the tail kept in `maxBytes`, `terminate` and the abort signal end the program, `waitForExit` waits for it; raw pipes, control channels, spill files and terminals are refused by name. The program is started by rebon through a new `process` seat: argv, cwd, stdin data, and dsh's scrubbed environment (no credential-shaped names, no `DSH_*`) plus what the plugin forwards. It answers the shared host only, since a program started for a contained plugin would be outside its container, and the tools that reach it are composition tools, which ask before they run. A package, or a bundle with a row, that injects `subprocess` asks for the seat at install. Tool executions also carry the session header dsh's shell environment reads.
Route authenticated remote events through one scoped kernel listener instead of a second publisher-side path. Preserve plane provenance, bounded delivery, explicit lag errors and symmetric shutdown cleanup. Reap spawned processes when their host stops and fix subprocess stream and cancellation boundaries. Exercise the public event transport with unchanged upstream packages, and verify the companion Verve revision in an isolated macOS dependency graph without changing production sources.
Check out Rebon-Code and the companion Verve revision as siblings so Boa inherits its own workspace metadata instead of the enclosing CLI workspace. Keep the temporary dependency graph isolated from verify. Build the working-directory contract fixture from an absolute path for the current platform instead of a Windows-only drive path.
The upstream JSON storage plugin syncs its parent directory after an atomic rename. Validate the corrected Verve revision in the isolated DSH graph while retaining the normal production dependency checks.
Use the verified Verve revision that closes server responses on peer shutdown and completes each response independently of keep-alive reuse. Keep this integration graph separate from the published dependency pin.
A worker can exit between the liveness check and the identity read. Confirm that it is no longer running before treating a missing identity as a completed exit, while preserving errors for live or unverifiable owners. Cover the observation boundaries deterministically and preserve the full error chain in stop and takeover test failures.
Prompt and projection tests protect the same process-wide switch with independent locks, or restore it without locking. Share one restoring guard across these tests and explicitly enable agents where the projection requires them. Keep the production availability policy unchanged.
Use the normal locked Verve and Boa dependency graph for DSH acceptance. Remove the companion checkout and job-local Cargo path patches so a passing integration run verifies the dependencies shipped to users.
Keep the production dependency history from the merged JIT phase 2 update before advancing Verve and Boa to the DSH-compatible runtime.
Advance Verve and its Boa crates together to the merged main revision containing upstream DSH loading and HTTP lifecycle compatibility. Keep the production dependency graph identical to DSH CI acceptance.
Quorafind
marked this pull request as ready for review
October 26, 2026 07:06
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.
Summary
Run unmodified upstream DSH npm host plugins on Rebon's Verve runtime and native Cordis plane. This implements the host side of the DSH client contract without embedding the DSH web shell.
$eventspublishing through plane-scoped kernel events and a bounded transport. Cover provenance checks, cross-plane isolation, ordering, lag, shutdown, and listener cleanup.Dependencies
e858dfcc48f6e64f9553d0f182e84ed250dbca86, the merge commit for fix(runtime): support upstream DSH package loading Quorafind/verve#6.--locked, including real npm packages, the base profile, and containment tests. Removes the separate Verve checkout and temporary path patches.CI validation
CI run 40279261725 passed for
78f4610:verifyandtest.Local validation
rebon-plugin-hostlibrary and tests, andrebon-session-runtimetests.rebon-plugin-hostlibrary tests: 111 passed.Out of scope
Native slot hosting, session-scoped RPC, waterfall remotes, settings propagation to existing workers, and Rebon Desktop DOM/drawer integration.