diff --git a/src/app/projects/[projectId]/components/newChat/NewChat.tsx b/src/app/projects/[projectId]/components/newChat/NewChat.tsx
index a81f944..3e615a2 100644
--- a/src/app/projects/[projectId]/components/newChat/NewChat.tsx
+++ b/src/app/projects/[projectId]/components/newChat/NewChat.tsx
@@ -120,7 +120,7 @@ export const NewChat: FC<{
{startNewChat.isPending ? (
<>
- Starting... This may take a while.
+ Sending... This may take a while.
>
) : (
<>
diff --git a/src/app/projects/[projectId]/sessions/[sessionId]/components/SessionPageContent.tsx b/src/app/projects/[projectId]/sessions/[sessionId]/components/SessionPageContent.tsx
index da68545..8fd7b59 100644
--- a/src/app/projects/[projectId]/sessions/[sessionId]/components/SessionPageContent.tsx
+++ b/src/app/projects/[projectId]/sessions/[sessionId]/components/SessionPageContent.tsx
@@ -45,7 +45,10 @@ export const SessionPageContent: FC<{
// 自動スクロール処理
useEffect(() => {
- if (isRunningTask && conversations.length !== previousConversationLength) {
+ if (
+ (isRunningTask || isPausedTask) &&
+ conversations.length !== previousConversationLength
+ ) {
setPreviousConversationLength(conversations.length);
const scrollContainer = scrollContainerRef.current;
if (scrollContainer) {
@@ -55,7 +58,7 @@ export const SessionPageContent: FC<{
});
}
}
- }, [conversations, isRunningTask, previousConversationLength]);
+ }, [conversations, isRunningTask, isPausedTask, previousConversationLength]);
return (
@@ -134,7 +137,7 @@ export const SessionPageContent: FC<{
-
+
{
- setMessage("");
if (sessionId !== response.sessionId) {
router.push(
`/projects/${projectId}/sessions/${response.sessionId}#message-${response.userMessageId}`,
);
}
+
+ setMessage("");
},
});
@@ -122,7 +123,7 @@ export const ResumeChat: FC<{
{resumeChat.isPending ? (
<>
- Starting... This may take a while.
+ Sending... This may take a while.
>
) : isPausedTask || isRunningTask ? (
<>
diff --git a/src/server/service/claude-code/ClaudeCodeTaskController.ts b/src/server/service/claude-code/ClaudeCodeTaskController.ts
index a6951c1..1591566 100644
--- a/src/server/service/claude-code/ClaudeCodeTaskController.ts
+++ b/src/server/service/claude-code/ClaudeCodeTaskController.ts
@@ -1,7 +1,7 @@
import { execSync } from "node:child_process";
import { query } from "@anthropic-ai/claude-code";
-import { ulid } from "ulid";
import prexit from "prexit";
+import { ulid } from "ulid";
import { type EventBus, getEventBus } from "../events/EventBus";
import { createMessageGenerator } from "./createMessageGenerator";
import type {
@@ -48,14 +48,15 @@ export class ClaudeCodeTaskController {
);
if (existingTask) {
- return this.continueTask(existingTask, message);
+ return await this.continueTask(existingTask, message);
} else {
return await this.startTask(currentSession, message);
}
}
- private continueTask(task: AliveClaudeCodeTask, message: string) {
+ private async continueTask(task: AliveClaudeCodeTask, message: string) {
task.setNextMessage(message);
+ await task.awaitFirstMessage();
return task;
}
@@ -67,8 +68,13 @@ export class ClaudeCodeTaskController {
},
message: string,
) {
- const { generateMessages, setNextMessage } =
- createMessageGenerator(message);
+ const {
+ generateMessages,
+ setNextMessage,
+ setFirstMessagePromise,
+ resolveFirstMessage,
+ awaitFirstMessage,
+ } = createMessageGenerator(message);
const task: PendingClaudeCodeTask = {
status: "pending",
@@ -78,6 +84,9 @@ export class ClaudeCodeTaskController {
cwd: currentSession.cwd,
generateMessages,
setNextMessage,
+ setFirstMessagePromise,
+ resolveFirstMessage,
+ awaitFirstMessage,
onMessageHandlers: [],
};
@@ -120,25 +129,31 @@ export class ClaudeCodeTaskController {
// 初回の system message だとまだ history ファイルが作成されていないので
if (
- !resolved &&
(message.type === "user" || message.type === "assistant") &&
message.uuid !== undefined
) {
- const runningTask: RunningClaudeCodeTask = {
- status: "running",
- id: task.id,
- projectId: task.projectId,
- cwd: task.cwd,
- generateMessages: task.generateMessages,
- setNextMessage: task.setNextMessage,
- onMessageHandlers: task.onMessageHandlers,
- userMessageId: message.uuid,
- sessionId: message.session_id,
- abortController: abortController,
- };
- this.tasks.push(runningTask);
- aliveTaskResolve(runningTask);
- resolved = true;
+ if (!resolved) {
+ const runningTask: RunningClaudeCodeTask = {
+ status: "running",
+ id: task.id,
+ projectId: task.projectId,
+ cwd: task.cwd,
+ generateMessages: task.generateMessages,
+ setNextMessage: task.setNextMessage,
+ resolveFirstMessage: task.resolveFirstMessage,
+ setFirstMessagePromise: task.setFirstMessagePromise,
+ awaitFirstMessage: task.awaitFirstMessage,
+ onMessageHandlers: task.onMessageHandlers,
+ userMessageId: message.uuid,
+ sessionId: message.session_id,
+ abortController: abortController,
+ };
+ this.tasks.push(runningTask);
+ aliveTaskResolve(runningTask);
+ resolved = true;
+ }
+
+ resolveFirstMessage();
}
await Promise.all(
@@ -152,6 +167,8 @@ export class ClaudeCodeTaskController {
...currentTask,
status: "paused",
});
+ resolved = true;
+ setFirstMessagePromise();
}
}
@@ -204,6 +221,9 @@ export class ClaudeCodeTaskController {
cwd: task.cwd,
generateMessages: task.generateMessages,
setNextMessage: task.setNextMessage,
+ resolveFirstMessage: task.resolveFirstMessage,
+ setFirstMessagePromise: task.setFirstMessagePromise,
+ awaitFirstMessage: task.awaitFirstMessage,
onMessageHandlers: task.onMessageHandlers,
baseSessionId: task.baseSessionId,
userMessageId: task.userMessageId,
diff --git a/src/server/service/claude-code/createMessageGenerator.ts b/src/server/service/claude-code/createMessageGenerator.ts
index 1e86f96..2534178 100644
--- a/src/server/service/claude-code/createMessageGenerator.ts
+++ b/src/server/service/claude-code/createMessageGenerator.ts
@@ -8,11 +8,11 @@ export type MessageGenerator = () => AsyncGenerator<
unknown
>;
-const createPromise = () => {
- let promiseResolve: ((value: string) => void) | undefined;
+const createPromise = () => {
+ let promiseResolve: ((value: T) => void) | undefined;
let promiseReject: ((reason?: unknown) => void) | undefined;
- const promise = new Promise((resolve, reject) => {
+ const promise = new Promise((resolve, reject) => {
promiseResolve = resolve;
promiseReject = reject;
});
@@ -33,8 +33,12 @@ export const createMessageGenerator = (
): {
generateMessages: MessageGenerator;
setNextMessage: (message: string) => void;
+ setFirstMessagePromise: () => void;
+ resolveFirstMessage: () => void;
+ awaitFirstMessage: () => Promise;
} => {
- let currentPromise = createPromise();
+ let sendMessagePromise = createPromise();
+ let receivedFirstMessagePromise = createPromise();
const createMessage = (message: string): SDKUserMessage => {
return {
@@ -50,19 +54,34 @@ export const createMessageGenerator = (
yield createMessage(firstMessage);
while (true) {
- const message = await currentPromise.promise;
- currentPromise = createPromise();
+ const message = await sendMessagePromise.promise;
+ sendMessagePromise = createPromise();
yield createMessage(message);
}
}
const setNextMessage = (message: string) => {
- currentPromise.resolve(message);
+ sendMessagePromise.resolve(message);
+ };
+
+ const setFirstMessagePromise = () => {
+ receivedFirstMessagePromise = createPromise();
+ };
+
+ const resolveFirstMessage = () => {
+ receivedFirstMessagePromise.resolve(undefined);
+ };
+
+ const awaitFirstMessage = async () => {
+ await receivedFirstMessagePromise.promise;
};
return {
generateMessages,
setNextMessage,
+ setFirstMessagePromise,
+ resolveFirstMessage,
+ awaitFirstMessage,
};
};
diff --git a/src/server/service/claude-code/types.ts b/src/server/service/claude-code/types.ts
index 12e2908..b43535d 100644
--- a/src/server/service/claude-code/types.ts
+++ b/src/server/service/claude-code/types.ts
@@ -7,6 +7,9 @@ type BaseClaudeCodeTask = {
cwd: string;
generateMessages: MessageGenerator;
setNextMessage: (message: string) => void;
+ resolveFirstMessage: () => void;
+ setFirstMessagePromise: () => void;
+ awaitFirstMessage: () => Promise;
onMessageHandlers: OnMessage[];
};
@@ -33,6 +36,7 @@ type CompletedClaudeCodeTask = BaseClaudeCodeTask & {
sessionId: string;
userMessageId: string;
abortController: AbortController;
+ resolveFirstMessage: () => void;
};
type FailedClaudeCodeTask = BaseClaudeCodeTask & {