openclaw-agent-dashboard 1.0.29 → 1.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -330,6 +330,37 @@ def _clean_task_name(task_name: str) -> str:
330
330
  return ''
331
331
 
332
332
 
333
+ def _enrich_main_agent_active_tasks_if_needed(
334
+ agent_active_tasks: Dict[str, List[Dict[str, Any]]],
335
+ main_agent_id: str,
336
+ ) -> Dict[str, List[Dict[str, Any]]]:
337
+ """
338
+ 主 Agent 仅更新主会话、subagents/runs 无条目时,agentActiveTasks 为空;
339
+ 在仍为 working 时补一条会话摘要,便于卡片「并行任务」区有文案。
340
+ """
341
+ from status.status_calculator import calculate_agent_status, get_current_task
342
+
343
+ if calculate_agent_status(main_agent_id) != 'working':
344
+ return agent_active_tasks
345
+ if agent_active_tasks.get(main_agent_id):
346
+ return agent_active_tasks
347
+ hint = get_current_task(main_agent_id)
348
+ name = _clean_task_name(hint) if hint else ''
349
+ if not name:
350
+ return agent_active_tasks
351
+ merged = dict(agent_active_tasks)
352
+ merged[main_agent_id] = [
353
+ {
354
+ 'id': 'task-main-session',
355
+ 'name': name,
356
+ 'status': 'working',
357
+ 'timestamp': None,
358
+ 'featureId': None,
359
+ }
360
+ ]
361
+ return merged
362
+
363
+
333
364
  def _get_agent_error_info(agent_id: str) -> Optional[Dict[str, Any]]:
334
365
  """获取 agent 的错误/异常信息"""
335
366
  from session_reader import get_last_error, has_recent_errors
@@ -505,7 +536,7 @@ async def get_collaboration():
505
536
  get_model_display_name, get_main_agent_id
506
537
  )
507
538
  from data.subagent_reader import get_active_runs
508
- from status.status_calculator import calculate_agent_status
539
+ from status.status_calculator import calculate_agent_status, get_current_task
509
540
 
510
541
  nodes = []
511
542
  edges = []
@@ -555,6 +586,8 @@ async def get_collaboration():
555
586
  break
556
587
  if not main_current_task and active_runs:
557
588
  main_current_task = _clean_task_name(active_runs[0].get('task', ''))
589
+ if not main_current_task:
590
+ main_current_task = _clean_task_name(get_current_task(main_agent_id))
558
591
  main_error = _get_agent_error_info(main_agent_id)
559
592
  main_stuck = _check_agent_stuck(main_agent_id)
560
593
 
@@ -773,7 +806,10 @@ async def get_collaboration():
773
806
  depths[aid] = 1
774
807
 
775
808
  # 构建多任务并行数据
776
- agent_active_tasks = _build_agent_active_tasks(active_runs, main_agent_id)
809
+ agent_active_tasks = _enrich_main_agent_active_tasks_if_needed(
810
+ _build_agent_active_tasks(active_runs, main_agent_id),
811
+ main_agent_id,
812
+ )
777
813
 
778
814
  return CollaborationFlow(
779
815
  nodes=nodes,
@@ -849,20 +885,8 @@ async def get_collaboration_dynamic():
849
885
  except Exception as e:
850
886
  logger.warning(f"Failed to get display status for {aid}: {e}")
851
887
 
852
- main_status = "working" if active_runs else "idle"
853
- agent_statuses[main_agent_id] = main_status
854
-
855
- # 获取主 agent 的详细显示状态
856
- try:
857
- main_dyn_status = get_display_status(main_agent_id)
858
- agent_dynamic_statuses[main_agent_id] = AgentDisplayStatus(
859
- status=main_dyn_status['status'],
860
- display=main_dyn_status['display'],
861
- duration=main_dyn_status['duration'],
862
- alert=main_dyn_status['alert']
863
- )
864
- except Exception as e:
865
- logger.warning(f"Failed to get display status for {main_agent_id}: {e}")
888
+ # Agent 状态已在上方循环中与 calculate_agent_status 一致,勿再用「有无 active_runs」覆盖
889
+ # (独立 PM 仅更新主会话时 runs 可能为空,否则会误显示空闲)
866
890
 
867
891
  # 处理活跃任务(简化:不在流程图中创建任务节点,任务信息由 agentActiveTasks 提供)
868
892
  # 任务详情在 Agent 卡片内显示,流程图只显示 Agent 之间的委托关系
@@ -896,7 +920,10 @@ async def get_collaboration_dynamic():
896
920
  ]
