






















11/**
22 * Shared validation for model-supplied tool parameters.
33 * Converts malformed file-tool arguments into retryable errors and fixes the
4- * specific XML suffix corruption seen in path arguments.
4+ * specific XML suffix and Office-extension corruption seen in path arguments.
55 */
66import type { AnyAgentTool } from "./agent-tools.types.js";
77@@ -14,7 +14,13 @@ export type RequiredParamGroup = {
14141515const RETRY_GUIDANCE_SUFFIX = " Supply correct parameters before retrying.";
1616const XML_ARG_VALUE_SUFFIX_RE = /<\/arg_value>>+$/;
17-const XML_ARG_VALUE_PATH_PARAM_KEYS = new Set(["path"]);
17+const FILE_TOOL_PATH_PARAM_KEYS = new Set(["path"]);
18+const HALLUCINATED_OFFICE_PATH_EXTENSION_RE = /\.(doc|ppt|xls)(?:odex|codex|xodex|xcodex)$/i;
19+const OFFICE_EXTENSION_BY_FAMILY: Record<string, string> = {
20+doc: ".docx",
21+ppt: ".pptx",
22+xls: ".xlsx",
23+};
18241925function parameterValidationError(message: string): Error {
2026return new Error(`${message}.${RETRY_GUIDANCE_SUFFIX}`);
@@ -110,6 +116,18 @@ export function stripMalformedXmlArgValueSuffix(value: string): string {
110116return value.includes("</arg_value>") ? value.replace(XML_ARG_VALUE_SUFFIX_RE, "") : value;
111117}
112118119+/** Normalize known model-hallucinated Office/codex path extensions. */
120+export function normalizeHallucinatedOfficePathExtension(value: string): string {
121+return value.replace(HALLUCINATED_OFFICE_PATH_EXTENSION_RE, (_match, family: string) => {
122+return OFFICE_EXTENSION_BY_FAMILY[family.toLowerCase()] ?? _match;
123+});
124+}
125+126+/** Normalize model-supplied file-tool path params without touching payload text. */
127+export function normalizeFileToolPathParam(value: string): string {
128+return normalizeHallucinatedOfficePathExtension(stripMalformedXmlArgValueSuffix(value));
129+}
130+113131/** Strip malformed XML suffixes from selected string fields without mutating input. */
114132export function stripMalformedXmlArgValueSuffixFromKeys<T extends Record<string, unknown>>(
115133record: T,
@@ -130,13 +148,31 @@ export function stripMalformedXmlArgValueSuffixFromKeys<T extends Record<string,
130148return normalized ?? record;
131149}
132150133-function resolveMalformedXmlArgValuePathKeys(
134-groups: readonly RequiredParamGroup[] | undefined,
135-): string[] {
151+/** Normalize selected file-tool path fields without mutating input. */
152+export function normalizeFileToolPathParamsFromKeys<T extends Record<string, unknown>>(
153+record: T,
154+keys: readonly string[],
155+): T {
156+let normalized: T | undefined;
157+for (const key of keys) {
158+const value = record[key];
159+if (typeof value !== "string") {
160+continue;
161+}
162+const normalizedValue = normalizeFileToolPathParam(value);
163+if (normalizedValue !== value) {
164+normalized ??= { ...record };
165+normalized[key as keyof T] = normalizedValue as T[keyof T];
166+}
167+}
168+return normalized ?? record;
169+}
170+171+function resolveFileToolPathParamKeys(groups: readonly RequiredParamGroup[] | undefined): string[] {
136172const keys = new Set<string>();
137173for (const group of groups ?? []) {
138174for (const key of group.keys) {
139-if (XML_ARG_VALUE_PATH_PARAM_KEYS.has(key)) {
175+if (FILE_TOOL_PATH_PARAM_KEYS.has(key)) {
140176keys.add(key);
141177}
142178}
@@ -195,10 +231,10 @@ export function wrapToolParamValidation(
195231 ...tool,
196232execute: async (toolCallId, params, signal, onUpdate) => {
197233const record = getToolParamsRecord(params);
198-const pathKeys = resolveMalformedXmlArgValuePathKeys(requiredParamGroups);
234+const pathKeys = resolveFileToolPathParamKeys(requiredParamGroups);
199235const normalizedParams =
200236record && pathKeys.length > 0
201- ? stripMalformedXmlArgValueSuffixFromKeys(record, pathKeys)
237+ ? normalizeFileToolPathParamsFromKeys(record, pathKeys)
202238 : params;
203239if (requiredParamGroups?.length) {
204240assertRequiredParams(getToolParamsRecord(normalizedParams), requiredParamGroups, tool.name);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。