=== KnowScapes AI Client ===
Contributors: xplicator
Tags: chat, chatbot, ai, assistant, rag
Requires at least: 6.5
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 2.3.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

AI chat widget for your site. Use your own OpenAI, Anthropic or OpenRouter key, or a free KnowScapes Community key.

== Description ==

KnowScapes AI Client adds a floating chat assistant to your site. Visitors type a
question, the reply is generated by the AI provider you selected, and the answer
is rendered as formatted text inside the chat panel.

You choose where the answers come from:

* **KnowScapes Community (free key)** — a retrieval-augmented agent run by xplicator GmbH that answers from your own website. You can request a free key directly from the settings screen, so no account with a commercial AI provider is needed to get started.
* **KnowScapes (paid plan)** — the same service and the same endpoint, with a key from a paid plan instead of the fair-use allowance. Both entries keep their own key, so you can hold a free key and a paid one side by side and switch between them without pasting a key again.
* **OpenAI** — with your own API key, billed to your own OpenAI account.
* **Anthropic** — with your own API key, billed to your own Anthropic account.
* **OpenRouter** — with your own API key, billed to your own OpenRouter account.
* **Any OpenAI-compatible endpoint** — for example a self-hosted gateway on your own infrastructure.

Where the chat appears is up to you: a floating launcher on every page, only on
the content types you pick, or nowhere at all — dropped into a post, a page or a
template with the `[knowscapes_ai_chat]` shortcode instead. Individual entries
can be excluded, the launcher can sit in either bottom corner and can be hidden
on phones, and the whole assistant can be kept for signed-in users only.

Two things make a first message more likely and cost nothing until they are used:
starter questions, shown as buttons in the empty chat, and page context, which
lets the assistant answer about the page the visitor is actually reading. Every
reply carries a copy button.

Every chat shows a short notice that the visitor is talking to an AI and that
replies may contain mistakes. You can reword it — into the language of your site,
for instance — but not switch it off, because the EU AI Act requires that people
can tell when they are talking to an AI.

Every feature of the plugin is available with every provider. Nothing in the
plugin is switched off, time-limited or gated behind a payment: which provider you
use, and what that provider charges you, is entirely your decision.

= The KnowScapes Community plan =

The Community plan is KnowScapes' free plan. On the KnowScapes side it covers one
website as the knowledge source plus one additional document, and the free key
answers a limited number of messages per hour and per day for the site it is used
on — at the time of writing 20 per hour and 480 per day, roughly three to six
visitor conversations an hour, which suits a small site. A short burst above the
hourly rate is absorbed rather than refused.

When the allowance is used up, the service declines further messages until the
next period. Nothing about the plugin changes: no feature is switched off, no
countdown starts, and every setting keeps working. Visitors see a short notice
that the assistant is unavailable for now, and you see the reason and the current
allowance on the plugin's settings screen. The current figures — messages,
pages and document size — are published at https://knowscapes.org/free-key-limits/
and the plans at https://knowscapes.org/pricing/

You are never obliged to use this key. The same plugin works with your own
OpenAI, Anthropic or OpenRouter key, or with your own endpoint, and it behaves
identically either way.

= Your API key stays on the server =

Visitors never see your key. The browser talks only to a REST route on your own
WordPress site; that route adds the key and forwards the request. The key is
stored in the WordPress options table and is never printed into a page, a script
variable or a response body. The settings screen only ever shows it masked.

= Nothing happens until you say so =

After activation the plugin is inert. It has no key, no provider endpoint it can
reach, and no widget on your site, and it never contacts anything on its own —
not on activation, not when an admin page loads, not when a visitor views your
site.

Three things can send a request, and each one is something you start:

1. Pressing **Request free key** sends your e-mail address and this site's URL to
   knowscapes.com, and only after you tick the box next to that button.
2. Entering an API key, or pressing **Load available entries**, asks the
   selected provider which models your key may use. Installing a free key through
   the link in its e-mail does this once as well, as part of that same button
   press. Opening the settings screen alone never does.
3. A visitor sending a chat message, which is possible only once you have
   supplied a key, chosen a model, confirmed the data transfer and switched the
   widget on.

No visitor data of any kind leaves your site before that last step.

= Features =

