AGENTS.md@packages/client/src/lib/plugins · git:20261022.4339930 · 2026-10-22 · sha256 006c9a8326af7e41

AGENTS.md@packages/client/src/lib/plugins git:20261022.4339930A

Immutable. This exact content never changes and is served at /api/v1/blob/006c9a8326af7e41.

# DOX — packages/client/src/lib/plugins

Files in this directory. One row per source file.

| File | Purpose |
|------|---------|
| `__tests__/chat-view-primitive.test.tsx` | `ui:chat-view` renders s1 messages, composer count 1 vs 0 (`readOnly`), incremental fold, no second `UiPrimitiveProvider` (source pin). See change: decouple-plugins-from-core. |
| `__tests__/plugin-availability.test.ts` | Store semantics: pre-read available, disabled/not-loaded/refused unavailable, snapshot changes only on real change. See change: decouple-plugins-from-core. |
| `__tests__/plugin-registry-event-types.test.ts` | Generated registry carries `eventTypes` for subagents (`subagent_*`) + flows (`flow_*`); owner lookup survives `setEnabledSet(∅)`. See change: decouple-plugins-from-core. |
| `__tests__/runtime-plugin-loader.test.tsx` | Loader: skip conditions (no import map / remote), registration parity (entry fns, lazy `module` 0→1, catalog, routes union, names, owners, bump, same-id skip), runtime predicate, failing entry import, `<link>` once on first render, `plugins-changed` state machine, late-owner regroup. See change: decouple-plugins-from-core. |
| `chat-view-primitive.tsx` | `ui:chat-view` impl `ChatViewPrimitive({sessionId, readOnly?, compact?})`: session `SessionState` = incremental fold (`useFoldedSessionState`) of `useSessionEvents(sessionId)` via `reduceEvent`; mounts `SessionAssetsProvider` (assets from `useSessionState`), renders `ChatView` + `CommandInput` (omitted when `readOnly`; send via `usePluginSend` `send_prompt`). Mounts NO `UiPrimitiveProvider`. `compact` → `data-compact` attr only (no layout behaviour yet). Needs the shell `PluginContextProvider` (strict hooks): not usable in a bare embed. See change: decouple-plugins-from-core. |
| `embedded-app-shell.ts` | `createEmbeddedAppShell(deps)` → runtime `EmbeddedAppShell`. `fetch`/`wsUrl` refuse non-root-relative paths (`isSafeDashboardPath`, no request); fetch adds `Authorization: Bearer` from `getApiBearer()`, `credentials:"same-origin"`; wsUrl mints `mintWsTicket("browser")` iff `getApiBearer()\|\|isDevicePaired()` (useWebSocket predicate). Folder codec = `lib/util/folder-encoding.ts`. Wired once in `App.tsx`. See change: add-plugin-app-host. |
| `event-type-owners.ts` | Module store of plugin event-type owners — a reducer INPUT (static per registry). `setEventTypeOwners`, `getEventTypeOwners`, `ownerOfEventType(type)`, `ownerDisplayName(id)`. Seeded once in `App.tsx` from `registry.getEventTypeOwners()`. See change: decouple-plugins-from-core. |
| `host-primitives.tsx` | `createHostPrimitiveRegistry()` (no params): full registry, one registration per `UI_PRIMITIVE_KEYS` entry (incl. `chatView`, `elapsedBadge`, `ConfirmDialogPrimitive`/`ToolCallStepPrimitive`/`ThinkingBlockPrimitive` adapters, shell-bound selectors from `shell-primitives.tsx`). Used by `main.tsx` and re-exported from `chat-embed`. See change: decouple-plugins-from-core. |
| `plugin-availability.ts` | `pluginAvailability` module store (enabled ∧ loaded ∧ client not refused) fed by `usePluginEnabledSet` from `/api/health.plugins[]`. `applyPluginStatuses`, `isPluginUnavailable`, `usePluginAvailability()` (immutable snapshot per change, `useSyncExternalStore`). No status read yet or id not listed → available. Read at RENDER time only; reducer never reads it. See change: decouple-plugins-from-core. |
| `runtime-plugin-loader.ts` | Shell-owned runtime plugin loader. `startRuntimePluginLoader(deps)` / `loadRuntimePlugins(deps)` (serialised): skips on non-empty `getApiBase()` or no `script[type=importmap]`, fetches `/api/plugins/runtime-clients`, `import()`s each entry (build-time id wins; failing import skipped + `console.error` with id), registers claims (entry fns + component or `lazyClaim(module)`; `clientStyles` `<link>` on first claim render), reducers/finalizers, i18n catalog, route union, names, event-type owners, then `bumpSlotClaimsVersion()` + `runtime-plugins-registered` event. A loaded plugin gone/hash-changed is never re-imported: `reloadNeeded`. Exports `useRuntimeLoaderState`, `useRuntimePluginNames`, `getRuntimeLoadingBlocker`, `regroupAllSessionStates`. See change: decouple-plugins-from-core (6.14, 6.15, 6.17, 6.18). Constant / same-origin navigation sink carries a `client-sinks: allow — <reason>` annotation. See change: harden-client-sinks. |
| `shell-primitives.tsx` | Shell-bound UI primitive wrappers registered in `main.tsx`. Exports `ModelSelectorPrimitive` (public contract `{ current, models, onSelect, placeholder }`; injects `favorites` / `onToggleFavorite` / `onRefresh` from `ModelConfigContext` via `useModelConfigOptional()` — context absent ⇒ no favorites, no refresh, caller's list still renders) and `ThinkingLevelSelectorPrimitive` (thin pass-through to the shell `ThinkingLevelSelector`; `supportedLevels` stays caller-supplied). Extracted from `main.tsx` so the binding is testable without booting the app. See change: upgrade-model-selector-primitives. Change: `ModelSelectorPrimitive` forwards `allowRoles`. See change: add-role-aware-model-refs. |