mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 19:21:23 +00:00
refactor(runtime): consolidate followup, gateway, and provider dedupe paths
This commit is contained in:
@@ -46,6 +46,12 @@ describe("boot-md handler", () => {
|
||||
return cfg;
|
||||
}
|
||||
|
||||
function setupSingleMainAgentBootConfig(cfg: unknown) {
|
||||
listAgentIds.mockReturnValue(["main"]);
|
||||
resolveAgentWorkspaceDir.mockReturnValue(MAIN_WORKSPACE_DIR);
|
||||
return cfg;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
@@ -82,9 +88,7 @@ describe("boot-md handler", () => {
|
||||
});
|
||||
|
||||
it("runs boot for single default agent when no agents configured", async () => {
|
||||
const cfg = {};
|
||||
listAgentIds.mockReturnValue(["main"]);
|
||||
resolveAgentWorkspaceDir.mockReturnValue(MAIN_WORKSPACE_DIR);
|
||||
const cfg = setupSingleMainAgentBootConfig({});
|
||||
runBootOnce.mockResolvedValue({ status: "skipped", reason: "missing" });
|
||||
|
||||
await runBootChecklist(makeEvent({ context: { cfg } }));
|
||||
@@ -112,9 +116,7 @@ describe("boot-md handler", () => {
|
||||
});
|
||||
|
||||
it("logs debug details when a per-agent boot run is skipped", async () => {
|
||||
const cfg = { agents: { list: [{ id: "main" }] } };
|
||||
listAgentIds.mockReturnValue(["main"]);
|
||||
resolveAgentWorkspaceDir.mockReturnValue(MAIN_WORKSPACE_DIR);
|
||||
const cfg = setupSingleMainAgentBootConfig({ agents: { list: [{ id: "main" }] } });
|
||||
runBootOnce.mockResolvedValue({ status: "skipped", reason: "missing" });
|
||||
|
||||
await runBootChecklist(makeEvent({ context: { cfg } }));
|
||||
|
||||
@@ -133,6 +133,33 @@ async function createSessionMemoryWorkspace(params?: {
|
||||
return { tempDir, sessionsDir, activeSessionFile };
|
||||
}
|
||||
|
||||
async function loadMemoryFromActiveSessionPointer(params: {
|
||||
tempDir: string;
|
||||
activeSessionFile: string;
|
||||
}): Promise<string> {
|
||||
const { memoryContent } = await runNewWithPreviousSessionEntry({
|
||||
tempDir: params.tempDir,
|
||||
previousSessionEntry: {
|
||||
sessionId: "test-123",
|
||||
sessionFile: params.activeSessionFile,
|
||||
},
|
||||
});
|
||||
return memoryContent;
|
||||
}
|
||||
|
||||
function expectMemoryConversation(params: {
|
||||
memoryContent: string;
|
||||
user: string;
|
||||
assistant: string;
|
||||
absent?: string;
|
||||
}) {
|
||||
expect(params.memoryContent).toContain(`user: ${params.user}`);
|
||||
expect(params.memoryContent).toContain(`assistant: ${params.assistant}`);
|
||||
if (params.absent) {
|
||||
expect(params.memoryContent).not.toContain(params.absent);
|
||||
}
|
||||
}
|
||||
|
||||
describe("session-memory hook", () => {
|
||||
it("skips non-command events", async () => {
|
||||
const tempDir = await makeTempWorkspace("openclaw-session-memory-");
|
||||
@@ -415,17 +442,17 @@ describe("session-memory hook", () => {
|
||||
]),
|
||||
});
|
||||
|
||||
const { memoryContent } = await runNewWithPreviousSessionEntry({
|
||||
const memoryContent = await loadMemoryFromActiveSessionPointer({
|
||||
tempDir,
|
||||
previousSessionEntry: {
|
||||
sessionId: "test-123",
|
||||
sessionFile: activeSessionFile!,
|
||||
},
|
||||
activeSessionFile: activeSessionFile!,
|
||||
});
|
||||
|
||||
expect(memoryContent).toContain("user: Newest rotated transcript");
|
||||
expect(memoryContent).toContain("assistant: Newest summary");
|
||||
expect(memoryContent).not.toContain("Older rotated transcript");
|
||||
expectMemoryConversation({
|
||||
memoryContent,
|
||||
user: "Newest rotated transcript",
|
||||
assistant: "Newest summary",
|
||||
absent: "Older rotated transcript",
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers active transcript when it is non-empty even with reset candidates", async () => {
|
||||
@@ -448,17 +475,17 @@ describe("session-memory hook", () => {
|
||||
]),
|
||||
});
|
||||
|
||||
const { memoryContent } = await runNewWithPreviousSessionEntry({
|
||||
const memoryContent = await loadMemoryFromActiveSessionPointer({
|
||||
tempDir,
|
||||
previousSessionEntry: {
|
||||
sessionId: "test-123",
|
||||
sessionFile: activeSessionFile!,
|
||||
},
|
||||
activeSessionFile: activeSessionFile!,
|
||||
});
|
||||
|
||||
expect(memoryContent).toContain("user: Active transcript message");
|
||||
expect(memoryContent).toContain("assistant: Active transcript summary");
|
||||
expect(memoryContent).not.toContain("Reset fallback message");
|
||||
expectMemoryConversation({
|
||||
memoryContent,
|
||||
user: "Active transcript message",
|
||||
assistant: "Active transcript summary",
|
||||
absent: "Reset fallback message",
|
||||
});
|
||||
});
|
||||
|
||||
it("handles empty session files gracefully", async () => {
|
||||
|
||||
@@ -33,6 +33,25 @@ describe("loader", () => {
|
||||
process.env.OPENCLAW_BUNDLED_HOOKS_DIR = "/nonexistent/bundled/hooks";
|
||||
});
|
||||
|
||||
async function writeHandlerModule(
|
||||
fileName: string,
|
||||
code = "export default async function() {}",
|
||||
): Promise<string> {
|
||||
const handlerPath = path.join(tmpDir, fileName);
|
||||
await fs.writeFile(handlerPath, code, "utf-8");
|
||||
return handlerPath;
|
||||
}
|
||||
|
||||
function createEnabledHooksConfig(
|
||||
handlers?: Array<{ event: string; module: string; export?: string }>,
|
||||
): OpenClawConfig {
|
||||
return {
|
||||
hooks: {
|
||||
internal: handlers ? { enabled: true, handlers } : { enabled: true },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
clearInternalHooks();
|
||||
envSnapshot.restore();
|
||||
@@ -67,27 +86,18 @@ describe("loader", () => {
|
||||
|
||||
it("should load a handler from a module", async () => {
|
||||
// Create a test handler module
|
||||
const handlerPath = path.join(tmpDir, "test-handler.js");
|
||||
const handlerCode = `
|
||||
export default async function(event) {
|
||||
// Test handler
|
||||
}
|
||||
`;
|
||||
await fs.writeFile(handlerPath, handlerCode, "utf-8");
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
hooks: {
|
||||
internal: {
|
||||
enabled: true,
|
||||
handlers: [
|
||||
{
|
||||
event: "command:new",
|
||||
module: path.basename(handlerPath),
|
||||
},
|
||||
],
|
||||
},
|
||||
const handlerPath = await writeHandlerModule("test-handler.js", handlerCode);
|
||||
const cfg = createEnabledHooksConfig([
|
||||
{
|
||||
event: "command:new",
|
||||
module: path.basename(handlerPath),
|
||||
},
|
||||
};
|
||||
]);
|
||||
|
||||
const count = await loadInternalHooks(cfg, tmpDir);
|
||||
expect(count).toBe(1);
|
||||
@@ -98,23 +108,13 @@ describe("loader", () => {
|
||||
|
||||
it("should load multiple handlers", async () => {
|
||||
// Create test handler modules
|
||||
const handler1Path = path.join(tmpDir, "handler1.js");
|
||||
const handler2Path = path.join(tmpDir, "handler2.js");
|
||||
const handler1Path = await writeHandlerModule("handler1.js");
|
||||
const handler2Path = await writeHandlerModule("handler2.js");
|
||||
|
||||
await fs.writeFile(handler1Path, "export default async function() {}", "utf-8");
|
||||
await fs.writeFile(handler2Path, "export default async function() {}", "utf-8");
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
hooks: {
|
||||
internal: {
|
||||
enabled: true,
|
||||
handlers: [
|
||||
{ event: "command:new", module: path.basename(handler1Path) },
|
||||
{ event: "command:stop", module: path.basename(handler2Path) },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const cfg = createEnabledHooksConfig([
|
||||
{ event: "command:new", module: path.basename(handler1Path) },
|
||||
{ event: "command:stop", module: path.basename(handler2Path) },
|
||||
]);
|
||||
|
||||
const count = await loadInternalHooks(cfg, tmpDir);
|
||||
expect(count).toBe(2);
|
||||
@@ -126,47 +126,32 @@ describe("loader", () => {
|
||||
|
||||
it("should support named exports", async () => {
|
||||
// Create a handler module with named export
|
||||
const handlerPath = path.join(tmpDir, "named-export.js");
|
||||
const handlerCode = `
|
||||
export const myHandler = async function(event) {
|
||||
// Named export handler
|
||||
}
|
||||
`;
|
||||
await fs.writeFile(handlerPath, handlerCode, "utf-8");
|
||||
const handlerPath = await writeHandlerModule("named-export.js", handlerCode);
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
hooks: {
|
||||
internal: {
|
||||
enabled: true,
|
||||
handlers: [
|
||||
{
|
||||
event: "command:new",
|
||||
module: path.basename(handlerPath),
|
||||
export: "myHandler",
|
||||
},
|
||||
],
|
||||
},
|
||||
const cfg = createEnabledHooksConfig([
|
||||
{
|
||||
event: "command:new",
|
||||
module: path.basename(handlerPath),
|
||||
export: "myHandler",
|
||||
},
|
||||
};
|
||||
]);
|
||||
|
||||
const count = await loadInternalHooks(cfg, tmpDir);
|
||||
expect(count).toBe(1);
|
||||
});
|
||||
|
||||
it("should handle module loading errors gracefully", async () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
hooks: {
|
||||
internal: {
|
||||
enabled: true,
|
||||
handlers: [
|
||||
{
|
||||
event: "command:new",
|
||||
module: "missing-handler.js",
|
||||
},
|
||||
],
|
||||
},
|
||||
const cfg = createEnabledHooksConfig([
|
||||
{
|
||||
event: "command:new",
|
||||
module: "missing-handler.js",
|
||||
},
|
||||
};
|
||||
]);
|
||||
|
||||
// Should not throw and should return 0 (handler failed to load)
|
||||
const count = await loadInternalHooks(cfg, tmpDir);
|
||||
@@ -175,22 +160,17 @@ describe("loader", () => {
|
||||
|
||||
it("should handle non-function exports", async () => {
|
||||
// Create a module with a non-function export
|
||||
const handlerPath = path.join(tmpDir, "bad-export.js");
|
||||
await fs.writeFile(handlerPath, 'export default "not a function";', "utf-8");
|
||||
const handlerPath = await writeHandlerModule(
|
||||
"bad-export.js",
|
||||
'export default "not a function";',
|
||||
);
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
hooks: {
|
||||
internal: {
|
||||
enabled: true,
|
||||
handlers: [
|
||||
{
|
||||
event: "command:new",
|
||||
module: path.basename(handlerPath),
|
||||
},
|
||||
],
|
||||
},
|
||||
const cfg = createEnabledHooksConfig([
|
||||
{
|
||||
event: "command:new",
|
||||
module: path.basename(handlerPath),
|
||||
},
|
||||
};
|
||||
]);
|
||||
|
||||
// Should not throw and should return 0 (handler is not a function)
|
||||
const count = await loadInternalHooks(cfg, tmpDir);
|
||||
@@ -199,25 +179,17 @@ describe("loader", () => {
|
||||
|
||||
it("should handle relative paths", async () => {
|
||||
// Create a handler module
|
||||
const handlerPath = path.join(tmpDir, "relative-handler.js");
|
||||
await fs.writeFile(handlerPath, "export default async function() {}", "utf-8");
|
||||
const handlerPath = await writeHandlerModule("relative-handler.js");
|
||||
|
||||
// Relative to workspaceDir (tmpDir)
|
||||
const relativePath = path.relative(tmpDir, handlerPath);
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
hooks: {
|
||||
internal: {
|
||||
enabled: true,
|
||||
handlers: [
|
||||
{
|
||||
event: "command:new",
|
||||
module: relativePath,
|
||||
},
|
||||
],
|
||||
},
|
||||
const cfg = createEnabledHooksConfig([
|
||||
{
|
||||
event: "command:new",
|
||||
module: relativePath,
|
||||
},
|
||||
};
|
||||
]);
|
||||
|
||||
const count = await loadInternalHooks(cfg, tmpDir);
|
||||
expect(count).toBe(1);
|
||||
@@ -225,7 +197,6 @@ describe("loader", () => {
|
||||
|
||||
it("should actually call the loaded handler", async () => {
|
||||
// Create a handler that we can verify was called
|
||||
const handlerPath = path.join(tmpDir, "callable-handler.js");
|
||||
const handlerCode = `
|
||||
let callCount = 0;
|
||||
export default async function(event) {
|
||||
@@ -235,21 +206,14 @@ describe("loader", () => {
|
||||
return callCount;
|
||||
}
|
||||
`;
|
||||
await fs.writeFile(handlerPath, handlerCode, "utf-8");
|
||||
const handlerPath = await writeHandlerModule("callable-handler.js", handlerCode);
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
hooks: {
|
||||
internal: {
|
||||
enabled: true,
|
||||
handlers: [
|
||||
{
|
||||
event: "command:new",
|
||||
module: path.basename(handlerPath),
|
||||
},
|
||||
],
|
||||
},
|
||||
const cfg = createEnabledHooksConfig([
|
||||
{
|
||||
event: "command:new",
|
||||
module: path.basename(handlerPath),
|
||||
},
|
||||
};
|
||||
]);
|
||||
|
||||
await loadInternalHooks(cfg, tmpDir);
|
||||
|
||||
@@ -288,13 +252,7 @@ describe("loader", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
hooks: {
|
||||
internal: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const cfg = createEnabledHooksConfig();
|
||||
|
||||
const count = await loadInternalHooks(cfg, tmpDir);
|
||||
expect(count).toBe(0);
|
||||
@@ -312,19 +270,12 @@ describe("loader", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
hooks: {
|
||||
internal: {
|
||||
enabled: true,
|
||||
handlers: [
|
||||
{
|
||||
event: "command:new",
|
||||
module: "legacy-handler.js",
|
||||
},
|
||||
],
|
||||
},
|
||||
const cfg = createEnabledHooksConfig([
|
||||
{
|
||||
event: "command:new",
|
||||
module: "legacy-handler.js",
|
||||
},
|
||||
};
|
||||
]);
|
||||
|
||||
const count = await loadInternalHooks(cfg, tmpDir);
|
||||
expect(count).toBe(0);
|
||||
|
||||
Reference in New Issue
Block a user