AI content integration
Phase 1 contracts, dependencies, migration mappings, and intentional differences from existing Commenda AI elements.
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 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
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
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
Use Attachment, Attachment group, and 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
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 and 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
| 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, Tool, and 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
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
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
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
Use Output List for artifact rows and 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.