@cosmicstack/mercury-agent 1.0.3 → 1.0.5
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/index.js +90 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3438,8 +3438,10 @@ var ToolCallLoopDetector = class _ToolCallLoopDetector {
|
|
|
3438
3438
|
totalCalls = 0;
|
|
3439
3439
|
hardAborted = false;
|
|
3440
3440
|
recentStepTexts = [];
|
|
3441
|
+
consecutiveNoActionSteps = 0;
|
|
3441
3442
|
static ABSOLUTE_MAX = 25;
|
|
3442
3443
|
static FAILED_ABSOLUTE_MAX = 12;
|
|
3444
|
+
static NO_ACTION_MAX = 5;
|
|
3443
3445
|
static HIGH_TOLERANCE_TOOLS = /* @__PURE__ */ new Set([
|
|
3444
3446
|
"fetch_url",
|
|
3445
3447
|
"read_file",
|
|
@@ -3465,10 +3467,15 @@ var ToolCallLoopDetector = class _ToolCallLoopDetector {
|
|
|
3465
3467
|
const paramsKey = JSON.stringify(params).slice(0, 200);
|
|
3466
3468
|
this.recentCalls.push({ tool: toolName, params: paramsKey, failed });
|
|
3467
3469
|
this.totalCalls++;
|
|
3470
|
+
this.consecutiveNoActionSteps = 0;
|
|
3468
3471
|
if (this.recentCalls.length > 30) {
|
|
3469
3472
|
this.recentCalls.shift();
|
|
3470
3473
|
}
|
|
3471
3474
|
}
|
|
3475
|
+
recordNoActionResult() {
|
|
3476
|
+
this.consecutiveNoActionSteps++;
|
|
3477
|
+
return this.consecutiveNoActionSteps >= _ToolCallLoopDetector.NO_ACTION_MAX;
|
|
3478
|
+
}
|
|
3472
3479
|
recordStepText(text) {
|
|
3473
3480
|
if (!text || text.length < 10) return;
|
|
3474
3481
|
const normalized = text.toLowerCase().replace(/\s+/g, " ").trim().slice(0, 200);
|
|
@@ -3597,6 +3604,7 @@ var ToolCallLoopDetector = class _ToolCallLoopDetector {
|
|
|
3597
3604
|
this.totalCalls = 0;
|
|
3598
3605
|
this.hardAborted = false;
|
|
3599
3606
|
this.recentStepTexts = [];
|
|
3607
|
+
this.consecutiveNoActionSteps = 0;
|
|
3600
3608
|
}
|
|
3601
3609
|
};
|
|
3602
3610
|
var MAX_STEPS = 10;
|
|
@@ -3965,6 +3973,17 @@ You can override this:
|
|
|
3965
3973
|
if (stepText) {
|
|
3966
3974
|
loopDetector.recordStepText(String(stepText));
|
|
3967
3975
|
}
|
|
3976
|
+
const noActionLoop = loopDetector.recordNoActionResult();
|
|
3977
|
+
if (noActionLoop) {
|
|
3978
|
+
logger.warn("Reasoning loop detected \u2014 model keeps thinking without acting, aborting");
|
|
3979
|
+
if (!loopWarningSent && channel && msg.channelType !== "internal") {
|
|
3980
|
+
loopWarningSent = true;
|
|
3981
|
+
await channel.send("\u26A0 I'm stuck in a reasoning loop (thinking without taking action). Stopping.", msg.channelId).catch(() => {
|
|
3982
|
+
});
|
|
3983
|
+
}
|
|
3984
|
+
loopAbortController.abort();
|
|
3985
|
+
return;
|
|
3986
|
+
}
|
|
3968
3987
|
const textRepeat = loopDetector.detectTextRepetition();
|
|
3969
3988
|
if (textRepeat) {
|
|
3970
3989
|
logger.warn({ pattern: textRepeat.pattern, count: textRepeat.count }, "Text repetition loop detected \u2014 aborting");
|
|
@@ -4113,6 +4132,17 @@ You can override this:
|
|
|
4113
4132
|
if (stepText) {
|
|
4114
4133
|
loopDetector.recordStepText(String(stepText));
|
|
4115
4134
|
}
|
|
4135
|
+
const noActionLoop = loopDetector.recordNoActionResult();
|
|
4136
|
+
if (noActionLoop) {
|
|
4137
|
+
logger.warn("Reasoning loop detected \u2014 model keeps thinking without acting, aborting");
|
|
4138
|
+
if (!loopWarningSent && channel && msg.channelType !== "internal") {
|
|
4139
|
+
loopWarningSent = true;
|
|
4140
|
+
await channel.send("\u26A0 I'm stuck in a reasoning loop (thinking without taking action). Stopping.", msg.channelId).catch(() => {
|
|
4141
|
+
});
|
|
4142
|
+
}
|
|
4143
|
+
loopAbortController.abort();
|
|
4144
|
+
return;
|
|
4145
|
+
}
|
|
4116
4146
|
const textRepeat = loopDetector.detectTextRepetition();
|
|
4117
4147
|
if (textRepeat) {
|
|
4118
4148
|
logger.warn({ pattern: textRepeat.pattern, count: textRepeat.count }, "Text repetition loop detected \u2014 aborting");
|
|
@@ -7461,21 +7491,20 @@ function isProcessRunning(pid) {
|
|
|
7461
7491
|
function getDaemonStatus() {
|
|
7462
7492
|
const pid = readPid();
|
|
7463
7493
|
if (!pid) return { running: false, pid: null, logPath: logPath() };
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
function startBackground() {
|
|
7467
|
-
const status = getDaemonStatus();
|
|
7468
|
-
if (status.running && status.pid) {
|
|
7469
|
-
console.log(chalk5.yellow(` Mercury is already running (PID: ${status.pid})`));
|
|
7470
|
-
console.log(chalk5.dim(` Use \`mercury stop\` to stop it first.`));
|
|
7471
|
-
console.log("");
|
|
7472
|
-
process2.exit(1);
|
|
7473
|
-
}
|
|
7474
|
-
if (status.pid && !status.running) {
|
|
7494
|
+
const running = isProcessRunning(pid);
|
|
7495
|
+
if (!running) {
|
|
7475
7496
|
try {
|
|
7476
7497
|
unlinkSync4(pidPath());
|
|
7477
7498
|
} catch {
|
|
7478
7499
|
}
|
|
7500
|
+
return { running: false, pid: null, logPath: logPath() };
|
|
7501
|
+
}
|
|
7502
|
+
return { running, pid, logPath: logPath() };
|
|
7503
|
+
}
|
|
7504
|
+
function ensureDaemonRunning() {
|
|
7505
|
+
const status = getDaemonStatus();
|
|
7506
|
+
if (status.running && status.pid) {
|
|
7507
|
+
return { pid: status.pid, fresh: false };
|
|
7479
7508
|
}
|
|
7480
7509
|
const home = getMercuryHome();
|
|
7481
7510
|
if (!existsSync17(home)) {
|
|
@@ -7491,13 +7520,25 @@ function startBackground() {
|
|
|
7491
7520
|
windowsHide: isWin
|
|
7492
7521
|
});
|
|
7493
7522
|
child.unref();
|
|
7523
|
+
if (!child.pid) {
|
|
7524
|
+
throw new Error("Failed to spawn daemon process");
|
|
7525
|
+
}
|
|
7494
7526
|
writeFileSync12(pidPath(), String(child.pid));
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7527
|
+
return { pid: child.pid, fresh: true };
|
|
7528
|
+
}
|
|
7529
|
+
function startBackground() {
|
|
7530
|
+
try {
|
|
7531
|
+
const result = ensureDaemonRunning();
|
|
7532
|
+
console.log("");
|
|
7533
|
+
console.log(chalk5.green(` Mercury started in background (PID: ${result.pid})`));
|
|
7534
|
+
console.log(chalk5.dim(` Logs: ${logPath()}`));
|
|
7535
|
+
console.log(chalk5.dim(` Use \`mercury stop\` to stop.`));
|
|
7536
|
+
console.log(chalk5.dim(` Use \`mercury logs\` to view logs.`));
|
|
7537
|
+
console.log("");
|
|
7538
|
+
} catch (err) {
|
|
7539
|
+
console.log(chalk5.red(` Failed to start: ${err.message}`));
|
|
7540
|
+
process2.exit(1);
|
|
7541
|
+
}
|
|
7501
7542
|
}
|
|
7502
7543
|
function stopDaemon() {
|
|
7503
7544
|
const status = getDaemonStatus();
|
|
@@ -7548,11 +7589,6 @@ function restartDaemon() {
|
|
|
7548
7589
|
} catch {
|
|
7549
7590
|
}
|
|
7550
7591
|
console.log(chalk5.green(" Mercury stopped."));
|
|
7551
|
-
} else if (status.pid) {
|
|
7552
|
-
try {
|
|
7553
|
-
unlinkSync4(pidPath());
|
|
7554
|
-
} catch {
|
|
7555
|
-
}
|
|
7556
7592
|
}
|
|
7557
7593
|
console.log(chalk5.yellow(" Starting Mercury..."));
|
|
7558
7594
|
startBackground();
|
|
@@ -7565,40 +7601,13 @@ function showLogs() {
|
|
|
7565
7601
|
return;
|
|
7566
7602
|
}
|
|
7567
7603
|
const content = readFileSync11(logFile, "utf-8");
|
|
7568
|
-
const lines = content.split(
|
|
7604
|
+
const lines = content.split(/\r?\n/).slice(-100);
|
|
7569
7605
|
console.log(lines.join("\n"));
|
|
7570
7606
|
}
|
|
7571
7607
|
function tryAutoDaemonize() {
|
|
7572
7608
|
try {
|
|
7573
|
-
const
|
|
7574
|
-
|
|
7575
|
-
return true;
|
|
7576
|
-
}
|
|
7577
|
-
if (status.pid && !status.running) {
|
|
7578
|
-
try {
|
|
7579
|
-
unlinkSync4(pidPath());
|
|
7580
|
-
} catch {
|
|
7581
|
-
}
|
|
7582
|
-
}
|
|
7583
|
-
const home = getMercuryHome();
|
|
7584
|
-
if (!existsSync17(home)) {
|
|
7585
|
-
mkdirSync10(home, { recursive: true });
|
|
7586
|
-
}
|
|
7587
|
-
const logFile = logPath();
|
|
7588
|
-
const isWin = process2.platform === "win32";
|
|
7589
|
-
const outFd = openSync(logFile, "a");
|
|
7590
|
-
const child = spawn(process2.execPath, [process2.argv[1], "start", "--daemon"], {
|
|
7591
|
-
detached: true,
|
|
7592
|
-
stdio: ["ignore", outFd, outFd],
|
|
7593
|
-
env: { ...process2.env },
|
|
7594
|
-
windowsHide: isWin
|
|
7595
|
-
});
|
|
7596
|
-
child.unref();
|
|
7597
|
-
if (!child.pid) {
|
|
7598
|
-
return false;
|
|
7599
|
-
}
|
|
7600
|
-
writeFileSync12(pidPath(), String(child.pid));
|
|
7601
|
-
return true;
|
|
7609
|
+
const result = ensureDaemonRunning();
|
|
7610
|
+
return result.pid > 0;
|
|
7602
7611
|
} catch {
|
|
7603
7612
|
return false;
|
|
7604
7613
|
}
|
|
@@ -8768,7 +8777,7 @@ async function configure(existingConfig) {
|
|
|
8768
8777
|
}
|
|
8769
8778
|
function autoDaemonize() {
|
|
8770
8779
|
const daemon = getDaemonStatus();
|
|
8771
|
-
if (daemon.running) {
|
|
8780
|
+
if (daemon.running && daemon.pid) {
|
|
8772
8781
|
return;
|
|
8773
8782
|
}
|
|
8774
8783
|
console.log(chalk7.dim(" Setting up background mode..."));
|
|
@@ -8786,7 +8795,7 @@ function autoDaemonize() {
|
|
|
8786
8795
|
console.log(chalk7.green(" \u2713 Auto-starts on login. Auto-restarts on crash."));
|
|
8787
8796
|
console.log(chalk7.dim(" Use `mercury stop` to stop. `mercury restart` to restart."));
|
|
8788
8797
|
} else {
|
|
8789
|
-
console.log(chalk7.
|
|
8798
|
+
console.log(chalk7.yellow(" Background mode not available. Run `mercury start` to set it up."));
|
|
8790
8799
|
}
|
|
8791
8800
|
console.log("");
|
|
8792
8801
|
}
|
|
@@ -8976,6 +8985,22 @@ async function runAgent(isDaemon = false) {
|
|
|
8976
8985
|
};
|
|
8977
8986
|
process.on("SIGINT", shutdown);
|
|
8978
8987
|
process.on("SIGTERM", shutdown);
|
|
8988
|
+
if (!isDaemon && process.platform !== "win32") {
|
|
8989
|
+
process.on("SIGHUP", () => {
|
|
8990
|
+
logger.info("SIGHUP received \u2014 terminal closed. Daemonizing.");
|
|
8991
|
+
try {
|
|
8992
|
+
const result = tryAutoDaemonize();
|
|
8993
|
+
if (result) {
|
|
8994
|
+
logger.info(`Forked daemon. Foreground process exiting.`);
|
|
8995
|
+
} else {
|
|
8996
|
+
logger.warn("SIGHUP received but daemonization failed. Shutting down.");
|
|
8997
|
+
}
|
|
8998
|
+
} catch {
|
|
8999
|
+
logger.warn("SIGHUP received but daemonization failed. Shutting down.");
|
|
9000
|
+
}
|
|
9001
|
+
process.exit(0);
|
|
9002
|
+
});
|
|
9003
|
+
}
|
|
8979
9004
|
}
|
|
8980
9005
|
var program = new Command();
|
|
8981
9006
|
program.name("mercury").description("Mercury \u2014 Soul-driven AI agent with permission-hardened tools, token budgets, and multi-channel access.").version(pkgVersion).option("-v, --verbose", "Show debug logs").action(async () => {
|
|
@@ -8984,22 +9009,24 @@ program.name("mercury").description("Mercury \u2014 Soul-driven AI agent with pe
|
|
|
8984
9009
|
autoDaemonize();
|
|
8985
9010
|
return;
|
|
8986
9011
|
}
|
|
9012
|
+
autoDaemonize();
|
|
8987
9013
|
await runAgent();
|
|
8988
9014
|
});
|
|
8989
|
-
program.command("start").description("Start Mercury
|
|
9015
|
+
program.command("start").description("Start Mercury \u2014 runs as a daemon by default, use --foreground to attach to terminal").option("-v, --verbose", "Show debug logs").option("-f, --foreground", "Run in foreground (attached to terminal)").option("-d, --detached", "Run in background (daemon mode) \u2014 same as default").option("--daemon", "Internal flag for daemon child process").action(async (opts) => {
|
|
8990
9016
|
if (opts.daemon) {
|
|
8991
9017
|
await runWithWatchdog(() => runAgent(true));
|
|
8992
9018
|
return;
|
|
8993
9019
|
}
|
|
8994
|
-
if (opts.detached) {
|
|
8995
|
-
startBackground();
|
|
8996
|
-
return;
|
|
8997
|
-
}
|
|
8998
9020
|
if (!isSetupComplete()) {
|
|
8999
9021
|
await configure();
|
|
9022
|
+
autoDaemonize();
|
|
9000
9023
|
return;
|
|
9001
9024
|
}
|
|
9002
|
-
|
|
9025
|
+
if (opts.foreground) {
|
|
9026
|
+
await runAgent();
|
|
9027
|
+
return;
|
|
9028
|
+
}
|
|
9029
|
+
startBackground();
|
|
9003
9030
|
});
|
|
9004
9031
|
program.command("stop").description("Stop a background Mercury process").action(() => {
|
|
9005
9032
|
stopDaemon();
|
|
@@ -9007,9 +9034,11 @@ program.command("stop").description("Stop a background Mercury process").action(
|
|
|
9007
9034
|
program.command("restart").description("Restart a background Mercury process").action(() => {
|
|
9008
9035
|
restartDaemon();
|
|
9009
9036
|
});
|
|
9010
|
-
program.command("up").description("
|
|
9037
|
+
program.command("up").description("Start Mercury as a persistent daemon (same as `mercury start`)").action(async () => {
|
|
9011
9038
|
if (!isSetupComplete()) {
|
|
9012
9039
|
await configure();
|
|
9040
|
+
autoDaemonize();
|
|
9041
|
+
return;
|
|
9013
9042
|
}
|
|
9014
9043
|
const daemon = getDaemonStatus();
|
|
9015
9044
|
if (daemon.running && daemon.pid) {
|
|
@@ -9024,7 +9053,6 @@ program.command("up").description("Ensure Mercury is running persistently \u2014
|
|
|
9024
9053
|
console.log(chalk7.cyan(" Installing Mercury as a system service..."));
|
|
9025
9054
|
installService();
|
|
9026
9055
|
}
|
|
9027
|
-
console.log(chalk7.cyan(" Starting Mercury in background..."));
|
|
9028
9056
|
startBackground();
|
|
9029
9057
|
});
|
|
9030
9058
|
program.command("logs").description("Show recent daemon logs").action(() => {
|