@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,1375 @@
1
+ // src/agent/agent.ts
2
+ import { z as z9 } from "zod";
3
+
4
+ // src/message/message.ts
5
+ function createMsg({
6
+ name,
7
+ content,
8
+ role,
9
+ metadata = {},
10
+ id = crypto.randomUUID(),
11
+ timestamp = (/* @__PURE__ */ new Date()).toISOString(),
12
+ usage
13
+ }) {
14
+ return { id, name, role, content, metadata, timestamp, usage };
15
+ }
16
+ function getContentBlocks(msg, blockType) {
17
+ if (!blockType) return msg.content;
18
+ return msg.content.filter((block) => block.type === blockType);
19
+ }
20
+
21
+ // src/tool/response.ts
22
+ function createToolResponse({
23
+ content,
24
+ state,
25
+ id = crypto.randomUUID(),
26
+ createdAt = (/* @__PURE__ */ new Date()).toISOString(),
27
+ metadata = {},
28
+ stream = false,
29
+ isLast = true,
30
+ isInterrupted = false
31
+ }) {
32
+ return {
33
+ content,
34
+ id,
35
+ createdAt,
36
+ metadata,
37
+ state,
38
+ stream,
39
+ isLast,
40
+ isInterrupted
41
+ };
42
+ }
43
+ function isToolResponse(obj) {
44
+ return obj && typeof obj === "object" && typeof obj.id === "string" && typeof obj.createdAt === "string" && Array.isArray(obj.content) && typeof obj.metadata === "object" && typeof obj.stream === "boolean" && typeof obj.isLast === "boolean" && typeof obj.isInterrupted === "boolean" && ["success", "error", "interrupted", "running"].includes(obj.state);
45
+ }
46
+
47
+ // src/tool/toolkit.ts
48
+ import * as fs from "fs";
49
+ import * as path from "path";
50
+ import { Validator } from "@cfworker/json-schema";
51
+ import matter from "gray-matter";
52
+ import { z } from "zod";
53
+
54
+ // src/_utils/common.ts
55
+ import { jsonrepair } from "jsonrepair";
56
+ function _jsonLoadsWithRepair(input) {
57
+ try {
58
+ const jsonObj = JSON.parse(input);
59
+ if (typeof jsonObj === "object" && jsonObj !== null && !Array.isArray(jsonObj)) {
60
+ return jsonObj;
61
+ } else {
62
+ return {};
63
+ }
64
+ } catch {
65
+ try {
66
+ const repairedString = jsonrepair(input);
67
+ const jsonObj = JSON.parse(repairedString);
68
+ if (typeof jsonObj === "object" && jsonObj !== null && !Array.isArray(jsonObj)) {
69
+ return jsonObj;
70
+ } else {
71
+ return {};
72
+ }
73
+ } catch (e) {
74
+ console.error(`Failed to parse JSON "${input}" with error:`, e);
75
+ return {};
76
+ }
77
+ }
78
+ }
79
+
80
+ // src/tool/toolkit.ts
81
+ var Toolkit = class {
82
+ tools;
83
+ skills;
84
+ skillDirs;
85
+ // The cache mapping from the skill name to its corresponding tool name in the toolkit.
86
+ _skillCache;
87
+ /**
88
+ * Initializes a new instance of the Toolkit class.
89
+ * @param config - The configuration object for initializing the toolkit, which can include an array of tools, an array of skill paths, an array of skill directory paths, and a boolean indicating whether to include the built-in skill tool for reading SKILL.md files.
90
+ * @param config.tools - An array of tool definitions to register in the toolkit.
91
+ * @param config.skills - An array of file paths pointing to individual skills.
92
+ * @param config.skillDirs - An array of directory paths, where each directory can contain multiple skills in its subdirectories.
93
+ * @param config.builtInSkillTool - A boolean flag indicating whether to include the built-in skill tool for reading SKILL.md files.
94
+ */
95
+ constructor(config) {
96
+ const { tools = [], skills = [], skillDirs = [], builtInSkillTool = true } = config || {};
97
+ this.tools = [];
98
+ if (builtInSkillTool) {
99
+ this.tools.push({
100
+ type: "function",
101
+ name: "Skill",
102
+ description: `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.
103
+
104
+ Usage:
105
+ - Provide the skill name as the input parameter
106
+ - The tool will return the complete SKILL.md file content for that skill
107
+ - If the skill is not found, an error message with available skills will be returned
108
+ - Available skills are listed in the skills-system section of the agent prompt`,
109
+ inputSchema: z.object({ name: z.string().describe("The name of the skill") }),
110
+ call: this._skillTool.bind(this),
111
+ requireUserConfirm: false
112
+ });
113
+ }
114
+ tools.map((tool) => {
115
+ this.tools.push({
116
+ type: "function",
117
+ ...tool
118
+ });
119
+ });
120
+ this.skills = skills;
121
+ this.skillDirs = skillDirs;
122
+ this._skillCache = {};
123
+ }
124
+ /**
125
+ * Registers a tool function to the toolkit. The function can be either a plain function that adheres to the ToolFunction type, or an instance of a class that extends ToolBase. When registering a plain function, the name, description, and input schema must be provided explicitly. When registering a ToolBase instance, these properties will be extracted from the instance itself.
126
+ *
127
+ * @params tool - The tool function to register, which can be either a plain function with explicit properties or an instance of a class that extends ToolBase.
128
+ * @returns The Toolkit instance with the new tool function registered
129
+ * @param tool
130
+ */
131
+ registerToolFunction(tool) {
132
+ this.tools.push({
133
+ type: "function",
134
+ ...tool
135
+ });
136
+ return this;
137
+ }
138
+ /**
139
+ * Registers functions from a given MCP client.
140
+ *
141
+ * @param root0
142
+ * @param root0.client
143
+ * @param root0.enabledTools
144
+ * @param root0.disabledTools
145
+ * @param root0.requireUserConfirm
146
+ * @returns The Toolkit instance with the new tools registered
147
+ */
148
+ async registerMCPClient({
149
+ client,
150
+ enabledTools,
151
+ disabledTools = [],
152
+ requireUserConfirm = false
153
+ }) {
154
+ const tools = await client.listTools();
155
+ const appendTools = [];
156
+ tools.filter(
157
+ (tool) => !(enabledTools && !enabledTools.includes(tool.name)) && !disabledTools.includes(tool.name)
158
+ ).forEach((tool) => {
159
+ this.tools.push({
160
+ type: "mcp",
161
+ mcpName: client.name,
162
+ ...tool,
163
+ requireUserConfirm
164
+ });
165
+ appendTools.push(tool.name);
166
+ });
167
+ console.log(`Registered tools from MCP client '${client.name}': ${appendTools.join(", ")}`);
168
+ return this;
169
+ }
170
+ /**
171
+ * Executes a registered tool function based on the provided ToolUseBlock.
172
+ * Note this method always returns an AsyncGenerator of ToolResponse, regardless of the tool function type.
173
+ *
174
+ * @param toolCall - The ToolUseBlock containing the tool name and input arguments
175
+ * @yields Incremental ToolResponse objects as they are produced by the tool function
176
+ * @returns The final complete ToolResponse after the tool function execution is finished
177
+ */
178
+ async *callToolFunction(toolCall) {
179
+ const tool = this.tools.find((tool2) => tool2.name === toolCall.name);
180
+ if (!tool) {
181
+ const notFoundRes = createToolResponse({
182
+ content: [
183
+ {
184
+ id: crypto.randomUUID(),
185
+ type: "text",
186
+ text: `FunctionNotFoundError: Cannot find the function named ${toolCall.name}`
187
+ }
188
+ ],
189
+ state: "error"
190
+ });
191
+ yield notFoundRes;
192
+ return notFoundRes;
193
+ }
194
+ let parsedInput;
195
+ try {
196
+ parsedInput = _jsonLoadsWithRepair(toolCall.input);
197
+ if (tool.inputSchema instanceof z.ZodObject) {
198
+ tool.inputSchema.parse(parsedInput);
199
+ } else {
200
+ const validator = new Validator(tool.inputSchema);
201
+ const validation = validator.validate(parsedInput);
202
+ if (!validation.valid) {
203
+ throw new Error(`Invalid input arguments: ${validation.errors}`);
204
+ }
205
+ }
206
+ } catch (error) {
207
+ const parseErrorRes = createToolResponse({
208
+ content: [
209
+ {
210
+ id: crypto.randomUUID(),
211
+ type: "text",
212
+ text: `InvalidArgumentError: ${String(error)}`
213
+ }
214
+ ],
215
+ state: "error"
216
+ });
217
+ yield parseErrorRes;
218
+ return parseErrorRes;
219
+ }
220
+ if (!tool.call) {
221
+ throw new Error(
222
+ `Cannot execute external tool '${toolCall.name}' because no call method is defined for it in the toolkit.`
223
+ );
224
+ }
225
+ let finalRes = null;
226
+ try {
227
+ const res = await tool.call(parsedInput);
228
+ if (typeof res === "string") {
229
+ const textRes = createToolResponse({
230
+ content: [
231
+ {
232
+ id: crypto.randomUUID(),
233
+ type: "text",
234
+ text: res
235
+ }
236
+ ],
237
+ state: "success"
238
+ });
239
+ yield textRes;
240
+ finalRes = textRes;
241
+ } else if (isToolResponse(res)) {
242
+ yield res;
243
+ finalRes = res;
244
+ } else if (Symbol.asyncIterator in res) {
245
+ const accContent = [];
246
+ let nextResult = await res.next();
247
+ while (!nextResult.done) {
248
+ const currentValue = nextResult.value;
249
+ nextResult = await res.next();
250
+ const isLastValue = nextResult.done;
251
+ if (typeof currentValue === "string") {
252
+ const itemRes = createToolResponse({
253
+ content: [
254
+ {
255
+ id: crypto.randomUUID(),
256
+ type: "text",
257
+ text: currentValue
258
+ }
259
+ ],
260
+ isLast: isLastValue,
261
+ state: "running"
262
+ });
263
+ yield itemRes;
264
+ accContent.push({
265
+ id: crypto.randomUUID(),
266
+ type: "text",
267
+ text: currentValue
268
+ });
269
+ } else if (isToolResponse(currentValue)) {
270
+ currentValue.isLast = currentValue.isLast ?? isLastValue;
271
+ yield currentValue;
272
+ accContent.push(...currentValue.content);
273
+ }
274
+ }
275
+ finalRes = createToolResponse({
276
+ content: accContent,
277
+ state: "success"
278
+ });
279
+ } else if (Symbol.iterator in res) {
280
+ const accContent = [];
281
+ let nextResult = res.next();
282
+ while (!nextResult.done) {
283
+ const currentValue = nextResult.value;
284
+ nextResult = res.next();
285
+ const isLastValue = nextResult.done;
286
+ if (typeof currentValue === "string") {
287
+ const itemRes = createToolResponse({
288
+ content: [
289
+ {
290
+ id: crypto.randomUUID(),
291
+ type: "text",
292
+ text: currentValue
293
+ }
294
+ ],
295
+ isLast: isLastValue,
296
+ state: "running"
297
+ });
298
+ yield itemRes;
299
+ accContent.push({
300
+ id: crypto.randomUUID(),
301
+ type: "text",
302
+ text: currentValue
303
+ });
304
+ } else if (isToolResponse(currentValue)) {
305
+ currentValue.isLast = currentValue.isLast ?? isLastValue;
306
+ yield currentValue;
307
+ accContent.push(...currentValue.content);
308
+ }
309
+ }
310
+ finalRes = createToolResponse({
311
+ content: accContent,
312
+ state: "success"
313
+ });
314
+ } else {
315
+ const invalidRes = createToolResponse({
316
+ content: [
317
+ {
318
+ id: crypto.randomUUID(),
319
+ type: "text",
320
+ text: String(res)
321
+ }
322
+ ],
323
+ state: "running"
324
+ });
325
+ yield invalidRes;
326
+ finalRes = invalidRes;
327
+ }
328
+ } catch (error) {
329
+ const errorRes = createToolResponse({
330
+ content: [
331
+ {
332
+ id: crypto.randomUUID(),
333
+ type: "text",
334
+ text: `ToolExecutionError: ${String(error)}`
335
+ }
336
+ ],
337
+ state: "error"
338
+ });
339
+ yield errorRes;
340
+ finalRes = errorRes;
341
+ }
342
+ if (!finalRes) {
343
+ return createToolResponse({
344
+ content: [
345
+ {
346
+ id: crypto.randomUUID(),
347
+ type: "text",
348
+ text: `Tool ${toolCall.name} executed successfully.`
349
+ }
350
+ ],
351
+ state: "success"
352
+ });
353
+ }
354
+ const cleanedContent = [];
355
+ let textBuffer = "";
356
+ for (const block of finalRes.content) {
357
+ if (block.type === "text") {
358
+ textBuffer += block.text;
359
+ } else {
360
+ if (textBuffer) {
361
+ cleanedContent.push({
362
+ id: crypto.randomUUID(),
363
+ type: "text",
364
+ text: textBuffer
365
+ });
366
+ textBuffer = "";
367
+ }
368
+ cleanedContent.push(block);
369
+ }
370
+ }
371
+ if (textBuffer) {
372
+ cleanedContent.push({
373
+ id: crypto.randomUUID(),
374
+ type: "text",
375
+ text: textBuffer
376
+ });
377
+ }
378
+ return {
379
+ ...finalRes,
380
+ content: cleanedContent
381
+ };
382
+ }
383
+ /**
384
+ * Returns the JSON schemas for all registered tools in a format compatible with LLM APIs.
385
+ *
386
+ * @returns An array of ToolJSONSchema objects
387
+ */
388
+ getJSONSchemas() {
389
+ return this.tools.map((tool) => {
390
+ const inputSchema = tool.inputSchema instanceof z.ZodObject ? tool.inputSchema.toJSONSchema({ target: "openapi-3.0" }) : tool.inputSchema;
391
+ return {
392
+ type: "function",
393
+ function: {
394
+ name: tool.name,
395
+ description: tool.description,
396
+ parameters: inputSchema
397
+ }
398
+ };
399
+ });
400
+ }
401
+ /**
402
+ * Get the instruction prompt for the agent to use the skills.
403
+ *
404
+ * @returns A string containing the instruction prompt of the available skills and how to use them.
405
+ */
406
+ getSkillsPrompt() {
407
+ this._skillCache = {};
408
+ if (this.skills.length === 0 && this.skillDirs.length === 0) return "";
409
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
410
+ const skillsInfo = [];
411
+ this.skills.forEach((skillPath) => {
412
+ const absSkillPath = path.resolve(skillPath);
413
+ if (!fs.existsSync(absSkillPath) || !fs.statSync(absSkillPath).isDirectory()) {
414
+ return;
415
+ }
416
+ const skillMdPath = path.join(absSkillPath, "SKILL.md");
417
+ if (!fs.existsSync(skillMdPath)) return;
418
+ try {
419
+ const content = fs.readFileSync(skillMdPath, "utf-8");
420
+ const { data } = matter(content);
421
+ const name = data.name || path.basename(skillPath);
422
+ const description = data.description || "No description provided";
423
+ skillsInfo.push({
424
+ name,
425
+ description,
426
+ location: absSkillPath
427
+ });
428
+ this._skillCache[name] = absSkillPath;
429
+ } catch (e) {
430
+ console.error(`Error reading SKILL.md for skill at ${skillPath}:`, e);
431
+ }
432
+ });
433
+ this.skillDirs.forEach((skillDir) => {
434
+ const absSkillDir = path.resolve(skillDir);
435
+ if (!fs.existsSync(absSkillDir) || !fs.statSync(absSkillDir).isDirectory()) {
436
+ return;
437
+ }
438
+ const subdirs = fs.readdirSync(absSkillDir).filter((subdir) => {
439
+ const subdirPath = path.join(absSkillDir, subdir);
440
+ return fs.statSync(subdirPath).isDirectory();
441
+ });
442
+ subdirs.forEach((subdir) => {
443
+ const skillMdPath = path.join(absSkillDir, subdir, "SKILL.md");
444
+ if (!fs.existsSync(skillMdPath)) return;
445
+ try {
446
+ const content = fs.readFileSync(skillMdPath, "utf-8");
447
+ const { data } = matter(content);
448
+ const name = data.name || subdir;
449
+ const description = data.description || "No description provided";
450
+ skillsInfo.push({
451
+ name,
452
+ description,
453
+ location: path.join(skillDir, subdir)
454
+ });
455
+ this._skillCache[name] = path.join(absSkillDir, subdir);
456
+ } catch (e) {
457
+ console.error(
458
+ `Error reading SKILL.md for skill at ${path.join(skillDir, subdir)}:`,
459
+ e
460
+ );
461
+ }
462
+ });
463
+ });
464
+ if (skillsInfo.length === 0) return "";
465
+ const skillsXml = skillsInfo.map(
466
+ (skill) => `<skill>
467
+ <name>${skill.name}</name>
468
+ <description>${skill.description}</description>
469
+ <location>${skill.location}</location>
470
+ </skill>`
471
+ ).reduce((acc, skillInfo) => acc + `
472
+ ${skillInfo}
473
+ `, "");
474
+ return `<skills-system>
475
+ ## What are Skills?
476
+ Skills are packages of domain expertise that extend your capabilities.
477
+
478
+ ## Important: How to Use Skills
479
+ **Skill names are NOT callable functions.** You cannot call a skill directly by its name.
480
+ ${skillsXml}
481
+ </skills-system>`;
482
+ }
483
+ return "";
484
+ }
485
+ /**
486
+ * The agent skill tool to read SKILL.md file content based on the skill name.
487
+ * @param root0
488
+ * @param root0.name
489
+ * @returns The content of the SKILL.md file for the specified skill, or an error message if the skill is not
490
+ * found or the SKILL.md file cannot be read.
491
+ */
492
+ async _skillTool({ name }) {
493
+ if (this._skillCache[name]) {
494
+ const skillDir = this._skillCache[name];
495
+ const skillMdPath = path.join(skillDir, "SKILL.md");
496
+ if (!fs.existsSync(skillMdPath)) {
497
+ try {
498
+ const fileContent = fs.readFileSync(skillMdPath, "utf-8");
499
+ return createToolResponse({
500
+ content: [
501
+ {
502
+ id: crypto.randomUUID(),
503
+ type: "text",
504
+ text: fileContent
505
+ }
506
+ ],
507
+ state: "success"
508
+ });
509
+ } catch {
510
+ }
511
+ }
512
+ }
513
+ this.getSkillsPrompt();
514
+ const refreshedSkillDir = this._skillCache[name];
515
+ if (refreshedSkillDir) {
516
+ const skillMdPath = path.join(refreshedSkillDir, "SKILL.md");
517
+ try {
518
+ const fileContent = fs.readFileSync(skillMdPath, "utf-8");
519
+ return createToolResponse({
520
+ content: [
521
+ {
522
+ id: crypto.randomUUID(),
523
+ type: "text",
524
+ text: fileContent
525
+ }
526
+ ],
527
+ state: "success"
528
+ });
529
+ } catch {
530
+ }
531
+ }
532
+ return createToolResponse({
533
+ content: [
534
+ {
535
+ id: crypto.randomUUID(),
536
+ type: "text",
537
+ text: `SkillNotFoundError: Cannot find the skill named ${name}, current available skills are ${Object.keys(this._skillCache).join(", ")}`
538
+ }
539
+ ],
540
+ state: "error"
541
+ });
542
+ }
543
+ /**
544
+ * Checks if a tool requires user confirmation before execution based on its name.
545
+ * @param toolName The name of the tool to check for user confirmation requirement.
546
+ * @returns A boolean indicating whether the specified tool requires user confirmation before execution. If the tool is not found, it returns false.
547
+ */
548
+ requireUserConfirm(toolName) {
549
+ const tool = this.tools.find((tool2) => tool2.name === toolName);
550
+ return tool ? tool.requireUserConfirm ?? false : false;
551
+ }
552
+ /**
553
+ * Checks if a tool requires external execution (e.g., by an MCP client) based on its name.
554
+ * @param toolName
555
+ * @returns A boolean indicating whether the specified tool requires external execution. If the tool is not found, it returns false.
556
+ */
557
+ requireExternalExecution(toolName) {
558
+ const tool = this.tools.find((tool2) => tool2.name === toolName);
559
+ return tool ? !tool.call : false;
560
+ }
561
+ };
562
+
563
+ // src/tool/bash.ts
564
+ import { exec } from "child_process";
565
+ import { promisify } from "util";
566
+ import { z as z2 } from "zod";
567
+ var execAsync = promisify(exec);
568
+
569
+ // src/tool/read.ts
570
+ import { z as z3 } from "zod";
571
+
572
+ // src/tool/write.ts
573
+ import { z as z4 } from "zod";
574
+
575
+ // src/tool/edit.ts
576
+ import { z as z5 } from "zod";
577
+
578
+ // src/tool/glob.ts
579
+ import { z as z6 } from "zod";
580
+
581
+ // src/tool/grep.ts
582
+ import { z as z7 } from "zod";
583
+
584
+ // src/tool/task.ts
585
+ import { z as z8 } from "zod";
586
+
587
+ // src/agent/agent.ts
588
+ var DEFAULT_COMPRESSION_PROMPT = "<system-hint>You have been working on the task described above but have not yet completed it. Now write a continuation summary that will allow you to resume work efficiently in a future context window where the conversation history will be replaced with this summary. Your summary should be structured, concise, and actionable.</system-hint>";
589
+ var DEFAULT_SUMMARY_SCHEMA = z9.object({
590
+ task_overview: z9.string().max(300).describe(
591
+ "The user's core request and success criteria. Any clarifications or constraints they specified"
592
+ ),
593
+ current_state: z9.string().max(300).describe(
594
+ "What has been completed so far. File created, modified, or analyzed (with paths if relevant). Key outputs or artifacts produced."
595
+ ),
596
+ important_discoveries: z9.string().max(300).describe(
597
+ "Technical constraints or requirements uncovered. Decisions made and their rationale. Errors encountered and how they were resolved. What approaches were tried that didn't work (and why)"
598
+ ),
599
+ next_steps: z9.string().max(200).describe(
600
+ "Specific actions needed to complete the task. Any blockers or open questions to resolve. Priority order if multiple steps remain"
601
+ ),
602
+ context_to_preserve: z9.string().max(300).describe(
603
+ "User preferences or style requirements. Domain-specific details that aren't obvious. Any promises made to the user"
604
+ )
605
+ });
606
+ var Agent = class {
607
+ // Agent configuration
608
+ name;
609
+ model;
610
+ maxIters;
611
+ toolkit;
612
+ storage;
613
+ context;
614
+ _loaded;
615
+ _sysPrompt;
616
+ compressionConfig;
617
+ // Agent state
618
+ replyId;
619
+ curIter;
620
+ confirmedToolCallIds;
621
+ curSummary;
622
+ /**
623
+ * Initialize an agent instance with the given parameters.
624
+ *
625
+ * @param options - The agent configuration options.
626
+ * @param options.name - The name of the agent.
627
+ * @param options.sysPrompt - The system prompt for the agent.
628
+ * @param options.model - The chat model to use.
629
+ * @param options.maxIters - Maximum iterations (default: 5).
630
+ * @param options.memory - Memory storage (default: InMemoryMemory).
631
+ * @param options.toolkit - Toolkit for tools (default: Toolkit).
632
+ */
633
+ constructor(options) {
634
+ if (options.maxIters !== void 0 && options.maxIters <= 0) {
635
+ throw new Error("maxIters must be greater than 0");
636
+ }
637
+ this.name = options.name;
638
+ this._sysPrompt = options.sysPrompt;
639
+ this.model = options.model;
640
+ this.maxIters = options.maxIters ?? 20;
641
+ this.context = [];
642
+ this.toolkit = options.toolkit ?? new Toolkit();
643
+ this.storage = options.storage;
644
+ this.compressionConfig = options.compressionConfig;
645
+ this._loaded = false;
646
+ this.replyId = "";
647
+ this.curIter = 0;
648
+ this.confirmedToolCallIds = [];
649
+ this.curSummary = "";
650
+ }
651
+ /**
652
+ * Load the state from the storage if storage is provided and not loaded yet.
653
+ */
654
+ async loadState() {
655
+ if (this._loaded || !this.storage) return;
656
+ const { context, metadata } = await this.storage.loadAgentState({ agentId: this.name });
657
+ console.log(`Load state for agent "${this.name}" from storage:`, { context, metadata });
658
+ this.context = context;
659
+ this.replyId = metadata.replyId || "";
660
+ this.curIter = metadata.curIter || 0;
661
+ this.curSummary = metadata.curSummary || "";
662
+ this._loaded = true;
663
+ }
664
+ /**
665
+ * Save the state of the current reply session to storage if storage is provided.
666
+ */
667
+ async saveState() {
668
+ if (!this.storage) return;
669
+ await this.storage.saveAgentState({
670
+ agentId: this.name,
671
+ context: this.context,
672
+ metadata: {
673
+ replyId: this.replyId,
674
+ curIter: this.curIter,
675
+ curSummary: this.curSummary
676
+ }
677
+ });
678
+ }
679
+ /**
680
+ * Get the system prompt of the agent.
681
+ *
682
+ * @returns The system prompt string.
683
+ */
684
+ get sysPrompt() {
685
+ const skillsPrompt = this.toolkit.getSkillsPrompt();
686
+ if (skillsPrompt.length > 0) {
687
+ return this._sysPrompt + "\n\n" + skillsPrompt;
688
+ }
689
+ return this._sysPrompt;
690
+ }
691
+ /**
692
+ * Reply to the given message and stream agent events as they are generated.
693
+ *
694
+ * @param options - The reply options containing the incoming message.
695
+ * @returns An async generator that yields agent events and resolves to the final reply message.
696
+ */
697
+ async *replyStream(options) {
698
+ await this.loadState();
699
+ try {
700
+ return yield* this._reply(options);
701
+ } finally {
702
+ await this.saveState();
703
+ }
704
+ }
705
+ /**
706
+ * Reply to the given message, consuming all streamed events internally.
707
+ *
708
+ * @param options - The reply options containing the incoming message.
709
+ * @param options.msgs - The incoming message(s) to reply to.
710
+ * @returns A promise that resolves to the final reply message.
711
+ */
712
+ async reply(options) {
713
+ await this.loadState();
714
+ try {
715
+ const res = this._reply(options);
716
+ while (true) {
717
+ const { value, done } = await res.next();
718
+ if (done) {
719
+ return value;
720
+ }
721
+ }
722
+ } finally {
723
+ await this.saveState();
724
+ }
725
+ }
726
+ /**
727
+ * Save the given content blocks into the context as a new block in the last assistant message,
728
+ * or create a new assistant message if the last message is not from the assistant or has a different name.
729
+ * @param blocks
730
+ * @param usage
731
+ */
732
+ _saveToContext(blocks, usage) {
733
+ const lastMsg = this.context.at(-1);
734
+ if (this.context.length === 0) {
735
+ this.context.push(
736
+ createMsg({ name: this.name, content: blocks, role: "assistant", usage })
737
+ );
738
+ } else if (lastMsg && lastMsg.role === "assistant" && lastMsg.name === this.name) {
739
+ lastMsg.content.push(...blocks);
740
+ if (usage) {
741
+ if (!lastMsg.usage) {
742
+ lastMsg.usage = {
743
+ inputTokens: 0,
744
+ outputTokens: 0
745
+ };
746
+ }
747
+ lastMsg.usage.inputTokens = lastMsg.usage.inputTokens + usage.inputTokens;
748
+ lastMsg.usage.outputTokens = lastMsg.usage.outputTokens + usage.outputTokens;
749
+ }
750
+ } else {
751
+ this.context.push(
752
+ createMsg({ name: this.name, content: blocks, role: "assistant", usage })
753
+ );
754
+ }
755
+ }
756
+ /**
757
+ * Get the pending tool calls that have no results yet in the context.
758
+ * @returns An array of pending `ToolCallBlock`s that are waiting for execution results.
759
+ */
760
+ _getPendingToolCalls() {
761
+ if (this.context.length === 0) return [];
762
+ const lastMsg = this.context.at(-1);
763
+ if (!lastMsg) return [];
764
+ if (lastMsg.role === "assistant") {
765
+ const toolCalls = getContentBlocks(lastMsg, "tool_call");
766
+ const toolResults = getContentBlocks(lastMsg, "tool_result");
767
+ return toolCalls.filter((toolCall) => !toolResults.some((tr) => tr.id === toolCall.id));
768
+ }
769
+ return [];
770
+ }
771
+ /**
772
+ * Get the awaiting tool calls that require user confirmation or external execution.
773
+ * @returns An array of `ToolCallBlock`s that are waiting for user confirmation or external execution.
774
+ */
775
+ _getAwaitingToolCalls() {
776
+ const pendingToolCalls = this._getPendingToolCalls();
777
+ const preToolCalls = [];
778
+ for (const [index, toolCall] of pendingToolCalls.entries()) {
779
+ if (this.toolkit.requireUserConfirm(toolCall.name) && !this.confirmedToolCallIds.includes(toolCall.id)) {
780
+ toolCall.awaitUserConfirmation = true;
781
+ let i = index + 1;
782
+ for (; i < pendingToolCalls.length; i++) {
783
+ const nextToolCall = pendingToolCalls[i];
784
+ if (!this.toolkit.requireUserConfirm(nextToolCall.name) || this.confirmedToolCallIds.includes(nextToolCall.id))
785
+ break;
786
+ nextToolCall.awaitUserConfirmation = true;
787
+ }
788
+ return {
789
+ awaitingType: "REQUIRE_USER_CONFIRM" /* REQUIRE_USER_CONFIRM */,
790
+ expectedEventType: "USER_CONFIRM_RESULT" /* USER_CONFIRM_RESULT */,
791
+ awaitingToolCalls: pendingToolCalls.slice(index, i),
792
+ preToolCalls
793
+ };
794
+ }
795
+ if (this.toolkit.requireExternalExecution(toolCall.name)) {
796
+ let i = index + 1;
797
+ for (; i < pendingToolCalls.length; i++) {
798
+ const nextToolCall = pendingToolCalls[i];
799
+ if (!this.toolkit.requireExternalExecution(nextToolCall.name)) break;
800
+ }
801
+ return {
802
+ awaitingType: "REQUIRE_EXTERNAL_EXECUTION" /* REQUIRE_EXTERNAL_EXECUTION */,
803
+ expectedEventType: "EXTERNAL_EXECUTION_RESULT" /* EXTERNAL_EXECUTION_RESULT */,
804
+ awaitingToolCalls: pendingToolCalls.slice(index, i),
805
+ preToolCalls
806
+ };
807
+ }
808
+ preToolCalls.push(toolCall);
809
+ }
810
+ return { awaitingToolCalls: [], preToolCalls };
811
+ }
812
+ /**
813
+ * Core reply logic without middlewares. Observes the incoming message, runs
814
+ * reasoning/acting iterations up to `maxIters`, and returns the final response.
815
+ *
816
+ * @param options - The reply options containing the incoming message.
817
+ * @returns An async generator that yields agent events and resolves to the final reply message.
818
+ */
819
+ async *_reply(options) {
820
+ const { expectedEventType } = this._getAwaitingToolCalls();
821
+ if (expectedEventType) {
822
+ if (!options || !options.event || options.event.type !== expectedEventType) {
823
+ throw new Error(
824
+ `Agent is awaiting for '${expectedEventType}' confirmation, but received event of type '${options?.event?.type ?? "none"}'.`
825
+ );
826
+ }
827
+ const event = options.event;
828
+ if (event.type === "EXTERNAL_EXECUTION_RESULT" /* EXTERNAL_EXECUTION_RESULT */) {
829
+ this._saveToContext(event.executionResults);
830
+ } else if (event.type === "USER_CONFIRM_RESULT" /* USER_CONFIRM_RESULT */) {
831
+ for (const result of event.confirmResults) {
832
+ if (result.confirmed) {
833
+ this.confirmedToolCallIds.push(result.toolCall.id);
834
+ } else {
835
+ const rejectionRes = `<system-info>**Note** the user rejected the execution of tool "${result.toolCall.name}"!</system-info>`;
836
+ yield {
837
+ id: crypto.randomUUID(),
838
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
839
+ type: "TOOL_RESULT_START" /* TOOL_RESULT_START */,
840
+ replyId: this.replyId,
841
+ toolCallId: result.toolCall.id
842
+ };
843
+ yield {
844
+ id: crypto.randomUUID(),
845
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
846
+ type: "TOOL_RESULT_TEXT_DELTA" /* TOOL_RESULT_TEXT_DELTA */,
847
+ replyId: this.replyId,
848
+ toolCallId: result.toolCall.id,
849
+ delta: rejectionRes
850
+ };
851
+ yield {
852
+ id: crypto.randomUUID(),
853
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
854
+ type: "TOOL_RESULT_END" /* TOOL_RESULT_END */,
855
+ replyId: this.replyId,
856
+ toolCallId: result.toolCall.id,
857
+ state: "interrupted"
858
+ };
859
+ this._saveToContext([
860
+ {
861
+ type: "tool_result",
862
+ id: result.toolCall.id,
863
+ name: result.toolCall.name,
864
+ output: [
865
+ {
866
+ id: crypto.randomUUID(),
867
+ type: "text",
868
+ text: `<system-info>**Note** the user rejected the execution of tool "${result.toolCall.name}"!</system-info>`
869
+ }
870
+ ],
871
+ state: "interrupted"
872
+ }
873
+ ]);
874
+ }
875
+ }
876
+ const processedToolCallIds = event.confirmResults.map((result) => result.toolCall.id);
877
+ this.context.at(-1)?.content.forEach((content) => {
878
+ if (content.type === "tool_call" && processedToolCallIds.includes(content.id)) {
879
+ delete content.awaitUserConfirmation;
880
+ }
881
+ });
882
+ }
883
+ } else {
884
+ this.curIter = 0;
885
+ this.replyId = crypto.randomUUID();
886
+ this.confirmedToolCallIds = [];
887
+ yield {
888
+ id: crypto.randomUUID(),
889
+ type: "RUN_STARTED" /* RUN_STARTED */,
890
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
891
+ sessionId: "",
892
+ replyId: this.replyId,
893
+ name: this.name,
894
+ role: "assistant"
895
+ };
896
+ }
897
+ if (Array.isArray(options?.msgs)) {
898
+ this.context.push(...options.msgs);
899
+ } else if (options?.msgs) {
900
+ this.context.push(options.msgs);
901
+ }
902
+ while (this.curIter < this.maxIters) {
903
+ const pendingToolCalls = this._getPendingToolCalls();
904
+ if (pendingToolCalls.length === 0) {
905
+ await this.compressMemoryIfNeeded();
906
+ const reasoningResponse = yield* this._reasoning({ toolChoice: "auto" });
907
+ this._saveToContext(reasoningResponse.content, reasoningResponse.usage);
908
+ }
909
+ const { awaitingType, awaitingToolCalls, preToolCalls } = this._getAwaitingToolCalls();
910
+ for (const toolCall of preToolCalls) {
911
+ const actingContent = yield* this._acting({ toolCall });
912
+ this._saveToContext([actingContent]);
913
+ this.confirmedToolCallIds = this.confirmedToolCallIds.filter(
914
+ (id) => id !== toolCall.id
915
+ );
916
+ }
917
+ if (awaitingType) {
918
+ yield {
919
+ id: crypto.randomUUID(),
920
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
921
+ type: awaitingType,
922
+ replyId: this.replyId,
923
+ toolCalls: awaitingToolCalls
924
+ };
925
+ return createMsg({
926
+ name: this.name,
927
+ content: [
928
+ {
929
+ id: crypto.randomUUID(),
930
+ type: "text",
931
+ text: awaitingType === "REQUIRE_USER_CONFIRM" /* REQUIRE_USER_CONFIRM */ ? "Waiting for user confirmation ..." : "Waiting for external execution ..."
932
+ }
933
+ ],
934
+ role: "assistant"
935
+ });
936
+ }
937
+ if (preToolCalls.length === 0) break;
938
+ this.curIter += 1;
939
+ }
940
+ if (this.context.at(-1)?.content.at(-1)?.type !== "text") {
941
+ const summaryResponse = yield* this._reasoning({ toolChoice: "none" });
942
+ this._saveToContext(summaryResponse.content, summaryResponse.usage);
943
+ }
944
+ yield {
945
+ id: crypto.randomUUID(),
946
+ type: "RUN_FINISHED" /* RUN_FINISHED */,
947
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
948
+ sessionId: "",
949
+ replyId: this.replyId
950
+ };
951
+ return createMsg({
952
+ id: this.replyId,
953
+ name: this.name,
954
+ // Must be a string for the final output message
955
+ content: [this.context.at(-1).content.at(-1)],
956
+ role: "assistant"
957
+ });
958
+ }
959
+ /**
960
+ * Core reasoning logic without middlewares. Calls the model with the current
961
+ * memory and system prompt, converts the response to agent events, and saves
962
+ * the resulting message to memory.
963
+ *
964
+ * @param options - The reasoning options, including tool choice strategy.
965
+ * @returns An async generator that yields agent events and resolves to the content blocks of the model response.
966
+ */
967
+ async *_reasoning(options) {
968
+ const tools = this.toolkit.getJSONSchemas();
969
+ yield {
970
+ id: crypto.randomUUID(),
971
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
972
+ type: "MODEL_CALL_STARTED" /* MODEL_CALL_STARTED */,
973
+ replyId: this.replyId,
974
+ modelName: this.model.modelName
975
+ };
976
+ const res = await this.model.call({
977
+ messages: [
978
+ createMsg({
979
+ name: "system",
980
+ content: [{ type: "text", text: this.sysPrompt, id: crypto.randomUUID() }],
981
+ role: "system"
982
+ }),
983
+ ...this.curSummary ? [
984
+ createMsg({
985
+ name: "user",
986
+ content: [
987
+ { type: "text", text: this.curSummary, id: crypto.randomUUID() }
988
+ ],
989
+ role: "user"
990
+ })
991
+ ] : [],
992
+ ...this.context
993
+ ],
994
+ tools,
995
+ toolChoice: options.toolChoice
996
+ });
997
+ let blockIds = {
998
+ textBlockId: null,
999
+ thinkingBlockId: null,
1000
+ toolCallIds: []
1001
+ };
1002
+ let completedResponse;
1003
+ if (this.model.stream) {
1004
+ while (true) {
1005
+ const { value, done } = await res.next();
1006
+ if (done) {
1007
+ completedResponse = value;
1008
+ break;
1009
+ }
1010
+ const chunk = value;
1011
+ yield* this.convertChatResponseToEvent(blockIds, chunk);
1012
+ }
1013
+ } else {
1014
+ completedResponse = res;
1015
+ yield* this.convertChatResponseToEvent(blockIds, res);
1016
+ }
1017
+ if (blockIds.textBlockId) {
1018
+ yield {
1019
+ id: crypto.randomUUID(),
1020
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1021
+ type: "TEXT_BLOCK_END" /* TEXT_BLOCK_END */,
1022
+ replyId: this.replyId,
1023
+ blockId: blockIds.textBlockId
1024
+ };
1025
+ }
1026
+ if (blockIds.thinkingBlockId) {
1027
+ yield {
1028
+ id: crypto.randomUUID(),
1029
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1030
+ type: "THINKING_BLOCK_END" /* THINKING_BLOCK_END */,
1031
+ replyId: this.replyId,
1032
+ blockId: blockIds.thinkingBlockId
1033
+ };
1034
+ }
1035
+ if (blockIds.toolCallIds.length > 0) {
1036
+ for (const toolCallId of blockIds.toolCallIds) {
1037
+ yield {
1038
+ id: crypto.randomUUID(),
1039
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1040
+ type: "TOOL_CALL_END" /* TOOL_CALL_END */,
1041
+ replyId: this.replyId,
1042
+ toolCallId
1043
+ };
1044
+ }
1045
+ }
1046
+ yield {
1047
+ id: crypto.randomUUID(),
1048
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1049
+ type: "MODEL_CALL_ENDED" /* MODEL_CALL_ENDED */,
1050
+ replyId: this.replyId,
1051
+ inputTokens: completedResponse.usage?.inputTokens || 0,
1052
+ outputTokens: completedResponse.usage?.outputTokens || 0
1053
+ };
1054
+ return completedResponse;
1055
+ }
1056
+ /**
1057
+ * Core acting logic without middlewares. Executes the given tool call, streams
1058
+ * intermediate tool result events, and saves the final tool response to memory.
1059
+ *
1060
+ * @param options - The acting options containing the tool call to execute.
1061
+ * @returns An async generator that yields tool result events.
1062
+ */
1063
+ async *_acting(options) {
1064
+ const res = this.toolkit.callToolFunction(options.toolCall);
1065
+ yield {
1066
+ type: "TOOL_RESULT_START" /* TOOL_RESULT_START */,
1067
+ id: crypto.randomUUID(),
1068
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1069
+ replyId: this.replyId,
1070
+ toolCallId: options.toolCall.id,
1071
+ toolCallName: options.toolCall.name
1072
+ };
1073
+ while (true) {
1074
+ const { value, done } = await res.next();
1075
+ if (done) {
1076
+ return {
1077
+ type: "tool_result",
1078
+ id: options.toolCall.id,
1079
+ name: options.toolCall.name,
1080
+ output: value.content,
1081
+ state: value.state
1082
+ };
1083
+ }
1084
+ yield* this.convertToolResponseToEvent(options.toolCall, value);
1085
+ }
1086
+ }
1087
+ /**
1088
+ * Receive external observation message(s) and save them into memory.
1089
+ *
1090
+ * @param options - The observe options containing the message to store.
1091
+ * @returns A promise that resolves when the message has been saved to memory.
1092
+ */
1093
+ async _observe(options) {
1094
+ await this.loadState();
1095
+ if (Array.isArray(options.msg)) {
1096
+ this.context.push(...options.msg);
1097
+ } else if (options.msg) {
1098
+ this.context.push(options.msg);
1099
+ }
1100
+ }
1101
+ /**
1102
+ * Convert a `ChatResponse` chunk into a sequence of typed agent events.
1103
+ * Tracks message IDs across calls via the mutable `responseId` object so that
1104
+ * start/content/end events are correctly correlated.
1105
+ *
1106
+ * @param responseId - Mutable object tracking IDs for the current text, thinking, and tool-call messages.
1107
+ * @param responseId.textMessageId - ID of the in-progress text message, or `null` if not yet started.
1108
+ * @param responseId.thinkingMessageId - ID of the in-progress thinking message, or `null` if not yet started.
1109
+ * @param responseId.textBlockId
1110
+ * @param responseId.thinkingBlockId
1111
+ * @param responseId.toolCallIds - List of tool-call IDs seen so far in this response.
1112
+ * @param chunk - The chat response chunk to convert.
1113
+ * @returns An async generator that yields the corresponding agent events.
1114
+ */
1115
+ async *convertChatResponseToEvent(responseId, chunk) {
1116
+ for (const block of chunk.content) {
1117
+ switch (block.type) {
1118
+ case "text":
1119
+ if (responseId.textBlockId === null) {
1120
+ responseId.textBlockId = crypto.randomUUID();
1121
+ yield {
1122
+ id: crypto.randomUUID(),
1123
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1124
+ type: "TEXT_BLOCK_START" /* TEXT_BLOCK_START */,
1125
+ replyId: this.replyId,
1126
+ blockId: responseId.textBlockId
1127
+ };
1128
+ }
1129
+ yield {
1130
+ id: crypto.randomUUID(),
1131
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1132
+ type: "TEXT_BLOCK_DELTA" /* TEXT_BLOCK_DELTA */,
1133
+ replyId: this.replyId,
1134
+ blockId: responseId.textBlockId,
1135
+ delta: block.text
1136
+ };
1137
+ break;
1138
+ case "thinking":
1139
+ if (responseId.thinkingBlockId === null) {
1140
+ responseId.thinkingBlockId = crypto.randomUUID();
1141
+ yield {
1142
+ id: crypto.randomUUID(),
1143
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1144
+ type: "THINKING_BLOCK_START" /* THINKING_BLOCK_START */,
1145
+ replyId: this.replyId,
1146
+ blockId: responseId.thinkingBlockId
1147
+ };
1148
+ }
1149
+ yield {
1150
+ id: crypto.randomUUID(),
1151
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1152
+ type: "THINKING_BLOCK_DELTA" /* THINKING_BLOCK_DELTA */,
1153
+ replyId: this.replyId,
1154
+ blockId: responseId.thinkingBlockId,
1155
+ delta: block.thinking
1156
+ };
1157
+ break;
1158
+ case "tool_call":
1159
+ if (!responseId.toolCallIds.includes(block.id)) {
1160
+ responseId.toolCallIds.push(block.id);
1161
+ yield {
1162
+ id: crypto.randomUUID(),
1163
+ type: "TOOL_CALL_START" /* TOOL_CALL_START */,
1164
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1165
+ replyId: this.replyId,
1166
+ toolCallId: block.id,
1167
+ toolCallName: block.name
1168
+ };
1169
+ }
1170
+ yield {
1171
+ id: crypto.randomUUID(),
1172
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1173
+ type: "TOOL_CALL_DELTA" /* TOOL_CALL_DELTA */,
1174
+ delta: block.input,
1175
+ replyId: this.replyId,
1176
+ toolCallId: block.id
1177
+ };
1178
+ }
1179
+ }
1180
+ }
1181
+ /**
1182
+ * Convert a `ToolResponse` into a sequence of typed agent events, followed by
1183
+ * a final `TOOL_RESULT_END` event.
1184
+ *
1185
+ * @param toolCall - The original tool-use block that triggered this response.
1186
+ * @param toolRes - The tool response containing result content blocks.
1187
+ * @returns An async generator that yields tool result events.
1188
+ */
1189
+ async *convertToolResponseToEvent(toolCall, toolRes) {
1190
+ for (const block of toolRes.content) {
1191
+ switch (block.type) {
1192
+ case "text":
1193
+ yield {
1194
+ id: crypto.randomUUID(),
1195
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1196
+ type: "TOOL_RESULT_TEXT_DELTA" /* TOOL_RESULT_TEXT_DELTA */,
1197
+ replyId: this.replyId,
1198
+ toolCallId: toolCall.id,
1199
+ delta: block.text
1200
+ };
1201
+ break;
1202
+ case "data":
1203
+ if (block.source.type === "base64") {
1204
+ yield {
1205
+ id: crypto.randomUUID(),
1206
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1207
+ type: "TOOL_RESULT_BINARY_DELTA" /* TOOL_RESULT_BINARY_DELTA */,
1208
+ replyId: this.replyId,
1209
+ toolCallId: toolCall.id,
1210
+ mediaType: block.source.mediaType,
1211
+ data: block.source.data
1212
+ };
1213
+ } else if (block.source.type === "url") {
1214
+ yield {
1215
+ id: crypto.randomUUID(),
1216
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1217
+ type: "TOOL_RESULT_BINARY_DELTA" /* TOOL_RESULT_BINARY_DELTA */,
1218
+ replyId: this.replyId,
1219
+ toolCallId: toolCall.id,
1220
+ mediaType: block.source.mediaType,
1221
+ url: block.source.url
1222
+ };
1223
+ }
1224
+ break;
1225
+ }
1226
+ }
1227
+ yield {
1228
+ id: crypto.randomUUID(),
1229
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1230
+ type: "TOOL_RESULT_END" /* TOOL_RESULT_END */,
1231
+ replyId: this.replyId,
1232
+ toolCallId: toolCall.id,
1233
+ state: toolRes.state
1234
+ };
1235
+ }
1236
+ /**
1237
+ * Convert the agent instance to a JSON-serializable object.
1238
+ * @returns An object containing the agent's name and system prompt.
1239
+ */
1240
+ async toJSON() {
1241
+ return {
1242
+ replyId: this.replyId,
1243
+ confirmedToolCallIds: this.confirmedToolCallIds,
1244
+ curIter: this.curIter
1245
+ };
1246
+ }
1247
+ /**
1248
+ * Split the current context into two parts: one part that needs to be compressed and another part that should be reserved based on the compression configuration. The method calculates how many recent "units" (blocks or tool call pairs) to keep uncompressed according to the `keepRecent` setting in the compression configuration, and ensures that tool calls and their corresponding results are not separated during the split.
1249
+ * @returns An object containing the `toCompressedContext` which includes the messages that need to be compressed, and the `reservedContext` which includes the recent messages that should be kept uncompressed.
1250
+ */
1251
+ _splitContextForCompression() {
1252
+ let toCompressedContext = [];
1253
+ let reservedContext = [];
1254
+ const keepRecent = this.compressionConfig.keepRecent ?? 0;
1255
+ const nBlocks = this.context.map((msg) => msg.content.length).reduce((a, b) => a + b, 0);
1256
+ const toCompressedBlockNumber = nBlocks - keepRecent > 0 ? nBlocks - keepRecent : 0;
1257
+ let currentCompressedBlocks = 0;
1258
+ for (const [index, msg] of this.context.entries()) {
1259
+ if (currentCompressedBlocks + msg.content.length <= toCompressedBlockNumber) {
1260
+ toCompressedContext.push(msg);
1261
+ currentCompressedBlocks += msg.content.length;
1262
+ } else {
1263
+ const reservedBlocks = msg.content.slice(
1264
+ toCompressedBlockNumber - currentCompressedBlocks
1265
+ );
1266
+ const unPairedToolResultIds = /* @__PURE__ */ new Set();
1267
+ for (const block of reservedBlocks) {
1268
+ if (block.type == "tool_call") {
1269
+ unPairedToolResultIds.add(block.id);
1270
+ } else if (block.type == "tool_result") {
1271
+ if (unPairedToolResultIds.has(block.id)) {
1272
+ unPairedToolResultIds.delete(block.id);
1273
+ }
1274
+ }
1275
+ }
1276
+ let i = toCompressedBlockNumber - currentCompressedBlocks - 1;
1277
+ for (; i >= 0; i--) {
1278
+ const block = msg.content[i];
1279
+ if (block.type === "tool_call" && unPairedToolResultIds.has(block.id)) {
1280
+ unPairedToolResultIds.delete(block.id);
1281
+ }
1282
+ if (unPairedToolResultIds.size === 0) break;
1283
+ }
1284
+ if (i <= 0) {
1285
+ reservedContext.push(msg);
1286
+ break;
1287
+ }
1288
+ const lastMsg = { ...msg };
1289
+ lastMsg.content = msg.content.slice(0, i);
1290
+ toCompressedContext.push(lastMsg);
1291
+ const reservedMsg = { ...msg };
1292
+ reservedMsg.content = msg.content.slice(i);
1293
+ reservedContext.push(reservedMsg);
1294
+ reservedContext.push(...this.context.slice(index + 1));
1295
+ break;
1296
+ }
1297
+ }
1298
+ return { toCompressedContext, reservedContext };
1299
+ }
1300
+ /**
1301
+ * Compress the agent's memory using the specified compression model (if provided) or the original model.
1302
+ */
1303
+ async compressMemoryIfNeeded() {
1304
+ if (!this.compressionConfig || !this.compressionConfig.enabled) return;
1305
+ const { toCompressedContext, reservedContext } = this._splitContextForCompression();
1306
+ if (toCompressedContext.length <= 0 || toCompressedContext.length === 1 && toCompressedContext.at(0)?.content.length === 1)
1307
+ return;
1308
+ const messages = [
1309
+ createMsg({
1310
+ name: "system",
1311
+ content: [{ type: "text", text: this.sysPrompt, id: crypto.randomUUID() }],
1312
+ role: "system"
1313
+ }),
1314
+ ...toCompressedContext,
1315
+ // instructions to compress the context into a summary
1316
+ createMsg({
1317
+ name: "user",
1318
+ content: [
1319
+ {
1320
+ id: crypto.randomUUID(),
1321
+ type: "text",
1322
+ text: this.compressionConfig.compressionPrompt || DEFAULT_COMPRESSION_PROMPT
1323
+ }
1324
+ ],
1325
+ role: "user"
1326
+ })
1327
+ ];
1328
+ const nTokens = await this.model.countTokens({
1329
+ messages,
1330
+ tools: this.toolkit.getJSONSchemas()
1331
+ });
1332
+ console.debug(`[AGENT ${this.name}] Current context token count: ${nTokens}.`);
1333
+ if (nTokens <= this.compressionConfig.triggerThreshold) return;
1334
+ console.log(
1335
+ `[AGENT ${this.name}] Compressing memory with ${toCompressedContext.length} messages.`
1336
+ );
1337
+ const res = await this.model.callStructured({
1338
+ messages: [
1339
+ createMsg({
1340
+ name: "system",
1341
+ content: [{ type: "text", text: this.sysPrompt, id: crypto.randomUUID() }],
1342
+ role: "system"
1343
+ }),
1344
+ ...toCompressedContext,
1345
+ // instructions to compress the context into a summary
1346
+ createMsg({
1347
+ name: "user",
1348
+ content: [
1349
+ {
1350
+ id: crypto.randomUUID(),
1351
+ type: "text",
1352
+ text: this.compressionConfig.compressionPrompt || DEFAULT_COMPRESSION_PROMPT
1353
+ }
1354
+ ],
1355
+ role: "user"
1356
+ })
1357
+ ],
1358
+ schema: this.compressionConfig.summarySchema || DEFAULT_SUMMARY_SCHEMA
1359
+ });
1360
+ let summaryText = "<system-reminder>Here is a summary of your previous work\n";
1361
+ for (const [key, value] of Object.entries(res.content)) {
1362
+ summaryText += `# ${key}
1363
+ ${value}
1364
+ `;
1365
+ }
1366
+ summaryText += "</system-reminder>";
1367
+ console.debug(`[AGENT ${this.name}] Compression summary: ${summaryText}`);
1368
+ this.context = reservedContext;
1369
+ this.curSummary = summaryText;
1370
+ }
1371
+ };
1372
+ export {
1373
+ Agent
1374
+ };
1375
+ //# sourceMappingURL=index.mjs.map