Skip to content

feat: add trusted local plugin system - #8014

Open
UtkarshUsername wants to merge 50 commits into
pingdotgg:mainfrom
UtkarshUsername:feat/plugin-system
Open

feat: add trusted local plugin system#8014
UtkarshUsername wants to merge 50 commits into
pingdotgg:mainfrom
UtkarshUsername:feat/plugin-system

Conversation

@UtkarshUsername

@UtkarshUsername UtkarshUsername commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • add an Effect-native plugin runtime with transactional reconciliation, generation-bound contributions, rollback, and scoped cleanup
  • add a server-owned plugin command catalog with fixed list, invoke, and subscribe RPC methods
  • discover trusted local plugin packages from each environment without rebuilding T3 Code
  • persist enabled package IDs per environment and support status, enable, disable, and reload operations
  • resolve required and optional package capabilities deterministically, with blocked status, duplicate-provider rejection, cycle handling, and dependent restarts
  • add persisted host capability grants plus namespaced settings, state, cache, secrets, files, HTTPS-origin, and exact-process brokers
  • run plugin activation and commands in supervised subprocesses with typed broker transport, deadlines, resource bounds, crash restart, and health diagnostics
  • add generation-bound declarative plugin UI for settings, navigation, pages, cards, statuses, composer and contextual actions, plus bounded notifications
  • expose plugin commands in the web and desktop command palette
  • add a Plugins settings page for web and desktop with lifecycle controls, diagnostics, permission handling, and trusted-code guidance
  • include an external runtime-status example package
  • keep mobile plugin UI and mobile slash commands deferred

architecture

  • one application-owned runtime and command catalog are shared across WebSocket sessions
  • manifest and contribution metadata are strict, versioned, immutable, and JSON-safe; executable handlers remain server-only
  • activation stages the next generation before publication, so failed imports, validation, projection, or activation preserve the last committed generation
  • command invocation carries the observed catalog generation, preventing stale clients from invoking replacement handlers through old metadata
  • packages are copied into generation-specific private caches before import, allowing real ESM reloads while preventing source mutation of an active generation
  • canonical path containment and symlink rejection keep package graphs inside the environment plugin directory
  • enabled state, runtime state, cache retirement, interruption, and cleanup are reconciled without publishing partial lifecycle transitions
  • read RPCs require orchestration:read; invocation and lifecycle mutations require orchestration:operate

trusted local scope

Plugins run in supervised subprocesses under the same OS user as the environment server. This PR intentionally does not add a marketplace, registry, signing, a hostile-code OS sandbox, arbitrary renderer code, or unrestricted host access.

user journey

  1. place a trusted package under the environment's userdata/plugins/<plugin-id>/ directory
  2. open Settings → Plugins in web or desktop
  3. inspect its manifest, capabilities, contributions, lifecycle state, and errors
  4. enable it and run its command from the command palette
  5. reload it after local changes without rebuilding T3 Code
  6. restart the server and retain its enabled state
  7. disable it and remove its command cleanly

UI Changes

Light

plugin management light mode

Dark

plugin management dark mode

Demo Video

I asked an agent to create a plugin for me, and it created a plugin which gives two commands to show time and server info.

plugin-demo.mp4

validation

  • node 24.13.1, pnpm 11.10.0
  • runtime, contracts, and server: 2,944 tests passed, 6 skipped after merging current main
  • plugin-runtime, contracts, client-runtime, server, web, and desktop typechecks passed after merging current main
  • web suite: 2,750 tests passed after merging current main
  • client-runtime suite: 662 tests passed after merging current main
  • mobile suite: 731 tests passed after removing the deferred mobile slash-command integration
  • production web build and Electron desktop smoke passed
  • real paired web app discovered, enabled, reloaded, and retained the external example package
  • existing light and dark screenshots cover the package lifecycle UI; declarative UI rendering was verified through host-renderer tests and production builds because local browser capture was unavailable
  • lint, one-thread formatting, and git diff --check passed

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

