batch fixes

This commit is contained in:
jaberjaber23
2026-03-02 23:12:19 +03:00
parent d3385f2cdc
commit 62e6e0f088
7 changed files with 161 additions and 23 deletions
Generated
+14 -14
View File
@@ -3866,7 +3866,7 @@ dependencies = [
[[package]]
name = "openfang-api"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"async-trait",
"axum",
@@ -3902,7 +3902,7 @@ dependencies = [
[[package]]
name = "openfang-channels"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"async-trait",
"axum",
@@ -3933,7 +3933,7 @@ dependencies = [
[[package]]
name = "openfang-cli"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"clap",
"clap_complete",
@@ -3960,7 +3960,7 @@ dependencies = [
[[package]]
name = "openfang-desktop"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"axum",
"open",
@@ -3986,7 +3986,7 @@ dependencies = [
[[package]]
name = "openfang-extensions"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"aes-gcm",
"argon2",
@@ -4014,7 +4014,7 @@ dependencies = [
[[package]]
name = "openfang-hands"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"chrono",
"dashmap",
@@ -4031,7 +4031,7 @@ dependencies = [
[[package]]
name = "openfang-kernel"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"async-trait",
"chrono",
@@ -4067,7 +4067,7 @@ dependencies = [
[[package]]
name = "openfang-memory"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"async-trait",
"chrono",
@@ -4086,7 +4086,7 @@ dependencies = [
[[package]]
name = "openfang-migrate"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"chrono",
"dirs 6.0.0",
@@ -4105,7 +4105,7 @@ dependencies = [
[[package]]
name = "openfang-runtime"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"anyhow",
"async-trait",
@@ -4137,7 +4137,7 @@ dependencies = [
[[package]]
name = "openfang-skills"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"chrono",
"hex",
@@ -4159,7 +4159,7 @@ dependencies = [
[[package]]
name = "openfang-types"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"async-trait",
"chrono",
@@ -4178,7 +4178,7 @@ dependencies = [
[[package]]
name = "openfang-wire"
version = "0.2.9"
version = "0.3.0"
dependencies = [
"async-trait",
"chrono",
@@ -8790,7 +8790,7 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
[[package]]
name = "xtask"
version = "0.2.9"
version = "0.3.0"
[[package]]
name = "yoke"
+7
View File
@@ -7568,6 +7568,13 @@ pub async fn patch_agent_config(
}
}
// Persist updated manifest to database so changes survive restart
if let Some(entry) = state.kernel.registry.get(agent_id) {
if let Err(e) = state.kernel.memory.save_agent(&entry) {
tracing::warn!("Failed to persist agent config update: {e}");
}
}
(
StatusCode::OK,
Json(serde_json::json!({"status": "ok", "agent_id": id})),
+6 -1
View File
@@ -373,10 +373,15 @@ async fn dispatch_message(
// Fetch per-channel overrides (if configured)
let overrides = handle.channel_overrides(ct_str).await;
let channel_default_format = match ct_str {
"telegram" => OutputFormat::TelegramHtml,
"slack" => OutputFormat::SlackMrkdwn,
_ => OutputFormat::Markdown,
};
let output_format = overrides
.as_ref()
.and_then(|o| o.output_format)
.unwrap_or(OutputFormat::Markdown);
.unwrap_or(channel_default_format);
let threading_enabled = overrides.as_ref().map(|o| o.threading).unwrap_or(false);
let thread_id = if threading_enabled {
message.thread_id.as_deref()
+1
View File
@@ -85,6 +85,7 @@ impl TelegramAdapter {
let body = serde_json::json!({
"chat_id": chat_id,
"text": chunk,
"parse_mode": "HTML",
});
let resp = self.client.post(&url).json(&body).send().await?;
+72 -5
View File
@@ -904,6 +904,61 @@ impl OpenFangKernel {
let agent_id = entry.id;
let name = entry.name.clone();
// Check if TOML on disk is newer/different — if so, update from file
let mut entry = entry;
let toml_path = kernel
.config
.home_dir
.join("agents")
.join(&name)
.join("agent.toml");
if toml_path.exists() {
match std::fs::read_to_string(&toml_path) {
Ok(toml_str) => {
match toml::from_str::<openfang_types::agent::AgentManifest>(
&toml_str,
) {
Ok(disk_manifest) => {
// Compare key fields to detect changes
let changed = disk_manifest.name != entry.manifest.name
|| disk_manifest.description != entry.manifest.description
|| disk_manifest.model.system_prompt != entry.manifest.model.system_prompt
|| disk_manifest.model.provider != entry.manifest.model.provider
|| disk_manifest.model.model != entry.manifest.model.model
|| disk_manifest.capabilities.tools != entry.manifest.capabilities.tools;
if changed {
info!(
agent = %name,
"Agent TOML on disk differs from DB, updating"
);
entry.manifest = disk_manifest;
// Persist the update back to DB
if let Err(e) = kernel.memory.save_agent(&entry) {
warn!(
agent = %name,
"Failed to persist TOML update: {e}"
);
}
}
}
Err(e) => {
warn!(
agent = %name,
path = %toml_path.display(),
"Invalid agent TOML on disk, using DB version: {e}"
);
}
}
}
Err(e) => {
warn!(
agent = %name,
"Failed to read agent TOML: {e}"
);
}
}
}
// Re-grant capabilities
let caps = manifest_to_capabilities(&entry.manifest);
kernel.capabilities.grant(agent_id, caps);
@@ -3132,18 +3187,30 @@ impl OpenFangKernel {
/// `Continuous`, `Periodic`, or `Proactive` schedules.
pub fn start_background_agents(self: &Arc<Self>) {
let agents = self.registry.list();
let mut started = 0u32;
let mut bg_agents: Vec<(openfang_types::agent::AgentId, String, ScheduleMode)> =
Vec::new();
for entry in &agents {
if matches!(entry.manifest.schedule, ScheduleMode::Reactive) {
continue;
}
self.start_background_for_agent(entry.id, &entry.name, &entry.manifest.schedule);
started += 1;
bg_agents.push((entry.id, entry.name.clone(), entry.manifest.schedule.clone()));
}
if started > 0 {
info!("Started {started} background agent loop(s)");
if !bg_agents.is_empty() {
let count = bg_agents.len();
let kernel = Arc::clone(self);
// Stagger agent startup to prevent rate-limit storm on shared providers.
// Each agent gets a 500ms delay before the next one starts.
tokio::spawn(async move {
for (i, (id, name, schedule)) in bg_agents.into_iter().enumerate() {
kernel.start_background_for_agent(id, &name, &schedule);
if i > 0 {
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
}
}
info!("Started {count} background agent loop(s) (staggered)");
});
}
// Start heartbeat monitor for agent health checking
+58 -3
View File
@@ -32,7 +32,7 @@ pub struct McpServerConfig {
}
fn default_timeout() -> u64 {
30
60
}
/// Transport type for MCP server connections.
@@ -393,7 +393,31 @@ impl McpConnection {
return Err("MCP command path contains '..': rejected".to_string());
}
let mut cmd = tokio::process::Command::new(command);
// On Windows, npm/npx install as .cmd batch wrappers. Detect and adapt.
let resolved_command: String = if cfg!(windows) {
// If the user already specified .cmd/.bat, use as-is
if command.ends_with(".cmd") || command.ends_with(".bat") {
command.to_string()
} else {
// Check if the .cmd variant exists on PATH
let cmd_variant = format!("{command}.cmd");
let has_cmd = std::env::var("PATH")
.unwrap_or_default()
.split(';')
.any(|dir| {
std::path::Path::new(dir).join(&cmd_variant).exists()
});
if has_cmd {
cmd_variant
} else {
command.to_string()
}
}
} else {
command.to_string()
};
let mut cmd = tokio::process::Command::new(&resolved_command);
cmd.args(args);
cmd.stdin(Stdio::piped());
cmd.stdout(Stdio::piped());
@@ -410,10 +434,41 @@ impl McpConnection {
if let Ok(path) = std::env::var("PATH") {
cmd.env("PATH", path);
}
// On Windows, npm/node need APPDATA, USERPROFILE, LOCALAPPDATA, and SystemRoot
if cfg!(windows) {
for var in &[
"APPDATA",
"LOCALAPPDATA",
"USERPROFILE",
"SystemRoot",
"TEMP",
"TMP",
"HOME",
"HOMEDRIVE",
"HOMEPATH",
] {
if let Ok(val) = std::env::var(var) {
cmd.env(var, val);
}
}
}
let mut child = cmd
.spawn()
.map_err(|e| format!("Failed to spawn MCP server '{command}': {e}"))?;
.map_err(|e| format!("Failed to spawn MCP server '{resolved_command}': {e}"))?;
// Log stderr in background for debugging MCP server issues
if let Some(stderr) = child.stderr.take() {
let cmd_name = resolved_command.clone();
tokio::spawn(async move {
use tokio::io::AsyncBufReadExt;
let reader = tokio::io::BufReader::new(stderr);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
tracing::debug!(mcp_server = %cmd_name, "stderr: {line}");
}
});
}
let stdin = child
.stdin
@@ -215,6 +215,9 @@ const TOOL_CALL_BEHAVIOR: &str = "\
- Prefer action over narration. If you can answer by using a tool, do it.
- When executing multiple sequential tool calls, batch them — don't output reasoning between each call.
- If a tool returns useful results, present the KEY information, not the raw output.
- When web_fetch or web_search returns content, you MUST include the relevant data in your response. \
Quote specific facts, numbers, or passages from the fetched content. Never say you fetched something \
without sharing what you found.
- Start with the answer, not meta-commentary about how you'll help.
- IMPORTANT: If your instructions or persona mention a shell command, script path, or code snippet, \
execute it via the appropriate tool call (shell_exec, file_write, etc.). Never output commands as \