@filigran/chatbot 3.14.0 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,283 @@ the versions follow [semantic versioning](https://semver.org/spec/v2.0.0.html).
8
8
  This file starts at 3.10.0; earlier releases are on the
9
9
  [releases page](https://github.com/FiligranHQ/filigran-ui/releases).
10
10
 
11
+ ## 3.15.0 — unreleased
12
+
13
+ ### Added
14
+
15
+ - `mode="inline"`: the panel fills the host's container instead of floating
16
+ over the page, laid out like fullscreen (conversation list beside the
17
+ thread), with no view switcher and no close button. For a host that gives
18
+ the chat a page of its own.
19
+ - A host can own the conversation on screen (`conversationId`,
20
+ `onConversationChange`), pick the agent selected first (`initialAgent`,
21
+ `onAgentChange`) and drive the panel through its `ref`, a handle that is the
22
+ same object for the panel's life:
23
+ - `send(text, { extras, displayText })`: the backend receives *text* with
24
+ `extras` in its body, and the user's bubble shows `displayText` (as with
25
+ `onBeforeSend`'s, which wins when it gives one). It resolves to what
26
+ became of the message: `'sent'` once the backend takes it, `'steered'`,
27
+ `'held'`, `'dropped'` (empty, taken over, read-only, or the panel
28
+ unmounted first) or `'failed'` (the backend refused it).
29
+ - `newConversation`, `selectConversation`, `setDraft`, `focusComposer` and
30
+ `stop`.
31
+ - `getDraft()` reads the composer's text as it stands (typed, or set by
32
+ `setDraft`), e.g. for a host taking over a held message (`draft.held`)
33
+ to tell whether the composer still holds what was typed.
34
+ - `scrollToMessage` jumps to a message and highlights it.
35
+ - `reloadAgents()` reads the agent list again, keeping the agent picked
36
+ unless it is gone (none is picked once the list is empty). A failed
37
+ reload keeps the list it had, and only the latest reload lands.
38
+ - `startVoiceCall()` starts a voice call (below).
39
+ - `onClose` and `onModeChange` are optional: the header shows the close
40
+ button and the view switcher only when they are given.
41
+ - `storageKeyPrefix` namespaces every key the panel keeps in browser storage,
42
+ for a host that mounts more than one panel.
43
+ - `readOnly` hides the composer, with `readOnlyNotice` in its place, and
44
+ the panel then sends nothing, from the handle or the welcome screen's
45
+ suggestions.
46
+ - `getRequestExtras` adds the host's own fields to each message body (the
47
+ panel's own fields, set by it or not, are never taken from there), and
48
+ `onStreamEvent` hands the host every frame received, including those the
49
+ panel does not render, with what the panel read in it: `end`
50
+ (`'provisional'` on an `error` or `cancelled` marked `provisional: true`,
51
+ `'stored'` on an end naming the stored answer's `message_id`: a
52
+ `done` that is not intermediate, a `cancelled`, an `error`) and `replayed`
53
+ (the `thinking` frame of a turn replayed from its start, `resumed: true`).
54
+ - The panel renders the frames of a backend whose turns outlive their stream:
55
+ a turn re-attached mid-way (`thinking.resumed`), an announced wait
56
+ (`status: wait_start`, with a countdown, kept through what it reports
57
+ meanwhile: `wait_status`, `tool_heartbeat`, `tool_progress`,
58
+ `consult_progress`; streamed text or any other status ends it), `cancelled`,
59
+ an intermediate `done` and the agent that `answered_by` a segment. A
60
+ provisional end (an `error` or `cancelled` marked `provisional: true`; any
61
+ other is final) ends the turn at once; the stored end that follows gives the
62
+ answer its id and a `done`'s content.
63
+ - History is restored with no agent list (`apiEndpoints.agents: null`).
64
+ - A host can render its own cards for the `[[KIND:args]]` markers of an
65
+ answer (`markerRenderers`), by marker name, and take over the built-in
66
+ `FILE`, `PRESENTATION`, `REPORT` and `VIDEO` ones, including the items an
67
+ answer carries with no marker. A card that throws, or a renderer that does,
68
+ shows `This item could not be loaded.` and leaves the rest of the answer
69
+ alone. A marker no renderer names stays text, as before.
70
+ - `transformMessageText` rewrites a message's text before it is shown, copied
71
+ or read aloud (masking secrets, converting a channel's syntax); one that
72
+ throws leaves the text as it came.
73
+ - `codeBlockRenderers` renders a fenced code block of a language the host
74
+ names (`(code, { streaming }) => ReactNode`, matched case-insensitively) in
75
+ place of the code block, e.g. a chart drawn from the block's data. A
76
+ renderer that throws shows `This item could not be loaded.`, as a marker
77
+ card does; a language no renderer names, or a renderer returning `null`,
78
+ stays a code block.
79
+ - `onArtifactOpen` opens a deck, report or video in the host, in place of the
80
+ link to XTM One.
81
+ - Slots for the host's own content: `headerActions`, `banner`,
82
+ `composerHeader`, `transcriptFooter`, `welcome`, `renderMessageExtras` and
83
+ `messageFooterActions` (given the message and `{ streaming }`,
84
+ `ChatMessageSlotContext`: the message is the answer still being written),
85
+ `sidebarRowActions`, `sidebarHeaderActions` and `sidebarSections`.
86
+ - `statusLabel`, `waitingMessages` and `renderWaitingPanel` adapt the status
87
+ line and the waiting panel; `fontFamily` sets the panel's font.
88
+ - An agent's `icon` shows in the picker when it is an `https:` or
89
+ `data:image/` URL, or emoji, and agents carrying a `group` are listed under
90
+ its heading.
91
+ - `@` in the composer can mention agents (`agentMentions`): the agents of
92
+ the agent list are offered above the conversations, and the message carries
93
+ the ids of those it still mentions as `mentioned_agent_ids`, in mention
94
+ order. A message that mentions an agent never steers the running turn.
95
+ - `steerAttachments` lets uploaded files steer the running turn (the steer
96
+ route then takes `file_ids`), and `holdSendsDuringTurn` holds a message that
97
+ cannot steer (files, references, mentions, body fields of its own, a turn
98
+ that has just ended) and sends it as the next turn, with a way to cancel it
99
+ meanwhile. Messages held one after another go together, keeping their body
100
+ fields, and Stop gives back to the composer what the user typed for them;
101
+ a message sent once the stopped turn ended goes out as it winds down.
102
+ - A voice call over the panel, started with `startVoiceCall()` on the handle
103
+ (`voiceCallSupported()` tells a host whether to offer it): the browser's
104
+ speech recognition listens, a phrase is sent after one second of silence
105
+ (`onBeforeSend` sees `source: 'voice'`, a `ChatSendDraft.source` of its
106
+ own), and the answer is said through `apiEndpoints.tts` in pieces of about
107
+ 200 characters, each requested while the one before plays, in the browser's
108
+ voice where the route fails, the browser cannot play a piece, or no route is
109
+ named. Only the final answer of the turn that answers the question is said
110
+ (the panel's own, or the one a fold or a steer names), and only the
111
+ addressed agent's: the other agents' replies show by name, `Did not answer`
112
+ beside a failed one. Speaking over the answer, a tap or Skip stops it; a tap
113
+ while it is awaited cancels the question and its turn, as `stop()` does; a turn silent for 120 s is checked through
114
+ `apiEndpoints.pendingApprovals` and given up on once over.
115
+ - Stable `data-testid`s for host tests: `filigran-chat-panel`,
116
+ `filigran-chat-transcript`, `filigran-chat-user-message`,
117
+ `filigran-chat-assistant-message`, `filigran-chat-composer`,
118
+ `filigran-chat-approval`, `filigran-chat-disclaimer`.
119
+ - `onReady` tells the host when the panel has chosen its agent and read back
120
+ the conversation on screen; calls through the handle made before then wait
121
+ for it, and all of them run in order (a new conversation's agent is in
122
+ place before the send that follows it).
123
+ - `onBeforeSend` sees every send first, from the composer or the handle: it
124
+ can take a send over, or reword it and add fields to its body. It can also
125
+ return `displayText`: the backend receives `text`, and the user's bubble
126
+ (live, steered or held) shows `displayText`, for a host that wraps what the
127
+ user typed in context of its own. A message held while a turn ran is asked
128
+ about again as it leaves (`turnRunning: false`, `held: true`), each send
129
+ held with it on its own; taken over then, what the user typed for it goes
130
+ back to the composer, after what they typed since, and a message all of
131
+ whose sends are taken over is settled `'returned'`.
132
+ - `hideConversationList` hides the conversation list of inline and fullscreen
133
+ modes, for a rail or a task pane: the header, thread and composer hold from
134
+ 320 px wide, and the header's history menu stands in for the list (open,
135
+ delete, Clear all), while a referenced conversation still opens in the panel.
136
+ - `onHeldSendSettled(text, outcome)` tells the host what became of a message
137
+ held until the turn ended: `'sent'`, `'failed'`, `'dropped'`, `'returned'`
138
+ (given back to the composer by Stop, or by `onBeforeSend` taking it over as
139
+ it leaves; told once the composer shows it, so `getDraft()` reads it) or
140
+ `'cancelled'`.
141
+ - `onTurnRunningChange(running, info)` tells the host when a turn starts or
142
+ stops running, own or relayed, and with `false` how it ended
143
+ (`info.ended`: `'answered'`, `'stopped'` or `'failed'`): see
144
+ [Host extensions](README.md#host-extensions) and
145
+ [Turns that outlive their stream](README.md#turns-that-outlive-their-stream).
146
+ - `ChatMessage.answeredBy` (`{ agentId, agentName, agentIcon }`) and
147
+ `ChatMessage.mentionFailed`, from the `answered_by` and `mention_failed` of
148
+ each `done` frame (intermediate ones included) and of each restored entry,
149
+ for a host's own rendering of a turn several agents answer. The panel's own
150
+ rendering is unchanged.
151
+ - A `session_expired` frame on the message stream or the event stream ends
152
+ the turn on screen at once, keeping what it wrote.
153
+ - `@filigran/chatbot/markdown` also exports the answer-marker helpers the
154
+ panel reads markers with: `splitAnswerMarkers`, `stripAnswerMarkers`,
155
+ `markerKinds` and the `AnswerPart` type (and `ChatArtifactKind`), still free
156
+ of React and side effects, so a host renders, previews or strips markers
157
+ exactly as the panel does.
158
+ - `onSessionEnded(reason)` tells the host the user's session ended: a
159
+ `session_expired` frame on either stream, or a `401` whose body's
160
+ `detail.code` is `session_expired` on any request the panel makes, with the
161
+ frame's or the `detail`'s `reason`. It comes once per set of
162
+ `requestHeaders`.
163
+ - `ChatMessage.raw`: the backend's own object for the message, a plain copy
164
+ (the restored, paged or relayed entry; the `done` frame of a live answer,
165
+ or the stored end after a provisional one), for the fields the panel does
166
+ not read.
167
+ - `@filigran/chatbot/workspaces`, a React-free entry point with the panel's
168
+ grouping of a conversation list by workspace: `parseWorkspaces` (which,
169
+ given `userId`, tells the caller's own workspaces by `user_id` where the
170
+ backend sends no `is_own`), `sortWorkspaces`, `groupConversations`
171
+ (generic over the host's conversation and workspace types),
172
+ `fileableWorkspaces`, `groupIsOpen`, `groupCanToggle`, `ELSEWHERE_GROUP`
173
+ and the `WorkspaceGroup`, `GroupableConversation` and `ChatWorkspace` types.
174
+ - `prepareMarkdown` on `@filigran/chatbot/markdown`: the panel's markdown
175
+ normalisation in its order, for a host's own markdown component.
176
+ - `waitingGameUrl` says where the waiting panel's invitation to play leads
177
+ (an http(s) URL or a path of the host's origin), over
178
+ `{agentDashboardUrl}/chat?arcade=play`.
179
+ - New exported types: `ChatPanelHandle`, `ChatPanelMode`, `ChatSendOptions`,
180
+ `ChatSendOutcome`, `ChatSendDraft`, `ChatSendDecision`, `ChatHeldOutcome`,
181
+ `ChatTurnEnd`, `ChatTurnOutcome`, `ChatMarkerRenderer`, `ChatMarkerContext`,
182
+ `ChatCodeBlockRenderer`, `ChatCodeBlockContext`, `ChatStreamEventInfo`,
183
+ `ChatMessageSlotContext`, `ChatAnsweredBy` and `AgentStatusState`.
184
+
185
+ ### Changed
186
+
187
+ - The panel ships its own translations (es, fr, ja, beside English) and
188
+ speaks `locale`'s language without the host translating anything. `t` is
189
+ now an override: the host's wording wins where it translates a key, the
190
+ panel's catalog answers where it returns the key. A host translating every
191
+ key, as before, sees no change. `ChatToggleButton` takes `locale` too.
192
+ A host that passes `t` but no `locale` keeps the panel in English where its
193
+ `t` returns the key; a host passing neither now gets the browser's
194
+ language where the panel has it, rather than English.
195
+ - A stopped answer says `Stopped`, rather than vanishing when nothing was
196
+ written yet.
197
+ - The Send, attach, close and view-switcher buttons have accessible names.
198
+ - Files dragged over the thread attach on drop (wherever uploads are on), and
199
+ the drop does not reach the host's own drop zones.
200
+ - A conversation being read back shows a skeleton rather than the welcome
201
+ screen.
202
+ - A conversation whose restore fails because the backend is unreachable or
203
+ answers 502, 503 or 504 keeps its id, rather than being dropped for a new
204
+ chat, and is read back once the backend answers (retried with backoff, and
205
+ at once when the browser is back online or the page is shown again, never
206
+ over a turn in progress); any other error still resets it.
207
+ - A conversation whose restore answers 401 or 403 (expired credentials) keeps
208
+ its id too, rather than being dropped for a new chat (a host's `?c=`
209
+ survives): the panel shows the response's `detail` (or `This conversation
210
+ could not be loaded.`) with a Retry button, and reads the conversation back
211
+ as soon as `requestHeaders` change.
212
+ - A message the backend refuses before streaming (a non-2xx answer) shows the
213
+ response's `detail` (a string, or the `message` or `detail` of an object)
214
+ instead of `Unable to connect. Please check the configuration.`, which stays
215
+ for a refusal that says nothing; `send` still resolves `'failed'`.
216
+ - The stylesheet declares Tailwind's theme variables (`--color-*`,
217
+ `--radius-*`, `--shadow-*`, ...) on the panel root only, no longer on
218
+ `:root` and `:host`, so loading it never redeclares a host's own variables;
219
+ the `@property` fallbacks of browsers without it are kept to the panel's
220
+ elements too. A host element outside the panel no longer inherits them.
221
+ - A fenced code block's language label shows the whole language
222
+ (`objective-c`, not `objective`).
223
+ - A long agent name in the header is cut short rather than pushing the
224
+ header's controls off a narrow panel.
225
+ - A steer the backend refuses shows the refusal's `detail` above the composer,
226
+ the text going back to the composer as before; a `409` (the turn has just
227
+ ended, an ordinary race) shows nothing. A steer stored that the backend
228
+ says no turn will answer (`"answered": false`) stays, with a notice that it
229
+ will be read with the next message, until that send.
230
+ - The composer's footer under a message that waits for the running turn
231
+ (a reference, a mention) says `This message waits for the current response`,
232
+ a new key, in place of `Referenced conversations wait for the current
233
+ response`.
234
+
235
+ ### Fixed
236
+
237
+ - A conversation read back after the user has already sent a message no
238
+ longer replaces that turn: its history joins the thread in place once the
239
+ turn ends (a stored message missing from the screen after the one stored
240
+ before it), and its agent's name shows at once.
241
+ - Sending no longer throws on a page served over plain http, where
242
+ `crypto.randomUUID` does not exist.
243
+
244
+ ### Host opt-in
245
+
246
+ - `apiEndpoints.cancel` (no default): Stop asks the backend to end the turn and
247
+ waits for the stream's `cancelled` frame, instead of dropping the stream. An
248
+ answer of `{ "turn": "none" }` lets go of the turn at once and reads the
249
+ conversation back, unless the Stop went before the panel's own send started
250
+ its turn: it then carries `"awaiting_send": true`, and on `none` stays
251
+ pending and is sent again, without the flag, as the turn starts; any other
252
+ answer reached a turn, and nothing goes again. A refused Stop (e.g. `503`)
253
+ keeps the turn running and says why above the composer; the status line
254
+ keeps what the turn reported since the click.
255
+ - `apiEndpoints.skipWait` (no default): `Continue now` on an agent's wait.
256
+ - `apiEndpoints.events` (no default): the panel follows the conversation's
257
+ event stream and renders turns started in another tab or before a reload,
258
+ user messages sent elsewhere, new titles and approvals answered elsewhere;
259
+ a panel whose own stream drops mid-turn picks the turn up from it, keeping
260
+ the segments it closed. Stop during the handover ends the turn. A relayed
261
+ turn the stream let go of while the page was hidden holds sends, and runs
262
+ on for `onTurnRunningChange`, until the reopened stream replays it, or, 5 s
263
+ after the page shows again or at once on a refused stream, until the
264
+ conversation read back says how it ended. Every frame of a turn names it
265
+ (`turn_id`): on the event stream, and on the message stream from its
266
+ `turn_started` on; an event stream frame of a turn naming none is not read,
267
+ and the console warns of it once per frame type. See
268
+ [Turns that outlive their stream](README.md#turns-that-outlive-their-stream)
269
+ for reconnection, the client id and replays.
270
+ - With `cancel`, `skipWait` or `events` named, a send made while a turn ended
271
+ provisionally still winds down is held, as during a turn, until its stored
272
+ end or its stream's close (the panel's own, for its own turn; another turn
273
+ starting ends it too), at most 5 s: the backend still holds the
274
+ conversation, and the message would join the ending turn unanswered. A
275
+ relayed turn winds down across the event stream's reopening.
276
+ - With `cancel`, `skipWait` or `events` named, a new chat's conversation is
277
+ created before its first message, so Stop, "Continue now" and steering
278
+ reach the backend from the first turn.
279
+ - `apiEndpoints.messagesPage` (no default): "Load earlier messages" fetches
280
+ older messages from the backend a page at a time, when the restore says
281
+ there are some (`has_more`), and keeps the reader where they were.
282
+ - `apiEndpoints.clearAll` (no default): the conversation list and the
283
+ header's history menu offer "Clear all conversations", confirmed first.
284
+ - `apiEndpoints.tts` (no default): read aloud uses the backend's voice, a
285
+ long answer in several bounded requests played in turn, and the browser's
286
+ voice for what is left when the route fails.
287
+
11
288
  ## 3.14.0 — 2026-10-19
12
289
 
13
290
  ### Added