Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions frontend/src/hooks/useConversationHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export const useConversationHistory = ({
const getLiveExecutionProcess = (
executionProcessId: string
): ExecutionProcess | undefined => {
return executionProcesses?.current.find(
return executionProcesses.current?.find(
(executionProcess) => executionProcess.id === executionProcessId
);
};
Expand Down Expand Up @@ -219,15 +219,15 @@ export const useConversationHistory = ({
};

const getActiveAgentProcess = (): ExecutionProcess | null => {
const activeProcesses = executionProcesses?.current.filter(
const activeProcesses = executionProcesses.current?.filter(
(p) =>
p.status === ExecutionProcessStatus.running &&
p.run_reason !== 'devserver'
);
) ?? [];
if (activeProcesses.length > 1) {
console.error('More than one active execution process found');
}
return activeProcesses[0] || null;
return activeProcesses[0] ?? null;
};

const flattenEntries = (
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/hooks/useDiffStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ export const useDiffStream = (

const diffs = useMemo(() => {
return Object.values(data?.entries ?? {})
.filter((entry) => entry?.type === 'DIFF')
.filter((entry): entry is PatchType & { type: 'DIFF'; content: Diff } =>
entry.type === 'DIFF' && entry.content != null
)
.map((entry) => entry.content);
}, [data?.entries]);

Expand Down
29 changes: 17 additions & 12 deletions frontend/src/hooks/useTaskMutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ import type {
UpdateTask,
} from 'shared/types';

// Helper to safely extract executor string from various object shapes
const getExecutorString = (obj: unknown): string => {
if (!obj || typeof obj !== 'object') return 'unknown';
const record = obj as Record<string, unknown>;
if (typeof record.executor === 'string') return record.executor;
Comment on lines +19 to +23

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Narrow executor type to satisfy analytics events

The new getExecutorString helper returns a plain string, but every analytics call here (trackTaskCreated, trackTaskCompleted, checkAndTrackFirstSuccess) still expects an ExecutorType literal union (frontend/src/types/analytics.ts). With strict mode enabled in frontend/tsconfig.json, the widened return type causes TypeScript to reject these calls, so the build will now fail before runtime. The helper should return/validate to ExecutorType (or cast) to keep the analytics payloads type-safe and compilable.

Useful? React with 👍 / 👎.

Comment on lines +19 to +23

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge getExecutorString returns plain string breaking ExecutorType checks

The new helper returns a generic string, but the analytics events you pass it into (trackTaskCreated and trackTaskCompleted) require executor to be the ExecutorType literal union defined in frontend/src/types/analytics.ts. With this change the call sites now infer executor as string, so a TypeScript type-check (or ESLint with type information) will fail with Type 'string' is not assignable to type 'ExecutorType', blocking the build. Please type the helper to return ExecutorType (or cast its result) so the calls satisfy the expected union.

Useful? React with 👍 / 👎.

if (record.executor_profile && typeof record.executor_profile === 'object') {
const profile = record.executor_profile as Record<string, unknown>;
if (typeof profile.executor === 'string') return profile.executor;
}
return 'unknown';
};

export function useTaskMutations(projectId?: string) {
const queryClient = useQueryClient();
const navigate = useNavigateWithSearch();
Expand All @@ -41,12 +53,9 @@ export function useTaskMutations(projectId?: string) {
// Track task creation with analytics
// Note: executor info is not in CreateTask, will be tracked from config
trackTaskCreated({
executor: ((
createdTask as Task & { executor_profile?: { executor?: string } }
).executor_profile?.executor ||
'unknown') as import('@/types/analytics').ExecutorType,
executor: getExecutorString(createdTask),
has_description: !!variables.description,
prompt_length: variables.description?.length || 0,
prompt_length: variables.description?.length ?? 0,
is_subtask: !!variables.parent_task_attempt,
});

Expand All @@ -69,9 +78,9 @@ export function useTaskMutations(projectId?: string) {
) => {
// Track task creation with analytics
trackTaskCreated({
executor: variables.executor_profile_id.executor || 'unknown',
executor: getExecutorString(variables.executor_profile_id),
has_description: !!variables.task.description,
prompt_length: variables.task.description?.length || 0,
prompt_length: variables.task.description?.length ?? 0,
is_subtask: !!variables.task.parent_task_attempt,
});

Expand Down Expand Up @@ -104,11 +113,7 @@ export function useTaskMutations(projectId?: string) {
try {
const attempts = await attemptsApi.getAll(updatedTask.id);
const latestAttempt = attempts[0]; // Sorted newest first
const executorStr =
(latestAttempt as { executor?: string } | undefined)?.executor ||
'unknown';
const executor =
executorStr as import('@/types/analytics').ExecutorType;
const executor = getExecutorString(latestAttempt);
const attemptCount = attempts.length;

trackTaskCompleted({
Expand Down