Skip to content

Commit e52d448

Browse files
committed
fix: Handle more web search events
1 parent 7e18de9 commit e52d448

6 files changed

Lines changed: 271 additions & 26 deletions

File tree

src/CodexAcpServer.ts

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
createDynamicToolCallUpdate,
4040
createFileChangeUpdate,
4141
createMcpToolCallUpdate,
42+
formatWebSearchTitle,
4243
} from "./CodexToolCallMapper";
4344
import {
4445
createFastModeConfigOption,
@@ -862,7 +863,7 @@ export class CodexAcpServer implements acp.Agent {
862863
sessionUpdate: "tool_call",
863864
toolCallId: item.id,
864865
kind: "search",
865-
title: this.formatWebSearchTitle(item),
866+
title: formatWebSearchTitle(item),
866867
status: "completed",
867868
rawInput: {
868869
query: item.query,
@@ -922,29 +923,6 @@ export class CodexAcpServer implements acp.Agent {
922923
};
923924
}
924925

925-
private formatWebSearchTitle(item: ThreadItem & { type: "webSearch" }): string {
926-
const action = item.action;
927-
if (!action) {
928-
return item.query ? `Web search: ${item.query}` : "Web search";
929-
}
930-
switch (action.type) {
931-
case "search": {
932-
const queries = action.queries?.filter((query) => query && query.length > 0) ?? [];
933-
const query = action.query ?? (queries.length > 0 ? queries.join(", ") : null) ?? item.query;
934-
return query ? `Web search: ${query}` : "Web search";
935-
}
936-
case "openPage":
937-
return action.url ? `Open page: ${action.url}` : "Open page";
938-
case "findInPage": {
939-
const pattern = action.pattern ? ` for '${action.pattern}'` : "";
940-
const url = action.url ? ` in ${action.url}` : "";
941-
return `Find in page${pattern}${url}`.trim();
942-
}
943-
case "other":
944-
return "Web search";
945-
}
946-
}
947-
948926
private toAcpToolCallStatus(status: CollabAgentToolCallStatus): "in_progress" | "completed" | "failed" {
949927
switch (status) {
950928
case "inProgress":

src/CodexEventHandler.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import {
3333
createFuzzyFileSearchComplete,
3434
createFuzzyFileSearchStartOrUpdate,
3535
createMcpToolCallUpdate,
36+
createWebSearchCompleteUpdate,
37+
createWebSearchStartUpdate,
3638
fuzzyFileSearchToolCallId,
3739
} from "./CodexToolCallMapper";
3840
import { stripShellPrefix } from "./CommandUtils";
@@ -252,12 +254,13 @@ export class CodexEventHandler {
252254
return await createMcpToolCallUpdate(event.item);
253255
case "dynamicToolCall":
254256
return await createDynamicToolCallUpdate(event.item);
257+
case "webSearch":
258+
return createWebSearchStartUpdate(event.item);
255259
case "collabAgentToolCall":
256260
case "userMessage":
257261
case "hookPrompt":
258262
case "agentMessage":
259263
case "reasoning":
260-
case "webSearch":
261264
case "imageView":
262265
case "imageGeneration":
263266
case "enteredReviewMode":
@@ -297,11 +300,12 @@ export class CodexEventHandler {
297300
text: summary
298301
}
299302
}
303+
case "webSearch":
304+
return createWebSearchCompleteUpdate(event.item);
300305
case "collabAgentToolCall":
301306
case "userMessage":
302307
case "hookPrompt":
303308
case "agentMessage":
304-
case "webSearch":
305309
case "imageView":
306310
case "imageGeneration":
307311
case "enteredReviewMode":

src/CodexToolCallMapper.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,64 @@ export function createFuzzyFileSearchComplete(
190190
};
191191
}
192192

193+
export function createWebSearchStartUpdate(
194+
item: ThreadItem & { type: "webSearch" }
195+
): UpdateSessionEvent {
196+
return {
197+
sessionUpdate: "tool_call",
198+
toolCallId: item.id,
199+
kind: "search",
200+
title: formatWebSearchTitle(item),
201+
status: "in_progress",
202+
rawInput: createWebSearchRawInput(item),
203+
};
204+
}
205+
206+
export function createWebSearchCompleteUpdate(
207+
item: ThreadItem & { type: "webSearch" }
208+
): UpdateSessionEvent {
209+
return {
210+
sessionUpdate: "tool_call_update",
211+
toolCallId: item.id,
212+
title: formatWebSearchTitle(item),
213+
status: "completed",
214+
rawInput: createWebSearchRawInput(item),
215+
};
216+
}
217+
218+
export function formatWebSearchTitle(item: ThreadItem & { type: "webSearch" }): string {
219+
const action = item.action;
220+
if (!action) {
221+
return item.query ? `Web search: ${item.query}` : "Web search";
222+
}
223+
switch (action.type) {
224+
case "search": {
225+
const queries = action.queries?.filter((query) => query && query.length > 0) ?? [];
226+
const query = action.query ?? (queries.length > 0 ? queries.join(", ") : null) ?? item.query;
227+
return query ? `Web search: ${query}` : "Web search";
228+
}
229+
case "openPage":
230+
return action.url ? `Open page: ${action.url}` : "Open page";
231+
case "findInPage": {
232+
const pattern = action.pattern ? ` for '${action.pattern}'` : "";
233+
const url = action.url ? ` in ${action.url}` : "";
234+
return `Find in page${pattern}${url}`.trim();
235+
}
236+
case "other":
237+
return "Web search";
238+
}
239+
}
240+
241+
function createWebSearchRawInput(item: ThreadItem & { type: "webSearch" }): {
242+
query: string,
243+
action: (ThreadItem & { type: "webSearch" })["action"],
244+
} {
245+
return {
246+
query: item.query,
247+
action: item.action,
248+
};
249+
}
250+
193251
function createCommandActionEvent(
194252
id: string,
195253
status: CommandExecutionStatus,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"method": "sessionUpdate",
3+
"args": [
4+
{
5+
"sessionId": "test-session-id",
6+
"update": {
7+
"sessionUpdate": "tool_call",
8+
"toolCallId": "web-open-1",
9+
"kind": "search",
10+
"title": "Open page: https://agentclientprotocol.com",
11+
"status": "in_progress",
12+
"rawInput": {
13+
"query": "https://agentclientprotocol.com",
14+
"action": {
15+
"type": "openPage",
16+
"url": "https://agentclientprotocol.com"
17+
}
18+
}
19+
}
20+
}
21+
]
22+
}
23+
{
24+
"method": "sessionUpdate",
25+
"args": [
26+
{
27+
"sessionId": "test-session-id",
28+
"update": {
29+
"sessionUpdate": "tool_call",
30+
"toolCallId": "web-find-1",
31+
"kind": "search",
32+
"title": "Find in page for 'tool calls' in https://agentclientprotocol.com/protocol",
33+
"status": "in_progress",
34+
"rawInput": {
35+
"query": "protocol",
36+
"action": {
37+
"type": "findInPage",
38+
"url": "https://agentclientprotocol.com/protocol",
39+
"pattern": "tool calls"
40+
}
41+
}
42+
}
43+
}
44+
]
45+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"method": "sessionUpdate",
3+
"args": [
4+
{
5+
"sessionId": "test-session-id",
6+
"update": {
7+
"sessionUpdate": "tool_call",
8+
"toolCallId": "web-search-1",
9+
"kind": "search",
10+
"title": "Web search: agent client protocol",
11+
"status": "in_progress",
12+
"rawInput": {
13+
"query": "agent client protocol",
14+
"action": {
15+
"type": "search",
16+
"query": "agent client protocol",
17+
"queries": null
18+
}
19+
}
20+
}
21+
}
22+
]
23+
}
24+
{
25+
"method": "sessionUpdate",
26+
"args": [
27+
{
28+
"sessionId": "test-session-id",
29+
"update": {
30+
"sessionUpdate": "tool_call_update",
31+
"toolCallId": "web-search-1",
32+
"title": "Web search: agent client protocol",
33+
"status": "completed",
34+
"rawInput": {
35+
"query": "agent client protocol",
36+
"action": {
37+
"type": "search",
38+
"query": "agent client protocol",
39+
"queries": null
40+
}
41+
}
42+
}
43+
}
44+
]
45+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { beforeEach, describe, expect, it, vi } from "vitest";
2+
import type { ServerNotification } from "../../app-server";
3+
import type { SessionState } from "../../CodexAcpServer";
4+
import { AgentMode } from "../../AgentMode";
5+
import {
6+
createCodexMockTestFixture,
7+
createTestSessionState,
8+
setupPromptAndSendNotifications,
9+
type CodexMockTestFixture
10+
} from "../acp-test-utils";
11+
12+
describe("CodexEventHandler - web search events", () => {
13+
let mockFixture: CodexMockTestFixture;
14+
const sessionId = "test-session-id";
15+
16+
beforeEach(() => {
17+
mockFixture = createCodexMockTestFixture();
18+
vi.clearAllMocks();
19+
});
20+
21+
const sessionState: SessionState = createTestSessionState({
22+
sessionId,
23+
currentModelId: "model-id[effort]",
24+
agentMode: AgentMode.DEFAULT_AGENT_MODE
25+
});
26+
27+
it("maps web search start and completion to a search tool call", async () => {
28+
const notifications: ServerNotification[] = [
29+
{
30+
method: "item/started",
31+
params: {
32+
threadId: sessionId,
33+
turnId: "turn-1",
34+
item: {
35+
type: "webSearch",
36+
id: "web-search-1",
37+
query: "agent client protocol",
38+
action: {
39+
type: "search",
40+
query: "agent client protocol",
41+
queries: null,
42+
},
43+
},
44+
},
45+
},
46+
{
47+
method: "item/completed",
48+
params: {
49+
threadId: sessionId,
50+
turnId: "turn-1",
51+
item: {
52+
type: "webSearch",
53+
id: "web-search-1",
54+
query: "agent client protocol",
55+
action: {
56+
type: "search",
57+
query: "agent client protocol",
58+
queries: null,
59+
},
60+
},
61+
},
62+
},
63+
];
64+
65+
await setupPromptAndSendNotifications(mockFixture, sessionId, sessionState, notifications);
66+
67+
await expect(mockFixture.getAcpConnectionDump([])).toMatchFileSnapshot(
68+
"data/web-search-start-and-complete.json"
69+
);
70+
});
71+
72+
it("formats open-page and find-in-page web search actions", async () => {
73+
const notifications: ServerNotification[] = [
74+
{
75+
method: "item/started",
76+
params: {
77+
threadId: sessionId,
78+
turnId: "turn-1",
79+
item: {
80+
type: "webSearch",
81+
id: "web-open-1",
82+
query: "https://agentclientprotocol.com",
83+
action: {
84+
type: "openPage",
85+
url: "https://agentclientprotocol.com",
86+
},
87+
},
88+
},
89+
},
90+
{
91+
method: "item/started",
92+
params: {
93+
threadId: sessionId,
94+
turnId: "turn-1",
95+
item: {
96+
type: "webSearch",
97+
id: "web-find-1",
98+
query: "protocol",
99+
action: {
100+
type: "findInPage",
101+
url: "https://agentclientprotocol.com/protocol",
102+
pattern: "tool calls",
103+
},
104+
},
105+
},
106+
},
107+
];
108+
109+
await setupPromptAndSendNotifications(mockFixture, sessionId, sessionState, notifications);
110+
111+
await expect(mockFixture.getAcpConnectionDump([])).toMatchFileSnapshot(
112+
"data/web-search-action-titles.json"
113+
);
114+
});
115+
});

0 commit comments

Comments
 (0)