From b16ceb2d88488611962e2ea1f4668eae2af1ce20 Mon Sep 17 00:00:00 2001 From: Ming Li Date: Fri, 13 Mar 2026 19:11:23 -0400 Subject: [PATCH 01/27] =?UTF-8?q?feat(scheduler):=20=E6=94=B6=E5=8F=A3?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=8F=90=E4=BA=A4=E9=97=B8=E9=97=A8=E5=B9=B6?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=86=99=E5=9B=9E=E9=87=8D=E8=AF=95=E9=93=BE?= =?UTF-8?q?=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/server.py | 2284 ++++++++++++++++++++++++-- scripts/freeze_scheduler_baseline.py | 102 ++ scripts/kanban_update.py | 121 +- scripts/replay_scheduler_report.py | 164 ++ tests/test_scheduler_gate.py | 223 +++ 5 files changed, 2733 insertions(+), 161 deletions(-) create mode 100644 scripts/freeze_scheduler_baseline.py create mode 100644 scripts/replay_scheduler_report.py create mode 100644 tests/test_scheduler_gate.py diff --git a/dashboard/server.py b/dashboard/server.py index 57fd64b2..1fac2442 100644 --- a/dashboard/server.py +++ b/dashboard/server.py @@ -11,7 +11,7 @@ GET /api/model-change-log → data/model_change_log.json GET /api/last-result → data/last_model_change_result.json """ -import json, pathlib, subprocess, sys, threading, argparse, datetime, logging, re, os +import json, pathlib, subprocess, sys, threading, argparse, datetime, logging, re, os, shlex, uuid from http.server import BaseHTTPRequestHandler, HTTPServer from urllib.parse import urlparse from urllib.request import Request, urlopen @@ -107,41 +107,69 @@ def handle_task_action(task_id, action, reason): old_state = task.get('state', '') _ensure_scheduler(task) _scheduler_snapshot(task, f'task-action-before-{action}') + run_id = _new_run_id() + _acquire_lease(task, stage=old_state, role='manual', owner_run_id=run_id, ttl_sec=180, force_takeover=True) if action == 'stop': - task['state'] = 'Blocked' - task['block'] = reason or '皇上叫停' - task['now'] = f'⏸️ 已暂停:{reason}' + commit = commit_state_change( + task, + action='manual_decide', + reason_code='manual_stop', + owner_run_id=run_id, + expected_version=task.get('_scheduler', {}).get('stateVersion'), + to_state='Blocked', + now_text=f'⏸️ 已暂停:{reason}', + block_text=reason or '皇上叫停', + flow_from='皇上', + flow_remark=f'⏸️ 叫停:{reason or "无"}', + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'{task_id} 叫停失败: {commit.get("blockedBy")}'} + task['_prev_state'] = old_state elif action == 'cancel': - task['state'] = 'Cancelled' - task['block'] = reason or '皇上取消' - task['now'] = f'🚫 已取消:{reason}' + commit = commit_state_change( + task, + action='manual_decide', + reason_code='manual_cancel', + owner_run_id=run_id, + expected_version=task.get('_scheduler', {}).get('stateVersion'), + to_state='Cancelled', + now_text=f'🚫 已取消:{reason}', + block_text=reason or '皇上取消', + flow_from='皇上', + flow_remark=f'🚫 取消:{reason or "无"}', + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'{task_id} 取消失败: {commit.get("blockedBy")}'} + task['_prev_state'] = old_state elif action == 'resume': - # Resume to previous active state or Doing - task['state'] = task.get('_prev_state', 'Doing') - task['block'] = '无' - task['now'] = f'▶️ 已恢复执行' - - if action in ('stop', 'cancel'): - task['_prev_state'] = old_state # Save for resume - - task.setdefault('flow_log', []).append({ - 'at': now_iso(), - 'from': '皇上', - 'to': task.get('org', ''), - 'remark': f'{"⏸️ 叫停" if action == "stop" else "🚫 取消" if action == "cancel" else "▶️ 恢复"}:{reason}' - }) - - if action == 'resume': - _scheduler_mark_progress(task, f'恢复到 {task.get("state", "Doing")}') - else: - _scheduler_add_flow(task, f'皇上{action}:{reason or "无"}') - - task['updatedAt'] = now_iso() + resume_state = task.get('_prev_state', 'Doing') + commit = commit_state_change( + task, + action='manual_decide', + reason_code='manual_resume', + owner_run_id=run_id, + expected_version=task.get('_scheduler', {}).get('stateVersion'), + to_state=resume_state, + now_text='▶️ 已恢复执行', + block_text='无', + flow_from='皇上', + flow_remark=f'▶️ 恢复:{reason or "无"}', + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'{task_id} 恢复失败: {commit.get("blockedBy")}'} + _scheduler_mark_progress(task, f'恢复到 {task.get("state", "Doing")}', reason_code='manual_resume') + _set_cooldown(task, 'noReassignUntil', _COOLDOWN_SECONDS['post_human_decision_reassign']) save_tasks(tasks) if action == 'resume' and task.get('state') not in _TERMINAL_STATES: - dispatch_for_state(task_id, task, task.get('state'), trigger='resume') + dispatch_for_state(task_id, task, task.get('state'), trigger='resume', owner_run_id=run_id) label = {'stop': '已叫停', 'cancel': '已取消', 'resume': '已恢复'}[action] return {'ok': True, 'message': f'{task_id} {label}'} @@ -542,8 +570,6 @@ def handle_create_task(title, org='中书省', official='中书令', priority='n title = re.split(r'\n*```', title, maxsplit=1)[0].strip() # 清理常见前缀: "传旨:" "下旨:" 等 title = re.sub(r'^(传旨|下旨)[::\uff1a]\s*', '', title) - if len(title) > 100: - title = title[:100] + '…' # 标题质量校验:防止闲聊被误建为旨意 if len(title) < _MIN_TITLE_LEN: return {'ok': False, 'error': f'标题过短({len(title)}<{_MIN_TITLE_LEN}字),不像是旨意'} @@ -610,42 +636,57 @@ def handle_review_action(task_id, action, comment=''): _ensure_scheduler(task) _scheduler_snapshot(task, f'review-before-{action}') + run_id = _new_run_id() + _acquire_lease(task, stage=task.get('state', ''), role='manual-review', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + version = task.get('_scheduler', {}).get('stateVersion') if action == 'approve': if task['state'] == 'Menxia': - task['state'] = 'Assigned' - task['now'] = '门下省准奏,移交尚书省派发' + next_state = 'Assigned' + next_now = '门下省准奏,移交尚书省派发' remark = f'✅ 准奏:{comment or "门下省审议通过"}' to_dept = '尚书省' else: # Review - task['state'] = 'Done' - task['now'] = '御批通过,任务完成' + next_state = 'Done' + next_now = '御批通过,任务完成' remark = f'✅ 御批准奏:{comment or "审查通过"}' to_dept = '皇上' elif action == 'reject': round_num = (task.get('review_round') or 0) + 1 task['review_round'] = round_num - task['state'] = 'Zhongshu' - task['now'] = f'封驳退回中书省修订(第{round_num}轮)' + next_state = 'Zhongshu' + next_now = f'封驳退回中书省修订(第{round_num}轮)' remark = f'🚫 封驳:{comment or "需要修改"}' to_dept = '中书省' else: return {'ok': False, 'error': f'未知操作: {action}'} - task.setdefault('flow_log', []).append({ - 'at': now_iso(), - 'from': '门下省' if task.get('state') != 'Done' else '皇上', - 'to': to_dept, - 'remark': remark - }) - _scheduler_mark_progress(task, f'审议动作 {action} -> {task.get("state")}') - task['updatedAt'] = now_iso() + commit = commit_state_change( + task, + action='manual_decide', + reason_code='manual_review_decision', + owner_run_id=run_id, + expected_version=version, + to_state=next_state, + to_org=_derive_org_for_state(task, next_state, task.get('org', '')), + now_text=next_now, + block_text='无', + flow_from='门下省' if next_state != 'Done' else '皇上', + flow_to=to_dept, + flow_remark=remark, + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'{task_id} 审批提交失败: {commit.get("blockedBy")}'} + _scheduler_mark_progress(task, f'审议动作 {action} -> {task.get("state")}', reason_code='manual_review_decision') + _set_cooldown(task, 'noReassignUntil', _COOLDOWN_SECONDS['post_human_decision_reassign']) save_tasks(tasks) # 🚀 审批后自动派发对应 Agent new_state = task['state'] if new_state not in ('Done',): - dispatch_for_state(task_id, task, new_state) + dispatch_for_state(task_id, task, new_state, owner_run_id=run_id) label = '已准奏' if action == 'approve' else '已封驳' dispatched = ' (已自动派发 Agent)' if new_state != 'Done' else '' @@ -856,6 +897,332 @@ def do_wake(): return {'ok': True, 'message': f'{agent_id} 唤醒指令已发出,约10-30秒后生效'} +def _agent_label(agent_id): + for dept in _AGENT_DEPTS: + if dept.get('id') == agent_id: + return dept.get('label') or agent_id + return agent_id + + +def _extract_json_obj(text): + if not text: + return None + s = text.strip() + if s.startswith('```'): + s = re.sub(r'^```[a-zA-Z]*\s*', '', s) + s = re.sub(r'\s*```$', '', s) + start = s.find('{') + end = s.rfind('}') + if start < 0 or end <= start: + return None + candidate = s[start:end + 1] + try: + obj = json.loads(candidate) + return obj if isinstance(obj, dict) else None + except Exception: + return None + + +def _run_agent_sync(agent_id, message, timeout_sec=120): + if not _SAFE_NAME_RE.match(agent_id): + raise ValueError(f'agent_id 非法: {agent_id}') + cmd = ['openclaw', 'agent', '--agent', agent_id, '-m', message, '--timeout', str(timeout_sec)] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout_sec + 15) + output = (result.stdout or '').strip() + err = (result.stderr or '').strip() + if result.returncode != 0: + raise RuntimeError(err[:400] or output[:400] or f'agent {agent_id} failed') + text = output or err + return text[:12000] + + +def _load_court_session(session_id): + items = atomic_json_read(DATA / 'court_discussions.json', []) + if not isinstance(items, list): + return None + for item in items: + if isinstance(item, dict) and item.get('id') == session_id: + return item + return None + + +def _upsert_court_session(session): + target = DATA / 'court_discussions.json' + sid = session.get('id') + if not sid: + return + + def updater(items): + if not isinstance(items, list): + items = [] + out = [] + replaced = False + for item in items: + if isinstance(item, dict) and item.get('id') == sid: + out.append(session) + replaced = True + else: + out.append(item) + if not replaced: + out.insert(0, session) + out.sort(key=lambda x: x.get('updatedAt', '') if isinstance(x, dict) else '', reverse=True) + return out[:120] + + atomic_json_update(target, updater, []) + + +def _pick_moderator(selected): + if 'menxia' in selected: + return 'menxia' + if 'taizi' in selected: + return 'taizi' + return selected[0] + + +def _build_court_response(session, message=''): + assessment = None + assessments = session.get('assessments') or [] + if assessments: + assessment = assessments[-1] + return { + 'ok': True, + 'sessionId': session.get('id'), + 'status': session.get('status', 'ongoing'), + 'topic': session.get('topic', ''), + 'participants': session.get('participants', []), + 'rounds': int(session.get('rounds') or 0), + 'moderator': { + 'id': session.get('moderatorId', ''), + 'label': _agent_label(session.get('moderatorId', '')), + }, + 'assessment': assessment, + 'suggestedAction': session.get('suggestedAction', 'next'), + 'discussion': (session.get('discussion') or [])[-80:], + 'final': session.get('final'), + 'message': message or session.get('message', ''), + } + + +def _run_court_round(session): + topic = session.get('topic', '') + selected = session.get('participants', []) + round_no = int(session.get('rounds') or 0) + 1 + transcript = session.setdefault('discussion', []) + round_entries = [] + + for aid in selected: + label = _agent_label(aid) + recent = transcript[-6:] + recent_text = '\n\n'.join([ + f'[{x.get("agentLabel", "")}] {(x.get("reply", "") or "")[:400]}' + for x in recent + ]) if recent else '暂无' + prompt = ( + f'你正在参与御前议政讨论,角色是「{label}」。\n' + f'议题:{topic}\n' + f'当前第 {round_no} 轮。\n\n' + f'最近讨论摘要:\n{recent_text}\n\n' + f'请输出四段(中文、简洁):\n' + f'【你认为最关键的澄清点】\n' + f'【你看到的主要风险】\n' + f'【你建议皇上现在做的决定】\n' + f'【可直接执行的修改建议】' + ) + reply = _run_agent_sync(aid, prompt, timeout_sec=120) + round_entries.append({ + 'round': round_no, + 'agentId': aid, + 'agentLabel': label, + 'reply': reply[:4000], + 'at': now_iso(), + }) + + transcript.extend(round_entries) + session['rounds'] = round_no + + moderator_id = session.get('moderatorId') or _pick_moderator(selected) + moderator_label = _agent_label(moderator_id) + recent_round_text = '\n\n'.join([ + f'[{x["agentLabel"]}] {x["reply"][:1200]}' + for x in round_entries + ]) + assess_prompt = ( + f'你现在是本轮议政主持人({moderator_label})。\n' + f'议题:{topic}\n' + f'第 {round_no} 轮各方意见如下:\n{recent_round_text}\n\n' + f'请只输出 JSON(不要代码块):\n' + f'{{\n' + f' "recommend_stop": true,\n' + f' "reason": "为何建议结束/继续",\n' + f' "question_to_emperor": "请皇上拍板的问题",\n' + f' "focus_next_round": ["若继续,下一轮重点1", "重点2"],\n' + f' "draft_direction": "若现在结束,旨意草案应强调什么"\n' + f'}}' + ) + assess_raw = _run_agent_sync(moderator_id, assess_prompt, timeout_sec=120) + assess = _extract_json_obj(assess_raw) or {} + assessment = { + 'round': round_no, + 'moderatorId': moderator_id, + 'moderatorLabel': moderator_label, + 'recommend_stop': bool(assess.get('recommend_stop', False)), + 'reason': str(assess.get('reason') or '').strip(), + 'question_to_emperor': str(assess.get('question_to_emperor') or '').strip(), + 'focus_next_round': assess.get('focus_next_round') if isinstance(assess.get('focus_next_round'), list) else [], + 'draft_direction': str(assess.get('draft_direction') or '').strip(), + 'raw': assess_raw[:2000], + 'at': now_iso(), + } + session.setdefault('assessments', []).append(assessment) + session['suggestedAction'] = 'finalize' if assessment['recommend_stop'] else 'next' + session['status'] = 'ongoing' + session['updatedAt'] = now_iso() + session['message'] = ( + f'第 {round_no} 轮结束,{moderator_label}建议' + f'{"可请皇上决定结束讨论" if assessment["recommend_stop"] else "继续讨论一轮"}' + ) + return round_entries, assessment + + +def _finalize_court_session(session, force=False): + topic = session.get('topic', '') + transcript = session.get('discussion') or [] + if not transcript: + return {'ok': False, 'error': '暂无讨论内容,无法生成结论'} + + moderator_id = session.get('moderatorId') or _pick_moderator(session.get('participants') or ['taizi']) + discuss_pack = '\n\n'.join([ + f'[{x.get("agentLabel", "")}] {(x.get("reply", "") or "")[:1200]}' + for x in transcript[-24:] + ]) + assess_pack = '\n'.join([ + f'第{a.get("round")}轮建议: {a.get("reason", "")}' + for a in (session.get('assessments') or [])[-6:] + ]) + synth_prompt = ( + f'请作为太子秘书处,基于御前讨论输出最终可执行结论。\n' + f'议题:{topic}\n' + f'主持审议摘要:\n{assess_pack or "暂无"}\n\n' + f'讨论记录:\n{discuss_pack}\n\n' + f'请只输出 JSON(不要代码块):\n' + f'{{\n' + f' "ready_for_edict": true,\n' + f' "clarified_goal": "一句话目标",\n' + f' "risks": ["风险1","风险2"],\n' + f' "questions_to_emperor": ["若仍有未定项,在此列出"],\n' + f' "recommended_edict": "可直接下旨的完整文本",\n' + f' "recommended_target_dept": "中书省",\n' + f' "recommended_priority": "normal"\n' + f'}}' + ) + synth_raw = _run_agent_sync(moderator_id, synth_prompt, timeout_sec=120) + synth = _extract_json_obj(synth_raw) or {} + final = { + 'ready_for_edict': bool(synth.get('ready_for_edict', False)), + 'clarified_goal': str(synth.get('clarified_goal') or '').strip(), + 'risks': synth.get('risks') if isinstance(synth.get('risks'), list) else [], + 'questions_to_emperor': ( + synth.get('questions_to_emperor') if isinstance(synth.get('questions_to_emperor'), list) else [] + ), + 'recommended_edict': str(synth.get('recommended_edict') or topic).strip(), + 'recommended_target_dept': str(synth.get('recommended_target_dept') or '中书省').strip(), + 'recommended_priority': str(synth.get('recommended_priority') or 'normal').strip(), + 'forceFinalized': bool(force), + 'raw': synth_raw[:4000], + } + if final['recommended_priority'] not in ('low', 'normal', 'high', 'critical'): + final['recommended_priority'] = 'normal' + if final['recommended_target_dept'] not in ('中书省', '尚书省', '礼部', '户部', '兵部', '刑部', '工部', '吏部'): + final['recommended_target_dept'] = '中书省' + + session['final'] = final + session['status'] = 'done' + session['suggestedAction'] = 'finalize' + session['updatedAt'] = now_iso() + session['finalizedAt'] = now_iso() + session['message'] = '议政讨论已结束,可直接下旨' + return {'ok': True, 'final': final} + + +def handle_court_discuss(action='start', topic='', participants=None, session_id='', force=False): + if not _check_gateway_alive(): + return {'ok': False, 'error': 'Gateway 未启动,请先运行 openclaw gateway start'} + + action = (action or 'start').strip().lower() + if action == 'start': + topic = (topic or '').strip() + if len(topic) < 10: + return {'ok': False, 'error': '议题至少 10 个字'} + + allowed = {dept.get('id') for dept in _AGENT_DEPTS} + selected = [] + for aid in (participants or []): + if isinstance(aid, str): + x = aid.strip() + if x and x in allowed and x not in selected: + selected.append(x) + if not selected: + selected = ['taizi', 'zhongshu', 'menxia'] + if len(selected) > 6: + selected = selected[:6] + if len(selected) < 2: + return {'ok': False, 'error': '至少选择 2 位大臣参与讨论'} + for aid in selected: + if not _check_agent_workspace(aid): + return {'ok': False, 'error': f'Agent {aid} 工作空间不存在,请先配置'} + + session = { + 'id': f"CD-{datetime.datetime.now().strftime('%Y%m%d-%H%M%S')}", + 'topic': topic, + 'participants': selected, + 'moderatorId': _pick_moderator(selected), + 'status': 'ongoing', + 'rounds': 0, + 'discussion': [], + 'assessments': [], + 'final': None, + 'createdAt': now_iso(), + 'updatedAt': now_iso(), + 'message': '议政会话已创建', + } + try: + _run_court_round(session) + except Exception as e: + return {'ok': False, 'error': f'首轮讨论失败: {str(e)[:240]}'} + _upsert_court_session(session) + return _build_court_response(session, session.get('message', '')) + + if not session_id: + return {'ok': False, 'error': 'sessionId required'} + session = _load_court_session(session_id) + if not session: + return {'ok': False, 'error': f'讨论会话 {session_id} 不存在'} + + if action == 'status': + return _build_court_response(session, '会话状态已返回') + + if action == 'next': + if session.get('status') == 'done': + return _build_court_response(session, '会话已结束,无需继续') + try: + _run_court_round(session) + except Exception as e: + return {'ok': False, 'error': f'继续讨论失败: {str(e)[:240]}'} + _upsert_court_session(session) + return _build_court_response(session, session.get('message', '')) + + if action == 'finalize': + if session.get('status') == 'done': + return _build_court_response(session, '会话已结束') + finalized = _finalize_court_session(session, force=bool(force)) + if not finalized.get('ok'): + return finalized + _upsert_court_session(session) + return _build_court_response(session, session.get('message', '')) + + return {'ok': False, 'error': f'unsupported action: {action}'} + + # ══ Agent 实时活动读取 ══ # 状态 → agent_id 映射 @@ -874,8 +1241,48 @@ def do_wake(): '刑部': 'xingbu', '工部': 'gongbu', '吏部': 'libu_hr', '中书省': 'zhongshu', '门下省': 'menxia', '尚书省': 'shangshu', } +_EXECUTION_DEPTS = {'礼部', '户部', '兵部', '刑部', '工部', '吏部'} _TERMINAL_STATES = {'Done', 'Cancelled'} +_DIAG_MAX_LOG = 300 +_FLOW_DEDUPE_WINDOW_SEC = 60 + +_CONTROL_STATE_BY_STATE = { + 'Pending': 'Pending', + 'Taizi': 'Taizi', + 'Zhongshu': 'Zhongshu', + 'Menxia': 'WaitingDecision', + 'Assigned': 'Assigned', + 'Next': 'Assigned', + 'Doing': 'Doing', + 'Review': 'WaitingDecision', + 'Done': 'Completed', + 'Cancelled': 'Cancelled', + 'Blocked': 'Blocked', +} + +_CONTROL_ACTION_ALLOWLIST = { + 'Pending': {'dispatch', 'advance', 'noop'}, + 'Taizi': {'dispatch', 'advance', 'retry', 'noop'}, + 'Zhongshu': {'dispatch', 'advance', 'retry', 'escalate', 'noop'}, + 'Assigned': {'dispatch', 'retry', 'escalate', 'advance', 'rollback', 'wait_human', 'noop'}, + 'Doing': {'retry', 'writeback_retry', 'advance', 'wait_human', 'noop'}, + 'ExecutionOutputReady': {'writeback_retry', 'wait_human', 'noop'}, + 'WritebackPending': {'writeback_retry', 'wait_human', 'noop'}, + 'RetryableFailure': {'retry', 'wait_human', 'rollback', 'noop'}, + 'WaitingDecision': {'wait_human', 'manual_decide', 'noop'}, + 'EscalationCandidate': {'escalate', 'wait_human', 'noop'}, + 'Blocked': {'manual_decide', 'noop'}, + 'Completed': {'manual_decide', 'noop'}, + 'Cancelled': {'manual_decide', 'noop'}, +} + +_COOLDOWN_SECONDS = { + 'post_dispatch_escalate': 90, + 'post_dispatch_dispatch': 90, + 'post_doing_retry': 120, + 'post_human_decision_reassign': 180, +} def _parse_iso(ts): @@ -887,6 +1294,178 @@ def _parse_iso(ts): return None +def _new_run_id(): + return f'run-{uuid.uuid4().hex[:10]}' + + +def _lease_expired(lease, now_dt=None): + if not isinstance(lease, dict): + return True + ttl_sec = int(lease.get('ttlSec') or 0) + hb = _parse_iso(lease.get('heartbeatAt') or lease.get('acquiredAt')) + if ttl_sec <= 0 or not hb: + return True + now_dt = now_dt or datetime.datetime.now(datetime.timezone.utc) + return (now_dt - hb).total_seconds() > ttl_sec + + +def _sync_control_state(task): + sched = task.setdefault('_scheduler', {}) + if not isinstance(sched, dict): + sched = {} + task['_scheduler'] = sched + state = task.get('state', '') + writeback = sched.get('writeback') or {} + wb_status = writeback.get('status', '') + if wb_status == 'WritebackPending': + control_state = 'WritebackPending' + elif wb_status == 'ExecutionOutputReady': + control_state = 'ExecutionOutputReady' + else: + control_state = _CONTROL_STATE_BY_STATE.get(state, 'Assigned') + sched['controlState'] = control_state + return control_state + + +def _append_diagnostic( + task, + event_type, + reason_code, + details='', + action='', + dedupe_key='', + suppress_window_sec=60, +): + diag = task.setdefault('diagnostic_log', []) + if dedupe_key: + now_dt = datetime.datetime.now(datetime.timezone.utc) + for item in reversed(diag[-30:]): + if item.get('dedupeKey') != dedupe_key: + continue + prev_dt = _parse_iso(item.get('at')) + if prev_dt and (now_dt - prev_dt).total_seconds() <= suppress_window_sec: + return False + break + diag.append({ + 'at': now_iso(), + 'eventType': event_type, + 'action': action, + 'reasonCode': reason_code, + 'details': details, + 'dedupeKey': dedupe_key, + }) + if len(diag) > _DIAG_MAX_LOG: + task['diagnostic_log'] = diag[-_DIAG_MAX_LOG:] + return True + + +def _set_cooldown(task, key, seconds): + if seconds <= 0: + return + sched = _ensure_scheduler(task) + cds = sched.setdefault('cooldowns', {}) + until_dt = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(seconds=int(seconds)) + cds[key] = until_dt.isoformat().replace('+00:00', 'Z') + + +def _cooldown_remaining(task, key): + sched = _ensure_scheduler(task) + cds = sched.get('cooldowns') or {} + until_dt = _parse_iso(cds.get(key)) + if not until_dt: + return 0 + left = (until_dt - datetime.datetime.now(datetime.timezone.utc)).total_seconds() + return max(0, int(left)) + + +def _cooldown_block_for_action(task, action): + blocks = [] + if action in ('dispatch', 'retry'): + left = _cooldown_remaining(task, 'noDispatchUntil') + if left > 0: + blocks.append(('dispatchCooldown', left)) + if action == 'retry' and task.get('state') == 'Doing': + left = _cooldown_remaining(task, 'noRetryUntil') + if left > 0: + blocks.append(('retryCooldown', left)) + if action == 'escalate': + left = _cooldown_remaining(task, 'noEscalateUntil') + if left > 0: + blocks.append(('escalateCooldown', left)) + left2 = _cooldown_remaining(task, 'noReassignUntil') + if left2 > 0: + blocks.append(('reassignCooldown', left2)) + return blocks + + +def _action_allowed(task, action): + sched = _ensure_scheduler(task) + control_state = _sync_control_state(task) + allowed = _CONTROL_ACTION_ALLOWLIST.get(control_state, {'noop'}) + if action not in allowed: + return { + 'ok': False, + 'blockedBy': 'stateGuard', + 'controlState': control_state, + 'allowedActions': sorted(allowed), + } + cd_blocks = _cooldown_block_for_action(task, action) + if cd_blocks: + code, left = cd_blocks[0] + return { + 'ok': False, + 'blockedBy': code, + 'cooldownSec': left, + 'controlState': control_state, + 'allowedActions': sorted(allowed), + } + return {'ok': True, 'controlState': control_state, 'allowedActions': sorted(allowed)} + + +def _acquire_lease(task, stage, role, owner_run_id, ttl_sec=180, force_takeover=False): + sched = _ensure_scheduler(task) + lease = sched.setdefault('lease', {}) + now_ts = now_iso() + current_owner = lease.get('ownerRunId', '') + expired = _lease_expired(lease) + if force_takeover or not current_owner or expired: + lease.update({ + 'stage': stage, + 'role': role, + 'ownerRunId': owner_run_id, + 'acquiredAt': now_ts, + 'heartbeatAt': now_ts, + 'ttlSec': int(ttl_sec), + }) + return {'ok': True, 'takenOver': bool(current_owner and current_owner != owner_run_id)} + if current_owner == owner_run_id: + lease['heartbeatAt'] = now_ts + lease['ttlSec'] = int(ttl_sec) + return {'ok': True, 'takenOver': False} + return {'ok': False, 'blockedBy': 'leaseBusy', 'ownerRunId': current_owner} + + +def _renew_lease(task, owner_run_id, ttl_sec=None): + sched = _ensure_scheduler(task) + lease = sched.setdefault('lease', {}) + if lease.get('ownerRunId') != owner_run_id: + return False + lease['heartbeatAt'] = now_iso() + if ttl_sec: + lease['ttlSec'] = int(ttl_sec) + return True + + +def _release_lease(task, owner_run_id): + sched = _ensure_scheduler(task) + lease = sched.get('lease') or {} + if lease.get('ownerRunId') != owner_run_id: + return False + lease['ownerRunId'] = '' + lease['releasedAt'] = now_iso() + return True + + def _ensure_scheduler(task): sched = task.setdefault('_scheduler', {}) if not isinstance(sched, dict): @@ -897,13 +1476,48 @@ def _ensure_scheduler(task): sched.setdefault('maxRetry', 1) sched.setdefault('retryCount', 0) sched.setdefault('escalationLevel', 0) + sched.setdefault('maxStateAgeSec', 900) sched.setdefault('autoRollback', True) + sched.setdefault('autoAdvance', True) if not sched.get('lastProgressAt'): sched['lastProgressAt'] = task.get('updatedAt') or now_iso() + cur_state = task.get('state', '') + if not sched.get('stateSince'): + sched['stateSince'] = task.get('updatedAt') or now_iso() + if sched.get('stateName') != cur_state: + sched['stateName'] = cur_state + sched['stateSince'] = task.get('updatedAt') or now_iso() if 'stallSince' not in sched: sched['stallSince'] = None + if 'awaitingEmperorDecision' not in sched: + sched['awaitingEmperorDecision'] = False if 'lastDispatchStatus' not in sched: sched['lastDispatchStatus'] = 'idle' + sched.setdefault('stateVersion', 0) + sched.setdefault('lastCommit', {}) + sched.setdefault('lastAction', {}) + sched.setdefault('cooldowns', {}) + lease = sched.setdefault('lease', {}) + if not isinstance(lease, dict): + lease = {} + sched['lease'] = lease + lease.setdefault('stage', '') + lease.setdefault('role', '') + lease.setdefault('ownerRunId', '') + lease.setdefault('acquiredAt', '') + lease.setdefault('heartbeatAt', '') + lease.setdefault('ttlSec', 180) + writeback = sched.setdefault('writeback', {}) + if not isinstance(writeback, dict): + writeback = {} + sched['writeback'] = writeback + writeback.setdefault('status', 'idle') + writeback.setdefault('retryCount', 0) + writeback.setdefault('maxRetry', 2) + writeback.setdefault('firstOutputAt', '') + writeback.setdefault('lastCommittedAt', '') + writeback.setdefault('lastError', '') + writeback.setdefault('lastDispatchOutput', '') if 'snapshot' not in sched: sched['snapshot'] = { 'state': task.get('state', ''), @@ -912,16 +1526,34 @@ def _ensure_scheduler(task): 'savedAt': now_iso(), 'note': 'init', } + _sync_control_state(task) return sched -def _scheduler_add_flow(task, remark, to=''): - task.setdefault('flow_log', []).append({ +def _scheduler_add_flow(task, remark, to='', reason_code=''): + flow_log = task.setdefault('flow_log', []) + now_dt = datetime.datetime.now(datetime.timezone.utc) + entry = { 'at': now_iso(), 'from': '太子调度', 'to': to or task.get('org', ''), 'remark': f'🧭 {remark}' - }) + } + if reason_code: + entry['reasonCode'] = reason_code + + if flow_log: + last = flow_log[-1] + last_dt = _parse_iso(last.get('at')) + if ( + last.get('from') == entry['from'] + and last.get('to') == entry['to'] + and last.get('remark') == entry['remark'] + and ((now_dt - last_dt).total_seconds() <= _FLOW_DEDUPE_WINDOW_SEC if last_dt else False) + ): + return False + flow_log.append(entry) + return True def _scheduler_snapshot(task, note=''): @@ -935,15 +1567,231 @@ def _scheduler_snapshot(task, note=''): } -def _scheduler_mark_progress(task, note=''): +def _scheduler_mark_progress(task, note='', reason_code='progress_update'): sched = _ensure_scheduler(task) sched['lastProgressAt'] = now_iso() sched['stallSince'] = None + sched['awaitingEmperorDecision'] = False + sched['decisionPacket'] = None sched['retryCount'] = 0 sched['escalationLevel'] = 0 sched['lastEscalatedAt'] = None if note: - _scheduler_add_flow(task, f'进展确认:{note}') + _scheduler_add_flow(task, f'进展确认:{note}', reason_code=reason_code) + + +def _scheduler_mark_state_change(task, new_state, reason_code='state_change'): + sched = _ensure_scheduler(task) + sched['stateName'] = new_state + sched['stateSince'] = now_iso() + sched['awaitingEmperorDecision'] = False + sched['decisionPacket'] = None + if new_state == 'Doing': + _set_cooldown(task, 'noRetryUntil', _COOLDOWN_SECONDS['post_doing_retry']) + _sync_control_state(task) + sched['lastAction'] = { + 'action': 'state_change', + 'reasonCode': reason_code, + 'at': now_iso(), + } + + +def commit_state_change( + task, + action, + reason_code, + owner_run_id='', + expected_version=None, + to_state=None, + to_org=None, + now_text=None, + block_text=None, + flow_remark='', + flow_from='太子调度', + flow_to='', + force=False, +): + sched = _ensure_scheduler(task) + current_state = task.get('state', '') + current_version = int(sched.get('stateVersion') or 0) + + if expected_version is not None and int(expected_version) != current_version: + _append_diagnostic( + task, + event_type='state_commit_blocked', + action=action, + reason_code='version_conflict', + details=f'expected={expected_version}, current={current_version}', + dedupe_key=f'{task.get("id")}:version:{action}:{expected_version}:{current_version}', + ) + sched['lastCommit'] = { + 'at': now_iso(), + 'action': action, + 'reasonCode': reason_code, + 'result': 'blocked', + 'blockedBy': 'versionConflict', + 'currentVersion': current_version, + 'expectedVersion': expected_version, + } + return {'ok': False, 'committed': False, 'blockedBy': 'versionConflict', 'currentVersion': current_version} + + if owner_run_id and not force: + lease = sched.get('lease') or {} + lease_owner = lease.get('ownerRunId') or '' + if not lease_owner or lease_owner != owner_run_id or _lease_expired(lease): + _append_diagnostic( + task, + event_type='state_commit_blocked', + action=action, + reason_code='stale_owner', + details=f'owner={owner_run_id}, leaseOwner={lease_owner}', + dedupe_key=f'{task.get("id")}:owner:{action}:{owner_run_id}:{lease_owner}', + ) + sched['lastCommit'] = { + 'at': now_iso(), + 'action': action, + 'reasonCode': reason_code, + 'result': 'blocked', + 'blockedBy': 'staleOwner', + 'ownerRunId': owner_run_id, + 'leaseOwner': lease_owner, + } + return {'ok': False, 'committed': False, 'blockedBy': 'staleOwner', 'leaseOwner': lease_owner} + _renew_lease(task, owner_run_id) + + if not force: + allowed = _action_allowed(task, action) + if not allowed.get('ok'): + _append_diagnostic( + task, + event_type='state_commit_blocked', + action=action, + reason_code='action_blocked', + details=str(allowed), + dedupe_key=f'{task.get("id")}:blocked:{action}:{allowed.get("blockedBy")}', + ) + sched['lastCommit'] = { + 'at': now_iso(), + 'action': action, + 'reasonCode': reason_code, + 'result': 'blocked', + 'blockedBy': allowed.get('blockedBy'), + 'controlState': allowed.get('controlState'), + } + return {'ok': False, 'committed': False, 'blockedBy': allowed.get('blockedBy')} + + state_changed = False + prev_state = current_state + if to_state is not None and to_state != current_state: + task['state'] = to_state + state_changed = True + if to_org is not None: + task['org'] = to_org + else: + task['org'] = _derive_org_for_state(task, to_state, task.get('org', '')) + _scheduler_mark_state_change(task, to_state, reason_code=reason_code) + elif to_org is not None: + task['org'] = to_org + + if now_text is not None: + task['now'] = now_text + if block_text is not None: + task['block'] = block_text + + if flow_remark: + task.setdefault('flow_log', []).append({ + 'at': now_iso(), + 'from': flow_from, + 'to': flow_to or task.get('org', ''), + 'remark': flow_remark, + 'reasonCode': reason_code, + }) + + sched['stateVersion'] = current_version + 1 + sched['lastAction'] = {'action': action, 'reasonCode': reason_code, 'at': now_iso()} + sched['lastCommit'] = { + 'at': now_iso(), + 'action': action, + 'reasonCode': reason_code, + 'result': 'committed', + 'ownerRunId': owner_run_id, + 'fromState': prev_state, + 'toState': task.get('state', ''), + 'stateChanged': state_changed, + 'version': sched['stateVersion'], + } + _sync_control_state(task) + task['updatedAt'] = now_iso() + return {'ok': True, 'committed': True, 'stateChanged': state_changed, 'stateVersion': sched['stateVersion']} + + +def _build_decision_packet(task, state, stalled_sec=0, state_age_sec=0): + title = (task.get('title') or '').strip() + now_text = (task.get('now') or '').strip() + progress_log = task.get('progress_log') or [] + flow_log = task.get('flow_log') or [] + latest_progress = (progress_log[-1].get('text') if progress_log else '') or now_text + latest_flow = (flow_log[-1].get('remark') if flow_log else '') + + if state == 'Menxia': + question = '请拍板:是否准奏并移交尚书省执行?' + options = [ + { + 'id': 'approve', + 'label': '准奏推进', + 'impact': '状态变更为 Assigned,尚书省开始派发六部执行(推荐)', + }, + { + 'id': 'reject', + 'label': '封驳退回', + 'impact': '状态回到 Zhongshu,要求中书省补充/修改方案后再审', + }, + ] + recommended = 'approve' + elif state == 'Review': + question = '请拍板:是否验收通过并结案?' + options = [ + { + 'id': 'approve', + 'label': '验收通过', + 'impact': '状态变更为 Done,任务归档结束(推荐)', + }, + { + 'id': 'reject', + 'label': '退回整改', + 'impact': '状态回到 Zhongshu,按封驳意见继续修订', + }, + ] + recommended = 'approve' + else: + question = '请拍板:是否继续当前推进策略?' + options = [ + {'id': 'approve', 'label': '继续推进', 'impact': '按当前流程继续自动调度(推荐)'}, + {'id': 'reject', 'label': '人工干预', 'impact': '改为人工指定下一步或回滚'}, + ] + recommended = 'approve' + + evidence = [ + f'任务: {task.get("id", "")}', + f'状态: {_STATE_LABELS.get(state, state)}', + f'停滞: {int(stalled_sec)}秒', + f'驻留: {int(state_age_sec)}秒', + ] + if title: + evidence.append(f'旨意: {title[:120]}') + if latest_progress: + evidence.append(f'最近进展: {latest_progress[:200]}') + if latest_flow: + evidence.append(f'最近流转: {latest_flow[:200]}') + + return { + 'state': state, + 'question': question, + 'options': options, + 'recommended': recommended, + 'evidence': evidence, + 'generatedAt': now_iso(), + } def _update_task_scheduler(task_id, updater): @@ -953,11 +1801,82 @@ def _update_task_scheduler(task_id, updater): return False sched = _ensure_scheduler(task) updater(task, sched) + _sync_control_state(task) task['updatedAt'] = now_iso() save_tasks(tasks) return True +def _retry_writeback_for_task(task_id, owner_run_id=''): + tasks = load_tasks() + task = next((t for t in tasks if t.get('id') == task_id), None) + if not task: + return {'ok': False, 'error': f'任务 {task_id} 不存在'} + + sched = _ensure_scheduler(task) + wb = sched.setdefault('writeback', {}) + lease = sched.get('lease') or {} + lease_owner = lease.get('ownerRunId') or '' + if owner_run_id and lease_owner and owner_run_id != lease_owner: + _append_diagnostic( + task, + event_type='writeback_retry_blocked', + action='writeback_retry', + reason_code='stale_owner', + details=f'owner={owner_run_id}, leaseOwner={lease_owner}', + dedupe_key=f'{task_id}:writeback_retry:stale_owner', + ) + save_tasks(tasks) + return {'ok': False, 'blockedBy': 'staleOwner'} + + output = wb.get('lastDispatchOutput', '') + if not output: + wb['status'] = 'ExecutionOutputReady' + wb['lastError'] = 'missing_dispatch_output' + _append_diagnostic( + task, + event_type='writeback_retry_blocked', + action='writeback_retry', + reason_code='missing_dispatch_output', + details=f'task={task_id}', + dedupe_key=f'{task_id}:writeback_retry:missing_output', + ) + save_tasks(tasks) + return {'ok': False, 'blockedBy': 'missingDispatchOutput'} + + bridge = _bridge_apply_kanban_commands(task_id, output) + if bridge.get('attempted', 0) > 0 and bridge.get('applied', 0) >= bridge.get('attempted', 0): + wb['status'] = 'idle' + wb['retryCount'] = 0 + wb['lastCommittedAt'] = now_iso() + wb['lastError'] = '' + _scheduler_add_flow(task, '写回重试成功,提交已落板', reason_code='writeback_retry_success') + _release_lease(task, owner_run_id or lease_owner) + save_tasks(tasks) + return {'ok': True, 'committed': True, 'attempted': bridge.get('attempted', 0), 'applied': bridge.get('applied', 0)} + + wb['status'] = 'WritebackPending' + wb['retryCount'] = int(wb.get('retryCount') or 0) + 1 + wb['lastError'] = '; '.join((bridge.get('errors') or [])[:2]) or 'writeback_retry_failed' + _append_diagnostic( + task, + event_type='writeback_retry_failed', + action='writeback_retry', + reason_code='writeback_retry_failed', + details=wb['lastError'], + dedupe_key=f'{task_id}:writeback_retry:failed', + ) + _scheduler_add_flow(task, '写回重试失败,等待下一次提交重试', reason_code='writeback_retry_failed') + save_tasks(tasks) + return { + 'ok': False, + 'committed': False, + 'attempted': bridge.get('attempted', 0), + 'applied': bridge.get('applied', 0), + 'error': wb.get('lastError', ''), + } + + def get_scheduler_state(task_id): tasks = load_tasks() task = next((t for t in tasks if t.get('id') == task_id), None) @@ -969,17 +1888,271 @@ def get_scheduler_state(task_id): stalled_sec = 0 if last_progress: stalled_sec = max(0, int((now_dt - last_progress).total_seconds())) + state_since = _parse_iso(sched.get('stateSince') or task.get('updatedAt')) + state_age_sec = 0 + if state_since: + state_age_sec = max(0, int((now_dt - state_since).total_seconds())) + state_age_limit = int(sched.get('maxStateAgeSec') or 0) return { 'ok': True, 'taskId': task_id, 'state': task.get('state', ''), 'org': task.get('org', ''), 'scheduler': sched, + 'controlState': sched.get('controlState'), + 'lease': sched.get('lease'), + 'lastAction': sched.get('lastAction'), + 'writeback': sched.get('writeback'), + 'decision': sched.get('decisionPacket'), 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + 'stateAgeLimitSec': state_age_limit, + 'checkedAt': now_iso(), + } + + +def get_scheduler_metrics(task_id=''): + tasks = load_tasks() + if task_id: + tasks = [t for t in tasks if t.get('id') == task_id] + if not tasks: + return {'ok': False, 'error': f'任务 {task_id} 不存在'} + + task_metrics = [] + total_dispatch_attempts = 0 + total_unique_steps = 0 + total_invalid_control = 0 + total_control_actions = 0 + writeback_lags = [] + + now_dt = datetime.datetime.now(datetime.timezone.utc) + for task in tasks: + sched = _ensure_scheduler(task) + progress_log = task.get('progress_log') or [] + diagnostic_log = task.get('diagnostic_log') or [] + flow_log = task.get('flow_log') or [] + + dispatch_attempts = int(sched.get('dispatchAttempts') or 0) + unique_execution_steps = len({ + (p.get('agent', ''), p.get('text', ''), p.get('state', '')) + for p in progress_log if p.get('text') + }) + unique_execution_steps = max(unique_execution_steps, 1 if dispatch_attempts > 0 else 0) + amplification_ratio = round(dispatch_attempts / unique_execution_steps, 2) if unique_execution_steps else 0.0 + + control_actions = sum( + 1 for f in flow_log + if isinstance(f, dict) and str(f.get('from', '')).startswith('太子调度') + ) + invalid_control = sum( + 1 for d in diagnostic_log + if d.get('eventType') in ('state_commit_blocked', 'control_blocked') + ) + invalid_ratio = round(invalid_control / control_actions, 3) if control_actions else 0.0 + + wb = sched.get('writeback') or {} + first_output = _parse_iso(wb.get('firstOutputAt')) + committed_at = _parse_iso(wb.get('lastCommittedAt')) + writeback_lag_sec = None + if first_output: + end_dt = committed_at or now_dt + writeback_lag_sec = max(0, int((end_dt - first_output).total_seconds())) + writeback_lags.append(writeback_lag_sec) + + task_metrics.append({ + 'taskId': task.get('id', ''), + 'state': task.get('state', ''), + 'dispatchAttempts': dispatch_attempts, + 'uniqueExecutionSteps': unique_execution_steps, + 'dispatchAmplificationRatio': amplification_ratio, + 'controlActions': control_actions, + 'invalidControlActions': invalid_control, + 'invalidControlRatio': invalid_ratio, + 'writebackLagSec': writeback_lag_sec, + 'writebackStatus': wb.get('status', 'idle'), + }) + + total_dispatch_attempts += dispatch_attempts + total_unique_steps += unique_execution_steps + total_invalid_control += invalid_control + total_control_actions += control_actions + + global_amp = round(total_dispatch_attempts / total_unique_steps, 2) if total_unique_steps else 0.0 + global_invalid = round(total_invalid_control / total_control_actions, 3) if total_control_actions else 0.0 + avg_writeback_lag = ( + round(sum(writeback_lags) / len(writeback_lags), 2) if writeback_lags else None + ) + + return { + 'ok': True, + 'taskId': task_id or '', + 'metrics': task_metrics, + 'summary': { + 'taskCount': len(task_metrics), + 'dispatchAttempts': total_dispatch_attempts, + 'uniqueExecutionSteps': total_unique_steps, + 'dispatchAmplificationRatio': global_amp, + 'controlActions': total_control_actions, + 'invalidControlActions': total_invalid_control, + 'invalidControlRatio': global_invalid, + 'avgWritebackLagSec': avg_writeback_lag, + }, 'checkedAt': now_iso(), } +def handle_scheduler_action(task_id, action, reason='', expected_version=None, owner_run_id='', recovery_target=''): + action = (action or '').strip() + if action == 'retry': + return handle_scheduler_retry(task_id, reason) + if action == 'escalate': + return handle_scheduler_escalate(task_id, reason) + if action == 'rollback': + return handle_scheduler_rollback(task_id, reason) + if action in ('wait_human', 'manual_decide'): + tasks = load_tasks() + task = next((t for t in tasks if t.get('id') == task_id), None) + if not task: + return {'ok': False, 'error': f'任务 {task_id} 不存在'} + _ensure_scheduler(task) + sched = task.get('_scheduler') or {} + run_id = owner_run_id or _new_run_id() + _acquire_lease(task, stage=task.get('state', ''), role='manual', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + target = (recovery_target or '').strip() + reason_code = 'manual_human_decision' + to_state = None + to_org = None + now_text = task.get('now', '') + block_text = task.get('block', '') + trigger_dispatch_state = '' + trigger_writeback_retry = False + + if target == 'continue_execution': + reason_code = 'human_continue_execution' + to_state = 'Doing' + to_org = _derive_org_for_state(task, 'Doing', task.get('org', '')) + now_text = '👑 皇上裁决:继续执行' + block_text = '无' + trigger_dispatch_state = 'Doing' + elif target == 'continue_writeback': + reason_code = 'human_continue_writeback' + now_text = '👑 皇上裁决:继续提交写回' + block_text = '无' + trigger_writeback_retry = True + elif target == 'reassign': + reason_code = 'human_reassign' + to_state = 'Assigned' + to_org = '尚书省' + now_text = '👑 皇上裁决:改派尚书省重新派发' + block_text = '无' + trigger_dispatch_state = 'Assigned' + elif target == 'terminate': + reason_code = 'human_terminate' + to_state = 'Cancelled' + to_org = task.get('org', '') + now_text = f'👑 皇上裁决:终止任务({reason or "人工终止"})' + block_text = reason or '皇上终止' + + commit = commit_state_change( + task, + action='manual_decide', + reason_code=reason_code, + owner_run_id=run_id, + expected_version=expected_version if expected_version is not None else sched.get('stateVersion'), + to_state=to_state, + to_org=to_org, + now_text=now_text, + block_text=block_text, + flow_from='皇上', + flow_remark=f'👑 人工裁决:{reason or "无"}(目标:{target or "仅记录"})', + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'提交失败: {commit.get("blockedBy")}'} + if trigger_writeback_retry: + wb = sched.setdefault('writeback', {}) + wb['status'] = 'WritebackPending' + wb['lastError'] = wb.get('lastError') or 'human_resume_writeback' + _set_cooldown(task, 'noReassignUntil', _COOLDOWN_SECONDS['post_human_decision_reassign']) + save_tasks(tasks) + if trigger_dispatch_state: + dispatch_for_state(task_id, task, trigger_dispatch_state, trigger='human-decision', owner_run_id=run_id) + if trigger_writeback_retry: + _retry_writeback_for_task(task_id, owner_run_id=run_id) + return {'ok': True, 'message': f'{task_id} 已记录人工裁决', 'recoveryTarget': target or 'record_only'} + return {'ok': False, 'error': f'不支持的 action: {action}'} + + +def handle_scheduler_commit(payload): + task_id = (payload.get('taskId') or '').strip() + action = (payload.get('action') or '').strip() + if not task_id or not action: + return {'ok': False, 'error': 'taskId/action required'} + tasks = load_tasks() + task = next((t for t in tasks if t.get('id') == task_id), None) + if not task: + return {'ok': False, 'error': f'任务 {task_id} 不存在'} + _ensure_scheduler(task) + sched = task.get('_scheduler') or {} + owner_run_id = (payload.get('ownerRunId') or '').strip() or _new_run_id() + expected_version = payload.get('expectedVersion') + to_state = payload.get('toState') + to_org = payload.get('toOrg') + reason_code = (payload.get('reasonCode') or '').strip() or f'manual_commit_{action}' + context = payload.get('context') if isinstance(payload.get('context'), dict) else {} + flow_remark = context.get('flowRemark', f'🔒 统一提交:{action}') + flow_from = context.get('flowFrom', '太子调度') + flow_to = context.get('flowTo', '') + force = bool(payload.get('force', False)) + lease = sched.get('lease') or {} + lease_owner = lease.get('ownerRunId') or '' + if force: + _acquire_lease( + task, + stage=task.get('state', ''), + role='manual-commit', + owner_run_id=owner_run_id, + ttl_sec=180, + force_takeover=True, + ) + elif not lease_owner: + _acquire_lease( + task, + stage=task.get('state', ''), + role='manual-commit', + owner_run_id=owner_run_id, + ttl_sec=180, + force_takeover=False, + ) + elif lease_owner == owner_run_id: + _renew_lease(task, owner_run_id, ttl_sec=180) + + commit = commit_state_change( + task, + action=action, + reason_code=reason_code, + owner_run_id=owner_run_id, + expected_version=expected_version if expected_version is not None else sched.get('stateVersion'), + to_state=to_state, + to_org=to_org, + now_text=context.get('nowText'), + block_text=context.get('blockText'), + flow_remark=flow_remark, + flow_from=flow_from, + flow_to=flow_to, + force=force, + ) + save_tasks(tasks) + return { + 'ok': bool(commit.get('ok')), + 'committed': bool(commit.get('committed')), + 'blockedBy': commit.get('blockedBy'), + 'currentVersion': task.get('_scheduler', {}).get('stateVersion'), + 'taskId': task_id, + } + + def handle_scheduler_retry(task_id, reason=''): tasks = load_tasks() task = next((t for t in tasks if t.get('id') == task_id), None) @@ -990,14 +2163,41 @@ def handle_scheduler_retry(task_id, reason=''): return {'ok': False, 'error': f'任务 {task_id} 当前状态 {state} 不支持重试'} sched = _ensure_scheduler(task) + run_id = _new_run_id() + lease_result = _acquire_lease(task, stage=state, role='scheduler', owner_run_id=run_id, ttl_sec=180) + if not lease_result.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action='retry', + reason_code='lease_busy', + details=str(lease_result), + dedupe_key=f'{task_id}:retry:lease_busy', + ) + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 当前被其它流程持有租约'} + + commit = commit_state_change( + task, + action='retry', + reason_code='manual_retry', + owner_run_id=run_id, + expected_version=sched.get('stateVersion'), + flow_remark=f'🔁 手动重试:{reason or "人工触发"}', + flow_from='皇上', + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 重试被拒绝: {commit.get("blockedBy")}'} + sched['retryCount'] = int(sched.get('retryCount') or 0) + 1 sched['lastRetryAt'] = now_iso() sched['lastDispatchTrigger'] = 'taizi-retry' - _scheduler_add_flow(task, f'触发重试第{sched["retryCount"]}次:{reason or "超时未推进"}') + _scheduler_add_flow(task, f'触发重试第{sched["retryCount"]}次:{reason or "超时未推进"}', reason_code='manual_retry') task['updatedAt'] = now_iso() save_tasks(tasks) - dispatch_for_state(task_id, task, state, trigger='taizi-retry') + dispatch_for_state(task_id, task, state, trigger='taizi-retry', owner_run_id=run_id) return {'ok': True, 'message': f'{task_id} 已触发重试派发', 'retryCount': sched['retryCount']} @@ -1011,6 +2211,33 @@ def handle_scheduler_escalate(task_id, reason=''): return {'ok': False, 'error': f'任务 {task_id} 已结束,无需升级'} sched = _ensure_scheduler(task) + run_id = _new_run_id() + lease_result = _acquire_lease(task, stage=state, role='scheduler', owner_run_id=run_id, ttl_sec=180) + if not lease_result.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action='escalate', + reason_code='lease_busy', + details=str(lease_result), + dedupe_key=f'{task_id}:escalate:lease_busy', + ) + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 当前被其它流程持有租约'} + + commit = commit_state_change( + task, + action='escalate', + reason_code='manual_escalate', + owner_run_id=run_id, + expected_version=sched.get('stateVersion'), + flow_remark=f'⬆️ 手动升级:{reason or "人工触发"}', + flow_from='皇上', + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 升级被拒绝: {commit.get("blockedBy")}'} + current_level = int(sched.get('escalationLevel') or 0) next_level = min(current_level + 1, 2) target = 'menxia' if next_level == 1 else 'shangshu' @@ -1018,7 +2245,7 @@ def handle_scheduler_escalate(task_id, reason=''): sched['escalationLevel'] = next_level sched['lastEscalatedAt'] = now_iso() - _scheduler_add_flow(task, f'升级到{target_label}协调:{reason or "任务停滞"}', to=target_label) + _scheduler_add_flow(task, f'升级到{target_label}协调:{reason or "任务停滞"}', to=target_label, reason_code='manual_escalate') task['updatedAt'] = now_iso() save_tasks(tasks) @@ -1041,21 +2268,48 @@ def handle_scheduler_rollback(task_id, reason=''): if not task: return {'ok': False, 'error': f'任务 {task_id} 不存在'} sched = _ensure_scheduler(task) + run_id = _new_run_id() + lease_result = _acquire_lease(task, stage=task.get('state', ''), role='scheduler', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + if not lease_result.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action='rollback', + reason_code='lease_busy', + details=str(lease_result), + dedupe_key=f'{task_id}:rollback:lease_busy', + ) + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 当前被其它流程持有租约'} + snapshot = sched.get('snapshot') or {} snap_state = snapshot.get('state') if not snap_state: return {'ok': False, 'error': f'任务 {task_id} 无可用回滚快照'} old_state = task.get('state', '') - task['state'] = snap_state - task['org'] = snapshot.get('org', task.get('org', '')) - task['now'] = f'↩️ 太子调度自动回滚:{reason or "恢复到上个稳定节点"}' - task['block'] = '无' + commit = commit_state_change( + task, + action='rollback', + reason_code='manual_rollback', + owner_run_id=run_id, + expected_version=sched.get('stateVersion'), + to_state=snap_state, + to_org=snapshot.get('org', task.get('org', '')), + now_text=f'↩️ 太子调度自动回滚:{reason or "恢复到上个稳定节点"}', + block_text='无', + flow_remark=f'↩️ 手动回滚:{old_state} → {snap_state},原因:{reason or "人工触发"}', + flow_from='皇上', + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 回滚被拒绝: {commit.get("blockedBy")}'} + sched['retryCount'] = 0 sched['escalationLevel'] = 0 sched['stallSince'] = None sched['lastProgressAt'] = now_iso() - _scheduler_add_flow(task, f'执行回滚:{old_state} → {snap_state},原因:{reason or "停滞恢复"}') + _scheduler_add_flow(task, f'执行回滚:{old_state} → {snap_state},原因:{reason or "停滞恢复"}', reason_code='manual_rollback') task['updatedAt'] = now_iso() save_tasks(tasks) @@ -1065,13 +2319,135 @@ def handle_scheduler_rollback(task_id, reason=''): return {'ok': True, 'message': f'{task_id} 已回滚到 {snap_state}'} +def decide_next_action(task, threshold_sec=180): + sched = _ensure_scheduler(task) + state = task.get('state', '') + task_threshold = int(sched.get('stallThresholdSec') or threshold_sec) + now_dt = datetime.datetime.now(datetime.timezone.utc) + last_progress = _parse_iso(sched.get('lastProgressAt') or task.get('updatedAt')) + if not last_progress: + last_progress = now_dt + stalled_sec = max(0, int((now_dt - last_progress).total_seconds())) + state_since = _parse_iso(sched.get('stateSince') or task.get('updatedAt')) + state_age_sec = max(0, int((now_dt - state_since).total_seconds())) if state_since else 0 + state_age_limit = max(task_threshold, int(sched.get('maxStateAgeSec') or (task_threshold * 4))) + age_overdue = ( + sched.get('autoAdvance', True) + and state in _AUTO_ADVANCE_SAFE_STATES + and state_age_sec >= state_age_limit + ) + + if state in _TERMINAL_STATES or task.get('archived') or state == 'Blocked': + return {'action': 'noop', 'reasonCode': 'state_terminal_or_blocked'} + + writeback = sched.get('writeback') or {} + wb_status = writeback.get('status') + if wb_status == 'WritebackPending': + retry_count = int(writeback.get('retryCount') or 0) + max_retry = int(writeback.get('maxRetry') or 2) + if retry_count < max_retry: + allow = _action_allowed(task, 'writeback_retry') + if allow.get('ok'): + return { + 'action': 'writeback_retry', + 'reasonCode': 'writeback_retry_budget', + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + return { + 'action': 'await-decision', + 'reasonCode': 'writeback_pending_need_human', + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + + if stalled_sec < task_threshold and not age_overdue: + return {'action': 'noop', 'reasonCode': 'below_threshold'} + + if age_overdue and stalled_sec < task_threshold: + flow = _STATE_FLOW.get(state) + if flow: + next_state, _, _, _ = flow + return { + 'action': 'auto-advance', + 'reasonCode': 'state_age_overdue', + 'toState': next_state, + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + + if state in _RISK_DECISION_STATES: + return { + 'action': 'await-decision', + 'reasonCode': 'risk_state_stalled', + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + + retry_count = int(sched.get('retryCount') or 0) + max_retry = max(0, int(sched.get('maxRetry') or 1)) + if retry_count < max_retry: + allow = _action_allowed(task, 'retry') + if allow.get('ok'): + return { + 'action': 'retry', + 'reasonCode': 'stall_retry_budget', + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + return {'action': 'noop', 'reasonCode': f'blocked_{allow.get("blockedBy", "retry")}'} + + level = int(sched.get('escalationLevel') or 0) + if level < 2: + allow = _action_allowed(task, 'escalate') + if allow.get('ok'): + next_level = level + 1 + target = 'menxia' if next_level == 1 else 'shangshu' + target_label = '门下省' if next_level == 1 else '尚书省' + return { + 'action': 'escalate', + 'reasonCode': 'stall_need_escalation', + 'to': target, + 'toLabel': target_label, + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + return {'action': 'noop', 'reasonCode': f'blocked_{allow.get("blockedBy", "escalate")}'} + + if sched.get('autoAdvance', True) and state in _AUTO_ADVANCE_SAFE_STATES: + flow = _STATE_FLOW.get(state) + if flow: + next_state, _, _, _ = flow + return { + 'action': 'auto-advance', + 'reasonCode': 'stall_auto_advance', + 'toState': next_state, + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + + if sched.get('autoRollback', True): + snapshot = sched.get('snapshot') or {} + snap_state = snapshot.get('state') + if snap_state and snap_state != state: + return { + 'action': 'rollback', + 'reasonCode': 'stall_auto_rollback', + 'toState': snap_state, + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + + return {'action': 'wait_human', 'reasonCode': 'no_safe_auto_action'} + + def handle_scheduler_scan(threshold_sec=180): threshold_sec = max(30, int(threshold_sec or 180)) tasks = load_tasks() - now_dt = datetime.datetime.now(datetime.timezone.utc) - pending_retries = [] + pending_dispatches = [] pending_escalates = [] - pending_rollbacks = [] + pending_auto_advances = [] + pending_writeback_retries = [] actions = [] changed = False @@ -1084,69 +2460,220 @@ def handle_scheduler_scan(threshold_sec=180): continue sched = _ensure_scheduler(task) - task_threshold = int(sched.get('stallThresholdSec') or threshold_sec) - last_progress = _parse_iso(sched.get('lastProgressAt') or task.get('updatedAt')) - if not last_progress: + decision = decide_next_action(task, threshold_sec) + action = decision.get('action', 'noop') + if action in ('noop', 'wait_human'): continue - stalled_sec = max(0, int((now_dt - last_progress).total_seconds())) - if stalled_sec < task_threshold: - continue - - if not sched.get('stallSince'): - sched['stallSince'] = now_iso() + run_id = _new_run_id() + version = sched.get('stateVersion') + lease_result = _acquire_lease( + task, + stage=state, + role='scheduler', + owner_run_id=run_id, + ttl_sec=180, + force_takeover=(action in ('rollback',)), + ) + if not lease_result.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action=action, + reason_code='lease_busy', + details=str(lease_result), + dedupe_key=f'{task_id}:{action}:lease_busy', + ) changed = True + continue - retry_count = int(sched.get('retryCount') or 0) - max_retry = max(0, int(sched.get('maxRetry') or 1)) - level = int(sched.get('escalationLevel') or 0) - - if retry_count < max_retry: - sched['retryCount'] = retry_count + 1 + if action == 'retry': + commit = commit_state_change( + task, + action='retry', + reason_code=decision.get('reasonCode', 'stall_retry_budget'), + owner_run_id=run_id, + expected_version=version, + flow_remark=f'🔁 自动重试:停滞{decision.get("stalledSec", 0)}秒', + ) + if not commit.get('committed'): + changed = True + continue + sched['retryCount'] = int(sched.get('retryCount') or 0) + 1 sched['lastRetryAt'] = now_iso() sched['lastDispatchTrigger'] = 'taizi-scan-retry' - _scheduler_add_flow(task, f'停滞{stalled_sec}秒,触发自动重试第{sched["retryCount"]}次') - pending_retries.append((task_id, state)) - actions.append({'taskId': task_id, 'action': 'retry', 'stalledSec': stalled_sec}) + pending_dispatches.append((task_id, state, run_id, 'taizi-scan-retry')) + actions.append({ + 'taskId': task_id, + 'action': 'retry', + 'stalledSec': decision.get('stalledSec'), + 'reasonCode': decision.get('reasonCode'), + }) changed = True continue - if level < 2: - next_level = level + 1 - target = 'menxia' if next_level == 1 else 'shangshu' - target_label = '门下省' if next_level == 1 else '尚书省' + if action == 'escalate': + next_level = min(int(sched.get('escalationLevel') or 0) + 1, 2) + target = decision.get('to') or ('menxia' if next_level == 1 else 'shangshu') + target_label = decision.get('toLabel') or ('门下省' if next_level == 1 else '尚书省') + commit = commit_state_change( + task, + action='escalate', + reason_code=decision.get('reasonCode', 'stall_need_escalation'), + owner_run_id=run_id, + expected_version=version, + flow_remark=f'⬆️ 自动升级:停滞{decision.get("stalledSec", 0)}秒,升级至{target_label}协调', + ) + if not commit.get('committed'): + changed = True + continue sched['escalationLevel'] = next_level sched['lastEscalatedAt'] = now_iso() - _scheduler_add_flow(task, f'停滞{stalled_sec}秒,升级至{target_label}协调', to=target_label) - pending_escalates.append((task_id, state, target, target_label, stalled_sec)) - actions.append({'taskId': task_id, 'action': 'escalate', 'to': target_label, 'stalledSec': stalled_sec}) + pending_escalates.append((task_id, state, target, target_label, decision.get('stalledSec', 0))) + actions.append({ + 'taskId': task_id, + 'action': 'escalate', + 'to': target_label, + 'stalledSec': decision.get('stalledSec'), + 'reasonCode': decision.get('reasonCode'), + }) + changed = True + continue + + if action == 'auto-advance': + flow = _STATE_FLOW.get(state) + if flow: + next_state, _, to_dept, _ = flow + _scheduler_snapshot(task, f'auto-advance-before-{state}') + commit = commit_state_change( + task, + action='advance', + reason_code=decision.get('reasonCode', 'auto_advance'), + owner_run_id=run_id, + expected_version=version, + to_state=next_state, + to_org=_derive_org_for_state(task, next_state, task.get('org', '')), + now_text=( + f'⏩ 太子调度自动推进:{_STATE_LABELS.get(state, state)}' + f' → {_STATE_LABELS.get(next_state, next_state)}' + ), + block_text='无', + flow_remark=( + f'⏩ 自动推进:{_STATE_LABELS.get(state, state)} → ' + f'{_STATE_LABELS.get(next_state, next_state)}' + ), + flow_to=to_dept, + ) + if not commit.get('committed'): + changed = True + continue + _scheduler_mark_progress(task, f'自动推进 {state} -> {next_state}', reason_code='auto_advance') + pending_auto_advances.append((task_id, next_state)) + actions.append({ + 'taskId': task_id, + 'action': 'auto-advance', + 'fromState': state, + 'toState': next_state, + 'reasonCode': decision.get('reasonCode'), + 'stalledSec': decision.get('stalledSec'), + 'stateAgeSec': decision.get('stateAgeSec'), + }) + changed = True + continue + + if action == 'writeback_retry': + commit = commit_state_change( + task, + action='writeback_retry', + reason_code=decision.get('reasonCode', 'writeback_retry_budget'), + owner_run_id=run_id, + expected_version=version, + flow_remark=f'🧩 自动提交重试:停滞{decision.get("stalledSec", 0)}秒', + ) + if not commit.get('committed'): + changed = True + continue + wb = sched.setdefault('writeback', {}) + wb['retryCount'] = int(wb.get('retryCount') or 0) + 1 + wb['status'] = 'WritebackPending' + wb['lastError'] = wb.get('lastError') or 'writeback_pending_retry' + _scheduler_add_flow( + task, + f'写回失败,触发提交重试第{wb["retryCount"]}次', + reason_code=decision.get('reasonCode', 'writeback_retry_budget'), + ) + pending_writeback_retries.append((task_id, run_id)) + actions.append({ + 'taskId': task_id, + 'action': 'writeback_retry', + 'stalledSec': decision.get('stalledSec'), + 'reasonCode': decision.get('reasonCode'), + }) changed = True continue - if sched.get('autoRollback', True): + if action == 'await-decision': + stalled_sec = int(decision.get('stalledSec') or 0) + state_age_sec = int(decision.get('stateAgeSec') or 0) + sched['decisionPacket'] = _build_decision_packet(task, state, stalled_sec, state_age_sec) + if not sched.get('awaitingEmperorDecision'): + sched['awaitingEmperorDecision'] = True + task['block'] = '风险节点停滞,等待皇上裁决' + task['now'] = f'⚠️ 风险节点{_STATE_LABELS.get(state, state)}停滞,等待皇上裁决' + _scheduler_add_flow( + task, + f'风险节点停滞{stalled_sec}秒,暂停自动推进并等待皇上裁决', + reason_code=decision.get('reasonCode', 'await_decision') + ) + actions.append({ + 'taskId': task_id, + 'action': 'await-decision', + 'state': state, + 'stalledSec': stalled_sec, + 'question': sched['decisionPacket'].get('question'), + 'reasonCode': decision.get('reasonCode'), + }) + task['updatedAt'] = now_iso() + changed = True + continue + + if action == 'rollback' and sched.get('autoRollback', True): snapshot = sched.get('snapshot') or {} snap_state = snapshot.get('state') if snap_state and snap_state != state: old_state = state - task['state'] = snap_state - task['org'] = snapshot.get('org', task.get('org', '')) - task['now'] = '↩️ 太子调度自动回滚到稳定节点' - task['block'] = '无' + commit = commit_state_change( + task, + action='rollback', + reason_code=decision.get('reasonCode', 'auto_rollback'), + owner_run_id=run_id, + expected_version=version, + to_state=snap_state, + to_org=snapshot.get('org', task.get('org', '')), + now_text='↩️ 太子调度自动回滚到稳定节点', + block_text='无', + flow_remark=f'↩️ 连续停滞,自动回滚:{old_state} → {snap_state}', + ) + if not commit.get('committed'): + changed = True + continue sched['retryCount'] = 0 sched['escalationLevel'] = 0 sched['stallSince'] = None sched['lastProgressAt'] = now_iso() - _scheduler_add_flow(task, f'连续停滞,自动回滚:{old_state} → {snap_state}') - pending_rollbacks.append((task_id, snap_state)) + pending_dispatches.append((task_id, snap_state, run_id, 'taizi-auto-rollback')) actions.append({'taskId': task_id, 'action': 'rollback', 'toState': snap_state}) changed = True if changed: save_tasks(tasks) - for task_id, state in pending_retries: + for task_id, state, owner_run_id, trigger in pending_dispatches: retry_task = next((t for t in tasks if t.get('id') == task_id), None) if retry_task: - dispatch_for_state(task_id, retry_task, state, trigger='taizi-scan-retry') + dispatch_for_state(task_id, retry_task, state, trigger=trigger, owner_run_id=owner_run_id) + + for task_id, owner_run_id in pending_writeback_retries: + _retry_writeback_for_task(task_id, owner_run_id=owner_run_id) for task_id, state, target, target_label, stalled_sec in pending_escalates: msg = ( @@ -1159,10 +2686,10 @@ def handle_scheduler_scan(threshold_sec=180): ) wake_agent(target, msg) - for task_id, state in pending_rollbacks: - rollback_task = next((t for t in tasks if t.get('id') == task_id), None) - if rollback_task and state not in _TERMINAL_STATES: - dispatch_for_state(task_id, rollback_task, state, trigger='taizi-auto-rollback') + for task_id, state in pending_auto_advances: + adv_task = next((t for t in tasks if t.get('id') == task_id), None) + if adv_task and state not in _TERMINAL_STATES: + dispatch_for_state(task_id, adv_task, state, trigger='taizi-auto-advance') return { 'ok': True, @@ -1676,6 +3203,18 @@ def get_task_activity(task_id): 'from': fl.get('from', ''), 'to': fl.get('to', ''), 'remark': fl.get('remark', ''), + 'reasonCode': fl.get('reasonCode', ''), + }) + + diagnostic_log = task.get('diagnostic_log', []) + for dg in diagnostic_log: + activity.append({ + 'at': dg.get('at', ''), + 'kind': 'diagnostic', + 'action': dg.get('action', ''), + 'reasonCode': dg.get('reasonCode', ''), + 'text': dg.get('details', ''), + 'eventType': dg.get('eventType', ''), }) progress_log = task.get('progress_log', []) @@ -1881,10 +3420,175 @@ def get_task_activity(task_id): 'Pending': '待处理', 'Taizi': '太子', 'Zhongshu': '中书省', 'Menxia': '门下省', 'Assigned': '尚书省', 'Next': '待执行', 'Doing': '执行中', 'Review': '审查', 'Done': '完成', } +_AUTO_ADVANCE_SAFE_STATES = {'Taizi', 'Zhongshu', 'Assigned', 'Next'} +_RISK_DECISION_STATES = {'Menxia', 'Review'} + + +def _derive_org_for_state(task, state, current_org=''): + """根据目标状态推导 org,避免 state/org 不一致导致后续派发失败。""" + fixed = { + 'Taizi': '太子', + 'Zhongshu': '中书省', + 'Menxia': '门下省', + 'Assigned': '尚书省', + 'Review': '尚书省', + } + if state in fixed: + return fixed[state] + + if state in ('Doing', 'Next'): + target_dept = (task.get('targetDept') or '').strip() + if target_dept in _ORG_AGENT_MAP: + return target_dept + if current_org in _ORG_AGENT_MAP and current_org not in ('中书省', '门下省', '尚书省', '太子'): + return current_org + return current_org or target_dept or '尚书省' + + return current_org or task.get('org', '') + + +def _extract_kanban_commands_from_text(text): + """从 agent 文本中提取 kanban_update.py 命令参数(仅白名单子命令)。""" + if not text: + return [] + allowed = {'progress', 'flow', 'state', 'todo', 'done', 'block'} + out = [] + for raw in text.splitlines(): + line = raw.strip() + if not line or 'kanban_update.py' not in line: + continue + line = line.strip('`').lstrip('-').strip() + if not line.startswith('python3'): + continue + try: + parts = shlex.split(line) + except Exception: + continue + script_idx = -1 + for i, tok in enumerate(parts): + if tok.endswith('kanban_update.py'): + script_idx = i + break + if script_idx < 0: + continue + args = parts[script_idx + 1:] + if not args: + continue + subcmd = args[0] + if subcmd not in allowed: + continue + out.append(args) + return out + + +def _bridge_apply_kanban_commands(task_id, text): + """在本机代执行 agent 输出中的 kanban_update.py 命令,解决 agent 沙箱无法写看板。""" + commands = _extract_kanban_commands_from_text(text) + if not commands: + return {'applied': 0, 'attempted': 0, 'errors': [], 'deptDispatches': []} + + script_path = str(SCRIPTS / 'kanban_update.py') + applied = 0 + errors = [] + attempted = 0 + dept_dispatches = [] + dept_seen = set() + + for args in commands[:6]: + subcmd = args[0] + # 所有允许子命令都要求第二个参数是 task_id,避免误执行到其它任务 + if len(args) < 2 or args[1] != task_id: + continue + if subcmd == 'flow' and len(args) >= 4: + from_dept = (args[2] or '').strip() + to_dept = (args[3] or '').strip() + remark = (args[4] or '').strip() if len(args) >= 5 else '' + if from_dept == '尚书省' and to_dept in _EXECUTION_DEPTS: + standby = ('待命' in remark) or ('排障' in remark and '派发' not in remark) + if not standby and to_dept not in dept_seen: + dept_seen.add(to_dept) + dept_dispatches.append(to_dept) + attempted += 1 + cmd = ['python3', script_path] + args + try: + result = subprocess.run(cmd, capture_output=True, text=True, timeout=20) + if result.returncode == 0: + applied += 1 + else: + stderr = (result.stderr or result.stdout or '').strip() + errors.append(f'{subcmd}: {stderr[:160]}') + except Exception as e: + errors.append(f'{subcmd}: {str(e)[:160]}') + + return { + 'applied': applied, + 'attempted': attempted, + 'errors': errors, + 'deptDispatches': dept_dispatches, + } + + +def _pick_execution_dept(task, dept_dispatches): + target_dept = (task.get('targetDept') or '').strip() + if target_dept in _EXECUTION_DEPTS: + return target_dept + for dept in dept_dispatches or []: + if dept in _EXECUTION_DEPTS: + return dept + return '' + + +def _auto_handoff_to_execution(task_id, preferred_dept='', trigger='shangshu-auto-handoff'): + """尚书省派发后自动切换到六部执行态,并自动派发对应执行 Agent。""" + tasks = load_tasks() + task = next((t for t in tasks if t.get('id') == task_id), None) + if not task: + return {'ok': False, 'reason': 'task_not_found'} + + cur_state = task.get('state', '') + if cur_state not in ('Assigned', 'Next'): + return {'ok': False, 'reason': f'state={cur_state}'} + dept = (preferred_dept or '').strip() + if dept not in _EXECUTION_DEPTS: + return {'ok': False, 'reason': f'dept={dept}'} -def dispatch_for_state(task_id, task, new_state, trigger='state-transition'): + _ensure_scheduler(task) + _scheduler_snapshot(task, f'{trigger}-before-{cur_state}') + run_id = _new_run_id() + _acquire_lease(task, stage=cur_state, role='auto-handoff', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + version = task.get('_scheduler', {}).get('stateVersion') + task['targetDept'] = dept + commit = commit_state_change( + task, + action='advance', + reason_code='shangshu_auto_handoff', + owner_run_id=run_id, + expected_version=version, + to_state='Doing', + to_org=dept, + now_text=f'尚书省已派发,{dept}执行中', + block_text='无', + flow_remark=f'尚书省派发完成,自动切换到{dept}执行', + flow_to=dept, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'reason': f'commit_blocked:{commit.get("blockedBy")}'} + _scheduler_mark_progress(task, f'自动切换执行:{cur_state} -> Doing ({dept})', reason_code='shangshu_auto_handoff') + save_tasks(tasks) + + dispatch_for_state(task_id, task, 'Doing', trigger=trigger, owner_run_id=run_id) + return {'ok': True, 'dept': dept} + + +def dispatch_for_state(task_id, task, new_state, trigger='state-transition', owner_run_id=''): """推进/审批后自动派发对应 Agent(后台异步,不阻塞响应)。""" + tasks = load_tasks() + persisted = next((t for t in tasks if t.get('id') == task_id), None) + if persisted: + task = persisted + agent_id = _STATE_AGENT_MAP.get(new_state) if agent_id is None and new_state in ('Doing', 'Next'): org = task.get('org', '') @@ -1893,18 +3597,86 @@ def dispatch_for_state(task_id, task, new_state, trigger='state-transition'): log.info(f'ℹ️ {task_id} 新状态 {new_state} 无对应 Agent,跳过自动派发') return - _update_task_scheduler(task_id, lambda t, s: ( - s.update({ - 'lastDispatchAt': now_iso(), - 'lastDispatchStatus': 'queued', - 'lastDispatchAgent': agent_id, - 'lastDispatchTrigger': trigger, - }), - _scheduler_add_flow(t, f'已入队派发:{new_state} → {agent_id}({trigger})', to=_STATE_LABELS.get(new_state, new_state)) - )) + sched = _ensure_scheduler(task) + action_ok = _action_allowed(task, 'dispatch') + if not action_ok.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action='dispatch', + reason_code='dispatch_blocked', + details=str(action_ok), + dedupe_key=f'{task_id}:dispatch:blocked:{action_ok.get("blockedBy")}', + ) + if persisted: + task['updatedAt'] = now_iso() + save_tasks(tasks) + return + + run_id = owner_run_id or _new_run_id() + lease_result = _acquire_lease( + task, + stage=new_state, + role=agent_id, + owner_run_id=run_id, + ttl_sec=300, + force_takeover=False, + ) + if not lease_result.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action='dispatch', + reason_code='lease_busy', + details=str(lease_result), + dedupe_key=f'{task_id}:dispatch:lease_busy', + ) + if persisted: + task['updatedAt'] = now_iso() + save_tasks(tasks) + return + + commit = commit_state_change( + task, + action='dispatch', + reason_code='dispatch_queued', + owner_run_id=run_id, + expected_version=sched.get('stateVersion'), + flow_remark=f'🚀 已入队派发:{new_state} → {agent_id}({trigger})', + flow_to=_STATE_LABELS.get(new_state, new_state), + ) + if not commit.get('committed'): + _append_diagnostic( + task, + event_type='control_blocked', + action='dispatch', + reason_code='commit_blocked', + details=str(commit), + dedupe_key=f'{task_id}:dispatch:commit:{commit.get("blockedBy")}', + ) + if persisted: + task['updatedAt'] = now_iso() + save_tasks(tasks) + return + + sched.update({ + 'lastDispatchAt': now_iso(), + 'lastDispatchStatus': 'queued', + 'lastDispatchAgent': agent_id, + 'lastDispatchTrigger': trigger, + 'dispatchRunId': run_id, + 'dispatchAttempts': int(sched.get('dispatchAttempts') or 0) + 1, + }) + _set_cooldown(task, 'noEscalateUntil', _COOLDOWN_SECONDS['post_dispatch_escalate']) + _set_cooldown(task, 'noDispatchUntil', _COOLDOWN_SECONDS['post_dispatch_dispatch']) + task['updatedAt'] = now_iso() + if persisted: + save_tasks(tasks) title = task.get('title', '(无标题)') target_dept = task.get('targetDept', '') + kanban_cmd = 'python3 "scripts/kanban_update.py"' + kanban_cmd_fallback = f'python3 "{OCLAW_HOME / "workspace-main" / "scripts" / "kanban_update.py"}"' # 根据 agent_id 构造针对性消息 _msgs = { @@ -1912,21 +3684,21 @@ def dispatch_for_state(task_id, task, new_state, trigger='state-transition'): f'📜 皇上旨意需要你处理\n' f'任务ID: {task_id}\n' f'旨意: {title}\n' - f'⚠️ 看板已有此任务,请勿重复创建。直接用 kanban_update.py 更新状态。\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' f'请立即转交中书省起草执行方案。' ), 'zhongshu': ( f'📜 旨意已到中书省,请起草方案\n' f'任务ID: {task_id}\n' f'旨意: {title}\n' - f'⚠️ 看板已有此任务记录,请勿重复创建。直接用 kanban_update.py state 更新状态。\n' + f'⚠️ 看板已有此任务记录,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' f'请立即起草执行方案,走完完整三省流程(中书起草→门下审议→尚书派发→六部执行)。' ), 'menxia': ( f'📋 中书省方案提交审议\n' f'任务ID: {task_id}\n' f'旨意: {title}\n' - f'⚠️ 看板已有此任务,请勿重复创建。\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' f'请审议中书省方案,给出准奏或封驳意见。' ), 'shangshu': ( @@ -1934,30 +3706,88 @@ def dispatch_for_state(task_id, task, new_state, trigger='state-transition'): f'任务ID: {task_id}\n' f'旨意: {title}\n' f'{"建议派发部门: " + target_dept if target_dept else ""}\n' - f'⚠️ 看板已有此任务,请勿重复创建。\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' f'请分析方案并派发给六部执行。' ), + 'gongbu': ( + f'🔧 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), + 'xingbu': ( + f'⚖️ 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), + 'libu': ( + f'📝 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), + 'hubu': ( + f'💰 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), + 'bingbu': ( + f'⚔️ 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), + 'libu_hr': ( + f'👔 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), } msg = _msgs.get(agent_id, ( f'📌 请处理任务\n' f'任务ID: {task_id}\n' f'旨意: {title}\n' - f'⚠️ 看板已有此任务,请勿重复创建。直接用 kanban_update.py 更新状态。' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。' )) def _do_dispatch(): try: if not _check_gateway_alive(): log.warning(f'⚠️ {task_id} 自动派发跳过: Gateway 未启动') - _update_task_scheduler(task_id, lambda t, s: s.update({ - 'lastDispatchAt': now_iso(), - 'lastDispatchStatus': 'gateway-offline', - 'lastDispatchAgent': agent_id, - 'lastDispatchTrigger': trigger, - })) + def _mark_gateway_offline(t, s): + s.update({ + 'lastDispatchAt': now_iso(), + 'lastDispatchStatus': 'gateway-offline', + 'lastDispatchAgent': agent_id, + 'lastDispatchTrigger': trigger, + }) + s['controlState'] = 'RetryableFailure' + _release_lease(t, run_id) + _append_diagnostic( + t, + event_type='dispatch_failed', + action='dispatch', + reason_code='gateway_offline', + details=f'task={task_id}, agent={agent_id}', + dedupe_key=f'{task_id}:dispatch:gateway_offline', + ) + _update_task_scheduler(task_id, _mark_gateway_offline) return - cmd = ['openclaw', 'agent', '--agent', agent_id, '-m', msg, - '--deliver', '--channel', 'feishu', '--timeout', '300'] + # 默认走本地 direct 调用,避免硬编码 feishu 导致无渠道环境派发失败。 + # 如需强制渠道投递,可设置 EDICT_DISPATCH_CHANNEL=feishu|telegram|signal... + cmd = ['openclaw', 'agent', '--agent', agent_id, '-m', msg, '--timeout', '300'] + dispatch_channel = (os.environ.get('EDICT_DISPATCH_CHANNEL') or '').strip() + if dispatch_channel: + cmd.extend(['--deliver', '--channel', dispatch_channel]) max_retries = 2 err = '' for attempt in range(1, max_retries + 1): @@ -1965,16 +3795,82 @@ def _do_dispatch(): result = subprocess.run(cmd, capture_output=True, text=True, timeout=310) if result.returncode == 0: log.info(f'✅ {task_id} 自动派发成功 → {agent_id}') - _update_task_scheduler(task_id, lambda t, s: ( + dispatch_text = ((result.stdout or '') + '\n' + (result.stderr or '')).strip() + bridge = _bridge_apply_kanban_commands(task_id, dispatch_text) + handoff = {'ok': False} + handoff_dept = '' + if agent_id == 'shangshu' and new_state in ('Assigned', 'Next'): + handoff_dept = _pick_execution_dept(task, bridge.get('deptDispatches', [])) + if handoff_dept: + handoff = _auto_handoff_to_execution( + task_id, + preferred_dept=handoff_dept, + trigger='shangshu-auto-handoff' + ) + if handoff.get('ok'): + log.info(f'🚦 {task_id} 尚书省派发完成,自动切换到 {handoff_dept} 执行') + if bridge.get('applied', 0) > 0: + log.info( + f'🧩 {task_id} 桥接执行看板命令: ' + f'{bridge.get("applied", 0)}/{bridge.get("attempted", 0)}' + ) + elif bridge.get('attempted', 0) > 0 and bridge.get('errors'): + log.warning(f'⚠️ {task_id} 桥接执行失败: {" | ".join(bridge.get("errors", [])[:2])}') + def _mark_dispatch_success(t, s): s.update({ 'lastDispatchAt': now_iso(), 'lastDispatchStatus': 'success', 'lastDispatchAgent': agent_id, 'lastDispatchTrigger': trigger, 'lastDispatchError': '', - }), - _scheduler_add_flow(t, f'派发成功:{agent_id}({trigger})', to=t.get('org', '')) - )) + }) + wb = s.setdefault('writeback', {}) + wb.setdefault('retryCount', 0) + wb.setdefault('maxRetry', 2) + wb['lastDispatchOutput'] = dispatch_text[:12000] + if not wb.get('firstOutputAt'): + wb['firstOutputAt'] = now_iso() + if bridge.get('attempted', 0) <= 0: + wb['status'] = 'ExecutionOutputReady' + wb['lastError'] = 'no_bridge_command_detected' + elif bridge.get('applied', 0) >= bridge.get('attempted', 0): + wb['status'] = 'idle' + wb['lastCommittedAt'] = now_iso() + wb['lastError'] = '' + wb['retryCount'] = 0 + else: + wb['status'] = 'WritebackPending' + wb['retryCount'] = int(wb.get('retryCount') or 0) + 1 + wb['lastError'] = '; '.join((bridge.get('errors') or [])[:2]) or 'writeback_failed' + _append_diagnostic( + t, + event_type='writeback_pending', + action='writeback_retry', + reason_code='writeback_failed', + details=wb['lastError'], + dedupe_key=f'{task_id}:writeback_pending', + ) + _sync_control_state(t) + _set_cooldown(t, 'noEscalateUntil', _COOLDOWN_SECONDS['post_dispatch_escalate']) + _set_cooldown(t, 'noDispatchUntil', _COOLDOWN_SECONDS['post_dispatch_dispatch']) + _release_lease(t, run_id) + _scheduler_add_flow( + t, + ( + f'派发成功:{agent_id}({trigger})' + + ( + f';桥接执行 {bridge.get("applied", 0)}/{bridge.get("attempted", 0)}' + if bridge.get('attempted', 0) > 0 else '' + ) + + ( + f';自动切换执行:{handoff_dept}' + if handoff.get('ok') and handoff_dept else '' + ) + ), + to=t.get('org', ''), + reason_code='dispatch_success', + ) + _update_task_scheduler(task_id, _mark_dispatch_success) return err = result.stderr[:200] if result.stderr else result.stdout[:200] log.warning(f'⚠️ {task_id} 自动派发失败(第{attempt}次): {err}') @@ -1982,40 +3878,70 @@ def _do_dispatch(): import time time.sleep(5) log.error(f'❌ {task_id} 自动派发最终失败 → {agent_id}') - _update_task_scheduler(task_id, lambda t, s: ( + def _mark_dispatch_failed(t, s): s.update({ 'lastDispatchAt': now_iso(), 'lastDispatchStatus': 'failed', 'lastDispatchAgent': agent_id, 'lastDispatchTrigger': trigger, 'lastDispatchError': err, - }), - _scheduler_add_flow(t, f'派发失败:{agent_id}({trigger})', to=t.get('org', '')) - )) + 'controlState': 'RetryableFailure', + }) + _release_lease(t, run_id) + _append_diagnostic( + t, + event_type='dispatch_failed', + action='dispatch', + reason_code='dispatch_failed', + details=err, + dedupe_key=f'{task_id}:dispatch_failed', + ) + _scheduler_add_flow(t, f'派发失败:{agent_id}({trigger})', to=t.get('org', ''), reason_code='dispatch_failed') + _update_task_scheduler(task_id, _mark_dispatch_failed) except subprocess.TimeoutExpired: log.error(f'❌ {task_id} 自动派发超时 → {agent_id}') - _update_task_scheduler(task_id, lambda t, s: ( + def _mark_dispatch_timeout(t, s): s.update({ 'lastDispatchAt': now_iso(), 'lastDispatchStatus': 'timeout', 'lastDispatchAgent': agent_id, 'lastDispatchTrigger': trigger, 'lastDispatchError': 'timeout', - }), - _scheduler_add_flow(t, f'派发超时:{agent_id}({trigger})', to=t.get('org', '')) - )) + 'controlState': 'RetryableFailure', + }) + _release_lease(t, run_id) + _append_diagnostic( + t, + event_type='dispatch_failed', + action='dispatch', + reason_code='dispatch_timeout', + details=f'task={task_id}, agent={agent_id}', + dedupe_key=f'{task_id}:dispatch_timeout', + ) + _scheduler_add_flow(t, f'派发超时:{agent_id}({trigger})', to=t.get('org', ''), reason_code='dispatch_timeout') + _update_task_scheduler(task_id, _mark_dispatch_timeout) except Exception as e: log.warning(f'⚠️ {task_id} 自动派发异常: {e}') - _update_task_scheduler(task_id, lambda t, s: ( + def _mark_dispatch_error(t, s): s.update({ 'lastDispatchAt': now_iso(), 'lastDispatchStatus': 'error', 'lastDispatchAgent': agent_id, 'lastDispatchTrigger': trigger, 'lastDispatchError': str(e)[:200], - }), - _scheduler_add_flow(t, f'派发异常:{agent_id}({trigger})', to=t.get('org', '')) - )) + 'controlState': 'RetryableFailure', + }) + _release_lease(t, run_id) + _append_diagnostic( + t, + event_type='dispatch_failed', + action='dispatch', + reason_code='dispatch_error', + details=str(e)[:200], + dedupe_key=f'{task_id}:dispatch_error', + ) + _scheduler_add_flow(t, f'派发异常:{agent_id}({trigger})', to=t.get('org', ''), reason_code='dispatch_error') + _update_task_scheduler(task_id, _mark_dispatch_error) threading.Thread(target=_do_dispatch, daemon=True).start() log.info(f'🚀 {task_id} 推进后自动派发 → {agent_id}') @@ -2032,24 +3958,35 @@ def handle_advance_state(task_id, comment=''): return {'ok': False, 'error': f'任务 {task_id} 状态为 {cur},无法推进'} _ensure_scheduler(task) _scheduler_snapshot(task, f'advance-before-{cur}') + run_id = _new_run_id() + _acquire_lease(task, stage=cur, role='manual-advance', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + version = task.get('_scheduler', {}).get('stateVersion') next_state, from_dept, to_dept, default_remark = _STATE_FLOW[cur] remark = comment or default_remark - task['state'] = next_state - task['now'] = f'⬇️ 手动推进:{remark}' - task.setdefault('flow_log', []).append({ - 'at': now_iso(), - 'from': from_dept, - 'to': to_dept, - 'remark': f'⬇️ 手动推进:{remark}' - }) - _scheduler_mark_progress(task, f'手动推进 {cur} -> {next_state}') - task['updatedAt'] = now_iso() + commit = commit_state_change( + task, + action='manual_decide', + reason_code='manual_advance', + owner_run_id=run_id, + expected_version=version, + to_state=next_state, + to_org=_derive_org_for_state(task, next_state, task.get('org', '')), + now_text=f'⬇️ 手动推进:{remark}', + flow_from=from_dept, + flow_to=to_dept, + flow_remark=f'⬇️ 手动推进:{remark}', + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 推进失败: {commit.get("blockedBy")}'} + _scheduler_mark_progress(task, f'手动推进 {cur} -> {next_state}', reason_code='manual_advance') save_tasks(tasks) # 🚀 推进后自动派发对应 Agent(Done 状态无需派发) if next_state != 'Done': - dispatch_for_state(task_id, task, next_state) + dispatch_for_state(task_id, task, next_state, owner_run_id=run_id) from_label = _STATE_LABELS.get(cur, cur) to_label = _STATE_LABELS.get(next_state, next_state) @@ -2179,6 +4116,14 @@ def do_GET(self): self.send_json({'ok': False, 'error': 'task_id required'}, 400) else: self.send_json(get_scheduler_state(task_id)) + elif p == '/api/scheduler-metrics': + self.send_json(get_scheduler_metrics()) + elif p.startswith('/api/scheduler-metrics/'): + task_id = p.replace('/api/scheduler-metrics/', '') + if not task_id: + self.send_json({'ok': False, 'error': 'task_id required'}, 400) + else: + self.send_json(get_scheduler_metrics(task_id)) elif p == '/api/agents-status': self.send_json(get_agents_status()) elif p.startswith('/api/agent-activity/'): @@ -2253,13 +4198,30 @@ def do_POST(self): self.send_json({'ok': False, 'error': f'repair flow order failed: {e}'}, 500) return + if p == '/api/scheduler-action': + task_id = body.get('taskId', '').strip() + action = body.get('action', '').strip() + reason = body.get('reason', '').strip() + expected_version = body.get('expectedVersion') + owner_run_id = body.get('ownerRunId', '').strip() + recovery_target = body.get('recoveryTarget', '').strip() + if not task_id or not action: + self.send_json({'ok': False, 'error': 'taskId/action required'}, 400) + return + self.send_json(handle_scheduler_action(task_id, action, reason, expected_version, owner_run_id, recovery_target)) + return + + if p == '/api/scheduler-commit': + self.send_json(handle_scheduler_commit(body)) + return + if p == '/api/scheduler-retry': task_id = body.get('taskId', '').strip() reason = body.get('reason', '').strip() if not task_id: self.send_json({'ok': False, 'error': 'taskId required'}, 400) return - self.send_json(handle_scheduler_retry(task_id, reason)) + self.send_json(handle_scheduler_action(task_id, 'retry', reason)) return if p == '/api/scheduler-escalate': @@ -2268,7 +4230,7 @@ def do_POST(self): if not task_id: self.send_json({'ok': False, 'error': 'taskId required'}, 400) return - self.send_json(handle_scheduler_escalate(task_id, reason)) + self.send_json(handle_scheduler_action(task_id, 'escalate', reason)) return if p == '/api/scheduler-rollback': @@ -2277,7 +4239,7 @@ def do_POST(self): if not task_id: self.send_json({'ok': False, 'error': 'taskId required'}, 400) return - self.send_json(handle_scheduler_rollback(task_id, reason)) + self.send_json(handle_scheduler_action(task_id, 'rollback', reason)) return if p == '/api/morning-brief/refresh': @@ -2402,6 +4364,22 @@ def do_refresh(): self.send_json(result) return + if p == '/api/court-discuss': + action = body.get('action', 'start') + topic = body.get('topic', '').strip() + participants = body.get('participants', []) + session_id = body.get('sessionId', '').strip() + force = bool(body.get('force', False)) + if action == 'start' and not topic: + self.send_json({'ok': False, 'error': 'topic required'}, 400) + return + if action in ('next', 'status', 'finalize') and not session_id: + self.send_json({'ok': False, 'error': 'sessionId required'}, 400) + return + result = handle_court_discuss(action=action, topic=topic, participants=participants, session_id=session_id, force=force) + self.send_json(result) + return + if p == '/api/review-action': task_id = body.get('taskId', '').strip() action = body.get('action', '').strip() # approve, reject diff --git a/scripts/freeze_scheduler_baseline.py b/scripts/freeze_scheduler_baseline.py new file mode 100644 index 00000000..db843fd2 --- /dev/null +++ b/scripts/freeze_scheduler_baseline.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +"""Freeze scheduler baseline metrics for recent tasks.""" + +import argparse +import datetime as dt +import json +import pathlib + + +ROOT = pathlib.Path(__file__).resolve().parents[1] +DATA = ROOT / "data" + + +def _parse_iso(value): + if not value or not isinstance(value, str): + return None + try: + return dt.datetime.fromisoformat(value.replace("Z", "+00:00")) + except Exception: + return None + + +def _task_metrics(task): + sched = task.get("_scheduler") or {} + flow = task.get("flow_log") or [] + progress = task.get("progress_log") or [] + unique_steps = len({ + (item.get("agent", ""), item.get("text", ""), item.get("state", "")) + for item in progress + if item.get("text") + }) + retries = sum(1 for item in flow if "重试" in (item.get("remark") or "")) + escalates = sum(1 for item in flow if "升级" in (item.get("remark") or "")) + rollbacks = sum(1 for item in flow if "回滚" in (item.get("remark") or "")) + return { + "taskId": task.get("id", ""), + "state": task.get("state", ""), + "dispatchAttempts": int(sched.get("dispatchAttempts") or 0), + "retryEvents": retries, + "escalateEvents": escalates, + "rollbackEvents": rollbacks, + "flowCount": len(flow), + "progressCount": len(progress), + "uniqueExecutionSteps": unique_steps, + "updatedAt": task.get("updatedAt", ""), + } + + +def _summary(rows): + total_dispatch = sum(item["dispatchAttempts"] for item in rows) + total_unique = sum(item["uniqueExecutionSteps"] for item in rows) + return { + "taskCount": len(rows), + "dispatchAttempts": total_dispatch, + "retryEvents": sum(item["retryEvents"] for item in rows), + "escalateEvents": sum(item["escalateEvents"] for item in rows), + "rollbackEvents": sum(item["rollbackEvents"] for item in rows), + "flowCount": sum(item["flowCount"] for item in rows), + "progressCount": sum(item["progressCount"] for item in rows), + "dispatchAmplificationRatio": round(total_dispatch / total_unique, 3) if total_unique else 0.0, + } + + +def main(): + parser = argparse.ArgumentParser(description="Freeze scheduler baseline snapshot") + parser.add_argument("--days", type=int, default=7, help="Lookback days") + parser.add_argument( + "--output", + default=str(DATA / "scheduler_baseline_latest.json"), + help="Output json path", + ) + args = parser.parse_args() + + source = DATA / "tasks_source.json" + tasks = json.loads(source.read_text(encoding="utf-8")) if source.exists() else [] + now = dt.datetime.now(dt.timezone.utc) + since = now - dt.timedelta(days=max(1, int(args.days))) + + selected = [] + for task in tasks: + updated = _parse_iso(task.get("updatedAt")) + if updated and updated < since: + continue + selected.append(_task_metrics(task)) + + payload = { + "generatedAt": now.isoformat().replace("+00:00", "Z"), + "lookbackDays": int(args.days), + "source": str(source), + "summary": _summary(selected), + "tasks": selected, + } + + output = pathlib.Path(args.output) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") + print(f"baseline written: {output}") + print(json.dumps(payload["summary"], ensure_ascii=False)) + + +if __name__ == "__main__": + main() diff --git a/scripts/kanban_update.py b/scripts/kanban_update.py index 0566cac7..c05301f0 100644 --- a/scripts/kanban_update.py +++ b/scripts/kanban_update.py @@ -24,7 +24,25 @@ """ import json, pathlib, datetime, sys, subprocess, logging, os, re -_BASE = pathlib.Path(__file__).resolve().parent.parent +_LOCAL_BASE = pathlib.Path(__file__).resolve().parent.parent + + +def _resolve_shared_base(): + """Resolve a shared kanban workspace base across multi-agent workspaces.""" + env_base = (os.environ.get('EDICT_SHARED_BASE') or os.environ.get('EDICT_KANBAN_BASE') or '').strip() + if env_base: + p = pathlib.Path(env_base).expanduser() + if (p / 'data' / 'tasks_source.json').exists(): + return p + + default_shared = pathlib.Path.home() / '.openclaw' / 'workspace' / 'edict' + if (default_shared / 'data' / 'tasks_source.json').exists(): + return default_shared + + return _LOCAL_BASE + + +_BASE = _resolve_shared_base() TASKS_FILE = _BASE / 'data' / 'tasks_source.json' REFRESH_SCRIPT = _BASE / 'scripts' / 'refresh_live_data.py' @@ -62,6 +80,30 @@ } MAX_PROGRESS_LOG = 100 # 单任务最大进展日志条数 +DEDUP_WINDOW_SEC = 60 + + +def _parse_iso(ts): + if not ts or not isinstance(ts, str): + return None + try: + return datetime.datetime.fromisoformat(ts.replace('Z', '+00:00')) + except Exception: + return None + + +def _is_duplicate_within(log_list, matcher, window_sec=DEDUP_WINDOW_SEC): + if not log_list: + return False + now_dt = datetime.datetime.now(datetime.timezone.utc) + for item in reversed(log_list[-20:]): + if not matcher(item): + continue + at_dt = _parse_iso(item.get('at')) + if at_dt and (now_dt - at_dt).total_seconds() <= window_sec: + return True + break + return False def load(): return atomic_json_read(TASKS_FILE, []) @@ -82,6 +124,29 @@ def find_task(tasks, task_id): return next((t for t in tasks if t.get('id') == task_id), None) +def _touch_scheduler(task, reset_retry=False): + """同步调度心跳,避免任务被误判为停滞。""" + sched = task.get('_scheduler') + if not isinstance(sched, dict): + sched = {} + task['_scheduler'] = sched + cur_state = task.get('state', '') + sched.setdefault('maxStateAgeSec', 900) + if not sched.get('stateSince'): + sched['stateSince'] = now_iso() + if sched.get('stateName') != cur_state: + sched['stateName'] = cur_state + sched['stateSince'] = now_iso() + sched['lastProgressAt'] = now_iso() + sched['stallSince'] = None + sched['awaitingEmperorDecision'] = False + sched['decisionPacket'] = None + if reset_retry: + sched['retryCount'] = 0 + sched['escalationLevel'] = 0 + sched['lastEscalatedAt'] = None + + # 旨意标题最低要求 _MIN_TITLE_LEN = 6 _JUNK_TITLES = { @@ -91,7 +156,7 @@ def find_task(tasks, task_id): } def _sanitize_text(raw, max_len=80): - """清洗文本:剥离文件路径、URL、Conversation 元数据、传旨前缀、截断过长内容。""" + """清洗文本:剥离文件路径、URL、Conversation 元数据、传旨前缀。""" t = (raw or '').strip() # 1) 剥离 Conversation info / Conversation 后面的所有内容 t = re.split(r'\n*Conversation\b', t, maxsplit=1)[0].strip() @@ -107,15 +172,15 @@ def _sanitize_text(raw, max_len=80): t = re.sub(r'(message_id|session_id|chat_id|open_id|user_id|tenant_key)\s*[:=]\s*\S+', '', t) # 7) 合并多余空白 t = re.sub(r'\s+', ' ', t).strip() - # 8) 截断过长内容 - if len(t) > max_len: + # 8) 截断过长内容(max_len<=0 表示不截断) + if max_len and max_len > 0 and len(t) > max_len: t = t[:max_len] + '…' return t def _sanitize_title(raw): - """清洗标题(最长 80 字符)。""" - return _sanitize_text(raw, 80) + """清洗标题(不截断,保留长指令全文)。""" + return _sanitize_text(raw, 0) def _sanitize_remark(raw): @@ -219,6 +284,7 @@ def modifier(tasks): t['org'] = STATE_ORG_MAP[new_state] if now_text: t['now'] = now_text + _touch_scheduler(t, reset_retry=True) t['updatedAt'] = now_iso() return tasks atomic_json_update(TASKS_FILE, modifier, []) @@ -229,19 +295,36 @@ def modifier(tasks): def cmd_flow(task_id, from_dept, to_dept, remark): """添加流转记录(原子操作)""" clean_remark = _sanitize_remark(remark) + deduped = [False] def modifier(tasks): t = find_task(tasks, task_id) if not t: log.error(f'任务 {task_id} 不存在') return tasks + flow_log = t.setdefault('flow_log', []) + if _is_duplicate_within( + flow_log, + lambda x: ( + x.get('from') == from_dept + and x.get('to') == to_dept + and x.get('remark') == clean_remark + ), + window_sec=DEDUP_WINDOW_SEC, + ): + deduped[0] = True + return tasks t.setdefault('flow_log', []).append({ "at": now_iso(), "from": from_dept, "to": to_dept, "remark": clean_remark }) + _touch_scheduler(t) t['updatedAt'] = now_iso() return tasks atomic_json_update(TASKS_FILE, modifier, []) save(load()) # trigger refresh - log.info(f'✅ {task_id} 流转记录: {from_dept} → {to_dept}') + if deduped[0]: + log.info(f'ℹ️ {task_id} 流转去重: {from_dept} → {to_dept} (60s窗口)') + else: + log.info(f'✅ {task_id} 流转记录: {from_dept} → {to_dept}') def cmd_done(task_id, output_path='', summary=''): @@ -258,6 +341,7 @@ def modifier(tasks): "at": now_iso(), "from": t.get('org', '执行部门'), "to": "皇上", "remark": f"✅ 完成:{summary or '任务已完成'}" }) + _touch_scheduler(t, reset_retry=True) t['updatedAt'] = now_iso() return tasks atomic_json_update(TASKS_FILE, modifier, []) @@ -274,6 +358,7 @@ def modifier(tasks): return tasks t['state'] = 'Blocked' t['block'] = reason + _touch_scheduler(t, reset_retry=True) t['updatedAt'] = now_iso() return tasks atomic_json_update(TASKS_FILE, modifier, []) @@ -332,6 +417,7 @@ def cmd_progress(task_id, now_text, todos_pipe='', tokens=0, cost=0.0, elapsed=0 done_cnt = [0] total_cnt = [0] + deduped = [False] def modifier(tasks): t = find_task(tasks, task_id) if not t: @@ -357,10 +443,25 @@ def modifier(tasks): log_entry['cost'] = cost if elapsed > 0: log_entry['elapsed'] = elapsed + progress_log = t.setdefault('progress_log', []) + if _is_duplicate_within( + progress_log, + lambda x: ( + x.get('agent') == log_entry.get('agent') + and x.get('text') == log_entry.get('text') + and x.get('state') == log_entry.get('state') + ), + window_sec=DEDUP_WINDOW_SEC, + ): + deduped[0] = True + done_cnt[0] = sum(1 for td in t.get('todos', []) if td.get('status') == 'completed') + total_cnt[0] = len(t.get('todos', [])) + return tasks t.setdefault('progress_log', []).append(log_entry) # 限制 progress_log 大小,防止无限增长 if len(t['progress_log']) > MAX_PROGRESS_LOG: t['progress_log'] = t['progress_log'][-MAX_PROGRESS_LOG:] + _touch_scheduler(t, reset_retry=True) t['updatedAt'] = at done_cnt[0] = sum(1 for td in t.get('todos', []) if td.get('status') == 'completed') total_cnt[0] = len(t.get('todos', [])) @@ -370,7 +471,10 @@ def modifier(tasks): res_info = '' if tokens or cost or elapsed: res_info = f' [res: {tokens}tok/${cost:.4f}/{elapsed}s]' - log.info(f'📡 {task_id} 进展: {clean[:40]}... [{done_cnt[0]}/{total_cnt[0]}]{res_info}') + if deduped[0]: + log.info(f'ℹ️ {task_id} 进展去重: {clean[:40]}... [{done_cnt[0]}/{total_cnt[0]}]{res_info}') + else: + log.info(f'📡 {task_id} 进展: {clean[:40]}... [{done_cnt[0]}/{total_cnt[0]}]{res_info}') def cmd_todo(task_id, todo_id, title, status='not-started', detail=''): """添加或更新子任务 todo(原子操作) @@ -401,6 +505,7 @@ def modifier(tasks): if detail: item['detail'] = detail t['todos'].append(item) + _touch_scheduler(t) t['updatedAt'] = now_iso() result_info[0] = sum(1 for td in t['todos'] if td.get('status') == 'completed') result_info[1] = len(t['todos']) diff --git a/scripts/replay_scheduler_report.py b/scripts/replay_scheduler_report.py new file mode 100644 index 00000000..94142e29 --- /dev/null +++ b/scripts/replay_scheduler_report.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +"""Build replay report for one scheduler task.""" + +import argparse +import datetime as dt +import json +import pathlib + + +ROOT = pathlib.Path(__file__).resolve().parents[1] +DATA = ROOT / "data" + + +def _parse_iso(value): + if not value or not isinstance(value, str): + return None + try: + return dt.datetime.fromisoformat(value.replace("Z", "+00:00")) + except Exception: + return None + + +def _metrics(task): + sched = task.get("_scheduler") or {} + flow = task.get("flow_log") or [] + progress = task.get("progress_log") or [] + diagnostics = task.get("diagnostic_log") or [] + dispatch_attempts = int(sched.get("dispatchAttempts") or 0) + unique_steps = len({ + (item.get("agent", ""), item.get("text", ""), item.get("state", "")) + for item in progress + if item.get("text") + }) + control_actions = sum(1 for item in diagnostics if item.get("eventType", "").startswith("state_commit")) + invalid_control = sum(1 for item in diagnostics if item.get("eventType") == "state_commit_blocked") + wb = sched.get("writeback") or {} + first_output = _parse_iso(wb.get("firstOutputAt")) + committed = _parse_iso(wb.get("lastCommittedAt")) + writeback_lag_sec = None + if first_output: + end = committed or dt.datetime.now(dt.timezone.utc) + writeback_lag_sec = max(0, int((end - first_output).total_seconds())) + return { + "taskId": task.get("id", ""), + "state": task.get("state", ""), + "dispatchAttempts": dispatch_attempts, + "uniqueExecutionSteps": unique_steps, + "dispatchAmplificationRatio": round(dispatch_attempts / unique_steps, 3) if unique_steps else 0.0, + "flowCount": len(flow), + "progressCount": len(progress), + "controlActions": control_actions, + "invalidControlActions": invalid_control, + "invalidControlRatio": round(invalid_control / control_actions, 3) if control_actions else 0.0, + "writebackStatus": wb.get("status", "idle"), + "writebackLagSec": writeback_lag_sec, + } + + +def _find_baseline_row(baseline, task_id): + rows = baseline.get("tasks") if isinstance(baseline, dict) else None + if not isinstance(rows, list): + return None + for row in rows: + if isinstance(row, dict) and row.get("taskId") == task_id: + return row + return None + + +def _render_markdown(task_id, metrics, baseline_row, now_iso): + lines = [] + lines.append(f"# 调度回放报告:{task_id}") + lines.append("") + lines.append(f"- 生成时间: {now_iso}") + lines.append(f"- 当前状态: {metrics['state']}") + lines.append("") + lines.append("## 当前指标") + lines.append("") + lines.append("| 指标 | 值 |") + lines.append("|---|---|") + lines.append(f"| dispatchAttempts | {metrics['dispatchAttempts']} |") + lines.append(f"| uniqueExecutionSteps | {metrics['uniqueExecutionSteps']} |") + lines.append(f"| dispatchAmplificationRatio | {metrics['dispatchAmplificationRatio']} |") + lines.append(f"| flowCount | {metrics['flowCount']} |") + lines.append(f"| progressCount | {metrics['progressCount']} |") + lines.append(f"| invalidControlRatio | {metrics['invalidControlRatio']} |") + lines.append(f"| writebackStatus | {metrics['writebackStatus']} |") + lines.append(f"| writebackLagSec | {metrics['writebackLagSec']} |") + lines.append("") + lines.append("## 与基线对比") + lines.append("") + if not baseline_row: + lines.append("- 未找到该任务基线记录(跳过对比)。") + else: + lines.append("| 指标 | 基线 | 当前 | 差值 |") + lines.append("|---|---:|---:|---:|") + for key in ("dispatchAttempts", "flowCount", "progressCount"): + base = int(baseline_row.get(key) or 0) + cur = int(metrics.get(key) or 0) + lines.append(f"| {key} | {base} | {cur} | {cur - base:+d} |") + lines.append("") + lines.append("## 结论") + lines.append("") + if metrics["dispatchAmplificationRatio"] <= 2: + lines.append("- 调度放大比处于可控区间。") + else: + lines.append("- 调度放大比偏高,建议继续收敛重试/升级触发条件。") + if metrics["invalidControlRatio"] <= 0.3: + lines.append("- 无效控制比处于可接受范围。") + else: + lines.append("- 无效控制比偏高,建议排查 blockedBy 分布。") + if metrics["writebackStatus"] == "idle": + lines.append("- 写回链路闭环完成。") + else: + lines.append("- 写回链路仍在处理中,需继续观察。") + lines.append("") + return "\n".join(lines) + + +def main(): + parser = argparse.ArgumentParser(description="Generate scheduler replay report") + parser.add_argument("--task-id", required=True, help="Task id to replay") + parser.add_argument( + "--baseline", + default=str(DATA / "scheduler_baseline_latest.json"), + help="Baseline snapshot json path", + ) + parser.add_argument( + "--output", + default="", + help="Output markdown path (default to .helloagents/plan//replay_report_.md)", + ) + args = parser.parse_args() + + source = DATA / "tasks_source.json" + tasks = json.loads(source.read_text(encoding="utf-8")) if source.exists() else [] + task = next((item for item in tasks if item.get("id") == args.task_id), None) + if not task: + raise SystemExit(f"task not found: {args.task_id}") + + baseline_path = pathlib.Path(args.baseline) + baseline = {} + if baseline_path.exists(): + baseline = json.loads(baseline_path.read_text(encoding="utf-8")) + + now_iso = dt.datetime.now(dt.timezone.utc).isoformat().replace("+00:00", "Z") + metrics = _metrics(task) + baseline_row = _find_baseline_row(baseline, args.task_id) + content = _render_markdown(args.task_id, metrics, baseline_row, now_iso) + + if args.output: + output = pathlib.Path(args.output) + else: + plan_root = ROOT / ".helloagents" / "plan" + candidates = sorted([p for p in plan_root.iterdir() if p.is_dir()], reverse=True) if plan_root.exists() else [] + target_dir = candidates[0] if candidates else plan_root + output = target_dir / f"replay_report_{args.task_id}.md" + + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(content, encoding="utf-8") + print(f"replay report written: {output}") + + +if __name__ == "__main__": + main() diff --git a/tests/test_scheduler_gate.py b/tests/test_scheduler_gate.py new file mode 100644 index 00000000..4be6c31b --- /dev/null +++ b/tests/test_scheduler_gate.py @@ -0,0 +1,223 @@ +"""Scheduler commit gate tests for dashboard/server.py.""" + +import json +import pathlib +import sys + +# Add project paths +ROOT = pathlib.Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / 'dashboard')) +sys.path.insert(0, str(ROOT / 'scripts')) + +import server as srv + + +def _write_tasks(data_dir, tasks): + (data_dir / 'tasks_source.json').write_text( + json.dumps(tasks, ensure_ascii=False, indent=2), + encoding='utf-8', + ) + + +def _base_task(): + now = srv.now_iso() + return { + 'id': 'JJC-TEST-SCHED-001', + 'title': '测试提交闸门', + 'state': 'Doing', + 'org': '兵部', + 'now': '执行中', + 'block': '无', + 'flow_log': [], + 'progress_log': [], + '_scheduler': { + 'stateVersion': 5, + 'controlState': 'Doing', + 'lease': { + 'stage': 'Doing', + 'role': 'bingbu', + 'ownerRunId': 'run-owner-a', + 'acquiredAt': now, + 'heartbeatAt': now, + 'ttlSec': 86400, + }, + 'writeback': { + 'status': 'idle', + 'retryCount': 0, + 'maxRetry': 2, + }, + }, + } + + +def test_scheduler_commit_blocks_stale_owner(tmp_path): + data_dir = tmp_path / 'data' + data_dir.mkdir() + srv.DATA = data_dir + + task = _base_task() + _write_tasks(data_dir, [task]) + + result = srv.handle_scheduler_commit({ + 'taskId': task['id'], + 'action': 'retry', + 'ownerRunId': 'run-owner-b', + 'expectedVersion': 5, + 'reasonCode': 'test_retry', + }) + + assert result['ok'] is False + assert result['committed'] is False + assert result['blockedBy'] == 'staleOwner' + + tasks = srv.load_tasks() + latest = next(t for t in tasks if t.get('id') == task['id']) + assert latest.get('_scheduler', {}).get('stateVersion') == 5 + + +def test_scheduler_commit_blocks_version_conflict(tmp_path): + data_dir = tmp_path / 'data' + data_dir.mkdir() + srv.DATA = data_dir + + task = _base_task() + _write_tasks(data_dir, [task]) + + result = srv.handle_scheduler_commit({ + 'taskId': task['id'], + 'action': 'retry', + 'ownerRunId': 'run-owner-a', + 'expectedVersion': 3, + 'reasonCode': 'test_retry', + }) + + assert result['ok'] is False + assert result['committed'] is False + assert result['blockedBy'] == 'versionConflict' + + tasks = srv.load_tasks() + latest = next(t for t in tasks if t.get('id') == task['id']) + assert latest.get('_scheduler', {}).get('stateVersion') == 5 + + +def test_action_allowlist_blocks_escalate_in_doing(tmp_path): + data_dir = tmp_path / 'data' + data_dir.mkdir() + srv.DATA = data_dir + + task = _base_task() + task['_scheduler']['lease']['ownerRunId'] = '' + _write_tasks(data_dir, [task]) + + result = srv.handle_scheduler_action(task['id'], 'escalate', '测试禁止') + + assert result['ok'] is False + assert '被拒绝' in (result.get('error') or '') + + +def test_action_allowlist_blocks_escalate_in_waiting_decision(tmp_path): + data_dir = tmp_path / 'data' + data_dir.mkdir() + srv.DATA = data_dir + + task = _base_task() + task['state'] = 'Menxia' + task['org'] = '门下省' + task['_scheduler']['controlState'] = 'WaitingDecision' + task['_scheduler']['lease']['ownerRunId'] = '' + _write_tasks(data_dir, [task]) + + result = srv.handle_scheduler_action(task['id'], 'escalate', '等待裁决时不允许升级') + + assert result['ok'] is False + assert '被拒绝' in (result.get('error') or '') + + +def test_scheduler_scan_single_action_per_tick(tmp_path, monkeypatch): + data_dir = tmp_path / 'data' + data_dir.mkdir() + srv.DATA = data_dir + + task = _base_task() + task['id'] = 'JJC-TEST-SCHED-002' + task['state'] = 'Assigned' + task['org'] = '尚书省' + task['updatedAt'] = '2026-03-13T00:00:00Z' + task['_scheduler']['controlState'] = 'Assigned' + task['_scheduler']['retryCount'] = 0 + task['_scheduler']['maxRetry'] = 1 + task['_scheduler']['lastProgressAt'] = '2026-03-13T00:00:00Z' + task['_scheduler']['stateSince'] = '2026-03-13T00:00:00Z' + task['_scheduler']['lease']['ownerRunId'] = '' + _write_tasks(data_dir, [task]) + + monkeypatch.setattr(srv, 'dispatch_for_state', lambda *args, **kwargs: None) + monkeypatch.setattr(srv, 'wake_agent', lambda *args, **kwargs: {'ok': True}) + + result = srv.handle_scheduler_scan(threshold_sec=30) + + assert result['ok'] is True + assert result['count'] == 1 + assert len(result['actions']) == 1 + assert result['actions'][0]['action'] == 'retry' + + +def test_writeback_retry_scan_only_retries_commit_chain(tmp_path, monkeypatch): + data_dir = tmp_path / 'data' + data_dir.mkdir() + srv.DATA = data_dir + + task = _base_task() + task['id'] = 'JJC-TEST-SCHED-003' + task['state'] = 'Doing' + task['_scheduler']['lease']['ownerRunId'] = '' + task['_scheduler']['writeback']['status'] = 'WritebackPending' + task['_scheduler']['writeback']['retryCount'] = 0 + task['_scheduler']['writeback']['maxRetry'] = 2 + _write_tasks(data_dir, [task]) + + dispatch_calls = [] + writeback_calls = [] + monkeypatch.setattr(srv, 'dispatch_for_state', lambda *args, **kwargs: dispatch_calls.append((args, kwargs))) + monkeypatch.setattr(srv, '_retry_writeback_for_task', lambda task_id, owner_run_id='': writeback_calls.append((task_id, owner_run_id)) or {'ok': True}) + + result = srv.handle_scheduler_scan(threshold_sec=30) + + assert result['ok'] is True + assert result['count'] == 1 + assert result['actions'][0]['action'] == 'writeback_retry' + assert dispatch_calls == [] + assert len(writeback_calls) == 1 + assert writeback_calls[0][0] == 'JJC-TEST-SCHED-003' + + +def test_manual_decision_reassign_has_explicit_recovery_target(tmp_path, monkeypatch): + data_dir = tmp_path / 'data' + data_dir.mkdir() + srv.DATA = data_dir + + task = _base_task() + task['id'] = 'JJC-TEST-SCHED-004' + task['state'] = 'Menxia' + task['org'] = '门下省' + task['_scheduler']['lease']['ownerRunId'] = '' + _write_tasks(data_dir, [task]) + + dispatch_calls = [] + monkeypatch.setattr(srv, 'dispatch_for_state', lambda *args, **kwargs: dispatch_calls.append((args, kwargs))) + + result = srv.handle_scheduler_action( + task['id'], + 'manual_decide', + '皇上要求改派', + expected_version=5, + recovery_target='reassign', + ) + + assert result['ok'] is True + assert result.get('recoveryTarget') == 'reassign' + assert len(dispatch_calls) == 1 + + latest = next(t for t in srv.load_tasks() if t.get('id') == task['id']) + assert latest.get('state') == 'Assigned' + assert latest.get('org') == '尚书省' From 2830d64ed9c233483a4c0e0f7b3caf492a15c660 Mon Sep 17 00:00:00 2001 From: Ming Li Date: Fri, 13 Mar 2026 19:11:38 -0400 Subject: [PATCH 02/27] =?UTF-8?q?feat(frontend):=20=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E8=B0=83=E5=BA=A6=E9=9D=A2=E6=9D=BF=E6=8E=A7=E5=88=B6=E6=80=81?= =?UTF-8?q?=E4=B8=8E=E6=8B=8D=E6=9D=BF=E4=BF=A1=E6=81=AF=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- edict/frontend/src/api.ts | 163 ++++++++++++++++++++ edict/frontend/src/components/TaskModal.tsx | 31 ++++ 2 files changed, 194 insertions(+) diff --git a/edict/frontend/src/api.ts b/edict/frontend/src/api.ts index 7e6c776d..e2b7dd13 100644 --- a/edict/frontend/src/api.ts +++ b/edict/frontend/src/api.ts @@ -39,6 +39,12 @@ export const api = { fetchJ(`${API_BASE}/api/task-activity/${encodeURIComponent(id)}`), schedulerState: (id: string) => fetchJ(`${API_BASE}/api/scheduler-state/${encodeURIComponent(id)}`), + schedulerMetrics: (id?: string) => + fetchJ( + id + ? `${API_BASE}/api/scheduler-metrics/${encodeURIComponent(id)}` + : `${API_BASE}/api/scheduler-metrics` + ), // 技能内容 skillContent: (agentId: string, skillName: string) => @@ -72,6 +78,20 @@ export const api = { postJ(`${API_BASE}/api/scheduler-escalate`, { taskId, reason }), schedulerRollback: (taskId: string, reason: string) => postJ(`${API_BASE}/api/scheduler-rollback`, { taskId, reason }), + schedulerAction: ( + taskId: string, + action: string, + reason: string, + expectedVersion?: number, + recoveryTarget?: 'continue_execution' | 'continue_writeback' | 'reassign' | 'terminate' + ) => + postJ(`${API_BASE}/api/scheduler-action`, { + taskId, + action, + reason, + expectedVersion, + recoveryTarget, + }), refreshMorning: () => postJ(`${API_BASE}/api/morning-brief/refresh`, {}), saveMorningConfig: (config: SubConfig) => @@ -93,6 +113,8 @@ export const api = { createTask: (data: CreateTaskPayload) => postJ(`${API_BASE}/api/create-task`, data), + courtDiscuss: (data: CourtDiscussPayload) => + postJ(`${API_BASE}/api/court-discuss`, data), }; // ── Types ── @@ -336,20 +358,107 @@ export interface TaskActivityData { export interface SchedulerInfo { retryCount?: number; escalationLevel?: number; + stateVersion?: number; lastDispatchStatus?: string; stallThresholdSec?: number; enabled?: boolean; lastProgressAt?: string; lastDispatchAt?: string; lastDispatchAgent?: string; + dispatchAttempts?: number; autoRollback?: boolean; + autoAdvance?: boolean; + stateSince?: string; + maxStateAgeSec?: number; + controlState?: string; + cooldowns?: Record; + lease?: LeaseInfo; + writeback?: WritebackInfo; + lastAction?: { action?: string; reasonCode?: string; at?: string }; + lastCommit?: { action?: string; reasonCode?: string; result?: string; blockedBy?: string; version?: number }; + awaitingEmperorDecision?: boolean; + decisionPacket?: DecisionPacket | null; +} + +export interface LeaseInfo { + stage?: string; + role?: string; + ownerRunId?: string; + acquiredAt?: string; + heartbeatAt?: string; + ttlSec?: number; +} + +export interface WritebackInfo { + status?: string; + retryCount?: number; + maxRetry?: number; + firstOutputAt?: string; + lastCommittedAt?: string; + lastError?: string; +} + +export interface DecisionOption { + id: string; + label: string; + impact: string; +} + +export interface DecisionPacket { + state: string; + question: string; + options: DecisionOption[]; + recommended?: string; + evidence?: string[]; + generatedAt?: string; } export interface SchedulerStateData { ok: boolean; error?: string; + taskId?: string; + state?: string; + org?: string; scheduler?: SchedulerInfo; + controlState?: string; + lease?: LeaseInfo; + lastAction?: { action?: string; reasonCode?: string; at?: string }; + writeback?: WritebackInfo; + decision?: DecisionPacket | null; stalledSec?: number; + stateAgeSec?: number; + stateAgeLimitSec?: number; +} + +export interface SchedulerTaskMetric { + taskId: string; + state: string; + dispatchAttempts: number; + uniqueExecutionSteps: number; + dispatchAmplificationRatio: number; + controlActions: number; + invalidControlActions: number; + invalidControlRatio: number; + writebackLagSec: number | null; + writebackStatus: string; +} + +export interface SchedulerMetricsData { + ok: boolean; + error?: string; + taskId?: string; + metrics?: SchedulerTaskMetric[]; + summary?: { + taskCount: number; + dispatchAttempts: number; + uniqueExecutionSteps: number; + dispatchAmplificationRatio: number; + controlActions: number; + invalidControlActions: number; + invalidControlRatio: number; + avgWritebackLagSec: number | null; + }; + checkedAt?: string; } export interface SkillContentResult { @@ -378,6 +487,60 @@ export interface CreateTaskPayload { params?: Record; } +export interface CourtDiscussPayload { + action: 'start' | 'next' | 'finalize' | 'status'; + topic?: string; + participants?: string[]; + sessionId?: string; + force?: boolean; +} + +export interface CourtDiscussEntry { + round: number; + agentId: string; + agentLabel: string; + reply: string; + at: string; +} + +export interface CourtDiscussFinal { + ready_for_edict: boolean; + clarified_goal: string; + risks: string[]; + questions_to_emperor: string[]; + recommended_edict: string; + recommended_target_dept: string; + recommended_priority: 'low' | 'normal' | 'high' | 'critical'; + raw?: string; +} + +export interface CourtDiscussAssessment { + round: number; + moderatorId: string; + moderatorLabel: string; + recommend_stop: boolean; + reason: string; + question_to_emperor: string; + focus_next_round: string[]; + draft_direction: string; + raw?: string; + at?: string; +} + +export interface CourtDiscussResult extends ActionResult { + sessionId?: string; + status?: 'ongoing' | 'done'; + topic?: string; + participants?: string[]; + rounds?: number; + moderator?: { id: string; label: string }; + assessment?: CourtDiscussAssessment; + suggestedAction?: 'next' | 'finalize'; + discussion?: CourtDiscussEntry[]; + partial?: CourtDiscussEntry[]; + final?: CourtDiscussFinal; +} + export interface RemoteSkillItem { skillName: string; agentId: string; diff --git a/edict/frontend/src/components/TaskModal.tsx b/edict/frontend/src/components/TaskModal.tsx index a5fdb848..0b3dda55 100644 --- a/edict/frontend/src/components/TaskModal.tsx +++ b/edict/frontend/src/components/TaskModal.tsx @@ -224,6 +224,8 @@ export default function TaskModal() { // Scheduler state const sched = schedData?.scheduler; const stalledSec = schedData?.stalledSec || 0; + const stateAgeSec = schedData?.stateAgeSec || 0; + const decision = schedData?.decision || sched?.decisionPacket || null; return (
@@ -296,16 +298,45 @@ export default function TaskModal() {
停滞时长
{fmtStalled(stalledSec)}
+
状态驻留
{fmtStalled(stateAgeSec)}
重试次数
{sched?.retryCount || 0}
升级级别
{!sched?.escalationLevel ? '无' : sched.escalationLevel === 1 ? '门下省' : '尚书省'}
派发状态
{sched?.lastDispatchStatus || 'idle'}
+
控制态
{schedData?.controlState || sched?.controlState || '-'}
+
写回状态
{schedData?.writeback?.status || sched?.writeback?.status || 'idle'}
+ {decision ? ( +
+
🧾 拍板事项
+
{decision.question}
+ {(decision.options || []).map((opt, idx) => ( +
+ {idx + 1}. {opt.label}{decision.recommended === opt.id ? '(推荐)' : ''} +
{opt.impact}
+
+ ))} + {(decision.evidence || []).length > 0 && ( +
+ {(decision.evidence || []).slice(0, 4).map((ev, i) => ( +
- {ev}
+ ))} +
+ )} +
+ ) : ( +
+ 当前无拍板事项(仅在门下省/审查节点停滞时触发) +
+ )} {sched && (
{sched.lastProgressAt && 最近进展 {(sched.lastProgressAt || '').replace('T', ' ').substring(0, 19)}} {sched.lastDispatchAt && 最近派发 {(sched.lastDispatchAt || '').replace('T', ' ').substring(0, 19)}} 自动回滚 {sched.autoRollback === false ? '关闭' : '开启'} {sched.lastDispatchAgent && 目标 {sched.lastDispatchAgent}} + {schedData?.lease?.ownerRunId && 租约 {schedData.lease.ownerRunId}} + {schedData?.lastAction?.reasonCode && 原因码 {schedData.lastAction.reasonCode}} + {schedData?.writeback?.lastError && 写回错误 {schedData.writeback.lastError}}
)}
From 01093ad421b7971930ff8bbad92401f7f416268d Mon Sep 17 00:00:00 2001 From: Ming Li Date: Fri, 13 Mar 2026 19:11:45 -0400 Subject: [PATCH 03/27] =?UTF-8?q?docs:=20=E8=A1=A5=E5=85=85=E8=B0=83?= =?UTF-8?q?=E5=BA=A6=E7=A8=B3=E5=AE=9A=E6=80=A7=E6=94=B9=E9=80=A0=E4=B8=8E?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E9=97=B8=E9=97=A8=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/task-dispatch-architecture.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/task-dispatch-architecture.md b/docs/task-dispatch-architecture.md index e5981e97..ac03048a 100644 --- a/docs/task-dispatch-architecture.md +++ b/docs/task-dispatch-architecture.md @@ -26,6 +26,34 @@ --- +## 🔄 2026-03-13 稳定性改造补充(控制层) + +为解决重复派发、Doing 阶段误升级、执行产出与状态提交脱节等问题,调度层新增以下硬约束: + +1. **单写提交入口(commit gate)** + - 主状态、`_scheduler.controlState`、调度关键字段写入统一走 `commit_state_change()`。 + - 提交时校验 `owner_run_id + state_version + action whitelist`。 + - 版本冲突或 owner 过期仅记录诊断事件,不覆盖已有状态。 + +2. **任务阶段租约锁(lease lock)** + - 维度:`task_id + stage + executor_role`。 + - 字段:`ownerRunId/acquiredAt/heartbeatAt/ttlSec`。 + - 未持锁流程禁止派发与提交;TTL 超时可接管,防止死锁。 + +3. **状态动作白名单** + - `Doing/WritebackPending/WaitingDecision` 禁止自动改派与自动升级。 + - 自动调度在每个 decision tick 内仅允许一个动作生效。 + +4. **写回闭环分层** + - 执行输出与状态提交分离:`ExecutionOutputReady` / `WritebackPending`。 + - 仅 writeback 成功才算状态前进;提交失败进入提交重试链路。 + +5. **事件分层与可观测指标** + - 事件分为 `flow/progress/diagnostic`,并增加 `reason_code`。 + - 核心指标:调度放大比、无效控制比、写回脱节时长。 + +--- + ## 📚 第一部分:业务架构 ### 1.1 帝国制度:分权制衡的设计哲学 From 16f83f5045b6864169ba62525311f76eb24ce4d1 Mon Sep 17 00:00:00 2001 From: Ming Li Date: Fri, 13 Mar 2026 19:11:54 -0400 Subject: [PATCH 04/27] =?UTF-8?q?build(dashboard):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=9E=84=E5=BB=BA=E4=BA=A7=E7=89=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/dist/assets/index-BcLa0Q8H.js | 90 +++++++++++++++++++++++++ dashboard/dist/assets/index-CqMbmm5B.js | 84 ----------------------- dashboard/dist/index.html | 2 +- 3 files changed, 91 insertions(+), 85 deletions(-) create mode 100644 dashboard/dist/assets/index-BcLa0Q8H.js delete mode 100644 dashboard/dist/assets/index-CqMbmm5B.js diff --git a/dashboard/dist/assets/index-BcLa0Q8H.js b/dashboard/dist/assets/index-BcLa0Q8H.js new file mode 100644 index 00000000..874b70f4 --- /dev/null +++ b/dashboard/dist/assets/index-BcLa0Q8H.js @@ -0,0 +1,90 @@ +(function(){const m=document.createElement("link").relList;if(m&&m.supports&&m.supports("modulepreload"))return;for(const w of document.querySelectorAll('link[rel="modulepreload"]'))y(w);new MutationObserver(w=>{for(const I of w)if(I.type==="childList")for(const B of I.addedNodes)B.tagName==="LINK"&&B.rel==="modulepreload"&&y(B)}).observe(document,{childList:!0,subtree:!0});function c(w){const I={};return w.integrity&&(I.integrity=w.integrity),w.referrerPolicy&&(I.referrerPolicy=w.referrerPolicy),w.crossOrigin==="use-credentials"?I.credentials="include":w.crossOrigin==="anonymous"?I.credentials="omit":I.credentials="same-origin",I}function y(w){if(w.ep)return;w.ep=!0;const I=c(w);fetch(w.href,I)}})();function Qi(o){return o&&o.__esModule&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o}var Di={exports:{}},Cr={},Mi={exports:{}},ve={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Hu;function ff(){if(Hu)return ve;Hu=1;var o=Symbol.for("react.element"),m=Symbol.for("react.portal"),c=Symbol.for("react.fragment"),y=Symbol.for("react.strict_mode"),w=Symbol.for("react.profiler"),I=Symbol.for("react.provider"),B=Symbol.for("react.context"),j=Symbol.for("react.forward_ref"),R=Symbol.for("react.suspense"),_=Symbol.for("react.memo"),E=Symbol.for("react.lazy"),f=Symbol.iterator;function N(g){return g===null||typeof g!="object"?null:(g=f&&g[f]||g["@@iterator"],typeof g=="function"?g:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},L=Object.assign,z={};function W(g,P,ce){this.props=g,this.context=P,this.refs=z,this.updater=ce||h}W.prototype.isReactComponent={},W.prototype.setState=function(g,P){if(typeof g!="object"&&typeof g!="function"&&g!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,g,P,"setState")},W.prototype.forceUpdate=function(g){this.updater.enqueueForceUpdate(this,g,"forceUpdate")};function $(){}$.prototype=W.prototype;function F(g,P,ce){this.props=g,this.context=P,this.refs=z,this.updater=ce||h}var ee=F.prototype=new $;ee.constructor=F,L(ee,W.prototype),ee.isPureReactComponent=!0;var T=Array.isArray,fe=Object.prototype.hasOwnProperty,ue={current:null},me={key:!0,ref:!0,__self:!0,__source:!0};function H(g,P,ce){var pe,u={},k=null,b=null;if(P!=null)for(pe in P.ref!==void 0&&(b=P.ref),P.key!==void 0&&(k=""+P.key),P)fe.call(P,pe)&&!me.hasOwnProperty(pe)&&(u[pe]=P[pe]);var oe=arguments.length-2;if(oe===1)u.children=ce;else if(1>>1,P=U[g];if(0>>1;gw(u,V))kw(b,u)?(U[g]=b,U[k]=V,g=k):(U[g]=u,U[pe]=V,g=pe);else if(kw(b,V))U[g]=b,U[k]=V,g=k;else break e}}return te}function w(U,te){var V=U.sortIndex-te.sortIndex;return V!==0?V:U.id-te.id}if(typeof performance=="object"&&typeof performance.now=="function"){var I=performance;o.unstable_now=function(){return I.now()}}else{var B=Date,j=B.now();o.unstable_now=function(){return B.now()-j}}var R=[],_=[],E=1,f=null,N=3,h=!1,L=!1,z=!1,W=typeof setTimeout=="function"?setTimeout:null,$=typeof clearTimeout=="function"?clearTimeout:null,F=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function ee(U){for(var te=c(_);te!==null;){if(te.callback===null)y(_);else if(te.startTime<=U)y(_),te.sortIndex=te.expirationTime,m(R,te);else break;te=c(_)}}function T(U){if(z=!1,ee(U),!L)if(c(R)!==null)L=!0,Ce(fe);else{var te=c(_);te!==null&&ge(T,te.startTime-U)}}function fe(U,te){L=!1,z&&(z=!1,$(H),H=-1),h=!0;var V=N;try{for(ee(te),f=c(R);f!==null&&(!(f.expirationTime>te)||U&&!se());){var g=f.callback;if(typeof g=="function"){f.callback=null,N=f.priorityLevel;var P=g(f.expirationTime<=te);te=o.unstable_now(),typeof P=="function"?f.callback=P:f===c(R)&&y(R),ee(te)}else y(R);f=c(R)}if(f!==null)var ce=!0;else{var pe=c(_);pe!==null&&ge(T,pe.startTime-te),ce=!1}return ce}finally{f=null,N=V,h=!1}}var ue=!1,me=null,H=-1,Te=5,X=-1;function se(){return!(o.unstable_now()-XU||125g?(U.sortIndex=V,m(_,U),c(R)===null&&U===c(_)&&(z?($(H),H=-1):z=!0,ge(T,V-g))):(U.sortIndex=P,m(R,U),L||h||(L=!0,Ce(fe))),U},o.unstable_shouldYield=se,o.unstable_wrapCallback=function(U){var te=N;return function(){var V=N;N=te;try{return U.apply(this,arguments)}finally{N=V}}}})(Oi)),Oi}var Yu;function vf(){return Yu||(Yu=1,$i.exports=hf()),$i.exports}/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Xu;function gf(){if(Xu)return nt;Xu=1;var o=Er(),m=vf();function c(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),R=Object.prototype.hasOwnProperty,_=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,E={},f={};function N(e){return R.call(f,e)?!0:R.call(E,e)?!1:_.test(e)?f[e]=!0:(E[e]=!0,!1)}function h(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function L(e,t,n,r){if(t===null||typeof t>"u"||h(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function z(e,t,n,r,s,i,a){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=s,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=i,this.removeEmptyString=a}var W={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){W[e]=new z(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];W[t]=new z(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){W[e]=new z(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){W[e]=new z(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){W[e]=new z(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){W[e]=new z(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){W[e]=new z(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){W[e]=new z(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){W[e]=new z(e,5,!1,e.toLowerCase(),null,!1,!1)});var $=/[\-:]([a-z])/g;function F(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace($,F);W[t]=new z(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace($,F);W[t]=new z(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace($,F);W[t]=new z(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){W[e]=new z(e,1,!1,e.toLowerCase(),null,!1,!1)}),W.xlinkHref=new z("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){W[e]=new z(e,1,!1,e.toLowerCase(),null,!0,!0)});function ee(e,t,n,r){var s=W.hasOwnProperty(t)?W[t]:null;(s!==null?s.type!==0:r||!(2d||s[a]!==i[d]){var p=` +`+s[a].replace(" at new "," at ");return e.displayName&&p.includes("")&&(p=p.replace("",e.displayName)),p}while(1<=a&&0<=d);break}}}finally{ce=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?P(e):""}function u(e){switch(e.tag){case 5:return P(e.type);case 16:return P("Lazy");case 13:return P("Suspense");case 19:return P("SuspenseList");case 0:case 2:case 15:return e=pe(e.type,!1),e;case 11:return e=pe(e.type.render,!1),e;case 1:return e=pe(e.type,!0),e;default:return""}}function k(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case me:return"Fragment";case ue:return"Portal";case Te:return"Profiler";case H:return"StrictMode";case ie:return"Suspense";case K:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case se:return(e.displayName||"Context")+".Consumer";case X:return(e._context.displayName||"Context")+".Provider";case ye:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case we:return t=e.displayName||null,t!==null?t:k(e.type)||"Memo";case Ce:t=e._payload,e=e._init;try{return k(e(t))}catch{}}return null}function b(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return k(t);case 8:return t===H?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function oe(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function ae(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Ae(e){var t=ae(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var s=n.get,i=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return s.call(this)},set:function(a){r=""+a,i.call(this,a)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(a){r=""+a},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function mn(e){e._valueTracker||(e._valueTracker=Ae(e))}function Yi(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=ae(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function Tr(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Wl(e,t){var n=t.checked;return V({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function Xi(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=oe(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function qi(e,t){t=t.checked,t!=null&&ee(e,"checked",t,!1)}function Ul(e,t){qi(e,t);var n=oe(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Hl(e,t.type,n):t.hasOwnProperty("defaultValue")&&Hl(e,t.type,oe(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Zi(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Hl(e,t,n){(t!=="number"||Tr(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Bn=Array.isArray;function hn(e,t,n,r){if(e=e.options,t){t={};for(var s=0;s"+t.valueOf().toString()+"",t=Lr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Fn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Wn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},hc=["Webkit","ms","Moz","O"];Object.keys(Wn).forEach(function(e){hc.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Wn[t]=Wn[e]})});function lo(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Wn.hasOwnProperty(e)&&Wn[e]?(""+t).trim():t+"px"}function so(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,s=lo(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,s):e[n]=s}}var vc=V({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Kl(e,t){if(t){if(vc[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(c(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(c(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(c(61))}if(t.style!=null&&typeof t.style!="object")throw Error(c(62))}}function Gl(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Yl=null;function Xl(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var ql=null,vn=null,gn=null;function io(e){if(e=cr(e)){if(typeof ql!="function")throw Error(c(280));var t=e.stateNode;t&&(t=Jr(t),ql(e.stateNode,e.type,t))}}function oo(e){vn?gn?gn.push(e):gn=[e]:vn=e}function ao(){if(vn){var e=vn,t=gn;if(gn=vn=null,io(e),t)for(e=0;e>>=0,e===0?32:31-(Ec(e)/zc|0)|0}var Dr=64,Mr=4194304;function Qn(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Ar(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,s=e.suspendedLanes,i=e.pingedLanes,a=n&268435455;if(a!==0){var d=a&~s;d!==0?r=Qn(d):(i&=a,i!==0&&(r=Qn(i)))}else a=n&~s,a!==0?r=Qn(a):i!==0&&(r=Qn(i));if(r===0)return 0;if(t!==0&&t!==r&&(t&s)===0&&(s=r&-r,i=t&-t,s>=i||s===16&&(i&4194240)!==0))return t;if((r&4)!==0&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function Kn(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-mt(t),e[t]=n}function Pc(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=tr),Ao=" ",$o=!1;function Oo(e,t){switch(e){case"keyup":return id.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Bo(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var kn=!1;function ad(e,t){switch(e){case"compositionend":return Bo(t);case"keypress":return t.which!==32?null:($o=!0,Ao);case"textInput":return e=t.data,e===Ao&&$o?null:e;default:return null}}function ud(e,t){if(kn)return e==="compositionend"||!hs&&Oo(e,t)?(e=Ro(),Wr=us=$t=null,kn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Ko(n)}}function Yo(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Yo(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Xo(){for(var e=window,t=Tr();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Tr(e.document)}return t}function ys(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function yd(e){var t=Xo(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Yo(n.ownerDocument.documentElement,n)){if(r!==null&&ys(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var s=n.textContent.length,i=Math.min(r.start,s);r=r.end===void 0?i:Math.min(r.end,s),!e.extend&&i>r&&(s=r,r=i,i=s),s=Go(n,i);var a=Go(n,r);s&&a&&(e.rangeCount!==1||e.anchorNode!==s.node||e.anchorOffset!==s.offset||e.focusNode!==a.node||e.focusOffset!==a.offset)&&(t=t.createRange(),t.setStart(s.node,s.offset),e.removeAllRanges(),i>r?(e.addRange(t),e.extend(a.node,a.offset)):(t.setEnd(a.node,a.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,jn=null,xs=null,sr=null,ks=!1;function qo(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;ks||jn==null||jn!==Tr(r)||(r=jn,"selectionStart"in r&&ys(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),sr&&lr(sr,r)||(sr=r,r=Xr(xs,"onSelect"),0_n||(e.current=Ps[_n],Ps[_n]=null,_n--)}function Ne(e,t){_n++,Ps[_n]=e.current,e.current=t}var Wt={},Ve=Ft(Wt),qe=Ft(!1),nn=Wt;function En(e,t){var n=e.type.contextTypes;if(!n)return Wt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var s={},i;for(i in n)s[i]=t[i];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=s),s}function Ze(e){return e=e.childContextTypes,e!=null}function el(){Ee(qe),Ee(Ve)}function fa(e,t,n){if(Ve.current!==Wt)throw Error(c(168));Ne(Ve,t),Ne(qe,n)}function pa(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var s in r)if(!(s in t))throw Error(c(108,b(e)||"Unknown",s));return V({},n,r)}function tl(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Wt,nn=Ve.current,Ne(Ve,e),Ne(qe,qe.current),!0}function ma(e,t,n){var r=e.stateNode;if(!r)throw Error(c(169));n?(e=pa(e,t,nn),r.__reactInternalMemoizedMergedChildContext=e,Ee(qe),Ee(Ve),Ne(Ve,e)):Ee(qe),Ne(qe,n)}var Et=null,nl=!1,Is=!1;function ha(e){Et===null?Et=[e]:Et.push(e)}function Ld(e){nl=!0,ha(e)}function Ut(){if(!Is&&Et!==null){Is=!0;var e=0,t=Se;try{var n=Et;for(Se=1;e>=a,s-=a,zt=1<<32-mt(t)+s|n<de?(We=le,le=null):We=le.sibling;var je=D(x,le,S[de],O);if(je===null){le===null&&(le=We);break}e&&le&&je.alternate===null&&t(x,le),v=i(je,v,de),re===null?J=je:re.sibling=je,re=je,le=We}if(de===S.length)return n(x,le),ze&&ln(x,de),J;if(le===null){for(;dede?(We=le,le=null):We=le.sibling;var Zt=D(x,le,je.value,O);if(Zt===null){le===null&&(le=We);break}e&&le&&Zt.alternate===null&&t(x,le),v=i(Zt,v,de),re===null?J=Zt:re.sibling=Zt,re=Zt,le=We}if(je.done)return n(x,le),ze&&ln(x,de),J;if(le===null){for(;!je.done;de++,je=S.next())je=A(x,je.value,O),je!==null&&(v=i(je,v,de),re===null?J=je:re.sibling=je,re=je);return ze&&ln(x,de),J}for(le=r(x,le);!je.done;de++,je=S.next())je=Q(le,x,de,je.value,O),je!==null&&(e&&je.alternate!==null&&le.delete(je.key===null?de:je.key),v=i(je,v,de),re===null?J=je:re.sibling=je,re=je);return e&&le.forEach(function(df){return t(x,df)}),ze&&ln(x,de),J}function be(x,v,S,O){if(typeof S=="object"&&S!==null&&S.type===me&&S.key===null&&(S=S.props.children),typeof S=="object"&&S!==null){switch(S.$$typeof){case fe:e:{for(var J=S.key,re=v;re!==null;){if(re.key===J){if(J=S.type,J===me){if(re.tag===7){n(x,re.sibling),v=s(re,S.props.children),v.return=x,x=v;break e}}else if(re.elementType===J||typeof J=="object"&&J!==null&&J.$$typeof===Ce&&ja(J)===re.type){n(x,re.sibling),v=s(re,S.props),v.ref=dr(x,re,S),v.return=x,x=v;break e}n(x,re);break}else t(x,re);re=re.sibling}S.type===me?(v=pn(S.props.children,x.mode,O,S.key),v.return=x,x=v):(O=Ll(S.type,S.key,S.props,null,x.mode,O),O.ref=dr(x,v,S),O.return=x,x=O)}return a(x);case ue:e:{for(re=S.key;v!==null;){if(v.key===re)if(v.tag===4&&v.stateNode.containerInfo===S.containerInfo&&v.stateNode.implementation===S.implementation){n(x,v.sibling),v=s(v,S.children||[]),v.return=x,x=v;break e}else{n(x,v);break}else t(x,v);v=v.sibling}v=Li(S,x.mode,O),v.return=x,x=v}return a(x);case Ce:return re=S._init,be(x,v,re(S._payload),O)}if(Bn(S))return Y(x,v,S,O);if(te(S))return q(x,v,S,O);il(x,S)}return typeof S=="string"&&S!==""||typeof S=="number"?(S=""+S,v!==null&&v.tag===6?(n(x,v.sibling),v=s(v,S),v.return=x,x=v):(n(x,v),v=Ti(S,x.mode,O),v.return=x,x=v),a(x)):n(x,v)}return be}var Rn=Sa(!0),wa=Sa(!1),ol=Ft(null),al=null,Pn=null,Os=null;function Bs(){Os=Pn=al=null}function Fs(e){var t=ol.current;Ee(ol),e._currentValue=t}function Ws(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function In(e,t){al=e,Os=Pn=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(Je=!0),e.firstContext=null)}function ut(e){var t=e._currentValue;if(Os!==e)if(e={context:e,memoizedValue:t,next:null},Pn===null){if(al===null)throw Error(c(308));Pn=e,al.dependencies={lanes:0,firstContext:e}}else Pn=Pn.next=e;return t}var sn=null;function Us(e){sn===null?sn=[e]:sn.push(e)}function Na(e,t,n,r){var s=t.interleaved;return s===null?(n.next=n,Us(t)):(n.next=s.next,s.next=n),t.interleaved=n,Lt(e,r)}function Lt(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var Ht=!1;function Hs(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Ca(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Rt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function Vt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,(xe&2)!==0){var s=r.pending;return s===null?t.next=t:(t.next=s.next,s.next=t),r.pending=t,Lt(e,n)}return s=r.interleaved,s===null?(t.next=t,Us(r)):(t.next=s.next,s.next=t),r.interleaved=t,Lt(e,n)}function ul(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ls(e,n)}}function _a(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var s=null,i=null;if(n=n.firstBaseUpdate,n!==null){do{var a={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};i===null?s=i=a:i=i.next=a,n=n.next}while(n!==null);i===null?s=i=t:i=i.next=t}else s=i=t;n={baseState:r.baseState,firstBaseUpdate:s,lastBaseUpdate:i,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function cl(e,t,n,r){var s=e.updateQueue;Ht=!1;var i=s.firstBaseUpdate,a=s.lastBaseUpdate,d=s.shared.pending;if(d!==null){s.shared.pending=null;var p=d,C=p.next;p.next=null,a===null?i=C:a.next=C,a=p;var M=e.alternate;M!==null&&(M=M.updateQueue,d=M.lastBaseUpdate,d!==a&&(d===null?M.firstBaseUpdate=C:d.next=C,M.lastBaseUpdate=p))}if(i!==null){var A=s.baseState;a=0,M=C=p=null,d=i;do{var D=d.lane,Q=d.eventTime;if((r&D)===D){M!==null&&(M=M.next={eventTime:Q,lane:0,tag:d.tag,payload:d.payload,callback:d.callback,next:null});e:{var Y=e,q=d;switch(D=t,Q=n,q.tag){case 1:if(Y=q.payload,typeof Y=="function"){A=Y.call(Q,A,D);break e}A=Y;break e;case 3:Y.flags=Y.flags&-65537|128;case 0:if(Y=q.payload,D=typeof Y=="function"?Y.call(Q,A,D):Y,D==null)break e;A=V({},A,D);break e;case 2:Ht=!0}}d.callback!==null&&d.lane!==0&&(e.flags|=64,D=s.effects,D===null?s.effects=[d]:D.push(d))}else Q={eventTime:Q,lane:D,tag:d.tag,payload:d.payload,callback:d.callback,next:null},M===null?(C=M=Q,p=A):M=M.next=Q,a|=D;if(d=d.next,d===null){if(d=s.shared.pending,d===null)break;D=d,d=D.next,D.next=null,s.lastBaseUpdate=D,s.shared.pending=null}}while(!0);if(M===null&&(p=A),s.baseState=p,s.firstBaseUpdate=C,s.lastBaseUpdate=M,t=s.shared.interleaved,t!==null){s=t;do a|=s.lane,s=s.next;while(s!==t)}else i===null&&(s.shared.lanes=0);un|=a,e.lanes=a,e.memoizedState=A}}function Ea(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Ys.transition;Ys.transition={};try{e(!1),t()}finally{Se=n,Ys.transition=r}}function Qa(){return ct().memoizedState}function bd(e,t,n){var r=Yt(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},Ka(e))Ga(t,n);else if(n=Na(e,t,n,r),n!==null){var s=Xe();kt(n,e,r,s),Ya(n,t,r)}}function Dd(e,t,n){var r=Yt(e),s={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(Ka(e))Ga(t,s);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var a=t.lastRenderedState,d=i(a,n);if(s.hasEagerState=!0,s.eagerState=d,ht(d,a)){var p=t.interleaved;p===null?(s.next=s,Us(t)):(s.next=p.next,p.next=s),t.interleaved=s;return}}catch{}finally{}n=Na(e,t,s,r),n!==null&&(s=Xe(),kt(n,e,r,s),Ya(n,t,r))}}function Ka(e){var t=e.alternate;return e===Re||t!==null&&t===Re}function Ga(e,t){hr=pl=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Ya(e,t,n){if((n&4194240)!==0){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ls(e,n)}}var vl={readContext:ut,useCallback:Qe,useContext:Qe,useEffect:Qe,useImperativeHandle:Qe,useInsertionEffect:Qe,useLayoutEffect:Qe,useMemo:Qe,useReducer:Qe,useRef:Qe,useState:Qe,useDebugValue:Qe,useDeferredValue:Qe,useTransition:Qe,useMutableSource:Qe,useSyncExternalStore:Qe,useId:Qe,unstable_isNewReconciler:!1},Md={readContext:ut,useCallback:function(e,t){return Nt().memoizedState=[e,t===void 0?null:t],e},useContext:ut,useEffect:$a,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,ml(4194308,4,Fa.bind(null,t,e),n)},useLayoutEffect:function(e,t){return ml(4194308,4,e,t)},useInsertionEffect:function(e,t){return ml(4,2,e,t)},useMemo:function(e,t){var n=Nt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Nt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=bd.bind(null,Re,e),[r.memoizedState,e]},useRef:function(e){var t=Nt();return e={current:e},t.memoizedState=e},useState:Ma,useDebugValue:ni,useDeferredValue:function(e){return Nt().memoizedState=e},useTransition:function(){var e=Ma(!1),t=e[0];return e=Id.bind(null,e[1]),Nt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=Re,s=Nt();if(ze){if(n===void 0)throw Error(c(407));n=n()}else{if(n=t(),Fe===null)throw Error(c(349));(an&30)!==0||Ra(r,t,n)}s.memoizedState=n;var i={value:n,getSnapshot:t};return s.queue=i,$a(Ia.bind(null,r,i,e),[e]),r.flags|=2048,yr(9,Pa.bind(null,r,i,n,t),void 0,null),n},useId:function(){var e=Nt(),t=Fe.identifierPrefix;if(ze){var n=Tt,r=zt;n=(r&~(1<<32-mt(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=vr++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=a.createElement(n,{is:r.is}):(e=a.createElement(n),n==="select"&&(a=e,r.multiple?a.multiple=!0:r.size&&(a.size=r.size))):e=a.createElementNS(e,n),e[St]=t,e[ur]=r,hu(e,t,!1,!1),t.stateNode=e;e:{switch(a=Gl(n,r),n){case"dialog":_e("cancel",e),_e("close",e),s=r;break;case"iframe":case"object":case"embed":_e("load",e),s=r;break;case"video":case"audio":for(s=0;s$n&&(t.flags|=128,r=!0,xr(i,!1),t.lanes=4194304)}else{if(!r)if(e=dl(a),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),xr(i,!0),i.tail===null&&i.tailMode==="hidden"&&!a.alternate&&!ze)return Ke(t),null}else 2*Ie()-i.renderingStartTime>$n&&n!==1073741824&&(t.flags|=128,r=!0,xr(i,!1),t.lanes=4194304);i.isBackwards?(a.sibling=t.child,t.child=a):(n=i.last,n!==null?n.sibling=a:t.child=a,i.last=a)}return i.tail!==null?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=Ie(),t.sibling=null,n=Le.current,Ne(Le,r?n&1|2:n&1),t):(Ke(t),null);case 22:case 23:return _i(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&(t.mode&1)!==0?(it&1073741824)!==0&&(Ke(t),t.subtreeFlags&6&&(t.flags|=8192)):Ke(t),null;case 24:return null;case 25:return null}throw Error(c(156,t.tag))}function Hd(e,t){switch(Ds(t),t.tag){case 1:return Ze(t.type)&&el(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return bn(),Ee(qe),Ee(Ve),Gs(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return Qs(t),null;case 13:if(Ee(Le),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(c(340));Ln()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return Ee(Le),null;case 4:return bn(),null;case 10:return Fs(t.type._context),null;case 22:case 23:return _i(),null;case 24:return null;default:return null}}var kl=!1,Ge=!1,Vd=typeof WeakSet=="function"?WeakSet:Set,G=null;function Mn(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){Pe(e,t,r)}else n.current=null}function mi(e,t,n){try{n()}catch(r){Pe(e,t,r)}}var yu=!1;function Qd(e,t){if(_s=Br,e=Xo(),ys(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var s=r.anchorOffset,i=r.focusNode;r=r.focusOffset;try{n.nodeType,i.nodeType}catch{n=null;break e}var a=0,d=-1,p=-1,C=0,M=0,A=e,D=null;t:for(;;){for(var Q;A!==n||s!==0&&A.nodeType!==3||(d=a+s),A!==i||r!==0&&A.nodeType!==3||(p=a+r),A.nodeType===3&&(a+=A.nodeValue.length),(Q=A.firstChild)!==null;)D=A,A=Q;for(;;){if(A===e)break t;if(D===n&&++C===s&&(d=a),D===i&&++M===r&&(p=a),(Q=A.nextSibling)!==null)break;A=D,D=A.parentNode}A=Q}n=d===-1||p===-1?null:{start:d,end:p}}else n=null}n=n||{start:0,end:0}}else n=null;for(Es={focusedElem:e,selectionRange:n},Br=!1,G=t;G!==null;)if(t=G,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,G=e;else for(;G!==null;){t=G;try{var Y=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(Y!==null){var q=Y.memoizedProps,be=Y.memoizedState,x=t.stateNode,v=x.getSnapshotBeforeUpdate(t.elementType===t.type?q:gt(t.type,q),be);x.__reactInternalSnapshotBeforeUpdate=v}break;case 3:var S=t.stateNode.containerInfo;S.nodeType===1?S.textContent="":S.nodeType===9&&S.documentElement&&S.removeChild(S.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(c(163))}}catch(O){Pe(t,t.return,O)}if(e=t.sibling,e!==null){e.return=t.return,G=e;break}G=t.return}return Y=yu,yu=!1,Y}function kr(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var s=r=r.next;do{if((s.tag&e)===e){var i=s.destroy;s.destroy=void 0,i!==void 0&&mi(t,n,i)}s=s.next}while(s!==r)}}function jl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function hi(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function xu(e){var t=e.alternate;t!==null&&(e.alternate=null,xu(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[St],delete t[ur],delete t[Rs],delete t[zd],delete t[Td])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function ku(e){return e.tag===5||e.tag===3||e.tag===4}function ju(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||ku(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function vi(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Zr));else if(r!==4&&(e=e.child,e!==null))for(vi(e,t,n),e=e.sibling;e!==null;)vi(e,t,n),e=e.sibling}function gi(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(gi(e,t,n),e=e.sibling;e!==null;)gi(e,t,n),e=e.sibling}var Ue=null,yt=!1;function Qt(e,t,n){for(n=n.child;n!==null;)Su(e,t,n),n=n.sibling}function Su(e,t,n){if(jt&&typeof jt.onCommitFiberUnmount=="function")try{jt.onCommitFiberUnmount(br,n)}catch{}switch(n.tag){case 5:Ge||Mn(n,t);case 6:var r=Ue,s=yt;Ue=null,Qt(e,t,n),Ue=r,yt=s,Ue!==null&&(yt?(e=Ue,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Ue.removeChild(n.stateNode));break;case 18:Ue!==null&&(yt?(e=Ue,n=n.stateNode,e.nodeType===8?Ls(e.parentNode,n):e.nodeType===1&&Ls(e,n),Zn(e)):Ls(Ue,n.stateNode));break;case 4:r=Ue,s=yt,Ue=n.stateNode.containerInfo,yt=!0,Qt(e,t,n),Ue=r,yt=s;break;case 0:case 11:case 14:case 15:if(!Ge&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){s=r=r.next;do{var i=s,a=i.destroy;i=i.tag,a!==void 0&&((i&2)!==0||(i&4)!==0)&&mi(n,t,a),s=s.next}while(s!==r)}Qt(e,t,n);break;case 1:if(!Ge&&(Mn(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(d){Pe(n,t,d)}Qt(e,t,n);break;case 21:Qt(e,t,n);break;case 22:n.mode&1?(Ge=(r=Ge)||n.memoizedState!==null,Qt(e,t,n),Ge=r):Qt(e,t,n);break;default:Qt(e,t,n)}}function wu(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Vd),t.forEach(function(r){var s=tf.bind(null,e,r);n.has(r)||(n.add(r),r.then(s,s))})}}function xt(e,t){var n=t.deletions;if(n!==null)for(var r=0;rs&&(s=a),r&=~i}if(r=s,r=Ie()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Gd(r/1960))-r,10e?16:e,Gt===null)var r=!1;else{if(e=Gt,Gt=null,_l=0,(xe&6)!==0)throw Error(c(331));var s=xe;for(xe|=4,G=e.current;G!==null;){var i=G,a=i.child;if((G.flags&16)!==0){var d=i.deletions;if(d!==null){for(var p=0;pIe()-ki?dn(e,0):xi|=n),tt(e,t)}function Mu(e,t){t===0&&((e.mode&1)===0?t=1:(t=Mr,Mr<<=1,(Mr&130023424)===0&&(Mr=4194304)));var n=Xe();e=Lt(e,t),e!==null&&(Kn(e,t,n),tt(e,n))}function ef(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Mu(e,n)}function tf(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,s=e.memoizedState;s!==null&&(n=s.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(c(314))}r!==null&&r.delete(t),Mu(e,n)}var Au;Au=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||qe.current)Je=!0;else{if((e.lanes&n)===0&&(t.flags&128)===0)return Je=!1,Wd(e,t,n);Je=(e.flags&131072)!==0}else Je=!1,ze&&(t.flags&1048576)!==0&&va(t,ll,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;xl(e,t),e=t.pendingProps;var s=En(t,Ve.current);In(t,n),s=qs(null,t,r,e,s,n);var i=Zs();return t.flags|=1,typeof s=="object"&&s!==null&&typeof s.render=="function"&&s.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Ze(r)?(i=!0,tl(t)):i=!1,t.memoizedState=s.state!==null&&s.state!==void 0?s.state:null,Hs(t),s.updater=gl,t.stateNode=s,s._reactInternals=t,li(t,r,e,n),t=ai(null,t,r,!0,i,n)):(t.tag=0,ze&&i&&bs(t),Ye(null,t,s,n),t=t.child),t;case 16:r=t.elementType;e:{switch(xl(e,t),e=t.pendingProps,s=r._init,r=s(r._payload),t.type=r,s=t.tag=rf(r),e=gt(r,e),s){case 0:t=oi(null,t,r,e,n);break e;case 1:t=uu(null,t,r,e,n);break e;case 11:t=lu(null,t,r,e,n);break e;case 14:t=su(null,t,r,gt(r.type,e),n);break e}throw Error(c(306,r,""))}return t;case 0:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:gt(r,s),oi(e,t,r,s,n);case 1:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:gt(r,s),uu(e,t,r,s,n);case 3:e:{if(cu(t),e===null)throw Error(c(387));r=t.pendingProps,i=t.memoizedState,s=i.element,Ca(e,t),cl(t,r,null,n);var a=t.memoizedState;if(r=a.element,i.isDehydrated)if(i={element:r,isDehydrated:!1,cache:a.cache,pendingSuspenseBoundaries:a.pendingSuspenseBoundaries,transitions:a.transitions},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){s=Dn(Error(c(423)),t),t=du(e,t,r,n,s);break e}else if(r!==s){s=Dn(Error(c(424)),t),t=du(e,t,r,n,s);break e}else for(st=Bt(t.stateNode.containerInfo.firstChild),lt=t,ze=!0,vt=null,n=wa(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(Ln(),r===s){t=Pt(e,t,n);break e}Ye(e,t,r,n)}t=t.child}return t;case 5:return za(t),e===null&&As(t),r=t.type,s=t.pendingProps,i=e!==null?e.memoizedProps:null,a=s.children,zs(r,s)?a=null:i!==null&&zs(r,i)&&(t.flags|=32),au(e,t),Ye(e,t,a,n),t.child;case 6:return e===null&&As(t),null;case 13:return fu(e,t,n);case 4:return Vs(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=Rn(t,null,r,n):Ye(e,t,r,n),t.child;case 11:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:gt(r,s),lu(e,t,r,s,n);case 7:return Ye(e,t,t.pendingProps,n),t.child;case 8:return Ye(e,t,t.pendingProps.children,n),t.child;case 12:return Ye(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,s=t.pendingProps,i=t.memoizedProps,a=s.value,Ne(ol,r._currentValue),r._currentValue=a,i!==null)if(ht(i.value,a)){if(i.children===s.children&&!qe.current){t=Pt(e,t,n);break e}}else for(i=t.child,i!==null&&(i.return=t);i!==null;){var d=i.dependencies;if(d!==null){a=i.child;for(var p=d.firstContext;p!==null;){if(p.context===r){if(i.tag===1){p=Rt(-1,n&-n),p.tag=2;var C=i.updateQueue;if(C!==null){C=C.shared;var M=C.pending;M===null?p.next=p:(p.next=M.next,M.next=p),C.pending=p}}i.lanes|=n,p=i.alternate,p!==null&&(p.lanes|=n),Ws(i.return,n,t),d.lanes|=n;break}p=p.next}}else if(i.tag===10)a=i.type===t.type?null:i.child;else if(i.tag===18){if(a=i.return,a===null)throw Error(c(341));a.lanes|=n,d=a.alternate,d!==null&&(d.lanes|=n),Ws(a,n,t),a=i.sibling}else a=i.child;if(a!==null)a.return=i;else for(a=i;a!==null;){if(a===t){a=null;break}if(i=a.sibling,i!==null){i.return=a.return,a=i;break}a=a.return}i=a}Ye(e,t,s.children,n),t=t.child}return t;case 9:return s=t.type,r=t.pendingProps.children,In(t,n),s=ut(s),r=r(s),t.flags|=1,Ye(e,t,r,n),t.child;case 14:return r=t.type,s=gt(r,t.pendingProps),s=gt(r.type,s),su(e,t,r,s,n);case 15:return iu(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:gt(r,s),xl(e,t),t.tag=1,Ze(r)?(e=!0,tl(t)):e=!1,In(t,n),qa(t,r,s),li(t,r,s,n),ai(null,t,r,!0,e,n);case 19:return mu(e,t,n);case 22:return ou(e,t,n)}throw Error(c(156,t.tag))};function $u(e,t){return go(e,t)}function nf(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function ft(e,t,n,r){return new nf(e,t,n,r)}function zi(e){return e=e.prototype,!(!e||!e.isReactComponent)}function rf(e){if(typeof e=="function")return zi(e)?1:0;if(e!=null){if(e=e.$$typeof,e===ye)return 11;if(e===we)return 14}return 2}function qt(e,t){var n=e.alternate;return n===null?(n=ft(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Ll(e,t,n,r,s,i){var a=2;if(r=e,typeof e=="function")zi(e)&&(a=1);else if(typeof e=="string")a=5;else e:switch(e){case me:return pn(n.children,s,i,t);case H:a=8,s|=8;break;case Te:return e=ft(12,n,t,s|2),e.elementType=Te,e.lanes=i,e;case ie:return e=ft(13,n,t,s),e.elementType=ie,e.lanes=i,e;case K:return e=ft(19,n,t,s),e.elementType=K,e.lanes=i,e;case ge:return Rl(n,s,i,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case X:a=10;break e;case se:a=9;break e;case ye:a=11;break e;case we:a=14;break e;case Ce:a=16,r=null;break e}throw Error(c(130,e==null?e:typeof e,""))}return t=ft(a,n,t,s),t.elementType=e,t.type=r,t.lanes=i,t}function pn(e,t,n,r){return e=ft(7,e,r,t),e.lanes=n,e}function Rl(e,t,n,r){return e=ft(22,e,r,t),e.elementType=ge,e.lanes=n,e.stateNode={isHidden:!1},e}function Ti(e,t,n){return e=ft(6,e,null,t),e.lanes=n,e}function Li(e,t,n){return t=ft(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function lf(e,t,n,r,s){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=rs(0),this.expirationTimes=rs(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=rs(0),this.identifierPrefix=r,this.onRecoverableError=s,this.mutableSourceEagerHydrationData=null}function Ri(e,t,n,r,s,i,a,d,p){return e=new lf(e,t,n,d,p),t===1?(t=1,i===!0&&(t|=8)):t=0,i=ft(3,null,null,t),e.current=i,i.stateNode=e,i.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Hs(i),e}function sf(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(o)}catch(m){console.error(m)}}return o(),Ai.exports=gf(),Ai.exports}var Zu;function xf(){if(Zu)return $l;Zu=1;var o=yf();return $l.createRoot=o.createRoot,$l.hydrateRoot=o.hydrateRoot,$l}var kf=xf();const jf=Qi(kf),Sf={},Ju=o=>{let m;const c=new Set,y=(E,f)=>{const N=typeof E=="function"?E(m):E;if(!Object.is(N,m)){const h=m;m=f??(typeof N!="object"||N===null)?N:Object.assign({},m,N),c.forEach(L=>L(m,h))}},w=()=>m,R={setState:y,getState:w,getInitialState:()=>_,subscribe:E=>(c.add(E),()=>c.delete(E)),destroy:()=>{(Sf?"production":void 0)!=="production"&&console.warn("[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."),c.clear()}},_=m=o(y,w,R);return R},wf=o=>o?Ju(o):Ju;var Bi={exports:{}},Fi={},Wi={exports:{}},Ui={};/** + * @license React + * use-sync-external-store-shim.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var ec;function Nf(){if(ec)return Ui;ec=1;var o=Er();function m(f,N){return f===N&&(f!==0||1/f===1/N)||f!==f&&N!==N}var c=typeof Object.is=="function"?Object.is:m,y=o.useState,w=o.useEffect,I=o.useLayoutEffect,B=o.useDebugValue;function j(f,N){var h=N(),L=y({inst:{value:h,getSnapshot:N}}),z=L[0].inst,W=L[1];return I(function(){z.value=h,z.getSnapshot=N,R(z)&&W({inst:z})},[f,h,N]),w(function(){return R(z)&&W({inst:z}),f(function(){R(z)&&W({inst:z})})},[f]),B(h),h}function R(f){var N=f.getSnapshot;f=f.value;try{var h=N();return!c(f,h)}catch{return!0}}function _(f,N){return N()}var E=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?_:j;return Ui.useSyncExternalStore=o.useSyncExternalStore!==void 0?o.useSyncExternalStore:E,Ui}var tc;function Cf(){return tc||(tc=1,Wi.exports=Nf()),Wi.exports}/** + * @license React + * use-sync-external-store-shim/with-selector.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var nc;function _f(){if(nc)return Fi;nc=1;var o=Er(),m=Cf();function c(_,E){return _===E&&(_!==0||1/_===1/E)||_!==_&&E!==E}var y=typeof Object.is=="function"?Object.is:c,w=m.useSyncExternalStore,I=o.useRef,B=o.useEffect,j=o.useMemo,R=o.useDebugValue;return Fi.useSyncExternalStoreWithSelector=function(_,E,f,N,h){var L=I(null);if(L.current===null){var z={hasValue:!1,value:null};L.current=z}else z=L.current;L=j(function(){function $(ue){if(!F){if(F=!0,ee=ue,ue=N(ue),h!==void 0&&z.hasValue){var me=z.value;if(h(me,ue))return T=me}return T=ue}if(me=T,y(ee,ue))return me;var H=N(ue);return h!==void 0&&h(me,H)?(ee=ue,me):(ee=ue,T=H)}var F=!1,ee,T,fe=f===void 0?null:f;return[function(){return $(E())},fe===null?void 0:function(){return $(fe())}]},[E,f,N,h]);var W=w(_,L[0],L[1]);return B(function(){z.hasValue=!0,z.value=W},[W]),R(W),W},Fi}var rc;function Ef(){return rc||(rc=1,Bi.exports=_f()),Bi.exports}var zf=Ef();const Tf=Qi(zf),uc={},{useDebugValue:Lf}=ac,{useSyncExternalStoreWithSelector:Rf}=Tf;let lc=!1;const Pf=o=>o;function If(o,m=Pf,c){(uc?"production":void 0)!=="production"&&c&&!lc&&(console.warn("[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937"),lc=!0);const y=Rf(o.subscribe,o.getState,o.getServerState||o.getInitialState,m,c);return Lf(y),y}const sc=o=>{(uc?"production":void 0)!=="production"&&typeof o!="function"&&console.warn("[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`.");const m=typeof o=="function"?wf(o):o,c=(y,w)=>If(m,y,w);return Object.assign(c,m),c},bf=o=>o?sc(o):sc,ke="";async function pt(o){const m=await fetch(o,{cache:"no-store"});if(!m.ok)throw new Error(String(m.status));return m.json()}async function Me(o,m){return(await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(m)})).json()}const he={liveStatus:()=>pt(`${ke}/api/live-status`),agentConfig:()=>pt(`${ke}/api/agent-config`),modelChangeLog:()=>pt(`${ke}/api/model-change-log`).catch(()=>[]),officialsStats:()=>pt(`${ke}/api/officials-stats`),morningBrief:()=>pt(`${ke}/api/morning-brief`),morningConfig:()=>pt(`${ke}/api/morning-config`),agentsStatus:()=>pt(`${ke}/api/agents-status`),taskActivity:o=>pt(`${ke}/api/task-activity/${encodeURIComponent(o)}`),schedulerState:o=>pt(`${ke}/api/scheduler-state/${encodeURIComponent(o)}`),schedulerMetrics:o=>pt(o?`${ke}/api/scheduler-metrics/${encodeURIComponent(o)}`:`${ke}/api/scheduler-metrics`),skillContent:(o,m)=>pt(`${ke}/api/skill-content/${encodeURIComponent(o)}/${encodeURIComponent(m)}`),setModel:(o,m)=>Me(`${ke}/api/set-model`,{agentId:o,model:m}),agentWake:o=>Me(`${ke}/api/agent-wake`,{agentId:o}),taskAction:(o,m,c)=>Me(`${ke}/api/task-action`,{taskId:o,action:m,reason:c}),reviewAction:(o,m,c)=>Me(`${ke}/api/review-action`,{taskId:o,action:m,comment:c}),advanceState:(o,m)=>Me(`${ke}/api/advance-state`,{taskId:o,comment:m}),archiveTask:(o,m)=>Me(`${ke}/api/archive-task`,{taskId:o,archived:m}),archiveAllDone:()=>Me(`${ke}/api/archive-task`,{archiveAllDone:!0}),schedulerScan:(o=180)=>Me(`${ke}/api/scheduler-scan`,{thresholdSec:o}),schedulerRetry:(o,m)=>Me(`${ke}/api/scheduler-retry`,{taskId:o,reason:m}),schedulerEscalate:(o,m)=>Me(`${ke}/api/scheduler-escalate`,{taskId:o,reason:m}),schedulerRollback:(o,m)=>Me(`${ke}/api/scheduler-rollback`,{taskId:o,reason:m}),schedulerAction:(o,m,c,y,w)=>Me(`${ke}/api/scheduler-action`,{taskId:o,action:m,reason:c,expectedVersion:y,recoveryTarget:w}),refreshMorning:()=>Me(`${ke}/api/morning-brief/refresh`,{}),saveMorningConfig:o=>Me(`${ke}/api/morning-config`,o),addSkill:(o,m,c,y)=>Me(`${ke}/api/add-skill`,{agentId:o,skillName:m,description:c,trigger:y}),addRemoteSkill:(o,m,c,y)=>Me(`${ke}/api/add-remote-skill`,{agentId:o,skillName:m,sourceUrl:c,description:y||""}),remoteSkillsList:()=>pt(`${ke}/api/remote-skills-list`),updateRemoteSkill:(o,m)=>Me(`${ke}/api/update-remote-skill`,{agentId:o,skillName:m}),removeRemoteSkill:(o,m)=>Me(`${ke}/api/remove-remote-skill`,{agentId:o,skillName:m}),createTask:o=>Me(`${ke}/api/create-task`,o),courtDiscuss:o=>Me(`${ke}/api/court-discuss`,o)},cc=[{key:"Inbox",dept:"皇上",icon:"👑",action:"下旨"},{key:"Taizi",dept:"太子",icon:"🤴",action:"分拣"},{key:"Zhongshu",dept:"中书省",icon:"📜",action:"起草"},{key:"Menxia",dept:"门下省",icon:"🔍",action:"审议"},{key:"Assigned",dept:"尚书省",icon:"📮",action:"派发"},{key:"Doing",dept:"六部",icon:"⚙️",action:"执行"},{key:"Review",dept:"尚书省",icon:"🔎",action:"汇总"},{key:"Done",dept:"回奏",icon:"✅",action:"完成"}],Df={Inbox:0,Pending:0,Taizi:1,Zhongshu:2,Menxia:3,Assigned:4,Doing:5,Review:6,Done:7,Blocked:5,Cancelled:5,Next:4},Mf={太子:"#e8a040",中书省:"#a07aff",门下省:"#6a9eff",尚书省:"#6aef9a",礼部:"#f5c842",户部:"#ff9a6a",兵部:"#ff5270",刑部:"#cc4444",工部:"#44aaff",吏部:"#9b59b6",皇上:"#ffd700",回奏:"#2ecc8a"},zr={Inbox:"收件",Pending:"待处理",Taizi:"太子分拣",Zhongshu:"中书起草",Menxia:"门下审议",Assigned:"已派发",Doing:"执行中",Review:"待审查",Done:"已完成",Blocked:"阻塞",Cancelled:"已取消",Next:"待执行"};function Ol(o){return Mf[o]||"#6a9eff"}function Ki(o){const m=o.review_round||0;return o.state==="Menxia"&&m>1?`门下审议(第${m}轮)`:o.state==="Zhongshu"&&m>0?`中书修订(第${m}轮)`:zr[o.state]||o.state}function Jt(o){return/^JJC-/i.test(o.id||"")}function Fl(o){return o.archived||["Done","Cancelled"].includes(o.state)}function Gi(o){const m=Df[o.state]??4;return cc.map((c,y)=>({...c,status:y({liveStatus:null,agentConfig:null,changeLog:[],officialsData:null,agentsStatusData:null,morningBrief:null,subConfig:null,activeTab:"edicts",edictFilter:"active",sessFilter:"all",tplCatFilter:"全部",selectedOfficial:null,modalTaskId:null,countdown:5,toasts:[],setActiveTab:c=>{o({activeTab:c});const y=m();["models","skills","sessions"].includes(c)&&!y.agentConfig&&y.loadAgentConfig(),c==="officials"&&!y.officialsData&&y.loadOfficials(),c==="monitor"&&y.loadAgentsStatus(),c==="morning"&&!y.morningBrief&&y.loadMorning()},setEdictFilter:c=>o({edictFilter:c}),setSessFilter:c=>o({sessFilter:c}),setTplCatFilter:c=>o({tplCatFilter:c}),setSelectedOfficial:c=>o({selectedOfficial:c}),setModalTaskId:c=>o({modalTaskId:c}),setCountdown:c=>o({countdown:c}),toast:(c,y="ok")=>{const w=++Ff;o(I=>({toasts:[...I.toasts,{id:w,msg:c,type:y}]})),setTimeout(()=>{o(I=>({toasts:I.toasts.filter(B=>B.id!==w)}))},3e3)},loadLive:async()=>{try{const c=await he.liveStatus();o({liveStatus:c}),m().officialsData||he.officialsStats().then(w=>o({officialsData:w})).catch(()=>{})}catch{}},loadAgentConfig:async()=>{try{const c=await he.agentConfig(),y=await he.modelChangeLog();o({agentConfig:c,changeLog:y})}catch{}},loadOfficials:async()=>{try{const c=await he.officialsStats();o({officialsData:c})}catch{}},loadAgentsStatus:async()=>{try{const c=await he.agentsStatus();o({agentsStatusData:c})}catch{o({agentsStatusData:null})}},loadMorning:async()=>{try{const[c,y]=await Promise.all([he.morningBrief(),he.morningConfig()]);o({morningBrief:c,subConfig:y})}catch{}},loadSubConfig:async()=>{try{const c=await he.morningConfig();o({subConfig:c})}catch{}},loadAll:async()=>{const c=m();await c.loadLive();const y=c.activeTab;["models","skills"].includes(y)&&await c.loadAgentConfig()}}));let _r=null;function Wf(){_r||(ne.getState().loadAll(),_r=setInterval(()=>{const o=ne.getState(),m=o.countdown-1;m<=0?(o.setCountdown(5),o.loadAll()):o.setCountdown(m)},1e3))}function Uf(){_r&&(clearInterval(_r),_r=null)}function Hf(o){if(!o)return"";try{const m=new Date(o.includes("T")?o:o.replace(" ","T")+"Z");if(isNaN(m.getTime()))return"";const c=Date.now()-m.getTime(),y=Math.floor(c/6e4);if(y<1)return"刚刚";if(y<60)return y+"分钟前";const w=Math.floor(y/60);return w<24?w+"小时前":Math.floor(w/24)+"天前"}catch{return""}}const ic={Doing:0,Review:1,Assigned:2,Menxia:3,Zhongshu:4,Taizi:5,Inbox:6,Blocked:7,Next:8,Done:9,Cancelled:10};function Vf({task:o}){const m=Gi(o);return l.jsx("div",{className:"ec-pipe",children:m.map((c,y)=>l.jsxs("span",{style:{display:"contents"},children:[l.jsxs("div",{className:`ep-node ${c.status}`,children:[l.jsx("div",{className:"ep-icon",children:c.icon}),l.jsx("div",{className:"ep-name",children:c.dept})]}),y$.setModalTaskId),c=ne($=>$.toast),y=ne($=>$.loadAll),w=o.heartbeat||{status:"unknown",label:"⚪"},I="st-"+(o.state||""),B="dt-"+(o.org||"").replace(/\s/g,""),j=cc.find(($,F)=>Gi(o)[F].status==="active"),R=o.todos||[],_=R.filter($=>$.status==="completed").length,E=R.length,f=!["Done","Blocked","Cancelled"].includes(o.state),N=["Blocked","Cancelled"].includes(o.state),h=Fl(o),L=o.block&&o.block!=="无"&&o.block!=="-",z=async($,F)=>{if(F.stopPropagation(),$==="stop"||$==="cancel"){const ee=prompt($==="stop"?"请输入叫停原因:":"请输入取消原因:");if(ee===null)return;try{const T=await he.taskAction(o.id,$,ee);T.ok?(c(T.message||"操作成功"),y()):c(T.error||"操作失败","err")}catch{c("服务器连接失败","err")}}else if($==="resume")try{const ee=await he.taskAction(o.id,"resume","恢复执行");ee.ok?(c(ee.message||"已恢复"),y()):c(ee.error||"操作失败","err")}catch{c("服务器连接失败","err")}},W=async $=>{$.stopPropagation();try{const F=await he.archiveTask(o.id,!o.archived);F.ok?(c(F.message||"操作成功"),y()):c(F.error||"操作失败","err")}catch{c("服务器连接失败","err")}};return l.jsxs("div",{className:`edict-card${h?" archived":""}`,onClick:()=>m(o.id),children:[l.jsx(Vf,{task:o}),l.jsx("div",{className:"ec-id",children:o.id}),l.jsx("div",{className:"ec-title",children:o.title||"(无标题)"}),l.jsxs("div",{className:"ec-meta",children:[l.jsx("span",{className:`tag ${I}`,children:Ki(o)}),o.org&&l.jsx("span",{className:`tag ${B}`,children:o.org}),j&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["当前: ",l.jsxs("b",{style:{color:Ol(j.dept)},children:[j.dept," · ",j.action]})]})]}),o.now&&o.now!=="-"&&l.jsx("div",{style:{fontSize:11,color:"var(--muted)",lineHeight:1.5,marginBottom:6},children:o.now.substring(0,80)}),(o.review_round||0)>0&&l.jsxs("div",{style:{fontSize:11,marginBottom:6},children:[Array.from({length:o.review_round||0},($,F)=>l.jsx("span",{style:{display:"inline-block",width:14,height:14,borderRadius:"50%",background:F<(o.review_round||0)-1?"#1a3a6a22":"var(--acc)22",border:`1px solid ${F<(o.review_round||0)-1?"#2a4a8a":"var(--acc)"}`,fontSize:9,textAlign:"center",lineHeight:"13px",marginRight:2,color:F<(o.review_round||0)-1?"#4a6aaa":"var(--acc)"},children:F+1},F)),l.jsxs("span",{style:{color:"var(--muted)",fontSize:10},children:["第 ",o.review_round," 轮磋商"]})]}),E>0&&l.jsxs("div",{className:"ec-todo-bar",children:[l.jsxs("span",{children:["📋 ",_,"/",E]}),l.jsx("div",{className:"ec-todo-track",children:l.jsx("div",{className:"ec-todo-fill",style:{width:`${Math.round(_/E*100)}%`}})}),l.jsx("span",{children:_===E?"✅ 全部完成":"🔄 进行中"})]}),l.jsxs("div",{className:"ec-footer",children:[l.jsx("span",{className:`hb ${w.status}`,children:w.label}),L&&l.jsxs("span",{className:"tag",style:{borderColor:"#ff527044",color:"var(--danger)",background:"#200a10"},children:["🚫 ",o.block]}),o.eta&&o.eta!=="-"&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["📅 ",o.eta]})]}),l.jsxs("div",{className:"ec-actions",onClick:$=>$.stopPropagation(),children:[f&&l.jsxs(l.Fragment,{children:[l.jsx("button",{className:"mini-act",onClick:$=>z("stop",$),children:"⏸ 叫停"}),l.jsx("button",{className:"mini-act danger",onClick:$=>z("cancel",$),children:"🚫 取消"})]}),N&&l.jsx("button",{className:"mini-act",onClick:$=>z("resume",$),children:"▶ 恢复"}),h&&!o.archived&&l.jsx("button",{className:"mini-act",onClick:W,children:"📦 归档"}),o.archived&&l.jsx("button",{className:"mini-act",onClick:W,children:"📤 取消归档"})]})]})}function Kf(){const o=ne(h=>h.liveStatus),m=ne(h=>h.edictFilter),c=ne(h=>h.setEdictFilter),y=ne(h=>h.toast),w=ne(h=>h.loadAll),B=((o==null?void 0:o.tasks)||[]).filter(Jt),j=B.filter(h=>!Fl(h)),R=B.filter(h=>Fl(h));let _;m==="active"?_=j:m==="archived"?_=R:_=B,_.sort((h,L)=>(ic[h.state]??9)-(ic[L.state]??9));const E=B.filter(h=>!h.archived&&["Done","Cancelled"].includes(h.state)),f=async()=>{if(confirm("将所有已完成/已取消的旨意移入归档?"))try{const h=await he.archiveAllDone();h.ok?(y(`📦 ${h.count||0} 道旨意已归档`),w()):y(h.error||"批量归档失败","err")}catch{y("服务器连接失败","err")}},N=async()=>{try{const h=await he.schedulerScan();h.ok?y(`🧭 太子巡检完成:${h.count||0} 个动作`):y(h.error||"巡检失败","err"),w()}catch{y("服务器连接失败","err")}};return l.jsxs("div",{children:[l.jsxs("div",{className:"archive-bar",children:[l.jsx("span",{className:"ab-label",children:"筛选:"}),["active","archived","all"].map(h=>l.jsx("button",{className:`ab-btn ${m===h?"active":""}`,onClick:()=>c(h),children:h==="active"?"活跃":h==="archived"?"归档":"全部"},h)),E.length>0&&l.jsx("button",{className:"ab-btn",onClick:f,children:"📦 一键归档"}),l.jsxs("span",{className:"ab-count",children:["活跃 ",j.length," · 归档 ",R.length," · 共 ",B.length]}),l.jsx("button",{className:"ab-scan",onClick:N,children:"🧭 太子巡检"})]}),l.jsx("div",{className:"edict-grid",children:_.length===0?l.jsxs("div",{className:"empty",style:{gridColumn:"1/-1"},children:["暂无旨意",l.jsx("br",{}),l.jsx("small",{style:{fontSize:11,marginTop:6,display:"block",color:"var(--muted)"},children:"通过飞书向太子发送任务,太子分拣后转中书省处理"})]}):_.map(h=>l.jsx(Qf,{task:h},h.id))})]})}function Gf(){var ee;const o=ne(T=>T.liveStatus),m=ne(T=>T.agentsStatusData),c=ne(T=>T.officialsData),y=ne(T=>T.loadAgentsStatus),w=ne(T=>T.setModalTaskId),I=ne(T=>T.toast);Z.useEffect(()=>{y()},[y]);const j=((o==null?void 0:o.tasks)||[]).filter(T=>Jt(T)&&T.state!=="Done"&&T.state!=="Next"),R={};c!=null&&c.officials&&c.officials.forEach(T=>{R[T.id]=T});const _=async T=>{try{const fe=await he.agentWake(T);I(fe.message||"唤醒指令已发出"),setTimeout(()=>y(),3e4)}catch{I("唤醒失败","err")}},E=async()=>{if(!m)return;const T=m.agents.filter(fe=>fe.id!=="main"&&fe.status!=="running"&&fe.status!=="unconfigured");if(!T.length){I("所有 Agent 均已在线");return}I(`正在唤醒 ${T.length} 个 Agent...`);for(const fe of T)try{await he.agentWake(fe.id)}catch{}I(`${T.length} 个唤醒指令已发出,30秒后刷新状态`),setTimeout(()=>y(),3e4)},f=m,N=((ee=f==null?void 0:f.agents)==null?void 0:ee.filter(T=>T.id!=="main"))||[],h=N.filter(T=>T.status==="running").length,L=N.filter(T=>T.status==="idle").length,z=N.filter(T=>T.status==="offline").length,W=N.filter(T=>T.status==="unconfigured").length,$=f==null?void 0:f.gateway,F=$!=null&&$.probe?"ok":$!=null&&$.alive?"warn":"err";return l.jsxs("div",{children:[f&&f.ok&&l.jsxs("div",{className:"as-panel",children:[l.jsxs("div",{className:"as-header",children:[l.jsx("span",{className:"as-title",children:"🔌 Agent 在线状态"}),l.jsxs("span",{className:`as-gw ${F}`,children:["Gateway: ",($==null?void 0:$.status)||"未知"]}),l.jsx("button",{className:"btn-refresh",onClick:()=>y(),style:{marginLeft:8},children:"🔄 刷新"}),z+W>0&&l.jsx("button",{className:"btn-refresh",onClick:E,style:{marginLeft:4,borderColor:"var(--warn)",color:"var(--warn)"},children:"⚡ 全部唤醒"})]}),l.jsx("div",{className:"as-grid",children:N.map(T=>{const fe=T.status!=="running"&&T.status!=="unconfigured"&&($==null?void 0:$.alive);return l.jsxs("div",{className:"as-card",title:`${T.role} · ${T.statusLabel}`,children:[l.jsx("div",{className:`as-dot ${T.status}`}),l.jsx("div",{style:{fontSize:22},children:T.emoji}),l.jsx("div",{style:{fontSize:12,fontWeight:700},children:T.label}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:T.role}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:T.statusLabel}),T.lastActive?l.jsxs("div",{style:{fontSize:10,color:"var(--muted)"},children:["⏰ ",T.lastActive]}):l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"无活动记录"}),fe&&l.jsx("button",{className:"as-wake-btn",onClick:ue=>{ue.stopPropagation(),_(T.id)},children:"⚡ 唤醒"})]},T.id)})}),l.jsxs("div",{className:"as-summary",children:[l.jsxs("span",{children:[l.jsx("span",{className:"as-dot running",style:{position:"static",width:8,height:8}})," ",h," 运行中"]}),l.jsxs("span",{children:[l.jsx("span",{className:"as-dot idle",style:{position:"static",width:8,height:8}})," ",L," 待命"]}),z>0&&l.jsxs("span",{children:[l.jsx("span",{className:"as-dot offline",style:{position:"static",width:8,height:8}})," ",z," 离线"]}),W>0&&l.jsxs("span",{children:[l.jsx("span",{className:"as-dot unconfigured",style:{position:"static",width:8,height:8}})," ",W," 未配置"]}),l.jsxs("span",{style:{marginLeft:"auto",fontSize:10,color:"var(--muted)"},children:["检测于 ",(f.checkedAt||"").substring(11,19)]})]})]}),l.jsx("div",{className:"duty-grid",children:$f.map(T=>{const fe=j.filter(ie=>ie.org===T.label),ue=fe.some(ie=>ie.state==="Doing"),me=fe.some(ie=>ie.state==="Blocked"),H=R[T.id],Te=(H==null?void 0:H.heartbeat)||{status:"idle"},X=me?"blocked":ue?"busy":Te.status==="active"?"active":"idle",se=me?"⚠️ 阻塞":ue?"⚙️ 执行中":Te.status==="active"?"🟢 活跃":"⚪ 候命",ye=me?"blocked-card":ue?"active-card":"";return l.jsxs("div",{className:`duty-card ${ye}`,children:[l.jsxs("div",{className:"dc-hdr",children:[l.jsx("span",{className:"dc-emoji",children:T.emoji}),l.jsxs("div",{className:"dc-info",children:[l.jsx("div",{className:"dc-name",children:T.label}),l.jsxs("div",{className:"dc-role",children:[T.role," · ",T.rank]})]}),l.jsxs("div",{className:"dc-status",children:[l.jsx("span",{className:`dc-dot ${X}`}),l.jsx("span",{children:se})]})]}),l.jsx("div",{className:"dc-body",children:fe.length>0?fe.map(ie=>l.jsxs("div",{className:"dc-task",onClick:()=>w(ie.id),children:[l.jsx("div",{className:"dc-task-id",children:ie.id}),l.jsx("div",{className:"dc-task-title",children:ie.title||"(无标题)"}),ie.now&&ie.now!=="-"&&l.jsx("div",{className:"dc-task-now",children:ie.now.substring(0,70)}),l.jsxs("div",{className:"dc-task-meta",children:[l.jsx("span",{className:`tag st-${ie.state}`,children:Ki(ie)}),ie.block&&ie.block!=="无"&&l.jsxs("span",{className:"tag",style:{borderColor:"#ff527044",color:"var(--danger)"},children:["🚫",ie.block]})]})]},ie.id)):l.jsxs("div",{className:"dc-idle",children:[l.jsx("span",{style:{fontSize:20},children:"🪭"}),l.jsx("span",{children:"候命中"})]})}),l.jsxs("div",{className:"dc-footer",children:[l.jsxs("span",{className:"dc-model",children:["🤖 ",(H==null?void 0:H.model_short)||"待配置"]}),(H==null?void 0:H.last_active)&&l.jsxs("span",{className:"dc-la",children:["⏰ ",H.last_active]})]})]},T.id)})})]})}const Yf=["🥇","🥈","🥉"];function Xf(){var f;const o=ne(N=>N.officialsData),m=ne(N=>N.selectedOfficial),c=ne(N=>N.setSelectedOfficial),y=ne(N=>N.loadOfficials),w=ne(N=>N.setModalTaskId);if(Z.useEffect(()=>{y()},[y]),!(o!=null&&o.officials))return l.jsx("div",{className:"empty",children:"⚠️ 请确保本地服务器已启动"});const I=o.officials,B=o.totals||{tasks_done:0,cost_cny:0},j=Math.max(...I.map(N=>N.tokens_in+N.tokens_out+N.cache_read+N.cache_write),1),R=I.filter(N=>{var h;return((h=N.heartbeat)==null?void 0:h.status)==="active"}),_=I.find(N=>{var h;return N.id===(m||((h=I[0])==null?void 0:h.id))}),E=(_==null?void 0:_.id)||((f=I[0])==null?void 0:f.id);return l.jsxs("div",{children:[R.length>0&&l.jsxs("div",{className:"off-activity",children:[l.jsx("span",{children:"🟢 当前活跃:"}),R.map(N=>l.jsxs("span",{style:{fontSize:12},children:[N.emoji," ",N.role]},N.id)),l.jsx("span",{style:{color:"var(--muted)",fontSize:11,marginLeft:"auto"},children:"其余官员待命"})]}),l.jsxs("div",{className:"off-kpi",children:[l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{color:"var(--acc)"},children:I.length}),l.jsx("div",{className:"kpi-l",children:"在职官员"})]}),l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{color:"#f5c842"},children:B.tasks_done||0}),l.jsx("div",{className:"kpi-l",children:"累计完成旨意"})]}),l.jsxs("div",{className:"kpi",children:[l.jsxs("div",{className:"kpi-v",style:{color:(B.cost_cny||0)>20?"var(--warn)":"var(--ok)"},children:["¥",B.cost_cny||0]}),l.jsx("div",{className:"kpi-l",children:"累计费用(含缓存)"})]}),l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{fontSize:16,paddingTop:4},children:o.top_official||"—"}),l.jsx("div",{className:"kpi-l",children:"功绩最高"})]})]}),l.jsxs("div",{className:"off-layout",children:[l.jsxs("div",{className:"off-ranklist",children:[l.jsx("div",{className:"orl-hdr",children:"功绩排行"}),I.map(N=>{const h=N.heartbeat||{status:"idle"};return l.jsxs("div",{className:`orl-item${E===N.id?" selected":""}`,onClick:()=>c(N.id),children:[l.jsx("span",{style:{minWidth:24,textAlign:"center"},children:N.merit_rank<=3?Yf[N.merit_rank-1]:"#"+N.merit_rank}),l.jsx("span",{children:N.emoji}),l.jsxs("span",{style:{flex:1},children:[l.jsx("div",{style:{fontSize:12,fontWeight:700},children:N.role}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:N.label})]}),l.jsxs("span",{style:{fontSize:11},children:[N.merit_score,"分"]}),l.jsx("span",{className:`dc-dot ${h.status}`,style:{width:8,height:8}})]},N.id)})]}),l.jsx("div",{className:"off-detail",children:_?l.jsx(qf,{official:_,maxTk:j,onOpenTask:w}):l.jsx("div",{className:"empty",children:"选择左侧官员查看详情"})})]})]})}function qf({official:o,maxTk:m,onOpenTask:c}){const y=o.heartbeat||{status:"idle",label:"⚪ 待命"},w=o.tokens_in+o.tokens_out+o.cache_read+o.cache_write,I=o.participated_edicts||[],B=[{l:"输入",v:o.tokens_in,color:"#6a9eff"},{l:"输出",v:o.tokens_out,color:"#a07aff"},{l:"缓存读",v:o.cache_read,color:"#2ecc8a"},{l:"缓存写",v:o.cache_write,color:"#f5c842"}];return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:16,alignItems:"center",marginBottom:20},children:[l.jsx("div",{style:{fontSize:40},children:o.emoji}),l.jsxs("div",{style:{flex:1},children:[l.jsx("div",{style:{fontSize:18,fontWeight:800},children:o.role}),l.jsxs("div",{style:{fontSize:12,color:"var(--muted)"},children:[o.label," · ",l.jsx("span",{style:{color:"var(--acc)"},children:o.model_short||o.model})]}),l.jsxs("div",{style:{fontSize:11,color:"var(--muted)",marginTop:2},children:["🏅 ",o.rank," · 功绩分 ",o.merit_score]})]}),l.jsxs("div",{style:{textAlign:"right"},children:[l.jsx("div",{className:`hb ${y.status}`,style:{marginBottom:4},children:y.label}),o.last_active&&l.jsxs("div",{style:{fontSize:10,color:"var(--muted)"},children:["活跃 ",o.last_active]}),l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",marginTop:2},children:[o.sessions," 个会话 · ",o.messages," 条消息"]})]})]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"功绩统计"}),l.jsxs("div",{style:{display:"flex",gap:16},children:[l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--ok)"},children:o.tasks_done}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"完成旨意"})]}),l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--warn)"},children:o.tasks_active}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"执行中"})]}),l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--acc)"},children:o.flow_participations}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"流转参与"})]})]})]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"Token 消耗"}),B.map(j=>l.jsxs("div",{style:{marginBottom:6},children:[l.jsxs("div",{style:{display:"flex",justifyContent:"space-between",fontSize:11,marginBottom:2},children:[l.jsx("span",{style:{color:"var(--muted)"},children:j.l}),l.jsx("span",{children:j.v.toLocaleString()})]}),l.jsx("div",{style:{height:6,background:"#0e1320",borderRadius:3},children:l.jsx("div",{style:{height:"100%",width:`${m>0?Math.round(j.v/m*100):0}%`,background:j.color,borderRadius:3}})})]},j.l))]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"累计费用"}),l.jsxs("div",{style:{display:"flex",gap:10},children:[l.jsxs("span",{style:{fontSize:12,color:o.cost_cny>10?"var(--danger)":o.cost_cny>3?"var(--warn)":"var(--ok)"},children:[l.jsxs("b",{children:["¥",o.cost_cny]})," 人民币"]}),l.jsxs("span",{style:{fontSize:12},children:[l.jsxs("b",{children:["$",o.cost_usd]})," 美元"]}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["总计 ",w.toLocaleString()," tokens"]})]})]}),l.jsxs("div",{children:[l.jsxs("div",{className:"sec-title",children:["参与旨意(",I.length," 道)"]}),I.length===0?l.jsx("div",{style:{fontSize:12,color:"var(--muted)",padding:"8px 0"},children:"暂无旨意记录"}):l.jsx("div",{style:{display:"flex",flexDirection:"column",gap:4},children:I.map(j=>l.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center",padding:"6px 8px",borderRadius:6,cursor:"pointer",border:"1px solid var(--line)"},onClick:()=>c(j.id),children:[l.jsx("span",{style:{fontSize:10,color:"var(--acc)",fontWeight:700},children:j.id}),l.jsx("span",{style:{flex:1,fontSize:12},children:j.title.substring(0,35)}),l.jsx("span",{className:`tag st-${j.state}`,style:{fontSize:10},children:zr[j.state]||j.state})]},j.id))})]})]})}const Zf=[{id:"anthropic/claude-sonnet-4-6",l:"Claude Sonnet 4.6",p:"Anthropic"},{id:"anthropic/claude-opus-4-5",l:"Claude Opus 4.5",p:"Anthropic"},{id:"anthropic/claude-haiku-3-5",l:"Claude Haiku 3.5",p:"Anthropic"},{id:"openai/gpt-4o",l:"GPT-4o",p:"OpenAI"},{id:"openai/gpt-4o-mini",l:"GPT-4o Mini",p:"OpenAI"},{id:"google/gemini-2.5-pro",l:"Gemini 2.5 Pro",p:"Google"},{id:"copilot/claude-sonnet-4",l:"Claude Sonnet 4",p:"Copilot"},{id:"copilot/claude-opus-4.5",l:"Claude Opus 4.5",p:"Copilot"},{id:"copilot/gpt-4o",l:"GPT-4o",p:"Copilot"},{id:"copilot/gemini-2.5-pro",l:"Gemini 2.5 Pro",p:"Copilot"}];function Jf(){var N;const o=ne(h=>h.agentConfig),m=ne(h=>h.changeLog),c=ne(h=>h.loadAgentConfig),y=ne(h=>h.toast),[w,I]=Z.useState({}),[B,j]=Z.useState({});if(Z.useEffect(()=>{c()},[c]),Z.useEffect(()=>{if(o!=null&&o.agents){const h={};o.agents.forEach(L=>{h[L.id]=L.model}),I(h)}},[o]),!(o!=null&&o.agents))return l.jsx("div",{className:"empty",style:{gridColumn:"1/-1"},children:"⚠️ 请先启动本地服务器"});const R=(N=o.knownModels)!=null&&N.length?o.knownModels.map(h=>({id:h.id,l:h.label,p:h.provider})):Zf,_=(h,L)=>{I(z=>({...z,[h]:L}))},E=h=>{const L=o.agents.find(z=>z.id===h);L&&I(z=>({...z,[h]:L.model}))},f=async h=>{const L=w[h];if(L){j(z=>({...z,[h]:{cls:"pending",text:"⟳ 提交中…"}}));try{const z=await he.setModel(h,L);z.ok?(j(W=>({...W,[h]:{cls:"ok",text:"✅ 已提交,Gateway 重启中(约5秒)"}})),y(h+" 模型已更改","ok"),setTimeout(()=>c(),5500)):j(W=>({...W,[h]:{cls:"err",text:"❌ "+(z.error||"错误")}}))}catch{j(z=>({...z,[h]:{cls:"err",text:"❌ 无法连接服务器"}}))}}};return l.jsxs("div",{children:[l.jsx("div",{className:"model-grid",children:o.agents.map(h=>{const L=w[h.id]||h.model,z=L!==h.model,W=B[h.id];return l.jsxs("div",{className:"mc-card",children:[l.jsxs("div",{className:"mc-top",children:[l.jsx("span",{className:"mc-emoji",children:h.emoji||"🏛️"}),l.jsxs("div",{children:[l.jsxs("div",{className:"mc-name",children:[h.label," ",l.jsx("span",{style:{fontSize:11,color:"var(--muted)"},children:h.id})]}),l.jsx("div",{className:"mc-role",children:h.role})]})]}),l.jsxs("div",{className:"mc-cur",children:["当前: ",l.jsx("b",{children:h.model})]}),l.jsx("select",{className:"msel",value:L,onChange:$=>_(h.id,$.target.value),children:R.map($=>l.jsxs("option",{value:$.id,children:[$.l," (",$.p,")"]},$.id))}),l.jsxs("div",{className:"mc-btns",children:[l.jsx("button",{className:"btn btn-p",disabled:!z,onClick:()=>f(h.id),children:"应用"}),l.jsx("button",{className:"btn btn-g",onClick:()=>E(h.id),children:"重置"})]}),W&&l.jsx("div",{className:`mc-st ${W.cls}`,children:W.text})]},h.id)})}),l.jsxs("div",{style:{marginTop:24},children:[l.jsx("div",{className:"sec-title",children:"变更日志"}),l.jsx("div",{className:"cl-list",children:m!=null&&m.length?[...m].reverse().slice(0,15).map((h,L)=>l.jsxs("div",{className:"cl-row",children:[l.jsx("span",{className:"cl-t",children:(h.at||"").substring(0,16).replace("T"," ")}),l.jsx("span",{className:"cl-a",children:h.agentId}),l.jsxs("span",{className:"cl-c",children:[l.jsx("b",{children:h.oldModel})," → ",l.jsx("b",{children:h.newModel}),h.rolledBack&&l.jsx("span",{style:{color:"var(--danger)",fontSize:10,border:"1px solid #ff527044",padding:"1px 5px",borderRadius:3,marginLeft:4},children:"⚠ 已回滚"})]})]},L)):l.jsx("div",{style:{fontSize:12,color:"var(--muted)",padding:"8px 0"},children:"暂无变更"})})]})]})}const ep=[{label:"obra/superpowers",emoji:"⚡",stars:"66.9k",desc:"完整开发工作流技能集",skills:[{name:"brainstorming",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/brainstorming/SKILL.md"},{name:"test-driven-development",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/test-driven-development/SKILL.md"},{name:"systematic-debugging",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/systematic-debugging/SKILL.md"},{name:"subagent-driven-development",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/subagent-driven-development/SKILL.md"},{name:"writing-plans",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/writing-plans/SKILL.md"},{name:"executing-plans",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/executing-plans/SKILL.md"},{name:"requesting-code-review",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/requesting-code-review/SKILL.md"},{name:"root-cause-tracing",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/root-cause-tracing/SKILL.md"},{name:"verification-before-completion",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/verification-before-completion/SKILL.md"},{name:"dispatching-parallel-agents",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/dispatching-parallel-agents/SKILL.md"}]},{label:"anthropics/skills",emoji:"🏛️",stars:"官方",desc:"Anthropic 官方技能库",skills:[{name:"docx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/docx/SKILL.md"},{name:"pdf",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/pdf/SKILL.md"},{name:"xlsx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/xlsx/SKILL.md"},{name:"pptx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/pptx/SKILL.md"},{name:"mcp-builder",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md"},{name:"frontend-design",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/frontend-design/SKILL.md"},{name:"web-artifacts-builder",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/web-artifacts-builder/SKILL.md"},{name:"webapp-testing",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/webapp-testing/SKILL.md"},{name:"algorithmic-art",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/algorithmic-art/SKILL.md"},{name:"canvas-design",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/canvas-design/SKILL.md"}]},{label:"ComposioHQ/awesome-claude-skills",emoji:"🌐",stars:"39.2k",desc:"100+ 社区精选技能",skills:[{name:"github-integration",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/github-integration/SKILL.md"},{name:"data-analysis",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/data-analysis/SKILL.md"},{name:"code-review",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/code-review/SKILL.md"}]}];function tp(){const o=ne(u=>u.agentConfig),m=ne(u=>u.loadAgentConfig),c=ne(u=>u.toast),[y,w]=Z.useState(null),[I,B]=Z.useState(null),[j,R]=Z.useState({name:"",desc:"",trigger:""}),[_,E]=Z.useState(!1),[f,N]=Z.useState("local"),[h,L]=Z.useState([]),[z,W]=Z.useState(!1),[$,F]=Z.useState(!1),[ee,T]=Z.useState({agentId:"",skillName:"",sourceUrl:"",description:""}),[fe,ue]=Z.useState(!1),[me,H]=Z.useState(null),[Te,X]=Z.useState(null),[se,ye]=Z.useState(null),[ie,K]=Z.useState("");Z.useEffect(()=>{m()},[m]),Z.useEffect(()=>{f==="remote"&&we()},[f]);const we=async()=>{W(!0);try{const u=await he.remoteSkillsList();u.ok&&L(u.remoteSkills||[])}catch{c("远程技能列表加载失败","err")}W(!1)},Ce=async(u,k)=>{w({agentId:u,name:k,content:"⟳ 加载中…",path:""});try{const b=await he.skillContent(u,k);b.ok?w({agentId:u,name:k,content:b.content||"",path:b.path||""}):w({agentId:u,name:k,content:"❌ "+(b.error||"无法读取"),path:""})}catch{w({agentId:u,name:k,content:"❌ 服务器连接失败",path:""})}},ge=(u,k)=>{B({agentId:u,agentLabel:k}),R({name:"",desc:"",trigger:""})},U=async u=>{if(u.preventDefault(),!(!I||!j.name)){E(!0);try{const k=await he.addSkill(I.agentId,j.name,j.desc,j.trigger);k.ok?(c(`✅ 技能 ${j.name} 已添加到 ${I.agentLabel}`,"ok"),B(null),m()):c(k.error||"添加失败","err")}catch{c("服务器连接失败","err")}E(!1)}},te=async u=>{u.preventDefault();const{agentId:k,skillName:b,sourceUrl:oe,description:ae}=ee;if(!(!k||!b||!oe)){ue(!0);try{const Ae=await he.addRemoteSkill(k,b,oe,ae);Ae.ok?(c(`✅ 远程技能 ${b} 已添加到 ${k}`,"ok"),F(!1),T({agentId:"",skillName:"",sourceUrl:"",description:""}),we(),m()):c(Ae.error||"添加失败","err")}catch{c("服务器连接失败","err")}ue(!1)}},V=async u=>{const k=`${u.agentId}/${u.skillName}`;H(k);try{const b=await he.updateRemoteSkill(u.agentId,u.skillName);b.ok?(c(`✅ 技能 ${u.skillName} 已更新`,"ok"),we()):c(b.error||"更新失败","err")}catch{c("服务器连接失败","err")}H(null)},g=async u=>{const k=`${u.agentId}/${u.skillName}`;X(k);try{const b=await he.removeRemoteSkill(u.agentId,u.skillName);b.ok?(c(`🗑️ 技能 ${u.skillName} 已移除`,"ok"),we(),m()):c(b.error||"移除失败","err")}catch{c("服务器连接失败","err")}X(null)},P=async(u,k)=>{if(!ie){c("请先选择目标 Agent","err");return}try{const b=await he.addRemoteSkill(ie,k,u,"");b.ok?(c(`✅ ${k} → ${ie}`,"ok"),we(),m()):c(b.error||"导入失败","err")}catch{c("服务器连接失败","err")}};if(!(o!=null&&o.agents))return l.jsx("div",{className:"empty",children:"无法加载"});const ce=l.jsx("div",{children:l.jsx("div",{className:"skills-grid",children:o.agents.map(u=>l.jsxs("div",{className:"sk-card",children:[l.jsxs("div",{className:"sk-hdr",children:[l.jsx("span",{className:"sk-emoji",children:u.emoji||"🏛️"}),l.jsx("span",{className:"sk-name",children:u.label}),l.jsxs("span",{className:"sk-cnt",children:[(u.skills||[]).length," 技能"]})]}),l.jsx("div",{className:"sk-list",children:(u.skills||[]).length?(u.skills||[]).map(k=>l.jsxs("div",{className:"sk-item",onClick:()=>Ce(u.id,k.name),children:[l.jsxs("span",{className:"si-name",children:["📦 ",k.name]}),l.jsx("span",{className:"si-desc",children:k.description||"无描述"}),l.jsx("span",{className:"si-arrow",children:"›"})]},k.name)):l.jsx("div",{className:"sk-empty",children:"暂无 Skills"})}),l.jsx("div",{className:"sk-add",onClick:()=>ge(u.id,u.label),children:"+ 添加技能"})]},u.id))})}),pe=l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:10,marginBottom:20,flexWrap:"wrap",alignItems:"center"},children:[l.jsx("button",{style:{padding:"8px 18px",background:"var(--acc)",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600,fontSize:13},onClick:()=>{F(!0),ye(null)},children:"+ 添加远程 Skill"}),l.jsx("button",{style:{padding:"8px 14px",background:"transparent",color:"var(--acc)",border:"1px solid var(--acc)",borderRadius:8,cursor:"pointer",fontSize:12},onClick:we,children:"⟳ 刷新列表"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:4},children:["共 ",h.length," 个远程技能"]})]}),l.jsxs("div",{style:{marginBottom:24},children:[l.jsx("div",{style:{fontSize:12,fontWeight:700,color:"var(--muted)",letterSpacing:".06em",marginBottom:10},children:"🌐 社区技能源 — 一键导入"}),l.jsx("div",{style:{display:"flex",gap:10,flexWrap:"wrap"},children:ep.map(u=>l.jsxs("div",{onClick:()=>ye((se==null?void 0:se.label)===u.label?null:u),style:{padding:"8px 14px",background:(se==null?void 0:se.label)===u.label?"#0d1f45":"var(--panel)",border:`1px solid ${(se==null?void 0:se.label)===u.label?"var(--acc)":"var(--line)"}`,borderRadius:10,cursor:"pointer",fontSize:12,transition:"all .15s"},children:[l.jsx("span",{style:{marginRight:6},children:u.emoji}),l.jsx("b",{style:{color:"var(--text)"},children:u.label}),l.jsxs("span",{style:{marginLeft:6,color:"#f0b429",fontSize:11},children:["★ ",u.stars]}),l.jsx("span",{style:{marginLeft:8,color:"var(--muted)"},children:u.desc})]},u.label))}),se&&l.jsxs("div",{style:{marginTop:14,background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:16},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12,marginBottom:14},children:[l.jsx("span",{style:{fontSize:12,fontWeight:600},children:"目标 Agent:"}),l.jsxs("select",{value:ie,onChange:u=>K(u.target.value),style:{padding:"6px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12},children:[l.jsx("option",{value:"",children:"— 选择 Agent —"}),o.agents.map(u=>l.jsxs("option",{value:u.id,children:[u.emoji," ",u.label," (",u.id,")"]},u.id))]})]}),l.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(260px, 1fr))",gap:8},children:se.skills.map(u=>{const k=h.some(b=>b.skillName===u.name&&b.agentId===ie);return l.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"8px 12px",background:"var(--panel2)",borderRadius:8,border:"1px solid var(--line)"},children:[l.jsxs("div",{children:[l.jsxs("div",{style:{fontSize:12,fontWeight:600},children:["📦 ",u.name]}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)",wordBreak:"break-all",maxWidth:180},children:u.url.split("/").slice(-2).join("/")})]}),k?l.jsx("span",{style:{fontSize:10,color:"#4caf88",fontWeight:600},children:"✓ 已导入"}):l.jsx("button",{onClick:()=>P(u.url,u.name),style:{padding:"4px 10px",background:"var(--acc)",color:"#fff",border:"none",borderRadius:6,cursor:"pointer",fontSize:11,whiteSpace:"nowrap"},children:"导入"})]},u.name)})})]})]}),z?l.jsx("div",{style:{textAlign:"center",padding:"40px 0",color:"var(--muted)",fontSize:13},children:"⟳ 加载中…"}):h.length===0?l.jsxs("div",{style:{textAlign:"center",padding:"40px",background:"var(--panel)",borderRadius:12,border:"1px dashed var(--line)"},children:[l.jsx("div",{style:{fontSize:32,marginBottom:10},children:"🌐"}),l.jsx("div",{style:{fontSize:14,color:"var(--muted)"},children:"尚无远程技能"}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginTop:6},children:"从社区技能源快速导入,或手动添加 URL"})]}):l.jsx("div",{style:{display:"flex",flexDirection:"column",gap:10},children:h.map(u=>{var Ae;const k=`${u.agentId}/${u.skillName}`,b=me===k,oe=Te===k,ae=o.agents.find(mn=>mn.id===u.agentId);return l.jsxs("div",{style:{background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:"14px 18px",display:"grid",gridTemplateColumns:"1fr auto",gap:12,alignItems:"center"},children:[l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,marginBottom:6},children:[l.jsxs("span",{style:{fontSize:14,fontWeight:700},children:["📦 ",u.skillName]}),l.jsx("span",{style:{fontSize:10,padding:"2px 8px",borderRadius:999,background:u.status==="valid"?"#0d3322":"#3d1111",color:u.status==="valid"?"#4caf88":"#ff5270",fontWeight:600},children:u.status==="valid"?"✓ 有效":"✗ 文件丢失"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",background:"var(--panel2)",padding:"2px 8px",borderRadius:6},children:[ae==null?void 0:ae.emoji," ",(ae==null?void 0:ae.label)||u.agentId]})]}),u.description&&l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:4},children:u.description}),l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",display:"flex",gap:16,flexWrap:"wrap"},children:[l.jsxs("span",{children:["🔗 ",l.jsx("a",{href:u.sourceUrl,target:"_blank",rel:"noreferrer",style:{color:"var(--acc)",textDecoration:"none"},children:u.sourceUrl.length>60?u.sourceUrl.slice(0,60)+"…":u.sourceUrl})]}),l.jsxs("span",{children:["📅 ",u.lastUpdated?u.lastUpdated.slice(0,10):(Ae=u.addedAt)==null?void 0:Ae.slice(0,10)]})]})]}),l.jsxs("div",{style:{display:"flex",gap:8},children:[l.jsx("button",{onClick:()=>Ce(u.agentId,u.skillName),style:{padding:"6px 12px",background:"transparent",color:"var(--muted)",border:"1px solid var(--line)",borderRadius:6,cursor:"pointer",fontSize:11},children:"查看"}),l.jsx("button",{onClick:()=>V(u),disabled:b,style:{padding:"6px 12px",background:"transparent",color:"var(--acc)",border:"1px solid var(--acc)",borderRadius:6,cursor:"pointer",fontSize:11},children:b?"⟳":"更新"}),l.jsx("button",{onClick:()=>g(u),disabled:oe,style:{padding:"6px 12px",background:"transparent",color:"#ff5270",border:"1px solid #ff5270",borderRadius:6,cursor:"pointer",fontSize:11},children:oe?"⟳":"删除"})]})]},k)})})]});return l.jsxs("div",{children:[l.jsx("div",{style:{display:"flex",gap:4,marginBottom:20,borderBottom:"1px solid var(--line)",paddingBottom:0},children:[{key:"local",label:"🏛️ 本地技能",count:o.agents.reduce((u,k)=>{var b;return u+(((b=k.skills)==null?void 0:b.length)||0)},0)},{key:"remote",label:"🌐 远程技能",count:h.length}].map(u=>l.jsxs("div",{onClick:()=>N(u.key),style:{padding:"8px 18px",cursor:"pointer",fontSize:13,borderRadius:"8px 8px 0 0",fontWeight:f===u.key?700:400,background:f===u.key?"var(--panel)":"transparent",color:f===u.key?"var(--text)":"var(--muted)",border:f===u.key?"1px solid var(--line)":"1px solid transparent",borderBottom:f===u.key?"1px solid var(--panel)":"1px solid transparent",position:"relative",bottom:-1,transition:"all .15s"},children:[u.label,u.count>0&&l.jsx("span",{style:{marginLeft:6,fontSize:10,padding:"1px 6px",borderRadius:999,background:"#1a2040",color:"var(--acc)"},children:u.count})]},u.key))}),f==="local"?ce:pe,y&&l.jsx("div",{className:"modal-bg open",onClick:()=>w(null),children:l.jsxs("div",{className:"modal",onClick:u=>u.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>w(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:y.agentId.toUpperCase()}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:16},children:["📦 ",y.name]}),l.jsxs("div",{className:"sk-modal-body",children:[l.jsx("div",{className:"sk-md",style:{whiteSpace:"pre-wrap",fontSize:12,lineHeight:1.7},children:y.content}),y.path&&l.jsxs("div",{className:"sk-path",style:{fontSize:10,color:"var(--muted)",marginTop:12},children:["📂 ",y.path]})]})]})]})}),I&&l.jsx("div",{className:"modal-bg open",onClick:()=>B(null),children:l.jsxs("div",{className:"modal",onClick:u=>u.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>B(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsxs("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:["为 ",I.agentLabel," 添加技能"]}),l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:18},children:"+ 新增 Skill"}),l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:10,padding:14,marginBottom:18,fontSize:12,lineHeight:1.7,color:"var(--muted)"},children:[l.jsx("b",{style:{color:"var(--text)"},children:"📋 Skill 规范说明"}),l.jsx("br",{}),"• 技能名称使用",l.jsx("b",{style:{color:"var(--text)"},children:"小写英文 + 连字符"}),l.jsx("br",{}),"• 创建后会生成模板文件 SKILL.md",l.jsx("br",{}),"• 技能会在 agent 收到相关任务时",l.jsx("b",{style:{color:"var(--text)"},children:"自动激活"})]}),l.jsxs("form",{onSubmit:U,style:{display:"flex",flexDirection:"column",gap:14},children:[l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["技能名称 ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"text",required:!0,placeholder:"如 data-analysis, code-review",value:j.name,onChange:u=>R(k=>({...k,name:u.target.value.toLowerCase().replace(/[^a-z0-9-]/g,"")})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"技能描述"}),l.jsx("input",{type:"text",placeholder:"一句话说明用途",value:j.desc,onChange:u=>R(k=>({...k,desc:u.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"触发条件(可选)"}),l.jsx("input",{type:"text",placeholder:"何时激活此技能",value:j.trigger,onChange:u=>R(k=>({...k,trigger:u.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end",marginTop:4},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:()=>B(null),style:{padding:"8px 20px"},children:"取消"}),l.jsx("button",{type:"submit",disabled:_,style:{padding:"8px 20px",fontSize:13,background:"var(--acc)",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600},children:_?"⟳ 创建中…":"📦 创建技能"})]})]})]})]})}),$&&l.jsx("div",{className:"modal-bg open",onClick:()=>F(!1),children:l.jsxs("div",{className:"modal",style:{maxWidth:520},onClick:u=>u.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>F(!1),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"#a07aff",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:"远程技能管理"}),l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:18},children:"🌐 添加远程 Skill"}),l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:10,padding:12,marginBottom:18,fontSize:11,color:"var(--muted)",lineHeight:1.7},children:["支持 GitHub Raw URL,如:",l.jsx("br",{}),l.jsx("code",{style:{color:"var(--acc)",fontSize:10},children:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/brainstorming/SKILL.md"})]}),l.jsxs("form",{onSubmit:te,style:{display:"flex",flexDirection:"column",gap:14},children:[l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["目标 Agent ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsxs("select",{required:!0,value:ee.agentId,onChange:u=>T(k=>({...k,agentId:u.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13},children:[l.jsx("option",{value:"",children:"— 选择 Agent —"}),o.agents.map(u=>l.jsxs("option",{value:u.id,children:[u.emoji," ",u.label," (",u.id,")"]},u.id))]})]}),l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["技能名称 ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"text",required:!0,placeholder:"如 brainstorming, code-review",value:ee.skillName,onChange:u=>T(k=>({...k,skillName:u.target.value.toLowerCase().replace(/[^a-z0-9-]/g,"")})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["源 URL ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"url",required:!0,placeholder:"https://raw.githubusercontent.com/...",value:ee.sourceUrl,onChange:u=>T(k=>({...k,sourceUrl:u.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:12,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"描述(可选)"}),l.jsx("input",{type:"text",placeholder:"一句话说明用途",value:ee.description,onChange:u=>T(k=>({...k,description:u.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end",marginTop:4},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:()=>F(!1),style:{padding:"8px 20px"},children:"取消"}),l.jsx("button",{type:"submit",disabled:fe,style:{padding:"8px 20px",fontSize:13,background:"#a07aff",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600},children:fe?"⟳ 下载中…":"🌐 添加远程技能"})]})]})]})]})})]})}function np(){const o=ne(y=>y.agentConfig),m={},c={};return o!=null&&o.agents&&o.agents.forEach(y=>{m[y.id]=y.emoji||"🏛️",c[y.id]=y.label||y.id}),{emojiMap:m,labelMap:c}}function Bl(o){const m=(o.id||"").match(/^OC-(\w+)-/);return m?m[1]:(o.org||"").replace(/省|部/g,"").toLowerCase()}function dc(o,m){let c=o.title||"";if(c==="heartbeat 会话")return"💓 心跳检测";const y=c.match(/^agent:(\w+):(\w+)/);if(y){const w=m[y[1]]||y[1];return y[2]==="main"?w+" · 主会话":y[2]==="subagent"?w+" · 子任务执行":y[2]==="cron"?w+" · 定时任务":w+" · "+y[2]}return c.replace(/ 会话$/,"")||o.id}function fc(o){const m=o.now||"";return m.includes("feishu/direct")?{icon:"💬",text:"飞书对话"}:m.includes("feishu")?{icon:"💬",text:"飞书"}:m.includes("webchat")?{icon:"🌐",text:"WebChat"}:m.includes("cron")?{icon:"⏰",text:"定时"}:m.includes("direct")?{icon:"📨",text:"直连"}:{icon:"🔗",text:"会话"}}function rp(o){const m=o.activity||[];for(let c=m.length-1;c>=0;c--){const y=m[c];if(y.kind==="assistant"){let w=y.text||"";if(w.startsWith("NO_REPLY")||w.startsWith("Reasoning:"))continue;return w=w.replace(/\[\[.*?\]\]/g,"").replace(/\*\*/g,"").replace(/^#+\s/gm,"").trim(),w.substring(0,120)+(w.length>120?"…":"")}}return""}function lp(){const o=ne(f=>f.liveStatus),m=ne(f=>f.sessFilter),c=ne(f=>f.setSessFilter),{emojiMap:y,labelMap:w}=np(),[I,B]=Z.useState(null),R=((o==null?void 0:o.tasks)||[]).filter(f=>!Jt(f));let _=R;m==="active"?_=R.filter(f=>!["Done","Cancelled"].includes(f.state)):m!=="all"&&(_=R.filter(f=>Bl(f)===m));const E=[...new Set(R.map(Bl))];return l.jsxs("div",{children:[l.jsx("div",{style:{display:"flex",gap:6,marginBottom:16,flexWrap:"wrap"},children:[{key:"all",label:`全部 (${R.length})`},{key:"active",label:"活跃"},...E.slice(0,8).map(f=>({key:f,label:w[f]||f}))].map(f=>l.jsx("span",{className:`sess-filter${m===f.key?" active":""}`,onClick:()=>c(f.key),children:f.label},f.key))}),l.jsx("div",{className:"sess-grid",children:_.length?_.map(f=>{const N=Bl(f),h=y[N]||"🏛️",L=w[N]||f.org||N,z=f.heartbeat||{status:"unknown",label:""},W=fc(f),$=dc(f,w),F=rp(f),T=(f.sourceMeta||{}).totalTokens,fe=f.eta||"",ue=z.status==="active"?"🟢":z.status==="warn"?"🟡":z.status==="stalled"?"🔴":"⚪",me=f.state||"Unknown";return l.jsxs("div",{className:"sess-card",onClick:()=>B(f),children:[l.jsxs("div",{className:"sc-top",children:[l.jsx("span",{className:"sc-emoji",children:h}),l.jsx("div",{style:{flex:1,minWidth:0},children:l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{className:"sc-agent",children:L}),l.jsxs("span",{style:{fontSize:10,color:"var(--muted)",background:"var(--panel2)",padding:"2px 6px",borderRadius:4},children:[W.icon," ",W.text]})]})}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{title:z.label||"",children:ue}),l.jsx("span",{className:`tag st-${me}`,style:{fontSize:10},children:zr[me]||me})]})]}),l.jsx("div",{className:"sc-title",children:$}),F&&l.jsx("div",{style:{fontSize:11,color:"var(--muted)",lineHeight:1.5,marginBottom:8,borderLeft:"2px solid var(--line)",paddingLeft:8,maxHeight:40,overflow:"hidden"},children:F}),l.jsxs("div",{className:"sc-meta",children:[T?l.jsxs("span",{style:{fontSize:10,color:"var(--muted)"},children:["🪙 ",T.toLocaleString()," tokens"]}):null,fe?l.jsx("span",{className:"sc-time",children:Hf(fe)}):null]})]},f.id)}):l.jsx("div",{style:{fontSize:13,color:"var(--muted)",padding:24,textAlign:"center",gridColumn:"1/-1"},children:"暂无小任务/会话数据"})}),I&&l.jsx(sp,{task:I,labelMap:w,emojiMap:y,onClose:()=>B(null)})]})}function sp({task:o,labelMap:m,emojiMap:c,onClose:y}){const w=Bl(o),I=c[w]||"🏛️",B=dc(o,m),j=fc(o),R=o.heartbeat||{label:""},_=o.sourceMeta||{},E=o.activity||[],f=o.state||"Unknown",N=_.totalTokens,h=_.inputTokens,L=_.outputTokens;return l.jsx("div",{className:"modal-bg open",onClick:y,children:l.jsxs("div",{className:"modal",onClick:z=>z.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:y,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:o.id}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[I," ",B]}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:18,flexWrap:"wrap"},children:[l.jsx("span",{className:`tag st-${f}`,children:zr[f]||f}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:[j.icon," ",j.text]}),R.label&&l.jsx("span",{style:{fontSize:11},children:R.label})]}),l.jsxs("div",{style:{display:"flex",gap:14,marginBottom:18,flexWrap:"wrap"},children:[N!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700,color:"var(--acc)"},children:N.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"总 Tokens"})]}),h!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700},children:h.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"输入"})]}),L!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700},children:L.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"输出"})]})]}),l.jsxs("div",{style:{fontSize:12,fontWeight:700,marginBottom:8},children:["📋 最近活动 ",l.jsxs("span",{style:{fontWeight:400,color:"var(--muted)"},children:["(",E.length," 条)"]})]}),l.jsx("div",{style:{maxHeight:350,overflowY:"auto",border:"1px solid var(--line)",borderRadius:10,background:"var(--panel2)"},children:E.length?E.slice(-15).reverse().map((z,W)=>{const $=z.kind||"",F=$==="assistant"?"🤖":$==="tool"?"🔧":$==="user"?"👤":"📝",ee=$==="assistant"?"回复":$==="tool"?"工具":$==="user"?"用户":"事件";let T=(z.text||"").replace(/\[\[.*?\]\]/g,"").replace(/\*\*/g,"").trim();T.length>200&&(T=T.substring(0,200)+"…");const fe=(z.at||"").substring(11,19);return l.jsxs("div",{style:{padding:"8px 12px",borderBottom:"1px solid var(--line)",fontSize:12,lineHeight:1.5},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:3},children:[l.jsx("span",{children:F}),l.jsx("span",{style:{fontWeight:600,fontSize:11},children:ee}),l.jsx("span",{style:{color:"var(--muted)",fontSize:10,marginLeft:"auto"},children:fe})]}),l.jsx("div",{style:{color:"var(--muted)"},children:T})]},W)}):l.jsx("div",{style:{padding:16,color:"var(--muted)",fontSize:12,textAlign:"center"},children:"暂无活动记录"})}),o.output&&o.output!=="-"&&l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",marginTop:12,wordBreak:"break-all",borderTop:"1px solid var(--line)",paddingTop:8},children:["📂 ",o.output]})]})]})})}function ip(){const o=ne(_=>_.liveStatus),[m,c]=Z.useState("all"),[y,w]=Z.useState(null),I=ne(_=>_.toast);let j=((o==null?void 0:o.tasks)||[]).filter(_=>Jt(_)&&["Done","Cancelled"].includes(_.state));m!=="all"&&(j=j.filter(_=>_.state===m));const R=_=>{const E=_.flow_log||[];let f=`# 📜 奏折 · ${_.title} + +`;if(f+=`- **任务编号**: ${_.id} +`,f+=`- **状态**: ${_.state} +`,f+=`- **负责部门**: ${_.org} +`,E.length){const N=E[0].at?E[0].at.substring(0,19).replace("T"," "):"未知",h=E[E.length-1].at?E[E.length-1].at.substring(0,19).replace("T"," "):"未知";f+=`- **开始时间**: ${N} +`,f+=`- **完成时间**: ${h} +`}f+=` +## 流转记录 + +`;for(const N of E)f+=`- **${N.from}** → **${N.to}** + ${N.remark} + _${(N.at||"").substring(0,19)}_ + +`;_.output&&_.output!=="-"&&(f+=`## 产出物 + +\`${_.output}\` +`),navigator.clipboard.writeText(f).then(()=>I("✅ 奏折已复制为 Markdown","ok"),()=>I("复制失败","err"))};return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:8,marginBottom:16,alignItems:"center"},children:[l.jsx("span",{style:{fontSize:12,color:"var(--muted)"},children:"筛选:"}),[{key:"all",label:"全部"},{key:"Done",label:"✅ 已完成"},{key:"Cancelled",label:"🚫 已取消"}].map(_=>l.jsx("span",{className:`sess-filter${m===_.key?" active":""}`,onClick:()=>c(_.key),children:_.label},_.key))]}),l.jsx("div",{className:"mem-list",children:j.length?j.map(_=>{const E=_.flow_log||[],f=[...new Set(E.map(z=>z.from).concat(E.map(z=>z.to)).filter(z=>z&&z!=="皇上"))],N=E.length?(E[0].at||"").substring(0,16).replace("T"," "):"",h=E.length?(E[E.length-1].at||"").substring(0,16).replace("T"," "):"",L=_.state==="Done"?"✅":"🚫";return l.jsxs("div",{className:"mem-card",onClick:()=>w(_),children:[l.jsx("div",{className:"mem-icon",children:"📜"}),l.jsxs("div",{className:"mem-info",children:[l.jsxs("div",{className:"mem-title",children:[L," ",_.title||_.id]}),l.jsxs("div",{className:"mem-sub",children:[_.id," · ",_.org||""," · 流转 ",E.length," 步"]}),l.jsx("div",{className:"mem-tags",children:f.slice(0,5).map(z=>l.jsx("span",{className:"mem-tag",children:z},z))})]}),l.jsxs("div",{className:"mem-right",children:[l.jsx("span",{className:"mem-date",children:N}),h!==N&&l.jsx("span",{className:"mem-date",children:h})]})]},_.id)}):l.jsx("div",{className:"mem-empty",children:"暂无奏折 — 任务完成后自动生成"})}),y&&l.jsx(op,{task:y,onClose:()=>w(null),onExport:R})]})}function op({task:o,onClose:m,onExport:c}){const y=o.flow_log||[],w=o.state||"Unknown",I=w==="Done"?"✅":w==="Cancelled"?"🚫":"🔄",B=[...new Set(y.map(h=>h.from).concat(y.map(h=>h.to)).filter(h=>h&&h!=="皇上"))],j=[],R=[],_=[],E=[],f=[];for(const h of y)h.from==="皇上"?j.push(h):h.to==="中书省"||h.from==="中书省"?R.push(h):h.to==="门下省"||h.from==="门下省"?_.push(h):h.remark&&(h.remark.includes("完成")||h.remark.includes("回奏"))?f.push(h):E.push(h);const N=(h,L,z)=>z.length?l.jsxs("div",{style:{marginBottom:18},children:[l.jsxs("div",{style:{fontSize:13,fontWeight:700,marginBottom:10},children:[L," ",h]}),l.jsx("div",{className:"md-timeline",children:z.map((W,$)=>{var ee,T;const F=(ee=W.remark)!=null&&ee.includes("✅")?"green":(T=W.remark)!=null&&T.includes("驳")?"red":"";return l.jsxs("div",{className:"md-tl-item",children:[l.jsx("div",{className:`md-tl-dot ${F}`}),l.jsxs("div",{style:{display:"flex",gap:6,alignItems:"baseline"},children:[l.jsx("span",{className:"md-tl-from",children:W.from}),l.jsxs("span",{className:"md-tl-to",children:["→ ",W.to]})]}),l.jsx("div",{className:"md-tl-remark",children:W.remark}),l.jsx("div",{className:"md-tl-time",children:(W.at||"").substring(0,19).replace("T"," ")})]},$)})})]}):null;return l.jsx("div",{className:"modal-bg open",onClick:m,children:l.jsxs("div",{className:"modal",onClick:h=>h.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:m,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:o.id}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[I," ",o.title||o.id]}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:18,flexWrap:"wrap"},children:[l.jsx("span",{className:`tag st-${w}`,children:zr[w]||w}),l.jsx("span",{style:{fontSize:11,color:"var(--muted)"},children:o.org}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["流转 ",y.length," 步"]}),B.map(h=>l.jsx("span",{className:"mem-tag",children:h},h))]}),o.now&&l.jsx("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:"10px 14px",marginBottom:18,fontSize:12,color:"var(--muted)"},children:o.now}),N("圣旨原文","👑",j),N("中书规划","📋",R),N("门下审议","🔍",_),N("六部执行","⚔️",E),N("汇总回奏","📨",f),o.output&&o.output!=="-"&&l.jsxs("div",{style:{marginTop:12,paddingTop:12,borderTop:"1px solid var(--line)"},children:[l.jsx("div",{style:{fontSize:11,fontWeight:600,marginBottom:4},children:"📦 产出物"}),l.jsx("code",{style:{fontSize:11,wordBreak:"break-all"},children:o.output})]}),l.jsx("div",{style:{display:"flex",gap:8,marginTop:16,justifyContent:"flex-end"},children:l.jsx("button",{className:"btn btn-g",onClick:()=>c(o),style:{fontSize:12,padding:"6px 16px"},children:"📋 复制奏折"})})]})]})})}const ap=["中书省","尚书省","礼部","户部","兵部","刑部","工部","吏部"],up=[{id:"taizi",label:"太子",emoji:"🤴"},{id:"zhongshu",label:"中书省",emoji:"📜"},{id:"menxia",label:"门下省",emoji:"🔍"},{id:"shangshu",label:"尚书省",emoji:"📮"},{id:"hubu",label:"户部",emoji:"💰"},{id:"bingbu",label:"兵部",emoji:"⚔️"}];function cp(){var ce,pe;const o=ne(u=>u.tplCatFilter),m=ne(u=>u.setTplCatFilter),c=ne(u=>u.toast),y=ne(u=>u.loadAll),[w,I]=Z.useState(null),[B,j]=Z.useState({}),[R,_]=Z.useState(""),[E,f]=Z.useState(""),[N,h]=Z.useState(""),[L,z]=Z.useState("normal"),[W,$]=Z.useState(""),[F,ee]=Z.useState(["taizi","zhongshu","menxia"]),[T,fe]=Z.useState(""),[ue,me]=Z.useState(!1),[H,Te]=Z.useState(null);let X=Of;o!=="全部"&&(X=X.filter(u=>u.cat===o));const se=u=>{const k={};u.params.forEach(b=>{k[b.key]=b.default||""}),j(k),I(u),_("")},ye=u=>{let k=u.command;for(const b of u.params)k=k.replace(new RegExp("\\{"+b.key+"\\}","g"),B[b.key]||b.default||"");return k},ie=()=>{w&&_(ye(w))},K=async()=>{try{const u=await he.agentsStatus();if(u.ok&&u.gateway&&!u.gateway.alive&&(c("⚠️ Gateway 未启动,任务将无法派发!","err"),!confirm("Gateway 未启动,继续?")))return!1}catch{}return!0},we=async u=>{if(u.preventDefault(),!w)return;const k=ye(w);if(!k.trim()){c("请填写必填参数","err");return}if(await K()&&confirm(`确认下旨? + +${k.substring(0,200)}${k.length>200?"…":""}`))try{const b={};for(const ae of w.params)b[ae.key]=B[ae.key]||ae.default||"";const oe=await he.createTask({title:k,org:"中书省",targetDept:w.depts[0]||"",priority:"normal",templateId:w.id,params:b});oe.ok?(c(`📜 ${oe.taskId} 旨意已下达`,"ok"),I(null),y()):c(oe.error||"下旨失败","err")}catch{c("⚠️ 服务器连接失败","err")}},Ce=u=>{ee(k=>k.includes(u)?k.length<=1?k:k.filter(b=>b!==u):[...k,u])},ge=async u=>{u.preventDefault();const k=W.trim();if(k.length<10){c("议题至少 10 个字","err");return}if(F.length<2){c("至少选择 2 位大臣参与议政","err");return}if(await K()&&confirm(`开始御前议政? + +议题:${k.substring(0,120)}${k.length>120?"…":""}`)){me(!0),fe(""),Te(null);try{const b=await he.courtDiscuss({action:"start",topic:k,participants:F});Te(b),fe(b.sessionId||""),b.ok?c("🧠 首轮议政完成,请皇上拍板继续或结束","ok"):c(b.error||"议政失败","err")}catch{c("⚠️ 服务器连接失败","err")}finally{me(!1)}}},U=async()=>{if(!T){c("请先开始议政","err");return}me(!0);try{const u=await he.courtDiscuss({action:"next",sessionId:T});Te(u),u.ok?c("已继续一轮讨论","ok"):c(u.error||"继续讨论失败","err")}catch{c("⚠️ 服务器连接失败","err")}finally{me(!1)}},te=async()=>{if(!T){c("请先开始议政","err");return}if(confirm("确认结束讨论并生成最终旨意草案?")){me(!0);try{const u=await he.courtDiscuss({action:"finalize",sessionId:T});Te(u),u.ok?c("✅ 已结束讨论并生成草案","ok"):c(u.error||"结束讨论失败","err")}catch{c("⚠️ 服务器连接失败","err")}finally{me(!1)}}},V=()=>{var k,b,oe,ae;const u=(b=(k=H==null?void 0:H.final)==null?void 0:k.recommended_edict)==null?void 0:b.trim();if(!u){c("没有可用的建议旨意","err");return}f(u),(oe=H==null?void 0:H.final)!=null&&oe.recommended_target_dept&&h(H.final.recommended_target_dept),(ae=H==null?void 0:H.final)!=null&&ae.recommended_priority&&z(H.final.recommended_priority),c("已填入自由下旨区,请确认后下旨","ok")},g=async()=>{var b;const u=H==null?void 0:H.final,k=(b=u==null?void 0:u.recommended_edict)==null?void 0:b.trim();if(!k){c("没有可下达的旨意草案","err");return}if(await K()&&confirm(`确认将议政结论直接下旨? + +${k.substring(0,200)}${k.length>200?"…":""}`))try{const oe=await he.createTask({title:k,org:"中书省",targetDept:(u==null?void 0:u.recommended_target_dept)||"",priority:(u==null?void 0:u.recommended_priority)||"normal",templateId:"court-discuss",params:{source:"court-discuss"}});oe.ok?(c(`📜 ${oe.taskId} 旨意已下达`,"ok"),y()):c(oe.error||"下旨失败","err")}catch{c("⚠️ 服务器连接失败","err")}},P=async u=>{u.preventDefault();const k=E.trim();if(!k){c("请先输入旨意内容","err");return}if(k.length<10){c("旨意至少 10 个字,避免被判定为闲聊","err");return}if(await K()&&confirm(`确认下旨给太子? + +${k.substring(0,200)}${k.length>200?"…":""}`))try{const b=await he.createTask({title:k,org:"中书省",targetDept:N||"",priority:L,templateId:"manual-free",params:{source:"free-edict"}});b.ok?(c(`📜 ${b.taskId} 旨意已下达`,"ok"),f(""),h(""),z("normal"),y()):c(b.error||"下旨失败","err")}catch{c("⚠️ 服务器连接失败","err")}};return l.jsxs("div",{children:[l.jsxs("div",{style:{background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:14,marginBottom:16},children:[l.jsx("div",{style:{fontSize:14,fontWeight:700,marginBottom:6},children:"御前议政(先讨论再下旨)"}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:10},children:"适合想法尚未成熟时,先让太子与诸臣讨论澄清,再生成可执行旨意。"}),l.jsxs("form",{onSubmit:ge,children:[l.jsx("textarea",{className:"tpl-input",style:{minHeight:88,resize:"vertical",marginBottom:10},placeholder:"例如:目前天下要闻标题是英文,想在看板中展示中文标题,但点击后保持英文原文。请先讨论清楚方案和边界。",value:W,onChange:u=>$(u.target.value),disabled:ue}),l.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginBottom:10},children:[l.jsx("button",{type:"submit",className:"tpl-go",style:{flex:"0 0 170px"},disabled:ue,children:ue?"⏳ 议政中...":"🧠 开始首轮议政"}),T&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",alignSelf:"center"},children:["会话: ",T]})]}),l.jsx("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginBottom:8},children:up.map(u=>{const k=F.includes(u.id);return l.jsxs("button",{type:"button",className:"tpl-cat",onClick:()=>Ce(u.id),style:{cursor:"pointer",borderColor:k?"var(--acc)":"var(--line)",color:k?"var(--text)":"var(--muted)",background:k?"var(--acc-soft)":"transparent"},disabled:ue,children:[u.emoji," ",u.label]},u.id)})})]}),H&&l.jsxs("div",{style:{borderTop:"1px dashed var(--line)",marginTop:10,paddingTop:10,fontSize:12},children:[!H.ok&&l.jsxs("div",{style:{color:"var(--danger)",marginBottom:8},children:["⚠️ ",H.error||"议政失败"]}),H.final&&l.jsxs(l.Fragment,{children:[l.jsx("div",{style:{fontWeight:700,marginBottom:6},children:H.final.ready_for_edict?"✅ 结论:可下旨":"🟡 结论:建议先澄清"}),H.final.clarified_goal&&l.jsxs("div",{style:{color:"var(--muted)",marginBottom:8},children:["目标澄清:",H.final.clarified_goal]}),((ce=H.final.risks)==null?void 0:ce.length)>0&&l.jsxs("div",{style:{marginBottom:8},children:[l.jsx("div",{style:{fontWeight:600,marginBottom:4},children:"主要风险"}),l.jsx("ul",{style:{margin:0,paddingLeft:18},children:H.final.risks.slice(0,4).map((u,k)=>l.jsx("li",{children:String(u)},k))})]}),((pe=H.final.questions_to_emperor)==null?void 0:pe.length)>0&&l.jsxs("div",{style:{marginBottom:8},children:[l.jsx("div",{style:{fontWeight:600,marginBottom:4},children:"待皇上确认"}),l.jsx("ul",{style:{margin:0,paddingLeft:18},children:H.final.questions_to_emperor.slice(0,4).map((u,k)=>l.jsx("li",{children:String(u)},k))})]}),l.jsx("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:10,whiteSpace:"pre-wrap",lineHeight:1.55,marginBottom:10},children:H.final.recommended_edict||"(暂无旨意草案)"}),l.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:V,children:"📥 采用到下旨框"}),l.jsx("button",{type:"button",className:"tpl-go",onClick:g,children:"📜 直接下旨"})]})]}),!H.final&&H.assessment&&l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:10,marginBottom:10},children:[l.jsxs("div",{style:{fontWeight:700,marginBottom:4},children:[H.assessment.moderatorLabel," 本轮建议:",H.assessment.recommend_stop?"请皇上决定是否结束讨论":"建议继续下一轮讨论"]}),H.assessment.reason&&l.jsxs("div",{style:{color:"var(--muted)",marginBottom:6},children:["原因:",H.assessment.reason]}),H.assessment.question_to_emperor&&l.jsxs("div",{style:{marginBottom:8},children:["请皇上拍板:",H.assessment.question_to_emperor]}),l.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:U,disabled:ue,children:"皇上:继续讨论一轮"}),l.jsx("button",{type:"button",className:"tpl-go",onClick:te,disabled:ue,children:"皇上:同意结束讨论"})]})]}),H.discussion&&H.discussion.length>0&&l.jsxs("details",{style:{marginTop:10},children:[l.jsxs("summary",{style:{cursor:"pointer",color:"var(--muted)"},children:["查看讨论详情(",H.discussion.length," 条)"]}),l.jsx("div",{style:{marginTop:8,maxHeight:260,overflow:"auto"},children:H.discussion.map((u,k)=>l.jsxs("div",{style:{border:"1px solid var(--line)",borderRadius:8,padding:8,marginBottom:8,background:"var(--panel2)"},children:[l.jsxs("div",{style:{fontWeight:700,marginBottom:4},children:["第",u.round,"轮 · ",u.agentLabel]}),l.jsx("div",{style:{whiteSpace:"pre-wrap",lineHeight:1.5},children:u.reply})]},k))})]})]})]}),l.jsxs("div",{style:{background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:14,marginBottom:16},children:[l.jsx("div",{style:{fontSize:14,fontWeight:700,marginBottom:6},children:"自由下旨(不走模板)"}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:10},children:"直接输入任务指令,系统会创建旨意并先派发给太子分拣。"}),l.jsxs("form",{onSubmit:P,children:[l.jsx("textarea",{className:"tpl-input",style:{minHeight:92,resize:"vertical",marginBottom:10},placeholder:"例如:请对 edict 项目做一次端到端测试审查,重点检查任务卡住时的自动重试与回滚链路。",value:E,onChange:u=>f(u.target.value)}),l.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginBottom:10},children:[l.jsxs("select",{className:"tpl-input",style:{flex:"1 1 220px"},value:N,onChange:u=>h(u.target.value),children:[l.jsx("option",{value:"",children:"建议执行部门(可选)"}),ap.map(u=>l.jsx("option",{value:u,children:u},u))]}),l.jsxs("select",{className:"tpl-input",style:{flex:"0 0 160px"},value:L,onChange:u=>z(u.target.value),children:[l.jsx("option",{value:"low",children:"低"}),l.jsx("option",{value:"normal",children:"普通"}),l.jsx("option",{value:"high",children:"高"}),l.jsx("option",{value:"critical",children:"紧急"})]}),l.jsx("button",{type:"submit",className:"tpl-go",style:{flex:"0 0 130px"},children:"📜 下旨"})]})]})]}),l.jsx("div",{style:{display:"flex",gap:6,marginBottom:16,flexWrap:"wrap"},children:Bf.map(u=>l.jsxs("span",{className:`tpl-cat${o===u.name?" active":""}`,onClick:()=>m(u.name),children:[u.icon," ",u.name]},u.name))}),l.jsx("div",{className:"tpl-grid",children:X.map(u=>l.jsxs("div",{className:"tpl-card",children:[l.jsxs("div",{className:"tpl-top",children:[l.jsx("span",{className:"tpl-icon",children:u.icon}),l.jsx("span",{className:"tpl-name",children:u.name})]}),l.jsx("div",{className:"tpl-desc",children:u.desc}),l.jsxs("div",{className:"tpl-footer",children:[u.depts.map(k=>l.jsx("span",{className:"tpl-dept",children:k},k)),l.jsxs("span",{className:"tpl-est",children:[u.est," · ",u.cost]}),l.jsx("button",{className:"tpl-go",onClick:()=>se(u),children:"下旨"})]})]},u.id))}),w&&l.jsx("div",{className:"modal-bg open",onClick:()=>I(null),children:l.jsxs("div",{className:"modal",onClick:u=>u.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>I(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:"圣旨模板"}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[w.icon," ",w.name]}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:18},children:w.desc}),l.jsxs("div",{style:{display:"flex",gap:6,marginBottom:18,flexWrap:"wrap"},children:[w.depts.map(u=>l.jsx("span",{className:"tpl-dept",children:u},u)),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:"auto"},children:[w.est," · ",w.cost]})]}),l.jsxs("form",{className:"tpl-form",onSubmit:we,children:[w.params.map(u=>l.jsxs("div",{className:"tpl-field",children:[l.jsxs("label",{className:"tpl-label",children:[u.label,u.required&&l.jsx("span",{style:{color:"#ff5270"},children:" *"})]}),u.type==="textarea"?l.jsx("textarea",{className:"tpl-input",style:{minHeight:80,resize:"vertical"},required:u.required,value:B[u.key]||"",onChange:k=>j(b=>({...b,[u.key]:k.target.value}))}):u.type==="select"?l.jsx("select",{className:"tpl-input",value:B[u.key]||u.default||"",onChange:k=>j(b=>({...b,[u.key]:k.target.value})),children:(u.options||[]).map(k=>l.jsx("option",{children:k},k))}):l.jsx("input",{className:"tpl-input",type:"text",required:u.required,value:B[u.key]||"",onChange:k=>j(b=>({...b,[u.key]:k.target.value}))})]},u.key)),R&&l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:12,marginBottom:14,fontSize:12,color:"var(--muted)"},children:[l.jsx("div",{style:{fontSize:11,fontWeight:600,color:"var(--text)",marginBottom:6},children:"📜 将发送给中书省的旨意:"}),l.jsx("div",{style:{whiteSpace:"pre-wrap",lineHeight:1.6},children:R})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end"},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:ie,style:{padding:"8px 16px",fontSize:12},children:"👁 预览旨意"}),l.jsx("button",{type:"submit",className:"tpl-go",style:{padding:"8px 20px",fontSize:13},children:"📜 下旨"})]})]})]})]})})]})}const pc={政治:{icon:"🏛️",color:"#6a9eff",desc:"全球政治动态"},军事:{icon:"⚔️",color:"#ff5270",desc:"军事与冲突"},经济:{icon:"💹",color:"#2ecc8a",desc:"经济与市场"},AI大模型:{icon:"🤖",color:"#a07aff",desc:"AI与大模型进展"}},Hi=["政治","军事","经济","AI大模型"];function dp(){const o=ne(X=>X.morningBrief),m=ne(X=>X.subConfig),c=ne(X=>X.loadMorning),y=ne(X=>X.loadSubConfig),w=ne(X=>X.toast),[I,B]=Z.useState(!1),[j,R]=Z.useState(null),[_,E]=Z.useState(!1),[f,N]=Z.useState("⟳ 立即采集"),h=Z.useRef(null);Z.useEffect(()=>{c()},[c]),Z.useEffect(()=>{m&&R(JSON.parse(JSON.stringify(m)))},[m]),Z.useEffect(()=>()=>{h.current&&clearInterval(h.current)},[]);const L=async()=>{E(!0),N("⟳ 采集中…");let X=null;try{X=(o==null?void 0:o.generated_at)||null}catch{}try{await he.refreshMorning(),w("采集已触发,自动检测更新中…","ok");let se=0;h.current&&clearInterval(h.current),h.current=setInterval(async()=>{if(se++,se>24){clearInterval(h.current),h.current=null,E(!1),N("⟳ 立即采集"),w("采集超时,请重试","err");return}try{const ye=await he.morningBrief();ye.generated_at&&ye.generated_at!==X?(clearInterval(h.current),h.current=null,E(!1),N("⟳ 立即采集"),c(),w("✅ 天下要闻已更新","ok")):N(`⟳ 采集中… (${se*5}s)`)}catch{}},5e3)}catch{w("触发失败","err"),E(!1),N("⟳ 立即采集")}},z=X=>{if(!j)return;const se=[...j.categories||[]],ye=se.find(ie=>ie.name===X);ye?ye.enabled=!ye.enabled:se.push({name:X,enabled:!0}),R({...j,categories:se})},W=X=>{if(!j||!X)return;const se=[...j.keywords||[]];se.includes(X)||se.push(X),R({...j,keywords:se})},$=X=>{if(!j)return;const se=[...j.keywords||[]];se.splice(X,1),R({...j,keywords:se})},F=(X,se,ye)=>{if(!j||!X||!se){w("请填写源名称和URL","err");return}const ie=[...j.custom_feeds||[]];ie.push({name:X,url:se,category:ye}),R({...j,custom_feeds:ie})},ee=X=>{if(!j)return;const se=[...j.custom_feeds||[]];se.splice(X,1),R({...j,custom_feeds:se})},T=async()=>{if(j)try{const X=await he.saveMorningConfig(j);X.ok?(w("订阅配置已保存","ok"),y()):w(X.error||"保存失败","err")}catch{w("服务器连接失败","err")}},fe=j?new Set((j.categories||[]).filter(X=>X.enabled).map(X=>X.name)):new Set(Hi),ue=((j==null?void 0:j.keywords)||[]).map(X=>X.toLowerCase()),me=(o==null?void 0:o.categories)||{},H=o!=null&&o.date?o.date.replace(/(\d{4})(\d{2})(\d{2})/,"$1年$2月$3日"):"",Te=Object.values(me).flat().length;return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:16},children:[l.jsxs("div",{children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:4},children:"🌅 天下要闻"}),l.jsxs("div",{style:{fontSize:12,color:"var(--muted)"},children:[H&&`${H} | `,(o==null?void 0:o.generated_at)&&`采集于 ${o.generated_at} | `,"共 ",Te," 条要闻"]})]}),l.jsxs("div",{style:{display:"flex",gap:8},children:[l.jsx("button",{className:"btn btn-g",onClick:()=>B(!I),style:{fontSize:12,padding:"6px 14px"},children:"⚙ 订阅配置"}),l.jsx("button",{className:"tpl-go",disabled:_,onClick:L,style:{fontSize:12,padding:"6px 14px"},children:f})]})]}),I&&j&&l.jsx(fp,{config:j,enabledSet:fe,onToggleCat:z,onAddKeyword:W,onRemoveKeyword:$,onAddFeed:F,onRemoveFeed:ee,onSave:T,onSetWebhook:X=>R({...j,feishu_webhook:X})}),Object.keys(me).length?l.jsx("div",{className:"mb-cats",children:Object.entries(me).map(([X,se])=>{if(!fe.has(X))return null;const ye=pc[X]||{icon:"📰",color:"var(--acc)"},ie=se.map(K=>{const we=((K.title||"")+(K.summary||"")).toLowerCase(),Ce=ue.filter(ge=>we.includes(ge)).length;return{...K,_kwHits:Ce}}).sort((K,we)=>we._kwHits-K._kwHits);return l.jsxs("div",{className:"mb-cat",children:[l.jsxs("div",{className:"mb-cat-hdr",children:[l.jsx("span",{className:"mb-cat-icon",children:ye.icon}),l.jsx("span",{className:"mb-cat-name",style:{color:ye.color},children:X}),l.jsxs("span",{className:"mb-cat-cnt",children:[ie.length," 条"]})]}),l.jsx("div",{className:"mb-news-list",children:ie.length?ie.map((K,we)=>{const Ce=!!(K.image&&K.image.startsWith("http"));return l.jsxs("div",{className:"mb-card",onClick:()=>window.open(K.link,"_blank"),children:[l.jsx("div",{className:"mb-img",children:Ce?l.jsx("img",{src:K.image,onError:ge=>{ge.target.style.display="none"},loading:"lazy",alt:""}):l.jsx("span",{children:ye.icon})}),l.jsxs("div",{className:"mb-info",children:[l.jsxs("div",{className:"mb-headline",children:[K.title,K._kwHits>0&&l.jsx("span",{style:{fontSize:9,padding:"1px 5px",borderRadius:999,background:"#a07aff22",color:"#a07aff",border:"1px solid #a07aff44",marginLeft:4},children:"⭐ 关注"})]}),l.jsx("div",{className:"mb-summary",children:K.summary||K.desc||""}),l.jsxs("div",{className:"mb-meta",children:[l.jsxs("span",{className:"mb-source",children:["📡 ",K.source||""]}),K.pub_date&&l.jsx("span",{className:"mb-time",children:K.pub_date.substring(0,16)})]})]})]},we)}):l.jsx("div",{className:"mb-empty",style:{padding:16},children:"暂无新闻"})})]},X)})}):l.jsx("div",{className:"mb-empty",children:"暂无数据,点击右上角「立即采集」获取今日简报"})]})}function fp({config:o,enabledSet:m,onToggleCat:c,onAddKeyword:y,onRemoveKeyword:w,onAddFeed:I,onRemoveFeed:B,onSave:j,onSetWebhook:R}){const[_,E]=Z.useState(""),[f,N]=Z.useState(""),[h,L]=Z.useState(""),[z,W]=Z.useState(Hi[0]),$=[...Hi];return(o.categories||[]).forEach(F=>{$.includes(F.name)||$.push(F.name)}),l.jsxs("div",{className:"sub-config",style:{marginBottom:20,padding:16,background:"var(--panel2)",borderRadius:12,border:"1px solid var(--line)"},children:[l.jsx("div",{style:{fontSize:14,fontWeight:700,marginBottom:12},children:"⚙ 订阅配置"}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"订阅分类"}),l.jsx("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:$.map(F=>{const ee=pc[F]||{icon:"📰"},T=m.has(F);return l.jsxs("div",{className:`sub-cat ${T?"active":""}`,onClick:()=>c(F),style:{cursor:"pointer",padding:"6px 12px",borderRadius:8,border:`1px solid ${T?"var(--acc)":"var(--line)"}`,display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{children:ee.icon}),l.jsx("span",{style:{fontSize:12},children:F}),T&&l.jsx("span",{style:{fontSize:10,color:"var(--ok)"},children:"✓"})]},F)})})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"关注关键词"}),l.jsx("div",{style:{display:"flex",gap:6,flexWrap:"wrap",marginBottom:6},children:(o.keywords||[]).map((F,ee)=>l.jsxs("span",{className:"sub-kw",style:{fontSize:11,padding:"2px 8px",borderRadius:4,background:"var(--bg)",border:"1px solid var(--line)"},children:[F,l.jsx("span",{style:{cursor:"pointer",marginLeft:4,color:"var(--danger)"},onClick:()=>w(ee),children:"✕"})]},ee))}),l.jsxs("div",{style:{display:"flex",gap:6},children:[l.jsx("input",{type:"text",value:_,onChange:F=>E(F.target.value),placeholder:"输入关键词",onKeyDown:F=>{F.key==="Enter"&&(y(_.trim()),E(""))},style:{flex:1,padding:"6px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12,outline:"none"}}),l.jsx("button",{className:"btn btn-g",onClick:()=>{y(_.trim()),E("")},style:{fontSize:11,padding:"4px 12px"},children:"添加"})]})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"自定义信息源"}),(o.custom_feeds||[]).map((F,ee)=>l.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center",marginBottom:4,fontSize:11},children:[l.jsx("span",{style:{fontWeight:600},children:F.name}),l.jsx("span",{style:{color:"var(--muted)",flex:1,overflow:"hidden",textOverflow:"ellipsis"},children:F.url}),l.jsx("span",{style:{color:"var(--acc)"},children:F.category}),l.jsx("span",{style:{cursor:"pointer",color:"var(--danger)"},onClick:()=>B(ee),children:"✕"})]},ee)),l.jsxs("div",{style:{display:"flex",gap:6,marginTop:6},children:[l.jsx("input",{placeholder:"源名称",value:f,onChange:F=>N(F.target.value),style:{width:100,padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"}}),l.jsx("input",{placeholder:"RSS / URL",value:h,onChange:F=>L(F.target.value),style:{flex:1,padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"}}),l.jsx("select",{value:z,onChange:F=>W(F.target.value),style:{padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"},children:$.map(F=>l.jsx("option",{value:F,children:F},F))}),l.jsx("button",{className:"btn btn-g",onClick:()=>{I(f,h,z),N(""),L("")},style:{fontSize:11,padding:"4px 12px"},children:"添加"})]})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:6},children:"飞书 Webhook"}),l.jsx("input",{type:"text",value:o.feishu_webhook||"",onChange:F=>R(F.target.value),placeholder:"https://open.feishu.cn/open-apis/bot/v2/hook/...",style:{width:"100%",padding:"8px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12,outline:"none"}})]}),l.jsx("div",{style:{display:"flex",justifyContent:"flex-end"},children:l.jsx("button",{className:"tpl-go",onClick:j,style:{fontSize:12,padding:"6px 16px"},children:"💾 保存配置"})})]})}const mc={main:"太子",zhongshu:"中书省",menxia:"门下省",shangshu:"尚书省",libu:"礼部",hubu:"户部",bingbu:"兵部",xingbu:"刑部",gongbu:"工部",libu_hr:"吏部",zaochao:"钦天监"},pp={Taizi:"中书省起草",Zhongshu:"门下省审议",Menxia:"尚书省派发",Assigned:"开始执行",Doing:"进入审查",Review:"完成"};function oc(o){const m=Math.max(0,o);if(m<60)return`${m}秒`;if(m<3600)return`${Math.floor(m/60)}分${m%60}秒`;const c=Math.floor(m/3600),y=Math.floor(m%3600/60);return`${c}小时${y}分`}function Vi(o){if(!o)return"";if(typeof o=="number"){const m=new Date(o);return`${String(m.getHours()).padStart(2,"0")}:${String(m.getMinutes()).padStart(2,"0")}:${String(m.getSeconds()).padStart(2,"0")}`}return typeof o=="string"&&o.length>=19?o.substring(11,19):String(o).substring(0,8)}function mp(){var U,te,V,g,P,ce,pe;const o=ne(u=>u.modalTaskId),m=ne(u=>u.setModalTaskId),c=ne(u=>u.liveStatus),y=ne(u=>u.loadAll),w=ne(u=>u.toast),[I,B]=Z.useState(null),[j,R]=Z.useState(null),_=Z.useRef(null),E=Z.useRef(null),f=((U=c==null?void 0:c.tasks)==null?void 0:U.find(u=>u.id===o))||null,N=Z.useCallback(async()=>{if(o)try{const u=await he.taskActivity(o);B(u)}catch{B(null)}},[o]),h=Z.useCallback(async()=>{if(o)try{const u=await he.schedulerState(o);R(u)}catch{R(null)}},[o]);if(Z.useEffect(()=>!o||!f?void 0:(N(),h(),["Done","Cancelled"].includes(f.state)||(_.current=setInterval(()=>{N(),h()},4e3)),()=>{_.current&&(clearInterval(_.current),_.current=null)}),[o,f==null?void 0:f.state,N,h]),Z.useEffect(()=>{E.current&&(E.current.scrollTop=E.current.scrollHeight)},[(te=I==null?void 0:I.activity)==null?void 0:te.length]),!o||!f)return null;const L=()=>m(null),z=Gi(f),W=z.find(u=>u.status==="active"),$=f.heartbeat||{status:"unknown",label:"⚪ 无数据"},F=f.flow_log||[],ee=f.todos||[],T=ee.filter(u=>u.status==="completed").length,fe=ee.length,ue=!["Done","Blocked","Cancelled"].includes(f.state),me=["Blocked","Cancelled"].includes(f.state),H=async(u,k)=>{try{const b=await he.taskAction(f.id,u,k);b.ok?(w(b.message||"操作成功","ok"),y(),L()):w(b.error||"操作失败","err")}catch{w("服务器连接失败","err")}},Te=async u=>{const k={approve:"准奏",reject:"封驳"},b=prompt(`${k[u]} ${f.id} + +请输入批注(可留空):`);if(b!==null)try{const oe=await he.reviewAction(f.id,u,b||"");oe.ok?(w(`✅ ${f.id} 已${k[u]}`,"ok"),y(),L()):w(oe.error||"操作失败","err")}catch{w("服务器连接失败","err")}},X=async()=>{const u=pp[f.state]||"下一步",k=prompt(`⏩ 手动推进 ${f.id} +当前: ${f.state} → 下一步: ${u} + +请输入说明(可留空):`);if(k!==null)try{const b=await he.advanceState(f.id,k||"");b.ok?(w(`⏩ ${b.message}`,"ok"),y(),L()):w(b.error||"推进失败","err")}catch{w("服务器连接失败","err")}},se=async u=>{if(u==="scan"){try{const ae=await he.schedulerScan(180);ae.ok?w(`🔍 扫描完成:${ae.count||0} 个动作`,"ok"):w(ae.error||"扫描失败","err"),h()}catch{w("服务器连接失败","err")}return}const b=prompt(`请输入${{retry:"重试",escalate:"升级",rollback:"回滚"}[u]}原因(可留空):`);if(b===null)return;const oe={retry:he.schedulerRetry,escalate:he.schedulerEscalate,rollback:he.schedulerRollback};try{const ae=await oe[u](f.id,b);ae.ok?w(ae.message||"操作成功","ok"):w(ae.error||"操作失败","err"),h(),y()}catch{w("服务器连接失败","err")}},ye=()=>{const u=prompt("请输入叫停原因(可留空):");u!==null&&H("stop",u)},ie=()=>{if(!confirm(`确定要取消 ${f.id} 吗?`))return;const u=prompt("请输入取消原因(可留空):");u!==null&&H("cancel",u)},K=j==null?void 0:j.scheduler,we=(j==null?void 0:j.stalledSec)||0,Ce=(j==null?void 0:j.stateAgeSec)||0,ge=(j==null?void 0:j.decision)||(K==null?void 0:K.decisionPacket)||null;return l.jsx("div",{className:"modal-bg open",onClick:L,children:l.jsxs("div",{className:"modal",onClick:u=>u.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:L,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{className:"modal-id",children:f.id}),l.jsx("div",{className:"modal-title",children:f.title||"(无标题)"}),W&&l.jsxs("div",{className:"cur-stage",children:[l.jsx("div",{className:"cs-icon",children:W.icon}),l.jsxs("div",{className:"cs-info",children:[l.jsx("div",{className:"cs-dept",style:{color:Ol(W.dept)},children:W.dept}),l.jsxs("div",{className:"cs-action",children:["当前阶段:",W.action]})]}),l.jsx("span",{className:`hb ${$.status} cs-hb`,children:$.label})]}),l.jsx("div",{className:"m-pipe",children:z.map((u,k)=>l.jsxs("div",{className:"mp-stage",children:[l.jsxs("div",{className:`mp-node ${u.status}`,children:[u.status==="done"&&l.jsx("div",{className:"mp-done-tick",children:"✓"}),l.jsx("div",{className:"mp-icon",children:u.icon}),l.jsx("div",{className:"mp-dept",style:u.status==="active"?{color:"var(--acc)"}:u.status==="done"?{color:"var(--ok)"}:{},children:u.dept}),l.jsx("div",{className:"mp-action",children:u.action})]}),kH("resume","恢复执行"),children:"▶️ 恢复执行"}),["Review","Menxia"].includes(f.state)&&l.jsxs(l.Fragment,{children:[l.jsx("button",{className:"btn-action",style:{background:"#2ecc8a22",color:"#2ecc8a",border:"1px solid #2ecc8a44"},onClick:()=>Te("approve"),children:"✅ 准奏"}),l.jsx("button",{className:"btn-action",style:{background:"#ff527022",color:"#ff5270",border:"1px solid #ff527044"},onClick:()=>Te("reject"),children:"🚫 封驳"})]}),["Pending","Taizi","Zhongshu","Menxia","Assigned","Doing","Review","Next"].includes(f.state)&&l.jsx("button",{className:"btn-action",style:{background:"#7c5cfc18",color:"#7c5cfc",border:"1px solid #7c5cfc44"},onClick:X,children:"⏩ 推进到下一步"})]}),l.jsxs("div",{className:"sched-section",children:[l.jsxs("div",{className:"sched-head",children:[l.jsx("span",{className:"sched-title",children:"🧭 太子调度"}),l.jsx("span",{className:"sched-status",children:K?`${K.enabled===!1?"已禁用":"运行中"} · 阈值 ${K.stallThresholdSec||180}s`:"加载中..."})]}),l.jsxs("div",{className:"sched-grid",children:[l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"停滞时长"}),l.jsx("div",{className:"v",children:oc(we)})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"状态驻留"}),l.jsx("div",{className:"v",children:oc(Ce)})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"重试次数"}),l.jsx("div",{className:"v",children:(K==null?void 0:K.retryCount)||0})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"升级级别"}),l.jsx("div",{className:"v",children:K!=null&&K.escalationLevel?K.escalationLevel===1?"门下省":"尚书省":"无"})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"派发状态"}),l.jsx("div",{className:"v",children:(K==null?void 0:K.lastDispatchStatus)||"idle"})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"控制态"}),l.jsx("div",{className:"v",children:(j==null?void 0:j.controlState)||(K==null?void 0:K.controlState)||"-"})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"写回状态"}),l.jsx("div",{className:"v",children:((V=j==null?void 0:j.writeback)==null?void 0:V.status)||((g=K==null?void 0:K.writeback)==null?void 0:g.status)||"idle"})]})]}),ge?l.jsxs("div",{className:"sched-line",style:{marginTop:10,display:"block",border:"1px solid #ffb74d44",borderRadius:10,padding:"10px 12px",background:"#ffb74d12"},children:[l.jsx("div",{style:{color:"#ffb74d",fontWeight:700,marginBottom:6},children:"🧾 拍板事项"}),l.jsx("div",{style:{marginBottom:8},children:ge.question}),(ge.options||[]).map((u,k)=>l.jsxs("div",{style:{marginBottom:6},children:[l.jsxs("strong",{children:[k+1,". ",u.label,ge.recommended===u.id?"(推荐)":""]}),l.jsx("div",{style:{color:"var(--muted)",fontSize:12},children:u.impact})]},`${u.id}-${k}`)),(ge.evidence||[]).length>0&&l.jsx("div",{style:{marginTop:8,color:"var(--muted)",fontSize:12},children:(ge.evidence||[]).slice(0,4).map((u,k)=>l.jsxs("div",{children:["- ",u]},k))})]}):l.jsx("div",{className:"sched-line",style:{marginTop:10,display:"block",border:"1px dashed #8a94a64d",borderRadius:10,padding:"8px 12px",color:"var(--muted)"},children:"当前无拍板事项(仅在门下省/审查节点停滞时触发)"}),K&&l.jsxs("div",{className:"sched-line",children:[K.lastProgressAt&&l.jsxs("span",{children:["最近进展 ",(K.lastProgressAt||"").replace("T"," ").substring(0,19)]}),K.lastDispatchAt&&l.jsxs("span",{children:["最近派发 ",(K.lastDispatchAt||"").replace("T"," ").substring(0,19)]}),l.jsxs("span",{children:["自动回滚 ",K.autoRollback===!1?"关闭":"开启"]}),K.lastDispatchAgent&&l.jsxs("span",{children:["目标 ",K.lastDispatchAgent]}),((P=j==null?void 0:j.lease)==null?void 0:P.ownerRunId)&&l.jsxs("span",{children:["租约 ",j.lease.ownerRunId]}),((ce=j==null?void 0:j.lastAction)==null?void 0:ce.reasonCode)&&l.jsxs("span",{children:["原因码 ",j.lastAction.reasonCode]}),((pe=j==null?void 0:j.writeback)==null?void 0:pe.lastError)&&l.jsxs("span",{children:["写回错误 ",j.writeback.lastError]})]}),l.jsxs("div",{className:"sched-actions",children:[l.jsx("button",{className:"sched-btn",onClick:()=>se("retry"),children:"🔁 重试派发"}),l.jsx("button",{className:"sched-btn warn",onClick:()=>se("escalate"),children:"📣 升级协调"}),l.jsx("button",{className:"sched-btn danger",onClick:()=>se("rollback"),children:"↩️ 回滚稳定点"}),l.jsx("button",{className:"sched-btn",onClick:()=>se("scan"),children:"🔍 立即扫描"})]})]}),fe>0&&l.jsx(hp,{todos:ee,todoDone:T,todoTotal:fe}),l.jsx("div",{className:"m-section",children:l.jsxs("div",{className:"m-rows",children:[l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"状态"}),l.jsxs("div",{className:"mr-val",children:[l.jsx("span",{className:`tag st-${f.state}`,children:Ki(f)}),(f.review_round||0)>0&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:8},children:["共磋商 ",f.review_round," 轮"]})]})]}),l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"执行部门"}),l.jsx("div",{className:"mr-val",children:l.jsx("span",{className:`tag dt-${(f.org||"").replace(/\s/g,"")}`,children:f.org||"—"})})]}),f.eta&&f.eta!=="-"&&l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"预计完成"}),l.jsx("div",{className:"mr-val",children:f.eta})]}),f.block&&f.block!=="无"&&f.block!=="-"&&l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",style:{color:"var(--danger)"},children:"阻塞项"}),l.jsx("div",{className:"mr-val",style:{color:"var(--danger)"},children:f.block})]}),f.now&&f.now!=="-"&&l.jsxs("div",{className:"m-row",style:{gridColumn:"1/-1"},children:[l.jsx("div",{className:"mr-label",children:"当前进展"}),l.jsx("div",{className:"mr-val",style:{fontWeight:400,fontSize:12},children:f.now})]}),f.ac&&l.jsxs("div",{className:"m-row",style:{gridColumn:"1/-1"},children:[l.jsx("div",{className:"mr-label",children:"验收标准"}),l.jsx("div",{className:"mr-val",style:{fontWeight:400,fontSize:12},children:f.ac})]})]})}),F.length>0&&l.jsxs("div",{className:"m-section",children:[l.jsxs("div",{className:"m-sec-label",children:["流转日志(",F.length," 条)"]}),l.jsx("div",{className:"fl-timeline",children:F.map((u,k)=>{const b=Ol(u.from||"");return l.jsxs("div",{className:"fl-item",children:[l.jsx("div",{className:"fl-time",children:u.at?u.at.substring(11,16):""}),l.jsx("div",{className:"fl-dot",style:{background:b}}),l.jsxs("div",{className:"fl-content",children:[l.jsxs("div",{className:"fl-who",children:[l.jsx("span",{className:"from",style:{color:b},children:u.from}),l.jsx("span",{style:{color:"var(--muted)"},children:" → "}),l.jsx("span",{className:"to",style:{color:Ol(u.to||"")},children:u.to})]}),l.jsx("div",{className:"fl-rem",children:u.remark})]})]},k)})})]}),f.output&&f.output!=="-"&&f.output!==""&&l.jsxs("div",{className:"m-section",children:[l.jsx("div",{className:"m-sec-label",children:"产出物"}),l.jsx("code",{children:f.output})]}),l.jsx(vp,{data:I,isDone:["Done","Cancelled"].includes(f.state),logRef:E})]})]})})}function hp({todos:o,todoDone:m,todoTotal:c}){return l.jsxs("div",{className:"todo-section",children:[l.jsxs("div",{className:"todo-header",children:[l.jsxs("div",{className:"m-sec-label",style:{marginBottom:0,border:"none",padding:0},children:["子任务清单(",m,"/",c,")"]}),l.jsxs("div",{className:"todo-progress",children:[l.jsx("div",{className:"todo-bar",children:l.jsx("div",{className:"todo-bar-fill",style:{width:`${Math.round(m/c*100)}%`}})}),l.jsxs("span",{children:[Math.round(m/c*100),"%"]})]})]}),l.jsx("div",{className:"todo-list",children:o.map(y=>{const w=y.status==="completed"?"✅":y.status==="in-progress"?"🔄":"⬜",I=y.status==="completed"?"已完成":y.status==="in-progress"?"进行中":"待开始",B=y.status==="completed"?"s-done":y.status==="in-progress"?"s-progress":"s-notstarted",j=y.status==="completed"?"done":"";return l.jsxs("div",{className:`todo-item ${j}`,children:[l.jsxs("div",{className:"t-row",children:[l.jsx("span",{className:"t-icon",children:w}),l.jsxs("span",{className:"t-id",children:["#",y.id]}),l.jsx("span",{className:"t-title",children:y.title}),l.jsx("span",{className:`t-status ${B}`,children:I})]}),y.detail&&l.jsx("div",{className:"todo-detail",children:y.detail})]},y.id)})})]})}function vp({data:o,isDone:m,logRef:c}){if(!o)return null;const y=o.activity||[],w=(()=>{if(!y.length)return!1;const L=y[y.length-1];if(!L.at)return!1;const z=typeof L.at=="number"?L.at:new Date(L.at).getTime();return Date.now()-z<3e5})(),I=[];o.agentLabel&&I.push(o.agentLabel),o.relatedAgents&&o.relatedAgents.length>1&&I.push(`${o.relatedAgents.length}个 Agent`),o.lastActive&&I.push(`最后活跃: ${o.lastActive}`);const B=o.phaseDurations||[],j=Math.max(...B.map(L=>L.durationSec||1),1),R={皇上:"#eab308",太子:"#f97316",中书省:"#3b82f6",门下省:"#8b5cf6",尚书省:"#10b981",六部:"#06b6d4",礼部:"#ec4899",户部:"#f59e0b",兵部:"#ef4444",刑部:"#6366f1",工部:"#14b8a6",吏部:"#d946ef"},_=o.todosSummary,E=o.resourceSummary,f=y.filter(L=>L.kind==="flow"),N=y.filter(L=>L.kind!=="flow"),h=new Map;return N.forEach(L=>{const z=L.agent||"unknown";h.has(z)||h.set(z,[]),h.get(z).push(L)}),l.jsxs("div",{className:"la-section",children:[l.jsxs("div",{className:"la-header",children:[l.jsxs("span",{className:"la-title",children:[l.jsx("span",{className:`la-dot${w?"":" idle"}`}),m?"执行回顾":"实时动态"]}),l.jsx("span",{className:"la-agent",children:I.join(" · ")||"加载中..."})]}),B.length>0&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)"},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:6},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"⏱ 阶段耗时"}),o.totalDuration&&l.jsxs("span",{style:{marginLeft:"auto",fontSize:10,color:"var(--muted)"},children:["总耗时 ",o.totalDuration]})]}),B.map((L,z)=>{const W=Math.max(5,Math.round((L.durationSec||1)/j*100)),$=R[L.phase]||"#6b7280";return l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,margin:"2px 0",fontSize:11},children:[l.jsx("span",{style:{minWidth:48,color:"var(--muted)",textAlign:"right"},children:L.phase}),l.jsx("div",{style:{flex:1,height:14,background:"var(--panel)",borderRadius:3,overflow:"hidden"},children:l.jsx("div",{style:{width:`${W}%`,height:"100%",background:$,borderRadius:3,opacity:L.ongoing?.6:.85}})}),l.jsxs("span",{style:{minWidth:60,fontSize:10,color:"var(--muted)"},children:[L.durationText,L.ongoing&&l.jsx("span",{style:{fontSize:9,color:"#60a5fa"},children:" ●进行中"})]})]},z)})]}),_&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)"},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:4},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"📊 执行进度"}),l.jsxs("span",{style:{fontSize:20,fontWeight:700,color:_.percent>=100?"#22c55e":_.percent>=50?"#60a5fa":"var(--text)"},children:[_.percent,"%"]}),l.jsxs("span",{style:{fontSize:10,color:"var(--muted)"},children:["✅",_.completed," 🔄",_.inProgress," ⬜",_.notStarted," / 共",_.total,"项"]})]}),l.jsxs("div",{style:{height:8,background:"var(--panel)",borderRadius:4,overflow:"hidden",display:"flex"},children:[l.jsx("div",{style:{width:`${_.total?_.completed/_.total*100:0}%`,background:"#22c55e",transition:"width .3s"}}),l.jsx("div",{style:{width:`${_.total?_.inProgress/_.total*100:0}%`,background:"#3b82f6",transition:"width .3s"}})]})]}),E&&(E.totalTokens||E.totalCost)&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)",display:"flex",gap:12,alignItems:"center"},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"📈 资源消耗"}),E.totalTokens!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["🔢 ",E.totalTokens.toLocaleString()," tokens"]}),E.totalCost!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["💰 $",E.totalCost.toFixed(4)]}),E.totalElapsedSec!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["⏳ ",E.totalElapsedSec>=60?`${Math.floor(E.totalElapsedSec/60)}分`:"",E.totalElapsedSec%60,"秒"]})]}),l.jsxs("div",{className:"la-log",ref:c,children:[f.length>0&&l.jsx("div",{className:"la-flow-wrap",children:f.map((L,z)=>l.jsxs("div",{className:"la-entry la-tool",children:[l.jsx("span",{className:"la-icon",children:"📋"}),l.jsxs("span",{className:"la-body",children:[l.jsx("b",{children:L.from})," → ",l.jsx("b",{children:L.to})," ",L.remark||""]}),l.jsx("span",{className:"la-time",children:Vi(L.at)})]},`flow-${z}`))}),h.size>0?l.jsx("div",{className:"la-groups",children:Array.from(h.entries()).map(([L,z])=>{const W=mc[L]||L||"未标识",$=z[z.length-1],F=$!=null&&$.at?Vi($.at):"--:--:--";return l.jsxs("div",{className:"la-group",children:[l.jsxs("div",{className:"la-group-hd",children:[l.jsx("span",{className:"name",children:W}),l.jsxs("span",{children:["最近更新 ",F]})]}),l.jsx("div",{className:"la-group-bd",children:z.map((ee,T)=>l.jsx(gp,{entry:ee},T))})]},L)})}):!f.length&&l.jsx("div",{className:"la-empty",children:o.message||o.error||"Agent 尚未上报进展(等待 Agent 调用 progress 命令)"})]})]})}function gp({entry:o}){var y,w,I;const m=Vi(o.at),c=o.agent?l.jsx("span",{style:{fontSize:9,color:"var(--muted)",background:"var(--panel)",padding:"1px 4px",borderRadius:3,marginRight:4},children:mc[o.agent]||o.agent}):null;if(o.kind==="progress")return l.jsxs("div",{className:"la-entry la-assistant",children:[l.jsx("span",{className:"la-icon",children:"🔄"}),l.jsxs("span",{className:"la-body",children:[c,l.jsx("b",{children:"当前进展:"}),o.text]}),l.jsx("span",{className:"la-time",children:m})]});if(o.kind==="todos"){const B=o.items||[],j=new Map;return o.diff&&((o.diff.changed||[]).forEach(R=>j.set(R.id,{type:"changed",from:R.from,to:R.to})),(o.diff.added||[]).forEach(R=>j.set(R.id,{type:"added"}))),l.jsxs("div",{className:"la-entry",style:{flexDirection:"column",alignItems:"flex-start",gap:2},children:[l.jsxs("div",{style:{fontSize:11,color:"var(--muted)",marginBottom:2},children:[c,"📝 执行计划"]}),B.map(R=>{const _=R.status==="completed"?"✅":R.status==="in-progress"?"🔄":"⬜",E=j.get(String(R.id)),f=R.status==="completed"?{opacity:.5,textDecoration:"line-through"}:R.status==="in-progress"?{color:"#60a5fa",fontWeight:"bold"}:{};return l.jsxs("div",{style:f,children:[_," ",R.title,E&&E.type==="changed"&&E.to==="completed"&&l.jsx("span",{style:{color:"#22c55e",fontSize:9,marginLeft:4},children:"✨刚完成"}),E&&E.type==="changed"&&E.to!=="completed"&&l.jsxs("span",{style:{color:"#f59e0b",fontSize:9,marginLeft:4},children:["↻",E.from,"→",E.to]}),E&&E.type==="added"&&l.jsx("span",{style:{color:"#3b82f6",fontSize:9,marginLeft:4},children:"🆕新增"})]},R.id)}),(w=(y=o.diff)==null?void 0:y.removed)==null?void 0:w.map(R=>l.jsxs("div",{style:{opacity:.4,textDecoration:"line-through"},children:["🗑 ",R.title]},R.id))]})}if(o.kind==="assistant")return l.jsxs(l.Fragment,{children:[o.thinking&&l.jsxs("div",{className:"la-entry la-thinking",children:[l.jsx("span",{className:"la-icon",children:"💭"}),l.jsxs("span",{className:"la-body",children:[c,o.thinking]}),l.jsx("span",{className:"la-time",children:m})]}),(I=o.tools)==null?void 0:I.map((B,j)=>l.jsxs("div",{className:"la-entry la-tool",children:[l.jsx("span",{className:"la-icon",children:"🔧"}),l.jsxs("span",{className:"la-body",children:[c,l.jsx("span",{className:"la-tool-name",children:B.name}),l.jsx("span",{className:"la-trunc",children:B.input_preview||""})]}),l.jsx("span",{className:"la-time",children:m})]},j)),o.text&&l.jsxs("div",{className:"la-entry la-assistant",children:[l.jsx("span",{className:"la-icon",children:"🤖"}),l.jsxs("span",{className:"la-body",children:[c,o.text]}),l.jsx("span",{className:"la-time",children:m})]})]});if(o.kind==="tool_result"){const B=o.exitCode===0||o.exitCode===null||o.exitCode===void 0;return l.jsxs("div",{className:`la-entry la-tool-result ${B?"ok":"err"}`,children:[l.jsx("span",{className:"la-icon",children:B?"✅":"❌"}),l.jsxs("span",{className:"la-body",children:[c,l.jsx("span",{className:"la-tool-name",children:o.tool||""}),o.output?o.output.substring(0,150):""]}),l.jsx("span",{className:"la-time",children:m})]})}return o.kind==="user"?l.jsxs("div",{className:"la-entry la-user",children:[l.jsx("span",{className:"la-icon",children:"📥"}),l.jsxs("span",{className:"la-body",children:[c,o.text||""]}),l.jsx("span",{className:"la-time",children:m})]}):null}function yp(){const o=ne(m=>m.toasts);return o.length?l.jsx("div",{className:"toaster",children:o.map(m=>l.jsx("div",{className:`toast ${m.type}`,children:m.msg},m.id))}):null}function xp(){const o=ne(L=>L.liveStatus),[m,c]=Z.useState(!1),[y,w]=Z.useState(!1);Z.useEffect(()=>{const L=localStorage.getItem("openclaw_court_date"),z=new Date().toISOString().substring(0,10);if(!JSON.parse(localStorage.getItem("openclaw_court_pref")||'{"enabled":true}').enabled||L===z)return;localStorage.setItem("openclaw_court_date",z),c(!0);const $=setTimeout(()=>I(),3500);return()=>clearTimeout($)},[]);const I=()=>{w(!0),setTimeout(()=>c(!1),500)};if(!m)return null;const j=((o==null?void 0:o.tasks)||[]).filter(Jt),R=j.filter(L=>!["Done","Cancelled"].includes(L.state)).length,_=j.filter(L=>L.state==="Done").length,E=j.filter(L=>L.state!=="Done"&&L.state!=="Cancelled"&&L.eta&&new Date(L.eta.replace(" ","T"))0&&` · ⚠ 超期 ${E} 件`]}),l.jsx("div",{className:"crm-date in",children:h}),l.jsx("div",{className:"crm-skip",children:"点击任意处跳过"})]})}function kp(){const o=ne(f=>f.activeTab),m=ne(f=>f.setActiveTab),c=ne(f=>f.liveStatus),y=ne(f=>f.countdown),w=ne(f=>f.loadAll);Z.useEffect(()=>(Wf(),()=>Uf()),[]);const I=(c==null?void 0:c.tasks)||[],B=I.filter(Jt),j=B.filter(f=>!Fl(f)),R=c==null?void 0:c.syncStatus,_=R==null?void 0:R.ok,E=f=>f==="edicts"?String(j.length):f==="sessions"?String(I.filter(N=>!Jt(N)).length):f==="memorials"?String(B.filter(N=>["Done","Cancelled"].includes(N.state)).length):f==="monitor"?I.filter(h=>Jt(h)&&h.state==="Doing").length+"活跃":"";return l.jsxs("div",{className:"wrap",children:[l.jsxs("div",{className:"hdr",children:[l.jsxs("div",{children:[l.jsx("div",{className:"logo",children:"三省六部 · 总控台"}),l.jsx("div",{className:"sub-text",children:"OpenClaw Sansheng-Liubu Dashboard"})]}),l.jsxs("div",{className:"hdr-r",children:[l.jsx("span",{className:`chip ${_?"ok":_===!1?"err":""}`,children:_?"✅ 同步正常":_===!1?"❌ 服务器未启动":"⏳ 连接中…"}),l.jsxs("span",{className:"chip",children:[j.length," 道旨意"]}),l.jsx("button",{className:"btn-refresh",onClick:()=>w(),children:"⟳ 刷新"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["⟳ ",y,"s"]})]})]}),l.jsx("div",{className:"tabs",children:Af.map(f=>l.jsxs("div",{className:`tab ${o===f.key?"active":""}`,onClick:()=>m(f.key),children:[f.icon," ",f.label,E(f.key)&&l.jsx("span",{className:"tbadge",children:E(f.key)})]},f.key))}),o==="edicts"&&l.jsx(Kf,{}),o==="monitor"&&l.jsx(Gf,{}),o==="officials"&&l.jsx(Xf,{}),o==="models"&&l.jsx(Jf,{}),o==="skills"&&l.jsx(tp,{}),o==="sessions"&&l.jsx(lp,{}),o==="memorials"&&l.jsx(ip,{}),o==="templates"&&l.jsx(cp,{}),o==="morning"&&l.jsx(dp,{}),l.jsx(mp,{}),l.jsx(yp,{}),l.jsx(xp,{})]})}jf.createRoot(document.getElementById("root")).render(l.jsx(ac.StrictMode,{children:l.jsx(kp,{})})); diff --git a/dashboard/dist/assets/index-CqMbmm5B.js b/dashboard/dist/assets/index-CqMbmm5B.js deleted file mode 100644 index d0a0215b..00000000 --- a/dashboard/dist/assets/index-CqMbmm5B.js +++ /dev/null @@ -1,84 +0,0 @@ -(function(){const p=document.createElement("link").relList;if(p&&p.supports&&p.supports("modulepreload"))return;for(const S of document.querySelectorAll('link[rel="modulepreload"]'))k(S);new MutationObserver(S=>{for(const D of S)if(D.type==="childList")for(const F of D.addedNodes)F.tagName==="LINK"&&F.rel==="modulepreload"&&k(F)}).observe(document,{childList:!0,subtree:!0});function u(S){const D={};return S.integrity&&(D.integrity=S.integrity),S.referrerPolicy&&(D.referrerPolicy=S.referrerPolicy),S.crossOrigin==="use-credentials"?D.credentials="include":S.crossOrigin==="anonymous"?D.credentials="omit":D.credentials="same-origin",D}function k(S){if(S.ep)return;S.ep=!0;const D=u(S);fetch(S.href,D)}})();function Qi(o){return o&&o.__esModule&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o}var Mi={exports:{}},Cr={},Ai={exports:{}},de={};/** - * @license React - * react.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Hu;function df(){if(Hu)return de;Hu=1;var o=Symbol.for("react.element"),p=Symbol.for("react.portal"),u=Symbol.for("react.fragment"),k=Symbol.for("react.strict_mode"),S=Symbol.for("react.profiler"),D=Symbol.for("react.provider"),F=Symbol.for("react.context"),_=Symbol.for("react.forward_ref"),R=Symbol.for("react.suspense"),C=Symbol.for("react.memo"),z=Symbol.for("react.lazy"),d=Symbol.iterator;function w(v){return v===null||typeof v!="object"?null:(v=d&&v[d]||v["@@iterator"],typeof v=="function"?v:null)}var m={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},T=Object.assign,g={};function M(v,L,ae){this.props=v,this.context=L,this.refs=g,this.updater=ae||m}M.prototype.isReactComponent={},M.prototype.setState=function(v,L){if(typeof v!="object"&&typeof v!="function"&&v!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,v,L,"setState")},M.prototype.forceUpdate=function(v){this.updater.enqueueForceUpdate(this,v,"forceUpdate")};function P(){}P.prototype=M.prototype;function B(v,L,ae){this.props=v,this.context=L,this.refs=g,this.updater=ae||m}var Z=B.prototype=new P;Z.constructor=B,T(Z,M.prototype),Z.isPureReactComponent=!0;var I=Array.isArray,oe=Object.prototype.hasOwnProperty,ue={current:null},pe={key:!0,ref:!0,__self:!0,__source:!0};function me(v,L,ae){var ce,x={},U=null,ee=null;if(L!=null)for(ce in L.ref!==void 0&&(ee=L.ref),L.key!==void 0&&(U=""+L.key),L)oe.call(L,ce)&&!pe.hasOwnProperty(ce)&&(x[ce]=L[ce]);var he=arguments.length-2;if(he===1)x.children=ae;else if(1>>1,L=E[v];if(0>>1;vS(x,W))US(ee,x)?(E[v]=ee,E[U]=W,v=U):(E[v]=x,E[ce]=W,v=ce);else if(US(ee,W))E[v]=ee,E[U]=W,v=U;else break e}}return Q}function S(E,Q){var W=E.sortIndex-Q.sortIndex;return W!==0?W:E.id-Q.id}if(typeof performance=="object"&&typeof performance.now=="function"){var D=performance;o.unstable_now=function(){return D.now()}}else{var F=Date,_=F.now();o.unstable_now=function(){return F.now()-_}}var R=[],C=[],z=1,d=null,w=3,m=!1,T=!1,g=!1,M=typeof setTimeout=="function"?setTimeout:null,P=typeof clearTimeout=="function"?clearTimeout:null,B=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function Z(E){for(var Q=u(C);Q!==null;){if(Q.callback===null)k(C);else if(Q.startTime<=E)k(C),Q.sortIndex=Q.expirationTime,p(R,Q);else break;Q=u(C)}}function I(E){if(g=!1,Z(E),!T)if(u(R)!==null)T=!0,_e(oe);else{var Q=u(C);Q!==null&&xe(I,Q.startTime-E)}}function oe(E,Q){T=!1,g&&(g=!1,P(me),me=-1),m=!0;var W=w;try{for(Z(Q),d=u(R);d!==null&&(!(d.expirationTime>Q)||E&&!le());){var v=d.callback;if(typeof v=="function"){d.callback=null,w=d.priorityLevel;var L=v(d.expirationTime<=Q);Q=o.unstable_now(),typeof L=="function"?d.callback=L:d===u(R)&&k(R),Z(Q)}else k(R);d=u(R)}if(d!==null)var ae=!0;else{var ce=u(C);ce!==null&&xe(I,ce.startTime-Q),ae=!1}return ae}finally{d=null,w=W,m=!1}}var ue=!1,pe=null,me=-1,be=5,X=-1;function le(){return!(o.unstable_now()-XE||125v?(E.sortIndex=W,p(C,E),u(R)===null&&E===u(C)&&(g?(P(me),me=-1):g=!0,xe(I,W-v))):(E.sortIndex=L,p(R,E),T||m||(T=!0,_e(oe))),E},o.unstable_shouldYield=le,o.unstable_wrapCallback=function(E){var Q=w;return function(){var W=w;w=Q;try{return E.apply(this,arguments)}finally{w=W}}}})($i)),$i}var Yu;function hf(){return Yu||(Yu=1,bi.exports=mf()),bi.exports}/** - * @license React - * react-dom.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Xu;function vf(){if(Xu)return nt;Xu=1;var o=_r(),p=hf();function u(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),R=Object.prototype.hasOwnProperty,C=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,z={},d={};function w(e){return R.call(d,e)?!0:R.call(z,e)?!1:C.test(e)?d[e]=!0:(z[e]=!0,!1)}function m(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function T(e,t,n,r){if(t===null||typeof t>"u"||m(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function g(e,t,n,r,s,i,a){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=s,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=i,this.removeEmptyString=a}var M={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){M[e]=new g(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];M[t]=new g(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){M[e]=new g(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){M[e]=new g(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){M[e]=new g(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){M[e]=new g(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){M[e]=new g(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){M[e]=new g(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){M[e]=new g(e,5,!1,e.toLowerCase(),null,!1,!1)});var P=/[\-:]([a-z])/g;function B(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(P,B);M[t]=new g(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(P,B);M[t]=new g(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(P,B);M[t]=new g(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){M[e]=new g(e,1,!1,e.toLowerCase(),null,!1,!1)}),M.xlinkHref=new g("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){M[e]=new g(e,1,!1,e.toLowerCase(),null,!0,!0)});function Z(e,t,n,r){var s=M.hasOwnProperty(t)?M[t]:null;(s!==null?s.type!==0:r||!(2c||s[a]!==i[c]){var f=` -`+s[a].replace(" at new "," at ");return e.displayName&&f.includes("")&&(f=f.replace("",e.displayName)),f}while(1<=a&&0<=c);break}}}finally{ae=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?L(e):""}function x(e){switch(e.tag){case 5:return L(e.type);case 16:return L("Lazy");case 13:return L("Suspense");case 19:return L("SuspenseList");case 0:case 2:case 15:return e=ce(e.type,!1),e;case 11:return e=ce(e.type.render,!1),e;case 1:return e=ce(e.type,!0),e;default:return""}}function U(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case pe:return"Fragment";case ue:return"Portal";case be:return"Profiler";case me:return"StrictMode";case se:return"Suspense";case J:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case le:return(e.displayName||"Context")+".Consumer";case X:return(e._context.displayName||"Context")+".Provider";case ke:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Ne:return t=e.displayName||null,t!==null?t:U(e.type)||"Memo";case _e:t=e._payload,e=e._init;try{return U(e(t))}catch{}}return null}function ee(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return U(t);case 8:return t===me?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function he(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function ve(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Me(e){var t=ve(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var s=n.get,i=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return s.call(this)},set:function(a){r=""+a,i.call(this,a)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(a){r=""+a},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function mn(e){e._valueTracker||(e._valueTracker=Me(e))}function Yi(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=ve(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function Tr(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Wl(e,t){var n=t.checked;return W({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function Xi(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=he(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Zi(e,t){t=t.checked,t!=null&&Z(e,"checked",t,!1)}function Ul(e,t){Zi(e,t);var n=he(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Hl(e,t.type,n):t.hasOwnProperty("defaultValue")&&Hl(e,t.type,he(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function qi(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Hl(e,t,n){(t!=="number"||Tr(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Fn=Array.isArray;function hn(e,t,n,r){if(e=e.options,t){t={};for(var s=0;s"+t.valueOf().toString()+"",t=Lr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Bn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Wn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},mc=["Webkit","ms","Moz","O"];Object.keys(Wn).forEach(function(e){mc.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Wn[t]=Wn[e]})});function lo(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Wn.hasOwnProperty(e)&&Wn[e]?(""+t).trim():t+"px"}function so(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,s=lo(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,s):e[n]=s}}var hc=W({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Kl(e,t){if(t){if(hc[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(u(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(u(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(u(61))}if(t.style!=null&&typeof t.style!="object")throw Error(u(62))}}function Gl(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Yl=null;function Xl(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Zl=null,vn=null,gn=null;function io(e){if(e=cr(e)){if(typeof Zl!="function")throw Error(u(280));var t=e.stateNode;t&&(t=Jr(t),Zl(e.stateNode,e.type,t))}}function oo(e){vn?gn?gn.push(e):gn=[e]:vn=e}function ao(){if(vn){var e=vn,t=gn;if(gn=vn=null,io(e),t)for(e=0;e>>=0,e===0?32:31-(Ec(e)/_c|0)|0}var Mr=64,Ar=4194304;function Qn(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Or(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,s=e.suspendedLanes,i=e.pingedLanes,a=n&268435455;if(a!==0){var c=a&~s;c!==0?r=Qn(c):(i&=a,i!==0&&(r=Qn(i)))}else a=n&~s,a!==0?r=Qn(a):i!==0&&(r=Qn(i));if(r===0)return 0;if(t!==0&&t!==r&&(t&s)===0&&(s=r&-r,i=t&-t,s>=i||s===16&&(i&4194240)!==0))return t;if((r&4)!==0&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function Kn(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-pt(t),e[t]=n}function Rc(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=tr),Oo=" ",bo=!1;function $o(e,t){switch(e){case"keyup":return sd.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Fo(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var kn=!1;function od(e,t){switch(e){case"compositionend":return Fo(t);case"keypress":return t.which!==32?null:(bo=!0,Oo);case"textInput":return e=t.data,e===Oo&&bo?null:e;default:return null}}function ad(e,t){if(kn)return e==="compositionend"||!hs&&$o(e,t)?(e=Ro(),Wr=us=bt=null,kn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Ko(n)}}function Yo(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Yo(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Xo(){for(var e=window,t=Tr();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Tr(e.document)}return t}function ys(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function gd(e){var t=Xo(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Yo(n.ownerDocument.documentElement,n)){if(r!==null&&ys(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var s=n.textContent.length,i=Math.min(r.start,s);r=r.end===void 0?i:Math.min(r.end,s),!e.extend&&i>r&&(s=r,r=i,i=s),s=Go(n,i);var a=Go(n,r);s&&a&&(e.rangeCount!==1||e.anchorNode!==s.node||e.anchorOffset!==s.offset||e.focusNode!==a.node||e.focusOffset!==a.offset)&&(t=t.createRange(),t.setStart(s.node,s.offset),e.removeAllRanges(),i>r?(e.addRange(t),e.extend(a.node,a.offset)):(t.setEnd(a.node,a.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,jn=null,xs=null,sr=null,ks=!1;function Zo(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;ks||jn==null||jn!==Tr(r)||(r=jn,"selectionStart"in r&&ys(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),sr&&lr(sr,r)||(sr=r,r=Xr(xs,"onSelect"),0En||(e.current=Ps[En],Ps[En]=null,En--)}function we(e,t){En++,Ps[En]=e.current,e.current=t}var Wt={},Ve=Bt(Wt),Ze=Bt(!1),nn=Wt;function _n(e,t){var n=e.type.contextTypes;if(!n)return Wt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var s={},i;for(i in n)s[i]=t[i];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=s),s}function qe(e){return e=e.childContextTypes,e!=null}function el(){Ee(Ze),Ee(Ve)}function fa(e,t,n){if(Ve.current!==Wt)throw Error(u(168));we(Ve,t),we(Ze,n)}function pa(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var s in r)if(!(s in t))throw Error(u(108,ee(e)||"Unknown",s));return W({},n,r)}function tl(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Wt,nn=Ve.current,we(Ve,e),we(Ze,Ze.current),!0}function ma(e,t,n){var r=e.stateNode;if(!r)throw Error(u(169));n?(e=pa(e,t,nn),r.__reactInternalMemoizedMergedChildContext=e,Ee(Ze),Ee(Ve),we(Ve,e)):Ee(Ze),we(Ze,n)}var _t=null,nl=!1,Is=!1;function ha(e){_t===null?_t=[e]:_t.push(e)}function Td(e){nl=!0,ha(e)}function Ut(){if(!Is&&_t!==null){Is=!0;var e=0,t=je;try{var n=_t;for(je=1;e>=a,s-=a,zt=1<<32-pt(t)+s|n<ie?(Be=re,re=null):Be=re.sibling;var ye=A(y,re,j[ie],$);if(ye===null){re===null&&(re=Be);break}e&&re&&ye.alternate===null&&t(y,re),h=i(ye,h,ie),ne===null?Y=ye:ne.sibling=ye,ne=ye,re=Be}if(ie===j.length)return n(y,re),ze&&ln(y,ie),Y;if(re===null){for(;ieie?(Be=re,re=null):Be=re.sibling;var qt=A(y,re,ye.value,$);if(qt===null){re===null&&(re=Be);break}e&&re&&qt.alternate===null&&t(y,re),h=i(qt,h,ie),ne===null?Y=qt:ne.sibling=qt,ne=qt,re=Be}if(ye.done)return n(y,re),ze&&ln(y,ie),Y;if(re===null){for(;!ye.done;ie++,ye=j.next())ye=b(y,ye.value,$),ye!==null&&(h=i(ye,h,ie),ne===null?Y=ye:ne.sibling=ye,ne=ye);return ze&&ln(y,ie),Y}for(re=r(y,re);!ye.done;ie++,ye=j.next())ye=H(re,y,ie,ye.value,$),ye!==null&&(e&&ye.alternate!==null&&re.delete(ye.key===null?ie:ye.key),h=i(ye,h,ie),ne===null?Y=ye:ne.sibling=ye,ne=ye);return e&&re.forEach(function(cf){return t(y,cf)}),ze&&ln(y,ie),Y}function Ie(y,h,j,$){if(typeof j=="object"&&j!==null&&j.type===pe&&j.key===null&&(j=j.props.children),typeof j=="object"&&j!==null){switch(j.$$typeof){case oe:e:{for(var Y=j.key,ne=h;ne!==null;){if(ne.key===Y){if(Y=j.type,Y===pe){if(ne.tag===7){n(y,ne.sibling),h=s(ne,j.props.children),h.return=y,y=h;break e}}else if(ne.elementType===Y||typeof Y=="object"&&Y!==null&&Y.$$typeof===_e&&ja(Y)===ne.type){n(y,ne.sibling),h=s(ne,j.props),h.ref=dr(y,ne,j),h.return=y,y=h;break e}n(y,ne);break}else t(y,ne);ne=ne.sibling}j.type===pe?(h=pn(j.props.children,y.mode,$,j.key),h.return=y,y=h):($=Ll(j.type,j.key,j.props,null,y.mode,$),$.ref=dr(y,h,j),$.return=y,y=$)}return a(y);case ue:e:{for(ne=j.key;h!==null;){if(h.key===ne)if(h.tag===4&&h.stateNode.containerInfo===j.containerInfo&&h.stateNode.implementation===j.implementation){n(y,h.sibling),h=s(h,j.children||[]),h.return=y,y=h;break e}else{n(y,h);break}else t(y,h);h=h.sibling}h=Li(j,y.mode,$),h.return=y,y=h}return a(y);case _e:return ne=j._init,Ie(y,h,ne(j._payload),$)}if(Fn(j))return K(y,h,j,$);if(Q(j))return G(y,h,j,$);il(y,j)}return typeof j=="string"&&j!==""||typeof j=="number"?(j=""+j,h!==null&&h.tag===6?(n(y,h.sibling),h=s(h,j),h.return=y,y=h):(n(y,h),h=Ti(j,y.mode,$),h.return=y,y=h),a(y)):n(y,h)}return Ie}var Rn=Sa(!0),wa=Sa(!1),ol=Bt(null),al=null,Pn=null,$s=null;function Fs(){$s=Pn=al=null}function Bs(e){var t=ol.current;Ee(ol),e._currentValue=t}function Ws(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function In(e,t){al=e,$s=Pn=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(Je=!0),e.firstContext=null)}function ut(e){var t=e._currentValue;if($s!==e)if(e={context:e,memoizedValue:t,next:null},Pn===null){if(al===null)throw Error(u(308));Pn=e,al.dependencies={lanes:0,firstContext:e}}else Pn=Pn.next=e;return t}var sn=null;function Us(e){sn===null?sn=[e]:sn.push(e)}function Na(e,t,n,r){var s=t.interleaved;return s===null?(n.next=n,Us(t)):(n.next=s.next,s.next=n),t.interleaved=n,Lt(e,r)}function Lt(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var Ht=!1;function Hs(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Ca(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Rt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function Vt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,(ge&2)!==0){var s=r.pending;return s===null?t.next=t:(t.next=s.next,s.next=t),r.pending=t,Lt(e,n)}return s=r.interleaved,s===null?(t.next=t,Us(r)):(t.next=s.next,s.next=t),r.interleaved=t,Lt(e,n)}function ul(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ls(e,n)}}function Ea(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var s=null,i=null;if(n=n.firstBaseUpdate,n!==null){do{var a={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};i===null?s=i=a:i=i.next=a,n=n.next}while(n!==null);i===null?s=i=t:i=i.next=t}else s=i=t;n={baseState:r.baseState,firstBaseUpdate:s,lastBaseUpdate:i,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function cl(e,t,n,r){var s=e.updateQueue;Ht=!1;var i=s.firstBaseUpdate,a=s.lastBaseUpdate,c=s.shared.pending;if(c!==null){s.shared.pending=null;var f=c,N=f.next;f.next=null,a===null?i=N:a.next=N,a=f;var O=e.alternate;O!==null&&(O=O.updateQueue,c=O.lastBaseUpdate,c!==a&&(c===null?O.firstBaseUpdate=N:c.next=N,O.lastBaseUpdate=f))}if(i!==null){var b=s.baseState;a=0,O=N=f=null,c=i;do{var A=c.lane,H=c.eventTime;if((r&A)===A){O!==null&&(O=O.next={eventTime:H,lane:0,tag:c.tag,payload:c.payload,callback:c.callback,next:null});e:{var K=e,G=c;switch(A=t,H=n,G.tag){case 1:if(K=G.payload,typeof K=="function"){b=K.call(H,b,A);break e}b=K;break e;case 3:K.flags=K.flags&-65537|128;case 0:if(K=G.payload,A=typeof K=="function"?K.call(H,b,A):K,A==null)break e;b=W({},b,A);break e;case 2:Ht=!0}}c.callback!==null&&c.lane!==0&&(e.flags|=64,A=s.effects,A===null?s.effects=[c]:A.push(c))}else H={eventTime:H,lane:A,tag:c.tag,payload:c.payload,callback:c.callback,next:null},O===null?(N=O=H,f=b):O=O.next=H,a|=A;if(c=c.next,c===null){if(c=s.shared.pending,c===null)break;A=c,c=A.next,A.next=null,s.lastBaseUpdate=A,s.shared.pending=null}}while(!0);if(O===null&&(f=b),s.baseState=f,s.firstBaseUpdate=N,s.lastBaseUpdate=O,t=s.shared.interleaved,t!==null){s=t;do a|=s.lane,s=s.next;while(s!==t)}else i===null&&(s.shared.lanes=0);un|=a,e.lanes=a,e.memoizedState=b}}function _a(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Ys.transition;Ys.transition={};try{e(!1),t()}finally{je=n,Ys.transition=r}}function Qa(){return ct().memoizedState}function Id(e,t,n){var r=Yt(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},Ka(e))Ga(t,n);else if(n=Na(e,t,n,r),n!==null){var s=Xe();xt(n,e,r,s),Ya(n,t,r)}}function Dd(e,t,n){var r=Yt(e),s={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(Ka(e))Ga(t,s);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var a=t.lastRenderedState,c=i(a,n);if(s.hasEagerState=!0,s.eagerState=c,mt(c,a)){var f=t.interleaved;f===null?(s.next=s,Us(t)):(s.next=f.next,f.next=s),t.interleaved=s;return}}catch{}finally{}n=Na(e,t,s,r),n!==null&&(s=Xe(),xt(n,e,r,s),Ya(n,t,r))}}function Ka(e){var t=e.alternate;return e===Le||t!==null&&t===Le}function Ga(e,t){hr=pl=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Ya(e,t,n){if((n&4194240)!==0){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ls(e,n)}}var vl={readContext:ut,useCallback:Qe,useContext:Qe,useEffect:Qe,useImperativeHandle:Qe,useInsertionEffect:Qe,useLayoutEffect:Qe,useMemo:Qe,useReducer:Qe,useRef:Qe,useState:Qe,useDebugValue:Qe,useDeferredValue:Qe,useTransition:Qe,useMutableSource:Qe,useSyncExternalStore:Qe,useId:Qe,unstable_isNewReconciler:!1},Md={readContext:ut,useCallback:function(e,t){return Nt().memoizedState=[e,t===void 0?null:t],e},useContext:ut,useEffect:ba,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,ml(4194308,4,Ba.bind(null,t,e),n)},useLayoutEffect:function(e,t){return ml(4194308,4,e,t)},useInsertionEffect:function(e,t){return ml(4,2,e,t)},useMemo:function(e,t){var n=Nt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Nt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=Id.bind(null,Le,e),[r.memoizedState,e]},useRef:function(e){var t=Nt();return e={current:e},t.memoizedState=e},useState:Aa,useDebugValue:ni,useDeferredValue:function(e){return Nt().memoizedState=e},useTransition:function(){var e=Aa(!1),t=e[0];return e=Pd.bind(null,e[1]),Nt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=Le,s=Nt();if(ze){if(n===void 0)throw Error(u(407));n=n()}else{if(n=t(),Fe===null)throw Error(u(349));(an&30)!==0||Ra(r,t,n)}s.memoizedState=n;var i={value:n,getSnapshot:t};return s.queue=i,ba(Ia.bind(null,r,i,e),[e]),r.flags|=2048,yr(9,Pa.bind(null,r,i,n,t),void 0,null),n},useId:function(){var e=Nt(),t=Fe.identifierPrefix;if(ze){var n=Tt,r=zt;n=(r&~(1<<32-pt(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=vr++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=a.createElement(n,{is:r.is}):(e=a.createElement(n),n==="select"&&(a=e,r.multiple?a.multiple=!0:r.size&&(a.size=r.size))):e=a.createElementNS(e,n),e[St]=t,e[ur]=r,hu(e,t,!1,!1),t.stateNode=e;e:{switch(a=Gl(n,r),n){case"dialog":Ce("cancel",e),Ce("close",e),s=r;break;case"iframe":case"object":case"embed":Ce("load",e),s=r;break;case"video":case"audio":for(s=0;sbn&&(t.flags|=128,r=!0,xr(i,!1),t.lanes=4194304)}else{if(!r)if(e=dl(a),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),xr(i,!0),i.tail===null&&i.tailMode==="hidden"&&!a.alternate&&!ze)return Ke(t),null}else 2*Pe()-i.renderingStartTime>bn&&n!==1073741824&&(t.flags|=128,r=!0,xr(i,!1),t.lanes=4194304);i.isBackwards?(a.sibling=t.child,t.child=a):(n=i.last,n!==null?n.sibling=a:t.child=a,i.last=a)}return i.tail!==null?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=Pe(),t.sibling=null,n=Te.current,we(Te,r?n&1|2:n&1),t):(Ke(t),null);case 22:case 23:return Ei(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&(t.mode&1)!==0?(it&1073741824)!==0&&(Ke(t),t.subtreeFlags&6&&(t.flags|=8192)):Ke(t),null;case 24:return null;case 25:return null}throw Error(u(156,t.tag))}function Ud(e,t){switch(Ms(t),t.tag){case 1:return qe(t.type)&&el(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Dn(),Ee(Ze),Ee(Ve),Gs(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return Qs(t),null;case 13:if(Ee(Te),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(u(340));Ln()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return Ee(Te),null;case 4:return Dn(),null;case 10:return Bs(t.type._context),null;case 22:case 23:return Ei(),null;case 24:return null;default:return null}}var kl=!1,Ge=!1,Hd=typeof WeakSet=="function"?WeakSet:Set,V=null;function An(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){Re(e,t,r)}else n.current=null}function mi(e,t,n){try{n()}catch(r){Re(e,t,r)}}var yu=!1;function Vd(e,t){if(Es=Fr,e=Xo(),ys(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var s=r.anchorOffset,i=r.focusNode;r=r.focusOffset;try{n.nodeType,i.nodeType}catch{n=null;break e}var a=0,c=-1,f=-1,N=0,O=0,b=e,A=null;t:for(;;){for(var H;b!==n||s!==0&&b.nodeType!==3||(c=a+s),b!==i||r!==0&&b.nodeType!==3||(f=a+r),b.nodeType===3&&(a+=b.nodeValue.length),(H=b.firstChild)!==null;)A=b,b=H;for(;;){if(b===e)break t;if(A===n&&++N===s&&(c=a),A===i&&++O===r&&(f=a),(H=b.nextSibling)!==null)break;b=A,A=b.parentNode}b=H}n=c===-1||f===-1?null:{start:c,end:f}}else n=null}n=n||{start:0,end:0}}else n=null;for(_s={focusedElem:e,selectionRange:n},Fr=!1,V=t;V!==null;)if(t=V,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,V=e;else for(;V!==null;){t=V;try{var K=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(K!==null){var G=K.memoizedProps,Ie=K.memoizedState,y=t.stateNode,h=y.getSnapshotBeforeUpdate(t.elementType===t.type?G:vt(t.type,G),Ie);y.__reactInternalSnapshotBeforeUpdate=h}break;case 3:var j=t.stateNode.containerInfo;j.nodeType===1?j.textContent="":j.nodeType===9&&j.documentElement&&j.removeChild(j.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(u(163))}}catch($){Re(t,t.return,$)}if(e=t.sibling,e!==null){e.return=t.return,V=e;break}V=t.return}return K=yu,yu=!1,K}function kr(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var s=r=r.next;do{if((s.tag&e)===e){var i=s.destroy;s.destroy=void 0,i!==void 0&&mi(t,n,i)}s=s.next}while(s!==r)}}function jl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function hi(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function xu(e){var t=e.alternate;t!==null&&(e.alternate=null,xu(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[St],delete t[ur],delete t[Rs],delete t[_d],delete t[zd])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function ku(e){return e.tag===5||e.tag===3||e.tag===4}function ju(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||ku(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function vi(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=qr));else if(r!==4&&(e=e.child,e!==null))for(vi(e,t,n),e=e.sibling;e!==null;)vi(e,t,n),e=e.sibling}function gi(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(gi(e,t,n),e=e.sibling;e!==null;)gi(e,t,n),e=e.sibling}var Ue=null,gt=!1;function Qt(e,t,n){for(n=n.child;n!==null;)Su(e,t,n),n=n.sibling}function Su(e,t,n){if(jt&&typeof jt.onCommitFiberUnmount=="function")try{jt.onCommitFiberUnmount(Dr,n)}catch{}switch(n.tag){case 5:Ge||An(n,t);case 6:var r=Ue,s=gt;Ue=null,Qt(e,t,n),Ue=r,gt=s,Ue!==null&&(gt?(e=Ue,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Ue.removeChild(n.stateNode));break;case 18:Ue!==null&&(gt?(e=Ue,n=n.stateNode,e.nodeType===8?Ls(e.parentNode,n):e.nodeType===1&&Ls(e,n),qn(e)):Ls(Ue,n.stateNode));break;case 4:r=Ue,s=gt,Ue=n.stateNode.containerInfo,gt=!0,Qt(e,t,n),Ue=r,gt=s;break;case 0:case 11:case 14:case 15:if(!Ge&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){s=r=r.next;do{var i=s,a=i.destroy;i=i.tag,a!==void 0&&((i&2)!==0||(i&4)!==0)&&mi(n,t,a),s=s.next}while(s!==r)}Qt(e,t,n);break;case 1:if(!Ge&&(An(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(c){Re(n,t,c)}Qt(e,t,n);break;case 21:Qt(e,t,n);break;case 22:n.mode&1?(Ge=(r=Ge)||n.memoizedState!==null,Qt(e,t,n),Ge=r):Qt(e,t,n);break;default:Qt(e,t,n)}}function wu(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Hd),t.forEach(function(r){var s=ef.bind(null,e,r);n.has(r)||(n.add(r),r.then(s,s))})}}function yt(e,t){var n=t.deletions;if(n!==null)for(var r=0;rs&&(s=a),r&=~i}if(r=s,r=Pe()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Kd(r/1960))-r,10e?16:e,Gt===null)var r=!1;else{if(e=Gt,Gt=null,El=0,(ge&6)!==0)throw Error(u(331));var s=ge;for(ge|=4,V=e.current;V!==null;){var i=V,a=i.child;if((V.flags&16)!==0){var c=i.deletions;if(c!==null){for(var f=0;fPe()-ki?dn(e,0):xi|=n),tt(e,t)}function Au(e,t){t===0&&((e.mode&1)===0?t=1:(t=Ar,Ar<<=1,(Ar&130023424)===0&&(Ar=4194304)));var n=Xe();e=Lt(e,t),e!==null&&(Kn(e,t,n),tt(e,n))}function Jd(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Au(e,n)}function ef(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,s=e.memoizedState;s!==null&&(n=s.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(u(314))}r!==null&&r.delete(t),Au(e,n)}var Ou;Ou=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||Ze.current)Je=!0;else{if((e.lanes&n)===0&&(t.flags&128)===0)return Je=!1,Bd(e,t,n);Je=(e.flags&131072)!==0}else Je=!1,ze&&(t.flags&1048576)!==0&&va(t,ll,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;xl(e,t),e=t.pendingProps;var s=_n(t,Ve.current);In(t,n),s=Zs(null,t,r,e,s,n);var i=qs();return t.flags|=1,typeof s=="object"&&s!==null&&typeof s.render=="function"&&s.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,qe(r)?(i=!0,tl(t)):i=!1,t.memoizedState=s.state!==null&&s.state!==void 0?s.state:null,Hs(t),s.updater=gl,t.stateNode=s,s._reactInternals=t,li(t,r,e,n),t=ai(null,t,r,!0,i,n)):(t.tag=0,ze&&i&&Ds(t),Ye(null,t,s,n),t=t.child),t;case 16:r=t.elementType;e:{switch(xl(e,t),e=t.pendingProps,s=r._init,r=s(r._payload),t.type=r,s=t.tag=nf(r),e=vt(r,e),s){case 0:t=oi(null,t,r,e,n);break e;case 1:t=uu(null,t,r,e,n);break e;case 11:t=lu(null,t,r,e,n);break e;case 14:t=su(null,t,r,vt(r.type,e),n);break e}throw Error(u(306,r,""))}return t;case 0:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:vt(r,s),oi(e,t,r,s,n);case 1:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:vt(r,s),uu(e,t,r,s,n);case 3:e:{if(cu(t),e===null)throw Error(u(387));r=t.pendingProps,i=t.memoizedState,s=i.element,Ca(e,t),cl(t,r,null,n);var a=t.memoizedState;if(r=a.element,i.isDehydrated)if(i={element:r,isDehydrated:!1,cache:a.cache,pendingSuspenseBoundaries:a.pendingSuspenseBoundaries,transitions:a.transitions},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){s=Mn(Error(u(423)),t),t=du(e,t,r,n,s);break e}else if(r!==s){s=Mn(Error(u(424)),t),t=du(e,t,r,n,s);break e}else for(st=Ft(t.stateNode.containerInfo.firstChild),lt=t,ze=!0,ht=null,n=wa(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(Ln(),r===s){t=Pt(e,t,n);break e}Ye(e,t,r,n)}t=t.child}return t;case 5:return za(t),e===null&&Os(t),r=t.type,s=t.pendingProps,i=e!==null?e.memoizedProps:null,a=s.children,zs(r,s)?a=null:i!==null&&zs(r,i)&&(t.flags|=32),au(e,t),Ye(e,t,a,n),t.child;case 6:return e===null&&Os(t),null;case 13:return fu(e,t,n);case 4:return Vs(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=Rn(t,null,r,n):Ye(e,t,r,n),t.child;case 11:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:vt(r,s),lu(e,t,r,s,n);case 7:return Ye(e,t,t.pendingProps,n),t.child;case 8:return Ye(e,t,t.pendingProps.children,n),t.child;case 12:return Ye(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,s=t.pendingProps,i=t.memoizedProps,a=s.value,we(ol,r._currentValue),r._currentValue=a,i!==null)if(mt(i.value,a)){if(i.children===s.children&&!Ze.current){t=Pt(e,t,n);break e}}else for(i=t.child,i!==null&&(i.return=t);i!==null;){var c=i.dependencies;if(c!==null){a=i.child;for(var f=c.firstContext;f!==null;){if(f.context===r){if(i.tag===1){f=Rt(-1,n&-n),f.tag=2;var N=i.updateQueue;if(N!==null){N=N.shared;var O=N.pending;O===null?f.next=f:(f.next=O.next,O.next=f),N.pending=f}}i.lanes|=n,f=i.alternate,f!==null&&(f.lanes|=n),Ws(i.return,n,t),c.lanes|=n;break}f=f.next}}else if(i.tag===10)a=i.type===t.type?null:i.child;else if(i.tag===18){if(a=i.return,a===null)throw Error(u(341));a.lanes|=n,c=a.alternate,c!==null&&(c.lanes|=n),Ws(a,n,t),a=i.sibling}else a=i.child;if(a!==null)a.return=i;else for(a=i;a!==null;){if(a===t){a=null;break}if(i=a.sibling,i!==null){i.return=a.return,a=i;break}a=a.return}i=a}Ye(e,t,s.children,n),t=t.child}return t;case 9:return s=t.type,r=t.pendingProps.children,In(t,n),s=ut(s),r=r(s),t.flags|=1,Ye(e,t,r,n),t.child;case 14:return r=t.type,s=vt(r,t.pendingProps),s=vt(r.type,s),su(e,t,r,s,n);case 15:return iu(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:vt(r,s),xl(e,t),t.tag=1,qe(r)?(e=!0,tl(t)):e=!1,In(t,n),Za(t,r,s),li(t,r,s,n),ai(null,t,r,!0,e,n);case 19:return mu(e,t,n);case 22:return ou(e,t,n)}throw Error(u(156,t.tag))};function bu(e,t){return go(e,t)}function tf(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function ft(e,t,n,r){return new tf(e,t,n,r)}function zi(e){return e=e.prototype,!(!e||!e.isReactComponent)}function nf(e){if(typeof e=="function")return zi(e)?1:0;if(e!=null){if(e=e.$$typeof,e===ke)return 11;if(e===Ne)return 14}return 2}function Zt(e,t){var n=e.alternate;return n===null?(n=ft(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Ll(e,t,n,r,s,i){var a=2;if(r=e,typeof e=="function")zi(e)&&(a=1);else if(typeof e=="string")a=5;else e:switch(e){case pe:return pn(n.children,s,i,t);case me:a=8,s|=8;break;case be:return e=ft(12,n,t,s|2),e.elementType=be,e.lanes=i,e;case se:return e=ft(13,n,t,s),e.elementType=se,e.lanes=i,e;case J:return e=ft(19,n,t,s),e.elementType=J,e.lanes=i,e;case xe:return Rl(n,s,i,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case X:a=10;break e;case le:a=9;break e;case ke:a=11;break e;case Ne:a=14;break e;case _e:a=16,r=null;break e}throw Error(u(130,e==null?e:typeof e,""))}return t=ft(a,n,t,s),t.elementType=e,t.type=r,t.lanes=i,t}function pn(e,t,n,r){return e=ft(7,e,r,t),e.lanes=n,e}function Rl(e,t,n,r){return e=ft(22,e,r,t),e.elementType=xe,e.lanes=n,e.stateNode={isHidden:!1},e}function Ti(e,t,n){return e=ft(6,e,null,t),e.lanes=n,e}function Li(e,t,n){return t=ft(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function rf(e,t,n,r,s){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=rs(0),this.expirationTimes=rs(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=rs(0),this.identifierPrefix=r,this.onRecoverableError=s,this.mutableSourceEagerHydrationData=null}function Ri(e,t,n,r,s,i,a,c,f){return e=new rf(e,t,n,c,f),t===1?(t=1,i===!0&&(t|=8)):t=0,i=ft(3,null,null,t),e.current=i,i.stateNode=e,i.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Hs(i),e}function lf(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(o)}catch(p){console.error(p)}}return o(),Oi.exports=vf(),Oi.exports}var qu;function yf(){if(qu)return bl;qu=1;var o=gf();return bl.createRoot=o.createRoot,bl.hydrateRoot=o.hydrateRoot,bl}var xf=yf();const kf=Qi(xf),jf={},Ju=o=>{let p;const u=new Set,k=(z,d)=>{const w=typeof z=="function"?z(p):z;if(!Object.is(w,p)){const m=p;p=d??(typeof w!="object"||w===null)?w:Object.assign({},p,w),u.forEach(T=>T(p,m))}},S=()=>p,R={setState:k,getState:S,getInitialState:()=>C,subscribe:z=>(u.add(z),()=>u.delete(z)),destroy:()=>{(jf?"production":void 0)!=="production"&&console.warn("[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."),u.clear()}},C=p=o(k,S,R);return R},Sf=o=>o?Ju(o):Ju;var Fi={exports:{}},Bi={},Wi={exports:{}},Ui={};/** - * @license React - * use-sync-external-store-shim.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var ec;function wf(){if(ec)return Ui;ec=1;var o=_r();function p(d,w){return d===w&&(d!==0||1/d===1/w)||d!==d&&w!==w}var u=typeof Object.is=="function"?Object.is:p,k=o.useState,S=o.useEffect,D=o.useLayoutEffect,F=o.useDebugValue;function _(d,w){var m=w(),T=k({inst:{value:m,getSnapshot:w}}),g=T[0].inst,M=T[1];return D(function(){g.value=m,g.getSnapshot=w,R(g)&&M({inst:g})},[d,m,w]),S(function(){return R(g)&&M({inst:g}),d(function(){R(g)&&M({inst:g})})},[d]),F(m),m}function R(d){var w=d.getSnapshot;d=d.value;try{var m=w();return!u(d,m)}catch{return!0}}function C(d,w){return w()}var z=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?C:_;return Ui.useSyncExternalStore=o.useSyncExternalStore!==void 0?o.useSyncExternalStore:z,Ui}var tc;function Nf(){return tc||(tc=1,Wi.exports=wf()),Wi.exports}/** - * @license React - * use-sync-external-store-shim/with-selector.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var nc;function Cf(){if(nc)return Bi;nc=1;var o=_r(),p=Nf();function u(C,z){return C===z&&(C!==0||1/C===1/z)||C!==C&&z!==z}var k=typeof Object.is=="function"?Object.is:u,S=p.useSyncExternalStore,D=o.useRef,F=o.useEffect,_=o.useMemo,R=o.useDebugValue;return Bi.useSyncExternalStoreWithSelector=function(C,z,d,w,m){var T=D(null);if(T.current===null){var g={hasValue:!1,value:null};T.current=g}else g=T.current;T=_(function(){function P(ue){if(!B){if(B=!0,Z=ue,ue=w(ue),m!==void 0&&g.hasValue){var pe=g.value;if(m(pe,ue))return I=pe}return I=ue}if(pe=I,k(Z,ue))return pe;var me=w(ue);return m!==void 0&&m(pe,me)?(Z=ue,pe):(Z=ue,I=me)}var B=!1,Z,I,oe=d===void 0?null:d;return[function(){return P(z())},oe===null?void 0:function(){return P(oe())}]},[z,d,w,m]);var M=S(C,T[0],T[1]);return F(function(){g.hasValue=!0,g.value=M},[M]),R(M),M},Bi}var rc;function Ef(){return rc||(rc=1,Fi.exports=Cf()),Fi.exports}var _f=Ef();const zf=Qi(_f),ac={},{useDebugValue:Tf}=oc,{useSyncExternalStoreWithSelector:Lf}=zf;let lc=!1;const Rf=o=>o;function Pf(o,p=Rf,u){(ac?"production":void 0)!=="production"&&u&&!lc&&(console.warn("[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937"),lc=!0);const k=Lf(o.subscribe,o.getState,o.getServerState||o.getInitialState,p,u);return Tf(k),k}const sc=o=>{(ac?"production":void 0)!=="production"&&typeof o!="function"&&console.warn("[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`.");const p=typeof o=="function"?Sf(o):o,u=(k,S)=>Pf(p,k,S);return Object.assign(u,p),u},If=o=>o?sc(o):sc,Se="";async function kt(o){const p=await fetch(o,{cache:"no-store"});if(!p.ok)throw new Error(String(p.status));return p.json()}async function We(o,p){return(await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(p)})).json()}const fe={liveStatus:()=>kt(`${Se}/api/live-status`),agentConfig:()=>kt(`${Se}/api/agent-config`),modelChangeLog:()=>kt(`${Se}/api/model-change-log`).catch(()=>[]),officialsStats:()=>kt(`${Se}/api/officials-stats`),morningBrief:()=>kt(`${Se}/api/morning-brief`),morningConfig:()=>kt(`${Se}/api/morning-config`),agentsStatus:()=>kt(`${Se}/api/agents-status`),taskActivity:o=>kt(`${Se}/api/task-activity/${encodeURIComponent(o)}`),schedulerState:o=>kt(`${Se}/api/scheduler-state/${encodeURIComponent(o)}`),skillContent:(o,p)=>kt(`${Se}/api/skill-content/${encodeURIComponent(o)}/${encodeURIComponent(p)}`),setModel:(o,p)=>We(`${Se}/api/set-model`,{agentId:o,model:p}),agentWake:o=>We(`${Se}/api/agent-wake`,{agentId:o}),taskAction:(o,p,u)=>We(`${Se}/api/task-action`,{taskId:o,action:p,reason:u}),reviewAction:(o,p,u)=>We(`${Se}/api/review-action`,{taskId:o,action:p,comment:u}),advanceState:(o,p)=>We(`${Se}/api/advance-state`,{taskId:o,comment:p}),archiveTask:(o,p)=>We(`${Se}/api/archive-task`,{taskId:o,archived:p}),archiveAllDone:()=>We(`${Se}/api/archive-task`,{archiveAllDone:!0}),schedulerScan:(o=180)=>We(`${Se}/api/scheduler-scan`,{thresholdSec:o}),schedulerRetry:(o,p)=>We(`${Se}/api/scheduler-retry`,{taskId:o,reason:p}),schedulerEscalate:(o,p)=>We(`${Se}/api/scheduler-escalate`,{taskId:o,reason:p}),schedulerRollback:(o,p)=>We(`${Se}/api/scheduler-rollback`,{taskId:o,reason:p}),refreshMorning:()=>We(`${Se}/api/morning-brief/refresh`,{}),saveMorningConfig:o=>We(`${Se}/api/morning-config`,o),addSkill:(o,p,u,k)=>We(`${Se}/api/add-skill`,{agentId:o,skillName:p,description:u,trigger:k}),addRemoteSkill:(o,p,u,k)=>We(`${Se}/api/add-remote-skill`,{agentId:o,skillName:p,sourceUrl:u,description:k||""}),remoteSkillsList:()=>kt(`${Se}/api/remote-skills-list`),updateRemoteSkill:(o,p)=>We(`${Se}/api/update-remote-skill`,{agentId:o,skillName:p}),removeRemoteSkill:(o,p)=>We(`${Se}/api/remove-remote-skill`,{agentId:o,skillName:p}),createTask:o=>We(`${Se}/api/create-task`,o)},uc=[{key:"Inbox",dept:"皇上",icon:"👑",action:"下旨"},{key:"Taizi",dept:"太子",icon:"🤴",action:"分拣"},{key:"Zhongshu",dept:"中书省",icon:"📜",action:"起草"},{key:"Menxia",dept:"门下省",icon:"🔍",action:"审议"},{key:"Assigned",dept:"尚书省",icon:"📮",action:"派发"},{key:"Doing",dept:"六部",icon:"⚙️",action:"执行"},{key:"Review",dept:"尚书省",icon:"🔎",action:"汇总"},{key:"Done",dept:"回奏",icon:"✅",action:"完成"}],Df={Inbox:0,Pending:0,Taizi:1,Zhongshu:2,Menxia:3,Assigned:4,Doing:5,Review:6,Done:7,Blocked:5,Cancelled:5,Next:4},Mf={太子:"#e8a040",中书省:"#a07aff",门下省:"#6a9eff",尚书省:"#6aef9a",礼部:"#f5c842",户部:"#ff9a6a",兵部:"#ff5270",刑部:"#cc4444",工部:"#44aaff",吏部:"#9b59b6",皇上:"#ffd700",回奏:"#2ecc8a"},zr={Inbox:"收件",Pending:"待处理",Taizi:"太子分拣",Zhongshu:"中书起草",Menxia:"门下审议",Assigned:"已派发",Doing:"执行中",Review:"待审查",Done:"已完成",Blocked:"阻塞",Cancelled:"已取消",Next:"待执行"};function $l(o){return Mf[o]||"#6a9eff"}function Ki(o){const p=o.review_round||0;return o.state==="Menxia"&&p>1?`门下审议(第${p}轮)`:o.state==="Zhongshu"&&p>0?`中书修订(第${p}轮)`:zr[o.state]||o.state}function Jt(o){return/^JJC-/i.test(o.id||"")}function Bl(o){return o.archived||["Done","Cancelled"].includes(o.state)}function Gi(o){const p=Df[o.state]??4;return uc.map((u,k)=>({...u,status:k({liveStatus:null,agentConfig:null,changeLog:[],officialsData:null,agentsStatusData:null,morningBrief:null,subConfig:null,activeTab:"edicts",edictFilter:"active",sessFilter:"all",tplCatFilter:"全部",selectedOfficial:null,modalTaskId:null,countdown:5,toasts:[],setActiveTab:u=>{o({activeTab:u});const k=p();["models","skills","sessions"].includes(u)&&!k.agentConfig&&k.loadAgentConfig(),u==="officials"&&!k.officialsData&&k.loadOfficials(),u==="monitor"&&k.loadAgentsStatus(),u==="morning"&&!k.morningBrief&&k.loadMorning()},setEdictFilter:u=>o({edictFilter:u}),setSessFilter:u=>o({sessFilter:u}),setTplCatFilter:u=>o({tplCatFilter:u}),setSelectedOfficial:u=>o({selectedOfficial:u}),setModalTaskId:u=>o({modalTaskId:u}),setCountdown:u=>o({countdown:u}),toast:(u,k="ok")=>{const S=++Ff;o(D=>({toasts:[...D.toasts,{id:S,msg:u,type:k}]})),setTimeout(()=>{o(D=>({toasts:D.toasts.filter(F=>F.id!==S)}))},3e3)},loadLive:async()=>{try{const u=await fe.liveStatus();o({liveStatus:u}),p().officialsData||fe.officialsStats().then(S=>o({officialsData:S})).catch(()=>{})}catch{}},loadAgentConfig:async()=>{try{const u=await fe.agentConfig(),k=await fe.modelChangeLog();o({agentConfig:u,changeLog:k})}catch{}},loadOfficials:async()=>{try{const u=await fe.officialsStats();o({officialsData:u})}catch{}},loadAgentsStatus:async()=>{try{const u=await fe.agentsStatus();o({agentsStatusData:u})}catch{o({agentsStatusData:null})}},loadMorning:async()=>{try{const[u,k]=await Promise.all([fe.morningBrief(),fe.morningConfig()]);o({morningBrief:u,subConfig:k})}catch{}},loadSubConfig:async()=>{try{const u=await fe.morningConfig();o({subConfig:u})}catch{}},loadAll:async()=>{const u=p();await u.loadLive();const k=u.activeTab;["models","skills"].includes(k)&&await u.loadAgentConfig()}}));let Er=null;function Bf(){Er||(q.getState().loadAll(),Er=setInterval(()=>{const o=q.getState(),p=o.countdown-1;p<=0?(o.setCountdown(5),o.loadAll()):o.setCountdown(p)},1e3))}function Wf(){Er&&(clearInterval(Er),Er=null)}function Uf(o){if(!o)return"";try{const p=new Date(o.includes("T")?o:o.replace(" ","T")+"Z");if(isNaN(p.getTime()))return"";const u=Date.now()-p.getTime(),k=Math.floor(u/6e4);if(k<1)return"刚刚";if(k<60)return k+"分钟前";const S=Math.floor(k/60);return S<24?S+"小时前":Math.floor(S/24)+"天前"}catch{return""}}const ic={Doing:0,Review:1,Assigned:2,Menxia:3,Zhongshu:4,Taizi:5,Inbox:6,Blocked:7,Next:8,Done:9,Cancelled:10};function Hf({task:o}){const p=Gi(o);return l.jsx("div",{className:"ec-pipe",children:p.map((u,k)=>l.jsxs("span",{style:{display:"contents"},children:[l.jsxs("div",{className:`ep-node ${u.status}`,children:[l.jsx("div",{className:"ep-icon",children:u.icon}),l.jsx("div",{className:"ep-name",children:u.dept})]}),kP.setModalTaskId),u=q(P=>P.toast),k=q(P=>P.loadAll),S=o.heartbeat||{status:"unknown",label:"⚪"},D="st-"+(o.state||""),F="dt-"+(o.org||"").replace(/\s/g,""),_=uc.find((P,B)=>Gi(o)[B].status==="active"),R=o.todos||[],C=R.filter(P=>P.status==="completed").length,z=R.length,d=!["Done","Blocked","Cancelled"].includes(o.state),w=["Blocked","Cancelled"].includes(o.state),m=Bl(o),T=o.block&&o.block!=="无"&&o.block!=="-",g=async(P,B)=>{if(B.stopPropagation(),P==="stop"||P==="cancel"){const Z=prompt(P==="stop"?"请输入叫停原因:":"请输入取消原因:");if(Z===null)return;try{const I=await fe.taskAction(o.id,P,Z);I.ok?(u(I.message||"操作成功"),k()):u(I.error||"操作失败","err")}catch{u("服务器连接失败","err")}}else if(P==="resume")try{const Z=await fe.taskAction(o.id,"resume","恢复执行");Z.ok?(u(Z.message||"已恢复"),k()):u(Z.error||"操作失败","err")}catch{u("服务器连接失败","err")}},M=async P=>{P.stopPropagation();try{const B=await fe.archiveTask(o.id,!o.archived);B.ok?(u(B.message||"操作成功"),k()):u(B.error||"操作失败","err")}catch{u("服务器连接失败","err")}};return l.jsxs("div",{className:`edict-card${m?" archived":""}`,onClick:()=>p(o.id),children:[l.jsx(Hf,{task:o}),l.jsx("div",{className:"ec-id",children:o.id}),l.jsx("div",{className:"ec-title",children:o.title||"(无标题)"}),l.jsxs("div",{className:"ec-meta",children:[l.jsx("span",{className:`tag ${D}`,children:Ki(o)}),o.org&&l.jsx("span",{className:`tag ${F}`,children:o.org}),_&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["当前: ",l.jsxs("b",{style:{color:$l(_.dept)},children:[_.dept," · ",_.action]})]})]}),o.now&&o.now!=="-"&&l.jsx("div",{style:{fontSize:11,color:"var(--muted)",lineHeight:1.5,marginBottom:6},children:o.now.substring(0,80)}),(o.review_round||0)>0&&l.jsxs("div",{style:{fontSize:11,marginBottom:6},children:[Array.from({length:o.review_round||0},(P,B)=>l.jsx("span",{style:{display:"inline-block",width:14,height:14,borderRadius:"50%",background:B<(o.review_round||0)-1?"#1a3a6a22":"var(--acc)22",border:`1px solid ${B<(o.review_round||0)-1?"#2a4a8a":"var(--acc)"}`,fontSize:9,textAlign:"center",lineHeight:"13px",marginRight:2,color:B<(o.review_round||0)-1?"#4a6aaa":"var(--acc)"},children:B+1},B)),l.jsxs("span",{style:{color:"var(--muted)",fontSize:10},children:["第 ",o.review_round," 轮磋商"]})]}),z>0&&l.jsxs("div",{className:"ec-todo-bar",children:[l.jsxs("span",{children:["📋 ",C,"/",z]}),l.jsx("div",{className:"ec-todo-track",children:l.jsx("div",{className:"ec-todo-fill",style:{width:`${Math.round(C/z*100)}%`}})}),l.jsx("span",{children:C===z?"✅ 全部完成":"🔄 进行中"})]}),l.jsxs("div",{className:"ec-footer",children:[l.jsx("span",{className:`hb ${S.status}`,children:S.label}),T&&l.jsxs("span",{className:"tag",style:{borderColor:"#ff527044",color:"var(--danger)",background:"#200a10"},children:["🚫 ",o.block]}),o.eta&&o.eta!=="-"&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["📅 ",o.eta]})]}),l.jsxs("div",{className:"ec-actions",onClick:P=>P.stopPropagation(),children:[d&&l.jsxs(l.Fragment,{children:[l.jsx("button",{className:"mini-act",onClick:P=>g("stop",P),children:"⏸ 叫停"}),l.jsx("button",{className:"mini-act danger",onClick:P=>g("cancel",P),children:"🚫 取消"})]}),w&&l.jsx("button",{className:"mini-act",onClick:P=>g("resume",P),children:"▶ 恢复"}),m&&!o.archived&&l.jsx("button",{className:"mini-act",onClick:M,children:"📦 归档"}),o.archived&&l.jsx("button",{className:"mini-act",onClick:M,children:"📤 取消归档"})]})]})}function Qf(){const o=q(m=>m.liveStatus),p=q(m=>m.edictFilter),u=q(m=>m.setEdictFilter),k=q(m=>m.toast),S=q(m=>m.loadAll),F=((o==null?void 0:o.tasks)||[]).filter(Jt),_=F.filter(m=>!Bl(m)),R=F.filter(m=>Bl(m));let C;p==="active"?C=_:p==="archived"?C=R:C=F,C.sort((m,T)=>(ic[m.state]??9)-(ic[T.state]??9));const z=F.filter(m=>!m.archived&&["Done","Cancelled"].includes(m.state)),d=async()=>{if(confirm("将所有已完成/已取消的旨意移入归档?"))try{const m=await fe.archiveAllDone();m.ok?(k(`📦 ${m.count||0} 道旨意已归档`),S()):k(m.error||"批量归档失败","err")}catch{k("服务器连接失败","err")}},w=async()=>{try{const m=await fe.schedulerScan();m.ok?k(`🧭 太子巡检完成:${m.count||0} 个动作`):k(m.error||"巡检失败","err"),S()}catch{k("服务器连接失败","err")}};return l.jsxs("div",{children:[l.jsxs("div",{className:"archive-bar",children:[l.jsx("span",{className:"ab-label",children:"筛选:"}),["active","archived","all"].map(m=>l.jsx("button",{className:`ab-btn ${p===m?"active":""}`,onClick:()=>u(m),children:m==="active"?"活跃":m==="archived"?"归档":"全部"},m)),z.length>0&&l.jsx("button",{className:"ab-btn",onClick:d,children:"📦 一键归档"}),l.jsxs("span",{className:"ab-count",children:["活跃 ",_.length," · 归档 ",R.length," · 共 ",F.length]}),l.jsx("button",{className:"ab-scan",onClick:w,children:"🧭 太子巡检"})]}),l.jsx("div",{className:"edict-grid",children:C.length===0?l.jsxs("div",{className:"empty",style:{gridColumn:"1/-1"},children:["暂无旨意",l.jsx("br",{}),l.jsx("small",{style:{fontSize:11,marginTop:6,display:"block",color:"var(--muted)"},children:"通过飞书向太子发送任务,太子分拣后转中书省处理"})]}):C.map(m=>l.jsx(Vf,{task:m},m.id))})]})}function Kf(){var Z;const o=q(I=>I.liveStatus),p=q(I=>I.agentsStatusData),u=q(I=>I.officialsData),k=q(I=>I.loadAgentsStatus),S=q(I=>I.setModalTaskId),D=q(I=>I.toast);te.useEffect(()=>{k()},[k]);const _=((o==null?void 0:o.tasks)||[]).filter(I=>Jt(I)&&I.state!=="Done"&&I.state!=="Next"),R={};u!=null&&u.officials&&u.officials.forEach(I=>{R[I.id]=I});const C=async I=>{try{const oe=await fe.agentWake(I);D(oe.message||"唤醒指令已发出"),setTimeout(()=>k(),3e4)}catch{D("唤醒失败","err")}},z=async()=>{if(!p)return;const I=p.agents.filter(oe=>oe.id!=="main"&&oe.status!=="running"&&oe.status!=="unconfigured");if(!I.length){D("所有 Agent 均已在线");return}D(`正在唤醒 ${I.length} 个 Agent...`);for(const oe of I)try{await fe.agentWake(oe.id)}catch{}D(`${I.length} 个唤醒指令已发出,30秒后刷新状态`),setTimeout(()=>k(),3e4)},d=p,w=((Z=d==null?void 0:d.agents)==null?void 0:Z.filter(I=>I.id!=="main"))||[],m=w.filter(I=>I.status==="running").length,T=w.filter(I=>I.status==="idle").length,g=w.filter(I=>I.status==="offline").length,M=w.filter(I=>I.status==="unconfigured").length,P=d==null?void 0:d.gateway,B=P!=null&&P.probe?"ok":P!=null&&P.alive?"warn":"err";return l.jsxs("div",{children:[d&&d.ok&&l.jsxs("div",{className:"as-panel",children:[l.jsxs("div",{className:"as-header",children:[l.jsx("span",{className:"as-title",children:"🔌 Agent 在线状态"}),l.jsxs("span",{className:`as-gw ${B}`,children:["Gateway: ",(P==null?void 0:P.status)||"未知"]}),l.jsx("button",{className:"btn-refresh",onClick:()=>k(),style:{marginLeft:8},children:"🔄 刷新"}),g+M>0&&l.jsx("button",{className:"btn-refresh",onClick:z,style:{marginLeft:4,borderColor:"var(--warn)",color:"var(--warn)"},children:"⚡ 全部唤醒"})]}),l.jsx("div",{className:"as-grid",children:w.map(I=>{const oe=I.status!=="running"&&I.status!=="unconfigured"&&(P==null?void 0:P.alive);return l.jsxs("div",{className:"as-card",title:`${I.role} · ${I.statusLabel}`,children:[l.jsx("div",{className:`as-dot ${I.status}`}),l.jsx("div",{style:{fontSize:22},children:I.emoji}),l.jsx("div",{style:{fontSize:12,fontWeight:700},children:I.label}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:I.role}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:I.statusLabel}),I.lastActive?l.jsxs("div",{style:{fontSize:10,color:"var(--muted)"},children:["⏰ ",I.lastActive]}):l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"无活动记录"}),oe&&l.jsx("button",{className:"as-wake-btn",onClick:ue=>{ue.stopPropagation(),C(I.id)},children:"⚡ 唤醒"})]},I.id)})}),l.jsxs("div",{className:"as-summary",children:[l.jsxs("span",{children:[l.jsx("span",{className:"as-dot running",style:{position:"static",width:8,height:8}})," ",m," 运行中"]}),l.jsxs("span",{children:[l.jsx("span",{className:"as-dot idle",style:{position:"static",width:8,height:8}})," ",T," 待命"]}),g>0&&l.jsxs("span",{children:[l.jsx("span",{className:"as-dot offline",style:{position:"static",width:8,height:8}})," ",g," 离线"]}),M>0&&l.jsxs("span",{children:[l.jsx("span",{className:"as-dot unconfigured",style:{position:"static",width:8,height:8}})," ",M," 未配置"]}),l.jsxs("span",{style:{marginLeft:"auto",fontSize:10,color:"var(--muted)"},children:["检测于 ",(d.checkedAt||"").substring(11,19)]})]})]}),l.jsx("div",{className:"duty-grid",children:Of.map(I=>{const oe=_.filter(se=>se.org===I.label),ue=oe.some(se=>se.state==="Doing"),pe=oe.some(se=>se.state==="Blocked"),me=R[I.id],be=(me==null?void 0:me.heartbeat)||{status:"idle"},X=pe?"blocked":ue?"busy":be.status==="active"?"active":"idle",le=pe?"⚠️ 阻塞":ue?"⚙️ 执行中":be.status==="active"?"🟢 活跃":"⚪ 候命",ke=pe?"blocked-card":ue?"active-card":"";return l.jsxs("div",{className:`duty-card ${ke}`,children:[l.jsxs("div",{className:"dc-hdr",children:[l.jsx("span",{className:"dc-emoji",children:I.emoji}),l.jsxs("div",{className:"dc-info",children:[l.jsx("div",{className:"dc-name",children:I.label}),l.jsxs("div",{className:"dc-role",children:[I.role," · ",I.rank]})]}),l.jsxs("div",{className:"dc-status",children:[l.jsx("span",{className:`dc-dot ${X}`}),l.jsx("span",{children:le})]})]}),l.jsx("div",{className:"dc-body",children:oe.length>0?oe.map(se=>l.jsxs("div",{className:"dc-task",onClick:()=>S(se.id),children:[l.jsx("div",{className:"dc-task-id",children:se.id}),l.jsx("div",{className:"dc-task-title",children:se.title||"(无标题)"}),se.now&&se.now!=="-"&&l.jsx("div",{className:"dc-task-now",children:se.now.substring(0,70)}),l.jsxs("div",{className:"dc-task-meta",children:[l.jsx("span",{className:`tag st-${se.state}`,children:Ki(se)}),se.block&&se.block!=="无"&&l.jsxs("span",{className:"tag",style:{borderColor:"#ff527044",color:"var(--danger)"},children:["🚫",se.block]})]})]},se.id)):l.jsxs("div",{className:"dc-idle",children:[l.jsx("span",{style:{fontSize:20},children:"🪭"}),l.jsx("span",{children:"候命中"})]})}),l.jsxs("div",{className:"dc-footer",children:[l.jsxs("span",{className:"dc-model",children:["🤖 ",(me==null?void 0:me.model_short)||"待配置"]}),(me==null?void 0:me.last_active)&&l.jsxs("span",{className:"dc-la",children:["⏰ ",me.last_active]})]})]},I.id)})})]})}const Gf=["🥇","🥈","🥉"];function Yf(){var d;const o=q(w=>w.officialsData),p=q(w=>w.selectedOfficial),u=q(w=>w.setSelectedOfficial),k=q(w=>w.loadOfficials),S=q(w=>w.setModalTaskId);if(te.useEffect(()=>{k()},[k]),!(o!=null&&o.officials))return l.jsx("div",{className:"empty",children:"⚠️ 请确保本地服务器已启动"});const D=o.officials,F=o.totals||{tasks_done:0,cost_cny:0},_=Math.max(...D.map(w=>w.tokens_in+w.tokens_out+w.cache_read+w.cache_write),1),R=D.filter(w=>{var m;return((m=w.heartbeat)==null?void 0:m.status)==="active"}),C=D.find(w=>{var m;return w.id===(p||((m=D[0])==null?void 0:m.id))}),z=(C==null?void 0:C.id)||((d=D[0])==null?void 0:d.id);return l.jsxs("div",{children:[R.length>0&&l.jsxs("div",{className:"off-activity",children:[l.jsx("span",{children:"🟢 当前活跃:"}),R.map(w=>l.jsxs("span",{style:{fontSize:12},children:[w.emoji," ",w.role]},w.id)),l.jsx("span",{style:{color:"var(--muted)",fontSize:11,marginLeft:"auto"},children:"其余官员待命"})]}),l.jsxs("div",{className:"off-kpi",children:[l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{color:"var(--acc)"},children:D.length}),l.jsx("div",{className:"kpi-l",children:"在职官员"})]}),l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{color:"#f5c842"},children:F.tasks_done||0}),l.jsx("div",{className:"kpi-l",children:"累计完成旨意"})]}),l.jsxs("div",{className:"kpi",children:[l.jsxs("div",{className:"kpi-v",style:{color:(F.cost_cny||0)>20?"var(--warn)":"var(--ok)"},children:["¥",F.cost_cny||0]}),l.jsx("div",{className:"kpi-l",children:"累计费用(含缓存)"})]}),l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{fontSize:16,paddingTop:4},children:o.top_official||"—"}),l.jsx("div",{className:"kpi-l",children:"功绩最高"})]})]}),l.jsxs("div",{className:"off-layout",children:[l.jsxs("div",{className:"off-ranklist",children:[l.jsx("div",{className:"orl-hdr",children:"功绩排行"}),D.map(w=>{const m=w.heartbeat||{status:"idle"};return l.jsxs("div",{className:`orl-item${z===w.id?" selected":""}`,onClick:()=>u(w.id),children:[l.jsx("span",{style:{minWidth:24,textAlign:"center"},children:w.merit_rank<=3?Gf[w.merit_rank-1]:"#"+w.merit_rank}),l.jsx("span",{children:w.emoji}),l.jsxs("span",{style:{flex:1},children:[l.jsx("div",{style:{fontSize:12,fontWeight:700},children:w.role}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:w.label})]}),l.jsxs("span",{style:{fontSize:11},children:[w.merit_score,"分"]}),l.jsx("span",{className:`dc-dot ${m.status}`,style:{width:8,height:8}})]},w.id)})]}),l.jsx("div",{className:"off-detail",children:C?l.jsx(Xf,{official:C,maxTk:_,onOpenTask:S}):l.jsx("div",{className:"empty",children:"选择左侧官员查看详情"})})]})]})}function Xf({official:o,maxTk:p,onOpenTask:u}){const k=o.heartbeat||{status:"idle",label:"⚪ 待命"},S=o.tokens_in+o.tokens_out+o.cache_read+o.cache_write,D=o.participated_edicts||[],F=[{l:"输入",v:o.tokens_in,color:"#6a9eff"},{l:"输出",v:o.tokens_out,color:"#a07aff"},{l:"缓存读",v:o.cache_read,color:"#2ecc8a"},{l:"缓存写",v:o.cache_write,color:"#f5c842"}];return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:16,alignItems:"center",marginBottom:20},children:[l.jsx("div",{style:{fontSize:40},children:o.emoji}),l.jsxs("div",{style:{flex:1},children:[l.jsx("div",{style:{fontSize:18,fontWeight:800},children:o.role}),l.jsxs("div",{style:{fontSize:12,color:"var(--muted)"},children:[o.label," · ",l.jsx("span",{style:{color:"var(--acc)"},children:o.model_short||o.model})]}),l.jsxs("div",{style:{fontSize:11,color:"var(--muted)",marginTop:2},children:["🏅 ",o.rank," · 功绩分 ",o.merit_score]})]}),l.jsxs("div",{style:{textAlign:"right"},children:[l.jsx("div",{className:`hb ${k.status}`,style:{marginBottom:4},children:k.label}),o.last_active&&l.jsxs("div",{style:{fontSize:10,color:"var(--muted)"},children:["活跃 ",o.last_active]}),l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",marginTop:2},children:[o.sessions," 个会话 · ",o.messages," 条消息"]})]})]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"功绩统计"}),l.jsxs("div",{style:{display:"flex",gap:16},children:[l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--ok)"},children:o.tasks_done}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"完成旨意"})]}),l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--warn)"},children:o.tasks_active}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"执行中"})]}),l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--acc)"},children:o.flow_participations}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"流转参与"})]})]})]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"Token 消耗"}),F.map(_=>l.jsxs("div",{style:{marginBottom:6},children:[l.jsxs("div",{style:{display:"flex",justifyContent:"space-between",fontSize:11,marginBottom:2},children:[l.jsx("span",{style:{color:"var(--muted)"},children:_.l}),l.jsx("span",{children:_.v.toLocaleString()})]}),l.jsx("div",{style:{height:6,background:"#0e1320",borderRadius:3},children:l.jsx("div",{style:{height:"100%",width:`${p>0?Math.round(_.v/p*100):0}%`,background:_.color,borderRadius:3}})})]},_.l))]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"累计费用"}),l.jsxs("div",{style:{display:"flex",gap:10},children:[l.jsxs("span",{style:{fontSize:12,color:o.cost_cny>10?"var(--danger)":o.cost_cny>3?"var(--warn)":"var(--ok)"},children:[l.jsxs("b",{children:["¥",o.cost_cny]})," 人民币"]}),l.jsxs("span",{style:{fontSize:12},children:[l.jsxs("b",{children:["$",o.cost_usd]})," 美元"]}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["总计 ",S.toLocaleString()," tokens"]})]})]}),l.jsxs("div",{children:[l.jsxs("div",{className:"sec-title",children:["参与旨意(",D.length," 道)"]}),D.length===0?l.jsx("div",{style:{fontSize:12,color:"var(--muted)",padding:"8px 0"},children:"暂无旨意记录"}):l.jsx("div",{style:{display:"flex",flexDirection:"column",gap:4},children:D.map(_=>l.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center",padding:"6px 8px",borderRadius:6,cursor:"pointer",border:"1px solid var(--line)"},onClick:()=>u(_.id),children:[l.jsx("span",{style:{fontSize:10,color:"var(--acc)",fontWeight:700},children:_.id}),l.jsx("span",{style:{flex:1,fontSize:12},children:_.title.substring(0,35)}),l.jsx("span",{className:`tag st-${_.state}`,style:{fontSize:10},children:zr[_.state]||_.state})]},_.id))})]})]})}const Zf=[{id:"anthropic/claude-sonnet-4-6",l:"Claude Sonnet 4.6",p:"Anthropic"},{id:"anthropic/claude-opus-4-5",l:"Claude Opus 4.5",p:"Anthropic"},{id:"anthropic/claude-haiku-3-5",l:"Claude Haiku 3.5",p:"Anthropic"},{id:"openai/gpt-4o",l:"GPT-4o",p:"OpenAI"},{id:"openai/gpt-4o-mini",l:"GPT-4o Mini",p:"OpenAI"},{id:"google/gemini-2.5-pro",l:"Gemini 2.5 Pro",p:"Google"},{id:"copilot/claude-sonnet-4",l:"Claude Sonnet 4",p:"Copilot"},{id:"copilot/claude-opus-4.5",l:"Claude Opus 4.5",p:"Copilot"},{id:"copilot/gpt-4o",l:"GPT-4o",p:"Copilot"},{id:"copilot/gemini-2.5-pro",l:"Gemini 2.5 Pro",p:"Copilot"}];function qf(){var w;const o=q(m=>m.agentConfig),p=q(m=>m.changeLog),u=q(m=>m.loadAgentConfig),k=q(m=>m.toast),[S,D]=te.useState({}),[F,_]=te.useState({});if(te.useEffect(()=>{u()},[u]),te.useEffect(()=>{if(o!=null&&o.agents){const m={};o.agents.forEach(T=>{m[T.id]=T.model}),D(m)}},[o]),!(o!=null&&o.agents))return l.jsx("div",{className:"empty",style:{gridColumn:"1/-1"},children:"⚠️ 请先启动本地服务器"});const R=(w=o.knownModels)!=null&&w.length?o.knownModels.map(m=>({id:m.id,l:m.label,p:m.provider})):Zf,C=(m,T)=>{D(g=>({...g,[m]:T}))},z=m=>{const T=o.agents.find(g=>g.id===m);T&&D(g=>({...g,[m]:T.model}))},d=async m=>{const T=S[m];if(T){_(g=>({...g,[m]:{cls:"pending",text:"⟳ 提交中…"}}));try{const g=await fe.setModel(m,T);g.ok?(_(M=>({...M,[m]:{cls:"ok",text:"✅ 已提交,Gateway 重启中(约5秒)"}})),k(m+" 模型已更改","ok"),setTimeout(()=>u(),5500)):_(M=>({...M,[m]:{cls:"err",text:"❌ "+(g.error||"错误")}}))}catch{_(g=>({...g,[m]:{cls:"err",text:"❌ 无法连接服务器"}}))}}};return l.jsxs("div",{children:[l.jsx("div",{className:"model-grid",children:o.agents.map(m=>{const T=S[m.id]||m.model,g=T!==m.model,M=F[m.id];return l.jsxs("div",{className:"mc-card",children:[l.jsxs("div",{className:"mc-top",children:[l.jsx("span",{className:"mc-emoji",children:m.emoji||"🏛️"}),l.jsxs("div",{children:[l.jsxs("div",{className:"mc-name",children:[m.label," ",l.jsx("span",{style:{fontSize:11,color:"var(--muted)"},children:m.id})]}),l.jsx("div",{className:"mc-role",children:m.role})]})]}),l.jsxs("div",{className:"mc-cur",children:["当前: ",l.jsx("b",{children:m.model})]}),l.jsx("select",{className:"msel",value:T,onChange:P=>C(m.id,P.target.value),children:R.map(P=>l.jsxs("option",{value:P.id,children:[P.l," (",P.p,")"]},P.id))}),l.jsxs("div",{className:"mc-btns",children:[l.jsx("button",{className:"btn btn-p",disabled:!g,onClick:()=>d(m.id),children:"应用"}),l.jsx("button",{className:"btn btn-g",onClick:()=>z(m.id),children:"重置"})]}),M&&l.jsx("div",{className:`mc-st ${M.cls}`,children:M.text})]},m.id)})}),l.jsxs("div",{style:{marginTop:24},children:[l.jsx("div",{className:"sec-title",children:"变更日志"}),l.jsx("div",{className:"cl-list",children:p!=null&&p.length?[...p].reverse().slice(0,15).map((m,T)=>l.jsxs("div",{className:"cl-row",children:[l.jsx("span",{className:"cl-t",children:(m.at||"").substring(0,16).replace("T"," ")}),l.jsx("span",{className:"cl-a",children:m.agentId}),l.jsxs("span",{className:"cl-c",children:[l.jsx("b",{children:m.oldModel})," → ",l.jsx("b",{children:m.newModel}),m.rolledBack&&l.jsx("span",{style:{color:"var(--danger)",fontSize:10,border:"1px solid #ff527044",padding:"1px 5px",borderRadius:3,marginLeft:4},children:"⚠ 已回滚"})]})]},T)):l.jsx("div",{style:{fontSize:12,color:"var(--muted)",padding:"8px 0"},children:"暂无变更"})})]})]})}const Jf=[{label:"obra/superpowers",emoji:"⚡",stars:"66.9k",desc:"完整开发工作流技能集",skills:[{name:"brainstorming",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/brainstorming/SKILL.md"},{name:"test-driven-development",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/test-driven-development/SKILL.md"},{name:"systematic-debugging",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/systematic-debugging/SKILL.md"},{name:"subagent-driven-development",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/subagent-driven-development/SKILL.md"},{name:"writing-plans",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/writing-plans/SKILL.md"},{name:"executing-plans",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/executing-plans/SKILL.md"},{name:"requesting-code-review",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/requesting-code-review/SKILL.md"},{name:"root-cause-tracing",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/root-cause-tracing/SKILL.md"},{name:"verification-before-completion",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/verification-before-completion/SKILL.md"},{name:"dispatching-parallel-agents",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/dispatching-parallel-agents/SKILL.md"}]},{label:"anthropics/skills",emoji:"🏛️",stars:"官方",desc:"Anthropic 官方技能库",skills:[{name:"docx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/docx/SKILL.md"},{name:"pdf",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/pdf/SKILL.md"},{name:"xlsx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/xlsx/SKILL.md"},{name:"pptx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/pptx/SKILL.md"},{name:"mcp-builder",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md"},{name:"frontend-design",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/frontend-design/SKILL.md"},{name:"web-artifacts-builder",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/web-artifacts-builder/SKILL.md"},{name:"webapp-testing",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/webapp-testing/SKILL.md"},{name:"algorithmic-art",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/algorithmic-art/SKILL.md"},{name:"canvas-design",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/canvas-design/SKILL.md"}]},{label:"ComposioHQ/awesome-claude-skills",emoji:"🌐",stars:"39.2k",desc:"100+ 社区精选技能",skills:[{name:"github-integration",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/github-integration/SKILL.md"},{name:"data-analysis",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/data-analysis/SKILL.md"},{name:"code-review",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/code-review/SKILL.md"}]}];function ep(){const o=q(x=>x.agentConfig),p=q(x=>x.loadAgentConfig),u=q(x=>x.toast),[k,S]=te.useState(null),[D,F]=te.useState(null),[_,R]=te.useState({name:"",desc:"",trigger:""}),[C,z]=te.useState(!1),[d,w]=te.useState("local"),[m,T]=te.useState([]),[g,M]=te.useState(!1),[P,B]=te.useState(!1),[Z,I]=te.useState({agentId:"",skillName:"",sourceUrl:"",description:""}),[oe,ue]=te.useState(!1),[pe,me]=te.useState(null),[be,X]=te.useState(null),[le,ke]=te.useState(null),[se,J]=te.useState("");te.useEffect(()=>{p()},[p]),te.useEffect(()=>{d==="remote"&&Ne()},[d]);const Ne=async()=>{M(!0);try{const x=await fe.remoteSkillsList();x.ok&&T(x.remoteSkills||[])}catch{u("远程技能列表加载失败","err")}M(!1)},_e=async(x,U)=>{S({agentId:x,name:U,content:"⟳ 加载中…",path:""});try{const ee=await fe.skillContent(x,U);ee.ok?S({agentId:x,name:U,content:ee.content||"",path:ee.path||""}):S({agentId:x,name:U,content:"❌ "+(ee.error||"无法读取"),path:""})}catch{S({agentId:x,name:U,content:"❌ 服务器连接失败",path:""})}},xe=(x,U)=>{F({agentId:x,agentLabel:U}),R({name:"",desc:"",trigger:""})},E=async x=>{if(x.preventDefault(),!(!D||!_.name)){z(!0);try{const U=await fe.addSkill(D.agentId,_.name,_.desc,_.trigger);U.ok?(u(`✅ 技能 ${_.name} 已添加到 ${D.agentLabel}`,"ok"),F(null),p()):u(U.error||"添加失败","err")}catch{u("服务器连接失败","err")}z(!1)}},Q=async x=>{x.preventDefault();const{agentId:U,skillName:ee,sourceUrl:he,description:ve}=Z;if(!(!U||!ee||!he)){ue(!0);try{const Me=await fe.addRemoteSkill(U,ee,he,ve);Me.ok?(u(`✅ 远程技能 ${ee} 已添加到 ${U}`,"ok"),B(!1),I({agentId:"",skillName:"",sourceUrl:"",description:""}),Ne(),p()):u(Me.error||"添加失败","err")}catch{u("服务器连接失败","err")}ue(!1)}},W=async x=>{const U=`${x.agentId}/${x.skillName}`;me(U);try{const ee=await fe.updateRemoteSkill(x.agentId,x.skillName);ee.ok?(u(`✅ 技能 ${x.skillName} 已更新`,"ok"),Ne()):u(ee.error||"更新失败","err")}catch{u("服务器连接失败","err")}me(null)},v=async x=>{const U=`${x.agentId}/${x.skillName}`;X(U);try{const ee=await fe.removeRemoteSkill(x.agentId,x.skillName);ee.ok?(u(`🗑️ 技能 ${x.skillName} 已移除`,"ok"),Ne(),p()):u(ee.error||"移除失败","err")}catch{u("服务器连接失败","err")}X(null)},L=async(x,U)=>{if(!se){u("请先选择目标 Agent","err");return}try{const ee=await fe.addRemoteSkill(se,U,x,"");ee.ok?(u(`✅ ${U} → ${se}`,"ok"),Ne(),p()):u(ee.error||"导入失败","err")}catch{u("服务器连接失败","err")}};if(!(o!=null&&o.agents))return l.jsx("div",{className:"empty",children:"无法加载"});const ae=l.jsx("div",{children:l.jsx("div",{className:"skills-grid",children:o.agents.map(x=>l.jsxs("div",{className:"sk-card",children:[l.jsxs("div",{className:"sk-hdr",children:[l.jsx("span",{className:"sk-emoji",children:x.emoji||"🏛️"}),l.jsx("span",{className:"sk-name",children:x.label}),l.jsxs("span",{className:"sk-cnt",children:[(x.skills||[]).length," 技能"]})]}),l.jsx("div",{className:"sk-list",children:(x.skills||[]).length?(x.skills||[]).map(U=>l.jsxs("div",{className:"sk-item",onClick:()=>_e(x.id,U.name),children:[l.jsxs("span",{className:"si-name",children:["📦 ",U.name]}),l.jsx("span",{className:"si-desc",children:U.description||"无描述"}),l.jsx("span",{className:"si-arrow",children:"›"})]},U.name)):l.jsx("div",{className:"sk-empty",children:"暂无 Skills"})}),l.jsx("div",{className:"sk-add",onClick:()=>xe(x.id,x.label),children:"+ 添加技能"})]},x.id))})}),ce=l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:10,marginBottom:20,flexWrap:"wrap",alignItems:"center"},children:[l.jsx("button",{style:{padding:"8px 18px",background:"var(--acc)",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600,fontSize:13},onClick:()=>{B(!0),ke(null)},children:"+ 添加远程 Skill"}),l.jsx("button",{style:{padding:"8px 14px",background:"transparent",color:"var(--acc)",border:"1px solid var(--acc)",borderRadius:8,cursor:"pointer",fontSize:12},onClick:Ne,children:"⟳ 刷新列表"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:4},children:["共 ",m.length," 个远程技能"]})]}),l.jsxs("div",{style:{marginBottom:24},children:[l.jsx("div",{style:{fontSize:12,fontWeight:700,color:"var(--muted)",letterSpacing:".06em",marginBottom:10},children:"🌐 社区技能源 — 一键导入"}),l.jsx("div",{style:{display:"flex",gap:10,flexWrap:"wrap"},children:Jf.map(x=>l.jsxs("div",{onClick:()=>ke((le==null?void 0:le.label)===x.label?null:x),style:{padding:"8px 14px",background:(le==null?void 0:le.label)===x.label?"#0d1f45":"var(--panel)",border:`1px solid ${(le==null?void 0:le.label)===x.label?"var(--acc)":"var(--line)"}`,borderRadius:10,cursor:"pointer",fontSize:12,transition:"all .15s"},children:[l.jsx("span",{style:{marginRight:6},children:x.emoji}),l.jsx("b",{style:{color:"var(--text)"},children:x.label}),l.jsxs("span",{style:{marginLeft:6,color:"#f0b429",fontSize:11},children:["★ ",x.stars]}),l.jsx("span",{style:{marginLeft:8,color:"var(--muted)"},children:x.desc})]},x.label))}),le&&l.jsxs("div",{style:{marginTop:14,background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:16},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12,marginBottom:14},children:[l.jsx("span",{style:{fontSize:12,fontWeight:600},children:"目标 Agent:"}),l.jsxs("select",{value:se,onChange:x=>J(x.target.value),style:{padding:"6px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12},children:[l.jsx("option",{value:"",children:"— 选择 Agent —"}),o.agents.map(x=>l.jsxs("option",{value:x.id,children:[x.emoji," ",x.label," (",x.id,")"]},x.id))]})]}),l.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(260px, 1fr))",gap:8},children:le.skills.map(x=>{const U=m.some(ee=>ee.skillName===x.name&&ee.agentId===se);return l.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"8px 12px",background:"var(--panel2)",borderRadius:8,border:"1px solid var(--line)"},children:[l.jsxs("div",{children:[l.jsxs("div",{style:{fontSize:12,fontWeight:600},children:["📦 ",x.name]}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)",wordBreak:"break-all",maxWidth:180},children:x.url.split("/").slice(-2).join("/")})]}),U?l.jsx("span",{style:{fontSize:10,color:"#4caf88",fontWeight:600},children:"✓ 已导入"}):l.jsx("button",{onClick:()=>L(x.url,x.name),style:{padding:"4px 10px",background:"var(--acc)",color:"#fff",border:"none",borderRadius:6,cursor:"pointer",fontSize:11,whiteSpace:"nowrap"},children:"导入"})]},x.name)})})]})]}),g?l.jsx("div",{style:{textAlign:"center",padding:"40px 0",color:"var(--muted)",fontSize:13},children:"⟳ 加载中…"}):m.length===0?l.jsxs("div",{style:{textAlign:"center",padding:"40px",background:"var(--panel)",borderRadius:12,border:"1px dashed var(--line)"},children:[l.jsx("div",{style:{fontSize:32,marginBottom:10},children:"🌐"}),l.jsx("div",{style:{fontSize:14,color:"var(--muted)"},children:"尚无远程技能"}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginTop:6},children:"从社区技能源快速导入,或手动添加 URL"})]}):l.jsx("div",{style:{display:"flex",flexDirection:"column",gap:10},children:m.map(x=>{var Me;const U=`${x.agentId}/${x.skillName}`,ee=pe===U,he=be===U,ve=o.agents.find(mn=>mn.id===x.agentId);return l.jsxs("div",{style:{background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:"14px 18px",display:"grid",gridTemplateColumns:"1fr auto",gap:12,alignItems:"center"},children:[l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,marginBottom:6},children:[l.jsxs("span",{style:{fontSize:14,fontWeight:700},children:["📦 ",x.skillName]}),l.jsx("span",{style:{fontSize:10,padding:"2px 8px",borderRadius:999,background:x.status==="valid"?"#0d3322":"#3d1111",color:x.status==="valid"?"#4caf88":"#ff5270",fontWeight:600},children:x.status==="valid"?"✓ 有效":"✗ 文件丢失"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",background:"var(--panel2)",padding:"2px 8px",borderRadius:6},children:[ve==null?void 0:ve.emoji," ",(ve==null?void 0:ve.label)||x.agentId]})]}),x.description&&l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:4},children:x.description}),l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",display:"flex",gap:16,flexWrap:"wrap"},children:[l.jsxs("span",{children:["🔗 ",l.jsx("a",{href:x.sourceUrl,target:"_blank",rel:"noreferrer",style:{color:"var(--acc)",textDecoration:"none"},children:x.sourceUrl.length>60?x.sourceUrl.slice(0,60)+"…":x.sourceUrl})]}),l.jsxs("span",{children:["📅 ",x.lastUpdated?x.lastUpdated.slice(0,10):(Me=x.addedAt)==null?void 0:Me.slice(0,10)]})]})]}),l.jsxs("div",{style:{display:"flex",gap:8},children:[l.jsx("button",{onClick:()=>_e(x.agentId,x.skillName),style:{padding:"6px 12px",background:"transparent",color:"var(--muted)",border:"1px solid var(--line)",borderRadius:6,cursor:"pointer",fontSize:11},children:"查看"}),l.jsx("button",{onClick:()=>W(x),disabled:ee,style:{padding:"6px 12px",background:"transparent",color:"var(--acc)",border:"1px solid var(--acc)",borderRadius:6,cursor:"pointer",fontSize:11},children:ee?"⟳":"更新"}),l.jsx("button",{onClick:()=>v(x),disabled:he,style:{padding:"6px 12px",background:"transparent",color:"#ff5270",border:"1px solid #ff5270",borderRadius:6,cursor:"pointer",fontSize:11},children:he?"⟳":"删除"})]})]},U)})})]});return l.jsxs("div",{children:[l.jsx("div",{style:{display:"flex",gap:4,marginBottom:20,borderBottom:"1px solid var(--line)",paddingBottom:0},children:[{key:"local",label:"🏛️ 本地技能",count:o.agents.reduce((x,U)=>{var ee;return x+(((ee=U.skills)==null?void 0:ee.length)||0)},0)},{key:"remote",label:"🌐 远程技能",count:m.length}].map(x=>l.jsxs("div",{onClick:()=>w(x.key),style:{padding:"8px 18px",cursor:"pointer",fontSize:13,borderRadius:"8px 8px 0 0",fontWeight:d===x.key?700:400,background:d===x.key?"var(--panel)":"transparent",color:d===x.key?"var(--text)":"var(--muted)",border:d===x.key?"1px solid var(--line)":"1px solid transparent",borderBottom:d===x.key?"1px solid var(--panel)":"1px solid transparent",position:"relative",bottom:-1,transition:"all .15s"},children:[x.label,x.count>0&&l.jsx("span",{style:{marginLeft:6,fontSize:10,padding:"1px 6px",borderRadius:999,background:"#1a2040",color:"var(--acc)"},children:x.count})]},x.key))}),d==="local"?ae:ce,k&&l.jsx("div",{className:"modal-bg open",onClick:()=>S(null),children:l.jsxs("div",{className:"modal",onClick:x=>x.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>S(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:k.agentId.toUpperCase()}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:16},children:["📦 ",k.name]}),l.jsxs("div",{className:"sk-modal-body",children:[l.jsx("div",{className:"sk-md",style:{whiteSpace:"pre-wrap",fontSize:12,lineHeight:1.7},children:k.content}),k.path&&l.jsxs("div",{className:"sk-path",style:{fontSize:10,color:"var(--muted)",marginTop:12},children:["📂 ",k.path]})]})]})]})}),D&&l.jsx("div",{className:"modal-bg open",onClick:()=>F(null),children:l.jsxs("div",{className:"modal",onClick:x=>x.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>F(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsxs("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:["为 ",D.agentLabel," 添加技能"]}),l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:18},children:"+ 新增 Skill"}),l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:10,padding:14,marginBottom:18,fontSize:12,lineHeight:1.7,color:"var(--muted)"},children:[l.jsx("b",{style:{color:"var(--text)"},children:"📋 Skill 规范说明"}),l.jsx("br",{}),"• 技能名称使用",l.jsx("b",{style:{color:"var(--text)"},children:"小写英文 + 连字符"}),l.jsx("br",{}),"• 创建后会生成模板文件 SKILL.md",l.jsx("br",{}),"• 技能会在 agent 收到相关任务时",l.jsx("b",{style:{color:"var(--text)"},children:"自动激活"})]}),l.jsxs("form",{onSubmit:E,style:{display:"flex",flexDirection:"column",gap:14},children:[l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["技能名称 ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"text",required:!0,placeholder:"如 data-analysis, code-review",value:_.name,onChange:x=>R(U=>({...U,name:x.target.value.toLowerCase().replace(/[^a-z0-9-]/g,"")})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"技能描述"}),l.jsx("input",{type:"text",placeholder:"一句话说明用途",value:_.desc,onChange:x=>R(U=>({...U,desc:x.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"触发条件(可选)"}),l.jsx("input",{type:"text",placeholder:"何时激活此技能",value:_.trigger,onChange:x=>R(U=>({...U,trigger:x.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end",marginTop:4},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:()=>F(null),style:{padding:"8px 20px"},children:"取消"}),l.jsx("button",{type:"submit",disabled:C,style:{padding:"8px 20px",fontSize:13,background:"var(--acc)",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600},children:C?"⟳ 创建中…":"📦 创建技能"})]})]})]})]})}),P&&l.jsx("div",{className:"modal-bg open",onClick:()=>B(!1),children:l.jsxs("div",{className:"modal",style:{maxWidth:520},onClick:x=>x.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>B(!1),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"#a07aff",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:"远程技能管理"}),l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:18},children:"🌐 添加远程 Skill"}),l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:10,padding:12,marginBottom:18,fontSize:11,color:"var(--muted)",lineHeight:1.7},children:["支持 GitHub Raw URL,如:",l.jsx("br",{}),l.jsx("code",{style:{color:"var(--acc)",fontSize:10},children:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/brainstorming/SKILL.md"})]}),l.jsxs("form",{onSubmit:Q,style:{display:"flex",flexDirection:"column",gap:14},children:[l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["目标 Agent ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsxs("select",{required:!0,value:Z.agentId,onChange:x=>I(U=>({...U,agentId:x.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13},children:[l.jsx("option",{value:"",children:"— 选择 Agent —"}),o.agents.map(x=>l.jsxs("option",{value:x.id,children:[x.emoji," ",x.label," (",x.id,")"]},x.id))]})]}),l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["技能名称 ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"text",required:!0,placeholder:"如 brainstorming, code-review",value:Z.skillName,onChange:x=>I(U=>({...U,skillName:x.target.value.toLowerCase().replace(/[^a-z0-9-]/g,"")})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["源 URL ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"url",required:!0,placeholder:"https://raw.githubusercontent.com/...",value:Z.sourceUrl,onChange:x=>I(U=>({...U,sourceUrl:x.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:12,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"描述(可选)"}),l.jsx("input",{type:"text",placeholder:"一句话说明用途",value:Z.description,onChange:x=>I(U=>({...U,description:x.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end",marginTop:4},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:()=>B(!1),style:{padding:"8px 20px"},children:"取消"}),l.jsx("button",{type:"submit",disabled:oe,style:{padding:"8px 20px",fontSize:13,background:"#a07aff",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600},children:oe?"⟳ 下载中…":"🌐 添加远程技能"})]})]})]})]})})]})}function tp(){const o=q(k=>k.agentConfig),p={},u={};return o!=null&&o.agents&&o.agents.forEach(k=>{p[k.id]=k.emoji||"🏛️",u[k.id]=k.label||k.id}),{emojiMap:p,labelMap:u}}function Fl(o){const p=(o.id||"").match(/^OC-(\w+)-/);return p?p[1]:(o.org||"").replace(/省|部/g,"").toLowerCase()}function cc(o,p){let u=o.title||"";if(u==="heartbeat 会话")return"💓 心跳检测";const k=u.match(/^agent:(\w+):(\w+)/);if(k){const S=p[k[1]]||k[1];return k[2]==="main"?S+" · 主会话":k[2]==="subagent"?S+" · 子任务执行":k[2]==="cron"?S+" · 定时任务":S+" · "+k[2]}return u.replace(/ 会话$/,"")||o.id}function dc(o){const p=o.now||"";return p.includes("feishu/direct")?{icon:"💬",text:"飞书对话"}:p.includes("feishu")?{icon:"💬",text:"飞书"}:p.includes("webchat")?{icon:"🌐",text:"WebChat"}:p.includes("cron")?{icon:"⏰",text:"定时"}:p.includes("direct")?{icon:"📨",text:"直连"}:{icon:"🔗",text:"会话"}}function np(o){const p=o.activity||[];for(let u=p.length-1;u>=0;u--){const k=p[u];if(k.kind==="assistant"){let S=k.text||"";if(S.startsWith("NO_REPLY")||S.startsWith("Reasoning:"))continue;return S=S.replace(/\[\[.*?\]\]/g,"").replace(/\*\*/g,"").replace(/^#+\s/gm,"").trim(),S.substring(0,120)+(S.length>120?"…":"")}}return""}function rp(){const o=q(d=>d.liveStatus),p=q(d=>d.sessFilter),u=q(d=>d.setSessFilter),{emojiMap:k,labelMap:S}=tp(),[D,F]=te.useState(null),R=((o==null?void 0:o.tasks)||[]).filter(d=>!Jt(d));let C=R;p==="active"?C=R.filter(d=>!["Done","Cancelled"].includes(d.state)):p!=="all"&&(C=R.filter(d=>Fl(d)===p));const z=[...new Set(R.map(Fl))];return l.jsxs("div",{children:[l.jsx("div",{style:{display:"flex",gap:6,marginBottom:16,flexWrap:"wrap"},children:[{key:"all",label:`全部 (${R.length})`},{key:"active",label:"活跃"},...z.slice(0,8).map(d=>({key:d,label:S[d]||d}))].map(d=>l.jsx("span",{className:`sess-filter${p===d.key?" active":""}`,onClick:()=>u(d.key),children:d.label},d.key))}),l.jsx("div",{className:"sess-grid",children:C.length?C.map(d=>{const w=Fl(d),m=k[w]||"🏛️",T=S[w]||d.org||w,g=d.heartbeat||{status:"unknown",label:""},M=dc(d),P=cc(d,S),B=np(d),I=(d.sourceMeta||{}).totalTokens,oe=d.eta||"",ue=g.status==="active"?"🟢":g.status==="warn"?"🟡":g.status==="stalled"?"🔴":"⚪",pe=d.state||"Unknown";return l.jsxs("div",{className:"sess-card",onClick:()=>F(d),children:[l.jsxs("div",{className:"sc-top",children:[l.jsx("span",{className:"sc-emoji",children:m}),l.jsx("div",{style:{flex:1,minWidth:0},children:l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{className:"sc-agent",children:T}),l.jsxs("span",{style:{fontSize:10,color:"var(--muted)",background:"var(--panel2)",padding:"2px 6px",borderRadius:4},children:[M.icon," ",M.text]})]})}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{title:g.label||"",children:ue}),l.jsx("span",{className:`tag st-${pe}`,style:{fontSize:10},children:zr[pe]||pe})]})]}),l.jsx("div",{className:"sc-title",children:P}),B&&l.jsx("div",{style:{fontSize:11,color:"var(--muted)",lineHeight:1.5,marginBottom:8,borderLeft:"2px solid var(--line)",paddingLeft:8,maxHeight:40,overflow:"hidden"},children:B}),l.jsxs("div",{className:"sc-meta",children:[I?l.jsxs("span",{style:{fontSize:10,color:"var(--muted)"},children:["🪙 ",I.toLocaleString()," tokens"]}):null,oe?l.jsx("span",{className:"sc-time",children:Uf(oe)}):null]})]},d.id)}):l.jsx("div",{style:{fontSize:13,color:"var(--muted)",padding:24,textAlign:"center",gridColumn:"1/-1"},children:"暂无小任务/会话数据"})}),D&&l.jsx(lp,{task:D,labelMap:S,emojiMap:k,onClose:()=>F(null)})]})}function lp({task:o,labelMap:p,emojiMap:u,onClose:k}){const S=Fl(o),D=u[S]||"🏛️",F=cc(o,p),_=dc(o),R=o.heartbeat||{label:""},C=o.sourceMeta||{},z=o.activity||[],d=o.state||"Unknown",w=C.totalTokens,m=C.inputTokens,T=C.outputTokens;return l.jsx("div",{className:"modal-bg open",onClick:k,children:l.jsxs("div",{className:"modal",onClick:g=>g.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:k,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:o.id}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[D," ",F]}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:18,flexWrap:"wrap"},children:[l.jsx("span",{className:`tag st-${d}`,children:zr[d]||d}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:[_.icon," ",_.text]}),R.label&&l.jsx("span",{style:{fontSize:11},children:R.label})]}),l.jsxs("div",{style:{display:"flex",gap:14,marginBottom:18,flexWrap:"wrap"},children:[w!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700,color:"var(--acc)"},children:w.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"总 Tokens"})]}),m!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700},children:m.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"输入"})]}),T!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700},children:T.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"输出"})]})]}),l.jsxs("div",{style:{fontSize:12,fontWeight:700,marginBottom:8},children:["📋 最近活动 ",l.jsxs("span",{style:{fontWeight:400,color:"var(--muted)"},children:["(",z.length," 条)"]})]}),l.jsx("div",{style:{maxHeight:350,overflowY:"auto",border:"1px solid var(--line)",borderRadius:10,background:"var(--panel2)"},children:z.length?z.slice(-15).reverse().map((g,M)=>{const P=g.kind||"",B=P==="assistant"?"🤖":P==="tool"?"🔧":P==="user"?"👤":"📝",Z=P==="assistant"?"回复":P==="tool"?"工具":P==="user"?"用户":"事件";let I=(g.text||"").replace(/\[\[.*?\]\]/g,"").replace(/\*\*/g,"").trim();I.length>200&&(I=I.substring(0,200)+"…");const oe=(g.at||"").substring(11,19);return l.jsxs("div",{style:{padding:"8px 12px",borderBottom:"1px solid var(--line)",fontSize:12,lineHeight:1.5},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:3},children:[l.jsx("span",{children:B}),l.jsx("span",{style:{fontWeight:600,fontSize:11},children:Z}),l.jsx("span",{style:{color:"var(--muted)",fontSize:10,marginLeft:"auto"},children:oe})]}),l.jsx("div",{style:{color:"var(--muted)"},children:I})]},M)}):l.jsx("div",{style:{padding:16,color:"var(--muted)",fontSize:12,textAlign:"center"},children:"暂无活动记录"})}),o.output&&o.output!=="-"&&l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",marginTop:12,wordBreak:"break-all",borderTop:"1px solid var(--line)",paddingTop:8},children:["📂 ",o.output]})]})]})})}function sp(){const o=q(C=>C.liveStatus),[p,u]=te.useState("all"),[k,S]=te.useState(null),D=q(C=>C.toast);let _=((o==null?void 0:o.tasks)||[]).filter(C=>Jt(C)&&["Done","Cancelled"].includes(C.state));p!=="all"&&(_=_.filter(C=>C.state===p));const R=C=>{const z=C.flow_log||[];let d=`# 📜 奏折 · ${C.title} - -`;if(d+=`- **任务编号**: ${C.id} -`,d+=`- **状态**: ${C.state} -`,d+=`- **负责部门**: ${C.org} -`,z.length){const w=z[0].at?z[0].at.substring(0,19).replace("T"," "):"未知",m=z[z.length-1].at?z[z.length-1].at.substring(0,19).replace("T"," "):"未知";d+=`- **开始时间**: ${w} -`,d+=`- **完成时间**: ${m} -`}d+=` -## 流转记录 - -`;for(const w of z)d+=`- **${w.from}** → **${w.to}** - ${w.remark} - _${(w.at||"").substring(0,19)}_ - -`;C.output&&C.output!=="-"&&(d+=`## 产出物 - -\`${C.output}\` -`),navigator.clipboard.writeText(d).then(()=>D("✅ 奏折已复制为 Markdown","ok"),()=>D("复制失败","err"))};return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:8,marginBottom:16,alignItems:"center"},children:[l.jsx("span",{style:{fontSize:12,color:"var(--muted)"},children:"筛选:"}),[{key:"all",label:"全部"},{key:"Done",label:"✅ 已完成"},{key:"Cancelled",label:"🚫 已取消"}].map(C=>l.jsx("span",{className:`sess-filter${p===C.key?" active":""}`,onClick:()=>u(C.key),children:C.label},C.key))]}),l.jsx("div",{className:"mem-list",children:_.length?_.map(C=>{const z=C.flow_log||[],d=[...new Set(z.map(g=>g.from).concat(z.map(g=>g.to)).filter(g=>g&&g!=="皇上"))],w=z.length?(z[0].at||"").substring(0,16).replace("T"," "):"",m=z.length?(z[z.length-1].at||"").substring(0,16).replace("T"," "):"",T=C.state==="Done"?"✅":"🚫";return l.jsxs("div",{className:"mem-card",onClick:()=>S(C),children:[l.jsx("div",{className:"mem-icon",children:"📜"}),l.jsxs("div",{className:"mem-info",children:[l.jsxs("div",{className:"mem-title",children:[T," ",C.title||C.id]}),l.jsxs("div",{className:"mem-sub",children:[C.id," · ",C.org||""," · 流转 ",z.length," 步"]}),l.jsx("div",{className:"mem-tags",children:d.slice(0,5).map(g=>l.jsx("span",{className:"mem-tag",children:g},g))})]}),l.jsxs("div",{className:"mem-right",children:[l.jsx("span",{className:"mem-date",children:w}),m!==w&&l.jsx("span",{className:"mem-date",children:m})]})]},C.id)}):l.jsx("div",{className:"mem-empty",children:"暂无奏折 — 任务完成后自动生成"})}),k&&l.jsx(ip,{task:k,onClose:()=>S(null),onExport:R})]})}function ip({task:o,onClose:p,onExport:u}){const k=o.flow_log||[],S=o.state||"Unknown",D=S==="Done"?"✅":S==="Cancelled"?"🚫":"🔄",F=[...new Set(k.map(m=>m.from).concat(k.map(m=>m.to)).filter(m=>m&&m!=="皇上"))],_=[],R=[],C=[],z=[],d=[];for(const m of k)m.from==="皇上"?_.push(m):m.to==="中书省"||m.from==="中书省"?R.push(m):m.to==="门下省"||m.from==="门下省"?C.push(m):m.remark&&(m.remark.includes("完成")||m.remark.includes("回奏"))?d.push(m):z.push(m);const w=(m,T,g)=>g.length?l.jsxs("div",{style:{marginBottom:18},children:[l.jsxs("div",{style:{fontSize:13,fontWeight:700,marginBottom:10},children:[T," ",m]}),l.jsx("div",{className:"md-timeline",children:g.map((M,P)=>{var Z,I;const B=(Z=M.remark)!=null&&Z.includes("✅")?"green":(I=M.remark)!=null&&I.includes("驳")?"red":"";return l.jsxs("div",{className:"md-tl-item",children:[l.jsx("div",{className:`md-tl-dot ${B}`}),l.jsxs("div",{style:{display:"flex",gap:6,alignItems:"baseline"},children:[l.jsx("span",{className:"md-tl-from",children:M.from}),l.jsxs("span",{className:"md-tl-to",children:["→ ",M.to]})]}),l.jsx("div",{className:"md-tl-remark",children:M.remark}),l.jsx("div",{className:"md-tl-time",children:(M.at||"").substring(0,19).replace("T"," ")})]},P)})})]}):null;return l.jsx("div",{className:"modal-bg open",onClick:p,children:l.jsxs("div",{className:"modal",onClick:m=>m.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:p,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:o.id}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[D," ",o.title||o.id]}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:18,flexWrap:"wrap"},children:[l.jsx("span",{className:`tag st-${S}`,children:zr[S]||S}),l.jsx("span",{style:{fontSize:11,color:"var(--muted)"},children:o.org}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["流转 ",k.length," 步"]}),F.map(m=>l.jsx("span",{className:"mem-tag",children:m},m))]}),o.now&&l.jsx("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:"10px 14px",marginBottom:18,fontSize:12,color:"var(--muted)"},children:o.now}),w("圣旨原文","👑",_),w("中书规划","📋",R),w("门下审议","🔍",C),w("六部执行","⚔️",z),w("汇总回奏","📨",d),o.output&&o.output!=="-"&&l.jsxs("div",{style:{marginTop:12,paddingTop:12,borderTop:"1px solid var(--line)"},children:[l.jsx("div",{style:{fontSize:11,fontWeight:600,marginBottom:4},children:"📦 产出物"}),l.jsx("code",{style:{fontSize:11,wordBreak:"break-all"},children:o.output})]}),l.jsx("div",{style:{display:"flex",gap:8,marginTop:16,justifyContent:"flex-end"},children:l.jsx("button",{className:"btn btn-g",onClick:()=>u(o),style:{fontSize:12,padding:"6px 16px"},children:"📋 复制奏折"})})]})]})})}function op(){const o=q(g=>g.tplCatFilter),p=q(g=>g.setTplCatFilter),u=q(g=>g.toast),k=q(g=>g.loadAll),[S,D]=te.useState(null),[F,_]=te.useState({}),[R,C]=te.useState("");let z=bf;o!=="全部"&&(z=z.filter(g=>g.cat===o));const d=g=>{const M={};g.params.forEach(P=>{M[P.key]=P.default||""}),_(M),D(g),C("")},w=g=>{let M=g.command;for(const P of g.params)M=M.replace(new RegExp("\\{"+P.key+"\\}","g"),F[P.key]||P.default||"");return M},m=()=>{S&&C(w(S))},T=async g=>{if(g.preventDefault(),!S)return;const M=w(S);if(!M.trim()){u("请填写必填参数","err");return}try{const P=await fe.agentsStatus();if(P.ok&&P.gateway&&!P.gateway.alive&&(u("⚠️ Gateway 未启动,任务将无法派发!","err"),!confirm("Gateway 未启动,继续?")))return}catch{}if(confirm(`确认下旨? - -${M.substring(0,200)}${M.length>200?"…":""}`))try{const P={};for(const Z of S.params)P[Z.key]=F[Z.key]||Z.default||"";const B=await fe.createTask({title:M.substring(0,120),org:"中书省",targetDept:S.depts[0]||"",priority:"normal",templateId:S.id,params:P});B.ok?(u(`📜 ${B.taskId} 旨意已下达`,"ok"),D(null),k()):u(B.error||"下旨失败","err")}catch{u("⚠️ 服务器连接失败","err")}};return l.jsxs("div",{children:[l.jsx("div",{style:{display:"flex",gap:6,marginBottom:16,flexWrap:"wrap"},children:$f.map(g=>l.jsxs("span",{className:`tpl-cat${o===g.name?" active":""}`,onClick:()=>p(g.name),children:[g.icon," ",g.name]},g.name))}),l.jsx("div",{className:"tpl-grid",children:z.map(g=>l.jsxs("div",{className:"tpl-card",children:[l.jsxs("div",{className:"tpl-top",children:[l.jsx("span",{className:"tpl-icon",children:g.icon}),l.jsx("span",{className:"tpl-name",children:g.name})]}),l.jsx("div",{className:"tpl-desc",children:g.desc}),l.jsxs("div",{className:"tpl-footer",children:[g.depts.map(M=>l.jsx("span",{className:"tpl-dept",children:M},M)),l.jsxs("span",{className:"tpl-est",children:[g.est," · ",g.cost]}),l.jsx("button",{className:"tpl-go",onClick:()=>d(g),children:"下旨"})]})]},g.id))}),S&&l.jsx("div",{className:"modal-bg open",onClick:()=>D(null),children:l.jsxs("div",{className:"modal",onClick:g=>g.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>D(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:"圣旨模板"}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[S.icon," ",S.name]}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:18},children:S.desc}),l.jsxs("div",{style:{display:"flex",gap:6,marginBottom:18,flexWrap:"wrap"},children:[S.depts.map(g=>l.jsx("span",{className:"tpl-dept",children:g},g)),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:"auto"},children:[S.est," · ",S.cost]})]}),l.jsxs("form",{className:"tpl-form",onSubmit:T,children:[S.params.map(g=>l.jsxs("div",{className:"tpl-field",children:[l.jsxs("label",{className:"tpl-label",children:[g.label,g.required&&l.jsx("span",{style:{color:"#ff5270"},children:" *"})]}),g.type==="textarea"?l.jsx("textarea",{className:"tpl-input",style:{minHeight:80,resize:"vertical"},required:g.required,value:F[g.key]||"",onChange:M=>_(P=>({...P,[g.key]:M.target.value}))}):g.type==="select"?l.jsx("select",{className:"tpl-input",value:F[g.key]||g.default||"",onChange:M=>_(P=>({...P,[g.key]:M.target.value})),children:(g.options||[]).map(M=>l.jsx("option",{children:M},M))}):l.jsx("input",{className:"tpl-input",type:"text",required:g.required,value:F[g.key]||"",onChange:M=>_(P=>({...P,[g.key]:M.target.value}))})]},g.key)),R&&l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:12,marginBottom:14,fontSize:12,color:"var(--muted)"},children:[l.jsx("div",{style:{fontSize:11,fontWeight:600,color:"var(--text)",marginBottom:6},children:"📜 将发送给中书省的旨意:"}),l.jsx("div",{style:{whiteSpace:"pre-wrap",lineHeight:1.6},children:R})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end"},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:m,style:{padding:"8px 16px",fontSize:12},children:"👁 预览旨意"}),l.jsx("button",{type:"submit",className:"tpl-go",style:{padding:"8px 20px",fontSize:13},children:"📜 下旨"})]})]})]})]})})]})}const fc={政治:{icon:"🏛️",color:"#6a9eff",desc:"全球政治动态"},军事:{icon:"⚔️",color:"#ff5270",desc:"军事与冲突"},经济:{icon:"💹",color:"#2ecc8a",desc:"经济与市场"},AI大模型:{icon:"🤖",color:"#a07aff",desc:"AI与大模型进展"}},Hi=["政治","军事","经济","AI大模型"];function ap(){const o=q(X=>X.morningBrief),p=q(X=>X.subConfig),u=q(X=>X.loadMorning),k=q(X=>X.loadSubConfig),S=q(X=>X.toast),[D,F]=te.useState(!1),[_,R]=te.useState(null),[C,z]=te.useState(!1),[d,w]=te.useState("⟳ 立即采集"),m=te.useRef(null);te.useEffect(()=>{u()},[u]),te.useEffect(()=>{p&&R(JSON.parse(JSON.stringify(p)))},[p]),te.useEffect(()=>()=>{m.current&&clearInterval(m.current)},[]);const T=async()=>{z(!0),w("⟳ 采集中…");let X=null;try{X=(o==null?void 0:o.generated_at)||null}catch{}try{await fe.refreshMorning(),S("采集已触发,自动检测更新中…","ok");let le=0;m.current&&clearInterval(m.current),m.current=setInterval(async()=>{if(le++,le>24){clearInterval(m.current),m.current=null,z(!1),w("⟳ 立即采集"),S("采集超时,请重试","err");return}try{const ke=await fe.morningBrief();ke.generated_at&&ke.generated_at!==X?(clearInterval(m.current),m.current=null,z(!1),w("⟳ 立即采集"),u(),S("✅ 天下要闻已更新","ok")):w(`⟳ 采集中… (${le*5}s)`)}catch{}},5e3)}catch{S("触发失败","err"),z(!1),w("⟳ 立即采集")}},g=X=>{if(!_)return;const le=[..._.categories||[]],ke=le.find(se=>se.name===X);ke?ke.enabled=!ke.enabled:le.push({name:X,enabled:!0}),R({..._,categories:le})},M=X=>{if(!_||!X)return;const le=[..._.keywords||[]];le.includes(X)||le.push(X),R({..._,keywords:le})},P=X=>{if(!_)return;const le=[..._.keywords||[]];le.splice(X,1),R({..._,keywords:le})},B=(X,le,ke)=>{if(!_||!X||!le){S("请填写源名称和URL","err");return}const se=[..._.custom_feeds||[]];se.push({name:X,url:le,category:ke}),R({..._,custom_feeds:se})},Z=X=>{if(!_)return;const le=[..._.custom_feeds||[]];le.splice(X,1),R({..._,custom_feeds:le})},I=async()=>{if(_)try{const X=await fe.saveMorningConfig(_);X.ok?(S("订阅配置已保存","ok"),k()):S(X.error||"保存失败","err")}catch{S("服务器连接失败","err")}},oe=_?new Set((_.categories||[]).filter(X=>X.enabled).map(X=>X.name)):new Set(Hi),ue=((_==null?void 0:_.keywords)||[]).map(X=>X.toLowerCase()),pe=(o==null?void 0:o.categories)||{},me=o!=null&&o.date?o.date.replace(/(\d{4})(\d{2})(\d{2})/,"$1年$2月$3日"):"",be=Object.values(pe).flat().length;return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:16},children:[l.jsxs("div",{children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:4},children:"🌅 天下要闻"}),l.jsxs("div",{style:{fontSize:12,color:"var(--muted)"},children:[me&&`${me} | `,(o==null?void 0:o.generated_at)&&`采集于 ${o.generated_at} | `,"共 ",be," 条要闻"]})]}),l.jsxs("div",{style:{display:"flex",gap:8},children:[l.jsx("button",{className:"btn btn-g",onClick:()=>F(!D),style:{fontSize:12,padding:"6px 14px"},children:"⚙ 订阅配置"}),l.jsx("button",{className:"tpl-go",disabled:C,onClick:T,style:{fontSize:12,padding:"6px 14px"},children:d})]})]}),D&&_&&l.jsx(up,{config:_,enabledSet:oe,onToggleCat:g,onAddKeyword:M,onRemoveKeyword:P,onAddFeed:B,onRemoveFeed:Z,onSave:I,onSetWebhook:X=>R({..._,feishu_webhook:X})}),Object.keys(pe).length?l.jsx("div",{className:"mb-cats",children:Object.entries(pe).map(([X,le])=>{if(!oe.has(X))return null;const ke=fc[X]||{icon:"📰",color:"var(--acc)"},se=le.map(J=>{const Ne=((J.title||"")+(J.summary||"")).toLowerCase(),_e=ue.filter(xe=>Ne.includes(xe)).length;return{...J,_kwHits:_e}}).sort((J,Ne)=>Ne._kwHits-J._kwHits);return l.jsxs("div",{className:"mb-cat",children:[l.jsxs("div",{className:"mb-cat-hdr",children:[l.jsx("span",{className:"mb-cat-icon",children:ke.icon}),l.jsx("span",{className:"mb-cat-name",style:{color:ke.color},children:X}),l.jsxs("span",{className:"mb-cat-cnt",children:[se.length," 条"]})]}),l.jsx("div",{className:"mb-news-list",children:se.length?se.map((J,Ne)=>{const _e=!!(J.image&&J.image.startsWith("http"));return l.jsxs("div",{className:"mb-card",onClick:()=>window.open(J.link,"_blank"),children:[l.jsx("div",{className:"mb-img",children:_e?l.jsx("img",{src:J.image,onError:xe=>{xe.target.style.display="none"},loading:"lazy",alt:""}):l.jsx("span",{children:ke.icon})}),l.jsxs("div",{className:"mb-info",children:[l.jsxs("div",{className:"mb-headline",children:[J.title,J._kwHits>0&&l.jsx("span",{style:{fontSize:9,padding:"1px 5px",borderRadius:999,background:"#a07aff22",color:"#a07aff",border:"1px solid #a07aff44",marginLeft:4},children:"⭐ 关注"})]}),l.jsx("div",{className:"mb-summary",children:J.summary||J.desc||""}),l.jsxs("div",{className:"mb-meta",children:[l.jsxs("span",{className:"mb-source",children:["📡 ",J.source||""]}),J.pub_date&&l.jsx("span",{className:"mb-time",children:J.pub_date.substring(0,16)})]})]})]},Ne)}):l.jsx("div",{className:"mb-empty",style:{padding:16},children:"暂无新闻"})})]},X)})}):l.jsx("div",{className:"mb-empty",children:"暂无数据,点击右上角「立即采集」获取今日简报"})]})}function up({config:o,enabledSet:p,onToggleCat:u,onAddKeyword:k,onRemoveKeyword:S,onAddFeed:D,onRemoveFeed:F,onSave:_,onSetWebhook:R}){const[C,z]=te.useState(""),[d,w]=te.useState(""),[m,T]=te.useState(""),[g,M]=te.useState(Hi[0]),P=[...Hi];return(o.categories||[]).forEach(B=>{P.includes(B.name)||P.push(B.name)}),l.jsxs("div",{className:"sub-config",style:{marginBottom:20,padding:16,background:"var(--panel2)",borderRadius:12,border:"1px solid var(--line)"},children:[l.jsx("div",{style:{fontSize:14,fontWeight:700,marginBottom:12},children:"⚙ 订阅配置"}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"订阅分类"}),l.jsx("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:P.map(B=>{const Z=fc[B]||{icon:"📰"},I=p.has(B);return l.jsxs("div",{className:`sub-cat ${I?"active":""}`,onClick:()=>u(B),style:{cursor:"pointer",padding:"6px 12px",borderRadius:8,border:`1px solid ${I?"var(--acc)":"var(--line)"}`,display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{children:Z.icon}),l.jsx("span",{style:{fontSize:12},children:B}),I&&l.jsx("span",{style:{fontSize:10,color:"var(--ok)"},children:"✓"})]},B)})})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"关注关键词"}),l.jsx("div",{style:{display:"flex",gap:6,flexWrap:"wrap",marginBottom:6},children:(o.keywords||[]).map((B,Z)=>l.jsxs("span",{className:"sub-kw",style:{fontSize:11,padding:"2px 8px",borderRadius:4,background:"var(--bg)",border:"1px solid var(--line)"},children:[B,l.jsx("span",{style:{cursor:"pointer",marginLeft:4,color:"var(--danger)"},onClick:()=>S(Z),children:"✕"})]},Z))}),l.jsxs("div",{style:{display:"flex",gap:6},children:[l.jsx("input",{type:"text",value:C,onChange:B=>z(B.target.value),placeholder:"输入关键词",onKeyDown:B=>{B.key==="Enter"&&(k(C.trim()),z(""))},style:{flex:1,padding:"6px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12,outline:"none"}}),l.jsx("button",{className:"btn btn-g",onClick:()=>{k(C.trim()),z("")},style:{fontSize:11,padding:"4px 12px"},children:"添加"})]})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"自定义信息源"}),(o.custom_feeds||[]).map((B,Z)=>l.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center",marginBottom:4,fontSize:11},children:[l.jsx("span",{style:{fontWeight:600},children:B.name}),l.jsx("span",{style:{color:"var(--muted)",flex:1,overflow:"hidden",textOverflow:"ellipsis"},children:B.url}),l.jsx("span",{style:{color:"var(--acc)"},children:B.category}),l.jsx("span",{style:{cursor:"pointer",color:"var(--danger)"},onClick:()=>F(Z),children:"✕"})]},Z)),l.jsxs("div",{style:{display:"flex",gap:6,marginTop:6},children:[l.jsx("input",{placeholder:"源名称",value:d,onChange:B=>w(B.target.value),style:{width:100,padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"}}),l.jsx("input",{placeholder:"RSS / URL",value:m,onChange:B=>T(B.target.value),style:{flex:1,padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"}}),l.jsx("select",{value:g,onChange:B=>M(B.target.value),style:{padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"},children:P.map(B=>l.jsx("option",{value:B,children:B},B))}),l.jsx("button",{className:"btn btn-g",onClick:()=>{D(d,m,g),w(""),T("")},style:{fontSize:11,padding:"4px 12px"},children:"添加"})]})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:6},children:"飞书 Webhook"}),l.jsx("input",{type:"text",value:o.feishu_webhook||"",onChange:B=>R(B.target.value),placeholder:"https://open.feishu.cn/open-apis/bot/v2/hook/...",style:{width:"100%",padding:"8px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12,outline:"none"}})]}),l.jsx("div",{style:{display:"flex",justifyContent:"flex-end"},children:l.jsx("button",{className:"tpl-go",onClick:_,style:{fontSize:12,padding:"6px 16px"},children:"💾 保存配置"})})]})}const pc={main:"太子",zhongshu:"中书省",menxia:"门下省",shangshu:"尚书省",libu:"礼部",hubu:"户部",bingbu:"兵部",xingbu:"刑部",gongbu:"工部",libu_hr:"吏部",zaochao:"钦天监"},cp={Taizi:"中书省起草",Zhongshu:"门下省审议",Menxia:"尚书省派发",Assigned:"开始执行",Doing:"进入审查",Review:"完成"};function dp(o){const p=Math.max(0,o);if(p<60)return`${p}秒`;if(p<3600)return`${Math.floor(p/60)}分${p%60}秒`;const u=Math.floor(p/3600),k=Math.floor(p%3600/60);return`${u}小时${k}分`}function Vi(o){if(!o)return"";if(typeof o=="number"){const p=new Date(o);return`${String(p.getHours()).padStart(2,"0")}:${String(p.getMinutes()).padStart(2,"0")}:${String(p.getSeconds()).padStart(2,"0")}`}return typeof o=="string"&&o.length>=19?o.substring(11,19):String(o).substring(0,8)}function fp(){var _e,xe;const o=q(E=>E.modalTaskId),p=q(E=>E.setModalTaskId),u=q(E=>E.liveStatus),k=q(E=>E.loadAll),S=q(E=>E.toast),[D,F]=te.useState(null),[_,R]=te.useState(null),C=te.useRef(null),z=te.useRef(null),d=((_e=u==null?void 0:u.tasks)==null?void 0:_e.find(E=>E.id===o))||null,w=te.useCallback(async()=>{if(o)try{const E=await fe.taskActivity(o);F(E)}catch{F(null)}},[o]),m=te.useCallback(async()=>{if(o)try{const E=await fe.schedulerState(o);R(E)}catch{R(null)}},[o]);if(te.useEffect(()=>!o||!d?void 0:(w(),m(),["Done","Cancelled"].includes(d.state)||(C.current=setInterval(()=>{w(),m()},4e3)),()=>{C.current&&(clearInterval(C.current),C.current=null)}),[o,d==null?void 0:d.state,w,m]),te.useEffect(()=>{z.current&&(z.current.scrollTop=z.current.scrollHeight)},[(xe=D==null?void 0:D.activity)==null?void 0:xe.length]),!o||!d)return null;const T=()=>p(null),g=Gi(d),M=g.find(E=>E.status==="active"),P=d.heartbeat||{status:"unknown",label:"⚪ 无数据"},B=d.flow_log||[],Z=d.todos||[],I=Z.filter(E=>E.status==="completed").length,oe=Z.length,ue=!["Done","Blocked","Cancelled"].includes(d.state),pe=["Blocked","Cancelled"].includes(d.state),me=async(E,Q)=>{try{const W=await fe.taskAction(d.id,E,Q);W.ok?(S(W.message||"操作成功","ok"),k(),T()):S(W.error||"操作失败","err")}catch{S("服务器连接失败","err")}},be=async E=>{const Q={approve:"准奏",reject:"封驳"},W=prompt(`${Q[E]} ${d.id} - -请输入批注(可留空):`);if(W!==null)try{const v=await fe.reviewAction(d.id,E,W||"");v.ok?(S(`✅ ${d.id} 已${Q[E]}`,"ok"),k(),T()):S(v.error||"操作失败","err")}catch{S("服务器连接失败","err")}},X=async()=>{const E=cp[d.state]||"下一步",Q=prompt(`⏩ 手动推进 ${d.id} -当前: ${d.state} → 下一步: ${E} - -请输入说明(可留空):`);if(Q!==null)try{const W=await fe.advanceState(d.id,Q||"");W.ok?(S(`⏩ ${W.message}`,"ok"),k(),T()):S(W.error||"推进失败","err")}catch{S("服务器连接失败","err")}},le=async E=>{if(E==="scan"){try{const L=await fe.schedulerScan(180);L.ok?S(`🔍 扫描完成:${L.count||0} 个动作`,"ok"):S(L.error||"扫描失败","err"),m()}catch{S("服务器连接失败","err")}return}const W=prompt(`请输入${{retry:"重试",escalate:"升级",rollback:"回滚"}[E]}原因(可留空):`);if(W===null)return;const v={retry:fe.schedulerRetry,escalate:fe.schedulerEscalate,rollback:fe.schedulerRollback};try{const L=await v[E](d.id,W);L.ok?S(L.message||"操作成功","ok"):S(L.error||"操作失败","err"),m(),k()}catch{S("服务器连接失败","err")}},ke=()=>{const E=prompt("请输入叫停原因(可留空):");E!==null&&me("stop",E)},se=()=>{if(!confirm(`确定要取消 ${d.id} 吗?`))return;const E=prompt("请输入取消原因(可留空):");E!==null&&me("cancel",E)},J=_==null?void 0:_.scheduler,Ne=(_==null?void 0:_.stalledSec)||0;return l.jsx("div",{className:"modal-bg open",onClick:T,children:l.jsxs("div",{className:"modal",onClick:E=>E.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:T,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{className:"modal-id",children:d.id}),l.jsx("div",{className:"modal-title",children:d.title||"(无标题)"}),M&&l.jsxs("div",{className:"cur-stage",children:[l.jsx("div",{className:"cs-icon",children:M.icon}),l.jsxs("div",{className:"cs-info",children:[l.jsx("div",{className:"cs-dept",style:{color:$l(M.dept)},children:M.dept}),l.jsxs("div",{className:"cs-action",children:["当前阶段:",M.action]})]}),l.jsx("span",{className:`hb ${P.status} cs-hb`,children:P.label})]}),l.jsx("div",{className:"m-pipe",children:g.map((E,Q)=>l.jsxs("div",{className:"mp-stage",children:[l.jsxs("div",{className:`mp-node ${E.status}`,children:[E.status==="done"&&l.jsx("div",{className:"mp-done-tick",children:"✓"}),l.jsx("div",{className:"mp-icon",children:E.icon}),l.jsx("div",{className:"mp-dept",style:E.status==="active"?{color:"var(--acc)"}:E.status==="done"?{color:"var(--ok)"}:{},children:E.dept}),l.jsx("div",{className:"mp-action",children:E.action})]}),Qme("resume","恢复执行"),children:"▶️ 恢复执行"}),["Review","Menxia"].includes(d.state)&&l.jsxs(l.Fragment,{children:[l.jsx("button",{className:"btn-action",style:{background:"#2ecc8a22",color:"#2ecc8a",border:"1px solid #2ecc8a44"},onClick:()=>be("approve"),children:"✅ 准奏"}),l.jsx("button",{className:"btn-action",style:{background:"#ff527022",color:"#ff5270",border:"1px solid #ff527044"},onClick:()=>be("reject"),children:"🚫 封驳"})]}),["Pending","Taizi","Zhongshu","Menxia","Assigned","Doing","Review","Next"].includes(d.state)&&l.jsx("button",{className:"btn-action",style:{background:"#7c5cfc18",color:"#7c5cfc",border:"1px solid #7c5cfc44"},onClick:X,children:"⏩ 推进到下一步"})]}),l.jsxs("div",{className:"sched-section",children:[l.jsxs("div",{className:"sched-head",children:[l.jsx("span",{className:"sched-title",children:"🧭 太子调度"}),l.jsx("span",{className:"sched-status",children:J?`${J.enabled===!1?"已禁用":"运行中"} · 阈值 ${J.stallThresholdSec||180}s`:"加载中..."})]}),l.jsxs("div",{className:"sched-grid",children:[l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"停滞时长"}),l.jsx("div",{className:"v",children:dp(Ne)})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"重试次数"}),l.jsx("div",{className:"v",children:(J==null?void 0:J.retryCount)||0})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"升级级别"}),l.jsx("div",{className:"v",children:J!=null&&J.escalationLevel?J.escalationLevel===1?"门下省":"尚书省":"无"})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"派发状态"}),l.jsx("div",{className:"v",children:(J==null?void 0:J.lastDispatchStatus)||"idle"})]})]}),J&&l.jsxs("div",{className:"sched-line",children:[J.lastProgressAt&&l.jsxs("span",{children:["最近进展 ",(J.lastProgressAt||"").replace("T"," ").substring(0,19)]}),J.lastDispatchAt&&l.jsxs("span",{children:["最近派发 ",(J.lastDispatchAt||"").replace("T"," ").substring(0,19)]}),l.jsxs("span",{children:["自动回滚 ",J.autoRollback===!1?"关闭":"开启"]}),J.lastDispatchAgent&&l.jsxs("span",{children:["目标 ",J.lastDispatchAgent]})]}),l.jsxs("div",{className:"sched-actions",children:[l.jsx("button",{className:"sched-btn",onClick:()=>le("retry"),children:"🔁 重试派发"}),l.jsx("button",{className:"sched-btn warn",onClick:()=>le("escalate"),children:"📣 升级协调"}),l.jsx("button",{className:"sched-btn danger",onClick:()=>le("rollback"),children:"↩️ 回滚稳定点"}),l.jsx("button",{className:"sched-btn",onClick:()=>le("scan"),children:"🔍 立即扫描"})]})]}),oe>0&&l.jsx(pp,{todos:Z,todoDone:I,todoTotal:oe}),l.jsx("div",{className:"m-section",children:l.jsxs("div",{className:"m-rows",children:[l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"状态"}),l.jsxs("div",{className:"mr-val",children:[l.jsx("span",{className:`tag st-${d.state}`,children:Ki(d)}),(d.review_round||0)>0&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:8},children:["共磋商 ",d.review_round," 轮"]})]})]}),l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"执行部门"}),l.jsx("div",{className:"mr-val",children:l.jsx("span",{className:`tag dt-${(d.org||"").replace(/\s/g,"")}`,children:d.org||"—"})})]}),d.eta&&d.eta!=="-"&&l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"预计完成"}),l.jsx("div",{className:"mr-val",children:d.eta})]}),d.block&&d.block!=="无"&&d.block!=="-"&&l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",style:{color:"var(--danger)"},children:"阻塞项"}),l.jsx("div",{className:"mr-val",style:{color:"var(--danger)"},children:d.block})]}),d.now&&d.now!=="-"&&l.jsxs("div",{className:"m-row",style:{gridColumn:"1/-1"},children:[l.jsx("div",{className:"mr-label",children:"当前进展"}),l.jsx("div",{className:"mr-val",style:{fontWeight:400,fontSize:12},children:d.now})]}),d.ac&&l.jsxs("div",{className:"m-row",style:{gridColumn:"1/-1"},children:[l.jsx("div",{className:"mr-label",children:"验收标准"}),l.jsx("div",{className:"mr-val",style:{fontWeight:400,fontSize:12},children:d.ac})]})]})}),B.length>0&&l.jsxs("div",{className:"m-section",children:[l.jsxs("div",{className:"m-sec-label",children:["流转日志(",B.length," 条)"]}),l.jsx("div",{className:"fl-timeline",children:B.map((E,Q)=>{const W=$l(E.from||"");return l.jsxs("div",{className:"fl-item",children:[l.jsx("div",{className:"fl-time",children:E.at?E.at.substring(11,16):""}),l.jsx("div",{className:"fl-dot",style:{background:W}}),l.jsxs("div",{className:"fl-content",children:[l.jsxs("div",{className:"fl-who",children:[l.jsx("span",{className:"from",style:{color:W},children:E.from}),l.jsx("span",{style:{color:"var(--muted)"},children:" → "}),l.jsx("span",{className:"to",style:{color:$l(E.to||"")},children:E.to})]}),l.jsx("div",{className:"fl-rem",children:E.remark})]})]},Q)})})]}),d.output&&d.output!=="-"&&d.output!==""&&l.jsxs("div",{className:"m-section",children:[l.jsx("div",{className:"m-sec-label",children:"产出物"}),l.jsx("code",{children:d.output})]}),l.jsx(mp,{data:D,isDone:["Done","Cancelled"].includes(d.state),logRef:z})]})]})})}function pp({todos:o,todoDone:p,todoTotal:u}){return l.jsxs("div",{className:"todo-section",children:[l.jsxs("div",{className:"todo-header",children:[l.jsxs("div",{className:"m-sec-label",style:{marginBottom:0,border:"none",padding:0},children:["子任务清单(",p,"/",u,")"]}),l.jsxs("div",{className:"todo-progress",children:[l.jsx("div",{className:"todo-bar",children:l.jsx("div",{className:"todo-bar-fill",style:{width:`${Math.round(p/u*100)}%`}})}),l.jsxs("span",{children:[Math.round(p/u*100),"%"]})]})]}),l.jsx("div",{className:"todo-list",children:o.map(k=>{const S=k.status==="completed"?"✅":k.status==="in-progress"?"🔄":"⬜",D=k.status==="completed"?"已完成":k.status==="in-progress"?"进行中":"待开始",F=k.status==="completed"?"s-done":k.status==="in-progress"?"s-progress":"s-notstarted",_=k.status==="completed"?"done":"";return l.jsxs("div",{className:`todo-item ${_}`,children:[l.jsxs("div",{className:"t-row",children:[l.jsx("span",{className:"t-icon",children:S}),l.jsxs("span",{className:"t-id",children:["#",k.id]}),l.jsx("span",{className:"t-title",children:k.title}),l.jsx("span",{className:`t-status ${F}`,children:D})]}),k.detail&&l.jsx("div",{className:"todo-detail",children:k.detail})]},k.id)})})]})}function mp({data:o,isDone:p,logRef:u}){if(!o)return null;const k=o.activity||[],S=(()=>{if(!k.length)return!1;const T=k[k.length-1];if(!T.at)return!1;const g=typeof T.at=="number"?T.at:new Date(T.at).getTime();return Date.now()-g<3e5})(),D=[];o.agentLabel&&D.push(o.agentLabel),o.relatedAgents&&o.relatedAgents.length>1&&D.push(`${o.relatedAgents.length}个 Agent`),o.lastActive&&D.push(`最后活跃: ${o.lastActive}`);const F=o.phaseDurations||[],_=Math.max(...F.map(T=>T.durationSec||1),1),R={皇上:"#eab308",太子:"#f97316",中书省:"#3b82f6",门下省:"#8b5cf6",尚书省:"#10b981",六部:"#06b6d4",礼部:"#ec4899",户部:"#f59e0b",兵部:"#ef4444",刑部:"#6366f1",工部:"#14b8a6",吏部:"#d946ef"},C=o.todosSummary,z=o.resourceSummary,d=k.filter(T=>T.kind==="flow"),w=k.filter(T=>T.kind!=="flow"),m=new Map;return w.forEach(T=>{const g=T.agent||"unknown";m.has(g)||m.set(g,[]),m.get(g).push(T)}),l.jsxs("div",{className:"la-section",children:[l.jsxs("div",{className:"la-header",children:[l.jsxs("span",{className:"la-title",children:[l.jsx("span",{className:`la-dot${S?"":" idle"}`}),p?"执行回顾":"实时动态"]}),l.jsx("span",{className:"la-agent",children:D.join(" · ")||"加载中..."})]}),F.length>0&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)"},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:6},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"⏱ 阶段耗时"}),o.totalDuration&&l.jsxs("span",{style:{marginLeft:"auto",fontSize:10,color:"var(--muted)"},children:["总耗时 ",o.totalDuration]})]}),F.map((T,g)=>{const M=Math.max(5,Math.round((T.durationSec||1)/_*100)),P=R[T.phase]||"#6b7280";return l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,margin:"2px 0",fontSize:11},children:[l.jsx("span",{style:{minWidth:48,color:"var(--muted)",textAlign:"right"},children:T.phase}),l.jsx("div",{style:{flex:1,height:14,background:"var(--panel)",borderRadius:3,overflow:"hidden"},children:l.jsx("div",{style:{width:`${M}%`,height:"100%",background:P,borderRadius:3,opacity:T.ongoing?.6:.85}})}),l.jsxs("span",{style:{minWidth:60,fontSize:10,color:"var(--muted)"},children:[T.durationText,T.ongoing&&l.jsx("span",{style:{fontSize:9,color:"#60a5fa"},children:" ●进行中"})]})]},g)})]}),C&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)"},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:4},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"📊 执行进度"}),l.jsxs("span",{style:{fontSize:20,fontWeight:700,color:C.percent>=100?"#22c55e":C.percent>=50?"#60a5fa":"var(--text)"},children:[C.percent,"%"]}),l.jsxs("span",{style:{fontSize:10,color:"var(--muted)"},children:["✅",C.completed," 🔄",C.inProgress," ⬜",C.notStarted," / 共",C.total,"项"]})]}),l.jsxs("div",{style:{height:8,background:"var(--panel)",borderRadius:4,overflow:"hidden",display:"flex"},children:[l.jsx("div",{style:{width:`${C.total?C.completed/C.total*100:0}%`,background:"#22c55e",transition:"width .3s"}}),l.jsx("div",{style:{width:`${C.total?C.inProgress/C.total*100:0}%`,background:"#3b82f6",transition:"width .3s"}})]})]}),z&&(z.totalTokens||z.totalCost)&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)",display:"flex",gap:12,alignItems:"center"},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"📈 资源消耗"}),z.totalTokens!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["🔢 ",z.totalTokens.toLocaleString()," tokens"]}),z.totalCost!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["💰 $",z.totalCost.toFixed(4)]}),z.totalElapsedSec!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["⏳ ",z.totalElapsedSec>=60?`${Math.floor(z.totalElapsedSec/60)}分`:"",z.totalElapsedSec%60,"秒"]})]}),l.jsxs("div",{className:"la-log",ref:u,children:[d.length>0&&l.jsx("div",{className:"la-flow-wrap",children:d.map((T,g)=>l.jsxs("div",{className:"la-entry la-tool",children:[l.jsx("span",{className:"la-icon",children:"📋"}),l.jsxs("span",{className:"la-body",children:[l.jsx("b",{children:T.from})," → ",l.jsx("b",{children:T.to})," ",T.remark||""]}),l.jsx("span",{className:"la-time",children:Vi(T.at)})]},`flow-${g}`))}),m.size>0?l.jsx("div",{className:"la-groups",children:Array.from(m.entries()).map(([T,g])=>{const M=pc[T]||T||"未标识",P=g[g.length-1],B=P!=null&&P.at?Vi(P.at):"--:--:--";return l.jsxs("div",{className:"la-group",children:[l.jsxs("div",{className:"la-group-hd",children:[l.jsx("span",{className:"name",children:M}),l.jsxs("span",{children:["最近更新 ",B]})]}),l.jsx("div",{className:"la-group-bd",children:g.map((Z,I)=>l.jsx(hp,{entry:Z},I))})]},T)})}):!d.length&&l.jsx("div",{className:"la-empty",children:o.message||o.error||"Agent 尚未上报进展(等待 Agent 调用 progress 命令)"})]})]})}function hp({entry:o}){var k,S,D;const p=Vi(o.at),u=o.agent?l.jsx("span",{style:{fontSize:9,color:"var(--muted)",background:"var(--panel)",padding:"1px 4px",borderRadius:3,marginRight:4},children:pc[o.agent]||o.agent}):null;if(o.kind==="progress")return l.jsxs("div",{className:"la-entry la-assistant",children:[l.jsx("span",{className:"la-icon",children:"🔄"}),l.jsxs("span",{className:"la-body",children:[u,l.jsx("b",{children:"当前进展:"}),o.text]}),l.jsx("span",{className:"la-time",children:p})]});if(o.kind==="todos"){const F=o.items||[],_=new Map;return o.diff&&((o.diff.changed||[]).forEach(R=>_.set(R.id,{type:"changed",from:R.from,to:R.to})),(o.diff.added||[]).forEach(R=>_.set(R.id,{type:"added"}))),l.jsxs("div",{className:"la-entry",style:{flexDirection:"column",alignItems:"flex-start",gap:2},children:[l.jsxs("div",{style:{fontSize:11,color:"var(--muted)",marginBottom:2},children:[u,"📝 执行计划"]}),F.map(R=>{const C=R.status==="completed"?"✅":R.status==="in-progress"?"🔄":"⬜",z=_.get(String(R.id)),d=R.status==="completed"?{opacity:.5,textDecoration:"line-through"}:R.status==="in-progress"?{color:"#60a5fa",fontWeight:"bold"}:{};return l.jsxs("div",{style:d,children:[C," ",R.title,z&&z.type==="changed"&&z.to==="completed"&&l.jsx("span",{style:{color:"#22c55e",fontSize:9,marginLeft:4},children:"✨刚完成"}),z&&z.type==="changed"&&z.to!=="completed"&&l.jsxs("span",{style:{color:"#f59e0b",fontSize:9,marginLeft:4},children:["↻",z.from,"→",z.to]}),z&&z.type==="added"&&l.jsx("span",{style:{color:"#3b82f6",fontSize:9,marginLeft:4},children:"🆕新增"})]},R.id)}),(S=(k=o.diff)==null?void 0:k.removed)==null?void 0:S.map(R=>l.jsxs("div",{style:{opacity:.4,textDecoration:"line-through"},children:["🗑 ",R.title]},R.id))]})}if(o.kind==="assistant")return l.jsxs(l.Fragment,{children:[o.thinking&&l.jsxs("div",{className:"la-entry la-thinking",children:[l.jsx("span",{className:"la-icon",children:"💭"}),l.jsxs("span",{className:"la-body",children:[u,o.thinking]}),l.jsx("span",{className:"la-time",children:p})]}),(D=o.tools)==null?void 0:D.map((F,_)=>l.jsxs("div",{className:"la-entry la-tool",children:[l.jsx("span",{className:"la-icon",children:"🔧"}),l.jsxs("span",{className:"la-body",children:[u,l.jsx("span",{className:"la-tool-name",children:F.name}),l.jsx("span",{className:"la-trunc",children:F.input_preview||""})]}),l.jsx("span",{className:"la-time",children:p})]},_)),o.text&&l.jsxs("div",{className:"la-entry la-assistant",children:[l.jsx("span",{className:"la-icon",children:"🤖"}),l.jsxs("span",{className:"la-body",children:[u,o.text]}),l.jsx("span",{className:"la-time",children:p})]})]});if(o.kind==="tool_result"){const F=o.exitCode===0||o.exitCode===null||o.exitCode===void 0;return l.jsxs("div",{className:`la-entry la-tool-result ${F?"ok":"err"}`,children:[l.jsx("span",{className:"la-icon",children:F?"✅":"❌"}),l.jsxs("span",{className:"la-body",children:[u,l.jsx("span",{className:"la-tool-name",children:o.tool||""}),o.output?o.output.substring(0,150):""]}),l.jsx("span",{className:"la-time",children:p})]})}return o.kind==="user"?l.jsxs("div",{className:"la-entry la-user",children:[l.jsx("span",{className:"la-icon",children:"📥"}),l.jsxs("span",{className:"la-body",children:[u,o.text||""]}),l.jsx("span",{className:"la-time",children:p})]}):null}function vp(){const o=q(p=>p.toasts);return o.length?l.jsx("div",{className:"toaster",children:o.map(p=>l.jsx("div",{className:`toast ${p.type}`,children:p.msg},p.id))}):null}function gp(){const o=q(T=>T.liveStatus),[p,u]=te.useState(!1),[k,S]=te.useState(!1);te.useEffect(()=>{const T=localStorage.getItem("openclaw_court_date"),g=new Date().toISOString().substring(0,10);if(!JSON.parse(localStorage.getItem("openclaw_court_pref")||'{"enabled":true}').enabled||T===g)return;localStorage.setItem("openclaw_court_date",g),u(!0);const P=setTimeout(()=>D(),3500);return()=>clearTimeout(P)},[]);const D=()=>{S(!0),setTimeout(()=>u(!1),500)};if(!p)return null;const _=((o==null?void 0:o.tasks)||[]).filter(Jt),R=_.filter(T=>!["Done","Cancelled"].includes(T.state)).length,C=_.filter(T=>T.state==="Done").length,z=_.filter(T=>T.state!=="Done"&&T.state!=="Cancelled"&&T.eta&&new Date(T.eta.replace(" ","T"))0&&` · ⚠ 超期 ${z} 件`]}),l.jsx("div",{className:"crm-date in",children:m}),l.jsx("div",{className:"crm-skip",children:"点击任意处跳过"})]})}function yp(){const o=q(d=>d.activeTab),p=q(d=>d.setActiveTab),u=q(d=>d.liveStatus),k=q(d=>d.countdown),S=q(d=>d.loadAll);te.useEffect(()=>(Bf(),()=>Wf()),[]);const D=(u==null?void 0:u.tasks)||[],F=D.filter(Jt),_=F.filter(d=>!Bl(d)),R=u==null?void 0:u.syncStatus,C=R==null?void 0:R.ok,z=d=>d==="edicts"?String(_.length):d==="sessions"?String(D.filter(w=>!Jt(w)).length):d==="memorials"?String(F.filter(w=>["Done","Cancelled"].includes(w.state)).length):d==="monitor"?D.filter(m=>Jt(m)&&m.state==="Doing").length+"活跃":"";return l.jsxs("div",{className:"wrap",children:[l.jsxs("div",{className:"hdr",children:[l.jsxs("div",{children:[l.jsx("div",{className:"logo",children:"三省六部 · 总控台"}),l.jsx("div",{className:"sub-text",children:"OpenClaw Sansheng-Liubu Dashboard"})]}),l.jsxs("div",{className:"hdr-r",children:[l.jsx("span",{className:`chip ${C?"ok":C===!1?"err":""}`,children:C?"✅ 同步正常":C===!1?"❌ 服务器未启动":"⏳ 连接中…"}),l.jsxs("span",{className:"chip",children:[_.length," 道旨意"]}),l.jsx("button",{className:"btn-refresh",onClick:()=>S(),children:"⟳ 刷新"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["⟳ ",k,"s"]})]})]}),l.jsx("div",{className:"tabs",children:Af.map(d=>l.jsxs("div",{className:`tab ${o===d.key?"active":""}`,onClick:()=>p(d.key),children:[d.icon," ",d.label,z(d.key)&&l.jsx("span",{className:"tbadge",children:z(d.key)})]},d.key))}),o==="edicts"&&l.jsx(Qf,{}),o==="monitor"&&l.jsx(Kf,{}),o==="officials"&&l.jsx(Yf,{}),o==="models"&&l.jsx(qf,{}),o==="skills"&&l.jsx(ep,{}),o==="sessions"&&l.jsx(rp,{}),o==="memorials"&&l.jsx(sp,{}),o==="templates"&&l.jsx(op,{}),o==="morning"&&l.jsx(ap,{}),l.jsx(fp,{}),l.jsx(vp,{}),l.jsx(gp,{})]})}kf.createRoot(document.getElementById("root")).render(l.jsx(oc.StrictMode,{children:l.jsx(yp,{})})); diff --git a/dashboard/dist/index.html b/dashboard/dist/index.html index 457c14dd..2efbe1b1 100644 --- a/dashboard/dist/index.html +++ b/dashboard/dist/index.html @@ -5,7 +5,7 @@ 三省六部 · Edict Dashboard - + From c2ed2f812e9a399e2cbac8f1d69fe1a67c14384c Mon Sep 17 00:00:00 2001 From: Ming Li Date: Fri, 13 Mar 2026 19:16:57 -0400 Subject: [PATCH 05/27] =?UTF-8?q?feat(template):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=BE=A1=E5=89=8D=E8=AE=AE=E6=94=BF=E4=B8=8E=E8=87=AA=E7=94=B1?= =?UTF-8?q?=E4=B8=8B=E6=97=A8=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/components/TemplatePanel.tsx | 459 +++++++++++++++++- 1 file changed, 447 insertions(+), 12 deletions(-) diff --git a/edict/frontend/src/components/TemplatePanel.tsx b/edict/frontend/src/components/TemplatePanel.tsx index 92e32228..ed35bb22 100644 --- a/edict/frontend/src/components/TemplatePanel.tsx +++ b/edict/frontend/src/components/TemplatePanel.tsx @@ -1,7 +1,17 @@ import { useState } from 'react'; import { useStore, TEMPLATES, TPL_CATS } from '../store'; import type { Template } from '../store'; -import { api } from '../api'; +import { api, type CourtDiscussResult } from '../api'; + +const FREE_TARGET_DEPTS = ['中书省', '尚书省', '礼部', '户部', '兵部', '刑部', '工部', '吏部']; +const DISCUSS_AGENT_OPTIONS = [ + { id: 'taizi', label: '太子', emoji: '🤴' }, + { id: 'zhongshu', label: '中书省', emoji: '📜' }, + { id: 'menxia', label: '门下省', emoji: '🔍' }, + { id: 'shangshu', label: '尚书省', emoji: '📮' }, + { id: 'hubu', label: '户部', emoji: '💰' }, + { id: 'bingbu', label: '兵部', emoji: '⚔️' }, +]; export default function TemplatePanel() { const tplCatFilter = useStore((s) => s.tplCatFilter); @@ -12,6 +22,14 @@ export default function TemplatePanel() { const [formTpl, setFormTpl] = useState