From f9b2fcc33af07477a9eac082e469655e445146a8 Mon Sep 17 00:00:00 2001 From: zhanggttry Date: Mon, 4 May 2026 16:03:27 +0800 Subject: [PATCH] feat(cron): add agentId filtering to cron list Adds optional agentId parameter to cron list action, allowing agents to filter cron jobs by their own agentId. This reduces noise in multi-agent setups where each agent sees all jobs across all agents. Changes: - Protocol schema: add optional agentId to CronListParamsSchema - Service types: add agentId to CronListPageOptions - Service ops: filter by agentId when provided - Gateway handler: pass through agentId param - Agent tool: auto-fill agentId from session context (like cron add) - CLI: add --agent option to 'openclaw cron list' When called from an agent session without explicit agentId, the tool auto-fills it from the calling agent's session context. When called from CLI without --agent, all jobs are shown (backward compatible). Closes #77118 --- src/agents/tools/cron-tool.ts | 14 ++++++++++++-- src/cli/cron-cli/register.cron-add.ts | 9 +++++++-- src/cron/service/list-page-types.ts | 1 + src/cron/service/ops.ts | 3 +++ src/gateway/protocol/schema/cron.ts | 1 + src/gateway/server-methods/cron.ts | 2 ++ 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/agents/tools/cron-tool.ts b/src/agents/tools/cron-tool.ts index b0ed6a05a36..b0ddbe65fb6 100644 --- a/src/agents/tools/cron-tool.ts +++ b/src/agents/tools/cron-tool.ts @@ -307,6 +307,7 @@ export const CronToolSchema = Type.Object( contextMessages: Type.Optional( Type.Number({ minimum: 0, maximum: REMINDER_CONTEXT_MESSAGES_MAX }), ), + agentId: Type.Optional(Type.String({ description: "Filter by agent id (list action)" })), }, { additionalProperties: true }, ); @@ -570,7 +571,7 @@ Main-session cron jobs enqueue system events for heartbeat handling. Isolated cr ACTIONS: - status: Check cron scheduler status -- list: List jobs (use includeDisabled:true to include disabled) +- list: List jobs (use includeDisabled:true to include disabled; agentId filters by agent, auto-filled from session) - add: Create job (requires job object, see schema below) - update: Modify job (requires jobId + patch object) - remove: Delete job (requires jobId) @@ -653,12 +654,21 @@ Use jobId as the canonical identifier; id is accepted for compatibility. Use con switch (action) { case "status": return jsonResult(await callGateway("cron.status", gatewayOpts, {})); - case "list": + case "list": { + const cfg = getRuntimeConfig(); + const listAgentId = + typeof params.agentId === "string" && params.agentId.trim() + ? params.agentId.trim() + : opts?.agentSessionKey + ? resolveSessionAgentId({ sessionKey: opts.agentSessionKey, config: cfg }) + : undefined; return jsonResult( await callGateway("cron.list", gatewayOpts, { includeDisabled: Boolean(params.includeDisabled), + agentId: listAgentId, }), ); + } case "add": { // Flat-params recovery: non-frontier models (e.g. Grok) sometimes flatten // job properties to the top level alongside `action` instead of nesting diff --git a/src/cli/cron-cli/register.cron-add.ts b/src/cli/cron-cli/register.cron-add.ts index fa3772c211f..be1cd74645f 100644 --- a/src/cli/cron-cli/register.cron-add.ts +++ b/src/cli/cron-cli/register.cron-add.ts @@ -45,12 +45,17 @@ export function registerCronListCommand(cron: Command) { .command("list") .description("List cron jobs") .option("--all", "Include disabled jobs", false) + .option("--agent ", "Filter by agent id") .option("--json", "Output JSON", false) .action(async (opts) => { try { - const res = await callGatewayFromCli("cron.list", opts, { + const listParams: Record = { includeDisabled: Boolean(opts.all), - }); + }; + if (opts.agent) { + listParams.agentId = opts.agent; + } + const res = await callGatewayFromCli("cron.list", opts, listParams); if (opts.json) { printCronJson(res); return; diff --git a/src/cron/service/list-page-types.ts b/src/cron/service/list-page-types.ts index da76033c51d..07ffd58ea45 100644 --- a/src/cron/service/list-page-types.ts +++ b/src/cron/service/list-page-types.ts @@ -12,6 +12,7 @@ export type CronListPageOptions = { enabled?: CronJobsEnabledFilter; sortBy?: CronJobsSortBy; sortDir?: CronSortDir; + agentId?: string; }; export type CronListPageResult = { diff --git a/src/cron/service/ops.ts b/src/cron/service/ops.ts index 0fdd3f23151..e2645b9d802 100644 --- a/src/cron/service/ops.ts +++ b/src/cron/service/ops.ts @@ -287,6 +287,9 @@ export async function listPage(state: CronServiceState, opts?: CronListPageOptio if (enabledFilter === "disabled" && isJobEnabled(job)) { return false; } + if (opts?.agentId && job.agentId !== opts.agentId) { + return false; + } if (!query) { return true; } diff --git a/src/gateway/protocol/schema/cron.ts b/src/gateway/protocol/schema/cron.ts index c985e9fcbac..816e82a5d5c 100644 --- a/src/gateway/protocol/schema/cron.ts +++ b/src/gateway/protocol/schema/cron.ts @@ -343,6 +343,7 @@ export const CronListParamsSchema = Type.Object( enabled: Type.Optional(CronJobsEnabledFilterSchema), sortBy: Type.Optional(CronJobsSortBySchema), sortDir: Type.Optional(CronSortDirSchema), + agentId: Type.Optional(NonEmptyString), }, { additionalProperties: false }, ); diff --git a/src/gateway/server-methods/cron.ts b/src/gateway/server-methods/cron.ts index eddaa1db12c..5893a647caa 100644 --- a/src/gateway/server-methods/cron.ts +++ b/src/gateway/server-methods/cron.ts @@ -199,6 +199,7 @@ export const cronHandlers: GatewayRequestHandlers = { enabled?: "all" | "enabled" | "disabled"; sortBy?: "nextRunAtMs" | "updatedAtMs" | "name"; sortDir?: "asc" | "desc"; + agentId?: string; }; const page = await context.cron.listPage({ includeDisabled: p.includeDisabled, @@ -208,6 +209,7 @@ export const cronHandlers: GatewayRequestHandlers = { enabled: p.enabled, sortBy: p.sortBy, sortDir: p.sortDir, + agentId: p.agentId, }); const deliveryPreviews = await resolveCronDeliveryPreviews({ cfg: context.getRuntimeConfig(),