Skip to content

fix(agent-tool): preserve nested run cache across tool-call GC#2491

Closed
OiPunk wants to merge 2 commits intoopenai:mainfrom
OiPunk:codex/openai-agents-2487-hitl-agent-tool-state
Closed

fix(agent-tool): preserve nested run cache across tool-call GC#2491
OiPunk wants to merge 2 commits intoopenai:mainfrom
OiPunk:codex/openai-agents-2487-hitl-agent-tool-state

Conversation

@OiPunk
Copy link
Contributor

@OiPunk OiPunk commented Feb 15, 2026

Summary

Fixes #2487.

agent.as_tool() resume logic currently depends on an in-memory cache keyed by tool-call identity. That cache was tied to the original tool_call object lifetime via weakref callbacks. After serializing state and dropping the original RunResult, GC could clear the cache entry before resume, causing nested runs to restart and repeatedly pause on the same approval.

This PR decouples cache retention from tool_call GC and keeps entries until explicit consume/drop.

Changes

  • src/agents/agent_tool_state.py
    • Removed weakref-based auto-eviction for nested agent-tool run results.
    • Keep signature/object-id indexes and clear entries only on explicit consume/drop paths.
  • tests/test_agent_tool_state.py
    • Added regression test proving nested result survives original tool-call GC and is still resumable by signature.
    • Updated shutdown-safety test for current globals.

Validation

  • uv run --with ruff ruff check src/agents/agent_tool_state.py tests/test_agent_tool_state.py
  • uv run mypy src/agents/agent_tool_state.py tests/test_agent_tool_state.py
  • env -u ALL_PROXY -u all_proxy -u HTTPS_PROXY -u https_proxy -u HTTP_PROXY -u http_proxy -u NO_PROXY -u no_proxy uv run --with pytest pytest -q tests/test_agent_tool_state.py tests/test_agent_as_tool.py -k "rejected_nested_approval_resumes_run or agent_tool_result_survives_tool_call_gc_until_consumed" (2 passed)
  • Changed executable lines in touched source files are covered (100%; source changes are deletion-only).

Copy link

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

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: 17ed2afba0

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

@OiPunk
Copy link
Contributor Author

OiPunk commented Feb 15, 2026

Thanks for flagging this edge case. I pushed d0a3619 to clear any stale signature index for the object id before storing a new run result, which prevents old signature mappings from surviving id reuse. I also expanded with targeted cases for id-reuse overwrite, ambiguous fallback candidates, direct consume/peek/drop paths, and fallback-drop cleanup.

@OiPunk
Copy link
Contributor Author

OiPunk commented Feb 15, 2026

Thanks for flagging this edge case.

I pushed commit d0a3619 to clear any stale signature index for the current object id before recording a new run result. This prevents old signature mappings from surviving object-id reuse and accidentally influencing later fallback lookups.

I also expanded tests/test_agent_tool_state.py with focused coverage for:

  • stale signature cleanup on object-id reuse
  • direct consume/peek/drop paths
  • no-candidate and ambiguous fallback branches
  • fallback-drop cleanup with a single candidate id
  • defensive index-drop behavior when global maps are partially unavailable

Local checks run on this update:

  • uv run --with ruff ruff check src/agents/agent_tool_state.py tests/test_agent_tool_state.py
  • uv run mypy src/agents/agent_tool_state.py tests/test_agent_tool_state.py
  • uv run --with pytest pytest -q tests/test_agent_tool_state.py
  • uv run --with coverage --with pytest coverage run -m pytest -q tests/test_agent_tool_state.py
  • uv run --with coverage coverage report -m src/agents/agent_tool_state.py (100%)

@seratch seratch added this to the 0.9.x milestone Feb 16, 2026
Copy link

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

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: d0a361979b

ℹ️ 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 on lines 72 to 73
_agent_tool_run_results_by_obj[tool_call_obj_id] = run_result
_index_agent_tool_run_result(tool_call, tool_call_obj_id)

Choose a reason for hiding this comment

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

P1 Badge Evict abandoned nested run results from global cache

record_agent_tool_run_result now keeps interrupted nested RunResult objects in module-level dicts with no GC-triggered eviction. If a paused run is abandoned before consume_agent_tool_run_result/drop_agent_tool_run_result is called, the entry is never removed and retains the full nested run state. In long-lived workers this can cause unbounded memory growth.

Useful? React with 👍 / 👎.

@seratch seratch marked this pull request as draft February 16, 2026 23:52
@seratch
Copy link
Member

seratch commented Feb 17, 2026

Closing this in favor of #2500

@seratch seratch closed this Feb 17, 2026
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.

Agent-as-tool + HITL resume: weakref cache loss causes infinite pause loop

2 participants