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