# AI content integration

Package: @commenda/pathfinder@0.1.0
Page: /docs/guides/ai-content

## Package contract [#package-contract]

All components live in `@commenda/pathfinder`. Import `styles.css` once and optionally `code-font.css` for Geist Mono. Individual entry points are `markdown`, `code-block`, and `inline-reference`. No app-side CSS compilation, AI SDK provider, router, or upload client is required.

Streamdown 2.5.0 supplies streaming Markdown parsing. Lowlight 3.3.0 supplies Highlight.js token trees rendered as React spans; new AI runtime components do not use Shiki. Fumadocs examples and MDX fences also use Lowlight, rendered on the server; its default Shiki highlighting is disabled. Mermaid and math rendering are not enabled. Streamdown itself lists Mermaid transitively; Pathfinder does not enable its renderer.

## Migration readiness [#migration-readiness]

Migration is out of scope for this build. Keep legacy components in place until their consumers are explicitly migrated.

| Existing API / call site                                 | Pathfinder equivalent                                                          | Difference / future work                                                                   |
| -------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| `MessageResponse` in `CommendaAiMessageItem`             | `Markdown children` and `isAnimating`                                          | Reuse the same `segment.part.text`; no message conversion                                  |
| Streamdown `components`                                  | `Markdown.components`                                                          | Typed semantic overrides; no leaking upstream AST types into the public contract           |
| Compound `CodeBlock`, header, filename and copy children | `CodeBlock code/language/filename/copyConfig`                                  | Header layout is internal; old children must be mapped explicitly                          |
| `showLineNumbers`                                        | Same compatibility prop                                                        | Defaults on to match Figma; `lineNumberConfig.visible` takes precedence                    |
| `MarkdownLink` with host routing and record lookup       | `referenceConfig.resolve`, `InlineReference.onClick`, or a typed link override | Host provider is not imported; app retains business routing and link confirmation          |
| Question fence renderer                                  | Plain code fallback in Phase 1                                                 | Interactive requests belong to Phase 5; do not migrate such consumers without mapping them |
| Math/Mermaid plugins and word animation                  | Not enabled                                                                    | Separate opt-in work, not silent migration parity                                          |
| Table copy/export/expand                                 | Built-in Markdown table controls                                               | Static semantic table; interactive sorting/editing remain Table features                   |

Preserve stable message/part IDs in the parent, part ordering, and application-owned state. Do not flatten tool results or attachments into strings. New reference configuration must update even when the text is unchanged.

## Upstream provenance [#upstream-provenance]

Behavioral reference: Vercel AI Elements `MessageResponse` and Code Block, revision `6a9d5b1822ffb10bba4bd97175f01edd7d8651cd`, Apache-2.0. Pathfinder replaces the visual controls, uses compact Figma typography, removes default plugins and the text-only memo comparator, and uses Lowlight instead of Shiki. See the package's `THIRD_PARTY_NOTICES.md`.

## Rendering rules [#rendering-rules]

Treat custom renderers as trusted application code. Keep untrusted raw HTML disabled, render code as text, and validate navigation schemes. References receive ready-to-render data or a loading state; they do not fetch records. Clipboard failures stay recoverable. Do not announce each streamed token.

Markdown is intentionally available through `@commenda/pathfinder/markdown` only (its types are also exported from the root). Streamdown has module side effects; keeping this runtime off the root barrel prevents unrelated icon and button imports from loading the Markdown parser.

## Attachments and context [#attachments-and-context]

Use [Attachment](/docs/components/attachment), [Attachment group](/docs/components/attachment-group), and [Composer context](/docs/components/composer-context) as independent leaves. Each accepts caller-owned data and callbacks; none requires an AI provider or transport.

Existing attachment `key`/`fileName`/`mimeType` values map to `id`/`filename`/`mediaType`. Keep opaque `fileRef` lookup, folder aggregation, cancellation, and viewer selection in the app. Context `onDismiss` maps to removal by stable ID. These are documented mappings and type fixtures, not consumer migrations.

## Composer and conversation starters [#composer-and-conversation-starters]

Use `/composer`, `/suggested-prompt`, and `/suggested-prompt-group` for Phase 3. These exports have no AI SDK runtime dependency. The Composer's `requestConfig.status` accepts AI SDK's `ready`, `submitted`, `streaming`, and `error` values directly.

