min-agent 0.1.7 → 0.1.8
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.js +32 -2
- package/dist/confirm.js +10 -1
- package/dist/tools/bash.js +12 -0
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -14,6 +14,8 @@ import { DoomLoopDetector } from "./doom-loop.js";
|
|
|
14
14
|
import { scanProject, buildCodeSystemPrompt } from "./code-mode.js";
|
|
15
15
|
import { ThinkingBodySplitter, stripThinkingFromAssistantText } from "./assistant-stream.js";
|
|
16
16
|
import { printHeader, printDivider, printToolCall, printToolResult, printDone } from "./output.js";
|
|
17
|
+
import { killActiveProcesses } from "./tools/bash.js";
|
|
18
|
+
import { setConfirmReadline } from "./confirm.js";
|
|
17
19
|
import readline from "readline";
|
|
18
20
|
const MAX_STEPS = 30;
|
|
19
21
|
function dimStyle() {
|
|
@@ -142,10 +144,12 @@ export async function runChat(modelId, resumeSessionId) {
|
|
|
142
144
|
output: process.stdout,
|
|
143
145
|
prompt: "\x1b[36m> \x1b[0m",
|
|
144
146
|
});
|
|
147
|
+
setConfirmReadline(rl);
|
|
145
148
|
// Handle Ctrl+C: abort current generation, don't exit
|
|
146
149
|
let abortController = null;
|
|
147
150
|
process.on("SIGINT", () => {
|
|
148
151
|
if (abortController) {
|
|
152
|
+
killActiveProcesses();
|
|
149
153
|
abortController.abort();
|
|
150
154
|
abortController = null;
|
|
151
155
|
console.log("\n\x1b[90m(cancelled)\x1b[0m\n");
|
|
@@ -157,7 +161,19 @@ export async function runChat(modelId, resumeSessionId) {
|
|
|
157
161
|
rl.close();
|
|
158
162
|
}
|
|
159
163
|
});
|
|
160
|
-
|
|
164
|
+
// Listen for ESC key to cancel running agent task
|
|
165
|
+
if (process.stdin.isTTY) {
|
|
166
|
+
process.stdin.on("data", (buf) => {
|
|
167
|
+
if (buf.length === 1 && buf[0] === 0x1b && abortController) {
|
|
168
|
+
killActiveProcesses();
|
|
169
|
+
abortController.abort();
|
|
170
|
+
abortController = null;
|
|
171
|
+
console.log("\n\x1b[90m(esc cancelled)\x1b[0m\n");
|
|
172
|
+
rl.prompt();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
console.log("\x1b[90m输入消息开始对话,输入 /help 查看命令,Esc 取消运行,/exit 退出\x1b[0m\n");
|
|
161
177
|
rl.prompt();
|
|
162
178
|
// Multi-line paste detection: collect rapid successive lines
|
|
163
179
|
let pasteBuffer = [];
|
|
@@ -251,9 +267,11 @@ export async function runCode(modelId, resumeSessionId) {
|
|
|
251
267
|
output: process.stdout,
|
|
252
268
|
prompt: "\x1b[32m❯ \x1b[0m",
|
|
253
269
|
});
|
|
270
|
+
setConfirmReadline(rl);
|
|
254
271
|
let abortController = null;
|
|
255
272
|
process.on("SIGINT", () => {
|
|
256
273
|
if (abortController) {
|
|
274
|
+
killActiveProcesses();
|
|
257
275
|
abortController.abort();
|
|
258
276
|
abortController = null;
|
|
259
277
|
console.log("\n\x1b[90m(cancelled)\x1b[0m\n");
|
|
@@ -264,7 +282,19 @@ export async function runCode(modelId, resumeSessionId) {
|
|
|
264
282
|
rl.close();
|
|
265
283
|
}
|
|
266
284
|
});
|
|
267
|
-
|
|
285
|
+
// Listen for ESC key to cancel running agent task
|
|
286
|
+
if (process.stdin.isTTY) {
|
|
287
|
+
process.stdin.on("data", (buf) => {
|
|
288
|
+
if (buf.length === 1 && buf[0] === 0x1b && abortController) {
|
|
289
|
+
killActiveProcesses();
|
|
290
|
+
abortController.abort();
|
|
291
|
+
abortController = null;
|
|
292
|
+
console.log("\n\x1b[90m(esc cancelled)\x1b[0m\n");
|
|
293
|
+
rl.prompt();
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
console.log("\x1b[90m输入任务开始编码,/help 查看命令,Esc 取消运行,Ctrl+C 中断\x1b[0m\n");
|
|
268
298
|
rl.prompt();
|
|
269
299
|
let pasteBuffer = [];
|
|
270
300
|
let pasteTimer = null;
|
package/dist/confirm.js
CHANGED
|
@@ -5,11 +5,18 @@ export function setAutoApprove(value) {
|
|
|
5
5
|
export function isAutoApprove() {
|
|
6
6
|
return autoApprove;
|
|
7
7
|
}
|
|
8
|
+
/** Optional readline interface to pause/resume during confirmation prompts. */
|
|
9
|
+
let _rl = null;
|
|
10
|
+
export function setConfirmReadline(rl) {
|
|
11
|
+
_rl = rl;
|
|
12
|
+
}
|
|
8
13
|
/** Ask user for confirmation. Returns true if approved. */
|
|
9
14
|
export async function confirm(message) {
|
|
10
15
|
if (autoApprove)
|
|
11
16
|
return true;
|
|
12
|
-
|
|
17
|
+
// Pause readline so it doesn't consume/echo the keystroke
|
|
18
|
+
_rl?.pause();
|
|
19
|
+
process.stdout.write(`\n\n\x1b[1;34m⚠ ${message} [y/N] \x1b[0m\n\n`);
|
|
13
20
|
return new Promise((resolve) => {
|
|
14
21
|
const wasRaw = process.stdin.isRaw;
|
|
15
22
|
if (process.stdin.isTTY)
|
|
@@ -21,6 +28,8 @@ export async function confirm(message) {
|
|
|
21
28
|
process.stdin.setRawMode(wasRaw ?? false);
|
|
22
29
|
// Echo the character and newline
|
|
23
30
|
process.stdout.write(ch === "\r" || ch === "\n" ? "\n" : `${ch}\n`);
|
|
31
|
+
// Resume readline after confirmation
|
|
32
|
+
_rl?.resume();
|
|
24
33
|
const answer = ch.trim().toLowerCase();
|
|
25
34
|
resolve(answer === "y");
|
|
26
35
|
};
|
package/dist/tools/bash.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { tool, jsonSchema } from "ai";
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
3
|
import { confirm, isDangerousCommand, isAutoApprove } from "../confirm.js";
|
|
4
|
+
/** Track active child processes so they can be killed on abort (e.g. ESC). */
|
|
5
|
+
const activeProcesses = new Set();
|
|
6
|
+
/** Kill all active child processes spawned by the bash tool. */
|
|
7
|
+
export function killActiveProcesses() {
|
|
8
|
+
for (const proc of activeProcesses) {
|
|
9
|
+
killProcess(proc.pid);
|
|
10
|
+
}
|
|
11
|
+
activeProcesses.clear();
|
|
12
|
+
}
|
|
4
13
|
export const bashTool = tool({
|
|
5
14
|
description: "Run a shell command. Use this for system operations, running builds, tests, git commands, etc. The command runs in the current working directory. You SHOULD set a timeout based on how long you expect the command to take. If no timeout is set, the command runs until it finishes or the user manually interrupts (Ctrl+C).",
|
|
6
15
|
inputSchema: jsonSchema({
|
|
@@ -31,6 +40,7 @@ export const bashTool = tool({
|
|
|
31
40
|
detached: process.platform !== "win32",
|
|
32
41
|
env: { ...process.env, ...(isWin ? { PYTHONIOENCODING: "utf-8" } : {}) },
|
|
33
42
|
});
|
|
43
|
+
activeProcesses.add(proc);
|
|
34
44
|
proc.stdout?.on("data", (chunk) => chunks.push(chunk));
|
|
35
45
|
proc.stderr?.on("data", (chunk) => chunks.push(chunk));
|
|
36
46
|
// Timeout kill (only if timeout is specified)
|
|
@@ -52,6 +62,7 @@ export const bashTool = tool({
|
|
|
52
62
|
};
|
|
53
63
|
process.on("SIGINT", sigintHandler);
|
|
54
64
|
proc.on("close", (code) => {
|
|
65
|
+
activeProcesses.delete(proc);
|
|
55
66
|
process.removeListener("SIGINT", sigintHandler);
|
|
56
67
|
if (timer)
|
|
57
68
|
clearTimeout(timer);
|
|
@@ -66,6 +77,7 @@ export const bashTool = tool({
|
|
|
66
77
|
}
|
|
67
78
|
});
|
|
68
79
|
proc.on("error", (err) => {
|
|
80
|
+
activeProcesses.delete(proc);
|
|
69
81
|
process.removeListener("SIGINT", sigintHandler);
|
|
70
82
|
if (timer)
|
|
71
83
|
clearTimeout(timer);
|