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
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ default-members = [
"crates/config",
"crates/cron",
"crates/discord",
"crates/external-agents",
"crates/gateway",
"crates/graphql",
"crates/httpd",
Expand All @@ -36,8 +37,8 @@ default-members = [
"crates/providers",
"crates/qmd",
"crates/routing",
"crates/service-traits",
"crates/secret-store",
"crates/service-traits",
"crates/sessions",
"crates/skills",
"crates/slack",
Expand Down Expand Up @@ -70,6 +71,7 @@ members = [
"crates/common",
"crates/config",
"crates/cron",
"crates/external-agents",
"crates/gateway",
"crates/graphql",
"crates/httpd",
Expand All @@ -91,8 +93,8 @@ members = [
"crates/qmd",
"crates/routing",
"crates/schema-export",
"crates/service-traits",
"crates/secret-store",
"crates/service-traits",
"crates/sessions",
"crates/skills",
"crates/slack",
Expand All @@ -116,8 +118,8 @@ resolver = "2"
# Widen libsqlite3-sys pin (0.30.1 → >=0.30.1,<0.36.0) so sqlx-sqlite 0.8.6
# can coexist with matrix-sdk-sqlite's rusqlite 0.37 (libsqlite3-sys 0.35).
# Remove once sqlx ≥ 0.9 ships stable.
sqlx-core = { git = "https://github.com/moltis-org/sqlx", rev = "33747ad2e2d0c7962c6450faf31591c4e0782c23" }
sqlx-sqlite = { git = "https://github.com/moltis-org/sqlx", rev = "33747ad2e2d0c7962c6450faf31591c4e0782c23" }
sqlx-core = { git = "https://github.com/moltis-org/sqlx", rev = "33747ad2e2d0c7962c6450faf31591c4e0782c23" }

[workspace.lints.rust]
unsafe_code = "deny"
Expand Down Expand Up @@ -317,6 +319,7 @@ moltis-common = { path = "crates/common" }
moltis-config = { path = "crates/config" }
moltis-cron = { path = "crates/cron" }
moltis-discord = { path = "crates/discord" }
moltis-external-agents = { path = "crates/external-agents" }
moltis-gateway = { default-features = false, path = "crates/gateway" }
moltis-graphql = { path = "crates/graphql" }
moltis-httpd = { default-features = false, path = "crates/httpd" }
Expand Down
2 changes: 2 additions & 0 deletions crates/chat/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9249,6 +9249,8 @@ mod tests {
preview: None,
agent_id: None,
node_id: None,
external_agent_kind: None,
external_session_id: None,
version: 0,
}
}
Expand Down
25 changes: 25 additions & 0 deletions crates/config/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,31 @@ pub struct MoltisConfig {
/// Process env vars take precedence (existing vars are not overwritten).
#[serde(default)]
pub env: HashMap<String, String>,
#[serde(default)]
pub external_agents: ExternalAgentsConfig,
}

/// Configuration for external CLI agent integrations.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct ExternalAgentsConfig {
pub enabled: bool,
#[serde(default)]
pub agents: HashMap<String, ExternalAgentConfig>,
}

/// Per-agent configuration for an external CLI agent.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct ExternalAgentConfig {
pub binary: Option<String>,
#[serde(default)]
pub args: Vec<String>,
#[serde(default)]
pub env: HashMap<String, String>,
pub working_dir: Option<String>,
pub timeout_secs: Option<u64>,
pub use_tmux: Option<bool>,
}

/// Agent spawn presets used by tools like `spawn_agent`.
Expand Down
27 changes: 27 additions & 0 deletions crates/config/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,33 @@ reset_on_exit = true # Reset serve/funnel when gateway shuts down
# model = "nomic-embed-text" # Embedding model name
# api_key = "..." # API key (optional for local endpoints like Ollama)

# ══════════════════════════════════════════════════════════════════════════════
# EXTERNAL AGENTS
# ══════════════════════════════════════════════════════════════════════════════
# Connect Moltis chat sessions to external CLI coding agents.
# Each agent runs as an external process (PTY/tmux or JSON-RPC).
# Moltis acts as orchestrator; the CLI agent owns its own context window.

[external_agents]
# enabled = false # Enable external agent bridge

# Per-agent configuration (key = agent kind)
# [external_agents.agents.claude-code]
# binary = "claude" # Override binary path (default: look up on $PATH)
# args = [] # Additional CLI arguments
# working_dir = "." # Override working directory
# timeout_secs = 300 # Session timeout
# use_tmux = false # Force tmux backend (vs direct PTY)
# [external_agents.agents.claude-code.env]
# ANTHROPIC_API_KEY = "sk-..." # Extra env vars for this agent

# [external_agents.agents.codex]
# binary = "codex"

# [external_agents.agents.opencode]
# binary = "opencode"
# use_tmux = true # opencode requires tmux (TUI app)

# ══════════════════════════════════════════════════════════════════════════════
# CHANNELS
# ══════════════════════════════════════════════════════════════════════════════
Expand Down
17 changes: 17 additions & 0 deletions crates/config/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,23 @@ fn build_schema_map() -> KnownKeys {
),
])),
),
(
"external_agents",
Struct(HashMap::from([
("enabled", Leaf),
(
"agents",
Map(Box::new(Struct(HashMap::from([
("binary", Leaf),
("args", Leaf),
("env", Map(Box::new(Leaf))),
("working_dir", Leaf),
("timeout_secs", Leaf),
("use_tmux", Leaf),
])))),
),
])),
),
]))
}

Expand Down
36 changes: 36 additions & 0 deletions crates/external-agents/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
edition.workspace = true
name = "moltis-external-agents"
repository.workspace = true
version.workspace = true

[features]
acp = []
claude-code = []
codex = []
default = ["acp", "claude-code", "codex", "opencode", "pi-agent"]
metrics = ["dep:moltis-metrics"]
opencode = []
pi-agent = []
tracing = ["dep:tracing"]

[dependencies]
anyhow = { workspace = true }
async-trait = { workspace = true }
futures = { workspace = true }
moltis-config = { workspace = true }
moltis-metrics = { optional = true, workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sha2 = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
tracing = { optional = true, workspace = true }
which = { workspace = true }

[dev-dependencies]
tokio-test = { workspace = true }

[lints]
workspace = true
Loading
Loading