import type { PluginExtensionAddedFunctionConfig } from '@grafana/data'; import type { ComponentType, ReactNode } from 'react'; /** * Extension point id consumed by the Grafana Assistant. * * A contributing app plugin registers a manifest function against this target via * `AppPlugin.addFunction()` (declared under `extensions.addedFunctions` in its `plugin.json`). * The Assistant discovers registrations with `usePluginFunctions()` and invokes each function to * obtain the plugin's {@link AssistantExtensionManifest}. * * @experimental This API is under active development and may change in future releases. */ export declare const ASSISTANT_EXTENSION_POINT = "grafana-assistant-app/extension/v1"; /** * Props passed to a `tool-details` chit component registered via {@link ChitContribution}. * * These are the normalized props every tool row in the Assistant chat track receives; a * registered component renders inside the expanded details area of the standard tool card, * in place of the generic expanded content. * * @experimental This API is under active development and may change in future releases. */ export interface ToolChitProps { /** The fully namespaced tool name that produced this card. */ toolName: string; /** The tool call input, as sent to the tool. */ input: unknown; /** The tool result, once available. `undefined` while the tool is still running. */ result: unknown; /** Execution state of the tool call. */ status: 'running' | 'success' | 'error'; } /** * A contribution to the Assistant chat track ("chits"): either an expanded-details renderer * keyed by tool name, or a component rendered in place of a tag inside assistant text. * * - `tool-details`: renders inside the expanded details area of the standard tool card for the * listed tool names, replacing the generic expanded content (Query/Result). The compact tool * row, its status, and the Allow/Deny confirmation flow are always built in — a plugin never * replaces the tool row and never renders while a confirmation is pending. A plugin may only * register details for tools it owns (tool names are namespaced by the tool pipeline and * checked against the contributing plugin id). * - `inline`: registers a component rendered in place of a custom tag inside assistant message * text. The full tag name is the plugin id with non-alphanumerics collapsed to underscores, * followed by an underscore and `name` — for example plugin `grafana-k6-app` with * `name: 'result'` renders `…`. Tag children * and allow-listed attributes are passed through as props. Nothing teaches the model the tag * exists; teach it from your own prompt contribution or page context. * * Component and function references are the only non-JSON-serializable parts of a manifest — * refer to {@link AssistantExtensionManifest} for the serializability contract. * * @experimental This API is under active development and may change in future releases. */ export type ChitContribution = { kind: 'tool-details'; /** Fully namespaced tool names this details renderer applies to. Must be owned by the contributing plugin. */ toolNames: string[]; /** The details component, rendered inside a per-chit error boundary. */ component: ComponentType; } | { kind: 'inline'; /** Plugin-local tag suffix: lowercase, `[a-z][a-z0-9]*`. Namespaced by the plugin id into the full tag name. */ name: string; /** Rendered in place of the tag, inside a per-chit error boundary. Receives tag children and allow-listed attributes as props. */ component: ComponentType<{ children?: ReactNode; }>; }; /** * The artifact file node handed to plugin artifact renderers and toolbars. * * Intentionally minimal and forward-compatible: renderers must treat this as the complete * artifact surface and must not fetch artifact content from anywhere else. * * @experimental This API is under active development and may change in future releases. */ export interface AssistantArtifactNode { /** File path of the artifact in the conversation's artifact filesystem. */ path: string; /** The artifact kind extension, e.g. `.grafana-k6-app.test`. */ ext: string; /** The artifact content. Shape is owned by the contributing plugin's `validate` contract. */ data: unknown; /** * Monotonically increasing per-artifact write counter. Increments on every write to this * artifact. Suitable for memoization keys, cache-busting, and change detection. NOT a * timestamp — carries no wall-clock meaning. */ revision: number; } /** * Read-only handle over the conversation's artifact filesystem, mirroring the host's read * surface. * * The handle spans the whole conversation: it includes other plugins' and built-in artifacts by * design — composition is the point. There is no write surface; artifact writes happen only * through the Assistant's validated `write_artifact` path. Renderers must treat this handle as * the only artifact-content source besides their own node. * * @experimental This API is under active development and may change in future releases. */ export interface AssistantArtifactFs { /** All artifacts in the conversation, in stable path order. */ list(): AssistantArtifactNode[]; /** Read one artifact by exact normalized path. */ read(path: string): AssistantArtifactNode | undefined; /** Whether an artifact exists at the path. */ has(path: string): boolean; } /** * Props passed to a plugin artifact renderer. * * @experimental This API is under active development and may change in future releases. */ export interface ArtifactRendererProps { /** The artifact node to render. */ node: AssistantArtifactNode; /** Read-only handle over the conversation's artifact filesystem. */ fs: AssistantArtifactFs; } /** * Props passed to an optional plugin artifact toolbar. * * @experimental This API is under active development and may change in future releases. */ export interface ArtifactToolbarProps { /** The artifact node the toolbar acts on. */ node: AssistantArtifactNode; /** Read-only handle over the conversation's artifact filesystem. */ fs: AssistantArtifactFs; } /** * A plugin-contributed artifact kind for the Assistant canvas. * * The `ext` is double-dot namespaced as `.{pluginId}.{kind}`, where `pluginId` is your full, * exact plugin id — for example `.grafana-k6-app.test`, not a shortened slug. This keeps plugin * kinds out of the built-in single-dot space and keeps orphaned artifacts attributable to their * owning plugin without a live registry, since the plugin id is readable from the artifact path. * * Everything except `validate`, `renderer`, and `toolbar` is JSON-serializable — refer to * {@link AssistantExtensionManifest} for the serializability contract. * * @experimental This API is under active development and may change in future releases. */ export interface ArtifactKindContribution { /** Artifact extension in the form `.{pluginId}.{kind}` (exact plugin id), double-dot namespaced. */ ext: `.${string}.${string}`; /** Human-readable label shown on artifact tabs and cards. */ label: string; /** Icon name shown next to the label. */ icon: string; /** * Tab promotion order, ascending: LOWER values are promoted and rendered earlier. Built-in * kinds always occupy the lowest values (`.report` = 0, `.dashboard` = 1, `.query` = 2) and * cannot be jumped — the host clamps this value to non-negative and offsets it into a range * after every built-in, so it only orders your kinds relative to other plugin-contributed * kinds. Ties break by most recently updated artifact first; kinds with no live registration * sort last. */ tabPriority: number; /** Write-time content contract: returns true when `data` is valid for this kind. */ validate(data: unknown): boolean; /** Renders the artifact in the canvas. Receives the artifact node and a read-only conversation filesystem handle. */ renderer: ComponentType; /** Optional toolbar rendered above the artifact. */ toolbar?: ComponentType; /** Optional one-line hint teaching the model how to write artifacts of this kind. */ promptHint?: string; } /** * Environment conditions evaluated by the Assistant when deciding whether a contributed mode is * available. Unknown condition strings fail closed (the mode is hidden). * * @experimental This API is under active development and may change in future releases. */ export type PromptContributionAvailability = 'not-oss' | 'not-trial'; /** * A plugin-contributed Assistant mode (system prompt variant). * * The contribution is exposed in the mode picker as `${pluginId}:${modeId}` and its prompt text * is always rendered inside an attributed `` wrapper, subject to a * per-plugin byte budget and an assistant-side allowlist. * * All fields are JSON-serializable — refer to {@link AssistantExtensionManifest} for the * serializability contract. * * @experimental This API is under active development and may change in future releases. */ export interface PromptContribution { /** Plugin-local mode id. Exposed to users as `${pluginId}:${modeId}`. */ modeId: string; /** Human-readable mode label shown in the mode picker. */ label: string; /** Short description of the mode shown in the mode picker. */ description: string; /** One line describing when the Assistant should suggest this mode. */ whenToUse: string; /** * How `prompt` composes with the base system prompt. * * - `prepend` / `append`: `prompt` is added before / after the base guidance, which is * otherwise kept intact. * - `replace`: the rest of the base guidance is dropped, but the Assistant's identity section * is ALWAYS retained — the composed prompt is the identity block, then the current-mode * block, then the attributed plugin body. A plugin never controls or replaces the * Assistant's identity. * * Prefer `prepend` or `append` unless the mode is a genuinely self-contained specialist that * needs none of the base guidance. */ compose: 'prepend' | 'append' | 'replace'; /** * The prompt snippet (`prepend`/`append`) or specialist body (`replace`). With `replace`, this * body is composed after the Assistant's always-retained identity section — refer to * {@link PromptContribution.compose}. */ prompt: string; /** Optional hidden first message sent when the mode is selected on a fresh chat. */ initialMessage?: string; /** * Whether the mode can be switched to mid-conversation. Defaults to true. When false the mode * is entry-point only: listed on fresh chats and reachable via deep links, but hidden from the * picker mid-conversation and excluded from mode suggestions. */ switchable?: boolean; /** Environment conditions evaluated by the Assistant; all must hold for the mode to show. */ availability?: PromptContributionAvailability[]; /** Names of the plugin's own tools scoped exclusively to this mode. */ modeToolNames?: string[]; /** Optional icon name shown in the mode picker. */ icon?: string; /** Optional feature state badge shown next to the mode label. */ featureState?: 'preview' | 'experimental'; } /** * A proposed first message shown on fresh chats (empty conversations) on the Assistant chat * surfaces, visibly attributed to the contributing plugin. * * Selecting a starter sends `message` as an ordinary, user-visible user message. Starters are * never auto-sent, never hidden, and never modify the system prompt — which is why they are part * of the v1 chat-content surface (master flag + per-plugin kill switch only; no allowlist, no * prompt-contributions flag). Contrast with {@link PromptContribution.initialMessage}, which is * hidden, auto-sent on mode selection, and ships behind the v2 prompt-contributions flag. * * Enforced bounds (entries beyond a cap are dropped whole by the Assistant): max 3 starters per * plugin, `message` at most 500 characters, `label` at most 80 characters. * * Starters are pure data (fully JSON-serializable), making them ideal for the future static * `plugin.json` transport — refer to {@link AssistantExtensionManifest} for the serializability * contract. * * @experimental This API is under active development and may change in future releases. */ export interface AssistantStarter { /** Optional short label shown on the starter; defaults to the message itself. Max 80 characters. */ label?: string; /** The user message sent, visibly and as-is, when the starter is selected. Max 500 characters. */ message: string; } /** * A plugin-contributed skill: a documented workflow the Assistant can load on demand. * * A skill is the recommended way for a plugin to teach the Assistant a workflow. By design only * a bounded catalog line (title, description, handle) is ever resident in the Assistant's * prompt; `content` enters model context only when the user attaches the skill or the model * loads it. Contrast with {@link PromptContribution}, which splices resident text into the * system prompt and ships behind a stricter flag and allowlist tier — skills need neither. * * Rollout status: today a registered skill appears read-only on the Assistant's Skills settings * page, attributed to your plugin. The catalog/load path that puts skills in front of the model * ships with the Assistant's agent-skills runtime and lights up automatically when it lands — * no plugin-side change needed. * * Each skill is identified by the handle `plugin:{yourPluginId}:{id}`, with the plugin id taken * from the extension registry — so skills can never collide with built-in or tenant skills, or * with another plugin's. Plugin skills are never persisted or indexed: they exist only while * your registration is live, and disappear everywhere at once when it is removed. * * Enforced bounds (entries violating any bound are dropped whole by the Assistant, never * truncated): max 5 skills per plugin; `id` matching `[a-z][a-z0-9-]*`, at most 64 characters; * `title` at most 80 characters; `description` at most 500 characters; `content` at most * 32768 UTF-8 bytes. * * Skills are pure data (fully JSON-serializable), making them ideal for the future static * `plugin.json` transport — refer to {@link AssistantExtensionManifest} for the serializability * contract. * * @experimental This API is under active development and may change in future releases. */ export interface SkillContribution { /** Plugin-local skill id: `[a-z][a-z0-9-]*`, max 64 characters. Namespaced by the plugin id into the catalog handle. */ id: string; /** Human-readable skill title shown in the catalog and in settings. Max 80 characters. */ title: string; /** One or two sentences describing what the skill does and when to use it. Max 500 characters. */ description: string; /** The skill body, loaded only on invocation — never resident in the system prompt. Max 32768 UTF-8 bytes. */ content: string; } /** * The manifest a Grafana app plugin returns from its assistant extension function to contribute * chits, starters, skills, artifact kinds, and prompt/mode variants to the Grafana Assistant. * * Serializability contract: every field of the manifest is JSON-serializable **except** * component and function references (`ChitContribution.component` / `detect` / `render`, * `ArtifactKindContribution.validate` / `renderer` / `toolbar`). This constraint is deliberate: * it enables a future transport where the static halves of a manifest are declared in signed * `plugin.json` metadata and only component references are resolved at runtime. * * Unknown fields and unknown contribution kinds are ignored by the Assistant, so manifests * produced by newer SDK versions degrade gracefully on older Assistant versions. * * @experimental This API is under active development and may change in future releases. */ export interface AssistantExtensionManifest { /** Manifest schema version. Always 1 for this SDK version. */ schemaVersion: 1; /** Chat-track contributions: tool-details renderers and inline components. */ chits?: ChitContribution[]; /** * Proposed first messages offered on fresh chats, attributed to the plugin. Max 3. * * When to use which: use `provideQuestions` to suggest questions on pages YOUR plugin already * runs on (URL-scoped, page-context registry, requires your module to be loaded on that page, * unattributed); use manifest `starters` for proposed first messages in the Assistant's fresh * chats ANYWHERE (extension-point transport, loads your module on demand, plugin-attributed, * governed by the Assistant's per-plugin kill switch and caps, statically declarable in the * future). They intentionally share the same rendering surface and send path. */ starters?: AssistantStarter[]; /** * Skills teaching the Assistant plugin-specific workflows, loaded on demand. Max 5. * * When to use which: use `skills` to document a workflow the Assistant should follow when a * conversation enters your plugin's territory — only a one-line catalog entry is resident, and * the body loads on invocation. Use `promptContributions` only for a full mode whose resident * prompt text must always be present; that surface sits behind a stricter flag and allowlist * tier. Skill content reaches the model only where the Assistant's agent-skills runtime is * enabled; the read-only settings listing needs no additional flag. */ skills?: SkillContribution[]; /** Canvas artifact kinds. */ artifactKinds?: ArtifactKindContribution[]; /** Assistant mode / system prompt contributions. */ promptContributions?: PromptContribution[]; } /** * Builds the `AppPlugin.addFunction()` configuration that registers an assistant extension * manifest with the Grafana Assistant. * * Purity contract: the registered function MUST be pure and stable — invoked any number of * times, it must return the same (deep-equal) manifest for the same environment. The Assistant * dedups deep-equal results; an unstable manifest churns downstream consumers and invalidates * prompt caching. Gate on your own feature flags *before* registering rather than returning a * varying manifest. * * @example * ```typescript * import { AppPlugin } from '@grafana/data'; * import { getAssistantExtensionConfig } from '@grafana/assistant'; * * export const plugin = new AppPlugin().addFunction( * getAssistantExtensionConfig({ * schemaVersion: 1, * chits: [{ kind: 'tool-details', toolNames: ['myplugin_run_check'], component: MyToolDetails }], * }) * ); * ``` * * @experimental This API is under active development and may change in future releases. */ export declare function getAssistantExtensionConfig(manifest: AssistantExtensionManifest): PluginExtensionAddedFunctionConfig<() => AssistantExtensionManifest>; //# sourceMappingURL=extension.d.ts.map