Skip to content

FE-1776: Add assistant plugins and move chat into the website - #9914

Draft
kube wants to merge 10 commits into
claude/plugin-v1-hash-embed-chromefrom
claude/plugin-v1-petrinaut-ai-in-website
Draft

kube wants to merge 10 commits into
claude/plugin-v1-hash-embed-chromefrom
claude/plugin-v1-petrinaut-ai-in-website

Conversation

@kube

@kube kube commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Before this PR, Petrinaut AI ran in two hosts. The website passed it as a plugin, and HASH's process embed passed it through the aiAssistant prop with a bridge transport, an iframe chat relay, saved conversations and its own chat route. Every assistant rendered the chat that @hashintel/petrinaut shipped, configured by an object the plugin returned.

This PR removes Petrinaut AI from the HASH app and moves the chat out of the library into the website's plugins/_shared/chat, where Petrinaut AI and Brunch render it. An assistant plugin returns its chat as a React node that draws the new PetrinautAssistantWindow. The library keeps the window, the empty-net prompt and a small editor API, so an editor without an assistant plugin has no chat at all.

Links

Changes

Library

  • Assistant plugins render their own chat

    providers.assistant.chat is a React node, rendered once per document behind its own plugin boundary.
    Extensions add tabs and startActions, and the empty-net prompt offers the first start action.

  • PetrinautAssistantWindow draws the frame around a plugin's chat

    Header, tab strip, plugin tabs, docking, floating, the compact layout and tab attention.
    usePetrinautAssistantWindow gives the chat its open state, compact state and start requests.
    PetrinautAssistantWindowPreview hosts a chat outside an editor.

  • Editor API for chats

    useLspActions().requestDiagnostics, useIsTitleEditable, mutationBlockedBy, useRevealInEditor, usePetrinautViewport and usePetrinautPluginService.

  • aiAssistant prop and every chat export removed

    The ai, @ai-sdk/react and react-markdown dependencies go with them.

  • A chat that throws leaves a window that says so

Website

  • Chat code moves to plugins/_shared/chat

    Controller, chat view, transcripts, tools, voice dock, their tests and stories, all with git mv.
    The chat reads the editor through public hooks only, gathered in one adapter tests can replace.

  • Voice reaches Brunch's chat through a service

    Voice provides VoiceChat, which Brunch's chat reads with usePetrinautPluginService. Voice adds the "Start voice mode" start action.

  • Storybook on port 6008

    The chat view's stories and the editor-with-assistant stories run here.

  • Tests compile plugins/_shared/chat with the React Compiler and shim matchMedia

HASH

  • Process embed has no assistant

    The iframe chat relay, saved conversations, the /api/petrinaut-ai-chat route and the ai and @ai-sdk/openai dependencies go.

  • Iframe sandbox drops allow-forms

    Only the assistant's input form needed it.

Prompt

  • Petrinaut AI's prompt leaves petrinaut-core for plugins/petrinaut-ai/server/

    Core exports its capability guidance in three sections; the website rebuilds the same prompt, pinned by the same hash.

Known issues

  • HASH users lose the process assistant until the assistant plugins are packaged for HASH
  • Website tests compile only the shared chat with the React Compiler

    Compiling the Brunch plugin in tests makes one approval test hang. Not investigated yet.

Next steps

  • Move the stock transcript to plugins/petrinaut-ai/, Brunch's transcript parts to plugins/brunch/ and the voice dock to plugins/voice/

    The chat view needs a presentation it receives instead of its "stock" | "brunch" switch.

Test coverage

  • assistant-window.test.tsx:

    Docking, closing and reopening keep the chat mounted; tab fallback, announcements and badges; the chat-tab attention marker; focus; a failing plugin tab; the preview host.

  • assistant-chat.test.tsx, chat-view.test.tsx and the other moved suites:

    The chat against a fake editor adapter inside the preview window.

  • plugins-provider.test.tsx:

    Extensions add tabs and start actions; a provided service reaches code the editor renders.

  • use-read-only-reason.test.ts:

    The read-only rule for one mutation.

  • ai-cta-modal.test.tsx:

    The empty-net prompt offers a start action and requests it once.

  • petrinaut-ai-prompt.test.ts:

    The website's prompt keeps the core prompt's hash and behaviour frame.

  • ai.test.ts:

    Capability guidance joins its sections in order.

  • @apps/hash-frontend type check and lint.
  • Local website in the browser:

    The window opens with Petrinaut AI's chat, floats, docks, closes, and reopens with ⌘⇧K with focus in the composer.