* Floating chat launcher with ten icons to pick from
* Light, dark or automatic colour scheme, and a configurable accent colour
* Markdown replies, sanitised before they are inserted into the page
* Optional collapsible reasoning block for reasoning-capable models
* Configurable system prompt, reply length cap and per-visitor rate limit
* Greeting message that costs nothing, because it is never sent to the provider
* AI notice under every chat, rewordable but always shown
* Uninstall routine that removes its option and its transients, across every site of a multisite network

== External services ==

This plugin is a client for external AI services. Which service is contacted
depends on the provider you select in its settings. Nothing below happens on a
default installation; each transfer requires the configuration steps described
above.

= KnowScapes AI API (xplicator GmbH) =

Used when either **KnowScapes Community (free key)** or **KnowScapes (paid plan)**
is selected. Both reach the same service at the same address; they differ only in
which key is sent, and therefore in the allowance that applies to it.

* **What is sent:** the text of the visitor's current message, the earlier messages of that same conversation (at most the last 20, each truncated to 4,000 characters), your configured system prompt, and the agent identifier. If you switch page context on, your system prompt can also carry the title, address and text of the published page the visitor is reading, up to the character limit you set. The API key is sent as an authorisation header.
* **When:** each time a visitor submits a message in the chat.
* **Endpoints:** `https://knowscapes.com/api/v1/chat/completions` and, when you press the button to load the agent list, `https://knowscapes.com/api/v1/models`.
* Terms of use: https://xplicator.com/legal-terms-of-use/
* Privacy policy: https://xplicator.com/legal-service-privacy/
* Service description (processing location, retention, sub-processors): https://xplicator.com/legal-service-knowscapes/

= KnowScapes free key service (xplicator GmbH) =

Used only when you fill in the request form on the settings screen, tick the
consent box and press the button. It is never called automatically.

* **What is sent:** the e-mail address you entered; the URL and the name of this site; and the address of this plugin's own settings page inside your WordPress admin area. The settings-page address is what allows the confirmation e-mail to carry a button that offers to install the key on this site.
* **When:** once, on that button press.
* **Endpoint:** `https://knowscapes.com/api/trial-keys` — the path is the service's own name for its route; the key it issues is a free one with a fair-use allowance, not a time-limited trial.
* **What comes back:** a confirmation that the key was sent. The key itself arrives by e-mail and is never contained in the response.
* **Stored locally:** only a flag that expires after 60 seconds, which stops the button being pressed repeatedly. Neither your e-mail address nor the key is kept.
* **Note on the install link:** the link in that e-mail carries the key as part of its address, so it can appear in your server's access log, and anyone holding the link could offer the key to any site. Opening it stores nothing — it shows a confirmation screen naming the key, and only your confirmation stores it. Confirm only a key you requested yourself, or ignore the link and paste the key from the e-mail into the API key field by hand.
* Terms of use: https://xplicator.com/legal-terms-of-use/
* Privacy policy: https://xplicator.com/legal-service-privacy/

= OpenAI =

Used when the provider **OpenAI** is selected, with your own key.

* **What is sent:** the conversation and system prompt as described above — including the page context, if you switched it on — plus the model name, authorised with your key.
* **When:** each time a visitor submits a message, and when you load the model list.
* **Endpoints:** `https://api.openai.com/v1/chat/completions`, `https://api.openai.com/v1/models`
* Terms of use: https://openai.com/policies/terms-of-use/
* Privacy policy: https://openai.com/policies/privacy-policy/

= Anthropic =

Used when the provider **Anthropic** is selected, with your own key.

* **What is sent:** the conversation and system prompt as described above, plus the model name, authorised with your key.
* **When:** each time a visitor submits a message, and when you load the model list.
* **Endpoints:** `https://api.anthropic.com/v1/messages`, `https://api.anthropic.com/v1/models`
* Terms of use: https://www.anthropic.com/legal/commercial-terms
* Privacy policy: https://www.anthropic.com/legal/privacy

= OpenRouter =

Used when the provider **OpenRouter** is selected, with your own key.

* **What is sent:** the conversation and system prompt as described above, plus the model name, authorised with your key.
* **When:** each time a visitor submits a message, and when you load the model list.
* **Endpoints:** `https://openrouter.ai/api/v1/chat/completions`, `https://openrouter.ai/api/v1/models`
* Terms of use: https://openrouter.ai/terms
* Privacy policy: https://openrouter.ai/privacy

