Skip to content

Conversation

@dkundel-openai
Copy link
Collaborator

No description provided.

@changeset-bot
Copy link

changeset-bot bot commented Nov 13, 2025

🦋 Changeset detected

Latest commit: 9ac6521

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@openai/agents-extensions Patch
@openai/agents-realtime Patch
@openai/agents-openai Patch
@openai/agents-core Patch
@openai/agents Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dkundel-openai dkundel-openai force-pushed the dkundel/shell-apply-patch branch from 6b3bc49 to 9ac6521 Compare November 13, 2025 18:25
@dkundel-openai dkundel-openai merged commit 2b57c4e into main Nov 13, 2025
4 checks passed
@dkundel-openai dkundel-openai deleted the dkundel/shell-apply-patch branch November 13, 2025 18:27
@chatgpt-codex-connector
Copy link

💡 Codex Review

const [functionResults, computerResults, shellResults, applyPatchResults] =
await Promise.all([
executeFunctionToolCalls(
agent,
processedResponse.functions as ToolRunFunction<unknown>[],
runner,
state,
),
executeComputerActions(
agent,
processedResponse.computerActions,
runner,
state._context,
),
executeShellActions(
agent,
processedResponse.shellActions,
runner,
state._context,
),
executeApplyPatchOperations(
agent,
processedResponse.applyPatchActions,
runner,
state._context,
),
]);
for (const result of functionResults) {
appendIfNew(result.runItem);
}
for (const item of computerResults) {
appendIfNew(item);
}
for (const item of shellResults) {
appendIfNew(item);
}
for (const item of applyPatchResults) {
appendIfNew(item);
}
// run hosted MCP approval requests
if (processedResponse.mcpApprovalRequests.length > 0) {
for (const approvalRequest of processedResponse.mcpApprovalRequests) {
const toolData = approvalRequest.mcpTool
.providerData as ProviderData.HostedMCPTool<TContext>;
const requestData = approvalRequest.requestItem.rawItem
.providerData as ProviderData.HostedMCPApprovalRequest;
if (toolData.on_approval) {
// synchronously handle the approval process here
const approvalResult = await toolData.on_approval(
state._context,
approvalRequest.requestItem,
);
const approvalResponseData: ProviderData.HostedMCPApprovalResponse = {
approve: approvalResult.approve,
approval_request_id: requestData.id,
reason: approvalResult.reason,
};
newItems.push(
new RunToolCallItem(
{
type: 'hosted_tool_call',
name: 'mcp_approval_response',
providerData: approvalResponseData,
},
agent as Agent<unknown, 'text'>,
),
);
} else {
// receive a user's approval on the next turn
newItems.push(approvalRequest.requestItem);
const approvalItem = {
type: 'hosted_mcp_tool_approval' as const,
tool: approvalRequest.mcpTool,
runItem: new RunToolApprovalItem(
{
type: 'hosted_tool_call',
name: requestData.name,
id: requestData.id,
arguments: requestData.arguments,
status: 'in_progress',
providerData: requestData,
},
agent,
),
};
functionResults.push(approvalItem);
// newItems.push(approvalItem.runItem);
}
}
}
// process handoffs
if (processedResponse.handoffs.length > 0) {
return await executeHandoffCalls(
agent,
originalInput,
preStepItems,
newItems,
newResponse,
processedResponse.handoffs,
runner,
state._context,
);
}
const completedStep = await maybeCompleteTurnFromToolResults({
agent,
runner,
state,
functionResults,
originalInput,
newResponse,
preStepItems,
newItems,
});
if (completedStep) {
return completedStep;
}
// If the model issued any tool calls or handoffs in this turn,
// we must NOT treat any assistant message in the same turn as the final output.
// We should run the loop again so the model can see the tool results and respond.
const hadToolCallsOrActions =
(processedResponse.functions?.length ?? 0) > 0 ||
(processedResponse.computerActions?.length ?? 0) > 0 ||
(processedResponse.shellActions?.length ?? 0) > 0 ||
(processedResponse.applyPatchActions?.length ?? 0) > 0 ||
(processedResponse.mcpApprovalRequests?.length ?? 0) > 0 ||
(processedResponse.handoffs?.length ?? 0) > 0;
if (hadToolCallsOrActions) {
return new SingleStepResult(
originalInput,
newResponse,
preStepItems,
newItems,
{ type: 'next_step_run_again' },
);
}
// No tool calls/actions in this turn; safe to consider a plain assistant message as final.
const messageItems = newItems.filter(
(item) => item instanceof RunMessageOutputItem,
);
// we will use the last content output as the final output
const potentialFinalOutput =
messageItems.length > 0
? getLastTextFromOutputMessage(
messageItems[messageItems.length - 1].rawItem,
)
: undefined;
// if there is no output we just run again
if (typeof potentialFinalOutput === 'undefined') {
return new SingleStepResult(
originalInput,
newResponse,
preStepItems,
newItems,
{ type: 'next_step_run_again' },
);
}
// Keep looping if any tool output placeholders still require an approval follow-up.
const hasPendingToolsOrApprovals = functionResults.some(
(result) => result.runItem instanceof RunToolApprovalItem,
);
if (!hasPendingToolsOrApprovals) {

P1 Badge Surface shell/apply_patch approvals as interruptions

When a shell or apply_patch tool requires approval, executeShellActions/executeApplyPatchOperations push a RunToolApprovalItem into shellResults/applyPatchResults (see lines 1417‑1479 and 1574‑1604), but resolveTurnAfterModelResponse only feeds functionResults into maybeCompleteTurnFromToolResults and only inspects functionResults when deciding whether to emit a next_step_interruption (lines 709‑879). That means a shell/apply_patch approval request never sets _currentStep to an interruption and the run loop keeps calling the model with the approval request still pending. The host never gets a chance to call state.approve/state.reject, so any tool configured with needsApproval simply deadlocks. The agent should treat RunToolApprovalItems coming from shell/apply_patch the same way it already treats function-tool approvals so the run pauses until the user answers.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants