optimize: channel Message Commands — Multilingual Support and Prefixes

This commit is contained in:
xintaofei
2026-04-01 16:22:54 +08:00
parent 05214d09de
commit adb5829613
4 changed files with 247 additions and 99 deletions

View File

@@ -156,7 +156,7 @@ async fn dispatch_command(
};
if has_session {
return session_commands::handle_followup(
db, text, channel_id, sender_id, conn_mgr, bridge, lang,
db, text, channel_id, sender_id, conn_mgr, bridge, lang, prefix,
)
.await;
}
@@ -193,23 +193,23 @@ async fn dispatch_command(
// Session commands
"folder" => {
session_commands::handle_folder(db, args, channel_id, sender_id, lang).await
session_commands::handle_folder(db, args, channel_id, sender_id, lang, prefix).await
}
"agent" => {
session_commands::handle_agent(db, args, channel_id, sender_id, lang).await
session_commands::handle_agent(db, args, channel_id, sender_id, lang, prefix).await
}
"task" | "do" => {
session_commands::handle_task(
db, args, channel_id, sender_id, conn_mgr, emitter, bridge, lang,
db, args, channel_id, sender_id, conn_mgr, emitter, bridge, lang, prefix,
)
.await
}
"sessions" => {
session_commands::handle_sessions(db, channel_id, sender_id, lang).await
session_commands::handle_sessions(db, channel_id, sender_id, lang, prefix).await
}
"resume" => {
session_commands::handle_resume(
db, args, channel_id, sender_id, conn_mgr, emitter, bridge, lang,
db, args, channel_id, sender_id, conn_mgr, emitter, bridge, lang, prefix,
)
.await
}

View File

@@ -610,14 +610,14 @@ pub fn help_title(lang: Lang) -> &'static str {
pub fn help_body(lang: Lang, prefix: &str) -> String {
match lang {
Lang::ZhCn => format!(
"📂 {prefix}folder - 选择工作目录\n\
🤖 {prefix}agent - 选择 Agent\n\
🚀 {prefix}task <描述> - 创建会话并执行任务\n\
📋 {prefix}sessions - 当前目录的活跃会话\n\
▶️ {prefix}resume <ID> - 恢复已有会话\n\
⏹️ {prefix}cancel - 取消当前任务\n\
{prefix}approve [always] - 批准权限请求\n\
{prefix}deny - 拒绝权限请求\n\
"{prefix}folder - 选择工作目录\n\
{prefix}agent - 选择 Agent\n\
{prefix}task <描述> - 创建会话并执行任务\n\
{prefix}sessions - 当前目录的活跃会话\n\
{prefix}resume <ID> - 恢复已有会话\n\
{prefix}cancel - 取消当前任务\n\
{prefix}approve [always] - 批准权限请求\n\
{prefix}deny - 拒绝权限请求\n\
\n\
{prefix}recent - 最近 5 条会话\n\
{prefix}search <关键词> - 搜索会话\n\
@@ -626,81 +626,169 @@ pub fn help_body(lang: Lang, prefix: &str) -> String {
{prefix}status - 渠道连接状态\n\
{prefix}help - 显示帮助\n\
\n\
💡 有活跃会话时,直接发文本即可继续对话"
有活跃会话时,直接发文本即可继续对话"
),
Lang::ZhTw => format!(
"{prefix}recent - 最近 5 條對話\n\
"{prefix}folder - 選擇工作目錄\n\
{prefix}agent - 選擇 Agent\n\
{prefix}task <描述> - 建立對話並執行任務\n\
{prefix}sessions - 當前目錄的活躍對話\n\
{prefix}resume <ID> - 恢復已有對話\n\
{prefix}cancel - 取消當前任務\n\
{prefix}approve [always] - 批准權限請求\n\
{prefix}deny - 拒絕權限請求\n\
\n\
{prefix}recent - 最近 5 條對話\n\
{prefix}search <關鍵字> - 搜尋對話\n\
{prefix}detail <ID> - 對話詳情\n\
{prefix}today - 今日活動匯總\n\
{prefix}status - 頻道連線狀態\n\
{prefix}help - 顯示幫助"
{prefix}help - 顯示幫助\n\
\n\
有活躍對話時,直接發文字即可繼續對話"
),
Lang::Ja => format!(
"{prefix}recent - 最新5件のセッション\n\
"{prefix}folder - 作業フォルダを選択\n\
{prefix}agent - エージェントを選択\n\
{prefix}task <説明> - セッションを作成してタスクを実行\n\
{prefix}sessions - フォルダ内のアクティブセッション\n\
{prefix}resume <ID> - セッションを再開\n\
{prefix}cancel - 現在のタスクをキャンセル\n\
{prefix}approve [always] - 権限を承認\n\
{prefix}deny - 権限を拒否\n\
\n\
{prefix}recent - 最新5件のセッション\n\
{prefix}search <キーワード> - セッション検索\n\
{prefix}detail <ID> - セッション詳細\n\
{prefix}today - 本日の活動まとめ\n\
{prefix}status - チャンネル接続状況\n\
{prefix}help - ヘルプを表示"
{prefix}help - ヘルプを表示\n\
\n\
セッションがアクティブな場合、テキストを送信するだけで会話を続けられます"
),
Lang::Ko => format!(
"{prefix}recent - 최근 5개 대화\n\
"{prefix}folder - 작업 폴더 선택\n\
{prefix}agent - 에이전트 선택\n\
{prefix}task <설명> - 세션 생성 및 작업 실행\n\
{prefix}sessions - 폴더 내 활성 세션\n\
{prefix}resume <ID> - 세션 재개\n\
{prefix}cancel - 현재 작업 취소\n\
{prefix}approve [always] - 권한 승인\n\
{prefix}deny - 권한 거부\n\
\n\
{prefix}recent - 최근 5개 대화\n\
{prefix}search <키워드> - 대화 검색\n\
{prefix}detail <ID> - 대화 상세\n\
{prefix}today - 오늘의 활동 요약\n\
{prefix}status - 채널 연결 상태\n\
{prefix}help - 도움말 표시"
{prefix}help - 도움말 표시\n\
\n\
세션이 활성화된 경우 텍스트를 보내면 대화를 계속할 수 있습니다"
),
Lang::Es => format!(
"{prefix}recent - 5 conversaciones más recientes\n\
"{prefix}folder - Seleccionar carpeta de trabajo\n\
{prefix}agent - Seleccionar agente\n\
{prefix}task <desc> - Crear sesion y ejecutar tarea\n\
{prefix}sessions - Sesiones activas en la carpeta\n\
{prefix}resume <ID> - Reanudar una sesion\n\
{prefix}cancel - Cancelar tarea actual\n\
{prefix}approve [always] - Aprobar permiso\n\
{prefix}deny - Denegar permiso\n\
\n\
{prefix}recent - 5 conversaciones mas recientes\n\
{prefix}search <palabra> - Buscar conversaciones\n\
{prefix}detail <ID> - Detalles de conversación\n\
{prefix}detail <ID> - Detalles de conversacion\n\
{prefix}today - Resumen de hoy\n\
{prefix}status - Estado de canales\n\
{prefix}help - Mostrar ayuda"
{prefix}help - Mostrar ayuda\n\
\n\
Cuando hay una sesion activa, simplemente escriba texto para continuar"
),
Lang::De => format!(
"{prefix}recent - 5 neueste Sitzungen\n\
"{prefix}folder - Arbeitsordner auswahlen\n\
{prefix}agent - Agent auswahlen\n\
{prefix}task <Beschreibung> - Sitzung erstellen und Aufgabe ausfuhren\n\
{prefix}sessions - Aktive Sitzungen im Ordner\n\
{prefix}resume <ID> - Sitzung fortsetzen\n\
{prefix}cancel - Aktuelle Aufgabe abbrechen\n\
{prefix}approve [always] - Berechtigung genehmigen\n\
{prefix}deny - Berechtigung verweigern\n\
\n\
{prefix}recent - 5 neueste Sitzungen\n\
{prefix}search <Stichwort> - Sitzungen suchen\n\
{prefix}detail <ID> - Sitzungsdetails\n\
{prefix}today - Heutige Zusammenfassung\n\
{prefix}status - Kanalstatus\n\
{prefix}help - Hilfe anzeigen"
{prefix}help - Hilfe anzeigen\n\
\n\
Bei aktiver Sitzung einfach Text eingeben, um das Gesprach fortzusetzen"
),
Lang::Fr => format!(
"{prefix}recent - 5 dernières sessions\n\
{prefix}search <mot-clé> - Rechercher des sessions\n\
{prefix}detail <ID> - Détails de la session\n\
{prefix}today - Résumé du jour\n\
"{prefix}folder - Selectionner le dossier de travail\n\
{prefix}agent - Selectionner l'agent\n\
{prefix}task <desc> - Creer une session et executer une tache\n\
{prefix}sessions - Sessions actives dans le dossier\n\
{prefix}resume <ID> - Reprendre une session\n\
{prefix}cancel - Annuler la tache en cours\n\
{prefix}approve [always] - Approuver la permission\n\
{prefix}deny - Refuser la permission\n\
\n\
{prefix}recent - 5 dernieres sessions\n\
{prefix}search <mot-cle> - Rechercher des sessions\n\
{prefix}detail <ID> - Details de la session\n\
{prefix}today - Resume du jour\n\
{prefix}status - Statut des canaux\n\
{prefix}help - Afficher l'aide"
{prefix}help - Afficher l'aide\n\
\n\
Lorsqu'une session est active, envoyez du texte pour continuer la conversation"
),
Lang::Pt => format!(
"{prefix}recent - 5 sessões mais recentes\n\
{prefix}search <palavra> - Buscar sessões\n\
{prefix}detail <ID> - Detalhes da sessão\n\
"{prefix}folder - Selecionar pasta de trabalho\n\
{prefix}agent - Selecionar agente\n\
{prefix}task <desc> - Criar sessao e executar tarefa\n\
{prefix}sessions - Sessoes ativas na pasta\n\
{prefix}resume <ID> - Retomar uma sessao\n\
{prefix}cancel - Cancelar tarefa atual\n\
{prefix}approve [always] - Aprovar permissao\n\
{prefix}deny - Negar permissao\n\
\n\
{prefix}recent - 5 sessoes mais recentes\n\
{prefix}search <palavra> - Buscar sessoes\n\
{prefix}detail <ID> - Detalhes da sessao\n\
{prefix}today - Resumo de hoje\n\
{prefix}status - Status dos canais\n\
{prefix}help - Mostrar ajuda"
{prefix}help - Mostrar ajuda\n\
\n\
Quando uma sessao esta ativa, basta digitar texto para continuar a conversa"
),
Lang::Ar => format!(
"{prefix}recent - أحدث 5 جلسات\n\
"{prefix}folder - اختيار مجلد العمل\n\
{prefix}agent - اختيار الوكيل\n\
{prefix}task <وصف> - انشاء جلسة وتنفيذ مهمة\n\
{prefix}sessions - الجلسات النشطة في المجلد\n\
{prefix}resume <ID> - استئناف جلسة\n\
{prefix}cancel - الغاء المهمة الحالية\n\
{prefix}approve [always] - الموافقة على الاذن\n\
{prefix}deny - رفض الاذن\n\
\n\
{prefix}recent - احدث 5 جلسات\n\
{prefix}search <كلمة> - البحث في الجلسات\n\
{prefix}detail <ID> - تفاصيل الجلسة\n\
{prefix}today - ملخص اليوم\n\
{prefix}status - حالة القنوات\n\
{prefix}help - عرض المساعدة"
{prefix}help - عرض المساعدة\n\
\n\
عندما تكون الجلسة نشطة، ارسل نصا لمتابعة المحادثة"
),
Lang::En => format!(
"📂 {prefix}folder - Select working folder\n\
🤖 {prefix}agent - Select agent\n\
🚀 {prefix}task <desc> - Create session & run task\n\
📋 {prefix}sessions - Active sessions in folder\n\
▶️ {prefix}resume <ID> - Resume a session\n\
⏹️ {prefix}cancel - Cancel current task\n\
{prefix}approve [always] - Approve permission\n\
{prefix}deny - Deny permission\n\
"{prefix}folder - Select working folder\n\
{prefix}agent - Select agent\n\
{prefix}task <desc> - Create session & run task\n\
{prefix}sessions - Active sessions in folder\n\
{prefix}resume <ID> - Resume a session\n\
{prefix}cancel - Cancel current task\n\
{prefix}approve [always] - Approve permission\n\
{prefix}deny - Deny permission\n\
\n\
{prefix}recent - 5 most recent conversations\n\
{prefix}search <keyword> - Search conversations\n\
@@ -709,7 +797,7 @@ pub fn help_body(lang: Lang, prefix: &str) -> String {
{prefix}status - Channel connection status\n\
{prefix}help - Show help\n\
\n\
💡 When a session is active, just type text to continue the conversation"
When a session is active, just type text to continue the conversation"
),
}
}
@@ -810,3 +898,20 @@ pub fn unknown_command_title(lang: Lang) -> &'static str {
Lang::En => "Unknown Command",
}
}
// ── Session progress messages ──
pub fn agent_responding(lang: Lang) -> &'static str {
match lang {
Lang::ZhCn => "Claude Code 正在响应中...",
Lang::ZhTw => "Claude Code 正在回應中...",
Lang::Ja => "Claude Code が応答中...",
Lang::Ko => "Claude Code 응답 중...",
Lang::Es => "Claude Code respondiendo...",
Lang::De => "Claude Code antwortet...",
Lang::Fr => "Claude Code en cours de reponse...",
Lang::Pt => "Claude Code respondendo...",
Lang::Ar => "...Claude Code يستجيب",
Lang::En => "Claude Code is responding...",
}
}

