@agentscope-ai/agentscope 0.0.2

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 (136) hide show
  1. package/dist/agent/index.d.mts +234 -0
  2. package/dist/agent/index.d.ts +234 -0
  3. package/dist/agent/index.js +1412 -0
  4. package/dist/agent/index.js.map +1 -0
  5. package/dist/agent/index.mjs +1375 -0
  6. package/dist/agent/index.mjs.map +1 -0
  7. package/dist/base-BOx3UzOl.d.mts +41 -0
  8. package/dist/base-BoIps2RL.d.ts +41 -0
  9. package/dist/base-C7jwyH4Z.d.mts +52 -0
  10. package/dist/base-Cwi4bjze.d.ts +127 -0
  11. package/dist/base-DYlBMCy_.d.mts +127 -0
  12. package/dist/base-NX-knWOv.d.ts +52 -0
  13. package/dist/block-VsnHrllL.d.mts +48 -0
  14. package/dist/block-VsnHrllL.d.ts +48 -0
  15. package/dist/event/index.d.mts +181 -0
  16. package/dist/event/index.d.ts +181 -0
  17. package/dist/event/index.js +58 -0
  18. package/dist/event/index.js.map +1 -0
  19. package/dist/event/index.mjs +33 -0
  20. package/dist/event/index.mjs.map +1 -0
  21. package/dist/formatter/index.d.mts +187 -0
  22. package/dist/formatter/index.d.ts +187 -0
  23. package/dist/formatter/index.js +647 -0
  24. package/dist/formatter/index.js.map +1 -0
  25. package/dist/formatter/index.mjs +616 -0
  26. package/dist/formatter/index.mjs.map +1 -0
  27. package/dist/index-BTJDlKvQ.d.mts +195 -0
  28. package/dist/index-BcatlwXQ.d.ts +195 -0
  29. package/dist/index-CAxQAkiP.d.mts +21 -0
  30. package/dist/index-CAxQAkiP.d.ts +21 -0
  31. package/dist/mcp/index.d.mts +9 -0
  32. package/dist/mcp/index.d.ts +9 -0
  33. package/dist/mcp/index.js +432 -0
  34. package/dist/mcp/index.js.map +1 -0
  35. package/dist/mcp/index.mjs +408 -0
  36. package/dist/mcp/index.mjs.map +1 -0
  37. package/dist/message/index.d.mts +10 -0
  38. package/dist/message/index.d.ts +10 -0
  39. package/dist/message/index.js +67 -0
  40. package/dist/message/index.js.map +1 -0
  41. package/dist/message/index.mjs +37 -0
  42. package/dist/message/index.mjs.map +1 -0
  43. package/dist/message-CkN21KaY.d.mts +99 -0
  44. package/dist/message-CzLeTlua.d.ts +99 -0
  45. package/dist/model/index.d.mts +377 -0
  46. package/dist/model/index.d.ts +377 -0
  47. package/dist/model/index.js +1880 -0
  48. package/dist/model/index.js.map +1 -0
  49. package/dist/model/index.mjs +1849 -0
  50. package/dist/model/index.mjs.map +1 -0
  51. package/dist/storage/index.d.mts +68 -0
  52. package/dist/storage/index.d.ts +68 -0
  53. package/dist/storage/index.js +250 -0
  54. package/dist/storage/index.js.map +1 -0
  55. package/dist/storage/index.mjs +212 -0
  56. package/dist/storage/index.mjs.map +1 -0
  57. package/dist/tool/index.d.mts +311 -0
  58. package/dist/tool/index.d.ts +311 -0
  59. package/dist/tool/index.js +1494 -0
  60. package/dist/tool/index.js.map +1 -0
  61. package/dist/tool/index.mjs +1447 -0
  62. package/dist/tool/index.mjs.map +1 -0
  63. package/dist/toolkit-CEpulFi0.d.ts +99 -0
  64. package/dist/toolkit-CGEZSZPa.d.mts +99 -0
  65. package/jest.config.js +11 -0
  66. package/package.json +92 -0
  67. package/src/_utils/common.ts +104 -0
  68. package/src/_utils/index.ts +1 -0
  69. package/src/agent/agent-base.ts +0 -0
  70. package/src/agent/agent.test.ts +1028 -0
  71. package/src/agent/agent.ts +1032 -0
  72. package/src/agent/index.ts +2 -0
  73. package/src/agent/interfaces.ts +23 -0
  74. package/src/agent/test-compression.ts +72 -0
  75. package/src/event/index.ts +250 -0
  76. package/src/formatter/base.ts +133 -0
  77. package/src/formatter/dashscope-chat-formatter.test.ts +372 -0
  78. package/src/formatter/dashscope-chat-formatter.ts +163 -0
  79. package/src/formatter/deepseek-chat-formatter.ts +130 -0
  80. package/src/formatter/index.ts +5 -0
  81. package/src/formatter/ollama-chat-formatter.ts +67 -0
  82. package/src/formatter/openai-chat-formatter.test.ts +263 -0
  83. package/src/formatter/openai-chat-formatter.ts +301 -0
  84. package/src/formatter/openai.md +767 -0
  85. package/src/mcp/base.ts +114 -0
  86. package/src/mcp/http.test.ts +303 -0
  87. package/src/mcp/http.ts +224 -0
  88. package/src/mcp/index.ts +2 -0
  89. package/src/mcp/stdio.test.ts +91 -0
  90. package/src/mcp/stdio.ts +119 -0
  91. package/src/message/block.ts +60 -0
  92. package/src/message/enums.ts +4 -0
  93. package/src/message/index.ts +12 -0
  94. package/src/message/message.test.ts +80 -0
  95. package/src/message/message.ts +131 -0
  96. package/src/model/base.ts +226 -0
  97. package/src/model/dashscope-model.test.ts +335 -0
  98. package/src/model/dashscope-model.ts +441 -0
  99. package/src/model/deepseek-model.test.ts +279 -0
  100. package/src/model/deepseek-model.ts +401 -0
  101. package/src/model/index.ts +7 -0
  102. package/src/model/ollama-model.test.ts +307 -0
  103. package/src/model/ollama-model.ts +356 -0
  104. package/src/model/openai-model.ts +327 -0
  105. package/src/model/response.ts +22 -0
  106. package/src/model/usage.ts +12 -0
  107. package/src/storage/base.ts +52 -0
  108. package/src/storage/file-system.test.ts +587 -0
  109. package/src/storage/file-system.ts +269 -0
  110. package/src/storage/index.ts +2 -0
  111. package/src/tool/base.ts +23 -0
  112. package/src/tool/bash.test.ts +174 -0
  113. package/src/tool/bash.ts +152 -0
  114. package/src/tool/edit.test.ts +83 -0
  115. package/src/tool/edit.ts +95 -0
  116. package/src/tool/glob.test.ts +63 -0
  117. package/src/tool/glob.ts +166 -0
  118. package/src/tool/grep.test.ts +74 -0
  119. package/src/tool/grep.ts +256 -0
  120. package/src/tool/index.ts +10 -0
  121. package/src/tool/read.test.ts +77 -0
  122. package/src/tool/read.ts +117 -0
  123. package/src/tool/response.ts +82 -0
  124. package/src/tool/task.test.ts +299 -0
  125. package/src/tool/task.ts +399 -0
  126. package/src/tool/toolkit.test.ts +636 -0
  127. package/src/tool/toolkit.ts +601 -0
  128. package/src/tool/write.test.ts +52 -0
  129. package/src/tool/write.ts +57 -0
  130. package/src/type/index.ts +52 -0
  131. package/tsconfig.build.json +4 -0
  132. package/tsconfig.cjs.json +11 -0
  133. package/tsconfig.esm.json +10 -0
  134. package/tsconfig.json +14 -0
  135. package/tsup.config.ts +20 -0
  136. package/typedoc.json +52 -0
