Preset-scoped Schedule tools
按 preset 提供的 Schedule 工具
`@deepseek-ai/dsh-schedule` registered `schedule_create`, `schedule_list`, `schedule_update`, and `schedule_delete` itself, attaching them to every live root Agent from an `agent/created` listener whose only filter was membership in `ctx.agents.roots()`. That predicate says nothing about the Agent preset, so `minimal` — composed for a capability-poor agent — carried all four schemas and paid their fixed request-conte
English
Problem
@deepseek-ai/dsh-schedule registered schedule_create, schedule_list, schedule_update, and schedule_delete itself, attaching them to every live root Agent from an agent/created listener whose only filter was membership in ctx.agents.roots(). That predicate says nothing about the Agent preset, so minimal — composed for a capability-poor agent — carried all four schemas and paid their fixed request-context token cost. Storage, delivery, and the preset mechanism were each correct; the availability decision sat in the package that owns the storage service.
Decision
@deepseek-ai/dsh-tool-schedule (packages/schedule/tool-schedule) contributes the four tools as a preset-level Consumer. It declares inject = ['tools'] and registers the definitions on ctx.tools from inside ctx.inject(['schedule'], …), so the mounting scope is the one that owns them and the registration waits for the Host Schedule service in that same scope. The shipped Web profile mounts the row in its standard, cordis, and ptc presets and omits it from minimal. Cordis effect ownership disposes the definitions with the mount, and a composition that never resolves schedule registers none of them. The Web bundle owns the schedule and ui-schedule rows the shipped composition mounts; this record owns the reminder tools' preset placement.
@deepseek-ai/dsh-schedule keeps the version-1 storage domain, the Host timer and serialized queue, Host delivery through the Session controller, the Automation page's backing reads, and the ctx.schedule interface. dsh-tool-schedule is the model-facing consumer of that interface: it checks selector and identity constraints before the service call, reads exec.agent for the Session binding, and maps failures that are not ScheduleInputError to internal_error so storage details never reach the model.
Alternatives considered
Keep registration in the Host service behind a Config flag. An exposeTools field would move a composition choice into the storage plugin, where no preset can state it, and every preset would still need an edit to change the outcome.
Register the tools whenever ctx.schedule is present. The shipped Web composition mounts the schedule Host service for the whole deployment, minimal included, so that condition restores the coupling between storage and model surface that this decision removes.
Consequences
minimalrequest headers and tool lists carry no reminder tool schema.- Each preset that mounts the row pays the four schemas' fixed token cost, and tool availability is read from the composition instead of inferred from the Host service's presence.
- A deployment can mount
dsh-schedulefor storage and delivery without granting its agents model-driven reminder management. dsh-scheduleinjects noctx.toolsand registers no model-facing tool.- A delegated child inherits the preset's four reminder schemas.
subagentCallerRefusalindsh-tool-schedulereturns{ code: 'subagent_session', message: 'A delegated subagent cannot use reminders.' }from each tool when the caller's delegation depth is above zero. The preset composition decision removes the redundant child filter; visibility does not authorize execution. - A Session a delegated child owns can never receive a delivered reminder, so
ScheduleService.createandScheduleService.updaterefuse it withsubagent_session. The refusal reads the delegation depth — the accounting the delegation cap itself enforces, carried by the persisted session header across a cold resume. The rule sits in the service, so other consumers reach it too — the Automation surface included.
Testing
packages/schedule/tool-schedule/tests/tool-schedule.spec.ts pins the four definitions and their error mapping, including refuses every tool for a delegated child caller. apps/cli/tests/web-agent-presets.e2e.ts asserts the minimal preset's tool list, and snapshots/web/minimal-preset/tool-schemas.expected.json records its tool table without reminder schemas. packages/schedule/schedule/tests/subagent-ownership.spec.ts pins the delegation-depth refusal, and scripts/optional-bundles.spec.ts asserts that both delegation rows in the three presets omit toolFilter. The Web preset e2e also checks equal parent/child tool schemas and refusal from all four reminder executors for fresh and seeded children. snapshots/web/subagent-fork-schedule records all four refusals in a seeded fork through the shipped Web composition and shares the standard preset's prompt and schema pins.
中文
Problem
@deepseek-ai/dsh-schedule 自己注册 schedule_create、schedule_list、schedule_update 和 schedule_delete,在 agent/created 监听器中把它们附加到每个 live 根 Agent,唯一的过滤条件是 ctx.agents.roots() 的成员资格。该判据不涉及 Agent preset,因此为能力克制而组合的 minimal 也携带全部四个 schema,并承担其固定的请求上下文 token 成本。存储、投递和 preset 机制各自都是正确的;可用性决策落在了拥有存储服务的包里。
Decision
@deepseek-ai/dsh-tool-schedule(packages/schedule/tool-schedule)以 preset 级 Consumer 的身份贡献这四个工具。它声明 inject = ['tools'],并在 ctx.inject(['schedule'], …) 内通过 ctx.tools 注册这些定义,因此拥有它们的是挂载它的作用域,而注册会等待同一作用域中的宿主 Schedule 服务。发布版 Web profile 在其 standard、cordis 和 ptc preset 中挂载该行,minimal 不挂载。Cordis 的 effect 所有权随挂载卸载而释放这些定义,而始终未解析出 schedule 的组合不会注册其中任何一个。Web bundle负责随发行版交付的组合挂载的 schedule 与 ui-schedule 两行;本记录负责提醒工具的 preset 归属。
@deepseek-ai/dsh-schedule 保留版本 1 storage domain、宿主定时器与串行队列、经由 Session controller 的宿主投递、自动化任务页面的读取来源,以及 ctx.schedule 接口。dsh-tool-schedule 是该接口面向模型的消费者:它在调用服务前校验选择器与身份约束,从 exec.agent 读取 Session 绑定,并把非 ScheduleInputError 的失败映射为 internal_error,使存储细节不会到达模型。
Alternatives considered
继续在宿主服务中通过 Config 开关注册。 exposeTools 这类字段会把组合选择放进存储插件,而任何 preset 都无法在那里声明它,且每个 preset 仍要各自编辑才能改变结果。
只要 ctx.schedule 存在就注册这些工具。 随发行版交付的 Web 组合为整个部署挂载 schedule 宿主服务,包括 minimal,因此该条件会恢复本决策所移除的、存储与模型界面之间的耦合。
Consequences
minimal的请求头与工具列表不含任何提醒工具 schema。- 挂载该行的每个 preset 承担四个 schema 的固定 token 成本,工具可用性取自组合,而不是由宿主服务是否存在推断。
- 部署可以只为存储与投递挂载
dsh-schedule,而不授予其 Agent 由模型驱动的提醒管理能力。 dsh-schedule不注入ctx.tools,也不注册任何面向模型的工具。- 被委派的子 agent 继承 preset 的四个提醒 schema。调用方委派深度大于零时,
dsh-tool-schedule中的subagentCallerRefusal从每个工具返回{ code: 'subagent_session', message: 'A delegated subagent cannot use reminders.' }。preset 组装决策移除了重复的子级过滤;可见性不授予执行权限。 - 被委派子 agent 拥有的 Session 永远收不到投递的提醒,因此
ScheduleService.create与ScheduleService.update会以subagent_session拒绝它。该拒绝读取委派深度——委派封顶本身执行的记账,由持久化 session header 承载并跨冷恢复保留。这条规则在服务层,因此其它消费方(包括自动化任务页面)也会命中。
Testing
packages/schedule/tool-schedule/tests/tool-schedule.spec.ts 固定这四个定义及其错误映射,包括 refuses every tool for a delegated child caller。apps/cli/tests/web-agent-presets.e2e.ts 断言 minimal preset 的工具列表,snapshots/web/minimal-preset/tool-schemas.expected.json 记录其不含提醒工具 schema 的工具表。packages/schedule/schedule/tests/subagent-ownership.spec.ts 固定委派深度拒绝,scripts/optional-bundles.spec.ts 断言三个 preset 的两行委派行都不含 toolFilter。Web preset e2e 还检查父子工具 schema 相等,且四个提醒执行器对新建和带种子的子级均拒绝执行。snapshots/web/subagent-fork-schedule 通过出货 Web 组合记录带种子 fork 中的四次拒绝,并共享 standard preset 的提示和 schema 预期。