Egenverk Babbla

Changelog for Egenverk Babbla

What changed in each plugin, newest first.

The changelog text is in English, as in the plugin.

Atom feed

Version 0.46.1

Fixed

  • With the chat closed, the unread badge could miss a message for up to two minutes when two arrived within a few seconds of each other. Tabs of one browser share the unread count for eight seconds (0.18.0), and a tab that noticed the second message could take the count another tab had fetched just before it. A shared count is now used only when it is at least as new as the change the tab noticed; otherwise the tab asks the server.

Version 0.46.0

Added

  • Settings → Babbla → Diagnostics can turn on lean chat reads. Every time a chat checks for new messages — the conversation list, a conversation's messages, the unread count and the combined check the open chat makes — WordPress normally starts with every plugin and the theme, WooCommerce included, although none of them is needed to answer. With lean reads on, those requests load only Babbla and no theme, so each one holds a PHP worker for a fraction of the time. Sending messages, the assistant and everything else on the site are unchanged. It installs a small must-use plugin, wp-content/mu-plugins/egenverk-babbla-lean.php, and is off until an admin turns it on: a plugin that grants chat access through its own capability rule is not loaded for those requests either, and its users would then be refused — turn lean reads off again in that case. Deactivating Babbla removes the file (on a multisite network only a network-wide deactivation does, since the file serves every site). wp egvb lean on|off|status does the same from WP-CLI. When a Babbla update changes the file, Diagnostics offers to update it.

Version 0.45.0

Added

  • Diagnostics now has a Server load card showing Babbla REST requests by route, request rate and response time. It needs a persistent object cache such as Redis; without one it counts nothing. Measurements remain for at most two hours.

Version 0.44.0

Changed

  • A new message or an assistant answer now wakes only the chats of the people who can see it, instead of every open wp-admin tab on the site. Each user has their own change signal file: an assistant conversation wakes only its owner, a direct message its two participants, a private room its members. A public room still wakes everyone. Before, every write — including each step of an assistant answer ("Searching the chat…", "Calculating…") in someone's private assistant chat — made every open tab of every user ask WordPress, so one question could cost more than a hundred WordPress starts across the site. The file still says only that something changed; its name is derived from the user id, a random key and the site's secret salt, so no one can work out a colleague's file.
  • Settings → Babbla → Diagnostics tests the admin's own signal file, so opening the tab no longer wakes every other open chat.
  • A wp-admin tab left open across the update reads the old shared file, which is deleted once the first user file is created: it gets three 404s and asks the server on a schedule until it is reloaded. Uninstall (with purge on) removes every user file.

Version 0.41.0

Added

  • Settings → Babbla → Diagnostics has a "Live updates" row. When the tab opens it rewrites the change signal file and fetches it back over HTTP the way a browser does: it says the signal works, that the file is cached or unreachable (with the HTTP status, or "stale content" when an old token comes back), that the test could not run from the server (a blocked loopback, which says nothing about the browser), or that uploads is not writable. The result is kept for five minutes, so reloading the tab does not rewrite the file each time.

Changed

  • The change signal file moved from the uploads root into its own directory, wp-content/uploads/egenverk-babbla-signal/, which carries an .htaccess that sends Cache-Control: no-store, private (Apache with mod_headers) and an empty index.php. The directory and both files are created on activation, on the first write, and again whenever one is missing. The old file in the uploads root is deleted once the new one exists; uninstall (with purge on) removes the directory as well. A wp-admin tab left open across the update still reads the old address: it gets three 404s and falls back to asking the server on a schedule until it is reloaded.
  • A cache that freezes the signal file no longer delays new messages by up to a minute for the rest of the page. When the server has reported a token for more than 30 seconds that the file still has not shown, the page stops reading the file, gives up its place as the browser's reader (0.40.0), falls back to the schedule it uses without a signal (0.38.0/0.39.0) and writes one warning to the browser console naming a cache in front of uploads as the likely cause. A file that lags less than 30 seconds, or a burst of writes, does not trigger it.

Version 0.40.0

Changed

  • With several wp-admin tabs open, only one of them reads the change signal file: the visible tab that holds a browser lock (Web Locks) reads it at the pace the fastest visible tab needs and passes every token to the other tabs (BroadcastChannel). Each tab still asks WordPress itself when the token changed, so a new message costs one request per tab as before, and every tab keeps its own safety net and the tab-return rule. A hidden tab never leads; a tab that hears no token for three of its read steps plus two seconds (a frozen or throttled leader) reads the file again itself. Measured in the browser tests: one hour of three visible tabs (one open idle conversation, two closed chats) is 1 826 file reads instead of 3 207, and the same 120 requests to WordPress (60 + 30 + 30). A browser without Web Locks or BroadcastChannel behaves exactly as in 0.39.0.

