@burdenoff/microfe-vibecontrols 2026.910.1 → 2026.915.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"GetHelpButton.js","names":[],"sources":["../../../src/components/shared/GetHelpButton.tsx"],"sourcesContent":["import { HelpCircle } from 'lucide-react';\nimport { useOpenAssistantHelp } from '@/hooks/useOpenAssistantHelp';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface GetHelpButtonProps {\n /** The prompt to auto-send to the assistant when clicked. */\n prompt: string;\n /** Optional label override. Defaults to \"Get Help\". */\n label?: string;\n /** Visual variant. `ghost` (default) is a subtle outlined button; `link` is text-only. */\n variant?: 'ghost' | 'link';\n /** Additional CSS class names. */\n className?: string;\n}\n\n/**\n * Shared \"Get Help\" button. Placed next to \"Add X\" / \"Create X\" buttons in\n * empty states across microfe-vibecontrols so users can ask the assistant\n * to explain the concept without leaving the current page.\n *\n * Clicking the button:\n * 1. Switches the assistant store to `general` mode\n * 2. Queues the provided prompt via `submitPrompt`\n * 3. Dispatches `vc:toggle-ai-assistant` with `detail.open=true` so the\n * shell panel opens (not toggles)\n *\n * The AssistantChatArea picks up the queued prompt on next render and\n * auto-sends it.\n */\nexport function GetHelpButton({\n prompt,\n label,\n variant = 'ghost',\n className = '',\n}: GetHelpButtonProps) {\n const tr = useTr();\n const resolvedLabel = label ?? tr('vibecontrols.shared.getHelpButton.label', 'Get Help');\n const openHelp = useOpenAssistantHelp();\n\n const baseClass =\n variant === 'ghost'\n ? 'inline-flex items-center gap-1.5 px-3 py-1.5 text-sm border border-border-default text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-md transition-colors'\n : 'inline-flex items-center gap-1.5 text-sm text-accent-purple hover:underline';\n\n return (\n <button\n type=\"button\"\n onClick={() => openHelp(prompt)}\n className={`${baseClass} ${className}`.trim()}\n title={tr('vibecontrols.shared.getHelpButton.tooltip', 'Ask the assistant to explain')}\n >\n <HelpCircle className=\"size-4\" />\n {resolvedLabel}\n </button>\n );\n}\n"],"mappings":";;;;;AA6BA,SAAgB,EAAc,EAC5B,WACA,UACA,aAAU,SACV,eAAY,MACS;CACrB,IAAM,IAAK,GAAO,EACZ,IAAgB,KAAS,EAAG,2CAA2C,WAAW,EAClF,IAAW,GAAsB;AAOvC,QACE,kBAAC,UAAD;EACE,MAAK;EACL,eAAe,EAAS,EAAO;EAC/B,WAAW,GARb,MAAY,UACR,kLACA,8EAMsB,GAAG,IAAY,MAAM;EAC7C,OAAO,EAAG,6CAA6C,+BAA+B;YAJxF,CAME,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA,EAChC,EACM"}
1
+ {"version":3,"file":"GetHelpButton.js","names":[],"sources":["../../../src/components/shared/GetHelpButton.tsx"],"sourcesContent":["import { HelpCircle } from 'lucide-react';\nimport { useOpenAssistantHelp } from '@/hooks/useOpenAssistantHelp';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface GetHelpButtonProps {\n /** The prompt to auto-send to the assistant when clicked. */\n prompt: string;\n /** Optional label override. Defaults to \"Get Help\". */\n label?: string;\n /** Visual variant. `ghost` (default) is a subtle outlined button; `link` is text-only. */\n variant?: 'ghost' | 'link';\n /** Additional CSS class names. */\n className?: string;\n}\n\n/**\n * Shared \"Get Help\" button. Placed next to \"Add X\" / \"Create X\" buttons in\n * empty states across microfe-vibecontrols so users can ask the assistant\n * to explain the concept without leaving the current page.\n *\n * Clicking the button:\n * 1. Dispatches `vc:toggle-ai-assistant` with `detail.open=true` so the\n * shell panel opens (not toggles)\n * 2. Queues the provided prompt via the SHARED assistant store's\n * `submitPrompt`, on the next macrotask\n *\n * The mode is left as the user set it. The shared chat area picks up the\n * queued prompt and auto-sends it. See `useOpenAssistantHelp`.\n */\nexport function GetHelpButton({\n prompt,\n label,\n variant = 'ghost',\n className = '',\n}: GetHelpButtonProps) {\n const tr = useTr();\n const resolvedLabel = label ?? tr('vibecontrols.shared.getHelpButton.label', 'Get Help');\n const openHelp = useOpenAssistantHelp();\n\n const baseClass =\n variant === 'ghost'\n ? 'inline-flex items-center gap-1.5 px-3 py-1.5 text-sm border border-border-default text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-md transition-colors'\n : 'inline-flex items-center gap-1.5 text-sm text-accent-purple hover:underline';\n\n return (\n <button\n type=\"button\"\n onClick={() => openHelp(prompt)}\n className={`${baseClass} ${className}`.trim()}\n title={tr('vibecontrols.shared.getHelpButton.tooltip', 'Ask the assistant to explain')}\n >\n <HelpCircle className=\"size-4\" />\n {resolvedLabel}\n </button>\n );\n}\n"],"mappings":";;;;;AA6BA,SAAgB,EAAc,EAC5B,WACA,UACA,aAAU,SACV,eAAY,MACS;CACrB,IAAM,IAAK,GAAO,EACZ,IAAgB,KAAS,EAAG,2CAA2C,WAAW,EAClF,IAAW,GAAsB;AAOvC,QACE,kBAAC,UAAD;EACE,MAAK;EACL,eAAe,EAAS,EAAO;EAC/B,WAAW,GARb,MAAY,UACR,kLACA,8EAMsB,GAAG,IAAY,MAAM;EAC7C,OAAO,EAAG,6CAA6C,+BAA+B;YAJxF,CAME,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA,EAChC,EACM"}
@@ -1,10 +1,10 @@
1
- import { useAssistantStore as e } from "../store/assistantStore.js";
2
- import { useCallback as t } from "react";
1
+ import { useCallback as e } from "react";
2
+ import { useAssistantStore as t } from "@burdenoff/fe-libs/shared/assistant/store";
3
3
  //#region src/hooks/useOpenAssistantHelp.ts