High Risk
Plugins can run processes, fetch network, and access namespaced secrets/files after persisted grants, and new RPCs mutate orchestration state. Isolation is subprocess-based, not a full sandbox.

Overview
Adds a trusted local plugin system so environment packages under userdata/plugins/ can register commands and declarative UI without rebuilding T3.

The server discovers manifests, persists enabledPluginIds, and runs activation/commands in supervised child processes (PluginWorkerRuntime.mjs) over a strict JSONL protocol. PluginHostCapabilityBroker requires explicit grants before namespaced settings/state/cache, secrets, files, origin-locked HTTPS, and exact process names. Catalog publication is generation-bound with rollback on failed reload; new permissions need disable/re-enable.

Clients get RPC list/invoke/subscribe plus package enable/disable/reload. Web/desktop expose palette commands, host-rendered views/cards/actions, notifications, and a Plugins settings page. Mobile shows home cards/status and native alerts. Read vs operate scopes are wired for discovery vs invocation/lifecycle.

Reviewed by Cursor Bugbot for commit 5501410. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add trusted local plugin system

  • Introduces @t3tools/plugin-runtime package with core contracts, manifest schemas, planner, and runtime for composing and activating plugins.
  • Server components (PluginWorkerSupervisor, PluginPackageManager, PluginCommandCatalog, PluginHostCapabilityBroker) handle plugin lifecycle, command/UI registration, and sandboxed host APIs including files, network, process execution, and settings.
  • Shared contracts add schemas and RPC definitions for plugin commands, UI, and package management. WebSocket route layer wires these RPCs with read/operate authorization scopes.
  • Web and mobile clients integrate plugin UI via command palette, settings panel, sidebar, notifications, and mobile cards.
  • Behavioral Change: ServerSettings schema adds enabledPluginIds field with a default empty array.

Macroscope summarized 5501410.

request provenance

additional request provenance

additional request provenance

t3-code Bot and others added 23 commits August 19, 2026 06:09
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
…t-ui

