From ca0e65f196d8fa6fae12d3af7b6e57501fdb68fe Mon Sep 17 00:00:00 2001 From: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Date: Sat, 25 Jul 2026 05:08:01 +0300 Subject: [PATCH] chore(app): remove unused files and code with Knip (#5185) --- app/knip.json | 23 +- app/package.json | 7 +- app/pnpm-lock.yaml | 35 +- app/src/__tests__/App.boot.test.tsx | 2 +- app/src/agentworld/assets.ts | 2 +- .../components/AgentProfileModal.tsx | 2 +- .../components/AmountCommitDialog.tsx | 4 +- app/src/agentworld/components/CommitFlow.tsx | 4 +- .../agentworld/components/ConfirmDialog.tsx | 2 +- .../components/TransferHandleModal.tsx | 2 +- .../components/X402ConfirmDialog.tsx | 6 +- .../agentworld/hooks/useTinyplaceStream.ts | 4 +- app/src/agentworld/hooks/useX402Buy.ts | 6 +- app/src/agentworld/iso/GameWorld.ts | 4 +- app/src/agentworld/iso/color.ts | 11 - app/src/agentworld/iso/furniture.ts | 2 +- app/src/agentworld/iso/geometry.ts | 9 +- app/src/agentworld/iso/index.ts | 30 +- app/src/agentworld/iso/rooms.ts | 10 +- app/src/agentworld/pages/directoryHelpers.ts | 4 +- app/src/chat/promptInjectionGuard.ts | 6 +- .../InitProgressScreen/InitProgressScreen.tsx | 2 +- app/src/components/LanguageSelect.tsx | 2 +- .../channels/mcp/McpInventoryManifest.ts | 2 +- .../components/channels/mcp/McpServersTab.tsx | 2 +- .../channels/mcp/McpToolPlayground.tsx | 2 +- .../components/chat/ApprovalRequestCard.tsx | 2 +- app/src/components/chat/ArtifactCard.tsx | 2 +- app/src/components/chat/ChatFilesChip.tsx | 2 +- app/src/components/chat/ChatFilesPanel.tsx | 2 +- .../chat/FlowApprovalRequestCard.tsx | 2 - .../chat/IntegrationConnectCard.tsx | 2 +- app/src/components/chat/QueuedFollowups.tsx | 2 +- .../chat/WorkflowProposalCard.test.tsx | 2 +- .../components/chat/WorkflowProposalCard.tsx | 2 +- .../FlowApprovalRequestCard.test.tsx | 2 +- .../components/composio/TriggerToggles.tsx | 2 +- .../components/dashboard/BudgetSummary.tsx | 2 +- app/src/components/dashboard/ChartTooltip.tsx | 4 +- app/src/components/dashboard/CostBarChart.tsx | 2 +- .../components/dashboard/ModelCostTable.tsx | 2 +- .../components/dashboard/TokenUsageChart.tsx | 2 +- .../components/dashboard/formatCurrency.ts | 2 +- .../feedback/FeedbackFilterSelect.tsx | 2 +- app/src/components/flows/FlowRowMenu.tsx | 4 +- .../flows/FlowRunInspectorDrawer.tsx | 2 - .../flows/FlowRunPendingApprovalCard.tsx | 2 - .../components/flows/FlowRunsDrawer.test.tsx | 2 +- app/src/components/flows/FlowRunsDrawer.tsx | 2 +- app/src/components/flows/FlowRunsSidebar.tsx | 2 +- .../components/flows/RunItemDataBrowser.tsx | 2 - .../flows/canvas/AgentNodeInspector.tsx | 4 +- .../flows/canvas/EditableFlowCanvas.tsx | 2 +- .../components/flows/canvas/FlowCanvas.tsx | 2 +- .../flows/canvas/FlowValidationBanner.tsx | 2 +- .../components/flows/canvas/NodePalette.tsx | 2 +- .../canvas/nodeConfig/NodeConfigDrawer.tsx | 2 +- .../canvas/nodeConfig/NodeConnections.tsx | 2 +- .../flows/canvas/nodeConfig/ScheduleField.tsx | 2 +- .../canvas/nodeConfig/composioFields.tsx | 4 +- .../canvas/nodeConfig/nativeToolFields.tsx | 2 +- .../canvas/nodeConfig/nodeConfigFields.tsx | 106 +-- .../canvas/nodeConfig/nodeConfigForms.tsx | 2 +- .../flows/canvas/useFlowValidation.ts | 6 +- app/src/components/flows/useCreateFlow.ts | 2 +- .../intelligence/AttentionQueue.tsx | 2 +- .../intelligence/AttentionQueueItem.tsx | 2 +- .../components/intelligence/HarnessGlyph.tsx | 2 +- .../components/intelligence/InstanceCard.tsx | 2 +- .../intelligence/InstanceStatusDot.tsx | 2 +- .../intelligence/MemorySourcesRegistry.tsx | 2 +- .../intelligence/MemoryTreeStatusPanel.tsx | 6 +- .../OrchestrationChatPrimitives.tsx | 2 +- .../components/intelligence/RelayBadge.tsx | 2 +- .../intelligence/SelfIdentityCard.tsx | 2 +- .../intelligence/TinyPlaceRoster.tsx | 2 +- app/src/components/intelligence/Toast.tsx | 2 +- .../intelligence/VaultHealthChecklist.tsx | 2 - .../intelligence/WorkflowRunApprovalCard.tsx | 4 +- .../intelligence/WorkflowRunDetail.tsx | 2 +- .../intelligence/memoryGraphLayout.ts | 4 +- .../intelligence/pixiGraphRenderer.ts | 2 +- .../components/intelligence/seedSvgLayout.ts | 4 +- .../intelligence/useSvgForceLayout.ts | 2 +- app/src/components/layout/ChipTabs.tsx | 2 +- .../components/layout/PageSectionHeader.tsx | 2 +- app/src/components/layout/PageWelcome.tsx | 8 +- app/src/components/layout/PanelHeader.tsx | 2 +- app/src/components/layout/PanelPage.tsx | 2 +- app/src/components/layout/PanelScaffold.tsx | 2 +- app/src/components/layout/TwoPaneNav.tsx | 4 +- app/src/components/layout/TwoPanelLayout.tsx | 4 +- .../layout/shell/RootShellLayout.tsx | 2 +- .../components/layout/usePageWelcomeView.tsx | 8 +- .../mcp-setup/SecretPromptDialog.test.tsx | 2 +- .../mcp-setup/SecretPromptDialog.tsx | 2 +- .../meetings/ActionItemChecklist.tsx | 2 +- .../meetings/ActiveMeetingBanner.tsx | 2 +- app/src/components/meetings/HistoryDetail.tsx | 2 +- app/src/components/meetings/HistoryRail.tsx | 2 +- .../components/meetings/HistorySection.tsx | 2 +- .../components/meetings/JoinPolicyToggle.tsx | 2 +- .../meetings/LiveTranscriptPanel.tsx | 4 +- app/src/components/meetings/MeetComposer.tsx | 2 +- .../meetings/MeetDefaultsDrawer.tsx | 2 +- app/src/components/meetings/MeetingsPage.tsx | 2 +- app/src/components/meetings/PlatformChips.tsx | 2 +- .../components/meetings/TranscriptViewer.tsx | 2 +- app/src/components/meetings/UpcomingTable.tsx | 2 +- app/src/components/meetings/meetingUtils.ts | 2 +- .../meetings/useUpcomingMeetings.ts | 2 +- .../components/oauth/oauthAuthReadiness.ts | 4 +- app/src/components/oauth/providerConfigs.tsx | 4 - .../orchestration/ActiveSubagentsRail.tsx | 2 +- .../orchestration/AgentChatPanel.tsx | 2 +- .../orchestration/SessionTranscript.tsx | 4 +- .../orchestration/demo/medullaDemoData.ts | 6 +- app/src/components/routines/RoutineCard.tsx | 140 --- .../components/routines/RoutineRunHistory.tsx | 90 -- app/src/components/routines/routineHelpers.ts | 25 - .../components/MemoryWindowControl.tsx | 32 - .../settings/components/PageBackButton.tsx | 26 - .../components/SettingsBackButton.tsx | 4 +- .../settings/components/SettingsHeader.tsx | 65 -- .../settings/controls/SettingsBadge.tsx | 2 +- .../settings/controls/SettingsCheckbox.tsx | 2 +- .../settings/controls/SettingsEmptyState.tsx | 2 +- .../settings/controls/SettingsListItem.tsx | 2 +- .../settings/controls/SettingsNumberField.tsx | 2 +- .../settings/controls/SettingsRow.tsx | 2 +- .../settings/controls/SettingsSection.tsx | 2 +- .../settings/controls/SettingsSelect.tsx | 4 +- .../settings/controls/SettingsStatusLine.tsx | 2 +- .../settings/controls/SettingsSwitch.tsx | 2 +- .../settings/controls/SettingsTextArea.tsx | 2 +- .../settings/controls/SettingsTextField.tsx | 2 +- app/src/components/settings/controls/index.ts | 13 +- .../settings/hooks/useSettingsNavigation.ts | 4 +- .../settings/layout/SettingsLayoutContext.tsx | 2 - .../settings/layout/SettingsPanel.tsx | 2 +- .../components/settings/panels/AIPanel.tsx | 4 +- .../settings/panels/AgentEditorPage.test.tsx | 4 - .../settings/panels/AgentsPanel.test.tsx | 4 - .../settings/panels/AppearancePanel.test.tsx | 4 - .../settings/panels/CronJobsPanel.test.tsx | 5 - .../settings/panels/DevicesPanel.tsx | 2 +- .../panels/NotificationRoutingPanel.test.tsx | 4 - .../panels/NotificationsPanel.test.tsx | 4 - .../settings/panels/PrivacyModeSection.tsx | 2 +- .../panels/ProfileEditorPage.test.tsx | 4 - .../settings/panels/ProfilesPanel.test.tsx | 9 - .../panels/ProviderSetupErrorNotice.tsx | 2 +- .../settings/panels/TeamInvitesPanel.test.tsx | 4 - .../panels/TeamManagementPanel.test.tsx | 4 - .../settings/panels/TeamMembersPanel.test.tsx | 4 - .../settings/panels/TeamPanel.test.tsx | 4 - .../settings/panels/ToolsPanel.test.tsx | 4 - .../panels/__tests__/ComposioPanel.test.tsx | 4 - .../__tests__/ComposioTriagePanel.test.tsx | 4 - .../__tests__/LocalModelDebugPanel.test.tsx | 2 - .../ScreenAwarenessDebugPanel.test.tsx | 2 - .../panels/__tests__/SecurityPanel.test.tsx | 4 - .../__tests__/TeamInvitesPanel.test.tsx | 1 - .../__tests__/TeamManagementPanel.test.tsx | 3 - .../__tests__/TeamMembersPanel.test.tsx | 1 - .../panels/__tests__/TeamPanel.test.tsx | 2 - .../components/settings/panels/aiRouting.ts | 2 +- .../panels/billing/PayAsYouGoCard.tsx | 156 ---- .../settings/panels/billingHelpers.ts | 9 - .../settings/panels/builtinCloudProviders.ts | 2 +- .../settings/panels/elevenlabsVoicePresets.ts | 4 +- .../panels/persona/personaSections.ts | 6 +- .../panels/persona/personaTemplates.ts | 2 +- .../settings/panels/theme/ColorTokenField.tsx | 2 +- .../panels/useReembedBackfillModal.ts | 4 +- .../settings/search/useSettingsSearch.ts | 4 +- .../settings/settingsRouteElements.tsx | 2 +- .../settings/settingsRouteRegistry.ts | 8 +- .../components/shortcuts/shortcutsView.tsx | 4 +- .../components/skills/CreateWorkflowForm.tsx | 4 +- .../components/skills/ScheduledCronCard.tsx | 2 +- app/src/components/skills/SkillCard.tsx | 4 +- .../components/skills/SmartIssuePicker.tsx | 2 +- .../components/skills/WorkflowRunnerBody.tsx | 4 +- .../__tests__/WorkflowRunnerBody.test.tsx | 2 +- .../components/skills/inputs/BranchPicker.tsx | 2 +- .../components/skills/inputs/RepoPicker.tsx | 4 +- app/src/components/skills/preflightGate.ts | 2 +- app/src/components/skills/skillIcons.tsx | 2 +- app/src/components/ui/Button.tsx | 8 +- app/src/components/ui/Card.tsx | 34 - app/src/components/ui/Tooltip.tsx | 4 +- app/src/components/ui/index.ts | 10 - .../components/userErrors/UserErrorCenter.tsx | 2 +- .../components/worktree/WorktreeActions.tsx | 2 +- app/src/config/navConfig.ts | 4 +- .../components/BackgroundProcessesPanel.tsx | 2 +- .../components/ChatThreadView.tsx | 2 +- .../conversations/components/LimitPill.tsx | 18 - .../components/PlanReviewCard.tsx | 2 - .../components/ThreadGoalChip.tsx | 2 +- .../components/ThreadTodoStrip.tsx | 2 - .../components/ToolTimelineBlock.tsx | 2 +- .../conversations/composerSendDecision.ts | 14 +- .../conversations/derived/mapDisplayItems.ts | 6 +- .../hooks/useBackgroundActivity.ts | 2 +- .../features/conversations/taskPlanActions.ts | 2 +- .../conversations/threadList/ThreadList.tsx | 2 +- .../timeline/ConversationTimeline.tsx | 2 +- .../conversations/timeline/selectors.ts | 2 +- .../features/conversations/timeline/types.ts | 5 - .../features/conversations/utils/format.ts | 54 +- .../conversations/utils/threadFilter.ts | 7 +- .../conversations/utils/workerThreadRef.ts | 2 +- app/src/features/daemon/store.ts | 8 +- .../features/human/Mascot/CustomGifMascot.tsx | 2 +- app/src/features/human/Mascot/Defs.tsx | 2 +- app/src/features/human/Mascot/Ghosty.tsx | 2 +- .../human/Mascot/ManifestRiveMascot.tsx | 2 +- .../human/Mascot/MascotChipAvatar.tsx | 4 +- app/src/features/human/Mascot/RiveMascot.tsx | 4 +- .../human/Mascot/backend/BackendMascot.tsx | 2 +- .../Mascot/backend/BackendRiveMascot.tsx | 2 +- .../human/Mascot/backend/renderCore.ts | 2 +- app/src/features/human/Mascot/index.ts | 28 +- .../human/Mascot/manifest/stateEngine.ts | 7 +- .../features/human/Mascot/manifest/types.ts | 5 - .../Mascot/manifest/useMascotManifest.ts | 2 +- .../features/human/Mascot/mascotPalette.ts | 2 +- app/src/features/human/Mascot/riveMaps.ts | 2 +- app/src/features/human/Mascot/visemes.ts | 2 +- app/src/features/human/MicComposer.test.tsx | 3 +- app/src/features/human/MicComposer.tsx | 4 +- app/src/features/human/SubMascotLayer.tsx | 4 +- app/src/features/human/useHumanMascot.ts | 4 +- app/src/features/human/voice/sttClient.ts | 10 +- app/src/features/human/voice/ttsClient.ts | 6 +- app/src/features/human/voice/visemeMap.ts | 2 +- app/src/features/meet/MascotFrameProducer.tsx | 2 - app/src/features/meet/useMeetingMascots.ts | 6 +- app/src/features/privacy/WhatLeavesLink.tsx | 2 +- .../privacy/WhatLeavesMyComputerSheet.tsx | 2 +- app/src/features/privacy/whatLeavesItems.ts | 2 +- app/src/features/screen-intelligence/api.ts | 4 +- .../useScreenIntelligenceSkillStatus.ts | 2 +- .../useScreenIntelligenceState.ts | 2 +- app/src/features/share/ShareCardModal.tsx | 4 +- app/src/features/share/ShareMessageButton.tsx | 4 +- app/src/features/share/shareCard.ts | 6 +- app/src/features/star/GitHubStarCard.tsx | 2 - app/src/features/voice/pttChimes.ts | 2 +- app/src/features/wallet/walletDisplay.ts | 4 +- app/src/hooks/useAppUpdate.ts | 8 +- app/src/hooks/useConsciousItems.ts | 2 +- app/src/hooks/useCostDashboard.ts | 10 +- app/src/hooks/useDaemonHealth.ts | 23 - app/src/hooks/useDictationHotkey.ts | 2 +- app/src/hooks/useFlowApprovalRequests.ts | 4 +- app/src/hooks/useFlowChanged.ts | 6 +- app/src/hooks/useFlowPendingApprovals.ts | 4 +- app/src/hooks/useFlowRunFinished.ts | 4 +- app/src/hooks/useFlowRunPoller.ts | 4 +- app/src/hooks/useFlowRunProgress.ts | 4 +- app/src/hooks/useFlowRunStarted.ts | 4 +- app/src/hooks/useFlowRunsLiveRefresh.ts | 2 - app/src/hooks/useIntelligenceSocket.ts | 6 - app/src/hooks/useMediaQuery.ts | 2 - app/src/hooks/useMemoryIngestionStatus.ts | 2 +- app/src/hooks/useOAuthConnectionListener.ts | 2 +- app/src/hooks/useRunsPendingApprovalSet.ts | 2 - app/src/hooks/useSubconscious.ts | 2 +- app/src/hooks/useUsageState.ts | 2 +- app/src/hooks/useWorkflowBuilderChat.ts | 4 +- app/src/lib/agentworld/invokeApiClient.ts | 218 +++-- app/src/lib/ai/skillToolChainLatency.ts | 4 +- app/src/lib/attachments.test.ts | 4 +- app/src/lib/attachments.ts | 21 +- app/src/lib/channels/routing.ts | 25 - app/src/lib/chat/toolCallEnvelope.ts | 2 +- app/src/lib/commands/hotkeyManager.ts | 2 +- app/src/lib/commands/registry.ts | 2 +- app/src/lib/composio/connectionCache.ts | 2 +- app/src/lib/composio/hooks.ts | 4 +- app/src/lib/composio/oauthHandoff.ts | 4 +- app/src/lib/composio/types.ts | 24 +- app/src/lib/cron/schedulePresets.ts | 2 +- app/src/lib/flows/cron.ts | 24 +- app/src/lib/flows/graphAdapter.ts | 4 +- app/src/lib/flows/graphDiff.ts | 2 +- app/src/lib/flows/nodeKindMeta.ts | 16 +- app/src/lib/flows/runStepSummary.ts | 4 +- app/src/lib/i18n/I18nContext.tsx | 2 - app/src/lib/mcp/errorHandler.ts | 8 - app/src/lib/mcp/index.ts | 11 - app/src/lib/mcp/rateLimiter.ts | 5 +- app/src/lib/mcp/types.ts | 23 - app/src/lib/nativeNotifications/index.ts | 1 - .../lib/nativeNotifications/tauriBridge.ts | 4 +- .../lib/orchestration/mergeToolActivity.ts | 4 +- .../lib/orchestration/orchestrationClient.ts | 12 +- .../orchestration/useOrchestrationChats.ts | 8 +- .../orchestration/useOrchestrationSessions.ts | 8 +- app/src/lib/orchestration/usePairing.ts | 4 +- app/src/lib/platform.ts | 10 - app/src/lib/recallCalendar/hooks.ts | 2 +- .../lib/recallCalendar/recallCalendarApi.ts | 4 +- app/src/lib/theme/presets.ts | 4 +- app/src/lib/theme/tokens.ts | 27 +- app/src/lib/tunnel/crypto.ts | 4 +- app/src/lib/webviewNotifications/index.ts | 7 +- app/src/pages/FlowCanvasPage.tsx | 8 +- app/src/pages/Routines.tsx | 252 ------ .../__tests__/Conversations.render.test.tsx | 6 - app/src/pages/onboarding/customWizardSteps.ts | 13 - app/src/polyfills.ts | 4 - app/src/services/analyticsRoutes.ts | 2 +- app/src/services/api/agentRegistryApi.ts | 4 +- app/src/services/api/agentTeamApi.ts | 14 +- app/src/services/api/agentWorkApi.ts | 6 +- app/src/services/api/aiSettingsApi.ts | 16 +- app/src/services/api/approvalApi.ts | 3 - app/src/services/api/channelConnectionsApi.ts | 8 +- app/src/services/api/creditsApi.ts | 14 +- app/src/services/api/embeddingsApi.ts | 2 +- app/src/services/api/flowsApi.ts | 101 ++- app/src/services/api/goalsApi.ts | 2 +- app/src/services/api/mcpClientsApi.ts | 2 +- app/src/services/api/mcpRegistryErrors.ts | 2 +- app/src/services/api/referralApi.ts | 2 +- app/src/services/api/rewardsApi.ts | 2 +- app/src/services/api/skillRegistryApi.ts | 6 +- app/src/services/api/skillsApi.ts | 10 +- app/src/services/api/subagentApi.ts | 2 +- app/src/services/api/threadUsageApi.ts | 4 +- app/src/services/api/todosApi.ts | 10 +- app/src/services/api/voiceInstallApi.ts | 4 +- app/src/services/api/voiceSettingsApi.ts | 22 +- app/src/services/api/workflowRunsApi.ts | 10 +- app/src/services/api/worktreeApi.ts | 6 +- app/src/services/artifactDownloadService.ts | 10 +- app/src/services/backendHealth.ts | 4 +- app/src/services/chatService.ts | 4 +- app/src/services/clientVersionHeaders.ts | 2 +- app/src/services/coreCommandClient.ts | 2 +- app/src/services/coreRpcClient.ts | 8 +- app/src/services/coreSocket.ts | 4 +- app/src/services/coreStateApi.ts | 6 +- app/src/services/meetCallService.ts | 12 +- app/src/services/pttService.ts | 2 +- app/src/services/rpcMethods.ts | 2 +- app/src/services/walletApi.ts | 6 +- app/src/services/webviewAccountService.ts | 27 +- app/src/store/backendMeetSlice.ts | 9 +- app/src/store/chatRuntimeSlice.ts | 81 +- app/src/store/connectivitySelectors.ts | 7 +- app/src/store/deepLinkAuthState.ts | 2 +- app/src/store/mascotSlice.ts | 9 +- app/src/store/notificationSlice.ts | 1 - app/src/store/personaSlice.ts | 3 +- app/src/store/pttSlice.ts | 2 - app/src/store/socketSlice.ts | 4 +- app/src/store/themeSlice.ts | 18 +- app/src/store/threadSlice.ts | 28 - app/src/store/userErrorsSelectors.ts | 2 +- app/src/test/commandTestUtils.ts | 2 +- app/src/types/api.ts | 3 - app/src/types/channels.ts | 7 +- app/src/types/intelligence.ts | 26 - app/src/types/notifications.ts | 8 - app/src/types/oauth.ts | 11 - app/src/types/thread.ts | 18 - app/src/utils/accountsFullscreen.ts | 16 - app/src/utils/agentMessageBubbles.ts | 2 +- app/src/utils/bootstrapActiveUser.ts | 2 +- app/src/utils/clearAllAppData.ts | 2 +- app/src/utils/cryptoKeys.ts | 4 +- app/src/utils/localAiBootstrap.ts | 2 +- app/src/utils/oauthAppVersionGate.ts | 12 +- app/src/utils/ollamaUrlValidation.ts | 2 +- app/src/utils/openrouterOAuth.ts | 2 +- app/src/utils/toolDefinitions.ts | 4 +- app/src/utils/toolTimelineFormatting.ts | 4 +- app/src/utils/workspaceLinks.ts | 2 +- app/test/e2e/helpers/app-helpers.ts | 26 +- app/test/e2e/helpers/artifacts.ts | 9 - app/test/e2e/helpers/config-toml.ts | 4 +- app/test/e2e/helpers/core-rpc-node.ts | 2 +- app/test/e2e/helpers/core-rpc-webview.ts | 48 +- app/test/e2e/helpers/core-rpc.ts | 3 +- app/test/e2e/helpers/element-helpers.ts | 33 +- app/test/e2e/helpers/platform.ts | 13 +- app/test/e2e/helpers/rpc-preflight.ts | 99 --- app/test/e2e/helpers/shared-flows.ts | 37 +- app/test/e2e/helpers/telegram.ts | 10 +- app/test/e2e/mock-server.ts | 2 - app/test/e2e/specs/card-payment-flow.spec.ts | 2 +- app/test/playwright/helpers/core-rpc.ts | 4 +- docs/plans/2026-07-24-knip-cleanup.md | 797 ++++++++++++++++++ docs/specs/2026-07-24-knip-cleanup-design.md | 107 +++ pnpm-lock.yaml | 37 +- 400 files changed, 1647 insertions(+), 2739 deletions(-) delete mode 100644 app/src/components/routines/RoutineCard.tsx delete mode 100644 app/src/components/routines/RoutineRunHistory.tsx delete mode 100644 app/src/components/settings/components/PageBackButton.tsx delete mode 100644 app/src/components/settings/components/SettingsHeader.tsx delete mode 100644 app/src/components/settings/panels/billing/PayAsYouGoCard.tsx delete mode 100644 app/src/components/ui/Card.tsx delete mode 100644 app/src/features/conversations/components/LimitPill.tsx delete mode 100644 app/src/pages/Routines.tsx delete mode 100644 app/test/e2e/helpers/rpc-preflight.ts create mode 100644 docs/plans/2026-07-24-knip-cleanup.md create mode 100644 docs/specs/2026-07-24-knip-cleanup-design.md diff --git a/app/knip.json b/app/knip.json index 668da51ad..fc50631c6 100644 --- a/app/knip.json +++ b/app/knip.json @@ -1,27 +1,28 @@ { "$schema": "https://unpkg.com/knip@6/schema.json", - "entry": ["src/main.tsx", "test/e2e/specs/**/*.spec.ts"], - "project": [ - "src/**/*.{ts,tsx}", - "test/**/*.{ts,tsx}", - "test/e2e/globals.d.ts", - "*.config.{js,ts}" + "entry": [ + "src/main.tsx", + "src/features/conversations/index.ts", + "vite.config.ts", + "test/vitest.config.ts", + "test/wdio.conf.ts", + "playwright.config.ts", + "test/e2e/specs/**/*.spec.ts", + "test/playwright/specs/**/*.spec.ts" ], + "project": ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "*.config.{js,ts}"], + "tags": ["-knipignore"], "ignoreDependencies": [ - "@tauri-apps/cli", - "@testing-library/dom", "@wdio/appium-service", "@wdio/cli", "@wdio/local-runner", "@wdio/mocha-framework", "@wdio/spec-reporter", "buffer", - "eslint", "husky", "os-browserify", - "prettier", "process", "util" ], - "ignoreBinaries": ["eslint", "knip", "open", "prettier", "tauri", "tsc", "vite", "vitest"] + "ignoreBinaries": ["open"] } diff --git a/app/package.json b/app/package.json index f7693d14e..89da92a0c 100644 --- a/app/package.json +++ b/app/package.json @@ -95,14 +95,12 @@ "d3-force": "^3.0.0", "debug": "^4.4.3", "katex": "^0.16.47", - "lottie-react": "^2.4.1", "os-browserify": "^0.3.0", "pixi.js": "^8.18.1", "process": "^0.11.10", "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-dom": "^19.1.0", - "react-ga4": "^3.0.1", "react-icons": "^5.6.0", "react-joyride": "^3.1.0", "react-markdown": "^10.1.0", @@ -145,9 +143,11 @@ "@vitest/coverage-v8": "^4.0.18", "@wdio/appium-service": "^9.24.0", "@wdio/cli": "^9.24.0", + "@wdio/globals": "^9.24.0", "@wdio/local-runner": "^9.24.0", "@wdio/mocha-framework": "^9.24.0", "@wdio/spec-reporter": "^9.24.0", + "@wdio/types": "^9.24.0", "autoprefixer": "^10.4.23", "cross-env": "^10.1.0", "eslint": "^9.39.2", @@ -166,6 +166,7 @@ "typescript": "~5.8.3", "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.26.0", - "vitest": "^4.0.18" + "vitest": "^4.0.18", + "webdriverio": "^9.24.0" } } diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index ff2f8cea7..695177c30 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -46,9 +46,6 @@ importers: debug: specifier: ^4.4.3 version: 4.4.3(supports-color@8.1.1) - lottie-react: - specifier: ^2.4.1 - version: 2.4.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) os-browserify: specifier: ^0.3.0 version: 0.3.0 @@ -149,6 +146,9 @@ importers: '@wdio/cli': specifier: ^9.24.0 version: 9.27.0(@types/node@25.6.0)(expect-webdriverio@5.6.5) + '@wdio/globals': + specifier: ^9.24.0 + version: 9.27.0(expect-webdriverio@5.6.5)(webdriverio@9.27.0) '@wdio/local-runner': specifier: ^9.24.0 version: 9.27.0(@wdio/globals@9.27.0)(webdriverio@9.27.0) @@ -158,6 +158,9 @@ importers: '@wdio/spec-reporter': specifier: ^9.24.0 version: 9.27.0 + '@wdio/types': + specifier: ^9.24.0 + version: 9.27.0 autoprefixer: specifier: ^10.4.23 version: 10.5.0(postcss@8.5.10) @@ -206,6 +209,9 @@ importers: vitest: specifier: ^4.0.18 version: 4.1.5(@types/node@25.6.0)(@vitest/coverage-v8@4.1.5)(jsdom@28.1.0(@noble/hashes@2.2.0))(vite@7.3.2(@types/node@25.6.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.3)) + webdriverio: + specifier: ^9.24.0 + version: 9.27.0 packages: '@acemir/cssom@0.9.31': @@ -5748,21 +5754,6 @@ packages: } hasBin: true - lottie-react@2.4.1: - resolution: - { - integrity: sha512-LQrH7jlkigIIv++wIyrOYFLHSKQpEY4zehPicL9bQsrt1rnoKRYCYgpCUe5maqylNtacy58/sQDZTkwMcTRxZw==, - } - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - lottie-web@5.13.0: - resolution: - { - integrity: sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ==, - } - lru-cache@10.4.3: resolution: { @@ -12146,14 +12137,6 @@ snapshots: dependencies: js-tokens: 4.0.0 - lottie-react@2.4.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5): - dependencies: - lottie-web: 5.13.0 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - - lottie-web@5.13.0: {} - lru-cache@10.4.3: {} lru-cache@11.3.5: {} diff --git a/app/src/__tests__/App.boot.test.tsx b/app/src/__tests__/App.boot.test.tsx index 181585202..1ad8f09f3 100644 --- a/app/src/__tests__/App.boot.test.tsx +++ b/app/src/__tests__/App.boot.test.tsx @@ -84,7 +84,7 @@ vi.mock('../components/upsell/GlobalUpsellBanner', () => ({ default: () => null vi.mock('../components/walkthrough/AppWalkthrough', () => ({ default: () => null })); vi.mock('../features/meet/MascotFrameProducer', () => ({ MascotFrameProducer: () => null })); vi.mock('../services/analytics', () => ({ trackPageView: vi.fn() })); -vi.mock('../utils/accountsFullscreen', () => ({ isAccountsFullscreen: vi.fn(() => false) })); +vi.mock('../utils/accountsFullscreen', () => ({ AGENT_ACCOUNT_ID: '__agent__' })); vi.mock('../store/hooks', () => ({ useAppSelector: vi.fn(() => null) })); vi.mock('@sentry/react', () => ({ ErrorBoundary: ({ children }: { children: React.ReactNode }) => <>{children}, diff --git a/app/src/agentworld/assets.ts b/app/src/agentworld/assets.ts index 42d7ec3f8..3917b6e12 100644 --- a/app/src/agentworld/assets.ts +++ b/app/src/agentworld/assets.ts @@ -15,7 +15,7 @@ const KNOWN_MINTS: Record = { }; /** Decimals per known symbol. USDC/CASH = 6, SOL/WSOL = 9, others = 0. */ -export function decimalsForSymbol(symbol: string | undefined): number { +function decimalsForSymbol(symbol: string | undefined): number { const up = (symbol ?? '').toUpperCase(); if (up === 'USDC' || up === 'CASH') return 6; if (up === 'SOL' || up === 'WSOL') return 9; diff --git a/app/src/agentworld/components/AgentProfileModal.tsx b/app/src/agentworld/components/AgentProfileModal.tsx index 00b66dbf8..3c5be56eb 100644 --- a/app/src/agentworld/components/AgentProfileModal.tsx +++ b/app/src/agentworld/components/AgentProfileModal.tsx @@ -37,7 +37,7 @@ function formatJoined(iso: string): string { return date.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }); } -export interface AgentProfileModalProps { +interface AgentProfileModalProps { agent: AgentCard; onClose: () => void; } diff --git a/app/src/agentworld/components/AmountCommitDialog.tsx b/app/src/agentworld/components/AmountCommitDialog.tsx index ed0b19203..85a6cd1b1 100644 --- a/app/src/agentworld/components/AmountCommitDialog.tsx +++ b/app/src/agentworld/components/AmountCommitDialog.tsx @@ -17,7 +17,7 @@ import Button from '../../components/ui/Button'; import { ModalShell } from '../../components/ui/ModalShell'; import { useT } from '../../lib/i18n/I18nContext'; -export interface AmountCommitDialogProps { +interface AmountCommitDialogProps { /** Header title, e.g. "Bid on @handle". */ title: string; /** Context line under the title. */ @@ -36,7 +36,7 @@ export interface AmountCommitDialogProps { onCancel: () => void; } -export interface ParsedAmount { +interface ParsedAmount { /** Amount in base units, ready for the RPC. Only set when `valid`. */ base: string | null; valid: boolean; diff --git a/app/src/agentworld/components/CommitFlow.tsx b/app/src/agentworld/components/CommitFlow.tsx index 1f71561de..bee0e9583 100644 --- a/app/src/agentworld/components/CommitFlow.tsx +++ b/app/src/agentworld/components/CommitFlow.tsx @@ -25,9 +25,9 @@ import { useT } from '../../lib/i18n/I18nContext'; import AmountCommitDialog from './AmountCommitDialog'; import X402ConfirmDialog, { type X402WalletBalance } from './X402ConfirmDialog'; -export type CommitKind = 'bid' | 'offer'; +type CommitKind = 'bid' | 'offer'; -export interface CommitFlowProps { +interface CommitFlowProps { kind: CommitKind; /** Display name of the listing/handle (e.g. "@auction"). */ name: string; diff --git a/app/src/agentworld/components/ConfirmDialog.tsx b/app/src/agentworld/components/ConfirmDialog.tsx index c38c828db..9aa794c74 100644 --- a/app/src/agentworld/components/ConfirmDialog.tsx +++ b/app/src/agentworld/components/ConfirmDialog.tsx @@ -14,7 +14,7 @@ import Button from '../../components/ui/Button'; import { ModalShell } from '../../components/ui/ModalShell'; -export interface ConfirmDialogProps { +interface ConfirmDialogProps { /** Modal header (e.g. "Delete post"). */ title: string; /** Body copy explaining the consequence (e.g. "Delete this post? This can't be undone."). */ diff --git a/app/src/agentworld/components/TransferHandleModal.tsx b/app/src/agentworld/components/TransferHandleModal.tsx index 98464ca2b..a9e0e01bf 100644 --- a/app/src/agentworld/components/TransferHandleModal.tsx +++ b/app/src/agentworld/components/TransferHandleModal.tsx @@ -21,7 +21,7 @@ import { apiClient } from '../AgentWorldShell'; // Namespaced already ('agentworld:identity'), so messages carry no prefix. const debug = debugFactory('agentworld:identity'); -export interface TransferHandleModalProps { +interface TransferHandleModalProps { /** The handle being transferred away (without a leading @). */ handle: string; onClose: () => void; diff --git a/app/src/agentworld/components/X402ConfirmDialog.tsx b/app/src/agentworld/components/X402ConfirmDialog.tsx index 22b4de788..00c306954 100644 --- a/app/src/agentworld/components/X402ConfirmDialog.tsx +++ b/app/src/agentworld/components/X402ConfirmDialog.tsx @@ -64,9 +64,9 @@ export interface X402WalletBalance { * - `commit` — signed bid/offer commitment. Insufficient balance SOFT-warns but * still allows Confirm (funds move only on acceptance). */ -export type X402ConfirmMode = 'spend' | 'commit'; +type X402ConfirmMode = 'spend' | 'commit'; -export interface X402ConfirmDialogProps { +interface X402ConfirmDialogProps { /** Title shown in the modal header (e.g. "Register @handle"). */ title: string; /** Optional subtitle / context line. */ @@ -107,7 +107,7 @@ export interface X402ConfirmDialogProps { * "unknown" case into "sufficient", silently bypassing the gate. Callers that * only need the provable-shortfall signal can use `isInsufficient` below. */ -export type BalanceStatus = 'unknown' | 'insufficient' | 'sufficient'; +type BalanceStatus = 'unknown' | 'insufficient' | 'sufficient'; export function balanceStatus(balance: X402WalletBalance | null, amount: string): BalanceStatus { if (!balance) return 'unknown'; diff --git a/app/src/agentworld/hooks/useTinyplaceStream.ts b/app/src/agentworld/hooks/useTinyplaceStream.ts index d34152cc5..a76f11814 100644 --- a/app/src/agentworld/hooks/useTinyplaceStream.ts +++ b/app/src/agentworld/hooks/useTinyplaceStream.ts @@ -2,13 +2,13 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { socketService } from '../../services/socketService'; -export interface TinyplaceStreamMessage { +interface TinyplaceStreamMessage { stream_id: string; kind: string; message: unknown; } -export interface TinyplaceStreamStatus { +interface TinyplaceStreamStatus { stream_id: string; status: string; } diff --git a/app/src/agentworld/hooks/useX402Buy.ts b/app/src/agentworld/hooks/useX402Buy.ts index 41b24d535..ffc8fa888 100644 --- a/app/src/agentworld/hooks/useX402Buy.ts +++ b/app/src/agentworld/hooks/useX402Buy.ts @@ -17,7 +17,7 @@ import { type X402BuyResult, } from '../../lib/agentworld/invokeApiClient'; -export type X402BuyState = +type X402BuyState = | { phase: 'idle' } | { phase: 'challenge_loading' } | { @@ -35,7 +35,7 @@ export type X402BuyState = | { phase: 'success'; result: Record; onChainTx?: string; network?: string } | { phase: 'error'; message: string; onChainTx?: string }; -export type X402BuyFn = (id: string, opts: { confirmed: boolean }) => Promise; +type X402BuyFn = (id: string, opts: { confirmed: boolean }) => Promise; /** Devnet/mainnet Solana explorer link for a settled payment tx. */ export function explorerTxUrl(tx: string, network?: string): string { @@ -49,7 +49,7 @@ export function extractOnChainTx(message: string): string | undefined { return match?.[1]; } -export interface UseX402Buy { +interface UseX402Buy { state: X402BuyState; reset: () => void; begin: (id: string) => void; diff --git a/app/src/agentworld/iso/GameWorld.ts b/app/src/agentworld/iso/GameWorld.ts index ce6f9d0e7..5c5f6f773 100644 --- a/app/src/agentworld/iso/GameWorld.ts +++ b/app/src/agentworld/iso/GameWorld.ts @@ -49,7 +49,7 @@ const RENDERER_PREFERENCE = 'webgl' as const; const INIT_TIMEOUT_MS = 10_000; /** Thrown when renderer init rejects or exceeds {@link INIT_TIMEOUT_MS}. */ -export class RendererInitError extends Error { +class RendererInitError extends Error { public constructor(message: string, options?: { cause?: unknown }) { super(message, options); this.name = 'RendererInitError'; @@ -191,7 +191,7 @@ function boxRelation(pointX: number, pointY: number, box: DepthObstacle): number // several worlds mount over a session (e.g. React strict-mode double-mounts). let fontsInstalled = false; -export interface AgentSummary { +interface AgentSummary { id: string; label: string; } diff --git a/app/src/agentworld/iso/color.ts b/app/src/agentworld/iso/color.ts index 02e4cb0f2..078925e8e 100644 --- a/app/src/agentworld/iso/color.ts +++ b/app/src/agentworld/iso/color.ts @@ -9,17 +9,6 @@ function pack(red: number, green: number, blue: number): number { return (clamp(red) << 16) | (clamp(green) << 8) | clamp(blue); } -/** Linearly blend two colours; `amount` 0 returns `colorA`, 1 returns `colorB`. */ -export function mixColor(colorA: number, colorB: number, amount: number): number { - const [redA, greenA, blueA] = channels(colorA); - const [redB, greenB, blueB] = channels(colorB); - return pack( - redA + (redB - redA) * amount, - greenA + (greenB - greenA) * amount, - blueA + (blueB - blueA) * amount - ); -} - /** Scale a colour's brightness (`factor` < 1 darkens, > 1 lightens). */ export function shadeColor(color: number, factor: number): number { const [red, green, blue] = channels(color); diff --git a/app/src/agentworld/iso/furniture.ts b/app/src/agentworld/iso/furniture.ts index dedcc4030..e0ffd11ca 100644 --- a/app/src/agentworld/iso/furniture.ts +++ b/app/src/agentworld/iso/furniture.ts @@ -150,7 +150,7 @@ function tableBlueprint(options: { }; } -export const FURNITURE_BLUEPRINTS: Record = { +const FURNITURE_BLUEPRINTS: Record = { pokerTable: { footprintWidth: 3, footprintHeight: 2, diff --git a/app/src/agentworld/iso/geometry.ts b/app/src/agentworld/iso/geometry.ts index 3d1e2ecff..efb6b79c5 100644 --- a/app/src/agentworld/iso/geometry.ts +++ b/app/src/agentworld/iso/geometry.ts @@ -29,7 +29,7 @@ export interface ScreenPoint { y: number; } -export interface TilePoint { +interface TilePoint { x: number; y: number; } @@ -62,7 +62,7 @@ export function screenToTile(screenX: number, screenY: number): TilePoint { * Layer biases keep entities that share a tile in a stable front-to-back order * without disturbing the dominant `gridX + gridY` ordering. */ -export const DEPTH_TILE_SCALE = 16; +const DEPTH_TILE_SCALE = 16; export const LAYER_FLOOR = 0; export const LAYER_DECAL = 1; export const LAYER_WALL = 2; @@ -78,8 +78,3 @@ export function depthAt(gridX: number, gridY: number, level = 0, layer = 0): num export function lerp(from: number, to: number, amount: number): number { return from + (to - from) * amount; } - -/** Euclidean distance between two tile points. */ -export function tileDistance(from: TilePoint, to: TilePoint): number { - return Math.hypot(to.x - from.x, to.y - from.y); -} diff --git a/app/src/agentworld/iso/index.ts b/app/src/agentworld/iso/index.ts index 57e8e2d7d..dc39904f9 100644 --- a/app/src/agentworld/iso/index.ts +++ b/app/src/agentworld/iso/index.ts @@ -1,30 +1,4 @@ /** Public surface of the isometric agent world engine. */ -export { GameWorld, RendererInitError } from './GameWorld'; -export type { AgentSummary } from './GameWorld'; -export { BaseRoom } from './BaseRoom'; -export { - ROOM_REGISTRY, - PokerTableRoom, - CourtHouseRoom, - OfficeRoom, - HomeRoom, - OutsideWorldRoom, -} from './rooms'; -export type { RoomEntry } from './rooms'; -export { ChatBubble } from './ChatBubble'; -export { Agent } from './Agent'; -export { FurnitureSprite, FURNITURE_BLUEPRINTS } from './furniture'; -export { TextureFactory } from './textures'; -export type { - AgentAction, - AgentState, - ChatMessage, - Facing, - FurnitureConfig, - InteractionPoint, - RoomDefinition, - RoomPalette, - WalkNode, -} from './types'; -export { TileCode } from './types'; +export { GameWorld } from './GameWorld'; +export { ROOM_REGISTRY } from './rooms'; diff --git a/app/src/agentworld/iso/rooms.ts b/app/src/agentworld/iso/rooms.ts index 3370c0c04..93ed63d5b 100644 --- a/app/src/agentworld/iso/rooms.ts +++ b/app/src/agentworld/iso/rooms.ts @@ -415,31 +415,31 @@ function outsideDefinition(): RoomDefinition { // ---- Concrete room subclasses ---------------------------------------------- -export class PokerTableRoom extends BaseRoom { +class PokerTableRoom extends BaseRoom { public constructor(factory: TextureFactory) { super(pokerDefinition(), factory); } } -export class CourtHouseRoom extends BaseRoom { +class CourtHouseRoom extends BaseRoom { public constructor(factory: TextureFactory) { super(courtDefinition(), factory); } } -export class OfficeRoom extends BaseRoom { +class OfficeRoom extends BaseRoom { public constructor(factory: TextureFactory) { super(officeDefinition(), factory); } } -export class HomeRoom extends BaseRoom { +class HomeRoom extends BaseRoom { public constructor(factory: TextureFactory) { super(homeDefinition(), factory); } } -export class OutsideWorldRoom extends BaseRoom { +class OutsideWorldRoom extends BaseRoom { public constructor(factory: TextureFactory) { super(outsideDefinition(), factory); } diff --git a/app/src/agentworld/pages/directoryHelpers.ts b/app/src/agentworld/pages/directoryHelpers.ts index 0d5875b59..58cb16764 100644 --- a/app/src/agentworld/pages/directoryHelpers.ts +++ b/app/src/agentworld/pages/directoryHelpers.ts @@ -8,7 +8,7 @@ */ import { type AgentCard } from '../../lib/agentworld/invokeApiClient'; -export const AVATAR_COLORS = [ +const AVATAR_COLORS = [ 'bg-blue-500', 'bg-purple-500', 'bg-pink-500', @@ -29,7 +29,7 @@ export function getAvatarColor(agentId: string): string { return AVATAR_COLORS[total % AVATAR_COLORS.length] ?? 'bg-blue-500'; } -export function getDisplayName(agent: AgentCard): string { +function getDisplayName(agent: AgentCard): string { const username = agent['username'] as string | undefined; return username ?? agent.name ?? agent.agentId.slice(0, 8); } diff --git a/app/src/chat/promptInjectionGuard.ts b/app/src/chat/promptInjectionGuard.ts index eeeff4c8a..4f1757a9d 100644 --- a/app/src/chat/promptInjectionGuard.ts +++ b/app/src/chat/promptInjectionGuard.ts @@ -1,11 +1,11 @@ -export type PromptInjectionVerdict = 'allow' | 'block' | 'review'; +type PromptInjectionVerdict = 'allow' | 'block' | 'review'; -export interface PromptInjectionReason { +interface PromptInjectionReason { code: string; message: string; } -export interface PromptInjectionCheck { +interface PromptInjectionCheck { verdict: PromptInjectionVerdict; score: number; reasons: PromptInjectionReason[]; diff --git a/app/src/components/InitProgressScreen/InitProgressScreen.tsx b/app/src/components/InitProgressScreen/InitProgressScreen.tsx index 23f831916..14f3d8030 100644 --- a/app/src/components/InitProgressScreen/InitProgressScreen.tsx +++ b/app/src/components/InitProgressScreen/InitProgressScreen.tsx @@ -57,7 +57,7 @@ function StepRow({ step }: { step: HarnessInitStep }) { ); } -export interface InitProgressScreenProps { +interface InitProgressScreenProps { snapshot: HarnessInitSnapshot; onRetry: () => void; onContinue: () => void; diff --git a/app/src/components/LanguageSelect.tsx b/app/src/components/LanguageSelect.tsx index 4c4159cce..7c54d05f5 100644 --- a/app/src/components/LanguageSelect.tsx +++ b/app/src/components/LanguageSelect.tsx @@ -11,7 +11,7 @@ interface LocaleOption { label: string; } -export const LOCALE_OPTIONS: LocaleOption[] = [ +const LOCALE_OPTIONS: LocaleOption[] = [ { value: 'en', flag: 'đŸ‡Ŧ🇧', label: 'English' }, { value: 'ko', flag: '🇰🇷', label: '한ęĩ­ė–´' }, { value: 'zh-CN', flag: 'đŸ‡¨đŸ‡ŗ', label: 'įŽ€äŊ“中文' }, diff --git a/app/src/components/channels/mcp/McpInventoryManifest.ts b/app/src/components/channels/mcp/McpInventoryManifest.ts index 62a61f250..e7e59b66f 100644 --- a/app/src/components/channels/mcp/McpInventoryManifest.ts +++ b/app/src/components/channels/mcp/McpInventoryManifest.ts @@ -119,7 +119,7 @@ export type ParseErrorCode = * actual schema string we got, etc.). Consumers render via * `t(\`mcp.inventory.parseError.\${errorCode}\`)` + optional detail. */ -export type ParseResult = +type ParseResult = | { ok: true; manifest: McpInventoryManifest } | { ok: false; errorCode: ParseErrorCode; detail?: string }; diff --git a/app/src/components/channels/mcp/McpServersTab.tsx b/app/src/components/channels/mcp/McpServersTab.tsx index 428560dc9..aa3f97731 100644 --- a/app/src/components/channels/mcp/McpServersTab.tsx +++ b/app/src/components/channels/mcp/McpServersTab.tsx @@ -93,7 +93,7 @@ const transportOf = (server: SmitheryServer): Transport => * `io.gitlab./â€Ļ`), which maps 1:1 to a repo page. Returns `null` for * vendor reverse-DNS slugs that don't encode a code host. */ -export const deriveRepoUrl = (qualifiedName: string): string | null => { +const deriveRepoUrl = (qualifiedName: string): string | null => { const slash = qualifiedName.indexOf('/'); if (slash < 1) return null; const prefix = qualifiedName.slice(0, slash); diff --git a/app/src/components/channels/mcp/McpToolPlayground.tsx b/app/src/components/channels/mcp/McpToolPlayground.tsx index c19a429cc..d528491e7 100644 --- a/app/src/components/channels/mcp/McpToolPlayground.tsx +++ b/app/src/components/channels/mcp/McpToolPlayground.tsx @@ -79,7 +79,7 @@ const formatArgs = (raw: string): string => { * caller can keep JSON-parse failures (user input) cleanly separate from RPC * failures (the actual tool call) — they surface to the user differently. */ -export type ParsedToolArgs = { ok: true; value: unknown } | { ok: false; error: string }; +type ParsedToolArgs = { ok: true; value: unknown } | { ok: false; error: string }; export const parseToolArgs = (argsJson: string, fallbackMessage: string): ParsedToolArgs => { if (argsJson.trim() === '') return { ok: true, value: {} }; diff --git a/app/src/components/chat/ApprovalRequestCard.tsx b/app/src/components/chat/ApprovalRequestCard.tsx index bdd856f70..221f1df93 100644 --- a/app/src/components/chat/ApprovalRequestCard.tsx +++ b/app/src/components/chat/ApprovalRequestCard.tsx @@ -31,7 +31,7 @@ interface Props { * thread; clears itself on a recorded decision (the turn-end handlers in * {@link ChatRuntimeProvider} also clear it if the turn is cancelled). */ -export const ApprovalRequestCard: React.FC = ({ threadId, approval }) => { +const ApprovalRequestCard: React.FC = ({ threadId, approval }) => { const { t } = useT(); const dispatch = useAppDispatch(); const [deciding, setDeciding] = useState(null); diff --git a/app/src/components/chat/ArtifactCard.tsx b/app/src/components/chat/ArtifactCard.tsx index d5ff4b016..4082bb544 100644 --- a/app/src/components/chat/ArtifactCard.tsx +++ b/app/src/components/chat/ArtifactCard.tsx @@ -28,7 +28,7 @@ import { extensionFor } from './artifactExtension'; * rounded card, dark/light Tailwind variants, mono accents on * numeric values, inline SVG icons. No new icon dependency. */ -export interface ArtifactCardProps { +interface ArtifactCardProps { artifact: ArtifactSnapshot; /** When provided, render a Retry button on the `failed` state. */ onRetry?: (artifactId: string) => void; diff --git a/app/src/components/chat/ChatFilesChip.tsx b/app/src/components/chat/ChatFilesChip.tsx index f3a226daf..0049a1f6c 100644 --- a/app/src/components/chat/ChatFilesChip.tsx +++ b/app/src/components/chat/ChatFilesChip.tsx @@ -18,7 +18,7 @@ import ChatFilesPanel from './ChatFilesPanel'; * survive app restarts via the `artifactsReadyOnlyTransform` configured in * `store/index.ts`. */ -export interface ChatFilesChipProps { +interface ChatFilesChipProps { threadId: string; } diff --git a/app/src/components/chat/ChatFilesPanel.tsx b/app/src/components/chat/ChatFilesPanel.tsx index 0bed3469d..851f0cc54 100644 --- a/app/src/components/chat/ChatFilesPanel.tsx +++ b/app/src/components/chat/ChatFilesPanel.tsx @@ -32,7 +32,7 @@ import { extensionFor } from './artifactExtension'; * state only shows if the user deletes the last artifact while the * panel is open). */ -export interface ChatFilesPanelProps { +interface ChatFilesPanelProps { threadId: string; artifacts: ArtifactSnapshot[]; onClose: () => void; diff --git a/app/src/components/chat/FlowApprovalRequestCard.tsx b/app/src/components/chat/FlowApprovalRequestCard.tsx index 337f0d83c..ce9086ffd 100644 --- a/app/src/components/chat/FlowApprovalRequestCard.tsx +++ b/app/src/components/chat/FlowApprovalRequestCard.tsx @@ -115,5 +115,3 @@ export const FlowApprovalRequestCard: React.FC = ({ request, onResolved } ); }; - -export default FlowApprovalRequestCard; diff --git a/app/src/components/chat/IntegrationConnectCard.tsx b/app/src/components/chat/IntegrationConnectCard.tsx index 41063a205..c0d3c009c 100644 --- a/app/src/components/chat/IntegrationConnectCard.tsx +++ b/app/src/components/chat/IntegrationConnectCard.tsx @@ -59,7 +59,7 @@ function errorText(e: unknown): string { return e instanceof Error ? e.message : String(e); } -export const IntegrationConnectCard: React.FC = ({ threadId, approval }) => { +const IntegrationConnectCard: React.FC = ({ threadId, approval }) => { const { t } = useT(); const dispatch = useAppDispatch(); // Canonicalize the slug the agent supplied (e.g. `google_drive` → diff --git a/app/src/components/chat/QueuedFollowups.tsx b/app/src/components/chat/QueuedFollowups.tsx index 39f59efd6..0aeced272 100644 --- a/app/src/components/chat/QueuedFollowups.tsx +++ b/app/src/components/chat/QueuedFollowups.tsx @@ -1,7 +1,7 @@ import { useT } from '../../lib/i18n/I18nContext'; import type { QueuedFollowup } from '../../store/chatRuntimeSlice'; -export interface QueuedFollowupsProps { +interface QueuedFollowupsProps { /** Follow-ups queued for the current thread while a turn is streaming. */ items: QueuedFollowup[]; /** Dismiss every queued follow-up (clears the backend run-queue too). */ diff --git a/app/src/components/chat/WorkflowProposalCard.test.tsx b/app/src/components/chat/WorkflowProposalCard.test.tsx index a6d6920ec..8c8e09b4a 100644 --- a/app/src/components/chat/WorkflowProposalCard.test.tsx +++ b/app/src/components/chat/WorkflowProposalCard.test.tsx @@ -2,7 +2,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { markWorkflowProposalCompleted, type WorkflowProposal } from '../../store/chatRuntimeSlice'; -import { WorkflowProposalCard } from './WorkflowProposalCard'; +import WorkflowProposalCard from './WorkflowProposalCard'; // Echo i18n keys so we can assert on the stable key string. vi.mock('../../lib/i18n/I18nContext', () => ({ useT: () => ({ t: (key: string) => key }) })); diff --git a/app/src/components/chat/WorkflowProposalCard.tsx b/app/src/components/chat/WorkflowProposalCard.tsx index f079f676f..1e9897216 100644 --- a/app/src/components/chat/WorkflowProposalCard.tsx +++ b/app/src/components/chat/WorkflowProposalCard.tsx @@ -106,7 +106,7 @@ interface Props { * the tool-timeline `StatusTag`/detail-chip visual language for the * node-kind badges + config hints in the step list. */ -export const WorkflowProposalCard: React.FC = ({ threadId, proposal, onSaved }) => { +const WorkflowProposalCard: React.FC = ({ threadId, proposal, onSaved }) => { const { t } = useT(); const dispatch = useAppDispatch(); const navigate = useNavigate(); diff --git a/app/src/components/chat/__tests__/FlowApprovalRequestCard.test.tsx b/app/src/components/chat/__tests__/FlowApprovalRequestCard.test.tsx index 4a91ad5a1..6aee20897 100644 --- a/app/src/components/chat/__tests__/FlowApprovalRequestCard.test.tsx +++ b/app/src/components/chat/__tests__/FlowApprovalRequestCard.test.tsx @@ -8,7 +8,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import type { FlowApprovalRequest } from '../../../hooks/useFlowApprovalRequests'; import { decideApproval } from '../../../services/api/approvalApi'; -import FlowApprovalRequestCard from '../FlowApprovalRequestCard'; +import { FlowApprovalRequestCard } from '../FlowApprovalRequestCard'; vi.mock('../../../services/api/approvalApi', () => ({ decideApproval: vi.fn() })); diff --git a/app/src/components/composio/TriggerToggles.tsx b/app/src/components/composio/TriggerToggles.tsx index 15a7fafe7..31659e495 100644 --- a/app/src/components/composio/TriggerToggles.tsx +++ b/app/src/components/composio/TriggerToggles.tsx @@ -40,7 +40,7 @@ export function activeTriggerSignature(t: ComposioActiveTrigger): string { return t.slug.toUpperCase(); } -export interface TriggerTogglesProps { +interface TriggerTogglesProps { toolkitSlug: string; toolkitName: string; connectionId: string; diff --git a/app/src/components/dashboard/BudgetSummary.tsx b/app/src/components/dashboard/BudgetSummary.tsx index 2399d8102..d76e4cebb 100644 --- a/app/src/components/dashboard/BudgetSummary.tsx +++ b/app/src/components/dashboard/BudgetSummary.tsx @@ -4,7 +4,7 @@ import type { BudgetStatus } from '../../hooks/useCostDashboard'; import { useT } from '../../lib/i18n/I18nContext'; import { formatCurrency } from './formatCurrency'; -export interface BudgetSummaryProps { +interface BudgetSummaryProps { currency: string; periodTotalUsd: number; monthlyPaceUsd: number; diff --git a/app/src/components/dashboard/ChartTooltip.tsx b/app/src/components/dashboard/ChartTooltip.tsx index 05466e6a4..e6f01214f 100644 --- a/app/src/components/dashboard/ChartTooltip.tsx +++ b/app/src/components/dashboard/ChartTooltip.tsx @@ -1,13 +1,13 @@ import type { ReactNode } from 'react'; -export interface ChartTooltipRow { +interface ChartTooltipRow { label: string; value: string; /** CSS colour for the legend swatch. */ color?: string; } -export interface ChartTooltipProps { +interface ChartTooltipProps { title: string; rows: ChartTooltipRow[]; footer?: ReactNode; diff --git a/app/src/components/dashboard/CostBarChart.tsx b/app/src/components/dashboard/CostBarChart.tsx index a75f9f7fc..de58fe1c7 100644 --- a/app/src/components/dashboard/CostBarChart.tsx +++ b/app/src/components/dashboard/CostBarChart.tsx @@ -15,7 +15,7 @@ import { useT } from '../../lib/i18n/I18nContext'; import ChartTooltip from './ChartTooltip'; import { dayOfMonth, formatCurrency, longDateLabel, shortDayLabel } from './formatCurrency'; -export interface CostBarChartProps { +interface CostBarChartProps { days: CostDashboardDay[]; currency: string; /** Monthly budget in USD; used to derive a daily target. */ diff --git a/app/src/components/dashboard/ModelCostTable.tsx b/app/src/components/dashboard/ModelCostTable.tsx index 844cc285e..6ec396945 100644 --- a/app/src/components/dashboard/ModelCostTable.tsx +++ b/app/src/components/dashboard/ModelCostTable.tsx @@ -2,7 +2,7 @@ import type { CostDashboardModelStats } from '../../hooks/useCostDashboard'; import { useT } from '../../lib/i18n/I18nContext'; import { formatCurrency, formatTokens } from './formatCurrency'; -export interface ModelCostTableProps { +interface ModelCostTableProps { models: CostDashboardModelStats[]; currency: string; } diff --git a/app/src/components/dashboard/TokenUsageChart.tsx b/app/src/components/dashboard/TokenUsageChart.tsx index 46a3d0a77..b77486a7c 100644 --- a/app/src/components/dashboard/TokenUsageChart.tsx +++ b/app/src/components/dashboard/TokenUsageChart.tsx @@ -5,7 +5,7 @@ import { useT } from '../../lib/i18n/I18nContext'; import ChartTooltip from './ChartTooltip'; import { dayOfMonth, formatTokens, longDateLabel, shortDayLabel } from './formatCurrency'; -export interface TokenUsageChartProps { +interface TokenUsageChartProps { days: CostDashboardDay[]; } diff --git a/app/src/components/dashboard/formatCurrency.ts b/app/src/components/dashboard/formatCurrency.ts index d8c37cc39..3aa711d0e 100644 --- a/app/src/components/dashboard/formatCurrency.ts +++ b/app/src/components/dashboard/formatCurrency.ts @@ -75,7 +75,7 @@ export function dayOfMonth(isoDate: string): string { * is invoked with a string key and is expected to return the localised * value (or the key itself when no translation is available). */ -export type RelativeTimeTranslator = (key: string, fallback?: string) => string; +type RelativeTimeTranslator = (key: string, fallback?: string) => string; /** * Human-friendly relative-time string for the "updated Ns ago" pill. diff --git a/app/src/components/feedback/FeedbackFilterSelect.tsx b/app/src/components/feedback/FeedbackFilterSelect.tsx index 8a0584692..9cd3ad4c4 100644 --- a/app/src/components/feedback/FeedbackFilterSelect.tsx +++ b/app/src/components/feedback/FeedbackFilterSelect.tsx @@ -1,6 +1,6 @@ import { useEffect, useId, useRef, useState } from 'react'; -export interface FilterOption { +interface FilterOption { value: string; label: string; } diff --git a/app/src/components/flows/FlowRowMenu.tsx b/app/src/components/flows/FlowRowMenu.tsx index 3a38a851e..d09e406c2 100644 --- a/app/src/components/flows/FlowRowMenu.tsx +++ b/app/src/components/flows/FlowRowMenu.tsx @@ -18,7 +18,7 @@ import { createPortal } from 'react-dom'; import { useEscapeKey } from '../../hooks/useEscapeKey'; import { useT } from '../../lib/i18n/I18nContext'; -export interface FlowRowMenuItem { +interface FlowRowMenuItem { key: string; label: string; onSelect: () => void; @@ -27,7 +27,7 @@ export interface FlowRowMenuItem { testId?: string; } -export interface FlowRowMenuProps { +interface FlowRowMenuProps { items: FlowRowMenuItem[]; /** Suffixed onto test ids so multiple rows stay addressable. */ rowId: string; diff --git a/app/src/components/flows/FlowRunInspectorDrawer.tsx b/app/src/components/flows/FlowRunInspectorDrawer.tsx index afca7a53c..e554507df 100644 --- a/app/src/components/flows/FlowRunInspectorDrawer.tsx +++ b/app/src/components/flows/FlowRunInspectorDrawer.tsx @@ -485,5 +485,3 @@ export function FlowRunInspectorDrawer({ runId, onClose, onFixWithAgent }: Props ); } - -export default FlowRunInspectorDrawer; diff --git a/app/src/components/flows/FlowRunPendingApprovalCard.tsx b/app/src/components/flows/FlowRunPendingApprovalCard.tsx index 6cb5548ff..5f5576891 100644 --- a/app/src/components/flows/FlowRunPendingApprovalCard.tsx +++ b/app/src/components/flows/FlowRunPendingApprovalCard.tsx @@ -96,5 +96,3 @@ export function FlowRunPendingApprovalCard({ approval, deciding, onDecide }: Pro ); } - -export default FlowRunPendingApprovalCard; diff --git a/app/src/components/flows/FlowRunsDrawer.test.tsx b/app/src/components/flows/FlowRunsDrawer.test.tsx index 74c05d63f..dc6a8584e 100644 --- a/app/src/components/flows/FlowRunsDrawer.test.tsx +++ b/app/src/components/flows/FlowRunsDrawer.test.tsx @@ -18,7 +18,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import type { FlowRun } from '../../services/api/flowsApi'; import { store } from '../../store'; -import { FlowRunsDrawer } from './FlowRunsDrawer'; +import FlowRunsDrawer from './FlowRunsDrawer'; const listFlowRuns = vi.hoisted(() => vi.fn()); vi.mock('../../services/api/flowsApi', () => ({ listFlowRuns })); diff --git a/app/src/components/flows/FlowRunsDrawer.tsx b/app/src/components/flows/FlowRunsDrawer.tsx index 17b95a63a..1ae6f76e8 100644 --- a/app/src/components/flows/FlowRunsDrawer.tsx +++ b/app/src/components/flows/FlowRunsDrawer.tsx @@ -74,7 +74,7 @@ interface Props { * unconditionally and just flip `flowId` (same convention as * `FlowRunInspectorDrawer`/`SubagentDrawer`). */ -export function FlowRunsDrawer({ flowId, flowName, onClose, onFixWithAgent }: Props) { +function FlowRunsDrawer({ flowId, flowName, onClose, onFixWithAgent }: Props) { const { t } = useT(); const [runs, setRuns] = useState([]); const [loading, setLoading] = useState(false); diff --git a/app/src/components/flows/FlowRunsSidebar.tsx b/app/src/components/flows/FlowRunsSidebar.tsx index 3645f54fe..1a15ebb58 100644 --- a/app/src/components/flows/FlowRunsSidebar.tsx +++ b/app/src/components/flows/FlowRunsSidebar.tsx @@ -48,7 +48,7 @@ function relativeTime(iso: string, t: TFn): string { const log = createDebug('app:flows:runs-sidebar'); -export interface FlowRunsSidebarProps { +interface FlowRunsSidebarProps { flowId: string; } diff --git a/app/src/components/flows/RunItemDataBrowser.tsx b/app/src/components/flows/RunItemDataBrowser.tsx index ac6d9c8c0..7e4265bc3 100644 --- a/app/src/components/flows/RunItemDataBrowser.tsx +++ b/app/src/components/flows/RunItemDataBrowser.tsx @@ -317,5 +317,3 @@ function FragmentRow({ ); } - -export default RunItemDataBrowser; diff --git a/app/src/components/flows/canvas/AgentNodeInspector.tsx b/app/src/components/flows/canvas/AgentNodeInspector.tsx index 0c60ad4cb..52666061f 100644 --- a/app/src/components/flows/canvas/AgentNodeInspector.tsx +++ b/app/src/components/flows/canvas/AgentNodeInspector.tsx @@ -37,12 +37,12 @@ const log = createDebug('app:flows:canvas:agentInspector'); * slugs `OpenHumanAgentRunner`/`resolve_model_for_hint` accept as bare tier * names — so the value written here runs unchanged in the flow engine. */ -export const AGENT_MANAGED_TIERS = ['reasoning-v1', 'chat-v1', 'agentic-v1', 'burst-v1'] as const; +const AGENT_MANAGED_TIERS = ['reasoning-v1', 'chat-v1', 'agentic-v1', 'burst-v1'] as const; /** Sentinel select value for "type a raw model id" — never persisted. */ const CUSTOM_MODEL = '__custom__'; -export interface AgentNodeInspectorProps { +interface AgentNodeInspectorProps { /** The agent node's controlled config object. */ config: Record; /** Shallow-merge patch into the node's config. */ diff --git a/app/src/components/flows/canvas/EditableFlowCanvas.tsx b/app/src/components/flows/canvas/EditableFlowCanvas.tsx index 05855c456..fe74b7831 100644 --- a/app/src/components/flows/canvas/EditableFlowCanvas.tsx +++ b/app/src/components/flows/canvas/EditableFlowCanvas.tsx @@ -117,7 +117,7 @@ const CLICK_ADD_ORIGIN = { x: 80, y: 80 }; /** Per-click cascade so repeated palette clicks don't stack on one spot. */ const CLICK_ADD_STEP = 32; -export interface EditableFlowCanvasProps { +interface EditableFlowCanvasProps { nodes: FlowNode[]; edges: FlowEdge[]; /** Graph-level metadata xyflow doesn't carry, needed to re-serialize on save. */ diff --git a/app/src/components/flows/canvas/FlowCanvas.tsx b/app/src/components/flows/canvas/FlowCanvas.tsx index baf645840..6dbe6c984 100644 --- a/app/src/components/flows/canvas/FlowCanvas.tsx +++ b/app/src/components/flows/canvas/FlowCanvas.tsx @@ -37,7 +37,7 @@ import EditableFlowCanvas, { import './flowCanvasStyles.css'; import FlowNodeComponent from './FlowNodeComponent'; -export interface FlowCanvasProps { +interface FlowCanvasProps { nodes: FlowNode[]; edges: FlowEdge[]; /** diff --git a/app/src/components/flows/canvas/FlowValidationBanner.tsx b/app/src/components/flows/canvas/FlowValidationBanner.tsx index db3062d9a..c50fb8410 100644 --- a/app/src/components/flows/canvas/FlowValidationBanner.tsx +++ b/app/src/components/flows/canvas/FlowValidationBanner.tsx @@ -20,7 +20,7 @@ import { memo } from 'react'; import { useT } from '../../../lib/i18n/I18nContext'; import type { FlowValidation } from '../../../services/api/flowsApi'; -export interface FlowValidationBannerProps { +interface FlowValidationBannerProps { validation: FlowValidation | null; /** Message from a failed `flows_update` Save, shown as a distinct error row. */ saveError?: string | null; diff --git a/app/src/components/flows/canvas/NodePalette.tsx b/app/src/components/flows/canvas/NodePalette.tsx index aef6882a7..525fa4588 100644 --- a/app/src/components/flows/canvas/NodePalette.tsx +++ b/app/src/components/flows/canvas/NodePalette.tsx @@ -23,7 +23,7 @@ import { useT } from '../../../lib/i18n/I18nContext'; /** dataTransfer MIME key for a palette drag — read by the canvas `onDrop`. */ export const PALETTE_DND_MIME = 'application/tinyflows-node'; -export interface NodePaletteProps { +interface NodePaletteProps { /** Add a node from the given palette entry at the canvas's default position. */ onAdd: (entry: PaletteEntry) => void; } diff --git a/app/src/components/flows/canvas/nodeConfig/NodeConfigDrawer.tsx b/app/src/components/flows/canvas/nodeConfig/NodeConfigDrawer.tsx index 228b5b81f..73519ea4d 100644 --- a/app/src/components/flows/canvas/nodeConfig/NodeConfigDrawer.tsx +++ b/app/src/components/flows/canvas/nodeConfig/NodeConfigDrawer.tsx @@ -36,7 +36,7 @@ export interface NodeConfigPatch { config?: Record; } -export interface NodeConfigDrawerProps { +interface NodeConfigDrawerProps { /** The selected node to edit, or `null` when nothing single-node is selected. */ node: FlowNode | null; onClose: () => void; diff --git a/app/src/components/flows/canvas/nodeConfig/NodeConnections.tsx b/app/src/components/flows/canvas/nodeConfig/NodeConnections.tsx index 7179635bb..8629c5b27 100644 --- a/app/src/components/flows/canvas/nodeConfig/NodeConnections.tsx +++ b/app/src/components/flows/canvas/nodeConfig/NodeConnections.tsx @@ -12,7 +12,7 @@ import { useMemo } from 'react'; import type { FlowEdge } from '../../../../lib/flows/graphAdapter'; import { useT } from '../../../../lib/i18n/I18nContext'; -export interface NodeConnectionsProps { +interface NodeConnectionsProps { nodeId: string; edges: FlowEdge[]; nodeLabelById: Record; diff --git a/app/src/components/flows/canvas/nodeConfig/ScheduleField.tsx b/app/src/components/flows/canvas/nodeConfig/ScheduleField.tsx index eba5c9c01..572a34a97 100644 --- a/app/src/components/flows/canvas/nodeConfig/ScheduleField.tsx +++ b/app/src/components/flows/canvas/nodeConfig/ScheduleField.tsx @@ -28,7 +28,7 @@ import { import { useT } from '../../../../lib/i18n/I18nContext'; import { Field, INPUT_CLASS, MONO_CLASS } from './nodeConfigFields'; -export interface ScheduleFieldProps { +interface ScheduleFieldProps { /** The cron string stored on `config.schedule`. */ value: string; onChange: (value: string) => void; diff --git a/app/src/components/flows/canvas/nodeConfig/composioFields.tsx b/app/src/components/flows/canvas/nodeConfig/composioFields.tsx index ce1e98195..65a8f87b6 100644 --- a/app/src/components/flows/canvas/nodeConfig/composioFields.tsx +++ b/app/src/components/flows/canvas/nodeConfig/composioFields.tsx @@ -33,7 +33,7 @@ function toolkitLabel(slug: string): string { } /** Distinct connected Composio toolkits from the canvas's loaded connections. */ -export function connectedToolkits(connections: FlowConnection[]): string[] { +function connectedToolkits(connections: FlowConnection[]): string[] { const seen = new Set(); for (const c of connections) { if (c.kind === 'composio' && c.toolkit) seen.add(c.toolkit); @@ -43,7 +43,7 @@ export function connectedToolkits(connections: FlowConnection[]): string[] { // ── toolkit (app) picker ───────────────────────────────────────────────────── -export interface ComposioToolkitFieldProps { +interface ComposioToolkitFieldProps { label: string; hint?: string; value: string; diff --git a/app/src/components/flows/canvas/nodeConfig/nativeToolFields.tsx b/app/src/components/flows/canvas/nodeConfig/nativeToolFields.tsx index 504e09d78..b27026f57 100644 --- a/app/src/components/flows/canvas/nodeConfig/nativeToolFields.tsx +++ b/app/src/components/flows/canvas/nodeConfig/nativeToolFields.tsx @@ -18,7 +18,7 @@ import { Field, INPUT_CLASS, MONO_CLASS } from './nodeConfigFields'; /** The `oh:` prefix that marks a native-tool slug (mirrors the Rust constant). */ export const NATIVE_TOOL_PREFIX = 'oh:'; -export interface NativeToolFieldProps { +interface NativeToolFieldProps { label: string; hint?: string; /** The full `config.slug`, e.g. `oh:web_search` (or empty / `oh:`). */ diff --git a/app/src/components/flows/canvas/nodeConfig/nodeConfigFields.tsx b/app/src/components/flows/canvas/nodeConfig/nodeConfigFields.tsx index daba64101..e557fa43d 100644 --- a/app/src/components/flows/canvas/nodeConfig/nodeConfigFields.tsx +++ b/app/src/components/flows/canvas/nodeConfig/nodeConfigFields.tsx @@ -75,7 +75,7 @@ export function Field({ ); } -export interface TextFieldProps { +interface TextFieldProps { label: string; hint?: string; value: string; @@ -101,7 +101,7 @@ export function TextField({ label, hint, value, onChange, placeholder, testId }: ); } -export interface TextAreaFieldProps extends Omit { +interface TextAreaFieldProps extends Omit { onChange: (value: string) => void; rows?: number; mono?: boolean; @@ -133,12 +133,12 @@ export function TextAreaField({ ); } -export interface SelectOption { +interface SelectOption { value: string; label: string; } -export interface SelectFieldProps { +interface SelectFieldProps { label: string; hint?: string; value: string; @@ -167,96 +167,6 @@ export function SelectField({ label, hint, value, onChange, options, testId }: S ); } -/** - * Canonical model route hints (mirrors `AgentEditorPage`'s list, which in turn - * mirrors the Rust `ModelSpec::Hint(...)` slugs). Selecting one routes the - * agent node by capability tier; the workspace resolves the concrete model. - */ -export const AGENT_MODEL_HINTS = [ - 'hint:reasoning', - 'hint:chat', - 'hint:agentic', - 'hint:burst', - 'hint:coding', - 'hint:summarization', - 'hint:vision', -] as const; - -/** Sentinel select value for "type a raw model id" — never persisted. */ -const CUSTOM_MODEL = '__custom__'; - -export interface ModelHintFieldProps { - label: string; - hint?: string; - value: string; - onChange: (value: string) => void; - testId?: string; -} - -/** - * Model selector for the `agent` node: a dropdown of the workspace's model - * route hints (`hint:chat`, `hint:coding`, â€Ļ) with an "inherit" default and a - * custom escape hatch for a raw BYOK model id. Writes `hint:` (or the raw - * id, or `''` to inherit) onto `config.model`. Mirrors the agent-editor model - * picker so hints stay consistent across the app. - */ -export function ModelHintField({ label, hint, value, onChange, testId }: ModelHintFieldProps) { - const { t } = useT(); - const id = useId(); - // A value that's neither empty nor a known hint is a raw custom model id, so - // the picker opens in custom mode showing it in the text box. - const isKnown = value === '' || (AGENT_MODEL_HINTS as readonly string[]).includes(value); - const [customMode, setCustomMode] = useState(value !== '' && !isKnown); - - const handleSelect = useCallback( - (next: string) => { - if (next === CUSTOM_MODEL) { - setCustomMode(true); - // Entering custom from a hint/inherit starts with an empty raw id. - if (isKnown) onChange(''); - return; - } - setCustomMode(false); - onChange(next); - }, - [isKnown, onChange] - ); - - return ( - -
- - {customMode && ( - onChange(e.target.value)} - /> - )} -
-
- ); -} - /** * Compact "insert an upstream value" dropdown (Feature: `nodes` scope picker). * Always renders with the empty placeholder selected; picking an option calls @@ -304,7 +214,7 @@ export function UpstreamInsertSelect({ ); } -export interface ExpressionFieldProps extends TextFieldProps { +interface ExpressionFieldProps extends TextFieldProps { /** * `=nodes.â€Ļ` expressions from upstream nodes; when non-empty a compact * insert dropdown renders beside the input (picking replaces the value). @@ -370,7 +280,7 @@ export function ExpressionField({ ); } -export interface KeyMapFieldProps { +interface KeyMapFieldProps { label: string; hint?: string; value: Record; @@ -473,7 +383,7 @@ export function KeyMapField({ ); } -export interface JsonFieldProps { +interface JsonFieldProps { label: string; hint?: string; value: unknown; @@ -545,7 +455,7 @@ export function JsonField({ label, hint, value, onChange, rows = 6, testId }: Js ); } -export interface CredentialPickerFieldProps { +interface CredentialPickerFieldProps { label?: string; value: string; onChange: (value: string) => void; diff --git a/app/src/components/flows/canvas/nodeConfig/nodeConfigForms.tsx b/app/src/components/flows/canvas/nodeConfig/nodeConfigForms.tsx index ad620cb92..39e681a7f 100644 --- a/app/src/components/flows/canvas/nodeConfig/nodeConfigForms.tsx +++ b/app/src/components/flows/canvas/nodeConfig/nodeConfigForms.tsx @@ -67,7 +67,7 @@ export interface NodeConfigFormProps { upstreamOptions?: UpstreamExpressionOption[]; } -export type NodeConfigForm = (props: NodeConfigFormProps) => React.ReactElement; +type NodeConfigForm = (props: NodeConfigFormProps) => React.ReactElement; // ── trigger ──────────────────────────────────────────────────────────────── diff --git a/app/src/components/flows/canvas/useFlowValidation.ts b/app/src/components/flows/canvas/useFlowValidation.ts index b037b5e8d..efaa101f1 100644 --- a/app/src/components/flows/canvas/useFlowValidation.ts +++ b/app/src/components/flows/canvas/useFlowValidation.ts @@ -23,9 +23,9 @@ import { type FlowValidation, validateFlow } from '../../../services/api/flowsAp const log = createDebug('app:flows:canvas:validate'); /** Idle delay after the last edit before auto-validating the draft. */ -export const VALIDATION_DEBOUNCE_MS = 500; +const VALIDATION_DEBOUNCE_MS = 500; -export interface FlowValidationState { +interface FlowValidationState { /** The most recent successful validation result, or `null` before the first. */ validation: FlowValidation | null; /** True while a validation RPC is in flight (debounced or manual). */ @@ -105,5 +105,3 @@ export function useFlowValidation( return { validation, validating, validateNow: run }; } - -export default useFlowValidation; diff --git a/app/src/components/flows/useCreateFlow.ts b/app/src/components/flows/useCreateFlow.ts index 78c1da33b..1a1b11b0d 100644 --- a/app/src/components/flows/useCreateFlow.ts +++ b/app/src/components/flows/useCreateFlow.ts @@ -25,7 +25,7 @@ const log = createDebug('app:flows:create'); /** Sentinel `busyKey` for the "start from scratch" path (not a template id). */ export const BLANK_FLOW_KEY = 'blank'; -export interface UseCreateFlow { +interface UseCreateFlow { /** Persist `graph` under `name`, then navigate into its canvas. `key` tags the busy affordance. */ create: (key: string, name: string, graph: WorkflowGraph) => Promise; /** The `key` of the create currently in flight, or `null`. */ diff --git a/app/src/components/intelligence/AttentionQueue.tsx b/app/src/components/intelligence/AttentionQueue.tsx index 4be0717c8..bd310b015 100644 --- a/app/src/components/intelligence/AttentionQueue.tsx +++ b/app/src/components/intelligence/AttentionQueue.tsx @@ -12,7 +12,7 @@ import { useT } from '../../lib/i18n/I18nContext'; import type { AttentionAction, AttentionQueue } from '../../lib/orchestration/orchestrationClient'; import AttentionQueueItem from './AttentionQueueItem'; -export interface AttentionQueueProps { +interface AttentionQueueProps { queue: AttentionQueue | null; loading?: boolean; onAction?: (action: AttentionAction) => void; diff --git a/app/src/components/intelligence/AttentionQueueItem.tsx b/app/src/components/intelligence/AttentionQueueItem.tsx index a792aea87..c16ca5979 100644 --- a/app/src/components/intelligence/AttentionQueueItem.tsx +++ b/app/src/components/intelligence/AttentionQueueItem.tsx @@ -17,7 +17,7 @@ import type { AttentionKind, } from '../../lib/orchestration/orchestrationClient'; -export interface AttentionQueueItemProps { +interface AttentionQueueItemProps { item: AttentionItem; onAction?: (action: AttentionAction) => void; } diff --git a/app/src/components/intelligence/HarnessGlyph.tsx b/app/src/components/intelligence/HarnessGlyph.tsx index 3d819651f..db37ba4e7 100644 --- a/app/src/components/intelligence/HarnessGlyph.tsx +++ b/app/src/components/intelligence/HarnessGlyph.tsx @@ -11,7 +11,7 @@ import type { HarnessType } from '../../lib/orchestration/orchestrationClient'; export type GlyphKind = HarnessType | 'openhuman'; -export interface HarnessGlyphProps { +interface HarnessGlyphProps { harness: GlyphKind; className?: string; } diff --git a/app/src/components/intelligence/InstanceCard.tsx b/app/src/components/intelligence/InstanceCard.tsx index 9210b1c5d..d7dafd913 100644 --- a/app/src/components/intelligence/InstanceCard.tsx +++ b/app/src/components/intelligence/InstanceCard.tsx @@ -10,7 +10,7 @@ import type { InstanceStatus, SessionSummary } from '../../lib/orchestration/orc import HarnessGlyph, { type GlyphKind } from './HarnessGlyph'; import InstanceStatusDot from './InstanceStatusDot'; -export interface InstanceCardProps { +interface InstanceCardProps { session: SessionSummary; selected?: boolean; onSelect?: () => void; diff --git a/app/src/components/intelligence/InstanceStatusDot.tsx b/app/src/components/intelligence/InstanceStatusDot.tsx index 14f806e31..fe6f9fbf5 100644 --- a/app/src/components/intelligence/InstanceStatusDot.tsx +++ b/app/src/components/intelligence/InstanceStatusDot.tsx @@ -11,7 +11,7 @@ */ import type { InstanceStatus } from '../../lib/orchestration/orchestrationClient'; -export interface InstanceStatusDotProps { +interface InstanceStatusDotProps { status: InstanceStatus; /** Accessible label (already translated by the caller). */ label?: string; diff --git a/app/src/components/intelligence/MemorySourcesRegistry.tsx b/app/src/components/intelligence/MemorySourcesRegistry.tsx index cd2025148..311c5956a 100644 --- a/app/src/components/intelligence/MemorySourcesRegistry.tsx +++ b/app/src/components/intelligence/MemorySourcesRegistry.tsx @@ -82,7 +82,7 @@ interface SyncResult { * Per-stage fallback percentages so the progress bar always advances even * when no numeric "N/M" ratio is present in the detail string (RC#4, #3295). */ -export const STAGE_FALLBACK_PERCENT: Record = { +const STAGE_FALLBACK_PERCENT: Record = { requested: 2, fetching: 5, stored: 15, diff --git a/app/src/components/intelligence/MemoryTreeStatusPanel.tsx b/app/src/components/intelligence/MemoryTreeStatusPanel.tsx index 7232a7396..1eb01afe5 100644 --- a/app/src/components/intelligence/MemoryTreeStatusPanel.tsx +++ b/app/src/components/intelligence/MemoryTreeStatusPanel.tsx @@ -205,7 +205,7 @@ function statusDotClass(kind: MemoryTreePipelineStatus['status']): string { * current data; per-provider failure attribution needs new core work and * is filed as a follow-up to issue #2763. */ -export type IntegrationHealth = 'active' | 'stale'; +type IntegrationHealth = 'active' | 'stale'; /** Map the wire `freshness` enum to the two-state UI classification. */ export function classifyIntegration( @@ -551,7 +551,3 @@ export function MemoryTreeStatusPanel({ onToast }: MemoryTreeStatusPanelProps) { ); } - -// Re-export the hook so unit tests can opt into the polling subscription -// directly without re-implementing it. -export { useMemoryTreeStatus }; diff --git a/app/src/components/intelligence/OrchestrationChatPrimitives.tsx b/app/src/components/intelligence/OrchestrationChatPrimitives.tsx index ea3e7270d..c0bef1e03 100644 --- a/app/src/components/intelligence/OrchestrationChatPrimitives.tsx +++ b/app/src/components/intelligence/OrchestrationChatPrimitives.tsx @@ -10,7 +10,7 @@ import { useT } from '../../lib/i18n/I18nContext'; import type { ChatMessage, ChatWindow } from '../../lib/orchestration/useOrchestrationChats'; import { formatTime } from './orchestrationTabHelpers'; -export interface ChatListButtonProps { +interface ChatListButtonProps { chat: ChatWindow; selected: boolean; onSelect: () => void; diff --git a/app/src/components/intelligence/RelayBadge.tsx b/app/src/components/intelligence/RelayBadge.tsx index 2ee82ba89..754b18f23 100644 --- a/app/src/components/intelligence/RelayBadge.tsx +++ b/app/src/components/intelligence/RelayBadge.tsx @@ -13,7 +13,7 @@ import { useT } from '../../lib/i18n/I18nContext'; import type { RelayInfo } from '../../lib/orchestration/orchestrationClient'; -export interface RelayBadgeProps { +interface RelayBadgeProps { relay: RelayInfo | null; } diff --git a/app/src/components/intelligence/SelfIdentityCard.tsx b/app/src/components/intelligence/SelfIdentityCard.tsx index 8a824d1a7..61a74f642 100644 --- a/app/src/components/intelligence/SelfIdentityCard.tsx +++ b/app/src/components/intelligence/SelfIdentityCard.tsx @@ -17,7 +17,7 @@ import { useCallback, useState } from 'react'; import { useT } from '../../lib/i18n/I18nContext'; import type { SelfIdentity } from '../../lib/orchestration/orchestrationClient'; -export interface SelfIdentityCardProps { +interface SelfIdentityCardProps { identity: SelfIdentity | null; loading: boolean; /** diff --git a/app/src/components/intelligence/TinyPlaceRoster.tsx b/app/src/components/intelligence/TinyPlaceRoster.tsx index 66727aa35..76efd5f40 100644 --- a/app/src/components/intelligence/TinyPlaceRoster.tsx +++ b/app/src/components/intelligence/TinyPlaceRoster.tsx @@ -11,7 +11,7 @@ import { useT } from '../../lib/i18n/I18nContext'; import type { HarnessType, SessionSummary } from '../../lib/orchestration/orchestrationClient'; import InstanceCard from './InstanceCard'; -export interface TinyPlaceRosterProps { +interface TinyPlaceRosterProps { sessions: SessionSummary[]; selectedId?: string; onSelect?: (sessionId: string) => void; diff --git a/app/src/components/intelligence/Toast.tsx b/app/src/components/intelligence/Toast.tsx index 66c7c7e73..bf7a4e9cd 100644 --- a/app/src/components/intelligence/Toast.tsx +++ b/app/src/components/intelligence/Toast.tsx @@ -57,7 +57,7 @@ const TOAST_ICON_STYLES = { info: 'text-primary-500', }; -export function Toast({ notification, onRemove }: ToastProps) { +function Toast({ notification, onRemove }: ToastProps) { const [isVisible, setIsVisible] = useState(false); const [isExiting, setIsExiting] = useState(false); diff --git a/app/src/components/intelligence/VaultHealthChecklist.tsx b/app/src/components/intelligence/VaultHealthChecklist.tsx index c59274367..8cefba567 100644 --- a/app/src/components/intelligence/VaultHealthChecklist.tsx +++ b/app/src/components/intelligence/VaultHealthChecklist.tsx @@ -241,5 +241,3 @@ export function VaultHealthChecklist({ onToast, title }: VaultHealthChecklistPro ); } - -export default VaultHealthChecklist; diff --git a/app/src/components/intelligence/WorkflowRunApprovalCard.tsx b/app/src/components/intelligence/WorkflowRunApprovalCard.tsx index 195f58994..08954ad6e 100644 --- a/app/src/components/intelligence/WorkflowRunApprovalCard.tsx +++ b/app/src/components/intelligence/WorkflowRunApprovalCard.tsx @@ -33,7 +33,7 @@ const REASON_KEY: Record = { }; /** i18n key for each safety tier label. */ -export const SAFETY_TIER_KEY: Record = { +const SAFETY_TIER_KEY: Record = { read_only: 'orchestration.tier.readOnly', standard: 'orchestration.tier.standard', edit_capable: 'orchestration.tier.editCapable', @@ -48,7 +48,7 @@ interface Props { onCancel: () => void; } -export const WorkflowRunApprovalCard: React.FC = ({ +const WorkflowRunApprovalCard: React.FC = ({ definition, reasons, starting = false, diff --git a/app/src/components/intelligence/WorkflowRunDetail.tsx b/app/src/components/intelligence/WorkflowRunDetail.tsx index 6b5909ae5..d60af42f4 100644 --- a/app/src/components/intelligence/WorkflowRunDetail.tsx +++ b/app/src/components/intelligence/WorkflowRunDetail.tsx @@ -78,7 +78,7 @@ interface Props { onResume: (id: string) => void; } -export const WorkflowRunDetail: React.FC = ({ +const WorkflowRunDetail: React.FC = ({ definition, run, busy = false, diff --git a/app/src/components/intelligence/memoryGraphLayout.ts b/app/src/components/intelligence/memoryGraphLayout.ts index 4e0b483aa..bfb701674 100644 --- a/app/src/components/intelligence/memoryGraphLayout.ts +++ b/app/src/components/intelligence/memoryGraphLayout.ts @@ -36,7 +36,7 @@ export const LEVEL_COLOR = [ export const LEAF_COLOR = '#94A3B8'; // raw chunks / leaves (no level) export const CONTACT_COLOR = '#A78BFA'; // person entities (contacts mode) export const SOURCE_COLOR = '#F97316'; // synthetic source root nodes -export const ROOT_COLOR = '#8B5CF6'; // master root hub (purple) +const ROOT_COLOR = '#8B5CF6'; // master root hub (purple) /** Layout is computed in this fixed coordinate space; the renderer pans/zooms it. */ export const VIEWPORT_W = 1100; @@ -100,7 +100,7 @@ export type SimLink = SimulationLinkDatum; * Tree mode draws an edge from each node to its `parent_id`; contacts mode * uses the explicit `edges`. Dangling endpoints are dropped. */ -export const ROOT_NODE_ID = '__root__'; +const ROOT_NODE_ID = '__root__'; export function buildGraph( nodes: GraphNode[], diff --git a/app/src/components/intelligence/pixiGraphRenderer.ts b/app/src/components/intelligence/pixiGraphRenderer.ts index 5408b740c..7f393e4cc 100644 --- a/app/src/components/intelligence/pixiGraphRenderer.ts +++ b/app/src/components/intelligence/pixiGraphRenderer.ts @@ -29,7 +29,7 @@ import { ZOOM_MIN, } from './memoryGraphLayout'; -export interface PixiGraphOptions { +interface PixiGraphOptions { simNodes: SimNode[]; links: SimLink[]; dark: boolean; diff --git a/app/src/components/intelligence/seedSvgLayout.ts b/app/src/components/intelligence/seedSvgLayout.ts index 6bdc81dcc..46bf0a3b7 100644 --- a/app/src/components/intelligence/seedSvgLayout.ts +++ b/app/src/components/intelligence/seedSvgLayout.ts @@ -18,14 +18,14 @@ import { type GraphEdge, type GraphMode, type GraphNode } from '../../utils/tauriCommands'; import { VIEWPORT_H, VIEWPORT_W } from './memoryGraphLayout'; -export interface SeededPosition { +interface SeededPosition { x: number; y: number; /** True when the node had no carried-over position (newly arrived). */ isNew: boolean; } -export interface SeedResult { +interface SeedResult { /** Index-aligned with the input `nodes`. */ positions: SeededPosition[]; /** Edge index pairs [childIdx, parentIdx] (tree) or [fromIdx, toIdx] (contacts). */ diff --git a/app/src/components/intelligence/useSvgForceLayout.ts b/app/src/components/intelligence/useSvgForceLayout.ts index 3311fb602..03060aba7 100644 --- a/app/src/components/intelligence/useSvgForceLayout.ts +++ b/app/src/components/intelligence/useSvgForceLayout.ts @@ -13,7 +13,7 @@ */ import { useCallback, useEffect, useRef } from 'react'; -export interface SvgLayoutNode { +interface SvgLayoutNode { x: number; y: number; } diff --git a/app/src/components/layout/ChipTabs.tsx b/app/src/components/layout/ChipTabs.tsx index 899a37908..43e4340d4 100644 --- a/app/src/components/layout/ChipTabs.tsx +++ b/app/src/components/layout/ChipTabs.tsx @@ -20,7 +20,7 @@ export interface ChipTabItem { testId?: string; } -export interface ChipTabsProps { +interface ChipTabsProps { /** Chips to render, left to right. */ items: ChipTabItem[]; /** Currently active chip id. */ diff --git a/app/src/components/layout/PageSectionHeader.tsx b/app/src/components/layout/PageSectionHeader.tsx index 55d4043ef..e047405d0 100644 --- a/app/src/components/layout/PageSectionHeader.tsx +++ b/app/src/components/layout/PageSectionHeader.tsx @@ -10,7 +10,7 @@ import type { ReactNode } from 'react'; * the same max-width and centering as the content beneath it — header and body * stay aligned. Pass width/centering via `className` (e.g. `mx-auto max-w-2xl`). */ -export interface PageSectionHeaderProps { +interface PageSectionHeaderProps { title: ReactNode; /** One-line description of what the view does. */ description?: ReactNode; diff --git a/app/src/components/layout/PageWelcome.tsx b/app/src/components/layout/PageWelcome.tsx index 733cf42d8..66e6668bc 100644 --- a/app/src/components/layout/PageWelcome.tsx +++ b/app/src/components/layout/PageWelcome.tsx @@ -13,7 +13,7 @@ import Button from '../ui/Button'; */ /** Accent families available for the icon tile. Keys map to design tokens. */ -export type PageWelcomeAccent = 'ocean' | 'sage' | 'amber' | 'coral'; +type PageWelcomeAccent = 'ocean' | 'sage' | 'amber' | 'coral'; /** Static per-accent tile tint (Tailwind only scans literal class names). */ const ACCENT_TILE: Record = { @@ -23,7 +23,7 @@ const ACCENT_TILE: Record = { coral: 'bg-coral-500/15 text-coral-600', }; -export interface WelcomeFeature { +interface WelcomeFeature { /** Leading glyph — emoji string or icon node. */ icon: ReactNode; /** Short feature title. */ @@ -32,7 +32,7 @@ export interface WelcomeFeature { description: ReactNode; } -export interface WelcomeCta { +interface WelcomeCta { label: ReactNode; onClick: () => void; /** First CTA renders as primary; the rest as secondary (override here). */ @@ -41,7 +41,7 @@ export interface WelcomeCta { testId?: string; } -export interface PageWelcomeProps { +interface PageWelcomeProps { /** Big glyph in the accent tile — emoji string or icon node. */ icon: ReactNode; /** Optional short lead-in above the title (e.g. the page name). */ diff --git a/app/src/components/layout/PanelHeader.tsx b/app/src/components/layout/PanelHeader.tsx index 297714cc4..6b93f7200 100644 --- a/app/src/components/layout/PanelHeader.tsx +++ b/app/src/components/layout/PanelHeader.tsx @@ -1,6 +1,6 @@ import type { ReactNode } from 'react'; -export interface PanelHeaderProps { +interface PanelHeaderProps { /** * Primary title rendered as an `h2` in the control row, left of `action`. * Optional — generic panels stay title-less; the settings template diff --git a/app/src/components/layout/PanelPage.tsx b/app/src/components/layout/PanelPage.tsx index c628bb6c0..f1daf3a7a 100644 --- a/app/src/components/layout/PanelPage.tsx +++ b/app/src/components/layout/PanelPage.tsx @@ -23,7 +23,7 @@ export interface PanelPageTab { chipTestId?: string; } -export interface PanelPageProps { +interface PanelPageProps { /** Page title, shown in the header above the description (optional). */ title?: ReactNode; /** Page description, shown below the title and above any chips. */ diff --git a/app/src/components/layout/PanelScaffold.tsx b/app/src/components/layout/PanelScaffold.tsx index cbe1d4558..feb85f8cb 100644 --- a/app/src/components/layout/PanelScaffold.tsx +++ b/app/src/components/layout/PanelScaffold.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from 'react'; import PanelHeader, { DEFAULT_PANEL_HEADER_BG, DEFAULT_PANEL_HEADER_CLASS } from './PanelHeader'; -export interface PanelScaffoldProps { +interface PanelScaffoldProps { /** Primary title rendered in the fixed header (optional; see {@link PanelHeader}). */ title?: ReactNode; /** Fixed sub-title rendered in a muted tone, below the title. */ diff --git a/app/src/components/layout/TwoPaneNav.tsx b/app/src/components/layout/TwoPaneNav.tsx index fe4be8cd1..083c2d651 100644 --- a/app/src/components/layout/TwoPaneNav.tsx +++ b/app/src/components/layout/TwoPaneNav.tsx @@ -1,12 +1,12 @@ import type { ReactNode } from 'react'; -export interface TwoPaneNavItem { +interface TwoPaneNavItem { value: string; label: string; icon?: ReactNode; } -export interface TwoPaneNavGroup { +interface TwoPaneNavGroup { /** Optional uppercase sub-header above the group's items. */ label?: string; items: TwoPaneNavItem[]; diff --git a/app/src/components/layout/TwoPanelLayout.tsx b/app/src/components/layout/TwoPanelLayout.tsx index 8e2fcb148..46ee15208 100644 --- a/app/src/components/layout/TwoPanelLayout.tsx +++ b/app/src/components/layout/TwoPanelLayout.tsx @@ -47,7 +47,7 @@ export function useTwoPanelLayout(id: string, defaults?: Partial) { }; } -export interface TwoPanelLayoutProps { +interface TwoPanelLayoutProps { /** Stable id used as the persistence key for this layout's geometry. */ id: string; /** Content of the mini sidebar (left pane). */ @@ -103,7 +103,7 @@ export interface TwoPanelLayoutProps { } /** Default card look shared by both panes. */ -export const DEFAULT_PANE_CLASS = 'bg-surface rounded-2xl shadow-soft border border-line'; +const DEFAULT_PANE_CLASS = 'bg-surface rounded-2xl shadow-soft border border-line'; const DEFAULT_MIN_WIDTH = 180; const DEFAULT_MAX_WIDTH = 480; diff --git a/app/src/components/layout/shell/RootShellLayout.tsx b/app/src/components/layout/shell/RootShellLayout.tsx index 00cda6c96..978720f74 100644 --- a/app/src/components/layout/shell/RootShellLayout.tsx +++ b/app/src/components/layout/shell/RootShellLayout.tsx @@ -50,7 +50,7 @@ export function useRootSidebar() { }; } -export interface RootShellLayoutProps { +interface RootShellLayoutProps { /** Always-visible left pane (the app sidebar). */ sidebar: ReactNode; /** Dynamic main content (the routed page area). */ diff --git a/app/src/components/layout/usePageWelcomeView.tsx b/app/src/components/layout/usePageWelcomeView.tsx index f38403f71..31caad1d3 100644 --- a/app/src/components/layout/usePageWelcomeView.tsx +++ b/app/src/components/layout/usePageWelcomeView.tsx @@ -15,10 +15,10 @@ const navIcon = (d: string) => ( const WELCOME_ICON = navIcon('M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z'); /** `'welcome'` / `'main'`, plus any extra sub-page value the caller declares. */ -export type PageWelcomeViewId = string; +type PageWelcomeViewId = string; /** An additional sub-page beyond Welcome ¡ Main (e.g. Workflows' Runs / Discoveries). */ -export interface PageWelcomeExtraItem { +interface PageWelcomeExtraItem { /** `?view=` value + nav selection id. */ value: string; /** Sidebar label. */ @@ -27,7 +27,7 @@ export interface PageWelcomeExtraItem { iconPath: string; } -export interface UsePageWelcomeViewOptions { +interface UsePageWelcomeViewOptions { /** Accessible label for the sidebar nav. */ ariaLabel: string; /** Label for the Welcome entry. */ @@ -42,7 +42,7 @@ export interface UsePageWelcomeViewOptions { extraItems?: PageWelcomeExtraItem[]; } -export interface PageWelcomeView { +interface PageWelcomeView { /** Current view — `welcome` (default landing) or `main`. */ view: PageWelcomeViewId; /** Switch views (updates `?view=`). */ diff --git a/app/src/components/mcp-setup/SecretPromptDialog.test.tsx b/app/src/components/mcp-setup/SecretPromptDialog.test.tsx index 00dcd0876..a1be58194 100644 --- a/app/src/components/mcp-setup/SecretPromptDialog.test.tsx +++ b/app/src/components/mcp-setup/SecretPromptDialog.test.tsx @@ -1,7 +1,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { SecretPromptDialog } from './SecretPromptDialog'; +import SecretPromptDialog from './SecretPromptDialog'; const callCoreRpc = vi.fn(); vi.mock('../../services/coreRpcClient', () => ({ diff --git a/app/src/components/mcp-setup/SecretPromptDialog.tsx b/app/src/components/mcp-setup/SecretPromptDialog.tsx index 3bd788c59..fad9e6c4e 100644 --- a/app/src/components/mcp-setup/SecretPromptDialog.tsx +++ b/app/src/components/mcp-setup/SecretPromptDialog.tsx @@ -16,7 +16,7 @@ import { callCoreRpc } from '../../services/coreRpcClient'; type Request = { refId: string; keyName: string; prompt: string }; -export function SecretPromptDialog() { +function SecretPromptDialog() { const { t } = useT(); const [request, setRequest] = useState(null); const [value, setValue] = useState(''); diff --git a/app/src/components/meetings/ActionItemChecklist.tsx b/app/src/components/meetings/ActionItemChecklist.tsx index 78d57a4c7..cacafcbe2 100644 --- a/app/src/components/meetings/ActionItemChecklist.tsx +++ b/app/src/components/meetings/ActionItemChecklist.tsx @@ -19,7 +19,7 @@ interface ActionItemChecklistProps { items: MeetCallActionItem[]; } -export function ActionItemChecklist({ items }: ActionItemChecklistProps) { +function ActionItemChecklist({ items }: ActionItemChecklistProps) { const { t } = useT(); const navigate = useNavigate(); const [checked, setChecked] = useState>({}); diff --git a/app/src/components/meetings/ActiveMeetingBanner.tsx b/app/src/components/meetings/ActiveMeetingBanner.tsx index a7c35fefe..63762380e 100644 --- a/app/src/components/meetings/ActiveMeetingBanner.tsx +++ b/app/src/components/meetings/ActiveMeetingBanner.tsx @@ -41,7 +41,7 @@ type Toast = { type: 'success' | 'error' | 'info'; title: string; message?: stri */ export const LEAVE_SAFETY_TIMEOUT_MS = 10_000; -export interface ActiveMeetingBannerProps { +interface ActiveMeetingBannerProps { onToast?: (toast: Toast) => void; } diff --git a/app/src/components/meetings/HistoryDetail.tsx b/app/src/components/meetings/HistoryDetail.tsx index 6aae6e7ac..49dc6fa07 100644 --- a/app/src/components/meetings/HistoryDetail.tsx +++ b/app/src/components/meetings/HistoryDetail.tsx @@ -58,7 +58,7 @@ interface LoadedResult { detail: MeetCallDetail | null; } -export function HistoryDetail({ record }: HistoryDetailProps) { +function HistoryDetail({ record }: HistoryDetailProps) { const { t } = useT(); // Keyed state: bundles status+detail with the request_id they belong to. diff --git a/app/src/components/meetings/HistoryRail.tsx b/app/src/components/meetings/HistoryRail.tsx index dfd3e52ff..e24c2b3dd 100644 --- a/app/src/components/meetings/HistoryRail.tsx +++ b/app/src/components/meetings/HistoryRail.tsx @@ -212,7 +212,7 @@ function formatRelativeTime(ms: number, t: (key: string) => string): string { } } -export function HistoryRail({ +function HistoryRail({ groups, selectedId, onSelect, diff --git a/app/src/components/meetings/HistorySection.tsx b/app/src/components/meetings/HistorySection.tsx index 16d7897d2..76462d02a 100644 --- a/app/src/components/meetings/HistorySection.tsx +++ b/app/src/components/meetings/HistorySection.tsx @@ -61,7 +61,7 @@ function groupRecords( return groups; } -export function HistorySection() { +function HistorySection() { const { t } = useT(); const [records, setRecords] = useState(null); const [loading, setLoading] = useState(true); diff --git a/app/src/components/meetings/JoinPolicyToggle.tsx b/app/src/components/meetings/JoinPolicyToggle.tsx index dcfd8f427..11771e640 100644 --- a/app/src/components/meetings/JoinPolicyToggle.tsx +++ b/app/src/components/meetings/JoinPolicyToggle.tsx @@ -9,7 +9,7 @@ import { useT } from '../../lib/i18n/I18nContext'; export type JoinPolicy = 'auto' | 'ask' | 'skip'; -export interface JoinPolicyToggleProps { +interface JoinPolicyToggleProps { value: JoinPolicy; onChange: (v: JoinPolicy) => void; disabled?: boolean; diff --git a/app/src/components/meetings/LiveTranscriptPanel.tsx b/app/src/components/meetings/LiveTranscriptPanel.tsx index b4aa243a7..01306b1f6 100644 --- a/app/src/components/meetings/LiveTranscriptPanel.tsx +++ b/app/src/components/meetings/LiveTranscriptPanel.tsx @@ -32,7 +32,7 @@ function parseLiveLine(content: string): { speaker: string | null; text: string return { speaker: null, text: content }; } -export interface LiveTranscriptPanelProps { +interface LiveTranscriptPanelProps { turns: BackendMeetTurn[]; partialIndex: number | null; } @@ -100,5 +100,3 @@ export function LiveTranscriptPanel({ turns = [], partialIndex }: LiveTranscript ); } - -export default LiveTranscriptPanel; diff --git a/app/src/components/meetings/MeetComposer.tsx b/app/src/components/meetings/MeetComposer.tsx index a64af50b4..0a4cf096c 100644 --- a/app/src/components/meetings/MeetComposer.tsx +++ b/app/src/components/meetings/MeetComposer.tsx @@ -46,7 +46,7 @@ const log = debug('meetings:composer'); type Toast = { type: 'success' | 'error' | 'info'; title: string; message?: string }; -export interface MeetComposerProps { +interface MeetComposerProps { onToast?: (toast: Toast) => void; /** Ref owned by the parent (MeetingsPage) so the success toast can fire * after the inline form unmounts on status → 'active'. */ diff --git a/app/src/components/meetings/MeetDefaultsDrawer.tsx b/app/src/components/meetings/MeetDefaultsDrawer.tsx index ba0c848c9..c40785194 100644 --- a/app/src/components/meetings/MeetDefaultsDrawer.tsx +++ b/app/src/components/meetings/MeetDefaultsDrawer.tsx @@ -27,7 +27,7 @@ import { const log = debug('meetings:defaults-drawer'); -export interface MeetDefaultsDrawerProps { +interface MeetDefaultsDrawerProps { open: boolean; onClose: () => void; } diff --git a/app/src/components/meetings/MeetingsPage.tsx b/app/src/components/meetings/MeetingsPage.tsx index a8f6fcac6..3610bd606 100644 --- a/app/src/components/meetings/MeetingsPage.tsx +++ b/app/src/components/meetings/MeetingsPage.tsx @@ -27,7 +27,7 @@ const log = debug('meetings:page'); type Toast = { type: 'success' | 'error' | 'info'; title: string; message?: string }; -export interface MeetingsPageProps { +interface MeetingsPageProps { onToast?: (toast: Toast) => void; } diff --git a/app/src/components/meetings/PlatformChips.tsx b/app/src/components/meetings/PlatformChips.tsx index 4ac528a4a..9c2ec6165 100644 --- a/app/src/components/meetings/PlatformChips.tsx +++ b/app/src/components/meetings/PlatformChips.tsx @@ -16,7 +16,7 @@ import { MEETING_PLATFORMS, platformLabel, platformLogoUrl } from './meetingUtil const log = debug('meetings:platform-chips'); -export interface PlatformChipsProps { +interface PlatformChipsProps { selected: MeetingPlatform; onSelect: (platform: MeetingPlatform) => void; disabled?: boolean; diff --git a/app/src/components/meetings/TranscriptViewer.tsx b/app/src/components/meetings/TranscriptViewer.tsx index 2e47abecd..9a3a8c7fa 100644 --- a/app/src/components/meetings/TranscriptViewer.tsx +++ b/app/src/components/meetings/TranscriptViewer.tsx @@ -25,7 +25,7 @@ function buildPlainText(lines: MeetCallTranscriptLine[]): string { .join('\n'); } -export function TranscriptViewer({ lines }: TranscriptViewerProps) { +function TranscriptViewer({ lines }: TranscriptViewerProps) { const { t } = useT(); async function handleCopy() { diff --git a/app/src/components/meetings/UpcomingTable.tsx b/app/src/components/meetings/UpcomingTable.tsx index 7ff6bcc6d..81a5d11b3 100644 --- a/app/src/components/meetings/UpcomingTable.tsx +++ b/app/src/components/meetings/UpcomingTable.tsx @@ -330,7 +330,7 @@ function SkeletonRow() { // Main component // --------------------------------------------------------------------------- -export interface UpcomingTableProps { +interface UpcomingTableProps { lookaheadMinutes?: number; limit?: number; /** diff --git a/app/src/components/meetings/meetingUtils.ts b/app/src/components/meetings/meetingUtils.ts index 86f158d00..af98ea65c 100644 --- a/app/src/components/meetings/meetingUtils.ts +++ b/app/src/components/meetings/meetingUtils.ts @@ -181,7 +181,7 @@ export function deriveDisplayNameFromEmail(email: string | undefined): string { * mailbox, then blank. Slugs are canonical Composio slugs (see * `canonicalizeComposioToolkitSlug`). */ -export const NAME_SOURCE_TOOLKITS: Record = { +const NAME_SOURCE_TOOLKITS: Record = { gmeet: ['googlemeet', 'gmail'], zoom: ['zoom', 'gmail'], teams: ['microsoft_teams', 'outlook', 'gmail'], diff --git a/app/src/components/meetings/useUpcomingMeetings.ts b/app/src/components/meetings/useUpcomingMeetings.ts index c150ffabd..e7ff8b442 100644 --- a/app/src/components/meetings/useUpcomingMeetings.ts +++ b/app/src/components/meetings/useUpcomingMeetings.ts @@ -14,7 +14,7 @@ const log = debug('meetings:upcoming'); const POLL_INTERVAL_MS = 60_000; -export interface UseUpcomingMeetingsResult { +interface UseUpcomingMeetingsResult { meetings: UpcomingMeeting[]; loading: boolean; error: string | null; diff --git a/app/src/components/oauth/oauthAuthReadiness.ts b/app/src/components/oauth/oauthAuthReadiness.ts index f0b16b039..05fe7034e 100644 --- a/app/src/components/oauth/oauthAuthReadiness.ts +++ b/app/src/components/oauth/oauthAuthReadiness.ts @@ -17,9 +17,9 @@ const warnLog = debug('oauth:auth-readiness:warn'); const DEFAULT_MAX_WAIT_MS = 30_000; const POLL_MS = 200; -export type OAuthAuthReadinessFailure = 'core_mode_unset' | 'core_unreachable'; +type OAuthAuthReadinessFailure = 'core_mode_unset' | 'core_unreachable'; -export type OAuthAuthReadinessResult = +type OAuthAuthReadinessResult = | { ready: true } | { ready: false; reason: OAuthAuthReadinessFailure }; diff --git a/app/src/components/oauth/providerConfigs.tsx b/app/src/components/oauth/providerConfigs.tsx index 38a283860..a76e94ef4 100644 --- a/app/src/components/oauth/providerConfigs.tsx +++ b/app/src/components/oauth/providerConfigs.tsx @@ -84,7 +84,3 @@ export const oauthProviderConfigs: OAuthProviderConfig[] = [ showOnWelcome: false, }, ]; - -export const getProviderConfig = (provider: string): OAuthProviderConfig | undefined => { - return oauthProviderConfigs.find(config => config.id === provider); -}; diff --git a/app/src/components/orchestration/ActiveSubagentsRail.tsx b/app/src/components/orchestration/ActiveSubagentsRail.tsx index 8cd803a57..328ad3289 100644 --- a/app/src/components/orchestration/ActiveSubagentsRail.tsx +++ b/app/src/components/orchestration/ActiveSubagentsRail.tsx @@ -74,7 +74,7 @@ function StatusDot({ state }: { state: ConnState }) { ); } -export interface ActiveSubagentsRailProps { +interface ActiveSubagentsRailProps { /** Sessions grouped by contact (`agentId`) — the instances. */ byContact: Map; /** Currently open session id (highlighted when on the agent tab). */ diff --git a/app/src/components/orchestration/AgentChatPanel.tsx b/app/src/components/orchestration/AgentChatPanel.tsx index 805c7ff11..4b9279bd7 100644 --- a/app/src/components/orchestration/AgentChatPanel.tsx +++ b/app/src/components/orchestration/AgentChatPanel.tsx @@ -364,7 +364,7 @@ function SessionChatView({ session }: { session: SessionSummary }) { ); } -export interface AgentChatPanelProps { +interface AgentChatPanelProps { /** * Controlled open peer-session id (the full-page session subpage). When * `onOpenSession` is provided the parent owns this (OrchestrationView drives diff --git a/app/src/components/orchestration/SessionTranscript.tsx b/app/src/components/orchestration/SessionTranscript.tsx index 8afd14b36..2fc61bb09 100644 --- a/app/src/components/orchestration/SessionTranscript.tsx +++ b/app/src/components/orchestration/SessionTranscript.tsx @@ -16,9 +16,9 @@ import { mergeToolActivity, type ToolActivity } from '../../lib/orchestration/me import type { ChatMessage } from '../../lib/orchestration/useOrchestrationChats'; import { formatTime } from '../intelligence/orchestrationTabHelpers'; -export type ApprovalDecision = 'approve' | 'deny' | 'always'; +type ApprovalDecision = 'approve' | 'deny' | 'always'; -export interface SessionTranscriptProps { +interface SessionTranscriptProps { messages: ChatMessage[]; /** * When present, approval rows show actionable Approve/Deny buttons wired to diff --git a/app/src/components/orchestration/demo/medullaDemoData.ts b/app/src/components/orchestration/demo/medullaDemoData.ts index 21690448c..a8b9c8fda 100644 --- a/app/src/components/orchestration/demo/medullaDemoData.ts +++ b/app/src/components/orchestration/demo/medullaDemoData.ts @@ -85,7 +85,7 @@ export function buildDemoGraph(deviceLabel: string): { nodes: GraphNode[]; edges export type DemoPeerStatus = 'connected' | 'connecting' | 'idle'; -export interface DemoPeer { +interface DemoPeer { id: string; address: string; status: DemoPeerStatus; @@ -112,9 +112,9 @@ export function buildDemoPeers(): DemoPeer[] { // ── Demo chat transcript ─────────────────────────────────────────────────── -export type DemoChatRole = 'user' | 'assistant' | 'activity'; +type DemoChatRole = 'user' | 'assistant' | 'activity'; -export interface DemoChatMessage { +interface DemoChatMessage { id: string; role: DemoChatRole; /** i18n key for the message body. */ diff --git a/app/src/components/routines/RoutineCard.tsx b/app/src/components/routines/RoutineCard.tsx deleted file mode 100644 index dd38427bf..000000000 --- a/app/src/components/routines/RoutineCard.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import { useT } from '../../lib/i18n/I18nContext'; -import type { CoreCronJob, CoreCronRun } from '../../utils/tauriCommands'; -import { cronToHuman, formatNextRun, formatRoutineName } from './routineHelpers'; -import RoutineRunHistory from './RoutineRunHistory'; - -interface RoutineCardProps { - job: CoreCronJob; - runs: CoreCronRun[]; - busyKeys: Set; - onToggle: () => void; - onRunNow: () => void; - onLoadRuns: () => void; -} - -const RoutineCard = ({ job, runs, busyKeys, onToggle, onRunNow, onLoadRuns }: RoutineCardProps) => { - const { t } = useT(); - - const isToggling = busyKeys.has(`toggle:${job.id}`); - const isRunning = busyKeys.has(`run:${job.id}`); - const isLoadingRuns = busyKeys.has(`runs:${job.id}`); - - const isSuccess = job.last_status === 'ok' || job.last_status === 'success'; - const isError = job.last_status === 'error'; - const typeLabel = job.job_type === 'agent' ? t('routines.typeAgent') : t('routines.typeCommand'); - - return ( -
- {/* Header: name + type badge + toggle */} -
-
-
-

- {formatRoutineName(job.name)} -

- - {typeLabel} - -
-
- - {/* Toggle switch */} - -
- - {/* Schedule + next run + status */} -
- {/* Schedule */} -
- - - - {cronToHuman(job.schedule)} -
- - {/* Next run */} - {job.enabled && ( -
- - - - - {t('routines.nextRun')}: {formatNextRun(job.next_run)} - -
- )} - - {/* Last run status */} -
- - - {isSuccess - ? t('routines.lastRunSuccess') - : isError - ? t('routines.lastRunFailed') - : t('routines.notRunYet')} - -
-
- - {/* Actions */} -
- - - -
-
- ); -}; - -export default RoutineCard; diff --git a/app/src/components/routines/RoutineRunHistory.tsx b/app/src/components/routines/RoutineRunHistory.tsx deleted file mode 100644 index 0e5d29620..000000000 --- a/app/src/components/routines/RoutineRunHistory.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { useState } from 'react'; - -import { useT } from '../../lib/i18n/I18nContext'; -import type { CoreCronRun } from '../../utils/tauriCommands'; -import { formatDuration, formatTimeAgo } from './routineHelpers'; - -interface RoutineRunHistoryProps { - runs: CoreCronRun[]; - loading: boolean; - onLoadRuns: () => void; -} - -const RoutineRunHistory = ({ runs, loading, onLoadRuns }: RoutineRunHistoryProps) => { - const { t } = useT(); - const [expanded, setExpanded] = useState(false); - const [expandedOutputId, setExpandedOutputId] = useState(null); - - const handleToggle = () => { - if (!expanded && runs.length === 0) { - onLoadRuns(); - } - setExpanded(prev => !prev); - }; - - return ( -
- - - {expanded && ( -
- {runs.length === 0 && !loading && ( -
{t('routines.noHistory')}
- )} - {runs.map(run => { - const isSuccess = run.status === 'ok' || run.status === 'success'; - const hasOutput = run.output && run.output.trim().length > 0; - const isOutputExpanded = expandedOutputId === run.id; - - return ( -
-
- - - {isSuccess ? t('routines.statusSuccess') : t('routines.statusError')} - - {formatTimeAgo(run.finished_at)} - {run.duration_ms != null && ( - ({formatDuration(run.duration_ms)}) - )} - {hasOutput && ( - - )} -
- {isOutputExpanded && hasOutput && ( -
-                    {run.output}
-                  
- )} -
- ); - })} -
- )} -
- ); -}; - -export default RoutineRunHistory; diff --git a/app/src/components/routines/routineHelpers.ts b/app/src/components/routines/routineHelpers.ts index 7e0672996..aebd609f2 100644 --- a/app/src/components/routines/routineHelpers.ts +++ b/app/src/components/routines/routineHelpers.ts @@ -156,31 +156,6 @@ export function formatNextRun(iso: string): string { return target.toLocaleString(); } -/** - * Format a past ISO timestamp as relative time (e.g. "2 hours ago"). - */ -export function formatTimeAgo(iso: string): string { - const past = new Date(iso); - const now = new Date(); - const diffMs = now.getTime() - past.getTime(); - - if (diffMs < 0) return past.toLocaleString(); - - const diffSec = Math.floor(diffMs / 1000); - if (diffSec < 60) return 'just now'; - - const diffMin = Math.floor(diffMs / 60_000); - if (diffMin < 60) return `${diffMin} minute${diffMin !== 1 ? 's' : ''} ago`; - - const diffHrs = Math.floor(diffMs / 3_600_000); - if (diffHrs < 24) return `${diffHrs} hour${diffHrs !== 1 ? 's' : ''} ago`; - - const diffDays = Math.floor(diffMs / 86_400_000); - if (diffDays < 30) return `${diffDays} day${diffDays !== 1 ? 's' : ''} ago`; - - return past.toLocaleDateString(); -} - /** * Format duration_ms into a compact string like "3s" or "1m 12s". */ diff --git a/app/src/components/settings/components/MemoryWindowControl.tsx b/app/src/components/settings/components/MemoryWindowControl.tsx index 5a115c554..9844cb053 100644 --- a/app/src/components/settings/components/MemoryWindowControl.tsx +++ b/app/src/components/settings/components/MemoryWindowControl.tsx @@ -15,38 +15,6 @@ interface PresetMeta { hint: string; } -/** - * Plain-language framing for each preset. The actual character budgets - * live in the Rust core (`MemoryContextWindow::limits` in - * `src/openhuman/config/schema/agent.rs`) — these strings only describe - * the UX tradeoff so users can pick without doing math. - */ -// NOTE: MEMORY_WINDOW_PRESET_META is now built inside the component using t() so labels/badges/hints -// are translated. The exported constant is kept for backwards-compat but callers that need -// localised strings should use the hook. -export const MEMORY_WINDOW_PRESET_META: Record = { - minimal: { - label: 'Minimal', - badge: 'Cheapest', - hint: 'Smallest memory window. Cheapest, fastest, least continuity between runs.', - }, - balanced: { - label: 'Balanced', - badge: 'Recommended', - hint: 'Sensible default — good continuity without burning extra tokens on every run.', - }, - extended: { - label: 'Extended', - badge: 'More context', - hint: 'More long-term memory injected into each run. Higher token cost per turn.', - }, - maximum: { - label: 'Maximum', - badge: 'Highest cost', - hint: 'The largest safe window. Best continuity, meaningfully higher token bill on every run.', - }, -}; - const isMemoryContextWindow = (value: unknown): value is MemoryContextWindow => typeof value === 'string' && (MEMORY_CONTEXT_WINDOWS as readonly string[]).includes(value); diff --git a/app/src/components/settings/components/PageBackButton.tsx b/app/src/components/settings/components/PageBackButton.tsx deleted file mode 100644 index 5e5646cb9..000000000 --- a/app/src/components/settings/components/PageBackButton.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import type { ReactNode } from 'react'; - -interface PageBackButtonProps { - label: string; - onClick: () => void; - trailingContent?: ReactNode; -} - -const PageBackButton = ({ label, onClick, trailingContent }: PageBackButtonProps) => { - return ( -
- - {trailingContent} -
- ); -}; - -export default PageBackButton; diff --git a/app/src/components/settings/components/SettingsBackButton.tsx b/app/src/components/settings/components/SettingsBackButton.tsx index ce4a363d4..38e159ab5 100644 --- a/app/src/components/settings/components/SettingsBackButton.tsx +++ b/app/src/components/settings/components/SettingsBackButton.tsx @@ -9,8 +9,8 @@ interface SettingsBackButtonProps { } /** - * Route-aware back button shared by {@link SettingsHeader} and panels built on - * {@link PanelScaffold}. Encapsulates the visibility rules so both render the + * Route-aware back button shared by settings panels built on + * {@link PanelScaffold}. Encapsulates the visibility rules so they render the * exact same affordance: * * - Hidden entirely when there's no `onBack`, or when a panel is embedded diff --git a/app/src/components/settings/components/SettingsHeader.tsx b/app/src/components/settings/components/SettingsHeader.tsx deleted file mode 100644 index aaeb6b2cc..000000000 --- a/app/src/components/settings/components/SettingsHeader.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import type { ReactNode } from 'react'; - -import { useT } from '../../../lib/i18n/I18nContext'; -import SettingsBackButton from './SettingsBackButton'; - -interface BreadcrumbItem { - label: string; - onClick?: () => void; -} - -/** - * @deprecated For settings panels use {@link SettingsPanel} - * (`components/settings/layout/SettingsPanel`), which bakes in a consistent - * title, back button, sub-nav and spacing. This component now only backs the - * couple of standalone non-settings page shells (e.g. Webhooks, Intelligence) - * that live outside the `/settings` route tree and therefore can't derive a - * title from the settings route registry. Do not add new settings call sites. - */ -interface SettingsHeaderProps { - className?: string; - title?: string; - showBackButton?: boolean; - onBack?: () => void; - /** - * Accepted for backward compatibility but no longer rendered — the two-pane - * sidebar replaced breadcrumb navigation. Call sites are cleaned up - * incrementally. - */ - breadcrumbs?: BreadcrumbItem[]; - /** - * Optional right-aligned action (e.g. a refresh or pair-device button). - * Rendered at the end of the header row so panels keep the canonical - * "SettingsHeader as first child" structure instead of wrapping the header - * in an ad-hoc flex row. - */ - action?: ReactNode; -} - -const SettingsHeader = ({ - className = '', - title, - showBackButton = false, - onBack, - action, -}: SettingsHeaderProps) => { - const { t } = useT(); - - return ( -
-
-
- {/* Route-aware back button (hidden when not applicable). */} - {showBackButton && } - - {/* Title */} -

{title ?? t('nav.settings')}

-
- - {action &&
{action}
} -
-
- ); -}; - -export default SettingsHeader; diff --git a/app/src/components/settings/controls/SettingsBadge.tsx b/app/src/components/settings/controls/SettingsBadge.tsx index abf85a951..047ff048f 100644 --- a/app/src/components/settings/controls/SettingsBadge.tsx +++ b/app/src/components/settings/controls/SettingsBadge.tsx @@ -2,7 +2,7 @@ import { type ReactNode } from 'react'; export type SettingsBadgeVariant = 'neutral' | 'primary' | 'success' | 'warning' | 'danger'; -export interface SettingsBadgeProps { +interface SettingsBadgeProps { variant: SettingsBadgeVariant; children: ReactNode; className?: string; diff --git a/app/src/components/settings/controls/SettingsCheckbox.tsx b/app/src/components/settings/controls/SettingsCheckbox.tsx index 021ac6b14..5f8c2261d 100644 --- a/app/src/components/settings/controls/SettingsCheckbox.tsx +++ b/app/src/components/settings/controls/SettingsCheckbox.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react'; -export interface SettingsCheckboxProps { +interface SettingsCheckboxProps { id: string; checked: boolean; onCheckedChange: (next: boolean) => void; diff --git a/app/src/components/settings/controls/SettingsEmptyState.tsx b/app/src/components/settings/controls/SettingsEmptyState.tsx index 3b1864bb1..bfd86b6c5 100644 --- a/app/src/components/settings/controls/SettingsEmptyState.tsx +++ b/app/src/components/settings/controls/SettingsEmptyState.tsx @@ -1,4 +1,4 @@ -export interface SettingsEmptyStateProps { +interface SettingsEmptyStateProps { label: string; className?: string; } diff --git a/app/src/components/settings/controls/SettingsListItem.tsx b/app/src/components/settings/controls/SettingsListItem.tsx index 47f9a4c98..5efe5e5f1 100644 --- a/app/src/components/settings/controls/SettingsListItem.tsx +++ b/app/src/components/settings/controls/SettingsListItem.tsx @@ -2,7 +2,7 @@ import { type ReactNode } from 'react'; import Button from '../../ui/Button'; -export interface SettingsListItemProps { +interface SettingsListItemProps { label: string; badge?: ReactNode; onRemove?: () => void; diff --git a/app/src/components/settings/controls/SettingsNumberField.tsx b/app/src/components/settings/controls/SettingsNumberField.tsx index 135a58613..30df807d3 100644 --- a/app/src/components/settings/controls/SettingsNumberField.tsx +++ b/app/src/components/settings/controls/SettingsNumberField.tsx @@ -1,6 +1,6 @@ import Input from '../../ui/Input'; -export interface SettingsNumberFieldProps { +interface SettingsNumberFieldProps { id: string; value: string; onChange: (v: string) => void; diff --git a/app/src/components/settings/controls/SettingsRow.tsx b/app/src/components/settings/controls/SettingsRow.tsx index 4d70ca568..845c4bd33 100644 --- a/app/src/components/settings/controls/SettingsRow.tsx +++ b/app/src/components/settings/controls/SettingsRow.tsx @@ -1,6 +1,6 @@ import { type ReactNode } from 'react'; -export interface SettingsRowProps { +interface SettingsRowProps { htmlFor?: string; label?: string; description?: string; diff --git a/app/src/components/settings/controls/SettingsSection.tsx b/app/src/components/settings/controls/SettingsSection.tsx index 65841e4ed..6b210b753 100644 --- a/app/src/components/settings/controls/SettingsSection.tsx +++ b/app/src/components/settings/controls/SettingsSection.tsx @@ -1,6 +1,6 @@ import { type ReactNode } from 'react'; -export interface SettingsSectionProps { +interface SettingsSectionProps { title?: string; description?: string; children: ReactNode; diff --git a/app/src/components/settings/controls/SettingsSelect.tsx b/app/src/components/settings/controls/SettingsSelect.tsx index 3d1114a48..ff9dccdf5 100644 --- a/app/src/components/settings/controls/SettingsSelect.tsx +++ b/app/src/components/settings/controls/SettingsSelect.tsx @@ -1,8 +1,8 @@ import { forwardRef, type SelectHTMLAttributes } from 'react'; -export type SettingsSelectSize = 'sm' | 'md'; +type SettingsSelectSize = 'sm' | 'md'; -export interface SettingsSelectProps extends SelectHTMLAttributes { +interface SettingsSelectProps extends SelectHTMLAttributes { inputSize?: SettingsSelectSize; 'data-testid'?: string; } diff --git a/app/src/components/settings/controls/SettingsStatusLine.tsx b/app/src/components/settings/controls/SettingsStatusLine.tsx index ac20ce763..e05d748d8 100644 --- a/app/src/components/settings/controls/SettingsStatusLine.tsx +++ b/app/src/components/settings/controls/SettingsStatusLine.tsx @@ -1,6 +1,6 @@ import { type ReactNode } from 'react'; -export interface SettingsStatusLineProps { +interface SettingsStatusLineProps { saving: boolean; savedNote?: string | null; error?: string | null; diff --git a/app/src/components/settings/controls/SettingsSwitch.tsx b/app/src/components/settings/controls/SettingsSwitch.tsx index 4e06fc6e7..a0166b763 100644 --- a/app/src/components/settings/controls/SettingsSwitch.tsx +++ b/app/src/components/settings/controls/SettingsSwitch.tsx @@ -1,4 +1,4 @@ -export interface SettingsSwitchProps { +interface SettingsSwitchProps { id: string; checked: boolean; onCheckedChange: (next: boolean) => void; diff --git a/app/src/components/settings/controls/SettingsTextArea.tsx b/app/src/components/settings/controls/SettingsTextArea.tsx index b898a94d7..707d51e97 100644 --- a/app/src/components/settings/controls/SettingsTextArea.tsx +++ b/app/src/components/settings/controls/SettingsTextArea.tsx @@ -1,6 +1,6 @@ import { forwardRef, type TextareaHTMLAttributes } from 'react'; -export interface SettingsTextAreaProps extends TextareaHTMLAttributes { +interface SettingsTextAreaProps extends TextareaHTMLAttributes { invalid?: boolean; rows?: number; } diff --git a/app/src/components/settings/controls/SettingsTextField.tsx b/app/src/components/settings/controls/SettingsTextField.tsx index bb95e224a..dfef7690d 100644 --- a/app/src/components/settings/controls/SettingsTextField.tsx +++ b/app/src/components/settings/controls/SettingsTextField.tsx @@ -2,7 +2,7 @@ import { forwardRef } from 'react'; import Input, { type InputProps } from '../../ui/Input'; -export interface SettingsTextFieldProps extends InputProps { +interface SettingsTextFieldProps extends InputProps { mono?: boolean; } diff --git a/app/src/components/settings/controls/index.ts b/app/src/components/settings/controls/index.ts index 9e525d7c2..1067d7b93 100644 --- a/app/src/components/settings/controls/index.ts +++ b/app/src/components/settings/controls/index.ts @@ -1,35 +1,24 @@ export { default as SettingsBadge } from './SettingsBadge'; -export type { SettingsBadgeProps, SettingsBadgeVariant } from './SettingsBadge'; +export type { SettingsBadgeVariant } from './SettingsBadge'; export { default as SettingsCheckbox } from './SettingsCheckbox'; -export type { SettingsCheckboxProps } from './SettingsCheckbox'; export { default as SettingsEmptyState } from './SettingsEmptyState'; -export type { SettingsEmptyStateProps } from './SettingsEmptyState'; export { default as SettingsListItem } from './SettingsListItem'; -export type { SettingsListItemProps } from './SettingsListItem'; export { default as SettingsNumberField } from './SettingsNumberField'; -export type { SettingsNumberFieldProps } from './SettingsNumberField'; export { default as SettingsRow } from './SettingsRow'; -export type { SettingsRowProps } from './SettingsRow'; export { default as SettingsSection } from './SettingsSection'; -export type { SettingsSectionProps } from './SettingsSection'; export { default as SettingsSelect } from './SettingsSelect'; -export type { SettingsSelectProps, SettingsSelectSize } from './SettingsSelect'; export { default as SettingsStatusLine } from './SettingsStatusLine'; -export type { SettingsStatusLineProps } from './SettingsStatusLine'; export { default as SettingsSwitch } from './SettingsSwitch'; -export type { SettingsSwitchProps } from './SettingsSwitch'; export { default as SettingsTextArea } from './SettingsTextArea'; -export type { SettingsTextAreaProps } from './SettingsTextArea'; export { default as SettingsTextField } from './SettingsTextField'; -export type { SettingsTextFieldProps } from './SettingsTextField'; diff --git a/app/src/components/settings/hooks/useSettingsNavigation.ts b/app/src/components/settings/hooks/useSettingsNavigation.ts index 035ec2df8..68c77c82c 100644 --- a/app/src/components/settings/hooks/useSettingsNavigation.ts +++ b/app/src/components/settings/hooks/useSettingsNavigation.ts @@ -14,7 +14,7 @@ const log = debug('settings:nav'); // SettingsRoute type — derived from the registry so it stays in sync. // --------------------------------------------------------------------------- -export type SettingsRoute = +type SettingsRoute = | 'home' | 'agents' | 'agent-access' @@ -58,7 +58,7 @@ export type SettingsRoute = | 'tool-policy-diagnostics' | 'about'; -export interface BreadcrumbItem { +interface BreadcrumbItem { label: string; onClick?: () => void; } diff --git a/app/src/components/settings/layout/SettingsLayoutContext.tsx b/app/src/components/settings/layout/SettingsLayoutContext.tsx index 0ae4dd5dc..292118ddc 100644 --- a/app/src/components/settings/layout/SettingsLayoutContext.tsx +++ b/app/src/components/settings/layout/SettingsLayoutContext.tsx @@ -21,5 +21,3 @@ const SettingsLayoutContext = createContext<{ export const SettingsLayoutProvider = SettingsLayoutContext.Provider; export const useSettingsLayout = () => useContext(SettingsLayoutContext); - -export default SettingsLayoutContext; diff --git a/app/src/components/settings/layout/SettingsPanel.tsx b/app/src/components/settings/layout/SettingsPanel.tsx index c7f4d941c..229fbea8d 100644 --- a/app/src/components/settings/layout/SettingsPanel.tsx +++ b/app/src/components/settings/layout/SettingsPanel.tsx @@ -8,7 +8,7 @@ import { findEntryById } from '../settingsRouteRegistry'; import { useSettingsLayout } from './SettingsLayoutContext'; import SettingsSubNav from './SettingsSubNav'; -export interface SettingsPanelProps { +interface SettingsPanelProps { /** * Override the panel title. Defaults to the active route's registry title, so * most panels omit it. Supply it for dynamic sub-pages (profile/agent diff --git a/app/src/components/settings/panels/AIPanel.tsx b/app/src/components/settings/panels/AIPanel.tsx index 4985206cf..074fafe65 100644 --- a/app/src/components/settings/panels/AIPanel.tsx +++ b/app/src/components/settings/panels/AIPanel.tsx @@ -1076,14 +1076,14 @@ const FormulaRow = ({ label, value, detail }: { label: string; value: string; de ); -export type BackgroundLoopControlsView = 'all' | 'heartbeat' | 'ledger'; +type BackgroundLoopControlsView = 'all' | 'heartbeat' | 'ledger'; /** Minimal cloud-provider shape consumed by the loop map's `describeProvider` * helper — only slug/label/id are read. Accepting this narrower shape lets * external panels (UsagePanel) feed in the API view * (`CloudProviderView`) without copying the AIPanel-internal extras * (`authStyle`, `maskedKey`). */ -export type BackgroundLoopProviderView = { id: string; slug: string; label: string }; +type BackgroundLoopProviderView = { id: string; slug: string; label: string }; export const BackgroundLoopControls = ({ routing, diff --git a/app/src/components/settings/panels/AgentEditorPage.test.tsx b/app/src/components/settings/panels/AgentEditorPage.test.tsx index d07328acd..697a1f879 100644 --- a/app/src/components/settings/panels/AgentEditorPage.test.tsx +++ b/app/src/components/settings/panels/AgentEditorPage.test.tsx @@ -23,10 +23,6 @@ vi.mock('react-router-dom', async importOriginal => { return { ...actual, useNavigate: () => mockNavigate }; }); -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>

{title}

, -})); - const mockGet = vi.mocked(agentRegistryApi.get); const mockAvailableTools = vi.mocked(agentRegistryApi.availableTools); const mockCreate = vi.mocked(agentRegistryApi.createCustom); diff --git a/app/src/components/settings/panels/AgentsPanel.test.tsx b/app/src/components/settings/panels/AgentsPanel.test.tsx index 0885a89c1..725c5f83d 100644 --- a/app/src/components/settings/panels/AgentsPanel.test.tsx +++ b/app/src/components/settings/panels/AgentsPanel.test.tsx @@ -32,10 +32,6 @@ vi.mock('../hooks/useSettingsNavigation', () => ({ }), })); -vi.mock('../SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>

{title}

, -})); - const mockList = vi.mocked(agentRegistryApi.list); const mockSetEnabled = vi.mocked(agentRegistryApi.setEnabled); diff --git a/app/src/components/settings/panels/AppearancePanel.test.tsx b/app/src/components/settings/panels/AppearancePanel.test.tsx index e5b952a7d..9b35e9a00 100644 --- a/app/src/components/settings/panels/AppearancePanel.test.tsx +++ b/app/src/components/settings/panels/AppearancePanel.test.tsx @@ -11,10 +11,6 @@ vi.mock('../hooks/useSettingsNavigation', () => ({ useSettingsNavigation: () => ({ breadcrumbs: [], navigateBack: vi.fn() }), })); -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>

{title}

, -})); - function renderPanel( fontSize: 'small' | 'medium' | 'large' | 'xlarge' = 'medium', customFontSizePx: number | null = null diff --git a/app/src/components/settings/panels/CronJobsPanel.test.tsx b/app/src/components/settings/panels/CronJobsPanel.test.tsx index 8338b4a6e..abd47fa2e 100644 --- a/app/src/components/settings/panels/CronJobsPanel.test.tsx +++ b/app/src/components/settings/panels/CronJobsPanel.test.tsx @@ -24,11 +24,6 @@ vi.mock('../../../store/hooks', () => ({ useAppSelector: () => [], })); -// ── Mock SettingsHeader ───────────────────────────────────────────────── -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>
{title}
, -})); - // ── Mock CronJobFormModal ─────────────────────────────────────────────── // The modal is independently tested; here we just verify it opens/closes // and the callbacks fire. diff --git a/app/src/components/settings/panels/DevicesPanel.tsx b/app/src/components/settings/panels/DevicesPanel.tsx index 106386f72..55cbf216d 100644 --- a/app/src/components/settings/panels/DevicesPanel.tsx +++ b/app/src/components/settings/panels/DevicesPanel.tsx @@ -22,7 +22,7 @@ const log = createDebug('app:devices-ui'); // Types (mirror the Rust types.rs) // --------------------------------------------------------------------------- -export interface PairedDevice { +interface PairedDevice { channel_id: string; label: string; device_pubkey: string; diff --git a/app/src/components/settings/panels/NotificationRoutingPanel.test.tsx b/app/src/components/settings/panels/NotificationRoutingPanel.test.tsx index e91907c50..b0e6243c9 100644 --- a/app/src/components/settings/panels/NotificationRoutingPanel.test.tsx +++ b/app/src/components/settings/panels/NotificationRoutingPanel.test.tsx @@ -13,10 +13,6 @@ vi.mock('../hooks/useSettingsNavigation', () => ({ }), })); -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>
{title}
, -})); - const fetchStatsMock = vi.fn(); const getSettingsMock = vi.fn(); const setSettingsMock = vi.fn(); diff --git a/app/src/components/settings/panels/NotificationsPanel.test.tsx b/app/src/components/settings/panels/NotificationsPanel.test.tsx index d61a70998..60fe14961 100644 --- a/app/src/components/settings/panels/NotificationsPanel.test.tsx +++ b/app/src/components/settings/panels/NotificationsPanel.test.tsx @@ -17,10 +17,6 @@ vi.mock('../hooks/useSettingsNavigation', () => ({ }), })); -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>
{title}
, -})); - const getBypassPrefsMock = vi.fn(); const setGlobalDndMock = vi.fn(); vi.mock('../../../services/webviewAccountService', () => ({ diff --git a/app/src/components/settings/panels/PrivacyModeSection.tsx b/app/src/components/settings/panels/PrivacyModeSection.tsx index 3eb01ef83..7eed77a21 100644 --- a/app/src/components/settings/panels/PrivacyModeSection.tsx +++ b/app/src/components/settings/panels/PrivacyModeSection.tsx @@ -9,7 +9,7 @@ import { SettingsSection, SettingsStatusLine } from '../controls'; const log = debug('privacy-mode'); /** Privacy Mode values as serialized by the Rust core (snake_case). */ -export type PrivacyMode = 'local_only' | 'standard' | 'sensitive'; +type PrivacyMode = 'local_only' | 'standard' | 'sensitive'; interface PrivacyModeResult { mode: PrivacyMode; diff --git a/app/src/components/settings/panels/ProfileEditorPage.test.tsx b/app/src/components/settings/panels/ProfileEditorPage.test.tsx index 5c8664278..d1867761f 100644 --- a/app/src/components/settings/panels/ProfileEditorPage.test.tsx +++ b/app/src/components/settings/panels/ProfileEditorPage.test.tsx @@ -19,10 +19,6 @@ vi.mock('react-router-dom', async importOriginal => { return { ...actual, useNavigate: () => mockNavigate }; }); -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>

{title}

, -})); - const mockUpsert = vi.mocked(agentProfilesApi.upsert); function profile(overrides: Partial = {}): AgentProfile { diff --git a/app/src/components/settings/panels/ProfilesPanel.test.tsx b/app/src/components/settings/panels/ProfilesPanel.test.tsx index 824b4744d..c9188de58 100644 --- a/app/src/components/settings/panels/ProfilesPanel.test.tsx +++ b/app/src/components/settings/panels/ProfilesPanel.test.tsx @@ -19,15 +19,6 @@ vi.mock('react-router-dom', async importOriginal => { return { ...actual, useNavigate: () => mockNavigate }; }); -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title, action }: { title: string; action?: React.ReactNode }) => ( -
-

{title}

- {action} -
- ), -})); - const mockList = vi.mocked(agentProfilesApi.list); const mockSelect = vi.mocked(agentProfilesApi.select); const mockDelete = vi.mocked(agentProfilesApi.delete); diff --git a/app/src/components/settings/panels/ProviderSetupErrorNotice.tsx b/app/src/components/settings/panels/ProviderSetupErrorNotice.tsx index cbb6c2a77..4eec9bd70 100644 --- a/app/src/components/settings/panels/ProviderSetupErrorNotice.tsx +++ b/app/src/components/settings/panels/ProviderSetupErrorNotice.tsx @@ -1,6 +1,6 @@ import { useT } from '../../../lib/i18n/I18nContext'; -export type ProviderErrorPresentation = { summary: string; details: string }; +type ProviderErrorPresentation = { summary: string; details: string }; function decodeJsonString(value: string): string { try { diff --git a/app/src/components/settings/panels/TeamInvitesPanel.test.tsx b/app/src/components/settings/panels/TeamInvitesPanel.test.tsx index 4fa3117c0..30de1266f 100644 --- a/app/src/components/settings/panels/TeamInvitesPanel.test.tsx +++ b/app/src/components/settings/panels/TeamInvitesPanel.test.tsx @@ -13,10 +13,6 @@ vi.mock('../hooks/useSettingsNavigation', () => ({ }), })); -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>
{title}
, -})); - const teamApiMock = { createInvite: vi.fn(), revokeInvite: vi.fn() }; vi.mock('../../../services/api/teamApi', () => ({ teamApi: teamApiMock })); diff --git a/app/src/components/settings/panels/TeamManagementPanel.test.tsx b/app/src/components/settings/panels/TeamManagementPanel.test.tsx index 496f1e60b..ee9c1de8e 100644 --- a/app/src/components/settings/panels/TeamManagementPanel.test.tsx +++ b/app/src/components/settings/panels/TeamManagementPanel.test.tsx @@ -14,10 +14,6 @@ vi.mock('../hooks/useSettingsNavigation', () => ({ }), })); -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>
{title}
, -})); - const teamApiMock = { updateTeam: vi.fn(), deleteTeam: vi.fn() }; vi.mock('../../../services/api/teamApi', () => ({ teamApi: teamApiMock })); diff --git a/app/src/components/settings/panels/TeamMembersPanel.test.tsx b/app/src/components/settings/panels/TeamMembersPanel.test.tsx index fbaff65ca..13996ed56 100644 --- a/app/src/components/settings/panels/TeamMembersPanel.test.tsx +++ b/app/src/components/settings/panels/TeamMembersPanel.test.tsx @@ -13,10 +13,6 @@ vi.mock('../hooks/useSettingsNavigation', () => ({ }), })); -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>
{title}
, -})); - const teamApiMock = { changeMemberRole: vi.fn(), removeMember: vi.fn() }; vi.mock('../../../services/api/teamApi', () => ({ teamApi: teamApiMock })); diff --git a/app/src/components/settings/panels/TeamPanel.test.tsx b/app/src/components/settings/panels/TeamPanel.test.tsx index 05b9f07de..2b308bf9f 100644 --- a/app/src/components/settings/panels/TeamPanel.test.tsx +++ b/app/src/components/settings/panels/TeamPanel.test.tsx @@ -14,10 +14,6 @@ vi.mock('../hooks/useSettingsNavigation', () => ({ }), })); -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>
{title}
, -})); - const teamApiMock = { createTeam: vi.fn(), joinTeam: vi.fn(), diff --git a/app/src/components/settings/panels/ToolsPanel.test.tsx b/app/src/components/settings/panels/ToolsPanel.test.tsx index 593ab3a09..5d630eed8 100644 --- a/app/src/components/settings/panels/ToolsPanel.test.tsx +++ b/app/src/components/settings/panels/ToolsPanel.test.tsx @@ -27,10 +27,6 @@ vi.mock('../hooks/useSettingsNavigation', () => ({ useSettingsNavigation: () => ({ breadcrumbs: [], navigateBack: vi.fn() }), })); -vi.mock('../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>

{title}

, -})); - function coreState(enabledTools: string[]) { return { snapshot: { diff --git a/app/src/components/settings/panels/__tests__/ComposioPanel.test.tsx b/app/src/components/settings/panels/__tests__/ComposioPanel.test.tsx index 217baaa3f..efb733cd1 100644 --- a/app/src/components/settings/panels/__tests__/ComposioPanel.test.tsx +++ b/app/src/components/settings/panels/__tests__/ComposioPanel.test.tsx @@ -28,10 +28,6 @@ vi.mock('../../hooks/useSettingsNavigation', () => ({ }), })); -vi.mock('../../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>
{title}
, -})); - // ComposioPanel now embeds the trigger-triage config; stub it so these tests // stay focused on the API-key / routing-mode logic ComposioPanel owns. vi.mock('../ComposioTriagePanel', () => ({ diff --git a/app/src/components/settings/panels/__tests__/ComposioTriagePanel.test.tsx b/app/src/components/settings/panels/__tests__/ComposioTriagePanel.test.tsx index 957bc5470..d0455eb25 100644 --- a/app/src/components/settings/panels/__tests__/ComposioTriagePanel.test.tsx +++ b/app/src/components/settings/panels/__tests__/ComposioTriagePanel.test.tsx @@ -18,10 +18,6 @@ vi.mock('../../hooks/useSettingsNavigation', () => ({ }), })); -vi.mock('../../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>
{title}
, -})); - async function importPanel() { vi.resetModules(); const mod = await import('../ComposioTriagePanel'); diff --git a/app/src/components/settings/panels/__tests__/LocalModelDebugPanel.test.tsx b/app/src/components/settings/panels/__tests__/LocalModelDebugPanel.test.tsx index 866694fa9..ff956e51d 100644 --- a/app/src/components/settings/panels/__tests__/LocalModelDebugPanel.test.tsx +++ b/app/src/components/settings/panels/__tests__/LocalModelDebugPanel.test.tsx @@ -14,8 +14,6 @@ vi.mock('../../hooks/useSettingsNavigation', () => ({ // which is needed by existing tests that assert on actual English text (e.g. 'Test Connection', // 'Reachable', 'http://localhost:11434' placeholder). New tests must also use real strings. -vi.mock('../components/SettingsHeader', () => ({ default: () => null })); - const mockGetConfig = vi.fn(); vi.mock('../../../../utils/tauriCommands/config', () => ({ openhumanGetConfig: (...args: unknown[]) => mockGetConfig(...args), diff --git a/app/src/components/settings/panels/__tests__/ScreenAwarenessDebugPanel.test.tsx b/app/src/components/settings/panels/__tests__/ScreenAwarenessDebugPanel.test.tsx index 53d3c081d..c46763806 100644 --- a/app/src/components/settings/panels/__tests__/ScreenAwarenessDebugPanel.test.tsx +++ b/app/src/components/settings/panels/__tests__/ScreenAwarenessDebugPanel.test.tsx @@ -24,8 +24,6 @@ vi.mock('../../hooks/useSettingsNavigation', () => ({ useSettingsNavigation: () => ({ navigateBack: vi.fn(), breadcrumbs: [] }), })); -vi.mock('../components/SettingsHeader', () => ({ default: () => null })); - vi.mock('../../../../components/intelligence/ScreenIntelligenceDebugPanel', () => ({ default: () =>
debug-content
, })); diff --git a/app/src/components/settings/panels/__tests__/SecurityPanel.test.tsx b/app/src/components/settings/panels/__tests__/SecurityPanel.test.tsx index 99bbfb447..6f3665b98 100644 --- a/app/src/components/settings/panels/__tests__/SecurityPanel.test.tsx +++ b/app/src/components/settings/panels/__tests__/SecurityPanel.test.tsx @@ -34,10 +34,6 @@ vi.mock('../../hooks/useSettingsNavigation', () => ({ useSettingsNavigation: () => ({ navigateBack: vi.fn(), breadcrumbs: [] }), })); -vi.mock('../../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>

{title}

, -})); - const mockUseCoreState = vi.fn(); vi.mock('../../../../providers/CoreStateProvider', () => ({ diff --git a/app/src/components/settings/panels/__tests__/TeamInvitesPanel.test.tsx b/app/src/components/settings/panels/__tests__/TeamInvitesPanel.test.tsx index a9c03f0c9..5dba0f827 100644 --- a/app/src/components/settings/panels/__tests__/TeamInvitesPanel.test.tsx +++ b/app/src/components/settings/panels/__tests__/TeamInvitesPanel.test.tsx @@ -56,7 +56,6 @@ vi.mock('../../hooks/useSettingsNavigation', () => ({ useSettingsNavigation: () => ({ navigateBack: vi.fn(), breadcrumbs: [] }), })); -vi.mock('../../components/SettingsHeader', () => ({ default: () => null })); vi.mock('../../components/SettingsBackButton', () => ({ default: () => null })); vi.mock('react-router-dom', () => ({ diff --git a/app/src/components/settings/panels/__tests__/TeamManagementPanel.test.tsx b/app/src/components/settings/panels/__tests__/TeamManagementPanel.test.tsx index a9f44aaaf..42b6faf95 100644 --- a/app/src/components/settings/panels/__tests__/TeamManagementPanel.test.tsx +++ b/app/src/components/settings/panels/__tests__/TeamManagementPanel.test.tsx @@ -62,9 +62,6 @@ vi.mock('../../hooks/useSettingsNavigation', () => ({ }), })); -vi.mock('../../components/SettingsHeader', () => ({ - default: ({ title }: { title: string }) =>

{title}

, -})); vi.mock('../../components/SettingsBackButton', () => ({ default: () => null })); const mockUpdateTeam = vi.fn(); diff --git a/app/src/components/settings/panels/__tests__/TeamMembersPanel.test.tsx b/app/src/components/settings/panels/__tests__/TeamMembersPanel.test.tsx index 6578ca9ef..469db15f7 100644 --- a/app/src/components/settings/panels/__tests__/TeamMembersPanel.test.tsx +++ b/app/src/components/settings/panels/__tests__/TeamMembersPanel.test.tsx @@ -60,7 +60,6 @@ vi.mock('../../hooks/useSettingsNavigation', () => ({ useSettingsNavigation: () => ({ navigateBack: vi.fn(), breadcrumbs: [] }), })); -vi.mock('../../components/SettingsHeader', () => ({ default: () => null })); vi.mock('../../components/SettingsBackButton', () => ({ default: () => null })); vi.mock('react-router-dom', () => ({ diff --git a/app/src/components/settings/panels/__tests__/TeamPanel.test.tsx b/app/src/components/settings/panels/__tests__/TeamPanel.test.tsx index 48c919bfd..dcfc301a1 100644 --- a/app/src/components/settings/panels/__tests__/TeamPanel.test.tsx +++ b/app/src/components/settings/panels/__tests__/TeamPanel.test.tsx @@ -65,8 +65,6 @@ vi.mock('../../hooks/useSettingsNavigation', () => ({ }), })); -vi.mock('../../components/SettingsHeader', () => ({ default: () => null })); - const mockCreateTeam = vi.fn(); const mockJoinTeam = vi.fn(); const mockSwitchTeam = vi.fn(); diff --git a/app/src/components/settings/panels/aiRouting.ts b/app/src/components/settings/panels/aiRouting.ts index a0248f2c5..d5bb9b306 100644 --- a/app/src/components/settings/panels/aiRouting.ts +++ b/app/src/components/settings/panels/aiRouting.ts @@ -77,7 +77,7 @@ export function isChatSelectableLocalModel(model: { chat_capable?: boolean | nul } /** A locally-installed model mapped to the picker shape consumed by the LLM/chat selectors. */ -export interface SelectableChatModel { +interface SelectableChatModel { id: string; sizeBytes: number; family: string; diff --git a/app/src/components/settings/panels/billing/PayAsYouGoCard.tsx b/app/src/components/settings/panels/billing/PayAsYouGoCard.tsx deleted file mode 100644 index e3e6002ae..000000000 --- a/app/src/components/settings/panels/billing/PayAsYouGoCard.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { useState } from 'react'; - -import { useT } from '../../../../lib/i18n/I18nContext'; -import { type CreditBalance } from '../../../../services/api/creditsApi'; -import Button from '../../../ui/Button'; -import { SettingsTextField } from '../../controls'; - -interface PayAsYouGoCardProps { - creditBalance: CreditBalance | null; - isLoadingCredits: boolean; - isToppingUp: boolean; - onTopUp: (amountUsd: number) => void; -} - -const PayAsYouGoCard = ({ - creditBalance, - isLoadingCredits, - isToppingUp, - onTopUp, -}: PayAsYouGoCardProps) => { - const { t } = useT(); - const promoCredits = creditBalance?.promotionBalanceUsd ?? 0; - const teamTopupCredits = creditBalance?.teamTopupUsd ?? 0; - const availableCredits = promoCredits + teamTopupCredits; - - const [customTopUpAmount, setCustomTopUpAmount] = useState(''); - const customTopUpAmountValid = Number(customTopUpAmount) > 0; - - const handleCustomTopUp = () => { - if (!customTopUpAmountValid || isToppingUp) return; - onTopUp(Number(customTopUpAmount)); - }; - - return ( - <> -
-

- {t('settings.billing.payAsYouGo.creditBalanceTitle')} -

-

- {t('settings.billing.payAsYouGo.creditBalanceDesc')} -

- {creditBalance ? ( -
-
-

- {t('settings.billing.payAsYouGo.available')} -

-

- ${availableCredits.toFixed(2)} -

-
-
-

- {t('settings.billing.payAsYouGo.promotionalCredits')} -

-

- ${promoCredits.toFixed(2)} -

-
-
-

- {t('settings.billing.payAsYouGo.topUpBalance')} -

-

- ${teamTopupCredits.toFixed(2)} -

-
-
- ) : isLoadingCredits ? ( -
- {[0, 1, 2].map(index => ( -
- ))} -
- ) : ( -

- {t('settings.billing.payAsYouGo.unableToLoad')} -

- )} -
-
-

- {t('settings.billing.payAsYouGo.chooseTopUpTitle')} -

-

- {t('settings.billing.payAsYouGo.chooseTopUpDesc')} -

- -
- {[5, 10, 25].map(amount => ( - - ))} -
- -
-
-
- -
- - $ - - setCustomTopUpAmount(e.target.value)} - onKeyDown={e => { - if (e.key === 'Enter') handleCustomTopUp(); - }} - placeholder={t('settings.billing.payAsYouGo.enterAmount')} - className="-ml-1 flex-1" - /> -
-

- {t('settings.billing.payAsYouGo.chooseTopUpDesc')} -

-
- -
-
-
- - ); -}; - -export default PayAsYouGoCard; diff --git a/app/src/components/settings/panels/billingHelpers.ts b/app/src/components/settings/panels/billingHelpers.ts index 9c0d89a75..b267c4e45 100644 --- a/app/src/components/settings/panels/billingHelpers.ts +++ b/app/src/components/settings/panels/billingHelpers.ts @@ -101,12 +101,3 @@ export function annualSavings( export function isUpgrade(targetTier: PlanTier, currentTier: PlanTier): boolean { return tierIndex(targetTier) > tierIndex(currentTier); } - -export function getPlanMeta(tier: PlanTier): PlanMeta | undefined { - return PLANS.find(plan => plan.tier === tier); -} - -export function formatUsdAmount(amount: number): string { - if (Number.isInteger(amount)) return `$${amount}`; - return `$${amount.toFixed(2).replace(/0+$/, '').replace(/\.$/, '')}`; -} diff --git a/app/src/components/settings/panels/builtinCloudProviders.ts b/app/src/components/settings/panels/builtinCloudProviders.ts index 3f6b762db..09b55291f 100644 --- a/app/src/components/settings/panels/builtinCloudProviders.ts +++ b/app/src/components/settings/panels/builtinCloudProviders.ts @@ -1,6 +1,6 @@ import type { AuthStyle } from '../../../utils/tauriCommands/config'; -export type BuiltinCloudProvider = { +type BuiltinCloudProvider = { slug: string; label: string; endpoint: string; diff --git a/app/src/components/settings/panels/elevenlabsVoicePresets.ts b/app/src/components/settings/panels/elevenlabsVoicePresets.ts index ee4054ccd..7078d99e6 100644 --- a/app/src/components/settings/panels/elevenlabsVoicePresets.ts +++ b/app/src/components/settings/panels/elevenlabsVoicePresets.ts @@ -22,7 +22,7 @@ */ import type { Locale } from '../../../lib/i18n/types'; -export interface ElevenLabsVoicePreset { +interface ElevenLabsVoicePreset { /** ElevenLabs voice id — opaque alphanumeric, sent verbatim to the TTS RPC. */ id: string; /** Display label rendered in the dropdown. Includes accent + gender hints. */ @@ -93,7 +93,7 @@ export const ELEVENLABS_VOICE_PRESETS: readonly ElevenLabsVoicePreset[] = [ * `en` covers the default. Other locales fall back to it via * `defaultVoiceIdForLocale()` when a specific entry is missing. */ -export const DEFAULT_VOICE_BY_LOCALE: Readonly< +const DEFAULT_VOICE_BY_LOCALE: Readonly< Partial>>> > = { // Female default: Rachel — neutral, widely-used. Male default: Adam. diff --git a/app/src/components/settings/panels/persona/personaSections.ts b/app/src/components/settings/panels/persona/personaSections.ts index dae3c7fa6..68b96e153 100644 --- a/app/src/components/settings/panels/persona/personaSections.ts +++ b/app/src/components/settings/panels/persona/personaSections.ts @@ -13,7 +13,7 @@ /** Managed field keys the guided builder can edit. */ export type PersonaFieldKey = 'personality' | 'voice' | 'about'; -export interface PersonaSectionDef { +interface PersonaSectionDef { key: PersonaFieldKey; /** Canonical `## ` heading text this field maps to inside SOUL.md. */ heading: string; @@ -24,13 +24,13 @@ export interface PersonaSectionDef { * in the bundled SOUL.md; `About You` is created on demand the first time the * user fills it in. Anything not listed here is preserved verbatim. */ -export const PERSONA_SECTIONS: readonly PersonaSectionDef[] = [ +const PERSONA_SECTIONS: readonly PersonaSectionDef[] = [ { key: 'personality', heading: 'Personality' }, { key: 'voice', heading: 'Voice' }, { key: 'about', heading: 'About You' }, ] as const; -export type PersonaFields = Record; +type PersonaFields = Record; const HEADING_FOR: Record = { personality: 'Personality', diff --git a/app/src/components/settings/panels/persona/personaTemplates.ts b/app/src/components/settings/panels/persona/personaTemplates.ts index c1b16e535..2edef285d 100644 --- a/app/src/components/settings/panels/persona/personaTemplates.ts +++ b/app/src/components/settings/panels/persona/personaTemplates.ts @@ -15,7 +15,7 @@ */ import { applyPersonaField } from './personaSections'; -export interface PersonaTemplate { +interface PersonaTemplate { id: string; labelKey: string; descriptionKey: string; diff --git a/app/src/components/settings/panels/theme/ColorTokenField.tsx b/app/src/components/settings/panels/theme/ColorTokenField.tsx index 63d83f613..f0007195e 100644 --- a/app/src/components/settings/panels/theme/ColorTokenField.tsx +++ b/app/src/components/settings/panels/theme/ColorTokenField.tsx @@ -2,7 +2,7 @@ import { useId } from 'react'; import { channelsToHex, hexToChannels } from '../../../../lib/theme/color'; -export interface ColorTokenFieldProps { +interface ColorTokenFieldProps { /** Token key without `--` (e.g. `surface-canvas`). */ tokenKey: string; /** Human label for the swatch row. */ diff --git a/app/src/components/settings/panels/useReembedBackfillModal.ts b/app/src/components/settings/panels/useReembedBackfillModal.ts index ec5561b9c..8767c632e 100644 --- a/app/src/components/settings/panels/useReembedBackfillModal.ts +++ b/app/src/components/settings/panels/useReembedBackfillModal.ts @@ -12,12 +12,12 @@ import { useCallback, useEffect, useState } from 'react'; import { memoryTreeBackfillStatus } from '../../../utils/tauriCommands/memoryTree'; -export interface ReembedState { +interface ReembedState { open: boolean; pending: number; } -export interface ReembedBackfillModal { +interface ReembedBackfillModal { reembed: ReembedState; /** Wrap a settings-save: persist, then (only on success) surface re-embed progress. */ handleSave: () => Promise; diff --git a/app/src/components/settings/search/useSettingsSearch.ts b/app/src/components/settings/search/useSettingsSearch.ts index 70243912f..c4bfb02bd 100644 --- a/app/src/components/settings/search/useSettingsSearch.ts +++ b/app/src/components/settings/search/useSettingsSearch.ts @@ -20,7 +20,7 @@ import { useDeveloperMode } from '../../../hooks/useDeveloperMode'; import { useT } from '../../../lib/i18n/I18nContext'; import { SETTINGS_SEARCH_ENTRIES, type SettingsSearchEntry } from './settingsSearchRegistry'; -export interface SettingsSearchResult { +interface SettingsSearchResult { entry: SettingsSearchEntry; /** Localised title for rendering. */ title: string; @@ -31,7 +31,7 @@ export interface SettingsSearchResult { } /** Lower-case and strip diacritics so "TÃŗol" matches "tool". */ -export const normalize = (value: string): string => +const normalize = (value: string): string => value .normalize('NFD') // Strip combining diacritical marks (U+0300–U+036F) so "TÃŗol" matches "tool". diff --git a/app/src/components/settings/settingsRouteElements.tsx b/app/src/components/settings/settingsRouteElements.tsx index acd66712a..8a5995296 100644 --- a/app/src/components/settings/settingsRouteElements.tsx +++ b/app/src/components/settings/settingsRouteElements.tsx @@ -49,7 +49,7 @@ import WorkflowRunnerPanel from './panels/WorkflowRunnerPanel'; * internal scroll; legacy panels that overflow scroll here. Either way there's * exactly one scrollbar. */ -export const WrappedSettingsPage = ({ children }: { children: ReactNode }) => { +const WrappedSettingsPage = ({ children }: { children: ReactNode }) => { return
{children}
; }; diff --git a/app/src/components/settings/settingsRouteRegistry.ts b/app/src/components/settings/settingsRouteRegistry.ts index 14ddf0542..fc09b882b 100644 --- a/app/src/components/settings/settingsRouteRegistry.ts +++ b/app/src/components/settings/settingsRouteRegistry.ts @@ -36,7 +36,7 @@ export type SettingsSection = * "System" group's Developer & Diagnostics sub-sections are now first-class * top-level groups. */ -export type SettingsNavGroup = +type SettingsNavGroup = | 'general' | 'assistant' | 'data' @@ -47,7 +47,7 @@ export type SettingsNavGroup = | 'automationIntegrations' | 'diagnosticsLogs'; -export const NAV_GROUP_ORDER: SettingsNavGroup[] = [ +const NAV_GROUP_ORDER: SettingsNavGroup[] = [ 'general', 'assistant', 'data', @@ -73,7 +73,7 @@ export const NAV_GROUP_LABEL_KEY: Record = { diagnosticsLogs: 'settings.devGroups.diagnosticsLogs', }; -export interface SettingsRegistryEntry { +interface SettingsRegistryEntry { /** Stable unique id — used as the React key, test id, and route slug. */ id: string; /** Route segment passed to `navigateToSettings(id)` (defaults to `id`). */ @@ -702,7 +702,7 @@ export const findEntryByRoute = (route: string): SettingsRegistryEntry | undefin // Sidebar helpers (two-pane layout) // --------------------------------------------------------------------------- -export interface SettingsSidebarGroup { +interface SettingsSidebarGroup { group: SettingsNavGroup; entries: SettingsRegistryEntry[]; } diff --git a/app/src/components/shortcuts/shortcutsView.tsx b/app/src/components/shortcuts/shortcutsView.tsx index 752ef55a1..9d8cdee7d 100644 --- a/app/src/components/shortcuts/shortcutsView.tsx +++ b/app/src/components/shortcuts/shortcutsView.tsx @@ -11,7 +11,7 @@ import Kbd from '../commands/Kbd'; // ordering and appends Help. Unknown groups sort alphabetically after these. const DISPLAY_ORDER = ['Navigation', 'Profiles', 'Chat', 'View', 'General', 'Help']; -export interface ShortcutGroup { +interface ShortcutGroup { group: string; items: RegisteredAction[]; } @@ -34,7 +34,7 @@ function getSnapshot(): RegisteredAction[] { * shortcut. Driven by the same registry the command palette uses, so the help * directory can never drift from the real bindings. */ -export function useActiveShortcutGroups(): ShortcutGroup[] { +function useActiveShortcutGroups(): ShortcutGroup[] { const actions = useSyncExternalStore(subscribe, getSnapshot); return useMemo(() => { const byGroup = new Map(); diff --git a/app/src/components/skills/CreateWorkflowForm.tsx b/app/src/components/skills/CreateWorkflowForm.tsx index 137013cb6..89d9039c7 100644 --- a/app/src/components/skills/CreateWorkflowForm.tsx +++ b/app/src/components/skills/CreateWorkflowForm.tsx @@ -78,7 +78,7 @@ function newRow(): InputRow { const log = debug('skills:create-form'); -export interface CreateSkillFormHandle { +interface CreateSkillFormHandle { /** True iff name+description are present and no submit is in flight. */ isValid: () => boolean; /** True while skillsApi.createWorkflow is in flight. */ @@ -87,7 +87,7 @@ export interface CreateSkillFormHandle { submit: () => Promise; } -export interface CreateSkillFormProps { +interface CreateSkillFormProps { /** * The id assigned to the underlying `
` element. Wrappers that * render their submit button outside the form (modal footer / page diff --git a/app/src/components/skills/ScheduledCronCard.tsx b/app/src/components/skills/ScheduledCronCard.tsx index c3e8c6014..2bd9d00c2 100644 --- a/app/src/components/skills/ScheduledCronCard.tsx +++ b/app/src/components/skills/ScheduledCronCard.tsx @@ -41,7 +41,7 @@ import { useT } from '../../lib/i18n/I18nContext'; import type { CoreCronJob } from '../../utils/tauriCommands/cron'; import { formatSchedule } from './scheduledCronFormat'; -export interface ScheduledCronCardProps { +interface ScheduledCronCardProps { /** The cron job this card represents (drives schedule + enable state). */ job: CoreCronJob; /** Visible heading. Defaults to `job.name ?? job.id`. */ diff --git a/app/src/components/skills/SkillCard.tsx b/app/src/components/skills/SkillCard.tsx index 10275f101..ab2e81b6f 100644 --- a/app/src/components/skills/SkillCard.tsx +++ b/app/src/components/skills/SkillCard.tsx @@ -3,7 +3,7 @@ import { type ReactNode, useEffect, useRef, useState } from 'react'; import { useT } from '../../lib/i18n/I18nContext'; import Button from '../ui/Button'; -export interface UnifiedSkillCardProps { +interface UnifiedSkillCardProps { icon: ReactNode; title: string; description: string; @@ -34,7 +34,7 @@ const CTA_STYLES: Record = { amber: 'border-amber-200 bg-amber-50 text-amber-700 hover:bg-amber-100', }; -export function UnifiedSkillCard({ +function UnifiedSkillCard({ icon, title, description, diff --git a/app/src/components/skills/SmartIssuePicker.tsx b/app/src/components/skills/SmartIssuePicker.tsx index cfde8c800..a408169f3 100644 --- a/app/src/components/skills/SmartIssuePicker.tsx +++ b/app/src/components/skills/SmartIssuePicker.tsx @@ -48,7 +48,7 @@ interface GhBranch { name: string; } -export interface SmartIssuePickerProps { +interface SmartIssuePickerProps { /** Current resolved input values (the four dev-workflow fields). */ values: { repo?: string; upstream?: string; target_branch?: string; fork_owner?: string }; /** Patch the parent's form-values map with the picker's resolutions. */ diff --git a/app/src/components/skills/WorkflowRunnerBody.tsx b/app/src/components/skills/WorkflowRunnerBody.tsx index 87865c45e..17301195b 100644 --- a/app/src/components/skills/WorkflowRunnerBody.tsx +++ b/app/src/components/skills/WorkflowRunnerBody.tsx @@ -221,7 +221,7 @@ function inferRuntimeRequirement(skill?: WorkflowSummary): 'node' | 'python' | ' // ── Component ────────────────────────────────────────────────────────── -export interface SkillsRunnerBodyProps { +interface SkillsRunnerBodyProps { /** * Optional override for the descriptive header text rendered above * the skill picker. Defaults to the Settings-panel description so @@ -239,7 +239,7 @@ export interface SkillsRunnerBodyProps { className?: string; } -export const WorkflowRunnerBody = ({ headerText, className }: SkillsRunnerBodyProps) => { +const WorkflowRunnerBody = ({ headerText, className }: SkillsRunnerBodyProps) => { const { t } = useT(); // Skill catalog (loaded once on mount) diff --git a/app/src/components/skills/__tests__/WorkflowRunnerBody.test.tsx b/app/src/components/skills/__tests__/WorkflowRunnerBody.test.tsx index ded274af5..a18edd80b 100644 --- a/app/src/components/skills/__tests__/WorkflowRunnerBody.test.tsx +++ b/app/src/components/skills/__tests__/WorkflowRunnerBody.test.tsx @@ -137,7 +137,7 @@ function makeJob(overrides: Partial> = {}) { async function importBody() { const mod = await import('../WorkflowRunnerBody'); - return mod.WorkflowRunnerBody; + return mod.default; } /** diff --git a/app/src/components/skills/inputs/BranchPicker.tsx b/app/src/components/skills/inputs/BranchPicker.tsx index 14f7ae42e..480354c64 100644 --- a/app/src/components/skills/inputs/BranchPicker.tsx +++ b/app/src/components/skills/inputs/BranchPicker.tsx @@ -22,7 +22,7 @@ interface GhBranch { name: string; } -export interface BranchPickerProps { +interface BranchPickerProps { /** Selected branch name (or empty). */ value: string; /** Fires with the picked branch name. */ diff --git a/app/src/components/skills/inputs/RepoPicker.tsx b/app/src/components/skills/inputs/RepoPicker.tsx index 8045ea649..948ce5780 100644 --- a/app/src/components/skills/inputs/RepoPicker.tsx +++ b/app/src/components/skills/inputs/RepoPicker.tsx @@ -22,7 +22,7 @@ import { useT } from '../../../lib/i18n/I18nContext'; const log = createDebug('app:skills:RepoPicker'); /** Shape returned by `openhuman.composio_list_github_repos`. */ -export interface ComposioGhRepo { +interface ComposioGhRepo { owner: string; repo: string; fullName: string; @@ -31,7 +31,7 @@ export interface ComposioGhRepo { htmlUrl?: string; } -export interface RepoPickerProps { +interface RepoPickerProps { /** Currently-selected `owner/name` (or empty). */ value: string; /** Fires with the picked `owner/name`. */ diff --git a/app/src/components/skills/preflightGate.ts b/app/src/components/skills/preflightGate.ts index c1ec212ee..03f47904c 100644 --- a/app/src/components/skills/preflightGate.ts +++ b/app/src/components/skills/preflightGate.ts @@ -23,7 +23,7 @@ const PREFLIGHT_PREFIX_RE = /^\[preflight:([a-z0-9_-]+):([a-z0-9_-]+)\]\s+/i; /** Parsed shape of a backend RPC error returned by `openhuman.skill_runtime_run`. */ -export interface WorkflowRunError { +interface WorkflowRunError { /** `'github'` when this is a github-gate failure; `null` for any other error. */ gate: string | null; /** diff --git a/app/src/components/skills/skillIcons.tsx b/app/src/components/skills/skillIcons.tsx index 3660cfac1..4f4d6b4eb 100644 --- a/app/src/components/skills/skillIcons.tsx +++ b/app/src/components/skills/skillIcons.tsx @@ -22,7 +22,7 @@ function iconClasses(...parts: Array): string { return parts.filter(Boolean).join(' '); } -export function SkillIconBadge({ +function SkillIconBadge({ icon: Icon, label, bgClassName, diff --git a/app/src/components/ui/Button.tsx b/app/src/components/ui/Button.tsx index 9972f7524..a4c242d37 100644 --- a/app/src/components/ui/Button.tsx +++ b/app/src/components/ui/Button.tsx @@ -14,11 +14,11 @@ import { type ButtonHTMLAttributes, forwardRef, type ReactNode } from 'react'; * Use `iconOnly` for icon-only affordances (close / refresh / add); it squares * the padding — always pass an `aria-label` in that case. */ -export type ButtonVariant = 'primary' | 'secondary' | 'tertiary'; -export type ButtonTone = 'default' | 'danger'; -export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; +type ButtonVariant = 'primary' | 'secondary' | 'tertiary'; +type ButtonTone = 'default' | 'danger'; +type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; -export interface ButtonProps extends ButtonHTMLAttributes { +interface ButtonProps extends ButtonHTMLAttributes { variant?: ButtonVariant; tone?: ButtonTone; size?: ButtonSize; diff --git a/app/src/components/ui/Card.tsx b/app/src/components/ui/Card.tsx deleted file mode 100644 index 35f802e8a..000000000 --- a/app/src/components/ui/Card.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'; - -export type CardVariant = 'surface' | 'elevated' | 'outlined' | 'subtle'; -export type CardPadding = 'none' | 'sm' | 'md' | 'lg'; - -export interface CardProps extends HTMLAttributes { - variant?: CardVariant; - padding?: CardPadding; - children?: ReactNode; -} - -const VARIANTS: Record = { - surface: 'bg-surface border border-line', - elevated: 'bg-surface border border-line shadow-soft dark:shadow-none', - outlined: 'bg-transparent border border-line', - subtle: 'bg-surface-muted border border-line-subtle', -}; - -const PADDINGS: Record = { none: '', sm: 'p-3', md: 'p-4', lg: 'p-6' }; - -const Card = forwardRef((props, ref) => { - const { variant = 'surface', padding = 'md', className, children, ...rest } = props; - const classes = ['rounded-xl text-content', VARIANTS[variant], PADDINGS[padding], className ?? ''] - .filter(Boolean) - .join(' '); - return ( -
- {children} -
- ); -}); -Card.displayName = 'Card'; - -export default Card; diff --git a/app/src/components/ui/Tooltip.tsx b/app/src/components/ui/Tooltip.tsx index 8bb63d42e..0af193304 100644 --- a/app/src/components/ui/Tooltip.tsx +++ b/app/src/components/ui/Tooltip.tsx @@ -15,9 +15,9 @@ import { createPortal } from 'react-dom'; const log = debug('ui:tooltip'); /** Which edge of the trigger the tooltip floats from. Sidebar icons use `right`. */ -export type TooltipSide = 'right' | 'top' | 'bottom' | 'left'; +type TooltipSide = 'right' | 'top' | 'bottom' | 'left'; -export interface TooltipProps { +interface TooltipProps { /** Short, minimal label — e.g. "Wallet", "Settings". */ label: string; /** Single focusable trigger (a button/anchor). Its hover/focus drives the tip. */ diff --git a/app/src/components/ui/index.ts b/app/src/components/ui/index.ts index e8c6f0561..fd30f5f1c 100644 --- a/app/src/components/ui/index.ts +++ b/app/src/components/ui/index.ts @@ -1,14 +1,4 @@ -export { default as Button } from './Button'; -export type { ButtonProps, ButtonVariant, ButtonSize } from './Button'; - -export { default as Card } from './Card'; -export type { CardProps, CardVariant, CardPadding } from './Card'; - -export { default as Input } from './Input'; -export type { InputProps, InputSize } from './Input'; - export { default as Tooltip } from './Tooltip'; -export type { TooltipProps, TooltipSide } from './Tooltip'; export { Spinner, CheckIcon, CloseIcon, WarningIcon } from './icons'; export { CenteredLoadingState, ErrorBanner, InlineLoadingStatus } from './LoadingState'; diff --git a/app/src/components/userErrors/UserErrorCenter.tsx b/app/src/components/userErrors/UserErrorCenter.tsx index 91e74e53b..caea70c32 100644 --- a/app/src/components/userErrors/UserErrorCenter.tsx +++ b/app/src/components/userErrors/UserErrorCenter.tsx @@ -41,7 +41,7 @@ const ACTION_LABEL_KEY: Record, string> = { // (react-hooks/purity); the calls below run only from event handlers. const nowMs = (): number => Date.now(); -export function UserErrorCenter() { +function UserErrorCenter() { const { t } = useT(); const dispatch = useAppDispatch(); const navigate = useNavigate(); diff --git a/app/src/components/worktree/WorktreeActions.tsx b/app/src/components/worktree/WorktreeActions.tsx index fbb939545..b0a7f054d 100644 --- a/app/src/components/worktree/WorktreeActions.tsx +++ b/app/src/components/worktree/WorktreeActions.tsx @@ -19,7 +19,7 @@ import { revealPath } from '../../utils/openUrl'; const log = debug('worktree:actions'); -export interface WorktreeActionsProps { +interface WorktreeActionsProps { /** Absolute worktree checkout path. */ path: string; /** Whether the worktree has uncommitted changes. */ diff --git a/app/src/config/navConfig.ts b/app/src/config/navConfig.ts index d5b5915d1..bebd91487 100644 --- a/app/src/config/navConfig.ts +++ b/app/src/config/navConfig.ts @@ -66,9 +66,9 @@ export const NAV_TABS: NavTab[] = [ * - `navigate` — internal `react-router-dom` navigation to `target`. * - `openUrl` — opens `target` in the system browser via `openUrl()`. */ -export type AvatarMenuItemKind = 'navigate' | 'openUrl'; +type AvatarMenuItemKind = 'navigate' | 'openUrl'; -export interface AvatarMenuItem { +interface AvatarMenuItem { /** Stable identifier. */ id: string; /** i18n key resolved by the consuming component. */ diff --git a/app/src/features/conversations/components/BackgroundProcessesPanel.tsx b/app/src/features/conversations/components/BackgroundProcessesPanel.tsx index 7f9a84bea..087de6bea 100644 --- a/app/src/features/conversations/components/BackgroundProcessesPanel.tsx +++ b/app/src/features/conversations/components/BackgroundProcessesPanel.tsx @@ -108,7 +108,7 @@ function statusStyle(status: ToolTimelineEntryStatus): { } } -export interface BackgroundProcessesPanelProps { +interface BackgroundProcessesPanelProps { open: boolean; processes: BackgroundProcess[]; onClose: () => void; diff --git a/app/src/features/conversations/components/ChatThreadView.tsx b/app/src/features/conversations/components/ChatThreadView.tsx index c72cc3262..ea5579545 100644 --- a/app/src/features/conversations/components/ChatThreadView.tsx +++ b/app/src/features/conversations/components/ChatThreadView.tsx @@ -112,7 +112,7 @@ export interface ChatThreadViewHandle { openBackgroundProcesses(): void; } -export interface ChatThreadViewProps { +interface ChatThreadViewProps { /** Thread whose transcript is rendered. `null` renders the empty state * (no thread selected). */ threadId: string | null; diff --git a/app/src/features/conversations/components/LimitPill.tsx b/app/src/features/conversations/components/LimitPill.tsx deleted file mode 100644 index 30054110f..000000000 --- a/app/src/features/conversations/components/LimitPill.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export function LimitPill({ label, usedPct }: { label: string; usedPct: number }) { - const barColor = - usedPct >= 1 ? 'bg-coral-500' : usedPct >= 0.8 ? 'bg-amber-500' : 'bg-primary-500'; - return ( -
- {label} -
-
-
- - {Math.round(usedPct * 100)}% - -
- ); -} diff --git a/app/src/features/conversations/components/PlanReviewCard.tsx b/app/src/features/conversations/components/PlanReviewCard.tsx index 117bcd3fe..64678c750 100644 --- a/app/src/features/conversations/components/PlanReviewCard.tsx +++ b/app/src/features/conversations/components/PlanReviewCard.tsx @@ -158,5 +158,3 @@ export const PlanReviewCard: React.FC = ({ threadId, review }) => {
); }; - -export default PlanReviewCard; diff --git a/app/src/features/conversations/components/ThreadGoalChip.tsx b/app/src/features/conversations/components/ThreadGoalChip.tsx index 1eae120a6..7db7e5157 100644 --- a/app/src/features/conversations/components/ThreadGoalChip.tsx +++ b/app/src/features/conversations/components/ThreadGoalChip.tsx @@ -27,7 +27,7 @@ import { const POLL_INTERVAL_MS = 10_000; /** Shared controller returned by {@link useThreadGoal}. */ -export interface ThreadGoalController { +interface ThreadGoalController { threadId: string | null; goal: ThreadGoal | null; /** Whether the editor panel (above the composer) is open. */ diff --git a/app/src/features/conversations/components/ThreadTodoStrip.tsx b/app/src/features/conversations/components/ThreadTodoStrip.tsx index b7d91d4ff..79ddb3224 100644 --- a/app/src/features/conversations/components/ThreadTodoStrip.tsx +++ b/app/src/features/conversations/components/ThreadTodoStrip.tsx @@ -191,5 +191,3 @@ function cardLabel(card: TaskBoardCard): string { if (objective) return objective; return card.id; } - -export default ThreadTodoStrip; diff --git a/app/src/features/conversations/components/ToolTimelineBlock.tsx b/app/src/features/conversations/components/ToolTimelineBlock.tsx index a3109d3b4..f1cb6d0ca 100644 --- a/app/src/features/conversations/components/ToolTimelineBlock.tsx +++ b/app/src/features/conversations/components/ToolTimelineBlock.tsx @@ -394,7 +394,7 @@ interface CoalescedRow { * same status, no unique body (result/detail/sub-agent/failure), and never the * live `running` row — so no information is lost, only duplication. */ -export function coalesceTimelineEntries(entries: ToolTimelineEntry[]): CoalescedRow[] { +function coalesceTimelineEntries(entries: ToolTimelineEntry[]): CoalescedRow[] { const rows: CoalescedRow[] = []; for (const entry of entries) { const mergeable = entry.status !== 'running' && !entryHasUniqueBody(entry); diff --git a/app/src/features/conversations/composerSendDecision.ts b/app/src/features/conversations/composerSendDecision.ts index 4c55e9e01..8c3a50675 100644 --- a/app/src/features/conversations/composerSendDecision.ts +++ b/app/src/features/conversations/composerSendDecision.ts @@ -1,13 +1,13 @@ -export type ComposerSendBlockReason = +type ComposerSendBlockReason = | 'empty_input' | 'missing_thread' | 'composer_blocked' | 'usage_limit_reached' | 'socket_disconnected'; -export type SlashCommandDecision = { kind: 'new_or_clear' } | { kind: 'not_handled' }; +type SlashCommandDecision = { kind: 'new_or_clear' } | { kind: 'not_handled' }; -export interface ComposerSendDecisionArgs { +interface ComposerSendDecisionArgs { rawText: string; selectedThreadId: string | null; composerInteractionBlocked: boolean; @@ -15,17 +15,17 @@ export interface ComposerSendDecisionArgs { socketStatus: string; } -export interface ComposerSendDecision { +interface ComposerSendDecision { shouldSend: boolean; trimmedText: string; blockReason?: ComposerSendBlockReason; } -export interface ComposerBlockedSendFeedback { +interface ComposerBlockedSendFeedback { error: { code: 'usage_limit_reached' | 'socket_disconnected'; message: string }; } -export interface ComposerKeyDownEventLike { +interface ComposerKeyDownEventLike { key: string; shiftKey?: boolean; isComposing?: boolean; @@ -67,7 +67,7 @@ export const evaluateComposerSend = (args: ComposerSendDecisionArgs): ComposerSe return { shouldSend: true, trimmedText }; }; -export const isComposerImeComposing = ( +const isComposerImeComposing = ( event: ComposerKeyDownEventLike, compositionActive = false ): boolean => diff --git a/app/src/features/conversations/derived/mapDisplayItems.ts b/app/src/features/conversations/derived/mapDisplayItems.ts index 3e9e9adef..74399d0e5 100644 --- a/app/src/features/conversations/derived/mapDisplayItems.ts +++ b/app/src/features/conversations/derived/mapDisplayItems.ts @@ -41,7 +41,7 @@ import { formatTimelineEntry } from '../../../utils/toolTimelineFormatting'; const log = debug('conversations.derived.mapDisplayItems'); /** A partial assistant answer left behind by an interrupted turn. */ -export interface DerivedInterruptedAnswer { +interface DerivedInterruptedAnswer { requestId: string; content: string; thinking: string; @@ -52,7 +52,7 @@ export interface DerivedInterruptedAnswer { * projection, ready to feed `setTurnTimelinesForThread` (timelines + * transcripts) and, for completeness, any interrupted partials found. */ -export interface MappedTranscript { +interface MappedTranscript { /** `requestId -> tool timeline rows` for each settled turn. */ timelines: Record; /** `requestId -> processing transcript` (narration / thinking / tool ptr). */ @@ -67,7 +67,7 @@ export interface MappedTranscript { } /** Options controlling {@link mapDisplayItems}. */ -export interface MapDisplayItemsOptions { +interface MapDisplayItemsOptions { /** * Request ids to omit from the output entirely — the live/most-recent turn * (rendered from socket-fed state / the turn_state snapshot) and any turn diff --git a/app/src/features/conversations/hooks/useBackgroundActivity.ts b/app/src/features/conversations/hooks/useBackgroundActivity.ts index d81f18d57..f228af36a 100644 --- a/app/src/features/conversations/hooks/useBackgroundActivity.ts +++ b/app/src/features/conversations/hooks/useBackgroundActivity.ts @@ -43,7 +43,7 @@ export interface MemorySyncSummary { providers: MemorySyncStatusRow[]; } -export interface BackgroundActivity { +interface BackgroundActivity { cronJobs: CoreCronJob[]; subconscious: SubconsciousSummary | null; memory: MemorySyncSummary; diff --git a/app/src/features/conversations/taskPlanActions.ts b/app/src/features/conversations/taskPlanActions.ts index ac0bfe860..316bb8528 100644 --- a/app/src/features/conversations/taskPlanActions.ts +++ b/app/src/features/conversations/taskPlanActions.ts @@ -11,7 +11,7 @@ import type { TaskBoardCard } from '../../types/turnState'; const debug = debugFactory('conversations:taskPlan'); -export interface RunDecidePlanArgs { +interface RunDecidePlanArgs { /** Active thread; a no-op when null (nothing to decide against). */ threadId: string | null; card: TaskBoardCard; diff --git a/app/src/features/conversations/threadList/ThreadList.tsx b/app/src/features/conversations/threadList/ThreadList.tsx index ee6dc2d34..92347bca2 100644 --- a/app/src/features/conversations/threadList/ThreadList.tsx +++ b/app/src/features/conversations/threadList/ThreadList.tsx @@ -4,7 +4,7 @@ import { useT } from '../../../lib/i18n/I18nContext'; import type { Thread } from '../../../types/thread'; import { isImeCompositionKeyEvent } from '../Conversations'; -export interface ThreadListProps { +interface ThreadListProps { /** Threads visible after the sidebar's search/tab filtering. */ threads: Thread[]; selectedThreadId: string | null; diff --git a/app/src/features/conversations/timeline/ConversationTimeline.tsx b/app/src/features/conversations/timeline/ConversationTimeline.tsx index 1014172f1..565bfd2d9 100644 --- a/app/src/features/conversations/timeline/ConversationTimeline.tsx +++ b/app/src/features/conversations/timeline/ConversationTimeline.tsx @@ -5,7 +5,7 @@ import { StreamingTailItem } from './items/StreamingTailItem'; import { UserMessageItem } from './items/UserMessageItem'; import type { TimelineItem } from './types'; -export interface ConversationTimelineHandlers { +interface ConversationTimelineHandlers { /** Opens the full-transcript drawer for a subagent row. */ onOpenSubagent?: (subagent: SubagentActivity) => void; /** Opens the whole-run "Agent Process Source" panel. */ diff --git a/app/src/features/conversations/timeline/selectors.ts b/app/src/features/conversations/timeline/selectors.ts index b318d2928..d6ae1016d 100644 --- a/app/src/features/conversations/timeline/selectors.ts +++ b/app/src/features/conversations/timeline/selectors.ts @@ -34,7 +34,7 @@ import { toTimelineToolStatus, } from './types'; -export interface BuildThreadTimelineInput { +interface BuildThreadTimelineInput { threadId: string; messages: ThreadMessage[]; toolTimeline: ToolTimelineEntry[]; diff --git a/app/src/features/conversations/timeline/types.ts b/app/src/features/conversations/timeline/types.ts index c495b5585..b82251334 100644 --- a/app/src/features/conversations/timeline/types.ts +++ b/app/src/features/conversations/timeline/types.ts @@ -71,11 +71,6 @@ export type TimelineItem = TimelineItemBase & } ); -export type TimelineItemKind = TimelineItem['kind']; - -/** The "agent process" kinds that `hideAgentInsights` suppresses. */ -export const AGENT_INSIGHT_KINDS: readonly TimelineItemKind[] = ['toolCall', 'subagentActivity']; - /** A contiguous group of items sharing a `turnId`, in render order. */ export interface TimelineTurn { turnId: string; diff --git a/app/src/features/conversations/utils/format.ts b/app/src/features/conversations/utils/format.ts index bd92dde73..0d7d6dc22 100644 --- a/app/src/features/conversations/utils/format.ts +++ b/app/src/features/conversations/utils/format.ts @@ -11,54 +11,6 @@ export function formatRelativeTime(dateStr: string): string { return `${days}d ago`; } -export function getInlineCompletionSuffix(input: string, suggestion: string): string { - if (!input || !suggestion) return ''; - const normalize = (value: string) => - value - .replace(/\u2192/g, ' ') - .replace(/\s+/g, ' ') - .trim(); - - const normalizedInput = normalize(input); - const normalizedSuggestion = normalize(suggestion); - if (!normalizedSuggestion) return ''; - - if (normalizedSuggestion.startsWith(normalizedInput)) { - return normalizedSuggestion.slice(normalizedInput.length).trimStart(); - } - - const maxOverlap = Math.min(normalizedInput.length, normalizedSuggestion.length, 120); - for (let overlap = maxOverlap; overlap >= 1; overlap -= 1) { - if ( - normalizedInput.slice(normalizedInput.length - overlap) === - normalizedSuggestion.slice(0, overlap) - ) { - return normalizedSuggestion.slice(overlap).trimStart(); - } - } - - if (normalizedInput.endsWith(normalizedSuggestion)) { - return ''; - } - return normalizedSuggestion; -} - -export function buildAcceptedInlineCompletion(input: string, suffix: string): string { - const normalizedInput = input.replace(/\u2192/g, ' ').replace(/\t+/g, ' '); - const cleanSuffix = suffix - .replace(/\u2192/g, ' ') - .replace(/\t+/g, ' ') - .replace(/\s+/g, ' ') - .trim(); - - if (!cleanSuffix) return normalizedInput; - - const needsSpace = - normalizedInput.length > 0 && !/\s$/.test(normalizedInput) && !/^[,.;:!?)]/.test(cleanSuffix); - - return `${normalizedInput}${needsSpace ? ' ' : ''}${cleanSuffix}`; -} - export function isAllowedExternalHref(rawHref: string): boolean { try { const url = new URL(rawHref); @@ -83,18 +35,18 @@ export function isAllowedExternalHref(rawHref: string): boolean { * Path is the hash route under HashRouter (e.g. `settings/notifications` * → `#/settings/notifications`). Leading/trailing slashes are tolerated. */ -export interface OpenhumanLinkSegment { +interface OpenhumanLinkSegment { kind: 'link'; path: string; label: string; } -export interface TextSegment { +interface TextSegment { kind: 'text'; text: string; } -export type BubbleSegment = TextSegment | OpenhumanLinkSegment; +type BubbleSegment = TextSegment | OpenhumanLinkSegment; const OPENHUMAN_LINK_RE = /([\s\S]*?)<\/openhuman-link>/gi; diff --git a/app/src/features/conversations/utils/threadFilter.ts b/app/src/features/conversations/utils/threadFilter.ts index 4c2c4ab66..e76c38ec5 100644 --- a/app/src/features/conversations/utils/threadFilter.ts +++ b/app/src/features/conversations/utils/threadFilter.ts @@ -3,9 +3,8 @@ import type { Thread } from '../../../types/thread'; export const GENERAL_TAB_VALUE = 'general'; export const SUBCONSCIOUS_TAB_VALUE = 'subconscious'; export const TASKS_TAB_VALUE = 'tasks'; -export const LEGACY_GENERAL_LABEL = 'work'; -export const LEGACY_SUBCONSCIOUS_LABELS = ['from_reflection', 'subconscious_tick']; -export const LEGACY_TASK_LABELS = ['agent-task', 'worker']; +const LEGACY_SUBCONSCIOUS_LABELS = ['from_reflection', 'subconscious_tick']; +const LEGACY_TASK_LABELS = ['agent-task', 'worker']; /** Labels that identify meeting transcript threads (now folded into Tasks). */ const MEETINGS_LABELS = ['meetings', 'Meetings']; @@ -17,7 +16,7 @@ function isSubconsciousThread(thread: Thread): boolean { return hasAnyLabel(thread, [SUBCONSCIOUS_TAB_VALUE, ...LEGACY_SUBCONSCIOUS_LABELS]); } -export function isTaskThread(thread: Thread): boolean { +function isTaskThread(thread: Thread): boolean { return Boolean( thread.parentThreadId || hasAnyLabel(thread, [TASKS_TAB_VALUE, ...LEGACY_TASK_LABELS, ...MEETINGS_LABELS]) diff --git a/app/src/features/conversations/utils/workerThreadRef.ts b/app/src/features/conversations/utils/workerThreadRef.ts index 4bdfa6f80..af39cb057 100644 --- a/app/src/features/conversations/utils/workerThreadRef.ts +++ b/app/src/features/conversations/utils/workerThreadRef.ts @@ -16,7 +16,7 @@ export interface WorkerThreadRef { iterations?: number; } -export interface ParsedWorkerThreadRef { +interface ParsedWorkerThreadRef { /** The text that appeared before the envelope (model-readable summary). */ before: string; /** The decoded reference, if the envelope parsed cleanly. */ diff --git a/app/src/features/daemon/store.ts b/app/src/features/daemon/store.ts index 3e9f97f0a..5a0bb3e95 100644 --- a/app/src/features/daemon/store.ts +++ b/app/src/features/daemon/store.ts @@ -1,6 +1,6 @@ import { useSyncExternalStore } from 'react'; -export type DaemonStatus = 'starting' | 'stopping' | 'running' | 'error' | 'disconnected'; +type DaemonStatus = 'starting' | 'stopping' | 'running' | 'error' | 'disconnected'; export type ComponentStatus = 'ok' | 'error' | 'starting'; export interface ComponentHealth { @@ -18,7 +18,7 @@ export interface HealthSnapshot { components: Record; } -export interface DaemonUserState { +interface DaemonUserState { status: DaemonStatus; healthSnapshot: HealthSnapshot | null; components: { @@ -70,14 +70,14 @@ const updateUserState = ( emitChange(); }; -export const subscribeDaemonStore = (listener: () => void): (() => void) => { +const subscribeDaemonStore = (listener: () => void): (() => void) => { listeners.add(listener); return () => { listeners.delete(listener); }; }; -export const getDaemonUserState = (userId?: string): DaemonUserState => +const getDaemonUserState = (userId?: string): DaemonUserState => currentUserState(userId || '__pending__'); export const useDaemonUserState = (userId?: string): DaemonUserState => diff --git a/app/src/features/human/Mascot/CustomGifMascot.tsx b/app/src/features/human/Mascot/CustomGifMascot.tsx index 48847d84e..6e4e6ff4f 100644 --- a/app/src/features/human/Mascot/CustomGifMascot.tsx +++ b/app/src/features/human/Mascot/CustomGifMascot.tsx @@ -1,6 +1,6 @@ import type { MascotFace } from './Ghosty'; -export interface CustomGifMascotProps { +interface CustomGifMascotProps { src: string; face?: MascotFace; } diff --git a/app/src/features/human/Mascot/Defs.tsx b/app/src/features/human/Mascot/Defs.tsx index 94a449350..8e0c7b061 100644 --- a/app/src/features/human/Mascot/Defs.tsx +++ b/app/src/features/human/Mascot/Defs.tsx @@ -12,7 +12,7 @@ import { BODY_PATH } from './paths'; */ export type GhostyVariant = 'shaded' | 'flat'; -export interface GhostyDefsProps { +interface GhostyDefsProps { idPrefix: string; bodyColor: string; variant?: GhostyVariant; diff --git a/app/src/features/human/Mascot/Ghosty.tsx b/app/src/features/human/Mascot/Ghosty.tsx index a96dd02bf..e83a24cb4 100644 --- a/app/src/features/human/Mascot/Ghosty.tsx +++ b/app/src/features/human/Mascot/Ghosty.tsx @@ -57,7 +57,7 @@ export type MascotFace = | 'drinking_boba' | 'normal'; -export interface GhostyProps { +interface GhostyProps { bodyColor?: string; blushColor?: string; arm?: 'wave' | 'none'; diff --git a/app/src/features/human/Mascot/ManifestRiveMascot.tsx b/app/src/features/human/Mascot/ManifestRiveMascot.tsx index f3396fb08..df4e9be2b 100644 --- a/app/src/features/human/Mascot/ManifestRiveMascot.tsx +++ b/app/src/features/human/Mascot/ManifestRiveMascot.tsx @@ -51,7 +51,7 @@ function matchEnumValue(value: string, options: readonly string[] | undefined): const RIVE_LAYOUT = new Layout({ fit: Fit.Contain }); -export interface ManifestRiveMascotProps { +interface ManifestRiveMascotProps { /** The manifest entry to render. Its runtime `.riv` is loaded + cached. */ entry: MascotManifestEntry; face?: MascotFace; diff --git a/app/src/features/human/Mascot/MascotChipAvatar.tsx b/app/src/features/human/Mascot/MascotChipAvatar.tsx index 472bbaec9..ef76821c0 100644 --- a/app/src/features/human/Mascot/MascotChipAvatar.tsx +++ b/app/src/features/human/Mascot/MascotChipAvatar.tsx @@ -16,7 +16,7 @@ import { type FC } from 'react'; import { Ghosty } from './Ghosty'; import { getMascotPalette, type MascotColor } from './mascotPalette'; -export interface MascotChipAvatarProps { +interface MascotChipAvatarProps { /** The user's selected mascot colour theme. */ color: MascotColor; /** Custom primary body colour, used only when `color === 'custom'`. */ @@ -65,5 +65,3 @@ export const MascotChipAvatar: FC = ({ ); }; - -export default MascotChipAvatar; diff --git a/app/src/features/human/Mascot/RiveMascot.tsx b/app/src/features/human/Mascot/RiveMascot.tsx index 98ccf1928..2d87834bf 100644 --- a/app/src/features/human/Mascot/RiveMascot.tsx +++ b/app/src/features/human/Mascot/RiveMascot.tsx @@ -34,9 +34,9 @@ function randBetween(min: number, max: number): number { } /** Bundled default mascot, served by Vite from `public/`. */ -export const DEFAULT_MASCOT_SRC = '/tiny_mascot.riv'; +const DEFAULT_MASCOT_SRC = '/tiny_mascot.riv'; -export interface RiveMascotProps { +interface RiveMascotProps { face?: MascotFace; size?: number | string; primaryColor?: number; diff --git a/app/src/features/human/Mascot/backend/BackendMascot.tsx b/app/src/features/human/Mascot/backend/BackendMascot.tsx index e7c1efbec..0fbb0ff93 100644 --- a/app/src/features/human/Mascot/backend/BackendMascot.tsx +++ b/app/src/features/human/Mascot/backend/BackendMascot.tsx @@ -17,7 +17,7 @@ import { injectViseme, tweenTransform } from './renderCore'; import { sanitizeSvg } from './sanitizeSvg'; import type { MascotDetail, MascotState } from './types'; -export interface BackendMascotProps { +interface BackendMascotProps { mascot: MascotDetail; /** Active state id. Falls back to `mascot.defaultState` when unknown. */ stateId?: string; diff --git a/app/src/features/human/Mascot/backend/BackendRiveMascot.tsx b/app/src/features/human/Mascot/backend/BackendRiveMascot.tsx index c977b4144..e9f396b7d 100644 --- a/app/src/features/human/Mascot/backend/BackendRiveMascot.tsx +++ b/app/src/features/human/Mascot/backend/BackendRiveMascot.tsx @@ -14,7 +14,7 @@ import { isRiveMascotDetail } from './types'; const log = debug('human:mascot:backend-rive'); -export interface BackendRiveMascotProps { +interface BackendRiveMascotProps { mascotId: string; face?: MascotFace; size?: number | string; diff --git a/app/src/features/human/Mascot/backend/renderCore.ts b/app/src/features/human/Mascot/backend/renderCore.ts index 35affbabc..956b96426 100644 --- a/app/src/features/human/Mascot/backend/renderCore.ts +++ b/app/src/features/human/Mascot/backend/renderCore.ts @@ -92,7 +92,7 @@ export function injectViseme(svg: string, mascot: VisemeCarrier, label: string | } /** Generic single-attribute string set on the element with the given id. */ -export function setAttrOnId(svg: string, id: string, name: string, value: string): string { +function setAttrOnId(svg: string, id: string, name: string, value: string): string { // Lazy `[^>]*?` + greedy `\/?>` ordering avoids swallowing the `/` of // self-closing tags (``) and emitting malformed // `attr"/ transform="...">` output. diff --git a/app/src/features/human/Mascot/index.ts b/app/src/features/human/Mascot/index.ts index 0985f7247..a031546d2 100644 --- a/app/src/features/human/Mascot/index.ts +++ b/app/src/features/human/Mascot/index.ts @@ -1,28 +1,6 @@ -export { Ghosty } from './Ghosty'; -export type { GhostyProps, MascotFace } from './Ghosty'; +export type { MascotFace } from './Ghosty'; export { CustomGifMascot } from './CustomGifMascot'; -export type { CustomGifMascotProps } from './CustomGifMascot'; -export { MascotChipAvatar } from './MascotChipAvatar'; -export type { MascotChipAvatarProps } from './MascotChipAvatar'; -export { RiveMascot, DEFAULT_MASCOT_SRC } from './RiveMascot'; -export type { RiveMascotProps } from './RiveMascot'; +export { RiveMascot } from './RiveMascot'; export { ManifestRiveMascot } from './ManifestRiveMascot'; -export type { ManifestRiveMascotProps } from './ManifestRiveMascot'; -export { - fetchMascotManifest, - defaultMascot, - findMascot, - loadManifestRiv, -} from './manifest/manifestService'; -export type { - MascotManifest, - MascotManifestEntry, - MascotStateEngine, - MascotManifestChannel, -} from './manifest/types'; -export { BackendRiveMascot } from './backend/BackendRiveMascot'; -export type { BackendRiveMascotProps } from './backend/BackendRiveMascot'; -export { lerpViseme, VISEMES, visemePath } from './visemes'; -export type { VisemeId, VisemeShape } from './visemes'; +export type { MascotManifestEntry } from './manifest/types'; export { getMascotPalette, hexToArgbInt } from './mascotPalette'; -export type { MascotColor, MascotPalette } from './mascotPalette'; diff --git a/app/src/features/human/Mascot/manifest/stateEngine.ts b/app/src/features/human/Mascot/manifest/stateEngine.ts index a84301f61..23e520369 100644 --- a/app/src/features/human/Mascot/manifest/stateEngine.ts +++ b/app/src/features/human/Mascot/manifest/stateEngine.ts @@ -9,7 +9,7 @@ */ import type { MascotFace } from '../Ghosty'; import { faceToPose, toRiveVisemeCode } from '../riveMaps'; -import type { MascotManifestEntry, MascotStateEngine } from './types'; +import type { MascotStateEngine } from './types'; /** Every pose value this mascot's state machine accepts (cycle + logical states). */ export function availablePoses(engine: MascotStateEngine): Set { @@ -77,8 +77,3 @@ export function initialChannelValues(engine: MascotStateEngine): Record f.role === 'runtime'); } - -/** The source (`.rev`) file for a mascot, or `undefined` if it has none. */ -export function sourceFile(entry: MascotManifestEntry): MascotManifestFile | undefined { - return entry.files.find(f => f.role === 'source'); -} diff --git a/app/src/features/human/Mascot/manifest/useMascotManifest.ts b/app/src/features/human/Mascot/manifest/useMascotManifest.ts index 946974396..bc2e43f07 100644 --- a/app/src/features/human/Mascot/manifest/useMascotManifest.ts +++ b/app/src/features/human/Mascot/manifest/useMascotManifest.ts @@ -15,7 +15,7 @@ import { import { defaultMascot, fetchMascotManifest, findMascot } from './manifestService'; import type { MascotManifest, MascotManifestEntry } from './types'; -export interface UseMascotManifestResult { +interface UseMascotManifestResult { manifest: MascotManifest | null; /** The selected mascot, or the default when none is chosen / found yet. */ entry: MascotManifestEntry | null; diff --git a/app/src/features/human/Mascot/mascotPalette.ts b/app/src/features/human/Mascot/mascotPalette.ts index d4b2e3758..a750c5bb7 100644 --- a/app/src/features/human/Mascot/mascotPalette.ts +++ b/app/src/features/human/Mascot/mascotPalette.ts @@ -1,6 +1,6 @@ export type MascotColor = 'yellow' | 'burgundy' | 'black' | 'navy' | 'custom'; -export interface MascotPalette { +interface MascotPalette { armHighlightMatrix: string; armShadowMatrix: string; bodyFill: string; diff --git a/app/src/features/human/Mascot/riveMaps.ts b/app/src/features/human/Mascot/riveMaps.ts index 8e5d2f442..527e2c6d2 100644 --- a/app/src/features/human/Mascot/riveMaps.ts +++ b/app/src/features/human/Mascot/riveMaps.ts @@ -86,7 +86,7 @@ export const RIVE_VISEME_SET = [ 'oh', 'ou', ] as const; -export type RiveVisemeCode = (typeof RIVE_VISEME_SET)[number]; +type RiveVisemeCode = (typeof RIVE_VISEME_SET)[number]; /** * Lowercased-alias → canonical `visme_codes` token. diff --git a/app/src/features/human/Mascot/visemes.ts b/app/src/features/human/Mascot/visemes.ts index 4354e14bd..fcd97aa97 100644 --- a/app/src/features/human/Mascot/visemes.ts +++ b/app/src/features/human/Mascot/visemes.ts @@ -6,7 +6,7 @@ * the original happy curve so the idle face stays alive. */ -export type VisemeId = 'REST' | 'A' | 'E' | 'I' | 'O' | 'U' | 'M' | 'F'; +type VisemeId = 'REST' | 'A' | 'E' | 'I' | 'O' | 'U' | 'M' | 'F'; export interface VisemeShape { /** 0 = closed, 1 = fully open vertically. */ diff --git a/app/src/features/human/MicComposer.test.tsx b/app/src/features/human/MicComposer.test.tsx index da5411b31..189f7981f 100644 --- a/app/src/features/human/MicComposer.test.tsx +++ b/app/src/features/human/MicComposer.test.tsx @@ -1,10 +1,9 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { +import MicComposer, { isLowConfidenceTranscript, MAX_RECORDING_MS, - MicComposer, STT_MAX_RETRIES, } from './MicComposer'; import { VoiceNotCompiledError } from './voice/sttClient'; diff --git a/app/src/features/human/MicComposer.tsx b/app/src/features/human/MicComposer.tsx index b843cac26..89558aa7a 100644 --- a/app/src/features/human/MicComposer.tsx +++ b/app/src/features/human/MicComposer.tsx @@ -105,7 +105,7 @@ function pickRecorderMime(): string { return ''; } -export interface MicComposerProps { +interface MicComposerProps { /** Disabled while a turn is in flight or the welcome message is pending. */ disabled: boolean; /** Receives the transcribed text — same callback the textarea send uses. */ @@ -144,7 +144,7 @@ export { STT_MAX_RETRIES }; * Single button, single decision: tap once to start recording, tap again to * stop and send. No textarea — that's the whole point of the mascot tab. */ -export function MicComposer({ +function MicComposer({ disabled, onSubmit, onError, diff --git a/app/src/features/human/SubMascotLayer.tsx b/app/src/features/human/SubMascotLayer.tsx index 52e153d9f..db118e9bc 100644 --- a/app/src/features/human/SubMascotLayer.tsx +++ b/app/src/features/human/SubMascotLayer.tsx @@ -21,7 +21,7 @@ const POSITIONS = [ { left: '50%', top: '10%' }, ] as const; -export interface SubMascotModel { +interface SubMascotModel { id: string; agentId: string; label: string; @@ -32,7 +32,7 @@ export interface SubMascotModel { position: (typeof POSITIONS)[number]; } -export interface SubMascotLayerProps { +interface SubMascotLayerProps { entries: ToolTimelineEntry[]; } diff --git a/app/src/features/human/useHumanMascot.ts b/app/src/features/human/useHumanMascot.ts index 3cac34520..6b8b6fef5 100644 --- a/app/src/features/human/useHumanMascot.ts +++ b/app/src/features/human/useHumanMascot.ts @@ -261,7 +261,7 @@ function toolToActivityFace(toolName: string): MascotFace | null { return null; } -export interface UseHumanMascotOptions { +interface UseHumanMascotOptions { /** When true, post-stream replies are sent to ElevenLabs and the mouth * follows the returned viseme timeline while the audio plays. */ speakReplies?: boolean; @@ -270,7 +270,7 @@ export interface UseHumanMascotOptions { listening?: boolean; } -export interface UseHumanMascotResult { +interface UseHumanMascotResult { face: MascotFace; viseme: VisemeShape; /** Raw Oculus 15-set viseme code for Rive's `mouthVisemeCode` input. */ diff --git a/app/src/features/human/voice/sttClient.ts b/app/src/features/human/voice/sttClient.ts index 4432a69a1..9ceddcfd4 100644 --- a/app/src/features/human/voice/sttClient.ts +++ b/app/src/features/human/voice/sttClient.ts @@ -11,7 +11,7 @@ const sttLog = debug('human:stt'); * is untranslated developer/log copy, and the UI is responsible for rendering * translated copy via `useT()` (see `MicComposer`'s `mic.voiceNotCompiled`). */ -export const VOICE_NOT_COMPILED_CODE = 'voice_not_compiled'; +const VOICE_NOT_COMPILED_CODE = 'voice_not_compiled'; /** * Thrown when the core answers `unknown method` for a `voice_*` RPC — i.e. the @@ -50,7 +50,7 @@ export function isVoiceNotCompiledError(err: unknown): err is VoiceNotCompiledEr ); } -export interface CloudTranscribeOptions { +interface CloudTranscribeOptions { /** Override the backend STT model id. Default is whatever the backend * resolves `whisper-v1` to today. */ model?: string; @@ -61,7 +61,7 @@ export interface CloudTranscribeOptions { fileName?: string; } -export interface CloudTranscribeResult { +interface CloudTranscribeResult { text: string; } @@ -129,7 +129,7 @@ export async function transcribeCloud( return text; } -export interface FactoryTranscribeOptions { +interface FactoryTranscribeOptions { /** BCP-47 language hint, e.g. `'en'`. */ language?: string; /** Override the server-side provider resolution (`'cloud'` | `'whisper'`). @@ -142,7 +142,7 @@ export interface FactoryTranscribeOptions { fileName?: string; } -export interface FactoryTranscribeResult { +interface FactoryTranscribeResult { text: string; /** Provider that actually ran ('cloud' or 'whisper'). */ provider: string; diff --git a/app/src/features/human/voice/ttsClient.ts b/app/src/features/human/voice/ttsClient.ts index 4427ab26a..bdc8e1b89 100644 --- a/app/src/features/human/voice/ttsClient.ts +++ b/app/src/features/human/voice/ttsClient.ts @@ -15,7 +15,7 @@ export interface VisemeFrame { end_ms: number; } -export interface AlignmentFrame { +interface AlignmentFrame { char: string; start_ms: number; end_ms: number; @@ -26,14 +26,14 @@ export interface AlignmentFrame { * The core does the messy "tolerate multiple backend response shapes" work * (see `src/openhuman/voice/reply_speech.rs`) so the UI can stay strict. */ -export interface TtsResponse { +interface TtsResponse { audio_base64: string; audio_mime: string; visemes: VisemeFrame[]; alignment?: AlignmentFrame[]; } -export interface TtsOptions { +interface TtsOptions { voiceId?: string; modelId?: string; outputFormat?: string; diff --git a/app/src/features/human/voice/visemeMap.ts b/app/src/features/human/voice/visemeMap.ts index 91e86ff4e..08ba2fda6 100644 --- a/app/src/features/human/voice/visemeMap.ts +++ b/app/src/features/human/voice/visemeMap.ts @@ -58,7 +58,7 @@ export function oculusVisemeToShape(viseme: string): VisemeShape { return TABLE[viseme.toLowerCase()] ?? VISEMES.REST; } -export interface TimedFrame { +interface TimedFrame { viseme: string; start_ms: number; end_ms: number; diff --git a/app/src/features/meet/MascotFrameProducer.tsx b/app/src/features/meet/MascotFrameProducer.tsx index 332f4bc96..2ad15b587 100644 --- a/app/src/features/meet/MascotFrameProducer.tsx +++ b/app/src/features/meet/MascotFrameProducer.tsx @@ -494,5 +494,3 @@ const MascotStage: FC<{ /> ); }; - -export default MascotFrameProducer; diff --git a/app/src/features/meet/useMeetingMascots.ts b/app/src/features/meet/useMeetingMascots.ts index 787846f30..a99348466 100644 --- a/app/src/features/meet/useMeetingMascots.ts +++ b/app/src/features/meet/useMeetingMascots.ts @@ -38,14 +38,14 @@ export type MeetingPhase = 'greeting' | 'active' | 'signoff'; /** Which slot is producing the audio the call currently hears. */ export type ActiveMascotSlot = 0 | 1; -export interface MeetingMascotSlotRender { +interface MeetingMascotSlotRender { /** Manifest mascot id, or `null` for the primary on the default mascot. */ mascotId: string | null; /** MascotFace to render this tick. */ face: MascotFace; } -export interface MeetingMascotsRenderState { +interface MeetingMascotsRenderState { /** True when a distinct second mascot is enabled (drives dual composite). */ dualEnabled: boolean; primary: MeetingMascotSlotRender; @@ -94,7 +94,7 @@ function dualSlotFace(slot: ActiveMascotSlot, input: UseMeetingMascotsInput): Ma * the face table can be exercised directly (the hook is a thin redux wrapper * over it). */ -export function computeMeetingMascotsRenderState( +function computeMeetingMascotsRenderState( dualEnabled: boolean, primaryMascotId: string | null, secondaryMascotId: string | null, diff --git a/app/src/features/privacy/WhatLeavesLink.tsx b/app/src/features/privacy/WhatLeavesLink.tsx index e4ed66a83..757429bdd 100644 --- a/app/src/features/privacy/WhatLeavesLink.tsx +++ b/app/src/features/privacy/WhatLeavesLink.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import { useT } from '../../lib/i18n/I18nContext'; import WhatLeavesMyComputerSheet from './WhatLeavesMyComputerSheet'; -export interface WhatLeavesLinkProps { +interface WhatLeavesLinkProps { label?: string; className?: string; } diff --git a/app/src/features/privacy/WhatLeavesMyComputerSheet.tsx b/app/src/features/privacy/WhatLeavesMyComputerSheet.tsx index 3c7be8922..47c95ec20 100644 --- a/app/src/features/privacy/WhatLeavesMyComputerSheet.tsx +++ b/app/src/features/privacy/WhatLeavesMyComputerSheet.tsx @@ -5,7 +5,7 @@ import Button from '../../components/ui/Button'; import { useT } from '../../lib/i18n/I18nContext'; import { WHAT_LEAVES_HEADLINE, WHAT_LEAVES_ITEMS, WHAT_LEAVES_SUBHEAD } from './whatLeavesItems'; -export interface WhatLeavesMyComputerSheetProps { +interface WhatLeavesMyComputerSheetProps { open: boolean; onClose: () => void; } diff --git a/app/src/features/privacy/whatLeavesItems.ts b/app/src/features/privacy/whatLeavesItems.ts index b7d92715e..9c513a9d5 100644 --- a/app/src/features/privacy/whatLeavesItems.ts +++ b/app/src/features/privacy/whatLeavesItems.ts @@ -1,4 +1,4 @@ -export interface PrivacyLeaveItem { +interface PrivacyLeaveItem { id: string; title: string; body: string; diff --git a/app/src/features/screen-intelligence/api.ts b/app/src/features/screen-intelligence/api.ts index ca928f02a..4e73cb115 100644 --- a/app/src/features/screen-intelligence/api.ts +++ b/app/src/features/screen-intelligence/api.ts @@ -41,12 +41,12 @@ const formatCoreIdentity = (status: AccessibilityStatus | null | undefined): str return startedAt ? `PID ${process.pid} at ${startedAt}` : `PID ${process.pid}`; }; -export interface RefreshPermissionsResult { +interface RefreshPermissionsResult { status: AccessibilityStatus; restartSummary: string; } -export async function fetchScreenIntelligenceStatus(): Promise { +async function fetchScreenIntelligenceStatus(): Promise { const response = await openhumanAccessibilityStatus(); return response.result; } diff --git a/app/src/features/screen-intelligence/useScreenIntelligenceSkillStatus.ts b/app/src/features/screen-intelligence/useScreenIntelligenceSkillStatus.ts index 9512a1cac..cb217f2ef 100644 --- a/app/src/features/screen-intelligence/useScreenIntelligenceSkillStatus.ts +++ b/app/src/features/screen-intelligence/useScreenIntelligenceSkillStatus.ts @@ -14,7 +14,7 @@ import { unsupportedStatus, } from '../skills/skillCardStatus'; -export interface ScreenIntelligenceSkillStatus extends SkillCardStatusDescriptor { +interface ScreenIntelligenceSkillStatus extends SkillCardStatusDescriptor { /** True when all three macOS permissions are granted. */ allPermissionsGranted: boolean; /** True when the platform doesn't support screen intelligence. */ diff --git a/app/src/features/screen-intelligence/useScreenIntelligenceState.ts b/app/src/features/screen-intelligence/useScreenIntelligenceState.ts index 772866044..656d69b68 100644 --- a/app/src/features/screen-intelligence/useScreenIntelligenceState.ts +++ b/app/src/features/screen-intelligence/useScreenIntelligenceState.ts @@ -47,7 +47,7 @@ export interface ScreenIntelligenceState { clearError: () => void; } -export interface UseScreenIntelligenceStateOptions { +interface UseScreenIntelligenceStateOptions { pollMs?: number; visionLimit?: number; loadVision?: boolean; diff --git a/app/src/features/share/ShareCardModal.tsx b/app/src/features/share/ShareCardModal.tsx index 73c8b8bc4..c216b8806 100644 --- a/app/src/features/share/ShareCardModal.tsx +++ b/app/src/features/share/ShareCardModal.tsx @@ -21,7 +21,7 @@ const LOG_PREFIX = '[share-modal]'; /** Marketing URL printed on the card footer. */ const CARD_URL = 'tinyhumans.ai'; -export interface ShareCardModalProps { +interface ShareCardModalProps { /** The assistant output being shared (already image-stripped plain text). */ content: string; /** Display name of the agent/profile that produced the output. */ @@ -267,5 +267,3 @@ function downloadBlob(blob: Blob, filename: string): void { anchor.remove(); URL.revokeObjectURL(url); } - -export default ShareCardModal; diff --git a/app/src/features/share/ShareMessageButton.tsx b/app/src/features/share/ShareMessageButton.tsx index 372a60d40..6dafdd3ab 100644 --- a/app/src/features/share/ShareMessageButton.tsx +++ b/app/src/features/share/ShareMessageButton.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import { useT } from '../../lib/i18n/I18nContext'; import { ShareCardModal } from './ShareCardModal'; -export interface ShareMessageButtonProps { +interface ShareMessageButtonProps { /** Plain-text agent output to turn into a share card. */ content: string; /** Display name of the agent/profile that produced the output. */ @@ -66,5 +66,3 @@ export function ShareMessageButton({ ); } - -export default ShareMessageButton; diff --git a/app/src/features/share/shareCard.ts b/app/src/features/share/shareCard.ts index 847e3f272..0d3c48008 100644 --- a/app/src/features/share/shareCard.ts +++ b/app/src/features/share/shareCard.ts @@ -16,13 +16,13 @@ export const CARD_WIDTH = 1200; export const CARD_HEIGHT = 675; -export const OCEAN = '#4A83DD'; +const OCEAN = '#4A83DD'; const OCEAN_DEEP = '#2C5AA8'; const INK = '#0E1726'; const LOG_PREFIX = '[share-card]'; -export interface ShareCardData { +interface ShareCardData { /** Punchy headline describing what the agent did. */ headline: string; /** Agent / profile name shown in the footer. */ @@ -33,7 +33,7 @@ export interface ShareCardData { brandUrl: string; } -export interface ShareCardModel { +interface ShareCardModel { headlineLines: string[]; agentName: string; stat: string | null; diff --git a/app/src/features/star/GitHubStarCard.tsx b/app/src/features/star/GitHubStarCard.tsx index 281245956..3eb5310f6 100644 --- a/app/src/features/star/GitHubStarCard.tsx +++ b/app/src/features/star/GitHubStarCard.tsx @@ -87,5 +87,3 @@ export function GitHubStarCard() {
); } - -export default GitHubStarCard; diff --git a/app/src/features/voice/pttChimes.ts b/app/src/features/voice/pttChimes.ts index dc7c7b22c..db4a45a4c 100644 --- a/app/src/features/voice/pttChimes.ts +++ b/app/src/features/voice/pttChimes.ts @@ -14,7 +14,7 @@ import closeSrc from '../../assets/audio/ptt-close.wav'; import errorSrc from '../../assets/audio/ptt-error.wav'; import openSrc from '../../assets/audio/ptt-open.wav'; -export type ChimeKind = 'open' | 'close' | 'error'; +type ChimeKind = 'open' | 'close' | 'error'; const sources: Record = { open: openSrc, close: closeSrc, error: errorSrc }; diff --git a/app/src/features/wallet/walletDisplay.ts b/app/src/features/wallet/walletDisplay.ts index 55ef1a263..6a8d500cc 100644 --- a/app/src/features/wallet/walletDisplay.ts +++ b/app/src/features/wallet/walletDisplay.ts @@ -7,7 +7,7 @@ import type { BalanceInfo, EvmNetwork, WalletChain } from '../../services/walletApi'; /** Full display name per EVM network. */ -export const EVM_NETWORK_LABEL: Record = { +const EVM_NETWORK_LABEL: Record = { ethereum_mainnet: 'Ethereum', base_mainnet: 'Base', arbitrum_one: 'Arbitrum', @@ -17,7 +17,7 @@ export const EVM_NETWORK_LABEL: Record = { }; /** Short badge label per EVM network. */ -export const EVM_NETWORK_BADGE: Record = { +const EVM_NETWORK_BADGE: Record = { ethereum_mainnet: 'ETH', base_mainnet: 'BASE', arbitrum_one: 'ARB', diff --git a/app/src/hooks/useAppUpdate.ts b/app/src/hooks/useAppUpdate.ts index 0e364678a..40fe8cb06 100644 --- a/app/src/hooks/useAppUpdate.ts +++ b/app/src/hooks/useAppUpdate.ts @@ -29,7 +29,7 @@ import { } from '../utils/tauriCommands'; /** Phases driven by `app-update:status`, plus locally-derived ones. */ -export type AppUpdatePhase = +type AppUpdatePhase = | 'idle' | 'checking' | 'available' @@ -40,14 +40,14 @@ export type AppUpdatePhase = | 'up_to_date' | 'error'; -export interface AppUpdateProgress { +interface AppUpdateProgress { /** Bytes received in the latest chunk callback. */ chunk: number; /** Total bytes (null when the manifest didn't advertise a content-length). */ total: number | null; } -export interface UseAppUpdateOptions { +interface UseAppUpdateOptions { /** * Run an automatic check shortly after the hook mounts. * Default: true. Skipped when `isTauri()` is false. @@ -68,7 +68,7 @@ export interface UseAppUpdateOptions { autoDownload?: boolean; } -export interface UseAppUpdateResult { +interface UseAppUpdateResult { phase: AppUpdatePhase; /** Last successful check result (current/available versions, body). */ info: AppUpdateInfo | null; diff --git a/app/src/hooks/useConsciousItems.ts b/app/src/hooks/useConsciousItems.ts index ca841885b..a4435fb83 100644 --- a/app/src/hooks/useConsciousItems.ts +++ b/app/src/hooks/useConsciousItems.ts @@ -132,7 +132,7 @@ function mapToActionableItem(item: ExtractedActionable, index: number): Actionab // ─── Hook ───────────────────────────────────────────────────────────────────── -export interface UseConsciousItemsResult { +interface UseConsciousItemsResult { items: ActionableItem[]; loading: boolean; isRunning: boolean; diff --git a/app/src/hooks/useCostDashboard.ts b/app/src/hooks/useCostDashboard.ts index e2d24e990..3ff477384 100644 --- a/app/src/hooks/useCostDashboard.ts +++ b/app/src/hooks/useCostDashboard.ts @@ -63,7 +63,7 @@ export interface CostUsageCategoryStats { percent_of_total: number; } -export interface CostUsageLogPayload { +interface CostUsageLogPayload { records: CostUsageRecord[]; by_category: CostUsageCategoryStats[]; total_cost_usd: number; @@ -81,13 +81,13 @@ interface RpcEnvelope { const DEFAULT_REFRESH_MS = 10_000; -export interface UseCostDashboardOptions { +interface UseCostDashboardOptions { refreshMs?: number; /** When true, polling pauses (e.g. when the panel is hidden). */ paused?: boolean; } -export interface UseCostDashboardResult { +interface UseCostDashboardResult { data: CostDashboardPayload | null; /** True only before the first successful fetch resolves. */ isLoading: boolean; @@ -100,12 +100,12 @@ export interface UseCostDashboardResult { refetch: () => Promise; } -export interface UseCostUsageLogOptions extends UseCostDashboardOptions { +interface UseCostUsageLogOptions extends UseCostDashboardOptions { days?: number; limit?: number; } -export interface UseCostUsageLogResult { +interface UseCostUsageLogResult { data: CostUsageLogPayload | null; isLoading: boolean; isFetching: boolean; diff --git a/app/src/hooks/useDaemonHealth.ts b/app/src/hooks/useDaemonHealth.ts index eb2804a10..73b680e19 100644 --- a/app/src/hooks/useDaemonHealth.ts +++ b/app/src/hooks/useDaemonHealth.ts @@ -216,26 +216,3 @@ function formatUptime(seconds: number): string { return `${secs}s`; } } - -/** - * Format relative time from ISO string - */ -export function formatRelativeTime(isoString: string): string { - const date = new Date(isoString); - const now = new Date(); - const diffMs = now.getTime() - date.getTime(); - const diffSeconds = Math.floor(diffMs / 1000); - - if (diffSeconds < 60) { - return `${diffSeconds}s ago`; - } else if (diffSeconds < 3600) { - const minutes = Math.floor(diffSeconds / 60); - return `${minutes}m ago`; - } else if (diffSeconds < 86400) { - const hours = Math.floor(diffSeconds / 3600); - return `${hours}h ago`; - } else { - const days = Math.floor(diffSeconds / 86400); - return `${days}d ago`; - } -} diff --git a/app/src/hooks/useDictationHotkey.ts b/app/src/hooks/useDictationHotkey.ts index c430e5845..564fe1fb9 100644 --- a/app/src/hooks/useDictationHotkey.ts +++ b/app/src/hooks/useDictationHotkey.ts @@ -45,7 +45,7 @@ interface DictationSettings { streaming_interval_ms: number; } -export interface DictationHotkeyState { +interface DictationHotkeyState { /** Whether dictation is enabled in the core config. */ dictationEnabled: boolean; /** Whether the core hotkey listener is active. */ diff --git a/app/src/hooks/useFlowApprovalRequests.ts b/app/src/hooks/useFlowApprovalRequests.ts index 92d40b6e4..795629042 100644 --- a/app/src/hooks/useFlowApprovalRequests.ts +++ b/app/src/hooks/useFlowApprovalRequests.ts @@ -58,7 +58,7 @@ function parsePayload(data: unknown): FlowApprovalRequest | null { }; } -export interface UseFlowApprovalRequestsResult { +interface UseFlowApprovalRequestsResult { /** Live flow-approval requests awaiting a decision, oldest first. */ requests: FlowApprovalRequest[]; /** Drop a request off the list once it's been decided (or otherwise resolved). */ @@ -106,5 +106,3 @@ export function useFlowApprovalRequests(): UseFlowApprovalRequestsResult { return { requests, dismiss }; } - -export default useFlowApprovalRequests; diff --git a/app/src/hooks/useFlowChanged.ts b/app/src/hooks/useFlowChanged.ts index d9111704b..4384232c1 100644 --- a/app/src/hooks/useFlowChanged.ts +++ b/app/src/hooks/useFlowChanged.ts @@ -29,10 +29,10 @@ const EVENT_COLON = 'flow:changed'; const EVENT_UNDERSCORE = 'flow_changed'; /** What happened to the flow. */ -export type FlowChangeKind = 'created' | 'updated' | 'deleted' | 'enabled_changed' | (string & {}); +type FlowChangeKind = 'created' | 'updated' | 'deleted' | 'enabled_changed' | (string & {}); /** Payload of a `flow:changed` socket event (`DomainEvent::FlowChanged`). */ -export interface FlowChangedEvent { +interface FlowChangedEvent { flow_id: string; kind: FlowChangeKind; /** Coarse hint: `agent` | `user` | `system`. */ @@ -81,5 +81,3 @@ export function useFlowChanged( }; }, [handle]); } - -export default useFlowChanged; diff --git a/app/src/hooks/useFlowPendingApprovals.ts b/app/src/hooks/useFlowPendingApprovals.ts index 552fd7f3a..299f21570 100644 --- a/app/src/hooks/useFlowPendingApprovals.ts +++ b/app/src/hooks/useFlowPendingApprovals.ts @@ -40,7 +40,7 @@ function matchesRun(approval: PendingApproval, flowId: string, runId: string): b return !!ctx && ctx.kind === 'flow' && ctx.flow_id === flowId && ctx.run_id === runId; } -export interface UseFlowPendingApprovalsResult { +interface UseFlowPendingApprovalsResult { /** Pending approvals scoped to this flow/run, oldest first (server order). */ approvals: PendingApproval[]; /** `request_id` of the approval currently being decided, or `null`. */ @@ -133,5 +133,3 @@ export function useFlowPendingApprovals( return { approvals, decidingId, error, decide }; } - -export default useFlowPendingApprovals; diff --git a/app/src/hooks/useFlowRunFinished.ts b/app/src/hooks/useFlowRunFinished.ts index 738a2dbc0..261273c5a 100644 --- a/app/src/hooks/useFlowRunFinished.ts +++ b/app/src/hooks/useFlowRunFinished.ts @@ -39,7 +39,7 @@ const EVENT_UNDERSCORE = 'flow_run_finished'; const DEDUP_CACHE_SIZE = 50; /** Payload of a `flow:run_finished` socket event (`DomainEvent::FlowRunFinished`). */ -export interface FlowRunFinishedEvent { +interface FlowRunFinishedEvent { flow_id: string; run_id: string; status: string; @@ -118,5 +118,3 @@ export function useFlowRunFinished( }; }, [handle]); } - -export default useFlowRunFinished; diff --git a/app/src/hooks/useFlowRunPoller.ts b/app/src/hooks/useFlowRunPoller.ts index 3a82a45a6..569046f88 100644 --- a/app/src/hooks/useFlowRunPoller.ts +++ b/app/src/hooks/useFlowRunPoller.ts @@ -62,7 +62,7 @@ function isTerminal(run: FlowRun | null): boolean { return run !== null && TERMINAL.has(run.status); } -export interface UseFlowRunPollerResult { +interface UseFlowRunPollerResult { run: FlowRun | null; loading: boolean; error: string | null; @@ -176,5 +176,3 @@ export function useFlowRunPoller(runId: string | null): UseFlowRunPollerResult { return { run, loading, error }; } - -export default useFlowRunPoller; diff --git a/app/src/hooks/useFlowRunProgress.ts b/app/src/hooks/useFlowRunProgress.ts index ced3b16f1..b208eb057 100644 --- a/app/src/hooks/useFlowRunProgress.ts +++ b/app/src/hooks/useFlowRunProgress.ts @@ -38,7 +38,7 @@ const EVENT_UNDERSCORE = 'flow_run_progress'; export type FlowNodeRunStatus = 'running' | 'success' | 'error' | (string & {}); /** node_id → latest live status for the watched run. */ -export type FlowRunProgressMap = Record; +type FlowRunProgressMap = Record; /** * Maps a live node status to the canvas CSS class that rings/animates the node. @@ -122,5 +122,3 @@ export function useFlowRunProgress(runId: string | null): FlowRunProgressMap { return statuses; } - -export default useFlowRunProgress; diff --git a/app/src/hooks/useFlowRunStarted.ts b/app/src/hooks/useFlowRunStarted.ts index 83db1bdc4..0df18d790 100644 --- a/app/src/hooks/useFlowRunStarted.ts +++ b/app/src/hooks/useFlowRunStarted.ts @@ -42,7 +42,7 @@ const EVENT_UNDERSCORE = 'flow_run_started'; const DEDUP_CACHE_SIZE = 50; /** Payload of a `flow:run_started` socket event (`DomainEvent::FlowRunStarted`). */ -export interface FlowRunStartedEvent { +interface FlowRunStartedEvent { flow_id: string; run_id: string; } @@ -110,5 +110,3 @@ export function useFlowRunStarted( }; }, [handle]); } - -export default useFlowRunStarted; diff --git a/app/src/hooks/useFlowRunsLiveRefresh.ts b/app/src/hooks/useFlowRunsLiveRefresh.ts index 525e5c76d..4e0730d3c 100644 --- a/app/src/hooks/useFlowRunsLiveRefresh.ts +++ b/app/src/hooks/useFlowRunsLiveRefresh.ts @@ -106,5 +106,3 @@ export function useFlowRunsLiveRefresh(runs: FlowRun[], refetch: () => void): vo }; }, [hasActive]); } - -export default useFlowRunsLiveRefresh; diff --git a/app/src/hooks/useIntelligenceSocket.ts b/app/src/hooks/useIntelligenceSocket.ts index 8aa687a91..e91a2e20c 100644 --- a/app/src/hooks/useIntelligenceSocket.ts +++ b/app/src/hooks/useIntelligenceSocket.ts @@ -64,9 +64,3 @@ export const useIntelligenceSocketManager = () => { return { connect, disconnect, isConnected, isReady: Boolean(token) && isConnected }; }; - -export const useIntelligenceEvents = () => ({ - onAgentResponse: () => () => {}, - onExecutionProgress: () => () => {}, - onExecutionComplete: () => () => {}, -}); diff --git a/app/src/hooks/useMediaQuery.ts b/app/src/hooks/useMediaQuery.ts index 4baf42855..0a73dfc46 100644 --- a/app/src/hooks/useMediaQuery.ts +++ b/app/src/hooks/useMediaQuery.ts @@ -23,5 +23,3 @@ export const useMediaQuery = (query: string): boolean => { return useSyncExternalStore(subscribe, getSnapshot, () => false); }; - -export default useMediaQuery; diff --git a/app/src/hooks/useMemoryIngestionStatus.ts b/app/src/hooks/useMemoryIngestionStatus.ts index 159da2834..1ff27a7c8 100644 --- a/app/src/hooks/useMemoryIngestionStatus.ts +++ b/app/src/hooks/useMemoryIngestionStatus.ts @@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { callCoreRpc } from '../services/coreRpcClient'; -export interface MemoryIngestionStatus { +interface MemoryIngestionStatus { running: boolean; currentDocumentId?: string; currentTitle?: string; diff --git a/app/src/hooks/useOAuthConnectionListener.ts b/app/src/hooks/useOAuthConnectionListener.ts index 348e75cbc..0b559f502 100644 --- a/app/src/hooks/useOAuthConnectionListener.ts +++ b/app/src/hooks/useOAuthConnectionListener.ts @@ -73,7 +73,7 @@ interface OAuthErrorDetail { message?: string; } -export interface UseOAuthConnectionListenerOptions { +interface UseOAuthConnectionListenerOptions { /** Channel that owns the OAuth flow (e.g. 'discord', 'telegram'). */ channel: ChannelType; /** Auth mode that the OAuth deep-link should resolve to. */ diff --git a/app/src/hooks/useRunsPendingApprovalSet.ts b/app/src/hooks/useRunsPendingApprovalSet.ts index 0d185a3ae..6aa44177d 100644 --- a/app/src/hooks/useRunsPendingApprovalSet.ts +++ b/app/src/hooks/useRunsPendingApprovalSet.ts @@ -114,5 +114,3 @@ export function resolveDisplayStatus(run: FlowRun, pendingRunIds: Set): } return run.status; } - -export default useRunsPendingApprovalSet; diff --git a/app/src/hooks/useSubconscious.ts b/app/src/hooks/useSubconscious.ts index 77d5e231c..1afb3527b 100644 --- a/app/src/hooks/useSubconscious.ts +++ b/app/src/hooks/useSubconscious.ts @@ -22,7 +22,7 @@ import type { export type TriggerKind = SubconsciousKind | 'all'; -export interface UseSubconsciousResult { +interface UseSubconsciousResult { status: SubconsciousStatus | null; /** One row per registered world, tolerant of an older core (falls back to * the top-level memory fields when `instances` is absent). */ diff --git a/app/src/hooks/useUsageState.ts b/app/src/hooks/useUsageState.ts index 8a7ca1256..4f00fcf8b 100644 --- a/app/src/hooks/useUsageState.ts +++ b/app/src/hooks/useUsageState.ts @@ -14,7 +14,7 @@ import { CoreRpcError } from '../services/coreRpcClient'; import type { CurrentPlanData, PlanTier } from '../types/api'; import { subscribeUsageRefresh } from './usageRefresh'; -export interface UsageState { +interface UsageState { teamUsage: TeamUsage | null; currentPlan: CurrentPlanData | null; currentTier: PlanTier; diff --git a/app/src/hooks/useWorkflowBuilderChat.ts b/app/src/hooks/useWorkflowBuilderChat.ts index 638af036a..05f0671cb 100644 --- a/app/src/hooks/useWorkflowBuilderChat.ts +++ b/app/src/hooks/useWorkflowBuilderChat.ts @@ -56,7 +56,7 @@ import type { ThreadMessage } from '../types/thread'; const log = createDebug('app:flows:builder-chat'); /** A single builder turn: what the user sees vs. the structured turn request. */ -export interface WorkflowBuilderSendParams { +interface WorkflowBuilderSendParams { /** Human-readable text shown as the user's message in the thread transcript. */ displayText: string; /** @@ -96,7 +96,7 @@ export interface WorkflowBuilderSendResult { proposed: boolean; } -export interface UseWorkflowBuilderChat { +interface UseWorkflowBuilderChat { /** The dedicated thread id, or `null` before the first send creates it. */ threadId: string | null; /** True while a builder turn is in flight on this thread. */ diff --git a/app/src/lib/agentworld/invokeApiClient.ts b/app/src/lib/agentworld/invokeApiClient.ts index e9386f5bf..103d4b9c7 100644 --- a/app/src/lib/agentworld/invokeApiClient.ts +++ b/app/src/lib/agentworld/invokeApiClient.ts @@ -99,7 +99,7 @@ async function call( // each `AgentQueryParams`, `AgentCard`, etc. with `import type { â€Ļ } from // '@tinyhumansai/tinyplace'`. -export interface AgentQueryParams { +interface AgentQueryParams { q?: string; skill?: string; tag?: string; @@ -129,27 +129,27 @@ export interface ExplorerOverview { [key: string]: unknown; } -export interface SearchResponse { +interface SearchResponse { results?: unknown[]; [key: string]: unknown; } // ── Directory extended types ────────────────────────────────────────────────── -export interface ResolveResponse { +interface ResolveResponse { identity?: unknown; agent?: AgentCard; [key: string]: unknown; } -export interface ReverseResponse { +interface ReverseResponse { cryptoId: string; identities: unknown[]; agents?: AgentCard[]; [key: string]: unknown; } -export interface IdentityListingQueryParams { +interface IdentityListingQueryParams { q?: string; tag?: string; category?: string; @@ -168,13 +168,13 @@ export interface DirectoryIdentityListingsResponse { [key: string]: unknown; } -export interface DirectorySkillsParams { +interface DirectorySkillsParams { q?: string; limit?: number; cursor?: string; } -export interface AgentSearchResponse { +interface AgentSearchResponse { agents?: unknown[]; cursor?: string; [key: string]: unknown; @@ -182,7 +182,7 @@ export interface AgentSearchResponse { // ── Profiles types ──────────────────────────────────────────────────────────── -export interface AgentProfile { +interface AgentProfile { username?: string; name?: string; description?: string; @@ -191,47 +191,47 @@ export interface AgentProfile { [key: string]: unknown; } -export interface ProfileActivity { +interface ProfileActivity { [key: string]: unknown; } -export interface ProfileGroupMembership { +interface ProfileGroupMembership { groupId?: string; name?: string; [key: string]: unknown; } -export interface ProfileGroupsResponse { +interface ProfileGroupsResponse { groups: ProfileGroupMembership[]; [key: string]: unknown; } -export interface ProfileBroadcast { +interface ProfileBroadcast { id?: string; content?: string; createdAt?: string; [key: string]: unknown; } -export interface ProfileBroadcastsResponse { +interface ProfileBroadcastsResponse { broadcasts: ProfileBroadcast[]; [key: string]: unknown; } -export interface ProfileAttestation { +interface ProfileAttestation { id?: string; attester?: string; [key: string]: unknown; } -export interface ProfileAttestationsResponse { +interface ProfileAttestationsResponse { attestations: ProfileAttestation[]; [key: string]: unknown; } // ── Users types ─────────────────────────────────────────────────────────────── -export interface User { +interface User { cryptoId: string; actorType: string; displayName: string; @@ -249,7 +249,7 @@ export interface User { [key: string]: unknown; } -export interface UserProfileUpdate { +interface UserProfileUpdate { displayName?: string; bio?: string; avatarEmail?: string; @@ -261,19 +261,6 @@ export interface UserProfileUpdate { [key: string]: unknown; } -// ── Users email verification types ────────────────────────────────────────── - -export interface UserEmailVerificationStartParams { - cryptoId: string; - email: string; -} - -export interface UserEmailVerificationConfirmParams { - cryptoId: string; - email: string; - code: string; -} - export interface AvailabilityResponse { available: boolean; name: string; @@ -283,7 +270,7 @@ export interface AvailabilityResponse { // ── Registry (x402 register) types ───────────────────────────────────────────── -export interface RegisterParams { +interface RegisterParams { username: string; /** false/omitted → challenge only (no spend); true → pay + register. */ confirmed?: boolean; @@ -321,7 +308,7 @@ export interface RegisteredIdentity { * - `{ challenge, walletBalance, walletAddress }` — unconfirmed; render confirm. * - `{ identity, payment }` — paid + registered. */ -export interface RegistrationResult { +interface RegistrationResult { identity?: RegisteredIdentity; challenge?: RegistrationChallenge; walletBalance?: RegistryWalletBalance | null; @@ -331,13 +318,13 @@ export interface RegistrationResult { } /** Result of `registry.assignPrimary` — the handle now flagged primary. */ -export interface AssignPrimaryResult { +interface AssignPrimaryResult { identity?: Identity; [key: string]: unknown; } /** Result of a handle transfer: the Identity now owned by the recipient. */ -export interface TransferHandleResult { +interface TransferHandleResult { identity?: Identity; [key: string]: unknown; } @@ -423,19 +410,19 @@ export interface X402BuyResult { * signed authorizations — no on-chain transfer until acceptance — so the result * is `{ result, committed: true }` (no `payment.onChainTx`). */ -export interface X402CommitResult { +interface X402CommitResult { result?: Record; committed?: boolean; [key: string]: unknown; } /** Amount + (optional) asset + network for a bid/offer commitment. */ -export interface CommitPriceParams { +interface CommitPriceParams { amount: string; asset?: string; network: string; } -export interface BidsResponse { +interface BidsResponse { bids: IdentityBid[]; [key: string]: unknown; } @@ -443,7 +430,7 @@ export interface IdentitiesResponse { identities: IdentityListing[]; [key: string]: unknown; } -export interface IdentityBid { +interface IdentityBid { bidId?: string; listingId?: string; bidder?: string; @@ -470,7 +457,7 @@ export interface IdentityListing { updatedAt: string; [key: string]: unknown; } -export interface IdentityOffer { +interface IdentityOffer { offerId: string; name?: string; buyer: string; @@ -487,7 +474,7 @@ export interface IdentitySale { createdAt: string; [key: string]: unknown; } -export interface IdentitySaleHistoryResponse { +interface IdentitySaleHistoryResponse { history?: IdentitySale[]; [key: string]: unknown; } @@ -497,7 +484,7 @@ export interface MarketplacePrice { network?: string; [key: string]: unknown; } -export interface OffersResponse { +interface OffersResponse { offers: IdentityOffer[]; [key: string]: unknown; } @@ -530,7 +517,7 @@ export interface ArtifactListResult { artifacts: Artifact[]; cursor?: string; } -export interface ArtifactQueryParams { +interface ArtifactQueryParams { role?: string; status?: string; referenceKind?: string; @@ -539,7 +526,7 @@ export interface ArtifactQueryParams { cursor?: string; [key: string]: unknown; } -export interface CategoriesResponse { +interface CategoriesResponse { categories: MarketplaceCategory[]; [key: string]: unknown; } @@ -554,28 +541,28 @@ export interface EscrowListResponse { escrows: Escrow[]; [key: string]: unknown; } -export interface EscrowQueryParams { +interface EscrowQueryParams { role?: string; status?: string; limit?: number; offset?: number; [key: string]: unknown; } -export interface FeaturedResponse { +interface FeaturedResponse { items: unknown[]; [key: string]: unknown; } -export interface JobListResponse { +interface JobListResponse { jobs: JobPosting[]; [key: string]: unknown; } -export interface JobPosting { +interface JobPosting { jobId: string; status: string; client: string; [key: string]: unknown; } -export interface JobQueryParams { +interface JobQueryParams { status?: string; skill?: string; q?: string; @@ -583,10 +570,10 @@ export interface JobQueryParams { offset?: number; [key: string]: unknown; } -export interface MarketplaceBrowseResponse { +interface MarketplaceBrowseResponse { [key: string]: unknown; } -export interface MarketplaceCategory { +interface MarketplaceCategory { [key: string]: unknown; } export interface Product { @@ -609,7 +596,7 @@ export interface Product { signerPublicKey?: string; [key: string]: unknown; } -export interface ProductQueryParams { +interface ProductQueryParams { q?: string; type?: string; category?: string; @@ -622,7 +609,7 @@ export interface ProductQueryParams { offset?: number; [key: string]: unknown; } -export interface ProductReview { +interface ProductReview { reviewId?: string; productId?: string; buyer?: string; @@ -630,7 +617,7 @@ export interface ProductReview { comment?: string; [key: string]: unknown; } -export interface ProductReviewsResponse { +interface ProductReviewsResponse { reviews: ProductReview[]; [key: string]: unknown; } @@ -639,57 +626,57 @@ export interface ProductsResponse { [key: string]: unknown; } -export interface GqlAgentCardListResult { +interface GqlAgentCardListResult { agents: AgentCard[]; count?: number; [key: string]: unknown; } -export interface GqlProduct extends Omit { +interface GqlProduct extends Omit { seller: FeedAuthor; sellerCryptoId?: string; } -export interface GqlProductListResult { +interface GqlProductListResult { products: GqlProduct[]; count?: number; [key: string]: unknown; } -export interface GqlIdentityListing extends Omit { +interface GqlIdentityListing extends Omit { seller?: FeedAuthor; sellerCryptoId?: string; } -export interface GqlIdentityListingListResult { +interface GqlIdentityListingListResult { identities?: GqlIdentityListing[]; listings?: GqlIdentityListing[]; count?: number; [key: string]: unknown; } -export interface GqlIdentityBidListResult { +interface GqlIdentityBidListResult { bids: IdentityBid[]; count?: number; [key: string]: unknown; } -export interface GqlIdentityOffer extends Omit { +interface GqlIdentityOffer extends Omit { buyer: FeedAuthor; } -export interface GqlIdentityOfferListResult { +interface GqlIdentityOfferListResult { offers: GqlIdentityOffer[]; count?: number; [key: string]: unknown; } -export interface GqlIdentitySale extends Omit { +interface GqlIdentitySale extends Omit { buyer: FeedAuthor; seller?: FeedAuthor; } -export interface GqlIdentitySaleListResult { +interface GqlIdentitySaleListResult { sales: GqlIdentitySale[]; count?: number; [key: string]: unknown; @@ -729,7 +716,7 @@ export interface Channel { updatedAt: string; [key: string]: unknown; } -export interface ChannelListResponse { +interface ChannelListResponse { channels: Channel[]; [key: string]: unknown; } @@ -796,7 +783,7 @@ export interface GroupInvite { [key: string]: unknown; } -export interface GroupInviteCreateRequest { +interface GroupInviteCreateRequest { ttlSeconds?: number; maxUses?: number; } @@ -812,7 +799,7 @@ export interface GroupInvitePreview { [key: string]: unknown; } -export interface InboxCounts { +interface InboxCounts { unread: number; read: number; archived: number; @@ -830,7 +817,7 @@ export interface InboxItem { from?: string; [key: string]: unknown; } -export interface InboxListResult { +interface InboxListResult { items: InboxItem[]; cursor?: string; unreadCount: number; @@ -868,7 +855,7 @@ export interface ContactView { contact: Contact; [key: string]: unknown; } -export interface ContactListParams { +interface ContactListParams { limit?: number; offset?: number; } @@ -879,7 +866,7 @@ export interface ContactRequestsResponse { incoming: ContactView[]; outgoing: ContactView[]; } -export interface ContactStatusResponse { +interface ContactStatusResponse { agentId: string; status: ContactStatus | 'none'; direction?: 'incoming' | 'outgoing'; @@ -905,14 +892,14 @@ export interface PairingSnapshot { requests: ContactRequestsResponse; stats: ContactStats | null; } -export interface PairingActionResult { +interface PairingActionResult { record?: PairingRecord | null; remote: unknown; } // ── Follows types ─────────────────────────────────────────────────────────── -export interface AgentFollow { +interface AgentFollow { follower: string; followee: string; createdAt: string; @@ -926,22 +913,22 @@ export interface FollowStats { [key: string]: unknown; } -export interface FollowListParams { +interface FollowListParams { limit?: number; offset?: number; } -export interface FollowersResponse { +interface FollowersResponse { followers: AgentFollow[]; [key: string]: unknown; } -export interface FollowingResponse { +interface FollowingResponse { following: AgentFollow[]; [key: string]: unknown; } -export interface FeedListParams { +interface FeedListParams { limit?: number; offset?: number; kind?: string; @@ -950,7 +937,7 @@ export interface FeedListParams { includeSelf?: boolean; } -export interface ActivityEvent { +interface ActivityEvent { eventId: string; kind: string; category: string; @@ -963,14 +950,14 @@ export interface ActivityEvent { [key: string]: unknown; } -export interface ActivityStats { +interface ActivityStats { total: number; byKind: Record; byCategory: Record; [key: string]: unknown; } -export interface FeedResponse { +interface FeedResponse { events: ActivityEvent[]; following: AgentFollow[]; stats: ActivityStats; @@ -1010,24 +997,24 @@ export interface GqlComment { author: FeedAuthor; } -export interface GqlPostLike { +interface GqlPostLike { postId: string; feedId: string; actor: FeedAuthor; createdAt: string; } -export interface GqlPostDetail extends GqlPost { +interface GqlPostDetail extends GqlPost { comments: GqlComment[]; likers: GqlPostLike[]; } -export interface GqlPostListResult { +interface GqlPostListResult { posts: GqlPost[]; count: number; } -export interface GqlPostLikerListResult { +interface GqlPostLikerListResult { likers: GqlPostLike[]; count: number; } @@ -1045,7 +1032,7 @@ export interface GqlHomeFeedResult { // ── Feeds REST types (write surface) ────────────────────────────────────── -export interface FeedsPost { +interface FeedsPost { postId: string; feedId: string; author: string; @@ -1061,7 +1048,7 @@ export interface FeedsPost { moderationState?: string; } -export interface FeedsComment { +interface FeedsComment { commentId: string; postId: string; feedId: string; @@ -1173,7 +1160,7 @@ export interface BountyCreateParams { durationDays?: number; } -export interface BountyQueryParams { +interface BountyQueryParams { creator?: string; status?: string; limit?: number; @@ -1181,14 +1168,14 @@ export interface BountyQueryParams { [key: string]: unknown; } -export interface BountySubmissionQueryParams { +interface BountySubmissionQueryParams { status?: string; submitter?: string; limit?: number; [key: string]: unknown; } -export interface BountyCommentQueryParams { +interface BountyCommentQueryParams { limit?: number; offset?: number; [key: string]: unknown; @@ -1199,25 +1186,16 @@ export interface BountyListResponse { [key: string]: unknown; } -export interface BountySubmissionsResponse { +interface BountySubmissionsResponse { submissions: BountySubmission[]; [key: string]: unknown; } -export interface BountyCommentsResponse { +interface BountyCommentsResponse { comments: BountyComment[]; [key: string]: unknown; } -export type BountyStatus = - | 'draft' - | 'open' - | 'judging' - | 'review' - | 'awarded' - | 'refunded' - | 'cancelled'; - // ── GraphQL Ledger types ──────────────────────────────────────────────────── export interface GqlLedgerReference { @@ -1245,12 +1223,12 @@ export interface GqlLedgerTransaction { [key: string]: unknown; } -export interface GqlLedgerTransactionListResult { +interface GqlLedgerTransactionListResult { transactions: GqlLedgerTransaction[]; count: number; } -export interface LedgerListParams { +interface LedgerListParams { limit?: number; offset?: number; agent?: string; @@ -1354,7 +1332,7 @@ export interface GqlBounty { } /** Filters for the GraphQL bounties query (all optional). */ -export interface GqlBountyQueryParams { +interface GqlBountyQueryParams { status?: string; creator?: string; limit?: number; @@ -1365,7 +1343,7 @@ export interface GqlBountyQueryParams { * Query params for the GraphQL jobs endpoint. Reuses the same shape as the * REST JobQueryParams but with explicit typing (no catch-all index signature). */ -export interface GqlJobQueryParams { +interface GqlJobQueryParams { client?: string; status?: string; category?: string; @@ -1412,20 +1390,20 @@ export interface Proposal { } /** Response from list_proposals. */ -export interface ProposalListResponse { +interface ProposalListResponse { proposals: Proposal[]; [key: string]: unknown; } /** Query params for listing proposals. */ -export interface ProposalQueryParams { +interface ProposalQueryParams { status?: string; limit?: number; offset?: number; } /** Result of selecting a candidate (spawns escrow). */ -export interface SelectCandidateResult { +interface SelectCandidateResult { job: JobPosting; contractEscrowId: string; [key: string]: unknown; @@ -1478,7 +1456,7 @@ export interface GqlProfile { } /** GqlIdentity: Identity fields flattened + optional owner profile. */ -export interface GqlIdentity { +interface GqlIdentity { username: string; cryptoId: string; publicKey: string; @@ -1499,7 +1477,7 @@ export interface GqlIdentity { // ── Feedback types ────────────────────────────────────────────────────────── -export interface FeedbackItem { +interface FeedbackItem { feedbackId: string; author: string; title: string; @@ -1521,34 +1499,34 @@ export interface FeedbackItem { [key: string]: unknown; } -export interface FeedbackListParams { +interface FeedbackListParams { status?: string; limit?: number; offset?: number; } -export interface FeedbackListResponse { +interface FeedbackListResponse { feedback: FeedbackItem[]; [key: string]: unknown; } // ── Solana types ──────────────────────────────────────────────────────────── -export interface SolanaRpcInfo { +interface SolanaRpcInfo { url: string; rateLimitPerMin: number; fallbacks: boolean; [key: string]: unknown; } -export interface SupportedAsset { +interface SupportedAsset { symbol: string; address?: string; decimals: number; [key: string]: unknown; } -export interface SolanaChainInfo { +interface SolanaChainInfo { network: string; name: string; kind: string; @@ -1562,33 +1540,33 @@ export interface SolanaChainInfo { // ── Streams types ───────────────────────────────────────────────────────────── -export interface StreamStartResult { +interface StreamStartResult { streamId: string; [key: string]: unknown; } -export interface StreamEntry { +interface StreamEntry { streamId: string; kind: string; status: string; [key: string]: unknown; } -export interface StreamListResult { +interface StreamListResult { streams: StreamEntry[]; [key: string]: unknown; } // ── Signal key management types ───────────────────────────────────────────── -export interface SignedKey { +interface SignedKey { keyId: string; publicKey: string; signature?: string; [key: string]: unknown; } -export interface KeyBundle { +interface KeyBundle { agentId: string; identityKey: string; signedPreKey: SignedKey; @@ -1617,7 +1595,7 @@ export interface SignalKeyStatus { [key: string]: unknown; } -export interface SignalMetadataEnvelope { +interface SignalMetadataEnvelope { ephemeralKey?: string; signedPreKeyId?: string; oneTimePreKeyId?: string; @@ -1633,7 +1611,7 @@ export interface SignalMetadataEnvelope { [key: string]: unknown; } -export interface MessageEnvelope { +interface MessageEnvelope { id: string; from: string; to: string; @@ -2410,5 +2388,3 @@ export function createInvokeApiClient() { }, }; } - -export type InvokeApiClient = ReturnType; diff --git a/app/src/lib/ai/skillToolChainLatency.ts b/app/src/lib/ai/skillToolChainLatency.ts index c0a8c0564..9d66569fc 100644 --- a/app/src/lib/ai/skillToolChainLatency.ts +++ b/app/src/lib/ai/skillToolChainLatency.ts @@ -18,7 +18,7 @@ /** The AC3 budget: complex skill tool chains should finish within this. */ export const SKILL_TOOL_CHAIN_TARGET_MS = 60_000; -export interface ToolChainMeasurement { +interface ToolChainMeasurement { /** Thread the chain ran on. */ threadId: string; /** Wall-clock from the first tool call to turn completion, in ms. */ @@ -32,7 +32,7 @@ export interface ToolChainMeasurement { ok: boolean; } -export interface SkillToolChainLatencyTracker { +interface SkillToolChainLatencyTracker { /** * Record a tool call on a thread. The first call starts the chain timer; each * subsequent call increments the tool count. No-op shape until the next diff --git a/app/src/lib/attachments.test.ts b/app/src/lib/attachments.test.ts index b758d27ae..e9c48f44b 100644 --- a/app/src/lib/attachments.test.ts +++ b/app/src/lib/attachments.test.ts @@ -4,8 +4,8 @@ import { ALLOWED_ATTACHMENT_MIME_TYPES, type Attachment, ATTACHMENT_MAX_FILE_SIZE_BYTES, + ATTACHMENT_MAX_IMAGE_SIZE_BYTES, ATTACHMENT_MAX_IMAGES, - ATTACHMENT_MAX_SIZE_BYTES, ATTACHMENT_MAX_VIDEO_SIZE_BYTES, attachmentKindForMime, buildMessageWithAttachments, @@ -140,7 +140,7 @@ describe('validateAndReadFile', () => { }); it('rejects files that exceed the size limit', async () => { - const oversizedFile = makeFile('big.png', 'image/png', ATTACHMENT_MAX_SIZE_BYTES + 1); + const oversizedFile = makeFile('big.png', 'image/png', ATTACHMENT_MAX_IMAGE_SIZE_BYTES + 1); const result = await validateAndReadFile(oversizedFile, 0); expect('error' in result).toBe(true); if ('error' in result) { diff --git a/app/src/lib/attachments.ts b/app/src/lib/attachments.ts index 27928b668..bf5362f6f 100644 --- a/app/src/lib/attachments.ts +++ b/app/src/lib/attachments.ts @@ -10,7 +10,7 @@ import debugFactory from 'debug'; const debug = debugFactory('chat:attachments'); -export const ALLOWED_IMAGE_MIME_TYPES = [ +const ALLOWED_IMAGE_MIME_TYPES = [ 'image/png', 'image/jpeg', 'image/webp', @@ -20,7 +20,7 @@ export const ALLOWED_IMAGE_MIME_TYPES = [ export type AllowedImageMimeType = (typeof ALLOWED_IMAGE_MIME_TYPES)[number]; -export const ALLOWED_FILE_MIME_TYPES = [ +const ALLOWED_FILE_MIME_TYPES = [ 'application/pdf', 'text/plain', 'text/csv', @@ -39,7 +39,7 @@ export type AllowedFileMimeType = (typeof ALLOWED_FILE_MIME_TYPES)[number]; // them through the existing `[IMAGE:]` vision path (see `extractVideoFrames` + // `buildMessageWithAttachments`). So a vision-capable tier is required, the same // gate as images; audio and motion between frames are not conveyed. -export const ALLOWED_VIDEO_MIME_TYPES = ['video/mp4', 'video/quicktime', 'video/webm'] as const; +const ALLOWED_VIDEO_MIME_TYPES = ['video/mp4', 'video/quicktime', 'video/webm'] as const; export type AllowedVideoMimeType = (typeof ALLOWED_VIDEO_MIME_TYPES)[number]; export type AllowedAttachmentMimeType = @@ -75,7 +75,6 @@ export const ATTACHMENT_MAX_FILES = 4; export const ATTACHMENT_MAX_IMAGE_SIZE_BYTES = 8 * 1024 * 1024; // 8 MB export const ATTACHMENT_MAX_FILE_SIZE_BYTES = 16 * 1024 * 1024; // 16 MB export const ATTACHMENT_MAX_VIDEO_SIZE_BYTES = 50 * 1024 * 1024; // 50 MB -export const ATTACHMENT_MAX_SIZE_BYTES = ATTACHMENT_MAX_IMAGE_SIZE_BYTES; // Still frames sampled from a video and forwarded as `[IMAGE:]` markers. 2 keeps // a clip within the 4-marker budget alongside other attachments (e.g. 1 video + // 2 images, or 2 videos). @@ -97,7 +96,7 @@ export interface Attachment { frames?: string[]; } -export type AttachmentError = +type AttachmentError = | { code: 'unsupported_type'; mimeType: string } | { code: 'too_large'; sizeBytes: number; maxBytes: number } | { code: 'too_many'; kind: AttachmentKind; max: number } @@ -113,28 +112,24 @@ export function isVideoMimeType(mime: string): mime is AllowedVideoMimeType { return (ALLOWED_VIDEO_MIME_TYPES as readonly string[]).includes(mime); } -export function isAllowedAttachmentMimeType(mime: string): mime is AllowedAttachmentMimeType { - return (ALLOWED_ATTACHMENT_MIME_TYPES as readonly string[]).includes(mime); -} - /** * The exact MIME set the ingest validator accepts — images plus the * text-extractable documents. A strict subset of {@link AllowedAttachmentMimeType} * (which also lists reference-only types like CSV/DOCX/ZIP that we reject). */ -export type SupportedAttachmentMimeType = +type SupportedAttachmentMimeType = | AllowedImageMimeType | AllowedVideoMimeType | (typeof EXTRACTABLE_FILE_MIME_TYPES)[number]; /** - * Stricter gate than {@link isAllowedAttachmentMimeType}: only the formats the - * backend actually reads — images, plus the text-extractable documents (PDF via + * Only accepts formats the backend actually reads — images, plus the + * text-extractable documents (PDF via * pdf_extract; TXT/Markdown via UTF-8). DOCX/PPTX/XLSX/ZIP are excluded so they * can't be attached as content-less reference stubs. Applied on every ingest * path (picker, drag-drop, paste). */ -export function isSupportedAttachmentMimeType(mime: string): mime is SupportedAttachmentMimeType { +function isSupportedAttachmentMimeType(mime: string): mime is SupportedAttachmentMimeType { return ( (ALLOWED_IMAGE_MIME_TYPES as readonly string[]).includes(mime) || (ALLOWED_VIDEO_MIME_TYPES as readonly string[]).includes(mime) || diff --git a/app/src/lib/channels/routing.ts b/app/src/lib/channels/routing.ts index d50dfdbc9..9215fa887 100644 --- a/app/src/lib/channels/routing.ts +++ b/app/src/lib/channels/routing.ts @@ -3,13 +3,10 @@ import type { ChannelConnection, ChannelConnectionsState, ChannelType, - OutboundRoute, } from '../../types/channels'; const SEND_PRIORITY: ChannelAuthMode[] = ['managed_dm', 'oauth', 'bot_token', 'api_key']; -const ALL_CHANNELS: ChannelType[] = ['telegram', 'discord', 'web']; - function isConnected(connection: ChannelConnection | undefined): boolean { return connection?.status === 'connected'; } @@ -27,25 +24,3 @@ export function resolvePreferredAuthModeForChannel( } return null; } - -export function resolveOutboundRoute( - state: ChannelConnectionsState, - preferredChannel?: ChannelType -): OutboundRoute | null { - const channel = preferredChannel ?? state.defaultMessagingChannel; - const mode = resolvePreferredAuthModeForChannel(state, channel); - if (mode) { - return { channel, authMode: mode }; - } - - // Try other channels as fallback. - for (const fallback of ALL_CHANNELS) { - if (fallback === channel) continue; - const fallbackMode = resolvePreferredAuthModeForChannel(state, fallback); - if (fallbackMode) { - return { channel: fallback, authMode: fallbackMode }; - } - } - - return null; -} diff --git a/app/src/lib/chat/toolCallEnvelope.ts b/app/src/lib/chat/toolCallEnvelope.ts index 70824e7e1..6008aa828 100644 --- a/app/src/lib/chat/toolCallEnvelope.ts +++ b/app/src/lib/chat/toolCallEnvelope.ts @@ -32,7 +32,7 @@ interface EnvelopeToolCall { } /** Result of unwrapping a chat message's raw `content` string. */ -export interface UnwrappedToolCallMessage { +interface UnwrappedToolCallMessage { /** The human-readable text to render (never the raw JSON). */ text: string; /** Tool names found on the envelope's `tool_calls` array, in order. */ diff --git a/app/src/lib/commands/hotkeyManager.ts b/app/src/lib/commands/hotkeyManager.ts index 0648c2526..66e28dcd8 100644 --- a/app/src/lib/commands/hotkeyManager.ts +++ b/app/src/lib/commands/hotkeyManager.ts @@ -18,7 +18,7 @@ function isEditableTarget(e: KeyboardEvent): boolean { return false; } -export interface HotkeyManager { +interface HotkeyManager { init: () => void; teardown: () => void; pushFrame: (kind: ScopeKind, id: string) => symbol; diff --git a/app/src/lib/commands/registry.ts b/app/src/lib/commands/registry.ts index 9308b538c..945709264 100644 --- a/app/src/lib/commands/registry.ts +++ b/app/src/lib/commands/registry.ts @@ -1,7 +1,7 @@ import { parseShortcut } from './shortcut'; import type { Action, RegisteredAction } from './types'; -export interface Registry { +interface Registry { registerAction: (action: Action, scopeFrame: symbol) => () => void; getAction: (id: string) => RegisteredAction | undefined; getActiveActions: (scopeStack: symbol[]) => RegisteredAction[]; diff --git a/app/src/lib/composio/connectionCache.ts b/app/src/lib/composio/connectionCache.ts index 32e5e8b39..a637b84cc 100644 --- a/app/src/lib/composio/connectionCache.ts +++ b/app/src/lib/composio/connectionCache.ts @@ -38,7 +38,7 @@ const TTL_MS = 24 * 60 * 60 * 1000; const ACTIVE_USER_KEY = 'OPENHUMAN_ACTIVE_USER_ID'; -export interface CachedConnectionState { +interface CachedConnectionState { fetchedAt: number; connections: ComposioConnection[]; toolkits: string[]; diff --git a/app/src/lib/composio/hooks.ts b/app/src/lib/composio/hooks.ts index 7cb6b1dde..ba33741aa 100644 --- a/app/src/lib/composio/hooks.ts +++ b/app/src/lib/composio/hooks.ts @@ -60,7 +60,7 @@ async function fetchLegWithRetries( // ── useComposioIntegrations ─────────────────────────────────────── -export interface UseComposioIntegrationsResult { +interface UseComposioIntegrationsResult { /** Toolkit slugs enabled on the backend allowlist. */ toolkits: string[]; /** @@ -351,7 +351,7 @@ export function useComposioIntegrations(pollIntervalMs = 5_000): UseComposioInte // ── useAgentReadyComposioToolkits ───────────────────────────────── -export interface UseAgentReadyComposioToolkitsResult { +interface UseAgentReadyComposioToolkitsResult { /** Lowercased slugs of toolkits that ship an agent-ready catalog. */ agentReady: ReadonlySet; /** Whether the initial fetch is still in flight. */ diff --git a/app/src/lib/composio/oauthHandoff.ts b/app/src/lib/composio/oauthHandoff.ts index 0c5081008..9682bf370 100644 --- a/app/src/lib/composio/oauthHandoff.ts +++ b/app/src/lib/composio/oauthHandoff.ts @@ -6,9 +6,9 @@ */ /** Toolkits whose OAuth flows are hosted by Meta. */ -export const META_OAUTH_TOOLKITS = ['instagram', 'facebook'] as const; +const META_OAUTH_TOOLKITS = ['instagram', 'facebook'] as const; -export type MetaOAuthToolkit = (typeof META_OAUTH_TOOLKITS)[number]; +type MetaOAuthToolkit = (typeof META_OAUTH_TOOLKITS)[number]; export function isMetaOAuthToolkit(slug: string): slug is MetaOAuthToolkit { const key = slug.trim().toLowerCase(); diff --git a/app/src/lib/composio/types.ts b/app/src/lib/composio/types.ts index bb0323a23..c84f95483 100644 --- a/app/src/lib/composio/types.ts +++ b/app/src/lib/composio/types.ts @@ -180,12 +180,7 @@ export interface ComposioDisableTriggerResponse { * Mirrors the `SkillConnectionStatus` shape so the same * `UnifiedSkillCard` can render both. */ -export type ComposioConnectionState = - | 'disconnected' - | 'pending' - | 'connected' - | 'expired' - | 'error'; +type ComposioConnectionState = 'disconnected' | 'pending' | 'connected' | 'expired' | 'error'; export function deriveComposioState( connection: ComposioConnection | undefined @@ -198,20 +193,3 @@ export function deriveComposioState( if (status === 'FAILED' || status === 'ERROR') return 'error'; return 'disconnected'; } - -export interface ComposioConnectionsState { - primary: ComposioConnectionState; - count: number; -} - -/** - * Derive composite state from multiple connections for a toolkit. - * Uses the first connection's state as primary (caller must ensure - * connections are sorted by priority/age), and reports the total count. - */ -export function deriveComposioStates( - connections: ComposioConnection[] | undefined -): ComposioConnectionsState { - if (!connections || connections.length === 0) return { primary: 'disconnected', count: 0 }; - return { primary: deriveComposioState(connections[0]), count: connections.length }; -} diff --git a/app/src/lib/cron/schedulePresets.ts b/app/src/lib/cron/schedulePresets.ts index b8e19ac90..77d09b567 100644 --- a/app/src/lib/cron/schedulePresets.ts +++ b/app/src/lib/cron/schedulePresets.ts @@ -10,7 +10,7 @@ * provides the canonical cron expressions, their shared labels, and a lookup set. */ -export interface SchedulePreset { +interface SchedulePreset { /** i18n key from the shared cron namespace (e.g. 'settings.cron.schedule.every30min'). */ labelKey: string; /** 5-field cron expression. */ diff --git a/app/src/lib/flows/cron.ts b/app/src/lib/flows/cron.ts index d32f2bc18..85c85fae1 100644 --- a/app/src/lib/flows/cron.ts +++ b/app/src/lib/flows/cron.ts @@ -173,22 +173,6 @@ export function describeCron(expr: string): string { return daysPhrase === 'every day' ? `Every day at ${time}` : `At ${time} on ${daysPhrase}`; } -/** - * The tagged shapes a trigger node's `config.schedule` can hold. The flows - * engine's `crate::openhuman::cron::Schedule` (an internally-tagged enum, - * `#[serde(tag = "kind")]`) is what `flows::tools` and the workflow-builder - * agent actually write today — `{kind:"cron",expr,tz?,active_hours?}` / - * `{kind:"at",at}` / `{kind:"every",every_ms}`. A bare cron string (what the - * visual builder above compiles to, and what the bundled flow templates use) - * is also accepted — the Rust side's custom `Deserialize` treats it as - * shorthand for `Cron{expr}`. - */ -export type ScheduleValue = - | string - | { kind?: string; expr?: string; tz?: string; at?: string; every_ms?: number } - | null - | undefined; - /** * Pull the bare cron expression out of a schedule value, if it has one (a * plain string, or a `{kind:"cron", expr}` object). Returns `null` for the @@ -231,10 +215,10 @@ export function describeEveryMs(everyMs: number): string { } /** - * Plain-language summary of a trigger's `schedule` config value, across every - * shape it can actually hold (see {@link ScheduleValue}). This is the single - * place that decides "No schedule set" vs. a real summary — callers should - * never re-derive it from just the cron string, or a valid `every`/`at` + * Plain-language summary of a trigger's `schedule` config value, across bare + * cron strings and the tagged `cron`, `at`, and `every` shapes. This is the + * single place that decides "No schedule set" vs. a real summary — callers + * should never re-derive it from just the cron string, or a valid `every`/`at` * schedule reads as unset (the canvas trigger-node bug this guards against). */ export function describeSchedule(value: unknown): string { diff --git a/app/src/lib/flows/graphAdapter.ts b/app/src/lib/flows/graphAdapter.ts index 6b7d0d495..f35981a38 100644 --- a/app/src/lib/flows/graphAdapter.ts +++ b/app/src/lib/flows/graphAdapter.ts @@ -50,7 +50,7 @@ export interface FlowNodeData extends Record { export type FlowNode = Node; export type FlowEdge = Edge; -export interface Point { +interface Point { x: number; y: number; } @@ -157,7 +157,7 @@ export function workflowGraphToXyflow(graph: WorkflowGraph): { * effective port names `FlowNodeComponent` renders — and may be `null` when a * node exposes a single unnamed handle, in which case they default to `main`. */ -export interface FlowConnectionCandidate { +interface FlowConnectionCandidate { source?: string | null; target?: string | null; sourceHandle?: string | null; diff --git a/app/src/lib/flows/graphDiff.ts b/app/src/lib/flows/graphDiff.ts index adbaae584..83aee08df 100644 --- a/app/src/lib/flows/graphDiff.ts +++ b/app/src/lib/flows/graphDiff.ts @@ -11,7 +11,7 @@ */ import type { WorkflowGraph } from './types'; -export interface GraphDiff { +interface GraphDiff { /** Node ids present in the proposed graph but not the current one. */ addedNodeIds: Set; /** Node ids present in the current graph but dropped from the proposed one. */ diff --git a/app/src/lib/flows/nodeKindMeta.ts b/app/src/lib/flows/nodeKindMeta.ts index 1c75cb1da..b0d6d3d4a 100644 --- a/app/src/lib/flows/nodeKindMeta.ts +++ b/app/src/lib/flows/nodeKindMeta.ts @@ -22,7 +22,7 @@ export type NodeColor = 'sage' | 'primary' | 'amber' | 'coral' | 'neutral'; */ export type NodeGroup = 'triggers' | 'actions' | 'logic'; -export interface NodeKindMeta { +interface NodeKindMeta { emoji: string; color: NodeColor; group: NodeGroup; @@ -33,7 +33,7 @@ export interface NodeKindMeta { * leads (every graph needs exactly one); the rest follow the logical grouping * of the `tinyflows::model::NodeKind` enum. */ -export const NODE_KINDS: NodeKind[] = [ +const NODE_KINDS: NodeKind[] = [ 'trigger', 'agent', 'tool_call', @@ -49,7 +49,7 @@ export const NODE_KINDS: NodeKind[] = [ ]; /** Per-kind emoji + border/chip color + palette group. See the module doc. */ -export const NODE_KIND_META: Record = { +const NODE_KIND_META: Record = { trigger: { emoji: '⚡', color: 'sage', group: 'triggers' }, agent: { emoji: '🤖', color: 'primary', group: 'actions' }, tool_call: { emoji: '🔧', color: 'amber', group: 'actions' }, @@ -64,15 +64,9 @@ export const NODE_KIND_META: Record = { output_parser: { emoji: '📋', color: 'amber', group: 'logic' }, }; -/** Palette group render order + the kinds in each, derived from NODE_KIND_META. */ +/** Palette group render order. */ export const NODE_GROUP_ORDER: NodeGroup[] = ['triggers', 'actions', 'logic']; -export const NODE_KINDS_BY_GROUP: Record = { - triggers: NODE_KINDS.filter(k => NODE_KIND_META[k].group === 'triggers'), - actions: NODE_KINDS.filter(k => NODE_KIND_META[k].group === 'actions'), - logic: NODE_KINDS.filter(k => NODE_KIND_META[k].group === 'logic'), -}; - /** * One palette entry. Usually 1:1 with a `NodeKind`, but `tool_call` splits into * TWO entries — an "App action" (Composio OAuth) node and a "Tool" (native @@ -140,7 +134,7 @@ export const PALETTE_ENTRIES_BY_GROUP: Record = { * here so an unrecognized kind renders as a plain neutral node instead of * crashing the whole canvas (there's no error boundary around ``). */ -export const DEFAULT_NODE_META: NodeKindMeta = { emoji: '❔', color: 'neutral', group: 'actions' }; +const DEFAULT_NODE_META: NodeKindMeta = { emoji: '❔', color: 'neutral', group: 'actions' }; /** Resolve a kind's metadata, falling back to {@link DEFAULT_NODE_META}. */ export function nodeKindMeta(kind: NodeKind): NodeKindMeta { diff --git a/app/src/lib/flows/runStepSummary.ts b/app/src/lib/flows/runStepSummary.ts index ddd385768..e024909da 100644 --- a/app/src/lib/flows/runStepSummary.ts +++ b/app/src/lib/flows/runStepSummary.ts @@ -34,9 +34,9 @@ import { type FlowRunItem, isPlainObject } from './runItems'; const log = createDebug('app:flows:step-summary'); -export type StepOutcome = 'success' | 'error' | 'neutral'; +type StepOutcome = 'success' | 'error' | 'neutral'; -export interface StepSummary { +interface StepSummary { outcome: StepOutcome; /** Plain-language, already-localized one-line summary (translation-ready inputs applied). */ text: string; diff --git a/app/src/lib/i18n/I18nContext.tsx b/app/src/lib/i18n/I18nContext.tsx index 3ee190700..21ce2c938 100644 --- a/app/src/lib/i18n/I18nContext.tsx +++ b/app/src/lib/i18n/I18nContext.tsx @@ -99,5 +99,3 @@ export function I18nProvider({ children }: { children: ReactNode }) { export function useT(): I18nContextValue { return useContext(I18nContext); } - -export { type Locale } from './types'; diff --git a/app/src/lib/mcp/errorHandler.ts b/app/src/lib/mcp/errorHandler.ts index a05b91ae5..0d67b010d 100644 --- a/app/src/lib/mcp/errorHandler.ts +++ b/app/src/lib/mcp/errorHandler.ts @@ -7,15 +7,7 @@ import { ValidationError } from './validation'; export enum ErrorCategory { CHAT = 'CHAT', MSG = 'MSG', - CONTACT = 'CONTACT', - GROUP = 'GROUP', - MEDIA = 'MEDIA', - PROFILE = 'PROFILE', - AUTH = 'AUTH', - ADMIN = 'ADMIN', VALIDATION = 'VALIDATION', - SEARCH = 'SEARCH', - DRAFT = 'DRAFT', } function generateErrorCode(functionName: string, category?: ErrorCategory | string): string { diff --git a/app/src/lib/mcp/index.ts b/app/src/lib/mcp/index.ts index c4667e4db..74c97db59 100644 --- a/app/src/lib/mcp/index.ts +++ b/app/src/lib/mcp/index.ts @@ -8,14 +8,3 @@ export * from './validation'; export * from './errorHandler'; export * from './logger'; export { SocketIOMCPTransportImpl } from './transport'; -export { - enforceRateLimit, - resetRequestCallCount, - classifyTool, - isStateOnlyTool, - isReadOnlyTool, - isHeavyTool, - getRateLimitStatus, - RATE_LIMIT_CONFIG, -} from './rateLimiter'; -export type { ToolTier } from './rateLimiter'; diff --git a/app/src/lib/mcp/rateLimiter.ts b/app/src/lib/mcp/rateLimiter.ts index 394df0bf1..5503b4862 100644 --- a/app/src/lib/mcp/rateLimiter.ts +++ b/app/src/lib/mcp/rateLimiter.ts @@ -31,7 +31,7 @@ export const RATE_LIMIT_CONFIG = { // Tool classification // --------------------------------------------------------------------------- -export type ToolTier = 'state_only' | 'api_read' | 'api_write'; +type ToolTier = 'state_only' | 'api_read' | 'api_write'; /** * Tools that ONLY read from cached Redux state — zero Telegram API calls. @@ -207,9 +207,6 @@ export function isHeavyTool(toolName: string): boolean { return API_WRITE_TOOLS.has(toolName); } -/** @deprecated Use isStateOnlyTool instead */ -export const isReadOnlyTool = isStateOnlyTool; - /** * Reset the per-request call counter. Call at the start of each new * MCP request (agent turn) to allow a fresh budget of tool calls. diff --git a/app/src/lib/mcp/types.ts b/app/src/lib/mcp/types.ts index 8055f897b..77c5f5745 100644 --- a/app/src/lib/mcp/types.ts +++ b/app/src/lib/mcp/types.ts @@ -2,29 +2,6 @@ * MCP (Model Context Protocol) shared types */ -export interface MCPServerConfig { - name: string; - version: string; -} - -export interface MCPToolInputSchema { - type: 'object'; - properties: Record; - required?: string[]; -} - -export interface MCPTool { - name: string; - description: string; - inputSchema: MCPToolInputSchema; - toHumanReadableAction?: (action: Record) => unknown; -} - -export interface MCPToolCall { - name: string; - arguments: Record; -} - export interface MCPToolResult { content: Array<{ type: 'text'; text: string }>; isError?: boolean; diff --git a/app/src/lib/nativeNotifications/index.ts b/app/src/lib/nativeNotifications/index.ts index 720a70c1e..8b58c1d78 100644 --- a/app/src/lib/nativeNotifications/index.ts +++ b/app/src/lib/nativeNotifications/index.ts @@ -1,2 +1 @@ export { startNativeNotificationsService, stopNativeNotificationsService } from './service'; -export { ensureNotificationPermission, showNativeNotification } from './tauriBridge'; diff --git a/app/src/lib/nativeNotifications/tauriBridge.ts b/app/src/lib/nativeNotifications/tauriBridge.ts index ba10a096d..8108c7bef 100644 --- a/app/src/lib/nativeNotifications/tauriBridge.ts +++ b/app/src/lib/nativeNotifications/tauriBridge.ts @@ -11,13 +11,13 @@ const errLog = debug('native-notifications:bridge:error'); export type NotificationPermissionState = 'not_tauri' | 'granted' | 'denied' | 'prompt' | 'unknown'; -export interface ShowNativeNotificationArgs { +interface ShowNativeNotificationArgs { title: string; body: string; tag?: string; } -export interface ShowNativeNotificationResult { +interface ShowNativeNotificationResult { delivered: boolean; reason?: 'not_tauri' | 'send_failed'; error?: string; diff --git a/app/src/lib/orchestration/mergeToolActivity.ts b/app/src/lib/orchestration/mergeToolActivity.ts index ce5424057..3f689734d 100644 --- a/app/src/lib/orchestration/mergeToolActivity.ts +++ b/app/src/lib/orchestration/mergeToolActivity.ts @@ -28,12 +28,12 @@ export interface ToolActivity { } /** A plain (non-tool) transcript row, rendered as a bubble/inline block. */ -export interface MessageRow { +interface MessageRow { kind: 'message'; message: ChatMessage; } -export type TranscriptRow = ToolActivity | MessageRow; +type TranscriptRow = ToolActivity | MessageRow; /** Whether a `tool_result` row represents a failure. */ export function toolResultFailed( diff --git a/app/src/lib/orchestration/orchestrationClient.ts b/app/src/lib/orchestration/orchestrationClient.ts index 7ae9906b1..f16efbf61 100644 --- a/app/src/lib/orchestration/orchestrationClient.ts +++ b/app/src/lib/orchestration/orchestrationClient.ts @@ -116,24 +116,24 @@ export interface OrchestrationStatus { cloudReachable?: boolean; } -export interface SessionsListResponse { +interface SessionsListResponse { sessions: SessionSummary[]; } -export interface SessionCreateResponse { +interface SessionCreateResponse { session: SessionSummary; } -export interface MessagesListResponse { +interface MessagesListResponse { messages: OrchestrationMessage[]; } -export interface SendMasterMessageResponse { +interface SendMasterMessageResponse { ok: true; messageId: string; } -export interface MarkReadResponse { +interface MarkReadResponse { ok: true; } @@ -313,5 +313,3 @@ export const orchestrationClient = { /** The relay endpoint + network label the core is talking to (RelayBadge). */ relayInfo: () => call('openhuman.orchestration_relay_info', {}), }; - -export type OrchestrationClient = typeof orchestrationClient; diff --git a/app/src/lib/orchestration/useOrchestrationChats.ts b/app/src/lib/orchestration/useOrchestrationChats.ts index 042791f94..47e2bd49e 100644 --- a/app/src/lib/orchestration/useOrchestrationChats.ts +++ b/app/src/lib/orchestration/useOrchestrationChats.ts @@ -68,20 +68,20 @@ export interface ChatWindow { type Translate = (key: string) => string; -export type SessionsState = +type SessionsState = | { status: 'loading' } | { status: 'error'; message: string } | { status: 'payment_required' } | { status: 'ok' }; -export type MessagesPaneState = +type MessagesPaneState = | { status: 'idle' } | { status: 'loading' } | { status: 'error'; message: string } | { status: 'ok' }; /** RPC `chat` key for a summary: sessions key on their id, pinned on their kind. */ -export function chatKeyForSession(summary: SessionSummary): string { +function chatKeyForSession(summary: SessionSummary): string { return summary.chatKind === 'session' ? summary.sessionId : summary.chatKind; } @@ -167,7 +167,7 @@ function buildChatWindow( }; } -export interface UseOrchestrationChatsResult { +interface UseOrchestrationChatsResult { sessionsState: SessionsState; messagesState: MessagesPaneState; chats: ChatWindow[]; diff --git a/app/src/lib/orchestration/useOrchestrationSessions.ts b/app/src/lib/orchestration/useOrchestrationSessions.ts index 0ed1232b0..8bcf4bdbc 100644 --- a/app/src/lib/orchestration/useOrchestrationSessions.ts +++ b/app/src/lib/orchestration/useOrchestrationSessions.ts @@ -28,13 +28,13 @@ const debug = debugFactory('orchestration:sessions'); const TRANSCRIPT_LIMIT = 100; -export type SessionsState = +type SessionsState = | { status: 'loading' } | { status: 'error'; message: string } | { status: 'payment_required' } | { status: 'ok' }; -export interface UseContactSessionsResult { +interface UseContactSessionsResult { state: SessionsState; /** All non-pinned session windows. */ sessions: SessionSummary[]; @@ -119,13 +119,13 @@ export function mapTranscriptMessage(message: OrchestrationMessage): ChatMessage }; } -export type TranscriptState = +type TranscriptState = | { status: 'idle' } | { status: 'loading' } | { status: 'error'; message: string } | { status: 'ok' }; -export interface UseSessionTranscriptResult { +interface UseSessionTranscriptResult { state: TranscriptState; messages: ChatMessage[]; refresh: () => Promise; diff --git a/app/src/lib/orchestration/usePairing.ts b/app/src/lib/orchestration/usePairing.ts index 8e6291335..6283fa1ef 100644 --- a/app/src/lib/orchestration/usePairing.ts +++ b/app/src/lib/orchestration/usePairing.ts @@ -16,13 +16,13 @@ import { type PairingSnapshot, PaymentRequiredError } from '../agentworld/invoke const debug = debugFactory('orchestration:pairing'); -export type PairingState = +type PairingState = | { status: 'loading' } | { status: 'error'; message: string } | { status: 'payment_required' } | { status: 'ok'; snapshot: PairingSnapshot }; -export interface UsePairingResult { +interface UsePairingResult { state: PairingState; /** Re-fetch the pairing snapshot. */ reload: () => Promise; diff --git a/app/src/lib/platform.ts b/app/src/lib/platform.ts index c311afaad..54208868e 100644 --- a/app/src/lib/platform.ts +++ b/app/src/lib/platform.ts @@ -86,13 +86,3 @@ export function getIsAndroid(): boolean { export function getIsMobile(): boolean { return getIsIOS() || getIsAndroid(); } - -/** - * Convenience re-export as a constant. - * Safe to import and use at module level — evaluated once on import. - * - * NOTE: if you need test overrides to work, call getIsIOS() instead, - * since this is evaluated at module load time. - */ -export const isIOS: boolean = detectIOS(); -export const isAndroid: boolean = detectAndroid(); diff --git a/app/src/lib/recallCalendar/hooks.ts b/app/src/lib/recallCalendar/hooks.ts index 6f1f088f9..e080a7c44 100644 --- a/app/src/lib/recallCalendar/hooks.ts +++ b/app/src/lib/recallCalendar/hooks.ts @@ -16,7 +16,7 @@ import type { RecallCalendarStatus } from './recallCalendarApi'; const POLL_INTERVAL_MS = 5000; type CalendarProvider = 'composio' | 'recall'; -export interface UseRecallCalendar { +interface UseRecallCalendar { status: RecallCalendarStatus | null; loading: boolean; busy: boolean; diff --git a/app/src/lib/recallCalendar/recallCalendarApi.ts b/app/src/lib/recallCalendar/recallCalendarApi.ts index 8004cc447..369d9afae 100644 --- a/app/src/lib/recallCalendar/recallCalendarApi.ts +++ b/app/src/lib/recallCalendar/recallCalendarApi.ts @@ -34,11 +34,11 @@ export interface RecallCalendarStatus { email?: string; } -export interface RecallCalendarConnect { +interface RecallCalendarConnect { connectUrl: string; } -export interface RecallCalendarDisconnect { +interface RecallCalendarDisconnect { disconnected: boolean; } diff --git a/app/src/lib/theme/presets.ts b/app/src/lib/theme/presets.ts index 5b20b7788..5b87abb14 100644 --- a/app/src/lib/theme/presets.ts +++ b/app/src/lib/theme/presets.ts @@ -10,8 +10,8 @@ import type { Theme, ThemeFamily } from './types'; * persisted selections keep resolving. */ -export const LIGHT_THEME_ID = 'light'; -export const DARK_THEME_ID = 'dark'; +const LIGHT_THEME_ID = 'light'; +const DARK_THEME_ID = 'dark'; const MONO_STACK = `'JetBrains Mono', 'SF Mono', Consolas, 'Liberation Mono', Courier, monospace`; const SERIF_STACK = `'Newsreader', Georgia, Cambria, 'Times New Roman', Times, serif`; diff --git a/app/src/lib/theme/tokens.ts b/app/src/lib/theme/tokens.ts index ebb0d5c9c..66984ebe5 100644 --- a/app/src/lib/theme/tokens.ts +++ b/app/src/lib/theme/tokens.ts @@ -11,7 +11,7 @@ */ /** Surface (background) token keys, without the leading `--`. */ -export const SURFACE_KEYS = [ +const SURFACE_KEYS = [ 'surface', 'surface-canvas', 'surface-muted', @@ -22,7 +22,7 @@ export const SURFACE_KEYS = [ ] as const; /** Text token keys. */ -export const CONTENT_KEYS = [ +const CONTENT_KEYS = [ 'content', 'content-secondary', 'content-muted', @@ -31,7 +31,7 @@ export const CONTENT_KEYS = [ ] as const; /** Border token keys. */ -export const LINE_KEYS = ['line', 'line-strong', 'line-subtle'] as const; +const LINE_KEYS = ['line', 'line-strong', 'line-subtle'] as const; /** Accent palette family names. Each expands to shades 50â€Ļ950. */ export const ACCENT_FAMILIES = ['primary', 'sage', 'amber', 'coral'] as const; @@ -39,23 +39,8 @@ export const ACCENT_FAMILIES = ['primary', 'sage', 'amber', 'coral'] as const; /** Tailwind accent shade steps. */ export const ACCENT_SHADES = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const; -/** All accent token keys, e.g. `primary-500`. */ -export const ACCENT_KEYS = ACCENT_FAMILIES.flatMap(fam => - ACCENT_SHADES.map(shade => `${fam}-${shade}` as const) -); - /** The `-500` base key of each accent family (what the editor shows by default). */ -export const ACCENT_BASE_KEYS = ACCENT_FAMILIES.map(fam => `${fam}-500`); - -/** Every colour token key the theme system knows about. */ -export const ALL_COLOR_KEYS: string[] = [ - ...SURFACE_KEYS, - ...CONTENT_KEYS, - ...LINE_KEYS, - ...ACCENT_KEYS, -]; - -export type ColorTokenKey = string; +const ACCENT_BASE_KEYS = ACCENT_FAMILIES.map(fam => `${fam}-500`); /** Font role keys, mapped to the `--font-` vars. */ export const FONT_ROLES = ['title', 'heading', 'body', 'mono', 'serif'] as const; @@ -66,7 +51,7 @@ export type FontRole = (typeof FONT_ROLES)[number]; * heading; `keys` are the tokens shown in that group. Accent shades beyond the * base `-500` are surfaced via the per-family advanced expander, not listed here. */ -export interface ColorGroup { +interface ColorGroup { id: string; i18nKey: string; keys: string[]; @@ -80,7 +65,7 @@ export const COLOR_GROUPS: ColorGroup[] = [ ]; /** Curated font choices offered per role in the Theme Studio. */ -export interface FontChoice { +interface FontChoice { /** Stable id stored in the theme. */ id: string; /** Human label (not translated — font names are proper nouns). */ diff --git a/app/src/lib/tunnel/crypto.ts b/app/src/lib/tunnel/crypto.ts index 7c9c3656a..4e1117ead 100644 --- a/app/src/lib/tunnel/crypto.ts +++ b/app/src/lib/tunnel/crypto.ts @@ -103,7 +103,7 @@ export function deriveSharedSecret(myPriv: Uint8Array, theirPub: Uint8Array): Ui * emits cannot decrypt under the server's own opener — guards against * cross-direction reflection. */ -export interface SessionKeys { +interface SessionKeys { c2s: Uint8Array; s2c: Uint8Array; } @@ -149,7 +149,7 @@ export function deriveSessionKeys( } /** Which side of the tunnel is operating the cipher. */ -export type TunnelRole = 'client' | 'server'; +type TunnelRole = 'client' | 'server'; /** * Stateful directional cipher mirroring Rust `TunnelCipher::for_role`. Holds diff --git a/app/src/lib/webviewNotifications/index.ts b/app/src/lib/webviewNotifications/index.ts index 559fb37e4..990a9ca03 100644 --- a/app/src/lib/webviewNotifications/index.ts +++ b/app/src/lib/webviewNotifications/index.ts @@ -1,6 +1 @@ -export { - handleNotificationClick, - startWebviewNotificationsService, - stopWebviewNotificationsService, -} from './service'; -export { WEBVIEW_NOTIFICATION_FIRED_EVENT, type WebviewNotificationFired } from './types'; +export { startWebviewNotificationsService, stopWebviewNotificationsService } from './service'; diff --git a/app/src/pages/FlowCanvasPage.tsx b/app/src/pages/FlowCanvasPage.tsx index a7f42381f..b711fb11a 100644 --- a/app/src/pages/FlowCanvasPage.tsx +++ b/app/src/pages/FlowCanvasPage.tsx @@ -60,7 +60,7 @@ import type { ToastNotification } from '../types/intelligence'; * agent" action (Phase 5c). Rides in `location.state` (ephemeral). The graph is * supplied by the editor itself, so only the run context travels here. */ -export interface CopilotRepairSeed { +interface CopilotRepairSeed { runId: string; error?: string | null; failingNodeIds?: string[]; @@ -72,7 +72,7 @@ export interface CopilotRepairSeed { * open already building the described workflow. Rides in `location.state` * (ephemeral — lost on hard reload, which just leaves a blank flow to edit). */ -export interface CopilotBuildSeed { +interface CopilotBuildSeed { /** The user's free-text workflow description from the prompt bar. */ description: string; /** @@ -105,7 +105,7 @@ export function asCopilotBuildSeed(state: unknown): CopilotBuildSeed | null { * pressing Send themselves. Rides in `location.state` (ephemeral — lost on * hard reload, which just leaves a blank flow with an empty copilot input). */ -export interface CopilotPrefillSeed { +interface CopilotPrefillSeed { /** The text to populate the copilot's composer with, unsent. */ text: string; /** @@ -135,7 +135,7 @@ export function asCopilotPrefillSeed(state: unknown): CopilotPrefillSeed | null } /** Narrow an opaque `location.state` to a {@link CopilotRepairSeed}. */ -export function asCopilotRepairSeed(state: unknown): CopilotRepairSeed | null { +function asCopilotRepairSeed(state: unknown): CopilotRepairSeed | null { if (!state || typeof state !== 'object') return null; const record = state as Record; const seed = record.copilotRepair; diff --git a/app/src/pages/Routines.tsx b/app/src/pages/Routines.tsx deleted file mode 100644 index 5fd98f718..000000000 --- a/app/src/pages/Routines.tsx +++ /dev/null @@ -1,252 +0,0 @@ -import createDebug from 'debug'; -import { useCallback, useEffect, useRef, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; - -import RoutineCard from '../components/routines/RoutineCard'; -import Button from '../components/ui/Button'; -import { useT } from '../lib/i18n/I18nContext'; -import { - type CoreCronJob, - type CoreCronRun, - openhumanCronList, - openhumanCronRun, - openhumanCronRuns, - openhumanCronUpdate, -} from '../utils/tauriCommands'; - -const log = createDebug('app:routines'); - -const Routines = () => { - const { t } = useT(); - const navigate = useNavigate(); - - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [jobs, setJobs] = useState([]); - const [runsByJob, setRunsByJob] = useState>({}); - const [busyKeys, setBusyKeys] = useState>(new Set()); - - const isMountedRef = useRef(true); - - useEffect(() => { - return () => { - isMountedRef.current = false; - }; - }, []); - - const addBusy = (key: string) => setBusyKeys(prev => new Set(prev).add(key)); - const removeBusy = (key: string) => - setBusyKeys(prev => { - const next = new Set(prev); - next.delete(key); - return next; - }); - - const loadJobs = useCallback(async () => { - log('loading routines'); - setLoading(true); - setError(null); - try { - const response = await openhumanCronList(); - const sorted = [...response.result].sort((a, b) => { - const aTs = new Date(a.next_run).getTime(); - const bTs = new Date(b.next_run).getTime(); - return aTs - bTs; - }); - setJobs(sorted); - log('loaded %d routines', sorted.length); - } catch (err) { - log('load failed', err); - const message = err instanceof Error ? err.message : String(err); - setError(message); - } finally { - setLoading(false); - } - }, []); - - useEffect(() => { - void loadJobs(); - }, [loadJobs]); - - const handleToggle = async (job: CoreCronJob) => { - const key = `toggle:${job.id}`; - addBusy(key); - setError(null); - try { - const response = await openhumanCronUpdate(job.id, { enabled: !job.enabled }); - const updated = response.result; - setJobs(prev => prev.map(j => (j.id === updated.id ? updated : j))); - } catch (err) { - const message = err instanceof Error ? err.message : String(err); - setError(message); - } finally { - removeBusy(key); - } - }; - - const handleRunNow = async (jobId: string) => { - const key = `run:${jobId}`; - addBusy(key); - setError(null); - try { - const runResponse = await openhumanCronRun(jobId); - - if (runResponse.result.status === 'queued') { - // Job was enqueued asynchronously — poll until a new run record appears. - // Compare by the latest run's id (not list length) so this works correctly - // when the job already has >= 10 runs and the list stays at the fetch limit. - const previousLatestId = (runsByJob[jobId] ?? [])[0]?.id; - const POLL_INTERVAL_MS = 2000; - const MAX_WAIT_MS = 120_000; - let elapsed = 0; - - while (elapsed < MAX_WAIT_MS) { - await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL_MS)); - if (!isMountedRef.current) return; - elapsed += POLL_INTERVAL_MS; - const runs = await openhumanCronRuns(jobId, 10); - if (!isMountedRef.current) return; - setRunsByJob(prev => ({ ...prev, [jobId]: runs.result })); - const latest = runs.result[0]; - if ( - latest?.id !== undefined && - latest.id !== previousLatestId && - latest.status !== 'queued' - ) { - break; - } - } - if (elapsed >= MAX_WAIT_MS && isMountedRef.current) { - setError(t('routines.runNowTimedOut')); - } - } else { - // Synchronous response (legacy path — kept for backward compatibility). - const runs = await openhumanCronRuns(jobId, 10); - setRunsByJob(prev => ({ ...prev, [jobId]: runs.result })); - } - - if (!isMountedRef.current) return; - - // Refresh job list to update last_status regardless of path. - const response = await openhumanCronList(); - if (!isMountedRef.current) return; - setJobs( - [...response.result].sort( - (a, b) => new Date(a.next_run).getTime() - new Date(b.next_run).getTime() - ) - ); - } catch (err) { - if (!isMountedRef.current) return; - const message = err instanceof Error ? err.message : String(err); - setError(message); - } finally { - if (isMountedRef.current) removeBusy(key); - } - }; - - const handleLoadRuns = async (jobId: string) => { - const key = `runs:${jobId}`; - addBusy(key); - try { - const runs = await openhumanCronRuns(jobId, 10); - setRunsByJob(prev => ({ ...prev, [jobId]: runs.result })); - } catch (err) { - const message = err instanceof Error ? err.message : String(err); - setError(message); - } finally { - removeBusy(key); - } - }; - - return ( -
-
- {/* Header */} -
- -
-

{t('routines.title')}

-

{t('routines.subtitle')}

-
-
- - {/* Error */} - {error && ( -
- {error} -
- )} - - {/* Loading */} - {loading && ( -
-
{t('routines.loading')}
-
- )} - - {/* Empty state */} - {!loading && jobs.length === 0 && !error && ( -
-
- - - -
-
-

{t('routines.empty')}

-

{t('routines.emptyHint')}

-
-
- )} - - {/* Routine cards */} - {!loading && - jobs.map(job => ( - void handleToggle(job)} - onRunNow={() => void handleRunNow(job.id)} - onLoadRuns={() => void handleLoadRuns(job.id)} - /> - ))} - - {/* Refresh */} - {!loading && jobs.length > 0 && ( -
- -
- )} -
-
- ); -}; - -export default Routines; diff --git a/app/src/pages/__tests__/Conversations.render.test.tsx b/app/src/pages/__tests__/Conversations.render.test.tsx index 8116dd9d7..80a55533d 100644 --- a/app/src/pages/__tests__/Conversations.render.test.tsx +++ b/app/src/pages/__tests__/Conversations.render.test.tsx @@ -762,9 +762,6 @@ describe('Conversations — smoke render (#1123 welcome-lock removal)', () => { }); }); - // CycleUsagePill moved into ChatComposer toolbar (#3611) — quota-pill - // loading test removed; the "Loadingâ€Ļ" text no longer renders here. - // Covers budget banner: budget-exhausted banner + OpenRouter CTA it('renders budget-limit banner when teamUsage is present', async () => { // cycleBudgetUsd: 0 → renders "Your included budget is complete" branch @@ -2001,9 +1998,6 @@ describe('Conversations — worker thread back-to-parent navigation (#1624)', () // cycleBudgetUsd=0 → false branch of cycleBudgetUsd > 0 ternary → budgetComplete key expect(screen.getByText(/Your included budget is complete/i)).toBeInTheDocument(); }); - - // CycleUsagePill (cycle-pill tooltip, loading pulse) moved into ChatComposer - // toolbar (#3611) — those tests removed; the pill no longer renders here. }); // #3717 (Bug 2) — A single logical assistant turn can be persisted as multiple diff --git a/app/src/pages/onboarding/customWizardSteps.ts b/app/src/pages/onboarding/customWizardSteps.ts index 7880d0f15..7139b86c9 100644 --- a/app/src/pages/onboarding/customWizardSteps.ts +++ b/app/src/pages/onboarding/customWizardSteps.ts @@ -23,16 +23,3 @@ export const CUSTOM_WIZARD_ROUTES: Record = { memory: '/onboarding/custom/memory', vault: '/onboarding/custom/vault', }; - -/** Deep-link target inside Settings for users who pick "Configure" and want - * to finish wiring this domain up after onboarding. */ -export const CUSTOM_WIZARD_SETTINGS_ROUTES: Record = { - inference: '/settings/llm', - voice: '/settings/voice', - oauth: '/connections?tab=composio-key', - search: '/settings/tools', - embeddings: '/settings/embeddings', - activity: '/settings/activity-level', - memory: '/settings/memory-data', - vault: '/settings/memory-data', -}; diff --git a/app/src/polyfills.ts b/app/src/polyfills.ts index 5c349f69e..81b145509 100644 --- a/app/src/polyfills.ts +++ b/app/src/polyfills.ts @@ -44,7 +44,3 @@ import process from 'process'; (global as any).util = util; } })(); - -// Export for use in modules -export { Buffer, process, util }; -export default Buffer; diff --git a/app/src/services/analyticsRoutes.ts b/app/src/services/analyticsRoutes.ts index 56c75d4fc..bad94933e 100644 --- a/app/src/services/analyticsRoutes.ts +++ b/app/src/services/analyticsRoutes.ts @@ -1,5 +1,5 @@ /** Convert a HashRouter hash into its application path. */ -export function hashToPath(hash: string): string { +function hashToPath(hash: string): string { const withoutHash = hash.slice(1); return withoutHash || '/'; } diff --git a/app/src/services/api/agentRegistryApi.ts b/app/src/services/api/agentRegistryApi.ts index aeecb7171..2a86e1a7e 100644 --- a/app/src/services/api/agentRegistryApi.ts +++ b/app/src/services/api/agentRegistryApi.ts @@ -38,7 +38,7 @@ export interface AgentRegistryEntry { } /** Fields accepted when creating a custom agent. */ -export interface CreateCustomAgentInput { +interface CreateCustomAgentInput { id: string; name: string; description: string; @@ -58,7 +58,7 @@ export interface AgentToolInfo { } /** Patch for `update` — any omitted field is left unchanged. */ -export interface UpdateAgentInput { +interface UpdateAgentInput { name?: string; description?: string; enabled?: boolean; diff --git a/app/src/services/api/agentTeamApi.ts b/app/src/services/api/agentTeamApi.ts index 3e48ffc0f..c96726ea1 100644 --- a/app/src/services/api/agentTeamApi.ts +++ b/app/src/services/api/agentTeamApi.ts @@ -33,7 +33,7 @@ export type AgentTeamStatus = 'active' | 'closed'; export type AgentTeamMemberStatus = 'pending' | 'active' | 'idle' | 'stopped'; /** Lifecycle of a coordination task. Mirrors Rust `AgentTeamTaskStatus`. */ -export type AgentTeamTaskStatus = 'todo' | 'ready' | 'in_progress' | 'blocked' | 'done'; +type AgentTeamTaskStatus = 'todo' | 'ready' | 'in_progress' | 'blocked' | 'done'; /** A team header row. Mirrors Rust `AgentTeam`. */ export interface AgentTeam { @@ -62,7 +62,7 @@ export interface AgentTeamMember { } /** A coordination task within a team. Mirrors Rust `AgentTeamTask`. */ -export interface AgentTeamTask { +interface AgentTeamTask { id: string; teamId: string; title: string; @@ -84,7 +84,7 @@ export interface AgentTeamTask { } /** A team plus its members and tasks. Mirrors Rust `TeamView` (the `get` shape). */ -export interface TeamView { +interface TeamView { team: AgentTeam; members: AgentTeamMember[]; tasks: AgentTeamTask[]; @@ -96,14 +96,14 @@ export interface TeamView { * the object; `gateFailed` carries the unmet-invariant reasons; the rest are * bare tags. */ -export type CompletionOutcome = +type CompletionOutcome = | ({ kind: 'completed' } & AgentTeamTask) | { kind: 'gateFailed'; reasons: string[] } | { kind: 'notClaimed' } | { kind: 'unknownTask' }; /** Result of stopping a member. Mirrors Rust `MemberShutdown`. */ -export interface MemberShutdown { +interface MemberShutdown { member: AgentTeamMember; releasedTaskIds: string[]; } @@ -113,7 +113,7 @@ export interface MemberShutdown { * (internally-tagged on `kind`): `started` carries the dispatched `runId` and the * claimed task; the rest explain why no worker was spawned. */ -export type StartMemberOutcome = +type StartMemberOutcome = | { kind: 'started'; runId: string; task: AgentTeamTask } | { kind: 'blocked'; unmet: string[] } | { kind: 'alreadyClaimed' } @@ -148,7 +148,7 @@ interface RawRunEvent { } /** Optional filters for {@link agentTeamApi.list}. Mirrors `AgentTeamListRequest`. */ -export interface AgentTeamListParams { +interface AgentTeamListParams { parentThreadId?: string; status?: AgentTeamStatus; limit?: number; diff --git a/app/src/services/api/agentWorkApi.ts b/app/src/services/api/agentWorkApi.ts index 83f149822..eb5510fd0 100644 --- a/app/src/services/api/agentWorkApi.ts +++ b/app/src/services/api/agentWorkApi.ts @@ -58,10 +58,10 @@ export interface AgentWorkResponse { * Control verb applied to a single run via `openhuman.agent_work_control`. * Mirrors the Rust `ControlVerb`. `continue` / `followUp` require a message. */ -export type AgentWorkAction = 'stop' | 'retry' | 'continue' | 'follow_up'; +type AgentWorkAction = 'stop' | 'retry' | 'continue' | 'follow_up'; /** Arguments for {@link agentWorkApi.control}. */ -export interface AgentWorkControlArgs { +interface AgentWorkControlArgs { runId: string; action: AgentWorkAction; /** Required for `continue` and `follow_up`; ignored otherwise. */ @@ -71,7 +71,7 @@ export interface AgentWorkControlArgs { } /** Response from `openhuman.agent_work_control`: the re-projected row. */ -export interface AgentWorkControlResponse { +interface AgentWorkControlResponse { row: AgentWorkRow; } diff --git a/app/src/services/api/aiSettingsApi.ts b/app/src/services/api/aiSettingsApi.ts index 54e55ebb3..9c5681c6f 100644 --- a/app/src/services/api/aiSettingsApi.ts +++ b/app/src/services/api/aiSettingsApi.ts @@ -36,7 +36,6 @@ import { type InstalledModelInfo, type LocalAiDiagnostics, type LocalAiStatus, - type ModelPresetResult, openhumanLocalAiApplyPreset, openhumanLocalAiDiagnostics, openhumanLocalAiPresets, @@ -58,12 +57,7 @@ export type WorkloadId = | 'subconscious'; export const CHAT_WORKLOADS: WorkloadId[] = ['chat', 'reasoning', 'agentic', 'coding']; -export const BACKGROUND_WORKLOADS: WorkloadId[] = [ - 'memory', - 'heartbeat', - 'learning', - 'subconscious', -]; +const BACKGROUND_WORKLOADS: WorkloadId[] = ['memory', 'heartbeat', 'learning', 'subconscious']; export const ALL_WORKLOADS: WorkloadId[] = [...CHAT_WORKLOADS, ...BACKGROUND_WORKLOADS]; // Workloads that own a `_provider` config field and must round-trip through @@ -72,7 +66,7 @@ export const ALL_WORKLOADS: WorkloadId[] = [...CHAT_WORKLOADS, ...BACKGROUND_WOR // the managed `vision-v1` tier and is a delegate (like agentic BYOK), so it does // not participate in the billing-suppression / "routed away from OpenHuman" // checks in `useUsageState`. -export const ROUTABLE_WORKLOADS: WorkloadId[] = [...ALL_WORKLOADS, 'vision']; +const ROUTABLE_WORKLOADS: WorkloadId[] = [...ALL_WORKLOADS, 'vision']; export const OPENAI_CODEX_OAUTH_MISSING_AUTH_URL = 'OPENAI_CODEX_OAUTH_MISSING_AUTH_URL'; export const OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL = 'OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL'; @@ -126,11 +120,11 @@ export interface ModelInfo { context_window?: number | null; } -export interface ProviderModelTestResult { +interface ProviderModelTestResult { reply: string; } -export interface OpenAiCodexOAuthStartResult { +interface OpenAiCodexOAuthStartResult { authUrl: string; state?: string; redirectUri?: string; @@ -625,5 +619,3 @@ export const localProvider = { applyPreset: (tier: string) => openhumanLocalAiApplyPreset(tier), setEnabled: (enabled: boolean) => setLocalRuntimeEnabled(enabled), }; - -export type { ModelPresetResult }; diff --git a/app/src/services/api/approvalApi.ts b/app/src/services/api/approvalApi.ts index 868a46145..96b139d4a 100644 --- a/app/src/services/api/approvalApi.ts +++ b/app/src/services/api/approvalApi.ts @@ -86,9 +86,6 @@ export const unwrapRows = (raw: unknown): T[] => { return []; }; -/** Default page size matching the core's `list_recent_decisions` default. */ -export const DEFAULT_APPROVAL_HISTORY_LIMIT = 50; - /** * Fetch recently decided approval rows for the audit/history surface. * `limit` is clamped core-side; omit to use the core default (50). diff --git a/app/src/services/api/channelConnectionsApi.ts b/app/src/services/api/channelConnectionsApi.ts index 7d802aef5..5e4a0511a 100644 --- a/app/src/services/api/channelConnectionsApi.ts +++ b/app/src/services/api/channelConnectionsApi.ts @@ -20,23 +20,23 @@ interface DisconnectChannelOptions { clearMemory?: boolean; } -export interface TelegramLoginStartResult { +interface TelegramLoginStartResult { linkToken: string; telegramUrl: string; botUsername: string; } -export interface DiscordLinkStartResult { +interface DiscordLinkStartResult { linkToken: string; instructions: string; } -export interface DiscordLinkCheckResult { +interface DiscordLinkCheckResult { linked: boolean; details?: Record | null; } -export interface TelegramLoginCheckResult { +interface TelegramLoginCheckResult { linked: boolean; details?: Record | null; } diff --git a/app/src/services/api/creditsApi.ts b/app/src/services/api/creditsApi.ts index f132ca310..41de5c862 100644 --- a/app/src/services/api/creditsApi.ts +++ b/app/src/services/api/creditsApi.ts @@ -79,7 +79,7 @@ export interface TeamUsage { insights: TeamUsageInsights; } -export interface TopUpResult { +interface TopUpResult { url: string; gatewayTransactionId: string; amountUsd: number; @@ -95,7 +95,7 @@ export interface CreditTransaction { createdAt: string; } -export interface PaginatedTransactions { +interface PaginatedTransactions { transactions: CreditTransaction[]; total: number; } @@ -117,7 +117,7 @@ export interface AutoRechargeSettings { lastError: string | null; } -export interface AutoRechargeUpdatePayload { +interface AutoRechargeUpdatePayload { enabled?: boolean; thresholdUsd?: number; rechargeAmountUsd?: number; @@ -148,26 +148,26 @@ export interface SavedCard { billingDetails: CardBillingDetails; } -export interface CardsData { +interface CardsData { customerId: string; defaultPaymentMethodId: string; cards: SavedCard[]; } -export interface SetupIntentData { +interface SetupIntentData { clientSecret: string; customerId: string; setupIntentId: string; } -export interface UpdateCardPayload { +interface UpdateCardPayload { isDefault?: boolean; billingDetails?: CardBillingDetails; } // ── Coupon types ──────────────────────────────────────────────────────────── -export interface CouponRedeemResult { +interface CouponRedeemResult { couponCode: string; amountUsd: number; pending: boolean; diff --git a/app/src/services/api/embeddingsApi.ts b/app/src/services/api/embeddingsApi.ts index 29b6045d6..f845c03ae 100644 --- a/app/src/services/api/embeddingsApi.ts +++ b/app/src/services/api/embeddingsApi.ts @@ -35,7 +35,7 @@ export interface EmbeddingsSettings { vector_search_enabled: boolean; } -export interface EmbeddingsUpdateResult { +interface EmbeddingsUpdateResult { provider?: string; model?: string; dimensions?: number; diff --git a/app/src/services/api/flowsApi.ts b/app/src/services/api/flowsApi.ts index 33574b64c..43062adbb 100644 --- a/app/src/services/api/flowsApi.ts +++ b/app/src/services/api/flowsApi.ts @@ -117,7 +117,7 @@ export interface FlowRun { * {@link getFlowRun} afterwards (thread_id === run id) if the caller needs the * up-to-date persisted status. */ -export interface FlowResumeResult { +interface FlowResumeResult { output: unknown; pending_approvals: string[]; thread_id: string; @@ -180,14 +180,14 @@ export interface FlowValidationErrorDetail { } /** Where a {@link FlowDraft} originated (`src/openhuman/flows/types.rs::DraftOrigin`). */ -export type DraftOrigin = 'chat' | 'canvas' | 'import'; +type DraftOrigin = 'chat' | 'canvas' | 'import'; /** * A core-managed, durable workflow draft (`src/openhuman/flows/types.rs::FlowDraft`) * — the shared working copy the agent tools and the canvas both read/write by * id across turns and reloads. Never live; promote runs the normal save gates. */ -export interface FlowDraft { +interface FlowDraft { id: string; /** The saved flow this draft edits, if any (promote → update vs create). */ flow_id?: string; @@ -204,7 +204,7 @@ export interface FlowDraft { * JSON; `n8n` is an n8n workflow export (mapped best-effort host-side); `auto` * (the default) detects the shape. */ -export type FlowImportFormat = 'native' | 'n8n' | 'auto'; +type FlowImportFormat = 'native' | 'n8n' | 'auto'; /** * Result of `openhuman.flows_import` (`src/openhuman/flows/types.rs::FlowImport`). @@ -213,7 +213,7 @@ export type FlowImportFormat = 'native' | 'n8n' | 'auto'; * notes (unmapped n8n node types, untranslated expressions, a synthesized or * demoted trigger). Import NEVER persists — the user Saves via the normal gate. */ -export interface FlowImport { +interface FlowImport { graph: unknown; warnings: string[]; } @@ -234,7 +234,7 @@ export interface FlowConnection { } /** Optional fields for {@link updateFlow}. Omitted fields are left untouched. */ -export interface FlowUpdate { +interface FlowUpdate { name?: string; graph?: unknown; requireApproval?: boolean; @@ -249,7 +249,7 @@ export interface FlowUpdate { } /** A revision snapshot (`src/openhuman/flows/types.rs::FlowRevision`). */ -export interface FlowRevision { +interface FlowRevision { id: string; flow_id: string; graph: unknown; @@ -263,7 +263,7 @@ export interface FlowRevision { * conflict (encoded in the RPC error message as JSON). Detect it by parsing a * caught update error — see {@link parseFlowVersionConflict}. */ -export interface FlowVersionConflict { +interface FlowVersionConflict { code: 'version_conflict'; message: string; current: Flow; @@ -273,6 +273,8 @@ export interface FlowVersionConflict { * If `err` is a `flows_update` version-conflict error, returns the structured * conflict (with the current server flow) so the UI can offer reload/diff; * otherwise `null`. + * + * @knipignore Documented flow history extension contract. */ export function parseFlowVersionConflict(err: unknown): FlowVersionConflict | null { const message = err instanceof Error ? err.message : typeof err === 'string' ? err : ''; @@ -591,7 +593,11 @@ export async function updateFlow(id: string, update: FlowUpdate): Promise return flow; } -/** List a flow's revision history via `openhuman.flows_get_history` (newest first). */ +/** + * List a flow's revision history via `openhuman.flows_get_history` (newest first). + * + * @knipignore Documented flow history extension contract. + */ export async function getFlowHistory(id: string, limit?: number): Promise { const response = await callCoreRpc({ method: 'openhuman.flows_get_history', @@ -605,6 +611,8 @@ export async function getFlowHistory(id: string, limit?: number): Promise" CTAs. Also surfaced on the workflow_proposal payload. + * + * @knipignore Documented tool catalog extension contract. */ export async function requiredConnections(graph: unknown): Promise { const response = await callCoreRpc({ @@ -730,7 +744,11 @@ export async function requiredConnections(graph: unknown): Promise { const response = await callCoreRpc({ method: 'openhuman.flows_get_tool_contract', @@ -743,7 +761,11 @@ export async function getToolContract(slug: string): Promise { // ── Core-managed drafts (F5) ───────────────────────────────────────────────── -/** Create a durable draft via `openhuman.flows_draft_create`. */ +/** + * Create a durable draft via `openhuman.flows_draft_create`. + * + * @knipignore Documented durable draft extension contract. + */ export async function createDraft(params: { name: string; graph: unknown; @@ -763,7 +785,11 @@ export async function createDraft(params: { return unwrapCliEnvelope(response); } -/** Fetch a draft by id via `openhuman.flows_draft_get`. */ +/** + * Fetch a draft by id via `openhuman.flows_draft_get`. + * + * @knipignore Documented durable draft extension contract. + */ export async function getDraft(id: string): Promise { const response = await callCoreRpc({ method: 'openhuman.flows_draft_get', @@ -772,7 +798,11 @@ export async function getDraft(id: string): Promise { return unwrapCliEnvelope(response); } -/** Patch a draft's name/graph/flow_id via `openhuman.flows_draft_update`. */ +/** + * Patch a draft's name/graph/flow_id via `openhuman.flows_draft_update`. + * + * @knipignore Documented durable draft extension contract. + */ export async function updateDraft( id: string, patch: { name?: string; graph?: unknown; flowId?: string } @@ -784,14 +814,22 @@ export async function updateDraft( return unwrapCliEnvelope(response); } -/** List all drafts (newest-updated first) via `openhuman.flows_draft_list`. */ +/** + * List all drafts (newest-updated first) via `openhuman.flows_draft_list`. + * + * @knipignore Documented durable draft extension contract. + */ export async function listDrafts(): Promise { const response = await callCoreRpc({ method: 'openhuman.flows_draft_list', params: {} }); const result = unwrapCliEnvelope<{ drafts: FlowDraft[] }>(response); return result.drafts ?? []; } -/** Delete a draft via `openhuman.flows_draft_delete`. */ +/** + * Delete a draft via `openhuman.flows_draft_delete`. + * + * @knipignore Documented durable draft extension contract. + */ export async function deleteDraft(id: string): Promise { const response = await callCoreRpc({ method: 'openhuman.flows_draft_delete', @@ -804,6 +842,8 @@ export async function deleteDraft(id: string): Promise { /** * Promote a draft into a saved flow via `openhuman.flows_draft_promote` (runs * the normal create/update gates, then removes the draft). Returns the Flow. + * + * @knipignore Documented durable draft extension contract. */ export async function promoteDraft(id: string, requireApproval?: boolean): Promise { log('promoteDraft: request id=%s', id); @@ -926,7 +966,7 @@ const FLOW_BUILD_TIMEOUT_MS = 610_000; * lockstep with `parseWorkflowProposal` in `ChatRuntimeProvider` (the streamed * path); returns null if the payload isn't a valid proposal. */ -export function mapWorkflowProposal(payload: unknown): WorkflowProposal | null { +function mapWorkflowProposal(payload: unknown): WorkflowProposal | null { if (!payload || typeof payload !== 'object') return null; const obj = payload as Record; if (obj.type !== 'workflow_proposal') return null; @@ -1040,26 +1080,3 @@ export async function markSuggestionBuilt(id: string): Promise { log('markSuggestionBuilt: response built=%s', result.built); return result.built; } - -export const flowsApi = { - createFlow, - importFlow, - discoverWorkflows, - listSuggestions, - dismissSuggestion, - markSuggestionBuilt, - resumeFlow, - listFlowRuns, - getFlowRun, - getFlow, - listFlows, - setFlowEnabled, - runFlow, - updateFlow, - deleteFlow, - duplicateFlow, - validateFlow, - listFlowConnections, -}; - -export default flowsApi; diff --git a/app/src/services/api/goalsApi.ts b/app/src/services/api/goalsApi.ts index ed099943f..87f46e167 100644 --- a/app/src/services/api/goalsApi.ts +++ b/app/src/services/api/goalsApi.ts @@ -24,7 +24,7 @@ export interface GoalItem { } /** Outcome of an enrichment (reflect) pass. */ -export interface ReflectResult { +interface ReflectResult { ran: boolean; summary: string; items: GoalItem[]; diff --git a/app/src/services/api/mcpClientsApi.ts b/app/src/services/api/mcpClientsApi.ts index 83f8ba385..5c5c3da65 100644 --- a/app/src/services/api/mcpClientsApi.ts +++ b/app/src/services/api/mcpClientsApi.ts @@ -94,7 +94,7 @@ interface UpdateEnvResult { } /** Non-secret registry-credentials snapshot. Secret *values* are never returned. */ -export interface RegistrySettings { +interface RegistrySettings { smithery_api_key_set: boolean; mcp_official_token_set: boolean; mcp_official_base?: string | null; diff --git a/app/src/services/api/mcpRegistryErrors.ts b/app/src/services/api/mcpRegistryErrors.ts index 0acc22826..7f7b4ccce 100644 --- a/app/src/services/api/mcpRegistryErrors.ts +++ b/app/src/services/api/mcpRegistryErrors.ts @@ -1,4 +1,4 @@ -export type McpRegistryErrorKind = 'not_found' | 'network' | 'unavailable'; +type McpRegistryErrorKind = 'not_found' | 'network' | 'unavailable'; const FALLBACK_MESSAGES: Record = { not_found: diff --git a/app/src/services/api/referralApi.ts b/app/src/services/api/referralApi.ts index 9ce61f369..6450a2e56 100644 --- a/app/src/services/api/referralApi.ts +++ b/app/src/services/api/referralApi.ts @@ -8,7 +8,7 @@ import { getOrCreateDeviceFingerprint } from '../../utils/deviceFingerprint'; import { callCoreCommand } from '../coreCommandClient'; /** Shape thrown by {@link referralApi.getStats} / {@link referralApi.claimReferral} on RPC failure. */ -export type ReferralRpcFailure = { success: false; error: string }; +type ReferralRpcFailure = { success: false; error: string }; function referralRpcErrorMessage(err: unknown): string { if (err && typeof err === 'object') { diff --git a/app/src/services/api/rewardsApi.ts b/app/src/services/api/rewardsApi.ts index 9c4a50485..2a9efafa1 100644 --- a/app/src/services/api/rewardsApi.ts +++ b/app/src/services/api/rewardsApi.ts @@ -7,7 +7,7 @@ import { apiClient } from '../apiClient'; const REWARDS_SNAPSHOT_TIMEOUT_MS = 15_000; const log = createDebug('rewards:api'); -export type RewardsApiError = ApiError & { code?: string; status?: number }; +type RewardsApiError = ApiError & { code?: string; status?: number }; function asRecord(value: unknown): Record | null { return value && typeof value === 'object' && !Array.isArray(value) diff --git a/app/src/services/api/skillRegistryApi.ts b/app/src/services/api/skillRegistryApi.ts index 346055574..ddfcec929 100644 --- a/app/src/services/api/skillRegistryApi.ts +++ b/app/src/services/api/skillRegistryApi.ts @@ -58,7 +58,7 @@ export interface CatalogEntry { license: string | null; } -export interface RegistryInstallResult { +interface RegistryInstallResult { url: string; stdout: string; stderr: string; @@ -72,7 +72,7 @@ interface RawRegistryInstallResult { new_skills: string[]; } -export interface RegistryUninstallResult { +interface RegistryUninstallResult { name: string; removedPath: string; scope: string; @@ -84,7 +84,7 @@ interface RawRegistryUninstallResult { scope: string; } -export interface ControllerSchemaSummary { +interface ControllerSchemaSummary { namespace: string; function: string; description: string; diff --git a/app/src/services/api/skillsApi.ts b/app/src/services/api/skillsApi.ts index d25863663..d3984b06f 100644 --- a/app/src/services/api/skillsApi.ts +++ b/app/src/services/api/skillsApi.ts @@ -70,7 +70,7 @@ type RawWorkflowSummary = Omit { - const result = await callCoreRpc<{ models: VoiceModelInfo[] }>({ - method: 'openhuman.voice_list_models', - params: { provider_id: providerId, capability }, - }); - return result.models ?? []; -} - // ---- Provider testing ---- const VOICE_TEST_TIMEOUT_MS = 30_000; diff --git a/app/src/services/api/workflowRunsApi.ts b/app/src/services/api/workflowRunsApi.ts index b73da626d..889c43f48 100644 --- a/app/src/services/api/workflowRunsApi.ts +++ b/app/src/services/api/workflowRunsApi.ts @@ -123,7 +123,7 @@ interface MaybeRunResult { } /** Filters accepted by `workflow_run_list`. */ -export interface ListRunsParams { +interface ListRunsParams { definitionId?: string; status?: WorkflowRunStatus; parentThreadId?: string; @@ -132,7 +132,7 @@ export interface ListRunsParams { } /** Parameters accepted by `workflow_run_start`. */ -export interface StartRunParams { +interface StartRunParams { definitionId: string; /** Run input, e.g. `{ question: "..." }` or `{ modelOverride: "..." }`. */ input?: Record; @@ -151,7 +151,7 @@ export interface StartRunParams { */ export type WorkflowCostReason = 'non_read_only_tier' | 'high_concurrency' | 'high_children'; -export interface WorkflowCostAssessment { +interface WorkflowCostAssessment { /** True when the definition must be explicitly approved before starting. */ requiresApproval: boolean; /** Machine-readable reasons (empty when no approval required). */ @@ -163,14 +163,14 @@ export interface WorkflowCostAssessment { * The builtin parallel-research workflow runs at 2, so routine read-only * fan-outs stay frictionless; only unusually wide phases trip the gate. */ -export const HIGH_CONCURRENCY_THRESHOLD = 4; +const HIGH_CONCURRENCY_THRESHOLD = 4; /** * Total child-agent budget at or above this is treated as high-cost — every * child is a full agent turn (tokens + wall-clock), so a large cap is the * clearest proxy for spend in the absence of an explicit cost field. */ -export const HIGH_CHILDREN_THRESHOLD = 8; +const HIGH_CHILDREN_THRESHOLD = 8; /** * Decide whether starting `def` needs explicit user approval. diff --git a/app/src/services/api/worktreeApi.ts b/app/src/services/api/worktreeApi.ts index 00711d14a..246913e1e 100644 --- a/app/src/services/api/worktreeApi.ts +++ b/app/src/services/api/worktreeApi.ts @@ -20,7 +20,7 @@ import { callCoreRpc } from '../coreRpcClient'; const log = debug('worktreeApi'); /** Snapshot of a single worktree. Mirrors the Rust `WorktreeStatus`. */ -export interface WorktreeStatus { +interface WorktreeStatus { /** Absolute path to the worktree checkout. */ path: string; /** Checked-out branch (or `(detached HEAD)`), when resolvable. */ @@ -32,7 +32,7 @@ export interface WorktreeStatus { } /** A file changed by more than one worker worktree (pre-merge conflict hint). */ -export interface WorktreeOverlap { +interface WorktreeOverlap { /** Relative path touched by multiple worktrees. */ file: string; /** Branches (or path fallbacks) of the worktrees that touched it. */ @@ -40,7 +40,7 @@ export interface WorktreeOverlap { } /** Response from `openhuman.worktree_list`. */ -export interface WorktreeListView { +interface WorktreeListView { worktrees: WorktreeStatus[]; overlaps: WorktreeOverlap[]; } diff --git a/app/src/services/artifactDownloadService.ts b/app/src/services/artifactDownloadService.ts index c7b47b7f4..5b705dc85 100644 --- a/app/src/services/artifactDownloadService.ts +++ b/app/src/services/artifactDownloadService.ts @@ -39,7 +39,7 @@ export type ArtifactErrorCode = | 'DELETE_FAILED'; /** Outcome surfaced to the UI for a single download attempt. */ -export interface DownloadArtifactOutcome { +interface DownloadArtifactOutcome { ok: boolean; /** Absolute destination path when `ok === true`. */ path?: string; @@ -56,7 +56,7 @@ export interface DownloadArtifactOutcome { } /** Outcome surfaced to the UI for a single delete attempt (#3024). */ -export interface DeleteArtifactOutcome { +interface DeleteArtifactOutcome { ok: boolean; /** * Stable failure code when `ok === false`. Pair with `error` (raw @@ -70,9 +70,9 @@ export interface DeleteArtifactOutcome { error?: string; } -export type ListedArtifactKind = 'presentation' | 'document' | 'image' | 'other'; +type ListedArtifactKind = 'presentation' | 'document' | 'image' | 'other'; -export interface ListedThreadArtifact { +interface ListedThreadArtifact { artifactId: string; kind: ListedArtifactKind; title: string; @@ -80,7 +80,7 @@ export interface ListedThreadArtifact { sizeBytes: number; } -export interface ListThreadArtifactsOutcome { +interface ListThreadArtifactsOutcome { ok: boolean; artifacts: ListedThreadArtifact[]; error?: string; diff --git a/app/src/services/backendHealth.ts b/app/src/services/backendHealth.ts index ca5fee2d1..d0cdabfba 100644 --- a/app/src/services/backendHealth.ts +++ b/app/src/services/backendHealth.ts @@ -3,13 +3,13 @@ import { getBackendUrl } from './backendUrl'; export const BACKEND_HEALTH_TIMEOUT_MS = 6_000; -export type BackendHealthFailureReason = +type BackendHealthFailureReason = | 'timeout' // AbortError after BACKEND_HEALTH_TIMEOUT_MS | 'network' // fetch rejected before any HTTP response (DNS, CORS, offline, TLS) | 'http-5xx' // upstream/edge returned a 5xx (e.g. Cloudflare 504 gateway timeout) | 'resolve-failure'; // could not resolve the backend URL at all -export type BackendHealthResult = +type BackendHealthResult = | { healthy: true; status: number; latencyMs: number; backendUrl: string } | { healthy: false; reason: BackendHealthFailureReason; status?: number; latencyMs: number }; diff --git a/app/src/services/chatService.ts b/app/src/services/chatService.ts index dc3571a94..95099c3b2 100644 --- a/app/src/services/chatService.ts +++ b/app/src/services/chatService.ts @@ -1211,9 +1211,9 @@ export function subscribeChatEvents(listeners: ChatEventListeners): () => void { }; } -export type QueueMode = 'interrupt' | 'steer' | 'followup' | 'collect' | 'parallel'; +type QueueMode = 'interrupt' | 'steer' | 'followup' | 'collect' | 'parallel'; -export interface ChatSendParams { +interface ChatSendParams { threadId: string; message: string; model?: string; diff --git a/app/src/services/clientVersionHeaders.ts b/app/src/services/clientVersionHeaders.ts index be0ead0cf..f8feb6de6 100644 --- a/app/src/services/clientVersionHeaders.ts +++ b/app/src/services/clientVersionHeaders.ts @@ -9,7 +9,7 @@ const CLIENT_VERSION_MAX_LENGTH = 64; let tauriVersionPromise: Promise | null = null; let coreVersionPromise: Promise | null = null; -export function sanitizeClientVersion(raw: string | null | undefined): string | null { +function sanitizeClientVersion(raw: string | null | undefined): string | null { const sanitized = String(raw ?? '') .trim() .replace(/[^0-9A-Za-z._+-]+/g, '') diff --git a/app/src/services/coreCommandClient.ts b/app/src/services/coreCommandClient.ts index 747bae28e..4cef4f7d6 100644 --- a/app/src/services/coreCommandClient.ts +++ b/app/src/services/coreCommandClient.ts @@ -1,6 +1,6 @@ import { callCoreRpc } from './coreRpcClient'; -export interface CoreCommandResponse { +interface CoreCommandResponse { result: T; logs: string[]; } diff --git a/app/src/services/coreRpcClient.ts b/app/src/services/coreRpcClient.ts index e79524f26..6ccc7a3eb 100644 --- a/app/src/services/coreRpcClient.ts +++ b/app/src/services/coreRpcClient.ts @@ -93,6 +93,8 @@ let _activeTransport: CoreTransport | null = null; /** * Override the active transport used by `callCoreRpc`. * Set to null to revert to the default local HTTP path. + * + * @knipignore Public transport-manager integration seam for remote profiles. */ export function setActiveCoreTransport(transport: CoreTransport | null): void { _activeTransport = transport; @@ -106,7 +108,7 @@ export function setActiveCoreTransport(transport: CoreTransport | null): void { * in again.`) can drive both a silent swallow in usage/credits chains AND * a global reauth signal without every caller re-implementing the regex. */ -export type CoreRpcErrorKind = +type CoreRpcErrorKind = | 'auth_expired' | 'provider_auth' // downstream provider 401 — NOT user session expiry | 'transport' @@ -285,6 +287,8 @@ const CORE_RPC_TOKEN_INVALIDATED_EVENT = 'invalidated'; * Subscribe to core RPC bearer invalidations. Returns an unsubscribe handle. * The listener fires AFTER the cache has been cleared, so a subsequent * `getCoreRpcToken()` will re-resolve. + * + * @knipignore Public token-invalidation subscription seam for long-lived RPC consumers. */ export function subscribeCoreRpcTokenInvalidated(listener: () => void): () => void { const wrapped = () => listener(); @@ -601,6 +605,8 @@ export async function getCoreHttpBaseUrl(): Promise { * logs this URL. The proper fix is a short-lived single-use handshake token * minted just before opening the stream; that requires a new core endpoint and * is tracked as the follow-up to this seam. Do not log the returned URL. + * + * @knipignore Documented webhook SSE authentication URL seam. */ export function buildWebhookEventsUrl(baseUrl: string, coreRpcToken: string | null): string | null { if (!coreRpcToken) return null; diff --git a/app/src/services/coreSocket.ts b/app/src/services/coreSocket.ts index 5b318ac3e..b16ba2dc6 100644 --- a/app/src/services/coreSocket.ts +++ b/app/src/services/coreSocket.ts @@ -14,7 +14,7 @@ import { io, type ManagerOptions, type Socket, type SocketOptions } from 'socket import { getCoreRpcToken } from './coreRpcClient'; -export interface CoreSocketOptions { +interface CoreSocketOptions { /** * Per-process core bearer (from `getCoreRpcToken()`). When `null` the * factory passes an empty string — the server will reject the @@ -50,7 +50,7 @@ export function createCoreSocket(baseUrl: string, opts: CoreSocketOptions): Sock return io(baseUrl, { ...DEFAULT_OPTIONS, ...(opts.overrides ?? {}), auth }); } -export interface ConnectCoreSocketOptions { +interface ConnectCoreSocketOptions { /** Resolves the Socket.IO base URL (no trailing `/rpc`). */ getBaseUrl: () => Promise; /** diff --git a/app/src/services/coreStateApi.ts b/app/src/services/coreStateApi.ts index 27ad1f3cf..98f512b14 100644 --- a/app/src/services/coreStateApi.ts +++ b/app/src/services/coreStateApi.ts @@ -5,7 +5,7 @@ import type { LocalAiStatus } from '../utils/tauriCommands/localAi'; import type { ServiceStatus } from '../utils/tauriCommands/service'; import { callCoreRpc } from './coreRpcClient'; -export interface OnboardingTasks { +interface OnboardingTasks { accessibilityPermissionGranted: boolean; localModelConsentGiven: boolean; localModelDownloadStarted: boolean; @@ -26,7 +26,7 @@ export interface KeyringStatus { backendName: string; } -export interface UpdateCoreLocalStateParams { +interface UpdateCoreLocalStateParams { encryptionKey?: string | null; onboardingTasks?: OnboardingTasks | null; keyringConsent?: KeyringConsentPreference | null; @@ -73,7 +73,7 @@ interface AppStateSnapshotResult { } /** Raw (snake_case) health payload embedded in the app-state snapshot. */ -export interface RawHealthSnapshot { +interface RawHealthSnapshot { pid: number; updated_at: string; uptime_seconds: number; diff --git a/app/src/services/meetCallService.ts b/app/src/services/meetCallService.ts index 953c925d0..9082074fc 100644 --- a/app/src/services/meetCallService.ts +++ b/app/src/services/meetCallService.ts @@ -36,7 +36,7 @@ function mapRiveColors(colors?: { return { primary_color: primary, secondary_color: secondary }; } -export type MeetJoinCallInput = { +type MeetJoinCallInput = { meetUrl: string; /** Bot's display name in Meet's "Your name" prompt. */ displayName: string; @@ -58,7 +58,7 @@ export type MeetJoinCallInput = { secondaryVoiceId?: string; }; -export type MeetJoinCallResult = { +type MeetJoinCallResult = { requestId: string; meetUrl: string; displayName: string; @@ -256,7 +256,7 @@ export async function getMeetCallDetail(requestId: string): Promise { * core Socket.IO bridge so backend bot events can be handled locally too. */ /** Alias of {@link MeetingPlatform} — kept for existing consumers. */ -export type MascotMeetPlatform = MeetingPlatform; +type MascotMeetPlatform = MeetingPlatform; -export interface MascotJoinMeetingInput { +interface MascotJoinMeetingInput { platform: MascotMeetPlatform; meetUrl: string; displayName?: string; } -export interface MascotJoinMeetingResult { +interface MascotJoinMeetingResult { success: boolean; data?: unknown; } diff --git a/app/src/services/pttService.ts b/app/src/services/pttService.ts index 2782e9e45..9b97cf2b1 100644 --- a/app/src/services/pttService.ts +++ b/app/src/services/pttService.ts @@ -49,7 +49,7 @@ export interface PttDeps { }; } -export interface PttService { +interface PttService { onStart(sessionId: number): Promise; onStop(sessionId: number): Promise; cancel(reason: 'preempted' | 'mic_failure' | 'user_cancel'): Promise; diff --git a/app/src/services/rpcMethods.ts b/app/src/services/rpcMethods.ts index c75732386..f66edc488 100644 --- a/app/src/services/rpcMethods.ts +++ b/app/src/services/rpcMethods.ts @@ -69,7 +69,7 @@ export const CORE_RPC_METHODS = { healthSystemInfo: 'openhuman.health_system_info', } as const; -export type CoreRpcMethod = (typeof CORE_RPC_METHODS)[keyof typeof CORE_RPC_METHODS]; +type CoreRpcMethod = (typeof CORE_RPC_METHODS)[keyof typeof CORE_RPC_METHODS]; export const LEGACY_METHOD_ALIASES: Record = { // #3565: old desktop clients used dotted namespace/function channel calls. diff --git a/app/src/services/walletApi.ts b/app/src/services/walletApi.ts index 651078f92..50ab4e095 100644 --- a/app/src/services/walletApi.ts +++ b/app/src/services/walletApi.ts @@ -40,7 +40,7 @@ export interface WalletStatus { updatedAtMs: number | null; } -export interface SetupWalletParams { +interface SetupWalletParams { consentGranted: boolean; source: WalletSetupSource; mnemonicWordCount: number; @@ -137,7 +137,7 @@ export interface ExecutionResult { transaction: PreparedTransaction; } -export interface PrepareTransferParams { +interface PrepareTransferParams { chain: WalletChain; toAddress: string; /** Amount in the asset's smallest unit (wei / sat / lamport / sun). */ @@ -175,7 +175,7 @@ export const executePrepared = async (quoteId: string): Promise return response.result; }; -export interface RevealRecoveryPhraseResult { +interface RevealRecoveryPhraseResult { phrase: string; wordCount: number; } diff --git a/app/src/services/webviewAccountService.ts b/app/src/services/webviewAccountService.ts index d16ad75db..0f7bf64f0 100644 --- a/app/src/services/webviewAccountService.ts +++ b/app/src/services/webviewAccountService.ts @@ -45,7 +45,7 @@ export { isTauri }; * which Sentry captures as `Non-Error promise rejection` with no stack trace. * Callers should branch on `kind` instead of re-parsing the message. */ -export type WebviewAccountErrorKind = 'unknown_provider' | 'invalid_url' | 'no_url' | 'unknown'; +type WebviewAccountErrorKind = 'unknown_provider' | 'invalid_url' | 'no_url' | 'unknown'; export class WebviewAccountError extends Error { readonly kind: WebviewAccountErrorKind; @@ -1381,15 +1381,6 @@ export async function hideWebviewAccount(accountId: string): Promise { } } -export async function showWebviewAccount(accountId: string): Promise { - if (!isTauri()) return; - try { - await invoke('webview_account_show', { args: { account_id: accountId } }); - } catch (err) { - errLog('show failed: %o', err); - } -} - export async function closeWebviewAccount(accountId: string): Promise { if (!isTauri()) return; log('close account=%s', accountId); @@ -1425,20 +1416,6 @@ export async function purgeWebviewAccount(accountId: string): Promise { // ────────────────────────── Notification bypass helpers ───────────────────── -/** - * Mute or unmute OS notification toasts for a specific embedded account. - * When muted, toasts from that account are suppressed regardless of focus state. - */ -export async function setAccountMuted(accountId: string, muted: boolean): Promise { - if (!isTauri()) return; - try { - await invoke('webview_notification_mute_account', { accountId, muted }); - log('notify-bypass: account=%s muted=%s', accountId, muted); - } catch (e) { - log('notify-bypass: setAccountMuted error %o', e); - } -} - /** * Enable or disable global Do Not Disturb mode for embedded webview notifications. * When enabled, all OS notification toasts from embedded accounts are suppressed. @@ -1477,7 +1454,7 @@ export async function getBypassPrefs(): Promise<{ * Rust uses this together with the window-focus state to suppress * notifications while the user is actively looking at that account. */ -export async function setFocusedAccount(accountId: string | null): Promise { +async function setFocusedAccount(accountId: string | null): Promise { if (!isTauri()) return; try { await invoke('webview_set_focused_account', { accountId }); diff --git a/app/src/store/backendMeetSlice.ts b/app/src/store/backendMeetSlice.ts index a2806eefe..13760eae1 100644 --- a/app/src/store/backendMeetSlice.ts +++ b/app/src/store/backendMeetSlice.ts @@ -23,7 +23,7 @@ export interface BackendMeetHarnessEvent { correlationId?: string; } -export interface BackendMeetTranscriptEvent { +interface BackendMeetTranscriptEvent { turns: BackendMeetTurn[]; duration_ms: number; correlationId?: string; @@ -35,14 +35,14 @@ export interface BackendMeetTranscriptEvent { * `index` supersedes an earlier one (used to finalize a partial line). * `is_partial` marks a not-yet-finalized line, rendered greyed in the UI. */ -export interface BackendMeetTranscriptDeltaEvent { +interface BackendMeetTranscriptDeltaEvent { turn: BackendMeetTurn; index: number; is_partial: boolean; correlationId?: string; } -export interface BackendMeetState { +interface BackendMeetState { status: BackendMeetStatus; meetUrl: string | null; meetingId: string | null; @@ -186,9 +186,6 @@ export const selectBackendMeetLastReply = (state: { backendMeet: BackendMeetStat state.backendMeet.lastReply; export const selectBackendMeetLastHarness = (state: { backendMeet: BackendMeetState }) => state.backendMeet.lastHarness; -export const selectBackendMeetMeetingId = (state: { - backendMeet: BackendMeetState; -}): string | null => state.backendMeet.meetingId; export const selectBackendMeetListenOnly = (state: { backendMeet: BackendMeetState }): boolean => state.backendMeet.listenOnly; export const selectBackendMeetError = (state: { backendMeet: BackendMeetState }): string | null => diff --git a/app/src/store/chatRuntimeSlice.ts b/app/src/store/chatRuntimeSlice.ts index 128260752..96d849dd2 100644 --- a/app/src/store/chatRuntimeSlice.ts +++ b/app/src/store/chatRuntimeSlice.ts @@ -39,7 +39,7 @@ export type ToolTimelineEntryStatus = | 'awaiting_user' | 'cancelled'; -export interface InferenceStatus { +interface InferenceStatus { phase: 'thinking' | 'tool_use' | 'subagent'; iteration: number; maxIterations: number; @@ -364,7 +364,7 @@ export interface SubAgentUsage { } /** Running per-session totals accumulated from `chat:done` events (#703). */ -export interface SessionTokenUsage { +interface SessionTokenUsage { inputTokens: number; outputTokens: number; turns: number; @@ -409,7 +409,7 @@ export function emptySessionTokenUsage(): SessionTokenUsage { } /** Payload accepted by `recordChatTurnUsage` (and applied per turn). */ -export interface ChatTurnUsagePayload { +interface ChatTurnUsagePayload { inputTokens: number; outputTokens: number; cachedTokens?: number; @@ -595,14 +595,6 @@ export interface ArtifactSnapshot { updatedAt: number; } -/** - * Queue behavior when a turn is already in flight for a thread. - * `parallel` runs an independent concurrent (forked) turn on the same thread - * instead of interrupting/queueing — its stream is tracked separately (see - * `parallelStreamsByThread`) so it renders as its own interleaved branch. - */ -export type QueueMode = 'interrupt' | 'steer' | 'followup' | 'collect' | 'parallel'; - /** * Per-thread UI state for an in-flight agent turn (socket events while the user * may navigate away from Conversations). The thread slice keeps `activeThreadId` @@ -1194,16 +1186,6 @@ const chatRuntimeSlice = createSlice({ } list.push({ kind, round, seq: list.length, text: delta }); }, - /** Record a tool call in the live processing transcript at its position. */ - recordProcessingTool: ( - state, - action: PayloadAction<{ threadId: string; round: number; callId: string }> - ) => { - const { threadId, round, callId } = action.payload; - const list = (state.processingByThread[threadId] ??= []); - if (list.some(i => i.kind === 'toolCall' && i.callId === callId)) return; - list.push({ kind: 'toolCall', round, seq: list.length, callId }); - }, /** * Reducer-side merge for a `tool_call` socket event (Phase 3 — replaces the * provider's `getState()` + find-row + full-array-rebuild). Upserts the row @@ -2302,7 +2284,6 @@ export const { toolResultReceived, clearProcessingForThread, appendProcessingProse, - recordProcessingTool, markSubagentCancelled, appendSubagentStreamDelta, recordSubagentTranscriptTool, @@ -2439,8 +2420,7 @@ export const fetchAndHydrateTurnHistory = createAsyncThunk( /** * Initial derived-transcript page size. Sized generously (the core clamps to * 500) so a reopened thread's visible turns all carry their process trail - * without a second round-trip. Older turns beyond this window load lazily via - * {@link loadOlderDerivedTranscript}. + * without a second round-trip. */ const DERIVED_TRANSCRIPT_INITIAL_LIMIT = 500; @@ -2546,61 +2526,8 @@ export const fetchAndHydrateDerivedTranscript = createAsyncThunk( page.hasMore ); dispatch(setTurnTimelinesForThread({ threadId, timelines, transcripts })); - // TODO(pagination): when `page.hasMore`, an insights "load older" affordance - // should call `loadOlderDerivedTranscript` with `page.nextCursor`. No UI - // surfaces older past-turn trails yet, so the first (generous) page is all - // we hydrate today. return { timelines, transcripts, nextCursor: page.nextCursor ?? null, hasMore: page.hasMore }; } ); -/** - * Load-older hook (Phase C, pagination): fetch the next (older) derived page - * for a thread by `cursor` and MERGE its trails into the already-hydrated - * {@link ChatRuntimeState.turnTimelinesByThread} / `turnTranscriptsByThread` - * (existing turns win — the newer page is authoritative). Wired but currently - * uncalled: no UI exposes a "load older insights" affordance yet. - */ -export const loadOlderDerivedTranscript = createAsyncThunk( - 'chatRuntime/loadOlderDerivedTranscript', - async (arg: { threadId: string; cursor: string }, { dispatch, getState }) => { - if (!DERIVED_TRANSCRIPT_ENABLED) return null; - const { threadId, cursor } = arg; - let page: DerivedTranscriptPage; - try { - page = await threadApi.getDerivedTranscript(threadId, { - cursor, - limit: DERIVED_TRANSCRIPT_INITIAL_LIMIT, - }); - } catch (error) { - derivedLog('load-older rpc failed thread=%s err=%O', threadId, error); - return null; - } - if (!page.hasTranscript) return null; - const skipRequestIds = liveRequestIdsToSkip(getState(), threadId, page.items); - const { timelines, transcripts } = mapDisplayItems(page.items, { skipRequestIds }); - const runtime = readChatRuntimeState(getState()); - const mergedTimelines = { ...timelines, ...(runtime?.turnTimelinesByThread[threadId] ?? {}) }; - const mergedTranscripts = { - ...transcripts, - ...(runtime?.turnTranscriptsByThread[threadId] ?? {}), - }; - derivedLog( - 'load-older merged thread=%s added_timelines=%d added_transcripts=%d hasMore=%s', - threadId, - Object.keys(timelines).length, - Object.keys(transcripts).length, - page.hasMore - ); - dispatch( - setTurnTimelinesForThread({ - threadId, - timelines: mergedTimelines, - transcripts: mergedTranscripts, - }) - ); - return { nextCursor: page.nextCursor ?? null, hasMore: page.hasMore }; - } -); - export default chatRuntimeSlice.reducer; diff --git a/app/src/store/connectivitySelectors.ts b/app/src/store/connectivitySelectors.ts index f049929fe..03778dfae 100644 --- a/app/src/store/connectivitySelectors.ts +++ b/app/src/store/connectivitySelectors.ts @@ -10,12 +10,7 @@ import { RootState } from './index'; * app stays usable, we just show a soft banner. * - `ok` : everything healthy. */ -export type BlockingState = 'internet-offline' | 'core-unreachable' | 'backend-only' | 'ok'; - -export const selectInternet = (s: RootState) => s.connectivity.internet; -export const selectCore = (s: RootState) => s.connectivity.core; -export const selectBackend = (s: RootState) => s.connectivity.backend; -export const selectConnectivityErrors = (s: RootState) => s.connectivity.lastError; +type BlockingState = 'internet-offline' | 'core-unreachable' | 'backend-only' | 'ok'; export const selectBlockingState = (s: RootState): BlockingState => { if (s.connectivity.internet === 'offline') return 'internet-offline'; diff --git a/app/src/store/deepLinkAuthState.ts b/app/src/store/deepLinkAuthState.ts index 32f3c5695..698399c04 100644 --- a/app/src/store/deepLinkAuthState.ts +++ b/app/src/store/deepLinkAuthState.ts @@ -1,6 +1,6 @@ import { useSyncExternalStore } from 'react'; -export interface DeepLinkAuthState { +interface DeepLinkAuthState { isProcessing: boolean; errorMessage: string | null; // Set when sign-in fails because the local core could not decrypt persisted diff --git a/app/src/store/mascotSlice.ts b/app/src/store/mascotSlice.ts index 7a001cfb0..8db44b9a1 100644 --- a/app/src/store/mascotSlice.ts +++ b/app/src/store/mascotSlice.ts @@ -28,7 +28,7 @@ export type MascotVoiceGender = 'male' | 'female'; * voice) so new users see consistent state in the Mascot settings * panel without any extra writes. */ -export const DEFAULT_MASCOT_VOICE_GENDER: MascotVoiceGender = 'male'; +const DEFAULT_MASCOT_VOICE_GENDER: MascotVoiceGender = 'male'; /** * Maximum length of a stored mascot voice id. ElevenLabs voice ids are @@ -94,7 +94,7 @@ function isMascotVoiceGender(value: unknown): value is MascotVoiceGender { return value === 'male' || value === 'female'; } -export interface MascotState { +interface MascotState { color: MascotColor; /** * User-selected ElevenLabs voice id for the mascot's reply speech, or @@ -490,7 +490,7 @@ export const selectEffectiveMascotVoiceId = (state: { return MASCOT_VOICE_ID || ELEVENLABS_VOICE_PRESETS[0].id; }; -export interface MeetingMascotSlot { +interface MeetingMascotSlot { /** Manifest mascot id, or `null` for the primary when the user is on * the default (first-`ready`) mascot. */ mascotId: string | null; @@ -499,7 +499,7 @@ export interface MeetingMascotSlot { voiceId: string; } -export interface MeetingMascotVoicePair { +interface MeetingMascotVoicePair { primary: MeetingMascotSlot; secondary: MeetingMascotSlot | null; } @@ -536,5 +536,4 @@ export const selectMeetingMascotVoicePair = (state: { return { primary, secondary }; }; -export { mascotSlice }; export default mascotSlice.reducer; diff --git a/app/src/store/notificationSlice.ts b/app/src/store/notificationSlice.ts index 8f51918be..e2d45b83b 100644 --- a/app/src/store/notificationSlice.ts +++ b/app/src/store/notificationSlice.ts @@ -218,5 +218,4 @@ export const { addIntegrationNotification, } = notificationSlice.actions; -export { notificationSlice }; export default notificationSlice.reducer; diff --git a/app/src/store/personaSlice.ts b/app/src/store/personaSlice.ts index f3aaf2e65..069a4dc23 100644 --- a/app/src/store/personaSlice.ts +++ b/app/src/store/personaSlice.ts @@ -20,7 +20,7 @@ import { resetUserScopedState } from './resetActions'; export const MAX_PERSONA_DISPLAY_NAME_LEN = 80; export const MAX_PERSONA_DESCRIPTION_LEN = 500; -export interface PersonaState { +interface PersonaState { /** User-facing name for the assistant persona, or `''` when unset. */ displayName: string; /** Short free-text description of the persona, or `''` when unset. */ @@ -88,5 +88,4 @@ export const selectPersonaDisplayName = (state: { persona: PersonaState }): stri export const selectPersonaDescription = (state: { persona: PersonaState }): string => state.persona.description; -export { personaSlice }; export default personaSlice.reducer; diff --git a/app/src/store/pttSlice.ts b/app/src/store/pttSlice.ts index c7038bf87..ac5a89b2e 100644 --- a/app/src/store/pttSlice.ts +++ b/app/src/store/pttSlice.ts @@ -75,8 +75,6 @@ export const selectSpeakReplies = (state: { ptt: PttState }): boolean => state.p export const selectShowOverlay = (state: { ptt: PttState }): boolean => state.ptt.showOverlay; -export const selectIsHeld = (state: { ptt: PttState }): boolean => state.ptt.isHeld; - export const selectPttRegistrationError = (state: { ptt: PttState }): string | null => state.ptt.registrationError; diff --git a/app/src/store/socketSlice.ts b/app/src/store/socketSlice.ts index 716ac5d78..595ee1897 100644 --- a/app/src/store/socketSlice.ts +++ b/app/src/store/socketSlice.ts @@ -2,9 +2,9 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { resetUserScopedState } from './resetActions'; -export type SocketConnectionStatus = 'connected' | 'disconnected' | 'connecting'; +type SocketConnectionStatus = 'connected' | 'disconnected' | 'connecting'; -export interface SocketUserState { +interface SocketUserState { status: SocketConnectionStatus; socketId: string | null; } diff --git a/app/src/store/themeSlice.ts b/app/src/store/themeSlice.ts index ede1a7f52..0ae11be80 100644 --- a/app/src/store/themeSlice.ts +++ b/app/src/store/themeSlice.ts @@ -1,11 +1,10 @@ -import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit'; +import { createSlice, type PayloadAction } from '@reduxjs/toolkit'; import { REHYDRATE } from 'redux-persist'; import { familyForThemeId, findFamily, findPreset, - PRESET_THEMES, resolveFamilyVariant, THEME_FAMILIES, } from '../lib/theme/presets'; @@ -17,9 +16,9 @@ export type ThemeMode = 'light' | 'dark' | 'system'; export type ThemeVariant = 'light' | 'dark' | 'system'; /** Sentinel active-theme id meaning "follow OS light/dark preference". */ -export const SYSTEM_THEME_ID = 'system'; +const SYSTEM_THEME_ID = 'system'; /** Default theme family selected on first run. */ -export const DEFAULT_FAMILY_ID = 'classic'; +const DEFAULT_FAMILY_ID = 'classic'; export type TabBarLabels = 'hover' | 'always'; export type AgentMessageViewMode = 'bubbles' | 'text'; /** @@ -51,7 +50,7 @@ export const FONT_SIZE_PX: Record = { export const MIN_FONT_SIZE_PX = 12; export const MAX_FONT_SIZE_PX = 28; /** Root size used when no preset/custom value resolves (matches `medium`). */ -export const DEFAULT_FONT_SIZE_PX = 16; +const DEFAULT_FONT_SIZE_PX = 16; /** Clamp an arbitrary px value into the supported range, rounded to whole px. */ export function clampFontSizePx(px: number): number { @@ -265,15 +264,6 @@ export default themeSlice.reducer; /** Built-in theme families (static). */ export const selectThemeFamilies = (): ThemeFamily[] => THEME_FAMILIES; -/** - * All selectable concrete themes: built-in variants followed by user themes. - * Memoized so the reference is stable while `customThemes` is unchanged. - */ -export const selectAllThemes = createSelector( - (state: { theme: ThemeState }) => state.theme.customThemes, - (customThemes): Theme[] => [...PRESET_THEMES, ...(customThemes ?? [])] -); - export const selectActiveThemeId = (state: { theme?: ThemeState }): string => state.theme?.activeThemeId ?? DEFAULT_FAMILY_ID; diff --git a/app/src/store/threadSlice.ts b/app/src/store/threadSlice.ts index 1362732fd..348f07a33 100644 --- a/app/src/store/threadSlice.ts +++ b/app/src/store/threadSlice.ts @@ -311,21 +311,6 @@ export const persistReaction = createAsyncThunk( } ); -export const updateThreadLabels = createAsyncThunk( - 'thread/updateThreadLabels', - async (payload: { threadId: string; labels: string[] }, { dispatch, rejectWithValue }) => { - try { - const thread = await threadApi.updateLabels(payload.threadId, payload.labels); - await dispatch(loadThreads()).unwrap(); - return thread; - } catch (error) { - return rejectWithValue( - error instanceof Error ? error.message : 'Failed to update thread labels' - ); - } - } -); - export const updateThreadTitle = createAsyncThunk( 'thread/updateThreadTitle', async (payload: { threadId: string; title: string }, { rejectWithValue }) => { @@ -339,19 +324,6 @@ export const updateThreadTitle = createAsyncThunk( } ); -export const purgeThreads = createAsyncThunk( - 'thread/purgeThreads', - async (_, { dispatch, rejectWithValue }) => { - try { - const result = await threadApi.purge(); - dispatch(clearAllThreads()); - return result; - } catch (error) { - return rejectWithValue(error instanceof Error ? error.message : 'Failed to purge threads'); - } - } -); - // ── Slice ───────────────────────────────────────────────────────── const threadSlice = createSlice({ diff --git a/app/src/store/userErrorsSelectors.ts b/app/src/store/userErrorsSelectors.ts index 67be05ac5..c2e6a47bf 100644 --- a/app/src/store/userErrorsSelectors.ts +++ b/app/src/store/userErrorsSelectors.ts @@ -14,7 +14,7 @@ import type { UserErrorsState } from './userErrorsSlice'; const selectSlice = (state: RootState): UserErrorsState => state.userErrors; /** All entries in insertion order (including resolved). */ -export const selectAllUserErrors = createSelector(selectSlice, slice => +const selectAllUserErrors = createSelector(selectSlice, slice => slice.order.map(id => slice.byId[id]).filter((e): e is UserActionableError => Boolean(e)) ); diff --git a/app/src/test/commandTestUtils.ts b/app/src/test/commandTestUtils.ts index 79400d6a5..747e23eb3 100644 --- a/app/src/test/commandTestUtils.ts +++ b/app/src/test/commandTestUtils.ts @@ -1,6 +1,6 @@ import { expect } from 'vitest'; -export interface PressKeyOptions { +interface PressKeyOptions { key: string; mod?: boolean; shift?: boolean; diff --git a/app/src/types/api.ts b/app/src/types/api.ts index fd55824aa..71c607368 100644 --- a/app/src/types/api.ts +++ b/app/src/types/api.ts @@ -102,6 +102,3 @@ export interface CoinbaseChargeData { status: string; expiresAt: string; } - -// API Endpoints -export type GetMeResponse = ApiResponse; diff --git a/app/src/types/channels.ts b/app/src/types/channels.ts index 8d87e66b0..30108164f 100644 --- a/app/src/types/channels.ts +++ b/app/src/types/channels.ts @@ -12,7 +12,7 @@ export type ChannelType = * from the core (which is typed `string`). `satisfies` keeps this list in * lockstep with the `ChannelType` union — adding a member there without updating * here is a compile error. */ -export const KNOWN_CHANNEL_TYPES = [ +const KNOWN_CHANNEL_TYPES = [ 'telegram', 'discord', 'web', @@ -58,11 +58,6 @@ export interface ChannelConnectionsState { connections: Record; } -export interface OutboundRoute { - channel: ChannelType; - authMode: ChannelAuthMode; -} - // --- Backend-driven definitions (from openhuman.channels_list) --- export interface FieldRequirement { diff --git a/app/src/types/intelligence.ts b/app/src/types/intelligence.ts index 5d3f108ad..485a49a41 100644 --- a/app/src/types/intelligence.ts +++ b/app/src/types/intelligence.ts @@ -47,30 +47,12 @@ export interface ActionableItem { currentSessionId?: string; } -export interface ActionableItemAction { - type: 'complete' | 'dismiss' | 'snooze'; - timestamp: Date; - itemId: string; - metadata?: Record; -} - export interface TimeGroup { label: string; items: ActionableItem[]; count: number; } -export interface IntelligencePageState { - items: ActionableItem[]; - loading: boolean; - error: string | null; - lastUpdate: Date | null; - - // UI state - showCompleted: boolean; - filter: ActionableItemSource | 'all'; -} - // Snooze time options export type SnoozeOption = { label: string; @@ -101,11 +83,3 @@ export interface ConfirmationModal { showDontShowAgain?: boolean; preferenceKey?: string; } - -// Chat message type -export interface ChatMessage { - id: string; - content: string; - sender: 'user' | 'ai'; - timestamp: Date; -} diff --git a/app/src/types/notifications.ts b/app/src/types/notifications.ts index fbd21247d..27a1880d4 100644 --- a/app/src/types/notifications.ts +++ b/app/src/types/notifications.ts @@ -25,14 +25,6 @@ export interface IntegrationNotification { deep_link?: string; } -export interface NotificationSettings { - provider: string; - enabled: boolean; - /** Minimum importance score 0.0–1.0 to show; 0.0 = show all */ - importance_threshold: number; - route_to_orchestrator: boolean; -} - export interface NotificationStats { total: number; unread: number; diff --git a/app/src/types/oauth.ts b/app/src/types/oauth.ts index 6f62d263f..46d14d21c 100644 --- a/app/src/types/oauth.ts +++ b/app/src/types/oauth.ts @@ -13,14 +13,3 @@ export interface OAuthProviderConfig { textColor: string; showOnWelcome?: boolean; } - -export interface OAuthLoginResponse { - success: boolean; - data: { jwtToken: string }; -} - -export interface OAuthError { - provider: OAuthProvider; - message: string; - code?: string; -} diff --git a/app/src/types/thread.ts b/app/src/types/thread.ts index 8b5f6c79a..6b079111d 100644 --- a/app/src/types/thread.ts +++ b/app/src/types/thread.ts @@ -30,28 +30,10 @@ export interface ThreadMessagesData { count: number; } -export interface ThreadCreateData { - id: string; -} - export interface ThreadDeleteData { deleted: boolean; } -/** Response from POST /chat/sendMessage — send user message and get agent reply */ -export interface SendMessageResponseData { - // Optional: backend can return empty {} or e.g. { messageId: string } - [key: string]: unknown; -} - -export interface PurgeRequestBody { - messages: boolean; - agentThreads: boolean; - deleteEverything: boolean; - deleteFrom?: string; - deleteTo?: string; -} - export interface PurgeResultData { messagesDeleted: number; agentThreadsDeleted: number; diff --git a/app/src/utils/accountsFullscreen.ts b/app/src/utils/accountsFullscreen.ts index ab48c1bde..3443a24c0 100644 --- a/app/src/utils/accountsFullscreen.ts +++ b/app/src/utils/accountsFullscreen.ts @@ -1,18 +1,2 @@ /** Sentinel id for the always-present agent entry in the Accounts page. */ export const AGENT_ACCOUNT_ID = '__agent__'; - -/** - * True when the route + selection means the app should render the - * embedded webview edge-to-edge (no bottom tab bar, no reserved padding). - * The Agent entry keeps the regular chrome visible so the user still has - * access to the tab bar while chatting. - */ -export function isAccountsFullscreen( - pathname: string, - activeAccountId: string | null | undefined -): boolean { - if (!pathname.startsWith('/chat')) return false; - // Agent selected (or nothing selected → defaults to Agent) keeps chrome. - if (!activeAccountId || activeAccountId === AGENT_ACCOUNT_ID) return false; - return true; -} diff --git a/app/src/utils/agentMessageBubbles.ts b/app/src/utils/agentMessageBubbles.ts index 8ed42cb79..a9a0ab5b4 100644 --- a/app/src/utils/agentMessageBubbles.ts +++ b/app/src/utils/agentMessageBubbles.ts @@ -207,7 +207,7 @@ function isHeadingLine(line: string): boolean { return /^\*\*[^*]+\*\*:?$/.test(trimmed); } -export interface ParsedMarkdownTable { +interface ParsedMarkdownTable { headers: string[]; rows: string[][]; } diff --git a/app/src/utils/bootstrapActiveUser.ts b/app/src/utils/bootstrapActiveUser.ts index ed5781834..71048e94f 100644 --- a/app/src/utils/bootstrapActiveUser.ts +++ b/app/src/utils/bootstrapActiveUser.ts @@ -20,7 +20,7 @@ * `userScopedStorage.ts::primeActiveUserId` and the "cloud-mode reload * survival" test. */ -export interface BootstrapContext { +interface BootstrapContext { isStandaloneNativeWindow: boolean; coreMode: 'local' | 'cloud' | null; getActiveUserIdFromCore: () => Promise; diff --git a/app/src/utils/clearAllAppData.ts b/app/src/utils/clearAllAppData.ts index 527a0f19e..31f918bc6 100644 --- a/app/src/utils/clearAllAppData.ts +++ b/app/src/utils/clearAllAppData.ts @@ -52,7 +52,7 @@ function clearUserScopedStorage(userId: string | null): void { } } -export interface ClearAllAppDataOptions { +interface ClearAllAppDataOptions { // Optional core-side session clear (e.g. `auth_clear_session`). Best-effort — // skipped silently if the caller cannot/does not provide it (e.g. pre-login // recovery from a corrupt key file, where there is no live session). diff --git a/app/src/utils/cryptoKeys.ts b/app/src/utils/cryptoKeys.ts index 62b8405a6..991ecfee7 100644 --- a/app/src/utils/cryptoKeys.ts +++ b/app/src/utils/cryptoKeys.ts @@ -13,10 +13,10 @@ import { wordlist } from '@scure/bip39/wordlists/english.js'; /** Word count for newly generated recovery phrases (128-bit entropy, BIP39). */ export const MNEMONIC_GENERATE_WORD_COUNT = 12; -export type WalletChain = 'evm' | 'btc' | 'solana' | 'tron'; +type WalletChain = 'evm' | 'btc' | 'solana' | 'tron'; export type WalletSetupSource = 'generated' | 'imported'; -export interface WalletAccountIdentity { +interface WalletAccountIdentity { chain: WalletChain; address: string; derivationPath: string; diff --git a/app/src/utils/localAiBootstrap.ts b/app/src/utils/localAiBootstrap.ts index ba6d4f9b2..9e106cff8 100644 --- a/app/src/utils/localAiBootstrap.ts +++ b/app/src/utils/localAiBootstrap.ts @@ -42,7 +42,7 @@ const retryLocalAiCommand = async ( throw lastError instanceof Error ? lastError : new Error(`Failed to ${label}`); }; -export interface LocalAiPresetResolution { +interface LocalAiPresetResolution { presets: PresetsResponse; recommendedTier: string; selectedTier: string | null; diff --git a/app/src/utils/oauthAppVersionGate.ts b/app/src/utils/oauthAppVersionGate.ts index 2f41bfaf4..16e1ab5e2 100644 --- a/app/src/utils/oauthAppVersionGate.ts +++ b/app/src/utils/oauthAppVersionGate.ts @@ -1,8 +1,6 @@ import { getVersion } from '@tauri-apps/api/app'; import { - type OAuthAuthReadinessFailure, - type OAuthAuthReadinessResult, oauthAuthReadinessUserMessage, prepareOAuthLoginLaunch, waitForOAuthAuthReadiness, @@ -11,15 +9,9 @@ import { LATEST_APP_DOWNLOAD_URL, MINIMUM_SUPPORTED_APP_VERSION } from './config import { isVersionAtLeast, parseSemverParts } from './semver'; import { isTauri } from './tauriCommands/common'; -export { - oauthAuthReadinessUserMessage, - prepareOAuthLoginLaunch, - waitForOAuthAuthReadiness, - type OAuthAuthReadinessFailure, - type OAuthAuthReadinessResult, -}; +export { oauthAuthReadinessUserMessage, prepareOAuthLoginLaunch, waitForOAuthAuthReadiness }; -export type OAuthAppVersionGateResult = +type OAuthAppVersionGateResult = | { ok: true } | { ok: false; current: string; minimum: string; downloadUrl: string }; diff --git a/app/src/utils/ollamaUrlValidation.ts b/app/src/utils/ollamaUrlValidation.ts index 18a90466f..eb2d263a0 100644 --- a/app/src/utils/ollamaUrlValidation.ts +++ b/app/src/utils/ollamaUrlValidation.ts @@ -1,4 +1,4 @@ -export interface OllamaUrlValidationResult { +interface OllamaUrlValidationResult { valid: boolean; normalized?: string; error?: string; diff --git a/app/src/utils/openrouterOAuth.ts b/app/src/utils/openrouterOAuth.ts index 546a633c9..0506d4297 100644 --- a/app/src/utils/openrouterOAuth.ts +++ b/app/src/utils/openrouterOAuth.ts @@ -16,7 +16,7 @@ interface OpenRouterExchangeResponse { error?: { message?: string } | string; } -export interface OpenRouterOAuthDeps { +interface OpenRouterOAuthDeps { startLoopbackListener?: (options?: StartLoopbackOptions) => Promise; openExternalUrl?: (url: string) => Promise; fetchImpl?: typeof fetch; diff --git a/app/src/utils/toolDefinitions.ts b/app/src/utils/toolDefinitions.ts index 5aec2e72b..17bdca2b4 100644 --- a/app/src/utils/toolDefinitions.ts +++ b/app/src/utils/toolDefinitions.ts @@ -1,4 +1,4 @@ -export interface ToolDefinition { +interface ToolDefinition { id: string; displayName: string; description: string; @@ -7,7 +7,7 @@ export interface ToolDefinition { rustToolNames: string[]; } -export type ToolCategory = 'System' | 'Files' | 'Vision' | 'Web' | 'Memory' | 'Automation'; +type ToolCategory = 'System' | 'Files' | 'Vision' | 'Web' | 'Memory' | 'Automation'; export const TOOL_CATEGORIES: ToolCategory[] = [ 'System', diff --git a/app/src/utils/toolTimelineFormatting.ts b/app/src/utils/toolTimelineFormatting.ts index 3998b5549..d0fcb3698 100644 --- a/app/src/utils/toolTimelineFormatting.ts +++ b/app/src/utils/toolTimelineFormatting.ts @@ -333,7 +333,7 @@ export function formatTimelineEntry(entry: ToolTimelineEntry): { title: string; * tool rows under a Hermes-style summary. {@link buildProcessingBlocks} * derives an ordered list of these from the interleaved transcript. */ -export type ProcessingBlock = +type ProcessingBlock = | { kind: 'narration'; key: string; text: string } | { kind: 'thinking'; key: string; text: string } | { kind: 'toolGroup'; key: string; summary: string; entries: ToolTimelineEntry[] }; @@ -582,7 +582,7 @@ function formatToolDetail( const KNOWN_TOOLKIT_RE = /^(gmail|notion|github|slack|discord|linear|jira|google_calendar|google_drive|calendar)$/i; -export function inferIntegrationName(input?: string): string | undefined { +function inferIntegrationName(input?: string): string | undefined { if (!input) return undefined; const delegateMatch = input.match(/^delegate_(.+)$/); diff --git a/app/src/utils/workspaceLinks.ts b/app/src/utils/workspaceLinks.ts index 85a801e3c..e6c277a04 100644 --- a/app/src/utils/workspaceLinks.ts +++ b/app/src/utils/workspaceLinks.ts @@ -1,4 +1,4 @@ -export interface WorkspaceLinkTarget { +interface WorkspaceLinkTarget { path: string; } diff --git a/app/test/e2e/helpers/app-helpers.ts b/app/test/e2e/helpers/app-helpers.ts index 0259125d2..55ccef3f7 100644 --- a/app/test/e2e/helpers/app-helpers.ts +++ b/app/test/e2e/helpers/app-helpers.ts @@ -68,7 +68,7 @@ const BOOT_CHECK_GATE_PICKER_HEADING_REGEX = /Select a Runtime|Connect to Your R * "Local" is pre-selected on desktop builds, so a single Continue click is * enough — no need to fill cloud URL/token. */ -export async function dismissBootCheckGate(timeout: number = 5_000): Promise { +async function dismissBootCheckGate(timeout: number = 5_000): Promise { if (!isTauriDriver()) return; const deadline = Date.now() + timeout; while (Date.now() < deadline) { @@ -207,27 +207,3 @@ export async function waitForAuthBootstrap(timeout: number = 20_000): Promise { - try { - if (isTauriDriver()) { - // Treat predicate as a CSS selector on Linux - const el = await browser.$(predicate); - return await el.isExisting(); - } - - const el = await browser.$(`-ios predicate string:${predicate}`); - return await el.isExisting(); - } catch { - return false; - } -} diff --git a/app/test/e2e/helpers/artifacts.ts b/app/test/e2e/helpers/artifacts.ts index 7dbb252e9..4c5dec0d0 100644 --- a/app/test/e2e/helpers/artifacts.ts +++ b/app/test/e2e/helpers/artifacts.ts @@ -176,12 +176,3 @@ export function saveMockRequestLog(label: string, log: unknown[]): string { console.log(`[artifacts] mock log "${label}" (${log.length} req) → ${path.basename(file)}`); return file; } - -/** - * Reset helper for tests that create multiple runs in one process. - */ -export function resetArtifactRun(): void { - runDir = null; - meta = null; - checkpointIndex = 0; -} diff --git a/app/test/e2e/helpers/config-toml.ts b/app/test/e2e/helpers/config-toml.ts index 62f9bac36..755d4cfce 100644 --- a/app/test/e2e/helpers/config-toml.ts +++ b/app/test/e2e/helpers/config-toml.ts @@ -21,7 +21,7 @@ function workspaceRoot(): string { return path.join(home, '.openhuman'); } -export function configTomlPath(): string { +function configTomlPath(): string { return path.join(workspaceRoot(), 'config.toml'); } @@ -57,7 +57,7 @@ export function topLevelValue(contents: string, key: string): string | null { * Extract a `key = value` line inside `[section]`. Works for any flat * `[a.b]`-style section name. */ -export function sectionValue(contents: string, section: string, key: string): string | null { +function sectionValue(contents: string, section: string, key: string): string | null { const lines = contents.split(/\r?\n/); let inSection = false; const target = `[${section}]`; diff --git a/app/test/e2e/helpers/core-rpc-node.ts b/app/test/e2e/helpers/core-rpc-node.ts index b6693f03a..cbbd7f2fd 100644 --- a/app/test/e2e/helpers/core-rpc-node.ts +++ b/app/test/e2e/helpers/core-rpc-node.ts @@ -138,7 +138,7 @@ async function tryPingRpc(url: string): Promise { * Resolve the sidecar JSON-RPC URL: full `OPENHUMAN_CORE_RPC_URL`, or * `OPENHUMAN_CORE_HOST` + `OPENHUMAN_CORE_PORT`, then probe host:port until core.ping succeeds. */ -export async function resolveCoreRpcUrl(): Promise { +async function resolveCoreRpcUrl(): Promise { if (cachedRpcUrl) { if (await tryPingRpc(cachedRpcUrl)) return cachedRpcUrl; cachedRpcUrl = null; diff --git a/app/test/e2e/helpers/core-rpc-webview.ts b/app/test/e2e/helpers/core-rpc-webview.ts index 2dffd41bb..05280e975 100644 --- a/app/test/e2e/helpers/core-rpc-webview.ts +++ b/app/test/e2e/helpers/core-rpc-webview.ts @@ -1,8 +1,5 @@ // @ts-nocheck -/** - * Invoke OpenHuman core JSON-RPC from the Tauri WebView (same transport as `callCoreRpc` in the app). - * Uses `invoke('core_rpc_url')` so the test follows the live sidecar port. - */ +/** Shared result shape for the Node-side E2E core RPC client. */ export interface RpcCallResult { ok: boolean; @@ -10,46 +7,3 @@ export interface RpcCallResult { error?: string; result?: T; } - -/** Linux tauri-driver only — Mac2 cannot run this (no WebView execute). Use `callOpenhumanRpc` from core-rpc.ts. */ -export async function callOpenhumanRpcWebView( - method: string, - params: Record = {} -): Promise> { - return browser.execute( - async (m: string, p: Record) => { - try { - const { invoke } = await import('@tauri-apps/api/core'); - const rpcUrl = await invoke('core_rpc_url'); - const id = Math.floor(Math.random() * 1e9); - const res = await fetch(rpcUrl, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ jsonrpc: '2.0', id, method: m, params: p }), - }); - const text = await res.text(); - let json: { error?: { message?: string }; result?: unknown }; - try { - json = JSON.parse(text) as typeof json; - } catch { - return { - ok: false, - httpStatus: res.status, - error: `Invalid JSON (${res.status}): ${text.slice(0, 240)}`, - }; - } - if (!res.ok) { - return { ok: false, httpStatus: res.status, error: text.slice(0, 500) }; - } - if (json.error) { - return { ok: false, error: json.error.message || JSON.stringify(json.error) }; - } - return { ok: true, result: json.result as T }; - } catch (e) { - return { ok: false, error: e instanceof Error ? e.message : String(e) }; - } - }, - method, - params - ); -} diff --git a/app/test/e2e/helpers/core-rpc.ts b/app/test/e2e/helpers/core-rpc.ts index 8c364525a..2f8ca9721 100644 --- a/app/test/e2e/helpers/core-rpc.ts +++ b/app/test/e2e/helpers/core-rpc.ts @@ -16,8 +16,7 @@ import { callOpenhumanRpcNode } from './core-rpc-node'; import type { RpcCallResult } from './core-rpc-webview'; -export type { RpcCallResult }; -export { expectRpcOk, formatRpcCallFailure } from './core-rpc-node'; +export { expectRpcOk } from './core-rpc-node'; export async function callOpenhumanRpc( method: string, diff --git a/app/test/e2e/helpers/element-helpers.ts b/app/test/e2e/helpers/element-helpers.ts index 53c42d238..bfdcad01d 100644 --- a/app/test/e2e/helpers/element-helpers.ts +++ b/app/test/e2e/helpers/element-helpers.ts @@ -148,7 +148,7 @@ export async function waitForText( * - Mac2: XCUIElementTypeButton XPath * - tauri-driver: CSS button / [role="button"] / a selector */ -export async function waitForButton( +async function waitForButton( text: string, timeout: number = 15_000 ): Promise { @@ -301,37 +301,6 @@ export async function clickNativeButton(text: string, timeout: number = 15_000): await clickAtElement(el); } -/** - * Wait for a toggle/switch element and click it. - * - * - Mac2: XCUIElementTypeSwitch / XCUIElementTypeCheckBox - * - tauri-driver: [role="switch"] / input[type="checkbox"] - */ -export async function clickToggle(_timeout: number = 15_000): Promise { - if (isTauriDriver()) { - const selectors = ['[role="switch"]', 'input[type="checkbox"]', 'button[aria-checked]']; - for (const sel of selectors) { - const el = await browser.$(sel); - if (await el.isExisting()) { - await clickAtElement(el); - return; - } - } - throw new Error('Toggle element not found'); - } - - // Mac2 path - const macSelectors = ['//XCUIElementTypeSwitch', '//XCUIElementTypeCheckBox']; - for (const sel of macSelectors) { - const el = await browser.$(sel); - if (await el.isExisting()) { - await clickAtElement(el); - return; - } - } - throw new Error('Toggle element not found'); -} - /** * Click an element matched by a selector. * diff --git a/app/test/e2e/helpers/platform.ts b/app/test/e2e/helpers/platform.ts index 6e06f9af2..37638a7a2 100644 --- a/app/test/e2e/helpers/platform.ts +++ b/app/test/e2e/helpers/platform.ts @@ -7,9 +7,8 @@ * Windows). Every session exposes the full DOM and supports W3C * `executeScript`, so the legacy branching collapses to a single backend. * - * These functions are kept (rather than deleted) as a shim so the ~40 - * existing specs that branch on `isTauriDriver()` / `isMac2()` still pass: - * they always take the DOM-capable code path now. + * The remaining detection functions are kept as a shim for specs that still + * branch on DOM execution support; they always take the DOM-capable path now. */ /** @@ -21,14 +20,6 @@ export function isTauriDriver(): boolean { return true; } -/** - * Always false. The Mac2 accessibility-tree backend is retired; macOS now - * speaks CDP just like Linux/Windows. - */ -export function isMac2(): boolean { - return false; -} - /** * Always true — Chromium driver is a full W3C WebDriver and supports * `browser.execute(...)` on every platform. diff --git a/app/test/e2e/helpers/rpc-preflight.ts b/app/test/e2e/helpers/rpc-preflight.ts deleted file mode 100644 index 8592f1fb5..000000000 --- a/app/test/e2e/helpers/rpc-preflight.ts +++ /dev/null @@ -1,99 +0,0 @@ -/** - * RPC contract preflight — validates that all RPC methods the E2E suite - * calls actually exist in the running core registry. - * - * Call this in a spec's `before()` or in wdio.conf.ts `before` hook. - * If any method is missing from the registry, the test fails immediately - * rather than silently returning "method not found" mid-test (RC-7 class fault). - */ -import { callOpenhumanRpc } from './core-rpc'; - -// The full list of openhuman.* RPC methods called across all E2E specs. -// When adding a new spec that calls a new RPC method, add it here. -const REQUIRED_RPC_METHODS = [ - 'core.ping', - 'openhuman.test_reset', - 'openhuman.notification_ingest', - 'openhuman.notification_list', - 'openhuman.notification_mark_read', - 'openhuman.notification_stats', - 'openhuman.memory_doc_put', - 'openhuman.memory_clear_namespace', - 'openhuman.memory_recall_memories', - 'openhuman.threads_create_new', - 'openhuman.threads_list', - 'openhuman.threads_message_append', - 'openhuman.threads_messages_list', - 'openhuman.webhooks_clear_logs', - 'openhuman.webhooks_register_echo', - 'openhuman.webhooks_unregister_echo', - 'openhuman.composio_list_tools', - 'openhuman.composio_list_available_triggers', - 'openhuman.composio_list_triggers', - 'openhuman.composio_enable_trigger', - 'openhuman.composio_disable_trigger', - 'openhuman.about_app_list', -] as const; - -export type RpcMethod = (typeof REQUIRED_RPC_METHODS)[number]; - -/** - * Fetch the controller schema list from the running core and verify - * every required method is registered. - * - * Returns an object: { ok: boolean; missing: string[]; registered: string[] } - * Does NOT throw — callers decide whether to fail the suite. - */ -export async function validateRpcContract(): Promise<{ - ok: boolean; - missing: string[]; - registered: string[]; -}> { - const result = await callOpenhumanRpc('openhuman.about_app_list', {}).catch(() => null); - if (!result?.ok) { - return { ok: false, missing: [], registered: [] }; - } - - // about_app_list returns: { controllers: [{ method: string }] } or similar - const controllers: Array<{ method?: string; name?: string }> = - (result.result as any)?.controllers ?? - (result.result as any)?.methods ?? - (result.result as any)?.result?.controllers ?? - []; - - const registered = controllers.map(c => c.method ?? c.name ?? '').filter(Boolean); - - const missing = REQUIRED_RPC_METHODS.filter( - m => !registered.includes(m) && m !== 'core.ping' // core.ping is not a controller - ); - - return { ok: missing.length === 0, missing, registered }; -} - -/** - * Assert the RPC contract. Call from a spec's before() hook. - * Skips gracefully if about_app_list is not available (older builds). - */ -export async function assertRpcContract(logPrefix = '[RpcPreflight]'): Promise { - console.log(`${logPrefix} Validating RPC contract...`); - const { missing, registered } = await validateRpcContract(); - - if (registered.length === 0) { - console.warn(`${logPrefix} Could not fetch controller registry — skipping validation`); - return; - } - - if (missing.length > 0) { - const msg = - `${logPrefix} FATAL: ${missing.length} RPC method(s) not found in registry:\n` + - missing.map(m => ` - ${m}`).join('\n') + - '\nThis is an RC-7 class fault — the spec calls ghost RPCs. ' + - 'Fix: update REQUIRED_RPC_METHODS or restore the missing controllers.'; - console.error(msg); - throw new Error(msg); - } - - console.log( - `${logPrefix} RPC contract OK — ${registered.length} controllers registered, all required methods present` - ); -} diff --git a/app/test/e2e/helpers/shared-flows.ts b/app/test/e2e/helpers/shared-flows.ts index 933597a89..5b3fe0562 100644 --- a/app/test/e2e/helpers/shared-flows.ts +++ b/app/test/e2e/helpers/shared-flows.ts @@ -99,19 +99,10 @@ export async function waitForHomePage(timeout = 15_000) { return null; } -export async function waitForTextToDisappear(text, timeout = 10_000) { - const deadline = Date.now() + timeout; - while (Date.now() < deadline) { - if (!(await textExists(text))) return true; - await browser.pause(500); - } - return false; -} - /** * Click the first matching text from a list of candidates. */ -export async function clickFirstMatch(candidates, timeout = 5_000) { +async function clickFirstMatch(candidates, timeout = 5_000) { for (const text of candidates) { if (await textExists(text)) { await clickText(text, timeout); @@ -489,31 +480,22 @@ export async function navigateToConnections() { await navigateViaHash('/connections?tab=composio'); } -/** @deprecated Phase 3: use navigateToActivity() instead. Still works via redirect. */ +/** @deprecated The legacy intelligence route still works via redirect. */ export async function navigateToIntelligence() { await navigateViaHash('/activity'); } -/** Navigate to the Activity page (was /intelligence in Phase 2). */ -export async function navigateToActivity() { - await navigateViaHash('/activity'); -} - export async function navigateToConversations() { await navigateViaHash('/chat'); } -export async function navigateToNotifications() { - await navigateViaHash('/notifications'); -} - // --------------------------------------------------------------------------- // Onboarding walkthrough // Current flow: Welcome → Skills → optional Context gathering. // --------------------------------------------------------------------------- /** Labels used to detect the onboarding overlay (same strings as Onboarding copy). */ -export const ONBOARDING_OVERLAY_TEXTS = [ +const ONBOARDING_OVERLAY_TEXTS = [ 'Skip', 'Welcome', "Hi. I'm OpenHuman.", @@ -549,10 +531,6 @@ async function onboardingOverlayLikelyVisible(): Promise { return false; } -export async function isOnboardingOverlayVisible(): Promise { - return onboardingOverlayLikelyVisible(); -} - export async function waitForOnboardingOverlayVisible(timeout = 10_000): Promise { const deadline = Date.now() + timeout; while (Date.now() < deadline) { @@ -562,15 +540,6 @@ export async function waitForOnboardingOverlayVisible(timeout = 10_000): Promise return false; } -export async function waitForOnboardingOverlayHidden(timeout = 10_000): Promise { - const deadline = Date.now() + timeout; - while (Date.now() < deadline) { - if (!(await onboardingOverlayLikelyVisible())) return true; - await browser.pause(400); - } - return false; -} - export async function dismissWalkthroughIfVisible(timeout = 6_000): Promise { const deadline = Date.now() + timeout; while (Date.now() < deadline) { diff --git a/app/test/e2e/helpers/telegram.ts b/app/test/e2e/helpers/telegram.ts index 0793b250a..5e6f98a80 100644 --- a/app/test/e2e/helpers/telegram.ts +++ b/app/test/e2e/helpers/telegram.ts @@ -31,7 +31,7 @@ const LOG_PREFIX = '[TelegramChannel]'; // Types // --------------------------------------------------------------------------- -export interface TelegramConnectOptions { +interface TelegramConnectOptions { /** Bot token issued by BotFather. */ botToken: string; /** @@ -47,7 +47,7 @@ export interface TelegramConnectOptions { mentionOnly?: boolean; } -export interface TelegramConnectResult { +interface TelegramConnectResult { ok: boolean; status?: string; restartRequired?: boolean; @@ -55,14 +55,14 @@ export interface TelegramConnectResult { error?: string; } -export interface TelegramStatusEntry { +interface TelegramStatusEntry { channelId: string; authMode: string; connected: boolean; hasCredentials: boolean; } -export interface TelegramUpdate { +interface TelegramUpdate { update_id: number; message: { message_id: number; @@ -79,7 +79,7 @@ export interface TelegramUpdate { }; } -export interface SentMessage { +interface SentMessage { method: string; chat_id: string | number; text?: string; diff --git a/app/test/e2e/mock-server.ts b/app/test/e2e/mock-server.ts index 84398b8ef..a14637447 100644 --- a/app/test/e2e/mock-server.ts +++ b/app/test/e2e/mock-server.ts @@ -7,8 +7,6 @@ */ export { clearRequestLog, - emitMockAgentAudioStream, - getMockBehavior, getMockServerPort, getRequestLog, resetMockBehavior, diff --git a/app/test/e2e/specs/card-payment-flow.spec.ts b/app/test/e2e/specs/card-payment-flow.spec.ts index 84688eebf..a86f81219 100644 --- a/app/test/e2e/specs/card-payment-flow.spec.ts +++ b/app/test/e2e/specs/card-payment-flow.spec.ts @@ -76,7 +76,7 @@ describe('Card Payment Flow', () => { expect(onSettings).toBe(true); console.log(`${LOG_PREFIX} 5.3 — back-to-settings navigation works`); } else { - // Fallback: use PageBackButton's generic back arrow + // Fallback: return through the settings navigation helper. await navigateToSettings(); const onSettings = await textExists('Settings'); expect(onSettings).toBe(true); diff --git a/app/test/playwright/helpers/core-rpc.ts b/app/test/playwright/helpers/core-rpc.ts index e92997e58..d21c1858c 100644 --- a/app/test/playwright/helpers/core-rpc.ts +++ b/app/test/playwright/helpers/core-rpc.ts @@ -42,7 +42,7 @@ export async function callCoreRpc( return payload.result; } -export async function resetCoreForWebUser(userId: string): Promise { +async function resetCoreForWebUser(userId: string): Promise { await callCoreRpc('openhuman.auth_clear_session', {}); await callCoreRpc('openhuman.config_set_onboarding_completed', { value: true }); await callCoreRpc('openhuman.auth_store_session', { token: buildBypassJwt(userId) }); @@ -112,7 +112,7 @@ async function completeAuthCallback(page: Page, token: string): Promise { } } -export async function resetCoreForWebGuest(): Promise { +async function resetCoreForWebGuest(): Promise { await callCoreRpc('openhuman.auth_clear_session', {}); await callCoreRpc('openhuman.config_set_onboarding_completed', { value: true }); } diff --git a/docs/plans/2026-07-24-knip-cleanup.md b/docs/plans/2026-07-24-knip-cleanup.md new file mode 100644 index 000000000..69bcbdc8d --- /dev/null +++ b/docs/plans/2026-07-24-knip-cleanup.md @@ -0,0 +1,797 @@ +# OpenHuman App Knip Cleanup Implementation Plan + +> **Execution note:** Carry this plan out from +> `/Users/enamakel/work/workflow-openhuman/worktrees/knip-cleanup/openhuman` on +> `chore/knip-cleanup`. Use Node 24 (`source "$HOME/.nvm/nvm.sh" && nvm use 24`) +> for every pnpm/Knip command. Do not push, open a PR, edit Rust, touch another +> umbrella submodule, or expand the cleanup beyond `app/`. + +**Goal:** Make Knip's OpenHuman app analysis trustworthy, then remove unreachable +TypeScript/React files, unused package dependencies, and unnecessary exported +surface without changing runtime behavior. + +**Design:** Treat Knip as a static-analysis regression test, not as an automatic +deletion list. First model every Vite, Vitest, WebdriverIO, Playwright, Tauri, +package-script, and shell-launched entry point. Then process findings in +subsystem-sized slices, independently checking static imports, barrel exports, +string references, package scripts, test configuration, Tauri configuration, +and shell/workflow invocation before changing a file. Delete a declaration only +when it is unused; when the declaration is used locally, remove only `export`. +Keep intentional contracts through the narrowest documented Knip exception. + +**Scope:** `app/knip.json`, `app/package.json`, the applicable pnpm lockfiles, +and Knip-confirmed dead or over-exported TypeScript/TSX under `app/src/` and +`app/test/`. Generated assets, `app/src-tauri/`, vendored sources, Rust, public +docs, and unrelated formatting/refactors are out of scope. + +--- + +## Mandatory execution discipline + +Apply these rules to every task below: + +1. Start with `git status --short --branch`. Stop if another agent has modified + a file needed by the task; coordinate instead of reverting or overwriting it. +2. Run the stated **red** Knip query before editing and save the relevant output + in the terminal/log, not in the repository. +3. Independently verify every candidate with `rg` before modifying it: + + ```bash + rg -n '' app package.json scripts .github \ + --glob '!app/pnpm-lock.yaml' --glob '!pnpm-lock.yaml' \ + --glob '!app/src-tauri/vendor/**' --glob '!node_modules/**' \ + --glob '!target/**' + ``` + + Also inspect `app/package.json`, `app/index.html`, `app/vite.config.ts`, + `app/test/vitest.config.ts`, `app/test/wdio.conf.ts`, + `app/playwright.config.ts`, `app/scripts/`, `app/src-tauri/tauri.conf.json`, + and `.github/workflows/` whenever the candidate could be loaded by name, + convention, a script, or Tauri. + +4. Make the smallest change: + - delete a file/declaration only when no runtime, build, test, documentation, + generated-manifest, tooling, or dynamic-loading role exists; + - remove only `export` when the declaration is used in its own module; + - remove one side of a duplicate named/default export and update imports to + the retained form; + - keep intentional public/test seams with the narrowest possible + `app/knip.json` exception and an adjacent reason. +5. Run the stated **green** checks. A targeted Knip category must have no + actionable findings in the files handled by the step and must introduce no + new `files`, `dependencies`, or `unlisted` findings. +6. Run Prettier only on the explicitly touched files: + + ```bash + pnpm --filter openhuman-app exec prettier --check + ``` + +7. Review `git diff --check` and `git diff -- `. +8. Commit immediately after validation. Manually enumerate every touched path; + never use a glob, directory, `git add`, or command substitution: + + ```bash + atomic-commit "" -- path/to/file1 path/to/file2 + ``` + +9. Do not begin the next task until the current task is committed. + +If deleting dead production code leaves a test whose only purpose was to test +that dead code, independently verify that fact and delete the orphan test in +the same atomic commit. A test is not evidence that a production entry point is +live. + +## Baseline + +With Node 24, the approved-design baseline currently reports: + +- 34 unused files; +- `lottie-react` and `react-ga4` as unused dependencies; +- direct but undeclared imports of `webdriverio` and `@wdio/globals`; +- 131 unused value exports; +- 276 unused exported types; +- one unused enum-member group; +- 41 duplicate exports. + +The counts may shrink after entry-point correction. Always use the post-config +report as the deletion authority. + +--- + +### Task 1: Model all real app and tooling entry points + +**Files:** + +- Modify: `app/knip.json` +- Modify only if direct dependency evidence requires it: + `app/package.json`, `pnpm-lock.yaml`, `app/pnpm-lock.yaml` + +**Step 1: Record the red baseline** + +Run: + +```bash +source "$HOME/.nvm/nvm.sh" +nvm use 24 +pnpm --filter openhuman-app exec knip --config knip.json --reporter compact +``` + +Expected: non-zero with `test/wdio.conf.ts` incorrectly reported as unused, +unlisted `webdriverio`/`@wdio/globals`, and the remaining cleanup candidates. + +**Step 2: Prove each indirect entry** + +Run and inspect: + +```bash +rg -n 'src/main\.tsx|vite\.config|vitest\.config|wdio\.conf|playwright\.config|build-parallel|e2e-run-session' \ + app/index.html app/package.json app/scripts scripts .github +rg -n 'test/e2e/specs|test/playwright/specs|src/test/setup' \ + app/test app/playwright.config.ts app/package.json app/scripts .github +rg -n 'webdriverio|@wdio/globals|@wdio/types' app/test app/package.json +``` + +Evidence that must be preserved: + +- `app/index.html` loads `src/main.tsx`; +- package build scripts execute `scripts/build-parallel.mjs`, which invokes the + Vite build; +- unit scripts load `test/vitest.config.ts`, whose `setupFiles` loads + `src/test/setup.ts` and whose `include` globs load app tests; +- `app/scripts/e2e-run-session.sh` executes + `pnpm exec wdio run test/wdio.conf.ts`; +- `test/wdio.conf.ts` loads all `test/e2e/specs/**/*.spec.ts`; +- Playwright configuration loads `test/playwright/specs/**/*.spec.ts`; +- WebdriverIO types/globals imported by source are direct dev dependencies, + even if another WDIO package currently installs them transitively; +- Tauri/Vite/package-script binaries are real even when referenced only in a + package script or shell script. + +**Step 3: Correct Knip configuration narrowly** + +Update `app/knip.json` so the verified configuration/spec/tooling files are +explicit entries. At minimum the graph must include: + +```json +{ + "entry": [ + "src/main.tsx", + "vite.config.ts", + "test/vitest.config.ts", + "test/wdio.conf.ts", + "playwright.config.ts", + "test/e2e/specs/**/*.spec.ts", + "test/playwright/specs/**/*.spec.ts" + ] +} +``` + +Retain `project` coverage for app and test TypeScript. Add a package-script or +tooling script as an entry only when Knip does not discover its verified +invocation. Do not add a broad `scripts/**`, `test/**`, or `src/**` entry merely +to make findings disappear. + +Remove an `ignoreDependencies` or `ignoreBinaries` entry only after the +corrected graph recognizes the real usage. Keep an exception only when the +package manager, Tauri, or a shell script invokes it in a way Knip cannot model, +and document the exact invocation next to that exception. + +**Step 4: Declare direct WDIO imports** + +If the corrected graph still reports the existing imports as unlisted, add +`@wdio/globals` and `webdriverio` to `app/devDependencies` at the same `9.24.x` +family used by the other WDIO packages. Add `@wdio/types` too if Knip or +`pnpm why @wdio/types` confirms `test/wdio.conf.ts` relies on it transitively. +Regenerate only the lockfile sections affected by those declarations. Do not +upgrade unrelated packages. + +**Step 5: Run the green checks** + +```bash +pnpm --filter openhuman-app exec knip --config knip.json --reporter compact +pnpm --filter openhuman-app compile +pnpm --filter openhuman-app exec tsc -p test/tsconfig.e2e.json --noEmit +pnpm --filter openhuman-app exec prettier --check knip.json package.json +git diff --check +``` + +Expected: `test/wdio.conf.ts` and all verified framework/tooling entries are no +longer unused; `webdriverio` and `@wdio/globals` are no longer unlisted; no +broad ignore has hidden app source. + +**Step 6: Commit** + +Use: + +```bash +atomic-commit "chore(app): make knip entry analysis accurate" -- app/knip.json app/package.json pnpm-lock.yaml app/pnpm-lock.yaml +``` + +Omit any listed path that was not actually changed, while continuing to name +every changed path explicitly. + +--- + +### Task 2: Remove independently verified unreachable files + +**Candidate files (post-Task-1 Knip must still report each before deletion):** + +- `app/src/agentworld/theme/AgentWorldThemeBridge.tsx` +- `app/src/assets/icons/GoogleIcon.tsx` +- `app/src/components/ConnectionBadge.tsx` +- `app/src/components/LottieAnimation.tsx` +- `app/src/components/accounts/RespondQueuePanel.tsx` +- `app/src/components/chat/CycleUsagePill.tsx` +- `app/src/components/chat/TokenUsagePill.tsx` +- `app/src/components/intelligence/SyncBudgetDialog.tsx` +- `app/src/components/intelligence/SyncConfirmDialog.tsx` +- `app/src/components/routines/RoutineCard.tsx` +- `app/src/components/routines/RoutineRunHistory.tsx` +- `app/src/components/settings/components/PageBackButton.tsx` +- `app/src/components/settings/components/SettingsHeader.tsx` +- `app/src/components/settings/panels/autocomplete/AppFilterSection.tsx` +- `app/src/components/settings/panels/autocomplete/CompletionStyleSection.tsx` +- `app/src/components/settings/panels/billing/BillingHistoryTab.tsx` +- `app/src/components/settings/panels/billing/BillingPaymentsTab.tsx` +- `app/src/components/settings/panels/billing/BillingPlansTab.tsx` +- `app/src/components/settings/panels/billing/PayAsYouGoCard.tsx` +- `app/src/components/skills/SkillResourceTree.tsx` +- `app/src/features/conversations/components/LimitPill.tsx` +- `app/src/features/conversations/index.ts` +- `app/src/hooks/useIntelligenceApiFallback.ts` +- `app/src/hooks/useIntelligenceStats.ts` +- `app/src/hooks/useScreenIntelligenceItems.ts` +- `app/src/lib/ai/skillsAgentContext.ts` +- `app/src/pages/Routines.tsx` +- `app/src/pages/onboarding/components/ConfigureLaterCallout.tsx` +- `app/src/pages/onboarding/pages/ApiKeysPage.tsx` +- `app/src/pages/onboarding/pages/ChatProviderPage.tsx` +- `app/src/pages/onboarding/pages/CustomMemoryPage.tsx` +- `app/src/pages/onboarding/steps/ReferralApplyStep.tsx` +- `app/test/e2e/helpers/rpc-preflight.ts` + +`app/test/wdio.conf.ts` is explicitly **not** a deletion candidate. + +**Step 1: Red-check the file set** + +```bash +pnpm --filter openhuman-app exec knip --config knip.json --include files --reporter compact +``` + +Expected: only files that survived Task 1 remain in the candidate set above. +Drop any file from this task if entry-point correction made it reachable. + +**Step 2: Verify every file independently** + +For every remaining file, search the full repository for its path, filename, +stem, exported component/hook names, route paths, lazy import strings, +`import.meta.glob`, index/barrel exports, package scripts, test configs, Tauri +resources, and documentation references. + +Pay special attention to: + +- `Routines.tsx`: current routes redirect legacy `/routines`; do not remove + translations or redirects merely because the old page is dead. +- onboarding pages: commented-out JSX is not a live entry; do not remove shared + onboarding types or current wizard routes. +- `GoogleIcon.tsx`: a separate local `GoogleIcon` exists in + `components/oauth/providerConfigs.tsx`; keep the live local implementation. +- `useScreenIntelligenceItems.ts`: inspect + `hooks/__tests__/useScreenIntelligenceItems.test.ts`; delete that test only if + it is an orphan that duplicates dead behavior rather than importing a live + unit. +- `rpc-preflight.ts`: comments claiming it can be called are not execution; + verify neither WDIO config nor a spec imports it. +- files in settings, routines, intelligence, and billing: search route + registries and lazy imports in addition to static imports. + +**Step 3: Delete only confirmed-dead files** + +Delete the files that remain unreachable. Remove now-empty barrel exports or +now-empty directories only when they contain no other tracked assets. Do not +delete localization strings, styles, or tests merely because their names are +similar. + +**Step 4: Green-check the slice** + +```bash +pnpm --filter openhuman-app exec knip --config knip.json --include files --reporter compact +pnpm --filter openhuman-app compile +pnpm --filter openhuman-app test -- --run +pnpm --filter openhuman-app lint +git diff --check +``` + +Expected: no verified file candidate remains; unit tests, types, and lint pass. + +**Step 5: Commit** + +Commit with every deleted file and any orphan test/barrel update named +explicitly: + +```bash +atomic-commit "refactor(app): remove unreachable frontend files" -- +``` + +Do not use the angle-bracket text literally; replace it with the full explicit +path list. + +--- + +### Task 3: Remove unused package dependencies + +**Files:** + +- Modify: `app/package.json` +- Modify: `pnpm-lock.yaml` +- Modify: `app/pnpm-lock.yaml` + +**Step 1: Red-check dependency findings** + +```bash +pnpm --filter openhuman-app exec knip --config knip.json --include dependencies,unlisted --reporter compact +rg -n "from ['\"](lottie-react|react-ga4)['\"]|import\\(['\"](lottie-react|react-ga4)['\"]\\)" \ + app/src app/test app/vite.config.ts app/scripts +``` + +Expected: after Task 2 deletes `LottieAnimation.tsx`, neither package has a +live import. A stale test comment mentioning `react-ga4` is not a dependency. + +**Step 2: Remove only confirmed-unused declarations** + +Remove `lottie-react` and `react-ga4` from `app/package.json`. Update the root +workspace lock and the tracked `app/pnpm-lock.yaml` without upgrading unrelated +packages. Inspect both lockfile diffs and revert any resolver churn unrelated +to these two removals and the direct WDIO declarations from Task 1. + +**Step 3: Green-check dependencies** + +```bash +pnpm --filter openhuman-app exec knip --config knip.json --include dependencies,unlisted --reporter compact +pnpm --filter openhuman-app compile +pnpm --filter openhuman-app test -- --run +pnpm --filter openhuman-app exec prettier --check package.json +git diff --check +``` + +Expected: no actionable unused or unlisted dependency finding. + +**Step 4: Commit** + +```bash +atomic-commit "chore(app): remove unused frontend dependencies" -- app/package.json pnpm-lock.yaml app/pnpm-lock.yaml +``` + +--- + +### Task 4: Normalize duplicate exports and barrels + +**Files:** The post-Task-3 duplicate report, initially including 41 findings +across: + +- `app/src/components/chat/` +- `app/src/components/flows/` +- `app/src/components/intelligence/` +- `app/src/components/mcp-setup/` +- `app/src/components/meetings/` +- `app/src/components/skills/` +- `app/src/components/userErrors/` +- `app/src/features/conversations/` +- `app/src/features/human/` +- `app/src/features/meet/` +- `app/src/features/share/` +- `app/src/hooks/` +- `app/src/lib/attachments.ts` +- `app/src/lib/mcp/rateLimiter.ts` +- `app/src/services/api/flowsApi.ts` + +**Step 1: Red-check duplicates** + +```bash +pnpm --filter openhuman-app exec knip --config knip.json --include duplicates --reporter compact +``` + +**Step 2: Choose one canonical export per symbol** + +For each duplicate, search every import and barrel re-export. Retain the import +shape already used by live consumers; remove the redundant named/default +export only. Update imports only when necessary to converge on that canonical +shape. Do not rename components or refactor their implementation. + +For `attachments.ts`, verify whether +`ATTACHMENT_MAX_IMAGE_SIZE_BYTES` and `ATTACHMENT_MAX_SIZE_BYTES` are aliases +before removing either export. For `rateLimiter.ts`, distinguish an actual +duplicate re-export from two different implementations. For `flowsApi.ts`, +retain the import shape used by the majority of live callers. + +Process and commit this task in at most three subsystem commits if the explicit +path list becomes too large: + +1. chat/flows/intelligence; +2. meetings/skills/human/share/hooks; +3. lib/services/barrels. + +Each commit must independently pass: + +```bash +pnpm --filter openhuman-app exec knip --config knip.json --include duplicates --reporter compact +pnpm --filter openhuman-app compile +pnpm --filter openhuman-app test -- --run +pnpm --filter openhuman-app lint +git diff --check +``` + +Commit each validated slice with explicit paths: + +```bash +atomic-commit "refactor(app): remove duplicate exports" -- +``` + +Expected at task completion: zero unexplained duplicate-export findings. + +--- + +### Task 5: Internalize Agent World and mascot exports + +**Files:** Post-Task-4 unused values/types in: + +- `app/src/agentworld/**` +- `app/src/features/human/Mascot/**` +- `app/src/features/meet/MascotFrameProducer.tsx` +- `app/src/features/meet/useMeetingMascots.ts` + +**Step 1: Red-check the subsystem** + +```bash +pnpm --filter openhuman-app exec knip --config knip.json --include exports,types --reporter compact \ + | rg 'src/(agentworld|features/human/Mascot|features/meet/)' +``` + +**Step 2: Verify public-looking barrels** + +Inspect `agentworld/iso/index.ts` and `features/human/Mascot/index.ts` against +all imports. A barrel item unused inside this private app workspace is not +automatically a public package API. Remove dead barrel re-exports, but keep the +underlying declaration when imported directly or used in its defining module. +For component props, manifest types, renderer types, and hook result types used +only locally, remove `export` rather than deleting the type. + +Do not modify runtime rendering, sprite/room registration, Rive asset lookup, +manifest discovery, or Tauri resource paths. + +**Step 3: Green-check and commit** + +```bash +pnpm --filter openhuman-app exec knip --config knip.json --include exports,types --reporter compact \ + | rg 'src/(agentworld|features/human/Mascot|features/meet/)' && exit 1 || true +pnpm --filter openhuman-app compile +pnpm --filter openhuman-app test -- --run +pnpm --filter openhuman-app lint +git diff --check +atomic-commit "refactor(app): internalize agent world and mascot APIs" -- +``` + +The `rg` pipeline must be interpreted carefully: only the absence of actionable +findings for this subsystem is green; a Knip process failure must not be hidden. + +--- + +### Task 6: Internalize conversations, flows, orchestration, and hook exports + +**Files:** Post-Task-5 unused values/types in: + +- `app/src/components/flows/**` +- `app/src/features/conversations/**` +- `app/src/hooks/useFlow*.ts` +- `app/src/hooks/useRunsPendingApprovalSet.ts` +- `app/src/lib/flows/**` +- `app/src/lib/orchestration/**` +- `app/src/pages/FlowCanvasPage.tsx` +- `app/src/services/api/flowsApi.ts` +- `app/src/services/api/workflowRunsApi.ts` + +**Step 1: Red-check and classify** + +Run Knip for `exports,types`, filter to these paths, then classify each finding: +delete an unused declaration, internalize a locally used declaration, or retain +an intentional test seam. Search tests before touching constants such as +`VALIDATION_DEBOUNCE_MS`, model hints, timeline kinds, flow API helpers, and +copilot seed types. + +**Step 2: Apply the smallest visibility/dead-code changes** + +Preserve runtime hook registration, event names, API payload shapes, and flow +node-kind registries. Do not remove a flow API method solely because the React +app does not call it until repository-wide and documented-extension searches +also show it is private and unused. + +**Step 3: Validate affected tests** + +Run the specific test files that import any changed module, followed by: + +```bash +pnpm --filter openhuman-app compile +pnpm --filter openhuman-app test -- --run +pnpm --filter openhuman-app lint +pnpm --filter openhuman-app exec knip --config knip.json --include exports,types --reporter compact +git diff --check +``` + +**Step 4: Commit** + +Split into a flows commit and a conversations/orchestration commit if needed, +with all paths explicit: + +```bash +atomic-commit "refactor(app): trim unused flow module exports" -- +atomic-commit "refactor(app): trim unused conversation exports" -- +``` + +Run and commit each command only after its own validation. + +--- + +### Task 7: Internalize settings, layout, UI, meetings, and feature exports + +**Files:** Post-Task-6 unused values/types in: + +- `app/src/components/layout/**` +- `app/src/components/ui/**` +- `app/src/components/settings/**` +- `app/src/components/meetings/**` +- `app/src/components/channels/**` +- `app/src/components/oauth/**` +- `app/src/components/skills/**` +- remaining `app/src/components/**` +- remaining `app/src/features/**` + +**Step 1: Red-check and inspect barrel contracts** + +Run the `exports,types` report filtered to these paths. Search all imports from +`components/ui`, `components/settings/controls`, settings route registries, +meeting components, and skill components before editing their index files. + +**Step 2: Remove only unnecessary external visibility** + +Typical safe changes are: + +- `export interface FooProps` to `interface FooProps` when only the component + in that file uses it; +- `export const` to `const` when local code/tests do not import it; +- remove a barrel re-export when every consumer imports directly; +- delete a value/type only when no local use remains. + +Preserve settings route registry data, Tauri command names, analytics IDs, +documented extension points, and intentional test seams. + +**Step 3: Green-check and commit in bounded slices** + +Use two commits if necessary: + +1. settings/layout/UI; +2. meetings/channels/skills/remaining features. + +For each slice: + +```bash +pnpm --filter openhuman-app compile +pnpm --filter openhuman-app test -- --run +pnpm --filter openhuman-app lint +pnpm --filter openhuman-app exec knip --config knip.json --include exports,types --reporter compact +git diff --check +atomic-commit "refactor(app): trim unused exports" -- +``` + +--- + +### Task 8: Internalize services, stores, libraries, and shared types + +**Files:** Post-Task-7 unused values/types in: + +- `app/src/services/**` +- `app/src/store/**` +- `app/src/lib/**` +- `app/src/types/**` +- `app/src/utils/**` +- remaining `app/src/hooks/**` +- `app/src/polyfills.ts` + +**Step 1: Red-check and separate API shape from TypeScript visibility** + +Run Knip for `exports,types`. For RPC/API modules, distinguish an exported +TypeScript declaration from a serialized field or method string: removing +`export` must not change the runtime object, payload, enum value, or command +name. Search the Rust/Tauri boundary and docs whenever a string might be an IPC +contract. + +**Step 2: Process high-risk modules conservatively** + +Explicitly verify: + +- `services/coreRpcClient.ts`, `services/rpcMethods.ts`, and all `services/api/*` + against frontend imports, tests, Tauri command strings, and documented RPC + contracts; +- Redux slice actions/selectors against `store/index.ts`, middleware, + persistence, tests, and dynamic dispatch; +- `lib/agentworld/invokeApiClient.ts` types against all Agent World consumers; +- `lib/mcp/*` tool classification and rate-limit symbols against tests and + string-driven tool handling; +- `polyfills.ts` is imported for side effects by `src/main.tsx`; remove only + unused exports, never the file or its side effects; +- platform, notification, webview, tunnel, attachment, and theme constants + against runtime branches and tests. + +**Step 3: Apply and validate in three atomic slices** + +1. services/API clients; +2. stores/selectors; +3. libraries/types/utils/hooks. + +For every slice: + +```bash +pnpm --filter openhuman-app compile +pnpm --filter openhuman-app test -- --run +pnpm --filter openhuman-app lint +pnpm --filter openhuman-app exec knip --config knip.json --include exports,types --reporter compact +git diff --check +atomic-commit "refactor(app): trim unused exports" -- +``` + +Do not batch the three slices into one commit. + +--- + +### Task 9: Clean test-helper exports and unused enum members + +**Files:** Post-Task-8 findings in: + +- `app/test/e2e/helpers/**` +- `app/test/e2e/mock-server.ts` +- `app/test/playwright/helpers/**` +- `app/src/lib/mcp/errorHandler.ts` + +**Step 1: Red-check** + +```bash +pnpm --filter openhuman-app exec knip --config knip.json \ + --include exports,types,enumMembers --reporter compact +``` + +**Step 2: Verify test discovery and helper use** + +Search all WDIO and Playwright specs before internalizing or deleting a helper. +Test specs and configs are entry points; helper files are not. Preserve helpers +called through the WDIO lifecycle hooks or fixture registration even when no +spec imports them directly. + +For `ErrorCategory`, search string values and computed property access in +addition to enum member names. Remove `CONTACT`, `GROUP`, `MEDIA`, `PROFILE`, +`AUTH`, `ADMIN`, `SEARCH`, and `DRAFT` only if none is used dynamically and +removing them does not alter serialized behavior. + +**Step 3: Green-check** + +```bash +pnpm --filter openhuman-app exec tsc -p test/tsconfig.e2e.json --noEmit +pnpm --filter openhuman-app compile +pnpm --filter openhuman-app test -- --run +pnpm --filter openhuman-app lint +pnpm --filter openhuman-app exec knip --config knip.json \ + --include exports,types,enumMembers --reporter compact +git diff --check +``` + +**Step 4: Commit** + +```bash +atomic-commit "refactor(app): trim unused test helper exports" -- +``` + +--- + +### Task 10: Resolve only genuinely intentional residual findings + +**Files:** + +- Modify only if needed: `app/knip.json` +- Modify only if needed: files containing deliberate public/test seams + +**Step 1: Run the complete report** + +```bash +pnpm --filter openhuman-app exec knip --config knip.json --reporter compact +pnpm --filter openhuman-app exec knip --config knip.json --production --reporter compact +``` + +**Step 2: Adversarially verify every residual** + +For every residual finding, repeat the full static/dynamic search. A finding may +remain only when there is concrete evidence of convention-based loading, +package/Tauri/tooling invocation, generated input, documented public contract, +or intentional test seam that Knip cannot model. + +Prefer modeling another precise entry or dependency over ignoring a finding. +If an exception is unavoidable, scope it to the exact file/package/symbol +supported by Knip's schema and add a one-line reason naming the loader or +contract. Do not add directory-wide ignores, `src/**`, `test/**`, or global +export suppression. + +**Step 3: Green-check and commit exceptions separately** + +```bash +pnpm --filter openhuman-app exec knip --config knip.json --reporter compact +pnpm --filter openhuman-app exec knip --config knip.json --production --reporter compact +pnpm --filter openhuman-app compile +pnpm --filter openhuman-app test -- --run +pnpm --filter openhuman-app lint +git diff --check +atomic-commit "chore(app): document intentional knip exceptions" -- app/knip.json +``` + +Skip the commit entirely if no exceptions are needed. + +--- + +### Task 11: Final independent verification + +**Files:** No planned edits. + +**Step 1: Confirm the branch and clean worktree** + +```bash +git status --short --branch +git log --oneline --decorate -15 +``` + +Expected: branch `chore/knip-cleanup`; every completed slice has its own atomic +commit; no uncommitted implementation changes. + +**Step 2: Re-run the complete quality gate from a fresh Node 24 shell** + +```bash +source "$HOME/.nvm/nvm.sh" +nvm use 24 +pnpm knip +pnpm knip:production +pnpm typecheck +pnpm test +pnpm lint +pnpm format:check +pnpm build +``` + +Expected: every command exits zero. If `format:check` finds pre-existing, +out-of-scope failures, prove they exist at the design commit before claiming an +exception; otherwise fix only formatting in files touched by this cleanup and +amend it through a new atomic formatting commit with explicit paths. + +**Step 3: Verify no scope leakage** + +```bash +git diff --name-only eb2bda897..HEAD +git diff --stat eb2bda897..HEAD +git diff --check eb2bda897..HEAD +git status --short +``` + +Expected: implementation changes are confined to `app/`; the approved design +and this plan are the only `docs/` changes; no Rust, vendor, generated asset, +other submodule, or umbrella gitlink changed. + +**Step 4: Review behavior preservation** + +Inspect the complete diff and confirm: + +- no route, command, event, RPC method, analytics ID, payload field, or visible + copy changed; +- Vite, Vitest, WDIO, Playwright, Tauri, package-script, and shell-launched + entry points remain tracked by Knip or have a precise documented exception; +- removed files have no imports, string references, generated-manifest role, + tooling role, or documentation contract; +- dependency changes contain no unrelated upgrades; +- every remaining export is live or deliberately retained. + +Do not push or open a PR. Report the commit list, deleted files/dependencies, +final Knip result, every validation command and exit status, and any narrowly +documented exception to the requesting agent. diff --git a/docs/specs/2026-07-24-knip-cleanup-design.md b/docs/specs/2026-07-24-knip-cleanup-design.md new file mode 100644 index 000000000..7a1307bf1 --- /dev/null +++ b/docs/specs/2026-07-24-knip-cleanup-design.md @@ -0,0 +1,107 @@ +# Knip Cleanup Design + +## Goal + +Remove unreachable frontend files, unused code, and unused package dependencies +from the OpenHuman app without changing user-visible behavior or deleting +framework, test, desktop, or tooling entry points that are loaded indirectly. + +## Scope + +This cleanup is limited to the `openhuman` repository and the +`openhuman-app` workspace under `app/`. Rust code, vendored repositories, +generated assets, other umbrella submodules, and unrelated refactors are out of +scope. + +The initial `pnpm knip` baseline reports: + +- 34 unused files +- 2 unused dependencies +- 10 unlisted dependencies +- 239 unused value exports +- additional unused exported types and enum members +- stale or redundant entries in `app/knip.json` + +These findings are candidates, not deletion instructions. Knip cannot always +see Tauri, Vite, Vitest, WebdriverIO, package-script, or convention-based entry +points. + +## Approach + +### 1. Make the analysis trustworthy + +Update `app/knip.json` and package declarations so Knip knows about every real +application, test, desktop, and tooling entry point. Replace transitive +dependency reliance with direct declarations where source files import a +package. Remove stale ignores and patterns only when Knip can discover the +corresponding usage without them. + +Configuration changes must be narrow. Broad ignore globs added solely to +silence findings are not acceptable. + +### 2. Verify candidates independently + +For each reported file or symbol: + +1. Search the repository for static references, re-exports, scripts, generated + manifests, test configuration, and string-based loading. +2. Check whether a framework or desktop runtime discovers it by convention. +3. Classify it as removable, internally used but over-exported, or deliberately + public. + +Files are deleted only when they have no runtime, build, test, documentation, or +tooling role. Used declarations with unnecessary exports become module-private +instead of being deleted. + +### 3. Remove dead surface in small slices + +Apply the cleanup in independently verifiable groups: + +1. Knip configuration and dependency declaration corrections. +2. Unreachable files and dependencies. +3. Unused value exports and local declarations. +4. Unused exported types and enum members where removal is source-compatible + inside the repository. + +Each validated group is committed separately with `atomic-commit`, listing +every touched file explicitly. + +### 4. Treat public contracts conservatively + +Exports used only by tests may remain exported when they are intentional test +seams. Package-facing APIs, Tauri integration boundaries, generated-code +inputs, and documented extension points remain unless repository evidence +shows they are private and unused. + +Any intentional finding that cannot be modeled as an entry point will receive +the narrowest possible Knip exception and a short reason. + +## Validation + +After each cleanup group: + +- run Knip and confirm the targeted findings disappear without new findings; +- run TypeScript compilation; +- run affected Vitest tests; +- run lint and formatting checks for touched files. + +Before completion: + +- `pnpm knip` +- `pnpm typecheck` +- `pnpm test` +- `pnpm lint` +- `pnpm format:check` +- `pnpm build` + +The cleanup is complete when Knip has no unexplained actionable findings, all +quality gates pass, and the diff contains no behavior changes beyond removal of +unreachable code and unnecessary export visibility. + +## Non-goals + +- Redesigning features or module boundaries +- Changing UI behavior +- Removing Rust code +- Updating unrelated dependencies +- Publishing branches or opening pull requests diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39b69ad82..44f270213 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,9 +96,6 @@ importers: katex: specifier: ^0.16.47 version: 0.16.47 - lottie-react: - specifier: ^2.4.1 - version: 2.4.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) os-browserify: specifier: ^0.3.0 version: 0.3.0 @@ -117,9 +114,6 @@ importers: react-dom: specifier: ^19.1.0 version: 19.2.5(react@19.2.5) - react-ga4: - specifier: ^3.0.1 - version: 3.0.1 react-icons: specifier: ^5.6.0 version: 5.6.0(react@19.2.5) @@ -241,6 +235,9 @@ importers: '@wdio/cli': specifier: ^9.24.0 version: 9.27.0(@types/node@25.6.0)(expect-webdriverio@5.6.5) + '@wdio/globals': + specifier: ^9.24.0 + version: 9.27.0(expect-webdriverio@5.6.5)(webdriverio@9.27.0) '@wdio/local-runner': specifier: ^9.24.0 version: 9.27.0(@wdio/globals@9.27.0)(webdriverio@9.27.0) @@ -250,6 +247,9 @@ importers: '@wdio/spec-reporter': specifier: ^9.24.0 version: 9.27.0 + '@wdio/types': + specifier: ^9.24.0 + version: 9.27.0 autoprefixer: specifier: ^10.4.23 version: 10.5.0(postcss@8.5.10) @@ -307,6 +307,9 @@ importers: vitest: specifier: ^4.0.18 version: 4.1.5(@types/node@25.6.0)(@vitest/coverage-v8@4.1.5)(jsdom@28.1.0(@noble/hashes@2.2.0))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)(jiti@1.21.7)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.3)) + webdriverio: + specifier: ^9.24.0 + version: 9.27.0 packages/tauri-plugin-ptt: devDependencies: @@ -4270,15 +4273,6 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - lottie-react@2.4.1: - resolution: {integrity: sha512-LQrH7jlkigIIv++wIyrOYFLHSKQpEY4zehPicL9bQsrt1rnoKRYCYgpCUe5maqylNtacy58/sQDZTkwMcTRxZw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - lottie-web@5.13.0: - resolution: {integrity: sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ==} - lowlight@3.3.0: resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==} @@ -4954,9 +4948,6 @@ packages: peerDependencies: react: ^19.2.5 - react-ga4@3.0.1: - resolution: {integrity: sha512-GyCc01bSheWXjzGDyHsXMOqk/SP5Cf/JrcJTg4hcpKx4eeSwaJKpJUc+ipF4ffLTZkmabmf3ZGBv4OKHTXNXyA==} - react-icons@5.6.0: resolution: {integrity: sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==} peerDependencies: @@ -10311,14 +10302,6 @@ snapshots: dependencies: js-tokens: 4.0.0 - lottie-react@2.4.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5): - dependencies: - lottie-web: 5.13.0 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - - lottie-web@5.13.0: {} - lowlight@3.3.0: dependencies: '@types/hast': 3.0.4 @@ -11324,8 +11307,6 @@ snapshots: react: 19.2.5 scheduler: 0.27.0 - react-ga4@3.0.1: {} - react-icons@5.6.0(react@19.2.5): dependencies: react: 19.2.5