fix: validate message poll duration hours · openclaw/openclaw@d2fbc8c
steipete
·
2026-05-29
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -390,6 +390,14 @@ describe("message tool gateway timeout", () => {
|
390 | 390 | expect(getToolProperties(tool).timeoutMs).toMatchObject({ type: "integer", minimum: 1 }); |
391 | 391 | }); |
392 | 392 | |
| 393 | +it("advertises shared poll duration as a positive integer", () => { |
| 394 | +const tool = createMessageTool(); |
| 395 | +expect(getToolProperties(tool).pollDurationHours).toMatchObject({ |
| 396 | +type: "integer", |
| 397 | +minimum: 1, |
| 398 | +}); |
| 399 | +}); |
| 400 | + |
393 | 401 | it.each([-1, 1.5, "fast"])( |
394 | 402 | "rejects invalid timeoutMs value %s before dispatch", |
395 | 403 | async (timeoutMs) => { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -40,7 +40,12 @@ import { stripFormattedReasoningMessage } from "../../shared/text/formatted-reas
|
40 | 40 | import { normalizeMessageChannel } from "../../utils/message-channel.js"; |
41 | 41 | import { resolveSessionAgentId } from "../agent-scope.js"; |
42 | 42 | import { listAllChannelSupportedActions, listChannelSupportedActions } from "../channel-tools.js"; |
43 | | -import { channelTargetSchema, channelTargetsSchema, stringEnum } from "../schema/typebox.js"; |
| 43 | +import { |
| 44 | +channelTargetSchema, |
| 45 | +channelTargetsSchema, |
| 46 | +optionalPositiveIntegerSchema, |
| 47 | +stringEnum, |
| 48 | +} from "../schema/typebox.js"; |
44 | 49 | import type { AnyAgentTool } from "./common.js"; |
45 | 50 | import { jsonResult, readStringParam } from "./common.js"; |
46 | 51 | import { gatewayCallOptionSchemaProperties } from "./gateway-schema.js"; |
@@ -349,8 +354,8 @@ function buildPollSchema() {
|
349 | 354 | case "stringArray": |
350 | 355 | props[name] = Type.Optional(Type.Array(Type.String())); |
351 | 356 | break; |
352 | | -case "number": |
353 | | -props[name] = Type.Optional(Type.Number()); |
| 357 | +case "positiveInteger": |
| 358 | +props[name] = optionalPositiveIntegerSchema(); |
354 | 359 | break; |
355 | 360 | case "boolean": |
356 | 361 | props[name] = Type.Optional(Type.Boolean()); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -191,6 +191,24 @@ describe("runMessageAction poll handling", () => {
|
191 | 191 | expect(call?.ctx?.params?.threadId).toBe("42"); |
192 | 192 | }); |
193 | 193 | |
| 194 | +it.each([0, -1, 1.5, "1.5", "soon"])( |
| 195 | +"rejects invalid pollDurationHours value %s", |
| 196 | +async (pollDurationHours) => { |
| 197 | +await expect( |
| 198 | +runPollAction({ |
| 199 | +cfg: pollerConfig, |
| 200 | +actionParams: { |
| 201 | +channel: "poller", |
| 202 | +target: "poller:123", |
| 203 | +pollQuestion: "Lunch?", |
| 204 | +pollOption: ["Pizza", "Sushi"], |
| 205 | + pollDurationHours, |
| 206 | +}, |
| 207 | +}), |
| 208 | +).rejects.toThrow(/pollDurationHours must be a positive integer/i); |
| 209 | +}, |
| 210 | +); |
| 211 | + |
194 | 212 | it("passes inbound event kind to poll execution", async () => { |
195 | 213 | const call = await runPollAction({ |
196 | 214 | cfg: pollerConfig, |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -2,7 +2,7 @@ import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-pay
|
2 | 2 | import { resolveSessionAgentId } from "../../agents/agent-scope.js"; |
3 | 3 | import type { AgentToolResult } from "../../agents/runtime/index.js"; |
4 | 4 | import { |
5 | | -readNumberParam, |
| 5 | +readPositiveIntegerParam, |
6 | 6 | readStringArrayParam, |
7 | 7 | readStringParam, |
8 | 8 | } from "../../agents/tools/common.js"; |
@@ -1174,9 +1174,8 @@ async function handlePollAction(ctx: ResolvedActionContext): Promise<MessageActi
|
1174 | 1174 | throw new Error("pollOption requires at least two values"); |
1175 | 1175 | } |
1176 | 1176 | const allowMultiselect = readBooleanParam(params, "pollMulti") ?? false; |
1177 | | -const durationHours = readNumberParam(params, "pollDurationHours", { |
1178 | | -integer: true, |
1179 | | -strict: true, |
| 1177 | +const durationHours = readPositiveIntegerParam(params, "pollDurationHours", { |
| 1178 | +message: "pollDurationHours must be a positive integer", |
1180 | 1179 | }); |
1181 | 1180 | |
1182 | 1181 | return { |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
1 | 1 | import { readSnakeCaseParamRaw } from "./param-key.js"; |
2 | 2 | import { normalizeLowercaseStringOrEmpty } from "./shared/string-coerce.js"; |
3 | 3 | |
4 | | -type PollCreationParamKind = "string" | "stringArray" | "number" | "boolean"; |
| 4 | +type PollCreationParamKind = "string" | "stringArray" | "positiveInteger" | "boolean"; |
5 | 5 | |
6 | 6 | type PollCreationParamDef = { |
7 | 7 | kind: PollCreationParamKind; |
@@ -10,7 +10,7 @@ type PollCreationParamDef = {
|
10 | 10 | const SHARED_POLL_CREATION_PARAM_DEFS = { |
11 | 11 | pollQuestion: { kind: "string" }, |
12 | 12 | pollOption: { kind: "stringArray" }, |
13 | | -pollDurationHours: { kind: "number" }, |
| 13 | +pollDurationHours: { kind: "positiveInteger" }, |
14 | 14 | pollMulti: { kind: "boolean" }, |
15 | 15 | } satisfies Record<string, PollCreationParamDef>; |
16 | 16 | |
@@ -91,7 +91,7 @@ export function hasPollCreationParams(params: Record<string, unknown>): boolean
|
91 | 91 | return true; |
92 | 92 | } |
93 | 93 | } |
94 | | -if (def.kind === "number") { |
| 94 | +if (def.kind === "positiveInteger") { |
95 | 95 | // Treat zero-valued numeric defaults as unset, but preserve any non-zero |
96 | 96 | // numeric value as explicit poll intent so invalid durations still hit |
97 | 97 | // the poll-only validation path. |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。