@webskill/chatbot
v0.23.0
Published
An embeddable agent-chat workbench for the browser, built on [`@webskill/sdk`](https://www.npmjs.com/package/@webskill/sdk). It renders a full conversational UI — assistant-ui message-part pipeline, streaming markdown, chain-of-thought grouping, human-in-
Readme
@webskill/chatbot
An embeddable agent-chat workbench for the browser, built on
@webskill/sdk. It renders a
full conversational UI — assistant-ui message-part pipeline, streaming
markdown, chain-of-thought grouping, human-in-the-loop interaction cards
(missing-parameter forms, confirmations, authorization), generative-UI
surfaces, and session management — driven by the SDK's ChatEngine.
@webskill/chatbot is an upper-layer product, not part of
@webskill/sdk. It consumes the SDK as a peer dependency, so your application
carries exactly one SDK instance shared between the chatbot, the console, and
your own code.
0.0.x phase — the public API may change between patch releases. Not recommended for production-critical paths until 0.1.0. See the versioning plan in the repository docs (
0.0.1 → 0.0.2 → 0.1.0, strict semver from 0.1.0).
Install
npm install @webskill/chatbot @webskill/sdk react react-domOptional peers
| Package | Install it to… | Without it |
| --------------------------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------------------------- |
| @a2ui/lit + @a2ui/web_core + @a2ui/markdown-it + @lit/context | enable the A2UI renderer track (BYOC Lit catalog) | A2UI track is greyed out in Settings; surfaces fall back to native |
| @openuidev/react-lang + zod | enable the OpenUI renderer track | OpenUI track is greyed out; surfaces/interactions fall back to native |
Missing peers never break the native track — the chatbot probes availability
and degrades to native rendering with an install hint (UI_UNAVAILABLE).
Vite integration
The optional peers above are loaded through dynamic import() so a host
without them still builds. Vite's dev server pre-bundles dependencies lazily, so
the first dynamic import of an un-optimized package triggers a full
re-optimization and a page reload — mid-conversation that aborts in-flight
module requests. Declare the ones you installed up front:
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
optimizeDeps: {
// Only list the tracks you actually installed; an entry for a package that
// is not installed makes the dev server fail to start.
// Subpaths matter: the A2UI track imports `@a2ui/*/v0_9`, and both tracks
// pull zod (A2UI needs `zod/v3` specifically).
include: [
'@a2ui/web_core/v0_9',
'@a2ui/lit/v0_9',
'@a2ui/markdown-it',
'@lit/context',
'lit',
'zod',
'zod/v3',
'@openuidev/react-lang'
]
}
});This matters only for the dev server; production builds resolve the same imports statically.
Minimal example
The snippet below is compiled as-is by this repository's consumer typecheck
probe (pnpm test:install-smoke), against the published .d.ts.
import { Chatbot, type ChatbotHostAdapter } from '@webskill/chatbot';
import { OpfsProvider } from '@webskill/sdk/browser';
import '@webskill/chatbot/chatbot.css';
// ChatbotHostAdapter: wire storage, skill roots and a navigation port
// (openConsole / openConsoleTrace). <Chatbot> constructs its own ChatEngine
// from the adapter; pass config for options like generativeUi.
// Model configuration lives in the runtime config store (config.runtimeConfig):
// RuntimeConfig.llm.entries holds the model list, llm.defaultId the default one.
const adapter: ChatbotHostAdapter = {
storage: new OpfsProvider({ rootName: 'my-app' }),
skillRoots: ['/skills'],
navigation: {
openConsole() {
/* route to your console surface */
}
}
};
export function App() {
return (
// Layout contract: the host gives the chatbot its height; the chatbot owns
// the single main scroll region (h-full min-h-0 on the theme wrapper).
<div className="h-full min-h-0">
<Chatbot adapter={adapter} config={{ generativeUi: true }} />
</div>
);
}See examples/chatbot-playground for a complete ChatbotHostAdapter
implementation (OPFS storage, skills, demo/model providers, console embedding).
Session list ordering and paging
ChatEngine.listSessions() forwards straight to the SessionStore, so the
default FsSessionStore decides what you see:
- Sessions come back newest first:
items[0]is the most recently created session, and the first screen holds the newest page. - The page size is the store's (
FS_SESSION_PAGE_SIZE, 50).<Chatbot>does not override it — pass your ownsessionStoreif you need another size. nextCursorpoints further back in history, and the page it returns is appended to the end of the list. While it is set, the session list shows a Load earlier sessions entry below the existing rows; when it is absent the list is at the end and the entry disappears, so "no button" always means "nothing older".- Session files that cannot be parsed are skipped rather than failing the whole
list. Their file names come back in
skippedand are shown above the list, so a session you expected to see but cannot is always accounted for.
If you supply your own SessionStore, its list() must return the page
newest-first. This is a behavioural contract that the type system cannot check:
a store that still returns ascending order will render the oldest session at the
top.
Embedding models
- As a component inside an existing web app — mount
<Chatbot>in your own layout, pass aChatEngineconfigured with your storage / skills / LLM. You control routing; the chatbot is one region on the page. - As a full-page foundation (e.g. a
webskill.ai/chat-style app) — pair<Chatbot>with@webskill/consolefor the operations surface, and let the console own navigation across its 22 pages.
Stability
Every public export carries a @stable or @experimental JSDoc tag. The tag is
part of the checked-in API snapshot (api-snapshots/chatbot/index.d.ts.snap),
so a symbol cannot be quietly downgraded — the change shows up in the diff.
@stable— covered by the semver contract from 0.1.0: bug fixes ship in a patch, backwards-compatible capabilities in a minor, breaking changes only in a major with release notes. This covers the surface you wire a host against:Chatbot,ChatEngine,ChatbotHostAdapter, the message/event/session data types,CompositeUiBridge, the layout, theme and i18n exports, and the shell components (InteractionCard,ResultBlocksPro,SettingsDrawer, …).@experimental— may change in a minor or patch release. Only three kinds of symbol qualify, and each one is here for a stated reason:- Non-native renderer tiers — the A2UI, OpenUI and Vercel surface hosts,
their snapshot variants,
VercelPayloadPreview,SpecInteraction,configureA2uiMarkdown, theprobe*helpers,RendererKind,RendererCapabilityandDEFAULT_RENDERER_CAPABILITIES. Their shape tracks upstream ecosystems this package does not own. - Surfaces not yet exercised by consumers —
ChatAttachmentInputandChatAttachmentMeta(multimodal attachments),ChatThinkingandChatTokenUsage(thinking segments and token usage persisted on messages, 0.7.0), andpickUsableLlmEntry(usable-model entry criterion, 0.7.0). RunSnapshot— the resume snapshot format,@experimentalin@webskill/sdkfor the same reason.
- Non-native renderer tiers — the A2UI, OpenUI and Vercel surface hosts,
their snapshot variants,
Anything a README example or the published-.d.ts consumer probe imports is
@stable by rule: once we teach a call site, we stop calling it experimental.
pnpm lint enforces that rule against this file.
License
MIT
