feat: sub-agent streaming progress — TaskTool emits real-time status events
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

- Rust: LoopEvent::SubtaskStatus variant added to loop_runner.rs
- Rust: ToolContext.event_sender field for streaming tool progress
- Rust: TaskTool emits started/running/completed/failed via event_sender
- Rust: StreamChatEvent::SubtaskStatus mapped in Tauri chat command
- TS: StreamEventSubtaskStatus type + onSubtaskStatus callback added
- TS: kernel-chat.ts handles subtaskStatus event from Tauri
- TS: streamStore.ts wires callback, maps backend→frontend status,
  updates assistant message subtasks array in real-time
This commit is contained in:
iven
2026-04-06 13:05:37 +08:00
parent 15a1849255
commit 9871c254be
7 changed files with 116 additions and 1 deletions

View File

@@ -10,7 +10,7 @@ use zclaw_types::{AgentId, Result, ZclawError};
use zclaw_memory::MemoryStore;
use crate::driver::LlmDriver;
use crate::loop_runner::AgentLoop;
use crate::loop_runner::{AgentLoop, LoopEvent};
use crate::tool::{Tool, ToolContext, ToolRegistry};
use crate::tool::builtin::register_builtin_tools;
use std::sync::Arc;
@@ -106,6 +106,15 @@ impl Tool for TaskTool {
description, max_iterations
);
// Emit subtask_started event
if let Some(ref tx) = context.event_sender {
let _ = tx.send(LoopEvent::SubtaskStatus {
description: description.to_string(),
status: "started".to_string(),
detail: None,
}).await;
}
// Create a sub-agent with its own ID
let sub_agent_id = AgentId::new();
@@ -148,6 +157,15 @@ impl Tool for TaskTool {
sub_loop = sub_loop.with_path_validator(validator.clone());
}
// Emit subtask_running event
if let Some(ref tx) = context.event_sender {
let _ = tx.send(LoopEvent::SubtaskStatus {
description: description.to_string(),
status: "running".to_string(),
detail: Some("子Agent正在执行中...".to_string()),
}).await;
}
// Execute the sub-agent loop (non-streaming — collect full result)
let result = match sub_loop.run(session_id.clone(), prompt.to_string()).await {
Ok(loop_result) => {
@@ -155,6 +173,19 @@ impl Tool for TaskTool {
"[TaskTool] Sub-agent completed: {} iterations, {} input tokens, {} output tokens",
loop_result.iterations, loop_result.input_tokens, loop_result.output_tokens
);
// Emit subtask_completed event
if let Some(ref tx) = context.event_sender {
let _ = tx.send(LoopEvent::SubtaskStatus {
description: description.to_string(),
status: "completed".to_string(),
detail: Some(format!(
"完成 ({}次迭代, {}输入token)",
loop_result.iterations, loop_result.input_tokens
)),
}).await;
}
json!({
"status": "completed",
"description": description,
@@ -166,6 +197,16 @@ impl Tool for TaskTool {
}
Err(e) => {
tracing::warn!("[TaskTool] Sub-agent failed: {}", e);
// Emit subtask_failed event
if let Some(ref tx) = context.event_sender {
let _ = tx.send(LoopEvent::SubtaskStatus {
description: description.to_string(),
status: "failed".to_string(),
detail: Some(e.to_string()),
}).await;
}
json!({
"status": "failed",
"description": description,