100 const std::filesystem::path& project_dir);
123 const std::filesystem::path&
socket_path()
const {
return socket_path_; }
167 std::string dispatch(
const std::string& request,
int client_fd);
201 bool begin_turn_wait(
long timeout_ms,
long* waited_ms_out,
202 const std::string& session_key =
"");
210 void end_turn_wait(
const std::string& session_key =
"");
219 void fail_task_engine_busy(
const std::string& task_id,
long waited_ms);
230 void commit_async_final_state(
const std::string& task_id,
231 const std::string& status,
232 const std::string& phase,
233 const std::string& text);
241 size_t turn_queue_depth()
const;
249 bool turn_in_flight()
const;
258 nlohmann::json handle_ask_status(
const nlohmann::json& args);
281 void attach_phase_observer(
const std::string& task_id);
288 void detach_phase_observer();
306 void run_async_ask(
const std::string& prompt,
307 const std::string& task_id,
309 const std::string& session_key =
"",
310 const std::string& tier =
"");
332 void write_sentinel(
const std::string& task_id,
333 const std::string& status);
350 std::filesystem::path async_sentinel_dir()
const;
365 void set_async_sentinel_root(
const std::filesystem::path& root);
372 void cleanup_expired_tasks();
387 std::string status =
"queued";
388 std::string phase =
"queued";
390 std::chrono::steady_clock::time_point
created;
418 return active_task_id_;
427 void subscribe(
int fd);
435 void unsubscribe(
int fd);
448 void broadcast_notification(
const nlohmann::json& notif);
463 void notify_client(
int fd,
const nlohmann::json& notif);
472 std::lock_guard<std::mutex> lock(subscribers_mutex_);
473 return subscribers_.size();
489 void update_task_phase(
const std::string& task_id,
490 const std::string& status,
491 const std::string& phase);
507 return observer_gen_.load() != attached_gen_;
524 void serve_client(
int client_fd);
537 void reap_finished_clients_locked();
546 void reap_finished_async_locked();
556 void join_async_workers();
574 uint64_t next_ticket = 0;
575 uint64_t now_serving = 0;
580 std::set<uint64_t> abandoned;
582 mutable std::mutex turn_mutex_;
583 std::condition_variable turn_cv_;
584 std::map<std::string, TurnLane> lanes_;
593 std::string lane_for(
const std::string& session_key)
const;
605 void async_worker(
const std::string& prompt,
const std::string& task_id,
606 int client_fd,
const std::string& session_key,
607 const std::string& tier);
608 std::filesystem::path socket_path_;
609 std::string bound_canonical_;
611 std::atomic<bool> running_{
false};
612 std::thread accept_thread_;
622 struct ClientThread {
628 std::atomic<bool> finished{
false};
631 std::vector<std::unique_ptr<ClientThread>> client_threads_;
636 std::atomic<bool> finished{
false};
639 std::vector<std::unique_ptr<AsyncWorker>> async_workers_;
640 std::mutex async_workers_mutex_;
641 mutable std::mutex client_threads_mutex_;
644 std::unordered_map<std::string, AsyncTask> tasks_;
651 std::unordered_set<int> subscribers_;
652 mutable std::mutex subscribers_mutex_;
659 std::string active_task_id_;
665 std::atomic<uint64_t> observer_gen_{0};
670 uint64_t attached_gen_ = 0;
675 std::filesystem::path async_sentinel_root_override_;