Home

mweinbach / agent-coworker

publicmweinbach/agent-coworker
Overview Code History Branches Pull requestsIssuesInsights
main
HomeOverview Code PRsIssues

feat: use remote marketplace for curated plugins (#131)

4 months ago

7d8302e
Authored
Max Weinbach5/28/2026, 5:48:34 PM
* feat: use remote plugin marketplace for curated skills

Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com>

* chore: regenerate websocket jsonrpc artifacts

Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com>

* fix: support cowork plugin manifests

Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com>

* fix: keep marketplace plugin details available

Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com>

* fix: harden plugin marketplace review paths

Remove destructive plugin deletion from the H3 always-allowed RPC set so trusted devices need workspace settings permission.

Gate remote marketplace metadata on marketplace provenance, reject remote bundles whose manifest id does not match the marketplace entry, and emit local plugin catalogs before asynchronous remote marketplace hydration.

Add focused regressions for H3 plugin delete permission gating, marketplace id/provenance handling, and non-blocking plugin catalog reads.

* style: format review fix paths

Apply Biome formatting to the marketplace catalog changes and the existing Linux shell parity test formatting reported by the CI check lane.

* fix: preserve marketplace plugin provenance

Record marketplace install metadata beside installed plugins so catalog refreshes can keep discovery/update provenance after remote hydration.

Keep default global marketplace bootstrap state incomplete when a configured default is unavailable, allowing later startups to retry once the remote marketplace includes it.

Replace fragile plugin UI fallback/separator glyphs with an ASCII separator and the shared lucide package icon.

Verification: bun test test/plugins.catalog.test.ts test/default-global-skills.test.ts test/session/agentSession.plugins.test.ts; bunx biome check; bun run typecheck; bun run docs:check; bun test --max-concurrency 1.

* fix: harden plugin marketplace catalog refresh

* lint fix

* fix: install workspace tools as plugin

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: harden plugin marketplace install paths

Centralize remote marketplace metadata mapping outside generic source resolution, avoid default global bootstrap fetches for local skip paths, and route Workspace Tools plugin replacement through the shared staged plugin install primitive.

Refactor desktop plugin mutations through a shared runner and split plugin action coverage out of the oversized skills action test file.

Verification: bun test test/codexPrimaryRuntime.test.ts test/default-global-skills.test.ts test/skills.sourceResolver.test.ts apps/desktop/test/skills-actions.test.ts apps/desktop/test/plugin-actions.test.ts --max-concurrency 1; bun run typecheck; bunx biome check touched files; git diff --check

* fix: reuse extension install materialization

* fix: harden plugin catalog marketplace state

Separate installed plugin catalog entries from remote marketplace offers so mutation paths only see installed plugins.

Move default-plugin uninstall state out of enabled=false overrides and migrate legacy tombstones into removedDefaultPlugins.

Move shared GitHub source helpers into the extension layer and extract remote plugin catalog/detail refresh orchestration into a session service.

Verification: bun run typecheck; bun test --max-concurrency 1 test/plugins.catalog.test.ts test/default-global-skills.test.ts test/plugins.sourceResolver.test.ts test/session/agentSession.plugins.test.ts apps/desktop/test/plugin-actions.test.ts apps/desktop/test/plugins-catalog-page.test.ts apps/desktop/test/skills-actions.test.ts; bun run docs:check; bun lint

* fix: respect user scope for default plugin bootstrap

Default marketplace plugin bootstrapping now only treats an existing user-scoped plugin as satisfying the global install contract. A workspace-local plugin with the same id no longer suppresses installing the shared user plugin for other workspaces.

Adds a regression that seeds a workspace-local workspace-tools plugin, runs the default bootstrap, and verifies the user plugin is still installed. Also fixes the existing one-time bootstrap test to delete the fixture plugin it actually installed.

* fix: harden plugin marketplace state ownership

Remove dead plugin discovery warning plumbing and collapse SkillManager's pure PluginCatalogService pass-through wrappers.

Split desktop plugin mutation state from skill mutation state so plugin dialogs stop OR'ing independent error channels back together, while sharing the common pending-key helpers across skill and plugin actions.

Preserve plugin bootstrap provenance in install metadata and avoid treating tombstoned default plugins as installed bootstrap state.

Validation: bun test --max-concurrency 1; bun run typecheck; bun run docs:check; bun run check.

* fix: make workspace tools bootstrap atomic

Stage Workspace Tools plugin replacements from the existing install and only overlay skills that need copying, so already-installed or manual plugin skills are preserved during partial updates.

Move standalone global runtime skill cleanup until after Workspace Tools bootstrap succeeds, and scope cleanup to skills that are actually available in the plugin so global fallbacks survive missing plugin skills.

Add regressions for copy failures, partial plugin replacements, and missing workspace tool sources.

* fix: track plugin skill roots and install waiters

Teach the skill catalog mtime snapshot to include plugin manifest-declared skill directories so custom or multi-root plugin skills trigger prompt/catalog refreshes. Also include plugin install waiters when disposing all control state so in-flight plugin installs are rejected even when no socket/store state remains.

Tests: bun test test/skillCatalogMtime.test.ts apps/desktop/test/control-socket.ensure.test.ts; bun run typecheck

* fix: harden plugin and skill bootstrap mutations

Reject multi-plugin install sources before mutating plugin directories, scope default global skill bootstrap promises by the requested install context, and keep marketplace detail reads lightweight.

Extract shared extension mutation refresh handling so plugin and skill mutations refresh catalogs, skills, commands, and MCP state consistently.

Tests: bun test test/plugins.catalog.test.ts test/default-global-skills.test.ts; bun run typecheck; bun run docs:check; bun test --max-concurrency 1

* fix: stabilize plugin marketplace UI state

* fix: distinguish partial plugin catalog refreshes

* fix: guard plugin detail install actions

* fix: gate default plugin tombstones by provenance

* fix: keep mobile plugin catalog schema aligned

* chore: organize schema contract test imports

* fix: preserve desktop mutation error context

* fix: resolve plugin install waiters from prior state

* fix: catch plugin detail install failures

* fix: track root skill mutation pending state

* fix: preserve plugin loading errors during mutations

* fix: align plugin install preview contract

Make plugin install preview surface the single-plugin install invariant and disable desktop installs when a preview has multiple valid candidates.

Collapse installPluginsFromSource to return a single pluginId instead of carrying a plural candidate model after multi-plugin installs are rejected.

* refactor: isolate desktop extension event handling

Move plugin and skill catalog mutation reducers out of the control socket helper and keep waiter resolution in focused extension event helpers.

Split plugin control-socket coverage into catalog and install event test files so the general event test no longer exceeds 1000 lines.

* refactor: clarify plugin install preview candidate rows

Rename the component-level install preview candidate memo so it no longer shadows the module helper used by install disable logic.

This addresses the PR #131 Bugbot review thread about validPreviewCandidates name shadowing.

* test: cover plugin preview inline failures

Add a desktop regression for the new plugin dialog preview path so RPC-level preview failures stored in pluginMutationError remain visible inline for the attempted source.

Verification: bun test apps/desktop/test/plugins-catalog-page.test.ts; bun run typecheck; bun run docs:check; bun test --max-concurrency 1

* fix: make plugin/extension logic consistent and stable

Correctness/stability/consistency:
- Pin default-plugin tombstones to user scope end-to-end so a workspace
  reinstall actually clears the user tombstone (drop dead scope param).
- Dedup marketplace discovery against the canonical root like direct
  discovery, so aliased entries no longer block enable/disable/delete.
- Keep cached marketplace rows when a remote-marketplace fetch fails by
  signalling failure and emitting a partial catalog.
- Run MCP credential rename + tombstone clear after the install FS commit
  so a post-rename failure cannot strand them against a rolled-back tree.
- Resolve a repo's real default branch for skill shorthand installs,
  matching plugins.
- Make skill install/copy/update atomic by reusing replacePluginInstallRoot
  so a failed copy preserves the prior install (+ regression test).

Simplification/dead-code:
- Remove the degenerate, unused shadowedPluginIds preview field everywhere
  (types, src+mobile schemas, generated docs, tests).
- Drop the dead, scope-lossy collapsed maps from PluginOverrideSnapshot.
- Delete dead __readMarketplaceDocument, the resolve*ForInstallInput
  wrappers, and the dead resolvePluginScope fallback.
- Collapse duplicated bootstrap helpers (shouldOverwriteBootstrapSkill,
  resolveRuntimeSkillSource) and use WORKSPACE_TOOLS_PLUGIN_ID in
  ServerRuntime.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Parent0d90e92

81 files changed
  • apps/desktop/src/app/store.actions.ts+2−0
  • apps/desktop/src/app/store.actions/plugins.ts+373−0
  • apps/desktop/src/app/store.actions/skillPluginHelpers.ts+171−0
  • apps/desktop/src/app/store.actions/skills.ts+50−613
  • apps/desktop/src/app/store.helpers.ts+1−0
  • apps/desktop/src/app/store.helpers/controlSocket.ts+61−102
  • apps/desktop/src/app/store.helpers/extensionEvents.ts+185−0
apps/desktop/src/app/store.helpers/runtimeState.ts
+2
−0
  • apps/desktop/src/app/types.ts+2−0
  • apps/desktop/src/lib/wsProtocol.ts+1−1
  • apps/desktop/src/ui/onboarding/DesktopOnboarding.tsx+1−9
  • apps/desktop/src/ui/plugins/InstallPluginDialog.tsx+13−23
  • apps/desktop/src/ui/plugins/PluginCardGrid.tsx+22−11
  • apps/desktop/src/ui/plugins/PluginDetailDialog.tsx+127−58
  • apps/desktop/src/ui/plugins/PluginsCatalogPage.tsx+26−5
  • apps/desktop/src/ui/skills/InstallSkillDialog.tsx+1−1
  • apps/desktop/src/ui/skills/utils.tsx+4−1
  • apps/desktop/test/control-socket.ensure.test.ts+17−0
  • apps/desktop/test/control-socket.events.test.ts+1−471
  • apps/desktop/test/control-socket.plugin-catalog-events.test.ts+483−0
  • apps/desktop/test/control-socket.plugin-install-events.test.ts+424−0
  • apps/desktop/test/linux-shell-parity.test.ts+9−3
  • apps/desktop/test/plugin-actions.test.ts+724−0
  • apps/desktop/test/plugin-detail-dialog.test.tsx+266−22
  • apps/desktop/test/plugins-catalog-page.test.ts+136−16
  • apps/desktop/test/protocol-v2-events.test.ts+2−0
  • apps/desktop/test/skill-plugin-actions.harness.ts+60−0
  • apps/desktop/test/skills-actions.test.ts+99−620
  • apps/mobile/src/cowork-shared/jsonrpcControlSchemas.ts+41−12
  • apps/mobile/src/cowork-shared/types.ts+38−14
  • docs/generated/websocket-jsonrpc.d.ts+11−9
  • docs/generated/websocket-jsonrpc.schema.json+1−1
  • docs/websocket-protocol.md+33−4
  • packages/harness/src/run_raw_agent_loops.ts+2−1
  • src/codexPrimaryRuntime.ts+1−0
  • src/codexPrimaryRuntime/ensureReady.ts+39−10
  • src/codexPrimaryRuntime/index.ts+1−0
  • src/codexPrimaryRuntime/skills.ts+313−11
  • src/codexPrimaryRuntime/types.ts+2−0
  • src/config.ts+3−3
  • src/{skills => extensions}/github.ts+1−1
  • src/extensions/source.ts+471−0
  • src/plugins/catalog.ts+166−63
  • src/plugins/discovery.ts+27−31
  • src/plugins/index.ts+8−1
  • src/plugins/manifest.ts+139−3
  • src/plugins/marketplace.ts+75−5
  • src/plugins/operations.ts+156−67
  • src/plugins/overrides.ts+100−55
  • src/plugins/remoteMarketplace.ts+185−0
  • src/plugins/skillBridge.ts+2−1
  • src/plugins/sourceResolver.ts+66−359
  • src/server/jsonrpc/routes/plugins.ts+20−0
  • src/server/jsonrpc/schema.plugins.ts+2−0
  • src/server/protocol.ts+2−0
  • src/server/runtime/ServerRuntime.ts+36−11
  • src/server/session/AgentSession.ts+4−0
  • src/server/session/ExtensionMutationCoordinator.ts+66−0
  • src/server/session/PluginCatalogService.ts+125−0
  • src/server/session/SessionRuntime.ts+4−0
  • src/server/session/SkillManager.ts+82−77
  • src/server/skillCatalogMtime.ts+20−7
  • src/shared/jsonrpcControlSchemas.ts+41−12
  • src/skills/catalog.ts+8−8
  • src/skills/defaultGlobalSkills.ts+167−100
  • src/skills/operations.ts+58−63
  • src/skills/sourceResolver.ts+55−229
  • src/types.ts+38−14
  • test/codexPrimaryRuntime.test.ts+390−7
  • test/config/config.directoryResolution.test.ts+14−0
  • test/default-global-skills.test.ts+596−92
  • test/h3.mobile-http-jsonrpc.test.ts+65−3
  • test/h3.mobile-server-pairing.test.ts+5−5
  • test/jsonrpc.control-schemas.test.ts+22−0
  • test/jsonrpc/control.plugins.test.ts+6−6
  • test/plugins.catalog.test.ts+572−4
  • test/plugins.sourceResolver.test.ts+22−10
  • test/session/agentSession.plugins.test.ts+225−0
  • test/skillCatalogMtime.test.ts+31−0
  • test/skills.operations.test.ts+81−4
  • test/skills.sourceResolver.test.ts+10−10