Add global Agent Plugins support and migration - #392
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aea0baadb0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| setSaving(true); | ||
| try { | ||
| await onSave(buildProviderDefaultLlmPatch({ profileId, model })); | ||
| writeCurrentChatModel(model); |
There was a problem hiding this comment.
Keep models from non-default providers selectable
When the user chooses a model belonging to a non-default provider, this saves only its model ID, but normalizeChatModelOptions in defaultServices.ts limits the chat catalog to the default provider whenever one exists. On opening Chat, resolveComposerModel therefore treats the saved ID as stale, removes it from local storage, and selects the default provider's first model, silently discarding the user's selection. Include all enabled providers in the chat catalog or persist enough provider-routing information to restore this choice.
Useful? React with 👍 / 👎.
| let plugin = PluginStore::default_global().install_from_directory(&source)?; | ||
| reconcile_plugin_mcp_runtime(state.inner())?; |
There was a problem hiding this comment.
Restart MCP servers after replacing plugin packages
When an enabled plugin is re-imported with an unchanged mcp.json but updated executable or script contents, this reconciliation retains the running MCP child. The normalized server still has the same cache path and serialized configuration, while McpRuntime::reconcile only stops a server when that serialized fingerprint changes, so subsequent calls continue using the old plugin code until the application restarts. Stop the plugin's servers before replacement or incorporate the installed package revision/content into their runtime fingerprints.
Useful? React with 👍 / 👎.
| ...(Object.keys(metadata).length ? { extra: metadata } : {}), | ||
| ...(model ? { model } : {}), | ||
| ...(workingDirectory ? { workingDirectory } : {}), | ||
| ...(Object.keys(extra).length ? { extra } : {}), |
There was a problem hiding this comment.
Preserve migration metadata in partial updates
When a user pins a plugin-migration conversation, sessionStore.pin supplies only { metadata: { pinned } }, and this helper turns that into extra: { pinned }. The Rust metadata patch replaces record.metadata.extra wholesale, so the existing pluginMigration value is erased; after the next refresh the conversation loses its migration status and install action even though the generated workspace remains. Merge partial extra fields with the thread's existing extra object, as the migration-status updater already does.
Useful? React with 👍 / 👎.
Summary
~/.tinybot/pluginsWhy
Tinybot previously managed skills and MCP configuration through separate paths, making discovery and reuse inconsistent across workspaces. Legacy skills or MCP packages also required manual restructuring before they could satisfy the Agent Plugins format.
Impact
Users can maintain one global plugin collection across projects, import compliant plugins, migrate legacy components through the agent, and manage activation from the Resources interface. Invalid sibling components are reported without discarding valid content.
Validation
cargo test plugins:: --jobs 4— 12 passednpm test -- src/app-core/native/desktopNativePlugins.test.ts src/app-core/native/nativeBackendContract.test.ts src/react-workbench/defaultServices.test.ts src/react-workbench/chat/ChatPage.test.tsx src/react-workbench/shell/DesktopShell.test.tsx— 154 passed