feat(agent): add controlled task execution

This commit is contained in:
2026-09-24 04:19:02 +08:00
parent d018bcae67
commit b31033e9ce
48 changed files with 14498 additions and 86 deletions
+62
View File
@@ -95,4 +95,66 @@ describe("SessionMetadataStore", () => {
);
expect(fetched?.title).toBe("新标题");
});
it("finds a triggered session only inside the owning context", async () => {
const context = {
actorKey: "actor-trigger",
projectId: "project-trigger",
projectKey: "project-key-trigger",
userId: "user-trigger",
};
const { record } = await store.ensure({
...context,
sessionId: "triggered-session",
trigger: {
triggerId: "peripheral.work_order.created:project:order:1",
source: "peripheral_sample",
subjectType: "work_order",
subjectId: "50000000-0000-4000-8000-000000000001",
workflow: "water_quality_complaint",
occurredAt: "2026-09-22T10:00:00+08:00",
},
});
expect(
await store.findByTrigger(
context,
"peripheral.work_order.created:project:order:1",
),
).toEqual(record);
expect(
await store.findByTrigger(
{ ...context, actorKey: "other-actor", userId: "other-user" },
"peripheral.work_order.created:project:order:1",
),
).toBeNull();
});
it("keeps ordinary chat, task execution, and task collaboration distinct", async () => {
const context = {
actorKey: "actor-purpose",
projectId: "project-purpose",
projectKey: "project-key-purpose",
userId: "user-purpose",
};
await store.ensure({ ...context, sessionId: "ordinary" });
await store.ensure({
...context,
sessionId: "execution",
purpose: "task_execution",
taskId: "50000000-0000-4000-8000-000000000001",
requestId: "60000000-0000-4000-8000-000000000001",
});
await store.ensure({
...context,
sessionId: "collaboration",
purpose: "task_collaboration",
taskId: "50000000-0000-4000-8000-000000000001",
});
expect((await store.list(context, { purpose: "ordinary_chat" })).map((item) => item.sessionId)).toEqual(["ordinary"]);
expect((await store.list(context, { purpose: "task_execution" })).map((item) => item.sessionId)).toEqual(["execution"]);
expect((await store.findByTask(context, "50000000-0000-4000-8000-000000000001", "task_collaboration"))?.sessionId).toBe("collaboration");
expect((await store.findByRequest(context, "60000000-0000-4000-8000-000000000001"))?.sessionId).toBe("execution");
});
});
+20
View File
@@ -0,0 +1,20 @@
import { expect, it } from "bun:test";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { SessionUiStateStore } from "../../src/sessions/uiStateStore.js";
it("restores plan history independently of chat messages after reconnecting", async () => {
const directory = await mkdtemp(join(tmpdir(), "tjwater-plan-state-"));
try {
const store = new SessionUiStateStore(directory);
await store.initialize();
const executionPlans = [{ revision: 1, nodes: [{ id: "sampling", status: "waiting" }] }];
await store.write({ sessionId: "session-1" }, { sessionId: "session-1", messages: [], executionPlans });
const reloaded = await new SessionUiStateStore(directory).read({ sessionId: "session-1" });
expect(reloaded?.executionPlans).toEqual(executionPlans);
expect(await store.read({ sessionId: "other-session" })).toBeNull();
} finally {
await rm(directory, { recursive: true, force: true });
}
});