@optilogic/chat 1.0.0-beta.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.
- package/LICENSE +21 -0
- package/dist/index.cjs +567 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +378 -0
- package/dist/index.d.ts +378 -0
- package/dist/index.js +537 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
- package/src/components/agent-response/AgentResponse.tsx +232 -0
- package/src/components/agent-response/components/ActionBar.tsx +150 -0
- package/src/components/agent-response/components/ActivityIndicators.tsx +140 -0
- package/src/components/agent-response/components/MetadataRow.tsx +145 -0
- package/src/components/agent-response/components/ThinkingSection.tsx +48 -0
- package/src/components/agent-response/components/index.ts +15 -0
- package/src/components/agent-response/hooks/index.ts +12 -0
- package/src/components/agent-response/hooks/useAgentResponseAccumulator.ts +186 -0
- package/src/components/agent-response/hooks/useThinkingTimer.ts +52 -0
- package/src/components/agent-response/index.ts +48 -0
- package/src/components/agent-response/types.ts +124 -0
- package/src/components/agent-response/utils.ts +48 -0
- package/src/index.ts +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Optilogic
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var core = require('@optilogic/core');
|
|
5
|
+
var lucideReact = require('lucide-react');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
function _interopNamespace(e) {
|
|
9
|
+
if (e && e.__esModule) return e;
|
|
10
|
+
var n = Object.create(null);
|
|
11
|
+
if (e) {
|
|
12
|
+
Object.keys(e).forEach(function (k) {
|
|
13
|
+
if (k !== 'default') {
|
|
14
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return e[k]; }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
27
|
+
|
|
28
|
+
// src/components/agent-response/AgentResponse.tsx
|
|
29
|
+
var ActivityIndicators = React__namespace.forwardRef(
|
|
30
|
+
({ toolCalls, knowledge, memory, className, ...props }, ref) => {
|
|
31
|
+
const hasAnyActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0;
|
|
32
|
+
if (!hasAnyActivity) return null;
|
|
33
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: core.cn("flex items-center gap-2", className), ...props, children: [
|
|
34
|
+
toolCalls.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(core.Popover, { children: [
|
|
35
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36
|
+
"button",
|
|
37
|
+
{
|
|
38
|
+
className: "flex items-center gap-1 text-muted-foreground hover:text-foreground transition-colors",
|
|
39
|
+
onClick: (e) => e.stopPropagation(),
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wrench, { className: "w-3.5 h-3.5" }),
|
|
42
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", children: toolCalls.length })
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
) }),
|
|
46
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.PopoverContent, { className: "w-80", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
47
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-medium text-sm", children: "Tool Calls" }),
|
|
48
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 max-h-60 overflow-auto", children: toolCalls.map((tool) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 bg-muted rounded text-xs", children: [
|
|
49
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: tool.name }),
|
|
50
|
+
tool.arguments && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-1 text-muted-foreground overflow-x-auto", children: JSON.stringify(tool.arguments, null, 2) })
|
|
51
|
+
] }, tool.id)) })
|
|
52
|
+
] }) })
|
|
53
|
+
] }),
|
|
54
|
+
knowledge.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(core.Popover, { children: [
|
|
55
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
56
|
+
"button",
|
|
57
|
+
{
|
|
58
|
+
className: "flex items-center gap-1 text-muted-foreground hover:text-foreground transition-colors",
|
|
59
|
+
onClick: (e) => e.stopPropagation(),
|
|
60
|
+
children: [
|
|
61
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Book, { className: "w-3.5 h-3.5" }),
|
|
62
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", children: knowledge.length })
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
) }),
|
|
66
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.PopoverContent, { className: "w-80", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
67
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-medium text-sm", children: "Knowledge Retrieved" }),
|
|
68
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 max-h-60 overflow-auto", children: knowledge.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 bg-muted rounded text-xs", children: [
|
|
69
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: item.source }),
|
|
70
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-muted-foreground", children: item.content })
|
|
71
|
+
] }, item.id)) })
|
|
72
|
+
] }) })
|
|
73
|
+
] }),
|
|
74
|
+
memory.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(core.Popover, { children: [
|
|
75
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
76
|
+
"button",
|
|
77
|
+
{
|
|
78
|
+
className: "flex items-center gap-1 text-muted-foreground hover:text-foreground transition-colors",
|
|
79
|
+
onClick: (e) => e.stopPropagation(),
|
|
80
|
+
children: [
|
|
81
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.HardDrive, { className: "w-3.5 h-3.5" }),
|
|
82
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", children: memory.length })
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
) }),
|
|
86
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.PopoverContent, { className: "w-80", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
87
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-medium text-sm", children: "Memory Accessed" }),
|
|
88
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 max-h-60 overflow-auto", children: memory.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 bg-muted rounded text-xs", children: [
|
|
89
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: item.type }),
|
|
90
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-muted-foreground", children: item.content })
|
|
91
|
+
] }, item.id)) })
|
|
92
|
+
] }) })
|
|
93
|
+
] })
|
|
94
|
+
] });
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
ActivityIndicators.displayName = "ActivityIndicators";
|
|
98
|
+
|
|
99
|
+
// src/components/agent-response/utils.ts
|
|
100
|
+
function formatTime(seconds, isComplete) {
|
|
101
|
+
if (seconds < 1) {
|
|
102
|
+
return isComplete ? "<1s" : "0s";
|
|
103
|
+
}
|
|
104
|
+
if (isComplete) {
|
|
105
|
+
if (seconds < 60) {
|
|
106
|
+
return `${Math.round(seconds)}s`;
|
|
107
|
+
}
|
|
108
|
+
const minutes = seconds / 60;
|
|
109
|
+
return `${minutes.toFixed(1)} min`;
|
|
110
|
+
}
|
|
111
|
+
if (seconds < 60) {
|
|
112
|
+
return `${Math.floor(seconds)}s`;
|
|
113
|
+
}
|
|
114
|
+
const mins = Math.floor(seconds / 60);
|
|
115
|
+
const secs = Math.floor(seconds % 60);
|
|
116
|
+
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
117
|
+
}
|
|
118
|
+
function formatTotalTime(seconds) {
|
|
119
|
+
if (seconds < 1) {
|
|
120
|
+
return "<1s";
|
|
121
|
+
}
|
|
122
|
+
if (seconds < 60) {
|
|
123
|
+
return `${seconds.toFixed(1)}s`;
|
|
124
|
+
}
|
|
125
|
+
const minutes = seconds / 60;
|
|
126
|
+
return `${minutes.toFixed(1)}m`;
|
|
127
|
+
}
|
|
128
|
+
var MetadataRow = React__namespace.forwardRef(
|
|
129
|
+
({
|
|
130
|
+
hasThinking,
|
|
131
|
+
isExpanded,
|
|
132
|
+
onToggle,
|
|
133
|
+
toolCalls,
|
|
134
|
+
knowledge,
|
|
135
|
+
memory,
|
|
136
|
+
status,
|
|
137
|
+
elapsedTime,
|
|
138
|
+
className,
|
|
139
|
+
...props
|
|
140
|
+
}, ref) => {
|
|
141
|
+
const isProcessing = status === "processing";
|
|
142
|
+
const isComplete = status === "complete";
|
|
143
|
+
const hasActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0;
|
|
144
|
+
const renderLeftContent = () => {
|
|
145
|
+
if (hasThinking) {
|
|
146
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
147
|
+
isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "w-3.5 h-3.5 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "w-3.5 h-3.5 text-muted-foreground" }),
|
|
148
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: isComplete ? `Thought for ${formatTime(elapsedTime, true)}` : `Thinking... ${formatTime(elapsedTime, false)}` })
|
|
149
|
+
] });
|
|
150
|
+
}
|
|
151
|
+
if (isProcessing) {
|
|
152
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
153
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.LoadingSpinner, { size: "sm", variant: "muted", className: "w-3.5 h-3.5" }),
|
|
154
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "Processing" })
|
|
155
|
+
] });
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
};
|
|
159
|
+
const leftContent = renderLeftContent();
|
|
160
|
+
if (!leftContent && !hasActivity) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
164
|
+
"div",
|
|
165
|
+
{
|
|
166
|
+
ref,
|
|
167
|
+
className: core.cn("w-full flex items-center justify-between px-3 py-2", className),
|
|
168
|
+
...props,
|
|
169
|
+
children: [
|
|
170
|
+
hasThinking ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
171
|
+
"button",
|
|
172
|
+
{
|
|
173
|
+
onClick: onToggle,
|
|
174
|
+
className: "flex items-center gap-1.5 hover:bg-muted/50 -ml-1.5 pl-1.5 pr-2 py-0.5 rounded transition-colors",
|
|
175
|
+
children: leftContent
|
|
176
|
+
}
|
|
177
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: leftContent }),
|
|
178
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
179
|
+
ActivityIndicators,
|
|
180
|
+
{
|
|
181
|
+
toolCalls,
|
|
182
|
+
knowledge,
|
|
183
|
+
memory
|
|
184
|
+
}
|
|
185
|
+
)
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
MetadataRow.displayName = "MetadataRow";
|
|
192
|
+
var ThinkingSection = React__namespace.forwardRef(
|
|
193
|
+
({ content, isExpanded, className, ...props }, ref) => {
|
|
194
|
+
if (!isExpanded || !content) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
198
|
+
"div",
|
|
199
|
+
{
|
|
200
|
+
ref,
|
|
201
|
+
className: core.cn("px-3 pb-3 border-t border-border", className),
|
|
202
|
+
...props,
|
|
203
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 max-h-[200px] overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: content }) })
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
ThinkingSection.displayName = "ThinkingSection";
|
|
209
|
+
var ActionBar = React__namespace.forwardRef(
|
|
210
|
+
({
|
|
211
|
+
response,
|
|
212
|
+
isVisible,
|
|
213
|
+
totalTimeSeconds,
|
|
214
|
+
feedback,
|
|
215
|
+
onFeedbackChange,
|
|
216
|
+
onResponseCopy,
|
|
217
|
+
className,
|
|
218
|
+
...props
|
|
219
|
+
}, ref) => {
|
|
220
|
+
const [copied, setCopied] = React.useState(false);
|
|
221
|
+
const handleCopy = React.useCallback(async () => {
|
|
222
|
+
try {
|
|
223
|
+
await navigator.clipboard.writeText(response);
|
|
224
|
+
setCopied(true);
|
|
225
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
226
|
+
onResponseCopy?.(response);
|
|
227
|
+
} catch (err) {
|
|
228
|
+
console.error("Failed to copy response:", err);
|
|
229
|
+
}
|
|
230
|
+
}, [response, onResponseCopy]);
|
|
231
|
+
const handleThumbsUp = React.useCallback(() => {
|
|
232
|
+
const newValue = feedback === "up" ? null : "up";
|
|
233
|
+
onFeedbackChange?.(newValue);
|
|
234
|
+
}, [feedback, onFeedbackChange]);
|
|
235
|
+
const handleThumbsDown = React.useCallback(() => {
|
|
236
|
+
const newValue = feedback === "down" ? null : "down";
|
|
237
|
+
onFeedbackChange?.(newValue);
|
|
238
|
+
}, [feedback, onFeedbackChange]);
|
|
239
|
+
const isThumbsUp = feedback === "up";
|
|
240
|
+
const isThumbsDown = feedback === "down";
|
|
241
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
242
|
+
"div",
|
|
243
|
+
{
|
|
244
|
+
ref,
|
|
245
|
+
className: core.cn(
|
|
246
|
+
"flex items-center justify-between px-4 py-2",
|
|
247
|
+
"transition-opacity duration-200",
|
|
248
|
+
isVisible ? "opacity-100" : "opacity-0 pointer-events-none",
|
|
249
|
+
className
|
|
250
|
+
),
|
|
251
|
+
...props,
|
|
252
|
+
children: [
|
|
253
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
254
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
255
|
+
"button",
|
|
256
|
+
{
|
|
257
|
+
onClick: handleCopy,
|
|
258
|
+
className: "p-1.5 rounded hover:bg-muted transition-colors text-muted-foreground hover:text-foreground",
|
|
259
|
+
title: copied ? "Copied!" : "Copy response",
|
|
260
|
+
children: copied ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4 text-green-500" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "w-4 h-4" })
|
|
261
|
+
}
|
|
262
|
+
),
|
|
263
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
264
|
+
"button",
|
|
265
|
+
{
|
|
266
|
+
onClick: handleThumbsUp,
|
|
267
|
+
className: core.cn(
|
|
268
|
+
"p-1.5 rounded hover:bg-muted transition-colors",
|
|
269
|
+
isThumbsUp ? "text-green-500" : "text-muted-foreground hover:text-foreground"
|
|
270
|
+
),
|
|
271
|
+
title: "Good response",
|
|
272
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ThumbsUp, { className: core.cn("w-4 h-4", isThumbsUp && "fill-current") })
|
|
273
|
+
}
|
|
274
|
+
),
|
|
275
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
276
|
+
"button",
|
|
277
|
+
{
|
|
278
|
+
onClick: handleThumbsDown,
|
|
279
|
+
className: core.cn(
|
|
280
|
+
"p-1.5 rounded hover:bg-muted transition-colors",
|
|
281
|
+
isThumbsDown ? "text-red-500" : "text-muted-foreground hover:text-foreground"
|
|
282
|
+
),
|
|
283
|
+
title: "Poor response",
|
|
284
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ThumbsDown, { className: core.cn("w-4 h-4", isThumbsDown && "fill-current") })
|
|
285
|
+
}
|
|
286
|
+
)
|
|
287
|
+
] }),
|
|
288
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
289
|
+
"Total time: ",
|
|
290
|
+
formatTotalTime(totalTimeSeconds)
|
|
291
|
+
] })
|
|
292
|
+
]
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
ActionBar.displayName = "ActionBar";
|
|
298
|
+
function useThinkingTimer({
|
|
299
|
+
startTime,
|
|
300
|
+
endTime,
|
|
301
|
+
status
|
|
302
|
+
}) {
|
|
303
|
+
const [elapsed, setElapsed] = React.useState(0);
|
|
304
|
+
React.useEffect(() => {
|
|
305
|
+
if (!startTime) {
|
|
306
|
+
setElapsed(0);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (status === "complete" && endTime) {
|
|
310
|
+
setElapsed((endTime - startTime) / 1e3);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (status === "processing") {
|
|
314
|
+
const updateElapsed = () => {
|
|
315
|
+
setElapsed((Date.now() - startTime) / 1e3);
|
|
316
|
+
};
|
|
317
|
+
updateElapsed();
|
|
318
|
+
const interval = setInterval(updateElapsed, 1e3);
|
|
319
|
+
return () => clearInterval(interval);
|
|
320
|
+
}
|
|
321
|
+
}, [startTime, endTime, status]);
|
|
322
|
+
return elapsed;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/components/agent-response/types.ts
|
|
326
|
+
var initialAgentResponseState = {
|
|
327
|
+
status: "idle",
|
|
328
|
+
thinking: "",
|
|
329
|
+
toolCalls: [],
|
|
330
|
+
knowledge: [],
|
|
331
|
+
memory: [],
|
|
332
|
+
response: "",
|
|
333
|
+
thinkingStartTime: null,
|
|
334
|
+
responseCompleteTime: null,
|
|
335
|
+
firstMessageTime: null
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// src/components/agent-response/hooks/useAgentResponseAccumulator.ts
|
|
339
|
+
function useAgentResponseAccumulator(options) {
|
|
340
|
+
const [state, setState] = React.useState(initialAgentResponseState);
|
|
341
|
+
const topic = options?.topic;
|
|
342
|
+
const handleMessage = React.useCallback(
|
|
343
|
+
(message) => {
|
|
344
|
+
let payload;
|
|
345
|
+
if (topic) {
|
|
346
|
+
const msg = message;
|
|
347
|
+
if (msg.topic !== topic) return;
|
|
348
|
+
payload = msg.message;
|
|
349
|
+
} else {
|
|
350
|
+
payload = message;
|
|
351
|
+
}
|
|
352
|
+
setState((prev) => {
|
|
353
|
+
let newStatus = prev.status;
|
|
354
|
+
const isFirstMessage = prev.status === "idle" && payload.type !== "status";
|
|
355
|
+
if (isFirstMessage) {
|
|
356
|
+
newStatus = "processing";
|
|
357
|
+
}
|
|
358
|
+
const firstMessageTime = prev.firstMessageTime ?? (isFirstMessage ? Date.now() : null);
|
|
359
|
+
switch (payload.type) {
|
|
360
|
+
case "status":
|
|
361
|
+
if (payload.message === "Harness connected" || payload.status === "Harness connected") {
|
|
362
|
+
return { ...initialAgentResponseState };
|
|
363
|
+
}
|
|
364
|
+
return { ...prev, status: newStatus };
|
|
365
|
+
case "thinking": {
|
|
366
|
+
const newThinking = payload.message || payload.content || "";
|
|
367
|
+
const separator = prev.thinking && newThinking ? "\n\n" : "";
|
|
368
|
+
const thinkingStartTime = prev.thinkingStartTime ?? (newThinking ? Date.now() : null);
|
|
369
|
+
return {
|
|
370
|
+
...prev,
|
|
371
|
+
status: newStatus,
|
|
372
|
+
thinking: prev.thinking + separator + newThinking,
|
|
373
|
+
thinkingStartTime,
|
|
374
|
+
firstMessageTime
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
case "tool_call": {
|
|
378
|
+
const toolName = payload.message || payload.tool?.name;
|
|
379
|
+
if (toolName) {
|
|
380
|
+
const newToolCall = {
|
|
381
|
+
id: payload.tool?.id || `tool-${Date.now()}`,
|
|
382
|
+
name: toolName,
|
|
383
|
+
arguments: payload.tool?.arguments,
|
|
384
|
+
timestamp: Date.now()
|
|
385
|
+
};
|
|
386
|
+
return {
|
|
387
|
+
...prev,
|
|
388
|
+
status: newStatus,
|
|
389
|
+
toolCalls: [...prev.toolCalls, newToolCall],
|
|
390
|
+
firstMessageTime
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
return { ...prev, status: newStatus, firstMessageTime };
|
|
394
|
+
}
|
|
395
|
+
case "knowledge": {
|
|
396
|
+
const knowledgeContent = payload.message || payload.knowledge?.content;
|
|
397
|
+
if (knowledgeContent) {
|
|
398
|
+
const newKnowledge = {
|
|
399
|
+
id: payload.knowledge?.id || `knowledge-${Date.now()}`,
|
|
400
|
+
source: payload.knowledge?.source || "unknown",
|
|
401
|
+
content: knowledgeContent,
|
|
402
|
+
timestamp: Date.now()
|
|
403
|
+
};
|
|
404
|
+
return {
|
|
405
|
+
...prev,
|
|
406
|
+
status: newStatus,
|
|
407
|
+
knowledge: [...prev.knowledge, newKnowledge],
|
|
408
|
+
firstMessageTime
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
return { ...prev, status: newStatus, firstMessageTime };
|
|
412
|
+
}
|
|
413
|
+
case "memory": {
|
|
414
|
+
const memoryContent = payload.message || payload.memory?.content;
|
|
415
|
+
if (memoryContent) {
|
|
416
|
+
const newMemory = {
|
|
417
|
+
id: payload.memory?.id || `memory-${Date.now()}`,
|
|
418
|
+
type: payload.memory?.type || "unknown",
|
|
419
|
+
content: memoryContent,
|
|
420
|
+
timestamp: Date.now()
|
|
421
|
+
};
|
|
422
|
+
return {
|
|
423
|
+
...prev,
|
|
424
|
+
status: newStatus,
|
|
425
|
+
memory: [...prev.memory, newMemory],
|
|
426
|
+
firstMessageTime
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
return { ...prev, status: newStatus, firstMessageTime };
|
|
430
|
+
}
|
|
431
|
+
case "response":
|
|
432
|
+
return {
|
|
433
|
+
...prev,
|
|
434
|
+
status: "complete",
|
|
435
|
+
response: payload.message || payload.content || "",
|
|
436
|
+
responseCompleteTime: Date.now(),
|
|
437
|
+
firstMessageTime: prev.firstMessageTime ?? Date.now()
|
|
438
|
+
};
|
|
439
|
+
default:
|
|
440
|
+
return { ...prev, status: newStatus, firstMessageTime };
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
},
|
|
444
|
+
[topic]
|
|
445
|
+
);
|
|
446
|
+
const reset = React.useCallback(() => {
|
|
447
|
+
setState(initialAgentResponseState);
|
|
448
|
+
}, []);
|
|
449
|
+
return { state, handleMessage, reset };
|
|
450
|
+
}
|
|
451
|
+
var AgentResponse = React__namespace.forwardRef(
|
|
452
|
+
({
|
|
453
|
+
state,
|
|
454
|
+
id,
|
|
455
|
+
timestamp,
|
|
456
|
+
feedback,
|
|
457
|
+
onFeedbackChange,
|
|
458
|
+
onResponseCopy,
|
|
459
|
+
defaultThinkingExpanded = false,
|
|
460
|
+
thinkingExpanded: controlledThinkingExpanded,
|
|
461
|
+
onThinkingExpandedChange,
|
|
462
|
+
actionsVisible = "hover",
|
|
463
|
+
renderMarkdown,
|
|
464
|
+
className,
|
|
465
|
+
...props
|
|
466
|
+
}, ref) => {
|
|
467
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = React.useState(defaultThinkingExpanded);
|
|
468
|
+
const isThinkingControlled = controlledThinkingExpanded !== void 0;
|
|
469
|
+
const thinkingExpanded = isThinkingControlled ? controlledThinkingExpanded : uncontrolledExpanded;
|
|
470
|
+
const toggleThinking = React.useCallback(() => {
|
|
471
|
+
const newValue = !thinkingExpanded;
|
|
472
|
+
if (isThinkingControlled) {
|
|
473
|
+
onThinkingExpandedChange?.(newValue);
|
|
474
|
+
} else {
|
|
475
|
+
setUncontrolledExpanded(newValue);
|
|
476
|
+
}
|
|
477
|
+
}, [thinkingExpanded, isThinkingControlled, onThinkingExpandedChange]);
|
|
478
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
479
|
+
const elapsedTime = useThinkingTimer({
|
|
480
|
+
startTime: state.thinkingStartTime,
|
|
481
|
+
endTime: state.responseCompleteTime,
|
|
482
|
+
status: state.status
|
|
483
|
+
});
|
|
484
|
+
const totalTimeSeconds = React.useMemo(() => {
|
|
485
|
+
if (!state.firstMessageTime || !state.responseCompleteTime) return 0;
|
|
486
|
+
return (state.responseCompleteTime - state.firstMessageTime) / 1e3;
|
|
487
|
+
}, [state.firstMessageTime, state.responseCompleteTime]);
|
|
488
|
+
const hasAnyContent = state.thinking || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.response;
|
|
489
|
+
const showMetadataRow = state.thinking || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.status === "processing";
|
|
490
|
+
const showActionBar = state.status === "complete" && state.response;
|
|
491
|
+
const isActionBarVisible = actionsVisible === true || actionsVisible === "hover" && isHovered;
|
|
492
|
+
if (!hasAnyContent) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
496
|
+
"div",
|
|
497
|
+
{
|
|
498
|
+
ref,
|
|
499
|
+
className,
|
|
500
|
+
onMouseEnter: () => setIsHovered(true),
|
|
501
|
+
onMouseLeave: () => setIsHovered(false),
|
|
502
|
+
...props,
|
|
503
|
+
children: [
|
|
504
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border border-border rounded-lg overflow-hidden", children: [
|
|
505
|
+
showMetadataRow && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
506
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
507
|
+
MetadataRow,
|
|
508
|
+
{
|
|
509
|
+
hasThinking: !!state.thinking,
|
|
510
|
+
isExpanded: thinkingExpanded,
|
|
511
|
+
onToggle: toggleThinking,
|
|
512
|
+
toolCalls: state.toolCalls,
|
|
513
|
+
knowledge: state.knowledge,
|
|
514
|
+
memory: state.memory,
|
|
515
|
+
status: state.status,
|
|
516
|
+
elapsedTime
|
|
517
|
+
}
|
|
518
|
+
),
|
|
519
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
520
|
+
ThinkingSection,
|
|
521
|
+
{
|
|
522
|
+
content: state.thinking,
|
|
523
|
+
isExpanded: thinkingExpanded
|
|
524
|
+
}
|
|
525
|
+
)
|
|
526
|
+
] }),
|
|
527
|
+
state.response && /* @__PURE__ */ jsxRuntime.jsx(
|
|
528
|
+
"div",
|
|
529
|
+
{
|
|
530
|
+
className: core.cn(
|
|
531
|
+
"bg-muted/50 p-4",
|
|
532
|
+
showMetadataRow && "border-t border-border"
|
|
533
|
+
),
|
|
534
|
+
children: renderMarkdown ? renderMarkdown(state.response) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-pre-wrap", children: state.response })
|
|
535
|
+
}
|
|
536
|
+
)
|
|
537
|
+
] }),
|
|
538
|
+
showActionBar && /* @__PURE__ */ jsxRuntime.jsx(
|
|
539
|
+
ActionBar,
|
|
540
|
+
{
|
|
541
|
+
response: state.response,
|
|
542
|
+
isVisible: isActionBarVisible,
|
|
543
|
+
totalTimeSeconds,
|
|
544
|
+
feedback,
|
|
545
|
+
onFeedbackChange,
|
|
546
|
+
onResponseCopy
|
|
547
|
+
}
|
|
548
|
+
)
|
|
549
|
+
]
|
|
550
|
+
}
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
);
|
|
554
|
+
AgentResponse.displayName = "AgentResponse";
|
|
555
|
+
|
|
556
|
+
exports.ActionBar = ActionBar;
|
|
557
|
+
exports.ActivityIndicators = ActivityIndicators;
|
|
558
|
+
exports.AgentResponse = AgentResponse;
|
|
559
|
+
exports.MetadataRow = MetadataRow;
|
|
560
|
+
exports.ThinkingSection = ThinkingSection;
|
|
561
|
+
exports.formatTime = formatTime;
|
|
562
|
+
exports.formatTotalTime = formatTotalTime;
|
|
563
|
+
exports.initialAgentResponseState = initialAgentResponseState;
|
|
564
|
+
exports.useAgentResponseAccumulator = useAgentResponseAccumulator;
|
|
565
|
+
exports.useThinkingTimer = useThinkingTimer;
|
|
566
|
+
//# sourceMappingURL=index.cjs.map
|
|
567
|
+
//# sourceMappingURL=index.cjs.map
|