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 <id> 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
This commit is contained in:
zhanggttry
2026-05-04 16:03:27 +08:00
committed by clawsweeper
parent a17d4371d1
commit f9b2fcc33a
6 changed files with 26 additions and 4 deletions

View File

@@ -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

View File

@@ -45,12 +45,17 @@ export function registerCronListCommand(cron: Command) {
.command("list")
.description("List cron jobs")
.option("--all", "Include disabled jobs", false)
.option("--agent <id>", "Filter by agent id")
.option("--json", "Output JSON", false)
.action(async (opts) => {
try {
const res = await callGatewayFromCli("cron.list", opts, {
const listParams: Record<string, unknown> = {
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;

View File

@@ -12,6 +12,7 @@ export type CronListPageOptions = {
enabled?: CronJobsEnabledFilter;
sortBy?: CronJobsSortBy;
sortDir?: CronSortDir;
agentId?: string;
};
export type CronListPageResult<TJobs extends readonly CronJob[] = CronJob[]> = {

View File

@@ -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;
}

View File

@@ -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 },
);

View File

@@ -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(),