openvibe 0.63.2 → 0.63.3
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/CHANGELOG.md +81 -0
- package/README.md +39 -0
- package/README_CN.md +1 -1
- package/dist/cli/args.d.ts +3 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +22 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +5 -3
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/companion/executor/bash-executor.d.ts +30 -0
- package/dist/core/companion/executor/bash-executor.d.ts.map +1 -0
- package/dist/core/companion/executor/bash-executor.js +105 -0
- package/dist/core/companion/executor/bash-executor.js.map +1 -0
- package/dist/core/companion/executor/code-executor.d.ts +33 -0
- package/dist/core/companion/executor/code-executor.d.ts.map +1 -0
- package/dist/core/companion/executor/code-executor.js +144 -0
- package/dist/core/companion/executor/code-executor.js.map +1 -0
- package/dist/core/companion/executor/resource-monitor.d.ts +55 -0
- package/dist/core/companion/executor/resource-monitor.d.ts.map +1 -0
- package/dist/core/companion/executor/resource-monitor.js +141 -0
- package/dist/core/companion/executor/resource-monitor.js.map +1 -0
- package/dist/core/companion/http-server.d.ts +92 -0
- package/dist/core/companion/http-server.d.ts.map +1 -0
- package/dist/core/companion/http-server.js +229 -0
- package/dist/core/companion/http-server.js.map +1 -0
- package/dist/core/companion/index.d.ts +29 -0
- package/dist/core/companion/index.d.ts.map +1 -0
- package/dist/core/companion/index.js +48 -0
- package/dist/core/companion/index.js.map +1 -0
- package/dist/core/companion/scheduler/resource-scheduler.d.ts +82 -0
- package/dist/core/companion/scheduler/resource-scheduler.d.ts.map +1 -0
- package/dist/core/companion/scheduler/resource-scheduler.js +333 -0
- package/dist/core/companion/scheduler/resource-scheduler.js.map +1 -0
- package/dist/core/companion/scheduler/workers/base-worker.d.ts +87 -0
- package/dist/core/companion/scheduler/workers/base-worker.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/base-worker.js +70 -0
- package/dist/core/companion/scheduler/workers/base-worker.js.map +1 -0
- package/dist/core/companion/scheduler/workers/cpu-worker.d.ts +12 -0
- package/dist/core/companion/scheduler/workers/cpu-worker.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/cpu-worker.js +134 -0
- package/dist/core/companion/scheduler/workers/cpu-worker.js.map +1 -0
- package/dist/core/companion/scheduler/workers/gpu-worker.d.ts +16 -0
- package/dist/core/companion/scheduler/workers/gpu-worker.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/gpu-worker.js +167 -0
- package/dist/core/companion/scheduler/workers/gpu-worker.js.map +1 -0
- package/dist/core/companion/scheduler/workers/index.d.ts +6 -0
- package/dist/core/companion/scheduler/workers/index.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/index.js +6 -0
- package/dist/core/companion/scheduler/workers/index.js.map +1 -0
- package/dist/core/companion/scheduler/workers/io-worker.d.ts +17 -0
- package/dist/core/companion/scheduler/workers/io-worker.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/io-worker.js +197 -0
- package/dist/core/companion/scheduler/workers/io-worker.js.map +1 -0
- package/dist/core/companion/scheduler/workers/network-worker.d.ts +13 -0
- package/dist/core/companion/scheduler/workers/network-worker.d.ts.map +1 -0
- package/dist/core/companion/scheduler/workers/network-worker.js +167 -0
- package/dist/core/companion/scheduler/workers/network-worker.js.map +1 -0
- package/dist/core/companion/security/security-manager.d.ts +50 -0
- package/dist/core/companion/security/security-manager.d.ts.map +1 -0
- package/dist/core/companion/security/security-manager.js +102 -0
- package/dist/core/companion/security/security-manager.js.map +1 -0
- package/dist/core/hybrid-cloud/extension.d.ts +8 -0
- package/dist/core/hybrid-cloud/extension.d.ts.map +1 -0
- package/dist/core/hybrid-cloud/extension.js +64 -0
- package/dist/core/hybrid-cloud/extension.js.map +1 -0
- package/dist/core/hybrid-cloud/index.d.ts +98 -0
- package/dist/core/hybrid-cloud/index.d.ts.map +1 -0
- package/dist/core/hybrid-cloud/index.js +211 -0
- package/dist/core/hybrid-cloud/index.js.map +1 -0
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +3 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +6 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +13 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +35 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +7 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +46 -0
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/todo-manager.d.ts +32 -0
- package/dist/core/todo-manager.d.ts.map +1 -0
- package/dist/core/todo-manager.js +117 -0
- package/dist/core/todo-manager.js.map +1 -0
- package/dist/core/tools/batch-write.d.ts +42 -0
- package/dist/core/tools/batch-write.d.ts.map +1 -0
- package/dist/core/tools/batch-write.js +267 -0
- package/dist/core/tools/batch-write.js.map +1 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +10 -2
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/fast-executor.d.ts +6 -1
- package/dist/core/tools/fast-executor.d.ts.map +1 -1
- package/dist/core/tools/fast-executor.js +83 -4
- package/dist/core/tools/fast-executor.js.map +1 -1
- package/dist/core/tools/index.d.ts +141 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +30 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/todo.d.ts +47 -0
- package/dist/core/tools/todo.d.ts.map +1 -0
- package/dist/core/tools/todo.js +212 -0
- package/dist/core/tools/todo.js.map +1 -0
- package/dist/core/tools/unified.d.ts +121 -0
- package/dist/core/tools/unified.d.ts.map +1 -0
- package/dist/core/tools/unified.js +481 -0
- package/dist/core/tools/unified.js.map +1 -0
- package/dist/core/tools/write.d.ts +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +20 -5
- package/dist/core/tools/write.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +63 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/index.d.ts +2 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +2 -0
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/todo-display.d.ts +9 -0
- package/dist/modes/interactive/components/todo-display.d.ts.map +1 -0
- package/dist/modes/interactive/components/todo-display.js +60 -0
- package/dist/modes/interactive/components/todo-display.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +8 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +151 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/plan-mode.d.ts +14 -0
- package/dist/modes/plan-mode.d.ts.map +1 -0
- package/dist/modes/plan-mode.js +107 -0
- package/dist/modes/plan-mode.js.map +1 -0
- package/dist/modes/spec-mode.d.ts +16 -0
- package/dist/modes/spec-mode.d.ts.map +1 -0
- package/dist/modes/spec-mode.js +186 -0
- package/dist/modes/spec-mode.js.map +1 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +2 -2
- package/dist/utils/version-check.js.map +1 -1
- package/docs/HYBRID_CLOUD_README.md +188 -0
- package/docs/hybrid-architecture-design.md +317 -0
- package/docs/todo.md +71 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { basename, dirname, resolve } from "path";
|
|
5
|
+
const BatchWriteFileSchema = Type.Object({
|
|
6
|
+
path: Type.String({ description: "File path to write" }),
|
|
7
|
+
content: Type.String({ description: "File content" }),
|
|
8
|
+
});
|
|
9
|
+
const BatchWriteOptionsSchema = Type.Object({
|
|
10
|
+
atomic: Type.Optional(Type.Boolean({ description: "Enable atomic write with rollback", default: true })),
|
|
11
|
+
backup: Type.Optional(Type.Boolean({ description: "Backup existing files", default: true })),
|
|
12
|
+
overwrite: Type.Optional(Type.Boolean({ description: "Overwrite existing files", default: true })),
|
|
13
|
+
createDirs: Type.Optional(Type.Boolean({ description: "Create parent directories", default: true })),
|
|
14
|
+
encoding: Type.Optional(Type.String({ description: "File encoding", default: "utf-8" })),
|
|
15
|
+
});
|
|
16
|
+
const BatchWriteSchema = Type.Object({
|
|
17
|
+
operation: Type.Literal("batch_write"),
|
|
18
|
+
files: Type.Array(BatchWriteFileSchema, { description: "Files to write" }),
|
|
19
|
+
options: Type.Optional(BatchWriteOptionsSchema),
|
|
20
|
+
});
|
|
21
|
+
function getBackupDir() {
|
|
22
|
+
const backupBase = resolve(homedir(), ".openvibe", "backups");
|
|
23
|
+
if (!existsSync(backupBase)) {
|
|
24
|
+
mkdirSync(backupBase, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
return backupBase;
|
|
27
|
+
}
|
|
28
|
+
function createBackup(filePath) {
|
|
29
|
+
if (!existsSync(filePath)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const backupDir = getBackupDir();
|
|
33
|
+
const timestamp = Date.now();
|
|
34
|
+
const fileName = basename(filePath);
|
|
35
|
+
const backupPath = resolve(backupDir, `${fileName}.${timestamp}.bak`);
|
|
36
|
+
try {
|
|
37
|
+
const content = readFileSync(filePath);
|
|
38
|
+
writeFileSync(backupPath, content);
|
|
39
|
+
return backupPath;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function restoreBackup(backupPath, originalPath) {
|
|
46
|
+
try {
|
|
47
|
+
if (!existsSync(backupPath)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const content = readFileSync(backupPath);
|
|
51
|
+
writeFileSync(originalPath, content);
|
|
52
|
+
unlinkSync(backupPath);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function cleanupBackup(backupPath) {
|
|
60
|
+
try {
|
|
61
|
+
if (existsSync(backupPath)) {
|
|
62
|
+
unlinkSync(backupPath);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch { }
|
|
66
|
+
}
|
|
67
|
+
function writeSingleFile(filePath, content, options) {
|
|
68
|
+
const absolutePath = resolve(filePath);
|
|
69
|
+
const parentDir = dirname(absolutePath);
|
|
70
|
+
try {
|
|
71
|
+
const fileExists = existsSync(absolutePath);
|
|
72
|
+
if (fileExists && !options.overwrite) {
|
|
73
|
+
return {
|
|
74
|
+
path: absolutePath,
|
|
75
|
+
status: "skipped",
|
|
76
|
+
bytes: 0,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (!existsSync(parentDir)) {
|
|
80
|
+
if (options.createDirs) {
|
|
81
|
+
mkdirSync(parentDir, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
return {
|
|
85
|
+
path: absolutePath,
|
|
86
|
+
status: "failed",
|
|
87
|
+
bytes: 0,
|
|
88
|
+
error: `Parent directory does not exist: ${parentDir}`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
let backupPath;
|
|
93
|
+
if (fileExists && options.backup && options.atomic) {
|
|
94
|
+
const backup = createBackup(absolutePath);
|
|
95
|
+
if (backup) {
|
|
96
|
+
backupPath = backup;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
writeFileSync(absolutePath, content, { encoding: options.encoding });
|
|
100
|
+
const bytes = Buffer.byteLength(content, options.encoding);
|
|
101
|
+
if (backupPath) {
|
|
102
|
+
cleanupBackup(backupPath);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
path: absolutePath,
|
|
106
|
+
status: fileExists ? "updated" : "created",
|
|
107
|
+
bytes,
|
|
108
|
+
backupPath,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
return {
|
|
113
|
+
path: absolutePath,
|
|
114
|
+
status: "failed",
|
|
115
|
+
bytes: 0,
|
|
116
|
+
error: error instanceof Error ? error.message : String(error),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function executeBatchWrite(input, onUpdate) {
|
|
121
|
+
const startTime = Date.now();
|
|
122
|
+
const options = {
|
|
123
|
+
atomic: input.options?.atomic ?? true,
|
|
124
|
+
backup: input.options?.backup ?? true,
|
|
125
|
+
overwrite: input.options?.overwrite ?? true,
|
|
126
|
+
createDirs: input.options?.createDirs ?? true,
|
|
127
|
+
encoding: input.options?.encoding ?? "utf-8",
|
|
128
|
+
};
|
|
129
|
+
const results = [];
|
|
130
|
+
const backups = new Map();
|
|
131
|
+
for (const file of input.files) {
|
|
132
|
+
const absolutePath = resolve(file.path);
|
|
133
|
+
if (existsSync(absolutePath) && options.backup && options.atomic) {
|
|
134
|
+
const backup = createBackup(absolutePath);
|
|
135
|
+
if (backup) {
|
|
136
|
+
backups.set(absolutePath, backup);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
for (let i = 0; i < input.files.length; i++) {
|
|
141
|
+
const file = input.files[i];
|
|
142
|
+
const result = writeSingleFile(file.path, file.content, options);
|
|
143
|
+
results.push(result);
|
|
144
|
+
if (result.status === "created" || result.status === "updated") {
|
|
145
|
+
const backup = backups.get(result.path);
|
|
146
|
+
if (backup) {
|
|
147
|
+
cleanupBackup(backup);
|
|
148
|
+
backups.delete(result.path);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const statusIcon = result.status === "created"
|
|
152
|
+
? "+"
|
|
153
|
+
: result.status === "updated"
|
|
154
|
+
? "~"
|
|
155
|
+
: result.status === "skipped"
|
|
156
|
+
? "-"
|
|
157
|
+
: "x";
|
|
158
|
+
onUpdate?.({
|
|
159
|
+
content: [
|
|
160
|
+
{
|
|
161
|
+
type: "text",
|
|
162
|
+
text: `[${statusIcon}] ${result.path} (${result.bytes} bytes)`,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
details: {
|
|
166
|
+
success: false,
|
|
167
|
+
total: input.files.length,
|
|
168
|
+
succeeded: results.filter((r) => r.status === "created" || r.status === "updated").length,
|
|
169
|
+
failed: results.filter((r) => r.status === "failed").length,
|
|
170
|
+
skipped: results.filter((r) => r.status === "skipped").length,
|
|
171
|
+
files: results,
|
|
172
|
+
duration: Date.now() - startTime,
|
|
173
|
+
currentIndex: i,
|
|
174
|
+
currentFile: result.path,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
const failedCount = results.filter((r) => r.status === "failed").length;
|
|
179
|
+
if (failedCount > 0 && options.atomic) {
|
|
180
|
+
for (const [originalPath, backupPath] of backups) {
|
|
181
|
+
restoreBackup(backupPath, originalPath);
|
|
182
|
+
}
|
|
183
|
+
for (const result of results) {
|
|
184
|
+
if (result.status === "created") {
|
|
185
|
+
try {
|
|
186
|
+
if (existsSync(result.path)) {
|
|
187
|
+
unlinkSync(result.path);
|
|
188
|
+
}
|
|
189
|
+
result.status = "failed";
|
|
190
|
+
result.error = "Rolled back due to batch failure";
|
|
191
|
+
}
|
|
192
|
+
catch { }
|
|
193
|
+
}
|
|
194
|
+
else if (result.status === "updated") {
|
|
195
|
+
const backup = backups.get(result.path);
|
|
196
|
+
if (backup) {
|
|
197
|
+
restoreBackup(backup, result.path);
|
|
198
|
+
result.status = "failed";
|
|
199
|
+
result.error = "Rolled back due to batch failure";
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const duration = Date.now() - startTime;
|
|
205
|
+
return {
|
|
206
|
+
success: failedCount === 0,
|
|
207
|
+
total: input.files.length,
|
|
208
|
+
succeeded: results.filter((r) => r.status === "created" || r.status === "updated").length,
|
|
209
|
+
failed: results.filter((r) => r.status === "failed").length,
|
|
210
|
+
skipped: results.filter((r) => r.status === "skipped").length,
|
|
211
|
+
files: results,
|
|
212
|
+
duration,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
export const batchWriteTool = {
|
|
216
|
+
name: "batch_write",
|
|
217
|
+
label: "batch_write",
|
|
218
|
+
description: "Batch write multiple files with atomic guarantees. Automatically handles directory creation, backup, and rollback on failure.",
|
|
219
|
+
parameters: BatchWriteSchema,
|
|
220
|
+
execute: async (_id, input, _signal, onUpdate) => {
|
|
221
|
+
const result = executeBatchWrite(input, onUpdate);
|
|
222
|
+
const lines = [
|
|
223
|
+
`Batch Write Complete`,
|
|
224
|
+
`====================`,
|
|
225
|
+
`Total: ${result.total} | Success: ${result.succeeded} | Failed: ${result.failed} | Skipped: ${result.skipped}`,
|
|
226
|
+
`Duration: ${result.duration}ms`,
|
|
227
|
+
];
|
|
228
|
+
return {
|
|
229
|
+
content: [
|
|
230
|
+
{
|
|
231
|
+
type: "text",
|
|
232
|
+
text: lines.join("\n"),
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
details: result,
|
|
236
|
+
};
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
export function detectBatchWriteIntent(toolCalls) {
|
|
240
|
+
const writeCalls = toolCalls.filter((c) => c.name === "write" || c.name === "write_file" || c.name === "create_file");
|
|
241
|
+
if (writeCalls.length < 2) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
const files = [];
|
|
245
|
+
for (const call of writeCalls) {
|
|
246
|
+
const args = call.arguments;
|
|
247
|
+
const path = args.path || args.file_path;
|
|
248
|
+
const content = args.content;
|
|
249
|
+
if (path && content !== undefined) {
|
|
250
|
+
files.push({ path, content });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (files.length < 2) {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
operation: "batch_write",
|
|
258
|
+
files,
|
|
259
|
+
options: {
|
|
260
|
+
atomic: true,
|
|
261
|
+
backup: true,
|
|
262
|
+
overwrite: true,
|
|
263
|
+
createDirs: true,
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=batch-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-write.js","sourceRoot":"","sources":["../../../src/core/tools/batch-write.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACpF,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAElD,MAAM,oBAAoB,GAAG,IAAI,CAAC,MAAM,CAAC;IACxC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACxD,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;CACrD,CAAC,CAAC;AAEH,MAAM,uBAAuB,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3C,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,mCAAmC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACxG,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,uBAAuB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5F,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,0BAA0B,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAClG,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,2BAA2B,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACpG,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;CACxF,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC;IACpC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;IACtC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,oBAAoB,EAAE,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAC1E,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,uBAAuB,CAAC;CAC/C,CAAC,CAAC;AAsBH,SAAS,YAAY,GAAW;IAC/B,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;IAC9D,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,UAAU,CAAC;AAAA,CAClB;AAED,SAAS,YAAY,CAAC,QAAgB,EAAiB;IACtD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACb,CAAC;IAED,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,GAAG,QAAQ,IAAI,SAAS,MAAM,CAAC,CAAC;IAEtE,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACvC,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACnC,OAAO,UAAU,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AAAA,CACD;AAED,SAAS,aAAa,CAAC,UAAkB,EAAE,YAAoB,EAAW;IACzE,IAAI,CAAC;QACJ,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QACzC,aAAa,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACrC,UAAU,CAAC,UAAU,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AAAA,CACD;AAED,SAAS,aAAa,CAAC,UAAkB,EAAQ;IAChD,IAAI,CAAC;QACJ,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,UAAU,CAAC,UAAU,CAAC,CAAC;QACxB,CAAC;IACF,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;AAAA,CACV;AAED,SAAS,eAAe,CACvB,QAAgB,EAChB,OAAe,EACf,OAAyD,EAClC;IACvB,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAExC,IAAI,CAAC;QACJ,MAAM,UAAU,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;QAE5C,IAAI,UAAU,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACtC,OAAO;gBACN,IAAI,EAAE,YAAY;gBAClB,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE,CAAC;aACR,CAAC;QACH,CAAC;QAED,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;gBACxB,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACP,OAAO;oBACN,IAAI,EAAE,YAAY;oBAClB,MAAM,EAAE,QAAQ;oBAChB,KAAK,EAAE,CAAC;oBACR,KAAK,EAAE,oCAAoC,SAAS,EAAE;iBACtD,CAAC;YACH,CAAC;QACF,CAAC;QAED,IAAI,UAA8B,CAAC;QAEnC,IAAI,UAAU,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACpD,MAAM,MAAM,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;YAC1C,IAAI,MAAM,EAAE,CAAC;gBACZ,UAAU,GAAG,MAAM,CAAC;YACrB,CAAC;QACF,CAAC;QAED,aAAa,CAAC,YAAY,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,QAA0B,EAAE,CAAC,CAAC;QACvF,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,QAA0B,CAAC,CAAC;QAE7E,IAAI,UAAU,EAAE,CAAC;YAChB,aAAa,CAAC,UAAU,CAAC,CAAC;QAC3B,CAAC;QAED,OAAO;YACN,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;YAC1C,KAAK;YACL,UAAU;SACV,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO;YACN,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,CAAC;YACR,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC7D,CAAC;IACH,CAAC;AAAA,CACD;AAED,SAAS,iBAAiB,CACzB,KAAsB,EACtB,QAAoD,EACjC;IACnB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAqD;QACjE,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI,IAAI;QACrC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI,IAAI;QACrC,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,SAAS,IAAI,IAAI;QAC3C,UAAU,EAAE,KAAK,CAAC,OAAO,EAAE,UAAU,IAAI,IAAI;QAC7C,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,IAAI,OAAO;KAC5C,CAAC;IAEF,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAChC,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAExC,IAAI,UAAU,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YAClE,MAAM,MAAM,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;YAC1C,IAAI,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YACnC,CAAC;QACF,CAAC;IACF,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAErB,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,MAAM,EAAE,CAAC;gBACZ,aAAa,CAAC,MAAM,CAAC,CAAC;gBACtB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC7B,CAAC;QACF,CAAC;QAED,MAAM,UAAU,GACf,MAAM,CAAC,MAAM,KAAK,SAAS;YAC1B,CAAC,CAAC,GAAG;YACL,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS;gBAC5B,CAAC,CAAC,GAAG;gBACL,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS;oBAC5B,CAAC,CAAC,GAAG;oBACL,CAAC,CAAC,GAAG,CAAC;QAEV,QAAQ,EAAE,CAAC;YACV,OAAO,EAAE;gBACR;oBACC,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,UAAU,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK,SAAS;iBAC9D;aACD;YACD,OAAO,EAAE;gBACR,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;gBACzB,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM;gBACzF,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,MAAM;gBAC3D,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM;gBAC7D,KAAK,EAAE,OAAO;gBACd,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;gBAChC,YAAY,EAAE,CAAC;gBACf,WAAW,EAAE,MAAM,CAAC,IAAI;aACxB;SACD,CAAC,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;IAExE,IAAI,WAAW,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACvC,KAAK,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,IAAI,OAAO,EAAE,CAAC;YAClD,aAAa,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACzC,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC9B,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBACjC,IAAI,CAAC;oBACJ,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;wBAC7B,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;oBACzB,CAAC;oBACD,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC;oBACzB,MAAM,CAAC,KAAK,GAAG,kCAAkC,CAAC;gBACnD,CAAC;gBAAC,MAAM,CAAC,CAAA,CAAC;YACX,CAAC;iBAAM,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBACxC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACxC,IAAI,MAAM,EAAE,CAAC;oBACZ,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;oBACnC,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC;oBACzB,MAAM,CAAC,KAAK,GAAG,kCAAkC,CAAC;gBACnD,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IAExC,OAAO;QACN,OAAO,EAAE,WAAW,KAAK,CAAC;QAC1B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;QACzB,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM;QACzF,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,MAAM;QAC3D,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM;QAC7D,KAAK,EAAE,OAAO;QACd,QAAQ;KACR,CAAC;AAAA,CACF;AAED,MAAM,CAAC,MAAM,cAAc,GAAuC;IACjE,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,aAAa;IACpB,WAAW,EACV,+HAA+H;IAChI,UAAU,EAAE,gBAAgB;IAC5B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC;QACjD,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAElD,MAAM,KAAK,GAAa;YACvB,sBAAsB;YACtB,sBAAsB;YACtB,UAAU,MAAM,CAAC,KAAK,eAAe,MAAM,CAAC,SAAS,cAAc,MAAM,CAAC,MAAM,eAAe,MAAM,CAAC,OAAO,EAAE;YAC/G,aAAa,MAAM,CAAC,QAAQ,IAAI;SAChC,CAAC;QAEF,OAAO;YACN,OAAO,EAAE;gBACR;oBACC,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;iBACtB;aACD;YACD,OAAO,EAAE,MAAM;SACf,CAAC;IAAA,CACF;CACD,CAAC;AAEF,MAAM,UAAU,sBAAsB,CAAC,SAAsD,EAA0B;IACtH,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAClC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa,CAChF,CAAC;IAEF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACb,CAAC;IAED,MAAM,KAAK,GAA6C,EAAE,CAAC;IAE3D,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAoE,CAAC;QACvF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC;QACzC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAE7B,IAAI,IAAI,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/B,CAAC;IACF,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,OAAO;QACN,SAAS,EAAE,aAAa;QACxB,KAAK;QACL,OAAO,EAAE;YACR,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE,IAAI;YACf,UAAU,EAAE,IAAI;SAChB;KACD,CAAC;AAAA,CACF","sourcesContent":["import type { AgentTool, AgentToolUpdateCallback } from \"@boxiaolanya2008/pi-agent-core\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { basename, dirname, resolve } from \"path\";\n\nconst BatchWriteFileSchema = Type.Object({\n\tpath: Type.String({ description: \"File path to write\" }),\n\tcontent: Type.String({ description: \"File content\" }),\n});\n\nconst BatchWriteOptionsSchema = Type.Object({\n\tatomic: Type.Optional(Type.Boolean({ description: \"Enable atomic write with rollback\", default: true })),\n\tbackup: Type.Optional(Type.Boolean({ description: \"Backup existing files\", default: true })),\n\toverwrite: Type.Optional(Type.Boolean({ description: \"Overwrite existing files\", default: true })),\n\tcreateDirs: Type.Optional(Type.Boolean({ description: \"Create parent directories\", default: true })),\n\tencoding: Type.Optional(Type.String({ description: \"File encoding\", default: \"utf-8\" })),\n});\n\nconst BatchWriteSchema = Type.Object({\n\toperation: Type.Literal(\"batch_write\"),\n\tfiles: Type.Array(BatchWriteFileSchema, { description: \"Files to write\" }),\n\toptions: Type.Optional(BatchWriteOptionsSchema),\n});\n\nexport type BatchWriteInput = Static<typeof BatchWriteSchema>;\nexport type BatchWriteFileResult = {\n\tpath: string;\n\tstatus: \"created\" | \"updated\" | \"skipped\" | \"failed\";\n\tbytes: number;\n\tbackupPath?: string;\n\terror?: string;\n};\nexport type BatchWriteResult = {\n\tsuccess: boolean;\n\ttotal: number;\n\tsucceeded: number;\n\tfailed: number;\n\tskipped: number;\n\tfiles: BatchWriteFileResult[];\n\tduration: number;\n\tcurrentIndex?: number;\n\tcurrentFile?: string;\n};\n\nfunction getBackupDir(): string {\n\tconst backupBase = resolve(homedir(), \".openvibe\", \"backups\");\n\tif (!existsSync(backupBase)) {\n\t\tmkdirSync(backupBase, { recursive: true });\n\t}\n\treturn backupBase;\n}\n\nfunction createBackup(filePath: string): string | null {\n\tif (!existsSync(filePath)) {\n\t\treturn null;\n\t}\n\n\tconst backupDir = getBackupDir();\n\tconst timestamp = Date.now();\n\tconst fileName = basename(filePath);\n\tconst backupPath = resolve(backupDir, `${fileName}.${timestamp}.bak`);\n\n\ttry {\n\t\tconst content = readFileSync(filePath);\n\t\twriteFileSync(backupPath, content);\n\t\treturn backupPath;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction restoreBackup(backupPath: string, originalPath: string): boolean {\n\ttry {\n\t\tif (!existsSync(backupPath)) {\n\t\t\treturn false;\n\t\t}\n\t\tconst content = readFileSync(backupPath);\n\t\twriteFileSync(originalPath, content);\n\t\tunlinkSync(backupPath);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction cleanupBackup(backupPath: string): void {\n\ttry {\n\t\tif (existsSync(backupPath)) {\n\t\t\tunlinkSync(backupPath);\n\t\t}\n\t} catch {}\n}\n\nfunction writeSingleFile(\n\tfilePath: string,\n\tcontent: string,\n\toptions: Required<Static<typeof BatchWriteOptionsSchema>>,\n): BatchWriteFileResult {\n\tconst absolutePath = resolve(filePath);\n\tconst parentDir = dirname(absolutePath);\n\n\ttry {\n\t\tconst fileExists = existsSync(absolutePath);\n\n\t\tif (fileExists && !options.overwrite) {\n\t\t\treturn {\n\t\t\t\tpath: absolutePath,\n\t\t\t\tstatus: \"skipped\",\n\t\t\t\tbytes: 0,\n\t\t\t};\n\t\t}\n\n\t\tif (!existsSync(parentDir)) {\n\t\t\tif (options.createDirs) {\n\t\t\t\tmkdirSync(parentDir, { recursive: true });\n\t\t\t} else {\n\t\t\t\treturn {\n\t\t\t\t\tpath: absolutePath,\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\tbytes: 0,\n\t\t\t\t\terror: `Parent directory does not exist: ${parentDir}`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tlet backupPath: string | undefined;\n\n\t\tif (fileExists && options.backup && options.atomic) {\n\t\t\tconst backup = createBackup(absolutePath);\n\t\t\tif (backup) {\n\t\t\t\tbackupPath = backup;\n\t\t\t}\n\t\t}\n\n\t\twriteFileSync(absolutePath, content, { encoding: options.encoding as BufferEncoding });\n\t\tconst bytes = Buffer.byteLength(content, options.encoding as BufferEncoding);\n\n\t\tif (backupPath) {\n\t\t\tcleanupBackup(backupPath);\n\t\t}\n\n\t\treturn {\n\t\t\tpath: absolutePath,\n\t\t\tstatus: fileExists ? \"updated\" : \"created\",\n\t\t\tbytes,\n\t\t\tbackupPath,\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tpath: absolutePath,\n\t\t\tstatus: \"failed\",\n\t\t\tbytes: 0,\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t};\n\t}\n}\n\nfunction executeBatchWrite(\n\tinput: BatchWriteInput,\n\tonUpdate?: AgentToolUpdateCallback<BatchWriteResult>,\n): BatchWriteResult {\n\tconst startTime = Date.now();\n\tconst options: Required<Static<typeof BatchWriteOptionsSchema>> = {\n\t\tatomic: input.options?.atomic ?? true,\n\t\tbackup: input.options?.backup ?? true,\n\t\toverwrite: input.options?.overwrite ?? true,\n\t\tcreateDirs: input.options?.createDirs ?? true,\n\t\tencoding: input.options?.encoding ?? \"utf-8\",\n\t};\n\n\tconst results: BatchWriteFileResult[] = [];\n\tconst backups = new Map<string, string>();\n\n\tfor (const file of input.files) {\n\t\tconst absolutePath = resolve(file.path);\n\n\t\tif (existsSync(absolutePath) && options.backup && options.atomic) {\n\t\t\tconst backup = createBackup(absolutePath);\n\t\t\tif (backup) {\n\t\t\t\tbackups.set(absolutePath, backup);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (let i = 0; i < input.files.length; i++) {\n\t\tconst file = input.files[i];\n\t\tconst result = writeSingleFile(file.path, file.content, options);\n\t\tresults.push(result);\n\n\t\tif (result.status === \"created\" || result.status === \"updated\") {\n\t\t\tconst backup = backups.get(result.path);\n\t\t\tif (backup) {\n\t\t\t\tcleanupBackup(backup);\n\t\t\t\tbackups.delete(result.path);\n\t\t\t}\n\t\t}\n\n\t\tconst statusIcon =\n\t\t\tresult.status === \"created\"\n\t\t\t\t? \"+\"\n\t\t\t\t: result.status === \"updated\"\n\t\t\t\t\t? \"~\"\n\t\t\t\t\t: result.status === \"skipped\"\n\t\t\t\t\t\t? \"-\"\n\t\t\t\t\t\t: \"x\";\n\n\t\tonUpdate?.({\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext: `[${statusIcon}] ${result.path} (${result.bytes} bytes)`,\n\t\t\t\t},\n\t\t\t],\n\t\t\tdetails: {\n\t\t\t\tsuccess: false,\n\t\t\t\ttotal: input.files.length,\n\t\t\t\tsucceeded: results.filter((r) => r.status === \"created\" || r.status === \"updated\").length,\n\t\t\t\tfailed: results.filter((r) => r.status === \"failed\").length,\n\t\t\t\tskipped: results.filter((r) => r.status === \"skipped\").length,\n\t\t\t\tfiles: results,\n\t\t\t\tduration: Date.now() - startTime,\n\t\t\t\tcurrentIndex: i,\n\t\t\t\tcurrentFile: result.path,\n\t\t\t},\n\t\t});\n\t}\n\n\tconst failedCount = results.filter((r) => r.status === \"failed\").length;\n\n\tif (failedCount > 0 && options.atomic) {\n\t\tfor (const [originalPath, backupPath] of backups) {\n\t\t\trestoreBackup(backupPath, originalPath);\n\t\t}\n\n\t\tfor (const result of results) {\n\t\t\tif (result.status === \"created\") {\n\t\t\t\ttry {\n\t\t\t\t\tif (existsSync(result.path)) {\n\t\t\t\t\t\tunlinkSync(result.path);\n\t\t\t\t\t}\n\t\t\t\t\tresult.status = \"failed\";\n\t\t\t\t\tresult.error = \"Rolled back due to batch failure\";\n\t\t\t\t} catch {}\n\t\t\t} else if (result.status === \"updated\") {\n\t\t\t\tconst backup = backups.get(result.path);\n\t\t\t\tif (backup) {\n\t\t\t\t\trestoreBackup(backup, result.path);\n\t\t\t\t\tresult.status = \"failed\";\n\t\t\t\t\tresult.error = \"Rolled back due to batch failure\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconst duration = Date.now() - startTime;\n\n\treturn {\n\t\tsuccess: failedCount === 0,\n\t\ttotal: input.files.length,\n\t\tsucceeded: results.filter((r) => r.status === \"created\" || r.status === \"updated\").length,\n\t\tfailed: results.filter((r) => r.status === \"failed\").length,\n\t\tskipped: results.filter((r) => r.status === \"skipped\").length,\n\t\tfiles: results,\n\t\tduration,\n\t};\n}\n\nexport const batchWriteTool: AgentTool<typeof BatchWriteSchema> = {\n\tname: \"batch_write\",\n\tlabel: \"batch_write\",\n\tdescription:\n\t\t\"Batch write multiple files with atomic guarantees. Automatically handles directory creation, backup, and rollback on failure.\",\n\tparameters: BatchWriteSchema,\n\texecute: async (_id, input, _signal, onUpdate) => {\n\t\tconst result = executeBatchWrite(input, onUpdate);\n\n\t\tconst lines: string[] = [\n\t\t\t`Batch Write Complete`,\n\t\t\t`====================`,\n\t\t\t`Total: ${result.total} | Success: ${result.succeeded} | Failed: ${result.failed} | Skipped: ${result.skipped}`,\n\t\t\t`Duration: ${result.duration}ms`,\n\t\t];\n\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext: lines.join(\"\\n\"),\n\t\t\t\t},\n\t\t\t],\n\t\t\tdetails: result,\n\t\t};\n\t},\n};\n\nexport function detectBatchWriteIntent(toolCalls: Array<{ name: string; arguments: unknown }>): BatchWriteInput | null {\n\tconst writeCalls = toolCalls.filter(\n\t\t(c) => c.name === \"write\" || c.name === \"write_file\" || c.name === \"create_file\",\n\t);\n\n\tif (writeCalls.length < 2) {\n\t\treturn null;\n\t}\n\n\tconst files: Array<{ path: string; content: string }> = [];\n\n\tfor (const call of writeCalls) {\n\t\tconst args = call.arguments as { path?: string; file_path?: string; content?: string };\n\t\tconst path = args.path || args.file_path;\n\t\tconst content = args.content;\n\n\t\tif (path && content !== undefined) {\n\t\t\tfiles.push({ path, content });\n\t\t}\n\t}\n\n\tif (files.length < 2) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\toperation: \"batch_write\",\n\t\tfiles,\n\t\toptions: {\n\t\t\tatomic: true,\n\t\t\tbackup: true,\n\t\t\toverwrite: true,\n\t\t\tcreateDirs: true,\n\t\t},\n\t};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAA2B,MAAM,gCAAgC,CAAC;AACzF,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAetD,QAAA,MAAM,UAAU;;;;EAId,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AACtD,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AACD,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAMD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,cAAc,CAAC;CAC5B;AACD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CA8InG;AACD,eAAO,MAAM,QAAQ;;;;QAAgC,CAAC","sourcesContent":["import type { AgentTool, AgentToolUpdateCallback } from \"@boxiaolanya2008/pi-agent-core\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { constants } from \"fs\";\nimport { access as fsAccess } from \"fs/promises\";\nimport {\n\tdetectLineEnding,\n\tfuzzyFindText,\n\tgenerateDiffString,\n\tnormalizeForFuzzyMatch,\n\tnormalizeToLF,\n\trestoreLineEndings,\n\tstripBom,\n} from \"./edit-diff.js\";\nimport { acceleratedReadFile, acceleratedWriteFile } from \"./local-accelerator.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\n\nconst editSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to edit (relative or absolute)\" }),\n\toldText: Type.String({ description: \"Exact text to find and replace (must match exactly)\" }),\n\tnewText: Type.String({ description: \"New text to replace the old text with\" }),\n});\nexport type EditToolInput = Static<typeof editSchema>;\nexport interface EditToolDetails {\n\tdiff: string;\n\tfirstChangedLine?: number;\n}\nexport interface EditOperations {\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\taccess: (absolutePath: string) => Promise<void>;\n}\nconst defaultEditOperations: EditOperations = {\n\treadFile: (path) => acceleratedReadFile(path),\n\twriteFile: (path, content) => acceleratedWriteFile(path, content),\n\taccess: (path) => fsAccess(path, constants.R_OK | constants.W_OK),\n};\nexport interface EditToolOptions {\n\toperations?: EditOperations;\n}\nexport function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema> {\n\tconst ops = options?.operations ?? defaultEditOperations;\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit a file by replacing exact text. The oldText must match exactly (including whitespace). Use this for precise, surgical edits.\",\n\t\tparameters: editSchema,\n\t\texecute: async (\n\t\t\t_toolCallId: string,\n\t\t\t{ path, oldText, newText }: { path: string; oldText: string; newText: string },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?: AgentToolUpdateCallback<EditToolDetails | undefined>,\n\t\t) => {\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\n\t\t\tonUpdate?.({\n\t\t\t\tcontent: [{ type: \"text\", text: `Editing ${path}...` }],\n\t\t\t\tdetails: undefined,\n\t\t\t});\n\n\t\t\treturn new Promise<{\n\t\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\t\tdetails: EditToolDetails | undefined;\n\t\t\t}>((resolve, reject) => {\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet aborted = false;\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\taborted = true;\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t};\n\t\t\t\tif (signal) {\n\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(new Error(`File not found: ${path}`));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\tconst rawContent = buffer.toString(\"utf-8\");\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst { bom, text: content } = stripBom(rawContent);\n\t\t\t\t\t\tconst originalEnding = detectLineEnding(content);\n\t\t\t\t\t\tconst normalizedContent = normalizeToLF(content);\n\t\t\t\t\t\tconst normalizedOldText = normalizeToLF(oldText);\n\t\t\t\t\t\tconst normalizedNewText = normalizeToLF(newText);\n\t\t\t\t\t\tconst matchResult = fuzzyFindText(normalizedContent, normalizedOldText);\n\t\t\t\t\t\tif (!matchResult.found) {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t`Could not find the exact text in ${path}. The old text must match exactly including all whitespace and newlines.`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst fuzzyContent = normalizeForFuzzyMatch(normalizedContent);\n\t\t\t\t\t\tconst fuzzyOldText = normalizeForFuzzyMatch(normalizedOldText);\n\t\t\t\t\t\tconst occurrences = fuzzyContent.split(fuzzyOldText).length - 1;\n\t\t\t\t\t\tif (occurrences > 1) {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t`Found ${occurrences} occurrences of the text in ${path}. The text must be unique. Please provide more context to make it unique.`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst baseContent = matchResult.contentForReplacement;\n\t\t\t\t\t\tconst newContent =\n\t\t\t\t\t\t\tbaseContent.substring(0, matchResult.index) +\n\t\t\t\t\t\t\tnormalizedNewText +\n\t\t\t\t\t\t\tbaseContent.substring(matchResult.index + matchResult.matchLength);\n\t\t\t\t\t\tif (baseContent === newContent) {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t`No changes made to ${path}. The replacement produced identical content. This might indicate an issue with special characters or the text not existing as expected.`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst finalContent = bom + restoreLineEndings(newContent, originalEnding);\n\t\t\t\t\t\tawait ops.writeFile(absolutePath, finalContent);\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst diffResult = generateDiffString(baseContent, newContent);\n\n\t\t\t\t\t\tonUpdate?.({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: `[~] ${path} (1 replacement)` }],\n\t\t\t\t\t\t\tdetails: { diff: diffResult.diff, firstChangedLine: diffResult.firstChangedLine },\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `[~] ${path} (1 replacement)`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { diff: diffResult.diff, firstChangedLine: diffResult.firstChangedLine },\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!aborted) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t},\n\t};\n}\nexport const editTool = createEditTool(process.cwd());\n"]}
|
package/dist/core/tools/edit.js
CHANGED
|
@@ -21,8 +21,12 @@ export function createEditTool(cwd, options) {
|
|
|
21
21
|
label: "edit",
|
|
22
22
|
description: "Edit a file by replacing exact text. The oldText must match exactly (including whitespace). Use this for precise, surgical edits.",
|
|
23
23
|
parameters: editSchema,
|
|
24
|
-
execute: async (_toolCallId, { path, oldText, newText }, signal) => {
|
|
24
|
+
execute: async (_toolCallId, { path, oldText, newText }, signal, onUpdate) => {
|
|
25
25
|
const absolutePath = resolveToCwd(path, cwd);
|
|
26
|
+
onUpdate?.({
|
|
27
|
+
content: [{ type: "text", text: `Editing ${path}...` }],
|
|
28
|
+
details: undefined,
|
|
29
|
+
});
|
|
26
30
|
return new Promise((resolve, reject) => {
|
|
27
31
|
if (signal?.aborted) {
|
|
28
32
|
reject(new Error("Operation aborted"));
|
|
@@ -102,11 +106,15 @@ export function createEditTool(cwd, options) {
|
|
|
102
106
|
signal.removeEventListener("abort", onAbort);
|
|
103
107
|
}
|
|
104
108
|
const diffResult = generateDiffString(baseContent, newContent);
|
|
109
|
+
onUpdate?.({
|
|
110
|
+
content: [{ type: "text", text: `[~] ${path} (1 replacement)` }],
|
|
111
|
+
details: { diff: diffResult.diff, firstChangedLine: diffResult.firstChangedLine },
|
|
112
|
+
});
|
|
105
113
|
resolve({
|
|
106
114
|
content: [
|
|
107
115
|
{
|
|
108
116
|
type: "text",
|
|
109
|
-
text: `
|
|
117
|
+
text: `[~] ${path} (1 replacement)`,
|
|
110
118
|
},
|
|
111
119
|
],
|
|
112
120
|
details: { diff: diffResult.diff, firstChangedLine: diffResult.firstChangedLine },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@boxiaolanya2008/pi-agent-core\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { constants } from \"fs\";\nimport { access as fsAccess } from \"fs/promises\";\nimport {\n\tdetectLineEnding,\n\tfuzzyFindText,\n\tgenerateDiffString,\n\tnormalizeForFuzzyMatch,\n\tnormalizeToLF,\n\trestoreLineEndings,\n\tstripBom,\n} from \"./edit-diff.js\";\nimport { acceleratedReadFile, acceleratedWriteFile } from \"./local-accelerator.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\n\nconst editSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to edit (relative or absolute)\" }),\n\toldText: Type.String({ description: \"Exact text to find and replace (must match exactly)\" }),\n\tnewText: Type.String({ description: \"New text to replace the old text with\" }),\n});\nexport type EditToolInput = Static<typeof editSchema>;\nexport interface EditToolDetails {\n\tdiff: string;\n\tfirstChangedLine?: number;\n}\nexport interface EditOperations {\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\taccess: (absolutePath: string) => Promise<void>;\n}\nconst defaultEditOperations: EditOperations = {\n\treadFile: (path) => acceleratedReadFile(path),\n\twriteFile: (path, content) => acceleratedWriteFile(path, content),\n\taccess: (path) => fsAccess(path, constants.R_OK | constants.W_OK),\n};\nexport interface EditToolOptions {\n\toperations?: EditOperations;\n}\nexport function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema> {\n\tconst ops = options?.operations ?? defaultEditOperations;\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit a file by replacing exact text. The oldText must match exactly (including whitespace). Use this for precise, surgical edits.\",\n\t\tparameters: editSchema,\n\t\texecute: async (\n\t\t\t_toolCallId: string,\n\t\t\t{ path, oldText, newText }: { path: string; oldText: string; newText: string },\n\t\t\tsignal?: AbortSignal,\n\t\t) => {\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\t\t\treturn new Promise<{\n\t\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\t\tdetails: EditToolDetails | undefined;\n\t\t\t}>((resolve, reject) => {\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet aborted = false;\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\taborted = true;\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t};\n\t\t\t\tif (signal) {\n\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(new Error(`File not found: ${path}`));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\tconst rawContent = buffer.toString(\"utf-8\");\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst { bom, text: content } = stripBom(rawContent);\n\t\t\t\t\t\tconst originalEnding = detectLineEnding(content);\n\t\t\t\t\t\tconst normalizedContent = normalizeToLF(content);\n\t\t\t\t\t\tconst normalizedOldText = normalizeToLF(oldText);\n\t\t\t\t\t\tconst normalizedNewText = normalizeToLF(newText);\n\t\t\t\t\t\tconst matchResult = fuzzyFindText(normalizedContent, normalizedOldText);\n\t\t\t\t\t\tif (!matchResult.found) {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t`Could not find the exact text in ${path}. The old text must match exactly including all whitespace and newlines.`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst fuzzyContent = normalizeForFuzzyMatch(normalizedContent);\n\t\t\t\t\t\tconst fuzzyOldText = normalizeForFuzzyMatch(normalizedOldText);\n\t\t\t\t\t\tconst occurrences = fuzzyContent.split(fuzzyOldText).length - 1;\n\t\t\t\t\t\tif (occurrences > 1) {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t`Found ${occurrences} occurrences of the text in ${path}. The text must be unique. Please provide more context to make it unique.`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst baseContent = matchResult.contentForReplacement;\n\t\t\t\t\t\tconst newContent =\n\t\t\t\t\t\t\tbaseContent.substring(0, matchResult.index) +\n\t\t\t\t\t\t\tnormalizedNewText +\n\t\t\t\t\t\t\tbaseContent.substring(matchResult.index + matchResult.matchLength);\n\t\t\t\t\t\tif (baseContent === newContent) {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t`No changes made to ${path}. The replacement produced identical content. This might indicate an issue with special characters or the text not existing as expected.`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst finalContent = bom + restoreLineEndings(newContent, originalEnding);\n\t\t\t\t\t\tawait ops.writeFile(absolutePath, finalContent);\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst diffResult = generateDiffString(baseContent, newContent);\n\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `Successfully replaced text in ${path}.`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { diff: diffResult.diff, firstChangedLine: diffResult.firstChangedLine },\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!aborted) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t},\n\t};\n}\nexport const editTool = createEditTool(process.cwd());\n"]}
|
|
1
|
+
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool, AgentToolUpdateCallback } from \"@boxiaolanya2008/pi-agent-core\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { constants } from \"fs\";\nimport { access as fsAccess } from \"fs/promises\";\nimport {\n\tdetectLineEnding,\n\tfuzzyFindText,\n\tgenerateDiffString,\n\tnormalizeForFuzzyMatch,\n\tnormalizeToLF,\n\trestoreLineEndings,\n\tstripBom,\n} from \"./edit-diff.js\";\nimport { acceleratedReadFile, acceleratedWriteFile } from \"./local-accelerator.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\n\nconst editSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to edit (relative or absolute)\" }),\n\toldText: Type.String({ description: \"Exact text to find and replace (must match exactly)\" }),\n\tnewText: Type.String({ description: \"New text to replace the old text with\" }),\n});\nexport type EditToolInput = Static<typeof editSchema>;\nexport interface EditToolDetails {\n\tdiff: string;\n\tfirstChangedLine?: number;\n}\nexport interface EditOperations {\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\taccess: (absolutePath: string) => Promise<void>;\n}\nconst defaultEditOperations: EditOperations = {\n\treadFile: (path) => acceleratedReadFile(path),\n\twriteFile: (path, content) => acceleratedWriteFile(path, content),\n\taccess: (path) => fsAccess(path, constants.R_OK | constants.W_OK),\n};\nexport interface EditToolOptions {\n\toperations?: EditOperations;\n}\nexport function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema> {\n\tconst ops = options?.operations ?? defaultEditOperations;\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit a file by replacing exact text. The oldText must match exactly (including whitespace). Use this for precise, surgical edits.\",\n\t\tparameters: editSchema,\n\t\texecute: async (\n\t\t\t_toolCallId: string,\n\t\t\t{ path, oldText, newText }: { path: string; oldText: string; newText: string },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?: AgentToolUpdateCallback<EditToolDetails | undefined>,\n\t\t) => {\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\n\t\t\tonUpdate?.({\n\t\t\t\tcontent: [{ type: \"text\", text: `Editing ${path}...` }],\n\t\t\t\tdetails: undefined,\n\t\t\t});\n\n\t\t\treturn new Promise<{\n\t\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\t\tdetails: EditToolDetails | undefined;\n\t\t\t}>((resolve, reject) => {\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet aborted = false;\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\taborted = true;\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t};\n\t\t\t\tif (signal) {\n\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(new Error(`File not found: ${path}`));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\tconst rawContent = buffer.toString(\"utf-8\");\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst { bom, text: content } = stripBom(rawContent);\n\t\t\t\t\t\tconst originalEnding = detectLineEnding(content);\n\t\t\t\t\t\tconst normalizedContent = normalizeToLF(content);\n\t\t\t\t\t\tconst normalizedOldText = normalizeToLF(oldText);\n\t\t\t\t\t\tconst normalizedNewText = normalizeToLF(newText);\n\t\t\t\t\t\tconst matchResult = fuzzyFindText(normalizedContent, normalizedOldText);\n\t\t\t\t\t\tif (!matchResult.found) {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t`Could not find the exact text in ${path}. The old text must match exactly including all whitespace and newlines.`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst fuzzyContent = normalizeForFuzzyMatch(normalizedContent);\n\t\t\t\t\t\tconst fuzzyOldText = normalizeForFuzzyMatch(normalizedOldText);\n\t\t\t\t\t\tconst occurrences = fuzzyContent.split(fuzzyOldText).length - 1;\n\t\t\t\t\t\tif (occurrences > 1) {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t`Found ${occurrences} occurrences of the text in ${path}. The text must be unique. Please provide more context to make it unique.`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst baseContent = matchResult.contentForReplacement;\n\t\t\t\t\t\tconst newContent =\n\t\t\t\t\t\t\tbaseContent.substring(0, matchResult.index) +\n\t\t\t\t\t\t\tnormalizedNewText +\n\t\t\t\t\t\t\tbaseContent.substring(matchResult.index + matchResult.matchLength);\n\t\t\t\t\t\tif (baseContent === newContent) {\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t`No changes made to ${path}. The replacement produced identical content. This might indicate an issue with special characters or the text not existing as expected.`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst finalContent = bom + restoreLineEndings(newContent, originalEnding);\n\t\t\t\t\t\tawait ops.writeFile(absolutePath, finalContent);\n\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst diffResult = generateDiffString(baseContent, newContent);\n\n\t\t\t\t\t\tonUpdate?.({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: `[~] ${path} (1 replacement)` }],\n\t\t\t\t\t\t\tdetails: { diff: diffResult.diff, firstChangedLine: diffResult.firstChangedLine },\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `[~] ${path} (1 replacement)`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { diff: diffResult.diff, firstChangedLine: diffResult.firstChangedLine },\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!aborted) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t},\n\t};\n}\nexport const editTool = createEditTool(process.cwd());\n"]}
|
|
@@ -18,8 +18,13 @@ interface ExecuteToolsOptions {
|
|
|
18
18
|
maxConcurrency?: number;
|
|
19
19
|
priority?: number;
|
|
20
20
|
}
|
|
21
|
+
export interface ToolUpdateEvent {
|
|
22
|
+
toolCallId: string;
|
|
23
|
+
toolName: string;
|
|
24
|
+
partialResult: AgentToolResult<any>;
|
|
25
|
+
}
|
|
21
26
|
export declare function extractToolCalls(tools: AgentTool<any>[] | undefined, assistantMessage: AssistantMessage): ToolCallInfo[];
|
|
22
|
-
export declare function executeToolsParallel(tools: AgentTool<any>[] | undefined, assistantMessage: AssistantMessage, signal?: AbortSignal, onProgress?: (result: ToolExecutionResult) => void, options?: ExecuteToolsOptions): Promise<ToolResultMessage[]>;
|
|
27
|
+
export declare function executeToolsParallel(tools: AgentTool<any>[] | undefined, assistantMessage: AssistantMessage, signal?: AbortSignal, onProgress?: (result: ToolExecutionResult) => void, options?: ExecuteToolsOptions, onToolUpdate?: (event: ToolUpdateEvent) => void): Promise<ToolResultMessage[]>;
|
|
23
28
|
export declare function canParallelize(tools: ToolCallInfo[]): boolean;
|
|
24
29
|
export declare function groupToolsByDependency(tools: ToolCallInfo[]): ToolCallInfo[][];
|
|
25
30
|
export declare function getExecutionStats(): Record<string, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-executor.d.ts","sourceRoot":"","sources":["../../../src/core/tools/fast-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGlF,UAAU,YAAY;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;CACjC;AACD,UAAU,mBAAmB;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACjB;AACD,UAAU,mBAAmB;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"fast-executor.d.ts","sourceRoot":"","sources":["../../../src/core/tools/fast-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGlF,UAAU,YAAY;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;CACjC;AACD,UAAU,mBAAmB;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACjB;AACD,UAAU,mBAAmB;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC;CACpC;AAkFD,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,EACnC,gBAAgB,EAAE,gBAAgB,GAChC,YAAY,EAAE,CAUhB;AAoCD,wBAAsB,oBAAoB,CACzC,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,EACnC,gBAAgB,EAAE,gBAAgB,EAClC,MAAM,CAAC,EAAE,WAAW,EACpB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,EAClD,OAAO,GAAE,mBAAwB,EACjC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GAC7C,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA4B9B;AAYD,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,OAAO,CAE7D;AACD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,EAAE,CAE9E;AACD,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAE5G;AACD,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C","sourcesContent":["import type { AgentTool, AgentToolResult } from \"@boxiaolanya2008/pi-agent-core\";\nimport type { AssistantMessage, ToolResultMessage } from \"@boxiaolanya2008/pi-ai\";\nimport { globalMetrics, parallelExecute } from \"./parallel-executor.js\";\n\ninterface ToolCallInfo {\n\tid: string;\n\tname: string;\n\targs: Record<string, unknown>;\n\ttool: AgentTool<any> | undefined;\n}\ninterface ToolExecutionResult {\n\ttoolCallId: string;\n\ttoolName: string;\n\tresult: AgentToolResult<any>;\n\tisError: boolean;\n\tduration: number;\n}\ninterface ExecuteToolsOptions {\n\tparallel?: boolean;\n\tmaxConcurrency?: number;\n\tpriority?: number;\n}\n\nexport interface ToolUpdateEvent {\n\ttoolCallId: string;\n\ttoolName: string;\n\tpartialResult: AgentToolResult<any>;\n}\n\nconst BATCH_WRITE_THRESHOLD = 2;\nconst WRITE_TOOL_NAMES = [\"write\", \"write_file\", \"create_file\", \"edit\"];\n\nfunction isWriteToolCall(tc: ToolCallInfo): boolean {\n\treturn WRITE_TOOL_NAMES.includes(tc.name);\n}\n\nfunction extractWriteFileData(tc: ToolCallInfo): { path: string; content: string } | null {\n\tconst args = tc.args as Record<string, unknown>;\n\tconst path = (args.path as string) || (args.file_path as string) || (args.filePath as string);\n\tconst content = (args.content as string) || (args.newText as string);\n\n\tif (!path || content === undefined) {\n\t\treturn null;\n\t}\n\n\treturn { path, content };\n}\n\nfunction optimizeToolCalls(toolCalls: ToolCallInfo[], tools: AgentTool<any>[] | undefined): ToolCallInfo[] {\n\tif (toolCalls.length < BATCH_WRITE_THRESHOLD) {\n\t\treturn toolCalls;\n\t}\n\n\tconst writeCalls: ToolCallInfo[] = [];\n\tconst otherCalls: ToolCallInfo[] = [];\n\n\tfor (const tc of toolCalls) {\n\t\tif (isWriteToolCall(tc)) {\n\t\t\twriteCalls.push(tc);\n\t\t} else {\n\t\t\totherCalls.push(tc);\n\t\t}\n\t}\n\n\tif (writeCalls.length < BATCH_WRITE_THRESHOLD) {\n\t\treturn toolCalls;\n\t}\n\n\tconst files: Array<{ path: string; content: string }> = [];\n\tconst writeCallIds: string[] = [];\n\n\tfor (const tc of writeCalls) {\n\t\tconst fileData = extractWriteFileData(tc);\n\t\tif (fileData) {\n\t\t\tfiles.push(fileData);\n\t\t\twriteCallIds.push(tc.id);\n\t\t} else {\n\t\t\totherCalls.push(tc);\n\t\t}\n\t}\n\n\tif (files.length < BATCH_WRITE_THRESHOLD) {\n\t\treturn toolCalls;\n\t}\n\n\tconst batchWriteTool = tools?.find((t) => t.name === \"batch_write\");\n\tif (!batchWriteTool) {\n\t\treturn toolCalls;\n\t}\n\n\tconst batchCall: ToolCallInfo = {\n\t\tid: `batch_${writeCallIds.join(\"_\")}`,\n\t\tname: \"batch_write\",\n\t\targs: {\n\t\t\toperation: \"batch_write\",\n\t\t\tfiles,\n\t\t\toptions: {\n\t\t\t\tatomic: true,\n\t\t\t\tbackup: true,\n\t\t\t\toverwrite: true,\n\t\t\t\tcreateDirs: true,\n\t\t\t},\n\t\t},\n\t\ttool: batchWriteTool,\n\t};\n\n\treturn [batchCall, ...otherCalls];\n}\n\nexport function extractToolCalls(\n\ttools: AgentTool<any>[] | undefined,\n\tassistantMessage: AssistantMessage,\n): ToolCallInfo[] {\n\tconst toolCalls = assistantMessage.content.filter((c) => c.type === \"toolCall\");\n\tconst rawCalls = toolCalls.map((tc) => ({\n\t\tid: tc.id,\n\t\tname: tc.name,\n\t\targs: tc.arguments,\n\t\ttool: tools?.find((t) => t.name === tc.name),\n\t}));\n\n\treturn optimizeToolCalls(rawCalls, tools);\n}\nasync function executeSingleTool(\n\ttoolCall: ToolCallInfo,\n\tsignal?: AbortSignal,\n\tonUpdate?: (partialResult: AgentToolResult<any>) => void,\n): Promise<ToolExecutionResult> {\n\tconst startTime = Date.now();\n\ttry {\n\t\tif (!toolCall.tool) {\n\t\t\tthrow new Error(`Tool ${toolCall.name} not found`);\n\t\t}\n\t\tconst result = await toolCall.tool.execute(toolCall.id, toolCall.args, signal, onUpdate);\n\t\tconst duration = Date.now() - startTime;\n\t\tglobalMetrics.record(`tool:${toolCall.name}`, duration);\n\t\treturn {\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\tresult,\n\t\t\tisError: false,\n\t\t\tduration,\n\t\t};\n\t} catch (error) {\n\t\tconst duration = Date.now() - startTime;\n\t\tglobalMetrics.record(`tool:${toolCall.name}:error`, duration);\n\t\treturn {\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\tresult: {\n\t\t\t\tcontent: [{ type: \"text\", text: error instanceof Error ? error.message : String(error) }],\n\t\t\t\tdetails: {},\n\t\t\t},\n\t\t\tisError: true,\n\t\t\tduration,\n\t\t};\n\t}\n}\nexport async function executeToolsParallel(\n\ttools: AgentTool<any>[] | undefined,\n\tassistantMessage: AssistantMessage,\n\tsignal?: AbortSignal,\n\tonProgress?: (result: ToolExecutionResult) => void,\n\toptions: ExecuteToolsOptions = {},\n\tonToolUpdate?: (event: ToolUpdateEvent) => void,\n): Promise<ToolResultMessage[]> {\n\tconst { parallel = true, maxConcurrency = 8 } = options;\n\tconst toolCalls = extractToolCalls(tools, assistantMessage);\n\tif (toolCalls.length === 0) {\n\t\treturn [];\n\t}\n\n\tconst createOnUpdate = (toolCall: ToolCallInfo) => {\n\t\tif (!onToolUpdate) return undefined;\n\t\treturn (partialResult: AgentToolResult<any>) => {\n\t\t\tonToolUpdate({\n\t\t\t\ttoolCallId: toolCall.id,\n\t\t\t\ttoolName: toolCall.name,\n\t\t\t\tpartialResult,\n\t\t\t});\n\t\t};\n\t};\n\n\tif (toolCalls.length === 1 || !parallel) {\n\t\tconst result = await executeSingleTool(toolCalls[0], signal, createOnUpdate(toolCalls[0]));\n\t\tonProgress?.(result);\n\t\treturn [toToolResultMessage(result)];\n\t}\n\tconst results = await parallelExecute(toolCalls, (tc) => executeSingleTool(tc, signal, createOnUpdate(tc)), {\n\t\tconcurrency: maxConcurrency,\n\t});\n\tfor (const result of results) onProgress?.(result);\n\treturn results.map(toToolResultMessage);\n}\nfunction toToolResultMessage(result: ToolExecutionResult): ToolResultMessage {\n\treturn {\n\t\trole: \"toolResult\",\n\t\ttoolCallId: result.toolCallId,\n\t\ttoolName: result.toolName,\n\t\tcontent: result.result.content,\n\t\tdetails: result.result.details,\n\t\tisError: result.isError,\n\t\ttimestamp: Date.now(),\n\t};\n}\nexport function canParallelize(tools: ToolCallInfo[]): boolean {\n\treturn tools.length > 1;\n}\nexport function groupToolsByDependency(tools: ToolCallInfo[]): ToolCallInfo[][] {\n\treturn [tools];\n}\nexport function getExecutionStats(): Record<string, { avg: number; min: number; max: number; count: number }> {\n\treturn globalMetrics.getAllStats();\n}\nexport function resetExecutionStats(): void {\n\tglobalMetrics.reset();\n}\n"]}
|
|
@@ -1,12 +1,80 @@
|
|
|
1
1
|
import { globalMetrics, parallelExecute } from "./parallel-executor.js";
|
|
2
|
+
const BATCH_WRITE_THRESHOLD = 2;
|
|
3
|
+
const WRITE_TOOL_NAMES = ["write", "write_file", "create_file", "edit"];
|
|
4
|
+
function isWriteToolCall(tc) {
|
|
5
|
+
return WRITE_TOOL_NAMES.includes(tc.name);
|
|
6
|
+
}
|
|
7
|
+
function extractWriteFileData(tc) {
|
|
8
|
+
const args = tc.args;
|
|
9
|
+
const path = args.path || args.file_path || args.filePath;
|
|
10
|
+
const content = args.content || args.newText;
|
|
11
|
+
if (!path || content === undefined) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return { path, content };
|
|
15
|
+
}
|
|
16
|
+
function optimizeToolCalls(toolCalls, tools) {
|
|
17
|
+
if (toolCalls.length < BATCH_WRITE_THRESHOLD) {
|
|
18
|
+
return toolCalls;
|
|
19
|
+
}
|
|
20
|
+
const writeCalls = [];
|
|
21
|
+
const otherCalls = [];
|
|
22
|
+
for (const tc of toolCalls) {
|
|
23
|
+
if (isWriteToolCall(tc)) {
|
|
24
|
+
writeCalls.push(tc);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
otherCalls.push(tc);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (writeCalls.length < BATCH_WRITE_THRESHOLD) {
|
|
31
|
+
return toolCalls;
|
|
32
|
+
}
|
|
33
|
+
const files = [];
|
|
34
|
+
const writeCallIds = [];
|
|
35
|
+
for (const tc of writeCalls) {
|
|
36
|
+
const fileData = extractWriteFileData(tc);
|
|
37
|
+
if (fileData) {
|
|
38
|
+
files.push(fileData);
|
|
39
|
+
writeCallIds.push(tc.id);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
otherCalls.push(tc);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (files.length < BATCH_WRITE_THRESHOLD) {
|
|
46
|
+
return toolCalls;
|
|
47
|
+
}
|
|
48
|
+
const batchWriteTool = tools?.find((t) => t.name === "batch_write");
|
|
49
|
+
if (!batchWriteTool) {
|
|
50
|
+
return toolCalls;
|
|
51
|
+
}
|
|
52
|
+
const batchCall = {
|
|
53
|
+
id: `batch_${writeCallIds.join("_")}`,
|
|
54
|
+
name: "batch_write",
|
|
55
|
+
args: {
|
|
56
|
+
operation: "batch_write",
|
|
57
|
+
files,
|
|
58
|
+
options: {
|
|
59
|
+
atomic: true,
|
|
60
|
+
backup: true,
|
|
61
|
+
overwrite: true,
|
|
62
|
+
createDirs: true,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
tool: batchWriteTool,
|
|
66
|
+
};
|
|
67
|
+
return [batchCall, ...otherCalls];
|
|
68
|
+
}
|
|
2
69
|
export function extractToolCalls(tools, assistantMessage) {
|
|
3
70
|
const toolCalls = assistantMessage.content.filter((c) => c.type === "toolCall");
|
|
4
|
-
|
|
71
|
+
const rawCalls = toolCalls.map((tc) => ({
|
|
5
72
|
id: tc.id,
|
|
6
73
|
name: tc.name,
|
|
7
74
|
args: tc.arguments,
|
|
8
75
|
tool: tools?.find((t) => t.name === tc.name),
|
|
9
76
|
}));
|
|
77
|
+
return optimizeToolCalls(rawCalls, tools);
|
|
10
78
|
}
|
|
11
79
|
async function executeSingleTool(toolCall, signal, onUpdate) {
|
|
12
80
|
const startTime = Date.now();
|
|
@@ -40,18 +108,29 @@ async function executeSingleTool(toolCall, signal, onUpdate) {
|
|
|
40
108
|
};
|
|
41
109
|
}
|
|
42
110
|
}
|
|
43
|
-
export async function executeToolsParallel(tools, assistantMessage, signal, onProgress, options = {}) {
|
|
111
|
+
export async function executeToolsParallel(tools, assistantMessage, signal, onProgress, options = {}, onToolUpdate) {
|
|
44
112
|
const { parallel = true, maxConcurrency = 8 } = options;
|
|
45
113
|
const toolCalls = extractToolCalls(tools, assistantMessage);
|
|
46
114
|
if (toolCalls.length === 0) {
|
|
47
115
|
return [];
|
|
48
116
|
}
|
|
117
|
+
const createOnUpdate = (toolCall) => {
|
|
118
|
+
if (!onToolUpdate)
|
|
119
|
+
return undefined;
|
|
120
|
+
return (partialResult) => {
|
|
121
|
+
onToolUpdate({
|
|
122
|
+
toolCallId: toolCall.id,
|
|
123
|
+
toolName: toolCall.name,
|
|
124
|
+
partialResult,
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
};
|
|
49
128
|
if (toolCalls.length === 1 || !parallel) {
|
|
50
|
-
const result = await executeSingleTool(toolCalls[0], signal);
|
|
129
|
+
const result = await executeSingleTool(toolCalls[0], signal, createOnUpdate(toolCalls[0]));
|
|
51
130
|
onProgress?.(result);
|
|
52
131
|
return [toToolResultMessage(result)];
|
|
53
132
|
}
|
|
54
|
-
const results = await parallelExecute(toolCalls, (tc) => executeSingleTool(tc, signal), {
|
|
133
|
+
const results = await parallelExecute(toolCalls, (tc) => executeSingleTool(tc, signal, createOnUpdate(tc)), {
|
|
55
134
|
concurrency: maxConcurrency,
|
|
56
135
|
});
|
|
57
136
|
for (const result of results)
|