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/);