feat: preserve direct action landing context
Some checks failed
StoryForge CI / Baseline checks (push) Has been cancelled
StoryForge CI / Backend tests (push) Has been cancelled
StoryForge CI / Web tests (push) Has been cancelled

This commit is contained in:
kris
2026-04-04 08:27:17 +08:00
parent ae4d4cd2ad
commit 3f93d5c088
3 changed files with 20 additions and 7 deletions

View File

@@ -16,6 +16,7 @@
- 前端新增统一的 `buildRecommendedActionAttrs(...)`,把 `job_id / review_id / platform / source_id` 这类上下文一起带进最近动作卡和执行结果卡,后续新增直接动作时不用再重复拼接跳转参数。
- 后端回归新增了 `review-draft / platform-self-check / generate-copy` 三类真实动作的推荐落点断言;前端回归则锁住了结果卡和最近动作卡必须使用统一的推荐动作属性映射。
- 这轮还顺手修掉了一个真实 bug保存录制源时usage 记账错误地读取了 `binding["id"]`,现在已改成兼容 `binding_id / id`,不会再因为键名差异导致录制源创建链路直接报错。
- 当前运行卡、最近完成、主 Agent 结果卡、平台 Agent 最近执行这几处“回到业务页”入口,现在也全部切到同一套结构化属性映射,不再只带 `run_id / screen`,从这些入口继续跳转时也能保留 `job_id / review_id / source_id` 这类精确上下文。
### 主 Agent 消息卡补齐配置追溯与主动作执行上下文

View File

