What's new
All notable changes to the chatbot widget.
All notable changes to ai-chatbot-widget.
The format is loosely based on Keep a Changelog.
The repository carries no git tags and package.json has stayed at 0.1.6 since
2026-07-07, so releases below are grouped by the date the work landed on develop
rather than by version number.
2026-09-25 — Agent targeting, input padding fix
Fixed
- The message input was clipped by the window's rounded bottom corners. In the default
messageInputPosition="bottom"layout the input container is the last child of the chat
surface, so its bottom edge is the surface's bottom edge — and that surface is
rounded-4xl(2rem radius) withoverflow-hidden. The container carriedpx-4 pt-2and no
bottom padding, which put the textarea's bottom-left corner (and the send button's
bottom-right) roughly 36px from the corner arc's centre, i.e. outside a 32px radius: the
corners were cut off flat. The container is nowpx-4 pt-2 pb-4, so the bottom inset matches
the sides and the controls clear the curve by ~9px. A longstanding bug, visible on every
default-configured widget.
Added
-
agentIdwidget option. An optional agent (UUID) that answers this widget's questions,
forwarded as theagentIdquery parameter on everyGET /v1/post/q. It is the intended
replacement for the retiredmodelprop: the API picks an agent per query, soagentId
is deliberately not sent when opening a thread or reading history.Legacy setups are untouched by design:
- When no agent is configured the parameter is omitted from the query string entirely
rather than sent empty, which is how the API is told to use the organization's default
agent — byte-for-byte the request the widget made before this option existed. - Blank, whitespace-only and non-string values all count as "not set" (host pages are plain
JavaScript, and templated snippets such asagentId: "{{AGENT_ID}}"can render empty).
mountChatbotWidgetwarns —[ChatbotWidget] "agentId" must be a non-empty string— and
still mounts, because an optional option must not break a page. Values are trimmed. agentIdjoins the thread-cookie scope, so pointing a widget at a different agent starts
a new conversation instead of continuing one another agent answered. The agent segment is
appended to the cookie hash only when an agent is set, so a widget withoutagentId
resolves the exact cookie it already had and shipping this build interrupts nobody. A test
pins the pre-feature hash (verbatim-session-b09i4rfor the fixture config) to keep it
that way.- The dev harness reads an optional
VITE_TEST_AGENT_IDfrom.env.local.
- When no agent is configured the parameter is omitted from the query string entirely
2026-09-09 — Thread API migration, conversation reset, CDN move
Added
- Conversation reset. A
resetbutton next to the "Online" status in the header,
backed by a newresetThread(config)insrc/utils/api.ts. It opens a fresh thread
and overwrites theverbatim-session-*cookie in place — the browser session itself is
never touched. Three deliberate behaviours: the message list is cleared only after the
new thread resolves (a failed reset leaves the visitor on the conversation they can still
see); thegreetingand quick-reply prompts survive, since they are configuration rather
than history; and a double-click joins the inflight create instead of stranding a second
thread. CLAUDE.md— repo guidance covering the two entry points, the intentional double
import ofwidget.css, the splitvitest.config.ts, the module-level thread caches, and
the four coordinated edits a new theme token requires.README.md→ "How to develop, run and debug" — requirements, three-command start,
annotatedsrc/tree, config-file table, npm scripts, built-bundle smoke testing, and the
lint noise to expect.
Changed
/v1/session/→/v1/thread/. Conversations now open withPOST /v1/thread/, and the
session → thread rename is carried through the types (ThreadCreateResponse). The cookie
keeps itsverbatim-session-prefix on purpose: it is an opaque hash key, and renaming it
would drop every visitor's thread the moment a new build hits the CDN.- Deprecated spec fields dropped.
sessionIdquery param →threadIdon/v1/post/and
/v1/post/q;modelis no longer sent in the thread-create body (the prop is kept and still
feeds the cookie hash, so existing embed snippets and cookies keep working);sessionId
removed from the response types. getPreviewUrlsbatches.pagesis required and capped at 10 values per request, so
longer page lists are split into parallel calls and concatenated. An empty list
short-circuits to[]instead of issuing a request that cannot succeed.PreviewSizeisSMALL | LARGE(wasSMALL | MEDIUM). The wrong value happened to work
because unknown sizes fell through to the default, but it was handed to integrators as API.- CI deploys to Scaleway S3.
cloudbuild.yamlreplacedgcloud storage cp dist/* gs://bmu-verbatim-cdn/widget/chatbot/v1/withaws s3 sync ./dist/ --deleteto
s3://cdn.verbatim-ai.com/widget/chatbot/v1/(endpoints3.fr-par.scw.cloud, region
fr-par, credentials via_AWS_ACCESS_KEY_ID/_AWS_SECRET_ACCESS_KEYsubstitutions).
ExplicitwaitForchains were removed; steps run in declaration order. Tests still gate
build and deploy.
Fixed
- History displayed newest-first.
GET /v1/post/defaults toorder=DESC, so the restored
conversation was reversed.getDialognow sendsorder: "ASC"explicitly. - Source thumbnails were labelled one page too high. The
+ 1is gone from all three places
it appeared — the visible caption, the button title, and the imagealt(the last two mattered
for screen readers and hover text). The API echoes the one-based numbering it was sent; the
OpenAPI schema documents this reply as zero-based and is wrong, verified against the live API.
The test fixture encoded the same off-by-one, which is why the bug survived review; it now pins
the real request/reply shape. - The header
resetbutton was invisible..widget-header__reset { color: … }has
specificity(0,1,0)and lost to the existing
.ai-chatbot button, input, textarea { color: inherit }reset at(0,1,1), painting the button near-black on a near-black header.
The rule is now scoped to.ai-chatbot .widget-header__reset. Icon-only buttons dodge this
because a descendantsvgrule colours them — the trap is documented inCLAUDE.md. - Broken-image icons on preview tiles. Previews render asynchronously and URLs are issued
without an existence check, so a tile can 404. A failing thumbnail now removes itself on
onError.
Tests
Suite grew from 129 to 148 tests, including guards that assert model is never sent and that
threadId (not sessionId) is used.
2026-08-01 → 2026-08-04 — Required props and staging
Added
- Required props are validated at the mount boundary.
mountChatbotWidgetenforces a
non-emptyaccessTokenand at least one non-emptycorpusIdsentry, withconsole.error+
throw.ChatbotWidgetitself no longer re-checks. Covered bysrc/index.test.tsx. - Staging section in
docs/widget-installation-guide.md, documenting
https://staging-api.verbatim-ai.comas thebaseUrlfor non-production embeds.
2026-07-31 — Source attachments, test suite, CI
Added
- Source attachments. Bot answers backed by documents show an "N sources" button that lazily
loads each document's summary, filename and size, plus per-page thumbnails with a click-to-zoom
lightbox and an open/collapse control (src/components/chatbot-sources.tsx, new attachment
endpoints insrc/utils/api.ts, ~130 lines ofwidget.css). docs/widget-installation-guide.md— the public embedding reference.- Vitest + Testing Library suite across components, utils and both entry points, with
TESTING.mdrecording the coverage snapshot and known gaps. - Cloud Build pipelines.
cloudbuild.yaml(main: install → test → build → deploy) and
cloudbuild-dev.yaml(branches: install → test → build, no deploy). Lint is not run in CI.
Fixed
- Thumbnail and message sizing in full-page / fullscreen mode.
Updated about 1 hour ago