diff --git a/src/langbot/pkg/api/http/service/monitoring_execution_details.py b/src/langbot/pkg/api/http/service/monitoring_execution_details.py
index 1b5fa94f9..d8496facc 100644
--- a/src/langbot/pkg/api/http/service/monitoring_execution_details.py
+++ b/src/langbot/pkg/api/http/service/monitoring_execution_details.py
@@ -4,6 +4,7 @@ from __future__ import annotations
import datetime
import json
+from langbot_plugin.api.entities.builtin.platform.events import event_summary
import sqlalchemy as sa
from sqlalchemy.ext.asyncio import AsyncSession
@@ -32,19 +33,8 @@ def event_content(content, event_data):
"""Keep event-specific properties alongside the normalized message input."""
if not isinstance(event_data, dict) or not event_data:
return content
- if not isinstance(content, dict):
- return event_data if not content else {**event_data, 'input': content}
- merged = dict(event_data)
- conflicts = {}
- for key, value in content.items():
- if key not in merged:
- merged[key] = value
- elif merged[key] != value and value is not None and value != '' and value != [] and value != {}:
- conflicts[key] = value
- if conflicts:
- # Never silently overwrite a custom field that shares an input name.
- return {'event': merged, 'input': conflicts}
- return merged
+ # Keep the original event intact; text is the existing normalized input field.
+ return {'text': event_summary(event_data), 'event': event_data, 'input': content}
def event_item(row):
diff --git a/src/langbot/pkg/platform/adapters/aiocqhttp/manifest.yaml b/src/langbot/pkg/platform/adapters/aiocqhttp/manifest.yaml
index 6911881c9..e14581cfb 100644
--- a/src/langbot/pkg/platform/adapters/aiocqhttp/manifest.yaml
+++ b/src/langbot/pkg/platform/adapters/aiocqhttp/manifest.yaml
@@ -15,6 +15,7 @@ metadata:
spec:
categories:
+ - popular
- protocol
help_links:
zh: https://langbot.app/docs/zh/usage/platforms/qq/aiocqhttp/napcat
diff --git a/src/langbot/pkg/platform/adapters/dingtalk/manifest.yaml b/src/langbot/pkg/platform/adapters/dingtalk/manifest.yaml
index 8e74f21d1..93e6da6de 100644
--- a/src/langbot/pkg/platform/adapters/dingtalk/manifest.yaml
+++ b/src/langbot/pkg/platform/adapters/dingtalk/manifest.yaml
@@ -15,6 +15,7 @@ metadata:
spec:
categories:
+ - popular
- china
help_links:
zh: https://langbot.app/docs/zh/usage/platforms/dingtalk
diff --git a/src/langbot/pkg/platform/botmgr.py b/src/langbot/pkg/platform/botmgr.py
index 291acd1db..fa80a5c6b 100644
--- a/src/langbot/pkg/platform/botmgr.py
+++ b/src/langbot/pkg/platform/botmgr.py
@@ -704,33 +704,20 @@ class RuntimeBot:
@classmethod
def _build_agent_input(cls, event: platform_events.EBAEvent) -> AgentInput:
- text = None
+ text = platform_events.event_summary(event)
contents: list[dict[str, typing.Any]] = []
message_chain = getattr(event, 'message_chain', None)
if message_chain:
- text_parts: list[str] = []
try:
for component in message_chain:
- if isinstance(component, platform_message.Plain):
- text_parts.append(component.text)
- elif isinstance(component, platform_message.Image):
+ if isinstance(component, platform_message.Image):
if component.url:
contents.append({'type': 'image_url', 'image_url': {'url': component.url}})
elif component.base64:
contents.append({'type': 'image_base64', 'image_base64': component.base64})
except TypeError:
- text_parts.append(str(message_chain))
- text = ''.join(text_parts) or str(message_chain)
-
- if text is None:
- feedback_content = getattr(event, 'feedback_content', None)
- if feedback_content:
- text = str(feedback_content)
- elif getattr(event, 'action', None):
- text = str(getattr(event, 'action'))
- else:
- text = str(getattr(event, 'type', 'event'))
+ pass
if text:
contents.insert(0, {'type': 'text', 'text': text})
diff --git a/src/langbot/pkg/platform/sources/aiocqhttp.yaml b/src/langbot/pkg/platform/sources/aiocqhttp.yaml
index d9880bcfe..fdabf4969 100644
--- a/src/langbot/pkg/platform/sources/aiocqhttp.yaml
+++ b/src/langbot/pkg/platform/sources/aiocqhttp.yaml
@@ -14,6 +14,7 @@ metadata:
spec:
legacy: true
categories:
+ - popular
- protocol
help_links:
zh: https://langbot.app/docs/zh/usage/platforms/qq/aiocqhttp/napcat
@@ -59,4 +60,4 @@ spec:
execution:
python:
path: ./aiocqhttp.py
- attr: AiocqhttpAdapter
\ No newline at end of file
+ attr: AiocqhttpAdapter
diff --git a/src/langbot/pkg/platform/sources/dingtalk.yaml b/src/langbot/pkg/platform/sources/dingtalk.yaml
index f8a7e6395..dcde99126 100644
--- a/src/langbot/pkg/platform/sources/dingtalk.yaml
+++ b/src/langbot/pkg/platform/sources/dingtalk.yaml
@@ -14,6 +14,7 @@ metadata:
spec:
legacy: true
categories:
+ - popular
- china
help_links:
zh: https://langbot.app/docs/zh/usage/platforms/dingtalk
diff --git a/src/langbot/pkg/platform/sources/http_bot.yaml b/src/langbot/pkg/platform/sources/http_bot.yaml
index 97392a444..5e13f437e 100644
--- a/src/langbot/pkg/platform/sources/http_bot.yaml
+++ b/src/langbot/pkg/platform/sources/http_bot.yaml
@@ -8,10 +8,14 @@ metadata:
zh_Hant: HTTP 通用接入
ja_JP: HTTP ボット
description:
- en_US: Integrate any backend over plain HTTP. Push messages in via a signed webhook, receive replies on a callback URL. Server-to-server, no long-lived connection. Preserves message aggregation (N->1) and multi-part replies (1->M).
- zh_Hans: 通过 HTTP 接入任意后端系统。以签名 Webhook 推入消息,在回调地址接收回复。面向服务间集成,无需长连接。完整保留消息聚合(多条合一)与多段回复(一条问、多条回)能力。
- zh_Hant: 透過 HTTP 接入任意後端系統。以簽名 Webhook 推入訊息,在回調地址接收回覆。面向服務間整合,無需長連線。完整保留訊息聚合(多條合一)與多段回覆(一條問、多條回)能力。
- ja_JP: 任意のバックエンドを HTTP で接続。署名付き Webhook でメッセージを送信し、コールバック URL で返信を受信します。サーバー間連携、長時間接続不要。メッセージ集約(N→1)とマルチパート返信(1→M)に対応。
+ en_US: Connect backend systems via HTTP. Send messages through signed webhooks and receive replies via callbacks.
+ zh_Hans: 通过 HTTP 接入后端系统,以签名 Webhook 发送消息,通过回调接收回复。
+ zh_Hant: 透過 HTTP 接入後端系統,以簽名 Webhook 傳送訊息,透過回呼接收回覆。
+ ja_JP: HTTP でバックエンドに接続。署名付き Webhook でメッセージを送信し、コールバックで返信を受信します。
+ es_ES: Conecta sistemas mediante HTTP. Envía mensajes con webhooks firmados y recibe respuestas mediante callbacks.
+ ru_RU: Подключайте серверные системы по HTTP. Отправляйте сообщения через подписанные вебхуки и получайте ответы через обратные вызовы.
+ th_TH: เชื่อมต่อระบบผ่าน HTTP ส่งข้อความด้วย Webhook ที่ลงลายเซ็นและรับคำตอบผ่าน callback
+ vi_VN: Kết nối hệ thống qua HTTP. Gửi tin nhắn bằng webhook có chữ ký và nhận phản hồi qua callback.
icon: http_bot.svg
spec:
categories:
diff --git a/src/langbot/pkg/platform/sources/line.yaml b/src/langbot/pkg/platform/sources/line.yaml
index 83f520286..b99ab5b2c 100644
--- a/src/langbot/pkg/platform/sources/line.yaml
+++ b/src/langbot/pkg/platform/sources/line.yaml
@@ -20,6 +20,7 @@ metadata:
icon: line.png
spec:
categories:
+ - popular
- global
help_links:
zh: https://langbot.app/docs/zh/usage/platforms/line
@@ -81,4 +82,4 @@ spec:
execution:
python:
path: ./line.py
- attr: LINEAdapter
\ No newline at end of file
+ attr: LINEAdapter
diff --git a/src/langbot/pkg/platform/sources/mattermost.yaml b/src/langbot/pkg/platform/sources/mattermost.yaml
index f46b125bb..6c958e42d 100644
--- a/src/langbot/pkg/platform/sources/mattermost.yaml
+++ b/src/langbot/pkg/platform/sources/mattermost.yaml
@@ -22,7 +22,6 @@ metadata:
spec:
categories:
- global
- - popular
config:
- name: server_url
label:
diff --git a/src/langbot/pkg/platform/sources/openclaw_weixin.py b/src/langbot/pkg/platform/sources/openclaw_weixin.py
index 4726888b1..3991f5555 100644
--- a/src/langbot/pkg/platform/sources/openclaw_weixin.py
+++ b/src/langbot/pkg/platform/sources/openclaw_weixin.py
@@ -385,6 +385,7 @@ class OpenClawWeixinAdapter(abstract_platform_adapter.AbstractMessagePlatformAda
await self.logger.error(
f'Failed to send component {type(component).__name__}: {traceback.format_exc()}'
)
+ raise
async def reply_message(
self,
diff --git a/web/src/app/home/agents/components/AgentMonitoringTab.tsx b/web/src/app/home/agents/components/AgentMonitoringTab.tsx
index 34c712240..a5cff9e97 100644
--- a/web/src/app/home/agents/components/AgentMonitoringTab.tsx
+++ b/web/src/app/home/agents/components/AgentMonitoringTab.tsx
@@ -1,6 +1,7 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { Brain, ChevronRight, RefreshCw, Wrench } from 'lucide-react';
+import { ChevronRight, RefreshCw } from 'lucide-react';
+import AgentRunTimeline, { isReplyStep, isMockStep } from './AgentRunTimeline';
import { httpClient } from '@/app/infra/http/HttpClient';
import type {
AgentPlatformTool,
@@ -206,7 +207,12 @@ export default function AgentMonitoringTab({
const labels = Object.fromEntries(
platformTools.map((tool) => [tool.name, extractI18nObject(tool.label)]),
);
- const steps = executionSteps(events);
+ const steps = executionSteps(events).filter(
+ (step) => step.kind === 'tool' || step.text.trim() || step.reasoning.trim(),
+ );
+ const toolSteps = steps.filter((step) => step.kind === 'tool');
+ const sentCount = toolSteps.filter((step) => isReplyStep(step) && !isMockStep(step) && step.status === 'completed').length;
+ const sendFailed = toolSteps.some((step) => isReplyStep(step) && step.status === 'failed');
const duration = selected ? processorRunDuration(selected) : null;
const failureReason =
selected?.status_reason ||
@@ -261,7 +267,7 @@ export default function AgentMonitoringTab({
- {selected.runner_id} -
+{t('agents.monitoring.runStatus')}
+{stateLabel(selected)}
+{t('agents.monitoring.toolCalls')}
+{toolSteps.length}
+{t('agents.monitoring.delivery')}
++ {t(`agents.monitoring.${sendFailed ? 'sendFailed' : sentCount ? 'sendCount' : 'noSend'}`, { count: sentCount })} +
+{step.text}
- )} - {step.reasoning && ( -- {step.error} -
- )} -{t('agents.monitoring.noSteps')}
} +{step.text}
} + {step.reasoning &&{replyText}
} + {!reply && actionText &&{actionText}
} + {summary.recipient &&{t('agents.monitoring.recipient')}: {summary.recipient}
} + {step.error &&{step.error}
} + {status === 'completed' && (mock || !reply) &&{t(`agents.monitoring.${mock ? 'simulatedAction' : 'actionCompleted'}`)}
} + {(step.parameters != null || step.result != null) &&