@clinebot/agents 0.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 (90) hide show
  1. package/README.md +145 -0
  2. package/dist/agent-input.d.ts +2 -0
  3. package/dist/agent.d.ts +56 -0
  4. package/dist/extensions.d.ts +21 -0
  5. package/dist/hooks/engine.d.ts +42 -0
  6. package/dist/hooks/index.d.ts +2 -0
  7. package/dist/hooks/lifecycle.d.ts +5 -0
  8. package/dist/hooks/node.d.ts +2 -0
  9. package/dist/hooks/subprocess-runner.d.ts +16 -0
  10. package/dist/hooks/subprocess.d.ts +268 -0
  11. package/dist/index.browser.d.ts +1 -0
  12. package/dist/index.browser.js +49 -0
  13. package/dist/index.d.ts +15 -0
  14. package/dist/index.js +49 -0
  15. package/dist/index.node.d.ts +5 -0
  16. package/dist/index.node.js +49 -0
  17. package/dist/mcp/index.d.ts +4 -0
  18. package/dist/mcp/policies.d.ts +14 -0
  19. package/dist/mcp/tools.d.ts +9 -0
  20. package/dist/mcp/types.d.ts +35 -0
  21. package/dist/message-builder.d.ts +31 -0
  22. package/dist/prompts/cline.d.ts +1 -0
  23. package/dist/prompts/index.d.ts +1 -0
  24. package/dist/runtime/agent-runtime-bus.d.ts +13 -0
  25. package/dist/runtime/conversation-store.d.ts +16 -0
  26. package/dist/runtime/lifecycle-orchestrator.d.ts +28 -0
  27. package/dist/runtime/tool-orchestrator.d.ts +39 -0
  28. package/dist/runtime/turn-processor.d.ts +21 -0
  29. package/dist/teams/index.d.ts +3 -0
  30. package/dist/teams/multi-agent.d.ts +566 -0
  31. package/dist/teams/spawn-agent-tool.d.ts +85 -0
  32. package/dist/teams/team-tools.d.ts +51 -0
  33. package/dist/tools/ask-question.d.ts +12 -0
  34. package/dist/tools/create.d.ts +59 -0
  35. package/dist/tools/execution.d.ts +61 -0
  36. package/dist/tools/formatting.d.ts +20 -0
  37. package/dist/tools/index.d.ts +11 -0
  38. package/dist/tools/registry.d.ts +26 -0
  39. package/dist/tools/validation.d.ts +27 -0
  40. package/dist/types.d.ts +826 -0
  41. package/package.json +54 -0
  42. package/src/agent-input.ts +116 -0
  43. package/src/agent.test.ts +931 -0
  44. package/src/agent.ts +1050 -0
  45. package/src/example.test.ts +564 -0
  46. package/src/extensions.ts +337 -0
  47. package/src/hooks/engine.test.ts +163 -0
  48. package/src/hooks/engine.ts +537 -0
  49. package/src/hooks/index.ts +6 -0
  50. package/src/hooks/lifecycle.ts +239 -0
  51. package/src/hooks/node.ts +18 -0
  52. package/src/hooks/subprocess-runner.ts +140 -0
  53. package/src/hooks/subprocess.test.ts +180 -0
  54. package/src/hooks/subprocess.ts +620 -0
  55. package/src/index.browser.ts +1 -0
  56. package/src/index.node.ts +21 -0
  57. package/src/index.ts +133 -0
  58. package/src/mcp/index.ts +17 -0
  59. package/src/mcp/policies.test.ts +51 -0
  60. package/src/mcp/policies.ts +53 -0
  61. package/src/mcp/tools.test.ts +76 -0
  62. package/src/mcp/tools.ts +60 -0
  63. package/src/mcp/types.ts +41 -0
  64. package/src/message-builder.test.ts +175 -0
  65. package/src/message-builder.ts +429 -0
  66. package/src/prompts/cline.ts +49 -0
  67. package/src/prompts/index.ts +1 -0
  68. package/src/runtime/agent-runtime-bus.ts +53 -0
  69. package/src/runtime/conversation-store.ts +61 -0
  70. package/src/runtime/lifecycle-orchestrator.ts +90 -0
  71. package/src/runtime/tool-orchestrator.ts +177 -0
  72. package/src/runtime/turn-processor.ts +250 -0
  73. package/src/streaming.test.ts +197 -0
  74. package/src/streaming.ts +307 -0
  75. package/src/teams/index.ts +63 -0
  76. package/src/teams/multi-agent.lifecycle.test.ts +48 -0
  77. package/src/teams/multi-agent.ts +1866 -0
  78. package/src/teams/spawn-agent-tool.test.ts +172 -0
  79. package/src/teams/spawn-agent-tool.ts +223 -0
  80. package/src/teams/team-tools.test.ts +448 -0
  81. package/src/teams/team-tools.ts +929 -0
  82. package/src/tools/ask-question.ts +78 -0
  83. package/src/tools/create.ts +104 -0
  84. package/src/tools/execution.ts +311 -0
  85. package/src/tools/formatting.ts +73 -0
  86. package/src/tools/index.ts +45 -0
  87. package/src/tools/registry.ts +52 -0
  88. package/src/tools/tools.test.ts +292 -0
  89. package/src/tools/validation.ts +73 -0
  90. package/src/types.ts +966 -0
