Skip to content

feat: add approval override argument parity for RunState resumes - #2695

Open
seratch wants to merge 1 commit into
mainfrom
feat/approval-override-arguments-parity
Open

feat: add approval override argument parity for RunState resumes#2695
seratch wants to merge 1 commit into
mainfrom
feat/approval-override-arguments-parity

Conversation

@seratch

@seratch seratch commented Mar 17, 2026

Copy link
Copy Markdown
Member

This pull request adds approval-time argument overrides for pending function calls resumed from RunState.

Applications can inspect an interrupted run, replace the model-proposed arguments during approval, and resume execution with the corrected values:

from agents import Runner, SQLiteSession

session = SQLiteSession("conversation-123")

first = await Runner.run(
    agent,
    input="Send the message",
    session=session,
)

state = first.to_state()
state.approve(
    first.interruptions[0],
    override_arguments={
        "recipient": "bob@example.com",
        "message": "Hello Bob",
    },
)

resumed = await Runner.run(
    agent,
    state,
    session=session,
)

By default, the override is durable: the SDK rewrites the canonical function call in a compatible local session before executing the tool. This prevents later replay, compaction, or model input from reverting to the original arguments.

For server-managed conversations, where the SDK does not own canonical history, callers can explicitly request an execution-only override:

state = first.to_state()
state.approve(
    first.interruptions[0],
    override_arguments={"recipient": "bob@example.com"},
    save_override_arguments=False,
)

resumed = await Runner.run(agent, state)

Execution-only mode changes the pending tool invocation without claiming to rewrite remote history. Raw model responses also retain the model-requested arguments as audit provenance, while pending approvals and processed execution use the approved arguments. This provenance model aligns with openai/openai-agents-js#1098 while retaining Python's nested-agent support.

The behavior is consistent across synchronous, streamed, serialized, and nested-agent resumes:

streamed = Runner.run_streamed(agent, input="Send the message", session=session)
await streamed.stream_events()

state = streamed.to_state()
state.approve(
    streamed.interruptions[0],
    override_arguments={"recipient": "bob@example.com"},
)

resumed = Runner.run_streamed(agent, state, session=session)
async for event in resumed.stream_events():
    handle(event)

Durable overrides require a locally managed, history-rewrite-aware session. SQLite and Responses compaction sessions support the required mutation contract. Unsupported ownership combinations, missing mutation targets, and unconfirmed rewrites fail before tool execution.

The change also introduces strict RunState schema 1.16 persistence for override modes and pending history mutations. Restoration validates call identity and mode-specific provenance, while earlier schemas remain readable without granting override behavior. Cleanup is tracked per call so partial failure or cancellation preserves unstarted sibling approvals without allowing unsafe retries to repeat tool side effects.

@seratch seratch added this to the 0.14.x milestone Mar 17, 2026
@github-actions github-actions Bot added enhancement New feature or request feature:sessions labels Mar 17, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e8b70a05e

ℹ️ 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".

Comment thread src/agents/run.py Outdated
@seratch seratch modified the milestones: 0.14.x, 0.15.x Mar 20, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8503fdc79

ℹ️ 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".

Comment thread src/agents/memory/sqlite_session.py Outdated
@seratch
seratch force-pushed the feat/approval-override-arguments-parity branch from a8503fd to b662362 Compare April 8, 2026 11:54

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b66236272e

ℹ️ 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".

Comment thread src/agents/run_internal/agent_runner_helpers.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 672358a3c0

ℹ️ 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".

Comment thread src/agents/run_internal/agent_runner_helpers.py Outdated
@seratch seratch modified the milestones: 0.15.x, 1.1.x May 1, 2026
@seratch
seratch force-pushed the feat/approval-override-arguments-parity branch from 672358a to 2cabc3a Compare August 11, 2026 10:53
Co-authored-by: Kazuhiro Sera <seratch@openai.com>
@seratch
seratch force-pushed the feat/approval-override-arguments-parity branch from 2cabc3a to 7bb9415 Compare August 12, 2026 14:59

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7bb9415c5b

