feat: pending-finish proposals and QQ image input
Proposal semantics: worker results no longer distinguish success/fail; only an explicit finish settles a pending proposal. Pending owner input follows up by resuming the original worker session. Dirty pending blocks start_next globally; clean pending only blocks its owner. QQ adapter now downloads image attachments and passes them as ACP image content blocks; video/file attachments degrade to link text.
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { spawn } from "node:child_process";
|
||||
import crypto from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
import type { RequestPermissionRequest } from "@agentclientprotocol/sdk";
|
||||
@@ -51,3 +54,37 @@ test("reports activity for every update from the active session", async () => {
|
||||
child.kill("SIGTERM");
|
||||
}
|
||||
});
|
||||
|
||||
test("prompt sends text and image content blocks and records the image capability", async () => {
|
||||
const logFile = path.join(os.tmpdir(), `gori-acp-client-${crypto.randomUUID()}.log`);
|
||||
const fixture = path.resolve("test/fixtures/fake-acp-agent.mjs");
|
||||
const child = spawn(process.execPath, [fixture], {
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
env: { ...process.env, FAKE_ACP_LOG: logFile }
|
||||
});
|
||||
const client = new AcpClient(child, {
|
||||
initializeTimeoutMs: 1_000,
|
||||
policy: { mode: "deny", allowedTools: [], allowedCommandPatterns: [] }
|
||||
});
|
||||
try {
|
||||
await client.initialize();
|
||||
assert.equal(client.supportsImageInput(), true);
|
||||
await client.newSession(path.resolve("."));
|
||||
const image = Buffer.from("fake-png").toString("base64");
|
||||
const reply = await client.prompt([
|
||||
{ type: "image", data: image, mimeType: "image/png" },
|
||||
{ type: "text", text: "hello blocks" }
|
||||
]);
|
||||
assert.match(reply, /^reply:.+:hello blocks$/);
|
||||
const prompts = fs.readFileSync(logFile, "utf8").trim().split("\n").filter(Boolean)
|
||||
.map((line) => JSON.parse(line) as { method: string; text?: string; images?: number })
|
||||
.filter((entry) => entry.method === "session/prompt");
|
||||
assert.equal(prompts.length, 1);
|
||||
assert.equal(prompts[0]!.text, "hello blocks");
|
||||
assert.equal(prompts[0]!.images, 1);
|
||||
} finally {
|
||||
client.close();
|
||||
child.kill("SIGTERM");
|
||||
await fs.promises.rm(logFile, { force: true });
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user