Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions crates/executors/default_profiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,24 @@
"model": "glm-4.6"
}
}
},
"MISTRAL_VIBE": {
"DEFAULT": {
"MISTRAL_VIBE": {
"auto_approve": true
}
},
"DEVSTRAL_SMALL": {
"MISTRAL_VIBE": {
"model": "devstral-small-2",
"auto_approve": true
}
},
"APPROVALS": {
"MISTRAL_VIBE": {
"auto_approve": false
}
}
}
}
}
4 changes: 2 additions & 2 deletions crates/executors/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ pub enum CommandBuildError {

#[derive(Debug, Clone)]
pub struct CommandParts {
program: String,
args: Vec<String>,
pub program: String,
pub args: Vec<String>,
}

impl CommandParts {
Expand Down
141 changes: 141 additions & 0 deletions crates/executors/src/executors/mistral_vibe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use std::{path::Path, sync::Arc};

use async_trait::async_trait;
use derivative::Derivative;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use workspace_utils::msg_store::MsgStore;

pub use super::acp::AcpAgentHarness;
use crate::{
approvals::ExecutorApprovalService,
command::{CmdOverrides, CommandBuilder, apply_overrides},
env::ExecutionEnv,
executors::{
AppendPrompt, AvailabilityInfo, ExecutorError, SpawnedChild, StandardCodingAgentExecutor,
},
};

#[derive(Derivative, Clone, Serialize, Deserialize, TS, JsonSchema)]
#[derivative(Debug, PartialEq)]
pub struct MistralVibe {
#[serde(default)]
pub append_prompt: AppendPrompt,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auto_approve: Option<bool>,
#[serde(flatten)]
pub cmd: CmdOverrides,
#[serde(skip)]
#[ts(skip)]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
pub approvals: Option<Arc<dyn ExecutorApprovalService>>,
}

impl MistralVibe {
fn build_command_builder(&self) -> CommandBuilder {
let builder = CommandBuilder::new("vibe-acp");
apply_overrides(builder, &self.cmd)
}
}

#[async_trait]
impl StandardCodingAgentExecutor for MistralVibe {
fn use_approvals(&mut self, approvals: Arc<dyn ExecutorApprovalService>) {
self.approvals = Some(approvals);
}

async fn spawn(
&self,
current_dir: &Path,
prompt: &str,
env: &ExecutionEnv,
) -> Result<SpawnedChild, ExecutorError> {
let mut harness = AcpAgentHarness::with_session_namespace("vibe_sessions");
if let Some(ref model) = self.model {
harness = harness.with_model(model);
}
let combined_prompt = self.append_prompt.combine_prompt(prompt);
let vibe_command = self.build_command_builder().build_initial()?;
let approvals = if self.auto_approve.unwrap_or(false) {
None
} else {
self.approvals.clone()
};
harness
.spawn_with_command(
current_dir,
combined_prompt,
vibe_command,
env,
&self.cmd,
approvals,
)
.await
}

async fn spawn_follow_up(
&self,
current_dir: &Path,
prompt: &str,
session_id: &str,
env: &ExecutionEnv,
) -> Result<SpawnedChild, ExecutorError> {
let mut harness = AcpAgentHarness::with_session_namespace("vibe_sessions");
if let Some(ref model) = self.model {
harness = harness.with_model(model);
}
let combined_prompt = self.append_prompt.combine_prompt(prompt);
let vibe_command = self.build_command_builder().build_follow_up(&[])?;
let approvals = if self.auto_approve.unwrap_or(false) {
None
} else {
self.approvals.clone()
};
harness
.spawn_follow_up_with_command(
current_dir,
combined_prompt,
session_id,
vibe_command,
env,
&self.cmd,
approvals,
)
.await
}

fn normalize_logs(&self, msg_store: Arc<MsgStore>, worktree_path: &Path) {
super::acp::normalize_logs(msg_store, worktree_path);
}

fn default_mcp_config_path(&self) -> Option<std::path::PathBuf> {
dirs::home_dir().map(|home| home.join(".vibe").join("config.toml"))
}

fn get_availability_info(&self) -> AvailabilityInfo {
if let Some(timestamp) = dirs::home_dir()
.and_then(|home| std::fs::metadata(home.join(".vibe").join(".env")).ok())
.and_then(|m| m.modified().ok())
.and_then(|modified| modified.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.as_secs() as i64)
{
return AvailabilityInfo::LoginDetected {
last_auth_timestamp: timestamp,
};
}

let mcp_config_found = self
.default_mcp_config_path()
.map(|p| p.exists())
.unwrap_or(false);

if mcp_config_found {
AvailabilityInfo::InstallationFound
} else {
AvailabilityInfo::NotFound
}
}
}
8 changes: 6 additions & 2 deletions crates/executors/src/executors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ use crate::{
env::ExecutionEnv,
executors::{
amp::Amp, claude::ClaudeCode, codex::Codex, copilot::Copilot, cursor::CursorAgent,
droid::Droid, gemini::Gemini, opencode::Opencode, qwen::QwenCode,
droid::Droid, gemini::Gemini, mistral_vibe::MistralVibe, opencode::Opencode,
qwen::QwenCode,
},
mcp_config::McpConfig,
};
Expand All @@ -32,6 +33,7 @@ pub mod copilot;
pub mod cursor;
pub mod droid;
pub mod gemini;
pub mod mistral_vibe;
pub mod opencode;
pub mod qwen;

Expand Down Expand Up @@ -100,6 +102,7 @@ pub enum CodingAgent {
QwenCode,
Copilot,
Droid,
MistralVibe,
}

impl CodingAgent {
Expand Down Expand Up @@ -160,7 +163,8 @@ impl CodingAgent {
| Self::Gemini(_)
| Self::QwenCode(_)
| Self::Droid(_)
| Self::Opencode(_) => vec![BaseAgentCapability::SessionFork],
| Self::Opencode(_)
| Self::MistralVibe(_) => vec![BaseAgentCapability::SessionFork],
Self::Codex(_) => vec![
BaseAgentCapability::SessionFork,
BaseAgentCapability::SetupHelper,
Expand Down
5 changes: 4 additions & 1 deletion crates/executors/src/mcp_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,10 @@ impl CodingAgent {
use Adapter::*;

let adapter = match self {
CodingAgent::ClaudeCode(_) | CodingAgent::Amp(_) | CodingAgent::Droid(_) => Passthrough,
CodingAgent::ClaudeCode(_)
| CodingAgent::Amp(_)
| CodingAgent::Droid(_)
| CodingAgent::MistralVibe(_) => Passthrough,
CodingAgent::QwenCode(_) | CodingAgent::Gemini(_) => Gemini,
CodingAgent::CursorAgent(_) => Cursor,
CodingAgent::Codex(_) => Codex,
Expand Down
5 changes: 5 additions & 0 deletions crates/server/src/bin/generate_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ fn generate_types_content() -> String {
executors::executors::droid::Droid::decl(),
executors::executors::droid::Autonomy::decl(),
executors::executors::droid::ReasoningEffortLevel::decl(),
executors::executors::mistral_vibe::MistralVibe::decl(),
executors::executors::AppendPrompt::decl(),
executors::actions::coding_agent_initial::CodingAgentInitialRequest::decl(),
executors::actions::coding_agent_follow_up::CodingAgentFollowUpRequest::decl(),
Expand Down Expand Up @@ -294,6 +295,10 @@ fn generate_schemas() -> Result<HashMap<&'static str, String>, serde_json::Error
"droid",
generate_json_schema::<executors::executors::droid::Droid>()?,
),
(
"mistral_vibe",
generate_json_schema::<executors::executors::mistral_vibe::MistralVibe>()?,
),
]);
println!(
"✅ JSON schemas generated. {} schemas created.",
Expand Down
10 changes: 10 additions & 0 deletions frontend/public/agents/mistral-vibe-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions frontend/public/agents/mistral-vibe-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions frontend/src/components/agents/AgentIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export function getAgentName(
return 'Copilot';
case BaseCodingAgent.DROID:
return 'Droid';
case BaseCodingAgent.MISTRAL_VIBE:
return 'Mistral Vibe';
}
}

Expand Down Expand Up @@ -82,6 +84,9 @@ export function AgentIcon({ agent, className = 'h-4 w-4' }: AgentIconProps) {
case BaseCodingAgent.DROID:
iconPath = `/agents/droid${suffix}.svg`;
break;
case BaseCodingAgent.MISTRAL_VIBE:
iconPath = `/agents/mistral-vibe${suffix}.svg`;
break;
default:
return null;
}
Expand Down
58 changes: 58 additions & 0 deletions shared/schemas/mistral_vibe.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"append_prompt": {
"title": "Append Prompt",
"description": "Extra text appended to the prompt",
"type": [
"string",
"null"
],
"format": "textarea",
"default": null
},
"model": {
"type": [
"string",
"null"
]
},
"auto_approve": {
"type": [
"boolean",
"null"
]
},
"base_command_override": {
"title": "Base Command Override",
"description": "Override the base command with a custom command",
"type": [
"string",
"null"
]
},
"additional_params": {
"title": "Additional Parameters",
"description": "Additional parameters to append to the base command",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"env": {
"title": "Environment Variables",
"description": "Environment variables to set when running the executor",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
}
},
"type": "object"
}
8 changes: 5 additions & 3 deletions shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,9 @@ working_dir: string | null, };

export type ScriptRequestLanguage = "Bash";

export enum BaseCodingAgent { CLAUDE_CODE = "CLAUDE_CODE", AMP = "AMP", GEMINI = "GEMINI", CODEX = "CODEX", OPENCODE = "OPENCODE", CURSOR_AGENT = "CURSOR_AGENT", QWEN_CODE = "QWEN_CODE", COPILOT = "COPILOT", DROID = "DROID" }
export enum BaseCodingAgent { CLAUDE_CODE = "CLAUDE_CODE", AMP = "AMP", GEMINI = "GEMINI", CODEX = "CODEX", OPENCODE = "OPENCODE", CURSOR_AGENT = "CURSOR_AGENT", QWEN_CODE = "QWEN_CODE", COPILOT = "COPILOT", DROID = "DROID", MISTRAL_VIBE = "MISTRAL_VIBE" }

export type CodingAgent = { "CLAUDE_CODE": ClaudeCode } | { "AMP": Amp } | { "GEMINI": Gemini } | { "CODEX": Codex } | { "OPENCODE": Opencode } | { "CURSOR_AGENT": CursorAgent } | { "QWEN_CODE": QwenCode } | { "COPILOT": Copilot } | { "DROID": Droid };
export type CodingAgent = { "CLAUDE_CODE": ClaudeCode } | { "AMP": Amp } | { "GEMINI": Gemini } | { "CODEX": Codex } | { "OPENCODE": Opencode } | { "CURSOR_AGENT": CursorAgent } | { "QWEN_CODE": QwenCode } | { "COPILOT": Copilot } | { "DROID": Droid } | { "MISTRAL_VIBE": MistralVibe };

export type AvailabilityInfo = { "type": "LOGIN_DETECTED", last_auth_timestamp: bigint, } | { "type": "INSTALLATION_FOUND" } | { "type": "NOT_FOUND" };

Expand All @@ -445,7 +445,7 @@ executor: BaseCodingAgent,
*/
variant: string | null, };

export type ExecutorConfig = { [key in string]?: { "CLAUDE_CODE": ClaudeCode } | { "AMP": Amp } | { "GEMINI": Gemini } | { "CODEX": Codex } | { "OPENCODE": Opencode } | { "CURSOR_AGENT": CursorAgent } | { "QWEN_CODE": QwenCode } | { "COPILOT": Copilot } | { "DROID": Droid } };
export type ExecutorConfig = { [key in string]?: { "CLAUDE_CODE": ClaudeCode } | { "AMP": Amp } | { "GEMINI": Gemini } | { "CODEX": Codex } | { "OPENCODE": Opencode } | { "CURSOR_AGENT": CursorAgent } | { "QWEN_CODE": QwenCode } | { "COPILOT": Copilot } | { "DROID": Droid } | { "MISTRAL_VIBE": MistralVibe } };

export type ExecutorConfigs = { executors: { [key in BaseCodingAgent]?: ExecutorConfig }, };

Expand Down Expand Up @@ -487,6 +487,8 @@ export type Autonomy = "normal" | "low" | "medium" | "high" | "skip-permissions-

export type DroidReasoningEffort = "none" | "dynamic" | "off" | "low" | "medium" | "high";

export type MistralVibe = { append_prompt: AppendPrompt, model?: string | null, auto_approve?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, env?: { [key in string]?: string } | null, };

export type AppendPrompt = string | null;

export type CodingAgentInitialRequest = { prompt: string,
Expand Down