// Scheduler driver: reads {"rows": [...]} from stdin, runs scheduler_tick over // an in-memory schedules table with a stub runner and prints the final table. #include "../src/agent/scheduler.h" #include "../src/llm/py.h" #include #include #include using namespace detz; using json::Value; namespace { struct Row { int64_t id; std::string name, prompt, profile; int64_t every_sec; std::string next_run; int64_t timeout; std::string status, last_status, last_result, updated; }; class MemSchedulerDb : public agent::SchedulerDbPort { public: std::vector rows; std::vector select_due(const std::string& now_iso) override { std::vector out; for (const auto& r : rows) { if (r.status == "pending" && r.next_run <= now_iso) { out.push_back({r.id, r.prompt, r.profile, r.every_sec, r.next_run, r.timeout, r.last_status, r.updated}); } } std::stable_sort(out.begin(), out.end(), [](const auto& a, const auto& b) { return a.next_run < b.next_run; }); return out; } bool claim_every(int64_t id, const std::string& new_next, const std::string& stamp, const std::string& expected) override { for (auto& r : rows) { if (r.id == id && r.status == "pending" && r.next_run == expected) { r.next_run = new_next; r.updated = stamp; r.last_status = "running"; return true; } } return false; } bool claim_once(int64_t id, const std::string& stamp) override { for (auto& r : rows) { if (r.id == id && r.status == "pending") { r.status = "done"; r.updated = stamp; return true; } } return false; } std::string unlock_prompt(const std::string& prompt) override { if (prompt.rfind("SEALED:", 0) == 0) { if (prompt == "SEALED:bad") { throw agent::SealFailure("cannot unlock"); } return prompt.substr(7); } return prompt; } void release_every(int64_t id, const std::string& next_run, const std::string& stamp, const std::string& last_status) override { for (auto& r : rows) { if (r.id == id) { r.next_run = next_run; r.updated = stamp; r.last_status = last_status; } } } void release_once(int64_t id, const std::string& stamp) override { for (auto& r : rows) { if (r.id == id) { r.status = "pending"; r.updated = stamp; } } } void note_result(int64_t id, const std::string& status, const std::string& result) override { for (auto& r : rows) { if (r.id == id) { r.last_status = status; r.last_result = result; r.updated = py::utc_now_iso(); } } } }; } // namespace int main() { std::string text((std::istreambuf_iterator(std::cin)), std::istreambuf_iterator()); Value in = Value::loads(text); MemSchedulerDb db; for (const auto& r : in.get("rows").items()) { db.rows.push_back({r.get("id").as_int(), r.get("name").str(), r.get("prompt").str(), r.get("profile").str(), r.get("every_sec").as_int(), r.get("next_run").str(), r.get("timeout").as_int(), "pending", r.get("last_status").str(), "", r.get("updated").str()}); } agent::AgentConfig cfg; int fired = 0; agent::Runner runner = [](const std::string& task, const std::string&, double) { return "ran: " + task; }; fired = agent::scheduler_tick(cfg, db, runner); // let the watcher threads record their outcome for (int i = 0; i < 100 && agent::running_agents() > 0; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(20)); } std::this_thread::sleep_for(std::chrono::milliseconds(300)); Value out = Value::object(); out.set("fired", fired); Value rows = Value::array(); for (const auto& r : db.rows) { Value o = Value::object(); o.set("id", r.id); o.set("status", r.status); o.set("next_run", r.next_run); o.set("last_status", r.last_status); o.set("last_result", r.last_result); rows.push(o); } out.set("rows", rows); std::cout << out.dumps() << "\n"; return 0; }