Skip to content

feat(tools): add "scheduler" toolset to run instructions on a time or recurring interval#3632

Open
dwin-gharibi wants to merge 8 commits into
docker:mainfrom
dwin-gharibi:feat/scheduler-toolset
Open

feat(tools): add "scheduler" toolset to run instructions on a time or recurring interval#3632
dwin-gharibi wants to merge 8 commits into
docker:mainfrom
dwin-gharibi:feat/scheduler-toolset

Conversation

@dwin-gharibi

Copy link
Copy Markdown
Contributor

Closes #3631


Summary

Adds a new built-in toolset, scheduler, that lets an agent schedule an instruction to run once at a time, after a delay, or on a recurring interval (every:1h, or minutely/hourly/daily/weekly). When a schedule is due, the instruction is injected back into the agent loop via the runtime's recall mechanism and the agent carries out the action with its normal tools.

Tools: create_schedule, list_schedules, cancel_schedule.

Motivation

background_jobs can run work now and recall on completion, but nothing lets an agent make something happen later or repeatedly - e.g. "every 15 minutes, check if origin/main moved," or "at 09:00, post the standup summary." The scheduler fills that gap and lets docker-agent drive timed / recurring workflows during a live session.

Design - execution model

The scheduler does not execute shell/api itself. When a schedule fires it calls Runtime.Recall(ctx, <instruction>) and the agent performs the action with its own tools. This was a deliberate choice:

  • works with any tool the agent has, not just shell;
  • reuses Recall exactly as designed ("background work completed → inject a message"), the same primitive background_jobs uses;
  • keeps the shell/permission/sandbox surface with the shell tool instead of adding a second, unattended command runner.

A deterministic-execution variant (the scheduler runs a shell command itself) is noted as a possible follow-up but is intentionally out of scope here.

Lifecycle: the toolset implements tools.Startable; Stop cancels the fire loop so no goroutine outlives the session. Schedules live in memory (not persisted across restarts). Scheduling requires host recall support (Runtime.Supports(CapabilityRecall)); otherwise create_schedule returns a clear error.

What changed

File Change
pkg/tools/builtin/scheduler/schedule.go New. parseWhen — spec → first fire time + interval.
pkg/tools/builtin/scheduler/store.go New. In-memory schedule store: add / list / cancel / popDue (re-arm recurring, skip missed slots) / untilNext. Clock-injected, pure.
pkg/tools/builtin/scheduler/scheduler.go New. The ToolSet: 3 tools, Recall firing, Startable fire loop, instructions.
pkg/tools/builtin/scheduler/*_test.go New. Parser, store, tool-handler, firing, and lifecycle tests.
pkg/teamloader/toolsets/toolsets.go Register the scheduler creator.
pkg/teamloader/toolsets/catalog.go Add the scheduler catalog entry (required by the drift-guard test).
docs/configuration/tools/index.md Add scheduler to the built-in toolsets table.
docs/tools/scheduler/index.md New. Dedicated docs page.

The existing catalog drift-guard test (TestBuiltinToolsetsCatalogMatchesRegistry) forces the catalog entry: registering the type without documenting it fails the build.

Example

toolsets:
  - type: scheduler
  - type: shell
create_schedule(prompt="Run `git fetch` and report if origin/main moved", when="every:15m", name="watch-main") -> Scheduled "watch-main" (sched-1): next fire at 2026-07-13T12:15:00Z, then every 15m0s.

list_schedules -> 2 active schedule(s):
     - "watch-main" (sched-1) [every:15m] next: 2026-07-13T12:15:00Z (every 15m0s) Run `git fetch` and report if origin/main moved
     - sched-2 [at:2026-07-14T09:00:00Z] next: 2026-07-14T09:00:00Z (one-shot) Post the standup summary

# when due, the agent receives:
  ⏰ Scheduled task "watch-main" (sched-1) is due:
  Run `git fetch` and report if origin/main moved

Testing

go test ./pkg/tools/builtin/scheduler/ ./pkg/teamloader/toolsets/
  • parseWhen: all spec forms + rejections (empty, garbage, past at:, non-positive every:).
  • store: add/list ordering, cancel, one-shot removal, recurring re-arm, missed-slot skipping, untilNext clamping.
  • Tool handlers: create (+ requires recall, requires prompt, invalid spec), list, cancel, fire → Recall, Start/Stop clean shutdown.
  • TestBuiltinToolsetsCatalogMatchesRegistry passes with scheduler in both registry and catalog.

All pass; gofmt -l and go vet clean. (CI runs task lint / task test.)

Scope / non-goals

  • No cron-expression syntax and no cross-restart persistence in v1 (noted as future work).
  • Does not execute commands itself - deliberately delegates the action to the agent's tools (see Design).

@dwin-gharibi dwin-gharibi requested a review from a team as a code owner July 13, 2026 18:09
@dwin-gharibi

Copy link
Copy Markdown
Contributor Author

@Sayt-0 @aheritier

@aheritier aheritier added area/docs Documentation changes area/tools For features/issues/fixes related to the usage of built-in and MCP tools kind/feat PR adds a new feature (maps to feat:). Use on PRs only. labels Jul 13, 2026
@dwin-gharibi

Copy link
Copy Markdown
Contributor Author

Hi @Sayt-0, the linting problems are now fixed. Could you please review the PR when you have a chance? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Documentation changes area/tools For features/issues/fixes related to the usage of built-in and MCP tools kind/feat PR adds a new feature (maps to feat:). Use on PRs only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: scheduler toolset - run instructions on a time / recurring interval

2 participants