feat: add main agent runtime flow v1
This commit is contained in:
@@ -52,6 +52,8 @@ const appState = {
|
||||
onelinerProfile: null,
|
||||
onelinerSessions: [],
|
||||
selectedOnelinerSessionId: "",
|
||||
onelinerRuns: [],
|
||||
selectedOnelinerRunId: "",
|
||||
onelinerMessages: [],
|
||||
onelinerActionRegistry: [],
|
||||
platformAgents: [],
|
||||
@@ -209,6 +211,15 @@ function safeArray(value) {
|
||||
return Array.isArray(value) ? value : [];
|
||||
}
|
||||
|
||||
function parseJsonSafe(value, fallback) {
|
||||
if (typeof value !== "string" || !value.trim()) return fallback;
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
function getRuntimePlatformValues() {
|
||||
return PLATFORM_RUNTIME.getRuntimePlatformValues();
|
||||
}
|
||||
@@ -918,6 +929,7 @@ function ensureOneLinerUi() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="oneliner-meta" data-role="oneliner-meta"></div>
|
||||
<div class="oneliner-runs" data-role="oneliner-runs"></div>
|
||||
<div class="oneliner-sessions" data-role="oneliner-sessions"></div>
|
||||
<div class="oneliner-messages" data-role="oneliner-messages"></div>
|
||||
<form class="oneliner-composer" data-role="oneliner-form">
|
||||
@@ -950,6 +962,97 @@ function renderOneLinerSessionTabs() {
|
||||
`;
|
||||
}
|
||||
|
||||
function renderOneLinerRunsHtml() {
|
||||
const runs = safeArray(appState.onelinerRuns);
|
||||
const currentRun = getCurrentOneLinerRun();
|
||||
if (!runs.length || !currentRun) {
|
||||
return `
|
||||
<div class="task-item compact">
|
||||
<h4>还没有主 Agent 运行任务</h4>
|
||||
<p>你在首页、策略页或 Agent 页点击“交给主 Agent”后,这里会先出现待确认执行卡。</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
const runEvents = safeArray(currentRun.events).slice(-3);
|
||||
const planSteps = safeArray(currentRun.plan?.steps).slice(0, 4);
|
||||
const runStatusLabel = {
|
||||
needs_confirmation: "待确认",
|
||||
queued: "排队中",
|
||||
running: "执行中",
|
||||
blocked: "已阻塞",
|
||||
done: "已完成",
|
||||
failed: "已失败",
|
||||
cancelled: "已取消"
|
||||
}[currentRun.run_status] || currentRun.run_status || "运行中";
|
||||
const statusTone = currentRun.run_status === "needs_confirmation"
|
||||
? "blue"
|
||||
: currentRun.run_status === "running"
|
||||
? "green"
|
||||
: currentRun.run_status === "queued"
|
||||
? "orange"
|
||||
: currentRun.run_status === "failed"
|
||||
? "orange"
|
||||
: "";
|
||||
return `
|
||||
<div class="task-item compact oneliner-run-card">
|
||||
<div class="panel-head">
|
||||
<div>
|
||||
<h4>${escapeHtml(currentRun.title || currentRun.plan?.goal || "主 Agent 任务")}</h4>
|
||||
<div class="panel-subtitle">${escapeHtml(currentRun.summary || currentRun.status_summary || "主 Agent 会先给你一张确认卡,再继续执行。")}</div>
|
||||
</div>
|
||||
<div class="task-meta">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
${currentRun.active_admin_override_notice?.title ? `
|
||||
<div class="task-item compact" style="margin-top:10px; border-color:rgba(245, 158, 11, 0.28); background:linear-gradient(180deg, rgba(255, 250, 240, 0.98) 0%, rgba(255, 255, 255, 0.98) 100%);">
|
||||
<h4>管理员覆盖生效中</h4>
|
||||
<p>${escapeHtml(currentRun.active_admin_override_notice.summary || "当前运行会优先遵循管理员覆盖层。")}</p>
|
||||
</div>
|
||||
` : ""}
|
||||
${planSteps.length ? `
|
||||
<div class="list" style="margin-top:10px;">
|
||||
${planSteps.map((step, index) => `
|
||||
<div class="task-item compact">
|
||||
<h4>步骤 ${escapeHtml(formatNumber(index + 1))}</h4>
|
||||
<p>${escapeHtml(step)}</p>
|
||||
</div>
|
||||
`).join("")}
|
||||
</div>
|
||||
` : ""}
|
||||
<div class="task-meta" style="margin-top:10px;">
|
||||
${currentRun.run_status === "needs_confirmation" ? `
|
||||
<span class="tag clickable-tag" data-action="confirm-oneliner-run" data-run-id="${escapeHtml(currentRun.id)}">确认执行</span>
|
||||
<span class="tag clickable-tag" data-action="cancel-oneliner-run" data-run-id="${escapeHtml(currentRun.id)}">取消本轮</span>
|
||||
` : `
|
||||
<span class="tag ${statusTone}">${escapeHtml(currentRun.status_summary || "主 Agent 正在推进中")}</span>
|
||||
`}
|
||||
</div>
|
||||
${runEvents.length ? `
|
||||
<div class="list" style="margin-top:10px;">
|
||||
${runEvents.map((item) => `
|
||||
<div class="task-item compact">
|
||||
<h4>${escapeHtml(item.event_type || "run.progress")}</h4>
|
||||
<p>${escapeHtml(item.summary || "运行状态已更新。")}</p>
|
||||
</div>
|
||||
`).join("")}
|
||||
</div>
|
||||
` : ""}
|
||||
</div>
|
||||
${runs.length > 1 ? `
|
||||
<div class="chip-row">
|
||||
${runs.slice(0, 6).map((item) => `
|
||||
<span class="chip clickable-tag ${item.id === currentRun.id ? "active" : ""}" data-action="select-oneliner-run" data-run-id="${escapeHtml(item.id)}">
|
||||
${escapeHtml(brief(item.title || item.plan?.goal || "主 Agent 任务", 14))}
|
||||
</span>
|
||||
`).join("")}
|
||||
</div>
|
||||
` : ""}
|
||||
`;
|
||||
}
|
||||
|
||||
function renderOneLinerMessagesHtml() {
|
||||
const messages = safeArray(appState.onelinerMessages);
|
||||
if (!messages.length) {
|
||||
@@ -1058,6 +1161,7 @@ function renderOneLinerUi() {
|
||||
ensureOneLinerUi();
|
||||
const fab = document.querySelector(".oneliner-fab");
|
||||
const meta = document.querySelector('[data-role="oneliner-meta"]');
|
||||
const runs = document.querySelector('[data-role="oneliner-runs"]');
|
||||
const sessions = document.querySelector('[data-role="oneliner-sessions"]');
|
||||
const messages = document.querySelector('[data-role="oneliner-messages"]');
|
||||
const status = document.querySelector('[data-role="oneliner-status"]');
|
||||
@@ -1067,8 +1171,18 @@ function renderOneLinerUi() {
|
||||
const activeAdminOverrideNotice = effective?.active_admin_override_notice || null;
|
||||
const highlights = summarizePolicyHighlights(effective?.effective_policy || {}, effective?.platform || "");
|
||||
const layers = safeArray(effective?.layers);
|
||||
const currentRun = getCurrentOneLinerRun();
|
||||
const activeRuns = safeArray(appState.onelinerRuns).filter((item) => !["done", "failed", "cancelled"].includes(item.run_status));
|
||||
if (fab) {
|
||||
fab.hidden = !appState.session;
|
||||
const mark = fab.querySelector(".oneliner-fab-mark");
|
||||
const text = fab.querySelector(".oneliner-fab-text");
|
||||
if (mark) mark.textContent = String(activeRuns.length || 1);
|
||||
if (text) {
|
||||
text.textContent = currentRun
|
||||
? `OneLiner · ${currentRun.run_status === "needs_confirmation" ? "待确认" : currentRun.run_status === "running" ? "执行中" : currentRun.run_status === "queued" ? "排队中" : "工作中"}`
|
||||
: "OneLiner";
|
||||
}
|
||||
}
|
||||
if (meta) {
|
||||
meta.innerHTML = `
|
||||
@@ -1096,6 +1210,7 @@ function renderOneLinerUi() {
|
||||
` : ""}
|
||||
`;
|
||||
}
|
||||
if (runs) runs.innerHTML = renderOneLinerRunsHtml();
|
||||
if (sessions) sessions.innerHTML = renderOneLinerSessionTabs();
|
||||
if (messages) {
|
||||
messages.innerHTML = renderOneLinerMessagesHtml();
|
||||
@@ -1284,6 +1399,8 @@ async function logoutSession() {
|
||||
appState.onelinerProfile = null;
|
||||
appState.onelinerSessions = [];
|
||||
appState.selectedOnelinerSessionId = "";
|
||||
appState.onelinerRuns = [];
|
||||
appState.selectedOnelinerRunId = "";
|
||||
appState.onelinerMessages = [];
|
||||
appState.onelinerActionRegistry = [];
|
||||
appState.platformAgents = [];
|
||||
@@ -1336,11 +1453,27 @@ async function loadStorageStatus(projectId = "") {
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function hydrateSelectedOneLinerRun() {
|
||||
const runId = appState.selectedOnelinerRunId || "";
|
||||
if (!runId || !backendSupports("/v2/oneliner/runs/{run_id}")) {
|
||||
return null;
|
||||
}
|
||||
const detail = await storyforgeFetch(`/v2/oneliner/runs/${encodeURIComponent(runId)}`).catch(() => null);
|
||||
if (!detail?.id) return null;
|
||||
const runs = safeArray(appState.onelinerRuns);
|
||||
const nextRuns = runs.some((item) => item.id === detail.id)
|
||||
? runs.map((item) => (item.id === detail.id ? detail : item))
|
||||
: [detail, ...runs];
|
||||
appState.onelinerRuns = nextRuns;
|
||||
return detail;
|
||||
}
|
||||
|
||||
async function loadAgentControlSurfaces(projectId = "") {
|
||||
const normalizedProjectId = projectId || getOneLinerProjectId();
|
||||
const governancePlatform = normalizePlatformValue(getPreferredPlatform(), "douyin");
|
||||
const supportsOneLinerProfile = backendSupports("/v2/oneliner/profile");
|
||||
const supportsOneLinerSessions = backendSupports("/v2/oneliner/sessions");
|
||||
const supportsOneLinerRuns = backendSupports("/v2/oneliner/runs");
|
||||
const supportsActionRegistry = backendSupports("/v2/oneliner/action-registry");
|
||||
const supportsPlatformAgents = backendSupports("/v2/platform-agents");
|
||||
const supportsGovernanceEffective = backendSupports("/v2/oneliner/governance/effective");
|
||||
@@ -1357,13 +1490,16 @@ async function loadAgentControlSurfaces(projectId = "") {
|
||||
const supportsTenantQuota = backendSupports("/v2/tenant/quota");
|
||||
const supportsTenantUsage = backendSupports("/v2/tenant/usage");
|
||||
|
||||
const [profile, sessionsPayload, actionRegistryPayload, platformAgentsPayload, governanceEffective, userGlobalPolicy, userCurrentPlatformPolicy, userPolicyAuditsPayload, adminSystemMainPolicy, adminSystemPlatformPolicies, adminGovernanceDirectory, tenantQuota, tenantUsage, adminOpsOverview, adminFixRunsPayload] = await Promise.all([
|
||||
const [profile, sessionsPayload, runsPayload, actionRegistryPayload, platformAgentsPayload, governanceEffective, userGlobalPolicy, userCurrentPlatformPolicy, userPolicyAuditsPayload, adminSystemMainPolicy, adminSystemPlatformPolicies, adminGovernanceDirectory, tenantQuota, tenantUsage, adminOpsOverview, adminFixRunsPayload] = await Promise.all([
|
||||
supportsOneLinerProfile
|
||||
? storyforgeFetch(`/v2/oneliner/profile?project_id=${encodeURIComponent(normalizedProjectId)}`).catch(() => null)
|
||||
: Promise.resolve(null),
|
||||
supportsOneLinerSessions
|
||||
? storyforgeFetch(`/v2/oneliner/sessions?project_id=${encodeURIComponent(normalizedProjectId)}`).catch(() => ({ items: [] }))
|
||||
: Promise.resolve({ items: [] }),
|
||||
supportsOneLinerRuns
|
||||
? storyforgeFetch(`/v2/oneliner/runs?project_id=${encodeURIComponent(normalizedProjectId)}`).catch(() => ({ items: [] }))
|
||||
: Promise.resolve({ items: [] }),
|
||||
supportsActionRegistry
|
||||
? storyforgeFetch(`/v2/oneliner/action-registry?project_id=${encodeURIComponent(normalizedProjectId)}`).catch(() => ({ items: [] }))
|
||||
: Promise.resolve({ items: [] }),
|
||||
@@ -1409,10 +1545,15 @@ async function loadAgentControlSurfaces(projectId = "") {
|
||||
|
||||
appState.onelinerProfile = profile;
|
||||
appState.onelinerSessions = safeArray(sessionsPayload?.items || sessionsPayload);
|
||||
appState.onelinerRuns = safeArray(runsPayload?.items || runsPayload);
|
||||
appState.onelinerActionRegistry = safeArray(actionRegistryPayload?.items || actionRegistryPayload);
|
||||
if (!appState.selectedOnelinerSessionId || !safeArray(appState.onelinerSessions).some((item) => item.id === appState.selectedOnelinerSessionId)) {
|
||||
appState.selectedOnelinerSessionId = safeArray(appState.onelinerSessions)[0]?.id || "";
|
||||
}
|
||||
appState.selectedOnelinerRunId = choosePreferredOneLinerRunId(appState.onelinerRuns, appState.selectedOnelinerRunId || "");
|
||||
if (appState.selectedOnelinerRunId) {
|
||||
await hydrateSelectedOneLinerRun();
|
||||
}
|
||||
appState.platformAgents = safeArray(platformAgentsPayload?.items || platformAgentsPayload);
|
||||
appState.onelinerGovernanceEffective = governanceEffective;
|
||||
appState.userGlobalPolicy = userGlobalPolicy;
|
||||
@@ -1514,6 +1655,50 @@ async function submitOneLinerMessage(content) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function createOneLinerRun(runRequest) {
|
||||
if (!backendSupports("/v2/oneliner/runs")) {
|
||||
throw new Error("当前后端还没有接入主 Agent 运行层。");
|
||||
}
|
||||
const projectId = getOneLinerProjectId();
|
||||
const payload = await storyforgeFetch("/v2/oneliner/runs", {
|
||||
method: "POST",
|
||||
body: {
|
||||
project_id: projectId,
|
||||
platform: getPreferredPlatform(),
|
||||
platform_scope: "single_platform",
|
||||
delivery_mode: "hybrid",
|
||||
scheduling_mode: "queued",
|
||||
...runRequest
|
||||
}
|
||||
});
|
||||
await loadAgentControlSurfaces(projectId);
|
||||
appState.selectedOnelinerRunId = payload?.id || choosePreferredOneLinerRunId(appState.onelinerRuns, "");
|
||||
rememberAction("主 Agent 已接单", payload?.title || payload?.plan?.goal || "已创建待确认任务。", "blue", payload);
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function confirmOneLinerRun(runId, reason = "") {
|
||||
const payload = await storyforgeFetch(`/v2/oneliner/runs/${encodeURIComponent(runId)}/confirm`, {
|
||||
method: "POST",
|
||||
body: { reason }
|
||||
});
|
||||
await loadAgentControlSurfaces(getOneLinerProjectId());
|
||||
appState.selectedOnelinerRunId = payload?.id || runId;
|
||||
rememberAction("主 Agent 已确认执行", payload?.status_summary || "当前任务已进入执行流。", "green", payload);
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function cancelOneLinerRun(runId, reason = "") {
|
||||
const payload = await storyforgeFetch(`/v2/oneliner/runs/${encodeURIComponent(runId)}/cancel`, {
|
||||
method: "POST",
|
||||
body: { reason }
|
||||
});
|
||||
await loadAgentControlSurfaces(getOneLinerProjectId());
|
||||
appState.selectedOnelinerRunId = choosePreferredOneLinerRunId(appState.onelinerRuns, "");
|
||||
rememberAction("主 Agent 任务已取消", payload?.status_summary || "当前任务已取消。", "orange", payload);
|
||||
return payload;
|
||||
}
|
||||
|
||||
function renderOneLinerExecutionPayloadHtml(payload) {
|
||||
if (!payload || typeof payload !== "object") {
|
||||
return `<div class="task-item compact"><h4>没有返回执行结果</h4><p>当前执行器没有附带额外数据。</p></div>`;
|
||||
@@ -2105,6 +2290,20 @@ function getCurrentOneLinerSession() {
|
||||
return sessions.find((item) => item.id === appState.selectedOnelinerSessionId) || sessions[0] || null;
|
||||
}
|
||||
|
||||
function choosePreferredOneLinerRunId(items, currentId = "") {
|
||||
const runs = safeArray(items);
|
||||
if (currentId && runs.some((item) => item.id === currentId)) {
|
||||
return currentId;
|
||||
}
|
||||
return runs.find((item) => item.run_status === "needs_confirmation")?.id || runs[0]?.id || "";
|
||||
}
|
||||
|
||||
function getCurrentOneLinerRun() {
|
||||
const runs = safeArray(appState.onelinerRuns);
|
||||
const preferredId = choosePreferredOneLinerRunId(runs, appState.selectedOnelinerRunId || "");
|
||||
return runs.find((item) => item.id === preferredId) || null;
|
||||
}
|
||||
|
||||
function onelinerIntentLabel(value) {
|
||||
return ONELINER_INTENT_LABELS[value] || value || "自定义任务";
|
||||
}
|
||||
@@ -3381,7 +3580,16 @@ function renderGovernanceSummaryCard({ title, subtitle, effective, primaryAction
|
||||
${resolvedActions.length ? `
|
||||
<div class="task-meta" style="margin-top:10px;">
|
||||
${resolvedActions.map((item) => `
|
||||
<span class="tag clickable-tag" data-action="${escapeHtml(item.action || "")}" ${item.platform ? `data-platform="${escapeHtml(item.platform)}"` : ""}>
|
||||
<span class="tag clickable-tag" data-action="${escapeHtml(item.action || "")}"
|
||||
${item.platform ? `data-platform="${escapeHtml(item.platform)}"` : ""}
|
||||
${item.sourceActionKey ? `data-source-action-key="${escapeHtml(item.sourceActionKey)}"` : ""}
|
||||
${item.sourceScreen ? `data-source-screen="${escapeHtml(item.sourceScreen)}"` : ""}
|
||||
${item.intentKey ? `data-intent-key="${escapeHtml(item.intentKey)}"` : ""}
|
||||
${item.title ? `data-title="${escapeHtml(item.title)}"` : ""}
|
||||
${item.goal ? `data-goal="${escapeHtml(item.goal)}"` : ""}
|
||||
${item.summary ? `data-summary="${escapeHtml(item.summary)}"` : ""}
|
||||
${item.platformScope ? `data-platform-scope="${escapeHtml(item.platformScope)}"` : ""}
|
||||
${item.planSteps ? `data-plan-steps="${escapeHtml(JSON.stringify(item.planSteps))}"` : ""}>
|
||||
${escapeHtml(item.label || "查看")}
|
||||
</span>
|
||||
`).join("")}
|
||||
@@ -5402,7 +5610,18 @@ function renderStrategyScreen() {
|
||||
actions: [
|
||||
{ action: "open-user-global-policy", label: "编辑我的全局策略" },
|
||||
{ action: "open-user-platform-policy", label: "编辑当前平台策略", platform },
|
||||
{ action: "open-oneliner", label: "交给 OneLiner 调整" }
|
||||
{
|
||||
action: "handoff-to-main-agent",
|
||||
label: "交给主 Agent 调整",
|
||||
platform,
|
||||
sourceScreen: "strategy",
|
||||
sourceActionKey: "governance-summary-handoff",
|
||||
intentKey: "custom",
|
||||
title: "调整当前策略",
|
||||
goal: "调整当前策略",
|
||||
summary: "先由主 Agent 读取当前治理层,再给一版确认卡。",
|
||||
planSteps: ["读取当前生效策略", "结合管理员覆盖与个人策略生成方案", "等待用户确认后执行"]
|
||||
}
|
||||
]
|
||||
})}
|
||||
</div>
|
||||
@@ -7515,7 +7734,7 @@ async function openPlatformAgentDetailAction(platform) {
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-profile" data-platform="${escapeHtml(normalizedPlatform)}">编辑配置</span>
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-memory" data-platform="${escapeHtml(normalizedPlatform)}">继续补记忆</span>
|
||||
<span class="tag clickable-tag" data-action="open-platform-agent-skill" data-platform="${escapeHtml(normalizedPlatform)}">继续补技能</span>
|
||||
<span class="tag clickable-tag" data-action="open-oneliner">让 OneLiner 调度</span>
|
||||
<span class="tag clickable-tag" data-action="handoff-to-main-agent" data-platform="${escapeHtml(normalizedPlatform)}" data-source-screen="playbook" data-source-action-key="platform-agent-handoff" data-intent-key="custom" data-title="继续完善平台 Agent" data-goal="继续完善平台 Agent" data-summary="让主 Agent 结合当前平台记忆和技能,给出下一步执行计划。" data-plan-steps="${escapeHtml(JSON.stringify(["读取当前平台 Agent 配置", "检查记忆与技能缺口", "生成下一步执行计划"]))}">交给主 Agent 继续</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
@@ -9122,6 +9341,61 @@ document.addEventListener("click", async (event) => {
|
||||
await openAdminOverrideHistoryAction();
|
||||
return;
|
||||
}
|
||||
if (name === "handoff-to-main-agent") {
|
||||
try {
|
||||
setBusy(true, "正在为主 Agent 创建执行计划...");
|
||||
const payload = await createOneLinerRun({
|
||||
source_screen: action.dataset.sourceScreen || appState.screen || "dashboard",
|
||||
source_action_key: action.dataset.sourceActionKey || name,
|
||||
title: action.dataset.title || action.textContent?.trim() || "交给主 Agent 处理",
|
||||
summary: action.dataset.summary || "",
|
||||
intent_key: action.dataset.intentKey || "custom",
|
||||
platform: action.dataset.platform || getPreferredPlatform(),
|
||||
platform_scope: action.dataset.platformScope || "single_platform",
|
||||
plan_request: {
|
||||
goal: action.dataset.goal || action.dataset.title || action.textContent?.trim() || "交给主 Agent 处理",
|
||||
steps: parseJsonSafe(action.dataset.planSteps, []),
|
||||
summary: action.dataset.summary || ""
|
||||
}
|
||||
});
|
||||
appState.selectedOnelinerRunId = payload?.id || "";
|
||||
openOneLinerPanel();
|
||||
renderAll();
|
||||
} catch (error) {
|
||||
presentActionFailure(error, "主 Agent 接单失败");
|
||||
openOneLinerPanel();
|
||||
} finally {
|
||||
setBusy(false, "");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (name === "confirm-oneliner-run") {
|
||||
try {
|
||||
setBusy(true, "正在确认执行计划...");
|
||||
await confirmOneLinerRun(action.dataset.runId || "", "user confirmed");
|
||||
} catch (error) {
|
||||
presentActionFailure(error, "主 Agent 确认失败");
|
||||
} finally {
|
||||
setBusy(false, "");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (name === "cancel-oneliner-run") {
|
||||
try {
|
||||
setBusy(true, "正在取消当前任务...");
|
||||
await cancelOneLinerRun(action.dataset.runId || "", "user cancelled");
|
||||
} catch (error) {
|
||||
presentActionFailure(error, "主 Agent 取消失败");
|
||||
} finally {
|
||||
setBusy(false, "");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (name === "select-oneliner-run") {
|
||||
appState.selectedOnelinerRunId = action.dataset.runId || "";
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
if (name === "select-oneliner-session") {
|
||||
appState.selectedOnelinerSessionId = action.dataset.sessionId || "";
|
||||
await loadOneLinerMessages(appState.selectedOnelinerSessionId);
|
||||
|
||||
@@ -1130,11 +1130,17 @@ select {
|
||||
}
|
||||
|
||||
.oneliner-meta,
|
||||
.oneliner-runs,
|
||||
.oneliner-sessions {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.oneliner-run-card {
|
||||
border-color: rgba(79, 143, 238, 0.16);
|
||||
background: linear-gradient(180deg, rgba(248, 252, 255, 0.98) 0%, rgba(255, 255, 255, 0.98) 100%);
|
||||
}
|
||||
|
||||
.oneliner-messages {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
|
||||
@@ -113,6 +113,9 @@ test("oneliner submit failures stay inside the app instead of using a browser al
|
||||
test("agent control surfaces load governance endpoints for user and admin summaries", () => {
|
||||
const source = extractBetween(APP, "async function loadAgentControlSurfaces(projectId = \"\")", "async function loadOneLinerMessages(sessionId)");
|
||||
assert.match(source, /\/v2\/oneliner\/governance\/effective/);
|
||||
assert.match(source, /\/v2\/oneliner\/runs/);
|
||||
assert.match(APP, /function hydrateSelectedOneLinerRun\(\)/);
|
||||
assert.match(APP, /\/v2\/oneliner\/runs\/\$\{encodeURIComponent\(runId\)\}/);
|
||||
assert.match(source, /\/v2\/oneliner\/governance\/user\/global/);
|
||||
assert.match(source, /\/v2\/oneliner\/governance\/user\/platforms\/\$\{encodeURIComponent\(governancePlatform\)\}/);
|
||||
assert.match(source, /\/v2\/admin\/oneliner\/governance\/system\/main-agent/);
|
||||
@@ -124,15 +127,26 @@ test("agent control surfaces load governance endpoints for user and admin summar
|
||||
assert.match(source, /const targetProjectId = requestedProjectId === ""/);
|
||||
});
|
||||
|
||||
test("oneliner panel includes a dedicated runtime header for agent runs", () => {
|
||||
const source = extractBetween(APP, "function ensureOneLinerUi()", "function renderOneLinerSessionTabs()");
|
||||
const runtime = extractBetween(APP, "function renderOneLinerRunsHtml()", "function renderOneLinerMessagesHtml()");
|
||||
assert.match(source, /data-role="oneliner-runs"/);
|
||||
assert.match(runtime, /confirm-oneliner-run/);
|
||||
assert.match(runtime, /cancel-oneliner-run/);
|
||||
});
|
||||
|
||||
test("oneliner meta and action handlers expose governance entry points", () => {
|
||||
const meta = extractBetween(APP, "function renderOneLinerUi()", "function openOneLinerPanel()");
|
||||
const messages = extractBetween(APP, "function renderOneLinerMessagesHtml()", "function renderOneLinerUi()");
|
||||
const actions = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {");
|
||||
assert.match(meta, /open-user-global-policy/);
|
||||
assert.match(meta, /renderOneLinerRunsHtml\(\)/);
|
||||
assert.match(meta, /policyScopeTagLabel/);
|
||||
assert.match(messages, /active_admin_override_notice/);
|
||||
assert.match(actions, /name === "open-user-global-policy"/);
|
||||
assert.match(actions, /name === "open-system-main-policy"/);
|
||||
assert.match(actions, /name === "handoff-to-main-agent"/);
|
||||
assert.match(actions, /name === "confirm-oneliner-run"/);
|
||||
});
|
||||
|
||||
test("system governance saves refresh control surfaces after persisting", () => {
|
||||
|
||||
Reference in New Issue
Block a user