@filigran/chatbot 3.19.1 → 3.21.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 +133 -3
- package/README.md +32 -13
- package/dist/index.d.ts +80 -9
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/markdown.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,7 +8,122 @@ 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.
|
|
11
|
+
## 3.21.0 — unreleased
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `onConversationChange` says why the conversation changed: its second argument
|
|
16
|
+
is `{ created }`, true for a conversation the panel has just created from a
|
|
17
|
+
new chat (its first message or file), false for one opened from the list, on
|
|
18
|
+
a reload or in place of a deleted one, and when it is left for a new chat. A
|
|
19
|
+
host could not tell an opened conversation from a created one, as both came
|
|
20
|
+
after a new chat.
|
|
21
|
+
|
|
22
|
+
## 3.20.0 — 2026-11-10
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `ChatMarkerRenderer.standalone`: an answer that is the renderer's marker and
|
|
27
|
+
nothing else shows the card alone, without the agent's name, the time or the
|
|
28
|
+
actions, as a note between messages (a change of agent, for instance). Each
|
|
29
|
+
answer showed its marker's card inside the answer's frame.
|
|
30
|
+
- The panel speaks German, Italian, Korean, Russian and Simplified Chinese on
|
|
31
|
+
its own: a catalog for each, picked from `locale` by its language whatever
|
|
32
|
+
the region (`de-de`, `it-it`, `ko-kr`, `ru-ru`, `zh-cn`). Those languages
|
|
33
|
+
showed in English unless the host translated every key through `t`.
|
|
34
|
+
Traditional Chinese tags (`zh-TW`, `zh-HK`, `zh-Hant`) keep falling back.
|
|
35
|
+
- An agent the backend marks `disable_chat: true` (`XtmAgent.disableChat`) is
|
|
36
|
+
said `Not recommended for chat` in the agent picker, and the composer warns
|
|
37
|
+
while it is the conversation's agent as the header names it. It stays
|
|
38
|
+
selectable.
|
|
39
|
+
- The header names the channel a conversation was started in under the
|
|
40
|
+
agent's name (`Agent · {channel}`), as its row does, when the host draws no
|
|
41
|
+
`headerSubtitle`; `channelLabel` names it there too. The restore's
|
|
42
|
+
`channel` names it before the list is read.
|
|
43
|
+
- The composer's warning also covers an agent the draft `@`-mentions that is
|
|
44
|
+
not meant for chat, until the mention is removed.
|
|
45
|
+
- With workspaces, the list's `Not in a workspace` group has a `New
|
|
46
|
+
conversation outside a workspace` button, beside each workspace's own.
|
|
47
|
+
- In a reply's "Working files" list, a file the same reply wrote again later,
|
|
48
|
+
with the same name and kind of file (MIME type without its parameters, else
|
|
49
|
+
the type label), carries a "Superseded" tag; its last version carries none.
|
|
50
|
+
A file listed again ranks where it first appeared.
|
|
51
|
+
- `ChatPanelHandle.attachFiles(files)` puts files in the composer's tray as if
|
|
52
|
+
the user picked them, uploaded and held to the same limits, for the next
|
|
53
|
+
message: files another app shared to the host. Nothing happens where the
|
|
54
|
+
panel takes no files.
|
|
55
|
+
- `initialConversationListCollapsed` starts the conversation list collapsed to
|
|
56
|
+
its strip, from which the user opens it: for a phone or a narrow window.
|
|
57
|
+
Read once, when the panel mounts.
|
|
58
|
+
- `replaceMessage(message, { streaming })` lets a host draw a message its own
|
|
59
|
+
way: `undefined` or a boolean leaves it to the panel, `null` leaves it out,
|
|
60
|
+
and anything else is drawn in its place without the message's frame. It is
|
|
61
|
+
asked of every message, the reply's empty placeholder included, and a throw
|
|
62
|
+
leaves that message to the panel. For messages a host folds into one of its
|
|
63
|
+
own, such as the replies of a turn cut and resumed shown as one card.
|
|
64
|
+
- `expandedWorkspaceGroups` and `onExpandedWorkspaceGroupsChange` let a host
|
|
65
|
+
keep, per user, which workspace groups of the conversation list are open.
|
|
66
|
+
Given, every other group starts collapsed until the user opens it, and a
|
|
67
|
+
workspace the user creates from the list opens. A host still reading them
|
|
68
|
+
passes `undefined` until it has them. Unset, groups start open and the ones
|
|
69
|
+
the user collapses are remembered per browser, as before.
|
|
70
|
+
|
|
71
|
+
### Changed
|
|
72
|
+
|
|
73
|
+
- The agent picker follows the conversation on screen: opening a conversation
|
|
74
|
+
of another agent, or reading back one the host continued with another agent,
|
|
75
|
+
picks that agent, so a new chat starts with it, after a reload too, and
|
|
76
|
+
`onAgentChange` is called.
|
|
77
|
+
An agent picked in the menu meanwhile stays picked. The picker kept the agent
|
|
78
|
+
last picked in the menu, while the header named the conversation's.
|
|
79
|
+
|
|
80
|
+
### Fixed
|
|
81
|
+
|
|
82
|
+
- `maxTotalSize: Infinity` lifts the cap on the total size of a message's
|
|
83
|
+
files, as a host with a per-file limit only passes it (XTM One's web chat and
|
|
84
|
+
Office add-in). The panel read a value that is not finite as invalid and kept
|
|
85
|
+
its 50 MB default total.
|
|
86
|
+
- A message sent from a new chat no longer comes back as the next new chat's
|
|
87
|
+
draft: the draft leaves the conversation it was typed in as it is sent. The
|
|
88
|
+
write of the emptied composer could land on the conversation the send
|
|
89
|
+
created, leaving the sent text under the new chat's draft.
|
|
90
|
+
- An image's lightbox is named after the image (its alt text, or the file's
|
|
91
|
+
name for an uploaded picture) and keeps `Image preview` for one with none; it
|
|
92
|
+
read `Image preview` for every picture.
|
|
93
|
+
- Tab and Shift+Tab stay on the lightbox's own controls (Download when offered,
|
|
94
|
+
Close) while it is open; they reached the page under it.
|
|
95
|
+
- An image's Expand button is visible while it has keyboard focus, so the focus
|
|
96
|
+
given back after the lightbox closes can be seen. It showed on hover only.
|
|
97
|
+
- A refusal whose body has no `detail` shows its `error` when that is a
|
|
98
|
+
sentence, as the products' proxies answer (`{ "error": "..." }`, beside a
|
|
99
|
+
`status` or not), never a code (`invalid_token`, `Unauthorized`) nor a
|
|
100
|
+
status's reason phrase (`Not Found`, `Too Many Requests`). It shows above
|
|
101
|
+
the composer, in place of the answer, on a failed upload's chip, under the
|
|
102
|
+
conversation list and on a conversation that could not be loaded. The panel
|
|
103
|
+
showed its generic message instead.
|
|
104
|
+
- The status line has its own wording when it has no agent's name (`The agent
|
|
105
|
+
is answering…`, `Consulting an agent…`, `Transferring to another agent…`),
|
|
106
|
+
where it filled the name with a lowercase `the agent`: `the agent is
|
|
107
|
+
answering…` started without a capital. A host translating through `t` in a
|
|
108
|
+
language the panel has no catalog for translates these three keys; it
|
|
109
|
+
translated `the agent` before, and shows them in English until it does.
|
|
110
|
+
- A host that can steer (a `steer` route, on by default) gets its conversation
|
|
111
|
+
created before the first message, so the first turn can be steered. Where
|
|
112
|
+
turns end with their stream (no `cancel`, `skipWait` or `events` route) the
|
|
113
|
+
panel learned the conversation's id only at the turn's end, and a message
|
|
114
|
+
typed while that first turn ran could not be sent.
|
|
115
|
+
- A steer the backend refuses because the turn just ended (409) goes out as
|
|
116
|
+
the next message, as a send during a turn winding down does, whatever
|
|
117
|
+
`holdSendsDuringTurn` says. Without it, the text went back to the composer
|
|
118
|
+
and the user had to send it again.
|
|
119
|
+
- Files added from outside the panel's own events (a host's call) upload too:
|
|
120
|
+
the upload launched on a timer that assumed the tray had been updated
|
|
121
|
+
already, which only a pick guaranteed.
|
|
122
|
+
- A wait a resumed turn continued (`wait_start`) ends on `wait_continued`
|
|
123
|
+
with `Analyzing results…`, as the turn moves on to read what it waited for.
|
|
124
|
+
The status line read `Thinking...` until the next status.
|
|
125
|
+
|
|
126
|
+
## 3.19.1 — 2026-11-02
|
|
12
127
|
|
|
13
128
|
### Added
|
|
14
129
|
|
|
@@ -17,9 +132,18 @@ This file starts at 3.10.0; earlier releases are on the
|
|
|
17
132
|
the region (`de-de`, `it-it`, `ko-kr`, `ru-ru`, `zh-cn`). Those languages
|
|
18
133
|
showed in English unless the host translated every key through `t`.
|
|
19
134
|
Traditional Chinese tags (`zh-TW`, `zh-HK`, `zh-Hant`) keep falling back.
|
|
135
|
+
- Inline, as a sidebar and fullscreen, the composer's footer has a line of
|
|
136
|
+
its own for what the keys do (`filigran-chat-composer-hint`), above the
|
|
137
|
+
disclaimer: an idle composer reads
|
|
138
|
+
`Enter to send · Shift+Enter for new line`, a busy one the status it read
|
|
139
|
+
before, and the line keeps its height when empty. The floating window keeps
|
|
140
|
+
one line, where a status takes the disclaimer's place.
|
|
20
141
|
|
|
21
142
|
### Fixed
|
|
22
143
|
|
|
144
|
+
- `filigran-chat-disclaimer` holds the disclaimer alone. It held the
|
|
145
|
+
composer's status in its place while a turn ran or a file failed to
|
|
146
|
+
upload; the status is now `filigran-chat-composer-hint`.
|
|
23
147
|
- A refusal whose body has no `detail` shows its `error` when that is a
|
|
24
148
|
sentence, as the products' proxies answer (`{ "error": "..." }`, beside a
|
|
25
149
|
`status` or not), never a code (`invalid_token`, `Unauthorized`) nor a
|
|
@@ -33,8 +157,14 @@ This file starts at 3.10.0; earlier releases are on the
|
|
|
33
157
|
answering…` started without a capital. A host translating through `t` in a
|
|
34
158
|
language the panel has no catalog for translates these three keys; it
|
|
35
159
|
translated `the agent` before, and shows them in English until it does.
|
|
160
|
+
- A conversation list that could not be read (a refusal, a server or network
|
|
161
|
+
error) says "Could not load your conversations." with a Retry, in the
|
|
162
|
+
fullscreen list and the header's history menu, where it said "No
|
|
163
|
+
conversations yet". A read that fails once the list was read keeps the
|
|
164
|
+
conversations it showed, unless it was refused (401/403), which drops them;
|
|
165
|
+
a backend without the route (404/405) still shows an empty list.
|
|
36
166
|
|
|
37
|
-
## 3.19.0 —
|
|
167
|
+
## 3.19.0 — 2026-11-02
|
|
38
168
|
|
|
39
169
|
### Added
|
|
40
170
|
|
|
@@ -190,7 +320,7 @@ This file starts at 3.10.0; earlier releases are on the
|
|
|
190
320
|
query: the list stayed filtered by a search the user could neither see nor
|
|
191
321
|
clear.
|
|
192
322
|
|
|
193
|
-
## 3.18.1 —
|
|
323
|
+
## 3.18.1 — 2026-11-02
|
|
194
324
|
|
|
195
325
|
### Fixed
|
|
196
326
|
|
package/README.md
CHANGED
|
@@ -105,9 +105,9 @@ import { ChatPanel } from '@filigran/chatbot';
|
|
|
105
105
|
| `composerToolbar` | `React.ReactNode` | — | Extra controls appended to the composer toolbar. The escape hatch for host-specific affordances (XTM One's session-tool picker) — the package never learns what they are. Pass nothing and the toolbar simply has none. |
|
|
106
106
|
| `storageKeyPrefix` | `string` | — | Namespaces every key the panel keeps in browser storage (`<prefix>:filigranChatConversationId`, …), for a host mounting more than one panel |
|
|
107
107
|
| `conversationId` | `string \| null` | — | The conversation on screen when the host owns it (a `?c=` URL); `null` is a new chat. Left `undefined`, the panel remembers its own, and a host-owned one is never written to storage |
|
|
108
|
-
| `onConversationChange` | `(id: string \| null) => void`
|
|
108
|
+
| `onConversationChange` | `(id: string \| null, change: { created: boolean }) => void` | — | Called whenever the conversation on screen changes, whoever changed it (a first answer, New chat, the history); `created` is true for one the panel has just created from a new chat (its first message or file) |
|
|
109
109
|
| `initialAgent` | `string` | — | Agent selected once the list loads, by id or slug, over the remembered one |
|
|
110
|
-
| `onAgentChange` | `(agent: XtmAgent) => void` | — | Called when another agent is picked |
|
|
110
|
+
| `onAgentChange` | `(agent: XtmAgent) => void` | — | Called when another agent is picked, in the menu or by opening a conversation of another agent: the picker follows the conversation on screen |
|
|
111
111
|
| `readOnly` | `boolean` | `false` | Hides the composer: a conversation the user may read but not write to |
|
|
112
112
|
| `readOnlyNotice` | `React.ReactNode` | — | Shown in place of the composer while `readOnly` |
|
|
113
113
|
| `getRequestExtras` | `() => Record<string, unknown>` | — | Fields merged into every message body, read at send time (the host's own per-message options). They never override the panel's fields |
|
|
@@ -119,6 +119,7 @@ import { ChatPanel } from '@filigran/chatbot';
|
|
|
119
119
|
| `onArtifactOpen` | `(artifact: ChatArtifact) => void` | — | Opens a deck, report or video in the host, in place of the `Open in XTM One` link |
|
|
120
120
|
| `headerActions` / `banner` / `composerHeader` / `transcriptFooter` / `welcome` | `React.ReactNode` | — | The host's content in the header, above the thread, above the composer, after the last message, or in place of the welcome screen |
|
|
121
121
|
| `renderMessageExtras` / `messageFooterActions` | `(message, { streaming }) => React.ReactNode` | — | The host's content under a message, and controls in a finished answer's footer; `streaming` while the message is the answer still being written. `message.raw` holds the backend's own fields — see [Host extensions](#host-extensions) |
|
|
122
|
+
| `replaceMessage` | `(message, { streaming }) => React.ReactNode` | — | The host's own drawing of a message: `undefined` or a boolean leaves it to the panel, `null` leaves it out, anything else is drawn in its place without the message's frame (name, time, actions). Asked of every message, the reply's empty placeholder included. A throw leaves that message to the panel. For messages a host folds into one of its own, such as a turn cut and resumed shown as one card |
|
|
122
123
|
| `sidebarRowActions` | `(conversation) => React.ReactNode` | — | The host's controls on a row of the conversation list |
|
|
123
124
|
| `sidebarHeaderActions` | `React.ReactNode` | — | The host's controls beside New conversation |
|
|
124
125
|
| `sidebarSections` | `React.ReactNode \| ((search: ChatSidebarSearch) => React.ReactNode)` | — | Sections under the list; a function draws them from the list's search (`query`, trimmed), again when the search or the function changes (pass a stable one, and put hooks in a component it returns), and offers the search box whatever the number of conversations |
|
|
@@ -137,12 +138,14 @@ import { ChatPanel } from '@filigran/chatbot';
|
|
|
137
138
|
| `onTurnRunningChange` | `(running: boolean, info?: ChatTurnEnd) => void` | — | A turn started or stopped running on screen, own or relayed: the state of the Stop button and of `draft.turnRunning`; with `false` comes how it ended, `info.ended`: `'answered'`, `'stopped'` or `'failed'` — see [Host extensions](#host-extensions). After a provisional end (see [Turns that outlive their stream](#turns-that-outlive-their-stream)) `false` comes at once, while sends are still held as the turn winds down: until its stored end or its stream's close, at most 5 s |
|
|
138
139
|
| `onBeforeSend` | `(draft) => false \| { text?, displayText?, extras? } \| void` | — | Sees every non-empty send first and takes it over (`false`), rewords it, shows the user's bubble other words or adds body fields (a message with fields of its own is held with `holdSendsDuringTurn`) — see [Host extensions](#host-extensions) |
|
|
139
140
|
| `hideConversationList` | `boolean` | `false` | Hides the conversation list of fullscreen and inline modes, for a rail or a task pane (usable from 320 px wide); the header's history menu stands in for it — see [Host extensions](#host-extensions) |
|
|
141
|
+
| `expandedWorkspaceGroups` | `string[]` | — | The workspace groups the user has opened (workspace ids), for a host that keeps them per user: every other group starts collapsed, and one the user creates from the list opens. Read once, the first time it is given (`undefined` while the host reads them). Unset, groups start open and the collapsed ones are remembered per browser |
|
|
142
|
+
| `onExpandedWorkspaceGroupsChange` | `(keys: string[]) => void` | — | The groups the user has opened, each time one is opened or collapsed (see `expandedWorkspaceGroups`) |
|
|
140
143
|
| `holdSendsDuringTurn` | `boolean` | `false` | A message that cannot steer is held and sent when the turn ends, instead of waiting in the composer. A turn winding down after a provisional end (until its stored end or its stream's close, at most 5 s) holds sends with or without it |
|
|
141
144
|
| `onBeforeMove` | `(conversation, workspace \| null) => boolean \| Promise<boolean>` | — | Asked before a drag or a row's Move menu files a conversation into a workspace (`null`: out of one); `false` leaves it where it was |
|
|
142
145
|
| `onConversationActionFailed` | `(action, conversation \| null, error \| null) => boolean \| void` | — | A failed `'delete'`, `'rename'`, `'move'` or `'clearAll'`, with the backend's reason; return `true` when the host told the user itself, and the panel says nothing |
|
|
143
146
|
| `sidebarSearchThreshold` | `number` | `7` | The conversation list's search box shows above this many conversations; `0` shows it from the first |
|
|
144
|
-
| `channelLabel` | `(channel: string) => string \| null \| undefined` | — | Names the channel a
|
|
145
|
-
| `headerTitle` / `headerSubtitle` | `React.ReactNode \| ((context: ChatHeaderContext) => React.ReactNode)` | — | A title beside the header's agent name and a line under it, or functions of the conversation on screen (as the list holds it) and the agent |
|
|
147
|
+
| `channelLabel` | `(channel: string) => string \| null \| undefined` | — | Names the channel a conversation was started in, on its row (`Started in {channel}`) and in the header (`Agent · {channel}`); `null` shows nothing, `undefined` keeps the panel's names (Slack, Teams, WhatsApp, Telegram, Discord, Google Chat) |
|
|
148
|
+
| `headerTitle` / `headerSubtitle` | `React.ReactNode \| ((context: ChatHeaderContext) => React.ReactNode)` | — | A title beside the header's agent name and a line under it, or functions of the conversation on screen (as the list holds it) and the agent. Without `headerSubtitle`, the line reads `Agent · {channel}` for a conversation started in a channel its row names |
|
|
146
149
|
| `agentHref` | `(agent: XtmAgent) => string \| undefined` | — | Links the header's agent name to this address (http(s) or a path of the host's origin), in place; the chevron beside it opens the picker |
|
|
147
150
|
| `onAgentNameClick` | `(agent: XtmAgent) => void` | — | Takes a plain click on the header's agent name (over `agentHref`); the chevron beside it opens the picker |
|
|
148
151
|
| `clearAllAction` | `'delete' \| 'archive'` | `'delete'` | What Clear all does, for its wording (`Archive all conversations`) |
|
|
@@ -194,13 +197,16 @@ A host adds what is its own without the package learning about it. An answer
|
|
|
194
197
|
marker `[[KIND:args]]` renders the host's card where it stands, once the answer
|
|
195
198
|
is done (`renderWhileStreaming` to show it earlier); a renderer named `FILE`,
|
|
196
199
|
`PRESENTATION`, `REPORT` or `VIDEO` replaces the built-in card, and a marker no
|
|
197
|
-
renderer names stays text
|
|
200
|
+
renderer names stays text. A `standalone` renderer draws an answer that is its
|
|
201
|
+
marker and nothing else as the card alone, without the agent's name, the time
|
|
202
|
+
or the actions: a note between messages, such as a change of agent:
|
|
198
203
|
|
|
199
204
|
```tsx
|
|
200
205
|
const markers = useMemo(
|
|
201
206
|
() => ({
|
|
202
207
|
WORK_ITEM: { render: (id) => <WorkItemCard id={id} /> },
|
|
203
208
|
INVESTIGATION: { render: (id) => <InvestigationBlock id={id} />, fullWidth: true },
|
|
209
|
+
AGENT_SWITCH: { render: (id) => <AgentSwitchDivider id={id} />, fullWidth: true, standalone: true },
|
|
204
210
|
}),
|
|
205
211
|
[],
|
|
206
212
|
);
|
|
@@ -465,6 +471,11 @@ Returns available AI agents.
|
|
|
465
471
|
`badges` is optional: short labels, already translated, shown after the
|
|
466
472
|
agent's name in the agent picker. Anything but strings is ignored.
|
|
467
473
|
|
|
474
|
+
`disable_chat` is optional: `true` marks an agent meant for assignments rather
|
|
475
|
+
than chat. The picker says `Not recommended for chat` after its name, and the
|
|
476
|
+
composer warns while it is the agent written to; it stays selectable. The
|
|
477
|
+
panel reads it as `XtmAgent.disableChat`.
|
|
478
|
+
|
|
468
479
|
### `POST {apiBaseUrl}/chat/sessions`
|
|
469
480
|
|
|
470
481
|
Restores conversation history (and, implicitly, resolves the session).
|
|
@@ -508,6 +519,11 @@ without one shows no time. `feedback` is the caller's own rating of an
|
|
|
508
519
|
assistant answer: the thumbs are then locked on it ("Feedback already
|
|
509
520
|
submitted").
|
|
510
521
|
|
|
522
|
+
The response may also carry the conversation's `channel` (`slack`, `teams`,
|
|
523
|
+
…), as the list does: the header names it (`Agent · {channel}`) without
|
|
524
|
+
reading the list, which floating and sidebar modes read only once the history
|
|
525
|
+
menu opens.
|
|
526
|
+
|
|
511
527
|
The response **should echo a `conversation_id`**. When the stored
|
|
512
528
|
`conversation_id` no longer exists (e.g. the platform was reset but the
|
|
513
529
|
browser kept an old id in `localStorage`), the backend is expected to
|
|
@@ -564,14 +580,15 @@ Lists the user's past conversations for the history menu in the chat header.
|
|
|
564
580
|
```
|
|
565
581
|
|
|
566
582
|
`last_message_preview` and `channel` are optional: a row shows the preview
|
|
567
|
-
under its title, and
|
|
568
|
-
in (see `channelLabel`).
|
|
583
|
+
under its title, and the search matches it; the row and the header name a
|
|
584
|
+
messaging channel the conversation was started in (see `channelLabel`).
|
|
569
585
|
|
|
570
586
|
Selecting a conversation restores it through the existing
|
|
571
587
|
`POST /chat/sessions` contract above. The endpoint is fetched lazily when the
|
|
572
588
|
history menu opens; a backend that doesn't implement it yet (404/405) simply
|
|
573
589
|
yields an empty list, so the menu shows its empty state instead of breaking
|
|
574
|
-
the chat.
|
|
590
|
+
the chat. Any other failed read shows "Could not load your conversations."
|
|
591
|
+
with a Retry, and keeps the conversations a previous read gave. Set `apiEndpoints.history` to `null` to hide the history menu
|
|
575
592
|
entirely, or point it at a dedicated path if your proxy can't route `GET` on
|
|
576
593
|
the sessions path.
|
|
577
594
|
|
|
@@ -834,13 +851,15 @@ if the user already typed something new), the send resolves `'failed'` and the
|
|
|
834
851
|
refusal's [reason](#a-refusals-reason), when there is one, shows above the composer — so a backend
|
|
835
852
|
without steering support degrades gracefully and the message is never silently
|
|
836
853
|
lost. A `409` means no turn is running any more, an ordinary race: no notice
|
|
837
|
-
shows, and
|
|
838
|
-
|
|
839
|
-
|
|
854
|
+
shows, and the message is held and sent as the next turn, with or without
|
|
855
|
+
`holdSendsDuringTurn`. Set `apiEndpoints.steer` to `null` to disable the
|
|
856
|
+
steering affordances entirely.
|
|
840
857
|
|
|
841
858
|
Steering only applies to sends on the `rest` backend with a known
|
|
842
|
-
`conversation_id
|
|
843
|
-
|
|
859
|
+
`conversation_id`. The stream names a fresh conversation only on `done`, so
|
|
860
|
+
while steering is on a new chat is created (`POST {sessions}`) before its first
|
|
861
|
+
message, and that first turn can be steered too; with `steer: null` it is not.
|
|
862
|
+
A message with attachments steers only with `steerAttachments`
|
|
844
863
|
(once every file is uploaded); one carrying conversation references, agent
|
|
845
864
|
mentions or body fields of its own (`send`'s `extras`, `onBeforeSend`'s
|
|
846
865
|
`extras`) never steers, since a steer reaches the agent as text alone: it waits
|
package/dist/index.d.ts
CHANGED
|
@@ -36,8 +36,11 @@ interface ApiEndpoints {
|
|
|
36
36
|
* message was persisted and will be injected into the running agentic loop
|
|
37
37
|
* at the next iteration boundary. On a non-2xx response the optimistic
|
|
38
38
|
* bubble is rolled back and the text is restored into the composer, so a
|
|
39
|
-
* backend without steering support degrades gracefully.
|
|
40
|
-
*
|
|
39
|
+
* backend without steering support degrades gracefully. A new chat is
|
|
40
|
+
* created (`POST {sessions}`) before its first message, so a steer can name
|
|
41
|
+
* the first turn's conversation: the stream names it only when the turn
|
|
42
|
+
* ends. Set to null to disable mid-run steering entirely, and that create
|
|
43
|
+
* with it.
|
|
41
44
|
*/
|
|
42
45
|
steer?: string | null;
|
|
43
46
|
/**
|
|
@@ -471,7 +474,7 @@ interface ChatPanelProps {
|
|
|
471
474
|
onDownloadError?: (error: unknown, attachment: ChatAttachment) => void;
|
|
472
475
|
/** Maximum number of files attachable in one chat context. Default: 10. */
|
|
473
476
|
maxFileCount?: number;
|
|
474
|
-
/** Maximum total size in bytes for attached files. Default: 50 * 1024 * 1024 (50 MB). */
|
|
477
|
+
/** Maximum total size in bytes for attached files; `Infinity` for none. Default: 50 * 1024 * 1024 (50 MB). */
|
|
475
478
|
maxTotalSize?: number;
|
|
476
479
|
/** Maximum size in bytes of one attached file. Default: none (only `maxTotalSize`). */
|
|
477
480
|
maxFileSize?: number;
|
|
@@ -615,11 +618,21 @@ interface ChatPanelProps {
|
|
|
615
618
|
* is a new chat. Left `undefined`, the panel remembers its own.
|
|
616
619
|
*/
|
|
617
620
|
conversationId?: string | null;
|
|
618
|
-
/**
|
|
619
|
-
|
|
621
|
+
/**
|
|
622
|
+
* Called whenever the conversation on screen changes, whoever changed it.
|
|
623
|
+
* `created` is true for a conversation the panel has just created from a new
|
|
624
|
+
* chat (its first message or file), false for one opened or left (the list, a
|
|
625
|
+
* reload, a new chat) and for one the backend put in place of a deleted one
|
|
626
|
+
* that was opened.
|
|
627
|
+
*/
|
|
628
|
+
onConversationChange?: (conversationId: string | null, change: ChatConversationChange) => void;
|
|
620
629
|
/** Agent selected once the list loads, by id or slug, over the remembered one. */
|
|
621
630
|
initialAgent?: string;
|
|
622
|
-
/**
|
|
631
|
+
/**
|
|
632
|
+
* Called when another agent is picked, in the menu or by opening a
|
|
633
|
+
* conversation of another agent (the picker follows the conversation on
|
|
634
|
+
* screen, so a new chat starts with its agent).
|
|
635
|
+
*/
|
|
623
636
|
onAgentChange?: (agent: XtmAgent) => void;
|
|
624
637
|
/** Hides the composer: a conversation the user may read but not write to. */
|
|
625
638
|
readOnly?: boolean;
|
|
@@ -682,6 +695,18 @@ interface ChatPanelProps {
|
|
|
682
695
|
welcome?: React.ReactNode;
|
|
683
696
|
/** Rendered under a message's content, before its footer; `context.streaming` while the answer is still coming. */
|
|
684
697
|
renderMessageExtras?: (message: ChatMessage, context: ChatMessageSlotContext) => React.ReactNode;
|
|
698
|
+
/**
|
|
699
|
+
* Draws a message in its own way: `undefined` (or a boolean, as
|
|
700
|
+
* `isCut(m) && <Card />` gives) leaves it to the panel, `null` leaves it
|
|
701
|
+
* out, and anything else is drawn in its place, without the message's frame
|
|
702
|
+
* (its name, time and actions). For a host that folds several messages into
|
|
703
|
+
* one of its own (a turn cut and resumed, drawn as one card on the first of
|
|
704
|
+
* them, the others left out). Asked of every message, streaming ones
|
|
705
|
+
* included, the reply's empty placeholder too (`null` leaves out its status
|
|
706
|
+
* bubble): return `undefined` fast for the others. A throw, while asked or
|
|
707
|
+
* while the replacement is drawn, leaves that message to the panel.
|
|
708
|
+
*/
|
|
709
|
+
replaceMessage?: (message: ChatMessage, context: ChatMessageSlotContext) => React.ReactNode;
|
|
685
710
|
/** Controls added to a finished message's footer. */
|
|
686
711
|
messageFooterActions?: (message: ChatMessage, context: ChatMessageSlotContext) => React.ReactNode;
|
|
687
712
|
/** Controls added to a conversation's row in the conversation list. */
|
|
@@ -805,6 +830,26 @@ interface ChatPanelProps {
|
|
|
805
830
|
* `sidebar*` slots render nothing meanwhile. Default: false.
|
|
806
831
|
*/
|
|
807
832
|
hideConversationList?: boolean;
|
|
833
|
+
/**
|
|
834
|
+
* The conversation list starts collapsed to its strip, from which the user
|
|
835
|
+
* opens it: where it would squeeze the thread, a phone or a narrow window.
|
|
836
|
+
* Read once, when the panel mounts. Default: false.
|
|
837
|
+
*/
|
|
838
|
+
initialConversationListCollapsed?: boolean;
|
|
839
|
+
/**
|
|
840
|
+
* The workspace groups of the conversation list the user has opened (their
|
|
841
|
+
* keys: a workspace's id), for a host that keeps them per user. Given, every
|
|
842
|
+
* other group starts collapsed until the user opens it, so many workspaces
|
|
843
|
+
* do not push the conversations in none below the fold; one the user creates
|
|
844
|
+
* from the list opens. Read once, the first time it is given: a host still
|
|
845
|
+
* reading them passes `undefined` until it has them. The panel keeps what
|
|
846
|
+
* the user opens from then on, while the list is hidden and shown again too.
|
|
847
|
+
* Unset, groups start open and the ones the user collapses are remembered
|
|
848
|
+
* per browser.
|
|
849
|
+
*/
|
|
850
|
+
expandedWorkspaceGroups?: string[];
|
|
851
|
+
/** The workspace groups the user has opened, each time they open or collapse one (see `expandedWorkspaceGroups`). */
|
|
852
|
+
onExpandedWorkspaceGroupsChange?: (keys: string[]) => void;
|
|
808
853
|
/**
|
|
809
854
|
* Asked before a conversation is filed into a workspace (`null`: taken out
|
|
810
855
|
* of its workspace), by a drag or the row's Move menu: `false` (or a promise
|
|
@@ -826,14 +871,18 @@ interface ChatPanelProps {
|
|
|
826
871
|
sidebarSearchThreshold?: number;
|
|
827
872
|
/**
|
|
828
873
|
* Names the channel a conversation was started in, on its row (`Started in
|
|
829
|
-
* {channel}`): a string names it, `null` shows nothing, `undefined` keeps
|
|
874
|
+
* {channel}`) and in the header (`Agent · {channel}`): a string names it, `null` shows nothing, `undefined` keeps
|
|
830
875
|
* the panel's own name (Slack, Teams, WhatsApp, Telegram, Discord, Google
|
|
831
876
|
* Chat; no other channel is shown).
|
|
832
877
|
*/
|
|
833
878
|
channelLabel?: (channel: string) => string | null | undefined;
|
|
834
879
|
/** A title beside the agent's name in the header (the conversation's, say), or a function of what is on screen. */
|
|
835
880
|
headerTitle?: React.ReactNode | ((context: ChatHeaderContext) => React.ReactNode);
|
|
836
|
-
/**
|
|
881
|
+
/**
|
|
882
|
+
* A line under the agent's name in the header, or a function of what is on
|
|
883
|
+
* screen. Undefined: `Agent · {channel}` for a conversation started in a
|
|
884
|
+
* channel its row names (see `channelLabel`), nothing otherwise.
|
|
885
|
+
*/
|
|
837
886
|
headerSubtitle?: React.ReactNode | ((context: ChatHeaderContext) => React.ReactNode);
|
|
838
887
|
/**
|
|
839
888
|
* Links the header's agent name to this address (an http(s) URL or a path
|
|
@@ -877,6 +926,11 @@ interface ChatMarkerRenderer {
|
|
|
877
926
|
renderWhileStreaming?: boolean;
|
|
878
927
|
/** Give it the thread's full width rather than the prose's. */
|
|
879
928
|
fullWidth?: boolean;
|
|
929
|
+
/**
|
|
930
|
+
* An answer that is this marker and nothing else shows the card alone, as a
|
|
931
|
+
* note between messages: no agent name, time or actions around it.
|
|
932
|
+
*/
|
|
933
|
+
standalone?: boolean;
|
|
880
934
|
}
|
|
881
935
|
/** What a code block renderer is drawn from. */
|
|
882
936
|
interface ChatCodeBlockContext {
|
|
@@ -906,6 +960,11 @@ interface ChatStreamEventInfo {
|
|
|
906
960
|
/** The frame acknowledges a turn replayed from its start (`thinking` with `resumed`). */
|
|
907
961
|
replayed?: boolean;
|
|
908
962
|
}
|
|
963
|
+
/** Why the conversation on screen changed (see `onConversationChange`). */
|
|
964
|
+
interface ChatConversationChange {
|
|
965
|
+
/** The panel has just created this conversation from a new chat; false when it was opened, replaced or left. */
|
|
966
|
+
created: boolean;
|
|
967
|
+
}
|
|
909
968
|
/** What a host can do to a mounted panel, through its `ref`. */
|
|
910
969
|
interface ChatPanelHandle {
|
|
911
970
|
/** Send *text* as the user, leaving the composer's own draft alone; resolves to what became of it. */
|
|
@@ -925,6 +984,12 @@ interface ChatPanelHandle {
|
|
|
925
984
|
*/
|
|
926
985
|
getDraft: () => string;
|
|
927
986
|
focusComposer: () => void;
|
|
987
|
+
/**
|
|
988
|
+
* Put *files* in the composer's tray as if the user picked them (uploaded and
|
|
989
|
+
* checked the same way), to go with the next message: files another app
|
|
990
|
+
* shared to the host. Nothing where the panel takes no files.
|
|
991
|
+
*/
|
|
992
|
+
attachFiles: (files: readonly File[]) => void;
|
|
928
993
|
/** Same as the Stop button; also cancels the question a voice call awaits, and stops it speaking. */
|
|
929
994
|
stop: () => void;
|
|
930
995
|
/**
|
|
@@ -1349,6 +1414,12 @@ interface XtmAgent {
|
|
|
1349
1414
|
group?: string;
|
|
1350
1415
|
/** Short labels shown after its name in the agent picker (`Company`), already translated. */
|
|
1351
1416
|
badges?: string[];
|
|
1417
|
+
/**
|
|
1418
|
+
* The agent is meant for assignments rather than chat (`disable_chat`): the
|
|
1419
|
+
* picker says "Not recommended for chat" and the composer warns while it is
|
|
1420
|
+
* the one written to. It stays selectable.
|
|
1421
|
+
*/
|
|
1422
|
+
disableChat?: boolean;
|
|
1352
1423
|
}
|
|
1353
1424
|
|
|
1354
1425
|
/**
|
|
@@ -1369,4 +1440,4 @@ interface TransferredAgent {
|
|
|
1369
1440
|
declare const voiceCallSupported: () => boolean;
|
|
1370
1441
|
|
|
1371
1442
|
export { ChatPanel, ChatToggleButton, voiceCallSupported };
|
|
1372
|
-
export type { AgentStatusState, ApiEndpoints, BackendType, ChatAnsweredBy, ChatArtifact, ChatArtifactKind, ChatAttachment, ChatCodeBlockContext, ChatCodeBlockRenderer, ChatContextBreakdown, ChatContextUsage, ChatConversationAction, ChatConversationRef, ChatConversationReferenceCandidate, ChatConversationSummary, ChatFile, ChatFileRefusal, ChatHeaderContext, ChatHeldOutcome, ChatMarkerContext, ChatMarkerRenderer, ChatMessage, ChatMessagePersistedFeedback, ChatMessageSlotContext, ChatMode, ChatPanelHandle, ChatPanelMode, ChatPanelProps, ChatPanelTheme, ChatPromptTemplate, ChatQuotaStatus, ChatSendDecision, ChatSendDraft, ChatSendOptions, ChatSendOutcome, ChatSidebarSearch, ChatStreamEventInfo, ChatToggleButtonProps, ChatToolLabels, ChatTurnEnd, ChatTurnOutcome, ChatWorkspace, ConsultStatus, MessageFeedback, ToolApprovalDecision, ToolApprovalProposal, ToolApprovalVerdict, TransferredAgent, Translate, XtmAgent };
|
|
1443
|
+
export type { AgentStatusState, ApiEndpoints, BackendType, ChatAnsweredBy, ChatArtifact, ChatArtifactKind, ChatAttachment, ChatCodeBlockContext, ChatCodeBlockRenderer, ChatContextBreakdown, ChatContextUsage, ChatConversationAction, ChatConversationChange, ChatConversationRef, ChatConversationReferenceCandidate, ChatConversationSummary, ChatFile, ChatFileRefusal, ChatHeaderContext, ChatHeldOutcome, ChatMarkerContext, ChatMarkerRenderer, ChatMessage, ChatMessagePersistedFeedback, ChatMessageSlotContext, ChatMode, ChatPanelHandle, ChatPanelMode, ChatPanelProps, ChatPanelTheme, ChatPromptTemplate, ChatQuotaStatus, ChatSendDecision, ChatSendDraft, ChatSendOptions, ChatSendOutcome, ChatSidebarSearch, ChatStreamEventInfo, ChatToggleButtonProps, ChatToolLabels, ChatTurnEnd, ChatTurnOutcome, ChatWorkspace, ConsultStatus, MessageFeedback, ToolApprovalDecision, ToolApprovalProposal, ToolApprovalVerdict, TransferredAgent, Translate, XtmAgent };
|