oricore 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +199 -0
  3. package/dist/agent/agent/agentManager.d.ts +38 -0
  4. package/dist/agent/agent/builtin/common.d.ts +5 -0
  5. package/dist/agent/agent/builtin/explore.d.ts +5 -0
  6. package/dist/agent/agent/builtin/general-purpose.d.ts +5 -0
  7. package/dist/agent/agent/builtin/index.d.ts +5 -0
  8. package/dist/agent/agent/executor.d.ts +2 -0
  9. package/dist/agent/agent/types.d.ts +98 -0
  10. package/dist/api/engine.d.ts +213 -0
  11. package/dist/communication/index.d.ts +4 -0
  12. package/dist/communication/messageBus.d.ts +71 -0
  13. package/dist/core/at.d.ts +26 -0
  14. package/dist/core/backgroundTaskManager.d.ts +27 -0
  15. package/dist/core/compact.d.ts +9 -0
  16. package/dist/core/config.d.ts +103 -0
  17. package/dist/core/constants.d.ts +32 -0
  18. package/dist/core/context.d.ts +57 -0
  19. package/dist/core/globalData.d.ts +21 -0
  20. package/dist/core/history.d.ts +24 -0
  21. package/dist/core/ide.d.ts +103 -0
  22. package/dist/core/jsonl.d.ts +37 -0
  23. package/dist/core/llmsContext.d.ts +14 -0
  24. package/dist/core/loop.d.ts +82 -0
  25. package/dist/core/message.d.ts +132 -0
  26. package/dist/core/model.d.ts +79 -0
  27. package/dist/core/output-style/builtin/default.d.ts +2 -0
  28. package/dist/core/output-style/builtin/explanatory.d.ts +2 -0
  29. package/dist/core/output-style/builtin/index.d.ts +6 -0
  30. package/dist/core/output-style/builtin/miao.d.ts +2 -0
  31. package/dist/core/output-style/builtin/minimal.d.ts +2 -0
  32. package/dist/core/output-style/types.d.ts +6 -0
  33. package/dist/core/outputFormat.d.ts +29 -0
  34. package/dist/core/outputStyle.d.ts +43 -0
  35. package/dist/core/paths.d.ts +20 -0
  36. package/dist/core/planSystemPrompt.d.ts +5 -0
  37. package/dist/core/plugin.d.ts +138 -0
  38. package/dist/core/project.d.ts +64 -0
  39. package/dist/core/promptCache.d.ts +3 -0
  40. package/dist/core/query.d.ts +14 -0
  41. package/dist/core/rules.d.ts +8 -0
  42. package/dist/core/systemPrompt.d.ts +9 -0
  43. package/dist/core/thinking-config.d.ts +3 -0
  44. package/dist/core/usage.d.ts +14 -0
  45. package/dist/index.d.ts +16 -0
  46. package/dist/index.js +144432 -0
  47. package/dist/mcp/mcp.d.ts +49 -0
  48. package/dist/modes/builtin.d.ts +34 -0
  49. package/dist/modes/index.d.ts +8 -0
  50. package/dist/modes/registry.d.ts +18 -0
  51. package/dist/modes/types.d.ts +51 -0
  52. package/dist/platform/index.d.ts +5 -0
  53. package/dist/platform/node.d.ts +28 -0
  54. package/dist/platform/types.d.ts +41 -0
  55. package/dist/session/session.d.ts +43 -0
  56. package/dist/skill/skill.d.ts +79 -0
  57. package/dist/tools/tool.d.ts +119 -0
  58. package/dist/tools/tools/askUserQuestion.d.ts +48 -0
  59. package/dist/tools/tools/bash.d.ts +43 -0
  60. package/dist/tools/tools/edit.d.ts +9 -0
  61. package/dist/tools/tools/fetch.d.ts +9 -0
  62. package/dist/tools/tools/glob.d.ts +7 -0
  63. package/dist/tools/tools/grep.d.ts +22 -0
  64. package/dist/tools/tools/ls.d.ts +6 -0
  65. package/dist/tools/tools/read.d.ts +9 -0
  66. package/dist/tools/tools/skill.d.ts +7 -0
  67. package/dist/tools/tools/task.d.ts +14 -0
  68. package/dist/tools/tools/todo.d.ts +37 -0
  69. package/dist/tools/tools/write.d.ts +7 -0
  70. package/dist/utils/apiKeyRotation.d.ts +2 -0
  71. package/dist/utils/applyEdit.d.ts +17 -0
  72. package/dist/utils/background-detection.d.ts +2 -0
  73. package/dist/utils/dotenv.d.ts +9 -0
  74. package/dist/utils/env.d.ts +6 -0
  75. package/dist/utils/error.d.ts +11 -0
  76. package/dist/utils/execFileNoThrow.d.ts +8 -0
  77. package/dist/utils/files.d.ts +10 -0
  78. package/dist/utils/git.d.ts +163 -0
  79. package/dist/utils/ide.d.ts +27 -0
  80. package/dist/utils/ignore.d.ts +6 -0
  81. package/dist/utils/isLocal.d.ts +1 -0
  82. package/dist/utils/language.d.ts +9 -0
  83. package/dist/utils/list.d.ts +20 -0
  84. package/dist/utils/mergeSystemMessagesMiddleware.d.ts +2 -0
  85. package/dist/utils/messageNormalization.d.ts +22 -0
  86. package/dist/utils/path.d.ts +34 -0
  87. package/dist/utils/prependSystemMessageMiddleware.d.ts +2 -0
  88. package/dist/utils/project.d.ts +1 -0
  89. package/dist/utils/proxy.d.ts +18 -0
  90. package/dist/utils/randomUUID.d.ts +5 -0
  91. package/dist/utils/renderSessionMarkdown.d.ts +10 -0
  92. package/dist/utils/ripgrep.d.ts +16 -0
  93. package/dist/utils/safeFrontMatter.d.ts +11 -0
  94. package/dist/utils/safeParseJson.d.ts +1 -0
  95. package/dist/utils/safeStringify.d.ts +1 -0
  96. package/dist/utils/sanitizeAIResponse.d.ts +30 -0
  97. package/dist/utils/setTerminalTitle.d.ts +1 -0
  98. package/dist/utils/shell-execution.d.ts +44 -0
  99. package/dist/utils/string.d.ts +8 -0
  100. package/dist/utils/symbols.d.ts +14 -0
  101. package/dist/utils/system-encoding.d.ts +40 -0
  102. package/dist/utils/tokenCounter.d.ts +8 -0
  103. package/dist/utils/username.d.ts +1 -0
  104. package/package.json +106 -0
  105. package/src/agent/agent/agentManager.test.ts +124 -0
  106. package/src/agent/agent/agentManager.ts +372 -0
  107. package/src/agent/agent/builtin/common.ts +20 -0
  108. package/src/agent/agent/builtin/explore.ts +53 -0
  109. package/src/agent/agent/builtin/general-purpose.ts +38 -0
  110. package/src/agent/agent/builtin/index.ts +13 -0
  111. package/src/agent/agent/executor.test.ts +339 -0
  112. package/src/agent/agent/executor.ts +224 -0
  113. package/src/agent/agent/types.ts +119 -0
  114. package/src/api/engine.ts +466 -0
  115. package/src/communication/index.ts +18 -0
  116. package/src/communication/messageBus.ts +393 -0
  117. package/src/core/at.ts +315 -0
  118. package/src/core/backgroundTaskManager.ts +129 -0
  119. package/src/core/compact.ts +95 -0
  120. package/src/core/config.ts +441 -0
  121. package/src/core/constants.ts +82 -0
  122. package/src/core/context.ts +214 -0
  123. package/src/core/globalData.ts +77 -0
  124. package/src/core/history.ts +323 -0
  125. package/src/core/ide.ts +325 -0
  126. package/src/core/jsonl.ts +100 -0
  127. package/src/core/llmsContext.ts +117 -0
  128. package/src/core/loop.ts +638 -0
  129. package/src/core/message.ts +304 -0
  130. package/src/core/model.ts +2198 -0
  131. package/src/core/output-style/builtin/default.ts +9 -0
  132. package/src/core/output-style/builtin/explanatory.ts +22 -0
  133. package/src/core/output-style/builtin/index.ts +19 -0
  134. package/src/core/output-style/builtin/miao.ts +22 -0
  135. package/src/core/output-style/builtin/minimal.ts +8 -0
  136. package/src/core/output-style/types.ts +6 -0
  137. package/src/core/outputFormat.ts +93 -0
  138. package/src/core/outputStyle.ts +255 -0
  139. package/src/core/paths.ts +161 -0
  140. package/src/core/planSystemPrompt.ts +46 -0
  141. package/src/core/plugin.ts +299 -0
  142. package/src/core/project.ts +492 -0
  143. package/src/core/promptCache.ts +32 -0
  144. package/src/core/query.ts +46 -0
  145. package/src/core/rules.ts +56 -0
  146. package/src/core/systemPrompt.ts +176 -0
  147. package/src/core/thinking-config.ts +98 -0
  148. package/src/core/usage.ts +68 -0
  149. package/src/index.ts +39 -0
  150. package/src/mcp/mcp.ts +637 -0
  151. package/src/modes/builtin.ts +305 -0
  152. package/src/modes/index.ts +22 -0
  153. package/src/modes/registry.ts +39 -0
  154. package/src/modes/types.ts +56 -0
  155. package/src/platform/index.ts +6 -0
  156. package/src/platform/node.ts +108 -0
  157. package/src/platform/types.ts +54 -0
  158. package/src/plugins/index.ts +15 -0
  159. package/src/session/session.ts +187 -0
  160. package/src/skill/skill.ts +702 -0
  161. package/src/tools/tool.ts +378 -0
  162. package/src/tools/tools/askUserQuestion.ts +134 -0
  163. package/src/tools/tools/bash.test.ts +425 -0
  164. package/src/tools/tools/bash.ts +999 -0
  165. package/src/tools/tools/edit.ts +86 -0
  166. package/src/tools/tools/fetch.ts +129 -0
  167. package/src/tools/tools/glob.ts +69 -0
  168. package/src/tools/tools/grep.test.ts +194 -0
  169. package/src/tools/tools/grep.ts +358 -0
  170. package/src/tools/tools/ls.ts +51 -0
  171. package/src/tools/tools/read.test.ts +169 -0
  172. package/src/tools/tools/read.ts +284 -0
  173. package/src/tools/tools/skill.ts +73 -0
  174. package/src/tools/tools/task.test.ts +262 -0
  175. package/src/tools/tools/task.ts +284 -0
  176. package/src/tools/tools/todo.ts +269 -0
  177. package/src/tools/tools/write.ts +71 -0
  178. package/src/types.d.ts +18 -0
  179. package/src/utils/apiKeyRotation.test.ts +70 -0
  180. package/src/utils/apiKeyRotation.ts +24 -0
  181. package/src/utils/applyEdit.test.ts +388 -0
  182. package/src/utils/applyEdit.ts +547 -0
  183. package/src/utils/background-detection.test.ts +61 -0
  184. package/src/utils/background-detection.ts +58 -0
  185. package/src/utils/dotenv.ts +26 -0
  186. package/src/utils/env.ts +90 -0
  187. package/src/utils/error.ts +38 -0
  188. package/src/utils/execFileNoThrow.ts +49 -0
  189. package/src/utils/files.ts +93 -0
  190. package/src/utils/git.ts +1152 -0
  191. package/src/utils/ide.ts +279 -0
  192. package/src/utils/ignore.ts +275 -0
  193. package/src/utils/isLocal.ts +6 -0
  194. package/src/utils/language.ts +33 -0
  195. package/src/utils/list.ts +200 -0
  196. package/src/utils/mergeSystemMessagesMiddleware.ts +32 -0
  197. package/src/utils/messageNormalization.test.ts +401 -0
  198. package/src/utils/messageNormalization.ts +168 -0
  199. package/src/utils/path.ts +98 -0
  200. package/src/utils/prependSystemMessageMiddleware.ts +16 -0
  201. package/src/utils/project.ts +32 -0
  202. package/src/utils/proxy.ts +102 -0
  203. package/src/utils/randomUUID.ts +11 -0
  204. package/src/utils/renderSessionMarkdown.ts +175 -0
  205. package/src/utils/ripgrep.ts +189 -0
  206. package/src/utils/safeFrontMatter.test.ts +118 -0
  207. package/src/utils/safeFrontMatter.ts +68 -0
  208. package/src/utils/safeParseJson.ts +7 -0
  209. package/src/utils/safeStringify.ts +10 -0
  210. package/src/utils/sanitizeAIResponse.test.ts +135 -0
  211. package/src/utils/sanitizeAIResponse.ts +55 -0
  212. package/src/utils/setTerminalTitle.ts +7 -0
  213. package/src/utils/shell-execution.test.ts +237 -0
  214. package/src/utils/shell-execution.ts +279 -0
  215. package/src/utils/string.ts +13 -0
  216. package/src/utils/symbols.ts +18 -0
  217. package/src/utils/system-encoding.test.ts +164 -0
  218. package/src/utils/system-encoding.ts +296 -0
  219. package/src/utils/tokenCounter.test.ts +38 -0
  220. package/src/utils/tokenCounter.ts +19 -0
  221. package/src/utils/username.ts +21 -0
