meerkat_mobkit/types.rs
1//! Core type definitions shared across the MobKit runtime.
2
3use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8/// A structural mob event projected from `meerkat_mob::AttributedEvent` and
9/// `MobEventKind` into a flat, identity-aware envelope suitable for SSE
10/// replay and JSON-RPC query.
11///
12/// Mirrors the shape of [`crate::console_contracts::ConsoleIdentityEventEnvelope`]
13/// but preserves structural fields (`mob_id`, `run_id`, `step_id`,
14/// `agent_identity`) that the lossy `UnifiedEvent::Agent` projection
15/// discards. `cursor` is a monotonically increasing sequence assigned at
16/// ingestion time and serves as the pagination token (clients pass the
17/// last-seen cursor as `EventQuery::after_seq`).
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub struct MobStructuralEventEnvelope {
20 /// Unique event id (mirrors the originating mob event cursor when
21 /// available, prefixed with `mob-evt-`).
22 pub event_id: String,
23 /// Monotonic ingestion sequence (used as `after_seq` cursor).
24 pub cursor: u64,
25 /// Mob this event belongs to.
26 pub mob_id: String,
27 /// Millisecond-precision wall-clock timestamp at ingestion.
28 pub timestamp_ms: u64,
29 /// Snake-case event kind (e.g. `flow_started`, `step_dispatched`).
30 pub kind: String,
31 /// Run id when the event is associated with a flow run, otherwise `None`.
32 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub run_id: Option<String>,
34 /// Step id when the event is associated with a flow step, otherwise `None`.
35 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub step_id: Option<String>,
37 /// Stable agent identity when the event is attributable to a member,
38 /// otherwise `None`.
39 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub agent_identity: Option<String>,
41 /// Mob-scoped labels at projection time (snapshot of the
42 /// `RuntimeMetadataTable` entry for `MetadataScope::Mob(mob_id)`).
43 /// Empty if no labels are set.
44 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
45 pub mob_labels: BTreeMap<String, String>,
46 /// Run-scoped labels at projection time (snapshot of the
47 /// `RuntimeMetadataTable` entry for `MetadataScope::Run(mob_id, run_id)`).
48 /// Empty when the event has no `run_id` or no labels are set for the run.
49 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
50 pub run_labels: BTreeMap<String, String>,
51 /// Full structured payload (the variant-specific fields of `MobEventKind`).
52 pub data: Value,
53}
54
55/// Timestamped wrapper around an event payload.
56#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
57pub struct EventEnvelope<T> {
58 pub event_id: String,
59 pub source: String,
60 pub timestamp_ms: u64,
61 pub event: T,
62}
63
64/// A runtime event from either an agent or a module.
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
66#[serde(tag = "kind", rename_all = "snake_case")]
67pub enum UnifiedEvent {
68 Agent {
69 agent_id: String,
70 event_type: String,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
72 payload: Option<Value>,
73 },
74 Module(ModuleEvent),
75}
76
77/// An event originating from a loaded MCP module.
78#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
79pub struct ModuleEvent {
80 pub module: String,
81 pub event_type: String,
82 pub payload: Value,
83}
84
85/// Configuration for a single MCP module to be loaded.
86#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
87pub struct ModuleConfig {
88 pub id: String,
89 pub command: String,
90 pub args: Vec<String>,
91 pub restart_policy: RestartPolicy,
92}
93
94/// Policy controlling automatic module restart behavior.
95#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
96#[serde(rename_all = "snake_case")]
97pub enum RestartPolicy {
98 Never,
99 OnFailure,
100 Always,
101}
102
103/// Specification for module discovery at startup.
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
105pub struct DiscoverySpec {
106 pub namespace: String,
107 pub modules: Vec<String>,
108}
109
110/// Agent-level discovery specification for spawning agents into a mob.
111///
112/// Unlike [`DiscoverySpec`] (which describes module discovery for `MobKitConfig`),
113/// this type captures the fields needed to discover and spawn individual agents.
114#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
115pub struct AgentDiscoverySpec {
116 /// Agent profile name (maps to a profile in the mob definition).
117 pub profile: String,
118 /// Unique agent ID within the mob.
119 pub meerkat_id: String,
120 /// Application-defined labels for this agent.
121 #[serde(default, skip_serializing_if = "Option::is_none")]
122 pub labels: Option<BTreeMap<String, String>>,
123 /// Opaque application context passed through to the agent build pipeline.
124 #[serde(default, skip_serializing_if = "Option::is_none")]
125 pub context: Option<Value>,
126 /// Extra instructions appended to the agent prompt.
127 #[serde(default, skip_serializing_if = "Vec::is_empty")]
128 pub additional_instructions: Vec<String>,
129 /// Resume an existing session instead of creating a new one.
130 #[serde(default, skip_serializing_if = "Option::is_none")]
131 pub resume_session_id: Option<String>,
132}
133
134/// Pre-spawn context passed to modules during discovery.
135#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
136pub struct PreSpawnData {
137 pub module_id: String,
138 pub env: Vec<(String, String)>,
139}
140
141/// Top-level configuration for a MobKit runtime instance.
142#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
143pub struct MobKitConfig {
144 pub modules: Vec<ModuleConfig>,
145 pub discovery: DiscoverySpec,
146 pub pre_spawn: Vec<PreSpawnData>,
147}