feat(mcp): 增强MCP服务器异常处理和错误描述

- 添加_iter_leaf_exceptions函数用于处理嵌套异常组
- 实现_describe_mcp_exception函数提供详细的MCP服务器错误信息
- 改进connect_mcp_servers中的错误处理,使用更精确的错误描述
- 在日志记录中包含更具体的错误详情

feat(outlook): 优化Outlook集成异常处理

- 添加_iter_leaf_exceptions函数用于异常处理
- 创建_coerce_outlook_mcp_exception函数统一异常转换
- 改进_call_outlook_mcp_tool中的异常捕获和处理
- 对认证令牌获取和MCP调用添加专门的超时和HTTP错误处理

feat(web): 改进Web会话定时任务结果记录

- 实现_record_cron_result_for_web_session函数
- 为Web模式下的独立定时任务执行结果提供持久化存储
- 支持将定时任务响应消息添加到目标会话中
- 确保前端可以显示定时任务执行结果
This commit is contained in:
2026-03-20 17:56:45 +08:00
parent bfa77204bf
commit 5e85129869
3 changed files with 140 additions and 23 deletions

View File

@ -540,6 +540,40 @@ def _infer_cron_route_from_session_key(session_key: str | None) -> tuple[str | N
return channel, chat_id
def _record_cron_result_for_web_session(
*,
session_manager: SessionManager,
job: CronJob,
result: CronExecutionResult,
) -> str | None:
"""Persist standalone web cron output so the frontend can surface it."""
target_session_key = _resolve_cron_session_key(job)
if not target_session_key.startswith("web:"):
return None
# agent_turn jobs already write their own history via AgentLoop.process_direct().
if job.payload.kind == "agent_turn":
return target_session_key
# reminder/system_event jobs bypass the agent loop, so standalone web mode
# must append the final message into the target session explicitly.
if job.payload.kind != "system_event" or not job.payload.deliver or not result.response:
return None
session = session_manager.get_or_create(target_session_key)
session.add_message(
"assistant",
result.response,
metadata={
"source": "cron",
"job_id": job.id,
"job_name": job.name,
},
)
session_manager.save(session)
return target_session_key
# ============================================================================
# App factory
# ============================================================================
@ -635,8 +669,12 @@ def create_app(
default_channel="web",
default_chat_id="default",
)
target_session_key = _resolve_cron_session_key(job)
if job.payload.kind == "agent_turn" and target_session_key.startswith("web:"):
target_session_key = _record_cron_result_for_web_session(
session_manager=session_manager,
job=job,
result=result,
)
if target_session_key:
await websocket_broadcaster.broadcast({
"type": "session_updated",
"session_id": target_session_key,