style: apply pinned rustfmt

This commit is contained in:
Bryan Helmkamp 2026-08-21 19:34:38 -04:00
parent 53efde3930
commit cfa8ae92c0
No known key found for this signature in database
13 changed files with 526 additions and 571 deletions

View file

@ -12,9 +12,8 @@ use fabro_model::{Catalog, ProviderId};
use fabro_redact::redact_string;
use fabro_sandbox::{DockerSandboxProvider, daytona};
use fabro_static::EnvVars;
use fabro_types::settings::SearchProvider;
use fabro_types::settings::ServerAuthMethod;
use fabro_types::settings::server::GithubIntegrationStrategy;
use fabro_types::settings::{SearchProvider, ServerAuthMethod};
use fabro_util::check_report::{CheckDetail, CheckResult, CheckSection, CheckStatus};
use fabro_util::dev_token::validate_dev_token_format;
use fabro_util::session_secret;
@ -44,21 +43,21 @@ fn http_client_or_check(
#[derive(Debug, Serialize)]
pub struct DiagnosticsReport {
pub version: String,
pub version: String,
pub sections: Vec<CheckSection>,
}
#[derive(Debug, Clone, Serialize)]
pub(crate) struct ProviderProbeReport {
pub data: Vec<ProviderProbeResult>,
pub data: Vec<ProviderProbeResult>,
pub summary: ProviderProbeSummary,
}
#[derive(Debug, Clone, Serialize)]
pub(crate) struct ProviderProbeResult {
pub provider: ProviderId,
pub model_id: Option<String>,
pub status: ProviderProbeStatus,
pub provider: ProviderId,
pub model_id: Option<String>,
pub status: ProviderProbeStatus,
pub error_message: Option<String>,
#[serde(skip)]
diagnostic_detail: Option<String>,
@ -67,7 +66,7 @@ pub(crate) struct ProviderProbeResult {
#[derive(Debug, Clone, Serialize)]
pub(crate) struct ProviderProbeSummary {
pub status: ProviderProbeStatus,
pub total: u32,
pub total: u32,
pub passed: u32,
pub failed: u32,
}
@ -105,14 +104,14 @@ pub async fn run_all(state: &AppState) -> DiagnosticsReport {
);
DiagnosticsReport {
version: FABRO_VERSION.to_string(),
version: FABRO_VERSION.to_string(),
sections: vec![
CheckSection {
title: "Credentials".to_string(),
title: "Credentials".to_string(),
checks: vec![llm, github, docker_sandbox, cloud_sandbox, web_search],
},
CheckSection {
title: "Configuration".to_string(),
title: "Configuration".to_string(),
checks: vec![crypto, check_storage_dir(state)],
},
],
@ -124,20 +123,20 @@ async fn check_llm_providers(state: &AppState) -> CheckResult {
Ok(report) => report,
Err(err) => {
return CheckResult {
name: "LLM Providers".to_string(),
status: CheckStatus::Error,
summary: "failed to initialize".to_string(),
details: vec![CheckDetail::new(format!("{err:#}"))],
name: "LLM Providers".to_string(),
status: CheckStatus::Error,
summary: "failed to initialize".to_string(),
details: vec![CheckDetail::new(format!("{err:#}"))],
remediation: Some("Check configured provider credentials".to_string()),
};
}
};
if report.data.is_empty() {
return CheckResult {
name: "LLM Providers".to_string(),
status: CheckStatus::Error,
summary: "none configured".to_string(),
details: Vec::new(),
name: "LLM Providers".to_string(),
status: CheckStatus::Error,
summary: "none configured".to_string(),
details: Vec::new(),
remediation: Some("Set at least one provider API key".to_string()),
};
}
@ -159,7 +158,7 @@ async fn check_llm_providers(state: &AppState) -> CheckResult {
.clone()
.unwrap_or_else(|| format!("{}: {message}", result.provider));
failures.push(ProviderFailure {
provider: result.provider.to_string(),
provider: result.provider.to_string(),
summary_line: short_error_line(message),
});
details.push(CheckDetail::new(detail));
@ -198,7 +197,7 @@ async fn check_llm_providers(state: &AppState) -> CheckResult {
}
struct ProviderFailure {
provider: String,
provider: String,
summary_line: String,
}
@ -355,10 +354,10 @@ async fn check_github_app(state: &AppState) -> CheckResult {
Ok(token) => token.to_string(),
Err(err) => {
return CheckResult {
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "token expired".to_string(),
details: vec![CheckDetail::new(err.to_string())],
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "token expired".to_string(),
details: vec![CheckDetail::new(err.to_string())],
remediation: Some(
"Run fabro install or run `fabro secret set GITHUB_TOKEN`"
.to_string(),
@ -370,10 +369,10 @@ async fn check_github_app(state: &AppState) -> CheckResult {
Ok(Some(_)) => unreachable!("token strategy should not return app credentials"),
Ok(None) => {
return CheckResult {
name: "GitHub Token".to_string(),
status: CheckStatus::Warning,
summary: "not configured".to_string(),
details: Vec::new(),
name: "GitHub Token".to_string(),
status: CheckStatus::Warning,
summary: "not configured".to_string(),
details: Vec::new(),
remediation: Some(
"Run fabro install or run `fabro secret set GITHUB_TOKEN`".to_string(),
),
@ -382,10 +381,10 @@ async fn check_github_app(state: &AppState) -> CheckResult {
Err(err) => {
let rendered = format!("{err:#}");
return CheckResult {
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "missing token".to_string(),
details: vec![CheckDetail::new(rendered.clone())],
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "missing token".to_string(),
details: vec![CheckDetail::new(rendered.clone())],
remediation: Some(rendered),
};
}
@ -407,18 +406,18 @@ async fn check_github_app(state: &AppState) -> CheckResult {
return match probe {
Ok(Ok(response)) if response.status().is_success() => CheckResult {
name: "GitHub Token".to_string(),
status: CheckStatus::Pass,
summary: "configured".to_string(),
details: Vec::new(),
name: "GitHub Token".to_string(),
status: CheckStatus::Pass,
summary: "configured".to_string(),
details: Vec::new(),
remediation: None,
},
Ok(Ok(response)) if response.status() == fabro_http::StatusCode::UNAUTHORIZED => {
CheckResult {
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "token invalid".to_string(),
details: vec![CheckDetail::new(format!(
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "token invalid".to_string(),
details: vec![CheckDetail::new(format!(
"GitHub returned {}",
response.status()
))],
@ -428,10 +427,10 @@ async fn check_github_app(state: &AppState) -> CheckResult {
}
}
Ok(Ok(response)) => CheckResult {
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "connectivity error".to_string(),
details: vec![CheckDetail::new(format!(
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "connectivity error".to_string(),
details: vec![CheckDetail::new(format!(
"GitHub returned {}",
response.status()
))],
@ -440,19 +439,19 @@ async fn check_github_app(state: &AppState) -> CheckResult {
),
},
Ok(Err(err)) => CheckResult {
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "connectivity error".to_string(),
details: vec![CheckDetail::new(err.to_string())],
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "connectivity error".to_string(),
details: vec![CheckDetail::new(err.to_string())],
remediation: Some(
"Check GitHub connectivity and the vault GITHUB_TOKEN".to_string(),
),
},
Err(_) => CheckResult {
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "timeout".to_string(),
details: vec![CheckDetail::new("GitHub probe timed out".to_string())],
name: "GitHub Token".to_string(),
status: CheckStatus::Error,
summary: "timeout".to_string(),
details: vec![CheckDetail::new("GitHub probe timed out".to_string())],
remediation: Some(
"Check GitHub connectivity and the vault GITHUB_TOKEN".to_string(),
),
@ -486,20 +485,20 @@ async fn check_github_app(state: &AppState) -> CheckResult {
&& !webhook_secret
{
return CheckResult {
name: "GitHub App".to_string(),
status: CheckStatus::Warning,
summary: "not configured".to_string(),
details: Vec::new(),
name: "GitHub App".to_string(),
status: CheckStatus::Warning,
summary: "not configured".to_string(),
details: Vec::new(),
remediation: Some("Configure GitHub App settings and secrets".to_string()),
};
}
let Some(app_id) = app_id else {
return CheckResult {
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "missing app_id".to_string(),
details: Vec::new(),
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "missing app_id".to_string(),
details: Vec::new(),
remediation: Some(
"Set [server.integrations.github].app_id in settings.toml".to_string(),
),
@ -507,10 +506,10 @@ async fn check_github_app(state: &AppState) -> CheckResult {
};
let Some(private_key_raw) = private_key_raw else {
return CheckResult {
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "missing private key".to_string(),
details: Vec::new(),
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "missing private key".to_string(),
details: Vec::new(),
remediation: Some("Run `fabro secret set GITHUB_APP_PRIVATE_KEY`".to_string()),
};
};
@ -519,10 +518,10 @@ async fn check_github_app(state: &AppState) -> CheckResult {
Ok(value) => value,
Err(err) => {
return CheckResult {
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "private key invalid".to_string(),
details: vec![CheckDetail::new(err.clone())],
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "private key invalid".to_string(),
details: vec![CheckDetail::new(err.clone())],
remediation: Some(err),
};
}
@ -532,10 +531,10 @@ async fn check_github_app(state: &AppState) -> CheckResult {
Ok(jwt) => jwt,
Err(err) => {
return CheckResult {
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "JWT signing failed".to_string(),
details: vec![CheckDetail::new(format!("{err:#}"))],
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "JWT signing failed".to_string(),
details: vec![CheckDetail::new(format!("{err:#}"))],
remediation: Some(err.to_string()),
};
}
@ -552,24 +551,24 @@ async fn check_github_app(state: &AppState) -> CheckResult {
.await;
match auth_result {
Ok(Ok(_app)) => CheckResult {
name: "GitHub App".to_string(),
status: CheckStatus::Pass,
summary: slug.unwrap_or_else(|| "configured".to_string()),
details: Vec::new(),
name: "GitHub App".to_string(),
status: CheckStatus::Pass,
summary: slug.unwrap_or_else(|| "configured".to_string()),
details: Vec::new(),
remediation: None,
},
Ok(Err(err)) => CheckResult {
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "connectivity error".to_string(),
details: vec![CheckDetail::new(format!("{err:#}"))],
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "connectivity error".to_string(),
details: vec![CheckDetail::new(format!("{err:#}"))],
remediation: Some("Check GitHub App credentials and network connectivity".to_string()),
},
Err(_) => CheckResult {
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "timeout".to_string(),
details: vec![CheckDetail::new("GitHub probe timed out".to_string())],
name: "GitHub App".to_string(),
status: CheckStatus::Error,
summary: "timeout".to_string(),
details: vec![CheckDetail::new("GitHub probe timed out".to_string())],
remediation: Some("Check GitHub connectivity and credentials".to_string()),
},
}
@ -605,10 +604,10 @@ where
{
if !enabled {
return CheckResult {
name: "Docker Sandbox".to_string(),
status: CheckStatus::Pass,
summary: "disabled".to_string(),
details: vec![CheckDetail::new(
name: "Docker Sandbox".to_string(),
status: CheckStatus::Pass,
summary: "disabled".to_string(),
details: vec![CheckDetail::new(
"server.sandbox.providers.docker.enabled = false".to_string(),
)],
remediation: None,
@ -653,10 +652,10 @@ async fn check_cloud_sandbox(state: &AppState) -> CheckResult {
};
let Some(api_key) = api_key else {
return CheckResult {
name: "Cloud Sandbox".to_string(),
status: CheckStatus::Warning,
summary: "recommended, not configured".to_string(),
details: Vec::new(),
name: "Cloud Sandbox".to_string(),
status: CheckStatus::Warning,
summary: "recommended, not configured".to_string(),
details: Vec::new(),
remediation: Some(
"Run `fabro secret set DAYTONA_API_KEY` to enable cloud sandbox execution"
.to_string(),
@ -673,17 +672,17 @@ async fn check_cloud_sandbox(state: &AppState) -> CheckResult {
fn cloud_sandbox_probe_check(probe: anyhow::Result<daytona::DaytonaKeyCheck>) -> CheckResult {
match probe {
Ok(check) if check.ok() => CheckResult {
name: "Cloud Sandbox".to_string(),
status: CheckStatus::Pass,
summary: format!("Daytona configured ({})", check.key_name),
details: Vec::new(),
name: "Cloud Sandbox".to_string(),
status: CheckStatus::Pass,
summary: format!("Daytona configured ({})", check.key_name),
details: Vec::new(),
remediation: None,
},
Ok(check) => CheckResult {
name: "Cloud Sandbox".to_string(),
status: CheckStatus::Error,
summary: "Daytona API key is missing required scopes".to_string(),
details: vec![CheckDetail::new(format!(
name: "Cloud Sandbox".to_string(),
status: CheckStatus::Error,
summary: "Daytona API key is missing required scopes".to_string(),
details: vec![CheckDetail::new(format!(
"missing: {}",
check.missing_display()
))],
@ -696,10 +695,10 @@ fn cloud_sandbox_probe_check(probe: anyhow::Result<daytona::DaytonaKeyCheck>) ->
Err(err) => {
if let Some(timeout) = err.downcast_ref::<daytona::DaytonaCredentialProbeTimeout>() {
return CheckResult {
name: "Cloud Sandbox".to_string(),
status: CheckStatus::Error,
summary: format!("timeout ({:?})", timeout.timeout()),
details: vec![CheckDetail::new("Daytona probe timed out".to_string())],
name: "Cloud Sandbox".to_string(),
status: CheckStatus::Error,
summary: format!("timeout ({:?})", timeout.timeout()),
details: vec![CheckDetail::new("Daytona probe timed out".to_string())],
remediation: Some(
"Verify DAYTONA_API_KEY value and Daytona reachability".to_string(),
),
@ -707,10 +706,10 @@ fn cloud_sandbox_probe_check(probe: anyhow::Result<daytona::DaytonaKeyCheck>) ->
}
CheckResult {
name: "Cloud Sandbox".to_string(),
status: CheckStatus::Error,
summary: "Daytona credential rejected".to_string(),
details: vec![CheckDetail::new(format!("{err:#}"))],
name: "Cloud Sandbox".to_string(),
status: CheckStatus::Error,
summary: "Daytona credential rejected".to_string(),
details: vec![CheckDetail::new(format!("{err:#}"))],
remediation: Some(
"Verify DAYTONA_API_KEY value and Daytona reachability".to_string(),
),
@ -781,10 +780,10 @@ async fn check_brave_search(state: &AppState) -> CheckResult {
};
let Some(api_key) = api_key else {
return CheckResult {
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Warning,
summary: "brave: optional, not configured".to_string(),
details: Vec::new(),
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Warning,
summary: "brave: optional, not configured".to_string(),
details: Vec::new(),
remediation: Some(
"Run `fabro secret set BRAVE_SEARCH_API_KEY` to enable web search".to_string(),
),
@ -816,10 +815,10 @@ async fn check_venice_search(state: &AppState) -> CheckResult {
};
let Some(api_key) = api_key else {
return CheckResult {
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Warning,
summary: "venice: optional, not configured".to_string(),
details: Vec::new(),
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Warning,
summary: "venice: optional, not configured".to_string(),
details: Vec::new(),
remediation: Some(
"Run `fabro secret set VENICE_API_KEY` to enable web search".to_string(),
),
@ -851,31 +850,31 @@ fn match_web_search_probe(
) -> CheckResult {
match probe {
Ok(Ok(response)) if response.status().is_success() => CheckResult {
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Pass,
summary: format!("{provider}: configured and reachable"),
details: Vec::new(),
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Pass,
summary: format!("{provider}: configured and reachable"),
details: Vec::new(),
remediation: None,
},
Ok(Ok(response)) => CheckResult {
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Warning,
summary: format!("{provider}: HTTP {}", response.status()),
details: Vec::new(),
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Warning,
summary: format!("{provider}: HTTP {}", response.status()),
details: Vec::new(),
remediation: Some(format!("Check {secret_name} and network connectivity")),
},
Ok(Err(err)) => CheckResult {
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Warning,
summary: format!("{provider}: connectivity error"),
details: vec![CheckDetail::new(format!("{err:#}"))],
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Warning,
summary: format!("{provider}: connectivity error"),
details: vec![CheckDetail::new(format!("{err:#}"))],
remediation: Some(format!("Check {secret_name} and network connectivity")),
},
Err(_) => CheckResult {
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Warning,
summary: format!("{provider}: timeout"),
details: vec![CheckDetail::new(format!(
name: WEB_SEARCH_CHECK_NAME.to_string(),
status: CheckStatus::Warning,
summary: format!("{provider}: timeout"),
details: vec![CheckDetail::new(format!(
"Web Search ({provider}) probe timed out"
))],
remediation: Some(format!("Check {secret_name} and network connectivity")),
@ -956,10 +955,10 @@ async fn diagnostic_secret(
name: &str,
) -> Result<Option<String>, CheckResult> {
state.vault_secret(name).await.map_err(|err| CheckResult {
name: check_name.to_string(),
status: CheckStatus::Error,
summary: "secret store unavailable".to_string(),
details: vec![CheckDetail::new(err.to_string())],
name: check_name.to_string(),
status: CheckStatus::Error,
summary: "secret store unavailable".to_string(),
details: vec![CheckDetail::new(err.to_string())],
remediation: Some("Check the Fabro database and retry".to_string()),
})
}

View file

@ -44,8 +44,8 @@ use crate::{
fn cli_tool_secrets() -> ToolSecrets {
ToolSecrets {
brave_search_api_key: std::env::var(EnvVars::BRAVE_SEARCH_API_KEY).ok(),
venice_api_key: std::env::var(EnvVars::VENICE_API_KEY).ok(),
search: search_settings_from_disk(),
venice_api_key: std::env::var(EnvVars::VENICE_API_KEY).ok(),
search: search_settings_from_disk(),
}
}

View file

@ -104,8 +104,8 @@ impl ToolHookCallback for ToolApprovalAdapter {
#[derive(Clone, Default, PartialEq, Eq)]
pub struct ToolSecrets {
pub brave_search_api_key: Option<String>,
pub venice_api_key: Option<String>,
pub search: SearchIntegrationSettings,
pub venice_api_key: Option<String>,
pub search: SearchIntegrationSettings,
}
impl std::fmt::Debug for ToolSecrets {
@ -125,8 +125,8 @@ impl std::fmt::Debug for ToolSecrets {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NativeToolOptions {
pub default_command_timeout_ms: u64,
pub max_command_timeout_ms: u64,
pub secrets: ToolSecrets,
pub max_command_timeout_ms: u64,
pub secrets: ToolSecrets,
}
impl NativeToolOptions {
@ -157,8 +157,8 @@ impl Default for NativeToolOptions {
fn default() -> Self {
Self {
default_command_timeout_ms: 10_000,
max_command_timeout_ms: 600_000,
secrets: ToolSecrets::default(),
max_command_timeout_ms: 600_000,
secrets: ToolSecrets::default(),
}
}
}
@ -447,12 +447,9 @@ mod tests {
let approval: ToolApprovalFn = Arc::new(|_name, _args| Err("denied".to_string()));
let adapter = ToolApprovalAdapter(approval);
let decision = adapter.pre_tool_use("shell", &serde_json::json!({})).await;
assert_eq!(
decision,
ToolHookDecision::Block {
reason: "denied".to_string(),
}
);
assert_eq!(decision, ToolHookDecision::Block {
reason: "denied".to_string(),
});
}
#[tokio::test]

View file

@ -174,20 +174,17 @@ mod tests {
let profile = Claude5Profile::new("claude-sonnet-5");
let mut names = profile.tool_registry().names();
names.sort();
assert_eq!(
names,
vec![
"Bash",
"Edit",
"Read",
"TaskCreate",
"TaskGet",
"TaskList",
"TaskUpdate",
"WebFetch",
"Write",
]
);
assert_eq!(names, vec![
"Bash",
"Edit",
"Read",
"TaskCreate",
"TaskGet",
"TaskList",
"TaskUpdate",
"WebFetch",
"Write",
]);
assert!(!names.iter().any(|name| name == "Grep" || name == "Glob"));
}

View file

@ -99,7 +99,7 @@ pub(crate) fn make_bash_tool(options: &NativeToolOptions) -> RegisteredTool {
"additionalProperties": false
}),
),
executor: Arc::new(move |args, ctx| {
executor: Arc::new(move |args, ctx| {
Box::pin(async move {
let command = tools::required_str(&args, "command")?;
let timeout_ms = args
@ -110,7 +110,7 @@ pub(crate) fn make_bash_tool(options: &NativeToolOptions) -> RegisteredTool {
tools::run_shell_command(&ctx, command, timeout_ms, None).await
})
}),
source: ToolSource::Native,
source: ToolSource::Native,
}
}
@ -221,7 +221,7 @@ pub(crate) fn make_agent_tool(
"additionalProperties": false
}),
),
executor: Arc::new(move |args, ctx| {
executor: Arc::new(move |args, ctx| {
let supervisor = supervisor.clone();
let session_factory = session_factory.clone();
Box::pin(async move {
@ -259,7 +259,7 @@ pub(crate) fn make_agent_tool(
}
})
}),
source: ToolSource::Native,
source: ToolSource::Native,
}
}
@ -330,7 +330,7 @@ pub(crate) fn make_task_output_tool(supervisor: SubAgentSupervisor) -> Registere
"additionalProperties": false
}),
),
executor: Arc::new(move |args, ctx| {
executor: Arc::new(move |args, ctx| {
let supervisor = supervisor.clone();
Box::pin(async move {
let task_id = tools::required_str(&args, "task_id")?;
@ -384,7 +384,7 @@ pub(crate) fn make_task_output_tool(supervisor: SubAgentSupervisor) -> Registere
}
})
}),
source: ToolSource::Native,
source: ToolSource::Native,
}
}
@ -406,7 +406,7 @@ pub(crate) fn make_task_stop_tool(supervisor: SubAgentSupervisor) -> RegisteredT
"additionalProperties": false
}),
),
executor: Arc::new(move |args, _ctx| {
executor: Arc::new(move |args, _ctx| {
let supervisor = supervisor.clone();
Box::pin(async move {
let task_id = tools::required_str(&args, "task_id")?;
@ -417,7 +417,7 @@ pub(crate) fn make_task_stop_tool(supervisor: SubAgentSupervisor) -> RegisteredT
Ok(format!("Agent {task_id} stopped."))
})
}),
source: ToolSource::Native,
source: ToolSource::Native,
}
}
@ -448,7 +448,7 @@ pub(crate) fn make_send_message_tool(supervisor: SubAgentSupervisor) -> Register
"additionalProperties": false
}),
),
executor: Arc::new(move |args, _ctx| {
executor: Arc::new(move |args, _ctx| {
let supervisor = supervisor.clone();
Box::pin(async move {
let recipient = tools::required_str(&args, "to")?;
@ -459,7 +459,7 @@ pub(crate) fn make_send_message_tool(supervisor: SubAgentSupervisor) -> Register
Ok(format!("Message sent to agent {recipient}."))
})
}),
source: ToolSource::Native,
source: ToolSource::Native,
}
}
@ -468,6 +468,7 @@ mod tests {
use std::collections::BTreeSet;
use std::sync::Mutex;
use fabro_types::settings::VeniceSearchEngine;
use serde_json::json;
use tokio_util::sync::CancellationToken;
@ -478,7 +479,6 @@ mod tests {
use crate::todo_tools::{
make_task_create_tool, make_task_get_tool, make_task_list_tool, make_task_update_tool,
};
use fabro_types::settings::VeniceSearchEngine;
fn property_names(tool: &RegisteredTool) -> BTreeSet<&str> {
tool.definition.parameters["properties"]
@ -513,12 +513,12 @@ mod tests {
fn context() -> ToolContext {
ToolContext {
env: Arc::new(MockSandbox::default()) as Arc<dyn Sandbox>,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: Some("root".to_string()),
root_session_id: Some("root".to_string()),
tool_call_id: Some("call".to_string()),
env: Arc::new(MockSandbox::default()) as Arc<dyn Sandbox>,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: Some("root".to_string()),
root_session_id: Some("root".to_string()),
tool_call_id: Some("call".to_string()),
agent_event_emitter: None,
}
}
@ -526,16 +526,13 @@ mod tests {
#[test]
fn core_adapter_schemas_match_the_claude5_contract() {
let options = NativeToolOptions::for_profile(fabro_model::AgentProfileKind::Claude5);
assert_schema(
&make_read_tool(),
&["file_path", "limit", "offset"],
&["file_path"],
);
assert_schema(
&make_write_tool(),
&["content", "file_path"],
&["content", "file_path"],
);
assert_schema(&make_read_tool(), &["file_path", "limit", "offset"], &[
"file_path",
]);
assert_schema(&make_write_tool(), &["content", "file_path"], &[
"content",
"file_path",
]);
assert_schema(
&make_edit_tool(),
&["file_path", "new_string", "old_string", "replace_all"],
@ -547,11 +544,9 @@ mod tests {
bash.definition.parameters["properties"]["timeout"]["maximum"],
600_000
);
assert_schema(
&make_web_fetch_tool(None),
&["prompt", "url"],
&["prompt", "url"],
);
assert_schema(&make_web_fetch_tool(None), &["prompt", "url"], &[
"prompt", "url",
]);
assert_schema(
&make_web_search_tool(SearchBackend::brave("key".to_string())),
&["query"],
@ -613,17 +608,13 @@ mod tests {
&["block", "task_id", "timeout"],
&["block", "task_id", "timeout"],
);
assert_schema(
&make_task_stop_tool(supervisor.clone()),
&["task_id"],
&["task_id"],
);
assert_schema(&make_task_stop_tool(supervisor.clone()), &["task_id"], &[
"task_id",
]);
let send_message = make_send_message_tool(supervisor);
assert_schema(
&send_message,
&["message", "summary", "to"],
&["message", "to"],
);
assert_schema(&send_message, &["message", "summary", "to"], &[
"message", "to",
]);
assert!(
send_message
.definition

View file

@ -12,9 +12,9 @@ use tokio::task;
use crate::config::NativeToolOptions;
use crate::sandbox::{ExecStreamingResult, GrepOptions};
use crate::web_search::{SearchBackend, make_web_search_tool};
use crate::tool_registry::{RegisteredTool, ToolContext, ToolRegistry, ToolSource};
use crate::types::AgentEvent;
use crate::web_search::{SearchBackend, make_web_search_tool};
const MAX_WEB_FETCH_BYTES: usize = 100 * 1024;
const MAX_READ_MANY_FILES_CONCURRENCY: usize = 8;
@ -23,7 +23,7 @@ pub(crate) const DEFAULT_READ_LINES: usize = 2000;
/// Configuration for the optional LLM-based summarizer used by `web_fetch`.
#[derive(Clone)]
pub struct WebFetchSummarizer {
pub client: Client,
pub client: Client,
pub model_id: ModelHandle,
}
@ -512,9 +512,9 @@ pub fn make_glob_tool() -> RegisteredTool {
pub(crate) fn make_read_many_files_tool() -> RegisteredTool {
RegisteredTool {
definition: ToolDefinition {
name: "read_many_files".into(),
name: "read_many_files".into(),
description: "Read multiple files at once".into(),
parameters: serde_json::json!({
parameters: serde_json::json!({
"type": "object",
"properties": {
"paths": {
@ -526,7 +526,7 @@ pub(crate) fn make_read_many_files_tool() -> RegisteredTool {
"required": ["paths"]
}),
},
executor: Arc::new(|args, ctx| {
executor: Arc::new(|args, ctx| {
Box::pin(async move {
let paths: Vec<String> = args["paths"]
.as_array()
@ -565,7 +565,7 @@ pub(crate) fn make_read_many_files_tool() -> RegisteredTool {
Ok(output)
})
}),
source: ToolSource::Native,
source: ToolSource::Native,
}
}
@ -573,9 +573,9 @@ pub(crate) fn make_read_many_files_tool() -> RegisteredTool {
pub(crate) fn make_list_dir_tool() -> RegisteredTool {
RegisteredTool {
definition: ToolDefinition {
name: "list_dir".into(),
name: "list_dir".into(),
description: "List directory contents with depth control".into(),
parameters: serde_json::json!({
parameters: serde_json::json!({
"type": "object",
"properties": {
"path": {"type": "string", "description": "Directory path to list"},
@ -584,7 +584,7 @@ pub(crate) fn make_list_dir_tool() -> RegisteredTool {
"required": ["path"]
}),
},
executor: Arc::new(|args, ctx| {
executor: Arc::new(|args, ctx| {
Box::pin(async move {
let path = required_str(&args, "path")?;
let depth = optional_usize_arg(&args, "depth")?;
@ -607,7 +607,7 @@ pub(crate) fn make_list_dir_tool() -> RegisteredTool {
Ok(lines.join("\n"))
})
}),
source: ToolSource::Native,
source: ToolSource::Native,
}
}
@ -725,7 +725,6 @@ mod tests {
use super::*;
use crate::config::{NativeToolOptions, SessionOptions, ToolSecrets};
use crate::web_search::make_web_search_tool_with_api_key;
use crate::event::{Emitter, SessionBoundEmitter};
use crate::local_sandbox::LocalSandbox;
use crate::sandbox::*;
@ -733,6 +732,7 @@ mod tests {
use crate::tool_registry::ToolContext;
use crate::truncation;
use crate::types::SessionEvent;
use crate::web_search::make_web_search_tool_with_api_key;
#[test]
fn core_tool_descriptions_include_actionable_guidance() {
@ -823,18 +823,15 @@ mod tests {
files,
..Default::default()
});
let result = (tool.executor)(
serde_json::json!({"file_path": "/test.txt"}),
ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
let result = (tool.executor)(serde_json::json!({"file_path": "/test.txt"}), ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
})
.await;
assert_eq!(result.unwrap(), "1 | hello\n2 | world\n");
}
@ -851,18 +848,15 @@ mod tests {
..Default::default()
});
let result = (tool.executor)(
serde_json::json!({"file_path": "/test.txt"}),
ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
let result = (tool.executor)(serde_json::json!({"file_path": "/test.txt"}), ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
})
.await
.unwrap();
@ -903,12 +897,12 @@ mod tests {
let result = (tool.executor)(
serde_json::json!({"file_path": "/out.txt", "content": "hello"}),
ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -938,12 +932,12 @@ mod tests {
"new_string": "goodbye"
}),
ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -1033,12 +1027,12 @@ mod tests {
"replace_all": true
}),
ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -1066,12 +1060,12 @@ mod tests {
"new_string": "goodbye"
}),
ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -1100,8 +1094,8 @@ mod tests {
root_session_id: Some("test-session".to_string()),
tool_call_id: Some("call_1".to_string()),
agent_event_emitter: Some(Arc::new(SessionBoundEmitter {
emitter: emitter.clone(),
session_id: "test-session".to_string(),
emitter: emitter.clone(),
session_id: "test-session".to_string(),
tool_call_id: Some("call_1".to_string()),
})),
..shell_context(env)
@ -1130,9 +1124,9 @@ mod tests {
async fn shell_success_returns_ok_with_metadata_and_separate_streams() {
let tool = make_shell_tool();
let env: Arc<dyn Sandbox> = mock_sandbox_with(ExecResult {
stdout: "hello".into(),
stderr: "a warning".into(),
exit_code: Some(0),
stdout: "hello".into(),
stderr: "a warning".into(),
exit_code: Some(0),
termination: CommandTermination::Exited,
duration_ms: 10,
});
@ -1154,9 +1148,9 @@ mod tests {
async fn shell_forwards_command_without_stream_redirection_wrapper() {
let tool = make_shell_tool();
let env = mock_sandbox_with(ExecResult {
stdout: String::new(),
stderr: String::new(),
exit_code: Some(0),
stdout: String::new(),
stderr: String::new(),
exit_code: Some(0),
termination: CommandTermination::Exited,
duration_ms: 1,
});
@ -1182,12 +1176,12 @@ mod tests {
let _result = (tool.executor)(
serde_json::json!({"command": "sleep 1", "timeout_ms": 5000}),
ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -1200,9 +1194,9 @@ mod tests {
let tool = make_shell_tool();
let env: Arc<dyn Sandbox> = Arc::new(MockSandbox {
exec_result: ExecResult {
stdout: "error".into(),
stderr: String::new(),
exit_code: Some(1),
stdout: "error".into(),
stderr: String::new(),
exit_code: Some(1),
termination: CommandTermination::Exited,
duration_ms: 10,
},
@ -1221,9 +1215,9 @@ mod tests {
async fn shell_timeout_returns_error_with_partial_output() {
let tool = make_shell_tool();
let env: Arc<dyn Sandbox> = mock_sandbox_with(ExecResult {
stdout: "partial".into(),
stderr: String::new(),
exit_code: None,
stdout: "partial".into(),
stderr: String::new(),
exit_code: None,
termination: CommandTermination::TimedOut,
duration_ms: 10000,
});
@ -1243,9 +1237,9 @@ mod tests {
async fn shell_cancellation_returns_error_with_partial_output() {
let tool = make_shell_tool();
let env: Arc<dyn Sandbox> = mock_sandbox_with(ExecResult {
stdout: "partial".into(),
stderr: String::new(),
exit_code: None,
stdout: "partial".into(),
stderr: String::new(),
exit_code: None,
termination: CommandTermination::Cancelled,
duration_ms: 42,
});
@ -1297,9 +1291,9 @@ mod tests {
async fn shell_emits_process_event_with_typed_outcome_and_redacted_tails() {
let tool = make_shell_tool();
let env: Arc<dyn Sandbox> = mock_sandbox_with(ExecResult {
stdout: "out".into(),
stderr: "boom key=AKIAYRWQG5EJLPZLBYNP".into(),
exit_code: Some(7),
stdout: "out".into(),
stderr: "boom key=AKIAYRWQG5EJLPZLBYNP".into(),
exit_code: Some(7),
termination: CommandTermination::Exited,
duration_ms: 12,
});
@ -1339,9 +1333,9 @@ mod tests {
let tool = make_shell_tool();
let env: Arc<dyn Sandbox> = Arc::new(MockSandbox {
exec_result: ExecResult {
stdout: "interleaved".into(),
stderr: String::new(),
exit_code: Some(0),
stdout: "interleaved".into(),
stderr: String::new(),
exit_code: Some(0),
termination: CommandTermination::Exited,
duration_ms: 5,
},
@ -1457,12 +1451,12 @@ mod tests {
let _result = (tool.executor)(
serde_json::json!({"command": "echo $MY_KEY"}),
ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: Some(Arc::new(crate::StaticEnvProvider(tool_env.clone()))),
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: Some(Arc::new(crate::StaticEnvProvider(tool_env.clone()))),
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -1505,12 +1499,12 @@ mod tests {
let _result = (tool.executor)(
serde_json::json!({"command": "echo $GITHUB_TOKEN"}),
ToolContext {
env: env.clone(),
cancel: CancellationToken::new(),
tool_env_provider: Some(provider.clone()),
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env.clone(),
cancel: CancellationToken::new(),
tool_env_provider: Some(provider.clone()),
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -1526,12 +1520,12 @@ mod tests {
let _result = (tool.executor)(
serde_json::json!({"command": "echo $GITHUB_TOKEN"}),
ToolContext {
env: env.clone(),
cancel: CancellationToken::new(),
tool_env_provider: Some(provider),
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env.clone(),
cancel: CancellationToken::new(),
tool_env_provider: Some(provider),
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -1581,18 +1575,15 @@ mod tests {
..Default::default()
});
let result = (tool.executor)(
serde_json::json!({"file_path": "/test.txt"}),
ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: Some(Arc::new(FailingToolEnvProvider)),
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
let result = (tool.executor)(serde_json::json!({"file_path": "/test.txt"}), ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: Some(Arc::new(FailingToolEnvProvider)),
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
})
.await;
assert_eq!(result.unwrap(), "1 | hello\n");
@ -1603,18 +1594,15 @@ mod tests {
let tool = make_shell_tool();
let env = Arc::new(MockSandbox::default());
let env_clone: Arc<dyn Sandbox> = env.clone();
let _result = (tool.executor)(
serde_json::json!({"command": "echo hello"}),
ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
let _result = (tool.executor)(serde_json::json!({"command": "echo hello"}), ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
})
.await;
let captured = env.captured_env_vars.lock().unwrap().clone();
assert_eq!(captured, None);
@ -1625,9 +1613,9 @@ mod tests {
let tool = make_web_fetch_tool(None);
let env = Arc::new(MockSandbox {
exec_result: ExecResult {
stdout: "fetched content".into(),
stderr: String::new(),
exit_code: Some(0),
stdout: "fetched content".into(),
stderr: String::new(),
exit_code: Some(0),
termination: CommandTermination::Exited,
duration_ms: 100,
},
@ -1639,12 +1627,12 @@ mod tests {
let _result = (tool.executor)(
serde_json::json!({"url": "https://example.com"}),
ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: Some(Arc::new(crate::StaticEnvProvider(tool_env.clone()))),
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: Some(Arc::new(crate::StaticEnvProvider(tool_env.clone()))),
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -1663,18 +1651,15 @@ mod tests {
],
..Default::default()
});
let result = (tool.executor)(
serde_json::json!({"pattern": "fn"}),
ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
let result = (tool.executor)(serde_json::json!({"pattern": "fn"}), ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
})
.await;
let output = result.unwrap();
assert!(output.contains("src/main.rs:10:fn main()"));
@ -1688,18 +1673,15 @@ mod tests {
glob_results: vec!["src/main.rs".into(), "src/lib.rs".into()],
..Default::default()
});
let result = (tool.executor)(
serde_json::json!({"pattern": "src/**/*.rs"}),
ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
let result = (tool.executor)(serde_json::json!({"pattern": "src/**/*.rs"}), ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
})
.await;
let output = result.unwrap();
assert!(output.contains("src/main.rs"));
@ -1719,18 +1701,15 @@ mod tests {
async fn web_search_missing_query_returns_error() {
let tool = make_web_search_tool_with_api_key("fake-key".into());
let env: Arc<dyn Sandbox> = Arc::new(MockSandbox::default());
let result = (tool.executor)(
serde_json::json!({}),
ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
let result = (tool.executor)(serde_json::json!({}), ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
})
.await;
let err = result.unwrap_err();
assert!(
@ -1756,18 +1735,15 @@ mod tests {
.get("web_search")
.expect("web_search should be registered");
let env: Arc<dyn Sandbox> = Arc::new(MockSandbox::default());
let result = (tool.executor)(
serde_json::json!({}),
ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
let result = (tool.executor)(serde_json::json!({}), ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
})
.await;
let err = result.unwrap_err();
@ -1782,9 +1758,9 @@ mod tests {
let tool = make_web_fetch_tool(None);
let env = Arc::new(MockSandbox {
exec_result: ExecResult {
stdout: "<html><body><h1>hello</h1></body></html>".into(),
stderr: String::new(),
exit_code: Some(0),
stdout: "<html><body><h1>hello</h1></body></html>".into(),
stderr: String::new(),
exit_code: Some(0),
termination: CommandTermination::Exited,
duration_ms: 100,
},
@ -1794,12 +1770,12 @@ mod tests {
let result = (tool.executor)(
serde_json::json!({"url": "https://example.com"}),
ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -1860,12 +1836,12 @@ mod tests {
let _result = (tool.executor)(
serde_json::json!({"url": "https://example.com", "timeout_ms": 15000}),
ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -1886,12 +1862,12 @@ mod tests {
let _result = (tool.executor)(
serde_json::json!({"url": "https://example.com", "timeout_ms": 120_000}),
ToolContext {
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
env: env_clone,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
@ -1910,9 +1886,9 @@ mod tests {
let tool = make_web_fetch_tool(None);
let env: Arc<dyn Sandbox> = Arc::new(MockSandbox {
exec_result: ExecResult {
stdout: large_content,
stderr: String::new(),
exit_code: Some(0),
stdout: large_content,
stderr: String::new(),
exit_code: Some(0),
termination: CommandTermination::Exited,
duration_ms: 100,
},
@ -1941,9 +1917,9 @@ mod tests {
let tool = make_web_fetch_tool(None);
let env: Arc<dyn Sandbox> = Arc::new(MockSandbox {
exec_result: ExecResult {
stdout: String::new(),
stderr: "curl: (6) Could not resolve host".into(),
exit_code: Some(6),
stdout: String::new(),
stderr: "curl: (6) Could not resolve host".into(),
exit_code: Some(6),
termination: CommandTermination::Exited,
duration_ms: 100,
},
@ -1985,16 +1961,17 @@ mod tests {
client,
model_id: ModelHandle::ByName {
provider: ProviderId::anthropic(),
model: "mock-model".to_string(),
model: "mock-model".to_string(),
},
};
let tool = make_web_fetch_tool(Some(summarizer));
let env: Arc<dyn Sandbox> = Arc::new(MockSandbox {
exec_result: ExecResult {
stdout: "<html><body><p>Lots of content about Rust...</p></body></html>".into(),
stderr: String::new(),
exit_code: Some(0),
stdout: "<html><body><p>Lots of content about Rust...</p></body></html>"
.into(),
stderr: String::new(),
exit_code: Some(0),
termination: CommandTermination::Exited,
duration_ms: 100,
},
@ -2025,10 +2002,11 @@ mod tests {
let tool = make_web_fetch_tool(None);
let env: Arc<dyn Sandbox> = Arc::new(MockSandbox {
exec_result: ExecResult {
stdout: "<html><body><p>Rust is a systems programming language.</p></body></html>"
.into(),
stderr: String::new(),
exit_code: Some(0),
stdout:
"<html><body><p>Rust is a systems programming language.</p></body></html>"
.into(),
stderr: String::new(),
exit_code: Some(0),
termination: CommandTermination::Exited,
duration_ms: 100,
},
@ -2068,7 +2046,7 @@ mod tests {
// "other_provider" is the default — it rejects all requests.
let default_provider: Arc<dyn ProviderAdapter> = Arc::new(MockErrorProvider {
error: LlmError::Provider {
kind: ProviderErrorKind::NotFound,
kind: ProviderErrorKind::NotFound,
detail: Box::new(ProviderErrorDetail::new(
"model not found",
"other_provider",
@ -2092,16 +2070,16 @@ mod tests {
client,
model_id: ModelHandle::ByName {
provider: ProviderId::anthropic(),
model: "target-model".to_string(),
model: "target-model".to_string(),
},
};
let tool = make_web_fetch_tool(Some(summarizer));
let env: Arc<dyn Sandbox> = Arc::new(MockSandbox {
exec_result: ExecResult {
stdout: "<html><body><p>Page content</p></body></html>".into(),
stderr: String::new(),
exit_code: Some(0),
stdout: "<html><body><p>Page content</p></body></html>".into(),
stderr: String::new(),
exit_code: Some(0),
termination: CommandTermination::Exited,
duration_ms: 100,
},

View file

@ -24,12 +24,12 @@ const MAX_RESULTS: u64 = 20;
#[derive(Clone, Debug)]
pub(crate) enum SearchBackend {
Brave {
api_key: String,
api_key: String,
search_url: String,
},
Venice {
api_key: String,
engine: VeniceSearchEngine,
api_key: String,
engine: VeniceSearchEngine,
search_url: String,
},
}
@ -207,10 +207,10 @@ fn format_brave_results(body: &serde_json::Value) -> String {
results
.iter()
.map(|result| SearchHit {
title: json_str(result, "title"),
url: json_str(result, "url"),
title: json_str(result, "title"),
url: json_str(result, "url"),
description: json_str(result, "description"),
date: None,
date: None,
})
.collect()
}))
@ -222,20 +222,20 @@ fn format_venice_results(body: &serde_json::Value) -> String {
results
.iter()
.map(|result| SearchHit {
title: json_str(result, "title"),
url: json_str(result, "url"),
title: json_str(result, "title"),
url: json_str(result, "url"),
description: json_str(result, "content"),
date: optional_json_str(result, "date"),
date: optional_json_str(result, "date"),
})
.collect()
}))
}
struct SearchHit {
title: String,
url: String,
title: String,
url: String,
description: String,
date: Option<String>,
date: Option<String>,
}
fn format_hits(hits: Option<Vec<SearchHit>>) -> String {
@ -364,8 +364,8 @@ mod tests {
fn secrets(brave: Option<&str>, venice: Option<&str>, provider: SearchProvider) -> ToolSecrets {
ToolSecrets {
brave_search_api_key: brave.map(str::to_string),
venice_api_key: venice.map(str::to_string),
search: SearchIntegrationSettings {
venice_api_key: venice.map(str::to_string),
search: SearchIntegrationSettings {
provider,
venice_engine: VeniceSearchEngine::Brave,
},
@ -374,18 +374,15 @@ mod tests {
async fn execute(tool: &RegisteredTool, args: serde_json::Value) -> Result<String, String> {
let env: Arc<dyn Sandbox> = Arc::new(MockSandbox::default());
(tool.executor)(
args,
ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
},
)
(tool.executor)(args, ToolContext {
env,
cancel: CancellationToken::new(),
tool_env_provider: None,
session_id: None,
root_session_id: None,
tool_call_id: None,
agent_event_emitter: None,
})
.await
}

View file

@ -26,22 +26,22 @@ type Provider = ProviderId;
#[derive(Clone)]
struct OpenAiTwinOptions {
base_url: String,
api_key: String,
api_key: String,
}
fn summarizer_model_id(provider: &Provider) -> ModelHandle {
match provider.as_str() {
ProviderId::OPENAI | "moonshot" | "zai" | "minimax" | "inception" => ModelHandle::ByName {
provider: ProviderId::openai(),
model: "gpt-5.4-mini".to_string(),
model: "gpt-5.4-mini".to_string(),
},
ProviderId::GEMINI => ModelHandle::ByName {
provider: ProviderId::gemini(),
model: "gemini-3-flash-preview".to_string(),
model: "gemini-3-flash-preview".to_string(),
},
ProviderId::ANTHROPIC => ModelHandle::ByName {
provider: ProviderId::anthropic(),
model: "claude-haiku-4-5".to_string(),
model: "claude-haiku-4-5".to_string(),
},
other => panic!("unexpected provider {other}"),
}
@ -49,7 +49,7 @@ fn summarizer_model_id(provider: &Provider) -> ModelHandle {
fn build_summarizer(provider: &Provider, client: &Client) -> WebFetchSummarizer {
WebFetchSummarizer {
client: client.clone(),
client: client.clone(),
model_id: summarizer_model_id(provider),
}
}
@ -170,13 +170,12 @@ fn make_openai_compatible_twin_session(
// twin fixture so the profile can resolve the same OpenAI-compatible
// codec that the manually registered adapter uses.
let mut settings = LlmCatalogSettings::default();
settings.providers.insert(
provider.to_string(),
ProviderCatalogSettings {
settings
.providers
.insert(provider.to_string(), ProviderCatalogSettings {
enabled: Some(true),
..ProviderCatalogSettings::default()
},
);
});
let catalog = Arc::new(
Catalog::from_builtin_with_overrides(&settings)
.expect("OpenAI-compatible twin catalog should build"),

View file

@ -238,8 +238,8 @@ async fn tool_secrets_from_configured_sources(vault: &Arc<AsyncRwLock<Vault>>) -
let vault = vault.read().await;
ToolSecrets {
brave_search_api_key: vault.get(EnvVars::BRAVE_SEARCH_API_KEY).map(str::to_string),
venice_api_key: vault.get(EnvVars::VENICE_API_KEY).map(str::to_string),
search: fabro_agent::search_settings_from_disk(),
venice_api_key: vault.get(EnvVars::VENICE_API_KEY).map(str::to_string),
search: fabro_agent::search_settings_from_disk(),
}
}

View file

@ -691,11 +691,8 @@ fn main() {
("AskFabro", "fabro_types::AskFabro", &[]),
("Automation", "fabro_automation::Automation", &[]),
("AutomationRef", "fabro_types::AutomationRef", &[]),
(
"AutomationTarget",
"fabro_automation::AutomationTarget",
&[],
),
("AutomationTarget", "fabro_automation::AutomationTarget", &[
]),
(
"AutomationTrigger",
"fabro_automation::AutomationTrigger",

View file

@ -13,25 +13,25 @@ use super::LogFilter;
#[serde(deny_unknown_fields)]
pub struct ServerLayer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub listen: Option<ServerListenLayer>,
pub listen: Option<ServerListenLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub api: Option<ServerApiLayer>,
pub api: Option<ServerApiLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub web: Option<ServerWebLayer>,
pub web: Option<ServerWebLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auth: Option<ServerAuthLayer>,
pub auth: Option<ServerAuthLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sandbox: Option<ServerSandboxLayer>,
pub sandbox: Option<ServerSandboxLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub storage: Option<ServerStorageLayer>,
pub storage: Option<ServerStorageLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub artifacts: Option<ServerArtifactsLayer>,
pub artifacts: Option<ServerArtifactsLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub slatedb: Option<ServerSlateDbLayer>,
pub slatedb: Option<ServerSlateDbLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scheduler: Option<ServerSchedulerLayer>,
pub scheduler: Option<ServerSchedulerLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub logging: Option<ServerLoggingLayer>,
pub logging: Option<ServerLoggingLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub integrations: Option<ServerIntegrationsLayer>,
}
@ -67,7 +67,7 @@ pub struct ServerWebLayer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
pub url: Option<String>,
}
/// `[server.auth]` — cohesive server auth surface.
@ -81,7 +81,7 @@ pub struct ServerAuthLayer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub methods: Option<Vec<ServerAuthMethod>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub github: Option<ServerAuthGithubLayer>,
pub github: Option<ServerAuthGithubLayer>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
@ -103,9 +103,9 @@ pub struct ServerSandboxLayer {
#[serde(deny_unknown_fields)]
pub struct ServerSandboxProvidersLayer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub local: Option<ServerSandboxProviderLayer>,
pub local: Option<ServerSandboxProviderLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub docker: Option<ServerSandboxProviderLayer>,
pub docker: Option<ServerSandboxProviderLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub daytona: Option<ServerSandboxProviderLayer>,
}
@ -132,11 +132,11 @@ pub struct ServerArtifactsLayer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider: Option<ObjectStoreProvider>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub prefix: Option<String>,
pub prefix: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub local: Option<ObjectStoreLocalLayer>,
pub local: Option<ObjectStoreLocalLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub s3: Option<ObjectStoreS3Layer>,
pub s3: Option<ObjectStoreS3Layer>,
}
/// `[server.slatedb]` — SlateDB bottomless storage plus tunables.
@ -144,17 +144,17 @@ pub struct ServerArtifactsLayer {
#[serde(deny_unknown_fields)]
pub struct ServerSlateDbLayer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider: Option<ObjectStoreProvider>,
pub provider: Option<ObjectStoreProvider>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub prefix: Option<String>,
pub prefix: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub flush_interval: Option<Duration>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub local: Option<ObjectStoreLocalLayer>,
pub local: Option<ObjectStoreLocalLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub s3: Option<ObjectStoreS3Layer>,
pub s3: Option<ObjectStoreS3Layer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub disk_cache: Option<bool>,
pub disk_cache: Option<bool>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
@ -170,11 +170,11 @@ pub struct ObjectStoreLocalLayer {
#[serde(deny_unknown_fields)]
pub struct ObjectStoreS3Layer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bucket: Option<String>,
pub bucket: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub region: Option<String>,
pub region: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub endpoint: Option<String>,
pub endpoint: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path_style: Option<bool>,
}
@ -192,7 +192,7 @@ pub struct ServerSchedulerLayer {
#[serde(deny_unknown_fields)]
pub struct ServerLoggingLayer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub level: Option<LogFilter>,
pub level: Option<LogFilter>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub destination: Option<LogDestination>,
}
@ -205,7 +205,7 @@ pub struct ServerIntegrationsLayer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub github: Option<GithubIntegrationLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub slack: Option<SlackIntegrationLayer>,
pub slack: Option<SlackIntegrationLayer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub search: Option<SearchIntegrationLayer>,
}
@ -216,17 +216,17 @@ pub struct ServerIntegrationsLayer {
#[serde(deny_unknown_fields)]
pub struct GithubIntegrationLayer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub strategy: Option<GithubIntegrationStrategy>,
pub strategy: Option<GithubIntegrationStrategy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub app_id: Option<String>,
pub app_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub client_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub slug: Option<String>,
pub slug: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub webhooks: Option<IntegrationWebhooksLayer>,
pub webhooks: Option<IntegrationWebhooksLayer>,
}
/// `[server.integrations.slack]` — Slack workspace credentials and defaults.
@ -234,7 +234,7 @@ pub struct GithubIntegrationLayer {
#[serde(deny_unknown_fields)]
pub struct SlackIntegrationLayer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default_channel: Option<String>,
}
@ -244,7 +244,7 @@ pub struct SlackIntegrationLayer {
#[serde(deny_unknown_fields)]
pub struct SearchIntegrationLayer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider: Option<SearchProvider>,
pub provider: Option<SearchProvider>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub venice_engine: Option<VeniceSearchEngine>,
}

View file

@ -51,7 +51,7 @@ pub fn resolve_server(layer: &ServerLayer, errors: &mut Vec<ResolveError>) -> Se
.expect("defaults.toml should provide server.scheduler.max_concurrent_runs"),
},
logging: ServerLoggingSettings {
level: layer
level: layer
.logging
.as_ref()
.and_then(|logging| logging.level.as_ref())
@ -70,10 +70,10 @@ fn resolve_sandbox(layer: Option<&ServerSandboxLayer>) -> ServerSandboxSettings
let providers = layer.and_then(|sandbox| sandbox.providers.as_ref());
ServerSandboxSettings {
providers: ServerSandboxProvidersSettings {
local: resolve_sandbox_provider(
local: resolve_sandbox_provider(
providers.and_then(|providers| providers.local.as_ref()),
),
docker: resolve_sandbox_provider(
docker: resolve_sandbox_provider(
providers.and_then(|providers| providers.docker.as_ref()),
),
daytona: resolve_sandbox_provider(
@ -150,7 +150,7 @@ fn resolve_auth(
let methods = if let Some(mut methods) = layer.and_then(|auth| auth.methods.clone()) {
if methods.is_empty() {
errors.push(ResolveError::Invalid {
path: "server.auth.methods".to_string(),
path: "server.auth.methods".to_string(),
reason: "must not be empty".to_string(),
});
}
@ -169,7 +169,7 @@ fn resolve_auth(
.unwrap_or_default();
if methods.contains(&ServerAuthMethod::Github) && github.allowed_usernames.is_empty() {
errors.push(ResolveError::Invalid {
path: "server.auth.github.allowed_usernames".to_string(),
path: "server.auth.github.allowed_usernames".to_string(),
reason: "must not be empty when github auth is enabled".to_string(),
});
}
@ -198,7 +198,7 @@ fn validate_github_webhook_strategy(
&& github.app_id.is_none()
{
errors.push(ResolveError::Invalid {
path: "server.integrations.github.app_id".to_string(),
path: "server.integrations.github.app_id".to_string(),
reason: "must be set when server.integrations.github.webhooks.strategy is configured"
.to_string(),
});
@ -208,7 +208,7 @@ fn validate_github_webhook_strategy(
&& api_layer.and_then(|api| api.url.as_ref()).is_none()
{
errors.push(ResolveError::Invalid {
path: "server.api.url".to_string(),
path: "server.api.url".to_string(),
reason:
"must be set when server.integrations.github.webhooks.strategy = \"server_url\""
.to_string(),
@ -348,20 +348,20 @@ fn resolve_integrations(layer: Option<&ServerIntegrationsLayer>) -> ServerIntegr
);
warn_if_demoted_template("server.integrations.github.slug", github.slug.as_deref());
GithubIntegrationSettings {
enabled: github.enabled.unwrap_or(true),
strategy: github.strategy.unwrap_or_default(),
app_id: github.app_id.clone(),
enabled: github.enabled.unwrap_or(true),
strategy: github.strategy.unwrap_or_default(),
app_id: github.app_id.clone(),
client_id: github.client_id.clone(),
slug: github.slug.clone(),
webhooks: github.webhooks.as_ref().map(resolve_github_webhooks),
slug: github.slug.clone(),
webhooks: github.webhooks.as_ref().map(resolve_github_webhooks),
}
})
.unwrap_or_default(),
slack: layer
slack: layer
.and_then(|integrations| integrations.slack.as_ref())
.map_or(
SlackIntegrationSettings {
enabled: false,
enabled: false,
default_channel: None,
},
|slack| {
@ -370,7 +370,7 @@ fn resolve_integrations(layer: Option<&ServerIntegrationsLayer>) -> ServerIntegr
slack.default_channel.as_deref(),
);
SlackIntegrationSettings {
enabled: slack.enabled.unwrap_or(true),
enabled: slack.enabled.unwrap_or(true),
default_channel: slack.default_channel.clone(),
}
},
@ -378,7 +378,7 @@ fn resolve_integrations(layer: Option<&ServerIntegrationsLayer>) -> ServerIntegr
search: layer
.and_then(|integrations| integrations.search.as_ref())
.map(|search| SearchIntegrationSettings {
provider: search.provider.unwrap_or_default(),
provider: search.provider.unwrap_or_default(),
venice_engine: search.venice_engine.unwrap_or_default(),
})
.unwrap_or_default(),

View file

@ -22,16 +22,16 @@ use super::duration::Duration;
/// (tests).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ServerNamespace {
pub listen: ServerListenSettings,
pub api: ServerApiSettings,
pub web: ServerWebSettings,
pub auth: ServerAuthSettings,
pub sandbox: ServerSandboxSettings,
pub storage: ServerStorageSettings,
pub artifacts: ServerArtifactsSettings,
pub slatedb: ServerSlateDbSettings,
pub scheduler: ServerSchedulerSettings,
pub logging: ServerLoggingSettings,
pub listen: ServerListenSettings,
pub api: ServerApiSettings,
pub web: ServerWebSettings,
pub auth: ServerAuthSettings,
pub sandbox: ServerSandboxSettings,
pub storage: ServerStorageSettings,
pub artifacts: ServerArtifactsSettings,
pub slatedb: ServerSlateDbSettings,
pub scheduler: ServerSchedulerSettings,
pub logging: ServerLoggingSettings,
pub integrations: ServerIntegrationsSettings,
}
@ -43,16 +43,16 @@ impl ServerNamespace {
#[must_use]
pub fn test_default() -> Self {
Self {
listen: ServerListenSettings::default(),
api: ServerApiSettings::default(),
web: ServerWebSettings::default(),
auth: ServerAuthSettings::default(),
sandbox: ServerSandboxSettings::default(),
storage: ServerStorageSettings::default(),
artifacts: ServerArtifactsSettings::default(),
slatedb: ServerSlateDbSettings::default(),
scheduler: ServerSchedulerSettings::default(),
logging: ServerLoggingSettings::default(),
listen: ServerListenSettings::default(),
api: ServerApiSettings::default(),
web: ServerWebSettings::default(),
auth: ServerAuthSettings::default(),
sandbox: ServerSandboxSettings::default(),
storage: ServerStorageSettings::default(),
artifacts: ServerArtifactsSettings::default(),
slatedb: ServerSlateDbSettings::default(),
scheduler: ServerSchedulerSettings::default(),
logging: ServerLoggingSettings::default(),
integrations: ServerIntegrationsSettings::default(),
}
}
@ -89,13 +89,13 @@ pub struct ServerApiSettings {
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ServerWebSettings {
pub enabled: bool,
pub url: String,
pub url: String,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ServerAuthSettings {
pub methods: Vec<ServerAuthMethod>,
pub github: ServerAuthGithubSettings,
pub github: ServerAuthGithubSettings,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
@ -117,8 +117,8 @@ pub struct ServerSandboxSettings {
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ServerSandboxProvidersSettings {
pub local: ServerSandboxProviderSettings,
pub docker: ServerSandboxProviderSettings,
pub local: ServerSandboxProviderSettings,
pub docker: ServerSandboxProviderSettings,
pub daytona: ServerSandboxProviderSettings,
}
@ -158,28 +158,28 @@ pub struct ServerStorageSettings {
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ServerArtifactsSettings {
pub prefix: String,
pub store: ObjectStoreSettings,
pub store: ObjectStoreSettings,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ServerSlateDbSettings {
pub prefix: String,
pub store: ObjectStoreSettings,
pub prefix: String,
pub store: ObjectStoreSettings,
#[serde(
serialize_with = "serialize_std_duration",
deserialize_with = "deserialize_std_duration"
)]
pub flush_interval: StdDuration,
pub disk_cache: bool,
pub disk_cache: bool,
}
impl Default for ServerSlateDbSettings {
fn default() -> Self {
Self {
prefix: String::new(),
store: ObjectStoreSettings::default(),
prefix: String::new(),
store: ObjectStoreSettings::default(),
flush_interval: StdDuration::ZERO,
disk_cache: false,
disk_cache: false,
}
}
}
@ -191,9 +191,9 @@ pub enum ObjectStoreSettings {
root: String,
},
S3 {
bucket: String,
region: String,
endpoint: Option<String>,
bucket: String,
region: String,
endpoint: Option<String>,
path_style: bool,
},
}
@ -233,7 +233,7 @@ pub enum LogDestination {
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ServerLoggingSettings {
pub level: Option<String>,
pub level: Option<String>,
#[serde(default)]
pub destination: LogDestination,
}
@ -241,30 +241,30 @@ pub struct ServerLoggingSettings {
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ServerIntegrationsSettings {
pub github: GithubIntegrationSettings,
pub slack: SlackIntegrationSettings,
pub slack: SlackIntegrationSettings,
pub search: SearchIntegrationSettings,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct GithubIntegrationSettings {
pub enabled: bool,
pub strategy: GithubIntegrationStrategy,
pub app_id: Option<String>,
pub enabled: bool,
pub strategy: GithubIntegrationStrategy,
pub app_id: Option<String>,
pub client_id: Option<String>,
pub slug: Option<String>,
pub webhooks: Option<IntegrationWebhooksSettings>,
pub slug: Option<String>,
pub webhooks: Option<IntegrationWebhooksSettings>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SlackIntegrationSettings {
pub enabled: bool,
pub enabled: bool,
pub default_channel: Option<String>,
}
impl Default for SlackIntegrationSettings {
fn default() -> Self {
Self {
enabled: true,
enabled: true,
default_channel: None,
}
}
@ -335,7 +335,7 @@ impl VeniceSearchEngine {
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct SearchIntegrationSettings {
pub provider: SearchProvider,
pub provider: SearchProvider,
pub venice_engine: VeniceSearchEngine,
}