Handle Windows shell wrappers in Codex command summaries (#931)
This commit is contained in:
@@ -64,6 +64,39 @@ describe("codex tool-call mapper", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("unwraps pwsh wrapper strings for commandExecution on Windows", () => {
|
||||||
|
const item = mapCodexToolCallFromThreadItem({
|
||||||
|
type: "commandExecution",
|
||||||
|
id: "codex-call-wrapper-pwsh-string",
|
||||||
|
status: "running",
|
||||||
|
command:
|
||||||
|
'"C:\\Users\\example\\AppData\\Local\\Microsoft\\WindowsApps\\pwsh.exe" -NoLogo -NoProfile -Command "echo hello"',
|
||||||
|
cwd: "C:\\repo",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(item?.detail).toEqual({
|
||||||
|
type: "shell",
|
||||||
|
command: "echo hello",
|
||||||
|
cwd: "C:\\repo",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("unwraps cmd wrapper arrays for commandExecution on Windows", () => {
|
||||||
|
const item = mapCodexToolCallFromThreadItem({
|
||||||
|
type: "commandExecution",
|
||||||
|
id: "codex-call-wrapper-cmd-array",
|
||||||
|
status: "running",
|
||||||
|
command: ["cmd.exe", "/c", "echo hello"],
|
||||||
|
cwd: "C:\\repo",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(item?.detail).toEqual({
|
||||||
|
type: "shell",
|
||||||
|
command: "echo hello",
|
||||||
|
cwd: "C:\\repo",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps only command output body when commandExecution output is wrapped in shell envelope", () => {
|
it("keeps only command output body when commandExecution output is wrapped in shell envelope", () => {
|
||||||
const item = mapCodexToolCallFromThreadItem({
|
const item = mapCodexToolCallFromThreadItem({
|
||||||
type: "commandExecution",
|
type: "commandExecution",
|
||||||
|
|||||||
@@ -200,21 +200,48 @@ const CodexThreadItemSchema = z.discriminatedUnion("type", [
|
|||||||
|
|
||||||
function maybeUnwrapShellWrapperCommand(command: string): string {
|
function maybeUnwrapShellWrapperCommand(command: string): string {
|
||||||
const trimmed = command.trim();
|
const trimmed = command.trim();
|
||||||
const wrapperMatch = trimmed.match(/^(?:\/bin\/)?(?:zsh|bash|sh)\s+-(?:lc|c)\s+([\s\S]+)$/);
|
const unixWrapperMatch = trimmed.match(/^(?:\/bin\/)?(?:zsh|bash|sh)\s+-(?:lc|c)\s+([\s\S]+)$/);
|
||||||
if (!wrapperMatch) {
|
if (unixWrapperMatch) {
|
||||||
|
const candidate = unixWrapperMatch[1]?.trim() ?? "";
|
||||||
|
if (!candidate) {
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
return stripMatchingEdgeQuotes(candidate);
|
||||||
|
}
|
||||||
|
const windowsWrapperMatch = trimmed.match(
|
||||||
|
/^(?:"[^"]*\\)?(?:pwsh|powershell|cmd)(?:\.exe)?"?\s+((?:-[A-Za-z]+(?:\s+[^-\s][^\s]*)?\s+)*)((?:-Command|-c|\/c)\s+[\s\S]+)$/i,
|
||||||
|
);
|
||||||
|
if (!windowsWrapperMatch) {
|
||||||
return trimmed;
|
return trimmed;
|
||||||
}
|
}
|
||||||
const candidate = wrapperMatch[1]?.trim() ?? "";
|
const wrappedCommand = windowsWrapperMatch[2]?.trim() ?? "";
|
||||||
|
if (!wrappedCommand) {
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
const commandMatch = wrappedCommand.match(/^(?:-Command|-c|\/c)\s+([\s\S]+)$/i);
|
||||||
|
if (!commandMatch) {
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
const candidate = commandMatch[1]?.trim() ?? "";
|
||||||
if (!candidate) {
|
if (!candidate) {
|
||||||
return trimmed;
|
return trimmed;
|
||||||
}
|
}
|
||||||
|
return stripMatchingEdgeQuotes(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripMatchingEdgeQuotes(value: string): string {
|
||||||
if (
|
if (
|
||||||
(candidate.startsWith('"') && candidate.endsWith('"')) ||
|
(value.startsWith('"') && value.endsWith('"')) ||
|
||||||
(candidate.startsWith("'") && candidate.endsWith("'"))
|
(value.startsWith("'") && value.endsWith("'"))
|
||||||
) {
|
) {
|
||||||
return candidate.slice(1, -1);
|
return value.slice(1, -1);
|
||||||
}
|
}
|
||||||
return candidate;
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWindowsShellCommand(command: string): boolean {
|
||||||
|
const normalized = command.replace(/^["']|["']$/g, "");
|
||||||
|
return /(?:^|\\)(?:pwsh|powershell|cmd)(?:\.exe)?$/i.test(normalized);
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeCommandExecutionCommand(value: unknown): string | undefined {
|
function normalizeCommandExecutionCommand(value: unknown): string | undefined {
|
||||||
@@ -236,6 +263,14 @@ function normalizeCommandExecutionCommand(value: unknown): string | undefined {
|
|||||||
const unwrapped = parts[2]?.trim();
|
const unwrapped = parts[2]?.trim();
|
||||||
return unwrapped && unwrapped.length > 0 ? unwrapped : undefined;
|
return unwrapped && unwrapped.length > 0 ? unwrapped : undefined;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
parts.length >= 3 &&
|
||||||
|
isWindowsShellCommand(parts[0] ?? "") &&
|
||||||
|
/^(-command|-c|\/c)$/i.test(parts[1] ?? "")
|
||||||
|
) {
|
||||||
|
const unwrapped = parts.slice(2).join(" ").trim();
|
||||||
|
return unwrapped.length > 0 ? stripMatchingEdgeQuotes(unwrapped) : undefined;
|
||||||
|
}
|
||||||
return parts.join(" ");
|
return parts.join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user