fix: disable tool approve for old claude code version

This commit is contained in:
d-kimsuon
2025-10-14 12:18:29 +09:00
parent 7c05168e4e
commit 8d592ce89b
6 changed files with 66 additions and 93 deletions

View File

@@ -32,7 +32,13 @@ export const useNewChatMutation = (
},
onSuccess: async (response) => {
onSuccess?.();
router.push(`/projects/${projectId}/sessions/${response.sessionId}`);
router.push(
`/projects/${projectId}/sessions/${response.sessionId}` +
response.userMessageId !==
undefined
? `#message-${response.userMessageId}`
: "",
);
},
});
};

View File

@@ -4,9 +4,9 @@ import { zValidator } from "@hono/zod-validator";
import { setCookie } from "hono/cookie";
import { streamSSE } from "hono/streaming";
import { z } from "zod";
import { configSchema } from "../config/config";
import { type Config, configSchema } from "../config/config";
import { env } from "../lib/env";
import { claudeCodeTaskController } from "../service/claude-code/ClaudeCodeTaskController";
import { ClaudeCodeTaskController } from "../service/claude-code/ClaudeCodeTaskController";
import type { SerializableAliveTask } from "../service/claude-code/types";
import { adaptInternalEventToSSE } from "../service/events/adaptInternalEventToSSE";
import { eventBus } from "../service/events/EventBus";
@@ -28,11 +28,15 @@ export const routes = async (app: HonoAppType) => {
const sessionRepository = new SessionRepository();
const projectRepository = new ProjectRepository();
const fileWatcher = getFileWatcher();
const eventBus = getEventBus();
if (env.get("NEXT_PHASE") !== "phase-production-build") {
await initialize({
sessionRepository,
projectRepository,
});
fileWatcher.startWatching();
setInterval(() => {
eventBus.emit("heartbeat", {});
}, 10 * 1000);
}
return (

View File

@@ -23,17 +23,13 @@ export class ClaudeCodeExecutor {
);
}
public get version() {
return this.claudeCodeVersion?.version;
}
public get availableFeatures() {
public get features() {
return {
canUseTool:
enableToolApproval:
this.claudeCodeVersion?.greaterThanOrEqual(
new ClaudeCodeVersion({ major: 1, minor: 0, patch: 82 }),
) ?? false,
uuidOnSDKMessage:
extractUuidFromSDKMessage:
this.claudeCodeVersion?.greaterThanOrEqual(
new ClaudeCodeVersion({ major: 1, minor: 0, patch: 86 }),
) ?? false,
@@ -41,18 +37,14 @@ export class ClaudeCodeExecutor {
}
public query(prompt: CCQueryPrompt, options: CCQueryOptions) {
const { canUseTool, permissionMode, ...baseOptions } = options;
const { canUseTool, ...baseOptions } = options;
return query({
prompt,
options: {
pathToClaudeCodeExecutable: this.pathToClaudeCodeExecutable,
...baseOptions,
...(this.availableFeatures.canUseTool
? { canUseTool, permissionMode }
: {
permissionMode: "bypassPermissions",
}),
...(this.features.enableToolApproval ? { canUseTool } : {}),
},
});
}

View File

@@ -1,10 +1,7 @@
import { resolve } from "node:path";
import prexit from "prexit";
import { ulid } from "ulid";
import type { Config } from "../../config/config";
import { eventBus } from "../events/EventBus";
import { parseCommandXml } from "../parseCommandXml";
import { decodeProjectId } from "../project/id";
import { predictSessionsDatabase } from "../session/PredictSessionsDatabase";
import { getEventBus, type IEventBus } from "../events/EventBus";
import { ClaudeCodeExecutor } from "./ClaudeCodeExecutor";
import { createMessageGenerator } from "./createMessageGenerator";
import type {
@@ -16,21 +13,23 @@ import type {
RunningClaudeCodeTask,
} from "./types";
class ClaudeCodeTaskController {
export class ClaudeCodeTaskController {
private claudeCode: ClaudeCodeExecutor;
private tasks: ClaudeCodeTask[] = [];
private config: Config;
private pendingPermissionRequests: Map<string, PermissionRequest> = new Map();
private permissionResponses: Map<string, PermissionResponse> = new Map();
constructor() {
constructor(config: Config) {
this.claudeCode = new ClaudeCodeExecutor();
this.config = {
hideNoUserMessageSession: false,
unifySameTitleSession: false,
enterKeyBehavior: "shift-enter-send",
permissionMode: "default",
};
this.eventBus = getEventBus();
this.config = config;
prexit(() => {
this.aliveTasks.forEach((task) => {
task.abortController.abort();
});
});
}
public updateConfig(config: Config) {
@@ -170,20 +169,9 @@ class ClaudeCodeTaskController {
);
if (existingTask) {
console.log(
`Alive task for session(id=${currentSession.sessionId}) continued.`,
);
const result = await this.continueTask(existingTask, message);
return result;
} else {
if (currentSession.sessionId === undefined) {
console.log(`New task started.`);
} else {
console.log(
`New task started for existing session(id=${currentSession.sessionId}).`,
);
}
const result = await this.startTask(currentSession, message);
return result;
}
@@ -265,41 +253,29 @@ class ClaudeCodeTaskController {
});
}
if (
message.type === "system" &&
message.subtype === "init" &&
currentSession.sessionId === undefined
) {
// because it takes time for the Claude Code file to be updated, simulate the message
predictSessionsDatabase.createPredictSession({
id: message.session_id,
jsonlFilePath: resolve(
decodeProjectId(currentSession.projectId),
`${message.session_id}.jsonl`,
),
conversations: [
{
type: "user",
message: {
role: "user",
content: userMessage,
},
isSidechain: false,
userType: "external",
cwd: message.cwd,
sessionId: message.session_id,
version: this.claudeCode.version?.toString() ?? "unknown",
uuid: message.uuid,
timestamp: new Date().toISOString(),
parentUuid: null,
},
],
meta: {
firstCommand: parseCommandXml(userMessage),
messageCount: 0,
},
lastModifiedAt: new Date(),
});
// 初回の system message だとまだ history ファイルが作成されていないので
if (message.type === "user" || message.type === "assistant") {
// 本来は message.uuid の存在チェックをしたいが、古いバージョンでは存在しないことがある
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;
}
eventBus.emit("sessionListChanged", {
projectId: task.projectId,
@@ -415,12 +391,6 @@ class ClaudeCodeTaskController {
});
}
public abortAllTasks() {
for (const task of this.aliveTasks) {
task.abortController.abort();
}
}
private upsertExistingTask(task: ClaudeCodeTask) {
const target = this.tasks.find((t) => t.id === task.id);
@@ -431,10 +401,12 @@ class ClaudeCodeTaskController {
Object.assign(target, task);
}
eventBus.emit("taskChanged", {
aliveTasks: this.aliveTasks,
changed: task,
});
if (task.status === "paused" || task.status === "running") {
this.eventBus.emit("taskChanged", {
aliveTasks: this.aliveTasks,
changed: task,
});
}
}
}

View File

@@ -43,10 +43,6 @@ export class ClaudeCodeVersion {
return this.version.patch;
}
public toString() {
return `${this.major}.${this.minor}.${this.patch}`;
}
public equals(other: ClaudeCodeVersion) {
return (
this.version.major === other.version.major &&

View File

@@ -20,18 +20,21 @@ export type PendingClaudeCodeTask = BaseClaudeCodeTask & {
export type RunningClaudeCodeTask = BaseClaudeCodeTask & {
status: "running";
sessionId: string;
userMessageId: string | undefined;
abortController: AbortController;
};
export type PausedClaudeCodeTask = BaseClaudeCodeTask & {
status: "paused";
sessionId: string;
userMessageId: string | undefined;
abortController: AbortController;
};
type CompletedClaudeCodeTask = BaseClaudeCodeTask & {
status: "completed";
sessionId: string;
userMessageId: string | undefined;
abortController: AbortController;
resolveFirstMessage: () => void;
};