@sema-agent/core
Version:
Stateless, task-oriented AI agent core
66 lines (65 loc) • 5.42 kB
TypeScript
/**
* design/105 — Scheduler 工具壳(CronCreate / CronDelete / CronList + ScheduleWakeup)。模型可调,把未来意图交给注入的
* daemon backend(`env` 实现 {@link SchedulerCapability})。**core 薄壳**:工具只做 ① cron expr 白名单校验
* ② 组装 `intent`(模型面) + `SchedulerContext`(Runner-held,闭包捕获 principal/scope/sessionId) ③ 调
* `env.schedule/cancel/list`。持久化/触发/起新 task 全在 daemon。
* design/136 §2.2(BREAKING 去超集批):`Sleep` 工具已撤除(CC 无此工具;等待抽象=Monitor/后台通知,定时再醒
* =CronCreate delay/at——纯语法糖零缺口)。旧名调用=通用响亮 roster miss(RB-476-A 墓碑已清);daemon 已排的 `label:"sleep"`
* delayed intent 照常触发(daemon 只存 prompt+when,零迁移)。
*
* [c209-D2] 同名工具消歧(durable 分层激活批 2026-07-16,防后人混淆):**我方 CronCreate = 持久 daemon 形**
* (对标上游产品 /schedule 的云端持久语义);**上游同名工具是会话级易失体**({cron: 5字段串, prompt,
* recurring: bool, durable: 死字段}——会话结束即消失);壳仓 vendored 的 ScheduleCronTool 是上游原生实现,
* 与本工具无关。两形调用在此汇流:上游生态 skill 平移过来的 CC 形调用(`cron` 字符串,`durable` 缺省 false)
* 落 **session 生存期**(会话终结随会话 reap,绝不落成永生任务);我方 `schedule` 对象形保持持久语义零回归,
* `durable: true` 显式亦持久。session 生存期需要 backend 能力位(supportsSessionLifetime),缺失时诚实拒绝
* 引导 `durable: true`——绝不静默升级语义。
*
* 挂载:prepare-task 在 `hasScheduler(executionEnv)` 时调 {@link createSchedulerTools}(需 Runner-held 上下文,故不在
* `createHandsToolkit` —— 与 present_plan/AskUserQuestion 同:这些工具都在 prepare-task 单独挂,那里有 spec.principal)。
*/
import type { AgentTool, ExecutionEnv } from "../internal/harness-types.js";
export declare const SCHEDULE_WAKEUP_TOOL_NAME = "ScheduleWakeup";
/** CronCreate-based autonomous-loop 哨兵(CC `Ldt`)。loop runtime(部署侧)在 fire 时解析回 autonomous-loop 指令。 */
export declare const AUTONOMOUS_LOOP_SENTINEL = "<<autonomous-loop>>";
/** ScheduleWakeup(dynamic-pacing)哨兵(CC `ePe`)——ScheduleWakeup 一律用 `-dynamic` 变体。 */
export declare const AUTONOMOUS_LOOP_DYNAMIC_SENTINEL = "<<autonomous-loop-dynamic>>";
/** Runner-held 上下文 + run-stop 出口,工具工厂闭包捕获(模型触不到)。 */
export interface SchedulerToolContext {
/**
* **会话腿**的越界隔离键(Runner 填 sessionId ?? principal ?? taskId)。durable 腿不用这条链——它的键由
* {@link durableScopeOf} 从 principal 独立铸(ruled 2026-08-03,见该函数注释)。
*/
scope: string;
principal?: string;
sessionId?: string;
taskConfig?: unknown;
/** `ctx.requestStopAfterTurn`(干净结束本 run 的 runner 原语出口)。design/136 §2.2:Sleep 撤除后本槽位
* 保留(human-suspend saga 等共用同一 runner 原语;prepare-task 仍注入)——当前 scheduler 三件不消费。 */
requestStopAfterTurn?: () => void;
}
/**
* cron 深校验(CC CronCreateTool.validateInput parity):arity(RB-438 D4)+ 数值域(如 `61 * * * *` 分钟越域)
* + 永不触发(如 `31 2 30 2 *` 二月 30 日)。返回给模型自纠的错误句;合法返回 null。
*
* RB-438 D4 — **只收 5 字段**。带秒的 6 段此前收单成功并回执「Scheduled.」,而兑现腿(常驻 daemon)按标准
* 5 字段解析:6 段记录算不出 next-run,首次 tick 即被当作坏记录丢弃 ⇒ 任务永不触发,且没有任何回执说过它
* 没排上。收单 arity 必须等于开火 arity,差额只能用响亮拒绝补,不能用静默丢弃补。seam 上没有「秒粒度」
* 能力位可判({@link SchedulerCapability} 只有 supported / 上下限 / 两个 session 位),故取保守形:一律拒。
* 将来若真出现支持秒的 backend,加一个 additive 能力位在此开口(缺省仍拒),不要悄悄放行。
*
* 永不触发判定:标准 cron OR 语义(CC utils/cron.ts:150-158)—— dom 与 dow **都**受限时任一匹配即触发,而任何受限
* dow 每周都出现 → 必触发。所以唯一永不触发形态 = dom 受限 + dow 通配 + dom 值全部超出所有排程月份的天数上限。
*/
export declare function cronScheduleError(expr: string): string | null;
/**
* cron → 人话(CC utils/cron.ts cronToHuman:218-308 类比,收窄到常见形态):"star/N * * * *"→"every N minutes"、
* "M star/N * * *"→"every N hours"、`M H * * *`→"daily at H:MM"、`M H * * D`→"weekly on <day> at H:MM"、
* `M H * * 1-5`→"weekdays at H:MM";其余原样返回(调用方以 `human !== expr` 判断是否可译)。
* 收 backend 的 `cron <expr>` 显示串(见 {@link cronExprFromSummary});6 段带秒不译(D4 已在收单面拒收)。
*/
export declare function cronToHuman(expr: string): string;
/**
* 构建三个 scheduler 工具(仅当 `env` 暴露 {@link SchedulerCapability} 且 `supported:true`)。否则返回 `[]`(INERT)。
*/
export declare function createSchedulerTools(env: ExecutionEnv, ctx: SchedulerToolContext): AgentTool[];