Skip to content

feat: add persist_session option - #1322

Open
runnithan wants to merge 1 commit into
anthropics:mainfrom
runnithan:feat/persist-session
Open

runnithan wants to merge 1 commit into
anthropics:mainfrom
runnithan:feat/persist-session

Conversation

@runnithan

Copy link
Copy Markdown

Summary

Adds ClaudeAgentOptions.persist_session, the Python counterpart of the TypeScript SDK's persistSession. With persist_session=False the CLI runs with --no-session-persistence and writes no transcript under ~/.claude/projects/, so ephemeral or automated runs leave no session history behind. The default (True) adds nothing to the command line, so existing behaviour is unchanged.

Until now the only way to get this from Python was extra_args={"no-session-persistence": None}.

It follows the TypeScript SDK (checked against @anthropic-ai/claude-agent-sdk 0.3.283):

  • The flag is sent only for an explicit persist_session=False (TS: if (this.options.persistSession === false) args.push("--no-session-persistence")).
  • session_store together with persist_session=False is rejected up front, as TS does (sessionStore cannot be used with persistSession: false), because the store mirrors the transcript from those local writes. The check sits in validate_session_store_options, so query() and ClaudeSDKClient both fail before the CLI is spawned.

Verification

  • Real model calls. CLI 2.1.283 through ClaudeSDKClient with model="haiku", setting_sources=[], a fresh temp cwd: two turns (the first reads a file with the Read tool), then a query(resume=<session_id>).

    • default: projects/-tmp-persist-real-m5d-fb86/09f8511b-5713-4ef1-92d2-76765b91de18.jsonl is written, and the resume answers from the earlier turns
    • persist_session=False: both turns succeed, no file anywhere under ~/.claude names the session, and the resume fails with No conversation found with session ID: a4521e2a-80b8-4019-858a-3192b82a85b8
  • Real CLI, no model calls. The bundled CLI version (2.1.283) driven through query() with an isolated CLAUDE_CONFIG_DIR, cwd in a temp dir, and ANTHROPIC_BASE_URL pointed at a local server that answers 401. The transcript is written before the first API request, so this shows what persists:

    • default: projects/-tmp-persist-probe-work-0in8dcqu/0d5dc4b2-ab19-4c6d-b5ba-b491f5e1ad3b.jsonl is written
    • persist_session=False: no .jsonl is written

    The CLI's --help says this flag "only works with --print". The SDK doesn't pass --print, and the run above shows the flag is honoured in SDK mode anyway.

  • query() and ClaudeSDKClient with session_store=InMemorySessionStore(), persist_session=False both raise the ValueError before spawning.

  • New tests: test_build_command_persist_session (tests/test_transport.py), and test_rejects_persist_session_false_combo and test_persist_session_false_without_store_is_valid (tests/test_session_store_conformance.py). With the src/ changes stashed, all three fail (TypeError: ... unexpected keyword argument 'persist_session'); with them, all three pass.

  • New e2e tests in e2e-tests/test_persist_session.py, laid out like the verbatim_prompts ones: a control run that list_sessions() finds, then query() and ClaudeSDKClient runs with persist_session=False that it doesn't. 3 passed against the real API.

  • ruff check, ruff format --check, mypy src/ scripts/ are clean. pytest tests/: 1590 passed, 6 skipped on Python 3.12 and 3.13, and again with mcp==1.23.0 (the floor job).

The TypeScript SDK's persistSession option has no Python counterpart, so
turning off transcript writes meant reaching for extra_args. With
persist_session=False the CLI runs with --no-session-persistence and saves
nothing under ~/.claude/projects/.

As in the TypeScript SDK, the flag is sent only for an explicit False, and
session_store is rejected alongside it because the store mirrors the
transcript from those local writes. The e2e tests follow the
verbatim_prompts layout: a control run that list_sessions() finds, then
query() and ClaudeSDKClient runs with persist_session=False that it doesn't.

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.

1 participant