agent-yes 1.117.0 → 1.118.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-BS7EiXxW.js +8 -0
- package/dist/{SUPPORTED_CLIS-DQYx5cvl.js → SUPPORTED_CLIS-CrIGjD28.js} +2 -2
- package/dist/cli.js +4 -4
- package/dist/index.js +3 -3
- package/dist/pidStore-BLcnCpkX.js +5 -0
- package/dist/{pidStore-DBjlqzo8.js → pidStore-BcGnnKQf.js} +4 -3
- package/dist/{serve-DJthZQAN.js → serve-uhfwjvBd.js} +6 -6
- package/dist/{subcommands-BuLieGot.js → subcommands-FtwGxxtc.js} +1 -1
- package/dist/{subcommands-B_JJRHkV.js → subcommands-tH8JdrBS.js} +92 -7
- package/dist/{ts-B4lhxCQx.js → ts-DYzATaI_.js} +5 -4
- package/dist/{versionChecker-CS7qsffQ.js → versionChecker-D-OYziVA.js} +2 -2
- package/package.json +1 -1
- package/ts/globalPidIndex.ts +5 -0
- package/ts/index.ts +10 -1
- package/ts/pidStore.ts +3 -0
- package/ts/subcommands.spec.ts +50 -0
- package/ts/subcommands.ts +0 -0
- package/dist/SUPPORTED_CLIS-DK9PO6Y6.js +0 -8
- package/dist/pidStore-9b3YTuf4.js +0 -5
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "./ts-DYzATaI_.js";
|
|
2
|
+
import "./logger-B9h0djqx.js";
|
|
3
|
+
import "./versionChecker-D-OYziVA.js";
|
|
4
|
+
import "./pidStore-BcGnnKQf.js";
|
|
5
|
+
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CrIGjD28.js";
|
|
7
|
+
|
|
8
|
+
export { SUPPORTED_CLIS };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { t as CLIS_CONFIG } from "./ts-DYzATaI_.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-CrIGjD28.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { n as logger } from "./logger-B9h0djqx.js";
|
|
3
|
-
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-
|
|
3
|
+
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-D-OYziVA.js";
|
|
4
4
|
import { argv } from "process";
|
|
5
5
|
import { execFileSync, spawn } from "child_process";
|
|
6
6
|
import ms from "ms";
|
|
@@ -482,7 +482,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
|
|
|
482
482
|
{
|
|
483
483
|
const rawArg = process.argv[2];
|
|
484
484
|
const isHelpFlag = rawArg === "-h" || rawArg === "--help";
|
|
485
|
-
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-
|
|
485
|
+
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-FtwGxxtc.js");
|
|
486
486
|
if (isHelpFlag && process.argv.length === 3) {
|
|
487
487
|
cmdHelp();
|
|
488
488
|
process.exit(0);
|
|
@@ -515,7 +515,7 @@ if (config.useRust) {
|
|
|
515
515
|
}
|
|
516
516
|
}
|
|
517
517
|
if (rustBinary) {
|
|
518
|
-
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-
|
|
518
|
+
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-BS7EiXxW.js");
|
|
519
519
|
const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
|
|
520
520
|
if (config.verbose) {
|
|
521
521
|
console.log(`[rust] Using binary: ${rustBinary}`);
|
|
@@ -545,7 +545,7 @@ if (config.showVersion) {
|
|
|
545
545
|
process.exit(0);
|
|
546
546
|
}
|
|
547
547
|
if (config.appendPrompt) {
|
|
548
|
-
const { PidStore } = await import("./pidStore-
|
|
548
|
+
const { PidStore } = await import("./pidStore-BLcnCpkX.js");
|
|
549
549
|
const ipcPath = await PidStore.findActiveFifo(process.cwd());
|
|
550
550
|
if (!ipcPath) {
|
|
551
551
|
console.error("No active agent with IPC found in current directory.");
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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-DYzATaI_.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-
|
|
4
|
-
import "./pidStore-
|
|
3
|
+
import "./versionChecker-D-OYziVA.js";
|
|
4
|
+
import "./pidStore-BcGnnKQf.js";
|
|
5
5
|
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
6
|
|
|
7
7
|
export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
|
|
@@ -223,7 +223,7 @@ var PidStore = class PidStore {
|
|
|
223
223
|
logger.warn("[pidStore] Failed to initialize:", error);
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
|
-
async registerProcess({ pid, cli, args, prompt, cwd }) {
|
|
226
|
+
async registerProcess({ pid, cli, args, prompt, cwd, wrapperPid }) {
|
|
227
227
|
const now = Date.now();
|
|
228
228
|
const argsJson = JSON.stringify(args);
|
|
229
229
|
const logFile = this.getRawLogPath(pid);
|
|
@@ -260,7 +260,8 @@ var PidStore = class PidStore {
|
|
|
260
260
|
status: "active",
|
|
261
261
|
exit_code: null,
|
|
262
262
|
exit_reason: null,
|
|
263
|
-
started_at: now
|
|
263
|
+
started_at: now,
|
|
264
|
+
wrapper_pid: wrapperPid ?? null
|
|
264
265
|
}).then(() => maybeCompactGlobalPids()).catch(() => null);
|
|
265
266
|
return result;
|
|
266
267
|
}
|
|
@@ -381,4 +382,4 @@ pid-db/
|
|
|
381
382
|
|
|
382
383
|
//#endregion
|
|
383
384
|
export { agentYesHome as n, PidStore as t };
|
|
384
|
-
//# sourceMappingURL=pidStore-
|
|
385
|
+
//# sourceMappingURL=pidStore-BcGnnKQf.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-DYzATaI_.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
4
|
-
import "./pidStore-
|
|
3
|
+
import { r as getInstalledPackage } from "./versionChecker-D-OYziVA.js";
|
|
4
|
+
import "./pidStore-BcGnnKQf.js";
|
|
5
5
|
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CrIGjD28.js";
|
|
7
7
|
import "./remotes-C3xPRtfg.js";
|
|
8
|
-
import { c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, m as snapshotStatus, r as controlCodeFromName, u as readNotes } from "./subcommands-
|
|
8
|
+
import { c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, m as snapshotStatus, r as controlCodeFromName, u as readNotes } from "./subcommands-tH8JdrBS.js";
|
|
9
9
|
import yargs from "yargs";
|
|
10
10
|
import { mkdir, open, readFile, writeFile } from "fs/promises";
|
|
11
11
|
import { homedir, hostname, userInfo } from "os";
|
|
@@ -789,4 +789,4 @@ Options:
|
|
|
789
789
|
|
|
790
790
|
//#endregion
|
|
791
791
|
export { cmdServe };
|
|
792
|
-
//# sourceMappingURL=serve-
|
|
792
|
+
//# sourceMappingURL=serve-uhfwjvBd.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./logger-B9h0djqx.js";
|
|
2
2
|
import "./globalPidIndex-yVd3mbsV.js";
|
|
3
3
|
import "./remotes-C3xPRtfg.js";
|
|
4
|
-
import { a as finalizedLines, c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, h as stopTipForCli, i as cursorAbs, l as matchKeyword, m as snapshotStatus, n as cmdHelp, o as isPidAlive, p as runSubcommand, r as controlCodeFromName, s as isSubcommand, t as GRACEFUL_EXIT_COMMANDS, u as readNotes } from "./subcommands-
|
|
4
|
+
import { a as finalizedLines, c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, h as stopTipForCli, i as cursorAbs, l as matchKeyword, m as snapshotStatus, n as cmdHelp, o as isPidAlive, p as runSubcommand, r as controlCodeFromName, s as isSubcommand, t as GRACEFUL_EXIT_COMMANDS, u as readNotes } from "./subcommands-tH8JdrBS.js";
|
|
5
5
|
|
|
6
6
|
export { cmdHelp, isSubcommand, runSubcommand };
|
|
@@ -60,6 +60,74 @@ async function compactNotes() {
|
|
|
60
60
|
})).join("\n");
|
|
61
61
|
await writeFile(notesPath(), lines ? lines + "\n" : "");
|
|
62
62
|
}
|
|
63
|
+
const READ_WINDOW_MS = 6e4;
|
|
64
|
+
const READS_KEY_SEP = "\0";
|
|
65
|
+
function readsPath() {
|
|
66
|
+
const dir = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
|
|
67
|
+
return path.join(dir, "reads.jsonl");
|
|
68
|
+
}
|
|
69
|
+
async function readReads() {
|
|
70
|
+
let raw;
|
|
71
|
+
try {
|
|
72
|
+
raw = await readFile(readsPath(), "utf-8");
|
|
73
|
+
} catch {
|
|
74
|
+
return /* @__PURE__ */ new Map();
|
|
75
|
+
}
|
|
76
|
+
const map = /* @__PURE__ */ new Map();
|
|
77
|
+
for (const line of raw.split("\n")) {
|
|
78
|
+
const t = line.trim();
|
|
79
|
+
if (!t) continue;
|
|
80
|
+
try {
|
|
81
|
+
const { by, target, at } = JSON.parse(t);
|
|
82
|
+
if (typeof by === "string" && typeof target === "number" && typeof at === "number") map.set(`${by}${READS_KEY_SEP}${target}`, at);
|
|
83
|
+
} catch {}
|
|
84
|
+
}
|
|
85
|
+
return map;
|
|
86
|
+
}
|
|
87
|
+
async function recordRead(by, target) {
|
|
88
|
+
const p = readsPath();
|
|
89
|
+
try {
|
|
90
|
+
await mkdir(path.dirname(p), { recursive: true });
|
|
91
|
+
await appendFile(p, JSON.stringify({
|
|
92
|
+
by,
|
|
93
|
+
target,
|
|
94
|
+
at: Date.now()
|
|
95
|
+
}) + "\n");
|
|
96
|
+
if ((await readFile(p, "utf-8").catch(() => "")).split("\n").length > 200) {
|
|
97
|
+
const lines = [...(await readReads()).entries()].map(([k, at]) => {
|
|
98
|
+
const i = k.indexOf(READS_KEY_SEP);
|
|
99
|
+
return JSON.stringify({
|
|
100
|
+
by: k.slice(0, i),
|
|
101
|
+
target: Number(k.slice(i + 1)),
|
|
102
|
+
at
|
|
103
|
+
});
|
|
104
|
+
}).join("\n");
|
|
105
|
+
await writeFile(p, lines ? lines + "\n" : "");
|
|
106
|
+
}
|
|
107
|
+
} catch {}
|
|
108
|
+
}
|
|
109
|
+
async function lastReadAt(by, target) {
|
|
110
|
+
return (await readReads()).get(`${by}${READS_KEY_SEP}${target}`) ?? null;
|
|
111
|
+
}
|
|
112
|
+
async function resolveSender() {
|
|
113
|
+
const envPid = process.env.AGENT_YES_PID ? Number(process.env.AGENT_YES_PID) : null;
|
|
114
|
+
if (!envPid || Number.isNaN(envPid)) return null;
|
|
115
|
+
const recs = await listRecords(void 0, {
|
|
116
|
+
all: true,
|
|
117
|
+
active: false,
|
|
118
|
+
json: false,
|
|
119
|
+
latest: false,
|
|
120
|
+
cwdScope: null
|
|
121
|
+
});
|
|
122
|
+
return recs.find((r) => r.wrapper_pid === envPid) ?? recs.find((r) => r.pid === envPid) ?? null;
|
|
123
|
+
}
|
|
124
|
+
async function senderContext() {
|
|
125
|
+
const agent = await resolveSender();
|
|
126
|
+
return {
|
|
127
|
+
key: agent ? `agent:${agent.pid}` : "human",
|
|
128
|
+
agent
|
|
129
|
+
};
|
|
130
|
+
}
|
|
63
131
|
/**
|
|
64
132
|
* Read the per-cwd TS PidStore JSONL and convert to the global record shape,
|
|
65
133
|
* so pre-existing TS agents that were spawned before the global-index mirror
|
|
@@ -163,7 +231,7 @@ async function runSubcommand(argv) {
|
|
|
163
231
|
case "restart": return await cmdRestart(rest);
|
|
164
232
|
case "note": return await cmdNote(rest);
|
|
165
233
|
case "serve": {
|
|
166
|
-
const { cmdServe } = await import("./serve-
|
|
234
|
+
const { cmdServe } = await import("./serve-uhfwjvBd.js");
|
|
167
235
|
return cmdServe(rest);
|
|
168
236
|
}
|
|
169
237
|
case "setup": {
|
|
@@ -762,6 +830,8 @@ async function cmdRead(rest, { mode }) {
|
|
|
762
830
|
const record = await resolveOne(keyword, opts);
|
|
763
831
|
const logPath = record.log_file;
|
|
764
832
|
if (!logPath) throw new Error(`pid ${record.pid}: no log_file recorded`);
|
|
833
|
+
const reader = await senderContext();
|
|
834
|
+
await recordRead(reader.key, record.pid);
|
|
765
835
|
let stats;
|
|
766
836
|
try {
|
|
767
837
|
stats = await stat(logPath);
|
|
@@ -779,7 +849,10 @@ async function cmdRead(rest, { mode }) {
|
|
|
779
849
|
process.stderr.write(header + "\n");
|
|
780
850
|
process.stdout.write(rendered);
|
|
781
851
|
if (!rendered.endsWith("\n")) process.stdout.write("\n");
|
|
782
|
-
if (follow)
|
|
852
|
+
if (follow) {
|
|
853
|
+
setInterval(() => void recordRead(reader.key, record.pid), 3e4).unref?.();
|
|
854
|
+
return plain ? followPlainLocal(logPath, buf) : followRawLocal(logPath, buf);
|
|
855
|
+
}
|
|
783
856
|
process.stderr.write(`
|
|
784
857
|
ay ls # list all agents
|
|
785
858
|
ay tail -f ${record.pid} # follow live output\n ay send ${record.pid} "next: ..." # send a prompt\n ay send ${record.pid} "" --code=ctrl-c # interrupt\n`);
|
|
@@ -1067,6 +1140,10 @@ async function cmdSend(rest) {
|
|
|
1067
1140
|
}).option("cwd", {
|
|
1068
1141
|
type: "string",
|
|
1069
1142
|
description: "Restrict to agents under this dir"
|
|
1143
|
+
}).option("force", {
|
|
1144
|
+
type: "boolean",
|
|
1145
|
+
default: false,
|
|
1146
|
+
description: "Skip the 'tailed recently' safety check (also: AGENT_YES_FORCE_SEND=1)"
|
|
1070
1147
|
}).help(false).version(false).exitProcess(false).parseAsync();
|
|
1071
1148
|
const opts = {
|
|
1072
1149
|
all: argv.all,
|
|
@@ -1093,9 +1170,17 @@ async function cmdSend(rest) {
|
|
|
1093
1170
|
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
1094
1171
|
body = Buffer.concat(chunks).toString("utf-8").trimEnd();
|
|
1095
1172
|
} else body = rawMessage;
|
|
1096
|
-
const
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1173
|
+
const sender = await senderContext();
|
|
1174
|
+
const force = Boolean(argv.force) || process.env.AGENT_YES_FORCE_SEND === "1";
|
|
1175
|
+
if (sender.agent && sender.agent.pid === record.pid && !force) throw new Error(`refusing to send to yourself (pid ${record.pid}) — pass --force if you really mean it.`);
|
|
1176
|
+
const last = await lastReadAt(sender.key, record.pid);
|
|
1177
|
+
if (!(last !== null && Date.now() - last <= READ_WINDOW_MS) && !force) {
|
|
1178
|
+
const ago = last === null ? "never read" : `last read ${Math.round((Date.now() - last) / 1e3)}s ago`;
|
|
1179
|
+
const what = `pid ${record.pid} (${record.cli}, ${shortenPath(record.cwd)}) — ${ago}, not within ${READ_WINDOW_MS / 1e3}s`;
|
|
1180
|
+
if (sender.agent) throw new Error(`${what}.\n Confirm it's the right agent first: ay tail ${record.pid}\n then resend, or pass --force to override.`);
|
|
1181
|
+
process.stderr.write(`warning: ${what} — make sure this is the agent you meant (ay tail ${record.pid}).\n`);
|
|
1182
|
+
}
|
|
1183
|
+
const fullBody = (sender.agent ? `[from ${sender.agent.cli} #${sender.agent.pid} @ ${shortenPath(sender.agent.cwd)} — reply: ay send ${sender.agent.pid} "..."]\n` : "") + body;
|
|
1099
1184
|
if (fullBody && trailing) {
|
|
1100
1185
|
await writeToIpc(fifoPath, fullBody);
|
|
1101
1186
|
await new Promise((r) => setTimeout(r, 200));
|
|
@@ -1103,7 +1188,7 @@ async function cmdSend(rest) {
|
|
|
1103
1188
|
} else await writeToIpc(fifoPath, fullBody + trailing);
|
|
1104
1189
|
const payload = body + trailing;
|
|
1105
1190
|
process.stdout.write(`sent to pid ${record.pid} (${record.cli}): ${truncate(payload, 80)}\n`);
|
|
1106
|
-
const replyHint =
|
|
1191
|
+
const replyHint = sender.agent ? ` ay send ${sender.agent.pid} "..." # reply to sender\n` : "";
|
|
1107
1192
|
process.stderr.write(`\n` + replyHint + ` ay tail ${record.pid} # watch output\n ay ls # list all agents\n`);
|
|
1108
1193
|
if (codeName === "ctrl-c" || codeName === "ctrlc") {
|
|
1109
1194
|
const tip = stopTipForCli(record.cli, record.pid);
|
|
@@ -1595,4 +1680,4 @@ async function cmdStatus(rest) {
|
|
|
1595
1680
|
|
|
1596
1681
|
//#endregion
|
|
1597
1682
|
export { finalizedLines as a, listRecords as c, renderRawLog as d, resolveOne as f, writeToIpc as g, stopTipForCli as h, cursorAbs as i, matchKeyword as l, snapshotStatus as m, cmdHelp as n, isPidAlive as o, runSubcommand as p, controlCodeFromName as r, isSubcommand as s, GRACEFUL_EXIT_COMMANDS as t, readNotes as u };
|
|
1598
|
-
//# sourceMappingURL=subcommands-
|
|
1683
|
+
//# sourceMappingURL=subcommands-tH8JdrBS.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as logger, t as addTransport } from "./logger-B9h0djqx.js";
|
|
2
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
3
|
-
import { n as agentYesHome, t as PidStore } from "./pidStore-
|
|
2
|
+
import { r as getInstalledPackage } from "./versionChecker-D-OYziVA.js";
|
|
3
|
+
import { n as agentYesHome, t as PidStore } from "./pidStore-BcGnnKQf.js";
|
|
4
4
|
import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CJxsoGdb.js";
|
|
5
5
|
import { i as readGlobalPids } from "./globalPidIndex-yVd3mbsV.js";
|
|
6
6
|
import { arch, platform } from "process";
|
|
@@ -1218,7 +1218,8 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
|
|
|
1218
1218
|
cli,
|
|
1219
1219
|
args: cliArgs,
|
|
1220
1220
|
prompt,
|
|
1221
|
-
cwd: workingDir
|
|
1221
|
+
cwd: workingDir,
|
|
1222
|
+
wrapperPid: process.pid
|
|
1222
1223
|
});
|
|
1223
1224
|
} catch (error) {
|
|
1224
1225
|
logger.warn(`[pidStore] Failed to register process ${shell.pid}:`, error);
|
|
@@ -1713,4 +1714,4 @@ function sleep(ms) {
|
|
|
1713
1714
|
|
|
1714
1715
|
//#endregion
|
|
1715
1716
|
export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
|
|
1716
|
-
//# sourceMappingURL=ts-
|
|
1717
|
+
//# sourceMappingURL=ts-DYzATaI_.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.118.0";
|
|
11
11
|
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region ts/versionChecker.ts
|
|
@@ -221,4 +221,4 @@ async function displayVersion() {
|
|
|
221
221
|
|
|
222
222
|
//#endregion
|
|
223
223
|
export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
|
|
224
|
-
//# sourceMappingURL=versionChecker-
|
|
224
|
+
//# sourceMappingURL=versionChecker-D-OYziVA.js.map
|
package/package.json
CHANGED
package/ts/globalPidIndex.ts
CHANGED
|
@@ -36,6 +36,11 @@ export interface GlobalPidRecord {
|
|
|
36
36
|
exit_code: number | null;
|
|
37
37
|
exit_reason: string | null;
|
|
38
38
|
started_at: number;
|
|
39
|
+
// The `ay` wrapper process pid that spawned this agent. The wrapper injects
|
|
40
|
+
// its own pid as AGENT_YES_PID into the agent's env (the agent's OWN pid isn't
|
|
41
|
+
// known until after spawn), so this maps that env value back to the agent's
|
|
42
|
+
// canonical record — see resolveSender() in subcommands.ts.
|
|
43
|
+
wrapper_pid?: number | null;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
/**
|
package/ts/index.ts
CHANGED
|
@@ -374,7 +374,16 @@ export default async function agentYes({
|
|
|
374
374
|
|
|
375
375
|
// Register process in pidStore (non-blocking - failures should not prevent agent from running)
|
|
376
376
|
try {
|
|
377
|
-
await pidStore.registerProcess({
|
|
377
|
+
await pidStore.registerProcess({
|
|
378
|
+
pid: shell.pid,
|
|
379
|
+
cli,
|
|
380
|
+
args: cliArgs,
|
|
381
|
+
prompt,
|
|
382
|
+
cwd: workingDir,
|
|
383
|
+
// We inject our own pid as AGENT_YES_PID into the agent's env above; record
|
|
384
|
+
// it so a child `ay send` can map that env value back to this agent.
|
|
385
|
+
wrapperPid: process.pid,
|
|
386
|
+
});
|
|
378
387
|
} catch (error) {
|
|
379
388
|
logger.warn(`[pidStore] Failed to register process ${shell.pid}:`, error);
|
|
380
389
|
}
|
package/ts/pidStore.ts
CHANGED
|
@@ -54,12 +54,14 @@ export class PidStore {
|
|
|
54
54
|
args,
|
|
55
55
|
prompt,
|
|
56
56
|
cwd,
|
|
57
|
+
wrapperPid,
|
|
57
58
|
}: {
|
|
58
59
|
pid: number;
|
|
59
60
|
cli: string;
|
|
60
61
|
args: string[];
|
|
61
62
|
prompt?: string;
|
|
62
63
|
cwd: string;
|
|
64
|
+
wrapperPid?: number;
|
|
63
65
|
}): Promise<PidRecord> {
|
|
64
66
|
const now = Date.now();
|
|
65
67
|
const argsJson = JSON.stringify(args);
|
|
@@ -113,6 +115,7 @@ export class PidStore {
|
|
|
113
115
|
exit_code: null,
|
|
114
116
|
exit_reason: null,
|
|
115
117
|
started_at: now,
|
|
118
|
+
wrapper_pid: wrapperPid ?? null,
|
|
116
119
|
})
|
|
117
120
|
.then(() => maybeCompactGlobalPids())
|
|
118
121
|
.catch(() => null);
|
package/ts/subcommands.spec.ts
CHANGED
|
@@ -537,6 +537,13 @@ describe("subcommands.cmdSend writes bytes to FIFO", () => {
|
|
|
537
537
|
stdout.push(String(s));
|
|
538
538
|
return true;
|
|
539
539
|
};
|
|
540
|
+
// Isolate from the send-safety guard: if the suite itself runs inside an
|
|
541
|
+
// ay-managed agent, AGENT_YES_PID would make cmdSend treat this as an agent
|
|
542
|
+
// sender (adding a "from …" prefix / blocking). Force-send to test pure
|
|
543
|
+
// byte delivery. --force keeps the agent prefix off only when no agent
|
|
544
|
+
// context resolves, so also clear AGENT_YES_PID for determinism.
|
|
545
|
+
const savedAyPid = process.env.AGENT_YES_PID;
|
|
546
|
+
delete process.env.AGENT_YES_PID;
|
|
540
547
|
try {
|
|
541
548
|
const code = await runSubcommand([
|
|
542
549
|
"bun",
|
|
@@ -544,11 +551,13 @@ describe("subcommands.cmdSend writes bytes to FIFO", () => {
|
|
|
544
551
|
"send",
|
|
545
552
|
String(process.pid),
|
|
546
553
|
"hello-fifo",
|
|
554
|
+
"--force",
|
|
547
555
|
]);
|
|
548
556
|
expect(code).toBe(0);
|
|
549
557
|
expect(stdout.join("")).toMatch(/sent to pid/);
|
|
550
558
|
} finally {
|
|
551
559
|
process.stdout.write = orig;
|
|
560
|
+
if (savedAyPid !== undefined) process.env.AGENT_YES_PID = savedAyPid;
|
|
552
561
|
}
|
|
553
562
|
|
|
554
563
|
// Now read the bytes back from our RDWR fd.
|
|
@@ -568,6 +577,47 @@ describe("subcommands.cmdSend writes bytes to FIFO", () => {
|
|
|
568
577
|
});
|
|
569
578
|
});
|
|
570
579
|
|
|
580
|
+
describe("subcommands.cmdSend safety guards", () => {
|
|
581
|
+
it("maps AGENT_YES_PID→wrapper_pid and blocks an agent from sending to itself", async () => {
|
|
582
|
+
const { runSubcommand } = await loadModule();
|
|
583
|
+
const { appendGlobalPid } = await import("./globalPidIndex.ts");
|
|
584
|
+
// We register an agent whose wrapper_pid is a known value, then run `ay send`
|
|
585
|
+
// with AGENT_YES_PID set to that wrapper — so resolveSender maps it back to
|
|
586
|
+
// this same agent, and sending to its own pid trips the self-send guard.
|
|
587
|
+
const wrapperPid = 424242;
|
|
588
|
+
await appendGlobalPid({
|
|
589
|
+
pid: process.pid,
|
|
590
|
+
cli: "claude",
|
|
591
|
+
prompt: null,
|
|
592
|
+
cwd: process.cwd(),
|
|
593
|
+
log_file: null,
|
|
594
|
+
fifo_file: "/tmp/ay-guard-test.fifo",
|
|
595
|
+
status: "active",
|
|
596
|
+
exit_code: null,
|
|
597
|
+
exit_reason: null,
|
|
598
|
+
started_at: Date.now(),
|
|
599
|
+
wrapper_pid: wrapperPid,
|
|
600
|
+
});
|
|
601
|
+
const stderr: string[] = [];
|
|
602
|
+
const orig = process.stderr.write.bind(process.stderr);
|
|
603
|
+
(process.stderr as any).write = (s: any) => {
|
|
604
|
+
stderr.push(String(s));
|
|
605
|
+
return true;
|
|
606
|
+
};
|
|
607
|
+
const savedAyPid = process.env.AGENT_YES_PID;
|
|
608
|
+
process.env.AGENT_YES_PID = String(wrapperPid);
|
|
609
|
+
try {
|
|
610
|
+
const code = await runSubcommand(["bun", "cli.js", "send", String(process.pid), "loop?"]);
|
|
611
|
+
expect(code).toBe(1);
|
|
612
|
+
expect(stderr.join("")).toMatch(/refusing to send to yourself/);
|
|
613
|
+
} finally {
|
|
614
|
+
process.stderr.write = orig;
|
|
615
|
+
if (savedAyPid === undefined) delete process.env.AGENT_YES_PID;
|
|
616
|
+
else process.env.AGENT_YES_PID = savedAyPid;
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
|
|
571
621
|
// ---------------------------------------------------------------------------
|
|
572
622
|
// cmdLs additional arg coverage
|
|
573
623
|
// ---------------------------------------------------------------------------
|
package/ts/subcommands.ts
CHANGED
|
Binary file
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import "./ts-B4lhxCQx.js";
|
|
2
|
-
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-CS7qsffQ.js";
|
|
4
|
-
import "./pidStore-DBjlqzo8.js";
|
|
5
|
-
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DQYx5cvl.js";
|
|
7
|
-
|
|
8
|
-
export { SUPPORTED_CLIS };
|