From e8c37b076e98b878d126df28eba12614b836a046 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sat, 18 Jul 2026 10:55:50 -0700 Subject: [PATCH] FN-8277: add in-app agentic reporting Add guided in-app reporting that safely files or endorses GitHub issues and discussions. - Add report actions, modal UI, API routes, and configurable per-action filing modes. - Scrub report payloads, self-check Help requests, and deduplicate before filing or endorsing. - Extend GitHub discussion support and cover the report pipeline with tests and documentation. Files changed: .changeset/fn-8277-agentic-reports.md | 7 + docs/dashboard-guide.md | 8 + docs/settings-reference.md | 2 + .../core/src/__tests__/settings-parity.test.ts | 9 + packages/core/src/index.gate.ts | 2 +- packages/core/src/index.ts | 2 +- packages/core/src/settings-schema.ts | 2 + packages/core/src/types.ts | 10 + packages/dashboard/app/api.ts | 1 + packages/dashboard/app/api/report.ts | 11 + packages/dashboard/app/components/Header.tsx | 30 +++ .../dashboard/app/components/ReportActionMenu.css | 6 + .../dashboard/app/components/ReportActionMenu.tsx | 22 ++ packages/dashboard/app/components/ReportModal.css | 6 + packages/dashboard/app/components/ReportModal.tsx | 81 ++++++++ .../app/components/__tests__/Header.test.tsx | 7 +- .../components/__tests__/ReportActionMenu.test.tsx | 18 ++ .../app/components/__tests__/ReportModal.test.tsx | 94 +++++++++ .../__tests__/SettingsModal.general.test.tsx | 15 ++ .../settings/sections/GeneralSection.tsx | 31 ++- .../src/__tests__/report-pipeline.test.ts | 127 ++++++++++++ .../dashboard/src/__tests__/report-routes.test.ts | 51 +++++ .../dashboard/src/__tests__/report-scrub.test.ts | 47 +++++ packages/dashboard/src/github.ts | 151 +++++++++++++- packages/dashboard/src/index.ts | 3 + packages/dashboard/src/report-help-selfcheck.ts | 13 ++ packages/dashboard/src/report-pipeline.ts | 225 +++++++++++++++++++++ packages/dashboard/src/report-scrub.ts | 66 ++++++ packages/dashboard/src/routes.ts | 2 + .../dashboard/src/routes/register-report-routes.ts | 118 +++++++++++ 30 files changed, 1156 insertions(+), 11 deletions(-) Fusion-Task-Id: FN-8277 Fusion-Task-Lineage: d50dc1da-7534-4324-ab05-1f8a0ad77c6a Co-authored-by: Fusion (runfusion.ai) --- .changeset/fn-8277-agentic-reports.md | 7 + docs/dashboard-guide.md | 8 + docs/settings-reference.md | 2 + .../src/__tests__/settings-parity.test.ts | 9 + packages/core/src/index.gate.ts | 2 +- packages/core/src/index.ts | 2 +- packages/core/src/settings-schema.ts | 2 + packages/core/src/types.ts | 10 + packages/dashboard/app/api.ts | 1 + packages/dashboard/app/api/report.ts | 11 + packages/dashboard/app/components/Header.tsx | 30 +++ .../app/components/ReportActionMenu.css | 6 + .../app/components/ReportActionMenu.tsx | 22 ++ .../dashboard/app/components/ReportModal.css | 6 + .../dashboard/app/components/ReportModal.tsx | 81 +++++++ .../app/components/__tests__/Header.test.tsx | 7 +- .../__tests__/ReportActionMenu.test.tsx | 18 ++ .../components/__tests__/ReportModal.test.tsx | 94 ++++++++ .../__tests__/SettingsModal.general.test.tsx | 15 ++ .../settings/sections/GeneralSection.tsx | 31 ++- .../src/__tests__/report-pipeline.test.ts | 127 ++++++++++ .../src/__tests__/report-routes.test.ts | 51 ++++ .../src/__tests__/report-scrub.test.ts | 47 ++++ packages/dashboard/src/github.ts | 151 +++++++++++- packages/dashboard/src/index.ts | 3 + .../dashboard/src/report-help-selfcheck.ts | 13 + packages/dashboard/src/report-pipeline.ts | 225 ++++++++++++++++++ packages/dashboard/src/report-scrub.ts | 66 +++++ packages/dashboard/src/routes.ts | 2 + .../src/routes/register-report-routes.ts | 118 +++++++++ 30 files changed, 1156 insertions(+), 11 deletions(-) create mode 100644 .changeset/fn-8277-agentic-reports.md create mode 100644 packages/dashboard/app/api/report.ts create mode 100644 packages/dashboard/app/components/ReportActionMenu.css create mode 100644 packages/dashboard/app/components/ReportActionMenu.tsx create mode 100644 packages/dashboard/app/components/ReportModal.css create mode 100644 packages/dashboard/app/components/ReportModal.tsx create mode 100644 packages/dashboard/app/components/__tests__/ReportActionMenu.test.tsx create mode 100644 packages/dashboard/app/components/__tests__/ReportModal.test.tsx create mode 100644 packages/dashboard/src/__tests__/report-pipeline.test.ts create mode 100644 packages/dashboard/src/__tests__/report-routes.test.ts create mode 100644 packages/dashboard/src/__tests__/report-scrub.test.ts create mode 100644 packages/dashboard/src/report-help-selfcheck.ts create mode 100644 packages/dashboard/src/report-pipeline.ts create mode 100644 packages/dashboard/src/report-scrub.ts create mode 100644 packages/dashboard/src/routes/register-report-routes.ts diff --git a/.changeset/fn-8277-agentic-reports.md b/.changeset/fn-8277-agentic-reports.md new file mode 100644 index 0000000000..95d8630ab1 --- /dev/null +++ b/.changeset/fn-8277-agentic-reports.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": minor +--- + +summary: Add guided in-app Bug, Feedback, Idea, and Help reporting. +category: feature +dev: Reports are privacy-scrubbed, deduplicated against open GitHub issues, and can be reviewed or filed automatically. diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index 6569cec7d7..137f31a6fc 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -2078,3 +2078,11 @@ When enabled, a proactive question holds the planner at `awaiting_input`, sends ### Mobile footer quick actions In **Settings → General**, choose up to six Mobile footer quick actions from the add dropdown. The selected list is ordered: use its earlier/later controls or remove actions to adjust the footer, and see each change immediately while Settings remains open. Eligible sidebar and More-sheet destinations can be promoted; unselected available destinations stay reachable in More. Feature-gated views remain hidden until enabled, and the trailing More tab is always present. + +## In-app reports + +The Header **Report** menu is available on desktop and mobile and offers **Bug**, **Feedback**, **Idea**, and **Help**. Each action begins with a short, guided prompt rather than a raw GitHub issue form. + +Fusion gathers available task/agent context, structures the prompt into a report, scrubs secrets, local paths, project names, home-directory identities, email addresses, and likely personal names, then checks **open** GitHub issues or Discussions for duplicates. Scrubbing is mandatory for every route and is repeated on the server when a reviewed draft is edited before filing. A strong duplicate receives a visible 👍 reaction and one scrubbed data-point comment instead of a new issue or Discussion. Bug and Idea reports use issues; Feedback and unresolved Help reports use repository Discussions. If preparation or filing cannot reach GitHub, Fusion preserves the draft and shows a retryable error. + +In **Settings → General**, choose **Review draft before filing** (the default) or **File automatically**. Both paths show the resulting issue, Discussion, or endorsement link. Help checks Fusion's local knowledge index on every server report path first and only escalates when it cannot find an answer. diff --git a/docs/settings-reference.md b/docs/settings-reference.md index 6d17730875..91026d06c9 100644 --- a/docs/settings-reference.md +++ b/docs/settings-reference.md @@ -651,6 +651,8 @@ Default notes: | `githubTrackingEnabledByDefault` | `boolean` | `false` | Project-level default for enabling issue tracking on ordinary new tasks. When this is false, the Quick Entry GitHub toggle is disabled until tracking is enabled in Settings. Imported GitHub issues still follow this default unless `githubLinkImportedIssuesToTracking` is enabled. | | `sessionAdvisorEnabledByDefault` | `boolean` | `false` | Project-level default for the session advisor (LLM overseer agent that reviews live executor transcripts). Off by default (opt-in). Quick Add exposes an eye toggle next to GitHub that inherits this default; each task can override via `sessionAdvisorEnabled`. Provider and model ids still come from workflow settings (`plannerOverseerAdvisorProvider` / `plannerOverseerAdvisorModelId`). Dashboard location: **Settings → Project → General → Session advisor (overseer agent)**. | | `githubLinkImportedIssuesToTracking` | `boolean` | `false` | Project-scoped, import-only option. When enabled, GitHub issue imports from the dashboard, CLI, and extension tools persist `githubTracking: { enabled: true }` so Fusion adopts the imported source issue as the tracking issue without turning tracking on for ordinary new tasks. Duplicate/skipped imports do not create tasks or tracking metadata. | +| `reportMode` | `"draft-review" \| "auto-file"` | `"draft-review"` | Default in-app Bug, Feedback, Idea, and Help report behavior. `"draft-review"` presents the scrubbed, structured draft for approval; `"auto-file"` files a new report or endorses a strong open duplicate automatically. Dashboard location: **Settings → Project → General → In-app report mode**. | +| `reportModeByAction` | `Partial>` | `undefined` | Optional per-action override map. An action-specific value takes precedence over `reportMode`; unset actions use the project default. Every mode always scrubs report content before GitHub egress. | | `githubImportAutoTranslate` | `boolean` | `false` | Project-scoped, import-only option. When enabled, the Import Tasks panel automatically translates foreign-language **GitHub open issue** titles and bodies into `importTranslateTargetLocale` and shows the translation by default (the original text stays one toggle away). Translation follows each reachable page of the fetched list (up to the 300-issue fetch cap in one hour); cached repeat views do not spend model calls. Pull requests and GitLab retain per-selection translation. Translations persist across app restarts until the upstream issue changes or closes. Off by default so all-English projects never pay for a per-issue AI call. Dashboard location: **Settings → Project → General → GitHub Tracking**. | | `importTranslateTargetLocale` | `Locale` | `undefined` | Target language for `githubImportAutoTranslate`. One of `SUPPORTED_LOCALES`. When unset, import translation follows the dashboard's own `language` setting. Dashboard location: **Settings → Project → General → GitHub Tracking**. | | `githubTrackingDefaultRepo` | `string` | `undefined` | Project default issue-tracking repo (`owner/repo`) used before global fallback for tracked task creation (precedence: task override → project default → global default). In Settings UI this is a detected-remote dropdown with a Custom fallback for manual entry. This key is dual-scope: project saves go through `PUT /api/settings` (Settings → General → GitHub Tracking) while global saves go through `PUT /api/settings/global` (Settings → Global General). | diff --git a/packages/core/src/__tests__/settings-parity.test.ts b/packages/core/src/__tests__/settings-parity.test.ts index 793723674c..10c9b88620 100644 --- a/packages/core/src/__tests__/settings-parity.test.ts +++ b/packages/core/src/__tests__/settings-parity.test.ts @@ -163,6 +163,15 @@ describe("settings key parity", () => { expect(GLOBAL_SETTINGS_KEYS).not.toContain("allowAbsoluteFileBrowserPaths"); }); + it("defaults in-app reports to draft review and keeps mode settings project-scoped", () => { + expect(DEFAULT_PROJECT_SETTINGS.reportMode).toBe("draft-review"); + expect(DEFAULT_PROJECT_SETTINGS.reportModeByAction).toBeUndefined(); + expect(isProjectSettingsKey("reportMode")).toBe(true); + expect(isProjectSettingsKey("reportModeByAction")).toBe(true); + expect(isGlobalSettingsKey("reportMode")).toBe(false); + expect(isGlobalSettingsKey("reportModeByAction")).toBe(false); + }); + it("defaults autoClaimCandidatesInPrompt to 5 and keeps it project-scoped", () => { expect(DEFAULT_PROJECT_SETTINGS.autoClaimCandidatesInPrompt).toBe(5); expect(isProjectSettingsKey("autoClaimCandidatesInPrompt")).toBe(true); diff --git a/packages/core/src/index.gate.ts b/packages/core/src/index.gate.ts index 68c26e456b..21a41c3c6a 100644 --- a/packages/core/src/index.gate.ts +++ b/packages/core/src/index.gate.ts @@ -43,7 +43,7 @@ etc. pulled in by production modules, not test files). */ export { COLUMNS, DEFAULT_COLUMN, isColumn, normalizeColumn, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS, DEFAULT_SETTINGS, DEFAULT_GLOBAL_SETTINGS, DEFAULT_PROJECT_SETTINGS, GLOBAL_SETTINGS_KEYS, PROJECT_SETTINGS_KEYS, isGlobalSettingsKey, isProjectSettingsKey, isMergeRequestContractShadowEnabled, resolvePersistAgentThinkingLog, THINKING_LEVELS, THEME_MODES, COLOR_THEMES, SUPPORTED_LOCALES, DEFAULT_LOCALE, isLocale, AGENT_PERMISSIONS, PERMANENT_AGENT_ACTION_CATEGORIES, AGENT_PERMISSION_POLICY_ACTION_CATEGORIES, AGENT_PROVISIONING_APPROVAL_MODES, SANDBOX_PROVISIONING_APPROVAL_MODES, AGENT_PERMISSION_POLICY_PRESET_IDS, LEGACY_AGENT_PERMISSION_POLICY_ACTION_CATEGORY_ALIASES, APPROVAL_REQUEST_STATUSES, APPROVAL_REQUEST_AUDIT_EVENT_TYPES, normalizeApprovalRequestActionCategory, isValidApprovalRequestTransition, agentToConfigSnapshot, diffConfigSnapshots, isEphemeralAgent, hasAgentIdentity, CheckoutConflictError, DEFAULT_HEARTBEAT_PROCEDURE_PATH, getDefaultHeartbeatProcedurePath, EXECUTION_MODES, DEFAULT_EXECUTION_MODE, PLANNER_OVERSIGHT_LEVELS, DEFAULT_PLANNER_OVERSIGHT_LEVEL, TASK_PRIORITIES, DEFAULT_TASK_PRIORITY, WORKFLOW_WORK_ITEM_KINDS, WORKFLOW_WORK_ITEM_STATES, HIGH_FANOUT_BLOCKER_TODO_THRESHOLD, STALE_HIGH_FANOUT_BLOCKER_AGE_THRESHOLD_MS, DASHBOARD_USER_ID, normalizeMessageParticipant, validateMessageMetadata, resolveEphemeralTaskCreationPolicy, validateDockerNodeConfig, sanitizeDockerNodeConfigForResponse, normalizeMergeIntegrationWorktreeMode, normalizeMergeAdvanceAutoSyncMode, DEFAULT_GITLAB_API_BASE_URL, DEFAULT_GITLAB_INSTANCE_URL, resolveGitlabConfig, resolveGitlabEnabled, PLANNING_DEEPEN_CHECKPOINT_ID, PLANNING_DEEPEN_CHECKPOINT_QUESTION, PLANNING_DEEPEN_PROCEED_OPTION_ID, PLANNING_DEEPEN_PROCEED_RESPONSE_KEY, MERGE_ADVANCE_AUTO_SYNC_MODES, normalizeMergeConflictStrategy, normalizeMergeStrategyOverlapBehavior, normalizePostMergeAuditMode, POST_MERGE_AUDIT_MODES, normalizeMergeAuditAutoRecovery, MERGE_AUDIT_AUTO_RECOVERY_MODES, normalizeMergerMode, MERGER_MODES, normalizeAutoRecovery, AUTO_RECOVERY_MODES, buildResearchDocumentKey, REPO_OVERRIDE_RE, SHARED_STATE_SNAPSHOT_VERSION, sanitizeCliAgentSettings, sanitizeCliAgentsSettings, sanitizeMcpServers, CLI_AGENT_ADAPTER_IDS, CLI_AGENT_AUTONOMY_MODES, isMcpSecretRef, OVERSEER_INTERVENTION_MUTATION } from "./types.js"; -export type { Column, ColumnId, IssueInfo, IssueState, TaskSourceIssue, TaskGitLabTracking, TaskGitLabTrackedItem, GitLabTrackedItemKind, PrInfo, PrConflictState, PrConflictDiagnostics, PrCheckState, PrCheckStatus, PrStatus, BranchGroup, BranchGroupCreateInput, BranchGroupUpdate, BranchGroupPrState, Task, TaskTokenUsage, TaskTokenUsagePerModel, TaskAttachment, TaskComment, TaskCommentInput, TaskDocument, TaskDocumentRevision, TaskDocumentCreateInput, TaskDocumentWithTask, ArtifactType, Artifact, ArtifactCreateInput, ArtifactWithTask, TaskCreateInput, TaskSource, SourceType, TaskDetail, RetrySummary, InboxTask, TodoList, TodoItem, TodoListCreateInput, TodoListUpdateInput, TodoItemCreateInput, TodoItemUpdateInput, TodoListWithItems, AgentLogEntry, AgentLogType, AgentRole, BoardConfig, DistributedTaskIdReserveInput, DistributedTaskIdReserveResult, DistributedTaskIdCommitInput, DistributedTaskIdCommitResult, DistributedTaskIdAbortInput, DistributedTaskIdAbortResult, DistributedTaskIdStateInput, DistributedTaskIdStateResult, AutostashOrphanRecord, AutostashOutcome, MergeDetails, MergeResult, MergeIntegrationWorktreeMode, MergeAdvanceAutoSyncMode, MergeConflictStrategy, CanonicalMergeConflictStrategy, MergeStrategyOverlapBehavior, PostMergeAuditMode, MergeAuditAutoRecoveryMode, MergerMode, MergerSettings, AutoRecoveryMode, AutoRecoveryFailureClass, AutoRecoverySettings, DirectMergeCommitStrategy, Settings, GlobalSettings, ProjectSettings, SecretsEnvConfig, WebSearchBackend, ResearchEnabledSources, ResearchGlobalDefaults, ResearchProjectLimits, ResearchProjectSettings, SandboxBackendName, SandboxFailureMode, SandboxPolicy, SandboxProjectSettings, EvalFollowUpPolicy, EvalProjectSettings, ResolvedEvalSettings, SettingsScope, DaemonTokenSettings, TaskStep, StepStatus, TaskLogEntry, RunMutationContext, ActivityLogEntry, ActivityEventType, ThinkingLevel, ThemeMode, ColorTheme, Locale, ExecutionMode, PlannerOversightLevel, TaskPriority, MergeQueueEntry, MergeQueueEnqueueOptions, MergeQueueAcquireOptions, MergeQueueReleaseOutcome, MergeRequestState, MergeRequestRecord, MergeRequestWorkflowProjectionOptions, CompletionHandoffMarker, WorkflowWorkItem, WorkflowWorkItemDueFilter, WorkflowWorkItemKind, WorkflowWorkItemState, WorkflowWorkItemTransitionPatch, WorkflowWorkItemUpsertInput, HandoffEvidence, HandoffToReviewOptions, UnavailableNodePolicy, OwningNodeHandoffPolicy, PlanningQuestion, PlanningSummary, PlanningResponse, PlanningQuestionType, ArchivedTaskEntry, BatchStatusRequest, BatchStatusResponse, BatchStatusEntry, BatchStatusResult, GithubIssueAction, ModelPreset, WorkflowStep, WorkflowStepMode, WorkflowStepGateMode, WorkflowStepPhase, WorkflowStepInput, WorkflowStepResult, WorkflowStepTemplate, Agent, OrgTreeNode, AgentState, AgentDetail, AgentCreateInput, AgentUpdateInput, AgentApiKey, AgentApiKeyCreateResult, AgentCapability, AgentPromptTemplate, AgentPromptsConfig, AgentPermission, PermanentAgentActionCategory, PermanentAgentSensitiveActionCategory, PermanentAgentGatingContext, AgentPermissionPolicy, AgentPermissionPolicyRules, AgentPermissionPolicyToolRules, AgentPermissionPolicyActionCategory, AgentProvisioningApprovalMode, SandboxProvisioningApprovalMode, LegacyAgentPermissionPolicyActionCategory, ApprovalRequestActionCategoryInput, ApprovalRequestActionCategory, AgentPermissionPolicyDisposition, AgentPermissionPolicyPresetId, ApprovalRequestStatus, ApprovalRequestAuditEventType, ApprovalRequestActorSnapshot, ApprovalRequestTargetAction, ApprovalRequestAuditEvent, ApprovalRequest, ApprovalRequestCreateInput, ApprovalRequestDecisionInput, ApprovalRequestCompletionInput, ApprovalRequestListInput, TaskAssignSource, AgentAccessState, AgentHeartbeatConfig, AgentBudgetConfig, AgentBudgetStatus, InstructionsBundleConfig, MessageResponseMode, AgentHeartbeatEvent, AgentHeartbeatRun, BlockedStateSnapshot, HeartbeatInvocationSource, AgentTaskSession, AgentRating, AgentRatingSummary, AgentRatingInput, AgentConfigSnapshot, RevisionFieldDiff, AgentConfigRevision, AgentStats, ReflectionTrigger, ReflectionMetrics, AgentReflection, AgentPerformanceSummary, NtfyNotificationEvent, NotificationEvent, NotificationPayload, NotificationProviderConfig, CustomProvider, SteeringComment, ParticipantType, MessageType, Message, MessageCreateInput, MessageFilter, MessageMetadata, ProposedTaskMetadata, EphemeralTaskCreationPolicy, MessageReplyReference, Mailbox, CheckoutLease, CheckoutClaimPrecondition, TaskClaimRow, CentralClaimStore, RunAuditDomain, RunAuditEvent, RunAuditEventInput, RunAuditEventFilter, AgentMemoryInclusionMode, HeartbeatPromptTemplate, HeartbeatScopeDisciplineMode, WorktrunkSettings, WorktrunkOnFailure, TaskBranchContext, CliAgentSettings, McpSecretRef, McpSensitiveValue, McpStdioTransport, McpSseTransport, McpStreamableHttpTransport, McpTransport, McpServerDefinition, McpServersSettings, GitlabConfigSettingsSource, ResolvedGitlabConfig, ResolveGitlabConfigInput, GitlabAuthTokenType, PlannerOversightStage, PlannerInterventionAction, PlannerInterventionOutcome, PlannerInterventionSourceLink, PlannerInterventionEntry, BackupSettingsMigrationCandidate, BackupSettingsMigrationConflict } from "./types.js"; +export type { Column, ColumnId, IssueInfo, IssueState, TaskSourceIssue, TaskGitLabTracking, TaskGitLabTrackedItem, GitLabTrackedItemKind, PrInfo, PrConflictState, PrConflictDiagnostics, PrCheckState, PrCheckStatus, PrStatus, BranchGroup, BranchGroupCreateInput, BranchGroupUpdate, BranchGroupPrState, Task, TaskTokenUsage, TaskTokenUsagePerModel, TaskAttachment, TaskComment, TaskCommentInput, TaskDocument, TaskDocumentRevision, TaskDocumentCreateInput, TaskDocumentWithTask, ArtifactType, Artifact, ArtifactCreateInput, ArtifactWithTask, TaskCreateInput, TaskSource, SourceType, TaskDetail, RetrySummary, InboxTask, TodoList, TodoItem, TodoListCreateInput, TodoListUpdateInput, TodoItemCreateInput, TodoItemUpdateInput, TodoListWithItems, AgentLogEntry, AgentLogType, AgentRole, BoardConfig, DistributedTaskIdReserveInput, DistributedTaskIdReserveResult, DistributedTaskIdCommitInput, DistributedTaskIdCommitResult, DistributedTaskIdAbortInput, DistributedTaskIdAbortResult, DistributedTaskIdStateInput, DistributedTaskIdStateResult, AutostashOrphanRecord, AutostashOutcome, MergeDetails, MergeResult, MergeIntegrationWorktreeMode, MergeAdvanceAutoSyncMode, MergeConflictStrategy, CanonicalMergeConflictStrategy, MergeStrategyOverlapBehavior, PostMergeAuditMode, MergeAuditAutoRecoveryMode, MergerMode, MergerSettings, AutoRecoveryMode, AutoRecoveryFailureClass, AutoRecoverySettings, DirectMergeCommitStrategy, Settings, GlobalSettings, ProjectSettings, ReportMode, ReportActionType, SecretsEnvConfig, WebSearchBackend, ResearchEnabledSources, ResearchGlobalDefaults, ResearchProjectLimits, ResearchProjectSettings, SandboxBackendName, SandboxFailureMode, SandboxPolicy, SandboxProjectSettings, EvalFollowUpPolicy, EvalProjectSettings, ResolvedEvalSettings, SettingsScope, DaemonTokenSettings, TaskStep, StepStatus, TaskLogEntry, RunMutationContext, ActivityLogEntry, ActivityEventType, ThinkingLevel, ThemeMode, ColorTheme, Locale, ExecutionMode, PlannerOversightLevel, TaskPriority, MergeQueueEntry, MergeQueueEnqueueOptions, MergeQueueAcquireOptions, MergeQueueReleaseOutcome, MergeRequestState, MergeRequestRecord, MergeRequestWorkflowProjectionOptions, CompletionHandoffMarker, WorkflowWorkItem, WorkflowWorkItemDueFilter, WorkflowWorkItemKind, WorkflowWorkItemState, WorkflowWorkItemTransitionPatch, WorkflowWorkItemUpsertInput, HandoffEvidence, HandoffToReviewOptions, UnavailableNodePolicy, OwningNodeHandoffPolicy, PlanningQuestion, PlanningSummary, PlanningResponse, PlanningQuestionType, ArchivedTaskEntry, BatchStatusRequest, BatchStatusResponse, BatchStatusEntry, BatchStatusResult, GithubIssueAction, ModelPreset, WorkflowStep, WorkflowStepMode, WorkflowStepGateMode, WorkflowStepPhase, WorkflowStepInput, WorkflowStepResult, WorkflowStepTemplate, Agent, OrgTreeNode, AgentState, AgentDetail, AgentCreateInput, AgentUpdateInput, AgentApiKey, AgentApiKeyCreateResult, AgentCapability, AgentPromptTemplate, AgentPromptsConfig, AgentPermission, PermanentAgentActionCategory, PermanentAgentSensitiveActionCategory, PermanentAgentGatingContext, AgentPermissionPolicy, AgentPermissionPolicyRules, AgentPermissionPolicyToolRules, AgentPermissionPolicyActionCategory, AgentProvisioningApprovalMode, SandboxProvisioningApprovalMode, LegacyAgentPermissionPolicyActionCategory, ApprovalRequestActionCategoryInput, ApprovalRequestActionCategory, AgentPermissionPolicyDisposition, AgentPermissionPolicyPresetId, ApprovalRequestStatus, ApprovalRequestAuditEventType, ApprovalRequestActorSnapshot, ApprovalRequestTargetAction, ApprovalRequestAuditEvent, ApprovalRequest, ApprovalRequestCreateInput, ApprovalRequestDecisionInput, ApprovalRequestCompletionInput, ApprovalRequestListInput, TaskAssignSource, AgentAccessState, AgentHeartbeatConfig, AgentBudgetConfig, AgentBudgetStatus, InstructionsBundleConfig, MessageResponseMode, AgentHeartbeatEvent, AgentHeartbeatRun, BlockedStateSnapshot, HeartbeatInvocationSource, AgentTaskSession, AgentRating, AgentRatingSummary, AgentRatingInput, AgentConfigSnapshot, RevisionFieldDiff, AgentConfigRevision, AgentStats, ReflectionTrigger, ReflectionMetrics, AgentReflection, AgentPerformanceSummary, NtfyNotificationEvent, NotificationEvent, NotificationPayload, NotificationProviderConfig, CustomProvider, SteeringComment, ParticipantType, MessageType, Message, MessageCreateInput, MessageFilter, MessageMetadata, ProposedTaskMetadata, EphemeralTaskCreationPolicy, MessageReplyReference, Mailbox, CheckoutLease, CheckoutClaimPrecondition, TaskClaimRow, CentralClaimStore, RunAuditDomain, RunAuditEvent, RunAuditEventInput, RunAuditEventFilter, AgentMemoryInclusionMode, HeartbeatPromptTemplate, HeartbeatScopeDisciplineMode, WorktrunkSettings, WorktrunkOnFailure, TaskBranchContext, CliAgentSettings, McpSecretRef, McpSensitiveValue, McpStdioTransport, McpSseTransport, McpStreamableHttpTransport, McpTransport, McpServerDefinition, McpServersSettings, GitlabConfigSettingsSource, ResolvedGitlabConfig, ResolveGitlabConfigInput, GitlabAuthTokenType, PlannerOversightStage, PlannerInterventionAction, PlannerInterventionOutcome, PlannerInterventionSourceLink, PlannerInterventionEntry, BackupSettingsMigrationCandidate, BackupSettingsMigrationConflict } from "./types.js"; export { AGENT_VALID_TRANSITIONS, DUPLICATE_OF_METADATA_KEY, assertNotWorkspaceTaskMerge, isWorkspaceTask, WorkspaceTaskMergeError } from "./types.js"; export { resolveEntryPointBranchAssignment, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 46959a8fbf..02942e7ec9 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,5 +1,5 @@ export { COLUMNS, DEFAULT_COLUMN, isColumn, normalizeColumn, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS, DEFAULT_SETTINGS, DEFAULT_GLOBAL_SETTINGS, DEFAULT_PROJECT_SETTINGS, GLOBAL_SETTINGS_KEYS, PROJECT_SETTINGS_KEYS, isGlobalSettingsKey, isProjectSettingsKey, isMergeRequestContractShadowEnabled, resolvePersistAgentThinkingLog, THINKING_LEVELS, THEME_MODES, COLOR_THEMES, SUPPORTED_LOCALES, DEFAULT_LOCALE, isLocale, AGENT_PERMISSIONS, PERMANENT_AGENT_ACTION_CATEGORIES, AGENT_PERMISSION_POLICY_ACTION_CATEGORIES, AGENT_PROVISIONING_APPROVAL_MODES, SANDBOX_PROVISIONING_APPROVAL_MODES, AGENT_PERMISSION_POLICY_PRESET_IDS, LEGACY_AGENT_PERMISSION_POLICY_ACTION_CATEGORY_ALIASES, APPROVAL_REQUEST_STATUSES, APPROVAL_REQUEST_AUDIT_EVENT_TYPES, normalizeApprovalRequestActionCategory, isValidApprovalRequestTransition, agentToConfigSnapshot, diffConfigSnapshots, isEphemeralAgent, hasAgentIdentity, CheckoutConflictError, DEFAULT_HEARTBEAT_PROCEDURE_PATH, getDefaultHeartbeatProcedurePath, EXECUTION_MODES, DEFAULT_EXECUTION_MODE, PLANNER_OVERSIGHT_LEVELS, DEFAULT_PLANNER_OVERSIGHT_LEVEL, TASK_PRIORITIES, DEFAULT_TASK_PRIORITY, WORKFLOW_WORK_ITEM_KINDS, WORKFLOW_WORK_ITEM_STATES, HIGH_FANOUT_BLOCKER_TODO_THRESHOLD, STALE_HIGH_FANOUT_BLOCKER_AGE_THRESHOLD_MS, DASHBOARD_USER_ID, normalizeMessageParticipant, validateMessageMetadata, resolveEphemeralTaskCreationPolicy, validateDockerNodeConfig, sanitizeDockerNodeConfigForResponse, normalizeMergeIntegrationWorktreeMode, normalizeMergeAdvanceAutoSyncMode, DEFAULT_GITLAB_API_BASE_URL, DEFAULT_GITLAB_INSTANCE_URL, resolveGitlabConfig, resolveGitlabEnabled, PLANNING_DEEPEN_CHECKPOINT_ID, PLANNING_DEEPEN_CHECKPOINT_QUESTION, PLANNING_DEEPEN_PROCEED_OPTION_ID, PLANNING_DEEPEN_PROCEED_RESPONSE_KEY, MERGE_ADVANCE_AUTO_SYNC_MODES, normalizeMergeConflictStrategy, normalizeMergeStrategyOverlapBehavior, normalizePostMergeAuditMode, POST_MERGE_AUDIT_MODES, normalizeMergeAuditAutoRecovery, MERGE_AUDIT_AUTO_RECOVERY_MODES, normalizeMergerMode, MERGER_MODES, normalizeAutoRecovery, AUTO_RECOVERY_MODES, buildResearchDocumentKey, REPO_OVERRIDE_RE, SHARED_STATE_SNAPSHOT_VERSION, sanitizeCliAgentSettings, sanitizeCliAgentsSettings, sanitizeMcpServers, CLI_AGENT_ADAPTER_IDS, CLI_AGENT_AUTONOMY_MODES, isMcpSecretRef, OVERSEER_INTERVENTION_MUTATION } from "./types.js"; -export type { Column, ColumnId, IssueInfo, IssueState, TaskSourceIssue, TaskGitLabTracking, TaskGitLabTrackedItem, GitLabTrackedItemKind, PrInfo, PrConflictState, PrConflictDiagnostics, PrCheckState, PrCheckStatus, PrStatus, BranchGroup, BranchGroupCreateInput, BranchGroupUpdate, BranchGroupPrState, Task, TaskTokenUsage, TaskTokenUsagePerModel, TaskAttachment, TaskComment, TaskCommentInput, TaskDocument, TaskDocumentRevision, TaskDocumentCreateInput, TaskDocumentWithTask, ArtifactType, Artifact, ArtifactCreateInput, ArtifactWithTask, TaskCreateInput, TaskSource, SourceType, TaskDetail, RetrySummary, InboxTask, TodoList, TodoItem, TodoListCreateInput, TodoListUpdateInput, TodoItemCreateInput, TodoItemUpdateInput, TodoListWithItems, AgentLogEntry, AgentLogType, AgentRole, BoardConfig, DistributedTaskIdReserveInput, DistributedTaskIdReserveResult, DistributedTaskIdCommitInput, DistributedTaskIdCommitResult, DistributedTaskIdAbortInput, DistributedTaskIdAbortResult, DistributedTaskIdStateInput, DistributedTaskIdStateResult, AutostashOrphanRecord, AutostashOutcome, MergeDetails, MergeResult, MergeIntegrationWorktreeMode, MergeAdvanceAutoSyncMode, MergeConflictStrategy, CanonicalMergeConflictStrategy, MergeStrategyOverlapBehavior, PostMergeAuditMode, MergeAuditAutoRecoveryMode, MergerMode, MergerSettings, AutoRecoveryMode, AutoRecoveryFailureClass, AutoRecoverySettings, DirectMergeCommitStrategy, Settings, GlobalSettings, ProjectSettings, SecretsEnvConfig, WebSearchBackend, ResearchEnabledSources, ResearchGlobalDefaults, ResearchProjectLimits, ResearchProjectSettings, SandboxBackendName, SandboxFailureMode, SandboxPolicy, SandboxProjectSettings, EvalFollowUpPolicy, EvalProjectSettings, ResolvedEvalSettings, SettingsScope, DaemonTokenSettings, TaskStep, StepStatus, TaskLogEntry, RunMutationContext, ActivityLogEntry, ActivityEventType, ThinkingLevel, ThemeMode, ColorTheme, Locale, ExecutionMode, PlannerOversightLevel, TaskPriority, MergeQueueEntry, MergeQueueEnqueueOptions, MergeQueueAcquireOptions, MergeQueueReleaseOutcome, MergeRequestState, MergeRequestRecord, MergeRequestWorkflowProjectionOptions, CompletionHandoffMarker, WorkflowWorkItem, WorkflowWorkItemDueFilter, WorkflowWorkItemKind, WorkflowWorkItemState, WorkflowWorkItemTransitionPatch, WorkflowWorkItemUpsertInput, HandoffEvidence, HandoffToReviewOptions, UnavailableNodePolicy, OwningNodeHandoffPolicy, PlanningQuestion, PlanningSummary, PlanningResponse, PlanningQuestionType, ArchivedTaskEntry, BatchStatusRequest, BatchStatusResponse, BatchStatusEntry, BatchStatusResult, GithubIssueAction, ModelPreset, WorkflowStep, WorkflowStepMode, WorkflowStepGateMode, WorkflowStepPhase, WorkflowStepInput, WorkflowStepResult, WorkflowStepTemplate, Agent, OrgTreeNode, AgentState, AgentDetail, AgentCreateInput, AgentUpdateInput, AgentApiKey, AgentApiKeyCreateResult, AgentCapability, AgentPromptTemplate, AgentPromptsConfig, AgentPermission, PermanentAgentActionCategory, PermanentAgentSensitiveActionCategory, PermanentAgentGatingContext, AgentPermissionPolicy, AgentPermissionPolicyRules, AgentPermissionPolicyToolRules, AgentPermissionPolicyActionCategory, AgentProvisioningApprovalMode, SandboxProvisioningApprovalMode, LegacyAgentPermissionPolicyActionCategory, ApprovalRequestActionCategoryInput, ApprovalRequestActionCategory, AgentPermissionPolicyDisposition, AgentPermissionPolicyPresetId, ApprovalRequestStatus, ApprovalRequestAuditEventType, ApprovalRequestActorSnapshot, ApprovalRequestTargetAction, ApprovalRequestAuditEvent, ApprovalRequest, ApprovalRequestCreateInput, ApprovalRequestDecisionInput, ApprovalRequestCompletionInput, ApprovalRequestListInput, TaskAssignSource, AgentAccessState, AgentHeartbeatConfig, AgentBudgetConfig, AgentBudgetStatus, InstructionsBundleConfig, MessageResponseMode, AgentHeartbeatEvent, AgentHeartbeatRun, BlockedStateSnapshot, HeartbeatInvocationSource, AgentTaskSession, AgentRating, AgentRatingSummary, AgentRatingInput, AgentConfigSnapshot, RevisionFieldDiff, AgentConfigRevision, AgentStats, ReflectionTrigger, ReflectionMetrics, AgentReflection, AgentPerformanceSummary, NtfyNotificationEvent, NotificationEvent, NotificationPayload, NotificationProviderConfig, CustomProvider, SteeringComment, ParticipantType, MessageType, Message, MessageCreateInput, MessageFilter, MessageMetadata, ProposedTaskMetadata, EphemeralTaskCreationPolicy, MessageReplyReference, Mailbox, CheckoutLease, CheckoutClaimPrecondition, TaskClaimRow, CentralClaimStore, RunAuditDomain, RunAuditEvent, RunAuditEventInput, RunAuditEventFilter, AgentMemoryInclusionMode, HeartbeatPromptTemplate, HeartbeatScopeDisciplineMode, WorktrunkSettings, WorktrunkOnFailure, TaskBranchContext, CliAgentSettings, McpSecretRef, McpSensitiveValue, McpStdioTransport, McpSseTransport, McpStreamableHttpTransport, McpTransport, McpServerDefinition, McpServersSettings, GitlabConfigSettingsSource, ResolvedGitlabConfig, ResolveGitlabConfigInput, GitlabAuthTokenType, PlannerOversightStage, PlannerInterventionAction, PlannerInterventionOutcome, PlannerInterventionSourceLink, PlannerInterventionEntry, ExecutorOverseerSignalMemory, BackupSettingsMigrationCandidate, BackupSettingsMigrationConflict } from "./types.js"; +export type { Column, ColumnId, IssueInfo, IssueState, TaskSourceIssue, TaskGitLabTracking, TaskGitLabTrackedItem, GitLabTrackedItemKind, PrInfo, PrConflictState, PrConflictDiagnostics, PrCheckState, PrCheckStatus, PrStatus, BranchGroup, BranchGroupCreateInput, BranchGroupUpdate, BranchGroupPrState, Task, TaskTokenUsage, TaskTokenUsagePerModel, TaskAttachment, TaskComment, TaskCommentInput, TaskDocument, TaskDocumentRevision, TaskDocumentCreateInput, TaskDocumentWithTask, ArtifactType, Artifact, ArtifactCreateInput, ArtifactWithTask, TaskCreateInput, TaskSource, SourceType, TaskDetail, RetrySummary, InboxTask, TodoList, TodoItem, TodoListCreateInput, TodoListUpdateInput, TodoItemCreateInput, TodoItemUpdateInput, TodoListWithItems, AgentLogEntry, AgentLogType, AgentRole, BoardConfig, DistributedTaskIdReserveInput, DistributedTaskIdReserveResult, DistributedTaskIdCommitInput, DistributedTaskIdCommitResult, DistributedTaskIdAbortInput, DistributedTaskIdAbortResult, DistributedTaskIdStateInput, DistributedTaskIdStateResult, AutostashOrphanRecord, AutostashOutcome, MergeDetails, MergeResult, MergeIntegrationWorktreeMode, MergeAdvanceAutoSyncMode, MergeConflictStrategy, CanonicalMergeConflictStrategy, MergeStrategyOverlapBehavior, PostMergeAuditMode, MergeAuditAutoRecoveryMode, MergerMode, MergerSettings, AutoRecoveryMode, AutoRecoveryFailureClass, AutoRecoverySettings, DirectMergeCommitStrategy, Settings, GlobalSettings, ProjectSettings, ReportMode, ReportActionType, SecretsEnvConfig, WebSearchBackend, ResearchEnabledSources, ResearchGlobalDefaults, ResearchProjectLimits, ResearchProjectSettings, SandboxBackendName, SandboxFailureMode, SandboxPolicy, SandboxProjectSettings, EvalFollowUpPolicy, EvalProjectSettings, ResolvedEvalSettings, SettingsScope, DaemonTokenSettings, TaskStep, StepStatus, TaskLogEntry, RunMutationContext, ActivityLogEntry, ActivityEventType, ThinkingLevel, ThemeMode, ColorTheme, Locale, ExecutionMode, PlannerOversightLevel, TaskPriority, MergeQueueEntry, MergeQueueEnqueueOptions, MergeQueueAcquireOptions, MergeQueueReleaseOutcome, MergeRequestState, MergeRequestRecord, MergeRequestWorkflowProjectionOptions, CompletionHandoffMarker, WorkflowWorkItem, WorkflowWorkItemDueFilter, WorkflowWorkItemKind, WorkflowWorkItemState, WorkflowWorkItemTransitionPatch, WorkflowWorkItemUpsertInput, HandoffEvidence, HandoffToReviewOptions, UnavailableNodePolicy, OwningNodeHandoffPolicy, PlanningQuestion, PlanningSummary, PlanningResponse, PlanningQuestionType, ArchivedTaskEntry, BatchStatusRequest, BatchStatusResponse, BatchStatusEntry, BatchStatusResult, GithubIssueAction, ModelPreset, WorkflowStep, WorkflowStepMode, WorkflowStepGateMode, WorkflowStepPhase, WorkflowStepInput, WorkflowStepResult, WorkflowStepTemplate, Agent, OrgTreeNode, AgentState, AgentDetail, AgentCreateInput, AgentUpdateInput, AgentApiKey, AgentApiKeyCreateResult, AgentCapability, AgentPromptTemplate, AgentPromptsConfig, AgentPermission, PermanentAgentActionCategory, PermanentAgentSensitiveActionCategory, PermanentAgentGatingContext, AgentPermissionPolicy, AgentPermissionPolicyRules, AgentPermissionPolicyToolRules, AgentPermissionPolicyActionCategory, AgentProvisioningApprovalMode, SandboxProvisioningApprovalMode, LegacyAgentPermissionPolicyActionCategory, ApprovalRequestActionCategoryInput, ApprovalRequestActionCategory, AgentPermissionPolicyDisposition, AgentPermissionPolicyPresetId, ApprovalRequestStatus, ApprovalRequestAuditEventType, ApprovalRequestActorSnapshot, ApprovalRequestTargetAction, ApprovalRequestAuditEvent, ApprovalRequest, ApprovalRequestCreateInput, ApprovalRequestDecisionInput, ApprovalRequestCompletionInput, ApprovalRequestListInput, TaskAssignSource, AgentAccessState, AgentHeartbeatConfig, AgentBudgetConfig, AgentBudgetStatus, InstructionsBundleConfig, MessageResponseMode, AgentHeartbeatEvent, AgentHeartbeatRun, BlockedStateSnapshot, HeartbeatInvocationSource, AgentTaskSession, AgentRating, AgentRatingSummary, AgentRatingInput, AgentConfigSnapshot, RevisionFieldDiff, AgentConfigRevision, AgentStats, ReflectionTrigger, ReflectionMetrics, AgentReflection, AgentPerformanceSummary, NtfyNotificationEvent, NotificationEvent, NotificationPayload, NotificationProviderConfig, CustomProvider, SteeringComment, ParticipantType, MessageType, Message, MessageCreateInput, MessageFilter, MessageMetadata, ProposedTaskMetadata, EphemeralTaskCreationPolicy, MessageReplyReference, Mailbox, CheckoutLease, CheckoutClaimPrecondition, TaskClaimRow, CentralClaimStore, RunAuditDomain, RunAuditEvent, RunAuditEventInput, RunAuditEventFilter, AgentMemoryInclusionMode, HeartbeatPromptTemplate, HeartbeatScopeDisciplineMode, WorktrunkSettings, WorktrunkOnFailure, TaskBranchContext, CliAgentSettings, McpSecretRef, McpSensitiveValue, McpStdioTransport, McpSseTransport, McpStreamableHttpTransport, McpTransport, McpServerDefinition, McpServersSettings, GitlabConfigSettingsSource, ResolvedGitlabConfig, ResolveGitlabConfigInput, GitlabAuthTokenType, PlannerOversightStage, PlannerInterventionAction, PlannerInterventionOutcome, PlannerInterventionSourceLink, PlannerInterventionEntry, ExecutorOverseerSignalMemory, BackupSettingsMigrationCandidate, BackupSettingsMigrationConflict } from "./types.js"; export { AGENT_VALID_TRANSITIONS, DUPLICATE_OF_METADATA_KEY, assertNotWorkspaceTaskMerge, isWorkspaceTask, WorkspaceTaskMergeError } from "./types.js"; export { resolveEntryPointBranchAssignment, diff --git a/packages/core/src/settings-schema.ts b/packages/core/src/settings-schema.ts index 9c984509e4..e31e01e903 100644 --- a/packages/core/src/settings-schema.ts +++ b/packages/core/src/settings-schema.ts @@ -634,6 +634,8 @@ export const DEFAULT_PROJECT_SETTINGS = { sessionAdvisorEnabledByDefault: false, githubLinkImportedIssuesToTracking: false, githubTrackingDefaultRepo: undefined, + reportMode: "draft-review" as const, + reportModeByAction: undefined, gitlabEnabled: undefined, gitlabInstanceUrl: undefined, gitlabApiBaseUrl: undefined, diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 717d894569..c61305fb7d 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -3089,6 +3089,9 @@ export type SecretsEnvConfig = SecretsEnvSettings; * Runtime state fields (globalPause, enginePaused) also live here because * different projects may need independent pause control. */ +export type ReportMode = "draft-review" | "auto-file"; +export type ReportActionType = "bug" | "feedback" | "idea" | "help"; + export interface ProjectSettings { /** Hard stop: when true, all automated agent activity is **immediately** * terminated — active triage, execution, and merge agent sessions are @@ -3995,6 +3998,13 @@ export interface ProjectSettings { /** Project default GitHub tracking repo in `owner/repo` format (FN-3868). * Falls back to global githubTrackingDefaultRepo when unset. */ githubTrackingDefaultRepo?: string; + /** + * FNXC:ReportPipeline 2026-07-16-12:00: + * In-app reports default to review before egress; operators can opt into + * direct filing globally or for one of the four guided report actions. + */ + reportMode?: ReportMode; + reportModeByAction?: Partial>; /** * FNXC:GitLabConfiguration 2026-07-02-00:00: * FN-7422 adds durable GitLab instance/API URL settings for GitLab.com and self-managed hosts. FN-7423 layers token settings onto the same project-over-global configuration contract without adding runtime GitLab imports or tracking. diff --git a/packages/dashboard/app/api.ts b/packages/dashboard/app/api.ts index 1a129debf5..05bae20164 100644 --- a/packages/dashboard/app/api.ts +++ b/packages/dashboard/app/api.ts @@ -6,3 +6,4 @@ */ export * from "./api/legacy"; export * from "./api-node"; +export * from "./api/report"; diff --git a/packages/dashboard/app/api/report.ts b/packages/dashboard/app/api/report.ts new file mode 100644 index 0000000000..487a1df0e0 --- /dev/null +++ b/packages/dashboard/app/api/report.ts @@ -0,0 +1,11 @@ +import type { ReportActionType } from "@fusion/core"; + +async function post(path: string, body: unknown) { + const response = await fetch(path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }); + if (!response.ok) throw new Error((await response.json().catch(() => ({ error: response.statusText }))).error ?? response.statusText); + return response.json(); +} + +export function reportDraft(input: { actionType: ReportActionType; userPrompt: string; contextRefs?: { taskId?: string; agentId?: string } }) { return post("/api/report/draft", input); } +export function reportFile(input: { actionType: ReportActionType; report: unknown; endorseIssueNumber?: number; endorseDiscussionId?: string }) { return post("/api/report/file", input); } +export function reportHelp(question: string) { return post("/api/report/help", { question }); } diff --git a/packages/dashboard/app/components/Header.tsx b/packages/dashboard/app/components/Header.tsx index 8db2fb5fe7..bf564b2a9e 100644 --- a/packages/dashboard/app/components/Header.tsx +++ b/packages/dashboard/app/components/Header.tsx @@ -17,11 +17,28 @@ import type { PluginDashboardViewEntry } from "../api"; import { buildPluginTaskViewId, isPluginViewId } from "../plugins/pluginViewRegistry"; import { getPluginNavIcon } from "./pluginNavIcon"; import type { ShellHostContext } from "../shell-host"; +import type { ReportActionType } from "@fusion/core"; +import { ReportActionMenu } from "./ReportActionMenu"; +import { ReportModal } from "./ReportModal"; export { useViewportMode }; const NO_BRANCH_FILTER_VALUE = "__fusion:no-branch__"; +/** + * FNXC:ReportPipeline 2026-07-16-19:30: + * Reports opened from task detail routes must gather the active task's logs, + * agent, and errors. Task navigation uses hash routes, while legacy links use + * query parameters, so resolve both at modal-open render time. + */ +export function resolveReportContextRefs(location: Pick): { taskId?: string; agentId?: string } | undefined { + const params = new URLSearchParams(location.search); + const hashTaskMatch = location.hash.match(/^#\/tasks\/([^/?#]+)/); + const taskId = hashTaskMatch?.[1] ? decodeURIComponent(hashTaskMatch[1]) : params.get("taskId") ?? undefined; + const agentId = params.get("agentId") ?? undefined; + return taskId || agentId ? { taskId, agentId } : undefined; +} + // Status icon config for project selector dropdown const PROJECT_STATUS_CONFIG: Record = { active: { color: "var(--success)" }, @@ -172,6 +189,10 @@ export function Header({ const isTablet = mode === "tablet"; const isCompact = isMobile || isTablet; const hideFullNav = isMobile && mobileNavEnabled; + const [reportAction, setReportAction] = useState(null); + // Resolve on every render so opening a report after in-app hash navigation + // uses the current task rather than the Header's initial route. + const reportContextRefs = typeof window === "undefined" ? undefined : resolveReportContextRefs(window.location); /* FNXC:Navigation 2026-06-19-00:00: When experimental left sidebar navigation is active on tablet/desktop, Header must suppress its view-toggle and More-views trigger so there is one canonical non-mobile navigation surface and no orphaned chevron remains. @@ -963,6 +984,14 @@ export function Header({ FNXC:Navigation 2026-06-22-00:00: When the left sidebar is active it owns Workflows as a main-content destination, so the Header drops its duplicate desktop Workflow button. The flag-off desktop layout keeps the Header button; mobile/tablet keep the overflow entry. */} + {/* + FNXC:ReportPipeline 2026-07-16-21:30: + Bug, Feedback, Idea, and Help must remain reachable at every responsive + breakpoint. Compact navigation can hide the overflow trigger entirely, + so the report menu lives directly in the header action row instead. + */} + + {!isCompact && !leftSidebarNavActive && onOpenWorkflowEditor && ( + {open &&
+ {actions.map(({ type, label, Icon }) => )} +
} + ; +} diff --git a/packages/dashboard/app/components/ReportModal.css b/packages/dashboard/app/components/ReportModal.css new file mode 100644 index 0000000000..e93c0bfbf6 --- /dev/null +++ b/packages/dashboard/app/components/ReportModal.css @@ -0,0 +1,6 @@ +.report-modal-backdrop { position: fixed; inset: 0; z-index: var(--z-modal); display: grid; place-items: center; padding: var(--space-4); background: color-mix(in srgb, var(--bg-base) 70%, transparent); } +.report-modal { position: relative; display: grid; gap: var(--space-3); inline-size: min(var(--space-96), 100%); padding: var(--space-5); } +.report-modal textarea { min-block-size: var(--space-32); resize: vertical; } +.report-modal__close { position: absolute; inset-block-start: var(--space-2); inset-inline-end: var(--space-2); } +.report-modal__error { color: var(--color-error); } +@media (max-width: 768px) { .report-modal { inline-size: 100%; padding: var(--space-4); } } diff --git a/packages/dashboard/app/components/ReportModal.tsx b/packages/dashboard/app/components/ReportModal.tsx new file mode 100644 index 0000000000..fd960efa05 --- /dev/null +++ b/packages/dashboard/app/components/ReportModal.tsx @@ -0,0 +1,81 @@ +import { useState } from "react"; +import type { ReportActionType } from "@fusion/core"; +import { reportDraft, reportFile, reportHelp } from "../api"; +import "./ReportModal.css"; + +const prompts: Record = { bug: "What went wrong?", feedback: "What would you like to share?", idea: "What would you like Fusion to do?", help: "What would you like help with?" }; +type ModalResult = { kind: string; report?: { userPrompt: string; sourcePrompt?: string; summary?: string; body?: string; context?: Record; sessionToken?: string }; issue?: { number: number; url: string; title: string; discussionId?: string }; url?: string; answer?: { summary?: string; content?: string }; message?: string }; + +/** + * FNXC:ReportPipeline 2026-07-16-12:00: + * The four actions start with a guided user prompt, not a raw issue form. The + * final filed or endorsed link remains visible after the selected mode acts. + */ +export function ReportModal({ actionType, onClose, contextRefs }: { actionType: ReportActionType; onClose: () => void; contextRefs?: { taskId?: string; agentId?: string } }) { + const [prompt, setPrompt] = useState(""); + const [result, setResult] = useState(); + const [busy, setBusy] = useState(false); + const [error, setError] = useState(); + const submit = async () => { + if (!prompt.trim()) return; + setBusy(true); + setError(undefined); + try { + if (actionType === "help") { + const help = await reportHelp(prompt); + if (help.answered) { setResult({ kind: "help", answer: help.answer }); return; } + } + setResult(await reportDraft({ actionType, userPrompt: prompt, contextRefs })); + } catch { + setError("We could not prepare your report. Check your connection and try again."); + } finally { setBusy(false); } + }; + const file = async (endorseIssueNumber?: number, endorseDiscussionId?: string) => { + if (!result?.report) return; + setBusy(true); + setError(undefined); + try { + setResult(await reportFile({ actionType, report: result.report, endorseIssueNumber, endorseDiscussionId })); + } catch { + setError("We could not send your report. Your draft is still here; try again."); + } finally { setBusy(false); } + }; + return
+ + {error &&

{error}

} + {!result && <>

{actionType[0].toUpperCase() + actionType.slice(1)}