> ## Documentation Index
> Fetch the complete documentation index at: https://help.stralos.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Doug assistant shell draft controller

> Shell-side toggle, user-menu entry, availability bridge, and toggle-state persistence for Doug, the AI assistant

Doug's assistant panel, draft network calls, conversation management, and the assistant activity view are owned by the assistant remote and the organisation feature remote. The shell's role is limited to the header toggle, the user-menu entry, the availability bridge that decides whether to show them, and toggle-state persistence.

## What the shell owns

The shell retains three assistant-related modules in `src/assistant/`:

| Module | Responsibility |
| - | - |
| `AssistantToggleButton.tsx` | Header toggle button. Receives `open` and `onToggle` props; rendering is gated on `useAssistantAvailable()`. Its visible label reads **Talk with Doug** when the panel is closed and **Close Doug** when it is open; the tooltip reads **AI Assistant** or **Close AI Assistant**. |
| `assistantBridge.ts` | Read-only subscriber to `window.__STRALOS_ASSISTANT__`, the bridge the assistant remote publishes. Exposes `useAssistantAvailable()` and `notifyAssistantBridgeInstalled()`. |
| `toggleState.ts` | Persists the open/closed state to `sessionStorage` under `stralos.assistant.toggle`. |

Everything else — the SSE turn loop, draft accept/reject/edit network calls, conversation list, draft state machine, activity audit, and fatigue summary — is owned by the assistant remote (`appframe-aside` widget slot) and the organisation feature remote (Settings → Assistant activity).

## Assistant panel and widget slot

The panel mounts as a `RemoteWidgetSlot` in the `appframe-aside` slot, outside `AppFrame`, so it persists across route changes. The shell loads it lazily:

* **Availability entry** (`assistant/availability`, ≈11 KB including remote entry and share shims): loaded eagerly once per manifest to publish `window.__STRALOS_ASSISTANT__` and let the toggle render.
* **Widget entry** (`assistant/widget`, ≈135 KB): loaded once, on first open.

Both entries share one store. Whichever arrives first publishes the bridge; the second is a no-op.

```tsx theme={null}
{assistantLoaded && remotes ? (
  <RemoteWidgetSlot
    slot="appframe-aside"
    manifest={remotes.manifest}
    degraded={remotes.degraded}
    widgetProps={{ open: assistantOpen, onOpenChange: setAssistantOpenPersisted }}
  />
) : null}
```

## Toggle state storage

The shell writes and reads only `stralos.assistant.toggle`. It no longer reads the legacy `stralos.assistant.panel` key: sessionStorage dies with the tab, and production first served users after the split shipped.

| Key | Owner | Contents |
| - | - | - |
| `stralos.assistant.toggle` | Shell (write) | `{ orgId: string \| null, open: boolean }` |
| `stralos.assistant.panel` | Remote | Legacy blob; may contain `conversationId` — the shell never reads or writes it |

The split exists to prevent the shell from destroying a `conversationId` the remote stores in the legacy key. The remote migrates the legacy key on first read.

## User-menu entry

The user menu shows **Talk with Doug (AI Assistant)** only while `useAssistantAvailable()` returns `true`, so it never offers an item that opens nothing. The item calls `openAssistant()`, which loads the widget if needed and opens the panel; unlike the header button, it never closes it. The billing recovery menu does not show the item because the assistant panel is not mounted there.

## Availability bridge

`assistantBridge.ts` subscribes to `window.__STRALOS_ASSISTANT__` (version 1), a store the remote publishes when its availability module evaluates.

`useAssistantAvailable()` returns:

* `undefined` — bridge not yet arrived; render nothing.
* `true` — assistant is available; show the toggle.
* `false` — assistant is disabled; hide the toggle.

The bridge arrives after the toggle mounts on every render. `notifyAssistantBridgeInstalled()` is called by `loadAssistantAvailability` once the availability load resolves so that already-mounted readers re-subscribe and pick up the initial snapshot.

## Assistant activity

Organisation admins can open **Assistant activity** in **Settings**. That panel is owned by the organisation feature remote, not the shell. The shell's `App.settingsHandoff.test.tsx` covers the integration path — verifying that the activity and fatigue endpoints are called with the host's bearer token after the remote mounts through the real organisation route.

See [AI copilot frontend](https://help.stralos.app/internal/ai-copilot-frontend.md) for the `ChatPanel` component contract, [Doug assistant frontend](https://help.stralos.app/internal/assistant-frontend.md) for the draft lifecycle, network calls, and kill switch, and [Organisation settings frontend](https://help.stralos.app/internal/organisation-settings-frontend.md) for the activity view.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.