feat: add project-aware agent workspace
Generic Container CI/CD / test-build-publish (push) Successful in 2m9s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 2m39s

This commit is contained in:
2026-10-22 18:52:18 +08:00
parent c6a0e74e72
commit 2d8506fea0
191 changed files with 12373 additions and 3556 deletions
@@ -1,5 +1,9 @@
import { describe, expect, it } from "vitest";
import { collectRecentAgentActivities } from "./use-agent-prompt-suggestions";
import { afterEach, describe, expect, it, vi } from "vitest";
import { act, createElement } from "react";
import { createRoot, type Root } from "react-dom/client";
import { collectRecentAgentActivities, useAgentPromptSuggestions } from "./use-agent-prompt-suggestions";
import { WorkbenchSessionProvider } from "./workbench-session-provider";
import type { ScheduledConditionItem } from "../types";
import type { AgentChatSessionSummary } from "@/features/agent";
describe("recent activities for Agent suggestions", () => {
@@ -21,3 +25,56 @@ describe("recent activities for Agent suggestions", () => {
expect(activities.filter((item) => item.kind === "session").map((item) => item.title)).toEqual(["updated-old-conversation"]);
});
});
describe("recommendation cache lifecycle", () => {
let root: Root | undefined;
afterEach(async () => { if (root) await act(async () => root!.unmount()); root = undefined; vi.restoreAllMocks(); });
it("reuses recommendations across history refresh, panel reopen and a new empty conversation", async () => {
const container = document.createElement("div");
root = createRoot(container);
const request = vi.fn(async () => [{ question: "核查当前压力", activityId: null }]);
const empty: never[] = [];
function Probe({ enabled, sessionId }: { enabled: boolean; sessionId: string | null }) {
const result = useAgentPromptSuggestions({ enabled, model: "fixture/model", cacheScope: "project:a", sessionId,
request, alerts: empty, alertDataSource: "backend", conditions: empty, runtimeDataSource: "backend", artifact: null, sessions: empty });
return createElement("p", null, result.suggestions[0]?.question ?? "loading");
}
for (let i = 0; i < 10; i++) {
await act(async () => root!.render(createElement(WorkbenchSessionProvider, null, createElement(Probe, { enabled: false, sessionId: null }))));
await act(async () => root!.render(createElement(WorkbenchSessionProvider, null, createElement(Probe, { enabled: true, sessionId: `empty-${i}` }))));
}
expect(request).toHaveBeenCalledTimes(1);
expect(container.textContent).toBe("核查当前压力");
});
it("ignores task heartbeat timestamps but regenerates when an activity changes status", async () => {
root = createRoot(document.createElement("div"));
const request = vi.fn(async () => [{ question: "复核任务证据", activityId: "task:task-a" }]);
const empty: never[] = [];
function Probe({ time, status }: { time: number; status: "running" | "completed" }) {
const condition: ScheduledConditionItem = { id: "task-a", kind: "condition", taskId: "scada-diagnosis", sessionId: "native-a",
title: "SCADA 证据复核", summary: "检查压力监测", riskLevel: "normal", scheduledAt: "2026-10-22T01:00:00Z", updatedAt: time, status };
useAgentPromptSuggestions({ enabled: true, model: "fixture/model", cacheScope: "project:a", sessionId: null, request,
alerts: empty, alertDataSource: "backend", conditions: [condition], runtimeDataSource: "backend", artifact: null, sessions: empty });
return null;
}
for (let i = 0; i < 10; i++) await act(async () => root!.render(createElement(WorkbenchSessionProvider, null,
createElement(Probe, { time: 1000 + i * 5000, status: "running" }))));
expect(request).toHaveBeenCalledTimes(1);
await act(async () => root!.render(createElement(WorkbenchSessionProvider, null, createElement(Probe, { time: 60000, status: "completed" }))));
expect(request).toHaveBeenCalledTimes(2);
});
it("isolates projects and caches generation errors instead of retrying each reopen", async () => {
root = createRoot(document.createElement("div"));
const request = vi.fn(async () => { throw new Error("unavailable"); });
const empty: never[] = [];
function Probe({ scope, enabled }: { scope: string; enabled: boolean }) {
useAgentPromptSuggestions({ enabled, cacheScope: scope, model: "fixture/model", sessionId: null,
request, alerts: empty, alertDataSource: "backend", conditions: empty, runtimeDataSource: "backend", artifact: null, sessions: empty });
return null;
}
for (const [scope, enabled] of [["a", true], ["a", false], ["a", true], ["b", true]] as const) {
await act(async () => root!.render(createElement(WorkbenchSessionProvider, null, createElement(Probe, { scope, enabled }))));
}
expect(request).toHaveBeenCalledTimes(2);
});
});