| Existing surface                                       | Pathfinder mapping                                                                |
| ------------------------------------------------------ | --------------------------------------------------------------------------------- |
| ChatComposer `initialInput` / provider text state      | Initialize React state and pass `value` / `onValueChange`                         |
| `onSubmit(text, attachments)`                          | Adapt `onSubmit({ text, attachments, command })` in the host                      |
| `status`, `onStop`                                     | `requestConfig={{ status, onStop }}`                                              |
| Existing attachment objects                            | Map to stable AttachmentItem IDs; keep backend metadata in the host               |
| `toolsSlot`                                            | `toolbar.leading`                                                                 |
| `contextSlot`                                          | `contextConfig` or existing ComposerContext composition                           |
| Existing upload service, file ingestion, folder upload | Retain in the host; wire `attachmentConfig.items`, `onAdd`, `onRemove`, `onRetry` |
| Suggestion click handler                               | SuggestedPrompt `onSelect` or SuggestedPromptGroup `onSelect`                     |

The selected skill is separate typed metadata, so plain-text drafts and existing backend payloads do not require rich-text migration. Convert that metadata only where the application builds its request. Do not serialize internal component state or import private implementation files.

Vercel AI Elements' [Prompt Input](https://elements.ai-sdk.dev/components/prompt-input) and [Suggestion](https://elements.ai-sdk.dev/components/suggestion) informed the textarea, toolbar, keyboard, and request-state responsibilities. Pathfinder implements those behaviors using its existing primitives and its controlled-data contract. It does not vendor a second Radix/shadcn component stack or introduce Shiki. Actual application migration remains out of scope.

## Activity and execution [#activity-and-execution]

| Existing AI Elements concept                    | Pathfinder                                                | Host responsibility                                  |
| ----------------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------- |
| Reasoning / ReasoningTrigger / ReasoningContent | `Reasoning` with optional `disclosureConfig` and children | Supply approved summary text and elapsed duration    |
| Tool / ToolInput / ToolOutput                   | `ToolCall` with `input` and `output` source strings       | Map tool status and redact secrets before rendering  |
| ChainOfThought step composition                 | `ChainOfThought` with discriminated `steps`               | Keep stable IDs and preserve source ordering         |
| Thinking / Shimmer                              | `ThinkingIndicator`                                       | Hide label after first content; remove at completion |

AI SDK mapping: `input-streaming` → pending, `input-available` → running, `output-available` → done, `output-error` → failed. Approval states require application UI; do not present approval as completed execution. Keep this adapter in the host so SDK upgrades do not change the design system contract. No consumer migrations are included.

Reasoning's `isStreaming` describes the reasoning part, not the entire chat request. Set it false when answer content starts so automatic collapse happens a second later. A manual toggle wins until remount; use a stable key for one response, a new key for a new response. A controlled `disclosureConfig.open` delegates all automation to the host. Compose `Markdown` explicitly for formatted summaries; it remains a subpath-only runtime dependency.

