From 7a75f1cd850d821146af53dd9b51a6040a15120d Mon Sep 17 00:00:00 2001 From: kris Date: Sun, 5 Apr 2026 07:08:47 +0800 Subject: [PATCH] feat: direct-execute discovery selected-account actions --- CHANGELOG.md | 5 + web/storyforge-web-v4/assets/app.js | 219 ++++++++++++++++-- .../tests/workbench-pages.test.mjs | 19 +- 3 files changed, 222 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca0d0b9..cb1f062 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -446,3 +446,8 @@ - `OneLiner / 主 Agent` 现在新增了 `直接查相似账号` 和 `直接存对标关系` 两条真实执行动作,不再只停留在“建议后跳回找对标”。 - `直接查相似账号` 会调用当前平台的相似搜索接口,返回真实候选数量,并在有候选账号时直接落到该账号详情。 - `直接存对标关系` 会优先复用最近一次相似搜索的候选,把它直接写入当前平台的对标关系,并把结果回写到找对标工作区。 + +### 找对标顶部动作改成 direct-execute + +- `导入当前对标 / 加入跟踪 / 账号分析 / 高分分析 / 查相似 / 存对标` 这批高频动作现在默认直接执行,不再先开表单。 +- 执行后会按真实 `recommended_action` 继续落到任务详情、当前对象或关系区;只有当前没有可直接执行的候选时,才回退到原来的高级表单。 diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js index 11accd4..88f949a 100644 --- a/web/storyforge-web-v4/assets/app.js +++ b/web/storyforge-web-v4/assets/app.js @@ -2361,23 +2361,108 @@ async function executeOneLinerAction(executorKey, options = {}) { if (appState.selectedOnelinerSessionId) { await loadOneLinerMessages(appState.selectedOnelinerSessionId); } - openActionModal({ - title: payload.title || "OneLiner 执行结果", - description: payload.summary || "已完成一次对话内执行。", - hideSubmit: true, - fields: [ - { - type: "html", - label: "执行结果", - html: `
${renderOneLinerExecutionPayloadHtml(payload.payload || {})}
` - } - ] - }); + if (options.showResultModal !== false) { + openActionModal({ + title: payload.title || "OneLiner 执行结果", + description: payload.summary || "已完成一次对话内执行。", + hideSubmit: true, + fields: [ + { + type: "html", + label: "执行结果", + html: `
${renderOneLinerExecutionPayloadHtml(payload.payload || {})}
` + } + ] + }); + } rememberAction("OneLiner 已执行", payload.summary || "当前动作已在对话内执行。", "green", payload); renderAll(); return payload; } +async function followRecommendedActionResult(payload, options = {}) { + const recommendedAction = payload?.recommended_action && typeof payload.recommended_action === "object" + ? payload.recommended_action + : null; + if (!recommendedAction?.action) return false; + const actionName = String(recommendedAction.action || "").trim(); + const platform = normalizePlatformValue( + recommendedAction.platform || payload?.payload?.platform || options.platform || getPreferredPlatform(), + getPreferredPlatform() + ); + if (options.refreshWorkbench) { + await bootstrap(); + } + if (actionName === "open-job-detail" && recommendedAction.job_id) { + closeActionModal(); + openJobDetailAction(recommendedAction.job_id); + return true; + } + if (actionName === "select-account" && recommendedAction.account_id) { + closeActionModal(); + await loadPlatformAccount(platform, recommendedAction.account_id); + if (options.discoveryFocus === "relations") { + focusDiscoveryRelations(); + } else if (options.discoveryFocus === "snapshots") { + focusDiscoveryInsights(); + } else if (options.discoveryFocus === "top-videos") { + focusDiscoveryTopVideoInsights(); + } else { + focusDiscoveryDetailTab("overview"); + } + return true; + } + if (actionName === "goto-tracking") { + closeActionModal(); + focusTrackingWorkspace(); + return true; + } + if (actionName === "open-edit-assistant" && recommendedAction.assistant_id) { + closeActionModal(); + focusPlaybookWorkspace(recommendedAction.assistant_id); + return true; + } + if (actionName === "open-review-edit" && recommendedAction.review_id) { + closeActionModal(); + focusReviewWorkspace(recommendedAction.review_id); + return true; + } + if (actionName === "goto-discovery") { + closeActionModal(); + if (options.discoveryFocus === "relations") { + focusDiscoveryRelations(); + } else { + focusDiscoveryDetailTab("overview"); + } + return true; + } + return false; +} + +async function runDirectDiscoveryAction(executorKey, payload, options = {}) { + const account = requireSelectedAccountRow(); + const platform = getAccountPlatform(account); + setBusy(true, options.busyLabel || "正在执行当前动作..."); + try { + const result = await executeOneLinerAction(executorKey, { + platform, + payload, + showResultModal: false + }); + await followRecommendedActionResult(result, { + platform, + refreshWorkbench: true, + discoveryFocus: options.discoveryFocus || "" + }); + return result; + } catch (error) { + presentActionFailure(error, options.errorTitle || "执行失败"); + throw error; + } finally { + setBusy(false, ""); + } +} + function openCurrentOneLinerRunResultAction(runId = "") { openOneLinerRunContextAction(runId) .then((currentRun) => { @@ -6017,6 +6102,8 @@ function renderDiscoveryScreen() { const topVideos = getHighScoreVideos(3); const latestVideos = getLatestVideos(2); const similarCandidates = safeArray(appState.lastSimilaritySearch?.candidates).slice(0, 5); + const saveBenchmarkActionName = similarCandidates.length ? "direct-save-benchmark-link" : "open-benchmark-link"; + const saveBenchmarkActionLabel = similarCandidates.length ? "直接存对标" : "存对标"; const selectedProject = getSelectedProject(); const importedSources = getCurrentProjectSourcesForAccount(selected, selectedProject?.id || ""); const tracked = selected?.id ? isTrackedAccount(selected.id) : false; @@ -6083,8 +6170,8 @@ function renderDiscoveryScreen() { detailBodyHtml = renderDiscoveryRelationsSection(linkedAccounts, similarCandidates); } const discoveryActionsHtml = isMobileUi - ? `${button("导入主页", "open-import-homepage")} ${button("交给主 Agent", "handoff-to-main-agent", "secondary", { attrs: discoveryHandoffAttrs })} ${button("存对标", "open-benchmark-link", "primary")}` - : `${button("导入主页", "open-import-homepage")} ${button("导入当前对标", "open-import-selected-account", "secondary")} ${button(tracked ? "已在跟踪" : "加入跟踪", "open-track-selected-account", "secondary")} ${button("账号分析", "analyze-selected-account", "secondary")} ${button("高分分析", "analyze-top-videos", "secondary")} ${button("查相似", "open-similar-search", "secondary")} ${button("交给主 Agent", "handoff-to-main-agent", "secondary", { attrs: discoveryHandoffAttrs })} ${button("存对标", "open-benchmark-link", "primary")}`; + ? `${button("导入主页", "open-import-homepage")} ${button("交给主 Agent", "handoff-to-main-agent", "secondary", { attrs: discoveryHandoffAttrs })} ${button(saveBenchmarkActionLabel, saveBenchmarkActionName, "primary")}` + : `${button("导入主页", "open-import-homepage")} ${button("导入当前对标", "direct-import-selected-account", "secondary")} ${button(tracked ? "更新跟踪" : "加入跟踪", "direct-track-selected-account", "secondary")} ${button("账号分析", "direct-analyze-selected-account", "secondary")} ${button("高分分析", "direct-analyze-top-videos", "secondary")} ${button("查相似", "direct-search-similar", "secondary")} ${button("交给主 Agent", "handoff-to-main-agent", "secondary", { attrs: discoveryHandoffAttrs })} ${button(saveBenchmarkActionLabel, saveBenchmarkActionName, "primary")}`; return screenShell( "找对标", `这里已经接入真实${currentPlatformLabel}账号列表和单账号详情。`, @@ -6119,9 +6206,9 @@ function renderDiscoveryScreen() {

${escapeHtml(selected ? `先围绕 ${getAccountName(selected)} 做导入、分析和相似扩展。` : "先从账号池里选一个对象,再继续导入和分析。")}

- ${actionTag("导入当前对标", "open-import-selected-account")} - ${actionTag("账号分析", "analyze-selected-account")} - ${actionTag("查相似", "open-similar-search")} + ${actionTag("导入当前对标", "direct-import-selected-account")} + ${actionTag("账号分析", "direct-analyze-selected-account")} + ${actionTag("查相似", "direct-search-similar")}
@@ -6206,9 +6293,9 @@ function renderDiscoveryScreen() {

${escapeHtml(selected ? `先围绕 ${getAccountName(selected)} 做导入、分析和相似扩展。` : "先从账号池里选一个对象,再继续导入和分析。")}

- ${actionTag("导入当前对标", "open-import-selected-account")} - ${actionTag("账号分析", "analyze-selected-account")} - ${actionTag("查相似", "open-similar-search")} + ${actionTag("导入当前对标", "direct-import-selected-account")} + ${actionTag("账号分析", "direct-analyze-selected-account")} + ${actionTag("查相似", "direct-search-similar")}
@@ -11927,6 +12014,35 @@ document.addEventListener("click", async (event) => { openTrackSelectedAccountAction(); return; } + if (name === "direct-import-selected-account") { + const account = requireSelectedAccountRow(); + const sourceUrl = String(getAccountProfileUrl(account) || "").trim(); + if (!sourceUrl) { + openImportSelectedAccountAction(); + return; + } + await runDirectDiscoveryAction("import-homepage", { + source_url: sourceUrl, + title: `${getAccountName(account) || "当前对标"} 对标主页`, + handle: getAccountHandle(account) || "" + }, { + busyLabel: "正在把当前对标接入项目...", + errorTitle: "导入当前对标失败" + }); + return; + } + if (name === "direct-track-selected-account") { + const account = requireSelectedAccountRow(); + await runDirectDiscoveryAction("track-account", { + target_account_id: account.id, + note: "由对标工作台直接加入跟踪。", + refresh_now: true + }, { + busyLabel: "正在更新跟踪状态...", + errorTitle: "加入跟踪失败" + }); + return; + } if (name === "refresh-tracked-account") { try { await refreshTrackedAccountAction(action.dataset.trackedAccountId || ""); @@ -12167,10 +12283,40 @@ document.addEventListener("click", async (event) => { openAnalyzeSelectedAccountAction(); return; } + if (name === "direct-analyze-selected-account") { + const account = requireSelectedAccountRow(); + await runDirectDiscoveryAction("analyze-account", { + target_account_id: account.id, + max_videos: 6, + extra_focus: "", + temperature: 0.35, + auto_analyze_top_videos: true, + top_video_analysis_count: 4 + }, { + busyLabel: "正在分析当前对标账号...", + errorTitle: "账号分析失败", + discoveryFocus: "snapshots" + }); + return; + } if (name === "analyze-top-videos") { openAnalyzeTopVideosAction(); return; } + if (name === "direct-analyze-top-videos") { + const account = requireSelectedAccountRow(); + await runDirectDiscoveryAction("analyze-top-videos", { + target_account_id: account.id, + top_video_count: 5, + min_score: 45, + temperature: 0.25 + }, { + busyLabel: "正在分析高分作品...", + errorTitle: "高分作品分析失败", + discoveryFocus: "top-videos" + }); + return; + } if (name === "open-generate-copy") { openGenerateCopyAction(); return; @@ -12216,10 +12362,43 @@ document.addEventListener("click", async (event) => { openSimilaritySearchAction(); return; } + if (name === "direct-search-similar") { + const account = requireSelectedAccountRow(); + await runDirectDiscoveryAction("search-similar-accounts", { + target_account_id: account.id, + max_candidates: 8, + extra_requirements: "" + }, { + busyLabel: "正在查找相似账号...", + errorTitle: "查相似失败", + discoveryFocus: "relations" + }); + return; + } if (name === "open-benchmark-link") { openBenchmarkLinkAction(); return; } + if (name === "direct-save-benchmark-link") { + const account = requireSelectedAccountRow(); + const candidate = safeArray(appState.lastSimilaritySearch?.candidates)[0] || null; + if (!candidate) { + openBenchmarkLinkAction(); + return; + } + await runDirectDiscoveryAction("save-benchmark-link", { + source_account_id: account.id, + target_account_id: candidate.candidate_account_id || "", + target_profile_url: candidate.candidate_account_id ? "" : (candidate.candidate_profile_url || ""), + search_id: appState.lastSimilaritySearch?.id || "", + note: brief(candidate.rationale_text || "由对标工作台直接加入对标库。", 120) + }, { + busyLabel: "正在保存对标关系...", + errorTitle: "保存对标失败", + discoveryFocus: "relations" + }); + return; + } if (name === "save-candidate-benchmark") { setBusy(true, "正在保存对标关系..."); try { diff --git a/web/storyforge-web-v4/tests/workbench-pages.test.mjs b/web/storyforge-web-v4/tests/workbench-pages.test.mjs index 6062b90..efc2cc9 100644 --- a/web/storyforge-web-v4/tests/workbench-pages.test.mjs +++ b/web/storyforge-web-v4/tests/workbench-pages.test.mjs @@ -433,13 +433,30 @@ test("mobile discovery and production simplify duplicated top-level actions", () assert.match(discovery, /const isMobileUi = isMobileViewport\(\);/); assert.match(discovery, /const discoveryActionsHtml = isMobileUi/); assert.match(discovery, /button\("导入主页", "open-import-homepage"\)/); - assert.match(discovery, /button\("存对标", "open-benchmark-link"/); + assert.match(discovery, /button\("导入当前对标", "direct-import-selected-account"/); + assert.match(discovery, /button\("查相似", "direct-search-similar"/); + assert.match(discovery, /saveBenchmarkActionName = similarCandidates\.length \? "direct-save-benchmark-link" : "open-benchmark-link"/); assert.match(production, /const isMobileUi = isMobileViewport\(\);/); assert.match(production, /const productionActionsHtml = isMobileUi/); assert.match(production, /button\("交给主 Agent", "handoff-to-main-agent"/); assert.match(production, /button\("去复盘", "goto-review", "primary"\)/); }); +test("discovery page promotes selected-account actions into direct execute flows", () => { + const discovery = extractBetween(APP, "function renderDiscoveryScreen()", "function renderTrackingScreen()"); + assert.match(APP, /async function runDirectDiscoveryAction\(executorKey, payload, options = \{\}\)/); + assert.match(APP, /async function followRecommendedActionResult\(payload, options = \{\}\)/); + assert.match(discovery, /actionTag\("导入当前对标", "direct-import-selected-account"\)/); + assert.match(discovery, /actionTag\("账号分析", "direct-analyze-selected-account"\)/); + assert.match(discovery, /actionTag\("查相似", "direct-search-similar"\)/); + assert.match(APP, /if \(name === "direct-import-selected-account"\)/); + assert.match(APP, /if \(name === "direct-track-selected-account"\)/); + assert.match(APP, /if \(name === "direct-analyze-selected-account"\)/); + assert.match(APP, /if \(name === "direct-analyze-top-videos"\)/); + assert.match(APP, /if \(name === "direct-search-similar"\)/); + assert.match(APP, /if \(name === "direct-save-benchmark-link"\)/); +}); + test("mobile discovery prioritizes the selected-account task flow before the scrollable account list", () => { const discovery = extractBetween(APP, "function renderDiscoveryScreen()", "function renderTrackingScreen()"); assert.match(discovery, /mobile-discovery-priority/);