Files
openclaw/src/agents/pi-embedded-runner/run/attempt.test.ts
2026-02-24 15:14:05 +00:00

165 lines
5.1 KiB
TypeScript

import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { ImageContent } from "@mariozechner/pi-ai";
import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../../config/config.js";
import {
injectHistoryImagesIntoMessages,
resolveAttemptFsWorkspaceOnly,
resolvePromptBuildHookResult,
resolvePromptModeForSession,
} from "./attempt.js";
describe("injectHistoryImagesIntoMessages", () => {
const image: ImageContent = { type: "image", data: "abc", mimeType: "image/png" };
it("injects history images and converts string content", () => {
const messages: AgentMessage[] = [
{
role: "user",
content: "See /tmp/photo.png",
} as AgentMessage,
];
const didMutate = injectHistoryImagesIntoMessages(messages, new Map([[0, [image]]]));
expect(didMutate).toBe(true);
const firstUser = messages[0] as Extract<AgentMessage, { role: "user" }> | undefined;
expect(Array.isArray(firstUser?.content)).toBe(true);
const content = firstUser?.content as Array<{ type: string; text?: string; data?: string }>;
expect(content).toHaveLength(2);
expect(content[0]?.type).toBe("text");
expect(content[1]).toMatchObject({ type: "image", data: "abc" });
});
it("avoids duplicating existing image content", () => {
const messages: AgentMessage[] = [
{
role: "user",
content: [{ type: "text", text: "See /tmp/photo.png" }, { ...image }],
} as AgentMessage,
];
const didMutate = injectHistoryImagesIntoMessages(messages, new Map([[0, [image]]]));
expect(didMutate).toBe(false);
const first = messages[0] as Extract<AgentMessage, { role: "user" }> | undefined;
if (!first || !Array.isArray(first.content)) {
throw new Error("expected array content");
}
expect(first.content).toHaveLength(2);
});
it("ignores non-user messages and out-of-range indices", () => {
const messages: AgentMessage[] = [
{
role: "assistant",
content: "noop",
} as unknown as AgentMessage,
];
const didMutate = injectHistoryImagesIntoMessages(messages, new Map([[1, [image]]]));
expect(didMutate).toBe(false);
const firstAssistant = messages[0] as Extract<AgentMessage, { role: "assistant" }> | undefined;
expect(firstAssistant?.content).toBe("noop");
});
});
describe("resolvePromptBuildHookResult", () => {
function createLegacyOnlyHookRunner() {
return {
hasHooks: vi.fn(
(hookName: "before_prompt_build" | "before_agent_start") =>
hookName === "before_agent_start",
),
runBeforePromptBuild: vi.fn(async () => undefined),
runBeforeAgentStart: vi.fn(async () => ({ prependContext: "from-hook" })),
};
}
it("reuses precomputed legacy before_agent_start result without invoking hook again", async () => {
const hookRunner = createLegacyOnlyHookRunner();
const result = await resolvePromptBuildHookResult({
prompt: "hello",
messages: [],
hookCtx: {},
hookRunner,
legacyBeforeAgentStartResult: { prependContext: "from-cache", systemPrompt: "legacy-system" },
});
expect(hookRunner.runBeforeAgentStart).not.toHaveBeenCalled();
expect(result).toEqual({
prependContext: "from-cache",
systemPrompt: "legacy-system",
});
});
it("calls legacy hook when precomputed result is absent", async () => {
const hookRunner = createLegacyOnlyHookRunner();
const messages = [{ role: "user", content: "ctx" }];
const result = await resolvePromptBuildHookResult({
prompt: "hello",
messages,
hookCtx: {},
hookRunner,
});
expect(hookRunner.runBeforeAgentStart).toHaveBeenCalledTimes(1);
expect(hookRunner.runBeforeAgentStart).toHaveBeenCalledWith({ prompt: "hello", messages }, {});
expect(result.prependContext).toBe("from-hook");
});
});
describe("resolvePromptModeForSession", () => {
it("uses minimal mode for subagent sessions", () => {
expect(resolvePromptModeForSession("agent:main:subagent:child")).toBe("minimal");
});
it("uses full mode for cron sessions", () => {
expect(resolvePromptModeForSession("agent:main:cron:job-1")).toBe("full");
expect(resolvePromptModeForSession("agent:main:cron:job-1:run:run-abc")).toBe("full");
});
});
describe("resolveAttemptFsWorkspaceOnly", () => {
it("uses global tools.fs.workspaceOnly when agent has no override", () => {
const cfg: OpenClawConfig = {
tools: {
fs: { workspaceOnly: true },
},
};
expect(
resolveAttemptFsWorkspaceOnly({
config: cfg,
sessionAgentId: "main",
}),
).toBe(true);
});
it("prefers agent-specific tools.fs.workspaceOnly override", () => {
const cfg: OpenClawConfig = {
tools: {
fs: { workspaceOnly: true },
},
agents: {
list: [
{
id: "main",
tools: {
fs: { workspaceOnly: false },
},
},
],
},
};
expect(
resolveAttemptFsWorkspaceOnly({
config: cfg,
sessionAgentId: "main",
}),
).toBe(false);
});
});