Fix Codex MCP thread item mapping

This commit is contained in:
Mohamed Boudra
2025-12-24 19:41:14 +07:00
parent c51eb75930
commit 383870687f
2 changed files with 53 additions and 10 deletions
@@ -129,6 +129,16 @@ function extractThreadItemCallId(item: Record<string, unknown>): string | undefi
);
}
function normalizeThreadEventType(type: string): string {
if (type.startsWith("thread.item.")) {
return type.slice("thread.".length);
}
if (type.startsWith("thread.turn.")) {
return type.slice("thread.".length);
}
return type;
}
function buildCommandDisplayName(command?: unknown): string {
if (typeof command === "string") {
const trimmed = command.trim();
@@ -969,13 +979,22 @@ class CodexMcpAgentSession implements AgentSession {
if (!event || typeof event !== "object") {
return;
}
const type = (event as { type?: unknown }).type;
if (typeof type !== "string") {
const record = event as Record<string, unknown>;
const data = record.data && typeof record.data === "object" ? (record.data as Record<string, unknown>) : null;
const recordType = record.type;
const dataType = data?.type;
let type = typeof recordType === "string" ? recordType : undefined;
let eventRecord: Record<string, unknown> = record;
if ((!type || type === "event") && typeof dataType === "string") {
type = dataType;
eventRecord = { ...record, ...data, type };
}
if (!type) {
return;
}
if (type.includes(".") || type.startsWith("turn.") || type.startsWith("thread.") || type.startsWith("item.")) {
this.handleThreadEvent(event as Record<string, unknown>);
this.handleThreadEvent(eventRecord);
return;
}
@@ -1163,10 +1182,14 @@ class CodexMcpAgentSession implements AgentSession {
}
private handleThreadEvent(event: Record<string, unknown>): void {
const type = event.type as string;
const rawType = event.type as string;
const type = normalizeThreadEventType(rawType);
const data =
event.data && typeof event.data === "object" ? (event.data as Record<string, unknown>) : null;
switch (type) {
case "thread.started": {
const threadId = event.thread_id as string | undefined;
const threadId =
(event.thread_id as string | undefined) ?? (data?.thread_id as string | undefined);
if (threadId) {
this.sessionId = threadId;
this.flushPendingHistory();
@@ -1182,19 +1205,26 @@ class CodexMcpAgentSession implements AgentSession {
this.emitEvent({ type: "turn_started", provider: "codex-mcp" });
break;
case "turn.completed": {
const usage = this.convertUsage((event as { usage?: unknown }).usage);
const usage = this.convertUsage(
(event as { usage?: unknown }).usage ?? (data as { usage?: unknown } | null)?.usage
);
this.emitEvent({ type: "turn_completed", provider: "codex-mcp", usage });
break;
}
case "turn.failed": {
const error = (event as { error?: { message?: string } }).error?.message ?? "Codex MCP turn failed";
const errorRecord =
(event as { error?: { message?: string } }).error ??
((data as { error?: { message?: string } } | null)?.error ?? null);
const error = errorRecord?.message ?? "Codex MCP turn failed";
this.emitEvent({ type: "turn_failed", provider: "codex-mcp", error });
break;
}
case "item.started":
case "item.updated":
case "item.completed": {
const item = event.item as Record<string, unknown> | undefined;
const item =
(event.item as Record<string, unknown> | undefined) ??
(data?.item as Record<string, unknown> | undefined);
if (!item) break;
const timelineItem = this.threadItemToTimeline(item);
if (timelineItem) {
@@ -1218,7 +1248,10 @@ class CodexMcpAgentSession implements AgentSession {
break;
}
case "error": {
const message = (event.message as string | undefined) ?? "Codex MCP stream error";
const message =
(event.message as string | undefined) ??
(data?.message as string | undefined) ??
"Codex MCP stream error";
this.emitEvent({
type: "timeline",
provider: "codex-mcp",
+11 -1
View File
@@ -118,12 +118,22 @@ Build a new Codex MCP provider side‑by‑side with the existing Codex SDK prov
- [x] **Fix**: Codex MCP command output should include exit codes for command tool calls.
- **Done (2025-12-24 19:36)**: Ensured command tool outputs include exit codes even when output text is missing.
- [ ] **Fix**: Codex MCP thread/item event mapping should capture file_change, mcp_tool_call, web_search, and todo_list.
- [x] **Fix**: Codex MCP thread/item event mapping should capture file_change, mcp_tool_call, web_search, and todo_list.
- **Done (2025-12-24 19:40)**: Normalized thread event types and added data fallback so item events map to timeline entries.
- [ ] **Fix**: Codex MCP should emit error timeline items for failed turns.
- [ ] **Fix**: Codex MCP persistence should include conversationId metadata for resume.
- [ ] **Investigate**: Elicitation was fixed in Codex 0.71.0 - why isn't it working for us?
- Web search found: Issue #6992 - Codex auto-declined elicitation requests, fixed in 0.71.0
- We're on 0.77.0 so elicitation SHOULD work now
- The bug was in `codex-rs/rmcp-client/src/logging_client_handler.rs`
- Verify we're actually receiving ElicitRequest but maybe handling it wrong
- Check GitHub issue #6992 for exact fix details
- Maybe happy-cli works because it's on a newer version?
- [ ] **Fix**: Codex MCP permission elicitation should surface permission_requested/resolved events (read-only/untrusted too).
- [ ] **Fix**: Codex SDK persistence hydration should include completed shell_command tool entries.