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-schedulerHow it's put together
serve --state <dir>is the tool, an MCP server over stdio, andrun --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,localor a zone's name, which it sets as the process'sTZafter 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'stoolsmust 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. Withoutlink.local/tools, a job has no list (the subagent gets every tool, as its harness gives them), andtaskrefuses one. - A call is scoped to its conversation:
listandcancelsee the jobs sent to it, the subagents it started (answerits 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 fromjobs/(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 infiring/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::FiringandFired), orInProcess, a function, for tests and a harness running it in its own process. The same tests run on both.
