feat: deepen main agent handoff surfaces
This commit is contained in:
@@ -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, "正在确认执行计划...");
|
||||
|
||||
@@ -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"/);
|
||||
|
||||
Reference in New Issue
Block a user