action_core/message.rs
1use core_types::Timestamp;
2
3/// Immediate acknowledgement returned to the client after a goal is sent.
4///
5/// One of the key differences between Action and Service: before a goal is
6/// actually accepted for execution, the server first returns a `GoalAck`
7/// indicating whether it is willing to handle the goal.
8/// A rejected goal never enters execution and produces no feedback or result.
9#[derive(Clone, Debug, Eq, PartialEq)]
10pub struct GoalAck {
11 /// Whether the server accepted this goal.
12 pub accepted: bool,
13 /// Optional rejection reason or additional message.
14 pub reason: Option<String>,
15}
16
17/// Current execution status of a goal (polled by the client, updated by the server).
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum GoalStatus {
20 /// Accepted by the server; not yet executing.
21 Accepted,
22 /// Currently executing.
23 Executing,
24 /// Cancel requested; server is processing the cancellation.
25 Canceling,
26 /// Execution completed successfully.
27 Succeeded,
28 /// Execution failed.
29 Failed,
30 /// Execution was canceled.
31 Canceled,
32 /// Goal was rejected by the server before execution began.
33 Rejected,
34}
35
36impl GoalStatus {
37 /// Returns `true` if this status is a terminal state (no further transitions possible).
38 pub fn is_terminal(self) -> bool {
39 matches!(
40 self,
41 GoalStatus::Succeeded
42 | GoalStatus::Failed
43 | GoalStatus::Canceled
44 | GoalStatus::Rejected
45 )
46 }
47}
48
49/// Intermediate progress published by the server during goal execution.
50///
51/// Feedback is keyed by `ActionGoalId` so the client can distinguish updates
52/// from concurrent goals. Delivery is best-effort; the client must not assume
53/// every item is received. Only the final `ActionResult` is delivered exactly once.
54#[derive(Clone, Debug, Eq, PartialEq)]
55pub struct ActionFeedback<F> {
56 /// Timestamp when this feedback was produced.
57 pub timestamp: Timestamp,
58 /// Application-defined feedback payload.
59 pub value: F,
60}
61
62/// Terminal result returned when a goal finishes (Succeeded / Failed / Canceled).
63///
64/// Regardless of the outcome, `ActionResult` is delivered exactly once.
65/// After the client receives it, the goal's lifecycle is complete.
66#[derive(Clone, Debug, Eq, PartialEq)]
67pub struct ActionResult<R> {
68 /// Terminal status of the goal.
69 pub status: GoalStatus,
70 /// Result payload; `None` when canceled or rejected.
71 pub value: Option<R>,
72 /// Failure reason; present when `status == GoalStatus::Failed`.
73 pub error: Option<String>,
74}
75
76/// Liveness classification for an action goal session.
77#[derive(Clone, Copy, Debug, Eq, PartialEq)]
78pub enum ActionLiveness {
79 /// The goal is progressing and heartbeat is within timeout budget.
80 Active,
81 /// The goal is not timed out yet but heartbeat age exceeds the stalled threshold.
82 Stalled,
83 /// The goal exceeded heartbeat timeout budget.
84 TimedOut,
85 /// Terminal goals are considered completed.
86 Completed,
87 /// Insufficient data to classify liveness.
88 Unknown,
89}
90
91/// Health snapshot for a specific action goal.
92#[derive(Clone, Debug, Eq, PartialEq)]
93pub struct ActionSessionHealth {
94 pub goal_id: core_types::ActionGoalId,
95 pub status: GoalStatus,
96 pub liveness: ActionLiveness,
97 pub heartbeat_timeout_nanos: Option<u128>,
98 pub stalled_threshold_nanos: Option<u128>,
99 pub last_heartbeat_at_unix_nanos: Option<u128>,
100 pub last_feedback_at_unix_nanos: Option<u128>,
101 pub last_result_at_unix_nanos: Option<u128>,
102}
103
104/// Schema descriptor for an action, covering the goal, feedback, and result message types.
105#[derive(Clone, Debug, Eq, PartialEq)]
106pub struct ActionSchema {
107 pub goal: data_model::SchemaDescriptor,
108 pub feedback: data_model::SchemaDescriptor,
109 pub result: data_model::SchemaDescriptor,
110}