= Custom endpoint =

When you select the custom provider, requests go to the HTTPS base URL you
entered and to no one else. You are responsible for the terms that apply there.

== Screenshots ==

1. The chat panel on the front end, opened from the floating launcher.
2. Provider selection: your own OpenAI, Anthropic or OpenRouter key, or a free KnowScapes Community key.
3. Requesting a free key. The transfer is named in full and happens only on this button press.
4. Placement and access: where the launcher appears, which post types carry it, and whether only signed-in users may chat.
5. The same assistant placed inline on a page with the shortcode.

== Installation ==

1. Upload the plugin through **Plugins → Add New → Upload Plugin**, then activate it.
2. Open **KnowScapes AI** in the admin menu.
3. Pick a provider.
4. Enter that provider's API key. For KnowScapes you can instead request a free key right under the API key field: enter your e-mail address, confirm the transfer, press the button, then either press the button in the e-mail you receive or paste the key into the API key field.
5. The list of models or agents loads by itself once the key is entered, and the first entry is selected. Pick another from the dropdown if you prefer, or choose **Enter manually…** for an endpoint that publishes no list.
6. Tick the consent box, tick **Show the chat widget on the front end**, and save.
7. The launcher now appears in the bottom-right corner of your site. To put the chat inside a post, a page or a template instead, use the `[knowscapes_ai_chat]` shortcode and set **Where the launcher appears** to the shortcode-only option.

== Frequently Asked Questions ==

= Do I need a paid account anywhere? =

No. KnowScapes issues a free Community key, which is enough to run the widget.
If you would rather use OpenAI, Anthropic or OpenRouter, you use your own key
there and are billed by them.

= I have a paid KnowScapes key. Which provider do I select? =

**KnowScapes (paid plan)**. It is technically identical to the free entry — same
service, same address, same agents — but it is a separate entry with a key slot
of its own, so a paid key is not pasted into a field labelled as the fair-use
tier, and switching between the two does not overwrite either key. The free-key
request form only appears while the free entry is selected.

= What happens when the Community plan's allowance is reached? =

The KnowScapes service declines further messages until the next period, and
visitors see a short notice that the assistant is unavailable for now. The
plugin itself is unaffected: nothing is disabled, no setting is lost, and the
widget resumes on its own once the allowance is back. The reason, and a link to
the current figures, appear on the plugin's settings screen while it lasts.

If the allowance is too small for your site, enter your own OpenAI, Anthropic or
OpenRouter key instead — the plugin works the same way with any of them.

= A reply stops in the middle of a sentence =

The reply ran into **Maximum reply length** on the settings screen. That setting
is the room a single answer gets; when a model reaches it, it stops wherever it
happens to be, which reads as the assistant breaking mid-thought. Raise it — the
default is 4096 tokens, roughly 3,000 words, and up to 32768 is accepted. Only the
tokens actually used are billed, so a generous limit costs nothing by itself.

The plugin now notices this rather than leaving you to spot it by reading the
chat: the reply carries a short neutral line telling the visitor it was cut short
and that they can ask for the rest, and the settings screen shows which limit was
hit and suggests a higher one. That notice clears itself once the limit is raised.

= How do I put the chat inside a page instead of in the corner? =

Use the shortcode `[knowscapes_ai_chat]` in a post, a page, a block or a
template. Add a height if the default of 480 pixels does not suit:
`[knowscapes_ai_chat height="600"]`. Each shortcode is a chat of its own, so two
on one page do not share a conversation, and the floating launcher — if you leave
it switched on — is a third. To have the chat only where you place it, set **Where
the launcher appears** to the shortcode-only option.

= Can I keep the chat off certain pages? =

Three ways, and they combine. **Where the launcher appears** can restrict it to
the content types you choose, which means single entries of those types —
archives, the blog index and search results are not single entries and never
match. **Never on these entries** takes a list of post or page IDs and suppresses
the launcher there whatever the rule above says, which is what you want for a
checkout or a contact page. And the launcher can be hidden on screens narrower
than 600 pixels, for sites where a chat panel would cover the page on a phone. A
shortcode placed inside an entry always works, exclusions included — placing it
there is itself the decision.

