Skip to content

FE-1776: Let plugins provide the Petrinaut AI assistant - #9809

Closed
kube wants to merge 5 commits into
cf/fe-1773-zeroth-reactive-modules-panelfrom
claude/fe-1776-assistant-plugins
Closed

kube wants to merge 5 commits into
cf/fe-1773-zeroth-reactive-modules-panelfrom
claude/fe-1776-assistant-plugins

Conversation

@kube

@kube kube commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Before this PR, the AI panel's assistant came only from the aiAssistant prop. The website built the stock assistant and Brunch in one host component and switched between them with its own Labs toggle and palette command, carried by the deprecated slots prop.

Adds an assistants place to the plugin API. A plugin contributes an assistant whose component builds it with hooks and passes it to usePetrinautAiAssistant, and the editor draws the AI panel from what the active assistant passes. No assistant hides the panel and every AI entry point, one is used as is, and several get a selector in User settings and a palette command each. The website's stock assistant and Brunch are plugins, Brunch's Voice toggle is a Brunch Labs group, and slots is removed.

Links

Changes

Library

  • Plugins provide AI assistants through an assistants entry

    An entry has an id, a label and a component.
    The component builds a PetrinautAiAssistant with hooks and passes it to usePetrinautAiAssistant.
    Passing null keeps the panel hidden until the assistant is ready.

  • One assistant is active per editor

    The one chosen in User settings while its plugin is installed, otherwise the first installed.
    Only its component is mounted, beside the editor and inside every editor provider, so a switch never remounts the editor.

  • AI entry points follow the active assistant

    No assistant hides the panel, its bottom-bar button, the first-run prompt and the toggle command.
    Panel is keyed on the assistant and the net, so each assistant gets its own chat and transport.

  • Several assistants add a selector and palette commands

    AI assistant group in General settings, stored as aiAssistantId.
    "Use the … assistant" command for each inactive assistant.

  • Failing assistant component removes the AI panel alone

    Reported as plugin.contribution with place assistant; the selector stays reachable.

  • usePetrinautActiveAssistantId tells a plugin's other contributions which assistant is active
  • Deprecated aiAssistant prop is served as the first assistant, labelled "AI"
  • slots prop and the PetrinautSlots type are removed

Website

  • Stock assistant and Brunch are assistant plugins

    Host passes what only it owns through a demo assistant context: the open document and handle, the repository's revision barrier, the stock history and the Voice preference.

  • Brunch's session, tools, history, Ledger tab and Voice mode run inside its assistant component

    It mounts only while Brunch is active and passes null until its conversation is bound to the document.

  • Assistant order follows the site's earlier choice, then the build default

    petrinaut-website:assistant is read, never written; Petrinaut's setting wins once the user picks.
    A worked-model document installs Brunch alone.

  • Voice toggle is a Brunch Labs settings group

    Brunch plugin checks Voice availability each time Brunch becomes active, keeps the answer across documents, and forgets it when another assistant takes over.

  • Stock assistant's conversation id is its own

    It no longer receives Brunch's fallback conversation id.

  • Palette switch is Petrinaut's

    demo.assistant.switch is removed.

Known issues

  • Switching assistants mid-response stops observing the old assistant's turn

    There is no busy state for the switch yet.

  • Another open tab can undo the assistant choice

    Every user setting shares one stored blob, and each tab rewrites all of it on any change, such as a canvas pan.

  • HASH embed and the website's experiments demo pass the deprecated aiAssistant prop

Next steps

  • Move the HASH embed and experiments demo assistants onto plugins
  • Register assistant tools from any plugin, and add a busy state to the switch

