console: add goal-mode invincible option

DispatchSection gains a 'goal 模式无敌版' checkbox next to 'goal 模式':
checking it sends goal_mode=1 plus no_time_limit=1 (server already
accepts the field; the console just never exposed it). Plain goal
checkbox behavior is unchanged; invincible wins when both are
checked. Plan/coordinator mutual exclusion now covers invincible
too (it is a goal variant). TaskDetailPanel shows
'goal 模式无敌版(不限时)' when a task carries no_time_limit.
Help text states the honest boundary: worker/docker total timeouts
are lifted, the 600s single-turn host guard stays. Built with
npm run build (tsc+vite pass); console.html synced verbatim from
dist/index.html.
This commit is contained in:
host committed 2026-10-29 17:58:50 +08:00
1 parent 9961a251d1
commit 7d400afa37
4 files changed
+36 -28

No files matched your search

+2 -1
View File
@@ -10,6 +10,7 @@ export interface Task {
turn_id?: string; root_turn_id?: string; plan?: number | boolean; forked_from?: string;
coordinator_parts?: Array<{ name: string; prompt?: string }> | null; parent_task_id?: string;
coordinator_part?: string; goal_mode?: number | boolean; verify_command?: string;
no_time_limit?: number | boolean;
environment_id?: string; attachments?: string[]; model?: string; effort?: string;
child_model?: string; child_effort?: string; approval?: string;
result_text?: string; error_text?: string;
@@ -56,7 +57,7 @@ export const ROLES = ["viewer", "contributor", "maintainer", "owner"];
export interface DispatchBody {
project_id: string; prompt: string; environment_id?: string; executor?: string;
model?: string; effort?: string; child_model?: string; child_effort?: string;
approval?: string; plan?: number; goal_mode?: number; upload_id?: string;
approval?: string; plan?: number; goal_mode?: number; no_time_limit?: number; upload_id?: string;
coordinator_parts?: Array<{ name: string; prompt?: string }>;
}
export interface ContinueBody {
@@ -9,6 +9,7 @@ export default function DispatchSection() {
const [prompt, setPrompt] = useState("");
const [coordParts, setCoordParts] = useState("");
const [goalMode, setGoalMode] = useState(false);
const [goalInvincible, setGoalInvincible] = useState(false);
const [planMode, setPlanMode] = useState(false);
const [executor, setExecutor] = useState("docker");
const [model, setModel] = useState("");
@@ -42,15 +43,19 @@ export default function DispatchSection() {
}
if (planMode) {
if (approval === "full") { setMsg("计划模式不能与完全访问同用"); return; }
if (goalMode) { setMsg("计划模式不能与 goal 模式同用"); return; }
if (goalMode || goalInvincible) { setMsg("计划模式不能与 goal 模式同用"); return; }
body.plan = 1;
}
if (goalMode) body.goal_mode = 1;
// 无敌版 = goal 模式 + 任务不限时:勾了它就同时发 goal_mode 与
// no_time_limit(服务端已支持该字段);与普通 goal 勾选不冲突,
// 无敌版优先,单独勾普通 goal 时行为不变。
if (goalInvincible) { body.goal_mode = 1; body.no_time_limit = 1; }
else if (goalMode) body.goal_mode = 1;
const coordRaw = coordParts.trim();
if (coordRaw) {
try { body.coordinator_parts = JSON.parse(coordRaw); } catch { setMsg("协调员子任务 JSON 解析失败"); return; }
if (!Array.isArray(body.coordinator_parts)) { setMsg("协调员子任务必须是数组"); return; }
if (goalMode || planMode) { setMsg("协调员任务不能与 goal/计划模式同用:协调员任务本身不跑模型回合,请取消 goal/计划勾选,或清空协调员 JSON"); return; }
if (goalMode || goalInvincible || planMode) { setMsg("协调员任务不能与 goal/计划模式同用:协调员任务本身不跑模型回合,请取消 goal/计划勾选,或清空协调员 JSON"); return; }
}
const atts = gatherAttachments(filesRef.current?.files || null, dirRef.current?.files || null, imgRef.current?.files || null);
try {
@@ -74,6 +79,7 @@ export default function DispatchSection() {
<option value="docker">docker(默认,多用户下隔离最强)</option><option value="local">local</option><option value="cloud">cloud</option>
</select>
<label><input type="checkbox" checked={goalMode} onChange={(e) => setGoalMode(e.target.checked)} /> goal 模式</label>
<label><input type="checkbox" checked={goalInvincible} onChange={(e) => setGoalInvincible(e.target.checked)} /> goal 模式无敌版</label>
<label><input type="checkbox" checked={planMode} onChange={(e) => setPlanMode(e.target.checked)} /> 计划模式(只读)</label>
</Bar>
<textarea className="prompt stacked" placeholder="任务 prompt(必填)" value={prompt} onChange={(e) => setPrompt(e.target.value)} />
@@ -106,6 +112,7 @@ export default function DispatchSection() {
</Bar>
<div className="muted">子任务模型/强度是派生默认:本任务用 marker task-create 或协调员分工派生的子任务,未显式写模型/强度时用它;块/分工里显式写了则覆盖它。也可以不在这里填、直接在 prompt 里写“子任务用 gpt-6-luna max”,建任务时服务端会解析成同一对字段(任务详情里可核对)。</div>
{goalMode ? <div className="muted">goal 模式:输入框内容即目标,不需要专用 prompt 与验收命令;不限轮次,模型确认达成后在回复末尾输出“目标已达成”即结束,未达成会一直运行,可用取消停止。</div> : null}
{goalInvincible ? <div className="muted">goal 模式无敌版:goal 模式 + 任务不限时(去掉 worker/docker 总超时;单轮 600 秒护栏还在,不限的是任务总时长与总轮次)。服务端默认的自动续跑与断点镜像同时生效。</div> : null}
<Bar>
<span className="muted">附件(放进任务工作区,图片与普通文件不区分):</span>
<input ref={filesRef} type="file" multiple title="文件" />
@@ -25,7 +25,7 @@ export default function TaskDetailPanel() {
<br />更新 {fmt(t.updated_at)}
{t.plan ? <><br />计划模式(只读)</> : null}
{t.forked_from ? <><br />分支自 {shortId(t.forked_from)}</> : null}
{t.goal_mode ? <><br />goal 模式(不限轮次,模型宣告完成)</> : t.verify_command ? <><br />goal 模式(旧语义,验收命令)</> : null}
{t.goal_mode ? <><br />{t.no_time_limit ? "goal 模式无敌版(不限时)" : "goal 模式(不限轮次,模型宣告完成)"}</> : t.verify_command ? <><br />goal 模式(旧语义,验收命令)</> : null}
{t.coordinator_parts ? <><br />协调员:{t.coordinator_parts.length} 个子任务({t.coordinator_parts.map((p) => p.name).join("、")})</> : null}
{t.parent_task_id ? <><br />协调子任务,父任务 {shortId(t.parent_task_id)}{t.coordinator_part ? "(" + t.coordinator_part + ")" : ""}</> : null}
{(t.attachments && t.attachments.length) ? <><br />附件:{t.attachments.join("、")}</> : null}