Tasks gain kind='main'|'side' (NULL=main). A side thread hangs under a parent task, defaults to the local executor, never builds a worktree, branch or container unless explicitly asked, and writes its conclusion back to the parent via task_followups on terminal transition. - server: kind validation + side-thread gates (parent required, no coordinator_parts/upload_id/environment_id, parent must not be a coordinator), local executor default, GET /tasks kind filter, /worktree 404 and /fork /merge /pr 409 for side threads. - worker: side-default shunt in _run_attempt (no-worktree scratch cwd, in-memory plan=1 when a repo is present, seed/checkpoint skipped), _writeback_side_thread, marker task-create kind field + inheritance, side threads excluded from marker spawn counts. - host/sandbox: --no-worktree plumbing for dispatch/goal/popen (Local + Docker), scratch cwd work/side-threads/<task-id>. - console (classic only): kind filter + [旁聊] rows + '开旁聊' entry, side-thread dispatch mode, side-thread list in detail, merge/PR/fork hidden for side threads; build exit 0, console.html sha df39db5a. - tests: new test_sidethread.py (29 tests) + kind backfill tests in test_p2_migrations.py; full cloud suite 3900 tests OK (skipped=13). Design: verification/codex-muse-role-alignment-design-20261108.md Summary: verification/codex-muse-role-align-p1p2-20261108.md Local only: not pushed, not merged, not deployed.
This commit is contained in:
1 parent
f6df76ee8f
commit
7983567491
24 files changed
+1244
-100
No files matched your search
@@ -12,18 +12,23 @@ export default function DetailActions() {
|
||||
if (!t) return null;
|
||||
const terminal = TRACE_TERMINAL.includes(t.status);
|
||||
const retryOk = ["failed", "interrupted", "merge-failed"].includes(t.status);
|
||||
// 旁聊(Muse 角色对齐 P2):无 worktree/分支,后端 /fork /merge
|
||||
// /pr 一律 409,这里只留重试/取消/查成本,不渲染那三处入口。
|
||||
const isSide = t.kind === "side";
|
||||
return (
|
||||
<>
|
||||
<Bar>
|
||||
<button disabled={!retryOk} title={retryOk ? "" : "仅 failed/interrupted/merge-failed 可重试"} onClick={() => ctx.doAction("retry")}>重试</button>
|
||||
<button className="danger" disabled={terminal} title={terminal ? "终态任务不可取消" : ""} onClick={() => ctx.doAction("cancel")}>取消</button>
|
||||
<button disabled={t.status !== "completed"} title={t.status !== "completed" ? "仅 completed 可合并" : ""} onClick={() => ctx.doAction("merge")}>合并(completed → merged)</button>
|
||||
<button disabled={t.status !== "merged"} title={t.status !== "merged" ? "仅 merged 可提 PR" : ""} onClick={() => ctx.doAction("pr")}>提 PR(须 merged)</button>
|
||||
{terminal ? <button onClick={() => ctx.setForkVisible(true)}>分支</button> : null}
|
||||
{isSide ? null : (<>
|
||||
<button disabled={t.status !== "completed"} title={t.status !== "completed" ? "仅 completed 可合并" : ""} onClick={() => ctx.doAction("merge")}>合并(completed → merged)</button>
|
||||
<button disabled={t.status !== "merged"} title={t.status !== "merged" ? "仅 merged 可提 PR" : ""} onClick={() => ctx.doAction("pr")}>提 PR(须 merged)</button>
|
||||
</>)}
|
||||
{terminal && !isSide ? <button onClick={() => ctx.setForkVisible(true)}>分支</button> : null}
|
||||
<button onClick={ctx.taskCost}>查成本</button>
|
||||
<span className="muted">{ctx.actionMsg} {ctx.actionReasons}</span>
|
||||
</Bar>
|
||||
{ctx.forkVisible ? (
|
||||
{ctx.forkVisible && !isSide ? (
|
||||
<div>
|
||||
<input type="text" placeholder="分支首轮消息(必填,带历史的新会话)" style={{ width: "100%", boxSizing: "border-box", margin: "6px 0" }} value={forkMsg} onChange={(e) => setForkMsg(e.target.value)} />
|
||||
<Bar><button className="seal" onClick={async () => { await ctx.forkTask(forkMsg); setForkMsg(""); }}>创建分支任务</button><span className="muted">{ctx.forkMsg}</span></Bar>
|
||||
|
||||
@@ -19,6 +19,23 @@ export function CoordPanel() {
|
||||
);
|
||||
}
|
||||
|
||||
// 旁聊(Muse 角色对齐 P2):当前任务挂着的旁聊线程,与协调员
|
||||
// 子任务列表同款组件复用;点行切到该旁聊的详情。
|
||||
export function SideThreadPanel() {
|
||||
const ctx = useConsole();
|
||||
if (!ctx.currentTask) return <div className="muted">点任务行查看详情后,这里显示旁聊</div>;
|
||||
if (ctx.sideThreads.length === 0) return <div className="muted">(暂无旁聊;列表“开旁聊”可在此任务下新建,不建 worktree)</div>;
|
||||
return (
|
||||
<DataListRaw>
|
||||
{ctx.sideThreads.map((k) => (
|
||||
<li key={k.id} onClick={() => ctx.showDetail(k.id)} style={{ cursor: "pointer" }}>
|
||||
<span className="row-main">旁聊 | 状态 <span className={"st-" + k.status}>{k.status}</span> | {shortId(k.id)} | {String(k.prompt || "").slice(0, 60)}</span>
|
||||
</li>
|
||||
))}
|
||||
</DataListRaw>
|
||||
);
|
||||
}
|
||||
|
||||
export function AgentsPanel() {
|
||||
const ctx = useConsole();
|
||||
if (!ctx.currentTask) return <div className="muted">点任务行查看详情后,这里显示子代理链路</div>;
|
||||
|
||||
Reference in new issue
Block a user