@filigran/chatbot 3.18.0 → 3.19.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 +170 -2
- package/README.md +35 -8
- package/dist/index.d.ts +152 -3
- 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/dist/workspaces.d.ts +4 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,7 +8,175 @@ 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.19.0 — unreleased
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `collapseUnnamedFiles`: the files a reply carries but whose markers it does
|
|
16
|
+
not name go behind one collapsed `Working files ({count})` line, all of them
|
|
17
|
+
(`true`) or those a predicate picks, instead of one card each. Opened, each
|
|
18
|
+
is listed with its card, after a thumbnail for a picture (fetched only once
|
|
19
|
+
the list opens; the picture icon when there is no URL, the fetch fails or
|
|
20
|
+
the bytes do not decode), and from 8 files a `Show less` closes it from its
|
|
21
|
+
end. Opened while following the end, it stops following once it pushes the
|
|
22
|
+
end out of view, so the list stays in view. Off unless the host asks.
|
|
23
|
+
- `renderReasoningAction`: a host's action in the corner of the reasoning
|
|
24
|
+
window, shown while the window is and gone with it.
|
|
25
|
+
- An image's lightbox has a Download action when the panel can download the
|
|
26
|
+
file, so a user's uploaded picture, which replaces its download card, can be
|
|
27
|
+
downloaded.
|
|
28
|
+
- A `Scroll to bottom` button shows once the reader has scrolled up, and
|
|
29
|
+
brings the view back to the end, following it again.
|
|
30
|
+
- Older messages load as the reader scrolling up nears the top, a second at
|
|
31
|
+
least after the last load ended, never while the view follows the end, and
|
|
32
|
+
only where the button would load them. A load that brings nothing (the
|
|
33
|
+
backend failing) stops them until the conversation is opened again; the
|
|
34
|
+
button still tries again. The button reads `Loading earlier messages…` while they load.
|
|
35
|
+
- `maxFileSize` limits each attached file, next to `maxTotalSize` (none by
|
|
36
|
+
default). A file refused, whichever limit it went past (`maxFileSize`,
|
|
37
|
+
`maxTotalSize`, `maxFileCount`), is named above the text field with why; it
|
|
38
|
+
was dropped without a word. `onFileRefused(file, reason)` tells the host too
|
|
39
|
+
(`ChatFileRefusal`).
|
|
40
|
+
- `acceptFileTypes` sets the file picker's `accept`, and refuses a pasted or
|
|
41
|
+
dropped file of another type (`'file-type'`). A file the browser gives no
|
|
42
|
+
MIME type (Chrome on Windows for `.md`, `.yaml`) is judged by the type its
|
|
43
|
+
extension stands for, and refused by a MIME token when it stands for none.
|
|
44
|
+
- `placeholder` replaces "Ask a question...": a text, or a function of the
|
|
45
|
+
agent selected.
|
|
46
|
+
- `onBeforeSend`'s draft says how many files the message carries
|
|
47
|
+
(`fileCount`).
|
|
48
|
+
- The composer takes the focus after a send from it and after a switch to
|
|
49
|
+
another conversation or a new one, unless the focus is in a field, a dialog
|
|
50
|
+
or the host's page; opening the panel takes none.
|
|
51
|
+
- `onBeforeMove(conversation, workspace)` is asked before a drag or a row's
|
|
52
|
+
Move menu files a conversation into a workspace (`workspace` is `null` when
|
|
53
|
+
taking it out of one): `false`, or a promise of it, leaves the conversation
|
|
54
|
+
where it was, e.g. once the user declined to share it with a workspace's
|
|
55
|
+
members. One that throws or rejects refuses the move.
|
|
56
|
+
- `onConversationActionFailed(action, conversation, error)` is told when
|
|
57
|
+
deleting, renaming, moving or clearing conversations fails (`'delete'`,
|
|
58
|
+
`'rename'`, `'move'`, `'clearAll'`), with the backend's `detail` when it
|
|
59
|
+
gave one; returning `true` keeps the panel's own notice from showing.
|
|
60
|
+
- A row of the conversation list shows the last message
|
|
61
|
+
(`last_message_preview`) under its title, and a small icon for a
|
|
62
|
+
conversation started in a messaging channel (`channel`), named `Started in
|
|
63
|
+
{channel}`: Slack, Teams, WhatsApp, Telegram, Discord and Google Chat out
|
|
64
|
+
of the box; `channelLabel(channel)` names another, or hides one (`null`).
|
|
65
|
+
`ChatConversationSummary` gains the optional `lastMessagePreview` and
|
|
66
|
+
`channel`.
|
|
67
|
+
- `sidebarSearchThreshold` sets how many conversations the list holds before
|
|
68
|
+
its search box shows (default 7, as before; `0` shows it from the first).
|
|
69
|
+
- `headerTitle` and `headerSubtitle`, a node or a function of what is on
|
|
70
|
+
screen (`ChatHeaderContext`: the conversation as the list holds it, and the
|
|
71
|
+
agent), show a title beside the header's agent name and a line under it.
|
|
72
|
+
- `agentHref(agent)` makes the header's agent name a link, followed in place
|
|
73
|
+
(an http(s) URL or a path of the host's origin), and `onAgentNameClick(agent)`
|
|
74
|
+
takes a plain click on it; the chevron beside the name opens the agent
|
|
75
|
+
picker then.
|
|
76
|
+
- `clearAllAction: 'archive'` words Clear all as an archive (`Archive all
|
|
77
|
+
conversations`).
|
|
78
|
+
- `XtmAgent.badges`: short labels the agents list may send (`badges`, strings
|
|
79
|
+
only), shown after the agent's name in the agent picker.
|
|
80
|
+
- `toolLabels`: the host's names for its tools, by raw name or as a function
|
|
81
|
+
of it, in the status line (a consult's tools too) and the reasoning details.
|
|
82
|
+
A name it does not give keeps the built-in one.
|
|
83
|
+
- `sidebarSections` also takes a function of the conversation list's search,
|
|
84
|
+
`(search: ChatSidebarSearch) => ReactNode`, given what the search box holds,
|
|
85
|
+
trimmed (`''` when empty). It is drawn again when the search or the function
|
|
86
|
+
changes, so a host passes a stable one, and puts hooks in a component the
|
|
87
|
+
function returns. With it the search box shows whatever the number of
|
|
88
|
+
conversations, and a search no conversation of the user's own matches says
|
|
89
|
+
"None of your conversations matches". Sections given as an element show as
|
|
90
|
+
they are.
|
|
91
|
+
|
|
92
|
+
### Fixed
|
|
93
|
+
|
|
94
|
+
- The view keeps the last line in view when the thread's own box shrinks
|
|
95
|
+
while following the end (the composer growing to several lines, a tray
|
|
96
|
+
opening); only growth inside the thread was followed.
|
|
97
|
+
- Earlier messages already loaded, shown by the button, leave the reader
|
|
98
|
+
where they were in every browser, as older messages fetched do.
|
|
99
|
+
- A failed upload's chip says why: the upload's `detail`
|
|
100
|
+
(`ChatFile.uploadError`), or "Upload failed". It showed a bare ✕.
|
|
101
|
+
- A message holding a file that failed to upload is not sent until the user
|
|
102
|
+
removes the file, turn or no turn; with no turn running it went without the
|
|
103
|
+
file, and nothing said so.
|
|
104
|
+
- Files attached before a host turns `disableFileManagement` on stay shown
|
|
105
|
+
and removable; they were hidden, yet still sent, or a failed one held every
|
|
106
|
+
send without a word.
|
|
107
|
+
- Deleting, renaming or clearing conversations failed silently: the panel now
|
|
108
|
+
says it under the conversation list, or under the header when the list is
|
|
109
|
+
not shown, in the backend's words when it gives some. A refused rename
|
|
110
|
+
still keeps the old title, and now undoes only that row: other changes to
|
|
111
|
+
the list made meanwhile stay.
|
|
112
|
+
- The conversation list's search matches a conversation's last message and
|
|
113
|
+
its agent's name too, not its title alone.
|
|
114
|
+
- A message sent in a conversation moves it to the top of its section at
|
|
115
|
+
once, with the time and its text as the preview; it stayed where it was
|
|
116
|
+
until the turn ended and the list was read again.
|
|
117
|
+
- A read-only panel showed the welcome screen's "Suggestions" heading over no
|
|
118
|
+
suggestions: nothing can be sent there, so the section is left out.
|
|
119
|
+
- The status line says what the turn does where it said `Thinking...`:
|
|
120
|
+
`{agent} is answering…` for each mentioned agent of a turn, with that
|
|
121
|
+
agent's name before the statuses of its run, in the voice call too
|
|
122
|
+
(`AgentStatusState.answeringAgentName`);
|
|
123
|
+
`Transferring to {agent}…` on `transfer_start`; the setup step of
|
|
124
|
+
`preparing` by its `phase` (`Loading conversation history…`, `Summarizing
|
|
125
|
+
earlier conversation…`, `Recalling memory…`, `Connecting integrations…`,
|
|
126
|
+
`Preparing tools…`, else `Preparing…`); `Compacting conversation context…`;
|
|
127
|
+
`Saving response…` on `persisting`; and `Analyzing results (step {step})…`
|
|
128
|
+
from the frame's `iteration` after the first step. A host's `statusLabel`
|
|
129
|
+
reads them as `AgentStatusState.agentName`, `phase` and `iteration`.
|
|
130
|
+
- While mentioned agents answer, a message does not steer: it would reach
|
|
131
|
+
whichever of them runs. It waits for the turn, held with
|
|
132
|
+
`holdSendsDuringTurn`, as a message that cannot steer does.
|
|
133
|
+
- A stored answer the server's restart cut short (`interrupted: true`,
|
|
134
|
+
`ChatMessage.interrupted`) is marked as a truncated one is, and its reasoning
|
|
135
|
+
details say why.
|
|
136
|
+
- A finished answer with no text and no files says `No response was
|
|
137
|
+
generated. Please try again.`; it showed `...`.
|
|
138
|
+
- A paste from an office suite or a spreadsheet (Excel, Word, PowerPoint,
|
|
139
|
+
OneNote, LibreOffice, Numbers) puts the copied text in the composer. These
|
|
140
|
+
apps copy a selection as its text, its HTML and a picture of it, and the
|
|
141
|
+
panel attached the picture and dropped the text. A paste whose files are all
|
|
142
|
+
images, with plain text and an HTML fragment that shows text, attaches
|
|
143
|
+
nothing and reaches the text box; a screenshot, a browser's "Copy image" and
|
|
144
|
+
a file copied in a file manager still attach. A picture copied together with
|
|
145
|
+
text around it (an image and its caption in Word or Outlook) is read the same
|
|
146
|
+
way: the text is pasted, the picture is not attached; copy the picture alone
|
|
147
|
+
to attach it.
|
|
148
|
+
- Enter while an input method composes a word (Chinese, Japanese, Korean)
|
|
149
|
+
confirms the word instead of sending the half-typed message, renaming a
|
|
150
|
+
conversation or naming a workspace, and Escape then cancels the composition
|
|
151
|
+
without stopping the turn, closing the field or clearing a search.
|
|
152
|
+
- A file dropped on the panel where it attaches none (a `readOnly` panel, one
|
|
153
|
+
with `disableFileManagement` or no upload route, or the header and the
|
|
154
|
+
conversation list of any panel) leaves the page where it is; the browser
|
|
155
|
+
opened it in the tab, away from the chat. A host's own drop zone around the
|
|
156
|
+
panel still receives the drop, and a picture or link dragged from a page
|
|
157
|
+
still goes into a text field as its address.
|
|
158
|
+
- The composer's draft is written when the page goes away (`pagehide`) or is
|
|
159
|
+
hidden, so a reload right after typing keeps it; a keystroke within the
|
|
160
|
+
300 ms the draft waits before being written was lost. Two panels on one
|
|
161
|
+
conversation write only the draft typed in them, so neither erases the
|
|
162
|
+
other's.
|
|
163
|
+
- A search box hidden as the list shrinks below its threshold clears its
|
|
164
|
+
query: the list stayed filtered by a search the user could neither see nor
|
|
165
|
+
clear.
|
|
166
|
+
|
|
167
|
+
## 3.18.1 — unreleased
|
|
168
|
+
|
|
169
|
+
### Fixed
|
|
170
|
+
|
|
171
|
+
- A message sent, or a file attached, before the panel's agents have loaded
|
|
172
|
+
goes to the agent the host asked for (`initialAgent`); it went with no agent
|
|
173
|
+
before, and the backend answered with the user's default one. The message
|
|
174
|
+
shows at once and leaves when the agents land, whether from the composer,
|
|
175
|
+
the handle or a voice call, and Stop lets it go meanwhile. One from the
|
|
176
|
+
composer or a voice call does not wait for a history still being read back;
|
|
177
|
+
the handle's `send` still does.
|
|
178
|
+
|
|
179
|
+
## 3.18.0 — 2026-10-31
|
|
12
180
|
|
|
13
181
|
### Added
|
|
14
182
|
|
|
@@ -28,7 +196,7 @@ This file starts at 3.10.0; earlier releases are on the
|
|
|
28
196
|
consult as `AgentStatusState.consult` (`ConsultStatus`); a host translating
|
|
29
197
|
the panel's keys translates the new ones too.
|
|
30
198
|
|
|
31
|
-
## 3.17.0 —
|
|
199
|
+
## 3.17.0 — published within 3.18.0
|
|
32
200
|
|
|
33
201
|
### Added
|
|
34
202
|
|
package/README.md
CHANGED
|
@@ -92,6 +92,10 @@ import { ChatPanel } from '@filigran/chatbot';
|
|
|
92
92
|
| `disableFileManagement` | `boolean` | `false` | No attaching or pasting files in the composer |
|
|
93
93
|
| `maxFileCount` | `number` | `10` | Most files attached to one message |
|
|
94
94
|
| `maxTotalSize` | `number` | 50 MB | Most bytes the files attached to one message add up to |
|
|
95
|
+
| `maxFileSize` | `number` | — | Most bytes one attached file may hold; none of its own by default, only `maxTotalSize` |
|
|
96
|
+
| `acceptFileTypes` | `string` | — | The file types the composer takes, in the file input's `accept` form (`'.pdf,image/*'`): the picker offers only these, and a pasted or dropped file of another type is refused. A file the browser gives no MIME type (Chrome on Windows for `.md`, `.yaml`) is judged by the type its extension stands for (`.md`, `.markdown`, `.yaml`, `.yml`, `.csv`, `.tsv`, `.txt`, `.log`, `.json`), and refused by a MIME token when it stands for none. Any type by default |
|
|
97
|
+
| `onFileRefused` | `(file: File, reason: ChatFileRefusal) => void` | — | A file the composer refused: `'file-size'` (over `maxFileSize`), `'total-size'` (past `maxTotalSize`), `'file-count'` (past `maxFileCount`) or `'file-type'` (not of `acceptFileTypes`). The composer names the file and says why above the text field either way |
|
|
98
|
+
| `placeholder` | `string \| ((agent: XtmAgent \| null) => string)` | `'Ask a question...'` | The composer's placeholder, or one for the agent selected (`null` until the agents load); used as given, so the host translates it |
|
|
95
99
|
| `onMessageFeedback` | `(id, feedback, message) => void` | — | Receives each rating of an answer (`null` clears it); `id` is the message's persisted id when the backend reported one (`done.message_id`, a restored entry's `id`), else the panel's own. On its own it enables 👍/👎 and the panel stores nothing; with `apiEndpoints.feedback` set it is called once the backend has stored the rating. With neither, no thumbs. |
|
|
96
100
|
| `locale` | `string` | browser's | BCP 47 language tag of the platform's language (e.g. `'fr-FR'`): the language of the panel's catalog for what `t` does not translate, of its message times and of read-aloud. Unset, the browser's — except that a host passing `t` gets no catalog then |
|
|
97
101
|
| `disableImagePreviews` | `boolean` | `false` | Render image attachments as download cards instead of inline previews |
|
|
@@ -116,10 +120,14 @@ import { ChatPanel } from '@filigran/chatbot';
|
|
|
116
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 |
|
|
117
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) |
|
|
118
122
|
| `sidebarRowActions` | `(conversation) => React.ReactNode` | — | The host's controls on a row of the conversation list |
|
|
119
|
-
| `sidebarHeaderActions`
|
|
123
|
+
| `sidebarHeaderActions` | `React.ReactNode` | — | The host's controls beside New conversation |
|
|
124
|
+
| `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 |
|
|
120
125
|
| `statusLabel` | `(status: AgentStatusState) => string \| undefined` | — | The status line's text for a status, over the built-in one |
|
|
126
|
+
| `toolLabels` | `Record<string, string> \| ((toolName: string) => string \| undefined)` | — | Tool names as the status line and the reasoning details show them, by the tool's raw name; a name it does not give keeps the built-in one (`search_iocs` → `Search Iocs`). A new function redraws every message: keep one across renders unless its labels change |
|
|
121
127
|
| `waitingMessages` | `string[]` | built-in | The waiting panel's rotating messages, already translated |
|
|
122
128
|
| `renderWaitingPanel` | `() => React.ReactNode` | — | Replaces the waiting panel's content (its messages and the invitation to play) |
|
|
129
|
+
| `renderReasoningAction` | `() => React.ReactNode` | — | An action in the reasoning window's corner (a game to play while the agent works), shown while the window is |
|
|
130
|
+
| `collapseUnnamedFiles` | `boolean \| (attachment, message) => boolean` | `false` | Puts the files a reply does not name behind one collapsed `Working files (N)` line, with a thumbnail for each picture once opened: all of them, or those the predicate picks |
|
|
123
131
|
| `fontFamily` | `string` | — | Font of the panel's text |
|
|
124
132
|
| `theme` | `ChatPanelTheme` | — | The host's colours, type and shapes; see [Custom Theme](#custom-theme) |
|
|
125
133
|
| `agentMentions` | `boolean` | `false` | `@` also mentions agents of the agent list; the message carries their ids as `mentioned_agent_ids` |
|
|
@@ -130,6 +138,14 @@ import { ChatPanel } from '@filigran/chatbot';
|
|
|
130
138
|
| `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) |
|
|
131
139
|
| `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) |
|
|
132
140
|
| `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
|
+
| `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
|
+
| `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
|
+
| `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 row's conversation was started in (`Started in {channel}`); `null` shows nothing, `undefined` keeps the panel's names (Slack, Teams, WhatsApp, Telegram, Discord, Google Chat) |
|
|
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 |
|
|
146
|
+
| `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
|
+
| `onAgentNameClick` | `(agent: XtmAgent) => void` | — | Takes a plain click on the header's agent name (over `agentHref`); the chevron beside it opens the picker |
|
|
148
|
+
| `clearAllAction` | `'delete' \| 'archive'` | `'delete'` | What Clear all does, for its wording (`Archive all conversations`) |
|
|
133
149
|
|
|
134
150
|
Plain-data props (`requestHeaders`, `apiEndpoints`, `pageContext`, `promptSuggestions`) are compared by content and callback props are called
|
|
135
151
|
through a stable wrapper, so building them inline is fine. `t` and `logoIcon` are used as given: keep them stable (`useCallback` / `useMemo`),
|
|
@@ -224,8 +240,9 @@ them stable, and a new one re-renders every message, which is also how to
|
|
|
224
240
|
refresh what they render.
|
|
225
241
|
|
|
226
242
|
`onBeforeSend(draft)` sees every non-empty send first, synchronously:
|
|
227
|
-
`draft.text`, `draft.source` (`'composer'`, `'host'` or `'voice'`)
|
|
228
|
-
`draft.turnRunning` (a turn is running)
|
|
243
|
+
`draft.text`, `draft.source` (`'composer'`, `'host'` or `'voice'`),
|
|
244
|
+
`draft.turnRunning` (a turn is running) and `draft.fileCount` (the files the
|
|
245
|
+
message carries), e.g. to refuse a send whose files the host cannot use. It returns `false` to take the send
|
|
229
246
|
over (the composer keeps its draft), or an object: `text` rewords what the
|
|
230
247
|
backend receives, `displayText` is what the user's bubble shows, and `extras`
|
|
231
248
|
adds body fields. The panel's own fields cannot be set this way, and a message
|
|
@@ -439,11 +456,15 @@ Returns available AI agents.
|
|
|
439
456
|
"name": "General Assistant",
|
|
440
457
|
"slug": "general",
|
|
441
458
|
"icon": null,
|
|
442
|
-
"description": "A helpful general-purpose assistant"
|
|
459
|
+
"description": "A helpful general-purpose assistant",
|
|
460
|
+
"badges": ["Company"]
|
|
443
461
|
}
|
|
444
462
|
]
|
|
445
463
|
```
|
|
446
464
|
|
|
465
|
+
`badges` is optional: short labels, already translated, shown after the
|
|
466
|
+
agent's name in the agent picker. Anything but strings is ignored.
|
|
467
|
+
|
|
447
468
|
### `POST {apiBaseUrl}/chat/sessions`
|
|
448
469
|
|
|
449
470
|
Restores conversation history (and, implicitly, resolves the session).
|
|
@@ -535,11 +556,17 @@ Lists the user's past conversations for the history menu in the chat header.
|
|
|
535
556
|
"conversation_id": "uuid-here",
|
|
536
557
|
"title": "What is the weather?",
|
|
537
558
|
"updated_at": "2026-06-10T08:30:00Z",
|
|
538
|
-
"message_count": 12
|
|
559
|
+
"message_count": 12,
|
|
560
|
+
"last_message_preview": "It is sunny in Paris.",
|
|
561
|
+
"channel": "slack"
|
|
539
562
|
}
|
|
540
563
|
]
|
|
541
564
|
```
|
|
542
565
|
|
|
566
|
+
`last_message_preview` and `channel` are optional: a row shows the preview
|
|
567
|
+
under its title, and names a messaging channel the conversation was started
|
|
568
|
+
in (see `channelLabel`).
|
|
569
|
+
|
|
543
570
|
Selecting a conversation restores it through the existing
|
|
544
571
|
`POST /chat/sessions` contract above. The endpoint is fetched lazily when the
|
|
545
572
|
history menu opens; a backend that doesn't implement it yet (404/405) simply
|
|
@@ -808,9 +835,9 @@ mentions or body fields of its own (`send`'s `extras`, `onBeforeSend`'s
|
|
|
808
835
|
`extras`) never steers, since a steer reaches the agent as text alone: it waits
|
|
809
836
|
for the turn to end (`holdSendsDuringTurn`), and `onBeforeSend` is asked again,
|
|
810
837
|
with `turnRunning: false`, before it goes out. A file that failed to upload
|
|
811
|
-
neither steers nor waits
|
|
812
|
-
|
|
813
|
-
|
|
838
|
+
neither steers nor waits, nor goes without that file: its chip says why (the
|
|
839
|
+
upload's `detail`), and the composer offers no send, and says why, until the
|
|
840
|
+
user removes it. `send` and a voice
|
|
814
841
|
call attach no files, so this never holds them. Esc stops generating.
|
|
815
842
|
|
|
816
843
|
#### Multi-segment responses
|
package/dist/index.d.ts
CHANGED
|
@@ -473,6 +473,30 @@ interface ChatPanelProps {
|
|
|
473
473
|
maxFileCount?: number;
|
|
474
474
|
/** Maximum total size in bytes for attached files. Default: 50 * 1024 * 1024 (50 MB). */
|
|
475
475
|
maxTotalSize?: number;
|
|
476
|
+
/** Maximum size in bytes of one attached file. Default: none (only `maxTotalSize`). */
|
|
477
|
+
maxFileSize?: number;
|
|
478
|
+
/**
|
|
479
|
+
* The file types the composer takes, in the file input's `accept` form
|
|
480
|
+
* (`'.pdf,image/*'`): the picker offers only these, and a pasted or dropped
|
|
481
|
+
* file of another type is refused. A file the browser gives no MIME type
|
|
482
|
+
* (Chrome on Windows for `.md`, `.yaml`) is judged by the type its extension
|
|
483
|
+
* stands for (`.md`, `.markdown`, `.yaml`, `.yml`, `.csv`, `.tsv`, `.txt`,
|
|
484
|
+
* `.log`, `.json`), and refused by a MIME token when it stands for none.
|
|
485
|
+
* Default: any type.
|
|
486
|
+
*/
|
|
487
|
+
acceptFileTypes?: string;
|
|
488
|
+
/**
|
|
489
|
+
* A file the user attached that the composer refused, and why. The composer
|
|
490
|
+
* names it and says why above the text field, whether or not the host
|
|
491
|
+
* listens.
|
|
492
|
+
*/
|
|
493
|
+
onFileRefused?: (file: File, reason: ChatFileRefusal) => void;
|
|
494
|
+
/**
|
|
495
|
+
* The composer's placeholder, over "Ask a question...": a text, or one for
|
|
496
|
+
* the agent selected (`null` until the agents load). Used as given, so the
|
|
497
|
+
* host translates it.
|
|
498
|
+
*/
|
|
499
|
+
placeholder?: string | ((agent: XtmAgent | null) => string);
|
|
476
500
|
/** Additional HTTP headers added to chatbot API requests (messages, sessions, agents, uploads). */
|
|
477
501
|
requestHeaders?: Record<string, string>;
|
|
478
502
|
/**
|
|
@@ -664,14 +688,42 @@ interface ChatPanelProps {
|
|
|
664
688
|
sidebarRowActions?: (conversation: ChatConversationSummary) => React.ReactNode;
|
|
665
689
|
/** Controls added beside New conversation, atop the conversation list. */
|
|
666
690
|
sidebarHeaderActions?: React.ReactNode;
|
|
667
|
-
/**
|
|
668
|
-
|
|
691
|
+
/**
|
|
692
|
+
* Sections added under the conversation list (conversations shared with the
|
|
693
|
+
* user). A function draws them from the list's search, so they filter on
|
|
694
|
+
* what is typed, as the list does. It is drawn again only when the search or
|
|
695
|
+
* the function changes: pass a stable one (`useCallback`). Hooks go in a
|
|
696
|
+
* component it returns (`({ query }) => <Shared query={query} />`): hook
|
|
697
|
+
* calls in the function itself break once it draws another branch. With it,
|
|
698
|
+
* the search box shows whatever the number of conversations, and a search
|
|
699
|
+
* with no match among the user's own says so ("None of your conversations
|
|
700
|
+
* matches").
|
|
701
|
+
*/
|
|
702
|
+
sidebarSections?: React.ReactNode | ((search: ChatSidebarSearch) => React.ReactNode);
|
|
669
703
|
/** The status line's text for a status, over the built-in one; `undefined` keeps it. */
|
|
670
704
|
statusLabel?: (status: AgentStatusState) => string | undefined;
|
|
705
|
+
/**
|
|
706
|
+
* Tool names as the status line and the reasoning details show them, over
|
|
707
|
+
* the raw name: by name, or a function of it (`undefined` keeps the raw name).
|
|
708
|
+
* A new function redraws every message: keep one across renders unless its
|
|
709
|
+
* labels change.
|
|
710
|
+
*/
|
|
711
|
+
toolLabels?: ChatToolLabels;
|
|
671
712
|
/** The rotating messages of the waiting panel, already translated. */
|
|
672
713
|
waitingMessages?: string[];
|
|
673
714
|
/** Replaces the waiting panel's content (its messages and the invitation to play). */
|
|
674
715
|
renderWaitingPanel?: () => React.ReactNode;
|
|
716
|
+
/**
|
|
717
|
+
* An action in the corner of the reasoning window (a game to play while the
|
|
718
|
+
* agent works), shown while the window is and gone with it.
|
|
719
|
+
*/
|
|
720
|
+
renderReasoningAction?: () => React.ReactNode;
|
|
721
|
+
/**
|
|
722
|
+
* Files a reply carries but whose markers it does not name go behind one
|
|
723
|
+
* collapsed "Working files (N)" line instead of one card each: all of them
|
|
724
|
+
* (`true`), or those the predicate picks. Default: one card each.
|
|
725
|
+
*/
|
|
726
|
+
collapseUnnamedFiles?: boolean | ((attachment: ChatAttachment, message: ChatMessage) => boolean);
|
|
675
727
|
/** Font of the panel's text; the package's own otherwise. */
|
|
676
728
|
fontFamily?: string;
|
|
677
729
|
/**
|
|
@@ -753,6 +805,63 @@ interface ChatPanelProps {
|
|
|
753
805
|
* `sidebar*` slots render nothing meanwhile. Default: false.
|
|
754
806
|
*/
|
|
755
807
|
hideConversationList?: boolean;
|
|
808
|
+
/**
|
|
809
|
+
* Asked before a conversation is filed into a workspace (`null`: taken out
|
|
810
|
+
* of its workspace), by a drag or the row's Move menu: `false` (or a promise
|
|
811
|
+
* of it) leaves it where it is, e.g. once the user declined to share it with
|
|
812
|
+
* a workspace's members. One that throws or rejects refuses the move.
|
|
813
|
+
*/
|
|
814
|
+
onBeforeMove?: (conversation: ChatConversationSummary, workspace: ChatWorkspace | null) => boolean | Promise<boolean>;
|
|
815
|
+
/**
|
|
816
|
+
* Told when deleting, renaming, moving or clearing conversations fails, with
|
|
817
|
+
* the backend's reason when it gave one (`conversation` is `null` for
|
|
818
|
+
* `clearAll`). The panel says it under the conversation list (or the
|
|
819
|
+
* header) unless this returns `true`: the host told the user itself.
|
|
820
|
+
*/
|
|
821
|
+
onConversationActionFailed?: (action: ChatConversationAction, conversation: ChatConversationSummary | null, error: string | null) => boolean | void;
|
|
822
|
+
/**
|
|
823
|
+
* The conversation list's search box shows once the list holds more than
|
|
824
|
+
* this many conversations; `0` shows it from the first one. Default: 7.
|
|
825
|
+
*/
|
|
826
|
+
sidebarSearchThreshold?: number;
|
|
827
|
+
/**
|
|
828
|
+
* Names the channel a conversation was started in, on its row (`Started in
|
|
829
|
+
* {channel}`): a string names it, `null` shows nothing, `undefined` keeps
|
|
830
|
+
* the panel's own name (Slack, Teams, WhatsApp, Telegram, Discord, Google
|
|
831
|
+
* Chat; no other channel is shown).
|
|
832
|
+
*/
|
|
833
|
+
channelLabel?: (channel: string) => string | null | undefined;
|
|
834
|
+
/** A title beside the agent's name in the header (the conversation's, say), or a function of what is on screen. */
|
|
835
|
+
headerTitle?: React.ReactNode | ((context: ChatHeaderContext) => React.ReactNode);
|
|
836
|
+
/** A line under the agent's name in the header (`Agent · Slack`, say), or a function of what is on screen. */
|
|
837
|
+
headerSubtitle?: React.ReactNode | ((context: ChatHeaderContext) => React.ReactNode);
|
|
838
|
+
/**
|
|
839
|
+
* Links the header's agent name to this address (an http(s) URL or a path
|
|
840
|
+
* of the host's own origin), opened in place; the chevron beside the name
|
|
841
|
+
* opens the agent picker then. `undefined` links nothing.
|
|
842
|
+
*/
|
|
843
|
+
agentHref?: (agent: XtmAgent) => string | undefined;
|
|
844
|
+
/**
|
|
845
|
+
* Called on a click of the header's agent name, over following `agentHref`
|
|
846
|
+
* (a click with a modifier key still follows it): a host routes to the
|
|
847
|
+
* agent's page itself. The chevron beside the name opens the agent picker then.
|
|
848
|
+
*/
|
|
849
|
+
onAgentNameClick?: (agent: XtmAgent) => void;
|
|
850
|
+
/**
|
|
851
|
+
* What Clear all does to the conversations, for its wording: `'archive'`
|
|
852
|
+
* says "Archive all conversations". Default: `'delete'`. A host words it
|
|
853
|
+
* otherwise through `t`.
|
|
854
|
+
*/
|
|
855
|
+
clearAllAction?: 'delete' | 'archive';
|
|
856
|
+
}
|
|
857
|
+
/** A conversation action of the list, as `onConversationActionFailed` names it. */
|
|
858
|
+
type ChatConversationAction = 'delete' | 'rename' | 'move' | 'clearAll';
|
|
859
|
+
/** What the header's `headerTitle` and `headerSubtitle` functions are given. */
|
|
860
|
+
interface ChatHeaderContext {
|
|
861
|
+
/** The conversation on screen as the conversation list holds it; `null` for a new chat, or before the list is read. */
|
|
862
|
+
conversation: ChatConversationSummary | null;
|
|
863
|
+
/** The agent the header names, when the agent list holds it. */
|
|
864
|
+
agent: XtmAgent | null;
|
|
756
865
|
}
|
|
757
866
|
/** What a marker card is drawn from. */
|
|
758
867
|
interface ChatMarkerContext {
|
|
@@ -774,6 +883,8 @@ interface ChatCodeBlockContext {
|
|
|
774
883
|
/** The answer is still streaming: the block may not be complete yet. */
|
|
775
884
|
streaming: boolean;
|
|
776
885
|
}
|
|
886
|
+
/** Tool names as a person reads them, by the tool's raw name (see `ChatPanelProps.toolLabels`). */
|
|
887
|
+
type ChatToolLabels = Readonly<Record<string, string>> | ((toolName: string) => string | undefined);
|
|
777
888
|
/** Renders one fenced code block of a language (see `ChatPanelProps.codeBlockRenderers`); *code* is the block's text. `null` keeps the block as code. */
|
|
778
889
|
type ChatCodeBlockRenderer = (code: string, context: ChatCodeBlockContext) => React.ReactNode;
|
|
779
890
|
/** What a message slot (`renderMessageExtras`, `messageFooterActions`) is drawn with. */
|
|
@@ -844,6 +955,8 @@ interface ChatSendDraft {
|
|
|
844
955
|
source: 'composer' | 'host' | 'voice';
|
|
845
956
|
/** A turn is running. */
|
|
846
957
|
turnRunning: boolean;
|
|
958
|
+
/** How many files the message carries (only the composer attaches files); always set by the panel. */
|
|
959
|
+
fileCount?: number;
|
|
847
960
|
/**
|
|
848
961
|
* A message held while a turn ran, asked about again as it leaves, the turn
|
|
849
962
|
* over (`turnRunning: false`), each send held with it on its own. `false`
|
|
@@ -998,6 +1111,12 @@ interface ChatMessage {
|
|
|
998
1111
|
* reasoning-details affordance, mirroring the XTM One web chat.
|
|
999
1112
|
*/
|
|
1000
1113
|
isTruncated?: boolean;
|
|
1114
|
+
/**
|
|
1115
|
+
* True when the server restarted while the turn ran, so the answer stopped
|
|
1116
|
+
* before it could finish (`interrupted` on the stored message): marked as a
|
|
1117
|
+
* truncated answer is.
|
|
1118
|
+
*/
|
|
1119
|
+
interrupted?: boolean;
|
|
1001
1120
|
/**
|
|
1002
1121
|
* The backend's own fields for the message, as it sent them: the restored
|
|
1003
1122
|
* (or paged, or relayed) entry, or the `done` frame - or the stored end -
|
|
@@ -1105,6 +1224,17 @@ interface AgentStatusState {
|
|
|
1105
1224
|
waitStartMs?: number;
|
|
1106
1225
|
/** `consulting`: the consult the agent is waiting on. */
|
|
1107
1226
|
consult?: ConsultStatus;
|
|
1227
|
+
/** `preparing`: the setup step running (`history`, `compaction`, `memory`, `integrations`, `tools`). */
|
|
1228
|
+
phase?: string;
|
|
1229
|
+
/** `analyzing`: the agent's step, from 1. */
|
|
1230
|
+
iteration?: number;
|
|
1231
|
+
/** `transferring`: the agent the conversation goes to; `mention_answering`: the mentioned agent answering. */
|
|
1232
|
+
agentName?: string;
|
|
1233
|
+
/**
|
|
1234
|
+
* The mentioned agent answering now, on every status of its run: a turn
|
|
1235
|
+
* that answers several mentioned agents runs one after the other.
|
|
1236
|
+
*/
|
|
1237
|
+
answeringAgentName?: string;
|
|
1108
1238
|
}
|
|
1109
1239
|
/**
|
|
1110
1240
|
* A consult the agent is waiting on, as the status line shows it. A consulted
|
|
@@ -1134,7 +1264,20 @@ interface ChatFile {
|
|
|
1134
1264
|
fileId?: string;
|
|
1135
1265
|
/** Upload status: 'pending' while uploading, 'done' when uploaded, 'error' on failure. */
|
|
1136
1266
|
uploadStatus?: 'pending' | 'done' | 'error';
|
|
1267
|
+
/** Why the upload failed, as the backend said (`detail`), when it said. */
|
|
1268
|
+
uploadError?: string;
|
|
1269
|
+
}
|
|
1270
|
+
/** The conversation list's search, as a host's sections read it (`sidebarSections`). */
|
|
1271
|
+
interface ChatSidebarSearch {
|
|
1272
|
+
/** What the search box holds, trimmed; `''` when it holds nothing. */
|
|
1273
|
+
query: string;
|
|
1137
1274
|
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Why the composer refused a file (see `onFileRefused`): larger than
|
|
1277
|
+
* `maxFileSize`, past `maxTotalSize` with the files attached, past
|
|
1278
|
+
* `maxFileCount`, or not of `acceptFileTypes`.
|
|
1279
|
+
*/
|
|
1280
|
+
type ChatFileRefusal = 'file-size' | 'total-size' | 'file-count' | 'file-type';
|
|
1138
1281
|
/**
|
|
1139
1282
|
* A past conversation surfaced in the history menu (REST backend only).
|
|
1140
1283
|
* Listed via `GET {apiBaseUrl}{apiEndpoints.history ?? apiEndpoints.sessions ?? '/chat/sessions'}`
|
|
@@ -1159,6 +1302,10 @@ interface ChatConversationSummary {
|
|
|
1159
1302
|
* backend reports it. Undefined on backends that do not.
|
|
1160
1303
|
*/
|
|
1161
1304
|
workspaceId?: string | null;
|
|
1305
|
+
/** The last message's text, shortened by the backend (`last_message_preview`), when it sends one. */
|
|
1306
|
+
lastMessagePreview?: string;
|
|
1307
|
+
/** Where the conversation was started (`channel`: `slack`, `teams`, `web`, …), when the backend reports it. */
|
|
1308
|
+
channel?: string;
|
|
1162
1309
|
}
|
|
1163
1310
|
/**
|
|
1164
1311
|
* A conversation the composer's `@` menu offers (see
|
|
@@ -1200,6 +1347,8 @@ interface XtmAgent {
|
|
|
1200
1347
|
description: string | null;
|
|
1201
1348
|
/** The heading the agent picker lists it under; ungrouped agents come first. */
|
|
1202
1349
|
group?: string;
|
|
1350
|
+
/** Short labels shown after its name in the agent picker (`Company`), already translated. */
|
|
1351
|
+
badges?: string[];
|
|
1203
1352
|
}
|
|
1204
1353
|
|
|
1205
1354
|
/**
|
|
@@ -1220,4 +1369,4 @@ interface TransferredAgent {
|
|
|
1220
1369
|
declare const voiceCallSupported: () => boolean;
|
|
1221
1370
|
|
|
1222
1371
|
export { ChatPanel, ChatToggleButton, voiceCallSupported };
|
|
1223
|
-
export type { AgentStatusState, ApiEndpoints, BackendType, ChatAnsweredBy, ChatArtifact, ChatArtifactKind, ChatAttachment, ChatCodeBlockContext, ChatCodeBlockRenderer, ChatContextBreakdown, ChatContextUsage, ChatConversationRef, ChatConversationReferenceCandidate, ChatConversationSummary, ChatFile, ChatHeldOutcome, ChatMarkerContext, ChatMarkerRenderer, ChatMessage, ChatMessagePersistedFeedback, ChatMessageSlotContext, ChatMode, ChatPanelHandle, ChatPanelMode, ChatPanelProps, ChatPanelTheme, ChatPromptTemplate, ChatQuotaStatus, ChatSendDecision, ChatSendDraft, ChatSendOptions, ChatSendOutcome, ChatStreamEventInfo, ChatToggleButtonProps, ChatTurnEnd, ChatTurnOutcome, ChatWorkspace, ConsultStatus, MessageFeedback, ToolApprovalDecision, ToolApprovalProposal, ToolApprovalVerdict, TransferredAgent, Translate, XtmAgent };
|
|
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 };
|