Architecture references: [Vercel Reasoning](https://elements.ai-sdk.dev/components/reasoning), [Tool](https://elements.ai-sdk.dev/components/tool), and [Chain of Thought](https://elements.ai-sdk.dev/components/chain-of-thought). Pathfinder adapts these concepts to Figma and uses Base UI Collapsible, the same panel measurement foundation used by Accordion. It does not import legacy UI wrappers or their Shiki/Streamdown code plugins.

## Requests, approvals, and optional actions [#requests-approvals-and-optional-actions]

QuestionCard, ApprovalCard, PlanReviewCard and UploadRequestCard share a request result contract. Keep request IDs and tool-call IDs stable and key each card by request ID/version. A handler returns accepted plus the server receipt, or failed plus a user-facing message. Retain drafts on failure and persist accepted ResponseRecap data in the originating message. Approval/plan receipts include the actor and timestamp. Explicit skip, deny, approve and submit are different domain events; temporary dismissal never resolves a request.

Route a typed Composer response to the active question as a written answer. For an active approval, the Figma contract treats a typed response as denial. Implement that deliberate mapping in the host orchestrator, together with authorization and idempotency. Do not attach approval to global keyboard shortcuts or merely closing a card. Plan review opens app-owned plan content. Upload request passes files to app-owned validation/upload and sends only ready attachment IDs.

ActionCard is optional: it does not pause or resume a turn. Return completed only after the action succeeds. Persist completion/dismissal independently; no toast or follow-up message is generated. Use state to restore completed or dismissed actions from history.

For migration, map existing Ask single/multiple data to QuestionCard; approve to ApprovalCard; plan to PlanReviewCard; upload to UploadRequestCard. Map accepted records to ResponseRecap and optional Action records to ActionCard. Keep existing orchestration callbacks behind these typed boundaries. No application migration, AI SDK state conversion, plan editor or upload transport is included in this phase.

### Existing question flow compatibility [#existing-question-flow-compatibility]

Reviewed `QuestionDockedCard` in `CommendaAiConversation`, the historical `QuestionBlockRenderer`, `QuestionSelectCard`, `QuestionPlanCard`, `QuestionAnsweredRecap`, and the `question-block.ts` backend contract. These remain unchanged.

| Existing contract                                                 | Pathfinder mapping                                                 | Preserved behavior / adapter boundary                                                                                                   |
| ----------------------------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| `toolCallId` and indexed `questions[]`                            | React key `${toolCallId}:${questionIndex}` plus `paginationConfig` | Host keeps the tool-call ID, batches answers and persists drafts; no automatic 250ms page advance in the primitive                      |
| `single_select` / `multi_select`, positional options with `title` | `mode`, `options: { id, label, description }[]`                    | Use stable position-derived IDs for this immutable request, not labels; duplicate labels remain distinct                                |
| `selectedByIndex`, `writeInByIndex`                               | `defaultAnswer`, `onAnswerChange`                                  | Store per-question drafts in the adapter before changing pages                                                                          |
| `ANSWER_MAX_LENGTH = 300`                                         | `writeInConfig={{ maxLength: 300 }}`                               | Retain the current backend limit; validate combined multi-choice text server-side                                                       |
| `onAnswer({toolCallId, answers, text})`                           | `onSubmit` / `onSkip`                                              | Adapter maps selected IDs back to `questionIndex`/`chosen`, carries the original tool ID and answer marker; accepted result is explicit |
| Plan body and correction textarea                                 | `planConfig.onOpen`, `onSuggestChanges`                            | Intentional new Figma design: plan/editor lives in the host, no Skip or embedded correction editor                                      |
| Completed `QuestionRecord`                                        | `ResponseRecap` / `state: resolved`                                | Restore accepted answers from persisted records; do not infer receipt state from message text                                           |
| `onClose`                                                         | `onDismiss`                                                        | Temporary hide only; do not submit an answer or cancel a tool                                                                           |

The existing `data-question-answer` marker and backend request schema remain application-owned. The visual component never rewrites history or infers a tool-call ID. ApprovalCard, UploadRequestCard and optional ActionCard are new surfaces with no required legacy transport. This phase implements the Figma request/action designs directly with Pathfinder controls; it vendors no additional Vercel AI Elements source or dependencies.

## Messages and conversation composition [#messages-and-conversation-composition]

Use `@commenda/pathfinder/message` and `@commenda/pathfinder/conversation`. Keep existing SDK objects and pass typed render callbacks; no serialization or role conversion is required. Message's `from="person"` is presentation only. Markdown remains a separate import, and unknown tool/custom parts must be handled by the application renderer.

| Existing integration                                 | Pathfinder boundary                                      | Host responsibility                                                        |
| ---------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------- |
| `CommendaAiMessageItem` and compound Message content | `Message<Part>` with `parts`, `getPartKey`, `renderPart` | Preserve original IDs, order, custom metadata and tool states              |
| `MessageActions` / feedback modal                    | `actionConfig`                                           | Controlled feedback, app modal, persistence and retry eligibility          |
| Compound Conversation / scroll button                | `Conversation<Item>`                                     | Stable message keys, bounded height and app-owned history loading          |
| Docked questions                                     | `footer` containing QuestionCard and Composer            | Put accepted recap in the originating message, persist it, remove the dock |

`MESSAGE-04`: errors retain partial replies. `CONVERSATION-02`: follow only near bottom and preserve reading position across history prepend. `CONVERSATION-03`: supply turn-level announcements, never per-token live text. These are optional boundaries for a future migration; the existing application is unchanged.

## Outputs and run history [#outputs-and-run-history]

Use [Output List](/docs/components/output-list) for artifact rows and [Run List](/docs/components/run-list) for execution history. The responsive panel and complete simulated workflow live in the Output List examples. Import each component from its own package subpath; no global AI provider is required.

| Existing integration                                | Pathfinder boundary                                         | Preserved behavior / intentional difference                                          |
| --------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| `AgentRunsRail` active conversation and run queries | `RunList.items`, `selectedId`, `onOpen`, `paginationConfig` | Host maps IDs, formatted dates, paging, and router navigation                        |
| `useMarkRunRead` and schedule actions               | `onMarkRead`, `actionConfig`                                | Host owns mutations; no optimistic unread change in the primitive                    |
| Canceled treated as failure                         | `status="canceled"`                                         | Deliberate Figma change: canceled and skipped use neutral indicators                 |
| `AttachmentPreviewPanel` / document viewer          | `OutputList.onOpen`                                         | Host retains signed URLs, preview tabs, downloads, and viewer lifecycle              |
| Per-person rail preference                          | `disclosureConfig` and host panel state                     | Host persists disclosure and responsive visibility; no storage access in the package |

`OUTPUT-03` keeps independent row/menu actions, and `RUN-02` keeps canceled runs neutral. Supply newest-first records with stable IDs. The compact lists do not sort backend data or virtualize unlimited history. These boundaries make incremental adoption possible; actual migration and full artifact editing are out of scope.
