import { assert, assertEquals, assertStringIncludes } from "@std/assert"; import { BACKGROUND_CONTEXT as context } from "@earendil-works/chord/context"; import { fauxAssistantMessage, fauxToolCall } from "@earendil-works/pi-ai/providers/faux"; import { readFile, writeFile } from "node:fs/promises"; import { join } from "node:path"; import diff from "../extensions/diff/mod.ts"; import type { OpenFile, Saved } from "../extensions/diff/edit-types.ts"; import { onPath, tempDir } from "@berry/util"; import { setup } from "./fixture.ts"; const IDENTITY = { GIT_AUTHOR_NAME: "Test", GIT_AUTHOR_EMAIL: "test@example.com", GIT_COMMITTER_NAME: "Test", GIT_COMMITTER_EMAIL: "test@example.com", GIT_CONFIG_GLOBAL: "/dev/null", }; async function run(cwd: string, ...args: string[]) { const result = await new Deno.Command("git", { args, cwd, env: IDENTITY, stdout: "piped", stderr: "piped", }).output(); if (!result.success) throw new Error(new TextDecoder().decode(result.stderr)); } Deno.test({ name: "a diff save lands while the conversation is busy, and its report follows at the boundary", ignore: !onPath("git"), fn: async () => { await using harness = await setup(); await using rootDir = await tempDir({ prefix: "diff-busy-test-" }); const root = rootDir.path; await run(root, "init", "--quiet"); await writeFile(join(root, "a.txt"), "base\nshared\nend\n"); await run(root, "add", "-A"); await run(root, "commit", "--quiet", "-m", "first"); await writeFile(join(root, "a.txt"), "agent\nshared\nend\n"); const { berry, faux, gate, model } = harness; await berry.install(diff); const tid = await berry.create({ extensions: { environment: { cwd: root }, gate: {}, diff: {} }, model, }); const conversation = (await berry.conversation(tid))!; const opened = await berry.request(tid, "diff", "open", { root, path: "a.txt", }) as unknown as OpenFile; faux.setResponses([ fauxAssistantMessage(fauxToolCall("hold", {}), { stopReason: "toolUse" }), fauxAssistantMessage("done"), ]); const running = await conversation.submit({ type: "input", content: "go" }, context); await gate.entered.promise; let finishing: Promise | undefined; try { // the agent's turn is held open; the save must still write the file and answer const saved = await berry.request(tid, "diff", "save", { session: "review", id: "edit", working: opened.working, path: "a.txt", version: opened.version, content: "agent\nhuman\nend\n", }) as unknown as Saved; assertEquals(await readFile(join(root, "a.txt"), "utf8"), "agent\nhuman\nend\n"); // reporting it may wait for the boundary, and is not awaited here finishing = berry.command( tid, "diff-finish", JSON.stringify({ session: "review", saves: [saved] }), ); assertEquals( (await conversation.context(context)).messages.some((message) => typeof message.content === "string" && message.content.includes("human edits") ), false, ); } finally { gate.release.resolve("released"); } await finishing; await running.wait(context); await conversation.waitForIdle(context); const report = (await conversation.context(context)).messages.findLast((message) => typeof message.content === "string" && message.content.includes("Historical human edits") ); assert(report); assertStringIncludes(report.content as string, "+human"); }, });