@@ -0,0 +1,78 @@
1
+ import {
2
+ type Tool,
3
+ type ToolContext,
4
+ validateWithZod,
5
+ zodToJsonSchema,
6
+ } from "@clinebot/shared";
7
+ import { z } from "zod";
8
+ import { createTool } from "./create.js";
9
+
10
+ export const AskQuestionInputSchema = z.object({
11
+ question: z
12
+ .string()
13
+ .min(1)
14
+ .describe(
15
+ 'The single question to ask the user. E.g. "How can I help you?"',
16
+ ),
17
+ options: z
18
+ .array(z.string().min(1))
19
+ .min(2)
20
+ .max(5)
21
+ .describe(
22
+ "Array of 2-5 user-selectable answer options for the single question",
23
+ ),
24
+ });
25
+
26
+ export type AskQuestionInput = z.infer<typeof AskQuestionInputSchema>;
27
+
28
+ export type AskQuestionExecutor = (
29
+ question: string,
30
+ options: string[],
31
+ context: ToolContext,
32
+ ) => Promise<string>;
33
+
34
+ export interface AskQuestionToolConfig {
35
+ askQuestionTimeoutMs?: number;
36
+ }
37
+
38
+ function withTimeout<T>(
39
+ promise: Promise<T>,
40
+ ms: number,
41
+ message: string,
42
+ ): Promise<T> {
43
+ return Promise.race([
44
+ promise,
45
+ new Promise<never>((_, reject) => {
46
+ setTimeout(() => reject(new Error(message)), ms);
47
+ }),
48
+ ]);
49
+ }
50
+
51
+ export function createAskQuestionTool(
52
+ executor: AskQuestionExecutor,
53
+ config: AskQuestionToolConfig = {},
54
+ ): Tool<AskQuestionInput, string> {
55
+ const timeoutMs = config.askQuestionTimeoutMs ?? 15000;
56
+
57
+ return createTool<AskQuestionInput, string>({
58
+ name: "ask_question",
59
+ description:
60
+ "Ask user a question for clarifying or gathering information needed to complete the task. " +
61
+ "For example, ask the user clarifying questions about a key implementation decision. " +
62
+ "You should only ask one question. " +
63
+ "Provide an array of 2-5 options for the user to choose from. " +
64
+ "Never include an option to toggle to Act mode.",
65
+ inputSchema: zodToJsonSchema(AskQuestionInputSchema),
66
+ timeoutMs,
67
+ retryable: false,
68
+ maxRetries: 0,
69
+ execute: async (input, context) => {
70
+ const validatedInput = validateWithZod(AskQuestionInputSchema, input);
71
+ return withTimeout(
72
+ executor(validatedInput.question, validatedInput.options, context),
73
+ timeoutMs,
74
+ `ask_question timed out after ${timeoutMs}ms`,
75
+ );
76
+ },
77
+ });
78
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Tool Creation Utilities
3
+ *
4
+ * Functions for creating tools with proper typing.
5
+ */
6
+
7
+ import type { providers as LlmsProviders } from "@clinebot/llms";
8
+ import { type Tool, type ToolContext, zodToJsonSchema } from "@clinebot/shared";
9
+ import { z } from "zod";
10
+
11
+ /**
12
+ * Create a tool with proper typing
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const readFile = createTool({
17
+ * name: "read_file",
18
+ * description: "Read the contents of a file",
19
+ * inputSchema: {
20
+ * type: "object",
21
+ * properties: {
22
+ * path: { type: "string", description: "File path to read" }
23
+ * },
24
+ * required: ["path"]
25
+ * },
26
+ * execute: async ({ path }) => {
27
+ * const content = await fs.readFile(path, "utf-8")
28
+ * return { content }
29
+ * }
30
+ * })
31
+ * ```
32
+ */
33
+ export function createTool<TInput, TOutput>(config: {
34
+ name: string;
35
+ description: string;
36
+ inputSchema: Record<string, unknown>;
37
+ execute: (input: TInput, context: ToolContext) => Promise<TOutput>;
38
+ timeoutMs?: number;
39
+ retryable?: boolean;
40
+ maxRetries?: number;
41
+ }): Tool<TInput, TOutput>;
42
+ export function createTool<TSchema extends z.ZodTypeAny, TOutput>(config: {
43
+ name: string;
44
+ description: string;
45
+ inputSchema: TSchema;
46
+ execute: (input: z.infer<TSchema>, context: ToolContext) => Promise<TOutput>;
47
+ timeoutMs?: number;
48
+ retryable?: boolean;
49
+ maxRetries?: number;
50
+ }): Tool<z.infer<TSchema>, TOutput>;
51
+ export function createTool<TInput, TOutput>(config: {
52
+ name: string;
53
+ description: string;
54
+ inputSchema: Record<string, unknown> | z.ZodTypeAny;
55
+ execute: (input: TInput, context: ToolContext) => Promise<TOutput>;
56
+ timeoutMs?: number;
57
+ retryable?: boolean;
58
+ maxRetries?: number;
59
+ }): Tool<TInput, TOutput> {
60
+ const inputSchema =
61
+ config.inputSchema instanceof z.ZodType
62
+ ? zodToJsonSchema(config.inputSchema)
63
+ : config.inputSchema;
64
+
65
+ return {
66
+ name: config.name,
67
+ description: config.description,
68
+ inputSchema,
69
+ execute: config.execute,
70
+ timeoutMs: config.timeoutMs ?? 30000,
71
+ retryable: config.retryable ?? true,
72
+ maxRetries: config.maxRetries ?? 2,
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Convert a Tool to a ToolDefinition for the provider
78
+ *
79
+ * This transforms our internal Tool format into the format expected
80
+ * by the provider's API.
81
+ */
82
+ export function toToolDefinition(tool: Tool): LlmsProviders.ToolDefinition {
83
+ return {
84
+ name: tool.name,
85
+ description: tool.description,
86
+ inputSchema: {
87
+ type: "object",
88
+ properties: tool.inputSchema.properties as Record<string, unknown>,
89
+ required: tool.inputSchema.required as string[] | undefined,
90
+ additionalProperties: tool.inputSchema.additionalProperties as
91
+ | boolean
92
+ | undefined,
93
+ },
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Convert an array of Tools to ToolDefinitions
99
+ */
100
+ export function toToolDefinitions(
101
+ tools: Tool[],
102
+ ): LlmsProviders.ToolDefinition[] {
103
+ return tools.map(toToolDefinition);
104
+ }
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Tool Execution
3
+ *
4
+ * Functions for executing tools with error handling, timeouts, and retries.
5
+ */
6
+
7
+ import type { Tool, ToolCallRecord, ToolContext } from "@clinebot/shared";
8
+ import type { PendingToolCall } from "../types.js";
9
+
10
+ export interface ToolExecutionObserver {
11
+ onToolCallStart?: (call: PendingToolCall) => Promise<void> | void;
12
+ onToolCallEnd?: (record: ToolCallRecord) => Promise<void> | void;
13
+ }
14
+
15
+ export interface ToolExecutionAuthorizer {
16
+ authorize?: (
17
+ call: PendingToolCall,
18
+ context: ToolContext,
19
+ ) =>
20
+ | Promise<{ allowed: true } | { allowed: false; reason: string }>
21
+ | { allowed: true }
22
+ | { allowed: false; reason: string };
23
+ }
24
+
25
+ export interface ToolExecutionOptions {
26
+ maxConcurrency?: number;
27
+ }
28
+
29
+ /**
30
+ * Execute a single tool with error handling and timeout
31
+ *
32
+ * @param tool - The tool to execute
33
+ * @param input - The input to pass to the tool
34
+ * @param context - The execution context
35
+ * @returns A record of the tool call execution
36
+ */
37
+ export async function executeTool(
38
+ tool: Tool,
39
+ input: unknown,
40
+ context: ToolContext,
41
+ ): Promise<{ output: unknown; error?: string; durationMs: number }> {
42
+ const startTime = Date.now();
43
+ const timeoutMs = tool.timeoutMs ?? 30000;
44
+ let timeoutId: ReturnType<typeof setTimeout> | undefined;
45
+ let abortHandler: (() => void) | undefined;
46
+
47
+ // Create a timeout promise
48
+ const timeoutPromise = new Promise<never>((_, reject) => {
49
+ timeoutId = setTimeout(() => {
50
+ reject(new Error(`Tool execution timed out after ${timeoutMs}ms`));
51
+ }, timeoutMs);
52
+ });
53
+
54
+ // Create abort handling
55
+ const abortPromise = context.abortSignal
56
+ ? new Promise<never>((_, reject) => {
57
+ if (context.abortSignal?.aborted) {
58
+ reject(new Error("Tool execution was aborted"));
59
+ return;
60
+ }
61
+ abortHandler = () => {
62
+ reject(new Error("Tool execution was aborted"));
63
+ };
64
+ context.abortSignal?.addEventListener("abort", abortHandler);
65
+ })
66
+ : null;
67
+
68
+ try {
69
+ // Execute with timeout and optional abort
70
+ const promises: Promise<unknown>[] = [
71
+ tool.execute(input, context),
72
+ timeoutPromise,
73
+ ];
74
+ if (abortPromise) {
75
+ promises.push(abortPromise);
76
+ }
77
+
78
+ const output = await Promise.race(promises);
79
+ const durationMs = Date.now() - startTime;
80
+
81
+ return { output, durationMs };
82
+ } catch (error) {
83
+ const durationMs = Date.now() - startTime;
84
+ const errorMessage = error instanceof Error ? error.message : String(error);
85
+
86
+ return { output: null, error: errorMessage, durationMs };
87
+ } finally {
88
+ if (timeoutId) {
89
+ clearTimeout(timeoutId);
90
+ }
91
+ if (context.abortSignal && abortHandler) {
92
+ context.abortSignal.removeEventListener("abort", abortHandler);
93
+ }
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Execute a tool with retries
99
+ */
100
+ export async function executeToolWithRetry(
101
+ tool: Tool,
102
+ input: unknown,
103
+ context: ToolContext,
104
+ ): Promise<{ output: unknown; error?: string; durationMs: number }> {
105
+ const maxRetries = tool.maxRetries ?? 2;
106
+ let lastResult: {
107
+ output: unknown;
108
+ error?: string;
109
+ durationMs: number;
110
+ } | null = null;
111
+
112
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
113
+ // Check for abort before each attempt
114
+ if (context.abortSignal?.aborted) {
115
+ return {
116
+ output: null,
117
+ error: "Tool execution was aborted",
118
+ durationMs: lastResult?.durationMs ?? 0,
119
+ };
120
+ }
121
+
122
+ const result = await executeTool(tool, input, context);
123
+ lastResult = result;
124
+
125
+ // If no error, return immediately
126
+ if (!result.error) {
127
+ return result;
128
+ }
129
+
130
+ // If tool is not retryable or we've exhausted retries, return the error
131
+ if (!tool.retryable || attempt === maxRetries) {
132
+ return result;
133
+ }
134
+
135
+ // Wait a bit before retrying (exponential backoff)
136
+ const delayMs = Math.min(1000 * 2 ** attempt, 10000);
137
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
138
+ }
139
+
140
+ return lastResult!;
141
+ }
142
+
143
+ /**
144
+ * Execute multiple tools in parallel
145
+ *
146
+ * @param toolRegistry - Map of tools by name
147
+ * @param calls - Array of tool calls to execute
148
+ * @param context - The execution context
149
+ * @returns Array of tool call records
150
+ */
151
+ export async function executeToolsInParallel(
152
+ toolRegistry: Map<string, Tool>,
153
+ calls: PendingToolCall[],
154
+ context: ToolContext,
155
+ observer?: ToolExecutionObserver,
156
+ authorizer?: ToolExecutionAuthorizer,
157
+ options?: ToolExecutionOptions,
158
+ ): Promise<ToolCallRecord[]> {
159
+ const executeCall = async (
160
+ call: PendingToolCall,
161
+ ): Promise<ToolCallRecord> => {
162
+ const startedAt = new Date();
163
+ await observer?.onToolCallStart?.(call);
164
+ const tool = toolRegistry.get(call.name);
165
+
166
+ if (!tool) {
167
+ const endedAt = new Date();
168
+ const record = {
169
+ id: call.id,
170
+ name: call.name,
171
+ input: call.input,
172
+ output: null,
173
+ error: `Unknown tool: ${call.name}`,
174
+ durationMs: endedAt.getTime() - startedAt.getTime(),
175
+ startedAt,
176
+ endedAt,
177
+ };
178
+ await observer?.onToolCallEnd?.(record);
179
+ return record;
180
+ }
181
+
182
+ const authorization = await authorizer?.authorize?.(call, context);
183
+ if (authorization && !authorization.allowed) {
184
+ const endedAt = new Date();
185
+ const record = {
186
+ id: call.id,
187
+ name: call.name,
188
+ input: call.input,
189
+ output: null,
190
+ error: authorization.reason,
191
+ durationMs: endedAt.getTime() - startedAt.getTime(),
192
+ startedAt,
193
+ endedAt,
194
+ };
195
+ await observer?.onToolCallEnd?.(record);
196
+ return record;
197
+ }
198
+
199
+ const result = await executeToolWithRetry(tool, call.input, context);
200
+ const endedAt = new Date();
201
+
202
+ const record = {
203
+ id: call.id,
204
+ name: call.name,
205
+ input: call.input,
206
+ output: result.output,
207
+ error: result.error,
208
+ durationMs: result.durationMs,
209
+ startedAt,
210
+ endedAt,
211
+ };
212
+ await observer?.onToolCallEnd?.(record);
213
+ return record;
214
+ };
215
+
216
+ const maxConcurrency = Math.max(
217
+ 1,
218
+ options?.maxConcurrency ?? (calls.length || 1),
219
+ );
220
+ const results = new Array<ToolCallRecord>(calls.length);
221
+ let nextIndex = 0;
222
+ const workerCount = Math.min(maxConcurrency, calls.length);
223
+ const workers = Array.from({ length: workerCount }, async () => {
224
+ while (true) {
225
+ const index = nextIndex++;
226
+ if (index >= calls.length) {
227
+ return;
228
+ }
229
+ results[index] = await executeCall(calls[index]);
230
+ }
231
+ });
232
+ await Promise.all(workers);
233
+ return results;
234
+ }
235
+
236
+ /**
237
+ * Execute tools sequentially (for cases where order matters)
238
+ */
239
+ export async function executeToolsSequentially(
240
+ toolRegistry: Map<string, Tool>,
241
+ calls: PendingToolCall[],
242
+ context: ToolContext,
243
+ observer?: ToolExecutionObserver,
244
+ authorizer?: ToolExecutionAuthorizer,
245
+ ): Promise<ToolCallRecord[]> {
246
+ const results: ToolCallRecord[] = [];
247
+
248
+ for (const call of calls) {
249
+ // Check for abort before each tool
250
+ if (context.abortSignal?.aborted) {
251
+ break;
252
+ }
253
+
254
+ const startedAt = new Date();
255
+ await observer?.onToolCallStart?.(call);
256
+ const tool = toolRegistry.get(call.name);
257
+
258
+ if (!tool) {
259
+ const endedAt = new Date();
260
+ const record = {
261
+ id: call.id,
262
+ name: call.name,
263
+ input: call.input,
264
+ output: null,
265
+ error: `Unknown tool: ${call.name}`,
266
+ durationMs: endedAt.getTime() - startedAt.getTime(),
267
+ startedAt,
268
+ endedAt,
269
+ };
270
+ await observer?.onToolCallEnd?.(record);
271
+ results.push(record);
272
+ continue;
273
+ }
274
+
275
+ const authorization = await authorizer?.authorize?.(call, context);
276
+ if (authorization && !authorization.allowed) {
277
+ const endedAt = new Date();
278
+ const record = {
279
+ id: call.id,
280
+ name: call.name,
281
+ input: call.input,
282
+ output: null,
283
+ error: authorization.reason,
284
+ durationMs: endedAt.getTime() - startedAt.getTime(),
285
+ startedAt,
286
+ endedAt,
287
+ };
288
+ await observer?.onToolCallEnd?.(record);
289
+ results.push(record);
290
+ continue;
291
+ }
292
+
293
+ const result = await executeToolWithRetry(tool, call.input, context);
294
+ const endedAt = new Date();
295
+
296
+ const record = {
297
+ id: call.id,
298
+ name: call.name,
299
+ input: call.input,
300
+ output: result.output,
301
+ error: result.error,
302
+ durationMs: result.durationMs,
303
+ startedAt,
304
+ endedAt,
305
+ };
306
+ await observer?.onToolCallEnd?.(record);
307
+ results.push(record);
308
+ }
309
+
310
+ return results;
311
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Tool Result Formatting
3
+ *
4
+ * Functions for formatting tool results for various purposes.
5
+ */
6
+
7
+ import type { ToolCallRecord } from "../types.js";
8
+
9
+ /**
10
+ * Format a tool result for sending back to the model
11
+ *
12
+ * The result is serialized to a string (JSON for objects, string for primitives)
13
+ */
14
+ export function formatToolResult(output: unknown, error?: string): string {
15
+ if (error) {
16
+ return JSON.stringify({ error });
17
+ }
18
+
19
+ if (output === null || output === undefined) {
20
+ return "null";
21
+ }
22
+
23
+ if (typeof output === "string") {
24
+ return output;
25
+ }
26
+
27
+ if (typeof output === "number" || typeof output === "boolean") {
28
+ return String(output);
29
+ }
30
+
31
+ try {
32
+ return JSON.stringify(output);
33
+ } catch {
34
+ return String(output);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Format multiple tool results into a structured summary
40
+ */
41
+ export function formatToolResultsSummary(records: ToolCallRecord[]): string {
42
+ if (records.length === 0) {
43
+ return "No tools were called.";
44
+ }
45
+
46
+ const lines = records.map((record) => {
47
+ const status = record.error ? "FAILED" : "SUCCESS";
48
+ const duration = `${record.durationMs}ms`;
49
+ return `- ${record.name}: ${status} (${duration})`;
50
+ });
51
+
52
+ return `Tool Results:\n${lines.join("\n")}`;
53
+ }
54
+
55
+ /**
56
+ * Format a tool call record as a detailed string
57
+ */
58
+ export function formatToolCallRecord(record: ToolCallRecord): string {
59
+ const lines = [
60
+ `Tool: ${record.name}`,
61
+ `ID: ${record.id}`,
62
+ `Status: ${record.error ? "FAILED" : "SUCCESS"}`,
63
+ `Duration: ${record.durationMs}ms`,
64
+ `Started: ${record.startedAt.toISOString()}`,
65
+ `Ended: ${record.endedAt.toISOString()}`,
66
+ ];
67
+
68
+ if (record.error) {
69
+ lines.push(`Error: ${record.error}`);
70
+ }
71
+
72
+ return lines.join("\n");
73
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Tool Utilities
3
+ *
4
+ * This module provides utilities for creating, managing, and executing tools.
5
+ */
6
+
7
+ export {
8
+ type AskQuestionExecutor,
9
+ type AskQuestionInput,
10
+ AskQuestionInputSchema,
11
+ type AskQuestionToolConfig,
12
+ createAskQuestionTool,
13
+ } from "./ask-question.js";
14
+ // Creation
15
+ export { createTool, toToolDefinition, toToolDefinitions } from "./create.js";
16
+ // Execution
17
+ export {
18
+ executeTool,
19
+ executeToolsInParallel,
20
+ executeToolsSequentially,
21
+ executeToolWithRetry,
22
+ type ToolExecutionAuthorizer,
23
+ type ToolExecutionObserver,
24
+ } from "./execution.js";
25
+ // Formatting
26
+ export {
27
+ formatToolCallRecord,
28
+ formatToolResult,
29
+ formatToolResultsSummary,
30
+ } from "./formatting.js";
31
+ // Registry
32
+ export {
33
+ createToolRegistry,
34
+ getAllTools,
35
+ getTool,
36
+ getToolNames,
37
+ hasTool,
38
+ } from "./registry.js";
39
+
40
+ // Validation
41
+ export {
42
+ validateToolDefinition,
43
+ validateToolInput,
44
+ validateTools,
45
+ } from "./validation.js";
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Tool Registry
3
+ *
4
+ * Utilities for managing collections of tools.
5
+ */
6
+
7
+ import type { Tool } from "../types.js";
8
+
9
+ /**
10
+ * Create a map of tools by name for quick lookup
11
+ */
12
+ export function createToolRegistry(tools: Tool[]): Map<string, Tool> {
13
+ const registry = new Map<string, Tool>();
14
+ for (const tool of tools) {
15
+ if (registry.has(tool.name)) {
16
+ throw new Error(`Duplicate tool name: ${tool.name}`);
17
+ }
18
+ registry.set(tool.name, tool);
19
+ }
20
+ return registry;
21
+ }
22
+
23
+ /**
24
+ * Get a tool by name from the registry
25
+ */
26
+ export function getTool(
27
+ registry: Map<string, Tool>,
28
+ name: string,
29
+ ): Tool | undefined {
30
+ return registry.get(name);
31
+ }
32
+
33
+ /**
34
+ * Check if a tool exists in the registry
35
+ */
36
+ export function hasTool(registry: Map<string, Tool>, name: string): boolean {
37
+ return registry.has(name);
38
+ }
39
+
40
+ /**
41
+ * Get all tool names from the registry
42
+ */
43
+ export function getToolNames(registry: Map<string, Tool>): string[] {
44
+ return Array.from(registry.keys());
45
+ }
46
+
47
+ /**
48
+ * Get all tools from the registry
49
+ */
50
+ export function getAllTools(registry: Map<string, Tool>): Tool[] {
51
+ return Array.from(registry.values());
52
+ }