Skip to content

Commit 14f1b4f

Browse files
committed
fix: Handle more web search events
1 parent 26b801d commit 14f1b4f

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,
@@ -898,7 +899,7 @@ export class CodexAcpServer implements acp.Agent {
898899
sessionUpdate: "tool_call",
899900
toolCallId: item.id,
900901
kind: "search",
901-
title: this.formatWebSearchTitle(item),
902+
title: formatWebSearchTitle(item),
902903
status: "completed",
903904
rawInput: {
904905
query: item.query,
@@ -958,29 +959,6 @@ export class CodexAcpServer implements acp.Agent {
958959
};
959960
}
960961

961-
private formatWebSearchTitle(item: ThreadItem & { type: "webSearch" }): string {
962-
const action = item.action;
963-
if (!action) {
964-
return item.query ? `Web search: ${item.query}` : "Web search";
965-
}
966-
switch (action.type) {
967-
case "search": {
968-
const queries = action.queries?.filter((query) => query && query.length > 0) ?? [];
969-
const query = action.query ?? (queries.length > 0 ? queries.join(", ") : null) ?? item.query;
970-
return query ? `Web search: ${query}` : "Web search";
971-
}
972-
case "openPage":
973-
return action.url ? `Open page: ${action.url}` : "Open page";
974-
case "findInPage": {
975-
const pattern = action.pattern ? ` for '${action.pattern}'` : "";
976-
const url = action.url ? ` in ${action.url}` : "";
977-
return `Find in page${pattern}${url}`.trim();
978-
}
979-
case "other":
980-
return "Web search";
981-
}
982-
}
983-
984962
private toAcpToolCallStatus(status: CollabAgentToolCallStatus): "in_progress" | "completed" | "failed" {
985963
switch (status) {
986964
case "inProgress":

src/CodexEventHandler.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import {
4040
createFuzzyFileSearchComplete,
4141
createFuzzyFileSearchStartOrUpdate,
4242
createMcpToolCallUpdate,
43+
createWebSearchCompleteUpdate,
44+
createWebSearchStartUpdate,
4345
fuzzyFileSearchToolCallId,
4446
} from "./CodexToolCallMapper";
4547
import { stripShellPrefix } from "./CommandUtils";
@@ -303,12 +305,13 @@ export class CodexEventHandler {
303305
return await createMcpToolCallUpdate(event.item);
304306
case "dynamicToolCall":
305307
return await createDynamicToolCallUpdate(event.item);
308+
case "webSearch":
309+
return createWebSearchStartUpdate(event.item);
306310
case "collabAgentToolCall":
307311
case "userMessage":
308312
case "hookPrompt":
309313
case "agentMessage":
310314
case "reasoning":
311-
case "webSearch":
312315
case "imageView":
313316
case "imageGeneration":
314317
case "enteredReviewMode":
@@ -348,11 +351,12 @@ export class CodexEventHandler {
348351
text: summary
349352
}
350353
}
354+
case "webSearch":
355+
return createWebSearchCompleteUpdate(event.item);
351356
case "collabAgentToolCall":
352357
case "userMessage":
353358
case "hookPrompt":
354359
case "agentMessage":
355-
case "webSearch":
356360
case "imageView":
357361
case "imageGeneration":
358362
case "enteredReviewMode":

src/CodexToolCallMapper.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,64 @@ export function createFuzzyFileSearchComplete(
229229
};
230230
}
231231

232+
export function createWebSearchStartUpdate(
233+
item: ThreadItem & { type: "webSearch" }
234+
): UpdateSessionEvent {
235+
return {
236+
sessionUpdate: "tool_call",
237+
toolCallId: item.id,
238+
kind: "search",
239+
title: formatWebSearchTitle(item),
240+
status: "in_progress",
241+
rawInput: createWebSearchRawInput(item),
242+
};
243+
}
244+
245+
export function createWebSearchCompleteUpdate(
246+
item: ThreadItem & { type: "webSearch" }
247+
): UpdateSessionEvent {
248+
return {
249+
sessionUpdate: "tool_call_update",
250+
toolCallId: item.id,
251+
title: formatWebSearchTitle(item),
252+
status: "completed",
253+
rawInput: createWebSearchRawInput(item),
254+
};
255+
}
256+
257+
export function formatWebSearchTitle(item: ThreadItem & { type: "webSearch" }): string {
258+
const action = item.action;
259+
if (!action) {
260+
return item.query ? `Web search: ${item.query}` : "Web search";
261+
}
262+
switch (action.type) {
263+
case "search": {
264+
const queries = action.queries?.filter((query) => query && query.length > 0) ?? [];
265+
const query = action.query ?? (queries.length > 0 ? queries.join(", ") : null) ?? item.query;
266+
return query ? `Web search: ${query}` : "Web search";
267+
}
268+
case "openPage":
269+
return action.url ? `Open page: ${action.url}` : "Open page";
270+
case "findInPage": {
271+
const pattern = action.pattern ? ` for '${action.pattern}'` : "";
272+
const url = action.url ? ` in ${action.url}` : "";
273+
return `Find in page${pattern}${url}`.trim();
274+
}
275+
case "other":
276+
return "Web search";
277+
}
278+
}
279+
280+
function createWebSearchRawInput(item: ThreadItem & { type: "webSearch" }): {
281+
query: string,
282+
action: (ThreadItem & { type: "webSearch" })["action"],
283+
} {
284+
return {
285+
query: item.query,
286+
action: item.action,
287+
};
288+
}
289+
232290
function createCommandActionEvent(
233291
id: string,
234292
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)