@@ -0,0 +1,378 @@
1
+ import type { LanguageModelV2FunctionTool } from '@ai-sdk/provider';
2
+ import path from 'pathe';
3
+ import * as z from 'zod';
4
+ import type { Context } from '../core/context';
5
+ import type { ImagePart, TextPart } from '../core/message';
6
+ import { resolveModelWithContext } from '../core/model';
7
+ import { createAskUserQuestionTool } from './tools/askUserQuestion';
8
+ import {
9
+ createBashOutputTool,
10
+ createBashTool,
11
+ createKillBashTool,
12
+ } from './tools/bash';
13
+ import { createEditTool } from './tools/edit';
14
+ import { createFetchTool } from './tools/fetch';
15
+ import { createGlobTool } from './tools/glob';
16
+ import { createGrepTool } from './tools/grep';
17
+ import { createLSTool } from './tools/ls';
18
+ import { createReadTool } from './tools/read';
19
+ import { createSkillTool } from './tools/skill';
20
+ import { createTaskTool } from './tools/task';
21
+ import { createTodoTool, type TodoItem } from './tools/todo';
22
+ import { createWriteTool } from './tools/write';
23
+
24
+ type ResolveToolsOpts = {
25
+ context: Context;
26
+ sessionId: string;
27
+ write?: boolean;
28
+ todo?: boolean;
29
+ askUserQuestion?: boolean;
30
+ signal?: AbortSignal;
31
+ task?: boolean;
32
+ };
33
+
34
+ export async function resolveTools(opts: ResolveToolsOpts) {
35
+ const { cwd, productName, paths } = opts.context;
36
+ const sessionId = opts.sessionId;
37
+ const model = (
38
+ await resolveModelWithContext(
39
+ opts.context.config.smallModel || opts.context.config.model,
40
+ opts.context,
41
+ )
42
+ ).model!;
43
+ const hasSkills =
44
+ opts.context.skillManager &&
45
+ opts.context.skillManager.getSkills().length > 0;
46
+ const readonlyTools = [
47
+ createReadTool({ cwd, productName }),
48
+ createLSTool({ cwd }),
49
+ createGlobTool({ cwd }),
50
+ createGrepTool({ cwd }),
51
+ createFetchTool({ model, fetch: opts.context.fetch }),
52
+ ...(hasSkills
53
+ ? [createSkillTool({ skillManager: opts.context.skillManager! })]
54
+ : []),
55
+ ];
56
+ const askUserQuestionTools = opts.askUserQuestion
57
+ ? [createAskUserQuestionTool()]
58
+ : [];
59
+ const writeTools = opts.write
60
+ ? [
61
+ createWriteTool({ cwd }),
62
+ createEditTool({ cwd }),
63
+ createBashTool({
64
+ cwd,
65
+ backgroundTaskManager: opts.context.backgroundTaskManager,
66
+ messageBus: opts.context.messageBus,
67
+ }),
68
+ ]
69
+ : [];
70
+ const todoTools = (() => {
71
+ if (!opts.todo) return [];
72
+ const { todoWriteTool } = createTodoTool({
73
+ filePath: path.join(paths.globalConfigDir, 'todos', `${sessionId}.json`),
74
+ });
75
+ return [todoWriteTool];
76
+ })();
77
+ // Bash background tools
78
+ const backgroundTools: any[] = opts.write
79
+ ? [
80
+ createBashOutputTool({
81
+ backgroundTaskManager: opts.context.backgroundTaskManager,
82
+ }),
83
+ createKillBashTool({
84
+ backgroundTaskManager: opts.context.backgroundTaskManager,
85
+ }),
86
+ ]
87
+ : [];
88
+
89
+ const mcpTools = await getMcpTools(opts.context);
90
+
91
+ const allTools = [
92
+ ...readonlyTools,
93
+ ...askUserQuestionTools,
94
+ ...writeTools,
95
+ ...todoTools,
96
+ ...backgroundTools,
97
+ ...mcpTools,
98
+ ];
99
+
100
+ const toolsConfig = opts.context.config.tools;
101
+ const availableTools = (() => {
102
+ if (!toolsConfig || Object.keys(toolsConfig).length === 0) {
103
+ return allTools;
104
+ }
105
+ return allTools.filter((tool) => {
106
+ // Check if the tool is disabled (only explicitly set to false will disable)
107
+ const isDisabled = toolsConfig[tool.name] === false;
108
+ return !isDisabled;
109
+ });
110
+ })();
111
+
112
+ const taskTools = (() => {
113
+ // Task tool is only available in quiet mode
114
+ if (!opts.task) return [];
115
+ if (!opts.context.agentManager) return [];
116
+ const tool = createTaskTool({
117
+ context: opts.context,
118
+ tools: availableTools,
119
+ sessionId: opts.sessionId,
120
+ signal: opts.signal,
121
+ });
122
+ if (toolsConfig && toolsConfig[tool.name] === false) {
123
+ return [];
124
+ }
125
+ return [tool];
126
+ })();
127
+
128
+ return [...availableTools, ...taskTools];
129
+ }
130
+
131
+ async function getMcpTools(context: Context): Promise<Tool[]> {
132
+ try {
133
+ const mcpManager = context.mcpManager;
134
+ await mcpManager.initAsync();
135
+ return await mcpManager.getAllTools();
136
+ } catch (error) {
137
+ console.warn('Failed to load MCP tools:', error);
138
+ return [];
139
+ }
140
+ }
141
+
142
+ export class Tools {
143
+ tools: Record<string, Tool>;
144
+ constructor(tools: Tool[]) {
145
+ this.tools = tools.reduce(
146
+ (acc, tool) => {
147
+ acc[tool.name] = tool;
148
+ return acc;
149
+ },
150
+ {} as Record<string, Tool>,
151
+ );
152
+ }
153
+
154
+ get(toolName: string) {
155
+ return this.tools[toolName];
156
+ }
157
+
158
+ length() {
159
+ return Object.keys(this.tools).length;
160
+ }
161
+
162
+ async invoke(
163
+ toolName: string,
164
+ args: string,
165
+ toolCallId: string,
166
+ ): Promise<ToolResult> {
167
+ const tool = this.tools[toolName];
168
+ if (!tool) {
169
+ return {
170
+ llmContent: `Tool ${toolName} not found`,
171
+ isError: true,
172
+ };
173
+ }
174
+ // // @ts-expect-error
175
+ // const result = validateToolParams(tool.parameters, args);
176
+ // if (!result.success) {
177
+ // return {
178
+ // llmContent: `Invalid tool parameters: ${result.error}`,
179
+ // isError: true,
180
+ // };
181
+ // }
182
+ let argsObj: any;
183
+ try {
184
+ argsObj = JSON.parse(args);
185
+ } catch (error) {
186
+ return {
187
+ llmContent: `Tool parameters parse failed: ${error}`,
188
+ isError: true,
189
+ };
190
+ }
191
+ return await tool.execute(argsObj, toolCallId);
192
+ }
193
+
194
+ toLanguageV2Tools(): LanguageModelV2FunctionTool[] {
195
+ return Object.entries(this.tools).map(([key, tool]) => {
196
+ // parameters of mcp tools is not zod object
197
+ const isMCP = key.startsWith('mcp__');
198
+ const schema = isMCP ? tool.parameters : z.toJSONSchema(tool.parameters);
199
+ // some providers have a limit on the description length, so we need to truncate it
200
+ // e.g. megallm.io has a limit of 1024 characters
201
+ const limit = process.env.TOOL_DESCRIPTION_LIMIT
202
+ ? Math.floor(parseInt(process.env.TOOL_DESCRIPTION_LIMIT, 10))
203
+ : 0;
204
+ const desc =
205
+ limit > 0 && tool.description.length > limit
206
+ ? `${tool.description.slice(0, limit - 3)}...`
207
+ : tool.description;
208
+ return {
209
+ type: 'function',
210
+ name: key,
211
+ description: desc,
212
+ inputSchema: schema as LanguageModelV2FunctionTool['inputSchema'],
213
+ providerOptions: {},
214
+ };
215
+ });
216
+ }
217
+ }
218
+
219
+ // function validateToolParams(schema: z.ZodObject<any>, params: string) {
220
+ // try {
221
+ // if (isZodObject(schema)) {
222
+ // const parsedParams = JSON.parse(params);
223
+ // const result = schema.safeParse(parsedParams);
224
+ // if (!result.success) {
225
+ // return {
226
+ // success: false,
227
+ // error: `Parameter validation failed: ${result.error.message}`,
228
+ // };
229
+ // }
230
+ // return {
231
+ // success: true,
232
+ // message: 'Tool parameters validated successfully',
233
+ // };
234
+ // }
235
+ // return {
236
+ // success: true,
237
+ // message: 'Tool parameters validated successfully',
238
+ // };
239
+ // } catch (error) {
240
+ // return {
241
+ // success: false,
242
+ // error: error,
243
+ // };
244
+ // }
245
+ // }
246
+
247
+ export type ToolUse = {
248
+ name: string;
249
+ params: Record<string, any>;
250
+ callId: string;
251
+ };
252
+
253
+ export type ToolUseResult = {
254
+ toolUse: ToolUse;
255
+ result: any;
256
+ approved: boolean;
257
+ };
258
+
259
+ export interface Tool<TSchema extends z.ZodTypeAny = z.ZodTypeAny> {
260
+ name: string;
261
+ description: string;
262
+ getDescription?: ({
263
+ params,
264
+ cwd,
265
+ }: {
266
+ params: z.output<TSchema>;
267
+ cwd: string;
268
+ }) => string;
269
+ displayName?: string;
270
+ execute: (
271
+ params: z.output<TSchema>,
272
+ toolCallId?: string,
273
+ ) => Promise<ToolResult> | ToolResult;
274
+ approval?: ToolApprovalInfo;
275
+ parameters: TSchema;
276
+ }
277
+
278
+ type ApprovalContext = {
279
+ toolName: string;
280
+ params: Record<string, any>;
281
+ approvalMode: string;
282
+ context: any;
283
+ };
284
+
285
+ export type ApprovalCategory = 'read' | 'write' | 'command' | 'network' | 'ask';
286
+
287
+ type ToolApprovalInfo = {
288
+ needsApproval?: (context: ApprovalContext) => Promise<boolean> | boolean;
289
+ category?: ApprovalCategory;
290
+ };
291
+
292
+ type TodoWriteReturnDisplay = {
293
+ type: 'todo_write';
294
+ oldTodos: TodoItem[];
295
+ newTodos: TodoItem[];
296
+ };
297
+
298
+ type DiffViewerReturnDisplay = {
299
+ type: 'diff_viewer';
300
+ originalContent: string | { inputKey: string };
301
+ newContent: string | { inputKey: string };
302
+ filePath: string;
303
+ [key: string]: any;
304
+ };
305
+
306
+ type AgentResultReturnDisplay = {
307
+ type: 'agent_result';
308
+ agentId: string;
309
+ agentType: string;
310
+ description: string;
311
+ prompt: string;
312
+ content: string;
313
+ stats: {
314
+ toolCalls: number;
315
+ duration: number;
316
+ tokens: {
317
+ input: number;
318
+ output: number;
319
+ };
320
+ };
321
+ status: 'completed' | 'failed';
322
+ };
323
+
324
+ export type ReturnDisplay =
325
+ | string
326
+ | DiffViewerReturnDisplay
327
+ | TodoWriteReturnDisplay
328
+ | AgentResultReturnDisplay;
329
+
330
+ export type ToolResult = {
331
+ llmContent: string | (TextPart | ImagePart)[];
332
+ returnDisplay?: ReturnDisplay;
333
+ isError?: boolean;
334
+ metadata?: {
335
+ agentId?: string;
336
+ agentType?: string;
337
+ [key: string]: any;
338
+ };
339
+ };
340
+
341
+ export function createTool<TSchema extends z.ZodTypeAny>(config: {
342
+ name: string;
343
+ displayName?: string;
344
+ description: string;
345
+ parameters: TSchema;
346
+ execute: (
347
+ params: z.output<TSchema>,
348
+ toolCallId?: string,
349
+ ) => Promise<ToolResult> | ToolResult;
350
+ approval?: ToolApprovalInfo;
351
+ getDescription?: ({
352
+ params,
353
+ cwd,
354
+ }: {
355
+ params: z.output<TSchema>;
356
+ cwd: string;
357
+ }) => string;
358
+ }): Tool<TSchema> {
359
+ return {
360
+ name: config.name,
361
+ displayName: config.displayName,
362
+ description: config.description,
363
+ getDescription: config.getDescription,
364
+ parameters: config.parameters,
365
+ execute: config.execute,
366
+ approval: config.approval,
367
+ };
368
+ }
369
+
370
+ export type ToolParams = Record<string, unknown>;
371
+
372
+ export type ToolApprovalResult =
373
+ | boolean
374
+ | {
375
+ approved: boolean;
376
+ params?: ToolParams;
377
+ denyReason?: string;
378
+ };
@@ -0,0 +1,134 @@
1
+ import { z } from 'zod';
2
+ import { TOOL_NAMES } from '../../core/constants';
3
+ import { createTool } from '../tool';
4
+
5
+ const MAX_HEADER_LENGTH = 12;
6
+
7
+ const QuestionOptionSchema = z.object({
8
+ label: z
9
+ .string()
10
+ .describe(
11
+ 'The display text for this option that the user will see and select. Should be concise (1-5 words) and clearly describe the choice.',
12
+ ),
13
+ description: z
14
+ .string()
15
+ .describe(
16
+ 'Explanation of what this option means or what will happen if chosen. Useful for providing context about trade-offs or implications.',
17
+ ),
18
+ });
19
+
20
+ const QuestionSchema = z.object({
21
+ question: z
22
+ .string()
23
+ .describe(
24
+ 'The complete question to ask the user. Should be clear, specific, and end with a question mark. Example: "Which library should we use for date formatting?" If multiSelect is true, phrase it accordingly, e.g. "Which features do you want to enable?',
25
+ ),
26
+ header: z
27
+ .string()
28
+ .describe(
29
+ `Very short label displayed as a chip/tag (max ${MAX_HEADER_LENGTH} chars). Examples: "Auth method", "Library", "Approach".`,
30
+ ),
31
+ options: z
32
+ .array(QuestionOptionSchema)
33
+ .min(2)
34
+ .max(4)
35
+ .describe(
36
+ `The available choices for this question. Must have 2-4 options. Each option should be a distinct, mutually exclusive choice (unless multiSelect is enabled). There should be no 'Other' option, that will be provided automatically.`,
37
+ ),
38
+ multiSelect: z
39
+ .boolean()
40
+ .optional()
41
+ .default(false)
42
+ .describe(
43
+ 'Set to true to allow the user to select multiple options instead of just one. Use when choices are not mutually exclusive.',
44
+ ),
45
+ });
46
+
47
+ const AskUserQuestionInputSchema = z
48
+ .object({
49
+ questions: z
50
+ .array(QuestionSchema)
51
+ .min(1)
52
+ .max(4)
53
+ .describe('Questions to ask the user (1-4 questions)'),
54
+ answers: z
55
+ .array(z.object({ question: z.string(), answer: z.string() }))
56
+ .optional()
57
+ .describe('User answers collected by the permission component'),
58
+ })
59
+ .refine(
60
+ (data) => {
61
+ const questionTexts = data.questions.map((q) => q.question);
62
+ return questionTexts.length === new Set(questionTexts).size;
63
+ },
64
+ {
65
+ message:
66
+ 'Question texts must be unique, option labels must be unique within each question',
67
+ },
68
+ )
69
+ .refine(
70
+ (data) => {
71
+ for (const question of data.questions) {
72
+ const labels = question.options.map((o) => o.label);
73
+ if (labels.length !== new Set(labels).size) {
74
+ return false;
75
+ }
76
+ }
77
+ return true;
78
+ },
79
+ {
80
+ message: 'Option labels must be unique within each question',
81
+ },
82
+ );
83
+
84
+ export type QuestionOption = z.infer<typeof QuestionOptionSchema>;
85
+ export type Question = z.infer<typeof QuestionSchema>;
86
+ export type AskUserQuestionInput = z.infer<typeof AskUserQuestionInputSchema>;
87
+
88
+ const TOOL_DESCRIPTION = `
89
+ Use this tool when you need to ask the user questions during execution. This allows you to:
90
+ 1. Gather user preferences or requirements
91
+ 2. Clarify ambiguous instructions
92
+ 3. Get decisions on implementation choices as you work
93
+ 4. Offer choices to the user about what direction to take.
94
+
95
+ Usage notes:
96
+ - Users will always be able to select "Other" to provide custom text input
97
+ - Use multiSelect: true to allow multiple answers to be selected for a question
98
+ - If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end
99
+ of the label
100
+ `;
101
+
102
+ export function createAskUserQuestionTool() {
103
+ return createTool({
104
+ name: TOOL_NAMES.ASK_USER_QUESTION,
105
+ description: TOOL_DESCRIPTION,
106
+ parameters: AskUserQuestionInputSchema,
107
+ async execute({ questions, answers }) {
108
+ if (!answers || answers.length === 0) {
109
+ return {
110
+ isError: true,
111
+ llmContent: 'No answers provided by user',
112
+ };
113
+ }
114
+
115
+ const answerSummary = answers
116
+ .map(({ question, answer }) => `"${question}" = "${answer}"`)
117
+ .join(', ');
118
+
119
+ const displayText = answers
120
+ .map(({ question, answer }) => `· ${question} → ${answer}`)
121
+ .join('\n');
122
+
123
+ return {
124
+ llmContent: `User has answered your questions: ${answerSummary}. You can now continue with the user's answers in mind.`,
125
+ returnDisplay: `User has answered your questions:\n${displayText}`,
126
+ };
127
+ },
128
+ approval: {
129
+ // Always require user input, even in yolo mode
130
+ category: 'ask',
131
+ needsApproval: () => true,
132
+ },
133
+ });
134
+ }