@agent-platform/ui 0.0.11 → 0.0.13

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 (111) hide show
  1. package/README.md +27 -10
  2. package/dist/index.d.ts +35 -3
  3. package/dist/index.js +2462 -2
  4. package/dist/styles/agent-ui.css +1226 -0
  5. package/dist/styles/globals.css +18 -902
  6. package/dist/styles/theme.css +34 -0
  7. package/package.json +14 -8
  8. package/dist/components/agent/agent-container-model.d.ts +0 -19
  9. package/dist/components/agent/agent-container-model.js +0 -17
  10. package/dist/components/agent/agent-container-state.d.ts +0 -2
  11. package/dist/components/agent/agent-container-state.js +0 -52
  12. package/dist/components/agent/agent-container-view.d.ts +0 -20
  13. package/dist/components/agent/agent-container-view.js +0 -19
  14. package/dist/components/agent/agent-container-view.test.d.ts +0 -1
  15. package/dist/components/agent/agent-container-view.test.js +0 -16
  16. package/dist/components/agent/agent-container.d.ts +0 -3
  17. package/dist/components/agent/agent-container.js +0 -24
  18. package/dist/components/agent/agent-greeting.d.ts +0 -3
  19. package/dist/components/agent/agent-greeting.js +0 -7
  20. package/dist/components/agent/agent-header.d.ts +0 -3
  21. package/dist/components/agent/agent-header.js +0 -9
  22. package/dist/components/agent/agent-home-card.d.ts +0 -3
  23. package/dist/components/agent/agent-home-card.js +0 -7
  24. package/dist/components/agent/agent-home-cards.d.ts +0 -9
  25. package/dist/components/agent/agent-home-cards.js +0 -13
  26. package/dist/components/agent/agent-input.d.ts +0 -3
  27. package/dist/components/agent/agent-input.js +0 -51
  28. package/dist/components/agent/agent-popup-widget.d.ts +0 -15
  29. package/dist/components/agent/agent-popup-widget.js +0 -49
  30. package/dist/components/agent/agent-screen.d.ts +0 -5
  31. package/dist/components/agent/agent-screen.js +0 -7
  32. package/dist/components/agent/approval-ui-model.d.ts +0 -18
  33. package/dist/components/agent/approval-ui-model.js +0 -27
  34. package/dist/components/agent/approval-ui-model.test.d.ts +0 -1
  35. package/dist/components/agent/approval-ui-model.test.js +0 -39
  36. package/dist/components/agent/defaults.d.ts +0 -5
  37. package/dist/components/agent/defaults.js +0 -12
  38. package/dist/components/agent/index.d.ts +0 -3
  39. package/dist/components/agent/index.js +0 -1
  40. package/dist/components/agent/input-mode.d.ts +0 -5
  41. package/dist/components/agent/input-mode.js +0 -9
  42. package/dist/components/agent/message/index.d.ts +0 -8
  43. package/dist/components/agent/message/index.js +0 -4
  44. package/dist/components/agent/message/markdown.d.ts +0 -6
  45. package/dist/components/agent/message/markdown.js +0 -66
  46. package/dist/components/agent/message/message-item.d.ts +0 -7
  47. package/dist/components/agent/message/message-item.js +0 -24
  48. package/dist/components/agent/message/message-list.d.ts +0 -8
  49. package/dist/components/agent/message/message-list.js +0 -20
  50. package/dist/components/agent/message/message-loading.d.ts +0 -4
  51. package/dist/components/agent/message/message-loading.js +0 -10
  52. package/dist/components/agent/message/tool-call-card.d.ts +0 -9
  53. package/dist/components/agent/message/tool-call-card.js +0 -26
  54. package/dist/components/agent/message/utils.d.ts +0 -8
  55. package/dist/components/agent/message/utils.js +0 -21
  56. package/dist/components/agent/provider/agent-context.d.ts +0 -4
  57. package/dist/components/agent/provider/agent-context.js +0 -13
  58. package/dist/components/agent/provider/agent-provider.d.ts +0 -10
  59. package/dist/components/agent/provider/agent-provider.js +0 -40
  60. package/dist/components/agent/provider/index.d.ts +0 -4
  61. package/dist/components/agent/provider/index.js +0 -2
  62. package/dist/components/agent/provider/parse-sse-buffer.d.ts +0 -9
  63. package/dist/components/agent/provider/parse-sse-buffer.js +0 -23
  64. package/dist/components/agent/provider/runtime-config.d.ts +0 -5
  65. package/dist/components/agent/provider/runtime-config.js +0 -24
  66. package/dist/components/agent/provider/types.d.ts +0 -66
  67. package/dist/components/agent/provider/types.js +0 -1
  68. package/dist/components/agent/provider/use-agent-chat.d.ts +0 -6
  69. package/dist/components/agent/provider/use-agent-chat.js +0 -493
  70. package/dist/components/agent/tool-approval-panel.d.ts +0 -9
  71. package/dist/components/agent/tool-approval-panel.js +0 -11
  72. package/dist/components/agent/types.d.ts +0 -44
  73. package/dist/components/agent/types.js +0 -1
  74. package/dist/components/index.d.ts +0 -7
  75. package/dist/components/index.js +0 -9
  76. package/dist/components/ui/badge.d.ts +0 -9
  77. package/dist/components/ui/badge.js +0 -24
  78. package/dist/components/ui/button.d.ts +0 -10
  79. package/dist/components/ui/button.js +0 -35
  80. package/dist/components/ui/card.d.ts +0 -9
  81. package/dist/components/ui/card.js +0 -24
  82. package/dist/components/ui/input.d.ts +0 -3
  83. package/dist/components/ui/input.js +0 -6
  84. package/dist/components/ui/label.d.ts +0 -4
  85. package/dist/components/ui/label.js +0 -7
  86. package/dist/components/ui/separator.d.ts +0 -4
  87. package/dist/components/ui/separator.js +0 -8
  88. package/dist/components/ui/textarea.d.ts +0 -3
  89. package/dist/components/ui/textarea.js +0 -6
  90. package/dist/lib/index.d.ts +0 -1
  91. package/dist/lib/index.js +0 -1
  92. package/dist/lib/utils.d.ts +0 -2
  93. package/dist/lib/utils.js +0 -5
  94. package/dist/modules/agent/agent.repository.d.ts +0 -58
  95. package/dist/modules/agent/agent.repository.js +0 -235
  96. package/dist/modules/agent/agent.repository.test.d.ts +0 -1
  97. package/dist/modules/agent/agent.repository.test.js +0 -64
  98. package/dist/modules/agent/domain/chat-state.d.ts +0 -64
  99. package/dist/modules/agent/domain/chat-state.js +0 -148
  100. package/dist/modules/agent/domain/chat-state.test.d.ts +0 -1
  101. package/dist/modules/agent/domain/chat-state.test.js +0 -72
  102. package/dist/modules/agent/use-agent-chat.d.ts +0 -6
  103. package/dist/modules/agent/use-agent-chat.js +0 -106
  104. package/dist/modules/agent/usecases/process-stream.d.ts +0 -26
  105. package/dist/modules/agent/usecases/process-stream.js +0 -112
  106. package/dist/modules/agent/usecases/process-stream.test.d.ts +0 -1
  107. package/dist/modules/agent/usecases/process-stream.test.js +0 -91
  108. package/dist/modules/agent/usecases/send-message.d.ts +0 -21
  109. package/dist/modules/agent/usecases/send-message.js +0 -298
  110. package/dist/modules/agent/usecases/send-message.test.d.ts +0 -1
  111. package/dist/modules/agent/usecases/send-message.test.js +0 -257