@@ -0,0 +1,636 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+
4
+ import { z } from 'zod';
5
+
6
+ import { createToolResponse } from './response';
7
+ import { Toolkit } from './toolkit';
8
+
9
+ /**
10
+ * A sample test tool function.
11
+ * @param input
12
+ * @param input.a
13
+ * @param input.b
14
+ * @returns ToolResponse
15
+ */
16
+ function testFunction(input: { a: string; b: number }) {
17
+ return createToolResponse({
18
+ content: [
19
+ { type: 'text', text: `Received a=${input.a}, b=${input.b}`, id: crypto.randomUUID() },
20
+ ],
21
+ state: 'success',
22
+ });
23
+ }
24
+
25
+ describe('Toolkit', () => {
26
+ test('Register and execute tool function with ZodObject schema', async () => {
27
+ const toolkit = new Toolkit();
28
+ toolkit.registerToolFunction({
29
+ call: testFunction,
30
+ name: 'test_function',
31
+ description: 'A test function',
32
+ inputSchema: z.object({
33
+ a: z.string().max(200).describe('The first parameter'),
34
+ b: z
35
+ .number()
36
+ .max(10)
37
+ .min(0)
38
+ .describe('The second parameter, a number between 0 and 10'),
39
+ }),
40
+ requireUserConfirm: false,
41
+ });
42
+
43
+ const schemas = toolkit.getJSONSchemas();
44
+ expect(schemas).toEqual([
45
+ {
46
+ function: {
47
+ description:
48
+ 'Retrieves the full content of a skill by reading its SKILL.md file. Skills are packages of domain expertise that extend agent capabilities. Use this tool to access detailed instructions, examples, and guidelines for a specific skill.\n\nUsage:\n- Provide the skill name as the input parameter\n- The tool will return the complete SKILL.md file content for that skill\n- If the skill is not found, an error message with available skills will be returned\n- Available skills are listed in the skills-system section of the agent prompt',
49
+ name: 'Skill',
50
+ parameters: {
51
+ additionalProperties: false,
52
+ properties: {
53
+ name: {
54
+ description: 'The name of the skill',
55
+ type: 'string',
56
+ },
57
+ },
58
+ required: ['name'],
59
+ type: 'object',
60
+ },
61
+ },
62
+ type: 'function',
63
+ },
64
+ {
65
+ type: 'function',
66
+ function: {
67
+ name: 'test_function',
68
+ description: 'A test function',
69
+ parameters: {
70
+ additionalProperties: false,
71
+ type: 'object',
72
+ properties: {
73
+ a: {
74
+ type: 'string',
75
+ maxLength: 200,
76
+ description: 'The first parameter',
77
+ },
78
+ b: {
79
+ type: 'number',
80
+ maximum: 10,
81
+ minimum: 0,
82
+ description: 'The second parameter, a number between 0 and 10',
83
+ },
84
+ },
85
+ required: ['a', 'b'],
86
+ },
87
+ },
88
+ },
89
+ ]);
90
+
91
+ const gen = toolkit.callToolFunction({
92
+ type: 'tool_call',
93
+ name: 'test_function',
94
+ input: '{"a":"hello","b":5}',
95
+ id: '1',
96
+ });
97
+
98
+ for await (const chunk of gen) {
99
+ expect(chunk.content).toEqual([
100
+ { id: expect.any(String), type: 'text', text: 'Received a=hello, b=5' },
101
+ ]);
102
+ expect(chunk.isLast).toBe(true);
103
+ }
104
+ });
105
+
106
+ test('Register and execute tool function with ToolInputSchema', async () => {
107
+ const toolkit = new Toolkit();
108
+ toolkit.registerToolFunction({
109
+ call: testFunction,
110
+ name: 'test_function',
111
+ description: 'A test function',
112
+ inputSchema: {
113
+ type: 'object',
114
+ properties: {
115
+ a: { type: 'string', maxLength: 200 },
116
+ b: { type: 'number', maximum: 10, minimum: 0 },
117
+ },
118
+ required: ['a', 'b'],
119
+ },
120
+ requireUserConfirm: false,
121
+ });
122
+
123
+ const schemas = toolkit.getJSONSchemas();
124
+ expect(schemas).toEqual([
125
+ {
126
+ function: {
127
+ description:
128
+ 'Retrieves the full content of a skill by reading its SKILL.md file. Skills are packages of domain expertise that extend agent capabilities. Use this tool to access detailed instructions, examples, and guidelines for a specific skill.\n\nUsage:\n- Provide the skill name as the input parameter\n- The tool will return the complete SKILL.md file content for that skill\n- If the skill is not found, an error message with available skills will be returned\n- Available skills are listed in the skills-system section of the agent prompt',
129
+ name: 'Skill',
130
+ parameters: {
131
+ additionalProperties: false,
132
+ properties: {
133
+ name: {
134
+ description: 'The name of the skill',
135
+ type: 'string',
136
+ },
137
+ },
138
+ required: ['name'],
139
+ type: 'object',
140
+ },
141
+ },
142
+ type: 'function',
143
+ },
144
+ {
145
+ type: 'function',
146
+ function: {
147
+ name: 'test_function',
148
+ description: 'A test function',
149
+ parameters: {
150
+ type: 'object',
151
+ properties: {
152
+ a: { type: 'string', maxLength: 200 },
153
+ b: { type: 'number', maximum: 10, minimum: 0 },
154
+ },
155
+ required: ['a', 'b'],
156
+ },
157
+ },
158
+ },
159
+ ]);
160
+
161
+ const gen = toolkit.callToolFunction({
162
+ type: 'tool_call',
163
+ name: 'test_function',
164
+ input: '{"a":"hello","b":5}',
165
+ id: '1b',
166
+ });
167
+
168
+ for await (const chunk of gen) {
169
+ expect(chunk.content).toEqual([
170
+ { id: expect.any(String), type: 'text', text: 'Received a=hello, b=5' },
171
+ ]);
172
+ expect(chunk.isLast).toBe(true);
173
+ }
174
+ });
175
+
176
+ test('None-parameter tool function', async () => {
177
+ const toolkit = new Toolkit();
178
+ toolkit.registerToolFunction({
179
+ call: () =>
180
+ createToolResponse({
181
+ content: [
182
+ { type: 'text', text: 'No parameters here', id: crypto.randomUUID() },
183
+ ],
184
+ state: 'success',
185
+ }),
186
+ name: 'no_param_function',
187
+ description: 'A function with no parameters',
188
+ inputSchema: z.object({}),
189
+ requireUserConfirm: false,
190
+ });
191
+
192
+ const schemas = toolkit.getJSONSchemas();
193
+ expect(schemas).toEqual([
194
+ {
195
+ function: {
196
+ description:
197
+ 'Retrieves the full content of a skill by reading its SKILL.md file. Skills are packages of domain expertise that extend agent capabilities. Use this tool to access detailed instructions, examples, and guidelines for a specific skill.\n\nUsage:\n- Provide the skill name as the input parameter\n- The tool will return the complete SKILL.md file content for that skill\n- If the skill is not found, an error message with available skills will be returned\n- Available skills are listed in the skills-system section of the agent prompt',
198
+ name: 'Skill',
199
+ parameters: {
200
+ additionalProperties: false,
201
+ properties: {
202
+ name: {
203
+ description: 'The name of the skill',
204
+ type: 'string',
205
+ },
206
+ },
207
+ required: ['name'],
208
+ type: 'object',
209
+ },
210
+ },
211
+ type: 'function',
212
+ },
213
+ {
214
+ type: 'function',
215
+ function: {
216
+ name: 'no_param_function',
217
+ description: 'A function with no parameters',
218
+ parameters: {
219
+ additionalProperties: false,
220
+ type: 'object',
221
+ properties: {},
222
+ },
223
+ },
224
+ },
225
+ ]);
226
+
227
+ const res = await toolkit.callToolFunction({
228
+ type: 'tool_call',
229
+ name: 'no_param_function',
230
+ id: '2',
231
+ input: '{}',
232
+ });
233
+
234
+ for await (const chunk of res) {
235
+ expect(chunk.content).toEqual([
236
+ { id: expect.any(String), type: 'text', text: 'No parameters here' },
237
+ ]);
238
+ }
239
+ });
240
+
241
+ test('Sync generator tool function', async () => {
242
+ const toolkit = new Toolkit();
243
+ toolkit.registerToolFunction({
244
+ call: function* (input: { count: number }) {
245
+ for (let i = 0; i < input.count; i++) {
246
+ yield createToolResponse({
247
+ content: [{ type: 'text', text: `Count: ${i}`, id: crypto.randomUUID() }],
248
+ stream: true,
249
+ isLast: i === input.count - 1,
250
+ state: 'success',
251
+ });
252
+ }
253
+ },
254
+ name: 'count_function',
255
+ description: 'A function that counts up to a number',
256
+ inputSchema: z.object({
257
+ count: z.number().min(1).max(5).describe('The number to count up to'),
258
+ }),
259
+ });
260
+
261
+ const gen = toolkit.callToolFunction({
262
+ type: 'tool_call',
263
+ name: 'count_function',
264
+ id: '3',
265
+ input: '{"count":3}',
266
+ });
267
+
268
+ // Verify intermediate chunks
269
+ const chunks = [];
270
+ let finalRes;
271
+ while (true) {
272
+ const next = await gen.next();
273
+ if (next.done) {
274
+ finalRes = next.value;
275
+ break;
276
+ }
277
+ chunks.push(next.value);
278
+ }
279
+
280
+ expect(chunks).toHaveLength(3);
281
+ expect(chunks[0].content).toEqual([
282
+ { id: expect.any(String), type: 'text', text: 'Count: 0' },
283
+ ]);
284
+ expect(chunks[0].isLast).toBe(false);
285
+ expect(chunks[1].content).toEqual([
286
+ { id: expect.any(String), type: 'text', text: 'Count: 1' },
287
+ ]);
288
+ expect(chunks[1].isLast).toBe(false);
289
+ expect(chunks[2].content).toEqual([
290
+ { id: expect.any(String), type: 'text', text: 'Count: 2' },
291
+ ]);
292
+ expect(chunks[2].isLast).toBe(true);
293
+
294
+ // Verify final accumulated result
295
+ expect(finalRes!.content).toEqual([
296
+ { id: expect.any(String), type: 'text', text: 'Count: 0Count: 1Count: 2' },
297
+ ]);
298
+ expect(finalRes!.isLast).toBe(true);
299
+ });
300
+
301
+ test('Async generator tool function', async () => {
302
+ const toolkit = new Toolkit();
303
+ toolkit.registerToolFunction({
304
+ call: async function* (input: { count: number }) {
305
+ for (let i = 0; i < input.count; i++) {
306
+ yield createToolResponse({
307
+ content: [
308
+ { type: 'text', text: `Async Count: ${i}`, id: crypto.randomUUID() },
309
+ ],
310
+ stream: true,
311
+ state: 'success',
312
+ isLast: i === input.count - 1,
313
+ });
314
+ }
315
+ },
316
+ name: 'async_count_function',
317
+ description: 'An async function that counts up to a number',
318
+ inputSchema: z.object({
319
+ count: z.number().min(1).max(5).describe('The number to count up to'),
320
+ }),
321
+ });
322
+
323
+ const gen = toolkit.callToolFunction({
324
+ type: 'tool_call',
325
+ name: 'async_count_function',
326
+ id: '4',
327
+ input: '{"count":2}',
328
+ });
329
+
330
+ // Verify intermediate chunks
331
+ const chunks = [];
332
+ let finalRes;
333
+ while (true) {
334
+ const next = await gen.next();
335
+ if (next.done) {
336
+ finalRes = next.value;
337
+ break;
338
+ }
339
+ chunks.push(next.value);
340
+ }
341
+
342
+ expect(chunks).toHaveLength(2);
343
+ expect(chunks[0].content).toEqual([
344
+ { id: expect.any(String), type: 'text', text: 'Async Count: 0' },
345
+ ]);
346
+ expect(chunks[0].isLast).toBe(false);
347
+ expect(chunks[1].content).toEqual([
348
+ { id: expect.any(String), type: 'text', text: 'Async Count: 1' },
349
+ ]);
350
+ expect(chunks[1].isLast).toBe(true);
351
+
352
+ // Verify final accumulated result
353
+ expect(finalRes!.content).toEqual([
354
+ { id: expect.any(String), type: 'text', text: 'Async Count: 0Async Count: 1' },
355
+ ]);
356
+ expect(finalRes!.isLast).toBe(true);
357
+ });
358
+
359
+ test('Sync generator tool function returning string', async () => {
360
+ const toolkit = new Toolkit();
361
+ toolkit.registerToolFunction({
362
+ call: function* (input: { count: number }) {
363
+ for (let i = 0; i < input.count; i++) {
364
+ yield `Chunk: ${i}`;
365
+ }
366
+ },
367
+ name: 'string_count_function',
368
+ description: 'A function that yields strings',
369
+ inputSchema: z.object({
370
+ count: z.number().min(1).max(5).describe('The number to count up to'),
371
+ }),
372
+ });
373
+
374
+ const gen = toolkit.callToolFunction({
375
+ type: 'tool_call',
376
+ name: 'string_count_function',
377
+ id: '8',
378
+ input: '{"count":3}',
379
+ });
380
+
381
+ const chunks = [];
382
+ let finalRes;
383
+ while (true) {
384
+ const next = await gen.next();
385
+ if (next.done) {
386
+ finalRes = next.value;
387
+ break;
388
+ }
389
+ chunks.push(next.value);
390
+ }
391
+
392
+ expect(chunks).toHaveLength(3);
393
+ expect(chunks[0].content).toEqual([
394
+ { id: expect.any(String), type: 'text', text: 'Chunk: 0' },
395
+ ]);
396
+ expect(chunks[0].isLast).toBe(false);
397
+ expect(chunks[1].content).toEqual([
398
+ { id: expect.any(String), type: 'text', text: 'Chunk: 1' },
399
+ ]);
400
+ expect(chunks[1].isLast).toBe(false);
401
+ expect(chunks[2].content).toEqual([
402
+ { id: expect.any(String), type: 'text', text: 'Chunk: 2' },
403
+ ]);
404
+ expect(chunks[2].isLast).toBe(true);
405
+
406
+ expect(finalRes!.content).toEqual([
407
+ { id: expect.any(String), type: 'text', text: 'Chunk: 0Chunk: 1Chunk: 2' },
408
+ ]);
409
+ expect(finalRes!.isLast).toBe(true);
410
+ });
411
+
412
+ test('Async generator tool function returning string', async () => {
413
+ const toolkit = new Toolkit();
414
+ toolkit.registerToolFunction({
415
+ call: async function* (input: { count: number }) {
416
+ for (let i = 0; i < input.count; i++) {
417
+ yield `Async Chunk: ${i}`;
418
+ }
419
+ },
420
+ name: 'async_string_count_function',
421
+ description: 'An async function that yields strings',
422
+ inputSchema: z.object({
423
+ count: z.number().min(1).max(5).describe('The number to count up to'),
424
+ }),
425
+ });
426
+
427
+ const gen = toolkit.callToolFunction({
428
+ type: 'tool_call',
429
+ name: 'async_string_count_function',
430
+ id: '9',
431
+ input: '{"count":2}',
432
+ });
433
+
434
+ const chunks = [];
435
+ let finalRes;
436
+ while (true) {
437
+ const next = await gen.next();
438
+ if (next.done) {
439
+ finalRes = next.value;
440
+ break;
441
+ }
442
+ chunks.push(next.value);
443
+ }
444
+
445
+ expect(chunks).toHaveLength(2);
446
+ expect(chunks[0].content).toEqual([
447
+ { id: expect.any(String), type: 'text', text: 'Async Chunk: 0' },
448
+ ]);
449
+ expect(chunks[0].isLast).toBe(false);
450
+ expect(chunks[1].content).toEqual([
451
+ { id: expect.any(String), type: 'text', text: 'Async Chunk: 1' },
452
+ ]);
453
+ expect(chunks[1].isLast).toBe(true);
454
+
455
+ expect(finalRes!.content).toEqual([
456
+ { id: expect.any(String), type: 'text', text: 'Async Chunk: 0Async Chunk: 1' },
457
+ ]);
458
+ expect(finalRes!.isLast).toBe(true);
459
+ });
460
+
461
+ test('Error conditions', async () => {
462
+ const toolkit = new Toolkit();
463
+
464
+ // Unregistered function
465
+ const res = await toolkit.callToolFunction({
466
+ type: 'tool_call',
467
+ name: 'non_existent_function',
468
+ id: '5',
469
+ input: '{}',
470
+ });
471
+
472
+ for await (const chunk of res) {
473
+ expect(chunk.content).toEqual([
474
+ {
475
+ id: expect.any(String),
476
+ type: 'text',
477
+ text: 'FunctionNotFoundError: Cannot find the function named non_existent_function',
478
+ },
479
+ ]);
480
+ }
481
+
482
+ // Wrong input with ZodObject schema
483
+ toolkit.registerToolFunction({
484
+ call: testFunction,
485
+ name: 'test_function',
486
+ description: 'A test function',
487
+ inputSchema: z.object({
488
+ a: z.string().max(200),
489
+ b: z.number().max(10).min(0),
490
+ }),
491
+ });
492
+
493
+ const res2 = await toolkit.callToolFunction({
494
+ type: 'tool_call',
495
+ name: 'test_function',
496
+ id: '6',
497
+ input: '{"a":"hello","b":20}',
498
+ });
499
+
500
+ for await (const chunk of res2) {
501
+ expect(chunk.content[0]).toEqual({
502
+ id: expect.any(String),
503
+ type: 'text',
504
+ text: 'InvalidArgumentError: [\n {\n "origin": "number",\n "code": "too_big",\n "maximum": 10,\n "inclusive": true,\n "path": [\n "b"\n ],\n "message": "Too big: expected number to be <=10"\n }\n]',
505
+ });
506
+ }
507
+
508
+ // Wrong input with ToolInputSchema (non-ZodObject)
509
+ const toolkit2 = new Toolkit();
510
+ toolkit2.registerToolFunction({
511
+ call: testFunction,
512
+ name: 'test_function',
513
+ description: 'A test function',
514
+ inputSchema: {
515
+ type: 'object',
516
+ properties: {
517
+ a: { type: 'string', maxLength: 200 },
518
+ b: { type: 'number', maximum: 10, minimum: 0 },
519
+ },
520
+ required: ['a', 'b'],
521
+ },
522
+ });
523
+
524
+ const res3 = toolkit2.callToolFunction({
525
+ type: 'tool_call',
526
+ name: 'test_function',
527
+ id: '7',
528
+ input: '{"a":"hello","b":20}',
529
+ });
530
+
531
+ for await (const chunk of res3) {
532
+ expect(chunk.content[0].type).toBe('text');
533
+ expect((chunk.content[0] as { type: 'text'; text: string }).text).toMatch(
534
+ /InvalidArgumentError/
535
+ );
536
+ }
537
+ });
538
+
539
+ test('Skill tool with SKILL.md file', async () => {
540
+ // Setup: Create a temporary skill directory and SKILL.md file
541
+ const testSkillDir = path.join(__dirname, '__test_skill__');
542
+ const skillMdPath = path.join(testSkillDir, 'SKILL.md');
543
+
544
+ // Create the skill directory
545
+ if (!fs.existsSync(testSkillDir)) {
546
+ fs.mkdirSync(testSkillDir, { recursive: true });
547
+ }
548
+
549
+ // Write SKILL.md with YAML front matter
550
+ const skillContent = `---
551
+ name: test_skill
552
+ description: A test skill for unit testing
553
+ ---
554
+
555
+ # Test Skill
556
+
557
+ This is a test skill used for unit testing the Skill tool functionality.
558
+
559
+ ## Usage
560
+
561
+ This skill demonstrates how to use the Skill tool to retrieve skill content.`;
562
+
563
+ fs.writeFileSync(skillMdPath, skillContent, 'utf-8');
564
+
565
+ try {
566
+ // Initialize toolkit with builtInSkillTool enabled and register the test skill
567
+ const toolkit = new Toolkit({
568
+ builtInSkillTool: true,
569
+ skills: [testSkillDir],
570
+ });
571
+
572
+ // Verify that the Skill tool is registered
573
+ const schemas = toolkit.getJSONSchemas();
574
+ const skillTool = schemas.find(schema => schema.function.name === 'Skill');
575
+ expect(skillTool).toBeDefined();
576
+ expect(skillTool?.function.description).toContain(
577
+ 'Retrieves the full content of a skill'
578
+ );
579
+
580
+ // Call the Skill tool with the test skill name
581
+ const gen = toolkit.callToolFunction({
582
+ type: 'tool_call',
583
+ name: 'Skill',
584
+ input: '{"name":"test_skill"}',
585
+ id: 'skill_test_1',
586
+ });
587
+
588
+ // Verify the response
589
+ let finalRes;
590
+ for await (const chunk of gen) {
591
+ finalRes = chunk;
592
+ }
593
+
594
+ expect(finalRes).toBeDefined();
595
+ expect(finalRes!.state).toBe('success');
596
+ expect(finalRes!.content).toHaveLength(1);
597
+ expect(finalRes!.content[0].type).toBe('text');
598
+ expect((finalRes!.content[0] as { type: 'text'; text: string }).text).toContain(
599
+ 'name: test_skill'
600
+ );
601
+ expect((finalRes!.content[0] as { type: 'text'; text: string }).text).toContain(
602
+ 'description: A test skill for unit testing'
603
+ );
604
+ expect((finalRes!.content[0] as { type: 'text'; text: string }).text).toContain(
605
+ 'This is a test skill used for unit testing'
606
+ );
607
+
608
+ // Test error case: non-existent skill
609
+ const errorGen = toolkit.callToolFunction({
610
+ type: 'tool_call',
611
+ name: 'Skill',
612
+ input: '{"name":"non_existent_skill"}',
613
+ id: 'skill_test_2',
614
+ });
615
+
616
+ let errorRes;
617
+ for await (const chunk of errorGen) {
618
+ errorRes = chunk;
619
+ }
620
+
621
+ expect(errorRes).toBeDefined();
622
+ expect(errorRes!.state).toBe('error');
623
+ expect((errorRes!.content[0] as { type: 'text'; text: string }).text).toContain(
624
+ 'SkillNotFoundError'
625
+ );
626
+ } finally {
627
+ // Cleanup: Remove the test skill directory
628
+ if (fs.existsSync(skillMdPath)) {
629
+ fs.unlinkSync(skillMdPath);
630
+ }
631
+ if (fs.existsSync(testSkillDir)) {
632
+ fs.rmdirSync(testSkillDir);
633
+ }
634
+ }
635
+ });
636
+ });