= What does page context actually send? =

With page context on, the title, address and plain text of the page the visitor
is reading are put into your system prompt wherever you wrote `{title}`, `{url}`
or `{content}`, up to the character limit you set, and travel to the provider
with each message. `{site_name}` is also available.

Only fully published, public content is ever read. A draft, a scheduled or
pending post, a private one, a password-protected one, a revision, an autosave
and anything of a post type that is not publicly queryable are all refused, and
the text is taken from the stored content with shortcodes stripped rather than
executed. This matters because the page is identified by an ID that comes from
the visitor's browser, so it has to be treated as something anyone can change:
the checks are what stop the assistant being asked about post 123 and answering
out of an unpublished draft.

It is off by default, because it changes what leaves your site. Switching it on
also adds a paragraph to the privacy policy text the plugin suggests under
**Tools → Privacy**.

= Can I keep the assistant for my own team? =

Set **Who may use it** to signed-in users only. That both hides the chat and
refuses the request behind it, so it is not bypassed by someone who knows the
REST route. Signed-in users also get their own rate limit, counted per account
rather than per address — which is fairer as well as more accurate, since
colleagues behind one office router no longer share a single budget.

= Why do so few visitors send a message? =

An empty box asks the visitor to think of something. Starter questions give them
two to four buttons instead; they cost nothing until one is clicked, and they
also steer people towards what your agent answers well. Set them under
Appearance, one per line.

= Can I remove the "AI assistant" line under the chat? =

You can reword it, not remove it. The field is **AI notice** under Appearance;
leave it empty for the built-in text, or write your own — in your site's
language, for example. Article 50 of the EU AI Act requires that people can tell
they are talking to an AI, and the same line tells them that replies can be wrong,
which protects you as well. If you build your own front end on the REST route
instead of using this widget, show an equivalent notice there.

= Can visitors see my API key? =

No. The browser only ever talks to a REST route on your own site. The key is
added server-side and never appears in any page, script or response.

= Is any visitor data stored? =

The plugin stores no conversations, no cookies and no visitor identifiers. A
conversation exists only in the visitor's browser and is gone when the page is
closed.

That covers this plugin and your site. It says nothing about how long your
chosen provider keeps the messages you transmit to them — that is set by their
terms and privacy policy, it differs between providers, and the plugin has no
way to know it. Read the policy of the provider you selected before you describe
retention in your own privacy policy; the links are next to the consent box on
the settings screen. The visitor's IP address is hashed into a short-lived counter purely to
enforce the rate limit, and that counter expires after two minutes. The address
itself is never written anywhere.

= The chat says the page has expired =

The request is protected by a WordPress nonce, and nonces have a limited
lifetime, so a page served from a cache older than that carries a dead one. The
widget detects this, fetches a fresh nonce and retries the message once by
itself, so in practice you should not see this. If you do, reloading the page
always fixes it.

= The rate limit counts all my visitors as one =

Visitors are bucketed by the address your web server reports. Forwarded-for
headers are deliberately not trusted, because anyone can send one and the limit
would be trivial to bypass. Behind a CDN or a reverse proxy, though, that address
is the proxy, so every visitor shares one budget. If you trust your proxy, supply
the real address with the `knowscapes_ai_client_client_ip` filter:

`add_filter( 'knowscapes_ai_client_client_ip', function () { return $_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR']; } );`

= How do I stop the chat running up a bill? =

Two limits work together. The per-visitor limit caps how fast one address can
send, and the daily ceiling caps how many messages the whole site will answer in
a day, which is the one that matters under abuse because an attacker can change
address. Both are on the settings screen; the daily ceiling defaults to 500 and
can be set to 0 to remove it. Only messages that actually reach the provider are
counted, so malformed requests cannot exhaust your budget.

The daily counter is best-effort rather than exact: WordPress offers no atomic
counter that works both with and without a persistent object cache, so a burst of
truly simultaneous requests can slip a few past the ceiling. It undercounts, never
over, so it will not lock out your visitors early. Treat it as a guardrail against
runaway usage, and set a hard spending limit at your provider as well if the bill
matters to you.

= Where do I see why the provider rejected something? =

