Skip to content

Commit b60effb

Browse files
committed
Address review comments
1 parent 60598e7 commit b60effb

File tree

14 files changed

+132
-46
lines changed

14 files changed

+132
-46
lines changed

packages/shared/src/tasks/api.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ export interface InitResponse {
2424
tasksSupported: boolean;
2525
}
2626

27+
export interface TaskIdParams {
28+
taskId: string;
29+
}
30+
2731
const init = defineRequest<void, InitResponse>("init");
2832
const getTasks = defineRequest<void, Task[]>("getTasks");
2933
const getTemplates = defineRequest<void, TaskTemplate[]>("getTemplates");
30-
const getTask = defineRequest<{ taskId: string }, Task>("getTask");
31-
const getTaskDetails = defineRequest<{ taskId: string }, TaskDetails>(
34+
const getTask = defineRequest<TaskIdParams, Task>("getTask");
35+
const getTaskDetails = defineRequest<TaskIdParams, TaskDetails>(
3236
"getTaskDetails",
3337
);
3438

@@ -39,21 +43,19 @@ export interface CreateTaskParams {
3943
}
4044
const createTask = defineRequest<CreateTaskParams, Task>("createTask");
4145

42-
export interface TaskActionParams {
43-
taskId: string;
46+
export interface TaskActionParams extends TaskIdParams {
4447
taskName: string;
4548
}
4649
const deleteTask = defineRequest<TaskActionParams, void>("deleteTask");
4750
const pauseTask = defineRequest<TaskActionParams, void>("pauseTask");
4851
const resumeTask = defineRequest<TaskActionParams, void>("resumeTask");
49-
const downloadLogs = defineRequest<{ taskId: string }, void>("downloadLogs");
50-
const sendTaskMessage = defineRequest<
51-
{ taskId: string; message: string },
52-
void
53-
>("sendTaskMessage");
52+
const downloadLogs = defineRequest<TaskIdParams, void>("downloadLogs");
53+
const sendTaskMessage = defineRequest<TaskIdParams & { message: string }, void>(
54+
"sendTaskMessage",
55+
);
5456

55-
const viewInCoder = defineCommand<{ taskId: string }>("viewInCoder");
56-
const viewLogs = defineCommand<{ taskId: string }>("viewLogs");
57+
const viewInCoder = defineCommand<TaskIdParams>("viewInCoder");
58+
const viewLogs = defineCommand<TaskIdParams>("viewLogs");
5759

5860
const taskUpdated = defineNotification<Task>("taskUpdated");
5961
const tasksUpdated = defineNotification<Task[]>("tasksUpdated");

packages/shared/src/tasks/utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ export function getTaskLabel(task: Task): string {
44
return task.display_name || task.name || task.id;
55
}
66

7+
/** Whether the agent is actively working (status is active and state is working). */
8+
export function isTaskWorking(task: Task): boolean {
9+
return task.status === "active" && task.current_state?.state === "working";
10+
}
11+
712
const PAUSABLE_STATUSES: readonly TaskStatus[] = [
813
"active",
914
"initializing",

packages/tasks/src/App.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,20 @@ export default function App() {
6161
}
6262
}, [data, createOpen, historyOpen]);
6363

64+
function renderHistory() {
65+
if (selectedTask) {
66+
return <TaskDetailView details={selectedTask} onBack={deselectTask} />;
67+
}
68+
if (isLoadingDetails) {
69+
return (
70+
<div className="loading-container">
71+
<VscodeProgressRing />
72+
</div>
73+
);
74+
}
75+
return <TaskList tasks={tasks} onSelectTask={selectTask} />;
76+
}
77+
6478
if (isLoading) {
6579
return (
6680
<div className="loading-container">
@@ -101,15 +115,7 @@ export default function App() {
101115
open={historyOpen}
102116
>
103117
<div ref={historyScrollRef} className="collapsible-content">
104-
{selectedTask ? (
105-
<TaskDetailView details={selectedTask} onBack={deselectTask} />
106-
) : isLoadingDetails ? (
107-
<div className="loading-container">
108-
<VscodeProgressRing />
109-
</div>
110-
) : (
111-
<TaskList tasks={tasks} onSelectTask={selectTask} />
112-
)}
118+
{renderHistory()}
113119
</div>
114120
</VscodeCollapsible>
115121
</div>

packages/tasks/src/components/AgentChatHistory.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ function getEmptyMessage(logsStatus: LogsStatus): string {
7474
return "Logs not available in current task state";
7575
case "error":
7676
return "Failed to load logs";
77-
default:
77+
case "ok":
7878
return "No messages yet";
7979
}
8080
}

packages/tasks/src/components/CreateTaskSection.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export function CreateTaskSection({ templates }: CreateTaskSectionProps) {
2525
const { mutate, isPending, error } = useMutation({
2626
mutationFn: (params: CreateTaskParams) => api.createTask(params),
2727
onSuccess: () => setPrompt(""),
28+
onError: (err) => logger.error("Failed to create task", err),
2829
});
2930

3031
const selectedTemplate = templates.find((t) => t.id === templateId);
@@ -52,7 +53,7 @@ export function CreateTaskSection({ templates }: CreateTaskSectionProps) {
5253
loading={isPending}
5354
actionIcon="send"
5455
actionLabel="Send"
55-
actionDisabled={!canSubmit}
56+
actionEnabled={canSubmit === true}
5657
/>
5758
{error && <div className="create-task-error">{error.message}</div>}
5859
<div className="create-task-options">

packages/tasks/src/components/PromptInput.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface PromptInputProps {
1414
placeholder?: string;
1515
actionIcon: "send" | "debug-pause";
1616
actionLabel: string;
17-
actionDisabled: boolean;
17+
actionEnabled: boolean;
1818
}
1919

2020
export function PromptInput({
@@ -26,7 +26,7 @@ export function PromptInput({
2626
placeholder = "Prompt your AI agent to start a task...",
2727
actionIcon,
2828
actionLabel,
29-
actionDisabled,
29+
actionEnabled,
3030
}: PromptInputProps) {
3131
return (
3232
<div className="prompt-input-container">
@@ -38,7 +38,7 @@ export function PromptInput({
3838
onKeyDown={(e) => {
3939
if (isSubmit(e)) {
4040
e.preventDefault();
41-
if (!actionDisabled) {
41+
if (actionEnabled) {
4242
onSubmit();
4343
}
4444
}
@@ -53,8 +53,8 @@ export function PromptInput({
5353
actionIcon
5454
name={actionIcon}
5555
label={actionLabel}
56-
onClick={() => !actionDisabled && onSubmit()}
57-
className={actionDisabled ? "disabled" : ""}
56+
onClick={() => actionEnabled && onSubmit()}
57+
className={actionEnabled ? "" : "disabled"}
5858
/>
5959
)}
6060
</div>

packages/tasks/src/components/TaskDetailView.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import { isTaskWorking, type TaskDetails } from "@repo/shared";
2+
13
import { AgentChatHistory } from "./AgentChatHistory";
24
import { ErrorBanner } from "./ErrorBanner";
35
import { TaskDetailHeader } from "./TaskDetailHeader";
46
import { TaskMessageInput } from "./TaskMessageInput";
57

6-
import type { TaskDetails } from "@repo/shared";
7-
88
interface TaskDetailViewProps {
99
details: TaskDetails;
1010
onBack: () => void;
@@ -13,8 +13,7 @@ interface TaskDetailViewProps {
1313
export function TaskDetailView({ details, onBack }: TaskDetailViewProps) {
1414
const { task, logs, logsStatus } = details;
1515

16-
const isThinking =
17-
task.status === "active" && task.current_state?.state === "working";
16+
const isThinking = isTaskWorking(task);
1817

1918
return (
2019
<div className="task-detail-view">

packages/tasks/src/components/TaskMessageInput.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { getTaskLabel, type Task, getTaskPermissions } from "@repo/shared";
1+
import {
2+
getTaskLabel,
3+
isTaskWorking,
4+
type Task,
5+
getTaskPermissions,
6+
} from "@repo/shared";
7+
import { logger } from "@repo/webview-shared/logger";
28
import { useMutation } from "@tanstack/react-query";
39
import { useState } from "react";
410

@@ -42,17 +48,19 @@ export function TaskMessageInput({ task }: TaskMessageInputProps) {
4248

4349
const { canPause, canSendMessage } = getTaskPermissions(task);
4450
const placeholder = getPlaceholder(task);
45-
const showPauseButton = task.current_state?.state === "working" && canPause;
51+
const showPauseButton = isTaskWorking(task) && canPause;
4652
const canSubmitMessage = canSendMessage && message.trim().length > 0;
4753

4854
const { mutate: pauseTask, isPending: isPausing } = useMutation({
4955
mutationFn: () =>
5056
api.pauseTask({ taskId: task.id, taskName: getTaskLabel(task) }),
57+
onError: (err) => logger.error("Failed to pause task", err),
5158
});
5259

5360
const { mutate: sendMessage, isPending: isSending } = useMutation({
5461
mutationFn: (msg: string) => api.sendTaskMessage(task.id, msg),
5562
onSuccess: () => setMessage(""),
63+
onError: (err) => logger.error("Failed to send message", err),
5664
});
5765

5866
return (
@@ -65,7 +73,7 @@ export function TaskMessageInput({ task }: TaskMessageInputProps) {
6573
loading={showPauseButton ? isPausing : isSending}
6674
actionIcon={showPauseButton ? "debug-pause" : "send"}
6775
actionLabel={showPauseButton ? "Pause task" : "Send message"}
68-
actionDisabled={showPauseButton ? false : !canSubmitMessage}
76+
actionEnabled={showPauseButton ? true : canSubmitMessage}
6977
/>
7078
);
7179
}

packages/tasks/src/hooks/useTasksApi.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,23 @@ import {
1414
type CreateTaskParams,
1515
type TaskActionParams,
1616
} from "@repo/shared";
17+
import { logger } from "@repo/webview-shared/logger";
1718
import { useIpc } from "@repo/webview-shared/react";
1819

1920
export function useTasksApi() {
2021
const { request, command } = useIpc();
2122

23+
function safeCommand<P>(
24+
definition: { method: string; _types?: { params: P } },
25+
...args: P extends void ? [] : [params: P]
26+
): void {
27+
try {
28+
command(definition, ...args);
29+
} catch (err) {
30+
logger.error(`Command ${definition.method} failed`, err);
31+
}
32+
}
33+
2234
return {
2335
// Requests
2436
init: () => request(TasksApi.init),
@@ -41,7 +53,8 @@ export function useTasksApi() {
4153
request(TasksApi.sendTaskMessage, { taskId, message }),
4254

4355
// Commands
44-
viewInCoder: (taskId: string) => command(TasksApi.viewInCoder, { taskId }),
45-
viewLogs: (taskId: string) => command(TasksApi.viewLogs, { taskId }),
56+
viewInCoder: (taskId: string) =>
57+
safeCommand(TasksApi.viewInCoder, { taskId }),
58+
viewLogs: (taskId: string) => safeCommand(TasksApi.viewLogs, { taskId }),
4659
};
4760
}

src/webviews/tasks/tasksPanel.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
type TaskTemplate,
1919
} from "@repo/shared";
2020

21+
import { errToStr } from "../../api/api-helper";
2122
import { type CoderApi } from "../../api/coderApi";
2223
import { toError } from "../../error/errorUtils";
2324
import { type Logger } from "../../logging/logger";
@@ -383,7 +384,9 @@ export class TasksPanel
383384
isAxiosError(err) &&
384385
(err.response?.status === 409 || err.response?.status === 400)
385386
) {
386-
throw new Error("Task is not ready to receive messages");
387+
throw new Error(
388+
`Task is not ready to receive messages (${errToStr(err)})`,
389+
);
387390
}
388391
throw err;
389392
}

0 commit comments

Comments
 (0)