mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-12 20:32:54 +00:00
refactor: dedupe agent and browser cli helpers
This commit is contained in:
@@ -28,6 +28,27 @@ describe("cron tool", () => {
|
||||
return params?.payload?.text ?? "";
|
||||
}
|
||||
|
||||
function expectSingleGatewayCallMethod(method: string) {
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
const call = readGatewayCall(0);
|
||||
expect(call.method).toBe(method);
|
||||
return call.params;
|
||||
}
|
||||
|
||||
function buildReminderAgentTurnJob(overrides: Record<string, unknown> = {}): {
|
||||
name: string;
|
||||
schedule: { at: string };
|
||||
payload: { kind: "agentTurn"; message: string };
|
||||
delivery?: { mode: string; to?: string };
|
||||
} {
|
||||
return {
|
||||
name: "reminder",
|
||||
schedule: { at: new Date(123).toISOString() },
|
||||
payload: { kind: "agentTurn", message: "hello" },
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
async function executeAddAndReadDelivery(params: {
|
||||
callId: string;
|
||||
agentSessionKey: string;
|
||||
@@ -37,9 +58,7 @@ describe("cron tool", () => {
|
||||
await tool.execute(params.callId, {
|
||||
action: "add",
|
||||
job: {
|
||||
name: "reminder",
|
||||
schedule: { at: new Date(123).toISOString() },
|
||||
payload: { kind: "agentTurn", message: "hello" },
|
||||
...buildReminderAgentTurnJob(),
|
||||
...(params.delivery !== undefined ? { delivery: params.delivery } : {}),
|
||||
},
|
||||
});
|
||||
@@ -114,13 +133,8 @@ describe("cron tool", () => {
|
||||
const tool = createCronTool();
|
||||
await tool.execute("call1", args);
|
||||
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
const call = callGatewayMock.mock.calls[0]?.[0] as {
|
||||
method?: string;
|
||||
params?: unknown;
|
||||
};
|
||||
expect(call.method).toBe(`cron.${action}`);
|
||||
expect(call.params).toEqual(expectedParams);
|
||||
const params = expectSingleGatewayCallMethod(`cron.${action}`);
|
||||
expect(params).toEqual(expectedParams);
|
||||
});
|
||||
|
||||
it("prefers jobId over id when both are provided", async () => {
|
||||
@@ -131,10 +145,7 @@ describe("cron tool", () => {
|
||||
id: "job-legacy",
|
||||
});
|
||||
|
||||
const call = callGatewayMock.mock.calls[0]?.[0] as {
|
||||
params?: unknown;
|
||||
};
|
||||
expect(call?.params).toEqual({ id: "job-primary", mode: "force" });
|
||||
expect(readGatewayCall().params).toEqual({ id: "job-primary", mode: "force" });
|
||||
});
|
||||
|
||||
it("supports due-only run mode", async () => {
|
||||
@@ -145,10 +156,7 @@ describe("cron tool", () => {
|
||||
runMode: "due",
|
||||
});
|
||||
|
||||
const call = callGatewayMock.mock.calls[0]?.[0] as {
|
||||
params?: unknown;
|
||||
};
|
||||
expect(call?.params).toEqual({ id: "job-due", mode: "due" });
|
||||
expect(readGatewayCall().params).toEqual({ id: "job-due", mode: "due" });
|
||||
});
|
||||
|
||||
it("normalizes cron.add job payloads", async () => {
|
||||
@@ -164,13 +172,8 @@ describe("cron tool", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
const call = callGatewayMock.mock.calls[0]?.[0] as {
|
||||
method?: string;
|
||||
params?: unknown;
|
||||
};
|
||||
expect(call.method).toBe("cron.add");
|
||||
expect(call.params).toEqual({
|
||||
const params = expectSingleGatewayCallMethod("cron.add");
|
||||
expect(params).toEqual({
|
||||
name: "wake-up",
|
||||
enabled: true,
|
||||
deleteAfterRun: true,
|
||||
@@ -367,15 +370,12 @@ describe("cron tool", () => {
|
||||
payload: { kind: "agentTurn", message: "do stuff" },
|
||||
});
|
||||
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
const call = callGatewayMock.mock.calls[0]?.[0] as {
|
||||
method?: string;
|
||||
params?: { name?: string; sessionTarget?: string; payload?: { kind?: string } };
|
||||
};
|
||||
expect(call.method).toBe("cron.add");
|
||||
expect(call.params?.name).toBe("flat-job");
|
||||
expect(call.params?.sessionTarget).toBe("isolated");
|
||||
expect(call.params?.payload?.kind).toBe("agentTurn");
|
||||
const params = expectSingleGatewayCallMethod("cron.add") as
|
||||
| { name?: string; sessionTarget?: string; payload?: { kind?: string } }
|
||||
| undefined;
|
||||
expect(params?.name).toBe("flat-job");
|
||||
expect(params?.sessionTarget).toBe("isolated");
|
||||
expect(params?.payload?.kind).toBe("agentTurn");
|
||||
});
|
||||
|
||||
it("recovers flat params when job is empty object", async () => {
|
||||
@@ -391,15 +391,12 @@ describe("cron tool", () => {
|
||||
payload: { kind: "systemEvent", text: "wake up" },
|
||||
});
|
||||
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
const call = callGatewayMock.mock.calls[0]?.[0] as {
|
||||
method?: string;
|
||||
params?: { name?: string; sessionTarget?: string; payload?: { text?: string } };
|
||||
};
|
||||
expect(call.method).toBe("cron.add");
|
||||
expect(call.params?.name).toBe("empty-job");
|
||||
expect(call.params?.sessionTarget).toBe("main");
|
||||
expect(call.params?.payload?.text).toBe("wake up");
|
||||
const params = expectSingleGatewayCallMethod("cron.add") as
|
||||
| { name?: string; sessionTarget?: string; payload?: { text?: string } }
|
||||
| undefined;
|
||||
expect(params?.name).toBe("empty-job");
|
||||
expect(params?.sessionTarget).toBe("main");
|
||||
expect(params?.payload?.text).toBe("wake up");
|
||||
});
|
||||
|
||||
it("recovers flat message shorthand as agentTurn payload", async () => {
|
||||
@@ -412,16 +409,13 @@ describe("cron tool", () => {
|
||||
message: "do stuff",
|
||||
});
|
||||
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
const call = callGatewayMock.mock.calls[0]?.[0] as {
|
||||
method?: string;
|
||||
params?: { payload?: { kind?: string; message?: string }; sessionTarget?: string };
|
||||
};
|
||||
expect(call.method).toBe("cron.add");
|
||||
const params = expectSingleGatewayCallMethod("cron.add") as
|
||||
| { payload?: { kind?: string; message?: string }; sessionTarget?: string }
|
||||
| undefined;
|
||||
// normalizeCronJobCreate infers agentTurn from message and isolated from agentTurn
|
||||
expect(call.params?.payload?.kind).toBe("agentTurn");
|
||||
expect(call.params?.payload?.message).toBe("do stuff");
|
||||
expect(call.params?.sessionTarget).toBe("isolated");
|
||||
expect(params?.payload?.kind).toBe("agentTurn");
|
||||
expect(params?.payload?.message).toBe("do stuff");
|
||||
expect(params?.sessionTarget).toBe("isolated");
|
||||
});
|
||||
|
||||
it("does not recover flat params when no meaningful job field is present", async () => {
|
||||
@@ -486,9 +480,7 @@ describe("cron tool", () => {
|
||||
tool.execute("call-webhook-missing", {
|
||||
action: "add",
|
||||
job: {
|
||||
name: "reminder",
|
||||
schedule: { at: new Date(123).toISOString() },
|
||||
payload: { kind: "agentTurn", message: "hello" },
|
||||
...buildReminderAgentTurnJob(),
|
||||
delivery: { mode: "webhook" },
|
||||
},
|
||||
}),
|
||||
@@ -503,9 +495,7 @@ describe("cron tool", () => {
|
||||
tool.execute("call-webhook-invalid", {
|
||||
action: "add",
|
||||
job: {
|
||||
name: "reminder",
|
||||
schedule: { at: new Date(123).toISOString() },
|
||||
payload: { kind: "agentTurn", message: "hello" },
|
||||
...buildReminderAgentTurnJob(),
|
||||
delivery: { mode: "webhook", to: "ftp://example.invalid/cron-finished" },
|
||||
},
|
||||
}),
|
||||
@@ -524,15 +514,12 @@ describe("cron tool", () => {
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
const call = callGatewayMock.mock.calls[0]?.[0] as {
|
||||
method?: string;
|
||||
params?: { id?: string; patch?: { name?: string; enabled?: boolean } };
|
||||
};
|
||||
expect(call.method).toBe("cron.update");
|
||||
expect(call.params?.id).toBe("job-1");
|
||||
expect(call.params?.patch?.name).toBe("new-name");
|
||||
expect(call.params?.patch?.enabled).toBe(false);
|
||||
const params = expectSingleGatewayCallMethod("cron.update") as
|
||||
| { id?: string; patch?: { name?: string; enabled?: boolean } }
|
||||
| undefined;
|
||||
expect(params?.id).toBe("job-1");
|
||||
expect(params?.patch?.name).toBe("new-name");
|
||||
expect(params?.patch?.enabled).toBe(false);
|
||||
});
|
||||
|
||||
it("recovers additional flat patch params for update action", async () => {
|
||||
@@ -546,16 +533,17 @@ describe("cron tool", () => {
|
||||
failureAlert: { after: 3, cooldownMs: 60_000 },
|
||||
});
|
||||
|
||||
const call = callGatewayMock.mock.calls[0]?.[0] as {
|
||||
method?: string;
|
||||
params?: {
|
||||
id?: string;
|
||||
patch?: { sessionTarget?: string; failureAlert?: { after?: number; cooldownMs?: number } };
|
||||
};
|
||||
};
|
||||
expect(call.method).toBe("cron.update");
|
||||
expect(call.params?.id).toBe("job-2");
|
||||
expect(call.params?.patch?.sessionTarget).toBe("main");
|
||||
expect(call.params?.patch?.failureAlert).toEqual({ after: 3, cooldownMs: 60_000 });
|
||||
const params = expectSingleGatewayCallMethod("cron.update") as
|
||||
| {
|
||||
id?: string;
|
||||
patch?: {
|
||||
sessionTarget?: string;
|
||||
failureAlert?: { after?: number; cooldownMs?: number };
|
||||
};
|
||||
}
|
||||
| undefined;
|
||||
expect(params?.id).toBe("job-2");
|
||||
expect(params?.patch?.sessionTarget).toBe("main");
|
||||
expect(params?.patch?.failureAlert).toEqual({ after: 3, cooldownMs: 60_000 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,32 +60,38 @@ export function coercePdfAssistantText(params: {
|
||||
provider: string;
|
||||
model: string;
|
||||
}): string {
|
||||
const stop = params.message.stopReason;
|
||||
const label = `${params.provider}/${params.model}`;
|
||||
const errorMessage = params.message.errorMessage?.trim();
|
||||
if (stop === "error" || stop === "aborted") {
|
||||
const fail = (message?: string) => {
|
||||
throw new Error(
|
||||
errorMessage
|
||||
? `PDF model failed (${params.provider}/${params.model}): ${errorMessage}`
|
||||
: `PDF model failed (${params.provider}/${params.model})`,
|
||||
message ? `PDF model failed (${label}): ${message}` : `PDF model failed (${label})`,
|
||||
);
|
||||
};
|
||||
if (params.message.stopReason === "error" || params.message.stopReason === "aborted") {
|
||||
fail(errorMessage);
|
||||
}
|
||||
if (errorMessage) {
|
||||
throw new Error(`PDF model failed (${params.provider}/${params.model}): ${errorMessage}`);
|
||||
fail(errorMessage);
|
||||
}
|
||||
const text = extractAssistantText(params.message);
|
||||
if (text.trim()) {
|
||||
return text.trim();
|
||||
const trimmed = text.trim();
|
||||
if (trimmed) {
|
||||
return trimmed;
|
||||
}
|
||||
throw new Error(`PDF model returned no text (${params.provider}/${params.model}).`);
|
||||
throw new Error(`PDF model returned no text (${label}).`);
|
||||
}
|
||||
|
||||
export function coercePdfModelConfig(cfg?: OpenClawConfig): PdfModelConfig {
|
||||
const primary = resolveAgentModelPrimaryValue(cfg?.agents?.defaults?.pdfModel);
|
||||
const fallbacks = resolveAgentModelFallbackValues(cfg?.agents?.defaults?.pdfModel);
|
||||
return {
|
||||
...(primary?.trim() ? { primary: primary.trim() } : {}),
|
||||
...(fallbacks.length > 0 ? { fallbacks } : {}),
|
||||
};
|
||||
const modelConfig: PdfModelConfig = {};
|
||||
if (primary?.trim()) {
|
||||
modelConfig.primary = primary.trim();
|
||||
}
|
||||
if (fallbacks.length > 0) {
|
||||
modelConfig.fallbacks = fallbacks;
|
||||
}
|
||||
return modelConfig;
|
||||
}
|
||||
|
||||
export function resolvePdfToolMaxTokens(
|
||||
|
||||
@@ -89,9 +89,14 @@ export async function handleTelegramAction(
|
||||
mediaLocalRoots?: readonly string[];
|
||||
},
|
||||
): Promise<AgentToolResult<unknown>> {
|
||||
const action = readStringParam(params, "action", { required: true });
|
||||
const accountId = readStringParam(params, "accountId");
|
||||
const isActionEnabled = createTelegramActionGate({ cfg, accountId });
|
||||
const { action, accountId } = {
|
||||
action: readStringParam(params, "action", { required: true }),
|
||||
accountId: readStringParam(params, "accountId"),
|
||||
};
|
||||
const isActionEnabled = createTelegramActionGate({
|
||||
cfg,
|
||||
accountId,
|
||||
});
|
||||
|
||||
if (action === "react") {
|
||||
// All react failures return soft results (jsonResult with ok:false) instead
|
||||
|
||||
Reference in New Issue
Block a user