Developing

lnk-link-scheduler is Link Scheduler: subagents and schedules for any harness that reads who a message is from. How to use it: Link Scheduler.

Build and test

From link-tools, with link-core beside it:

cargo build -p link-plugin-link-scheduler
cargo test -p link-plugin-link-scheduler

How it's put together

  • serve --state <dir> is the tool, an MCP server over stdio, and run --state <dir> --way-in <socket> the runner; both take the caps (--per-day, --subagents, --hops, --late) and the time zone its times are in (--zone, local or a zone's name, which it sets as the process's TZ after checking the machine's zone database has it), and the runner how long it waits on a firing (--timeout), which Link reads from the agent's spec ([tool.scheduler] firing), so no number is the program's own.
  • A call says which conversation it's made in, and how many messages led to it, in its _meta (link.local/thread, link.local/hops), as Link Harness sends them, never in arguments the model writes. A harness that keeps a conversation's tools says them too (link.local/tools, their names): task's tools must be among them, and left out, the subagent gets them all, so a narrowed conversation's subagent is never wider. The job keeps the list (tools), and its firing carries it to the bridge, which sends it with the task (Link-Tools); said as anything but a list of names, it's read as none. Without link.local/tools, a job has no list (the subagent gets every tool, as its harness gives them), and task refuses one.
  • A call is scoped to its conversation: list and cancel see the jobs sent to it, the subagents it started (answer its thread), and theirs.
  • The store is a folder: jobs/<id>.json, each written aside and moved in place, so the tool and the runner never see half of one; firing/<id>.json, a job the runner took to fire, moved there from jobs/ (a cancel meanwhile wins the move) and back with its next time or its next try once the firing ends, or removed; day.json, the day's count, the day in its time zone. A subagent is at work while its task is in either folder. At its start, the runner moves what's in firing/ back, each counted as a try.
  • The runner looks at every job when the soonest is due, a minute at most, and else only at jobs/'s modified time, once a second.
  • The runner fires through a driver: WayIn, the agent's bridge's way in (a Unix socket, one JSON line each way, link_plugin::channel::Firing and Fired), or InProcess, a function, for tests and a harness running it in its own process. The same tests run on both.