How to test

HASH

  • Open HASH preview on Vercel
  • Processes > any process

    Expect no AI button in the bottom bar

Website

  • Open Petrinaut preview on Vercel
  • Bottom bar > AI > send a message

    Expect Petrinaut AI to answer

  • Float, then Dock, then Close, then ⌘⇧K

    Expect the conversation kept and focus in the composer

  • Menu > New

    Expect the empty-net prompt

  • Type a request > Send

    Expect the window to open with the request sent

Storybook

  • yarn workspace @apps/petrinaut-website storybook
  • Plugins > Assistant chat > With Ai Assistant

    Expect a scripted reply in the editor's window

@kube kube self-assigned this Oct 5, 2026
@vercel

vercel Bot commented Oct 5, 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 Oct 9, 2026 9:56am UTC
petrinaut Ready Ready Preview Oct 9, 2026 9:56am UTC
petrinaut-docs Ready Ready Preview Oct 9, 2026 9:56am UTC
1 Skipped Deployment
Project Deployment Actions Updated
hashdotdesign-tokens Ignored Ignored Preview Oct 9, 2026 9:56am UTC

Request Review

@CLAassistant

CLAassistant commented Oct 5, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions Bot added area/deps Relates to third-party dependencies (area) area/apps > hash* Affects HASH (a `hash-*` app) area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team type/eng > backend Owned by the @backend team area/apps labels Oct 5, 2026
@kube
kube added this pull request to stack #9915 October 5, 2026 01:29
@kube kube changed the title Move Petrinaut AI into the website plugin and remove it from the HASH app Move the AI chat into the website plugins and remove Petrinaut AI from the HASH app Oct 5, 2026
@github-actions github-actions Bot added the area/apps > hash.design Affects the `hash.design` design site (app) label Oct 5, 2026
@kube
kube force-pushed the claude/plugin-v1-petrinaut-ai-in-website branch from a287b93 to f6f307e Compare October 5, 2026 12:28
@kube
kube removed this pull request from stack #9915 October 7, 2026 17:11
@kube
kube force-pushed the claude/plugin-v1-petrinaut-ai-in-website branch from f6f307e to 76e663d Compare October 7, 2026 17:12
@github-actions github-actions Bot removed the area/apps > hash* Affects HASH (a `hash-*` app) label Oct 7, 2026
@kube
kube force-pushed the claude/plugin-v1-petrinaut-ai-in-website branch from 3b9e518 to e7fc133 Compare October 8, 2026 16:57
@kube
kube force-pushed the claude/plugin-v1-petrinaut-ai-in-website branch from e7fc133 to 19ebd53 Compare October 8, 2026 22:00
@kube
kube added this pull request to stack #9937 October 8, 2026 22:01
@kube kube changed the title Add assistant plugins and move Petrinaut AI into a website plugin FE-1776: Add assistant plugins and move chat into the website Oct 9, 2026
@kube
kube force-pushed the claude/plugin-v1-petrinaut-ai-in-website branch from 19ebd53 to 2567041 Compare October 9, 2026 02:42
kube added 10 commits October 9, 2026 09:45
The frame, header, tab strip, tabs, docking, focus and tab attention move
out of the chat view into PetrinautAssistantWindow, which the chat draws
around its transcript. The editor keeps the shown tab, start requests and
focus requests above the chat, which reads them through
usePetrinautAssistantWindow. PetrinautAssistantWindowPreview hosts a chat
outside an editor for stories and tests.
Renames only: the AI panel, its chat view, tools, transports, voice
session and the chat types move from the library to
apps/petrinaut-website/src/main/app/plugins/_shared/chat. Imports are not
updated here, so this commit does not build alone; the next commits adapt
the library and the kit.
A plugin with `assistant: { label }` provides the editor's AI assistant; one with `assistant: { extends }` adds tabs and start actions to another plugin's. The provider resolves the active assistant once, from the stored `aiAssistantId` or the first running assistant, and User settings -> General -> AI assistant and the palette's switch commands change it. An extension whose parent is missing or off shows "Needs ..." in the Plugins section.

