diff --git a/src/apps/officer-web/App.tsx b/src/apps/officer-web/App.tsx
index 340d5c8f..af71fffe 100644
--- a/src/apps/officer-web/App.tsx
+++ b/src/apps/officer-web/App.tsx
@@ -40,6 +40,7 @@ export function App() {
} />
} />
} />
+ } />
} />
} />
} />
diff --git a/src/workspaces/plugins/ChatHistory/client/ChatPanel.tsx b/src/apps/officer-web/Screens/Dashboard/Chat/ChatPanel.tsx
similarity index 92%
rename from src/workspaces/plugins/ChatHistory/client/ChatPanel.tsx
rename to src/apps/officer-web/Screens/Dashboard/Chat/ChatPanel.tsx
index b14991ff..f5368a94 100644
--- a/src/workspaces/plugins/ChatHistory/client/ChatPanel.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/ChatPanel.tsx
@@ -1,13 +1,10 @@
import { useRef, useEffect, useState } from 'react';
import { useNavigate, useLocation } from 'react-router';
-import { useSessions } from './state/useSessions';
+import { useSessions, useOpenCodeSessions, useSlashCommands, SessionBar } from 'widgets/ChatHistory';
import type { ModelOption } from '@/state/useModels';
-import { useOpenCodeSessions } from './state/useOpenCodeSessions';
-import type { useClaude, Attachment } from 'plugins/Chat/client';
-import { EmbeddableChat } from 'plugins/Chat/client';
-import { useSlashCommands } from './state/useSlashCommands';
+import type { useClaude } from './useClaude';
+import { EmbeddableChat, type Attachment } from './EmbeddableChat';
import { Card } from '@/components/Card';
-import { SessionBar } from './SessionBar';
export type { Attachment };
diff --git a/src/workspaces/plugins/Chat/client/EmbeddableChat.tsx b/src/apps/officer-web/Screens/Dashboard/Chat/EmbeddableChat.tsx
similarity index 99%
rename from src/workspaces/plugins/Chat/client/EmbeddableChat.tsx
rename to src/apps/officer-web/Screens/Dashboard/Chat/EmbeddableChat.tsx
index ad5c2b25..34af76c8 100644
--- a/src/workspaces/plugins/Chat/client/EmbeddableChat.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/EmbeddableChat.tsx
@@ -4,7 +4,7 @@ import { toast } from 'sonner';
import { useClient } from 'hooks/useClient';
import type { ModelOption } from '@/state/useModels';
import type { useClaude } from './useClaude';
-import { MessageList } from './MessageList';
+import { MessageList } from 'widgets/Chat';
import { InputArea } from './InputArea';
export type Attachment =
diff --git a/src/workspaces/plugins/Chat/client/InputArea.tsx b/src/apps/officer-web/Screens/Dashboard/Chat/InputArea.tsx
similarity index 99%
rename from src/workspaces/plugins/Chat/client/InputArea.tsx
rename to src/apps/officer-web/Screens/Dashboard/Chat/InputArea.tsx
index 703b5847..b11b649b 100644
--- a/src/workspaces/plugins/Chat/client/InputArea.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/InputArea.tsx
@@ -10,7 +10,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import type { ModelOption } from '@/state/useModels';
-import type { ChatMessage } from './types';
+import type { ChatMessage } from 'widgets/Chat';
import type { Attachment } from './EmbeddableChat';
import { Settings } from './Settings';
diff --git a/src/workspaces/plugins/Chat/client/OpenCodeModelPicker.tsx b/src/apps/officer-web/Screens/Dashboard/Chat/OpenCodeModelPicker.tsx
similarity index 100%
rename from src/workspaces/plugins/Chat/client/OpenCodeModelPicker.tsx
rename to src/apps/officer-web/Screens/Dashboard/Chat/OpenCodeModelPicker.tsx
diff --git a/src/workspaces/plugins/Chat/client/Settings.tsx b/src/apps/officer-web/Screens/Dashboard/Chat/Settings.tsx
similarity index 98%
rename from src/workspaces/plugins/Chat/client/Settings.tsx
rename to src/apps/officer-web/Screens/Dashboard/Chat/Settings.tsx
index a211b3d2..16f932dd 100644
--- a/src/workspaces/plugins/Chat/client/Settings.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/Settings.tsx
@@ -1,7 +1,7 @@
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { useAuth } from 'hooks/useAuth';
import type { ModelOption } from '@/state/useModels';
-import type { ChatMessage } from './types';
+import type { ChatMessage } from 'widgets/Chat';
import { OpenCodeModelPicker } from './OpenCodeModelPicker';
type SettingsProps = {
diff --git a/src/apps/officer-web/Screens/Dashboard/Chat/index.tsx b/src/apps/officer-web/Screens/Dashboard/Chat/index.tsx
index f2242134..582f7afe 100644
--- a/src/apps/officer-web/Screens/Dashboard/Chat/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/index.tsx
@@ -1,8 +1,10 @@
import { useState } from 'react';
import { useParams } from 'react-router';
import { useQueryClient } from '@tanstack/react-query';
-import { useClaude, useOpenCode, type SessionEntry } from 'plugins/Chat/client';
-import { ChatPanel } from 'plugins/ChatHistory/client';
+import type { SessionEntry } from 'widgets/Chat';
+import { useClaude } from './useClaude';
+import { useOpenCode } from './useOpenCode';
+import { ChatPanel } from './ChatPanel';
import { useVisibleClaudeModels, useVisibleOpenCodeModels } from '@/state/useModels';
export const ClaudeChat = () => {
diff --git a/src/workspaces/plugins/Chat/client/useClaude.ts b/src/apps/officer-web/Screens/Dashboard/Chat/useClaude.ts
similarity index 98%
rename from src/workspaces/plugins/Chat/client/useClaude.ts
rename to src/apps/officer-web/Screens/Dashboard/Chat/useClaude.ts
index ece4dfbf..6f5fd439 100644
--- a/src/workspaces/plugins/Chat/client/useClaude.ts
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/useClaude.ts
@@ -1,7 +1,7 @@
import { useState, useEffect, useRef } from 'react';
import { useChatWebSocket } from 'hooks/useChatWebSocket';
-import { useSessions } from 'plugins/ChatHistory/client';
-import type { ChatMessage, ServerMessage, TaskInfo } from './types';
+import { useSessions } from 'widgets/ChatHistory';
+import type { ChatMessage, ServerMessage, TaskInfo } from 'widgets/Chat';
const SAVE_DEBOUNCE_MS = 1000;
diff --git a/src/workspaces/plugins/Chat/client/useOpenCode.ts b/src/apps/officer-web/Screens/Dashboard/Chat/useOpenCode.ts
similarity index 98%
rename from src/workspaces/plugins/Chat/client/useOpenCode.ts
rename to src/apps/officer-web/Screens/Dashboard/Chat/useOpenCode.ts
index d86e5594..22d3d607 100644
--- a/src/workspaces/plugins/Chat/client/useOpenCode.ts
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/useOpenCode.ts
@@ -2,8 +2,8 @@ import { useState, useEffect, useRef } from 'react';
import { useChatWebSocket } from 'hooks/useChatWebSocket';
import { useSettings } from '@/state/useSettings';
import { useVisibleOpenCodeModels } from '@/state/useModels';
-import { useOpenCodeSessions } from 'plugins/ChatHistory/client';
-import type { ChatMessage, ServerMessage, TaskInfo } from './types';
+import { useOpenCodeSessions } from 'widgets/ChatHistory';
+import type { ChatMessage, ServerMessage, TaskInfo } from 'widgets/Chat';
type UseOpenCodeOptions = {
replaceUrl?: boolean;
diff --git a/src/workspaces/plugins/ChatHistory/client/Screen.tsx b/src/apps/officer-web/Screens/Dashboard/ChatHistory/Screen.tsx
similarity index 97%
rename from src/workspaces/plugins/ChatHistory/client/Screen.tsx
rename to src/apps/officer-web/Screens/Dashboard/ChatHistory/Screen.tsx
index 7ec337c6..2f40e34a 100644
--- a/src/workspaces/plugins/ChatHistory/client/Screen.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/ChatHistory/Screen.tsx
@@ -3,8 +3,7 @@ import { Link } from 'react-router';
import { Plus, MessageSquare, Trash2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/Card';
-import { useSessions } from './state/useSessions';
-import { useOpenCodeSessions } from './state/useOpenCodeSessions';
+import { useSessions, useOpenCodeSessions } from 'widgets/ChatHistory';
type Filter = 'all' | 'claude' | 'opencode';
diff --git a/src/workspaces/plugins/ChatHistory/client/Widget.tsx b/src/apps/officer-web/Screens/Dashboard/ChatHistory/Widget.tsx
similarity index 97%
rename from src/workspaces/plugins/ChatHistory/client/Widget.tsx
rename to src/apps/officer-web/Screens/Dashboard/ChatHistory/Widget.tsx
index 232fde4e..9e9745c1 100644
--- a/src/workspaces/plugins/ChatHistory/client/Widget.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/ChatHistory/Widget.tsx
@@ -2,8 +2,8 @@ import { useMemo } from 'react';
import { Link } from 'react-router';
import { MessageSquare, Trash2, ChevronDown, ChevronUp } from 'lucide-react';
import { Card } from '@/components/Card';
-import { useSessions } from './state/useSessions';
-import { useOpenCodeSessions } from './state/useOpenCodeSessions';
+import { useSessions } from 'widgets/ChatHistory';
+import { useOpenCodeSessions } from 'widgets/ChatHistory';
import { useUserState } from '@/state/useUserState';
export const ChatHistory = () => {
diff --git a/src/apps/officer-web/Screens/Dashboard/ChatHistory/index.tsx b/src/apps/officer-web/Screens/Dashboard/ChatHistory/index.tsx
new file mode 100644
index 00000000..031f0f94
--- /dev/null
+++ b/src/apps/officer-web/Screens/Dashboard/ChatHistory/index.tsx
@@ -0,0 +1,2 @@
+export { ChatHistory as ChatHistoryWidget } from './Widget';
+export { SessionList } from './Screen';
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Screen/TaskRunnerModal.tsx b/src/apps/officer-web/Screens/Dashboard/Files/Screen/TaskRunnerModal.tsx
index 1e82b128..86a4cdc2 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Screen/TaskRunnerModal.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Files/Screen/TaskRunnerModal.tsx
@@ -3,7 +3,10 @@ import { X } from 'lucide-react';
import { Dialog, DialogOverlay, DialogPortal } from '@/components/ui/dialog';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { cardStyle } from '@/components/Card';
-import { useClaude, useOpenCode, EmbeddableChat, type TaskInfo } from 'plugins/Chat/client';
+import type { TaskInfo } from 'widgets/Chat';
+import { useClaude } from '@/Screens/Dashboard/Chat/useClaude';
+import { useOpenCode } from '@/Screens/Dashboard/Chat/useOpenCode';
+import { EmbeddableChat } from '@/Screens/Dashboard/Chat/EmbeddableChat';
import { useVisibleClaudeModels, useVisibleOpenCodeModels } from '@/state/useModels';
import { useSettings } from '@/state/useSettings';
import type { TaskSummary } from 'widgets/FileBrowser';
diff --git a/src/apps/officer-web/Screens/Dashboard/Home/ChatLauncher.tsx b/src/apps/officer-web/Screens/Dashboard/Home/ChatLauncher.tsx
index b117a486..c37d25b5 100644
--- a/src/apps/officer-web/Screens/Dashboard/Home/ChatLauncher.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Home/ChatLauncher.tsx
@@ -26,7 +26,7 @@ import {
} from '@/components/ui/dropdown-menu';
import { useSettings } from '@/state/useSettings';
import { useVisibleClaudeModels, useVisibleOpenCodeModels } from '@/state/useModels';
-import type { Attachment } from 'plugins/Chat/client';
+import type { Attachment } from '@/Screens/Dashboard/Chat/EmbeddableChat';
export const ChatLauncher = () => {
const navigate = useNavigate();
diff --git a/src/apps/officer-web/Screens/Dashboard/Home/index.tsx b/src/apps/officer-web/Screens/Dashboard/Home/index.tsx
index 46351a48..917c51b0 100644
--- a/src/apps/officer-web/Screens/Dashboard/Home/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Home/index.tsx
@@ -1,6 +1,6 @@
import { ChatLauncher } from './ChatLauncher';
import { FileBrowserWidget as FileBrowser } from '@/Screens/Dashboard/Files';
-import { Widget as ChatHistory } from 'plugins/ChatHistory/client';
+import { ChatHistoryWidget as ChatHistory } from '@/Screens/Dashboard/ChatHistory';
import { Catalog } from 'sounds';
export const HomeScreen = () => {
diff --git a/src/apps/officer-web/Screens/Dashboard/ResourcePage.tsx b/src/apps/officer-web/Screens/Dashboard/ResourcePage.tsx
index a2e1aa58..d6a8be20 100644
--- a/src/apps/officer-web/Screens/Dashboard/ResourcePage.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/ResourcePage.tsx
@@ -10,7 +10,9 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } f
import { useClient } from 'hooks/useClient';
import { useVisibleClaudeModels } from '@/state/useModels';
import { Card } from '@/components/Card';
-import { useClaude, EmbeddableChat, type ChatMessage } from 'plugins/Chat/client';
+import type { ChatMessage } from 'widgets/Chat';
+import { useClaude } from '@/Screens/Dashboard/Chat/useClaude';
+import { EmbeddableChat } from '@/Screens/Dashboard/Chat/EmbeddableChat';
type ResourceSummary = {
dirName: string;
name: string;
diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/index.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/index.tsx
index 990f7b17..efa1012a 100644
--- a/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/index.tsx
@@ -5,7 +5,9 @@ import { toast } from 'sonner';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/Card';
import { useClient } from 'hooks/useClient';
-import { EmbeddableChat, useClaude, useOpenCode } from 'plugins/Chat/client';
+import { useClaude } from '@/Screens/Dashboard/Chat/useClaude';
+import { useOpenCode } from '@/Screens/Dashboard/Chat/useOpenCode';
+import { EmbeddableChat } from '@/Screens/Dashboard/Chat/EmbeddableChat';
import { useVisibleClaudeModels, useVisibleOpenCodeModels } from '@/state/useModels';
type AppStatus = {
diff --git a/src/apps/officer-web/Screens/Dashboard/TaskLogs/index.tsx b/src/apps/officer-web/Screens/Dashboard/TaskLogs/index.tsx
index 285e2f70..a5e53d08 100644
--- a/src/apps/officer-web/Screens/Dashboard/TaskLogs/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/TaskLogs/index.tsx
@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
import { Search, AlertCircle, CheckCircle2, Clock, ArrowLeft } from 'lucide-react';
import { useClient } from 'hooks/useClient';
import { Card } from '@/components/Card';
-import { MessageBubble, type ChatMessage } from 'plugins/Chat/client';
+import { MessageBubble, type ChatMessage } from 'widgets/Chat';
type LogMetadata = {
filename: string;
diff --git a/src/apps/officer-web/Screens/Dashboard/index.tsx b/src/apps/officer-web/Screens/Dashboard/index.tsx
index 662215cb..eb91c027 100644
--- a/src/apps/officer-web/Screens/Dashboard/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/index.tsx
@@ -12,3 +12,4 @@ export * from './TaskLogs';
export * from './Tasks';
export * from './Terminal';
export * from './Files';
+export * from './ChatHistory';
diff --git a/src/apps/officer-web/state/useInitialData.ts b/src/apps/officer-web/state/useInitialData.ts
index 48ae9cb0..f1f17cc0 100644
--- a/src/apps/officer-web/state/useInitialData.ts
+++ b/src/apps/officer-web/state/useInitialData.ts
@@ -1,4 +1,4 @@
-import { useSessions } from 'plugins/ChatHistory/client';
+import { useSessions } from 'widgets/ChatHistory';
import { usePlans } from './usePlans';
import { useSettings } from './useSettings';
import { useThemeSync } from './useThemeSync';
diff --git a/src/workspaces/plugins/Chat/index.ts b/src/workspaces/plugins/Chat/index.ts
deleted file mode 100644
index dda14b28..00000000
--- a/src/workspaces/plugins/Chat/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export * from './client';
-
-export const plugin = {
- id: 'Chat',
- name: 'Chat',
- description: 'Embeddable chat UI',
-};
diff --git a/src/workspaces/plugins/ChatHistory/client/index.ts b/src/workspaces/plugins/ChatHistory/client/index.ts
deleted file mode 100644
index 964d9c50..00000000
--- a/src/workspaces/plugins/ChatHistory/client/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export { ChatHistory as Widget } from './Widget';
-export { SessionList as Screen } from './Screen';
-export { ChatPanel } from './ChatPanel';
-export { SessionBar } from './SessionBar';
-export { useSessions } from './state/useSessions';
-export { useOpenCodeSessions } from './state/useOpenCodeSessions';
-export { useSlashCommands, type SlashCommandResult } from './state/useSlashCommands';
diff --git a/src/workspaces/plugins/ChatHistory/index.ts b/src/workspaces/plugins/ChatHistory/index.ts
deleted file mode 100644
index 7ccfde25..00000000
--- a/src/workspaces/plugins/ChatHistory/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export { Widget, Screen, ChatPanel, SessionBar } from './client';
-
-export const plugin = {
- id: 'ChatHistory',
- name: 'Chat History',
- description: 'Session management for chat history',
-};
diff --git a/src/workspaces/plugins/PLUGINS.md b/src/workspaces/plugins/PLUGINS.md
deleted file mode 100644
index 557ae6b1..00000000
--- a/src/workspaces/plugins/PLUGINS.md
+++ /dev/null
@@ -1,259 +0,0 @@
-# Building a Plugin
-
-Plugins are self-contained modules that extend Officer with new features. They live under `src/workspaces/plugins/` and are auto-discovered at startup — no registration step required.
-
-## Directory Structure
-
-```
-src/workspaces/plugins/
-├── package.json # Workspace exports (update when adding a plugin)
-└── MyPlugin/
- ├── index.ts # Metadata + re-exports
- ├── server/
- │ ├── index.ts # Exports router and apiPath
- │ └── router.ts # Hono API routes
- └── client/
- ├── index.ts # Exports Widget and/or Screen
- ├── Widget.tsx # Compact component (dashboard home)
- ├── Screen/
- │ └── index.tsx # Full-page component (dedicated route)
- └── state/
- └── useMyPlugin.ts # API client hook
-```
-
-A plugin can be server-only, client-only, or both. The discovery system checks for the presence of `server/index.ts` and `client/index.ts` to determine what the plugin provides.
-
-## Step-by-Step
-
-### 1. Create the plugin directory
-
-```
-mkdir -p src/workspaces/plugins/MyPlugin/{server,client/state,client/Screen}
-```
-
-### 2. Plugin metadata — `MyPlugin/index.ts`
-
-Every plugin must export a `plugin` object with metadata. This is read by the settings UI and the `/plugins` API.
-
-```ts
-export { Widget, Screen } from './client';
-export { router, apiPath } from './server';
-
-export const plugin = {
- id: 'MyPlugin', // Must match directory name
- name: 'My Plugin', // Display name in settings
- description: 'What this plugin does',
-};
-```
-
-If your plugin is server-only, omit the client export. If client-only, omit the server export.
-
-### 3. Server router — `MyPlugin/server/`
-
-**`server/index.ts`** — Exports the router instance and the API path prefix:
-
-```ts
-export * from './router';
-
-export const apiPath = 'my-plugin';
-```
-
-The `apiPath` determines the URL prefix. This router gets mounted at `/api/my-plugin`.
-
-**`server/router.ts`** — Define your API endpoints:
-
-```ts
-import { createRouter } from '@@/create-router';
-import * as errors from '@@/custom-errors';
-
-export const router = createRouter();
-
-router.get('/items', async (ctx) => {
- const user = ctx.get('user'); // Authenticated user
- // ...
- return ctx.json({ items: [] });
-});
-
-router.post('/items', async (ctx) => {
- const body = ctx.get('body'); // Parsed request body
- if (!body.name) throw errors.BAD_REQUEST('Name is required');
- // ...
- return ctx.json({ created: true });
-});
-```
-
-Key points:
-- `createRouter()` from `@@/create-router` gives you a typed Hono router
-- All plugin routes are **protected** — user authentication is enforced automatically
-- Access the authenticated user with `ctx.get('user')` (returns `User` from types)
-- Access parsed body with `ctx.get('body')`
-- Throw `CustomError` instances for error responses — they're caught by the global error handler
-
-**Available error helpers** (`@@/custom-errors`):
-- `BAD_REQUEST(msg?)` — 400
-- `UNAUTHORIZED(msg?)` — 401
-- `FORBIDDEN(msg?)` — 403
-- `NOT_FOUND(msg?)` — 404
-- `CONFLICT(msg?)` — 409
-- `INTERNAL_SERVER_ERROR(msg?)` — 500
-- `TOO_MANY_REQUESTS(msg?, retryAfter?)` — 429
-
-**User data helpers** (`@@/data-path`):
-- `DATA_PATH` — base data directory
-- `getHomeDir(email)` — user's home directory
-
-### 4. Client components — `MyPlugin/client/`
-
-**`client/index.ts`** — Export your components with standardized names:
-
-```ts
-export { MyPluginWidget as Widget } from './Widget';
-export { MyPluginScreen as Screen } from './Screen';
-```
-
-**`client/state/useMyPlugin.ts`** — API client hook:
-
-```ts
-import { useClient } from 'hooks/useClient';
-
-type Item = {
- id: string;
- name: string;
-};
-
-export const useMyPlugin = () => {
- const client = useClient();
-
- return {
- listItems: () => client.get- ('/my-plugin/items'),
- createItem: (name: string) => client.post('/my-plugin/items', { name }),
- };
-};
-```
-
-The `useClient()` hook provides an authenticated HTTP client. The path must match your `apiPath` from the server.
-
-**`client/Widget.tsx`** — Compact component for the dashboard home:
-
-```tsx
-import { useState, useEffect } from 'react';
-import { useMyPlugin } from './state/useMyPlugin';
-
-export const MyPluginWidget = () => {
- // Widget implementation
-};
-```
-
-**`client/Screen/index.tsx`** — Full-page component:
-
-```tsx
-import { DashboardLayout } from '@/Screens/Dashboard/Layout';
-
-export const MyPluginScreen = () => {
- return (
-
- {/* Screen implementation */}
-
- );
-};
-```
-
-### 5. Register workspace exports — `plugins/package.json`
-
-Add subpath exports so the monorepo can import your plugin:
-
-```json
-{
- "exports": {
- "./FileBrowser": "./FileBrowser/index.ts",
- "./FileBrowser/client": "./FileBrowser/client/index.ts",
- "./FileBrowser/server": "./FileBrowser/server/index.ts",
- "./MyPlugin": "./MyPlugin/index.ts",
- "./MyPlugin/client": "./MyPlugin/client/index.ts",
- "./MyPlugin/server": "./MyPlugin/server/index.ts"
- }
-}
-```
-
-This lets other code import your plugin as:
-
-```ts
-import { Widget, Screen } from 'plugins/MyPlugin/client';
-import { router, apiPath } from 'plugins/MyPlugin/server';
-import { plugin } from 'plugins/MyPlugin';
-```
-
-### 6. Wire into the app (optional)
-
-The server router is auto-discovered and mounted — no changes needed. But if your plugin has client components that should appear in the main app, you'll need to add them manually:
-
-**Route** — `src/apps/officer-web/App.tsx`:
-
-```tsx
-import { Screen as MyPluginScreen } from 'plugins/MyPlugin/client';
-
-// Inside the authenticated routes:
-{plugins?.MyPlugin !== false && } />}
-```
-
-**Nav item** — `src/apps/officer-web/Screens/Dashboard/Layout.tsx`:
-
-```tsx
-{plugins?.MyPlugin !== false && (
-
-
-
- My Plugin
-
-
-)}
-```
-
-**Home widget** — `src/apps/officer-web/Screens/Dashboard/Home/index.tsx`:
-
-```tsx
-import { Widget as MyPluginWidget } from 'plugins/MyPlugin/client';
-
-// Inside the layout:
-{plugins?.MyPlugin !== false && }
-```
-
-The `plugins` object comes from `useServerSettings()` and is `undefined` when no overrides exist — so `plugins?.MyPlugin !== false` defaults to showing the plugin (opt-out model).
-
-## Enable/Disable
-
-Plugins are **enabled by default**. Admins can disable them in Server Settings > Plugins, which writes to `~/.config/officer.dev/server-settings.json`:
-
-```json
-{
- "plugins": {
- "MyPlugin": false
- }
-}
-```
-
-When disabled:
-- **Server-side**: The plugin router is not mounted (requires server restart)
-- **Client-side**: Routes, nav items, and widgets are hidden immediately
-
-## Import Aliases
-
-| Alias | Resolves to | Use in |
-|-------|-------------|--------|
-| `@@/` | `src/servers/` | Server code (`createRouter`, `custom-errors`, `data-path`) |
-| `@/` | `src/apps/officer-web/` | Client code (`components/ui/*`, `Screens/*`, `state/*`) |
-| `hooks/` | `src/workspaces/hooks/src/` | Both (`useClient`, `useAuth`) |
-| `types` | `src/workspaces/types/` | Both |
-| `config` | `src/workspaces/config/` | Client code |
-| `plugins/` | `src/workspaces/plugins/` | Both |
-
-## Checklist
-
-- [ ] `MyPlugin/index.ts` exports `plugin` metadata with `id` matching directory name
-- [ ] `MyPlugin/server/index.ts` exports `router` and `apiPath`
-- [ ] `MyPlugin/client/index.ts` exports `Widget` and/or `Screen`
-- [ ] `plugins/package.json` has subpath exports for the new plugin
-- [ ] App routes gated with `plugins?.MyPlugin !== false`
-- [ ] Nav items gated with `plugins?.MyPlugin !== false`
-- [ ] Home widgets gated with `plugins?.MyPlugin !== false`
-- [ ] `bunx tsgo` — no new type errors
diff --git a/src/workspaces/plugins/package.json b/src/workspaces/plugins/package.json
deleted file mode 100644
index 1302524f..00000000
--- a/src/workspaces/plugins/package.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "name": "plugins",
- "version": "0.0.1",
- "private": true,
- "type": "module",
- "exports": {
- "./Chat": "./Chat/index.ts",
- "./Chat/client": "./Chat/client/index.ts",
- "./ChatHistory": "./ChatHistory/index.ts",
- "./ChatHistory/client": "./ChatHistory/client/index.ts"
- }
-}
diff --git a/src/workspaces/plugins/Chat/client/MessageBubble.tsx b/src/workspaces/widgets/Chat/MessageBubble.tsx
similarity index 100%
rename from src/workspaces/plugins/Chat/client/MessageBubble.tsx
rename to src/workspaces/widgets/Chat/MessageBubble.tsx
diff --git a/src/workspaces/plugins/Chat/client/MessageList.tsx b/src/workspaces/widgets/Chat/MessageList.tsx
similarity index 100%
rename from src/workspaces/plugins/Chat/client/MessageList.tsx
rename to src/workspaces/widgets/Chat/MessageList.tsx
diff --git a/src/workspaces/plugins/Chat/client/QuestionActivity.tsx b/src/workspaces/widgets/Chat/QuestionActivity.tsx
similarity index 100%
rename from src/workspaces/plugins/Chat/client/QuestionActivity.tsx
rename to src/workspaces/widgets/Chat/QuestionActivity.tsx
diff --git a/src/workspaces/plugins/Chat/client/ToolActivity.tsx b/src/workspaces/widgets/Chat/ToolActivity.tsx
similarity index 100%
rename from src/workspaces/plugins/Chat/client/ToolActivity.tsx
rename to src/workspaces/widgets/Chat/ToolActivity.tsx
diff --git a/src/workspaces/plugins/Chat/client/index.ts b/src/workspaces/widgets/Chat/index.ts
similarity index 50%
rename from src/workspaces/plugins/Chat/client/index.ts
rename to src/workspaces/widgets/Chat/index.ts
index 2cc184b9..96c5f870 100644
--- a/src/workspaces/plugins/Chat/client/index.ts
+++ b/src/workspaces/widgets/Chat/index.ts
@@ -1,11 +1,5 @@
-export { EmbeddableChat, type Attachment } from './EmbeddableChat';
export { MessageList } from './MessageList';
-export { InputArea } from './InputArea';
-export { Settings } from './Settings';
export { MessageBubble, StreamingBubble } from './MessageBubble';
export { ToolActivity } from './ToolActivity';
export { QuestionActivity } from './QuestionActivity';
-export { OpenCodeModelPicker } from './OpenCodeModelPicker';
-export { useClaude } from './useClaude';
-export { useOpenCode } from './useOpenCode';
export type { ChatMessage, SessionEntry, ServerMessage, TaskInfo } from './types';
diff --git a/src/workspaces/plugins/Chat/client/types.ts b/src/workspaces/widgets/Chat/types.ts
similarity index 100%
rename from src/workspaces/plugins/Chat/client/types.ts
rename to src/workspaces/widgets/Chat/types.ts
diff --git a/src/workspaces/plugins/ChatHistory/client/SessionBar.tsx b/src/workspaces/widgets/ChatHistory/SessionBar.tsx
similarity index 100%
rename from src/workspaces/plugins/ChatHistory/client/SessionBar.tsx
rename to src/workspaces/widgets/ChatHistory/SessionBar.tsx
diff --git a/src/workspaces/widgets/ChatHistory/index.ts b/src/workspaces/widgets/ChatHistory/index.ts
new file mode 100644
index 00000000..2d149bce
--- /dev/null
+++ b/src/workspaces/widgets/ChatHistory/index.ts
@@ -0,0 +1,4 @@
+export { SessionBar } from './SessionBar';
+export { useSessions } from './useSessions';
+export { useOpenCodeSessions } from './useOpenCodeSessions';
+export { useSlashCommands, type SlashCommandResult } from './useSlashCommands';
diff --git a/src/workspaces/plugins/ChatHistory/client/state/useOpenCodeSessions.ts b/src/workspaces/widgets/ChatHistory/useOpenCodeSessions.ts
similarity index 95%
rename from src/workspaces/plugins/ChatHistory/client/state/useOpenCodeSessions.ts
rename to src/workspaces/widgets/ChatHistory/useOpenCodeSessions.ts
index 40b9cfc8..39bae256 100644
--- a/src/workspaces/plugins/ChatHistory/client/state/useOpenCodeSessions.ts
+++ b/src/workspaces/widgets/ChatHistory/useOpenCodeSessions.ts
@@ -1,4 +1,4 @@
-import type { SessionEntry, ChatMessage } from 'plugins/Chat/client';
+import type { SessionEntry, ChatMessage } from 'widgets/Chat';
import { useAuth } from 'hooks/useAuth';
import { useClient } from 'hooks/useClient';
import { useQuery, useQueryClient } from '@tanstack/react-query';
diff --git a/src/workspaces/plugins/ChatHistory/client/state/useSessions.ts b/src/workspaces/widgets/ChatHistory/useSessions.ts
similarity index 96%
rename from src/workspaces/plugins/ChatHistory/client/state/useSessions.ts
rename to src/workspaces/widgets/ChatHistory/useSessions.ts
index c3266700..922ecdce 100644
--- a/src/workspaces/plugins/ChatHistory/client/state/useSessions.ts
+++ b/src/workspaces/widgets/ChatHistory/useSessions.ts
@@ -1,4 +1,4 @@
-import type { SessionEntry, ChatMessage } from 'plugins/Chat/client';
+import type { SessionEntry, ChatMessage } from 'widgets/Chat';
import type { SlashCommandResult } from './useSlashCommands';
import { useAuth } from 'hooks/useAuth';
import { useClient } from 'hooks/useClient';
diff --git a/src/workspaces/plugins/ChatHistory/client/state/useSlashCommands.ts b/src/workspaces/widgets/ChatHistory/useSlashCommands.ts
similarity index 100%
rename from src/workspaces/plugins/ChatHistory/client/state/useSlashCommands.ts
rename to src/workspaces/widgets/ChatHistory/useSlashCommands.ts
diff --git a/src/workspaces/widgets/package.json b/src/workspaces/widgets/package.json
index 635f0ca4..57cc316a 100644
--- a/src/workspaces/widgets/package.json
+++ b/src/workspaces/widgets/package.json
@@ -3,6 +3,8 @@
"private": true,
"exports": {
"./Terminal": "./Terminal/index.ts",
- "./FileBrowser": "./FileBrowser/index.ts"
+ "./FileBrowser": "./FileBrowser/index.ts",
+ "./ChatHistory": "./ChatHistory/index.ts",
+ "./Chat": "./Chat/index.ts"
}
}