refactor: extract tool call status normalization
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user