59 lines
3.3 KiB
TypeScript
59 lines
3.3 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
import { AcpBackendRegistry } from "../src/acp/backend-registry.js";
|
|
import { AcpSessionManager } from "../src/acp/session-manager.js";
|
|
import { parseConfig } from "../src/config.js";
|
|
import { DurableSessionStore } from "../src/core/durable-session-store.js";
|
|
import { RoleRegistry } from "../src/roles/role-registry.js";
|
|
|
|
const fixture = path.resolve("test/fixtures/fake-acp-agent.mjs");
|
|
|
|
function config(stateFile: string, logFile: string) {
|
|
return parseConfig({
|
|
configVersion: 2, acp: { stateFile, promptTimeoutMs: 2_000, cancelGraceMs: 100, idleTimeoutMs: 100, sweepIntervalMs: 20, maxProcesses: 2 },
|
|
backends: [{ id: "kimi", command: process.execPath, args: [fixture], env: { FAKE_ACP_LOG: logFile } }],
|
|
skills: [], defaultRole: "assistant",
|
|
roles: [{ id: "assistant", backend: "kimi", workspace: path.resolve("."), persona: "test", skills: [], policy: { permissionMode: "deny" } }],
|
|
platforms: {}
|
|
});
|
|
}
|
|
|
|
async function runtime(stateFile: string, logFile: string) {
|
|
const cfg = config(stateFile, logFile); const store = new DurableSessionStore(stateFile); await store.open();
|
|
return { cfg, store, manager: new AcpSessionManager(cfg.acp, new AcpBackendRegistry(cfg.backends), new RoleRegistry(cfg), store) };
|
|
}
|
|
|
|
test("creates, persists, idles, and resumes the same native session", async () => {
|
|
const dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "gori-acp-")); const state = path.join(dir, "state.json"); const log = path.join(dir, "fake.log");
|
|
const first = await runtime(state, log);
|
|
const response = await first.manager.prompt({ platform: "qq", chatId: "chat", userId: "user", text: "one" });
|
|
const sessionId = response.text.split(":")[1];
|
|
assert.match(response.text, /reply:fake-/);
|
|
await new Promise((resolve) => setTimeout(resolve, 180));
|
|
await first.manager.prompt({ platform: "qq", chatId: "chat", userId: "user", text: "two" });
|
|
await first.manager.shutdown(); await first.store.close();
|
|
|
|
const second = await runtime(state, log);
|
|
const resumed = await second.manager.prompt({ platform: "qq", chatId: "chat", userId: "user", text: "three" });
|
|
assert.equal(resumed.text, `reply:${sessionId}:three`);
|
|
const entries = (await fs.promises.readFile(log, "utf8")).trim().split("\n").map(JSON.parse);
|
|
assert.ok(entries.some((entry) => entry.method === "session/resume" && entry.sessionId === sessionId));
|
|
await second.manager.shutdown(); await second.store.close();
|
|
});
|
|
|
|
test("cancel reaches a hanging ACP prompt and new unbinds", async () => {
|
|
const dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "gori-acp-cancel-")); const state = path.join(dir, "state.json"); const log = path.join(dir, "fake.log");
|
|
const current = await runtime(state, log);
|
|
await current.manager.prompt({ platform: "qq", chatId: "chat", userId: "user", text: "ready" });
|
|
const hanging = current.manager.prompt({ platform: "qq", chatId: "chat", userId: "user", text: "hang" });
|
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
assert.equal(await current.manager.cancel("qq", "chat"), true);
|
|
await hanging;
|
|
await current.manager.reset("qq", "chat");
|
|
assert.equal(current.store.stats().bindings, 0);
|
|
await current.manager.shutdown(); await current.store.close();
|
|
});
|