package/dist/index.js CHANGED
@@ -1,2 +1,2462 @@
1
- import './styles/globals.css';
2
- export { AgentScreen } from './components/agent';
1
+ import { createContext, useRef, useEffect, useState, useMemo, useCallback, useContext, useReducer } from 'react';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+ import { CaretDown, Gear, Paperclip, PaperPlaneTilt } from '@phosphor-icons/react/dist/ssr';
6
+ import { cva } from 'class-variance-authority';
7
+ import { Slot } from 'radix-ui';
8
+ import ReactMarkdown from 'react-markdown';
9
+ import remarkGfm from 'remark-gfm';
10
+ import { createAuthClient } from 'better-auth/client';
11
+ import { anonymousClient } from 'better-auth/client/plugins';
12
+
13
+ // src/components/agent/agent-container.tsx
14
+
15
+ // src/components/agent/agent-container-model.ts
16
+ function resolveEffectiveInputProps({
17
+ sendMessage,
18
+ isLoading
19
+ }) {
20
+ return {
21
+ onSend: sendMessage,
22
+ isLoading
23
+ };
24
+ }
25
+ function resolveAgentContainerLayoutState(input) {
26
+ const shouldShowMessages = input.messageCount > 0 || input.isLoading || input.hasError;
27
+ const shouldShowToolApprovalPanel = input.hasActiveApprovalRequest;
28
+ const shouldShowInput = !input.hasActiveApprovalRequest;
29
+ return {
30
+ shouldShowMessages,
31
+ shouldShowToolApprovalPanel,
32
+ shouldShowInput
33
+ };
34
+ }
35
+ function cn(...inputs) {
36
+ return twMerge(clsx(inputs));
37
+ }
38
+ function AgentGreeting({
39
+ greeting = "\u3053\u3093\u306B\u3061\u306F \u{1F44B}",
40
+ subtext = "\u4F55\u304B\u304A\u624B\u4F1D\u3044\u3067\u304D\u308B\u3053\u3068\u306F\u3042\u308A\u307E\u3059\u304B\uFF1F",
41
+ className
42
+ }) {
43
+ return /* @__PURE__ */ jsxs(
44
+ "div",
45
+ {
46
+ "data-slot": "agent-greeting",
47
+ className: cn("flex flex-col items-center gap-1.5 text-center", className),
48
+ children: [
49
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: greeting }),
50
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tracking-tight text-balance text-card-foreground", children: subtext })
51
+ ]
52
+ }
53
+ );
54
+ }
55
+ var buttonVariants = cva(
56
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
57
+ {
58
+ variants: {
59
+ variant: {
60
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
61
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
62
+ outline: "border bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
63
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
64
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
65
+ link: "text-primary underline-offset-4 hover:underline"
66
+ },
67
+ size: {
68
+ default: "h-10 px-4 py-2 has-[>svg]:px-3",
69
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
70
+ sm: "h-9 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
71
+ lg: "h-11 rounded-md px-6 has-[>svg]:px-4",
72
+ icon: "size-10",
73
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
74
+ "icon-sm": "size-8 rounded-md [&_svg:not([class*='size-'])]:size-4",
75
+ "icon-lg": "size-10 rounded-md [&_svg:not([class*='size-'])]:size-5"
76
+ }
77
+ },
78
+ defaultVariants: {
79
+ variant: "default",
80
+ size: "default"
81
+ }
82
+ }
83
+ );
84
+ function Button({
85
+ className,
86
+ variant = "default",
87
+ size = "default",
88
+ asChild = false,
89
+ ...props
90
+ }) {
91
+ const Comp = asChild ? Slot.Root : "button";
92
+ return /* @__PURE__ */ jsx(
93
+ Comp,
94
+ {
95
+ "data-slot": "button",
96
+ "data-variant": variant,
97
+ "data-size": size,
98
+ className: cn(buttonVariants({ variant, size, className })),
99
+ ...props
100
+ }
101
+ );
102
+ }
103
+ function AgentHeader({
104
+ historyLabel = "\u5C65\u6B74",
105
+ onHistoryClick,
106
+ onSettingsClick,
107
+ onNewChatClick,
108
+ newChatLabel = "\u65B0\u898F\u30C1\u30E3\u30C3\u30C8",
109
+ settingsAriaLabel = "\u8A2D\u5B9A",
110
+ isHistoryOpen = false,
111
+ className
112
+ }) {
113
+ return /* @__PURE__ */ jsxs(
114
+ "div",
115
+ {
116
+ "data-slot": "agent-header",
117
+ className: cn("flex w-full items-center justify-between bg-background px-4 py-2", className),
118
+ children: [
119
+ /* @__PURE__ */ jsxs(
120
+ "button",
121
+ {
122
+ type: "button",
123
+ className: "flex items-center gap-1 rounded-md px-2 py-1 transition-colors hover:bg-muted",
124
+ onClick: onHistoryClick,
125
+ "aria-label": historyLabel,
126
+ "aria-expanded": isHistoryOpen,
127
+ children: [
128
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: historyLabel }),
129
+ /* @__PURE__ */ jsx(
130
+ CaretDown,
131
+ {
132
+ className: cn("size-4 transition-transform duration-200", isHistoryOpen && "rotate-180")
133
+ }
134
+ )
135
+ ]
136
+ }
137
+ ),
138
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
139
+ /* @__PURE__ */ jsx(
140
+ Button,
141
+ {
142
+ variant: "ghost",
143
+ size: "icon-sm",
144
+ onClick: onSettingsClick,
145
+ "aria-label": settingsAriaLabel,
146
+ className: "text-foreground",
147
+ children: /* @__PURE__ */ jsx(Gear, { className: "size-5" })
148
+ }
149
+ ),
150
+ /* @__PURE__ */ jsx(
151
+ Button,
152
+ {
153
+ size: "sm",
154
+ onClick: onNewChatClick,
155
+ className: "bg-foreground text-background text-xs font-medium hover:bg-foreground/90",
156
+ children: newChatLabel
157
+ }
158
+ )
159
+ ] })
160
+ ]
161
+ }
162
+ );
163
+ }
164
+
165
+ // src/components/agent/history/format-timestamp.ts
166
+ var MINUTE = 60 * 1e3;
167
+ var HOUR = 60 * MINUTE;
168
+ function isSameDay(a, b) {
169
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
170
+ }
171
+ function isYesterday(date, now) {
172
+ const yesterday = new Date(now);
173
+ yesterday.setDate(yesterday.getDate() - 1);
174
+ return isSameDay(date, yesterday);
175
+ }
176
+ function isWithinDays(date, now, days) {
177
+ const diff = now.getTime() - date.getTime();
178
+ return diff >= 0 && diff < days * 24 * HOUR;
179
+ }
180
+ function padTwo(n) {
181
+ return n.toString().padStart(2, "0");
182
+ }
183
+ function formatTime(date) {
184
+ return `${padTwo(date.getHours())}:${padTwo(date.getMinutes())}`;
185
+ }
186
+ var WEEKDAYS = ["\u65E5\u66DC\u65E5", "\u6708\u66DC\u65E5", "\u706B\u66DC\u65E5", "\u6C34\u66DC\u65E5", "\u6728\u66DC\u65E5", "\u91D1\u66DC\u65E5", "\u571F\u66DC\u65E5"];
187
+ function formatRelativeTimestamp(updatedAt, now) {
188
+ const date = new Date(updatedAt);
189
+ if (Number.isNaN(date.getTime())) {
190
+ return updatedAt;
191
+ }
192
+ const ref = /* @__PURE__ */ new Date();
193
+ const diff = ref.getTime() - date.getTime();
194
+ if (diff < 0) {
195
+ return formatTime(date);
196
+ }
197
+ if (diff < MINUTE) {
198
+ return "\u305F\u3063\u305F\u4ECA";
199
+ }
200
+ if (diff < HOUR) {
201
+ return `${Math.floor(diff / MINUTE)}\u5206\u524D`;
202
+ }
203
+ if (isSameDay(date, ref)) {
204
+ return `\u4ECA\u65E5 ${formatTime(date)}`;
205
+ }
206
+ if (isYesterday(date, ref)) {
207
+ return `\u6628\u65E5 ${formatTime(date)}`;
208
+ }
209
+ if (isWithinDays(date, ref, 7)) {
210
+ return `${WEEKDAYS[date.getDay()]} ${formatTime(date)}`;
211
+ }
212
+ if (date.getFullYear() === ref.getFullYear()) {
213
+ return `${padTwo(date.getMonth() + 1)}/${padTwo(date.getDate())}`;
214
+ }
215
+ return `${date.getFullYear()}/${padTwo(date.getMonth() + 1)}/${padTwo(date.getDate())}`;
216
+ }
217
+
218
+ // src/components/agent/history/group-threads.ts
219
+ var DAY_MS = 24 * 60 * 60 * 1e3;
220
+ function startOfDay(date) {
221
+ const d = new Date(date);
222
+ d.setHours(0, 0, 0, 0);
223
+ return d;
224
+ }
225
+ var GROUP_LABELS = {
226
+ today: "\u4ECA\u65E5",
227
+ yesterday: "\u6628\u65E5",
228
+ thisWeek: "\u4ECA\u9031",
229
+ older: "\u305D\u308C\u4EE5\u524D"
230
+ };
231
+ var GROUP_ORDER = ["today", "yesterday", "thisWeek", "older"];
232
+ function resolveGroupKey(updatedAt, now) {
233
+ const date = new Date(updatedAt);
234
+ if (Number.isNaN(date.getTime())) {
235
+ return "older";
236
+ }
237
+ const todayStart = startOfDay(now);
238
+ const yesterdayStart = new Date(todayStart.getTime() - DAY_MS);
239
+ const weekStart = new Date(todayStart.getTime() - 6 * DAY_MS);
240
+ if (date >= todayStart) {
241
+ return "today";
242
+ }
243
+ if (date >= yesterdayStart) {
244
+ return "yesterday";
245
+ }
246
+ if (date >= weekStart) {
247
+ return "thisWeek";
248
+ }
249
+ return "older";
250
+ }
251
+ function groupThreadsByDate(threads, now) {
252
+ const ref = /* @__PURE__ */ new Date();
253
+ const grouped = /* @__PURE__ */ new Map();
254
+ for (const thread of threads) {
255
+ const key = resolveGroupKey(thread.updatedAt, ref);
256
+ const list = grouped.get(key) ?? [];
257
+ list.push(thread);
258
+ grouped.set(key, list);
259
+ }
260
+ return GROUP_ORDER.filter((key) => grouped.has(key)).map((key) => ({
261
+ label: GROUP_LABELS[key],
262
+ threads: grouped.get(key)
263
+ }));
264
+ }
265
+ function extractFirstLine(text) {
266
+ const line = text.split("\n").find((l) => l.trim().length > 0);
267
+ return line ? line.trim() : text.trim().split("\n")[0] ?? "";
268
+ }
269
+ function resolveThreadDisplayLabel(thread) {
270
+ if (thread.firstUserText && thread.firstUserText.trim().length > 0) {
271
+ return extractFirstLine(thread.firstUserText);
272
+ }
273
+ if (thread.title && thread.title.trim().length > 0) {
274
+ return extractFirstLine(thread.title);
275
+ }
276
+ return "\u65B0\u3057\u3044\u4F1A\u8A71";
277
+ }
278
+ function AgentHistoryDropdown({
279
+ threads,
280
+ isLoading,
281
+ error,
282
+ currentThreadId,
283
+ onSelect
284
+ }) {
285
+ if (isLoading) {
286
+ return /* @__PURE__ */ jsx("div", { className: "absolute left-0 right-0 top-full z-30 mx-3 mt-1 max-w-sm rounded-lg border border-border bg-card shadow-xl", children: /* @__PURE__ */ jsx("p", { className: "px-3 py-4 text-sm text-muted-foreground", children: "\u5C65\u6B74\u3092\u8AAD\u307F\u8FBC\u307F\u4E2D..." }) });
287
+ }
288
+ if (error) {
289
+ return /* @__PURE__ */ jsx("div", { className: "absolute left-0 right-0 top-full z-30 mx-3 mt-1 rounded-lg border border-border bg-card shadow-xl", children: /* @__PURE__ */ jsx("p", { className: "px-3 py-4 text-sm text-red-600", children: error }) });
290
+ }
291
+ if (threads.length === 0) {
292
+ return /* @__PURE__ */ jsx("div", { className: "absolute left-0 right-0 top-full z-30 mx-3 mt-1 rounded-lg border border-border bg-card shadow-xl", children: /* @__PURE__ */ jsx("p", { className: "px-3 py-4 text-sm text-muted-foreground", children: "\u5C65\u6B74\u306F\u307E\u3060\u3042\u308A\u307E\u305B\u3093" }) });
293
+ }
294
+ const groups = groupThreadsByDate(threads);
295
+ return /* @__PURE__ */ jsx("div", { className: "absolute left-0 right-0 top-full z-30 mx-3 mt-1 max-h-[60vh] max-w-sm overflow-y-auto rounded-lg border border-border bg-card shadow-xl", children: groups.map((group) => /* @__PURE__ */ jsxs("div", { children: [
296
+ /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 border-b border-border/50 bg-card/95 px-3 py-1.5 backdrop-blur-sm", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-muted-foreground", children: group.label }) }),
297
+ /* @__PURE__ */ jsx("ul", { className: "p-1", children: group.threads.map((thread) => {
298
+ const isActive = currentThreadId === thread.id;
299
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
300
+ "button",
301
+ {
302
+ type: "button",
303
+ className: `w-full rounded-md px-3 py-2.5 text-left transition-colors ${isActive ? "border-l-2 border-l-primary bg-primary/5" : "hover:bg-muted/80"}`,
304
+ onClick: () => onSelect(thread.id),
305
+ children: [
306
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: resolveThreadDisplayLabel(thread) }),
307
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: formatRelativeTimestamp(thread.updatedAt) })
308
+ ]
309
+ }
310
+ ) }, thread.id);
311
+ }) })
312
+ ] }, group.label)) });
313
+ }
314
+ function Textarea({ className, ...props }) {
315
+ return /* @__PURE__ */ jsx(
316
+ "textarea",
317
+ {
318
+ "data-slot": "textarea",
319
+ className: cn(
320
+ "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
321
+ className
322
+ ),
323
+ ...props
324
+ }
325
+ );
326
+ }
327
+ function AgentInput({
328
+ value: controlledValue,
329
+ onChange,
330
+ onSend,
331
+ onAttach,
332
+ placeholder = "AI\u306B\u76F8\u8AC7\u3057\u3066\u307F\u307E\u3057\u3087\u3046",
333
+ disabled = false,
334
+ isLoading = false,
335
+ className
336
+ }) {
337
+ const [uncontrolledValue, setUncontrolledValue] = useState("");
338
+ const textareaRef = useRef(null);
339
+ const isControlled = controlledValue !== void 0;
340
+ const value = isControlled ? controlledValue : uncontrolledValue;
341
+ const resetTextareaHeight = useCallback(() => {
342
+ if (textareaRef.current) {
343
+ textareaRef.current.style.height = "auto";
344
+ }
345
+ }, []);
346
+ const adjustTextareaHeight = useCallback(() => {
347
+ const textarea = textareaRef.current;
348
+ if (textarea) {
349
+ textarea.style.height = "auto";
350
+ textarea.style.height = `${Math.min(textarea.scrollHeight, 160)}px`;
351
+ }
352
+ }, []);
353
+ const handleChange = useCallback(
354
+ (e) => {
355
+ const newValue = e.target.value;
356
+ if (!isControlled) {
357
+ setUncontrolledValue(newValue);
358
+ }
359
+ onChange?.(newValue);
360
+ adjustTextareaHeight();
361
+ },
362
+ [isControlled, onChange, adjustTextareaHeight]
363
+ );
364
+ const handleSend = useCallback(() => {
365
+ if (value.trim() && !disabled && !isLoading) {
366
+ onSend?.(value.trim());
367
+ if (!isControlled) {
368
+ setUncontrolledValue("");
369
+ }
370
+ resetTextareaHeight();
371
+ }
372
+ }, [value, disabled, isLoading, onSend, isControlled, resetTextareaHeight]);
373
+ const handleKeyDown = useCallback(
374
+ (e) => {
375
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
376
+ e.preventDefault();
377
+ handleSend();
378
+ }
379
+ },
380
+ [handleSend]
381
+ );
382
+ const canSend = value.trim().length > 0 && !disabled && !isLoading;
383
+ return /* @__PURE__ */ jsx(
384
+ "div",
385
+ {
386
+ "data-slot": "agent-input",
387
+ className: cn("w-full border-t border-border bg-muted px-3 pb-3 pt-2", className),
388
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-1 p-2 rounded-lg border border-border bg-background transition-colors", children: [
389
+ /* @__PURE__ */ jsx(
390
+ Button,
391
+ {
392
+ type: "button",
393
+ variant: "ghost",
394
+ size: "icon-sm",
395
+ onClick: onAttach,
396
+ disabled,
397
+ "aria-label": "\u30D5\u30A1\u30A4\u30EB\u3092\u6DFB\u4ED8",
398
+ className: "mb-0.5 text-muted-foreground hover:text-foreground",
399
+ children: /* @__PURE__ */ jsx(Paperclip, { className: "size-4", weight: "bold" })
400
+ }
401
+ ),
402
+ /* @__PURE__ */ jsx(
403
+ Textarea,
404
+ {
405
+ ref: textareaRef,
406
+ value,
407
+ onChange: handleChange,
408
+ onKeyDown: handleKeyDown,
409
+ placeholder,
410
+ disabled,
411
+ rows: 1,
412
+ className: "border-none p-1 resize-none min-h-8 focus-visible:ring-0 focus-visible:border-0 bg-transparent"
413
+ }
414
+ ),
415
+ /* @__PURE__ */ jsx(
416
+ Button,
417
+ {
418
+ type: "button",
419
+ size: "icon-sm",
420
+ onClick: handleSend,
421
+ disabled: !canSend,
422
+ "aria-label": "\u9001\u4FE1 (\u2318+Enter)",
423
+ children: /* @__PURE__ */ jsx(PaperPlaneTilt, { className: "size-5", weight: "fill" })
424
+ }
425
+ )
426
+ ] })
427
+ }
428
+ );
429
+ }
430
+ var listItemPattern = /^\s*(?:[-*+]\s+|\d+[.)]\s+)/;
431
+ var codeFenceStartPattern = /^\s*```/;
432
+ var blockquotePattern = /^\s*>/;
433
+ function isListItemLine(line) {
434
+ return listItemPattern.test(line);
435
+ }
436
+ function isPotentialParagraphLine(line) {
437
+ if (!line.trim()) return false;
438
+ if (/^(?: {4,}|\t)/.test(line)) return false;
439
+ const normalizedLine = line.replace(/^[ \u3000]+/, "");
440
+ if (!normalizedLine) return false;
441
+ if (isListItemLine(normalizedLine)) return false;
442
+ if (blockquotePattern.test(normalizedLine)) return false;
443
+ if (codeFenceStartPattern.test(normalizedLine)) return false;
444
+ return true;
445
+ }
446
+ function normalizeAgentMarkdown(content) {
447
+ const lines = content.replace(/\r\n?/g, "\n").split("\n");
448
+ const normalized = [];
449
+ for (const line of lines) {
450
+ const prevLine = normalized[normalized.length - 1] ?? "";
451
+ if (isListItemLine(prevLine) && isPotentialParagraphLine(line)) {
452
+ normalized.push("");
453
+ }
454
+ normalized.push(line);
455
+ }
456
+ return normalized.join("\n");
457
+ }
458
+ var components = {
459
+ pre: ({ children }) => /* @__PURE__ */ jsx("pre", { className: "bg-background/50 rounded p-2 overflow-x-auto my-1.5 text-xs", children }),
460
+ code: ({ children, className }) => {
461
+ if (className) {
462
+ return /* @__PURE__ */ jsx("code", { className: cn("text-xs", className), children });
463
+ }
464
+ return /* @__PURE__ */ jsx("code", { className: "bg-background/50 px-1 py-0.5 rounded text-xs", children });
465
+ },
466
+ a: ({ href, children }) => /* @__PURE__ */ jsx(
467
+ "a",
468
+ {
469
+ href,
470
+ target: "_blank",
471
+ rel: "noopener noreferrer",
472
+ className: "text-primary underline hover:text-primary/80",
473
+ children
474
+ }
475
+ ),
476
+ ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "list-disc list-inside pl-3 my-1", children }),
477
+ ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "list-decimal list-inside pl-3 my-1", children }),
478
+ li: ({ children }) => /* @__PURE__ */ jsx("li", { className: "leading-relaxed", children }),
479
+ p: ({ children }) => /* @__PURE__ */ jsx("p", { className: "first:mt-0 last:mb-0", children }),
480
+ h1: ({ children }) => /* @__PURE__ */ jsx("h1", { className: "text-base font-bold mt-3 mb-1 first:mt-0", children }),
481
+ h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: "text-sm font-bold mt-2.5 mb-1 first:mt-0", children }),
482
+ h3: ({ children }) => /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold mt-2 mb-1 first:mt-0", children }),
483
+ blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", { className: "border-l-2 border-border pl-3 my-1.5 text-muted-foreground italic", children }),
484
+ table: ({ children }) => /* @__PURE__ */ jsx("div", { className: "overflow-x-auto my-1.5", children: /* @__PURE__ */ jsx("table", { className: "min-w-full border-collapse border border-border text-xs", children }) }),
485
+ thead: ({ children }) => /* @__PURE__ */ jsx("thead", { className: "bg-muted/50", children }),
486
+ th: ({ children }) => /* @__PURE__ */ jsx("th", { className: "border border-border px-2 py-1.5 text-left font-semibold", children }),
487
+ td: ({ children }) => /* @__PURE__ */ jsx("td", { className: "border border-border px-2 py-1.5", children }),
488
+ hr: () => /* @__PURE__ */ jsx("hr", { className: "my-3 border-border" })
489
+ };
490
+ function Markdown({ content, className }) {
491
+ const normalizedContent = normalizeAgentMarkdown(content);
492
+ return /* @__PURE__ */ jsx("div", { className: cn("text-sm leading-relaxed", className), children: /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components, children: normalizedContent }) });
493
+ }
494
+ var defaultStatusLabels = {
495
+ pending: "\u5F85\u6A5F\u4E2D",
496
+ "awaiting-approval": "\u627F\u8A8D\u5F85\u3061",
497
+ executing: "\u5B9F\u884C\u4E2D",
498
+ completed: "\u5B8C\u4E86",
499
+ error: "\u30A8\u30E9\u30FC"
500
+ };
501
+ var statusColors = {
502
+ pending: "bg-muted text-muted-foreground",
503
+ "awaiting-approval": "bg-amber-50 text-amber-700 dark:bg-amber-950 dark:text-amber-300",
504
+ executing: "bg-primary/10 text-primary",
505
+ completed: "bg-green-50 text-green-600 dark:bg-green-950 dark:text-green-400",
506
+ error: "bg-destructive/10 text-destructive"
507
+ };
508
+ function ToolCallCard({
509
+ toolCallState,
510
+ statusLabels,
511
+ parametersLabel = "\u30D1\u30E9\u30E1\u30FC\u30BF",
512
+ resultLabel = "\u7D50\u679C\u3092\u8868\u793A"
513
+ }) {
514
+ const { toolCall, status, result, error } = toolCallState;
515
+ const toolLabel = toolCall.toolName;
516
+ const mergedStatusLabels = statusLabels ? { ...defaultStatusLabels, ...statusLabels } : defaultStatusLabels;
517
+ const statusLabel = mergedStatusLabels[status];
518
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-md p-1 mt-3 bg-card", children: [
519
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
520
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground", children: toolLabel }),
521
+ status === "executing" ? /* @__PURE__ */ jsx("div", { className: "h-3 w-3 animate-spin rounded-full border border-primary border-t-transparent" }) : /* @__PURE__ */ jsx("span", { className: cn("rounded-full px-2 py-0.5 text-[11px]", statusColors[status]), children: statusLabel })
522
+ ] }),
523
+ /* @__PURE__ */ jsxs("details", { className: "mt-2", children: [
524
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-xs text-muted-foreground", children: parametersLabel }),
525
+ /* @__PURE__ */ jsx("pre", { className: "mt-1 ml-4 overflow-x-auto rounded bg-muted p-2 text-xs text-muted-foreground", children: JSON.stringify(toolCall.input, null, 2) })
526
+ ] }),
527
+ status === "completed" && result !== void 0 && /* @__PURE__ */ jsxs("details", { className: "mt-2", children: [
528
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-xs text-muted-foreground", children: resultLabel }),
529
+ /* @__PURE__ */ jsx("pre", { className: "mt-1 ml-4 max-h-40 overflow-auto rounded bg-muted p-2 text-xs text-muted-foreground", children: typeof result === "string" ? result : JSON.stringify(result, null, 2) })
530
+ ] }),
531
+ status === "error" && error && /* @__PURE__ */ jsx("div", { className: "mt-2 rounded bg-destructive/10 p-2 text-xs text-destructive", children: error })
532
+ ] });
533
+ }
534
+
535
+ // src/components/agent/message/utils.ts
536
+ function extractTextContent(content) {
537
+ return content.filter((part) => part.type === "text").map((part) => part.text).join("");
538
+ }
539
+ function extractToolCalls(content) {
540
+ return content.filter((part) => part.type === "tool-call").map((part) => part.toolCall);
541
+ }
542
+ function shouldShowLoadingIndicator({
543
+ messages,
544
+ isLoading
545
+ }) {
546
+ if (!isLoading) return false;
547
+ const lastMessage = messages[messages.length - 1];
548
+ const hasNoMessages = messages.length === 0;
549
+ if (hasNoMessages) return true;
550
+ const lastAssistantHasContent = lastMessage?.role === "assistant" && extractTextContent(lastMessage.content).trim().length > 0;
551
+ return lastMessage?.role === "user" || !lastAssistantHasContent;
552
+ }
553
+ function MessageItem({ message, pendingToolCalls }) {
554
+ const isUser = message.role === "user";
555
+ const textContent = extractTextContent(message.content);
556
+ const toolCalls = extractToolCalls(message.content);
557
+ if (!isUser && message.isStreaming && !textContent && toolCalls.length === 0) {
558
+ return null;
559
+ }
560
+ return /* @__PURE__ */ jsx("div", { className: cn("flex", isUser ? "justify-end" : "justify-start"), children: /* @__PURE__ */ jsxs("div", { className: "max-w-3/4 space-y-1.5", children: [
561
+ textContent && /* @__PURE__ */ jsx(
562
+ "div",
563
+ {
564
+ className: cn(
565
+ "rounded-lg px-3 py-2",
566
+ isUser ? "bg-muted text-foreground" : "border border-border bg-card text-card-foreground"
567
+ ),
568
+ children: isUser ? /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap text-sm leading-relaxed", children: textContent }) : /* @__PURE__ */ jsx(Markdown, { content: textContent })
569
+ }
570
+ ),
571
+ !isUser && toolCalls.length > 0 && /* @__PURE__ */ jsx("div", { className: "ml-2 space-y-1.5 border-l-2 border-border pl-2", children: toolCalls.map((tc) => {
572
+ const toolCallState = pendingToolCalls.find(
573
+ (p) => p.toolCall.toolCallId === tc.toolCallId
574
+ );
575
+ if (toolCallState) {
576
+ return /* @__PURE__ */ jsx(
577
+ ToolCallCard,
578
+ {
579
+ toolCallState
580
+ },
581
+ tc.toolCallId
582
+ );
583
+ }
584
+ return null;
585
+ }) })
586
+ ] }) });
587
+ }
588
+ function LoadingDots() {
589
+ return /* @__PURE__ */ jsxs("span", { className: "flex gap-0.5", "aria-hidden": "true", children: [
590
+ /* @__PURE__ */ jsx("span", { className: "h-0.5 w-0.5 animate-dot-fade rounded-full bg-muted-foreground/50 [animation-delay:0ms]" }),
591
+ /* @__PURE__ */ jsx("span", { className: "h-0.5 w-0.5 animate-dot-fade rounded-full bg-muted-foreground/50 [animation-delay:150ms]" }),
592
+ /* @__PURE__ */ jsx("span", { className: "h-0.5 w-0.5 animate-dot-fade rounded-full bg-muted-foreground/50 [animation-delay:300ms]" })
593
+ ] });
594
+ }
595
+ function MessageLoading({ className }) {
596
+ return /* @__PURE__ */ jsx("output", { className: cn("flex justify-start", className), "aria-label": "AI\u5FDC\u7B54\u3092\u5F85\u6A5F\u4E2D", children: /* @__PURE__ */ jsx("div", { className: "max-w-xl", children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-card px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
597
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "\u8003\u3048\u4E2D" }),
598
+ /* @__PURE__ */ jsx(LoadingDots, {})
599
+ ] }) }) }) });
600
+ }
601
+ function MessageList({
602
+ messages,
603
+ pendingToolCalls,
604
+ isLoading = false
605
+ }) {
606
+ const bottomRef = useRef(null);
607
+ useEffect(() => {
608
+ bottomRef.current?.scrollIntoView({ behavior: "smooth" });
609
+ }, [messages, isLoading]);
610
+ if (messages.length === 0 && !isLoading) {
611
+ return null;
612
+ }
613
+ const showLoadingIndicator = shouldShowLoadingIndicator({
614
+ messages,
615
+ isLoading
616
+ });
617
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 p-3", children: [
618
+ messages.map((message) => /* @__PURE__ */ jsx(
619
+ MessageItem,
620
+ {
621
+ message,
622
+ pendingToolCalls
623
+ },
624
+ message.id
625
+ )),
626
+ showLoadingIndicator && /* @__PURE__ */ jsx(MessageLoading, {}),
627
+ /* @__PURE__ */ jsx("div", { ref: bottomRef })
628
+ ] });
629
+ }
630
+
631
+ // src/components/agent/approval-ui-model.ts
632
+ var DEFAULT_TOOL_APPROVAL_LABELS = {
633
+ title: "\u3053\u306E\u64CD\u4F5C\u3092\u5B9F\u884C\u3057\u3066\u3082\u3088\u308D\u3057\u3044\u3067\u3057\u3087\u3046\u304B\uFF1F",
634
+ approveButton: "\u5B9F\u884C\u3059\u308B",
635
+ rejectButton: "\u5B9F\u884C\u3057\u306A\u3044"
636
+ };
637
+ function resolveToolApprovalLabels(toolApprovalLabels) {
638
+ if (!toolApprovalLabels) {
639
+ return DEFAULT_TOOL_APPROVAL_LABELS;
640
+ }
641
+ return {
642
+ title: toolApprovalLabels.title ?? DEFAULT_TOOL_APPROVAL_LABELS.title,
643
+ approveButton: toolApprovalLabels.approveButton ?? DEFAULT_TOOL_APPROVAL_LABELS.approveButton,
644
+ rejectButton: toolApprovalLabels.rejectButton ?? DEFAULT_TOOL_APPROVAL_LABELS.rejectButton
645
+ };
646
+ }
647
+ function resolveToolApprovalPanelState(activeApprovalRequest) {
648
+ if (!activeApprovalRequest) {
649
+ return {
650
+ isActionable: false
651
+ };
652
+ }
653
+ return {
654
+ actionLabel: activeApprovalRequest.actionLabel,
655
+ isActionable: true
656
+ };
657
+ }
658
+ function ToolApprovalPanel({
659
+ activeApprovalRequest,
660
+ approveToolCall,
661
+ rejectToolCall,
662
+ toolApprovalLabels
663
+ }) {
664
+ const approvalLabels = resolveToolApprovalLabels(toolApprovalLabels);
665
+ const panelState = resolveToolApprovalPanelState(activeApprovalRequest);
666
+ return /* @__PURE__ */ jsx(
667
+ "div",
668
+ {
669
+ "data-slot": "agent-tool-approval",
670
+ className: "w-full border-t border-border bg-muted px-3 pb-3 pt-2",
671
+ children: /* @__PURE__ */ jsxs(
672
+ "div",
673
+ {
674
+ "data-state": panelState.isActionable ? "actionable" : "idle",
675
+ className: cn(
676
+ "flex flex-col gap-2 rounded-lg border border-border bg-primary-foreground p-3 shadow-sm",
677
+ !panelState.isActionable && "opacity-75"
678
+ ),
679
+ children: [
680
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-bold text-foreground text-balance", children: approvalLabels.title }),
681
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 pt-1", children: [
682
+ /* @__PURE__ */ jsx(
683
+ Button,
684
+ {
685
+ type: "button",
686
+ variant: "outline",
687
+ onClick: approveToolCall,
688
+ disabled: !panelState.isActionable,
689
+ className: "justify-start",
690
+ children: approvalLabels.approveButton
691
+ }
692
+ ),
693
+ /* @__PURE__ */ jsx(
694
+ Button,
695
+ {
696
+ type: "button",
697
+ variant: "outline",
698
+ onClick: rejectToolCall,
699
+ disabled: !panelState.isActionable,
700
+ className: "justify-start",
701
+ children: approvalLabels.rejectButton
702
+ }
703
+ )
704
+ ] })
705
+ ]
706
+ }
707
+ )
708
+ }
709
+ );
710
+ }
711
+ function AgentContainerView({
712
+ onNewChatClick,
713
+ onHistoryClick,
714
+ onHistorySelect,
715
+ isHistoryOpen = false,
716
+ historyThreads,
717
+ isHistoryLoading,
718
+ historyError,
719
+ currentThreadId,
720
+ historyAreaRef,
721
+ inputProps,
722
+ className,
723
+ messages,
724
+ pendingToolCalls,
725
+ activeApprovalRequest,
726
+ approveToolCall,
727
+ rejectToolCall,
728
+ toolApprovalLabels,
729
+ isLoading,
730
+ error
731
+ }) {
732
+ const layoutState = resolveAgentContainerLayoutState({
733
+ messageCount: messages.length,
734
+ isLoading,
735
+ hasActiveApprovalRequest: Boolean(activeApprovalRequest),
736
+ hasError: Boolean(error)
737
+ });
738
+ return /* @__PURE__ */ jsxs(
739
+ "div",
740
+ {
741
+ "data-slot": "agent-container",
742
+ className: cn("flex h-full w-full flex-col mx-auto max-w-[1120px]", className),
743
+ children: [
744
+ /* @__PURE__ */ jsxs("div", { ref: historyAreaRef, className: "relative", children: [
745
+ /* @__PURE__ */ jsx(
746
+ AgentHeader,
747
+ {
748
+ onNewChatClick,
749
+ onHistoryClick,
750
+ isHistoryOpen
751
+ }
752
+ ),
753
+ isHistoryOpen && /* @__PURE__ */ jsx(
754
+ AgentHistoryDropdown,
755
+ {
756
+ threads: historyThreads,
757
+ isLoading: isHistoryLoading,
758
+ error: historyError,
759
+ currentThreadId,
760
+ onSelect: onHistorySelect ?? (() => void 0)
761
+ }
762
+ )
763
+ ] }),
764
+ layoutState.shouldShowMessages ? /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto bg-white pb-[97px]", children: [
765
+ /* @__PURE__ */ jsx(
766
+ MessageList,
767
+ {
768
+ messages,
769
+ pendingToolCalls,
770
+ isLoading
771
+ }
772
+ ),
773
+ error && /* @__PURE__ */ jsx("div", { className: "mx-4 mb-4 rounded-lg bg-red-50 p-3 text-sm text-red-600", children: error })
774
+ ] }) : /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col items-center justify-center gap-6 bg-background px-4 py-4", children: /* @__PURE__ */ jsx(AgentGreeting, {}) }),
775
+ /* @__PURE__ */ jsxs(
776
+ "div",
777
+ {
778
+ className: "fixed bottom-0 left-0 right-0 mx-auto w-full max-w-[1120px] bg-background",
779
+ children: [
780
+ layoutState.shouldShowInput && /* @__PURE__ */ jsx(AgentInput, { ...inputProps }),
781
+ layoutState.shouldShowToolApprovalPanel && /* @__PURE__ */ jsx(
782
+ ToolApprovalPanel,
783
+ {
784
+ activeApprovalRequest,
785
+ approveToolCall,
786
+ rejectToolCall,
787
+ toolApprovalLabels
788
+ }
789
+ )
790
+ ]
791
+ }
792
+ )
793
+ ]
794
+ }
795
+ );
796
+ }
797
+ var AgentContext = createContext(null);
798
+ function useOptionalAgentContext() {
799
+ return useContext(AgentContext);
800
+ }
801
+ function AgentContainer({ className }) {
802
+ const context = useOptionalAgentContext();
803
+ const messages = context?.messages ?? [];
804
+ const pendingToolCalls = context?.pendingToolCalls ?? [];
805
+ const activeApprovalRequest = context?.activeApprovalRequest ?? null;
806
+ const approveToolCall = context?.approveToolCall ?? (() => void 0);
807
+ const rejectToolCall = context?.rejectToolCall ?? (() => void 0);
808
+ const toolApprovalLabels = context?.config.toolApprovalLabels;
809
+ const error = context?.error ?? null;
810
+ const isLoading = context?.isLoading ?? false;
811
+ const historyThreads = context?.historyThreads ?? [];
812
+ const isHistoryLoading = context?.isHistoryLoading ?? false;
813
+ const historyError = context?.historyError ?? null;
814
+ const loadHistoryThreads = context?.loadHistoryThreads;
815
+ const loadThreadMessages = context?.loadThreadMessages;
816
+ const sendMessage = context?.sendMessage;
817
+ const onNewChatClick = context?.clearChat;
818
+ const currentThreadId = context?.threadId ?? null;
819
+ const [isHistoryOpen, setIsHistoryOpen] = useState(false);
820
+ const historyAreaRef = useRef(null);
821
+ const effectiveInputProps = useMemo(
822
+ () => resolveEffectiveInputProps({ sendMessage, isLoading }),
823
+ [sendMessage, isLoading]
824
+ );
825
+ const onHistoryClick = useCallback(() => {
826
+ if (!isHistoryOpen) {
827
+ void loadHistoryThreads?.();
828
+ }
829
+ setIsHistoryOpen((prev) => !prev);
830
+ }, [isHistoryOpen, loadHistoryThreads]);
831
+ const onHistorySelect = useCallback(
832
+ (threadId) => {
833
+ void loadThreadMessages?.(threadId);
834
+ setIsHistoryOpen(false);
835
+ },
836
+ [loadThreadMessages]
837
+ );
838
+ useEffect(() => {
839
+ if (!isHistoryOpen) return;
840
+ function handleClickOutside(event) {
841
+ if (historyAreaRef.current && !historyAreaRef.current.contains(event.target)) {
842
+ setIsHistoryOpen(false);
843
+ }
844
+ }
845
+ document.addEventListener("mousedown", handleClickOutside);
846
+ return () => document.removeEventListener("mousedown", handleClickOutside);
847
+ }, [isHistoryOpen]);
848
+ return /* @__PURE__ */ jsx(
849
+ AgentContainerView,
850
+ {
851
+ onNewChatClick,
852
+ onHistoryClick,
853
+ isHistoryOpen,
854
+ historyThreads,
855
+ isHistoryLoading,
856
+ historyError,
857
+ currentThreadId,
858
+ onHistorySelect,
859
+ historyAreaRef,
860
+ inputProps: effectiveInputProps,
861
+ className,
862
+ messages,
863
+ pendingToolCalls,
864
+ activeApprovalRequest,
865
+ approveToolCall,
866
+ rejectToolCall,
867
+ toolApprovalLabels,
868
+ isLoading,
869
+ error
870
+ }
871
+ );
872
+ }
873
+ function createAgentAuthClient(agentServerUrl) {
874
+ const baseURL = agentServerUrl.replace(/\/api\/agent\/?$/, "");
875
+ return createAuthClient({
876
+ baseURL,
877
+ basePath: "/api/auth",
878
+ plugins: [anonymousClient()]
879
+ });
880
+ }
881
+
882
+ // src/modules/agent/auth/use-agent-auth.ts
883
+ function useAgentAuth(agentServerUrl) {
884
+ const client = useMemo(() => createAgentAuthClient(agentServerUrl), [agentServerUrl]);
885
+ const [isReady, setIsReady] = useState(false);
886
+ const [error, setError] = useState(null);
887
+ const [userId, setUserId] = useState(null);
888
+ const tokenRef = useRef("");
889
+ useEffect(() => {
890
+ let cancelled = false;
891
+ let timeoutId;
892
+ async function init() {
893
+ try {
894
+ const sessionResult = await client.getSession();
895
+ if (cancelled) return;
896
+ if (sessionResult.data?.session) {
897
+ tokenRef.current = sessionResult.data.session.token;
898
+ setUserId(sessionResult.data.user.id);
899
+ setError(null);
900
+ setIsReady(true);
901
+ return;
902
+ }
903
+ const signInResult = await client.signIn.anonymous();
904
+ if (cancelled) return;
905
+ if (signInResult.error) {
906
+ setError(signInResult.error.message ?? "Anonymous sign-in failed");
907
+ setIsReady(true);
908
+ return;
909
+ }
910
+ if (signInResult.data) {
911
+ tokenRef.current = signInResult.data.token ?? "";
912
+ setUserId(signInResult.data.user?.id ?? null);
913
+ }
914
+ setError(null);
915
+ setIsReady(true);
916
+ } catch (e) {
917
+ if (cancelled) return;
918
+ setError(e instanceof Error ? e.message : "Auth initialization failed");
919
+ setIsReady(true);
920
+ } finally {
921
+ if (timeoutId !== void 0) {
922
+ clearTimeout(timeoutId);
923
+ }
924
+ }
925
+ }
926
+ timeoutId = setTimeout(() => {
927
+ if (!cancelled) {
928
+ setError("Auth initialization timed out");
929
+ setIsReady(true);
930
+ }
931
+ }, 1e4);
932
+ init();
933
+ return () => {
934
+ cancelled = true;
935
+ if (timeoutId !== void 0) {
936
+ clearTimeout(timeoutId);
937
+ }
938
+ };
939
+ }, [client]);
940
+ const getToken = useCallback(async () => {
941
+ try {
942
+ const sessionResult = await client.getSession();
943
+ if (sessionResult.data?.session?.token) {
944
+ tokenRef.current = sessionResult.data.session.token;
945
+ }
946
+ } catch {
947
+ }
948
+ return tokenRef.current;
949
+ }, [client]);
950
+ return { isReady, error, userId, getToken };
951
+ }
952
+
953
+ // src/modules/agent/agent.repository.ts
954
+ function isRecord(value) {
955
+ return typeof value === "object" && value !== null;
956
+ }
957
+ function readRecord(value) {
958
+ if (!isRecord(value)) {
959
+ return null;
960
+ }
961
+ return value;
962
+ }
963
+ function readString(value) {
964
+ if (typeof value === "string") {
965
+ return value;
966
+ }
967
+ return void 0;
968
+ }
969
+ function readNumber(value) {
970
+ if (typeof value === "number") {
971
+ return value;
972
+ }
973
+ return void 0;
974
+ }
975
+ function readArray(value) {
976
+ if (Array.isArray(value)) {
977
+ return value;
978
+ }
979
+ return void 0;
980
+ }
981
+ function isAgentStreamEvent(value) {
982
+ const event = readRecord(value);
983
+ if (!event) {
984
+ return false;
985
+ }
986
+ const type = readString(event.type);
987
+ const payload = readRecord(event.payload);
988
+ if (!type || !payload) {
989
+ return false;
990
+ }
991
+ if (type === "text-delta") {
992
+ return typeof payload.text === "string";
993
+ }
994
+ if (type === "tool-intent") {
995
+ return typeof payload.toolCallId === "string" && typeof payload.toolName === "string";
996
+ }
997
+ if (type === "tool-call") {
998
+ return typeof payload.toolCallId === "string" && typeof payload.toolName === "string";
999
+ }
1000
+ if (type === "tool-result") {
1001
+ return typeof payload.toolCallId === "string";
1002
+ }
1003
+ if (type === "finish") {
1004
+ return true;
1005
+ }
1006
+ if (type === "error") {
1007
+ return typeof payload.error === "string";
1008
+ }
1009
+ if (type === "thread-id") {
1010
+ return typeof payload.threadId === "string";
1011
+ }
1012
+ if (type === "agent-id") {
1013
+ return typeof payload.agentId === "string";
1014
+ }
1015
+ return false;
1016
+ }
1017
+ function parseSSEBuffer(buffer) {
1018
+ const events = [];
1019
+ const lines = buffer.split("\n");
1020
+ const remaining = lines.pop() || "";
1021
+ for (const line of lines) {
1022
+ if (!line.startsWith("data: ")) {
1023
+ continue;
1024
+ }
1025
+ const jsonString = line.slice(6);
1026
+ if (!jsonString) {
1027
+ continue;
1028
+ }
1029
+ try {
1030
+ const parsed = JSON.parse(jsonString);
1031
+ if (isAgentStreamEvent(parsed)) {
1032
+ events.push(parsed);
1033
+ }
1034
+ } catch (error) {
1035
+ console.warn("Failed to parse SSE event:", error);
1036
+ }
1037
+ }
1038
+ return { events, remaining };
1039
+ }
1040
+ function buildToolEndpointRequest(toolCall, apiBaseUrl) {
1041
+ if (!toolCall.apiRequest) {
1042
+ return null;
1043
+ }
1044
+ const { apiRequest, input } = toolCall;
1045
+ const inputData = readRecord(input) ?? {};
1046
+ const method = apiRequest.method.toUpperCase();
1047
+ let path = apiRequest.path;
1048
+ const usedParams = /* @__PURE__ */ new Set();
1049
+ for (const paramName of apiRequest.pathParams) {
1050
+ const value = inputData[paramName];
1051
+ if (value === void 0) {
1052
+ continue;
1053
+ }
1054
+ path = path.replace(`{${paramName}}`, encodeURIComponent(String(value)));
1055
+ usedParams.add(paramName);
1056
+ }
1057
+ const queryParams = {};
1058
+ const bodyData = {};
1059
+ for (const key of apiRequest.queryParams) {
1060
+ const value = inputData[key];
1061
+ if (value === void 0) {
1062
+ continue;
1063
+ }
1064
+ queryParams[key] = String(value);
1065
+ usedParams.add(key);
1066
+ }
1067
+ for (const key of apiRequest.bodyFields) {
1068
+ const value = inputData[key];
1069
+ if (value === void 0) {
1070
+ continue;
1071
+ }
1072
+ bodyData[key] = value;
1073
+ usedParams.add(key);
1074
+ }
1075
+ for (const [key, value] of Object.entries(inputData)) {
1076
+ if (usedParams.has(key) || value === void 0) {
1077
+ continue;
1078
+ }
1079
+ if (method === "GET" || method === "DELETE") {
1080
+ queryParams[key] = String(value);
1081
+ continue;
1082
+ }
1083
+ bodyData[key] = value;
1084
+ }
1085
+ const queryString = new URLSearchParams(queryParams).toString();
1086
+ return {
1087
+ method,
1088
+ url: `${apiBaseUrl}${path}${queryString ? `?${queryString}` : ""}`,
1089
+ bodyData
1090
+ };
1091
+ }
1092
+ function normalizeThreadSummary(thread) {
1093
+ const threadRecord = readRecord(thread);
1094
+ if (!threadRecord) {
1095
+ return null;
1096
+ }
1097
+ const id = readString(threadRecord.id);
1098
+ const resourceId = readString(threadRecord.resourceId);
1099
+ const createdAt = readString(threadRecord.createdAt);
1100
+ const updatedAt = readString(threadRecord.updatedAt);
1101
+ if (!id || !resourceId || !createdAt || !updatedAt) {
1102
+ return null;
1103
+ }
1104
+ return {
1105
+ id,
1106
+ resourceId,
1107
+ title: readString(threadRecord.title),
1108
+ createdAt,
1109
+ updatedAt,
1110
+ firstUserText: readString(threadRecord.firstUserText)
1111
+ };
1112
+ }
1113
+ function normalizeListThreadsResponse(body) {
1114
+ const bodyRecord = readRecord(body) ?? {};
1115
+ const threadValues = readArray(bodyRecord.threads);
1116
+ const threads = threadValues ? threadValues.map((thread) => normalizeThreadSummary(thread)).filter((thread) => Boolean(thread)) : [];
1117
+ const page = readNumber(bodyRecord.page) ?? 0;
1118
+ const perPage = readNumber(bodyRecord.perPage) ?? threads.length;
1119
+ const total = readNumber(bodyRecord.total) ?? threads.length;
1120
+ return {
1121
+ threads,
1122
+ page,
1123
+ perPage,
1124
+ hasMore: Boolean(bodyRecord.hasMore),
1125
+ total
1126
+ };
1127
+ }
1128
+ function normalizeMessagePart(part) {
1129
+ const partRecord = readRecord(part);
1130
+ if (!partRecord) {
1131
+ return null;
1132
+ }
1133
+ const partType = readString(partRecord.type);
1134
+ if (!partType) {
1135
+ return null;
1136
+ }
1137
+ if (partType === "text" || partType === "input_text") {
1138
+ const text = readString(partRecord.text);
1139
+ if (text === void 0) {
1140
+ return null;
1141
+ }
1142
+ return { type: "text", text };
1143
+ }
1144
+ if (partType === "tool-call") {
1145
+ const toolCallRecord = readRecord(partRecord.toolCall);
1146
+ if (!toolCallRecord) {
1147
+ return null;
1148
+ }
1149
+ const toolCallId = readString(toolCallRecord.toolCallId);
1150
+ const toolName = readString(toolCallRecord.toolName);
1151
+ if (!toolCallId || !toolName) {
1152
+ return null;
1153
+ }
1154
+ return {
1155
+ type: "tool-call",
1156
+ toolCall: {
1157
+ toolCallId,
1158
+ toolName,
1159
+ input: toolCallRecord.input
1160
+ }
1161
+ };
1162
+ }
1163
+ if (partType === "tool-result") {
1164
+ const toolResultRecord = readRecord(partRecord.toolResult);
1165
+ if (!toolResultRecord) {
1166
+ return null;
1167
+ }
1168
+ const toolCallId = readString(toolResultRecord.toolCallId);
1169
+ if (!toolCallId) {
1170
+ return null;
1171
+ }
1172
+ return {
1173
+ type: "tool-result",
1174
+ toolResult: {
1175
+ toolCallId,
1176
+ result: toolResultRecord.result,
1177
+ isError: typeof toolResultRecord.isError === "boolean" ? toolResultRecord.isError : void 0
1178
+ }
1179
+ };
1180
+ }
1181
+ return null;
1182
+ }
1183
+ function normalizeMessageContent(content) {
1184
+ if (typeof content === "string") {
1185
+ return content.length > 0 ? [{ type: "text", text: content }] : null;
1186
+ }
1187
+ const contentValues = readArray(content);
1188
+ if (!contentValues) {
1189
+ return null;
1190
+ }
1191
+ const normalizedParts = [];
1192
+ for (const part of contentValues) {
1193
+ const normalizedPart = normalizeMessagePart(part);
1194
+ if (!normalizedPart) {
1195
+ continue;
1196
+ }
1197
+ normalizedParts.push(normalizedPart);
1198
+ }
1199
+ return normalizedParts.length > 0 ? normalizedParts : null;
1200
+ }
1201
+ function normalizeThreadMessage(message) {
1202
+ const messageRecord = readRecord(message);
1203
+ if (!messageRecord) {
1204
+ return null;
1205
+ }
1206
+ const id = readString(messageRecord.id);
1207
+ const role = readString(messageRecord.role);
1208
+ const isSupportedRole = role === "user" || role === "assistant";
1209
+ if (!id || !isSupportedRole) {
1210
+ return null;
1211
+ }
1212
+ const content = normalizeMessageContent(messageRecord.content);
1213
+ if (!content) {
1214
+ return null;
1215
+ }
1216
+ return {
1217
+ id,
1218
+ role,
1219
+ content
1220
+ };
1221
+ }
1222
+ function normalizeGetThreadMessagesResponse(body) {
1223
+ const bodyRecord = readRecord(body) ?? {};
1224
+ const messageValues = readArray(bodyRecord.messages);
1225
+ const messages = messageValues ? messageValues.map((message) => normalizeThreadMessage(message)).filter(
1226
+ (message) => Boolean(message)
1227
+ ) : [];
1228
+ const threadId = readString(bodyRecord.threadId) ?? "";
1229
+ const page = readNumber(bodyRecord.page) ?? 0;
1230
+ const perPage = readNumber(bodyRecord.perPage) ?? messages.length;
1231
+ const total = readNumber(bodyRecord.total) ?? messages.length;
1232
+ return {
1233
+ threadId,
1234
+ messages,
1235
+ page,
1236
+ perPage,
1237
+ hasMore: Boolean(bodyRecord.hasMore),
1238
+ total
1239
+ };
1240
+ }
1241
+ function createAgentRepository(options) {
1242
+ const { fetchFn = fetch, getAuthToken, disableToolApiAuthHeader = true } = options;
1243
+ const resolveToken = async () => {
1244
+ return getAuthToken ? getAuthToken() : void 0;
1245
+ };
1246
+ return {
1247
+ buildAgentHeaders: async (getAgentHeaders) => {
1248
+ const headers = {
1249
+ "Content-Type": "application/json"
1250
+ };
1251
+ const token = await resolveToken();
1252
+ if (token) {
1253
+ headers.Authorization = token.startsWith("Bearer ") ? token : `Bearer ${token}`;
1254
+ }
1255
+ const customHeaders = getAgentHeaders ? await getAgentHeaders() : void 0;
1256
+ if (!customHeaders) {
1257
+ return headers;
1258
+ }
1259
+ for (const [key, value] of Object.entries(customHeaders)) {
1260
+ if (value) {
1261
+ headers[key] = value;
1262
+ }
1263
+ }
1264
+ return headers;
1265
+ },
1266
+ buildToolApiHeaders: async () => {
1267
+ const headers = {};
1268
+ if (disableToolApiAuthHeader) {
1269
+ return headers;
1270
+ }
1271
+ const token = await resolveToken();
1272
+ if (!token) {
1273
+ return headers;
1274
+ }
1275
+ headers.Authorization = token.startsWith("Bearer ") ? token : `Bearer ${token}`;
1276
+ return headers;
1277
+ },
1278
+ sendChat: async ({ agentServerUrl, threadId, message, appId, agentId, headers, signal }) => {
1279
+ return fetchFn(`${agentServerUrl}/chat`, {
1280
+ method: "POST",
1281
+ headers,
1282
+ credentials: "include",
1283
+ body: JSON.stringify({ threadId, message, appId, ...agentId && { agentId } }),
1284
+ signal
1285
+ });
1286
+ },
1287
+ continueChat: async ({ agentServerUrl, payload, headers, signal }) => {
1288
+ return fetchFn(`${agentServerUrl}/chat/tool-result`, {
1289
+ method: "POST",
1290
+ headers,
1291
+ credentials: "include",
1292
+ body: JSON.stringify(payload),
1293
+ signal
1294
+ });
1295
+ },
1296
+ listThreads: async ({ agentServerUrl, headers, agentId, appId, signal }) => {
1297
+ const params = new URLSearchParams();
1298
+ if (agentId) {
1299
+ params.set("agentId", agentId);
1300
+ } else if (appId) {
1301
+ params.set("appId", appId);
1302
+ }
1303
+ return fetchFn(`${agentServerUrl}/chat/threads?${params.toString()}`, {
1304
+ method: "GET",
1305
+ headers,
1306
+ credentials: "include",
1307
+ signal
1308
+ });
1309
+ },
1310
+ getThreadMessages: async ({ agentServerUrl, headers, threadId, agentId, appId, signal }) => {
1311
+ const params = new URLSearchParams();
1312
+ if (agentId) {
1313
+ params.set("agentId", agentId);
1314
+ } else if (appId) {
1315
+ params.set("appId", appId);
1316
+ }
1317
+ return fetchFn(`${agentServerUrl}/chat/threads/${encodeURIComponent(threadId)}/messages?${params.toString()}`, {
1318
+ method: "GET",
1319
+ headers,
1320
+ credentials: "include",
1321
+ signal
1322
+ });
1323
+ },
1324
+ collectStreamEvents: async (response, options2) => {
1325
+ const reader = response.body?.getReader();
1326
+ if (!reader) {
1327
+ return [];
1328
+ }
1329
+ const decoder = new TextDecoder();
1330
+ const events = [];
1331
+ let buffer = "";
1332
+ try {
1333
+ while (true) {
1334
+ const { done, value } = await reader.read();
1335
+ if (done) {
1336
+ break;
1337
+ }
1338
+ buffer += decoder.decode(value, { stream: true });
1339
+ const parsed = parseSSEBuffer(buffer);
1340
+ buffer = parsed.remaining;
1341
+ events.push(...parsed.events);
1342
+ if (parsed.events.length > 0) {
1343
+ options2?.onEvents?.(parsed.events);
1344
+ }
1345
+ }
1346
+ if (buffer.startsWith("data: ")) {
1347
+ try {
1348
+ const parsed = JSON.parse(buffer.slice(6));
1349
+ if (isAgentStreamEvent(parsed)) {
1350
+ events.push(parsed);
1351
+ options2?.onEvents?.([parsed]);
1352
+ }
1353
+ } catch (error) {
1354
+ console.warn("Failed to parse remaining SSE event:", error);
1355
+ }
1356
+ }
1357
+ return events;
1358
+ } finally {
1359
+ reader.releaseLock();
1360
+ }
1361
+ },
1362
+ executeToolCall: async ({ toolCall, apiBaseUrl, authHeaders }) => {
1363
+ const request = buildToolEndpointRequest(toolCall, apiBaseUrl);
1364
+ if (!request) {
1365
+ return { output: "No API endpoint defined for this tool", isError: true };
1366
+ }
1367
+ try {
1368
+ const fetchOptions = {
1369
+ method: request.method,
1370
+ headers: {
1371
+ "Content-Type": "application/json",
1372
+ ...authHeaders
1373
+ },
1374
+ credentials: "include"
1375
+ };
1376
+ if ((request.method === "POST" || request.method === "PUT" || request.method === "PATCH") && Object.keys(request.bodyData).length > 0) {
1377
+ fetchOptions.body = JSON.stringify(request.bodyData);
1378
+ }
1379
+ const response = await fetchFn(request.url, fetchOptions);
1380
+ const text = await response.text();
1381
+ let data;
1382
+ try {
1383
+ data = text ? JSON.parse(text) : null;
1384
+ } catch {
1385
+ data = { raw: text, status: response.status };
1386
+ }
1387
+ if (!response.ok) {
1388
+ return { output: data, isError: true };
1389
+ }
1390
+ return { output: data };
1391
+ } catch (error) {
1392
+ return {
1393
+ output: error instanceof Error ? error.message : "Network error",
1394
+ isError: true
1395
+ };
1396
+ }
1397
+ },
1398
+ parseErrorResponse: async (response, fallback) => {
1399
+ try {
1400
+ const contentType = response.headers.get("content-type");
1401
+ if (contentType?.includes("application/json")) {
1402
+ const errorData = await response.json();
1403
+ if (typeof errorData?.error === "string") {
1404
+ return errorData.error;
1405
+ }
1406
+ if (typeof errorData?.message === "string") {
1407
+ return errorData.message;
1408
+ }
1409
+ }
1410
+ } catch (error) {
1411
+ console.warn("Failed to parse error response:", error);
1412
+ }
1413
+ return fallback;
1414
+ }
1415
+ };
1416
+ }
1417
+
1418
+ // src/modules/agent/history/merge-history-threads.ts
1419
+ function mergeHistoryThreads(serverThreads, threadIndexByAgent) {
1420
+ const entries = threadIndexByAgent.map((item) => [
1421
+ item.threadId,
1422
+ item
1423
+ ]);
1424
+ const threadIndexMap = new Map(entries);
1425
+ return serverThreads.map((thread) => {
1426
+ const indexItem = threadIndexMap.get(thread.id);
1427
+ if (!indexItem) {
1428
+ return thread;
1429
+ }
1430
+ return {
1431
+ ...thread,
1432
+ firstUserText: indexItem.firstUserText || thread.firstUserText
1433
+ };
1434
+ }).sort((a, b) => a.updatedAt < b.updatedAt ? 1 : -1);
1435
+ }
1436
+
1437
+ // src/modules/agent/history/thread-index-storage.ts
1438
+ var THREAD_INDEX_STORAGE_KEY = "agent_thread_index_v1";
1439
+ function isRecord2(value) {
1440
+ return typeof value === "object" && value !== null;
1441
+ }
1442
+ function isStoredThreadIndexItem(value) {
1443
+ if (!isRecord2(value)) {
1444
+ return false;
1445
+ }
1446
+ return typeof value.threadId === "string" && typeof value.agentId === "string" && typeof value.firstUserText === "string";
1447
+ }
1448
+ function readStorageRaw() {
1449
+ if (typeof window === "undefined") {
1450
+ return null;
1451
+ }
1452
+ try {
1453
+ return window.localStorage.getItem(THREAD_INDEX_STORAGE_KEY);
1454
+ } catch {
1455
+ return null;
1456
+ }
1457
+ }
1458
+ function writeStorageRaw(value) {
1459
+ if (typeof window === "undefined") {
1460
+ return;
1461
+ }
1462
+ try {
1463
+ window.localStorage.setItem(THREAD_INDEX_STORAGE_KEY, value);
1464
+ } catch {
1465
+ }
1466
+ }
1467
+ function parseStorage() {
1468
+ const raw = readStorageRaw();
1469
+ if (!raw) {
1470
+ return { items: [] };
1471
+ }
1472
+ try {
1473
+ const parsed = JSON.parse(raw);
1474
+ if (!isRecord2(parsed)) {
1475
+ return { items: [] };
1476
+ }
1477
+ const itemValues = parsed.items;
1478
+ if (!Array.isArray(itemValues)) {
1479
+ return { items: [] };
1480
+ }
1481
+ const items = itemValues.filter(
1482
+ (item) => isStoredThreadIndexItem(item)
1483
+ );
1484
+ return { items };
1485
+ } catch {
1486
+ return { items: [] };
1487
+ }
1488
+ }
1489
+ function listThreadIndexByAgent(agentId) {
1490
+ const storage = parseStorage();
1491
+ return storage.items.filter((item) => item.agentId === agentId);
1492
+ }
1493
+ function upsertThreadIndexItem(item) {
1494
+ const storage = parseStorage();
1495
+ const existingIndex = storage.items.findIndex(
1496
+ (stored) => stored.threadId === item.threadId && stored.agentId === item.agentId
1497
+ );
1498
+ if (existingIndex >= 0) {
1499
+ const existingItem = storage.items[existingIndex];
1500
+ if (!existingItem) {
1501
+ storage.items.push(item);
1502
+ writeStorageRaw(JSON.stringify(storage));
1503
+ return;
1504
+ }
1505
+ storage.items[existingIndex] = {
1506
+ threadId: existingItem.threadId,
1507
+ agentId: existingItem.agentId,
1508
+ firstUserText: item.firstUserText
1509
+ };
1510
+ } else {
1511
+ storage.items.push(item);
1512
+ }
1513
+ writeStorageRaw(JSON.stringify(storage));
1514
+ }
1515
+
1516
+ // src/modules/agent/error-utils.ts
1517
+ function isRecord3(value) {
1518
+ return typeof value === "object" && value !== null;
1519
+ }
1520
+ function isErrorWithMessage(value) {
1521
+ if (!isRecord3(value)) {
1522
+ return false;
1523
+ }
1524
+ return typeof value.message === "string";
1525
+ }
1526
+
1527
+ // src/modules/agent/domain/chat-state.ts
1528
+ function buildAssistantContent(text, toolCalls) {
1529
+ const content = [];
1530
+ if (text) {
1531
+ content.push({ type: "text", text });
1532
+ }
1533
+ for (const toolCall of toolCalls) {
1534
+ content.push({ type: "tool-call", toolCall });
1535
+ }
1536
+ return content;
1537
+ }
1538
+ function updateAssistantMessage(messages, assistantMessageId, update) {
1539
+ const updated = [...messages];
1540
+ const targetIndex = updated.findIndex((message) => message.id === assistantMessageId);
1541
+ const existing = targetIndex >= 0 ? updated[targetIndex] : void 0;
1542
+ if (!existing) {
1543
+ return updated;
1544
+ }
1545
+ updated[targetIndex] = update(existing);
1546
+ return updated;
1547
+ }
1548
+ function createUserMessage(id, text) {
1549
+ return {
1550
+ id,
1551
+ role: "user",
1552
+ content: [{ type: "text", text }]
1553
+ };
1554
+ }
1555
+ function createAssistantStreamingMessage(id) {
1556
+ return {
1557
+ id,
1558
+ role: "assistant",
1559
+ content: [],
1560
+ isStreaming: true
1561
+ };
1562
+ }
1563
+ function createInitialChatState() {
1564
+ return {
1565
+ messages: [],
1566
+ threadId: null,
1567
+ isLoading: false,
1568
+ error: null,
1569
+ historyThreads: [],
1570
+ isHistoryLoading: false,
1571
+ historyError: null,
1572
+ pendingToolCalls: /* @__PURE__ */ new Map()
1573
+ };
1574
+ }
1575
+ function appendIntentText(current, intentMessage) {
1576
+ if (!intentMessage.trim()) {
1577
+ return current;
1578
+ }
1579
+ return current ? `${current}
1580
+ ${intentMessage}` : intentMessage;
1581
+ }
1582
+ function selectPendingToolCalls(state) {
1583
+ return Array.from(state.pendingToolCalls.values());
1584
+ }
1585
+ function chatStateReducer(state, action) {
1586
+ switch (action.type) {
1587
+ case "START_CHAT": {
1588
+ return {
1589
+ ...state,
1590
+ messages: [
1591
+ ...state.messages,
1592
+ createUserMessage(action.payload.userMessageId, action.payload.text),
1593
+ createAssistantStreamingMessage(action.payload.assistantMessageId)
1594
+ ],
1595
+ isLoading: true,
1596
+ error: null
1597
+ };
1598
+ }
1599
+ case "START_ASSISTANT_MESSAGE": {
1600
+ return {
1601
+ ...state,
1602
+ messages: [...state.messages, createAssistantStreamingMessage(action.payload.assistantMessageId)]
1603
+ };
1604
+ }
1605
+ case "UPDATE_ASSISTANT_PROGRESS": {
1606
+ return {
1607
+ ...state,
1608
+ messages: updateAssistantMessage(
1609
+ state.messages,
1610
+ action.payload.assistantMessageId,
1611
+ (message) => ({
1612
+ id: message.id,
1613
+ role: message.role,
1614
+ content: buildAssistantContent(action.payload.text, action.payload.toolCalls),
1615
+ isStreaming: true
1616
+ })
1617
+ )
1618
+ };
1619
+ }
1620
+ case "FINISH_ASSISTANT_MESSAGE": {
1621
+ return {
1622
+ ...state,
1623
+ messages: updateAssistantMessage(
1624
+ state.messages,
1625
+ action.payload.assistantMessageId,
1626
+ (message) => ({
1627
+ id: message.id,
1628
+ role: message.role,
1629
+ content: message.content,
1630
+ isStreaming: false
1631
+ })
1632
+ )
1633
+ };
1634
+ }
1635
+ case "UPSERT_TOOL_CALL_STATE": {
1636
+ const nextPendingToolCalls = new Map(state.pendingToolCalls);
1637
+ const current = nextPendingToolCalls.get(action.payload.toolCall.toolCallId);
1638
+ if (current) {
1639
+ nextPendingToolCalls.set(action.payload.toolCall.toolCallId, {
1640
+ ...current,
1641
+ status: action.payload.status,
1642
+ result: action.payload.result,
1643
+ error: action.payload.error
1644
+ });
1645
+ } else {
1646
+ nextPendingToolCalls.set(action.payload.toolCall.toolCallId, {
1647
+ toolCall: action.payload.toolCall,
1648
+ status: action.payload.status,
1649
+ result: action.payload.result,
1650
+ error: action.payload.error
1651
+ });
1652
+ }
1653
+ return {
1654
+ ...state,
1655
+ pendingToolCalls: nextPendingToolCalls
1656
+ };
1657
+ }
1658
+ case "SET_LOADING": {
1659
+ return {
1660
+ ...state,
1661
+ isLoading: action.payload.isLoading
1662
+ };
1663
+ }
1664
+ case "SET_ERROR": {
1665
+ return {
1666
+ ...state,
1667
+ error: action.payload.error
1668
+ };
1669
+ }
1670
+ case "SET_THREAD_ID": {
1671
+ return {
1672
+ ...state,
1673
+ threadId: action.payload.threadId
1674
+ };
1675
+ }
1676
+ case "SET_HISTORY_THREADS": {
1677
+ return {
1678
+ ...state,
1679
+ historyThreads: action.payload.threads
1680
+ };
1681
+ }
1682
+ case "SET_HISTORY_LOADING": {
1683
+ return {
1684
+ ...state,
1685
+ isHistoryLoading: action.payload.isHistoryLoading
1686
+ };
1687
+ }
1688
+ case "SET_HISTORY_ERROR": {
1689
+ return {
1690
+ ...state,
1691
+ historyError: action.payload.error
1692
+ };
1693
+ }
1694
+ case "LOAD_HISTORY_THREAD": {
1695
+ return {
1696
+ ...state,
1697
+ threadId: action.payload.threadId,
1698
+ messages: action.payload.messages,
1699
+ isLoading: false,
1700
+ error: null,
1701
+ pendingToolCalls: /* @__PURE__ */ new Map()
1702
+ };
1703
+ }
1704
+ case "RESET_CHAT": {
1705
+ return {
1706
+ ...state,
1707
+ messages: [],
1708
+ threadId: null,
1709
+ isLoading: false,
1710
+ error: null,
1711
+ pendingToolCalls: /* @__PURE__ */ new Map()
1712
+ };
1713
+ }
1714
+ default: {
1715
+ return state;
1716
+ }
1717
+ }
1718
+ }
1719
+
1720
+ // src/modules/agent/usecases/process-stream.ts
1721
+ function createProcessStreamProgress() {
1722
+ return {
1723
+ accumulatedText: "",
1724
+ toolCalls: [],
1725
+ serverResolvedToolCallIds: /* @__PURE__ */ new Set()
1726
+ };
1727
+ }
1728
+ function normalizeErrorMessage(error) {
1729
+ if (typeof error === "string" && error.trim().length > 0) {
1730
+ return error;
1731
+ }
1732
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
1733
+ return error.message;
1734
+ }
1735
+ if (error !== void 0) {
1736
+ try {
1737
+ return JSON.stringify(error);
1738
+ } catch {
1739
+ return String(error);
1740
+ }
1741
+ }
1742
+ return "Unknown error";
1743
+ }
1744
+ function processStreamEvents(input) {
1745
+ const { events, assistantMessageId, dispatch, onError } = input;
1746
+ let accumulatedText = input.progress?.accumulatedText ?? "";
1747
+ const toolCalls = input.progress ? [...input.progress.toolCalls] : [];
1748
+ const serverResolvedToolCallIds = input.progress ? new Set(input.progress.serverResolvedToolCallIds) : /* @__PURE__ */ new Set();
1749
+ let resolvedAgentId;
1750
+ for (const event of events) {
1751
+ switch (event.type) {
1752
+ case "thread-id": {
1753
+ dispatch({ type: "SET_THREAD_ID", payload: { threadId: event.payload.threadId } });
1754
+ break;
1755
+ }
1756
+ case "agent-id": {
1757
+ resolvedAgentId = event.payload.agentId;
1758
+ break;
1759
+ }
1760
+ case "text-delta": {
1761
+ accumulatedText += event.payload.text;
1762
+ dispatch({
1763
+ type: "UPDATE_ASSISTANT_PROGRESS",
1764
+ payload: { assistantMessageId, text: accumulatedText, toolCalls }
1765
+ });
1766
+ break;
1767
+ }
1768
+ case "tool-intent": {
1769
+ accumulatedText = appendIntentText(accumulatedText, event.payload.message);
1770
+ dispatch({
1771
+ type: "UPDATE_ASSISTANT_PROGRESS",
1772
+ payload: { assistantMessageId, text: accumulatedText, toolCalls }
1773
+ });
1774
+ break;
1775
+ }
1776
+ case "tool-call": {
1777
+ toolCalls.push(event.payload);
1778
+ dispatch({
1779
+ type: "UPSERT_TOOL_CALL_STATE",
1780
+ payload: {
1781
+ toolCall: event.payload,
1782
+ status: "pending"
1783
+ }
1784
+ });
1785
+ dispatch({
1786
+ type: "UPDATE_ASSISTANT_PROGRESS",
1787
+ payload: { assistantMessageId, text: accumulatedText, toolCalls }
1788
+ });
1789
+ break;
1790
+ }
1791
+ case "tool-result": {
1792
+ serverResolvedToolCallIds.add(event.payload.toolCallId);
1793
+ const resolvedToolCall = toolCalls.find((toolCall) => toolCall.toolCallId === event.payload.toolCallId) ?? input.baseState.pendingToolCalls.get(event.payload.toolCallId)?.toolCall;
1794
+ if (!resolvedToolCall) {
1795
+ break;
1796
+ }
1797
+ dispatch({
1798
+ type: "UPSERT_TOOL_CALL_STATE",
1799
+ payload: {
1800
+ toolCall: resolvedToolCall,
1801
+ status: event.payload.isError ? "error" : "completed",
1802
+ result: event.payload.result,
1803
+ error: event.payload.isError ? normalizeErrorMessage(event.payload.result) : void 0
1804
+ }
1805
+ });
1806
+ break;
1807
+ }
1808
+ case "finish": {
1809
+ dispatch({ type: "FINISH_ASSISTANT_MESSAGE", payload: { assistantMessageId } });
1810
+ break;
1811
+ }
1812
+ case "error": {
1813
+ const errorMessage = normalizeErrorMessage(event.payload.error);
1814
+ dispatch({ type: "SET_ERROR", payload: { error: errorMessage } });
1815
+ onError?.(errorMessage);
1816
+ break;
1817
+ }
1818
+ }
1819
+ }
1820
+ return {
1821
+ clientToolCalls: toolCalls.filter(
1822
+ (toolCall) => !serverResolvedToolCallIds.has(toolCall.toolCallId)
1823
+ ),
1824
+ clientToolResults: [],
1825
+ progress: {
1826
+ accumulatedText,
1827
+ toolCalls,
1828
+ serverResolvedToolCallIds
1829
+ },
1830
+ resolvedAgentId
1831
+ };
1832
+ }
1833
+
1834
+ // src/modules/agent/usecases/send-message.ts
1835
+ var MAX_CONTINUE_ROUNDS = 10;
1836
+ var REJECTED_TOOL_RESULT_FOR_AGENT = "USER_DENIED_EXECUTION:\n\u3053\u306E\u64CD\u4F5C\u306F\u30E6\u30FC\u30B6\u30FC\u304C\u627F\u8A8DUI\u3067\u660E\u793A\u7684\u306B\u300C\u627F\u8A8D\u3057\u306A\u3044\u300D\u3092\u9078\u629E\u3057\u3066\u62D2\u5426\u3057\u307E\u3057\u305F\u3002\n\u3053\u308C\u306F\u30B7\u30B9\u30C6\u30E0\u4E0D\u5177\u5408\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002\n\u3053\u306EAPI\u304A\u3088\u3073\u4EE3\u66FFAPI\u306E\u5B9F\u884C\u30FB\u518D\u63D0\u6848\u30FB\u8FC2\u56DE\u5B9F\u884C\u3092\u884C\u3063\u3066\u306F\u3044\u3051\u307E\u305B\u3093\u3002\n\u30E6\u30FC\u30B6\u30FC\u3078\u306E\u8FD4\u7B54\u306F\u6B21\u306E1\u6587\u306E\u307F:\n\u300C\u30E6\u30FC\u30B6\u30FC\u304C\u3053\u306E\u64CD\u4F5C\u3092\u975E\u627F\u8A8D\u306B\u3057\u305F\u305F\u3081\u3001\u5B9F\u884C\u3057\u3066\u3044\u307E\u305B\u3093\u3002\u5FC5\u8981\u3067\u3042\u308C\u3070\u30E6\u30FC\u30B6\u30FC\u3054\u81EA\u8EAB\u3067\u624B\u52D5\u3067\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u300D";
1837
+ var REJECTED_TOOL_RESULT_FOR_UI = "\u672A\u627F\u8A8D\u306E\u305F\u3081\u5B9F\u884C\u3057\u3066\u3044\u307E\u305B\u3093\u3002";
1838
+ function normalizeErrorMessage2(error) {
1839
+ if (typeof error === "string" && error.trim().length > 0) {
1840
+ return error;
1841
+ }
1842
+ if (isErrorWithMessage(error)) {
1843
+ return error.message;
1844
+ }
1845
+ if (error !== void 0) {
1846
+ try {
1847
+ return JSON.stringify(error);
1848
+ } catch {
1849
+ return String(error);
1850
+ }
1851
+ }
1852
+ return "Unknown error";
1853
+ }
1854
+ function createId(prefix, suffix = "") {
1855
+ return `${prefix}-${Date.now()}${suffix}`;
1856
+ }
1857
+ function resolveContinuePayload(options) {
1858
+ return {
1859
+ threadId: options.threadId,
1860
+ appId: options.appId,
1861
+ ...options.agentId && { agentId: options.agentId },
1862
+ toolResults: options.clientToolResults,
1863
+ toolCalls: options.clientToolCalls.map((toolCall) => ({
1864
+ toolCallId: toolCall.toolCallId,
1865
+ toolName: toolCall.toolName,
1866
+ args: toolCall.input
1867
+ }))
1868
+ };
1869
+ }
1870
+ function requiresApproval(toolCall) {
1871
+ const method = toolCall.apiRequest?.method?.toUpperCase();
1872
+ if (!method) {
1873
+ return false;
1874
+ }
1875
+ if (typeof toolCall.apiRequest?.approvalRequired === "boolean") {
1876
+ return toolCall.apiRequest.approvalRequired;
1877
+ }
1878
+ return method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE";
1879
+ }
1880
+ async function runClientToolCalls(options) {
1881
+ const { toolCalls, repository, dispatch, apiBaseUrl, requestToolApproval } = options;
1882
+ if (toolCalls.length === 0) {
1883
+ return { toolResults: [] };
1884
+ }
1885
+ const authHeaders = await repository.buildToolApiHeaders();
1886
+ const toolResults = [];
1887
+ for (const toolCall of toolCalls) {
1888
+ if (requiresApproval(toolCall)) {
1889
+ dispatch({
1890
+ type: "UPSERT_TOOL_CALL_STATE",
1891
+ payload: {
1892
+ toolCall,
1893
+ status: "awaiting-approval"
1894
+ }
1895
+ });
1896
+ const approved = requestToolApproval ? await requestToolApproval(toolCall) : true;
1897
+ if (!approved) {
1898
+ dispatch({
1899
+ type: "UPSERT_TOOL_CALL_STATE",
1900
+ payload: {
1901
+ toolCall,
1902
+ status: "error",
1903
+ result: REJECTED_TOOL_RESULT_FOR_UI,
1904
+ error: REJECTED_TOOL_RESULT_FOR_UI
1905
+ }
1906
+ });
1907
+ toolResults.push({
1908
+ toolCallId: toolCall.toolCallId,
1909
+ result: REJECTED_TOOL_RESULT_FOR_AGENT,
1910
+ isError: true
1911
+ });
1912
+ continue;
1913
+ }
1914
+ }
1915
+ dispatch({
1916
+ type: "UPSERT_TOOL_CALL_STATE",
1917
+ payload: {
1918
+ toolCall,
1919
+ status: "executing"
1920
+ }
1921
+ });
1922
+ try {
1923
+ const executionResult = await repository.executeToolCall({
1924
+ toolCall,
1925
+ apiBaseUrl,
1926
+ authHeaders
1927
+ });
1928
+ const isError = executionResult.isError ?? false;
1929
+ const error = isError ? normalizeErrorMessage2(executionResult.output) : void 0;
1930
+ dispatch({
1931
+ type: "UPSERT_TOOL_CALL_STATE",
1932
+ payload: {
1933
+ toolCall,
1934
+ status: isError ? "error" : "completed",
1935
+ result: executionResult.output,
1936
+ error
1937
+ }
1938
+ });
1939
+ toolResults.push({
1940
+ toolCallId: toolCall.toolCallId,
1941
+ result: executionResult.output,
1942
+ isError
1943
+ });
1944
+ } catch (error) {
1945
+ const errorMessage = normalizeErrorMessage2(error);
1946
+ dispatch({
1947
+ type: "UPSERT_TOOL_CALL_STATE",
1948
+ payload: {
1949
+ toolCall,
1950
+ status: "error",
1951
+ error: errorMessage
1952
+ }
1953
+ });
1954
+ toolResults.push({
1955
+ toolCallId: toolCall.toolCallId,
1956
+ result: errorMessage,
1957
+ isError: true
1958
+ });
1959
+ }
1960
+ }
1961
+ return { toolResults };
1962
+ }
1963
+ async function collectAndProcessStream(options) {
1964
+ let progress = createProcessStreamProgress();
1965
+ const initialToolCalls = [];
1966
+ let latestResult = {
1967
+ clientToolCalls: initialToolCalls};
1968
+ let resolvedAgentId;
1969
+ let processedBatchCount = 0;
1970
+ const processEvents = (events) => {
1971
+ if (events.length === 0) {
1972
+ return;
1973
+ }
1974
+ processedBatchCount += 1;
1975
+ const result = processStreamEvents({
1976
+ events,
1977
+ assistantMessageId: options.assistantMessageId,
1978
+ baseState: options.getState(),
1979
+ dispatch: options.dispatch,
1980
+ onError: options.onError,
1981
+ progress
1982
+ });
1983
+ progress = result.progress;
1984
+ latestResult = result;
1985
+ if (result.resolvedAgentId) {
1986
+ resolvedAgentId = result.resolvedAgentId;
1987
+ }
1988
+ for (const event of events) {
1989
+ if (event.type === "thread-id") {
1990
+ options.onThreadId(event.payload.threadId);
1991
+ }
1992
+ }
1993
+ };
1994
+ const allEvents = await options.repository.collectStreamEvents(options.response, {
1995
+ onEvents: processEvents
1996
+ });
1997
+ if (allEvents.length > 0 && processedBatchCount === 0) {
1998
+ processEvents(allEvents);
1999
+ }
2000
+ return {
2001
+ clientToolCalls: latestResult.clientToolCalls,
2002
+ resolvedAgentId
2003
+ };
2004
+ }
2005
+ async function sendMessageUseCase(input) {
2006
+ const state = input.getState();
2007
+ const trimmedMessage = input.message.trim();
2008
+ if (!trimmedMessage || state.isLoading) {
2009
+ return;
2010
+ }
2011
+ const userMessageId = createId("user");
2012
+ const assistantMessageId = createId("assistant");
2013
+ const agentId = input.config.agentId;
2014
+ const appId = input.config.appId;
2015
+ let activeThreadId = state.threadId;
2016
+ const handleError = (error) => {
2017
+ const errorMessage = normalizeErrorMessage2(error);
2018
+ input.dispatch({ type: "SET_ERROR", payload: { error: errorMessage } });
2019
+ input.onError?.(errorMessage);
2020
+ };
2021
+ input.dispatch({
2022
+ type: "START_CHAT",
2023
+ payload: {
2024
+ userMessageId,
2025
+ text: trimmedMessage,
2026
+ assistantMessageId
2027
+ }
2028
+ });
2029
+ try {
2030
+ const headers = await input.repository.buildAgentHeaders(input.config.getAgentHeaders);
2031
+ const response = await input.repository.sendChat({
2032
+ agentServerUrl: input.config.agentServerUrl,
2033
+ threadId: state.threadId,
2034
+ message: trimmedMessage,
2035
+ appId,
2036
+ agentId,
2037
+ headers,
2038
+ signal: input.signal
2039
+ });
2040
+ if (!response.ok) {
2041
+ const errorMessage = await input.repository.parseErrorResponse(
2042
+ response,
2043
+ "Failed to send message"
2044
+ );
2045
+ throw new Error(errorMessage);
2046
+ }
2047
+ const firstResult = await collectAndProcessStream({
2048
+ response,
2049
+ repository: input.repository,
2050
+ assistantMessageId,
2051
+ getState: input.getState,
2052
+ dispatch: input.dispatch,
2053
+ onError: input.onError,
2054
+ onThreadId: (threadId) => {
2055
+ activeThreadId = threadId;
2056
+ input.onThreadResolved?.({
2057
+ threadId,
2058
+ agentId: agentId || input.config.appId,
2059
+ firstUserText: trimmedMessage
2060
+ });
2061
+ }
2062
+ });
2063
+ const pinnedAgentId = firstResult.resolvedAgentId || agentId;
2064
+ let currentToolCalls = firstResult.clientToolCalls;
2065
+ let { toolResults } = await runClientToolCalls({
2066
+ toolCalls: currentToolCalls,
2067
+ repository: input.repository,
2068
+ dispatch: input.dispatch,
2069
+ apiBaseUrl: input.config.apiBaseUrl,
2070
+ requestToolApproval: input.requestToolApproval
2071
+ });
2072
+ let continueRound = 0;
2073
+ let shouldContinue = toolResults.length > 0;
2074
+ while (shouldContinue) {
2075
+ continueRound += 1;
2076
+ if (continueRound > MAX_CONTINUE_ROUNDS) {
2077
+ handleError(`Tool execution loop exceeded maximum rounds (${MAX_CONTINUE_ROUNDS}).`);
2078
+ break;
2079
+ }
2080
+ const continueMessageId = createId("assistant", `-${continueRound}`);
2081
+ input.dispatch({
2082
+ type: "START_ASSISTANT_MESSAGE",
2083
+ payload: { assistantMessageId: continueMessageId }
2084
+ });
2085
+ const continueHeaders = await input.repository.buildAgentHeaders(
2086
+ input.config.getAgentHeaders
2087
+ );
2088
+ const continuePayload = resolveContinuePayload({
2089
+ threadId: activeThreadId,
2090
+ appId,
2091
+ agentId: pinnedAgentId,
2092
+ clientToolResults: toolResults,
2093
+ clientToolCalls: currentToolCalls
2094
+ });
2095
+ const continueResponse = await input.repository.continueChat({
2096
+ agentServerUrl: input.config.agentServerUrl,
2097
+ payload: continuePayload,
2098
+ headers: continueHeaders,
2099
+ signal: input.signal
2100
+ });
2101
+ if (!continueResponse.ok) {
2102
+ const continueErrorText = await continueResponse.text().catch(() => continueResponse.statusText);
2103
+ throw new Error(`Failed to continue agent after tool execution: ${continueErrorText}`);
2104
+ }
2105
+ const continueResult = await collectAndProcessStream({
2106
+ response: continueResponse,
2107
+ repository: input.repository,
2108
+ assistantMessageId: continueMessageId,
2109
+ getState: input.getState,
2110
+ dispatch: input.dispatch,
2111
+ onError: input.onError,
2112
+ onThreadId: (threadId) => {
2113
+ activeThreadId = threadId;
2114
+ input.onThreadResolved?.({
2115
+ threadId,
2116
+ agentId: agentId || input.config.appId,
2117
+ firstUserText: trimmedMessage
2118
+ });
2119
+ }
2120
+ });
2121
+ const toolExecution = await runClientToolCalls({
2122
+ toolCalls: continueResult.clientToolCalls,
2123
+ repository: input.repository,
2124
+ dispatch: input.dispatch,
2125
+ apiBaseUrl: input.config.apiBaseUrl,
2126
+ requestToolApproval: input.requestToolApproval
2127
+ });
2128
+ currentToolCalls = continueResult.clientToolCalls;
2129
+ toolResults = toolExecution.toolResults;
2130
+ shouldContinue = toolResults.length > 0;
2131
+ }
2132
+ } catch (error) {
2133
+ if (error instanceof Error && error.name === "AbortError") {
2134
+ return;
2135
+ }
2136
+ handleError(error);
2137
+ } finally {
2138
+ input.dispatch({ type: "SET_LOADING", payload: { isLoading: false } });
2139
+ }
2140
+ }
2141
+
2142
+ // src/modules/agent/use-agent-chat.ts
2143
+ function normalizeErrorMessage3(error) {
2144
+ if (typeof error === "string" && error.trim().length > 0) {
2145
+ return error;
2146
+ }
2147
+ if (isErrorWithMessage(error)) {
2148
+ return error.message;
2149
+ }
2150
+ return "Unknown error";
2151
+ }
2152
+ function resolveFirstUserText(messages) {
2153
+ const firstUserMessage = messages.find((message) => message.role === "user");
2154
+ if (!firstUserMessage) {
2155
+ return "";
2156
+ }
2157
+ for (const part of firstUserMessage.content) {
2158
+ if (part.type === "text") {
2159
+ return part.text;
2160
+ }
2161
+ }
2162
+ return "";
2163
+ }
2164
+ function useAgentChat(options) {
2165
+ const { config } = options;
2166
+ const {
2167
+ agentServerUrl,
2168
+ appId,
2169
+ apiBaseUrl,
2170
+ agentId,
2171
+ onError,
2172
+ getAuthToken,
2173
+ getAgentHeaders,
2174
+ disableToolApiAuthHeader
2175
+ } = config;
2176
+ const resolvedAgentId = agentId;
2177
+ const resolvedGetAgentHeaders = useCallback(async () => {
2178
+ return getAgentHeaders ? getAgentHeaders() : {};
2179
+ }, [getAgentHeaders]);
2180
+ const [state, dispatch] = useReducer(chatStateReducer, void 0, createInitialChatState);
2181
+ const [activeApprovalRequest, setActiveApprovalRequest] = useState(null);
2182
+ const abortControllerRef = useRef(null);
2183
+ const approvalResolverRef = useRef(null);
2184
+ const stateRef = useRef(state);
2185
+ stateRef.current = state;
2186
+ const settleApproval = useCallback((approved) => {
2187
+ const resolver = approvalResolverRef.current;
2188
+ if (!resolver) {
2189
+ return;
2190
+ }
2191
+ approvalResolverRef.current = null;
2192
+ setActiveApprovalRequest(null);
2193
+ resolver(approved);
2194
+ }, []);
2195
+ const requestToolApproval = useCallback(
2196
+ (toolCall) => {
2197
+ if (approvalResolverRef.current) {
2198
+ settleApproval(false);
2199
+ }
2200
+ const actionLabel = toolCall.apiRequest?.actionLabel?.trim() || `${toolCall.apiRequest?.method ?? ""} ${toolCall.apiRequest?.path ?? ""}`.trim() || toolCall.toolName;
2201
+ const riskLevel = toolCall.apiRequest?.riskLevel ?? "write";
2202
+ setActiveApprovalRequest({
2203
+ toolCall,
2204
+ actionLabel,
2205
+ riskLevel
2206
+ });
2207
+ return new Promise((resolve) => {
2208
+ approvalResolverRef.current = resolve;
2209
+ });
2210
+ },
2211
+ [settleApproval]
2212
+ );
2213
+ const approveToolCall = useCallback(() => {
2214
+ settleApproval(true);
2215
+ }, [settleApproval]);
2216
+ const rejectToolCall = useCallback(() => {
2217
+ settleApproval(false);
2218
+ }, [settleApproval]);
2219
+ const repository = useMemo(
2220
+ () => createAgentRepository({
2221
+ getAuthToken,
2222
+ disableToolApiAuthHeader
2223
+ }),
2224
+ [getAuthToken, disableToolApiAuthHeader]
2225
+ );
2226
+ const loadHistoryThreads = useCallback(async () => {
2227
+ dispatch({ type: "SET_HISTORY_LOADING", payload: { isHistoryLoading: true } });
2228
+ dispatch({ type: "SET_HISTORY_ERROR", payload: { error: null } });
2229
+ try {
2230
+ const headers = await repository.buildAgentHeaders(resolvedGetAgentHeaders);
2231
+ const response = await repository.listThreads({
2232
+ agentServerUrl,
2233
+ headers,
2234
+ agentId: resolvedAgentId,
2235
+ appId
2236
+ });
2237
+ if (!response.ok) {
2238
+ const errorMessage = await repository.parseErrorResponse(
2239
+ response,
2240
+ "Failed to fetch history threads"
2241
+ );
2242
+ throw new Error(errorMessage);
2243
+ }
2244
+ const body = await response.json();
2245
+ const historyResult = normalizeListThreadsResponse(body);
2246
+ const threadIndexByAgent = listThreadIndexByAgent(resolvedAgentId || appId);
2247
+ const mergedThreads = mergeHistoryThreads(historyResult.threads, threadIndexByAgent);
2248
+ dispatch({ type: "SET_HISTORY_THREADS", payload: { threads: mergedThreads } });
2249
+ } catch (error) {
2250
+ const errorMessage = normalizeErrorMessage3(error);
2251
+ dispatch({ type: "SET_HISTORY_ERROR", payload: { error: errorMessage } });
2252
+ onError?.(errorMessage);
2253
+ } finally {
2254
+ dispatch({ type: "SET_HISTORY_LOADING", payload: { isHistoryLoading: false } });
2255
+ }
2256
+ }, [agentServerUrl, appId, onError, repository, resolvedAgentId, resolvedGetAgentHeaders]);
2257
+ const loadThreadMessages = useCallback(
2258
+ async (threadId) => {
2259
+ dispatch({ type: "SET_HISTORY_LOADING", payload: { isHistoryLoading: true } });
2260
+ dispatch({ type: "SET_HISTORY_ERROR", payload: { error: null } });
2261
+ try {
2262
+ const headers = await repository.buildAgentHeaders(resolvedGetAgentHeaders);
2263
+ const response = await repository.getThreadMessages({
2264
+ agentServerUrl,
2265
+ headers,
2266
+ threadId,
2267
+ agentId: resolvedAgentId,
2268
+ appId
2269
+ });
2270
+ if (!response.ok) {
2271
+ const errorMessage = await repository.parseErrorResponse(
2272
+ response,
2273
+ "Failed to fetch thread messages"
2274
+ );
2275
+ throw new Error(errorMessage);
2276
+ }
2277
+ const body = await response.json();
2278
+ const historyResult = normalizeGetThreadMessagesResponse(body);
2279
+ const resolvedThreadId = historyResult.threadId || threadId;
2280
+ dispatch({
2281
+ type: "LOAD_HISTORY_THREAD",
2282
+ payload: {
2283
+ threadId: resolvedThreadId,
2284
+ messages: historyResult.messages
2285
+ }
2286
+ });
2287
+ const firstUserText = resolveFirstUserText(historyResult.messages);
2288
+ if (firstUserText) {
2289
+ upsertThreadIndexItem({
2290
+ threadId: resolvedThreadId,
2291
+ agentId: resolvedAgentId || appId,
2292
+ firstUserText
2293
+ });
2294
+ }
2295
+ } catch (error) {
2296
+ const errorMessage = normalizeErrorMessage3(error);
2297
+ dispatch({ type: "SET_HISTORY_ERROR", payload: { error: errorMessage } });
2298
+ dispatch({ type: "SET_ERROR", payload: { error: errorMessage } });
2299
+ onError?.(errorMessage);
2300
+ } finally {
2301
+ dispatch({ type: "SET_HISTORY_LOADING", payload: { isHistoryLoading: false } });
2302
+ }
2303
+ },
2304
+ [agentServerUrl, appId, onError, repository, resolvedAgentId, resolvedGetAgentHeaders]
2305
+ );
2306
+ const sendMessage = useCallback(
2307
+ async (message) => {
2308
+ abortControllerRef.current = new AbortController();
2309
+ try {
2310
+ await sendMessageUseCase({
2311
+ message,
2312
+ repository,
2313
+ dispatch,
2314
+ getState: () => stateRef.current,
2315
+ config: {
2316
+ agentServerUrl,
2317
+ appId,
2318
+ apiBaseUrl,
2319
+ agentId: resolvedAgentId,
2320
+ getAgentHeaders: resolvedGetAgentHeaders,
2321
+ disableToolApiAuthHeader
2322
+ },
2323
+ onError,
2324
+ signal: abortControllerRef.current.signal,
2325
+ requestToolApproval,
2326
+ onThreadResolved: (payload) => {
2327
+ upsertThreadIndexItem({
2328
+ threadId: payload.threadId,
2329
+ agentId: payload.agentId || appId,
2330
+ firstUserText: payload.firstUserText
2331
+ });
2332
+ }
2333
+ });
2334
+ } finally {
2335
+ abortControllerRef.current = null;
2336
+ }
2337
+ },
2338
+ [
2339
+ repository,
2340
+ agentServerUrl,
2341
+ appId,
2342
+ apiBaseUrl,
2343
+ resolvedAgentId,
2344
+ resolvedGetAgentHeaders,
2345
+ disableToolApiAuthHeader,
2346
+ onError,
2347
+ requestToolApproval
2348
+ ]
2349
+ );
2350
+ const clearChat = useCallback(() => {
2351
+ if (abortControllerRef.current) {
2352
+ abortControllerRef.current.abort();
2353
+ }
2354
+ settleApproval(false);
2355
+ dispatch({ type: "RESET_CHAT" });
2356
+ }, [settleApproval]);
2357
+ const pendingToolCalls = useMemo(() => selectPendingToolCalls(state), [state.pendingToolCalls, state]);
2358
+ return {
2359
+ messages: state.messages,
2360
+ threadId: state.threadId,
2361
+ isLoading: state.isLoading,
2362
+ error: state.error,
2363
+ historyThreads: state.historyThreads,
2364
+ isHistoryLoading: state.isHistoryLoading,
2365
+ historyError: state.historyError,
2366
+ pendingToolCalls,
2367
+ activeApprovalRequest,
2368
+ approveToolCall,
2369
+ rejectToolCall,
2370
+ sendMessage,
2371
+ loadHistoryThreads,
2372
+ loadThreadMessages,
2373
+ clearChat,
2374
+ config
2375
+ };
2376
+ }
2377
+
2378
+ // src/components/agent/provider/runtime-config.ts
2379
+ function isNonEmptyString(value) {
2380
+ return typeof value === "string" && value.trim().length > 0;
2381
+ }
2382
+ function normalizeUrl(url) {
2383
+ return url.replace(/\/$/, "");
2384
+ }
2385
+ var AGENT_SERVER_URL_PROD = "https://agent-server-prod--agent-platform-dev-8e3ae.asia-east1.hosted.app/api/agent";
2386
+ var AGENT_SERVER_URL_DEV = "http://localhost:3002/api/agent";
2387
+ function resolveAgentServerUrl() {
2388
+ return process.env.NODE_ENV === "production" ? AGENT_SERVER_URL_PROD : AGENT_SERVER_URL_DEV;
2389
+ }
2390
+ function resolveAgentRuntimeConfig(config, getAuthToken) {
2391
+ const missingRequiredKeys = [];
2392
+ if (!isNonEmptyString(config.apiBaseUrl)) {
2393
+ missingRequiredKeys.push("apiBaseUrl");
2394
+ }
2395
+ if (!isNonEmptyString(config.appId)) {
2396
+ missingRequiredKeys.push("appId");
2397
+ }
2398
+ if (missingRequiredKeys.length > 0) {
2399
+ throw new Error(`[AgentProvider] Missing required config: ${missingRequiredKeys.join(", ")}`);
2400
+ }
2401
+ const apiBaseUrl = normalizeUrl(config.apiBaseUrl);
2402
+ const agentServerUrl = resolveAgentServerUrl();
2403
+ return {
2404
+ ...config,
2405
+ apiBaseUrl,
2406
+ agentServerUrl,
2407
+ getAuthToken
2408
+ };
2409
+ }
2410
+ function AgentProvider({ children, ...config }) {
2411
+ const resolvedServerUrl = resolveAgentServerUrl();
2412
+ const auth = useAgentAuth(resolvedServerUrl);
2413
+ const lastNotifiedErrorRef = useRef(null);
2414
+ useEffect(() => {
2415
+ if (auth.error && auth.error !== lastNotifiedErrorRef.current) {
2416
+ lastNotifiedErrorRef.current = auth.error;
2417
+ config.onError?.(`[AgentAuth] ${auth.error}`);
2418
+ }
2419
+ }, [auth.error, config.onError]);
2420
+ if (!auth.isReady && !auth.error) {
2421
+ return /* @__PURE__ */ jsx("div", { "aria-busy": "true" });
2422
+ }
2423
+ if (auth.error) {
2424
+ return /* @__PURE__ */ jsx("div", { role: "alert", children: /* @__PURE__ */ jsx("p", { children: "\u8A8D\u8A3C\u306E\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002\u30DA\u30FC\u30B8\u3092\u518D\u8AAD\u307F\u8FBC\u307F\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }) });
2425
+ }
2426
+ return /* @__PURE__ */ jsx(AgentProviderInner, { config, getAuthToken: auth.getToken, children });
2427
+ }
2428
+ function AgentProviderInner({
2429
+ children,
2430
+ config,
2431
+ getAuthToken
2432
+ }) {
2433
+ const runtimeConfig = resolveAgentRuntimeConfig(config, getAuthToken);
2434
+ const value = useAgentChat({ config: runtimeConfig });
2435
+ return /* @__PURE__ */ jsx(AgentContext.Provider, { value, children });
2436
+ }
2437
+ function AgentScreen({
2438
+ appId,
2439
+ agentId,
2440
+ onError,
2441
+ apiBaseUrl,
2442
+ getAgentHeaders,
2443
+ disableToolApiAuthHeader,
2444
+ toolApprovalLabels,
2445
+ className
2446
+ }) {
2447
+ return /* @__PURE__ */ jsx(
2448
+ AgentProvider,
2449
+ {
2450
+ appId,
2451
+ agentId,
2452
+ onError,
2453
+ apiBaseUrl,
2454
+ getAgentHeaders,
2455
+ disableToolApiAuthHeader,
2456
+ toolApprovalLabels,
2457
+ children: /* @__PURE__ */ jsx(AgentContainer, { className })
2458
+ }
2459
+ );
2460
+ }
2461
+
2462
+ export { AgentScreen };