Skills & entity pills
An assistant that surfaces its capabilities as / skill pills. Type / in the composer to open the menu, pick a skill like Summarize or Code Review, and send it to get a reply tailored to that capability. The badges above the thread show the same skills as <kai-skills>.
How it works
Section titled “How it works”<kai-chat> owns the thread and the composer. Pass a triggers array as a property — typing / opens a menu, and choosing a skill inserts an atomic pill into the composer. On kai-submit the pill is flattened into the message text (using its promptText), so you can route to the matching capability. <kai-skills> renders the same capabilities as badges from a separate skills property.
<kai-skills id="skills"></kai-skills><kai-chat id="chat" chat-title="Workspace assistant"></kai-chat>
<script type="module"> import '@kitn.ai/ui/elements';
await customElements.whenDefined('kai-chat');
const chat = document.getElementById('chat'); const skills = document.getElementById('skills');
// `/` skill triggers — each item becomes an atomic pill when chosen. chat.triggers = [ { char: '/', kind: 'skill', items: [ { id: 'summarize', label: 'Summarize', description: 'Condense a thread into key points', promptText: 'Summarize this thread.' }, { id: 'translate', label: 'Translate', description: 'Translate text into another language', promptText: 'Translate this text.' }, { id: 'code-review', label: 'Code Review', description: 'Review a diff for bugs and style', promptText: 'Review this diff.' }, { id: 'search-docs', label: 'Search Docs', description: 'Search the knowledge base', promptText: 'Search the docs.' }, ] }, ];
// Skill badges: { id, name } skills.skills = [ { id: 'summarize', name: 'Summarize' }, { id: 'translate', name: 'Translate' }, { id: 'code-review', name: 'Code Review' }, { id: 'search-docs', name: 'Search Docs' }, ];
// The submitted value carries the pill's prompt text — route on it. chat.addEventListener('kai-submit', async (e) => { const prompt = e.detail.value; const aId = crypto.randomUUID();
chat.messages = [ ...chat.messages, { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: prompt }] }, { id: aId, role: 'assistant', parts: [] }, ]; chat.loading = true;
// Dispatch to the matching capability on your backend. const reply = await runCapability(prompt); let answer = ''; for await (const token of reply) { answer += token; chat.messages = chat.messages.map((m) => m.id === aId ? { ...m, parts: [{ type: 'text', text: answer }] } : m, ); } chat.loading = false; });</script>triggers shape — each entry is { char, kind, items }; each item:
| field | type | notes |
|---|---|---|
id | string | Stable identifier; carried on the emitted entities |
label | string | Shown in the menu and on the pill (the sigil is rendered for you) |
description | string? | Secondary text shown beside the label |
promptText | string? | What the pill flattens to in the submitted text (defaults to label) |
Use kind: 'agent' (or per-item kind: 'plugin') and char: '@' for an agent/plugin menu. Set triggers on <kai-prompt-input> directly when you compose the input yourself instead of using the full <kai-chat> — there, kai-submit and kai-value-change also carry the structured doc + entities.
Next steps
Section titled “Next steps”kai-chatreference — thetriggersandkindIconsprops plus thekai-submitevent.kai-composerreference — the full entity-pill model behind the input.kai-skillsreference — theskillsproperty and the declarative<kai-skill>children path.- Streaming recipe — parsing SSE from your backend and driving the token loop.