fix: harden executor session handling
This commit is contained in:
@@ -55,7 +55,7 @@ function writeFakeCli(dir: string, opts: {
|
||||
const stdout = opts.stdout ?? "";
|
||||
|
||||
const script = `#!/bin/bash
|
||||
echo "$@" >> "${argsFile}"
|
||||
printf '%s\\0' "$@" >> "${argsFile}"
|
||||
echo '${stdout.replace(/'/g, "'\\''")}'
|
||||
exit ${exitCode}
|
||||
`;
|
||||
@@ -224,11 +224,20 @@ describe("ClaudeAdapter", () => {
|
||||
|
||||
describe("AgyAdapter", () => {
|
||||
let tmpDir: string;
|
||||
let previousHome: string | undefined;
|
||||
|
||||
beforeEach(() => { tmpDir = makeTmpDir(); });
|
||||
afterEach(() => { cleanupDir(tmpDir); });
|
||||
beforeEach(() => {
|
||||
tmpDir = makeTmpDir();
|
||||
previousHome = process.env.HOME;
|
||||
process.env.HOME = tmpDir;
|
||||
});
|
||||
afterEach(() => {
|
||||
if (previousHome === undefined) delete process.env.HOME;
|
||||
else process.env.HOME = previousHome;
|
||||
cleanupDir(tmpDir);
|
||||
});
|
||||
|
||||
it("start uses --print and --mode accept-edits", async () => {
|
||||
it("start passes the complete prompt as the --print value and enables headless tools", async () => {
|
||||
const argsFile = path.join(tmpDir, "args.txt");
|
||||
const fakeBin = writeFakeCli(tmpDir, {
|
||||
argsFile,
|
||||
@@ -242,11 +251,19 @@ describe("AgyAdapter", () => {
|
||||
runDir: tmpDir,
|
||||
cycleIndex: 1,
|
||||
config: { binary: fakeBin },
|
||||
timeoutSeconds: 1800,
|
||||
});
|
||||
|
||||
const capturedArgs = fs.existsSync(argsFile) ? fs.readFileSync(argsFile, "utf8") : "";
|
||||
expect(capturedArgs).toContain("--print");
|
||||
expect(capturedArgs).toContain("--mode accept-edits");
|
||||
const capturedArgs = fs.readFileSync(argsFile, "utf8").split("\0").filter(Boolean);
|
||||
const printIndex = capturedArgs.indexOf("--print");
|
||||
expect(capturedArgs).toContain("--dangerously-skip-permissions");
|
||||
expect(capturedArgs).toContain("--mode");
|
||||
expect(capturedArgs[capturedArgs.indexOf("--mode") + 1]).toBe("accept-edits");
|
||||
expect(capturedArgs).toContain("--print-timeout");
|
||||
expect(capturedArgs[capturedArgs.indexOf("--print-timeout") + 1]).toBe("1800s");
|
||||
expect(capturedArgs).not.toContain("--");
|
||||
expect(printIndex).toBe(capturedArgs.length - 2);
|
||||
expect(capturedArgs[printIndex + 1]).toContain("# Implementation Task: Test plan");
|
||||
|
||||
if (result.sessionHandle?.kind === "agy") {
|
||||
expect(result.sessionHandle.conversationId).toBe("agy-conv-id-456");
|
||||
@@ -285,11 +302,15 @@ describe("AgyAdapter", () => {
|
||||
runDir: tmpDir,
|
||||
cycleIndex: 2,
|
||||
config: { binary: fakeBin },
|
||||
timeoutSeconds: 900,
|
||||
});
|
||||
|
||||
const capturedArgs = fs.existsSync(argsFile) ? fs.readFileSync(argsFile, "utf8") : "";
|
||||
const capturedArgs = fs.readFileSync(argsFile, "utf8").split("\0").filter(Boolean);
|
||||
expect(capturedArgs).toContain("--conversation");
|
||||
expect(capturedArgs).toContain("known-conv-id");
|
||||
expect(capturedArgs).toContain("--dangerously-skip-permissions");
|
||||
expect(capturedArgs[capturedArgs.indexOf("--print-timeout") + 1]).toBe("900s");
|
||||
expect(capturedArgs[capturedArgs.indexOf("--print") + 1]).toContain("# Fix Request: Test plan");
|
||||
});
|
||||
|
||||
it("degraded resume uses --continue flag", async () => {
|
||||
@@ -310,6 +331,71 @@ describe("AgyAdapter", () => {
|
||||
const capturedArgs = fs.existsSync(argsFile) ? fs.readFileSync(argsFile, "utf8") : "";
|
||||
expect(capturedArgs).toContain("--continue");
|
||||
});
|
||||
|
||||
it("captures the conversation ID from the Agy log", async () => {
|
||||
const fakeBin = path.join(tmpDir, "fake-agy.sh");
|
||||
fs.writeFileSync(fakeBin, `#!/bin/bash
|
||||
while [[ $# -gt 0 ]]; do
|
||||
if [[ "$1" == "--log-file" ]]; then
|
||||
shift
|
||||
echo 'Print mode: conversation=12345678-1234-1234-1234-123456789abc, sending message' > "$1"
|
||||
fi
|
||||
shift
|
||||
done
|
||||
echo 'implemented'
|
||||
`, { mode: 0o755 });
|
||||
|
||||
const result = await createExecutorAdapter("agy").start({
|
||||
repoRoot: tmpDir,
|
||||
plan: makeValidPlan(),
|
||||
runDir: tmpDir,
|
||||
cycleIndex: 1,
|
||||
config: { binary: fakeBin },
|
||||
});
|
||||
|
||||
expect(result.sessionHandle).toEqual({
|
||||
kind: "agy",
|
||||
conversationId: "12345678-1234-1234-1234-123456789abc",
|
||||
degraded: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("does not reuse a workspace conversation that predates start", async () => {
|
||||
const cacheDir = path.join(tmpDir, ".gemini", "antigravity-cli", "cache");
|
||||
fs.mkdirSync(cacheDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(cacheDir, "last_conversations.json"), JSON.stringify({
|
||||
[tmpDir]: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
||||
}));
|
||||
const fakeBin = writeFakeCli(tmpDir, { stdout: "implemented" });
|
||||
|
||||
const result = await createExecutorAdapter("agy").start({
|
||||
repoRoot: tmpDir,
|
||||
plan: makeValidPlan(),
|
||||
runDir: tmpDir,
|
||||
cycleIndex: 1,
|
||||
config: { binary: fakeBin },
|
||||
});
|
||||
|
||||
expect(result.sessionHandle).toEqual({ kind: "agy", degraded: true });
|
||||
});
|
||||
|
||||
it("treats headless permission denial as failure even with exit code zero", async () => {
|
||||
const fakeBin = writeFakeCli(tmpDir, {
|
||||
stdout: 'jetski: no output produced — a tool required the "command" permission that headless mode cannot prompt for, so it was auto-denied.',
|
||||
});
|
||||
|
||||
const result = await createExecutorAdapter("agy").start({
|
||||
repoRoot: tmpDir,
|
||||
plan: makeValidPlan(),
|
||||
runDir: tmpDir,
|
||||
cycleIndex: 1,
|
||||
config: { binary: fakeBin },
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.failed).toBe(true);
|
||||
expect(result.failureReason).toContain("no output produced");
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user