From c09e240a6a45b641edb6e7f94746be492fb8472a Mon Sep 17 00:00:00 2001 From: zmdyy0318 Date: Mon, 9 Nov 2026 20:50:56 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(tasks):=20Agent=20=E5=86=85=E9=83=A8=20?= =?UTF-8?q?PostStop=20=E6=97=B6=E6=8C=89=E5=81=9C=E6=AD=A2=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=9B=9E=E8=B0=83=E6=94=B6=E5=B0=BE=EF=BC=8C=E5=9B=9E?= =?UTF-8?q?=E6=94=B6=E6=9C=AC=E8=BD=AE=20Agent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tasker::post_stop 会清空排队任务且不为其发送回调,只补发一个 MaaTaskerPostStop 停止任务。 Agent 内部调用 PostStop 时,被清掉的任务永远等不到终态,收尾不会触发,本轮 Agent 残留, 下次启动时新 Agent 注册同名自定义识别失败,报 "MaaFramework internal error: status 0"。 收到 MaaTaskerPostStop 的终态回调且队列仍有任务无结果时,按失败走现有收尾流程。 --- src-tauri/src/commands/utils.rs | 50 +++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/commands/utils.rs b/src-tauri/src/commands/utils.rs index 71702ffb..28fc8686 100644 --- a/src-tauri/src/commands/utils.rs +++ b/src-tauri/src/commands/utils.rs @@ -81,14 +81,22 @@ pub fn handle_task_callback( return; } - // 解析 task_id - let task_id: i64 = match serde_json::from_str::(details) - .ok() + // 解析 task_id 与 entry + let detail_json = serde_json::from_str::(details).ok(); + let task_id: i64 = match detail_json + .as_ref() .and_then(|v| v.get("task_id").and_then(|id| id.as_i64())) { Some(id) => id, None => return, }; + let entry = detail_json + .as_ref() + .and_then(|v| v.get("entry").and_then(|e| e.as_str())); + if !is_started && entry == Some(POST_STOP_ENTRY) { + handle_post_stop_callback(maa_state, app, instance_id, lifetime); + return; + } let mut completion = None; { @@ -151,6 +159,42 @@ pub fn handle_task_callback( } } +/// MaaFramework `Tasker::post_stop` 补发的停止任务 entry +const POST_STOP_ENTRY: &str = "MaaTaskerPostStop"; + +/// `Tasker::post_stop` 会清空排队任务且不为其发送回调,只补发一个停止任务。 +/// Agent 内部调用 PostStop 时,这是本轮唯一必定到达的终态回调:若仍有任务没有结果, +/// 按失败收尾以回收本轮 Agent;队列已全部有结果时由原路径收尾,这里不覆盖其状态。 +fn handle_post_stop_callback( + maa_state: &Arc, + app: &AppHandle, + instance_id: &str, + lifetime: &super::callback_lifecycle::CallbackGuard, +) { + let completion = { + let mut instances = match maa_state.instances.lock() { + Ok(g) => g, + Err(_) => return, + }; + let instance = match instances.get_mut(instance_id) { + Some(i) => i, + None => return, + }; + if !instance.callback_lifetime.is_current(lifetime) + || instance.task_run_state.pending_task_ids.is_empty() + || instance.queue_result().is_some() + { + return; + } + instance.begin_completion(false) + }; + + if let Some((tasker, completion)) = completion { + emit_state_changed(app, instance_id, "task-progress"); + schedule_run_completion(maa_state, app, instance_id, tasker, completion); + } +} + /// Every terminal path shares the same idle/cleanup/notification barrier. Called /// only after releasing the instance and telemetry locks; begin() owns the worker. pub(super) fn schedule_run_completion( From 1283b8b2d8ff1d29dcee02af577c96eddd06a5af Mon Sep 17 00:00:00 2001 From: zmdyy0318 Date: Mon, 9 Nov 2026 21:21:17 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(tasks):=20Agent=20=E5=86=85=E9=83=A8=20?= =?UTF-8?q?PostStop=20=E6=94=B6=E5=B0=BE=E6=97=B6=E4=B8=8E=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E5=81=9C=E6=AD=A2=E5=AF=B9=E9=BD=90=EF=BC=8C=E6=A0=87?= =?UTF-8?q?=E8=AE=B0=E8=A2=AB=E6=B8=85=E9=99=A4=E4=BB=BB=E5=8A=A1=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E5=B9=B6=E9=98=BB=E6=AD=A2=E8=BF=BD=E5=8A=A0=E6=8F=90?= =?UTF-8?q?=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/commands/utils.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src-tauri/src/commands/utils.rs b/src-tauri/src/commands/utils.rs index 28fc8686..174cbd48 100644 --- a/src-tauri/src/commands/utils.rs +++ b/src-tauri/src/commands/utils.rs @@ -181,11 +181,22 @@ fn handle_post_stop_callback( None => return, }; if !instance.callback_lifetime.is_current(lifetime) + // MXU 自己发起的停止已由 stop_task_impl 收尾 + || instance.stop_in_progress || instance.task_run_state.pending_task_ids.is_empty() || instance.queue_result().is_some() { return; } + // 与 stop_task_impl 对齐:阻止已准入的追加提交,被清掉的任务标记为失败 + instance.run_completion.cancel_submission(); + instance.stop_in_progress = true; + instance.stop_started_at = Some(std::time::Instant::now()); + for status in instance.task_run_state.statuses.values_mut() { + if status == "pending" || status == "running" { + *status = "failed".to_string(); + } + } instance.begin_completion(false) };