@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,220 @@
|
|
|
1
|
+
import { streamText, CoreMessage } from 'ai';
|
|
2
|
+
import { createOpenAI } from '@ai-sdk/openai';
|
|
3
|
+
import { AgentEvent, Provider, ProviderConfig, ProviderSendOptions } from '../types';
|
|
4
|
+
|
|
5
|
+
export class OpenAIProvider implements Provider {
|
|
6
|
+
async *sendMessage(
|
|
7
|
+
messages: CoreMessage[],
|
|
8
|
+
config: ProviderConfig,
|
|
9
|
+
options?: ProviderSendOptions
|
|
10
|
+
): AsyncGenerator<AgentEvent> {
|
|
11
|
+
const cleanApiKey = config.apiKey?.trim().replace(/[\r\n]+/g, '');
|
|
12
|
+
const cleanModel = config.model.trim().replace(/[\r\n]+/g, '');
|
|
13
|
+
|
|
14
|
+
const openai = createOpenAI({
|
|
15
|
+
apiKey: cleanApiKey,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
type OpenAIEndpoint = 'responses' | 'chat' | 'completion';
|
|
19
|
+
|
|
20
|
+
const pickModel = (endpoint: OpenAIEndpoint) => {
|
|
21
|
+
switch (endpoint) {
|
|
22
|
+
case 'responses':
|
|
23
|
+
return openai.responses(cleanModel);
|
|
24
|
+
case 'chat':
|
|
25
|
+
return openai.chat(cleanModel);
|
|
26
|
+
case 'completion':
|
|
27
|
+
return openai.completion(cleanModel);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const run = async function* (
|
|
32
|
+
endpoint: OpenAIEndpoint,
|
|
33
|
+
strictJsonSchema: boolean
|
|
34
|
+
): AsyncGenerator<AgentEvent> {
|
|
35
|
+
const result = streamText({
|
|
36
|
+
model: pickModel(endpoint),
|
|
37
|
+
messages: messages,
|
|
38
|
+
system: config.systemPrompt,
|
|
39
|
+
tools: config.tools,
|
|
40
|
+
maxSteps: config.maxSteps ?? 10,
|
|
41
|
+
abortSignal: options?.abortSignal,
|
|
42
|
+
providerOptions: {
|
|
43
|
+
openai: {
|
|
44
|
+
strictJsonSchema,
|
|
45
|
+
reasoningEffort: 'medium',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
let stepCounter = 0;
|
|
51
|
+
|
|
52
|
+
for await (const chunk of result.fullStream as any) {
|
|
53
|
+
const c: any = chunk;
|
|
54
|
+
switch (c.type) {
|
|
55
|
+
case 'reasoning':
|
|
56
|
+
if (c.textDelta) {
|
|
57
|
+
yield {
|
|
58
|
+
type: 'reasoning-delta',
|
|
59
|
+
content: c.textDelta,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case 'text-delta':
|
|
65
|
+
yield {
|
|
66
|
+
type: 'text-delta',
|
|
67
|
+
content: c.textDelta,
|
|
68
|
+
};
|
|
69
|
+
break;
|
|
70
|
+
|
|
71
|
+
case 'step-start':
|
|
72
|
+
yield {
|
|
73
|
+
type: 'step-start',
|
|
74
|
+
stepNumber: typeof c.stepIndex === 'number' ? c.stepIndex : stepCounter,
|
|
75
|
+
};
|
|
76
|
+
stepCounter++;
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case 'step-finish':
|
|
80
|
+
yield {
|
|
81
|
+
type: 'step-finish',
|
|
82
|
+
stepNumber:
|
|
83
|
+
typeof c.stepIndex === 'number' ? c.stepIndex : Math.max(0, stepCounter - 1),
|
|
84
|
+
finishReason: String(c.finishReason ?? 'stop'),
|
|
85
|
+
};
|
|
86
|
+
break;
|
|
87
|
+
|
|
88
|
+
case 'tool-call':
|
|
89
|
+
yield {
|
|
90
|
+
type: 'tool-call-end',
|
|
91
|
+
toolCallId: String(c.toolCallId ?? ''),
|
|
92
|
+
toolName: String(c.toolName ?? ''),
|
|
93
|
+
args: (c.args ?? {}) as Record<string, unknown>,
|
|
94
|
+
};
|
|
95
|
+
break;
|
|
96
|
+
|
|
97
|
+
case 'tool-result':
|
|
98
|
+
yield {
|
|
99
|
+
type: 'tool-result',
|
|
100
|
+
toolCallId: String(c.toolCallId ?? ''),
|
|
101
|
+
toolName: String(c.toolName ?? ''),
|
|
102
|
+
result: c.result,
|
|
103
|
+
};
|
|
104
|
+
break;
|
|
105
|
+
|
|
106
|
+
case 'finish':
|
|
107
|
+
yield {
|
|
108
|
+
type: 'finish',
|
|
109
|
+
finishReason: String(c.finishReason ?? 'stop'),
|
|
110
|
+
usage: c.usage,
|
|
111
|
+
};
|
|
112
|
+
break;
|
|
113
|
+
|
|
114
|
+
case 'error':
|
|
115
|
+
{
|
|
116
|
+
const err = c.error;
|
|
117
|
+
const msg =
|
|
118
|
+
err instanceof Error
|
|
119
|
+
? err.message
|
|
120
|
+
: typeof err === 'string'
|
|
121
|
+
? err
|
|
122
|
+
: 'Unknown error';
|
|
123
|
+
yield {
|
|
124
|
+
type: 'error',
|
|
125
|
+
error: msg,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const classifyEndpointError = (msg: string): OpenAIEndpoint | null => {
|
|
136
|
+
const m = msg || '';
|
|
137
|
+
if (m.includes('v1/chat/completions')) {
|
|
138
|
+
if (m.toLowerCase().includes('not a chat model')) return 'responses';
|
|
139
|
+
}
|
|
140
|
+
if (m.includes('v1/responses')) {
|
|
141
|
+
if (m.toLowerCase().includes('not supported') || m.toLowerCase().includes('unknown')) return 'chat';
|
|
142
|
+
}
|
|
143
|
+
if (m.includes('v1/completions')) {
|
|
144
|
+
return 'completion';
|
|
145
|
+
}
|
|
146
|
+
if (m.toLowerCase().includes('did you mean to use v1/completions')) {
|
|
147
|
+
return 'completion';
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
yield* run('responses', true);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (options?.abortSignal?.aborted) return;
|
|
156
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
157
|
+
const looksLikeStrictSchemaError =
|
|
158
|
+
msg.includes('Invalid schema for function') &&
|
|
159
|
+
msg.includes('required') &&
|
|
160
|
+
msg.includes('properties');
|
|
161
|
+
|
|
162
|
+
if (looksLikeStrictSchemaError) {
|
|
163
|
+
try {
|
|
164
|
+
yield* run('responses', false);
|
|
165
|
+
return;
|
|
166
|
+
} catch (retryError) {
|
|
167
|
+
if (options?.abortSignal?.aborted) return;
|
|
168
|
+
yield {
|
|
169
|
+
type: 'error',
|
|
170
|
+
error: retryError instanceof Error ? retryError.message : 'Unknown error occurred',
|
|
171
|
+
};
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const fallbackEndpoint = classifyEndpointError(msg);
|
|
177
|
+
if (fallbackEndpoint && fallbackEndpoint !== 'responses') {
|
|
178
|
+
try {
|
|
179
|
+
yield* run(fallbackEndpoint, true);
|
|
180
|
+
return;
|
|
181
|
+
} catch (endpointError) {
|
|
182
|
+
if (options?.abortSignal?.aborted) return;
|
|
183
|
+
const endpointMsg = endpointError instanceof Error ? endpointError.message : String(endpointError);
|
|
184
|
+
const strictSchemaFromFallback =
|
|
185
|
+
endpointMsg.includes('Invalid schema for function') &&
|
|
186
|
+
endpointMsg.includes('required') &&
|
|
187
|
+
endpointMsg.includes('properties');
|
|
188
|
+
|
|
189
|
+
if (strictSchemaFromFallback) {
|
|
190
|
+
try {
|
|
191
|
+
yield* run(fallbackEndpoint, false);
|
|
192
|
+
return;
|
|
193
|
+
} catch (endpointRetryError) {
|
|
194
|
+
if (options?.abortSignal?.aborted) return;
|
|
195
|
+
yield {
|
|
196
|
+
type: 'error',
|
|
197
|
+
error:
|
|
198
|
+
endpointRetryError instanceof Error
|
|
199
|
+
? endpointRetryError.message
|
|
200
|
+
: 'Unknown error occurred',
|
|
201
|
+
};
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
yield {
|
|
207
|
+
type: 'error',
|
|
208
|
+
error: endpointMsg || 'Unknown error occurred',
|
|
209
|
+
};
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
yield {
|
|
215
|
+
type: 'error',
|
|
216
|
+
error: msg || 'Unknown error occurred',
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { streamText, CoreMessage } from 'ai';
|
|
2
|
+
import { createXai } from '@ai-sdk/xai';
|
|
3
|
+
import { AgentEvent, Provider, ProviderConfig, ProviderSendOptions } from '../types';
|
|
4
|
+
|
|
5
|
+
export class XaiProvider implements Provider {
|
|
6
|
+
async *sendMessage(
|
|
7
|
+
messages: CoreMessage[],
|
|
8
|
+
config: ProviderConfig,
|
|
9
|
+
options?: ProviderSendOptions
|
|
10
|
+
): AsyncGenerator<AgentEvent> {
|
|
11
|
+
const cleanApiKey = config.apiKey?.trim().replace(/[\r\n]+/g, '');
|
|
12
|
+
const cleanModel = config.model.trim().replace(/[\r\n]+/g, '');
|
|
13
|
+
|
|
14
|
+
const xai = createXai({
|
|
15
|
+
apiKey: cleanApiKey,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const result = streamText({
|
|
19
|
+
model: xai(cleanModel),
|
|
20
|
+
messages: messages,
|
|
21
|
+
system: config.systemPrompt,
|
|
22
|
+
tools: config.tools,
|
|
23
|
+
maxSteps: config.maxSteps || 10,
|
|
24
|
+
abortSignal: options?.abortSignal,
|
|
25
|
+
providerOptions: {
|
|
26
|
+
xai: {
|
|
27
|
+
reasoningEffort: 'high',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
let stepCounter = 0;
|
|
34
|
+
|
|
35
|
+
for await (const chunk of result.fullStream as any) {
|
|
36
|
+
const c: any = chunk;
|
|
37
|
+
switch (c.type) {
|
|
38
|
+
case 'reasoning':
|
|
39
|
+
if (c.textDelta) {
|
|
40
|
+
yield {
|
|
41
|
+
type: 'reasoning-delta',
|
|
42
|
+
content: c.textDelta,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
break;
|
|
46
|
+
|
|
47
|
+
case 'text-delta':
|
|
48
|
+
yield {
|
|
49
|
+
type: 'text-delta',
|
|
50
|
+
content: c.textDelta,
|
|
51
|
+
};
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
case 'step-start':
|
|
55
|
+
yield {
|
|
56
|
+
type: 'step-start',
|
|
57
|
+
stepNumber: typeof c.stepIndex === 'number' ? c.stepIndex : stepCounter,
|
|
58
|
+
};
|
|
59
|
+
stepCounter++;
|
|
60
|
+
break;
|
|
61
|
+
|
|
62
|
+
case 'step-finish':
|
|
63
|
+
yield {
|
|
64
|
+
type: 'step-finish',
|
|
65
|
+
stepNumber:
|
|
66
|
+
typeof c.stepIndex === 'number' ? c.stepIndex : Math.max(0, stepCounter - 1),
|
|
67
|
+
finishReason: String(c.finishReason ?? 'stop'),
|
|
68
|
+
};
|
|
69
|
+
break;
|
|
70
|
+
|
|
71
|
+
case 'tool-call':
|
|
72
|
+
yield {
|
|
73
|
+
type: 'tool-call-end',
|
|
74
|
+
toolCallId: String(c.toolCallId ?? ''),
|
|
75
|
+
toolName: String(c.toolName ?? ''),
|
|
76
|
+
args: (c.args ?? {}) as Record<string, unknown>,
|
|
77
|
+
};
|
|
78
|
+
break;
|
|
79
|
+
|
|
80
|
+
case 'tool-result':
|
|
81
|
+
yield {
|
|
82
|
+
type: 'tool-result',
|
|
83
|
+
toolCallId: String(c.toolCallId ?? ''),
|
|
84
|
+
toolName: String(c.toolName ?? ''),
|
|
85
|
+
result: c.result,
|
|
86
|
+
};
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case 'finish':
|
|
90
|
+
yield {
|
|
91
|
+
type: 'finish',
|
|
92
|
+
finishReason: String(c.finishReason ?? 'stop'),
|
|
93
|
+
usage: c.usage,
|
|
94
|
+
};
|
|
95
|
+
break;
|
|
96
|
+
|
|
97
|
+
case 'error':
|
|
98
|
+
{
|
|
99
|
+
const err = c.error;
|
|
100
|
+
const msg =
|
|
101
|
+
err instanceof Error
|
|
102
|
+
? err.message
|
|
103
|
+
: typeof err === 'string'
|
|
104
|
+
? err
|
|
105
|
+
: 'Unknown error';
|
|
106
|
+
yield {
|
|
107
|
+
type: 'error',
|
|
108
|
+
error: msg,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (options?.abortSignal?.aborted) return;
|
|
116
|
+
yield {
|
|
117
|
+
type: 'error',
|
|
118
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { tool, type CoreTool } from 'ai';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { executeTool } from './executor';
|
|
4
|
+
|
|
5
|
+
export const bash: CoreTool = tool({
|
|
6
|
+
description: 'Execute a shell command in the workspace. Add --timeout <ms> at the END of your command for long-running processes (max 90000ms). IMPORTANT: This operation requires user approval - the user will see the command that will be executed and must approve before it runs. If rejected, ask the user for clarification using the question tool.',
|
|
7
|
+
parameters: z.object({
|
|
8
|
+
command: z.string().describe('The shell command to execute. Add --timeout <ms> at the end (max 90000ms) for: dev servers (--timeout 5000), builds (--timeout 90000), tests (--timeout 60000), installs (--timeout 90000), interactive CLIs with menus/options (--timeout 5000). You will receive the command output and must analyze it to determine if it succeeded or failed.'),
|
|
9
|
+
}),
|
|
10
|
+
execute: async (args) => {
|
|
11
|
+
const result = await executeTool('bash', args);
|
|
12
|
+
if (!result.success) {
|
|
13
|
+
const errorMessage = result.error || 'Unknown error occurred';
|
|
14
|
+
return result.userMessage
|
|
15
|
+
? { error: errorMessage, userMessage: result.userMessage }
|
|
16
|
+
: { error: errorMessage };
|
|
17
|
+
}
|
|
18
|
+
return result.result;
|
|
19
|
+
},
|
|
20
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CoreTool } from 'ai';
|
|
2
|
+
|
|
3
|
+
import { bash } from './bash.ts';
|
|
4
|
+
import { list } from './list.ts';
|
|
5
|
+
import { read } from './read.ts';
|
|
6
|
+
import { write } from './write.ts';
|
|
7
|
+
import { glob } from './glob.ts';
|
|
8
|
+
import { grep } from './grep.ts';
|
|
9
|
+
import { edit } from './edit.ts';
|
|
10
|
+
import { question } from './question.ts';
|
|
11
|
+
import { explore } from './explore.ts';
|
|
12
|
+
|
|
13
|
+
export const tools: Record<string, CoreTool> = {
|
|
14
|
+
read,
|
|
15
|
+
write,
|
|
16
|
+
list,
|
|
17
|
+
bash,
|
|
18
|
+
glob,
|
|
19
|
+
grep,
|
|
20
|
+
edit,
|
|
21
|
+
question,
|
|
22
|
+
explore,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function getTools(): Record<string, CoreTool> {
|
|
26
|
+
return tools;
|
|
27
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { tool, type CoreTool } from 'ai';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { executeTool } from './executor';
|
|
4
|
+
|
|
5
|
+
export const edit: CoreTool = tool({
|
|
6
|
+
description: 'Edit a specific part of a file by replacing old content with new content. More precise than rewriting the entire file. IMPORTANT: This operation requires user approval - the user will see a preview showing the old and new content and must approve before changes are made. If rejected, ask the user for clarification using the question tool.',
|
|
7
|
+
parameters: z.object({
|
|
8
|
+
path: z.string().describe('The path to the file relative to the workspace root'),
|
|
9
|
+
old_content: z.string().describe('The exact text content to find and replace'),
|
|
10
|
+
new_content: z.string().describe('The new text content to replace with'),
|
|
11
|
+
occurrence: z.number().nullable().optional().describe('Which occurrence to replace (1 for first, 2 for second, etc. Use null for 1)'),
|
|
12
|
+
}),
|
|
13
|
+
execute: async (args) => {
|
|
14
|
+
const result = await executeTool('edit', args);
|
|
15
|
+
if (!result.success) {
|
|
16
|
+
const errorMessage = result.error || 'Unknown error occurred';
|
|
17
|
+
return result.userMessage
|
|
18
|
+
? { error: errorMessage, userMessage: result.userMessage }
|
|
19
|
+
: { error: errorMessage };
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
},
|
|
23
|
+
});
|