# Conflicts:
#	apps/server/src/auth/RpcAuthorization.test.ts
#	apps/server/src/ws.ts
@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 23, 2026
@UtkarshUsername UtkarshUsername changed the title Feat/plugin system feat: add trusted local plugin system Aug 23, 2026
@github-actions github-actions Bot added the size:XXL 1,000+ changed lines (additions + deletions). label Aug 23, 2026
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 51ba8e9e-d071-4a1c-81d3-aded08a354d8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread apps/web/src/components/settings/PluginsSettings.tsx
A failed enable, disable, or reload still changes the server-side
snapshot (package errors are set or cleared), but the settings panel
only refreshed on success, leaving stale rows until a manual refresh.
Refresh after any completed non-interrupted action and cover both
outcomes with tests.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the worker dispose path re-wraps an already structured PluginWorkerError and drops the underlying cause. Everything else previously raised (tagged manifest/contribution errors, catchTags for known tags, Effect-typed dispose, exported make, bounded network detail, interruption-preserving startup retry) looks addressed.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/plugins/PluginWorkerSupervisor.ts
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two findings in apps/server/src/plugins/PluginWorkerSupervisor.ts, both about the crash-reporting path losing/leaking error context. Everything flagged in earlier runs (worker dispose typing, catchTags for the invocation timeout, dispose pass-through, distinct package manifest error classes, catalog catchTags, exported broker make, URL-free network detail, startup retry with interruption preserved) looks addressed.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/plugins/PluginWorkerSupervisor.ts Outdated
Comment thread apps/server/src/plugins/PluginWorkerSupervisor.ts Outdated
Comment thread apps/server/src/plugins/PluginWorkerSupervisor.ts
t3-code Bot and others added 2 commits August 24, 2026 01:31
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the declarative plugin UI kit added in cd7c496 (previous findings on the worker supervisor are already addressed or flagged). Four findings, all in the new server-side error modelling: a free-form detail field is being used both as the failure discriminator and as the wrapper message source.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/plugins/PluginCommandCatalog.ts Outdated
Comment thread apps/server/src/plugins/PluginPackageManager.ts Outdated
Comment thread apps/server/src/plugins/PluginPackageManager.ts Outdated
Comment thread apps/server/src/plugins/PluginPackageManager.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three consistency findings in the new web plugin UI, all in apps/web/src/components/plugins/PluginUi.tsx. The rest of the changed web surface (settings panel, command palette, routes, settings nav) reuses the shared primitives correctly, and the permission/capability chips flagged in earlier runs now truncate inside Tooltip.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Comment thread apps/web/src/components/plugins/PluginUi.tsx
Comment thread apps/web/src/components/plugins/PluginUi.tsx
Comment thread packages/contracts/src/pluginPackages.ts Outdated
Comment thread apps/server/src/plugins/PluginWorkerRuntime.mjs Outdated
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Comment thread apps/web/src/components/plugins/PluginUi.tsx
Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the web UI changes in this PR (plugin composer/view rendering, plugin page route, sidebar nav, plugin settings). The three findings from the previous run are addressed: the tone tokens now use the -foreground roles, the sidebar entries match the icon-only utility-row contract with a Tooltip, and the setting controls route through Select/Input. Three new findings below, all in newly added markup.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/routes/plugins.$pluginId.$viewId.tsx Outdated
Comment thread apps/web/src/components/plugins/PluginUi.tsx
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two new findings in the web plugin UI; the three items from earlier runs (composer placement in ChatView.tsx, unbounded status chips in PluginComposerContributions, the hand-rolled page frame in plugins.$pluginId.$viewId.tsx) are still present.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/CommandPalette.tsx Outdated
Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Comment thread apps/web/src/components/plugins/PluginUi.tsx
Comment thread apps/web/src/components/plugins/PluginUi.tsx
Comment thread apps/web/src/routes/plugins.$pluginId.$viewId.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed web UI surfaces (PluginUi.tsx, PluginsSettings.tsx, CommandPalette.tsx, SidebarChrome.tsx, plugins.$pluginId.$viewId.tsx). The earlier rounds' primitive/token/scroller findings look addressed. Three remaining items below; previously reported items that are unchanged are not repeated.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two consistency findings in the newly moved composer row and the sidebar plugin entries. Everything else in this revision looks aligned with the shared contracts: the Select/Input primitives, SidebarMenuButton isActive, the -foreground tone tokens, the StatusBadge truncation + Tooltip, and the ScrollArea scrollFade scrollbarGutter props on the plugin page (which now apply the mask and gutter to the viewport the primitive owns rather than to the root).

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Comment thread apps/web/src/components/settings/PluginsSettings.tsx
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the composer row now opts into the attached-drawer seam contract, but ChatView's externalComposerDrawerAttached flag does not account for it, so the composer glass shell keeps its continuous backdrop and closed top outline when plugin contributions are the only drawer above it.

The rest of this revision looks good — the sidebar item now reuses the shared SidebarUtilityItem primitive, the card treatment is a single local component consumed by both call sites, and the command toast matches CommandPalette's title/description split.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/components/ChatView.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the web UI surfaces in this PR against the shared component system. The composer seam issue raised on the previous commit is resolved: usePluginComposerContributionState().isAttached now feeds externalComposerDrawerAttached, so the plugin drawer row toggles chat-composer-glass-shell-attached / ChatComposer's externalDrawerAttached exactly like ComposerBannerStack and ThreadSyncStatusPill.

Two remaining consistency findings in apps/web/src/components/plugins/PluginUi.tsx, both commented inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated
Comment thread apps/web/src/components/plugins/PluginUi.tsx Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 68c92fb. Configure here.

Comment thread apps/web/src/components/ChatView.tsx Outdated
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant