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