Skip to main content
The loader (widget.js) puts one object on your page, window.JunDesk. This page lists everything on it. The loader is MIT-licensed and readable in the repo at public/widget.js.

Waiting for the loader

With the recommended snippet (defer, in <head>, before your scripts), window.JunDesk exists by the time your own deferred scripts run. If your code can run earlier, or the snippet loads some other way, wait for the jundesk:ready event:
For one-off calls, optional chaining is enough: window.JunDesk?.open(). Every method is safe to call at any time; none of them throws into your page.

Script attributes

Methods

JunDesk.open(options?)

Opens the chat. With an intent, opens a purpose-built chat defined by a task (Intents).
string
The intent’s name: lowercase letters, digits, _ and -, up to 40 characters. Anything else opens a plain chat.
function
Called once when the visitor clicks the intent’s exit button, after the chat closes. Required when the intent has an exit; without it, a plain chat opens and a console warning explains why.

JunDesk.close()

Closes the chat. With the island, folds it back to its resting pill.

JunDesk.toggle()

Opens the chat if it’s closed, closes it if it’s open.

JunDesk.identify(token)

Tells the desk who the signed-in customer is.
string | null
required
A short-lived HS256 JWT your backend signed with the desk’s identity secret. Passing null forgets the identity in the chat; use logout() on sign-out instead.
The token goes to the desk over the loader’s live connection and from the chat frame. If it’s rejected, the frame logs [Jun] identify failed: <reason> in the console and the visitor stays anonymous.

JunDesk.logout()

Call it when the customer signs out of your app. Forgets this browser’s chat identity (the next person starts fresh), drops the customer’s user secrets on the desk, and starts a new visitor session. Takes no arguments.
boolean
required
true when the visitor accepts, false when they decline or withdraw.
Only matters with data-consent="required". true lets the widget store its session and visitor token and connect to the visitor list. false disconnects and deletes what the chat frame stored. Consent isn’t remembered across page loads: call it on every page where your consent tool says yes.

JunDesk.setTheme(theme)

Makes the widget follow your site’s own light / dark switch, instead of the theme set on the desk.
"light" | "dark" | "auto" | null
required
"light" or "dark" forces one. "auto" follows the visitor’s system setting. null (or anything else) goes back to the desk’s Theme.
The button, the greeting card, the island and the open chat switch at once, with no reload. It lasts until the page unloads, so call it on each page load, or put data-theme on the tag for the first paint:
With the island on Dark (its default), the island stays near-black whatever the theme; the theme changes the island only on Match theme.

JunDesk.reportError(error)

Adds your app’s own description of a failure to the debug timeline, where the AI and your agents see it. It can also trigger a “Want a hand?” nudge.
string
required
What went wrong, in your app’s words. Masked and cut to 300 characters.
string
An optional code for your team, like import.row_invalid. Letters, digits, _, . and -, up to 60 characters. Agents and issue drafts see it; the AI answering visitors doesn’t.
Does nothing with data-capture="off", before consent, or without a non-empty message.

JunDesk.registerAction(action)

Registers a page action the AI can run for the visitor: a WebMCP tool plus Jun’s extras. Returns a function that removes it.
string
required
The action’s name, up to 64 characters.
string
required
What it does, in one line.
(input, signal) => any
required
Runs it in the visitor’s browser.
object
JSON Schema for the inputs.
object
WebMCP hints (readOnlyHint, consequentialHint).
string[]
Paths it’s offered on ("/pricing", "/plans/*").
string | number
One instance per item on screen.
Element | RefObject
Highlighted while it runs.
object | function
Small facts for the AI.
() => boolean
Checked before it’s offered and before it runs.
(result) => any
Adds an Undo button.
"auto" | "confirm" | "human"
default:"confirm"
How it runs.
string
The line shown while it runs.
string
A desk widget to show the result as.
Without name, description or an execute function, it logs Jun: registerAction needs name, description and execute and isn’t registered. Registering the same name (and key) again replaces the older one, with a console warning. Page actions explains every field, risk levels and multi-step runs.

JunDesk.onWidgetAction(handler)

Listens for card buttons meant for your page. A widget card’s action with ChatKit’s handler: "client" never goes to the desk: it comes here. Returns a function that removes the listener.
(action) => void
required
Called with the action:
string
The action’s type, as written in the widget.
any
The action’s payload, if it has one.
object
The values of the card’s form fields when it was pressed.
string
The widget’s name.

Events

The loader fires these on window. onWidgetAction is a thin wrapper around jundesk:widget-action, so either works:
jundesk:ready fires during the loader’s own run. A listener added after that misses it, which is why the pattern above checks window.JunDesk first.

document.modelContext

If the browser has no native WebMCP, the loader defines document.modelContext before your scripts run, so you can register tools in plain WebMCP: In browsers with native WebMCP, the loader leaves document.modelContext alone and reads from it. Page actions has the details.

Keyboard

  • With the island, / anywhere on the page opens the chat, unless the visitor is typing in a field.
  • Esc closes the open chat window, and folds the island back.

Internal members

JunDesk._q and JunDesk._a are the loader’s own plumbing for queued and registered page actions. Don’t rely on them. The postMessage messages between the loader and the chat frame (jun:*) aren’t a public API either: they can change in any release.

Install

The snippet and where it goes.

Widget options reference

Every setting in one table.