Add ACP-backed role sessions
This commit is contained in:
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env node
|
||||
import fs from "node:fs";
|
||||
import { Readable, Writable } from "node:stream";
|
||||
import * as acp from "@agentclientprotocol/sdk";
|
||||
|
||||
const pending = new Map();
|
||||
const logFile = process.env.FAKE_ACP_LOG;
|
||||
const log = (entry) => { if (logFile) fs.appendFileSync(logFile, `${JSON.stringify(entry)}\n`); };
|
||||
|
||||
const app = acp.agent({ name: "fake-acp-agent" })
|
||||
.onRequest(acp.methods.agent.initialize, ({ params }) => {
|
||||
log({ method: "initialize" });
|
||||
return {
|
||||
protocolVersion: params.protocolVersion,
|
||||
agentCapabilities: { loadSession: true, sessionCapabilities: { resume: {}, close: {}, list: {} } },
|
||||
agentInfo: { name: "fake-acp-agent", version: "1" }
|
||||
};
|
||||
})
|
||||
.onRequest(acp.methods.agent.session.new, ({ params }) => {
|
||||
const sessionId = `fake-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
log({ method: "session/new", sessionId, cwd: params.cwd });
|
||||
return { sessionId };
|
||||
})
|
||||
.onRequest(acp.methods.agent.session.load, ({ params }) => { log({ method: "session/load", sessionId: params.sessionId }); return {}; })
|
||||
.onRequest(acp.methods.agent.session.resume, ({ params }) => { log({ method: "session/resume", sessionId: params.sessionId }); return {}; })
|
||||
.onRequest(acp.methods.agent.session.close, ({ params }) => { log({ method: "session/close", sessionId: params.sessionId }); return {}; })
|
||||
.onRequest(acp.methods.agent.session.list, () => ({ sessions: [] }))
|
||||
.onRequest(acp.methods.agent.session.prompt, async ({ params, client, signal }) => {
|
||||
const text = params.prompt.filter((item) => item.type === "text").map((item) => item.text).join("");
|
||||
log({ method: "session/prompt", sessionId: params.sessionId, text });
|
||||
if (text === "crash") process.exit(9);
|
||||
if (text === "permission") {
|
||||
const response = await client.request(acp.methods.client.session.requestPermission, {
|
||||
sessionId: params.sessionId,
|
||||
toolCall: { toolCallId: "bash-1", title: "bash git status", kind: "execute", name: "bash", rawInput: "git status" },
|
||||
options: [
|
||||
{ optionId: "allow", name: "Allow", kind: "allow_once" },
|
||||
{ optionId: "reject", name: "Reject", kind: "reject_once" }
|
||||
]
|
||||
});
|
||||
await update(client, params.sessionId, response.outcome.outcome === "selected" ? response.outcome.optionId : "cancelled");
|
||||
return { stopReason: "end_turn" };
|
||||
}
|
||||
if (text === "hang") {
|
||||
await new Promise((resolve) => {
|
||||
const done = () => resolve(undefined);
|
||||
pending.set(params.sessionId, done);
|
||||
signal.addEventListener("abort", done, { once: true });
|
||||
});
|
||||
pending.delete(params.sessionId);
|
||||
return { stopReason: "cancelled" };
|
||||
}
|
||||
await update(client, params.sessionId, text.includes("Initialize this ACP session") ? "READY" : `reply:${params.sessionId}:${text}`);
|
||||
return { stopReason: "end_turn" };
|
||||
})
|
||||
.onNotification(acp.methods.agent.session.cancel, ({ params }) => {
|
||||
log({ method: "session/cancel", sessionId: params.sessionId });
|
||||
pending.get(params.sessionId)?.();
|
||||
});
|
||||
|
||||
function update(client, sessionId, text) {
|
||||
return client.notify(acp.methods.client.session.update, {
|
||||
sessionId,
|
||||
update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text } }
|
||||
});
|
||||
}
|
||||
|
||||
const stream = acp.ndJsonStream(
|
||||
Writable.toWeb(process.stdout),
|
||||
Readable.toWeb(process.stdin)
|
||||
);
|
||||
const connection = app.connect(stream);
|
||||
await connection.closed;
|
||||
Reference in New Issue
Block a user