agent-yes 1.140.1 → 1.141.0
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/{SUPPORTED_CLIS-Cjay1Iru.js → SUPPORTED_CLIS-CnBoRIDN.js} +2 -2
- package/dist/SUPPORTED_CLIS-bW-3DoHX.js +8 -0
- package/dist/cli.js +3 -3
- package/dist/index.js +2 -2
- package/dist/{schedule-DEcxVffO.js → schedule-PVnlPldE.js} +4 -4
- package/dist/{serve-BAZlVy52.js → serve-D8UEMOas.js} +5 -5
- package/dist/{setup-C9csSnJG.js → setup-BPAdHGDG.js} +2 -2
- package/dist/subcommands-3BaA-AmB.js +7 -0
- package/dist/{subcommands-D4BFGRle.js → subcommands-dbx7EoK-.js} +85 -20
- package/dist/{ts-CKCsYKjz.js → ts-CMMwNq--.js} +2 -2
- package/dist/{versionChecker-CZ6UM9LY.js → versionChecker-DMpCuaZe.js} +2 -2
- package/lab/ui/console-logic.js +1 -1
- package/lab/ui/index.html +17 -4
- package/package.json +1 -1
- package/ts/lsWatch.ts +1 -1
- package/ts/needsInput.spec.ts +20 -1
- package/ts/needsInput.ts +14 -0
- package/ts/subcommands.spec.ts +119 -0
- package/ts/subcommands.ts +0 -0
- package/dist/SUPPORTED_CLIS-dlEtFEAn.js +0 -8
- package/dist/subcommands-CvO1YYns.js +0 -7
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { t as CLIS_CONFIG } from "./ts-CMMwNq--.js";
|
|
2
2
|
|
|
3
3
|
//#region ts/SUPPORTED_CLIS.ts
|
|
4
4
|
const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
|
|
5
5
|
|
|
6
6
|
//#endregion
|
|
7
7
|
export { SUPPORTED_CLIS as t };
|
|
8
|
-
//# sourceMappingURL=SUPPORTED_CLIS-
|
|
8
|
+
//# sourceMappingURL=SUPPORTED_CLIS-CnBoRIDN.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "./ts-CMMwNq--.js";
|
|
2
|
+
import "./logger-CDIsZ-Pp.js";
|
|
3
|
+
import "./versionChecker-DMpCuaZe.js";
|
|
4
|
+
import "./pidStore-fqXqTKkh.js";
|
|
5
|
+
import "./globalPidIndex-DlmmJlO8.js";
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CnBoRIDN.js";
|
|
7
|
+
|
|
8
|
+
export { SUPPORTED_CLIS };
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { t as invokedCliName } from "./invokedCli-zdFbz1ST.js";
|
|
3
3
|
import { n as logger } from "./logger-CDIsZ-Pp.js";
|
|
4
|
-
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-
|
|
4
|
+
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-DMpCuaZe.js";
|
|
5
5
|
import { argv } from "process";
|
|
6
6
|
import { execFileSync, spawn } from "child_process";
|
|
7
7
|
import ms from "ms";
|
|
@@ -480,7 +480,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
|
|
|
480
480
|
const rawArg = process.argv[2];
|
|
481
481
|
const managerCommands = !invokedCliName(process.argv);
|
|
482
482
|
const isHelpFlag = rawArg === "-h" || rawArg === "--help";
|
|
483
|
-
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-
|
|
483
|
+
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-3BaA-AmB.js");
|
|
484
484
|
if (isHelpFlag && process.argv.length === 3) {
|
|
485
485
|
cmdHelp(managerCommands);
|
|
486
486
|
process.exit(0);
|
|
@@ -513,7 +513,7 @@ if (config.useRust) {
|
|
|
513
513
|
}
|
|
514
514
|
}
|
|
515
515
|
if (rustBinary) {
|
|
516
|
-
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-
|
|
516
|
+
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-bW-3DoHX.js");
|
|
517
517
|
const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
|
|
518
518
|
if (config.verbose) {
|
|
519
519
|
console.log(`[rust] Using binary: ${rustBinary}`);
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-CMMwNq--.js";
|
|
2
2
|
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-DMpCuaZe.js";
|
|
4
4
|
import "./pidStore-fqXqTKkh.js";
|
|
5
5
|
import "./globalPidIndex-DlmmJlO8.js";
|
|
6
6
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-CMMwNq--.js";
|
|
2
2
|
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-DMpCuaZe.js";
|
|
4
4
|
import "./pidStore-fqXqTKkh.js";
|
|
5
5
|
import "./globalPidIndex-DlmmJlO8.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CnBoRIDN.js";
|
|
7
7
|
import { n as resolveSpawnCwd } from "./workspaceConfig-BCOqRBEW.js";
|
|
8
8
|
import { createHash } from "node:crypto";
|
|
9
9
|
|
|
@@ -141,4 +141,4 @@ async function cmdSchedule(rest) {
|
|
|
141
141
|
|
|
142
142
|
//#endregion
|
|
143
143
|
export { cmdSchedule };
|
|
144
|
-
//# sourceMappingURL=schedule-
|
|
144
|
+
//# sourceMappingURL=schedule-PVnlPldE.js.map
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-CMMwNq--.js";
|
|
2
2
|
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
3
|
+
import { r as getInstalledPackage } from "./versionChecker-DMpCuaZe.js";
|
|
4
4
|
import "./pidStore-fqXqTKkh.js";
|
|
5
5
|
import { a as updateGlobalPidStatus } from "./globalPidIndex-DlmmJlO8.js";
|
|
6
6
|
import { t as pgidForWrapper } from "./reaper-C-eWAxIj.js";
|
|
7
7
|
import "./configShared-C1C04bbq.js";
|
|
8
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
8
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CnBoRIDN.js";
|
|
9
9
|
import "./remotes-PKKjfTI1.js";
|
|
10
|
-
import {
|
|
10
|
+
import { C as writeToIpc, c as extractTaskCounts, g as renderRawLog, h as readNotes, i as controlCodeFromName, o as deriveLiveStatus, p as listRecords, v as resolveOne, x as snapshotStatus } from "./subcommands-dbx7EoK-.js";
|
|
11
11
|
import yargs from "yargs";
|
|
12
12
|
import { mkdir, open, readFile, stat, writeFile } from "fs/promises";
|
|
13
13
|
import { homedir, hostname, userInfo } from "os";
|
|
@@ -1175,4 +1175,4 @@ Options:
|
|
|
1175
1175
|
|
|
1176
1176
|
//#endregion
|
|
1177
1177
|
export { cmdServe };
|
|
1178
|
-
//# sourceMappingURL=serve-
|
|
1178
|
+
//# sourceMappingURL=serve-D8UEMOas.js.map
|
|
@@ -32,7 +32,7 @@ async function cmdSetup(rest) {
|
|
|
32
32
|
if (!existsSync(abs)) process.stderr.write(` note: that directory doesn't exist yet — create it, or agents spawned there will fail\n`);
|
|
33
33
|
if (noShare) return 0;
|
|
34
34
|
process.stdout.write(`\nsharing this machine to agent-yes.com…\n`);
|
|
35
|
-
const { cmdServe } = await import("./serve-
|
|
35
|
+
const { cmdServe } = await import("./serve-D8UEMOas.js");
|
|
36
36
|
return cmdServe([
|
|
37
37
|
"install",
|
|
38
38
|
"--share",
|
|
@@ -42,4 +42,4 @@ async function cmdSetup(rest) {
|
|
|
42
42
|
|
|
43
43
|
//#endregion
|
|
44
44
|
export { cmdSetup };
|
|
45
|
-
//# sourceMappingURL=setup-
|
|
45
|
+
//# sourceMappingURL=setup-BPAdHGDG.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import "./logger-CDIsZ-Pp.js";
|
|
2
|
+
import "./globalPidIndex-DlmmJlO8.js";
|
|
3
|
+
import "./configShared-C1C04bbq.js";
|
|
4
|
+
import "./remotes-PKKjfTI1.js";
|
|
5
|
+
import { C as writeToIpc, S as stopTipForCli, _ as renderRawLogLines, a as cursorAbs, b as runSubcommand, c as extractTaskCounts, d as isPidAlive, f as isSubcommand, g as renderRawLog, h as readNotes, i as controlCodeFromName, l as finalizedLines, m as matchKeyword, n as READ_PAGE_DEFAULT, o as deriveLiveStatus, p as listRecords, r as cmdHelp, s as extractNeedsInput, t as GRACEFUL_EXIT_COMMANDS, u as isAgentStuck, v as resolveOne, x as snapshotStatus, y as resolveReadWindow } from "./subcommands-dbx7EoK-.js";
|
|
6
|
+
|
|
7
|
+
export { cmdHelp, isSubcommand, runSubcommand };
|
|
@@ -182,6 +182,19 @@ function classifyNeedsInput(lines, cfg) {
|
|
|
182
182
|
const end = Math.min(lines.length, last + 6);
|
|
183
183
|
return { question: lines.slice(start, end).map((l) => l.trim()).filter((l) => l && !isChromeLine(l)).join(" • ").slice(0, 400) };
|
|
184
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* True when the rendered screen still shows a "busy" marker (config `working`,
|
|
187
|
+
* e.g. claude's `esc to interrupt`). Paired with a long-quiet log this is the
|
|
188
|
+
* `stuck` signal: a live spinner writes to the log every frame, so a busy marker
|
|
189
|
+
* on screen WITHOUT recent output means the agent wedged mid-stream (a silent
|
|
190
|
+
* API stream stall) rather than finishing. Pure + synchronous like the rest of
|
|
191
|
+
* this module so it's trivially unit-testable.
|
|
192
|
+
*/
|
|
193
|
+
function isWorkingScreen(lines, working) {
|
|
194
|
+
if (!working?.length) return false;
|
|
195
|
+
const text = lines.join("\n");
|
|
196
|
+
return working.some((re) => reTest(re, text));
|
|
197
|
+
}
|
|
185
198
|
|
|
186
199
|
//#endregion
|
|
187
200
|
//#region ts/lsWatch.ts
|
|
@@ -351,6 +364,7 @@ async function compactNotes() {
|
|
|
351
364
|
await writeFile(notesPath(), lines ? lines + "\n" : "");
|
|
352
365
|
}
|
|
353
366
|
const READ_WINDOW_MS = 6e4;
|
|
367
|
+
const IPC_WRITE_TIMEOUT_MS = 1e4;
|
|
354
368
|
const READS_KEY_SEP = "\0";
|
|
355
369
|
function readsPath() {
|
|
356
370
|
const dir = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
|
|
@@ -497,6 +511,10 @@ const SUBCOMMANDS = new Set([
|
|
|
497
511
|
]);
|
|
498
512
|
const MANAGER_SUBCOMMANDS = new Set(["setup"]);
|
|
499
513
|
const IDLE_THRESHOLD_MS = 60 * 1e3;
|
|
514
|
+
const STUCK_THRESHOLD_MS = (() => {
|
|
515
|
+
const n = Number(process.env.AGENT_YES_STUCK_MS);
|
|
516
|
+
return Number.isFinite(n) && n > 0 ? n : 300 * 1e3;
|
|
517
|
+
})();
|
|
500
518
|
/**
|
|
501
519
|
* Whether `name` is a subcommand. `managerCommands` (default true, for the
|
|
502
520
|
* generic `ay`/`agent-yes` entry) additionally admits manager-only commands
|
|
@@ -533,15 +551,15 @@ async function runSubcommand(argv) {
|
|
|
533
551
|
case "restart": return await cmdRestart(rest);
|
|
534
552
|
case "note": return await cmdNote(rest);
|
|
535
553
|
case "serve": {
|
|
536
|
-
const { cmdServe } = await import("./serve-
|
|
554
|
+
const { cmdServe } = await import("./serve-D8UEMOas.js");
|
|
537
555
|
return cmdServe(rest);
|
|
538
556
|
}
|
|
539
557
|
case "setup": {
|
|
540
|
-
const { cmdSetup } = await import("./setup-
|
|
558
|
+
const { cmdSetup } = await import("./setup-BPAdHGDG.js");
|
|
541
559
|
return cmdSetup(rest);
|
|
542
560
|
}
|
|
543
561
|
case "schedule": {
|
|
544
|
-
const { cmdSchedule } = await import("./schedule-
|
|
562
|
+
const { cmdSchedule } = await import("./schedule-PVnlPldE.js");
|
|
545
563
|
return cmdSchedule(rest);
|
|
546
564
|
}
|
|
547
565
|
case "remote": {
|
|
@@ -976,6 +994,10 @@ async function deriveLiveState(r) {
|
|
|
976
994
|
state: "needs_input",
|
|
977
995
|
question: ni.question
|
|
978
996
|
};
|
|
997
|
+
if (base === "idle" && await isAgentStuck(r)) return {
|
|
998
|
+
state: "stuck",
|
|
999
|
+
question: null
|
|
1000
|
+
};
|
|
979
1001
|
}
|
|
980
1002
|
return {
|
|
981
1003
|
state: base,
|
|
@@ -999,7 +1021,7 @@ async function cmdLs(rest) {
|
|
|
999
1021
|
alias: "w",
|
|
1000
1022
|
type: "boolean",
|
|
1001
1023
|
default: false,
|
|
1002
|
-
description: "Stream agent state transitions (needs_input | idle | active | stopped) as NDJSON across all matched agents — one event stream for a whole fan-out, instead of N per-pid `ay status --watch`es. Runs until Ctrl-C."
|
|
1024
|
+
description: "Stream agent state transitions (needs_input | idle | active | stuck | stopped) as NDJSON across all matched agents — one event stream for a whole fan-out, instead of N per-pid `ay status --watch`es. Runs until Ctrl-C."
|
|
1003
1025
|
}).option("interval", {
|
|
1004
1026
|
type: "number",
|
|
1005
1027
|
default: 2,
|
|
@@ -1645,9 +1667,12 @@ function cliDefaults() {
|
|
|
1645
1667
|
* and renders it through xterm, then runs the CLI's `needsInput`/`working`
|
|
1646
1668
|
* patterns. Returns null when no menu is detected (or the CLI defines none).
|
|
1647
1669
|
*/
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1670
|
+
/**
|
|
1671
|
+
* Render the last `n` lines of a raw PTY log (reads only the final 32KB). Returns
|
|
1672
|
+
* null on any read/render error or an empty log. Shared by the needs_input and
|
|
1673
|
+
* stuck classifiers so they don't each re-implement the tail read.
|
|
1674
|
+
*/
|
|
1675
|
+
async function renderLogTailLines(logPath, n = 40) {
|
|
1651
1676
|
const TAIL_BYTES = 32 * 1024;
|
|
1652
1677
|
let buf;
|
|
1653
1678
|
try {
|
|
@@ -1670,17 +1695,41 @@ async function extractNeedsInput(logPath, cli) {
|
|
|
1670
1695
|
return null;
|
|
1671
1696
|
}
|
|
1672
1697
|
try {
|
|
1673
|
-
return
|
|
1698
|
+
return (await renderRawLog(buf, {
|
|
1674
1699
|
mode: "tail",
|
|
1675
|
-
n
|
|
1676
|
-
})).split("\n")
|
|
1677
|
-
needsInput: cfg.needsInput,
|
|
1678
|
-
working: cfg.working
|
|
1679
|
-
});
|
|
1700
|
+
n
|
|
1701
|
+
})).split("\n");
|
|
1680
1702
|
} catch {
|
|
1681
1703
|
return null;
|
|
1682
1704
|
}
|
|
1683
1705
|
}
|
|
1706
|
+
async function extractNeedsInput(logPath, cli) {
|
|
1707
|
+
const cfg = (await cliDefaults())[cli];
|
|
1708
|
+
if (!cfg?.needsInput?.length) return null;
|
|
1709
|
+
const lines = await renderLogTailLines(logPath, 40);
|
|
1710
|
+
if (!lines) return null;
|
|
1711
|
+
return classifyNeedsInput(lines, {
|
|
1712
|
+
needsInput: cfg.needsInput,
|
|
1713
|
+
working: cfg.working
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* Whether an alive agent is wedged: its log has been silent for at least
|
|
1718
|
+
* STUCK_THRESHOLD_MS yet its screen still shows a `working` busy marker (a live
|
|
1719
|
+
* spinner keeps writing, so busy + long-silent = a mid-stream stall). Pass the
|
|
1720
|
+
* already-stat'd log mtime to skip a redundant stat. Returns false when the CLI
|
|
1721
|
+
* has no `working` markers configured (nothing to key off).
|
|
1722
|
+
*/
|
|
1723
|
+
async function isAgentStuck(record, logMtimeMs) {
|
|
1724
|
+
if (!record.log_file) return false;
|
|
1725
|
+
const cfg = (await cliDefaults())[record.cli];
|
|
1726
|
+
if (!cfg?.working?.length) return false;
|
|
1727
|
+
const mtime = logMtimeMs ?? await stat(record.log_file).then((s) => s.mtimeMs).catch(() => null);
|
|
1728
|
+
if (mtime === null || Date.now() - mtime < STUCK_THRESHOLD_MS) return false;
|
|
1729
|
+
const lines = await renderLogTailLines(record.log_file, 40);
|
|
1730
|
+
if (!lines) return false;
|
|
1731
|
+
return isWorkingScreen(lines, cfg.working);
|
|
1732
|
+
}
|
|
1684
1733
|
function extractActivityFromLines(lines) {
|
|
1685
1734
|
const isChrome = (l) => {
|
|
1686
1735
|
const s = l.trim();
|
|
@@ -1847,10 +1896,26 @@ async function writeToIpc(ipcPath, payload) {
|
|
|
1847
1896
|
});
|
|
1848
1897
|
});
|
|
1849
1898
|
} else {
|
|
1850
|
-
const { openSync,
|
|
1899
|
+
const { openSync, writeSync, closeSync, constants } = await import("fs");
|
|
1851
1900
|
const fd = openSync(ipcPath, constants.O_WRONLY | constants.O_NONBLOCK);
|
|
1852
1901
|
try {
|
|
1853
|
-
|
|
1902
|
+
const buf = Buffer.from(payload, "utf8");
|
|
1903
|
+
const deadline = Date.now() + IPC_WRITE_TIMEOUT_MS;
|
|
1904
|
+
let off = 0;
|
|
1905
|
+
while (off < buf.length) {
|
|
1906
|
+
let wrote = 0;
|
|
1907
|
+
try {
|
|
1908
|
+
wrote = writeSync(fd, buf, off, buf.length - off);
|
|
1909
|
+
} catch (e) {
|
|
1910
|
+
const code = e?.code;
|
|
1911
|
+
if (code !== "EAGAIN" && code !== "EWOULDBLOCK") throw e;
|
|
1912
|
+
}
|
|
1913
|
+
off += wrote;
|
|
1914
|
+
if (off < buf.length) {
|
|
1915
|
+
if (Date.now() >= deadline) throw new Error(`writeToIpc: ${ipcPath} reader not draining — wrote ${off}/${buf.length} bytes in ${IPC_WRITE_TIMEOUT_MS}ms`);
|
|
1916
|
+
await new Promise((r) => setTimeout(r, wrote > 0 ? 1 : 15));
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1854
1919
|
} finally {
|
|
1855
1920
|
closeSync(fd);
|
|
1856
1921
|
}
|
|
@@ -2172,7 +2237,7 @@ async function snapshotStatus(record) {
|
|
|
2172
2237
|
if (ni) {
|
|
2173
2238
|
state = "needs_input";
|
|
2174
2239
|
question = ni.question;
|
|
2175
|
-
}
|
|
2240
|
+
} else if (state === "idle" && await isAgentStuck(record, logMtimeMs)) state = "stuck";
|
|
2176
2241
|
}
|
|
2177
2242
|
const note = (await readNotes()).get(record.pid) ?? null;
|
|
2178
2243
|
return {
|
|
@@ -2252,7 +2317,7 @@ async function cmdStatus(rest) {
|
|
|
2252
2317
|
const startedAt = Date.now();
|
|
2253
2318
|
for (;;) {
|
|
2254
2319
|
const snap = await snapshotStatus(record);
|
|
2255
|
-
if (snap.state === "needs_input" || snap.state === "idle" || snap.state === "stopped") {
|
|
2320
|
+
if (snap.state === "needs_input" || snap.state === "idle" || snap.state === "stuck" || snap.state === "stopped") {
|
|
2256
2321
|
emit(snap);
|
|
2257
2322
|
return 0;
|
|
2258
2323
|
}
|
|
@@ -2267,7 +2332,7 @@ async function cmdStatus(rest) {
|
|
|
2267
2332
|
const startedAt = Date.now();
|
|
2268
2333
|
for (;;) {
|
|
2269
2334
|
const snap = await snapshotStatus(record);
|
|
2270
|
-
if (snap.state === "idle") {
|
|
2335
|
+
if (snap.state === "idle" || snap.state === "stuck") {
|
|
2271
2336
|
emit(snap);
|
|
2272
2337
|
return 0;
|
|
2273
2338
|
}
|
|
@@ -2439,5 +2504,5 @@ async function cmdResultSet(rest) {
|
|
|
2439
2504
|
}
|
|
2440
2505
|
|
|
2441
2506
|
//#endregion
|
|
2442
|
-
export { writeToIpc as S,
|
|
2443
|
-
//# sourceMappingURL=subcommands-
|
|
2507
|
+
export { writeToIpc as C, stopTipForCli as S, renderRawLogLines as _, cursorAbs as a, runSubcommand as b, extractTaskCounts as c, isPidAlive as d, isSubcommand as f, renderRawLog as g, readNotes as h, controlCodeFromName as i, finalizedLines as l, matchKeyword as m, READ_PAGE_DEFAULT as n, deriveLiveStatus as o, listRecords as p, cmdHelp as r, extractNeedsInput as s, GRACEFUL_EXIT_COMMANDS as t, isAgentStuck as u, resolveOne as v, snapshotStatus as x, resolveReadWindow as y };
|
|
2508
|
+
//# sourceMappingURL=subcommands-dbx7EoK-.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as logger, t as addTransport } from "./logger-CDIsZ-Pp.js";
|
|
2
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
2
|
+
import { r as getInstalledPackage } from "./versionChecker-DMpCuaZe.js";
|
|
3
3
|
import { t as agentYesHome } from "./agentYesHome-_eJa3DaX.js";
|
|
4
4
|
import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-V4qvXgAw.js";
|
|
5
5
|
import { t as PidStore } from "./pidStore-fqXqTKkh.js";
|
|
@@ -1788,4 +1788,4 @@ function sleep(ms) {
|
|
|
1788
1788
|
|
|
1789
1789
|
//#endregion
|
|
1790
1790
|
export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
|
|
1791
|
-
//# sourceMappingURL=ts-
|
|
1791
|
+
//# sourceMappingURL=ts-CMMwNq--.js.map
|
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
|
|
|
7
7
|
|
|
8
8
|
//#region package.json
|
|
9
9
|
var name = "agent-yes";
|
|
10
|
-
var version = "1.
|
|
10
|
+
var version = "1.141.0";
|
|
11
11
|
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region ts/versionChecker.ts
|
|
@@ -215,4 +215,4 @@ async function displayVersion() {
|
|
|
215
215
|
|
|
216
216
|
//#endregion
|
|
217
217
|
export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
|
|
218
|
-
//# sourceMappingURL=versionChecker-
|
|
218
|
+
//# sourceMappingURL=versionChecker-DMpCuaZe.js.map
|
package/lab/ui/console-logic.js
CHANGED
|
@@ -440,7 +440,7 @@ export function selSegments(sel, myLast, vy, myRows, peerCols, myCols) {
|
|
|
440
440
|
rB = myLast - sel.fb,
|
|
441
441
|
cA = sel.ca,
|
|
442
442
|
cB = sel.cb;
|
|
443
|
-
if (rA > rB) ([rA, rB] = [rB, rA]), ([cA, cB] = [cB, cA]); // rA = top row
|
|
443
|
+
if (rA > rB) (([rA, rB] = [rB, rA]), ([cA, cB] = [cB, cA])); // rA = top row
|
|
444
444
|
const segs = [];
|
|
445
445
|
const from = Math.max(rA, vy),
|
|
446
446
|
to = Math.min(rB, vy + myRows - 1);
|
package/lab/ui/index.html
CHANGED
|
@@ -1489,7 +1489,10 @@
|
|
|
1489
1489
|
/>
|
|
1490
1490
|
<div class="omni-results" id="omni-results"></div>
|
|
1491
1491
|
<div class="omni-foot">
|
|
1492
|
-
<span
|
|
1492
|
+
<span
|
|
1493
|
+
><b>↑↓</b> move <b>⏎</b> open <b>⌘⏎</b> spawn here
|
|
1494
|
+
<b>esc</b> close</span
|
|
1495
|
+
>
|
|
1493
1496
|
</div>
|
|
1494
1497
|
</div>
|
|
1495
1498
|
</div>
|
|
@@ -2699,9 +2702,17 @@
|
|
|
2699
2702
|
const sel = parseSel(p.sel);
|
|
2700
2703
|
if (!sel) continue;
|
|
2701
2704
|
const hue = hashHue(p.viewer);
|
|
2702
|
-
const tag =
|
|
2705
|
+
const tag =
|
|
2706
|
+
esc(String(p.viewer).slice(0, 4)) + " " + (p.cols || "?") + "×" + (p.rows || "?");
|
|
2703
2707
|
let tagged = false;
|
|
2704
|
-
for (const seg of selSegments(
|
|
2708
|
+
for (const seg of selSegments(
|
|
2709
|
+
sel,
|
|
2710
|
+
myLast,
|
|
2711
|
+
vy,
|
|
2712
|
+
term.rows,
|
|
2713
|
+
p.cols || term.cols,
|
|
2714
|
+
term.cols,
|
|
2715
|
+
)) {
|
|
2705
2716
|
const vr = seg.row - vy; // our buffer row → our viewport row
|
|
2706
2717
|
if (vr < 0 || vr > term.rows - 1) continue; // safety (selSegments already clips)
|
|
2707
2718
|
const left = ox + seg.a * cw;
|
|
@@ -3740,7 +3751,9 @@
|
|
|
3740
3751
|
const seq = ++omniTailSeq;
|
|
3741
3752
|
const have = new Set(omniRows.filter((r) => r.kind === "agent").map((r) => r.entry._key));
|
|
3742
3753
|
const ql = q.toLowerCase();
|
|
3743
|
-
const cands = entries
|
|
3754
|
+
const cands = entries
|
|
3755
|
+
.filter((e) => !have.has(e._key) && e.status !== "exited")
|
|
3756
|
+
.slice(0, 20);
|
|
3744
3757
|
await Promise.all(
|
|
3745
3758
|
cands.map(async (e) => {
|
|
3746
3759
|
const src = srcFor(e);
|
package/package.json
CHANGED
package/ts/lsWatch.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* `needsInput.ts`.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
export type LiveState = "active" | "idle" | "stopped" | "needs_input";
|
|
13
|
+
export type LiveState = "active" | "idle" | "stopped" | "needs_input" | "stuck";
|
|
14
14
|
|
|
15
15
|
/** The observable state of one agent at a single tick. */
|
|
16
16
|
export interface LsAgentState {
|
package/ts/needsInput.spec.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test } from "vitest";
|
|
2
|
-
import { classifyNeedsInput } from "./needsInput.ts";
|
|
2
|
+
import { classifyNeedsInput, isWorkingScreen } from "./needsInput.ts";
|
|
3
3
|
import { loadSharedCliDefaults } from "./configShared.ts";
|
|
4
4
|
|
|
5
5
|
// Use the REAL shipped claude/codex patterns so the test guards the actual config.
|
|
@@ -11,6 +11,25 @@ test("claude config actually ships a needsInput pattern", () => {
|
|
|
11
11
|
expect(claude.needsInput?.length).toBeGreaterThan(0);
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
+
test("claude config ships a working busy marker (the stuck detector keys off it)", () => {
|
|
15
|
+
expect(claude.working?.length).toBeGreaterThan(0);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("isWorkingScreen: true when the shipped claude busy marker is on screen", () => {
|
|
19
|
+
const screen = ["⏺ Running the test suite…", "", "esc to interrupt · ← for agents"];
|
|
20
|
+
expect(isWorkingScreen(screen, claude.working)).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("isWorkingScreen: false at a finished/idle prompt (no busy marker)", () => {
|
|
24
|
+
const screen = ["⏺ Done — all tests pass.", "", "❯", "", "? for shortcuts"];
|
|
25
|
+
expect(isWorkingScreen(screen, claude.working)).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("isWorkingScreen: false when no working patterns are configured", () => {
|
|
29
|
+
expect(isWorkingScreen(["esc to interrupt"], undefined)).toBe(false);
|
|
30
|
+
expect(isWorkingScreen(["esc to interrupt"], [])).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
14
33
|
test("detects a claude AskUserQuestion selection menu", () => {
|
|
15
34
|
const screen = [
|
|
16
35
|
"Which auth method should we use?",
|
package/ts/needsInput.ts
CHANGED
|
@@ -66,3 +66,17 @@ export function classifyNeedsInput(
|
|
|
66
66
|
.filter((l) => l && !isChromeLine(l));
|
|
67
67
|
return { question: block.join(" • ").slice(0, 400) };
|
|
68
68
|
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* True when the rendered screen still shows a "busy" marker (config `working`,
|
|
72
|
+
* e.g. claude's `esc to interrupt`). Paired with a long-quiet log this is the
|
|
73
|
+
* `stuck` signal: a live spinner writes to the log every frame, so a busy marker
|
|
74
|
+
* on screen WITHOUT recent output means the agent wedged mid-stream (a silent
|
|
75
|
+
* API stream stall) rather than finishing. Pure + synchronous like the rest of
|
|
76
|
+
* this module so it's trivially unit-testable.
|
|
77
|
+
*/
|
|
78
|
+
export function isWorkingScreen(lines: string[], working?: RegExp[]): boolean {
|
|
79
|
+
if (!working?.length) return false;
|
|
80
|
+
const text = lines.join("\n");
|
|
81
|
+
return working.some((re) => reTest(re, text));
|
|
82
|
+
}
|
package/ts/subcommands.spec.ts
CHANGED
|
@@ -719,6 +719,54 @@ describe("subcommands.cmdSend writes bytes to FIFO", () => {
|
|
|
719
719
|
});
|
|
720
720
|
});
|
|
721
721
|
|
|
722
|
+
describe("subcommands.writeToIpc reliable delivery", () => {
|
|
723
|
+
const itUnix = process.platform === "linux" || process.platform === "darwin";
|
|
724
|
+
|
|
725
|
+
it.skipIf(!itUnix)(
|
|
726
|
+
"delivers a payload larger than the FIFO buffer to a slow reader",
|
|
727
|
+
async () => {
|
|
728
|
+
const { writeToIpc } = await loadModule();
|
|
729
|
+
const { spawnSync } = await import("child_process");
|
|
730
|
+
const fs = await import("fs");
|
|
731
|
+
const tmp = await mkdtemp(path.join(tmpdir(), "ay-ipc-"));
|
|
732
|
+
try {
|
|
733
|
+
const fifo = path.join(tmp, "big.fifo");
|
|
734
|
+
if (spawnSync("mkfifo", [fifo]).status !== 0) return;
|
|
735
|
+
// Reader present (so open() doesn't ENXIO) but draining slowly, in small
|
|
736
|
+
// chunks on a timer — this backs the ~8KB kernel buffer up and makes the
|
|
737
|
+
// old single non-blocking writeFileSync EAGAIN/truncate.
|
|
738
|
+
const rfd = fs.openSync(fifo, fs.constants.O_RDONLY | fs.constants.O_NONBLOCK);
|
|
739
|
+
const chunks: Buffer[] = [];
|
|
740
|
+
const drain = setInterval(() => {
|
|
741
|
+
const b = Buffer.alloc(1000);
|
|
742
|
+
try {
|
|
743
|
+
const n = fs.readSync(rfd, b, 0, b.length, null);
|
|
744
|
+
if (n > 0) chunks.push(Buffer.from(b.subarray(0, n)));
|
|
745
|
+
} catch {
|
|
746
|
+
/* EAGAIN when momentarily empty */
|
|
747
|
+
}
|
|
748
|
+
}, 5);
|
|
749
|
+
try {
|
|
750
|
+
// 50KB >> the FIFO buffer: forces many partial writes + EAGAIN retries.
|
|
751
|
+
const payload = "abcdefghij".repeat(5000);
|
|
752
|
+
await writeToIpc(fifo, payload);
|
|
753
|
+
// Let the drainer flush whatever is still buffered.
|
|
754
|
+
const deadline = Date.now() + 3000;
|
|
755
|
+
while (Buffer.concat(chunks).length < payload.length && Date.now() < deadline) {
|
|
756
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
757
|
+
}
|
|
758
|
+
expect(Buffer.concat(chunks).toString("utf8")).toBe(payload);
|
|
759
|
+
} finally {
|
|
760
|
+
clearInterval(drain);
|
|
761
|
+
fs.closeSync(rfd);
|
|
762
|
+
}
|
|
763
|
+
} finally {
|
|
764
|
+
await rm(tmp, { recursive: true, force: true }).catch(() => null);
|
|
765
|
+
}
|
|
766
|
+
},
|
|
767
|
+
);
|
|
768
|
+
});
|
|
769
|
+
|
|
722
770
|
describe("subcommands.cmdSend safety guards", () => {
|
|
723
771
|
it("maps AGENT_YES_PID→wrapper_pid and blocks an agent from sending to itself", async () => {
|
|
724
772
|
const { runSubcommand } = await loadModule();
|
|
@@ -1523,3 +1571,74 @@ describe("subcommands.deriveLiveStatus", () => {
|
|
|
1523
1571
|
}
|
|
1524
1572
|
});
|
|
1525
1573
|
});
|
|
1574
|
+
|
|
1575
|
+
describe("subcommands.isAgentStuck / stuck state", () => {
|
|
1576
|
+
const rec = (over: any) => ({
|
|
1577
|
+
pid: process.pid,
|
|
1578
|
+
cli: "claude",
|
|
1579
|
+
prompt: null,
|
|
1580
|
+
cwd: "/tmp",
|
|
1581
|
+
log_file: null,
|
|
1582
|
+
fifo_file: null,
|
|
1583
|
+
status: "active",
|
|
1584
|
+
exit_code: null,
|
|
1585
|
+
exit_reason: null,
|
|
1586
|
+
started_at: 0,
|
|
1587
|
+
...over,
|
|
1588
|
+
});
|
|
1589
|
+
// A log whose rendered tail shows claude's shipped `working` busy marker.
|
|
1590
|
+
const BUSY = "⏺ Cogitating…\r\nesc to interrupt · ← for agents\r\n";
|
|
1591
|
+
const tenMinAgo = () => new Date(Date.now() - 10 * 60 * 1000);
|
|
1592
|
+
|
|
1593
|
+
it("isAgentStuck: true when a busy marker is on screen and the log is long-silent", async () => {
|
|
1594
|
+
const dir = await mkdtemp(path.join(tmpdir(), "ay-stuck-"));
|
|
1595
|
+
try {
|
|
1596
|
+
const log = path.join(dir, "a.log");
|
|
1597
|
+
await writeFile(log, BUSY);
|
|
1598
|
+
await utimes(log, tenMinAgo(), tenMinAgo());
|
|
1599
|
+
const mod = await loadModule();
|
|
1600
|
+
expect(await mod.isAgentStuck(rec({ log_file: log }))).toBe(true);
|
|
1601
|
+
} finally {
|
|
1602
|
+
await rm(dir, { recursive: true, force: true }).catch(() => null);
|
|
1603
|
+
}
|
|
1604
|
+
});
|
|
1605
|
+
|
|
1606
|
+
it("isAgentStuck: false when the busy log was written recently (still working)", async () => {
|
|
1607
|
+
const dir = await mkdtemp(path.join(tmpdir(), "ay-stuck-"));
|
|
1608
|
+
try {
|
|
1609
|
+
const log = path.join(dir, "a.log");
|
|
1610
|
+
await writeFile(log, BUSY); // fresh mtime — under the stuck threshold
|
|
1611
|
+
const mod = await loadModule();
|
|
1612
|
+
expect(await mod.isAgentStuck(rec({ log_file: log }))).toBe(false);
|
|
1613
|
+
} finally {
|
|
1614
|
+
await rm(dir, { recursive: true, force: true }).catch(() => null);
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
it("isAgentStuck: false when long-silent but no busy marker on screen (genuinely idle)", async () => {
|
|
1619
|
+
const dir = await mkdtemp(path.join(tmpdir(), "ay-stuck-"));
|
|
1620
|
+
try {
|
|
1621
|
+
const log = path.join(dir, "a.log");
|
|
1622
|
+
await writeFile(log, "⏺ Done — all green.\r\n❯\r\n");
|
|
1623
|
+
await utimes(log, tenMinAgo(), tenMinAgo());
|
|
1624
|
+
const mod = await loadModule();
|
|
1625
|
+
expect(await mod.isAgentStuck(rec({ log_file: log }))).toBe(false);
|
|
1626
|
+
} finally {
|
|
1627
|
+
await rm(dir, { recursive: true, force: true }).catch(() => null);
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
|
|
1631
|
+
it("snapshotStatus: reports 'stuck' for a long-silent busy agent (not 'idle')", async () => {
|
|
1632
|
+
const dir = await mkdtemp(path.join(tmpdir(), "ay-stuck-"));
|
|
1633
|
+
try {
|
|
1634
|
+
const log = path.join(dir, "a.log");
|
|
1635
|
+
await writeFile(log, BUSY);
|
|
1636
|
+
await utimes(log, tenMinAgo(), tenMinAgo());
|
|
1637
|
+
const mod = await loadModule();
|
|
1638
|
+
const snap = await mod.snapshotStatus(rec({ log_file: log }));
|
|
1639
|
+
expect(snap.state).toBe("stuck");
|
|
1640
|
+
} finally {
|
|
1641
|
+
await rm(dir, { recursive: true, force: true }).catch(() => null);
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1644
|
+
});
|
package/ts/subcommands.ts
CHANGED
|
Binary file
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import "./ts-CKCsYKjz.js";
|
|
2
|
-
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import "./versionChecker-CZ6UM9LY.js";
|
|
4
|
-
import "./pidStore-fqXqTKkh.js";
|
|
5
|
-
import "./globalPidIndex-DlmmJlO8.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Cjay1Iru.js";
|
|
7
|
-
|
|
8
|
-
export { SUPPORTED_CLIS };
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import "./logger-CDIsZ-Pp.js";
|
|
2
|
-
import "./globalPidIndex-DlmmJlO8.js";
|
|
3
|
-
import "./configShared-C1C04bbq.js";
|
|
4
|
-
import "./remotes-PKKjfTI1.js";
|
|
5
|
-
import { S as writeToIpc, _ as resolveOne, a as cursorAbs, b as snapshotStatus, c as extractTaskCounts, d as isSubcommand, f as listRecords, g as renderRawLogLines, h as renderRawLog, i as controlCodeFromName, l as finalizedLines, m as readNotes, n as READ_PAGE_DEFAULT, o as deriveLiveStatus, p as matchKeyword, r as cmdHelp, s as extractNeedsInput, t as GRACEFUL_EXIT_COMMANDS, u as isPidAlive, v as resolveReadWindow, x as stopTipForCli, y as runSubcommand } from "./subcommands-D4BFGRle.js";
|
|
6
|
-
|
|
7
|
-
export { cmdHelp, isSubcommand, runSubcommand };
|