The chat itself only ever tells visitors that the assistant is unavailable, so
nothing about your configuration leaks to them. The provider's own wording is
shown to you when you press **Load available entries** on the settings screen,
and, with `WP_DEBUG` and `WP_DEBUG_LOG` both enabled, is written to the WordPress
debug log. The API key is never included in either.

= Why does the reply appear all at once instead of word by word? =

This version fetches the complete reply and then reveals it progressively in the
browser, which keeps the whole transport on the WordPress HTTP API and needs no
direct socket handling. A token-by-token streaming transport is planned for a
later release.

= Which third-party libraries are bundled? =

Two, both GPL-compatible, both shipped unminified, both served locally and never
from a CDN:

* marked 15.0.12 (MIT) — https://github.com/markedjs/marked
* DOMPurify 3.4.16 (Apache-2.0 / MPL-2.0) — https://github.com/cure53/DOMPurify

Nothing in this package is minified and no build step runs over the plugin's own
code: every line that runs is a line you can read. Both libraries are the browser
distribution each project publishes, unmodified apart from a trailing source-map
comment removed because the map files are not shipped. marked is pinned to 15.0.12
rather than the newest release because from version 16 on it publishes only a
bundled, minified browser build. The plugin's own source is at
https://githel.xplicator.com/.

== Changelog ==

= 2.3.0 =
* Added an AI notice under every chat: visitors are told that they are talking to an AI and that replies may contain mistakes. The wording can be changed under Appearance → AI notice (up to 200 characters, plain text); an empty field uses the built-in text. The notice cannot be switched off, because the EU AI Act requires that people can tell they are interacting with an AI. It is linked to the chat panel for screen readers.
* The model or agent is now picked from a dropdown filled with the provider's own list, instead of typed. The list loads by itself as soon as an API key is entered — after a paste, when the field is left, or after a short pause in typing — and the first entry is selected unless the current one is in the list. **Enter manually…** keeps free entry for endpoints that publish no list. Opening the settings screen still contacts no one.
* Moved the free-key request from the foot of the settings screen to the row directly under the API key field, where the key is needed. It is open while no key is stored and folds into one line once there is one. It still posts separately from the settings and only after its consent box is ticked.
* Renamed the free provider entry to **KnowScapes Community (free key)**, after the free KnowScapes plan. Stored keys and settings are unaffected.
* The readme now links the KnowScapes service description, which states processing location, retention and sub-processors.

= 2.2.1 =
* Corrected the privacy policy text the plugin suggests under Tools → Privacy. It said conversations are held in the browser only — true of the plugin, but a site owner pasting it into their own policy would have been stating something about their chosen provider's retention that the plugin cannot know. Every claim is now scoped to the plugin, with a sentence pointing at the provider's own terms.
* The same clarification in the "Is any visitor data stored?" FAQ entry.

= 2.2.0 =
* Added the `[knowscapes_ai_chat]` shortcode, so the chat can sit inside a post, a page, a block or a template instead of only in the corner. Each one is a conversation of its own, and the height is settable per shortcode.
* Added placement control: the floating launcher can appear on every page, only on the content types you choose, or nowhere at all. Individual entries can be excluded by ID, and the launcher can be hidden on screens narrower than 600 pixels.
* Added starter questions — up to four buttons in the empty chat. They are sent only when a visitor clicks one, so they cost nothing until then.
* Added page context: `{title}`, `{url}`, `{site_name}` and `{content}` in the system prompt are filled in from the page the visitor is reading. Off by default. Only fully published public content is ever read — drafts, scheduled, pending and private posts, password-protected posts, revisions, autosaves and non-public post types are all refused, because the page is identified by an ID that comes from the browser.
* Added a signed-in-users-only mode. It hides the chat and refuses the REST request, rather than only hiding it, and signed-in users get their own rate limit counted per account instead of per address.
* Added a copy button to every reply, optional avatars, and a choice of bottom corner and distance from the edge for the launcher.
* Avatars are drawn in the browser from the launcher icon and a generic symbol. No avatar service is contacted, so this adds no outbound request and tells no third party that the page was viewed.
* The privacy policy text the plugin suggests now describes the page-context transfer, but only while page context is switched on.
* Scripts and styles are now registered up front and enqueued only where a chat is actually rendered, so a page with no chat on it loads neither.

