Repository navigation
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 Walkthrough
|
Model: Claude Opus 5.5. Rebased onto current main with Claude Opus 5.5 on 2026-10-12.
Stacked on #16054 (and #15010). Review only the top 3 commits: cc15aac.
Problem
The plugin PRs below this one let a server run trusted local plugins, but a user has no way to add, approve, enable or remove one. Every step is a raw
plugins.*request from an administrative connection. A plugin whose event delivery keeps failing also looks healthy, because nothing shows delivery next to the process state.This PR adds a Plugins page to Settings on web and desktop. It is where a user adds a plugin directory, reviews the exact files before anything runs, approves and enables it, and later disables, resumes, re-checks or removes it. The mobile app gets the same list and details read-only.
Why this qualifies
This is the proposal route in CONTRIBUTING, and no maintainer has agreed to it yet. It needs the plugin-system approval on #6714 (with #6837 for the extension API), the same as the plugin host PR.
It stacks on the npm install PR, which stacks on the plugin views, actions, settings, tools, event delivery and plugin host PRs. It uses only the management RPCs from the plugin host PR and the event delivery state from the event delivery PR; the PRs in between are ordering only. It adds no server code, no RPC and no scope. This is the PR that makes the plugin host PR usable, so we suggest reviewing them together. The settings forms come in the next PR, and the npm install buttons after that. If the answer is no, we close this and the plugin PRs around it. Previous PR in this stack: feat(server): install plugins from npm with integrity checks and staged updates (#16054).
Fix
Shared client state (
packages/client-runtime):state/pluginssubscribes to each environment's catalogue and exposes the seven management commands. A server without thepluginscapability is never subscribed to, and every command checks the capability on the session that sends it.state/pluginPresentationholds the decisions both clients show, so web and mobile cannot drift:events: receiving, retrying (with the reason) or stopped. Retrying or stopped delivery offers Resume even while the process runs; missing delivery is "unknown".enabled, so the way out exists from every state, including a row whose directory is gone.access:writefrom the current session read and a live catalogue. A failed or in-flight read grants nothing, even when an earlier read succeeded, so re-pairing with a standard link turns management off.consentthenenableand re-checks before each step that the same installation still has the digest on screen, so new bytes arriving mid-approval stop it beforeenable.Web and desktop:
/settings/plugins, a sidebar entry, a settings search result and a command palette result. These appear only when a connected environment has thepluginscapability. There is one section per environment. Each row has a state badge, a Review button or an enable switch, and a menu with Details, Resume, Disable, Check files again and Remove (the confirm says an added directory stays and files downloaded from npm are deleted once the plugin stops). Add plugin says the path is on the environment's machine ("this device" only for the desktop app's own server). The review dialog shows the plugin ID, version, directory, file count and size, the fullsha256:digest, the declared capabilities, and the trust and digest statements, behind an "I trust this code" checkbox. A standard pairing sees the page with a "View only" notice and disabled controls.Mobile (read-only): Settings > Server settings > Plugins lists each environment's plugins with the same states and event delivery, and a plugin screen shows its details (ID, version, directory, files, digest, capabilities, problem). There is no Add, approve, enable or remove on mobile. Both screens say plainly that plugins are view-only on mobile and are managed from an administrative web or desktop connection. The mobile app always pairs with standard scopes (unchanged from main), so it never has the
access:writethese actions need; mobile administration needs its own pairing change, which is out of scope here.Docs: the five plugin pages (
plugin-tools,plugin-settings,plugin-actions,plugin-views,plugin-npm) become one guide,docs/user/plugins.md. It is rewritten, not appended: managing plugins first, then one short section per capability. The pages repeated the same trust and setup text and pointed at raw requests that this page replaces. All five capabilities share one manifest and one approval, so one guide is easier to follow.Size: 31 files, +3757 / −306. About 1.5k of the added lines are tests (including a small fake transport the next PRs reuse); −300 is the old pages.
Evidence
Environment: macOS arm64; this PR on top of the npm install PR.
How to exercise it: use an isolated
vp run devwith a scratch plugin directory outside any git checkout (at3-plugin.jsonplus amain.mjsthat only logs). Open Settings > Plugins with an administrative pairing, add the directory, approve it, and toggle it. Then pair a second browser with a standard link.Captured in isolated real clients: web (Chromium), the built desktop app (its own isolated profile and bundled server), and one
vp run dev --shareremote browser. The web, desktop and remote captures are of the page this head ships unchanged. Android was captured at this head on an emulator paired the only way mobile can pair (standard access), after the plugin was added and enabled from an administrative web session on the same server. Fixtures are scratch plugins outside any checkout. The server label was set to "Proof server", so nothing is masked.On Android the list and details are read-only and end with "Plugins are view-only on mobile. Add, approve, enable, or remove them from an administrative web or desktop connection." There is no Add, approve, enable or remove control (light details, dark list).
Observed after (each step below has a screenshot):
sha256:digest,events; the statement names when files are checked and says to use Check files again; Approve stays disabled until the box is checkedVideos: add → review → enable · disable, check files, re-review, remove · event delivery and Resume · desktop · remote · before: web, desktop, Android.
Remote: an owned
vp run dev --shareserver and a fresh browser over its HTTPS origin. With an administrative pairing, add → approve → enable worked; a second browser with a standard pairing saw the page view-only. That run used an earlier revision of the settings forms PR, whose Plugins page code is identical apart from oneexport.Checks at this head (
27eb816e85), re-run 2026-10-05 (CI=true, all exit 0):vp test run src/state/plugins.test.ts src/state/pluginPresentation.test.ts: 2 files, 45 tests pass. These cover the states and controls, event delivery (retrying and stopped offer Resume while the process runs; absent is unknown), access (a credential switch on a real SWR atom goes granted → pending → unreadable and never keeps the admin result), the approval gate (new bytes whileconsentis held:enableis never sent), and the add handoff on a real stream atom whose clock steps backwards.vp test run src/components/settings/PluginsSettings.test.tsx src/components/settings/PluginsSettings.catalog.test.tsx src/components/settings/settingsSearch.test.ts src/components/settings/settingsScopeNavigation.test.ts: 4 files, 85 tests pass.PluginsSettings.catalog.test.tsxmounts an environment's plugin list in React with the client's own atoms, commands and registry; only the transport is fake, and it holds each request until the test answers. Typing a directory and choosing Add and review sendsplugins.addwith the trimmed path; the reply opens the review showing the added plugin's directory and Approve and enable. When the restarted catalogue no longer lists it, the open review says "Plugin removed". A denied session shows the "View only" access explanation, which stays through an access re-check while the header says "Checking access…" and Add plugin is disabled.PluginsSettings.test.tsxchecks the dispatch guards: losing access while the add dialog is open sends noplugins.add, and new bytes arriving whileconsentis held send zero enables.vp test run src/plugins/PluginCatalog.test.ts src/plugins/PluginNpm.test.ts: 2 files, 34 tests pass (an added directory survives removal; npm files are deleted). Mobilesrc/dependency-graph.test.ts: 3 pass.vp run --filtertypecheck for contracts, server, client-runtime, web and mobile;vp lint --report-unused-disable-directivesandvp fmt --checkon the added and modified files;vp run knip:check;vp run lint:mobile;vp run --filter @t3tools/web build;vp run build:desktop;node scripts/release-smoke.ts. The one lint warning (SettingsSidebarNav.tsx,set-state-in-effect) is on an unchanged effect and is also reported on the parent.Fail-before and mutation records (recorded during development): on the parent, the client-runtime files and the
PluginsSettingstests cannot load and the plugins search test fails (recorded on an earlier revision with the same tests; the mounted list test added at this head cannot load there either, as the page does not exist). At this head, with the catalogue restart after an add removed, "Plugin removed" never appears; with the access explanation following the live check, the "View only" text disappears during the re-check: 2 of the 3 mounted tests fail. With the event delivery part reverted (earlier revision), 6 delivery and resume tests fail.Surfaces
PluginInstallationStatus, is exported again for the shared presentation.docs/user/plugins.mdand listed indocs/README.md. No internals doc.Not verified
apps/mobile/src/connection/platform.ts, unchanged from main), so mobile has noaccess:write; this PR ships mobile read-only rather than controls no mobile session can use, and does not change any scope.Claude Opus 5.5 (build), GPT-6.1 Sol (review) and GPT-6 Astra (captures) via T3 Code
🤖 Generated with Claude Code