diff --git a/cloud/console-react/src/App.tsx b/cloud/console-react/src/App.tsx
index 51ca873..3a81d2c 100644
--- a/cloud/console-react/src/App.tsx
+++ b/cloud/console-react/src/App.tsx
@@ -19,17 +19,13 @@ import SkillsToolsPanel from "./sections/SkillsToolsPanel";
import AutomationsSection from "./sections/AutomationsSection";
import TriageSection from "./sections/TriageSection";
-// Layout only: ONE scrollable console page, Muse web/desktop tokens
-// (white, hairlines, grey/blue bubbles) but console ordering — no
-// icon-rail column, no left list column, no right panel column, no
-// view switching. Everything is on the page: a slim sticky top bar
-// (name + real connection status + health + quota + refresh stamp +
-// anchor nav), a data-cards row, then every module as a section in
-// order. The old right panel's content moved here: its profile/status
-// line is the top bar, its 今天 list is redundant with the full task
-// list section, and its detail/context panels sit under their own
-// sections below (detail under 对话, members under 项目, and so on).
-// The old icon rail's module switching is the anchor nav.
+// Layout only: ONE scrollable console page in GitHub Primer style —
+// a dark header bar (name + real connection status + health + quota
+// + refresh stamp + anchor nav as header links), a data-cards row,
+// then every module as a bordered section in order. No icon-rail
+// column, no left list column, no right panel column, no view
+// switching. Detail/context panels sit under their own sections
+// below (detail under 对话, members under 项目, and so on).
const NAV: Array<[string, string]> = [
["#sec-dispatch", "派发"], ["#sec-tasks", "任务"], ["#sec-conv", "对话"],
["#sec-projects", "项目"], ["#sec-env", "环境"], ["#sec-workers", "Workers"],
diff --git a/cloud/console-react/src/components/chat/Chat.tsx b/cloud/console-react/src/components/chat/Chat.tsx
index 832cbd9..1560e22 100644
--- a/cloud/console-react/src/components/chat/Chat.tsx
+++ b/cloud/console-react/src/components/chat/Chat.tsx
@@ -18,16 +18,19 @@ export function useAutoFollow(dep: unknown) {
return { ref, following, onScroll };
}
+// GitHub-style comment box, not a chat bubble: full-width white box
+// with a subtle header row (role label only — no timestamp or sender
+// is invented; the backend provides neither) and the body below.
+// Status entries are plain muted timeline text on the timeline line.
export function ChatBubble({ msg }: { msg: ChatMessage }) {
if (msg.role === "status") {
return
{msg.text}
;
}
+ const author = msg.label || (msg.role === "user" ? "用户" : msg.role === "assistant" ? "助手" : "工具");
return (
-
-
- {msg.label ?
{msg.label}
: null}
-
{msg.text}
-
+
);
}
@@ -54,25 +57,24 @@ export function ChatInput({ onSend, disabled, placeholder }: {
onSend(v);
setText("");
};
- // Pill input bar, Muse screenshot style: the textarea is the pill
- // itself, and send is a light-blue circular arrow. No mic or
- // attachment button here: the continue chat has no voice or
- // attachment API (attachments live in Dispatch), so a mic/+ icon
- // would be a dead button; the left list header's + is the new-task
- // entry instead.
+ // GitHub comment box: textarea on top, a subtle footer row with the
+ // green primary send button. No mic or attachment button here: the
+ // continue chat has no voice or attachment API (attachments live in
+ // Dispatch), so a mic/+ icon would be a dead button.
return (
-
+
);
}
diff --git a/cloud/console-react/src/sections/TaskListSection.tsx b/cloud/console-react/src/sections/TaskListSection.tsx
index a8f9530..d70190e 100644
--- a/cloud/console-react/src/sections/TaskListSection.tsx
+++ b/cloud/console-react/src/sections/TaskListSection.tsx
@@ -1,14 +1,13 @@
import { useConsole } from "../context";
import { shortId } from "../api/client";
-// Left list panel, Muse screenshot style: rounded search on top, a
-// 主要聊天 section header with a + button, then the task list. The
-// search input IS the existing task search (id/prompt); the status and
-// executor filters are kept as a compact row below it — the screenshot
-// has no filters, but dropping them would cut functionality, so they
-// stay, just compact. List rows show the prompt truncated on one line
-// with a status dot, short id, status and executor below it; the
-// selected row is highlighted grey, as in the screenshot.
+// GitHub issues-style task list: search on top, the project selector
+// (the auto-project fix — do not remove it), a 主要聊天 header with a
+// + button, then hairline rows with a Primer status dot at the left,
+// a 14px title and a 12px muted meta line. The search input IS the
+// existing task search (id/prompt); status and executor filters stay
+// as a compact row below it. The selected row gets the light
+// blue-grey background (#ddf4ff), not a bubble.
export default function TaskListSection({ onNew }: { onNew: () => void }) {
const ctx = useConsole();
return (
@@ -46,18 +45,18 @@ export default function TaskListSection({ onNew }: { onNew: () => void }) {
: !ctx.projectId ?
先选项目
: ctx.filteredTasks.length === 0 ?
{ctx.tasks.length ? "没有符合筛选条件的任务" : "该项目暂无任务"}
: (
-
+
{ctx.filteredTasks.slice().reverse().map((t) => (
- ctx.showDetail(t.id)}>
-
- {t.plan ? "[计划] " : ""}{t.forked_from ? "[分支] " : ""}{t.coordinator_parts ? "[协调] " : ""}{t.parent_task_id ? "[协调子任务] " : ""}{String(t.prompt || shortId(t.id)).slice(0, 60)}
-
-
-
- {shortId(t.id)}
- {t.status}
- {t.executor || ""}
-
+ ctx.showDetail(t.id)}>
+
+
+
+ {t.plan ? "[计划] " : ""}{t.forked_from ? "[分支] " : ""}{t.coordinator_parts ? "[协调] " : ""}{t.parent_task_id ? "[协调子任务] " : ""}{String(t.prompt || shortId(t.id)).slice(0, 60)}
+
+
+ {shortId(t.id)} · {t.status} · {t.executor || ""}{t.updated_at ? " · " + new Date(t.updated_at * 1000).toLocaleString() : ""}
+
+
))}
diff --git a/cloud/console-react/src/sections/detail/TracePanel.tsx b/cloud/console-react/src/sections/detail/TracePanel.tsx
index 9e6e91b..e9537f9 100644
--- a/cloud/console-react/src/sections/detail/TracePanel.tsx
+++ b/cloud/console-react/src/sections/detail/TracePanel.tsx
@@ -31,7 +31,7 @@ export default function TracePanel() {
- 对话时间线:首条是本任务 prompt(用户);助手回复来自 message 条目,命令/文件/工具/调用是工具气泡,计划/思考/子代理开始结束是居中状态提示。流式进行中时新条目实时追加;你在底部时自动跟随,往上翻时暂停跟随,点“回到最新”恢复。
+ 对话时间线:首条是本任务 prompt(用户);助手回复来自 message 条目,命令/文件/工具/调用是工具评论框,计划/思考/子代理开始结束是时间线上的状态文字。流式进行中时新条目实时追加;你在底部时自动跟随,往上翻时暂停跟随,点“回到最新”恢复。
>
);
}
diff --git a/cloud/console-react/src/tokens.css b/cloud/console-react/src/tokens.css
index af93b83..b31e242 100644
--- a/cloud/console-react/src/tokens.css
+++ b/cloud/console-react/src/tokens.css
@@ -1,135 +1,151 @@
-/* Single token source — Muse web/desktop palette (2026-10-29 redesign).
- This supersedes the previous v3 porcelain/ink/seal palette: white page,
- light-grey hairlines, grey assistant bubbles, light-blue user bubbles,
- blue accent, green only for 已连接. The old pine/seal colours are gone;
- the button class name "seal" is retained only so Dispatch/DetailActions
- need no change — it now renders in screenshot blue. No coloured page
- backgrounds, no big header bar, no shadows. Sans-serif only. */
+/* Single token source — GitHub Primer (light) visual language.
+ This supersedes the Muse web/desktop palette: Primer canvas,
+ borders, foregrounds and accents only. No grey/blue chat bubbles,
+ no pill shapes, no coloured page backgrounds, no shadows beyond
+ GitHub's hairline button shadow. Layout is unchanged (one
+ scrollable console page: dark header + data cards + stacked
+ sections); only the visual system moved to Primer. */
:root{
- --text:#1d2129; --muted:#667085; --line:#e4e7ec; --line-strong:#d0d5dd;
- --paper:#ffffff; --card:#ffffff;
- --bubble-assistant:#f2f2f2; --bubble-user:#bfe0ff;
- --accent:#3b82f6; --accent-dark:#2563eb; --accent-soft:#eef4ff;
- --green:#22c55e; --success:#16a34a; --danger:#b42318; --danger-soft:#fef3f2;
- --amber:#b54708; --purple:#6941c6;
- --radius:12px; --radius-sm:10px; --shadow:none;
- --sans:-apple-system,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
- --mono:"IBM Plex Mono",Menlo,Consolas,monospace;
- --ctl:40px;
+ --text:#1f2328; --muted:#656d76; --subtle:#6e7781;
+ --line:#d0d7de; --line-muted:#d8dee4;
+ --paper:#ffffff; --card:#ffffff; --subtle-bg:#f6f8fa; --inset-bg:#f6f8fa;
+ --accent:#0969da; --accent-emphasis:#0969da;
+ --green:#2da44e; --green-hover:#2c974b; --success:#1a7f37;
+ --danger:#d1242f; --danger-bg:#ffebe9;
+ --attention:#9a6700; --done:#8250df; --selected-bg:#ddf4ff;
+ --radius:6px; --shadow:0 1px 0 rgba(27,31,36,.04);
+ --sans:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif;
+ --mono:ui-monospace,SFMono-Regular,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
+ --ctl:32px;
}
*{box-sizing:border-box}
html,body,#root{height:100%}
-body{font-family:var(--sans);margin:0;color:var(--text);background:#fff;line-height:1.6;font-size:14px}
+body{font-family:var(--sans);margin:0;color:var(--text);background:#fff;line-height:1.5;font-size:14px}
a{color:var(--accent);text-underline-offset:3px}
-:focus-visible{outline:3px solid var(--accent);outline-offset:2px}
+:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
@media (prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}
-.muted{color:var(--muted);font-size:13px;line-height:1.6}
+.muted{color:var(--muted);font-size:12px;line-height:1.5}
.err{color:var(--danger)} .ok{color:var(--success)}
-button{cursor:pointer;font-weight:600;border-radius:999px}
+/* GitHub buttons: secondary by default, 6px radius, 5px 16px padding.
+ "primary" and the legacy "seal" name are the green primary action
+ (dispatch / submit), exactly like GitHub's primary button. */
+button{cursor:pointer;font-weight:500;border-radius:var(--radius);border:1px solid var(--line);background:#fff;color:var(--text);box-shadow:var(--shadow)}
+button:hover{background:var(--subtle-bg)}
button:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}
-button.primary{background:var(--accent);border-color:var(--accent);color:#fff}
-button.primary:hover{background:var(--accent-dark)}
-/* "seal" is a legacy class name only; its colour is screenshot blue now. */
-button.seal{background:var(--accent);border-color:var(--accent);color:#fff}
-button.seal:hover{background:var(--accent-dark)}
-button.danger{color:var(--danger);border-color:#fecdca;background:var(--danger-soft)}
-button.danger:hover{background:#fee4e2}
-.icon-btn{width:32px;height:32px;padding:0;display:grid;place-items:center;border:1px solid var(--line);border-radius:50%;background:#fff;color:var(--text);font-size:16px;line-height:1}
-.icon-btn:hover{background:#f9fafb}
+button.primary,button.seal{background:var(--green);border-color:rgba(27,31,36,.15);color:#fff}
+button.primary:hover,button.seal:hover{background:var(--green-hover)}
+button.danger{color:var(--danger);border-color:var(--line);background:#fff}
+button.danger:hover{background:var(--danger-bg);color:var(--danger)}
+.icon-btn{width:32px;height:32px;padding:0;display:grid;place-items:center;border:1px solid var(--line);border-radius:var(--radius);background:#fff;color:var(--text);font-size:16px;line-height:1}
+.icon-btn:hover{background:var(--subtle-bg)}
-/* Dashboard shell: ONE scrollable console page, same Muse tokens.
- Slim sticky top bar (name + status + health + quota + refresh
- stamp + anchor nav), a data-cards row, then every module as a
- section. No icon-rail column, no left list column, no right
- panel column — the old shell classes are gone with them. */
-.dash-topbar{position:sticky;top:0;z-index:10;background:#fff;border-bottom:1px solid var(--line);padding:10px 20px 8px}
+/* Dashboard shell: ONE scrollable console page.
+ Dark GitHub-style header (#24292f, white text) with the anchor nav
+ inside it, a data-cards row, then every module as a section. */
+.dash-topbar{position:sticky;top:0;z-index:10;background:#24292f;color:#fff;border-bottom:1px solid rgba(255,255,255,.15);padding:10px 20px 8px}
+.dash-topbar .muted{color:rgba(255,255,255,.75)}
.dash-topbar-row{display:flex;gap:14px;align-items:center;flex-wrap:wrap}
-.dash-brand{font-weight:700;font-size:17px}
-.dash-status{font-size:13px;font-weight:600;color:var(--muted)}
-.dash-status.ok{color:var(--green)}
+.dash-brand{font-weight:600;font-size:16px;color:#fff}
+.dash-status{font-size:13px;font-weight:600;color:rgba(255,255,255,.75)}
+.dash-status.ok{color:#2da44e}
.dash-stamp{margin-left:auto}
.dash-nav{display:flex;gap:4px;flex-wrap:wrap;margin-top:8px}
-.dash-nav a{color:var(--text);text-decoration:none;font-size:13px;font-weight:600;padding:6px 12px;border-radius:999px}
-.dash-nav a:hover{background:#f2f2f2}
+.dash-nav a{color:#fff;text-decoration:none;font-size:13px;font-weight:600;padding:6px 12px;border-radius:var(--radius)}
+.dash-nav a:hover{background:rgba(255,255,255,.12);text-decoration:none}
.dash-page{max-width:1180px;margin:0 auto;padding:20px;display:flex;flex-direction:column;gap:20px}
-.dash-section{scroll-margin-top:130px;display:flex;flex-direction:column;gap:20px}
-.dash-section.card{padding:20px}
+.dash-section{scroll-margin-top:130px;display:flex;flex-direction:column;gap:16px}
+.dash-section.card{padding:16px}
.dash-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
-.dash-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:16px 18px;min-width:0}
-.dash-card-label{color:var(--muted);font-size:13px;font-weight:600}
-.dash-card-value{font-size:28px;font-weight:700;line-height:1.3;overflow-wrap:anywhere}
+.dash-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:16px;min-width:0}
+.dash-card-label{color:var(--muted);font-size:12px;font-weight:600}
+.dash-card-value{font-size:24px;font-weight:600;line-height:1.3;overflow-wrap:anywhere}
-
-/* Cards: flat white sections with a hairline border, no shadow. */
-.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:none;padding:20px}
-.card h2{font-size:17px;margin:0 0 12px;font-weight:600}
-.card h3{font-size:15px;margin:24px 0 12px;padding-top:20px;border-top:1px solid var(--line);font-weight:600}
+/* Boxes: bordered, 6px radius, white body, subtle header row. */
+.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:none;padding:16px}
+.card h2{font-size:14px;margin:0 0 12px;font-weight:600}
+.card h3{font-size:14px;margin:24px 0 12px;padding-top:16px;border-top:1px solid var(--line);font-weight:600}
.card h3:first-of-type{border-top:0;padding-top:0}
-.composer h2{font-size:20px}
-.composer textarea.prompt{min-height:148px;font-size:16px;padding:16px 18px;border-radius:12px}
+.composer h2{font-size:16px}
+.composer textarea.prompt{min-height:148px;font-size:14px;padding:12px;border-radius:var(--radius)}
.composer textarea.coord{font-family:var(--mono);font-size:13px}
-/* Unified controls: same-row controls are all 40px */
+/* Unified controls: same-row controls are all 32px (GitHub height) */
.bar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:12px 0}
.bar > button,.bar > label{display:inline-flex;align-items:center;height:var(--ctl);min-height:var(--ctl);padding:0 16px;box-sizing:border-box;white-space:nowrap}
.bar > input,.bar > select{height:var(--ctl);min-height:var(--ctl)}
-input,select,textarea,button{font:inherit;font-size:14px;padding:10px 13px;border-radius:var(--radius-sm);border:1px solid var(--line-strong);background:#fff;color:var(--text)}
-input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(59,130,246,.15)}
-button:hover{box-shadow:none}
-select{appearance:none;-webkit-appearance:none;cursor:pointer;padding-right:34px;background-image:url("data:image/svg+xml;utf8, ");background-repeat:no-repeat;background-position:right 12px center}
+input,select,textarea,button{font:inherit;font-size:14px;padding:5px 12px;border-radius:var(--radius);border:1px solid var(--line);background:#fff;color:var(--text)}
+input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(9,105,218,.3)}
+select{appearance:none;-webkit-appearance:none;cursor:pointer;padding-right:34px;background-image:url("data:image/svg+xml;utf8, ");background-repeat:no-repeat;background-position:right 12px center}
select::-ms-expand{display:none}
-select:disabled,input:disabled{color:var(--muted);background:#f9fafb;cursor:not-allowed}
+select:disabled,input:disabled{color:var(--muted);background:var(--subtle-bg);cursor:not-allowed}
input[type="checkbox"]{width:16px;height:16px;accent-color:var(--accent)}
-input[type="file"]{height:var(--ctl);display:inline-flex;align-items:center;padding:0 12px;border:1px dashed var(--line-strong);border-radius:999px;background:#fff}
-input[type="file"]::file-selector-button{height:28px;padding:0 12px;margin-right:10px;border:0;border-radius:999px;background:#f2f2f2;color:var(--text);font:inherit;font-size:13px;cursor:pointer}
-textarea{width:100%;min-height:84px;resize:vertical;line-height:1.65;padding:12px 14px;border-radius:12px}
-.composer .bar label{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--line-strong);border-radius:999px;padding:7px 13px;background:#fff;cursor:pointer;font-size:13px;font-weight:600}
+input[type="file"]{height:var(--ctl);display:inline-flex;align-items:center;padding:0 12px;border:1px dashed var(--line);border-radius:var(--radius);background:#fff}
+input[type="file"]::file-selector-button{height:24px;padding:0 12px;margin-right:10px;border:1px solid var(--line);border-radius:var(--radius);background:var(--subtle-bg);color:var(--text);font:inherit;font-size:13px;cursor:pointer}
+textarea{width:100%;min-height:84px;resize:vertical;line-height:1.5;padding:12px;border-radius:var(--radius)}
+.composer .bar label{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--line);border-radius:var(--radius);padding:5px 12px;background:#fff;cursor:pointer;font-size:13px;font-weight:500}
.composer .bar label:has(input:checked){background:var(--text);color:#fff;border-color:var(--text)}
.stacked{margin-bottom:12px}
.stacked + .bar{margin-top:0}
-/* Data lists: one shared list style, no per-section inline styles */
+/* Data lists: bordered rows, no coloured backgrounds */
.data-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}
-.data-list li{position:relative;margin:0;padding:10px 12px;background:#f9fafb;border:1px solid var(--line);border-radius:12px;line-height:1.6;display:flex;flex-wrap:wrap;align-items:center;gap:8px;overflow-wrap:anywhere}
+.data-list li{position:relative;margin:0;padding:8px 12px;background:#fff;border:1px solid var(--line);border-radius:var(--radius);line-height:1.5;display:flex;flex-wrap:wrap;align-items:center;gap:8px;overflow-wrap:anywhere}
.data-list li .row-main{flex:1 1 200px;min-width:0}
.data-list li button{flex:none}
.data-list li.is-online,.data-list li.is-offline{padding-left:26px}
-.data-list li.is-online::before,.data-list li.is-offline::before{content:"";position:absolute;left:12px;top:17px;width:8px;height:8px;border-radius:50%}
-.data-list li.is-online::before{background:var(--green)}
+.data-list li.is-online::before,.data-list li.is-offline::before{content:"";position:absolute;left:12px;top:15px;width:8px;height:8px;border-radius:50%}
+.data-list li.is-online::before{background:var(--success)}
.data-list li.is-offline::before{background:var(--danger)}
.data-group{margin-bottom:12px}.data-group:last-child{margin-bottom:0}.data-group > b{display:block;margin-bottom:8px}
-/* Status colours (text only; dots use currentColor) */
-.st-queued{color:var(--muted)}.st-running{color:var(--amber);font-weight:700}.st-completed{color:var(--success)}.st-failed,.st-merge-failed{color:var(--danger)}.st-merged{color:var(--purple)}.st-interrupted{color:var(--amber)}
-/* Task table (kept for any table view; the left list is the primary list) */
+/* Status colours (Primer text; issue dots use the same classes) */
+.st-queued{color:var(--muted)}.st-running{color:var(--success);font-weight:600}.st-completed{color:var(--done)}.st-failed,.st-merge-failed{color:var(--danger)}.st-merged{color:var(--done)}.st-interrupted{color:var(--attention)}
+/* Task table */
.table-wrap{border:0;border-top:1px solid var(--line);overflow:auto;background:var(--card)}
table{border-collapse:collapse;width:100%;font-size:13px}
-th,td{padding:12px 16px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
-th{background:#f9fafb;color:var(--muted);font-size:12px;font-weight:600;white-space:nowrap;letter-spacing:.3px}
+th,td{padding:8px 16px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
+th{background:var(--subtle-bg);color:var(--muted);font-size:12px;font-weight:600;white-space:nowrap}
tbody tr{cursor:pointer}
-tbody tr:hover{background:#f9fafb}
-tr.selected{background:#f2f2f2}
-td.prompt-col{font-size:15px;line-height:1.55}
-/* Result / error blocks: light, not dark — the screenshot has no dark log block */
-pre{background:#f9fafb;color:var(--text);border:1px solid var(--line);border-radius:12px;padding:16px 18px;overflow:auto;max-height:420px;white-space:pre-wrap;word-break:break-word;font-size:13px;font-family:var(--mono);line-height:1.7}
-.trace-box{background:#f9fafb;color:var(--text);border:1px solid var(--line);border-radius:12px;padding:16px 18px;max-height:520px;overflow:auto;font-size:13px;line-height:1.9;font-family:var(--mono)}
+tbody tr:hover{background:var(--subtle-bg)}
+tr.selected{background:var(--selected-bg)}
+td.prompt-col{font-size:14px;line-height:1.5}
+/* Result / error blocks and trace: subtle inset, monospace */
+pre{background:var(--inset-bg);color:var(--text);border:1px solid var(--line);border-radius:var(--radius);padding:16px;overflow:auto;max-height:420px;white-space:pre-wrap;word-break:break-word;font-size:13px;font-family:var(--mono);line-height:1.5}
+.trace-box{background:var(--inset-bg);color:var(--text);border:1px solid var(--line);border-radius:var(--radius);padding:16px;max-height:520px;overflow:auto;font-size:13px;line-height:1.5;font-family:var(--mono)}
.trace-box .muted{color:var(--muted)}
-/* Chat: Muse screenshot bubbles. Assistant grey left, user light-blue
- right, dark text on both, rounded ~16px, max-width ~75%. One shared
- style for the trace timeline and the continue chat. */
-.conversation{display:flex;flex-direction:column;gap:20px}
+/* GitHub issues-style task list: hairline rows, status dot at left,
+ 14px title, 12px muted meta, selected row light blue-grey. */
+.search-box{display:flex;align-items:center;gap:8px;border:1px solid var(--line);border-radius:var(--radius);background:#fff;padding:0 12px;color:var(--muted)}
+.search-box input{flex:1;border:0;padding:5px 0}
+.search-box input:focus{box-shadow:none}
+.list-section-head{display:flex;align-items:center;justify-content:space-between;font-size:14px;font-weight:600;margin:12px 0 4px}
+.issue-list{list-style:none;margin:0;padding:0;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
+.issue-item{padding:8px 16px;border-bottom:1px solid var(--line);cursor:pointer;display:flex;gap:10px;align-items:flex-start;background:#fff}
+.issue-item:last-child{border-bottom:0}
+.issue-item:hover{background:var(--subtle-bg)}
+.issue-item.selected{background:var(--selected-bg)}
+.issue-state{flex:none;width:16px;height:16px;margin-top:2px;border-radius:50%;background:var(--muted)}
+.issue-state.st-running,.issue-state.st-queued{background:var(--success)}
+.issue-state.st-completed,.issue-state.st-merged{background:var(--done)}
+.issue-state.st-failed,.issue-state.st-merge-failed{background:var(--danger)}
+.issue-state.st-interrupted{background:var(--attention)}
+.issue-title{font-size:14px;line-height:1.5;overflow-wrap:anywhere}
+.issue-meta{font-size:12px;color:var(--muted);line-height:1.5}
+/* Conversation as a GitHub timeline / comments: white comment boxes
+ with a subtle header row, connected by a muted vertical line.
+ No bubbles, no left/right alignment by role — every entry is a
+ full-width comment. Status entries are plain muted timeline text. */
+.conversation{display:flex;flex-direction:column;gap:16px}
.chat-column{max-width:100%;margin:0 auto;width:100%}
-.chat-list{position:relative;display:flex;flex-direction:column;gap:12px;max-height:none;overflow:visible;padding:8px 0}
-.chat-row{display:flex}.chat-row.user{justify-content:flex-end}.chat-row.assistant,.chat-row.tool{justify-content:flex-start}
-.chat-bubble{max-width:75%;padding:10px 14px;border-radius:16px;line-height:1.65;white-space:pre-wrap;word-break:break-word;color:var(--text)}
-.chat-bubble.user{background:var(--bubble-user);border-bottom-right-radius:4px}
-.chat-bubble.assistant{background:var(--bubble-assistant);border-bottom-left-radius:4px}
-.chat-bubble.tool{background:var(--bubble-assistant);font-family:var(--mono);font-size:13px;border-bottom-left-radius:4px}
-.chat-label{font-size:12px;color:var(--muted);margin-bottom:4px}
-.chat-status{text-align:center;font-size:12px;color:var(--muted)}
-.chat-follow{position:sticky;bottom:8px;align-self:center;background:var(--accent);border-color:var(--accent);color:#fff;z-index:2}
-.chat-input-bar{position:sticky;bottom:0;display:flex;gap:8px;align-items:center;background:#fff;border:1px solid var(--line);border-radius:999px;padding:6px 6px 6px 16px;box-shadow:0 1px 4px rgba(0,0,0,.06);z-index:2}
-.chat-input{flex:1;height:var(--ctl);min-height:var(--ctl);resize:none;border:0;background:transparent;padding:8px 0}
-.chat-input:focus{box-shadow:none;outline:none}
-.chat-send{width:36px;height:36px;border-radius:50%;background:var(--bubble-user);border:0;color:var(--accent-dark);padding:0;display:grid;place-items:center;font-size:18px;line-height:1;flex:none}
-.chat-send:disabled{opacity:.45;background:#f2f2f2;color:var(--muted)}
+.chat-list{position:relative;display:flex;flex-direction:column;gap:12px;max-height:none;overflow:visible;padding:4px 0 4px 20px;border-left:2px solid var(--line);margin-left:8px}
+.gh-comment{border:1px solid var(--line);border-radius:var(--radius);background:#fff;overflow:hidden}
+.gh-comment-header{background:var(--subtle-bg);border-bottom:1px solid var(--line);padding:8px 16px;font-size:13px}
+.gh-comment-author{font-weight:600;color:var(--text)}
+.gh-comment-body{padding:16px;white-space:pre-wrap;word-break:break-word;line-height:1.5}
+.gh-comment.tool .gh-comment-body{font-family:var(--mono);font-size:13px}
+.chat-status{font-size:12px;color:var(--muted);line-height:1.5}
+.chat-follow{position:sticky;bottom:8px;align-self:center;background:#fff;border:1px solid var(--line);color:var(--text);z-index:2}
+.gh-comment-box{border:1px solid var(--line);border-radius:var(--radius);background:#fff;overflow:hidden}
+.gh-comment-box textarea{border:0;border-radius:0;min-height:84px}
+.gh-comment-box textarea:focus{box-shadow:none}
+.gh-comment-footer{display:flex;justify-content:flex-end;gap:8px;padding:8px;border-top:1px solid var(--line);background:var(--subtle-bg)}
.conv-divider{border-top:1px solid var(--line);margin:4px 0}
@media (max-width:900px){
.dash-cards{grid-template-columns:repeat(2,1fr)}
@@ -145,5 +161,5 @@ pre{background:#f9fafb;color:var(--text);border:1px solid var(--line);border-rad
.card{padding:16px}
th:nth-child(6),td:nth-child(6),th:nth-child(9),td:nth-child(9){display:none}
.bar > input[type="text"],.bar > input[type="password"],.bar > select{flex:1 1 100%}
- th,td{padding:10px 12px}
+ th,td{padding:8px 12px}
}
diff --git a/cloud/console.html b/cloud/console.html
index 03bab9f..a7130d5 100644
--- a/cloud/console.html
+++ b/cloud/console.html
@@ -4,7 +4,7 @@
Codex 命令台
-
-
+`),O(Z||"(暂无 result/error 输出)")}catch{}dl(H.id),Bl(H.id),Ye(H.id),Vt(H.id),Hl(H),ol(H.id),ll(H.id),ml(H.id),ql(H.id)}},[dl,Bl,Ye,Vt,Hl,ol,ll,ml,ql]),yn=y.useCallback(async H=>{const R=le.current;if(!R)return;const Z={cancel:"确定取消该任务吗?",merge:"确定把 completed 任务标为 merged 吗?",pr:"确定为该任务提 PR 吗?(会在仓库真实建 PR)"};if(!(Z[H]&&!window.confirm(Z[H])))try{const F=await lt(P.taskAction(R.id,H),"POST",{});E(H+" 已提交:"+JSON.stringify(F).slice(0,300)),r(),await Wl(R.id)}catch(F){E(H+" 失败:"+F.message)}},[Wl,r]),Ke=y.useCallback(async()=>{const H=le.current;if(H)try{const R=await lt(P.taskCost(H.id));b(JSON.stringify(R,null,2).slice(0,4e3))}catch(R){b("查成本失败:"+R.message)}},[]),La=y.useCallback(async H=>{const R=le.current;if(R){if(!H.trim()){et("分支消息必填");return}if(!cu.includes(R.status)){et("仅终态任务可分支");return}try{const Z=await lt(P.taskFork(R.id),"POST",{message:H.trim()});et("分支任务已建:"+Z.id.slice(0,8)),r()}catch(Z){et("分支失败:"+Z.message)}}},[r]),va=y.useCallback(async H=>{const R=le.current;if(R)try{await lt(P.taskReviews(R.id),"POST",H),Ft("评审已登记(requested)"),await Ye(R.id)}catch(Z){Ft("登记评审需求失败:"+Z.message)}},[Ye]),fu=y.useCallback(H=>{const R=le.current;if(!R)return;const Z=window.prompt("输入该条评审的 findings(将写回记录并标为 completed):");Z!==null&<(P.taskReviewFindings(R.id,H),"POST",{findings:Z}).then(()=>void Ye(R.id)).catch(F=>Ft("写 findings 失败:"+F.message))},[Ye]),ru=y.useCallback((H,R)=>{const Z=le.current;if(!Z)return;const F=R==="approved"?"approve":"deny";lt(P.taskApprovalDecision(Z.id,H,F),"POST",{}).then(()=>{J("已记录"+(R==="approved"?"允许":"拒绝")+"(当前版本只留回执,不拦截实时行为)"),Vt(Z.id)}).catch(Nt=>J("决定失败:"+Nt.message))},[Vt]),$l=y.useCallback(async(H,R,Z)=>{const F=le.current;if(F){if(!R.trim()){Q("目标任务 id 不能为空");return}if(!Z.trim()){Q("消息正文不能为空");return}try{const Nt=await lt(P.taskMessagesTo(R.trim()),"POST",{from_task_id:F.id,body:Z.trim(),mode:H});Q("已发送("+Nt.delivery+")"),await ll(F.id)}catch(Nt){Q("发送失败:"+Nt.message)}}},[ll]),pe=y.useCallback((H,R)=>{const Z=le.current;if(!Z)return;if(!["completed","failed","interrupted"].includes(Z.status)){Gt("仅 completed/failed/interrupted 任务可操作线程(merged/merge-failed 不可改,服务端同样 409)");return}const F={};if(H==="rename"){if(!R.trim()){Gt("新线程名不能为空");return}F.name=R.trim()}lt(P.taskThreadAction(Z.id,H),"POST",F).then(Nt=>{Gt(H+" 已提交:"+JSON.stringify(Nt).slice(0,300)),ml(Z.id),ql(Z.id)}).catch(Nt=>Gt(H+" 失败:"+Nt.message))},[ml,ql]),hl=y.useCallback(async H=>{const R=le.current;if(!R)return;const Z=H.trim();if(!Z){ee("消息不能为空");return}try{cu.includes(R.status)&&R.status!=="merged"?(await lt(P.taskContinue(R.id),"POST",{message:Z}),ee("已重新排队,同一会话继续"),r(),await Wl(R.id)):["queued","running"].includes(R.status)?(await lt(P.taskFollowups(R.id),"POST",{body:Z,mode:"direct"}),ee("已直接发送"),await dl(R.id),await Bl(R.id)):ee("merged/merge-failed 任务不可继续,也不可发后续消息")}catch(F){ee("发送失败:"+F.message)}},[Wl,r,dl,Bl]),be=y.useCallback(async()=>{const H=le.current;if(!H){te("先选一个任务");return}try{const R=await lt(P.taskTrace(H.id)),Z=new Blob([JSON.stringify({task_id:H.id,status:R.status,note:R.note||"",count:(R.entries||[]).length,entries:R.entries||[]},null,2)],{type:"application/json"}),F=document.createElement("a");F.href=URL.createObjectURL(Z),F.download="trace-"+H.id.slice(0,8)+".json",document.body.appendChild(F),F.click(),F.remove(),setTimeout(()=>URL.revokeObjectURL(F.href),1e3),te("已下载完整链路 "+(R.entries||[]).length+" 条")}catch(R){te("下载失败:"+R.message)}},[]);return y.useEffect(()=>()=>qe(),[qe]),{currentTask:m,detailBody:_,actionMsg:N,actionReasons:X,costBox:G,forkVisible:w,setForkVisible:at,forkMsg:st,coordChildren:k,agents:Ut,agentsNote:Me,reviews:fe,reviewMsg:vt,reviewError:Pt,traceEntries:re,traceNote:tl,traceDone:q,traceKind:tt,setTraceKind:xt,traceMsg:yt,followups:ze,approvals:v,approvalMsg:W,peerMessages:gt,peerMsg:jt,threadText:nt,threadGateNote:Va,threadMsg:Le,threadList:Ot,chatMsg:Fl,chatHistory:ha,showDetail:Wl,refreshDetailLists:vn,doAction:yn,taskCost:Ke,forkTask:La,postReview:va,submitFindings:fu,decideApproval:ru,postPeer:$l,threadAction:pe,sendChatMessage:hl,downloadTrace:be,refreshReviews:()=>void Ye(),refreshApprovals:()=>void Vt()}}function Jy(i){const[r,m]=y.useState([]),[o,_]=y.useState(""),[O,N]=y.useState(null),[E,X]=y.useState(""),D=y.useCallback(async at=>{const st=at!==void 0?at:i();if(!st){m([]);return}try{const et=await lt(P.projectAutomations(st));m(et.automations||[])}catch(et){_("自动化加载失败:"+et.message)}},[i]),G=y.useCallback(async(at,st,et,k)=>{const dt=i();if(!dt){_("先选项目");return}if(!at.trim()){_("自动化名称必填");return}if(!st.trim()){_("定时任务内容必填");return}const Ut={name:at.trim(),prompt:st.trim()};et.trim()&&(Ut.interval_seconds=parseInt(et.trim(),10)),k.trim()&&(Ut.repo_url=k.trim());try{await lt(P.projectAutomations(dt),"POST",Ut),_("自动化已创建(登记定义,到期后由执行器认领)"),await D(dt)}catch(ye){_("创建失败:"+ye.message)}},[i,D]),b=y.useCallback(at=>{const st=i();st&<(P.projectAutomationCancel(st,at),"POST",{}).then(()=>{_("自动化已取消"),D(st)}).catch(et=>_("取消失败:"+et.message))},[i,D]),w=y.useCallback(async at=>{const st=at!==void 0?at:i();if(!st){N(null);return}try{const et=await lt(P.projectTriage(st));N(et),X("")}catch(et){X("分诊加载失败:"+et.message)}},[i]);return{automations:r,autoMsg:o,triage:O,triageError:E,refreshAutomations:D,createAutomation:G,cancelAutomation:b,refreshTriage:w}}function Fy(i,r){const m=y.useRef(i);m.current=i,y.useEffect(()=>{const o=setInterval(()=>{const _=document.activeElement;m.current(_?_.tagName==="SELECT":!1)},r);return()=>clearInterval(o)},[r])}function Wy(i,r){const m=y.useRef(i);m.current=i,y.useEffect(()=>{const o=setInterval(()=>m.current(),r);return()=>clearInterval(o)},[r])}function $y(i){const r=m=>String(m).padStart(2,"0");return r(i.getHours())+":"+r(i.getMinutes())+":"+r(i.getSeconds())}function Iy(){var et;const i=Ly(),r=ky(),m=y.useRef("");m.current=i.projectId;const o=y.useCallback(()=>m.current,[]),_=Jy(o),O=y.useCallback(k=>{const dt=i.projects.find(Ut=>Ut.id===k);return dt&&dt.my_role||null},[i.projects]),N=y.useCallback(()=>{r.refreshTasks(void 0,m.current)},[r]),E=Ky(O,N),[X,D]=y.useState("--:--:--"),G=y.useCallback(k=>{i.setProjectId(k),m.current=k,r.refreshTasks(k),i.refreshEnvs(k),i.refreshMembers(k),i.refreshSkills(k),_.refreshAutomations(k),_.refreshTriage(k)},[i,r,_]),b=(et=i.projects[0])==null?void 0:et.id;y.useEffect(()=>{!i.projectId&&b&&G(b)},[i.projectId,b,G]);const w=y.useCallback(k=>{i.saveKeyInput(k),k.trim()&&(i.refreshHealth(),i.refreshProjects(),i.refreshWorkers(),m.current&&(r.refreshTasks(m.current),i.refreshEnvs(m.current),i.refreshMembers(m.current)),i.loadRelayModels(),i.loadQuota(),i.loadGithubRepos(),i.probeAdmin())},[i,r]),at=y.useCallback(k=>{if(D($y(new Date)),!Rl()){i.refreshHealth();return}r.refreshTasks(void 0,m.current),i.refreshHealth(),i.loadQuota(),i.refreshWorkers(),i.refreshMembers(),i.refreshSkills(),_.refreshAutomations(),_.refreshTriage(),E.refreshDetailLists(),k||(i.refreshProjects(),i.refreshEnvs())},[i,r,_,E]),st=y.useCallback(()=>{Rl()&&(i.loadRelayModels(),i.loadGithubRepos())},[i]);return y.useEffect(()=>{i.refreshHealth(),i.refreshProjects(),i.refreshWorkers(),i.loadQuota(),i.loadGithubRepos(),Rl()&&i.loadRelayModels(),i.probeAdmin()},[]),{keySaved:i.keySaved,keyState:i.keyState,saveKeyInput:w,clearKeyInput:i.clearKeyInput,quotaText:i.quotaText,healthText:i.healthText,refreshHealth:i.refreshHealth,adminGate:i.adminGate,adminBody:i.adminBody,adminResult:i.adminResult,adminUserId:i.adminUserId,setAdminUserId:i.setAdminUserId,adminQuota:i.adminQuota,adminSweep:i.adminSweep,adminBackup:i.adminBackup,projects:i.projects,projectId:i.projectId,projectMsg:i.projectMsg,selectProject:G,refreshProjects:i.refreshProjects,createProject:i.createProject,githubRepos:i.githubRepos,workers:i.workers,workersSummary:i.workersSummary,workersError:i.workersError,refreshWorkers:i.refreshWorkers,members:i.members,memberMsg:i.memberMsg,memberError:i.memberError,refreshMembers:()=>void i.refreshMembers(),inviteMember:i.inviteMember,changeMemberRole:i.changeMemberRole,skills:i.skills,apps:i.apps,mcpServers:i.mcpServers,skillMsg:i.skillMsg,skillError:i.skillError,mcpMsg:i.mcpMsg,mcpOauthMsg:i.mcpOauthMsg,mcpOauthBox:i.mcpOauthBox,refreshSkills:()=>void i.refreshSkills(),reloadMcp:i.reloadMcp,mcpOauth:i.mcpOauth,envs:i.envs,envCount:i.envCount,envMsg:i.envMsg,envRepoLine:i.envRepoLine,envRepoBar:i.envRepoBar,envRepoMsg:i.envRepoMsg,envSelect:i.envSelect,setEnvSelect:i.setEnvSelect,refreshEnvs:()=>void i.refreshEnvs(),createEnv:i.createEnv,archiveEnv:i.archiveEnv,saveEnvRepo:i.saveEnvRepo,models:i.models,modelCustomVisible:i.modelCustomVisible,tasks:r.tasks,taskCountText:r.taskCountText,taskError:r.taskError,refreshTasks:()=>void r.refreshTasks(void 0,m.current),statusFilter:r.statusFilter,setStatusFilter:r.setStatusFilter,executorFilter:r.executorFilter,setExecutorFilter:r.setExecutorFilter,searchText:r.searchText,setSearchText:r.setSearchText,filteredTasks:r.filteredTasks,currentTask:E.currentTask,detailBody:E.detailBody,actionMsg:E.actionMsg,actionReasons:E.actionReasons,showDetail:k=>void E.showDetail(k),doAction:E.doAction,taskCost:E.taskCost,costBox:E.costBox,forkVisible:E.forkVisible,setForkVisible:E.setForkVisible,forkTask:E.forkTask,forkMsg:E.forkMsg,coordChildren:E.coordChildren,coordText:"",agents:E.agents,agentsNote:E.agentsNote,reviews:E.reviews,reviewMsg:E.reviewMsg,reviewError:E.reviewError,refreshReviews:E.refreshReviews,postReview:E.postReview,submitFindings:E.submitFindings,traceEntries:E.traceEntries,traceNote:E.traceNote,traceDone:E.traceDone,traceKind:E.traceKind,setTraceKind:E.setTraceKind,downloadTrace:E.downloadTrace,traceMsg:E.traceMsg,sendChatMessage:E.sendChatMessage,chatMsg:E.chatMsg,chatHistory:E.chatHistory,followups:E.followups,approvals:E.approvals,approvalMsg:E.approvalMsg,refreshApprovals:E.refreshApprovals,decideApproval:E.decideApproval,peerMessages:E.peerMessages,peerMsg:E.peerMsg,postPeer:E.postPeer,automations:_.automations,autoMsg:_.autoMsg,refreshAutomations:()=>void _.refreshAutomations(),createAutomation:_.createAutomation,cancelAutomation:_.cancelAutomation,triage:_.triage,triageError:_.triageError,refreshTriage:()=>void _.refreshTriage(),threadText:E.threadText,threadGateNote:E.threadGateNote,threadMsg:E.threadMsg,threadList:E.threadList,threadAction:E.threadAction,dispatch:async(k,dt)=>{const Ut=await r.dispatch(k,dt);return r.refreshTasks(m.current),Ut},dispatchMsg:"",refreshAll:at,refreshSlow:st,refreshStamp:X}}function iu({label:i,value:r,sub:m}){return s.jsxs("div",{className:"dash-card data-card",children:[s.jsx("div",{className:"dash-card-label",children:i}),s.jsx("div",{className:"dash-card-value",children:r}),s.jsx("div",{className:"muted",children:m})]})}function Py(){const i=At(),r=i.healthText==="未加载"?"未加载":i.healthText.includes("worker_ok=true")?"正常":i.healthText.includes("worker_ok=false")?"异常":i.healthText.includes("加载失败")?"加载失败":"未知",m=i.quotaText.match(/剩余 \$([0-9.]+)/),o=i.quotaText.includes("加载失败")?"加载失败":i.quotaText.includes("未填 key")||i.quotaText==="未加载"?"未加载":m?"$"+m[1]:"配额",_=["running","completed","failed","queued","merged","interrupted"].map(b=>i.tasks.filter(w=>w.status===b).length).map((b,w)=>["running","completed","failed","queued","merged","interrupted"][w]+" "+b).join(" | "),O=i.projects.find(b=>b.id===i.projectId),N=i.taskError?{value:"加载失败",sub:i.taskError}:i.projectId?{value:String(i.tasks.length),sub:i.taskCountText+" | "+_+(O?" | "+O.name:"")}:{value:"先选项目",sub:i.projectMsg||"项目加载后自动选第一个"};let E;if(i.workersError)E={value:"加载失败",sub:i.workersError};else if(i.workers===null)E={value:i.workersSummary||"未加载",sub:"完整 worker 清单仅全局管理员可见"};else{const b=i.workers.filter(w=>{if(w.alive!==void 0&&w.alive!==null)return!!w.alive;const at=wh(w).age;return at!==null&&at<=120}).length;E={value:b+" 在线 / "+i.workers.length,sub:i.workers.length-b+" 离线"}}const X=i.projectMsg.includes("加载失败")?{value:"加载失败",sub:i.projectMsg}:{value:i.projects.length+" 个",sub:O?"当前项目:"+O.name:"未选项目(加载后自动选第一个)"},D=[i.envMsg,i.memberError,i.skillError].filter(b=>b&&b.includes("失败")),G=i.projectId?D.length?{value:"部分加载失败",sub:D.join(" | ")}:{value:i.envs.length+" 个环境",sub:i.members.length+" 成员 | "+i.skills.length+" 技能 | "+i.apps.length+" 应用 | "+i.mcpServers.length+" MCP"}:{value:"先选项目",sub:"选项目后显示环境 / 成员 / 技能计数"};return s.jsxs("section",{className:"dash-cards","aria-label":"数据卡片",children:[s.jsx(iu,{label:"健康",value:r,sub:i.healthText}),s.jsx(iu,{label:"配额",value:o,sub:i.quotaText}),s.jsx(iu,{label:"任务",value:N.value,sub:N.sub}),s.jsx(iu,{label:"Workers",value:E.value,sub:E.sub}),s.jsx(iu,{label:"项目",value:X.value,sub:X.sub}),s.jsx(iu,{label:"项目数据",value:G.value,sub:G.sub})]})}function Ze({id:i,className:r,title:m,children:o}){return s.jsxs("section",{id:i,className:"card"+(r?" "+r:""),children:[m?s.jsx("h2",{children:m}):null,o]})}function Yt({children:i}){return s.jsx("div",{className:"bar",children:i})}function Dl({items:i,empty:r}){return i.length?s.jsx("ul",{className:"data-list",children:i.map((m,o)=>s.jsx("li",{children:m},o))}):s.jsx("div",{className:"muted",children:r||"(暂无)"})}function su({children:i}){return s.jsx("ul",{className:"data-list",children:i})}function tg(){const i=At(),[r,m]=y.useState(""),[o,_]=y.useState(""),[O,N]=y.useState(!1),[E,X]=y.useState(!1),[D,G]=y.useState("docker"),[b,w]=y.useState(""),[at,st]=y.useState(""),[et,k]=y.useState(""),[dt,Ut]=y.useState(""),[ye,Me]=y.useState(""),[Jt,fe]=y.useState(""),[ct,vt]=y.useState(""),Ft=y.useRef(null),Pt=y.useRef(null),kt=y.useRef(null),[re,Ht]=y.useState("");async function tl(){var yt,te,ze;if(Ht(""),!i.projectId){Ht("先选项目");return}if(!r.trim()){Ht("prompt 必填");return}const U={project_id:i.projectId,prompt:r.trim()};i.envSelect&&(U.environment_id=i.envSelect),D&&(U.executor=D);const q=at.trim()||b;q&&(U.model=q),et&&(U.effort=et);const K=Jt.trim()||ye;if(K&&(U.child_model=K),ct&&(U.child_effort=ct),dt){if(dt==="full"&&!window.confirm("完全访问 = 自动批准 + 不受工作区限制,agent 可读写工作区外路径。确认提交吗?"))return;U.approval=dt}if(E){if(dt==="full"){Ht("计划模式不能与完全访问同用");return}if(O){Ht("计划模式不能与 goal 模式同用");return}U.plan=1}O&&(U.goal_mode=1);const tt=o.trim();if(tt){try{U.coordinator_parts=JSON.parse(tt)}catch{Ht("协调员子任务 JSON 解析失败");return}if(!Array.isArray(U.coordinator_parts)){Ht("协调员子任务必须是数组");return}if(O||E){Ht("协调员任务不能与 goal/计划模式同用:协调员任务本身不跑模型回合,请取消 goal/计划勾选,或清空协调员 JSON");return}}const xt=Zy(((yt=Ft.current)==null?void 0:yt.files)||null,((te=Pt.current)==null?void 0:te.files)||null,((ze=kt.current)==null?void 0:ze.files)||null);try{const ne=await i.dispatch(U,xt);Ht(ne),m(""),_(""),Ft.current&&(Ft.current.value=""),Pt.current&&(Pt.current.value=""),kt.current&&(kt.current.value="")}catch(ne){Ht("提交失败:"+((ne==null?void 0:ne.message)||String(ne)))}}return s.jsxs(Ze,{id:"nav-newtask",className:"composer",title:"新建任务",children:[s.jsxs(Yt,{children:[s.jsxs("select",{"aria-label":"环境",value:i.envSelect,onChange:U=>i.setEnvSelect(U.target.value),children:[s.jsx("option",{value:"",children:"环境:不指定(走项目仓库)"}),i.envs.map(U=>s.jsxs("option",{value:U.id,disabled:U.status!=="ready",children:[U.name,"(",U.status,")"]},U.id))]}),s.jsxs("select",{"aria-label":"执行器",value:D,onChange:U=>G(U.target.value),children:[s.jsx("option",{value:"docker",children:"docker(默认,多用户下隔离最强)"}),s.jsx("option",{value:"local",children:"local"}),s.jsx("option",{value:"cloud",children:"cloud"})]}),s.jsxs("label",{children:[s.jsx("input",{type:"checkbox",checked:O,onChange:U=>N(U.target.checked)})," goal 模式"]}),s.jsxs("label",{children:[s.jsx("input",{type:"checkbox",checked:E,onChange:U=>X(U.target.checked)})," 计划模式(只读)"]})]}),s.jsx("textarea",{className:"prompt stacked",placeholder:"任务 prompt(必填)",value:r,onChange:U=>m(U.target.value)}),s.jsx("textarea",{className:"coord stacked",placeholder:"协调员子任务 JSON(可选),留空为普通任务",value:o,onChange:U=>_(U.target.value)}),s.jsxs("div",{className:"muted",children:["协调员格式:[","{",'"name":"后端","prompt":"做后端"',"}",",","{",'"name":"前端","prompt":"做前端"',"}","];提交后 worker 并行建子任务、等齐、合流汇总。不能与 goal/计划模式同用。协调员与 goal/计划互斥:勾了 goal 或计划模式时不要填协调员 JSON,提交前会校验并拦截。子任务费用记父任务创建人账;父任务创建时按分工数预留配额,余额不足时整个父任务被挡下。"]}),s.jsxs(Yt,{children:[s.jsxs("select",{"aria-label":"模型",value:b,onChange:U=>w(U.target.value),children:[s.jsx("option",{value:"",children:"模型:默认(跟随配置)"}),i.models.map(U=>s.jsx("option",{value:U,children:U},U))]}),i.modelCustomVisible?s.jsx("input",{type:"text",placeholder:"手动输入模型(中转列表取不到时用)",value:at,onChange:U=>st(U.target.value)}):null,s.jsxs("select",{"aria-label":"推理强度",value:et,onChange:U=>k(U.target.value),children:[s.jsx("option",{value:"",children:"推理强度:默认(跟随配置)"}),Bh.map(U=>s.jsx("option",{value:U,children:U},U))]}),s.jsxs("select",{"aria-label":"权限",value:dt,onChange:U=>Ut(U.target.value),children:[s.jsx("option",{value:"",children:"权限:默认(跟随配置)"}),s.jsx("option",{value:"ask",children:"请求批准"}),s.jsx("option",{value:"auto",children:"帮我批准"}),s.jsx("option",{value:"full",children:"完全访问"})]})]}),s.jsxs(Yt,{children:[s.jsxs("select",{"aria-label":"子任务模型",value:ye,onChange:U=>Me(U.target.value),children:[s.jsx("option",{value:"",children:"子任务模型:跟随本任务模型"}),i.models.map(U=>s.jsx("option",{value:U,children:U},U))]}),s.jsx("input",{type:"text",placeholder:"子任务模型手动输入(中转列表里没有时填,如 gpt-6-luna;留空=跟随本任务模型)",value:Jt,onChange:U=>fe(U.target.value)}),s.jsxs("select",{"aria-label":"子任务强度",value:ct,onChange:U=>vt(U.target.value),children:[s.jsx("option",{value:"",children:"子任务强度:跟随本任务强度"}),Bh.map(U=>s.jsx("option",{value:U,children:U},U))]})]}),s.jsx("div",{className:"muted",children:"子任务模型/强度是派生默认:本任务用 marker task-create 或协调员分工派生的子任务,未显式写模型/强度时用它;块/分工里显式写了则覆盖它。也可以不在这里填、直接在 prompt 里写“子任务用 gpt-6-luna max”,建任务时服务端会解析成同一对字段(任务详情里可核对)。"}),O?s.jsx("div",{className:"muted",children:"goal 模式:输入框内容即目标,不需要专用 prompt 与验收命令;不限轮次,模型确认达成后在回复末尾输出“目标已达成”即结束,未达成会一直运行,可用取消停止。"}):null,s.jsxs(Yt,{children:[s.jsx("span",{className:"muted",children:"附件(放进任务工作区,图片与普通文件不区分):"}),s.jsx("input",{ref:Ft,type:"file",multiple:!0,title:"文件"}),s.jsx("input",{ref:Pt,type:"file",multiple:!0,title:"文件夹",webkitdirectory:""}),s.jsx("input",{ref:kt,type:"file",accept:"image/*",multiple:!0,title:"图片"})]}),s.jsxs(Yt,{children:[s.jsx("button",{className:"seal",onClick:tl,children:"提交任务"}),s.jsx("span",{className:"muted",children:re||i.dispatchMsg})]})]})}function eg({onNew:i}){const r=At();return s.jsxs("div",{children:[s.jsxs("div",{className:"search-box",children:[s.jsxs("svg",{viewBox:"0 0 24 24",width:"18",height:"18",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round","aria-hidden":"true",children:[s.jsx("circle",{cx:"11",cy:"11",r:"7"}),s.jsx("path",{d:"M21 21l-4.3-4.3"})]}),s.jsx("input",{type:"text",placeholder:"搜索","aria-label":"搜索任务",value:r.searchText,onChange:m=>r.setSearchText(m.target.value)})]}),s.jsx("div",{className:"bar",children:s.jsxs("select",{"aria-label":"项目",value:r.projectId,onChange:m=>r.selectProject(m.target.value),children:[r.projects.length===0?s.jsx("option",{value:"",children:"(先加载项目)"}):null,r.projects.map(m=>s.jsxs("option",{value:m.id,children:[m.name," (",Oe(m.id),")"]},m.id))]})}),s.jsxs("div",{className:"list-section-head",children:[s.jsx("span",{children:"主要聊天"}),s.jsx("button",{type:"button","aria-label":"派发新任务",title:"派发新任务",onClick:i,children:"+"})]}),s.jsxs("div",{className:"bar",children:[s.jsxs("select",{"aria-label":"状态筛选",value:r.statusFilter,onChange:m=>r.setStatusFilter(m.target.value),children:[s.jsx("option",{value:"",children:"状态:全部"}),["queued","running","completed","failed","interrupted","merged","merge-failed"].map(m=>s.jsx("option",{value:m,children:m},m))]}),s.jsxs("select",{"aria-label":"执行器筛选",value:r.executorFilter,onChange:m=>r.setExecutorFilter(m.target.value),children:[s.jsx("option",{value:"",children:"执行器:全部"}),s.jsx("option",{value:"local",children:"local"}),s.jsx("option",{value:"docker",children:"docker"}),s.jsx("option",{value:"cloud",children:"cloud"})]})]}),s.jsxs("div",{className:"bar",children:[s.jsx("button",{onClick:r.refreshTasks,children:"刷新"}),s.jsx("span",{className:"muted",children:r.taskCountText})]}),r.taskError?s.jsx("div",{className:"err",children:r.taskError}):r.projectId?r.filteredTasks.length===0?s.jsx("div",{className:"muted",children:r.tasks.length?"没有符合筛选条件的任务":"该项目暂无任务"}):s.jsx("ul",{className:"issue-list",children:r.filteredTasks.slice().reverse().map(m=>{var o;return s.jsxs("li",{className:"issue-item"+(((o=r.currentTask)==null?void 0:o.id)===m.id?" selected":""),onClick:()=>r.showDetail(m.id),children:[s.jsx("span",{className:"issue-state st-"+m.status,"aria-hidden":"true"}),s.jsxs("span",{children:[s.jsxs("div",{className:"issue-title",children:[m.plan?"[计划] ":"",m.forked_from?"[分支] ":"",m.coordinator_parts?"[协调] ":"",m.parent_task_id?"[协调子任务] ":"",String(m.prompt||Oe(m.id)).slice(0,60)]}),s.jsxs("div",{className:"issue-meta",children:[Oe(m.id)," · ",s.jsx("span",{className:"st-"+m.status,children:m.status})," · ",m.executor||"",m.updated_at?" · "+new Date(m.updated_at*1e3).toLocaleString():""]})]})]},m.id)})}):s.jsx("div",{className:"muted",children:"先选项目"})]})}function lg(i){const r=y.useRef(null),[m,o]=y.useState(!0),_=y.useRef(!0),O=()=>{const N=r.current;if(!N)return;const E=N.scrollHeight-N.scrollTop-N.clientHeight<80;_.current=E,o(E)};return y.useEffect(()=>{_.current&&r.current&&(r.current.scrollTop=r.current.scrollHeight)},[i]),{ref:r,following:m,onScroll:O}}function ag({msg:i}){if(i.role==="status")return s.jsx("div",{className:"chat-status",children:i.text});const r=i.label||(i.role==="user"?"用户":i.role==="assistant"?"助手":"工具");return s.jsxs("div",{className:"gh-comment "+i.role,children:[s.jsx("div",{className:"gh-comment-header",children:s.jsx("span",{className:"gh-comment-author",children:r})}),s.jsx("div",{className:"gh-comment-body",children:i.text})]})}function Gh({messages:i,empty:r}){const{ref:m,following:o,onScroll:_}=lg(i.length);return s.jsxs("div",{className:"chat-list",ref:m,onScroll:_,children:[i.length===0?s.jsx("div",{className:"muted",children:r||"(暂无消息)"}):i.map(O=>s.jsx(ag,{msg:O},O.id)),!o&&i.length>0?s.jsx("button",{className:"chat-follow",onClick:()=>{m.current&&(m.current.scrollTop=m.current.scrollHeight)},children:"回到最新 ↓"}):null]})}function ng({onSend:i,disabled:r,placeholder:m}){const[o,_]=y.useState(""),O=()=>{const N=o.trim();!N||r||(i(N),_(""))};return s.jsxs("div",{className:"gh-comment-box",children:[s.jsx("textarea",{"aria-label":"会话输入",rows:3,placeholder:m||"消息",value:o,disabled:r,onChange:N=>_(N.target.value),onKeyDown:N=>{N.key==="Enter"&&!N.shiftKey&&(N.preventDefault(),O())}}),s.jsx("div",{className:"gh-comment-footer",children:s.jsx("button",{type:"button",className:"primary","aria-label":"发送",disabled:r||!o.trim(),onClick:O,children:"发送"})})]})}function Qh(i,r){const m=i.id||"trace-"+r;if(i.kind==="message")return{id:m,role:"assistant",text:(i.phase?"["+i.phase+"] ":"")+(i.text||""),label:"助手"};if(i.kind==="exec"||i.kind==="file"||i.kind==="tool"||i.kind==="call"){const o=i.kind==="exec"?"命令":i.kind==="file"?"文件":i.kind==="tool"?"工具":"调用";return{id:m,role:"tool",text:fr(i),label:o}}return i.kind==="plan"?{id:m,role:"status",text:i.text||""}:i.kind==="reasoning"?{id:m,role:"status",text:"思考:"+(i.summary||"")}:i.kind==="agent-start"?{id:m,role:"status",text:"子代理开始:"+(i.name||"")+(i.prompt?"("+i.prompt+")":"")}:i.kind==="agent-done"?{id:m,role:"status",text:"子代理结束:"+(i.name||"")+" "+(i.status||"")}:{id:m,role:"status",text:fr(i)}}function ug(){const i=At(),r=y.useMemo(()=>{const m=i.currentTask,o=m&&m.prompt?[{id:"task-prompt",role:"user",text:m.prompt}]:[],O=(i.traceKind?i.traceEntries.filter(E=>E.kind===i.traceKind):i.traceEntries).map((E,X)=>Qh(E,X)),N=i.traceDone?[{id:"trace-done",role:"status",text:"链路已结束("+i.traceDone+")"}]:[];return[...o,...O,...N]},[i.currentTask,i.traceEntries,i.traceKind,i.traceDone]);return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"muted",children:"实时链路"}),s.jsxs(Yt,{children:[s.jsxs("select",{"aria-label":"链路类型",value:i.traceKind,onChange:m=>i.setTraceKind(m.target.value),children:[s.jsx("option",{value:"",children:"链路类型:全部"}),["exec","file","call","tool","message","reasoning","plan","agent-start","agent-done"].map(m=>s.jsx("option",{value:m,children:m==="exec"?"命令":m==="file"?"文件":m==="call"?"调用":m==="tool"?"工具":m==="message"?"消息":m==="reasoning"?"思考":m==="plan"?"计划":"子代理"},m))]}),s.jsx("button",{onClick:i.downloadTrace,children:"下载完整链路"}),s.jsx("span",{className:"muted",children:i.traceMsg})]}),i.traceNote?s.jsx("div",{children:s.jsx("span",{className:"err",children:i.traceNote})}):null,s.jsx("div",{className:"chat-column",children:s.jsx(Gh,{messages:r,empty:"(暂无链路条目)"})}),s.jsx("div",{className:"muted",children:"对话时间线:首条是本任务 prompt(用户);助手回复来自 message 条目,命令/文件/工具/调用是工具评论框,计划/思考/子代理开始结束是时间线上的状态文字。流式进行中时新条目实时追加;你在底部时自动跟随,往上翻时暂停跟随,点“回到最新”恢复。"})]})}function ig(){const i=At(),r=i.currentTask,m=y.useMemo(()=>{if(!r)return[];if(i.chatHistory)return i.chatHistory.messages.map(b=>({id:b.id,role:b.role,text:b.text,label:b.label}));const X=r.prompt?[{id:"task-prompt",role:"user",text:r.prompt}]:[],D=i.traceEntries.filter(b=>b.kind==="message").map((b,w)=>Qh(b,w)),G=i.followups.map((b,w)=>({id:"followup-"+w,role:"user",text:String(b.body||""),label:"后续消息"}));return[...X,...D,...G]},[r,i.chatHistory,i.traceEntries,i.followups]);if(!r)return s.jsx("div",{className:"muted",children:"点任务行查看详情后,这里显示继续会话"});const o=cu.includes(r.status),_=["queued","running"].includes(r.status),O=o||_,N=o?"终态任务:发送走继续接口(同一会话再跑一轮)":_?"运行中/排队中:发送走后续消息直接发送(跑完当前轮后优先执行)":"merged/merge-failed 任务不可继续,也不可发后续消息",E=i.chatHistory?i.chatHistory.legacy_continue_count??null:null;return s.jsxs("div",{className:"chat-column",children:[E!==null&&E>0?s.jsxs("div",{className:"muted",children:["此前有 ",E," 条继续消息发生在持久化之前,未入库,上面历史不完整;部署后新发的继续消息会正常显示。"]}):null,i.chatHistory&&i.chatHistory.trace_note?s.jsx("div",{className:"muted",children:i.chatHistory.trace_note}):null,s.jsx(Gh,{messages:m,empty:"(暂无会话消息)"}),s.jsx(ng,{disabled:!O,placeholder:"消息",onSend:X=>void i.sendChatMessage(X)}),s.jsxs("div",{className:"muted",children:[N,"。",i.chatMsg]})]})}function cg(){return At().currentTask?s.jsxs("div",{className:"conversation",children:[s.jsx(ug,{}),s.jsx("div",{className:"conv-divider"}),s.jsx(ig,{})]}):s.jsx("div",{className:"conversation",children:s.jsx("div",{className:"muted",children:"在任务列表里点一条任务,对话和详情就切到该任务;也可以在上面派发一个新任务。"})})}function sg(){const i=At(),r=i.currentTask,[m,o]=y.useState("");if(!r)return null;const _=cu.includes(r.status),O=["failed","interrupted","merge-failed"].includes(r.status);return s.jsxs(s.Fragment,{children:[s.jsxs(Yt,{children:[s.jsx("button",{disabled:!O,title:O?"":"仅 failed/interrupted/merge-failed 可重试",onClick:()=>i.doAction("retry"),children:"重试"}),s.jsx("button",{className:"danger",disabled:_,title:_?"终态任务不可取消":"",onClick:()=>i.doAction("cancel"),children:"取消"}),s.jsx("button",{disabled:r.status!=="completed",title:r.status!=="completed"?"仅 completed 可合并":"",onClick:()=>i.doAction("merge"),children:"合并(completed → merged)"}),s.jsx("button",{disabled:r.status!=="merged",title:r.status!=="merged"?"仅 merged 可提 PR":"",onClick:()=>i.doAction("pr"),children:"提 PR(须 merged)"}),_?s.jsx("button",{onClick:()=>i.setForkVisible(!0),children:"分支"}):null,s.jsx("button",{onClick:i.taskCost,children:"查成本"}),s.jsxs("span",{className:"muted",children:[i.actionMsg," ",i.actionReasons]})]}),i.forkVisible?s.jsxs("div",{children:[s.jsx("input",{type:"text",placeholder:"分支首轮消息(必填,带历史的新会话)",style:{width:"100%",boxSizing:"border-box",margin:"6px 0"},value:m,onChange:N=>o(N.target.value)}),s.jsxs(Yt,{children:[s.jsx("button",{className:"seal",onClick:async()=>{await i.forkTask(m),o("")},children:"创建分支任务"}),s.jsx("span",{className:"muted",children:i.forkMsg})]})]}):null]})}function fg(){const i=At(),[r,m]=y.useState("uncommittedChanges"),[o,_]=y.useState(""),[O,N]=y.useState(""),[E,X]=y.useState("");return s.jsxs(s.Fragment,{children:[s.jsx("h3",{children:"评审队列"}),s.jsxs("select",{"aria-label":"评审目标",value:r,onChange:D=>m(D.target.value),children:[s.jsx("option",{value:"uncommittedChanges",children:"未提交改动(工作区)"}),s.jsx("option",{value:"baseBranch",children:"与分支的差异"}),s.jsx("option",{value:"commit",children:"指定提交"}),s.jsx("option",{value:"custom",children:"自定义评审指令"})]}),s.jsx("input",{className:"stacked",placeholder:"与分支的差异用:分支名(如 main)",style:{width:"100%"},value:o,onChange:D=>_(D.target.value)}),s.jsx("input",{className:"stacked",placeholder:"指定提交用:提交 sha",style:{width:"100%"},value:O,onChange:D=>N(D.target.value)}),s.jsx("textarea",{placeholder:"自定义评审指令用:评审指令正文",value:E,onChange:D=>X(D.target.value)}),s.jsxs(Yt,{children:[s.jsx("button",{className:"primary",onClick:()=>void i.postReview({target_type:r,branch:o.trim()||void 0,sha:O.trim()||void 0,instructions:E.trim()||void 0}),children:"登记评审需求"}),s.jsx("button",{onClick:i.refreshReviews,children:"刷新队列"}),s.jsx("span",{className:"muted",children:i.reviewMsg})]}),s.jsx("div",{className:"muted",children:"本基线只登记评审需求(requested),不自动跑评审;评审是否执行、结果如何,以评审队列里的记录为准。"}),i.reviewError?s.jsx("div",{className:"muted",children:i.reviewError}):i.reviews.length===0?s.jsx("div",{className:"muted",children:"点任务行查看详情后,点“刷新队列”查看评审记录"}):s.jsx(su,{children:i.reviews.map(D=>{let G=D.target_type||"";return D.branch&&(G+=":"+D.branch),D.sha&&(G+=":"+String(D.sha).slice(0,12)),D.instructions&&(G+=":"+String(D.instructions).slice(0,80)),s.jsxs("li",{children:[s.jsxs("span",{className:"row-main",children:[s.jsx("b",{children:"评审"})," ",Oe(D.id)," | ",G," | ",s.jsx("span",{className:"st-"+(D.status||""),children:D.status}),D.created_at?" | "+ni(D.created_at):"",s.jsx("br",{}),D.findings?String(D.findings).slice(0,1e3):s.jsx("span",{className:"muted",children:"(暂无 findings:评审尚未执行或尚未写回)"})]}),s.jsx("button",{onClick:()=>i.submitFindings(D.id),children:"写 findings"})]},D.id)})})]})}function rg(){const i=At();return i.currentTask?i.currentTask.coordinator_parts?i.coordChildren.length===0?s.jsx("div",{className:"muted",children:"(子任务尚未创建)"}):s.jsx(su,{children:i.coordChildren.map(r=>s.jsx("li",{children:s.jsxs("span",{className:"row-main",children:["分工 ",s.jsx("b",{children:r.coordinator_part||""})," | 状态 ",s.jsx("span",{className:"st-"+r.status,children:r.status})," | ",Oe(r.id)," | ",String(r.prompt||"").slice(0,60)]})},r.id))}):s.jsx("div",{className:"muted",children:"(非协调员任务)"}):s.jsx("div",{className:"muted",children:"点任务行查看详情后,这里显示协调员的子任务"})}function og(){const i=At();if(!i.currentTask)return s.jsx("div",{className:"muted",children:"点任务行查看详情后,这里显示子代理链路"});if(i.agents.length===0)return s.jsxs("div",{className:"muted",children:["(暂无子代理)",i.agentsNote?s.jsx("span",{className:"err",children:i.agentsNote}):null]});const r={main:"主任务",delegate:"委派子任务",model:"模型子代理"},m={running:"进行中",completed:"已完成",failed:"失败",interrupted:"已中断",unknown:"未知"};return s.jsx(su,{children:i.agents.map((o,_)=>s.jsx("li",{children:s.jsxs("span",{className:"row-main",children:[s.jsxs("b",{children:[r[o.kind||""]||o.kind||"",":"]}),o.name||""," ",s.jsx("span",{className:"st-"+(o.status||""),children:m[o.status||""]||o.status||"?"})," | 过程调用 "+(o.calls?o.calls.length:0)+" 条",o.updated_ts?" | 最近活动 "+ni(o.updated_ts/1e3):"",o.prompt?s.jsxs(s.Fragment,{children:[s.jsx("br",{}),s.jsxs("span",{className:"muted",children:["任务:",o.prompt]})]}):null,o.calls&&o.calls.length?s.jsxs(s.Fragment,{children:[s.jsx("br",{}),o.calls.slice(-50).map((O,N)=>s.jsx("div",{children:fr(O)},N))]}):s.jsxs(s.Fragment,{children:[s.jsx("br",{}),s.jsx("span",{className:"muted",children:"(暂无过程调用)"})]})]})},_))})}function dg(){const i=At();return s.jsxs(s.Fragment,{children:[s.jsxs(Yt,{children:[s.jsx("button",{onClick:i.refreshApprovals,children:"刷新审批队列"}),s.jsx("span",{className:"muted",children:i.approvalMsg})]}),s.jsx("div",{className:"muted",children:"待审批项需人工点记录允许或记录拒绝;超时默认拒绝。当前版本只留回执,不拦截实时行为(实时请求的事前拦截待 host 接入后生效,在此之前实时行为不变)。"}),s.jsx(Dl,{empty:"(暂无审批项)",items:i.approvals.map(r=>s.jsxs("span",{className:"row-main",children:["[",r.kind,"|",r.status,"] ",r.summary,["approved","denied","expired"].includes(r.status||"")?"|回执:"+(r.decision||r.status)+" by "+String(r.decided_by||"").slice(0,8)+" "+ni(r.decided_at):"",r.status==="pending"?s.jsxs(s.Fragment,{children:[" ",s.jsx("button",{onClick:()=>i.decideApproval(r.id,"approved"),children:"记录允许"})," ",s.jsx("button",{onClick:()=>i.decideApproval(r.id,"denied"),children:"记录拒绝"})]}):null]},r.id))})]})}function mg(){const i=At(),[r,m]=y.useState(""),[o,_]=y.useState("");return s.jsxs(s.Fragment,{children:[s.jsx("input",{placeholder:"目标任务 id(与本任务同项目;对方运行中排队执行、已终态则续跑复活,绝不打断当前轮)",style:{width:"100%"},value:r,onChange:O=>m(O.target.value)}),s.jsx("textarea",{placeholder:"消息正文(以本任务名义发出;对方 agent 会看到发送方任务 id,可照此回消息)",value:o,onChange:O=>_(O.target.value)}),s.jsxs(Yt,{children:[s.jsx("button",{onClick:async()=>{await i.postPeer("queued",r,o),_("")},children:"排队发给对方"}),s.jsx("button",{className:"primary",onClick:async()=>{await i.postPeer("direct",r,o),_("")},children:"直接发送"}),s.jsx("span",{className:"muted",children:i.peerMsg})]}),s.jsx(Dl,{empty:"(本任务尚未发出任务间消息)",items:i.peerMessages.map((O,N)=>s.jsxs("span",{className:"row-main",children:["[发给 ",String(O.task_id).slice(0,8),"|",O.mode,"|",O.status,"] ",String(O.body||"").slice(0,120)]},N))})]})}function hg(){const i=At(),[r,m]=y.useState(""),o=i.currentTask?["completed","failed","interrupted"].includes(i.currentTask.status):!1;return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"muted",children:i.threadText||"点任务行查看详情后,这里显示线程元数据"}),s.jsxs(Yt,{children:[s.jsx("input",{type:"text",placeholder:"新线程名",value:r,onChange:_=>m(_.target.value)}),s.jsx("button",{disabled:!o,onClick:()=>i.threadAction("rename",r),children:"重命名"}),s.jsx("button",{disabled:!o,onClick:()=>i.threadAction("archive",""),children:"归档"}),s.jsx("button",{disabled:!o,onClick:()=>i.threadAction("unarchive",""),children:"取消归档"}),s.jsx("button",{disabled:!o,onClick:()=>i.threadAction("compact",""),children:"压缩"}),s.jsx("span",{className:"muted",children:i.threadGateNote}),s.jsx("span",{className:"muted",children:i.threadMsg})]}),s.jsx(Dl,{empty:"暂无其他线程",items:i.threadList.map((_,O)=>s.jsxs("span",{className:"row-main",children:[_.name||_.thread_name||_.id||""," [",_.id||"","]",_.archived?" [已归档]":"",_.cwd?" "+_.cwd:""]},O))}),s.jsx("div",{className:"muted",children:"限制:Docker/异机任务线程操作一律 409;merged/merge-failed 不可改;turn/steer 与 review/start 不在本批。"})]})}function vg(){const i=At(),r=i.currentTask;return r?s.jsxs(Ze,{id:"nav-detail",title:"任务详情",children:[s.jsxs("div",{children:["状态 ",s.jsx("b",{className:"st-"+r.status,children:r.status}),"(",wy[r.status]||"未知状态",")",s.jsx("br",{}),"任务 ",r.id," | turn ",Oe(r.turn_id)," | root turn ",Oe(r.root_turn_id),s.jsx("br",{}),"executor ",r.executor||""," | 主机 ",r.worktree_host||"",s.jsx("br",{}),"模型 ",r.model||"默认"," | 强度 ",r.effort||"默认",r.child_model||r.child_effort?s.jsxs(s.Fragment,{children:[s.jsx("br",{}),"子任务默认 ",(r.child_model||"跟随本任务")+" / "+(r.child_effort||"跟随本任务")]}):null,s.jsx("br",{}),"权限 ",r.approval||"默认"," | 项目 ",Oe(r.project_id),s.jsx("br",{}),"更新 ",ni(r.updated_at),r.plan?s.jsxs(s.Fragment,{children:[s.jsx("br",{}),"计划模式(只读)"]}):null,r.forked_from?s.jsxs(s.Fragment,{children:[s.jsx("br",{}),"分支自 ",Oe(r.forked_from)]}):null,r.goal_mode?s.jsxs(s.Fragment,{children:[s.jsx("br",{}),"goal 模式(不限轮次,模型宣告完成)"]}):r.verify_command?s.jsxs(s.Fragment,{children:[s.jsx("br",{}),"goal 模式(旧语义,验收命令)"]}):null,r.coordinator_parts?s.jsxs(s.Fragment,{children:[s.jsx("br",{}),"协调员:",r.coordinator_parts.length," 个子任务(",r.coordinator_parts.map(m=>m.name).join("、"),")"]}):null,r.parent_task_id?s.jsxs(s.Fragment,{children:[s.jsx("br",{}),"协调子任务,父任务 ",Oe(r.parent_task_id),r.coordinator_part?"("+r.coordinator_part+")":""]}):null,r.attachments&&r.attachments.length?s.jsxs(s.Fragment,{children:[s.jsx("br",{}),"附件:",r.attachments.join("、")]}):null,r.pr_url?s.jsxs(s.Fragment,{children:[s.jsx("br",{}),s.jsxs("a",{href:r.pr_url,target:"_blank",rel:"noopener noreferrer",children:["PR #",r.pr_number]})]}):null,s.jsx("br",{}),"prompt:",r.prompt||""]}),s.jsx(sg,{}),s.jsx("pre",{children:i.detailBody||"(暂无 result/error 输出)"}),i.costBox?s.jsx("pre",{children:i.costBox}):null,s.jsx("h3",{children:"协调子任务"}),s.jsx(rg,{}),s.jsx("h3",{children:"子代理链路"}),s.jsx(og,{}),s.jsx("h3",{children:"评审队列"}),s.jsx(fg,{}),s.jsx("h3",{children:"运行时审批队列"}),s.jsx(dg,{}),s.jsx("h3",{children:"给其他任务发消息"}),s.jsx(mg,{}),s.jsx("h3",{children:"线程"}),s.jsx(hg,{})]}):s.jsx(Ze,{id:"nav-detail",title:"任务详情",children:s.jsx("div",{className:"muted",children:"点任务行查看详情"})})}function yg(){const i=At(),[r,m]=y.useState("");return s.jsxs(Ze,{id:"nav-key",title:"API Key",children:[s.jsxs(Yt,{children:[s.jsx("input",{type:"password",placeholder:"粘贴你的 X-API-Key(与调 API 用的同一个)",autoComplete:"off",value:r,onChange:o=>m(o.target.value)}),s.jsx("button",{className:"primary",onClick:()=>{i.saveKeyInput(r),m("")},children:"保存到本浏览器"}),s.jsx("button",{onClick:i.clearKeyInput,children:"清除"}),s.jsx("span",{className:"muted",children:i.keyState})]}),s.jsx("div",{children:s.jsx("span",{className:"muted",children:i.quotaText})})]})}function gg(){const i=At();return s.jsxs(Ze,{id:"adminCard",title:s.jsxs(s.Fragment,{children:["管理区 ",s.jsx("span",{className:"muted",children:"(仅全局管理员)"})]}),children:[s.jsx("div",{className:"muted",children:i.adminGate}),i.adminBody?s.jsxs(s.Fragment,{children:[s.jsxs(Yt,{children:[s.jsx("input",{type:"text",placeholder:"要查配额的用户 id",value:i.adminUserId,onChange:r=>i.setAdminUserId(r.target.value)}),s.jsx("button",{onClick:i.adminQuota,children:"查该用户配额"})]}),s.jsxs(Yt,{children:[s.jsx("button",{onClick:i.adminSweep,children:"手动触发 sweep(清理陈旧 running 任务)"}),s.jsx("button",{onClick:i.adminBackup,children:"手动触发备份"})]}),s.jsx("div",{className:"muted",children:i.adminResult})]}):null]})}function Sg(){const i=At(),[r,m]=y.useState(""),[o,_]=y.useState(""),[O,N]=y.useState("");return s.jsxs(Ze,{id:"nav-projects",title:"项目",children:[s.jsxs(Yt,{children:[s.jsxs("select",{"aria-label":"项目",value:i.projectId,onChange:E=>i.selectProject(E.target.value),children:[i.projects.length===0?s.jsx("option",{value:"",children:"(先加载项目)"}):null,i.projects.map(E=>s.jsxs("option",{value:E.id,children:[E.name," (",Oe(E.id),")"]},E.id))]}),s.jsx("button",{onClick:i.refreshProjects,children:"刷新"})]}),s.jsxs(Yt,{children:[s.jsx("input",{type:"text",placeholder:"新项目名",value:r,onChange:E=>m(E.target.value)}),s.jsxs("select",{"aria-label":"从 GitHub 仓库选",value:o,onChange:E=>{_(E.target.value),E.target.value&&N(E.target.value)},children:[s.jsx("option",{value:"",children:"(从 GitHub App 安装仓库里选,或手动输入)"}),i.githubRepos.map(E=>s.jsx("option",{value:E.clone_url,children:E.repo_full_name},E.clone_url))]}),s.jsx("input",{type:"text",placeholder:"repo_url(可选)",value:O,onChange:E=>N(E.target.value)}),s.jsx("button",{onClick:async()=>{await i.createProject(r,O),m(""),N(""),_("")},children:"新建项目"})]}),s.jsx("span",{className:"muted",children:i.projectMsg})]})}function pg(){const i=At(),[r,m]=y.useState(""),[o,_]=y.useState("contributor"),O=i.projects.find(N=>N.id===i.projectId);return s.jsxs(Ze,{title:"项目成员",children:[O?s.jsxs("div",{className:"muted",children:[O.name," | ",O.id," | 仓库 ",O.repo_url||"未绑定"," | 我的角色 ",O.my_role||"未知"]}):s.jsx("div",{className:"muted",children:"先选项目"}),s.jsxs(Yt,{children:[s.jsx("input",{type:"text",placeholder:"成员 user_id 或用户名",value:r,onChange:N=>m(N.target.value)}),s.jsxs("select",{"aria-label":"邀请角色",value:o,onChange:N=>_(N.target.value),children:[s.jsx("option",{value:"viewer",children:"viewer(只读)"}),s.jsx("option",{value:"contributor",children:"contributor(可建任务)"}),s.jsx("option",{value:"maintainer",children:"maintainer(可批合并)"}),s.jsx("option",{value:"owner",children:"owner(可管项目)"})]}),s.jsx("button",{onClick:async()=>{await i.inviteMember(r,o),m("")},children:"邀请成员"}),s.jsx("button",{onClick:i.refreshMembers,children:"刷新成员"})]}),i.memberError?s.jsx("div",{className:"muted",children:i.memberError}):i.members.length===0?s.jsx("div",{className:"muted",children:"先选项目"}):s.jsx(su,{children:i.members.map(N=>{const E=(N.username||"")+(N.username?"("+String(N.user_id).slice(0,8)+")":String(N.user_id).slice(0,8));return s.jsxs("li",{children:[s.jsx("span",{className:"row-main",children:E}),s.jsx("select",{"aria-label":"成员角色",value:N.role||"viewer",onChange:X=>void i.changeMemberRole(N.user_id,X.target.value),children:Gy.map(X=>s.jsx("option",{value:X,children:X},X))})]},N.user_id)})}),s.jsx("span",{className:"muted",children:i.memberMsg})]})}function bg(){const i=At(),[r,m]=y.useState(""),[o,_]=y.useState(""),[O,N]=y.useState("");return s.jsxs(Ze,{id:"nav-env",title:s.jsxs(s.Fragment,{children:["环境 ",s.jsx("span",{className:"muted",children:"(选、建、管:当前项目的基准检出)"})]}),children:[s.jsxs(Yt,{children:[s.jsx("button",{onClick:i.refreshEnvs,children:"刷新"}),s.jsx("span",{className:"muted",children:i.envCount})]}),i.envs.length===0?s.jsx("div",{className:"muted",children:"先选项目"}):s.jsx(su,{children:i.envs.map(E=>s.jsxs("li",{children:[s.jsxs("span",{className:"row-main",children:[E.name,"(",Oe(E.id),") / 分支 ",E.branch||"默认分支"," / 执行器 ",E.executor||"不限"," / 状态 ",E.status,E.status==="failed"&&E.error_text?" / 失败原因:"+E.error_text:""]}),E.status==="archived"?null:s.jsx("button",{onClick:()=>void i.archiveEnv(E.id),children:"归档"})]},E.id))}),s.jsxs(Yt,{children:[s.jsx("input",{type:"text",placeholder:"新环境名",value:r,onChange:E=>m(E.target.value)}),s.jsx("input",{type:"text",placeholder:"分支(可选,空=默认分支)",value:o,onChange:E=>_(E.target.value)}),s.jsxs("select",{"aria-label":"新环境执行器",value:O,onChange:E=>N(E.target.value),children:[s.jsx("option",{value:"",children:"执行器:不限"}),s.jsx("option",{value:"local",children:"local"}),s.jsx("option",{value:"docker",children:"docker"})]}),s.jsx("button",{onClick:async()=>{await i.createEnv(r,o,O),m(""),_("")},children:"新建环境"})]}),s.jsx("span",{className:"muted",children:i.envMsg})]})}function Tg(){const i=At(),[r,m]=y.useState("");return s.jsxs(Ze,{title:"仓库与环境上下文",children:[s.jsx("div",{className:"muted",children:i.envRepoLine||"先选项目"}),s.jsx("div",{className:"muted",children:i.envCount}),i.envRepoBar?s.jsxs(Yt,{children:[s.jsxs("select",{"aria-label":"补仓库",value:r,onChange:o=>m(o.target.value),children:[s.jsx("option",{value:"",children:"(从 GitHub App 安装仓库里选)"}),i.githubRepos.map(o=>s.jsx("option",{value:o.clone_url,children:o.repo_full_name},o.clone_url))]}),s.jsx("button",{onClick:()=>void i.saveEnvRepo(r),children:"保存仓库"})]}):null,s.jsx("span",{className:"muted",children:i.envRepoMsg})]})}function jg(){const i=At();return s.jsxs(Ze,{id:"nav-workers",title:"Workers",children:[s.jsx(Yt,{children:s.jsx("button",{onClick:i.refreshWorkers,children:"刷新"})}),i.workersError?s.jsx("div",{className:"muted",children:i.workersError}):i.workers===null?s.jsx("div",{className:"muted",children:i.workersSummary||"未加载"}):i.workers.length===0?s.jsx("div",{className:"muted",children:"无 worker 注册"}):s.jsx(su,{children:i.workers.map((r,m)=>{const o=r.running===void 0||r.running===null?"?":r.running,_=r.capacity===void 0||r.capacity===null?"?":r.capacity,O=wh(r),N=r.alive!==void 0&&r.alive!==null?!!r.alive:O.age!==null&&O.age<=120,E=N?" / 在线":" / 离线(占用为上次上报值)";return s.jsx("li",{className:N?"is-online":"is-offline",children:s.jsxs("span",{className:"row-main",children:[r.worker_id||r.id||"?"," / ",r.executor||""," / 占用 ",o,"/",_,E," / 最后心跳 ",O.text]})},m)})})]})}function xg(){const i=At();return s.jsxs(Ze,{title:"健康与配额",children:[s.jsxs(Yt,{children:[s.jsx("span",{className:"muted",children:i.healthText}),s.jsx("button",{onClick:i.refreshHealth,children:"刷新"})]}),s.jsx("div",{children:s.jsx("span",{className:"muted",children:i.quotaText})}),s.jsx("div",{className:"muted",children:i.workersSummary})]})}function Eg(){const i=At();return s.jsxs(Ze,{id:"nav-skills",title:"技能与 MCP",children:[s.jsx("div",{className:"muted",children:"反映宿主租户 home,容器内 home 可能不同;重载只在 schema 枚举里,当前 pin 的二进制拒绝时原样 502;OAuth 只发起登录,不代表登录完成。"}),s.jsxs(Yt,{children:[s.jsx("button",{onClick:i.refreshSkills,children:"刷新"}),s.jsx("span",{className:"muted",children:i.skillMsg})]}),s.jsx("h3",{children:"技能"}),i.skillError?s.jsx("div",{className:"muted",children:i.skillError}):s.jsx(Dl,{empty:"暂无技能",items:i.skills.map((r,m)=>s.jsxs("span",{className:"row-main",children:[r.name||r.id||"",r.description?" - "+r.description:"",r.detail?" - "+r.detail:""]},m))}),s.jsx("h3",{children:"应用"}),s.jsx(Dl,{empty:"暂无应用",items:i.apps.map((r,m)=>s.jsxs("span",{className:"row-main",children:[r.name||r.id||"",r.description?" - "+r.description:""]},m))}),s.jsx("h3",{children:"MCP 服务"}),s.jsx(Dl,{empty:"暂无 MCP 服务",items:i.mcpServers.map((r,m)=>{const o=r.detail||r.details||r.state||"";return s.jsxs("span",{className:"row-main",children:[r.name||r.id||""," | 状态 ",r.status||"",o?" | "+o:""]},m)})})]})}function _g(){const i=At(),[r,m]=y.useState("");return s.jsxs(Ze,{title:"MCP 操作",children:[s.jsxs(Yt,{children:[s.jsx("button",{onClick:i.reloadMcp,children:"重载配置"}),s.jsx("span",{className:"muted",children:i.mcpMsg})]}),s.jsxs(Yt,{children:[s.jsx("input",{type:"text",placeholder:"MCP 服务名",value:r,onChange:o=>m(o.target.value)}),s.jsx("button",{onClick:()=>void i.mcpOauth(r),children:"发起 OAuth 登录"}),s.jsx("span",{className:"muted",children:i.mcpOauthMsg})]}),i.mcpOauthBox?s.jsx("pre",{children:i.mcpOauthBox}):null]})}function Ng(){const i=At(),[r,m]=y.useState(""),[o,_]=y.useState(""),[O,N]=y.useState(""),[E,X]=y.useState("");return s.jsxs(s.Fragment,{children:[s.jsx("input",{placeholder:"自动化名称(同项目内启用中的名称唯一)",style:{width:"100%"},value:r,onChange:D=>m(D.target.value)}),s.jsx("textarea",{placeholder:"定时任务内容(创建只登记定义与到期状态;执行由后续执行器经 claim 抢单后建任务,不另起调度器)",value:o,onChange:D=>_(D.target.value)}),s.jsxs(Yt,{children:[s.jsx("input",{placeholder:"间隔秒数(默认 86400,最小 60)",style:{width:"40%"},value:O,onChange:D=>N(D.target.value)}),s.jsx("input",{placeholder:"仓库(可选)",style:{width:"40%"},value:E,onChange:D=>X(D.target.value)}),s.jsx("button",{className:"primary",onClick:async()=>{await i.createAutomation(r,o,O,E),m(""),_(""),N(""),X("")},children:"创建自动化"}),s.jsx("button",{onClick:i.refreshAutomations,children:"刷新自动化"}),s.jsx("span",{className:"muted",children:i.autoMsg})]}),s.jsx(Dl,{empty:"(暂无自动化)",items:i.automations.map(D=>s.jsxs("span",{className:"row-main",children:["[",D.status,D.due?"|到期":"","] ",D.name,"|每 ",D.interval_seconds," 秒|下次 ",ni(D.next_run_at),D.status==="active"?s.jsxs(s.Fragment,{children:[" ",s.jsx("button",{onClick:()=>i.cancelAutomation(D.id),children:"取消"})]}):null]},D.id))})]})}function Cg(){var m,o,_,O;const i=At(),r=i.triage;return i.triageError?s.jsx("div",{className:"muted",children:i.triageError}):r?s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"data-group",children:[s.jsxs("b",{children:["到期自动化(",((m=r.counts)==null?void 0:m.automations_due)||0,")"]}),s.jsx(Dl,{empty:"(无)",items:(r.automations_due||[]).map((N,E)=>s.jsxs("span",{className:"row-main",children:[N.name,"|每 ",N.interval_seconds," 秒"]},E))})]}),s.jsxs("div",{className:"data-group",children:[s.jsxs("b",{children:["失败/中断任务(",((o=r.counts)==null?void 0:o.failed_tasks)||0,")"]}),s.jsx(Dl,{empty:"(无)",items:(r.failed_tasks||[]).map((N,E)=>s.jsxs("span",{className:"row-main",children:[Oe(N.id),"|",N.status,"|",String(N.prompt||"").slice(0,80)]},E))})]}),s.jsxs("div",{className:"data-group",children:[s.jsxs("b",{children:["待处理后续消息(",((_=r.counts)==null?void 0:_.pending_followups)||0,")"]}),s.jsx(Dl,{empty:"(无)",items:(r.pending_followups||[]).map((N,E)=>s.jsxs("span",{className:"row-main",children:["任务 ",Oe(N.task_id),"|",String(N.body||"").slice(0,80)]},E))})]}),s.jsxs("div",{className:"data-group",children:[s.jsxs("b",{children:["最近自动化产出(",((O=r.counts)==null?void 0:O.recent_runs)||0,")"]}),s.jsx(Dl,{empty:"(无)",items:(r.recent_runs||[]).map((N,E)=>s.jsxs("span",{className:"row-main",children:[Oe(N.automation_id),"|",N.status,N.task_id?"|任务 "+Oe(N.task_id):"",N.task_status?"|"+N.task_status:""]},E))})]})]}):s.jsx("div",{className:"muted",children:"(先选项目)"})}function Ag(){return s.jsx(Ze,{id:"nav-automations",title:"自动化",children:s.jsx(Ng,{})})}function Og(){const i=At();return s.jsxs(Ze,{id:"nav-triage",title:"分诊收件箱",children:[s.jsx(Yt,{children:s.jsx("button",{onClick:i.refreshTriage,children:"刷新分诊"})}),s.jsx(Cg,{})]})}const Mg=[["#sec-dispatch","派发"],["#sec-tasks","任务"],["#sec-conv","对话"],["#sec-projects","项目"],["#sec-env","环境"],["#sec-workers","Workers"],["#sec-skills","技能"],["#sec-automations","自动化"],["#sec-triage","分诊"],["#sec-admin","管理区"],["#sec-key","API Key"]];function zg(){return s.jsxs("header",{className:"dash-topbar",children:[s.jsxs("div",{className:"dash-topbar-row",children:[s.jsx("span",{className:"dash-brand",children:"Codex 命令台"}),s.jsx(Rg,{}),s.jsx("span",{className:"muted dash-hide-sm",children:s.jsx(Dg,{})}),s.jsx("span",{className:"muted dash-hide-sm",children:s.jsx(Ug,{})}),s.jsx("span",{className:"muted dash-stamp",children:s.jsx(Hg,{})})]}),s.jsx("nav",{className:"dash-nav","aria-label":"页面导航",children:Mg.map(([i,r])=>s.jsx("a",{href:i,children:r},i))})]})}function Rg(){const i=At();return s.jsx("span",{className:i.keySaved?"dash-status ok":"dash-status off",children:i.keySaved?"● 已连接":"○ 未连接"})}function Dg(){return s.jsx(s.Fragment,{children:At().healthText})}function Ug(){return s.jsx(s.Fragment,{children:At().quotaText})}function Hg(){return s.jsxs(s.Fragment,{children:["自动刷新 · 每 10 秒 · 已刷新 ",At().refreshStamp]})}function Bg(){const i=Iy();Fy(m=>i.refreshAll(m),1e4),Wy(()=>i.refreshSlow(),6e4);const r=()=>{var m;(m=document.getElementById("sec-dispatch"))==null||m.scrollIntoView({behavior:"smooth"})};return s.jsxs(Yh.Provider,{value:i,children:[s.jsx(zg,{}),s.jsxs("main",{className:"dash-page",children:[s.jsx(Py,{}),s.jsx("section",{id:"sec-dispatch",className:"dash-section",children:s.jsx(tg,{})}),s.jsxs("section",{id:"sec-tasks",className:"dash-section card",children:[s.jsx("h2",{children:"任务列表"}),s.jsx(eg,{onNew:r})]}),s.jsxs("section",{id:"sec-conv",className:"dash-section",children:[s.jsxs("div",{className:"card",children:[s.jsx("h2",{children:"对话"}),s.jsx(cg,{})]}),s.jsx(vg,{})]}),s.jsxs("section",{id:"sec-projects",className:"dash-section",children:[s.jsx(Sg,{}),s.jsx(pg,{})]}),s.jsxs("section",{id:"sec-env",className:"dash-section",children:[s.jsx(bg,{}),s.jsx(Tg,{})]}),s.jsxs("section",{id:"sec-workers",className:"dash-section",children:[s.jsx(jg,{}),s.jsx(xg,{})]}),s.jsxs("section",{id:"sec-skills",className:"dash-section",children:[s.jsx(Eg,{}),s.jsx(_g,{})]}),s.jsx("section",{id:"sec-automations",className:"dash-section",children:s.jsx(Ag,{})}),s.jsx("section",{id:"sec-triage",className:"dash-section",children:s.jsx(Og,{})}),s.jsx("section",{id:"sec-admin",className:"dash-section",children:s.jsx(gg,{})}),s.jsx("section",{id:"sec-key",className:"dash-section",children:s.jsx(yg,{})})]})]})}qy.createRoot(document.getElementById("root")).render(s.jsx(Oy.StrictMode,{children:s.jsx(Bg,{})}));
+