diff --git a/.changeset/task-chat-new-command-guard.md b/.changeset/task-chat-new-command-guard.md new file mode 100644 index 0000000000..ceef9b5908 --- /dev/null +++ b/.changeset/task-chat-new-command-guard.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: /new and /clear in Chat no longer wipe a task-bound planner chat's history. +category: fix +dev: ChatView's exact `/new`//`/clear` intercept now recognizes `task-planner:` sessions (surfaced in the common feed via `showTaskChatsInCommonFeed`) and consumes the command with a warning toast instead of calling createSession. diff --git a/packages/dashboard/app/components/ChatView.tsx b/packages/dashboard/app/components/ChatView.tsx index a832af4671..f99163524f 100644 --- a/packages/dashboard/app/components/ChatView.tsx +++ b/packages/dashboard/app/components/ChatView.tsx @@ -22,7 +22,7 @@ import { PinOff, MoreHorizontal, } from "lucide-react"; -import { FN_AGENT_ID, useChat, type ChatMessageInfo } from "../hooks/useChat"; +import { FN_AGENT_ID, TASK_PLANNER_CHAT_AGENT_ID_PREFIX, useChat, type ChatMessageInfo } from "../hooks/useChat"; import { RoomMessageDeliveredButReplyFailedError, useChatRooms } from "../hooks/useChatRooms"; import { useChatUnread } from "../hooks/useChatUnread"; import { useViewportMode } from "./Header"; @@ -1854,6 +1854,18 @@ export function ChatView({ projectId, addToast, floating = false, compactLayout } if (trimmed === "/clear" || trimmed === "/new") { + /* + FNXC:ChatSlashCommands 2026-07-23-12:00: + `/new`//`/clear` must never wipe a task-bound planner chat. With `showTaskChatsInCommonFeed` + enabled, task-planner sessions appear in the common Direct feed, so a user can run `/new` + against one directly — but that transcript is the task's planner history, and createSession + would orphan it behind a fresh session. Consume the command with feedback instead of clearing. + */ + if (activeSession.agentId.startsWith(TASK_PLANNER_CHAT_AGENT_ID_PREFIX)) { + clearComposerState(); + addToast(t("chat.newNotAllowedForTaskChat", "This chat is tied to a task — /new and /clear can't clear it"), "warning"); + return; + } clearComposerState(); clearPendingMessage(); stopStreaming(); diff --git a/packages/dashboard/app/components/__tests__/ChatView.core.test.tsx b/packages/dashboard/app/components/__tests__/ChatView.core.test.tsx index 67f3cbbf62..24fbecbffe 100644 --- a/packages/dashboard/app/components/__tests__/ChatView.core.test.tsx +++ b/packages/dashboard/app/components/__tests__/ChatView.core.test.tsx @@ -1300,6 +1300,41 @@ describe("ChatView", () => { expect(clearPendingMessage).toHaveBeenCalledTimes(1); }); + /* + FNXC:ChatSlashCommands 2026-07-23-12:00: + With `showTaskChatsInCommonFeed` enabled, task-planner sessions (`task-planner:`) + are selectable in the common Chat feed. `/new`//`/clear` there must not replace the session — + the transcript is the task's planner history. Invariant covers both command spellings. + */ + it.each(["/new", "/clear"])("does not clear a task-bound planner chat on %s", async (command) => { + const sendMessage = vi.fn(); + const createSession = vi.fn(); + const stopStreaming = vi.fn(); + const clearPendingMessage = vi.fn(); + const addToast = vi.fn(); + + setupMockChat({ + activeSession: { ...activeSessionFixture, agentId: "task-planner:task-123" }, + messages: [], + sendMessage, + createSession, + stopStreaming, + clearPendingMessage, + }); + + await renderWithAct(); + + const textarea = screen.getByTestId("chat-input"); + await userEvent.type(textarea, ` ${command} {enter}`); + + expect(sendMessage).not.toHaveBeenCalled(); + expect(createSession).not.toHaveBeenCalled(); + expect(stopStreaming).not.toHaveBeenCalled(); + expect(clearPendingMessage).not.toHaveBeenCalled(); + expect(addToast).toHaveBeenCalledWith(expect.stringContaining("tied to a task"), "warning"); + expect(textarea).toHaveValue(""); + }); + it("does not intercept non-exact /new text", async () => { const sendMessage = vi.fn(); const createSession = vi.fn(); @@ -1315,7 +1350,7 @@ describe("ChatView", () => { const textarea = screen.getByTestId("chat-input"); await userEvent.type(textarea, "/new now{enter}"); - expect(sendMessage).toHaveBeenCalledWith("/new now", []); + expect(sendMessage).toHaveBeenCalledWith("/new now", [], expect.objectContaining({ onDelivered: expect.any(Function), onFailed: expect.any(Function) })); expect(createSession).not.toHaveBeenCalled(); }); @@ -1334,7 +1369,7 @@ describe("ChatView", () => { const textarea = screen.getByTestId("chat-input"); await userEvent.type(textarea, "/clear now{enter}"); - expect(sendMessage).toHaveBeenCalledWith("/clear now", []); + expect(sendMessage).toHaveBeenCalledWith("/clear now", [], expect.objectContaining({ onDelivered: expect.any(Function), onFailed: expect.any(Function) })); expect(createSession).not.toHaveBeenCalled(); }); @@ -1351,7 +1386,7 @@ describe("ChatView", () => { const textarea = screen.getByTestId("chat-input"); await userEvent.type(textarea, "Hello world{enter}"); - expect(sendMessage).toHaveBeenCalledWith("Hello world", []); + expect(sendMessage).toHaveBeenCalledWith("Hello world", [], expect.objectContaining({ onDelivered: expect.any(Function), onFailed: expect.any(Function) })); }); it("sends message on touch tap when the synthetic click is suppressed (mobile)", async () => { @@ -1381,7 +1416,7 @@ describe("ChatView", () => { }); expect(sendMessage).toHaveBeenCalledTimes(1); - expect(sendMessage).toHaveBeenCalledWith("Touch hello", []); + expect(sendMessage).toHaveBeenCalledWith("Touch hello", [], expect.objectContaining({ onDelivered: expect.any(Function), onFailed: expect.any(Function) })); } finally { Object.defineProperty(window, "innerWidth", { value: originalInnerWidth, configurable: true }); } @@ -1404,7 +1439,7 @@ describe("ChatView", () => { fireEvent.pointerDown(screen.getByTestId("chat-send-btn"), { pointerType: "touch" }); }); expect(sendMessage).toHaveBeenCalledTimes(1); - expect(sendMessage).toHaveBeenLastCalledWith("Direct first", []); + expect(sendMessage).toHaveBeenLastCalledWith("Direct first", [], expect.objectContaining({ onDelivered: expect.any(Function), onFailed: expect.any(Function) })); await act(async () => { await new Promise((resolve) => window.setTimeout(resolve, 0)); }); @@ -1415,7 +1450,7 @@ describe("ChatView", () => { }); expect(sendMessage).toHaveBeenCalledTimes(2); - expect(sendMessage).toHaveBeenLastCalledWith("Direct second", []); + expect(sendMessage).toHaveBeenLastCalledWith("Direct second", [], expect.objectContaining({ onDelivered: expect.any(Function), onFailed: expect.any(Function) })); viewportSpy.mockRestore(); }); @@ -1531,7 +1566,7 @@ describe("ChatView", () => { const textarea = screen.getByTestId("chat-input") as HTMLTextAreaElement; await userEvent.type(textarea, "Direct hello{enter}"); - expect(sendMessage).toHaveBeenCalledWith("Direct hello", []); + expect(sendMessage).toHaveBeenCalledWith("Direct hello", [], expect.objectContaining({ onDelivered: expect.any(Function), onFailed: expect.any(Function) })); expect(sendRoomMessage).not.toHaveBeenCalled(); expect(textarea.value).toBe(""); localStorage.removeItem("fusion:chat-scope"); diff --git a/packages/dashboard/app/hooks/useChat.ts b/packages/dashboard/app/hooks/useChat.ts index 4bfd6fb038..6c1bf00043 100644 --- a/packages/dashboard/app/hooks/useChat.ts +++ b/packages/dashboard/app/hooks/useChat.ts @@ -25,7 +25,15 @@ const ACTIVE_SESSION_STORAGE_KEY = "kb-chat-active-session"; * Model-loop direct sessions store this sentinel agent id so the UI and hook share one target-mode check instead of duplicating the literal in each composer surface. */ export const FN_AGENT_ID = "__fn_agent__"; -const TASK_PLANNER_CHAT_AGENT_ID_PREFIX = "task-planner:"; +/** + * FNXC:ChatSlashCommands 2026-07-23-12:00: + * Exported (as a primitive, which survives the test harness's useChat automock) so composer + * surfaces can recognize a task-bound planner session (`task-planner:`). Task chats + * surfaced in the common Direct feed via `showTaskChatsInCommonFeed` must never be cleared or + * replaced by `/new`//`/clear` — the transcript IS the task's planner history, not a disposable + * direct conversation. + */ +export const TASK_PLANNER_CHAT_AGENT_ID_PREFIX = "task-planner:"; /** FNXC:ChatPinned 2026-07-16-12:00: one comparator keeps refresh, cache, * optimistic mutations, SSE updates, and search results pinned-first. */ diff --git a/packages/i18n/locales/en/app.json b/packages/i18n/locales/en/app.json index bc61b3d56f..26e3d8ccf6 100644 --- a/packages/i18n/locales/en/app.json +++ b/packages/i18n/locales/en/app.json @@ -1306,6 +1306,7 @@ "newChatModeAgent": "Agent", "newChatModeModel": "Model", "newChatTitle": "New Chat", + "newNotAllowedForTaskChat": "This chat is tied to a task — /new and /clear can't clear it", "noAgentsAvailable": "No agents available", "noConversationsYet": "No conversations yet", "noMessage": "No message", diff --git a/packages/i18n/locales/es/app.json b/packages/i18n/locales/es/app.json index 9c5b9cbea0..d9f2296e43 100644 --- a/packages/i18n/locales/es/app.json +++ b/packages/i18n/locales/es/app.json @@ -1387,7 +1387,8 @@ "noModelsAvailable": "No models available", "currentAgentTarget": "Current agent: {{name}}", "currentModelTarget": "Current model: {{model}}", - "currentDefaultTarget": "Using the default chat target" + "currentDefaultTarget": "Using the default chat target", + "newNotAllowedForTaskChat": "" }, "chatRooms": { "error": { diff --git a/packages/i18n/locales/fr/app.json b/packages/i18n/locales/fr/app.json index 03d3f1a05d..e6981cf4e4 100644 --- a/packages/i18n/locales/fr/app.json +++ b/packages/i18n/locales/fr/app.json @@ -1387,7 +1387,8 @@ "noModelsAvailable": "No models available", "currentAgentTarget": "Current agent: {{name}}", "currentModelTarget": "Current model: {{model}}", - "currentDefaultTarget": "Using the default chat target" + "currentDefaultTarget": "Using the default chat target", + "newNotAllowedForTaskChat": "" }, "chatRooms": { "error": { diff --git a/packages/i18n/locales/ko/app.json b/packages/i18n/locales/ko/app.json index 0bcbfe26da..ee3b2d5130 100644 --- a/packages/i18n/locales/ko/app.json +++ b/packages/i18n/locales/ko/app.json @@ -1387,7 +1387,8 @@ "noModelsAvailable": "No models available", "currentAgentTarget": "Current agent: {{name}}", "currentModelTarget": "Current model: {{model}}", - "currentDefaultTarget": "Using the default chat target" + "currentDefaultTarget": "Using the default chat target", + "newNotAllowedForTaskChat": "" }, "chatRooms": { "error": { diff --git a/packages/i18n/locales/zh-CN/app.json b/packages/i18n/locales/zh-CN/app.json index 3ee346d92e..6576dfc6a3 100644 --- a/packages/i18n/locales/zh-CN/app.json +++ b/packages/i18n/locales/zh-CN/app.json @@ -1387,7 +1387,8 @@ "noModelsAvailable": "No models available", "currentAgentTarget": "Current agent: {{name}}", "currentModelTarget": "Current model: {{model}}", - "currentDefaultTarget": "Using the default chat target" + "currentDefaultTarget": "Using the default chat target", + "newNotAllowedForTaskChat": "" }, "chatRooms": { "error": { diff --git a/packages/i18n/locales/zh-TW/app.json b/packages/i18n/locales/zh-TW/app.json index e496684a32..cd591582b9 100644 --- a/packages/i18n/locales/zh-TW/app.json +++ b/packages/i18n/locales/zh-TW/app.json @@ -1387,7 +1387,8 @@ "noModelsAvailable": "No models available", "currentAgentTarget": "Current agent: {{name}}", "currentModelTarget": "Current model: {{model}}", - "currentDefaultTarget": "Using the default chat target" + "currentDefaultTarget": "Using the default chat target", + "newNotAllowedForTaskChat": "" }, "chatRooms": { "error": { diff --git a/packages/i18n/src/resources.d.ts b/packages/i18n/src/resources.d.ts index ddb3790d45..dc1fbca8d5 100644 --- a/packages/i18n/src/resources.d.ts +++ b/packages/i18n/src/resources.d.ts @@ -359,6 +359,7 @@ export default interface Resources { "bulkAgentActions": "Bulk agent actions", "bulkConfirmMessage_one": "{{action}} {{count}} agent(s) in this project?", "bulkConfirmMessage_other": "{{action}} {{count}} agent(s) in this project?", + "bulkFailures": "failed {{count}}", "bulkNoEligible": "No eligible agents", "bulkResultWithFailures": "{{action}} {{count}} agent(s), {{failed}} failed", "bulkResult_one": "{{action}} {{successCount}} {{agentWord}}; skipped {{skippedCount}}", @@ -446,9 +447,19 @@ export default interface Resources { "details": "Details", "dialogAriaLabel": "Create new agent", "dialogTitle": "New Agent", + "disableAllHeartbeats": "Disable all heartbeats", + "disableAllHeartbeatsConfirm": "Disable heartbeats for {{count}} project agents?", + "disableHeartbeat": "Disable heartbeat", + "disableHeartbeatsCountHint": "Disable {{count}} enabled project heartbeats", + "disableHeartbeatsSummary": "Disabled {{count}} heartbeats; skipped {{skipped}}", "employeesTitle": "Employees", "emptySubtitle": "Create an agent to get started", "emptyTitle": "No agents found", + "enableAllHeartbeats": "Enable all heartbeats", + "enableAllHeartbeatsConfirm": "Enable heartbeats for {{count}} project agents?", + "enableHeartbeat": "Enable heartbeat", + "enableHeartbeatsCountHint": "Enable {{count}} disabled project heartbeats", + "enableHeartbeatsSummary": "Enabled {{count}} heartbeats; skipped {{skipped}}", "engineerBacklogAutoClaim": "Engineer Backlog Auto-Claim", "engineerBacklogAutoClaimHint": "Per-agent override of the project default. Allows this engineer-role agent to auto-claim unowned backlog tasks; explicit assignment and delegation are unchanged.", "errors": "Errors", @@ -511,11 +522,13 @@ export default interface Resources { }, "healthError": "Error", "heartbeat": "Heartbeat:", + "heartbeatActionForAgent": "{{action}} for {{name}}", "heartbeatAndHealth": "Heartbeat & Health", "heartbeatClampedToMin_one": "Heartbeat interval set to 5 minutes (minimum). {{count}} minute was below the 5-minute minimum.", "heartbeatClampedToMin_other": "Heartbeat interval set to 5 minutes (minimum). {{count}} minutes was below the 5-minute minimum.", "heartbeatCustom": "Custom heartbeat run", "heartbeatEnabled": "Heartbeat Enabled", + "heartbeatEnabledForAgent": "Heartbeat enabled for {{name}}", "heartbeatEnabledHint": "Allow this agent to run on a scheduled heartbeat.", "heartbeatEnterMinutes": "Please enter a heartbeat interval in minutes", "heartbeatFileEditMode": "Edit mode", @@ -557,6 +570,8 @@ export default interface Resources { "heartbeatStarted": "Heartbeat started", "heartbeatTimeoutHint": "Maximum time in seconds a heartbeat run may take before being killed.", "heartbeatTimeoutLabel": "Heartbeat Timeout (s)", + "heartbeatUpdateFailed": "Failed to update heartbeat: {{error}}", + "heartbeatUpdating": "Updating heartbeat…", "heartbeatUpgradeFailed": "Failed to upgrade heartbeat procedure", "iconLabel": "Icon", "iconPlaceholder": "e.g. 🤖", @@ -716,6 +731,8 @@ export default interface Resources { "noCompaniesMatch": "No companies match your search", "noEmployees": "No employees", "noEmployeesDesc": "This agent has no direct reports.", + "noHeartbeatsToDisable": "No agent heartbeats need disabling", + "noHeartbeatsToEnable": "No agent heartbeats need enabling", "noInboxMessages": "No messages in inbox", "noInstructionsPath": "No instructions file path set", "noLimit": "No limit", @@ -1136,6 +1153,7 @@ export default interface Resources { "backendError": { "failedFetch": "Failed to fetch projects" }, + "migrationInProgress": "Database migration in progress — the dashboard will reconnect when it completes.", "testMode": "Test mode — no real AI calls" }, "appName": "Fusion", @@ -1284,6 +1302,7 @@ export default interface Resources { "newChatModeAgent": "Agent", "newChatModeModel": "Model", "newChatTitle": "New Chat", + "newNotAllowedForTaskChat": "This chat is tied to a task — /new and /clear can't clear it", "noAgentsAvailable": "No agents available", "noConversationsYet": "No conversations yet", "noMessage": "No message", @@ -1306,6 +1325,7 @@ export default interface Resources { "questionTextPlaceholder": "Type your answer here…", "queuedMessage": "Queued: {{preview}}", "quickChatTitle": "Quick Chat", + "recent": "Recent", "relativeTimeDays_one": "{{count}}d ago", "relativeTimeDays_other": "{{count}}d ago", "relativeTimeHours_one": "{{count}}h ago", @@ -1682,6 +1702,30 @@ export default interface Resources { "toolCategoriesHint": "Autonomous work grouped by tool family", "uniqueModels": "Unique models" }, + "portability": { + "description": "Download or preview a secret-scrubbed organization bundle.", + "export": { + "action": "Export org bundle", + "error": "Export failed", + "success": "Export ready", + "working": "Exporting…" + }, + "import": { + "apply": "Apply import", + "confirmApply": "Import bundle", + "confirmMessage": "This applies the previewed configuration to this project.", + "confirmTitle": "Import organization bundle?", + "error": "Unable to preview or import bundle", + "importing": "Importing…", + "invalid": "Paste a valid org bundle JSON document", + "label": "Org bundle JSON", + "placeholder": "Paste a secret-scrubbed org bundle", + "preview": "Preview import", + "previewReady": "Preview ready. Confirm to apply this bundle.", + "previewing": "Previewing…" + }, + "title": "Org export / import" + }, "productivity": { "averageDuration": "Average", "backfillAppliedLabel": "Applied", @@ -1719,10 +1763,6 @@ export default interface Resources { "volumeHint": "volume, not outcome", "volumeTitle": "Volume (proxy)" }, - "reviewArtifacts": { - "empty": "No review artifacts are available yet.", - "title": "Review artifacts" - }, "range": { "custom": "Custom range", "dialogLabel": "Select date range", @@ -1730,6 +1770,10 @@ export default interface Resources { "invalidRange": "Start date must be on or before end date", "to": "To" }, + "reviewArtifacts": { + "empty": "No review artifacts are available yet.", + "title": "Review artifacts" + }, "signals": { "bySeverity": "By severity", "bySource": "By source", @@ -1769,8 +1813,8 @@ export default interface Resources { "nodes": "Nodes", "overview": "Overview", "productivity": "Productivity", - "reviewArtifacts": "Review artifacts", "reliability": "Reliability", + "reviewArtifacts": "Review artifacts", "signals": "Signals", "system": "System", "team": "Team", @@ -1991,6 +2035,7 @@ export default interface Resources { "connectRemoteButton": "Connect to Remote Fusion", "couldNotStart": "Couldn't start local Fusion", "loading": "Loading Fusion…", + "migrationInProgress": "Database migration in progress — this can take a few minutes.", "opening": "Opening…", "retry": "Retry", "runLocalButton": "Run Fusion Locally", @@ -4539,6 +4584,7 @@ export default interface Resources { "advancedSettings": "Advanced planning settings", "agentClarification": "Allow agent clarification questions", "aiThinking": "AI is thinking...", + "answeredQuestions": "Answered questions", "archiveSession": "Archive session", "backToSessions": "Back to sessions", "backToSummary": "Back to Summary", @@ -4557,11 +4603,6 @@ export default interface Resources { "breakingDown": "Breaking down...", "collapse": "Collapse", "continue": "Continue", - "nextQuestion": "Next question", - "answeredQuestions": "Answered questions", - "runningPlan": "Running plan", - "renameSession": "Rename session", - "editAnswer": "Edit answer for {{question}}", "createSingleTask": "Create Single Task", "createTasks": "Create Tasks", "creating": "Creating...", @@ -4576,6 +4617,7 @@ export default interface Resources { "description": "Description", "dismiss": "Dismiss", "dragToReorder": "Drag to reorder", + "editAnswer": "Edit answer for {{question}}", "examplePlan1": "Build a user authentication system with login and signup", "examplePlan2": "Add dark mode support to the dashboard", "examplePlan3": "Create an API endpoint for exporting tasks as CSV", @@ -4607,13 +4649,13 @@ export default interface Resources { "moveUp": "Move up", "newPlanningSession": "New planning session", "newSession": "New session", + "nextQuestion": "Next question", "noActiveQuestion": "No active question in session", "noPreviousSubtasks": "No previous subtasks available.", "noSavedSessions": "No saved sessions yet. Start one on the right to see it here.", "plain": "Plain", "planningComplete": "Planning Complete!", "planningCompleteSubheading": "Review and refine your task before creating it.", - "planningDepth": "Planning depth", "planningModel": "Planning Model", "planningModelBlurb": "Selects which model runs the planning interview and writes the final draft.", "planningSessions": "Planning sessions", @@ -4633,8 +4675,10 @@ export default interface Resources { "relativeTimeWeeks_one": "{{count}}w ago", "relativeTimeWeeks_other": "{{count}}w ago", "remove": "Remove", + "renameSession": "Rename session", "retryFailed": "Retry failed. Please try again.", "retrying": "Retrying...", + "runningPlan": "Running plan", "sessionFailed": "Session failed while contacting the AI.", "sessionFailed2": "Session failed", "showArchived": "Show archived", @@ -5143,6 +5187,19 @@ export default interface Resources { }, "window": "Window: {{start}} → {{end}}" }, + "report": { + "roadmapDuplicate": { + "title": "Already on the roadmap — add your data point?" + }, + "roadmapMatch": { + "message": "This report matches a feature that is already planned.", + "title": "Already on the roadmap" + }, + "targetDiscussion": "GitHub Discussion", + "targetInherit": "Use configured action target", + "targetIssue": "GitHub Issue", + "targetLabel": "Filing target" + }, "research": { "actionFailed": "Action failed", "addCredentialsHint": "Add provider credentials in Authentication settings.", @@ -5807,7 +5864,27 @@ export default interface Resources { "eGPnpmTest": "e.g. pnpm test", "testCommand": "Test Command" }, + "configVersions": { + "confirmMessage": "Restore this version? The rollback is recorded as a new version.", + "confirmRollback": "Roll back", + "confirmTitle": "Roll back configuration?", + "description": "Restore any recorded project configuration version.", + "empty": "No configuration versions yet.", + "loadError": "Unable to load configuration versions", + "loading": "Loading versions…", + "rollback": "Roll back", + "rollbackError": "Unable to roll back configuration", + "rollingBack": "Rolling back…", + "title": "Configuration versions" + }, "conflictModalTitle": "Resolve Settings Conflicts", + "database": { + "advanced": "Advanced database settings", + "embeddedConnectionCap": "Embedded PostgreSQL connection cap", + "embeddedConnectionCapError": "Enter a value between 32 and 2,000.", + "embeddedConnectionCapHelp": "Maximum server connections for Fusion's embedded PostgreSQL. Applies after restarting Fusion. Range: 32–2,000. Unset by default — Fusion picks 500, or 150 on Windows where each connection is a separate process and higher caps can crash backends. External PostgreSQL uses its provider's connection limit.", + "embeddedConnectionCapPlaceholder": "auto" + }, "experimental": { "experimentalFeatures": "Experimental Features", "experimentalFeaturesAreEarlyCapabilitiesThatAreNot": " Experimental features are early capabilities that are not yet fully stable. Enable them to test new functionality, but be aware they may change or be removed. Default: disabled for every feature flag below. ", @@ -5850,8 +5927,6 @@ export default interface Resources { "chatHistory": "Chat history", "chatRooms": "Chat Rooms", "completionDocumentationAutomation": "Completion Documentation Automation", - "reviewArtifacts": "Review Artifacts", - "reviewArtifactsHint": "Controls whether eligible future tasks generate review deliverables. User-facing limits generation to user-facing work; on enables it for all eligible tasks. Individual PROMPT.md headers can override this. Default: off.", "controlsHowFutureTaskSpecsHandleReleaseNote": " Controls how future task specs handle release-note artifacts at completion. Use changeset mode for repositories that follow ", "controlsWhetherNewlyCreatedTasksHaveGitHubIssue": " Controls whether newly created tasks have GitHub issue tracking enabled by default. Individual tasks can still override this from the task detail modal. ", "defaultRepoUsedWhenCreatingGitHubIssuesFor": "Default repo used when creating GitHub issues for tracked tasks. Falls back to the global default if blank.", @@ -5861,6 +5936,11 @@ export default interface Resources { "disabledBuiltInWorkflowsAreHiddenFromWorkflow": "Disabled built-in workflows are hidden from workflow pickers. Existing tasks that already use one continue to resolve.", "disabledFusionWorkflowsAreHiddenFromWorkflow": "Disabled Fusion workflows are hidden from workflow pickers. Existing tasks that already use one continue to resolve. Default: all built-in workflows enabled (unset).", "enableSummarizationInProjectModelsToConfigureThatModel": " Enable summarization in Project Models to configure that model.", + "ephemeralAgentTaskCreationPolicy": "Ephemeral agent follow-up tasks", + "ephemeralAgentTaskCreationPolicyAllow": "Allow", + "ephemeralAgentTaskCreationPolicyDeny": "Deny", + "ephemeralAgentTaskCreationPolicyHint": "No default — unset policy falls back to Allow. Upon validation sends a proposal to your mailbox for one-click approval; Deny rejects follow-up task creation.", + "ephemeralAgentTaskCreationPolicyUponValidation": "Upon validation", "fN": "FN", "followDashboardLanguage": "Follow dashboard language", "general": "General", @@ -5887,12 +5967,41 @@ export default interface Resources { "quickChatLauncherHint": "Choose whether Quick Chat opens from the draggable floating button, a footer button beside Terminal, or stays hidden. Default: off (hidden).", "recentVerbatimRoomMessages": "Recent verbatim room messages", "removeNavItem": "Remove {{item}}", + "reportDiscussionCategory": "Discussion category", + "reportDiscussionCategoryEmpty": "No Discussion categories are available for the configured repository.", + "reportDiscussionCategoryHelp": "Required when filing to GitHub Discussions. Categories are loaded from the configured repository. No default — unset until a category is selected.", + "reportDiscussionCategorySelect": "Select a Discussion category", + "reportDiscussionCategoryUnavailable": "Discussion categories are unavailable for the configured repository.", + "reportModeByActionHelp": "Optional per-action override of the project report mode for bug, feedback, idea, or help. No default — unset actions inherit reportMode.", + "reportModeHelp": "How in-app bug/feedback/idea/help reports are filed. Default: draft-review (operator reviews a draft before filing).", + "reportRoadmapDedup": "Check roadmap before filing reports", + "reportRoadmapDedupHelp": "When enabled, matching roadmap features are shown inline instead of filing another GitHub Issue or Discussion. Default: off.", + "reportRoadmapDedupeEnabled": "Deduplicate reports against public roadmap", + "reportRoadmapDedupeEnabledHelp": "Match open, labeled roadmap issues before filing. Default: on.", + "reportRoadmapLabel": "Public roadmap label", + "reportRoadmapLabelHelp": "Open GitHub Issues with this label are considered roadmap items. Default: roadmap.", + "reportRoadmapRepo": "Public roadmap repository (optional)", + "reportRoadmapRepoHelp": "GitHub owner/repository containing public roadmap issues. When unset, uses the tracking repository. No default — unset.", + "reportTarget": "Default report target", + "reportTargetByActionHelp": "Optional per-action override of the default report target for bug, feedback, idea, or help. No default — unset actions inherit reportTarget / action defaults.", + "reportTargetDiscussion": "GitHub Discussion", + "reportTargetHelp": "Optional Issue or Discussion target. When unset, Feedback and Help use Discussions while Bug and Idea use Issues. No default — unset.", + "reportTargetInherit": "Preserve action default", + "reportTargetIssue": "GitHub Issue", + "reportTargetOverride": { + "bug": "Bug target override", + "feedback": "Feedback target override", + "help": "Help target override", + "idea": "Idea target override" + }, "requireChangelogUpdateExistingChangelog": "Require changelog update (existing changelog)", "requireChangesetChangesetMd": "Require changeset (.changeset/*.md)", "restartFailed": "Restart could not be scheduled. Try restarting Fusion manually.", "restartNow": "Restart Fusion", "restartUnavailable": "Needs a supervising parent — restart Fusion manually without --no-supervise.", "restarting": "Restarting… Your connection will close shortly.", + "reviewArtifacts": "Review Artifacts", + "reviewArtifactsHint": "Controls whether eligible future tasks generate review deliverables. User-facing limits generation to user-facing work; on enables it for all eligible tasks. Individual PROMPT.md headers can override this. Default: off.", "roomCompactionFetchLimit": "Room compaction fetch limit", "roomSummaryMaxCharacters": "Room summary max characters", "searchTheTrackingRepoForLikelyDuplicatesBefore": " Search the tracking repo for likely duplicates before opening a new issue ", @@ -5930,12 +6039,16 @@ export default interface Resources { "autoReloadDashboardOnVersionChange": " Auto-reload dashboard on version change ", "cLIBinaryOnPATH": " CLI binary on PATH ", "cLIBySpawning": " CLI by spawning", + "channelBeta": "Beta — early builds from main", + "channelStable": "Stable (recommended)", "checkForThe": " Check for the ", "checkForUpdatesAutomatically": " Check for updates automatically ", "controlsHowOftenTheDashboardReFetchesThe": " Controls how often the dashboard re-fetches the npm registry. Use the version + refresh control in the header to trigger an immediate check at any time. Default: daily. ", "dailyRecommended": "Daily (recommended)", "disableThisIfYourLocalDevProcessIs": ". Disable this if your local dev process is the source of truth and you don't want any outdated globally-installed binary executed during the probe. Default: enabled. ", "dismissModalsByClickingOutsideHint": " When enabled, clicking or tapping a modal backdrop closes the modal. Default: disabled, to prevent accidental dismissal. ", + "enableProactiveTaskChat": " Enable proactive task-chat updates ", + "enableProactiveTaskChatHint": " When enabled, Task chat reports step progress, failures, reviews, and rollbacks in real time. Default: disabled. ", "frequency": "Frequency", "general": "General", "gitLabApiBaseUrlHint": "Blank derives /api/v4. Override only for self-managed GitLab API gateways that use a different absolute http:// or https:// URL. No default — unset.", @@ -5949,6 +6062,14 @@ export default interface Resources { "onStartupOncePerServerLaunch": "On startup — once per server launch", "ownerRepo": "owner/repo", "projectsInheritThisValueWhenTheyDoNot": "Projects inherit this value when they do not set a project default tracking repo. No default — unset.", + "releaseChannel": "Release channel", + "releaseChannelHelp": "Stable follows official releases. Beta follows pre-releases cut from main and also picks up each stable release once it overtakes the beta. Switching back to Stable never downgrades. Default: stable.", + "reportRoadmapDedupeEnabled": "Global public-roadmap deduplication", + "reportRoadmapDedupeEnabledHelp": "Fallback for projects that do not set a public-roadmap deduplication preference. No default — unset (projects default to enabled).", + "reportRoadmapLabel": "Global public roadmap label", + "reportRoadmapLabelHelp": "Fallback label for open public-roadmap issues when a project does not set one. No default — unset (projects default to roadmap).", + "reportRoadmapRepo": "Global public roadmap repository (optional)", + "reportRoadmapRepoHelp": "Fallback GitHub owner/repository for public-roadmap issues. When unset, uses the tracking repository. No default — unset.", "rowsAndDoesNotAffectAssistantTextOr": " rows and does not affect assistant text or tool rows. Default: disabled for both permanent and ephemeral agents. ", "saveAIThinkingForEphemeralTaskWorkerAgents": " Save AI thinking for ephemeral / task-worker agents ", "saveAIThinkingForPermanentAgents": " Save AI thinking for permanent agents ", @@ -6290,6 +6411,7 @@ export default interface Resources { "global": "Global setting", "project": "Project setting" }, + "configVersions": "Configuration Versions", "globalMcp": "MCP Servers · Global", "mcp": "MCP Servers · Project", "prompts": "Prompts", @@ -7833,7 +7955,12 @@ export default interface Resources { "createdBy": "Created by", "createdToUndo": "Created to undo", "createdVia": "Created via", + "creatingAgent": "Creating agent", + "importedFrom": "Imported from", + "nearDuplicateOf": "Flagged near-duplicate of", + "parentTask": "Parent task", "parentTaskOf": "of", + "title": "Provenance", "undoTask": "Undo task" }, "recoveryState": "Recovery state", @@ -7996,7 +8123,7 @@ export default interface Resources { }, "tokenTotalsAria": "Task token totals", "tokenUsage": "Token Usage", - "totalExecutionTime": "Total execution time", + "totalExecutionTime": "Total active time", "totalTokens": "Total", "updateFailed": "Failed to update {{id}}: {{error}}", "updateSuccess": "Updated {{id}}", @@ -8455,6 +8582,8 @@ export default interface Resources { "failedToStartTerminal": "Failed to start terminal: {{error}}", "increaseFontSize": "Increase terminal font size", "initializeError": "Terminal UI failed to initialize: {{error}}", + "manualStartError": "Failed to start terminal: {{message}}", + "manualStartHint": "The terminal is ready — start a session to begin.", "newSession": "New Session", "newTerminal": "New terminal", "preferenceCursorBlink": "Blink cursor", @@ -8471,6 +8600,7 @@ export default interface Resources { "rendererReopenNote": "Reopen the terminal to apply renderer changes.", "resetPreferences": "Reset to defaults", "shortcuts": "Shortcuts", + "startTerminal": "Start terminal", "startingTerminal": "Starting terminal...", "statusConnected": "Connected", "statusConnecting": "Connecting...",