View File

@@ -24,14 +24,15 @@ pub async fn handle_folder(
channel_id: i32,
sender_id: &str,
lang: Lang,
prefix: &str,
) -> RichMessage {
if args.is_empty() {
return list_folders(db, channel_id, sender_id, lang).await;
return list_folders(db, channel_id, sender_id, lang, prefix).await;
}
// Try parse as index (1-based)
if let Ok(idx) = args.parse::<usize>() {
return select_folder_by_index(db, idx, channel_id, sender_id, lang).await;
return select_folder_by_index(db, idx, channel_id, sender_id, lang, prefix).await;
}
// Treat as path
@@ -43,6 +44,7 @@ async fn list_folders(
channel_id: i32,
sender_id: &str,
lang: Lang,
prefix: &str,
) -> RichMessage {
let folders = match folder_service::list_folders(db).await {
Ok(f) => f,
@@ -77,10 +79,11 @@ async fn list_folders(
body.push_str(&format!(
"\n{}",
t(
tp(
lang,
"Reply /folder <number> to select.",
"回复 /folder <数字> 选择目录。"
prefix,
"Reply {prefix}folder <number> to select.",
"回复 {prefix}folder <数字> 选择目录。"
)
));
@@ -94,6 +97,7 @@ async fn select_folder_by_index(
channel_id: i32,
sender_id: &str,
lang: Lang,
prefix: &str,
) -> RichMessage {
if idx == 0 {
return RichMessage::info(t(lang, "Index starts from 1.", "序号从 1 开始。"));
@@ -105,10 +109,11 @@ async fn select_folder_by_index(
};
let Some(folder) = folders.get(idx - 1) else {
return RichMessage::info(t(
return RichMessage::info(tp(
lang,
"Index out of range. Use /folder to list.",
"序号超出范围,请使用 /folder 查看列表。",
prefix,
"Index out of range. Use {prefix}folder to list.",
"序号超出范围,请使用 {prefix}folder 查看列表。",
));
};
@@ -146,14 +151,15 @@ pub async fn handle_agent(
channel_id: i32,
sender_id: &str,
lang: Lang,
prefix: &str,
) -> RichMessage {
if args.is_empty() {
return list_agents(db, channel_id, sender_id, lang).await;
return list_agents(db, channel_id, sender_id, lang, prefix).await;
}
// Try parse as index
if let Ok(idx) = args.parse::<usize>() {
return select_agent_by_index(db, idx, channel_id, sender_id, lang).await;
return select_agent_by_index(db, idx, channel_id, sender_id, lang, prefix).await;
}
// Try parse as agent type name
@@ -165,6 +171,7 @@ async fn list_agents(
channel_id: i32,
sender_id: &str,
lang: Lang,
prefix: &str,
) -> RichMessage {
let agents = all_acp_agents();
let ctx = sender_context_service::get_or_create(db, channel_id, sender_id)
@@ -185,10 +192,11 @@ async fn list_agents(
body.push_str(&format!(
"\n{}",
t(
tp(
lang,
"Reply /agent <number> or /agent <name> to select.",
"回复 /agent <数字> 或 /agent <名称> 选择。"
prefix,
"Reply {prefix}agent <number> or {prefix}agent <name> to select.",
"回复 {prefix}agent <数字> 或 {prefix}agent <名称> 选择。"
)
));
@@ -202,13 +210,15 @@ async fn select_agent_by_index(
channel_id: i32,
sender_id: &str,
lang: Lang,
prefix: &str,
) -> RichMessage {
let agents = all_acp_agents();
if idx == 0 || idx > agents.len() {
return RichMessage::info(t(
return RichMessage::info(tp(
lang,
"Index out of range. Use /agent to list.",
"序号超出范围,请使用 /agent 查看列表。",
prefix,
"Index out of range. Use {prefix}agent to list.",
"序号超出范围,请使用 {prefix}agent 查看列表。",
));
}
@@ -257,12 +267,14 @@ pub async fn handle_task(
emitter: &EventEmitter,
bridge: &Arc<Mutex<SessionBridge>>,
lang: Lang,
prefix: &str,
) -> RichMessage {
if task_description.is_empty() {
return RichMessage::info(t(
return RichMessage::info(tp(
lang,
"Usage: /task <description>",
"用法: /task <任务描述>",
prefix,
"Usage: {prefix}task <description>",
"用法: {prefix}task <任务描述>",
));
}
@@ -275,10 +287,11 @@ pub async fn handle_task(
let folder_id = match ctx.current_folder_id {
Some(id) => id,
None => {
return RichMessage::info(t(
return RichMessage::info(tp(
lang,
"No folder selected. Use /folder first.",
"未选择工作目录,请先使用 /folder 选择。",
prefix,
"No folder selected. Use {prefix}folder first.",
"未选择工作目录,请先使用 {prefix}folder 选择。",
));
}
};
@@ -287,10 +300,11 @@ pub async fn handle_task(
let folder = match folder_service::get_folder_by_id(db, folder_id).await {
Ok(Some(f)) => f,
_ => {
return RichMessage::info(t(
return RichMessage::info(tp(
lang,
"Folder not found. Use /folder to select.",
"目录不存在,请使用 /folder 重新选择。",
prefix,
"Folder not found. Use {prefix}folder to select.",
"目录不存在,请使用 {prefix}folder 重新选择。",
));
}
};
@@ -381,6 +395,7 @@ pub async fn handle_sessions(
channel_id: i32,
sender_id: &str,
lang: Lang,
prefix: &str,
) -> RichMessage {
let ctx = match sender_context_service::get_or_create(db, channel_id, sender_id).await {
Ok(c) => c,
@@ -390,10 +405,11 @@ pub async fn handle_sessions(
let folder_id = match ctx.current_folder_id {
Some(id) => id,
None => {
return RichMessage::info(t(
return RichMessage::info(tp(
lang,
"No folder selected. Use /folder first.",
"未选择工作目录,请先使用 /folder 选择。",
prefix,
"No folder selected. Use {prefix}folder first.",
"未选择工作目录,请先使用 {prefix}folder 选择。",
));
}
};
@@ -456,10 +472,11 @@ pub async fn handle_sessions(
body.push_str(&format!(
"\n{}",
t(
tp(
lang,
"Reply /resume <id> to continue.",
"回复 /resume <ID> 继续会话。"
prefix,
"Reply {prefix}resume <id> to continue.",
"回复 {prefix}resume <ID> 继续会话。"
)
));
@@ -482,14 +499,16 @@ pub async fn handle_resume(
emitter: &EventEmitter,
bridge: &Arc<Mutex<SessionBridge>>,
lang: Lang,
prefix: &str,
) -> RichMessage {
let conversation_id: i32 = match args.parse() {
Ok(id) => id,
Err(_) => {
return RichMessage::info(t(
return RichMessage::info(tp(
lang,
"Usage: /resume <conversation_id>",
"用法: /resume <会话ID>",
prefix,
"Usage: {prefix}resume <conversation_id>",
"用法: {prefix}resume <会话ID>",
));
}
};
@@ -753,6 +772,7 @@ pub async fn handle_followup(
conn_mgr: &ConnectionManager,
bridge: &Arc<Mutex<SessionBridge>>,
lang: Lang,
prefix: &str,
) -> RichMessage {
let ctx = match sender_context_service::get_or_create(db, channel_id, sender_id).await {
Ok(c) => c,
@@ -762,10 +782,11 @@ pub async fn handle_followup(
let connection_id = match &ctx.current_connection_id {
Some(id) => id.clone(),
None => {
return RichMessage::info(t(
return RichMessage::info(tp(
lang,
"No active session. Use /task to start one.",
"没有活跃的会话,请使用 /task 开始新任务。",
prefix,
"No active session. Use {prefix}task to start one.",
"没有活跃的会话,请使用 {prefix}task 开始新任务。",
));
}
};
@@ -777,10 +798,11 @@ pub async fn handle_followup(
// Connection lost, clear context
drop(bridge_guard);
let _ = sender_context_service::clear_session(db, channel_id, sender_id).await;
return RichMessage::info(t(
return RichMessage::info(tp(
lang,
"Session connection lost. Use /task to start a new one.",
"会话连接已断开,请使用 /task 开始新任务。",
prefix,
"Session connection lost. Use {prefix}task to start a new one.",
"会话连接已断开,请使用 {prefix}task 开始新任务。",
));
}
}
@@ -812,6 +834,11 @@ fn t(lang: Lang, en: &str, zh: &str) -> String {
}
}
/// Like `t()` but replaces `{prefix}` placeholders with the actual command prefix.
fn tp(lang: Lang, prefix: &str, en: &str, zh: &str) -> String {
t(lang, en, zh).replace("{prefix}", prefix)
}
fn agent_type_to_string(at: AgentType) -> String {
serde_json::to_value(at)
.ok()

View File

@@ -17,10 +17,12 @@ use crate::web::event_bridge::WebEventBroadcaster;
use super::manager::ChatChannelManager;
const FLUSH_INTERVAL_SECS: u64 = 5;
const FLUSH_INTERVAL_SECS: u64 = 10;
const BUFFER_FLUSH_THRESHOLD: usize = 500;
const MAX_MESSAGE_LEN: usize = 2000;
const MESSAGE_LANGUAGE_KEY: &str = "chat_message_language";
const COMMAND_PREFIX_KEY: &str = "chat_command_prefix";
const DEFAULT_COMMAND_PREFIX: &str = "/";
pub fn spawn_session_event_subscriber(
broadcaster: Arc<WebEventBroadcaster>,
@@ -59,7 +61,7 @@ pub fn spawn_session_event_subscriber(
}
_ = tokio::time::sleep(Duration::from_secs(FLUSH_INTERVAL_SECS)) => {
if last_heartbeat.elapsed() >= Duration::from_secs(FLUSH_INTERVAL_SECS) {
flush_progress(&bridge, &manager).await;
flush_progress(&bridge, &manager, &db_conn).await;
last_heartbeat = Instant::now();
}
}
@@ -77,6 +79,14 @@ async fn get_lang(db: &DatabaseConnection) -> Lang {
.unwrap_or_default()
}
async fn get_prefix(db: &DatabaseConnection) -> String {
app_metadata_service::get_value(db, COMMAND_PREFIX_KEY)
.await
.ok()
.flatten()
.unwrap_or_else(|| DEFAULT_COMMAND_PREFIX.to_string())
}
async fn handle_acp_event_payload(
payload: &serde_json::Value,
bridge: &Arc<Mutex<SessionBridge>>,
@@ -135,11 +145,11 @@ async fn handle_acp_event_payload(
&& session.last_flushed.elapsed() >= Duration::from_secs(2)
{
let channel_id = session.channel_id;
let buf_len = session.content_buffer.len();
let last_tool = session.tool_calls.last().cloned();
session.last_flushed = Instant::now();
let mut status = format!("... ({buf_len} chars)");
let lang = get_lang(db).await;
let mut status = super::i18n::agent_responding(lang).to_string();
if let Some(tool) = last_tool {
status.push_str(&format!(" | {tool}"));
}
@@ -252,12 +262,13 @@ async fn handle_acp_event_payload(
drop(guard);
let lang = get_lang(db).await;
let prefix = get_prefix(db).await;
let body = match lang {
Lang::ZhCn | Lang::ZhTw => {
format!("Agent 请求权限: {tool_desc}\n\n/approve 批准 | /deny 拒绝 | /approve always 自动批准")
format!("Agent 请求权限: {tool_desc}\n\n{prefix}approve 批准 | {prefix}deny 拒绝 | {prefix}approve always 自动批准")
}
_ => {
format!("Agent requests permission: {tool_desc}\n\n/approve | /deny | /approve always")
format!("Agent requests permission: {tool_desc}\n\n{prefix}approve | {prefix}deny | {prefix}approve always")
}
};
@@ -387,7 +398,11 @@ async fn handle_acp_event_payload(
}
}
async fn flush_progress(bridge: &Arc<Mutex<SessionBridge>>, manager: &ChatChannelManager) {
async fn flush_progress(
bridge: &Arc<Mutex<SessionBridge>>,
manager: &ChatChannelManager,
db: &DatabaseConnection,
) {
let updates: Vec<(i32, String)> = {
let mut guard = bridge.lock().await;
let mut out = Vec::new();
@@ -395,13 +410,14 @@ async fn flush_progress(bridge: &Arc<Mutex<SessionBridge>>, manager: &ChatChanne
if !session.content_buffer.is_empty()
&& session.last_flushed.elapsed() >= Duration::from_secs(FLUSH_INTERVAL_SECS)
{
let buf_len = session.content_buffer.len();
let tool_count = session.tool_calls.len();
session.last_flushed = Instant::now();
out.push((
session.channel_id,
format!("... ({buf_len} chars, {tool_count} tools)"),
));
let last_tool = session.tool_calls.last().cloned();
let lang = get_lang(db).await;
let mut status = super::i18n::agent_responding(lang).to_string();
if let Some(tool) = last_tool {
status.push_str(&format!(" | {tool}"));
}
out.push((session.channel_id, status));
}
}
out