oudecheng 4cb26b5b67 feat: 子智能体任务消息查看,实时广播工具调用事件
- 新增 LoadTaskMessages 命令,加载子智能体任务的历史消息
- SubAgentEmitter 通过 MessageBus 实时广播子智能体工具调用
- 前端新增子智能体视图,支持导航进入/退出子智能体会话
- 外部渠道过滤子智能体事件,避免推送到飞书/微信
- ToolCall/ToolResult 新增 subagent_task_id 字段

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 11:15:38 +08:00

168 lines
4.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

pub mod ws_adapter;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionSummary {
pub session_id: String,
pub title: String,
pub channel_name: String,
pub chat_id: String,
pub message_count: i64,
pub last_active_at: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub archived_at: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Channel {
pub id: String,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(rename = "isWritable")]
pub is_writable: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TopicSummary {
pub topic_id: String,
pub session_id: String,
pub title: String,
pub message_count: i64,
pub created_at: i64,
pub last_active_at: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MediaSummary {
pub path: String,
pub media_type: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content_base64: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum WsInbound {
/// 普通用户消息
#[serde(rename = "message")]
Message {
content: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
channel: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
chat_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
sender_id: Option<String>,
},
/// 命令JSON 格式)
#[serde(rename = "command")]
Command { payload: String },
#[serde(rename = "ping")]
Ping,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum WsOutbound {
#[serde(rename = "assistant_response")]
AssistantResponse {
id: String,
content: String,
role: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
attachments: Vec<MediaSummary>,
},
#[serde(rename = "tool_call")]
ToolCall {
id: String,
tool_call_id: String,
tool_name: String,
arguments: serde_json::Value,
content: String,
role: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
subagent_task_id: Option<String>,
},
#[serde(rename = "tool_result")]
ToolResult {
id: String,
tool_call_id: String,
tool_name: String,
content: String,
role: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
subagent_task_id: Option<String>,
},
#[serde(rename = "tool_pending")]
ToolPending {
id: String,
tool_call_id: String,
tool_name: String,
content: String,
role: String,
resume_hint: String,
},
#[serde(rename = "error")]
Error { code: String, message: String },
#[serde(rename = "session_established")]
SessionEstablished { session_id: String },
#[serde(rename = "session_created")]
SessionCreated { session_id: String, title: String },
#[serde(rename = "session_list")]
SessionList {
sessions: Vec<SessionSummary>,
#[serde(default, skip_serializing_if = "Option::is_none")]
current_session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
channel_name: Option<String>,
},
#[serde(rename = "channel_list")]
ChannelList {
channels: Vec<Channel>,
},
#[serde(rename = "topic_list")]
TopicList {
topics: Vec<TopicSummary>,
session_id: String,
},
#[serde(rename = "session_loaded")]
SessionLoaded {
session_id: String,
title: String,
message_count: i64,
},
#[serde(rename = "session_saved")]
SessionSaved { session_id: String, filepath: String },
#[serde(rename = "task_messages_loaded")]
TaskMessagesLoaded {
task_id: String,
description: String,
subagent_type: String,
status: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
summary: Option<String>,
},
#[serde(rename = "pong")]
Pong,
}
pub fn parse_inbound(raw: &str) -> Result<WsInbound, serde_json::Error> {
serde_json::from_str(raw)
}
pub fn serialize_inbound(msg: &WsInbound) -> Result<String, serde_json::Error> {
serde_json::to_string(msg)
}
pub fn serialize_outbound(msg: &WsOutbound) -> Result<String, serde_json::Error> {
serde_json::to_string(msg)
}