ℹ️ 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".

Comment thread src/agents/run_state.py
Comment on lines +675 to +679
nested_state.approve(
nested_item,
always_approve=always_approve,
override_arguments=override_arguments,
save_override_arguments=save_override_arguments,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate nested overrides before starting the outer tool

When the approval belongs to a nested Agent.as_tool() run, the override and its persistence requirements are recorded only on nested_state, so the outer runner's preflight cannot see them. Validation or history rewriting therefore occurs after the outer function tool starts; with Agent.as_tool()'s default failure_error_function, an unsupported execution-only override backed by local history—or a failed durable rewrite—is converted into a model-visible tool error, allowing the parent run to continue without executing the approved corrected call. Surface nested persistence requirements to the outer preflight, or otherwise validate them before invoking the parent tool.

AGENTS.md reference: AGENTS.md:L88-L88

Useful? React with 👍 / 👎.

Comment on lines +302 to +303
if session is None or is_session_history_rewrite_aware_session(session):
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require the original session for durable overrides

When an interrupted run was persisted to a local session but the caller resumes its RunState without passing that session, this branch treats session is None as supported. The pending mutation is consequently skipped, the corrected tool invocation executes, and its bookkeeping is cleared while the original session still contains the model-proposed arguments; later replay or compaction from that session can therefore revert to the unapproved call. Distinguish session-backed states from sessionless states and reject a durable override before execution when its canonical session is unavailable.

AGENTS.md reference: AGENTS.md:L156-L156

Useful? React with 👍 / 👎.

Comment thread src/agents/run_state.py
Comment on lines +4419 to +4421
else:
state._approval_argument_override_modes = {}
state._session_history_mutations = []

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject downgraded override snapshots

If a current durable-override snapshot is relabeled with any schema version below 1.16, this branch clears only the override bookkeeping while retaining the overridden arguments in the interruption, generated, session, and processed-response payloads. Resuming that state executes the corrected arguments with no pending history mutation, leaving a supplied local session's original function call unchanged and allowing later replay to revert it. Reject legacy schema labels that contain these fields, or detect the argument divergence instead of silently discarding the mutation metadata.

AGENTS.md reference: AGENTS.md:L158-L158

Useful? React with 👍 / 👎.

Comment thread src/agents/run_state.py
Comment on lines +860 to +862
self._approval_argument_override_modes[call_id] = (
"durable" if should_save_override_arguments else "execution_only"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear override state when a rejection supersedes approval

A caller can approve with override_arguments and then change the same per-call decision to rejection before resuming, because reject_tool() explicitly replaces the earlier approval. The override mode and durable mutation recorded here remain active, however, so resume rewrites history even though the call is rejected; because the invocation never executes, _clear_executed_approval_argument_overrides() leaves the mode behind, and a later RunState.from_json() rejects the resulting snapshot because the mode no longer belongs to an approved pending call. Unwind the override mutation and mode when rejection supersedes the approval.

AGENTS.md reference: AGENTS.md:L147-L147

Useful? React with 👍 / 👎.

Comment on lines +300 to +301
previous_items = await self._get_all_underlying_session_items(underlying_session)
rewritten_items = apply_session_history_mutations(previous_items, mutations)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize mutations for ID-stripping session backends

When OpenAIResponsesCompactionSession wraps a valid client-managed backend that strips or reassigns provider id fields on add_items(), the stored function call lacks the id retained in the mutation's expected payload. This fallback performs an exact comparison through apply_session_history_mutations(), so it raises a target-mismatch error and durable approval overrides cannot resume even though the wrapper advertises supports_expected_history_mutations = True. Normalize mutation payloads using the underlying session's storage policy, or require an explicit rewrite-aware underlying backend instead of advertising unsupported fallback behavior.

AGENTS.md reference: AGENTS.md:L156-L156

Useful? React with 👍 / 👎.

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.

1 participant