@agentuity/workbench 0.0.105 → 0.0.107

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 (171) hide show
  1. package/dist/components/App.d.ts.map +1 -1
  2. package/dist/components/App.js +15 -13
  3. package/dist/components/App.js.map +1 -1
  4. package/dist/components/ai-elements/actions.d.ts +1 -1
  5. package/dist/components/ai-elements/actions.d.ts.map +1 -1
  6. package/dist/components/ai-elements/actions.js +1 -1
  7. package/dist/components/ai-elements/actions.js.map +1 -1
  8. package/dist/components/ai-elements/code-block.d.ts +1 -1
  9. package/dist/components/ai-elements/code-block.d.ts.map +1 -1
  10. package/dist/components/ai-elements/code-block.js +22 -20
  11. package/dist/components/ai-elements/code-block.js.map +1 -1
  12. package/dist/components/ai-elements/conversation.d.ts +2 -2
  13. package/dist/components/ai-elements/conversation.d.ts.map +1 -1
  14. package/dist/components/ai-elements/conversation.js +5 -3
  15. package/dist/components/ai-elements/conversation.js.map +1 -1
  16. package/dist/components/ai-elements/message.d.ts +1 -1
  17. package/dist/components/ai-elements/message.d.ts.map +1 -1
  18. package/dist/components/ai-elements/message.js +4 -9
  19. package/dist/components/ai-elements/message.js.map +1 -1
  20. package/dist/components/ai-elements/prompt-input.d.ts.map +1 -1
  21. package/dist/components/ai-elements/prompt-input.js +1 -1
  22. package/dist/components/ai-elements/prompt-input.js.map +1 -1
  23. package/dist/components/ai-elements/shimmer.d.ts.map +1 -1
  24. package/dist/components/ai-elements/shimmer.js +1 -1
  25. package/dist/components/ai-elements/shimmer.js.map +1 -1
  26. package/dist/components/internal/chat.d.ts +10 -0
  27. package/dist/components/internal/chat.d.ts.map +1 -0
  28. package/dist/components/internal/chat.js +104 -0
  29. package/dist/components/internal/chat.js.map +1 -0
  30. package/dist/components/internal/{Header.d.ts → header.d.ts} +4 -6
  31. package/dist/components/internal/header.d.ts.map +1 -0
  32. package/dist/components/internal/header.js +25 -0
  33. package/dist/components/internal/header.js.map +1 -0
  34. package/dist/components/internal/{InputSection.d.ts → input-section.d.ts} +9 -9
  35. package/dist/components/internal/input-section.d.ts.map +1 -0
  36. package/dist/components/internal/input-section.js +162 -0
  37. package/dist/components/internal/input-section.js.map +1 -0
  38. package/dist/components/internal/json-editor.d.ts +14 -0
  39. package/dist/components/internal/json-editor.d.ts.map +1 -0
  40. package/dist/components/internal/{MonacoJsonEditor.js → json-editor.js} +40 -37
  41. package/dist/components/internal/json-editor.js.map +1 -0
  42. package/dist/components/internal/logo.d.ts +2 -3
  43. package/dist/components/internal/logo.d.ts.map +1 -1
  44. package/dist/components/internal/logo.js +2 -2
  45. package/dist/components/internal/logo.js.map +1 -1
  46. package/dist/components/internal/resizable-provider.d.ts.map +1 -0
  47. package/dist/components/internal/resizable-provider.js.map +1 -0
  48. package/dist/components/internal/{Schema.d.ts → schema.d.ts} +2 -2
  49. package/dist/components/internal/schema.d.ts.map +1 -0
  50. package/dist/components/internal/schema.js +13 -0
  51. package/dist/components/internal/schema.js.map +1 -0
  52. package/dist/components/internal/{WorkbenchProvider.d.ts → workbench-provider.d.ts} +8 -4
  53. package/dist/components/internal/workbench-provider.d.ts.map +1 -0
  54. package/dist/components/internal/{WorkbenchProvider.js → workbench-provider.js} +87 -60
  55. package/dist/components/internal/workbench-provider.js.map +1 -0
  56. package/dist/components/ui/avatar.d.ts +1 -1
  57. package/dist/components/ui/avatar.d.ts.map +1 -1
  58. package/dist/components/ui/avatar.js.map +1 -1
  59. package/dist/components/ui/button.d.ts +1 -1
  60. package/dist/components/ui/command.d.ts +1 -1
  61. package/dist/components/ui/command.d.ts.map +1 -1
  62. package/dist/components/ui/command.js.map +1 -1
  63. package/dist/components/ui/dialog.d.ts +1 -1
  64. package/dist/components/ui/dialog.d.ts.map +1 -1
  65. package/dist/components/ui/dialog.js.map +1 -1
  66. package/dist/components/ui/dropdown-menu.d.ts +1 -1
  67. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  68. package/dist/components/ui/dropdown-menu.js.map +1 -1
  69. package/dist/components/ui/hover-card.d.ts +1 -1
  70. package/dist/components/ui/hover-card.d.ts.map +1 -1
  71. package/dist/components/ui/hover-card.js.map +1 -1
  72. package/dist/components/ui/input-group.d.ts +2 -2
  73. package/dist/components/ui/input-group.d.ts.map +1 -1
  74. package/dist/components/ui/input-group.js.map +1 -1
  75. package/dist/components/ui/input.d.ts +1 -1
  76. package/dist/components/ui/input.d.ts.map +1 -1
  77. package/dist/components/ui/scroll-area.d.ts +1 -1
  78. package/dist/components/ui/scroll-area.d.ts.map +1 -1
  79. package/dist/components/ui/scroll-area.js.map +1 -1
  80. package/dist/components/ui/textarea.d.ts +1 -1
  81. package/dist/components/ui/textarea.d.ts.map +1 -1
  82. package/dist/components/ui/theme-provider.d.ts.map +1 -1
  83. package/dist/components/ui/theme-provider.js +1 -1
  84. package/dist/components/ui/theme-provider.js.map +1 -1
  85. package/dist/components/ui/tooltip.d.ts +1 -1
  86. package/dist/components/ui/tooltip.d.ts.map +1 -1
  87. package/dist/components/ui/tooltip.js.map +1 -1
  88. package/dist/hooks/useAgentSchemas.d.ts +10 -10
  89. package/dist/hooks/useAgentSchemas.d.ts.map +1 -1
  90. package/dist/hooks/useAgentSchemas.js +9 -7
  91. package/dist/hooks/useAgentSchemas.js.map +1 -1
  92. package/dist/hooks/useLogger.d.ts.map +1 -1
  93. package/dist/hooks/useLogger.js +2 -1
  94. package/dist/hooks/useLogger.js.map +1 -1
  95. package/dist/hooks/useWorkbenchWebsocket.d.ts +2 -2
  96. package/dist/hooks/useWorkbenchWebsocket.d.ts.map +1 -1
  97. package/dist/hooks/useWorkbenchWebsocket.js +24 -20
  98. package/dist/hooks/useWorkbenchWebsocket.js.map +1 -1
  99. package/dist/index.d.ts +5 -5
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +4 -6
  102. package/dist/index.js.map +1 -1
  103. package/dist/lib/utils.d.ts +3 -0
  104. package/dist/lib/utils.d.ts.map +1 -1
  105. package/dist/lib/utils.js +59 -0
  106. package/dist/lib/utils.js.map +1 -1
  107. package/dist/server.d.ts +1 -1
  108. package/dist/server.d.ts.map +1 -1
  109. package/dist/server.js.map +1 -1
  110. package/dist/standalone.css +360 -295
  111. package/dist/types/config.d.ts +27 -18
  112. package/dist/types/config.d.ts.map +1 -1
  113. package/package.json +5 -5
  114. package/src/base.css +186 -158
  115. package/src/components/App.tsx +31 -16
  116. package/src/components/ai-elements/actions.tsx +2 -2
  117. package/src/components/ai-elements/code-block.tsx +46 -32
  118. package/src/components/ai-elements/conversation.tsx +18 -17
  119. package/src/components/ai-elements/message.tsx +4 -9
  120. package/src/components/ai-elements/prompt-input.tsx +1 -1
  121. package/src/components/ai-elements/shimmer.tsx +1 -1
  122. package/src/components/internal/chat.tsx +326 -0
  123. package/src/components/internal/{Header.tsx → header.tsx} +37 -40
  124. package/src/components/internal/{InputSection.tsx → input-section.tsx} +173 -119
  125. package/src/components/internal/{MonacoJsonEditor.tsx → json-editor.tsx} +77 -49
  126. package/src/components/internal/logo.tsx +3 -5
  127. package/src/components/internal/schema.tsx +96 -0
  128. package/src/components/internal/{WorkbenchProvider.tsx → workbench-provider.tsx} +194 -68
  129. package/src/components/ui/avatar.tsx +1 -1
  130. package/src/components/ui/command.tsx +1 -1
  131. package/src/components/ui/dialog.tsx +1 -1
  132. package/src/components/ui/dropdown-menu.tsx +1 -1
  133. package/src/components/ui/hover-card.tsx +1 -1
  134. package/src/components/ui/input-group.tsx +1 -1
  135. package/src/components/ui/input.tsx +1 -1
  136. package/src/components/ui/scroll-area.tsx +1 -1
  137. package/src/components/ui/textarea.tsx +1 -1
  138. package/src/components/ui/theme-provider.tsx +1 -1
  139. package/src/components/ui/tooltip.tsx +1 -1
  140. package/src/hooks/useAgentSchemas.ts +26 -15
  141. package/src/hooks/useLogger.ts +7 -1
  142. package/src/hooks/useWorkbenchWebsocket.ts +67 -32
  143. package/src/index.ts +5 -9
  144. package/src/lib/utils.ts +88 -0
  145. package/src/server.ts +1 -1
  146. package/src/types/config.ts +28 -21
  147. package/dist/components/internal/Chat.d.ts +0 -14
  148. package/dist/components/internal/Chat.d.ts.map +0 -1
  149. package/dist/components/internal/Chat.js +0 -61
  150. package/dist/components/internal/Chat.js.map +0 -1
  151. package/dist/components/internal/Header.d.ts.map +0 -1
  152. package/dist/components/internal/Header.js +0 -31
  153. package/dist/components/internal/Header.js.map +0 -1
  154. package/dist/components/internal/InputSection.d.ts.map +0 -1
  155. package/dist/components/internal/InputSection.js +0 -152
  156. package/dist/components/internal/InputSection.js.map +0 -1
  157. package/dist/components/internal/MonacoJsonEditor.d.ts +0 -13
  158. package/dist/components/internal/MonacoJsonEditor.d.ts.map +0 -1
  159. package/dist/components/internal/MonacoJsonEditor.js.map +0 -1
  160. package/dist/components/internal/Schema.d.ts.map +0 -1
  161. package/dist/components/internal/Schema.js +0 -13
  162. package/dist/components/internal/Schema.js.map +0 -1
  163. package/dist/components/internal/WorkbenchProvider.d.ts.map +0 -1
  164. package/dist/components/internal/WorkbenchProvider.js.map +0 -1
  165. package/dist/components/ui/resizable-provider.d.ts.map +0 -1
  166. package/dist/components/ui/resizable-provider.js.map +0 -1
  167. package/src/components/internal/Chat.tsx +0 -201
  168. package/src/components/internal/Schema.tsx +0 -100
  169. /package/dist/components/{ui → internal}/resizable-provider.d.ts +0 -0
  170. /package/dist/components/{ui → internal}/resizable-provider.js +0 -0
  171. /package/src/components/{ui → internal}/resizable-provider.tsx +0 -0
