@kirosnn/mosaic 0.0.7
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/.mosaic/mosaic.local.jsonc +0 -0
- package/MOSAIC.md +188 -0
- package/README.md +127 -0
- package/docs/mosaic.png +0 -0
- package/package.json +42 -0
- package/src/agent/Agent.ts +131 -0
- package/src/agent/context.ts +96 -0
- package/src/agent/index.ts +2 -0
- package/src/agent/prompts/systemPrompt.ts +138 -0
- package/src/agent/prompts/toolsPrompt.ts +139 -0
- package/src/agent/provider/anthropic.ts +122 -0
- package/src/agent/provider/google.ts +124 -0
- package/src/agent/provider/mistral.ts +117 -0
- package/src/agent/provider/ollama.ts +531 -0
- package/src/agent/provider/openai.ts +220 -0
- package/src/agent/provider/xai.ts +122 -0
- package/src/agent/tools/bash.ts +20 -0
- package/src/agent/tools/definitions.ts +27 -0
- package/src/agent/tools/edit.ts +23 -0
- package/src/agent/tools/executor.ts +751 -0
- package/src/agent/tools/explore.ts +18 -0
- package/src/agent/tools/exploreExecutor.ts +320 -0
- package/src/agent/tools/glob.ts +16 -0
- package/src/agent/tools/grep.ts +19 -0
- package/src/agent/tools/index.ts +4 -0
- package/src/agent/tools/list.ts +20 -0
- package/src/agent/tools/question.ts +20 -0
- package/src/agent/tools/read.ts +15 -0
- package/src/agent/tools/write.ts +21 -0
- package/src/agent/types.ts +155 -0
- package/src/components/App.tsx +174 -0
- package/src/components/CommandsModal.tsx +77 -0
- package/src/components/CustomInput.tsx +328 -0
- package/src/components/Main.tsx +1112 -0
- package/src/components/Notification.tsx +91 -0
- package/src/components/SelectList.tsx +47 -0
- package/src/components/Setup.tsx +528 -0
- package/src/components/ShortcutsModal.tsx +67 -0
- package/src/components/Welcome.tsx +39 -0
- package/src/components/main/ApprovalPanel.tsx +134 -0
- package/src/components/main/ChatPage.tsx +516 -0
- package/src/components/main/HomePage.tsx +111 -0
- package/src/components/main/QuestionPanel.tsx +85 -0
- package/src/components/main/ThinkingIndicator.tsx +101 -0
- package/src/components/main/types.ts +55 -0
- package/src/components/main/wrapText.ts +41 -0
- package/src/index.tsx +212 -0
- package/src/utils/approvalBridge.ts +129 -0
- package/src/utils/commands/echo.ts +22 -0
- package/src/utils/commands/help.ts +25 -0
- package/src/utils/commands/index.ts +68 -0
- package/src/utils/commands/init.ts +68 -0
- package/src/utils/commands/redo.ts +74 -0
- package/src/utils/commands/registry.ts +29 -0
- package/src/utils/commands/sessions.ts +129 -0
- package/src/utils/commands/types.ts +20 -0
- package/src/utils/commands/undo.ts +75 -0
- package/src/utils/commands/web.ts +77 -0
- package/src/utils/config.ts +357 -0
- package/src/utils/diff.ts +201 -0
- package/src/utils/diffRendering.tsx +62 -0
- package/src/utils/exploreBridge.ts +87 -0
- package/src/utils/fileChangeTracker.ts +98 -0
- package/src/utils/fileChangesBridge.ts +18 -0
- package/src/utils/history.ts +106 -0
- package/src/utils/markdown.tsx +232 -0
- package/src/utils/models.ts +304 -0
- package/src/utils/questionBridge.ts +122 -0
- package/src/utils/terminalUtils.ts +25 -0
- package/src/utils/toolFormatting.ts +384 -0
- package/src/utils/undoRedo.ts +429 -0
- package/src/utils/undoRedoBridge.ts +45 -0
- package/src/utils/undoRedoDb.ts +338 -0
- package/src/utils/uninstall.ts +45 -0
- package/src/utils/version.ts +3 -0
- package/src/web/app.tsx +606 -0
- package/src/web/assets/css/ChatPage.css +212 -0
- package/src/web/assets/css/FileExplorer.css +202 -0
- package/src/web/assets/css/HomePage.css +119 -0
- package/src/web/assets/css/Markdown.css +178 -0
- package/src/web/assets/css/MessageItem.css +160 -0
- package/src/web/assets/css/Sidebar.css +208 -0
- package/src/web/assets/css/SidebarModal.css +137 -0
- package/src/web/assets/css/ThinkingIndicator.css +47 -0
- package/src/web/assets/css/ToolMessage.css +148 -0
- package/src/web/assets/css/global.css +226 -0
- package/src/web/assets/fonts/Geist-Black.woff2 +0 -0
- package/src/web/assets/fonts/Geist-BlackItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-Bold.woff2 +0 -0
- package/src/web/assets/fonts/Geist-BoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-ExtraBold.woff2 +0 -0
- package/src/web/assets/fonts/Geist-ExtraBoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-ExtraLight.woff2 +0 -0
- package/src/web/assets/fonts/Geist-ExtraLightItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-Italic[wght].woff2 +0 -0
- package/src/web/assets/fonts/Geist-Light.woff2 +0 -0
- package/src/web/assets/fonts/Geist-LightItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-Medium.woff2 +0 -0
- package/src/web/assets/fonts/Geist-MediumItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-Regular.woff2 +0 -0
- package/src/web/assets/fonts/Geist-RegularItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-SemiBold.woff2 +0 -0
- package/src/web/assets/fonts/Geist-SemiBoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-Thin.woff2 +0 -0
- package/src/web/assets/fonts/Geist-ThinItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Black.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-BlackItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Bold.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-BoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-ExtraBold.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-ExtraBoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-ExtraLight.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-ExtraLightItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Italic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Italic[wght].woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Light.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-LightItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Medium.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-MediumItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Regular.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-SemiBold.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-SemiBoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Thin.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-ThinItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono[wght].woff2 +0 -0
- package/src/web/assets/fonts/Geist[wght].woff2 +0 -0
- package/src/web/assets/fonts/blauer-nue-regular.woff2 +0 -0
- package/src/web/assets/fonts/neue-montreal-regular.woff2 +0 -0
- package/src/web/assets/images/favicon-v2.svg +6 -0
- package/src/web/assets/images/favicon.png +0 -0
- package/src/web/assets/images/foruse.svg +5 -0
- package/src/web/assets/images/logo_black.svg +5 -0
- package/src/web/assets/images/logo_white.svg +5 -0
- package/src/web/assets/images/logoblack.png +0 -0
- package/src/web/assets/images/logowhite.png +0 -0
- package/src/web/build.ts +23 -0
- package/src/web/components/ApprovalPanel.tsx +191 -0
- package/src/web/components/ChatPage.tsx +273 -0
- package/src/web/components/FileExplorer.tsx +162 -0
- package/src/web/components/HomePage.tsx +121 -0
- package/src/web/components/MessageItem.tsx +178 -0
- package/src/web/components/Modal.tsx +30 -0
- package/src/web/components/QuestionPanel.tsx +149 -0
- package/src/web/components/Setup.tsx +211 -0
- package/src/web/components/Sidebar.tsx +292 -0
- package/src/web/components/ThinkingIndicator.tsx +85 -0
- package/src/web/logo_black.svg +5 -0
- package/src/web/logo_white.svg +5 -0
- package/src/web/router.ts +46 -0
- package/src/web/server.tsx +662 -0
- package/src/web/storage.ts +92 -0
- package/src/web/types.ts +17 -0
- package/src/web/utils.ts +61 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import { formatWriteToolResult, formatEditToolResult } from './diff';
|
|
2
|
+
|
|
3
|
+
const TOOL_BODY_INDENT = 2;
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_MAX_TOOL_LINES = 10;
|
|
6
|
+
|
|
7
|
+
const TOOL_DISPLAY_NAMES: Record<string, string> = {
|
|
8
|
+
read: 'Read',
|
|
9
|
+
write: 'Write',
|
|
10
|
+
edit: 'Edit',
|
|
11
|
+
list: 'List',
|
|
12
|
+
create_directory: 'Mkdir',
|
|
13
|
+
glob: 'Glob',
|
|
14
|
+
grep: 'Grep',
|
|
15
|
+
bash: 'Command',
|
|
16
|
+
question: 'Question',
|
|
17
|
+
explore: 'Explore',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function getToolDisplayName(toolName: string): string {
|
|
21
|
+
return TOOL_DISPLAY_NAMES[toolName] || toolName;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function truncateLines(lines: string[], maxLines?: number): string[] {
|
|
25
|
+
if (!maxLines || maxLines <= 0) return lines;
|
|
26
|
+
if (lines.length <= maxLines) return lines;
|
|
27
|
+
|
|
28
|
+
if (maxLines === 1) return [lines[0] || ''];
|
|
29
|
+
|
|
30
|
+
const visibleLines = lines.slice(0, Math.max(0, maxLines - 1));
|
|
31
|
+
const hiddenCount = Math.max(0, lines.length - visibleLines.length);
|
|
32
|
+
return [...visibleLines, `(${hiddenCount} more lines)`];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function formatToolResult(result: unknown): string {
|
|
36
|
+
if (typeof result === 'string') return result;
|
|
37
|
+
if (result === null || result === undefined) return '';
|
|
38
|
+
try {
|
|
39
|
+
return JSON.stringify(result, null, 2);
|
|
40
|
+
} catch {
|
|
41
|
+
return String(result);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function formatKnownToolArgs(toolName: string, args: Record<string, unknown>): string | null {
|
|
46
|
+
switch (toolName) {
|
|
47
|
+
case 'read':
|
|
48
|
+
case 'write':
|
|
49
|
+
case 'edit':
|
|
50
|
+
case 'list':
|
|
51
|
+
case 'create_directory':
|
|
52
|
+
case 'glob':
|
|
53
|
+
case 'grep':
|
|
54
|
+
case 'bash':
|
|
55
|
+
case 'explore': {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
case 'question': {
|
|
60
|
+
const prompt = typeof args.prompt === 'string' ? args.prompt : '';
|
|
61
|
+
return prompt ? `prompt: "${prompt}"` : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
default: {
|
|
65
|
+
const keys = Object.keys(args);
|
|
66
|
+
if (keys.length === 0) return null;
|
|
67
|
+
try {
|
|
68
|
+
return `args: ${JSON.stringify(args)}`;
|
|
69
|
+
} catch {
|
|
70
|
+
return 'args: [unserializable]';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function isToolSuccess(result: unknown): boolean {
|
|
77
|
+
if (result === null || result === undefined) return false;
|
|
78
|
+
|
|
79
|
+
if (typeof result === 'object') {
|
|
80
|
+
const obj = result as Record<string, unknown>;
|
|
81
|
+
if (obj.success === false) return false;
|
|
82
|
+
if (typeof obj.error === 'string' && obj.error.trim()) return false;
|
|
83
|
+
if ('error' in obj && obj.error !== undefined && obj.error !== null) return false;
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return typeof result === 'string';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function formatToolHeader(toolName: string, args: Record<string, unknown>): string {
|
|
91
|
+
const displayName = getToolDisplayName(toolName);
|
|
92
|
+
const path = typeof args.path === 'string' ? args.path : '';
|
|
93
|
+
|
|
94
|
+
switch (toolName) {
|
|
95
|
+
case 'read':
|
|
96
|
+
case 'write':
|
|
97
|
+
case 'edit':
|
|
98
|
+
case 'list':
|
|
99
|
+
case 'create_directory':
|
|
100
|
+
return path ? `${displayName} (${path})` : displayName;
|
|
101
|
+
case 'glob': {
|
|
102
|
+
const pattern = typeof args.pattern === 'string' ? args.pattern : '';
|
|
103
|
+
return pattern ? `${displayName} (${pattern})` : displayName;
|
|
104
|
+
}
|
|
105
|
+
case 'grep': {
|
|
106
|
+
const pattern = typeof args.file_pattern === 'string' ? args.file_pattern : '';
|
|
107
|
+
return pattern ? `${displayName} (pattern: ${pattern})` : displayName;
|
|
108
|
+
}
|
|
109
|
+
case 'bash': {
|
|
110
|
+
const command = typeof args.command === 'string' ? args.command : '';
|
|
111
|
+
const cleanCommand = command.replace(/\s+--timeout\s+\d+$/, '');
|
|
112
|
+
return cleanCommand ? `${displayName} (${cleanCommand})` : displayName;
|
|
113
|
+
}
|
|
114
|
+
case 'explore': {
|
|
115
|
+
const purpose = typeof args.purpose === 'string' ? args.purpose : '';
|
|
116
|
+
return purpose ? `${displayName} (${purpose})` : displayName;
|
|
117
|
+
}
|
|
118
|
+
default:
|
|
119
|
+
return displayName;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function parseToolHeader(toolName: string, args: Record<string, unknown>): { name: string; info: string | null } {
|
|
124
|
+
const displayName = getToolDisplayName(toolName);
|
|
125
|
+
const path = typeof args.path === 'string' ? args.path : '';
|
|
126
|
+
|
|
127
|
+
switch (toolName) {
|
|
128
|
+
case 'read':
|
|
129
|
+
case 'write':
|
|
130
|
+
case 'edit':
|
|
131
|
+
case 'list':
|
|
132
|
+
case 'create_directory':
|
|
133
|
+
return { name: displayName, info: path || null };
|
|
134
|
+
case 'glob': {
|
|
135
|
+
const pattern = typeof args.pattern === 'string' ? args.pattern : '';
|
|
136
|
+
return { name: displayName, info: pattern || null };
|
|
137
|
+
}
|
|
138
|
+
case 'grep': {
|
|
139
|
+
const pattern = typeof args.file_pattern === 'string' ? args.file_pattern : '';
|
|
140
|
+
return { name: displayName, info: pattern ? `pattern: ${pattern}` : null };
|
|
141
|
+
}
|
|
142
|
+
case 'bash': {
|
|
143
|
+
const command = typeof args.command === 'string' ? args.command : '';
|
|
144
|
+
const cleanCommand = command.replace(/\s+--timeout\s+\d+$/, '');
|
|
145
|
+
return { name: displayName, info: cleanCommand || null };
|
|
146
|
+
}
|
|
147
|
+
case 'explore': {
|
|
148
|
+
const purpose = typeof args.purpose === 'string' ? args.purpose : '';
|
|
149
|
+
return { name: displayName, info: purpose || null };
|
|
150
|
+
}
|
|
151
|
+
default:
|
|
152
|
+
return { name: displayName, info: null };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function getLineCount(text: string): number {
|
|
157
|
+
if (!text) return 0;
|
|
158
|
+
return text.split(/\r?\n/).length;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function formatListTree(result: unknown): string[] {
|
|
162
|
+
if (typeof result !== 'string') return [];
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(result) as Array<{ name?: string; path?: string; type?: string }>;
|
|
165
|
+
if (!Array.isArray(parsed)) return [];
|
|
166
|
+
|
|
167
|
+
const entries = parsed
|
|
168
|
+
.map((e) => ({
|
|
169
|
+
name: typeof e.name === 'string' ? e.name : (typeof e.path === 'string' ? e.path : ''),
|
|
170
|
+
type: typeof e.type === 'string' ? e.type : '',
|
|
171
|
+
}))
|
|
172
|
+
.filter((e) => e.name);
|
|
173
|
+
|
|
174
|
+
const dirs = entries
|
|
175
|
+
.filter((e) => e.type === 'directory')
|
|
176
|
+
.map((e) => `${e.name}/`)
|
|
177
|
+
.sort((a, b) => a.localeCompare(b));
|
|
178
|
+
|
|
179
|
+
const files = entries
|
|
180
|
+
.filter((e) => e.type !== 'directory')
|
|
181
|
+
.map((e) => e.name)
|
|
182
|
+
.sort((a, b) => a.localeCompare(b));
|
|
183
|
+
|
|
184
|
+
return [...dirs, ...files];
|
|
185
|
+
} catch {
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function formatGrepResult(result: unknown): string[] {
|
|
191
|
+
if (typeof result !== 'string') return [];
|
|
192
|
+
try {
|
|
193
|
+
const parsed = JSON.parse(result);
|
|
194
|
+
|
|
195
|
+
if (Array.isArray(parsed)) {
|
|
196
|
+
if (parsed.length === 0) return ['No results'];
|
|
197
|
+
|
|
198
|
+
if (typeof parsed[0] === 'string') {
|
|
199
|
+
return parsed.map(file => ` ${file}`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (typeof parsed[0] === 'object' && parsed[0] !== null) {
|
|
203
|
+
const lines: string[] = [];
|
|
204
|
+
for (const item of parsed) {
|
|
205
|
+
if (item.file) {
|
|
206
|
+
lines.push(`${item.file}:`);
|
|
207
|
+
if (Array.isArray(item.matches)) {
|
|
208
|
+
for (const match of item.matches) {
|
|
209
|
+
if (match.line && match.content) {
|
|
210
|
+
lines.push(` ${match.line}: ${match.content.trim()}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return lines.length > 0 ? lines : ['No matches'];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return [result];
|
|
221
|
+
} catch {
|
|
222
|
+
return typeof result === 'string' ? [result] : [];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function getToolErrorText(result: unknown): string | null {
|
|
227
|
+
if (!result || typeof result !== 'object') return null;
|
|
228
|
+
const obj = result as Record<string, unknown>;
|
|
229
|
+
|
|
230
|
+
const userMessage = obj.userMessage;
|
|
231
|
+
if (typeof userMessage === 'string' && userMessage.trim()) {
|
|
232
|
+
return userMessage.trim();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const error = obj.error;
|
|
236
|
+
return typeof error === 'string' && error.trim() ? error.trim() : null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function formatToolBodyLines(toolName: string, args: Record<string, unknown>, result: unknown): string[] {
|
|
240
|
+
const errorText = getToolErrorText(result);
|
|
241
|
+
if (errorText) return [`Tool error: ${errorText}`];
|
|
242
|
+
|
|
243
|
+
switch (toolName) {
|
|
244
|
+
case 'read': {
|
|
245
|
+
const content = typeof result === 'string' ? result : '';
|
|
246
|
+
const lineCount = getLineCount(content);
|
|
247
|
+
return [`Read ${lineCount} lines`];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
case 'write': {
|
|
251
|
+
const append = args.append === true;
|
|
252
|
+
return formatWriteToolResult(result, append);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
case 'edit': {
|
|
256
|
+
return formatEditToolResult(result);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
case 'create_directory': {
|
|
260
|
+
return ['Created'];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
case 'list': {
|
|
264
|
+
const treeLines = formatListTree(result);
|
|
265
|
+
return treeLines.length > 0 ? treeLines : ['(empty)'];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
case 'glob': {
|
|
269
|
+
if (typeof result !== 'string') {
|
|
270
|
+
return ['Error: result is not a string'];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const trimmed = result.trim();
|
|
274
|
+
if (!trimmed) return ['No results (empty response)'];
|
|
275
|
+
if (trimmed === '[]') return ['No results'];
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
const parsed = JSON.parse(trimmed);
|
|
279
|
+
if (!Array.isArray(parsed)) {
|
|
280
|
+
return [`Error: result is not an array (${typeof parsed})`];
|
|
281
|
+
}
|
|
282
|
+
if (parsed.length === 0) {
|
|
283
|
+
return ['No results'];
|
|
284
|
+
}
|
|
285
|
+
return parsed.map((file: string) => ` ${file}`);
|
|
286
|
+
} catch (error) {
|
|
287
|
+
return [`Parse error: ${error instanceof Error ? error.message : 'unknown'}`, `Raw result: ${result.substring(0, 200)}`];
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
case 'grep': {
|
|
292
|
+
return formatGrepResult(result);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
case 'question': {
|
|
296
|
+
if (result && typeof result === 'object') {
|
|
297
|
+
const obj = result as Record<string, unknown>;
|
|
298
|
+
const label = typeof obj.label === 'string' ? obj.label : '';
|
|
299
|
+
const value = typeof obj.value === 'string' ? obj.value : '';
|
|
300
|
+
if (label && value) return [`Selected: ${label} (${value})`];
|
|
301
|
+
if (label) return [`Selected: ${label}`];
|
|
302
|
+
if (value) return [`Selected: ${value}`];
|
|
303
|
+
}
|
|
304
|
+
return ['Selected'];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
case 'explore': {
|
|
308
|
+
if (typeof result === 'string') {
|
|
309
|
+
const lines = result.split(/\r?\n/).filter(line => line.trim());
|
|
310
|
+
return lines.length > 0 ? lines : ['Exploration completed'];
|
|
311
|
+
}
|
|
312
|
+
if (result && typeof result === 'object') {
|
|
313
|
+
const obj = result as Record<string, unknown>;
|
|
314
|
+
if (typeof obj.error === 'string') {
|
|
315
|
+
return [`Error: ${obj.error}`];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return ['Exploration completed'];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
default: {
|
|
322
|
+
const toolResultText = formatToolResult(result);
|
|
323
|
+
if (!toolResultText) return [];
|
|
324
|
+
return toolResultText.split(/\r?\n/);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function formatToolContent(
|
|
330
|
+
toolName: string,
|
|
331
|
+
args: Record<string, unknown>,
|
|
332
|
+
result: unknown,
|
|
333
|
+
options?: {
|
|
334
|
+
maxLines?: number;
|
|
335
|
+
}
|
|
336
|
+
): string {
|
|
337
|
+
const lines: string[] = [formatToolHeader(toolName, args)];
|
|
338
|
+
|
|
339
|
+
const argsLine = formatKnownToolArgs(toolName, args);
|
|
340
|
+
if (argsLine) lines.push(argsLine);
|
|
341
|
+
|
|
342
|
+
const bodyLines = formatToolBodyLines(toolName, args, result);
|
|
343
|
+
for (const line of bodyLines) lines.push(line);
|
|
344
|
+
|
|
345
|
+
const skipTruncate = toolName === 'write' || toolName === 'edit';
|
|
346
|
+
if (skipTruncate) {
|
|
347
|
+
return lines.join('\n');
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return truncateLines(lines, options?.maxLines).join('\n');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export function formatToolMessage(
|
|
354
|
+
toolName: string,
|
|
355
|
+
args: Record<string, unknown>,
|
|
356
|
+
result: unknown,
|
|
357
|
+
options?: {
|
|
358
|
+
maxLines?: number;
|
|
359
|
+
}
|
|
360
|
+
): {
|
|
361
|
+
content: string;
|
|
362
|
+
success: boolean;
|
|
363
|
+
} {
|
|
364
|
+
return {
|
|
365
|
+
content: formatToolContent(toolName, args, result, options),
|
|
366
|
+
success: isToolSuccess(result),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export function getToolParagraphIndent(paragraphIndex: number): number {
|
|
371
|
+
return paragraphIndex > 0 ? TOOL_BODY_INDENT : 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export function getToolWrapTarget(paragraph: string, paragraphIndex: number): string {
|
|
375
|
+
return paragraphIndex > 0 ? paragraph.trimStart() : paragraph;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function getToolWrapWidth(maxWidth: number, paragraphIndex: number): number {
|
|
379
|
+
return Math.max(1, maxWidth - getToolParagraphIndent(paragraphIndex));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export function formatErrorMessage(errorType: 'API' | 'Mosaic' | 'Tool', errorMessage: string): string {
|
|
383
|
+
return `${errorType} Error\n${errorMessage}`;
|
|
384
|
+
}
|