Skip to content

Commit 43a366d

Browse files
xuiocodex
andcommitted
Fix native MCP response bugs
Co-Authored-By: OpenAI Codex <noreply@openai.com>
1 parent f568a4d commit 43a366d

14 files changed

Lines changed: 317 additions & 75 deletions

dist/index.js

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21207,14 +21207,14 @@ var reviewFindingsSchema = {
2120721207
items: {
2120821208
type: "object",
2120921209
additionalProperties: false,
21210-
required: ["severity", "title", "description"],
21210+
required: ["severity", "title", "description", "file", "line", "recommendation"],
2121121211
properties: {
2121221212
severity: { type: "string", enum: ["critical", "high", "medium", "low", "info"] },
2121321213
title: { type: "string" },
2121421214
description: { type: "string" },
21215-
file: { type: "string" },
21216-
line: { type: "integer" },
21217-
recommendation: { type: "string" }
21215+
file: { type: ["string", "null"] },
21216+
line: { type: ["integer", "null"] },
21217+
recommendation: { type: ["string", "null"] }
2121821218
}
2121921219
}
2122021220
}
@@ -21231,12 +21231,12 @@ var planSchema = {
2123121231
items: {
2123221232
type: "object",
2123321233
additionalProperties: false,
21234-
required: ["title", "description"],
21234+
required: ["title", "description", "status", "files"],
2123521235
properties: {
2123621236
title: { type: "string" },
2123721237
description: { type: "string" },
21238-
status: { type: "string" },
21239-
files: { type: "array", items: { type: "string" } }
21238+
status: { type: ["string", "null"] },
21239+
files: { type: ["array", "null"], items: { type: "string" } }
2124021240
}
2124121241
}
2124221242
}
@@ -21253,13 +21253,13 @@ var riskMatrixSchema = {
2125321253
items: {
2125421254
type: "object",
2125521255
additionalProperties: false,
21256-
required: ["risk", "likelihood", "impact", "mitigation"],
21256+
required: ["risk", "likelihood", "impact", "mitigation", "owner"],
2125721257
properties: {
2125821258
risk: { type: "string" },
2125921259
likelihood: { type: "string", enum: ["low", "medium", "high"] },
2126021260
impact: { type: "string", enum: ["low", "medium", "high"] },
2126121261
mitigation: { type: "string" },
21262-
owner: { type: "string" }
21262+
owner: { type: ["string", "null"] }
2126321263
}
2126421264
}
2126521265
}
@@ -21276,13 +21276,13 @@ var patchSuggestionsSchema = {
2127621276
items: {
2127721277
type: "object",
2127821278
additionalProperties: false,
21279-
required: ["file", "description"],
21279+
required: ["file", "description", "line", "suggested_change", "rationale"],
2128021280
properties: {
2128121281
file: { type: "string" },
21282-
line: { type: "integer" },
21282+
line: { type: ["integer", "null"] },
2128321283
description: { type: "string" },
21284-
suggested_change: { type: "string" },
21285-
rationale: { type: "string" }
21284+
suggested_change: { type: ["string", "null"] },
21285+
rationale: { type: ["string", "null"] }
2128621286
}
2128721287
}
2128821288
}
@@ -23601,13 +23601,16 @@ function compactSessionTurns(value) {
2360123601
return value.slice(0, 20).map(compactSessionTurn);
2360223602
}
2360323603
function compactSessionSnapshotForMcp(session) {
23604+
const sessionRecord = session;
23605+
const status = sessionRecord.status === "active" && sessionRecord.active === false ? "idle" : sessionRecord.status;
2360423606
return {
2360523607
...session,
23608+
status,
2360623609
lastResult: compactRunValue(session.lastResult),
2360723610
partial: isPartial(session.partial) ? compactPartialForMcp(session.partial) : compactUnknown(session.partial, 2e3),
23608-
activeTurn: compactSessionTurn(session.activeTurn),
23609-
queuedTurns: compactSessionTurns(session.queuedTurns),
23610-
recentTurns: compactSessionTurns(session.recentTurns)
23611+
activeTurn: compactSessionTurn(sessionRecord.activeTurn),
23612+
queuedTurns: compactSessionTurns(sessionRecord.queuedTurns),
23613+
recentTurns: compactSessionTurns(sessionRecord.recentTurns)
2361123614
};
2361223615
}
2361323616
function compactRunValue(value) {
@@ -23964,10 +23967,11 @@ var CodexAppServerSession = class _CodexAppServerSession {
2396423967
let timedOut = false;
2396523968
let timeoutReason;
2396623969
const prompt = `${this.preparedSubagents.promptPrefix}${options.prompt}`;
23970+
const outputSchema = schemaForOutputContract(options.outputContract, options.outputSchema);
2396723971
this.acceptingStartNotifications = true;
2396823972
let turnResponse;
2396923973
try {
23970-
turnResponse = await this.request("turn/start", {
23974+
const turnStartParams = {
2397123975
threadId: this.threadId,
2397223976
input: [userText(prompt)],
2397323977
cwd: this.cwd,
@@ -23977,7 +23981,13 @@ var CodexAppServerSession = class _CodexAppServerSession {
2397723981
serviceTier: options.serviceTier ?? null,
2397823982
effort: reasoningEffort,
2397923983
summary: reasoningSummary ?? null
23980-
}, options.spawnTimeoutMs ?? 3e4);
23984+
};
23985+
if (outputSchema) turnStartParams.outputSchema = outputSchema;
23986+
turnResponse = await this.request(
23987+
"turn/start",
23988+
turnStartParams,
23989+
options.spawnTimeoutMs ?? 3e4
23990+
);
2398123991
} catch (error2) {
2398223992
this.acceptingStartNotifications = false;
2398323993
this.pendingStartNotifications = [];
@@ -24005,7 +24015,7 @@ var CodexAppServerSession = class _CodexAppServerSession {
2400524015
const finish = (status, error2) => {
2400624016
const final = truncate2(redactSensitiveText(summary.lastAgentMessage ?? ""), maxOutputChars);
2400724017
const wantsStructuredOutput = Boolean(
24008-
schemaForOutputContract(options.outputContract, options.outputSchema) || options.outputContract && options.outputContract !== "freeform"
24018+
outputSchema || options.outputContract && options.outputContract !== "freeform"
2400924019
);
2401024020
const structured = wantsStructuredOutput ? parseStructuredOutput(summary.lastAgentMessage ?? "") : { value: void 0, error: void 0 };
2401124021
const resultStatus = status === "completed" && structured.error ? "failed" : status;
@@ -24040,7 +24050,7 @@ var CodexAppServerSession = class _CodexAppServerSession {
2404024050
eventSummary: cloneSummary(summary),
2404124051
structuredOutput: structured.value === void 0 ? void 0 : redactJsonValue(structured.value),
2404224052
structuredOutputError: structured.error,
24043-
commandPreview: [this.codexBinary.path, "app-server", "--listen", "stdio://", "turn/start"],
24053+
commandPreview: [this.codexBinary.path, "app-server", "--listen", "stdio://"],
2404424054
timeoutReason,
2404524055
codexSubagents: {
2404624056
customAgents: this.preparedSubagents.names,
@@ -24459,7 +24469,7 @@ var CodexAppServerSession = class _CodexAppServerSession {
2445924469
eventSummary: cloneSummary(active.summary),
2446024470
structuredOutput: structured.value === void 0 ? void 0 : redactJsonValue(structured.value),
2446124471
structuredOutputError: structured.error,
24462-
commandPreview: [this.codexBinary.path, "app-server", "--listen", "stdio://", "turn/start"],
24472+
commandPreview: [this.codexBinary.path, "app-server", "--listen", "stdio://"],
2446324473
timeoutReason: active.timeoutReason,
2446424474
codexSubagents: {
2446524475
customAgents: this.preparedSubagents.names,
@@ -24955,9 +24965,11 @@ var CodexSessionManager = class {
2495524965
const turn2 = turnId ? this.findTurn(session, turnId) : void 0;
2495624966
const completed = turn2 ? terminal(turn2.status) : !session.controller && session.queuedTurns.length === 0;
2495724967
if (completed) {
24968+
const result = turn2 ? turn2.result : session.lastResult;
2495824969
return {
2495924970
session: snapshot2(session),
2496024971
turn: turn2 ? turnSnapshot(turn2) : void 0,
24972+
result,
2496124973
completed: true
2496224974
};
2496324975
}
@@ -24995,6 +25007,7 @@ var CodexSessionManager = class {
2499525007
return {
2499625008
session: snapshot2(session),
2499725009
turn: turn ? turnSnapshot(turn) : void 0,
25010+
result: turn ? turn.result : session.lastResult,
2499825011
completed: false,
2499925012
timeoutReason: "wait_timeout"
2500025013
};
@@ -25354,6 +25367,7 @@ var CodexSessionManager = class {
2535425367
completeTurn(session, turn, result) {
2535525368
session.turns += 1;
2535625369
session.lastResult = result;
25370+
session.partial = void 0;
2535725371
session.codexThreadId = result.eventSummary.threadId ?? session.codexThreadId;
2535825372
session.projectDir = result.cwd;
2535925373
session.cwd = result.cwd;
@@ -25872,6 +25886,13 @@ function stringifyResultValue(value, fallback = "") {
2587225886
return String(value);
2587325887
}
2587425888
}
25889+
function summarizeResultValue(value, fallbackText, fallback) {
25890+
if (value && typeof value === "object" && !Array.isArray(value)) {
25891+
const summary = value.summary;
25892+
if (typeof summary === "string" && summary.trim()) return firstUsefulLine(summary, fallback);
25893+
}
25894+
return firstUsefulLine(stringifyResultValue(value, fallbackText), fallback);
25895+
}
2587525896
function suggestedActionForAgent(result, recovery) {
2587625897
if (recovery?.recommendedAction) return recovery.recommendedAction;
2587725898
if (result.ok) return "Use the result directly, or ask a follow-up Codex task only if more independent analysis is needed.";
@@ -25887,12 +25908,16 @@ function nativeTextResult(value, isError = false) {
2588725908
}
2588825909
function nativeErrorPayload(error2, context = "tool_call") {
2588925910
const recovery = recoveryForError(error2, context);
25911+
const message = redactSensitiveText(error2 instanceof Error ? error2.message : String(error2));
25912+
const result = recovery.recommendedAction ? `${message}
25913+
25914+
Next: ${recovery.recommendedAction}` : message;
2589025915
return {
2589125916
ok: false,
25892-
summary: "Codex task failed.",
25893-
result: "",
25917+
summary: `Codex task failed: ${firstUsefulLine(message, "unknown error")}`,
25918+
result,
2589425919
error: {
25895-
message: redactSensitiveText(error2 instanceof Error ? error2.message : String(error2)),
25920+
message,
2589625921
recoverable: recovery.recoverable,
2589725922
kind: recovery.reason,
2589825923
retry_after_ms: recovery.retryAfterMs
@@ -25914,26 +25939,31 @@ function diagnosticsForAgent(agent) {
2591425939
artifact_paths: agent.outputArtifacts,
2591525940
event_summary: agent.eventSummary,
2591625941
stderr_tail: agent.stderr || void 0,
25917-
stdout_tail: agent.stdoutTail || void 0
25942+
stdout_tail: agent.stdoutTail || void 0,
25943+
structured_output_error: agent.structuredOutputError || void 0
2591825944
};
2591925945
}
2592025946
function nativeAgentPayload(result, context) {
2592125947
const agent = compactAgentResultForMcp(result);
2592225948
const recovery = recoveryForAgentResult(result);
2592325949
const resultValue = agent.structuredOutput ?? agent.finalMessage;
2592425950
const answer = stringifyResultValue(resultValue, agent.finalMessage);
25951+
const structuredOutputError = typeof agent.structuredOutputError === "string" ? agent.structuredOutputError : void 0;
25952+
const visibleAnswer = !agent.ok && structuredOutputError ? `${answer}
25953+
25954+
Structured output parse failed: ${structuredOutputError}` : answer;
2592525955
const sessionId = context.session && typeof context.session === "object" ? context.session.id : void 0;
2592625956
return {
2592725957
ok: agent.ok,
2592825958
status: agent.status,
25929-
summary: firstUsefulLine(answer, `Codex task ${agent.status}`),
25930-
result: answer,
25959+
summary: summarizeResultValue(resultValue, visibleAnswer, `Codex task ${agent.status}`),
25960+
result: visibleAnswer,
2593125961
session_id: sessionId,
2593225962
turn: context.turn,
2593325963
structured: agent.structuredOutput,
2593425964
hint: recovery?.recommendedAction ?? suggestedActionForAgent(agent, recovery),
2593525965
error: recovery ? {
25936-
message: agent.eventSummary.errors[0] ?? agent.stderr ?? `Codex task ${agent.status}`,
25966+
message: structuredOutputError ? `Structured output parse failed: ${structuredOutputError}` : agent.eventSummary.errors[0] ?? agent.stderr ?? `Codex task ${agent.status}`,
2593725967
recoverable: recovery.recoverable,
2593825968
kind: recovery.reason,
2593925969
retry_after_ms: recovery.retryAfterMs
@@ -25954,7 +25984,7 @@ function nativeTaskGroupResponse(runs) {
2595425984
name: agent.name ?? run.task.name ?? run.task.description ?? `codex-task-${index + 1}`,
2595525985
ok: agent.ok,
2595625986
status: agent.status,
25957-
summary: firstUsefulLine(answer, `Codex task ${agent.status}`),
25987+
summary: summarizeResultValue(agent.structuredOutput ?? agent.finalMessage, answer, `Codex task ${agent.status}`),
2595825988
result: answer,
2595925989
session_id: run.session?.id,
2596025990
structured: agent.structuredOutput,
@@ -26957,19 +26987,18 @@ registerTool(
2695726987
}
2695826988
const compactSession2 = compactSessionSnapshotForMcp(waited.session);
2695926989
const recovery = recoveryForWait("codex_session", waited.timeoutReason);
26960-
const lastResult = compactSession2.lastResult;
26961-
const resultText = lastResult && typeof lastResult === "object" ? stringifyResultValue(
26962-
lastResult.structuredOutput ?? lastResult.finalMessage,
26963-
lastResult.finalMessage ?? ""
26964-
) : "";
26990+
const waitResult = waited.result ? compactAgentResultForMcp(waited.result) : compactSession2.lastResult;
26991+
const waitValue = waitResult && typeof waitResult === "object" ? waitResult.structuredOutput ?? waitResult.finalMessage : void 0;
26992+
const waitFallback = waitResult && typeof waitResult === "object" ? waitResult.finalMessage ?? "" : "";
26993+
const resultText = waitResult && typeof waitResult === "object" ? stringifyResultValue(waitValue, waitFallback) : "";
2696526994
const completed = Boolean(waited.completed);
2696626995
return nativeTextResult(
2696726996
{
2696826997
ok: waited.timeoutReason !== "wait_cancelled",
2696926998
status: completed ? "completed" : "running",
2697026999
completed,
2697127000
timeoutReason: waited.timeoutReason,
26972-
summary: completed ? "Codex session is ready." : waited.timeoutReason === "wait_timeout" ? "Codex session is still running." : "Codex session wait was cancelled.",
27001+
summary: completed ? summarizeResultValue(waitValue, resultText, "Codex session is ready.") : waited.timeoutReason === "wait_timeout" ? "Codex session is still running." : "Codex session wait was cancelled.",
2697327002
result: resultText || (completed ? "Codex session is idle." : "Codex session is still running."),
2697427003
session_id: args.session_id,
2697527004
turn: waited.turn,

src/app-server.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -427,10 +427,11 @@ export class CodexAppServerSession {
427427
let timeoutReason: AgentRunResult["timeoutReason"];
428428

429429
const prompt = `${this.preparedSubagents.promptPrefix}${options.prompt}`;
430+
const outputSchema = schemaForOutputContract(options.outputContract, options.outputSchema);
430431
this.acceptingStartNotifications = true;
431432
let turnResponse: { turn?: { id?: string } };
432433
try {
433-
turnResponse = await this.request("turn/start", {
434+
const turnStartParams: JsonObject = {
434435
threadId: this.threadId,
435436
input: [userText(prompt)],
436437
cwd: this.cwd,
@@ -440,7 +441,13 @@ export class CodexAppServerSession {
440441
serviceTier: options.serviceTier ?? null,
441442
effort: reasoningEffort,
442443
summary: reasoningSummary ?? null,
443-
}, options.spawnTimeoutMs ?? 30_000) as { turn?: { id?: string } };
444+
};
445+
if (outputSchema) turnStartParams.outputSchema = outputSchema;
446+
turnResponse = await this.request(
447+
"turn/start",
448+
turnStartParams,
449+
options.spawnTimeoutMs ?? 30_000,
450+
) as { turn?: { id?: string } };
444451
} catch (error) {
445452
this.acceptingStartNotifications = false;
446453
this.pendingStartNotifications = [];
@@ -468,8 +475,7 @@ export class CodexAppServerSession {
468475
const finish = (status: AgentRunResult["status"], error?: string): AgentRunResult => {
469476
const final = truncate(redactSensitiveText(summary.lastAgentMessage ?? ""), maxOutputChars);
470477
const wantsStructuredOutput = Boolean(
471-
schemaForOutputContract(options.outputContract, options.outputSchema) ||
472-
(options.outputContract && options.outputContract !== "freeform"),
478+
outputSchema || (options.outputContract && options.outputContract !== "freeform"),
473479
);
474480
const structured =
475481
wantsStructuredOutput
@@ -507,7 +513,7 @@ export class CodexAppServerSession {
507513
eventSummary: cloneSummary(summary),
508514
structuredOutput: structured.value === undefined ? undefined : redactJsonValue(structured.value),
509515
structuredOutputError: structured.error,
510-
commandPreview: [this.codexBinary.path, "app-server", "--listen", "stdio://", "turn/start"],
516+
commandPreview: [this.codexBinary.path, "app-server", "--listen", "stdio://"],
511517
timeoutReason,
512518
codexSubagents: {
513519
customAgents: this.preparedSubagents.names,
@@ -967,7 +973,7 @@ export class CodexAppServerSession {
967973
eventSummary: cloneSummary(active.summary),
968974
structuredOutput: structured.value === undefined ? undefined : redactJsonValue(structured.value),
969975
structuredOutputError: structured.error,
970-
commandPreview: [this.codexBinary.path, "app-server", "--listen", "stdio://", "turn/start"],
976+
commandPreview: [this.codexBinary.path, "app-server", "--listen", "stdio://"],
971977
timeoutReason: active.timeoutReason,
972978
codexSubagents: {
973979
customAgents: this.preparedSubagents.names,

0 commit comments

Comments
 (0)