refactor: extract tool call status normalization

This commit is contained in:
Mohamed Boudra
2026-05-01 22:47:01 +07:00
parent 6eab2a89e9
commit 14d78327c3
3 changed files with 58 additions and 59 deletions
@@ -1,7 +1,11 @@
import { z } from "zod";
import type { ToolCallTimelineItem } from "../../agent-sdk-types.js";
import { extractCodexShellOutput, truncateDiffText } from "../tool-call-mapper-utils.js";
import {
extractCodexShellOutput,
normalizeToolCallStatus,
truncateDiffText,
} from "../tool-call-mapper-utils.js";
import { deriveCodexToolDetail, normalizeCodexFilePath } from "./tool-call-detail-parser.js";
import { isSpeakToolName } from "../../tool-name-normalization.js";
@@ -9,9 +13,6 @@ interface CodexMapperOptions {
cwd?: string | null;
}
const FAILED_STATUSES = new Set(["failed", "error", "errored", "rejected", "denied"]);
const CANCELED_STATUSES = new Set(["canceled", "cancelled", "interrupted", "aborted"]);
const COMPLETED_STATUSES = new Set(["completed", "complete", "done", "success", "succeeded"]);
const CodexCommandValueSchema = z.union([z.string(), z.array(z.string())]);
const CodexToolCallStatusSchema = z.enum(["running", "completed", "failed", "canceled"]);
@@ -552,34 +553,6 @@ function hasRenderableEditDetail(detail: ToolCallTimelineItem["detail"]): boolea
);
}
function resolveStatus(
rawStatus: string | undefined,
error: unknown,
output: unknown,
): ToolCallTimelineItem["status"] {
if (error !== undefined && error !== null) {
return "failed";
}
if (typeof rawStatus === "string") {
const normalized = rawStatus.trim().toLowerCase();
if (normalized.length > 0) {
if (FAILED_STATUSES.has(normalized)) {
return "failed";
}
if (CANCELED_STATUSES.has(normalized)) {
return "canceled";
}
if (COMPLETED_STATUSES.has(normalized)) {
return "completed";
}
return "running";
}
}
return output !== null && output !== undefined ? "completed" : "running";
}
function buildMcpToolName(server: string | undefined, tool: string): string {
const trimmedTool = tool.trim();
if (!trimmedTool) {
@@ -633,7 +606,7 @@ function mapCommandExecutionItem(
const name = "shell";
const error = item.error ?? null;
const status = resolveStatus(item.status, error, output);
const status = normalizeToolCallStatus(item.status, error, output);
return {
callId: item.id,
@@ -808,7 +781,7 @@ function mapFileChangeItem(
const name = "apply_patch";
const error = item.error ?? null;
const status = resolveStatus(item.status, error, output);
const status = normalizeToolCallStatus(item.status, error, output);
const firstFile = files[0];
const firstTextFields = resolveFileChangeTextFields(firstFile);
const hasFirstTextFields = Object.keys(firstTextFields).length > 0;
@@ -846,7 +819,7 @@ function mapMcpToolCallItem(
const input = item.arguments ?? null;
const output = item.result ?? null;
const error = item.error ?? null;
const status = resolveStatus(item.status, error, output);
const status = normalizeToolCallStatus(item.status, error, output);
return {
callId: item.id,
@@ -866,7 +839,7 @@ function mapWebSearchItem(
const output = item.action ?? null;
const name = "web_search";
const error = item.error ?? null;
const status = resolveStatus(item.status ?? "completed", error, output);
const status = normalizeToolCallStatus(item.status ?? "completed", error, output);
return {
callId: item.id,
@@ -943,7 +916,11 @@ export function mapCodexRolloutToolCall(params: {
input: normalizedInput,
output: parsed.data.output ?? null,
error: parsed.data.error ?? null,
status: resolveStatus("completed", parsed.data.error ?? null, parsed.data.output ?? null),
status: normalizeToolCallStatus(
"completed",
parsed.data.error ?? null,
parsed.data.output ?? null,
),
cwd: params.cwd ?? null,
});
if (!pass1.success) {
@@ -1,6 +1,7 @@
import { z } from "zod";
import type { ToolCallTimelineItem } from "../../agent-sdk-types.js";
import { normalizeToolCallStatus } from "../tool-call-mapper-utils.js";
import { deriveOpencodeToolDetail } from "./tool-call-detail-parser.js";
interface OpencodeToolCallParams {
@@ -13,10 +14,6 @@ interface OpencodeToolCallParams {
metadata?: Record<string, unknown>;
}
const FAILED_STATUSES = new Set(["error", "failed", "failure"]);
const CANCELED_STATUSES = new Set(["canceled", "cancelled", "aborted", "interrupted"]);
const COMPLETED_STATUSES = new Set(["complete", "completed", "success", "succeeded", "done"]);
const OpencodeToolCallStatusSchema = z.enum(["running", "completed", "failed", "canceled"]);
const OpencodeRawToolCallSchema = z
@@ -37,24 +34,8 @@ const OpencodeNormalizedToolCallPass1Schema = OpencodeRawToolCallSchema.transfor
const error = raw.error ?? null;
const callId =
typeof raw.callId === "string" && raw.callId.trim().length > 0 ? raw.callId.trim() : null;
let status: z.infer<typeof OpencodeToolCallStatusSchema>;
if (error !== null) {
status = "failed";
} else if (typeof raw.status === "string") {
const normalized = raw.status.trim().toLowerCase();
if (FAILED_STATUSES.has(normalized)) {
status = "failed";
} else if (CANCELED_STATUSES.has(normalized)) {
status = "canceled";
} else if (COMPLETED_STATUSES.has(normalized)) {
status = "completed";
} else {
status = "running";
}
} else {
status = output !== null ? "completed" : "running";
}
const rawStatus = typeof raw.status === "string" ? raw.status : undefined;
const status = normalizeToolCallStatus(rawStatus, error, output);
return {
callId,
@@ -1,3 +1,44 @@
export type NormalizedToolCallStatus = "running" | "completed" | "failed" | "canceled";
const FAILED_STATUS_VOCAB = new Set([
"failed",
"failure",
"error",
"errored",
"rejected",
"denied",
]);
const CANCELED_STATUS_VOCAB = new Set(["canceled", "cancelled", "interrupted", "aborted"]);
const COMPLETED_STATUS_VOCAB = new Set(["completed", "complete", "done", "success", "succeeded"]);
export function normalizeToolCallStatus(
rawStatus: string | undefined | null,
error: unknown,
output: unknown,
): NormalizedToolCallStatus {
if (error !== undefined && error !== null) {
return "failed";
}
if (typeof rawStatus === "string") {
const normalized = rawStatus.trim().toLowerCase();
if (normalized.length > 0) {
if (FAILED_STATUS_VOCAB.has(normalized)) {
return "failed";
}
if (CANCELED_STATUS_VOCAB.has(normalized)) {
return "canceled";
}
if (COMPLETED_STATUS_VOCAB.has(normalized)) {
return "completed";
}
return "running";
}
}
return output !== null && output !== undefined ? "completed" : "running";
}
interface ReadChunkLike {
text?: string;
content?: string;