# @meistrari/chat-nuxt

Nuxt module that adds a complete AI chat interface to your app. Server-side chat behavior is delegated to the dedicated Chat API; the Nuxt module owns the UI, auth bridge, and local proxy routes.

Consumer documentation lives in `docs/guides/` (indexed [below](#guides)); this README covers the package boundary and working on the package itself.

## Public Surface

`@meistrari/chat-nuxt` is an external package. Its supported consumer surface is intentionally small:

- Nuxt module registration through `@meistrari/chat-nuxt`.
- Module options under `chatNuxt`.
- Auto-registered `<MeistrariChatEmbed>`.
- Public component import from `@meistrari/chat-nuxt/components/MeistrariChatEmbed`.
- Public types from the package root and `@meistrari/chat-nuxt/types/*`.

Runtime internals under `src/runtime/**`, generated Nuxt aliases, and workspace-only schema package imports are implementation details unless they are explicitly exported above.

`typedoc.ts` at the package root is the documentation barrel for that surface — it is what the published API reference is generated from. Adding an export there makes it public; keep internals out of it.

## Guides

| Guide | Covers |
|-------|--------|
| [Installation and setup](docs/guides/installation.md) | Install, Vue dedupe override, module registration, module options, auth modes |
| [Embedding in your layout](docs/guides/embedding.md) | Layout, key props, conversation scoping, events, slots |
| [Chat runtime modes](docs/guides/tela-agent-mode.md) | Default chat mode vs Tela agent mode, agent inputs |
| [Citations](docs/guides/citations.md) | `cite://` chips, metadata popover, document panel |
| [Feature flags](docs/guides/feature-flags.md) | Usage tab, debug entry, cancel button, PostHog wiring |
| [Message feedback](docs/guides/message-feedback.md) | 👍/👎 controls and host-owned persistence |
| [Composer extension](docs/guides/composer-extension.md) | Host actions, shared attachment strip, outbound content and retry lifecycle, "/" commands |
| [Interactive message widgets](docs/guides/message-widgets.md) | Message context and programmatic send for `customComponents` widgets |
| [Workspace settings](docs/guides/workspace-settings.md) | Settings overrides, agent runtime ownership, `<ChatConfigurationModal>` |
| [Upgrading to 4.0](docs/guides/upgrading-to-4.md) | Required Chat API version for v2 sends, upgrade order, vue-i18n removal |

## Harness selection

Select the Tela agent; its published `agent.config.json` determines the harness:

```vue
<MeistrariChatEmbed tela-agent-id="your-agent-id" />
```

The chat resolves the agent before mounting its UI. Explicit branches use that branch's configuration. Missing, unreadable or unsupported harnesses show an error and never fall back to Claude. The host cannot select a harness through props or HTTP headers.

Both harnesses share the chat screen, composer, navigation and state controller, while their protocol adapters and renderers remain separate. Selecting another agent clears the old UI and resolves its configuration again. The backend independently reads the configured harness for routed requests.

Requires Agent API v4’s `GET /v4/agents/{organizationName}/{repository}/harness` endpoint. Chat resolves the existing agent coordinates, then reads the configured harness directly from Agent API using the published commit or selected branch. The response exposes only the harness. Deploy the Agent API endpoint before this Chat version; no Tela API changes are required.

## Conversation forks

Completed assistant responses offer **Continuar em nova conversa** in the three-dot
menu below the response, beside the feedback controls. User messages cannot be forked.
The embed selects the new conversation and emits its existing `update:conversationId` event.
The new conversation stays idle until the user sends a message.
Fork titles use `<original title> - Cópia <number>` and remain intact when the first response completes.
New forks show a separator after the copied response with a link to the immediate source conversation.
Existing forks do not gain this separator.

This feature requires the matching Chat API fork endpoint described in
[Conversation forks](../chat-api/README.md#conversation-forks). History, tool data and file references
are copied; the new agent session receives the history as text, without cloning the original sandbox.

## API Reference

The JSDoc on each type is the source of truth, and the hosted reference is generated from it — Pantry builds it from [`typedoc.ts`](typedoc.ts), the barrel listing every public symbol, using its own pinned TypeDoc. Generated output is never committed here. The `postinstall` hook runs `nuxi prepare` so the Nuxt types `tsconfig.json` depends on exist before that build runs.

To render the reference locally:

```bash
bunx --bun typedoc --entryPoints typedoc.ts --out /tmp/chat-nuxt-api --skipErrorChecking
```

The table below is a quick index for reading in-repo. **When you change a prop, update its JSDoc and this row together.**

The embed automatically sets the browser tab title to the active conversation title and synchronizes its ID to `?conversationId=` using router replacement, preserving other query parameters and the URL fragment. Starting a new conversation removes the conversation parameter. Opening or reloading that URL restores the conversation; existing `?conversation=` links are also accepted and converted to `?conversationId=`. Explicit `conversationId` and `initialConversationId` props take precedence over the URL on mount. Without a conversation title, the host application controls the tab title.

The model catalog loads in the background and never gates send, retry or model
persistence. Its in-memory cache is scoped to workspace, agent, branch,
conversation scope, conversation and harness. Lists remain fresh for five minutes;
refresh failures retain the last valid list for up to 24 hours. Browser requests have a 30-second deadline including proxying and harness resolution;
the backend catalog lookup has a three-second timeout. Requests share one pending
request per key and back off from 30 seconds
to five minutes on failure. Mounted consumers periodically refresh the cache.
Reloading the page starts a new cache. Cold Claude pickers use bundled options;
Lightweight keeps the configured/restored selection or the agent default. Neither
harness changes the selected model when catalog metadata arrives.

### `<MeistrariChatEmbed>` props

Declared in `src/runtime/embed/types.ts`; prop, feature-flag, and event payload types are importable from the package root or `@meistrari/chat-nuxt/types/embed`.

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `hideSidebar` | `boolean` | `false` | Hide the conversation list and render only the active chat surface |
| `sidebar` | `ChatSidebarConfig` | `undefined` | Sidebar layout: `{ position?, width?, bottomHeight?, collapsible?, defaultCollapsed? }`. `position` (`'left'`\|`'right'`, default `'left'`) picks the edge; `width` is the sidebar width in pixels (default `240`); `bottomHeight` is the `sidebar-bottom` pane height as a percentage 0–100 of the conversation area (default `50`); `collapsible` (default `false`) renders a native collapse handle and animates the sidebar open/closed; `defaultCollapsed` (default `false`) starts it collapsed |
| `hideSettings` | `boolean` | `false` | Hide workspace settings actions while keeping the chat header and content unchanged |
| `conversationId` | `string \| null` | `null` | Controlled conversation (supports `v-model:conversation-id`) |
| `initialConversationId` | `string \| null` | `null` | Conversation to open once on mount when `conversationId` is uncontrolled |
| `conversationScope` | `string \| null` | `null` | Isolate conversation history by technical scope within the current workspace and runtime. Scoped embeds only see same-scope conversations; values are trimmed, blank becomes `null`, and the server accepts 1-200 chars from `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, `/`, `-` |
| `defaultConversationCreatorFilter` | `'all' \| 'mine'` | `'all'` | Initial sidebar creator filter: every conversation in scope, or only the current user's |
| `availableModels` | `AvailableChatModels` | Harness catalog | Visual filter and ordering for catalog options. Unknown ids are omitted; if no ids match, the full catalog is shown. Does not change defaults, restored selections or execution. The Agent validates availability |
| `defaultModel` | `ChatModel` | Package default | Initial model for untouched drafts. Explicit selections and existing conversations take precedence; `availableModels` only filters the selector |
| `defaultReasoningEffort` | `ChatReasoningEffort` | Automatic (`null`) | Initial reasoning effort for untouched drafts. Explicit user choices (including automatic) take precedence |
| `conversationCreatorFilter` | `'all' \| 'mine'` | — | Controlled sidebar creator filter; use with `v-model:conversation-creator-filter` when the host changes it at runtime |
| `hideConversationCreatorFilter` | `boolean` | `false` | Hide the creator selector without changing the active filter |
| `telaAgentId` | `string` | — | Use Tela agent mode for this embed |
| `telaAgentBranch` | `string` | Published version | Repository branch to use for new agent sessions |
| `telaAgentInputs` | `TelaAgentExecutionInput[] \| null` | `undefined` | Inputs sent with each Tela agent run; hides the variables panel when non-null |
| `workspaceSettings` | `WorkspaceSettings \| null` | `null` | Override settings from host app; not accepted in Tela agent mode |
| `user` | `ChatActor \| null` | `null` | Override current user display info; not accepted in Tela agent mode |
| `features` | `Partial<ChatFeatureConfig>` | `{}` | Toggle `showUsageTab`, `showDebugOption`, `showCancelButton`, `showTtft`, and `chatOutboundQueue`; all released features default to `true` |
| `loadingMessages` | `readonly string[] \| null` | Default chat messages | Override pending-response messages |
| `loadingMessagesMode` | `'ordered' \| 'random' \| null` | `'ordered'` | Rotate custom messages in order or at random; only applies when `loadingMessages` has a non-empty message |
| `labels` | `Partial<ChatLabels>` | Portuguese defaults | Override conversation-search text; see [UI labels](docs/guides/embedding.md#ui-labels) for supported keys and surfaces |
| `customComponents` | `Record<string, Component>` | `undefined` | Custom markdown renderers keyed by markdown node or tag name. Components receive `node` plus the parser lifecycle props `loading` and `autoClosed` |
| `customHtmlTags` | `readonly string[]` | `undefined` | Extra HTML tags allowed by the markdown renderer |
| `citations` | `ChatCitationsConfig \| null` | `undefined` | Enables `cite://` citations in assistant and user messages (see [Citations](docs/guides/citations.md)) |
| `feedbackConfig` | `MessageFeedbackConfig \| null` | `undefined` | Enables 👍/👎 feedback on assistant messages; providing the prop (even `{}`) shows the controls |
| `messageFeedback` | `Record<string, MessageFeedbackRating> \| null` | `undefined` | Host-persisted votes keyed by message id; when provided, it is the single source of truth for the selected thumbs |

Events and slots are listed in [Embedding in your layout](docs/guides/embedding.md); `<ChatConfigurationModal>` props and events are in [Workspace settings](docs/guides/workspace-settings.md).

## What's Included

The module auto-registers everything — no manual imports needed:

- **Components**: full chat UI, message bubbles, tool widgets (code, search, files), markdown rendering, file previews
- **Composables**: `useChat`, `useConversations`, `useWorkspaceSettings`, `useFileUpload`, `useFeatureFlags`, and more
- **Server routes**: thin proxy routes that forward chat requests to the Chat API service

## Development

Run from this package:

```bash
pnpm build          # nuxt-module-build + embedded schema/declaration post-processing
pnpm dev:prepare    # stub build for local development
pnpm typecheck      # builds the module, prepares chat-app, typechecks the host app
pnpm test:types     # build + tsc against the consumer type tests
pnpm smoke:pack     # pack the module and install it into a scratch consumer
pnpm smoke:browser  # packed-consumer smoke test with a real browser
pnpm lint           # eslint
pnpm lint:attrs     # guards against stray `!` attribute typos in runtime templates
```

Unit tests run from the workspace root (`pnpm test`), which prepares this package before invoking vitest.

Package versions are assigned by release automation from conventional commits; do not edit the version manually.

## Package Dependencies

Installing `@meistrari/chat-nuxt` installs the runtime packages used by the layer. The host Nuxt app still owns the Nuxt and Vue versions.

| Package | Version | Purpose |
|---------|---------|---------|
| `@meistrari/auth-nuxt` | `3.31.0` | Authentication (required) |
| `@meistrari/logger` | `^2.1.3` | Structured logging for the server proxy routes |
| `@meistrari/tela-build` | `^1.70.2` | UI component library (Nuxt layer), with no global i18n requirement |
| `@sentry/nuxt` | `^10.0.0` | Client-side error capture used by chat composables |
| `@iconify/vue` + `@iconify-json/ph` | `^5.0.0` / `^1.2.2` | Icon rendering support (Phosphor set) |
| `@vueuse/core` | `^12.8.0` | Runtime composables used by chat UI |
| `@vueuse/components` | `^12.8.0` | VueUse component peer expected by the shared runtime |
| `mermaid` | `^11.13.0` | Native markstream Mermaid diagram rendering |
| `h3` | `^1.15.0` | Server route utilities provided by Nuxt/Nitro |
| `markstream-vue` | `2.0.7` | Markdown streaming renderer |
| `motion` | `^11.13.0` | Animation component runtime |
| `nitropack` | `^2.10.0` | Runtime config and task utilities provided by Nuxt/Nitro |
| `pdfjs-dist` | `^5.4.530` | PDF file previews |
| `posthog-js` | `^1.364.2` | Optional PostHog feature-flag helper |
| `stream-diffs` | `0.0.2` | Native markstream rich code and diff rendering |
| `virtua` | `^0.42.0` | Virtualized list runtime expected by shared UI |
| `xlsx` | `^0.18.5` | Spreadsheet file previews |
| `zod` | `^4.1.13` | Schema validation for API payloads |

The chat package does not declare or expect the host application to configure an i18n plugin. Hosts can override the supported conversation-search text through `labels`, using plain strings or their own translator. This is not a full-chat localization API; other UI text retains its existing defaults.

Peer dependencies:

| Package | Version | Purpose |
|---------|---------|---------|
| `nuxt` | `^3.17.0 \|\| ^4.0.0` | Host framework |
| `vue` | `^3.5.0` | Host Vue runtime |
