diff --git a/include/network/hmas_coordinator_service.hpp b/include/network/hmas_coordinator_service.hpp index b192e38b..d3a1b95b 100644 --- a/include/network/hmas_coordinator_service.hpp +++ b/include/network/hmas_coordinator_service.hpp @@ -1,5 +1,6 @@ #pragma once +#include "core/i_message_router.hpp" #include "network/service_registry.hpp" #include "network/task_phase_utils.hpp" #include "network/task_router.hpp" @@ -104,6 +105,11 @@ class HMASCoordinatorServiceImpl final : public hmas::HMASCoordinator::Service { /// @return Number of tasks cleaned up int32_t cleanupOldTasks(int64_t age_threshold_ms = 3600000); // Default: 1 hour + /// Set the message router used to notify agents of task lifecycle events. + /// When set, CancelTask sends a CANCEL_TASK KeystoneMessage to the assigned agent. + /// @param router Pointer to IMessageRouter (must outlive this object; may be nullptr) + void setMessageRouter(core::IMessageRouter* router); + private: /// Generate unique task ID std::string generateTaskId() const; @@ -111,6 +117,10 @@ class HMASCoordinatorServiceImpl final : public hmas::HMASCoordinator::Service { std::shared_ptr registry_; std::shared_ptr router_; + /// Optional message router for agent notifications (e.g., cancellation). + /// Not owned by this object. + core::IMessageRouter* message_router_{nullptr}; + /// Active tasks map: task_id -> TaskState std::unordered_map active_tasks_; diff --git a/include/network/task_phase_utils.hpp b/include/network/task_phase_utils.hpp index 543fbc6d..85847361 100644 --- a/include/network/task_phase_utils.hpp +++ b/include/network/task_phase_utils.hpp @@ -74,8 +74,18 @@ inline hmas::TaskPhase stringToPhase(const std::string& phase_str) { return hmas::TASK_PHASE_CANCELLED; if (phase_str == "ERROR") return hmas::TASK_PHASE_ERROR; - + if (phase_str == "UNSPECIFIED") + return hmas::TASK_PHASE_UNSPECIFIED; return hmas::TASK_PHASE_UNSPECIFIED; } +/// Returns true if the string maps to a known (non-UNSPECIFIED) TaskPhase value. +inline bool isKnownPhaseString(const std::string& phase_str) { + if (phase_str.empty()) + return false; + if (phase_str == "UNSPECIFIED") + return false; + return stringToPhase(phase_str) != hmas::TASK_PHASE_UNSPECIFIED; +} + } // namespace keystone::network diff --git a/src/network/yaml_parser.cpp b/src/network/yaml_parser.cpp index cf64bca8..35cc5902 100644 --- a/src/network/yaml_parser.cpp +++ b/src/network/yaml_parser.cpp @@ -1,7 +1,10 @@ #include "network/yaml_parser.hpp" +#include "network/task_phase_utils.hpp" + #include #include +#include namespace keystone::network { @@ -53,6 +56,8 @@ std::optional YamlParser::parseTaskSpec(const YAML::Node& return spec; } catch (const YAML::Exception& e) { return std::nullopt; + } catch (const std::exception& e) { + return std::nullopt; } } @@ -297,7 +302,14 @@ TaskStatus YamlParser::parseStatus(const YAML::Node& node) { TaskStatus status; if (node["phase"]) { - status.phase = node["phase"].as(); + const std::string phase_str = node["phase"].as(); + // Validate phase string against the known TaskPhase enum values. + // An empty string defaults to "PENDING" (struct default); any non-empty + // string that does not map to a valid phase is rejected. + if (!phase_str.empty() && !isKnownPhaseString(phase_str)) { + throw std::runtime_error("Unknown task phase string: '" + phase_str + "'"); + } + status.phase = phase_str; } if (node["startTime"]) { status.start_time = node["startTime"].as();