@agents-at-scale/ark 0.1.35 → 0.1.36-rc1
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/arkServices.d.ts +42 -0
- package/dist/arkServices.js +138 -0
- package/dist/arkServices.spec.d.ts +1 -0
- package/dist/arkServices.spec.js +24 -0
- package/dist/charts/charts.d.ts +5 -0
- package/dist/charts/charts.js +6 -0
- package/dist/charts/dependencies.d.ts +6 -0
- package/dist/charts/dependencies.js +50 -0
- package/dist/charts/types.d.ts +40 -0
- package/dist/charts/types.js +1 -0
- package/dist/commands/agents/index.d.ts +3 -0
- package/dist/commands/agents/index.js +65 -0
- package/dist/commands/agents/index.spec.d.ts +1 -0
- package/dist/commands/agents/index.spec.js +67 -0
- package/dist/commands/agents/selector.d.ts +8 -0
- package/dist/commands/agents/selector.js +53 -0
- package/dist/commands/agents.d.ts +2 -0
- package/dist/commands/agents.js +53 -0
- package/dist/commands/chat/index.d.ts +3 -0
- package/dist/commands/chat/index.js +29 -0
- package/dist/commands/chat.d.ts +2 -0
- package/dist/commands/chat.js +45 -0
- package/dist/commands/cluster/get.d.ts +2 -0
- package/dist/commands/cluster/get.js +39 -0
- package/dist/commands/cluster/get.spec.d.ts +1 -0
- package/dist/commands/cluster/get.spec.js +92 -0
- package/dist/commands/cluster/index.d.ts +2 -1
- package/dist/commands/cluster/index.js +3 -5
- package/dist/commands/cluster/index.spec.d.ts +1 -0
- package/dist/commands/cluster/index.spec.js +24 -0
- package/dist/commands/completion/index.d.ts +3 -0
- package/dist/commands/completion/index.js +230 -0
- package/dist/commands/completion/index.spec.d.ts +1 -0
- package/dist/commands/completion/index.spec.js +34 -0
- package/dist/commands/completion.js +159 -2
- package/dist/commands/config/index.d.ts +3 -0
- package/dist/commands/config/index.js +42 -0
- package/dist/commands/config/index.spec.d.ts +1 -0
- package/dist/commands/config/index.spec.js +78 -0
- package/dist/commands/config.d.ts +0 -3
- package/dist/commands/config.js +38 -321
- package/dist/commands/dashboard/index.d.ts +4 -0
- package/dist/commands/dashboard/index.js +39 -0
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.js +39 -0
- package/dist/commands/dev/index.d.ts +3 -0
- package/dist/commands/dev/index.js +9 -0
- package/dist/commands/dev/tool/check.d.ts +2 -0
- package/dist/commands/dev/tool/check.js +142 -0
- package/dist/commands/dev/tool/clean.d.ts +2 -0
- package/dist/commands/dev/tool/clean.js +153 -0
- package/dist/commands/dev/tool/generate.d.ts +2 -0
- package/dist/commands/dev/tool/generate.js +28 -0
- package/dist/commands/dev/tool/index.d.ts +2 -0
- package/dist/commands/dev/tool/index.js +14 -0
- package/dist/commands/dev/tool/init.d.ts +2 -0
- package/dist/commands/dev/tool/init.js +320 -0
- package/dist/commands/dev/tool/shared.d.ts +5 -0
- package/dist/commands/dev/tool/shared.js +258 -0
- package/dist/commands/dev/tool/status.d.ts +2 -0
- package/dist/commands/dev/tool/status.js +136 -0
- package/dist/commands/dev/tool-generate.spec.d.ts +1 -0
- package/dist/commands/dev/tool-generate.spec.js +163 -0
- package/dist/commands/dev/tool.d.ts +2 -0
- package/dist/commands/dev/tool.js +559 -0
- package/dist/commands/dev/tool.spec.d.ts +1 -0
- package/dist/commands/dev/tool.spec.js +48 -0
- package/dist/commands/docs/index.d.ts +4 -0
- package/dist/commands/docs/index.js +18 -0
- package/dist/commands/generate/config.js +5 -24
- package/dist/commands/generate/generators/mcpserver.d.ts +2 -1
- package/dist/commands/generate/generators/mcpserver.js +26 -5
- package/dist/commands/generate/generators/project.js +22 -41
- package/dist/commands/generate/index.d.ts +2 -1
- package/dist/commands/generate/index.js +1 -1
- package/dist/commands/install/index.d.ts +8 -0
- package/dist/commands/install/index.js +295 -0
- package/dist/commands/install/index.spec.d.ts +1 -0
- package/dist/commands/install/index.spec.js +143 -0
- package/dist/commands/install.d.ts +3 -0
- package/dist/commands/install.js +147 -0
- package/dist/commands/models/create.d.ts +1 -0
- package/dist/commands/models/create.js +213 -0
- package/dist/commands/models/create.spec.d.ts +1 -0
- package/dist/commands/models/create.spec.js +125 -0
- package/dist/commands/models/index.d.ts +3 -0
- package/dist/commands/models/index.js +75 -0
- package/dist/commands/models/index.spec.d.ts +1 -0
- package/dist/commands/models/index.spec.js +96 -0
- package/dist/commands/models/selector.d.ts +8 -0
- package/dist/commands/models/selector.js +53 -0
- package/dist/commands/query/index.d.ts +3 -0
- package/dist/commands/query/index.js +24 -0
- package/dist/commands/query/index.spec.d.ts +1 -0
- package/dist/commands/query/index.spec.js +53 -0
- package/dist/commands/routes/index.d.ts +3 -0
- package/dist/commands/routes/index.js +93 -0
- package/dist/commands/routes.d.ts +2 -0
- package/dist/commands/routes.js +101 -0
- package/dist/commands/status/index.d.ts +3 -0
- package/dist/commands/status/index.js +281 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.js +33 -0
- package/dist/commands/targets/index.d.ts +3 -0
- package/dist/commands/targets/index.js +72 -0
- package/dist/commands/targets/index.spec.d.ts +1 -0
- package/dist/commands/targets/index.spec.js +154 -0
- package/dist/commands/targets.d.ts +2 -0
- package/dist/commands/targets.js +65 -0
- package/dist/commands/teams/index.d.ts +3 -0
- package/dist/commands/teams/index.js +64 -0
- package/dist/commands/teams/index.spec.d.ts +1 -0
- package/dist/commands/teams/index.spec.js +70 -0
- package/dist/commands/teams/selector.d.ts +8 -0
- package/dist/commands/teams/selector.js +55 -0
- package/dist/commands/tools/index.d.ts +3 -0
- package/dist/commands/tools/index.js +49 -0
- package/dist/commands/tools/index.spec.d.ts +1 -0
- package/dist/commands/tools/index.spec.js +70 -0
- package/dist/commands/tools/selector.d.ts +8 -0
- package/dist/commands/tools/selector.js +53 -0
- package/dist/commands/uninstall/index.d.ts +3 -0
- package/dist/commands/uninstall/index.js +101 -0
- package/dist/commands/uninstall/index.spec.d.ts +1 -0
- package/dist/commands/uninstall/index.spec.js +125 -0
- package/dist/commands/uninstall.d.ts +2 -0
- package/dist/commands/uninstall.js +83 -0
- package/dist/components/ChatUI.d.ts +16 -0
- package/dist/components/ChatUI.js +801 -0
- package/dist/components/StatusView.d.ts +10 -0
- package/dist/components/StatusView.js +39 -0
- package/dist/components/statusChecker.d.ts +14 -24
- package/dist/components/statusChecker.js +295 -129
- package/dist/config.d.ts +3 -22
- package/dist/config.js +10 -161
- package/dist/index.d.ts +1 -1
- package/dist/index.js +42 -42
- package/dist/lib/arkApiClient.d.ts +53 -0
- package/dist/lib/arkApiClient.js +102 -0
- package/dist/lib/arkApiProxy.d.ts +9 -0
- package/dist/lib/arkApiProxy.js +22 -0
- package/dist/lib/arkServiceProxy.d.ts +14 -0
- package/dist/lib/arkServiceProxy.js +95 -0
- package/dist/lib/arkStatus.d.ts +10 -0
- package/dist/lib/arkStatus.js +79 -0
- package/dist/lib/arkStatus.spec.d.ts +1 -0
- package/dist/lib/arkStatus.spec.js +49 -0
- package/dist/lib/chatClient.d.ts +33 -0
- package/dist/lib/chatClient.js +93 -0
- package/dist/lib/cluster.d.ts +2 -1
- package/dist/lib/cluster.js +37 -16
- package/dist/lib/cluster.spec.d.ts +1 -0
- package/dist/lib/cluster.spec.js +338 -0
- package/dist/lib/commandUtils.d.ts +4 -0
- package/dist/lib/commandUtils.js +18 -0
- package/dist/lib/commandUtils.test.d.ts +1 -0
- package/dist/lib/commandUtils.test.js +44 -0
- package/dist/lib/commands.d.ts +16 -0
- package/dist/lib/commands.js +29 -0
- package/dist/lib/commands.spec.d.ts +1 -0
- package/dist/lib/commands.spec.js +146 -0
- package/dist/lib/config.d.ts +26 -80
- package/dist/lib/config.js +70 -205
- package/dist/lib/config.spec.d.ts +1 -0
- package/dist/lib/config.spec.js +99 -0
- package/dist/lib/config.test.d.ts +1 -0
- package/dist/lib/config.test.js +93 -0
- package/dist/lib/consts.d.ts +0 -1
- package/dist/lib/consts.js +0 -2
- package/dist/lib/consts.spec.d.ts +1 -0
- package/dist/lib/consts.spec.js +15 -0
- package/dist/lib/dev/tools/analyzer.d.ts +30 -0
- package/dist/lib/dev/tools/analyzer.js +190 -0
- package/dist/lib/dev/tools/discover_tools.py +392 -0
- package/dist/lib/dev/tools/mcp-types.d.ts +28 -0
- package/dist/lib/dev/tools/mcp-types.js +86 -0
- package/dist/lib/dev/tools/types.d.ts +50 -0
- package/dist/lib/dev/tools/types.js +1 -0
- package/dist/lib/errors.js +1 -1
- package/dist/lib/errors.spec.d.ts +1 -0
- package/dist/lib/errors.spec.js +221 -0
- package/dist/lib/exec.d.ts +0 -4
- package/dist/lib/exec.js +0 -11
- package/dist/lib/executeQuery.d.ts +20 -0
- package/dist/lib/executeQuery.js +135 -0
- package/dist/lib/executeQuery.spec.d.ts +1 -0
- package/dist/lib/executeQuery.spec.js +170 -0
- package/dist/lib/nextSteps.d.ts +4 -0
- package/dist/lib/nextSteps.js +20 -0
- package/dist/lib/nextSteps.spec.d.ts +1 -0
- package/dist/lib/nextSteps.spec.js +59 -0
- package/dist/lib/output.d.ts +36 -0
- package/dist/lib/output.js +89 -0
- package/dist/lib/output.spec.d.ts +1 -0
- package/dist/lib/output.spec.js +123 -0
- package/dist/lib/portUtils.d.ts +8 -0
- package/dist/lib/portUtils.js +39 -0
- package/dist/lib/queryRunner.d.ts +22 -0
- package/dist/lib/queryRunner.js +142 -0
- package/dist/lib/startup.d.ts +9 -0
- package/dist/lib/startup.js +87 -0
- package/dist/lib/startup.spec.d.ts +1 -0
- package/dist/lib/startup.spec.js +152 -0
- package/dist/lib/types.d.ts +87 -3
- package/dist/lib/versions.d.ts +23 -0
- package/dist/lib/versions.js +51 -0
- package/dist/types/types.d.ts +40 -0
- package/dist/types/types.js +1 -0
- package/dist/ui/AgentSelector.d.ts +8 -0
- package/dist/ui/AgentSelector.js +53 -0
- package/dist/ui/MainMenu.d.ts +5 -1
- package/dist/ui/MainMenu.js +226 -91
- package/dist/ui/ModelSelector.d.ts +8 -0
- package/dist/ui/ModelSelector.js +53 -0
- package/dist/ui/TeamSelector.d.ts +8 -0
- package/dist/ui/TeamSelector.js +55 -0
- package/dist/ui/ToolSelector.d.ts +8 -0
- package/dist/ui/ToolSelector.js +53 -0
- package/dist/ui/statusFormatter.d.ts +22 -7
- package/dist/ui/statusFormatter.js +39 -39
- package/dist/ui/statusFormatter.spec.d.ts +1 -0
- package/dist/ui/statusFormatter.spec.js +58 -0
- package/package.json +16 -5
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput, useApp } from 'ink';
|
|
3
|
+
import TextInput from 'ink-text-input';
|
|
4
|
+
import Spinner from 'ink-spinner';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
import { marked } from 'marked';
|
|
8
|
+
// @ts-ignore - no types available
|
|
9
|
+
import TerminalRenderer from 'marked-terminal';
|
|
10
|
+
import { ChatClient, } from '../lib/chatClient.js';
|
|
11
|
+
import { AgentSelector } from '../ui/AgentSelector.js';
|
|
12
|
+
import { ModelSelector } from '../ui/ModelSelector.js';
|
|
13
|
+
import { TeamSelector } from '../ui/TeamSelector.js';
|
|
14
|
+
import { ToolSelector } from '../ui/ToolSelector.js';
|
|
15
|
+
// Generate a unique ID for messages
|
|
16
|
+
let messageIdCounter = 0;
|
|
17
|
+
const generateMessageId = () => {
|
|
18
|
+
return `msg-${Date.now()}-${messageIdCounter++}`;
|
|
19
|
+
};
|
|
20
|
+
// Configure marked with terminal renderer for markdown output
|
|
21
|
+
const configureMarkdown = () => {
|
|
22
|
+
marked.setOptions({
|
|
23
|
+
renderer: new TerminalRenderer({
|
|
24
|
+
showSectionPrefix: false,
|
|
25
|
+
width: 80,
|
|
26
|
+
reflowText: true,
|
|
27
|
+
preserveNewlines: true,
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const ChatUI = ({ initialTargetId, arkApiClient, arkApiProxy, config, }) => {
|
|
32
|
+
const { exit } = useApp();
|
|
33
|
+
const [messages, setMessages] = React.useState([]);
|
|
34
|
+
const [input, setInput] = React.useState('');
|
|
35
|
+
const [isTyping, setIsTyping] = React.useState(false);
|
|
36
|
+
const [target, setTarget] = React.useState(null);
|
|
37
|
+
const [availableTargets, setAvailableTargets] = React.useState([]);
|
|
38
|
+
const [error, setError] = React.useState(null);
|
|
39
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
40
|
+
const [targetIndex, setTargetIndex] = React.useState(0);
|
|
41
|
+
const [abortController, setAbortController] = React.useState(null);
|
|
42
|
+
const [showCommands, setShowCommands] = React.useState(false);
|
|
43
|
+
const [filteredCommands, setFilteredCommands] = React.useState([]);
|
|
44
|
+
const [inputKey, setInputKey] = React.useState(0); // Key to force re-mount of TextInput
|
|
45
|
+
const [outputFormat, setOutputFormat] = React.useState(config?.chat?.outputFormat || 'text');
|
|
46
|
+
const [showAgentSelector, setShowAgentSelector] = React.useState(false);
|
|
47
|
+
const [showModelSelector, setShowModelSelector] = React.useState(false);
|
|
48
|
+
const [showTeamSelector, setShowTeamSelector] = React.useState(false);
|
|
49
|
+
const [showToolSelector, setShowToolSelector] = React.useState(false);
|
|
50
|
+
// Message history navigation
|
|
51
|
+
const [messageHistory, setMessageHistory] = React.useState([]);
|
|
52
|
+
const [historyIndex, setHistoryIndex] = React.useState(-1);
|
|
53
|
+
// Initialize chat config from config prop
|
|
54
|
+
const [chatConfig, setChatConfig] = React.useState({
|
|
55
|
+
streamingEnabled: config?.chat?.streaming ?? true,
|
|
56
|
+
currentTarget: undefined,
|
|
57
|
+
});
|
|
58
|
+
const chatClientRef = React.useRef(undefined);
|
|
59
|
+
// Configure markdown when output format changes
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
if (outputFormat === 'markdown') {
|
|
62
|
+
configureMarkdown();
|
|
63
|
+
}
|
|
64
|
+
}, [outputFormat]);
|
|
65
|
+
// Initialize chat client and fetch targets on mount
|
|
66
|
+
React.useEffect(() => {
|
|
67
|
+
const initializeChat = async () => {
|
|
68
|
+
try {
|
|
69
|
+
// Use the provided ArkApiClient to create ChatClient
|
|
70
|
+
const client = new ChatClient(arkApiClient);
|
|
71
|
+
chatClientRef.current = client;
|
|
72
|
+
const targets = await client.getQueryTargets();
|
|
73
|
+
setAvailableTargets(targets);
|
|
74
|
+
if (initialTargetId) {
|
|
75
|
+
// If initialTargetId is provided, find and set the target
|
|
76
|
+
const matchedTarget = targets.find((t) => t.id === initialTargetId);
|
|
77
|
+
const matchedIndex = targets.findIndex((t) => t.id === initialTargetId);
|
|
78
|
+
if (matchedTarget) {
|
|
79
|
+
setTarget(matchedTarget);
|
|
80
|
+
setTargetIndex(matchedIndex >= 0 ? matchedIndex : 0);
|
|
81
|
+
setChatConfig((prev) => ({ ...prev, currentTarget: matchedTarget }));
|
|
82
|
+
setMessages([]);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// If target not found, show error and exit
|
|
86
|
+
console.error(chalk.red('Error:'), `Target "${initialTargetId}" not found`);
|
|
87
|
+
console.error(chalk.gray('Use "ark targets list" to see available targets'));
|
|
88
|
+
if (arkApiProxy) {
|
|
89
|
+
arkApiProxy.stop();
|
|
90
|
+
}
|
|
91
|
+
exit();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else if (targets.length > 0) {
|
|
95
|
+
// No initial target specified - auto-select first available
|
|
96
|
+
// Priority: agents > models > tools
|
|
97
|
+
const agents = targets.filter((t) => t.type === 'agent');
|
|
98
|
+
const models = targets.filter((t) => t.type === 'model');
|
|
99
|
+
const tools = targets.filter((t) => t.type === 'tool');
|
|
100
|
+
let selectedTarget = null;
|
|
101
|
+
let selectedIndex = 0;
|
|
102
|
+
if (agents.length > 0) {
|
|
103
|
+
selectedTarget = agents[0];
|
|
104
|
+
selectedIndex = targets.findIndex((t) => t.id === agents[0].id);
|
|
105
|
+
}
|
|
106
|
+
else if (models.length > 0) {
|
|
107
|
+
selectedTarget = models[0];
|
|
108
|
+
selectedIndex = targets.findIndex((t) => t.id === models[0].id);
|
|
109
|
+
}
|
|
110
|
+
else if (tools.length > 0) {
|
|
111
|
+
selectedTarget = tools[0];
|
|
112
|
+
selectedIndex = targets.findIndex((t) => t.id === tools[0].id);
|
|
113
|
+
}
|
|
114
|
+
if (selectedTarget) {
|
|
115
|
+
setTarget(selectedTarget);
|
|
116
|
+
setTargetIndex(selectedIndex);
|
|
117
|
+
setChatConfig((prev) => ({ ...prev, currentTarget: selectedTarget }));
|
|
118
|
+
setMessages([]);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
setError('No targets available');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
setError('No agents, models, or tools available');
|
|
126
|
+
}
|
|
127
|
+
setIsLoading(false);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
const errorMessage = err instanceof Error ? err.message : 'Failed to initialize chat';
|
|
131
|
+
console.error(chalk.red('Error:'), errorMessage);
|
|
132
|
+
if (arkApiProxy) {
|
|
133
|
+
arkApiProxy.stop();
|
|
134
|
+
}
|
|
135
|
+
exit();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
initializeChat();
|
|
139
|
+
// Cleanup function to close port forward when component unmounts
|
|
140
|
+
return () => {
|
|
141
|
+
if (arkApiProxy) {
|
|
142
|
+
arkApiProxy.stop();
|
|
143
|
+
}
|
|
144
|
+
chatClientRef.current = undefined;
|
|
145
|
+
};
|
|
146
|
+
}, [initialTargetId]);
|
|
147
|
+
// Handle keyboard input
|
|
148
|
+
useInput((inputChar, key) => {
|
|
149
|
+
// Handle Ctrl+C to exit cleanly
|
|
150
|
+
if (inputChar === '\x03' || (key.ctrl && inputChar === 'c')) {
|
|
151
|
+
// Clean up resources
|
|
152
|
+
if (arkApiProxy) {
|
|
153
|
+
arkApiProxy.stop();
|
|
154
|
+
}
|
|
155
|
+
if (abortController) {
|
|
156
|
+
abortController.abort();
|
|
157
|
+
}
|
|
158
|
+
// Exit the app properly
|
|
159
|
+
exit();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
// Note: Ctrl+W for word deletion doesn't work reliably due to terminal/readline
|
|
163
|
+
// intercepting it before it reaches the app. Most terminals handle this at a lower level.
|
|
164
|
+
// Handle arrow keys for message history navigation
|
|
165
|
+
if (!showCommands && messageHistory.length > 0) {
|
|
166
|
+
if (key.upArrow && input === '') {
|
|
167
|
+
// Go back in history
|
|
168
|
+
const newIndex = historyIndex === -1
|
|
169
|
+
? messageHistory.length - 1
|
|
170
|
+
: Math.max(0, historyIndex - 1);
|
|
171
|
+
if (newIndex >= 0 && newIndex < messageHistory.length) {
|
|
172
|
+
setHistoryIndex(newIndex);
|
|
173
|
+
setInput(messageHistory[newIndex]);
|
|
174
|
+
setInputKey((prev) => prev + 1); // Force re-mount to update cursor
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (key.downArrow && input === '') {
|
|
179
|
+
// Go forward in history
|
|
180
|
+
if (historyIndex >= 0) {
|
|
181
|
+
const newIndex = Math.min(messageHistory.length - 1, historyIndex + 1);
|
|
182
|
+
if (newIndex === messageHistory.length - 1) {
|
|
183
|
+
// At the end of history, clear input
|
|
184
|
+
setHistoryIndex(-1);
|
|
185
|
+
setInput('');
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
setHistoryIndex(newIndex);
|
|
189
|
+
setInput(messageHistory[newIndex]);
|
|
190
|
+
}
|
|
191
|
+
setInputKey((prev) => prev + 1); // Force re-mount to update cursor
|
|
192
|
+
}
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Tab to autocomplete when there's a single matching command
|
|
197
|
+
if (key.tab &&
|
|
198
|
+
!key.shift &&
|
|
199
|
+
showCommands &&
|
|
200
|
+
filteredCommands.length === 1) {
|
|
201
|
+
// Set the completed command with a space at the end
|
|
202
|
+
const completedCommand = filteredCommands[0].command + ' ';
|
|
203
|
+
setInput(completedCommand);
|
|
204
|
+
// Keep the command hint visible but update to show only the completed command
|
|
205
|
+
setFilteredCommands([filteredCommands[0]]);
|
|
206
|
+
// Force re-mount of TextInput to reset cursor position
|
|
207
|
+
setInputKey((prev) => prev + 1);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// Shift+Tab to cycle through targets
|
|
211
|
+
if (key.shift && key.tab && availableTargets.length > 0) {
|
|
212
|
+
// Cycle to next target
|
|
213
|
+
const nextIndex = (targetIndex + 1) % availableTargets.length;
|
|
214
|
+
const nextTarget = availableTargets[nextIndex];
|
|
215
|
+
setTargetIndex(nextIndex);
|
|
216
|
+
setTarget(nextTarget);
|
|
217
|
+
setChatConfig((prev) => ({ ...prev, currentTarget: nextTarget }));
|
|
218
|
+
}
|
|
219
|
+
// Esc to cancel current request
|
|
220
|
+
if (key.escape && isTyping && abortController) {
|
|
221
|
+
abortController.abort();
|
|
222
|
+
setAbortController(null);
|
|
223
|
+
setIsTyping(false);
|
|
224
|
+
// Mark the agent/team message as cancelled and add system message
|
|
225
|
+
setMessages((prev) => {
|
|
226
|
+
const newMessages = [...prev];
|
|
227
|
+
const lastMessage = newMessages[newMessages.length - 1];
|
|
228
|
+
if (lastMessage &&
|
|
229
|
+
(lastMessage.type === 'agent' || lastMessage.type === 'team')) {
|
|
230
|
+
lastMessage.cancelled = true;
|
|
231
|
+
// Remove the message if it has no content
|
|
232
|
+
if (lastMessage.type === 'agent' && !lastMessage.content) {
|
|
233
|
+
newMessages.pop();
|
|
234
|
+
}
|
|
235
|
+
else if (lastMessage.type === 'team' &&
|
|
236
|
+
lastMessage.members.length === 0) {
|
|
237
|
+
newMessages.pop();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Add system message about interruption
|
|
241
|
+
const systemMessage = {
|
|
242
|
+
id: generateMessageId(),
|
|
243
|
+
type: 'system',
|
|
244
|
+
content: 'Interrupted by user',
|
|
245
|
+
timestamp: new Date(),
|
|
246
|
+
};
|
|
247
|
+
newMessages.push(systemMessage);
|
|
248
|
+
return newMessages;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
const handleSubmit = async (value) => {
|
|
253
|
+
if (!value.trim())
|
|
254
|
+
return;
|
|
255
|
+
// Check for slash commands first (these work without a target)
|
|
256
|
+
if (value.startsWith('/output')) {
|
|
257
|
+
const parts = value.split(' ');
|
|
258
|
+
const arg = parts[1]?.toLowerCase();
|
|
259
|
+
if (arg === 'text' || arg === 'markdown') {
|
|
260
|
+
// Set output format
|
|
261
|
+
setOutputFormat(arg);
|
|
262
|
+
// Add system message to show the change
|
|
263
|
+
const systemMessage = {
|
|
264
|
+
id: generateMessageId(),
|
|
265
|
+
type: 'system',
|
|
266
|
+
content: `Output format set to ${arg}`,
|
|
267
|
+
timestamp: new Date(),
|
|
268
|
+
command: '/output',
|
|
269
|
+
};
|
|
270
|
+
setMessages((prev) => [...prev, systemMessage]);
|
|
271
|
+
}
|
|
272
|
+
else if (!arg) {
|
|
273
|
+
// Show current format
|
|
274
|
+
const systemMessage = {
|
|
275
|
+
id: generateMessageId(),
|
|
276
|
+
type: 'system',
|
|
277
|
+
content: `Current output format: ${outputFormat}`,
|
|
278
|
+
timestamp: new Date(),
|
|
279
|
+
command: '/output',
|
|
280
|
+
};
|
|
281
|
+
setMessages((prev) => [...prev, systemMessage]);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
// Show usage message
|
|
285
|
+
const systemMessage = {
|
|
286
|
+
id: generateMessageId(),
|
|
287
|
+
type: 'system',
|
|
288
|
+
content: `Use 'text' or 'markdown' e.g. /output markdown`,
|
|
289
|
+
timestamp: new Date(),
|
|
290
|
+
command: '/output',
|
|
291
|
+
};
|
|
292
|
+
setMessages((prev) => [...prev, systemMessage]);
|
|
293
|
+
}
|
|
294
|
+
setInput('');
|
|
295
|
+
setShowCommands(false);
|
|
296
|
+
setFilteredCommands([]);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (value.startsWith('/streaming')) {
|
|
300
|
+
const parts = value.split(' ');
|
|
301
|
+
const arg = parts[1]?.toLowerCase();
|
|
302
|
+
if (arg === 'on' || arg === 'off') {
|
|
303
|
+
// Set streaming based on argument
|
|
304
|
+
const newState = arg === 'on';
|
|
305
|
+
setChatConfig((prev) => ({ ...prev, streamingEnabled: newState }));
|
|
306
|
+
// Add system message to show the change
|
|
307
|
+
const systemMessage = {
|
|
308
|
+
id: generateMessageId(),
|
|
309
|
+
type: 'system',
|
|
310
|
+
content: `Streaming ${newState ? 'enabled' : 'disabled'}`,
|
|
311
|
+
timestamp: new Date(),
|
|
312
|
+
command: '/streaming',
|
|
313
|
+
};
|
|
314
|
+
setMessages((prev) => [...prev, systemMessage]);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
// Show usage message
|
|
318
|
+
const systemMessage = {
|
|
319
|
+
id: generateMessageId(),
|
|
320
|
+
type: 'system',
|
|
321
|
+
content: `Use either 'on' or 'off' e.g. /streaming on`,
|
|
322
|
+
timestamp: new Date(),
|
|
323
|
+
command: '/streaming',
|
|
324
|
+
};
|
|
325
|
+
setMessages((prev) => [...prev, systemMessage]);
|
|
326
|
+
}
|
|
327
|
+
setInput('');
|
|
328
|
+
setShowCommands(false);
|
|
329
|
+
setFilteredCommands([]);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (value.startsWith('/reset')) {
|
|
333
|
+
// Clear all messages
|
|
334
|
+
setMessages([]);
|
|
335
|
+
// Add system message to show the reset
|
|
336
|
+
const systemMessage = {
|
|
337
|
+
id: generateMessageId(),
|
|
338
|
+
type: 'system',
|
|
339
|
+
content: 'Message history cleared',
|
|
340
|
+
timestamp: new Date(),
|
|
341
|
+
command: '/reset',
|
|
342
|
+
};
|
|
343
|
+
setMessages([systemMessage]);
|
|
344
|
+
// Clear message history for arrow key navigation
|
|
345
|
+
setMessageHistory([]);
|
|
346
|
+
setHistoryIndex(-1);
|
|
347
|
+
setInput('');
|
|
348
|
+
setShowCommands(false);
|
|
349
|
+
setFilteredCommands([]);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (value.startsWith('/agents')) {
|
|
353
|
+
setShowAgentSelector(true);
|
|
354
|
+
setInput('');
|
|
355
|
+
setShowCommands(false);
|
|
356
|
+
setFilteredCommands([]);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (value.startsWith('/models')) {
|
|
360
|
+
setShowModelSelector(true);
|
|
361
|
+
setInput('');
|
|
362
|
+
setShowCommands(false);
|
|
363
|
+
setFilteredCommands([]);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (value.startsWith('/teams')) {
|
|
367
|
+
setShowTeamSelector(true);
|
|
368
|
+
setInput('');
|
|
369
|
+
setShowCommands(false);
|
|
370
|
+
setFilteredCommands([]);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (value.startsWith('/tools')) {
|
|
374
|
+
setShowToolSelector(true);
|
|
375
|
+
setInput('');
|
|
376
|
+
setShowCommands(false);
|
|
377
|
+
setFilteredCommands([]);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
// For regular messages, we need a target and client
|
|
381
|
+
if (!target || !chatClientRef.current) {
|
|
382
|
+
const systemMessage = {
|
|
383
|
+
id: generateMessageId(),
|
|
384
|
+
type: 'system',
|
|
385
|
+
content: 'No target selected. Use Shift+Tab to select a target.',
|
|
386
|
+
timestamp: new Date(),
|
|
387
|
+
};
|
|
388
|
+
setMessages((prev) => [...prev, systemMessage]);
|
|
389
|
+
setInput('');
|
|
390
|
+
setShowCommands(false);
|
|
391
|
+
setFilteredCommands([]);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
const userMessage = {
|
|
395
|
+
id: generateMessageId(),
|
|
396
|
+
type: 'user',
|
|
397
|
+
content: value,
|
|
398
|
+
timestamp: new Date(),
|
|
399
|
+
};
|
|
400
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
401
|
+
// Add to message history
|
|
402
|
+
setMessageHistory((prev) => [...prev, value]);
|
|
403
|
+
setHistoryIndex(-1); // Reset history navigation
|
|
404
|
+
setInput('');
|
|
405
|
+
setIsTyping(true);
|
|
406
|
+
setError(null);
|
|
407
|
+
try {
|
|
408
|
+
// Create abort controller for this request
|
|
409
|
+
const controller = new AbortController();
|
|
410
|
+
setAbortController(controller);
|
|
411
|
+
// Convert messages to format expected by OpenAI API - only include user and agent messages
|
|
412
|
+
const apiMessages = messages
|
|
413
|
+
.filter((msg) => msg.type === 'user' || msg.type === 'agent' || msg.type === 'team')
|
|
414
|
+
.map((msg) => {
|
|
415
|
+
if (msg.type === 'user') {
|
|
416
|
+
return {
|
|
417
|
+
role: 'user',
|
|
418
|
+
content: msg.content,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
else if (msg.type === 'agent') {
|
|
422
|
+
return {
|
|
423
|
+
role: 'assistant',
|
|
424
|
+
content: msg.content,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
else if (msg.type === 'team') {
|
|
428
|
+
// For teams, concatenate all member responses
|
|
429
|
+
const content = msg.members.map((m) => m.content).join(' ');
|
|
430
|
+
return {
|
|
431
|
+
role: 'assistant',
|
|
432
|
+
content: content || '',
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
return { role: 'user', content: '' };
|
|
436
|
+
});
|
|
437
|
+
// Add the new user message
|
|
438
|
+
apiMessages.push({
|
|
439
|
+
role: 'user',
|
|
440
|
+
content: value,
|
|
441
|
+
});
|
|
442
|
+
// Add a placeholder message based on target type
|
|
443
|
+
const messageId = generateMessageId();
|
|
444
|
+
if (target.type === 'team') {
|
|
445
|
+
// For teams, create a TeamMessage
|
|
446
|
+
const teamMessage = {
|
|
447
|
+
id: messageId,
|
|
448
|
+
type: 'team',
|
|
449
|
+
targetName: target.name,
|
|
450
|
+
timestamp: new Date(),
|
|
451
|
+
members: [],
|
|
452
|
+
};
|
|
453
|
+
setMessages((prev) => [...prev, teamMessage]);
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
// For agents/models, create an AgentMessage
|
|
457
|
+
const agentMessage = {
|
|
458
|
+
id: messageId,
|
|
459
|
+
type: 'agent',
|
|
460
|
+
targetName: target.name,
|
|
461
|
+
timestamp: new Date(),
|
|
462
|
+
content: '',
|
|
463
|
+
};
|
|
464
|
+
setMessages((prev) => [...prev, agentMessage]);
|
|
465
|
+
}
|
|
466
|
+
// Send message and get response with abort signal
|
|
467
|
+
const fullResponse = await chatClientRef.current.sendMessage(target.id, apiMessages, chatConfig, (chunk, toolCalls, arkMetadata) => {
|
|
468
|
+
// Update message progressively as chunks arrive
|
|
469
|
+
setMessages((prev) => {
|
|
470
|
+
const newMessages = [...prev];
|
|
471
|
+
const lastMessage = newMessages[newMessages.length - 1];
|
|
472
|
+
// Only update if not cancelled
|
|
473
|
+
if (lastMessage && !lastMessage.cancelled) {
|
|
474
|
+
if (lastMessage.type === 'team' && arkMetadata?.agent) {
|
|
475
|
+
// Handle team messages with agent metadata
|
|
476
|
+
const teamMsg = lastMessage;
|
|
477
|
+
// Find or create team member entry
|
|
478
|
+
let member = teamMsg.members.find((m) => m.agentName === arkMetadata.agent);
|
|
479
|
+
if (!member) {
|
|
480
|
+
member = {
|
|
481
|
+
agentName: arkMetadata.agent,
|
|
482
|
+
content: '',
|
|
483
|
+
color: 'blueBright',
|
|
484
|
+
};
|
|
485
|
+
teamMsg.members.push(member);
|
|
486
|
+
}
|
|
487
|
+
// Update member's content or tool calls
|
|
488
|
+
if (chunk) {
|
|
489
|
+
member.content += chunk;
|
|
490
|
+
}
|
|
491
|
+
if (toolCalls) {
|
|
492
|
+
member.toolCalls = toolCalls;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
else if (lastMessage.type === 'agent') {
|
|
496
|
+
// Handle regular agent messages
|
|
497
|
+
const agentMsg = lastMessage;
|
|
498
|
+
if (chunk) {
|
|
499
|
+
agentMsg.content += chunk;
|
|
500
|
+
}
|
|
501
|
+
if (toolCalls) {
|
|
502
|
+
agentMsg.toolCalls = toolCalls;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return newMessages;
|
|
507
|
+
});
|
|
508
|
+
}, controller.signal);
|
|
509
|
+
// For non-streaming responses or final validation
|
|
510
|
+
setMessages((prev) => {
|
|
511
|
+
const newMessages = [...prev];
|
|
512
|
+
const lastMessage = newMessages[newMessages.length - 1];
|
|
513
|
+
// Only update if not cancelled
|
|
514
|
+
if (lastMessage && !lastMessage.cancelled) {
|
|
515
|
+
if (lastMessage.type === 'agent') {
|
|
516
|
+
const agentMsg = lastMessage;
|
|
517
|
+
// If content is empty (no streaming occurred), set the full response
|
|
518
|
+
if (!agentMsg.content && fullResponse) {
|
|
519
|
+
agentMsg.content = fullResponse;
|
|
520
|
+
}
|
|
521
|
+
// If no content at all, show a default message
|
|
522
|
+
if (!agentMsg.content && !agentMsg.toolCalls) {
|
|
523
|
+
agentMsg.content = 'No response received';
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
else if (lastMessage.type === 'team') {
|
|
527
|
+
const teamMsg = lastMessage;
|
|
528
|
+
// For teams in non-streaming mode, add the full response as a single member
|
|
529
|
+
if (!chatConfig.streamingEnabled &&
|
|
530
|
+
fullResponse &&
|
|
531
|
+
teamMsg.members.length === 0) {
|
|
532
|
+
teamMsg.members.push({
|
|
533
|
+
agentName: 'team',
|
|
534
|
+
content: fullResponse,
|
|
535
|
+
color: 'blueBright',
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return newMessages;
|
|
541
|
+
});
|
|
542
|
+
setIsTyping(false);
|
|
543
|
+
setAbortController(null);
|
|
544
|
+
}
|
|
545
|
+
catch (err) {
|
|
546
|
+
// Check if this was cancelled by user
|
|
547
|
+
if (err instanceof Error && err.name === 'AbortError') {
|
|
548
|
+
// Request was cancelled, message already updated by Esc handler
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
const errorMessage = err instanceof Error ? err.message : 'Failed to send message';
|
|
552
|
+
setError(errorMessage);
|
|
553
|
+
setIsTyping(false);
|
|
554
|
+
setAbortController(null);
|
|
555
|
+
// Update the agent/team message with the error
|
|
556
|
+
setMessages((prev) => {
|
|
557
|
+
const newMessages = [...prev];
|
|
558
|
+
const lastMessage = newMessages[newMessages.length - 1];
|
|
559
|
+
if (lastMessage && !lastMessage.cancelled) {
|
|
560
|
+
if (lastMessage.type === 'agent') {
|
|
561
|
+
lastMessage.content = `Error: ${errorMessage}`;
|
|
562
|
+
}
|
|
563
|
+
else if (lastMessage.type === 'team') {
|
|
564
|
+
// For team messages, add error as a single member
|
|
565
|
+
lastMessage.members = [
|
|
566
|
+
{
|
|
567
|
+
agentName: 'error',
|
|
568
|
+
content: `Error: ${errorMessage}`,
|
|
569
|
+
color: 'red',
|
|
570
|
+
},
|
|
571
|
+
];
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return newMessages;
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
const renderMessage = (msg, index) => {
|
|
579
|
+
const isLastMessage = index === messages.length - 1;
|
|
580
|
+
const isCurrentlyTyping = isTyping && isLastMessage;
|
|
581
|
+
const isCancelled = msg.cancelled === true;
|
|
582
|
+
// Handle different message types
|
|
583
|
+
switch (msg.type) {
|
|
584
|
+
case 'system':
|
|
585
|
+
return renderSystemMessage(msg, index);
|
|
586
|
+
case 'user':
|
|
587
|
+
return renderUserMessage(msg, index);
|
|
588
|
+
case 'agent':
|
|
589
|
+
return renderAgentMessage(msg, index, isCurrentlyTyping, isCancelled);
|
|
590
|
+
case 'team':
|
|
591
|
+
return renderTeamMessage(msg, index, isCurrentlyTyping, isCancelled);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
const renderSystemMessage = (msg, index) => {
|
|
595
|
+
const isInterruption = msg.content === 'Interrupted by user';
|
|
596
|
+
// If it's a slash command response, show with special formatting
|
|
597
|
+
if (msg.command) {
|
|
598
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: "gray", children: ["\u203A ", msg.command] }) }), _jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: "gray", children: ["\u23BF ", msg.content] }) })] }, index));
|
|
599
|
+
}
|
|
600
|
+
// For other system messages (interruptions, errors, etc.)
|
|
601
|
+
const color = isInterruption ? 'yellow' : 'gray';
|
|
602
|
+
return (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: color, children: ["\u2022 ", msg.content] }) }) }, index));
|
|
603
|
+
};
|
|
604
|
+
const renderUserMessage = (msg, index) => {
|
|
605
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "\u25CF" }), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", bold: true, children: "You" }), _jsxs(Text, { color: "gray", children: [" ", msg.timestamp.toLocaleTimeString()] })] }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { children: msg.content }) })] }, index));
|
|
606
|
+
};
|
|
607
|
+
const renderAgentMessage = (msg, index, isCurrentlyTyping, isCancelled) => {
|
|
608
|
+
const hasError = msg.content.startsWith('Error:') ||
|
|
609
|
+
msg.content === 'No response received';
|
|
610
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [!isCurrentlyTyping && !hasError && !isCancelled && (_jsx(Text, { color: "green", children: "\u25CF" })), isCurrentlyTyping && (_jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) })), hasError && _jsx(Text, { color: "red", children: "\u25CF" }), isCancelled && _jsx(Text, { color: "gray", children: "\u25CF" }), _jsx(Text, { children: " " }), _jsx(Text, { color: isCurrentlyTyping
|
|
611
|
+
? 'yellow'
|
|
612
|
+
: isCancelled
|
|
613
|
+
? 'gray'
|
|
614
|
+
: hasError
|
|
615
|
+
? 'red'
|
|
616
|
+
: 'green', bold: true, children: msg.targetName }), isCurrentlyTyping ? (_jsx(Text, { color: "gray", children: " (esc to interrupt)" })) : (_jsxs(Text, { color: "gray", children: [" ", msg.timestamp.toLocaleTimeString()] }))] }), msg.toolCalls &&
|
|
617
|
+
msg.toolCalls.length > 0 &&
|
|
618
|
+
renderToolCalls(msg.toolCalls), msg.content && (_jsx(Box, { marginLeft: 2, children: outputFormat === 'markdown' ? (_jsx(Text, { children: marked.parseInline(msg.content) })) : (_jsx(Text, { children: msg.content })) }))] }, index));
|
|
619
|
+
};
|
|
620
|
+
const renderTeamMessage = (msg, index, isCurrentlyTyping, isCancelled) => {
|
|
621
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [!isCurrentlyTyping && !isCancelled && _jsx(Text, { color: "green", children: "\u25CF" }), isCurrentlyTyping && (_jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) })), isCancelled && _jsx(Text, { color: "gray", children: "\u25CF" }), _jsx(Text, { children: " " }), _jsx(Text, { color: isCurrentlyTyping ? 'yellow' : isCancelled ? 'gray' : 'green', bold: true, children: msg.targetName }), isCurrentlyTyping ? (_jsx(Text, { color: "gray", children: " (esc to interrupt)" })) : (_jsxs(Text, { color: "gray", children: [" ", msg.timestamp.toLocaleTimeString()] }))] }), msg.members.map((member, memberIndex) => (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: memberIndex > 0 ? 1 : 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: "blueBright", children: "\u2022" }), _jsx(Text, { children: " " }), _jsx(Text, { color: "blueBright", bold: true, children: member.agentName })] }), member.toolCalls && member.toolCalls.length > 0 && (_jsx(Box, { marginLeft: 2, children: renderToolCalls(member.toolCalls) })), member.content && (_jsx(Box, { marginLeft: 2, children: outputFormat === 'markdown' ? (_jsx(Text, { children: marked.parseInline(member.content) })) : (_jsx(Text, { children: member.content })) }))] }, memberIndex)))] }, index));
|
|
622
|
+
};
|
|
623
|
+
const renderToolCalls = (toolCalls) => {
|
|
624
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "magenta", bold: true, children: "Tool Calls:" }), toolCalls.map((toolCall, toolIndex) => (_jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: "magenta", children: ["\u2022 ", toolCall.function.name] }), toolCall.function.arguments && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", dimColor: true, children: (() => {
|
|
625
|
+
try {
|
|
626
|
+
const args = JSON.parse(toolCall.function.arguments);
|
|
627
|
+
return JSON.stringify(args, null, 2);
|
|
628
|
+
}
|
|
629
|
+
catch {
|
|
630
|
+
return toolCall.function.arguments;
|
|
631
|
+
}
|
|
632
|
+
})() }) }))] }, toolIndex)))] }));
|
|
633
|
+
};
|
|
634
|
+
// Show loading state
|
|
635
|
+
if (isLoading) {
|
|
636
|
+
return (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { color: "yellow", children: [_jsx(Spinner, { type: "dots" }), " Loading available targets..."] }) }));
|
|
637
|
+
}
|
|
638
|
+
// Show error if no targets available
|
|
639
|
+
if (!target && error) {
|
|
640
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "red", children: ["\u26A0 Error: ", error] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "Please ensure ark-api is running and has available agents, models, or tools." }) })] }));
|
|
641
|
+
}
|
|
642
|
+
// Show agent selector if requested
|
|
643
|
+
if (showAgentSelector) {
|
|
644
|
+
return (_jsx(AgentSelector, { arkApiClient: arkApiClient, onSelect: (agent) => {
|
|
645
|
+
// Update the target to the selected agent
|
|
646
|
+
const agentTarget = {
|
|
647
|
+
id: `agent/${agent.name}`,
|
|
648
|
+
name: agent.name,
|
|
649
|
+
type: 'agent',
|
|
650
|
+
description: agent.description,
|
|
651
|
+
};
|
|
652
|
+
setTarget(agentTarget);
|
|
653
|
+
setChatConfig((prev) => ({ ...prev, currentTarget: agentTarget }));
|
|
654
|
+
setMessages([]);
|
|
655
|
+
setShowAgentSelector(false);
|
|
656
|
+
// Add system message about the selection
|
|
657
|
+
const systemMessage = {
|
|
658
|
+
id: generateMessageId(),
|
|
659
|
+
type: 'system',
|
|
660
|
+
content: `Switched to agent: ${agent.name}`,
|
|
661
|
+
timestamp: new Date(),
|
|
662
|
+
command: '/agents',
|
|
663
|
+
};
|
|
664
|
+
setMessages([systemMessage]);
|
|
665
|
+
}, onExit: () => setShowAgentSelector(false) }));
|
|
666
|
+
}
|
|
667
|
+
// Show model selector if requested
|
|
668
|
+
if (showModelSelector) {
|
|
669
|
+
return (_jsx(ModelSelector, { arkApiClient: arkApiClient, onSelect: (model) => {
|
|
670
|
+
// Update the target to the selected model
|
|
671
|
+
const modelTarget = {
|
|
672
|
+
id: `model/${model.name}`,
|
|
673
|
+
name: model.name,
|
|
674
|
+
type: 'model',
|
|
675
|
+
description: model.type,
|
|
676
|
+
};
|
|
677
|
+
setTarget(modelTarget);
|
|
678
|
+
setChatConfig((prev) => ({ ...prev, currentTarget: modelTarget }));
|
|
679
|
+
setMessages([]);
|
|
680
|
+
setShowModelSelector(false);
|
|
681
|
+
// Add system message about the selection
|
|
682
|
+
const systemMessage = {
|
|
683
|
+
id: generateMessageId(),
|
|
684
|
+
type: 'system',
|
|
685
|
+
content: `Switched to model: ${model.name}`,
|
|
686
|
+
timestamp: new Date(),
|
|
687
|
+
command: '/models',
|
|
688
|
+
};
|
|
689
|
+
setMessages([systemMessage]);
|
|
690
|
+
}, onExit: () => setShowModelSelector(false) }));
|
|
691
|
+
}
|
|
692
|
+
// Show team selector if requested
|
|
693
|
+
if (showTeamSelector) {
|
|
694
|
+
return (_jsx(TeamSelector, { arkApiClient: arkApiClient, onSelect: (team) => {
|
|
695
|
+
// Update the target to the selected team
|
|
696
|
+
const teamTarget = {
|
|
697
|
+
id: `team/${team.name}`,
|
|
698
|
+
name: team.name,
|
|
699
|
+
type: 'team',
|
|
700
|
+
description: team.strategy,
|
|
701
|
+
};
|
|
702
|
+
setTarget(teamTarget);
|
|
703
|
+
setChatConfig((prev) => ({ ...prev, currentTarget: teamTarget }));
|
|
704
|
+
setMessages([]);
|
|
705
|
+
setShowTeamSelector(false);
|
|
706
|
+
// Add system message about the selection
|
|
707
|
+
const systemMessage = {
|
|
708
|
+
id: generateMessageId(),
|
|
709
|
+
type: 'system',
|
|
710
|
+
content: `Switched to team: ${team.name}`,
|
|
711
|
+
timestamp: new Date(),
|
|
712
|
+
command: '/teams',
|
|
713
|
+
};
|
|
714
|
+
setMessages([systemMessage]);
|
|
715
|
+
}, onExit: () => setShowTeamSelector(false) }));
|
|
716
|
+
}
|
|
717
|
+
// Show tool selector if requested
|
|
718
|
+
if (showToolSelector) {
|
|
719
|
+
return (_jsx(ToolSelector, { arkApiClient: arkApiClient, onSelect: (tool) => {
|
|
720
|
+
// Update the target to the selected tool
|
|
721
|
+
const toolTarget = {
|
|
722
|
+
id: `tool/${tool.name}`,
|
|
723
|
+
name: tool.name,
|
|
724
|
+
type: 'tool',
|
|
725
|
+
description: tool.description,
|
|
726
|
+
};
|
|
727
|
+
setTarget(toolTarget);
|
|
728
|
+
setChatConfig((prev) => ({ ...prev, currentTarget: toolTarget }));
|
|
729
|
+
setMessages([]);
|
|
730
|
+
setShowToolSelector(false);
|
|
731
|
+
// Add system message about the selection
|
|
732
|
+
const systemMessage = {
|
|
733
|
+
id: generateMessageId(),
|
|
734
|
+
type: 'system',
|
|
735
|
+
content: `Switched to tool: ${tool.name}`,
|
|
736
|
+
timestamp: new Date(),
|
|
737
|
+
command: '/tools',
|
|
738
|
+
};
|
|
739
|
+
setMessages([systemMessage]);
|
|
740
|
+
}, onExit: () => setShowToolSelector(false) }));
|
|
741
|
+
}
|
|
742
|
+
return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [messages.length === 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, paddingX: 2, children: [_jsx(Text, { bold: true, color: "green", children: "\u273B Welcome to ARK Chat!" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Type your message and press Enter to start" }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: "Type '/' for available commands" }) })] })), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: messages.map(renderMessage) }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { borderStyle: "round", borderColor: "gray", paddingX: 1, children: _jsxs(Box, { flexDirection: "row", width: "100%", children: [_jsx(Text, { color: "cyan", bold: true, children: "\u203A" }), _jsx(Box, { marginLeft: 1, flexGrow: 1, children: _jsx(TextInput, { value: input, onChange: (value) => {
|
|
743
|
+
setInput(value);
|
|
744
|
+
// Show commands menu only when input starts with '/'
|
|
745
|
+
const shouldShowCommands = value.startsWith('/');
|
|
746
|
+
setShowCommands(shouldShowCommands);
|
|
747
|
+
// Update filtered commands
|
|
748
|
+
if (shouldShowCommands) {
|
|
749
|
+
const inputLower = value.toLowerCase();
|
|
750
|
+
const commands = [
|
|
751
|
+
{
|
|
752
|
+
command: '/agents',
|
|
753
|
+
description: 'Select an agent to chat with',
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
command: '/models',
|
|
757
|
+
description: 'Select a model to chat with',
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
command: '/teams',
|
|
761
|
+
description: 'Select a team to chat with',
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
command: '/tools',
|
|
765
|
+
description: 'Select a tool to use',
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
command: '/output',
|
|
769
|
+
description: `Set output format (${outputFormat}) - use: /output text|markdown`,
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
command: '/streaming',
|
|
773
|
+
description: `Toggle streaming mode (${chatConfig.streamingEnabled ? 'on' : 'off'}) - use: /streaming on|off`,
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
command: '/reset',
|
|
777
|
+
description: 'Clear message history',
|
|
778
|
+
},
|
|
779
|
+
];
|
|
780
|
+
// Check if user has typed a complete command (with space or at exact match)
|
|
781
|
+
const hasSpace = value.includes(' ');
|
|
782
|
+
const baseCommand = hasSpace ? value.split(' ')[0] : value;
|
|
783
|
+
// Filter commands - show matching commands or the current command if fully typed
|
|
784
|
+
const filtered = commands.filter((cmd) => {
|
|
785
|
+
if (hasSpace) {
|
|
786
|
+
// If there's a space, only show the exact matching command
|
|
787
|
+
return cmd.command === baseCommand;
|
|
788
|
+
}
|
|
789
|
+
else {
|
|
790
|
+
// Otherwise show all commands that start with the input
|
|
791
|
+
return cmd.command.toLowerCase().startsWith(inputLower);
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
setFilteredCommands(filtered);
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
setFilteredCommands([]);
|
|
798
|
+
}
|
|
799
|
+
}, onSubmit: handleSubmit, placeholder: "Type your message..." }, inputKey) })] }) }), showCommands && filteredCommands.length > 0 && (_jsx(Box, { marginLeft: 1, marginTop: 1, flexDirection: "column", children: filteredCommands.map((cmd, index) => (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: cmd.command }), _jsxs(Text, { color: "gray", children: [" ", cmd.description] })] }, index))) })), !showCommands && (_jsx(Box, { marginLeft: 1, marginTop: 0, children: _jsxs(Box, { flexDirection: "row", children: [target && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: "Chatting with " }), _jsxs(Text, { color: "gray", children: [target.type, " "] }), _jsx(Text, { color: "green", children: target.name }), _jsx(Text, { color: "gray", children: " \u2022 Shift+Tab to cycle \u2022 " })] })), _jsx(Text, { color: "gray", children: "Ctrl+C to exit" })] }) }))] })] }));
|
|
800
|
+
};
|
|
801
|
+
export default ChatUI;
|