Skip to content

Commit 0148306

Browse files
committed
refactor(task): add per-call ToolExecutionContext
1 parent fafad20 commit 0148306

31 files changed

Lines changed: 415 additions & 24 deletions

‎src/core/assistant-message/__tests__/presentAssistantMessage-auto-deny.spec.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ interface MockTask {
6767
didCompleteReadingStream: boolean
6868
didRejectTool: boolean
6969
didAlreadyUseTool: boolean
70+
activeToolContexts: Map<string, unknown>
7071
consecutiveMistakeCount: number
7172
clineMessages: unknown[]
7273
api: { getModel: () => { id: string; info: Record<string, unknown> } }
@@ -99,6 +100,7 @@ function buildMockTask(): MockTask {
99100
didCompleteReadingStream: true,
100101
didRejectTool: false,
101102
didAlreadyUseTool: false,
103+
activeToolContexts: new Map(),
102104
consecutiveMistakeCount: 0,
103105
clineMessages: [],
104106
api: {

‎src/core/assistant-message/__tests__/presentAssistantMessage-custom-tool.spec.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ describe("presentAssistantMessage - Custom Tool Recording", () => {
6565
didCompleteReadingStream: false,
6666
didRejectTool: false,
6767
didAlreadyUseTool: false,
68+
activeToolContexts: new Map(),
6869
consecutiveMistakeCount: 0,
6970
clineMessages: [],
7071
getTaskMode: vi.fn().mockResolvedValue("code"),

‎src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calling", () =
4141
didCompleteReadingStream: false,
4242
didRejectTool: false,
4343
didAlreadyUseTool: false,
44+
activeToolContexts: new Map(),
4445
consecutiveMistakeCount: 0,
4546
getTaskMode: vi.fn().mockResolvedValue("code"),
4647
api: {

‎src/core/assistant-message/__tests__/presentAssistantMessage-tool-repetition.spec.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ describe("presentAssistantMessage - Tool Repetition Detection", () => {
151151
didCompleteReadingStream: false,
152152
didRejectTool: false,
153153
didAlreadyUseTool: false,
154+
activeToolContexts: new Map(),
154155
consecutiveMistakeCount: 0,
155156
consecutiveMistakeLimit: HARD_LIMIT,
156157
clineMessages: [],

‎src/core/assistant-message/__tests__/presentAssistantMessage-tool-usage-attribution.spec.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ interface MockTask {
5858
didCompleteReadingStream: boolean
5959
didRejectTool: boolean
6060
didAlreadyUseTool: boolean
61+
activeToolContexts: Map<string, unknown>
6162
consecutiveMistakeCount: number
6263
clineMessages: unknown[]
6364
getTaskMode: Mock<() => Promise<string>>
@@ -97,6 +98,7 @@ describe("presentAssistantMessage - tool usage attribution", () => {
9798
didCompleteReadingStream: false,
9899
didRejectTool: false,
99100
didAlreadyUseTool: false,
101+
activeToolContexts: new Map(),
100102
consecutiveMistakeCount: 0,
101103
clineMessages: [],
102104
getTaskMode: vi.fn().mockResolvedValue("code"),

‎src/core/assistant-message/__tests__/presentAssistantMessage-unknown-tool.spec.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ describe("presentAssistantMessage - Unknown Tool Handling", () => {
4242
didCompleteReadingStream: false,
4343
didRejectTool: false,
4444
didAlreadyUseTool: false,
45+
activeToolContexts: new Map(),
4546
consecutiveMistakeCount: 0,
4647
clineMessages: [],
4748
getTaskMode: vi.fn().mockResolvedValue("code"),

‎src/core/assistant-message/presentAssistantMessage.ts‎

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { markToolRejected } from "../task/toolExecutionContext"
12
import { serializeError } from "serialize-error"
23
import { Anthropic } from "@anthropic-ai/sdk"
34

@@ -401,6 +402,7 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
401402
askApproval,
402403
handleError,
403404
pushToolResult,
405+
toolCallId: toolCallId ?? "",
404406
onValidated: mcpBlock.partial
405407
? undefined
406408
: () => {
@@ -576,6 +578,10 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
576578
}
577579
}
578580

581+
if (!cline.activeToolContexts.has(toolCallId)) {
582+
cline.activeToolContexts.set(toolCallId, { toolCallId, didReject: false })
583+
}
584+
579585
// Store approval feedback to merge into tool result (GitHub #10465)
580586
let approvalFeedback: { text: string; images?: string[] } | undefined
581587

@@ -679,7 +685,7 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
679685
} else {
680686
pushToolResult(formatResponse.toolDenied())
681687
}
682-
cline.didRejectTool = true
688+
markToolRejected(cline, toolCallId)
683689
return false
684690
}
685691

@@ -866,13 +872,15 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
866872
askApproval,
867873
handleError,
868874
pushToolResult,
875+
toolCallId,
869876
})
870877
break
871878
case "update_todo_list":
872879
await updateTodoListTool.handle(cline, block as ToolUse<"update_todo_list">, {
873880
askApproval,
874881
handleError,
875882
pushToolResult,
883+
toolCallId,
876884
})
877885
break
878886
case "apply_diff":
@@ -881,6 +889,7 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
881889
askApproval,
882890
handleError,
883891
pushToolResult,
892+
toolCallId,
884893
})
885894
break
886895
case "edit":
@@ -890,6 +899,7 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
890899
askApproval,
891900
handleError,
892901
pushToolResult,
902+
toolCallId,
893903
})
894904
break
895905
case "search_replace":
@@ -898,6 +908,7 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
898908
askApproval,
899909
handleError,
900910
pushToolResult,
911+
toolCallId,
901912
})
902913
break
903914
case "edit_file":
@@ -906,6 +917,7 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
906917
askApproval,
907918
handleError,
908919
pushToolResult,
920+
toolCallId,
909921
})
910922
break
911923
case "apply_patch":
@@ -914,6 +926,7 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
914926
askApproval,
915927
handleError,
916928
pushToolResult,
929+
toolCallId,
917930
})
918931
break
919932
case "read_file":
@@ -922,69 +935,79 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
922935
askApproval,
923936
handleError,
924937
pushToolResult,
938+
toolCallId,
925939
})
926940
break
927941
case "list_files":
928942
await listFilesTool.handle(cline, block as ToolUse<"list_files">, {
929943
askApproval,
930944
handleError,
931945
pushToolResult,
946+
toolCallId,
932947
})
933948
break
934949
case "codebase_search":
935950
await codebaseSearchTool.handle(cline, block as ToolUse<"codebase_search">, {
936951
askApproval,
937952
handleError,
938953
pushToolResult,
954+
toolCallId,
939955
})
940956
break
941957
case "search_files":
942958
await searchFilesTool.handle(cline, block as ToolUse<"search_files">, {
943959
askApproval,
944960
handleError,
945961
pushToolResult,
962+
toolCallId,
946963
})
947964
break
948965
case "execute_command":
949966
await executeCommandTool.handle(cline, block as ToolUse<"execute_command">, {
950967
askApproval,
951968
handleError,
952969
pushToolResult,
970+
toolCallId,
953971
})
954972
break
955973
case "read_command_output":
956974
await readCommandOutputTool.handle(cline, block as ToolUse<"read_command_output">, {
957975
askApproval,
958976
handleError,
959977
pushToolResult,
978+
toolCallId,
960979
})
961980
break
962981
case "use_mcp_tool":
963982
await useMcpToolTool.handle(cline, block as ToolUse<"use_mcp_tool">, {
964983
askApproval,
965984
handleError,
966985
pushToolResult,
986+
toolCallId,
967987
})
968988
break
969989
case "access_mcp_resource":
970990
await accessMcpResourceTool.handle(cline, block as ToolUse<"access_mcp_resource">, {
971991
askApproval,
972992
handleError,
973993
pushToolResult,
994+
toolCallId,
974995
})
975996
break
976997
case "ask_followup_question":
977998
await askFollowupQuestionTool.handle(cline, block as ToolUse<"ask_followup_question">, {
978999
askApproval,
9791000
handleError,
9801001
pushToolResult,
1002+
toolCallId,
9811003
})
9821004
break
9831005
case "switch_mode":
9841006
await switchModeTool.handle(cline, block as ToolUse<"switch_mode">, {
9851007
askApproval,
9861008
handleError,
9871009
pushToolResult,
1010+
toolCallId,
9881011
})
9891012
break
9901013
case "new_task":
@@ -993,7 +1016,7 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
9931016
askApproval,
9941017
handleError,
9951018
pushToolResult,
996-
toolCallId: block.id,
1019+
toolCallId,
9971020
})
9981021
break
9991022
case "attempt_completion": {
@@ -1003,7 +1026,7 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
10031026
pushToolResult,
10041027
askFinishSubTaskApproval,
10051028
toolDescription,
1006-
toolCallId: block.id,
1029+
toolCallId,
10071030
}
10081031
await attemptCompletionTool.handle(
10091032
cline,
@@ -1017,13 +1040,15 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
10171040
askApproval,
10181041
handleError,
10191042
pushToolResult,
1043+
toolCallId,
10201044
})
10211045
break
10221046
case "skill":
10231047
await skillTool.handle(cline, block as ToolUse<"skill">, {
10241048
askApproval,
10251049
handleError,
10261050
pushToolResult,
1051+
toolCallId,
10271052
})
10281053
break
10291054
case "generate_image":
@@ -1032,6 +1057,7 @@ async function presentAssistantMessageBlock(cline: Task): Promise<void> {
10321057
askApproval,
10331058
handleError,
10341059
pushToolResult,
1060+
toolCallId,
10351061
})
10361062
break
10371063
default: {

‎src/core/task/Task.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ function queuedResponseForAsk(type: ClineAsk, text?: string): QueuedAskResolutio
241241
const FORCED_CONTEXT_REDUCTION_PERCENT = 75 // Keep 75% of context (remove 25%) on context window errors
242242
const MAX_CONTEXT_WINDOW_RETRIES = 3 // Maximum retries for context window errors
243243

244+
export interface ToolExecutionContext {
245+
toolCallId: string
246+
didReject: boolean
247+
}
248+
244249
export interface TaskOptions extends CreateTaskOptions {
245250
provider: ClineProvider
246251
apiConfiguration: ProviderSettings
@@ -420,7 +425,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
420425
rooIgnoreController?: RooIgnoreController
421426
rooProtectedController?: RooProtectedController
422427
fileContextTracker: FileContextTracker
423-
terminalProcess?: RooTerminalProcess
428+
terminalProcesses = new Map<string, RooTerminalProcess>()
429+
activeToolContexts = new Map<string, ToolExecutionContext>()
424430

425431
// Editing
426432
diffViewProvider: DiffViewProvider
@@ -2524,9 +2530,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
25242530

25252531
async handleTerminalOperation(terminalOperation: "continue" | "abort") {
25262532
if (terminalOperation === "continue") {
2527-
this.terminalProcess?.continue()
2533+
for (const process of this.terminalProcesses.values()) process.continue()
25282534
} else if (terminalOperation === "abort") {
2529-
this.terminalProcess?.abort()
2535+
for (const process of this.terminalProcesses.values()) process.abort()
25302536
}
25312537
}
25322538

@@ -4016,6 +4022,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
40164022
this.didCompleteReadingStream = false
40174023
this.userMessageContent = []
40184024
this.userMessageContentReady = false
4025+
this.activeToolContexts.clear()
40194026
this.didRejectTool = false
40204027
this.didAlreadyUseTool = false
40214028
this.assistantMessageSavedToHistory = false

0 commit comments

Comments
 (0)