Skip to main content

macp_modes/mode/
mod.rs

1pub mod decision;
2pub mod handoff;
3pub mod multi_round;
4pub mod passthrough;
5pub mod proposal;
6pub mod quorum;
7pub mod task;
8pub mod util;
9
10use macp_core::error::MacpError;
11use macp_core::session::Session;
12use macp_pb::pb::{Envelope, ModeDescriptor};
13use std::collections::HashMap;
14
15/// The canonical standards-track modes implemented by this runtime.
16pub const STANDARD_MODE_NAMES: &[&str] = &[
17    "macp.mode.decision.v1",
18    "macp.mode.proposal.v1",
19    "macp.mode.task.v1",
20    "macp.mode.handoff.v1",
21    "macp.mode.quorum.v1",
22];
23
24/// Built-in extension modes shipped with this runtime but not yet standards-track.
25pub const EXTENSION_MODE_NAMES: &[&str] = &["ext.multi_round.v1"];
26
27// `ModeResponse` (data) lives in `macp-core` so `Session::apply_mode_response`
28// can consume it without a core->modes cycle. The `Mode` trait (behavior) stays
29// here. Re-exported so `crate::mode::ModeResponse` keeps resolving.
30pub use macp_core::mode::{MessageContext, ModeResponse};
31
32/// Trait that coordination modes implement.
33/// Modes receive immutable session references and return a ModeResponse.
34/// The runtime kernel is responsible for applying the response.
35pub trait Mode: Send + Sync {
36    fn on_session_start(
37        &self,
38        session: &Session,
39        env: &Envelope,
40    ) -> Result<ModeResponse, MacpError>;
41
42    fn on_message(&self, session: &Session, env: &Envelope) -> Result<ModeResponse, MacpError>;
43
44    /// Kernel entry point: `on_message` plus the runtime's
45    /// [`macp_core::mode::MessageContext`] (acceptance clock). Defaulted to plain
46    /// `on_message` so most modes ignore it; modes that need a trustworthy
47    /// time source (Handoff) override this instead of reading the forgeable
48    /// `Envelope.timestamp_unix_ms`. The runtime and replay always call this,
49    /// with the same clock value that the log entry records.
50    fn on_message_at(
51        &self,
52        session: &Session,
53        env: &Envelope,
54        ctx: &macp_core::mode::MessageContext,
55    ) -> Result<ModeResponse, MacpError> {
56        let _ = ctx;
57        self.on_message(session, env)
58    }
59
60    /// Authorize the sender for this message. Modes can override to customize
61    /// authorization (e.g., allowing orchestrator bypass for Commitment messages).
62    fn authorize_sender(&self, session: &Session, env: &Envelope) -> Result<(), MacpError> {
63        if !session.participants.is_empty() && !session.participants.contains(&env.sender) {
64            return Err(MacpError::Forbidden);
65        }
66        Ok(())
67    }
68}
69
70pub fn standard_mode_names() -> &'static [&'static str] {
71    STANDARD_MODE_NAMES
72}
73
74pub fn extension_mode_names() -> &'static [&'static str] {
75    EXTENSION_MODE_NAMES
76}
77
78fn schema_map(path: &str) -> HashMap<String, String> {
79    HashMap::from([("protobuf".to_string(), path.to_string())])
80}
81
82pub fn standard_mode_descriptors() -> Vec<ModeDescriptor> {
83    vec![
84        ModeDescriptor {
85            mode: "macp.mode.decision.v1".into(),
86            mode_version: "1.0.0".into(),
87            title: "Decision Mode".into(),
88            description: "Structured decision making with proposals, evaluations, objections, votes, and a terminal Commitment.".into(),
89            determinism_class: "semantic-deterministic".into(),
90            participant_model: "declared".into(),
91            message_types: vec![
92                "SessionStart".into(),
93                "Proposal".into(),
94                "Evaluation".into(),
95                "Objection".into(),
96                "Vote".into(),
97                "Commitment".into(),
98            ],
99            terminal_message_types: vec!["Commitment".into()],
100            schema_uris: schema_map("buf.build/multiagentcoordinationprotocol/macp"),
101        },
102        ModeDescriptor {
103            mode: "macp.mode.proposal.v1".into(),
104            mode_version: "1.0.0".into(),
105            title: "Proposal Mode".into(),
106            description: "Negotiation with proposals, counterproposals, accepts, rejects, withdrawals, and a terminal Commitment.".into(),
107            determinism_class: "semantic-deterministic".into(),
108            participant_model: "peer".into(),
109            message_types: vec![
110                "SessionStart".into(),
111                "Proposal".into(),
112                "CounterProposal".into(),
113                "Accept".into(),
114                "Reject".into(),
115                "Withdraw".into(),
116                "Commitment".into(),
117            ],
118            terminal_message_types: vec!["Commitment".into()],
119            schema_uris: schema_map("buf.build/multiagentcoordinationprotocol/macp"),
120        },
121        ModeDescriptor {
122            mode: "macp.mode.task.v1".into(),
123            mode_version: "1.0.0".into(),
124            title: "Task Mode".into(),
125            description: "One bounded delegated task with assignee responses, progress, completion/failure reports, and a terminal Commitment.".into(),
126            determinism_class: "structural-only".into(),
127            participant_model: "orchestrated".into(),
128            message_types: vec![
129                "SessionStart".into(),
130                "TaskRequest".into(),
131                "TaskAccept".into(),
132                "TaskReject".into(),
133                "TaskUpdate".into(),
134                "TaskComplete".into(),
135                "TaskFail".into(),
136                "Commitment".into(),
137            ],
138            terminal_message_types: vec!["Commitment".into()],
139            schema_uris: schema_map("buf.build/multiagentcoordinationprotocol/macp"),
140        },
141        ModeDescriptor {
142            mode: "macp.mode.handoff.v1".into(),
143            mode_version: "1.0.0".into(),
144            title: "Handoff Mode".into(),
145            description: "Scoped responsibility transfer with handoff offers, context, target responses, and a terminal Commitment.".into(),
146            determinism_class: "context-frozen".into(),
147            participant_model: "delegated".into(),
148            message_types: vec![
149                "SessionStart".into(),
150                "HandoffOffer".into(),
151                "HandoffContext".into(),
152                "HandoffAccept".into(),
153                "HandoffDecline".into(),
154                "Commitment".into(),
155            ],
156            terminal_message_types: vec!["Commitment".into()],
157            schema_uris: schema_map("buf.build/multiagentcoordinationprotocol/macp"),
158        },
159        ModeDescriptor {
160            mode: "macp.mode.quorum.v1".into(),
161            mode_version: "1.0.0".into(),
162            title: "Quorum Mode".into(),
163            description: "Threshold approval with one approval request, participant ballots, and a terminal Commitment.".into(),
164            determinism_class: "semantic-deterministic".into(),
165            participant_model: "quorum".into(),
166            message_types: vec![
167                "SessionStart".into(),
168                "ApprovalRequest".into(),
169                "Approve".into(),
170                "Reject".into(),
171                "Abstain".into(),
172                "Commitment".into(),
173            ],
174            terminal_message_types: vec!["Commitment".into()],
175            schema_uris: schema_map("buf.build/multiagentcoordinationprotocol/macp"),
176        },
177    ]
178}
179
180pub fn extension_mode_descriptors() -> Vec<ModeDescriptor> {
181    vec![ModeDescriptor {
182        mode: "ext.multi_round.v1".into(),
183        mode_version: "1.0.0".into(),
184        title: "Multi-Round Mode".into(),
185        description: "Iterative convergence through multiple contribution rounds until all participants agree, with a terminal Commitment.".into(),
186        determinism_class: "semantic-deterministic".into(),
187        participant_model: "peer".into(),
188        message_types: vec![
189            "SessionStart".into(),
190            "Contribute".into(),
191            "Commitment".into(),
192        ],
193        terminal_message_types: vec!["Commitment".into()],
194        schema_uris: schema_map("buf.build/multiagentcoordinationprotocol/macp"),
195    }]
196}
197
198pub fn all_mode_descriptors() -> Vec<ModeDescriptor> {
199    let mut all = standard_mode_descriptors();
200    all.extend(extension_mode_descriptors());
201    all
202}