Cloud|

@k2b/cloud

Cloud assistant chat

Cloud AI messages and composer adapters bound to Cloud sessions, turns, tools, and attachments.

Chat.Timeline@k2b/uicreateAiChatTimeline@k2b/cloud/ai/ui

The generic timeline owns chat presentation. Cloud projects its persisted messages and active turn into that contract.

Start a conversation

TSX

Copy
const items = createAiChatTimeline({ messages: () => [], activeTurn: () => null });
<AiChatActionsProvider>
<Chat.Timeline items={items()} emptyTitle="Start a conversation" />
</AiChatActionsProvider>
Chat.Composer@k2b/uiaiChatModelOptions@k2b/cloud/ai/ui

The generic composer owns interaction and accessibility. Cloud only adapts model profiles and outgoing payloads.

Send message

TSX

Copy
<Chat.Composer
value={draft()}
onValueChange={setDraft}
models={aiChatModelOptions(models)}
selectedModelId={selectedModelId()}
onModelChange={setSelectedModelId}
commands={commands}
contextUsage={{
usage: latestUsage,
loopUsage: latestLoopUsage,
contextWindow: 262_000,
modelLabel: "vLLM Qwen 3.6",
}}
onSubmit={(input) => sendMessage(aiComposerSendInput(input))}
/>

The Cloud adapter connects AI sessions, turns, tools, attachments, retries, forks, and usage snapshots to Chat from @k2b/ui.

Use Cloud assistant chat

Use the Cloud adapter only for the platform AI protocol. Standalone Solid applications and other backends use Chat directly.

Import

tsx
import {
  AiChatActionsProvider,
  aiChatAttachments,
  aiChatModelOptions,
  aiComposerSendInput,
  createAiChatTimeline,
} from "@k2b/cloud/ai/ui";
import { Chat } from "@k2b/ui";

Cloud ownership

  • createAiChatTimeline reactively maps persisted messages and the active turn to ChatTimelineItem[]. Each assistant turn shows one work line, its results, its newest text, and its actions; while the turn runs, a newest text that a step follows reads as a quiet status, and only a text that may be the answer keeps answer typography; the live turn and its history share item ids, so finishing a turn moves nothing. Pass reconnecting from the controller's stream status: while the stream reconnects or a model call waits for its retry, the work line says so in place and its clock stands. Pass timeZone, such as the page's getTimeZone(c), so receipt times and dates in server-rendered history match the browser.
  • AiChatActionsProvider binds approval, outcome-check, frontend-tool, retry, fork, message-feedback, and file behavior to rich Cloud blocks. Every Action question and its outcome render as card v4 from Chat.Decision, Chat.Preview, and Chat.Outcome; pass onCheckAction so an unknown outcome can be checked again without repeating the Action, and onContinueTurn so an expired question can be asked again through a visible message.
  • aiChatModelOptions and aiChatAttachments map Cloud records into portable values.
  • aiComposerSendInput maps ChatSubmitInput back to the Cloud controller input.

The application still owns the current controller and mutation callbacks. Cloud persistence, streaming, tools, approvals, and files remain outside @k2b/ui.

The controller keeps a submitted message visible while attachments upload and the server accepts the draft. This local send is separate from server snapshots: a reconnect cannot remove it, and the consumed draft revision identifies its confirmation without rendering a duplicate message.

Accessibility

Chat owns common keyboard, focus, status, and accessible-name behavior. Cloud renderers preserve visible status and accessible names for domain-specific tool blocks.

Runtime

Persisted messages, live turns, attachments, retries, tools, feedback, and mutations require the Cloud AI controller and authenticated platform routes. Message feedback belongs to the private conversation owner and never enters model context.

Example

tsx
const Conversation = () => {
  const items = createAiChatTimeline({
    messages: chat.messages,
    activeTurn: chat.activeTurn,
    reconnecting: () => chat.streamStatus() === "reconnecting",
  });

  return (
    <Chat>
      <Chat.Timeline
        items={items()}
        loading={chat.loadingConversation()}
        hasMore={chat.hasMoreHistory()}
        onLoadOlder={chat.loadOlderMessages}
      />
      <Chat.Composer
        value={draft()}
        onValueChange={setDraft}
        state={chat.runStatus() === "stopping" ? "stopping" : chat.running() ? "running" : "idle"}
        models={aiChatModelOptions(availableModels())}
        selectedModelId={selectedModelId()}
        onModelChange={setSelectedModelId}
        attachments={aiChatAttachments(attachments())}
        onSubmit={(input) =>
          input.intent === "steer"
            ? chat.steer(input.text)
            : chat.send(aiComposerSendInput(input))
        }
        onStop={chat.abort}
        contextUsage={{
          usage: latestUsage(),
          loopUsage: latestLoopUsage(),
          contextWindow: selectedContextWindow(),
          modelLabel: selectedModelLabel(),
        }}
      />
    </Chat>
  );
};

<AiChatActionsProvider actions={messageActions}>
  <Conversation />
</AiChatActionsProvider>;

Connect a coding agent

The Cloud skill provides compact working instructions. MCP supplies exact current documentation when details matter.

CLI command
bunx skills add https://docs.example.com

This command installs the working instructions published by this website in the selected coding agent.

For exact, current details, also connect the MCP server through one of the agent tabs.

Installation uses the open-source Vercel Skills CLI.

The skill and MCP complement each other: the skill describes workflows, while MCP supplies current documentation.