@@ -1108,7 +1108,7 @@ function renderOneLinerRunsHtml() {
title: currentRun.title || currentRun.plan?.goal || "主 Agent 任务",
summary: previewAction?.summary || currentRun.summary || ""
});
const resultLandingAttrs = buildMainAgentLandingAttrs({
const resultLandingAttrs = buildRecommendedActionAttrs(recommendedAction, {
runId: currentRun.id || "",
screen: recommendedAction?.screen || "",
title: currentRun.title || currentRun.plan?.goal || "主 Agent 任务",
@@ -1304,7 +1304,7 @@ function renderOneLinerRunsHtml() {
<div class="list" style="margin-top:10px;">
${recentCompletedRuns.map((item) => {
const doneAction = item.result?.recommended_action || null;
const doneLandingAttrs = buildMainAgentLandingAttrs({
const doneLandingAttrs = buildRecommendedActionAttrs(doneAction, {
runId: item.id || "",
screen: doneAction?.screen || "",
title: item.title || item.plan?.goal || "主 Agent 任务",
@@ -2019,7 +2019,7 @@ function renderOneLinerExecutionPayloadHtml(payload) {
platformAgentProfile,
latestPlatformAgentProfile.current_version || {}
);
const landingAttrs = buildMainAgentLandingAttrs({
const landingAttrs = buildRecommendedActionAttrs(payload.recommended_action, {
runId: landingRunId,
screen: landingScreen,
title: landingTitle,
@@ -2036,7 +2036,7 @@ function renderOneLinerExecutionPayloadHtml(payload) {
${currentRunOnelinerConfigStale ? `<span class="tag orange">主配置已更新</span>` : ""}
${currentRunPlatformAgentConfigStale ? `<span class="tag orange">平台 Agent 已更新</span>` : ""}
<span class="tag green">已收口</span>
${payload.recommended_action?.action ? `<span class="tag clickable-tag" data-action="${escapeHtml(payload.recommended_action.action)}" data-main-agent-run-id="${escapeHtml(landingRunId)}" data-main-agent-screen="${escapeHtml(landingScreen)}" data-main-agent-title="${escapeHtml(landingTitle)}" data-main-agent-summary="${escapeHtml(landingSummary)}" ${landingAttrs}>${escapeHtml(payload.recommended_action.label || "回到对应页面")}</span>` : ""}
${payload.recommended_action?.action ? `<span class="tag clickable-tag" data-action="${escapeHtml(payload.recommended_action.action)}" ${landingAttrs}>${escapeHtml(payload.recommended_action.label || "回到对应页面")}</span>` : ""}
</div>
</div>
${configVersion.version_no ? `
@@ -4586,7 +4586,7 @@ function renderPlatformAgentPanel() {
${item.recent_execution.source_screen ? `<span class="tag">${escapeHtml(screenLabel(item.recent_execution.source_screen) || item.recent_execution.source_screen)}</span>` : ""}
</div>
<div class="task-meta" style="margin-top:8px;">
${item.recent_execution.recommended_action?.action ? `<span class="tag clickable-tag" data-action="${escapeHtml(item.recent_execution.recommended_action.action)}" ${buildMainAgentLandingAttrs({ runId: item.recent_execution.run_id, screen: item.recent_execution.recommended_action.screen || item.recent_execution.source_screen, title: item.recent_execution.recommended_action.label || "回到业务页", summary: item.recent_execution.recommended_action.summary || item.recent_execution.summary || "" })}>${escapeHtml(item.recent_execution.recommended_action.label || "回到业务页")}</span>` : ""}
${item.recent_execution.recommended_action?.action ? `<span class="tag clickable-tag" data-action="${escapeHtml(item.recent_execution.recommended_action.action)}" ${buildRecommendedActionAttrs(item.recent_execution.recommended_action, { runId: item.recent_execution.run_id, screen: item.recent_execution.recommended_action.screen || item.recent_execution.source_screen, title: item.recent_execution.recommended_action.label || "回到业务页", summary: item.recent_execution.recommended_action.summary || item.recent_execution.summary || "" })}>${escapeHtml(item.recent_execution.recommended_action.label || "回到业务页")}</span>` : ""}
${item.recent_execution.oneliner_profile_version_no ? `<span class="tag clickable-tag" data-action="open-oneliner-profile-history" data-version-id="${escapeHtml(item.recent_execution.oneliner_profile_version_id || "")}">主配置历史</span>` : ""}
${item.recent_execution.platform_agent_profile_version_no ? `<span class="tag clickable-tag" data-action="open-platform-agent-profile-history" data-platform="${escapeHtml(item.platform)}" data-version-id="${escapeHtml(item.recent_execution.platform_agent_profile_version_id || "")}">平台配置历史</span>` : ""}
<span class="tag clickable-tag" data-action="open-oneliner-run-result" data-run-id="${escapeHtml(item.recent_execution.run_id)}">查看执行结果</span>
@@ -9755,7 +9755,7 @@ async function openPlatformAgentDetailAction(platform) {
${profile.recent_execution.source_screen ? `<span class="tag">${escapeHtml(screenLabel(profile.recent_execution.source_screen) || profile.recent_execution.source_screen)}</span>` : ""}
</div>
<div class="task-meta" style="margin-top:8px;">
${profile.recent_execution.recommended_action?.action ? `<span class="tag clickable-tag" data-action="${escapeHtml(profile.recent_execution.recommended_action.action)}" ${buildMainAgentLandingAttrs({ runId: profile.recent_execution.run_id, screen: profile.recent_execution.recommended_action.screen || profile.recent_execution.source_screen, title: profile.recent_execution.recommended_action.label || "回到业务页", summary: profile.recent_execution.recommended_action.summary || profile.recent_execution.summary || "" })}>${escapeHtml(profile.recent_execution.recommended_action.label || "回到业务页")}</span>` : ""}
${profile.recent_execution.recommended_action?.action ? `<span class="tag clickable-tag" data-action="${escapeHtml(profile.recent_execution.recommended_action.action)}" ${buildRecommendedActionAttrs(profile.recent_execution.recommended_action, { runId: profile.recent_execution.run_id, screen: profile.recent_execution.recommended_action.screen || profile.recent_execution.source_screen, title: profile.recent_execution.recommended_action.label || "回到业务页", summary: profile.recent_execution.recommended_action.summary || profile.recent_execution.summary || "" })}>${escapeHtml(profile.recent_execution.recommended_action.label || "回到业务页")}</span>` : ""}
${profile.recent_execution.oneliner_profile_version_no ? `<span class="tag clickable-tag" data-action="open-oneliner-profile-history" data-version-id="${escapeHtml(profile.recent_execution.oneliner_profile_version_id || "")}">主配置历史</span>` : ""}
${profile.recent_execution.platform_agent_profile_version_no ? `<span class="tag clickable-tag" data-action="open-platform-agent-profile-history" data-platform="${escapeHtml(normalizedPlatform)}" data-version-id="${escapeHtml(profile.recent_execution.platform_agent_profile_version_id || "")}">平台配置历史</span>` : ""}
<span class="tag clickable-tag" data-action="open-oneliner-run-result" data-run-id="${escapeHtml(profile.recent_execution.run_id)}">查看执行结果</span>

View File

@@ -773,6 +773,18 @@ test("direct oneliner execution results preserve structured follow-up attrs", ()
assert.match(lastAction, /actionTag\(recommendedAction\.label \|\| "回到对应页面"/);
});
test("main agent runtime and platform recent execution preserve structured follow-up attrs", () => {
const runtime = extractBetween(APP, "function renderOneLinerRunsHtml()", "function renderOneLinerMessagesHtml()");
const execution = extractBetween(APP, "function renderOneLinerExecutionPayloadHtml(payload)", "function parseOneLinerActionPayloadValue(value)");
const playbook = extractBetween(APP, "function renderPlatformAgentPanel()", "function renderAdminOpsPanel()");
const detail = extractBetween(APP, "async function openPlatformAgentDetailAction(platform)", "function openPlatformSkillReviewAction(platform, skillId, accepted)");
assert.match(runtime, /const resultLandingAttrs = buildRecommendedActionAttrs\(recommendedAction/);
assert.match(runtime, /const doneLandingAttrs = buildRecommendedActionAttrs\(doneAction/);
assert.match(execution, /const landingAttrs = buildRecommendedActionAttrs\(payload\.recommended_action/);
assert.match(playbook, /buildRecommendedActionAttrs\(item\.recent_execution\.recommended_action/);
assert.match(detail, /buildRecommendedActionAttrs\(profile\.recent_execution\.recommended_action/);
});
test("platform agent profiles expose history, rollback, and execution version context", () => {
const actions = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {");
const profileEditor = extractBetween(APP, "function openPlatformAgentProfileAction(platform)", "async function openPlatformAgentProfileHistoryAction(platform, preferredVersionId = \"\")");
@@ -835,7 +847,7 @@ test("main agent route actions keep landing context and destination screens rend
const strategy = extractBetween(APP, "function renderStrategyScreen()", "function renderCreditsScreen()");
const landing = extractBetween(APP, "function renderMainAgentLandingNotice(screenKey)", "function renderEmptyState(title, description)");
const production = extractBetween(APP, "function renderProductionScreen()", "function renderReviewScreen()");
assert.match(execution, /data-main-agent-run-id/);
assert.match(execution, /buildRecommendedActionAttrs\(payload\.recommended_action/);
assert.match(actions, /captureMainAgentLandingContext\(action,\s*"goto-production"/);
assert.match(actions, /captureMainAgentLandingContext\(action,\s*"goto-strategy"/);
assert.match(actions, /name === "dismiss-main-agent-landing"/);