feat: deepen main agent handoff surfaces

This commit is contained in:
kris
2026-03-29 18:55:07 +08:00
parent d4be3a2ce1
commit 1c5108dcc1
2 changed files with 163 additions and 4 deletions

View File

@@ -54,6 +54,7 @@ const appState = {
selectedOnelinerSessionId: "",
onelinerRuns: [],
selectedOnelinerRunId: "",
lastCompletedOnelinerRunId: "",
onelinerMessages: [],
onelinerActionRegistry: [],
platformAgents: [],
@@ -975,6 +976,8 @@ function renderOneLinerRunsHtml() {
}
const runEvents = safeArray(currentRun.events).slice(-3);
const planSteps = safeArray(currentRun.plan?.steps).slice(0, 4);
const resultPayload = currentRun.result && typeof currentRun.result === "object" ? currentRun.result : null;
const hasResultPayload = Boolean(resultPayload && Object.keys(resultPayload).length);
const runStatusLabel = {
needs_confirmation: "待确认",
queued: "排队中",
@@ -1004,6 +1007,7 @@ function renderOneLinerRunsHtml() {
<span class="tag ${statusTone}">${escapeHtml(runStatusLabel)}</span>
${currentRun.platform_label ? `<span class="tag">${escapeHtml(currentRun.platform_label)}</span>` : ""}
<span class="tag">${escapeHtml(onelinerIntentLabel(currentRun.intent_key))}</span>
${currentRun.source_screen ? `<span class="tag">${escapeHtml(currentRun.source_screen)}</span>` : ""}
</div>
</div>
${currentRun.active_admin_override_notice?.title ? `
@@ -1012,6 +1016,15 @@ function renderOneLinerRunsHtml() {
<p>${escapeHtml(currentRun.active_admin_override_notice.summary || "当前运行会优先遵循管理员覆盖层。")}</p>
</div>
` : ""}
<div class="task-item compact" style="margin-top:10px;">
<h4>当前计划</h4>
<p>${escapeHtml(currentRun.plan?.summary || currentRun.summary || "主 Agent 会先按这张确认卡理解目标,再继续执行。")}</p>
<div class="task-meta">
<span class="tag blue">${escapeHtml(currentRun.source_action_key || "manual-handoff")}</span>
<span class="tag">${escapeHtml(currentRun.platform_scope === "all_platforms" ? "全平台" : "单平台")}</span>
${currentRun.delivery_mode ? `<span class="tag">${escapeHtml(currentRun.delivery_mode)}</span>` : ""}
</div>
</div>
${planSteps.length ? `
<div class="list" style="margin-top:10px;">
${planSteps.map((step, index) => `
@@ -1029,7 +1042,14 @@ function renderOneLinerRunsHtml() {
` : `
<span class="tag ${statusTone}">${escapeHtml(currentRun.status_summary || "主 Agent 正在推进中")}</span>
`}
${hasResultPayload ? `<span class="tag clickable-tag" data-action="open-oneliner-run-result" data-run-id="${escapeHtml(currentRun.id)}">查看结果</span>` : ""}
</div>
${hasResultPayload ? `
<div class="task-item compact" style="margin-top:10px;">
<h4>执行结果</h4>
<div class="sheet-html">${renderOneLinerExecutionPayloadHtml(currentRun.result)}</div>
</div>
` : ""}
${runEvents.length ? `
<div class="list" style="margin-top:10px;">
${runEvents.map((item) => `
@@ -1401,6 +1421,7 @@ async function logoutSession() {
appState.selectedOnelinerSessionId = "";
appState.onelinerRuns = [];
appState.selectedOnelinerRunId = "";
appState.lastCompletedOnelinerRunId = "";
appState.onelinerMessages = [];
appState.onelinerActionRegistry = [];
appState.platformAgents = [];
@@ -1465,6 +1486,10 @@ async function hydrateSelectedOneLinerRun() {
? runs.map((item) => (item.id === detail.id ? detail : item))
: [detail, ...runs];
appState.onelinerRuns = nextRuns;
if (detail.run_status === "done" && detail.id && appState.lastCompletedOnelinerRunId !== detail.id) {
rememberAction("主 Agent 已完成本轮", detail.result?.execution_summary || detail.status_summary || detail.summary || "当前运行已经完成,可以继续执行下一步。", "green", detail);
appState.lastCompletedOnelinerRunId = detail.id;
}
return detail;
}
@@ -1703,6 +1728,29 @@ function renderOneLinerExecutionPayloadHtml(payload) {
if (!payload || typeof payload !== "object") {
return `<div class="task-item compact"><h4>没有返回执行结果</h4><p>当前执行器没有附带额外数据。</p></div>`;
}
if (payload.result_kind === "main_agent_plan") {
return `
<div class="task-item compact">
<h4>${escapeHtml(payload.goal || "主 Agent 执行建议")}</h4>
<p>${escapeHtml(payload.execution_summary || payload.summary_text || "已形成一版可继续执行的主 Agent 建议。")}</p>
<div class="task-meta">
${payload.platform ? `<span class="tag blue">${escapeHtml(platformLabel(payload.platform))}</span>` : ""}
<span class="tag">${escapeHtml(payload.platform_scope === "all_platforms" ? "全平台" : "单平台")}</span>
<span class="tag green">已收口</span>
</div>
</div>
${safeArray(payload.next_steps).length ? `
<div class="list" style="margin-top:12px;">
${safeArray(payload.next_steps).slice(0, 4).map((step, index) => `
<div class="task-item compact">
<h4>下一步 ${escapeHtml(formatNumber(index + 1))}</h4>
<p>${escapeHtml(step)}</p>
</div>
`).join("")}
</div>
` : ""}
`;
}
if (payload.job) {
const job = payload.job || {};
const sourceJob = payload.source_job || {};
@@ -1909,6 +1957,32 @@ async function executeOneLinerAction(executorKey, options = {}) {
return payload;
}
function openCurrentOneLinerRunResultAction(runId = "") {
const currentRun = safeArray(appState.onelinerRuns).find((item) => item.id === runId) || getCurrentOneLinerRun();
if (!currentRun?.id) {
rememberAction("还没有可查看的结果", "当前主 Agent 任务还没有返回可展示的执行结果。", "orange");
renderAll();
return;
}
if (!currentRun.result || !Object.keys(currentRun.result || {}).length) {
rememberAction("结果还在生成中", currentRun.status_summary || "当前主 Agent 任务还没有返回执行结果。", "orange", currentRun);
renderAll();
return;
}
openActionModal({
title: currentRun.title || currentRun.plan?.goal || "主 Agent 执行结果",
description: currentRun.result?.execution_summary || currentRun.status_summary || "这是当前主 Agent 任务的执行结果。",
hideSubmit: true,
fields: [
{
type: "html",
label: "执行结果",
html: `<div class="sheet-html">${renderOneLinerExecutionPayloadHtml(currentRun.result)}</div>`
}
]
});
}
async function loadPlatformAccount(platform, accountId, requestToken = 0) {
if (!accountId) return;
const normalizedPlatform = normalizePlatformValue(platform, getPreferredPlatform());
@@ -3992,6 +4066,7 @@ function button(label, action, tone = "secondary", options = {}) {
if (options.disabledReason) classes.push("is-disabled");
const targetAction = options.disabledReason ? "show-disabled-reason" : action;
const title = options.disabledReason || options.title || "";
const attrs = options.attrs || "";
return `
<button
class="${classes.join(" ")}"
@@ -3999,6 +4074,7 @@ function button(label, action, tone = "secondary", options = {}) {
data-action="${escapeHtml(targetAction)}"
${options.disabledReason ? `data-disabled-reason="${escapeHtml(options.disabledReason)}" aria-disabled="true"` : ""}
${title ? `title="${escapeHtml(title)}"` : ""}
${attrs}
>${escapeHtml(label)}</button>
`.replace(/\s+/g, " ").trim();
}
@@ -4097,6 +4173,35 @@ function renderIntegrationOverviewPanel(options = {}) {
`;
}
function buildMainAgentHandoffAttrs({
sourceScreen = "",
sourceActionKey = "",
intentKey = "custom",
title = "",
goal = "",
summary = "",
platform = "",
platformScope = "single_platform",
planSteps = []
} = {}) {
const attrs = [
`data-source-screen="${escapeHtml(sourceScreen || appState.screen || "dashboard")}"`,
`data-source-action-key="${escapeHtml(sourceActionKey || "main-agent-handoff")}"`,
`data-intent-key="${escapeHtml(intentKey || "custom")}"`,
`data-title="${escapeHtml(title || goal || "交给主 Agent 处理")}"`,
`data-goal="${escapeHtml(goal || title || "交给主 Agent 处理")}"`,
`data-summary="${escapeHtml(summary || "")}"`,
`data-platform-scope="${escapeHtml(platformScope || "single_platform")}"`
];
if (platform) {
attrs.push(`data-platform="${escapeHtml(platform)}"`);
}
if (safeArray(planSteps).length) {
attrs.push(`data-plan-steps="${escapeHtml(JSON.stringify(safeArray(planSteps)))}"`);
}
return attrs.join(" ");
}
function renderEmptyState(title, description) {
return `<div class="panel pad"><div class="empty-state"><strong>${escapeHtml(title)}</strong><p>${escapeHtml(description)}</p></div></div>`;
}
@@ -4997,10 +5102,20 @@ function renderAutomationScreen() {
{ value: "guards", label: "动作防呆" }
];
const activeTab = getActiveDetailTab("automationDetailTab", tabs);
const automationHandoffAttrs = buildMainAgentHandoffAttrs({
sourceScreen: "automation",
sourceActionKey: "automation-main-agent-handoff",
intentKey: "ops_admin",
title: "继续检查自动流程",
goal: "继续检查自动流程",
summary: "让主 Agent 结合依赖健康和动作防呆状态,给出下一步处理建议。",
platform: getPreferredPlatform(),
planSteps: ["读取当前依赖健康", "检查动作防呆和拦截状态", "生成下一步处理建议"]
});
return screenShell(
"自动流程",
"自动同步、日报生成和失败补跑先统一看这里。",
`${button("刷新", "refresh-data")} ${button("OneLiner", "open-oneliner")} ${renderPipelineButton("aiVideo")} ${renderPipelineButton("realCut")} ${button("去生产", "goto-production", "primary")}`,
`${button("刷新", "refresh-data")} ${button("交给主 Agent", "handoff-to-main-agent", "secondary", { attrs: automationHandoffAttrs })} ${renderPipelineButton("aiVideo")} ${renderPipelineButton("realCut")} ${button("去生产", "goto-production", "primary")}`,
`
<div class="hero-card">
<h3>自动流程</h3>
@@ -5143,6 +5258,16 @@ function renderPlaybookScreen() {
const localCatalog = appState.localModelCatalog || {};
const activeAdminOverrideNotice = appState.onelinerGovernanceEffective?.active_admin_override_notice || null;
const gatewayModels = safeArray(localCatalog.models).map((item) => item.id).filter(Boolean);
const playbookHandoffAttrs = buildMainAgentHandoffAttrs({
sourceScreen: "playbook",
sourceActionKey: "playbook-main-agent-handoff",
intentKey: "custom",
title: "继续梳理当前 Agent 工作区",
goal: "继续梳理当前 Agent 工作区",
summary: "让主 Agent 结合当前 Agent、模型和策略状态给出下一步执行建议。",
platform: appState.onelinerGovernanceEffective?.platform || appState.onelinerProfile?.default_platform || getPreferredPlatform(),
planSteps: ["读取当前 Agent 与模型配置", "检查当前策略与平台 Agent 缺口", "生成下一步执行建议"]
});
const tabs = [
{ value: "workspace", label: "当前 Agent 工作台" },
{ value: "platform_agents", label: "平台 Agent" },
@@ -5152,7 +5277,7 @@ function renderPlaybookScreen() {
return screenShell(
"Agent",
"这里接真实 Agent 列表,当前已经支持切换和编辑 Agent。",
`${button("配置 OneLiner", "open-oneliner-profile")} ${button("设主模型", "open-preferred-model")} ${button("新建 Agent", "open-create-assistant")} ${button("生成文案", "open-generate-copy")} ${button("去生产", "goto-production", "primary")}`,
`${button("配置 OneLiner", "open-oneliner-profile")} ${button("交给主 Agent", "handoff-to-main-agent", "secondary", { attrs: playbookHandoffAttrs })} ${button("设主模型", "open-preferred-model")} ${button("新建 Agent", "open-create-assistant")} ${button("去生产", "goto-production", "primary")}`,
`
<div class="hero-card">
<h3>Agent 概览</h3>
@@ -5181,7 +5306,7 @@ function renderPlaybookScreen() {
<div class="task-meta">
<span class="tag blue">${escapeHtml(appState.onelinerProfile?.display_name || "OneLiner")}</span>
<span class="tag">${escapeHtml(appState.onelinerProfile?.default_platform ? platformLabel(appState.onelinerProfile.default_platform) : "未设默认平台")}</span>
<span class="tag clickable-tag" data-action="open-oneliner">打开对话</span>
${actionTag("交给主 Agent", "handoff-to-main-agent", playbookHandoffAttrs)}
</div>
</div>
<div class="task-item compact">
@@ -5573,10 +5698,20 @@ function renderStrategyScreen() {
const project = getSelectedProject();
const platform = appState.onelinerGovernanceEffective?.platform || appState.onelinerProfile?.default_platform || getPreferredPlatform();
const activeAdminOverrideNotice = appState.onelinerGovernanceEffective?.active_admin_override_notice || null;
const strategyHandoffAttrs = buildMainAgentHandoffAttrs({
sourceScreen: "strategy",
sourceActionKey: "strategy-main-agent-handoff",
intentKey: "custom",
title: "继续调整我的策略",
goal: "继续调整我的策略",
summary: "让主 Agent 结合当前生效层、个人策略和管理员覆盖,给出下一步治理建议。",
platform,
planSteps: ["读取当前生效策略", "检查用户层与管理员覆盖差异", "生成下一步治理建议"]
});
return screenShell(
"我的策略",
"把你和主 Agent 的对话沉淀成可查看、可回滚、可追溯的个人策略层。",
`${button("编辑全局策略", "open-user-global-policy")} ${button("编辑当前平台策略", "open-user-platform-policy", "primary")} ${button("打开 OneLiner", "open-oneliner")}`,
`${button("编辑全局策略", "open-user-global-policy")} ${button("编辑当前平台策略", "open-user-platform-policy", "primary")} ${button("交给主 Agent", "handoff-to-main-agent", "secondary", { attrs: strategyHandoffAttrs })}`,
`
<div class="hero-card">
<h3>当前策略工作区</h3>
@@ -9369,6 +9504,10 @@ document.addEventListener("click", async (event) => {
}
return;
}
if (name === "open-oneliner-run-result") {
openCurrentOneLinerRunResultAction(action.dataset.runId || "");
return;
}
if (name === "confirm-oneliner-run") {
try {
setBusy(true, "正在确认执行计划...");

View File

@@ -133,6 +133,9 @@ test("oneliner panel includes a dedicated runtime header for agent runs", () =>
assert.match(source, /data-role="oneliner-runs"/);
assert.match(runtime, /confirm-oneliner-run/);
assert.match(runtime, /cancel-oneliner-run/);
assert.match(runtime, /当前计划/);
assert.match(runtime, /renderOneLinerExecutionPayloadHtml\(currentRun\.result\)/);
assert.match(runtime, /open-oneliner-run-result/);
});
test("oneliner meta and action handlers expose governance entry points", () => {
@@ -147,6 +150,16 @@ test("oneliner meta and action handlers expose governance entry points", () => {
assert.match(actions, /name === "open-system-main-policy"/);
assert.match(actions, /name === "handoff-to-main-agent"/);
assert.match(actions, /name === "confirm-oneliner-run"/);
assert.match(actions, /name === "open-oneliner-run-result"/);
});
test("oneliner runtime remembers completed runs exactly once after hydration", () => {
const hydrate = extractBetween(APP, "async function hydrateSelectedOneLinerRun()", "async function loadAgentControlSurfaces(projectId = \"\")");
const state = extractBetween(APP, "const appState = {", "};\n\nlet PLATFORM_RUNTIME");
assert.match(state, /lastCompletedOnelinerRunId/);
assert.match(hydrate, /detail\.run_status === "done"/);
assert.match(hydrate, /appState\.lastCompletedOnelinerRunId !== detail\.id/);
assert.match(hydrate, /rememberAction\("主 Agent 已完成本轮"/);
});
test("system governance saves refresh control surfaces after persisting", () => {
@@ -199,12 +212,19 @@ test("governance UI exposes admin override target picker and history rollback en
test("user governance UI exposes personal history and rollback entrypoints", () => {
const playbook = extractBetween(APP, "function renderPlaybookScreen()", "function renderProductionScreen()");
const strategy = extractBetween(APP, "function renderStrategyScreen()", "function renderCreditsScreen()");
const automation = extractBetween(APP, "function renderAutomationScreen()", "function renderOwnedScreen()");
const actions = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {");
assert.match(playbook, /open-user-global-policy-history/);
assert.match(playbook, /open-user-platform-policy-history/);
assert.match(playbook, /handoff-to-main-agent/);
assert.match(playbook, /playbook-main-agent-handoff/);
assert.match(strategy, /active_admin_override_notice/);
assert.match(strategy, /管理员覆盖生效中/);
assert.match(strategy, /handoff-to-main-agent/);
assert.match(strategy, /strategy-main-agent-handoff/);
assert.match(automation, /handoff-to-main-agent/);
assert.match(automation, /automation-main-agent-handoff/);
assert.match(actions, /name === "open-user-global-policy-history"/);
assert.match(actions, /name === "open-user-platform-policy-history"/);