agentmux_common/
api_types.rs

1// Copyright 2026, AgentMux Corp.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Canonical HTTP request/response DTOs shared between agentmux-srv,
5//! agentmux-mcp, and agentmux-bashwrap.  Every struct here is the single
6//! source of truth for the corresponding wire shape; the three crates all
7//! import rather than redeclare these types.
8
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12fn is_zero(n: &usize) -> bool {
13    *n == 0
14}
15
16// ── WPS publish ───────────────────────────────────────────────────────────────
17
18/// `POST /agentmux/wps/publish` — shared client/server envelope.
19///
20/// Sent by `agentmux-bashwrap` and received by `agentmux-srv`.
21/// Mirrors `WaveEvent` but omits the server-populated `sender` field.
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct WpsPublishRequest {
24    pub event: String,
25    #[serde(default, skip_serializing_if = "Vec::is_empty")]
26    pub scopes: Vec<String>,
27    #[serde(default, skip_serializing_if = "is_zero")]
28    pub persist: usize,
29    pub data: serde_json::Value,
30}
31
32// ── Shell ─────────────────────────────────────────────────────────────────────
33
34/// `POST /api/v1/shell/create`
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct ShellCreateRequest {
37    pub agent_block_id: String,
38    pub cmd: String,
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub cwd: Option<String>,
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub title: Option<String>,
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub env: Option<HashMap<String, String>>,
45}
46
47/// Response from `POST /api/v1/shell/create`
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct ShellCreateResponse {
50    pub shell_id: String,
51}
52
53/// `POST /api/v1/shell/stop`
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct ShellStopRequest {
56    pub shell_id: String,
57}
58
59/// Response from `POST /api/v1/shell/stop`
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct ShellStopResponse {
62    pub stopped: bool,
63}
64
65// ── Inject (SendMessage + Loop) ───────────────────────────────────────────────
66
67/// `POST /agentmux/reactive/inject` — deliver a message to an agent.
68///
69/// Used by the `SendMessage` and `Loop` MCP tools.
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct InjectRequest {
72    pub target_agent: String,
73    pub message: String,
74    #[serde(default, skip_serializing_if = "Option::is_none")]
75    pub source_agent: Option<String>,
76}
77
78// ── Pane ──────────────────────────────────────────────────────────────────────
79
80/// `POST /api/v1/pane/open` — open a new pane.
81///
82/// Structurally equivalent to `rpc_types::CommandPaneOpenData` (same wire
83/// fields) so the two are interchangeable on the wire; srv deserializes into
84/// `CommandPaneOpenData`, mcp serializes from this type.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct PaneOpenRequest {
87    pub view: String,
88    #[serde(default, skip_serializing_if = "Option::is_none")]
89    pub file: Option<String>,
90    #[serde(default, skip_serializing_if = "Option::is_none")]
91    pub url: Option<String>,
92    #[serde(default, skip_serializing_if = "Option::is_none")]
93    pub cwd: Option<String>,
94    #[serde(default, skip_serializing_if = "Option::is_none")]
95    pub title: Option<String>,
96    #[serde(default, skip_serializing_if = "Option::is_none")]
97    pub tab_id: Option<String>,
98    #[serde(default, skip_serializing_if = "Option::is_none")]
99    pub split_direction: Option<String>,
100    #[serde(default, skip_serializing_if = "Option::is_none")]
101    pub split_reference_block_id: Option<String>,
102    #[serde(default, skip_serializing_if = "Option::is_none")]
103    pub focus: Option<bool>,
104    #[serde(default, skip_serializing_if = "Option::is_none")]
105    pub tree_expanded: Option<bool>,
106    #[serde(default, skip_serializing_if = "Option::is_none")]
107    pub floating: Option<bool>,
108}
109
110/// Response from `POST /api/v1/pane/open`
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct PaneOpenResponse {
113    pub block_id: String,
114}
115
116/// `POST /api/v1/pane/title` — set a pane's display title.
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct PaneTitleRequest {
119    #[serde(default, skip_serializing_if = "Option::is_none")]
120    pub block_id: Option<String>,
121    pub title: String,
122}
123
124// ── Tab ───────────────────────────────────────────────────────────────────────
125
126/// `POST /api/v1/tab/activate` — switch the active tab.
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct TabActivateRequest {
129    pub tab_id: String,
130}
131
132/// `POST /api/v1/tab/new` — create a new tab in a workspace.
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct TabNewRequest {
135    #[serde(default, skip_serializing_if = "Option::is_none")]
136    pub block_id: Option<String>,
137    #[serde(default, skip_serializing_if = "Option::is_none")]
138    pub workspace_id: Option<String>,
139    #[serde(default, skip_serializing_if = "Option::is_none")]
140    pub name: Option<String>,
141}
142
143/// `POST /api/v1/tab/name` — rename a tab.
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct TabNameRequest {
146    #[serde(default, skip_serializing_if = "Option::is_none")]
147    pub block_id: Option<String>,
148    #[serde(default, skip_serializing_if = "Option::is_none")]
149    pub tab_id: Option<String>,
150    pub name: String,
151}
152
153// ── Window ────────────────────────────────────────────────────────────────────
154
155/// `POST /api/v1/window/focus` — bring a window to the foreground.
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct WindowFocusRequest {
158    #[serde(default, skip_serializing_if = "Option::is_none")]
159    pub block_id: Option<String>,
160    #[serde(default, skip_serializing_if = "Option::is_none")]
161    pub window_id: Option<String>,
162}
163
164/// `POST /api/v1/window/name` — set a window's display name.
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct WindowNameRequest {
167    #[serde(default, skip_serializing_if = "Option::is_none")]
168    pub block_id: Option<String>,
169    pub name: String,
170    #[serde(default, skip_serializing_if = "Option::is_none")]
171    pub window_id: Option<String>,
172}
173
174// ── Workspace ─────────────────────────────────────────────────────────────────
175
176/// `POST /api/v1/workspace/name` — rename a workspace.
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct WorkspaceNameRequest {
179    #[serde(default, skip_serializing_if = "Option::is_none")]
180    pub block_id: Option<String>,
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub workspace_id: Option<String>,
183    pub name: String,
184}