Version 0.39.0

Changed

  • An open chat asks the server about once a minute instead of every 4–16 seconds while nothing happens; new messages still appear within one to two seconds. The open conversation and the conversation list read the small change file every second for a minute after something happens (a message arrives or is sent, the user types, a conversation is opened) and every two seconds otherwise, and ask WordPress only when the file changed, when the tab comes back after more than 30 seconds, or on a one-minute safety net. Measured in the browser tests: one hour of an open conversation with nothing new is 60 requests to WordPress and 1 767 file reads, against 226 requests in 0.38.0. The list is refreshed with every change and on the safety net instead of on every fourth request.
  • The assistant's progress no longer costs a request every 1.5 seconds: while it works the chat reads the file every second, and the assistant's status and answer arrive with the change that carries them.
  • The open chat follows the server's poll_interval and signal_token from GET /poll too (see 0.38.0); a busy site's egvb_poll_interval now also stretches the open chat's safety net. Without the file (uploads not writable, or three failed reads) the open chat keeps the 0.38.0 schedule unchanged.

Version 0.38.0

Added

  • GET /unread and GET /poll return poll_interval (seconds) and signal_token (the change signal's current token, read before the answer is built). A new filter, egvb_poll_interval (default 0, clamped to 0–600 seconds), lets a busy site make every closed chat wait longer between its scheduled requests without a release.

Changed

  • An idle wp-admin tab with the chat closed asks the server about once every two minutes instead of every ten seconds; the unread badge still updates within about five seconds of a new message. The closed chat now checks the same small file as the open chat, every five seconds, and asks WordPress for the unread count only when that file changed (at most once per five seconds), when the tab comes back after more than 30 seconds, or on a two-minute safety net. Measured in the browser tests: one hour of a closed chat with nothing new is 30 requests to WordPress and 720 file reads, against 360 requests before.
  • A hidden tab asks every five minutes instead of every 30 seconds, and does not read the file. Without the file (uploads not writable, or three failed reads) the closed chat keeps the 10- and 30-second schedule of 0.37.1. An open conversation and the conversation list are unchanged.

Version 0.37.1

Changed

  • Database schema 8: updating converts that one column in place (ALTER TABLE … MODIFY request_id). Existing messages and ids are kept; if the conversion fails, the plugin retries it on the next request.

Fixed

  • A message containing å, ä, ö or any other non-ASCII character was never saved: the chat showed "Send status unknown" and a retry failed the same way. The same fault could stop the assistant's answers and chat searches that contained such characters. Since 0.17.0 the column holding each message's request id used the ascii character set; WordPress then treats the whole messages table as ascii and refuses any query on it that carries other characters. The column now uses the table's own character set, still compared case-sensitively.

Version 0.37.0

Added

  • A new route, GET /babbla/v1/poll, answers in one request what an open conversation used to ask for in two: its new messages and, when due, the conversation list (and, on request, the unread count). Each part is exactly what its own route returns. The existing routes are unchanged.

Changed

  • A new message in an open conversation now costs the server one request instead of two: the chat fetches the message and the refreshed conversation list together, so WordPress starts once per update instead of twice. A page loaded before the update keeps working: when the server does not know the new route, the chat goes back to the separate requests.

Version 0.36.0

Added

  • Messages from colleagues appear within about two seconds instead of up to sixteen. The open chat checks a small file in the uploads folder every two seconds (three on the conversation list) and only asks the server for messages when that file says something changed. The file holds a timestamp and nothing else: it reveals that something changed in the chat, never what, where or by whom. It is removed on uninstall when "Purge data on uninstall" is on.
  • The assistant's answer, and its "Searching the chat…"-style status, show sooner: while it works the conversation is checked every one and a half seconds, for up to two minutes; after that the small file wakes the chat when the answer is ready.

Changed

  • Less load on the server: an open chat with nothing new asks WordPress for messages every 16 seconds in a conversation and every 20 seconds on the list, instead of every 4 to 16 seconds. A new message elsewhere also refreshes the conversation list right away, so its unread count shows without waiting.
  • When the uploads folder is not writable, or reading the file fails three times in a row (an error page or something that is not the file; a dropped network connection only skips that check), the chat falls back to exactly the checking schedule of 0.35.2. A CDN that caches the file only slows the chat to the 16- and 20-second checks. A hidden tab or a closed chat is unchanged.

Version 0.35.2

Fixed

  • The thin loading stripe at the top of the chat no longer blinks every few seconds on its own. Checking for new messages, unread counts, read markers and the colleague list now run quietly in the background; the stripe shows only while something you did (send, create, search, open) is waiting for the server.

Version 0.35.1

Fixed

  • On the full-page chat, the mention picker and the other panels (new message, new room, room info) cover only the conversation, not the whole admin screen.
  • The full-page chat's conversation list is wider (300 px), so room and person names are no longer cut to a few letters.

Version 0.35.0

Changed

  • Access, Usage and Diagnostics follow the Egenverk plugin shell like the Settings tab: each section is a card with its heading, tables use the Egenverk table style, and confirmations ("Access saved.", "Babbla ownership claimed.", "The log files were deleted.") are green notices.
  • Access: claiming ownership, finding a user and choosing a user's Babbla role are each a card of rows, label and help on the left and the field and its button on the right.
  • Usage: the period's figures are a row of tiles (questions, failed, tokens in, tokens out, and the estimated cost when prices are set) instead of one sentence. The daily bars use the Babbla colour, and a day over the budget is red.
  • Diagnostics: each status row names its state in a chip ("OK", "Warning", "Error") instead of a coloured dot alone. The log file picker has a visible label, and the log scrolls inside its own box. "Delete all log files" is a red button that shows a loader while it works.
  • On a phone the rows stack and the tables scroll sideways inside their card.
  • Form fields, what each form sends and who may use it are unchanged.

Version 0.34.0

Changed

  • Settings → Babbla follows the Egenverk plugin shell. The version sits beside the Babbla name in the header, and each tab's content fades in on load.
  • The Settings tab is one page of cards: Overview, Provider, Behaviour, Limits and cost, and Data. The left section menu is gone. Each setting is a row with its label and help on the left and the field on the right. On a phone the rows stack.
  • The floating bubble, "Purge data on uninstall" and each integration are now on/off switches.
  • Provider is one choice at the top of the Provider card. Only the chosen provider's key and model fields show. The "In use" badge and the second "Use … for the assistant" radio are gone. "Save everything and test the connection" is a secondary button with its description on its own line, and it tests the chosen provider.
  • Saving works the Egenverk way. A save bar slides up from the bottom only when something has changed, and a "Settings saved." toast confirms the save. The always-visible save bar at the top and the second Save button at the bottom are gone. Pressing Enter in a field saves, and it does not run the connection test.
  • Option names, stored values and what is saved are unchanged.

Version 0.33.4

Fixed

  • The assistant can be mentioned from the picker again. In a room (public or private) it is the first row of "Mention a colleague or assistant", with its avatar and "Answers once in this room", and it filters with the search like everyone else; the near-invisible "@Agent" button above the list is gone. Direct messages still do not offer it, since the assistant never answers there.
  • The picker's search field is visible in the dark theme: it has a fill, a border and a search icon, and a clear (×) button empties it.
  • Every overlay (mention picker, new DM, new room, room info) has a close (×) button in its header that returns to where you were, like Back and Esc.
  • Selected mentions above the composer are pills with the name and a separate × to remove them.

Version 0.33.3

Fixed

  • Picking a person under New direct message no longer turns the row into "PAPatrik" with a spinner. The busy state flattened the row to text, so the avatar's initials ran into the name and the avatar never came back after a failed request. The spinner now takes the avatar's place and the avatar returns when the request fails.

Version 0.33.2

Fixed

  • Plugin Check reports no code errors. The access compare-and-swap passes $wpdb->prepare() straight to $wpdb->query(); the analytics queries that build their SQL from fixed fragments and placeholder lists say so where they run; calculator error messages escape the function names they quote; uninstall deletes log files with wp_delete_file(); the "Selected user" string has a translators comment.
  • Tested up to is 6.9, the version the acceptance runs used.

Version 0.33.1

Changed

  • The Egenverk mark is centred on its grid (kit sync from _commercial/brand/egenverk-ui/): the symbol and the Babbla glyph in assets/egenverk-ui/, the "by egenverk" signature on Settings → Babbla, and the wordpress.org banner and icons, now rendered from sources kept in .wordpress-org/src/.

Version 0.33.0

Changed

  • One menu entry: Settings → Babbla. The top-level Babbla menu is gone. Settings, Access, Usage and Diagnostics are tabs of one page (options-general.php?page=egenverk-babbla&tab=…), each shown only to users who may open it; a chat user without Babbla administration sees Access only. The Plugins screen gets a Settings link to it.
  • The full-page chat has no menu entry. The admin-bar chat opens it, and the drawer's ⋯ menu has "Open as full page". Its url is admin.php?page=egenverk-babbla-chat.
  • Settings → Babbla is built with the Egenverk UI kit 1.1.0 (assets/egenverk-ui/, copied unchanged, loaded only on that page): header with the Babbla glyph and the egenverk signature, lead, tabs and footer. Fields and behaviour are unchanged; each tab's own heading replaces its old page title, and the settings sidebar no longer repeats links to Access and Usage.
  • wordpress.org banner (1544×500, 772×250) and icon (128, 256) in .wordpress-org/, kept out of the release zip.
  • The settings help texts point at the Usage and Diagnostics tabs instead of the old "Babbla → …" menu path, and only the end of the page keeps room for the floating chat bubble (every form used to reserve it, leaving a gap under the usage period selector).
  • The admin urls of 0.31.0 (admin.php?page=bbl-*) and 0.32.0 (admin.php?page=egvb-*) redirect to their tab or to the full-page chat for one version, keeping their other query args. This replaces 0.32.0's bbl-* → egvb-* redirect.

Version 0.32.0

Changed

  • The plugin is now Egenverk Babbla (slug, folder and text domain egenverk-babbla, main file egenverk-babbla.php, Author Egenverk). WordPress sees the new folder as a new plugin: activating it deactivates the old babbla/babbla.php copy, so the two never answer the same question twice.
  • Every PHP, CSS and JavaScript identifier moves from BBL_/bbl_/bbl- to EGVB_/egvb_/egvb-: classes, constants, options, capabilities (egvb_chat, egvb_manage), transients, admin-post actions, the log cron, admin page slugs, CSS classes and the egvbConfig client global. The WP-CLI command is wp egvb job. wordpress.org requires a prefix of at least four characters for global symbols.
  • Filters are renamed to egvb_* (egvb_agent_tools, egvb_integrations, egvb_capability, egvb_admin_capability, egvb_tool_capability, egvb_tool_status_label, egvb_redacted_keys, egvb_rate_limit, egvb_anthropic_request, egvb_openai_request).
  • The API key constants are EGVB_ANTHROPIC_KEY and EGVB_OPENAI_KEY.
  • A readme.txt for wordpress.org, with the external services the assistant uses.
  • The bbl_* filter names still run for this version, through apply_filters_deprecated(). So do BBL_ANTHROPIC_KEY/BBL_OPENAI_KEY in wp-config.php, the wp bbl job command, and agent jobs Action Scheduler queued under bbl_run_job before the update.
  • EGVB_Install::migrate_from_bbl() runs on activation and on the first request after the update, after the 0.8.0 wctc_ move: bbl_settings, bbl_access, bbl_db_version, bbl_log_dir and bbl_purge_on_uninstall are copied to their egvb_ names (an existing egvb_ value wins) and deleted; a stored agent_capability of bbl_chat/bbl_manage and any role granted those capabilities move to the new names; the bbl_log_cleanup event is cleared. It runs once and a second run changes nothing. Deleting the old keys also disarms the old copy's uninstall.php, which would otherwise read its purge flag and drop the tables this version uses. Transients are left to expire. uninstall.php cleans the old and the new keys.

Version 0.31.0

Changed

  • The SQLite double used by the tests can be told to fail a chosen statement (fail_on), the way a timed-out query fails in production: no rows plus last_error, never an exception. Every fix above is pinned by a test that fails without it.

Fixed

  • A chat search that the statement guard stops no longer reads as "nothing found". get_results() answers a failed query with false, the array cast turned that into zero rows, and the agent reported — with every appearance of certainty — that nothing in the chat mentioned what was asked about. BBL_Repository::search_messages() returns null when the query itself failed, and search_chat refuses with an error that tells the model to say so instead of answering from it.
  • The channel pre-scan that orders a search now runs inside the same statement guard, and a failure there is an error rather than an empty channel list — which reached the model as "nothing matched" by another route. list_conversations refuses in the same way instead of printing every conversation as empty and never touched.
  • POST /channels/{id}/read answers 500 when the marker was not stored. mark_read() ignored both write results and read back a missing row as 0, so the client cleared its badge on a marker the server had never saved and the unread count returned on the next poll.
  • finish_job() reports whether a row actually moved. BBL_Agent_Runner's finally branch treats status = 'running' as unfinished, so a silently failed UPDATE could fail an answered job and post "The agent could not answer" underneath the answer it had just given. A job whose answer reached the channel is now closed as done by that branch, and the failure notice is posted only by whoever actually closed the row.

Version 0.30.0

Changed

  • The settings sections work the way Customer Hub's do: the server renders the section the url names and the nav links are real urls, so a section is reachable, bookmarkable and shareable with no JavaScript at all. assets/js/bbl-settings.js only upgrades the click to an in-place swap, so switching costs no request and keeps unsaved edits in the fields. A url with an unknown or missing section opens Overview, and a connection test still opens Provider, where its result renders.
  • The back button walks the sections that were opened instead of leaving the page.

Fixed

  • Clicking a settings section no longer jumps the page down a step. The switcher assigned window.location.hash, and the browser answers that by scrolling the named element to the top of the viewport; every click pushed the screen further down. The section now lives in the url as ?section=<slug> and the client swaps the card with history.pushState, which does not scroll.

Version 0.29.0

Added

  • The floating launcher bubble can be hidden entirely and scaled in three sizes: small (40px), medium (56px, today's size, unchanged) and large (72px). The site sets a default (BBL_Settings::get( 'launcher_visible' | 'launcher_size' ), new fields on the settings page); a signed-in user's own choice, made from the bubble's own menu, is stored in uiPrefs in localStorage exactly like theme and drawer/window are, and overrides the site default until the user changes it back — a later site-default change does not silently undo it. Hidden means not rendered at all, not display: none on an element that still exists, so no keyboard trap is left behind.
  • The admin-bar Babbla icon carries a second badge, @N (@99+ above 99), for unread mentions, next to the existing total-unread badge. With the bubble hidden it is the only place @-mentions surface outside the panel itself.

Version 0.28.0

Changed

  • The composer no longer waits for GET /channels. The cache paints the conversation on the first frame, and the textarea, the mention button and Send used to stay disabled until the channel list answered — on a host that takes seconds per request, that wait was the whole delay made visible in the one place the user came to type. A channel the cache still has but the server does not is answered with 403/404 and cleared, as before.
  • The cached (or linked) conversation's messages are requested in parallel with the channel list instead of after it. The list only confirms that the conversation still exists, so running the two in series cost a second round trip for nothing; when the list lands on the same conversation it no longer spends a second request on it.
  • An unanswered first load shows placeholder bubbles rather than "No messages yet". An empty pane under a request in flight is a claim the client cannot make, and in the assistant conversation it also offered the starter chips over a history the user had not seen yet.
  • Asking the assistant shows it working immediately instead of at the next poll, and that poll is brought forward to a second after the send is confirmed. The indicator is optimistic: a failed send clears it, and the next agent_busy corrects it either way.
  • Opening the panel, and picking a conversation, puts the cursor in the composer.

Version 0.27.0

Added

  • A conversation menu (⋯) in the chat header: Room info, Open as drawer/window, Copy link to conversation, and Clear conversation. The padlock that used to sit in the header is gone — it opened Room info, which no padlock has ever meant.
  • DELETE /channels/{id}/messages empties a conversation, and only the caller's own assistant conversation: a DM or a room holds what colleagues wrote, and no menu item deletes that. Refused with 409 while a job for that channel is queued or running, since the answer would land in a conversation the user just emptied. Mention rows and read markers go with the messages, so the next answer is not born already read.

Changed

  • The surface toggle moved from its own header icon into the menu, where its label says which surface it switches to.

Version 0.26.0

Changed

  • The name picker paints the people it already knows, filtered locally, and refreshes behind that instead of showing "Loading…" until the server answers. The list is fetched once when the panel opens and kept in the same per-user cache entry as the conversations — names only, never anything about who may see what.
  • Picking someone you already have a direct message with opens it immediately, with no request at all. Only a genuinely new conversation waits for the server.
  • The picker fills the panel instead of a 220px window inside it.
  • A click outside the drawer closes it, the way other wp-admin overlays behave. Page mode is a screen, not an overlay, so it is exempt, and the launcher is excluded because it toggles the panel itself.

Version 0.25.1

Changed

  • The plugin header names Viktor Borg as the author, with https://github.com/vigg3 as the author URI. Regenerated catalogs carry the same name.

Version 0.25.0

Changed

  • The settings screen follows the same shape as Customer Hub's: a sticky bar that always carries Save and says whether there are unsaved changes, a sectioned left column, and one card at a time instead of four headings down a long page. Form rows are label/control pairs in a 220px grid rather than a WordPress table.
  • A new Overview section opens first and answers what an administrator checks before reading any field: whether a provider key is stored (and whether it comes from wp-config.php), which model is entered, and what the daily budget is. "Getting started" moved into it.
  • Section links switch cards instead of jumping down the page, and the open section is kept in the URL hash so a bookmark or a browser back step returns to it. With no hash the server decides which card opens: the Provider card after a connection test, so its result is on screen rather than one click away, and Overview otherwise.

Version 0.24.0

Changed

  • The "What is sent" help tab says what tool results now contain, and that the conversation itself is still sent as colleagues wrote it.

Security

  • No tool result carries personal data to the provider any more. BBL_Tools::run() scrubs every result through the new BBL_Redact, before the transient is filled, so a cached entry is clean too and an integration cannot forget the step. Removed keys cover names, e-mail addresses, phone numbers, street addresses, postcodes, cities, personal numbers and IP addresses, at any depth and whatever their case; an e-mail address written inside free text is masked in place. Filter bbl_redacted_keys lets an integration add keys of its own.
  • order_lookup no longer reads the customer's name or e-mail at all. It returns whether the order is a guest order; staff have the customer on screen in wp-admin next to the answer.

Version 0.23.0

Added

  • Integration registry: a plugin that adds agent tools through bbl_agent_tools declares itself through the new bbl_integrations filter with a name, version, the tools it registers and a note about what they read. Settings → Assistant → Integrations lists them with a checkbox each, so an administrator can see what a plugin offers the assistant and switch it off without deactivating the plugin.
  • Setting enabled_integrations. A newly installed integration starts off: activating a plugin must not change what leaves the store for the provider until someone has seen what it does and said yes.
  • Tools that arrive through the filter without a declaration are grouped under "Tools from an undeclared plugin" — visible and switchable rather than silently trusted or silently dropped.

Changed

  • BBL_Tools::resolve_tools() drops a filter-registered tool whose integration is not enabled, so the model is never offered it and run() refuses it as an unknown tool. Tools registered in core are unaffected.
  • The "What is sent" help tab says that an enabled integration's results go to the provider like every other tool result.

Version 0.22.0

Changed

  • Schema 7: bbl_jobs.message_id is unique, so two workers retrying the same question cannot both create a job and answer twice. BBL_Install::DB_VERSION is 7; maybe_upgrade() adds the index through dbDelta.
  • The SQLite wpdb double returns an empty result and sets last_error when a select fails, the way wpdb does, instead of throwing. A missing table used to throw in tests while production returned null, which let a test prove something production does not do.

Version 0.21.0

Added

  • The channel says what the agent is doing while it works: GET /channels/{id}/messages returns agent_status with the tool now running and a label already translated for display ("Searching the chat…"). The label is built server-side from the tool name and its validated arguments, never from model text, and is always null when agent_busy is false, so a status left behind by a crashed job is never shown under a finished answer. The value lives in a two-minute transient rather than a bbl_jobs column: it is worthless once the job ends. Filter bbl_tool_status_label lets an integration label its own tools.
  • An answer records what it looked at: meta.sources holds up to twelve items a tool actually returned, each with its source_id, a label and a URL built server-side. The assistant is asked to cite a claim with [[s:<id>]]. The client renders those markers in the next release, and only for ids present in meta.sources — a reference the model invents has nothing to render.

Changed

  • The default system prompt carries the citation instruction. A customized prompt is untouched; an untouched one is not stored and follows the new wording.
  • tests/test-i18n.php no longer freezes the sv_SE default prompt to the 0.5.0 wording. That invariant kept a Swedish site on the text it read before the prompt became translatable, but it also meant no new instruction could ever reach one. It is replaced by the requirement that the translation keeps calculate, analytics_report, source_id and the [[s: marker syntax untranslated — a translated marker would make every citation unrenderable.

Version 0.20.0

Added

  • The agent can read the store's own published documents through two read-only tools: list_documents (what is readable) and read_document (one page as plain text). The readable set is WooCommerce's terms page, the site's privacy policy page and the pages an administrator picks under Settings → Assistant → Readable pages — nothing else. Drafts, password-protected pages, posts and unknown slugs are refused with one wording that does not reveal whether the page exists.
  • Setting context_pages: up to 20 published pages the assistant may quote, such as shipping, returns and warranty. Ids are validated when saved and again on every read, so a page that is unpublished or protected afterwards stops being quotable at once.
  • Each read carries a source_id (doc:<slug>) and the page's permalink, built server-side — the anchor the reference chips in a later release attach to.

Changed

  • Document text comes from the stored content with shortcodes and markup stripped; shortcodes and dynamic blocks are never executed, since that would run third-party callbacks inside an agent job outside any page request. A page whose text lives only in a dynamic block therefore reads as empty rather than appearing complete.

Version 0.19.0

Added

  • The agent can read the chat itself when a question calls for it, through three read-only tools: list_conversations (the conversations you can see), search_chat (text search, default the last 90 days) and read_conversation (a window of messages, paged with before). Every read is scoped to the person asking — BBL_Repository::visible_channels() and channel_for_user() decide, so the agent never returns a private room, a DM or another user's assistant conversation the asker could not already open. A channel id the asker cannot see is refused with the same wording as one that does not exist, so the tools cannot be used to probe which conversations exist.
  • Every match carries a source_id (msg:<channel_id>:<message_id>), the anchor the reference chips in a later release attach to.

Changed

  • A tool callback now receives the asking user id as its second argument: fn( array $args, int $user_id ). Existing callbacks that only read shop data ignore it. A user-scoped tool must take it from there and never call get_current_user_id(), because the agent job runs in a background request with no user set.

Version 0.18.1

Added

  • A WP-CLI migration rehearsal (tests/migration-rehearsal.php) that exercises the schema 4 to 6 upgrade, the request-id constraint and the legacy rename against a real WordPress and MySQL, on an isolated table prefix. Every option the migration reads or deletes is snapshotted and put back, since option names carry no table prefix.

Changed

  • Settings screen: help text and the system prompt are capped at a readable line length instead of spanning the full admin width; the section links read as navigation; "Getting started" collapses once a provider key is stored; the provider panel no longer repeats the card's numbered steps or the "in use" status the tab badge already shows; both price fields share one note that says the provider quotes US dollars while the labels carry the store currency; and the test button says it saves everything.

Fixed

  • The trailing read marker posts the newest message on screen instead of deferring itself indefinitely, so the last message of a burst no longer stays unread.
  • The first message page is authoritative for the ids it covers: a locally cached message the server no longer returns is dropped instead of staying on screen. Older loaded history is kept, and a send confirmed while the request was in flight is never dropped. The poll cursor is the newest id the response covers: it moves down when a stale cached id is dropped, and it does not jump to a send confirmed mid-request, so nothing another author wrote below that send is skipped.
  • After a dropped request an open conversation retries after eight seconds instead of sixteen.
  • Inbox and divider dates follow the site language instead of the browser's: the client now receives the locale the interface is translated against and passes it to toLocaleDateString. A Swedish site in an English browser showed 9/18/2026. A WordPress locale that is not a valid BCP 47 tag, such as pt_PT_ao90, falls back to the browser's format instead of throwing inside the inbox render.
  • Swedish: assistantsvar corrected to assistentsvar in the new-group screen and the shared-answer label.
  • The unread count on the floating launcher has its red background again. The launcher sits outside the themed chat root, where the colour variable it referenced is not defined, so the number was white on transparent.
  • Browser test cases destroy their apps and remove their fixtures even when they fail, so one failure no longer makes later cases fail for the wrong reason.

Version 0.18.0

Added

  • The floating launcher shows a separate unread-mention count, alongside ordinary unread messages, with accessible labels. Confirmed reads clear both immediately.
  • Background tabs share short-lived unread counts through a site/user-scoped lease; no conversation content is shared. Closed visible chat checks notifications every ten seconds and on startup. Older in-flight snapshots cannot replace newer shared counts.

Changed

  • Conversation aggregates reuse persistent object cache when available. Fresh visibility checks, live message ids and read markers determine each cache key; role and membership checks remain uncached. Without persistent object cache, the existing database path is retained.

Version 0.17.0

Added

  • Session-scoped chat recovery after admin-page navigation, including drafts, selected mentions, reading position and explicit retry of uncertain sends. Recovery retains the most recently used conversations, including revisited chats after the 20-conversation limit.
  • Durable per-user/per-channel request identity prevents duplicate sends after the former 15-minute retry window. Schema version 6 adds a nullable request id and a unique index without changing existing messages.
  • Backward message pagination for loading older conversation history.

Changed

  • Recently visited conversations render from a bounded memory cache before server revalidation.
  • Conversation-list requests are coordinated and unchanged rows avoid unnecessary redraws. Idle polling backs off without overlapping requests. Opening the chat or returning to the tab during an active request queues an immediate refresh.
  • Conversation previews fetch bounded text instead of complete message bodies and metadata.

Version 0.16.0

Added

  • Colleague and assistant mention picker, personal unread-mention badges, and explicit private-group creation from a personal assistant conversation. Only the composed message and an explicitly selected answer are shared; existing private history is never merged.
  • Delegated access remains effective when capability filters use custom names, while external WordPress and WooCommerce capabilities are never granted.
  • Site-scoped Babbla ownership and delegation. The installer owns a fresh single-site installation; existing or unattended installations use explicit owner setup. Owners assign administrators and chat users. Administrators manage settings and usage without delegation rights or additional private-conversation access.
  • Navigable settings sections and an access page with user search. Delegation preserves existing grants by default, and colleague search includes directly granted chat roles. Store-data tools retain separate permissions when chat access is delegated.

Changed

  • Chat lists and headers state their audience. Shared rooms keep a visible warning about shared assistant answers and requester-authorized store data. Nonmember administrators can no longer invite each other into private rooms.
  • Mobile chat uses one pane with a back action. User pickers search the server, show retry actions and use accessible multi-selection controls. Assistant activity is shown in shared rooms too.
  • Mention recipients and room creation are stored transactionally; room creation retries reuse the same room. Schema version 5 adds an indexed mention table while retaining existing conversations.

Fixed

  • Drafts and send responses remain scoped to their channel. Initial snapshots and concurrent messages merge in order without hiding replies or showing them in another chat. Failed read acknowledgements remain retryable.
  • Queued assistant jobs recheck the original actor's access and use history ending at the triggering question. Later questions cannot replace the queued request's context.
  • Private member lists require current membership even for administrators. User search accepts partial WordPress query rows without hiding eligible colleagues.
  • Membership database failures return an error and roll back partial changes. Private-channel listing fetches memberships once instead of once per room.

Version 0.15.0

Added

  • Swedish. The plugin now loads its text domain and ships a complete sv_SE catalog: the chat client, the settings, usage and diagnostics screens, the system messages the assistant writes into a conversation, and the provider errors it reports. A site running any other locale keeps the English source strings.
  • languages/babbla.pot for translators, plus bin/make-pot.sh, which regenerates it, merges the new strings into every shipped .po, and compiles the .mo and .l10n.php catalogs WordPress reads. Header updates normalize wrapped fields and only touch the catalog header; malformed headers and invalid printf translations stop generation.
  • A test over the translation surface: every client string the JavaScript asks for is localized in PHP with the same English text (a missing key would silently show the fallback), every source string reaches babbla.pot (including echoing helpers, contexts and plurals), and every Swedish entry and plural form is translated and present in both compiled catalogs. Client translation calls require literal arguments, enforced by lint; composer test also runs the catalog-generator regression.

Changed

  • Persisted assistant notices and errors use the configured site language during both enqueue and job execution, with the caller language restored afterward. Background dispatch no longer changes the language stored in a conversation.
  • The default system prompt is translatable, and asks the assistant to answer in the language of the site instead of always in Swedish. On a Swedish site the prompt is the same text as before; on an English one the assistant now answers in English rather than Swedish under an English interface. The configured site locale (the WPLANG option, with WordPress fallbacks) takes precedence over the administrator's profile language, and saving an unchanged default keeps it following future locale and wording changes.
  • An installation still running the 0.5.0–0.14.0 default prompt untouched picks up the new one on upgrade, the same rule that has applied to the 0.3.0 default since 0.5.0. A prompt the admin edited is never touched.
  • Provider errors that reach the user ("Could not reach the OpenAI API.") are translatable. Two kinds stay English on purpose: a tool's argument errors, which only the model reads, and the provider's own HTTP error text, which the connection test shows verbatim because it comes from the API.
  • A cleared system prompt falls back to the shipped one when the settings are saved. Empty prompts stored by older versions also fall back immediately on read, without requiring another save. An empty prompt still reached the provider, only with none of the instructions that keep an answer to tool data — a worse state than the default, and one nothing on the page announced.
  • The release zip no longer carries the .po/.pot sources — WordPress loads the compiled catalogs, so those are development files.

Version 0.14.0

Added

  • The assistant can answer customer questions. analytics_report gains three metrics — customers (distinct customers with a paid order in the period), new_customers and returning_customers (split by WooCommerce's own returning_customer flag, so the numbers match its Analytics screens) — and a customer breakdown that returns one row per customer with their id and name, for "who buys the most".
  • New and returning are decided per customer, not per order: someone who first orders during the period is new once, even if they order again within it, and returning is everyone else — so new plus returning always equals customers, including rows whose flag WooCommerce never set.
  • An order with no customer record at all is left out of the customer counts and out of a per-customer breakdown, rather than becoming a customer named nobody.
  • The tool's description lists the customer metrics among the order-level ones that cannot be split by product or category, so "how many customers bought product X" is not a question the model is steered into asking and having refused.
  • The tool's note now states what a customer count includes: guests count, because WooCommerce keeps a customer record per guest email, so one person who ordered under two emails counts twice.

Changed

  • Customer names come from wc_customer_lookup, the table Analytics maintains — no wp_users, no usermeta, no free-form SQL, and the same 50-row cap and paid-status predicate as every other breakdown. A customer breakdown is order-level, so it is refused together with a product or category split for the same reason the other order totals are.