@agent-native/agentkit 0.5.1-nightly-20260929180759 → 0.198.1

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.
Files changed (44) hide show
  1. package/ARCHITECTURE.md +15 -15
  2. package/README.md +56 -27
  3. package/dist/client/client.d.ts +3 -0
  4. package/dist/client/client.d.ts.map +1 -1
  5. package/dist/client/client.js +86 -39
  6. package/dist/client/client.js.map +1 -1
  7. package/dist/client/state.d.ts +7 -0
  8. package/dist/client/state.d.ts.map +1 -1
  9. package/dist/client/state.js +43 -1
  10. package/dist/client/state.js.map +1 -1
  11. package/package.json +9 -58
  12. package/dist/react/chat.d.ts +0 -27
  13. package/dist/react/chat.d.ts.map +0 -1
  14. package/dist/react/chat.js +0 -19
  15. package/dist/react/chat.js.map +0 -1
  16. package/dist/react/components.d.ts +0 -107
  17. package/dist/react/components.d.ts.map +0 -1
  18. package/dist/react/components.js +0 -1940
  19. package/dist/react/components.js.map +0 -1
  20. package/dist/react/composer-submission.d.ts +0 -21
  21. package/dist/react/composer-submission.d.ts.map +0 -1
  22. package/dist/react/composer-submission.js +0 -28
  23. package/dist/react/composer-submission.js.map +0 -1
  24. package/dist/react/context.d.ts +0 -303
  25. package/dist/react/context.d.ts.map +0 -1
  26. package/dist/react/context.js +0 -393
  27. package/dist/react/context.js.map +0 -1
  28. package/dist/react/headless.d.ts +0 -3
  29. package/dist/react/headless.d.ts.map +0 -1
  30. package/dist/react/headless.js +0 -3
  31. package/dist/react/headless.js.map +0 -1
  32. package/dist/react/index.d.ts +0 -6
  33. package/dist/react/index.d.ts.map +0 -1
  34. package/dist/react/index.js +0 -6
  35. package/dist/react/index.js.map +0 -1
  36. package/dist/react/root.d.ts +0 -49
  37. package/dist/react/root.d.ts.map +0 -1
  38. package/dist/react/root.js +0 -145
  39. package/dist/react/root.js.map +0 -1
  40. package/dist/react/streaming-text.d.ts +0 -17
  41. package/dist/react/streaming-text.d.ts.map +0 -1
  42. package/dist/react/streaming-text.js +0 -257
  43. package/dist/react/streaming-text.js.map +0 -1
  44. package/dist/react/styles.css +0 -1740