4
4
  function n() {
5
- return t((t) => {
6
- e.getState().setMode("general"), window.dispatchEvent(new CustomEvent("vc:toggle-ai-assistant", { detail: { open: !0 } })), setTimeout(() => {
7
- e.getState().submitPrompt(t);
5
+ return e((e) => {
6
+ window.dispatchEvent(new CustomEvent("vc:toggle-ai-assistant", { detail: { open: !0 } })), setTimeout(() => {
7
+ t.getState().submitPrompt(e);
8
8
  }, 0);
9
9
  }, []);
10
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useOpenAssistantHelp.js","names":[],"sources":["../../src/hooks/useOpenAssistantHelp.ts"],"sourcesContent":["/**\n * useOpenAssistantHelp\n *\n * Returns a callback that opens the AI assistant panel (via the shell's\n * global `vc:toggle-ai-assistant` event), switches it to General mode,\n * and queues a prompt that the chat area will auto-send on next render.\n *\n * Used by the shared `GetHelpButton` that appears next to \"Add X\" buttons\n * in empty states so users can ask the assistant to explain concepts like\n * \"What is a Target?\" without any typing.\n */\nimport { useCallback } from 'react';\nimport { useAssistantStore } from '@/store/assistantStore';\n\nexport function useOpenAssistantHelp() {\n return useCallback((prompt: string) => {\n const store = useAssistantStore.getState();\n store.setMode('general');\n // Open the panel FIRST so the host shell's panel provider commits\n // `isOpen=true` and React mounts the AssistantChatArea before we\n // mutate the store. Previously we queued the prompt before\n // dispatching the open event; when the panel was closed, the chat\n // area's `pendingPrompt` subscriber didn't exist yet, so the\n // `submitPrompt` call's change notification was lost. The mount-time\n // `consumePendingPrompt()` in the chat area then races against\n // zustand persist rehydration, which on slower first paints could\n // clear the queued prompt before the effect fired.\n //\n // Opening first + queueing on the next macrotask gives React a\n // stable commit boundary: the panel mounts and registers its\n // subscriber, and the queued `submitPrompt` call cleanly fires the\n // subscriber on the next tick.\n window.dispatchEvent(\n new CustomEvent('vc:toggle-ai-assistant', {\n detail: { open: true },\n })\n );\n setTimeout(() => {\n useAssistantStore.getState().submitPrompt(prompt);\n }, 0);\n }, []);\n}\n"],"mappings":";;;AAcA,SAAgB,IAAuB;AACrC,QAAO,GAAa,MAAmB;AAsBrC,EArBc,EAAkB,UAAU,CACpC,QAAQ,UAAU,EAexB,OAAO,cACL,IAAI,YAAY,0BAA0B,EACxC,QAAQ,EAAE,MAAM,IAAM,EACvB,CAAC,CACH,EACD,iBAAiB;AACf,KAAkB,UAAU,CAAC,aAAa,EAAO;KAChD,EAAE;IACJ,EAAE,CAAC"}
1
+ {"version":3,"file":"useOpenAssistantHelp.js","names":[],"sources":["../../src/hooks/useOpenAssistantHelp.ts"],"sourcesContent":["/**\n * useOpenAssistantHelp\n *\n * Returns a callback that opens the AI assistant panel (via the shell's\n * global `vc:toggle-ai-assistant` event) and queues a prompt that the chat\n * area will auto-send on next render. It deliberately leaves the MODE alone —\n * see the note in the callback.\n *\n * Used by the shared `GetHelpButton` that appears next to \"Add X\" buttons\n * in empty states so users can ask the assistant to explain concepts like\n * \"What is a Target?\" without any typing.\n */\nimport { useCallback } from 'react';\n/**\n * ★★ The SHARED store, not `@/store/assistantStore`.\n *\n * The panel this product mounts is fe-libs' `AIAssistantPanel`, and its chat\n * area consumes `pendingPrompt` from `fe-libs/shared/assistant/store`. The local\n * store is the pre-migration fork's: a prompt queued there was persisted under\n * `vc-ai-assistant` and read by nobody, so every Get Help button opened an\n * empty panel.\n *\n * ★ Imported from the DEEP subpath, not the barrel. The barrel star-exports the\n * panel components and pulls react-markdown in with them.\n */\nimport { useAssistantStore } from '@burdenoff/fe-libs/shared/assistant/store';\n\nexport function useOpenAssistantHelp() {\n return useCallback((prompt: string) => {\n // Do NOT change the mode.\n //\n // This used to force `general`. That call went to the fork's store, so it\n // never reached the shared panel; moved across with the queue it would take\n // effect for the first time. The shared store PERSISTS `mode`, so one press\n // of a Get Help button would silently move the user out of the mode they\n // chose Assistant, API Calls, Vibe Plugins — for every later turn too.\n //\n // Whatever mode the user is in already answers \"what is this page for\".\n //\n // Open FIRST, queue on the next macrotask. This is a contract, pinned by the\n // test \"the prompt is not queued until after the open event\", not a race\n // guard. The chat area picks the prompt up in either order: its\n // `pendingPrompt` subscriber if it is mounted, its mount-time\n // `consumePendingPrompt()` if it mounts later. `pendingPrompt` is not\n // persisted, so rehydration cannot clear it.\n //\n // The ordering matters only for a shell that toggles and ignores `open`.\n // Pressed with the panel open, that shell closes it; a prompt queued in the\n // same tick would reach the still-mounted chat area and start a turn in a\n // panel about to unmount. Queued a macrotask later, it waits in the store\n // and is sent on the next open.\n //\n // `open: true` OPENS; it does not toggle. vibecontrols-app's overlay honours\n // it from BOFF-7292, so pressed with the panel already open it stays open.\n window.dispatchEvent(\n new CustomEvent('vc:toggle-ai-assistant', {\n detail: { open: true },\n })\n );\n setTimeout(() => {\n useAssistantStore.getState().submitPrompt(prompt);\n }, 0);\n }, []);\n}\n"],"mappings":";;;AA2BA,SAAgB,IAAuB;AACrC,QAAO,GAAa,MAAmB;AA+BrC,EALA,OAAO,cACL,IAAI,YAAY,0BAA0B,EACxC,QAAQ,EAAE,MAAM,IAAM,EACvB,CAAC,CACH,EACD,iBAAiB;AACf,KAAkB,UAAU,CAAC,aAAa,EAAO;KAChD,EAAE;IACJ,EAAE,CAAC"}
@@ -1,9 +1,9 @@
1
1
  import { useWorkspaceWriteAccess as e } from "@burdenoff/fe-libs/shared/assistant/useWorkspaceWriteAccess";
2
2
  //#region src/hooks/useWorkspaceWriteAccess.ts
3
3
  var t = [
4
- "vibecontrols_vibe",
5
- "vibecontrols_session",
6
- "vibecontrols_agent"
4
+ "vibe",
5
+ "session",
6
+ "agent"
7
7
  ];
8
8
  function n(n, r) {
9
9
  return e(n, r, t);
@@ -1 +1 @@
1
- {"version":3,"file":"useWorkspaceWriteAccess.js","names":[],"sources":["../../src/hooks/useWorkspaceWriteAccess.ts"],"sourcesContent":["/**\n * VibeControls' write-access probe.\n *\n * ★ The mechanism lives in `fe-libs/shared/assistant`; the RESOURCE TYPES stay\n * here, because which resources prove \"this user can write in this workspace\"\n * is the product's answer. The shared hook takes them with no default, so\n * another product cannot accidentally probe VibeControls' vibes — or the other\n * way round.\n */\nimport { useWorkspaceWriteAccess as useShared } from '@burdenoff/fe-libs/shared/assistant/useWorkspaceWriteAccess';\nexport type { WorkspaceWriteAccess } from '@burdenoff/fe-libs/shared/assistant/useWorkspaceWriteAccess';\n\n/**\n * Resources an api-calls turn is most likely to write. Not exhaustive — the\n * assistant can reach far more — but a representative probe.\n *\n * ⚠️ These names follow the `<product>_<entity>` convention that HealthyBowl\n * uses (`healthybowl_crop`), derived from VibeControls' own route vocabulary.\n * They have NOT been verified against a seeded row in `wspace-rbac-svc`, and\n * RBAC there is default-DENY.\n *\n * That is safe, and deliberately so: this probe is **advisory, never a gate**.\n * An unseeded resource type resolves to \"denied\", which makes the assistant warn\n * before a Take-action turn instead of after it. It never removes the mode. If\n * the rows are seeded later the warning disappears with no code change and if\n * they are never seeded, the product still works exactly as it does today.\n */\nconst PROBE_RESOURCES = [\n 'vibecontrols_vibe',\n 'vibecontrols_session',\n 'vibecontrols_agent',\n] as const;\n\nexport function useWorkspaceWriteAccess(actorId: string | null, workspaceId: string | null) {\n return useShared(actorId, workspaceId, PROBE_RESOURCES);\n}\n"],"mappings":";;AA2BA,IAAM,IAAkB;CACtB;CACA;CACA;CACD;AAED,SAAgB,EAAwB,GAAwB,GAA4B;AAC1F,QAAO,EAAU,GAAS,GAAa,EAAgB"}
1
+ {"version":3,"file":"useWorkspaceWriteAccess.js","names":[],"sources":["../../src/hooks/useWorkspaceWriteAccess.ts"],"sourcesContent":["/**\n * VibeControls' write-access probe.\n *\n * ★ The mechanism lives in `fe-libs/shared/assistant`; the RESOURCE TYPES stay\n * here, because which resources prove \"this user can write in this workspace\"\n * is the product's answer. The shared hook takes them with no default, so\n * another product cannot accidentally probe VibeControls' vibes — or the other\n * way round.\n */\nimport { useWorkspaceWriteAccess as useShared } from '@burdenoff/fe-libs/shared/assistant/useWorkspaceWriteAccess';\nexport type { WorkspaceWriteAccess } from '@burdenoff/fe-libs/shared/assistant/useWorkspaceWriteAccess';\n\n/**\n * Resources an api-calls turn is most likely to write. Not exhaustive — the\n * assistant can reach far more — but a representative probe. The shared hook\n * asks `checkPermissions` for `action: \"create\"` on each.\n *\n * ★★ These are the names the BACKEND enforces, which are unprefixed:\n * vibe `createVibe` @rbac(action: \"create\", resource: \"vibe\")\n * session `createSession` @rbac(action: \"create\", resource: \"session\")\n * agent `createVibecontrolsAgent` @rbac(action: \"create\", resource: \"agent\")\n * (wspace-vibecontrols-svc, and the composed `graphql/wspace.graphql` here) —\n * and the RBAC service seeds exactly these resource types.\n *\n * They were `vibecontrols_vibe` / `_session` / `_agent`, copied from\n * HealthyBowl's `healthybowl_crop` convention. No such resource type exists in\n * VibeControls' schema or RBAC seed, so no seeded role grants them, RBAC falls\n * through to default-DENY, and the composer's warning said nothing about the\n * permissions a Take-action turn actually needs.\n *\n * Still advisory, never a gate: the shared panel only shows a warning.\n * `useWorkspaceWriteAccess.test.ts` checks these against the schema.\n */\nexport const PROBE_RESOURCES = ['vibe', 'session', 'agent'] as const;\n\nexport function useWorkspaceWriteAccess(actorId: string | null, workspaceId: string | null) {\n return useShared(actorId, workspaceId, PROBE_RESOURCES);\n}\n"],"mappings":";;AAiCA,IAAa,IAAkB;CAAC;CAAQ;CAAW;CAAQ;AAE3D,SAAgB,EAAwB,GAAwB,GAA4B;AAC1F,QAAO,EAAU,GAAS,GAAa,EAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"assistantStore.js","names":[],"sources":["../../src/store/assistantStore.ts"],"sourcesContent":["/**\n * AI Assistant state management.\n *\n * Zustand store with profile-scoped persistence for conversations,\n * active mode, and streaming state. Follows the same pattern as\n * vibeControlsStore.ts.\n */\n\nimport { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport { createProfileScopedStorage } from '@burdenoff/fe-libs/shared/utils';\nimport type {\n AssistantConversationProcessing,\n AssistantConversationStatus,\n AssistantMode,\n AssistantSandboxRuntime,\n AssistantSandboxStatus,\n Conversation,\n Message,\n} from '@/types/assistant';\n\ninterface AssistantState {\n conversations: Conversation[];\n activeConversationId: string | null;\n mode: AssistantMode;\n processingByConversationId: Record<string, AssistantConversationProcessing>;\n conversationListOpen: boolean;\n /** Active sandboxes keyed by assistant mode */\n sandboxesByMode: Partial<Record<AssistantMode, AssistantSandboxRuntime>>;\n /**\n * A prompt queued up for auto-submit by the chat area. Used by the\n * `GetHelpButton` / `useOpenAssistantHelp` hook so pages can prompt the\n * assistant contextually from an empty state button without coupling to\n * the chat area internals.\n */\n pendingPrompt: string | null;\n\n // Actions\n createConversation: (title?: string, mode?: AssistantMode) => string;\n deleteConversation: (id: string) => void;\n renameConversation: (id: string, title: string) => void;\n setActiveConversation: (id: string | null) => void;\n setMode: (mode: AssistantMode) => void;\n setConversationMode: (conversationId: string, mode: AssistantMode) => void;\n addMessage: (conversationId: string, message: Message) => void;\n updateMessage: (conversationId: string, messageId: string, content: string) => void;\n patchMessage: (conversationId: string, messageId: string, patch: Partial<Message>) => void;\n setConversationProcessing: (\n conversationId: string,\n status: AssistantConversationStatus,\n messageId?: string | null,\n error?: string | null\n ) => void;\n clearConversationProcessing: (conversationId: string) => void;\n toggleConversationList: () => void;\n setConversationListOpen: (open: boolean) => void;\n clearConversation: (id: string) => void;\n setBackendSessionId: (conversationId: string, sessionId: string) => void;\n clearBackendSessionId: (conversationId: string) => void;\n setSandbox: (\n mode: AssistantMode,\n sandboxId: string | null,\n status: AssistantSandboxStatus\n ) => void;\n clearSandbox: (mode: AssistantMode) => void;\n resetStore: () => void;\n /** Queue a prompt for the chat area to auto-send on next render. */\n submitPrompt: (text: string) => void;\n /** Returns and clears any queued prompt. */\n consumePendingPrompt: () => string | null;\n}\n\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\nexport const useAssistantStore = create<AssistantState>()(\n persist(\n (set, get) => ({\n conversations: [],\n activeConversationId: null,\n mode: 'assistant',\n processingByConversationId: {},\n conversationListOpen: false,\n sandboxesByMode: {},\n pendingPrompt: null,\n\n createConversation: (title?: string, mode?: AssistantMode) => {\n const id = generateId();\n const now = new Date().toISOString();\n const conversationMode = mode ?? get().mode;\n const conversation: Conversation = {\n id,\n title: title || 'New conversation',\n mode: conversationMode,\n messages: [],\n backendSessionId: null,\n createdAt: now,\n updatedAt: now,\n };\n set((state) => ({\n conversations: [conversation, ...state.conversations],\n activeConversationId: id,\n conversationListOpen: false,\n }));\n return id;\n },\n\n deleteConversation: (id) =>\n set((state) => {\n const filtered = state.conversations.filter((c) => c.id !== id);\n const nextProcessing = { ...state.processingByConversationId };\n delete nextProcessing[id];\n return {\n conversations: filtered,\n processingByConversationId: nextProcessing,\n activeConversationId:\n state.activeConversationId === id\n ? (filtered[0]?.id ?? null)\n : state.activeConversationId,\n };\n }),\n\n renameConversation: (id, title) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === id ? { ...c, title, updatedAt: new Date().toISOString() } : c\n ),\n })),\n\n setActiveConversation: (id) =>\n set((state) => {\n const selectedConversation = state.conversations.find(\n (conversation) => conversation.id === id\n );\n return {\n activeConversationId: id,\n mode: selectedConversation?.mode ?? state.mode,\n conversationListOpen: false,\n };\n }),\n\n setMode: (mode) => set({ mode }),\n\n setConversationMode: (conversationId, mode) =>\n set((state) => ({\n conversations: state.conversations.map((conversation) =>\n conversation.id === conversationId\n ? {\n ...conversation,\n mode,\n updatedAt: new Date().toISOString(),\n }\n : conversation\n ),\n mode: state.activeConversationId === conversationId ? mode : state.mode,\n })),\n\n addMessage: (conversationId, message) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === conversationId\n ? {\n ...c,\n messages: [...c.messages, message],\n updatedAt: new Date().toISOString(),\n // Auto-title from first user message\n title:\n c.messages.length === 0 && message.role === 'user'\n ? message.content.slice(0, 50) + (message.content.length > 50 ? '...' : '')\n : c.title,\n }\n : c\n ),\n })),\n\n updateMessage: (conversationId, messageId, content) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === conversationId\n ? {\n ...c,\n messages: c.messages.map((m) => (m.id === messageId ? { ...m, content } : m)),\n updatedAt: new Date().toISOString(),\n }\n : c\n ),\n })),\n\n patchMessage: (conversationId, messageId, patch) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === conversationId\n ? {\n ...c,\n messages: c.messages.map((m) => (m.id === messageId ? { ...m, ...patch } : m)),\n updatedAt: new Date().toISOString(),\n }\n : c\n ),\n })),\n\n setConversationProcessing: (conversationId, status, messageId = null, error = null) =>\n set((state) => ({\n processingByConversationId: {\n ...state.processingByConversationId,\n [conversationId]: {\n status,\n messageId,\n error,\n updatedAt: new Date().toISOString(),\n },\n },\n })),\n\n clearConversationProcessing: (conversationId) =>\n set((state) => {\n const next = { ...state.processingByConversationId };\n delete next[conversationId];\n return { processingByConversationId: next };\n }),\n\n toggleConversationList: () =>\n set((state) => ({ conversationListOpen: !state.conversationListOpen })),\n\n setConversationListOpen: (open) => set({ conversationListOpen: open }),\n\n clearConversation: (id) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === id\n ? {\n ...c,\n messages: [],\n backendSessionId: null,\n updatedAt: new Date().toISOString(),\n }\n : c\n ),\n processingByConversationId: {\n ...state.processingByConversationId,\n [id]: {\n status: 'idle',\n messageId: null,\n error: null,\n updatedAt: new Date().toISOString(),\n },\n },\n })),\n\n setBackendSessionId: (conversationId, sessionId) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === conversationId\n ? {\n ...c,\n backendSessionId: sessionId,\n }\n : c\n ),\n })),\n\n clearBackendSessionId: (conversationId) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === conversationId ? { ...c, backendSessionId: null } : c\n ),\n })),\n\n setSandbox: (mode, sandboxId, status) =>\n set((state) => {\n if (!sandboxId) {\n const nextSandboxesByMode = { ...state.sandboxesByMode };\n delete nextSandboxesByMode[mode];\n return { sandboxesByMode: nextSandboxesByMode };\n }\n\n return {\n sandboxesByMode: {\n ...state.sandboxesByMode,\n [mode]: {\n sandboxId,\n status,\n },\n },\n };\n }),\n\n clearSandbox: (mode) =>\n set((state) => {\n const nextSandboxesByMode = { ...state.sandboxesByMode };\n delete nextSandboxesByMode[mode];\n return { sandboxesByMode: nextSandboxesByMode };\n }),\n\n resetStore: () =>\n set({\n conversations: [],\n activeConversationId: null,\n mode: 'general',\n processingByConversationId: {},\n conversationListOpen: false,\n sandboxesByMode: {},\n pendingPrompt: null,\n }),\n\n submitPrompt: (text: string) => set({ pendingPrompt: text }),\n\n consumePendingPrompt: () => {\n const current = get().pendingPrompt;\n if (current !== null) set({ pendingPrompt: null });\n return current;\n },\n }),\n {\n name: 'vc-ai-assistant',\n storage: createProfileScopedStorage(),\n partialize: (state) => ({\n conversations: state.conversations.map(({ ...conversation }) => conversation),\n activeConversationId: state.activeConversationId,\n mode: state.mode,\n conversationListOpen: state.conversationListOpen,\n }),\n }\n )\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"assistantStore.js","names":[],"sources":["../../src/store/assistantStore.ts"],"sourcesContent":["/**\n * AI Assistant state management.\n *\n * Zustand store with profile-scoped persistence for conversations,\n * active mode, and streaming state. Follows the same pattern as\n * vibeControlsStore.ts.\n */\n\nimport { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport { createProfileScopedStorage } from '@burdenoff/fe-libs/shared/utils';\nimport type {\n AssistantConversationProcessing,\n AssistantConversationStatus,\n AssistantMode,\n AssistantSandboxRuntime,\n AssistantSandboxStatus,\n Conversation,\n Message,\n} from '@/types/assistant';\n\ninterface AssistantState {\n conversations: Conversation[];\n activeConversationId: string | null;\n mode: AssistantMode;\n processingByConversationId: Record<string, AssistantConversationProcessing>;\n conversationListOpen: boolean;\n /** Active sandboxes keyed by assistant mode */\n sandboxesByMode: Partial<Record<AssistantMode, AssistantSandboxRuntime>>;\n /**\n * A prompt queued up for auto-submit by this fork's chat area.\n *\n * ★ NOT what `GetHelpButton` / `useOpenAssistantHelp` write to: the mounted\n * panel is fe-libs' shared one, which reads `pendingPrompt` from\n * `@burdenoff/fe-libs/shared/assistant/store`. A prompt queued here reaches\n * no mounted component.\n */\n pendingPrompt: string | null;\n\n // Actions\n createConversation: (title?: string, mode?: AssistantMode) => string;\n deleteConversation: (id: string) => void;\n renameConversation: (id: string, title: string) => void;\n setActiveConversation: (id: string | null) => void;\n setMode: (mode: AssistantMode) => void;\n setConversationMode: (conversationId: string, mode: AssistantMode) => void;\n addMessage: (conversationId: string, message: Message) => void;\n updateMessage: (conversationId: string, messageId: string, content: string) => void;\n patchMessage: (conversationId: string, messageId: string, patch: Partial<Message>) => void;\n setConversationProcessing: (\n conversationId: string,\n status: AssistantConversationStatus,\n messageId?: string | null,\n error?: string | null\n ) => void;\n clearConversationProcessing: (conversationId: string) => void;\n toggleConversationList: () => void;\n setConversationListOpen: (open: boolean) => void;\n clearConversation: (id: string) => void;\n setBackendSessionId: (conversationId: string, sessionId: string) => void;\n clearBackendSessionId: (conversationId: string) => void;\n setSandbox: (\n mode: AssistantMode,\n sandboxId: string | null,\n status: AssistantSandboxStatus\n ) => void;\n clearSandbox: (mode: AssistantMode) => void;\n resetStore: () => void;\n /** Queue a prompt for the chat area to auto-send on next render. */\n submitPrompt: (text: string) => void;\n /** Returns and clears any queued prompt. */\n consumePendingPrompt: () => string | null;\n}\n\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\nexport const useAssistantStore = create<AssistantState>()(\n persist(\n (set, get) => ({\n conversations: [],\n activeConversationId: null,\n mode: 'assistant',\n processingByConversationId: {},\n conversationListOpen: false,\n sandboxesByMode: {},\n pendingPrompt: null,\n\n createConversation: (title?: string, mode?: AssistantMode) => {\n const id = generateId();\n const now = new Date().toISOString();\n const conversationMode = mode ?? get().mode;\n const conversation: Conversation = {\n id,\n title: title || 'New conversation',\n mode: conversationMode,\n messages: [],\n backendSessionId: null,\n createdAt: now,\n updatedAt: now,\n };\n set((state) => ({\n conversations: [conversation, ...state.conversations],\n activeConversationId: id,\n conversationListOpen: false,\n }));\n return id;\n },\n\n deleteConversation: (id) =>\n set((state) => {\n const filtered = state.conversations.filter((c) => c.id !== id);\n const nextProcessing = { ...state.processingByConversationId };\n delete nextProcessing[id];\n return {\n conversations: filtered,\n processingByConversationId: nextProcessing,\n activeConversationId:\n state.activeConversationId === id\n ? (filtered[0]?.id ?? null)\n : state.activeConversationId,\n };\n }),\n\n renameConversation: (id, title) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === id ? { ...c, title, updatedAt: new Date().toISOString() } : c\n ),\n })),\n\n setActiveConversation: (id) =>\n set((state) => {\n const selectedConversation = state.conversations.find(\n (conversation) => conversation.id === id\n );\n return {\n activeConversationId: id,\n mode: selectedConversation?.mode ?? state.mode,\n conversationListOpen: false,\n };\n }),\n\n setMode: (mode) => set({ mode }),\n\n setConversationMode: (conversationId, mode) =>\n set((state) => ({\n conversations: state.conversations.map((conversation) =>\n conversation.id === conversationId\n ? {\n ...conversation,\n mode,\n updatedAt: new Date().toISOString(),\n }\n : conversation\n ),\n mode: state.activeConversationId === conversationId ? mode : state.mode,\n })),\n\n addMessage: (conversationId, message) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === conversationId\n ? {\n ...c,\n messages: [...c.messages, message],\n updatedAt: new Date().toISOString(),\n // Auto-title from first user message\n title:\n c.messages.length === 0 && message.role === 'user'\n ? message.content.slice(0, 50) + (message.content.length > 50 ? '...' : '')\n : c.title,\n }\n : c\n ),\n })),\n\n updateMessage: (conversationId, messageId, content) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === conversationId\n ? {\n ...c,\n messages: c.messages.map((m) => (m.id === messageId ? { ...m, content } : m)),\n updatedAt: new Date().toISOString(),\n }\n : c\n ),\n })),\n\n patchMessage: (conversationId, messageId, patch) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === conversationId\n ? {\n ...c,\n messages: c.messages.map((m) => (m.id === messageId ? { ...m, ...patch } : m)),\n updatedAt: new Date().toISOString(),\n }\n : c\n ),\n })),\n\n setConversationProcessing: (conversationId, status, messageId = null, error = null) =>\n set((state) => ({\n processingByConversationId: {\n ...state.processingByConversationId,\n [conversationId]: {\n status,\n messageId,\n error,\n updatedAt: new Date().toISOString(),\n },\n },\n })),\n\n clearConversationProcessing: (conversationId) =>\n set((state) => {\n const next = { ...state.processingByConversationId };\n delete next[conversationId];\n return { processingByConversationId: next };\n }),\n\n toggleConversationList: () =>\n set((state) => ({ conversationListOpen: !state.conversationListOpen })),\n\n setConversationListOpen: (open) => set({ conversationListOpen: open }),\n\n clearConversation: (id) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === id\n ? {\n ...c,\n messages: [],\n backendSessionId: null,\n updatedAt: new Date().toISOString(),\n }\n : c\n ),\n processingByConversationId: {\n ...state.processingByConversationId,\n [id]: {\n status: 'idle',\n messageId: null,\n error: null,\n updatedAt: new Date().toISOString(),\n },\n },\n })),\n\n setBackendSessionId: (conversationId, sessionId) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === conversationId\n ? {\n ...c,\n backendSessionId: sessionId,\n }\n : c\n ),\n })),\n\n clearBackendSessionId: (conversationId) =>\n set((state) => ({\n conversations: state.conversations.map((c) =>\n c.id === conversationId ? { ...c, backendSessionId: null } : c\n ),\n })),\n\n setSandbox: (mode, sandboxId, status) =>\n set((state) => {\n if (!sandboxId) {\n const nextSandboxesByMode = { ...state.sandboxesByMode };\n delete nextSandboxesByMode[mode];\n return { sandboxesByMode: nextSandboxesByMode };\n }\n\n return {\n sandboxesByMode: {\n ...state.sandboxesByMode,\n [mode]: {\n sandboxId,\n status,\n },\n },\n };\n }),\n\n clearSandbox: (mode) =>\n set((state) => {\n const nextSandboxesByMode = { ...state.sandboxesByMode };\n delete nextSandboxesByMode[mode];\n return { sandboxesByMode: nextSandboxesByMode };\n }),\n\n resetStore: () =>\n set({\n conversations: [],\n activeConversationId: null,\n mode: 'general',\n processingByConversationId: {},\n conversationListOpen: false,\n sandboxesByMode: {},\n pendingPrompt: null,\n }),\n\n submitPrompt: (text: string) => set({ pendingPrompt: text }),\n\n consumePendingPrompt: () => {\n const current = get().pendingPrompt;\n if (current !== null) set({ pendingPrompt: null });\n return current;\n },\n }),\n {\n name: 'vc-ai-assistant',\n storage: createProfileScopedStorage(),\n partialize: (state) => ({\n conversations: state.conversations.map(({ ...conversation }) => conversation),\n activeConversationId: state.activeConversationId,\n mode: state.mode,\n conversationListOpen: state.conversationListOpen,\n }),\n }\n )\n);\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-vibecontrols",
3
- "version": "2026.910.1",
3
+ "version": "2026.915.1",
4
4
  "description": "VibeControls microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [
@@ -38,7 +38,8 @@
38
38
  "format": "prettier --write \"src/**/*.{ts,tsx}\"",
39
39
  "format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
40
40
  "type:check": "bash scripts/build-limits.sh tsc --noEmit",
41
- "sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run codegen && bun run lint:sanity && bun run format:check && bun run type:check && bun run build && bun run build-storybook'",
41
+ "test": "bun test ./src",
42
+ "sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run codegen && bun run lint:sanity && bun run format:check && bun run type:check && bun run test && bun run build && bun run build-storybook'",
42
43
  "storybook": "storybook dev -p 6006",
43
44
  "build-storybook": "storybook build",
44
45
  "test:storybook": "test-storybook",