The library drops the `aiAssistant` prop, its chat types and stories, and the AI SDK and react-markdown dependencies, which move to the website with the chat kit.
`AssistantChat` takes the assistant plugin's `api` (document and experiments, write access) and holds it in a kit-owned context, so the chat reads the net, edits it, runs experiments and reports errors through the plugin api instead of the editor's internals. Refused edits come back as blocked tool output. Each chat renders its own Voice session.

`executePetrinautAiMutation` and the automatic tools take `edit`. Core drops `createPetrinautAiWritableCallbacks`, which nothing uses any more.
`website.petrinaut-ai` renders the shared chat as its assistant view and keeps each document's transcript in this browser; the `/ai-experiments` route passes its scripted assistant as `website.ai-experiments-demo`. While Brunch is the selected assistant, the shell passes `website.brunch`, a bridge plugin that renders the Brunch chat and Ledger tab the shell still builds.

Website imports of the chat kit come from `plugins/_shared/chat`, and the Brunch tools edit through the plugin api.
The website's unit and integration tests load a `matchMedia` shim for jsdom, the shared chat's components compile with the React Compiler under test, and the React Compiler lint rules apply to `plugins/_shared/chat`.
Adds the Assistant plugins and host-tools pages, the assistant window diagram, the assistant rows of the plugin usage manual and the demo site's assistant plugins, and points the README's AI section and the user guide at them. The user guide describes the AI assistant setting and the Needs chip.

Pending @hashintel/petrinaut changesets whose subject moved to the website with the chat (Brunch, Voice, chat and tool UI) are deleted; mixed ones keep the part that stays in the library. The changelog's hand-written Unreleased block, which announced the same chat and Voice APIs, goes too.
The Brunch bridge offers Voice's start action in the empty-net prompt again while its chat has Voice mode. The assistant window leaves out a tab that reuses the chat tab's id or another tab's and reports it under that plugin's id, instead of throwing outside every plugin boundary. The chat tab falls back to the plugin's assistant label, as the user guide now says. The browser-storage helpers move to app/shared, so Petrinaut AI no longer imports from the demo shell.

Review cleanups: one list of running assistants, one select style in User settings, the preview built on the standalone host, the auto-layout output in one helper, an empty test instance for the kit tests, type tests for the assistant manifest, and no dead exports, aliases, stale names or blank-line-only hunks.

Docs: the user guide calls the stock assistant Petrinaut AI and describes assistants in the Plugins section; the plugin pages, diagrams and the AI-experiments, snapshot-diagnostics and testing notes describe the assistant runtime as built.
Assistant tab activity identities are strings, so the attention state keeps them as given. The user guide drops the removed host mutation check, the experiment chat page names the chat as the dispatcher, and the moved kit's comments name the chat rather than the library. The changesets name the added assistant API and the removed exports, and keep the release notes for the assistant window's clearance and auto-layout framing. The AI experiments demo names its plugin, the jsdom shim leaves matchMedia to the vitest setup, and new tests cover Petrinaut AI in the real editor and the canvas insets of a window whose assistant is off.
@kube
kube force-pushed the claude/plugin-v1-petrinaut-ai-in-website branch from 2567041 to 458f3d4 Compare October 9, 2026 09:45

This branch was successfully deployed

3 active deployments
Preview – hash — 458f3d48 Deployed Oct 9, 2026 by vercel[bot]
Preview – petrinaut-docs — 458f3d48 Deployed Oct 9, 2026 by vercel[bot]
Preview – petrinaut — 458f3d48 Deployed Oct 9, 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 > hash.design Affects the `hash.design` design site (app) area/apps area/deps Relates to third-party dependencies (area) 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.

3 participants