Skip to content

Commit 736a2eb

Browse files
authored
Merge permission endpoints (#194)
1 parent 43b093e commit 736a2eb

File tree

10 files changed

+27
-67
lines changed

10 files changed

+27
-67
lines changed

app/modules/services/LLMService/Sources/RequestStreamingHelper.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -494,11 +494,7 @@ actor RequestStreamingHelper: Sendable {
494494
toolUseId: toolUsePermissionRequest.toolUseId,
495495
approvalResult: permissionResponse))
496496

497-
if isUsingNewClaudeCodeApi {
498-
_ = try await localServer.postRequest(path: "sendMessage/toolUse/permission/acp", data: data)
499-
} else {
500-
_ = try await localServer.postRequest(path: "sendMessage/toolUse/permission", data: data)
501-
}
497+
_ = try await localServer.postRequest(path: "sendMessage/toolUse/permission/acp", data: data)
502498
} catch {
503499
defaultLogger
504500
.error(

app/modules/services/LLMService/Tests/RequestStreamingHelperTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ struct RequestStreamingHelperReasoningTests {
295295
let localServer = MockLocalServer()
296296
localServer.onPostRequest = { path, data, _ in
297297
#expect(permissionRequested.isFulfilled)
298-
#expect(path == "sendMessage/toolUse/permission")
298+
#expect(path == "sendMessage/toolUse/permission/acp")
299299
#expect(data.jsonString() == """
300300
{
301301
"approvalResult" : {

local-server/build.sha256

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
e37522f77d95fcd2ad2624f9f8a473e832bc98692e9afc51a37bab6fd2be679b
1+
515c0f03d96189063a50cc18d7926239410bbc9a1a5fe2475556195056ac4adc

local-server/build.size

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"development": {
3-
"size": "5.02MB",
4-
"compressedSize": "1.04MB"
3+
"size": "4.85MB",
4+
"compressedSize": "1.01MB"
55
},
66
"production": {
77
"size": "3.69MB",

local-server/src/server/endpoints/sendMessage/acp/clients/ACPClient.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import { UserFacingError } from "@/server/errors"
1515
import { AsyncStream } from "@/utils/asyncStream"
1616
import { ACPToolInput, ACPToolOutput } from "@/server/schemas/toolsSchema"
1717
import { mapToolCallContent } from "./helper"
18-
19-
const pendingToolApprovalRequests = new Map<string, (result: ApprovalResult) => void>()
18+
import { pendingToolApprovalRequests } from "../../pendingToolApprovalRequests"
2019

2120
export interface ACPClient<SessionInitializationParams extends { cwd: string }> {
2221
prompt(

local-server/src/server/endpoints/sendMessage/claudeCode/__tests__/sendMessageToClaudeCode.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const assistantMessageMetadata: Omit<APIAssistantMessage, "content"> = {
3030

3131
describe("sendMessageToClaudeCode", () => {
3232
let sendMessageToClaudeCode: typeof import("../sendMessageToClaudeCode").sendMessageToClaudeCode
33-
let registerEndpoint: typeof import("../sendMessageToClaudeCode").registerEndpoint
33+
let registerEndpoint: typeof import("../../acp/clients/ACPClient").registerEndpoint
3434
let res: MockResponse
3535

3636
let query: MockedQuery
@@ -81,8 +81,8 @@ describe("sendMessageToClaudeCode", () => {
8181
),
8282
}))
8383

84-
const { sendMessageToClaudeCode: sendMessageToClaudeCodeImpl, registerEndpoint: registerEndpointImpl } =
85-
await import("../sendMessageToClaudeCode")
84+
const { sendMessageToClaudeCode: sendMessageToClaudeCodeImpl } = await import("../sendMessageToClaudeCode")
85+
const { registerEndpoint: registerEndpointImpl } = await import("../../acp/clients/ACPClient")
8686
sendMessageToClaudeCode = sendMessageToClaudeCodeImpl
8787
registerEndpoint = registerEndpointImpl
8888
})
@@ -987,8 +987,8 @@ describe("sendMessageToClaudeCode", () => {
987987
body: requestBody,
988988
}
989989

990-
// Find and call the POST handler for /sendMessage/toolUse/permission
991-
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission")
990+
// Find and call the POST handler for /sendMessage/toolUse/permission/acp
991+
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission/acp")
992992
expect(routes).toHaveLength(1)
993993

994994
const postRoute = routes[0]?.route?.stack.find((layer) => layer.method === "post")
@@ -1064,7 +1064,7 @@ describe("sendMessageToClaudeCode", () => {
10641064

10651065
mockRequest = { body: requestBody }
10661066

1067-
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission")
1067+
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission/acp")
10681068
const postRoute = routes[0]?.route?.stack.find((layer) => layer.method === "post")
10691069

10701070
await postRoute!.handle(mockRequest as Request, mockResponse as Response, jest.fn())
@@ -1090,7 +1090,7 @@ describe("sendMessageToClaudeCode", () => {
10901090

10911091
mockRequest = { body: requestBody }
10921092

1093-
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission")
1093+
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission/acp")
10941094
const postRoute = routes[0]?.route?.stack.find((layer) => layer.method === "post")
10951095

10961096
await expect(
@@ -1106,7 +1106,7 @@ describe("sendMessageToClaudeCode", () => {
11061106

11071107
mockRequest = { body: requestBody }
11081108

1109-
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission")
1109+
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission/acp")
11101110
const postRoute = routes[0]?.route?.stack.find((layer) => layer.method === "post")
11111111

11121112
await expect(
@@ -1122,7 +1122,7 @@ describe("sendMessageToClaudeCode", () => {
11221122

11231123
mockRequest = { body: requestBody }
11241124

1125-
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission")
1125+
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission/acp")
11261126
const postRoute = routes[0]?.route?.stack.find((layer) => layer.method === "post")
11271127

11281128
await expect(
@@ -1199,7 +1199,7 @@ describe("sendMessageToClaudeCode", () => {
11991199
await yieldToEventLoop()
12001200

12011201
// Approve first tool
1202-
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission")
1202+
const routes = router.stack.filter((layer) => layer.route?.path === "/sendMessage/toolUse/permission/acp")
12031203
const postRoute = routes[0]?.route?.stack.find((layer) => layer.method === "post")
12041204

12051205
mockRequest = {

local-server/src/server/endpoints/sendMessage/claudeCode/sendMessageToClaudeCode.ts

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { spawn } from "@/utils/spawn-promise"
3131
import { homedir } from "os"
3232
import { sendCommandToHostApp } from "../../interProcessesBridge"
3333
import { v4 as uuidv4 } from "uuid"
34+
import { pendingToolApprovalRequests } from "../pendingToolApprovalRequests"
3435

3536
// Constants
3637
const TOOL_NAME_PREFIX = "claude_code_"
@@ -49,8 +50,6 @@ function createInputHash(input: unknown): string {
4950
// find the matching one and pull its id that can then be forwarded.
5051
const toolUseRequests = new Map<string, Array<Omit<ToolUseRequest, "idx"> & { timestamp: number; inputHash: string }>>()
5152

52-
const pendingToolApprovalRequests = new Map<string, (result: ApprovalResult) => void>()
53-
5453
type ExtendedSDKMessage = SDKMessage | Omit<ToolUsePermissionRequest, "idx">
5554

5655
export const sendMessageToClaudeCode = async (
@@ -683,43 +682,6 @@ type SessionIdInfo = {
683682
sessionId: string
684683
}
685684

686-
export const registerEndpoint = (router: Router) => {
687-
// This endpoint is used to receive the result of pending tool permission requests.
688-
router.post("/sendMessage/toolUse/permission", async (req: Request, res: Response) => {
689-
const body = req.body as ApproveToolUseRequestParams
690-
const { toolUseId, approvalResult } = body
691-
692-
if (!toolUseId || typeof toolUseId !== "string") {
693-
throw new UserFacingError({
694-
message: "Invalid toolUseId",
695-
statusCode: 400,
696-
})
697-
}
698-
699-
if (!approvalResult || !approvalResult.type) {
700-
throw new UserFacingError({
701-
message: "Invalid approvalResult",
702-
statusCode: 400,
703-
})
704-
}
705-
706-
logInfo(`received tool use permission request: ${toolUseId}, ${JSON.stringify(approvalResult)}.`)
707-
708-
const pendingRequest = pendingToolApprovalRequests.get(toolUseId)
709-
if (!pendingRequest) {
710-
throw new UserFacingError({
711-
message: `No pending tool use approval request found for tool use ${toolUseId}`,
712-
statusCode: 404,
713-
})
714-
}
715-
716-
// Remove from pending requests and resolve
717-
pendingToolApprovalRequests.delete(toolUseId)
718-
pendingRequest(approvalResult)
719-
res.json({ success: true })
720-
})
721-
}
722-
723685
function arrayToAsyncIterable<T>(arr: T[], endStream: Promise<void>): AsyncIterable<T> {
724686
return {
725687
async *[Symbol.asyncIterator]() {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { ApprovalResult } from "@/server/schemas/toolApprovalSchema"
2+
3+
/**
4+
* Shared map to track pending tool approval requests across both old and new Claude Code implementations.
5+
* Key: toolUseId, Value: resolver function to respond with approval result
6+
*/
7+
export const pendingToolApprovalRequests = new Map<string, (result: ApprovalResult) => void>()

local-server/src/server/endpoints/sendMessage/sendMessage.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,11 @@ import {
3535
ProviderMetadata,
3636
} from "ai"
3737
import { mapResponseError } from "./errorParsing"
38-
import {
39-
sendMessageToClaudeCode,
40-
registerEndpoint as registerClaudeCodeEndpoint,
41-
} from "./claudeCode/sendMessageToClaudeCode"
38+
import { sendMessageToClaudeCode } from "./claudeCode/sendMessageToClaudeCode"
4239
import { sendMessageToClaudeCode as sendMessageToClaudeCode2 } from "./claudeCode/sendMessageToClaudeCode2"
4340
import { sendMessageToCodex } from "./claudeCode/sendMessageToCodex"
4441

4542
export const registerEndpoint = (router: Router, aiProviders: AIProvider[], getPort: () => number) => {
46-
registerClaudeCodeEndpoint(router)
4743
router.post("/sendMessage", async (req: Request, res: Response) => {
4844
if (!req.body) {
4945
throw new UserFacingError({

local-server/src/server/server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { registerEndpoint as registerSearchFilesEndpoint } from "./endpoints/too
99
import { registerEndpoints as registerCheckpointEndpoints } from "./endpoints/checkpoint"
1010
import { registerEndpoint as registerGetFileIconEndpoint } from "./endpoints/getFileIcon"
1111
import { registerEndpoint as registerListModelsEndpoint } from "./endpoints/listModels"
12-
import { registerEndpoint as registerACPClientEndpoints } from "./endpoints/sendMessage/acp/clients/ACPClient"
12+
import { registerEndpoint as registerACPClientEndpoint } from "./endpoints/sendMessage/acp/clients/ACPClient"
1313
import errorHandler from "./errorHandler"
1414
import fs from "fs"
1515
import path from "path"
@@ -56,7 +56,7 @@ registerListFilesEndpoint(router)
5656
registerSearchFilesEndpoint(router)
5757
registerCheckpointEndpoints(router)
5858
registerGetFileIconEndpoint(router)
59-
registerACPClientEndpoints(router)
59+
registerACPClientEndpoint(router)
6060

6161
if (process.env.NODE_ENV === "production") {
6262
setupExpressErrorHandler(app)

0 commit comments

Comments
 (0)