@meshagent/meshagent-tailwind 0.38.2 → 0.38.3
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 +9 -0
- package/dist/cjs/Chat.d.ts +11 -3
- package/dist/cjs/Chat.js +376 -29
- package/dist/cjs/ChatBotView.d.ts +29 -0
- package/dist/cjs/ChatBotView.js +491 -0
- package/dist/cjs/ChatInput.d.ts +12 -3
- package/dist/cjs/ChatInput.js +143 -44
- package/dist/cjs/ChatThread.d.ts +17 -3
- package/dist/cjs/ChatThread.js +646 -90
- package/dist/cjs/ChatTypingIndicator.d.ts +12 -5
- package/dist/cjs/ChatTypingIndicator.js +104 -13
- package/dist/cjs/FileUploader.d.ts +3 -2
- package/dist/cjs/FileUploader.js +35 -11
- package/dist/cjs/UploadPill.d.ts +2 -2
- package/dist/cjs/UploadPill.js +70 -32
- package/dist/cjs/chat-hooks.d.ts +38 -0
- package/dist/cjs/chat-hooks.js +390 -0
- package/dist/cjs/chat-message.d.ts +11 -0
- package/dist/cjs/chat-message.js +33 -0
- package/dist/cjs/components/ui/button.d.ts +1 -1
- package/dist/cjs/conversation-descriptor.d.ts +59 -0
- package/dist/cjs/conversation-descriptor.js +300 -0
- package/dist/cjs/file-attachment.d.ts +45 -0
- package/dist/cjs/file-attachment.js +171 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/multi-thread-view.d.ts +18 -0
- package/dist/cjs/multi-thread-view.js +88 -0
- package/dist/cjs/tools/ui-toolkit.d.ts +1 -1
- package/dist/cjs/tools/ui-toolkit.js +2 -1
- package/dist/esm/Chat.d.ts +11 -3
- package/dist/esm/Chat.js +378 -31
- package/dist/esm/ChatBotView.d.ts +29 -0
- package/dist/esm/ChatBotView.js +486 -0
- package/dist/esm/ChatInput.d.ts +12 -3
- package/dist/esm/ChatInput.js +143 -34
- package/dist/esm/ChatThread.d.ts +17 -3
- package/dist/esm/ChatThread.js +648 -92
- package/dist/esm/ChatTypingIndicator.d.ts +12 -5
- package/dist/esm/ChatTypingIndicator.js +94 -13
- package/dist/esm/FileUploader.d.ts +3 -2
- package/dist/esm/FileUploader.js +26 -12
- package/dist/esm/UploadPill.d.ts +2 -2
- package/dist/esm/UploadPill.js +60 -32
- package/dist/esm/chat-hooks.d.ts +38 -0
- package/dist/esm/chat-hooks.js +372 -0
- package/dist/esm/chat-message.d.ts +11 -0
- package/dist/esm/chat-message.js +13 -0
- package/dist/esm/components/ui/button.d.ts +1 -1
- package/dist/esm/conversation-descriptor.d.ts +59 -0
- package/dist/esm/conversation-descriptor.js +280 -0
- package/dist/esm/file-attachment.d.ts +45 -0
- package/dist/esm/file-attachment.js +151 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/multi-thread-view.d.ts +18 -0
- package/dist/esm/multi-thread-view.js +68 -0
- package/dist/esm/tools/ui-toolkit.d.ts +1 -1
- package/dist/esm/tools/ui-toolkit.js +2 -1
- package/dist/index.css +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## [0.38.3]
|
|
2
|
+
- Breaking: TypeScript container image summaries now use `references`/`preferredRef` and metadata fields (timestamps/media type), and `inspectImage` returns manifests/layers/content size; `tags`/`size` are removed.
|
|
3
|
+
- `getUsage` now supports filters for users, room, provider, model, and usage type.
|
|
4
|
+
- Room connection and errors improved: `RoomServerException` now carries `statusCode` and `retryable`, and `RoomClient` can route OAuth/secret requests via handler options.
|
|
5
|
+
- React hooks add robust connection retry/backoff, new authorization helpers, and optional secret/OAuth handlers; document connection now supports schema/initial JSON and improved cleanup.
|
|
6
|
+
- New Livekit support (client + protocol channel) and room participant hook; hosted toolkits are now shared per room to avoid duplicate starts.
|
|
7
|
+
- Breaking: `meshagent-react` no longer exports the legacy chat and file-upload modules.
|
|
8
|
+
- Tailwind chat UI is rebuilt with multi-thread support, new thread creation via agent tools, and file attachment utilities, with new exports for thread and conversation helpers.
|
|
9
|
+
|
|
1
10
|
## [0.38.2]
|
|
2
11
|
- Stability
|
|
3
12
|
|
package/dist/cjs/Chat.d.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { Participant, RoomClient } from "@meshagent/meshagent";
|
|
2
3
|
export interface ChatProps {
|
|
3
4
|
room: RoomClient;
|
|
4
|
-
path
|
|
5
|
+
path?: string;
|
|
5
6
|
participants?: Participant[];
|
|
7
|
+
agentName?: string;
|
|
8
|
+
toolkit?: string;
|
|
9
|
+
tool?: string;
|
|
10
|
+
centerComposer?: boolean;
|
|
11
|
+
emptyStateTitle?: string;
|
|
12
|
+
emptyStateDescription?: string;
|
|
13
|
+
onThreadResolved?: (path: string | null, displayName: string | null) => void;
|
|
6
14
|
}
|
|
7
|
-
export declare function Chat({ room, path, participants }: ChatProps):
|
|
15
|
+
export declare function Chat({ room, path, participants, agentName, toolkit, tool, centerComposer, emptyStateTitle, emptyStateDescription, onThreadResolved, }: ChatProps): ReactElement;
|
package/dist/cjs/Chat.js
CHANGED
|
@@ -23,55 +23,193 @@ __export(Chat_exports, {
|
|
|
23
23
|
module.exports = __toCommonJS(Chat_exports);
|
|
24
24
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
25
25
|
var import_react = require("react");
|
|
26
|
+
var import_meshagent = require("@meshagent/meshagent");
|
|
26
27
|
var import_meshagent_react = require("@meshagent/meshagent-react");
|
|
27
|
-
var
|
|
28
|
+
var import_lucide_react = require("lucide-react");
|
|
28
29
|
var import_ChatInput = require("./ChatInput");
|
|
29
|
-
var
|
|
30
|
-
var
|
|
30
|
+
var import_ChatThread = require("./ChatThread");
|
|
31
|
+
var import_button = require("./components/ui/button");
|
|
31
32
|
var import_sonner = require("./components/ui/sonner");
|
|
33
|
+
var import_file_attachment = require("./file-attachment");
|
|
34
|
+
var import_chat_hooks = require("./chat-hooks");
|
|
35
|
+
class NewThreadCancelledError extends Error {
|
|
36
|
+
constructor() {
|
|
37
|
+
super("new thread creation cancelled");
|
|
38
|
+
this.name = "NewThreadCancelledError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function normalizeThreadPath(path) {
|
|
42
|
+
const normalizedPath = path?.trim();
|
|
43
|
+
return normalizedPath ? normalizedPath : null;
|
|
44
|
+
}
|
|
32
45
|
function getParticipantName(participant) {
|
|
33
|
-
const name = participant
|
|
34
|
-
return typeof name === "string" ? name : "";
|
|
46
|
+
const name = participant.getAttribute("name");
|
|
47
|
+
return typeof name === "string" ? name.trim() : "";
|
|
48
|
+
}
|
|
49
|
+
function displayParticipantName(name) {
|
|
50
|
+
const normalizedName = name?.trim();
|
|
51
|
+
if (!normalizedName) {
|
|
52
|
+
return "agent";
|
|
53
|
+
}
|
|
54
|
+
return normalizedName.split("@")[0]?.trim() || normalizedName;
|
|
55
|
+
}
|
|
56
|
+
function isAgentParticipant(participant) {
|
|
57
|
+
return participant.role === "agent" || participant.getAttribute("supports_agent_messages") === true;
|
|
58
|
+
}
|
|
59
|
+
function findTargetAgent(room, agentName) {
|
|
60
|
+
const normalizedAgentName = agentName?.trim();
|
|
61
|
+
for (const participant of room.messaging.remoteParticipants) {
|
|
62
|
+
if (!isAgentParticipant(participant)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (normalizedAgentName && getParticipantName(participant) !== normalizedAgentName) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
return participant;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
function ensureOperationActive(operationId, activeOperationRef) {
|
|
73
|
+
if (activeOperationRef.current !== operationId) {
|
|
74
|
+
throw new NewThreadCancelledError();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function delay(milliseconds) {
|
|
78
|
+
return new Promise((resolve) => {
|
|
79
|
+
window.setTimeout(resolve, milliseconds);
|
|
80
|
+
});
|
|
35
81
|
}
|
|
36
|
-
function
|
|
82
|
+
async function waitForTargetAgent(params) {
|
|
83
|
+
const { room, agentName, operationId, activeOperationRef } = params;
|
|
84
|
+
while (true) {
|
|
85
|
+
ensureOperationActive(operationId, activeOperationRef);
|
|
86
|
+
const targetAgent = findTargetAgent(room, agentName);
|
|
87
|
+
if (targetAgent) {
|
|
88
|
+
return targetAgent;
|
|
89
|
+
}
|
|
90
|
+
await delay(250);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function waitForToolkitAvailable(params) {
|
|
37
94
|
const {
|
|
95
|
+
room,
|
|
96
|
+
participantId,
|
|
97
|
+
toolkit,
|
|
98
|
+
operationId,
|
|
99
|
+
activeOperationRef
|
|
100
|
+
} = params;
|
|
101
|
+
while (true) {
|
|
102
|
+
ensureOperationActive(operationId, activeOperationRef);
|
|
103
|
+
try {
|
|
104
|
+
const toolkits = await room.agents.listToolkits({ participantId, timeout: 1e3 });
|
|
105
|
+
if (toolkits.some((toolkitDescription) => toolkitDescription.name === toolkit)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
}
|
|
110
|
+
await delay(250);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function getStringField(record, key) {
|
|
114
|
+
const value = record[key];
|
|
115
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
116
|
+
}
|
|
117
|
+
function parseThreadToolResult(toolkit, tool, content) {
|
|
118
|
+
const path = getStringField(content.json, "path");
|
|
119
|
+
if (!path) {
|
|
120
|
+
throw new Error(`${toolkit}.${tool} response missing path`);
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
path,
|
|
124
|
+
displayName: getStringField(content.json, "name")
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function describeError(error) {
|
|
128
|
+
if (error instanceof Error && error.message.trim() !== "") {
|
|
129
|
+
return error.message;
|
|
130
|
+
}
|
|
131
|
+
return `${error}`;
|
|
132
|
+
}
|
|
133
|
+
function EmptyState({
|
|
134
|
+
title,
|
|
135
|
+
description
|
|
136
|
+
}) {
|
|
137
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mx-auto flex max-w-2xl flex-col items-center justify-center px-6 py-20 text-center", children: [
|
|
138
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-4xl font-semibold tracking-tight text-foreground sm:text-5xl", children: title }),
|
|
139
|
+
description?.trim() ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-3 max-w-xl text-sm leading-6 text-muted-foreground sm:text-base", children: description }) : null
|
|
140
|
+
] });
|
|
141
|
+
}
|
|
142
|
+
function ErrorBanner({ message }) {
|
|
143
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-auto w-full max-w-[912px] whitespace-pre-wrap rounded-3xl border border-destructive/30 bg-destructive/5 px-6 py-5 text-sm text-destructive", children: message });
|
|
144
|
+
}
|
|
145
|
+
function ResolvedChatView({
|
|
146
|
+
room,
|
|
147
|
+
path,
|
|
148
|
+
participants,
|
|
149
|
+
agentName,
|
|
150
|
+
emptyStateTitle,
|
|
151
|
+
emptyStateDescription,
|
|
152
|
+
showNewThreadButton = false,
|
|
153
|
+
onStartNewThread
|
|
154
|
+
}) {
|
|
155
|
+
const {
|
|
156
|
+
document,
|
|
38
157
|
messages,
|
|
39
158
|
sendMessage,
|
|
40
159
|
selectAttachments,
|
|
41
160
|
attachments,
|
|
42
161
|
setAttachments,
|
|
43
|
-
|
|
162
|
+
onlineParticipants,
|
|
163
|
+
localParticipantName,
|
|
44
164
|
cancelRequest
|
|
45
|
-
} = (0,
|
|
46
|
-
const { thinking } = (0, import_meshagent_react.useRoomIndicators)({ room, path });
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const onTextChange = (0, import_react.useCallback)((_) => {
|
|
52
|
-
const removeParticipant = room.messaging.remoteParticipants;
|
|
53
|
-
for (const part of removeParticipant) {
|
|
165
|
+
} = (0, import_chat_hooks.useChatThread)({ room, path, participants, agentName });
|
|
166
|
+
const { typing, thinking } = (0, import_meshagent_react.useRoomIndicators)({ room, path });
|
|
167
|
+
const threadStatus = (0, import_chat_hooks.useThreadStatus)({ room, path, agentName });
|
|
168
|
+
const [showCompletedToolCalls, setShowCompletedToolCalls] = (0, import_react.useState)(false);
|
|
169
|
+
const onTextChange = (0, import_react.useCallback)(() => {
|
|
170
|
+
for (const participant of onlineParticipants) {
|
|
54
171
|
room.messaging.sendMessage({
|
|
55
|
-
to:
|
|
172
|
+
to: participant,
|
|
56
173
|
type: "typing",
|
|
57
174
|
message: { path }
|
|
58
175
|
});
|
|
59
176
|
}
|
|
60
|
-
}, [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
177
|
+
}, [onlineParticipants, path, room]);
|
|
178
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col", children: [
|
|
179
|
+
showNewThreadButton && onStartNewThread ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-4 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-auto flex w-full max-w-[912px] justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
180
|
+
import_button.Button,
|
|
181
|
+
{
|
|
182
|
+
type: "button",
|
|
183
|
+
variant: "outline",
|
|
184
|
+
size: "sm",
|
|
185
|
+
className: "rounded-full shadow-xs",
|
|
186
|
+
onClick: onStartNewThread,
|
|
187
|
+
children: [
|
|
188
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Plus, { className: "mr-2 h-4 w-4" }),
|
|
189
|
+
"New thread"
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
) }) }) : null,
|
|
66
193
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
67
194
|
import_ChatThread.ChatThread,
|
|
68
195
|
{
|
|
69
196
|
room,
|
|
197
|
+
path,
|
|
70
198
|
messages,
|
|
71
|
-
|
|
199
|
+
isLoading: document === null,
|
|
200
|
+
localParticipantName,
|
|
201
|
+
showCompletedToolCalls,
|
|
202
|
+
onShowCompletedToolCallsChanged: setShowCompletedToolCalls,
|
|
203
|
+
typing,
|
|
204
|
+
thinking,
|
|
205
|
+
threadStatusText: threadStatus.text,
|
|
206
|
+
threadStatusStartedAt: threadStatus.startedAt,
|
|
207
|
+
threadStatusMode: threadStatus.mode,
|
|
208
|
+
onCancelRequest: cancelRequest,
|
|
209
|
+
emptyStateTitle,
|
|
210
|
+
emptyStateDescription
|
|
72
211
|
}
|
|
73
212
|
),
|
|
74
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ChatTypingIndicator.ChatTypingIndicator, { room, path }),
|
|
75
213
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
76
214
|
import_ChatInput.ChatInput,
|
|
77
215
|
{
|
|
@@ -79,11 +217,220 @@ function Chat({ room, path, participants }) {
|
|
|
79
217
|
attachments,
|
|
80
218
|
onFilesSelected: selectAttachments,
|
|
81
219
|
setAttachments,
|
|
82
|
-
onTextChange
|
|
83
|
-
onCancelRequest: cancelRequest,
|
|
84
|
-
showCancelButton: thinking
|
|
220
|
+
onTextChange
|
|
85
221
|
}
|
|
86
|
-
)
|
|
222
|
+
)
|
|
223
|
+
] });
|
|
224
|
+
}
|
|
225
|
+
function Chat({
|
|
226
|
+
room,
|
|
227
|
+
path,
|
|
228
|
+
participants,
|
|
229
|
+
agentName,
|
|
230
|
+
toolkit = "chat",
|
|
231
|
+
tool = "new_thread",
|
|
232
|
+
centerComposer = true,
|
|
233
|
+
emptyStateTitle,
|
|
234
|
+
emptyStateDescription,
|
|
235
|
+
onThreadResolved
|
|
236
|
+
}) {
|
|
237
|
+
const [internalThreadPath, setInternalThreadPath] = (0, import_react.useState)(null);
|
|
238
|
+
const [newThreadDraft, setNewThreadDraft] = (0, import_react.useState)("");
|
|
239
|
+
const [newThreadAttachments, setNewThreadAttachments] = (0, import_react.useState)([]);
|
|
240
|
+
const [newThreadError, setNewThreadError] = (0, import_react.useState)(null);
|
|
241
|
+
const [creatingNewThread, setCreatingNewThread] = (0, import_react.useState)(false);
|
|
242
|
+
const [waitingForAgent, setWaitingForAgent] = (0, import_react.useState)(false);
|
|
243
|
+
const activeOperationRef = (0, import_react.useRef)(0);
|
|
244
|
+
const controlledPath = (0, import_react.useMemo)(() => normalizeThreadPath(path), [path]);
|
|
245
|
+
const managesOwnThread = controlledPath === null;
|
|
246
|
+
const activePath = controlledPath ?? internalThreadPath;
|
|
247
|
+
(0, import_react.useEffect)(() => {
|
|
248
|
+
return () => {
|
|
249
|
+
activeOperationRef.current += 1;
|
|
250
|
+
};
|
|
251
|
+
}, []);
|
|
252
|
+
(0, import_react.useEffect)(() => {
|
|
253
|
+
if (controlledPath !== null) {
|
|
254
|
+
setInternalThreadPath(null);
|
|
255
|
+
}
|
|
256
|
+
}, [controlledPath]);
|
|
257
|
+
(0, import_react.useEffect)(() => {
|
|
258
|
+
activeOperationRef.current += 1;
|
|
259
|
+
setInternalThreadPath(null);
|
|
260
|
+
setNewThreadDraft("");
|
|
261
|
+
setNewThreadAttachments([]);
|
|
262
|
+
setNewThreadError(null);
|
|
263
|
+
setCreatingNewThread(false);
|
|
264
|
+
setWaitingForAgent(false);
|
|
265
|
+
}, [agentName, managesOwnThread, room]);
|
|
266
|
+
const selectNewThreadAttachments = (0, import_react.useCallback)((files) => {
|
|
267
|
+
const nextAttachments = files.map((file) => new import_file_attachment.MeshagentFileUpload(
|
|
268
|
+
room,
|
|
269
|
+
`uploaded-files/${file.name}`,
|
|
270
|
+
(0, import_file_attachment.fileToAsyncIterable)(file),
|
|
271
|
+
file.size
|
|
272
|
+
));
|
|
273
|
+
setNewThreadAttachments((currentAttachments) => [...currentAttachments, ...nextAttachments]);
|
|
274
|
+
}, [room]);
|
|
275
|
+
const cancelPendingNewThread = (0, import_react.useCallback)(() => {
|
|
276
|
+
activeOperationRef.current += 1;
|
|
277
|
+
setCreatingNewThread(false);
|
|
278
|
+
setWaitingForAgent(false);
|
|
279
|
+
setNewThreadError(null);
|
|
280
|
+
}, []);
|
|
281
|
+
const openNewThreadComposer = (0, import_react.useCallback)(() => {
|
|
282
|
+
activeOperationRef.current += 1;
|
|
283
|
+
setInternalThreadPath(null);
|
|
284
|
+
setNewThreadDraft("");
|
|
285
|
+
setNewThreadAttachments([]);
|
|
286
|
+
setNewThreadError(null);
|
|
287
|
+
setCreatingNewThread(false);
|
|
288
|
+
setWaitingForAgent(false);
|
|
289
|
+
}, []);
|
|
290
|
+
const handleCreateThread = (0, import_react.useCallback)(async () => {
|
|
291
|
+
const text = newThreadDraft.trim();
|
|
292
|
+
const hasDraft = text !== "" || newThreadAttachments.length > 0;
|
|
293
|
+
if (!hasDraft || creatingNewThread || waitingForAgent) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const operationId = activeOperationRef.current + 1;
|
|
297
|
+
activeOperationRef.current = operationId;
|
|
298
|
+
const initialTargetAgent = findTargetAgent(room, agentName);
|
|
299
|
+
setWaitingForAgent(initialTargetAgent === null);
|
|
300
|
+
setCreatingNewThread(initialTargetAgent !== null);
|
|
301
|
+
setNewThreadError(null);
|
|
302
|
+
try {
|
|
303
|
+
const targetAgent = initialTargetAgent ?? await waitForTargetAgent({
|
|
304
|
+
room,
|
|
305
|
+
agentName,
|
|
306
|
+
operationId,
|
|
307
|
+
activeOperationRef
|
|
308
|
+
});
|
|
309
|
+
ensureOperationActive(operationId, activeOperationRef);
|
|
310
|
+
if (initialTargetAgent === null) {
|
|
311
|
+
setWaitingForAgent(false);
|
|
312
|
+
setCreatingNewThread(true);
|
|
313
|
+
}
|
|
314
|
+
await waitForToolkitAvailable({
|
|
315
|
+
room,
|
|
316
|
+
participantId: targetAgent.id,
|
|
317
|
+
toolkit,
|
|
318
|
+
operationId,
|
|
319
|
+
activeOperationRef
|
|
320
|
+
});
|
|
321
|
+
ensureOperationActive(operationId, activeOperationRef);
|
|
322
|
+
const response = await room.agents.invokeTool({
|
|
323
|
+
toolkit,
|
|
324
|
+
tool,
|
|
325
|
+
arguments: {
|
|
326
|
+
message: {
|
|
327
|
+
text,
|
|
328
|
+
attachments: newThreadAttachments.map((attachment) => ({ path: attachment.path }))
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
ensureOperationActive(operationId, activeOperationRef);
|
|
333
|
+
if (!(response instanceof import_meshagent.JsonContent)) {
|
|
334
|
+
throw new Error(`${toolkit}.${tool} returned non-JSON content`);
|
|
335
|
+
}
|
|
336
|
+
const result = parseThreadToolResult(toolkit, tool, response);
|
|
337
|
+
if (controlledPath === null) {
|
|
338
|
+
setInternalThreadPath(result.path);
|
|
339
|
+
}
|
|
340
|
+
setNewThreadDraft("");
|
|
341
|
+
setNewThreadAttachments([]);
|
|
342
|
+
setNewThreadError(null);
|
|
343
|
+
setCreatingNewThread(false);
|
|
344
|
+
setWaitingForAgent(false);
|
|
345
|
+
onThreadResolved?.(result.path, result.displayName);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
if (error instanceof NewThreadCancelledError) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
setCreatingNewThread(false);
|
|
351
|
+
setWaitingForAgent(false);
|
|
352
|
+
setNewThreadError(describeError(error));
|
|
353
|
+
}
|
|
354
|
+
}, [
|
|
355
|
+
agentName,
|
|
356
|
+
controlledPath,
|
|
357
|
+
creatingNewThread,
|
|
358
|
+
newThreadAttachments,
|
|
359
|
+
newThreadDraft,
|
|
360
|
+
onThreadResolved,
|
|
361
|
+
room,
|
|
362
|
+
toolkit,
|
|
363
|
+
tool,
|
|
364
|
+
waitingForAgent
|
|
365
|
+
]);
|
|
366
|
+
(0, import_react.useEffect)(() => {
|
|
367
|
+
if (!managesOwnThread) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const handleKeyDown = (event) => {
|
|
371
|
+
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== "n") {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
event.preventDefault();
|
|
375
|
+
openNewThreadComposer();
|
|
376
|
+
};
|
|
377
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
378
|
+
return () => {
|
|
379
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
380
|
+
};
|
|
381
|
+
}, [managesOwnThread, openNewThreadComposer]);
|
|
382
|
+
const targetAgentLabel = (0, import_react.useMemo)(() => {
|
|
383
|
+
const knownAgentName = agentName?.trim();
|
|
384
|
+
if (knownAgentName) {
|
|
385
|
+
return displayParticipantName(knownAgentName);
|
|
386
|
+
}
|
|
387
|
+
const targetAgent = findTargetAgent(room);
|
|
388
|
+
return displayParticipantName(targetAgent ? getParticipantName(targetAgent) : null);
|
|
389
|
+
}, [agentName, room]);
|
|
390
|
+
const pendingStatusText = waitingForAgent ? `Waiting for ${targetAgentLabel} to be ready.` : creatingNewThread ? `Starting a thread with ${targetAgentLabel}.` : null;
|
|
391
|
+
const composer = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
392
|
+
import_ChatInput.ChatInput,
|
|
393
|
+
{
|
|
394
|
+
onSubmit: handleCreateThread,
|
|
395
|
+
attachments: newThreadAttachments,
|
|
396
|
+
onFilesSelected: selectNewThreadAttachments,
|
|
397
|
+
setAttachments: setNewThreadAttachments,
|
|
398
|
+
value: newThreadDraft,
|
|
399
|
+
onValueChange: setNewThreadDraft,
|
|
400
|
+
clearOnSubmit: false,
|
|
401
|
+
showCancelButton: creatingNewThread || waitingForAgent,
|
|
402
|
+
onCancelRequest: cancelPendingNewThread,
|
|
403
|
+
disabled: creatingNewThread || waitingForAgent,
|
|
404
|
+
placeholder: agentName?.trim() ? `Type a message or @${displayParticipantName(agentName)}` : "Type a message"
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col", children: [
|
|
408
|
+
activePath ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
409
|
+
ResolvedChatView,
|
|
410
|
+
{
|
|
411
|
+
room,
|
|
412
|
+
path: activePath,
|
|
413
|
+
participants,
|
|
414
|
+
agentName,
|
|
415
|
+
emptyStateTitle,
|
|
416
|
+
emptyStateDescription,
|
|
417
|
+
showNewThreadButton: managesOwnThread,
|
|
418
|
+
onStartNewThread: openNewThreadComposer
|
|
419
|
+
},
|
|
420
|
+
activePath
|
|
421
|
+
) : centerComposer ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex min-h-0 flex-1 items-center justify-center px-4 py-6", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "w-full max-w-[912px] space-y-5", children: [
|
|
422
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2 text-center", children: [
|
|
423
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-4xl font-semibold tracking-tight text-foreground sm:text-5xl", children: "Start a new thread" }),
|
|
424
|
+
pendingStatusText ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-muted-foreground sm:text-base", children: pendingStatusText }) : null
|
|
425
|
+
] }),
|
|
426
|
+
composer,
|
|
427
|
+
newThreadError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorBanner, { message: newThreadError }) : null
|
|
428
|
+
] }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col", children: [
|
|
429
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1", children: emptyStateTitle ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyState, { title: emptyStateTitle, description: emptyStateDescription }) : null }),
|
|
430
|
+
pendingStatusText ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-4 pb-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-auto w-full max-w-[912px] text-sm text-muted-foreground", children: pendingStatusText }) }) : null,
|
|
431
|
+
newThreadError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-4 pb-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorBanner, { message: newThreadError }) }) : null,
|
|
432
|
+
composer
|
|
433
|
+
] }),
|
|
87
434
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sonner.Toaster, {})
|
|
88
435
|
] });
|
|
89
436
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { Participant, RoomClient } from "@meshagent/meshagent";
|
|
3
|
+
import { ChatThreadDisplayMode } from "./conversation-descriptor";
|
|
4
|
+
export { ChatThreadDisplayMode, chatDocumentPath, resolvedThreadListPath, } from "./conversation-descriptor";
|
|
5
|
+
export interface ChatBotViewProps {
|
|
6
|
+
room: RoomClient;
|
|
7
|
+
path?: string;
|
|
8
|
+
documentPath?: string;
|
|
9
|
+
participants?: Participant[];
|
|
10
|
+
agentName?: string;
|
|
11
|
+
threadDisplayMode?: ChatThreadDisplayMode;
|
|
12
|
+
threadDir?: string;
|
|
13
|
+
threadListPath?: string;
|
|
14
|
+
toolkit?: string;
|
|
15
|
+
tool?: string;
|
|
16
|
+
centerComposer?: boolean;
|
|
17
|
+
emptyStateTitle?: string;
|
|
18
|
+
emptyStateDescription?: string;
|
|
19
|
+
selectedThreadPath?: string | null;
|
|
20
|
+
selectedThreadDisplayName?: string | null;
|
|
21
|
+
onSelectedThreadPathChanged?: (path: string | null) => void;
|
|
22
|
+
onSelectedThreadResolved?: (path: string | null, displayName: string | null) => void;
|
|
23
|
+
onThreadResolved?: (path: string | null, displayName: string | null) => void;
|
|
24
|
+
newThreadResetVersion?: number;
|
|
25
|
+
showThreadList?: boolean;
|
|
26
|
+
threadListWidth?: number;
|
|
27
|
+
threadListCollapsedHeight?: number;
|
|
28
|
+
}
|
|
29
|
+
export declare function ChatBotView({ room, path, documentPath, participants, agentName, threadDisplayMode, threadDir, threadListPath, toolkit, tool, centerComposer, emptyStateTitle, emptyStateDescription, selectedThreadPath, onSelectedThreadPathChanged, onSelectedThreadResolved, onThreadResolved, newThreadResetVersion, showThreadList, threadListWidth, threadListCollapsedHeight, }: ChatBotViewProps): ReactElement;
|