/** * design/105 — Scheduler seam(自唤醒/自调度)。「手」腿(`ExecutionEnv`)的**跨-task** 未来意图能力:一个 run 里的模型 * 把「未来要做的活」(cron 排程 / 延迟唤醒)持久化交给一个**常驻 backend**(TOC = shell daemon),由它在未来重起一个 * **新** task。 * * 🔴 与 {@link import("./background-shell.js").BackgroundShellCapability} 的根本区别(design/105 §3.1 D1): * background-shell 的进程 ⊆ task(在每条退出路径 dispose,不跨 suspend);scheduler 的意图必须 **SURVIVE task 结束** * (schedule 的全部意义就是当前 task 结束后 daemon 还能重起)——镜像反转,**绝不 dispose-on-exit**。 * * core 薄壳裁定:core 只出接口 + 工具壳 + `hasScheduler` 门控;**持久化存储 / cron 时钟 / idle 触发 / 起新 task 全在 * backend(daemon)**——core 是 request-driven 不常驻,绝不假装调度器。NodeExecutionEnv 不自带 daemon(不同于 * background-shell 自带 spawn),而是**委托一个注入的 backend**(飞轮的 shell daemon 实现本接口)。 */ import type { ExecutionEnv, Result } from "../internal/harness.js"; /** 一个已排未来意图的句柄。**backend-local、durable(daemon 持久化、重启存活)、对调用方不透明** —— 绝不暴露可枚举的 raw id。 */ export type ScheduledTaskId = string & { readonly __brand: "ScheduledTaskId"; }; /** * 类型化错误码(对齐 background-shell 风格)。方法 MUST NOT throw(返回 {@link Result})。 */ export type SchedulerErrorCode = "unsupported" | "not_found" | "limit_exceeded" | "invalid_schedule" | "io"; /** scheduler 操作的类型化错误。`code` 给程序分类,`message` 给模型自纠。 */ export declare class SchedulerError extends Error { readonly code: SchedulerErrorCode; readonly cause?: unknown | undefined; constructor(code: SchedulerErrorCode, message: string, cause?: unknown | undefined); } /** * 🔴 **模型可控面(不可信)**:纯意图,**无任何身份字段** —— 防模型经自排 prompt 提权(design/62 同款红线:身份是 * Runner-held,非工具参数)。`prompt` = 未来 task 的不可信 `objective`(daemon 起 task 时经完整 prepare-task gate)。 */ export interface ScheduledIntent { /** 未来要跑的 prompt(agent 自排)。daemon 起新 task 时作 `objective`。 */ prompt: string; /** 何时触发。`cron`=重复(受 minCronIntervalSec 约束);`at`=一次性绝对时刻;`delay`=相对延迟(一次性 self re-wake 用)。 */ when: { kind: "cron"; expr: string; } | { kind: "at"; atMs: number; } | { kind: "delay"; delaySec: number; }; /** 可选短标签;与 `(scope, when)` 一起做 upsert 去重键(防工具重试产生重复 intent)。 */ label?: string; /** * 执行形态(CC ScheduleWakeup 对齐批 2026-07-11;缺省 `"task"` = 既有行为,全向后兼容)。 * - `"task"`:daemon 起一个**无上下文新 task**(`prompt` 作 objective,经完整 prepare-task gate)——CronCreate 语义。 * - `"session-wakeup"`:daemon **续 `ctx.sessionId` 指定的同一会话**(`prompt` 作该会话下一轮输入;同上下文、 * 同 prompt cache)——CC ScheduleWakeup 的 /loop dynamic 语义。🔴 契约:`mode:"session-wakeup"` 的 intent * MUST 随一个带 `sessionId` 的 {@link SchedulerContext} 排入(工具壳保证);daemon 触发时 MUST resume 该 * session 而非起新 task,且 principal 原样(不升权)。不支持续会话的 daemon MUST 拒(`invalid_schedule`), * 绝不静默降级成新 task(丢上下文=静默换语义)。 */ mode?: "task" | "session-wakeup"; } /** * 🔴 **Runner-held 可信面**(工具工厂闭包捕获,模型无法触及)。镜像 subagent 经 `ctx.principal` 继承的代码层强制 * (`agents/subagent.ts`)。`schedule` 的实现 MUST 把 `principal` 钉死进持久化 intent;daemon 触发时原样传 * `runTask({ principal })` —— **不升权、不省略**。 */ export interface SchedulerContext { /** 越权隔离键(cancel/list 只能操作同 scope 排的)。由 Runner 填(sessionId ?? principal ?? taskId)。 */ scope: string; /** 排程者身份;daemon 触发时原样传给 runTask(防提权)。 */ principal?: string; /** 可选:daemon 触发新 task 时复用的 session(chat-continuity;缺省=fresh-session)。⚠️ 复用需 durable sessionStore(design/105 §3.4)。 */ sessionId?: string; /** * 可选 opaque 起-task 配置(model/policy 提示)。v1 daemon 可忽略、用部署默认模板起 task(intent.prompt + 继承的 * principal 已足);未来精细化时由 daemon 解释。core 不定其 schema(最薄)。**不可由模型提供**(Runner-held)。 */ taskConfig?: unknown; } /** {@link SchedulerCapability.list} 的条目:足够 self-discovery + 防重复排,不含完整意图。 */ export interface ScheduledTaskSummary { id: ScheduledTaskId; /** 渲染用:cron expr / at 时刻 / delay 秒 的人读摘要。 */ when: string; label?: string; /** 下次触发的 epoch ms(cron/at 可算;daemon 提供)。 */ nextRunMs?: number; /** 执行形态回显({@link ScheduledIntent.mode})。ScheduleWakeup 的 stop:true 靠它定位要取消的 pending * wakeup(cron/task 意图不受 stop 影响——CC 206 D9 语义)。老 backend 不回 = undefined(工具壳以 * label 兜底过滤)。 */ mode?: "task" | "session-wakeup"; } /** * 「手」腿的跨-task 调度能力。一个具体 `ExecutionEnv` 可选附加它(交叉类型),经 {@link hasScheduler} 检测。不支持的 * env(无注入 daemon backend)= `schedulerCapabilities.supported=false`,四个 scheduler 工具自动不挂(INERT)。 */ export interface SchedulerCapability { /** 显式声明能力(像 background-shell 一样不猜)。`supported:false` → 工具不挂(design/105 §3.9 TOB / 无 daemon)。 */ readonly schedulerCapabilities: { readonly supported: boolean; /** 单 scope 存量 intent 上限(防自排炸弹;超限 `limit_exceeded`)。 */ readonly maxScheduledPerScope: number; /** delay/at 最小延迟秒(防 busy 自醒)。 */ readonly minDelaySec: number; /** cron 解析后实际触发间隔下限秒(`* * * * *` 占 1 slot 但无限触发 → 须有下限)。 */ readonly minCronIntervalSec: number; /** 最远排程地平线秒(fail-closed 有界,绝不无限未来)。 */ readonly maxScheduledHorizonSec: number; }; /** * 持久化一个未来意图。`intent` = 模型可控(无身份);`ctx` = Runner-held 可信上下文(principal/scope/...)。返回不透明 * 句柄。🔴 实现 MUST 把 `ctx.principal` 钉死进持久化 intent(daemon 触发原样传 runTask,不升权);MUST 用 cron 库 * 解析 `when.expr` **禁 shell-exec**(RCE 红线);MUST 持久化(durable,daemon 重启存活)。upsert:同 `(scope, when, label)` 覆盖(幂等)。 */ schedule(intent: ScheduledIntent, ctx: SchedulerContext): Promise>; /** 取消一个已排意图(幂等:取消不存在的是 ok no-op)。🔴 越权:`id` MUST 属本 scope;非本 scope 一律 `not_found`,不泄露存在性。 */ cancel(id: ScheduledTaskId, ctx: SchedulerContext): Promise>; /** 列**本 scope** 已排意图(self-discovery + 防重复排)。范围 = `ctx.scope`,非全局。 */ list(ctx: SchedulerContext): Promise>; } /** * 结构 + 语义检测:`env` 是否暴露 scheduler 能力且声明 `supported:true`。挂载门控用此(对齐 `hasBackgroundShell`)。 */ export declare function hasScheduler(env: ExecutionEnv): env is ExecutionEnv & SchedulerCapability; export declare function isValidCronExpr(expr: string): boolean; //# sourceMappingURL=scheduler.d.ts.map