diff --git a/packages/shared/src/analytics-events.ts b/packages/shared/src/analytics-events.ts index 67eb146a7e..7a26c7408c 100644 --- a/packages/shared/src/analytics-events.ts +++ b/packages/shared/src/analytics-events.ts @@ -831,6 +831,7 @@ export type ChannelsSurface = | "sidebar" | "command_menu" | "new_task" + | "task_input" | "channel_home" | "channel_history" | "channel_artifacts" diff --git a/packages/ui/src/features/canvas/hooks/useTaskChannels.ts b/packages/ui/src/features/canvas/hooks/useTaskChannels.ts index 944dab81de..f0a3068673 100644 --- a/packages/ui/src/features/canvas/hooks/useTaskChannels.ts +++ b/packages/ui/src/features/canvas/hooks/useTaskChannels.ts @@ -20,7 +20,7 @@ export function normalizeChannelName(name: string): string { * folder "channels" stay on the desktop file system for CONTEXT.md and * artifacts). Listing also lazily provisions the requester's #me channel. */ -export function useTaskChannels(): { +export function useTaskChannels(options?: { enabled?: boolean }): { channels: TaskChannel[]; personalChannel: TaskChannel | undefined; isLoading: boolean; @@ -28,7 +28,10 @@ export function useTaskChannels(): { const query = useAuthenticatedQuery( TASK_CHANNELS_QUERY_KEY, (client) => client.getTaskChannels(), - { refetchInterval: TASK_CHANNELS_POLL_INTERVAL_MS }, + { + enabled: options?.enabled ?? true, + refetchInterval: TASK_CHANNELS_POLL_INTERVAL_MS, + }, ); const channels = useMemo(() => query.data ?? [], [query.data]); const personalChannel = useMemo( diff --git a/packages/ui/src/features/task-detail/hooks/useTaskCreation.ts b/packages/ui/src/features/task-detail/hooks/useTaskCreation.ts index 91acc634e1..36c8024c1f 100644 --- a/packages/ui/src/features/task-detail/hooks/useTaskCreation.ts +++ b/packages/ui/src/features/task-detail/hooks/useTaskCreation.ts @@ -13,10 +13,13 @@ import { useHostTRPC, useHostTRPCClient } from "@posthog/host-router/react"; import { type Adapter, ANALYTICS_EVENTS, + PROJECT_BLUEBIRD_FLAG, type TaskCreationInput, type WorkspaceMode, } from "@posthog/shared"; import type { ExecutionMode, Task } from "@posthog/shared/domain-types"; +import { useTaskChannels } from "@posthog/ui/features/canvas/hooks/useTaskChannels"; +import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag"; import { useTaskInputPrefillStore } from "@posthog/ui/features/task-detail/stores/taskInputPrefillStore"; import { navigateToTaskPending } from "@posthog/ui/router/navigationBridge"; import { openTask, openTaskInput } from "@posthog/ui/router/useOpenTask"; @@ -201,6 +204,17 @@ export function useTaskCreation({ // Used to name the task occupying a branch's worktree when reuse is blocked. const { data: tasks } = useTasks(); + // Tasks created without a channel default into the user's private #me + // backend channel so they still surface in the Channels space instead of + // staying unfiled. The personal channel is per-user and provisioned lazily + // server-side on first list, so this can't collide across teammates. If it + // hasn't loaded yet the task is created unfiled, as before. + const bluebirdEnabled = useFeatureFlag( + PROJECT_BLUEBIRD_FLAG, + import.meta.env.DEV, + ); + const { personalChannel } = useTaskChannels({ enabled: bluebirdEnabled }); + const hasRequiredPath = allowNoRepo ? true : workspaceMode === "cloud" @@ -307,6 +321,11 @@ export function useTaskCreation({ } const settings = useSettingsStore.getState(); + const defaultedChannelId = + bluebirdEnabled && !channelId && !channelName + ? personalChannel?.id + : undefined; + const input = prepareTaskInput(serializedContent, filePaths, { // In channels chat-box mode no repo is attached up front, even if a // directory/repo is lingering in the persisted picker state. @@ -329,7 +348,7 @@ export function useTaskCreation({ additionalDirectories, channelContext, channelName, - channelId, + channelId: channelId ?? defaultedChannelId, customInstructions: getEffectiveCustomInstructions(settings), autoPublishCloudRuns: settings.autoPublishCloudRuns, rtkEnabledCloud: settings.rtkEnabledCloud, @@ -376,6 +395,15 @@ export function useTaskCreation({ if (!pendingTaskKey && !contentOverride) { editor.clear(); } + if (defaultedChannelId) { + track(ANALYTICS_EVENTS.CHANNEL_ACTION, { + action_type: "file_task", + surface: "task_input", + channel_id: defaultedChannelId, + task_id: output.task.id, + success: true, + }); + } onTaskCreatedEffect?.(output.task); if (onTaskCreated) { onTaskCreated(output.task); @@ -493,6 +521,8 @@ export function useTaskCreation({ channelName, channelId, allowNoRepo, + bluebirdEnabled, + personalChannel?.id, clearTaskInputReportAssociation, invalidateTasks, onTaskCreated,