Skip to main content

mdx_rust_core/
agent_runtime.rs

1//! Agent runtime manifest and pack artifacts.
2//!
3//! These records describe how external coding agents can call `mdx-rust`
4//! without scraping human output.
5
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
10pub struct AgentRuntimeManifest {
11    pub schema_version: String,
12    pub product_version: String,
13    pub protocol_version: String,
14    pub http_auth: AgentRuntimeAuth,
15    pub transports: Vec<AgentRuntimeTransport>,
16    pub tools: Vec<AgentRuntimeTool>,
17    pub mutation_rules: Vec<String>,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
21pub struct AgentRuntimeAuth {
22    pub mode: String,
23    pub header: String,
24    pub env_var: String,
25    pub required_when_token_configured: bool,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
29pub struct AgentRuntimeTransport {
30    pub id: String,
31    pub command: String,
32    pub description: String,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
36pub struct AgentRuntimeTool {
37    pub name: String,
38    pub description: String,
39    pub read_only: bool,
40    pub mutation_capable: bool,
41    pub required_flags_for_mutation: Vec<String>,
42    pub request_schema: String,
43    pub response_schema: String,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
47pub struct AgentPack {
48    pub schema_version: String,
49    pub target_agent: String,
50    pub files: Vec<AgentPackFile>,
51    pub install_note: String,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
55pub struct AgentPackFile {
56    pub path: String,
57    pub purpose: String,
58    pub content: String,
59}
60
61pub fn agent_runtime_manifest() -> AgentRuntimeManifest {
62    AgentRuntimeManifest {
63        schema_version: "1.0".to_string(),
64        product_version: env!("CARGO_PKG_VERSION").to_string(),
65        protocol_version: "mdx-runtime/1.0".to_string(),
66        http_auth: AgentRuntimeAuth {
67            mode: "optional-local-bearer-token".to_string(),
68            header: "authorization: Bearer <token>".to_string(),
69            env_var: "MDX_RUST_RUNTIME_TOKEN".to_string(),
70            required_when_token_configured: true,
71        },
72        transports: vec![
73            AgentRuntimeTransport {
74                id: "cli-json".to_string(),
75                command: "mdx-rust --json <command>".to_string(),
76                description: "Stable command-line JSON contract for humans, scripts, and coding agents.".to_string(),
77            },
78            AgentRuntimeTransport {
79                id: "mcp-stdio".to_string(),
80                command: "mdx-rust mcp --stdio".to_string(),
81                description: "Line-delimited JSON tool protocol over stdin/stdout for local coding agents.".to_string(),
82            },
83            AgentRuntimeTransport {
84                id: "local-http".to_string(),
85                command: "mdx-rust serve --bind 127.0.0.1:3799 --token <token>".to_string(),
86                description: "Localhost-only HTTP surface for read-only tool calls and explicit mutation-gated evolution calls. A bearer token is required when configured.".to_string(),
87            },
88        ],
89        tools: runtime_tools(),
90        mutation_rules: vec![
91            "Read-only tools must never mutate source files.".to_string(),
92            "Mutation-capable runtime tools require apply=true and confirm_mutation=true before source files can change.".to_string(),
93            "Runtime callers cannot bypass evidence, stale-plan, validation, behavior eval, or rollback gates.".to_string(),
94            "HTTP runtime callers must pass the configured bearer token when MDX_RUST_RUNTIME_TOKEN or --token is set.".to_string(),
95            "The HTTP runtime is localhost-only and does not provide remote multi-tenant rate limiting or abuse protection.".to_string(),
96            "Runtime callers should inspect artifact_path fields instead of scraping human output.".to_string(),
97        ],
98    }
99}
100
101pub fn agent_pack(target_agent: &str) -> AgentPack {
102    let file_path = match target_agent {
103        "codex" => ".codex/skills/mdx-rust-evolution/SKILL.md",
104        "claude" => ".claude/skills/mdx-rust-evolution/SKILL.md",
105        "cursor" => ".cursor/rules/mdx-rust-evolution.mdc",
106        "aider" => ".mdx-rust/agent-pack/aider-conventions.md",
107        "goose" => ".mdx-rust/agent-pack/goosehints.md",
108        _ => ".mdx-rust/agent-pack/mdx-rust-evolution.md",
109    };
110    AgentPack {
111        schema_version: "1.0".to_string(),
112        target_agent: target_agent.to_string(),
113        files: vec![AgentPackFile {
114            path: file_path.to_string(),
115            purpose: "Teach a coding agent how to use mdx-rust as a safe Rust evolution engine.".to_string(),
116            content: agent_pack_content(target_agent),
117        }],
118        install_note:
119            "Review the generated file before committing it. The pack is instructions only and never grants mutation permission by itself."
120                .to_string(),
121    }
122}
123
124fn runtime_tools() -> Vec<AgentRuntimeTool> {
125    vec![
126        runtime_tool(
127            "agent-contract",
128            "Discover commands, schemas, artifacts, and mutation rules.",
129            true,
130            false,
131            "agent-contract-request",
132            "agent-contract",
133        ),
134        runtime_tool(
135            "recipes",
136            "List evidence-gated recipe capabilities.",
137            true,
138            false,
139            "recipes-request",
140            "recipe-catalog",
141        ),
142        runtime_tool(
143            "scorecard",
144            "Build a single target briefing for external agents.",
145            true,
146            false,
147            "scorecard-request",
148            "evolution-scorecard",
149        ),
150        runtime_tool(
151            "agent-ready",
152            "Return a compact readiness report for safe external-agent autonomy.",
153            true,
154            false,
155            "agent-ready-request",
156            "agent-ready-report",
157        ),
158        runtime_tool(
159            "evidence",
160            "Collect measured test, coverage, mutation, and semver evidence.",
161            true,
162            false,
163            "evidence-request",
164            "evidence-run",
165        ),
166        runtime_tool(
167            "map",
168            "Build a non-mutating codebase map.",
169            true,
170            false,
171            "map-request",
172            "codebase-map",
173        ),
174        runtime_tool(
175            "plan",
176            "Build a non-mutating refactor plan.",
177            true,
178            false,
179            "plan-request",
180            "refactor-plan",
181        ),
182        runtime_tool(
183            "explain",
184            "Explain a saved mdx-rust artifact.",
185            true,
186            false,
187            "explain-request",
188            "artifact-explanation",
189        ),
190        runtime_tool(
191            "evolve",
192            "Run budget-bounded evolution through autopilot gates.",
193            false,
194            true,
195            "evolve-request",
196            "autopilot-run",
197        ),
198    ]
199}
200
201fn runtime_tool(
202    name: &str,
203    description: &str,
204    read_only: bool,
205    mutation_capable: bool,
206    request_schema: &str,
207    response_schema: &str,
208) -> AgentRuntimeTool {
209    AgentRuntimeTool {
210        name: name.to_string(),
211        description: description.to_string(),
212        read_only,
213        mutation_capable,
214        required_flags_for_mutation: if mutation_capable {
215            vec![
216                "apply=true".to_string(),
217                "confirm_mutation=true".to_string(),
218            ]
219        } else {
220            Vec::new()
221        },
222        request_schema: request_schema.to_string(),
223        response_schema: response_schema.to_string(),
224    }
225}
226
227fn agent_pack_content(target_agent: &str) -> String {
228    format!(
229        r#"---
230name: mdx-rust-evolution
231description: Use mdx-rust to inspect, plan, and safely evolve Rust codebases with evidence-gated autonomy.
232---
233
234# mdx-rust Evolution
235
236Use this when working on Rust repositories, especially when asked to harden,
237refactor, improve quality, or let an agent make autonomous changes.
238
239## Required Intake
240
2411. Run `mdx-rust --json agent-contract`.
2422. Run `mdx-rust --json scorecard <target>`.
2433. Inspect `readiness`, `next_commands`, `security`, and candidate autonomy decisions.
244
245## Mutation Rule
246
247Never add `--apply` unless the human explicitly asked for mutation. Plans,
248maps, recipes, explanations, evidence runs, and scorecards are read-only.
249Runtime `evolve` calls with `apply=true` must also include
250`confirm_mutation=true`.
251
252## Safe Workflows
253
254- Review only: `mdx-rust --json evolve <target> --budget 10m --tier 2 --min-evidence covered`
255- Apply Tier 1: `mdx-rust --json evolve <target> --budget 10m --tier 1 --apply`
256- Apply Tier 2: `mdx-rust --json evidence <target> --include-coverage`, then `mdx-rust --json evolve <target> --budget 10m --tier 2 --min-evidence covered --apply`
257- Local HTTP: `mdx-rust serve --bind 127.0.0.1:3799 --token <token>`
258
259## Reporting
260
261Report artifact paths, evidence grade, executed candidates, validation status,
262rollback status, and remaining blocked or review-only work.
263
264Generated for: {target_agent}
265"#
266    )
267}