@@ -1,1940 +0,0 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ActionButton, AgentSuggestionBar, IconButton, MessageQueueDrawer, PromptComposer, Surface, TextField, agentSuggestionPrompt, splitMarkdownBlocks, writeClipboardText, } from "@agent-native/toolkit/agentkit";
3
- import { Dialog, Menu, Popover, TextArea, } from "@agent-native/toolkit/design-system";
4
- import { IconActivity, IconAlertCircle, IconArrowDown, IconArrowFork, IconArrowUp, IconBook2, IconBrain, IconChevronLeft, IconChevronRight, IconCircleCheck, IconCode, IconCopy, IconDotsVertical, IconFile, IconFolder, IconFileText, IconCheckbox, IconMail, IconUser, IconPresentation, IconStack2, IconMessageChatbot, IconGitBranch, IconId, IconMessage, IconPlugConnected, IconPlayerPause, IconPlayerPlay, IconPencil, IconRefresh, IconSearch, IconTerminal2, IconChecklist, IconThumbDown, IconThumbUp, IconTool, IconX, } from "@tabler/icons-react";
5
- import { createContext, Component, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
6
- import ReactMarkdown from "react-markdown";
7
- import remarkGfm from "remark-gfm";
8
- import { createAgentKitComposerSubmission, snapshotComposerValue, } from "./composer-submission.js";
9
- import { hasActiveAgentRuns } from "../client/state.js";
10
- import { inferAgentActivityKind, } from "../protocol/index.js";
11
- import { useAgentCapability, useAgentConnection, useAgentKit, useAgentKitControl, useAgentKitMutation, useAgentThread, } from "./context.js";
12
- import { AgentStreamingText } from "./streaming-text.js";
13
- /** Isolates custom renderers so one extension cannot take down the chat. */
14
- export class AgentKitErrorBoundary extends Component {
15
- state = {
16
- resetKey: this.props.resetKey,
17
- };
18
- static getDerivedStateFromError(error) {
19
- return { error };
20
- }
21
- static getDerivedStateFromProps(props, state) {
22
- return props.resetKey !== state.resetKey
23
- ? { error: undefined, resetKey: props.resetKey }
24
- : null;
25
- }
26
- componentDidCatch(error, info) {
27
- try {
28
- this.props.onError?.(error, info);
29
- }
30
- catch (observerError) {
31
- if (typeof globalThis.reportError === "function") {
32
- globalThis.reportError(observerError);
33
- }
34
- else {
35
- console.error("AgentKit render observer failed.", observerError);
36
- }
37
- }
38
- }
39
- render() {
40
- if (!this.state.error)
41
- return this.props.children;
42
- return typeof this.props.fallback === "function"
43
- ? this.props.fallback(this.state.error)
44
- : this.props.fallback;
45
- }
46
- }
47
- function AgentKitSurfaceBoundary({ surface, resetKey, children, }) {
48
- const { labels, onRenderError, threadId } = useAgentKit();
49
- return (_jsx(AgentKitErrorBoundary, { resetKey: resetKey, fallback: _jsxs("div", { className: "agentkit-error", "data-render-surface": surface, role: "alert", children: [_jsx(IconAlertCircle, { "aria-hidden": "true", className: "agentkit-icon" }), _jsx("span", { children: labels.renderError })] }), onError: (error, info) => onRenderError?.({
50
- error,
51
- surface,
52
- threadId,
53
- componentStack: info.componentStack ?? undefined,
54
- }), children: children }));
55
- }
56
- function AgentKitRegionSlot({ slot: Slot, threadId, children, }) {
57
- return Slot ? _jsx(Slot, { threadId: threadId, children: children }) : children;
58
- }
59
- const allowedAgentProtocols = new Set(["http:", "https:", "mailto:", "blob:"]);
60
- const allowedAgentImageProtocols = new Set(["http:", "https:", "blob:"]);
61
- /** Keeps an unfinished bold delimiter literal while a streamed message grows. */
62
- function escapeIncompleteStrongMarkdown(text) {
63
- const delimiterPositions = new Map();
64
- let inFence = false;
65
- let fenceMarker = "";
66
- let inlineCodeTicks = 0;
67
- let offset = 0;
68
- for (const line of text.split("\n")) {
69
- const trimmed = line.trimStart();
70
- if (!inFence) {
71
- const fenceMatch = /^(?:`{3,}|~{3,})/.exec(trimmed);
72
- if (fenceMatch) {
73
- inFence = true;
74
- fenceMarker = fenceMatch[0].charAt(0).repeat(fenceMatch[0].length);
75
- }
76
- }
77
- if (!inFence) {
78
- for (let index = 0; index < line.length; index += 1) {
79
- if (line[index] === "\\") {
80
- index += 1;
81
- continue;
82
- }
83
- if (line[index] === "`") {
84
- let tickCount = 1;
85
- while (line[index + tickCount] === "`")
86
- tickCount += 1;
87
- inlineCodeTicks = inlineCodeTicks === 0 ? tickCount : 0;
88
- index += tickCount - 1;
89
- continue;
90
- }
91
- if (inlineCodeTicks !== 0)
92
- continue;
93
- const delimiter = line.slice(index, index + 2);
94
- if (delimiter !== "**" && delimiter !== "__")
95
- continue;
96
- const positions = delimiterPositions.get(delimiter) ?? [];
97
- positions.push(offset + index);
98
- delimiterPositions.set(delimiter, positions);
99
- index += 1;
100
- }
101
- }
102
- else {
103
- const closeMatch = /^(?:`{3,}|~{3,})\s*$/.exec(trimmed);
104
- if (closeMatch &&
105
- closeMatch[0].charAt(0) === fenceMarker.charAt(0) &&
106
- closeMatch[0].trim().length >= fenceMarker.length) {
107
- inFence = false;
108
- fenceMarker = "";
109
- }
110
- }
111
- offset += line.length + 1;
112
- }
113
- const unmatched = [...delimiterPositions.entries()].flatMap(([delimiter, positions]) => positions.length % 2 === 1
114
- ? [{ delimiter, position: positions.at(-1) }]
115
- : []);
116
- return unmatched
117
- .sort((left, right) => right.position - left.position)
118
- .reduce((value, { delimiter, position }) => `${value.slice(0, position)}\\${delimiter[0]}\\${delimiter[1]}${value.slice(position + 2)}`, text);
119
- }
120
- /** Rejects executable and embedded-data URLs before they reach a default anchor. */
121
- export function safeAgentHref(href) {
122
- if (!href)
123
- return undefined;
124
- try {
125
- const parsed = new URL(href, "https://agentkit.invalid");
126
- return allowedAgentProtocols.has(parsed.protocol) ? href : undefined;
127
- }
128
- catch (error) {
129
- if (error instanceof TypeError)
130
- return undefined;
131
- throw error;
132
- }
133
- }
134
- const AgentMarkdownBlock = memo(function AgentMarkdownBlock({ text, }) {
135
- const safeText = escapeIncompleteStrongMarkdown(text);
136
- return (_jsx(ReactMarkdown, { skipHtml: true, remarkPlugins: [remarkGfm], components: {
137
- a: ({ children, href }) => {
138
- const safeHref = safeAgentHref(href);
139
- return safeHref ? (_jsx("a", { href: safeHref, children: children })) : (_jsx("span", { children: children }));
140
- },
141
- }, children: safeText }));
142
- });
143
- function AgentMarkdown({ text }) {
144
- const { completedBlocks, tail } = useMemo(() => splitMarkdownBlocks(text), [text]);
145
- const blocks = useMemo(() => (tail ? [...completedBlocks, tail] : completedBlocks), [completedBlocks, tail]);
146
- return (_jsx("div", { className: "agentkit-markdown", "data-format": "markdown", children: blocks.map((block, index) => (_jsx(AgentMarkdownBlock, { text: block }, index))) }));
147
- }
148
- /** Keeps participant avatars on image-capable, non-executable URL schemes. */
149
- export function safeAgentImageSrc(src) {
150
- if (!src)
151
- return undefined;
152
- try {
153
- const parsed = new URL(src, "https://agentkit.invalid");
154
- return allowedAgentImageProtocols.has(parsed.protocol) ? src : undefined;
155
- }
156
- catch (error) {
157
- if (error instanceof TypeError)
158
- return undefined;
159
- throw error;
160
- }
161
- }
162
- function createInvocationId(widgetId, actionId) {
163
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
164
- return `${widgetId}:${actionId}:${crypto.randomUUID()}`;
165
- }
166
- return `${widgetId}:${actionId}:${Date.now().toString(36)}`;
167
- }
168
- function activityIcon(kind) {
169
- const props = { "aria-hidden": true, className: "agentkit-icon" };
170
- switch (kind) {
171
- case "status":
172
- return _jsx(IconActivity, { ...props });
173
- case "reasoning":
174
- case "model":
175
- return _jsx(IconBrain, { ...props });
176
- case "search":
177
- return _jsx(IconSearch, { ...props });
178
- case "read":
179
- return _jsx(IconBook2, { ...props });
180
- case "write":
181
- case "edit":
182
- return _jsx(IconCode, { ...props });
183
- case "command":
184
- return _jsx(IconTerminal2, { ...props });
185
- case "check":
186
- return _jsx(IconCircleCheck, { ...props });
187
- case "mcp":
188
- case "connection":
189
- return _jsx(IconPlugConnected, { ...props });
190
- case "navigation":
191
- return _jsx(IconGitBranch, { ...props });
192
- case "delegation":
193
- return _jsx(IconArrowFork, { ...props });
194
- case "approval":
195
- return _jsx(IconChecklist, { ...props });
196
- default:
197
- return _jsx(IconTool, { ...props });
198
- }
199
- }
200
- function interactionIcon(kind) {
201
- const props = { "aria-hidden": true, className: "agentkit-icon" };
202
- switch (kind) {
203
- case "started":
204
- case "resumed":
205
- return _jsx(IconPlayerPlay, { ...props });
206
- case "messaged":
207
- return _jsx(IconMessage, { ...props });
208
- case "delegated":
209
- return _jsx(IconArrowFork, { ...props });
210
- case "paused":
211
- return _jsx(IconPlayerPause, { ...props });
212
- case "completed":
213
- return _jsx(IconCircleCheck, { ...props });
214
- case "failed":
215
- return _jsx(IconAlertCircle, { ...props });
216
- case "closed":
217
- return _jsx(IconX, { ...props });
218
- default:
219
- return _jsx(IconActivity, { ...props });
220
- }
221
- }
222
- export function AgentObjectReferenceView({ value: object, }) {
223
- const { onOpenObject } = useAgentKit();
224
- const content = (_jsxs(_Fragment, { children: [_jsx("span", { className: "agentkit-object-label", children: object.label }), typeof object.metadata?.added === "number" ? (_jsxs("span", { className: "agentkit-diff-added", children: ["+", object.metadata.added] })) : null, typeof object.metadata?.removed === "number" ? (_jsxs("span", { className: "agentkit-diff-removed", children: ["\u2212", object.metadata.removed] })) : null] }));
225
- return onOpenObject ? (_jsx("button", { type: "button", className: "agentkit-object", onClick: () => onOpenObject(object), children: content })) : (_jsx("span", { className: "agentkit-object", children: content }));
226
- }
227
- export function AgentParticipantView({ value: agent, }) {
228
- return (_jsx(AgentIdentityChip, { id: agent.id, name: agent.name, kind: agent.kind, status: agent.status }));
229
- }
230
- function AgentIdentityChip({ id, name, kind, status, }) {
231
- return (_jsx("span", { className: "agentkit-agent", "data-agent-id": id, "data-agent-kind": kind, "data-status": status, children: _jsx("span", { className: "agentkit-agent-name", children: name }) }));
232
- }
233
- function defaultInteractionLabel(interaction, labels) {
234
- if (interaction.label)
235
- return interaction.label;
236
- switch (interaction.kind) {
237
- case "started":
238
- return labels.agentStarted;
239
- case "resumed":
240
- return labels.agentResumed;
241
- case "messaged":
242
- return labels.agentMessaged;
243
- case "delegated":
244
- return labels.agentDelegated;
245
- case "paused":
246
- return labels.agentPaused;
247
- case "completed":
248
- return labels.agentCompleted;
249
- case "failed":
250
- return labels.agentFailed;
251
- case "closed":
252
- return labels.agentClosed;
253
- default:
254
- return interaction.kind;
255
- }
256
- }
257
- export function AgentInteractionItem({ value: interaction, threadId, }) {
258
- const { slots, registry, labels } = useAgentKit();
259
- const thread = useAgentThread(threadId);
260
- const agent = thread.agents[interaction.agentId];
261
- const target = interaction.targetAgentId
262
- ? thread.agents[interaction.targetAgentId]
263
- : undefined;
264
- const AgentRenderer = agent
265
- ? (registry.agents?.[agent.kind ?? ""] ??
266
- slots.agent ??
267
- AgentParticipantView)
268
- : undefined;
269
- const TargetRenderer = target
270
- ? (registry.agents?.[target.kind ?? ""] ??
271
- slots.agent ??
272
- AgentParticipantView)
273
- : undefined;
274
- const ObjectRenderer = slots.object ?? AgentObjectReferenceView;
275
- const object = interaction.object ?? interaction.source;
276
- return (_jsxs("div", { className: "agentkit-agent-interaction", "data-kind": interaction.kind, "data-scope": interaction.scope, children: [interactionIcon(interaction.kind), agent && AgentRenderer ? (_jsx(AgentRenderer, { value: agent, threadId: threadId })) : (_jsx(AgentIdentityChip, { id: interaction.agentId, name: interaction.agentId })), _jsx("span", { className: "agentkit-agent-interaction-label", children: defaultInteractionLabel(interaction, labels) }), target && TargetRenderer ? (_jsx(TargetRenderer, { value: target, threadId: threadId })) : interaction.targetAgentId ? (_jsx(AgentIdentityChip, { id: interaction.targetAgentId, name: interaction.targetAgentId })) : null, object ? (_jsx(ObjectRenderer, { value: object, threadId: threadId })) : interaction.detail ? (_jsx("span", { className: "agentkit-agent-interaction-detail", children: interaction.detail })) : null] }));
277
- }
278
- export function AgentActivityItem({ value: activity, threadId, }) {
279
- const { slots, registry, labels } = useAgentKit();
280
- const thread = useAgentThread();
281
- const [open, setOpen] = useState(false);
282
- const expandable = Boolean(activity.summary?.length);
283
- const agent = activity.agentId ? thread.agents[activity.agentId] : undefined;
284
- const AgentRenderer = agent
285
- ? (registry.agents?.[agent.kind ?? ""] ??
286
- slots.agent ??
287
- AgentParticipantView)
288
- : undefined;
289
- const ObjectRenderer = slots.object ?? AgentObjectReferenceView;
290
- return (_jsxs("div", { className: "agentkit-activity-item", "data-activity-kind": activity.kind, "data-status": activity.status, children: [_jsxs("div", { className: "agentkit-activity-row", children: [activityIcon(activity.kind), agent && AgentRenderer ? (_jsx(AgentRenderer, { value: agent, threadId: threadId })) : activity.agentId ? (_jsx(AgentIdentityChip, { id: activity.agentId, name: activity.agentId })) : null, _jsx("span", { className: "agentkit-activity-label", children: activity.label }), activity.object ? (_jsx(ObjectRenderer, { value: activity.object, threadId: threadId })) : null, !activity.object && activity.source ? (_jsx(ObjectRenderer, { value: activity.source, threadId: threadId })) : !activity.object && activity.detail ? (_jsx("span", { className: "agentkit-activity-detail", children: activity.detail })) : null, expandable ? (_jsx("button", { type: "button", className: "agentkit-activity-disclosure", "aria-expanded": open, "aria-label": open ? labels.collapseActivity : labels.expandActivity, onClick: () => setOpen((current) => !current), children: _jsx(IconChevronRight, { "aria-hidden": "true", className: "agentkit-icon agentkit-disclosure-icon", "data-open": open ? "true" : "false" }) })) : null] }), open ? (_jsx("div", { className: "agentkit-activity-summary", children: activity.summary?.map((part, index) => (_jsx(AgentMessagePartView, { value: part, threadId: threadId }, `${activity.id}-summary-${index}`))) })) : null] }));
291
- }
292
- function objectReferenceIdentity(object) {
293
- return object
294
- ? [object.kind, object.id, object.uri ?? ""].join("\u0000")
295
- : "";
296
- }
297
- function activityClusterIdentity(activity) {
298
- return [
299
- activity.kind,
300
- activity.label.trim(),
301
- activity.status,
302
- activity.agentId ?? "",
303
- objectReferenceIdentity(activity.object),
304
- objectReferenceIdentity(activity.source),
305
- ].join("\u0001");
306
- }
307
- function RepeatedActivityCluster({ activities, threadId, }) {
308
- const { slots, registry } = useAgentKit();
309
- const thread = useAgentThread();
310
- const [open, setOpen] = useState(false);
311
- const activity = activities[0];
312
- if (!activity)
313
- return null;
314
- const agent = activity.agentId ? thread.agents[activity.agentId] : undefined;
315
- const AgentRenderer = agent
316
- ? (registry.agents?.[agent.kind ?? ""] ??
317
- slots.agent ??
318
- AgentParticipantView)
319
- : undefined;
320
- const ObjectRenderer = slots.object ?? AgentObjectReferenceView;
321
- return (_jsxs("details", { className: "agentkit-activity-cluster", "data-activity-kind": activity.kind, open: open, children: [_jsxs("summary", { className: "agentkit-activity-row", onClick: (event) => {
322
- event.preventDefault();
323
- setOpen((current) => !current);
324
- }, children: [activityIcon(activity.kind), agent && AgentRenderer ? (_jsx(AgentRenderer, { value: agent, threadId: threadId })) : activity.agentId ? (_jsx(AgentIdentityChip, { id: activity.agentId, name: activity.agentId })) : null, _jsx("span", { className: "agentkit-activity-label", children: activity.label }), activity.object ? (_jsx(ObjectRenderer, { value: activity.object, threadId: threadId })) : null, !activity.object && activity.source ? (_jsx(ObjectRenderer, { value: activity.source, threadId: threadId })) : null, _jsxs("span", { className: "agentkit-activity-cluster-count", children: ["\u00D7", activities.length] }), _jsx(IconChevronRight, { "aria-hidden": "true", className: "agentkit-icon agentkit-disclosure-icon", "data-open": open ? "true" : "false" })] }), _jsx("div", { className: "agentkit-activity-cluster-items", children: activities.map((item) => (_jsx(AgentActivityItem, { value: item, threadId: threadId }, item.id))) })] }));
325
- }
326
- function toolToActivity(tool) {
327
- return {
328
- id: tool.id,
329
- kind: inferAgentActivityKind(tool.name),
330
- label: tool.name,
331
- status: tool.status === "completed"
332
- ? "completed"
333
- : tool.status === "running"
334
- ? "running"
335
- : tool.status,
336
- detail: typeof tool.output === "string"
337
- ? tool.output
338
- : typeof tool.input === "string"
339
- ? tool.input
340
- : undefined,
341
- };
342
- }
343
- function sequenceInRange(sequence, { afterSequence, throughSequence }) {
344
- return ((afterSequence === undefined || sequence > afterSequence) &&
345
- (throughSequence === undefined || sequence <= throughSequence));
346
- }
347
- export function formatAgentKitDuration(ms, units = {}) {
348
- const hour = units.hour ?? "h";
349
- const minute = units.minute ?? "m";
350
- const second = units.second ?? "s";
351
- const totalSeconds = Math.max(0, Math.round(ms / 1_000));
352
- if (totalSeconds < 60)
353
- return `${Math.max(1, totalSeconds)}${second}`;
354
- const minutes = Math.floor(totalSeconds / 60);
355
- const seconds = totalSeconds % 60;
356
- if (minutes < 60) {
357
- return seconds === 0
358
- ? `${minutes}${minute}`
359
- : `${minutes}${minute} ${seconds}${second}`;
360
- }
361
- const hours = Math.floor(minutes / 60);
362
- const remainingMinutes = minutes % 60;
363
- return remainingMinutes === 0
364
- ? `${hours}${hour}`
365
- : `${hours}${hour} ${remainingMinutes}${minute}`;
366
- }
367
- function workEventIdentity(event) {
368
- if (event.type === "activity.started" ||
369
- event.type === "activity.updated" ||
370
- event.type === "activity.completed") {
371
- return `activity:${event.activity.id}`;
372
- }
373
- if (event.type === "tool.started" || event.type === "tool.updated") {
374
- return `tool:${event.toolCall.id}`;
375
- }
376
- if (event.type === "tool.delta")
377
- return `tool:${event.toolCallId}`;
378
- if (event.type === "agent.interaction") {
379
- return `interaction:${event.interaction.id}`;
380
- }
381
- if (event.type === "task.created" ||
382
- event.type === "task.updated" ||
383
- event.type === "task.completed") {
384
- return `task:${event.task.id}`;
385
- }
386
- return undefined;
387
- }
388
- function firstWorkEvents(events) {
389
- const seen = new Set();
390
- return events.filter((event) => {
391
- const identity = workEventIdentity(event);
392
- if (!identity)
393
- return false;
394
- const scopedIdentity = `${event.runId}:${identity}`;
395
- if (seen.has(scopedIdentity))
396
- return false;
397
- seen.add(scopedIdentity);
398
- return true;
399
- });
400
- }
401
- function messageEventHasVisibleAssistantOutput(event, assistantMessageIds) {
402
- if (event.type === "message.delta") {
403
- return (event.text.trim().length > 0 && assistantMessageIds.has(event.messageId));
404
- }
405
- if (event.type !== "message.created" && event.type !== "message.completed") {
406
- return false;
407
- }
408
- return (event.message.role === "assistant" &&
409
- event.message.parts.some((part) => {
410
- if (part.type === "reasoning")
411
- return false;
412
- if (part.type === "text")
413
- return part.text.trim().length > 0;
414
- return true;
415
- }));
416
- }
417
- export function AgentActivityGroup({ runId, afterSequence, throughSequence, excludeAgentActivities = false, }) {
418
- const { threadId, slots, registry, labels } = useAgentKit();
419
- const thread = useAgentThread();
420
- const runEvents = runId
421
- ? thread.events.filter((event) => event.runId === runId)
422
- : thread.events;
423
- const activityMap = new Map();
424
- const toolMap = new Map();
425
- const firstSequence = new Map();
426
- const itemOrder = [];
427
- const seenItems = new Set();
428
- const remember = (id, sequence) => {
429
- if (seenItems.has(id))
430
- return;
431
- seenItems.add(id);
432
- itemOrder.push(id);
433
- firstSequence.set(id, sequence);
434
- };
435
- for (const event of runEvents) {
436
- if (event.type === "activity.started" ||
437
- event.type === "activity.updated" ||
438
- event.type === "activity.completed") {
439
- if (excludeAgentActivities && event.activity.agentId)
440
- continue;
441
- activityMap.set(event.activity.id, thread.activities[event.activity.id] ?? event.activity);
442
- remember(event.activity.id, event.sequence);
443
- }
444
- if (event.type === "tool.started" || event.type === "tool.updated") {
445
- toolMap.set(event.toolCall.id, thread.tools[event.toolCall.id] ?? event.toolCall);
446
- remember(event.toolCall.id, event.sequence);
447
- }
448
- if (event.type === "tool.delta") {
449
- const tool = thread.tools[event.toolCallId];
450
- if (tool)
451
- toolMap.set(tool.id, tool);
452
- remember(event.toolCallId, event.sequence);
453
- }
454
- }
455
- const items = itemOrder.flatMap((id) => {
456
- const sequence = firstSequence.get(id);
457
- if (sequence === undefined ||
458
- !sequenceInRange(sequence, { afterSequence, throughSequence })) {
459
- return [];
460
- }
461
- const activity = activityMap.get(id);
462
- if (activity)
463
- return [activity];
464
- const tool = toolMap.get(id);
465
- return tool ? [toolToActivity(tool)] : [];
466
- });
467
- const durableToolResults = items.flatMap((activity) => {
468
- const tool = toolMap.get(activity.id);
469
- const Renderer = tool
470
- ? (registry.tools?.[tool.name] ?? slots.tool)
471
- : undefined;
472
- return tool && Renderer && tool.status !== "running"
473
- ? [{ tool, Renderer }]
474
- : [];
475
- });
476
- const durableToolResultIds = new Set(durableToolResults.map(({ tool }) => tool.id));
477
- const activityItems = items.filter((activity) => !durableToolResultIds.has(activity.id));
478
- const running = items.some((item) => item.status === "running");
479
- const run = runId ? thread.runs[runId] : undefined;
480
- const segmentStartedEvent = firstWorkEvents(runEvents).find((event) => sequenceInRange(event.sequence, { afterSequence, throughSequence }));
481
- const startedAt = afterSequence === undefined && run?.startedAt
482
- ? Date.parse(run.startedAt)
483
- : segmentStartedEvent
484
- ? Date.parse(segmentStartedEvent.occurredAt)
485
- : Number.NaN;
486
- const responseStartedEvent = throughSequence === undefined
487
- ? undefined
488
- : runEvents.find((event) => event.sequence === throughSequence);
489
- const completedAt = responseStartedEvent
490
- ? Date.parse(responseStartedEvent.occurredAt)
491
- : run?.completedAt
492
- ? Date.parse(run.completedAt)
493
- : Number.NaN;
494
- const durationMs = Number.isFinite(startedAt) && Number.isFinite(completedAt)
495
- ? Math.max(0, completedAt - startedAt)
496
- : undefined;
497
- const activelyWorking = throughSequence === undefined &&
498
- (run === undefined ? running : run.status === "running");
499
- const visiblyRunning = throughSequence === undefined && activelyWorking && running;
500
- const [elapsedAt, setElapsedAt] = useState();
501
- useEffect(() => {
502
- if (!activelyWorking || !Number.isFinite(startedAt))
503
- return;
504
- const update = () => setElapsedAt(Date.now());
505
- update();
506
- const interval = globalThis.setInterval(update, 1_000);
507
- return () => globalThis.clearInterval(interval);
508
- }, [activelyWorking, startedAt]);
509
- const activeDurationMs = activelyWorking && elapsedAt !== undefined && Number.isFinite(startedAt)
510
- ? Math.max(0, elapsedAt - startedAt)
511
- : undefined;
512
- const completedRunSummary = throughSequence !== undefined ||
513
- (items.length > 0 && !running) ||
514
- runEvents.some((event) => event.runId === runId &&
515
- (event.type === "run.completed" ||
516
- event.type === "run.failed" ||
517
- event.type === "run.cancelled")) ||
518
- (afterSequence === undefined &&
519
- run !== undefined &&
520
- ["completed", "failed", "cancelled"].includes(run.status));
521
- const [open, setOpen] = useState(visiblyRunning);
522
- const previousRunningRef = useRef(visiblyRunning);
523
- useEffect(() => {
524
- if (previousRunningRef.current === visiblyRunning)
525
- return;
526
- previousRunningRef.current = visiblyRunning;
527
- setOpen(visiblyRunning);
528
- }, [visiblyRunning]);
529
- if (items.length === 0)
530
- return null;
531
- const displayGroups = [];
532
- for (const activity of activityItems) {
533
- const sourceTool = toolMap.get(activity.id);
534
- const hasCustomToolRenderer = Boolean(sourceTool &&
535
- !activityMap.has(activity.id) &&
536
- (registry.tools?.[sourceTool.name] ?? slots.tool));
537
- const hasCustomActivityRenderer = Boolean(registry.activities?.[activity.kind] ?? slots.activity);
538
- const previous = displayGroups.at(-1);
539
- if (!hasCustomToolRenderer &&
540
- !hasCustomActivityRenderer &&
541
- previous &&
542
- activityClusterIdentity(previous[0]) ===
543
- activityClusterIdentity(activity)) {
544
- previous.push(activity);
545
- }
546
- else {
547
- displayGroups.push([activity]);
548
- }
549
- }
550
- const formatDuration = (ms) => formatAgentKitDuration(ms, {
551
- hour: labels.durationHourShort,
552
- minute: labels.durationMinuteShort,
553
- second: labels.durationSecondShort,
554
- });
555
- const summaryLabel = activelyWorking
556
- ? activeDurationMs !== undefined && activeDurationMs >= 1_000
557
- ? labels.workingFor.replace("{{duration}}", formatDuration(activeDurationMs))
558
- : labels.working
559
- : completedRunSummary
560
- ? durationMs !== undefined && durationMs >= 1_000
561
- ? labels.workedFor.replace("{{duration}}", formatDuration(durationMs))
562
- : labels.worked
563
- : labels.activities;
564
- return (_jsxs(_Fragment, { children: [durableToolResults.length ? (_jsx("div", { "data-agentkit-tool-results": "true", children: durableToolResults.map(({ tool, Renderer }) => (_jsx(Renderer, { value: tool, threadId: threadId }, tool.id))) })) : null, displayGroups.length ? (_jsxs("details", { className: "agentkit-activities", "data-running": visiblyRunning ? "true" : undefined, open: open, children: [_jsxs("summary", { className: "agentkit-activities-summary", onClick: (event) => {
565
- event.preventDefault();
566
- setOpen((current) => !current);
567
- }, children: [_jsx(IconActivity, { "aria-hidden": "true", className: "agentkit-icon" }), _jsx("span", { className: "agentkit-activities-label", children: summaryLabel }), !completedRunSummary ? (_jsx("span", { className: "agentkit-activities-count", children: activityItems.length })) : null, _jsx(IconChevronRight, { "aria-hidden": "true", className: "agentkit-icon agentkit-summary-chevron" })] }), _jsx("div", { className: "agentkit-activities-list", children: displayGroups.map((activities) => {
568
- const activity = activities[0];
569
- if (activities.length > 1) {
570
- return (_jsx(RepeatedActivityCluster, { activities: activities, threadId: threadId }, `cluster:${activity.id}`));
571
- }
572
- const sourceTool = toolMap.get(activity.id);
573
- const ToolRenderer = sourceTool
574
- ? (registry.tools?.[sourceTool.name] ?? slots.tool)
575
- : undefined;
576
- if (sourceTool && ToolRenderer && !activityMap.has(activity.id)) {
577
- return (_jsx(ToolRenderer, { value: sourceTool, threadId: threadId }, sourceTool.id));
578
- }
579
- const Renderer = registry.activities?.[activity.kind] ??
580
- slots.activity ??
581
- AgentActivityItem;
582
- return (_jsx(Renderer, { value: activity, threadId: threadId }, activity.id));
583
- }) })] })) : null] }));
584
- }
585
- export function AgentCollaborationFeed({ runId, afterSequence, throughSequence, }) {
586
- const { threadId, slots, registry, labels } = useAgentKit();
587
- const thread = useAgentThread();
588
- const entries = new Map();
589
- const firstSequence = new Map();
590
- const order = [];
591
- const remember = (key, sequence, entry) => {
592
- if (!entries.has(key)) {
593
- order.push(key);
594
- firstSequence.set(key, sequence);
595
- }
596
- entries.set(key, entry);
597
- };
598
- for (const event of thread.events) {
599
- if (runId && event.runId !== runId)
600
- continue;
601
- if (event.type === "agent.interaction") {
602
- remember(`interaction:${event.interaction.id}`, event.sequence, {
603
- type: "interaction",
604
- value: event.interaction,
605
- });
606
- }
607
- if ((event.type === "activity.started" ||
608
- event.type === "activity.updated" ||
609
- event.type === "activity.completed") &&
610
- event.activity.agentId) {
611
- remember(`activity:${event.activity.id}`, event.sequence, {
612
- type: "activity",
613
- value: event.activity,
614
- });
615
- }
616
- }
617
- const selectedOrder = order.filter((key) => {
618
- const sequence = firstSequence.get(key);
619
- return (sequence !== undefined &&
620
- sequenceInRange(sequence, { afterSequence, throughSequence }));
621
- });
622
- if (selectedOrder.length === 0)
623
- return null;
624
- return (_jsx("section", { className: "agentkit-agent-feed", "aria-label": labels.agents, "data-agent-collaboration-feed": "true", children: selectedOrder.map((key) => {
625
- const entry = entries.get(key);
626
- if (!entry)
627
- return null;
628
- if (entry.type === "interaction") {
629
- const Renderer = registry.agentInteractions?.[entry.value.kind] ??
630
- slots.agentInteraction ??
631
- AgentInteractionItem;
632
- return _jsx(Renderer, { value: entry.value, threadId: threadId }, key);
633
- }
634
- const Renderer = registry.activities?.[entry.value.kind] ??
635
- slots.activity ??
636
- AgentActivityItem;
637
- return _jsx(Renderer, { value: entry.value, threadId: threadId }, key);
638
- }) }));
639
- }
640
- export function AgentTaskItem({ value: task, threadId, }) {
641
- const { slots, registry } = useAgentKit();
642
- const thread = useAgentThread();
643
- const ObjectRenderer = slots.object ?? AgentObjectReferenceView;
644
- const agent = task.assignedAgentId
645
- ? thread.agents[task.assignedAgentId]
646
- : undefined;
647
- const AgentRenderer = agent
648
- ? (registry.agents?.[agent.kind ?? ""] ??
649
- slots.agent ??
650
- AgentParticipantView)
651
- : undefined;
652
- const progress = task.progress
653
- ? `${task.progress.completed}/${task.progress.total}`
654
- : undefined;
655
- return (_jsxs("div", { className: "agentkit-task-row", "data-status": task.status, children: [_jsx(IconChecklist, { "aria-hidden": "true", className: "agentkit-icon" }), agent && AgentRenderer ? (_jsx(AgentRenderer, { value: agent, threadId: threadId })) : task.assignedAgentId ? (_jsx(AgentIdentityChip, { id: task.assignedAgentId, name: task.assignedAgentId })) : null, _jsx("span", { className: "agentkit-task-title", children: task.title }), task.object ? (_jsx(ObjectRenderer, { value: task.object, threadId: threadId })) : null, !task.object && task.source ? (_jsx(ObjectRenderer, { value: task.source, threadId: threadId })) : !task.object && task.detail ? (_jsx("span", { className: "agentkit-task-detail", children: task.detail })) : null, progress ? (_jsx("span", { className: "agentkit-task-progress", children: progress })) : null] }));
656
- }
657
- export function AgentTaskGroup({ runId, afterSequence, throughSequence, }) {
658
- const { threadId, slots, registry, labels } = useAgentKit();
659
- const thread = useAgentThread();
660
- const ids = [];
661
- const tasks = new Map();
662
- const firstSequence = new Map();
663
- for (const event of thread.events) {
664
- if (runId && event.runId !== runId)
665
- continue;
666
- if (event.type !== "task.created" &&
667
- event.type !== "task.updated" &&
668
- event.type !== "task.completed") {
669
- continue;
670
- }
671
- if (!tasks.has(event.task.id)) {
672
- ids.push(event.task.id);
673
- firstSequence.set(event.task.id, event.sequence);
674
- }
675
- tasks.set(event.task.id, thread.tasks[event.task.id] ?? event.task);
676
- }
677
- const selectedIds = ids.filter((id) => {
678
- const sequence = firstSequence.get(id);
679
- return (sequence !== undefined &&
680
- sequenceInRange(sequence, { afterSequence, throughSequence }));
681
- });
682
- if (selectedIds.length === 0)
683
- return null;
684
- return (_jsxs("details", { className: "agentkit-tasks", children: [_jsxs("summary", { className: "agentkit-tasks-summary", children: [_jsx(IconChecklist, { "aria-hidden": "true", className: "agentkit-icon" }), _jsx("span", { children: labels.tasks }), _jsx("span", { className: "agentkit-tasks-count", children: selectedIds.length }), _jsx(IconChevronRight, { "aria-hidden": "true", className: "agentkit-icon agentkit-summary-chevron" })] }), _jsx("div", { className: "agentkit-tasks-list", children: selectedIds.map((id) => {
685
- const task = tasks.get(id);
686
- if (!task)
687
- return null;
688
- const Renderer = registry.tasks?.[task.kind ?? task.status] ??
689
- slots.task ??
690
- AgentTaskItem;
691
- return _jsx(Renderer, { value: task, threadId: threadId }, id);
692
- }) })] }));
693
- }
694
- export function AgentApprovalPrompt({ request, runId, }) {
695
- const { labels, requestComposerFocus, threadId } = useAgentKit();
696
- const control = useAgentKitControl();
697
- const [selected, setSelected] = useState([]);
698
- const [input, setInput] = useState("");
699
- const [otherSelected, setOtherSelected] = useState(false);
700
- const [other, setOther] = useState("");
701
- const resolution = useAgentKitMutation((response) => control.resolveApproval(runId, request.id, response));
702
- const options = request.options ?? [];
703
- const choice = request.kind === "choice" || options.length > 0;
704
- const allowOther = choice && request.allowOther !== false;
705
- const simpleApproval = !choice && options.length === 0 && !request.input;
706
- const toggle = (id) => {
707
- if (!request.allowMultiple)
708
- setOtherSelected(false);
709
- setSelected((current) => request.allowMultiple
710
- ? current.includes(id)
711
- ? current.filter((item) => item !== id)
712
- : [...current, id]
713
- : [id]);
714
- };
715
- const toggleOther = () => {
716
- setOtherSelected((current) => !current);
717
- if (!request.allowMultiple)
718
- setSelected([]);
719
- };
720
- const resolve = (response) => void resolution
721
- .execute(response)
722
- .catch(() => undefined)
723
- .finally(() => requestComposerFocus(threadId));
724
- return (_jsxs(Surface, { as: "section", className: "agentkit-approval", elevation: "low", padding: "none", "aria-labelledby": `${request.id}-title`, children: [_jsxs("div", { className: "agentkit-approval-copy", children: [_jsx("h3", { id: `${request.id}-title`, children: request.title }), request.description ? _jsx("p", { children: request.description }) : null] }), options.length || allowOther ? (_jsxs("div", { className: "agentkit-approval-options", children: [options.map((option) => (_jsxs("button", { type: "button", "aria-pressed": selected.includes(option.id), "data-kind": option.kind, disabled: resolution.pending, onClick: () => toggle(option.id), children: [_jsx("span", { children: option.label }), option.description ? _jsx("small", { children: option.description }) : null] }, option.id))), allowOther ? (_jsx("button", { type: "button", "aria-pressed": otherSelected, "data-kind": "other", disabled: resolution.pending, onClick: toggleOther, children: _jsx("span", { children: labels.approvalOther }) })) : null] })) : null, allowOther && otherSelected ? (_jsx(TextField, { autoFocus: true, className: "agentkit-approval-input", label: labels.approvalOther, disabled: resolution.pending, placeholder: labels.approvalOtherPlaceholder, value: other, onChange: setOther })) : null, request.input ? (_jsx(TextField, { className: "agentkit-approval-input", label: request.input.label, type: request.input.type === "url" ? "url" : "text", inputMode: request.input.type === "number" ? "decimal" : undefined, required: request.input.required, disabled: resolution.pending, placeholder: request.input.placeholder, value: input, onChange: setInput })) : null, _jsx("div", { className: "agentkit-approval-footer", children: simpleApproval ? (_jsxs(_Fragment, { children: [_jsx(ActionButton, { emphasis: "outline", size: "compact", disabled: resolution.pending, onPress: () => resolve({ decision: "deny", optionIds: ["deny"] }), children: labels.approvalDeny }), _jsx(ActionButton, { className: "agentkit-primary-button", intent: "primary", size: "compact", disabled: resolution.pending, pending: resolution.pending, onPress: () => resolve({ decision: "approve", optionIds: ["approve"] }), children: labels.approvalApprove })] })) : (_jsx(ActionButton, { className: "agentkit-primary-button", intent: "primary", size: "compact", disabled: (selected.length === 0 && !otherSelected && !request.input) ||
725
- (otherSelected && other.trim().length === 0) ||
726
- (request.input?.required && input.trim().length === 0) ||
727
- resolution.pending, pending: resolution.pending, onPress: () => resolve({
728
- decision: "approve",
729
- optionIds: selected.length ? selected : undefined,
730
- other: otherSelected ? other.trim() : undefined,
731
- input: request.input
732
- ? { [request.input.id]: input }
733
- : undefined,
734
- }), children: labels.approvalSubmit })) }), resolution.error ? (_jsx("p", { className: "agentkit-command-error", role: "alert", children: resolution.error.message })) : null] }));
735
- }
736
- export function AgentConnectionRequestCard({ request, runId, }) {
737
- const { labels, onConnectionRequest, requestComposerFocus, threadId } = useAgentKit();
738
- const control = useAgentKitControl();
739
- const resolution = useAgentKitMutation(async (decision) => {
740
- const response = decision === "decline"
741
- ? { status: "declined" }
742
- : await onConnectionRequest?.(request);
743
- if (!response) {
744
- throw new Error(labels.connectionFailed);
745
- }
746
- await control.resolveConnectionRequest(runId, request.id, response);
747
- return response;
748
- });
749
- const resolve = (decision) => void resolution
750
- .execute(decision)
751
- .catch(() => undefined)
752
- .finally(() => requestComposerFocus(threadId));
753
- const terminal = request.status === "connected" ||
754
- request.status === "declined" ||
755
- request.status === "failed";
756
- const title = request.status === "connected"
757
- ? labels.connectionConnected
758
- : request.status === "failed"
759
- ? labels.connectionFailed
760
- : request.reason === "admin_required"
761
- ? labels.connectionAdminRequired
762
- : `${labels.connectionConnect} ${request.provider}`;
763
- return (_jsxs(Surface, { as: "section", className: "agentkit-connection-request", elevation: "low", padding: "none", "data-status": request.status, "aria-labelledby": `${request.id}-title`, children: [_jsxs("div", { className: "agentkit-connection-request-copy", children: [_jsx(IconPlugConnected, { "aria-hidden": "true", className: "agentkit-icon" }), _jsxs("div", { children: [_jsx("h3", { id: `${request.id}-title`, children: title }), request.detail ? _jsx("p", { children: request.detail }) : null] })] }), !terminal && request.reason !== "admin_required" ? (_jsxs("div", { className: "agentkit-connection-request-actions", children: [_jsx(ActionButton, { emphasis: "outline", size: "compact", disabled: resolution.pending, onPress: () => resolve("decline"), children: labels.connectionNotNow }), _jsx(ActionButton, { className: "agentkit-primary-button", intent: "primary", size: "compact", disabled: resolution.pending || !onConnectionRequest, pending: resolution.pending, onPress: () => resolve("connect"), children: resolution.pending
764
- ? labels.connectionConnecting
765
- : labels.connectionConnect })] })) : request.status === "failed" && onConnectionRequest ? (_jsx("div", { className: "agentkit-connection-request-actions", children: _jsx(ActionButton, { emphasis: "outline", size: "compact", disabled: resolution.pending, pending: resolution.pending, onPress: () => resolve("connect"), children: labels.connectionRetry }) })) : null, resolution.error ? (_jsxs("div", { className: "agentkit-command-error", role: "alert", children: [_jsx(IconAlertCircle, { "aria-hidden": "true", className: "agentkit-icon" }), _jsx("span", { children: resolution.error.message })] })) : null] }));
766
- }
767
- function AgentWidgetActionButton({ widget, action, threadId, }) {
768
- const control = useAgentKitControl(threadId);
769
- const invocation = useAgentKitMutation(() => control.invokeAction({
770
- id: createInvocationId(widget.id, action.id),
771
- action: action.action ?? "",
772
- threadId,
773
- widgetId: widget.id,
774
- itemId: action.id,
775
- payload: action.payload,
776
- }), threadId);
777
- return (_jsxs("div", { className: "agentkit-widget-action", children: [_jsx(ActionButton, { className: "agentkit-widget-action-button", intent: action.kind === "primary"
778
- ? "primary"
779
- : action.kind === "danger"
780
- ? "danger"
781
- : "neutral", emphasis: action.kind === "primary" ? "solid" : "outline", size: "compact", disabled: action.disabled || !action.action || invocation.pending, pending: invocation.pending, onPress: () => void invocation.execute().catch(() => undefined), children: action.label }), invocation.error ? (_jsx("span", { className: "agentkit-command-error", role: "alert", children: invocation.error.message })) : null] }));
782
- }
783
- export function AgentWidgetView({ value: widget, threadId, }) {
784
- if (!widget.title &&
785
- typeof widget.data !== "string" &&
786
- !widget.actions?.length) {
787
- return null;
788
- }
789
- const titleId = `${widget.id}-title`;
790
- return (_jsx("section", { className: "agentkit-widget-shell", "data-widget-kind": widget.kind, "aria-labelledby": widget.title ? titleId : undefined, children: _jsxs(Surface, { className: "agentkit-widget", elevation: "low", padding: "none", children: [widget.title ? _jsx("h3", { id: titleId, children: widget.title }) : null, typeof widget.data === "string" ? _jsx("p", { children: widget.data }) : null, widget.actions?.length ? (_jsx("div", { className: "agentkit-widget-actions", children: widget.actions.map((action) => (_jsx(AgentWidgetActionButton, { widget: widget, action: action, threadId: threadId }, action.id))) })) : null] }) }));
791
- }
792
- function AgentMentionIcon({ icon }) {
793
- const props = { size: 14, className: "agentkit-mention-icon" };
794
- switch (icon) {
795
- case "folder":
796
- return _jsx(IconFolder, { ...props });
797
- case "document":
798
- return _jsx(IconFileText, { ...props });
799
- case "form":
800
- return _jsx(IconCheckbox, { ...props });
801
- case "email":
802
- return _jsx(IconMail, { ...props });
803
- case "user":
804
- return _jsx(IconUser, { ...props });
805
- case "deck":
806
- return _jsx(IconPresentation, { ...props });
807
- case "agent":
808
- return _jsx(IconMessageChatbot, { ...props });
809
- case "file":
810
- return _jsx(IconFile, { ...props });
811
- default:
812
- return _jsx(IconStack2, { ...props });
813
- }
814
- }
815
- function renderUserMessageText(text) {
816
- const richMatches = Array.from(text.matchAll(/@\[([^\]|]+)\|([^\]]+)\]/g));
817
- if (richMatches.length) {
818
- const parts = [];
819
- let lastIndex = 0;
820
- richMatches.forEach((match, index) => {
821
- const start = match.index ?? 0;
822
- if (start > lastIndex)
823
- parts.push(text.slice(lastIndex, start));
824
- const label = match[1] ?? "";
825
- parts.push(_jsxs("span", { className: "agentkit-mention", "data-mention-label": label, children: [_jsx(AgentMentionIcon, { icon: match[2] ?? "" }), _jsx("span", { className: "agentkit-mention-label", children: label })] }, `rich-mention:${start}:${index}`));
826
- lastIndex = start + match[0].length;
827
- });
828
- if (lastIndex < text.length)
829
- parts.push(text.slice(lastIndex));
830
- return parts;
831
- }
832
- const plainMatches = Array.from(text.matchAll(/(^|\s)@(\w+)/g));
833
- if (!plainMatches.length)
834
- return [text];
835
- const parts = [];
836
- let lastIndex = 0;
837
- plainMatches.forEach((match, index) => {
838
- const matchIndex = match.index ?? 0;
839
- const start = matchIndex + (match[1]?.length ?? 0);
840
- const end = matchIndex + match[0].length;
841
- if (start > lastIndex)
842
- parts.push(text.slice(lastIndex, start));
843
- const label = match[2] ?? "";
844
- parts.push(_jsxs("span", { className: "agentkit-mention agentkit-mention--plain", "data-mention-label": label, children: ["@", label] }, `plain-mention:${start}:${index}`));
845
- lastIndex = end;
846
- });
847
- if (lastIndex < text.length)
848
- parts.push(text.slice(lastIndex));
849
- return parts;
850
- }
851
- function AgentUserMessageText({ text }) {
852
- const { labels } = useAgentKit();
853
- const [expanded, setExpanded] = useState(false);
854
- const [expandable, setExpandable] = useState(false);
855
- const contentRef = useRef(null);
856
- const content = useMemo(() => renderUserMessageText(text), [text]);
857
- useLayoutEffect(() => {
858
- const element = contentRef.current;
859
- if (!element)
860
- return;
861
- const measure = () => setExpandable(element.scrollHeight > 200);
862
- measure();
863
- if (typeof ResizeObserver === "undefined")
864
- return;
865
- const observer = new ResizeObserver(measure);
866
- observer.observe(element);
867
- return () => observer.disconnect();
868
- }, [content]);
869
- return (_jsxs("div", { className: "agentkit-user-message-text", children: [_jsx("div", { className: "agentkit-user-message-text-clip", "data-clamped": !expanded && expandable ? "true" : undefined, children: _jsx("div", { ref: contentRef, className: "agentkit-user-message-text-content", children: content }) }), expandable ? (_jsx("button", { type: "button", className: "agentkit-message-expand", "aria-expanded": expanded, onClick: () => setExpanded((value) => !value), children: expanded ? labels.collapseMessage : labels.expandMessage })) : null] }));
870
- }
871
- function AgentImageAttachmentPreview({ src, alt, }) {
872
- const { labels } = useAgentKit();
873
- const [open, setOpen] = useState(false);
874
- return (_jsx(Dialog, { open: open, onOpenChange: setOpen, title: alt || labels.imagePreview, closeLabel: labels.closePreview, size: "fullscreen", trigger: _jsx("button", { type: "button", className: "agentkit-image-attachment-trigger", "aria-label": labels.previewAttachment.replace("{{name}}", alt), title: alt, children: _jsx("img", { src: src, alt: alt }) }), children: _jsx("div", { className: "agentkit-image-preview", children: _jsx("img", { src: src, alt: alt, draggable: false }) }) }));
875
- }
876
- export function AgentMessagePartView({ value: part, threadId, active = false, resetKey = `${threadId}:${part.type}`, userMessage = false, }) {
877
- const { labels, slots, registry } = useAgentKit();
878
- const RegisteredRenderer = registry.messageParts?.[part.type];
879
- if (RegisteredRenderer) {
880
- return (_jsx(RegisteredRenderer, { value: part, threadId: threadId, active: active, resetKey: resetKey }));
881
- }
882
- switch (part.type) {
883
- case "text": {
884
- const Renderer = userMessage ? undefined : slots.text;
885
- return Renderer ? (_jsx(Renderer, { value: part, threadId: threadId, active: active, resetKey: resetKey })) : userMessage ? (_jsx(AgentUserMessageText, { text: part.text })) : part.format === "markdown" ? (_jsx(AgentStreamingText, { text: part.text, active: active, resetKey: resetKey, children: (visibleText) => _jsx(AgentMarkdown, { text: visibleText }) })) : (_jsx("p", { "data-format": "plain", children: _jsx(AgentStreamingText, { text: part.text, active: active, resetKey: resetKey }) }));
886
- }
887
- case "reasoning": {
888
- const Renderer = slots.reasoning;
889
- return Renderer ? (_jsx(Renderer, { value: part, threadId: threadId })) : part.visibility === "hidden" ? null : (_jsxs("details", { className: "agentkit-reasoning", "data-active": active ? "true" : undefined, children: [_jsxs("summary", { className: "agentkit-reasoning-summary", children: [activityIcon("reasoning"), _jsx("span", { className: "agentkit-reasoning-label", children: part.label ?? labels.reasoning }), _jsx(IconChevronRight, { "aria-hidden": "true", className: "agentkit-icon agentkit-reasoning-chevron" })] }), _jsx("div", { className: "agentkit-reasoning-content", children: _jsx("p", { children: _jsx(AgentStreamingText, { text: part.text, active: active, resetKey: resetKey }) }) })] }));
890
- }
891
- case "citation": {
892
- const Renderer = slots.citation;
893
- if (Renderer)
894
- return _jsx(Renderer, { value: part, threadId: threadId });
895
- const href = safeAgentHref(part.url);
896
- return href ? _jsx("a", { href: href, children: part.title }) : _jsx("span", { children: part.title });
897
- }
898
- case "annotation": {
899
- const Renderer = slots.annotation;
900
- if (Renderer)
901
- return _jsx(Renderer, { value: part, threadId: threadId });
902
- const href = safeAgentHref(part.annotation.url);
903
- return href ? (_jsx("a", { href: href, children: part.annotation.label })) : (_jsx("span", { children: part.annotation.label }));
904
- }
905
- case "file": {
906
- const Renderer = slots.file;
907
- if (Renderer)
908
- return _jsx(Renderer, { value: part, threadId: threadId });
909
- const href = safeAgentHref(part.url);
910
- if (userMessage && part.mediaType?.startsWith("image/") && href) {
911
- return _jsx(AgentImageAttachmentPreview, { src: href, alt: part.name });
912
- }
913
- if (userMessage && part.name.startsWith("pasted-text-")) {
914
- return href ? (_jsxs("a", { className: "agentkit-file agentkit-file--pasted-text", href: href, download: part.name, title: part.name, children: [_jsx(IconFileText, { "aria-hidden": "true", className: "agentkit-icon" }), _jsx("span", { children: labels.pastedText })] })) : (_jsxs("span", { className: "agentkit-file agentkit-file--pasted-text", children: [_jsx(IconFileText, { "aria-hidden": "true", className: "agentkit-icon" }), _jsx("span", { children: labels.pastedText })] }));
915
- }
916
- return href ? (_jsxs("a", { className: "agentkit-file", href: href, download: part.name, children: [_jsx(IconFile, { "aria-hidden": "true", className: "agentkit-icon" }), _jsx("span", { children: part.name })] })) : (_jsxs("span", { className: "agentkit-file", children: [_jsx(IconFile, { "aria-hidden": "true", className: "agentkit-icon" }), _jsx("span", { children: part.name })] }));
917
- }
918
- case "widget": {
919
- const Renderer = registry.widgets?.[part.widget.kind] ?? slots.widget ?? AgentWidgetView;
920
- return _jsx(Renderer, { value: part.widget, threadId: threadId });
921
- }
922
- case "data": {
923
- const Renderer = slots.data;
924
- return Renderer ? _jsx(Renderer, { value: part, threadId: threadId }) : null;
925
- }
926
- default:
927
- return null;
928
- }
929
- }
930
- /** Resolves the stable server request identity without exposing local UI ids. */
931
- export function resolveAgentMessageRequestId(message, events) {
932
- const metadata = message.metadata;
933
- for (const source of [metadata?.custom, metadata]) {
934
- for (const key of ["requestId", "runId"]) {
935
- const value = source?.[key];
936
- if (typeof value === "string" && value)
937
- return value;
938
- }
939
- }
940
- for (let index = events.length - 1; index >= 0; index -= 1) {
941
- const event = events[index];
942
- const matches = (event.type === "message.created" ||
943
- event.type === "message.completed") &&
944
- event.message.id === message.id;
945
- if (matches ||
946
- ((event.type === "message.delta" || event.type === "reasoning.delta") &&
947
- event.messageId === message.id)) {
948
- return event.runId;
949
- }
950
- }
951
- return undefined;
952
- }
953
- function resolveAgentMessageRunId(message, events) {
954
- const metadata = message.metadata;
955
- for (const source of [metadata?.custom, metadata]) {
956
- const value = source?.runId;
957
- if (typeof value === "string" && value)
958
- return value;
959
- }
960
- for (let index = events.length - 1; index >= 0; index -= 1) {
961
- const event = events[index];
962
- if (((event.type === "message.created" ||
963
- event.type === "message.completed") &&
964
- event.message.id === message.id) ||
965
- ((event.type === "message.delta" || event.type === "reasoning.delta") &&
966
- event.messageId === message.id)) {
967
- return event.runId;
968
- }
969
- }
970
- return undefined;
971
- }
972
- const AgentMessageEditContext = createContext(null);
973
- function messageText(message) {
974
- return message.parts
975
- .filter((part) => part.type === "text")
976
- .map((part) => part.text)
977
- .join("\n");
978
- }
979
- function messageRunOptions(message) {
980
- const metadata = message.metadata ?? {};
981
- const effort = metadata.reasoningEffort ?? metadata.effort;
982
- const reasoningEffort = [
983
- "none",
984
- "minimal",
985
- "low",
986
- "medium",
987
- "high",
988
- "xhigh",
989
- ].includes(String(effort))
990
- ? String(effort)
991
- : undefined;
992
- return {
993
- ...(typeof metadata.agentId === "string"
994
- ? { agentId: metadata.agentId }
995
- : {}),
996
- ...(typeof metadata.model === "string" ? { model: metadata.model } : {}),
997
- ...(typeof (metadata.requestMode ?? metadata.mode) === "string"
998
- ? { mode: String(metadata.requestMode ?? metadata.mode) }
999
- : {}),
1000
- ...(reasoningEffort ? { reasoningEffort } : {}),
1001
- metadata,
1002
- };
1003
- }
1004
- function findPreviousMessage(messages, messageId) {
1005
- const index = messages.findIndex((message) => message.id === messageId);
1006
- return index < 0 ? null : messages[index - 1];
1007
- }
1008
- function findPreviousUserMessage(messages, messageId) {
1009
- const index = messages.findIndex((message) => message.id === messageId);
1010
- if (index < 0)
1011
- return undefined;
1012
- for (let previous = index - 1; previous >= 0; previous -= 1) {
1013
- const message = messages[previous];
1014
- if (message?.role === "user")
1015
- return message;
1016
- }
1017
- return undefined;
1018
- }
1019
- async function forkAndResubmitMessage({ controller, threadId, messages, sourceMessage, text, attachments, options, metadata, onThreadForked, messageUnavailable, }) {
1020
- const previousMessage = findPreviousMessage(messages, sourceMessage.id);
1021
- if (previousMessage === null) {
1022
- throw new Error(messageUnavailable);
1023
- }
1024
- const forkedThread = await controller.forkThread(threadId, previousMessage?.id);
1025
- onThreadForked?.(forkedThread);
1026
- await controller.sendMessage({
1027
- threadId: forkedThread.id,
1028
- text,
1029
- attachments,
1030
- options,
1031
- metadata,
1032
- });
1033
- }
1034
- export function AgentMessageActions({ value: message, threadId, }) {
1035
- const { controller, labels, slots, onThreadForked, onCopyMessage, branchNavigation, } = useAgentKit();
1036
- const thread = useAgentThread(threadId);
1037
- const feedbackCapability = useAgentCapability("feedback");
1038
- const forkingCapability = useAgentCapability("threadForking");
1039
- const control = useAgentKitControl(threadId);
1040
- const editContext = useContext(AgentMessageEditContext);
1041
- const text = messageText(message);
1042
- const previousUserMessage = message.role === "assistant"
1043
- ? findPreviousUserMessage(thread.messages, message.id)
1044
- : undefined;
1045
- const isLastAssistantMessage = message.role === "assistant" && thread.messages.at(-1)?.id === message.id;
1046
- const [copied, setCopied] = useState(false);
1047
- const [feedback, setFeedback] = useState(null);
1048
- const [feedbackReason, setFeedbackReason] = useState("");
1049
- const [feedbackReasonOpen, setFeedbackReasonOpen] = useState(false);
1050
- const [feedbackReasonSubmitted, setFeedbackReasonSubmitted] = useState(false);
1051
- const copiedTimer = useRef(null);
1052
- const [requestIdCopied, setRequestIdCopied] = useState(false);
1053
- const [actionsMenuOpen, setActionsMenuOpen] = useState(false);
1054
- const requestIdCopiedTimer = useRef(null);
1055
- const requestId = useMemo(() => resolveAgentMessageRequestId(message, thread.events), [message, thread.events]);
1056
- const runId = useMemo(() => resolveAgentMessageRunId(message, thread.events), [message, thread.events]);
1057
- const messageSeq = thread.messages.findIndex((item) => item.id === message.id);
1058
- useEffect(() => () => {
1059
- if (copiedTimer.current)
1060
- clearTimeout(copiedTimer.current);
1061
- if (requestIdCopiedTimer.current)
1062
- clearTimeout(requestIdCopiedTimer.current);
1063
- }, [message.id]);
1064
- const copyAction = useAgentKitMutation(async () => {
1065
- const handled = await onCopyMessage?.({ message, text });
1066
- if (!handled && !(await writeClipboardText(text))) {
1067
- throw new Error(labels.copyUnavailable);
1068
- }
1069
- setCopied(true);
1070
- if (copiedTimer.current)
1071
- clearTimeout(copiedTimer.current);
1072
- copiedTimer.current = setTimeout(() => setCopied(false), 1_400);
1073
- }, `${threadId}:${message.id}:copy`);
1074
- const feedbackAction = useAgentKitMutation(({ value, reason, }) => {
1075
- const options = {
1076
- ...(runId ? { runId } : {}),
1077
- ...(messageSeq >= 0 ? { messageSeq } : {}),
1078
- ...(reason ? { reason } : {}),
1079
- };
1080
- return Object.keys(options).length
1081
- ? control.submitFeedback(message.id, value, options)
1082
- : control.submitFeedback(message.id, value);
1083
- }, `${threadId}:${message.id}:feedback`);
1084
- const branchNavigationAction = useAgentKitMutation(async (direction) => {
1085
- if (direction === "previous")
1086
- await branchNavigation?.onPrevious();
1087
- else
1088
- await branchNavigation?.onNext();
1089
- }, `${threadId}:${message.id}:branch-navigation`);
1090
- const requestIdAction = useAgentKitMutation(async () => {
1091
- if (!requestId)
1092
- throw new Error(labels.requestIdUnavailable);
1093
- if (!(await writeClipboardText(requestId))) {
1094
- throw new Error(labels.copyUnavailable);
1095
- }
1096
- setRequestIdCopied(true);
1097
- if (requestIdCopiedTimer.current)
1098
- clearTimeout(requestIdCopiedTimer.current);
1099
- requestIdCopiedTimer.current = setTimeout(() => {
1100
- setRequestIdCopied(false);
1101
- }, 1_400);
1102
- }, `${threadId}:${message.id}:request-id:${requestId ?? "unavailable"}`);
1103
- const forkAction = useAgentKitMutation(async () => {
1104
- const thread = await control.fork(message.id);
1105
- onThreadForked?.(thread);
1106
- }, `${threadId}:${message.id}:fork`);
1107
- const regenerateAction = useAgentKitMutation(async () => {
1108
- if (!previousUserMessage)
1109
- return;
1110
- await forkAndResubmitMessage({
1111
- controller,
1112
- threadId,
1113
- messages: thread.messages,
1114
- sourceMessage: previousUserMessage,
1115
- text: messageText(previousUserMessage),
1116
- attachments: previousUserMessage.parts.filter((part) => part.type === "file"),
1117
- options: messageRunOptions(previousUserMessage),
1118
- metadata: previousUserMessage.metadata,
1119
- onThreadForked,
1120
- messageUnavailable: labels.messageUnavailable,
1121
- });
1122
- }, `${threadId}:${message.id}:regenerate`);
1123
- const updateFeedback = async (value) => {
1124
- const previous = feedback;
1125
- if (previous === value)
1126
- return;
1127
- setFeedback(value);
1128
- try {
1129
- await feedbackAction.execute({ value });
1130
- }
1131
- catch {
1132
- setFeedback(previous);
1133
- }
1134
- };
1135
- const submitFeedbackReason = async (event) => {
1136
- event.preventDefault();
1137
- const reason = feedbackReason.trim();
1138
- if (!reason)
1139
- return;
1140
- try {
1141
- await feedbackAction.execute({ value: "negative", reason });
1142
- setFeedbackReason("");
1143
- setFeedbackReasonSubmitted(true);
1144
- setFeedbackReasonOpen(false);
1145
- }
1146
- catch {
1147
- // coercion-ok: feedbackAction.error is rendered below and the draft stays available for retry.
1148
- }
1149
- };
1150
- const actionError = copyAction.error ??
1151
- feedbackAction.error ??
1152
- branchNavigationAction.error ??
1153
- requestIdAction.error ??
1154
- forkAction.error ??
1155
- regenerateAction.error;
1156
- return (_jsxs("div", { className: "agentkit-message-actions", children: [message.role === "assistant" ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "agentkit-message-actions-leading", children: [_jsx(IconButton, { label: copied ? labels.copied : labels.copy, icon: copied ? (_jsx(IconCircleCheck, { "aria-hidden": "true" })) : (_jsx(IconCopy, { "aria-hidden": "true" })), size: "compact", pending: copyAction.pending, onPress: () => void copyAction.execute().catch(() => undefined) }), feedbackCapability.visible ? (_jsxs(_Fragment, { children: [_jsx(IconButton, { label: labels.positiveFeedback, icon: _jsx(IconThumbUp, { "aria-hidden": "true" }), size: "compact", "aria-pressed": feedback === "positive", pending: feedbackAction.pending && feedback === "positive", disabled: feedbackAction.pending || !feedbackCapability.enabled, title: feedbackCapability.reason, onPress: () => void updateFeedback("positive") }), _jsx(Popover, { open: feedbackReasonOpen, onOpenChange: (open) => {
1157
- setFeedbackReasonOpen(open);
1158
- if (open)
1159
- setFeedbackReasonSubmitted(false);
1160
- }, placement: "bottom", align: "start", className: "agentkit-feedback-popover", trigger: _jsx(IconButton, { label: labels.negativeFeedback, icon: _jsx(IconThumbDown, { "aria-hidden": "true" }), size: "compact", "aria-pressed": feedback === "negative", pending: feedbackAction.pending && feedback === "negative", disabled: feedbackAction.pending || !feedbackCapability.enabled, title: feedbackCapability.reason, onPress: () => void updateFeedback("negative") }), children: _jsxs("form", { className: "agentkit-feedback-form", onSubmit: submitFeedbackReason, children: [_jsx(TextArea, { label: labels.feedbackWhatWentWrong, value: feedbackReason, onChange: setFeedbackReason, placeholder: labels.feedbackPlaceholder, rows: 3, maxLength: 2_000, disabled: feedbackAction.pending, className: "agentkit-feedback-textarea" }), _jsxs("div", { className: "agentkit-feedback-footer", children: [_jsx("span", { children: labels.feedbackKeyboardHint.replace("{{shortcut}}", typeof navigator !== "undefined" &&
1161
- /Mac|iPhone|iPad/.test(navigator.userAgent)
1162
- ? "⌘"
1163
- : "Ctrl") }), _jsx(ActionButton, { type: "submit", intent: "primary", size: "compact", pending: feedbackAction.pending, disabled: feedbackAction.pending || !feedbackReason.trim(), children: labels.feedbackSubmit })] })] }) }), feedbackReasonSubmitted ? (_jsx("span", { className: "agentkit-visually-hidden", "aria-live": "polite", children: labels.feedbackSubmitted })) : null] })) : null] }), _jsxs("div", { className: "agentkit-message-actions-trailing", children: [isLastAssistantMessage &&
1164
- previousUserMessage &&
1165
- forkingCapability.visible &&
1166
- onThreadForked ? (_jsx(IconButton, { label: labels.regenerateResponse, icon: _jsx(IconRefresh, { "aria-hidden": "true" }), size: "compact", pending: regenerateAction.pending, disabled: regenerateAction.pending ||
1167
- hasActiveAgentRuns(thread) ||
1168
- !forkingCapability.enabled, title: forkingCapability.reason, onPress: () => void regenerateAction.execute().catch(() => undefined) })) : null, branchNavigation && branchNavigation.count > 1 ? (_jsxs("div", { className: "agentkit-branch-navigation", "aria-label": labels.branchPosition
1169
- .replace("{{index}}", String(branchNavigation.index))
1170
- .replace("{{count}}", String(branchNavigation.count)), children: [_jsx(IconButton, { label: labels.previousBranch, icon: _jsx(IconChevronLeft, { "aria-hidden": "true" }), size: "compact", disabled: branchNavigation.index <= 1 ||
1171
- branchNavigationAction.pending, pending: branchNavigationAction.pending, onPress: () => void branchNavigationAction
1172
- .execute("previous")
1173
- .catch(() => undefined) }), _jsx("span", { "aria-live": "polite", children: labels.branchPosition
1174
- .replace("{{index}}", String(branchNavigation.index))
1175
- .replace("{{count}}", String(branchNavigation.count)) }), _jsx(IconButton, { label: labels.nextBranch, icon: _jsx(IconChevronRight, { "aria-hidden": "true" }), size: "compact", disabled: branchNavigation.index >= branchNavigation.count ||
1176
- branchNavigationAction.pending, pending: branchNavigationAction.pending, onPress: () => void branchNavigationAction
1177
- .execute("next")
1178
- .catch(() => undefined) })] })) : null, message.createdAt ? (_jsx("time", { dateTime: message.createdAt, children: labels.formatTimestamp?.(message.createdAt) ??
1179
- new Date(message.createdAt).toLocaleTimeString([], {
1180
- hour: "numeric",
1181
- minute: "2-digit",
1182
- }) })) : null, slots.messageActionsTrailing ? (_jsx(slots.messageActionsTrailing, { value: message, threadId: threadId })) : null, _jsx(Menu, { open: actionsMenuOpen, onOpenChange: (open) => {
1183
- setActionsMenuOpen(open);
1184
- if (!open)
1185
- setRequestIdCopied(false);
1186
- }, placement: "bottom", align: "end", className: "agentkit-message-menu w-48", trigger: _jsx(IconButton, { label: labels.messageActions, icon: _jsx(IconDotsVertical, { "aria-hidden": "true" }), size: "compact", "aria-expanded": actionsMenuOpen, title: labels.messageActions }), items: [
1187
- {
1188
- id: "copy-request-id",
1189
- label: requestIdCopied
1190
- ? labels.copied
1191
- : requestId
1192
- ? labels.copyRequestId
1193
- : labels.requestIdUnavailable,
1194
- icon: requestIdCopied ? (_jsx(IconCircleCheck, { size: 14, "aria-hidden": "true" })) : (_jsx(IconId, { size: 14, "aria-hidden": "true" })),
1195
- disabled: !requestId || requestIdAction.pending,
1196
- },
1197
- ...(forkingCapability.visible && onThreadForked
1198
- ? [
1199
- {
1200
- id: "fork-chat",
1201
- label: (_jsx("span", { title: forkingCapability.reason, children: labels.fork })),
1202
- icon: _jsx(IconGitBranch, { size: 14, "aria-hidden": "true" }),
1203
- disabled: !forkingCapability.enabled || forkAction.pending,
1204
- },
1205
- ]
1206
- : []),
1207
- ], onAction: (id) => {
1208
- if (id === "copy-request-id") {
1209
- void requestIdAction.execute().catch(() => undefined);
1210
- }
1211
- else if (id === "fork-chat") {
1212
- void forkAction.execute().catch(() => undefined);
1213
- }
1214
- } })] })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "agentkit-message-actions-leading", children: [_jsx(IconButton, { label: copied ? labels.copied : labels.copy, icon: copied ? (_jsx(IconCircleCheck, { "aria-hidden": "true" })) : (_jsx(IconCopy, { "aria-hidden": "true" })), size: "compact", pending: copyAction.pending, onPress: () => void copyAction.execute().catch(() => undefined) }), editContext?.enabled &&
1215
- forkingCapability.visible &&
1216
- text.trim() ? (_jsx(IconButton, { label: labels.editMessage, icon: _jsx(IconPencil, { "aria-hidden": "true" }), size: "compact", disabled: hasActiveAgentRuns(thread) || !forkingCapability.enabled, title: forkingCapability.reason, "aria-pressed": editContext.message?.id === message.id, onPress: () => editContext.setMessage(message) })) : null] }), _jsx("div", { className: "agentkit-message-actions-trailing", children: message.createdAt ? (_jsx("time", { dateTime: message.createdAt, children: labels.formatTimestamp?.(message.createdAt) ??
1217
- new Date(message.createdAt).toLocaleTimeString([], {
1218
- hour: "numeric",
1219
- minute: "2-digit",
1220
- }) })) : null })] })), actionError ? (_jsx("span", { className: "agentkit-command-error", role: "alert", children: actionError.message })) : null] }));
1221
- }
1222
- export function AgentMessageView({ value: message, threadId, }) {
1223
- const { labels, slots } = useAgentKit();
1224
- const thread = useAgentThread(threadId);
1225
- const Supplement = slots.messageSupplement;
1226
- const Actions = slots.messageActions ?? AgentMessageActions;
1227
- const embeddedWidgetIds = new Set(message.parts.flatMap((part) => part.type === "widget" ? [part.widget.id] : []));
1228
- const embeddedAnnotationIds = new Set(message.parts.flatMap((part) => part.type === "annotation" ? [part.annotation.id] : []));
1229
- const attachedWidgets = Object.values(thread.widgets).filter((widget) => thread.widgetMessageIds[widget.id] === message.id &&
1230
- !embeddedWidgetIds.has(widget.id));
1231
- const attachedAnnotations = Object.values(thread.annotations).filter((annotation) => thread.annotationMessageIds[annotation.id] === message.id &&
1232
- !embeddedAnnotationIds.has(annotation.id));
1233
- return (_jsxs("article", { className: "agentkit-message", "data-role": message.role, "data-message-id": message.id, "aria-label": message.role === "user" ? labels.you : labels.assistant, "aria-busy": message.status === "streaming", children: [_jsxs("div", { className: "agentkit-message-content", children: [message.parts.map((part, index) => (_jsx(AgentMessagePartView, { value: part, threadId: threadId, active: message.status === "streaming", resetKey: `${threadId}:${message.id}:${index}`, userMessage: message.role === "user" }, `${message.id}-${index}`))), attachedWidgets.map((widget) => (_jsx(AgentMessagePartView, { value: { type: "widget", widget }, threadId: threadId }, widget.id))), attachedAnnotations.length ? (_jsx("div", { className: "agentkit-annotations", children: attachedAnnotations.map((annotation) => (_jsx(AgentMessagePartView, { value: { type: "annotation", annotation }, threadId: threadId }, annotation.id))) })) : null, Supplement ? _jsx(Supplement, { value: message, threadId: threadId }) : null] }), _jsx(Actions, { value: message, threadId: threadId })] }));
1234
- }
1235
- export function AgentRunFailure({ error, runId, threadId, }) {
1236
- const { labels } = useAgentKit();
1237
- return (_jsxs("div", { className: "agentkit-run-failure", "data-error-code": error.code, "data-run-id": runId, "data-thread-id": threadId, role: "alert", children: [_jsx(IconAlertCircle, { "aria-hidden": "true", className: "agentkit-icon" }), _jsxs("div", { className: "agentkit-run-failure-copy", children: [_jsx("strong", { children: labels.runFailed }), _jsx("span", { children: error.message })] })] }));
1238
- }
1239
- export function AgentConnectionErrorView({ error, threadId, recover, recovering, recoveryError, }) {
1240
- const { labels } = useAgentKit();
1241
- return (_jsxs("div", { className: "agentkit-error", "data-error-code": error.code, "data-thread-id": threadId, role: "alert", children: [_jsx("strong", { children: labels.error }), _jsx("span", { children: error.message }), error.retryable ? (_jsx("div", { className: "agentkit-error-actions", children: _jsx(ActionButton, { emphasis: "outline", size: "compact", pending: recovering, onPress: () => void recover().catch(() => undefined), children: labels.reconnect }) })) : null, recoveryError ? (_jsx("span", { className: "agentkit-command-error", children: recoveryError.message })) : null] }));
1242
- }
1243
- function hasActiveRuns(thread) {
1244
- return hasActiveAgentRuns(thread);
1245
- }
1246
- export function AgentKitComposer({ threadId: requestedThreadId, className, queueWhileRunning = true, showModelSelector = true, slashCommands, slashSkills, includeDefaultSlashCommands, includeDefaultSlashSkills, onSlashCommand, plusMenuMode, voiceEnabled = false, autoFocus = true, mode, defaultMode = "act", onModeChange, toolbarSlot, disabled, onDisabledClick, placeholder, initialText, initialTextKey, layoutVariant, availableModels, modelListLoading, selectedModel, selectedEngine, selectedEffort, onConnectProvider, onConnectLocalRuntime, imageModelMenu, onModelChange, onEffortChange, availableAgents, selectedAgent, agentOnly, onAgentChange, onModelSelectorOpenChange, modelStatusChecksEnabled, attachmentsEnabled, onAttachmentRequest, contextButtonTooltipDisabled, onTextChange, contextItems, onRemoveContextItem, extraActionButton, onSubmit: onSubmitOverride, onBeforeSubmit, onAttachmentError, interceptBuildRequestsForBuilder, planModeDisabled, planModeDisabledReason, stopButton, onInspectContextItem, onRetryContextItem, contextMenuItems, attachmentAdapter, inlineTextAttachments, beforeSend, }) {
1247
- const { threadId: contextThreadId, controller, labels, onThreadForked, registerComposerFocus, slots, } = useAgentKit();
1248
- const editContext = useContext(AgentMessageEditContext);
1249
- const editingMessage = editContext?.message ?? null;
1250
- const threadId = requestedThreadId ?? contextThreadId;
1251
- const queueCapability = useAgentCapability("messageQueue");
1252
- const suggestionsCapability = useAgentCapability("suggestions");
1253
- const uploadsCapability = useAgentCapability("uploads");
1254
- const modelSelectionCapability = useAgentCapability("modelSelection");
1255
- const canQueue = queueCapability.enabled;
1256
- const canUpload = uploadsCapability.enabled && attachmentsEnabled !== false;
1257
- // The host opts in through showModelSelector, so a capability the backend
1258
- // never reported keeps the selector instead of silently removing a control
1259
- // the host asked for. Only an explicit denial or outage takes it away.
1260
- const canSelectModel = modelSelectionCapability.state === "unknown" ||
1261
- modelSelectionCapability.enabled;
1262
- const control = useAgentKitControl(threadId);
1263
- const thread = useAgentThread(threadId);
1264
- const command = useAgentKitMutation((execute) => execute(), threadId);
1265
- const composerRef = useRef(null);
1266
- const [uncontrolledMode, setUncontrolledMode] = useState(defaultMode);
1267
- const [attachmentError, setAttachmentError] = useState(null);
1268
- const executionMode = mode ?? uncontrolledMode;
1269
- const active = hasActiveRuns(thread);
1270
- const composerInitialText = editingMessage
1271
- ? messageText(editingMessage)
1272
- : initialText;
1273
- const composerInitialTextKey = editingMessage
1274
- ? `edit:${editingMessage.id}`
1275
- : initialTextKey;
1276
- const focusComposer = useCallback(() => {
1277
- if (!autoFocus)
1278
- return;
1279
- composerRef.current?.focus();
1280
- if (typeof globalThis.requestAnimationFrame === "function") {
1281
- globalThis.requestAnimationFrame(() => composerRef.current?.focus());
1282
- }
1283
- }, [autoFocus]);
1284
- const reportAttachmentError = useCallback((message) => {
1285
- if (onAttachmentError)
1286
- onAttachmentError(message);
1287
- else
1288
- setAttachmentError(message);
1289
- }, [onAttachmentError]);
1290
- const addDroppedAttachment = useCallback(async (file) => {
1291
- try {
1292
- const composer = composerRef.current;
1293
- if (!composer)
1294
- throw new Error(labels.error);
1295
- await composer.addAttachment(file);
1296
- setAttachmentError(null);
1297
- }
1298
- catch (error) {
1299
- const message = error instanceof Error ? error.message : labels.error;
1300
- reportAttachmentError(message);
1301
- throw error;
1302
- }
1303
- }, [labels.error, reportAttachmentError]);
1304
- useEffect(() => {
1305
- const receiveAttachments = (event) => {
1306
- const detail = event.detail;
1307
- if (detail?.threadId !== threadId || !canUpload)
1308
- return;
1309
- void Promise.all(detail.files.map((file) => addDroppedAttachment(file))).catch(() => undefined);
1310
- };
1311
- window.addEventListener("agentkit:attach-files", receiveAttachments);
1312
- return () => window.removeEventListener("agentkit:attach-files", receiveAttachments);
1313
- }, [addDroppedAttachment, canUpload, threadId]);
1314
- const composerToolbarSlot = editingMessage ? (_jsxs(_Fragment, { children: [_jsx(IconButton, { label: labels.cancelEditing, icon: _jsx(IconX, { "aria-hidden": "true" }), size: "compact", onPress: () => {
1315
- composerRef.current?.setText(initialText ?? "");
1316
- editContext?.setMessage(null);
1317
- focusComposer();
1318
- } }), toolbarSlot] })) : (toolbarSlot);
1319
- useEffect(() => registerComposerFocus(threadId, focusComposer), [focusComposer, registerComposerFocus, threadId]);
1320
- // i18n-ignore: These type-level callback property names are not visible copy.
1321
- const submitMessage = async (text, files, references, // i18n-ignore: Type-only callback tuple, not rendered copy.
1322
- options) => {
1323
- const effort = options.effort;
1324
- const contextItemsForSubmission = options.contextItems ?? contextItems;
1325
- const metadata = {
1326
- ...(editingMessage?.metadata ?? {}),
1327
- ...(contextItemsForSubmission === undefined
1328
- ? {}
1329
- : { contextItems: contextItemsForSubmission }),
1330
- ...(references.length ? { references } : {}),
1331
- ...(options.engine ? { engine: options.engine } : {}),
1332
- ...(options.model ? { model: options.model } : {}),
1333
- ...(effort ? { effort } : {}),
1334
- ...(selectedAgent ? { agentId: selectedAgent } : {}),
1335
- mode: executionMode,
1336
- requestMode: executionMode,
1337
- };
1338
- const runOptions = {
1339
- ...(selectedAgent ? { agentId: selectedAgent } : {}),
1340
- model: options.model,
1341
- mode: executionMode,
1342
- reasoningEffort: effort && !["auto", "max"].includes(effort)
1343
- ? effort
1344
- : undefined,
1345
- metadata,
1346
- };
1347
- const uploadFiles = files.map((file) => ({
1348
- name: file.name,
1349
- mediaType: file.type || "application/octet-stream",
1350
- size: file.size,
1351
- body: file,
1352
- }));
1353
- if (editingMessage) {
1354
- if (!onThreadForked) {
1355
- throw new Error(labels.navigationUnavailable);
1356
- }
1357
- const previousMessage = findPreviousMessage(thread.messages, editingMessage.id);
1358
- if (previousMessage === null) {
1359
- throw new Error(labels.messageUnavailable);
1360
- }
1361
- const forkedThread = await controller.forkThread(threadId, previousMessage?.id);
1362
- onThreadForked(forkedThread);
1363
- const uploadedAttachments = uploadFiles.length && canUpload
1364
- ? await controller.uploadFiles(forkedThread.id, uploadFiles)
1365
- : [];
1366
- const previousAttachments = editingMessage.parts.filter((part) => part.type === "file");
1367
- const draft = createAgentKitComposerSubmission({
1368
- threadId: forkedThread.id,
1369
- intent: "immediate",
1370
- text,
1371
- contextItems: contextItemsForSubmission,
1372
- references,
1373
- options: runOptions,
1374
- });
1375
- const payload = Object.freeze({
1376
- ...draft,
1377
- attachments: snapshotComposerValue([
1378
- ...previousAttachments,
1379
- ...uploadedAttachments,
1380
- ]),
1381
- });
1382
- await beforeSend?.(payload);
1383
- const sendMetadata = {
1384
- ...payload.options.metadata,
1385
- ...(payload.references.length
1386
- ? { references: payload.references }
1387
- : {}),
1388
- ...(payload.contextItems === undefined
1389
- ? {}
1390
- : { contextItems: payload.contextItems }),
1391
- };
1392
- await controller.sendMessage({
1393
- threadId: forkedThread.id,
1394
- text: payload.text,
1395
- attachments: [...payload.attachments],
1396
- options: payload.options,
1397
- metadata: sendMetadata,
1398
- });
1399
- editContext?.setMessage(null);
1400
- return;
1401
- }
1402
- if (onSubmitOverride) {
1403
- await onSubmitOverride(text, files, references, options);
1404
- return;
1405
- }
1406
- const submissionThread = controller.getThread(threadId);
1407
- const activeAtSubmit = hasActiveRuns(submissionThread);
1408
- const draft = createAgentKitComposerSubmission({
1409
- threadId,
1410
- intent: canQueue &&
1411
- (options.intent === "queued" || (activeAtSubmit && queueWhileRunning))
1412
- ? "queued"
1413
- : "immediate",
1414
- text,
1415
- contextItems: contextItemsForSubmission,
1416
- references,
1417
- options: runOptions,
1418
- });
1419
- const attachments = uploadFiles.length && canUpload
1420
- ? await control.uploadFiles(uploadFiles)
1421
- : [];
1422
- const payload = Object.freeze({
1423
- ...draft,
1424
- attachments: snapshotComposerValue(attachments),
1425
- });
1426
- await beforeSend?.(payload);
1427
- const sendMetadata = {
1428
- ...payload.options.metadata,
1429
- ...(payload.references.length ? { references: payload.references } : {}),
1430
- ...(payload.contextItems === undefined
1431
- ? {}
1432
- : { contextItems: payload.contextItems }),
1433
- };
1434
- const message = {
1435
- text: payload.text,
1436
- attachments: [...payload.attachments],
1437
- options: payload.options,
1438
- metadata: sendMetadata,
1439
- };
1440
- if (payload.intent === "queued") {
1441
- await control.queueMessage(message);
1442
- }
1443
- else {
1444
- await control.sendMessage(message);
1445
- }
1446
- };
1447
- const prepareHostSubmit = async () => {
1448
- if (disabled) {
1449
- onDisabledClick?.();
1450
- return false;
1451
- }
1452
- if (editingMessage)
1453
- return true;
1454
- return !onBeforeSubmit || (await onBeforeSubmit());
1455
- };
1456
- const steerQueued = !disabled
1457
- ? (item) => void command
1458
- .execute(async () => {
1459
- if (!(await prepareHostSubmit()))
1460
- return;
1461
- if (onSubmitOverride) {
1462
- await submitMessage(item.text, [], [], {
1463
- intent: "immediate",
1464
- attachments: item.attachments,
1465
- });
1466
- await control.removeQueued(item.id);
1467
- }
1468
- else {
1469
- await control.steerQueued(item.id);
1470
- }
1471
- })
1472
- .catch(() => undefined)
1473
- .finally(focusComposer)
1474
- : undefined;
1475
- const supportsQueueReordering = controller.supportsQueuedMessageReordering?.() ?? false;
1476
- const moveQueuedToTop = useAgentKitMutation((messageId) => control.moveQueuedMessageToTop(messageId), `${threadId}:move-queued-to-top`);
1477
- const removeQueued = (item) => void command
1478
- .execute(() => control.removeQueued(item.id))
1479
- .catch(() => undefined)
1480
- .finally(focusComposer);
1481
- const selectSuggestion = (suggestion) => void command
1482
- .execute(async () => {
1483
- if (!(await prepareHostSubmit()))
1484
- return;
1485
- await submitMessage(agentSuggestionPrompt(suggestion), [], [], {
1486
- intent: hasActiveRuns(controller.getThread(threadId)) &&
1487
- queueWhileRunning &&
1488
- canQueue
1489
- ? "queued"
1490
- : "immediate",
1491
- contextItems,
1492
- });
1493
- })
1494
- .catch(() => undefined)
1495
- .finally(focusComposer);
1496
- const Queue = slots.queue;
1497
- const Suggestions = slots.suggestions;
1498
- return (_jsxs("div", { className: `agentkit-composer-stack ${className ?? ""}`, children: [queueCapability.visible ? (Queue ? (_jsx(Queue, { items: thread.queuedMessages, threadId: threadId, active: active, pending: command.pending || Boolean(disabled), onSteer: steerQueued, onRemove: removeQueued })) : (_jsx(MessageQueueDrawer, { variant: "recessed", items: thread.queuedMessages.map((message) => ({
1499
- id: message.id,
1500
- text: message.text,
1501
- images: (message.attachments ?? []).flatMap((attachment) => {
1502
- if (!attachment.mediaType?.startsWith("image/"))
1503
- return [];
1504
- const src = safeAgentImageSrc(attachment.url);
1505
- return src ? [src] : [];
1506
- }),
1507
- })), disabled: command.pending || Boolean(disabled), onSteer: steerQueued
1508
- ? (item) => {
1509
- const message = thread.queuedMessages.find((candidate) => candidate.id === item.id);
1510
- if (message)
1511
- steerQueued(message);
1512
- }
1513
- : undefined, onRemove: (item) => {
1514
- const message = thread.queuedMessages.find((candidate) => candidate.id === item.id);
1515
- if (message)
1516
- removeQueued(message);
1517
- }, labels: {
1518
- region: labels.queue,
1519
- steer: labels.queueSteer,
1520
- steerHint: labels.queueSteerHint,
1521
- remove: labels.queueRemove,
1522
- moreActions: labels.queueMore,
1523
- }, getItemActions: supportsQueueReordering
1524
- ? (item) => [
1525
- {
1526
- id: "move-to-top",
1527
- label: labels.queueMoveToTop,
1528
- icon: _jsx(IconArrowUp, { "aria-hidden": "true", size: 14 }),
1529
- disabled: item.id === thread.queuedMessages[0]?.id ||
1530
- moveQueuedToTop.pending,
1531
- onSelect: (selected) => void command
1532
- .execute(() => moveQueuedToTop.execute(selected.id))
1533
- .catch(() => undefined),
1534
- },
1535
- ]
1536
- : undefined }))) : null, suggestionsCapability.visible && thread.suggestions.length ? (Suggestions ? (_jsx(Suggestions, { suggestions: thread.suggestions, threadId: threadId, pending: command.pending, onSelect: selectSuggestion })) : (_jsx(AgentSuggestionBar, { suggestions: thread.suggestions.map((suggestion) => ({
1537
- ...suggestion,
1538
- disabled: command.pending || Boolean(disabled),
1539
- })), ariaLabel: labels.suggestions, onSelect: selectSuggestion, className: "agentkit-suggestions" }))) : null, _jsx(PromptComposer, { contextItems: contextItems, onRemoveContextItem: onRemoveContextItem, onInspectContextItem: onInspectContextItem, onRetryContextItem: onRetryContextItem, contextMenuItems: contextMenuItems, attachmentAdapter: attachmentAdapter, inlineTextAttachments: inlineTextAttachments, rootClassName: "agentkit-composer", draftScope: `agentkit:${threadId}${editingMessage ? `:edit:${editingMessage.id}` : ""}`, ariaLabel: labels.composerLabel, placeholder: placeholder ?? labels.composerPlaceholder, disabled: disabled, onDisabledClick: onDisabledClick, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, imageModelMenu: imageModelMenu, autoFocus: autoFocus, composerRef: composerRef, submitting: command.pending, willQueue: active && queueWhileRunning && canQueue, showModelSelector: showModelSelector && canSelectModel, availableModels: availableModels, modelListLoading: modelListLoading, selectedModel: selectedModel, selectedEngine: selectedEngine, selectedEffort: selectedEffort, onModelChange: onModelChange, onEffortChange: onEffortChange, availableAgents: availableAgents, selectedAgent: selectedAgent, agentOnly: agentOnly, onAgentChange: onAgentChange, onModelSelectorOpenChange: onModelSelectorOpenChange, modelStatusChecksEnabled: modelStatusChecksEnabled, layoutVariant: layoutVariant, toolbarSlot: composerToolbarSlot, initialText: composerInitialText, initialTextKey: composerInitialTextKey, onTextChange: onTextChange, extraActionButton: extraActionButton, onBeforeSubmit: onBeforeSubmit, onAttachmentError: reportAttachmentError, interceptBuildRequestsForBuilder: interceptBuildRequestsForBuilder, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, stopButton: stopButton, attachmentsEnabled: canUpload, onAttachmentRequest: onAttachmentRequest, contextButtonTooltipDisabled: contextButtonTooltipDisabled, slashCommands: slashCommands, slashSkills: slashSkills, includeDefaultSlashCommands: includeDefaultSlashCommands ?? false, includeDefaultSlashSkills: includeDefaultSlashSkills ?? false, onSlashCommand: onSlashCommand, plusMenuMode: plusMenuMode ?? "full", voiceEnabled: voiceEnabled, execMode: executionMode === "plan" ? "plan" : "build", onExecModeChange: (nextMode) => {
1540
- const next = nextMode === "plan" ? "plan" : "act";
1541
- if (mode === undefined)
1542
- setUncontrolledMode(next);
1543
- onModeChange?.(next);
1544
- }, onSubmit: async (text, files, references, options) => {
1545
- if (disabled) {
1546
- onDisabledClick?.();
1547
- return;
1548
- }
1549
- focusComposer();
1550
- try {
1551
- await command.execute(async () => {
1552
- await submitMessage(text, files, references, options);
1553
- });
1554
- }
1555
- finally {
1556
- focusComposer();
1557
- }
1558
- } }), command.error ? (_jsxs("div", { className: "agentkit-composer-error", role: "alert", children: [_jsx(IconAlertCircle, { "aria-hidden": "true", className: "agentkit-icon" }), _jsx("span", { children: command.error.message })] })) : null, attachmentError ? (_jsxs("div", { className: "agentkit-composer-error", role: "alert", children: [_jsx(IconAlertCircle, { "aria-hidden": "true", className: "agentkit-icon" }), _jsx("span", { children: attachmentError })] })) : null] }));
1559
- }
1560
- const AGENTKIT_TRANSCRIPT_BOTTOM_THRESHOLD = 24;
1561
- const AGENTKIT_TRANSCRIPT_SCROLLBAR_IDLE_MS = 900;
1562
- const AGENTKIT_TRANSCRIPT_SCROLL_KEYS = new Set([
1563
- "ArrowDown",
1564
- "ArrowUp",
1565
- "End",
1566
- "Home",
1567
- "PageDown",
1568
- "PageUp",
1569
- " ",
1570
- ]);
1571
- export function isAgentKitTranscriptNearBottom(element, threshold = AGENTKIT_TRANSCRIPT_BOTTOM_THRESHOLD) {
1572
- return (element.scrollHeight - element.clientHeight - element.scrollTop <= threshold);
1573
- }
1574
- export function AgentKitChat({ title, toolbar, composer = true, composerProps, hasRenderedMessages = false, emptyComposerPlacement = "center", autoScroll = true, className, }) {
1575
- const { threadId, slots, labels, onThreadForked } = useAgentKit();
1576
- const connection = useAgentConnection();
1577
- const uploadsCapability = useAgentCapability("uploads");
1578
- const control = useAgentKitControl(threadId);
1579
- const recovery = useAgentKitMutation(() => control.load(), threadId);
1580
- const thread = useAgentThread(threadId);
1581
- const [pendingEdit, setPendingEdit] = useState(null);
1582
- const editingMessage = pendingEdit?.threadId === threadId ? pendingEdit.message : null;
1583
- const setEditingMessage = useCallback((message) => {
1584
- setPendingEdit(message ? { threadId, message } : null);
1585
- }, [threadId]);
1586
- const messageEditContext = {
1587
- enabled: composer && Boolean(onThreadForked),
1588
- message: editingMessage,
1589
- setMessage: setEditingMessage,
1590
- };
1591
- useEffect(() => setPendingEdit(null), [threadId]);
1592
- const hasConversation = thread.messages.length > 0 || hasRenderedMessages;
1593
- const Message = slots.message ?? AgentMessageView;
1594
- const EmptyState = slots.emptyState;
1595
- const Composer = slots.composer ?? AgentKitComposer;
1596
- const approvalEntries = Object.entries(thread.approvals);
1597
- const connectionRequestEntries = Object.entries(thread.connectionRequests ?? {});
1598
- const ErrorState = slots.connectionError ?? AgentConnectionErrorView;
1599
- const RunFailure = slots.runFailure ?? AgentRunFailure;
1600
- const Approval = slots.approval;
1601
- const ConnectionRequest = slots.connectionRequest;
1602
- const Header = slots.header;
1603
- const Toolbar = slots.toolbar;
1604
- const Transcript = slots.transcript;
1605
- const Footer = slots.footer;
1606
- const transcriptRef = useRef(null);
1607
- const transcriptContentRef = useRef(null);
1608
- const fileDragDepthRef = useRef(0);
1609
- const followsTranscriptRef = useRef(true);
1610
- const transcriptThreadIdRef = useRef(threadId);
1611
- const previousLastMessageIdRef = useRef(undefined);
1612
- const transcriptFollowFrameRef = useRef(undefined);
1613
- const transcriptScrollbarInteractionRef = useRef(false);
1614
- const transcriptScrollbarTimeoutRef = useRef(undefined);
1615
- const [transcriptScrollbarVisible, setTranscriptScrollbarVisible] = useState(false);
1616
- const [transcriptAwayFromBottom, setTranscriptAwayFromBottom] = useState(false);
1617
- const [fileDragActive, setFileDragActive] = useState(false);
1618
- const lastMessage = thread.messages.at(-1);
1619
- const lastEventId = thread.events.at(-1)?.id;
1620
- const connectionError = connection.status === "error" ? connection.error : undefined;
1621
- const canDropFiles = composer &&
1622
- uploadsCapability.enabled &&
1623
- composerProps?.attachmentsEnabled !== false;
1624
- const hasFiles = (event) => Array.from(event.dataTransfer.types).includes("Files");
1625
- const handleFileDragEnter = (event) => {
1626
- if (!canDropFiles || event.defaultPrevented || !hasFiles(event))
1627
- return;
1628
- if (event.relatedTarget instanceof Node &&
1629
- event.currentTarget.contains(event.relatedTarget)) {
1630
- return;
1631
- }
1632
- event.preventDefault();
1633
- fileDragDepthRef.current += 1;
1634
- setFileDragActive(true);
1635
- };
1636
- const handleFileDragOver = (event) => {
1637
- if (!canDropFiles || event.defaultPrevented || !hasFiles(event))
1638
- return;
1639
- event.preventDefault();
1640
- event.dataTransfer.dropEffect = "copy";
1641
- setFileDragActive(true);
1642
- };
1643
- const handleFileDragLeave = (event) => {
1644
- if (!canDropFiles || !hasFiles(event))
1645
- return;
1646
- if (event.relatedTarget instanceof Node &&
1647
- event.currentTarget.contains(event.relatedTarget)) {
1648
- return;
1649
- }
1650
- fileDragDepthRef.current = Math.max(0, fileDragDepthRef.current - 1);
1651
- setFileDragActive(fileDragDepthRef.current > 0);
1652
- };
1653
- const handleFileDrop = (event) => {
1654
- if (!canDropFiles || !hasFiles(event))
1655
- return;
1656
- fileDragDepthRef.current = 0;
1657
- setFileDragActive(false);
1658
- if (event.defaultPrevented)
1659
- return;
1660
- event.preventDefault();
1661
- event.stopPropagation();
1662
- const files = Array.from(event.dataTransfer.files);
1663
- if (!files.length)
1664
- return;
1665
- window.dispatchEvent(new CustomEvent("agentkit:attach-files", {
1666
- detail: { threadId, files },
1667
- }));
1668
- };
1669
- const clearTranscriptScrollbarTimeout = useCallback(() => {
1670
- if (transcriptScrollbarTimeoutRef.current === undefined)
1671
- return;
1672
- clearTimeout(transcriptScrollbarTimeoutRef.current);
1673
- transcriptScrollbarTimeoutRef.current = undefined;
1674
- }, []);
1675
- const hideTranscriptScrollbar = useCallback(() => {
1676
- clearTranscriptScrollbarTimeout();
1677
- transcriptScrollbarInteractionRef.current = false;
1678
- setTranscriptScrollbarVisible(false);
1679
- }, [clearTranscriptScrollbarTimeout]);
1680
- const scheduleTranscriptScrollbarHide = useCallback(() => {
1681
- clearTranscriptScrollbarTimeout();
1682
- transcriptScrollbarTimeoutRef.current = setTimeout(() => {
1683
- transcriptScrollbarTimeoutRef.current = undefined;
1684
- transcriptScrollbarInteractionRef.current = false;
1685
- setTranscriptScrollbarVisible(false);
1686
- }, AGENTKIT_TRANSCRIPT_SCROLLBAR_IDLE_MS);
1687
- }, [clearTranscriptScrollbarTimeout]);
1688
- const revealTranscriptScrollbar = useCallback(() => {
1689
- const transcript = transcriptRef.current;
1690
- if (!transcript || transcript.scrollHeight <= transcript.clientHeight)
1691
- return;
1692
- transcriptScrollbarInteractionRef.current = true;
1693
- setTranscriptScrollbarVisible(true);
1694
- scheduleTranscriptScrollbarHide();
1695
- }, [scheduleTranscriptScrollbarHide]);
1696
- const scrollTranscriptToBottom = useCallback(() => {
1697
- const transcript = transcriptRef.current;
1698
- if (!autoScroll || !transcript || !followsTranscriptRef.current)
1699
- return;
1700
- const nextScrollTop = Math.max(0, transcript.scrollHeight - transcript.clientHeight);
1701
- if (Math.abs(transcript.scrollTop - nextScrollTop) <= 1)
1702
- return;
1703
- transcript.scrollTop = nextScrollTop;
1704
- }, [autoScroll]);
1705
- const returnTranscriptToBottom = useCallback(() => {
1706
- const transcript = transcriptRef.current;
1707
- if (!transcript)
1708
- return;
1709
- followsTranscriptRef.current = true;
1710
- const nextScrollTop = Math.max(0, transcript.scrollHeight - transcript.clientHeight);
1711
- transcript.scrollTop = nextScrollTop;
1712
- setTranscriptAwayFromBottom(false);
1713
- }, []);
1714
- const cancelScheduledTranscriptFollow = useCallback(() => {
1715
- if (transcriptFollowFrameRef.current === undefined)
1716
- return;
1717
- if (typeof globalThis.cancelAnimationFrame === "function") {
1718
- globalThis.cancelAnimationFrame(transcriptFollowFrameRef.current);
1719
- }
1720
- transcriptFollowFrameRef.current = undefined;
1721
- }, []);
1722
- const scheduleTranscriptFollow = useCallback(() => {
1723
- if (!autoScroll || transcriptFollowFrameRef.current !== undefined)
1724
- return;
1725
- if (typeof globalThis.requestAnimationFrame !== "function") {
1726
- scrollTranscriptToBottom();
1727
- return;
1728
- }
1729
- transcriptFollowFrameRef.current = globalThis.requestAnimationFrame(() => {
1730
- transcriptFollowFrameRef.current = undefined;
1731
- scrollTranscriptToBottom();
1732
- });
1733
- }, [autoScroll, scrollTranscriptToBottom]);
1734
- useEffect(() => () => {
1735
- clearTranscriptScrollbarTimeout();
1736
- cancelScheduledTranscriptFollow();
1737
- }, [cancelScheduledTranscriptFollow, clearTranscriptScrollbarTimeout]);
1738
- useLayoutEffect(() => {
1739
- if (transcriptThreadIdRef.current !== threadId) {
1740
- transcriptThreadIdRef.current = threadId;
1741
- previousLastMessageIdRef.current = undefined;
1742
- followsTranscriptRef.current = true;
1743
- setTranscriptAwayFromBottom(false);
1744
- hideTranscriptScrollbar();
1745
- }
1746
- const previousLastMessageId = previousLastMessageIdRef.current;
1747
- previousLastMessageIdRef.current = lastMessage?.id;
1748
- if (!autoScroll)
1749
- return;
1750
- if (lastMessage?.role === "user" &&
1751
- lastMessage.id !== previousLastMessageId) {
1752
- followsTranscriptRef.current = true;
1753
- hideTranscriptScrollbar();
1754
- }
1755
- scrollTranscriptToBottom();
1756
- }, [
1757
- autoScroll,
1758
- hideTranscriptScrollbar,
1759
- lastEventId,
1760
- lastMessage?.id,
1761
- lastMessage?.role,
1762
- scrollTranscriptToBottom,
1763
- threadId,
1764
- ]);
1765
- useLayoutEffect(() => {
1766
- const transcript = transcriptRef.current;
1767
- const content = transcriptContentRef.current;
1768
- if (!autoScroll ||
1769
- !transcript ||
1770
- !content ||
1771
- typeof ResizeObserver === "undefined") {
1772
- return;
1773
- }
1774
- const observer = new ResizeObserver(scheduleTranscriptFollow);
1775
- observer.observe(transcript);
1776
- observer.observe(content);
1777
- return () => {
1778
- observer.disconnect();
1779
- cancelScheduledTranscriptFollow();
1780
- };
1781
- }, [
1782
- autoScroll,
1783
- cancelScheduledTranscriptFollow,
1784
- scheduleTranscriptFollow,
1785
- threadId,
1786
- ]);
1787
- const handleTranscriptScroll = useCallback(() => {
1788
- const transcript = transcriptRef.current;
1789
- if (!transcript)
1790
- return;
1791
- const nearBottom = isAgentKitTranscriptNearBottom(transcript);
1792
- if (nearBottom || transcriptScrollbarInteractionRef.current) {
1793
- followsTranscriptRef.current = nearBottom;
1794
- }
1795
- setTranscriptAwayFromBottom(!nearBottom);
1796
- if (transcriptScrollbarInteractionRef.current) {
1797
- scheduleTranscriptScrollbarHide();
1798
- }
1799
- }, [scheduleTranscriptScrollbarHide]);
1800
- const handleTranscriptPointerDown = useCallback((event) => {
1801
- const bounds = event.currentTarget.getBoundingClientRect();
1802
- const edgeDistance = Math.min(Math.abs(event.clientX - bounds.left), Math.abs(bounds.right - event.clientX));
1803
- if (edgeDistance <= 12)
1804
- revealTranscriptScrollbar();
1805
- }, [revealTranscriptScrollbar]);
1806
- const handleTranscriptKeyDown = useCallback((event) => {
1807
- if (AGENTKIT_TRANSCRIPT_SCROLL_KEYS.has(event.key)) {
1808
- revealTranscriptScrollbar();
1809
- }
1810
- }, [revealTranscriptScrollbar]);
1811
- const messageRunIds = useMemo(() => {
1812
- const result = new Map();
1813
- for (const event of thread.events) {
1814
- if (event.type === "message.created" ||
1815
- event.type === "message.completed") {
1816
- result.set(event.message.id, event.runId);
1817
- }
1818
- if (event.type === "message.delta" || event.type === "reasoning.delta") {
1819
- result.set(event.messageId, event.runId);
1820
- }
1821
- }
1822
- return result;
1823
- }, [thread.events]);
1824
- const messageBoundarySequences = useMemo(() => {
1825
- const firstVisibleSequence = new Map();
1826
- const lastTextDeltaSequence = new Map();
1827
- const assistantMessageIds = new Set(thread.messages
1828
- .filter((message) => message.role === "assistant")
1829
- .map((message) => message.id));
1830
- for (const event of thread.events) {
1831
- if (!messageEventHasVisibleAssistantOutput(event, assistantMessageIds)) {
1832
- continue;
1833
- }
1834
- const messageId = event.type === "message.created" || event.type === "message.completed"
1835
- ? event.message.id
1836
- : event.type === "message.delta"
1837
- ? event.messageId
1838
- : undefined;
1839
- if (!messageId)
1840
- continue;
1841
- if (!firstVisibleSequence.has(messageId)) {
1842
- firstVisibleSequence.set(messageId, event.sequence);
1843
- }
1844
- if (event.type === "message.delta") {
1845
- lastTextDeltaSequence.set(messageId, event.sequence);
1846
- }
1847
- }
1848
- const result = new Map();
1849
- for (const [messageId, sequence] of firstVisibleSequence) {
1850
- result.set(messageId, lastTextDeltaSequence.get(messageId) ?? sequence);
1851
- }
1852
- return result;
1853
- }, [thread.events, thread.messages]);
1854
- const lastAssistantMessagesByRun = useMemo(() => {
1855
- const result = new Map();
1856
- for (const message of thread.messages) {
1857
- const runId = messageRunIds.get(message.id);
1858
- const sequence = messageBoundarySequences.get(message.id);
1859
- if (runId && message.role === "assistant" && sequence !== undefined) {
1860
- result.set(runId, { id: message.id, sequence });
1861
- }
1862
- }
1863
- return result;
1864
- }, [messageBoundarySequences, messageRunIds, thread.messages]);
1865
- const failedRuns = useMemo(() => {
1866
- const result = new Map();
1867
- for (const run of Object.values(thread.runs)) {
1868
- if (run.status === "failed" && run.error) {
1869
- result.set(run.id, run.error);
1870
- }
1871
- }
1872
- return result;
1873
- }, [thread.runs]);
1874
- const runWorkStarts = useMemo(() => firstWorkEvents(thread.events), [thread.events]);
1875
- const hasRunWork = (runId, afterSequence, throughSequence) => runWorkStarts.some((event) => event.runId === runId &&
1876
- sequenceInRange(event.sequence, { afterSequence, throughSequence }));
1877
- const pendingRunIds = Array.from(new Set(runWorkStarts
1878
- .filter((event) => {
1879
- const boundary = lastAssistantMessagesByRun.get(event.runId);
1880
- return boundary === undefined || event.sequence > boundary.sequence;
1881
- })
1882
- .map((event) => event.runId)));
1883
- const pendingRuns = new Set(pendingRunIds);
1884
- const renderRunFailure = (runId) => {
1885
- const error = failedRuns.get(runId);
1886
- return error ? (_jsx(RunFailure, { error: error, runId: runId, threadId: threadId })) : null;
1887
- };
1888
- const renderRunWork = ({ runId, anchor, afterSequence, throughSequence, }) => (_jsxs(AgentKitSurfaceBoundary, { surface: "activity", resetKey: `${runId}:${anchor}:${thread.events.length}`, children: [_jsx(AgentCollaborationFeed, { runId: runId, afterSequence: afterSequence, throughSequence: throughSequence }), _jsx(AgentTaskGroup, { runId: runId, afterSequence: afterSequence, throughSequence: throughSequence }), _jsx(AgentActivityGroup, { runId: runId, afterSequence: afterSequence, throughSequence: throughSequence, excludeAgentActivities: true })] }, `run-work:${runId}:${anchor}`));
1889
- const transcriptItems = [];
1890
- const previousAssistantByRun = new Map();
1891
- for (const message of thread.messages) {
1892
- const runId = messageRunIds.get(message.id);
1893
- const sequence = messageBoundarySequences.get(message.id);
1894
- const isAssistantBoundary = message.role === "assistant" &&
1895
- runId !== undefined &&
1896
- sequence !== undefined;
1897
- const previousAssistant = isAssistantBoundary && runId
1898
- ? previousAssistantByRun.get(runId)
1899
- : undefined;
1900
- if (isAssistantBoundary &&
1901
- runId &&
1902
- hasRunWork(runId, previousAssistant?.sequence, sequence)) {
1903
- transcriptItems.push(renderRunWork({
1904
- runId,
1905
- anchor: previousAssistant?.id ?? "start",
1906
- afterSequence: previousAssistant?.sequence,
1907
- throughSequence: sequence,
1908
- }));
1909
- }
1910
- transcriptItems.push(_jsx(AgentKitSurfaceBoundary, { surface: "message", resetKey: `${message.id}:${thread.events.length}`, children: _jsx(Message, { value: message, threadId: threadId }) }, `message:${message.id}`));
1911
- if (isAssistantBoundary && runId) {
1912
- previousAssistantByRun.set(runId, { id: message.id, sequence });
1913
- }
1914
- const concludesRun = isAssistantBoundary &&
1915
- runId !== undefined &&
1916
- lastAssistantMessagesByRun.get(runId)?.id === message.id;
1917
- if (concludesRun && !pendingRuns.has(runId) && failedRuns.has(runId)) {
1918
- transcriptItems.push(_jsx(AgentKitSurfaceBoundary, { surface: "activity", resetKey: `${runId}:${thread.events.length}`, children: renderRunFailure(runId) }, `run-failure:${runId}`));
1919
- }
1920
- }
1921
- for (const runId of pendingRunIds) {
1922
- const boundary = lastAssistantMessagesByRun.get(runId);
1923
- transcriptItems.push(renderRunWork({
1924
- runId,
1925
- anchor: boundary?.id ?? "start",
1926
- afterSequence: boundary?.sequence,
1927
- }));
1928
- if (failedRuns.has(runId)) {
1929
- transcriptItems.push(_jsx(AgentKitSurfaceBoundary, { surface: "activity", resetKey: `${runId}:${thread.events.length}`, children: renderRunFailure(runId) }, `run-failure:${runId}`));
1930
- }
1931
- }
1932
- for (const [runId] of failedRuns) {
1933
- if (lastAssistantMessagesByRun.has(runId) || pendingRuns.has(runId)) {
1934
- continue;
1935
- }
1936
- transcriptItems.push(_jsx(AgentKitSurfaceBoundary, { surface: "activity", resetKey: `${runId}:${thread.events.length}`, children: renderRunFailure(runId) }, `run-failure:${runId}`));
1937
- }
1938
- return (_jsx(AgentMessageEditContext.Provider, { value: messageEditContext, children: _jsxs("section", { "aria-label": typeof title === "string" ? title : labels.conversation, className: `agentkit-chat ${className ?? ""}`, "data-empty": !hasConversation, "data-empty-composer-placement": hasConversation ? "bottom" : emptyComposerPlacement, onDragEnter: handleFileDragEnter, onDragOver: handleFileDragOver, onDragLeave: handleFileDragLeave, onDrop: handleFileDrop, children: [fileDragActive ? (_jsx("div", { className: "agentkit-file-drop-overlay", role: "status", children: labels.dropFilesToAttach })) : null, hasConversation && (title || toolbar) ? (_jsx(AgentKitSurfaceBoundary, { surface: "header", resetKey: threadId, children: _jsx(AgentKitRegionSlot, { slot: Header, threadId: threadId, children: _jsxs("header", { className: "agentkit-chat-header", children: [_jsx("div", { className: "agentkit-chat-title", children: title }), _jsx(AgentKitRegionSlot, { slot: Toolbar, threadId: threadId, children: _jsx("div", { className: "agentkit-chat-toolbar", children: toolbar }) })] }) }) })) : null, _jsxs("div", { ref: transcriptRef, className: "agentkit-transcript", "aria-live": "off", "data-scrollbar-visible": transcriptScrollbarVisible, onKeyDown: handleTranscriptKeyDown, onPointerDown: handleTranscriptPointerDown, onScroll: handleTranscriptScroll, onTouchMove: revealTranscriptScrollbar, onWheel: revealTranscriptScrollbar, children: [_jsx("div", { ref: transcriptContentRef, className: "agentkit-transcript-content", children: _jsxs(AgentKitRegionSlot, { slot: Transcript, threadId: threadId, children: [!hasConversation && EmptyState ? (_jsx(EmptyState, { threadId: threadId })) : null, transcriptItems, approvalEntries.map(([approvalId, request]) => (_jsx(AgentKitSurfaceBoundary, { surface: "approval", resetKey: `${approvalId}:${thread.events.length}`, children: Approval ? (_jsx(Approval, { value: request, threadId: threadId, runId: thread.approvalRunIds[approvalId] ?? "" })) : (_jsx(AgentApprovalPrompt, { request: request, runId: thread.approvalRunIds[approvalId] ?? "" })) }, approvalId))), connectionRequestEntries.map(([requestId, request]) => (_jsx(AgentKitSurfaceBoundary, { surface: "connection-request", resetKey: `${requestId}:${request.status}`, children: ConnectionRequest ? (_jsx(ConnectionRequest, { value: request, threadId: threadId, runId: thread.connectionRequestRunIds[requestId] ?? "" })) : (_jsx(AgentConnectionRequestCard, { request: request, runId: thread.connectionRequestRunIds[requestId] ?? "" })) }, requestId))), connectionError ? (_jsx(AgentKitSurfaceBoundary, { surface: "connection-error", resetKey: `${threadId}:${connectionError.code}`, children: _jsx(ErrorState, { error: connectionError, threadId: threadId, recover: recovery.execute, recovering: recovery.pending, recoveryError: recovery.error }) })) : null] }) }), transcriptAwayFromBottom ? (_jsx("div", { className: "agentkit-scroll-to-bottom-control", children: _jsx(IconButton, { label: labels.scrollToBottom, icon: _jsx(IconArrowDown, { "aria-hidden": "true" }), size: "compact", onPress: returnTranscriptToBottom }) })) : null] }), composer ? (_jsx("footer", { className: "agentkit-chat-footer", children: _jsx(AgentKitSurfaceBoundary, { surface: "composer", resetKey: threadId, children: _jsx(AgentKitRegionSlot, { slot: Footer, threadId: threadId, children: slots.composer ? (_jsx(Composer, { threadId: threadId })) : (_jsx(AgentKitComposer, { ...composerProps, threadId: threadId })) }) }) })) : null] }) }));
1939
- }
1940
- //# sourceMappingURL=components.js.map