@@ -1,7 +1,10 @@
1
1
  'use client';
2
2
 
3
- import { Button } from '../ui/button';
4
- import { cn } from '../../lib/utils';
3
+ import javascriptLang from '@shikijs/langs/javascript';
4
+ import jsonLang from '@shikijs/langs/json';
5
+ import typescriptLang from '@shikijs/langs/typescript';
6
+ import themeDarkModule from '@shikijs/themes/dark-plus';
7
+ import themeLightModule from '@shikijs/themes/light-plus';
5
8
  import type { Element } from 'hast';
6
9
  import { CheckIcon, CopyIcon } from 'lucide-react';
7
10
  import {
@@ -13,21 +16,19 @@ import {
13
16
  useRef,
14
17
  useState,
15
18
  } from 'react';
19
+ import type { ShikiTransformer, ThemeRegistration } from 'shiki';
16
20
  import { createHighlighterCore } from 'shiki/core';
17
21
  import { createOnigurumaEngine } from 'shiki/engine/oniguruma';
18
- import type { ShikiTransformer, ThemeRegistration } from 'shiki';
19
- import oneLightModule from '@shikijs/themes/one-light';
20
- import oneDarkProModule from '@shikijs/themes/one-dark-pro';
21
- import jsonLang from '@shikijs/langs/json';
22
- import javascriptLang from '@shikijs/langs/javascript';
23
- import typescriptLang from '@shikijs/langs/typescript';
22
+ import { cn } from '../../lib/utils';
23
+ import { Button } from '../ui/button';
24
+ import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
24
25
 
25
26
  // Extract theme objects from default exports
26
- const oneLight = (
27
- 'default' in oneLightModule ? oneLightModule.default : oneLightModule
27
+ const themeLight = (
28
+ 'default' in themeLightModule ? themeLightModule.default : themeLightModule
28
29
  ) as ThemeRegistration;
29
- const oneDarkPro = (
30
- 'default' in oneDarkProModule ? oneDarkProModule.default : oneDarkProModule
30
+ const themeDark = (
31
+ 'default' in themeDarkModule ? themeDarkModule.default : themeDarkModule
31
32
  ) as ThemeRegistration;
32
33
 
33
34
  type SupportedLanguage = 'json' | 'javascript' | 'typescript';
@@ -52,7 +53,7 @@ let highlighterPromise: ReturnType<typeof createHighlighterCore> | null = null;
52
53
  function getHighlighter() {
53
54
  if (!highlighterPromise) {
54
55
  highlighterPromise = createHighlighterCore({
55
- themes: [oneLight, oneDarkPro],
56
+ themes: [themeLight, themeDark],
56
57
  langs: [jsonLang, javascriptLang, typescriptLang],
57
58
  engine: createOnigurumaEngine(import('shiki/wasm')),
58
59
  });
@@ -87,17 +88,18 @@ export async function highlightCode(
87
88
  showLineNumbers = false
88
89
  ): Promise<readonly [string, string]> {
89
90
  const highlighter = await getHighlighter();
91
+
90
92
  const transformers: ShikiTransformer[] = showLineNumbers ? [lineNumberTransformer] : [];
91
93
 
92
94
  return [
93
95
  highlighter.codeToHtml(code, {
94
96
  lang: language,
95
- theme: oneLight.name ?? 'one-light',
97
+ theme: themeLight.name ?? 'github-light',
96
98
  transformers,
97
99
  }),
98
100
  highlighter.codeToHtml(code, {
99
101
  lang: language,
100
- theme: oneDarkPro.name ?? 'one-dark-pro',
102
+ theme: themeDark.name ?? 'github-dark',
101
103
  transformers,
102
104
  }),
103
105
  ] as const;
@@ -111,14 +113,14 @@ export const CodeBlock = ({
111
113
  children,
112
114
  ...props
113
115
  }: CodeBlockProps) => {
114
- const [html, setHtml] = useState<string>('');
116
+ const [lightHtml, setLightHtml] = useState<string>('');
115
117
  const [darkHtml, setDarkHtml] = useState<string>('');
116
118
  const mounted = useRef(false);
117
119
 
118
120
  useEffect(() => {
119
121
  highlightCode(code, language, showLineNumbers).then(([light, dark]) => {
120
122
  if (!mounted.current) {
121
- setHtml(light);
123
+ setLightHtml(light);
122
124
  setDarkHtml(dark);
123
125
  mounted.current = true;
124
126
  }
@@ -129,6 +131,9 @@ export const CodeBlock = ({
129
131
  };
130
132
  }, [code, language, showLineNumbers]);
131
133
 
134
+ const baseClass =
135
+ 'overflow-hidden [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&>pre]:whitespace-pre-wrap [&_code]:font-mono [&_code]:text-sm';
136
+
132
137
  return (
133
138
  <CodeBlockContext.Provider value={{ code }}>
134
139
  <div
@@ -139,18 +144,22 @@ export const CodeBlock = ({
139
144
  {...props}
140
145
  >
141
146
  <div className="relative">
147
+ {/* Light Mode */}
142
148
  <div
143
- className="overflow-hidden dark:hidden [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm"
144
- // biome-ignore lint/security/noDangerouslySetInnerHtml: "this is needed."
145
- dangerouslySetInnerHTML={{ __html: html }}
149
+ className={cn(baseClass, 'dark:hidden')}
150
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: must be added via this method as per the library
151
+ dangerouslySetInnerHTML={{ __html: lightHtml }}
146
152
  />
153
+
154
+ {/* Dark Mode */}
147
155
  <div
148
- className="hidden overflow-hidden dark:block [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm"
149
- // biome-ignore lint/security/noDangerouslySetInnerHtml: "this is needed."
156
+ className={cn(baseClass, 'hidden dark:block')}
157
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: must be added via this method as per the library
150
158
  dangerouslySetInnerHTML={{ __html: darkHtml }}
151
159
  />
160
+
152
161
  {children && (
153
- <div className="absolute top-2 right-2 flex items-center gap-2">{children}</div>
162
+ <div className="absolute top-1 right-1 flex items-center gap-2">{children}</div>
154
163
  )}
155
164
  </div>
156
165
  </div>
@@ -194,14 +203,19 @@ export const CodeBlockCopyButton = ({
194
203
  const Icon = isCopied ? CheckIcon : CopyIcon;
195
204
 
196
205
  return (
197
- <Button
198
- className={cn('shrink-0', className)}
199
- onClick={copyToClipboard}
200
- size="icon"
201
- variant="ghost"
202
- {...props}
203
- >
204
- {children ?? <Icon size={14} />}
205
- </Button>
206
+ <Tooltip>
207
+ <TooltipTrigger asChild>
208
+ <Button
209
+ className={cn('size-7 shrink-0 rounded-sm', className)}
210
+ onClick={copyToClipboard}
211
+ size="icon"
212
+ variant="ghost"
213
+ {...props}
214
+ >
215
+ {children ?? <Icon size={14} />}
216
+ </Button>
217
+ </TooltipTrigger>
218
+ <TooltipContent>Copy to clipboard</TooltipContent>
219
+ </Tooltip>
206
220
  );
207
221
  };
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
 
3
- import { Button } from '../ui/button';
4
- import { cn } from '../../lib/utils';
5
3
  import { ArrowDownIcon } from 'lucide-react';
6
4
  import type { ComponentProps } from 'react';
7
5
  import { useCallback } from 'react';
8
6
  import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom';
7
+ import { cn } from '../../lib/utils';
8
+ import { Button } from '../ui/button';
9
9
 
10
10
  export type ConversationProps = ComponentProps<typeof StickToBottom>;
11
11
 
@@ -71,20 +71,21 @@ export const ConversationScrollButton = ({
71
71
  }, [scrollToBottom]);
72
72
 
73
73
  return (
74
- !isAtBottom && (
75
- <Button
76
- className={cn(
77
- 'absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full',
78
- className
79
- )}
80
- onClick={handleScrollToBottom}
81
- size="icon"
82
- type="button"
83
- variant="outline"
84
- {...props}
85
- >
86
- <ArrowDownIcon className="size-4" />
87
- </Button>
88
- )
74
+ <Button
75
+ className={cn(
76
+ 'absolute bottom-0 left-[50%] translate-x-[-50%] rounded-full transition-all duration-300',
77
+ isAtBottom
78
+ ? 'opacity-0 pointer-events-none -translate-y-1/2'
79
+ : 'opacity-100 translate-y-0',
80
+ className
81
+ )}
82
+ onClick={handleScrollToBottom}
83
+ size="icon"
84
+ type="button"
85
+ variant="outline"
86
+ {...props}
87
+ >
88
+ <ArrowDownIcon className="size-4" />
89
+ </Button>
89
90
  );
90
91
  };
@@ -1,8 +1,8 @@
1
- import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
2
- import { cn } from '../../lib/utils';
3
1
  import type { UIMessage } from 'ai';
4
2
  import { cva, type VariantProps } from 'class-variance-authority';
5
3
  import type { ComponentProps, HTMLAttributes } from 'react';
4
+ import { cn } from '../../lib/utils';
5
+ import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
6
6
 
7
7
  export type MessageProps = HTMLAttributes<HTMLDivElement> & {
8
8
  from: UIMessage['role'];
@@ -20,18 +20,13 @@ export const Message = ({ className, from, ...props }: MessageProps) => (
20
20
  );
21
21
 
22
22
  const messageContentVariants = cva(
23
- 'is-user:dark flex flex-col gap-2 overflow-hidden rounded-lg text-sm',
23
+ 'is-user:dark flex flex-col gap-2 overflow-hidden rounded-md text-sm',
24
24
  {
25
25
  variants: {
26
26
  variant: {
27
- contained: [
28
- 'max-w-[80%] px-4 py-3',
29
- 'group-[.is-user]:bg-primary group-[.is-user]:text-primary-foreground',
30
- 'group-[.is-assistant]:bg-secondary group-[.is-assistant]:text-foreground',
31
- ],
27
+ contained: ['max-w-[80%] px-4 py-3 bg-input/30 text-foreground'],
32
28
  flat: [
33
29
  'group-[.is-user]:max-w-[80%] group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground',
34
- 'group-[.is-assistant]:text-foreground',
35
30
  ],
36
31
  },
37
32
  },
@@ -34,6 +34,7 @@ import {
34
34
  useRef,
35
35
  useState,
36
36
  } from 'react';
37
+ import { cn } from '../../lib/utils';
37
38
  import { Button } from '../ui/button';
38
39
  import {
39
40
  Command,
@@ -58,7 +59,6 @@ import {
58
59
  InputGroupTextarea,
59
60
  } from '../ui/input-group';
60
61
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select';
61
- import { cn } from '../../lib/utils';
62
62
  // ============================================================================
63
63
  // Provider Context & Types
64
64
  // ============================================================================
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
- import { cn } from '../../lib/utils';
4
3
  import { motion } from 'motion/react';
5
4
  import { type CSSProperties, type ElementType, type JSX, memo, useMemo } from 'react';
5
+ import { cn } from '../../lib/utils';
6
6
 
7
7
  export type TextShimmerProps = {
8
8
  children: string;
@@ -0,0 +1,326 @@
1
+ import { ChevronRight, Copy, Loader, RefreshCcw } from 'lucide-react';
2
+ import { useState } from 'react';
3
+ import { useLogger } from '../../hooks/useLogger';
4
+ import { cn, formatErrorForCopy } from '../../lib/utils';
5
+ import { Action, Actions } from '../ai-elements/actions';
6
+ import { CodeBlock } from '../ai-elements/code-block';
7
+ import {
8
+ Conversation,
9
+ ConversationContent,
10
+ ConversationScrollButton,
11
+ } from '../ai-elements/conversation';
12
+ import { Message, MessageContent } from '../ai-elements/message';
13
+ import { Shimmer } from '../ai-elements/shimmer';
14
+ import { InputSection } from './input-section';
15
+ import { useWorkbench } from './workbench-provider';
16
+
17
+ export interface ChatProps {
18
+ className?: string;
19
+ emptyState?: React.ReactNode;
20
+ onSchemaToggle?: () => void;
21
+ onSessionOpen?: (sessionId: string) => void;
22
+ schemaOpen?: boolean;
23
+ }
24
+
25
+ export function Chat({
26
+ className: _className,
27
+ emptyState,
28
+ onSchemaToggle,
29
+ onSessionOpen,
30
+ schemaOpen,
31
+ }: ChatProps) {
32
+ const {
33
+ agents,
34
+ clearAgentState,
35
+ connectionStatus,
36
+ isLoading,
37
+ messages,
38
+ selectedAgent,
39
+ setSelectedAgent,
40
+ submitMessage,
41
+ } = useWorkbench();
42
+ const logger = useLogger('Chat');
43
+ const [value, setValue] = useState('');
44
+
45
+ const handleSubmit = () => {
46
+ logger.debug('🎯 Chat handleSubmit - selectedAgent:', selectedAgent, 'value:', value);
47
+
48
+ const selectedAgentData = Object.values(agents).find(
49
+ (agent) => agent.metadata.agentId === selectedAgent
50
+ );
51
+
52
+ logger.debug('📊 Chat handleSubmit - selectedAgentData:', selectedAgentData);
53
+
54
+ const hasInputSchema = selectedAgentData?.schema?.input?.json;
55
+
56
+ logger.debug('📝 Chat handleSubmit - hasInputSchema:', hasInputSchema);
57
+
58
+ // If agent has no input schema, submit without requiring input
59
+ if (!hasInputSchema) {
60
+ submitMessage('');
61
+
62
+ return;
63
+ }
64
+
65
+ // For agents with input schema, require input
66
+ if (!value.trim()) {
67
+ return;
68
+ }
69
+
70
+ submitMessage(value);
71
+ setValue('');
72
+ };
73
+
74
+ return (
75
+ <div className="relative flex flex-col h-full w-full overflow-hidden">
76
+ <Conversation
77
+ className="flex-1 overflow-y-auto [&>div]:overflow-y-auto"
78
+ id="chat-conversation"
79
+ >
80
+ {connectionStatus === 'disconnected' && emptyState ? (
81
+ <div className="flex flex-col h-full">{emptyState}</div>
82
+ ) : (
83
+ <ConversationContent>
84
+ {messages.map((message) => {
85
+ const { role, parts, id } = message;
86
+
87
+ const isStreaming = parts.some(
88
+ (part) => part.type === 'text' && part.state === 'streaming'
89
+ );
90
+
91
+ const tokens =
92
+ 'tokens' in message ? (message as { tokens?: string }).tokens : undefined;
93
+
94
+ const duration =
95
+ 'duration' in message
96
+ ? (message as { duration?: string }).duration
97
+ : undefined;
98
+
99
+ const sessionId =
100
+ 'sessionId' in message
101
+ ? (message as { sessionId?: string }).sessionId
102
+ : undefined;
103
+
104
+ // Check for agent error in text content
105
+ let errorInfo:
106
+ | {
107
+ message: string;
108
+ stack?: string;
109
+ code?: string;
110
+ cause?: unknown;
111
+ }
112
+ | undefined;
113
+
114
+ if (parts.length === 1 && parts[0].type === 'text') {
115
+ const text = parts[0].text;
116
+
117
+ if (text.startsWith('{') && text.includes('"__agentError"')) {
118
+ try {
119
+ const parsed = JSON.parse(text) as {
120
+ __agentError?: boolean;
121
+ message?: string;
122
+ stack?: string;
123
+ code?: string;
124
+ cause?: unknown;
125
+ };
126
+
127
+ if (parsed.__agentError) {
128
+ errorInfo = {
129
+ message: parsed.message || 'Unknown error',
130
+ stack: parsed.stack,
131
+ code: parsed.code,
132
+ cause: parsed.cause,
133
+ };
134
+ }
135
+ } catch {
136
+ // Not valid JSON, ignore
137
+ }
138
+ }
139
+ }
140
+
141
+ return (
142
+ <div key={id} className="mb-2">
143
+ {role === 'assistant' && (
144
+ <div className="w-fit flex items-center mb-2 text-muted-foreground text-sm transition-colors">
145
+ <Loader
146
+ className={cn(
147
+ 'size-4 transition-all',
148
+ isStreaming ? 'animate-spin mr-2' : 'w-0 mr-2.5'
149
+ )}
150
+ />
151
+
152
+ {isStreaming ? (
153
+ <Shimmer duration={1}>Running...</Shimmer>
154
+ ) : (
155
+ <button
156
+ type="button"
157
+ className={cn(
158
+ 'flex items-center bg-transparent border-none p-0 text-inherit',
159
+ sessionId && onSessionOpen
160
+ ? 'hover:text-foreground transition-colors cursor-pointer'
161
+ : 'cursor-default'
162
+ )}
163
+ onClick={() => sessionId && onSessionOpen?.(sessionId)}
164
+ tabIndex={0}
165
+ aria-label="Open session"
166
+ disabled={!sessionId}
167
+ style={{ background: 'none' }}
168
+ >
169
+ {duration ? (
170
+ <>
171
+ Ran for
172
+ <span className="mx-1">{duration}</span>
173
+ </>
174
+ ) : (
175
+ <>Finished</>
176
+ )}
177
+
178
+ {duration && tokens && (
179
+ <>
180
+ and consumed
181
+ <span className="mx-1">{tokens}</span> tokens
182
+ </>
183
+ )}
184
+
185
+ {sessionId && onSessionOpen && (
186
+ <ChevronRight className="size-4" />
187
+ )}
188
+ </button>
189
+ )}
190
+ </div>
191
+ )}
192
+
193
+ {(role === 'user' || !isStreaming) && (
194
+ <>
195
+ <Message
196
+ key={id}
197
+ from={role as 'user' | 'system' | 'assistant'}
198
+ className="p-0"
199
+ >
200
+ <MessageContent
201
+ className={cn(errorInfo && 'bg-destructive/10')}
202
+ >
203
+ {errorInfo ? (
204
+ errorInfo.stack ? (
205
+ <pre className="font-mono whitespace-pre-wrap overflow-x-auto text-destructive">
206
+ {errorInfo.stack}
207
+ </pre>
208
+ ) : (
209
+ <p className="font-mono whitespace-pre-wrap overflow-x-auto text-destructive">
210
+ {errorInfo.message || 'Unknown error'}
211
+ </p>
212
+ )
213
+ ) : (
214
+ parts.map((part, index) => {
215
+ switch (part.type) {
216
+ case 'text':
217
+ // json?
218
+ if (
219
+ part.text.startsWith('{') &&
220
+ part.text.endsWith('}')
221
+ ) {
222
+ try {
223
+ const json = JSON.parse(part.text);
224
+
225
+ // json!
226
+ return (
227
+ <CodeBlock
228
+ key={`${id}-${part.text}-${index}`}
229
+ code={JSON.stringify(json, null, 2)}
230
+ language="json"
231
+ className="bg-transparent border-0 [&>div>div>pre]:bg-transparent! [&_pre]:p-0!"
232
+ />
233
+ );
234
+ } catch (_error) {
235
+ // not json :(
236
+ return (
237
+ <div
238
+ key={`${id}-${part.text}-${index}`}
239
+ >
240
+ {part.text || ''}
241
+ </div>
242
+ );
243
+ }
244
+ }
245
+
246
+ // text/markdown
247
+ return (
248
+ <div key={`${id}-${part.text}-${index}`}>
249
+ {part.text || ''}
250
+ </div>
251
+ );
252
+ default:
253
+ return null;
254
+ }
255
+ })
256
+ )}
257
+ </MessageContent>
258
+ </Message>
259
+
260
+ <Actions
261
+ className={cn('mt-1 gap-0', role === 'user' && 'justify-end')}
262
+ >
263
+ {role === 'user' && (
264
+ <Action
265
+ tooltip="Re-run"
266
+ label="Re-run"
267
+ className="size-8 hover:bg-transparent!"
268
+ onClick={() =>
269
+ setValue(
270
+ parts
271
+ .filter((part) => part.type === 'text')
272
+ .map((part) => part.text)
273
+ .join('')
274
+ )
275
+ }
276
+ >
277
+ <RefreshCcw className="size-4" />
278
+ </Action>
279
+ )}
280
+
281
+ <Action
282
+ tooltip="Copy to clipboard"
283
+ label="Copy to clipboard"
284
+ className="size-8 hover:bg-transparent!"
285
+ onClick={() => {
286
+ const text = errorInfo
287
+ ? formatErrorForCopy(errorInfo)
288
+ : parts
289
+ .filter((part) => part.type === 'text')
290
+ .map((part) => part.text)
291
+ .join('');
292
+
293
+ navigator.clipboard.writeText(text);
294
+ }}
295
+ >
296
+ <Copy className="size-4" />
297
+ </Action>
298
+ </Actions>
299
+ </>
300
+ )}
301
+ </div>
302
+ );
303
+ })}
304
+ </ConversationContent>
305
+ )}
306
+
307
+ <ConversationScrollButton />
308
+ </Conversation>
309
+
310
+ <InputSection
311
+ agents={agents}
312
+ clearAgentState={clearAgentState}
313
+ isLoading={isLoading}
314
+ isSchemaOpen={schemaOpen}
315
+ onChange={setValue}
316
+ onSchemaToggle={onSchemaToggle}
317
+ onSubmit={handleSubmit}
318
+ selectedAgent={selectedAgent}
319
+ setSelectedAgent={setSelectedAgent}
320
+ value={value}
321
+ />
322
+ </div>
323
+ );
324
+ }
325
+
326
+ export default Chat;