community fixes

This commit is contained in:
jaberjaber23
2026-03-15 01:25:15 +03:00
parent a7a96a7b0f
commit 135c37fbf7
13 changed files with 72 additions and 48 deletions
Generated
+14 -14
View File
@@ -3792,7 +3792,7 @@ dependencies = [
[[package]]
name = "openfang-api"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"async-trait",
"axum",
@@ -3832,7 +3832,7 @@ dependencies = [
[[package]]
name = "openfang-channels"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"async-trait",
"axum",
@@ -3864,7 +3864,7 @@ dependencies = [
[[package]]
name = "openfang-cli"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"clap",
"clap_complete",
@@ -3891,7 +3891,7 @@ dependencies = [
[[package]]
name = "openfang-desktop"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"axum",
"open",
@@ -3917,7 +3917,7 @@ dependencies = [
[[package]]
name = "openfang-extensions"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"aes-gcm",
"argon2",
@@ -3945,7 +3945,7 @@ dependencies = [
[[package]]
name = "openfang-hands"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"chrono",
"dashmap",
@@ -3962,7 +3962,7 @@ dependencies = [
[[package]]
name = "openfang-kernel"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"async-trait",
"chrono",
@@ -3999,7 +3999,7 @@ dependencies = [
[[package]]
name = "openfang-memory"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"async-trait",
"chrono",
@@ -4018,7 +4018,7 @@ dependencies = [
[[package]]
name = "openfang-migrate"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"chrono",
"dirs 6.0.0",
@@ -4037,7 +4037,7 @@ dependencies = [
[[package]]
name = "openfang-runtime"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"anyhow",
"async-trait",
@@ -4071,7 +4071,7 @@ dependencies = [
[[package]]
name = "openfang-skills"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"chrono",
"hex",
@@ -4094,7 +4094,7 @@ dependencies = [
[[package]]
name = "openfang-types"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"async-trait",
"chrono",
@@ -4113,7 +4113,7 @@ dependencies = [
[[package]]
name = "openfang-wire"
version = "0.4.1"
version = "0.4.2"
dependencies = [
"async-trait",
"chrono",
@@ -8776,7 +8776,7 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
[[package]]
name = "xtask"
version = "0.4.1"
version = "0.4.2"
[[package]]
name = "yoke"
+4 -1
View File
@@ -18,7 +18,7 @@ members = [
]
[workspace.package]
version = "0.4.2"
version = "0.4.3"
edition = "2021"
license = "Apache-2.0 OR MIT"
repository = "https://github.com/RightNow-AI/openfang"
@@ -138,6 +138,9 @@ imap = "2"
native-tls = "0.2"
mailparse = "0.15"
# OpenSSL (vendored = statically compiled, no runtime libssl dependency on Linux)
openssl = { version = "0.10", features = ["vendored"] }
# Testing
tokio-test = "0.4"
tempfile = "3"
+2 -2
View File
@@ -322,7 +322,7 @@ pub async fn chat_completions(
let kernel_handle: Arc<dyn KernelHandle> = state.kernel.clone() as Arc<dyn KernelHandle>;
match state
.kernel
.send_message_with_handle(agent_id, &last_user_msg, Some(kernel_handle))
.send_message_with_handle(agent_id, &last_user_msg, Some(kernel_handle), None, None)
.await
{
Ok(result) => {
@@ -378,7 +378,7 @@ async fn stream_response(
let (mut rx, _handle) = state
.kernel
.send_message_streaming(agent_id, message, Some(kernel_handle))
.send_message_streaming(agent_id, message, Some(kernel_handle), None, None)
.map_err(|e| format!("Streaming setup failed: {e}"))?;
let (tx, stream_rx) = tokio::sync::mpsc::channel::<Result<SseEvent, Infallible>>(64);
+6 -14
View File
@@ -364,16 +364,10 @@ pub async fn send_message(
}
}
// TODO(#597): Thread req.sender_id / req.sender_name into PromptContext
// so the agent loop can see who sent the message. Requires extending
// send_message_with_handle to accept optional sender metadata.
let _sender_id = req.sender_id;
let _sender_name = req.sender_name;
let kernel_handle: Arc<dyn KernelHandle> = state.kernel.clone() as Arc<dyn KernelHandle>;
match state
.kernel
.send_message_with_handle(agent_id, &req.message, Some(kernel_handle))
.send_message_with_handle(agent_id, &req.message, Some(kernel_handle), req.sender_id, req.sender_name)
.await
{
Ok(result) => {
@@ -1349,15 +1343,11 @@ pub async fn send_message_stream(
.into_response();
}
// TODO(#597): Thread req.sender_id / req.sender_name into PromptContext
let _sender_id = req.sender_id;
let _sender_name = req.sender_name;
let kernel_handle: Arc<dyn KernelHandle> = state.kernel.clone() as Arc<dyn KernelHandle>;
let (rx, _handle) =
match state
.kernel
.send_message_streaming(agent_id, &req.message, Some(kernel_handle))
.send_message_streaming(agent_id, &req.message, Some(kernel_handle), req.sender_id, req.sender_name)
{
Ok(pair) => pair,
Err(e) => {
@@ -3990,7 +3980,9 @@ pub async fn install_hand_deps(
let combined = format!("{stdout}{stderr}");
let likely_ok = combined.contains("already installed")
|| combined.contains("No applicable update")
|| combined.contains("No available upgrade");
|| combined.contains("No available upgrade")
|| combined.contains("already an App at")
|| combined.contains("is already installed");
results.push(serde_json::json!({
"key": req.key,
"status": if likely_ok { "installed" } else { "error" },
@@ -8189,7 +8181,7 @@ pub async fn run_schedule(
);
let kernel_handle: Arc<dyn KernelHandle> = state.kernel.clone() as Arc<dyn KernelHandle>;
match state.kernel.send_message_with_handle(target_agent, &run_message, Some(kernel_handle)).await {
match state.kernel.send_message_with_handle(target_agent, &run_message, Some(kernel_handle), None, None).await {
Ok(result) => (
StatusCode::OK,
Json(serde_json::json!({
+1 -1
View File
@@ -504,7 +504,7 @@ async fn handle_text_message(
state.kernel.clone() as Arc<dyn KernelHandle>;
match state
.kernel
.send_message_streaming(agent_id, &content, Some(kernel_handle))
.send_message_streaming(agent_id, &content, Some(kernel_handle), None, None)
{
Ok((mut rx, handle)) => {
// Forward stream events to WebSocket with debouncing
+2
View File
@@ -27,6 +27,8 @@ function renderMarkdown(text) {
var html = marked.parse(text);
// Add copy buttons to code blocks
html = html.replace(/<pre><code/g, '<pre><button class="copy-btn" onclick="copyCode(this)">Copy</button><code');
// Open external links in new tab
html = html.replace(/<a\s+href="(https?:\/\/[^"]*)"(?![^>]*target=)([^>]*)>/gi, '<a href="$1" target="_blank" rel="noopener"$2>');
return html;
}
return escapeHtml(text);
@@ -401,8 +401,12 @@ function settingsPage() {
var key = this.providerKeyInputs[provider.id];
if (!key || !key.trim()) { OpenFangToast.error('Please enter an API key'); return; }
try {
await OpenFangAPI.post('/api/providers/' + encodeURIComponent(provider.id) + '/key', { key: key.trim() });
OpenFangToast.success('API key saved for ' + provider.display_name);
var resp = await OpenFangAPI.post('/api/providers/' + encodeURIComponent(provider.id) + '/key', { key: key.trim() });
if (resp && resp.switched_default) {
OpenFangToast.warning(resp.message || 'Default provider was switched to ' + provider.display_name);
} else {
OpenFangToast.success('API key saved for ' + provider.display_name);
}
this.providerKeyInputs[provider.id] = '';
await this.loadProviders();
await this.loadModels();
+1 -1
View File
@@ -303,7 +303,7 @@ pub fn spawn_inprocess_stream(
// send_message_streaming() finds the reactor.
let _guard = rt.enter();
match kernel.send_message_streaming(agent_id, &message, None) {
match kernel.send_message_streaming(agent_id, &message, None, None, None) {
Ok((mut rx, handle)) => {
rt.block_on(async {
while let Some(ev) = rx.recv().await {
+18 -9
View File
@@ -1389,7 +1389,7 @@ impl OpenFangKernel {
.get()
.and_then(|w| w.upgrade())
.map(|arc| arc as Arc<dyn KernelHandle>);
self.send_message_with_handle(agent_id, message, handle)
self.send_message_with_handle(agent_id, message, handle, None, None)
.await
}
@@ -1408,7 +1408,7 @@ impl OpenFangKernel {
.get()
.and_then(|w| w.upgrade())
.map(|arc| arc as Arc<dyn KernelHandle>);
self.send_message_with_handle_and_blocks(agent_id, message, handle, Some(blocks))
self.send_message_with_handle_and_blocks(agent_id, message, handle, Some(blocks), None, None)
.await
}
@@ -1418,8 +1418,10 @@ impl OpenFangKernel {
agent_id: AgentId,
message: &str,
kernel_handle: Option<Arc<dyn KernelHandle>>,
sender_id: Option<String>,
sender_name: Option<String>,
) -> KernelResult<AgentLoopResult> {
self.send_message_with_handle_and_blocks(agent_id, message, kernel_handle, None)
self.send_message_with_handle_and_blocks(agent_id, message, kernel_handle, None, sender_id, sender_name)
.await
}
@@ -1438,6 +1440,8 @@ impl OpenFangKernel {
message: &str,
kernel_handle: Option<Arc<dyn KernelHandle>>,
content_blocks: Option<Vec<openfang_types::message::ContentBlock>>,
sender_id: Option<String>,
sender_name: Option<String>,
) -> KernelResult<AgentLoopResult> {
// Acquire per-agent lock to serialize concurrent messages for the same agent.
// This prevents session corruption when multiple messages arrive in quick
@@ -1467,7 +1471,7 @@ impl OpenFangKernel {
self.execute_python_agent(&entry, agent_id, message).await
} else {
// Default: LLM agent loop (builtin:chat or any unrecognized module)
self.execute_llm_agent(&entry, agent_id, message, kernel_handle, content_blocks)
self.execute_llm_agent(&entry, agent_id, message, kernel_handle, content_blocks, sender_id, sender_name)
.await
};
@@ -1522,6 +1526,8 @@ impl OpenFangKernel {
agent_id: AgentId,
message: &str,
kernel_handle: Option<Arc<dyn KernelHandle>>,
sender_id: Option<String>,
sender_name: Option<String>,
) -> KernelResult<(
tokio::sync::mpsc::Receiver<StreamEvent>,
tokio::task::JoinHandle<KernelResult<AgentLoopResult>>,
@@ -1759,8 +1765,8 @@ impl OpenFangKernel {
},
peer_agents,
current_date: Some(chrono::Local::now().format("%A, %B %d, %Y (%Y-%m-%d %H:%M %Z)").to_string()),
sender_id: None,
sender_name: None,
sender_id,
sender_name,
};
manifest.model.system_prompt =
openfang_runtime::prompt_builder::build_system_prompt(&prompt_ctx);
@@ -2087,6 +2093,7 @@ impl OpenFangKernel {
}
/// Execute the default LLM-based agent loop.
#[allow(clippy::too_many_arguments)]
async fn execute_llm_agent(
&self,
entry: &AgentEntry,
@@ -2094,6 +2101,8 @@ impl OpenFangKernel {
message: &str,
kernel_handle: Option<Arc<dyn KernelHandle>>,
content_blocks: Option<Vec<openfang_types::message::ContentBlock>>,
sender_id: Option<String>,
sender_name: Option<String>,
) -> KernelResult<AgentLoopResult> {
// Check metering quota before starting
self.metering
@@ -2268,8 +2277,8 @@ impl OpenFangKernel {
},
peer_agents,
current_date: Some(chrono::Local::now().format("%A, %B %d, %Y (%Y-%m-%d %H:%M %Z)").to_string()),
sender_id: None,
sender_name: None,
sender_id,
sender_name,
};
manifest.model.system_prompt =
openfang_runtime::prompt_builder::build_system_prompt(&prompt_ctx);
@@ -3924,7 +3933,7 @@ impl OpenFangKernel {
let kh: std::sync::Arc<dyn openfang_runtime::kernel_handle::KernelHandle> = kernel.clone();
match tokio::time::timeout(
timeout,
kernel.send_message_with_handle(agent_id, message, Some(kh)),
kernel.send_message_with_handle(agent_id, message, Some(kh), None, None),
)
.await
{
@@ -303,7 +303,7 @@ async fn test_wasm_agent_streaming_fallback() {
let agent_id = kernel.spawn_agent(manifest).unwrap();
let (mut rx, handle) = kernel
.send_message_streaming(agent_id, "Hi!", None)
.send_message_streaming(agent_id, "Hi!", None, None, None)
.expect("Streaming should start");
// Collect all stream events
@@ -865,6 +865,10 @@ fn builtin_aliases() -> HashMap<String, String> {
("qwen-coder", "qwen-code/qwen3-coder"),
("qwen-coder-plus", "qwen-code/qwen-coder-plus"),
("qwq", "qwen-code/qwq-32b"),
// OpenRouter free-tier aliases
("openrouter/free", "openrouter/meta-llama/llama-3.1-8b-instruct:free"),
("free", "openrouter/meta-llama/llama-3.1-8b-instruct:free"),
("free-reasoning", "openrouter/deepseek/deepseek-r1:free"),
];
pairs
.into_iter()
+2 -1
View File
@@ -37,7 +37,8 @@ pub const GITHUB_COPILOT_BASE_URL: &str = "https://api.githubcopilot.com";
// ── Chinese providers ─────────────────────────────────────────────
pub const QWEN_BASE_URL: &str = "https://dashscope.aliyuncs.com/compatible-mode/v1";
pub const MINIMAX_BASE_URL: &str = "https://api.minimaxi.chat/v1";
/// Global endpoint. For China mainland, override via `[provider_urls] minimax = "https://api.minimaxi.com/v1"`.
pub const MINIMAX_BASE_URL: &str = "https://api.minimax.io/v1";
pub const ZHIPU_BASE_URL: &str = "https://open.bigmodel.cn/api/paas/v4";
pub const ZHIPU_CODING_BASE_URL: &str = "https://open.bigmodel.cn/api/coding/paas/v4";
/// Z.AI domain aliases (same API, different domain).
+11 -2
View File
@@ -125,12 +125,21 @@ async function startConnection() {
if (msg.key.remoteJid === 'status@broadcast') continue;
const sender = msg.key.remoteJid || '';
const text = msg.message?.conversation
let text = msg.message?.conversation
|| msg.message?.extendedTextMessage?.text
|| msg.message?.imageMessage?.caption
|| '';
if (!text) continue;
// Detect media type if no text
if (!text) {
const m = msg.message;
if (m?.imageMessage) text = '[Image received]' + (m.imageMessage.caption ? ': ' + m.imageMessage.caption : '');
else if (m?.audioMessage) text = '[Voice note received]';
else if (m?.videoMessage) text = '[Video received]' + (m.videoMessage.caption ? ': ' + m.videoMessage.caption : '');
else if (m?.documentMessage) text = '[Document received: ' + (m.documentMessage.fileName || 'file') + ']';
else if (m?.stickerMessage) text = '[Sticker received]';
else continue; // Only skip truly empty messages
}
// Extract phone number from JID (e.g. "1234567890@s.whatsapp.net" → "+1234567890")
const phone = '+' + sender.replace(/@.*$/, '');