feat: structured plugin parsing (#13711)

#### What

Add structured `@plugin` parsing and TUI support for plugin mentions.

- Core: switch from plain-text `@display_name` parsing to structured
`plugin://...` mentions via `UserInput::Mention` and
`[$...](plugin://...)` links in text, same pattern as apps/skills.
- TUI: add plugin mention popup, autocomplete, and chips when typing
`$`. Load plugin capability summaries and feed them into the composer;
plugin mentions appear alongside skills and apps.
- Generalize mention parsing to a sigil parameter, still defaults to `$`

<img width="797" height="119" alt="image"
src="https://github.com/user-attachments/assets/f0fe2658-d908-4927-9139-73f850805ceb"
/>

Builds on #13510. Currently clients have to build their own `id` via
`plugin@marketplace` and filter plugins to show by `enabled`, but we
will add `id` and `available` as fields returned from `plugin/list`
soon.

####Tests

Added tests, verified locally.
This commit is contained in:
sayan-oai
2026-03-06 11:08:36 -08:00
committed by GitHub
parent 0e41a5c4a8
commit 8a54d3caaa
18 changed files with 468 additions and 181 deletions

View File

@@ -476,6 +476,26 @@ Invoke an app by including `$<app-slug>` in the text input and adding a `mention
} } }
```
### Example: Start a turn (invoke a plugin)
Invoke a plugin by including a UI mention token such as `@sample` in the text input and adding a `mention` input item with the exact `plugin://<plugin-name>@<marketplace-name>` path returned by `plugin/list`.
```json
{ "method": "turn/start", "id": 35, "params": {
"threadId": "thr_123",
"input": [
{ "type": "text", "text": "@sample Summarize the latest updates." },
{ "type": "mention", "name": "Sample Plugin", "path": "plugin://sample@test" }
]
} }
{ "id": 35, "result": { "turn": {
"id": "turn_459",
"status": "inProgress",
"items": [],
"error": null
} } }
```
### Example: Interrupt an active turn
You can cancel a running Turn with `turn/interrupt`.
@@ -976,7 +996,7 @@ The server also emits `app/list/updated` notifications whenever either source (a
}
```
Invoke an app by inserting `$<app-slug>` in the text input. The slug is derived from the app name and lowercased with non-alphanumeric characters replaced by `-` (for example, "Demo App" becomes `$demo-app`). Add a `mention` input item (recommended) so the server uses the exact `app://<connector-id>` path rather than guessing by name.
Invoke an app by inserting `$<app-slug>` in the text input. The slug is derived from the app name and lowercased with non-alphanumeric characters replaced by `-` (for example, "Demo App" becomes `$demo-app`). Add a `mention` input item (recommended) so the server uses the exact `app://<connector-id>` path rather than guessing by name. Plugins use the same `mention` item shape, but with `plugin://<plugin-name>@<marketplace-name>` paths from `plugin/list`.
Example: