mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 20:14:30 +00:00
fix: run BOOT.md for each configured agent at startup (#20569)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 9098a4cc64
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
122
src/hooks/bundled/boot-md/handler.test.ts
Normal file
122
src/hooks/bundled/boot-md/handler.test.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { InternalHookEvent } from "../../internal-hooks.js";
|
||||
|
||||
const runBootOnce = vi.fn();
|
||||
const listAgentIds = vi.fn();
|
||||
const resolveAgentWorkspaceDir = vi.fn();
|
||||
const logWarn = vi.fn();
|
||||
const logDebug = vi.fn();
|
||||
|
||||
vi.mock("../../../gateway/boot.js", () => ({ runBootOnce }));
|
||||
vi.mock("../../../agents/agent-scope.js", () => ({
|
||||
listAgentIds,
|
||||
resolveAgentWorkspaceDir,
|
||||
}));
|
||||
vi.mock("../../../logging/subsystem.js", () => ({
|
||||
createSubsystemLogger: () => ({
|
||||
warn: logWarn,
|
||||
debug: logDebug,
|
||||
}),
|
||||
}));
|
||||
|
||||
const { default: runBootChecklist } = await import("./handler.js");
|
||||
|
||||
function makeEvent(overrides?: Partial<InternalHookEvent>): InternalHookEvent {
|
||||
return {
|
||||
type: "gateway",
|
||||
action: "startup",
|
||||
sessionKey: "test",
|
||||
context: {},
|
||||
timestamp: new Date(),
|
||||
messages: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("boot-md handler", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
logWarn.mockReset();
|
||||
logDebug.mockReset();
|
||||
});
|
||||
|
||||
it("skips non-gateway events", async () => {
|
||||
await runBootChecklist(makeEvent({ type: "command", action: "new" }));
|
||||
expect(runBootOnce).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("skips non-startup actions", async () => {
|
||||
await runBootChecklist(makeEvent({ action: "shutdown" }));
|
||||
expect(runBootOnce).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("skips when cfg is missing from context", async () => {
|
||||
await runBootChecklist(makeEvent({ context: { workspaceDir: "/tmp" } }));
|
||||
expect(runBootOnce).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("runs boot for each agent", async () => {
|
||||
const cfg = { agents: { list: [{ id: "main" }, { id: "ops" }] } };
|
||||
listAgentIds.mockReturnValue(["main", "ops"]);
|
||||
resolveAgentWorkspaceDir.mockImplementation((_cfg: unknown, id: string) => `/ws/${id}`);
|
||||
runBootOnce.mockResolvedValue({ status: "ran" });
|
||||
|
||||
await runBootChecklist(makeEvent({ context: { cfg } }));
|
||||
|
||||
expect(listAgentIds).toHaveBeenCalledWith(cfg);
|
||||
expect(runBootOnce).toHaveBeenCalledTimes(2);
|
||||
expect(runBootOnce).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ cfg, workspaceDir: "/ws/main", agentId: "main" }),
|
||||
);
|
||||
expect(runBootOnce).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ cfg, workspaceDir: "/ws/ops", agentId: "ops" }),
|
||||
);
|
||||
});
|
||||
|
||||
it("runs boot for single default agent when no agents configured", async () => {
|
||||
const cfg = {};
|
||||
listAgentIds.mockReturnValue(["main"]);
|
||||
resolveAgentWorkspaceDir.mockReturnValue("/ws/main");
|
||||
runBootOnce.mockResolvedValue({ status: "skipped", reason: "missing" });
|
||||
|
||||
await runBootChecklist(makeEvent({ context: { cfg } }));
|
||||
|
||||
expect(runBootOnce).toHaveBeenCalledTimes(1);
|
||||
expect(runBootOnce).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ cfg, workspaceDir: "/ws/main", agentId: "main" }),
|
||||
);
|
||||
});
|
||||
|
||||
it("logs warning details when a per-agent boot run fails", async () => {
|
||||
const cfg = { agents: { list: [{ id: "main" }, { id: "ops" }] } };
|
||||
listAgentIds.mockReturnValue(["main", "ops"]);
|
||||
resolveAgentWorkspaceDir.mockImplementation((_cfg: unknown, id: string) => `/ws/${id}`);
|
||||
runBootOnce
|
||||
.mockResolvedValueOnce({ status: "ran" })
|
||||
.mockResolvedValueOnce({ status: "failed", reason: "agent failed" });
|
||||
|
||||
await runBootChecklist(makeEvent({ context: { cfg } }));
|
||||
|
||||
expect(logWarn).toHaveBeenCalledTimes(1);
|
||||
expect(logWarn).toHaveBeenCalledWith("boot-md failed for agent startup run", {
|
||||
agentId: "ops",
|
||||
workspaceDir: "/ws/ops",
|
||||
reason: "agent failed",
|
||||
});
|
||||
});
|
||||
|
||||
it("logs debug details when a per-agent boot run is skipped", async () => {
|
||||
const cfg = { agents: { list: [{ id: "main" }] } };
|
||||
listAgentIds.mockReturnValue(["main"]);
|
||||
resolveAgentWorkspaceDir.mockReturnValue("/ws/main");
|
||||
runBootOnce.mockResolvedValue({ status: "skipped", reason: "missing" });
|
||||
|
||||
await runBootChecklist(makeEvent({ context: { cfg } }));
|
||||
|
||||
expect(logDebug).toHaveBeenCalledWith("boot-md skipped for agent startup run", {
|
||||
agentId: "main",
|
||||
workspaceDir: "/ws/main",
|
||||
reason: "missing",
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user