= 2.1.0 =
* Added **KnowScapes (paid plan)** as a provider of its own, for sites using a paid KnowScapes key rather than the free fair-use one. It is the same service and endpoint as the free entry, with a separate key slot, so both keys can be stored side by side. The free-key request form now appears only while the free entry is selected.
* Raised the default reply length from 1024 to 4096 tokens, and the accepted maximum from 8192 to 32768. 1024 tokens is roughly 750 words, which a thorough answer passes easily — the reply then stopped mid-sentence with nothing to say why. Installations still on the old default are raised to the new one on update; a value you set yourself is left untouched.
* A reply that runs into the length limit is now recognised as such. Visitors get a short neutral line saying the reply was cut short and that they can ask for the rest, and the settings screen names the limit that was hit and suggests a higher one. Previously a truncated reply was indistinguishable from a finished one.
* The reply length field now explains what the setting does and what it costs.

= 2.0.1 =
* Fixed the Save button doing nothing at all on the settings screen. The endpoint field was validated by the browser while being hidden for every provider except the custom one, and a browser refuses to submit a form holding an invalid control it cannot focus — with no message anywhere. The field is now validated on the server, which can report the problem properly.
* The endpoint URL is no longer discarded when a save does not include that field.
* Removed the 64-token step on the reply length, which rejected sensible values such as 1000.
* Turned off browser autofill on the endpoint and model fields.

= 2.0.0 =
* Added support for OpenAI, Anthropic, OpenRouter and any OpenAI-compatible endpoint alongside KnowScapes, each with its own stored key.
* Added a request form for a free KnowScapes fair-use key, with the key delivered by e-mail and an install link that opens a confirmation screen on this site.
* Removed the shared API key and agent identifier that earlier versions shipped as defaults. Every installation now supplies its own key.
* The widget now stays hidden until the site owner has consented to the outbound data transfer and switched it on explicitly.
* Replaced the direct cURL streaming transport with the WordPress HTTP API; replies are revealed progressively in the browser instead.
* Added a configurable system prompt and reply length cap.
* Added suggested privacy policy text via the WordPress privacy tools.
* Renamed all CSS classes, script handles and JavaScript globals to a plugin-specific prefix.
* Every string is now translatable and every output escaped.
* Uninstalling now removes the plugin's transients as well as its option, across every site of a multisite network.
* Added a site-wide daily ceiling next to the per-visitor rate limit, so the assistant cannot run up an unbounded bill under abuse.
* A provider reporting that an allowance is used up is now told apart from one reporting momentary overload. The provider's own wording and any link it supplies are shown to the site owner on the settings screen and clear themselves once the allowance is back; visitors see only a short neutral notice.
* The chat widget now recovers by itself from a stale nonce on a heavily cached page instead of asking the visitor to reload.
* Visitor messages are no longer passed through a tag stripper, which used to truncate any message containing a "<" character.
* Bundled marked and DOMPurify now ship unminified and readable, so no minified code remains anywhere in the package.

= 1.0.1 =
* Fixed the launcher being distorted by themes that set global button dimensions.
* Fixed the launcher icon not rendering in Firefox.

= 1.0.0 =
* Initial release.

== Upgrade Notice ==

= 2.3.0 =
Adds an AI notice under every chat, as required by the EU AI Act. It appears
automatically after the update; reword it under Appearance → AI notice if the
default does not suit your site.

= 2.2.1 =
Corrects the suggested privacy policy text, which implied something about your
provider's retention that the plugin cannot know. If you copied that text into
your privacy policy, re-copy it.

= 2.2.0 =
Adds the [knowscapes_ai_chat] shortcode, placement and access control, starter
questions and optional page context. Nothing existing changes: the launcher keeps
appearing everywhere until you say otherwise, and page context starts off.

= 2.1.0 =
Adds a provider entry for a paid KnowScapes key, and fixes replies stopping
mid-sentence by raising the reply length limit and reporting when it is reached.

= 2.0.1 =
Fixes a Save button that silently did nothing on the settings screen. Update if
your settings would not persist.

= 2.0.0 =
This release removes the shared API key that earlier versions shipped with. After
updating, open the settings screen, supply your own key or request a free one,
confirm the data transfer and switch the widget back on.
