chat: 持久化继续消息,chat-history 交替历史
基线 main 811912c(PR #125 Muse 风格之后):服务端与单测与 9538825 相同;React 侧在 Muse 风格 ContinuePanel 上重放 chat-history 集成(新版输入框占位文案保持 Muse 风格的“消息”), console.html 由新版 src 重新构建。 - 新表 task_continue_messages:POST /continue 的用户消息与状态改回 queued 同一事务落盘,409 被拒不留 phantom 消息 - GET /tasks/{id}/chat-history:prompt + 继续 + 后续消息 + 链路按 时间交错拼好返回;部署前继续以 legacy_continue_count 如实标注 - React ContinuePanel 优先用 chat-history,旧服务端自动回退
This commit is contained in:
1 parent
811912c841
commit
f02c513729
9 files changed
+524
-40
No files matched your search
@@ -38,6 +38,7 @@ export const paths = {
|
||||
taskApprovals: (id: string) => "/tasks/" + enc(id) + "/approvals",
|
||||
taskApprovalDecision: (id: string, aid: string, verb: string) => "/tasks/" + enc(id) + "/approvals/" + enc(aid) + "/" + verb,
|
||||
taskFollowups: (id: string) => "/tasks/" + enc(id) + "/followups",
|
||||
taskChatHistory: (id: string) => "/tasks/" + enc(id) + "/chat-history",
|
||||
taskMessages: (id: string) => "/tasks/" + enc(id) + "/messages",
|
||||
taskMessagesTo: (target: string) => "/tasks/" + enc(target) + "/messages",
|
||||
taskThread: (id: string) => "/tasks/" + enc(id) + "/thread",
|
||||
|
||||
@@ -17,6 +17,13 @@ export interface Task {
|
||||
export interface Review { id: string; target_type?: string; branch?: string; sha?: string; instructions?: string; status?: string; created_at?: number; findings?: string }
|
||||
export interface Approval { id: string; kind?: string; status?: string; summary?: string; decision?: string; decided_by?: string; decided_at?: number }
|
||||
export interface Followup { mode?: string; status?: string; sender_task_id?: string; body?: string }
|
||||
// Chat history from GET /tasks/:id/chat-history: server-merges the task
|
||||
// prompt, persisted continue messages, followups and trace entries in
|
||||
// timestamp order (see server _task_chat_history). ts_ms is null for
|
||||
// trace entries that carried no ts; the server order is authoritative,
|
||||
// the client must not re-sort.
|
||||
export interface ChatHistoryMessage { id: string; role: "user" | "assistant" | "tool" | "status"; source?: string; label?: string; text: string; ts_ms?: number | null }
|
||||
export interface ChatHistory { task_id?: string; status?: string; messages: ChatHistoryMessage[]; continue_messages_stored?: number; legacy_continue_count?: number | null; trace_note?: string }
|
||||
export interface PeerMessage { task_id?: string; mode?: string; status?: string; body?: string }
|
||||
export interface Automation { id: string; name?: string; status?: string; due?: boolean; interval_seconds?: number; next_run_at?: number }
|
||||
export interface Member { user_id: string; username?: string; role?: string }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createContext, useContext } from "react";
|
||||
import type { Automation, Env, Member, Project, Review, Approval, Followup, PeerMessage, Task, ThreadInfo, Triage, Worker, SkillItem, McpServer, Agent, DispatchBody, ReviewBody, TraceEntry } from "./api/types";
|
||||
import type { Automation, Env, Member, Project, Review, Approval, Followup, PeerMessage, Task, ThreadInfo, Triage, Worker, SkillItem, McpServer, Agent, DispatchBody, ReviewBody, TraceEntry, ChatHistory } from "./api/types";
|
||||
|
||||
export interface ConsoleCtx {
|
||||
keySaved: boolean; keyState: string;
|
||||
@@ -30,6 +30,7 @@ export interface ConsoleCtx {
|
||||
reviews: Review[]; reviewMsg: string; reviewError: string; refreshReviews: () => void; postReview: (body: ReviewBody) => Promise<void>; submitFindings: (id: string) => void;
|
||||
traceEntries: TraceEntry[]; traceNote: string; traceDone: string; traceKind: string; setTraceKind: (v: string) => void; downloadTrace: () => void; traceMsg: string;
|
||||
sendChatMessage: (text: string) => Promise<void>; chatMsg: string;
|
||||
chatHistory: ChatHistory | null;
|
||||
followups: Followup[];
|
||||
approvals: Approval[]; approvalMsg: string; refreshApprovals: () => void; decideApproval: (id: string, decision: string) => void;
|
||||
peerMessages: PeerMessage[]; peerMsg: string; postPeer: (mode: string, target: string, text: string) => Promise<void>;
|
||||
|
||||
@@ -126,6 +126,7 @@ export function useConsoleStore(): ConsoleCtx {
|
||||
traceEntries: detail.traceEntries, traceNote: detail.traceNote, traceDone: detail.traceDone,
|
||||
traceKind: detail.traceKind, setTraceKind: detail.setTraceKind, downloadTrace: detail.downloadTrace, traceMsg: detail.traceMsg,
|
||||
sendChatMessage: detail.sendChatMessage, chatMsg: detail.chatMsg,
|
||||
chatHistory: detail.chatHistory,
|
||||
followups: detail.followups,
|
||||
approvals: detail.approvals, approvalMsg: detail.approvalMsg, refreshApprovals: detail.refreshApprovals, decideApproval: detail.decideApproval,
|
||||
peerMessages: detail.peerMessages, peerMsg: detail.peerMsg, postPeer: detail.postPeer,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { api, getKey } from "../api/client";
|
||||
import { paths } from "../api/paths";
|
||||
import { TRACE_TERMINAL } from "../api/types";
|
||||
import type { Agent, Approval, Followup, PeerMessage, Review, Task, ThreadInfo, TraceEntry } from "../api/types";
|
||||
import type { Agent, Approval, ChatHistory, Followup, PeerMessage, Review, Task, ThreadInfo, TraceEntry } from "../api/types";
|
||||
|
||||
// Task detail state: the selected task, its trace SSE/one-shot stream,
|
||||
// and every structured sub-list. Chat sending lives here too:
|
||||
@@ -36,6 +36,7 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
const [threadMsg, setThreadMsg] = useState("");
|
||||
const [threadList, setThreadList] = useState<ThreadInfo[]>([]);
|
||||
const [chatMsg, setChatMsg] = useState("");
|
||||
const [chatHistory, setChatHistory] = useState<ChatHistory | null>(null);
|
||||
const traceAbortRef = useRef<AbortController | null>(null);
|
||||
const agentTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const currentTaskRef = useRef<Task | null>(null);
|
||||
@@ -72,6 +73,15 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
catch { setFollowups([]); }
|
||||
}, []);
|
||||
|
||||
// True alternating history: server-merged prompt + persisted
|
||||
// continues + followups + trace, in timestamp order. On failure
|
||||
// (old server without /chat-history) keep null and ContinuePanel
|
||||
// falls back to its previous prompt + trace + followups view.
|
||||
const loadChatHistory = useCallback(async (taskId: string) => {
|
||||
try { const d = await api<ChatHistory>(paths.taskChatHistory(taskId)); setChatHistory(d); }
|
||||
catch { setChatHistory(null); }
|
||||
}, []);
|
||||
|
||||
const loadReviews = useCallback(async (taskId?: string) => {
|
||||
const id = taskId || currentTaskRef.current?.id;
|
||||
if (!id) return;
|
||||
@@ -226,6 +236,7 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
void startTrace(taskId, t.status);
|
||||
void loadAgents(taskId);
|
||||
void loadCoordChildren(t);
|
||||
void loadChatHistory(taskId);
|
||||
void loadFollowups(taskId);
|
||||
void loadPeerMessages(taskId);
|
||||
void loadThread(taskId);
|
||||
@@ -236,7 +247,7 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
setCurrentTask(null);
|
||||
setDetailBody("加载失败:" + e.message);
|
||||
}
|
||||
}, [stopTrace, startTrace, loadAgents, loadCoordChildren, loadFollowups, loadPeerMessages, loadThread, loadThreads, loadReviews, loadApprovals, getRole]);
|
||||
}, [stopTrace, startTrace, loadAgents, loadCoordChildren, loadChatHistory, loadFollowups, loadPeerMessages, loadThread, loadThreads, loadReviews, loadApprovals, getRole]);
|
||||
|
||||
const refreshDetailLists = useCallback(async () => {
|
||||
const t = currentTaskRef.current;
|
||||
@@ -250,6 +261,7 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
setDetailBody(body || "(暂无 result/error 输出)");
|
||||
} catch { /* light header refresh must not clear the detail */ }
|
||||
void loadFollowups(t.id);
|
||||
void loadChatHistory(t.id);
|
||||
void loadReviews(t.id);
|
||||
void loadApprovals(t.id);
|
||||
void loadCoordChildren(t);
|
||||
@@ -257,7 +269,7 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
void loadPeerMessages(t.id);
|
||||
void loadThread(t.id);
|
||||
void loadThreads(t.id);
|
||||
}, [loadFollowups, loadReviews, loadApprovals, loadCoordChildren, loadAgents, loadPeerMessages, loadThread, loadThreads]);
|
||||
}, [loadFollowups, loadChatHistory, loadReviews, loadApprovals, loadCoordChildren, loadAgents, loadPeerMessages, loadThread, loadThreads]);
|
||||
|
||||
const doAction = useCallback(async (kind: string) => {
|
||||
const t = currentTaskRef.current;
|
||||
@@ -370,11 +382,12 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
await api(paths.taskFollowups(t.id), "POST", { body: v, mode: "direct" });
|
||||
setChatMsg("已直接发送");
|
||||
await loadFollowups(t.id);
|
||||
await loadChatHistory(t.id);
|
||||
} else {
|
||||
setChatMsg("merged/merge-failed 任务不可继续,也不可发后续消息");
|
||||
}
|
||||
} catch (e: any) { setChatMsg("发送失败:" + e.message); }
|
||||
}, [showDetail, onTasksReload, loadFollowups]);
|
||||
}, [showDetail, onTasksReload, loadFollowups, loadChatHistory]);
|
||||
|
||||
const downloadTrace = useCallback(async () => {
|
||||
const t = currentTaskRef.current;
|
||||
@@ -399,7 +412,7 @@ export function useTaskDetail(getRole: (pid?: string) => string | null, onTasksR
|
||||
currentTask, detailBody, actionMsg, actionReasons, costBox, forkVisible, setForkVisible, forkMsg,
|
||||
coordChildren, agents, agentsNote, reviews, reviewMsg, reviewError, traceEntries, traceNote, traceDone,
|
||||
traceKind, setTraceKind, traceMsg, followups, approvals, approvalMsg, peerMessages, peerMsg,
|
||||
threadText, threadGateNote, threadMsg, threadList, chatMsg,
|
||||
threadText, threadGateNote, threadMsg, threadList, chatMsg, chatHistory,
|
||||
showDetail, refreshDetailLists, doAction, taskCost, forkTask, postReview, submitFindings,
|
||||
decideApproval, postPeer, threadAction, sendChatMessage, downloadTrace,
|
||||
refreshReviews: () => void loadReviews(), refreshApprovals: () => void loadApprovals(),
|
||||
|
||||
@@ -5,25 +5,33 @@ import { traceEntryToChatMessage, type ChatMessage } from "../../components/chat
|
||||
import { TRACE_TERMINAL } from "../../api/types";
|
||||
|
||||
// Continue chat: same ChatList/ChatBubble/ChatInput as the trace timeline.
|
||||
// History is the task prompt (user) + message-kind trace entries
|
||||
// (assistant) + followups already sent (user). Live followups and trace
|
||||
// entries carry no timestamps, so they cannot be interleaved exactly:
|
||||
// followups are appended after trace messages and this is stated here
|
||||
// and in the final report, not hidden by a fabricated order.
|
||||
// Sending uses only live APIs: terminal tasks -> POST /tasks/:id/continue,
|
||||
// queued/running tasks -> POST /tasks/:id/followups (direct). No new API.
|
||||
// Preferred source is GET /chat-history (server-merged true alternating
|
||||
// history: prompt + persisted continues + followups + trace, in
|
||||
// timestamp order). Only when that endpoint is unavailable (old server)
|
||||
// fall back to the previous view: prompt + message-kind trace entries
|
||||
// + followups appended after — that fallback cannot interleave exactly
|
||||
// and cannot show pre-persistence continues, and it says so nowhere
|
||||
// else, so the fallback path is kept only as a compatibility shim.
|
||||
// Sending uses only live APIs: terminal tasks -> POST /tasks/:id/continue
|
||||
// (the server now persists that message in the same transaction),
|
||||
// queued/running tasks -> POST /tasks/:id/followups (direct).
|
||||
export default function ContinuePanel() {
|
||||
const ctx = useConsole();
|
||||
const t = ctx.currentTask;
|
||||
const messages = useMemo<ChatMessage[]>(() => {
|
||||
if (!t) return [];
|
||||
if (ctx.chatHistory) {
|
||||
return ctx.chatHistory.messages.map((m) => ({
|
||||
id: m.id, role: m.role, text: m.text, label: m.label,
|
||||
}));
|
||||
}
|
||||
const head: ChatMessage[] = t.prompt ? [{ id: "task-prompt", role: "user", text: t.prompt }] : [];
|
||||
const assistant = ctx.traceEntries.filter((e) => e.kind === "message").map((e, i) => traceEntryToChatMessage(e, i));
|
||||
const followups: ChatMessage[] = ctx.followups.map((f, i) => ({
|
||||
id: "followup-" + i, role: "user" as const, text: String(f.body || ""), label: "后续消息",
|
||||
}));
|
||||
return [...head, ...assistant, ...followups];
|
||||
}, [t, ctx.traceEntries, ctx.followups]);
|
||||
}, [t, ctx.chatHistory, ctx.traceEntries, ctx.followups]);
|
||||
if (!t) return <div className="muted">点任务行查看详情后,这里显示继续会话</div>;
|
||||
const terminal = TRACE_TERMINAL.includes(t.status);
|
||||
const running = ["queued", "running"].includes(t.status);
|
||||
@@ -33,8 +41,15 @@ export default function ContinuePanel() {
|
||||
: running
|
||||
? "运行中/排队中:发送走后续消息直接发送(跑完当前轮后优先执行)"
|
||||
: "merged/merge-failed 任务不可继续,也不可发后续消息";
|
||||
const legacy = ctx.chatHistory ? (ctx.chatHistory.legacy_continue_count ?? null) : null;
|
||||
return (
|
||||
<div className="chat-column">
|
||||
{legacy !== null && legacy > 0 ? (
|
||||
<div className="muted">此前有 {legacy} 条继续消息发生在持久化之前,未入库,上面历史不完整;部署后新发的继续消息会正常显示。</div>
|
||||
) : null}
|
||||
{ctx.chatHistory && ctx.chatHistory.trace_note ? (
|
||||
<div className="muted">{ctx.chatHistory.trace_note}</div>
|
||||
) : null}
|
||||
<ChatList messages={messages} empty="(暂无会话消息)" />
|
||||
<ChatInput
|
||||
disabled={!canSend}
|
||||
|
||||
+25
-25
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Codex 命令台</title>
|
||||
<script type="module" crossorigin>(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const _ of document.querySelectorAll('link[rel="modulepreload"]'))o(_);new MutationObserver(_=>{for(const M of _)if(M.type==="childList")for(const N of M.addedNodes)N.tagName==="LINK"&&N.rel==="modulepreload"&&o(N)}).observe(document,{childList:!0,subtree:!0});function m(_){const M={};return _.integrity&&(M.integrity=_.integrity),_.referrerPolicy&&(M.referrerPolicy=_.referrerPolicy),_.crossOrigin==="use-credentials"?M.credentials="include":_.crossOrigin==="anonymous"?M.credentials="omit":M.credentials="same-origin",M}function o(_){if(_.ep)return;_.ep=!0;const M=m(_);fetch(_.href,M)}})();function qh(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var tr={exports:{}},Pu={};/**
|
||||
<script type="module" crossorigin>(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const _ of document.querySelectorAll('link[rel="modulepreload"]'))o(_);new MutationObserver(_=>{for(const M of _)if(M.type==="childList")for(const N of M.addedNodes)N.tagName==="LINK"&&N.rel==="modulepreload"&&o(N)}).observe(document,{childList:!0,subtree:!0});function m(_){const M={};return _.integrity&&(M.integrity=_.integrity),_.referrerPolicy&&(M.referrerPolicy=_.referrerPolicy),_.crossOrigin==="use-credentials"?M.credentials="include":_.crossOrigin==="anonymous"?M.credentials="omit":M.credentials="same-origin",M}function o(_){if(_.ep)return;_.ep=!0;const M=m(_);fetch(_.href,M)}})();function qh(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var er={exports:{}},li={};/**
|
||||
* @license React
|
||||
* react-jsx-runtime.production.js
|
||||
*
|
||||
@@ -12,7 +12,7 @@
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/var Nh;function Ay(){if(Nh)return Pu;Nh=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function m(o,_,M){var N=null;if(M!==void 0&&(N=""+M),_.key!==void 0&&(N=""+_.key),"key"in _){M={};for(var j in _)j!=="key"&&(M[j]=_[j])}else M=_;return _=M.ref,{$$typeof:i,type:o,key:N,ref:_!==void 0?_:null,props:M}}return Pu.Fragment=r,Pu.jsx=m,Pu.jsxs=m,Pu}var Ch;function My(){return Ch||(Ch=1,tr.exports=Ay()),tr.exports}var s=My(),er={exports:{}},ct={};/**
|
||||
*/var Nh;function Ay(){if(Nh)return li;Nh=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function m(o,_,M){var N=null;if(M!==void 0&&(N=""+M),_.key!==void 0&&(N=""+_.key),"key"in _){M={};for(var E in _)E!=="key"&&(M[E]=_[E])}else M=_;return _=M.ref,{$$typeof:i,type:o,key:N,ref:_!==void 0?_:null,props:M}}return li.Fragment=r,li.jsx=m,li.jsxs=m,li}var Ch;function My(){return Ch||(Ch=1,er.exports=Ay()),er.exports}var s=My(),lr={exports:{}},ct={};/**
|
||||
* @license React
|
||||
* react.production.js
|
||||
*
|
||||
@@ -20,7 +20,7 @@
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/var Ah;function Oy(){if(Ah)return ct;Ah=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),m=Symbol.for("react.fragment"),o=Symbol.for("react.strict_mode"),_=Symbol.for("react.profiler"),M=Symbol.for("react.consumer"),N=Symbol.for("react.context"),j=Symbol.for("react.forward_ref"),B=Symbol.for("react.suspense"),R=Symbol.for("react.memo"),G=Symbol.for("react.lazy"),E=Symbol.for("react.activity"),J=Symbol.for("react.view_transition"),ut=Symbol.iterator;function k(v){return v===null||typeof v!="object"?null:(v=ut&&v[ut]||v["@@iterator"],typeof v=="function"?v:null)}var P={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},it=Object.assign,Tt={};function Vt(v,D,F){this.props=v,this.context=D,this.refs=Tt,this.updater=F||P}Vt.prototype.isReactComponent={},Vt.prototype.setState=function(v,D){if(typeof v!="object"&&typeof v!="function"&&v!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,v,D,"setState")},Vt.prototype.forceUpdate=function(v){this.updater.enqueueForceUpdate(this,v,"forceUpdate")};function be(){}be.prototype=Vt.prototype;function Me(v,D,F){this.props=v,this.context=D,this.refs=Tt,this.updater=F||P}var Ft=Me.prototype=new be;Ft.constructor=Me,it(Ft,Vt.prototype),Ft.isPureReactComponent=!0;var oe=Array.isArray;function st(){}var ht={H:null,A:null,T:null,S:null},Wt=Object.prototype.hasOwnProperty;function te(v,D,F){var K=F.ref;return{$$typeof:i,type:v,key:D,ref:K!==void 0?K:null,props:F}}function Lt(v,D){return te(v.type,D,v.props)}function de(v){return typeof v=="object"&&v!==null&&v.$$typeof===i}function Dt(v){var D={"=":"=0",":":"=2"};return"$"+v.replace(/[=:]/g,function(F){return D[F]})}var Pe=/\/+/g;function H(v,D){return typeof v=="object"&&v!==null&&v.key!=null?Dt(""+v.key):D.toString(36)}function w(v){switch(v.status){case"fulfilled":return v.value;case"rejected":throw v.reason;default:switch(typeof v.status=="string"?v.then(st,st):(v.status="pending",v.then(function(D){v.status==="pending"&&(v.status="fulfilled",v.value=D)},function(D){v.status==="pending"&&(v.status="rejected",v.reason=D)})),v.status){case"fulfilled":return v.value;case"rejected":throw v.reason}}throw v}function L(v,D,F,K,yt){var dt=typeof v;(dt==="undefined"||dt==="boolean")&&(v=null);var xt=!1;if(v===null)xt=!0;else switch(dt){case"bigint":case"string":case"number":xt=!0;break;case"object":switch(v.$$typeof){case i:case r:xt=!0;break;case G:return xt=v._init,L(xt(v._payload),D,F,K,yt)}}if(xt)return yt=yt(v),xt=K===""?"."+H(v,0):K,oe(yt)?(F="",xt!=null&&(F=xt.replace(Pe,"$&/")+"/"),L(yt,D,F,"",function(xe){return xe})):yt!=null&&(de(yt)&&(yt=Lt(yt,F+(yt.key==null||v&&v.key===yt.key?"":(""+yt.key).replace(Pe,"$&/")+"/")+xt)),D.push(yt)),1;xt=0;var Q=K===""?".":K+":";if(oe(v))for(var at=0;at<v.length;at++)K=v[at],dt=Q+H(K,at),xt+=L(K,D,F,dt,yt);else if(at=k(v),typeof at=="function")for(v=at.call(v),at=0;!(K=v.next()).done;)K=K.value,dt=Q+H(K,at++),xt+=L(K,D,F,dt,yt);else if(dt==="object"){if(typeof v.then=="function")return L(w(v),D,F,K,yt);throw D=String(v),Error("Objects are not valid as a React child (found: "+(D==="[object Object]"?"object with keys {"+Object.keys(v).join(", ")+"}":D)+"). If you meant to render a collection of children, use an array instead.")}return xt}function tt(v,D,F){if(v==null)return v;var K=[],yt=0;return L(v,K,"","",function(dt){return D.call(F,dt,yt++)}),K}function jt(v){if(v._status===-1){var D=v._result,F=D();F.then(function(K){(v._status===0||v._status===-1)&&(v._status=1,v._result=K,F.status===void 0&&(F.status="fulfilled",F.value=K))},function(K){(v._status===0||v._status===-1)&&(v._status=2,v._result=K,F.status===void 0&&(F.status="rejected",F.reason=K))}),v._status===-1&&(v._status=0,v._result=F)}if(v._status===1)return v._result.default;throw v._result}var vt=typeof reportError=="function"?reportError:function(v){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var D=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof v=="object"&&v!==null&&typeof v.message=="string"?String(v.message):String(v),error:v});if(!window.dispatchEvent(D))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",v);return}console.error(v)};function ee(v){var D=ht.T,F={};F.types=D!==null?D.types:null,ht.T=F;try{var K=v(),yt=ht.S;yt!==null&&yt(F,K),typeof K=="object"&&K!==null&&typeof K.then=="function"&&K.then(st,vt)}catch(dt){vt(dt)}finally{D!==null&&F.types!==null&&(D.types=F.types),ht.T=D}}function Oe(v){var D=ht.T;if(D!==null){var F=D.types;F===null?D.types=[v]:F.indexOf(v)===-1&&F.push(v)}else ee(Oe.bind(null,v))}var ue={map:tt,forEach:function(v,D,F){tt(v,function(){D.apply(this,arguments)},F)},count:function(v){var D=0;return tt(v,function(){D++}),D},toArray:function(v){return tt(v,function(D){return D})||[]},only:Line truncated
|
||||
*/var Ah;function Oy(){if(Ah)return ct;Ah=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),m=Symbol.for("react.fragment"),o=Symbol.for("react.strict_mode"),_=Symbol.for("react.profiler"),M=Symbol.for("react.consumer"),N=Symbol.for("react.context"),E=Symbol.for("react.forward_ref"),B=Symbol.for("react.suspense"),z=Symbol.for("react.memo"),G=Symbol.for("react.lazy"),j=Symbol.for("react.activity"),V=Symbol.for("react.view_transition"),ut=Symbol.iterator;function K(v){return v===null||typeof v!="object"?null:(v=ut&&v[ut]||v["@@iterator"],typeof v=="function"?v:null)}var tt={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},it=Object.assign,jt={};function Xt(v,R,W){this.props=v,this.context=R,this.refs=jt,this.updater=W||tt}Xt.prototype.isReactComponent={},Xt.prototype.setState=function(v,R){if(typeof v!="object"&&typeof v!="function"&&v!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,v,R,"setState")},Xt.prototype.forceUpdate=function(v){this.updater.enqueueForceUpdate(this,v,"forceUpdate")};function ge(){}ge.prototype=Xt.prototype;function Oe(v,R,W){this.props=v,this.context=R,this.refs=jt,this.updater=W||tt}var Jt=Oe.prototype=new ge;Jt.constructor=Oe,it(Jt,Xt.prototype),Jt.isPureReactComponent=!0;var fe=Array.isArray;function st(){}var ht={H:null,A:null,T:null,S:null},Ft=Object.prototype.hasOwnProperty;function Pt(v,R,W){var J=W.ref;return{$$typeof:i,type:v,key:R,ref:J!==void 0?J:null,props:W}}function kt(v,R){return Pt(v.type,R,v.props)}function re(v){return typeof v=="object"&&v!==null&&v.$$typeof===i}function Dt(v){var R={"=":"=0",":":"=2"};return"$"+v.replace(/[=:]/g,function(W){return R[W]})}var tl=/\/+/g;function U(v,R){return typeof v=="object"&&v!==null&&v.key!=null?Dt(""+v.key):R.toString(36)}function w(v){switch(v.status){case"fulfilled":return v.value;case"rejected":throw v.reason;default:switch(typeof v.status=="string"?v.then(st,st):(v.status="pending",v.then(function(R){v.status==="pending"&&(v.status="fulfilled",v.value=R)},function(R){v.status==="pending"&&(v.status="rejected",v.reason=R)})),v.status){case"fulfilled":return v.value;case"rejected":throw v.reason}}throw v}function k(v,R,W,J,yt){var dt=typeof v;(dt==="undefined"||dt==="boolean")&&(v=null);var xt=!1;if(v===null)xt=!0;else switch(dt){case"bigint":case"string":case"number":xt=!0;break;case"object":switch(v.$$typeof){case i:case r:xt=!0;break;case G:return xt=v._init,k(xt(v._payload),R,W,J,yt)}}if(xt)return yt=yt(v),xt=J===""?"."+U(v,0):J,fe(yt)?(W="",xt!=null&&(W=xt.replace(tl,"$&/")+"/"),k(yt,R,W,"",function(Se){return Se})):yt!=null&&(re(yt)&&(yt=kt(yt,W+(yt.key==null||v&&v.key===yt.key?"":(""+yt.key).replace(tl,"$&/")+"/")+xt)),R.push(yt)),1;xt=0;var Q=J===""?".":J+":";if(fe(v))for(var at=0;at<v.length;at++)J=v[at],dt=Q+U(J,at),xt+=k(J,R,W,dt,yt);else if(at=K(v),typeof at=="function")for(v=at.call(v),at=0;!(J=v.next()).done;)J=J.value,dt=Q+U(J,at++),xt+=k(J,R,W,dt,yt);else if(dt==="object"){if(typeof v.then=="function")return k(w(v),R,W,J,yt);throw R=String(v),Error("Objects are not valid as a React child (found: "+(R==="[object Object]"?"object with keys {"+Object.keys(v).join(", ")+"}":R)+"). If you meant to render a collection of children, use an array instead.")}return xt}function et(v,R,W){if(v==null)return v;var J=[],yt=0;return k(v,J,"","",function(dt){return R.call(W,dt,yt++)}),J}function Tt(v){if(v._status===-1){var R=v._result,W=R();W.then(function(J){(v._status===0||v._status===-1)&&(v._status=1,v._result=J,W.status===void 0&&(W.status="fulfilled",W.value=J))},function(J){(v._status===0||v._status===-1)&&(v._status=2,v._result=J,W.status===void 0&&(W.status="rejected",W.reason=J))}),v._status===-1&&(v._status=0,v._result=W)}if(v._status===1)return v._result.default;throw v._result}var vt=typeof reportError=="function"?reportError:function(v){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var R=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof v=="object"&&v!==null&&typeof v.message=="string"?String(v.message):String(v),error:v});if(!window.dispatchEvent(R))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",v);return}console.error(v)};function te(v){var R=ht.T,W={};W.types=R!==null?R.types:null,ht.T=W;try{var J=v(),yt=ht.S;yt!==null&&yt(W,J),typeof J=="object"&&J!==null&&typeof J.then=="function"&&J.then(st,vt)}catch(dt){vt(dt)}finally{R!==null&&W.types!==null&&(R.types=W.types),ht.T=R}}function ze(v){var R=ht.T;if(R!==null){var W=R.types;W===null?R.types=[v]:W.indexOf(v)===-1&&W.push(v)}else te(ze.bind(null,v))}var ne={map:et,forEach:function(v,R,W){et(v,function(){R.apply(this,arguments)},W)},count:function(v){var R=0;return et(v,function(){R++}),R},toArray:function(v){return et(v,function(R){return R})||[]},onLine truncated
|
||||
* @license React
|
||||
* scheduler.production.js
|
||||
*
|
||||
@@ -28,7 +28,7 @@
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/var Oh;function Ry(){return Oh||(Oh=1,(function(i){function r(w,L){var tt=w.length;w.push(L);t:for(;0<tt;){var jt=tt-1>>>1,vt=w[jt];if(0<_(vt,L))w[jt]=L,w[tt]=vt,tt=jt;else break t}}function m(w){return w.length===0?null:w[0]}function o(w){if(w.length===0)return null;var L=w[0],tt=w.pop();if(tt!==L){w[0]=tt;t:for(var jt=0,vt=w.length,ee=vt>>>1;jt<ee;){var Oe=2*(jt+1)-1,ue=w[Oe],v=Oe+1,D=w[v];if(0>_(ue,tt))v<vt&&0>_(D,ue)?(w[jt]=D,w[v]=tt,jt=v):(w[jt]=ue,w[Oe]=tt,jt=Oe);else if(v<vt&&0>_(D,tt))w[jt]=D,w[v]=tt,jt=v;else break t}}return L}function _(w,L){var tt=w.sortIndex-L.sortIndex;return tt!==0?tt:w.id-L.id}if(i.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var M=performance;i.unstable_now=function(){return M.now()}}else{var N=Date,j=N.now();i.unstable_now=function(){return N.now()-j}}var B=[],R=[],G=1,E=null,J=3,ut=!1,k=!1,P=!1,it=!1,Tt=typeof setTimeout=="function"?setTimeout:null,Vt=typeof clearTimeout=="function"?clearTimeout:null,be=typeof setImmediate<"u"?setImmediate:null;function Me(w){for(var L=m(R);L!==null;){if(L.callback===null)o(R);else if(L.startTime<=w)o(R),L.sortIndex=L.expirationTime,r(B,L);else break;L=m(R)}}function Ft(w){if(P=!1,Me(w),!k)if(m(B)!==null)k=!0,oe||(oe=!0,de());else{var L=m(R);L!==null&&H(Ft,L.startTime-w)}}var oe=!1,st=-1,ht=5,Wt=-1;function te(){return it?!0:!(i.unstable_now()-Wt<ht)}function Lt(){if(it=!1,oe){var w=i.unstable_now();Wt=w;var L=!0;try{t:{k=!1,P&&(P=!1,Vt(st),st=-1),ut=!0;var tt=J;try{e:{for(Me(w),E=m(B);E!==null&&!(E.expirationTime>w&&te());){var jt=E.callback;if(typeof jt=="function"){E.callback=null,J=E.priorityLevel;var vt=jt(E.expirationTime<=w);if(w=i.unstable_now(),typeof vt=="function"){E.callback=vt,Me(w),L=!0;break e}E===m(B)&&o(B),Me(w)}else o(B);E=m(B)}if(E!==null)L=!0;else{var ee=m(R);ee!==null&&H(Ft,ee.startTime-w),L=!1}}break t}finally{E=null,J=tt,ut=!1}L=void 0}}finally{L?de():oe=!1}}}var de;if(typeof be=="function")de=function(){be(Lt)};else if(typeof MessageChannel<"u"){var Dt=new MessageChannel,Pe=Dt.port2;Dt.port1.onmessage=Lt,de=function(){Pe.postMessage(null)}}else de=function(){Tt(Lt,0)};function H(w,L){st=Tt(function(){w(i.unstable_now())},L)}i.unstable_IdlePriority=5,i.unstable_ImmediatePriority=1,i.unstable_LowPriority=4,i.unstable_NormalPriority=3,i.unstable_Profiling=null,i.unstable_UserBlockingPriority=2,i.unstable_cancelCallback=function(w){w.callback=null},i.unstable_forceFrameRate=function(w){0>w||125<w?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):ht=0<w?Math.floor(1e3/w):5},i.unstable_getCurrentPriorityLevel=function(){return J},i.unstable_next=function(w){switch(J){case 1:case 2:case 3:var L=3;break;default:L=J}var tt=J;J=L;try{return w()}finally{J=tt}},i.unstable_requestPaint=function(){it=!0},i.unstable_runWithPriority=function(w,L){switch(w){case 1:case 2:case 3:case 4:case 5:break;default:w=3}var tt=J;J=w;try{return L()}finally{J=tt}},i.unstable_scheduleCallback=function(w,L,tt){var jt=i.unstable_now();switch(typeof tt=="object"&&tt!==null?(tt=tt.delay,tt=typeof tt=="number"&&0<tt?jt+tt:jt):tt=jt,w){case 1:var vt=-1;break;case 2:vt=250;break;case 5:vt=1073741823;break;case 4:vt=1e4;break;default:vt=5e3}return vt=tt+vt,w={id:G++,callback:L,priorityLevel:w,startTime:tt,expirationTime:vt,sortIndex:-1},tt>jt?(w.sortIndex=tt,r(R,w),m(B)===null&&w===m(R)&&(P?(Vt(st),st=-1):P=!0,H(Ft,tt-jt))):(w.sortIndex=vt,r(B,w),k||ut||(k=!0,oe||(oe=!0,de()))),w},i.unstable_shouldYield=te,i.unstable_wrapCallback=function(w){var L=J;return function(){var tt=J;J=L;try{return w.apply(this,arguments)}finally{J=tt}}}})(nr)),nr}var zh;function Dy(){return zh||(zh=1,ar.exports=Ry()),ar.exports}var ur={exports:{}},Ae={};/**
|
||||
*/var Oh;function Ry(){return Oh||(Oh=1,(function(i){function r(w,k){var et=w.length;w.push(k);t:for(;0<et;){var Tt=et-1>>>1,vt=w[Tt];if(0<_(vt,k))w[Tt]=k,w[et]=vt,et=Tt;else break t}}function m(w){return w.length===0?null:w[0]}function o(w){if(w.length===0)return null;var k=w[0],et=w.pop();if(et!==k){w[0]=et;t:for(var Tt=0,vt=w.length,te=vt>>>1;Tt<te;){var ze=2*(Tt+1)-1,ne=w[ze],v=ze+1,R=w[v];if(0>_(ne,et))v<vt&&0>_(R,ne)?(w[Tt]=R,w[v]=et,Tt=v):(w[Tt]=ne,w[ze]=et,Tt=ze);else if(v<vt&&0>_(R,et))w[Tt]=R,w[v]=et,Tt=v;else break t}}return k}function _(w,k){var et=w.sortIndex-k.sortIndex;return et!==0?et:w.id-k.id}if(i.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var M=performance;i.unstable_now=function(){return M.now()}}else{var N=Date,E=N.now();i.unstable_now=function(){return N.now()-E}}var B=[],z=[],G=1,j=null,V=3,ut=!1,K=!1,tt=!1,it=!1,jt=typeof setTimeout=="function"?setTimeout:null,Xt=typeof clearTimeout=="function"?clearTimeout:null,ge=typeof setImmediate<"u"?setImmediate:null;function Oe(w){for(var k=m(z);k!==null;){if(k.callback===null)o(z);else if(k.startTime<=w)o(z),k.sortIndex=k.expirationTime,r(B,k);else break;k=m(z)}}function Jt(w){if(tt=!1,Oe(w),!K)if(m(B)!==null)K=!0,fe||(fe=!0,re());else{var k=m(z);k!==null&&U(Jt,k.startTime-w)}}var fe=!1,st=-1,ht=5,Ft=-1;function Pt(){return it?!0:!(i.unstable_now()-Ft<ht)}function kt(){if(it=!1,fe){var w=i.unstable_now();Ft=w;var k=!0;try{t:{K=!1,tt&&(tt=!1,Xt(st),st=-1),ut=!0;var et=V;try{e:{for(Oe(w),j=m(B);j!==null&&!(j.expirationTime>w&&Pt());){var Tt=j.callback;if(typeof Tt=="function"){j.callback=null,V=j.priorityLevel;var vt=Tt(j.expirationTime<=w);if(w=i.unstable_now(),typeof vt=="function"){j.callback=vt,Oe(w),k=!0;break e}j===m(B)&&o(B),Oe(w)}else o(B);j=m(B)}if(j!==null)k=!0;else{var te=m(z);te!==null&&U(Jt,te.startTime-w),k=!1}}break t}finally{j=null,V=et,ut=!1}k=void 0}}finally{k?re():fe=!1}}}var re;if(typeof ge=="function")re=function(){ge(kt)};else if(typeof MessageChannel<"u"){var Dt=new MessageChannel,tl=Dt.port2;Dt.port1.onmessage=kt,re=function(){tl.postMessage(null)}}else re=function(){jt(kt,0)};function U(w,k){st=jt(function(){w(i.unstable_now())},k)}i.unstable_IdlePriority=5,i.unstable_ImmediatePriority=1,i.unstable_LowPriority=4,i.unstable_NormalPriority=3,i.unstable_Profiling=null,i.unstable_UserBlockingPriority=2,i.unstable_cancelCallback=function(w){w.callback=null},i.unstable_forceFrameRate=function(w){0>w||125<w?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):ht=0<w?Math.floor(1e3/w):5},i.unstable_getCurrentPriorityLevel=function(){return V},i.unstable_next=function(w){switch(V){case 1:case 2:case 3:var k=3;break;default:k=V}var et=V;V=k;try{return w()}finally{V=et}},i.unstable_requestPaint=function(){it=!0},i.unstable_runWithPriority=function(w,k){switch(w){case 1:case 2:case 3:case 4:case 5:break;default:w=3}var et=V;V=w;try{return k()}finally{V=et}},i.unstable_scheduleCallback=function(w,k,et){var Tt=i.unstable_now();switch(typeof et=="object"&&et!==null?(et=et.delay,et=typeof et=="number"&&0<et?Tt+et:Tt):et=Tt,w){case 1:var vt=-1;break;case 2:vt=250;break;case 5:vt=1073741823;break;case 4:vt=1e4;break;default:vt=5e3}return vt=et+vt,w={id:G++,callback:k,priorityLevel:w,startTime:et,expirationTime:vt,sortIndex:-1},et>Tt?(w.sortIndex=et,r(z,w),m(B)===null&&w===m(z)&&(tt?(Xt(st),st=-1):tt=!0,U(Jt,et-Tt))):(w.sortIndex=vt,r(B,w),K||ut||(K=!0,fe||(fe=!0,re()))),w},i.unstable_shouldYield=Pt,i.unstable_wrapCallback=function(w){var k=V;return function(){var et=V;V=k;try{return w.apply(this,arguments)}finally{V=et}}}})(ur)),ur}var zh;function Dy(){return zh||(zh=1,nr.exports=Ry()),nr.exports}var ir={exports:{}},Me={};/**
|
||||
* @license React
|
||||
* react-dom.production.js
|
||||
*
|
||||
@@ -36,7 +36,7 @@
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/var Rh;function Uy(){if(Rh)return Ae;Rh=1;var i=fr();function r(G){var E="https://react.dev/errors/"+G;if(1<arguments.length){E+="?args[]="+encodeURIComponent(arguments[1]);for(var J=2;J<arguments.length;J++)E+="&args[]="+encodeURIComponent(arguments[J])}return"Minified React error #"+G+"; visit "+E+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function m(){}var o={d:{f:m,r:function(){throw Error(r(522))},D:m,C:m,L:m,m,X:m,S:m,M:m},p:0,findDOMNode:null},_=Symbol.for("react.portal"),M=Symbol.for("react.recoverable"),N=Symbol.for("react.optimistic_key");function j(G,E,J){var ut=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:_,key:ut==null?null:ut===N?N:""+ut,children:G,containerInfo:E,implementation:J}}var B=i.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function R(G,E){if(G==="font")return"";if(typeof E=="string")return E==="use-credentials"?E:""}return Ae.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=o,Ae.browser=function(G){return{$$typeof:M,_reason:G}},Ae.createPortal=function(G,E){var J=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!E||E.nodeType!==1&&E.nodeType!==9&&E.nodeType!==11)throw Error(r(299));return j(G,E,null,J)},Ae.flushSync=function(G){var E=B.T,J=o.p;try{if(B.T=null,o.p=2,G)return G()}finally{B.T=E,o.p=J,o.d.f()}},Ae.preconnect=function(G,E){typeof G=="string"&&(E?(E=E.crossOrigin,E=typeof E=="string"?E==="use-credentials"?E:"":void 0):E=null,o.d.C(G,E))},Ae.prefetchDNS=function(G){typeof G=="string"&&o.d.D(G)},Ae.preinit=function(G,E){if(typeof G=="string"&&E&&typeof E.as=="string"){var J=E.as,ut=R(J,E.crossOrigin),k=typeof E.integrity=="string"?E.integrity:void 0,P=typeof E.fetchPriority=="string"?E.fetchPriority:void 0;J==="style"?o.d.S(G,typeof E.precedence=="string"?E.precedence:void 0,{crossOrigin:ut,integrity:k,fetchPriority:P}):J==="script"&&o.d.X(G,{crossOrigin:ut,integrity:k,fetchPriority:P,nonce:typeof E.nonce=="string"?E.nonce:void 0})}},Ae.preinitModule=function(G,E){if(typeof G=="string")if(typeof E=="object"&&E!==null){if(E.as==null||E.as==="script"){var J=R(E.as,E.crossOrigin);o.d.M(G,{crossOrigin:J,integrity:typeof E.integrity=="string"?E.integrity:void 0,nonce:typeof E.nonce=="string"?E.nonce:void 0,fetchPriority:typeof E.fetchPriority=="string"?E.fetchPriority:void 0})}}else E==null&&o.d.M(G)},Ae.preload=function(G,E){if(typeof G=="string"&&typeof E=="object"&&E!==null&&typeof E.as=="string"){var J=E.as,ut=R(J,E.crossOrigin);o.d.L(G,J,{crossOrigin:ut,integrity:typeof E.integrity=="string"?E.integrity:void 0,nonce:typeof E.nonce=="string"?E.nonce:void 0,type:typeof E.type=="string"?E.type:void 0,fetchPriority:typeof E.fetchPriority=="string"?E.fetchPriority:void 0,referrerPolicy:typeof E.referrerPolicy=="string"?E.referrerPolicy:void 0,imageSrcSet:typeof E.imageSrcSet=="string"?E.imageSrcSet:void 0,imageSizes:typeof E.imageSizes=="string"?E.imageSizes:void 0,media:typeof E.media=="string"?E.media:void 0})}},Ae.preloadModule=function(G,E){if(typeof G=="string")if(E){var J=R(E.as,E.crossOrigin);o.d.m(G,{as:typeof E.as=="string"&&E.as!=="script"?E.as:void 0,crossOrigin:J,integrity:typeof E.integrity=="string"?E.integrity:void 0,nonce:typeof E.nonce=="string"?E.nonce:void 0,fetchPriority:typeof E.fetchPriority=="string"?E.fetchPriority:void 0})}else o.d.m(G)},Ae.requestFormReset=function(G){o.d.r(G)},Ae.unstable_batchedUpdates=function(G,E){return G(E)},Ae.useFormState=function(G,E,J){return B.H.useFormState(G,E,J)},Ae.useFormStatus=function(){return B.H.useHostTransitionStatus()},Ae.version="19.3.1",Ae}var Dh;function Hy(){if(Dh)return ur.exports;Dh=1;function i(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(i)}catch(r){console.error(r)}}return i(),ur.exports=Uy(),ur.exports}/**
|
||||
*/var Rh;function Uy(){if(Rh)return Me;Rh=1;var i=rr();function r(G){var j="https://react.dev/errors/"+G;if(1<arguments.length){j+="?args[]="+encodeURIComponent(arguments[1]);for(var V=2;V<arguments.length;V++)j+="&args[]="+encodeURIComponent(arguments[V])}return"Minified React error #"+G+"; visit "+j+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function m(){}var o={d:{f:m,r:function(){throw Error(r(522))},D:m,C:m,L:m,m,X:m,S:m,M:m},p:0,findDOMNode:null},_=Symbol.for("react.portal"),M=Symbol.for("react.recoverable"),N=Symbol.for("react.optimistic_key");function E(G,j,V){var ut=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:_,key:ut==null?null:ut===N?N:""+ut,children:G,containerInfo:j,implementation:V}}var B=i.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function z(G,j){if(G==="font")return"";if(typeof j=="string")return j==="use-credentials"?j:""}return Me.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=o,Me.browser=function(G){return{$$typeof:M,_reason:G}},Me.createPortal=function(G,j){var V=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!j||j.nodeType!==1&&j.nodeType!==9&&j.nodeType!==11)throw Error(r(299));return E(G,j,null,V)},Me.flushSync=function(G){var j=B.T,V=o.p;try{if(B.T=null,o.p=2,G)return G()}finally{B.T=j,o.p=V,o.d.f()}},Me.preconnect=function(G,j){typeof G=="string"&&(j?(j=j.crossOrigin,j=typeof j=="string"?j==="use-credentials"?j:"":void 0):j=null,o.d.C(G,j))},Me.prefetchDNS=function(G){typeof G=="string"&&o.d.D(G)},Me.preinit=function(G,j){if(typeof G=="string"&&j&&typeof j.as=="string"){var V=j.as,ut=z(V,j.crossOrigin),K=typeof j.integrity=="string"?j.integrity:void 0,tt=typeof j.fetchPriority=="string"?j.fetchPriority:void 0;V==="style"?o.d.S(G,typeof j.precedence=="string"?j.precedence:void 0,{crossOrigin:ut,integrity:K,fetchPriority:tt}):V==="script"&&o.d.X(G,{crossOrigin:ut,integrity:K,fetchPriority:tt,nonce:typeof j.nonce=="string"?j.nonce:void 0})}},Me.preinitModule=function(G,j){if(typeof G=="string")if(typeof j=="object"&&j!==null){if(j.as==null||j.as==="script"){var V=z(j.as,j.crossOrigin);o.d.M(G,{crossOrigin:V,integrity:typeof j.integrity=="string"?j.integrity:void 0,nonce:typeof j.nonce=="string"?j.nonce:void 0,fetchPriority:typeof j.fetchPriority=="string"?j.fetchPriority:void 0})}}else j==null&&o.d.M(G)},Me.preload=function(G,j){if(typeof G=="string"&&typeof j=="object"&&j!==null&&typeof j.as=="string"){var V=j.as,ut=z(V,j.crossOrigin);o.d.L(G,V,{crossOrigin:ut,integrity:typeof j.integrity=="string"?j.integrity:void 0,nonce:typeof j.nonce=="string"?j.nonce:void 0,type:typeof j.type=="string"?j.type:void 0,fetchPriority:typeof j.fetchPriority=="string"?j.fetchPriority:void 0,referrerPolicy:typeof j.referrerPolicy=="string"?j.referrerPolicy:void 0,imageSrcSet:typeof j.imageSrcSet=="string"?j.imageSrcSet:void 0,imageSizes:typeof j.imageSizes=="string"?j.imageSizes:void 0,media:typeof j.media=="string"?j.media:void 0})}},Me.preloadModule=function(G,j){if(typeof G=="string")if(j){var V=z(j.as,j.crossOrigin);o.d.m(G,{as:typeof j.as=="string"&&j.as!=="script"?j.as:void 0,crossOrigin:V,integrity:typeof j.integrity=="string"?j.integrity:void 0,nonce:typeof j.nonce=="string"?j.nonce:void 0,fetchPriority:typeof j.fetchPriority=="string"?j.fetchPriority:void 0})}else o.d.m(G)},Me.requestFormReset=function(G){o.d.r(G)},Me.unstable_batchedUpdates=function(G,j){return G(j)},Me.useFormState=function(G,j,V){return B.H.useFormState(G,j,V)},Me.useFormStatus=function(){return B.H.useHostTransitionStatus()},Me.version="19.3.1",Me}var Dh;function Hy(){if(Dh)return ir.exports;Dh=1;function i(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(i)}catch(r){console.error(r)}}return i(),ir.exports=Uy(),ir.exports}/**
|
||||
* @license React
|
||||
* react-dom-client.production.js
|
||||
*
|
||||
@@ -44,31 +44,31 @@
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/var Uh;function By(){if(Uh)return ti;Uh=1;var i=Dy(),r=fr(),m=Hy();function o(t){var e="https://react.dev/errors/"+t;if(1<arguments.length){e+="?args[]="+encodeURIComponent(arguments[1]);for(var l=2;l<arguments.length;l++)e+="&args[]="+encodeURIComponent(arguments[l])}return"Minified React error #"+t+"; visit "+e+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function _(t){return!(!t||t.nodeType!==1&&t.nodeType!==9&&t.nodeType!==11)}function M(t){for(var e=t,l=e;l&&!l.alternate;)e=l,(e.flags&4098)!==0&&(t=e.return),l=e.return;for(;e.return;)e=e.return;return e.tag===3?t:null}function N(t){if(t.tag===13){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function j(t){if(t.tag===31){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function B(t){if(M(t)!==t)throw Error(o(188))}function R(t){var e=t.alternate;if(!e){if(e=M(t),e===null)throw Error(o(188));return e!==t?null:t}for(var l=t,a=e;;){var n=l.return;if(n===null)break;var u=n.alternate;if(u===null){if(a=n.return,a!==null){l=a;continue}break}if(n.child===u.child){for(u=n.child;u;){if(u===l)return B(n),t;if(u===a)return B(n),e;u=u.sibling}throw Error(o(188))}if(l.return!==a.return)l=n,a=u;else{for(var c=!1,f=n.child;f;){if(f===l){c=!0,l=n,a=u;break}if(f===a){c=!0,a=n,l=u;break}f=f.sibling}if(!c){for(f=u.child;f;){if(f===l){c=!0,l=u,a=n;break}if(f===a){c=!0,a=u,l=n;break}f=f.sibling}if(!c)throw Error(o(189))}}if(l.alternate!==a)throw Error(o(190))}if(l.tag!==3)throw Error(o(188));return l.stateNode.current===l?t:e}function G(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t;for(t=t.child;t!==null;){if(e=G(t),e!==null)return e;t=t.sibling}return null}function E(t,e,l,a,n,u){for(;t!==null;){if((t.tag===5||t.tag===27||t.tag===6)&&l(t,a,n,u)||(t.tag!==22||t.memoizedState===null)&&(e||t.tag!==5&&t.tag!==27)&&E(t.child,e,l,a,n,u))return!0;t=t.sibling}return!1}function J(t){for(t=t.return;t!==null;){if(t.tag===3||t.tag===5||t.tag===27)return t;t=t.return}return null}function ut(t){var e=!1;for(t=t.return;t!==null&&(t.tag===4&&(e=!0),!(t.tag===3||t.tag===5||t.tag===27));)t=t.return;return e}function k(t){var e=[null,null],l=J(t);return l===null||P(e,t,l.child,{foundSelf:!1}),e}function P(t,e,l,a){for(;l!==null;){if(l===e)a.foundSelf=!0;else if(l.tag===5||l.tag===27||l.tag===6){if(a.foundSelf)return t[1]=l,!0;t[0]=l}else if((l.tag!==22||l.memoizedState===null)&&P(t,e,l.child,a))return!0;l=l.sibling}return!1}function it(t){switch(t.tag){case 5:case 27:case 6:return t.stateNode;case 3:return t.stateNode.containerInfo;default:throw Error(o(559))}}var Tt=null,Vt=null;function be(t,e,l){return t===l?!0:t===e?(Tt=t,!0):!1}function Me(t,e,l){return t===l?(Vt=t,!1):t===e?(Vt!==null&&(Tt=t),!0):!1}function Ft(t){if(t===null)return null;do t=t===null?null:t.return;while(t&&t.tag!==5&&t.tag!==27&&t.tag!==3);return t||null}function oe(t,e,l){for(var a=0,n=t;n;n=l(n))a++;n=0;for(var u=e;u;u=l(u))n++;for(;0<a-n;)t=l(t),a--;for(;0<n-a;)e=l(e),n--;for(;a--;){if(t===e||e!==null&&t===e.alternate)return t;t=l(t),e=l(e)}return null}var st=Object.assign,ht=Symbol.for("react.element"),Wt=Symbol.for("react.transitional.element"),te=Symbol.for("react.portal"),Lt=Symbol.for("react.fragment"),de=Symbol.for("react.strict_mode"),Dt=Symbol.for("react.profiler"),Pe=Symbol.for("react.consumer"),H=Symbol.for("react.context"),w=Symbol.for("react.forward_ref"),L=Symbol.for("react.suspense"),tt=Symbol.for("react.suspense_list"),jt=Symbol.for("react.memo"),vt=Symbol.for("react.lazy"),ee=Symbol.for("react.activity"),Oe=Symbol.for("react.legacy_hidden"),ue=Symbol.for("react.memo_cache_sentinel"),v=Symbol.for("react.view_transition"),D=Symbol.for("react.recoverable"),F=Symbol.iterator;function K(t){return t===null||typeof t!="object"?null:(t=F&&t[F]||t["@@iterator"],typeof t=="function"?t:null)}var yt=Symbol.for("react.client.reference");function dt(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===yt?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case Lt:return"Fragment";case Dt:return"Profiler";case de:return"StrictMode";case L:return"Suspense";case tt:return"SuspenseList";case ee:return"Activity";case v:return"ViewTransition"}if(typeof t=="object")switch(t.$$typeof){case te:return"Portal";case H:return t.displayName||"Context";case Pe:return(t._context.displayName||"Context")+".Consumer";case w:var e=t.render;return t=t.displayName,t||(t=e.displayName||e.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case jt:return e=t.displayName||null,e!==null?e:dt(t.type)||"Memo";case vt:e=t._payload,t=t._init;try{return dt(t(e))}catch{}}return null}var xt=Array.isArray,Q=r.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,at=m.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,xe={pending:!1,data:null,method:null,action:Line truncated
|
||||
*/var Uh;function By(){if(Uh)return ai;Uh=1;var i=Dy(),r=rr(),m=Hy();function o(t){var e="https://react.dev/errors/"+t;if(1<arguments.length){e+="?args[]="+encodeURIComponent(arguments[1]);for(var l=2;l<arguments.length;l++)e+="&args[]="+encodeURIComponent(arguments[l])}return"Minified React error #"+t+"; visit "+e+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function _(t){return!(!t||t.nodeType!==1&&t.nodeType!==9&&t.nodeType!==11)}function M(t){for(var e=t,l=e;l&&!l.alternate;)e=l,(e.flags&4098)!==0&&(t=e.return),l=e.return;for(;e.return;)e=e.return;return e.tag===3?t:null}function N(t){if(t.tag===13){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function E(t){if(t.tag===31){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function B(t){if(M(t)!==t)throw Error(o(188))}function z(t){var e=t.alternate;if(!e){if(e=M(t),e===null)throw Error(o(188));return e!==t?null:t}for(var l=t,a=e;;){var n=l.return;if(n===null)break;var u=n.alternate;if(u===null){if(a=n.return,a!==null){l=a;continue}break}if(n.child===u.child){for(u=n.child;u;){if(u===l)return B(n),t;if(u===a)return B(n),e;u=u.sibling}throw Error(o(188))}if(l.return!==a.return)l=n,a=u;else{for(var c=!1,f=n.child;f;){if(f===l){c=!0,l=n,a=u;break}if(f===a){c=!0,a=n,l=u;break}f=f.sibling}if(!c){for(f=u.child;f;){if(f===l){c=!0,l=u,a=n;break}if(f===a){c=!0,a=u,l=n;break}f=f.sibling}if(!c)throw Error(o(189))}}if(l.alternate!==a)throw Error(o(190))}if(l.tag!==3)throw Error(o(188));return l.stateNode.current===l?t:e}function G(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t;for(t=t.child;t!==null;){if(e=G(t),e!==null)return e;t=t.sibling}return null}function j(t,e,l,a,n,u){for(;t!==null;){if((t.tag===5||t.tag===27||t.tag===6)&&l(t,a,n,u)||(t.tag!==22||t.memoizedState===null)&&(e||t.tag!==5&&t.tag!==27)&&j(t.child,e,l,a,n,u))return!0;t=t.sibling}return!1}function V(t){for(t=t.return;t!==null;){if(t.tag===3||t.tag===5||t.tag===27)return t;t=t.return}return null}function ut(t){var e=!1;for(t=t.return;t!==null&&(t.tag===4&&(e=!0),!(t.tag===3||t.tag===5||t.tag===27));)t=t.return;return e}function K(t){var e=[null,null],l=V(t);return l===null||tt(e,t,l.child,{foundSelf:!1}),e}function tt(t,e,l,a){for(;l!==null;){if(l===e)a.foundSelf=!0;else if(l.tag===5||l.tag===27||l.tag===6){if(a.foundSelf)return t[1]=l,!0;t[0]=l}else if((l.tag!==22||l.memoizedState===null)&&tt(t,e,l.child,a))return!0;l=l.sibling}return!1}function it(t){switch(t.tag){case 5:case 27:case 6:return t.stateNode;case 3:return t.stateNode.containerInfo;default:throw Error(o(559))}}var jt=null,Xt=null;function ge(t,e,l){return t===l?!0:t===e?(jt=t,!0):!1}function Oe(t,e,l){return t===l?(Xt=t,!1):t===e?(Xt!==null&&(jt=t),!0):!1}function Jt(t){if(t===null)return null;do t=t===null?null:t.return;while(t&&t.tag!==5&&t.tag!==27&&t.tag!==3);return t||null}function fe(t,e,l){for(var a=0,n=t;n;n=l(n))a++;n=0;for(var u=e;u;u=l(u))n++;for(;0<a-n;)t=l(t),a--;for(;0<n-a;)e=l(e),n--;for(;a--;){if(t===e||e!==null&&t===e.alternate)return t;t=l(t),e=l(e)}return null}var st=Object.assign,ht=Symbol.for("react.element"),Ft=Symbol.for("react.transitional.element"),Pt=Symbol.for("react.portal"),kt=Symbol.for("react.fragment"),re=Symbol.for("react.strict_mode"),Dt=Symbol.for("react.profiler"),tl=Symbol.for("react.consumer"),U=Symbol.for("react.context"),w=Symbol.for("react.forward_ref"),k=Symbol.for("react.suspense"),et=Symbol.for("react.suspense_list"),Tt=Symbol.for("react.memo"),vt=Symbol.for("react.lazy"),te=Symbol.for("react.activity"),ze=Symbol.for("react.legacy_hidden"),ne=Symbol.for("react.memo_cache_sentinel"),v=Symbol.for("react.view_transition"),R=Symbol.for("react.recoverable"),W=Symbol.iterator;function J(t){return t===null||typeof t!="object"?null:(t=W&&t[W]||t["@@iterator"],typeof t=="function"?t:null)}var yt=Symbol.for("react.client.reference");function dt(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===yt?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case kt:return"Fragment";case Dt:return"Profiler";case re:return"StrictMode";case k:return"Suspense";case et:return"SuspenseList";case te:return"Activity";case v:return"ViewTransition"}if(typeof t=="object")switch(t.$$typeof){case Pt:return"Portal";case U:return t.displayName||"Context";case tl:return(t._context.displayName||"Context")+".Consumer";case w:var e=t.render;return t=t.displayName,t||(t=e.displayName||e.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case Tt:return e=t.displayName||null,e!==null?e:dt(t.type)||"Memo";case vt:e=t._payload,t=t._init;try{return dt(t(e))}catch{}}return null}var xt=Array.isArray,Q=r.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,at=m.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,Se={pending:!1,data:null,method:null,actiLine truncated
|
||||
at`)?" (<anonymous>)":-1<l.stack.indexOf("@")?"@unknown:0:0":""}return`
|
||||
`+ol+t+Dl}var Ue=!1;function el(t,e){if(!t||Ue)return"";Ue=!0;var l=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var a={DetermineComponentFrameRoot:function(){try{if(e){var A=function(){throw Error()};if(Object.defineProperty(A.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(A,[])}catch(q){var g=q}Reflect.construct(t,[],A)}else{try{A.call()}catch(q){g=q}A=!1;try{var x=Object.getOwnPropertyDescriptor(t.prototype,"props");Object.defineProperty(t.prototype,"props",{configurable:!0,set:function(){throw Error()}}),A=!0,new t}finally{A&&(x!==void 0?Object.defineProperty(t.prototype,"props",x):delete t.prototype.props)}}}else{try{throw Error()}catch(q){g=q}(A=t())&&typeof A.catch=="function"&&A.catch(function(){})}}catch(q){if(q&&g&&typeof q.stack=="string")return[q.stack,g.stack]}return[null,null]}};a.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var n=Object.getOwnPropertyDescriptor(a.DetermineComponentFrameRoot,"name");n&&n.configurable&&Object.defineProperty(a.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var u=a.DetermineComponentFrameRoot(),c=u[0],f=u[1];if(c&&f){var d=c.split(`
|
||||
`+qe+t+ol}var Hl=!1;function dl(t,e){if(!t||Hl)return"";Hl=!0;var l=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var a={DetermineComponentFrameRoot:function(){try{if(e){var A=function(){throw Error()};if(Object.defineProperty(A.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(A,[])}catch(q){var g=q}Reflect.construct(t,[],A)}else{try{A.call()}catch(q){g=q}A=!1;try{var x=Object.getOwnPropertyDescriptor(t.prototype,"props");Object.defineProperty(t.prototype,"props",{configurable:!0,set:function(){throw Error()}}),A=!0,new t}finally{A&&(x!==void 0?Object.defineProperty(t.prototype,"props",x):delete t.prototype.props)}}}else{try{throw Error()}catch(q){g=q}(A=t())&&typeof A.catch=="function"&&A.catch(function(){})}}catch(q){if(q&&g&&typeof q.stack=="string")return[q.stack,g.stack]}return[null,null]}};a.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var n=Object.getOwnPropertyDescriptor(a.DetermineComponentFrameRoot,"name");n&&n.configurable&&Object.defineProperty(a.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var u=a.DetermineComponentFrameRoot(),c=u[0],f=u[1];if(c&&f){var d=c.split(`
|
||||
`),p=f.split(`
|
||||
`);for(n=a=0;a<d.length&&!d[a].includes("DetermineComponentFrameRoot");)a++;for(;n<p.length&&!p[n].includes("DetermineComponentFrameRoot");)n++;if(a===d.length||n===p.length)for(a=d.length-1,n=p.length-1;1<=a&&0<=n&&d[a]!==p[n];)n--;for(;1<=a&&0<=n;a--,n--)if(d[a]!==p[n]){if(a!==1||n!==1)do if(a--,n--,0>n||d[a]!==p[n]){var T=`
|
||||
`+d[a].replace(" at new "," at ");return t.displayName&&T.includes("<anonymous>")&&(T=T.replace("<anonymous>",t.displayName)),T}while(1<=a&&0<=n);break}}}finally{Ue=!1,Error.prepareStackTrace=l}return(l=t?t.displayName||t.name:"")?ie(l):""}function Ul(t,e){switch(t.tag){case 26:case 27:case 5:return ie(t.type);case 16:return ie("Lazy");case 13:return t.child!==e&&e!==null?ie("Suspense Fallback"):ie("Suspense");case 19:return ie("SuspenseList");case 0:case 15:return el(t.type,!1);case 11:return el(t.type.render,!1);case 1:return el(t.type,!0);case 31:return ie("Activity");case 30:return ie("ViewTransition");default:return""}}function dl(t){try{var e="",l=null;do e+=Ul(t,l),l=t,t=t.return;while(t);return e}catch(a){return`
|
||||
`+d[a].replace(" at new "," at ");return t.displayName&&T.includes("<anonymous>")&&(T=T.replace("<anonymous>",t.displayName)),T}while(1<=a&&0<=n);break}}}finally{Hl=!1,Error.prepareStackTrace=l}return(l=t?t.displayName||t.name:"")?pe(l):""}function Bl(t,e){switch(t.tag){case 26:case 27:case 5:return pe(t.type);case 16:return pe("Lazy");case 13:return t.child!==e&&e!==null?pe("Suspense Fallback"):pe("Suspense");case 19:return pe("SuspenseList");case 0:case 15:return dl(t.type,!1);case 11:return dl(t.type.render,!1);case 1:return dl(t.type,!0);case 31:return pe("Activity");case 30:return pe("ViewTransition");default:return""}}function we(t){try{var e="",l=null;do e+=Bl(t,l),l=t,t=t.return;while(t);return e}catch(a){return`
|
||||
Error generating stack: `+a.message+`
|
||||
`+a.stack}}var Kt=Object.prototype.hasOwnProperty,oa=i.unstable_scheduleCallback,da=i.unstable_cancelCallback,Jl=i.unstable_shouldYield,Xa=i.unstable_requestPaint,ae=i.unstable_now,iu=i.unstable_getCurrentPriorityLevel,mn=i.unstable_ImmediatePriority,hn=i.unstable_UserBlockingPriority,Ze=i.unstable_NormalPriority,Va=i.unstable_LowPriority,ma=i.unstable_IdlePriority,cu=i.log,su=i.unstable_setDisableYieldValue,Fl=null,U=null;function z(t){if(typeof cu=="function"&&su(t),U&&typeof U.setStrictMode=="function")try{U.setStrictMode(Fl,t)}catch{}}var Y=Math.clz32?Math.clz32:ha,$=Math.log,Ut=Math.LN2;function ha(t){return t>>>=0,t===0?32:31-($(t)/Ut|0)|0}var ll=256,He=262144,me=4194304;function ml(t){var e=t&42;if(e!==0)return e;switch(t&-t){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:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&-t;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function O(t,e,l){var a=t.pendingLanes;if(a===0)return 0;var n=0,u=t.suspendedLanes,c=t.pingedLanes;t=t.warmLanes;var f=a&134217727;return f!==0?(a=f&~u,a!==0?n=ml(a):(c&=f,c!==0?n=ml(c):l||(l=f&~t,l!==0&&(n=ml(l))))):(f=a&~u,f!==0?n=ml(f):c!==0?n=ml(c):l||(l=a&~t,l!==0&&(n=ml(l)))),n===0?0:e!==0&&e!==n&&(e&u)===0&&(u=n&-n,l=e&-e,u>=l||u===32&&(l&4194048)!==0)?e:n}function V(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function W(t,e){(e&8)!==0&&(e|=e&32);var l=t.entangledLanes;if(l!==0)for(t=t.entanglements,l&=e;0<l;){var a=31-Y(l),n=1<<a;e|=t[a],l&=~n}return e}function Gt(t,e){switch(t){case 1:case 2:case 4:case 8:case 64:return e+250;case 16:case 32: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+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Mt(){var t=me;return me<<=1,(me&62914560)===0&&(me=4194304),t}function $t(t){for(var e=[],l=0;31>l;l++)e.push(t);return e}function Le(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function li(t,e,l,a,n,u){var c=t.pendingLanes;t.pendingLanes=l,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=l,t.entangledLanes&=l,t.errorRecoveryDisabledLanes&=l,t.shellSuspendCounter=0;var f=t.entanglements,d=t.expirationTimes,p=t.hiddenUpdates;for(l=c&~l;0<l;){var T=31-Y(l),A=1<<T;f[T]=0,d[T]=-1;var g=p[T];if(g!==null)for(p[T]=null,T=0;T<g.length;T++){var x=g[T];x!==null&&(x.lane&=-536870913)}l&=~A}a!==0&&ke(t,a,0),u!==0&&n===0&&t.tag!==0&&(t.suspendedLanes|=u&~(c&~e))}function ke(t,e,l){t.pendingLanes|=e,t.suspendedLanes&=~e;var a=31-Y(e);t.entangledLanes|=e,t.entanglements[a]=t.entanglements[a]|1073741824|l&261930}function Be(t,e){var l=t.entangledLanes|=e;for(t=t.entanglements;l;){var a=31-Y(l),n=1<<a;n&e|t[a]&e&&(t[a]|=e),l&=~n}}function ai(t,e){var l=e&-e;return l=(l&42)!==0?1:Cc(l),(l&(t.suspendedLanes|e))!==0?0:l}function Cc(t){switch(t){case 2:t=1;break;case 8:t=4;break;case 32:t=16;break;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:case 4194304:case 8388608:case 16777216:case 33554432:t=128;break;case 268435456:t=134217728;break;default:t=0}return t}function Ac(t){return t&=-t,2<t?8<t?(t&134217727)!==0?32:268435456:8:2}function or(){var t=at.p;return t!==0?t:(t=window.event,t===void 0?32:ph(t.type))}function dr(t,e){var l=at.p;try{return at.p=t,e()}finally{at.p=l}}var Wl=Math.random().toString(36).slice(2),Te="__reactFiber$"+Wl,Ke="__reactProps$"+Wl,vn="__reactContainer$"+Wl,mr="__reactEvents$"+Wl,Zh="__reactListeners$"+Wl,Lh="__reactHandles$"+Wl,hr="__reactResources$"+Wl,fu="__reactMarker$"+Wl,ni="__reactLoad$"+Wl;function ui(t){delete t[Te],delete t[Ke],delete t[Zh],delete t[Lh]}function Za(t){var e;if(e=t[Te])return e;for(var l=t.parentNode;l;){if(e=l[vn]||l[Te]){if(l=e.alternate,e.child!==null||l!==null&&l.child!==null)for(t=Pm(t);t!==null;){if(l=t[Te])return l;t=Pm(t)}return e}t=l,l=t.parentNode}return null}function yn(t){if(t=t[Te]||t[vn]){var e=t.tag;if(e===5||e===6||e===13||e===31||e===26||e===27||e===3)return t}return null}function ru(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t.stateNode;throw Error(o(33))}function gn(t){var e=t[hr];return e||(e=t[hr]={hoistableStyles:new Map,hoistableScripts:new Map}),e}function ye(t){t[fu]=!0}function vr(t){t[ni]=void 0}var yr=new Set,gr={};function La(t,e){Sn(t,e),Sn(t+"Capture",e)}function Sn(t,e){forLine truncated
|
||||
`).replace(R0,"")}function Dm(t,e){return e=Rm(e),Rm(t)===e}function Rt(t,e,l,a,n,u){switch(l){case"children":if(typeof a=="string")e==="body"||e==="textarea"&&a===""||bn(t,a);else if(typeof a=="number"||typeof a=="bigint")e!=="body"&&bn(t,""+a);else return;break;case"className":ci(t,"class",a);break;case"tabIndex":ci(t,"tabindex",a);break;case"dir":case"role":case"viewBox":case"width":case"height":ci(t,l,a);break;case"style":Cr(t,a,u);return;case"data":if(e!=="object"){ci(t,"data",a);break}case"src":case"href":if(a===""&&(e!=="a"||l!=="href")){t.removeAttribute(l);break}if(a==null||typeof a=="function"||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=si(a),t.setAttribute(l,a);break;case"action":case"formAction":if(typeof a=="function"){t.setAttribute(l,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof u=="function"&&(l==="formAction"?(e!=="input"&&Rt(t,e,"name",n.name,n,null),Rt(t,e,"formEncType",n.formEncType,n,null),Rt(t,e,"formMethod",n.formMethod,n,null),Rt(t,e,"formTarget",n.formTarget,n,null)):(Rt(t,e,"encType",n.encType,n,null),Rt(t,e,"method",n.method,n,null),Rt(t,e,"target",n.target,n,null)));if(a==null||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=si(a),t.setAttribute(l,a);break;case"onClick":a!=null&&(t.onclick=Hl);return;case"onScroll":a!=null&&St("scroll",t);return;case"onScrollEnd":a!=null&&St("scrollend",t);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(o(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(o(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"multiple":t.multiple=a&&typeof a!="function"&&typeof a!="symbol";break;case"muted":t.muted=a&&typeof a!="function"&&typeof a!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(a==null||typeof a=="function"||typeof a=="boolean"||typeof a=="symbol"){t.removeAttribute("xlink:href");break}l=si(a),t.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",l);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"credentialless":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":a&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,""):t.removeAttribute(l);break;case"capture":case"download":a===!0?t.setAttribute(l,""):a!==!1&&a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"cols":case"rows":case"size":case"span":a!=null&&typeof a!="function"&&typeof a!="symbol"&&!isNaN(a)&&1<=a?t.setAttribute(l,a):t.removeAttribute(l);break;case"rowSpan":case"start":a==null||typeof a=="function"||typeof a=="symbol"||isNaN(a)?t.removeAttribute(l):t.setAttribute(l,a);break;case"popover":St("beforetoggle",t),St("toggle",t),ii(t,"popover",a);break;case"xlinkActuate":$l(t,"http://www.w3.org/1999/xlink","xlink:actuate",a);break;case"xlinkArcrole":$l(t,"http://www.w3.org/1999/xlink","xlink:arcrole",a);break;case"xlinkRole":$l(t,"http://www.w3.org/1999/xlink","xlink:role",a);break;case"xlinkShow":$l(t,"http://www.w3.org/1999/xlink","xlink:show",a);break;case"xlinkTitle":$l(t,"http://www.w3.org/1999/xlink","xlink:title",a);break;case"xlinkType":$l(t,"http://www.w3.org/1999/xlink","xlink:type",a);break;case"xmlBase":$l(t,"http://www.w3.org/XML/1998/namespace","xml:base",a);break;case"xmlLang":$l(t,"http://www.w3.org/XML/1998/namespace","xml:lang",a);break;case"xmlSpace":$l(t,"http://www.w3.org/XML/1998/namespace","xml:space",a);break;case"is":ii(t,"is",a);break;case"innerText":case"textContent":return;default:if(!(2<l.length)||l[0]!=="o"&&l[0]!=="O"||l[1]!=="n"&&l[1]!=="N")l=$h.get(l)||l,ii(t,l,a);else return}_t=!0}function zf(t,e,l,a,n,u){switch(l){case"style":Cr(t,a,u);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(o(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(o(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"children":if(typeof a=="string")bn(t,a);else if(typeof a=="number"||typeof a=="bigint")bn(t,""+a);else return;break;case"onScroll":a!=null&&St("scroll",t);return;case"onScrollEnd":a!=null&&St("scroLine truncated
|
||||
`;Gt&&($t+=Gt+`
|
||||
`),Mt&&($t+=(Array.isArray(Mt)?Mt.join(`
|
||||
`):String(Mt))+`
|
||||
`),$t+=JSON.stringify(W,null,2).slice(0,4e3),Rl($t),xe("已返回登录信息,见下方")}catch(W){xe("发起失败:"+W.message)}},[]),He=y.useCallback(async()=>{const O=k.trim();if(!O){ut("请填要查配额的用户 id");return}try{const V=await lt(et.adminQuotaUser(O));ut("用户 "+O.slice(0,8)+":本月用量 "+cr(V.used_usd_micros)+" / 配额 "+cr(V.quota_usd_micros)+(V.remaining_usd_micros!==null&&V.remaining_usd_micros!==void 0?"(剩余 "+cr(V.remaining_usd_micros)+")":"")+" | 月份 "+(V.month||""))}catch(V){ut(ir(V))}},[k]),me=y.useCallback(async()=>{if(window.confirm("确定手动触发 sweep 吗?超阈值的陈旧 running 任务会被标为 interrupted。"))try{const V=(await lt(et.adminSweep,"POST",{})).swept||[];ut("sweep 完成,清理 "+V.length+" 个任务"+(V.length?":"+V.join("、"):""))}catch(O){ut(ir(O))}},[]),ml=y.useCallback(async()=>{try{const O=await lt(et.adminBackup,"POST",{});ut("备份完成:backend="+O.backend+" | 路径 "+O.path+" | 大小 "+O.size_bytes+" 字节 | 任务 "+O.task_count+" 行")}catch(O){ut(ir(O))}},[]);return{keySaved:i,keyState:m,quotaText:_,healthText:N,adminGate:B,adminBody:G,adminResult:J,adminUserId:k,setAdminUserId:P,projects:it,projectId:Vt,setProjectId:be,projectMsg:Me,githubRepos:oe,workers:ht,workersSummary:te,workersError:de,members:Pe,memberMsg:w,memberError:tt,skills:vt,apps:Oe,mcpServers:v,skillMsg:F,skillError:yt,mcpMsg:xt,mcpOauthMsg:at,mcpOauthBox:Qa,envs:Xe,envCount:Ct,envMsg:Kl,envRepoLine:jl,envRepoBar:kt,envRepoMsg:tl,envSelect:Dl,setEnvSelect:ie,models:Ue,modelCustomVisible:Ul,refreshHealth:da,loadQuota:Jl,probeAdmin:Xa,refreshProjects:ae,loadGithubRepos:iu,loadRelayModels:mn,refreshWorkers:hn,refreshEnvs:Ze,refreshMembers:Va,refreshSkills:ma,saveKeyInput:cu,clearKeyInput:su,createProject:Fl,createEnv:U,archiveEnv:z,saveEnvRepo:Y,inviteMember:$,changeMemberRole:Ut,reloadMcp:ha,mcpOauth:ll,adminQuota:He,adminSweep:me,adminBackup:ml}}function Fy(){const[i,r]=y.useState([]),[m,o]=y.useState(""),[_,M]=y.useState(""),[N,j]=y.useState(""),[B,R]=y.useState(""),G=y.useCallback(async(k,P)=>{const it=k!==void 0?k:P;if(!it){r([]);return}try{const Tt=await lt(et.tasks(it));r(Tt.tasks||[]),o("")}catch(Tt){o("加载失败:"+Tt.message)}},[]),E=y.useMemo(()=>{const k=B.trim().toLowerCase();return i.filter(P=>!(_&&P.status!==_||N&&(P.executor||"")!==N||k&&!((P.id||"")+" "+(P.prompt||"")).toLowerCase().includes(k)))},[i,_,N,B]),J=E.length+" 个任务(共 "+i.length+" 个)",ut=y.useCallback(async(k,P)=>{const it={...k};P.length&&(it.upload_id=await Ly(P));const Tt=await lt(et.tasksCreate,"POST",it);return"任务已建:"+ve(Tt.id)},[]);return{tasks:i,taskError:m,statusFilter:_,setStatusFilter:M,executorFilter:N,setExecutorFilter:j,searchText:B,setSearchText:R,filteredTasks:E,taskCountText:J,refreshTasks:G,dispatch:ut}}function Wy(i,r){const[m,o]=y.useState(null),[_,M]=y.useState(""),[N,j]=y.useState(""),[B,R]=y.useState(""),[G,E]=y.useState(""),[J,ut]=y.useState(!1),[k,P]=y.useState(""),[it,Tt]=y.useState([]),[Vt,be]=y.useState([]),[Me,Ft]=y.useState(""),[oe,st]=y.useState([]),[ht,Wt]=y.useState(""),[te,Lt]=y.useState(""),[de,Dt]=y.useState([]),[Pe,H]=y.useState(""),[w,L]=y.useState(""),[tt,jt]=y.useState(""),[vt,ee]=y.useState(""),[Oe,ue]=y.useState([]),[v,D]=y.useState([]),[F,K]=y.useState(""),[yt,dt]=y.useState([]),[xt,Q]=y.useState(""),[at,xe]=y.useState(""),[Qa,Rl]=y.useState(""),[Xe,Yt]=y.useState(""),[Ct,ze]=y.useState([]),[Kl,le]=y.useState(""),jl=y.useRef(null),De=y.useRef(null),kt=y.useRef(null);kt.current=m;const Ve=y.useCallback(()=>{if(jl.current){try{jl.current.abort()}catch{}jl.current=null}De.current&&(clearTimeout(De.current),De.current=null)},[]),tl=y.useCallback(async U=>{try{const z=await lt(et.taskAgents(U));be(z.agents||[]),Ft(z.note||"")}catch(z){be([]),Ft("子代理链路加载失败:"+z.message)}},[]),ol=y.useCallback(U=>{De.current&&clearTimeout(De.current),De.current=setTimeout(()=>void tl(U),1e3)},[tl]),Dl=y.useCallback(async U=>{if(!U.coordinator_parts){Tt([]);return}try{const z=await lt(et.tasksByParent(U.project_id||"",U.id));Tt(z.tasks||[])}catch{Tt([])}},[]),ie=y.useCallback(async U=>{try{const z=await lt(et.taskFollowups(U));ue(z.followups||[])}catch{ue([])}},[]),Ue=y.useCallback(async U=>{var Y;const z=U||((Y=kt.current)==null?void 0:Y.id);if(z)try{const $=await lt(et.taskReviews(z));st($.reviews||[]),Lt("")}catch($){Lt("评审队列加载失败:"+$.message)}},[]),el=y.useCallback(async U=>{var Y;const z=U||((Y=kt.current)==null?void 0:Y.id);if(z)try{const $=await lt(et.taskApprovals(z));D($.approvals||[])}catch($){K("审批队列加载失败:"+$.message)}},[]),Ul=y.useCallback(async U=>{try{const z=await lt(et.taskMessages(U));dt(z.messages||[])}catch{dt([])}},[]),dl=y.useCallback(async U=>{try{const z=await lt(et.taskThread(U)),Y=z.thread||z;let $=Y.turns;$==null&&($=Y.turn_count),$==null&&($=Y.turns_count),xe("id "+(Y.id||U)+" | 名字 "+(Y.name||Y.thread_name||"")+" | 归档 "+(Y.aLine truncated
|
||||
`+a.stack}}var Vt=Object.prototype.hasOwnProperty,ll=i.unstable_scheduleCallback,ml=i.unstable_cancelCallback,ql=i.unstable_shouldYield,La=i.unstable_requestPaint,Wt=i.unstable_now,$l=i.unstable_getCurrentPriorityLevel,yn=i.unstable_ImmediatePriority,gn=i.unstable_UserBlockingPriority,Ke=i.unstable_NormalPriority,ka=i.unstable_LowPriority,ya=i.unstable_IdlePriority,fu=i.log,ru=i.unstable_setDisableYieldValue,Il=null,be=null;function hl(t){if(typeof fu=="function"&&ru(t),be&&typeof be.setStrictMode=="function")try{be.setStrictMode(Il,t)}catch{}}var xe=Math.clz32?Math.clz32:Z,H=Math.log,D=Math.LN2;function Z(t){return t>>>=0,t===0?32:31-(H(t)/D|0)|0}var F=256,Nt=262144,_l=4194304;function De(t){var e=t&42;if(e!==0)return e;switch(t&-t){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:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&-t;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function O(t,e,l){var a=t.pendingLanes;if(a===0)return 0;var n=0,u=t.suspendedLanes,c=t.pingedLanes;t=t.warmLanes;var f=a&134217727;return f!==0?(a=f&~u,a!==0?n=De(a):(c&=f,c!==0?n=De(c):l||(l=f&~t,l!==0&&(n=De(l))))):(f=a&~u,f!==0?n=De(f):c!==0?n=De(c):l||(l=a&~t,l!==0&&(n=De(l)))),n===0?0:e!==0&&e!==n&&(e&u)===0&&(u=n&-n,l=e&-e,u>=l||u===32&&(l&4194048)!==0)?e:n}function Y(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function $(t,e){(e&8)!==0&&(e|=e&32);var l=t.entangledLanes;if(l!==0)for(t=t.entanglements,l&=e;0<l;){var a=31-xe(l),n=1<<a;e|=t[a],l&=~n}return e}function Ut(t,e){switch(t){case 1:case 2:case 4:case 8:case 64:return e+250;case 16:case 32: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+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Zt(){var t=_l;return _l<<=1,(_l&62914560)===0&&(_l=4194304),t}function je(t){for(var e=[],l=0;31>l;l++)e.push(t);return e}function Ue(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function ga(t,e,l,a,n,u){var c=t.pendingLanes;t.pendingLanes=l,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=l,t.entangledLanes&=l,t.errorRecoveryDisabledLanes&=l,t.shellSuspendCounter=0;var f=t.entanglements,d=t.expirationTimes,p=t.hiddenUpdates;for(l=c&~l;0<l;){var T=31-xe(l),A=1<<T;f[T]=0,d[T]=-1;var g=p[T];if(g!==null)for(p[T]=null,T=0;T<g.length;T++){var x=g[T];x!==null&&(x.lane&=-536870913)}l&=~A}a!==0&&Pl(t,a,0),u!==0&&n===0&&t.tag!==0&&(t.suspendedLanes|=u&~(c&~e))}function Pl(t,e,l){t.pendingLanes|=e,t.suspendedLanes&=~e;var a=31-xe(e);t.entangledLanes|=e,t.entanglements[a]=t.entanglements[a]|1073741824|l&261930}function ui(t,e){var l=t.entangledLanes|=e;for(t=t.entanglements;l;){var a=31-xe(l),n=1<<a;n&e|t[a]&e&&(t[a]|=e),l&=~n}}function ou(t,e){var l=e&-e;return l=(l&42)!==0?1:Ye(l),(l&(t.suspendedLanes|e))!==0?0:l}function Ye(t){switch(t){case 2:t=1;break;case 8:t=4;break;case 32:t=16;break;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:case 4194304:case 8388608:case 16777216:case 33554432:t=128;break;case 268435456:t=134217728;break;default:t=0}return t}function He(t){return t&=-t,2<t?8<t?(t&134217727)!==0?32:268435456:8:2}function ii(){var t=at.p;return t!==0?t:(t=window.event,t===void 0?32:ph(t.type))}function dr(t,e){var l=at.p;try{return at.p=t,e()}finally{at.p=l}}var ta=Math.random().toString(36).slice(2),Te="__reactFiber$"+ta,Je="__reactProps$"+ta,Sn="__reactContainer$"+ta,mr="__reactEvents$"+ta,Zh="__reactListeners$"+ta,Lh="__reactHandles$"+ta,hr="__reactResources$"+ta,du="__reactMarker$"+ta,ci="__reactLoad$"+ta;function si(t){delete t[Te],delete t[Je],delete t[Zh],delete t[Lh]}function Ka(t){var e;if(e=t[Te])return e;for(var l=t.parentNode;l;){if(e=l[Sn]||l[Te]){if(l=e.alternate,e.child!==null||l!==null&&l.child!==null)for(t=Pm(t);t!==null;){if(l=t[Te])return l;t=Pm(t)}return e}t=l,l=t.parentNode}return null}function pn(t){if(t=t[Te]||t[Sn]){var e=t.tag;if(e===5||e===6||e===13||e===31||e===26||e===27||e===3)return t}return null}function mu(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t.stateNode;throw Error(o(33))}function bn(t){var e=t[hr];return e||(e=t[hr]={hoistableStyles:new Map,hoistableScripts:new Map}),e}function me(t){t[du]=!0}function vr(t){t[ci]=void 0}var yr=new Set,gr={};function Ja(t,e){xn(t,e),xn(t+"Capture",e)}function xn(t,eLine truncated
|
||||
`).replace(R0,"")}function Dm(t,e){return e=Rm(e),Rm(t)===e}function Rt(t,e,l,a,n,u){switch(l){case"children":if(typeof a=="string")e==="body"||e==="textarea"&&a===""||Tn(t,a);else if(typeof a=="number"||typeof a=="bigint")e!=="body"&&Tn(t,""+a);else return;break;case"className":ri(t,"class",a);break;case"tabIndex":ri(t,"tabindex",a);break;case"dir":case"role":case"viewBox":case"width":case"height":ri(t,l,a);break;case"style":Cr(t,a,u);return;case"data":if(e!=="object"){ri(t,"data",a);break}case"src":case"href":if(a===""&&(e!=="a"||l!=="href")){t.removeAttribute(l);break}if(a==null||typeof a=="function"||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=oi(a),t.setAttribute(l,a);break;case"action":case"formAction":if(typeof a=="function"){t.setAttribute(l,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof u=="function"&&(l==="formAction"?(e!=="input"&&Rt(t,e,"name",n.name,n,null),Rt(t,e,"formEncType",n.formEncType,n,null),Rt(t,e,"formMethod",n.formMethod,n,null),Rt(t,e,"formTarget",n.formTarget,n,null)):(Rt(t,e,"encType",n.encType,n,null),Rt(t,e,"method",n.method,n,null),Rt(t,e,"target",n.target,n,null)));if(a==null||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=oi(a),t.setAttribute(l,a);break;case"onClick":a!=null&&(t.onclick=wl);return;case"onScroll":a!=null&&St("scroll",t);return;case"onScrollEnd":a!=null&&St("scrollend",t);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(o(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(o(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"multiple":t.multiple=a&&typeof a!="function"&&typeof a!="symbol";break;case"muted":t.muted=a&&typeof a!="function"&&typeof a!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(a==null||typeof a=="function"||typeof a=="boolean"||typeof a=="symbol"){t.removeAttribute("xlink:href");break}l=oi(a),t.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",l);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"credentialless":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":a&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,""):t.removeAttribute(l);break;case"capture":case"download":a===!0?t.setAttribute(l,""):a!==!1&&a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"cols":case"rows":case"size":case"span":a!=null&&typeof a!="function"&&typeof a!="symbol"&&!isNaN(a)&&1<=a?t.setAttribute(l,a):t.removeAttribute(l);break;case"rowSpan":case"start":a==null||typeof a=="function"||typeof a=="symbol"||isNaN(a)?t.removeAttribute(l):t.setAttribute(l,a);break;case"popover":St("beforetoggle",t),St("toggle",t),fi(t,"popover",a);break;case"xlinkActuate":ea(t,"http://www.w3.org/1999/xlink","xlink:actuate",a);break;case"xlinkArcrole":ea(t,"http://www.w3.org/1999/xlink","xlink:arcrole",a);break;case"xlinkRole":ea(t,"http://www.w3.org/1999/xlink","xlink:role",a);break;case"xlinkShow":ea(t,"http://www.w3.org/1999/xlink","xlink:show",a);break;case"xlinkTitle":ea(t,"http://www.w3.org/1999/xlink","xlink:title",a);break;case"xlinkType":ea(t,"http://www.w3.org/1999/xlink","xlink:type",a);break;case"xmlBase":ea(t,"http://www.w3.org/XML/1998/namespace","xml:base",a);break;case"xmlLang":ea(t,"http://www.w3.org/XML/1998/namespace","xml:lang",a);break;case"xmlSpace":ea(t,"http://www.w3.org/XML/1998/namespace","xml:space",a);break;case"is":fi(t,"is",a);break;case"innerText":case"textContent":return;default:if(!(2<l.length)||l[0]!=="o"&&l[0]!=="O"||l[1]!=="n"&&l[1]!=="N")l=$h.get(l)||l,fi(t,l,a);else return}_t=!0}function Rf(t,e,l,a,n,u){switch(l){case"style":Cr(t,a,u);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(o(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(o(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"children":if(typeof a=="string")Tn(t,a);else if(typeof a=="number"||typeof a=="bigint")Tn(t,""+a);else return;break;case"onScroll":a!=null&&St("scroll",t);return;case"onScrollEnd":a!=null&&St("scroLine truncated
|
||||
`;Ut&&(je+=Ut+`
|
||||
`),Zt&&(je+=(Array.isArray(Zt)?Zt.join(`
|
||||
`):String(Zt))+`
|
||||
`),je+=JSON.stringify($,null,2).slice(0,4e3),Ul(je),Se("已返回登录信息,见下方")}catch($){Se("发起失败:"+$.message)}},[]),Nt=y.useCallback(async()=>{const O=K.trim();if(!O){ut("请填要查配额的用户 id");return}try{const Y=await lt(P.adminQuotaUser(O));ut("用户 "+O.slice(0,8)+":本月用量 "+sr(Y.used_usd_micros)+" / 配额 "+sr(Y.quota_usd_micros)+(Y.remaining_usd_micros!==null&&Y.remaining_usd_micros!==void 0?"(剩余 "+sr(Y.remaining_usd_micros)+")":"")+" | 月份 "+(Y.month||""))}catch(Y){ut(cr(Y))}},[K]),_l=y.useCallback(async()=>{if(window.confirm("确定手动触发 sweep 吗?超阈值的陈旧 running 任务会被标为 interrupted。"))try{const Y=(await lt(P.adminSweep,"POST",{})).swept||[];ut("sweep 完成,清理 "+Y.length+" 个任务"+(Y.length?":"+Y.join("、"):""))}catch(O){ut(cr(O))}},[]),De=y.useCallback(async()=>{try{const O=await lt(P.adminBackup,"POST",{});ut("备份完成:backend="+O.backend+" | 路径 "+O.path+" | 大小 "+O.size_bytes+" 字节 | 任务 "+O.task_count+" 行")}catch(O){ut(cr(O))}},[]);return{keySaved:i,keyState:m,quotaText:_,healthText:N,adminGate:B,adminBody:G,adminResult:V,adminUserId:K,setAdminUserId:tt,projects:it,projectId:Xt,setProjectId:ge,projectMsg:Oe,githubRepos:fe,workers:ht,workersSummary:Pt,workersError:re,members:tl,memberMsg:w,memberError:et,skills:vt,apps:ze,mcpServers:v,skillMsg:W,skillError:yt,mcpMsg:xt,mcpOauthMsg:at,mcpOauthBox:Za,envs:Le,envCount:At,envMsg:Wl,envRepoLine:va,envRepoBar:el,envRepoMsg:le,envSelect:ol,setEnvSelect:pe,models:Hl,modelCustomVisible:Bl,refreshHealth:ml,loadQuota:ql,probeAdmin:La,refreshProjects:Wt,loadGithubRepos:$l,loadRelayModels:yn,refreshWorkers:gn,refreshEnvs:Ke,refreshMembers:ka,refreshSkills:ya,saveKeyInput:fu,clearKeyInput:ru,createProject:Il,createEnv:be,archiveEnv:hl,saveEnvRepo:xe,inviteMember:H,changeMemberRole:D,reloadMcp:Z,mcpOauth:F,adminQuota:Nt,adminSweep:_l,adminBackup:De}}function Fy(){const[i,r]=y.useState([]),[m,o]=y.useState(""),[_,M]=y.useState(""),[N,E]=y.useState(""),[B,z]=y.useState(""),G=y.useCallback(async(K,tt)=>{const it=K!==void 0?K:tt;if(!it){r([]);return}try{const jt=await lt(P.tasks(it));r(jt.tasks||[]),o("")}catch(jt){o("加载失败:"+jt.message)}},[]),j=y.useMemo(()=>{const K=B.trim().toLowerCase();return i.filter(tt=>!(_&&tt.status!==_||N&&(tt.executor||"")!==N||K&&!((tt.id||"")+" "+(tt.prompt||"")).toLowerCase().includes(K)))},[i,_,N,B]),V=j.length+" 个任务(共 "+i.length+" 个)",ut=y.useCallback(async(K,tt)=>{const it={...K};tt.length&&(it.upload_id=await Ly(tt));const jt=await lt(P.tasksCreate,"POST",it);return"任务已建:"+de(jt.id)},[]);return{tasks:i,taskError:m,statusFilter:_,setStatusFilter:M,executorFilter:N,setExecutorFilter:E,searchText:B,setSearchText:z,filteredTasks:j,taskCountText:V,refreshTasks:G,dispatch:ut}}function Wy(i,r){const[m,o]=y.useState(null),[_,M]=y.useState(""),[N,E]=y.useState(""),[B,z]=y.useState(""),[G,j]=y.useState(""),[V,ut]=y.useState(!1),[K,tt]=y.useState(""),[it,jt]=y.useState([]),[Xt,ge]=y.useState([]),[Oe,Jt]=y.useState(""),[fe,st]=y.useState([]),[ht,Ft]=y.useState(""),[Pt,kt]=y.useState(""),[re,Dt]=y.useState([]),[tl,U]=y.useState(""),[w,k]=y.useState(""),[et,Tt]=y.useState(""),[vt,te]=y.useState(""),[ze,ne]=y.useState([]),[v,R]=y.useState([]),[W,J]=y.useState(""),[yt,dt]=y.useState([]),[xt,Q]=y.useState(""),[at,Se]=y.useState(""),[Za,Ul]=y.useState(""),[Le,Yt]=y.useState(""),[At,Re]=y.useState([]),[Wl,ee]=y.useState(""),[va,El]=y.useState(null),el=y.useRef(null),ke=y.useRef(null),le=y.useRef(null);le.current=m;const qe=y.useCallback(()=>{if(el.current){try{el.current.abort()}catch{}el.current=null}ke.current&&(clearTimeout(ke.current),ke.current=null)},[]),ol=y.useCallback(async H=>{try{const D=await lt(P.taskAgents(H));ge(D.agents||[]),Jt(D.note||"")}catch(D){ge([]),Jt("子代理链路加载失败:"+D.message)}},[]),pe=y.useCallback(H=>{ke.current&&clearTimeout(ke.current),ke.current=setTimeout(()=>void ol(H),1e3)},[ol]),Hl=y.useCallback(async H=>{if(!H.coordinator_parts){jt([]);return}try{const D=await lt(P.tasksByParent(H.project_id||"",H.id));jt(D.tasks||[])}catch{jt([])}},[]),dl=y.useCallback(async H=>{try{const D=await lt(P.taskFollowups(H));ne(D.followups||[])}catch{ne([])}},[]),Bl=y.useCallback(async H=>{try{const D=await lt(P.taskChatHistory(H));El(D)}catch{El(null)}},[]),we=y.useCallback(async H=>{var Z;const D=H||((Z=le.current)==null?void 0:Z.id);if(D)try{const F=await lt(P.taskReviews(D));st(F.reviews||[]),kt("")}catch(F){kt("评审队列加载失败:"+F.message)}},[]),Vt=y.useCallback(async H=>{var Z;const D=H||((Z=le.current)==null?void 0:Z.id);if(D)try{const F=await lt(P.taskApprovals(D));R(F.approvals||[])}catch(F){J("审批队列加载失败:"+F.message)}},[]),ll=y.useCallback(async H=>{try{const D=await lt(P.taskMessages(H));dt(D.messages||[])}catch{dt([])}},[]),ml=y.useCallback(async H=>{try{const D=await lt(P.taskThread(H)),Z=D.thread||D;let F=Z.turns;F==null&Line truncated
|
||||
|
||||
`);Gt=$t.pop()||"";for(const Le of $t){const li=Le.split(`
|
||||
`).filter(ke=>ke.indexOf("data: ")===0).map(ke=>ke.slice(6)).join(`
|
||||
`);if(li)try{const ke=JSON.parse(li);if(ke.type==="snapshot")$=ke.entries||[],Object.keys(Ut).forEach(Be=>delete Ut[Be]),$.forEach((Be,ai)=>{Be.id&&(Ut[Be.id]=ai)}),ha=ke.note||"",Dt([...$]),H(ha),ol(U);else if(ke.type==="entry"){const Be=ke.entry;Be.id&&Be.id in Ut?$[Ut[Be.id]]=Be:(Be.id&&(Ut[Be.id]=$.length),$.push(Be)),Dt([...$]),ol(U)}else ke.type==="done"&&(ll=!0,He=ke.status,L(He))}catch{}}if(ll)break}}catch(me){if(me&&me.name==="AbortError")return;H("链路流中断:"+me.message);return}ll&&r()},[Ve,ol,r]),Jl=y.useCallback(async U=>{Ve();try{const z=await lt(et.task(U));o(z);let Y="";z.result_text&&(Y+=`=== result ===
|
||||
`+z.result_text.slice(0,8e3)+`
|
||||
`),z.error_text&&(Y+=`=== error ===
|
||||
`+z.error_text.slice(0,8e3)+`
|
||||
`),Y||(Y="(暂无 result/error 输出)"),M(Y),E(""),ut(!1),P(""),Q(""),Yt(""),oa(z.status);const $=i(z.project_id),Ut=$==="maintainer"||$==="owner",ha=$==="contributor"||Ut,ll=nu.includes(z.status),He=[];["failed","interrupted","merge-failed"].includes(z.status)&&Ut||He.push("重试:不可用——仅 failed/interrupted/merge-failed 可重试"+(Ut?"":",且仅 maintainer 可重试")),!ll&&ha||He.push("取消:不可用——"+(ll?"终态任务不可取消":"仅项目成员(contributor 及以上)可取消")),z.status==="completed"&&Ut||He.push("合并:不可用——仅 completed 可合并"+(Ut?"":",且仅 maintainer 可合并")),z.status==="merged"&&Ut||He.push("提 PR:不可用——仅 merged 可提 PR"+(Ut?"":",且仅 maintainer 可提 PR")),R(He.join(";")),j(""),da(U,z.status),tl(U),Dl(z),ie(U),Ul(U),dl(U),Kt(U),Ue(U),el(U)}catch(z){o(null),M("加载失败:"+z.message)}},[Ve,da,tl,Dl,ie,Ul,dl,Kt,Ue,el,i]),Xa=y.useCallback(async()=>{const U=kt.current;if(U){try{const z=await lt(et.task(U.id));o(z);let Y="";z.result_text&&(Y+=`=== result ===
|
||||
`+z.result_text.slice(0,8e3)+`
|
||||
`),z.error_text&&(Y+=`=== error ===
|
||||
`+z.error_text.slice(0,8e3)+`
|
||||
`),M(Y||"(暂无 result/error 输出)")}catch{}ie(U.id),Ue(U.id),el(U.id),Dl(U),tl(U.id),Ul(U.id),dl(U.id),Kt(U.id)}},[ie,Ue,el,Dl,tl,Ul,dl,Kt]),ae=y.useCallback(async U=>{const z=kt.current;if(!z)return;const Y={cancel:"确定取消该任务吗?",merge:"确定把 completed 任务标为 merged 吗?",pr:"确定为该任务提 PR 吗?(会在仓库真实建 PR)"};if(!(Y[U]&&!window.confirm(Y[U])))try{const $=await lt(et.taskAction(z.id,U),"POST",{});j(U+" 已提交:"+JSON.stringify($).slice(0,300)),r(),await Jl(z.id)}catch($){j(U+" 失败:"+$.message)}},[Jl,r]),iu=y.useCallback(async()=>{const U=kt.current;if(U)try{const z=await lt(et.taskCost(U.id));E(JSON.stringify(z,null,2).slice(0,4e3))}catch(z){E("查成本失败:"+z.message)}},[]),mn=y.useCallback(async U=>{const z=kt.current;if(z){if(!U.trim()){P("分支消息必填");return}if(!nu.includes(z.status)){P("仅终态任务可分支");return}try{const Y=await lt(et.taskFork(z.id),"POST",{message:U.trim()});P("分支任务已建:"+Y.id.slice(0,8)),r()}catch(Y){P("分支失败:"+Y.message)}}},[r]),hn=y.useCallback(async U=>{const z=kt.current;if(z)try{await lt(et.taskReviews(z.id),"POST",U),Wt("评审已登记(requested)"),await Ue(z.id)}catch(Y){Wt("登记评审需求失败:"+Y.message)}},[Ue]),Ze=y.useCallback(U=>{const z=kt.current;if(!z)return;const Y=window.prompt("输入该条评审的 findings(将写回记录并标为 completed):");Y!==null&<(et.taskReviewFindings(z.id,U),"POST",{findings:Y}).then(()=>void Ue(z.id)).catch($=>Wt("写 findings 失败:"+$.message))},[Ue]),Va=y.useCallback((U,z)=>{const Y=kt.current;if(!Y)return;const $=z==="approved"?"approve":"deny";lt(et.taskApprovalDecision(Y.id,U,$),"POST",{}).then(()=>{K("已记录"+(z==="approved"?"允许":"拒绝")+"(当前版本只留回执,不拦截实时行为)"),el(Y.id)}).catch(Ut=>K("决定失败:"+Ut.message))},[el]),ma=y.useCallback(async(U,z,Y)=>{const $=kt.current;if($){if(!z.trim()){Q("目标任务 id 不能为空");return}if(!Y.trim()){Q("消息正文不能为空");return}try{const Ut=await lt(et.taskMessagesTo(z.trim()),"POST",{from_task_id:$.id,body:Y.trim(),mode:U});Q("已发送("+Ut.delivery+")"),await Ul($.id)}catch(Ut){Q("发送失败:"+Ut.message)}}},[Ul]),cu=y.useCallback((U,z)=>{const Y=kt.current;if(!Y)return;if(!["completed","failed","interrupted"].includes(Y.status)){Yt("仅 completed/failed/interrupted 任务可操作线程(merged/merge-failed 不可改,服务端同样 409)");return}const $={};if(U==="rename"){if(!z.trim()){Yt("新线程名不能为空");return}$.name=z.trim()}lt(et.taskThreadAction(Y.id,U),"POST",$).then(Ut=>{Yt(U+" 已提交:"+JSON.stringify(Ut).slice(0,300)),dl(Y.id),Kt(Y.id)}).catch(Ut=>Yt(U+" 失败:"+Ut.message))},[dl,Kt]),su=y.useCallback(async U=>{const z=kt.current;if(!z)return;const Y=U.trim();if(!Y){le("消息不能为空");return}try{nu.includes(z.status)&&z.status!=="merged"?(await lt(et.taskContinue(z.id),"POST",{message:Y}),le("已重新排队,同一会话继续"),r(),await Jl(z.id)):["queued","running"].includes(z.status)?(await lt(et.taskFollowups(z.id),"POST",{body:Y,mode:"direct"}),le("已直接发送"),await ie(z.id)):le("merged/merge-failed 任务不可继续,也不可发后续消息")}catch($){le("发送失败:"+$.message)}},[Jl,r,ie]),Fl=y.useCallback(async()=>{const U=kt.current;if(!U){ee("先选一个任务");return}try{const z=await lt(et.taskTrace(U.id)),Y=new Blob([JSON.stringify({task_id:U.id,status:z.status,note:z.note||"",count:(z.entries||[]).length,entries:z.entries||[]},null,2)],{type:"application/json"}),$=document.createElement("a");$.href=URL.createObjectURL(Y),$.download="trace-"+U.id.slice(0,8)+".json",document.body.appendChild($),$.click(),$.remove(),setTimeout(()=>URL.revokeObjectURL($.href),1e3),ee("已下载完整链路 "+(z.entries||[]).length+" 条")}catch(z){ee("下载失败:"+z.message)}},[]);return y.useEffect(()=>()=>Ve(),[Ve]),{currentTask:m,detailBody:_,actionMsg:N,actionReasons:B,costBox:G,forkVisible:J,setForkVisible:ut,forkMsg:k,coordChildren:it,agents:Vt,agentsNote:Me,reviews:oe,reviewMsg:ht,reviewError:te,traceEntries:de,traceNote:Pe,traceDone:w,traceKind:tt,setTraceKind:jt,traceMsg:vt,followups:Oe,approvals:v,approvalMsg:F,peerMessages:yt,peerMsg:xt,threadText:at,threadGateNote:Qa,threadMsg:Xe,threadList:Ct,chatMsg:Kl,showDetail:Jl,refreshDetailLists:Xa,doAction:ae,taskCost:iu,forkTask:mn,postReview:hn,submitFindings:Ze,decideApproval:Va,postPeer:ma,threadAction:cu,sendChatMessage:su,downloadTrace:Fl,refreshReviews:()=>void Ue(),refreshApprovals:()=>void el()}}function $y(i){const[r,m]=y.useState([]),[o,_]=y.useState(""),[M,N]=y.useState(null),[j,B]=y.useState(""),R=y.useCallback(async ut=>{const k=ut!==void 0?ut:i();if(!k){m([]);return}try{const P=await lt(et.projectAutomations(k));m(P.automations||[])}catch(P){_("自动化加载失败:"+P.message)}},[i]),G=y.useCallback(async(ut,k,P,it)=>{const Tt=i();if(!Tt){_("先选项目"Line truncated
|
||||
`);Ue=Pl.pop()||"";for(const ui of Pl){const ou=ui.split(`
|
||||
`).filter(Ye=>Ye.indexOf("data: ")===0).map(Ye=>Ye.slice(6)).join(`
|
||||
`);if(ou)try{const Ye=JSON.parse(ou);if(Ye.type==="snapshot")F=Ye.entries||[],Object.keys(Nt).forEach(He=>delete Nt[He]),F.forEach((He,ii)=>{He.id&&(Nt[He.id]=ii)}),_l=Ye.note||"",Dt([...F]),U(_l),pe(H);else if(Ye.type==="entry"){const He=Ye.entry;He.id&&He.id in Nt?F[Nt[He.id]]=He:(He.id&&(Nt[He.id]=F.length),F.push(He)),Dt([...F]),pe(H)}else Ye.type==="done"&&(De=!0,O=Ye.status,k(O))}catch{}}if(De)break}}catch(Y){if(Y&&Y.name==="AbortError")return;U("链路流中断:"+Y.message);return}De&&r()},[qe,pe,r]),$l=y.useCallback(async H=>{qe();try{const D=await lt(P.task(H));o(D);let Z="";D.result_text&&(Z+=`=== result ===
|
||||
`+D.result_text.slice(0,8e3)+`
|
||||
`),D.error_text&&(Z+=`=== error ===
|
||||
`+D.error_text.slice(0,8e3)+`
|
||||
`),Z||(Z="(暂无 result/error 输出)"),M(Z),j(""),ut(!1),tt(""),Q(""),Yt(""),La(D.status);const F=i(D.project_id),Nt=F==="maintainer"||F==="owner",_l=F==="contributor"||Nt,De=cu.includes(D.status),O=[];["failed","interrupted","merge-failed"].includes(D.status)&&Nt||O.push("重试:不可用——仅 failed/interrupted/merge-failed 可重试"+(Nt?"":",且仅 maintainer 可重试")),!De&&_l||O.push("取消:不可用——"+(De?"终态任务不可取消":"仅项目成员(contributor 及以上)可取消")),D.status==="completed"&&Nt||O.push("合并:不可用——仅 completed 可合并"+(Nt?"":",且仅 maintainer 可合并")),D.status==="merged"&&Nt||O.push("提 PR:不可用——仅 merged 可提 PR"+(Nt?"":",且仅 maintainer 可提 PR")),z(O.join(";")),E(""),Wt(H,D.status),ol(H),Hl(D),Bl(H),dl(H),ll(H),ml(H),ql(H),we(H),Vt(H)}catch(D){o(null),M("加载失败:"+D.message)}},[qe,Wt,ol,Hl,Bl,dl,ll,ml,ql,we,Vt,i]),yn=y.useCallback(async()=>{const H=le.current;if(H){try{const D=await lt(P.task(H.id));o(D);let Z="";D.result_text&&(Z+=`=== result ===
|
||||
`+D.result_text.slice(0,8e3)+`
|
||||
`),D.error_text&&(Z+=`=== error ===
|
||||
`+D.error_text.slice(0,8e3)+`
|
||||
`),M(Z||"(暂无 result/error 输出)")}catch{}dl(H.id),Bl(H.id),we(H.id),Vt(H.id),Hl(H),ol(H.id),ll(H.id),ml(H.id),ql(H.id)}},[dl,Bl,we,Vt,Hl,ol,ll,ml,ql]),gn=y.useCallback(async H=>{const D=le.current;if(!D)return;const Z={cancel:"确定取消该任务吗?",merge:"确定把 completed 任务标为 merged 吗?",pr:"确定为该任务提 PR 吗?(会在仓库真实建 PR)"};if(!(Z[H]&&!window.confirm(Z[H])))try{const F=await lt(P.taskAction(D.id,H),"POST",{});E(H+" 已提交:"+JSON.stringify(F).slice(0,300)),r(),await $l(D.id)}catch(F){E(H+" 失败:"+F.message)}},[$l,r]),Ke=y.useCallback(async()=>{const H=le.current;if(H)try{const D=await lt(P.taskCost(H.id));j(JSON.stringify(D,null,2).slice(0,4e3))}catch(D){j("查成本失败:"+D.message)}},[]),ka=y.useCallback(async H=>{const D=le.current;if(D){if(!H.trim()){tt("分支消息必填");return}if(!cu.includes(D.status)){tt("仅终态任务可分支");return}try{const Z=await lt(P.taskFork(D.id),"POST",{message:H.trim()});tt("分支任务已建:"+Z.id.slice(0,8)),r()}catch(Z){tt("分支失败:"+Z.message)}}},[r]),ya=y.useCallback(async H=>{const D=le.current;if(D)try{await lt(P.taskReviews(D.id),"POST",H),Ft("评审已登记(requested)"),await we(D.id)}catch(Z){Ft("登记评审需求失败:"+Z.message)}},[we]),fu=y.useCallback(H=>{const D=le.current;if(!D)return;const Z=window.prompt("输入该条评审的 findings(将写回记录并标为 completed):");Z!==null&<(P.taskReviewFindings(D.id,H),"POST",{findings:Z}).then(()=>void we(D.id)).catch(F=>Ft("写 findings 失败:"+F.message))},[we]),ru=y.useCallback((H,D)=>{const Z=le.current;if(!Z)return;const F=D==="approved"?"approve":"deny";lt(P.taskApprovalDecision(Z.id,H,F),"POST",{}).then(()=>{J("已记录"+(D==="approved"?"允许":"拒绝")+"(当前版本只留回执,不拦截实时行为)"),Vt(Z.id)}).catch(Nt=>J("决定失败:"+Nt.message))},[Vt]),Il=y.useCallback(async(H,D,Z)=>{const F=le.current;if(F){if(!D.trim()){Q("目标任务 id 不能为空");return}if(!Z.trim()){Q("消息正文不能为空");return}try{const Nt=await lt(P.taskMessagesTo(D.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]),be=y.useCallback((H,D)=>{const Z=le.current;if(!Z)return;if(!["completed","failed","interrupted"].includes(Z.status)){Yt("仅 completed/failed/interrupted 任务可操作线程(merged/merge-failed 不可改,服务端同样 409)");return}const F={};if(H==="rename"){if(!D.trim()){Yt("新线程名不能为空");return}F.name=D.trim()}lt(P.taskThreadAction(Z.id,H),"POST",F).then(Nt=>{Yt(H+" 已提交:"+JSON.stringify(Nt).slice(0,300)),ml(Z.id),ql(Z.id)}).catch(Nt=>Yt(H+" 失败:"+Nt.message))},[ml,ql]),hl=y.useCallback(async H=>{const D=le.current;if(!D)return;const Z=H.trim();if(!Z){ee("消息不能为空");return}try{cu.includes(D.status)&&D.status!=="merged"?(await lt(P.taskContinue(D.id),"POST",{message:Z}),ee("已重新排队,同一会话继续"),r(),await $l(D.id)):["queued","running"].includes(D.status)?(await lt(P.taskFollowups(D.id),"POST",{body:Z,mode:"direct"}),ee("已直接发送"),await dl(D.id),await Bl(D.id)):ee("merged/merge-failed 任务不可继续,也不可发后续消息")}catch(F){ee("发送失败:"+F.message)}},[$l,r,dl,Bl]),xe=y.useCallback(async()=>{const H=le.current;if(!H){te("先选一个任务");return}try{const D=await lt(P.taskTrace(H.id)),Z=new Blob([JSON.stringify({task_id:H.id,status:D.status,note:D.note||"",count:(D.entries||[]).length,entries:D.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("已下载完整链路 "+(D.entries||[]).length+" 条")}catch(D){te("下载失败:"+D.message)}},[]);return y.useEffect(()=>()=>qe(),[qe]),{currentTask:m,detailBody:_,actionMsg:N,actionReasons:B,costBox:G,forkVisible:V,setForkVisible:ut,forkMsg:K,coordChildren:it,agents:Xt,agentsNote:Oe,reviews:fe,reviewMsg:ht,reviewError:Pt,traceEntries:re,traceNote:tl,traceDone:w,traceKind:et,setTraceKind:Tt,traceMsg:vt,followups:ze,approvals:v,approvalMsg:W,peerMessages:yt,peerMsg:xt,threadText:at,threadGateNote:Za,threadMsg:Le,threadList:At,chatMsg:Wl,chatHistory:va,showDetail:$l,refreshDetailLists:yn,doAction:gn,taskCost:Ke,forkTask:ka,postReview:ya,submitFindings:fu,decideApproval:ru,postPeer:Il,threadAction:be,sendChatMessage:hl,downloadTrace:xe,refreshReviews:()=>void we(),refreshApprovals:()=>void Vt()}}function $y(i){const[r,m]=y.useState([]),[o,_]=y.useState(""),[M,N]=y.useState(null),[E,B]=y.useState(""),z=y.useCallback(async ut=>{const K=ut!==void 0?ut:i();if(!K){m([]);return}try{const tt=await lt(P.projectAutomations(K));m(tt.automations||[])}catch(tt){_("自动化加载失败:"+tt.message)}},[i]),G=y.useCallback(async(ut,K,tt,iLine truncated
|
||||
<style rel="stylesheet" crossorigin>: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}*{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}a{color:var(--accent);text-underline-offset:3px}:focus-visible{outline:3px 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}.err{color:var(--danger)}.ok{color:var(--success)}button{cursor:pointer;font-weight:600;border-radius:999px}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)}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}.muse-app{display:grid;grid-template-columns:56px 260px minmax(0,1fr) 300px;height:100vh;background:#fff}.muse-app.no-right{grid-template-columns:56px 260px minmax(0,1fr)}.icon-rail{border-right:1px solid var(--line);display:flex;flex-direction:column;align-items:center;gap:4px;padding:12px 0;overflow-y:auto}.icon-rail button{width:40px;height:40px;border:0;border-radius:12px;background:transparent;display:grid;place-items:center;color:var(--text);padding:0;flex:none}.icon-rail button:hover{background:#f2f2f2}.icon-rail button.active{background:var(--accent-soft);color:var(--accent)}.icon-rail svg{width:22px;height:22px}.list-col{border-right:1px solid var(--line);overflow-y:auto;padding:16px 12px;min-width:0}.search-box{display:flex;align-items:center;gap:8px;border:1px solid var(--line);border-radius:999px;padding:8px 12px;background:#fff}.search-box svg{flex:none;color:var(--muted)}.search-box input{border:0;padding:0;flex:1;min-width:0;font-size:14px;background:transparent}.search-box input:focus{box-shadow:none;outline:none}.list-section-head{display:flex;align-items:center;justify-content:space-between;padding:16px 4px 8px;font-weight:600;font-size:14px}.list-section-head button{width:28px;height:28px;padding:0;display:grid;place-items:center;border-radius:50%}.muse-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}.muse-item{padding:10px 12px;border-radius:12px;cursor:pointer;overflow:hidden}.muse-item:hover{background:#f9fafb}.muse-item.selected{background:#f2f2f2}.muse-item-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:14px}.muse-item-sub{display:flex;gap:6px;align-items:center;color:var(--muted);font-size:12px;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.task-dot{width:8px;height:8px;border-radius:50%;background:currentColor;flex:none}.center-col{min-width:0;display:flex;flex-direction:column;overflow-y:auto;background:#fff}.center-topbar{position:sticky;top:0;z-index:5;display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#fff;border-bottom:1px solid var(--line)}.center-inner{max-width:760px;margin:0 auto;width:100%;padding:24px 20px 16px;display:flex;flex-direction:column;gap:20px;flex:1}.right-col{border-left:1px solid var(--line);overflow-y:auto;padding:20px 16px;display:flex;flex-direction:column;gap:20px;min-width:0}.right-profile{text-align:center;position:relative}.right-profile .icon-btn{position:absolute;top:0;right:0}.right-avatar{width:72px;height:72px;border-radius:50%;background:var(--accent-soft);color:var(--accent);display:grid;place-items:center;font-size:28px;font-weight:700;margin:0 auto}.right-name{font-weight:700;font-size:17px;margin-top:10px}.right-status{color:var(--green);font-size:13px;font-weight:600}.right-status.off{color:var(--muted)}.right-icon-row{display:flex;gap:4px;justify-content:space-between;border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:10px 4px}.right-icon-row button{width:36px;height:36px;border:0;border-radius:12px;background:transparent;display:grid;place-items:center;color:var(--text);padding:0}.right-icon-row button:hover{background:#f2f2f2}.right-icon-row button.active{background:var(--accent-sLine truncated
|
||||
</head>
|
||||
<body>
|
||||
|
||||
+232
-1
@@ -1215,6 +1215,97 @@ def _followup_to_dict(row):
|
||||
}
|
||||
|
||||
|
||||
def _ensure_continue_messages_table(conn):
|
||||
"""幂等建继续消息表:POST /continue 接受的每条用户消息都在
|
||||
与状态改回 queued 的同一事务里落盘(见 _continue_task),
|
||||
否则继续消息只活在 resume_message 列里、worker 跑完即清,
|
||||
聊天历史永远拼不出用户那一半。老库第一次跑到这里时自动
|
||||
补表,不丢数据;此前已发生的继续(audit 里 task_continued
|
||||
有、表里没有)无法追回,chat-history 会如实标注缺口数,
|
||||
不编造。列写法与 followups 表一致(DOUBLE PRECISION 在
|
||||
SQLite 与 Postgres 下都可用)。"""
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS task_continue_messages("
|
||||
"id TEXT PRIMARY KEY, "
|
||||
"task_id TEXT, "
|
||||
"project_id TEXT, "
|
||||
"body TEXT, "
|
||||
"created_by TEXT, "
|
||||
"created_at DOUBLE PRECISION)"
|
||||
)
|
||||
conn.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_continue_messages_task"
|
||||
" ON task_continue_messages(task_id, created_at, id)")
|
||||
|
||||
|
||||
def _continue_message_to_dict(row):
|
||||
return {
|
||||
"id": row["id"],
|
||||
"task_id": row["task_id"],
|
||||
"project_id": row["project_id"],
|
||||
"body": row["body"],
|
||||
"created_by": row["created_by"],
|
||||
"created_at": row["created_at"],
|
||||
}
|
||||
|
||||
|
||||
def _chat_message_from_trace(entry):
|
||||
"""链路条目 -> 聊天消息(与 React 的 mapTrace.ts 同款映射,
|
||||
两边改动要同步):message 是助手回复,exec/file/tool/call
|
||||
是工具气泡,plan/reasoning/agent-start/agent-done 是居中
|
||||
状态提示。链路条目本身没有发送方字段,这里不编造角色,
|
||||
只按 kind 分流。"""
|
||||
kind = entry.get("kind") or ""
|
||||
eid = entry.get("id") or ""
|
||||
if kind == "message":
|
||||
text = ((("[" + entry["phase"] + "] ") if entry.get("phase")
|
||||
else "") + (entry.get("text") or ""))
|
||||
return {"id": eid, "role": "assistant", "source": "trace",
|
||||
"kind": kind, "label": "助手", "text": text}
|
||||
if kind in ("exec", "file", "tool", "call"):
|
||||
label = {"exec": "命令", "file": "文件",
|
||||
"tool": "工具"}.get(kind, "调用")
|
||||
if kind == "exec":
|
||||
# 与 React traceLineText 同款:缺失字段为空,不渲染 None。
|
||||
def _s(v):
|
||||
return "" if v is None else str(v)
|
||||
text = "%s(exit=%s,%sms,cwd=%s)" % (
|
||||
entry.get("command") or "", _s(entry.get("exitCode")),
|
||||
_s(entry.get("durationMs")), entry.get("cwd") or "")
|
||||
elif kind == "file":
|
||||
text = "%s %s %s" % (entry.get("path") or "",
|
||||
entry.get("change") or "",
|
||||
entry.get("status") or "")
|
||||
elif kind == "tool":
|
||||
text = "%s/%s %s" % (entry.get("server") or "",
|
||||
entry.get("tool") or "",
|
||||
entry.get("status") or "")
|
||||
else:
|
||||
text = entry.get("name") or ""
|
||||
return {"id": eid, "role": "tool", "source": "trace",
|
||||
"kind": kind, "label": label, "text": text}
|
||||
if kind == "plan":
|
||||
return {"id": eid, "role": "status", "source": "trace",
|
||||
"kind": kind, "text": entry.get("text") or ""}
|
||||
if kind == "reasoning":
|
||||
return {"id": eid, "role": "status", "source": "trace",
|
||||
"kind": kind,
|
||||
"text": "思考:" + (entry.get("summary") or "")}
|
||||
if kind == "agent-start":
|
||||
return {"id": eid, "role": "status", "source": "trace",
|
||||
"kind": kind,
|
||||
"text": "子代理开始:" + (entry.get("name") or "")
|
||||
+ (("(" + entry["prompt"] + ")")
|
||||
if entry.get("prompt") else "")}
|
||||
if kind == "agent-done":
|
||||
return {"id": eid, "role": "status", "source": "trace",
|
||||
"kind": kind,
|
||||
"text": "子代理结束:" + (entry.get("name") or "")
|
||||
+ " " + (entry.get("status") or "")}
|
||||
return {"id": eid, "role": "status", "source": "trace",
|
||||
"kind": kind, "text": json.dumps(entry)[:300]}
|
||||
|
||||
|
||||
def _ensure_environments_table(conn):
|
||||
"""幂等建环境表:server init_db 与各环境接口都可调它,老库
|
||||
第一次跑到这里时自动补表,不丢数据。类型写法与 followups
|
||||
@@ -1420,6 +1511,7 @@ def init_db(db_path):
|
||||
migrate_tasks_table(conn)
|
||||
migrate_projects_table(conn)
|
||||
_ensure_followups_table(conn)
|
||||
_ensure_continue_messages_table(conn)
|
||||
_ensure_environments_table(conn)
|
||||
_ensure_quotas_table(conn)
|
||||
_backfill_default_quotas(conn)
|
||||
@@ -1454,6 +1546,7 @@ def init_db(db_path):
|
||||
migrate_tasks_table(conn)
|
||||
migrate_projects_table(conn)
|
||||
_ensure_followups_table(conn)
|
||||
_ensure_continue_messages_table(conn)
|
||||
_ensure_environments_table(conn)
|
||||
_ensure_quotas_table(conn)
|
||||
_backfill_default_quotas(conn)
|
||||
@@ -1983,6 +2076,9 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
self._create_followup(user_id, rest[: -len("/followups")])
|
||||
elif rest.endswith("/followups") and method == "GET":
|
||||
self._list_followups(user_id, rest[: -len("/followups")])
|
||||
elif rest.endswith("/chat-history") and method == "GET":
|
||||
self._task_chat_history(
|
||||
user_id, rest[: -len("/chat-history")])
|
||||
elif rest.endswith("/messages") and method == "POST":
|
||||
self._create_peer_message(user_id, rest[: -len("/messages")])
|
||||
elif rest.endswith("/messages") and method == "GET":
|
||||
@@ -4476,6 +4572,124 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
self._send_json(200, {
|
||||
"followups": [_followup_to_dict(r) for r in rows]})
|
||||
|
||||
def _task_chat_history(self, user_id, task_id):
|
||||
"""GET /tasks/{id}/chat-history:真正的交替历史,一次性
|
||||
按时间交错拼好返回,前端不再猜顺序。
|
||||
|
||||
来源(全部是后端已有记录,不编造):
|
||||
- 首条 prompt:任务行 prompt + created_at(用户)
|
||||
- 继续消息:task_continue_messages(用户,部署后才入库)
|
||||
- 后续消息:task_followups(用户,含任务间互发)
|
||||
- 助手/工具/状态:链路 message/exec/file/tool/call/
|
||||
plan/reasoning/agent-* 条目(trace 的 ts 是毫秒时间戳,
|
||||
无 ts 的条目按链路顺序贴在上一条有 ts 的条目之后,
|
||||
不给它伪造时间戳——输出里 ts_ms 为 null)
|
||||
|
||||
排序:按 ts_ms 升序,同一毫秒用户消息在链路条目之前
|
||||
(用户消息是触发该轮的原因),其余按原顺序稳定排序。
|
||||
旧任务缺口:部署前发生的继续只在 audit 里留了
|
||||
task_continued(只有任务 id,没有正文),无法追回;
|
||||
legacy_continue_count = audit 次数 - 已入库条数,>0 时
|
||||
前端必须在聊天窗上标“此前继续消息未入库,历史不完整”,
|
||||
不能假装这就是全部对话。异机 worktree 时链路读不到,
|
||||
messages 里只有用户侧 + note,不谎报无过程。"""
|
||||
row = self._fetch_task(task_id)
|
||||
if row is None:
|
||||
self._send_json(404, {"error": "task not found"})
|
||||
return
|
||||
task = _task_to_dict(row)
|
||||
with _auth_store(self.server.db_path) as store:
|
||||
if not store.check_permission(
|
||||
user_id, task["project_id"], "read"):
|
||||
self._send_json(403, {"error": "forbidden"})
|
||||
return
|
||||
entries, trace_note = self._trace_snapshot(task)
|
||||
conn = self._db()
|
||||
try:
|
||||
_ensure_continue_messages_table(conn)
|
||||
_ensure_followups_table(conn)
|
||||
cont_rows = conn.execute(
|
||||
"SELECT * FROM task_continue_messages WHERE task_id = ?"
|
||||
" ORDER BY created_at, id",
|
||||
(task_id,),
|
||||
).fetchall()
|
||||
fu_rows = conn.execute(
|
||||
"SELECT * FROM task_followups WHERE task_id = ?"
|
||||
" ORDER BY created_at, id",
|
||||
(task_id,),
|
||||
).fetchall()
|
||||
legacy = None
|
||||
try:
|
||||
aud = conn.execute(
|
||||
"SELECT COUNT(*) FROM audit_log"
|
||||
" WHERE action = 'task_continued' AND detail = ?",
|
||||
(task_id,),
|
||||
).fetchone()
|
||||
legacy = max(0, int(aud[0]) - len(cont_rows))
|
||||
except Exception:
|
||||
legacy = None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
merged = []
|
||||
|
||||
def _ms(seconds):
|
||||
try:
|
||||
return int(float(seconds) * 1000)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
if task.get("prompt"):
|
||||
merged.append((0, _ms(task.get("created_at")), {
|
||||
"id": "task-prompt", "role": "user", "source": "prompt",
|
||||
"label": "用户", "text": task["prompt"],
|
||||
"ts_ms": _ms(task.get("created_at"))}))
|
||||
for r in cont_rows:
|
||||
d = _continue_message_to_dict(r)
|
||||
merged.append((0, _ms(d["created_at"]), {
|
||||
"id": "continue-" + d["id"], "role": "user",
|
||||
"source": "continue", "label": "用户",
|
||||
"text": d["body"], "ts_ms": _ms(d["created_at"])}))
|
||||
for r in fu_rows:
|
||||
d = _followup_to_dict(r)
|
||||
merged.append((0, _ms(d["created_at"]), {
|
||||
"id": "followup-" + d["id"], "role": "user",
|
||||
"source": "followup", "label": "后续消息",
|
||||
"text": d["body"], "ts_ms": _ms(d["created_at"])}))
|
||||
last_trace_ms = _ms(task.get("created_at"))
|
||||
for e in entries:
|
||||
if not isinstance(e, dict):
|
||||
continue
|
||||
if str(e.get("id") or "").startswith("fu-"):
|
||||
# 直接发送的后续消息已在链路里留了一条回声
|
||||
# (_append_direct_trace 写 kind=message),
|
||||
# followups 表里同一条已作用户消息返回;这里
|
||||
# 跳过回声,否则同一条消息会先以用户、再以
|
||||
# 助手各出现一次。
|
||||
continue
|
||||
msg = _chat_message_from_trace(e)
|
||||
ts = e.get("ts")
|
||||
if isinstance(ts, (int, float)):
|
||||
last_trace_ms = int(ts)
|
||||
sort_ms = int(ts)
|
||||
else:
|
||||
sort_ms = last_trace_ms
|
||||
msg["ts_ms"] = int(ts) if isinstance(ts, (int, float)) else None
|
||||
merged.append((1, sort_ms, msg))
|
||||
# 排序键:(时间, 用户优先, 原顺序)。merged 里用户项的
|
||||
# 第一元组是 0、链路项是 1;Python 排序稳定,同一
|
||||
# (时间, 优先级) 内保持插入顺序。
|
||||
merged.sort(key=lambda item: (
|
||||
item[1] if item[1] is not None else 0, item[0]))
|
||||
self._send_json(200, {
|
||||
"task_id": task_id,
|
||||
"status": task["status"],
|
||||
"messages": [m for _p, _t, m in merged],
|
||||
"continue_messages_stored": len(cont_rows),
|
||||
"legacy_continue_count": legacy,
|
||||
"trace_note": trace_note,
|
||||
})
|
||||
|
||||
def _create_peer_message(self, user_id, task_id):
|
||||
"""任务间互发消息(API 发送方):以 from_task_id 任务的名义
|
||||
给目标任务发消息。投递语义与 worker 路径共用 deliver 层,
|
||||
@@ -6623,13 +6837,30 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
% ", ".join(sets),
|
||||
tuple(vals),
|
||||
)
|
||||
conn.commit()
|
||||
if cur.rowcount != 1:
|
||||
# 状态已变化:更新 0 行,同一事务里什么都不留——
|
||||
# 尤其不能先把继续消息落盘再报 409,那会凭空多
|
||||
# 一条从未被执行的“用户消息”。
|
||||
conn.rollback()
|
||||
self._send_json(409, {
|
||||
"error": "任务状态已变化,请刷新后重试",
|
||||
"status": task["status"],
|
||||
})
|
||||
return
|
||||
# 继续消息与状态改回 queued 同一事务:要么一起生效,
|
||||
# 要么一起不生效,绝不出现“任务已重排队、消息却丢了”
|
||||
# 的半截状态。created_at 用本请求的 updated_at 时间戳
|
||||
# (vals[1]),与任务行的时间源一致,便于按时间交错
|
||||
# 拼聊天历史。
|
||||
_ensure_continue_messages_table(conn)
|
||||
conn.execute(
|
||||
"INSERT INTO task_continue_messages(id, task_id,"
|
||||
" project_id, body, created_by, created_at)"
|
||||
" VALUES(?, ?, ?, ?, ?, ?)",
|
||||
(uuid.uuid4().hex, task_id, task["project_id"],
|
||||
message, user_id, vals[1]),
|
||||
)
|
||||
conn.commit()
|
||||
new_row = conn.execute(
|
||||
"SELECT * FROM tasks WHERE id = ?", (task_id,)
|
||||
).fetchone()
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
"""继续消息持久化 + 交替历史单测:POST /continue 接受的用户
|
||||
消息必须落盘(与状态改回 queued 同一事务),GET
|
||||
/tasks/{id}/chat-history 按时间交错拼出 prompt / 继续 /
|
||||
后续消息 / 链路助手消息;409 的继续绝不能留 phantom 消息;
|
||||
部署前发生的继续在 audit 里只有任务 id、正文无法追回,
|
||||
接口要如实报 legacy_continue_count,不编造。
|
||||
|
||||
不跑真实模型回合、不联网:server 侧走本地临时端口 + 临时
|
||||
SQLite,链路通过 server.trace_dir 注入临时 jsonl。
|
||||
"""
|
||||
|
||||
import http.client
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from cloud.auth import AuthStore
|
||||
from cloud.server import create_server
|
||||
except ImportError: # discover -s cloud/tests 时把项目根目录补进 sys.path
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
from cloud.auth import AuthStore
|
||||
from cloud.server import create_server
|
||||
|
||||
|
||||
class ChatHistoryTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.db_path = os.path.join(self.tmp.name, "control.db")
|
||||
self.trace_dir = os.path.join(self.tmp.name, "traces")
|
||||
os.makedirs(self.trace_dir)
|
||||
self.auth = AuthStore(self.db_path)
|
||||
self.owner_id, self.owner_key = self.auth.create_user("owner")
|
||||
self.server = create_server(self.db_path, "127.0.0.1", 0)
|
||||
self.server.trace_dir = self.trace_dir
|
||||
self.port = self.server.server_address[1]
|
||||
self.thread = threading.Thread(
|
||||
target=self.server.serve_forever, daemon=True)
|
||||
self.thread.start()
|
||||
status, self.project = self._req(
|
||||
"POST", "/projects", {"name": "demo"}, self.owner_key)
|
||||
self.assertEqual(status, 201)
|
||||
|
||||
def tearDown(self):
|
||||
self.server.shutdown()
|
||||
self.server.server_close()
|
||||
self.thread.join(timeout=5)
|
||||
try:
|
||||
self.auth.conn.close()
|
||||
except Exception:
|
||||
pass
|
||||
self.tmp.cleanup()
|
||||
|
||||
def _req(self, method, path, body=None, key=None):
|
||||
conn = http.client.HTTPConnection(
|
||||
"127.0.0.1", self.port, timeout=10)
|
||||
hdrs = {}
|
||||
payload = None
|
||||
if key:
|
||||
hdrs["X-API-Key"] = key
|
||||
if body is not None:
|
||||
payload = json.dumps(body)
|
||||
hdrs["Content-Type"] = "application/json"
|
||||
conn.request(method, path, body=payload, headers=hdrs)
|
||||
resp = conn.getresponse()
|
||||
raw = resp.read()
|
||||
conn.close()
|
||||
try:
|
||||
return resp.status, json.loads(raw.decode("utf-8"))
|
||||
except (ValueError, UnicodeDecodeError):
|
||||
return resp.status, {}
|
||||
|
||||
def _task(self, status="completed"):
|
||||
_s, task = self._req(
|
||||
"POST", "/tasks",
|
||||
{"project_id": self.project["id"], "prompt": "original"},
|
||||
self.owner_key)
|
||||
self.assertEqual(_s, 201, task)
|
||||
if status != "queued":
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
conn.execute("UPDATE tasks SET status = ? WHERE id = ?",
|
||||
(status, task["id"]))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return task["id"]
|
||||
|
||||
def _set_status(self, task_id, status):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
conn.execute("UPDATE tasks SET status = ? WHERE id = ?",
|
||||
(status, task_id))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def _chat(self, task_id, key=None):
|
||||
return self._req(
|
||||
"GET", "/tasks/%s/chat-history" % task_id,
|
||||
key=key or self.owner_key)
|
||||
|
||||
def test_continue_persisted_and_interleaved(self):
|
||||
tid = self._task("completed")
|
||||
s, _t = self._req(
|
||||
"POST", "/tasks/%s/continue" % tid,
|
||||
{"message": "first follow-up"}, self.owner_key)
|
||||
self.assertEqual(s, 200)
|
||||
self._set_status(tid, "completed")
|
||||
s, _t = self._req(
|
||||
"POST", "/tasks/%s/continue" % tid,
|
||||
{"message": "second follow-up"}, self.owner_key)
|
||||
self.assertEqual(s, 200)
|
||||
s, chat = self._chat(tid)
|
||||
self.assertEqual(s, 200, chat)
|
||||
self.assertEqual(chat["continue_messages_stored"], 2)
|
||||
# 两次继续都有 audit,全部已入库,缺口为 0。
|
||||
self.assertEqual(chat["legacy_continue_count"], 0)
|
||||
texts = [(m["role"], m["source"], m["text"])
|
||||
for m in chat["messages"]]
|
||||
self.assertEqual(texts, [
|
||||
("user", "prompt", "original"),
|
||||
("user", "continue", "first follow-up"),
|
||||
("user", "continue", "second follow-up"),
|
||||
])
|
||||
# 注入链路:把两次继续与任务创建时间钉到确定时间戳,
|
||||
# 助手回复落在两次继续之间与之后,按时间交错(不靠
|
||||
# 墙钟间隔赌顺序——两次继续在测试里只隔几十毫秒,
|
||||
# 助手 +500ms 本就该排在第二次继续之后)。
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
conn.execute("UPDATE tasks SET created_at = ? WHERE id = ?",
|
||||
(1699999900.0, tid))
|
||||
conn.execute(
|
||||
"UPDATE task_continue_messages SET created_at = ?"
|
||||
" WHERE task_id = ? AND body = ?",
|
||||
(1700000000.0, tid, "first follow-up"))
|
||||
conn.execute(
|
||||
"UPDATE task_continue_messages SET created_at = ?"
|
||||
" WHERE task_id = ? AND body = ?",
|
||||
(1700000100.0, tid, "second follow-up"))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
entries = [
|
||||
{"kind": "message", "id": "m1", "ts": 1700000050000,
|
||||
"phase": "final_answer", "text": "done first"},
|
||||
{"kind": "message", "id": "m2", "ts": 1700000150000,
|
||||
"phase": "final_answer", "text": "done second"},
|
||||
{"kind": "exec", "id": "e1", "ts": 1700000150600,
|
||||
"command": "ls", "exitCode": 0, "durationMs": 1,
|
||||
"cwd": "/tmp"},
|
||||
]
|
||||
with open(os.path.join(self.trace_dir, tid + ".jsonl"),
|
||||
"w", encoding="utf-8") as f:
|
||||
for e in entries:
|
||||
f.write(json.dumps(e) + "\n")
|
||||
s, chat = self._chat(tid)
|
||||
self.assertEqual(s, 200, chat)
|
||||
texts = [(m["role"], m["source"], m["text"])
|
||||
for m in chat["messages"]]
|
||||
self.assertEqual(texts, [
|
||||
("user", "prompt", "original"),
|
||||
("user", "continue", "first follow-up"),
|
||||
("assistant", "trace", "[final_answer] done first"),
|
||||
("user", "continue", "second follow-up"),
|
||||
("assistant", "trace", "[final_answer] done second"),
|
||||
("tool", "trace", "ls(exit=0,1ms,cwd=/tmp)"),
|
||||
])
|
||||
|
||||
def test_followup_and_legacy_gap_reported(self):
|
||||
# 后续消息仅支持进行中任务,completed 会 409。
|
||||
tid = self._task("running")
|
||||
s, _f = self._req(
|
||||
"POST", "/tasks/%s/followups" % tid,
|
||||
{"body": "ping while running", "mode": "direct"},
|
||||
self.owner_key)
|
||||
self.assertEqual(s, 201, _f)
|
||||
# 模拟部署前的一次继续:audit 里有记录、表里没有正文。
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
conn.execute(
|
||||
"INSERT INTO audit_log(user_id, project_id, action,"
|
||||
" detail, created_at) VALUES(?, ?, 'task_continued', ?, ?)",
|
||||
(self.owner_id, self.project["id"], tid, time.time()))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
s, chat = self._chat(tid)
|
||||
self.assertEqual(s, 200, chat)
|
||||
self.assertEqual(chat["continue_messages_stored"], 0)
|
||||
self.assertEqual(chat["legacy_continue_count"], 1)
|
||||
sources = [(m["role"], m["source"]) for m in chat["messages"]]
|
||||
self.assertEqual(sources, [
|
||||
("user", "prompt"), ("user", "followup")])
|
||||
|
||||
def test_rejected_continue_stores_nothing(self):
|
||||
tid = self._task("queued")
|
||||
code, _b = self._req(
|
||||
"POST", "/tasks/%s/continue" % tid,
|
||||
{"message": "should not persist"}, self.owner_key)
|
||||
self.assertEqual(code, 409)
|
||||
s, chat = self._chat(tid)
|
||||
self.assertEqual(s, 200, chat)
|
||||
self.assertEqual(chat["continue_messages_stored"], 0)
|
||||
self.assertEqual(len(chat["messages"]), 1) # 只有 prompt
|
||||
|
||||
def test_chat_history_auth(self):
|
||||
tid = self._task("completed")
|
||||
code, _b = self._chat(tid, key="bad-key")
|
||||
self.assertEqual(code, 401)
|
||||
code, _b = self._chat("no-such-task")
|
||||
self.assertEqual(code, 404)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user