Test coverage

  • plugin-assistants.test.tsx:

    None, one and several assistants, the choice and its fallback, palette switching, null while not ready, a failing assistant, and the deprecated prop.

  • plugin.test.ts:

    Assistant resolution and duplicate assistant ids.

  • user-settings.test.tsx:

    Selector hidden with one assistant, a choice that persists, and Labs focus flow through plugin groups.

  • editor-view.test.tsx:

    Editor draws the AI panel from a plugin assistant.

  • local-storage-demo-app.test.tsx:

    Stock and Brunch as plugins: default order, palette switching, separate histories, Voice gating and the worked-model route.
    Its mock of @hashintel/petrinaut/ui resolves the active assistant the way the editor does.

  • brunch-voice-setting.test.tsx:

    Voice row states.

  • Headless Playwright run of the website with a Brunch endpoint:

    Selector, persisted choice, Brunch Labs group and palette commands.

How to test

One assistant

Two assistants

  • VITE_BRUNCH_CHAT_ENDPOINT=/agents/chat yarn workspace @apps/petrinaut-website dev
  • Menu > User settings

    Expect General > AI assistant with Petrinaut selected

    • Assistant > Brunch

      Expect Brunch selected

    • Labs

      Expect a Brunch group with Enable Voice

  • Reload the page
  • Menu > User settings

    Expect Brunch still selected

  • Press ⌘K, type assistant

    Expect Use the Petrinaut assistant

A plugin's `assistants` entry names an assistant and a component that
builds it with hooks and passes it to `usePetrinautAiAssistant`. The editor
mounts the active assistant's component beside itself and shows the AI
panel, its toggle and the empty-net prompt only while that assistant has
passed a value. The active assistant is the one chosen in User settings,
or the first installed; with several installed, User settings offers a
selector and the palette a switch command for each other assistant. The
deprecated `aiAssistant` prop is served as the first assistant.
The stock assistant and Brunch are assistant plugins, and the host passes
them what only it owns through a demo assistant context: the open
document and its handle, the repository's revision barrier, the stock
history and the Voice preference. Brunch's session, tools, history,
Ledger tab and Voice mode run inside its assistant component, which mounts
only while Brunch is active. Petrinaut's User settings now hold the
choice; the site's earlier stored choice and build default only order the
assistants until the user picks one. The Voice toggle is a Brunch Labs
settings group, and the palette switch comes from Petrinaut.
No host passes `slots` any more: the website's Labs rows are a Brunch
settings group. Plugins add Labs groups through `settingsGroups`, and the
settings tests that passed Labs content through `slots` install a plugin
group instead.
The plugins usage manual gains the assistants place, its rules and an
example, and loses the assistant contribution from what is not here yet.
The user guide explains the AI assistant choice in User settings and the
palette, the host props table marks `aiAssistant` deprecated, and the
website README describes the stock and Brunch plugins and how they are
ordered.
…across nets

The canvas insets count the AI panel only while an assistant is showing,
so an assistant that fails or passes `null` no longer leaves a gap that
nothing can close. Brunch's Voice check runs in the Brunch plugin's
component, keyed on Brunch being active: it re-checks on each switch to
Brunch and keeps its answer when another document opens. The docs say
that a conversation survives a switch only when its assistant keeps it,
and the hook's comment says which fields apply once per conversation.
@kube kube self-assigned this Sep 23, 2026
@vercel

vercel Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
hash Ready Ready Preview Sep 23, 2026 12:34pm UTC
petrinaut Ready Ready Preview Sep 23, 2026 12:34pm UTC
petrinaut-docs Ready Ready Preview Sep 23, 2026 12:34pm UTC
1 Skipped Deployment
Project Deployment Actions Updated
hashdotdesign-tokens Ignored Ignored Preview Sep 23, 2026 12:34pm UTC

Request Review

@kube

kube commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #9914 (assistant plugins) in the rebuilt plugin API stack.

@kube kube closed this Oct 7, 2026

This branch was successfully deployed

3 active deployments
Preview – hash — 6597ba3a Deployed Sep 23, 2026 by vercel[bot]
Preview – petrinaut-docs — 6597ba3a Deployed Sep 23, 2026 by vercel[bot]
Preview – petrinaut — 6597ba3a Deployed Sep 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > backend Owned by the @backend team type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

1 participant