feat: expose task execution history for debugging
This commit is contained in:
@@ -14,6 +14,7 @@ const http = globalThis.fetch;
|
||||
const projectId = "10000000-0000-4000-8000-000000000001";
|
||||
const firstTask = "50000000-0000-4000-8000-000000000001";
|
||||
const secondTask = "50000000-0000-4000-8000-000000000002";
|
||||
const firstRun = "70000000-0000-4000-8000-000000000001";
|
||||
let root: string | undefined;
|
||||
let server: Server | undefined;
|
||||
|
||||
@@ -75,9 +76,15 @@ async function fixture() {
|
||||
await new Promise<void>((resolve) => server!.once("listening", resolve));
|
||||
const address = server.address();
|
||||
if (!address || typeof address === "string") throw new Error("missing server address");
|
||||
const request = async (path: string, method = "GET", user = "owner") => {
|
||||
const request = async (path: string, method = "GET", user = "owner", body?: unknown) => {
|
||||
const response = await http(`http://127.0.0.1:${address.port}/api/v1${path}`, {
|
||||
method, headers: { Authorization: `Bearer ${user}`, "X-Project-Id": projectId },
|
||||
method,
|
||||
headers: {
|
||||
Authorization: `Bearer ${user}`,
|
||||
"X-Project-Id": projectId,
|
||||
...(body === undefined ? {} : { "Content-Type": "application/json" }),
|
||||
},
|
||||
body: body === undefined ? undefined : JSON.stringify(body),
|
||||
});
|
||||
return { status: response.status, body: await response.json() as Record<string, any> };
|
||||
};
|
||||
@@ -87,7 +94,20 @@ async function fixture() {
|
||||
await states.write({ ...context, sessionId }, { sessionId, messages: [], isTitleManuallyEdited: manuallyEdited });
|
||||
return titled;
|
||||
};
|
||||
return { request, seed, sessions, context, taskReads, setAvailable: (value: boolean) => { available = value; },
|
||||
const seedExecution = async (sessionId: string, taskId: string, runId: string) => {
|
||||
const workerContext = {
|
||||
actorKey: toActorKey("service:operational-worker"),
|
||||
projectKey: context.projectKey,
|
||||
projectId,
|
||||
userId: "operational-worker",
|
||||
};
|
||||
await sessions.ensure({ ...workerContext, sessionId, taskId, runId, purpose: "task_execution" });
|
||||
await states.write({ ...workerContext, sessionId }, { sessionId, messages: [
|
||||
{ id: "execution-user", role: "user", parts: [{ type: "text", text: "执行后台调查任务" }] },
|
||||
{ id: "execution-assistant", role: "assistant", parts: [{ type: "text", text: "已完成压力与流量证据核对" }] },
|
||||
] });
|
||||
};
|
||||
return { request, seed, seedExecution, sessions, context, taskReads, setAvailable: (value: boolean) => { available = value; },
|
||||
setTaskReadHook: (hook: () => Promise<unknown>) => { onTaskRead = hook; },
|
||||
waitForTitleAbort: () => { waitForAbort = true; }, didAbort: () => observedAbort };
|
||||
}
|
||||
@@ -106,6 +126,72 @@ it("names new collaboration sessions after their verified tasks", async () => {
|
||||
expect(taskReads).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("opens an exact task execution binding as a read-only cross-actor history", async () => {
|
||||
const { request, seedExecution } = await fixture();
|
||||
await seedExecution("worker-execution", firstTask, firstRun);
|
||||
|
||||
expect((await request("/session/worker-execution")).status).toBe(404);
|
||||
const result = await request(`/task/${firstTask}/execution-session?run_id=${firstRun}&session_id=worker-execution`);
|
||||
|
||||
expect(result.status).toBe(200);
|
||||
expect(result.body).toMatchObject({
|
||||
session_id: "worker-execution",
|
||||
title: "南桥管道爆管调查 · 执行记录",
|
||||
purpose: "task_execution",
|
||||
task_id: firstTask,
|
||||
read_only: true,
|
||||
is_streaming: false,
|
||||
});
|
||||
expect(result.body.messages.map((message: { id: string }) => message.id)).toEqual([
|
||||
"execution-user",
|
||||
"execution-assistant",
|
||||
]);
|
||||
expect((await request(`/task/${firstTask}/execution-session?run_id=${secondTask}&session_id=worker-execution`)).status).toBe(404);
|
||||
expect((await request(`/task/${secondTask}/execution-session?run_id=${firstRun}&session_id=worker-execution`)).status).toBe(404);
|
||||
});
|
||||
|
||||
it("creates one user-owned debug branch from an exact task execution history", async () => {
|
||||
const { request, seedExecution } = await fixture();
|
||||
await seedExecution("worker-execution", firstTask, firstRun);
|
||||
|
||||
const first = await request(`/task/${firstTask}/execution-session/debug`, "POST", "owner", {
|
||||
run_id: firstRun,
|
||||
session_id: "worker-execution",
|
||||
});
|
||||
|
||||
expect(first.status).toBe(201);
|
||||
expect(first.body).toMatchObject({
|
||||
title: "南桥管道爆管调查 · 调试",
|
||||
purpose: "task_collaboration",
|
||||
task_id: firstTask,
|
||||
parent_session_id: "worker-execution",
|
||||
created: true,
|
||||
});
|
||||
const debug = await request(`/session/${first.body.session_id}`);
|
||||
expect(debug.body.messages.map((message: { id: string }) => message.id)).toEqual([
|
||||
"execution-user",
|
||||
"execution-assistant",
|
||||
]);
|
||||
|
||||
const reused = await request(`/task/${firstTask}/execution-session/debug`, "POST", "owner", {
|
||||
run_id: firstRun,
|
||||
session_id: "worker-execution",
|
||||
});
|
||||
expect(reused.status).toBe(200);
|
||||
expect(reused.body).toMatchObject({ session_id: first.body.session_id, created: false });
|
||||
});
|
||||
|
||||
it("validates current task access before revealing an execution history", async () => {
|
||||
const { request, seedExecution, setAvailable } = await fixture();
|
||||
await seedExecution("protected-execution", firstTask, firstRun);
|
||||
setAvailable(false);
|
||||
|
||||
const result = await request(`/task/${firstTask}/execution-session?run_id=${firstRun}&session_id=protected-execution`);
|
||||
|
||||
expect(result.status).toBe(503);
|
||||
expect(result.body.message).toBe("task context unavailable");
|
||||
});
|
||||
|
||||
it("repairs returned legacy history without changing timestamps or custom names, then searches the repaired titles", async () => {
|
||||
const { request, seed, sessions, context, taskReads } = await fixture();
|
||||
const first = await seed("legacy-first", firstTask, "任务协作");
|
||||
|
||||
Reference in New Issue
Block a user