897
921
 
898
922
  # 构建多任务并行数据
899
- agent_active_tasks = _build_agent_active_tasks(active_runs, main_agent_id)
923
+ agent_active_tasks = _enrich_main_agent_active_tasks_if_needed(
924
+ _build_agent_active_tasks(active_runs, main_agent_id),
925
+ main_agent_id,
926
+ )
900
927
 
901
928
  return CollaborationDynamic(
902
929
  activePath=list(set(active_path)),
@@ -136,6 +136,29 @@ def get_recent_messages(agent_id: str, limit: int = 10) -> List[Dict[str, Any]]:
136
136
  return messages[-limit:] if len(messages) > limit else messages
137
137
 
138
138
 
139
+ def get_latest_user_message_text(agent_id: str, scan_limit: int = 80) -> str:
140
+ """
141
+ 从最新会话中取最近一条 user 消息的文本(不做截断)。
142
+ 用于无 subagent run 时展示当前任务摘要(独立 PM / 仅主会话)。
143
+ """
144
+ messages = get_recent_messages(agent_id, limit=scan_limit)
145
+ for msg in reversed(messages):
146
+ if msg.get('role') != 'user':
147
+ continue
148
+ content = msg.get('content', [])
149
+ if isinstance(content, str):
150
+ t = content.strip()
151
+ if t:
152
+ return t
153
+ elif isinstance(content, list):
154
+ for c in content:
155
+ if isinstance(c, dict) and c.get('type') == 'text':
156
+ t = (c.get('text') or '').strip()
157
+ if t:
158
+ return t
159
+ return ''
160
+
161
+
139
162
  def has_recent_errors(agent_id: str, minutes: int = 5) -> bool:
140
163
  """检查最近是否有错误"""
141
164
  messages = get_recent_messages(agent_id, limit=50)
@@ -9,7 +9,7 @@ sys.path.append(str(Path(__file__).parent.parent))
9
9
  import logging
10
10
  import time
11
11
  from typing import Literal, Dict, Any, List, Optional
12
- from data.config_reader import get_agents_list, get_agent_config
12
+ from data.config_reader import get_agents_list, get_agent_config, get_main_agent_id
13
13
  from data.subagent_reader import is_agent_working, get_agent_runs
14
14
  from data.session_reader import (
15
15
  has_recent_errors,
@@ -105,19 +105,34 @@ def get_agents_with_status() -> list:
105
105
 
106
106
 
107
107
  def get_current_task(agent_id: str) -> str:
108
- """获取 Agent 当前任务"""
108
+ """
109
+ 获取 Agent 当前任务描述。
110
+ 优先 subagents/runs.json 中该 Agent 作为执行者的记录。
111
+ 子 Agent 任务仅来自 runs(业务上不存在「仅有会话、无 run」的工作态)。
112
+ 主 Agent 无 run 时,仅在仍活跃时用主会话最近一条 user 消息作摘要,避免空闲时显示陈旧文案。
113
+ """
109
114
  runs = get_agent_runs(agent_id, limit=1)
110
- if not runs:
115
+ if runs:
116
+ task = runs[0].get('task', '') or ''
117
+ if len(task) > 60:
118
+ task = task[:57] + '...'
119
+ return task
120
+
121
+ if agent_id != get_main_agent_id():
111
122
  return ''
112
123
 
113
- run = runs[0]
114
- task = run.get('task', '')
124
+ if not is_agent_working(agent_id) and not has_recent_session_activity(agent_id, minutes=2):
125
+ return ''
115
126
 
116
- # 截取前60个字符(统一长度)
117
- if len(task) > 60:
118
- task = task[:57] + '...'
127
+ from data.session_reader import get_latest_user_message_text
119
128
 
120
- return task
129
+ text = get_latest_user_message_text(agent_id)
130
+ if not text:
131
+ return ''
132
+ text = ' '.join(text.split())
133
+ if len(text) > 60:
134
+ text = text[:57] + '...'
135
+ return text
121
136
 
122
137
 
123
138
  def get_last_active_time(agent_id: str) -> int:
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-agent-dashboard",
3
3
  "name": "OpenClaw Agent Dashboard",
4
4
  "description": "多 Agent 可视化看板 - 状态、任务、API、工作流、协作流程",
5
- "version": "1.0.29",
5
+ "version": "1.0.30",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-agent-dashboard",
3
- "version": "1.0.29",
3
+ "version": "1.0.30",
4
4
  "description": "多 Agent 可视化看板 - OpenClaw 插件",
5
5
  "main": "index.js",
6
6
  "openclaw": {