diff --git a/.changeset/fn-7098-prompts-workflow-alignment.md b/.changeset/fn-7098-prompts-workflow-alignment.md new file mode 100644 index 0000000000..1f37a430be --- /dev/null +++ b/.changeset/fn-7098-prompts-workflow-alignment.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Settings → Prompts now links to Workflow Editor prompts and clarifies prompt ownership. +category: feature +dev: PromptsSection threads onOpenWorkflowSettings and reuses MovedSettingsStub; AgentPromptsManager tabs stay in Settings. diff --git a/docs/settings-reference.md b/docs/settings-reference.md index 32e0e22def..cf38359116 100644 --- a/docs/settings-reference.md +++ b/docs/settings-reference.md @@ -605,8 +605,8 @@ Default notes: | `missionStaleThresholdMs` | `number` | `600000` | Mission stale threshold in ms while `activating` (10 min). | | `missionMaxTaskRetries` | `number` | `3` | Max automatic retries for failed mission-linked tasks. | | `missionHealthCheckIntervalMs` | `number` | `300000` | Mission health-check interval in ms (5 min). | -| `agentPrompts` | `AgentPromptsConfig` | `undefined` | Custom role prompt templates and assignments. | -| `promptOverrides` | `Record` | `undefined` | Segment-level prompt overrides (set a key to `null` to clear it). | +| `agentPrompts` | `AgentPromptsConfig` | `undefined` | Custom role prompt templates and assignments edited in Settings → Prompts. | +| `promptOverrides` | `Record` | `undefined` | Global PromptKey segment-level overrides edited in Settings → Prompts (set a key to `null` to clear it). | | `reflectionEnabled` | `boolean` | `false` | Enable/disable agent self-reflection workflows. | | `reflectionIntervalMs` | `number` | `3600000` | Periodic reflection interval in ms. | | `reflectionAfterTask` | `boolean` | `true` | Trigger reflection after task completion. | @@ -1114,8 +1114,15 @@ For runtime details, see the [OpenClaw Runtime Plugin documentation](../plugins/ ## Prompt Overrides + + Fusion supports fine-grained customization of AI agent prompts through the `promptOverrides` setting. This enables surgical customization of specific prompt segments without replacing entire role prompts (which `agentPrompts` does). +Settings → Prompts owns `agentPrompts` role system prompt templates, role assignments, and global PromptKey segment overrides. Per-workflow step prompts for workflow `prompt` and `gate` nodes are edited in the Workflow Editor; the Prompts settings section includes an **Open workflow settings** link to jump to that surface. + ### Supported Prompt Keys | Key | Agent Role | Description | diff --git a/packages/dashboard/app/components/SettingsModal.tsx b/packages/dashboard/app/components/SettingsModal.tsx index b435c38412..57308cad92 100644 --- a/packages/dashboard/app/components/SettingsModal.tsx +++ b/packages/dashboard/app/components/SettingsModal.tsx @@ -3021,7 +3021,14 @@ export function SettingsModal({ /> ); case "prompts": - return ; + return ( + + ); case "plugins": return ( { + const { createDashboardApiMock } = await import("../../test/mockApi"); + return createDashboardApiMock(() => importOriginal(), { + fetchSettings: (...args: unknown[]) => mockFetchSettings(...args), + fetchSettingsByScope: (...args: unknown[]) => mockFetchSettingsByScope(...args), + updateSettings: (...args: unknown[]) => mockUpdateSettings(...args), + updateGlobalSettings: (...args: unknown[]) => mockUpdateGlobalSettings(...args), + fetchAuthStatus: (...args: unknown[]) => mockFetchAuthStatus(...args), + fetchModels: (...args: unknown[]) => mockFetchModels(...args), + }); +}); + +vi.mock("../../hooks/useMemoryBackendStatus", () => ({ + useMemoryBackendStatus: (...args: unknown[]) => mockUseMemoryBackendStatus(...args), +})); + +vi.mock("../../hooks/useMobileKeyboard", () => ({ + useMobileKeyboard: (...args: unknown[]) => mockUseMobileKeyboard(...args), +})); + +vi.mock("../../hooks/useConfirm", () => ({ + useConfirm: () => ({ confirm: (...args: unknown[]) => mockConfirm(...args) }), +})); + +vi.mock("../../hooks/useWorktrunkInstallStatus", () => ({ + useWorktrunkInstallStatus: (...args: unknown[]) => mockUseWorktrunkInstallStatus(...args), +})); + +vi.mock("../PluginManager", () => ({ + PluginManager: () =>
Plugin manager content
, +})); + +vi.mock("../PiExtensionsManager", () => ({ + PiExtensionsManager: () =>
Pi extensions content
, +})); + +describe("SettingsModal Prompts section", () => { + installSettingsModalEnv(); + + it("explains prompt ownership, opens workflow settings, and keeps prompt tabs", async () => { + const onOpenWorkflowSettings = vi.fn(); + mockFetchSettings.mockResolvedValue({ + ...defaultSettings, + agentPrompts: { + templates: [ + { + id: "custom-executor", + name: "Custom Executor", + description: "Custom executor system prompt", + role: "executor", + prompt: "Custom executor prompt", + }, + ], + }, + promptOverrides: { "executor-welcome": "Custom welcome" }, + }); + mockFetchSettingsByScope.mockResolvedValue({ + global: defaultSettings, + project: { + agentPrompts: { + templates: [ + { + id: "custom-executor", + name: "Custom Executor", + description: "Custom executor system prompt", + role: "executor", + prompt: "Custom executor prompt", + }, + ], + }, + promptOverrides: { "executor-welcome": "Custom welcome" }, + }, + }); + + renderModal({ initialSection: "prompts", onOpenWorkflowSettings }); + await waitForSettingsModalReady(); + + expect(screen.getByText(/agent role system prompt templates, role assignments, and global PromptKey segment overrides/i)).toBeInTheDocument(); + expect(screen.getByText(/Per-workflow step prompts for prompt and gate nodes live in the Workflow Editor/i)).toBeInTheDocument(); + + await settingsModalUser.click(screen.getByRole("button", { name: "Open workflow settings" })); + expect(onOpenWorkflowSettings).toHaveBeenCalledTimes(1); + + expect(screen.getByRole("button", { name: /Templates/i })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /Assignments/i })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /Overrides/i })).toBeInTheDocument(); + expect(screen.getByText("Custom Executor")).toBeInTheDocument(); + }); + + it("disables the workflow settings affordance when standalone wiring is absent", async () => { + renderModal({ initialSection: "prompts" }); + await waitForSettingsModalReady(); + + const button = screen.getByRole("button", { name: "Open workflow settings" }); + expect(button).toBeDisabled(); + await settingsModalUser.click(button); + }); +}); diff --git a/packages/dashboard/app/components/settings/sections/PromptsSection.tsx b/packages/dashboard/app/components/settings/sections/PromptsSection.tsx index a79947fa5a..c41e5a92f6 100644 --- a/packages/dashboard/app/components/settings/sections/PromptsSection.tsx +++ b/packages/dashboard/app/components/settings/sections/PromptsSection.tsx @@ -9,18 +9,39 @@ import type { ReactNode } from "react"; import { useTranslation } from "react-i18next"; import type { AgentPromptsConfig } from "@fusion/core"; import { AgentPromptsManager } from "../../AgentPromptsManager"; +import { MovedSettingsStub } from "./MovedSettingsStub"; import type { SectionBaseProps } from "./context"; export interface PromptsSectionProps extends SectionBaseProps { scopeBanner: ReactNode; + /** + * FNXC:Settings 2026-06-26-16:54: + * Settings Prompts and Workflow Editor prompts are distinct editing surfaces. Settings owns agent role templates plus PromptKey segment overrides, while this callback links users to per-workflow, per-node prompt/gate prompts in the Workflow Editor. + */ + onOpenWorkflowSettings?: () => void; } -export function PromptsSection({ scopeBanner, form, setForm }: PromptsSectionProps) { +export function PromptsSection({ scopeBanner, form, setForm, onOpenWorkflowSettings }: PromptsSectionProps) { const { t } = useTranslation("app"); return ( <> {scopeBanner}

{t("settings.nav.prompts", "Prompts")}

+
+ + {t( + "settings.prompts.surfaceExplanation", + "Use this section for agent role system prompt templates, role assignments, and global PromptKey segment overrides. Per-workflow step prompts for prompt and gate nodes are edited in the Workflow Editor.", + )} + +
+ {