@agentuity/workbench 0.0.79 → 0.0.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/internal/Chat.d.ts.map +1 -1
- package/dist/components/internal/Chat.js +6 -1
- package/dist/components/internal/Chat.js.map +1 -1
- package/dist/components/internal/InputSection.d.ts.map +1 -1
- package/dist/components/internal/InputSection.js +29 -151
- package/dist/components/internal/InputSection.js.map +1 -1
- package/dist/components/internal/MonacoJsonEditor.d.ts +11 -0
- package/dist/components/internal/MonacoJsonEditor.d.ts.map +1 -0
- package/dist/components/internal/MonacoJsonEditor.js +270 -0
- package/dist/components/internal/MonacoJsonEditor.js.map +1 -0
- package/dist/components/internal/Schema.d.ts.map +1 -1
- package/dist/components/internal/Schema.js +1 -1
- package/dist/components/internal/Schema.js.map +1 -1
- package/dist/components/internal/WorkbenchProvider.d.ts.map +1 -1
- package/dist/components/internal/WorkbenchProvider.js +30 -9
- package/dist/components/internal/WorkbenchProvider.js.map +1 -1
- package/dist/components/ui/field.d.ts +1 -1
- package/dist/components.d.ts +1 -0
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +1 -0
- package/dist/components.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/useAgentSchemas.d.ts +1 -0
- package/dist/hooks/useAgentSchemas.d.ts.map +1 -1
- package/dist/hooks/useAgentSchemas.js.map +1 -1
- package/dist/hooks/useLogger.d.ts +9 -0
- package/dist/hooks/useLogger.d.ts.map +1 -0
- package/dist/hooks/useLogger.js +41 -0
- package/dist/hooks/useLogger.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +31 -0
- package/dist/lib/utils.js.map +1 -1
- package/dist/styles.css +60 -0
- package/package.json +10 -5
- package/src/components/internal/Chat.tsx +8 -1
- package/src/components/internal/InputSection.tsx +71 -193
- package/src/components/internal/MonacoJsonEditor.tsx +359 -0
- package/src/components/internal/Schema.tsx +2 -1
- package/src/components/internal/WorkbenchProvider.tsx +35 -9
- package/src/components.tsx +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAgentSchemas.ts +1 -0
- package/src/hooks/useLogger.ts +57 -0
- package/src/index.ts +1 -0
- package/src/lib/utils.ts +41 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import Editor, { type Monaco, type OnMount } from '@monaco-editor/react';
|
|
3
|
+
import { useTheme } from '../ui/theme-provider';
|
|
4
|
+
import { bundledThemes } from 'shiki';
|
|
5
|
+
import type { JSONSchema7 } from 'ai';
|
|
6
|
+
|
|
7
|
+
interface MonacoJsonEditorProps {
|
|
8
|
+
value: string;
|
|
9
|
+
onChange: (value: string) => void;
|
|
10
|
+
schema?: JSONSchema7;
|
|
11
|
+
schemaUri?: string;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Convert color value to valid hex for Monaco
|
|
16
|
+
function normalizeColorForMonaco(color: string | undefined, isDark: boolean): string {
|
|
17
|
+
if (!color) return isDark ? 'abb2bf' : '383a42'; // Default foreground colors
|
|
18
|
+
|
|
19
|
+
// Remove # prefix if present
|
|
20
|
+
let normalized = color.replace('#', '');
|
|
21
|
+
|
|
22
|
+
// Handle common color names that might appear in themes
|
|
23
|
+
const colorMap: Record<string, string> = {
|
|
24
|
+
white: isDark ? 'ffffff' : '383a42',
|
|
25
|
+
black: isDark ? '000000' : 'abb2bf',
|
|
26
|
+
red: 'e45649',
|
|
27
|
+
green: '50a14f',
|
|
28
|
+
blue: '4078f2',
|
|
29
|
+
yellow: '986801',
|
|
30
|
+
cyan: '0184bc',
|
|
31
|
+
magenta: 'a626a4',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
if (colorMap[normalized.toLowerCase()]) {
|
|
35
|
+
normalized = colorMap[normalized.toLowerCase()];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Validate it's a proper hex color (3 or 6 characters)
|
|
39
|
+
if (!/^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{6}$/.test(normalized)) {
|
|
40
|
+
return isDark ? 'abb2bf' : '383a42'; // Fallback to default
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Convert Shiki theme to Monaco theme
|
|
47
|
+
function convertShikiToMonaco(
|
|
48
|
+
shikiTheme: {
|
|
49
|
+
colors?: Record<string, string>;
|
|
50
|
+
tokenColors?: Array<{
|
|
51
|
+
scope?: string | string[];
|
|
52
|
+
settings?: { foreground?: string; fontStyle?: string };
|
|
53
|
+
}>;
|
|
54
|
+
},
|
|
55
|
+
themeName: string
|
|
56
|
+
) {
|
|
57
|
+
const colors = shikiTheme.colors || {};
|
|
58
|
+
const tokenColors = shikiTheme.tokenColors || [];
|
|
59
|
+
const isDark = themeName.includes('dark');
|
|
60
|
+
|
|
61
|
+
// Convert token colors to Monaco rules
|
|
62
|
+
const rules: Array<{ token: string; foreground: string; fontStyle?: string }> = [];
|
|
63
|
+
tokenColors.forEach((tokenColor) => {
|
|
64
|
+
if (tokenColor.scope && tokenColor.settings?.foreground) {
|
|
65
|
+
const scopes = Array.isArray(tokenColor.scope) ? tokenColor.scope : [tokenColor.scope];
|
|
66
|
+
scopes.forEach((scope: string) => {
|
|
67
|
+
// Map common scopes to Monaco tokens
|
|
68
|
+
let token = scope;
|
|
69
|
+
if (scope.includes('string.quoted.double.json')) token = 'string.value.json';
|
|
70
|
+
if (scope.includes('support.type.property-name.json')) token = 'string.key.json';
|
|
71
|
+
if (scope.includes('constant.numeric.json')) token = 'number.json';
|
|
72
|
+
if (scope.includes('constant.language.json')) token = 'keyword.json';
|
|
73
|
+
if (scope.includes('punctuation.definition.string.json'))
|
|
74
|
+
token = 'delimiter.bracket.json';
|
|
75
|
+
|
|
76
|
+
const normalizedColor = normalizeColorForMonaco(
|
|
77
|
+
tokenColor.settings?.foreground,
|
|
78
|
+
isDark
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
rules.push({
|
|
82
|
+
token,
|
|
83
|
+
foreground: normalizedColor,
|
|
84
|
+
fontStyle: tokenColor.settings?.fontStyle || undefined,
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
base: isDark ? 'vs-dark' : 'vs',
|
|
92
|
+
inherit: true,
|
|
93
|
+
rules,
|
|
94
|
+
colors: {
|
|
95
|
+
'editor.background': '#00000000', // Always transparent
|
|
96
|
+
'editor.foreground': normalizeColorForMonaco(colors['editor.foreground'], isDark),
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function MonacoJsonEditor({
|
|
102
|
+
value,
|
|
103
|
+
onChange,
|
|
104
|
+
schema,
|
|
105
|
+
schemaUri = 'agentuity://schema/default',
|
|
106
|
+
className = '',
|
|
107
|
+
}: MonacoJsonEditorProps) {
|
|
108
|
+
const { theme } = useTheme();
|
|
109
|
+
const [editorInstance, setEditorInstance] = useState<Parameters<OnMount>[0] | null>(null);
|
|
110
|
+
const [monacoInstance, setMonacoInstance] = useState<Monaco | null>(null);
|
|
111
|
+
const [editorHeight, setEditorHeight] = useState(120);
|
|
112
|
+
|
|
113
|
+
// Get resolved theme (similar to useTheme's resolvedTheme from next-themes)
|
|
114
|
+
const resolvedTheme = React.useMemo(() => {
|
|
115
|
+
if (theme === 'system') {
|
|
116
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
117
|
+
}
|
|
118
|
+
return theme;
|
|
119
|
+
}, [theme]);
|
|
120
|
+
|
|
121
|
+
// Configure JSON schema when schema or monacoInstance changes
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (!monacoInstance || !schema) return;
|
|
124
|
+
|
|
125
|
+
const schemaObject = typeof schema === 'string' ? JSON.parse(schema) : schema;
|
|
126
|
+
|
|
127
|
+
// Configure Monaco JSON language support for schema validation
|
|
128
|
+
monacoInstance.languages.json.jsonDefaults.setDiagnosticsOptions({
|
|
129
|
+
validate: true,
|
|
130
|
+
schemas: [
|
|
131
|
+
{
|
|
132
|
+
uri: schemaUri,
|
|
133
|
+
fileMatch: ['*'],
|
|
134
|
+
schema: schemaObject,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
}, [monacoInstance, schema, schemaUri]);
|
|
139
|
+
|
|
140
|
+
// Handle theme changes for existing editor instance
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (editorInstance && monacoInstance) {
|
|
143
|
+
editorInstance.updateOptions({
|
|
144
|
+
theme: resolvedTheme === 'light' ? 'custom-light' : 'custom-dark',
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}, [resolvedTheme, editorInstance, monacoInstance]);
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<div
|
|
151
|
+
className={`w-full pl-3 pb-3 [&_.monaco-editor]:!bg-transparent [&_.monaco-editor-background]:!bg-transparent [&_.view-lines]:!bg-transparent [&_.monaco-editor]:!shadow-none [&_.monaco-scrollable-element]:!shadow-none [&_.overflow-guard]:!shadow-none [&_.monaco-scrollable-element>.shadow.top]:!hidden [&_.monaco-editor_.scroll-decoration]:!hidden [&_.shadow.top]:!hidden [&_.scroll-decoration]:!hidden ${className}`}
|
|
152
|
+
style={{ minHeight: '64px', maxHeight: '192px', height: `${editorHeight}px` }}
|
|
153
|
+
>
|
|
154
|
+
<Editor
|
|
155
|
+
value={value || '{}'}
|
|
156
|
+
onChange={(newValue) => onChange(newValue || '')}
|
|
157
|
+
language="json"
|
|
158
|
+
theme={resolvedTheme === 'light' ? 'custom-light' : 'custom-dark'}
|
|
159
|
+
height="100%"
|
|
160
|
+
options={{
|
|
161
|
+
minimap: { enabled: false },
|
|
162
|
+
lineNumbers: 'off',
|
|
163
|
+
folding: false,
|
|
164
|
+
scrollBeyondLastLine: false,
|
|
165
|
+
wordWrap: 'on',
|
|
166
|
+
renderLineHighlight: 'none',
|
|
167
|
+
overviewRulerBorder: false,
|
|
168
|
+
overviewRulerLanes: 0,
|
|
169
|
+
hideCursorInOverviewRuler: true,
|
|
170
|
+
fixedOverflowWidgets: true,
|
|
171
|
+
roundedSelection: false,
|
|
172
|
+
occurrencesHighlight: 'off',
|
|
173
|
+
selectionHighlight: false,
|
|
174
|
+
renderWhitespace: 'none',
|
|
175
|
+
fontSize: 14,
|
|
176
|
+
fontWeight: '400',
|
|
177
|
+
formatOnPaste: true,
|
|
178
|
+
formatOnType: true,
|
|
179
|
+
autoIndent: 'full',
|
|
180
|
+
glyphMargin: false,
|
|
181
|
+
lineDecorationsWidth: 0,
|
|
182
|
+
lineNumbersMinChars: 0,
|
|
183
|
+
automaticLayout: true,
|
|
184
|
+
scrollbar: {
|
|
185
|
+
vertical: 'auto',
|
|
186
|
+
horizontal: 'auto',
|
|
187
|
+
verticalScrollbarSize: 10,
|
|
188
|
+
horizontalScrollbarSize: 10,
|
|
189
|
+
// Disable scroll shadows
|
|
190
|
+
verticalHasArrows: false,
|
|
191
|
+
horizontalHasArrows: false,
|
|
192
|
+
},
|
|
193
|
+
padding: { top: 12, bottom: 12 },
|
|
194
|
+
// Additional background transparency options
|
|
195
|
+
renderValidationDecorations: 'off',
|
|
196
|
+
guides: {
|
|
197
|
+
indentation: false,
|
|
198
|
+
highlightActiveIndentation: false,
|
|
199
|
+
},
|
|
200
|
+
// Disable sticky scroll feature
|
|
201
|
+
stickyScroll: { enabled: false },
|
|
202
|
+
// Disable scroll decorations/shadows
|
|
203
|
+
scrollBeyondLastColumn: 0,
|
|
204
|
+
renderLineHighlightOnlyWhenFocus: true,
|
|
205
|
+
}}
|
|
206
|
+
onMount={(editor, monaco) => {
|
|
207
|
+
setEditorInstance(editor);
|
|
208
|
+
setMonacoInstance(monaco);
|
|
209
|
+
editor.focus();
|
|
210
|
+
|
|
211
|
+
// Auto-resize based on content
|
|
212
|
+
const updateHeight = () => {
|
|
213
|
+
const contentHeight = editor.getContentHeight();
|
|
214
|
+
const maxHeight = 192; // max-h-48 = 12rem = 192px
|
|
215
|
+
const minHeight = 64; // min-h-16 = 4rem = 64px
|
|
216
|
+
const newHeight = Math.min(Math.max(contentHeight + 24, minHeight), maxHeight);
|
|
217
|
+
setEditorHeight(newHeight);
|
|
218
|
+
|
|
219
|
+
// Layout after height change
|
|
220
|
+
setTimeout(() => editor.layout(), 0);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Update height on content changes
|
|
224
|
+
editor.onDidChangeModelContent(updateHeight);
|
|
225
|
+
|
|
226
|
+
// Initial height update
|
|
227
|
+
setTimeout(updateHeight, 0);
|
|
228
|
+
|
|
229
|
+
// Ensure background transparency and remove shadows
|
|
230
|
+
setTimeout(() => {
|
|
231
|
+
const editorElement = editor.getDomNode();
|
|
232
|
+
if (editorElement) {
|
|
233
|
+
// Set transparent backgrounds on all relevant elements
|
|
234
|
+
const elementsToMakeTransparent = [
|
|
235
|
+
'.monaco-editor',
|
|
236
|
+
'.monaco-editor .monaco-editor-background',
|
|
237
|
+
'.monaco-editor .view-lines',
|
|
238
|
+
'.monaco-editor .margin',
|
|
239
|
+
'.monaco-editor .monaco-scrollable-element',
|
|
240
|
+
'.monaco-editor .overflow-guard',
|
|
241
|
+
'.view-overlays',
|
|
242
|
+
'.decorationsOverviewRuler',
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
elementsToMakeTransparent.forEach((selector) => {
|
|
246
|
+
const element = editorElement.querySelector(selector);
|
|
247
|
+
if (element) {
|
|
248
|
+
(element as HTMLElement).style.backgroundColor = 'transparent';
|
|
249
|
+
(element as HTMLElement).style.boxShadow = 'none';
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Remove scroll shadows specifically - target the exact classes
|
|
254
|
+
const shadowTop = editorElement.querySelector(
|
|
255
|
+
'.monaco-scrollable-element > .shadow.top'
|
|
256
|
+
);
|
|
257
|
+
if (shadowTop) {
|
|
258
|
+
(shadowTop as HTMLElement).style.display = 'none';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const scrollDecorations = editorElement.querySelectorAll(
|
|
262
|
+
'.monaco-editor .scroll-decoration, .scroll-decoration'
|
|
263
|
+
);
|
|
264
|
+
scrollDecorations.forEach((decoration) => {
|
|
265
|
+
(decoration as HTMLElement).style.display = 'none';
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const scrollableElement = editorElement.querySelector(
|
|
269
|
+
'.monaco-scrollable-element'
|
|
270
|
+
);
|
|
271
|
+
if (scrollableElement) {
|
|
272
|
+
(scrollableElement as HTMLElement).style.setProperty(
|
|
273
|
+
'--scroll-shadow',
|
|
274
|
+
'none'
|
|
275
|
+
);
|
|
276
|
+
(scrollableElement as HTMLElement).style.setProperty(
|
|
277
|
+
'box-shadow',
|
|
278
|
+
'none',
|
|
279
|
+
'important'
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Also set transparent and remove shadow on the editor element itself
|
|
284
|
+
(editorElement as HTMLElement).style.backgroundColor = 'transparent';
|
|
285
|
+
(editorElement as HTMLElement).style.boxShadow = 'none';
|
|
286
|
+
}
|
|
287
|
+
}, 0);
|
|
288
|
+
}}
|
|
289
|
+
beforeMount={async (monaco) => {
|
|
290
|
+
setMonacoInstance(monaco);
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
// Try to use actual Shiki themes
|
|
294
|
+
const oneLightThemeModule = await bundledThemes['one-light']();
|
|
295
|
+
const oneDarkProThemeModule = await bundledThemes['one-dark-pro']();
|
|
296
|
+
|
|
297
|
+
if (oneLightThemeModule?.default) {
|
|
298
|
+
const lightMonacoTheme = convertShikiToMonaco(
|
|
299
|
+
oneLightThemeModule.default,
|
|
300
|
+
'one-light'
|
|
301
|
+
);
|
|
302
|
+
monaco.editor.defineTheme('custom-light', lightMonacoTheme);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (oneDarkProThemeModule?.default) {
|
|
306
|
+
const darkMonacoTheme = convertShikiToMonaco(
|
|
307
|
+
oneDarkProThemeModule.default,
|
|
308
|
+
'one-dark-pro'
|
|
309
|
+
);
|
|
310
|
+
monaco.editor.defineTheme('custom-dark', darkMonacoTheme);
|
|
311
|
+
}
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.warn(
|
|
314
|
+
'Failed to load Shiki themes, falling back to manual themes:',
|
|
315
|
+
error
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
// Fallback to manual theme definitions
|
|
319
|
+
monaco.editor.defineTheme('custom-light', {
|
|
320
|
+
base: 'vs',
|
|
321
|
+
inherit: true,
|
|
322
|
+
rules: [
|
|
323
|
+
{ token: 'string.key.json', foreground: 'e45649' },
|
|
324
|
+
{ token: 'string.value.json', foreground: '50a14f' },
|
|
325
|
+
{ token: 'number.json', foreground: '986801' },
|
|
326
|
+
{ token: 'keyword.json', foreground: '986801' },
|
|
327
|
+
{ token: 'string', foreground: '50a14f' },
|
|
328
|
+
{ token: 'number', foreground: '986801' },
|
|
329
|
+
{ token: 'keyword', foreground: '986801' },
|
|
330
|
+
],
|
|
331
|
+
colors: {
|
|
332
|
+
'editor.background': '#00000000',
|
|
333
|
+
'editor.foreground': '#383a42',
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
monaco.editor.defineTheme('custom-dark', {
|
|
338
|
+
base: 'vs-dark',
|
|
339
|
+
inherit: true,
|
|
340
|
+
rules: [
|
|
341
|
+
{ token: 'string.key.json', foreground: 'e06c75' },
|
|
342
|
+
{ token: 'string.value.json', foreground: '98c379' },
|
|
343
|
+
{ token: 'number.json', foreground: 'd19a66' },
|
|
344
|
+
{ token: 'keyword.json', foreground: 'c678dd' },
|
|
345
|
+
{ token: 'string', foreground: '98c379' },
|
|
346
|
+
{ token: 'number', foreground: 'd19a66' },
|
|
347
|
+
{ token: 'keyword', foreground: 'c678dd' },
|
|
348
|
+
],
|
|
349
|
+
colors: {
|
|
350
|
+
'editor.background': '#00000000',
|
|
351
|
+
'editor.foreground': '#abb2bf',
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}}
|
|
356
|
+
/>
|
|
357
|
+
</div>
|
|
358
|
+
);
|
|
359
|
+
}
|
|
@@ -14,7 +14,8 @@ export interface SchemaProps {
|
|
|
14
14
|
export function Schema({ open, onOpenChange }: SchemaProps) {
|
|
15
15
|
const { agents, selectedAgent, schemasLoading, schemasError } = useWorkbench();
|
|
16
16
|
|
|
17
|
-
const selectedAgentData =
|
|
17
|
+
const selectedAgentData =
|
|
18
|
+
Object.values(agents).find((agent) => agent.metadata.agentId === selectedAgent) || null;
|
|
18
19
|
|
|
19
20
|
return (
|
|
20
21
|
<>
|
|
@@ -4,7 +4,8 @@ import type { WorkbenchConfig } from '@agentuity/core/workbench';
|
|
|
4
4
|
import type { WorkbenchContextType, ConnectionStatus } from '../../types/config';
|
|
5
5
|
import { useAgentSchemas } from '../../hooks/useAgentSchemas';
|
|
6
6
|
import { useWorkbenchWebsocket } from '../../hooks/useWorkbenchWebsocket';
|
|
7
|
-
import {
|
|
7
|
+
import { useLogger } from '../../hooks/useLogger';
|
|
8
|
+
import { getTotalTokens, parseTokensHeader, defaultBaseUrl } from '../../lib/utils';
|
|
8
9
|
|
|
9
10
|
const WorkbenchContext = createContext<WorkbenchContextType | null>(null);
|
|
10
11
|
|
|
@@ -22,6 +23,8 @@ interface WorkbenchProviderProps {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export function WorkbenchProvider({ config, children }: WorkbenchProviderProps) {
|
|
26
|
+
const logger = useLogger('WorkbenchProvider');
|
|
27
|
+
|
|
25
28
|
const [messages, setMessages] = useState<UIMessage[]>([]);
|
|
26
29
|
const [selectedAgent, setSelectedAgent] = useState<string>('');
|
|
27
30
|
const [inputMode, setInputMode] = useState<'text' | 'form'>('text');
|
|
@@ -29,7 +32,7 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
29
32
|
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('disconnected');
|
|
30
33
|
|
|
31
34
|
// Config values
|
|
32
|
-
const baseUrl =
|
|
35
|
+
const baseUrl = defaultBaseUrl;
|
|
33
36
|
const apiKey = config.apiKey;
|
|
34
37
|
const shouldUseSchemas = true;
|
|
35
38
|
|
|
@@ -99,7 +102,14 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
99
102
|
// Set initial agent selection
|
|
100
103
|
useEffect(() => {
|
|
101
104
|
if (agents && Object.keys(agents).length > 0 && !selectedAgent) {
|
|
102
|
-
|
|
105
|
+
logger.debug('🔍 Available agents:', agents);
|
|
106
|
+
const sortedAgents = Object.values(agents).sort((a, b) =>
|
|
107
|
+
a.metadata.name.localeCompare(b.metadata.name)
|
|
108
|
+
);
|
|
109
|
+
const firstAgent = sortedAgents[0];
|
|
110
|
+
logger.debug('🎯 First agent (alphabetically):', firstAgent);
|
|
111
|
+
logger.debug('🆔 Setting selectedAgent to:', firstAgent.metadata.agentId);
|
|
112
|
+
setSelectedAgent(firstAgent.metadata.agentId);
|
|
103
113
|
}
|
|
104
114
|
}, [agents, selectedAgent]);
|
|
105
115
|
|
|
@@ -115,11 +125,21 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
115
125
|
const submitMessage = async (value: string, _mode: 'text' | 'form' = 'text') => {
|
|
116
126
|
if (!selectedAgent) return;
|
|
117
127
|
|
|
118
|
-
|
|
128
|
+
logger.debug('🚀 Submitting message with selectedAgent:', selectedAgent);
|
|
129
|
+
const selectedAgentData = agents
|
|
130
|
+
? Object.values(agents).find((agent) => agent.metadata.agentId === selectedAgent)
|
|
131
|
+
: undefined;
|
|
132
|
+
logger.debug('📊 Found selectedAgentData:', selectedAgentData);
|
|
119
133
|
const hasInputSchema = selectedAgentData?.schema?.input?.json;
|
|
134
|
+
logger.debug('📝 hasInputSchema:', hasInputSchema, 'value:', value);
|
|
120
135
|
|
|
121
136
|
// Only require value for agents with input schemas
|
|
122
|
-
if (hasInputSchema && !value.trim())
|
|
137
|
+
if (hasInputSchema && !value.trim()) {
|
|
138
|
+
logger.debug('❌ Returning early - hasInputSchema but no value');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
logger.debug('✅ Validation passed, continuing with message submission...');
|
|
123
143
|
|
|
124
144
|
// Add user message
|
|
125
145
|
const displayText = hasInputSchema
|
|
@@ -134,7 +154,9 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
134
154
|
setMessages((prev) => [...prev, userMessage]);
|
|
135
155
|
setIsLoading(true);
|
|
136
156
|
|
|
157
|
+
logger.debug('🔗 baseUrl:', baseUrl);
|
|
137
158
|
if (!baseUrl) {
|
|
159
|
+
logger.debug('❌ No baseUrl configured!');
|
|
138
160
|
const errorMessage: UIMessage = {
|
|
139
161
|
id: (Date.now() + 1).toString(),
|
|
140
162
|
role: 'assistant',
|
|
@@ -164,6 +186,7 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
164
186
|
}
|
|
165
187
|
}
|
|
166
188
|
|
|
189
|
+
logger.debug('🌐 About to make API call...');
|
|
167
190
|
// Call execution endpoint with timeout
|
|
168
191
|
const headers: Record<string, string> = {
|
|
169
192
|
'Content-Type': 'application/json',
|
|
@@ -178,13 +201,15 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
178
201
|
const startTime = performance.now();
|
|
179
202
|
|
|
180
203
|
try {
|
|
204
|
+
const requestPayload = {
|
|
205
|
+
agentId: selectedAgent,
|
|
206
|
+
input: parsedInput,
|
|
207
|
+
};
|
|
208
|
+
logger.debug('📤 API Request payload:', requestPayload);
|
|
181
209
|
const response = await fetch(`${baseUrl}/_agentuity/workbench/execute`, {
|
|
182
210
|
method: 'POST',
|
|
183
211
|
headers,
|
|
184
|
-
body: JSON.stringify(
|
|
185
|
-
agentId: selectedAgent,
|
|
186
|
-
input: parsedInput,
|
|
187
|
-
}),
|
|
212
|
+
body: JSON.stringify(requestPayload),
|
|
188
213
|
signal: controller.signal,
|
|
189
214
|
});
|
|
190
215
|
clearTimeout(timeoutId);
|
|
@@ -252,6 +277,7 @@ export function WorkbenchProvider({ config, children }: WorkbenchProviderProps)
|
|
|
252
277
|
};
|
|
253
278
|
|
|
254
279
|
const handleAgentSelect = async (agentId: string) => {
|
|
280
|
+
logger.debug('🔄 handleAgentSelect called with:', agentId);
|
|
255
281
|
setSelectedAgent(agentId);
|
|
256
282
|
// No handlers configured for now
|
|
257
283
|
};
|
package/src/components.tsx
CHANGED
package/src/hooks/index.ts
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
4
|
+
|
|
5
|
+
interface Logger {
|
|
6
|
+
debug: (...args: unknown[]) => void;
|
|
7
|
+
info: (...args: unknown[]) => void;
|
|
8
|
+
warn: (...args: unknown[]) => void;
|
|
9
|
+
error: (...args: unknown[]) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const getLogLevel = (): LogLevel | null => {
|
|
13
|
+
try {
|
|
14
|
+
const level = localStorage.getItem('AGENTUITY_LOG_LEVEL');
|
|
15
|
+
if (level && ['debug', 'info', 'warn', 'error'].includes(level)) {
|
|
16
|
+
return level as LogLevel;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const shouldLog = (messageLevel: LogLevel): boolean => {
|
|
25
|
+
const currentLevel = getLogLevel();
|
|
26
|
+
if (!currentLevel) return false;
|
|
27
|
+
|
|
28
|
+
const levels: Record<LogLevel, number> = {
|
|
29
|
+
debug: 0,
|
|
30
|
+
info: 1,
|
|
31
|
+
warn: 2,
|
|
32
|
+
error: 3,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return levels[messageLevel] >= levels[currentLevel];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export function useLogger(component?: string): Logger {
|
|
39
|
+
const createLogFunction = useCallback(
|
|
40
|
+
(level: LogLevel) =>
|
|
41
|
+
(...args: unknown[]) => {
|
|
42
|
+
if (!shouldLog(level)) return;
|
|
43
|
+
|
|
44
|
+
const prefix = component ? `[${component}]` : '[Workbench]';
|
|
45
|
+
const consoleFn = console[level] || console.log;
|
|
46
|
+
consoleFn(prefix, ...args);
|
|
47
|
+
},
|
|
48
|
+
[component]
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
debug: createLogFunction('debug'),
|
|
53
|
+
info: createLogFunction('info'),
|
|
54
|
+
warn: createLogFunction('warn'),
|
|
55
|
+
error: createLogFunction('error'),
|
|
56
|
+
};
|
|
57
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -16,4 +16,5 @@ export { Input } from './components/ui/input';
|
|
|
16
16
|
// Export components
|
|
17
17
|
export { default as App } from './components/App';
|
|
18
18
|
export { default as Inline } from './components/Inline';
|
|
19
|
+
export { MonacoJsonEditor } from './components/internal/MonacoJsonEditor';
|
|
19
20
|
export { createWorkbench } from './workbench';
|
package/src/lib/utils.ts
CHANGED
|
@@ -38,3 +38,44 @@ export function parseTokensHeader(header: string): Record<string, number> {
|
|
|
38
38
|
export function getTotalTokens(tokens: Record<string, number>): number {
|
|
39
39
|
return Object.keys(tokens).reduce((sum, key) => sum + tokens[key], 0);
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
export const getProcessEnv = (key: string): string | undefined => {
|
|
43
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
44
|
+
return process.env[key];
|
|
45
|
+
}
|
|
46
|
+
if (typeof import.meta.env !== 'undefined') {
|
|
47
|
+
return import.meta.env[key];
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const buildUrl = (
|
|
53
|
+
base: string,
|
|
54
|
+
path: string,
|
|
55
|
+
subpath?: string,
|
|
56
|
+
query?: URLSearchParams
|
|
57
|
+
): string => {
|
|
58
|
+
path = path.startsWith('/') ? path : `/${path}`;
|
|
59
|
+
let url = base.replace(/\/$/, '') + path;
|
|
60
|
+
if (subpath) {
|
|
61
|
+
subpath = subpath.startsWith('/') ? subpath : `/${subpath}`;
|
|
62
|
+
url += subpath;
|
|
63
|
+
}
|
|
64
|
+
if (query) {
|
|
65
|
+
url += `?${query.toString()}`;
|
|
66
|
+
}
|
|
67
|
+
return url;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const tryOrigin = () => {
|
|
71
|
+
if (typeof window !== 'undefined') {
|
|
72
|
+
return window.location.origin;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const defaultBaseUrl: string =
|
|
77
|
+
getProcessEnv('NEXT_PUBLIC_AGENTUITY_URL') ||
|
|
78
|
+
getProcessEnv('VITE_AGENTUITY_URL') ||
|
|
79
|
+
getProcessEnv('AGENTUITY_URL') ||
|
|
80
|
+
tryOrigin() ||
|
|
81
|
+
'http://localhost:3500';
|