Add office agent management wizard (#56)
* Add agents * Agent * Added agents management * Polish agent wizard and release blockers. Finalize the office agent management flow by aligning the gateway fallback behavior, cleaning up UI semantics, and updating tests so the branch is ready to ship. Made-with: Cursor --------- Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com> Co-authored-by: iamlukethedev <lucas.guilherme@smartwayslfl.com>
This commit is contained in:
@@ -111,14 +111,14 @@ describe("AgentBrainPanel", () => {
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("heading", { name: "Persona" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("heading", { name: "SOUL.md" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.getByRole("heading", { name: "Directives" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("heading", { name: "Context" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("heading", { name: "Identity" })).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Directives")).toHaveValue("alpha agents");
|
||||
expect(screen.getByLabelText("Persona")).toHaveValue(
|
||||
expect(screen.getByRole("heading", { name: "AGENTS.md" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("heading", { name: "USER.md" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("heading", { name: "IDENTITY.md" })).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("AGENTS.md")).toHaveValue("alpha agents");
|
||||
expect(screen.getByLabelText("SOUL.md")).toHaveValue(
|
||||
"# SOUL.md - Who You Are\n\n## Core Truths\n\nBe useful."
|
||||
);
|
||||
expect(screen.getByLabelText("Name")).toHaveValue("Alpha");
|
||||
@@ -154,10 +154,10 @@ describe("AgentBrainPanel", () => {
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText("Directives")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("AGENTS.md")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.change(screen.getByLabelText("Directives"), {
|
||||
fireEvent.change(screen.getByLabelText("AGENTS.md"), {
|
||||
target: { value: "alpha directives updated" },
|
||||
});
|
||||
|
||||
@@ -171,15 +171,17 @@ describe("AgentBrainPanel", () => {
|
||||
expect(filesByAgent["agent-1"]["AGENTS.md"]).toBe("alpha directives updated");
|
||||
});
|
||||
|
||||
it("discards_unsaved_changes_without_writing_files", async () => {
|
||||
it("calls_cancel_without_writing_files", async () => {
|
||||
const { client, calls } = createMockClient();
|
||||
const agents = [createAgent("agent-1", "Alpha", "session-1")];
|
||||
const onCancel = vi.fn();
|
||||
|
||||
render(
|
||||
createElement(AgentBrainPanel, {
|
||||
client,
|
||||
agents,
|
||||
selectedAgentId: "agent-1",
|
||||
onCancel,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -192,12 +194,12 @@ describe("AgentBrainPanel", () => {
|
||||
});
|
||||
expect(screen.getByLabelText("Name")).toHaveValue("Alpha Prime");
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Discard" }));
|
||||
expect(screen.getByLabelText("Name")).toHaveValue("Alpha");
|
||||
fireEvent.click(screen.getByRole("button", { name: "Cancel" }));
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
expect(calls.some((entry) => entry.method === "agents.files.set")).toBe(false);
|
||||
});
|
||||
|
||||
it("does_not_render_name_editor_in_personality_panel", async () => {
|
||||
it("does_not_render_legacy_name_editor_controls", async () => {
|
||||
const { client } = createMockClient();
|
||||
const agents = [createAgent("agent-1", "Alpha", "session-1")];
|
||||
|
||||
@@ -210,7 +212,7 @@ describe("AgentBrainPanel", () => {
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("heading", { name: "Persona" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("heading", { name: "SOUL.md" })).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.queryByLabelText("Agent name")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Update Name" })).not.toBeInTheDocument();
|
||||
|
||||
@@ -615,7 +615,7 @@ describe("AgentSettingsPanel", () => {
|
||||
target: { value: "git" },
|
||||
});
|
||||
fireEvent.click(screen.getByRole("button", { name: "Configure" }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "Remove skill from gateway" }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "Remove for all agents" }));
|
||||
|
||||
expect(onRemoveSkill).toHaveBeenCalledWith({
|
||||
skillKey: "github",
|
||||
|
||||
@@ -18,7 +18,7 @@ describe("resolveGatewayAutoRetryDelayMs", () => {
|
||||
expect(delay).toBeNull();
|
||||
});
|
||||
|
||||
it("retries for non-auth connect failures", () => {
|
||||
it("does not retry when the upstream websocket upgrade fails", () => {
|
||||
const delay = resolveGatewayAutoRetryDelayMs({
|
||||
status: "disconnected",
|
||||
didAutoConnect: true,
|
||||
@@ -31,8 +31,7 @@ describe("resolveGatewayAutoRetryDelayMs", () => {
|
||||
attempt: 0,
|
||||
});
|
||||
|
||||
expect(delay).toBeTypeOf("number");
|
||||
expect(delay).toBeGreaterThan(0);
|
||||
expect(delay).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { act, renderHook } from "@testing-library/react";
|
||||
import { useOnboardingState } from "@/features/onboarding/useOnboardingState";
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ describe("settingsRouteWorkflow", () => {
|
||||
})
|
||||
).toEqual([
|
||||
{ kind: "set-personality-dirty", value: false },
|
||||
{ kind: "push", href: "/agents" },
|
||||
{ kind: "push", href: "/" },
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -169,7 +169,7 @@ describe("settingsRouteWorkflow", () => {
|
||||
hasRouteAgent: false,
|
||||
currentInspectSidebar: null,
|
||||
})
|
||||
).toEqual([{ kind: "replace", href: "/agents" }]);
|
||||
).toEqual([{ kind: "replace", href: "/" }]);
|
||||
});
|
||||
|
||||
it("plans non-route selection reconciliation", () => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { CronRunResult } from "@/lib/cron/types";
|
||||
import type { MutationBlockState } from "@/features/agents/operations/mutationLifecycleWorkflow";
|
||||
|
||||
import { useAgentSettingsMutationController } from "@/features/agents/operations/useAgentSettingsMutationController";
|
||||
import { deleteAgentViaStudio } from "@/features/agents/operations/deleteAgentOperation";
|
||||
import { deleteAgentRecordViaStudio } from "@/features/agents/operations/deleteAgentOperation";
|
||||
import { performCronCreateFlow } from "@/features/agents/operations/cronCreateOperation";
|
||||
import { updateAgentPermissionsViaStudio } from "@/features/agents/operations/agentPermissionsOperation";
|
||||
import { runAgentConfigMutationLifecycle } from "@/features/agents/operations/mutationLifecycleWorkflow";
|
||||
@@ -50,7 +50,7 @@ vi.mock("@/features/agents/operations/useGatewayRestartBlock", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("@/features/agents/operations/deleteAgentOperation", () => ({
|
||||
deleteAgentViaStudio: vi.fn(),
|
||||
deleteAgentRecordViaStudio: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/features/agents/operations/cronCreateOperation", () => ({
|
||||
@@ -219,7 +219,7 @@ const renderController = (overrides?: Partial<Parameters<typeof useAgentSettings
|
||||
};
|
||||
|
||||
describe("useAgentSettingsMutationController", () => {
|
||||
const mockedDeleteAgentViaStudio = vi.mocked(deleteAgentViaStudio);
|
||||
const mockedDeleteAgentViaStudio = vi.mocked(deleteAgentRecordViaStudio);
|
||||
const mockedPerformCronCreateFlow = vi.mocked(performCronCreateFlow);
|
||||
const mockedRunCronJobNow = vi.mocked(runCronJobNow);
|
||||
const mockedRemoveCronJob = vi.mocked(removeCronJob);
|
||||
@@ -347,7 +347,7 @@ describe("useAgentSettingsMutationController", () => {
|
||||
deps.clearBlock();
|
||||
return true;
|
||||
});
|
||||
mockedDeleteAgentViaStudio.mockResolvedValue({ trashed: { trashDir: "", moved: [] }, restored: null });
|
||||
mockedDeleteAgentViaStudio.mockResolvedValue(undefined);
|
||||
|
||||
const ctx = renderController();
|
||||
|
||||
@@ -653,13 +653,15 @@ describe("useAgentSettingsMutationController", () => {
|
||||
});
|
||||
});
|
||||
|
||||
expect(mockedRemoveSkillFromGateway).toHaveBeenCalledWith({
|
||||
skillKey: "browser",
|
||||
source: "openclaw-workspace",
|
||||
baseDir: "/tmp/workspace/skills/browser",
|
||||
workspaceDir: "/tmp/workspace",
|
||||
managedSkillsDir: "/tmp/skills",
|
||||
});
|
||||
expect(mockedRemoveSkillFromGateway).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
skillKey: "browser",
|
||||
source: "openclaw-workspace",
|
||||
baseDir: "/tmp/workspace/skills/browser",
|
||||
workspaceDir: "/tmp/workspace",
|
||||
managedSkillsDir: "/tmp/skills",
|
||||
})
|
||||
);
|
||||
expect(ctx.enqueueConfigMutation).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ kind: "update-skill-setup" })
|
||||
);
|
||||
|
||||
@@ -276,7 +276,7 @@ describe("useSettingsRouteController", () => {
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(ctx.replace).toHaveBeenCalledWith("/agents");
|
||||
expect(ctx.replace).toHaveBeenCalledWith("/");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user