diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-capability-settings.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-capability-settings.tsx index c1e168c191..9faf3b3bfd 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-capability-settings.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-capability-settings.tsx @@ -439,7 +439,7 @@ function CapabilityCatalog({ callbacks, catalog, goalId, loading, notification, {!jsonValid ? {t("capabilities.jsonInvalid")} : null} } > : null} - {selected.capability_id === "reward_memory" && selected.current?.binding_status === "unbound" ?
{locale === "zh-CN" ? "当前没有启用的经验配置。" : "No experience configuration is currently enabled."}
: null} + {selected.capability_id === "reward_memory" && selected.current?.effective_available === false ?{locale === "zh-CN" ? "此 Goal 当前没有可用的经验配置。" : "No usable experience configuration is available in this Goal."}
: null} {selected.capability_id === "reward_memory" && Array.isArray(selected.current?.agent_bindings) && selected.current.agent_bindings.length > 0 ?diff --git a/examples/personal-workspace-browser/assets/memory-availability/after-desktop.png b/examples/personal-workspace-browser/assets/memory-availability/after-desktop.png new file mode 100644 index 0000000000..3b0f22d91e Binary files /dev/null and b/examples/personal-workspace-browser/assets/memory-availability/after-desktop.png differ diff --git a/examples/personal-workspace-browser/assets/memory-availability/after-mobile.png b/examples/personal-workspace-browser/assets/memory-availability/after-mobile.png new file mode 100644 index 0000000000..38b696a4aa Binary files /dev/null and b/examples/personal-workspace-browser/assets/memory-availability/after-mobile.png differ diff --git a/examples/personal-workspace-browser/assets/memory-availability/before-desktop.png b/examples/personal-workspace-browser/assets/memory-availability/before-desktop.png new file mode 100644 index 0000000000..29c892b87b Binary files /dev/null and b/examples/personal-workspace-browser/assets/memory-availability/before-desktop.png differ diff --git a/examples/personal-workspace-browser/capability-scope.mjs b/examples/personal-workspace-browser/capability-scope.mjs index 234b02b97b..85c9dd0a4a 100644 --- a/examples/personal-workspace-browser/capability-scope.mjs +++ b/examples/personal-workspace-browser/capability-scope.mjs @@ -95,6 +95,39 @@ export const capabilityScopeScenario = { await page.screenshot({ path: resolve(outputDir, "capability-composition-mobile.png"), animations: "disabled" }); if (await page.evaluate(() => document.documentElement.scrollWidth > innerWidth + 1)) throw new Error("Composition controls overflow the narrow viewport"); await page.setViewportSize({ width: 1512, height: 982 }); + // A failed sibling binding must not make an available private route look empty. + const memory = {enabled: true, binding_status: "unbound", effective_available: true, + registered_agents: ["reviewer", "writer"], enabled_agents: ["reviewer"], agent_bindings: [ + {agent_id: "writer", binding_source: "agent", enabled: true, available: true, status: "available"}, + {agent_id: "reviewer", binding_source: "goal", enabled: true, available: false, status: "config_invalid"}, + ]}; + api.goalMemoryConfigurations.set("research-monitor", memory); + await catalog.getByRole("button", {name: "记忆与资料", exact: true}).click(); + const reloadMemory = () => Promise.all([ + page.waitForResponse(response => new URL(response.url()).pathname === "/api/chat/goal-configuration" && response.request().method() === "GET"), + page.getByRole("button", {name: "重新读取配置", exact: true}).click(), + ]); + await reloadMemory(); + await page.getByRole("tab", {name: "经验学习", exact: true}).click(); + await page.getByRole("combobox", {name: "经验作用范围", exact: true}).selectOption("writer"); + const memoryReadback = page.getByRole("region", {name: "Agent 记忆绑定读回", exact: true}); + await memoryReadback.getByText(/独立绑定.*可用/u).waitFor(); + await memoryReadback.scrollIntoViewIfNeeded(); + if (await page.locator(".personal-memory-unbound").count()) throw new Error("An available Agent route is contradicted by a Goal-wide empty claim"); + await page.screenshot({path: resolve(outputDir, "capability-memory-available-desktop.png"), animations: "disabled"}); + await page.setViewportSize({width: 390, height: 844}); + await catalog.getByRole("button", {name: "记忆与资料", exact: true}).scrollIntoViewIfNeeded(); + await memoryReadback.scrollIntoViewIfNeeded(); + await page.screenshot({path: resolve(outputDir, "capability-memory-available-mobile.png"), animations: "disabled"}); + if (await page.evaluate(() => document.documentElement.scrollWidth > innerWidth + 1)) throw new Error("Memory scope overflows the narrow viewport"); + await page.setViewportSize({width: 1512, height: 982}); + api.goalMemoryConfigurations.set("research-monitor", {...memory, enabled: false, effective_available: false, + agent_bindings: memory.agent_bindings.map(row => ({...row, available: false, enabled: false, status: "disabled"}))}); + await reloadMemory(); + await page.locator(".personal-memory-unbound").waitFor({state: "visible"}); + api.goalMemoryConfigurations.set("research-monitor", {...memory, effective_available: undefined, agent_bindings: []}); + await reloadMemory(); + await page.locator(".personal-memory-unbound").waitFor({state: "hidden"}); await defaults.check(); await page.getByRole("navigation", { name: "机器能力目录" }).waitFor(); if (await page.locator(".personal-capability-preview").count()) throw new Error("A Goal preview crossed into device defaults");