79 lines
3.5 KiB
JavaScript
79 lines
3.5 KiB
JavaScript
// scenarios/agent-lifecycle.mjs — full plugin lifecycle against the real
|
|
// daemon: hello→welcome, persisted events, online flag, events after disconnect.
|
|
|
|
import { agentClient, agentEvent, agentHello, rest, sessionSnapshot, sid, waitUntil } from "../lib.mjs";
|
|
|
|
export async function run(ctx) {
|
|
const { r, base, token, agentUrl } = ctx;
|
|
const id = sid("e2e-life");
|
|
const snapshot = sessionSnapshot(id);
|
|
|
|
const agent = await agentClient(agentUrl, token);
|
|
const welcome = await agentHello(agent, snapshot);
|
|
r.check("hello → welcome received", welcome !== null && welcome.type === "welcome");
|
|
r.check("welcome echoes sessionId", welcome?.sessionId === id, JSON.stringify(welcome ?? null));
|
|
r.check("welcome lastSeq 0 on fresh session", welcome?.lastSeq === 0, `got ${welcome?.lastSeq}`);
|
|
r.check("welcome envelope v=1", welcome?.v === 1);
|
|
|
|
agentEvent(agent, id, 1, "message_end", {
|
|
message: { role: "user", id: "m1", text: "hello from e2e", thinking: null, toolCalls: [], toolCallId: null },
|
|
});
|
|
agentEvent(agent, id, 2, "tool_execution_start", { toolCallId: "tc1", toolName: "bash", args: { command: "ls" } });
|
|
agentEvent(agent, id, 3, "tool_execution_end", { toolCallId: "tc1", toolName: "bash", isError: false, resultPreview: "file1\nfile2" });
|
|
agentEvent(agent, id, 4, "agent_end", { usage: { inputTokens: 10, outputTokens: 5, totalCost: 0.25 } });
|
|
|
|
const listUrl = "/api/sessions";
|
|
const online = await waitUntil(async () => {
|
|
const res = await rest(base, token, listUrl);
|
|
return res.json?.find((s) => s.id === id)?.online === true;
|
|
});
|
|
r.check("GET /api/sessions shows online=true while agent connected", online);
|
|
|
|
const listed = (await rest(base, token, listUrl)).json.find((s) => s.id === id);
|
|
r.check(
|
|
"session snapshot fields exposed",
|
|
listed?.cwd === "/work/e2e" &&
|
|
listed?.model === "glm-5.3" &&
|
|
listed?.provider === "zai-renaud" &&
|
|
listed?.agent === false &&
|
|
listed?.repo === null &&
|
|
typeof listed?.startedAt === "number" &&
|
|
typeof listed?.lastEventAt === "number",
|
|
JSON.stringify(listed ?? null),
|
|
);
|
|
|
|
agent.close();
|
|
const offline = await waitUntil(async () => {
|
|
const res = await rest(base, token, listUrl);
|
|
return res.json?.find((s) => s.id === id)?.online === false;
|
|
});
|
|
r.check("online=false after agent disconnect", offline);
|
|
|
|
const eventsUrl = `/api/sessions/${id}/events?after=0`;
|
|
const persisted = await waitUntil(
|
|
async () => (await rest(base, token, eventsUrl)).json?.length === 4,
|
|
);
|
|
r.check("all 4 persisted events served after=0", persisted);
|
|
const ev = (await rest(base, token, eventsUrl)).json ?? [];
|
|
r.check(
|
|
"event seqs ascending [1,2,3,4]",
|
|
JSON.stringify(ev.map((e) => e.seq)) === "[1,2,3,4]",
|
|
JSON.stringify(ev.map((e) => e.seq)),
|
|
);
|
|
r.check(
|
|
"event types in order",
|
|
JSON.stringify(ev.map((e) => e.type)) ===
|
|
JSON.stringify(["message_end", "tool_execution_start", "tool_execution_end", "agent_end"]),
|
|
JSON.stringify(ev.map((e) => e.type)),
|
|
);
|
|
r.check(
|
|
"persisted frames re-serialized with envelope",
|
|
ev[0]?.v === 1 && ev[0]?.sessionId === id && typeof ev[0]?.ts === "number" && ev[0]?.message?.text === "hello from e2e",
|
|
JSON.stringify(ev[0] ?? null),
|
|
);
|
|
const tail = (await rest(base, token, `/api/sessions/${id}/events?after=2`)).json ?? [];
|
|
r.check("events?after=2 returns only seq≥3", JSON.stringify(tail.map((e) => e.seq)) === "[3,4]", JSON.stringify(tail.map((e) => e.seq)));
|
|
|
|
ctx.state.lifecycle = { id, snapshot, lastSeq: 4 };
|
|
}
|