Skip to content

Cache state after a clean finish when clear_cache_on_success=False - #105

Open
xh123637 wants to merge 1 commit into
ArtificialAnalysis:mainfrom
xh123637:fix/91-cache-on-success
Open

xh123637 wants to merge 1 commit into
ArtificialAnalysis:mainfrom
xh123637:fix/91-cache-on-success

Conversation

@xh123637

@xh123637 xh123637 commented Nov 3, 2026

Copy link
Copy Markdown

Summary

Fixes #91. Agent.__aexit__ only saved the session state on non-success exits — an exception, or a run that never called finish. A run that ended with a clean finish wrote nothing, so the state was discarded even when the caller passed clear_cache_on_success=False. That parameter only ever governed the cleanup side (run() clearing the cache at finish), never the saving side — which made session(resume=True, clear_cache_on_success=False) a no-op across successful runs: a later run() on the same task text found no cache and started fresh, silently losing the finished session's context for task-scoped orchestration workflows that reactivate a session hours apart.

Changes

src/stirrup/core/agent.py only:

  • The should_cache gate in __aexit__ now also passes on a clean finish when clear_cache_on_success=False — i.e. the parameter is treated as "save AND keep", which is what its name already promises. The saved state is the same _current_run_state snapshot the interrupt path saves (and the same one the issue's private-API workaround saves manually).
  • The session() docstring for clear_cache_on_success now states this save-and-keep semantics.

Behavior that does not change:

  • clear_cache_on_success=True (default): run() still clears any cache the moment the run finishes, and nulls _current_task_hash/_current_run_state, so __aexit__ still saves nothing after a clean finish.
  • Error/interrupt exits (exception propagating, or finish never called): cached exactly as before, under either flag value.
  • cache_on_interrupt=False: still disables __aexit__ caching entirely.
  • Sub-agent sessions (depth > 0) and sessions where no run started: still never cached.

The alternative from the issue — a new explicit cache_on_success parameter — was not taken: it would add a second flag whose interaction with clear_cache_on_success still needs defining, while reading the existing flag as save-and-keep matches the name and needs no API addition. Happy to switch if maintainers prefer the explicit parameter.

Tests

I wrote the tests first and checked that they fail without the fix. With src/stirrup/core/agent.py reverted, the first test below fails (no cache is written on the clean finish) and the other two still pass; with the fix, all three pass:

  • test_clean_finish_is_cached_and_resumed_when_clear_cache_on_success_is_false — the reported flow end to end: a first session (resume=True, clear_cache_on_success=False) does one turn of work and finishes cleanly; its state is in the cache afterwards (verified via CacheManager.load_state, including the first turn's content), and a second session with resume=True on the same task text opens with that history in context.
  • test_error_exit_is_still_cached — a run whose client raises still writes its state for resume (pre-existing path, unchanged).
  • test_clean_finish_still_clears_cache_when_clear_cache_on_success_is_true — seeds a cache via an error exit, then a cleanly finishing run with the default clear_cache_on_success=True clears it and saves nothing.

Local runs (Python 3.13, uv sync --locked --dev --extra mcp):

  • tests/test_agent.py + tests/test_logging.py: 62 passed, 1 skipped.
  • Full suite (pytest --ignore=tests/test_browser_use.py -m "not docker and not e2b and not browser"): 319 passed / 34 failed with the fix, against 316 passed / 34 failed on unmodified main in the same sandbox (the 3 extra passes are the new tests). The failure sets differ only by one test_web_fetch.py DNS test in each direction; both pass on a re-run, and the remaining failures are the pre-existing sandbox environment failures in the chat-completions/open-responses client tests (httpx/proxy), identical on main and unrelated to this change.
  • ruff format --check . and ruff check . pass; ty check reports no diagnostics in the changed files.

Agent.__aexit__ only saved the session state on non-success exits
(exc_type set, or finish never called), so a run that ended with a
clean finish wrote nothing. clear_cache_on_success=False only ever
governed the cleanup side, leaving session(resume=True,
clear_cache_on_success=False) a no-op across successful runs: a later
run on the same task text found no cache and started fresh.

Treat clear_cache_on_success=False as save-and-keep, as the name
reads: a clean finish now also passes the __aexit__ cache gate and
the final state is saved, so a later session(resume=True) can reopen
the successfully finished session. Behavior is unchanged when
clear_cache_on_success=True (run() still clears the cache at finish
and no state is saved), on error/interrupt exits, and when
cache_on_interrupt=False.

Fixes ArtificialAnalysis#91

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent state is not cached after a clean finish — resume=True workflows cannot reopen a successfully finished session

1 participant