PKU harness

定时任务与通知

提醒、待办、上课提醒和定时检查都由同一个定时任务引擎驱动。每个任务由一个触发器(单次,或有限次的重复)和一个动作(通知、检查或助手)组成,准时到分钟触发。

PKU harness 里所有和时间有关的功能都由同一个定时任务引擎(scheduler)处理。引擎里的每一项叫作一个任务(job)。例如,一条提醒、某个待办的提醒、上课提醒,以及「每天早上查一下我的校园卡余额」,都是任务。

你可以在「日程」标签页里创建任务,也可以直接让助手创建。插件通过 services.scheduler 创建任务。

每个任务由两部分组成:触发器(trigger)决定它什么时候运行,动作(action)决定它运行时做什么。

任务的结构

type Job = {
  id: string;
  title: string;
  trigger: Trigger;          // 何时触发
  action: Action;            // 做什么
  enabled: boolean;
  source: 'user' | 'agent' | 'plugin';
  runs: number;              // 已触发(或已错过)的次数
  lastRunAt: number | null;
  nextRunAt: number | null;  // 系列结束后为 null
  // check / agent 任务还会记录 lastValue、lastError、history(最近 20 次)、failures …
  meta?: Record<string, unknown>;  // 指回所属对象,例如 { todoId }
  createdAt: number;
  updatedAt: number;
};

触发器

触发器有两种,所有时间都按手机的本地时间计算。

单次触发器 { type: 'at' } 只有一个字段 at,是一个毫秒时间戳。它适用于「两天后」「明天 8:00」这样的提醒。

重复触发器 { type: 'every' } 适用于「每天早上 8 点」「每 3 天一次,共 16 次后停止」「每周一、三、五 07:30,直到学期结束」这样的提醒。它有以下字段。

字段含义
unit重复的单位:minute、hour、day 或 week
interval每隔几个单位重复一次,取值 1–1000
times?一天中的时刻,格式为 "HH:MM",用于 day 和 week
weekdays?星期几,0 表示周日,6 表示周六,用于 week
startAt开始时间
count?最多触发几次
until?截止日期(含当天)

重复触发器的计算方法如下。一个重复系列由若干个周期组成,每个周期是一个分钟或小时步长、一天,或者从周一开始的一周。每个周期里有若干个时间点,由时刻和星期组合而成。早于 startAt 的时间点会被跳过,剩下的依次编号为 0、1、2……,最后由 count、until 或两者共同截断。

助手和表单使用一种更容易书写的输入格式 ScheduleInput,时间写成本地时间字符串 "YYYY-MM-DD HH:mm"。例如,「每 3 天一次,09:00,共 16 次」写作:

{ "type": "repeat", "unit": "day", "interval": 3, "times": ["09:00"], "count": 16 }

toTrigger(input) 把它转换成触发器。summarize(trigger) 返回 { schedule, next, total, finished },例如 "每 3 天 09:00 · 共 16 次" 以及接下来的三个触发时间。助手可以在保存之前把这些时间告诉你,请你确认。

动作

动作有三种。

动作字段
notifytitle、body?、url?
checktool、args?、condition、title、body?、url?
agentprompt(4–1000 个字符)

notify 发送一条通知。它会提前注册为精确通知,触发时不运行任何代码。url 是点击通知后打开的 App 内路由。

check 是定时检查。它调用一个只读工具(形如 <plugin>.<tool>),读取结果中 condition.path 处的值,只有条件成立时才发送通知。通知文案里的 {value} 会被替换成读到的值。

agent 让助手定时运行一次。每次运行都新开一个只读的助手会话,不带任何聊天记录。如果助手回复 NO_REPLY,就表示这次不需要打扰你;否则,它的简短回答就是通知的内容。

check 的条件写作 { path, op, value? }。op 取以下值之一:<、<=、>、>=、==、!=、changed、always。

下面的检查在校园卡余额低于 100 元时提醒你充值:

{
  "type": "check",
  "tool": "wallet.cardBalance",
  "condition": { "path": "balance", "op": "<", "value": 100 },
  "title": "校园卡余额只剩 ¥{value},记得充值"
}

在 Android 上准时送达

notify 任务会提前注册为精确闹钟。注册的范围是未来 14 天,每个任务最多 64 个,总共最多 400 个。App 启动时、任务每次变更时、App 回到前台时,以及后台任务运行时,都会重新注册。

check 和 agent 任务在 JavaScript 里运行。App 在前台时,由计时器准时到分钟触发它们。App 在后台或已被系统关闭时,由精确闹钟唤醒一个后台任务(headless task)。这个后台任务会执行所有到期的任务,注册下一个闹钟,并移除占位通知。

准时送达需要两项权限:通知权限,以及「闹钟和提醒」这项特殊权限。Android 14 及以上版本默认不授予后者。关闭电池优化只是建议,不是必需的。

插件可以用以下函数检查和修复这些设置。

  • services.notify.readiness() 报告这三项的状态。
  • deliveryBlocker(readiness) 指出第一个阻碍准时送达的项目。
  • services.notify.fix(kind) 打开对应的系统弹窗或设置页。fix 必须在用户点击时调用。

无人值守时的限制

定时检查运行时,没有人在旁边看着。所以它的权限受到限制,每次运行都有记录,失败时会明确提醒你。

规则取值
可调用的工具只能是 read 工具
启用中的任务最多 50 个(MAX_ACTIVE_JOBS)
检查频率最多每 15 分钟一次(MIN_CHECK_GAP_MS)
助手任务的数量最多 10 个(MAX_AGENT_JOBS)
助手任务的频率最多每小时一次(MIN_AGENT_GAP_MS)
助手任务每次的步数最多 8 步(AGENT_MAX_STEPS)
迟到的运行迟到超过 2 小时(MAX_LATE_MS)记为错过
运行日志保留最近 20 次(HISTORY_LIMIT)
连续失败后提醒2 次(FAIL_ALERT_AFTER)
连续失败后停用10 次(DISABLE_AFTER)

对这些规则的补充说明如下。

  • 工具的效果在运行时会重新检查。如果授权方式为 ask,这次调用视为被拒绝。
  • 不允许创建重复的检查,也就是工具、参数和条件都相同的检查。
  • 迟到的运行记为错过,是为了避免几个小时后才弹出一条早已过时的提醒。
  • 运行日志记录计划时间和实际时间、结果、读到的值和错误。
  • 连续失败 2 次时,App 会提醒你一次,6 小时后如果仍然失败,会再提醒一次。连续失败 10 次时,App 会停用这个任务并告诉你。

写给插件作者

  • 让工具适合被检查。 这样的工具效果为 read,响应快,并且有一个稳定的数字或布尔字段可以比较,例如 balance、unread 或 available。请把这些工具列在 dataSources 里,助手才能找到它们。
  • 只在响应用户操作时创建任务, 写法是 services.scheduler.create({ …, source: 'plugin' })。请保存好任务 id(放在 meta 或你的 kv 里),之后才能更新或删除这个任务。
  • SDK 导出了纯计算的引擎函数, 例如 occurrencesAfter、nextOccurrence、describeTrigger 和 evaluate。详见 SDK → scheduler。

本页目录