// 定时任务工具(docs/design/14-schedules.md §4):由 MCP Gateway 提供给所有会话(普通会话与工作组中的机器人) import { z } from 'zod' import { fail, ok, type BuiltinTool, type ToolResult } from '../mcp-gateway/builtin' import type { GatewayRunContext } from '../mcp-gateway/tokens' const scheduleArg = z.object({ kind: z.enum(['once', 'daily', 'weekdays', 'weekly', 'monthly', 'interval']) .describe('once:只执行一次;daily:每天;weekdays:周一到周五;weekly:每周的某几天;monthly:每月某天;interval:每 N 小时'), time: z.string().optional().describe('daily / weekdays / weekly / monthly 的执行时间,HH:mm(24 小时制)'), days: z.array(z.number().int()).optional().describe('weekly 的星期几,1=周一 … 7=周日'), day: z.number().int().optional().describe('monthly 的日期 1–31,超过当月天数时取最后一天'), at: z.string().optional().describe('once 的执行时间,YYYY-MM-DD HH:mm'), after_minutes: z.number().optional().describe('once 的另一种写法:多少分钟后执行(用户说"两小时后"等相对时间时使用)'), hours: z.number().optional().describe('interval 的间隔小时数'), }) const schemasFor = (timezone: string) => ({ schedule_create: z.object({ title: z.string().min(1).max(60).describe('简短的名称,如"销售日报"'), prompt: z.string().min(1).max(20_000).describe('到点时发给你的完整指令:要能独立执行,写清楚做什么、数据从哪里来、产出放在哪里'), schedule: scheduleArg.describe(`时间表,时间按 ${timezone} 时区`), }), schedule_list: z.object({}), schedule_update: z.object({ schedule_id: z.string().describe('定时任务 id(见 schedule_list)'), title: z.string().max(60).optional(), prompt: z.string().max(20_000).optional(), schedule: scheduleArg.describe(`时间表,时间按 ${timezone} 时区`).optional(), status: z.enum(['active', 'paused']).optional().describe('暂停或恢复'), }), schedule_delete: z.object({ schedule_id: z.string() }), }) type ToolName = keyof ReturnType const DESCRIPTIONS: Record = { schedule_create: '用户要求定期执行或在将来某个时间执行某项工作时,创建定时任务。会显示确认卡片,用户确认后生效;到点时系统把 prompt 作为一条新消息发到当前会话(工作组中发到工作组),由你执行。', schedule_list: '列出当前会话(或工作组)的定时任务。', schedule_update: '修改定时任务的名称、指令或时间表(需要用户确认),或者暂停、恢复(不需要确认)。', schedule_delete: '删除定时任务。', } export function scheduleTools(timezone: string, handle: (name: ToolName, args: Record, ctx: GatewayRunContext) => Promise): BuiltinTool[] { const schemas = schemasFor(timezone) return (Object.keys(schemas) as ToolName[]).map(name => ({ name, description: DESCRIPTIONS[name], schema: schemas[name], readOnly: name === 'schedule_list', async handler(args, ctx): Promise { try { return ok(await handle(name, args as Record, ctx)) } catch (err) { return fail((err as Error).message) } }, })) }